def _transfer_ray_4x4_1_windows(windows, field, wavenumbers, layer, dmat1, dmat2, betas, phis, nsteps = 1, out = None, betamax = BETAMAX): d, epsv, epsa = layer kd = wavenumbers*d if out is None: out = np.zeros_like(field) for j in range(nsteps): field = dotmf(dmat1,field) for window, beta, phi in zip(windows, betas, phis): alpha, f = alphaf(beta,phi,epsv,epsa) p = phasem(alpha,kd[...,None,None]) e = E_mat(f, mode = None) ei = inv(e) f = field * window f = ifft2(f, out = f) f = dotmdmf(e,p,ei,f, out = f) f = fft2(field, out = f) out += f out = dotmf(dmat2,out, out = out) return out
def _transfer_ray_4x4_1(field, wavenumbers, layer, dmat1, dmat2, beta=0, phi=0, nsteps=1, betamax=BETAMAX, out=None): d, epsv, epsa = layer kd = wavenumbers * d alpha, f = alphaf(beta, phi, epsv, epsa) p = phasem(alpha, kd[..., None, None]) e = E_mat(f, mode=None) ei = inv(e) for j in range(nsteps): field = dotmf(dmat1, field, out=out) field = ifft2(field, out=field) field = dotmdmf(e, p, ei, field, out=field) field = fft2(field, out=field) field = dotmf(dmat2, field, out=out) return field
def transmitted_field_direct(field, beta, phi, n=1.): ev = refind2eps([n] * 3) ea = np.zeros_like(ev) alpha, fmat = alphaf(beta, phi, ev, ea) pmat = projection_mat(fmat) field0 = np.empty_like(field) dotmv(pmat, transpose(field), out=transpose(field0)) return field0
def Epn_correction_matrix(beta, phi, ks, d=1., epsv=(1, 1, 1), epsa=(0, 0, 0.), out=None): alpha, f = alphaf(beta, phi, epsv, epsa) kd = -np.asarray(ks) * d pmat = phase_mat(alpha, kd[..., None, None]) e = E_mat(f, mode=None) ei = inv(e) return dotmdm(e, pmat, ei, out=out)
def ray_jonesmat4x4( jmat, beta=0, phi=0, epsv=(1., 1., 1.), epsa=(0., 0., 0.), ): """Returns a ray polarizer for field data in the laboratory frame. Numpy broadcasting rules apply. Parameters ---------- jmat : (...,2,2) array A 2x2 jones matrix that needs to be converted to 4x4 mode matrices. beta : float The beta parameter of the beam. phi : float The phi parameter of the beam. epsv : (...,3) array Medium epsilon eigenvalues epsa : (...,3) array Medium epsilon euler angles betamax : float The betamax parameter Returns ------- pmat : (...,4,4) array Output matrix. See Also -------- mode_jonesmat4x4 : for applying the jones matrix in the fft space. jonesmat4x4 : for applying the jones matrix in the eigenframe. """ epsv = np.asarray(epsv, CDTYPE) epsa = np.asarray(epsa, FDTYPE) beta = np.asarray(beta, FDTYPE) phi = np.asarray(phi, FDTYPE) alpha, f = alphaf(beta, phi, epsv, epsa) jmat = rotated_matrix(jmat, phi) pmat = jonesmat4x4(jmat, f) return pmat
def _layer_mat3d(k0, d, epsv, epsa, mask, betas, phis, indices, method): n = len(betas) kd = k0 * d #/2. shape = epsv.shape[-3], epsv.shape[-2] if method.startswith("2x2"): out = np.empty(shape=(n, n, 2, 2), dtype=CDTYPE) else: out = np.empty(shape=(n, n, 4, 4), dtype=CDTYPE) for j, (beta, phi) in enumerate(zip(betas, phis)): if method.startswith("2x2"): alpha, fmat = alphaf(beta, phi, epsv, epsa) f = tmm.E_mat(fmat, mode=+1, copy=False) fi = inv(f) pmat = phase_mat(alpha[..., ::2], kd) else: alpha, f, fi = alphaffi(beta, phi, epsv, epsa) pmat = phase_mat(alpha, -kd) if method == "4x4_1": pmat[..., 1::2] = 0. if method != "4x4": raise ValueError("Unsupported method.") wave = eigenwave(shape, indices[j, 0], indices[j, 1], amplitude=1.) m = dotmdm(f, pmat, fi) mw = m * wave[..., None, None] mf = mfft2(mw, overwrite_x=True) #dd = np.linspace(0,1.,10)*d # dmat = 0. # for dm in dd: # dmat = dmat + second_field_diffraction_matrix(shape, -k0, beta, phi,dm, # epsv = (1.5,1.5,1.5), # epsa = (0.,0.,0.), betamax = 1.4) /len(dd) # mf = dotmm(dmat,mf) mf = mf[mask, ...] out[:, j, :, :] = mf return out
def ray_polarizer( jones=(1, 0), beta=0, phi=0, epsv=(1., 1., 1.), epsa=(0., 0., 0.), ): """Returns a ray polarizer that should be applied in real space. Good for beams that can be approximated with a single wave vector and with a direction of ray propagation beta and phi parameters. See also mode_polarizer, which is for non-planewave field data.""" epsv = np.asarray(epsv, CDTYPE) epsa = np.asarray(epsa, FDTYPE) beta = np.asarray(beta, FDTYPE) phi = np.asarray(phi, FDTYPE) alpha, f = alphaf(beta, phi, epsv, epsa) jones = jonesvec(jones, phi) pmat = polarizer4x4(jones, f) return pmat
def diffraction_alphaf(shape, ks, epsv=(1., 1., 1.), epsa=(0., 0., 0.), betamax=BETAMAX, out=None): ks = np.asarray(ks) ks = abs(ks) beta, phi = betaphi(shape, ks) epsv = np.asarray(epsv, CDTYPE) epsa = np.asarray(epsa, FDTYPE) alpha, f = alphaf(beta, phi, epsv, epsa, out=out) out = (alpha, f) mask0 = (beta >= betamax) f[mask0] = 0. alpha[mask0] = 0. return out
def test_alphaf(self): for e in (1, 2): alpha0, f0 = alphaf(0, 0, (e, e, e), (0, 0, 0)) alpha, f = alphaf(0.00001, 0.0001, (e, e, e), (0, 0, 0)) self.allclose(np.round(f, 3), np.round(f0, 3)) alpha, f = alphaf(0., 0, (e, e, e + 0.0001), (0, 0, 0)) self.allclose(np.round(normalize_f(f), 3), np.round(normalize_f(f0), 3)) alpha, f = alphaf(0.0001, 0, (e, e, e + 0.0001), (0, 0, 0)) self.allclose(np.round(normalize_f(f), 3), np.round(normalize_f(f0), 3)) alpha0, f0 = alphaf(0, np.pi / 4, (e, e, e), (0, 0, 0)) alpha, f = alphaf(0.0001, np.pi / 4, (e, e, e + 0.0001), (0, 0, 0)) self.allclose(np.round(normalize_f(f), 3), np.round(normalize_f(f0), 3))
def illumination_data(shape, wavelengths, pixelsize=1., beta=0., phi=0., intensity=1., n=1., focus=0., window=None, backdir=False, jones=None, diffraction=True, betamax=BETAMAX): """Constructs forward (or backward) propagating input illumination field data. Parameters ---------- shape : (int,int) Shape of the illumination wavelengths : array_like A list of wavelengths. pixelsize : float, optional Size of the pixel in nm. beta : float or array_like of floats, optional Beta parameter(s) of the illumination. (Default 0. - normal incidence) phi : float or array_like of floats, optional Azimuthal angle(s) of the illumination. n : float, optional Refractive index of the media that this illumination field is assumed to be propagating in (default 1.) focus : float, optional Focal plane of the field. By default it is set at z=0. window : array or None, optional If None, no window function is applied. This window function is multiplied with the constructed plane waves to define field diafragm of the input light. See :func:`.window.aperture`. backdir : bool, optional Whether field is bacward propagating, instead of being forward propagating (default) jones : jones vector or None, optional If specified it has to be a valid jones vector that defines polarization of the light. If not given (default), the resulting field will have two polarization components. See documentation for details and examples. diffraction : bool, optional Specifies whether field is diffraction limited or not. By default, the field is filtered so that it has only propagating waves. You can disable this by specifying diffraction = False. betamax : float, optional The betamax parameter of the propagating field. """ verbose_level = DTMMConfig.verbose if verbose_level > 0: print("Building illumination data.") wavelengths = np.asarray(wavelengths) wavenumbers = 2 * np.pi / wavelengths * pixelsize if wavenumbers.ndim not in (1, ): raise ValueError("Wavelengths should be 1D array") if jones is None: intensity = intensity / 2. waves = illumination_waves(shape, wavenumbers, beta=beta, phi=phi, window=window) #intensity = ((np.abs(waves)**2).sum((-2,-1)))* np.asarray(intensity)[...,None]#sum over pixels #intensity = intensity * intensity mode = -1 if backdir else +1 _beta = np.asarray(beta, FDTYPE) _phi = np.asarray(phi, FDTYPE) _intensity = np.asarray(intensity, FDTYPE) nrays = len(_beta) if _beta.ndim > 0 else 1 beta = _beta[..., None, None, None] phi = _phi[..., None, None, None] intensity = _intensity[..., None, None, None, None] epsa = np.asarray((0., 0., 0.), FDTYPE) alpha, fmat = alphaf(beta, phi, refind2eps([n] * 3), epsa) field = waves2field2(waves, fmat, jones=jones, phi=phi, mode=mode) intensity1 = field2intensity(field) norm = np.ones_like(intensity1) norm[:, ...] = (intensity / nrays)**0.5 if diffraction == True: diffracted_field(field, wavenumbers, d=-focus, n=n, mode=mode, betamax=betamax, out=field) intensity2 = field2intensity(field) ratio = (intensity1.sum((-2, -1)) / intensity2.sum((-2, -1)))**0.5 norm[...] = norm * ratio[..., None, None] np.multiply(norm[..., None, :, :], field, field) return (field, wavelengths, pixelsize)
def propagate_2x2_full(field, wavenumbers, layer, input_layer=None, nsteps=1, mode=+1, reflection=True, betamax=BETAMAX, refl=None, bulk=None, out=None): shape = field.shape[-2:] d, epsv, epsa = layer if input_layer is not None: d_in, epsv_in, epsa_in = input_layer kd = wavenumbers * d / nsteps if out is None: out = np.empty_like(field) ii, jj = np.meshgrid(range(shape[0]), range(shape[1]), copy=False, indexing="ij") for step in range(nsteps): for i in range(len(wavenumbers)): ffield = fft2(field[..., i, :, :, :]) ofield = np.zeros_like(out[..., i, :, :, :]) b, p = betaphi(shape, wavenumbers[i]) mask = b < betamax amplitude = ffield[..., mask] betas = b[mask] phis = p[mask] iind = ii[mask] jind = jj[mask] if bulk is not None: obulk = bulk[..., i, :, :, :] if refl is not None: tampl = fft2(refl[..., i, :, :, :])[..., mask] orefl = refl[..., i, :, :, :] orefl[...] = 0. for bp in sorted(zip(range(len(betas)), betas, phis, iind, jind), key=lambda el: el[1], reverse=False): #for j,bp in enumerate(zip(betas,phis,iind,jind)): j, beta, phi, ieig, jeig = bp out_af = alphaf(beta, phi, epsv, epsa) alpha, fmat_out = out_af e = E_mat(fmat_out, mode=mode) ei0 = inv(e) ei = ei0 pm = phase_mat(alpha, kd[i, None, None], mode=mode) w = eigenwave(amplitude.shape[:-1] + shape, ieig, jeig, amplitude=amplitude[..., j]) if step == 0 and reflection != False: alphain, fmat_in = alphaf(beta, phi, epsv_in, epsa_in) if refl is not None: ei, eri = Etri_mat(fmat_in, fmat_out, mode=mode) ein = E_mat(fmat_in, mode=-1 * mode) t = eigenwave(amplitude.shape[:-1] + shape, ieig, jeig, amplitude=tampl[..., j]) r = dotmf(eri, w) r = dotmf(ein, r, out=r) np.add(orefl, r, orefl) w = dotmf(ei, w, out=w) t = dotmf(ei0, t, out=t) w = np.add(t, w, out=w) else: ei = Eti_mat(fmat_in, fmat_out, mode=mode) w = dotmf(ei, w, out=w) w = dotmf(dotmd(e, pm), w, out=w) np.add(ofield, w, ofield) else: w = dotmdmf(e, pm, ei, w, out=w) np.add(ofield, w, ofield) if bulk is not None: e2h = E2H_mat(fmat_out, mode=mode) obulk[..., 1::2, :, :] += dotmf(e2h, w) obulk[..., ::2, :, :] += w out[..., i, :, :, :] = ofield field = out return out, refl
def _transfer_ray_2x2_1(fft_field, wavenumbers, layer, effective_layer_in, effective_layer_out, dmat1, dmat2, beta=0, phi=0, nsteps=1, mode=+1, reflection=True, betamax=BETAMAX, refl=None, bulk=None, out=None, tmpdata=None): _out = {} if tmpdata is None else tmpdata #fft_field = fft2(fft_field, out = out) shape = fft_field.shape[-2:] d_in, epsv_in, epsa_in = effective_layer_in d_out, epsv_out, epsa_out = effective_layer_out if reflection: tmat, rmat = E_tr_matrix(shape, wavenumbers, epsv_in=epsv_in, epsa_in=epsa_in, epsv_out=epsv_out, epsa_out=epsa_out, mode=mode, betamax=betamax) d, epsv, epsa = layer alpha, fmat = alphaf(beta, phi, epsv, epsa, out=_out.get("alphaf")) e = E_mat(fmat, mode=mode, copy=False) #2x2 E-only view of fmat ei = inv(e, out=_out.get("ei")) # kd = wavenumbers * d p = phase_mat(alpha, kd[..., None, None], mode=mode, out=_out.get("p")) if tmpdata is not None: _out["alphaf"] = alpha, fmat _out["ei"] = ei _out["p"] = p for j in range(nsteps): if j == 0 and reflection: #reflect only at the beginning if refl is not None: trans = refl.copy() refl = dotmf(rmat, fft_field, out=refl) fft_field = dotmf(tmat, fft_field, out=out) fft_field = np.add(fft_field, trans, out=fft_field) if mode == -1 and bulk is not None: field = ifft2(fft_field) e2h = E2H_mat(fmat, mode=mode) bulk[..., ::2, :, :] += field bulk[..., 1::2, :, :] += dotmf(e2h, field, out=field) fft_field = dotmf(dmat1, fft_field, out=fft_field) out = fft_field else: fft_field = dotmf(tmat, fft_field, out=out) fft_field = dotmf(dmat1, fft_field, out=fft_field) out = fft_field else: if dmat1 is not None: fft_field = dotmf(dmat1, fft_field, out=out) out = fft_field field = ifft2(fft_field, out=out) field = dotmdmf(e, p, ei, field, out=field) fft_field = fft2(field, out=field) if dmat2 is not None: fft_field = dotmf(dmat2, fft_field, out=fft_field) #return fft_field, refl #out = ifft2(fft_field, out = out) if mode == +1 and bulk is not None: field = ifft2(fft_field) e2h = E2H_mat(fmat, mode=mode) bulk[..., 1::2, :, :] += dotmf(e2h, field) bulk[..., ::2, :, :] += field return fft_field, refl
def _transfer_ray_2x2_2(field, wavenumbers, in_layer, out_layer, dmat=None, beta=0, phi=0, nsteps=1, mode=+1, reflection=True, betamax=BETAMAX, refl=None, bulk=None, out=None, tmpdata=None): _out = {} if tmpdata is None else tmpdata if in_layer is not None: d, epsv, epsa = in_layer alpha, fmat_in = alphaf(beta, phi, epsv, epsa, out=_out.get("afin")) if tmpdata is not None: _out["afin"] = alpha, fmat_in d, epsv, epsa = out_layer alpha, fmat = alphaf(beta, phi, epsv, epsa, out=_out.get("afout")) e = E_mat(fmat, mode=mode, copy=False) #2x2 E-only view of fmat # if refl is not None: # ein = E_mat(fmat_in, mode = mode * (-1)) #reverse direction # # if reflection == 0: # kd = wavenumbers * d # else: kd = wavenumbers * d / 2 p = phase_mat(alpha, kd[..., None, None], mode=mode, out=_out.get("p")) ei0 = inv(e, out=_out.get("ei0")) ei = ei0 if tmpdata is not None: _out["afout"] = alpha, fmat _out["ei0"] = ei _out["p"] = p for j in range(nsteps): #reflect only at the beginning if j == 0 and reflection != 0: #if we need to track reflections (multipass) if refl is not None: #ei,eri = Etri_mat(fmat_in, fmat, mode = mode, out = _out.get("eieri")) tmat, rmat = tr_mat(fmat_in, fmat, mode=mode, out=_out.get("eieri")) if tmpdata is not None: #_out["eieri"] = ei,eri _out["eieri"] = tmat, rmat trans = refl.copy() #refl = dotmf(eri, field, out = refl) refl = dotmf(rmat, field, out=refl) #field = dotmf(ei,field, out = out) field = dotmf(tmat, field, out=out) field = np.add(field, trans, out=field) if mode == -1 and bulk is not None: #tmp_field = dotmf(e,field) tmp_field = field e2h = E2H_mat(fmat, mode=mode) bulk[..., ::2, :, :] += tmp_field bulk[..., 1::2, :, :] += dotmf(e2h, tmp_field) field = dotmf(ei, field, out=field) if d != 0.: field = dotmf(dotmd(e, p), field, out=field) else: field = dotmf(e, field, out=field) out = field # rmat = dotmm(ein, eri, out = eri) # trans = refl.copy() # refl = dotmf(rmat, field, out = refl) # ei0 = inv(e) # field = dotmf(ei,field, out = out) # field = dotmf(e,field) + trans # if d != 0.: # field = dotmdmf(e,p,ei0,field, out = out) ei = ei0 else: ei = Eti_mat(fmat_in, fmat, mode=mode, out=_out.get("eti")) field = dotmdmf(e, p, ei, field, out=out) out = field if tmpdata is not None: _out["eti"] = ei ei = ei0 else: #no need to compute if d == 0!.. identity if d != 0.: field = dotmdmf(e, p, ei, field, out=out) out = field if dmat is not None: field = diffract(field, dmat, out=out) out = field #no need to compute if d == 0!.. identity if d != 0.: field = dotmdmf(e, p, ei, field, out=out) if mode == +1 and bulk is not None: e2h = E2H_mat(fmat, mode=mode) bulk[..., 1::2, :, :] += dotmf(e2h, field) bulk[..., ::2, :, :] += field return field, refl
def field2jones(field, ks=None, beta=None, phi=None, epsv=(1., 1., 1.), epsa=(0., 0., 0.), mode=+1, input_fft=False, output_fft=False, betamax=BETAMAX): """Converts (..., 4,n,m) field array to (..., 2,n,m) jones array The conversion is done either in reciprocal space (default) or in real space, if you provide the beta or phi parameter. Parameters ---------- field : ndarray Input field array data of shape (..., 4,n,m). ks : array A list of k-values (wave numbers). Required if beta and phi are not provided. beta : float, optional If set, it defines the field beta parameter, conversion is then done in real space. phi : float, optional If set, it defines the field phi parameter, conversion is then done in real space. epsv : array The epsilon eigenvalues of the medium epsv : array The Euler rotation angles. mode : [+1 | -1] Propagation mode, +1 (forward) by default input_fft : bool Specifies whether input array is fft data or not. output_fft : bool Specifies whether output array is fft data or not. betamax : float Defines the betamax parameter if transformation is done in Fourier space. Returns ------- jones : ndarray Output jones array of shape (..., 2,n,m). """ modal = beta is None and phi is None if modal and ks is None: raise ValueError( "For modal fields, you need to define the wave number(s).") elif not modal and ks is not None: import warnings warnings.warn( "`ks`, was set, although it is not required - field is not modal", UserWarning) field = np.asarray(field) out = np.empty(field.shape[:-3] + (2, ) + field.shape[-2:], field.dtype) if input_fft == False and modal: field = fft2(field) elif input_fft == True and not modal: field = ifft2(field) shape = field.shape[-3:] if modal: a, fmat = diffraction_alphaf(shape, ks, epsv, epsa, betamax=betamax) else: a, fmat, = alphaf(beta, phi, epsv, epsa) fvec = field2fvec(field) evec = field2fvec(out) evec[...] = fvec2E(fvec, fmat=fmat, mode=mode) if output_fft == False and modal: ifft2(out, out=out) elif output_fft == True and not modal: fft2(out, out=out) return out
def jones2field(jones, ks, beta=None, phi=None, epsv=(1., 1., 1.), epsa=(0., 0., 0.), mode=+1, input_fft=False, output_fft=False, betamax=BETAMAX): """Converts (..., 2,n,m) jones array to (..., 4,n,m) field array The conversion is done either in reciprocal space (default) or in real space, if you provide the beta or phi parameter. Parameters ---------- jones : ndarray Input jones array data of shape (..., 2,n,m). ks : array A list of k-values (wave numbers). beta : float, optional If set, it defines the field beta parameter, conversion is then done in real space. phi : float, optional If set, it defines the field phi parameter, conversion is then done in real space. epsv : array The epsilon eigenvalues of the medium epsv : array The Euler rotation angles. mode : [+1 | -1] Propagation mode, +1 (forward) by default input_fft : bool Specifies whether input array is fft data or not. output_fft : bool Specifies whether output array is fft data or not. betamax : float Defines the betamax parameter if transformation is done in Fourier space. Returns ------- field : ndarray Output field array of shape (..., 4,n,m). """ jones = np.asarray(jones) modal = beta is None and phi is None out = np.empty(jones.shape[:-3] + (4, ) + jones.shape[-2:], jones.dtype) if input_fft == False and modal: jones = fft2(jones) elif input_fft == True and not modal: jones = ifft2(jones) shape = jones.shape[-3:] if modal: a, fmat = diffraction_alphaf(shape, ks, epsv, epsa, betamax=betamax) else: a, fmat, = alphaf(beta, phi, epsv, epsa) fvec = field2fvec(out) jvec = field2fvec(jones) E2fvec(jvec, fmat=fmat, mode=mode, out=fvec) if output_fft == False and modal: ifft2(out, out=out) elif output_fft == True and not modal: fft2(out, out=out) return out