def test_resonant_modes(self): self.sim.sources = [mp.Source(mp.GaussianSource(self.fcen, fwidth=self.df), mp.Hz, mp.Vector3())] self.sim.symmetries = [mp.Mirror(mp.Y, phase=-1), mp.Mirror(mp.X, phase=-1)] h = mp.Harminv(mp.Hz, mp.Vector3(), self.fcen, self.df) self.sim.run(mp.at_beginning(mp.output_epsilon), mp.after_sources(h), until_after_sources=400) expected = [ 0.23445415346009466, -3.147812367338531e-4, 372.40808234438254, 5.8121430334347135, -3.763107485715599, -4.429450156854109, ] m = h.modes[0] res = [m.freq, m.decay, m.Q, abs(m.amp), m.amp.real, m.amp.imag] np.testing.assert_allclose(expected, res)
def test_resonant_modes(self): self.sim.sources = [ mp.Source(mp.GaussianSource(self.fcen, fwidth=self.df), mp.Hz, mp.Vector3()) ] self.sim.symmetries = [ mp.Mirror(mp.Y, phase=-1), mp.Mirror(mp.X, phase=-1) ] h = mp.Harminv(mp.Hz, mp.Vector3(), self.fcen, self.df) self.sim.run(mp.at_beginning(mp.output_epsilon), mp.after_sources(h), until_after_sources=400) expected = [ 0.23445415346009466, -3.147812367338531e-4, 372.40808234438254, 5.8121430334347135, -3.763107485715599, -4.429450156854109, ] m = h.modes[0] res = [m.freq, m.decay, m.Q, abs(m.amp), m.amp.real, m.amp.imag] np.testing.assert_allclose(expected, res)
def main(): c = mp.Cylinder(radius=3, material=mp.Medium(index=3.5)) e = mp.Ellipsoid(size=mp.Vector3(1, 2, 1e20)) src_cmpt = mp.Hz sources = mp.Source(src=mp.GaussianSource(1, fwidth=0.1), component=src_cmpt, center=mp.Vector3()) if src_cmpt == mp.Ez: symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] if src_cmpt == mp.Hz: symmetries = [mp.Mirror(mp.X, -1), mp.Mirror(mp.Y, -1)] sim = mp.Simulation(cell_size=mp.Vector3(10, 10), geometry=[c, e], boundary_layers=[mp.PML(1.0)], sources=[sources], symmetries=symmetries, resolution=100) def print_stuff(sim_obj): v = mp.Vector3(4.13, 3.75, 0) p = sim.get_field_point(src_cmpt, v) print("t, Ez: {} {}+{}i".format(sim.round_time(), p.real, p.imag)) sim.run(mp.at_beginning(mp.output_epsilon), mp.at_every(0.25, print_stuff), mp.at_end(print_stuff), mp.at_end(mp.output_efield_z), until=23) print("stopped at meep time = {}".format(sim.round_time()))
def main(): c = mp.Cylinder(radius=3, material=mp.Medium(index=3.5)) e = mp.Ellipsoid(size=mp.Vector3(1, 2, mp.inf)) src_cmpt = mp.Hz sources = mp.Source(src=mp.GaussianSource(1, fwidth=0.1), component=src_cmpt, center=mp.Vector3()) if src_cmpt == mp.Ez: symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] if src_cmpt == mp.Hz: symmetries = [mp.Mirror(mp.X, -1), mp.Mirror(mp.Y, -1)] sim = mp.Simulation(cell_size=mp.Vector3(10, 10), geometry=[c, e], boundary_layers=[mp.PML(1.0)], sources=[sources], symmetries=symmetries, resolution=100) def print_stuff(sim_obj): v = mp.Vector3(4.13, 3.75, 0) p = sim.get_field_point(src_cmpt, v) print("t, Ez: {} {}+{}i".format(sim.round_time(), p.real, p.imag)) sim.run(mp.at_beginning(mp.output_epsilon), mp.at_every(0.25, print_stuff), mp.at_end(print_stuff), mp.at_end(mp.output_efield_z), until=23) print("stopped at meep time = {}".format(sim.round_time()))
def main(args): n = 3.4 # index of waveguide w = 1 # width of waveguide r = 1 # inner radius of ring pad = 4 # padding between waveguide and edge of PML dpml = 32 # thickness of PML sr = r + w + pad + dpml # radial size (cell is from 0 to sr) dimensions = mp.CYLINDRICAL cell = mp.Vector3(sr, 0, 0) # in cylindrical coordinates, the phi (angular) dependence of the fields # is given by exp(i m phi), where m is given by: m = args.m geometry = [ mp.Block(center=mp.Vector3(r + (w / 2)), size=mp.Vector3(w, 1e20, 1e20), material=mp.Medium(index=n)) ] pml_layers = [mp.PML(dpml)] resolution = 20 # If we don't want to excite a specific mode symmetry, we can just # put a single point source at some arbitrary place, pointing in some # arbitrary direction. We will only look for TM modes (E out of the plane). fcen = args.fcen # pulse center frequency df = args.df # pulse frequency width sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(r + 0.1)) ] # note that the r -> -r mirror symmetry is exploited automatically sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, resolution=resolution, sources=sources, dimensions=dimensions, m=m) sim.run(mp.after_sources(mp.Harminv(mp.Ez, mp.Vector3(r + 0.1), fcen, df)), until_after_sources=200) # Output fields for one period at the end. (If we output # at a single time, we might accidentally catch the Ez field when it is # almost zero and get a distorted view.) We'll append the fields # to a file to get an r-by-t picture. We'll also output from -sr to -sr # instead of from 0 to sr. sim.run(mp.in_volume( mp.Volume(center=mp.Vector3(), size=mp.Vector3(2 * sr)), mp.at_beginning(mp.output_epsilon), mp.to_appended("ez", mp.at_every(1 / fcen / 20, mp.output_efield_z))), until=1 / fcen)
def empty_run(self, sx, sy, animate=False): sources, refl_fr, trans_fr = self.set_source(sx, sy) sim = mp.Simulation(cell_size=mp.Vector3(sx, sy, self.sz), geometry=[], sources=sources, boundary_layers=self.pml_layers, k_point=self.k, resolution=self.resolution) refl = sim.add_flux(self.fcen, self.df, self.nfreq, refl_fr) trans = sim.add_flux(self.fcen, self.df, self.nfreq, trans_fr) if animate: sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("ex", mp.at_every(0.6, mp.output_efield_z)), until_after_sources=mp.stop_when_fields_decayed(25, mp.Ey, self.pt, 1e-3)) else: sim.run(until_after_sources=mp.stop_when_fields_decayed(25, mp.Ey, self.pt, 1e-3)) # for normalization run, save flux fields data for reflection plane self.store['straight_refl_data'] = sim.get_flux_data(refl) # save incident power for transmission plane self.store['flux_freqs'] = mp.get_flux_freqs(refl) self.store['straight_tran_flux'] = mp.get_fluxes(trans) self.store['straight_refl_flux'] = mp.get_fluxes(refl)
def get_step_funcs(self): sf = {self._get_meep_output_comp(f) for f in self.static_fields} sf = (mp.at_beginning(f) for f in sf) df = {self._get_meep_output_comp(f) for f in self.dynamic_fields} df = (mp.at_every(self.dt, f) for f in df) volume = mp.in_volume(mp.Volume(center=self.size / 2, size=self.size)) fields = mp.with_prefix(f'{results_dir}/', *sf, *df) return volume, fields
def main(args): n = 3.4 # index of waveguide w = 1 # width of waveguide r = 1 # inner radius of ring pad = 4 # padding between waveguide and edge of PML dpml = 32 # thickness of PML sr = r + w + pad + dpml # radial size (cell is from 0 to sr) dimensions = mp.CYLINDRICAL cell = mp.Vector3(sr, 0, 0) # in cylindrical coordinates, the phi (angular) dependence of the fields # is given by exp(i m phi), where m is given by: m = args.m geometry = [mp.Block(center=mp.Vector3(r + (w / 2)), size=mp.Vector3(w, mp.inf, mp.inf), material=mp.Medium(index=n))] pml_layers = [mp.PML(dpml)] resolution = 20 # If we don't want to excite a specific mode symmetry, we can just # put a single point source at some arbitrary place, pointing in some # arbitrary direction. We will only look for Ez-polarized modes. fcen = args.fcen # pulse center frequency df = args.df # pulse frequency width sources = [mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(r + 0.1))] # note that the r -> -r mirror symmetry is exploited automatically sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, resolution=resolution, sources=sources, dimensions=dimensions, m=m) sim.run(mp.after_sources(mp.Harminv(mp.Ez, mp.Vector3(r + 0.1), fcen, df)), until_after_sources=200) # Output fields for one period at the end. (If we output # at a single time, we might accidentally catch the Ez field when it is # almost zero and get a distorted view.) We'll append the fields # to a file to get an r-by-t picture. We'll also output from -sr to -sr # instead of from 0 to sr. sim.run(mp.in_volume(mp.Volume(center=mp.Vector3(), size=mp.Vector3(2 * sr)), mp.at_beginning(mp.output_epsilon), mp.to_appended("ez", mp.at_every(1 / fcen / 20, mp.output_efield_z))), until=1 / fcen)
def run_simulation(self): self.sim.run(mp.at_beginning(mp.output_epsilon), mp.at_every(0.25, self.print_stuff), mp.at_end(self.print_stuff), mp.at_end(mp.output_efield_z), until=23) ref_out_field = self.ref_Ez if self.src_cmpt == mp.Ez else self.ref_Hz out_field = self.sim.fields.get_field(self.src_cmpt, mp.vec(4.13, 3.75)).real diff = abs(out_field - ref_out_field) self.assertTrue(abs(diff) <= 0.05 * abs(ref_out_field), "Field output differs")
def main(): # Some parameters to describe the geometry: eps = 13 # dielectric constant of waveguide w = 1.2 # width of waveguide r = 0.36 # radius of holes # The cell dimensions sy = 12 # size of cell in y direction (perpendicular to wvg.) dpml = 1 # PML thickness (y direction only!) cell = mp.Vector3(1, sy) b = mp.Block(size=mp.Vector3(mp.inf, w, mp.inf), material=mp.Medium(epsilon=eps)) c = mp.Cylinder(radius=r) fcen = 0.25 # pulse center frequency df = 1.5 # pulse freq. width: large df = short impulse s = mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Hz, center=mp.Vector3(0.1234)) sym = mp.Mirror(direction=mp.Y, phase=-1) sim = mp.Simulation(cell_size=cell, geometry=[b, c], sources=[s], symmetries=[sym], boundary_layers=[mp.PML(dpml, direction=mp.Y)], resolution=20) kx = False # if true, do run at specified kx and get fields k_interp = 19 # # k-points to interpolate, otherwise if kx: sim.k_point = mp.Vector3(kx) sim.run(mp.at_beginning(mp.output_epsilon), mp.after_sources( mp.Harminv(mp.Hz, mp.Vector3(0.1234), fcen, df)), until_after_sources=300) sim.run(mp.at_every(1 / fcen / 20, mp.output_hfield_z), until=1 / fcen) else: sim.run_k_points( 300, mp.interpolate(k_interp, [mp.Vector3(), mp.Vector3(0.5)]))
def run_simulation(self): self.sim.run(mp.at_beginning(mp.output_epsilon), mp.at_every(0.25, self.print_stuff), mp.at_end(self.print_stuff), mp.at_end(mp.output_efield_z), until=23) ref_out_field = self.ref_Ez if self.src_cmpt == mp.Ez else self.ref_Hz out_field = self.sim.fields.get_field(self.src_cmpt, mp.vec(4.13, 3.75)).real diff = abs(out_field - ref_out_field) self.assertTrue( abs(diff) <= 0.05 * abs(ref_out_field), "Field output differs")
def test_harminv(self): self.init() self.sim.run( mp.at_beginning(mp.output_epsilon), mp.after_sources(self.h), until_after_sources=300 ) m1, m2, m3 = self.h.modes self.assertAlmostEqual(m1.freq, 0.118101315147, places=4) self.assertAlmostEqual(m1.decay, -0.000731513241623, places=4) self.assertAlmostEqual(abs(m1.amp), 0.00341267634436, places=4) self.assertAlmostEqual(m1.amp.real, -0.00304951667301, places=4) self.assertAlmostEqual(m1.amp.imag, -0.00153192946717, places=4) fp = self.sim.get_field_point(mp.Ez, mp.Vector3(1, 1)) self.assertAlmostEqual(fp, -0.08185972142450348)
def do_simrun(base_refl_data=None, do_live=True, geo=None, **kwargs): sim = mp.Simulation(progress_interval=1e6 if do_live else 4, **sim_kwargs(geo=geo, **kwargs)) sim.reset_meep() # Now put in some flux monitors. Make sure the pulse source was selected refl, tran = add_monitors(sim) # for normal run, load negated fields to subtract incident from refl. fields if base_refl_data is not None: sim.load_minus_flux_data(refl, base_refl_data) run_args = ( mp.at_beginning(livefield), mp.at_every(5, livefield), ) if do_live else tuple() t0 = time.time() sim.run(*run_args, **monitor_until(geo=geo, **kwargs)) print('Realtime duration = {:.2f} seconds'.format(time.time() - t0)) return sim, refl, tran
def main(): n = 3.4 # index of waveguide w = 1 # width of waveguide r = 1 # inner radius of ring pad = 4 # padding between waveguide and edge of PML dpml = 2 # thickness of PML sxy = 2 * (r + w + pad + dpml) # cell size # Create a ring waveguide by two overlapping cylinders - later objects # take precedence over earlier objects, so we put the outer cylinder first. # and the inner (air) cylinder second. c1 = mp.Cylinder(radius=r + w, material=mp.Medium(index=n)) c2 = mp.Cylinder(radius=r) # If we don't want to excite a specific mode symmetry, we can just # put a single point source at some arbitrary place, pointing in some # arbitrary direction. We will only look for Ez-polarized modes. fcen = 0.15 # pulse center frequency df = 0.1 # pulse width (in frequency) src = mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ez, mp.Vector3(r + 0.1)) sim = mp.Simulation(cell_size=mp.Vector3(sxy, sxy), geometry=[c1, c2], sources=[src], resolution=10, symmetries=[mp.Mirror(mp.Y)], boundary_layers=[mp.PML(dpml)]) sim.run( mp.at_beginning(mp.output_epsilon), mp.after_sources(mp.Harminv(mp.Ez, mp.Vector3(r + 0.1), fcen, df)), until_after_sources=300 ) # Output fields for one period at the end. (If we output # at a single time, we might accidentally catch the Ez field when it is # almost zero and get a distorted view.) sim.run(mp.at_every((1 / fcen / 20), mp.output_efield_z), until=(1 / fcen))
def test_harminv(self): self.init() self.sim.run(mp.at_beginning(mp.output_epsilon), mp.after_sources(self.h), until_after_sources=300) m1 = self.h.modes[0] self.assertAlmostEqual(m1.freq, 0.118101315147, places=4) self.assertAlmostEqual(m1.decay, -0.000731513241623, places=4) self.assertAlmostEqual(abs(m1.amp), 0.00341267634436, places=4) self.assertAlmostEqual(m1.amp.real, -0.00304951667301, places=4) self.assertAlmostEqual(m1.amp.imag, -0.00153192946717, places=3) v = mp.Vector3(1, 1) fp = self.sim.get_field_point(mp.Ez, v) ep = self.sim.get_epsilon_point(v) places = 5 if mp.is_single_precision() else 7 self.assertAlmostEqual(ep, 11.559999999999999, places=places) self.assertAlmostEqual(fp, -0.08185972142450348, places=places)
def main(): n = 3.4 # index of waveguide w = 1 # width of waveguide r = 1 # inner radius of ring pad = 4 # padding between waveguide and edge of PML dpml = 2 # thickness of PML sxy = 2*(r+w+pad+dpml) # cell size # Create a ring waveguide by two overlapping cylinders - later objects # take precedence over earlier objects, so we put the outer cylinder first. # and the inner (air) cylinder second. c1 = mp.Cylinder(radius=r+w, material=mp.Medium(index=n)) c2 = mp.Cylinder(radius=r) # If we don't want to excite a specific mode symmetry, we can just # put a single point source at some arbitrary place, pointing in some # arbitrary direction. We will only look for Ez-polarized modes. fcen = 0.15 # pulse center frequency df = 0.1 # pulse width (in frequency) src = mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ez, mp.Vector3(r+0.1)) sim = mp.Simulation(cell_size=mp.Vector3(sxy, sxy), geometry=[c1, c2], sources=[src], resolution=10, symmetries=[mp.Mirror(mp.Y)], boundary_layers=[mp.PML(dpml)]) sim.run(mp.at_beginning(mp.output_epsilon), mp.after_sources(mp.Harminv(mp.Ez, mp.Vector3(r+0.1), fcen, df)), until_after_sources=300) # Output fields for one period at the end. (If we output # at a single time, we might accidentally catch the Ez field when it is # almost zero and get a distorted view.) sim.run(mp.at_every(1/fcen/20, mp.output_efield_z), until=1/fcen)
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 main(args): resolution = 20 # pixels/um eps = 13 # epsilon of waveguide w = 1.2 # width of the waveguide dpml = 1 # PML thickness largC = 16 # largura da celula altC = 16 # altura da celula sx = largC + dpml sy = altC + dpml fcen = args.fcen # pulse centger frequency df = args.df # pulse frequency width cell = mp.Vector3(sx,sy,0) def epsP(p): valorIm = -0.5 + math.pow(math.cos((p.y)*(2*math.pi/(altC/4))),2) return mp.Medium(epsilon=3, D_conductivity=2*math.pi*fcen*(valorIm)/3) epsP.do_averaging = True blk = mp.Block(size=mp.Vector3(mp.inf,mp.inf,mp.inf), material=mp.Medium(epsilon=eps)) geometry = [blk] # AQUI ESTÁ A LINHA DE TESTE: USAMOS A FUNCAO PARA DEFINIR O EPSILON # geometry.append(mp.Block(size=mp.Vector3(mp.inf,w,mp.inf))) geometry.append(mp.Block(center=mp.Vector3(),size=mp.Vector3(mp.inf,altC,mp.inf),material=epsP)) pml_layers = [mp.PML(1.0)] src = [mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(-0.5*sx+dpml), # fonte na esquerda; para colocar na direita usar 0.5*sx - dpml size=mp.Vector3(0,w))] sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=src, #symmetries=sym, resolution=resolution) sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("hz",mp.at_every(0.4,mp.output_hfield_z)), until=300) epsilon0 = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) plt.figure() plt.imshow(epsilon0.transpose(), interpolation='spline36', cmap='RdBu') plt.axis('off') plt.savefig('epsilon.png',format='png') plt.show()
results_plane = [] results_line = [] def get_slice_plane(sim): results_plane.append(sim.get_array( center=mp.Vector3(*plane_center), size=mp.Vector3(*plane_size), component=mp.Ez)) def get_slice_line(sim): results_line.append(sim.get_array( center=mp.Vector3(*line_center), size=mp.Vector3(*line_size), component=mp.Ez)) to_do_while_running = [mp.at_beginning(get_slice_line), mp.at_beginning(get_slice_plane), mp.at_every(period_line, get_slice_line), mp.at_every(period_plane, get_slice_plane)] #%% INITIALIZE sim.reset_meep() sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, sources=sources, symmetries=symmetries) sim.init_sim()
def simulation(f_cen, df, fmin, fmax, sy, dpml, air, sx, resolution, nfreq, geometry, init_refl_data, init_tran_flux, n, THICKNESS): #----------------------Simulation------------------------------ cell = mp.Vector3(sx, sy) thick = np.sum(THICKNESS) #define Gaussian plane wave sources = [ mp.Source(mp.GaussianSource(f_cen, fwidth=df), component=mp.Ez, center=mp.Vector3(0, 0.5 * sy - dpml - 0.02 * air, 0), size=mp.Vector3(x=sx)) ] #define pml layers pml_layers = [ mp.PML(thickness=dpml, direction=mp.Y, side=mp.High), mp.Absorber(thickness=dpml, direction=mp.Y, side=mp.Low) ] tran_fr = mp.FluxRegion(center=mp.Vector3(0, -0.5 * sy + dpml + 0.05), size=mp.Vector3(x=sx)) refl_fr = mp.FluxRegion(center=mp.Vector3(0, 0.5 * sy - dpml - 0.1 * air), size=mp.Vector3(x=sx)) mp.quiet(quietval=True) if n == 0: #if os.path.exists('dft_Z_empty.h5'): #os.remove('dft_Z_empty.h5') sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, sources=sources, symmetries=[mp.Mirror(mp.X)], dimensions=2, resolution=resolution, k_point=mp.Vector3()) #----------------------Monitors------------------------------ refl = sim.add_flux(f_cen, df, nfreq, refl_fr) tran = sim.add_flux(f_cen, df, nfreq, tran_fr) dfts_Z = sim.add_dft_fields([mp.Ez], fmin, fmax, nfreq, where=mp.Volume(center=mp.Vector3( 0, -sy * 0.5 + dpml + THICKNESS * 0.5, 0), size=mp.Vector3( sx, THICKNESS))) #----------------------Run------------------------------ sim.run(until_after_sources=mp.stop_when_fields_decayed( 5, mp.Ez, mp.Vector3(), 1e-3)) #----------------------Genetic------------------------------ sim.output_dft(dfts_Z, "dft_Z_empty") init_refl_data = sim.get_flux_data(refl) init_tran_flux = mp.get_fluxes(tran) sim.reset_meep() get = init_refl_data, init_tran_flux elif n == 1: #if os.path.exists('dft_Z_fields.h5'): #os.remove('dft_Z_fields.h5') sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.X)], dimensions=2, resolution=resolution, k_point=mp.Vector3()) refl = sim.add_flux(f_cen, df, nfreq, refl_fr) tran = sim.add_flux(f_cen, df, nfreq, tran_fr) sim.load_minus_flux_data(refl, init_refl_data) dfts_Z = sim.add_dft_fields([mp.Ez], fmin, fmax, nfreq, where=mp.Volume(center=mp.Vector3( 0, -sy * 0.5 + dpml + thick * 0.5, 0), size=mp.Vector3(sx, thick))) sim.run(until_after_sources=mp.stop_when_fields_decayed( 5, mp.Ez, mp.Vector3(), 1e-3)) #mp.at_beginning(mp.output_epsilon), sim.output_dft(dfts_Z, "dft_Z_fields") flux_freqs = mp.get_flux_freqs(refl) final_refl_flux = mp.get_fluxes(refl) wl = [] Rs = [] for i in range(nfreq): wl = np.append(wl, 1 / flux_freqs[i]) Rs = np.append(Rs, -final_refl_flux[i] / init_tran_flux[i]) sim.reset_meep() #get = np.min(Rs) en = enhance(Rs, 0) get = en, Rs, wl elif n == 2: #if os.path.exists('dft_Z_fields.h5'): #os.remove('dft_Z_fields.h5') sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.X)], dimensions=2, resolution=resolution, k_point=mp.Vector3()) refl = sim.add_flux(f_cen, df, nfreq, refl_fr) tran = sim.add_flux(f_cen, df, nfreq, tran_fr) sim.load_minus_flux_data(refl, init_refl_data) dfts_Z = sim.add_dft_fields([mp.Ez], fmin, fmax, nfreq, where=mp.Volume(center=mp.Vector3( 0, -sy * 0.5 + dpml + thick * 0.5, 0), size=mp.Vector3(sx, thick))) sim.run(mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 5, mp.Ez, mp.Vector3(), 1e-3)) #mp.at_beginning(mp.output_epsilon), sim.output_dft(dfts_Z, "dft_Z_fields") flux_freqs = mp.get_flux_freqs(refl) final_refl_flux = mp.get_fluxes(refl) final_tran_flux = mp.get_fluxes(tran) wl = [] Rs = [] Ts = [] for i in range(nfreq): wl = np.append(wl, 1 / flux_freqs[i]) Rs = np.append(Rs, -final_refl_flux[i] / init_tran_flux[i]) Ts = np.append(Ts, final_tran_flux[i] / init_tran_flux[i]) As = 1 - Rs - Ts plt.clf() plt.figure() plt.plot(wl, Rs, 'bo-', label='reflectance') plt.plot(wl, Ts, 'ro-', label='transmittance') plt.plot(wl, As, 'go-', label='absorption') plt.xlabel("wavelength (μm)") plt.legend(loc="upper right") plt.savefig('Extinction.png') #get = np.min(Rs) en = enhance(Rs, 1) get = en, Rs, wl #plt.figure() #plt.plot(wl,Rs,'bo-',label='reflectance') eps = h5py.File('_last-eps-000000000.h5', 'r') eps = eps.get('eps').value Enhance = np.rot90(eps) plt.clf() plt.figure() heat_map = sb.heatmap(Enhance, cmap='plasma', xticklabels=False, yticklabels=False) plt.xlabel("x-axis") plt.ylabel("y-axis") plt.savefig('Epsilon.png') return (get)
def main(args): resolution = 20 # pixels/um eps = 13 # dielectric constant of waveguide w = 1.2 # width of waveguide r = 0.36 # radius of holes d = 1.4 # defect spacing (ordinary spacing = 1) N = args.N # number of holes on either side of defect sy = args.sy # size of cell in y direction (perpendicular to wvg.) pad = 2 # padding between last hole and PML edge dpml = 1 # PML thickness sx = 2*(pad+dpml+N)+d-1 # size of cell in x direction cell = mp.Vector3(sx,sy,0) blk = mp.Block(size=mp.Vector3(mp.inf,w,mp.inf), material=mp.Medium(epsilon=eps)) geometry = [blk] 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)))) fcen = args.fcen # pulse center frequency df = args.df # pulse frequency width nfreq = 500 # number of frequencies at which to compute flux sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[], boundary_layers=[mp.PML(dpml)], resolution=20) if args.resonant_modes: sim.sources.append(mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Hz, center=mp.Vector3())) sim.symmetries.append(mp.Mirror(mp.Y, phase=-1)) sim.symmetries.append(mp.Mirror(mp.X, phase=-1)) sim.run(mp.at_beginning(mp.output_epsilon), mp.after_sources(mp.Harminv(mp.Hz, mp.Vector3(), fcen, df)), until_after_sources=400) sim.run(mp.at_every(1/fcen/20, mp.output_hfield_z), until=1/fcen) else: sim.sources.append(mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(-0.5*sx+dpml), size=mp.Vector3(0,w))) sim.symmetries.append(mp.Mirror(mp.Y, phase=-1)) freg = mp.FluxRegion(center=mp.Vector3(0.5*sx-dpml-0.5), size=mp.Vector3(0,2*w)) # transmitted flux trans = sim.add_flux(fcen, df, nfreq, freg) vol = mp.Volume(mp.Vector3(), size=mp.Vector3(sx)) sim.run(mp.at_beginning(mp.output_epsilon), mp.during_sources(mp.in_volume(vol, mp.to_appended("hz-slice", mp.at_every(0.4, mp.output_hfield_z)))), until_after_sources=mp.stop_when_fields_decayed(50, mp.Ey, mp.Vector3(0.5*sx-dpml-0.5), 1e-3)) sim.display_fluxes(trans) # print out the flux spectrum
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"))
def main(args): # Some parameters to describe the geometry: eps = 13 # dielectric constant of waveguide w = 1.2 # width of waveguide r = 0.36 # radius of holes d = 1.4 # defect spacing (ordinary spacing = 1) N = args.N # number of holes on either side of defect # The cell dimensions sy = args.sy # size of cell in y direction (perpendicular to wvg.) pad = 2 # padding between last hole and PML edge dpml = 1 # PML thickness sx = 2 * (pad + dpml + N) + d - 1 # size of cell in x direction cell = mp.Vector3(sx, sy, 0) blk = mp.Block(size=mp.Vector3(mp.inf, w, mp.inf), material=mp.Medium(epsilon=eps)) geometry = [blk] 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)))) fcen = args.fcen # pulse center frequency df = args.df # pulse frequency width nfreq = 500 # number of frequencies at which to compute flux sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[], boundary_layers=[mp.PML(dpml)], resolution=20) if args.resonant_modes: sim.sources.append( mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Hz, mp.Vector3())) sim.symmetries.append(mp.Mirror(mp.Y, phase=-1)) sim.symmetries.append(mp.Mirror(mp.X, phase=-1)) sim.run( # mp.at_beginning(mp.output_epsilon), mp.after_sources(mp.Harminv(mp.Hz, mp.Vector3(), fcen, df)), until_after_sources=400) # sim.run(mp.at_every(1 / fcen / 20, mp.output_hfield_z), until=1 / fcen) else: sim.sources.append( mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ey, mp.Vector3(dpml + (-0.5 * sx)), size=mp.Vector3(0, w))) sim.symmetries.append(mp.Mirror(mp.Y, phase=-1)) freg = mp.FluxRegion(center=mp.Vector3((0.5 * sx) - dpml - 0.5), size=mp.Vector3(0, 2 * w)) # transmitted flux trans = sim.add_flux(fcen, df, nfreq, freg) vol = mp.Volume(mp.Vector3(), size=mp.Vector3(sx)) sim.run(mp.at_beginning(mp.output_epsilon), mp.during_sources( mp.in_volume( vol, mp.to_appended("hz-slice", mp.at_every(0.4, mp.output_hfield_z)))), until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ey, mp.Vector3((0.5 * sx) - dpml - 0.5, 0), 1e-3)) sim.display_fluxes(trans) # print out the flux spectrum
def generate_model(params, args): cell = mp.Vector3(params.cell_width, params.cell_height, 0) spheres = mplib.geo2D_spherical_pc(params.ps_n, params.si_n, params.ps_thickness, params.si_thickness, params.cell_height, params.num_layers, params.cell_width/2-200, params.sph_radius, params.sph_spacing, params.offset, params.xoffset) thin_film = mplib.geo2D_thin_film(params.ps_n, params.ps_thickness, params.cell_height, params.cell_width/2-100) pc_ideal = mplib.geo2D_photonic_crystal(params.ps_n, params.other_n, params.ps_thickness, params.cell_height, params.si_thickness, params.si_n, 5, params.cell_width/2-200, 50) geometry = pc_ideal if args.background: geometry = [] ## Gaussian source source_pos = -1*params.cell_width/2 + params.dpml+5 sources = [mp.Source(mp.GaussianSource(frequency=params.freq, fwidth=params.df), mp.Ez, center=mp.Vector3(source_pos,0,0), size=mp.Vector3(0,params.cell_height,0))] pml_layers = [mp.PML(params.dpml, direction=mp.X)] sim = mp.Simulation(cell_size = cell, boundary_layers = pml_layers, geometry = geometry, filename_prefix=args.filename, sources = sources, k_point = mp.Vector3(0,0,0), resolution = params.resolution) freg_trans = mp.FluxRegion(center = mp.Vector3(0.5*params.cell_width - params.dpml - 1,0,0), size = mp.Vector3(0, params.cell_height, 0)) freg_ref = mp.FluxRegion(center = mp.Vector3(source_pos + 10, 0, 0), size = mp.Vector3(0, params.cell_height), weight=1.0) trans_flux = sim.add_flux(params.freq, params.df , 500, freg_trans) ref_flux = sim.add_flux(params.freq, params.df, 500, freg_ref) vol = mp.Volume(mp.Vector3(0), size = mp.Vector3(params.cell_width,0,0)) if not args.reflectflux: sim.load_minus_flux("pulse_bg_flux_{:d}".format(params.wavelength),ref_flux) if args.outdir: print("Output directory: {:s}".format(args.outdir)) sim.use_output_directory(args.outdir) if args.geometry: sim.run(mp.at_beginning(mp.output_epsilon), until=1) else: sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("ez", mp.at_every(params.dt, mp.output_efield_z)), #mp.to_appended("ep", mp.at_every(params.dt, mp.output_dpwr)), mp.in_volume(vol, mp.to_appended("ez_slice", mp.at_every(params.dt, mp.output_efield_z))), until=params.time) if args.reflectflux: sim.save_flux("pulse_bg_flux_{:d}".format(params.wavelength),ref_flux) #sim.save_flux("bg_flux_other", trans_flux) sim.display_fluxes(trans_flux, ref_flux)
import meep as mp cell_size = mp.Vector3(6, 6, 0) geometry1 = [ mp.Cylinder(center=mp.Vector3(), radius=1.0, material=mp.Medium(index=3.5)) ] sim1 = mp.Simulation(cell_size=cell_size, geometry=geometry1, resolution=20) sim1.init_sim() geometry2 = [ mp.Cylinder(center=mp.Vector3(1, 1), radius=1.0, material=mp.Medium(index=3.5)) ] sim2 = mp.Simulation(cell_size=cell_size, geometry=geometry2, resolution=20) sim2.init_sim() sim1.fields.phase_in_material(sim2.structure, 10.0) sim1.run(mp.at_beginning(mp.output_epsilon), mp.at_every(0.5, mp.output_epsilon), until=10)
def simulate(self, config): start = time.time() dpml = config["dpml"] resolution = config["resolution"] use_fixed_time = config["use_fixed_time"] simulation_time = config["simulation_time"] padding = config["padding"] ff_pts = config["ff_pts"] ff_cover = config["ff_cover"] ff_calc = config["ff_calc"] use_symmetries = config["use_symmetries"] calculate_flux = config["calculate_flux"] calculate_source_flux = config["calculate_source_flux"] pixels = config["source_flux_pixel_size"] ff_calculations = config["ff_calculations"] ff_angle = math.pi / config["ff_angle"] FibonacciSampling = config["fibb_sampling"] simulation_ratio = eval(config["simulation_ratio"]) substrate_ratio = eval(config["substrate_ratio"]) output_ff = config["output_ff"] polarization_in_plane = config["polarization_in_plane"] geometry = config["geometry"] material_function = config["material_function"] substrate_height = self.pyramid_height * substrate_ratio #height of the substrate, measured as fraction of pyramid height #"Cell size" sx = self.pyramid_width * simulation_ratio #size of the cell in xy-plane is measured as a fraction of pyramid width sy = sx sz = self.pyramid_height * simulation_ratio #z-"height" of sim. cell measured as a fraction of pyramid height. sh = substrate_height sh = 0 padding = padding ##distance from pml_layers to flux regions so PML don't overlap flux regions cell = mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, sz + 2 * dpml) #size of the simulation cell in meep units if self.CL_material == "Au": CL_material = Au elif self.CL_material == "Ag": CL_material = Ag elif self.CL_material == "Ag_visible": CL_material = Ag_visible else: CL_material = GaN print('CL MATERIAL:', CL_material, self.CL_material) #Symmetries for the simulation def create_symmetry(self): if self.source_direction == mp.Ex or self.source_direction == [ 1, 0, 0 ]: symmetry = [mp.Mirror(mp.Y), mp.Mirror(mp.X, phase=-1)] elif self.source_direction == mp.Ey or self.source_direction == [ 0, 1, 0 ]: symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y, phase=-1)] elif self.source_direction == mp.Ez or self.source_direction == [ 0, 0, 1 ]: symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] else: symmetry = [] return symmetry #Flux regions to calculate the total flux emitted from the simulation def define_flux_regions(sx, sy, sz, padding): fluxregion = [] fluxregion.append( mp.FluxRegion( #region x to calculate flux from center=mp.Vector3(sx / 2 - padding, 0, 0), size=mp.Vector3(0, sy - padding * 2, sz - padding * 2), direction=mp.X)) fluxregion.append( mp.FluxRegion( # region -x to calculate flux from center=mp.Vector3(-sx / 2 + padding, 0, 0), size=mp.Vector3(0, sy - padding * 2, sz - padding * 2), direction=mp.X, weight=-1)) fluxregion.append( mp.FluxRegion( #region y to calculate flux from center=mp.Vector3(0, sy / 2 - padding, 0), size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2), direction=mp.Y)) fluxregion.append( mp.FluxRegion( #region -y to calculate flux from center=mp.Vector3(0, -sy / 2 + padding, 0), size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2), direction=mp.Y, weight=-1)) fluxregion.append( mp.FluxRegion( #z-bottom region to calculate flux from center=mp.Vector3(0, 0, sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z)) fluxregion.append( mp.FluxRegion( #z-top region to calculate flux from center=mp.Vector3(0, 0, -sz / 2 + padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z, weight=-1)) return fluxregion #Flux regions to calculate the far field flux def define_nearfield_regions(sx, sy, sz, sh, padding, ff_cover): nearfieldregions = [] nfrAbove = [] nfrBelow = [] #if ff_calc == True: #If we wish to calculate the far-field above and below the pyramid # if ff_calc == "Both": nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0, -sh / 2), size=mp.Vector3(0, sy - padding * 2, sz - sh - padding * 2), direction=mp.X)) nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0, -sh / 2), size=mp.Vector3(0, sy - padding * 2, sz - sh - padding * 2), direction=mp.X, weight=-1)) nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding, -sh / 2), size=mp.Vector3(sx - padding * 2, 0, sz - sh - padding * 2), direction=mp.Y)) nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding, -sh / 2), size=mp.Vector3(sx - padding * 2, 0, sz - sh - padding * 2), direction=mp.Y, weight=-1)) nfrAbove.append( mp.Near2FarRegion( #nearfield -z. above pyramid. center=mp.Vector3(0, 0, -sz / 2 + padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z, weight=-1)) #Far-field to calculate below transmissions nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0, sz / 2 - sh / 2), size=mp.Vector3(0, sy - padding * 2, sh - padding * 2), direction=mp.X)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0, sz / 2 - sh / 2), size=mp.Vector3(0, sy - padding * 2, sh - padding * 2), direction=mp.X, weight=-1)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding, sz / 2 - sh / 2), size=mp.Vector3(sx - padding * 2, 0, sh - padding * 2), direction=mp.Y)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding, sz / 2 - sh / 2), size=mp.Vector3(sx - padding * 2, 0, sh - padding * 2), direction=mp.Y, weight=-1)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(0, 0, sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z)) if ff_cover == True: nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3( 0, 0, sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3( 0, 0, -sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z, weight=-1)) nearfieldregions.append(nfrAbove) nearfieldregions.append(nfrBelow) return nearfieldregions ###GEOMETRY FOR THE SIMULATION################################################# #"Material parameters" air = mp.Medium(epsilon=1) #air dielectric value SubstrateEps = GaN #substrate epsilon #"Geometry to define the substrate and block of air to truncate the pyramid if self.truncation =/= 0" geometries = [] #Substrate geometries.append( mp.Sphere(center=mp.Vector3(0, 0, self.distance / 2 + self.radius), radius=self.radius, material=CL_material)) geometries.append( mp.Sphere(center=mp.Vector3(0, 0, -self.distance / 2 - self.radius), radius=self.radius, material=CL_material)) if geometry == None: geometries = [] if material_function == None: material_function = None ###SYMMETRIES######################################################### #"Symmetry logic." if use_symmetries: symmetry = create_symmetry(self) print('symmetry on:', symmetry) else: symmetry = [] ###PML_LAYERS################################################################### pml_layer = [mp.PML(dpml)] ###SOURCE####################################################################### #"A gaussian with pulse source proportional to exp(-iwt-(t-t_0)^2/(2w^2))" abs_source_position_x = 0 abs_source_position_y = 0 abs_source_position_z = 0 source = [ mp.Source( mp.GaussianSource( frequency=self.frequency_center, fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source component=mp.Ez, amplitude=1, center=mp.Vector3(abs_source_position_x, abs_source_position_y, abs_source_position_z)) ] #source.append(mp.Source(mp.GaussianSource(frequency=self.frequency_center,fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source # component=mp.Ey, # amplitude=self.source_direction[1], # center=mp.Vector3(abs_source_position_x,abs_source_position_y,abs_source_position_z))) #source.append(mp.Source(mp.GaussianSource(frequency=self.frequency_center,fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source # component=mp.Ez, # amplitude=self.source_direction[2], # center=mp.Vector3(abs_source_position_x,abs_source_position_y,abs_source_position_z))) #MEEP simulation constructor sim = mp.Simulation( cell_size=cell, geometry=geometries, symmetries=symmetry, sources=source, #eps_averaging=True, #subpixel_tol=1e-4, #subpixel_maxeval=1000, dimensions=3, #default_material=GaN, Courant=0.1, extra_materials=[CL_material], boundary_layers=pml_layer, split_chunks_evenly=False, resolution=resolution) ###SOURCE REGION################################################### print('pixels', pixels) def define_flux_source_regions(abs_source_position_x, abs_source_position_y, abs_source_position_z, resolution, pixels): distance = pixels * 1 / resolution source_region = [] source_region.append( mp.FluxRegion( #region x to calculate flux from center=mp.Vector3(abs_source_position_x + distance, abs_source_position_y, abs_source_position_z), size=mp.Vector3(0, 2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution), direction=mp.X)) source_region.append( mp.FluxRegion( # region -x to calculate flux from center=mp.Vector3(abs_source_position_x - distance, abs_source_position_y, abs_source_position_z), size=mp.Vector3(0, 2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution), direction=mp.X, weight=-1)) source_region.append( mp.FluxRegion( #region y to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y + distance, abs_source_position_z), size=mp.Vector3(2 * pixels * 1 / resolution, 0, 2 * pixels * 1 / resolution), direction=mp.Y)) source_region.append( mp.FluxRegion( #region -y to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y - distance, abs_source_position_z), size=mp.Vector3(2 * pixels * 1 / resolution, 0, 2 * pixels * 1 / resolution), direction=mp.Y, weight=-1)) source_region.append( mp.FluxRegion( #z-bottom region to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y, abs_source_position_z + distance), size=mp.Vector3(2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution, 0), direction=mp.Z)) source_region.append( mp.FluxRegion( #z-top region to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y, abs_source_position_z - distance), size=mp.Vector3(2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution, 0), direction=mp.Z, weight=-1)) return source_region ###REGIONS###################################################################### #"These regions define the borders of the cell with distance 'padding' between the flux region and the dpml region to avoid calculation errors." if calculate_flux: flux_regions = define_flux_regions(sx, sy, sz, padding) fr1, fr2, fr3, fr4, fr5, fr6 = flux_regions flux_total = sim.add_flux(self.frequency_center, self.frequency_width, self.number_of_freqs, fr1, fr2, fr3, fr4, fr5, fr6) #calculate flux for flux regions if calculate_source_flux: sr1, sr2, sr3, sr4, sr5, sr6 = define_flux_source_regions( abs_source_position_x, abs_source_position_y, abs_source_position_z, resolution, pixels) flux_source = sim.add_flux(self.frequency_center, self.frequency_width, self.number_of_freqs, sr1, sr2, sr3, sr4, sr5, sr6) ###FAR FIELD REGION############################################################# #"The simulation calculates the far field flux from the regions 1-5 below. It correspons to the air above and at the side of the pyramids. The edge of the simulation cell that touches the substrate is not added to this region. Far-field calculations can not handle different materials." if ff_calculations == True: nearfieldregions = define_nearfield_regions( sx, sy, sz, sh, padding, ff_cover) if ff_cover == True: nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6 = nearfieldregions[0] nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6 = nearfieldregions[1] else: nfrA1, nfrA2, nfrA3, nfrA4, nfrA6 = nearfieldregions[0] nfrB1, nfrB2, nfrB3, nfrB4, nfrB6 = nearfieldregions[1] if ff_calc == "Both" and ff_cover == True: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6) nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6) elif ff_calc == "Above" and ff_cover == True: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6) elif ff_calc == "Above" and ff_cover == False: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA6) elif ff_calc == "Below" and ff_cover == True: nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6) elif ff_calc == "Below" and ff_cover == False: nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB6) #Assumed ff_calc == Both and ff_cover == False else: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA6) nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB6) ###RUN########################################################################## #"Run the simulation" if True: sim.plot2D(output_plane=mp.Volume( center=mp.Vector3(0, 0 * abs_source_position_y, 0), size=mp.Vector3(0, sy + 2 * dpml, sz + 2 * dpml))) #plt.show() plt.savefig('foo.pdf') sim.plot2D(output_plane=mp.Volume( center=mp.Vector3(0 * abs_source_position_x, 0, 0), size=mp.Vector3(sx + 2 * dpml, 0, sz + 2 * dpml))) #plt.show() plt.savefig('foo2.pdf') sim.plot2D(output_plane=mp.Volume( center=mp.Vector3(0, 0, abs_source_position_z), size=mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, 0))) #sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,sz/2-sh-0.01),size=mp.Vector3(sx+2*dpml,sy+2*dpml,0))) plt.savefig('foo3.pdf') if use_fixed_time: sim.run( mp.dft_ldos(self.frequency_center, self.frequency_width, self.number_of_freqs), # mp.at_beginning(mp.output_epsilon), #until_after_sources=mp.stop_when_fields_decayed(2,mp.Ey,mp.Vector3(0,0,sbs_cource_position+0.2),1e-2)) until=simulation_time) else: #Withdraw maximum dipole amplitude direction max_index = self.source_direction.index(max(self.source_direction)) if max_index == 0: detector_pol = mp.Ex elif max_index == 1: detector_pol = mp.Ey else: detector_pol = mp.Ez #TODO: exchange self.source_direction to maxmimum dipole ampltitude sim.run( mp.dft_ldos(self.frequency_center, self.frequency_width, self.number_of_freqs), #mp.to_appended("ex", mp.at_every(0.6, mp.output_efield_x)), mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 2, detector_pol, mp.Vector3(0, 0, abs_source_position_z + 0.2), 1e-3)) ###OUTPUT CALCULATIONS########################################################## #"Calculate the poynting flux given the far field values of E, H." myIntegration = True nfreq = self.number_of_freqs r = 2 * math.pow( self.pyramid_height, 2 ) * self.frequency_center * 2 * 10 # 10 times the Fraunhofer-distance if ff_calculations: fields = [] P_tot_ff = np.zeros(self.number_of_freqs) npts = ff_pts #number of far-field points Px = 0 Py = 0 Pz = 0 theta = ff_angle phi = math.pi * 2 #"How many points on the ff-sphere" #global xPts #xPts=[] #global yPts #yPts=[] #global zPts #zPts=[] Pr_Array = [] if myIntegration == True: #how to pick ff-points, this uses fibbonaci-sphere distribution if FibonacciSampling == True: if theta == math.pi / 3: offset = 1.5 / npts elif theta == math.pi / 4: npts = npts * 2 offset = 1.15 / npts elif theta == math.pi / 5: npts = npts * 2.5 offset = 0.95 / npts elif theta == math.pi / 6: #npts=npts*3 offset = 0.8 / npts elif theta == math.pi / 7: npts = npts * 3 offset = 0.7 / npts elif theta == math.pi / 8: npts = npts * 3 offset = 0.6 / npts elif theta == math.pi / 12: npts = npts * 3 offset = 0.4 / npts else: offset = 0.2 / npts xPts, yPts, zPts = fibspherepts(r, theta, npts, offset) range_npts = int((theta / math.pi) * npts) else: #check out Lebedev quadrature #if not fibb sampling then spherical sampling theta_pts = npts phi_pts = npts * 2 xPts, yPts, zPts = sphericalpts(r, theta, phi, theta_pts, phi_pts) range_npts = int(theta_pts * phi_pts + 1) if ff_calc == "Both": #Pr_ArrayA for above, B for below Pr_ArrayA = [] Pr_ArrayB = [] P_tot_ffA = [0] * (self.number_of_freqs) P_tot_ffB = [0] * (self.number_of_freqs) for n in range(range_npts): ffA = sim.get_farfield( nearfieldAbove, mp.Vector3(xPts[n], yPts[n], zPts[n])) ffB = sim.get_farfield( nearfieldBelow, mp.Vector3(xPts[n], yPts[n], -1 * zPts[n])) i = 0 for k in range(nfreq): "Calculate the poynting vector in x,y,z direction" PrA = myPoyntingFlux(ffA, i) PrB = myPoyntingFlux(ffB, i) Pr_ArrayA.append(PrA) Pr_ArrayB.append(PrB) "the spherical cap has area 2*pi*r^2*(1-cos(theta))" "divided by npts and we get evenly sized area chunks" surface_Element = 2 * math.pi * pow( r, 2) * (1 - math.cos(theta)) / range_npts P_tot_ffA[k] += surface_Element * (1) * (PrA) P_tot_ffB[k] += surface_Element * (1) * (PrB) i = i + 6 #to keep track of the correct entries in the ff array if ff_calc == "Below": for n in range(range_npts): ff = sim.get_farfield( nearfieldBelow, mp.Vector3(xPts[n], yPts[n], -1 * zPts[n])) fields.append({ "pos": (xPts[n], yPts[n], zPts[n]), "field": ff }) i = 0 for k in range(nfreq): "Calculate the poynting vector in x,y,z direction" Pr = myPoyntingFlux(ff, i) Pr_Array.append(Pr) "the spherical cap has area 2*pi*r^2*(1-cos(theta))" "divided by npts and we get evenly sized area chunks" surface_Element = 2 * math.pi * pow( r, 2) * (1 - math.cos(theta)) / range_npts P_tot_ff[k] += surface_Element * (1) * (Pr) i = i + 6 #to keep track of the correct entries in the ff array if ff_calc == "Above": for n in range(range_npts): ff = sim.get_farfield( nearfieldAbove, mp.Vector3(xPts[n], yPts[n], zPts[n])) fields.append({ "pos": (xPts[n], yPts[n], zPts[n]), "field": ff }) # print('ff,n,x,y,z',n,xPts[n],yPts[n],zPts[n],ff) # print('fields',fields[n]) # print('------') i = 0 for k in range(nfreq): "Calculate the poynting vector in x,y,z direction" Pr = myPoyntingFlux(ff, i) Pr_Array.append(Pr) "the spherical cap has area 2*pi*r^2*(1-cos(theta))" "divided by npts and we get evenly sized area chunks" surface_Element = 2 * math.pi * pow( r, 2) * (1 - math.cos(theta)) / range_npts P_tot_ff[k] += surface_Element * (1) * (Pr) #print('S',surface_Element,'r',r,'theta',theta,'range npts',range_npts) i = i + 6 #to keep track of the correct entries in the ff array # print('P_tot_ff',P_tot_ff) # print('fields',fields) # print('Pr_Array',Pr_Array) ##CALCULATE FLUX OUT FROM BOX########################################### if calculate_flux: #"Initialize variables to be used, pf stands for 'per frequency'" flux_tot_value = np.zeros( self.number_of_freqs) #total flux out from box flux_tot_ff_ratio = np.zeros(self.number_of_freqs) flux_tot_out = mp.get_fluxes(flux_total) #save total flux data freqs_out = mp.get_flux_freqs(flux_total) print('freqs', freqs_out) if calculate_source_flux: source_flux_out = mp.get_fluxes(flux_source) elapsed_time = round((time.time() - start) / 60, 1) print('LDOS', sim.ldos_data) print('LDOS0', sim.ldos_data[0]) for i in range(len(flux_tot_out)): print(flux_tot_out[i] * 100 / source_flux_out[i], 'LE percentage for lambda: ', 1 / freqs_out[i]) if False: fig = plt.figure() ax = fig.gca(projection='3d') Pr_Max = max(Pr_Array) R = [n / Pr_Max for n in Pr_Array] print(R) # R=1 pts = len(Pr_Array) theta, phi = np.linspace(0, np.pi, pts), np.linspace(0, 2 * np.pi, pts) THETA, PHI = np.meshgrid(theta, phi) #print(xPts) # print(yPts) # print(zPts) X = np.zeros(pts**2) Y = np.zeros(pts**2) Z = np.zeros(pts**2) print('R pts', pts) print('sample pts', len(xPts)) for n in range(pts): xPts[n] = R[n] * xPts[n] yPts[n] = R[n] * yPts[n] zPts[n] = R[n] * zPts[n] # #print(X) # #print(Y) # #print(Z) #ax.set_xlim(-100,100) #ax.set_zlim(-100,100) #ax.set_ylim(-100,100) #ax.scatter(xPts,yPts,zPts) u = np.linspace(0, 2 * np.pi, 120) v = np.linspace(0, np.pi / 6, 120) r_ = np.asarray(R) x = np.outer(np.cos(u), np.sin(v)) y = np.outer(np.sin(u), np.sin(v)) z = R * np.outer(np.ones(np.size(u)), np.cos(v)) print('lenx', len(x), 'leny', len(y), 'lenz', len(z)) #r = np.sqrt(xPts**2+yPts**2+zPts**2) #plot(r) ax.plot_surface(x, y, z, cmap='plasma') #ax.quiver(0,0,0,0,0,1,length=1.2,color='brown',normalize='true') #ax.text(0,0,1.3, '$\mathcal{P}$',size=20, zdir=None) #ax.set_zlim(-1,1) #ax.axis('off') #ax.plot_trisurf(list(x),list(y),list(z),cmap='plasma') plt.show() for n in range(len(flux_tot_out)): flux_tot_out[n] = round(flux_tot_out[n], 11) # P_tot_ff[n]=round(P_tot_ff[n],9) ##Some processing to calculate the flux ratios per frequency if ff_calculations: for n in range(len(flux_tot_out)): # flux_tot_out[n]=round(flux_tot_out[n],9) P_tot_ff[n] = round(P_tot_ff[n], 11) for i in range(self.number_of_freqs): flux_tot_ff_ratio[i] = round(P_tot_ff[i] / flux_tot_out[i], 11) if ff_calc == "Both": P_tot_ff = [] P_tot_ff.append(P_tot_ffA) P_tot_ff.append(P_tot_ffB) flux_tot_ff_ratio = [] flux_tot_ff_ratioA = [0] * (self.number_of_freqs) flux_tot_ff_ratioB = [0] * (self.number_of_freqs) for i in range(self.number_of_freqs): flux_tot_ff_ratioA[i] = round( P_tot_ffA[i] / flux_tot_out[i], 11) flux_tot_ff_ratioB[i] = round( P_tot_ffB[i] / flux_tot_out[i], 11) flux_tot_ff_ratio.append(flux_tot_ff_ratioA) flux_tot_ff_ratio.append(flux_tot_ff_ratioB) return { "total_flux": flux_tot_out, "source_flux": source_flux_out, "ff_at_angle": P_tot_ff, "flux_ratio": flux_tot_ff_ratio, "LDOS": sim.ldos_data, "Elapsed time (min)": elapsed_time } else: return { "total_flux": flux_tot_out, "source_flux": source_flux_out, "ff_at_angle": None, "flux_ratio": None, "LDOS": sim.ldos_data, "Elapsed time (min)": elapsed_time }
def main(args): print("\nstart time:", datetime.now()) # -------------------------------------------------------------------------- # physical parameters characterizing light source and interface characteris- # tics (must be adjusted - eihter here or via command line interface (CLI)) # -------------------------------------------------------------------------- interface = args.interface s_pol = args.s_pol ref_medium = args.ref_medium n1 = args.n1 n2 = args.n2 kw_0 = args.kw_0 kr_w = args.kr_w kr_c = args.kr_c # angle of incidence chi_deg = args.chi_deg #chi_deg = 1.0*Critical(n1, n2) #chi_deg = 0.95*Brewster(n1, n2) test_output = args.test_output # -------------------------------------------------------------------------- # specific Meep parameters (may need to be adjusted) # -------------------------------------------------------------------------- sx = 5 # size of cell including PML in x-direction sy = 5 # size of cell including PML in y-direction pml_thickness = 0.25 # thickness of PML layer freq = 12 # vacuum frequency of source (5 to 12 is good) runtime = 10 # runs simulation for 10 times freq periods # number of pixels per wavelength in the denser medium (at least 10, # 20 to 30 is a good choice) pixel = 10 # source position with respect to the center (point of impact) in Meep # units (-2.15 good); if equal -r_w, then source position coincides with # waist position source_shift = -2.15 # -------------------------------------------------------------------------- # derived (Meep) parameters (do not change) # -------------------------------------------------------------------------- k_vac = 2 * math.pi * freq k1 = n1 * k_vac n_ref = (1 if ref_medium == 0 else n1 if ref_medium == 1 else n2 if ref_medium == 2 else math.nan) r_w = kr_w / (n_ref * k_vac) w_0 = kw_0 / (n_ref * k_vac) r_c = kr_c / (n_ref * k_vac) shift = source_shift + r_w chi_rad = math.radians(chi_deg) params = dict(W_y=w_0, k=k1) # -------------------------------------------------------------------------- # placement of the dielectric interface within the computational cell # -------------------------------------------------------------------------- # helper functions def alpha(chi_rad): """Angle of inclined plane with y-axis in radians.""" return math.pi/2 - chi_rad def Delta_x(alpha): """Inclined plane offset to the center of the cell.""" sin_alpha = math.sin(alpha) cos_alpha = math.cos(alpha) return (sx/2) * (((math.sqrt(2) - cos_alpha) - sin_alpha) / sin_alpha) cell = mp.Vector3(sx, sy, 0) # geometry-lattice if interface == "planar": default_material = mp.Medium(index=n1) # located at lower right edge for 45 degree geometry = [mp.Block(size=mp.Vector3(mp.inf, sx*math.sqrt(2), mp.inf), center=mp.Vector3(+sx/2 + Delta_x(alpha(chi_rad)), -sy/2), e1=mp.Vector3(1/math.tan(alpha(chi_rad)), 1, 0), e2=mp.Vector3(-1, 1/math.tan(alpha(chi_rad)), 0), e3=mp.Vector3(0, 0, 1), material=mp.Medium(index=n2))] elif interface == "concave": default_material = mp.Medium(index=n2) # move center to the right in order to ensure that the point of impact # is always centrally placed geometry = [mp.Cylinder(center=mp.Vector3(-r_c*math.cos(chi_rad), +r_c*math.sin(chi_rad)), height=mp.inf, radius=r_c, material=mp.Medium(index=n1))] elif interface == "convex": default_material = mp.Medium(index=n1) # move center to the right in order to ensure that the point of impact # is always centrally placed geometry = [mp.Cylinder(center=mp.Vector3(+r_c*math.cos(chi_rad), -r_c*math.sin(chi_rad)), height=mp.inf, radius=r_c, material=mp.Medium(index=n2))] # -------------------------------------------------------------------------- # add absorbing boundary conditions and discretize structure # -------------------------------------------------------------------------- pml_layers = [mp.PML(pml_thickness)] resolution = pixel * (n1 if n1 > n2 else n2) * freq # set Courant factor (mandatory if either n1 or n2 is smaller than 1) Courant = (n1 if n1 < n2 else n2) / 2 # -------------------------------------------------------------------------- # beam profile distribution (field amplitude) at the waist of the beam # -------------------------------------------------------------------------- def Gauss(r, params): """Gauss profile.""" W_y = params['W_y'] return math.exp(-(r.y / W_y)**2) # -------------------------------------------------------------------------- # spectrum amplitude distribution # -------------------------------------------------------------------------- def f_Gauss(k_y, params): """Gaussian spectrum amplitude.""" W_y = params['W_y'] return math.exp(-(k_y*W_y/2)**2) if test_output: print("Gauss spectrum:", f_Gauss(0.2, params)) # -------------------------------------------------------------------------- # plane wave decomposition # (purpose: calculate field amplitude at light source position if not # coinciding with beam waist) # -------------------------------------------------------------------------- def psi(r, x, params): """Field amplitude function.""" try: getattr(psi, "called") except AttributeError: psi.called = True print("Calculating inital field configuration. " "This will take some time...") def phase(k_y, x, y): """Phase function.""" return x*math.sqrt(k1**2 - k_y**2) + k_y*y try: (result, real_tol, imag_tol) = complex_quad(lambda k_y: f_Gauss(k_y, params) * cmath.exp(1j*phase(k_y, x, r.y)), -k1, k1) except Exception as e: print(type(e).__name__ + ":", e) sys.exit() return result # -------------------------------------------------------------------------- # some test outputs (uncomment if needed) # -------------------------------------------------------------------------- if test_output: x, y, z = -2.15, 0.3, 0.5 r = mp.Vector3(0, y, z) print() print("psi :", psi(r, x, params)) sys.exit() # -------------------------------------------------------------------------- # display values of physical variables # -------------------------------------------------------------------------- print() print("Specified variables and derived values:") print("n1:", n1) print("n2:", n2) print("chi: ", chi_deg, " [degree]") print("incl.:", 90 - chi_deg, " [degree]") print("kw_0: ", kw_0) if interface != "planar": print("kr_c: ", kr_c) print("kr_w: ", kr_w) print("k_vac:", k_vac) print("polarisation:", "s" if s_pol else "p") print("interface:", interface) print() # -------------------------------------------------------------------------- # specify current source, output functions and run simulation # -------------------------------------------------------------------------- force_complex_fields = False # default: False eps_averaging = True # default: True filename_prefix = None sources = [mp.Source(src=mp.ContinuousSource(frequency=freq, width=0.5), component=mp.Ez if s_pol else mp.Ey, size=mp.Vector3(0, 2, 0), center=mp.Vector3(source_shift, 0, 0), #amp_func=lambda r: Gauss(r, params) amp_func=lambda r: psi(r, shift, params) ) ] sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, default_material=default_material, Courant=Courant, geometry=geometry, sources=sources, resolution=resolution, force_complex_fields=force_complex_fields, eps_averaging=eps_averaging, filename_prefix=filename_prefix ) sim.use_output_directory(interface) # put output files in a separate folder def eSquared(r, ex, ey, ez): """Calculate |E|^2. With |.| denoting the complex modulus if 'force_complex_fields?' is set to true, otherwise |.| gives the Euclidean norm. """ return mp.Vector3(ex, ey, ez).norm()**2 def output_efield2(sim): """Output E-field intensity.""" name = "e2_s" if s_pol else "e2_p" func = eSquared cs = [mp.Ex, mp.Ey, mp.Ez] return sim.output_field_function(name, cs, func, real_only=True) sim.run(mp.at_beginning(mp.output_epsilon), mp.at_end(mp.output_efield_z if s_pol else mp.output_efield_y), mp.at_end(output_efield2), until=runtime) print("\nend time:", datetime.now())
def main(): # Prefix all output files with the command line argument file_prefix = sys.argv[1] # Number of pixels per micron resolution = 150 # Simulation volume (um) cell_x = 2 cell_y = 2 cell_z = 2.5 # Refractive indicies index_si = 3.6 # previously 3.4467 index_sio2 = 1.444 # Durations in units of micron/c duration = round(1.5 * cell_x + 2) num_timesteps = duration * resolution # Absorbing layer on boundary pml = 0.5 # Geometry src_buffer = pml / 16 nbn_buffer = src_buffer nbn_length = cell_x - 2 * pml - src_buffer - nbn_buffer nbn_center_x = (src_buffer + nbn_buffer) / 2 wavelength = 1.55 waveguide_width = 0.750 # 750 nm waveguide_height = 0.110 # 110 nm plane_shift_y = 0 # nbn is 10/8 times thicker than in reality to have enough simulation pixels # so we reduce its absorption by a factor of 5/4 to compensate nbn_thickness_comp = 250 / resolution nbn_thickness = 0.008 * nbn_thickness_comp # Actually 8 nm, but simulating this for 2 grid points nbn_width = 0.100 # 100 nm nbn_spacing = 0.120 # 120 nm # Also compensate the difference in index by the same amount nbn_base_index = 5.23 # Taken from Hu thesis p86 nbn_index = (5.23 - index_si) / nbn_thickness_comp + index_si nbn_base_k = 5.82 # Taken from Hu thesis p86 nbn_k = nbn_base_k / nbn_thickness_comp conductivity = 2 * math.pi * wavelength * nbn_k / nbn_index flux_length = cell_x - 2 * pml - 4 * src_buffer # Generate simulation obejcts cell = mp.Vector3(cell_x, cell_y, cell_z) freq = 1 / wavelength src_pt = mp.Vector3(-cell_x / 2 + pml + src_buffer, 0, 0) output_slice = mp.Volume(center=mp.Vector3(y=(3 * waveguide_height / 4) + plane_shift_y), size=(cell_x, 0, cell_z)) # Log important quantities print('ABSORBING RUN') print('File prefix: {}'.format(file_prefix)) print('Duration: {}'.format(duration)) print('Resolution: {}'.format(resolution)) print('Dimensions: {} um, {} um, {} um'.format(cell_x, cell_y, cell_z)) print('Wavelength: {} um'.format(wavelength)) print('Si thickness: {} um'.format(waveguide_height)) print('NbN thickness: {} um'.format(nbn_thickness)) print('Si index: {}; SiO2 index: {}'.format(index_si, index_sio2)) print('Absorber dimensions: {} um, {} um, {} um'.format( nbn_length, nbn_thickness, nbn_width)) print('Absorber n (base value): {} ({}), k: {} ({})'.format( nbn_index, nbn_base_index, nbn_k, nbn_base_k)) print('Absorber compensation for thickness: {}'.format(nbn_thickness_comp)) print('Flux length: {} um'.format(flux_length)) print('\n\n**********\n\n') default_material = mp.Medium(epsilon=1) # Physical geometry of the simulation geometry = [ mp.Block(mp.Vector3(mp.inf, cell_y, mp.inf), center=mp.Vector3(0, -cell_y / 2 + plane_shift_y, 0), material=mp.Medium(epsilon=index_sio2)), mp.Block(mp.Vector3(mp.inf, waveguide_height, waveguide_width), center=mp.Vector3(0, waveguide_height / 2 + plane_shift_y, 0), material=mp.Medium(epsilon=index_si)) ] # Absorber will only be appended to geometry for the second simulation absorber = [ mp.Block(mp.Vector3(nbn_length, nbn_thickness, nbn_width), center=mp.Vector3( nbn_center_x, waveguide_height + nbn_thickness / nbn_thickness_comp / 2 + plane_shift_y, nbn_spacing / 2), material=mp.Medium(epsilon=nbn_index, D_conductivity=conductivity)), mp.Block(mp.Vector3(nbn_length, nbn_thickness, nbn_width), center=mp.Vector3( nbn_center_x, waveguide_height + nbn_thickness / nbn_thickness_comp / 2 + plane_shift_y, -nbn_spacing / 2), material=mp.Medium(epsilon=nbn_index, D_conductivity=conductivity)), ] # geometry += absorber # Calculate eigenmode source src_max_y = cell_y - 2 * pml src_max_z = cell_z - 2 * pml src_y = src_max_y src_z = src_max_z # min(3 * waveguide_width, src_max_z) src_center_y = 0 # plane_shift_y sources = [ mp.EigenModeSource(src=mp.ContinuousSource(frequency=freq), center=mp.Vector3(-cell_x / 2 + pml + src_buffer, src_center_y, 0), size=mp.Vector3(0, src_y, src_z), eig_match_freq=True, eig_parity=mp.ODD_Z, eig_band=1) ] pml_layers = [mp.PML(pml)] # Pass all simulation parameters to meep sim = mp.Simulation( cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution, # eps_averaging=False, default_material=default_material, symmetries=[mp.Mirror(mp.Z, phase=-1)]) # Create flux monitors to calculate transmission and absorption fr_y = cell_y - 2 * pml fr_z = cell_z - 2 * pml # Reflected flux refl_fr = mp.FluxRegion(center=mp.Vector3( -0.5 * cell_x + pml + 2 * src_buffer, 0, 0), size=mp.Vector3(0, fr_y, fr_z)) refl = sim.add_flux(freq, 0, 1, refl_fr) # Transmitted flux tran_fr = mp.FluxRegion(center=mp.Vector3( 0.5 * cell_x - pml - 2 * src_buffer, 0, 0), size=mp.Vector3(0, fr_y, fr_z)) tran = sim.add_flux(freq, 0, 1, tran_fr) # Run simulation, outputting the epsilon distribution and the fields in the # x-y plane every 0.25 microns/c sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended( "ez_z0", mp.in_volume(output_slice, mp.at_every(2 / resolution, mp.output_efield_z))), until=duration) print('\n\n**********\n\n') sim.fields.synchronize_magnetic_fields() # For normalization run, save flux fields data for reflection plane no_absorber_refl_data = sim.get_flux_data(refl) # Save incident power for transmission plane no_absorber_tran_flux = mp.get_fluxes(tran) print("Flux: {}".format(no_absorber_tran_flux[0])) eps_data = sim.get_array(center=mp.Vector3(z=(nbn_spacing + nbn_width) / 2), size=mp.Vector3(cell_x, cell_y, 0), component=mp.Dielectric) eps_cross_data = sim.get_array(center=mp.Vector3(x=cell_x / 4), size=mp.Vector3(0, cell_y, cell_z), component=mp.Dielectric) max_field = 1.5 # Plot epsilon distribution if mp.am_master(): plt.figure() plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') plt.axis('off') plt.savefig(file_prefix + '_Eps_A.png', dpi=300) print('Saved ' + file_prefix + '_Eps_A.png') # Plot field on x-y plane ez_data = sim.get_array(center=mp.Vector3(), size=mp.Vector3(cell_x, cell_y, 0), component=mp.Ez) if mp.am_master(): plt.figure() plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') plt.imshow(ez_data.transpose(), interpolation='spline36', cmap='RdBu', alpha=0.9) plt.axis('off') plt.savefig(file_prefix + '_Ez_A.png', dpi=300) print('Saved ' + file_prefix + '_Ez_A.png') energy_side_data = sim.get_array(center=mp.Vector3(), size=mp.Vector3(cell_x, cell_y, 0), component=mp.EnergyDensity) if mp.am_master(): plt.figure() plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') plt.imshow(energy_side_data.transpose(), interpolation='spline36', cmap='hot', alpha=0.9) plt.axis('off') plt.savefig(file_prefix + '_Pwr0_A.png', dpi=300) print('Saved ' + file_prefix + '_Pwr0_A.png') # Plot energy density on y-z plane energy_data = sim.get_array(center=mp.Vector3(), size=mp.Vector3(0, cell_y, cell_z), component=mp.EnergyDensity) if mp.am_master(): plt.figure() plt.imshow(eps_cross_data, interpolation='spline36', cmap='binary') plt.imshow(energy_data, interpolation='spline36', cmap='hot', alpha=0.9) plt.axis('off') plt.savefig(file_prefix + '_Pwr1_A.png', dpi=300) print('Saved ' + file_prefix + '_Pwr1_A.png') energy_data = sim.get_array(center=mp.Vector3(x=cell_x / 4), size=mp.Vector3(0, cell_y, cell_z), component=mp.EnergyDensity) if mp.am_master(): plt.figure() plt.imshow(eps_cross_data, interpolation='spline36', cmap='binary') plt.imshow(energy_data, interpolation='spline36', cmap='hot', alpha=0.9) plt.axis('off') plt.savefig(file_prefix + '_Pwr2_A.png', dpi=300) print('Saved ' + file_prefix + '_Pwr2_A.png') # Plot cross-sectional fields at several locations to ensure seeing nonzero fields num_x = 4 num_y = 3 fig, ax = plt.subplots(num_x, num_y) fig.suptitle('Cross Sectional Ez Fields') for i in range(num_x * num_y): monitor_x = i * (cell_x / 4) / (num_x * num_y) ez_cross_data = sim.get_array(center=mp.Vector3(x=monitor_x), size=mp.Vector3(0, cell_y, cell_z), component=mp.Ez) ax_num = i // num_y, i % num_y if mp.am_master(): ax[ax_num].imshow(eps_cross_data, interpolation='spline36', cmap='binary') ax[ax_num].imshow(ez_cross_data, interpolation='spline36', cmap='RdBu', alpha=0.9, norm=ZeroNormalize(vmax=np.max(max_field))) ax[ax_num].axis('off') ax[ax_num].set_title('x = {}'.format( round(cell_x / 4 + i / resolution, 3))) if mp.am_master(): plt.savefig(file_prefix + '_Ez_CS_A.png', dpi=300) print('Saved ' + file_prefix + '_Ez_CS_A.png') fig_e, ax_e = plt.subplots(num_x, num_y) fig_e.suptitle('Cross Sectional Energy Density') for i in range(num_x * num_y): monitor_x = i * (cell_x / 4) / (num_x * num_y) energy_cross_data = sim.get_array(center=mp.Vector3(x=monitor_x), size=mp.Vector3(0, cell_y, cell_z), component=mp.EnergyDensity) ax_num = i // num_y, i % num_y if mp.am_master(): ax_e[ax_num].imshow(eps_cross_data, interpolation='spline36', cmap='binary') ax_e[ax_num].imshow(energy_cross_data, interpolation='spline36', cmap='hot', alpha=0.9) ax_e[ax_num].axis('off') ax_e[ax_num].set_title('x = {}'.format( round(cell_x / 4 + i / resolution, 3))) if mp.am_master(): plt.savefig(file_prefix + '_Pwr_CS_A.png', dpi=300) print('Saved ' + file_prefix + '_Pwr_CS_A.png') print('\n\n**********\n\n') # Reset simulation for absorption run """ sim.reset_meep() geometry += absorber sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution, eps_averaging=False, default_material=default_material, symmetries=[mp.Mirror(mp.Z, phase=-1)]) refl = sim.add_flux(freq, 0, 1, refl_fr) tran = sim.add_flux(freq, 0, 1, tran_fr) sim.load_minus_flux_data(refl, no_absorber_refl_data) # Run simulation with absorber sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("ez_z0", mp.in_volume(output_slice, mp.at_every(0.25, mp.output_efield_z))), until=duration) print('\n\n**********\n\n') # Calculate transmission and absorption absorber_refl_flux = mp.get_fluxes(refl) absorber_tran_flux = mp.get_fluxes(tran) transmittance = absorber_tran_flux[0] / no_absorber_tran_flux[0] reflectance = absorber_refl_flux[0] / no_absorber_tran_flux[0] absorption = 1 - transmittance penetration_depth = - nbn_length / math.log(transmittance) print('Flux: {}'.format(absorber_tran_flux[0])) print("Transmittance: %f" % transmittance) print("Reflectance: %f" % reflectance) print("Absorption: {} over {} um".format(absorption, nbn_length)) print("lambda = {} mm".format(penetration_depth / 1000)) eps_data = sim.get_array(center=mp.Vector3(z=(nbn_spacing + nbn_width) / 2), size=mp.Vector3(cell_x, cell_y, 0), component=mp.Dielectric) max_field = 1 # Plot epsilon distribution with absorber if mp.am_master(): plt.figure() plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') plt.axis('off') plt.savefig(file_prefix + '_Eps_B.png', dpi=300) print('Saved ' + file_prefix + '_Eps_B.png') # Plot fields in x-y plane with absorber ez_data = sim.get_array(center=mp.Vector3(), size=mp.Vector3(cell_x, cell_y, 0), component=mp.Ez) if mp.am_master(): plt.figure() plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') plt.imshow(ez_data.transpose(), interpolation='spline36', cmap='RdBu', alpha=0.9) plt.axis('off') plt.savefig(file_prefix + '_Ez_B.png', dpi=300) print('Saved ' + file_prefix + '_Ez_B.png') # Plot field cross sections with absorber eps_cross_data = sim.get_array(center=mp.Vector3(x=cell_x/4), size=mp.Vector3(0, cell_y, cell_z), component=mp.Dielectric) num_x = 4 num_y = 3 fig, ax = plt.subplots(num_x, num_y) fig.suptitle('Cross Sectional Ez Fields') for i in range(num_x * num_y): monitor_x = cell_x/4 + i / resolution ez_cross_data = sim.get_array(center=mp.Vector3(x=monitor_x), size=mp.Vector3(0, cell_y, cell_z), component=mp.Ez) ax_num = i // num_y, i % num_y if mp.am_master(): ax[ax_num].imshow(eps_cross_data, interpolation='spline36', cmap='binary') ax[ax_num].imshow(ez_cross_data, interpolation='spline36', cmap='RdBu', alpha=0.9, norm=ZeroNormalize(vmax=np.max(max_field))) ax[ax_num].axis('off') ax[ax_num].set_title('x = {}'.format(round(cell_x/4 + i / resolution, 3))) if mp.am_master(): plt.savefig(file_prefix + '_Ez_CS_B.png', dpi=300) print('Saved ' + file_prefix + '_Ez_CS_B.png') print('\n\n**********\n\n') """ print('Program finished.')
def main(args): resolution = 30 nSiO2 = 1.4 SiO2 = mp.Medium(index=nSiO2) # conversion factor for eV to 1/um eV_um_scale = 1 / 1.23984193 # W, from Rakic et al., Applied Optics, vol. 32, p. 5274 (1998) W_eps_inf = 1 W_plasma_frq = 13.22 * eV_um_scale W_f0 = 0.206 W_frq0 = 1e-10 W_gam0 = 0.064 * eV_um_scale W_sig0 = W_f0 * W_plasma_frq**2 / W_frq0**2 W_f1 = 0.054 W_frq1 = 1.004 * eV_um_scale # 1.235 um W_gam1 = 0.530 * eV_um_scale W_sig1 = W_f1 * W_plasma_frq**2 / W_frq1**2 W_f2 = 0.166 W_frq2 = 1.917 * eV_um_scale # 0.647 W_gam2 = 1.281 * eV_um_scale W_sig2 = W_f2 * W_plasma_frq**2 / W_frq2**2 W_susc = [ mp.DrudeSusceptibility(frequency=W_frq0, gamma=W_gam0, sigma=W_sig0), mp.LorentzianSusceptibility(frequency=W_frq1, gamma=W_gam1, sigma=W_sig1), mp.LorentzianSusceptibility(frequency=W_frq2, gamma=W_gam2, sigma=W_sig2) ] W = mp.Medium(epsilon=W_eps_inf, E_susceptibilities=W_susc) # crystalline Si, from M.A. Green, Solar Energy Materials and Solar Cells, vol. 92, pp. 1305-1310 (2008) # fitted Lorentzian parameters, only for 600nm-1100nm Si_eps_inf = 9.14 Si_eps_imag = -0.0334 Si_eps_imag_frq = 1 / 1.55 Si_frq = 2.2384 Si_gam = 4.3645e-02 Si_sig = 14.797 / Si_frq**2 Si = mp.Medium(epsilon=Si_eps_inf, D_conductivity=2 * math.pi * Si_eps_imag_frq * Si_eps_imag / Si_eps_inf, E_susceptibilities=[ mp.LorentzianSusceptibility(frequency=Si_frq, gamma=Si_gam, sigma=Si_sig) ]) a = args.a # lattice periodicity cone_r = args.cone_r # cone radius cone_h = args.cone_h # cone height wire_w = args.wire_w # metal-grid wire width wire_h = args.wire_h # metal-grid wire height trench_w = args.trench_w # trench width trench_h = args.trench_h # trench height Np = args.Np # number of periods in supercell dair = 1.0 # air gap thickness dmcl = 1.7 # micro lens thickness dsub = 3000.0 # substrate thickness dpml = 1.0 # PML thickness sxy = Np * a sz = dpml + dair + dmcl + dsub + dpml cell_size = mp.Vector3(sxy, sxy, sz) boundary_layers = [ mp.PML(dpml, direction=mp.Z, side=mp.High), mp.Absorber(dpml, direction=mp.Z, side=mp.Low) ] geometry = [] if args.substrate: geometry = [ mp.Sphere(material=SiO2, radius=dmcl, center=mp.Vector3(0, 0, 0.5 * sz - dpml - dair - dmcl)), mp.Block(material=Si, size=mp.Vector3(mp.inf, mp.inf, dsub + dpml), center=mp.Vector3(0, 0, -0.5 * sz + 0.5 * (dsub + dpml))), mp.Block( material=W, size=mp.Vector3(mp.inf, wire_w, wire_h), center=mp.Vector3(0, -0.5 * sxy + 0.5 * wire_w, -0.5 * sz + dpml + dsub + 0.5 * wire_h)), mp.Block( material=W, size=mp.Vector3(mp.inf, wire_w, wire_h), center=mp.Vector3(0, +0.5 * sxy - 0.5 * wire_w, -0.5 * sz + dpml + dsub + 0.5 * wire_h)), mp.Block( material=W, size=mp.Vector3(wire_w, mp.inf, wire_h), center=mp.Vector3(-0.5 * sxy + 0.5 * wire_w, 0, -0.5 * sz + dpml + dsub + 0.5 * wire_h)), mp.Block(material=W, size=mp.Vector3(wire_w, mp.inf, wire_h), center=mp.Vector3(+0.5 * sxy - 0.5 * wire_w, 0, -0.5 * sz + dpml + dsub + 0.5 * wire_h)) ] if args.substrate and args.texture: for nx in range(Np): for ny in range(Np): cx = -0.5 * sxy + (nx + 0.5) * a cy = -0.5 * sxy + (ny + 0.5) * a geometry.append( mp.Cone(material=SiO2, radius=0, radius2=cone_r, height=cone_h, center=mp.Vector3( cx, cy, 0.5 * sz - dpml - dair - dmcl - 0.5 * cone_h))) if args.substrate: geometry.append( mp.Block(material=SiO2, size=mp.Vector3(mp.inf, trench_w, trench_h), center=mp.Vector3( 0, -0.5 * sxy + 0.5 * trench_w, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) geometry.append( mp.Block(material=SiO2, size=mp.Vector3(mp.inf, trench_w, trench_h), center=mp.Vector3( 0, +0.5 * sxy - 0.5 * trench_w, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) geometry.append( mp.Block(material=SiO2, size=mp.Vector3(trench_w, mp.inf, trench_h), center=mp.Vector3( -0.5 * sxy + 0.5 * trench_w, 0, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) geometry.append( mp.Block(material=SiO2, size=mp.Vector3(trench_w, mp.inf, trench_h), center=mp.Vector3( +0.5 * sxy - 0.5 * trench_w, 0, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) k_point = mp.Vector3(0, 0, 0) lambda_min = 0.7 # minimum source wavelength lambda_max = 1.0 # maximum source wavelength fmin = 1 / lambda_max fmax = 1 / lambda_min fcen = 0.5 * (fmin + fmax) df = fmax - fmin sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ex, center=mp.Vector3(0, 0, 0.5 * sz - dpml - 0.5 * dair), size=mp.Vector3(sxy, sxy, 0)) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, dimensions=3, k_point=k_point, sources=sources) nfreq = 50 refl = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, 0.5 * sz - dpml), size=mp.Vector3(sxy, sxy, 0))) trans_grid = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.5 * sz + dpml + dsub + wire_h), size=mp.Vector3(sxy, sxy, 0))) trans_sub_top = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.5 * sz + dpml + dsub), size=mp.Vector3(sxy, sxy, 0))) trans_sub_bot = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.5 * sz + dpml), size=mp.Vector3(sxy, sxy, 0))) sim.run(mp.at_beginning(mp.output_epsilon), until=0) if args.substrate: sim.load_minus_flux('refl-flux', refl) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ex, mp.Vector3(0, 0, -0.5 * sz + dpml + 0.5 * dsub), 1e-9)) if not args.substrate: sim.save_flux('refl-flux', refl) sim.display_fluxes(refl, trans_grid, trans_sub_top, trans_sub_bot)
def main(args): sx = 8.0 #spatial extent along x including pmls (μm) sy = 4 sz = 0 dpml = 1.0 cell = mp.Vector3(sx, sy, sz) pml_layers = [mp.PML(dpml)] resolution = args.res wvl = args.wvl #source wavelength fcen = 1 / wvl #center frequency df = 0 #frequency bandwidth nfreq = 1 #number of frequencies dw = args.dw # slab thickness increment(um) w_init = args.w_init # initial slab thickness(μm) n = args.n w = w_init + n * dw #next slab thicknessw slab_center_y = 0.5 * w #slab center y coordinate sfor_y = w + 0.005 #sfor_y = y coordinate of the slit_flux_output_region s = 0.030 #slit width print("wavelength =", wvl, "um") print("slab size =", w, "um") print("center frequency =", fcen, "1/um") print("slit_flux_output_region y coordinate =", sfor_y, "um") print("slab center y coordinate =", slab_center_y, "um") geometry = [ mp.Block(mp.Vector3(mp.inf, w, mp.inf), center=mp.Vector3(0, slab_center_y), material=Ag) ] source = [ mp.Source(mp.GaussianSource(fcen, df, nfreq), component=mp.Ex, center=mp.Vector3(0, -0.5 * (sy - 2.1), 0), size=mp.Vector3(sx - 2.0, 0, 0)) ] symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)] sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=source, resolution=resolution, boundary_layers=pml_layers, filename_prefix='slab_noslit') sim.use_output_directory() pt = mp.Vector3( 0, -0.5 * (sy - 2.1), 0 ) #point where field intensity is measured (usually at source position) slab_flux_in_region = mp.FluxRegion( center=mp.Vector3(0, -0.005, 0), size=mp.Vector3(sx, 0, 0)) #incident flux monitor 5nm above slab. slab_flux_in = sim.add_flux(fcen, df, nfreq, slab_flux_in_region) slab_flux_in_data = sim.get_flux_data( slab_flux_in) #data are the E and H fields used to calculate S=ExH slab_flux_out_region = mp.FluxRegion( center=mp.Vector3(0, sfor_y, 0), size=mp.Vector3(sx, 0, 0)) #trans flux monitor 5nm below slab. slab_flux_out = sim.add_flux(fcen, df, nfreq, slab_flux_out_region) slab_flux_out_data = sim.get_flux_data(slab_flux_out) sim.run(mp.dft_ldos(fcen, df, nfreq), mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 10, mp.Hz, pt, 1e-5)) slab_flux_input = [] slab_flux_output = [] slab_flux_input = mp.get_fluxes(slab_flux_in) slab_flux_output = mp.get_fluxes(slab_flux_out) print('incident flux slab =', slab_flux_input, ' ', 'transmitted flux slab =', slab_flux_output, ' ', 'normed transmitted flux=', np.divide(slab_flux_output, slab_flux_input)) eps_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Dielectric) ex_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Ex) hz_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Hz) sim.reset_meep() geometry = [ mp.Block(mp.Vector3(mp.inf, w, mp.inf), center=mp.Vector3(0, 0.5 * (w)), material=Ag), mp.Block(mp.Vector3(s, w, 0), center=mp.Vector3(0, 0.5 * (w), 0), material=mp.Medium(epsilon=1)) ] sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=source, resolution=resolution, boundary_layers=pml_layers, filename_prefix="slab_slit") sim.use_output_directory() slit_flux_in_region = mp.FluxRegion(center=mp.Vector3(0, -0.005, 0), size=mp.Vector3( sx, 0, 0)) #same as slab_flux_in_region slit_flux_in = sim.add_flux(fcen, df, nfreq, slit_flux_in_region) slit_flux_in_data = sim.get_flux_data(slit_flux_in) slit_flux_out_region = mp.FluxRegion(center=mp.Vector3(0, w + 0.005, 0), size=mp.Vector3( sx, 0, 0)) #same as slab_flux_out_region slit_flux_out = sim.add_flux(fcen, df, nfreq, slit_flux_out_region) slit_flux_out_data = sim.get_flux_data(slit_flux_out) sim.load_minus_flux_data(slit_flux_out, slab_flux_out_data) #subract transmitted slab flux data (slab_flux_out_data) from slit_flux_out. This is a correction so that flux only #from the slit is registered in slit_flux_out. sim.run(mp.dft_ldos(fcen, 0, nfreq), mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 10, mp.Hz, pt, 1e-4)) slit_flux_input = [] slit_flux_output = [] slit_flux_input = mp.get_fluxes(slit_flux_in) slit_flux_output = mp.get_fluxes(slit_flux_out) print('slit flux net =', slit_flux_output, ' ', 'slit_flux_normed =', np.divide(slit_flux_output, slab_flux_input)) eps_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Dielectric) ex_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Ex) hz_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Hz) slabFlux = [] slabFlux = np.append(slabFlux, slab_flux_input) slitFlux = [] slitFlux = np.append(slitFlux, slit_flux_output) fracFlux = np.divide(slitFlux, slabFlux) print('normalised slit flux,', end=" ") #suppress the print newline function with end=" " np.savetxt( sys.stdout, fracFlux, fmt='%7.5f' ) #print to stdout transmitted slit flux normalised to input flux. print('slab thickness,', end=" ") print(w) #-------------------------------- Next three lines ok for data file in mp meep, but writes data np times (np no. processors) in pmp. #f=open('fracFL.dat','ab') #np.savetxt(f,fracFl,fmt='%6.5f') #f.close() #---------------------------------------------------- eps_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Dielectric) hz_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Hz) hz_scat = hz_data_slit - hz_data_slab
def get_step_funcs(self, dt): sf = (mp.at_beginning(self.get_meep_output_comp(f)) for f in self.dynamic_fields) df = (mp.at_every(dt, self.get_meep_output_comp(f)) for f in self.static_fields) return (*sf, *df)
def main(args): """ Args: * **fields** (boolean): If true, outputs the fields at the relevant waveguide cross-sections (top-down and side-view) * **output_directory** (string): Name of the output directory (for storing the fields) * **eps_input_file** (string): Name of the hdf5 file that defines the geometry through prisms * **input_pol** (string): Either "TE", or "TM", corresponding to the desired input mode. Defaults to "TE" * **res** (int): Resolution of the MEEP simulation * **nfreq** (int): The number of wavelength points to record in the transmission/reflection spectra * **input_direction** (1 or -1): Direction of propagation for the input eigenmode. If +1, goes in +x, else if -1, goes in -x. Defaults to +1. * **dpml** (float): Length (in microns) of the perfectly-matched layer (PML) at simulation boundaries. Defaults to 0.5 um. * **wl_center** (float): Center wavelength (in microns) * **wl_span** (float): Wavelength span (determines the pulse width) * **port_vcenter** (float): Vertical center of the waveguide * **port_height** (float): Height of the port cross-section (flux plane) * **port_width** (float): Width of the port cross-section (flux plane) * **source_offset** (float): Offset (in x-direction) between reflection monitor and source. Defaults to 0.1 um. * **center_x** (float): x-coordinate of the center of the simulation region * **center_y** (float): y-coordinate of the center of the simulation region * **center_z** (float): z-coordinate of the center of the simulation region * **sx** (float): Size of the simulation region in x-direction * **sx** (float): Size of the simulation region in y-direction * **sz** (float): Size of the simulation region in z-direction * **port_coords** (list): List of the port coordinates (variable length), in the format [x1, y1, x2, y2, x3, y3, ...] (*must* be even) """ #Boolean inputs fields = args.fields #String inputs output_directory = args.output_directory eps_input_file = args.eps_input_file input_pol = args.input_pol #Int inputs res = args.res nfreq = args.nfreq input_direction = args.input_direction #Float inputs dpml = args.dpml wl_center = args.wl_center wl_span = args.wl_span port_vcenter = args.port_vcenter port_height = args.port_height port_width = args.port_width source_offset = args.source_offset center_x, center_y, center_z = args.center_x, args.center_y, args.center_z sx, sy, sz = args.sx, args.sy, args.sz #List of floats port_coords = [float(x) for x in args.port_coords[0].split(" ")] ports = [(port_coords[2 * i], port_coords[2 * i + 1]) for i in range(int(len(port_coords) / 2))] if input_pol == "TE": parity = mp.ODD_Z elif input_pol == "TM": parity = mp.EVEN_Z else: raise ValueError( "Warning! Improper value of 'input_pol' was passed to mcts.py (input_pol given =" + str(input_pol) + ")") if len(port_coords) % 2 != 0: raise ValueError( "Warning! Improper port_coords was passed to `meep_compute_transmission_spectra`. Must be even number of port_coords in [x1, y1, x2, y2, ..] format." ) # Setup the simulation geometries prism_objects = get_prism_objects(eps_input_file) geometry = [] for p in prism_objects: # print('vertices='+str(p['vlist'])) # print('axis = '+str(mp.Vector3(0,1,0))) # print('height = '+str(p['height'])) print('material = ' + str(p['eps'])) # print('\n') geometry.append( mp.Prism(p['vlist'], axis=mp.Vector3(0, 1, 0), height=p['height'], material=mp.Medium(epsilon=p['eps']))) # Setup the simulation sources fmax = 1.0 / (wl_center - 0.5 * wl_span) fmin = 1.0 / (wl_center + 0.5 * wl_span) fcen = (fmax + fmin) / 2.0 df = fmax - fmin if abs(abs(input_direction) - 1) > 1E-6: print(input_direction) raise ValueError("Warning! input_direction is not +1 or -1.") # Use first port in 'ports' as the location of the eigenmode source sources = [ mp.EigenModeSource( src=mp.GaussianSource(fcen, fwidth=df, cutoff=30), component=mp.ALL_COMPONENTS, size=mp.Vector3(0, 3 * float(port_height), 3 * float(port_width)), center=mp.Vector3(ports[0][0] + source_offset - center_x, float(port_vcenter) - center_y, ports[0][1] - center_z), eig_match_freq=True, eig_parity=parity, eig_kpoint=mp.Vector3(float(input_direction) * wl_center, 0, 0), eig_resolution=2 * res if res > 16 else 32, ) ] # Setup the simulation sim = mp.Simulation(cell_size=mp.Vector3(sx, sy, sz), boundary_layers=[mp.PML(dpml)], geometry=geometry, sources=sources, dimensions=3, resolution=res, filename_prefix=False) """ Add power flux monitors """ print("ADDING FLUX MONITORS") flux_plane_objects = [] for port in ports: flux_region = mp.FluxRegion(size=mp.Vector3(0, float(port_height), float(port_width)), center=mp.Vector3( float(port[0]) - center_x, float(port_vcenter) - center_y, float(port[1]) - center_z)) fpo = sim.add_flux(fcen, df, nfreq, flux_region) flux_plane_objects.append(fpo) sim.use_output_directory(str(output_directory)) """ Run the simulation """ """ Monitor the amplitude in the center of the structure """ decay_pt = mp.Vector3(0, port_vcenter, 0) sv = mp.Volume(size=mp.Vector3(sx, sy, 0), center=mp.Vector3(0, 0, 0)) tv = mp.Volume(size=mp.Vector3(sx, 0, sz), center=mp.Vector3(0, port_vcenter, 0)) print("RUNNING SIMULATION") if fields: sim.run(mp.at_beginning(mp.output_epsilon), mp.at_beginning( mp.with_prefix(str("sideview-"), mp.in_volume(sv, mp.output_epsilon))), mp.at_beginning( mp.with_prefix(str("topview-"), mp.in_volume(tv, mp.output_epsilon))), mp.at_every( 1.0, mp.to_appended(str("ez-sideview"), mp.in_volume(sv, mp.output_efield_z))), mp.at_every( 1.0, mp.to_appended(str("ez-topview"), mp.in_volume(tv, mp.output_efield_z))), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ez, decay_pt, 1e-4)) else: sim.run(until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ez, decay_pt, 1e-4)) sim.display_fluxes(*flux_plane_objects) print("FINISHED SIMULATION")
pml_layers = [mp.PML(1.0)] force_complex_fields = True # so we can get time-average flux resolution = 10 sim = mp.Simulation( cell_size=cell, geometry=geometry, sources=sources, boundary_layers=pml_layers, force_complex_fields=force_complex_fields, resolution=resolution ) sim.run( mp.at_beginning(mp.output_epsilon), mp.at_end(mp.output_png(mp.Ez, "-a yarg -A $EPS -S3 -Zc dkbluered", rm_h5=False)), until=200 ) flux1 = sim.flux_in_box(mp.X, mp.Volume(center=mp.Vector3(-6.0), size=mp.Vector3(1.8, 6))) flux2 = sim.flux_in_box(mp.X, mp.Volume(center=mp.Vector3(6.0), size=mp.Vector3(1.8, 6))) # averaged over y region of width 1.8 print("left-going flux = {}".format(flux1 / -1.8)) # averaged over y region of width 1.8 print("right-going flux = {}".format(flux2 / 1.8))
def notch(w): print("#----------------------------------------") print("NOTCH WIDTH: %s nanometers" % (w * 1000)) print("#----------------------------------------") # w is the width of the notch in the waveguide angrad = ang * pi / 180 bottomoffset = e * h / tan(angrad) vertices = [ mp.Vector3(w / 2 + bottomoffset, (.5 + e) * h), mp.Vector3(-w / 2 - bottomoffset, (.5 + e) * h), mp.Vector3(-w / 2 + bottomoffset, (.5 - e) * h), mp.Vector3(w / 2 - bottomoffset, (.5 - e) * h) ] if bottomoffset > w / 2: ratio = (w / 2) / bottomoffset vertices = [ mp.Vector3(w / 2, h / 2), mp.Vector3(-w / 2, h / 2), mp.Vector3(0, (.5 - e * ratio) * h) ] print(vertices) #Waveguide Geometry cell = mp.Vector3(a, H) geometry = [ mp.Block(cell, center=mp.Vector3(0, 0), material=default_material), mp.Block(mp.Vector3(a, hu + h / 2), center=mp.Vector3(0, (hu + h / 2) / 2), material=upper_material), mp.Block(mp.Vector3(a, hl + h / 2), center=mp.Vector3(0, -(hl + h / 2) / 2), material=lower_material), mp.Block(mp.Vector3(a, h), center=mp.Vector3(0, 0), material=core_material) ] if w > 0: geometry.append(mp.Prism(vertices, height=1, material=upper_material)) pml_layers = [mp.Absorber(thickness=dpml)] r00 = None r01 = None r10 = None r11 = None t00 = None t01 = None t10 = None t11 = None su0 = None sd0 = None su1 = None sd1 = None modes = [0, 1] if only_fund: modes = [0] # eig_parity_fund = mp.EVEN_Z+mp.EVEN_Y; # eig_parity_first = mp.EVEN_Z+mp.ODD_Y; eig_parity_fund = mp.EVEN_Y eig_parity_first = mp.ODD_Y # for mode in [0, 1]: for mode in modes: if mode == 0: eig_parity = eig_parity_fund # Fundamental print("-----------") print("MODE TYPE: FUNDAMENTAL") else: eig_parity = eig_parity_first # First Order print("-----------") print("MODE TYPE: FIRST ORDER") sources = [ mp.EigenModeSource(mp.ContinuousSource(frequency=fcen), size=mp.Vector3(0, H), center=mp.Vector3(Ls, 0), eig_parity=eig_parity) ] sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution, force_complex_fields=True) ''' #-------------------------------------------------- #FOR DISPLAYING THE GEOMETRY sim.run(until = 200) eps_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) plt.figure(dpi=100) plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') #plt.axis('off') plt.show() quit() #---------------------------------------------------- ''' ''' #------------------------------------------------------ #FOR GENERATING THE ELECTRIC FIELD GIF #Note: After running this program, write the following commands in Terminal: # $ source deactivate mp # $ cd notch-out/ # $ python ../NotchIP.py sim.use_output_directory() sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("ez", mp.at_every(0.6, mp.output_efield_z)), until = 200) #sim.run(mp.at_every(0.6 , mp.output_png(mp.Ez, "-Zc dkbluered")), until=200) #--------------------------------------------------------- ''' #--------------------------------------------------------- # FOR GENERATING THE TRANSMITTANCE SPECTRUM nfreq = 1 # number of frequencies at which to compute flux refl_fr1 = mp.FluxRegion(center=mp.Vector3(Lr1, 0), size=mp.Vector3( 0, monitorheight)) # Reflected flux 1 refl_fr2 = mp.FluxRegion(center=mp.Vector3(Lr2, 0), size=mp.Vector3( 0, monitorheight)) # Reflected flux 2 tran_fr = mp.FluxRegion(center=mp.Vector3(Lt, 0), size=mp.Vector3( 0, monitorheight)) # Transmitted flux su_fr = mp.FluxRegion(center=mp.Vector3(0, monitorheight / 2), size=mp.Vector3( a, 0)) # Flux loss above the waveguide sd_fr = mp.FluxRegion(center=mp.Vector3(0, -monitorheight / 2), size=mp.Vector3( a, 0)) # Flux loss below the waveguide # 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) # ------------------------ CODE FOR SEPARATING FUND AND FIRST ORDER MODE STARTS HERE ------------------------ refl_vals = [] tran_vals = [] def get_refl_slice(sim): # print(sim.get_array(center=mp.Vector3(Lr1,0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True)) # refl_val = sim.get_array(center=mp.Vector3(Lr1,0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True) refl_vals.append( sim.get_array(center=mp.Vector3(Lr1, 0), size=mp.Vector3(0, monitorheight - 2 / resolution), component=mp.Ez, cmplx=True)) def get_tran_slice(sim): # print(sim.get_array(center=mp.Vector3(Lt, 0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True)) # tran_val = sim.get_array(center=mp.Vector3(Lt, 0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True) tran_vals.append( sim.get_array(center=mp.Vector3(Lt, 0), size=mp.Vector3(0, monitorheight - 2 / resolution), component=mp.Ez, cmplx=True)) gif = True if gif: # and w == 0.1: sim.use_output_directory() sim.run(mp.at_beginning(mp.output_epsilon), mp.at_end(get_refl_slice), mp.at_end(get_tran_slice), until=100) 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) # sim.run(mp.at_every(wavelength / 20, mp.output_efield_z), until=wavelength) # sim.run(mp.at_every(wavelength/20 , mp.output_png(mp.Ez, "-RZc bluered -A notch-out/notch-eps-000000000.h5 -a gray:.2")), until=19*wavelength/20) sim.run(mp.at_every( wavelength / 20, mp.output_png( mp.Ez, "-RZc bluered -A notch-out/notch-eps-000000.00.h5 -a gray:.2" )), until=19 * wavelength / 20) sim.run(until=50) else: sim.run(mp.at_end(get_refl_slice), mp.at_end(get_tran_slice), until=100) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(), 1e-5)) os.system( "h5topng notch-out/notch-eps-000000.00.h5; mv notch-out/notch-eps-000000.00.png " + case + "-" + str(int(w * 1000)) + "-" + str(mode) + "-eps.png") os.system("cp notch-out/notch-ez-000100.00.png " + case + "-" + str(int(w * 1000)) + "-" + str(mode) + ".png") os.system("convert notch-out/notch-ez-*.png " + case + "-" + str(int(w * 1000)) + "-" + str(mode) + ".gif") # get_eigenmode(fcen, mp., refl_fr1, 1, kpoint) # v = mp.volume(mp.vec(Lr1, -monitorheight/2), mp.vec(Lr1, monitorheight/2)) # mode = get_eigenmode(fcen, mp.X, v, v, 1, mp.vec(0, 0, 0), True, 0, 0, 1e-7, True) # print(mode.amplitude) # coef_refl_fund = mp.get_eigenmode_coefficients_and_kpoints(refl_fr1, 1, eig_parity=eig_parity_fund, eig_resolution=resolution, eig_tolerance=1e-7) # coef_refl_first = mp.get_eigenmode_coefficients_and_kpoints(refl_fr1, 1, eig_parity=eig_parity_first, eig_resolution=resolution, eig_tolerance=1e-7) # # coef_tran_fund = mp.get_eigenmode_coefficients_and_kpoints(tran_fr, 1, eig_parity=eig_parity_fund, eig_resolution=resolution, eig_tolerance=1e-7) # coef_tran_first = mp.get_eigenmode_coefficients_and_kpoints(tran_fr, 1, eig_parity=eig_parity_first, eig_resolution=resolution, eig_tolerance=1e-7) ep = mp.ODD_Z #coef_refl_fund, vgrp, kpoints_fund = sim.get_eigenmode_coefficients(refl1, [1], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) #coef_refl_first, vgrp, kpoints_first = sim.get_eigenmode_coefficients(refl1, [2], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) #coef_tran_fund, vgrp, kpoints_fund = sim.get_eigenmode_coefficients(tran, [1], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) #coef_tran_first, vgrp, kpoints_first = sim.get_eigenmode_coefficients(tran, [2], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) # print(kpoints_fund) # print(kpoints_first) # print(kpoints_fund[0]) # print(type(kpoints_fund[0])) # print(dir(kpoints_fund[0])) # n_eff_fund = wavelength*kpoints_fund[0].x # n_eff_first = wavelength*kpoints_first[0].x n_eff_fund = neff n_eff_first = 1 print(n_eff_fund) print(n_eff_first) # print(coef_refl_fund) # print(type(coef_refl_fund)) # print(dir(coef_refl_fund)) # print(coef_refl_fund[0]) # print(coef_refl_fund[0][0,0,:]) # # fund_refl_amp = coef_refl_fund[0][0,0,1]; # first_order_refl_amp = coef_refl_first[0][0,0,1]; # fund_tran_amp = coef_tran_fund[0][0,0,0]; # first_order_tran_amp = coef_tran_first[0][0,0,0]; print("get_eigenmode_coefficients:\n") #print(coef_refl_fund) #print(coef_refl_first) #print(coef_tran_fund) #print(coef_tran_first) print("\n") # print(coef_refl_fund[0,0,:]) #fund_refl_amp = coef_refl_fund[0,0,1]; #first_order_refl_amp = coef_refl_first[0,0,1]; #fund_tran_amp = coef_tran_fund[0,0,0]; #first_order_tran_amp = coef_tran_first[0,0,0]; refl_val = refl_vals[0] tran_val = tran_vals[0] # n_eff must satisfy n_e <= n_eff <= n_c for the mode to be bound. def fund_func(n_eff): if n_eff >= n_c and n_eff <= n_e: return sqrt(n_eff**2 - n_c**2) - sqrt(n_e**2 - n_eff**2) * tan( pi * h / wavelength * sqrt(n_e**2 - n_eff**2)) def first_order_func(n_eff): if n_eff >= n_c and n_eff <= n_e: return sqrt(n_eff**2 - n_c**2) - sqrt(n_e**2 - n_eff**2) * tan( pi * h / wavelength * sqrt(n_e**2 - n_eff**2) - pi / 2) initial_guess = (n_c + n_e) / 2 # n_eff_fund = fsolve(fund_func, initial_guess) # n_eff_first = fsolve(first_order_func, n_c) print(n_eff_fund, n_eff_first) assert (n_eff_fund > n_eff_first) if len(n_eff_funds) == 0: n_eff_funds.append(n_eff_fund) if len(n_eff_firsts) == 0: n_eff_firsts.append(n_eff_first) ky0_fund = np.abs(2 * pi / wavelength * sqrt(n_e**2 - n_eff_fund**2)) ky0_first = np.abs(2 * pi / wavelength * sqrt(n_e**2 - n_eff_first**2)) ky1_fund = ky0_fund # np.abs(2 * pi / wavelength * sqrt(n_eff_fund **2 - n_c**2)) ky1_first = ky0_first # np.abs(2 * pi / wavelength * sqrt(n_eff_first**2 - n_c**2)) E_fund = lambda y: cos(ky0_fund * y) if np.abs(y) < h / 2 else cos( ky0_fund * h / 2) * np.exp(-ky1_fund * (np.abs(y) - h / 2)) E_first_order = lambda y: sin(ky0_first * y) if np.abs( y) < h / 2 else sin(ky0_first * h / 2) * np.exp(-ky1_first * ( np.abs(y) - h / 2)) * np.sign(y) # y_list = np.arange(-H/2+.5/resolution, H/2-.5/resolution, 1/resolution) #print("Y LIST: ", y_list) #print("SIZE OF Y LIST: ", y_list.size) E_fund_vec = np.zeros(y_list.size) E_first_order_vec = np.zeros(y_list.size) for index in range(y_list.size): y = y_list[index] E_fund_vec[index] = E_fund(y) E_first_order_vec[index] = E_first_order(y) # print(dir(sim)) # print(type(sim.get_eigenmode_coefficients)) # print(dir(sim.get_eigenmode_coefficients)) # print(type(sim.get_eigenmode)) # print(dir(sim.get_eigenmode)) # print(sim.get_eigenmode.__code__.co_varnames) # print(sim.get_eigenmode.__defaults__) # E1 = sim.get_eigenmode(fcen, mp.X, refl1.where, 1, None) # E2 = sim.get_eigenmode(fcen, mp.X, refl1.where, 2, None) # E3 = sim.get_eigenmode(fcen, mp.X, refl1.where, 3, None) # E4 = sim.get_eigenmode(fcen, mp.X, refl1.where, 4, None) # E1 = sim.get_eigenmode(fcen, mp.X, refl1.where, 1, mp.Vector3(0, 0, 0)) # E2 = sim.get_eigenmode(fcen, mp.X, refl1.where, 2, mp.Vector3(0, 0, 0)) # E3 = sim.get_eigenmode(fcen, mp.X, refl1.where, 3, mp.Vector3(0, 0, 0)) # E4 = sim.get_eigenmode(fcen, mp.X, refl1.where, 4, mp.Vector3(0, 0, 0)) # print(refl1.where) # numEA = 0 # E1 = sim.fields.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 1, mp.vec(0,0,0), True, 0, numEA, 1e-7, True) # print(type(E1)) # print(dir(E1)) # # print(refl1.where) # # print(E1, E2, E3, E4) # print(E1.amplitude, E1.band_num, E1.group_velocity, E1.k) # print(type(E1.amplitude)) # print(dir(E1.amplitude)) # print(doc(E1.amplitude)) # print(self(E1.amplitude)) # print(E1.amplitude(y_list)) # print(type(E1.amplitude(y_list))) # print(dir(E1.amplitude(y_list))) # print(E1.amplitude, E2.amplitude, E3.amplitude, E4.amplitude) # E1 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 1, mp.vec(0, 0, 0)) # E2 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 2, mp.vec(0, 0, 0)) # E3 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 3, mp.vec(0, 0, 0)) # E4 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 4, mp.vec(0, 0, 0)) # print(y_list) # # E1a = np.zeros(y_list.size, dtype='Complex128'); # E2a = np.zeros(y_list.size, dtype='Complex128'); # E3a = np.zeros(y_list.size, dtype='Complex128'); # E4a = np.zeros(y_list.size, dtype='Complex128'); # # # print(mp.eigenmode_amplitude.__code__.co_varnames) # # print(mp.eigenmode_amplitude.__defaults__) # # for i in range(y_list.size): # # print(E1) # # print(E1.swigobj) # # print(E1.amplitude) # # print(mp.vec(Lr1, y_list[i], 0)) # # print(mp.Vector3(Lr1, y_list[i], 0)) # # print(mp.Ez) # # E1a[i] = mp.eigenmode_amplitude(E1.swigobj, mp.vec(Lr1, y_list[i], 0), mp.Ez); # eval_point = mp.vec(Lr1, y_list[i], 0) # # print(eval_point) # E1a[i] = mp.eigenmode_amplitude(E1.swigobj, eval_point, mp.Ex) # E2a[i] = mp.eigenmode_amplitude(E2.swigobj, eval_point, mp.Ex) # E3a[i] = mp.eigenmode_amplitude(E3.swigobj, eval_point, mp.Ex) # E4a[i] = mp.eigenmode_amplitude(E4.swigobj, eval_point, mp.Ex) # # E1a[i] = mp.eigenmode_amplitude(E1.amplitude, mp.Vector3(Lr1, y_list[i], 0), mp.Ez); # # plt.plot(y_list, np.abs(E1a)**2, 'bo-', label='E1') # plt.plot(y_list, np.abs(E2a)**2, 'ro-', label='E2') # plt.plot(y_list, np.abs(E3a)**2, 'go-', label='E3') # plt.plot(y_list, np.abs(E4a)**2, 'co-', label='E4') # # plt.axis([40.0, 300.0, 0.0, 100.0]) # plt.xlabel("y (um)") # plt.ylabel("Field (a.u.)") # plt.legend(loc="center right") # plt.show() # print("r VECTOR: ", refl_val) # print("t VECTOR: ", tran_val) # print("E0 VECTOR: ", E_fund_vec) # print("E1 VECTOR: ", E_first_order_vec) # fund_refl_amp_2 = np.conj(np.dot(refl_val, E_fund_vec) / np.dot(E_fund_vec, E_fund_vec)) # Conjugate becasue MEEP uses physics exp(kz-wt) rather than engineering exp(wt-kz) # first_order_refl_amp_2 = np.conj(np.dot(refl_val, E_first_order_vec) / np.dot(E_first_order_vec, E_first_order_vec)) # fund_tran_amp_2 = np.conj(np.dot(tran_val, E_fund_vec) / np.dot(E_fund_vec, E_fund_vec)) # first_order_tran_amp_2 = np.conj(np.dot(tran_val, E_first_order_vec) / np.dot(E_first_order_vec, E_first_order_vec)) fund_refl_amp = np.conj( np.dot(refl_val, E0) / np.dot(E0, E0) ) # Conjugate becasue MEEP uses physics exp(kz-wt) rather than engineering exp(wt-kz) first_order_refl_amp = 0 # np.conj(np.dot(refl_val, E1[:,2]) / np.dot(E1[:,2], E1[:,2])) fund_tran_amp = np.conj(np.dot(tran_val, E0) / np.dot(E0, E0)) first_order_tran_amp = 0 # np.conj(np.dot(tran_val, E1[:,2]) / np.dot(E1[:,2], E1[:,2])) fund_refl = np.conj(fund_refl_amp) * E0 not_fund_refl = refl_val - fund_refl fund_tran = np.conj(fund_tran_amp) * E0 not_fund_tran = tran_val - fund_tran fund_refl_power = np.dot(np.conj(fund_refl), fund_refl) not_fund_refl_power = np.dot(np.conj(not_fund_refl), not_fund_refl) fund_tran_power = np.dot(np.conj(fund_tran), fund_tran) not_fund_tran_power = np.dot(np.conj(not_fund_tran), not_fund_tran) fund_refl_ratio = np.abs(fund_refl_power / (fund_refl_power + not_fund_refl_power)) first_order_refl_ratio = 0 fund_tran_ratio = np.abs(fund_tran_power / (fund_tran_power + not_fund_tran_power)) first_order_tran_ratio = 0 # plt.plot(y_list, np.abs(refl_val), 'bo-',label='reflectance') # plt.plot(y_list, np.abs(tran_val), 'ro-',label='transmittance') # plt.plot(y_list, E0, 'go-',label='E0') # plt.plot(y_list, fund_refl_amp*E0, 'co-',label='over') # # plt.axis([40.0, 300.0, 0.0, 100.0]) # plt.xlabel("y (um)") # plt.ylabel("Field") # plt.legend(loc="center right") # plt.show() # # print("\n") # # print(tran_val.size, refl_val.size) # # print("\n") # # print(tran_val, refl_val, E0) # # print("\n") # # print(np.conj(tran_val), tran_val, E1[:,2]) # # # # print(np.conj(refl_val), refl_val, E1[:,2]) # # refl_tot_power = np.abs(np.dot(np.conj(refl_val), refl_val)) # tran_tot_power = np.abs(np.dot(np.conj(tran_val), tran_val)) # # print(fund_refl_amp, refl_tot_power, fund_tran_amp, tran_tot_power) # print(fund_refl_amp , fund_refl_amp_2 ) # print(first_order_refl_amp , first_order_refl_amp_2) # print(fund_tran_amp , fund_tran_amp_2 ) # print(first_order_tran_amp , first_order_tran_amp_2) # # print(np.angle(fund_refl_amp), np.angle(fund_refl_amp_2)) # print(np.angle(first_order_refl_amp), np.angle(first_order_refl_amp_2)) # print(np.angle(fund_tran_amp), np.angle(fund_tran_amp_2)) # print(np.angle(first_order_tran_amp), np.angle(first_order_tran_amp_2)) # fund_refl_power = np.abs(fund_tran_amp) ** 2 # fund_refl_power = np.abs(fund_refl_amp) ** 2 # first_order_refl_power = np.abs(first_order_refl_amp) ** 2 # fund_tran_power = np.abs(fund_tran_amp) ** 2 # first_order_tran_power = np.abs(first_order_tran_amp) ** 2 # print(fund_refl_power, first_order_refl_power, fund_tran_power, first_order_tran_power) # fund_refl_ratio = fund_refl_power / (fund_refl_power + first_order_refl_power) # first_order_refl_ratio = first_order_refl_power / (fund_refl_power + first_order_refl_power) # fund_tran_ratio = fund_tran_power / (fund_tran_power + first_order_tran_power) # first_order_tran_ratio = first_order_tran_power / (fund_tran_power + first_order_tran_power) # fund_refl_power = np.abs(fund_refl_amp) ** 2 # first_order_refl_power = np.abs(first_order_refl_amp) ** 2 # fund_tran_power = np.abs(fund_tran_amp) ** 2 # first_order_tran_power = np.abs(first_order_tran_amp) ** 2 # # fund_refl_ratio = fund_refl_power / refl_tot_power # first_order_refl_ratio = first_order_refl_power / refl_tot_power # fund_tran_ratio = fund_tran_power / tran_tot_power # first_order_tran_ratio = first_order_tran_power / tran_tot_power # # fund_refl_ratio = 1 # first_order_refl_ratio = 0 # fund_tran_ratio = 1 # first_order_tran_ratio = 0 print("Percentage of reflected light in fundamental mode: ", fund_refl_ratio * 100) print("Percentage of reflected light in first order mode: ", first_order_refl_ratio * 100) print("Percentage of transmitted light in fundamental mode: ", fund_tran_ratio * 100) print("Percentage of transmitted light in first order mode: ", first_order_tran_ratio * 100) # ------------------------ CODE FOR SEPARATING FUND AND FIRST ORDER MODE ENDS HERE ------------------------ wl = [] #list of wavelengths refl1_flux = mp.get_fluxes(refl1) refl2_flux = mp.get_fluxes(refl2) tran_flux = mp.get_fluxes(tran) su_flux = mp.get_fluxes(su) sd_flux = mp.get_fluxes(sd) flux_freqs = mp.get_flux_freqs(refl1) for i in range(nfreq): wl = np.append(wl, 1 / flux_freqs[i]) print(1 / flux_freqs[i]) # for ind, elt in enumerate(wl): # #print(round(elt, 4)) # if round(elt, 3) == 0.637: # #print("ALERT: MATCH FOUND") # index = ind index = 0 rp = refl1_flux[index] tp = tran_flux[index] # print("rp/tp:\n") # print(rp) # print(tp) # print("\n") R = -refl1_flux[index] / (refl2_flux[index] - refl1_flux[index]) T = tran_flux[index] / (refl2_flux[index] - refl1_flux[index]) S = (refl2_flux[index] - tran_flux[index]) / (refl2_flux[index] - refl1_flux[index]) Su = su_flux[index] / (refl2_flux[index] - refl1_flux[index]) Sd = -sd_flux[index] / (refl2_flux[index] - refl1_flux[index]) S_correction = (1 - R - T) / (Su + Sd) # print(R, T, S, Su, Sd) r = sqrt(R) t = sqrt(T) # The amplitude ... times the phase ... accounting for the distance to the detector (Reverse exp(-kz) of phase). # r_fund = (r * fund_refl_ratio) * (fund_refl_amp / np.abs(fund_refl_amp)) * (np.exp( 2j*pi * (-Lr1 - w/2) * n_eff_fund / wavelength)) # Lr1 is negative because it is the (negative) position of the monitor, not the distace from the monitor to the center. # r_first = (r * first_order_refl_ratio) * (first_order_refl_amp / np.abs(first_order_refl_amp)) * (np.exp( 2j*pi * (-Lr1 - w/2) * n_eff_first / wavelength)) # t_fund = (t * fund_tran_ratio) * (fund_tran_amp / np.abs(fund_tran_amp)) * (np.exp( 2j*pi * ( Lt - w/2) * n_eff_fund / wavelength)) # t_first = (t * first_order_tran_ratio) * (first_order_tran_amp / np.abs(first_order_tran_amp)) * (np.exp( 2j*pi * ( Lt - w/2) * n_eff_first / wavelength)) r_fund = (r * fund_refl_ratio) * np.exp( 1j * np.angle(fund_refl_amp) + 2j * pi * (-Lr1 - w / 2) * n_eff_fund / wavelength ) # Lr1 is negative because it is the (negative) position of the monitor, not the distace from the monitor to the center. r_first = (r * first_order_refl_ratio ) * np.exp(1j * np.angle(first_order_refl_amp) + 2j * pi * (-Lr1 - w / 2) * n_eff_first / wavelength) t_fund = (t * fund_tran_ratio ) * np.exp(1j * np.angle(fund_tran_amp) + 2j * pi * (Lt - w / 2) * n_eff_fund / wavelength) t_first = (t * first_order_tran_ratio ) * np.exp(1j * np.angle(first_order_tran_amp) + 2j * pi * (Lt - w / 2) * n_eff_first / wavelength) if mode == 0: r00 = r_fund r01 = r_first t00 = t_fund t01 = t_first su0 = sqrt(np.abs(Su * S_correction)) sd0 = sqrt(np.abs(Sd * S_correction)) # su0 = sqrt(Su) # sd0 = sqrt(Sd) r00s.append(r00) r01s.append(r01) t00s.append(t00) t01s.append(t01) su0s.append(su0) sd0s.append(sd0) if only_fund: r10s.append(0) r11s.append(0) t10s.append(0) t11s.append(1) su1s.append(0) sd1s.append(0) else: r10 = r_fund r11 = r_first t10 = t_fund t11 = t_first su1 = sqrt(np.abs(Su * S_correction)) sd1 = sqrt(np.abs(Sd * S_correction)) # su1 = sqrt(Su) # sd1 = sqrt(Sd) r10s.append(r10) r11s.append(r11) t10s.append(t10) t11s.append(t11) su1s.append(su1) sd1s.append(sd1) norm_Su = S * Su / (Su + Sd) NET = round((R + T + S) * 100, 0) if NET > 100.0: NET = 100.0 NET_LOSS = round((Su + Sd) / S * 100, 0) if NET_LOSS > 100.0: NET_LOSS = 100.0 ''' np.append(ws, [w * 1000]) np.append(Rs, [R * 100]) np.append(Ts, [T * 100]) np.append(Ss, [S * 100]) np.append(NET_LIST, [NET]) np.append(Sus, [Su * 100]) np.append(Sds, [Sd * 100]) np.append(NET_LOSS_LIST, [NET_LOSS]) np.append(norm_Sus, [norm_Su * 100]) ''' if mode == 0: ws.append(w * 1000) Rs.append(R * 100) Ts.append(T * 100) Ss.append(S * 100) NET_LIST.append(NET) Sus.append(Su * 100) Sds.append(Sd * 100) NET_LOSS_LIST.append(NET_LOSS) norm_Sus.append(norm_Su * 100) if mode == 0: f1.write("--------------------------------------------------- \n") f1.write("Notch Width: %s nanometers \n" % (w * 1000)) f1.write("Reflection Percentage: %s\n" % (R * 100)) f1.write("Transmission Percentage: %s\n" % (T * 100)) f1.write("Total Loss Percentage: %s\n" % (S * 100)) f1.write("Percentage of Light Accounted For: %s\n" % (NET)) f1.write("Upper Loss Percentage: %s\n" % (Su * 100)) f1.write("Lower Loss Percentage: %s\n" % (Sd * 100)) f1.write("Percentage of Total Loss Accounted For: %s\n" % (NET_LOSS)) f1.write("Normalized Upper Loss Percentage: %s\n" % (norm_Su * 100)) f1.write("\n \n") f1.write("FUNDAMENTAL MODE \n") f1.write("n_eff: %s\n" % (n_eff_fund)) f1.write("Re(r00): %s\n" % (np.real(r00))) f1.write("Im(r00): %s\n" % (np.imag(r00))) f1.write("Re(r01): %s\n" % (np.real(r01))) f1.write("Im(r01): %s\n" % (np.imag(r01))) f1.write("Re(t00): %s\n" % (np.real(t00))) f1.write("Im(t00): %s\n" % (np.imag(t00))) f1.write("Re(t01): %s\n" % (np.real(t01))) f1.write("Im(t01): %s\n" % (np.imag(t01))) f1.write("Re(su0): %s\n" % (np.real(su0))) f1.write("Im(su0): %s\n" % (np.imag(su0))) f1.write("Re(sd0): %s\n" % (np.real(sd0))) f1.write("Im(sd0): %s\n" % (np.imag(sd0))) f1.write("\n") else: f1.write("FIRST ORDER MODE \n") f1.write("n_eff: %s\n" % (n_eff_first)) f1.write("Re(r10): %s\n" % (np.real(r10))) f1.write("Im(r10): %s\n" % (np.imag(r10))) f1.write("Re(r11): %s\n" % (np.real(r11))) f1.write("Im(r11): %s\n" % (np.imag(r11))) f1.write("Re(t10): %s\n" % (np.real(t10))) f1.write("Im(t10): %s\n" % (np.imag(t10))) f1.write("Re(t11): %s\n" % (np.real(t11))) f1.write("Im(t11): %s\n" % (np.imag(t11))) f1.write("Re(su1): %s\n" % (np.real(su1))) f1.write("Im(su1): %s\n" % (np.imag(su1))) f1.write("Re(sd1): %s\n" % (np.real(sd1))) f1.write("Im(sd1): %s\n" % (np.imag(sd1))) f1.write("--------------------------------------------------- \n") sim.reset_meep()
# dft_objx = [] for i in range(len(flux_freqsX)): dft_objx = np.append( dft_objx, sim.add_dft_fields([polarisation], flux_freqsX[i], flux_freqsX[i], 1, where=monitor)) dft_obj = sim.add_dft_fields([polarisation], fcen, fcen, 1, where=monitor) sim.use_output_directory('flux-out_1') sim.run(mp.in_volume(monitor, mp.at_beginning(mp.output_epsilon)), mp.in_volume( monitor, mp.to_appended("ez", mp.at_every(time_step, mp.output_efield_x))), until_after_sources=mp.stop_when_fields_decayed( add_time, polarisation, pt, decay)) ''' sim.run(until_after_sources=mp.stop_when_fields_decayed(add_time,polarisation,pt,decay)) ''' #save scattered reflected flux from the surfaces scat_refl_data_t = mp.get_fluxes(refl_t) scat_refl_data_b = mp.get_fluxes(refl_b) scat_refl_data_l = mp.get_fluxes(refl_l) scat_refl_data_r = mp.get_fluxes(refl_r)
sources = [ mp.Source(mp.ContinuousSource(wavelength=2 * (11 * 0.5), width=20), component=mp.Ez, center=mp.Vector3(-7, -3.5), size=mp.Vector3(0, 1)) ] resolution = 10 sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution) sim.run(mp.at_beginning(mp.output_epsilon), mp.at_beginning(mp.output_mu), mp.to_appended("ez", mp.at_every(0.6, mp.output_efield_z)), until=200) """Post-processing""" # plot the epsilon and mu values eps_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) mu_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Permeability) fig, axs = plt.subplots(2, 2) axs[0, 0].imshow(eps_data.transpose(), cmap='binary')
refl_fr = mp.FluxRegion(center=mp.Vector3((-0.5 * sx) + 1, 0),size=mp.Vector3(0, sy)) # reflected flux refl = sim.add_flux(fcen, df, nfreq, refl_fr) # for normal run, load negated fields to subtract incident from refl. fields if not args.no_ARC: sim.load_minus_flux('refl-flux', refl) if args.no_ARC: pt = mp.Vector3((T_Si / 2) - (T_Si / 100), 0) else: pt = mp.Vector3((T_Si / 2) - (T_Si / 100), 0) sim.run(mp.at_beginning(mp.output_epsilon),until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, pt, 1e-3)) # for normalization run, save flux fields for refl. plane if args.no_ARC: sim.save_flux('refl-flux', refl) sim.display_fluxes(trans, refl) def test(inputv): print(inputv) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-n', '--no_ARC', action='store_true', default=False, help="Straight waveguide without bend.") parser.add_argument('-v')