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 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 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_with_prefix(self): sim = self.init_simple_simulation() sim.use_output_directory(self.temp_dir) sim.run(mp.with_prefix('test_prefix-', mp.at_end(mp.output_efield_z)), until=200) fname = os.path.join(self.temp_dir, 'test_prefix-simulation-ez-000200.00.h5') self.assertTrue(os.path.exists(fname))
def test_geometry_center(self): resolution = 20 cell_size = mp.Vector3(10, 10) pml = [mp.PML(1)] center = mp.Vector3(2, -1) result = [] fcen = 0.15 df = 0.1 sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3()) ] geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 3, mp.inf), material=mp.Medium(epsilon=12)) ] def print_field(sim): result.append(sim.get_field_point(mp.Ez, mp.Vector3(2, -1))) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml, sources=sources, geometry=geometry, geometry_center=center) sim.run(mp.at_end(print_field), until=50) self.assertAlmostEqual(result[0], -0.0599602798684155)
def test_set_materials(self): def change_geom(sim): t = sim.meep_time() fn = t * 0.02 geom = [mp.Cylinder(radius=3, material=mp.Medium(index=3.5), center=mp.Vector3(fn, fn)), mp.Ellipsoid(size=mp.Vector3(1, 2, mp.inf), center=mp.Vector3(fn, fn))] sim.set_materials(geometry=geom) c = mp.Cylinder(radius=3, material=mp.Medium(index=3.5)) e = mp.Ellipsoid(size=mp.Vector3(1, 2, mp.inf)) sources = mp.Source(src=mp.GaussianSource(1, fwidth=0.1), component=mp.Hz, center=mp.Vector3()) 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=16) eps = {'arr1': None, 'arr2': None} def get_arr1(sim): eps['arr1'] = sim.get_array(mp.Dielectric, mp.Volume(mp.Vector3(), mp.Vector3(10, 10))) def get_arr2(sim): eps['arr2'] = sim.get_array(mp.Dielectric, mp.Volume(mp.Vector3(), mp.Vector3(10, 10))) sim.run(mp.at_time(50, get_arr1), mp.at_time(100, change_geom), mp.at_end(get_arr2), until=200) self.assertFalse(np.array_equal(eps['arr1'], eps['arr2']))
def test_use_output_directory_default(self): sim = self.init_simple_simulation() output_dir = os.path.join(self.temp_dir, 'simulation-out') sim.use_output_directory(output_dir) sim.run(mp.at_end(mp.output_efield_z), until=200) self.assertTrue(os.path.exists(os.path.join(output_dir, self.fname)))
def test_multilevel_atom(self): resolution = 20 ncav = 1.5 Lcav = 1 dpad = 1 dpml = 1 sz = Lcav + dpad + dpml cell_size = mp.Vector3(z=sz) dimensions = 1 pml_layers = [mp.PML(dpml, side=mp.High)] omega_a = 40 freq_21 = omega_a / (2 * math.pi) gamma_perp = 4 gamma_21 = (2 * gamma_perp) / (2 * math.pi) theta = 1 sigma_21 = 2 * theta * theta * omega_a rate_21 = 0.005 N0 = 28 Rp = 0.0051 t1 = mp.Transition(1, 2, pumping_rate=Rp, frequency=freq_21, gamma=gamma_21, sigma_diag=mp.Vector3(sigma_21, sigma_21, sigma_21)) t2 = mp.Transition(2, 1, transition_rate=rate_21) ml_atom = mp.MultilevelAtom(sigma=1, transitions=[t1, t2], initial_populations=[N0]) two_level = mp.Medium(index=ncav, E_susceptibilities=[ml_atom]) geometry = [ mp.Block(center=mp.Vector3(z=(-0.5 * sz) + (0.5 * Lcav)), size=mp.Vector3(mp.inf, mp.inf, Lcav), material=two_level) ] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, boundary_layers=pml_layers, geometry=geometry, dimensions=dimensions) def field_func(p): return 1 if p.z == (-0.5 * sz) + (0.5 * Lcav) else 0 def check_field(sim): fp = sim.get_field_point( mp.Ex, mp.Vector3(z=(-0.5 * sz) + Lcav + (0.5 * dpad))).real self.assertAlmostEqual(fp, 1.8040684243391956) sim.init_sim() sim.fields.initialize_field(mp.Ex, field_func) sim.run(mp.at_end(check_field), until=7000)
def test_in_point(self): sim = self.init_simple_simulation() sim.use_output_directory(self.temp_dir) sim.filename_prefix = 'test_in_point' pt = mp.Vector3() sim.run(mp.at_end(mp.in_point(pt, mp.output_efield_z)), until=200) fn = os.path.join(self.temp_dir, 'test_in_point-ez-000200.00.h5') self.assertTrue(os.path.exists(fn))
def test_in_volume(self): sim = self.init_simple_simulation() sim.use_output_directory(self.temp_dir) sim.filename_prefix = 'test_in_volume' vol = mp.Volume(mp.Vector3(), size=mp.Vector3(x=2)) sim.run(mp.at_end(mp.in_volume(vol, mp.output_efield_z)), until=200) fn = os.path.join(self.temp_dir, 'test_in_volume-ez-000200.00.h5') self.assertTrue(os.path.exists(fn))
def test_multilevel_atom(self): resolution = 40 ncav = 1.5 Lcav = 1 dpad = 1 dpml = 1 sz = Lcav + dpad + dpml cell_size = mp.Vector3(z=sz) dimensions = 1 pml_layers = [mp.PML(dpml, side=mp.High)] omega_a = 40 freq_21 = omega_a / (2 * math.pi) gamma_perp = 4 gamma_21 = (2 * gamma_perp) / (2 * math.pi) theta = 1 sigma_21 = 2 * theta * theta * omega_a rate_21 = 0.005 N0 = 28 Rp = 0.0051 t1 = mp.Transition( 1, 2, pumping_rate=Rp, frequency=freq_21, gamma=gamma_21, sigma_diag=mp.Vector3(sigma_21, sigma_21, sigma_21) ) t2 = mp.Transition(2, 1, transition_rate=rate_21) ml_atom = mp.MultilevelAtom(sigma=1, transitions=[t1, t2], initial_populations=[N0]) two_level = mp.Medium(index=ncav, E_susceptibilities=[ml_atom]) geometry = [mp.Block(center=mp.Vector3(z=(-0.5 * sz) + (0.5 * Lcav)), size=mp.Vector3(mp.inf, mp.inf, Lcav), material=two_level)] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, boundary_layers=pml_layers, geometry=geometry, dimensions=dimensions) def field_func(p): return 1 if p.z == (-0.5 * sz) + (0.5 * Lcav) else 0 def check_field(sim): fp = sim.get_field_point(mp.Ex, mp.Vector3(z=(-0.5 * sz) + Lcav + (0.5 * dpad))).real self.assertAlmostEqual(fp, -2.7110969214986387) sim.init_sim() sim.initialize_field(mp.Ex, field_func) sim.run(mp.at_end(check_field), until=7000)
def test_in_point(self): sim = self.init_simple_simulation(filename_prefix='test_in_point') fn = sim.filename_prefix + '-ez-000200.00.h5' pt = mp.Vector3() sim.run(mp.at_end(mp.in_point(pt, mp.output_efield_z)), until=200) self.assertTrue(os.path.exists(fn)) mp.all_wait() if mp.am_master(): os.remove(fn)
def test_with_prefix(self): sim = self.init_simple_simulation() sim.run(mp.with_prefix('test_prefix-', mp.at_end(mp.output_efield_z)), until=200) fname = 'test_prefix-simulation-ez-000200.00.h5' self.assertTrue(os.path.exists(fname)) mp.all_wait() if mp.am_master(): os.remove(fname)
def test_use_output_directory_custom(self): sim = self.init_simple_simulation() sim.use_output_directory('custom_dir') sim.run(mp.at_end(mp.output_efield_z), until=200) output_dir = 'custom_dir' self.assertTrue(os.path.exists(os.path.join(output_dir, self.fname))) mp.all_wait() if mp.am_master(): shutil.rmtree(output_dir)
def test_pw_source(self): self.sim.run(mp.at_end(mp.output_efield_z), until=400) v1 = mp.Vector3(0.5 * self.s, 0) v2 = mp.Vector3(0.5 * self.s, 0.5 * self.s) pt1 = self.sim.get_field_point(mp.Ez, v1) pt2 = self.sim.get_field_point(mp.Ez, v2) self.assertAlmostEqual(pt1 / pt2, 27.557668029008262) self.assertAlmostEqual(cmath.exp(1j * self.k.dot(v1 - v2)), 0.7654030066070924 - 0.6435512702783076j)
def test_pw_source(self): self.sim.run(mp.at_end(mp.output_efield_z), until=400) v1 = mp.Vector3(0.5 * self.s, 0) v2 = mp.Vector3(0.5 * self.s, 0.5 * self.s) pt1 = self.sim.get_field_point(mp.Ez, v1) pt2 = self.sim.get_field_point(mp.Ez, v2) tol = 1e-4 if mp.is_single_precision() else 1e-9 self.assertClose(pt1 / pt2, 27.557668029008262, epsilon=tol) self.assertAlmostEqual(cmath.exp(1j * self.k.dot(v1 - v2)), 0.7654030066070924 - 0.6435512702783076j)
def main(args): resolution = 20 # 20 pixels per unit 1 um eps = 80 # epsilon of cylinder medium Mat1 = mp.Medium(epsilon=eps) # definition of the material dimensions = mp.CYLINDRICAL r = args.r # the value of cylinder radius rl = args.rl # the r/l ration is given, to obtain the l value l=r/rl x = args.x dpml = args.dpml dair = args.dair m=args.m w=1*x h = r/rl # the height of the cylinder sr = r + dair + dpml sz = h + 2*dair + 2*dpml w_max=1.8 w_min=0.2 dw=w_max-w_min boundary_layers = [mp.PML(dpml)] Cyl = mp.Cylinder(material=Mat1, radius=r, height=h, center=mp.Vector3(0,0,0)) # make a cylinder with given parameters geometry = [Cyl] cell_size = mp.Vector3(sr,0,sz) #sources = [ mp.Source(mp.ContinuousSource(frequency = w), component=mp.Hz, center=mp.Vector3(r+dair,0,0)) ] sources = [mp.Source(mp.GaussianSource(w, fwidth=dw), component=mp.Hz, center=mp.Vector3(r+dair,0,0))] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=sources, dimensions=dimensions, m=m) sim.run(mp.in_volume(mp.Volume(center=mp.Vector3(), size=mp.Vector3(sr,0,sz)), mp.at_end(mp.output_epsilon, mp.output_efield_z)), mp.after_sources(mp.Harminv(mp.Hz, mp.Vector3(), w, dw)), until_after_sources=100)
def test_geometry_center(self): resolution = 20 cell_size = mp.Vector3(10, 10) pml = [mp.PML(1)] center = mp.Vector3(2, -1) result = [] fcen = 0.15 df = 0.1 sources = [mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3())] geometry = [mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 3, mp.inf), material=mp.Medium(epsilon=12))] def print_field(sim): result.append(sim.get_field_point(mp.Ez, mp.Vector3(2, -1))) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml, sources=sources, geometry=geometry, geometry_center=center) sim.run(mp.at_end(print_field), until=50) self.assertAlmostEqual(result[0], -0.0599602798684155)
def main(from_um_factor, resolution, courant, r, material, paper, reference, submerged_index, displacement, surface_index, wlen_range, nfreq, air_r_factor, pml_wlen_factor, flux_r_factor, time_factor_cell, second_time_factor, series, folder, parallel, n_processes, n_cores, n_nodes, split_chunks_evenly, load_flux, load_chunks, near2far): #%% CLASSIC INPUT PARAMETERS """ # Simulation size from_um_factor = 10e-3 # Conversion of 1 μm to my length unit (=10nm/1μm) resolution = 2 # >=8 pixels per smallest wavelength, i.e. np.floor(8/wvl_min) courant = 0.5 # Nanoparticle specifications: Sphere in Vacuum :) r = 51.5 # Radius of sphere in nm paper = "R" reference = "Meep" displacement = 0 # Displacement of the surface from the bottom of the sphere in nm submerged_index = 1 # 1.33 for water surface_index = 1 # 1.54 for glass # Frequency and wavelength wlen_range = np.array([350,500]) # Wavelength range in nm nfreq = 100 # Box dimensions pml_wlen_factor = 0.38 air_r_factor = 0.5 flux_r_factor = 0 #0.1 # Simulation time time_factor_cell = 1.2 second_time_factor = 10 # Saving directories series = "SilverRes4" folder = "Test/TestSilver" # Configuration parallel = False n_processes = 1 n_cores = 1 n_nodes = 1 split_chunks_evenly = True load_flux = False load_chunks = True near2far = False """ #%% MORE INPUT PARAMETERS # Frequency and wavelength cutoff = 3.2 # Gaussian planewave source's parameter of shape nazimuthal = 16 npolar = 20 ### TREATED INPUT PARAMETERS # Nanoparticle specifications: Sphere in Vacuum :) r = r / (from_um_factor * 1e3) # Now in Meep units if reference == "Meep": medium = vmt.import_medium(material, from_um_factor=from_um_factor, paper=paper) # Importing material constants dependant on frequency from Meep Library elif reference == "RIinfo": medium = vmt.MediumFromFile(material, paper=paper, reference=reference, from_um_factor=from_um_factor) # Importing material constants dependant on frequency from external file else: raise ValueError("Reference for medium not recognized. Sorry :/") displacement = displacement / (from_um_factor * 1e3) # Now in Meep units # Frequency and wavelength wlen_range = np.array(wlen_range) wlen_range = wlen_range / (from_um_factor * 1e3) # Now in Meep units freq_range = 1 / wlen_range # Hz range in Meep units from highest to lowest freq_center = np.mean(freq_range) freq_width = max(freq_range) - min(freq_range) # Space configuration pml_width = pml_wlen_factor * max(wlen_range) # 0.5 * max(wlen_range) air_width = air_r_factor * r # 0.5 * max(wlen_range) flux_box_size = 2 * (1 + flux_r_factor) * r # Saving directories if series is None: series = "Test" if folder is None: folder = "Test" params_list = [ "from_um_factor", "resolution", "courant", "material", "r", "paper", "reference", "submerged_index", "displacement", "surface_index", "wlen_range", "nfreq", "nazimuthal", "npolar", "cutoff", "flux_box_size", "cell_width", "pml_width", "air_width", "source_center", "until_after_sources", "time_factor_cell", "second_time_factor", "enlapsed", "parallel", "n_processes", "n_cores", "n_nodes", "split_chunks_evenly", "near2far", "script", "sysname", "path" ] #%% GENERAL GEOMETRY SETUP air_width = air_width - air_width % (1 / resolution) pml_width = pml_width - pml_width % (1 / resolution) pml_layers = [mp.PML(thickness=pml_width)] # symmetries = [mp.Mirror(mp.Y), # mp.Mirror(mp.Z, phase=-1)] # Two mirror planes reduce cell size to 1/4 # Issue related that lead me to comment this lines: # https://github.com/NanoComp/meep/issues/1484 cell_width = 2 * (pml_width + air_width + r) cell_width = cell_width - cell_width % (1 / resolution) cell_size = mp.Vector3(cell_width, cell_width, cell_width) # surface_center = r/4 - displacement/2 + cell_width/4 # surface_center = surface_center - surface_center%(1/resolution) # displacement = r/2 + cell_width/2 - 2*surface_center displacement = displacement - displacement % (1 / resolution) flux_box_size = flux_box_size - flux_box_size % (1 / resolution) source_center = -0.5 * cell_width + pml_width sources = [ mp.Source(mp.GaussianSource(freq_center, fwidth=freq_width, is_integrated=True, cutoff=cutoff), center=mp.Vector3(source_center), size=mp.Vector3(0, cell_width, cell_width), component=mp.Ez) ] # Ez-polarized planewave pulse # (its size parameter fills the entire cell in 2d) # >> The planewave source extends into the PML # ==> is_integrated=True must be specified until_after_sources = time_factor_cell * cell_width * submerged_index # Enough time for the pulse to pass through all the cell # Originally: Aprox 3 periods of lowest frequency, using T=λ/c=λ in Meep units # Now: Aprox 3 periods of highest frequency, using T=λ/c=λ in Meep units geometry = [mp.Sphere(material=medium, center=mp.Vector3(), radius=r)] # Au sphere with frequency-dependant characteristics imported from Meep. if surface_index != 1: geometry = [ mp.Block(material=mp.Medium(index=surface_index), center=mp.Vector3( r / 2 - displacement / 2 + cell_width / 4, 0, 0), size=mp.Vector3(cell_width / 2 - r + displacement, cell_width, cell_width)), *geometry ] # A certain material surface underneath it home = vs.get_home() sysname = vs.get_sys_name() path = os.path.join(home, folder, series) if not os.path.isdir(path) and vm.parallel_assign(0, n_processes, parallel): os.makedirs(path) file = lambda f: os.path.join(path, f) # Computation enlapsed = [] parallel_specs = np.array([n_processes, n_cores, n_nodes], dtype=int) max_index = np.argmax(parallel_specs) for index, item in enumerate(parallel_specs): if item == 0: parallel_specs[index] = 1 parallel_specs[0:max_index] = np.full(parallel_specs[0:max_index].shape, max(parallel_specs)) n_processes, n_cores, n_nodes = parallel_specs parallel = max(parallel_specs) > 1 del parallel_specs, max_index, index, item if parallel: np_process = mp.count_processors() else: np_process = 1 #%% FIRST RUN measure_ram() params = {} for p in params_list: params[p] = eval(p) stable, max_courant = vm.check_stability(params) if stable: print("As a whole, the simulation should be stable") else: print("As a whole, the simulation could not be stable") print(f"Recommended maximum courant factor is {max_courant}") if load_flux: try: flux_path = vm.check_midflux(params)[0] flux_needed = False except: flux_needed = True else: flux_needed = True if load_chunks and not split_chunks_evenly: try: chunks_path = vm.check_chunks(params)[0] chunk_layout = os.path.join(chunks_path, "Layout.h5") chunks_needed = False except: chunks_needed = True flux_needed = True else: if not split_chunks_evenly: chunks_needed = True flux_needed = True else: chunk_layout = None chunks_needed = False if chunks_needed: sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), output_single_precision=True, split_chunks_evenly=split_chunks_evenly, # symmetries=symmetries, geometry=geometry) sim.init_sim() chunks_path = vm.save_chunks(sim, params, path) chunk_layout = os.path.join(chunks_path, "Layout.h5") del sim if flux_needed: #% FIRST RUN: SET UP measure_ram() sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), split_chunks_evenly=split_chunks_evenly, chunk_layout=chunk_layout, output_single_precision=True) #, # symmetries=symmetries) # >> k_point zero specifies boundary conditions needed # for the source to be infinitely extended measure_ram() # Scattered power --> Computed by surrounding it with closed DFT flux box # (its size and orientation are irrelevant because of Poynting's theorem) box_x1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_x2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_y1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_y2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_z1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) box_z2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) # Funny you can encase the sphere (r radius) so closely (2r-sided box) measure_ram() if near2far: near2far_box = sim.add_near2far( freq_center, freq_width, nfreq, mp.Near2FarRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=+1)) measure_ram() else: near2far_box = None # used_ram.append(used_ram[-1]) #% FIRST RUN: INITIALIZE temp = time() sim.init_sim() enlapsed.append(time() - temp) measure_ram() step_ram_function = lambda sim: measure_ram() #% FIRST RUN: SIMULATION NEEDED TO NORMALIZE temp = time() sim.run(mp.at_beginning(step_ram_function), mp.at_time(int(until_after_sources / 2), step_ram_function), mp.at_end(step_ram_function), until_after_sources=until_after_sources) # mp.stop_when_fields_decayed( # np.mean(wlen_range), # dT = mean period of source # mp.Ez, # Component of field to check # mp.Vector3(0.5*cell_width - pml_width, 0, 0), # Where to check # 1e-3)) # Factor to decay enlapsed.append(time() - temp) #% SAVE MID DATA for p in params_list: params[p] = eval(p) flux_path = vm.save_midflux(sim, box_x1, box_x2, box_y1, box_y2, box_z1, box_z2, near2far_box, params, path) freqs = np.asarray(mp.get_flux_freqs(box_x1)) box_x1_flux0 = np.asarray(mp.get_fluxes(box_x1)) box_x2_flux0 = np.asarray(mp.get_fluxes(box_x2)) box_y1_flux0 = np.asarray(mp.get_fluxes(box_y1)) box_y2_flux0 = np.asarray(mp.get_fluxes(box_y2)) box_z1_flux0 = np.asarray(mp.get_fluxes(box_z1)) box_z2_flux0 = np.asarray(mp.get_fluxes(box_z2)) data_mid = np.array([ 1e3 * from_um_factor / freqs, box_x1_flux0, box_x2_flux0, box_y1_flux0, box_y2_flux0, box_z1_flux0, box_z2_flux0 ]).T header_mid = [ "Longitud de onda [nm]", "Flujo X10 [u.a.]", "Flujo X20 [u.a]", "Flujo Y10 [u.a]", "Flujo Y20 [u.a]", "Flujo Z10 [u.a]", "Flujo Z20 [u.a]" ] if vm.parallel_assign(0, n_processes, parallel): vs.savetxt(file("MidFlux.txt"), data_mid, header=header_mid, footer=params) if not split_chunks_evenly: vm.save_chunks(sim, params, path) if parallel: f = h5.File(file("MidRAM.h5"), "w", driver='mpio', comm=MPI.COMM_WORLD) current_process = mp.my_rank() f.create_dataset("RAM", (len(used_ram), np_process), dtype="float") f["RAM"][:, current_process] = used_ram for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", (len(used_ram), np_process), dtype="int") f["SWAP"][:, current_process] = swapped_ram for a in params: f["SWAP"].attrs[a] = params[a] else: f = h5.File(file("MidRAM.h5"), "w") f.create_dataset("RAM", data=used_ram) for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", data=swapped_ram) for a in params: f["SWAP"].attrs[a] = params[a] f.close() del f #% PLOT FLUX FOURIER MID DATA if vm.parallel_assign(1, np_process, parallel): ylims = (np.min(data_mid[:, 1:]), np.max(data_mid[:, 1:])) ylims = (ylims[0] - .1 * (ylims[1] - ylims[0]), ylims[1] + .1 * (ylims[1] - ylims[0])) fig, ax = plt.subplots(3, 2, sharex=True) fig.subplots_adjust(hspace=0, wspace=.05) for a in ax[:, 1]: a.yaxis.tick_right() a.yaxis.set_label_position("right") for a, h in zip(np.reshape(ax, 6), header_mid[1:]): a.set_ylabel(h) for d, a in zip(data_mid[:, 1:].T, np.reshape(ax, 6)): a.plot(1e3 * from_um_factor / freqs, d) a.set_ylim(*ylims) ax[-1, 0].set_xlabel("Wavelength [nm]") ax[-1, 1].set_xlabel("Wavelength [nm]") plt.savefig(file("MidFlux.png")) del fig, ax, ylims, a, h sim.reset_meep() #%% SECOND RUN: SETUP measure_ram() sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), Courant=courant, default_material=mp.Medium(index=submerged_index), output_single_precision=True, split_chunks_evenly=split_chunks_evenly, chunk_layout=chunk_layout, # symmetries=symmetries, geometry=geometry) measure_ram() box_x1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_x2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size))) box_y1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_y2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size))) box_z1 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) box_z2 = sim.add_flux( freq_center, freq_width, nfreq, mp.FluxRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0))) measure_ram() if near2far: near2far_box = sim.add_near2far( freq_center, freq_width, nfreq, mp.Near2FarRegion(center=mp.Vector3(x=-flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(x=+flux_box_size / 2), size=mp.Vector3(0, flux_box_size, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(y=-flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=-1), mp.Near2FarRegion(center=mp.Vector3(y=+flux_box_size / 2), size=mp.Vector3(flux_box_size, 0, flux_box_size), weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=-flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(z=+flux_box_size / 2), size=mp.Vector3(flux_box_size, flux_box_size, 0), weight=+1)) measure_ram() else: near2far_box = None # used_ram.append(used_ram[-1]) #%% SECOND RUN: INITIALIZE temp = time() sim.init_sim() enlapsed.append(time() - temp) measure_ram() #%% LOAD FLUX FROM FILE vm.load_midflux(sim, box_x1, box_x2, box_y1, box_y2, box_z1, box_z2, near2far_box, flux_path) measure_ram() freqs = np.asarray(mp.get_flux_freqs(box_x1)) box_x1_flux0 = np.asarray(mp.get_fluxes(box_x1)) box_x1_data = sim.get_flux_data(box_x1) box_x2_data = sim.get_flux_data(box_x2) box_y1_data = sim.get_flux_data(box_y1) box_y2_data = sim.get_flux_data(box_y2) box_z1_data = sim.get_flux_data(box_z1) box_z2_data = sim.get_flux_data(box_z2) if near2far: near2far_data = sim.get_near2far_data(near2far_box) temp = time() sim.load_minus_flux_data(box_x1, box_x1_data) sim.load_minus_flux_data(box_x2, box_x2_data) sim.load_minus_flux_data(box_y1, box_y1_data) sim.load_minus_flux_data(box_y2, box_y2_data) sim.load_minus_flux_data(box_z1, box_z1_data) sim.load_minus_flux_data(box_z2, box_z2_data) if near2far: sim.load_minus_near2far_data(near2far_box, near2far_data) enlapsed.append(time() - temp) del box_x1_data, box_x2_data, box_y1_data, box_y2_data del box_z1_data, box_z2_data if near2far: del near2far_data measure_ram() #%% SECOND RUN: SIMULATION :D step_ram_function = lambda sim: measure_ram() temp = time() sim.run(mp.at_beginning(step_ram_function), mp.at_time(int(second_time_factor * until_after_sources / 2), step_ram_function), mp.at_end(step_ram_function), until_after_sources=second_time_factor * until_after_sources) # mp.stop_when_fields_decayed( # np.mean(wlen_range), # dT = mean period of source # mp.Ez, # Component of field to check # mp.Vector3(0.5*cell_width - pml_width, 0, 0), # Where to check # 1e-3)) # Factor to decay enlapsed.append(time() - temp) del temp # Aprox 30 periods of lowest frequency, using T=λ/c=λ in Meep units box_x1_flux = np.asarray(mp.get_fluxes(box_x1)) box_x2_flux = np.asarray(mp.get_fluxes(box_x2)) box_y1_flux = np.asarray(mp.get_fluxes(box_y1)) box_y2_flux = np.asarray(mp.get_fluxes(box_y2)) box_z1_flux = np.asarray(mp.get_fluxes(box_z1)) box_z2_flux = np.asarray(mp.get_fluxes(box_z2)) #%% SCATTERING ANALYSIS scatt_flux = box_x1_flux - box_x2_flux scatt_flux = scatt_flux + box_y1_flux - box_y2_flux scatt_flux = scatt_flux + box_z1_flux - box_z2_flux intensity = box_x1_flux0 / (flux_box_size)**2 # Flux of one of the six monitor planes / Área # (the closest one, facing the planewave source) # This is why the six sides of the flux box are separated # (Otherwise, the box could've been one flux object with weights ±1 per side) scatt_cross_section = np.divide(scatt_flux, intensity) # Scattering cross section σ = # = scattered power in all directions / incident intensity. scatt_eff_meep = -1 * scatt_cross_section / (np.pi * r**2) # Scattering efficiency = # = scattering cross section / cross sectional area of the sphere freqs = np.array(freqs) scatt_eff_theory = [ ps.MieQ(np.sqrt(medium.epsilon(f)[0, 0] * medium.mu(f)[0, 0]), 1e3 * from_um_factor / f, 2 * r * 1e3 * from_um_factor, nMedium=submerged_index, asDict=True)['Qsca'] for f in freqs ] # The simulation results are validated by comparing with # analytic theory of PyMieScatt module #%% ANGULAR PATTERN ANALYSIS if near2far: fraunhofer_distance = 8 * (r**2) / min(wlen_range) radial_distance = max(10 * fraunhofer_distance, 1.5 * cell_width / 2) # radius of far-field circle must be at least Fraunhofer distance azimuthal_angle = np.arange(0, 2 + 2 / nazimuthal, 2 / nazimuthal) # in multiples of pi polar_angle = np.arange(0, 1 + 1 / npolar, 1 / npolar) poynting_x = [] poynting_y = [] poynting_z = [] poynting_r = [] for phi in azimuthal_angle: poynting_x.append([]) poynting_y.append([]) poynting_z.append([]) poynting_r.append([]) for theta in polar_angle: farfield_dict = sim.get_farfields( near2far_box, 1, where=mp.Volume(center=mp.Vector3( radial_distance * np.cos(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.sin(np.pi * phi) * np.sin(np.pi * theta), radial_distance * np.cos(np.pi * theta)))) Px = farfield_dict["Ey"] * np.conjugate(farfield_dict["Hz"]) Px -= farfield_dict["Ez"] * np.conjugate(farfield_dict["Hy"]) Py = farfield_dict["Ez"] * np.conjugate(farfield_dict["Hx"]) Py -= farfield_dict["Ex"] * np.conjugate(farfield_dict["Hz"]) Pz = farfield_dict["Ex"] * np.conjugate(farfield_dict["Hy"]) Pz -= farfield_dict["Ey"] * np.conjugate(farfield_dict["Hx"]) Px = np.real(Px) Py = np.real(Py) Pz = np.real(Pz) poynting_x[-1].append(Px) poynting_y[-1].append(Py) poynting_z[-1].append(Pz) poynting_r[-1].append( np.sqrt(np.square(Px) + np.square(Py) + np.square(Pz))) poynting_x = np.array(poynting_x) poynting_y = np.array(poynting_y) poynting_z = np.array(poynting_z) poynting_r = np.array(poynting_r) #%% SAVE FINAL DATA for p in params_list: params[p] = eval(p) data = np.array( [1e3 * from_um_factor / freqs, scatt_eff_meep, scatt_eff_theory]).T header = [ "Longitud de onda [nm]", "Sección eficaz efectiva (Meep) [u.a.]", "Sección eficaz efectiva (Theory) [u.a.]" ] data_base = np.array([ 1e3 * from_um_factor / freqs, box_x1_flux0, box_x1_flux, box_x2_flux, box_y1_flux, box_y2_flux, box_z1_flux, box_z2_flux, intensity, scatt_flux, scatt_cross_section ]).T header_base = [ "Longitud de onda [nm]", "Flujo X10 [u.a.]", "Flujo X1 [u.a]", "Flujo X2 [u.a]", "Flujo Y1 [u.a]", "Flujo Y2 [u.a]", "Flujo Z1 [u.a]", "Flujo Z2 [u.a]", "Intensidad incidente [u.a.]", "Flujo scattereado [u.a.]", "Sección eficaz de scattering [u.a.]" ] if vm.parallel_assign(0, np_process, parallel): vs.savetxt(file("Results.txt"), data, header=header, footer=params) vs.savetxt(file("BaseResults.txt"), data_base, header=header_base, footer=params) if near2far: header_near2far = [ "Poynting medio Px [u.a.]", "Poynting medio Py [u.a.]", "Poynting medio Pz [u.a.]", "Poynting medio Pr [u.a.]" ] data_near2far = [ poynting_x.reshape(poynting_x.size), poynting_y.reshape(poynting_y.size), poynting_z.reshape(poynting_z.size), poynting_r.reshape(poynting_r.size) ] if vm.parallel_assign(1, np_process, parallel): vs.savetxt(file("Near2FarResults.txt"), data_near2far, header=header_near2far, footer=params) if not split_chunks_evenly: vm.save_chunks(sim, params, path) if parallel: f = h5.File(file("RAM.h5"), "w", driver='mpio', comm=MPI.COMM_WORLD) current_process = mp.my_rank() f.create_dataset("RAM", (len(used_ram), np_process), dtype="float") f["RAM"][:, current_process] = used_ram for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", (len(used_ram), np_process), dtype="int") f["SWAP"][:, current_process] = swapped_ram for a in params: f["SWAP"].attrs[a] = params[a] else: f = h5.File(file("RAM.h5"), "w") f.create_dataset("RAM", data=used_ram) for a in params: f["RAM"].attrs[a] = params[a] f.create_dataset("SWAP", data=swapped_ram) for a in params: f["SWAP"].attrs[a] = params[a] f.close() del f if flux_needed and vm.parallel_assign(0, np_process, parallel): os.remove(file("MidRAM.h5")) #%% PLOT ALL TOGETHER if vm.parallel_assign(0, np_process, parallel) and surface_index == 1: plt.figure() plt.plot(1e3 * from_um_factor / freqs, scatt_eff_meep, 'bo-', label='Meep') plt.plot(1e3 * from_um_factor / freqs, scatt_eff_theory, 'ro-', label='Theory') plt.xlabel('Wavelength [nm]') plt.ylabel('Scattering efficiency [σ/πr$^{2}$]') plt.legend() plt.title('Scattering of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) plt.tight_layout() plt.savefig(file("Comparison.png")) #%% PLOT SEPARATE if vm.parallel_assign(1, np_process, parallel): plt.figure() plt.plot(1e3 * from_um_factor / freqs, scatt_eff_meep, 'bo-', label='Meep') plt.xlabel('Wavelength [nm]') plt.ylabel('Scattering efficiency [σ/πr$^{2}$]') plt.legend() plt.title('Scattering of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) plt.tight_layout() plt.savefig(file("Meep.png")) if vm.parallel_assign(0, np_process, parallel) and surface_index == 1: plt.figure() plt.plot(1e3 * from_um_factor / freqs, scatt_eff_theory, 'ro-', label='Theory') plt.xlabel('Wavelength [nm]') plt.ylabel('Scattering efficiency [σ/πr$^{2}$]') plt.legend() plt.title('Scattering of Au Sphere With {:.1f} nm Radius'.format(r * 10)) plt.tight_layout() plt.savefig(file("Theory.png")) #%% PLOT ONE ABOVE THE OTHER if vm.parallel_assign(1, np_process, parallel) and surface_index == 1: fig, axes = plt.subplots(nrows=2, sharex=True) fig.subplots_adjust(hspace=0) plt.suptitle('Scattering of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) axes[0].plot(1e3 * from_um_factor / freqs, scatt_eff_meep, 'bo-', label='Meep') axes[0].yaxis.tick_right() axes[0].set_ylabel('Scattering efficiency [σ/πr$^{2}$]') axes[0].legend() axes[1].plot(1e3 * from_um_factor / freqs, scatt_eff_theory, 'ro-', label='Theory') axes[1].set_xlabel('Wavelength [nm]') axes[1].set_ylabel('Scattering efficiency [σ/πr$^{2}$]') axes[1].legend() plt.savefig(file("SeparatedComparison.png")) #%% PLOT FLUX FOURIER FINAL DATA if vm.parallel_assign(0, np_process, parallel): ylims = (np.min(data_base[:, 2:8]), np.max(data_base[:, 2:8])) ylims = (ylims[0] - .1 * (ylims[1] - ylims[0]), ylims[1] + .1 * (ylims[1] - ylims[0])) fig, ax = plt.subplots(3, 2, sharex=True) fig.subplots_adjust(hspace=0, wspace=.05) plt.suptitle('Final flux of Au Sphere With {:.1f} nm Radius'.format( r * from_um_factor * 1e3)) for a in ax[:, 1]: a.yaxis.tick_right() a.yaxis.set_label_position("right") for a, h in zip(np.reshape(ax, 6), header_base[1:7]): a.set_ylabel(h) for d, a in zip(data_base[:, 3:9].T, np.reshape(ax, 6)): a.plot(1e3 * from_um_factor / freqs, d) a.set_ylim(*ylims) ax[-1, 0].set_xlabel("Wavelength [nm]") ax[-1, 1].set_xlabel("Wavelength [nm]") plt.savefig(file("FinalFlux.png")) #%% PLOT ANGULAR PATTERN IN 3D if near2far and vm.parallel_assign(1, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) fig = plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax = fig.add_subplot(1, 1, 1, projection='3d') ax.plot_surface(poynting_x[:, :, freq_index], poynting_y[:, :, freq_index], poynting_z[:, :, freq_index], cmap=plt.get_cmap('jet'), linewidth=1, antialiased=False, alpha=0.5) ax.set_xlabel(r"$P_x$") ax.set_ylabel(r"$P_y$") ax.set_zlabel(r"$P_z$") plt.savefig(file("AngularPattern.png")) #%% PLOT ANGULAR PATTERN PROFILE FOR DIFFERENT POLAR ANGLES if near2far and vm.parallel_assign(0, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(polar_angle).index(alpha) for alpha in [0, .25, .5, .75, 1] ] plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax_plain = plt.axes() for i in index: ax_plain.plot(poynting_x[:, i, freq_index], poynting_y[:, i, freq_index], ".-", label=rf"$\theta$ = {polar_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_x$") ax_plain.set_ylabel(r"$P_y$") ax_plain.set_aspect("equal") plt.savefig(file("AngularPolar.png")) #%% PLOT ANGULAR PATTERN PROFILE FOR DIFFERENT AZIMUTHAL ANGLES if near2far and vm.parallel_assign(1, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(azimuthal_angle).index(alpha) for alpha in [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2] ] plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax_plain = plt.axes() for i in index: ax_plain.plot(poynting_x[i, :, freq_index], poynting_z[i, :, freq_index], ".-", label=rf"$\phi$ = {azimuthal_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_x$") ax_plain.set_ylabel(r"$P_z$") plt.savefig(file("AngularAzimuthal.png")) if near2far and vm.parallel_assign(0, np_process, parallel): freq_index = np.argmin(np.abs(freqs - freq_center)) index = [ list(azimuthal_angle).index(alpha) for alpha in [0, .25, .5, .75, 1, 1.25, 1.5, 1.75, 2] ] plt.figure() plt.suptitle( 'Angular Pattern of Au Sphere With {:.1f} nm Radius at {:.1f} nm'. format(r * from_um_factor * 1e3, from_um_factor * 1e3 / freqs[freq_index])) ax_plain = plt.axes() for i in index: ax_plain.plot(np.sqrt( np.square(poynting_x[i, :, freq_index]) + np.square(poynting_y[i, :, freq_index])), poynting_z[i, :, freq_index], ".-", label=rf"$\phi$ = {azimuthal_angle[i]:.2f} $\pi$") plt.legend() ax_plain.set_xlabel(r"$P_\rho$") ax_plain.set_ylabel(r"$P_z$") plt.savefig(file("AngularAzimuthalAbs.png"))
def test_in_volume(self): sim = self.init_simple_simulation() sim.filename_prefix = 'test_in_volume' vol = mp.Volume(mp.Vector3(), size=mp.Vector3(x=2)) sim.run(mp.at_end(mp.in_volume(vol, mp.output_efield_z)), until=200)
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)) # -------------------------------------------------------------------------- e_z = args.e_z e_y = args.e_y m_charge = args.m_charge ref_medium = args.ref_medium n1 = args.n1 n2 = args.n2 kw_0 = args.kw_0 kr_w = args.kr_w # 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 sz = 4 # size of cell including PML in z-direction pml_thickness = 0.25 # thickness of PML layer freq = 5 # vacuum frequency of source (default 5) 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) shift = source_shift + r_w chi_rad = math.radians(chi_deg) s_pol = True if (e_z == 1 and e_y == 0) else False p_pol = True if (e_z == 0 and e_y == 1) else False params = dict(W_y=w_0, m=m_charge, 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, sz) # geometry-lattice default_material = mp.Medium(index=n1) # located at lower right edge for 45 degree tilt 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)) ] # -------------------------------------------------------------------------- # 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) / 3 # -------------------------------------------------------------------------- # 2d-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**2 + r.z**2) / W_y**2)) # -------------------------------------------------------------------------- # some test outputs # -------------------------------------------------------------------------- if test_output: print("Gauss 2d beam profile:", Gauss(mp.Vector3(0, 0.5, 0.2), params)) print() # -------------------------------------------------------------------------- # spectrum amplitude distribution(s) # -------------------------------------------------------------------------- # cartesian coordinates (not recommmended) ------------------------- def phi(k_y, k_z): """Azimuthal angle. Part of coordinate transformation from k-space to (theta, phi)-space. """ return math.atan2(k_y, -k_z) def theta(k_y, k_z, k): """Polar angle. Part of coordinate transformation from k-space to (theta, phi)-space. """ return math.acos(cmath.sqrt(k**2 - k_y**2 - k_z**2).real / k) def f_Gauss_cartesian(k_y, k_z, params): """2d-Gaussian spectrum amplitude. Impementation for Cartesian coordinates. """ W_y = params['W_y'] return math.exp(-W_y**2 * (k_y**2 + k_z**2) / 4) def f_Laguerre_Gauss_cartesian(k_y, k_z, params): """Laguerre-Gaussian spectrum amplitude. Impementation for Cartesian coordinates. """ m, k = params['m'], params['k'] return f_Gauss_cartesian(k_y, k_z, params) * \ cmath.exp(1j*m*phi(k_y, k_z)) * theta(k_y, k_z, k)**abs(m) # spherical coordinates -------------------------------------------- def f_Gauss_spherical(sin_theta, theta, phi, params): """2d-Gaussian spectrum amplitude. Impementation for spherical coordinates. """ W_y, k = params['W_y'], params['k'] return math.exp(-(k * W_y * sin_theta / 2)**2) def f_Laguerre_Gauss_spherical(sin_theta, theta, phi, params): """Laguerre-Gaussian spectrum amplitude. Impementation for spherical coordinates. """ m = params['m'] return f_Gauss_spherical(sin_theta, theta, phi, params) * theta**abs(m) * \ cmath.exp(1j*m*phi) # -------------------------------------------------------------------------- # some test outputs # -------------------------------------------------------------------------- if test_output: k_y, k_z = 1.0, 5.2 theta_ = theta(k_y, k_z, k1) phi_ = phi(k_y, k_z) print("Gauss spectrum (cartesian):", f_Gauss_cartesian(k_y, k_z, params)) print("Gauss spectrum (spherical):", f_Gauss_spherical(math.sin(theta_), theta_, phi_, params)) print() print("L-G spectrum (cartesian):", f_Laguerre_Gauss_cartesian(k_y, k_z, params)) print( "L-G spectrum (spherical):", f_Laguerre_Gauss_spherical(math.sin(theta_), theta_, phi_, params)) print() # -------------------------------------------------------------------------- # plane wave decomposition # (purpose: calculate field amplitude at light source position if not # coinciding with beam waist) # -------------------------------------------------------------------------- def psi_cartesian(r, x, params): """Field amplitude function. Integration in Cartesian coordinates. """ k, m = params['k'], params['m'] try: getattr(psi_cartesian, "called") except AttributeError: psi_cartesian.called = True print("Calculating inital field configuration. " "This will take some time...") def phase(k_y, k_z, x, y, z): """Phase function.""" return x * cmath.sqrt(k**2 - k_y**2 - k_z**2).real + y * k_y + z * k_z f = (f_Gauss_cartesian if m == 0 else f_Laguerre_Gauss_cartesian) try: (result, real_tol, imag_tol) = complex_dblquad( lambda k_y, k_z: f(k_y, k_z, params) * cmath.exp(1j * phase( k_y, k_z, x, r.y, r.z)), -k, k, -k, k) except Exception as e: print(type(e).__name__ + ":", e) sys.exit() return result def psi_spherical(r, x, params): """Field amplitude function. Integration in spherical coordinates. """ k, m = params['k'], params['m'] try: getattr(psi_spherical, "called") except AttributeError: psi_spherical.called = True print("Calculating inital field configuration. " "This will take some time...") def phase(theta, phi, x, y, z): """Phase function.""" sin_theta, sin_phi = math.sin(theta), math.sin(phi) cos_theta, cos_phi = math.cos(theta), math.cos(phi) return k * (sin_theta * (y * sin_phi - z * cos_phi) + cos_theta * x) f = (f_Gauss_spherical if m == 0 else f_Laguerre_Gauss_spherical) try: (result, real_tol, imag_tol) = complex_dblquad( lambda theta, phi: math.sin(theta) * math.cos(theta) * f(math.sin(theta), theta, phi, params) * cmath.exp(1j * phase( theta, phi, x, r.y, r.z)), 0, 2 * math.pi, 0, math.pi / 2) except Exception as e: print(type(e).__name__ + ":", e) sys.exit() return k**2 * result # -------------------------------------------------------------------------- # some test outputs (uncomment if needed) # -------------------------------------------------------------------------- if test_output: k_y, k_z = 1.0, 5.2 x, y, z = -2.15, 0.3, 0.5 r = mp.Vector3(0, y, z) print("phi:", phi(k_y, k_z)) print() print("psi (cartesian):", psi_cartesian(r, f_Laguerre_Gauss_cartesian, x, params)) print("psi (spherical):", psi_spherical(r, f_Laguerre_Gauss_spherical, x, params)) print("psi (origin, simple):", Gauss(r, params)) sys.exit() # -------------------------------------------------------------------------- # display values of physical variables # -------------------------------------------------------------------------- print() print("Expected output file size:", round(8 * (sx * sy * sz * resolution**3) / (1024**2)), "MiB") print() print("Specified variables and derived values:") print("n1:", n1) print("n2:", n2) print("chi: ", chi_deg, " [degree]") # interface inclination with respect to the x-axis print("incl.:", 90 - chi_deg, " [degree]") print("kw_0: ", kw_0) print("kr_w: ", kr_w) print("k_vac:", k_vac) print("vortex charge:", m_charge) print("Jones vector components: " "(e_z=", e_z, ", e_y=", e_y, ")", sep="", end="") print(" --->", ("s-" if s_pol else "p-" if p_pol else "mixed-") + "polarisation") print("degree of linear polarisation at pi/4:", 2 * (-e_z.conjugate() * e_y).real) print("degree of circular polarisation:", 2 * (-e_z.conjugate() * e_y).imag) # -------------------------------------------------------------------------- # exploiting symmetries to reduce computational effort # (only possible for beams without intrinsic orbital angular momentum, i.e. # no vortex charge) # -------------------------------------------------------------------------- # The plane of incidence (x-y-plane) is a mirror plane which is characterised # to be orthogonal to the z-axis (symmetry of the geometric structure). # Symmetry of the sources must be ensured simultaneously, which is only # possible for certain cases. If I am not mistaken this can only be achieved # for vortex free beams with pure s- or p-polarisation, i.e. where either # the Ez or Ey component is specified. symmetries = [] if m_charge == 0: if s_pol: symmetries.append(mp.Mirror(mp.Z, phase=-1)) if p_pol: symmetries.append(mp.Mirror(mp.Y, phase=+1)) # -------------------------------------------------------------------------- # specify current source, output functions and run simulation # -------------------------------------------------------------------------- force_complex_fields = True # default: True eps_averaging = True # default: True sources = [] if e_z != 0: source_Ez = mp.Source( src=mp.ContinuousSource(frequency=freq, width=0.5), component=mp.Ez, amplitude=e_z, size=mp.Vector3(0, 3, 3), center=mp.Vector3(source_shift, 0, 0), #amp_func=lambda r: Gauss(r, params) #amp_func=lambda r: psi_cartesian(r, shift, params) amp_func=lambda r: psi_spherical(r, shift, params)) sources.append(source_Ez) if e_y != 0: source_Ey = mp.Source( src=mp.ContinuousSource(frequency=freq, width=0.5), component=mp.Ey, amplitude=e_y, size=mp.Vector3(0, 3, 3), center=mp.Vector3(source_shift, 0, 0), #amp_func=lambda r: Gauss(r, params) #amp_func=lambda r: psi_cartesian(r, shift, params) amp_func=lambda r: psi_spherical(r, shift, params)) sources.append(source_Ey) sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, symmetries=symmetries, default_material=default_material, Courant=Courant, geometry=geometry, sources=sources, resolution=resolution, force_complex_fields=force_complex_fields, eps_averaging=eps_averaging) sim.use_output_directory() # put output files in a separate folder def efield_real_squared(r, ex, ey, ez): """Calculate |Re E|^2. With |.| denoting the complex modulus if 'force_complex_fields?' is set to true, otherwise |.| gives the Euclidean norm. """ return ex.real**2 + ey.real**2 + ez.real**2 def efield_imag_squared(r, ex, ey, ez): """Calculate |Im E|^2. With |.| denoting the complex modulus if 'force_complex_fields?' is set to true, otherwise |.| gives the Euclidean norm. """ return ex.imag**2 + ey.imag**2 + ez.imag**2 def output_efield_real_squared(sim): """Output E-field (real part) intensity.""" name = "e_real2_s" if s_pol else "e_real2_p" if p_pol else "e_real2_mixed" func = efield_real_squared cs = [mp.Ex, mp.Ey, mp.Ez] return sim.output_field_function(name, cs, func, real_only=True) def output_efield_imag_squared(sim): """Output E-field (imag part) intensity.""" name = "e_imag2_s" if s_pol else "e_imag2_p" if p_pol else "e_imag2_mixed" func = efield_imag_squared cs = [mp.Ex, mp.Ey, mp.Ez] return sim.output_field_function(name, cs, func, real_only=True) run_args = [ #mp.at_beginning(mp.output_epsilon), # output of dielectric function #mp.at_end(mp.output_efield_x), # output of E_x component #mp.at_end(mp.output_efield_z), # output of E_y component #mp.at_end(mp.output_efield_y), # output of E_z component mp.at_end(output_efield_real_squared ) # output of electric field intensity ] if force_complex_fields: run_args.append(mp.at_end(output_efield_imag_squared)) sim.run(*run_args, until=runtime) print("\nend time:", datetime.now())
def main(args): resolution = 30 # pixels/um a_start = args.a_start # starting periodicity a_end = args.a_end # ending periodicity s_cav = args.s_cav # cavity length r = args.r # hole radius (units of a) h = args.hh # waveguide height w = args.w # waveguide width dair = 1.00 # air padding dpml = 1.00 # PML thickness Ndef = args.Ndef # number of defect periods a_taper = mp.interpolate(Ndef, [a_start, a_end]) dgap = a_end - 2 * r * a_end Nwvg = args.Nwvg # number of waveguide periods sx = 2 * (Nwvg * a_start + sum(a_taper)) - dgap + s_cav sy = dpml + dair + w + dair + dpml sz = dpml + dair + h + dair + dpml cell_size = mp.Vector3(sx, sy, sz) boundary_layers = [mp.PML(dpml)] nSi = 3.45 Si = mp.Medium(index=nSi) geometry = [ mp.Block(material=Si, center=mp.Vector3(), size=mp.Vector3(mp.inf, w, h)) ] for mm in range(Nwvg): geometry.append( mp.Cylinder(material=mp.air, radius=r * a_start, height=mp.inf, center=mp.Vector3( -0.5 * sx + 0.5 * a_start + mm * a_start, 0, 0))) geometry.append( mp.Cylinder(material=mp.air, radius=r * a_start, height=mp.inf, center=mp.Vector3( +0.5 * sx - 0.5 * a_start - mm * a_start, 0, 0))) for mm in range(Ndef + 2): geometry.append( mp.Cylinder(material=mp.air, radius=r * a_taper[mm], height=mp.inf, center=mp.Vector3( -0.5 * sx + Nwvg * a_start + (sum(a_taper[:mm]) if mm > 0 else 0) + 0.5 * a_taper[mm], 0, 0))) geometry.append( mp.Cylinder(material=mp.air, radius=r * a_taper[mm], height=mp.inf, center=mp.Vector3( +0.5 * sx - Nwvg * a_start - (sum(a_taper[:mm]) if mm > 0 else 0) - 0.5 * a_taper[mm], 0, 0))) lambda_min = 1.46 # minimum source wavelength lambda_max = 1.66 # 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.Ey, center=mp.Vector3()) ] symmetries = [ mp.Mirror(mp.X, +1), mp.Mirror(mp.Y, -1), mp.Mirror(mp.Z, +1) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=sources, dimensions=3, symmetries=symmetries) sim.run(mp.in_volume( mp.Volume(center=mp.Vector3(), size=mp.Vector3(sx, sy, 0)), mp.at_end(mp.output_epsilon, mp.output_efield_y)), mp.after_sources(mp.Harminv(mp.Ey, mp.Vector3(), fcen, df)), until_after_sources=500)
def notch(w): print("#----------------------------------------") print("NOTCH WIDTH: %s nanometers" % (w * 1000)) print("#----------------------------------------") # w is the width of the notch in the waveguide #Waveguide Math e = 0.4 # etch fraction [0 -> 1] h2 = h * (1 - e) #height of the etched region d = 1 #spacing of the waveguides posd = d / 2 #pos half d negd = -d / 2 #neg half d ang = 80 #angle of the sidewall angrad = ang * pi / 180 bottomoffset = h / tan(angrad) c = bottomoffset / 2 r = sqrt(bottomoffset**2 + h**2) fcen = 1 / wavelength df = 0.05 n_e = 2.2012 # refractive index inside waveguide (at wavelength 0.6372) n_c = 1.4569 # refractive index outside waveguide (at wavelength 0.6372) #Waveguide Geometry cell = mp.Vector3(a, H) default_material = mp.Medium(epsilon=n_c**2) core_material = mp.Medium(epsilon=n_e**2) geometry = [ mp.Block(cell, center=mp.Vector3(0, 0), material=default_material), mp.Block(mp.Vector3(a + 2 * h, h), center=mp.Vector3(0, 0), material=core_material), # mp.Block(mp.Vector3(w, e * h), mp.Block(mp.Vector3(w, e * h), center=mp.Vector3(0, h2 / 2), material=default_material) ] # pml_layers = [mp.PML(0.2)] pml_layers = [mp.Absorber(thickness=pmlthickness)] # resolution = 50 r00 = None r01 = None r10 = None r11 = None t00 = None t01 = None t10 = None t11 = None su0 = None sd0 = None su1 = None sd1 = None only_fund = True modes = [0, 1] if only_fund: modes = [0] # for mode in [0, 1]: for mode in modes: if mode == 0: eig_parity = mp.EVEN_Y # Fundamental print("-----------") print("MODE TYPE: FUNDAMENTAL") else: eig_parity = mp.ODD_Y # First Order print("-----------") print("MODE TYPE: FIRST ORDER") sources = [ mp.EigenModeSource(mp.GaussianSource(frequency=fcen, fwidth=df), 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 = 20 # 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 ------------------------ y_list = np.arange(-H / 2, H / 2, 1 / resolution) 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, H), 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, H), component=mp.Ez, cmplx=True)) pt = mp.Vector3(9.75, 0) # Hardcoded? 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) # 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-000000.00.h5 -a gray:.2" )), until=19 * wavelength / 20) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, pt, 1e-3)) else: sim.run(mp.at_beginning(mp.output_epsilon), mp.at_time(100, get_refl_slice), mp.at_time(100, get_tran_slice), until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, pt, 1e-3)) os.system("cp notch-out/notch-ez-000100.00.png " + str(int(w * 1000)) + "-" + str(mode) + ".png") os.system("convert notch-out/notch-ez-*.png " + str(int(w * 1000)) + "-" + str(mode) + ".gif") 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[0]) if len(n_eff_firsts) == 0: n_eff_firsts.append(n_eff_first[0]) ky0_fund = np.absolute(2 * pi / wavelength * sqrt(n_e**2 - n_eff_fund**2)) ky0_first = np.absolute(2 * pi / wavelength * sqrt(n_e**2 - n_eff_first**2)) ky1_fund = np.absolute(2 * pi / wavelength * sqrt(n_eff_fund**2 - n_c**2)) ky1_first = np.absolute(2 * pi / wavelength * sqrt(n_eff_first**2 - n_c**2)) E_fund = lambda y: cos(ky0_fund * y) if np.absolute( y) < h / 2 else cos(ky0_fund * h / 2) * np.exp(-ky1_fund * ( np.absolute(y) - h / 2)) E_first_order = lambda y: sin(ky0_first * y) if np.absolute( y) < h / 2 else sin(ky0_first * h / 2) * np.exp(-ky1_first * ( np.absolute(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("r VECTOR: ", refl_val) # print("t VECTOR: ", tran_val) # print("E0 VECTOR: ", E_fund_vec) # print("E1 VECTOR: ", E_first_order_vec) # plt.plot(y_list, refl_val*100,'bo-',label='reflectance') # plt.plot(y_list, tran_val*1,'ro-',label='transmittance') # plt.plot(y_list, E_fund_vec,'go-',label='E0') # plt.plot(y_list, E_first_order_vec, 'co-', label='E1') # # plt.axis([40.0, 300.0, 0.0, 100.0]) # plt.xlabel("y (um)") # plt.ylabel("Field") # plt.legend(loc="center right") # plt.show() fund_refl_amp = 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 = np.conj( np.dot(refl_val, E_first_order_vec) / np.dot(E_first_order_vec, E_first_order_vec)) fund_tran_amp = np.conj( np.dot(tran_val, E_fund_vec) / np.dot(E_fund_vec, E_fund_vec)) first_order_tran_amp = np.conj( np.dot(tran_val, E_first_order_vec) / np.dot(E_first_order_vec, E_first_order_vec)) # 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) 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 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)) 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)) r00s.append(r00[0]) r01s.append(r01[0]) t00s.append(t00[0]) t01s.append(t01[0]) 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)) r10s.append(r10[0]) r11s.append(r11[0]) t10s.append(t10[0]) t11s.append(t11[0]) 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[0])) f1.write("Re(r00): %s\n" % (np.real(r00))[0]) f1.write("Im(r00): %s\n" % (np.imag(r00))[0]) f1.write("Re(r01): %s\n" % (np.real(r01))[0]) f1.write("Im(r01): %s\n" % (np.imag(r01))[0]) f1.write("Re(t00): %s\n" % (np.real(t00))[0]) f1.write("Im(t00): %s\n" % (np.imag(t00))[0]) f1.write("Re(t01): %s\n" % (np.real(t01))[0]) f1.write("Im(t01): %s\n" % (np.imag(t01))[0]) 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[0])) f1.write("Re(r10): %s\n" % (np.real(r10))[0]) f1.write("Im(r10): %s\n" % (np.imag(r10))[0]) f1.write("Re(r11): %s\n" % (np.real(r11))[0]) f1.write("Im(r11): %s\n" % (np.imag(r11))[0]) f1.write("Re(t10): %s\n" % (np.real(t10))[0]) f1.write("Im(t10): %s\n" % (np.imag(t10))[0]) f1.write("Re(t11): %s\n" % (np.real(t11))[0]) f1.write("Im(t11): %s\n" % (np.imag(t11))[0]) 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()
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)) # -------------------------------------------------------------------------- e_z = args.e_z e_y = args.e_y m_charge = args.m_charge ref_medium = args.ref_medium n1 = args.n1 n2 = args.n2 kw_0 = args.kw_0 kr_w = args.kr_w # angle of incidence chi_deg = args.chi_deg #chi_deg = 1.0*op.critical(n1, n2) #chi_deg = 0.95*op.brewster(n1, n2) # -------------------------------------------------------------------------- # 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 sz = 4 # size of cell including PML in z-direction pml_thickness = 0.25 # thickness of PML layer freq = 5 # vacuum frequency of source (default 5) 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) shift = source_shift + r_w chi_rad = math.radians(chi_deg) s_pol = True if (e_z == 1 and e_y == 0) else False p_pol = True if (e_z == 0 and e_y == 1) else False params = dict(W_y=w_0, m=m_charge, 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, sz) # geometry-lattice default_material = mp.Medium(index=n1) # located at lower right edge for 45 degree tilt 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))] # -------------------------------------------------------------------------- # 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) / 3 # -------------------------------------------------------------------------- # display values of physical variables # -------------------------------------------------------------------------- print() print("Expected output file size:", round(8*(sx*sy*sz*resolution**3)/(1024**2)), "MiB") print() print("Specified variables and derived values:") print("n1:", n1) print("n2:", n2) print("chi: ", chi_deg, " [degree]") # interface inclination with respect to the x-axis print("incl.:", 90 - chi_deg, " [degree]") print("kw_0: ", kw_0) print("kr_w: ", kr_w) print("k_vac:", k_vac) print("vortex charge:", m_charge) print("Jones vector components: " "(e_z=", e_z, ", e_y=", e_y, ")", sep="", end="") print(" --->", ("s-" if s_pol else "p-" if p_pol else "mixed-") + "polarisation") print("degree of linear polarisation at pi/4:", 2*(-e_z.conjugate()*e_y).real) print("degree of circular polarisation:", 2*(-e_z.conjugate()*e_y).imag) # -------------------------------------------------------------------------- # exploiting symmetries to reduce computational effort # (only possible for beams without intrinsic orbital angular momentum, i.e. # no vortex charge) # -------------------------------------------------------------------------- # The plane of incidence (x-y-plane) is a mirror plane which is characterised # to be orthogonal to the z-axis (symmetry of the geometric structure). # Symmetry of the sources must be ensured simultaneously, which is only # possible for certain cases. If I am not mistaken this can only be achieved # for vortex free beams with pure s- or p-polarisation, i.e. where either # the Ez or Ey component is specified. symmetries = [] if m_charge == 0: if s_pol: symmetries.append(mp.Mirror(mp.Z, phase=-1)) if p_pol: symmetries.append(mp.Mirror(mp.Y, phase=+1)) # -------------------------------------------------------------------------- # specify current source, output functions and run simulation # -------------------------------------------------------------------------- force_complex_fields = True # default: True eps_averaging = True # default: True sources = [] # specify optical beam LGbeam = op.LaguerreGauss3d(x=shift, params=params) if e_z != 0: source_Ez = mp.Source(src=mp.ContinuousSource(frequency=freq, width=0.5), component=mp.Ez, amplitude=e_z, size=mp.Vector3(0, 3, 3), center=mp.Vector3(source_shift, 0, 0), amp_func=LGbeam.profile ) sources.append(source_Ez) if e_y != 0: source_Ey = mp.Source(src=mp.ContinuousSource(frequency=freq, width=0.5), component=mp.Ey, amplitude=e_y, size=mp.Vector3(0, 3, 3), center=mp.Vector3(source_shift, 0, 0), amp_func=LGbeam.profile ) sources.append(source_Ey) sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, symmetries=symmetries, default_material=default_material, Courant=Courant, geometry=geometry, sources=sources, resolution=resolution, force_complex_fields=force_complex_fields, eps_averaging=eps_averaging ) sim.use_output_directory() # put output files in a separate folder def efield_real_squared(r, ex, ey, ez): """Calculate |Re E|^2. With |.| denoting the complex modulus if 'force_complex_fields?' is set to true, otherwise |.| gives the Euclidean norm. """ return ex.real**2 + ey.real**2 + ez.real**2 def efield_imag_squared(r, ex, ey, ez): """Calculate |Im E|^2. With |.| denoting the complex modulus if 'force_complex_fields?' is set to true, otherwise |.| gives the Euclidean norm. """ return ex.imag**2 + ey.imag**2 + ez.imag**2 def output_efield_real_squared(sim): """Output E-field (real part) intensity.""" name = "e_real2_s" if s_pol else "e_real2_p" if p_pol else "e_real2_mixed" func = efield_real_squared cs = [mp.Ex, mp.Ey, mp.Ez] return sim.output_field_function(name, cs, func, real_only=True) def output_efield_imag_squared(sim): """Output E-field (imag part) intensity.""" name = "e_imag2_s" if s_pol else "e_imag2_p" if p_pol else "e_imag2_mixed" func = efield_imag_squared cs = [mp.Ex, mp.Ey, mp.Ez] return sim.output_field_function(name, cs, func, real_only=True) run_args = [#mp.at_beginning(mp.output_epsilon), # output of dielectric function #mp.at_end(mp.output_efield_x), # output of E_x component #mp.at_end(mp.output_efield_z), # output of E_y component #mp.at_end(mp.output_efield_y), # output of E_z component mp.at_end(output_efield_real_squared) # output of electric field intensity ] if force_complex_fields: run_args.append(mp.at_end(output_efield_imag_squared)) sim.run(*run_args, until=runtime) print("\nend time:", datetime.now())
sources = [mp.Source(mp.ContinuousSource(frequency=0.15), component=mp.Ez, center=mp.Vector3(-7,0))] pml_layers = [mp.PML(1.0)] 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_end(mp.output_efield_z), until=200) import h5py import numpy as np import matplotlib.pyplot as plt eps_h5file = h5py.File('straight_waveguide-eps-000000.00.h5','r') eps_data = np.array(eps_h5file['eps']) plt.figure(dpi=100) plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') plt.axis('off') plt.show() ez_h5file = h5py.File('straight_waveguide-ez-000200.00.h5','r')
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): 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 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()
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 test_extra_materials(self): sim = self.init_simple_simulation() sim.extra_materials = [mp.Medium(epsilon=5), mp.Medium(epsilon=10)] sim.run(mp.at_end(lambda sim: None), until=5)
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 test_pw_source(self): self.sim.run(mp.at_end(mp.output_efield_z), until=400)
return _pw_amp fcen = 0.8 # pulse center frequency df = 0.02 # turn-on bandwidth kdir = mp.Vector3(1, 1) # direction of k (length is irrelevant) n = 1 # refractive index of material containing the source k = kdir.unit().scale(2 * math.pi * fcen * n) # k with correct length sources = [ mp.Source(mp.ContinuousSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(-0.5 * s, 0), size=mp.Vector3(0, s), amp_func=pw_amp(k, mp.Vector3(x=-0.5 * s))), mp.Source(mp.ContinuousSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(0, -0.5 * s), size=mp.Vector3(s, 0), amp_func=pw_amp(k, mp.Vector3(y=-0.5 * s))) ] sim = mp.Simulation(cell_size=cell, sources=sources, boundary_layers=pml_layers, resolution=resolution) t = 400 # run time sim.run(mp.at_end(mp.output_efield_z), until=t)
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)) # -------------------------------------------------------------------------- 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 # Airy beam parameters M = args.M W = args.W # angle of incidence chi_deg = args.chi_deg #chi_deg = 1.0*op.critical(n1, n2) #chi_deg = 0.95*op.brewster(n1, n2) # -------------------------------------------------------------------------- # specific Meep parameters (may need to be adjusted) # -------------------------------------------------------------------------- sx = 10 # size of cell including PML in x-direction sy = 10 # size of cell including PML in y-direction pml_thickness = 0.25 # thickness of PML layer freq = 12 # vacuum frequency of source (4 to 12 is good) runtime = 90 # runs simulation for X times freq periods # number of pixels per wavelength in the denser medium (at least 10, # 20 to 30 is a good choice) pixel = 15 # 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 = 0 source_shift = -0.4 * (sx - 2 * pml_thickness) # -------------------------------------------------------------------------- # 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) shift = source_shift + r_w chi_rad = math.radians(chi_deg) params = dict(W_y=w_0, k=k1, M=M, W=W) # -------------------------------------------------------------------------- # 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 default_material = mp.Medium(index=n1) geometry = [ mp.Block(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)) ] # -------------------------------------------------------------------------- # 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 # -------------------------------------------------------------------------- # 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) print("kr_w: ", kr_w) print("k_vac:", k_vac) print("polarisation:", "s" if s_pol else "p") print() # -------------------------------------------------------------------------- # specify current source, output functions and run simulation # -------------------------------------------------------------------------- force_complex_fields = False # default: False eps_averaging = True # default: True filename_prefix = None # specify optical beam beam = op.IncAiry2d(x=shift, params=params) sources = [ mp.Source(src=mp.ContinuousSource(frequency=freq, width=0.5), component=mp.Ez if s_pol else mp.Ey, size=mp.Vector3(0, 9, 0), center=mp.Vector3(source_shift, 0, 0), amp_func=beam.profile) ] 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() # 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())