def run_RCWA_2D(lam0, theta, phi, ER, UR, layer_thicknesses, lattice_constants, pte, ptm, N, M, e_half): ''' :param lam0: :param theta: incident angle :param phi: incident angle (azimuthal) :param ER: list of convolution matrices for each layer :param UR: list of convolution matrices for each layer :param layer_thicknesses: list of thicknesses of each layer :param lattice_constants: [Lx, Ly] 2 element array containing lattice constants of the 2D unit cell :param pte: te mode amplitude :param ptm: tm mode amplitude :param N: num orders for x direction :param M: num orders for y direction :param e_half: [e_r e_t], dielectric constants of the reflection and transmission spaces :return: ''' ## convention specifications normal_vector = np.array([0, 0, -1]) # positive z points down; ate_vector = np.array([0, 1, 0]) # vector for the out of plane E-field ## =========================== Lx = lattice_constants[0] Ly = lattice_constants[1] NM = (2 * N + 1) * (2 * M + 1) # define vacuum wavevector k0 k0 = 2 * np.pi / lam0 ## ============== values to keep track of =======================## S_matrices = list() kz_storage = list() ## ==============================================================## m_r = 1 e_r = e_half[0] ## incident wave properties, at this point, everything is in units of k_0 n_i = np.sqrt(e_r * m_r) # actually, in the definitions here, kx = k0*sin(theta)*cos(phi), so kx, ky here are normalized kx_inc = n_i * np.sin(theta) * np.cos(phi) ky_inc = n_i * np.sin(theta) * np.sin(phi) # constant in ALL LAYERS; ky = 0 for normal incidence kz_inc = cmath.sqrt(e_r * 1 - kx_inc**2 - ky_inc**2) # remember, these Kx and Ky come out already normalized Kx, Ky = km.K_matrix_cubic_2D(kx_inc, ky_inc, k0, Lx, Ly, N, M) # Kx and Ky are diagonal but have a 0 on it ## =============== K Matrices for gap medium ========================= ## specify gap media (this is an LHI so no eigenvalue problem should be solved e_h = 1 Wg, Vg, Kzg = hl.homogeneous_module(Kx, Ky, e_h) ### ================= Working on the Reflection Side =========== ## Wr, Vr, kzr = hl.homogeneous_module(Kx, Ky, e_r) kz_storage.append(kzr) ## calculating A and B matrices for scattering matrix # since gap medium and reflection media are the same, this doesn't affect anything Ar, Br = sm.A_B_matrices(Wg, Wr, Vg, Vr) ## s_ref is a matrix, Sr_dict is a dictionary S_ref, Sr_dict = sm.S_R(Ar, Br) # scatter matrix for the reflection region S_matrices.append(S_ref) Sg = Sr_dict ## go through the layers for i in range(len(ER)): # ith layer material parameters e_conv = ER[i] mu_conv = UR[i] # longitudinal k_vector P, Q, kzl = pq.P_Q_kz(Kx, Ky, e_conv, mu_conv) kz_storage.append(kzl) Gamma_squared = P @ Q ## E-field modes that can propagate in the medium, these are well-conditioned W_i, lambda_matrix = em.eigen_W(Gamma_squared) V_i = em.eigen_V(Q, W_i, lambda_matrix) # now defIne A and B, slightly worse conditoined than W and V A, B = sm.A_B_matrices(W_i, Wg, V_i, Vg) # ORDER HERE MATTERS A LOT because W_i is not diagonal # calculate scattering matrix Li = layer_thicknesses[i] S_layer, Sl_dict = sm.S_layer(A, B, Li, k0, lambda_matrix) S_matrices.append(S_layer) ## update global scattering matrix using redheffer star Sg_matrix, Sg = rs.RedhefferStar(Sg, Sl_dict) ##========= Working on the Transmission Side==============## m_t = 1 e_t = e_half[1] Wt, Vt, kz_trans = hl.homogeneous_module(Kx, Ky, e_t) # get At, Bt # since transmission is the same as gap, order does not matter At, Bt = sm.A_B_matrices(Wg, Wt, Vg, Vt) ST, ST_dict = sm.S_T(At, Bt) S_matrices.append(ST) # update global scattering matrix Sg_matrix, Sg = rs.RedhefferStar(Sg, ST_dict) ## finally CONVERT THE GLOBAL SCATTERING MATRIX BACK TO A MATRIX K_inc_vector = n_i * np.array([np.sin(theta) * np.cos(phi), \ np.sin(theta) * np.sin(phi), np.cos(theta)]) E_inc, cinc, Polarization = ic.initial_conditions(K_inc_vector, theta, normal_vector, pte, ptm, N, M) # print(cinc.shape) # print(cinc) cinc = np.linalg.inv(Wr) @ cinc ## COMPUTE FIELDS: similar idea but more complex for RCWA since you have individual modes each contributing reflected = Wr @ Sg['S11'] @ cinc transmitted = Wt @ Sg['S21'] @ cinc rx = reflected[0:NM, :] # rx is the Ex component. ry = reflected[NM:, :] # tx = transmitted[0:NM, :] ty = transmitted[NM:, :] # longitudinal components; should be 0 rz = np.linalg.inv(kzr) @ (Kx @ rx + Ky @ ry) tz = np.linalg.inv(kz_trans) @ (Kx @ tx + Ky @ ty) ## we need to do some reshaping at some point ## apparently we're not done...now we need to compute 'diffraction efficiency' r_sq = np.square(np.abs(rx)) + np.square(np.abs(ry)) + np.square( np.abs(rz)) t_sq = np.square(np.abs(tx)) + np.square(np.abs(ty)) + np.square( np.abs(tz)) R = np.real(kzr) @ r_sq / np.real(kz_inc) #division by a scalar T = np.real(kz_trans) @ t_sq / (np.real(kz_inc)) return np.sum(R), np.sum(T) ## need a simulation which can return the field profiles inside the structure
## Get Kzr and Kztrans Wr, Vr, Kzr = hl.homogeneous_module(Kx, Ky, er1); Ar, Br = sm.A_B_matrices_half_space(Wr, Wg, Vr, Vg); #make sure this order is right Sr, Sr_dict = sm.S_R(Ar, Br) Sg = Sr_dict; ## =================================================================## ## First LAYER (homogeneous) ## =======================================================================## P, Q, Kzl = pq.P_Q_kz(Kx, Ky, E_conv, mu_conv) omega_sq = P @ Q; ## no gaurantees this is hermitian or symmetric W1, lambda_matrix = em.eigen_W(omega_sq) V1 = em.eigen_V(Q, W1, lambda_matrix) A1, B1 = sm.A_B_matrices(W1, Wg, V1, Vg); S1, S1_dict = sm.S_layer(A1, B1, d1, k0, lambda_matrix) Sg_matrix, Sg = rs.RedhefferStar(Sg, S1_dict) ## =================================================================## ## SECOND LAYER (homogeneous) ## =======================================================================## ##check with PQ formalism, which is unnecessary P2, Q2, Kz2_check = pq.P_Q_kz(Kx, Ky, ERC2, URC2) omega_sq_2 = P2 @ Q2; W2, lambda_matrix_2 = em.eigen_W(omega_sq_2) #somehow lambda_matrix is fine but W is full of errors V2 = em.eigen_V(Q2,W2,lambda_matrix_2); A2, B2 = sm.A_B_matrices(W2, Wg,V2, Vg); S2, S2_dict = sm.S_layer(A2,B2, d2, k0, lambda_matrix_2) Sg_matrix, Sg = rs.RedhefferStar(Sg, S2_dict);
#longitudinal k_vector P, Q, kzl = pq.P_Q_kz(Kx, Ky, e_conv, mu_conv) kz_storage.append(kzl) Gamma_squared = P @ Q ## E-field modes that can propagate in the medium, these are well-conditioned W_i, lambda_matrix = em.eigen_W(Gamma_squared) V_i = em.eigen_V(Q, W_i, lambda_matrix) #now defIne A and B, slightly worse conditoined than W and V A, B = sm.A_B_matrices(W_i, Wg, V_i, Vg) #ORDER HERE MATTERS A LOT because W_i is not diagonal #calculate scattering matrix Li = layer_thicknesses[i] S_layer, Sl_dict = sm.S_layer(A, B, Li, k0, lambda_matrix) S_matrices.append(S_layer) ## update global scattering matrix using redheffer star Sg_matrix, Sg = rs.RedhefferStar(Sg, Sl_dict) ##========= Working on the Transmission Side==============## m_t = 1 e_t = 1 Wt, Vt, kz_trans = hl.homogeneous_module(Kx, Ky, e_t) #get At, Bt # since transmission is the same as gap, order does not matter At, Bt = sm.A_B_matrices(Wg, Wt, Vg, Vt) ST, ST_dict = sm.S_T(At, Bt)
def run_TMM_simulation(wavelengths, polarization_amplitudes, k_inc, theta, phi, ER, UR, layer_thicknesses,\ transmission_medium, incident_medium): """ :param wavelengths: :param k_inc: holds kx, ky of the incident wave-vector (which is enough to specify kz since k0 is specified by wavelength :param theta: :param phi: :param ER: relative dielectric constants of each layer :param UR: relative permeability of each layer :param layer_thicknesses: :param transmission_medium: :param incident_medium: :return: """ assert len(layer_thicknesses) == len(ER) == len(UR); "number of layer parameters not the same" ref = []; trans = []; I = np.matrix(np.eye(2, 2)); # unit 2x2 matrix [e_r, m_r] = incident_medium; [e_t, m_t] = transmission_medium; n_i = np.sqrt(e_r*m_r); [kx, ky] = k_inc; normal_vector = np.array([0, 0, -1]) # positive z points down; ate_vector = np.matrix([0, 1, 0]); # vector for the out of plane E-field ## ================= specify gap media ========================## e_h = 1; m_h = 1; Pg, Qg, kzg = pq.P_Q_kz(kx, ky, e_h, m_h) Wg = I; # Wg should be the eigenmodes of the E field, which paparently is the identity, yes for a homogeneous medium sqrt_lambda = cmath.sqrt(-1) * Wg; # remember Vg is really Qg*(Omg)^-1; Vg is the eigenmodes of the H fields Vg = Qg * Wg * (sqrt_lambda) ** -1; ## ========================================== ## [pte, ptm] = polarization_amplitudes; for i in range(len(wavelengths)): # in SI units ## initialize global scattering matrix: should be a 4x4 identity so when we start the redheffer star, we get I*SR Sg11 = np.matrix(np.zeros((2, 2))); Sg12 = np.matrix(np.eye(2, 2)); Sg21 = np.matrix(np.eye(2, 2)); Sg22 = np.matrix(np.zeros((2, 2))); # matrices Sg = np.block( [[Sg11, Sg12], [Sg21, Sg22]]); # initialization is equivelant as that for S_reflection side matrix ### ================= Working on the Reflection Side =========== ## Pr, Qr, kzr = pq.P_Q_kz(kx, ky, e_r, m_r) ## ============== values to keep track of =======================## S_matrices = list(); kz_storage = [kzr]; X_storage = list(); ## ==============================================================## # define vacuum wavevector k0 lam0 = wavelengths[i]; # k0 and lam0 are related by 2*pi/lam0 = k0 k0 = 2*np.pi/lam0; ## modes of the layer Om_r = np.matrix(cmath.sqrt(-1) * kzr * I); X_storage.append(Om_r); W_ref = I; V_ref = Qr * Om_r.I; # can't play games with V like with W because matrices for V are complex ## calculating A and B matrices for scattering matrix Ar, Br = sm.A_B_matrices(Wg, W_ref, Vg, V_ref); S_ref, Sr_dict = sm.S_R(Ar, Br); # scatter matrix for the reflection region S_matrices.append(S_ref); Sg, D_r, F_r = rs.RedhefferStar(Sg, S_ref); ## go through the layers for i in range(len(ER)): # ith layer material parameters e = ER[i]; m = UR[i]; # longitudinal k_vector P, Q, kzl = pq.P_Q_kz(kx, ky, e, m) kz_storage.append(kzl) ## E-field modes that can propagate in the medium W_i = I; ## corresponding H-field modes. Om = cmath.sqrt(-1) * kzl * I; X_storage.append(Om) V_i = Q * np.linalg.inv(Om); # now defIne A and B A, B = sm.A_B_matrices(Wg, W_i, Vg, V_i); # calculate scattering matrix S_layer, Sl_dict = sm.S_layer(A, B, layer_thicknesses[i], k0, Om) S_matrices.append(S_layer); ## update global scattering matrix using redheffer star Sg, D_i, F_i = rs.RedhefferStar(Sg, S_layer); ##========= Working on the Transmission Side==============## Pt, Qt, kz_trans = pq.P_Q_kz(kx, ky, e_t, m_t); kz_storage.append(kz_trans); Om = cmath.sqrt(-1) * kz_trans * I; Vt = Qt * np.linalg.inv(Om); # get At, Bt At, Bt = sm.A_B_matrices(Wg, I, Vg, Vt) ST, ST_dict = sm.S_T(At, Bt) S_matrices.append(ST); # update global scattering matrix Sg, D_t, F_t = rs.RedhefferStar(Sg, ST); K_inc_vector = n_i * k0 * np.matrix([np.sin(theta) * np.cos(phi), \ np.sin(theta) * np.sin(phi), np.cos(theta)]); # cinc is the c1+ E_inc, cinc, Polarization = ic.initial_conditions(K_inc_vector, theta, normal_vector, pte, ptm) ## COMPUTE FIELDS Er = Sg[0:2, 0:2] * cinc; # S11; #(cinc = initial mode amplitudes), cout = Sg*cinc; #2d because Ex, Ey... Et = Sg[2:, 0:2] * cinc; # S21 Er = np.squeeze(np.asarray(Er)); Et = np.squeeze(np.asarray(Et)); Erx = Er[0]; Ery = Er[1]; Etx = Et[0]; Ety = Et[1]; # apply the grad(E) = 0 equation to get z components Erz = -(kx * Erx + ky * Ery) / kzr; Etz = -(kx * Etx + ky * Ety) / kz_trans; ## using divergence of E equation here # add in the Erz component to vectors Er = np.matrix([Erx, Ery, Erz]); # a vector Et = np.matrix([Etx, Ety, Etz]); R = np.linalg.norm(Er) ** 2; T = np.linalg.norm(Et) ** 2; ref.append(R); trans.append(T); return ref, trans
Wr, Vr, Kzr = hl.homogeneous_1D(KX, 1, m_r=1) ## transmission medium; Wt, Vt, Kzt = hl.homogeneous_1D(KX, 1, m_r=1) ## S matrices for the reflection region #Ar, Br = sm.A_B_matrices(Wg, Wr, Vg, Vr); Ar, Br = sm.A_B_matrices_half_space(Wr, Wg, Vr, Vg) # make sure this order is right S_ref, Sr_dict = sm.S_R(Ar, Br) # scatter matrix for the reflection region ## calculating A and B matrices for scattering matrix Sg = Sr_dict ## define S matrix for the GRATING REGION A, B = sm.A_B_matrices(W, Wg, V, Vg) S, S_dict = sm.S_layer(A, B, d, k0, lambda_matrix) Sg_matrix, Sg = rs.RedhefferStar(Sg, S_dict) ## define S matrices for the Transmission region At, Bt = sm.A_B_matrices_half_space(Wt, Wg, Vt, Vg) # make sure this order is right St, St_dict = sm.S_T(At, Bt) #scatter matrix for the reflection region Sg_matrix, Sg = rs.RedhefferStar(Sg, St_dict) #check scattering matrix is unitary #print(np.linalg.norm(np.linalg.inv(Sg_matrix)@Sg_matrix - np.matrix(np.eye(2*(2*num_ord+1))))) ## ======================== CALCULATE R AND T ===============================## K_inc_vector = n1*np.matrix([np.sin(theta_inc), \ 0, np.cos(theta_inc)])
def run_TMM_anisotropic(wavelengths, polarization_amplitudes, theta, phi, ER, UR, layer_thicknesses,\ transmission_medium, incident_medium): ref = []; trans = [] wvlen_scan = np.linspace(0.5, 4, 1000); thickness = 0.5; # 1 layer for wvlen in wvlen_scan: k0 = 2*np.pi/wvlen; kx = np.sin(theta)*np.cos(phi); ky = np.sin(theta)*np.sin(phi); # we will build the system by rows? a11 = -1j*(ky*mu_tensor[1,2]/mu_tensor[2,2] + kx*(epsilon_tensor[2,0]/epsilon_tensor[2,2])) a12 = 1j*kx*(mu_tensor[1,2]/mu_tensor[2,2] - epsilon_tensor[2,1]/epsilon_tensor[2,2]); a13 = kx*ky/epsilon_tensor[2,2] + mu_tensor[1,0] - mu_tensor[1,2]*mu_tensor[2,0]/mu_tensor[2,2]; a14 = -kx**2/epsilon_tensor[2,2] + mu_tensor[1,1]- mu_tensor[1,2]*mu_tensor[2,1]/mu_tensor[2,2]; a21 = 1j* ky *(mu_tensor[0,2]/mu_tensor[2,2] - epsilon_tensor[2,0]/epsilon_tensor[2,2]); a22 = -1j * kx*(mu_tensor[0,2]/mu_tensor[2,2]) +ky *(epsilon_tensor[2,1]/epsilon_tensor[2,2]); a23 = ky**2/epsilon_tensor[2,2] - mu_tensor[0,0] + mu_tensor[0,2]*mu_tensor[2,0]/mu_tensor[2,2]; a24 = -kx*ky/epsilon_tensor[2,2] - mu_tensor[0,1] + mu_tensor[0,2]*mu_tensor[2,1]/mu_tensor[2,2]; a31 = (kx*ky/mu_tensor[2,2] + epsilon_tensor[1,0] - epsilon_tensor[1,2]*epsilon_tensor[2,0]/epsilon_tensor[2,2]) a32 = (-kx**2/mu_tensor[2,2] +epsilon_tensor[1,1] - epsilon_tensor[1,2]*epsilon_tensor[2,1]/epsilon_tensor[2,2]); a33 = -1j*(ky*(epsilon_tensor[1,2]/epsilon_tensor[2,2])+kx*(mu_tensor[2,0]/mu_tensor[2,2])); a34 = 1j*kx*(epsilon_tensor[1,2]/epsilon_tensor[2,2]-mu_tensor[2,1]/mu_tensor[2,2] ) a41 = ky**2/mu_tensor[2,2] - epsilon_tensor[0,0] + epsilon_tensor[0,2]*epsilon_tensor[2,0]/epsilon_tensor[2,2]; a42 = -kx*ky/mu_tensor[2,2] - epsilon_tensor[0,1] + epsilon_tensor[0,2]*epsilon_tensor[2,1]/epsilon_tensor[2,2]; a43 = 1j*ky*(epsilon_tensor[0,2]/epsilon_tensor[2,2]-mu_tensor[2,0]/mu_tensor[2,2] ); a44 = -1j*(kx*(epsilon_tensor[0,2]/epsilon_tensor[2,2])+ky*(mu_tensor[2,1]/mu_tensor[2,2])); A = np.matrix([[a11, a12, a13, a14], [a21, a22, a23, a24], [a31, a32, a33, a34], [a41, a42, a43, a44]]); #print(np.linalg.cond(A)) eigenvals, eigenmodes = np.linalg.eig(A); rounded_eigenvals = np.round(eigenvals, 3) sorted_eigs, sorted_inds = nonHermitianEigenSorter(np.round(eigenvals,10)); ## ======================================================== W_i = eigenmodes[0:2, sorted_inds]; V_i = eigenmodes[2:, sorted_inds]; Om = np.matrix(np.diag(sorted_eigs) ); #print(np.round(W_i,3), np.round(V_i,3), np.round(Om,3)) #then what... match boundary conditions... try using the gaylord formulation. # or use the scattering matrix formalism, where we still, technically deal with all field components... Sg11 = np.matrix(np.zeros((2, 2))); Sg12 = np.matrix(np.eye(2, 2)); Sg21 = np.matrix(np.eye(2, 2)); Sg22 = np.matrix(np.zeros((2, 2))); # matrices Sg = np.block([[Sg11, Sg12], [Sg21, Sg22]]); # initialization is equivelant as that for S_reflection side matrix ### ================= Working on the Reflection Side =========== ## Pr, Qr, kzr = pq.P_Q_kz(kx, ky, e_r, m_r) ## ============== values to keep track of =======================## S_matrices = list(); kz_storage = [kzr]; X_storage = list(); ## ==============================================================## # define vacuum wavevector k0 lam0 = wvlen; # k0 and lam0 are related by 2*pi/lam0 = k0 k0 = 2*np.pi/lam0; ## modes of the layer Om_r = np.matrix(cmath.sqrt(-1) * kzr * I); X_storage.append(Om_r); W_ref = I; V_ref = Qr * Om_r.I; # can't play games with V like with W because matrices for V are complex #print(Om_r) ## calculating A and B matrices for scattering matrix Ar, Br = sm.A_B_matrices(Wg, W_ref, Vg, V_ref); S_ref, Sr_dict = sm.S_R(Ar, Br); # scatter matrix for the reflection region S_matrices.append(S_ref); Sg, D_r, F_r = rs.RedhefferStar(Sg, S_ref); # longitudinal k_vector ## ============ WORKING INSIDE ANISOTROPIC LAYER ================# # now defIne A and B Al, Bl = sm.A_B_matrices(Wg, W_i, Vg, V_i); # calculate scattering matrix S_layer, Sl_dict = sm.S_layer(Al, Bl, thickness, k0, Om) S_matrices.append(S_layer); ## update global scattering matrix using redheffer star Sg, D_i, F_i = rs.RedhefferStar(Sg, S_layer); ##========= Working on the Transmission Side==============## Pt, Qt, kz_trans = pq.P_Q_kz(kx, ky, e_t, m_t); kz_storage.append(kz_trans); Omt = cmath.sqrt(-1) * kz_trans * I; Vt = Qt * np.linalg.inv(Omt); # get At, Bt At, Bt = sm.A_B_matrices(Wg, I, Vg, Vt) ST, ST_dict = sm.S_T(At, Bt) S_matrices.append(ST); # update global scattering matrix Sg, D_t, F_t = rs.RedhefferStar(Sg, ST); K_inc_vector = n_i * k0 * np.matrix([np.sin(theta) * np.cos(phi), \ np.sin(theta) * np.sin(phi), np.cos(theta)]); # cinc is the c1+ E_inc, cinc, Polarization = ic.initial_conditions(K_inc_vector, theta, normal_vector, pte, ptm) ## COMPUTE FIELDS Er = Sg[0:2, 0:2] * cinc; # S11; #(cinc = initial mode amplitudes), cout = Sg*cinc; #2d because Ex, Ey... Et = Sg[2:, 0:2] * cinc; # S21 Er = np.squeeze(np.asarray(Er)); Et = np.squeeze(np.asarray(Et)); Erx = Er[0]; Ery = Er[1]; Etx = Et[0]; Ety = Et[1]; # apply the grad(E) = 0 equation to get z components, this equation comes out of the longitudinal equation # or the divergence equation and is valid since the transmission region is VACUUM (as is the reflection region) Erz = -(kx * Erx + ky * Ery) / kzr; #uses the divergence law Etz = -(kx * Etx + ky * Ety) / kz_trans; ## using divergence of E equation here # add in the Erz component to vectors Er = np.matrix([Erx, Ery, Erz]); # a vector Et = np.matrix([Etx, Ety, Etz]); R = np.linalg.norm(Er) ** 2; T = np.linalg.norm(Et) ** 2; ref.append(R); trans.append(T); return ref, trans;
def fun(): ''' run the RCWA simulation...do not use this code... :return: ''' meters = 1 centimeters = 1e-2 * meters degrees = np.pi / 180 # Source parameters lam0 = 2 * centimeters theta = 0 phi = 0 pte = 1 # te polarized ptm = 0 normal_vector = np.array([0, 0, -1]) # positive z points down; ate_vector = np.array([0, 1, 0]) # vector for the out of plane E-field k0 = 2 * np.pi / lam0 print('k0: ' + str(k0)) # structure parameters # reflection 1 and transmission 2 ur1 = 1 er1 = 2 n_i = np.sqrt(ur1 * er1) ur2 = 1 er2 = 9 ## second layer urd = 1 erd = 6 # dimensions of the unit cell Lx = 1.75 * centimeters Ly = 1.5 * centimeters # thickness of layers d1 = 0.5 * centimeters d2 = 0.3 * centimeters w = 0.8 * Ly # RCWA parameters Nx = 512 Ny = round(Nx * Ly / Lx) PQ = [1, 1] # number of spatial harmonics NH = (2 * (PQ[0]) + 1) * (2 * (PQ[1]) + 1) ## =========================== BUILD DEVICE ON GRID ==================================## dx = Lx / Nx dy = Ly / Ny xa = np.linspace(0, Lx, Nx) xa = xa - np.mean(xa) ya = np.linspace(0, Ly, Ny) ya = ya - np.mean(ya) # initialize layers UR = np.ones((Nx, Ny, 2)) # interestin ER = erd * np.ones((Nx, Ny, 2)) L = [d1, d2] # Build the triangle h = 0.5 * np.sqrt(3) * w ny = int(np.round(h / dy)) # discrete height ny1 = np.round((Ny - ny) / 2) ny2 = ny1 + ny - 1 print(str(ny1) + ', ' + str(ny2)) for ny_ind in np.arange(ny1, ny2 + 1): # build the triangle slice wise f = (ny_ind - ny1) / (ny2 - ny1) # fractional occupation; nx = int(round(f * (w / Lx) * Nx)) # x width nx1 = 1 + int(np.floor((Nx - nx) / 2)) nx2 = int(nx1 + nx) # print(str(nx1)+', '+str(nx2)) ER[nx1:nx2 + 1, int(ny_ind), 0] = er1 E_conv = (cm.convmat2D(ER[:, :, 0], PQ[0], PQ[1])) np.set_printoptions(precision=4) print(E_conv) mu_conv = (np.identity(NH)) ## Build the second layer (uniform) URC2 = (np.identity(NH)) ERC2 = erd * (np.identity(NH)) ## BUILD THE K_MATRIX kx_inc = n_i * np.sin(theta) * np.cos(phi) ky_inc = n_i * np.sin(theta) * np.sin(phi) # constant in ALL LAYERS; ky = 0 for normal incidence kz_inc = cmath.sqrt(n_i**2 - kx_inc**2 - ky_inc**2) Kx, Ky = km.K_matrix_cubic_2D(kx_inc, ky_inc, k0, Lx, Ly, PQ[0], PQ[1]) # gap media Wg, Vg, Kzg = hl.homogeneous_module(Kx, Ky, 2) ## Get Kzr and Kztrans Wr, Vr, Kzr = hl.homogeneous_module(Kx, Ky, er1) Ar, Br = sm.A_B_matrices_half_space(Wr, Wg, Vr, Vg) # make sure this order is right Sr, Sr_dict = sm.S_R(Ar, Br) Sg = Sr_dict ## =================================================================## ## First LAYER (homogeneous) ## =======================================================================## P, Q, Kzl = pq.P_Q_kz(Kx, Ky, E_conv, mu_conv) omega_sq = P @ Q ## no gaurantees this is hermitian or symmetric W1, lambda_matrix = em.eigen_W(omega_sq) V1 = em.eigen_V(Q, W1, lambda_matrix) A1, B1 = sm.A_B_matrices(W1, Wg, V1, Vg) S1, S1_dict = sm.S_layer(A1, B1, d1, k0, lambda_matrix) Sg_matrix, Sg = rs.RedhefferStar(Sg, S1_dict) ## =================================================================## ## SECOND LAYER (homogeneous) ## =======================================================================## ##check with PQ formalism, which is unnecessary P2, Q2, Kz2_check = pq.P_Q_kz(Kx, Ky, ERC2, URC2) omega_sq_2 = P2 @ Q2 W2, lambda_matrix_2 = em.eigen_W( omega_sq_2) # somehow lambda_matrix is fine but W is full of errors V2 = em.eigen_V(Q2, W2, lambda_matrix_2) A2, B2 = sm.A_B_matrices(W2, Wg, V2, Vg) S2, S2_dict = sm.S_layer(A2, B2, d2, k0, lambda_matrix_2) Sg_matrix, Sg = rs.RedhefferStar(Sg, S2_dict) ## TRANSMISSION LAYER # #create ST Wt, Vt, Kzt = hl.homogeneous_module(Kx, Ky, er2) At, Bt = sm.A_B_matrices_half_space(Wt, Wg, Vt, Vg) # make sure this order is right St, St_dict = sm.S_T(At, Bt) ### FUCKKKKKKKKKKKKKKKK Sg_matrix, Sg = rs.RedhefferStar(Sg, St_dict) print('final Sg') print(Sg['S11']) ## ================START THE SSCATTERING CALCULATION ==========================## K_inc_vector = n_i * np.array([np.sin(theta) * np.cos(phi), \ np.sin(theta) * np.sin(phi), np.cos(theta)]) E_inc, cinc, Polarization = ic.initial_conditions(K_inc_vector, theta, normal_vector, pte, ptm, PQ[0], PQ[1]) ## COMPUTE FIELDS: similar idea but more complex for RCWA since you have individual modes each contributing reflected = Wr @ Sg['S11'] @ cinc # reflection coefficients for every mode... transmitted = Wt @ Sg['S21'] @ cinc ## these include only (rx, ry), (tx, ty), which is okay as these are the only components for normal incidence in LHI rx = reflected[0:NH, :] ry = reflected[NH:, :] tx = transmitted[0:NH, :] ty = transmitted[NH:, :] # longitudinal components; should be 0 rz = np.linalg.inv(Kzr) @ (Kx @ rx + Ky @ ry) tz = np.linalg.inv(Kzt) @ (Kx @ tx + Ky @ ty) print('rx') print(rx) print('ry') print(ry) print('rz') print(rz) ## apparently we're not done...now we need to compute 'diffraction efficiency' r_sq = np.square(np.abs(rx)) + np.square(np.abs(ry)) + np.square( np.abs(rz)) t_sq = np.square(np.abs(tx)) + np.square(np.abs(ty)) + np.square( np.abs(tz)) R = np.real(Kzr) @ r_sq / np.real(kz_inc) T = np.real(Kzt) @ t_sq / (np.real(kz_inc)) print('final R vector-> matrix') print(np.reshape(R, (3, 3))) # should be 3x3 print('final T vector/matrix') print(np.reshape(T, (3, 3))) print('final reflection: ' + str(np.sum(R))) print('final transmission: ' + str(np.sum(T))) print('sum of R and T: ' + str(np.sum(R) + np.sum(T))) ## if the sum isn't 1, that's a PROBLEM t1 = time.time() return np.sum(R), np.sum(T)