Kx, Ky = km.K_matrix_cubic_2D(beta_x, beta_y, ax, ay, P, Q) #from here, we can actually dtermine omega from our specification of kx and ky # omega = c0*np.sqrt(beta_x**2+beta_y**2)/L0; # # ## we can do a determination of a dispersive medium from here # #drude for example #eps_drude = 1-omega_p**2/(omega**2-cmath.sqrt(-1)*gamma*omega); #print(eps_drude); eps_tracker.append(eps_drude); epsilon = np.ones((Nx, Ny)) halfy = int(Ny / 2) epsilon[halfy - 100:halfy + 100, :] = 12 ## =============== Convolution Matrices ============== E_r = cm.convmat2D(epsilon, P, Q) ## =========================================================== eigenvalues, eigenvectors, A_matrix = eg.PWEM2D_TE(Kx, Ky, E_r) #what are the eigenvalue units omega_eig_store.append(np.sqrt(abs(np.real(eigenvalues)))) plt.plot(beta_x * np.ones((PQ, )), np.sort(np.sqrt(abs(np.real(eigenvalues)))), '.b') plt.plot(beta_x * np.ones((PQ, )), np.sort(np.sqrt(abs(np.imag(eigenvalues)))), '.r') ## plot the light line plt.plot(kx_scan, abs(kx_scan)) plt.show()
#print(str(nx1)+', '+str(nx2)) ER[nx1:nx2+1, int(ny_ind), 0] = er1; # plt.imshow(ER[:,:,0]) # plt.colorbar() # plt.show() # Af = np.fft.fftshift(np.fft.fft2(ER[:,:,0])); # plt.figure(); # plt.subplot(121) # plt.imshow(np.log(abs(Af))); #note that the fft HAS A HUGE RANGE OF VALUES, which can become a source of inaccuracy # plt.colorbar() # plt.subplot(122) # plt.imshow(np.abs(ER[:,:,0])) # plt.show(); ## conv matrices of the 1st 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]);
# ============== build high resolution circle ================== Nx = 512; Ny = 512; A = np.ones((Nx,Ny)); ci = int(Nx/2); cj= int(Ny/2); cr = (radius/a)*Nx; I,J=np.meshgrid(np.arange(A.shape[0]),np.arange(A.shape[1])); dist = np.sqrt((I-ci)**2 + (J-cj)**2); A[np.where(dist<cr)] = e_r; #visualize structure plt.imshow(A); plt.show() ## =============== Convolution Matrices ============== E_r = cm.convmat2D(A, P,Q) print(E_r.shape) print(type(E_r)) plt.figure(); plt.imshow(abs(E_r), cmap = 'jet'); plt.colorbar() plt.show() ## =============== K Matrices ========================= beta_x = beta_y = 0; plt.figure(); ## check K-matrices for normal icnidence Kx, Ky = km.K_matrix_cubic_2D(0,0, a, a, P, Q); np.set_printoptions(precision = 3)
Nx = 512 Ny = 512 A = e_r * np.ones((Nx, Ny)) A = A.astype('complex') x1 = int(Nx / 2) - 50 x2 = int(Nx / 2) + 50 y1 = int(Ny / 2) - 50 y2 = int(Ny / 2) + 50 A[:, y1:y2] = eps_drude ## A METALLIC HOLE...the fact that we have to recalculate the convolution # plt.imshow(np.abs(A)); # plt.show(); ## =============== Convolution Matrices ============== E_r = cm.convmat2D(A, N, M) NM = (2 * N + 1) * (2 * M + 1) ## ================== GEOMETRY OF THE LAYERS AND CONVOLUTIONS ==================## thickness_slab = 0.2 # in units of L0; ER = [E_r] UR = [np.identity(NM)] layer_thicknesses = [thickness_slab] # this retains SI unit convention #source parameters theta = 0 * degrees #%elevation angle phi = 0 * degrees
#print(str(nx1)+', '+str(nx2)) ER[nx1:nx2+1, int(ny_ind), 0] = er1; # plt.imshow(ER[:,:,0]) # plt.colorbar() # plt.show() Af = np.fft.fftshift(np.fft.fft2(ER[:,:,0])); plt.figure(); plt.subplot(121) plt.imshow(np.log(abs(Af))); #note that the fft HAS A HUGE RANGE OF VALUES, which can become a source of inaccuracy plt.colorbar() plt.subplot(122) plt.imshow(np.abs(ER[:,:,0])) plt.show(); ## conv matrices of the 1st E_conv = np.matrix(cm.convmat2D(ER[:, :, 0], PQ[0], PQ[1])); np.set_printoptions(precision = 4) print(E_conv) mu_conv = np.matrix(np.identity(NH)); ## Build the second layer (uniform) URC2 = np.matrix(np.identity(NH)) ERC2= erd*np.matrix(np.identity(NH)); ER = [E_conv, ERC2]; UR = [mu_conv, URC2]; wavelengths = np.linspace(0.5, 5, 501)*centimeters ref = list(); trans = list(); for i in range(len(wavelengths)): #in SI units
## Specify number of fourier orders to use: #sometimes can get orders which make the system singular N = 2 M = 2 NM = (2 * N + 1) * (2 * M + 1) # ============== build high resolution circle ================== E_r = e_layer * np.matrix(np.identity(NM)) U_r = np.matrix(np.identity(NM)) #check that the convmat returns an identity matrix eps_grid = np.ones((512, 512)) er_check = e_layer * cm.convmat2D(eps_grid, N, M) er_fft = np.fft.fftshift(np.fft.fft(np.ones((3, 3)))) / (9) ## ================== GEOMETRY OF THE LAYERS AND CONVOLUTIONS ==================## thickness_slab = 0.76 * L0 #100 nm; ER = [E_r] UR = [U_r] layer_thicknesses = [thickness_slab] #this retains SI unit convention ## =============== Simulation Parameters ========================= ## set wavelength scanning range wavelengths = L0 * np.linspace(0.25, 1, 1000) #500 nm to 1000 nm #LHI fails for small wavelengths? kmagnitude_scan = 2 * np.pi / wavelengths
A = e_r * np.ones((Nx, Ny)) A = A.astype('complex') A2 = e_r * np.ones((Nx, Ny)) A2 = A2.astype('complex') x1 = int(Nx / 2) - 50 x2 = int(Nx / 2) + 50 y1 = int(Ny / 2) - 50 y2 = int(Ny / 2) + 50 A[:, y1:y2] = eps_drude ## A METALLIC HOLE...the fact that we have to recalculate the convolution A2[x1:x2, :] = eps_drude # plt.imshow(np.abs(A)); # plt.show(); ## =============== Convolution Matrices ============== E_r = cm.convmat2D(A, N, M) E_r2 = cm.convmat2D(A2, N, M) NM = (2 * N + 1) * (2 * M + 1) ## ================== GEOMETRY OF THE LAYERS AND CONVOLUTIONS ==================## thickness_slab = 0.2 # in units of L0; ER = [E_r, e_r * np.identity(NM), E_r2] UR = [np.identity(NM), np.identity(NM), np.identity(NM)] layer_thicknesses = [thickness_slab, thickness_slab, thickness_slab] # this retains SI unit convention #source parameters theta = 0 * degrees #%elevation angle
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)