def test_NPML(self): # NPML a number with self.assertRaises(AssertionError): Simulation(self.omega, self.eps_r, self.dl, 10, self.pol) # NPML too many elements with self.assertRaises(AssertionError): Simulation(self.omega, self.eps_r, self.dl, [10, 10, 10], self.pol)
def test_1D(self): Nx = self.eps_r.shape[0] eps_1d = np.ones((Nx, )) S = Simulation(self.omega, eps_1d, self.dl, [10, 0], self.pol) S.src = np.zeros(S.eps_r.shape, dtype=np.complex64) S.src[Nx // 2, 0] = 1j (Hx, Hy, Ez) = S.solve_fields()
def test_pol(self): # polarization not a string with self.assertRaises(AssertionError): Simulation(self.omega, self.eps_r, self.dl, self.NPML, 5) # polarization not the right string with self.assertRaises(AssertionError): Simulation(self.omega, self.eps_r, self.dl, self.NPML, 'WrongPolarization')
def solve(self, epsilons: np.array, omega=OMEGA_1550, src_x=None, clip_buffers=False): total_length = self.device_length + 2 * self.buffer_length + 2 * self.npml start = self.npml + self.buffer_length end = start + self.device_length permittivities = np.ones(total_length, dtype=np.float64) # set permittivity and reflection zone permittivities[:start] = self.buffer_permittivity permittivities[start:end] = epsilons permittivities[end:] = self.buffer_permittivity if src_x is None: src_x = int(self.device_length / 2) sim = Simulation(omega, permittivities, self.dl, [self.npml, 0], self.mode, L0=self.L0, use_dirichlet_bcs=self.use_dirichlet_bcs) sim.src[src_x + self.npml + self.buffer_length] = 1j if clip_buffers: clip0 = self.npml + self.buffer_length clip1 = -(self.npml + self.buffer_length) else: clip0 = None clip1 = None if self.mode == "Ez": Hx, Hy, Ez = sim.solve_fields() permittivities = permittivities[clip0:clip1] Hx = Hx[clip0:clip1] Hy = Hy[clip0:clip1] Ez = Ez[clip0:clip1] return permittivities, src_x, Hx, Hy, Ez elif self.mode == "Hz": Ex, Ey, Hz = sim.solve_fields() permittivities = permittivities[clip0:clip1] Ex = Ex[clip0:clip1] Ey = Ey[clip0:clip1] Hz = Hz[clip0:clip1] return permittivities, src_x, Ex, Ey, Hz else: raise ValueError("Polarization must be Ez or Hz!")
def get_operators(self, omega=OMEGA_1550): total_length = self.device_length + 2 * self.buffer_length + 2 * self.npml perms = np.ones((total_length, total_length), dtype=np.float64) start = self.npml + self.buffer_length end = start + self.device_length # set permittivity and reflection zone perms[:, :start] = self.buffer_permittivity perms[:start, :] = self.buffer_permittivity perms[:, end:] = self.buffer_permittivity perms[end:, :] = self.buffer_permittivity sim = Simulation(omega, perms, self.dl, [self.npml, self.npml], self.mode, L0=self.L0, use_dirichlet_bcs=self.use_dirichlet_bcs) Dyb, Dxb, Dxf, Dyf = unpack_derivs(sim.derivs) N = np.asarray(perms.shape) M = np.prod(N) vector_eps_z = EPSILON0 * self.L0 * perms.reshape((-1, )) T_eps_z = sp.spdiags(vector_eps_z, 0, M, M, format='csr') curl_curl = (Dxf @ Dxb + Dyf @ Dyb) other = omega**2 * MU0 * self.L0 * T_eps_z return curl_curl.todense(), other.todense()
def test_freq(self): # negative frequency with self.assertRaises(AssertionError): Simulation(-self.omega, self.eps_r, self.dl, self.NPML, self.pol)
# geometric parameters for a 1 -> 2 port device L = 6 # length of box (L0) H = 4 # height of box (L0) w = .3 # width of waveguides (L0) d = H/1.5 # distance between waveguides (L0) l = 4 # length of waveguide from PML to box (L0) spc = 2 # space between box and PML (L0) # define permittivity of three port system eps_r, design_region = three_port(L, H, w, d, l, spc, dl, NPML, eps_m) (Nx, Ny) = eps_r.shape nx, ny = int(Nx/2), int(Ny/2) # halfway grid points # make a new simulation object simulation = Simulation(omega, eps_r, dl, NPML, pol) # set the input waveguide modal source simulation.add_mode(neff=np.sqrt(eps_m), direction_normal='x', center=[NPML[0]+int(l/2/dl), ny], width=int(H/2/dl), scale=source_amp) simulation.setup_modes() # make a new simulation to get the modal profile of the top output port top = Simulation(omega, eps_r, dl, NPML, 'Ez') top.add_mode(neff=np.sqrt(eps_m), direction_normal='x', center=[-NPML[0]-int(l/2/dl), ny+int(d/2/dl)], width=int(H/2/dl)) top.setup_modes() J_top = np.abs(top.src) # make a new simulation to get the modal profile of the bottom output port bot = Simulation(omega, eps_r, dl, NPML, 'Ez') bot.add_mode(neff=np.sqrt(eps_m), direction_normal='x', center=[-NPML[0]-int(l/2/dl), ny-int(d/2/dl)], width=int(H/2/dl)) bot.setup_modes()
def test_born_newton(self): """Tests whether born and newton methods get the same result""" n0 = 3.4 omega = 2 * np.pi * 200e12 dl = 0.01 chi3 = 2.8E-18 width = 1 L = 5 L_chi3 = 4 width_voxels = int(width / dl) L_chi3_voxels = int(L_chi3 / dl) Nx = int(L / dl) Ny = int(3.5 * width / dl) eps_r = np.ones((Nx, Ny)) eps_r[:, int(Ny / 2 - width_voxels / 2):int(Ny / 2 + width_voxels / 2)] = np.square(n0) nl_region = np.zeros(eps_r.shape) nl_region[int(Nx / 2 - L_chi3_voxels / 2):int(Nx / 2 + L_chi3_voxels / 2), int(Ny / 2 - width_voxels / 2):int(Ny / 2 + width_voxels / 2)] = 1 simulation = Simulation(omega, eps_r, dl, [15, 15], 'Ez') simulation.add_mode(n0, 'x', [17, int(Ny / 2)], width_voxels * 3) simulation.setup_modes() simulation.add_nl(chi3, nl_region, eps_scale=True, eps_max=np.max(eps_r)) srcval_vec = np.logspace(1, 3, 3) pwr_vec = np.array([]) T_vec = np.array([]) for srcval in srcval_vec: simulation.setup_modes() simulation.src *= srcval # Newton simulation.solve_fields_nl(solver_nl='newton') E_newton = simulation.fields["Ez"] # Born simulation.solve_fields_nl(solver_nl='born') E_born = simulation.fields["Ez"] # More solvers (if any) should be added here with corresponding calls to assert_allclose() below assert_allclose(E_newton, E_born, rtol=1e-3)
# geometric parameters L1 = 6 # length waveguides in design region (L0) L2 = 6 # width of box (L0) H1 = 6 # height waveguides in design region (L0) H2 = 6 # height of box (L0) w = .3 # width of waveguides (L0) l = 3 # length of waveguide from PML to box (L0) spc = 2 # space between box and PML (L0) # define permittivity of three port system eps_r, design_region = ortho_port(L1, L2, H1, H2, w, l, dl, NPML, eps_m) (Nx, Ny) = eps_r.shape nx, ny = int(Nx/2), int(Ny/2) # halfway grid points simulation = Simulation(omega,eps_r,dl,NPML,pol) # set the modal source and probes simulation = Simulation(omega, eps_r, dl, NPML, 'Ez') simulation.add_mode(np.sqrt(eps_m), 'x', [NPML[0]+int(l/2/dl), ny], int(H1/2/dl), scale=source_amp) simulation.setup_modes() # left modal profile right = Simulation(omega, eps_r, dl, NPML, 'Ez') right.add_mode(np.sqrt(eps_m), 'x', [-NPML[0]-int(l/2/dl), ny], int(H1/2/dl)) right.setup_modes() J_right = np.abs(right.src) # top modal profile top = Simulation(omega, eps_r, dl, NPML, 'Ez') top.add_mode(np.sqrt(eps_m), 'y', [nx, -NPML[1]-int(l/2/dl)], int(L1/2/dl))
if plot_all: print('-> solving FDFD for continuous epsilon') F = fdfd(omega0, dl, eps_total, NPML) Hx, Hy, Ez = F.solve(source) plt.imshow(np.abs(imarr(Ez)), cmap='magma') plt.title('|Ez| for continuous permitivity') plt.xlabel('x') plt.ylabel('y') plt.colorbar() plt.show() """ DEFINE MODAL SOURCE / PROBE """ print('-> setting up modal source') # make an angler simulation with the starting permittivity, to compute the modal source sim = Simulation(omega0, eps_base, dl, [npml, npml], pol='Ez', L0=1) center_y = npml + int((spc + subs + h1 / 2) / dl) sim.add_mode(neff_hole, 'x', center=[npml + int(spc / dl), center_y], width=int(5 * h1 / dl), scale=1, order=1) sim.setup_modes() # get the wg mode profile as an array wg_mode = np.flipud(np.abs(sim.src.copy())).reshape((Nx, Ny, 1)) # compute the power in the waveguide mode for normalization wg_mode_p = np.sum(np.square(np.abs(wg_mode)))
def setUp(self): # create a simulation to test just like in notebook lambda0 = 2e-6 # free space wavelength (m) c0 = 3e8 # speed of light in vacuum (m/s) omega = 2 * np.pi * c0 / lambda0 # angular frequency (2pi/s) dl = 1.1e-1 # grid size (L0) NPML = [15, 15] # number of pml grid points on x and y borders pol = 'Ez' # polarization (either 'Hz' or 'Ez') source_amp = 100 # amplitude of modal source (A/L0^2?) # material constants n_index = 2.44 # refractive index eps_m = n_index**2 # relative permittivity max_ind_shift = 5.8e-2 # maximum allowed nonlinear index shift # geometric parameters L = 4 # length of box (L0) H = 4 # height of box (L0) w = .2 # width of waveguides (L0) d = H / 2.44 # distance between waveguides (L0) l = 3 # length of waveguide from PML to box (L0) spc = 2 # space between box and PML (L0) # define permittivity of three port system (eps_r, design_region) = three_port(L, H, w, d, dl, l, spc, NPML, eps_start=eps_m) (Nx, Ny) = eps_r.shape nx, ny = int(Nx / 2), int(Ny / 2) # halfway grid points # set the modal source and probes self.simulation = Simulation(omega, eps_r, dl, NPML, 'Ez') self.simulation.add_mode(np.sqrt(eps_m), 'x', [NPML[0] + int(l / 2 / dl), ny], int(H / 2 / dl), scale=source_amp) self.simulation.setup_modes() self.simulation.init_design_region(design_region, eps_m) # top modal profile top = Simulation(omega, eps_r, dl, NPML, 'Ez') top.add_mode(np.sqrt(eps_m), 'x', [-NPML[0] - int(l / 2 / dl), ny + int(d / 2 / dl)], int(H / 2 / dl)) top.setup_modes() J_top = np.abs(top.src) # bottom modal profile bot = Simulation(omega, eps_r, dl, NPML, 'Ez') bot.add_mode(np.sqrt(eps_m), 'x', [-NPML[0] - int(l / 2 / dl), ny - int(d / 2 / dl)], int(d / dl)) bot.setup_modes() J_bot = np.abs(bot.src) # define linear and nonlinear parts of objective function + the total objective function form J = lambda e, e_nl: npa.sum(npa.square(npa.abs(e)) * J_top) + npa.sum( npa.square(npa.abs(e_nl)) * J_bot) import autograd.numpy as npa def J(e, e_nl): linear_top = 1 * npa.sum(npa.square(npa.abs(e)) * J_top) linear_bot = -1 * npa.sum(npa.square(npa.abs(e)) * J_bot) nonlinear_top = -1 * npa.sum(npa.square(npa.abs(e_nl)) * J_top) nonlinear_bot = 1 * npa.sum(npa.square(npa.abs(e_nl)) * J_bot) objfn = linear_top + nonlinear_top + nonlinear_bot + linear_top return objfn self.design_region = design_region self.optimization = Optimization(J=J, simulation=self.simulation, design_region=self.design_region, eps_m=eps_m)