def getHessFull(gammas=None, amounts=None, numExps=3): if gammas is None: gammas = getGammas(numExps) if amounts is None: amounts = getAmounts(numExps) numExps = scipy.size(gammas) hessgg = getHessGG(gammas) hessAA = getHessAA(amounts, gammas) numerAg1 = -scipy.outer(amounts, amounts * gammas) denomAg1 = scipy.outer(scipy.ones(numExps), gammas) denomAg2 = scipy.transpose(denomAg1) + denomAg1 denomAg3 = denomAg2 * denomAg2 hessAg = old_div(numerAg1, denomAg3) numergA1 = -scipy.outer(amounts * gammas, amounts) denomgA1 = scipy.outer(gammas, scipy.ones(numExps)) denomgA2 = scipy.transpose(denomgA1) + denomgA1 denomgA3 = denomgA2 * denomgA2 hessgA = old_div(numergA1, denomgA3) hessFull = scipy.bmat([[hessAA, hessAg], [hessgA, hessgg]]) return hessFull
def getHessFullLogTime(gammas=None, amounts=None, numExps=3): """use this routine for new paper""" if gammas is None: gammas = getGammas(numExps) if amounts is None: amounts = getAmounts(numExps) numExps = scipy.size(gammas) amountsLess1 = amounts[0:-1] gammasLess1 = gammas[0:-1] hessgg = getHessGGLogTime(gammas=gammas, amounts=amounts) hessAA = getHessAALogTime(amounts=amounts, gammas=gammas) numerAg1 = scipy.array(-2. * scipy.outer(amountsLess1, amounts * gammas)) denomAg1 = scipy.array(scipy.outer(gammasLess1, scipy.ones(numExps))) denomAg2 = scipy.array(scipy.outer(scipy.ones(numExps - 1), gammas)) denomAg3 = scipy.array(denomAg1 + denomAg2) denomAg4 = denomAg2 + gammas[-1] hessAg = scipy.array(old_div(numerAg1, denomAg3)) - scipy.array( old_div(numerAg1, denomAg4)) hessgA = scipy.transpose(hessAg) hessFull = scipy.array(scipy.bmat([[hessAA, hessAg], [hessgA, hessgg]])) return hessFull
def getDerivatives(self, N, op): """ Compute the successive derivative of an eigenvalue of an OP instance Parameters ----------- N: int the number derivative to compute op: OP the operator OP instance that describe the eigenvalue problem RHS derivative must start at n=1 for 1st derivatives """ # get nu0 value where the derivative are computed self.nu0 = op.nu0 # construction de la matrice de l'opérateur L, ie L.L L = op.createL(self.lda) # normalization condition (push elsewhere : différente méthode, indépendace vs type ) # must be done before L1x v = np.ones(shape=self.x.shape) # see also VecScale self.x *= (1 / v.dot(self.x)) self.dx[0] = self.x # constrution du vecteur (\partial_\lambda L)x, ie L.L1x L1x = op.createDL_ldax(self) # bordered # --------------------------------------------------------------------- # Same matrix to factorize for all RHS Zer = np.zeros(shape=(1, 1), dtype=np.complex) Zerv = np.zeros(shape=(1, ), dtype=np.complex) Bord = sp.bmat([[L, L1x.reshape(-1, 1)], [v.reshape(1, -1), Zer]]) # reshape is to avoid (n,) in bmat # if N > 1 loop for higher order terms print('> Linear solve...') # n start now at 1 for uniformization for n in range(1, N + 1): # compute RHS tic = time.time() # init timer Ftemp = op.getRHS(self, n) #F= sp.bmat([Ftemp, Zerv]).reshape(-1,1) F = np.concatenate((Ftemp, Zerv)) # print(" # getRHS real time :", time.time()-tic) tic = time.time() # init timer if n == 1: # compute the lu factor lu, piv = sp.linalg.lu_factor(Bord) # Forward and back substitution, u contains [dx, dlda]) u = sp.linalg.lu_solve((lu, piv), F) # print(" # solve LU real time :", time.time()-tic) # get lda^(n) derivee = u[-1] # store the value self.dlda.append(derivee) self.dx.append(u.copy()[:-1])
def getJacobianLog(times, gammas, amounts=None): """This is the routine to use for new paper (with times exponentially distributed.""" numExps = scipy.size(gammas) if amounts is None: amounts = scipy.ones(numExps, 'd') jacG = getJacobianLogG(times, gammas, amounts) jacA = getJacobianLogA(times, gammas, amounts) return scipy.transpose(scipy.array(scipy.bmat([[jacA], [jacG]])))
def ProcessQuarterMatrix(p): """ this is used if parameters define just upper right and lower left quadrants of antisymmetric matrix. """ n = scipy.sqrt(scipy.size(p)) Mu = scipy.resize(p, (n, n)) Mfull = scipy.bmat([[scipy.zeros((n, n)), Mu], [-scipy.transpose(Mu), scipy.zeros((n, n))]]) return scipy.linalg.expm(Mfull)
def solve(self, wls): """Anisotropic solver. INPUT wls = wavelengths to scan (any asarray-able object). OUTPUT self.DEO1, self.DEE1, self.DEO3, self.DEE3 = power reflected and transmitted. """ self.wls = S.atleast_1d(wls) LAMBDA = self.LAMBDA n = self.n multilayer = self.multilayer alpha = self.alpha delta = self.delta psi = self.psi phi = self.phi nlayers = len(multilayer) i = S.arange(-n, n + 1) nood = 2 * n + 1 hmax = nood - 1 DEO1 = S.zeros((nood, self.wls.size)) DEO3 = S.zeros_like(DEO1) DEE1 = S.zeros_like(DEO1) DEE3 = S.zeros_like(DEO1) c1 = np.array([1., 0., 0.]) c3 = np.array([1., 0., 0.]) # grating on the xy plane K = 2 * pi / LAMBDA * np.array( [S.sin(phi), 0., S.cos(phi)], dtype=complex) dirk1 = np.array([ S.sin(alpha) * S.cos(delta), S.sin(alpha) * S.sin(delta), S.cos(alpha) ]) # D polarization vector u = np.array([ S.cos(psi) * S.cos(alpha) * S.cos(delta) - S.sin(psi) * S.sin(delta), S.cos(psi) * S.cos(alpha) * S.sin(delta) + S.sin(psi) * S.cos(delta), -S.cos(psi) * S.sin(alpha), ]) kO1i = S.zeros((3, i.size), dtype=complex) kE1i = S.zeros_like(kO1i) kO3i = S.zeros_like(kO1i) kE3i = S.zeros_like(kO1i) Mp = S.zeros((4 * nood, 4 * nood, nlayers), dtype=complex) M = S.zeros((4 * nood, 4 * nood, nlayers), dtype=complex) dlt = (i == 0).astype(int) for iwl, wl in enumerate(self.wls): nO1 = nE1 = multilayer[0].mat.n(wl).item() nO3 = nE3 = multilayer[-1].mat.n(wl).item() # wavevectors k = 2 * pi / wl eps1 = S.diag(S.asarray([nE1, nO1, nO1])**2) eps3 = S.diag(S.asarray([nE3, nO3, nO3])**2) # ordinary wave abskO1 = k * nO1 # abskO3 = k * nO3 # extraordinary wave # abskE1 = k * nO1 *nE1 / S.sqrt(nO1**2 + (nE1**2 - nO1**2) * S.dot(-c1, dirk1)**2) # abskE3 = k * nO3 *nE3 / S.sqrt(nO3**2 + (nE3**2 - nO3**2) * S.dot(-c3, dirk1)**2) k1 = abskO1 * dirk1 kO1i[0, :] = k1[0] - i * K[0] kO1i[1, :] = k1[1] * S.ones_like(i) kO1i[2, :] = -dispersion_relation_ordinary(kO1i[0, :], kO1i[1, :], k, nO1) kE1i[0, :] = kO1i[0, :] kE1i[1, :] = kO1i[1, :] kE1i[2, :] = -dispersion_relation_extraordinary( kE1i[0, :], kE1i[1, :], k, nO1, nE1, c1) kO3i[0, :] = kO1i[0, :] kO3i[1, :] = kO1i[1, :] kO3i[2, :] = dispersion_relation_ordinary(kO3i[0, :], kO3i[1, :], k, nO3) kE3i[0, :] = kO1i[0, :] kE3i[1, :] = kO1i[1, :] kE3i[2, :] = dispersion_relation_extraordinary( kE3i[0, :], kE3i[1, :], k, nO3, nE3, c3) # k2i = S.r_[[k1[0] - i * K[0]], [k1[1] - i * K[1]], [k1[2] - i * K[2]]] k2i = S.r_[[k1[0] - i * K[0]], [k1[1] - i * K[1]], [-i * K[2]]] # aliases for constant wavevectors kx = kO1i[0, :] # o kE1i(1,;), tanto e' lo stesso ky = k1[1] # matrices I = S.eye(nood, dtype=complex) ZERO = S.zeros((nood, nood), dtype=complex) Kx = S.diag(kx / k) Ky = ky / k * I Kz = S.diag(k2i[2, :] / k) KO1z = S.diag(kO1i[2, :] / k) KE1z = S.diag(kE1i[2, :] / k) KO3z = S.diag(kO3i[2, :] / k) KE3z = S.diag(kE3i[2, :] / k) ARO = Kx * eps1[0, 0] + Ky * eps1[1, 0] + KO1z * eps1[2, 0] BRO = Kx * eps1[0, 1] + Ky * eps1[1, 1] + KO1z * eps1[2, 1] CRO_1 = inv(Kx * eps1[0, 2] + Ky * eps1[1, 2] + KO1z * eps1[2, 2]) ARE = Kx * eps1[0, 0] + Ky * eps1[1, 0] + KE1z * eps1[2, 0] BRE = Kx * eps1[0, 1] + Ky * eps1[1, 1] + KE1z * eps1[2, 1] CRE_1 = inv(Kx * eps1[0, 2] + Ky * eps1[1, 2] + KE1z * eps1[2, 2]) ATO = Kx * eps3[0, 0] + Ky * eps3[1, 0] + KO3z * eps3[2, 0] BTO = Kx * eps3[0, 1] + Ky * eps3[1, 1] + KO3z * eps3[2, 1] CTO_1 = inv(Kx * eps3[0, 2] + Ky * eps3[1, 2] + KO3z * eps3[2, 2]) ATE = Kx * eps3[0, 0] + Ky * eps3[1, 0] + KE3z * eps3[2, 0] BTE = Kx * eps3[0, 1] + Ky * eps3[1, 1] + KE3z * eps3[2, 1] CTE_1 = inv(Kx * eps3[0, 2] + Ky * eps3[1, 2] + KE3z * eps3[2, 2]) DRE = c1[1] * KE1z - c1[2] * Ky ERE = c1[2] * Kx - c1[0] * KE1z FRE = c1[0] * Ky - c1[1] * Kx DTE = c3[1] * KE3z - c3[2] * Ky ETE = c3[2] * Kx - c3[0] * KE3z FTE = c3[0] * Ky - c3[1] * Kx b = S.r_[u[0] * dlt, u[1] * dlt, (k1[1] / k * u[2] - k1[2] / k * u[1]) * dlt, (k1[2] / k * u[0] - k1[0] / k * u[2]) * dlt, ] Ky_CRO_1 = ky / k * CRO_1 Ky_CRE_1 = ky / k * CRE_1 Kx_CRO_1 = kx[:, S.newaxis] / k * CRO_1 Kx_CRE_1 = kx[:, S.newaxis] / k * CRE_1 MR31 = -S.dot(Ky_CRO_1, ARO) MR32 = -S.dot(Ky_CRO_1, BRO) - KO1z MR33 = -S.dot(Ky_CRE_1, ARE) MR34 = -S.dot(Ky_CRE_1, BRE) - KE1z MR41 = S.dot(Kx_CRO_1, ARO) + KO1z MR42 = S.dot(Kx_CRO_1, BRO) MR43 = S.dot(Kx_CRE_1, ARE) + KE1z MR44 = S.dot(Kx_CRE_1, BRE) MR = S.asarray( S.bmat([ [I, ZERO, I, ZERO], [ZERO, I, ZERO, I], [MR31, MR32, MR33, MR34], [MR41, MR42, MR43, MR44], ])) Ky_CTO_1 = ky / k * CTO_1 Ky_CTE_1 = ky / k * CTE_1 Kx_CTO_1 = kx[:, S.newaxis] / k * CTO_1 Kx_CTE_1 = kx[:, S.newaxis] / k * CTE_1 MT31 = -S.dot(Ky_CTO_1, ATO) MT32 = -S.dot(Ky_CTO_1, BTO) - KO3z MT33 = -S.dot(Ky_CTE_1, ATE) MT34 = -S.dot(Ky_CTE_1, BTE) - KE3z MT41 = S.dot(Kx_CTO_1, ATO) + KO3z MT42 = S.dot(Kx_CTO_1, BTO) MT43 = S.dot(Kx_CTE_1, ATE) + KE3z MT44 = S.dot(Kx_CTE_1, BTE) MT = S.asarray( S.bmat([ [I, ZERO, I, ZERO], [ZERO, I, ZERO, I], [MT31, MT32, MT33, MT34], [MT41, MT42, MT43, MT44], ])) Mp.fill(0.0) M.fill(0.0) for nlayer in range(nlayers - 2, 0, -1): # internal layers layer = multilayer[nlayer] thickness = layer.thickness EPS2, EPS21 = layer.getEPSFourierCoeffs(wl, n, anisotropic=True) # Exx = S.squeeze(EPS2[0, 0, :]) # Exx = toeplitz(S.flipud(Exx[0:hmax + 1]), Exx[hmax:]) Exy = S.squeeze(EPS2[0, 1, :]) Exy = toeplitz(S.flipud(Exy[0:hmax + 1]), Exy[hmax:]) Exz = S.squeeze(EPS2[0, 2, :]) Exz = toeplitz(S.flipud(Exz[0:hmax + 1]), Exz[hmax:]) Eyx = S.squeeze(EPS2[1, 0, :]) Eyx = toeplitz(S.flipud(Eyx[0:hmax + 1]), Eyx[hmax:]) Eyy = S.squeeze(EPS2[1, 1, :]) Eyy = toeplitz(S.flipud(Eyy[0:hmax + 1]), Eyy[hmax:]) Eyz = S.squeeze(EPS2[1, 2, :]) Eyz = toeplitz(S.flipud(Eyz[0:hmax + 1]), Eyz[hmax:]) Ezx = S.squeeze(EPS2[2, 0, :]) Ezx = toeplitz(S.flipud(Ezx[0:hmax + 1]), Ezx[hmax:]) Ezy = S.squeeze(EPS2[2, 1, :]) Ezy = toeplitz(S.flipud(Ezy[0:hmax + 1]), Ezy[hmax:]) Ezz = S.squeeze(EPS2[2, 2, :]) Ezz = toeplitz(S.flipud(Ezz[0:hmax + 1]), Ezz[hmax:]) Exx_1 = S.squeeze(EPS21[0, 0, :]) Exx_1 = toeplitz(S.flipud(Exx_1[0:hmax + 1]), Exx_1[hmax:]) Exx_1_1 = inv(Exx_1) # lalanne Ezz_1 = inv(Ezz) Ky_Ezz_1 = ky / k * Ezz_1 Kx_Ezz_1 = kx[:, S.newaxis] / k * Ezz_1 Exz_Ezz_1 = S.dot(Exz, Ezz_1) Eyz_Ezz_1 = S.dot(Eyz, Ezz_1) H11 = 1j * S.dot(Ky_Ezz_1, Ezy) H12 = 1j * S.dot(Ky_Ezz_1, Ezx) H13 = S.dot(Ky_Ezz_1, Kx) H14 = I - S.dot(Ky_Ezz_1, Ky) H21 = 1j * S.dot(Kx_Ezz_1, Ezy) H22 = 1j * S.dot(Kx_Ezz_1, Ezx) H23 = S.dot(Kx_Ezz_1, Kx) - I H24 = -S.dot(Kx_Ezz_1, Ky) H31 = S.dot(Kx, Ky) + Exy - S.dot(Exz_Ezz_1, Ezy) H32 = Exx_1_1 - S.dot(Ky, Ky) - S.dot(Exz_Ezz_1, Ezx) H33 = 1j * S.dot(Exz_Ezz_1, Kx) H34 = -1j * S.dot(Exz_Ezz_1, Ky) H41 = S.dot(Kx, Kx) - Eyy + S.dot(Eyz_Ezz_1, Ezy) H42 = -S.dot(Kx, Ky) - Eyx + S.dot(Eyz_Ezz_1, Ezx) H43 = -1j * S.dot(Eyz_Ezz_1, Kx) H44 = 1j * S.dot(Eyz_Ezz_1, Ky) H = 1j * S.diag(S.repeat(S.diag(Kz), 4)) + S.asarray( S.bmat([ [H11, H12, H13, H14], [H21, H22, H23, H24], [H31, H32, H33, H34], [H41, H42, H43, H44], ])) q, W = eig(H) W1, W2, W3, W4 = S.split(W, 4) # # boundary conditions # # x = [R T] # R = [ROx ROy REx REy] # T = [TOx TOy TEx TEy] # b + MR.R = M1p.c # M1.c = M2p.c # ... # ML.c = MT.T # therefore: b + MR.R = (M1p.M1^-1.M2p.M2^-1. ...).MT.T # missing equations from (46)..(49) in glytsis_rigorous # [b] = [-MR Mtot.MT] [R] # [0] [...........] [T] z = S.zeros_like(q) z[S.where(q.real > 0)] = -thickness D = S.exp(k * q * z) Sy0 = W1 * D[S.newaxis, :] Sx0 = W2 * D[S.newaxis, :] Uy0 = W3 * D[S.newaxis, :] Ux0 = W4 * D[S.newaxis, :] z = thickness * S.ones_like(q) z[S.where(q.real > 0)] = 0 D = S.exp(k * q * z) D1 = S.exp(-1j * k2i[2, :] * thickness) Syd = D1[:, S.newaxis] * W1 * D[S.newaxis, :] Sxd = D1[:, S.newaxis] * W2 * D[S.newaxis, :] Uyd = D1[:, S.newaxis] * W3 * D[S.newaxis, :] Uxd = D1[:, S.newaxis] * W4 * D[S.newaxis, :] Mp[:, :, nlayer] = S.r_[Sx0, Sy0, -1j * Ux0, -1j * Uy0] M[:, :, nlayer] = S.r_[Sxd, Syd, -1j * Uxd, -1j * Uyd] Mtot = S.eye(4 * nood, dtype=complex) for nlayer in range(1, nlayers - 1): Mtot = S.dot(S.dot(Mtot, Mp[:, :, nlayer]), inv(M[:, :, nlayer])) BC_b = S.r_[b, S.zeros_like(b)] BC_A1 = S.c_[-MR, S.dot(Mtot, MT)] BC_A2 = S.asarray( S.bmat([ [ (c1[0] * I - c1[2] * S.dot(CRO_1, ARO)), (c1[1] * I - c1[2] * S.dot(CRO_1, BRO)), ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ], [ ZERO, ZERO, (DRE - S.dot(S.dot(FRE, CRE_1), ARE)), (ERE - S.dot(S.dot(FRE, CRE_1), BRE)), ZERO, ZERO, ZERO, ZERO, ], [ ZERO, ZERO, ZERO, ZERO, (c3[0] * I - c3[2] * S.dot(CTO_1, ATO)), (c3[1] * I - c3[2] * S.dot(CTO_1, BTO)), ZERO, ZERO, ], [ ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, (DTE - S.dot(S.dot(FTE, CTE_1), ATE)), (ETE - S.dot(S.dot(FTE, CTE_1), BTE)), ], ])) BC_A = S.r_[BC_A1, BC_A2] x = linsolve(BC_A, BC_b) ROx, ROy, REx, REy, TOx, TOy, TEx, TEy = S.split(x, 8) ROz = -S.dot(CRO_1, (S.dot(ARO, ROx) + S.dot(BRO, ROy))) REz = -S.dot(CRE_1, (S.dot(ARE, REx) + S.dot(BRE, REy))) TOz = -S.dot(CTO_1, (S.dot(ATO, TOx) + S.dot(BTO, TOy))) TEz = -S.dot(CTE_1, (S.dot(ATE, TEx) + S.dot(BTE, TEy))) denom = (k1[2] - S.dot(u, k1) * u[2]).real DEO1[:, iwl] = (-( (S.absolute(ROx)**2 + S.absolute(ROy)**2 + S.absolute(ROz)**2) * S.conj(kO1i[2, :]) - (ROx * kO1i[0, :] + ROy * kO1i[1, :] + ROz * kO1i[2, :]) * S.conj(ROz)).real / denom) DEE1[:, iwl] = (-( (S.absolute(REx)**2 + S.absolute(REy)**2 + S.absolute(REz)**2) * S.conj(kE1i[2, :]) - (REx * kE1i[0, :] + REy * kE1i[1, :] + REz * kE1i[2, :]) * S.conj(REz)).real / denom) DEO3[:, iwl] = ( (S.absolute(TOx)**2 + S.absolute(TOy)**2 + S.absolute(TOz)**2) * S.conj(kO3i[2, :]) - (TOx * kO3i[0, :] + TOy * kO3i[1, :] + TOz * kO3i[2, :]) * S.conj(TOz)).real / denom DEE3[:, iwl] = ( (S.absolute(TEx)**2 + S.absolute(TEy)**2 + S.absolute(TEz)**2) * S.conj(kE3i[2, :]) - (TEx * kE3i[0, :] + TEy * kE3i[1, :] + TEz * kE3i[2, :]) * S.conj(TEz)).real / denom # save the results self.DEO1 = DEO1 self.DEE1 = DEE1 self.DEO3 = DEO3 self.DEE3 = DEE3 return self
def solve(self, wls): """Isotropic solver. INPUT wls = wavelengths to scan (any asarray-able object). OUTPUT self.DE1, self.DE3 = power reflected and transmitted. NOTE see: Moharam, "Formulation for stable and efficient implementation of the rigorous coupled-wave analysis of binary gratings", JOSA A, 12(5), 1995 Lalanne, "Highly improved convergence of the coupled-wave method for TM polarization", JOSA A, 13(4), 1996 Moharam, "Stable implementation of the rigorous coupled-wave analysis for surface-relief gratings: enhanced trasmittance matrix approach", JOSA A, 12(5), 1995 """ self.wls = S.atleast_1d(wls) LAMBDA = self.LAMBDA n = self.n multilayer = self.multilayer alpha = self.alpha delta = self.delta psi = self.psi phi = self.phi nlayers = len(multilayer) i = S.arange(-n, n + 1) nood = 2 * n + 1 hmax = nood - 1 # grating vector (on the xz plane) # grating on the xy plane K = 2 * pi / LAMBDA * np.array( [S.sin(phi), 0., S.cos(phi)], dtype=complex) DE1 = S.zeros((nood, self.wls.size)) DE3 = S.zeros_like(DE1) dirk1 = np.array([ S.sin(alpha) * S.cos(delta), S.sin(alpha) * S.sin(delta), S.cos(alpha) ]) # usefull matrices I = S.eye(i.size) I2 = S.eye(i.size * 2) ZERO = S.zeros_like(I) X = S.zeros((2 * nood, 2 * nood, nlayers), dtype=complex) MTp1 = S.zeros((2 * nood, 2 * nood, nlayers), dtype=complex) MTp2 = S.zeros_like(MTp1) EPS2 = S.zeros(2 * hmax + 1, dtype=complex) EPS21 = S.zeros_like(EPS2) dlt = (i == 0).astype(int) for iwl, wl in enumerate(self.wls): # free space wavevector k = 2 * pi / wl n1 = multilayer[0].mat.n(wl).item() n3 = multilayer[-1].mat.n(wl).item() # incident plane wave wavevector k1 = k * n1 * dirk1 # all the other wavevectors tmp_x = k1[0] - i * K[0] tmp_y = k1[1] * S.ones_like(i) tmp_z = dispersion_relation_ordinary(tmp_x, tmp_y, k, n1) k1i = S.r_[[tmp_x], [tmp_y], [tmp_z]] # k2i = S.r_[[k1[0] - i*K[0]], [k1[1] - i * K[1]], [-i * K[2]]] tmp_z = dispersion_relation_ordinary(tmp_x, tmp_y, k, n3) k3i = S.r_[[k1i[0, :]], [k1i[1, :]], [tmp_z]] # aliases for constant wavevectors kx = k1i[0, :] ky = k1[1] # angles of reflection # phi_i = S.arctan2(ky,kx) phi_i = S.arctan2(ky, kx.real) # OKKIO Kx = S.diag(kx / k) Ky = ky / k * I Z1 = S.diag(k1i[2, :] / (k * n1**2)) Y1 = S.diag(k1i[2, :] / k) Z3 = S.diag(k3i[2, :] / (k * n3**2)) Y3 = S.diag(k3i[2, :] / k) # Fc = S.diag(S.cos(phi_i)) fc = S.cos(phi_i) # Fs = S.diag(S.sin(phi_i)) fs = S.sin(phi_i) MR = S.asarray( S.bmat([[I, ZERO], [-1j * Y1, ZERO], [ZERO, I], [ZERO, -1j * Z1]])) MT = S.asarray( S.bmat([[I, ZERO], [1j * Y3, ZERO], [ZERO, I], [ZERO, 1j * Z3]])) # internal layers (grating or layer) X.fill(0.0) MTp1.fill(0.0) MTp2.fill(0.0) for nlayer in range(nlayers - 2, 0, -1): # internal layers layer = multilayer[nlayer] d = layer.thickness EPS2, EPS21 = layer.getEPSFourierCoeffs(wl, n, anisotropic=False) E = toeplitz(EPS2[hmax::-1], EPS2[hmax:]) E1 = toeplitz(EPS21[hmax::-1], EPS21[hmax:]) E11 = inv(E1) # B = S.dot(Kx, linsolve(E,Kx)) - I B = kx[:, S.newaxis] / k * linsolve(E, Kx) - I # A = S.dot(Kx, Kx) - E A = S.diag((kx / k)**2) - E # Note: solution bug alfredo # randomizzo Kx un po' a caso finche' cond(A) e' piccolo (<1e10) # soluzione sporca... :-( # per certi kx, l'operatore di helmholtz ha 2 autovalori nulli e A, B # non sono invertibili --> cambio leggermente i kx... ma dovrei invece # trattare separatamente (analiticamente) questi casi if cond(A) > 1e10: warning("BAD CONDITIONING: randomization of kx") while cond(A) > 1e10: Kx = Kx * (1 + 1e-9 * S.rand()) B = kx[:, S.newaxis] / k * linsolve(E, Kx) - I A = S.diag((kx / k)**2) - E if S.absolute(K[2] / k) > 1e-10: raise ValueError( "First Order Helmholtz Operator not implemented, yet!") elif ky == 0 or S.allclose(S.diag(Ky / ky * k), 1): # lalanne # H_U_reduced = S.dot(Ky, Ky) + A H_U_reduced = (ky / k)**2 * I + A # H_S_reduced = S.dot(Ky, Ky) + S.dot(Kx, linsolve(E, S.dot(Kx, E11))) - E11 H_S_reduced = ((ky / k)**2 * I + kx[:, S.newaxis] / k * linsolve(E, kx[:, S.newaxis] / k * E11) - E11) q1, W1 = eig(H_U_reduced) q1 = S.sqrt(q1) q2, W2 = eig(H_S_reduced) q2 = S.sqrt(q2) # boundary conditions # V11 = S.dot(linsolve(A, W1), S.diag(q1)) V11 = linsolve(A, W1) * q1[S.newaxis, :] V12 = (ky / k) * S.dot(linsolve(A, Kx), W2) V21 = (ky / k) * S.dot(linsolve(B, Kx), linsolve(E, W1)) # V22 = S.dot(linsolve(B, W2), S.diag(q2)) V22 = linsolve(B, W2) * q2[S.newaxis, :] # Vss = S.dot(Fc, V11) Vss = fc[:, S.newaxis] * V11 # Wss = S.dot(Fc, W1) + S.dot(Fs, V21) Wss = fc[:, S.newaxis] * W1 + fs[:, S.newaxis] * V21 # Vsp = S.dot(Fc, V12) - S.dot(Fs, W2) Vsp = fc[:, S.newaxis] * V12 - fs[:, S.newaxis] * W2 # Wsp = S.dot(Fs, V22) Wsp = fs[:, S.newaxis] * V22 # Wpp = S.dot(Fc, V22) Wpp = fc[:, S.newaxis] * V22 # Vpp = S.dot(Fc, W2) + S.dot(Fs, V12) Vpp = fc[:, S.newaxis] * W2 + fs[:, S.newaxis] * V12 # Wps = S.dot(Fc, V21) - S.dot(Fs, W1) Wps = fc[:, S.newaxis] * V21 - fs[:, S.newaxis] * W1 # Vps = S.dot(Fs, V11) Vps = fs[:, S.newaxis] * V11 Mc2bar = S.asarray( S.bmat([ [Vss, Vsp, Vss, Vsp], [Wss, Wsp, -Wss, -Wsp], [Wps, Wpp, -Wps, -Wpp], [Vps, Vpp, Vps, Vpp], ])) x = S.r_[S.exp(-k * q1 * d), S.exp(-k * q2 * d)] # Mc1 = S.dot(Mc2bar, S.diag(S.r_[S.ones_like(x), x])) xx = S.r_[S.ones_like(x), x] Mc1 = Mc2bar * xx[S.newaxis, :] X[:, :, nlayer] = S.diag(x) MTp = linsolve(Mc2bar, MT) MTp1[:, :, nlayer] = MTp[0:2 * nood, :] MTp2 = MTp[2 * nood:, :] MT = S.dot( Mc1, S.r_[I2, S. dot(MTp2, linsolve(MTp1[:, :, nlayer], X[:, :, nlayer])), ], ) else: ValueError( "Second Order Helmholtz Operator not implemented, yet!" ) # M = S.asarray(S.bmat([-MR, MT])) M = S.c_[-MR, MT] b = S.r_[S.sin(psi) * dlt, 1j * S.sin(psi) * n1 * S.cos(alpha) * dlt, -1j * S.cos(psi) * n1 * dlt, S.cos(psi) * S.cos(alpha) * dlt, ] x = linsolve(M, b) R, T = S.split(x, 2) Rs, Rp = S.split(R, 2) for ii in range(1, nlayers - 1): T = S.dot(linsolve(MTp1[:, :, ii], X[:, :, ii]), T) Ts, Tp = S.split(T, 2) DE1[:, iwl] = (k1i[2, :] / (k1[2])).real * S.absolute(Rs)**2 + ( k1i[2, :] / (k1[2] * n1**2)).real * S.absolute(Rp)**2 DE3[:, iwl] = (k3i[2, :] / (k1[2])).real * S.absolute(Ts)**2 + ( k3i[2, :] / (k1[2] * n3**2)).real * S.absolute(Tp)**2 # save the results self.DE1 = DE1 self.DE3 = DE3 return self
def solve(self, wls): """Anisotropic solver. INPUT wls = wavelengths to scan (any asarray-able object). OUTPUT self.DEO1, self.DEE1, self.DEO3, self.DEE3 = power reflected and transmitted. """ self.wls = S.atleast_1d(wls) LAMBDA = self.LAMBDA n = self.n multilayer = self.multilayer alpha = self.alpha delta = self.delta psi = self.psi phi = self.phi nlayers = len(multilayer) i = S.arange(-n, n + 1) nood = 2 * n + 1 hmax = nood - 1 DEO1 = S.zeros((nood, self.wls.size)) DEO3 = S.zeros_like(DEO1) DEE1 = S.zeros_like(DEO1) DEE3 = S.zeros_like(DEO1) c1 = S.array([1., 0., 0.]) c3 = S.array([1., 0., 0.]) # grating on the xy plane K = 2 * pi / LAMBDA * \ S.array([S.sin(phi), 0., S.cos(phi)], dtype=complex) dirk1 = S.array([S.sin(alpha) * S.cos(delta), S.sin(alpha) * S.sin(delta), S.cos(alpha)]) # D polarization vector u = S.array([S.cos(psi) * S.cos(alpha) * S.cos(delta) - S.sin(psi) * S.sin(delta), S.cos(psi) * S.cos(alpha) * S.sin(delta) + S.sin(psi) * S.cos(delta), -S.cos(psi) * S.sin(alpha)]) kO1i = S.zeros((3, i.size), dtype=complex) kE1i = S.zeros_like(kO1i) kO3i = S.zeros_like(kO1i) kE3i = S.zeros_like(kO1i) Mp = S.zeros((4 * nood, 4 * nood, nlayers), dtype=complex) M = S.zeros((4 * nood, 4 * nood, nlayers), dtype=complex) dlt = (i == 0).astype(int) for iwl, wl in enumerate(self.wls): nO1 = nE1 = multilayer[0].mat.n(wl).item() nO3 = nE3 = multilayer[-1].mat.n(wl).item() # wavevectors k = 2 * pi / wl eps1 = S.diag(S.asarray([nE1, nO1, nO1]) ** 2) eps3 = S.diag(S.asarray([nE3, nO3, nO3]) ** 2) # ordinary wave abskO1 = k * nO1 # abskO3 = k * nO3 # extraordinary wave # abskE1 = k * nO1 *nE1 / S.sqrt(nO1**2 + (nE1**2 - nO1**2) * S.dot(-c1, dirk1)**2) # abskE3 = k * nO3 *nE3 / S.sqrt(nO3**2 + (nE3**2 - nO3**2) * S.dot(-c3, dirk1)**2) k1 = abskO1 * dirk1 kO1i[0, :] = k1[0] - i * K[0] kO1i[1, :] = k1[1] * S.ones_like(i) kO1i[2, :] = - \ dispersion_relation_ordinary(kO1i[0, :], kO1i[1, :], k, nO1) kE1i[0, :] = kO1i[0, :] kE1i[1, :] = kO1i[1, :] kE1i[2, :] = -dispersion_relation_extraordinary(kE1i[0, :], kE1i[1, :], k, nO1, nE1, c1) kO3i[0, :] = kO1i[0, :] kO3i[1, :] = kO1i[1, :] kO3i[ 2, :] = dispersion_relation_ordinary( kO3i[ 0, :], kO3i[ 1, :], k, nO3) kE3i[0, :] = kO1i[0, :] kE3i[1, :] = kO1i[1, :] kE3i[ 2, :] = dispersion_relation_extraordinary( kE3i[ 0, :], kE3i[ 1, :], k, nO3, nE3, c3) # k2i = S.r_[[k1[0] - i * K[0]], [k1[1] - i * K[1]], [k1[2] - i * K[2]]] k2i = S.r_[[k1[0] - i * K[0]], [k1[1] - i * K[1]], [- i * K[2]]] # aliases for constant wavevectors kx = kO1i[0, :] # o kE1i(1,;), tanto e' lo stesso ky = k1[1] # matrices I = S.eye(nood, dtype=complex) ZERO = S.zeros((nood, nood), dtype=complex) Kx = S.diag(kx / k) Ky = ky / k * I Kz = S.diag(k2i[2, :] / k) KO1z = S.diag(kO1i[2, :] / k) KE1z = S.diag(kE1i[2, :] / k) KO3z = S.diag(kO3i[2, :] / k) KE3z = S.diag(kE3i[2, :] / k) ARO = Kx * eps1[0, 0] + Ky * eps1[1, 0] + KO1z * eps1[2, 0] BRO = Kx * eps1[0, 1] + Ky * eps1[1, 1] + KO1z * eps1[2, 1] CRO_1 = inv(Kx * eps1[0, 2] + Ky * eps1[1, 2] + KO1z * eps1[2, 2]) ARE = Kx * eps1[0, 0] + Ky * eps1[1, 0] + KE1z * eps1[2, 0] BRE = Kx * eps1[0, 1] + Ky * eps1[1, 1] + KE1z * eps1[2, 1] CRE_1 = inv(Kx * eps1[0, 2] + Ky * eps1[1, 2] + KE1z * eps1[2, 2]) ATO = Kx * eps3[0, 0] + Ky * eps3[1, 0] + KO3z * eps3[2, 0] BTO = Kx * eps3[0, 1] + Ky * eps3[1, 1] + KO3z * eps3[2, 1] CTO_1 = inv(Kx * eps3[0, 2] + Ky * eps3[1, 2] + KO3z * eps3[2, 2]) ATE = Kx * eps3[0, 0] + Ky * eps3[1, 0] + KE3z * eps3[2, 0] BTE = Kx * eps3[0, 1] + Ky * eps3[1, 1] + KE3z * eps3[2, 1] CTE_1 = inv(Kx * eps3[0, 2] + Ky * eps3[1, 2] + KE3z * eps3[2, 2]) DRE = c1[1] * KE1z - c1[2] * Ky ERE = c1[2] * Kx - c1[0] * KE1z FRE = c1[0] * Ky - c1[1] * Kx DTE = c3[1] * KE3z - c3[2] * Ky ETE = c3[2] * Kx - c3[0] * KE3z FTE = c3[0] * Ky - c3[1] * Kx b = S.r_[u[0] * dlt, u[1] * dlt, (k1[1] / k * u[2] - k1[2] / k * u[1]) * dlt, ( k1[2] / k * u[0] - k1[0] / k * u[2]) * dlt] Ky_CRO_1 = ky / k * CRO_1 Ky_CRE_1 = ky / k * CRE_1 Kx_CRO_1 = kx[:, S.newaxis] / k * CRO_1 Kx_CRE_1 = kx[:, S.newaxis] / k * CRE_1 MR31 = -S.dot(Ky_CRO_1, ARO) MR32 = -S.dot(Ky_CRO_1, BRO) - KO1z MR33 = -S.dot(Ky_CRE_1, ARE) MR34 = -S.dot(Ky_CRE_1, BRE) - KE1z MR41 = S.dot(Kx_CRO_1, ARO) + KO1z MR42 = S.dot(Kx_CRO_1, BRO) MR43 = S.dot(Kx_CRE_1, ARE) + KE1z MR44 = S.dot(Kx_CRE_1, BRE) MR = S.asarray(S.bmat([[I, ZERO, I, ZERO], [ZERO, I, ZERO, I], [MR31, MR32, MR33, MR34], [MR41, MR42, MR43, MR44]])) Ky_CTO_1 = ky / k * CTO_1 Ky_CTE_1 = ky / k * CTE_1 Kx_CTO_1 = kx[:, S.newaxis] / k * CTO_1 Kx_CTE_1 = kx[:, S.newaxis] / k * CTE_1 MT31 = -S.dot(Ky_CTO_1, ATO) MT32 = -S.dot(Ky_CTO_1, BTO) - KO3z MT33 = -S.dot(Ky_CTE_1, ATE) MT34 = -S.dot(Ky_CTE_1, BTE) - KE3z MT41 = S.dot(Kx_CTO_1, ATO) + KO3z MT42 = S.dot(Kx_CTO_1, BTO) MT43 = S.dot(Kx_CTE_1, ATE) + KE3z MT44 = S.dot(Kx_CTE_1, BTE) MT = S.asarray(S.bmat([[I, ZERO, I, ZERO], [ZERO, I, ZERO, I], [MT31, MT32, MT33, MT34], [MT41, MT42, MT43, MT44]])) Mp.fill(0.0) M.fill(0.0) for nlayer in range(nlayers - 2, 0, -1): # internal layers layer = multilayer[nlayer] thickness = layer.thickness EPS2, EPS21 = layer.getEPSFourierCoeffs( wl, n, anisotropic=True) # Exx = S.squeeze(EPS2[0, 0, :]) # Exx = toeplitz(S.flipud(Exx[0:hmax + 1]), Exx[hmax:]) Exy = S.squeeze(EPS2[0, 1, :]) Exy = toeplitz(S.flipud(Exy[0:hmax + 1]), Exy[hmax:]) Exz = S.squeeze(EPS2[0, 2, :]) Exz = toeplitz(S.flipud(Exz[0:hmax + 1]), Exz[hmax:]) Eyx = S.squeeze(EPS2[1, 0, :]) Eyx = toeplitz(S.flipud(Eyx[0:hmax + 1]), Eyx[hmax:]) Eyy = S.squeeze(EPS2[1, 1, :]) Eyy = toeplitz(S.flipud(Eyy[0:hmax + 1]), Eyy[hmax:]) Eyz = S.squeeze(EPS2[1, 2, :]) Eyz = toeplitz(S.flipud(Eyz[0:hmax + 1]), Eyz[hmax:]) Ezx = S.squeeze(EPS2[2, 0, :]) Ezx = toeplitz(S.flipud(Ezx[0:hmax + 1]), Ezx[hmax:]) Ezy = S.squeeze(EPS2[2, 1, :]) Ezy = toeplitz(S.flipud(Ezy[0:hmax + 1]), Ezy[hmax:]) Ezz = S.squeeze(EPS2[2, 2, :]) Ezz = toeplitz(S.flipud(Ezz[0:hmax + 1]), Ezz[hmax:]) Exx_1 = S.squeeze(EPS21[0, 0, :]) Exx_1 = toeplitz(S.flipud(Exx_1[0:hmax + 1]), Exx_1[hmax:]) Exx_1_1 = inv(Exx_1) # lalanne Ezz_1 = inv(Ezz) Ky_Ezz_1 = ky / k * Ezz_1 Kx_Ezz_1 = kx[:, S.newaxis] / k * Ezz_1 Exz_Ezz_1 = S.dot(Exz, Ezz_1) Eyz_Ezz_1 = S.dot(Eyz, Ezz_1) H11 = 1j * S.dot(Ky_Ezz_1, Ezy) H12 = 1j * S.dot(Ky_Ezz_1, Ezx) H13 = S.dot(Ky_Ezz_1, Kx) H14 = I - S.dot(Ky_Ezz_1, Ky) H21 = 1j * S.dot(Kx_Ezz_1, Ezy) H22 = 1j * S.dot(Kx_Ezz_1, Ezx) H23 = S.dot(Kx_Ezz_1, Kx) - I H24 = -S.dot(Kx_Ezz_1, Ky) H31 = S.dot(Kx, Ky) + Exy - S.dot(Exz_Ezz_1, Ezy) H32 = Exx_1_1 - S.dot(Ky, Ky) - S.dot(Exz_Ezz_1, Ezx) H33 = 1j * S.dot(Exz_Ezz_1, Kx) H34 = -1j * S.dot(Exz_Ezz_1, Ky) H41 = S.dot(Kx, Kx) - Eyy + S.dot(Eyz_Ezz_1, Ezy) H42 = -S.dot(Kx, Ky) - Eyx + S.dot(Eyz_Ezz_1, Ezx) H43 = -1j * S.dot(Eyz_Ezz_1, Kx) H44 = 1j * S.dot(Eyz_Ezz_1, Ky) H = 1j * S.diag(S.repeat(S.diag(Kz), 4)) + \ S.asarray(S.bmat([[H11, H12, H13, H14], [H21, H22, H23, H24], [H31, H32, H33, H34], [H41, H42, H43, H44]])) q, W = eig(H) W1, W2, W3, W4 = S.split(W, 4) # # boundary conditions # # x = [R T] # R = [ROx ROy REx REy] # T = [TOx TOy TEx TEy] # b + MR.R = M1p.c # M1.c = M2p.c # ... # ML.c = MT.T # therefore: b + MR.R = (M1p.M1^-1.M2p.M2^-1. ...).MT.T # missing equations from (46)..(49) in glytsis_rigorous # [b] = [-MR Mtot.MT] [R] # [0] [...........] [T] z = S.zeros_like(q) z[S.where(q.real > 0)] = -thickness D = S.exp(k * q * z) Sy0 = W1 * D[S.newaxis, :] Sx0 = W2 * D[S.newaxis, :] Uy0 = W3 * D[S.newaxis, :] Ux0 = W4 * D[S.newaxis, :] z = thickness * S.ones_like(q) z[S.where(q.real > 0)] = 0 D = S.exp(k * q * z) D1 = S.exp(-1j * k2i[2, :] * thickness) Syd = D1[:, S.newaxis] * W1 * D[S.newaxis, :] Sxd = D1[:, S.newaxis] * W2 * D[S.newaxis, :] Uyd = D1[:, S.newaxis] * W3 * D[S.newaxis, :] Uxd = D1[:, S.newaxis] * W4 * D[S.newaxis, :] Mp[:, :, nlayer] = S.r_[Sx0, Sy0, -1j * Ux0, -1j * Uy0] M[:, :, nlayer] = S.r_[Sxd, Syd, -1j * Uxd, -1j * Uyd] Mtot = S.eye(4 * nood, dtype=complex) for nlayer in range(1, nlayers - 1): Mtot = S.dot( S.dot(Mtot, Mp[:, :, nlayer]), inv(M[:, :, nlayer])) BC_b = S.r_[b, S.zeros_like(b)] BC_A1 = S.c_[-MR, S.dot(Mtot, MT)] BC_A2 = S.asarray(S.bmat( [[(c1[0] * I - c1[2] * S.dot(CRO_1, ARO)), (c1[1] * I - c1[2] * S.dot(CRO_1, BRO)), ZERO, ZERO, ZERO, ZERO, ZERO, ZERO], [ZERO, ZERO, (DRE - S.dot(S.dot(FRE, CRE_1), ARE)), (ERE - S.dot(S.dot(FRE, CRE_1), BRE)), ZERO, ZERO, ZERO, ZERO], [ZERO, ZERO, ZERO, ZERO, (c3[0] * I - c3[2] * S.dot(CTO_1, ATO)), (c3[1] * I - c3[2] * S.dot(CTO_1, BTO)), ZERO, ZERO], [ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, (DTE - S.dot(S.dot(FTE, CTE_1), ATE)), (ETE - S.dot(S.dot(FTE, CTE_1), BTE))]])) BC_A = S.r_[BC_A1, BC_A2] x = linsolve(BC_A, BC_b) ROx, ROy, REx, REy, TOx, TOy, TEx, TEy = S.split(x, 8) ROz = -S.dot(CRO_1, (S.dot(ARO, ROx) + S.dot(BRO, ROy))) REz = -S.dot(CRE_1, (S.dot(ARE, REx) + S.dot(BRE, REy))) TOz = -S.dot(CTO_1, (S.dot(ATO, TOx) + S.dot(BTO, TOy))) TEz = -S.dot(CTE_1, (S.dot(ATE, TEx) + S.dot(BTE, TEy))) denom = (k1[2] - S.dot(u, k1) * u[2]).real DEO1[:, iwl] = -((S.absolute(ROx) ** 2 + S.absolute(ROy) ** 2 + S.absolute(ROz) ** 2) * S.conj(kO1i[2, :]) - (ROx * kO1i[0, :] + ROy * kO1i[1, :] + ROz * kO1i[2, :]) * S.conj(ROz)).real / denom DEE1[:, iwl] = -((S.absolute(REx) ** 2 + S.absolute(REy) ** 2 + S.absolute(REz) ** 2) * S.conj(kE1i[2, :]) - (REx * kE1i[0, :] + REy * kE1i[1, :] + REz * kE1i[2, :]) * S.conj(REz)).real / denom DEO3[:, iwl] = ((S.absolute(TOx) ** 2 + S.absolute(TOy) ** 2 + S.absolute(TOz) ** 2) * S.conj(kO3i[2, :]) - (TOx * kO3i[0, :] + TOy * kO3i[1, :] + TOz * kO3i[2, :]) * S.conj(TOz)).real / denom DEE3[:, iwl] = ((S.absolute(TEx) ** 2 + S.absolute(TEy) ** 2 + S.absolute(TEz) ** 2) * S.conj(kE3i[2, :]) - (TEx * kE3i[0, :] + TEy * kE3i[1, :] + TEz * kE3i[2, :]) * S.conj(TEz)).real / denom # save the results self.DEO1 = DEO1 self.DEE1 = DEE1 self.DEO3 = DEO3 self.DEE3 = DEE3 return self
def solve(self, wls): """Isotropic solver. INPUT wls = wavelengths to scan (any asarray-able object). OUTPUT self.DE1, self.DE3 = power reflected and transmitted. NOTE see: Moharam, "Formulation for stable and efficient implementation of the rigorous coupled-wave analysis of binary gratings", JOSA A, 12(5), 1995 Lalanne, "Highly improved convergence of the coupled-wave method for TM polarization", JOSA A, 13(4), 1996 Moharam, "Stable implementation of the rigorous coupled-wave analysis for surface-relief gratings: enhanced trasmittance matrix approach", JOSA A, 12(5), 1995 """ self.wls = S.atleast_1d(wls) LAMBDA = self.LAMBDA n = self.n multilayer = self.multilayer alpha = self.alpha delta = self.delta psi = self.psi phi = self.phi nlayers = len(multilayer) i = S.arange(-n, n + 1) nood = 2 * n + 1 hmax = nood - 1 # grating vector (on the xz plane) # grating on the xy plane K = 2 * pi / LAMBDA * \ S.array([S.sin(phi), 0., S.cos(phi)], dtype=complex) DE1 = S.zeros((nood, self.wls.size)) DE3 = S.zeros_like(DE1) dirk1 = S.array([S.sin(alpha) * S.cos(delta), S.sin(alpha) * S.sin(delta), S.cos(alpha)]) # usefull matrices I = S.eye(i.size) I2 = S.eye(i.size * 2) ZERO = S.zeros_like(I) X = S.zeros((2 * nood, 2 * nood, nlayers), dtype=complex) MTp1 = S.zeros((2 * nood, 2 * nood, nlayers), dtype=complex) MTp2 = S.zeros_like(MTp1) EPS2 = S.zeros(2 * hmax + 1, dtype=complex) EPS21 = S.zeros_like(EPS2) dlt = (i == 0).astype(int) for iwl, wl in enumerate(self.wls): # free space wavevector k = 2 * pi / wl n1 = multilayer[0].mat.n(wl).item() n3 = multilayer[-1].mat.n(wl).item() # incident plane wave wavevector k1 = k * n1 * dirk1 # all the other wavevectors tmp_x = k1[0] - i * K[0] tmp_y = k1[1] * S.ones_like(i) tmp_z = dispersion_relation_ordinary(tmp_x, tmp_y, k, n1) k1i = S.r_[[tmp_x], [tmp_y], [tmp_z]] # k2i = S.r_[[k1[0] - i*K[0]], [k1[1] - i * K[1]], [-i * K[2]]] tmp_z = dispersion_relation_ordinary(tmp_x, tmp_y, k, n3) k3i = S.r_[[k1i[0, :]], [k1i[1, :]], [tmp_z]] # aliases for constant wavevectors kx = k1i[0, :] ky = k1[1] # angles of reflection # phi_i = S.arctan2(ky,kx) phi_i = S.arctan2(ky, kx.real) # OKKIO Kx = S.diag(kx / k) Ky = ky / k * I Z1 = S.diag(k1i[2, :] / (k * n1 ** 2)) Y1 = S.diag(k1i[2, :] / k) Z3 = S.diag(k3i[2, :] / (k * n3 ** 2)) Y3 = S.diag(k3i[2, :] / k) # Fc = S.diag(S.cos(phi_i)) fc = S.cos(phi_i) # Fs = S.diag(S.sin(phi_i)) fs = S.sin(phi_i) MR = S.asarray(S.bmat([[I, ZERO], [-1j * Y1, ZERO], [ZERO, I], [ZERO, -1j * Z1]])) MT = S.asarray(S.bmat([[I, ZERO], [1j * Y3, ZERO], [ZERO, I], [ZERO, 1j * Z3]])) # internal layers (grating or layer) X.fill(0.0) MTp1.fill(0.0) MTp2.fill(0.0) for nlayer in range(nlayers - 2, 0, -1): # internal layers layer = multilayer[nlayer] d = layer.thickness EPS2, EPS21 = layer.getEPSFourierCoeffs( wl, n, anisotropic=False) E = toeplitz(EPS2[hmax::-1], EPS2[hmax:]) E1 = toeplitz(EPS21[hmax::-1], EPS21[hmax:]) E11 = inv(E1) # B = S.dot(Kx, linsolve(E,Kx)) - I B = kx[:, S.newaxis] / k * linsolve(E, Kx) - I # A = S.dot(Kx, Kx) - E A = S.diag((kx / k) ** 2) - E # Note: solution bug alfredo # randomizzo Kx un po' a caso finche' cond(A) e' piccolo (<1e10) # soluzione sporca... :-( # per certi kx, l'operatore di helmholtz ha 2 autovalori nulli e A, B # non sono invertibili --> cambio leggermente i kx... ma dovrei invece # trattare separatamente (analiticamente) questi casi if cond(A) > 1e10: warning('BAD CONDITIONING: randomization of kx') while cond(A) > 1e10: Kx = Kx * (1 + 1e-9 * S.rand()) B = kx[:, S.newaxis] / k * linsolve(E, Kx) - I A = S.diag((kx / k) ** 2) - E if S.absolute(K[2] / k) > 1e-10: raise ValueError( 'First Order Helmholtz Operator not implemented, yet!') elif ky == 0 or S.allclose(S.diag(Ky / ky * k), 1): # lalanne # H_U_reduced = S.dot(Ky, Ky) + A H_U_reduced = (ky / k) ** 2 * I + A # H_S_reduced = S.dot(Ky, Ky) + S.dot(Kx, linsolve(E, S.dot(Kx, E11))) - E11 H_S_reduced = (ky / k) ** 2 * I + kx[:, S.newaxis] / k * linsolve(E, kx[:, S.newaxis] / k * E11) - E11 q1, W1 = eig(H_U_reduced) q1 = S.sqrt(q1) q2, W2 = eig(H_S_reduced) q2 = S.sqrt(q2) # boundary conditions # V11 = S.dot(linsolve(A, W1), S.diag(q1)) V11 = linsolve(A, W1) * q1[S.newaxis, :] V12 = (ky / k) * S.dot(linsolve(A, Kx), W2) V21 = (ky / k) * S.dot(linsolve(B, Kx), linsolve(E, W1)) # V22 = S.dot(linsolve(B, W2), S.diag(q2)) V22 = linsolve(B, W2) * q2[S.newaxis, :] # Vss = S.dot(Fc, V11) Vss = fc[:, S.newaxis] * V11 # Wss = S.dot(Fc, W1) + S.dot(Fs, V21) Wss = fc[:, S.newaxis] * W1 + fs[:, S.newaxis] * V21 # Vsp = S.dot(Fc, V12) - S.dot(Fs, W2) Vsp = fc[:, S.newaxis] * V12 - fs[:, S.newaxis] * W2 # Wsp = S.dot(Fs, V22) Wsp = fs[:, S.newaxis] * V22 # Wpp = S.dot(Fc, V22) Wpp = fc[:, S.newaxis] * V22 # Vpp = S.dot(Fc, W2) + S.dot(Fs, V12) Vpp = fc[:, S.newaxis] * W2 + fs[:, S.newaxis] * V12 # Wps = S.dot(Fc, V21) - S.dot(Fs, W1) Wps = fc[:, S.newaxis] * V21 - fs[:, S.newaxis] * W1 # Vps = S.dot(Fs, V11) Vps = fs[:, S.newaxis] * V11 Mc2bar = S.asarray(S.bmat([[Vss, Vsp, Vss, Vsp], [Wss, Wsp, -Wss, -Wsp], [Wps, Wpp, -Wps, -Wpp], [Vps, Vpp, Vps, Vpp]])) x = S.r_[S.exp(-k * q1 * d), S.exp(-k * q2 * d)] # Mc1 = S.dot(Mc2bar, S.diag(S.r_[S.ones_like(x), x])) xx = S.r_[S.ones_like(x), x] Mc1 = Mc2bar * xx[S.newaxis, :] X[:, :, nlayer] = S.diag(x) MTp = linsolve(Mc2bar, MT) MTp1[:, :, nlayer] = MTp[0:2 * nood, :] MTp2 = MTp[2 * nood:, :] MT = S.dot( Mc1, S.r_[ I2, S.dot( MTp2, linsolve( MTp1[ :, :, nlayer], X[ :, :, nlayer]))]) else: ValueError( 'Second Order Helmholtz Operator not implemented, yet!') # M = S.asarray(S.bmat([-MR, MT])) M = S.c_[-MR, MT] b = S.r_[S.sin(psi) * dlt, 1j * S.sin(psi) * n1 * S.cos(alpha) * dlt, -1j * S.cos(psi) * n1 * dlt, S.cos(psi) * S.cos(alpha) * dlt] x = linsolve(M, b) R, T = S.split(x, 2) Rs, Rp = S.split(R, 2) for ii in range(1, nlayers - 1): T = S.dot(linsolve(MTp1[:, :, ii], X[:, :, ii]), T) Ts, Tp = S.split(T, 2) DE1[:, iwl] = (k1i[2, :] / (k1[2])).real * S.absolute(Rs) ** 2 + \ (k1i[2, :] / (k1[2] * n1 ** 2)).real * \ S.absolute(Rp) ** 2 DE3[:, iwl] = (k3i[2, :] / (k1[2])).real * S.absolute(Ts) ** 2 + \ (k3i[2, :] / (k1[2] * n3 ** 2)).real * \ S.absolute(Tp) ** 2 # save the results self.DE1 = DE1 self.DE3 = DE3 return self
def _LMLgrad_covar(self,covar,**kw_args): """ calculates LMLgrad for covariance parameters """ start = TIME.time() # precompute some stuff if covar=='Cr': n_params = self.Cr.getNumberParams() elif covar=='Cg': n_params = self.Cg.getNumberParams() elif covar=='Cn': n_params = self.Cn.getNumberParams() if covar=='Cr': trR = self.cache['trXrXr'] RY = self.cache['XrXrY'] RLY = self.cache['XrXrLY'] WrRY1 = self.cache['XrXrXrY'] WrRY2 = self.cache['XXrXrY'] WrRLY1 = self.cache['XrXrXrLY'] WrRLY2 = self.cache['XXrXrLY'] XrRXr = self.cache['XrXrXrXr'] XrRX = self.cache['XrXrXrX'] XRX = self.cache['XXrXrX'] elif covar=='Cg': trR = self.cache['trXX'] RY = self.cache['XXY'] RLY = self.cache['XXLY'] WrRY1 = self.cache['XrXXY'] WrRY2 = self.cache['XXXY'] WrRLY1 = self.cache['XrXXLY'] WrRLY2 = self.cache['XXXLY'] XrRXr = self.cache['XrXXXr'] XrRX = self.cache['XrXXX'] XRX = self.cache['XXXX'] else: trR = self.N RY = self.Y RLY = self.cache['LY'] WrRY1 = self.cache['XrY'] WrRY2 = self.cache['XY'] WrRLY1 = self.cache['XrLY'] WrRLY2 = self.cache['XLY'] XrRXr = self.cache['XrXr'] XrRX = self.cache['XXr'].T XRX = self.cache['XX'] smartSum(self.time,'lmlgrad_trace2_rKDW_%s'%covar,TIME.time()-start) smartSum(self.count,'lmlgrad_trace2_rKDW_%s'%covar,1) # fill gradient vector RV = SP.zeros(n_params) for i in range(n_params): #0. calc LCL if covar=='Cr': C = self.Cr.Kgrad_param(i) elif covar=='Cg': C = self.Cg.Kgrad_param(i) elif covar=='Cn': C = self.Cn.Kgrad_param(i) LCL = SP.dot(self.cache['Lc'],SP.dot(C,self.cache['Lc'].T)) ELCL = SP.dot(self.cache['Estar'].T,LCL) ELCLE = SP.dot(ELCL,self.cache['Estar']) ELCLCsh = SP.dot(ELCL,self.cache['CstarH']) CshLCL = SP.dot(self.cache['CstarH'].T,LCL) CshLCLCsh = SP.dot(CshLCL,self.cache['CstarH']) # WCoRW WCoRW11 = SP.kron(ELCLE,XrRXr) WCoRW12 = SP.kron(ELCLCsh,XrRX) WCoRW22 = SP.kron(CshLCLCsh,XRX) WCoRW = SP.array(SP.bmat([[WCoRW11,WCoRW12],[WCoRW12.T,WCoRW22]])) # WCoRLY WCoRLY1 = SP.dot(WrRLY1,ELCL.T) WCoRLY2 = SP.dot(WrRLY2,CshLCL.T) WCoRLY = SP.concatenate([SP.reshape(WCoRLY1,(WCoRLY1.size,1),order='F'), SP.reshape(WCoRLY2,(WCoRLY2.size,1),order='F')]) # CoRLY CoRLY = SP.dot(RLY,LCL.T) #1. der of log det start = TIME.time() trC = LCL.diagonal().sum() RV[i] = trC*trR RV[i]-= SP.sum(self.cache['Bi']*WCoRW) smartSum(self.time,'lmlgrad_trace2_WDKDW_%s'%covar,TIME.time()-start) smartSum(self.count,'lmlgrad_trace2_WDKDW_%s'%covar,1) #2. der of quad form start = TIME.time() RV[i] -= SP.sum(self.cache['LY']*CoRLY) RV[i] -= SP.sum(self.cache['BiWLY']*SP.dot(WCoRW,self.cache['BiWLY'])) RV[i] += 2*SP.sum(self.cache['BiWLY']*WCoRLY) smartSum(self.time,'lmlgrad_quadForm_%s'%covar,TIME.time()-start) smartSum(self.count,'lmlgrad_quadForm_%s'%covar,1) RV[i] *= 0.5 return RV
def _update_cache(self): """ Update cache """ cov_params_have_changed = self.Cr.params_have_changed or self.Cg.params_have_changed or self.Cn.params_have_changed if self.X_has_changed: self.cache['trXX'] = SP.sum(self.X**2) self.cache['XX'] = SP.dot(self.X.T,self.X) self.cache['XY'] = SP.dot(self.X.T,self.Y) self.cache['XXY'] = SP.dot(self.X,self.cache['XY']) self.cache['XXXY'] = SP.dot(self.cache['XX'],self.cache['XY']) self.cache['XXXX'] = SP.dot(self.cache['XX'],self.cache['XX']) if self.Xr_has_changed: self.cache['trXrXr'] = SP.sum(self.Xr**2) self.cache['XrXr'] = SP.dot(self.Xr.T,self.Xr) self.cache['XrY'] = SP.dot(self.Xr.T,self.Y) self.cache['XrXrY'] = SP.dot(self.Xr,self.cache['XrY']) self.cache['XrXrXrY'] = SP.dot(self.cache['XrXr'],self.cache['XrY']) self.cache['XrXrXrXr'] = SP.dot(self.cache['XrXr'],self.cache['XrXr']) if self.X_has_changed or self.Xr_has_changed: self.cache['XXr'] = SP.dot(self.X.T,self.Xr) self.cache['XXrXrY'] = SP.dot(self.cache['XXr'],self.cache['XrY']) self.cache['XXrXrX'] = SP.dot(self.cache['XXr'],self.cache['XXr'].T) self.cache['XrXXY'] = SP.dot(self.cache['XXr'].T,self.cache['XY']) self.cache['XrXXXr'] = SP.dot(self.cache['XXr'].T,self.cache['XXr']) self.cache['XrXrXrX'] = SP.dot(self.cache['XrXr'],self.cache['XXr'].T) self.cache['XrXXX'] = SP.dot(self.cache['XXr'].T,self.cache['XX']) if cov_params_have_changed: start = TIME.time() """ Col SVD Bg + Noise """ S2,U2 = LA.eigh(self.Cn.K()+self.offset*SP.eye(self.P)) self.cache['Sc2'] = S2 US2 = SP.dot(U2,SP.diag(SP.sqrt(S2))) USi2 = SP.dot(U2,SP.diag(SP.sqrt(1./S2))) self.cache['Lc'] = USi2.T self.cache['Cstar'] = SP.dot(USi2.T,SP.dot(self.Cg.K(),USi2)) self.cache['Scstar'],Ucstar = LA.eigh(self.cache['Cstar']) self.cache['CstarH'] = Ucstar*((self.cache['Scstar']**(0.5))[SP.newaxis,:]) E = SP.reshape(self.Cr.getParams(),(self.P,self.rank),order='F') self.cache['Estar'] = SP.dot(USi2.T,E) self.cache['CE'] = SP.dot(self.cache['CstarH'].T,self.cache['Estar']) self.cache['EE'] = SP.dot(self.cache['Estar'].T,self.cache['Estar']) if cov_params_have_changed or self.Y_has_changed: self.cache['LY'] = SP.dot(self.Y,self.cache['Lc'].T) if cov_params_have_changed or self.Xr_has_changed or self.Y_has_changed: self.cache['XrLY'] = SP.dot(self.cache['XrY'],self.cache['Lc'].T) self.cache['WLY1'] = SP.dot(self.cache['XrLY'],self.cache['Estar']) self.cache['XrXrLY'] = SP.dot(self.cache['XrXrY'],self.cache['Lc'].T) self.cache['XrXrXrLY'] = SP.dot(self.cache['XrXrXrY'],self.cache['Lc'].T) if cov_params_have_changed or self.X_has_changed or self.Y_has_changed: self.cache['XLY'] = SP.dot(self.cache['XY'],self.cache['Lc'].T) self.cache['WLY2'] = SP.dot(self.cache['XLY'],self.cache['CstarH']) self.cache['XXLY'] = SP.dot(self.cache['XXY'],self.cache['Lc'].T) self.cache['XXXLY'] = SP.dot(self.cache['XXXY'],self.cache['Lc'].T) if cov_params_have_changed or self.X_has_changed or self.Xr_has_changed: """ calculate B """ B11 = SP.kron(self.cache['EE'],self.cache['XrXr']) B11+= SP.eye(B11.shape[0]) B21 = SP.kron(self.cache['CE'],self.cache['XXr']) B22 = SP.kron(SP.diag(self.cache['Scstar']),self.cache['XX']) B22+= SP.eye(B22.shape[0]) B = SP.bmat([[B11,B21.T],[B21,B22]]) self.cache['cholB'] = LA.cholesky(B).T self.cache['Bi'] = LA.cho_solve((self.cache['cholB'],True),SP.eye(B.shape[0])) if cov_params_have_changed or self.X_has_changed or self.Xr_has_changed or self.Y_has_changed: self.cache['WLY'] = SP.concatenate([SP.reshape(self.cache['WLY1'],(self.cache['WLY1'].size,1),order='F'), SP.reshape(self.cache['WLY2'],(self.cache['WLY2'].size,1),order='F')]) self.cache['BiWLY'] = SP.dot(self.cache['Bi'],self.cache['WLY']) self.cache['XXrXrLY'] = SP.dot(self.cache['XXrXrY'],self.cache['Lc'].T) self.cache['XrXXLY'] = SP.dot(self.cache['XrXXY'],self.cache['Lc'].T) self.Xr_has_changed = False self.X_has_changed = False self.Y_has_changed = False self.Cr.params_have_changed = False self.Cg.params_have_changed = False self.Cn.params_have_changed = False
def interp_decomp(A, k=None, mode='column', index_set=False): """ Interpolative decomposition (ID). Algorithm for computing the low-rank ID decomposition of a rectangular `(m, n)` matrix `A`, with target rank `k << min{m, n}`. Input matrix is factored as `A = C * V`, using the column pivoted QR decomposition. The factor matrix `C` is formed of a subset of columns of `A`, also called the partial column skeleton. The factor matrix `V` contains a `(k, k)` identity matrix as a submatrix, and is well-conditioned. If `mode='row'`, then the input matrix is factored as `A = Z * R`, using the row pivoted QR decomposition. The factor matrix `R` is now formed as a subset of rows of `A`, also called the partial row skeleton. Parameters ---------- A : array_like, shape `(m, n)`. Input matrix. k : integer, `k << min{m,n}`. Target rank. mode: str `{'column', 'row'}`, default: `mode='column'`. 'column' : ID using column pivoted QR. 'row' : ID using row pivoted QR. index_set: str `{'True', 'False'}`, default: `index_set='False'`. 'True' : Return column/row index set instead of `C` or `R`. Returns ------- If `mode='column'`: C: array_like, shape `(m, k)`. Partial column skeleton. V : array_like, shape `(k, n)`. Well-conditioned matrix. If `mode='row'`: Z: array_like, shape `(m, k)`. Well-conditioned matrix. R : array_like, shape `(k, n)`. Partial row skeleton. Notes ----- References ---------- S. Voronin and P.Martinsson. "RSVDPACK: Subroutines for computing partial singular value decompositions via randomized sampling on single core, multi core, and GPU architectures" (2015). (available at `arXiv <http://arxiv.org/abs/1502.05366>`_). Examples -------- """ # Shape of input matrix m, n = A.shape dat_type = A.dtype if dat_type == sci.float32: isreal = True real_type = sci.float32 fT = rT elif dat_type == sci.float64: isreal = True real_type = sci.float64 fT = rT elif dat_type == sci.complex64: isreal = False real_type = sci.float32 fT = cT elif dat_type == sci.complex128: isreal = False real_type = sci.float64 fT = cT else: raise ValueError("A.dtype is not supported") if k is None: raise ValueError("Target rank k is required.") if k < 0: raise ValueError("Target rank k not valid.") if k > min(m, n): k = min(m, n) if mode == 'row': A = rT(A) m, n = A.shape #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #Pivoted QR decomposition #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Q, R, P = sci.linalg.qr(A, mode='economic', overwrite_a=False, pivoting=True) # Select column subset C = A[:, P[0:k]] # Compute V T = sci.linalg.pinv2(R[0:k, 0:k]).dot(R[0:k, k:n]) V = sci.bmat([[np.eye(k), T]]) V = V[:, sci.argsort(P)] #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return ID #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if mode == 'column': if index_set == False: return (C, V) else: return (P[0:k], V) if mode == 'row': if index_set == False: return (rT(V), rT(C)) else: return (rT(V), P[0:k])