def test_get_dft_array(self): sim = self.init() sim.init_sim() dft_fields = sim.add_dft_fields([mp.Ez], self.fcen, self.fcen, 1) fr = mp.FluxRegion(mp.Vector3(), size=mp.Vector3(self.sxy, self.sxy), direction=mp.X) dft_flux = sim.add_flux(self.fcen, 0, 1, fr) # volumes with zero thickness in x and y directions to test collapsing # of empty dimensions in DFT array and HDF5 output routines thin_x_volume = mp.Volume(center=mp.Vector3(0.35 * self.sxy), size=mp.Vector3(y=0.8 * self.sxy)) thin_x_flux = sim.add_dft_fields([mp.Ez], self.fcen, self.fcen, 1, where=thin_x_volume) thin_y_volume = mp.Volume(center=mp.Vector3(y=0.25 * self.sxy), size=mp.Vector3(x=self.sxy)) thin_y_flux = sim.add_flux(self.fcen, self.fcen, 1, mp.FluxRegion(volume=thin_y_volume)) sim.run(until_after_sources=100) # test proper collapsing of degenerate dimensions in HDF5 files and arrays thin_x_array = sim.get_dft_array(thin_x_flux, mp.Ez, 0) thin_y_array = sim.get_dft_array(thin_y_flux, mp.Ez, 0) np.testing.assert_equal(thin_x_array.ndim, 1) np.testing.assert_equal(thin_y_array.ndim, 1) sim.output_dft(thin_x_flux, 'thin-x-flux') sim.output_dft(thin_y_flux, 'thin-y-flux') with h5py.File('thin-x-flux.h5', 'r') as thin_x: thin_x_h5 = mp.complexarray(thin_x['ez_0.r'][()], thin_x['ez_0.i'][()]) with h5py.File('thin-y-flux.h5', 'r') as thin_y: thin_y_h5 = mp.complexarray(thin_y['ez_0.r'][()], thin_y['ez_0.i'][()]) np.testing.assert_allclose(thin_x_array, thin_x_h5) np.testing.assert_allclose(thin_y_array, thin_y_h5) # compare array data to HDF5 file content for fields and flux fields_arr = sim.get_dft_array(dft_fields, mp.Ez, 0) flux_arr = sim.get_dft_array(dft_flux, mp.Ez, 0) sim.output_dft(dft_fields, 'dft-fields') sim.output_dft(dft_flux, 'dft-flux') with h5py.File('dft-fields.h5', 'r') as fields, h5py.File('dft-flux.h5', 'r') as flux: exp_fields = mp.complexarray(fields['ez_0.r'][()], fields['ez_0.i'][()]) exp_flux = mp.complexarray(flux['ez_0.r'][()], flux['ez_0.i'][()]) np.testing.assert_allclose(exp_fields, fields_arr) np.testing.assert_allclose(exp_flux, flux_arr)
def test_1d_with_partial_fragment(self): # A cell with z=26, with a 16 unit block in the center, split into 3 fragments, # with the first and last fragment of length 8, and 3/8 covered by the block, # and the middle fragment completely covered. # dft_flux with 2 volumes, 1 covering the first fragment and one covering # half of the second fragment dft_vecs = make_dft_vecs(flx_reg=[ mp.FluxRegion(mp.Vector3(z=-9), mp.Vector3(z=8)), mp.FluxRegion(mp.Vector3(z=-2.5), mp.Vector3(z=5)) ]) fs = self.get_fragment_stats(mp.Vector3(z=16), mp.Vector3(z=26), 1, dft_vecs=dft_vecs) self.assertEqual(len(fs), 3) # Check first and last box sizes self.assertEqual(fs[0].box.low.z, -13) self.assertEqual(fs[0].box.high.z, -5) self.assertEqual(fs[2].box.low.z, 5) self.assertEqual(fs[2].box.high.z, 13) # Middle fragment is completely covered by block self.check_stats(fs[1], a_eps=100, a_mu=100, nonlin=300, susc=300, cond=300) # Outer fragments are 3/8 covered for i in [0, 2]: self.check_stats(fs[i], a_eps=30, a_mu=30, nonlin=90, susc=90, cond=90) # Check dft stats self.assertEqual(fs[0].num_dft_pixels, 8000) self.assertEqual(fs[1].num_dft_pixels, 5600) self.assertEqual(fs[2].num_dft_pixels, 0)
def set_source(self, sx, sy): sources = [mp.Source(mp.GaussianSource(self.fcen, fwidth=self.df), component=mp.Hx, center=mp.Vector3(0, 0, self.src_pos), size=mp.Vector3(sx, sy, 0), amp_func=pw_amp(self.k, mp.Vector3(0, 0, self.src_pos)))] refl_fr = mp.FluxRegion(center=mp.Vector3(0, 0, self.z_flux_refl), size=mp.Vector3(sx, sy, 0)) trans_fr = mp.FluxRegion(center=mp.Vector3(0, 0, self.z_flux_trans), size=mp.Vector3(sx, sy, 0)) return sources, refl_fr, trans_fr
def init(self, no_bend=False): sx = 16 sy = 32 cell = mp.Vector3(sx, sy, 0) pad = 4 w = 1 wvg_ycen = -0.5 * (sy - w - (2 * pad)) wvg_xcen = 0.5 * (sx - w - (2 * pad)) height = 100 if no_bend: no_bend_vertices = [mp.Vector3(-0.5 * sx - 5, wvg_ycen - 0.5 * w), mp.Vector3(+0.5 * sx + 5, wvg_ycen - 0.5 * w), mp.Vector3(+0.5 * sx + 5, wvg_ycen + 0.5 * w), mp.Vector3(-0.5 * sx - 5, wvg_ycen + 0.5 * w)] geometry = [mp.Prism(no_bend_vertices, height, material=mp.Medium(epsilon=12))] else: bend_vertices = [mp.Vector3(-0.5 * sx, wvg_ycen - 0.5 * w), mp.Vector3(wvg_xcen + 0.5 * w, wvg_ycen - 0.5 * w), mp.Vector3(wvg_xcen + 0.5 * w, 0.5 * sy), mp.Vector3(wvg_xcen - 0.5 * w, 0.5 * sy), mp.Vector3(wvg_xcen - 0.5 * w, wvg_ycen + 0.5 * w), mp.Vector3(-0.5 * sx, wvg_ycen + 0.5 * w)] geometry = [mp.Prism(bend_vertices, height, material=mp.Medium(epsilon=12))] fcen = 0.15 df = 0.1 sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(1 + (-0.5 * sx), wvg_ycen), size=mp.Vector3(0, w))] pml_layers = [mp.PML(1.0)] resolution = 10 nfreq = 100 self.sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution) if no_bend: fr = mp.FluxRegion(center=mp.Vector3((sx / 2) - 1.5, wvg_ycen), size=mp.Vector3(0, w * 2)) else: fr = mp.FluxRegion(center=mp.Vector3(wvg_xcen, (sy / 2) - 1.5), size=mp.Vector3(w * 2, 0)) self.trans = self.sim.add_flux(fcen, df, nfreq, fr) refl_fr = mp.FluxRegion(center=mp.Vector3((-0.5 * sx) + 1.5, wvg_ycen), size=mp.Vector3(0, w * 2)) self.refl = self.sim.add_flux(fcen, df, nfreq, refl_fr) if no_bend: self.pt = mp.Vector3((sx / 2) - 1.5, wvg_ycen) else: self.pt = mp.Vector3(wvg_xcen, (sy / 2) - 1.5)
def main(args): sz = 100 # size of cell in z direction fcen = 1 / 3.0 # center frequency of source df = fcen / 20.0 # frequency width of source amp = args.amp # amplitude of source k = 10**args.logk # Kerr susceptibility dpml = 1.0 # PML thickness # We'll use an explicitly 1d simulation. Setting dimensions=1 will actually # result in faster execution than just using two no-size dimensions. However, # in this case Meep requires us to use E in the x direction (and H in y), # and our one no-size dimension must be z. dimensions = 1 cell = mp.Vector3(0, 0, sz) pml_layers = mp.PML(dpml) resolution = 20 # to put the same material in all space, we can just set the default material # and pass it to the Simulation constructor default_material = mp.Medium(index=1, chi3=k) sources = mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ex, center=mp.Vector3(0, 0, -0.5 * sz + dpml), amplitude=amp) # frequency range for flux calculation nfreq = 400 fmin = fcen / 2.0 fmax = fcen * 4 sim = mp.Simulation(cell_size=cell, geometry=[], sources=[sources], boundary_layers=[pml_layers], default_material=default_material, resolution=resolution, dimensions=dimensions) # trans = sim.add_flux(0.5 * (fmin + fmax), fmax - fmin, nfreq, # mp.FluxRegion(mp.Vector3(0, 0, 0.5*sz - dpml - 0.5))) trans1 = sim.add_flux( fcen, 0, 1, mp.FluxRegion(mp.Vector3(0, 0, 0.5 * sz - dpml - 0.5))) trans3 = sim.add_flux( 3 * fcen, 0, 1, mp.FluxRegion(mp.Vector3(0, 0, 0.5 * sz - dpml - 0.5))) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ex, mp.Vector3(0, 0, 0.5 * sz - dpml - 0.5), 1e-6)) # sim.display_fluxes(trans) print("harmonics:, {}, {}, {}, {}".format(k, amp, mp.get_fluxes(trans1)[0], mp.get_fluxes(trans3)[0]))
def test_divide_parallel_processes(self): resolution = 20 sxy = 4 dpml = 1 cell = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml) pml_layers = [mp.PML(dpml)] n = mp.divide_parallel_processes(2) fcen = 1.0 / (n + 1) sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), center=mp.Vector3(), component=mp.Ez) ] symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] self.sim = mp.Simulation(cell_size=cell, resolution=resolution, sources=sources, symmetries=symmetries, boundary_layers=pml_layers) flux_box = self.sim.add_flux(fcen, 0, 1, mp.FluxRegion(mp.Vector3(y=0.5 * sxy), size=mp.Vector3(sxy)), mp.FluxRegion(mp.Vector3(y=-0.5 * sxy), size=mp.Vector3(sxy), weight=-1), mp.FluxRegion(mp.Vector3(0.5 * sxy), size=mp.Vector3(y=sxy)), mp.FluxRegion(mp.Vector3(-0.5 * sxy), size=mp.Vector3(y=sxy), weight=-1), decimation_factor=1) self.sim.run(until_after_sources=30) tot_flux = mp.get_fluxes(flux_box)[0] tot_fluxes = mp.merge_subgroup_data(tot_flux) fcens = mp.merge_subgroup_data(fcen) self.assertEqual(fcens[0], 1) self.assertEqual(fcens[1], 0.5) places = 4 if mp.is_single_precision() else 7 self.assertAlmostEqual(tot_fluxes[0], 9.8628728533, places=places) self.assertAlmostEqual(tot_fluxes[1], 19.6537275387, places=places)
def test_chunks(self): sxy = 10 cell = mp.Vector3(sxy, sxy, 0) fcen = 1.0 # pulse center frequency df = 0.1 # pulse width (in frequency) sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ez, mp.Vector3())] dpml = 1.0 pml_layers = [mp.PML(dpml)] resolution = 10 sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, sources=sources, resolution=resolution, split_chunks_evenly=False) sim.use_output_directory(temp_dir) top = mp.FluxRegion(center=mp.Vector3(0,+0.5*sxy-dpml), size=mp.Vector3(sxy-2*dpml,0), weight=+1.0) bot = mp.FluxRegion(center=mp.Vector3(0,-0.5*sxy+dpml), size=mp.Vector3(sxy-2*dpml,0), weight=-1.0) rgt = mp.FluxRegion(center=mp.Vector3(+0.5*sxy-dpml,0), size=mp.Vector3(0,sxy-2*dpml), weight=+1.0) lft = mp.FluxRegion(center=mp.Vector3(-0.5*sxy+dpml,0), size=mp.Vector3(0,sxy-2*dpml), weight=-1.0) tot_flux = sim.add_flux(fcen, 0, 1, top, bot, rgt, lft) sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-5)) sim.save_flux('tot_flux', tot_flux) sim1 = sim geometry = [mp.Block(center=mp.Vector3(), size=mp.Vector3(sxy, sxy, mp.inf), material=mp.Medium(index=3.5)), mp.Block(center=mp.Vector3(), size=mp.Vector3(sxy-2*dpml, sxy-2*dpml, mp.inf), material=mp.air)] sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=sources, resolution=resolution, chunk_layout=sim1) sim.use_output_directory(temp_dir) tot_flux = sim.add_flux(fcen, 0, 1, top, bot, rgt, lft) sim.load_minus_flux('tot_flux', tot_flux) sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-5)) self.assertAlmostEqual(86.90826609300862, mp.get_fluxes(tot_flux)[0])
def test_1d_with_partial_fragment(self): # A cell with z=26, with a 16 unit block in the center # dft_flux with 2 volumes, 1 covering the first 10 units and one covering # half of the second 10 dft_vecs = make_dft_vecs(flx_reg=[ mp.FluxRegion(mp.Vector3(z=-9), mp.Vector3(z=8)), mp.FluxRegion(mp.Vector3(z=-2.5), mp.Vector3(z=5)) ]) fs = self.get_fragment_stats(mp.Vector3(z=16), mp.Vector3(z=26), 1, dft_vecs=dft_vecs) self.check_stats(fs, a_eps=260, a_mu=260, nonlin=480, susc=480, cond=480) # Check dft stats self.assertEqual(fs.num_dft_pixels, 10400)
def test_cyl(self): # A 30 x 30 cell, with a 10 x 10 block in the middle # flux covering top-left fragment, near2far covering top-middle, force covering top-right dft_vecs = make_dft_vecs([ mp.FluxRegion(mp.Vector3(-10, z=10), size=mp.Vector3(10, z=10)) ], [mp.Near2FarRegion(mp.Vector3(0, z=10), size=mp.Vector3(10, z=10))], [ mp.ForceRegion(mp.Vector3(10, z=10), direction=mp.X, size=mp.Vector3(10, z=10)) ]) fs = self.get_fragment_stats(mp.Vector3(10, 0, 10), mp.Vector3(30, 0, 30), mp.CYLINDRICAL, dft_vecs=dft_vecs) self.assertEqual(fs.box.low.x, -15) self.assertEqual(fs.box.low.z, -15) self.assertEqual(fs.box.high.x, 15) self.assertEqual(fs.box.high.z, 15) self.check_stats(fs, a_eps=10000, a_mu=10000, nonlin=30000, susc=30000, cond=30000) self.assertEqual(fs.num_dft_pixels, 2040000)
def _test_3d(self, sym, pml=[]): # A 30 x 30 x 30 cell with a 10 x 10 x 10 block placed at the center # flux covering lower-front-left 10x10x10, near2far covering lower-middle-left, # force covering lower-back-left dft_vecs = make_dft_vecs([ mp.FluxRegion(mp.Vector3(-10, -10, -10), size=mp.Vector3(10, 10, 10)) ], [ mp.Near2FarRegion(mp.Vector3(-10, -10, 0), size=mp.Vector3(10, 10, 10)) ], [ mp.ForceRegion(mp.Vector3(-10, -10, 10), direction=mp.X, size=mp.Vector3(10, 10, 10)) ]) fs = self.get_fragment_stats(mp.Vector3(10, 10, 10), mp.Vector3(30, 30, 30), 3, dft_vecs=dft_vecs, sym=sym, pml=pml) sym_factor = 8 if sym else 1 self.check_stats(fs, a_eps=1000000 / sym_factor, a_mu=1000000 / sym_factor, nonlin=3000000 / sym_factor, susc=3000000 / sym_factor, cond=3000000 / sym_factor) # Check DFT regions self.assertEqual(fs.num_dft_pixels, 102000000) self.fs = fs
def add_eigenmode_monitor(self, port, wavelength, fwidth, nfreq, z=None, height=None): """ Adds an eigenmode monitor at the given port. :param port: Port at which the monitor is added :param wavelength: Center wavelength of the monitored spectrum :param fwidth: Width of the monitored spectrum :param nfreq: Number of monitored wavelengths :param z: Z-position of the monitor :param height: Height of the area for calculating the eigenmode :return: Added Meep-Monitor """ size = [ 0, port.total_width * 2 ] if port.angle % np.pi < np.pi / 4 else [port.total_width * 2, 0] monitor = self.sim.add_mode_monitor( 1 / wavelength, fwidth, nfreq, mp.FluxRegion(mp.Vector3(*port.origin, z), size=mp.Vector3(*size, height))) return monitor
def compute_flux(m, n): if m == 2: sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(sx * (-0.5 + n / ndipole), -0.5 * sy + dAg + 0.5 * dsub)) ] else: sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(0, -0.5 * sy + dAg + 0.5 * dsub), size=mp.Vector3(sx, 0), amp_func=src_amp_func(n)) ] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, k_point=mp.Vector3(), boundary_layers=pml_layers, geometry=geometry, sources=sources) flux_mon = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0.5 * sy - dpml), size=mp.Vector3(sx))) sim.run(until=run_time) flux = mp.get_fluxes(flux_mon) freqs = mp.get_flux_freqs(flux_mon) return freqs, flux
def add_flux_plane(sim, fcen, df, nfreq, position, size): """add a flux plane to a meep simulation""" position = meep.Vector3(*position) size = meep.Vector3(*size) plane = meep.FluxRegion(center=position, size=size) return sim.add_flux(fcen, df, nfreq, plane)
def get_refl_ref(): geometry = [ # mp.Block( # size=mp.Vector3(mp.inf, mp.inf, w), # center=mp.Vector3(0,0,0), # material=mp.perfect_electric_conductor), ] sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution, k_point=mp.Vector3()) refl_fr = mp.FluxRegion(center=mp.Vector3(0,0,-400), direction=mp.Y, weight=-1) refl = sim.add_flux(fcen, df, nfreq, refl_fr) pt = mp.Vector3(0,0,-400) # sim.run(until_after_sources=mp.stop_when_fields_decayed(50,mp.Ex,pt,1e-3)) sim.run(until=time) straight_refl_data = sim.get_flux_data(refl) return straight_refl_data
def _test_2d(self, sym, pml=[]): # A 30 x 30 cell, with a 10 x 10 block in the middle # flux covering top-left 10x10, near2far covering top-middle 10x10, force covering top-right dft_vecs = make_dft_vecs( [mp.FluxRegion(mp.Vector3(-10, 10), size=mp.Vector3(10, 10))], [mp.Near2FarRegion(mp.Vector3(0, 10), size=mp.Vector3(10, 10))], [mp.ForceRegion(mp.Vector3(10, 10), direction=mp.X, size=mp.Vector3(10, 10))] ) fs = self.get_fragment_stats(mp.Vector3(10, 10), mp.Vector3(30, 30), 2, dft_vecs=dft_vecs, sym=sym, pml=pml) # Check fragment boxes self.assertEqual(fs.box.low.x, -15) self.assertEqual(fs.box.low.y, -15) self.assertEqual(fs.box.high.x, 15) self.assertEqual(fs.box.high.y, 15) # Middle fragment contains entire block sym_factor = 4 if sym else 1 self.check_stats(fs, a_eps=90000 / sym_factor, a_mu=90000 / sym_factor, nonlin=30000 / sym_factor, susc=30000 / sym_factor, cond=30000 / sym_factor) # Check DFT regions self.assertEqual(fs.num_dft_pixels, 2040000) self.fs = fs
def setUp(self): self.sz = 100 fcen = 1 / 3.0 df = fcen / 20.0 self.amp = 1.0 self.k = 1e-2 self.dpml = 1.0 dimensions = 1 cell = mp.Vector3(0, 0, self.sz) default_material = mp.Medium(index=1, chi3=self.k) pml_layers = mp.PML(self.dpml) sources = mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ex, center=mp.Vector3(0, 0, (-0.5 * self.sz) + self.dpml), amplitude=self.amp) nfreq = 400 fmin = fcen / 2.0 fmax = fcen * 4 self.sim = mp.Simulation(cell_size=cell, geometry=[], sources=[sources], boundary_layers=[pml_layers], default_material=default_material, resolution=20, dimensions=dimensions) fr = mp.FluxRegion(mp.Vector3(0, 0, (0.5 * self.sz) - self.dpml - 0.5)) self.trans = self.sim.add_flux(0.5 * (fmin + fmax), fmax - fmin, nfreq, fr, decimation_factor=1) self.trans1 = self.sim.add_flux(fcen, 0, 1, fr, decimation_factor=1) self.trans3 = self.sim.add_flux(3 * fcen, 0, 1, fr, decimation_factor=1)
def _test_1d(self, sym, pml=[]): # A z=30 cell, with a size 10 block in the middle. # flux covering first 10 units, near2far covering second 10, and force covering third dft_vecs = make_dft_vecs( [mp.FluxRegion(mp.Vector3(z=-10), size=mp.Vector3(z=10))], [mp.Near2FarRegion(mp.Vector3(), size=mp.Vector3(z=10))], [ mp.ForceRegion( mp.Vector3(z=10), direction=mp.X, size=mp.Vector3(z=10)) ]) fs = self.get_fragment_stats(mp.Vector3(z=10), mp.Vector3(z=30), 1, dft_vecs=dft_vecs, sym=sym, pml=pml) sym_factor = 2 if sym else 1 self.check_stats(fs, a_eps=100 / sym_factor, a_mu=100 / sym_factor, nonlin=300 / sym_factor, susc=300 / sym_factor, cond=300 / sym_factor) # Check DFT regions self.assertEqual(fs.num_dft_pixels, 40800) self.fs = fs
def _test_1d(self, sym): # A z=30 cell, split into three fragments of size 10 each, with a block # covering the middle fragment. # flux covering first fragment, near2far covering second, force covering third dft_vecs = make_dft_vecs( [mp.FluxRegion(mp.Vector3(z=-10), size=mp.Vector3(z=10))], [mp.Near2FarRegion(mp.Vector3(), size=mp.Vector3(z=10))], [mp.ForceRegion(mp.Vector3(z=10), direction=mp.X, size=mp.Vector3(z=10))] ) fs = self.get_fragment_stats(mp.Vector3(z=10), mp.Vector3(z=30), 1, dft_vecs=dft_vecs, sym=sym) self.assertEqual(len(fs), 3) # First and last fragments have no geometry, only default_material for i in [0, 2]: self.check_stats(fs[i], 0, 0, 0, 0, 0) # Second fragment contains entire block sym_factor = 2 if sym else 1 self.check_stats(fs[1], a_eps=100 / sym_factor, a_mu=100 / sym_factor, nonlin=300 / sym_factor, susc=300 / sym_factor, cond=300 / sym_factor) # Check DFT regions self.assertEqual(fs[0].num_dft_pixels, 10240) self.assertEqual(fs[1].num_dft_pixels, 17120) self.assertEqual(fs[2].num_dft_pixels, 23840)
def register(self, sim): """ 'Register' the cell in a MEEP simulation to request computation of frequency-domain fields. Parameters ---------- sim : mp.Simulation """ self.sim = sim if self.celltype == 'flux': flux_region = mp.FluxRegion(self.region.center, self.region.size, direction=self.normal) self.dft_obj = sim.add_flux(self.fcen, self.df, self.nfreq, flux_region) else: self.dft_obj = sim.add_dft_fields(self.components, self.freqs[0], self.freqs[-1], self.nfreq, center=self.region.center, size=self.region.size) # take this opportunity to initialize simulation-dependent fields if self.grid is None: xyzw = sim.get_array_metadata(center=self.region.center, size=self.region.size, collapse=True, snap=True) fix_array_metadata(xyzw, self.region.center, self.region.size) self.grid = xyzw2grid(xyzw)
def _test_2d(self, sym): # A 30 x 30 cell, with a 10 x 10 block in the middle, split into 9 10 x 10 fragments. # flux covering top-left fragment, near2far covering top-middle, force covering top-right dft_vecs = make_dft_vecs( [mp.FluxRegion(mp.Vector3(-10, 10), size=mp.Vector3(10, 10))], [mp.Near2FarRegion(mp.Vector3(0, 10), size=mp.Vector3(10, 10))], [ mp.ForceRegion(mp.Vector3(10, 10), direction=mp.X, size=mp.Vector3(10, 10)) ]) fs = self.get_fragment_stats(mp.Vector3(10, 10), mp.Vector3(30, 30), 2, dft_vecs=dft_vecs, sym=sym) self.assertEqual(len(fs), 9) # Check fragment boxes self.assertEqual(fs[0].box.low.x, -15) self.assertEqual(fs[0].box.low.y, -15) self.assertEqual(fs[0].box.high.x, -5) self.assertEqual(fs[0].box.high.y, -5) self.assertEqual(fs[1].box.low.x, -15) self.assertEqual(fs[1].box.low.y, -5) self.assertEqual(fs[1].box.high.x, -5) self.assertEqual(fs[1].box.high.y, 5) self.assertEqual(fs[2].box.low.x, -15) self.assertEqual(fs[2].box.low.y, 5) self.assertEqual(fs[2].box.high.x, -5) self.assertEqual(fs[2].box.high.y, 15) # All fragments besides the middle one have no geometry, only default_material for i in [0, 1, 2, 3, 5, 6, 7, 8]: self.check_stats(fs[i], 0, 0, 0, 0, 0) # Middle fragment contains entire block idx = 4 sym_factor = 4 if sym else 1 self.check_stats(fs[idx], a_eps=10000 / sym_factor, a_mu=10000 / sym_factor, nonlin=30000 / sym_factor, susc=30000 / sym_factor, cond=30000 / sym_factor) # Check DFT regions for i in [0, 3, 6]: self.assertEqual(fs[i].num_dft_pixels, 0) self.assertEqual(fs[1].num_dft_pixels, 8224) self.assertEqual(fs[4].num_dft_pixels, 11792) self.assertEqual(fs[7].num_dft_pixels, 21824) self.assertEqual(fs[2].num_dft_pixels, 411200) self.assertEqual(fs[5].num_dft_pixels, 589600) self.assertEqual(fs[8].num_dft_pixels, 1091200)
def _test_3d(self, sym): # A 30 x 30 x 30 cell with a 10 x 10 x 10 block placed at the center, split # into 27 10 x 10 x 10 fragments # flux covering lower-front-left fragment, near2far covering lower-middle-left, # force covering lower-back-left dft_vecs = make_dft_vecs( [mp.FluxRegion(mp.Vector3(-10, -10, -10), size=mp.Vector3(10, 10, 10))], [mp.Near2FarRegion(mp.Vector3(-10, -10, 0), size=mp.Vector3(10, 10, 10))], [mp.ForceRegion(mp.Vector3(-10, -10, 10), direction=mp.X, size=mp.Vector3(10, 10, 10))] ) fs = self.get_fragment_stats(mp.Vector3(10, 10, 10), mp.Vector3(30, 30, 30), 3, dft_vecs=dft_vecs, sym=sym) self.assertEqual(len(fs), 27) # All fragments besides the middle one have no geometry, only default_material for i in range(27): if i == 13: continue self.check_stats(fs[i], 0, 0, 0, 0, 0) # Middle fragments contains entire block idx = 13 sym_factor = 8 if sym else 1 self.check_stats(fs[idx], a_eps=10000 / sym_factor, a_mu=10000 / sym_factor, nonlin=30000 / sym_factor, susc=30000 / sym_factor, cond=30000 / sym_factor) # Check DFT regions self.assertEqual(fs[0].num_dft_pixels, 256000) self.assertEqual(fs[1].num_dft_pixels, 428000) self.assertEqual(fs[2].num_dft_pixels, 596000)
def add_monitors(simulation, geo=None, **kwargs): geo = kwargs_to_geo(geo, **kwargs) # reflected flux refl_fr = mp.FluxRegion(center=mp.Vector3(-monitor_x(geo), 0, 0), size=mp.Vector3(0, 2 * geo.sm_width, 2 * geo.thickness)) refl = simulation.add_flux(fcen, df, nfreq, refl_fr) # transmitted flux tran_fr = mp.FluxRegion(center=mp.Vector3(monitor_x(geo), 0, 0), size=mp.Vector3(0, 2 * geo.sm_width, 2 * geo.thickness)) tran = simulation.add_flux(fcen, df, nfreq, tran_fr) return refl, tran
def get_flux_region(self): p, a, w = self.pos, int(np.round(self.angle)) % 360, self.width if a % 90 < 1e-3: raise ValueError('Flux can only be measured on orthogonal lines' f'got: {a}') s = [w, 0] if a in {0, 180} else [0, w] return mp.FluxRegion(center=mp.Vector3(*p), size=mp.Vector3(*s))
def test_cyl(self): # A 30 x 30 cell, with a 10 x 10 block in the middle, split into 9 10 x 10 fragments. # flux covering top-left fragment, near2far covering top-middle, force covering top-right dft_vecs = make_dft_vecs([ mp.FluxRegion(mp.Vector3(-10, z=10), size=mp.Vector3(10, z=10)) ], [mp.Near2FarRegion(mp.Vector3(0, z=10), size=mp.Vector3(10, z=10))], [ mp.ForceRegion(mp.Vector3(10, z=10), mp.X, size=mp.Vector3(10, z=10)) ]) fs = self.get_fragment_stats(mp.Vector3(10, 0, 10), mp.Vector3(30, 0, 30), mp.CYLINDRICAL, dft_vecs=dft_vecs) self.assertEqual(len(fs), 9) # Check fragment boxes self.assertEqual(fs[0].box.low.x, -15) self.assertEqual(fs[0].box.low.z, -15) self.assertEqual(fs[0].box.high.x, -5) self.assertEqual(fs[0].box.high.z, -5) self.assertEqual(fs[1].box.low.x, -15) self.assertEqual(fs[1].box.low.z, -5) self.assertEqual(fs[1].box.high.x, -5) self.assertEqual(fs[1].box.high.z, 5) self.assertEqual(fs[2].box.low.x, -15) self.assertEqual(fs[2].box.low.z, 5) self.assertEqual(fs[2].box.high.x, -5) self.assertEqual(fs[2].box.high.z, 15) # All fragments besides the middle one have no geometry, only default_material for i in [0, 1, 2, 3, 5, 6, 7, 8]: self.check_stats(fs[i], 0, 0, 0, 0, 0) # Middle fragment contains entire block idx = 4 self.check_stats(fs[idx], a_eps=1000, a_mu=1000, nonlin=3000, susc=3000, cond=3000) # Check DFT regions for i in [0, 3, 6]: self.assertEqual(fs[i].num_dft_pixels, 0) self.assertEqual(fs[1].num_dft_pixels, 10240) self.assertEqual(fs[4].num_dft_pixels, 17120) self.assertEqual(fs[7].num_dft_pixels, 23840) self.assertEqual(fs[2].num_dft_pixels, 51200) self.assertEqual(fs[5].num_dft_pixels, 85600) self.assertEqual(fs[8].num_dft_pixels, 119200)
def test_eigsrc_kz(self, kz_2d): resolution = 30 # pixels/um cell_size = mp.Vector3(14, 14) pml_layers = [mp.PML(thickness=2)] geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 1, mp.inf), material=mp.Medium(epsilon=12)) ] fsrc = 0.3 # frequency of eigenmode or constant-amplitude source bnum = 1 # band number of eigenmode kz = 0.2 # fixed out-of-plane wavevector component sources = [ mp.EigenModeSource(src=mp.GaussianSource(fsrc, fwidth=0.2 * fsrc), center=mp.Vector3(), size=mp.Vector3(y=14), eig_band=bnum, eig_parity=mp.EVEN_Y, eig_match_freq=True) ] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, boundary_layers=pml_layers, sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.Y)], k_point=mp.Vector3(z=kz), kz_2d=kz_2d) tran = sim.add_flux( fsrc, 0, 1, mp.FluxRegion(center=mp.Vector3(x=5), size=mp.Vector3(y=14))) sim.run(until_after_sources=50) res = sim.get_eigenmode_coefficients(tran, [1, 2], eig_parity=mp.EVEN_Y) total_flux = mp.get_fluxes(tran)[0] mode1_flux = abs(res.alpha[0, 0, 0])**2 mode2_flux = abs(res.alpha[1, 0, 0])**2 mode1_frac = 0.99 self.assertGreater(mode1_flux / total_flux, mode1_frac) self.assertLess(mode2_flux / total_flux, 1 - mode1_frac) d = 3.5 ez1 = sim.get_field_point(mp.Ez, mp.Vector3(2.3, -5.7, 4.8)) ez2 = sim.get_field_point(mp.Ez, mp.Vector3(2.3, -5.7, 4.8 + d)) ratio_ez = ez2 / ez1 phase_diff = cmath.exp(1j * 2 * cmath.pi * kz * d) self.assertAlmostEqual(ratio_ez.real, phase_diff.real, places=10) self.assertAlmostEqual(ratio_ez.imag, phase_diff.imag, places=10)
def test_waveguide_flux(self): cell_size = mp.Vector3(10, 10, 0) pml_layers = [mp.PML(thickness=2.0)] rot_angles = range( 0, 60, 20) # rotation angle of waveguide, CCW around z-axis fluxes = [] for t in rot_angles: rot_angle = math.radians(t) sources = [ mp.EigenModeSource(src=mp.GaussianSource(1.0, fwidth=0.1), size=mp.Vector3(y=10), center=mp.Vector3(x=-3), direction=mp.NO_DIRECTION, eig_kpoint=mp.Vector3( math.cos(rot_angle), math.sin(rot_angle), 0), eig_band=1, eig_parity=mp.ODD_Z, eig_match_freq=True) ] geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 1, mp.inf), e1=mp.Vector3(1, 0, 0).rotate(mp.Vector3(0, 0, 1), rot_angle), e2=mp.Vector3(0, 1, 0).rotate(mp.Vector3(0, 0, 1), rot_angle), material=mp.Medium(index=1.5)) ] sim = mp.Simulation(cell_size=cell_size, resolution=50, boundary_layers=pml_layers, sources=sources, geometry=geometry) tran = sim.add_flux( 1.0, 0, 1, mp.FluxRegion(center=mp.Vector3(x=3), size=mp.Vector3(y=10))) sim.run(until_after_sources=100) fluxes.append(mp.get_fluxes(tran)[0]) print("flux:, {:.2f}, {:.6f}".format(t, fluxes[-1])) self.assertAlmostEqual(fluxes[0], fluxes[1], places=0) self.assertAlmostEqual(fluxes[1], fluxes[2], places=0) # self.assertAlmostEqual(fluxes[0], fluxes[2], places=0) # sadly the above line requires a workaround due to the # following annoying numerical accident: # AssertionError: 100.33815231783535 != 99.81145343586365 within 0 places f0, f2 = fluxes[0], fluxes[2] self.assertLess(abs(f0 - f2), 0.5 * max(abs(f0), abs(f2)))
def register_monitors(self, frequencies): self.frequencies = np.asarray(frequencies) self.monitor = self.sim.add_mode_monitor(frequencies, mp.FluxRegion( center=self.volume.center, size=self.volume.size), yee_grid=True) self.normal_direction = self.monitor.normal_direction return self.monitor
def refl_planar(self, theta, special_kz): resolution = 100 # pixels/um dpml = 1.0 sx = 3+2*dpml sy = 1/resolution cell_size = mp.Vector3(sx,sy) pml_layers = [mp.PML(dpml,direction=mp.X)] fcen = 1.0 # source wavelength = 1 um k_point = mp.Vector3(z=math.sin(theta)).scale(fcen) sources = [mp.Source(mp.GaussianSource(fcen,fwidth=0.2*fcen), component=mp.Ez, center=mp.Vector3(-0.5*sx+dpml), size=mp.Vector3(y=sy))] sim = mp.Simulation(cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=k_point, special_kz=special_kz, resolution=resolution) refl_fr = mp.FluxRegion(center=mp.Vector3(-0.25*sx), size=mp.Vector3(y=sy)) refl = sim.add_flux(fcen,0,1,refl_fr) sim.run(until_after_sources=mp.stop_when_fields_decayed(50,mp.Ez,mp.Vector3(),1e-9)) empty_flux = mp.get_fluxes(refl) empty_data = sim.get_flux_data(refl) sim.reset_meep() geometry = [mp.Block(material=mp.Medium(index=3.5), size=mp.Vector3(0.5*sx,mp.inf,mp.inf), center=mp.Vector3(0.25*sx))] sim = mp.Simulation(cell_size=cell_size, boundary_layers=pml_layers, geometry=geometry, sources=sources, k_point=k_point, special_kz=special_kz, resolution=resolution) refl = sim.add_flux(fcen,0,1,refl_fr) sim.load_minus_flux_data(refl,empty_data) sim.run(until_after_sources=mp.stop_when_fields_decayed(50,mp.Ez,mp.Vector3(),1e-9)) refl_flux = mp.get_fluxes(refl) Rmeep = -refl_flux[0]/empty_flux[0] return Rmeep
def test_check_material_frequencies(self): mat = mp.Medium(valid_freq_range=mp.FreqRange(min=10, max=20)) invalid_sources = [ [mp.Source(mp.GaussianSource(5, fwidth=1), mp.Ez, mp.Vector3())], [mp.Source(mp.ContinuousSource(10, fwidth=1), mp.Ez, mp.Vector3())], [mp.Source(mp.GaussianSource(10, width=1), mp.Ez, mp.Vector3())], [mp.Source(mp.GaussianSource(20, width=1), mp.Ez, mp.Vector3())], ] cell_size = mp.Vector3(5, 5) resolution = 5 def check_warnings(sim, should_warn=True): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sim.run(until=5) if should_warn: self.assertEqual(len(w), 1) self.assertIn("material", str(w[-1].message)) else: self.assertEqual(len(w), 0) geom = [mp.Sphere(0.2, material=mat)] for s in invalid_sources: # Check for invalid extra_materials sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=s, extra_materials=[mat]) check_warnings(sim) # Check for invalid geometry materials sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=s, geometry=geom) check_warnings(sim) valid_sources = [ [mp.Source(mp.GaussianSource(15, fwidth=1), mp.Ez, mp.Vector3())], [mp.Source(mp.ContinuousSource(15, width=5), mp.Ez, mp.Vector3())] ] for s in valid_sources: sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=s, extra_materials=[mat]) check_warnings(sim, False) # Check DFT frequencies # Invalid extra_materials sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=valid_sources[0], extra_materials=[mat]) fregion = mp.FluxRegion(center=mp.Vector3(0, 1), size=mp.Vector3(2, 2), direction=mp.X) sim.add_flux(18, 6, 2, fregion) check_warnings(sim) # Invalid geometry material sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=valid_sources[0], geometry=geom) sim.add_flux(18, 6, 2, fregion) check_warnings(sim)
def register_monitors(self, fcen, df, nf): self.fcen = fcen self.df = df self.nf = nf self.monitor = self.sim.add_mode_monitor( self.fcen, self.df, self.nf, mp.FluxRegion(center=self.volume.center, size=self.volume.size)) self.normal_direction = self.monitor.normal_direction return self.monitor