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): """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