def test_grad_for_E(self): print('\ttesting E fields in FDTD (forward mode)') F = fdtd(self.eps_r, dL=self.dL, npml=self.pml) def objective(c): F = fdtd(c * self.eps_r, dL=self.dL, npml=self.pml) S = 0.0 for t_index in range(self.steps): fields = F.forward(Jx=self.gaussian(t_index)) S += fields['Ex'] + fields['Ey'] + fields['Ez'] return S c0 = 2.0 jac_autograd_for = jacobian(objective, mode='forward')(c0) jac_numerical = jacobian(objective, mode='numerical', step_size=DEPS)(c0) if VERBOSE: print('\tobjective function value: ', objective(self.eps_arr)) print('\tjacobian (auto): \n\t\t', jac_autograd_for) print('\tjacobian (num): \n\t\t', jac_numerical) self.check_gradient_error(jac_numerical, jac_autograd_for)
def spectral_power(ff): """ This function computes the spectral power as a function of fill factor will autograd/differentiate this with FMD (cevijacobian function) """ # setup FDTD using explicit projection into permittivity eps_teeth_proj = projection(teeth_density, (1 - ff), sub_eps, grating_eps) eps_total = eps_teeth_proj + eps_base F = fdtd(eps_total, dl, [npml, npml, 0]) # compute fields at each time step at the source above # note: we're solving the reciprocal problem of a waveguide -> free space coupler measured = [] print('-> running FDTD within objective function') for t_index in range(steps): if t_index % 1000 == 0: print(' - done with time step {} of {} ({}%)'.format( t_index, steps, int(t_index / steps * 100))) fields = F.forward(Jz=source_fn(t_index)) measured.append(npa.sum(fields['Ez'] * source)) # get spectral power through FFT print('-> computing FFT') measured_f = my_fft(npa.array(measured)) spect_power = npa.square(npa.abs(measured_f)) / source_p return spect_power
def objective(c): F = fdtd(c * self.eps_r, dL=self.dL, npml=self.pml) S = 0.0 for t_index in range(self.steps): fields = F.forward(Jx=self.gaussian(t_index)) S += fields['Ex'] + fields['Ey'] + fields['Ez'] return S
def test_fields_H(self): F = fdtd(self.eps_r, dL=self.dL, npml=self.pml) fig, ax = plt.subplots(figsize=(10, 10)) im = ax.pcolormesh(np.zeros((self.Nx, self.Ny)), cmap='RdBu') for t_index in range(self.steps): fields = F.forward(Jx=self.gaussian(t_index)) if t_index % self.skip_rate == 0: max_E = np.abs(fields['Hz']).max() im.set_array(fields['Hz'][:, :, 0].ravel()) im.set_clim([-1, 1]) plt.pause(0.001) ax.set_title('time = {}'.format(t_index))
def test_grad_rev_E(self): print('\ttesting E fields in FDTD (reverse mode)') F = fdtd(self.eps_r, dL=self.dL, npml=self.pml) def objective(eps_arr): F.eps_r = eps_arr.reshape((self.Nx, self.Ny, self.Nz)) S = 0.0 for t_index in range(self.steps): fields = F.forward(Jz=self.gaussian(t_index)) S += npa.sum(fields['Ex'] + fields['Ey'] + fields['Ez']) return S jac_autograd_rev = jacobian(objective, mode='reverse')(self.eps_arr) jac_numerical = jacobian(objective, mode='numerical', step_size=DEPS)(self.eps_arr) if VERBOSE: print('\tobjective function value: ', objective(self.eps_arr)) print('\tjacobian (auto): \n\t\t', jac_autograd_rev) print('\tjacobian (num): \n\t\t', jac_numerical) self.check_gradient_error(jac_numerical, jac_autograd_rev)
omega = 2*np.pi*200e12 dL = 5e-8 pml = [npml, npml, 0] # source parameters sigma = 10e-15 total_time = 0.5e-12 t0 = sigma * 10 source_amp = 1 source_pos = np.zeros((Nx, Ny, Nz)) source_pos[npml+10, Ny//2, Nz//2] = source_amp # starting relative permittivity (random for debugging) eps_r = np.random.random((Nx, Ny, Nz)) + 1 F = fdtd(eps_r, dL=dL, npml=pml) dt = F.dt steps = int(total_time / dt) print('{} time steps'.format(steps)) gaussian = lambda t: source_amp * np.exp(-(t - t0 / dt)**2 / 2 / (sigma / dt)**2) source = lambda t: source_pos * gaussian(t) * np.cos(omega * t * dt) plt.plot(dt * np.arange(steps), np.sum(source(np.arange(steps)), axis=(0,1))) plt.xlabel('time (sec)') plt.ylabel('source amplitude') plt.show() measure_pos = np.zeros((Nx, Ny, Nz)) measure_pos[-npml-10, Ny//2, Nz//2] = 1
# plot the wavegiude mode source array if plot_all: plt.imshow(np.real(imarr(wg_mode)), cmap='magma') plt.title('modal source array') plt.xlabel('x') plt.ylabel('y') plt.show() """ DEFINE TIME DOMAIN PROBLEM """ # reshape things for the FDTD eps_total = eps_total.reshape((Nx, Ny, 1)) source = source.reshape((Nx, Ny, 1)) # make an FDTD and get its time step F_t = fdtd(eps_total, dl, [npml, npml, 0]) dt = F_t.dt steps = int(total_time / dt) times = np.arange(steps) print('-> total of {} time steps'.format(steps)) # make a gaussian source gaussian = lambda t: np.exp(-(t - t0 / dt)**2 / 2 / (sigma / dt)**2) * np.cos(omega0 * t * dt) source_fn = lambda t: np.abs(wg_mode) * gaussian(t) spect_in = np.square(np.abs(my_fft(gaussian(times)))) delta_f = 1 / steps / dt freq_x = np.arange(steps) * delta_f # compute normalization power spectrum F_norm = fdtd(eps_base.reshape((Nx, Ny, 1)), dl, [npml, npml, 0])