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 adjoint_linear_Hz(simulation, b_aj, averaging=False, solver=DEFAULT_SOLVER, matrix_format=DEFAULT_MATRIX_FORMAT): # Compute the adjoint field for a linear problem # Note: the correct definition requires simulating with the transpose matrix A.T EPSILON_0_ = EPSILON_0*simulation.L0 MU_0_ = MU_0*simulation.L0 omega = simulation.omega (Nx, Ny) = (simulation.Nx, simulation.Ny) M = Nx*Ny A = simulation.A hz = solver_direct(A.T, b_aj, solver=solver) (Dyb, Dxb, Dxf, Dyf) = unpack_derivs(simulation.derivs) (Dyb_T, Dxb_T, Dxf_T, Dyf_T) = (Dyb.T, Dxb.T, Dxf.T, Dyf.T) if averaging: vector_eps_x = grid_average(EPSILON_0_*simulation.eps_r, 'x').reshape((-1,)) vector_eps_y = grid_average(EPSILON_0_*simulation.eps_r, 'y').reshape((-1,)) else: vector_eps_x = EPSILON_0_*simulation.eps_r.reshape((-1,)) vector_eps_y = EPSILON_0_*simulation.eps_r.reshape((-1,)) T_eps_x_inv = sp.spdiags(1/vector_eps_x, 0, M, M, format=matrix_format) T_eps_y_inv = sp.spdiags(1/vector_eps_y, 0, M, M, format=matrix_format) # Note: to get the correct gradient in the end, we must use Dxf, Dyf here ex = 1/1j/omega * T_eps_y_inv.dot(Dyf_T).dot(hz).T ey = -1/1j/omega * T_eps_x_inv.dot(Dxf_T).dot(hz).T Ex = ex.reshape((Nx, Ny)) Ey = ey.reshape((Nx, Ny)) return (Ex, Ey)
def solve_fields(self, include_nl=False, timing=False, averaging=False, matrix_format=DEFAULT_MATRIX_FORMAT): # performs direct solve for A given source EPSILON_0_ = EPSILON_0 * self.L0 MU_0_ = MU_0 * self.L0 if include_nl == False: eps_tot = self.eps_r X = solver_direct(self.A, self.src * 1j * self.omega, timing=timing) else: eps_tot = self.eps_r + self.eps_nl X = solver_direct(self.A + self.Anl, self.src * 1j * self.omega, timing=timing) (Nx, Ny) = self.src.shape M = Nx * Ny (Dyb, Dxb, Dxf, Dyf) = unpack_derivs(self.derivs) if self.pol == 'Hz': if averaging: eps_x = grid_average(EPSILON_0_ * (eps_tot), 'x') vector_eps_x = eps_x.reshape((-1, )) eps_y = grid_average(EPSILON_0_ * (eps_tot), 'y') vector_eps_y = eps_y.reshape((-1, )) else: vector_eps_x = EPSILON_0_ * (eps_tot).reshape((-1, )) vector_eps_y = EPSILON_0_ * (eps_tot).reshape((-1, )) T_eps_x_inv = sp.spdiags(1 / vector_eps_x, 0, M, M, format=matrix_format) T_eps_y_inv = sp.spdiags(1 / vector_eps_y, 0, M, M, format=matrix_format) ex = 1 / 1j / self.omega * T_eps_y_inv.dot(Dyb).dot(X) ey = -1 / 1j / self.omega * T_eps_x_inv.dot(Dxb).dot(X) Ex = ex.reshape((Nx, Ny)) Ey = ey.reshape((Nx, Ny)) Hz = X.reshape((Nx, Ny)) if include_nl == False: self.fields['Ex'] = Ex self.fields['Ey'] = Ey self.fields['Hz'] = Hz return (Ex, Ey, Hz) elif self.pol == 'Ez': hx = -1 / 1j / self.omega / MU_0_ * Dyb.dot(X) hy = 1 / 1j / self.omega / MU_0_ * Dxb.dot(X) Hx = hx.reshape((Nx, Ny)) Hy = hy.reshape((Nx, Ny)) Ez = X.reshape((Nx, Ny)) if include_nl == False: self.fields['Hx'] = Hx self.fields['Hy'] = Hy self.fields['Ez'] = Ez return (Hx, Hy, Ez) else: raise ValueError('Invalid polarization: {}'.format(str(self.pol)))