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_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, 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 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): # 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 _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 _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 forward_simulation(design_params): matgrid = mp.MaterialGrid(mp.Vector3(Nr, 0, Nz), SiO2, Si, weights=design_params.reshape(Nr, 1, Nz)) geometry = [ mp.Block(center=mp.Vector3(design_r / 2, 0, 0), size=mp.Vector3(design_r, 0, design_z), material=matgrid) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, sources=source, geometry=geometry, dimensions=dimensions, m=m) frequencies = [fcen] far_x = [mp.Vector3(5, 0, 20)] mode = sim.add_near2far( frequencies, mp.Near2FarRegion(center=mp.Vector3( design_r / 2, 0, (sz / 2 - dpml + design_z / 2) / 2), size=mp.Vector3(design_r, 0, 0), weight=+1)) sim.run(until_after_sources=1200) Er = sim.get_farfield(mode, far_x[0]) sim.reset_meep() return abs(Er[0])**2
def define_near_field_box(self, wvl_near_min=0.6, wvl_near_max=1, nfreq_near=1, near_box_dis=1): if self._sim is not None: self.wvl_near_min = wvl_near_min self.wvl_near_max = wvl_near_max self.nfreq_near = nfreq_near self.near_box_dis = near_box_dis #distance from the whole structure self.f_near_min = 1 / self.wvl_near_max self.f_near_max = 1 / self.wvl_near_min self.fcen_near = 0.5 * (self.f_near_min + self.f_near_max) self.df_near = self.f_near_max - self.f_near_min #upper surface self.near_field_z1 = self._sim.add_near2far( self.fcen_near, self.df_near, self.nfreq_near, mp.Near2FarRegion(center=mp.Vector3( 0, 0, self.source_position + self.shift + self.near_box_dis), size=mp.Vector3(2 * self.near_box_dis), direction=mp.Z, weight=+1))
def free_space_radiation(self): sxy = 4 dpml = 1 cell_size = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml) pml_layers = [mp.PML(dpml)] fcen = 1 / self.wvl sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), center=mp.Vector3(), component=self.src_cmpt) ] if self.src_cmpt == mp.Hz: symmetries = [mp.Mirror(mp.X, phase=-1), mp.Mirror(mp.Y, phase=-1)] elif self.src_cmpt == mp.Ez: symmetries = [mp.Mirror(mp.X, phase=+1), mp.Mirror(mp.Y, phase=+1)] else: symmetries = [] sim = mp.Simulation(cell_size=cell_size, resolution=self.resolution, sources=sources, symmetries=symmetries, boundary_layers=pml_layers, default_material=mp.Medium(index=self.n)) nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0, +0.5 * sxy), size=mp.Vector3(sxy, 0)), mp.Near2FarRegion(center=mp.Vector3(0, -0.5 * sxy), size=mp.Vector3(sxy, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(+0.5 * sxy, 0), size=mp.Vector3(0, sxy)), mp.Near2FarRegion(center=mp.Vector3(-0.5 * sxy, 0), size=mp.Vector3(0, sxy), weight=-1)) sim.run(until_after_sources=mp.stop_when_dft_decayed()) Pr = self.radial_flux(sim, nearfield_box, self.r) return Pr
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 setUp(self): resolution = 50 sxy = 4 dpml = 1 cell = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml, 0) pml_layers = mp.PML(dpml) fcen = 1.0 df = 0.4 self.src_cmpt = mp.Ez sources = mp.Source( src=mp.GaussianSource(fcen, fwidth=df), center=mp.Vector3(), component=self.src_cmpt ) if self.src_cmpt == mp.Ex: symmetries = [mp.Mirror(mp.Y)] elif self.src_cmpt == mp.Ey: symmetries = [mp.Mirror(mp.X)] elif self.src_cmpt == 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] ) self.nearfield = self.sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(0, 0.5 * sxy), size=mp.Vector3(sxy)), mp.Near2FarRegion(mp.Vector3(0, -0.5 * sxy), size=mp.Vector3(sxy), weight=-1.0), mp.Near2FarRegion(mp.Vector3(0.5 * sxy), size=mp.Vector3(0, sxy)), mp.Near2FarRegion(mp.Vector3(-0.5 * sxy), size=mp.Vector3(0, sxy), weight=-1.0) )
def near2far(position, size): """create 6 near2far planes of a closed box with size and position""" position = meep.Vector3(*position) size = meep.Vector3(*size) x1 = meep.Near2FarRegion(center=position - meep.Vector3(size[0] / 2, 0, 0), size=meep.Vector3(0, size[1], size[2]), weight=-1) x2 = meep.Near2FarRegion(center=position + meep.Vector3(size[0] / 2, 0, 0), size=meep.Vector3(0, size[1], size[2]), weight=1) y1 = meep.Near2FarRegion(center=position - meep.Vector3(0, size[1] / 2, 0), size=meep.Vector3(size[0], 0, size[2]), weight=-1) y2 = meep.Near2FarRegion(center=position + meep.Vector3(0, size[1] / 2, 0), size=meep.Vector3(size[0], 0, size[2]), weight=1) z1 = meep.Near2FarRegion(center=position - meep.Vector3(0, 0, size[2] / 2), size=meep.Vector3(size[0], size[1], 0), weight=-1) z2 = meep.Near2FarRegion(center=position + meep.Vector3(0, 0, size[2] / 2), size=meep.Vector3(size[0], size[1], 0), weight=1) return [x1, x2, y1, y2, z1, z2]
def adjoint_solver(design_params): design_variables = mp.MaterialGrid(mp.Vector3(Nr, 0, Nz), SiO2, Si) design_region = mpa.DesignRegion( design_variables, volume=mp.Volume(center=mp.Vector3(design_r / 2, 0, 0), size=mp.Vector3(design_r, 0, design_z))) geometry = [ mp.Block(center=design_region.center, size=design_region.size, material=design_variables) ] sim = mp.Simulation(cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=source, resolution=resolution, dimensions=dimensions, m=m) far_x = [mp.Vector3(5, 0, 20)] NearRegions = [ mp.Near2FarRegion(center=mp.Vector3( design_r / 2, 0, (sz / 2 - dpml + design_z / 2) / 2), size=mp.Vector3(design_r, 0, 0), weight=+1) ] FarFields = mpa.Near2FarFields(sim, NearRegions, far_x) ob_list = [FarFields] def J(alpha): return npa.abs(alpha[0, 0, 0])**2 opt = mpa.OptimizationProblem(simulation=sim, objective_functions=J, objective_arguments=ob_list, design_regions=[design_region], fcen=fcen, df=0, nf=1, maximum_run_time=1200) f, dJ_du = opt([design_params]) sim.reset_meep() return f, dJ_du
sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=src_pt)] k_point = mp.Vector3() glass = mp.Medium(index=1.5) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, k_point=k_point, default_material=glass, sources=sources) nfreq = 21 n2f_pt = mp.Vector3(0.5*sx-dpml-0.5*dpad) n2f_obj = sim.add_near2far(fcen, df, nfreq, mp.Near2FarRegion(center=n2f_pt)) sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, n2f_pt, 1e-9)) ff_source = sim.get_farfields(n2f_obj, ff_res, center=mp.Vector3(ff_distance,0.5*ff_length), size=mp.Vector3(y=ff_length)) sim.reset_meep() ### unit cell with periodic boundaries sy = gp cell_size = mp.Vector3(sx,sy) sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=src_pt, size=mp.Vector3(y=sy))] geometry = [mp.Block(material=glass, size=mp.Vector3(dpml+dsub,mp.inf,mp.inf), center=mp.Vector3(-0.5*sx+0.5*(dpml+dsub))),
symmetries = [mp.Mirror(mp.X, phase=+1), mp.Mirror(mp.Y, phase=-1)] elif src_cmpt == mp.Ez: symmetries = [mp.Mirror(mp.X, phase=+1), mp.Mirror(mp.Y, phase=+1)] sim = mp.Simulation( cell_size=cell, resolution=resolution, sources=sources, # symmetries=symmetries, boundary_layers=pml_layers) # The flux is measured in a box of 1 um side around the source but these are used to get the far field nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0, +0.5 * sxy), size=mp.Vector3(sxy, 0), weight=+1), mp.Near2FarRegion(center=mp.Vector3(0, -0.5 * sxy), size=mp.Vector3(sxy, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(+0.5 * sxy, 0), size=mp.Vector3(0, sxy), weight=+1), mp.Near2FarRegion(center=mp.Vector3(-0.5 * sxy, 0), size=mp.Vector3(0, sxy), weight=-1)) flux_box = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mp.Vector3(0, +0.5 * sxy), size=mp.Vector3(sxy, 0),
component=mp.Hz, center=mp.Vector3()) symmetries = [mp.Mirror(mp.Y, phase=-1), mp.Mirror(mp.X, phase=-1)] d1 = 0.2 sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[sources], symmetries=symmetries, boundary_layers=[pml_layers], resolution=resolution) nearfield = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(0, 0.5 * w + d1), size=mp.Vector3(2 * dpml - sx)), mp.Near2FarRegion(mp.Vector3(-0.5 * sx + dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(0, d1), weight=-1.0), mp.Near2FarRegion(mp.Vector3(0.5 * sx - dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(0, d1)) ) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Hz, mp.Vector3(0.12, -0.37), 1e-8)) d2 = 20 h = 4 sim.output_farfields(nearfield, "spectra-{}-{}-{}".format(d1, d2, h), resolution, where=mp.Volume(mp.Vector3(0, (0.5 * w) + d2 + (0.5 * h)), size=mp.Vector3(sx - 2 * dpml, h)))
def main(from_um_factor, resolution, courant, r, material, paper, reference, submerged_index, displacement, surface_index, wlen_range, nfreq, air_r_factor, pml_wlen_factor, flux_r_factor, time_factor_cell, second_time_factor, series, folder, parallel, n_processes, n_cores, n_nodes, split_chunks_evenly, load_flux, load_chunks, near2far): #%% CLASSIC INPUT PARAMETERS """ # Simulation size from_um_factor = 10e-3 # Conversion of 1 μm to my length unit (=10nm/1μm) resolution = 2 # >=8 pixels per smallest wavelength, i.e. np.floor(8/wvl_min) courant = 0.5 # Nanoparticle specifications: Sphere in Vacuum :) r = 51.5 # Radius of sphere in nm paper = "R" reference = "Meep" displacement = 0 # Displacement of the surface from the bottom of the sphere in nm submerged_index = 1 # 1.33 for water surface_index = 1 # 1.54 for glass # Frequency and wavelength wlen_range = np.array([350,500]) # Wavelength range in nm nfreq = 100 # Box dimensions pml_wlen_factor = 0.38 air_r_factor = 0.5 flux_r_factor = 0 #0.1 # Simulation time time_factor_cell = 1.2 second_time_factor = 10 # Saving directories series = "SilverRes4" folder = "Test/TestSilver" # Configuration parallel = False n_processes = 1 n_cores = 1 n_nodes = 1 split_chunks_evenly = True load_flux = False load_chunks = True near2far = False """ #%% MORE INPUT PARAMETERS # Frequency and wavelength cutoff = 3.2 # Gaussian planewave source's parameter of shape nazimuthal = 16 npolar = 20 ### TREATED INPUT PARAMETERS # Nanoparticle specifications: Sphere in Vacuum :) r = r / (from_um_factor * 1e3) # Now in Meep units if reference == "Meep": medium = vmt.import_medium(material, from_um_factor=from_um_factor, paper=paper) # Importing material constants dependant on frequency from Meep Library elif reference == "RIinfo": medium = vmt.MediumFromFile(material, paper=paper, reference=reference, from_um_factor=from_um_factor) # Importing material constants dependant on frequency from external file else: raise ValueError("Reference for medium not recognized. Sorry :/") displacement = displacement / (from_um_factor * 1e3) # Now in Meep units # Frequency and wavelength wlen_range = np.array(wlen_range) wlen_range = wlen_range / (from_um_factor * 1e3) # Now in Meep units freq_range = 1 / wlen_range # Hz range in Meep units from highest to lowest freq_center = np.mean(freq_range) freq_width = max(freq_range) - min(freq_range) # Space configuration pml_width = pml_wlen_factor * max(wlen_range) # 0.5 * max(wlen_range) air_width = air_r_factor * r # 0.5 * max(wlen_range) flux_box_size = 2 * (1 + flux_r_factor) * r # Saving directories if series is None: series = "Test" if folder is None: folder = "Test" params_list = [ "from_um_factor", "resolution", "courant", "material", "r", "paper", "reference", "submerged_index", "displacement", "surface_index", "wlen_range", "nfreq", "nazimuthal", "npolar", "cutoff", "flux_box_size", "cell_width", "pml_width", "air_width", "source_center", "until_after_sources", "time_factor_cell", "second_time_factor", "enlapsed", "parallel", "n_processes", "n_cores", "n_nodes", "split_chunks_evenly", "near2far", "script", "sysname", "path" ] #%% GENERAL GEOMETRY SETUP air_width = air_width - air_width % (1 / resolution) pml_width = pml_width - pml_width % (1 / resolution) pml_layers = [mp.PML(thickness=pml_width)] # symmetries = [mp.Mirror(mp.Y), # mp.Mirror(mp.Z, phase=-1)] # Two mirror planes reduce cell size to 1/4 # Issue related that lead me to comment this lines: # https://github.com/NanoComp/meep/issues/1484 cell_width = 2 * (pml_width + air_width + r) cell_width = cell_width - cell_width % (1 / resolution) cell_size = mp.Vector3(cell_width, cell_width, cell_width) # surface_center = r/4 - displacement/2 + cell_width/4 # surface_center = surface_center - surface_center%(1/resolution) # displacement = r/2 + cell_width/2 - 2*surface_center displacement = displacement - displacement % (1 / resolution) flux_box_size = flux_box_size - flux_box_size % (1 / resolution) source_center = -0.5 * cell_width + pml_width sources = [ mp.Source(mp.GaussianSource(freq_center, fwidth=freq_width, is_integrated=True, cutoff=cutoff), center=mp.Vector3(source_center), size=mp.Vector3(0, cell_width, cell_width), component=mp.Ez) ] # Ez-polarized planewave pulse # (its size parameter fills the entire cell in 2d) # >> The planewave source extends into the PML # ==> is_integrated=True must be specified until_after_sources = time_factor_cell * cell_width * submerged_index # Enough time for the pulse to pass through all the cell # Originally: Aprox 3 periods of lowest frequency, using T=λ/c=λ in Meep units # Now: Aprox 3 periods of highest frequency, using T=λ/c=λ in Meep units geometry = [mp.Sphere(material=medium, center=mp.Vector3(), radius=r)] # Au sphere with frequency-dependant characteristics imported from Meep. if surface_index != 1: geometry = [ mp.Block(material=mp.Medium(index=surface_index), center=mp.Vector3( r / 2 - displacement / 2 + cell_width / 4, 0, 0), size=mp.Vector3(cell_width / 2 - r + displacement, cell_width, cell_width)), *geometry ] # A certain material surface underneath it home = vs.get_home() sysname = vs.get_sys_name() path = os.path.join(home, folder, series) if not os.path.isdir(path) and vm.parallel_assign(0, n_processes, parallel): os.makedirs(path) file = lambda f: os.path.join(path, f) # Computation enlapsed = [] parallel_specs = np.array([n_processes, n_cores, n_nodes], dtype=int) max_index = np.argmax(parallel_specs) for index, item in enumerate(parallel_specs): if item == 0: parallel_specs[index] = 1 parallel_specs[0:max_index] = np.full(parallel_specs[0:max_index].shape, max(parallel_specs)) n_processes, n_cores, n_nodes = parallel_specs parallel = max(parallel_specs) > 1 del parallel_specs, max_index, index, item if parallel: np_process = mp.count_processors() else: np_process = 1 #%% FIRST RUN measure_ram() params = {} for p in params_list: params[p] = eval(p) stable, max_courant = vm.check_stability(params) if stable: print("As a whole, the simulation should be stable") else: print("As a whole, the simulation could not be stable") print(f"Recommended maximum courant factor is {max_courant}") if load_flux: try: flux_path = vm.check_midflux(params)[0] flux_needed = False except: flux_needed = True else: flux_needed = True if load_chunks and not split_chunks_evenly: try: chunks_path = vm.check_chunks(params)[0] chunk_layout = os.path.join(chunks_path, "Layout.h5") chunks_needed = False except: chunks_needed = True flux_needed = True else: if not split_chunks_evenly: chunks_needed = True flux_needed = True else: chunk_layout = None chunks_needed = False if chunks_needed: sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), output_single_precision=True, split_chunks_evenly=split_chunks_evenly, # symmetries=symmetries, geometry=geometry) sim.init_sim() chunks_path = vm.save_chunks(sim, params, path) chunk_layout = os.path.join(chunks_path, "Layout.h5") del sim if flux_needed: #% FIRST RUN: SET UP measure_ram() sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), split_chunks_evenly=split_chunks_evenly, chunk_layout=chunk_layout, output_single_precision=True) #, # symmetries=symmetries) # >> k_point zero specifies boundary conditions needed # for the source to be infinitely extended measure_ram() # Scattered power --> Computed by surrounding it with closed DFT flux box # (its size and orientation are irrelevant because of Poynting's theorem) box_x1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_x2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_y1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_y2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_z1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) box_z2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) # Funny you can encase the sphere (r radius) so closely (2r-sided box) measure_ram() if near2far: near2far_box = sim.add_near2far( freq_center, freq_width, nfreq, mp.Near2FarRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=+1)) measure_ram() else: near2far_box = None # used_ram.append(used_ram[-1]) #% FIRST RUN: INITIALIZE temp = time() sim.init_sim() enlapsed.append(time() - temp) measure_ram() step_ram_function = lambda sim: measure_ram() #% FIRST RUN: SIMULATION NEEDED TO NORMALIZE temp = time() sim.run(mp.at_beginning(step_ram_function), mp.at_time(int(until_after_sources / 2), step_ram_function), mp.at_end(step_ram_function), until_after_sources=until_after_sources) # mp.stop_when_fields_decayed( # np.mean(wlen_range), # dT = mean period of source # mp.Ez, # Component of field to check # mp.Vector3(0.5*cell_width - pml_width, 0, 0), # Where to check # 1e-3)) # Factor to decay enlapsed.append(time() - temp) #% SAVE MID DATA for p in params_list: params[p] = eval(p) flux_path = vm.save_midflux(sim, box_x1, box_x2, box_y1, box_y2, box_z1, box_z2, near2far_box, params, path) freqs = np.asarray(mp.get_flux_freqs(box_x1)) box_x1_flux0 = np.asarray(mp.get_fluxes(box_x1)) box_x2_flux0 = np.asarray(mp.get_fluxes(box_x2)) box_y1_flux0 = np.asarray(mp.get_fluxes(box_y1)) box_y2_flux0 = np.asarray(mp.get_fluxes(box_y2)) box_z1_flux0 = np.asarray(mp.get_fluxes(box_z1)) box_z2_flux0 = np.asarray(mp.get_fluxes(box_z2)) data_mid = np.array([ 1e3 * from_um_factor / freqs, box_x1_flux0, box_x2_flux0, box_y1_flux0, box_y2_flux0, box_z1_flux0, box_z2_flux0 ]).T header_mid = [ "Longitud de onda [nm]", "Flujo X10 [u.a.]", "Flujo X20 [u.a]", "Flujo Y10 [u.a]", "Flujo Y20 [u.a]", "Flujo Z10 [u.a]", "Flujo Z20 [u.a]" ] if vm.parallel_assign(0, n_processes, parallel): vs.savetxt(file("MidFlux.txt"), data_mid, header=header_mid, footer=params) if not split_chunks_evenly: vm.save_chunks(sim, params, path) if parallel: f = h5.File(file("MidRAM.h5"), "w", driver='mpio', comm=MPI.COMM_WORLD) current_process = mp.my_rank() f.create_dataset("RAM", (len(used_ram), np_process), dtype="float") f["RAM"][:, current_process] = used_ram for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", (len(used_ram), np_process), dtype="int") f["SWAP"][:, current_process] = swapped_ram for a in params: f["SWAP"].attrs[a] = params[a] else: f = h5.File(file("MidRAM.h5"), "w") f.create_dataset("RAM", data=used_ram) for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", data=swapped_ram) for a in params: f["SWAP"].attrs[a] = params[a] f.close() del f #% PLOT FLUX FOURIER MID DATA if vm.parallel_assign(1, np_process, parallel): ylims = (np.min(data_mid[:, 1:]), np.max(data_mid[:, 1:])) ylims = (ylims[0] - .1 * (ylims[1] - ylims[0]), ylims[1] + .1 * (ylims[1] - ylims[0])) fig, ax = plt.subplots(3, 2, sharex=True) fig.subplots_adjust(hspace=0, wspace=.05) for a in ax[:, 1]: a.yaxis.tick_right() a.yaxis.set_label_position("right") for a, h in zip(np.reshape(ax, 6), header_mid[1:]): a.set_ylabel(h) for d, a in zip(data_mid[:, 1:].T, np.reshape(ax, 6)): a.plot(1e3 * from_um_factor / freqs, d) a.set_ylim(*ylims) ax[-1, 0].set_xlabel("Wavelength [nm]") ax[-1, 1].set_xlabel("Wavelength [nm]") plt.savefig(file("MidFlux.png")) del fig, ax, ylims, a, h sim.reset_meep() #%% SECOND RUN: SETUP measure_ram() sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), output_single_precision=True, split_chunks_evenly=split_chunks_evenly, chunk_layout=chunk_layout, # symmetries=symmetries, geometry=geometry) measure_ram() box_x1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_x2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_y1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_y2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_z1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) box_z2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) measure_ram() if near2far: near2far_box = sim.add_near2far( freq_center, freq_width, nfreq, mp.Near2FarRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=+1)) measure_ram() else: near2far_box = None # used_ram.append(used_ram[-1]) #%% SECOND RUN: INITIALIZE temp = time() sim.init_sim() enlapsed.append(time() - temp) measure_ram() #%% LOAD FLUX FROM FILE vm.load_midflux(sim, box_x1, box_x2, box_y1, box_y2, box_z1, box_z2, near2far_box, flux_path) measure_ram() freqs = np.asarray(mp.get_flux_freqs(box_x1)) box_x1_flux0 = np.asarray(mp.get_fluxes(box_x1)) box_x1_data = sim.get_flux_data(box_x1) box_x2_data = sim.get_flux_data(box_x2) box_y1_data = sim.get_flux_data(box_y1) box_y2_data = sim.get_flux_data(box_y2) box_z1_data = sim.get_flux_data(box_z1) box_z2_data = sim.get_flux_data(box_z2) if near2far: near2far_data = sim.get_near2far_data(near2far_box) temp = time() sim.load_minus_flux_data(box_x1, box_x1_data) sim.load_minus_flux_data(box_x2, box_x2_data) sim.load_minus_flux_data(box_y1, box_y1_data) sim.load_minus_flux_data(box_y2, box_y2_data) sim.load_minus_flux_data(box_z1, box_z1_data) sim.load_minus_flux_data(box_z2, box_z2_data) if near2far: sim.load_minus_near2far_data(near2far_box, near2far_data) enlapsed.append(time() - temp) del box_x1_data, box_x2_data, box_y1_data, box_y2_data del box_z1_data, box_z2_data if near2far: del near2far_data measure_ram() #%% SECOND RUN: SIMULATION :D step_ram_function = lambda sim: measure_ram() temp = time() sim.run(mp.at_beginning(step_ram_function), mp.at_time(int(second_time_factor * until_after_sources / 2), step_ram_function), mp.at_end(step_ram_function), until_after_sources=second_time_factor * until_after_sources) # mp.stop_when_fields_decayed( # np.mean(wlen_range), # dT = mean period of source # mp.Ez, # Component of field to check # mp.Vector3(0.5*cell_width - pml_width, 0, 0), # Where to check # 1e-3)) # Factor to decay enlapsed.append(time() - temp) del temp # Aprox 30 periods of lowest frequency, using T=λ/c=λ in Meep units box_x1_flux = np.asarray(mp.get_fluxes(box_x1)) box_x2_flux = np.asarray(mp.get_fluxes(box_x2)) box_y1_flux = np.asarray(mp.get_fluxes(box_y1)) box_y2_flux = np.asarray(mp.get_fluxes(box_y2)) box_z1_flux = np.asarray(mp.get_fluxes(box_z1)) box_z2_flux = np.asarray(mp.get_fluxes(box_z2)) #%% SCATTERING ANALYSIS scatt_flux = box_x1_flux - box_x2_flux scatt_flux = scatt_flux + box_y1_flux - box_y2_flux scatt_flux = scatt_flux + box_z1_flux - box_z2_flux intensity = box_x1_flux0 / (flux_box_size)**2 # Flux of one of the six monitor planes / Área # (the closest one, facing the planewave source) # This is why the six sides of the flux box are separated # (Otherwise, the box could've been one flux object with weights ±1 per side) scatt_cross_section = np.divide(scatt_flux, intensity) # Scattering cross section σ = # = scattered power in all directions / incident intensity. scatt_eff_meep = -1 * scatt_cross_section / (np.pi * r**2) # Scattering efficiency = # = scattering cross section / cross sectional area of the sphere freqs = np.array(freqs) scatt_eff_theory = [ ps.MieQ(np.sqrt(medium.epsilon(f)[0, 0] * medium.mu(f)[0, 0]), 1e3 * from_um_factor / f, 2 * r * 1e3 * from_um_factor, nMedium=submerged_index, asDict=True)['Qsca'] for f in freqs ] # The simulation results are validated by comparing with # analytic theory of PyMieScatt module #%% ANGULAR PATTERN ANALYSIS if near2far: fraunhofer_distance = 8 * (r**2) / min(wlen_range) radial_distance = max(10 * fraunhofer_distance, 1.5 * cell_width / 2) # radius of far-field circle must be at least Fraunhofer distance azimuthal_angle = np.arange(0, 2 + 2 / nazimuthal, 2 / nazimuthal) # in multiples of pi polar_angle = np.arange(0, 1 + 1 / npolar, 1 / npolar) poynting_x = [] poynting_y = [] poynting_z = [] poynting_r = [] for phi in azimuthal_angle: poynting_x.append([]) poynting_y.append([]) poynting_z.append([]) poynting_r.append([]) for theta in polar_angle: farfield_dict = sim.get_farfields( near2far_box, 1, where=mp.Volume(center=mp.Vector3( radial_distance * np.cos(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.sin(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.cos(np.pi * theta)))) Px = farfield_dict["Ey"] * np.conjugate(farfield_dict["Hz"]) Px -= farfield_dict["Ez"] * np.conjugate(farfield_dict["Hy"]) Py = farfield_dict["Ez"] * np.conjugate(farfield_dict["Hx"]) Py -= farfield_dict["Ex"] * np.conjugate(farfield_dict["Hz"]) Pz = farfield_dict["Ex"] * np.conjugate(farfield_dict["Hy"]) Pz -= farfield_dict["Ey"] * np.conjugate(farfield_dict["Hx"]) Px = np.real(Px) Py = np.real(Py) Pz = np.real(Pz) poynting_x[-1].append(Px) poynting_y[-1].append(Py) poynting_z[-1].append(Pz) poynting_r[-1].append( np.sqrt(np.square(Px) + np.square(Py) + np.square(Pz))) poynting_x = np.array(poynting_x) poynting_y = np.array(poynting_y) poynting_z = np.array(poynting_z) poynting_r = np.array(poynting_r) #%% SAVE FINAL DATA for p in params_list: params[p] = eval(p) data = np.array( [1e3 * from_um_factor / freqs, scatt_eff_meep, scatt_eff_theory]).T header = [ "Longitud de onda [nm]", "Sección eficaz efectiva (Meep) [u.a.]", "Sección eficaz efectiva (Theory) [u.a.]" ] data_base = np.array([ 1e3 * from_um_factor / freqs, box_x1_flux0, box_x1_flux, box_x2_flux, box_y1_flux, box_y2_flux, box_z1_flux, box_z2_flux, intensity, scatt_flux, scatt_cross_section ]).T header_base = [ "Longitud de onda [nm]", "Flujo X10 [u.a.]", "Flujo X1 [u.a]", "Flujo X2 [u.a]", "Flujo Y1 [u.a]", "Flujo Y2 [u.a]", "Flujo Z1 [u.a]", "Flujo Z2 [u.a]", "Intensidad incidente [u.a.]", "Flujo scattereado [u.a.]", "Sección eficaz de scattering [u.a.]" ] if vm.parallel_assign(0, np_process, parallel): vs.savetxt(file("Results.txt"), data, header=header, footer=params) vs.savetxt(file("BaseResults.txt"), data_base, header=header_base, footer=params) if near2far: header_near2far = [ "Poynting medio Px [u.a.]", "Poynting medio Py [u.a.]", "Poynting medio Pz [u.a.]", "Poynting medio Pr [u.a.]" ] data_near2far = [ poynting_x.reshape(poynting_x.size), poynting_y.reshape(poynting_y.size), poynting_z.reshape(poynting_z.size), poynting_r.reshape(poynting_r.size) ] if vm.parallel_assign(1, np_process, parallel): vs.savetxt(file("Near2FarResults.txt"), data_near2far, header=header_near2far, footer=params) if not split_chunks_evenly: vm.save_chunks(sim, params, path) if parallel: f = h5.File(file("RAM.h5"), "w", driver='mpio', comm=MPI.COMM_WORLD) current_process = mp.my_rank() f.create_dataset("RAM", (len(used_ram), np_process), dtype="float") f["RAM"][:, current_process] = used_ram for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", (len(used_ram), np_process), dtype="int") f["SWAP"][:, current_process] = swapped_ram for a in params: f["SWAP"].attrs[a] = params[a] else: f = h5.File(file("RAM.h5"), "w") f.create_dataset("RAM", data=used_ram) for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", data=swapped_ram) for a in params: f["SWAP"].attrs[a] = params[a] f.close() del f if flux_needed and vm.parallel_assign(0, np_process, parallel): os.remove(file("MidRAM.h5")) #%% PLOT ALL TOGETHER if vm.parallel_assign(0, np_process, parallel) and surface_index == 1: plt.figure() plt.plot(1e3 * from_um_factor / freqs, scatt_eff_meep, 'bo-', label='Meep') plt.plot(1e3 * from_um_factor / freqs, scatt_eff_theory, 'ro-', label='Theory') plt.xlabel('Wavelength [nm]') plt.ylabel('Scattering efficiency [σ/πr$^{2}$]') plt.legend() plt.title('Scattering of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) plt.tight_layout() plt.savefig(file("Comparison.png")) #%% PLOT SEPARATE if vm.parallel_assign(1, np_process, parallel): plt.figure() plt.plot(1e3 * from_um_factor / freqs, scatt_eff_meep, 'bo-', label='Meep') plt.xlabel('Wavelength [nm]') plt.ylabel('Scattering efficiency [σ/πr$^{2}$]') plt.legend() plt.title('Scattering of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) plt.tight_layout() plt.savefig(file("Meep.png")) if vm.parallel_assign(0, np_process, parallel) and surface_index == 1: plt.figure() plt.plot(1e3 * from_um_factor / freqs, scatt_eff_theory, 'ro-', label='Theory') plt.xlabel('Wavelength [nm]') plt.ylabel('Scattering efficiency [σ/πr$^{2}$]') plt.legend() plt.title('Scattering of Au Sphere With {:.1f} nm Radius'.format(r * 10)) plt.tight_layout() plt.savefig(file("Theory.png")) #%% PLOT ONE ABOVE THE OTHER if vm.parallel_assign(1, np_process, parallel) and surface_index == 1: fig, axes = plt.subplots(nrows=2, sharex=True) fig.subplots_adjust(hspace=0) plt.suptitle('Scattering of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) axes[0].plot(1e3 * from_um_factor / freqs, scatt_eff_meep, 'bo-', label='Meep') axes[0].yaxis.tick_right() axes[0].set_ylabel('Scattering efficiency [σ/πr$^{2}$]') axes[0].legend() axes[1].plot(1e3 * from_um_factor / freqs, scatt_eff_theory, 'ro-', label='Theory') axes[1].set_xlabel('Wavelength [nm]') axes[1].set_ylabel('Scattering efficiency [σ/πr$^{2}$]') axes[1].legend() plt.savefig(file("SeparatedComparison.png")) #%% PLOT FLUX FOURIER FINAL DATA if vm.parallel_assign(0, np_process, parallel): ylims = (np.min(data_base[:, 2:8]), np.max(data_base[:, 2:8])) ylims = (ylims[0] - .1 * (ylims[1] - ylims[0]), ylims[1] + .1 * (ylims[1] - ylims[0])) fig, ax = plt.subplots(3, 2, sharex=True) fig.subplots_adjust(hspace=0, wspace=.05) plt.suptitle('Final flux of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) for a in ax[:, 1]: a.yaxis.tick_right() a.yaxis.set_label_position("right") for a, h in zip(np.reshape(ax, 6), header_base[1:7]): a.set_ylabel(h) for d, a in zip(data_base[:, 3:9].T, np.reshape(ax, 6)): a.plot(1e3 * from_um_factor / freqs, d) a.set_ylim(*ylims) ax[-1, 0].set_xlabel("Wavelength [nm]") ax[-1, 1].set_xlabel("Wavelength [nm]") plt.savefig(file("FinalFlux.png")) #%% PLOT ANGULAR PATTERN IN 3D if near2far and vm.parallel_assign(1, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) fig = plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax = fig.add_subplot(1, 1, 1, projection='3d') ax.plot_surface(poynting_x[:, :, freq_index], poynting_y[:, :, freq_index], poynting_z[:, :, freq_index], cmap=plt.get_cmap('jet'), linewidth=1, antialiased=False, alpha=0.5) ax.set_xlabel(r"$P_x$") ax.set_ylabel(r"$P_y$") ax.set_zlabel(r"$P_z$") plt.savefig(file("AngularPattern.png")) #%% PLOT ANGULAR PATTERN PROFILE FOR DIFFERENT POLAR ANGLES if near2far and vm.parallel_assign(0, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(polar_angle).index(alpha) for alpha in [0, .25, .5, .75, 1] ] plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax_plain = plt.axes() for i in index: ax_plain.plot(poynting_x[:, i, freq_index], poynting_y[:, i, freq_index], ".-", label=rf"$\theta$ = {polar_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_x$") ax_plain.set_ylabel(r"$P_y$") ax_plain.set_aspect("equal") plt.savefig(file("AngularPolar.png")) #%% PLOT ANGULAR PATTERN PROFILE FOR DIFFERENT AZIMUTHAL ANGLES if near2far and vm.parallel_assign(1, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(azimuthal_angle).index(alpha) for alpha in [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2] ] plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax_plain = plt.axes() for i in index: ax_plain.plot(poynting_x[i, :, freq_index], poynting_z[i, :, freq_index], ".-", label=rf"$\phi$ = {azimuthal_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_x$") ax_plain.set_ylabel(r"$P_z$") plt.savefig(file("AngularAzimuthal.png")) if near2far and vm.parallel_assign(0, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(azimuthal_angle).index(alpha) for alpha in [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2] ] plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax_plain = plt.axes() for i in index: ax_plain.plot(np.sqrt( np.square(poynting_x[i, :, freq_index]) + np.square(poynting_y[i, :, freq_index])), poynting_z[i, :, freq_index], ".-", label=rf"$\phi$ = {azimuthal_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_\rho$") ax_plain.set_ylabel(r"$P_z$") plt.savefig(file("AngularAzimuthalAbs.png"))
def test_farfield(self): resolution = 50 sxy = 4 dpml = 1 cell = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml) pml_layers = mp.PML(dpml) fcen = 1.0 df = 0.4 sources = mp.Source(src=mp.GaussianSource(fcen, fwidth=df), center=mp.Vector3(), component=mp.Ez) symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] sim = mp.Simulation(cell_size=cell, resolution=resolution, sources=[sources], symmetries=symmetries, boundary_layers=[pml_layers]) nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(y=0.5 * sxy), size=mp.Vector3(sxy)), mp.Near2FarRegion(mp.Vector3(y=-0.5 * sxy), size=mp.Vector3(sxy), weight=-1), mp.Near2FarRegion(mp.Vector3(0.5 * sxy), size=mp.Vector3(y=sxy)), mp.Near2FarRegion(mp.Vector3(-0.5 * sxy), size=mp.Vector3(y=sxy), weight=-1)) flux_box = 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)) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(), 1e-8)) near_flux = mp.get_fluxes(flux_box)[0] r = 1000 / fcen # radius of far field circle npts = 100 # number of points in [0,2*pi) range of angles E = np.zeros((npts, 3), dtype=np.complex128) H = np.zeros((npts, 3), dtype=np.complex128) for n in range(npts): ff = sim.get_farfield( nearfield_box, mp.Vector3(r * math.cos(2 * math.pi * n / npts), r * math.sin(2 * math.pi * n / npts))) E[n, :] = [np.conj(ff[j]) for j in range(3)] H[n, :] = [ff[j + 3] for j in range(3)] Px = np.real(E[:, 1] * H[:, 2] - E[:, 2] * H[:, 1]) Py = np.real(E[:, 2] * H[:, 0] - E[:, 0] * H[:, 2]) Pr = np.sqrt(np.square(Px) + np.square(Py)) far_flux_circle = np.sum(Pr) * 2 * np.pi * r / len(Pr) rr = 20 / fcen # length of far field square box far_flux_square = ( nearfield_box.flux( mp.Y, mp.Volume(center=mp.Vector3(y=0.5 * rr), size=mp.Vector3(rr)), resolution)[0] - nearfield_box.flux( mp.Y, mp.Volume(center=mp.Vector3(y=-0.5 * rr), size=mp.Vector3(rr)), resolution)[0] + nearfield_box.flux( mp.X, mp.Volume(center=mp.Vector3(0.5 * rr), size=mp.Vector3(y=rr)), resolution)[0] - nearfield_box.flux( mp.X, mp.Volume(center=mp.Vector3(-0.5 * rr), size=mp.Vector3(y=rr)), resolution)[0]) print("flux:, {:.6f}, {:.6f}, {:.6f}".format(near_flux, far_flux_circle, far_flux_square)) self.assertAlmostEqual(near_flux, far_flux_circle, places=2) self.assertAlmostEqual(far_flux_circle, far_flux_square, places=2) self.assertAlmostEqual(far_flux_square, near_flux, places=2)
sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3()) box_flux = sim.add_flux( frq_cen, 0, 1, mp.FluxRegion(center=mp.Vector3(x=-2 * r), size=mp.Vector3(0, 4 * r, 4 * r))) nearfield_box = sim.add_near2far( frq_cen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(x=-2 * r), size=mp.Vector3(0, 4 * r, 4 * r), weight=+1), mp.Near2FarRegion(center=mp.Vector3(x=+2 * r), size=mp.Vector3(0, 4 * r, 4 * r), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=-2 * r), size=mp.Vector3(4 * r, 0, 4 * r), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=+2 * r), size=mp.Vector3(4 * r, 0, 4 * r), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=-2 * r), size=mp.Vector3(4 * r, 4 * r, 0), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=+2 * r), size=mp.Vector3(4 * r, 4 * r, 0),
def diff(ht,wvl_cen): import meep as mp import numpy as np import math import random #450 1.674 #550 1.642 #650 1.629 if (wvl_cen == 0.45): photoresist = mp.Medium(index=1.674) #波长为450nm时光刻胶的折射率 elif (wvl_cen == 0.55): photoresist = mp.Medium(index=1.642) #波长为550nm时光刻胶的折射率 else: photoresist = mp.Medium(index=1.629) #波长为650nm时光刻胶的折射率 ## at least 8 pixels per smallest wavelength, i.e. np.floor(8/wvl_min) resolution = 25.0 dpml=1.0 #PML厚度 dpad =2.0 #透镜与PML间距 dsub = 2.0 # substrate thickness width = 0.5 #环的宽度 r= width*len(ht) #透镜的半径 50um focal_length = 100.0 #焦距 spot_length=100 #far-field line length ff_res = 10.0 #far-field resoluton h = 1.25 NA = math.sin(math.atan(r/focal_length)) NA = round(NA, 3) pml_layers = [mp.PML(thickness=dpml)] sr=r+dpad+dpml sz=dpml+dpad+h+dpad+dpml #sz=dpml+dsub+h+dpad+dpml cell_size =mp.Vector3(sr,0,sz) #glass = mp.Medium(index=1.5) # substrate #geometry = [mp.Block(material=glass, # size=mp.Vector3(sr,0,dpml+dsub), # center=mp.Vector3(0.5*sr,0,-0.5*sz+0.5*(dpml+dsub)))] # #for n in range (len(ht)): # geometry.append(mp.Block(material=photoresist, # size=mp.Vector3(width,0,ht[n]), # center=mp.Vector3(width*n+0.5*width,0,-0.5*sz+dpml+dsub+0.5*ht[n]))) #透镜的结构 #geometry = [mp.Block(material=photoresist, # size=mp.Vector3(width,0,ht[0]), # center=mp.Vector3(0.5*width,0,-0.5*sz+dpml+dpad+0.5*ht[0]))] #for n in range (1,len(ht)): # geometry.append(mp.Block(material=photoresist, # size=mp.Vector3(width,0,ht[n]), # center=mp.Vector3(width*n+0.5*width,0,-0.5*sz+dpml+dpad+0.5*ht[n]))) #透镜的结构 geometry = [mp.Block(material=photoresist, size=mp.Vector3(sr,0,dpml+dsub), center=mp.Vector3(0.5*sr,0,-0.5*sz+0.5*(dpml+dsub)))] for n in range (0,len(ht)): geometry.append(mp.Block(material=photoresist, size=mp.Vector3(width,0,ht[n]), center=mp.Vector3((width*(n)+0.5*width),0,-0.5*sz+dpml+dsub+0.5*ht[n]))) #透镜的结构 #wvl_cen = 0.5 frq_cen = 1/wvl_cen dfrq = 0.2*frq_cen sources = [mp.Source(mp.GaussianSource(frq_cen,fwidth=dfrq,is_integrated=True), component=mp.Er, center=mp.Vector3(0.5*(sr),0,-0.5*sz+dpml), size=mp.Vector3(sr)), mp.Source(mp.GaussianSource(frq_cen,fwidth=dfrq,is_integrated=True), component=mp.Ep, center=mp.Vector3(0.5*(sr),0,-0.5*sz+dpml), size=mp.Vector3(sr), amplitude=-1j)] sim = mp.Simulation(cell_size=cell_size, boundary_layers=pml_layers, resolution=resolution, sources=sources, geometry=geometry, dimensions=mp.CYLINDRICAL, m=-1) ## near-field monitor n2f_obj = sim.add_near2far(frq_cen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0.5*(sr-dpml),0,0.5*sz-dpml),size=mp.Vector3(sr-dpml))) # mp.Near2FarRegion(center=mp.Vector3(sr-dpml,0,0.5*sz-0.5*(dsub+zh+dpad)),size=mp.Vector3(z=dsub+zh+dpad))) sim.run(until_after_sources=100) ff_r = sim.get_farfields(n2f_obj, ff_res, center=mp.Vector3(0.5*(sr-dpml),0,-0.5*sz+dpml+dsub+h+focal_length),size=mp.Vector3(sr-dpml)) E2_r = np.absolute(ff_r['Ex'])**2+np.absolute(ff_r['Ey'])**2+np.absolute(ff_r['Ez'])**2 E_sum = sum(E2_r) n = round((1.5*(wvl_cen/(2*NA)))/(r/(len(E2_r)-1))) #print ('n = %d' % n) E = 0.0 for i in range (n): E += E2_r[i] return E/E_sum
if src_cmpt == mp.Ex: symmetries = [mp.Mirror(mp.Y)] elif src_cmpt == mp.Ey: symmetries = [mp.Mirror(mp.X)] elif src_cmpt == mp.Ez: symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] sim = mp.Simulation(cell_size=cell, resolution=resolution, sources=[sources], symmetries=symmetries, boundary_layers=[pml_layers]) nearfield = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(0, 0.5 * sxy), size=mp.Vector3(sxy)), mp.Near2FarRegion(mp.Vector3(0, -0.5 * sxy), size=mp.Vector3(sxy), weight=-1.0), mp.Near2FarRegion(mp.Vector3(0.5 * sxy), size=mp.Vector3(0, sxy)), mp.Near2FarRegion(mp.Vector3(-0.5 * sxy), size=mp.Vector3(0, sxy), weight=-1.0)) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, src_cmpt, mp.Vector3(), 1e-8)) r = 1000 * (1 / fcen) # 1000 wavelengths out from the source npts = 100 # number of points in [0,2*pi) range of angles for n in range(npts):
center=mp.Vector3()) symmetries = [mp.Mirror(mp.X, phase=-1), mp.Mirror(mp.Y, phase=-1)] sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[sources], symmetries=symmetries, boundary_layers=[pml_layers], resolution=resolution) d1 = 0.2 nearfield = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(y=0.5 * w + d1), size=mp.Vector3(sx - 2 * dpml)), mp.Near2FarRegion(mp.Vector3(-0.5 * sx + dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(y=d1), weight=-1.0), mp.Near2FarRegion(mp.Vector3(0.5 * sx - dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(y=d1))) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Hz, mp.Vector3(0.12, -0.37), 1e-8)) print(sim.run_index) # In[2]: d2 = 20 h = 4
def main(from_um_factor, resolution_wlen, courant, submerged_index, displacement, surface_index, wlen_center, wlen_width, nfreq, air_wlen_factor, pml_wlen_factor, flux_wlen_factor, time_factor_cell, second_time_factor, series, folder, parallel, n_processes, split_chunks_evenly): #%% CLASSIC INPUT PARAMETERS """ # Simulation size from_um_factor = 100e-3 # Conversion of 1 μm to my length unit (=100nm/1μm) resolution_wlen = 20 # >=8 pixels per smallest wavelength, i.e. np.floor(8/wvl_min) courant = 0.5 # Nanoparticle specifications: Sphere in Vacuum :) paper = "R" reference = "Meep" displacement = 0 # Displacement of the surface from the bottom of the sphere in nm submerged_index = 1.33 # 1.33 for water surface_index = 1.54 # 1.54 for glass # Frequency and wavelength wlen_center = 650 # Main wavelength range in nm wlen_width = 200 # Wavelength band in nm nfreq = 100 # Box dimensions pml_wlen_factor = 0.38 air_wlen_factor = 0.15 flux_wlen_factor = 0.1 # Simulation time time_factor_cell = 1.2 second_time_factor = 10 # Saving directories series = "1stTest" folder = "Test/TestDipole/DipoleGlass" # Configuration parallel = False n_processes = 1 split_chunks_evenly = True """ #%% MORE INPUT PARAMETERS # Simulation size resolution = (from_um_factor * 1e3) * resolution_wlen / (wlen_center + wlen_width / 2) resolution = int(np.ceil(resolution / 2)) * 2 # Frequency and wavelength cutoff = 3.2 # Gaussian planewave source's parameter of shape nazimuthal = 16 npolar = 20 ### TREATED INPUT PARAMETERS # Nanoparticle specifications: Sphere in Vacuum :) displacement = displacement / (from_um_factor * 1e3) # Now in Meep units # Frequency and wavelength wlen_center = wlen_center / (from_um_factor * 1e3) # Now in Meep units wlen_width = wlen_width / (from_um_factor * 1e3) # Now in Meep units freq_center = 1 / wlen_center # Hz center frequency in Meep units freq_width = 1 / wlen_width # Hz range in Meep units from highest to lowest # Space configuration pml_width = pml_wlen_factor * (wlen_center + wlen_width / 2 ) # 0.5 * max(wlen_range) air_width = air_wlen_factor * (wlen_center + wlen_width / 2) flux_box_size = flux_wlen_factor * (wlen_center + wlen_width / 2) # Computation enlapsed = [] if parallel: np_process = mp.count_processors() else: np_process = 1 # Saving directories if series is None: series = "Test" if folder is None: folder = "Test" params_list = [ "from_um_factor", "resolution_wlen", "resolution", "courant", "submerged_index", "displacement", "surface_index", "wlen_center", "wlen_width", "cutoff", "nfreq", "nazimuthal", "npolar", "flux_box_size", "cell_width", "pml_width", "air_width", "until_after_sources", "time_factor_cell", "second_time_factor", "enlapsed", "parallel", "n_processes", "split_chunks_evenly", "script", "sysname", "path" ] #%% GENERAL GEOMETRY SETUP air_width = air_width - air_width % (1 / resolution) pml_width = pml_width - pml_width % (1 / resolution) pml_layers = [mp.PML(thickness=pml_width)] # symmetries = [mp.Mirror(mp.Y), # mp.Mirror(mp.Z, phase=-1)] # Two mirror planes reduce cell size to 1/4 # Issue related that lead me to comment this lines: # https://github.com/NanoComp/meep/issues/1484 cell_width = 2 * (pml_width + air_width) cell_width = cell_width - cell_width % (1 / resolution) cell_size = mp.Vector3(cell_width, cell_width, cell_width) # surface_center = r/4 - displacement/2 + cell_width/4 # surface_center = surface_center - surface_center%(1/resolution) # displacement = r/2 + cell_width/2 - 2*surface_center displacement = displacement - displacement % (1 / resolution) flux_box_size = flux_box_size - flux_box_size % (1 / resolution) sources = [ mp.Source(mp.GaussianSource(freq_center, fwidth=freq_width, is_integrated=True, cutoff=cutoff), center=mp.Vector3(), component=mp.Ez) ] # Ez-polarized point pulse # (its size parameter is null and it is centered at zero) # >> The planewave source extends into the PML # ==> is_integrated=True must be specified until_after_sources = time_factor_cell * cell_width * submerged_index # Enough time for the pulse to pass through all the cell # Originally: Aprox 3 periods of lowest frequency, using T=λ/c=λ in Meep units # Now: Aprox 3 periods of highest frequency, using T=λ/c=λ in Meep units # if surface_index != 1: # geometry = [mp.Block(material=mp.Medium(index=surface_index), # center=mp.Vector3( # - displacement/2 + cell_width/4, # 0, 0), # size=mp.Vector3( # cell_width/2 + displacement, # cell_width, cell_width))] # else: # geometry = [] # A certain material surface underneath it home = vs.get_home() sysname = vs.get_sys_name() path = os.path.join(home, folder, series) if not os.path.isdir(path) and vm.parallel_assign(0, n_processes, parallel): os.makedirs(path) file = lambda f: os.path.join(path, f) #%% BASE SIMULATION: SETUP measure_ram() params = {} for p in params_list: params[p] = eval(p) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), output_single_precision=True, split_chunks_evenly=split_chunks_evenly) measure_ram() near2far_box = sim.add_near2far( freq_center, freq_width, nfreq, mp.Near2FarRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=+1)) measure_ram() # used_ram.append(used_ram[-1]) #%% BASE SIMULATION: INITIALIZE temp = time() sim.init_sim() enlapsed.append(time() - temp) measure_ram() #%% BASE SIMULATION: SIMULATION :D step_ram_function = lambda sim: measure_ram() temp = time() sim.run(mp.at_beginning(step_ram_function), mp.at_time(int(second_time_factor * until_after_sources / 2), step_ram_function), mp.at_end(step_ram_function), until_after_sources=second_time_factor * until_after_sources) # mp.stop_when_fields_decayed( # np.mean(wlen_range), # dT = mean period of source # mp.Ez, # Component of field to check # mp.Vector3(0.5*cell_width - pml_width, 0, 0), # Where to check # 1e-3)) # Factor to decay enlapsed.append(time() - temp) del temp # Aprox 30 periods of lowest frequency, using T=λ/c=λ in Meep units #%% BASE SIMULATION: ANGULAR PATTERN ANALYSIS freqs = np.linspace(freq_center - freq_center / 2, freq_center + freq_width / 2, nfreq) wlens = 1 / freqs # fraunhofer_distance = 8 * (r**2) / min(wlen_range) # radial_distance = max( 10 * fraunhofer_distance , 1.5 * cell_width/2 ) # radius of far-field circle must be at least Fraunhofer distance radial_distance = cell_width azimuthal_angle = np.arange(0, 2 + 2 / nazimuthal, 2 / nazimuthal) # in multiples of pi polar_angle = np.arange(0, 1 + 1 / npolar, 1 / npolar) poynting_x = [] poynting_y = [] poynting_z = [] poynting_r = [] for phi in azimuthal_angle: poynting_x.append([]) poynting_y.append([]) poynting_z.append([]) poynting_r.append([]) for theta in polar_angle: farfield_dict = sim.get_farfields( near2far_box, 1, where=mp.Volume(center=mp.Vector3( radial_distance * np.cos(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.sin(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.cos(np.pi * theta)))) Px = farfield_dict["Ey"] * np.conjugate(farfield_dict["Hz"]) Px -= farfield_dict["Ez"] * np.conjugate(farfield_dict["Hy"]) Py = farfield_dict["Ez"] * np.conjugate(farfield_dict["Hx"]) Py -= farfield_dict["Ex"] * np.conjugate(farfield_dict["Hz"]) Pz = farfield_dict["Ex"] * np.conjugate(farfield_dict["Hy"]) Pz -= farfield_dict["Ey"] * np.conjugate(farfield_dict["Hx"]) Px = np.real(Px) Py = np.real(Py) Pz = np.real(Pz) poynting_x[-1].append(Px) poynting_y[-1].append(Py) poynting_z[-1].append(Pz) poynting_r[-1].append( np.sqrt(np.square(Px) + np.square(Py) + np.square(Pz))) del phi, theta, farfield_dict, Px, Py, Pz poynting_x = np.array(poynting_x) poynting_y = np.array(poynting_y) poynting_z = np.array(poynting_z) poynting_r = np.array(poynting_r) #%% BASE SIMULATION: SAVE DATA for p in params_list: params[p] = eval(p) os.chdir(path) sim.save_near2far("BaseNear2Far", near2far_box) if vm.parallel_assign(0, np_process, parallel): f = h5.File("BaseNear2Far.h5", "r+") for key, par in params.items(): f[list(f.keys())[0]].attrs[key] = par f.close() del f os.chdir(syshome) if vm.parallel_assign(1, np_process, parallel): f = h5.File(file("BaseResults.h5"), "w") f["Px"] = poynting_x f["Py"] = poynting_y f["Pz"] = poynting_z f["Pr"] = poynting_r for dset in f.values(): for key, par in params.items(): dset.attrs[key] = par f.close() del f # if not split_chunks_evenly: # vm.save_chunks(sim, params, path) if parallel: f = h5.File(file("BaseRAM.h5"), "w", driver='mpio', comm=MPI.COMM_WORLD) current_process = mp.my_rank() f.create_dataset("RAM", (len(used_ram), np_process), dtype="float") f["RAM"][:, current_process] = used_ram for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", (len(used_ram), np_process), dtype="int") f["SWAP"][:, current_process] = swapped_ram for a in params: f["SWAP"].attrs[a] = params[a] else: f = h5.File(file("BaseRAM.h5"), "w") f.create_dataset("RAM", data=used_ram) for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", data=swapped_ram) for a in params: f["SWAP"].attrs[a] = params[a] f.close() del f sim.reset_meep() #%% FURTHER SIMULATION if surface_index != 1: #% FURTHER SIMULATION: SETUP measure_ram() params = {} for p in params_list: params[p] = eval(p) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=surface_index), output_single_precision=True, split_chunks_evenly=split_chunks_evenly) measure_ram() near2far_box = sim.add_near2far( freq_center, freq_width, nfreq, mp.Near2FarRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=+1)) measure_ram() # used_ram.append(used_ram[-1]) #% FURTHER SIMULATION: INITIALIZE temp = time() sim.init_sim() enlapsed.append(time() - temp) measure_ram() #% FURTHER SIMULATION: SIMULATION :D step_ram_function = lambda sim: measure_ram() temp = time() sim.run(mp.at_beginning(step_ram_function), mp.at_time(int(second_time_factor * until_after_sources / 2), step_ram_function), mp.at_end(step_ram_function), until_after_sources=second_time_factor * until_after_sources) # mp.stop_when_fields_decayed( # np.mean(wlen_range), # dT = mean period of source # mp.Ez, # Component of field to check # mp.Vector3(0.5*cell_width - pml_width, 0, 0), # Where to check # 1e-3)) # Factor to decay enlapsed.append(time() - temp) del temp # Aprox 30 periods of lowest frequency, using T=λ/c=λ in Meep units #% FURTHER SIMULATION: ANGULAR PATTERN ANALYSIS poynting_x2 = [] poynting_y2 = [] poynting_z2 = [] poynting_r2 = [] for phi in azimuthal_angle: poynting_x2.append([]) poynting_y2.append([]) poynting_z2.append([]) poynting_r2.append([]) for theta in polar_angle: farfield_dict = sim.get_farfields( near2far_box, 1, where=mp.Volume(center=mp.Vector3( radial_distance * np.cos(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.sin(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.cos(np.pi * theta)))) Px = farfield_dict["Ey"] * np.conjugate(farfield_dict["Hz"]) Px -= farfield_dict["Ez"] * np.conjugate(farfield_dict["Hy"]) Py = farfield_dict["Ez"] * np.conjugate(farfield_dict["Hx"]) Py -= farfield_dict["Ex"] * np.conjugate(farfield_dict["Hz"]) Pz = farfield_dict["Ex"] * np.conjugate(farfield_dict["Hy"]) Pz -= farfield_dict["Ey"] * np.conjugate(farfield_dict["Hx"]) Px = np.real(Px) Py = np.real(Py) Pz = np.real(Pz) poynting_x2[-1].append(Px) poynting_y2[-1].append(Py) poynting_z2[-1].append(Pz) poynting_r2[-1].append( np.sqrt(np.square(Px) + np.square(Py) + np.square(Pz))) del phi, theta, farfield_dict, Px, Py, Pz poynting_x2 = np.array(poynting_x2) poynting_y2 = np.array(poynting_y2) poynting_z2 = np.array(poynting_z2) poynting_r2 = np.array(poynting_r2) #% FURTHER SIMULATION: SAVE DATA for p in params_list: params[p] = eval(p) os.chdir(path) sim.save_near2far("FurtherNear2Far", near2far_box) if vm.parallel_assign(0, np_process, parallel): f = h5.File("FurtherNear2Far.h5", "r+") for key, par in params.items(): f[list(f.keys())[0]].attrs[key] = par f.close() del f os.chdir(syshome) if vm.parallel_assign(1, np_process, parallel): f = h5.File(file("FurtherResults.h5"), "w") f["Px"] = poynting_x2 f["Py"] = poynting_y2 f["Pz"] = poynting_z2 f["Pr"] = poynting_r2 for dset in f.values(): for key, par in params.items(): dset.attrs[key] = par f.close() del f # if not split_chunks_evenly: # vm.save_chunks(sim, params, path) if parallel: f = h5.File(file("FurtherRAM.h5"), "w", driver='mpio', comm=MPI.COMM_WORLD) current_process = mp.my_rank() f.create_dataset("RAM", (len(used_ram), np_process), dtype="float") f["RAM"][:, current_process] = used_ram for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", (len(used_ram), np_process), dtype="int") f["SWAP"][:, current_process] = swapped_ram for a in params: f["SWAP"].attrs[a] = params[a] else: f = h5.File(file("FurtherRAM.h5"), "w") f.create_dataset("RAM", data=used_ram) for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", data=swapped_ram) for a in params: f["SWAP"].attrs[a] = params[a] f.close() del f #%% NORMALIZE AND REARANGE DATA if surface_index != 1: max_poynting_r = np.max( [np.max(np.abs(poynting_r)), np.max(np.abs(poynting_r2))]) else: max_poynting_r = np.max(np.abs(poynting_r)) poynting_x = np.array(poynting_x) / max_poynting_r poynting_y = np.array(poynting_y) / max_poynting_r poynting_z = np.array(poynting_z) / max_poynting_r poynting_r = np.array(poynting_r) / max_poynting_r if surface_index != 1: poynting_x0 = np.array(poynting_x) poynting_y0 = np.array(poynting_y) poynting_z0 = np.array(poynting_z) poynting_r0 = np.array(poynting_r) poynting_x2 = np.array(poynting_x2) / max_poynting_r poynting_y2 = np.array(poynting_y2) / max_poynting_r poynting_z2 = np.array(poynting_z2) / max_poynting_r poynting_r2 = np.array(poynting_r2) / max_poynting_r #%% """ poynting_x = np.array(poynting_x0) poynting_y = np.array(poynting_y0) poynting_z = np.array(poynting_z0) poynting_r = np.array(poynting_r0) """ if surface_index != 1: polar_limit = np.arcsin(displacement / radial_distance) + .5 for i in range(len(polar_angle)): if polar_angle[i] > polar_limit: index_limit = i break poynting_x[:, index_limit:, :] = poynting_x2[:, index_limit:, :] poynting_y[:, index_limit:, :] = poynting_y2[:, index_limit:, :] poynting_z[:, index_limit:, :] = poynting_z2[:, index_limit:, :] poynting_r[:, index_limit:, :] = poynting_r2[:, index_limit:, :] poynting_x[:, index_limit - 1, :] = np.array([[ np.mean([p2, p0]) for p2, p0 in zip(poy2, poy0) ] for poy2, poy0 in zip(poynting_x2[:, index_limit - 1, :], poynting_x0[:, index_limit - 1, :])]) poynting_y[:, index_limit - 1, :] = np.array([[ np.mean([p2, p0]) for p2, p0 in zip(poy2, poy0) ] for poy2, poy0 in zip(poynting_y2[:, index_limit - 1, :], poynting_y0[:, index_limit - 1, :])]) poynting_z[:, index_limit - 1, :] = np.array([[ np.mean([p2, p0]) for p2, p0 in zip(poy2, poy0) ] for poy2, poy0 in zip(poynting_z2[:, index_limit - 1, :], poynting_z0[:, index_limit - 1, :])]) poynting_r[:, index_limit - 1, :] = np.sqrt( np.squared(poynting_x[:, index_limit - 1, :]) + np.squared(poynting_y[:, index_limit - 1, :]) + np.squared(poynting_y[:, index_limit - 1, :])) #%% SAVE FINAL DATA if surface_index != 1 and vm.parallel_assign(0, np_process, parallel): os.remove(file("BaseRAM.h5")) os.rename(file("FurtherRAM.h5"), file("RAM.h5")) elif vm.parallel_assign(0, np_process, parallel): os.rename(file("BaseRAM.h5"), file("RAM.h5")) #%% PLOT ANGULAR PATTERN IN 3D if vm.parallel_assign(1, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) fig = plt.figure() plt.suptitle( f'Angular Pattern of point dipole molecule at {from_um_factor * 1e3 * wlen_center:.0f} nm' ) ax = fig.add_subplot(1, 1, 1, projection='3d') ax.plot_surface(poynting_x[:, :, freq_index], poynting_y[:, :, freq_index], poynting_z[:, :, freq_index], cmap=plt.get_cmap('jet'), linewidth=1, antialiased=False, alpha=0.5) ax.set_xlabel(r"$P_x$") ax.set_ylabel(r"$P_y$") ax.set_zlabel(r"$P_z$") plt.savefig(file("AngularPattern.png")) #%% PLOT ANGULAR PATTERN PROFILE FOR DIFFERENT POLAR ANGLES if vm.parallel_assign(0, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(polar_angle).index(alpha) for alpha in [0, .25, .5, .75, 1] ] plt.figure() plt.suptitle( f'Angular Pattern of point dipole molecule at {from_um_factor * 1e3 * wlen_center:.0f} nm' ) ax_plain = plt.axes() for i in index: ax_plain.plot(poynting_x[:, i, freq_index], poynting_y[:, i, freq_index], ".-", label=rf"$\theta$ = {polar_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_x$") ax_plain.set_ylabel(r"$P_y$") ax_plain.set_aspect("equal") plt.savefig(file("AngularPolar.png")) #%% PLOT ANGULAR PATTERN PROFILE FOR DIFFERENT AZIMUTHAL ANGLES if vm.parallel_assign(1, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(azimuthal_angle).index(alpha) for alpha in [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2] ] plt.figure() plt.suptitle( f'Angular Pattern of point dipole molecule at {from_um_factor * 1e3 * wlen_center:.0f} nm' ) ax_plain = plt.axes() for i in index: ax_plain.plot(poynting_x[i, :, freq_index], poynting_z[i, :, freq_index], ".-", label=rf"$\phi$ = {azimuthal_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_x$") ax_plain.set_ylabel(r"$P_z$") plt.savefig(file("AngularAzimuthal.png")) if vm.parallel_assign(0, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(azimuthal_angle).index(alpha) for alpha in [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2] ] plt.figure() plt.suptitle( f'Angular Pattern of point dipole molecule at {from_um_factor * 1e3 * wlen_center:.0f} nm' ) ax_plain = plt.axes() for i in index: ax_plain.plot(np.sqrt( np.square(poynting_x[i, :, freq_index]) + np.square(poynting_y[i, :, freq_index])), poynting_z[i, :, freq_index], ".-", label=rf"$\phi$ = {azimuthal_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_\rho$") ax_plain.set_ylabel(r"$P_z$") plt.savefig(file("AngularAzimuthalAbs.png"))
else: os.system("convert " + outputdir + "/grating_validation-ez-*.png " + outputdir + "/N=" + str(num_notches) + "-NearfieldTransient.gif") os.system("rm " + outputdir + "/grating_validation-ez-*.png") # Delete after printing output refl1 = sim.add_flux(fcen, df, nfreq, refl_fr1) refl2 = sim.add_flux(fcen, df, nfreq, refl_fr2) tran = sim.add_flux(fcen, df, nfreq, tran_fr) su = sim.add_flux(fcen, df, nfreq, su_fr) sd = sim.add_flux(fcen, df, nfreq, sd_fr) nearfield = sim.add_near2far(fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(0, 0.5 * sxy), size=mp.Vector3(sxy))) # old_refl1_flux = mp.get_fluxes(refl1) old_refl2_flux = mp.get_fluxes(refl2) old_tran_flux = mp.get_fluxes(tran) old_su_flux = mp.get_fluxes(su) old_sd_flux = mp.get_fluxes(sd) # Simulation run for STEADY state sim.run(mp.at_every( 1, mp.output_png( mp.Ez, "-RZc bluered -A " + outputdir + "/grating_validation-" + epsform + ".h5 -a gray:.2")), until=19 * wavelength / 20) sim.run(until=19 * wavelength)
def pec_ground_plane_radiation(src_cmpt=mp.Hz): L = 8.0 # length of non-PML region dpml = 1.0 # thickness of PML sxy = dpml + L + dpml cell_size = mp.Vector3(sxy, sxy, 0) boundary_layers = [mp.PML(dpml)] fcen = 1 / wvl # The near-to-far field transformation feature only supports # homogeneous media which means it cannot explicitly take into # account the ground plane. As a workaround, we use two antennas # of opposite sign surrounded by a single near2far box which # encloses both antennas. We then use an odd mirror symmetry to # divide the computational cell in half which is effectively # equivalent to a PEC boundary condition on one side. # Note: This setup means that the radiation pattern can only # be measured in the top half above the dipole. sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), component=src_cmpt, center=mp.Vector3(0, +h)), mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), component=src_cmpt, center=mp.Vector3(0, -h), amplitude=-1 if src_cmpt == mp.Ez else +1) ] symmetries = [ mp.Mirror(direction=mp.X, phase=+1 if src_cmpt == mp.Ez else -1), mp.Mirror(direction=mp.Y, phase=-1 if src_cmpt == mp.Ez else +1) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, sources=sources, symmetries=symmetries, default_material=mp.Medium(index=n)) nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0, 2 * h), size=mp.Vector3(2 * h, 0), weight=+1), mp.Near2FarRegion(center=mp.Vector3(0, -2 * h), size=mp.Vector3(2 * h, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(h, 0), size=mp.Vector3(0, 4 * h), weight=+1), mp.Near2FarRegion(center=mp.Vector3(-h, 0), size=mp.Vector3(0, 4 * h), weight=-1)) sim.plot2D() plt.savefig('antenna_pec_ground_plane.png', bbox_inches='tight') sim.run(until_after_sources=mp.stop_when_dft_decayed()) Pr = radial_flux(sim, nearfield_box, r) return Pr
def grating(gp, gh, gdc_list): sx = dpml + dsub + gh + dpad + dpml src_pt = mp.Vector3(-0.5 * sx + dpml + 0.5 * dsub) mon_pt = mp.Vector3(0.5 * sx - dpml - 0.5 * dpad) geometry = [ mp.Block(material=glass, size=mp.Vector3(dpml + dsub, mp.inf, mp.inf), center=mp.Vector3(-0.5 * sx + 0.5 * (dpml + dsub))) ] num_cells = len(gdc_list) if num_cells == 1: sy = gp cell_size = mp.Vector3(sx, sy, 0) sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=src_pt, size=mp.Vector3(y=sy)) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, k_point=k_point, default_material=glass, sources=sources, symmetries=symmetries) flux_obj = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mon_pt, size=mp.Vector3(y=sy))) sim.run(until_after_sources=50) input_flux = mp.get_fluxes(flux_obj) sim.reset_meep() geometry.append( mp.Block(material=glass, size=mp.Vector3(gh, gdc_list[0] * gp, mp.inf), center=mp.Vector3(-0.5 * sx + dpml + dsub + 0.5 * gh))) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, geometry=geometry, k_point=k_point, sources=sources, symmetries=symmetries) flux_obj = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mon_pt, size=mp.Vector3(y=sy))) sim.run(until_after_sources=200) freqs = mp.get_eigenmode_freqs(flux_obj) res = sim.get_eigenmode_coefficients(flux_obj, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) coeffs = res.alpha mode_tran = abs(coeffs[0, 0, 0])**2 / input_flux[0] mode_phase = np.angle(coeffs[0, 0, 0]) if mode_phase > 0: mode_phase -= 2 * np.pi return mode_tran, mode_phase else: sy = num_cells * gp cell_size = mp.Vector3(sx, sy, 0) sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=src_pt, size=mp.Vector3(y=sy)) ] for j in range(num_cells): geometry.append( mp.Block(material=glass, size=mp.Vector3(gh, gdc_list[j] * gp, mp.inf), center=mp.Vector3(-0.5 * sx + dpml + dsub + 0.5 * gh, -0.5 * sy + (j + 0.5) * gp))) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, geometry=geometry, k_point=k_point, sources=sources, symmetries=symmetries) n2f_obj = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(center=mon_pt, size=mp.Vector3(y=sy))) sim.run(until_after_sources=500) return abs( sim.get_farfields(n2f_obj, ff_res, center=mp.Vector3(-0.5 * sx + dpml + dsub + gh + focal_length), size=mp.Vector3(spot_length))['Ez'])**2
size=mp.Vector3(r[n], 0, zh), center=mp.Vector3(0.5 * r[n], 0, -0.5 * sz + dpml + dsub + 0.5 * zh))) sim = mp.Simulation(cell_size=cell_size, boundary_layers=pml_layers, resolution=resolution, sources=sources, geometry=geometry, dimensions=mp.CYLINDRICAL, m=-1) ## near-field monitor n2f_obj = sim.add_near2far( frq_cen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0.5 * (sr - dpml), 0, 0.5 * sz - dpml), size=mp.Vector3(sr - dpml)), mp.Near2FarRegion(center=mp.Vector3(sr - dpml, 0, 0.5 * sz - 0.5 * (dsub + zh + dpad)), size=mp.Vector3(z=dsub + zh + dpad))) f = plt.figure(dpi=150) sim.plot2D() # plt.show() plt.savefig('2Dstructure.png') sim.run(until_after_sources=100) ff_r = sim.get_farfields(n2f_obj, ff_res, center=mp.Vector3( 0.5 * (sr - dpml), 0,
def run_test(self, nfreqs): eps = 13 w = 1.2 r = 0.36 d = 1.4 N = 3 sy = 6 pad = 2 dpml = 1 sx = 2 * (pad + dpml + N) + d - 1 cell = mp.Vector3(sx, sy, 0) geometry = [mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, w, mp.inf), material=mp.Medium(epsilon=eps))] for i in range(N): geometry.append(mp.Cylinder(r, center=mp.Vector3(d / 2 + i))) geometry.append(mp.Cylinder(r, center=mp.Vector3(d / -2 - i))) pml_layers = mp.PML(dpml) resolution = 10 fcen = 0.25 df = 0.2 sources = mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Hz, center=mp.Vector3()) symmetries = [mp.Mirror(mp.Y, phase=-1), mp.Mirror(mp.X, phase=-1)] d1 = 0.2 sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[sources], symmetries=symmetries, boundary_layers=[pml_layers], resolution=resolution) nearfield = sim.add_near2far( fcen, 0.1, nfreqs, mp.Near2FarRegion(mp.Vector3(0, 0.5 * w + d1), size=mp.Vector3(2 * dpml - sx)), mp.Near2FarRegion(mp.Vector3(-0.5 * sx + dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(0, d1), weight=-1.0), mp.Near2FarRegion(mp.Vector3(0.5 * sx - dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(0, d1)) ) sim.run(until=200) d2 = 20 h = 4 vol = mp.Volume(mp.Vector3(0, (0.5 * w) + d2 + (0.5 * h)), size=mp.Vector3(sx - 2 * dpml, h)) result = sim.get_farfields(nearfield, resolution, where=vol) fname = 'cavity-farfield.h5' if nfreqs == 1 else 'cavity-farfield-4-freqs.h5' ref_file = os.path.join(self.data_dir, fname) with h5py.File(ref_file, 'r') as f: # Get reference data into memory ref_ex = mp.complexarray(f['ex.r'][()], f['ex.i'][()]) ref_ey = mp.complexarray(f['ey.r'][()], f['ey.i'][()]) ref_ez = mp.complexarray(f['ez.r'][()], f['ez.i'][()]) ref_hx = mp.complexarray(f['hx.r'][()], f['hx.i'][()]) ref_hy = mp.complexarray(f['hy.r'][()], f['hy.i'][()]) ref_hz = mp.complexarray(f['hz.r'][()], f['hz.i'][()]) np.testing.assert_allclose(ref_ex, result['Ex']) np.testing.assert_allclose(ref_ey, result['Ey']) np.testing.assert_allclose(ref_ez, result['Ez']) np.testing.assert_allclose(ref_hx, result['Hx']) np.testing.assert_allclose(ref_hy, result['Hy']) np.testing.assert_allclose(ref_hz, result['Hz'])
def run(): #G1 params in 10^-10m gp = 1000. # grating period gh = 1027.1 # grating height lw = 540.4 #line width tr = 162.7 #top radius br = 157.9 #bottom radius sa = 90.91 #sidewall angle (deg) #G2 params in 10^-10m gp = 1500. # grating period gh = 1195.0 # grating height lw = 673.0 #line width tr = 91.6 #top radius br = 130.2 #bottom radius sa = 84.73 #sidewall angle (deg) #params for spacing above and below grating dsub = 300 dpad = 300 dpml = 50 resolution = 4.5 / 20 S = 0.49 #courant parameter alpha = 0.86 * np.pi / 180 #from Fig. 4, in radians k_point = mp.Vector3(0, 0, 0) sx = 2 * gp #simulation width sy = dsub + gh + dpad #simulation height cell = mp.Vector3(sx, sy + 2 * dpml) #only absorb in Y direction so that we can have periodic boundaries in X pml_layers = [mp.PML(thickness=dpml, direction=mp.Y)] #params for light e = 0 Energies = [5.5, 5.55, 5.6] wavelengths = [2.25, 2.23, 2.21] #corresponding params for Si at these energies delta = [1.1676e-5, 1.6068e-5, 1.5779e-5] beta = [3.9765e-7, 7.3314e-7, 7.082e-7] E = Energies[e] wvl = wavelengths[e] f = 1 / wvl src = build_src(sx=sx, sy=sy, f=f * np.sin(alpha), alpha=alpha) #use frequency in 2d slice of infinite plane mat = build_mat(delta=delta[e], beta=beta[e], f=f) #use base frequency to get material params right geom = build_geom(sx=sx, sy=sy, dsub=dsub, gp=gp, gh=gh, lw=lw, tr=tr, br=br, sa=sa, material=mat) #first run empty sim for the background sim = mp.Simulation(cell_size=cell, resolution=resolution, Courant=S, k_point=k_point, boundary_layers=pml_layers, geometry=[], sources=src, force_complex_fields=True, eps_averaging=True) #nearfield needs to be specified in a region of homogenous space nearfield = sim.add_near2far( f, 0, 1, mp.Near2FarRegion(mp.Vector3(y=-sy / 2), size=mp.Vector3(sx), weight=-1, direction=mp.Y), mp.Near2FarRegion(mp.Vector3(-sx / 2, -dsub), size=mp.Vector3(y=sy - dsub), weight=-1, direction=mp.X), mp.Near2FarRegion(mp.Vector3(sx / 2, -dsub), size=mp.Vector3(y=sy - dsub), weight=1, direction=mp.X)) #run til things settle sim.run(until=5000) _eps, _I = localfield(sim=sim, sx=sx, sy=sy) _inten = _I / np.amax(_I) plt.imshow(_eps.transpose(), cmap='binary') plt.imshow(_inten.transpose(), cmap='gist_heat', alpha=0.9) plt.colorbar() plt.show() #calculate far field approximation through meep d = 1.7e10 #min detector distance in paper steps = 50 neighbors = 4 _q, _farI = farfield(sim=sim, nearfield=nearfield, sx=sx, alpha=alpha, wavelength=wvl, d=d, steps=steps, neighbors=neighbors) #plot far field (compare to Fig. 6) plt.plot(_q, _farI / np.amax(_farI)) plt.title('Far Field of E = ' + str(E) + 'keV') plt.xlabel('$\\Delta q_x (nm^{-1})$') plt.ylabel('Relative Intensity') plt.axis('on') plt.xlim(-0.4, 0.4) plt.show() #now run the same simulation with the gratings sim.reset_meep() sim = mp.Simulation(cell_size=cell, resolution=resolution, Courant=S, k_point=k_point, boundary_layers=pml_layers, geometry=geom, sources=src, force_complex_fields=True, eps_averaging=True) #nearfield needs to be specified in a region of homogenous space nearfield = sim.add_near2far( f, 0, 1, mp.Near2FarRegion(mp.Vector3(y=-sy / 2), size=mp.Vector3(sx), weight=-1, direction=mp.Y), mp.Near2FarRegion(mp.Vector3(-sx / 2, -dsub), size=mp.Vector3(y=sy - dsub), weight=-1, direction=mp.X), mp.Near2FarRegion(mp.Vector3(sx / 2, -dsub), size=mp.Vector3(y=sy - dsub), weight=1, direction=mp.X)) #run til things settle sim.run(until=5000) #calculate near field eps, I = localfield(sim=sim, sx=sx, sy=sy) #plot near field (compare to Fig. 4) inten = I / np.amax(I) plt.imshow(eps.transpose(), cmap='binary') plt.imshow(inten.transpose(), cmap='gist_heat', alpha=0.9) plt.colorbar() plt.show() #calculate far field approximation through meep d = 1.7e10 #min detector distance in paper steps = 50 neighbors = 4 q, farI = farfield(sim=sim, nearfield=nearfield, sx=sx, alpha=alpha, wavelength=wvl, d=d, steps=steps, neighbors=neighbors) #plot far field (compare to Fig. 6) plt.plot(q, farI / np.amax(farI)) plt.title('Far Field of E = ' + str(E) + 'keV') plt.xlabel('$\\Delta q_x (nm^{-1})$') plt.ylabel('Relative Intensity') plt.axis('on') plt.xlim(-0.4, 0.4) plt.show() plt.plot(q, (farI - _farI) / np.amax(farI - _farI)) plt.title('Far Field of E = ' + str(E) + 'keV') plt.xlabel('$\\Delta q_x (nm^{-1})$') plt.ylabel('Relative Intensity') plt.axis('on') plt.xlim(-0.4, 0.4) plt.show()