def sim(separation): """perform scattering simulation""" L = separation + 2*radius + 2*pml_monitor_gap + 2*particle_monitor_gap + 2*pml.thickness cell = meep.Vector3(L,L,L) p1 = meep.Vector3(-separation/2, 0, 0) p2 = meep.Vector3(separation/2, 0, 0) geometry = [meep.Sphere(center=p1, radius=radius, material=gold), meep.Sphere(center=p2, radius=radius, material=gold)] scat = meep.Simulation(cell_size=cell, boundary_layers=[pml], geometry=geometry, resolution=resolution) scat.init_fields() source(scat) L = 2*radius + 2*particle_monitor_gap Fx = meep_ext.add_force_box(scat, fcen, 0, 1, p2, [L,L,L], meep.X) Fy = meep_ext.add_force_box(scat, fcen, 0, 1, p2, [L,L,L], meep.Y) Fz = meep_ext.add_force_box(scat, fcen, 0, 1, p2, [L,L,L], meep.Z) # scat.run(until_after_sources=8*um) scat.run(until_after_sources=meep.stop_when_fields_decayed(.5*um, meep.Ex, pt=p2-meep.Vector3(0,0,L/2), decay_by=1e-5)) return {'Fx': np.array(meep.get_forces(Fx))[0], 'Fy': np.array(meep.get_forces(Fy))[0], 'Fz': np.array(meep.get_forces(Fz))[0]}
def sim(separation, monitor_size, unique_id): """perform scattering simulation""" monitor_size = np.asarray(monitor_size) cell_size = monitor_size + 2*pml_monitor_gap + 2*pml.thickness cell = meep.Vector3(*cell_size) p1 = meep.Vector3(-separation/2, 0, 0) p2 = meep.Vector3(separation/2, 0, 0) geometry = [meep.Sphere(center=p1, radius=radius, material=gold), meep.Sphere(center=p2, radius=radius, material=gold)] scat = meep.Simulation(cell_size=cell, boundary_layers=[pml], geometry=geometry, default_material=medium, resolution=resolution) scat.init_fields() source(scat) flux_box_absorb = meep_ext.add_flux_box(scat, fcen, 0, 1, [0,0,0], monitor_size) flux_box_scat = meep_ext.add_flux_box(scat, fcen, 0, 1, [0,0,0], monitor_size) scat.load_minus_flux(norm_file_ext.format(unique_id), flux_box_scat) # scat.run(until_after_sources=8*um) scat.run(until_after_sources=meep.stop_when_fields_decayed(.5*um, polarization, pt=p2 - meep.Vector3(0,0,monitor_size[2]/2), decay_by=1e-4)) return {'scattering': np.array(meep.get_fluxes(flux_box_scat)), 'absorption': -np.array(meep.get_fluxes(flux_box_absorb))}
def test_epsilon_warning(self): ## fields blow up using dispersive material ## when compiled using single precision with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") from meep.materials import Si self.assertEqual(len(w), 0) from meep.materials import Mo geom = [mp.Sphere(radius=0.2, material=Mo)] sim = self.init_simple_simulation(geometry=geom) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sim.run(until=5) self.assertGreater(len(w), 0) self.assertIn("Epsilon", str(w[0].message)) from meep.materials import SiO2 geom = [mp.Sphere(radius=0.2, material=SiO2)] sim = self.init_simple_simulation(geometry=geom) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sim.run(until=5) self.assertEqual(len(w), 1) self.assertNotIn("Epsilon", str(w[0].message))
def test_check_material_frequencies(self): mat = mp.Medium(valid_freq_range=mp.FreqRange(min=10, max=20)) invalid_sources = [ [mp.Source(mp.GaussianSource(5, fwidth=1), mp.Ez, mp.Vector3())], [mp.Source(mp.ContinuousSource(10, fwidth=1), mp.Ez, mp.Vector3())], [mp.Source(mp.GaussianSource(10, width=1), mp.Ez, mp.Vector3())], [mp.Source(mp.GaussianSource(20, width=1), mp.Ez, mp.Vector3())], ] cell_size = mp.Vector3(5, 5) resolution = 5 def check_warnings(sim, should_warn=True): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sim.run(until=5) if should_warn: self.assertEqual(len(w), 1) self.assertIn("material", str(w[-1].message)) else: self.assertEqual(len(w), 0) geom = [mp.Sphere(0.2, material=mat)] for s in invalid_sources: # Check for invalid extra_materials sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=s, extra_materials=[mat]) check_warnings(sim) # Check for invalid geometry materials sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=s, geometry=geom) check_warnings(sim) valid_sources = [ [mp.Source(mp.GaussianSource(15, fwidth=1), mp.Ez, mp.Vector3())], [mp.Source(mp.ContinuousSource(15, width=5), mp.Ez, mp.Vector3())] ] for s in valid_sources: sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=s, extra_materials=[mat]) check_warnings(sim, False) # Check DFT frequencies # Invalid extra_materials sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=valid_sources[0], extra_materials=[mat]) fregion = mp.FluxRegion(center=mp.Vector3(0, 1), size=mp.Vector3(2, 2), direction=mp.X) sim.add_flux(18, 6, 2, fregion) check_warnings(sim) # Invalid geometry material sim = mp.Simulation(cell_size=cell_size, resolution=resolution, sources=valid_sources[0], geometry=geom) sim.add_flux(18, 6, 2, fregion) check_warnings(sim)
def __init__(self, dir_name, wavelength, cyt_absorb): self.base_directory = base_directory + str(dir_name) self.wavelength = wavelength self.cyt_absorb = cyt_absorb self.frequency = 1 / wavelength # Calculate wavelengths dependent on RI self.wavelength_in_media = wavelength / ri_media self.wavelength_in_cytoplasm = wavelength / ri_cytoplasm max_freq = self.frequency - 0.01 min_freq = self.frequency + 0.01 self.pulse_width = abs(max_freq - min_freq) cell = mp.Vector3(sxx, sxy, 0) pml_layers = [mp.PML(dpml)] cytoplasm_material = mp.Medium(index=ri_cytoplasm, D_conductivity=2 * math.pi * self.frequency * (cyt_absorb / (ri_cytoplasm ** 2))) cytoplasm_region = mp.Sphere(radius=cell_radius, center=mp.Vector3(0, 0), material=cytoplasm_material) geometry = [cytoplasm_region] # Sources kdir = mp.Vector3(1, 0, 0) # direction of k (length is irrelevant) n = ri_media # refractive index of material containing the source k = kdir.unit().scale(2 * math.pi * self.frequency * n) # k with correct length def pw_amp(k, x0): def _pw_amp(x): return cmath.exp(1j * k.dot(x + x0)) return _pw_amp source = [ mp.Source( mp.ContinuousSource(frequency=self.frequency, fwidth=self.pulse_width), # along x axis component=mp.Ez, center=mp.Vector3(-0.5 * simulation_size, 0), # x, ,y ,z size=mp.Vector3(0, sxy, 0), amp_func=pw_amp(k, mp.Vector3(x=-0.5 * simulation_size)) ) ] sim = mp.Simulation( cell_size=cell, sources=source, boundary_layers=pml_layers, resolution=resolution, geometry=geometry, default_material=mp.Medium(index=ri_media), force_complex_fields=complex_f, eps_averaging=eps_av, ) sim.use_output_directory(self.base_directory) sim.run(mp.at_every(0.6, mp.output_png(mp.Ez, "-Zc/data/home/bt16268/simInput/customColour.txt")), until=t)
def sweep_dis(l, sources, dis_mat, tstop, span, pos, frq): if sources == "x": sources_use = sourcesx if sources == "z": sources_use = sourcesz geometryAu = [ mp.Sphere(material=Au, center=mp.Vector3(x=r + dis_mat[l]), radius=r) ] frqs, p_flux = purcell_cal(sources_use, geometryAu, tstop, span, pos, frq) return p_flux
def main(args): sx = 4.0 #spatial extent along x including pmls (μm) sy = 4.0 sz = 4.0 dpml = 1.0 cell = mp.Vector3(sx, sy, sz) pml_layers = [mp.PML(dpml)] geometry = [ mp.Sphere(radius=0.7, center=mp.Vector3(0, 0, 0), material=Graph), mp.Sphere(radius=0.5, center=mp.Vector3(0, 0, 0), material=mp.Medium(epsilon=12.25)) ] resolution = args.res wvl = args.wvl / 1000 #convert args.wvl from nm to μm fcen = 1 / wvl df = 0.1 * fcen nfreq = 1 print("wavelength =", wvl, "μm") print("center frequency =", fcen, "1/μm") source = [ mp.Source(mp.GaussianSource(fcen, df, nfreq), component=mp.Ex, center=mp.Vector3(0, 0.705, 0)) ] symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)] sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=source, resolution=resolution, boundary_layers=pml_layers) pt = mp.Vector3(0, 0.75, 0) sim.run(mp.dft_ldos(fcen, df, nfreq), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ex, pt, 1e-9))
def __init__(self, dir_name, wavelength, cyt_absorb): self.base_directory = base_directory + str(dir_name) self.wavelength = wavelength self.cyt_absorb = cyt_absorb self.frequency = 1 / wavelength # Calculate wavelengths dependent on RI self.wavelength_in_media = wavelength / ri_media self.wavelength_in_cytoplasm = wavelength / ri_cytoplasm max_freq = self.frequency - 0.01 min_freq = self.frequency + 0.01 self.pulse_width = abs(max_freq - min_freq) cell = mp.Vector3(sxx, sxy, 0) pml_layers = [mp.PML(dpml)] cytoplasm_material = mp.Medium(index=ri_cytoplasm, D_conductivity=2 * math.pi * self.frequency * (cyt_absorb / (ri_cytoplasm**2))) cytoplasm_region = mp.Sphere(radius=cell_radius, center=mp.Vector3(0, 0), material=cytoplasm_material) geometry = [] source = [ mp.Source(mp.ContinuousSource(frequency=self.frequency, fwidth=self.pulse_width), compone=mp.Ez, center=(mp.Vector3(-0.5 * simulation_size, 0)), size=mp.Vector3(0, 10)) ] sim = mp.Simulation( cell_size=cell, sources=source, boundary_layers=pml_layers, resolution=resolution, geometry=geometry, default_material=mp.Medium(index=ri_media), force_complex_fields=complex_f, eps_averaging=eps_av, ) sim.use_output_directory(self.base_directory) sim.run(mp.at_every( 0.6, mp.output_png(mp.Ez, "-Zc/data/home/bt16268/simInput/customColour.txt")), until=t)
def test_epsilon_warning(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") from materials_library import Si self.assertEqual(len(w), 0) from materials_library import Mo geom = [mp.Sphere(radius=0.2, material=Mo)] sim = self.init_simple_simulation(geometry=geom) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sim.run(until=5) self.assertGreater(len(w), 0) self.assertIn("Epsilon", str(w[0].message)) from materials_library import SiO2 geom = [mp.Sphere(radius=0.2, material=SiO2)] sim = self.init_simple_simulation(geometry=geom) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sim.run(until=5) self.assertEqual(len(w), 1) self.assertNotIn("Epsilon", str(w[0].message))
def test_geometric_object_duplicates_xyz(self): rad = 1 s = mp.Sphere(rad) res = mp.geometric_object_duplicates(mp.Vector3(1, 1, 1), 1, 5, s) expected = [ mp.Sphere(rad, center=mp.Vector3(1, 1, 1)), mp.Sphere(rad, center=mp.Vector3(2, 2, 2)), mp.Sphere(rad, center=mp.Vector3(3, 3, 3)), mp.Sphere(rad, center=mp.Vector3(4, 4, 4)), mp.Sphere(rad, center=mp.Vector3(5, 5, 5)) ] for r, e in zip(res, expected): self.assertEqual(r.center, e.center)
def test_geometric_object_duplicates_x(self): rad = 1 s = mp.Sphere(rad) res = mp.geometric_object_duplicates(mp.Vector3(x=1), 1, 5, s) expected = [ mp.Sphere(rad, center=mp.Vector3(x=1)), mp.Sphere(rad, center=mp.Vector3(x=2)), mp.Sphere(rad, center=mp.Vector3(x=3)), mp.Sphere(rad, center=mp.Vector3(x=4)), mp.Sphere(rad, center=mp.Vector3(x=5)) ] for r, e in zip(res, expected): self.assertEqual(r.center, e.center)
def geo2D_spherical_pc(n_matrix, n_substrate, film_xsize, substrate_xsize, film_ysize, n_layers, film_base_x, sph_radius, sph_spacing, layer_offset, sph_x_offset=0): """ Generate a layered structure with spheres. Within a layer the spheres are hexagonally packed """ matrix_block = mp.Block(size=mp.Vector3(film_xsize, film_ysize, 1e20), center=mp.Vector3(film_base_x - film_xsize / 2), material=mp.Medium(epsilon=n_matrix**2)) si_block = mp.Block(size=mp.Vector3(substrate_xsize, film_ysize, 1e20), center=mp.Vector3(film_base_x + substrate_xsize / 2, 0, 0), material=mp.Medium(epsilon=n_substrate**2)) print(n_substrate) spheres = [si_block, matrix_block] #spheres = [] for n in range(n_layers): x = film_base_x - film_xsize * (n + 0.5) / n_layers + sph_x_offset offset = layer_offset[n] num_spheres = int(film_ysize / sph_spacing) sphere_layer = [ mp.Sphere(radius=sph_radius, center=mp.Vector3(x, y + offset - film_ysize / 2, 0), material=mp.Medium(epsilon=1)) for y in np.linspace(0, film_ysize, num_spheres) ] spheres = spheres + sphere_layer return spheres
import meep as mp import numpy as np import math cell_size = mp.Vector3(2, 2, 2) geometry = [ #mp.Block(center=mp.Vector3(0,0,0), size=cell_size, material=mp.air), mp.Sphere(center=mp.Vector3(0, 0, 0), radius=1, material=mp.metal) ] sim = mp.Simulation(resolution=50, cell_size=cell_size, geometry=geometry) sim.init_sim() eps_data = sim.get_epsilon() #eps_data = sim.get_array() #sim.plot3D(eps_data) #from mayavi import mlab #s = mlab.contour3d() #mlab.show() #mlab.savefig(filename='test2.png') from mayavi import mlab s = mlab.contour3d(eps_data, colormap="hsv") #mlab.show() mlab.savefig('Sphere3D.pdf')
def compute_resonant_mode_3d(use_matgrid=True): resolution = 25 wvl = 1.27 fcen = 1 / wvl df = 0.02 * fcen nSi = 3.45 Si = mp.Medium(index=nSi) nSiO2 = 1.45 SiO2 = mp.Medium(index=nSiO2) s = 1.0 cell_size = mp.Vector3(s, s, s) rad = 0.34 # radius of sphere if use_matgrid: matgrid_resolution = 2 * resolution N = int(s * matgrid_resolution) coord = np.linspace(-0.5 * s, 0.5 * s, N) xv, yv, zv = np.meshgrid(coord, coord, coord) weights = np.sqrt(np.square(xv) + np.square(yv) + np.square(zv)) < rad filtered_weights = gaussian_filter(weights, sigma=4 / resolution, output=np.double) matgrid = mp.MaterialGrid(mp.Vector3(N, N, N), SiO2, Si, weights=filtered_weights, do_averaging=True, beta=1000, eta=0.5) geometry = [ mp.Block(center=mp.Vector3(), size=cell_size, material=matgrid) ] else: geometry = [mp.Sphere(center=mp.Vector3(), radius=rad, material=Si)] sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), size=mp.Vector3(), center=mp.Vector3(0.13, 0.25, 0.06), component=mp.Ez) ] k_point = mp.Vector3(0.23, -0.17, 0.35) sim = mp.Simulation(resolution=resolution, cell_size=cell_size, sources=sources, default_material=SiO2, k_point=k_point, geometry=geometry) h = mp.Harminv(mp.Ez, mp.Vector3(-0.2684, 0.1185, 0.0187), fcen, df) sim.run(mp.after_sources(h), until_after_sources=200) try: for m in h.modes: print("harminv:, {}, {}, {}".format(resolution, m.freq, m.Q)) freq = h.modes[0].freq except: raise RuntimeError("No resonant modes found.") return freq
center=mp.Vector3(0, 0, -(sizeZ / 2 + 6 * addFlux)), #3D case # center=mp.Vector3(-sizeX/2, 0, 0), size=mp.Vector3(sizeX, sizeY, 0), amplitude=1) ] #sources = create_sources(freq, df, 0.7, 5, resolution, 4, 0, 0, 0, 24, 0) #sys.stderr.write(np.str(mp.Vector3(z=-0.5*sizeZ-addFlux/2))) #sys.stderr.write(np.str(mp.Vector3(sizeX+addFlux/2, sizeY+addFlux/2, 0))) geometry = [] while Z < sizeZ / 2 - 1: for i in range(N): coord[0, i] = np.random.rand() * sizeX - sizeX / 2 coord[1, i] = np.random.rand() * sizeY - sizeY / 2 coord[2, i] = Z bufferShape = mp.Sphere(center=mp.Vector3(coord[0, i], coord[1, i], coord[2, i]), radius=radius, material=mp.Medium(epsilon=1.44)) geometry.append(bufferShape) Z = Z + dZ # for i in range(N): # coord[0, i] = np.random.rand()*sizeX-sizeX/2 # coord[1, i] = np.random.rand()*sizeY-sizeY/2 # coord[2, i] = Z # bufferShape = mp.Sphere(center=mp.Vector3(coord[0, i], coord[1, i], coord[2, i]), # radius=radius, # material=mp.Medium(epsilon=12)) # geometry.append(bufferShape) pml_layers = [mp.PML(thicknessPML)] sim = mp.Simulation( cell_size=cell,
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"))
weight=+1), mp.Near2FarRegion(center=mp.Vector3(z=+2 * r), size=mp.Vector3(4 * r, 4 * r, 0), weight=-1)) sim.run(until_after_sources=10) input_flux = mp.get_fluxes(box_flux)[0] nearfield_box_data = sim.get_near2far_data(nearfield_box) sim.reset_meep() n_sphere = 2.0 geometry = [ mp.Sphere(material=mp.Medium(index=n_sphere), center=mp.Vector3(), radius=r) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=mp.Vector3(), geometry=geometry) nearfield_box = sim.add_near2far( frq_cen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(x=-2 * r), size=mp.Vector3(0, 4 * r, 4 * r), weight=+1),
import meep as mp import numpy as np import matplotlib.pyplot as plt import argparse from meep.materials import Ag, BK7 sx = 4.0 #spatial extent along x including pmls (μm) sy = 4.0 sz = 0.0 dpml = 1.0 cell = mp.Vector3(sx, sy, sz) pml_layers = [mp.PML(dpml)] geometry = [ mp.Sphere(radius=0.7, center=mp.Vector3(0, 0, 0), material=Ag), mp.Sphere(radius=0.5, center=mp.Vector3(0, 0, 0), material=mp.Medium(epsilon=12.25)) ] resolution = 200 wvl = 825 / 1000 #convert args.wvl from nm to μm fcen = 1 / wvl df = 0 nfreq = 1 print("wavelength =", wvl, "μm") print("center frequency =", fcen, "1/μm") source = [ mp.Source(mp.GaussianSource(fcen, df, nfreq), component=mp.Ey, center=mp.Vector3(0, -0.705, 0))
cell_size = mp.Vector3(cell_width, cell_width, cell_width) source_center = -0.5 * cell_width + pml_width print("Resto Source Center: {}".format(source_center % (1 / resolution))) sources = [ mp.Source(mp.ContinuousSource(wavelength=wlen, is_integrated=True), center=mp.Vector3(source_center), size=mp.Vector3(0, cell_width, cell_width), component=mp.Ez) ] # Ez-polarized monochromatic planewave # (its size parameter fills the entire cell in 2d) # >> The planewave source extends into the PML # ==> is_integrated=True must be specified geometry = [mp.Sphere(material=medium, center=mp.Vector3(), radius=r)] # Au sphere with frequency-dependant characteristics imported from Meep. path = os.path.join(home, folder, series) if not os.path.isdir(path): vs.new_dir(path) file = lambda f: os.path.join(path, f) #%% SAVE GET FUNCTIONS def get_line(sim): return sim.get_array(center=mp.Vector3(), size=mp.Vector3(cell_width), component=mp.Ez)
def simulate(self, config): start = time.time() dpml = config["dpml"] resolution = config["resolution"] use_fixed_time = config["use_fixed_time"] simulation_time = config["simulation_time"] padding = config["padding"] ff_pts = config["ff_pts"] ff_cover = config["ff_cover"] ff_calc = config["ff_calc"] use_symmetries = config["use_symmetries"] calculate_flux = config["calculate_flux"] calculate_source_flux = config["calculate_source_flux"] pixels = config["source_flux_pixel_size"] ff_calculations = config["ff_calculations"] ff_angle = math.pi / config["ff_angle"] FibonacciSampling = config["fibb_sampling"] simulation_ratio = eval(config["simulation_ratio"]) substrate_ratio = eval(config["substrate_ratio"]) output_ff = config["output_ff"] polarization_in_plane = config["polarization_in_plane"] geometry = config["geometry"] material_function = config["material_function"] substrate_height = self.pyramid_height * substrate_ratio #height of the substrate, measured as fraction of pyramid height #"Cell size" sx = self.pyramid_width * simulation_ratio #size of the cell in xy-plane is measured as a fraction of pyramid width sy = sx sz = self.pyramid_height * simulation_ratio #z-"height" of sim. cell measured as a fraction of pyramid height. sh = substrate_height sh = 0 padding = padding ##distance from pml_layers to flux regions so PML don't overlap flux regions cell = mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, sz + 2 * dpml) #size of the simulation cell in meep units if self.CL_material == "Au": CL_material = Au elif self.CL_material == "Ag": CL_material = Ag elif self.CL_material == "Ag_visible": CL_material = Ag_visible else: CL_material = GaN print('CL MATERIAL:', CL_material, self.CL_material) #Symmetries for the simulation def create_symmetry(self): if self.source_direction == mp.Ex or self.source_direction == [ 1, 0, 0 ]: symmetry = [mp.Mirror(mp.Y), mp.Mirror(mp.X, phase=-1)] elif self.source_direction == mp.Ey or self.source_direction == [ 0, 1, 0 ]: symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y, phase=-1)] elif self.source_direction == mp.Ez or self.source_direction == [ 0, 0, 1 ]: symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] else: symmetry = [] return symmetry #Flux regions to calculate the total flux emitted from the simulation def define_flux_regions(sx, sy, sz, padding): fluxregion = [] fluxregion.append( mp.FluxRegion( #region x to calculate flux from center=mp.Vector3(sx / 2 - padding, 0, 0), size=mp.Vector3(0, sy - padding * 2, sz - padding * 2), direction=mp.X)) fluxregion.append( mp.FluxRegion( # region -x to calculate flux from center=mp.Vector3(-sx / 2 + padding, 0, 0), size=mp.Vector3(0, sy - padding * 2, sz - padding * 2), direction=mp.X, weight=-1)) fluxregion.append( mp.FluxRegion( #region y to calculate flux from center=mp.Vector3(0, sy / 2 - padding, 0), size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2), direction=mp.Y)) fluxregion.append( mp.FluxRegion( #region -y to calculate flux from center=mp.Vector3(0, -sy / 2 + padding, 0), size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2), direction=mp.Y, weight=-1)) fluxregion.append( mp.FluxRegion( #z-bottom region to calculate flux from center=mp.Vector3(0, 0, sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z)) fluxregion.append( mp.FluxRegion( #z-top region to calculate flux from center=mp.Vector3(0, 0, -sz / 2 + padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z, weight=-1)) return fluxregion #Flux regions to calculate the far field flux def define_nearfield_regions(sx, sy, sz, sh, padding, ff_cover): nearfieldregions = [] nfrAbove = [] nfrBelow = [] #if ff_calc == True: #If we wish to calculate the far-field above and below the pyramid # if ff_calc == "Both": nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0, -sh / 2), size=mp.Vector3(0, sy - padding * 2, sz - sh - padding * 2), direction=mp.X)) nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0, -sh / 2), size=mp.Vector3(0, sy - padding * 2, sz - sh - padding * 2), direction=mp.X, weight=-1)) nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding, -sh / 2), size=mp.Vector3(sx - padding * 2, 0, sz - sh - padding * 2), direction=mp.Y)) nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding, -sh / 2), size=mp.Vector3(sx - padding * 2, 0, sz - sh - padding * 2), direction=mp.Y, weight=-1)) nfrAbove.append( mp.Near2FarRegion( #nearfield -z. above pyramid. center=mp.Vector3(0, 0, -sz / 2 + padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z, weight=-1)) #Far-field to calculate below transmissions nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0, sz / 2 - sh / 2), size=mp.Vector3(0, sy - padding * 2, sh - padding * 2), direction=mp.X)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0, sz / 2 - sh / 2), size=mp.Vector3(0, sy - padding * 2, sh - padding * 2), direction=mp.X, weight=-1)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding, sz / 2 - sh / 2), size=mp.Vector3(sx - padding * 2, 0, sh - padding * 2), direction=mp.Y)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding, sz / 2 - sh / 2), size=mp.Vector3(sx - padding * 2, 0, sh - padding * 2), direction=mp.Y, weight=-1)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3(0, 0, sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z)) if ff_cover == True: nfrAbove.append( mp.Near2FarRegion(center=mp.Vector3( 0, 0, sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z)) nfrBelow.append( mp.Near2FarRegion(center=mp.Vector3( 0, 0, -sz / 2 - padding), size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0), direction=mp.Z, weight=-1)) nearfieldregions.append(nfrAbove) nearfieldregions.append(nfrBelow) return nearfieldregions ###GEOMETRY FOR THE SIMULATION################################################# #"Material parameters" air = mp.Medium(epsilon=1) #air dielectric value SubstrateEps = GaN #substrate epsilon #"Geometry to define the substrate and block of air to truncate the pyramid if self.truncation =/= 0" geometries = [] #Substrate geometries.append( mp.Sphere(center=mp.Vector3(0, 0, self.distance / 2 + self.radius), radius=self.radius, material=CL_material)) geometries.append( mp.Sphere(center=mp.Vector3(0, 0, -self.distance / 2 - self.radius), radius=self.radius, material=CL_material)) if geometry == None: geometries = [] if material_function == None: material_function = None ###SYMMETRIES######################################################### #"Symmetry logic." if use_symmetries: symmetry = create_symmetry(self) print('symmetry on:', symmetry) else: symmetry = [] ###PML_LAYERS################################################################### pml_layer = [mp.PML(dpml)] ###SOURCE####################################################################### #"A gaussian with pulse source proportional to exp(-iwt-(t-t_0)^2/(2w^2))" abs_source_position_x = 0 abs_source_position_y = 0 abs_source_position_z = 0 source = [ mp.Source( mp.GaussianSource( frequency=self.frequency_center, fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source component=mp.Ez, amplitude=1, center=mp.Vector3(abs_source_position_x, abs_source_position_y, abs_source_position_z)) ] #source.append(mp.Source(mp.GaussianSource(frequency=self.frequency_center,fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source # component=mp.Ey, # amplitude=self.source_direction[1], # center=mp.Vector3(abs_source_position_x,abs_source_position_y,abs_source_position_z))) #source.append(mp.Source(mp.GaussianSource(frequency=self.frequency_center,fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source # component=mp.Ez, # amplitude=self.source_direction[2], # center=mp.Vector3(abs_source_position_x,abs_source_position_y,abs_source_position_z))) #MEEP simulation constructor sim = mp.Simulation( cell_size=cell, geometry=geometries, symmetries=symmetry, sources=source, #eps_averaging=True, #subpixel_tol=1e-4, #subpixel_maxeval=1000, dimensions=3, #default_material=GaN, Courant=0.1, extra_materials=[CL_material], boundary_layers=pml_layer, split_chunks_evenly=False, resolution=resolution) ###SOURCE REGION################################################### print('pixels', pixels) def define_flux_source_regions(abs_source_position_x, abs_source_position_y, abs_source_position_z, resolution, pixels): distance = pixels * 1 / resolution source_region = [] source_region.append( mp.FluxRegion( #region x to calculate flux from center=mp.Vector3(abs_source_position_x + distance, abs_source_position_y, abs_source_position_z), size=mp.Vector3(0, 2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution), direction=mp.X)) source_region.append( mp.FluxRegion( # region -x to calculate flux from center=mp.Vector3(abs_source_position_x - distance, abs_source_position_y, abs_source_position_z), size=mp.Vector3(0, 2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution), direction=mp.X, weight=-1)) source_region.append( mp.FluxRegion( #region y to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y + distance, abs_source_position_z), size=mp.Vector3(2 * pixels * 1 / resolution, 0, 2 * pixels * 1 / resolution), direction=mp.Y)) source_region.append( mp.FluxRegion( #region -y to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y - distance, abs_source_position_z), size=mp.Vector3(2 * pixels * 1 / resolution, 0, 2 * pixels * 1 / resolution), direction=mp.Y, weight=-1)) source_region.append( mp.FluxRegion( #z-bottom region to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y, abs_source_position_z + distance), size=mp.Vector3(2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution, 0), direction=mp.Z)) source_region.append( mp.FluxRegion( #z-top region to calculate flux from center=mp.Vector3(abs_source_position_x, abs_source_position_y, abs_source_position_z - distance), size=mp.Vector3(2 * pixels * 1 / resolution, 2 * pixels * 1 / resolution, 0), direction=mp.Z, weight=-1)) return source_region ###REGIONS###################################################################### #"These regions define the borders of the cell with distance 'padding' between the flux region and the dpml region to avoid calculation errors." if calculate_flux: flux_regions = define_flux_regions(sx, sy, sz, padding) fr1, fr2, fr3, fr4, fr5, fr6 = flux_regions flux_total = sim.add_flux(self.frequency_center, self.frequency_width, self.number_of_freqs, fr1, fr2, fr3, fr4, fr5, fr6) #calculate flux for flux regions if calculate_source_flux: sr1, sr2, sr3, sr4, sr5, sr6 = define_flux_source_regions( abs_source_position_x, abs_source_position_y, abs_source_position_z, resolution, pixels) flux_source = sim.add_flux(self.frequency_center, self.frequency_width, self.number_of_freqs, sr1, sr2, sr3, sr4, sr5, sr6) ###FAR FIELD REGION############################################################# #"The simulation calculates the far field flux from the regions 1-5 below. It correspons to the air above and at the side of the pyramids. The edge of the simulation cell that touches the substrate is not added to this region. Far-field calculations can not handle different materials." if ff_calculations == True: nearfieldregions = define_nearfield_regions( sx, sy, sz, sh, padding, ff_cover) if ff_cover == True: nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6 = nearfieldregions[0] nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6 = nearfieldregions[1] else: nfrA1, nfrA2, nfrA3, nfrA4, nfrA6 = nearfieldregions[0] nfrB1, nfrB2, nfrB3, nfrB4, nfrB6 = nearfieldregions[1] if ff_calc == "Both" and ff_cover == True: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6) nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6) elif ff_calc == "Above" and ff_cover == True: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6) elif ff_calc == "Above" and ff_cover == False: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA6) elif ff_calc == "Below" and ff_cover == True: nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6) elif ff_calc == "Below" and ff_cover == False: nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB6) #Assumed ff_calc == Both and ff_cover == False else: nearfieldAbove = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrA1, nfrA2, nfrA3, nfrA4, nfrA6) nearfieldBelow = sim.add_near2far(self.frequency_center, self.frequency_width, self.number_of_freqs, nfrB1, nfrB2, nfrB3, nfrB4, nfrB6) ###RUN########################################################################## #"Run the simulation" if True: sim.plot2D(output_plane=mp.Volume( center=mp.Vector3(0, 0 * abs_source_position_y, 0), size=mp.Vector3(0, sy + 2 * dpml, sz + 2 * dpml))) #plt.show() plt.savefig('foo.pdf') sim.plot2D(output_plane=mp.Volume( center=mp.Vector3(0 * abs_source_position_x, 0, 0), size=mp.Vector3(sx + 2 * dpml, 0, sz + 2 * dpml))) #plt.show() plt.savefig('foo2.pdf') sim.plot2D(output_plane=mp.Volume( center=mp.Vector3(0, 0, abs_source_position_z), size=mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, 0))) #sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,sz/2-sh-0.01),size=mp.Vector3(sx+2*dpml,sy+2*dpml,0))) plt.savefig('foo3.pdf') if use_fixed_time: sim.run( mp.dft_ldos(self.frequency_center, self.frequency_width, self.number_of_freqs), # mp.at_beginning(mp.output_epsilon), #until_after_sources=mp.stop_when_fields_decayed(2,mp.Ey,mp.Vector3(0,0,sbs_cource_position+0.2),1e-2)) until=simulation_time) else: #Withdraw maximum dipole amplitude direction max_index = self.source_direction.index(max(self.source_direction)) if max_index == 0: detector_pol = mp.Ex elif max_index == 1: detector_pol = mp.Ey else: detector_pol = mp.Ez #TODO: exchange self.source_direction to maxmimum dipole ampltitude sim.run( mp.dft_ldos(self.frequency_center, self.frequency_width, self.number_of_freqs), #mp.to_appended("ex", mp.at_every(0.6, mp.output_efield_x)), mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 2, detector_pol, mp.Vector3(0, 0, abs_source_position_z + 0.2), 1e-3)) ###OUTPUT CALCULATIONS########################################################## #"Calculate the poynting flux given the far field values of E, H." myIntegration = True nfreq = self.number_of_freqs r = 2 * math.pow( self.pyramid_height, 2 ) * self.frequency_center * 2 * 10 # 10 times the Fraunhofer-distance if ff_calculations: fields = [] P_tot_ff = np.zeros(self.number_of_freqs) npts = ff_pts #number of far-field points Px = 0 Py = 0 Pz = 0 theta = ff_angle phi = math.pi * 2 #"How many points on the ff-sphere" #global xPts #xPts=[] #global yPts #yPts=[] #global zPts #zPts=[] Pr_Array = [] if myIntegration == True: #how to pick ff-points, this uses fibbonaci-sphere distribution if FibonacciSampling == True: if theta == math.pi / 3: offset = 1.5 / npts elif theta == math.pi / 4: npts = npts * 2 offset = 1.15 / npts elif theta == math.pi / 5: npts = npts * 2.5 offset = 0.95 / npts elif theta == math.pi / 6: #npts=npts*3 offset = 0.8 / npts elif theta == math.pi / 7: npts = npts * 3 offset = 0.7 / npts elif theta == math.pi / 8: npts = npts * 3 offset = 0.6 / npts elif theta == math.pi / 12: npts = npts * 3 offset = 0.4 / npts else: offset = 0.2 / npts xPts, yPts, zPts = fibspherepts(r, theta, npts, offset) range_npts = int((theta / math.pi) * npts) else: #check out Lebedev quadrature #if not fibb sampling then spherical sampling theta_pts = npts phi_pts = npts * 2 xPts, yPts, zPts = sphericalpts(r, theta, phi, theta_pts, phi_pts) range_npts = int(theta_pts * phi_pts + 1) if ff_calc == "Both": #Pr_ArrayA for above, B for below Pr_ArrayA = [] Pr_ArrayB = [] P_tot_ffA = [0] * (self.number_of_freqs) P_tot_ffB = [0] * (self.number_of_freqs) for n in range(range_npts): ffA = sim.get_farfield( nearfieldAbove, mp.Vector3(xPts[n], yPts[n], zPts[n])) ffB = sim.get_farfield( nearfieldBelow, mp.Vector3(xPts[n], yPts[n], -1 * zPts[n])) i = 0 for k in range(nfreq): "Calculate the poynting vector in x,y,z direction" PrA = myPoyntingFlux(ffA, i) PrB = myPoyntingFlux(ffB, i) Pr_ArrayA.append(PrA) Pr_ArrayB.append(PrB) "the spherical cap has area 2*pi*r^2*(1-cos(theta))" "divided by npts and we get evenly sized area chunks" surface_Element = 2 * math.pi * pow( r, 2) * (1 - math.cos(theta)) / range_npts P_tot_ffA[k] += surface_Element * (1) * (PrA) P_tot_ffB[k] += surface_Element * (1) * (PrB) i = i + 6 #to keep track of the correct entries in the ff array if ff_calc == "Below": for n in range(range_npts): ff = sim.get_farfield( nearfieldBelow, mp.Vector3(xPts[n], yPts[n], -1 * zPts[n])) fields.append({ "pos": (xPts[n], yPts[n], zPts[n]), "field": ff }) i = 0 for k in range(nfreq): "Calculate the poynting vector in x,y,z direction" Pr = myPoyntingFlux(ff, i) Pr_Array.append(Pr) "the spherical cap has area 2*pi*r^2*(1-cos(theta))" "divided by npts and we get evenly sized area chunks" surface_Element = 2 * math.pi * pow( r, 2) * (1 - math.cos(theta)) / range_npts P_tot_ff[k] += surface_Element * (1) * (Pr) i = i + 6 #to keep track of the correct entries in the ff array if ff_calc == "Above": for n in range(range_npts): ff = sim.get_farfield( nearfieldAbove, mp.Vector3(xPts[n], yPts[n], zPts[n])) fields.append({ "pos": (xPts[n], yPts[n], zPts[n]), "field": ff }) # print('ff,n,x,y,z',n,xPts[n],yPts[n],zPts[n],ff) # print('fields',fields[n]) # print('------') i = 0 for k in range(nfreq): "Calculate the poynting vector in x,y,z direction" Pr = myPoyntingFlux(ff, i) Pr_Array.append(Pr) "the spherical cap has area 2*pi*r^2*(1-cos(theta))" "divided by npts and we get evenly sized area chunks" surface_Element = 2 * math.pi * pow( r, 2) * (1 - math.cos(theta)) / range_npts P_tot_ff[k] += surface_Element * (1) * (Pr) #print('S',surface_Element,'r',r,'theta',theta,'range npts',range_npts) i = i + 6 #to keep track of the correct entries in the ff array # print('P_tot_ff',P_tot_ff) # print('fields',fields) # print('Pr_Array',Pr_Array) ##CALCULATE FLUX OUT FROM BOX########################################### if calculate_flux: #"Initialize variables to be used, pf stands for 'per frequency'" flux_tot_value = np.zeros( self.number_of_freqs) #total flux out from box flux_tot_ff_ratio = np.zeros(self.number_of_freqs) flux_tot_out = mp.get_fluxes(flux_total) #save total flux data freqs_out = mp.get_flux_freqs(flux_total) print('freqs', freqs_out) if calculate_source_flux: source_flux_out = mp.get_fluxes(flux_source) elapsed_time = round((time.time() - start) / 60, 1) print('LDOS', sim.ldos_data) print('LDOS0', sim.ldos_data[0]) for i in range(len(flux_tot_out)): print(flux_tot_out[i] * 100 / source_flux_out[i], 'LE percentage for lambda: ', 1 / freqs_out[i]) if False: fig = plt.figure() ax = fig.gca(projection='3d') Pr_Max = max(Pr_Array) R = [n / Pr_Max for n in Pr_Array] print(R) # R=1 pts = len(Pr_Array) theta, phi = np.linspace(0, np.pi, pts), np.linspace(0, 2 * np.pi, pts) THETA, PHI = np.meshgrid(theta, phi) #print(xPts) # print(yPts) # print(zPts) X = np.zeros(pts**2) Y = np.zeros(pts**2) Z = np.zeros(pts**2) print('R pts', pts) print('sample pts', len(xPts)) for n in range(pts): xPts[n] = R[n] * xPts[n] yPts[n] = R[n] * yPts[n] zPts[n] = R[n] * zPts[n] # #print(X) # #print(Y) # #print(Z) #ax.set_xlim(-100,100) #ax.set_zlim(-100,100) #ax.set_ylim(-100,100) #ax.scatter(xPts,yPts,zPts) u = np.linspace(0, 2 * np.pi, 120) v = np.linspace(0, np.pi / 6, 120) r_ = np.asarray(R) x = np.outer(np.cos(u), np.sin(v)) y = np.outer(np.sin(u), np.sin(v)) z = R * np.outer(np.ones(np.size(u)), np.cos(v)) print('lenx', len(x), 'leny', len(y), 'lenz', len(z)) #r = np.sqrt(xPts**2+yPts**2+zPts**2) #plot(r) ax.plot_surface(x, y, z, cmap='plasma') #ax.quiver(0,0,0,0,0,1,length=1.2,color='brown',normalize='true') #ax.text(0,0,1.3, '$\mathcal{P}$',size=20, zdir=None) #ax.set_zlim(-1,1) #ax.axis('off') #ax.plot_trisurf(list(x),list(y),list(z),cmap='plasma') plt.show() for n in range(len(flux_tot_out)): flux_tot_out[n] = round(flux_tot_out[n], 11) # P_tot_ff[n]=round(P_tot_ff[n],9) ##Some processing to calculate the flux ratios per frequency if ff_calculations: for n in range(len(flux_tot_out)): # flux_tot_out[n]=round(flux_tot_out[n],9) P_tot_ff[n] = round(P_tot_ff[n], 11) for i in range(self.number_of_freqs): flux_tot_ff_ratio[i] = round(P_tot_ff[i] / flux_tot_out[i], 11) if ff_calc == "Both": P_tot_ff = [] P_tot_ff.append(P_tot_ffA) P_tot_ff.append(P_tot_ffB) flux_tot_ff_ratio = [] flux_tot_ff_ratioA = [0] * (self.number_of_freqs) flux_tot_ff_ratioB = [0] * (self.number_of_freqs) for i in range(self.number_of_freqs): flux_tot_ff_ratioA[i] = round( P_tot_ffA[i] / flux_tot_out[i], 11) flux_tot_ff_ratioB[i] = round( P_tot_ffB[i] / flux_tot_out[i], 11) flux_tot_ff_ratio.append(flux_tot_ff_ratioA) flux_tot_ff_ratio.append(flux_tot_ff_ratioB) return { "total_flux": flux_tot_out, "source_flux": source_flux_out, "ff_at_angle": P_tot_ff, "flux_ratio": flux_tot_ff_ratio, "LDOS": sim.ldos_data, "Elapsed time (min)": elapsed_time } else: return { "total_flux": flux_tot_out, "source_flux": source_flux_out, "ff_at_angle": None, "flux_ratio": None, "LDOS": sim.ldos_data, "Elapsed time (min)": elapsed_time }
mp.Vector3(0, 0.5, 0.5), # X mp.Vector3(0.25, 0.75, 0.5), # W mp.Vector3(0.375, 0.75, 0.375) # K ] k_points = mp.interpolate(4, vlist) # define a couple of parameters (which we can set from the command_line) eps = 11.56 # the dielectric constant of the spheres r = 0.25 # the radius of the spheres diel = mp.Medium(epsilon=eps) # A diamond lattice has two "atoms" per unit cell: geometry = [ mp.Sphere(r, center=mp.Vector3(0.125, 0.125, 0.125), material=diel), mp.Sphere(r, center=mp.Vector3(-0.125, -0.125, -0.125), material=diel) ] # (A simple fcc lattice would have only one sphere/object at the origin.) resolution = 16 # use a 16x16x16 grid mesh_size = 5 num_bands = 5 ms = mpb.ModeSolver(geometry_lattice=geometry_lattice, k_points=k_points, geometry=geometry, resolution=resolution, num_bands=num_bands, mesh_size=mesh_size)
def main(series, folder, resolution, from_um_factor, r, paper, wlen): #%% PARAMETERS # Au sphere r = r / (from_um_factor * 1e3) # Radius of sphere now in Meep units medium = import_medium("Au", from_um_factor, paper=paper) # Medium of sphere: gold (Au) # Frequency and wavelength wlen = wlen / (from_um_factor * 1e3) # Wavelength now in Meep units # Space configuration pml_width = 0.38 * wlen # 0.5 * wlen air_width = r / 2 # 2 * r # Field Measurements period_line = 1 period_plane = 1 after_cell_run_time = 10 * wlen # Computation time enlapsed = [] # Saving directories if series is None: series = f"AuSphereField{2*r*from_um_factor*1e3:.0f}WLen{wlen*from_um_factor*1e3:.0f}" if folder is None: folder = "AuMieSphere/AuSphereField" home = vs.get_home() #%% 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)] # Cause of symmetry, two mirror planes reduce cell size to 1/4 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) source_center = -0.5 * cell_width + pml_width print("Resto Source Center: {}".format(source_center % (1 / resolution))) sources = [ mp.Source(mp.ContinuousSource(wavelength=wlen, is_integrated=True), center=mp.Vector3(source_center), size=mp.Vector3(0, cell_width, cell_width), component=mp.Ez) ] # Ez-polarized monochromatic planewave # (its size parameter fills the entire cell in 2d) # >> The planewave source extends into the PML # ==> is_integrated=True must be specified geometry = [mp.Sphere(material=medium, center=mp.Vector3(), radius=r)] # Au sphere with frequency-dependant characteristics imported from Meep. path = os.path.join(home, folder, series) if not os.path.isdir(path): vs.new_dir(path) file = lambda f: os.path.join(path, f) #%% SAVE GET FUNCTIONS def get_line(sim): return sim.get_array(center=mp.Vector3(), size=mp.Vector3(cell_width), component=mp.Ez) def get_plane(sim): return sim.get_array(center=mp.Vector3(), size=mp.Vector3(0, cell_width, cell_width), component=mp.Ez) #%% INITIALIZE sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources, symmetries=symmetries, geometry=geometry) temp = time() sim.init_sim() enlapsed.append(time() - temp) #%% DEFINE SAVE STEP FUNCTIONS f, save_line = vs.save_slice_generator(sim, file("Lines.h5"), "Ez", get_line) g, save_plane = vs.save_slice_generator(sim, file("Planes.h5"), "Ez", get_plane) to_do_while_running = [ mp.at_every(period_line, save_line), mp.at_every(period_plane, save_plane) ] #%% RUN! temp = time() sim.run(*to_do_while_running, until=cell_width + after_cell_run_time) del f, g enlapsed.append(time() - temp) #%% SAVE METADATA params = dict(from_um_factor=from_um_factor, resolution=resolution, r=r, paper=paper, pml_width=pml_width, air_width=air_width, cell_width=cell_width, source_center=source_center, wlen=wlen, period_line=period_line, period_plane=period_plane, after_cell_run_time=after_cell_run_time, series=series, folder=folder, home=home, enlapsed=enlapsed) f = h5.File(file("Lines.h5"), "r+") for a in params: f["Ez"].attrs[a] = params[a] f.close() del f g = h5.File(file("Planes.h5"), "r+") for a in params: g["Ez"].attrs[a] = params[a] g.close() del g sim.reset_meep()
def __init__(self, dir_name, wavelength, thy_absorb, cyt_absorb): self.base_directory = base_directory + str(dir_name) self.wavelength = wavelength self.thy_absorb = thy_absorb self.cyt_absorb = cyt_absorb self.frequency = 1 / wavelength # Calculate wavelengths dependent on RI self.wavelength_in_media = wavelength / ri_media self.wavelength_in_cytoplasm = wavelength / ri_cytoplasm self.wavelength_in_thylakoid = wavelength / ri_thylakoid max_freq = self.frequency - 0.01 min_freq = self.frequency + 0.01 self.pulse_width = abs(max_freq - min_freq) cell = mp.Vector3(sxx, sxy, sxz) pml_layers = [mp.PML(dpml)] thylakoid_material = mp.Medium(index=ri_thylakoid, D_conductivity=2 * math.pi * self.frequency * (thy_absorb / (ri_thylakoid**2))) cytoplasm_material = mp.Medium(index=ri_cytoplasm, D_conductivity=2 * math.pi * self.frequency * (cyt_absorb / (ri_cytoplasm**2))) thylakoid_region = mp.Sphere(radius=cell_radius, center=mp.Vector3(0, 0, 0), material=thylakoid_material) cytoplasm_region = mp.Sphere(radius=cell_radius - thylakoid_thickness, center=mp.Vector3(0, 0, 0), material=cytoplasm_material) geometry = [thylakoid_region, cytoplasm_region] # Sources kdir = mp.Vector3(1, 0, 0) # direction of k (length is irrelevant) n = ri_media # refractive index of material containing the source k = kdir.unit().scale(2 * math.pi * self.frequency * n) # k with correct length def pw_amp(k, x0): def _pw_amp(x): return cmath.exp(1j * k.dot(x + x0)) return _pw_amp source = [ mp.Source( mp.ContinuousSource(frequency=self.frequency, fwidth=self.pulse_width), # along x axis component=mp.Ez, center=mp.Vector3(-0.5 * simulation_size, 0, 0), # x, ,y ,z size=mp.Vector3(0, sxy, sxz), amp_func=pw_amp(k, mp.Vector3(x=-0.5 * simulation_size))) ] sim = mp.Simulation( cell_size=cell, sources=source, boundary_layers=pml_layers, resolution=resolution, geometry=geometry, default_material=mp.Medium(index=ri_media), force_complex_fields=complex_f, eps_averaging=eps_av, ) def output_fields(sim): ez_output = open(base_directory + "Ez Field 430 NoAbs" + ".npy", 'wb') ez_array = sim.get_array(component=mp.Ez, cmplx=complex_f) ez_field_output = np.asarray(ez_array) np.save(ez_output, ez_field_output) ez_output.close() # ey_output = open(base_directory + "Ey Field 300 Std" + ".npy", 'wb') # ey_array = sim.get_array(component=mp.Ey, cmplx=complex_f) # ey_field_output = np.asarray(ey_array) # np.save(ey_output, ey_field_output) # ey_output.close() # # ex_output = open(base_directory + "Ex Field 300 std abs" + ".npy", 'wb') # ex_array = sim.get_array(component=mp.Ex, cmplx=complex_f) # ex_field_output = np.asarray(ex_array) # np.save(ex_output, ex_field_output) # ex_output.close() # eps_output = open(base_directory + "Refractive Index" + ".npy", 'wb') # eps_array = sim.get_array(component=mp.Dielectric, cmplx=complex_f) # np.save(eps_output, eps_array) # eps_output.close() sim.use_output_directory(self.base_directory) sim.run(mp.at_every(1, output_fields), until=t)
def main(args): resolution = 30 nSiO2 = 1.4 SiO2 = mp.Medium(index=nSiO2) # conversion factor for eV to 1/um eV_um_scale = 1 / 1.23984193 # W, from Rakic et al., Applied Optics, vol. 32, p. 5274 (1998) W_eps_inf = 1 W_plasma_frq = 13.22 * eV_um_scale W_f0 = 0.206 W_frq0 = 1e-10 W_gam0 = 0.064 * eV_um_scale W_sig0 = W_f0 * W_plasma_frq**2 / W_frq0**2 W_f1 = 0.054 W_frq1 = 1.004 * eV_um_scale # 1.235 um W_gam1 = 0.530 * eV_um_scale W_sig1 = W_f1 * W_plasma_frq**2 / W_frq1**2 W_f2 = 0.166 W_frq2 = 1.917 * eV_um_scale # 0.647 W_gam2 = 1.281 * eV_um_scale W_sig2 = W_f2 * W_plasma_frq**2 / W_frq2**2 W_susc = [ mp.DrudeSusceptibility(frequency=W_frq0, gamma=W_gam0, sigma=W_sig0), mp.LorentzianSusceptibility(frequency=W_frq1, gamma=W_gam1, sigma=W_sig1), mp.LorentzianSusceptibility(frequency=W_frq2, gamma=W_gam2, sigma=W_sig2) ] W = mp.Medium(epsilon=W_eps_inf, E_susceptibilities=W_susc) # crystalline Si, from M.A. Green, Solar Energy Materials and Solar Cells, vol. 92, pp. 1305-1310 (2008) # fitted Lorentzian parameters, only for 600nm-1100nm Si_eps_inf = 9.14 Si_eps_imag = -0.0334 Si_eps_imag_frq = 1 / 1.55 Si_frq = 2.2384 Si_gam = 4.3645e-02 Si_sig = 14.797 / Si_frq**2 Si = mp.Medium(epsilon=Si_eps_inf, D_conductivity=2 * math.pi * Si_eps_imag_frq * Si_eps_imag / Si_eps_inf, E_susceptibilities=[ mp.LorentzianSusceptibility(frequency=Si_frq, gamma=Si_gam, sigma=Si_sig) ]) a = args.a # lattice periodicity cone_r = args.cone_r # cone radius cone_h = args.cone_h # cone height wire_w = args.wire_w # metal-grid wire width wire_h = args.wire_h # metal-grid wire height trench_w = args.trench_w # trench width trench_h = args.trench_h # trench height Np = args.Np # number of periods in supercell dair = 1.0 # air gap thickness dmcl = 1.7 # micro lens thickness dsub = 3000.0 # substrate thickness dpml = 1.0 # PML thickness sxy = Np * a sz = dpml + dair + dmcl + dsub + dpml cell_size = mp.Vector3(sxy, sxy, sz) boundary_layers = [ mp.PML(dpml, direction=mp.Z, side=mp.High), mp.Absorber(dpml, direction=mp.Z, side=mp.Low) ] geometry = [] if args.substrate: geometry = [ mp.Sphere(material=SiO2, radius=dmcl, center=mp.Vector3(0, 0, 0.5 * sz - dpml - dair - dmcl)), mp.Block(material=Si, size=mp.Vector3(mp.inf, mp.inf, dsub + dpml), center=mp.Vector3(0, 0, -0.5 * sz + 0.5 * (dsub + dpml))), mp.Block( material=W, size=mp.Vector3(mp.inf, wire_w, wire_h), center=mp.Vector3(0, -0.5 * sxy + 0.5 * wire_w, -0.5 * sz + dpml + dsub + 0.5 * wire_h)), mp.Block( material=W, size=mp.Vector3(mp.inf, wire_w, wire_h), center=mp.Vector3(0, +0.5 * sxy - 0.5 * wire_w, -0.5 * sz + dpml + dsub + 0.5 * wire_h)), mp.Block( material=W, size=mp.Vector3(wire_w, mp.inf, wire_h), center=mp.Vector3(-0.5 * sxy + 0.5 * wire_w, 0, -0.5 * sz + dpml + dsub + 0.5 * wire_h)), mp.Block(material=W, size=mp.Vector3(wire_w, mp.inf, wire_h), center=mp.Vector3(+0.5 * sxy - 0.5 * wire_w, 0, -0.5 * sz + dpml + dsub + 0.5 * wire_h)) ] if args.substrate and args.texture: for nx in range(Np): for ny in range(Np): cx = -0.5 * sxy + (nx + 0.5) * a cy = -0.5 * sxy + (ny + 0.5) * a geometry.append( mp.Cone(material=SiO2, radius=0, radius2=cone_r, height=cone_h, center=mp.Vector3( cx, cy, 0.5 * sz - dpml - dair - dmcl - 0.5 * cone_h))) if args.substrate: geometry.append( mp.Block(material=SiO2, size=mp.Vector3(mp.inf, trench_w, trench_h), center=mp.Vector3( 0, -0.5 * sxy + 0.5 * trench_w, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) geometry.append( mp.Block(material=SiO2, size=mp.Vector3(mp.inf, trench_w, trench_h), center=mp.Vector3( 0, +0.5 * sxy - 0.5 * trench_w, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) geometry.append( mp.Block(material=SiO2, size=mp.Vector3(trench_w, mp.inf, trench_h), center=mp.Vector3( -0.5 * sxy + 0.5 * trench_w, 0, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) geometry.append( mp.Block(material=SiO2, size=mp.Vector3(trench_w, mp.inf, trench_h), center=mp.Vector3( +0.5 * sxy - 0.5 * trench_w, 0, 0.5 * sz - dpml - dair - dmcl - 0.5 * trench_h))) k_point = mp.Vector3(0, 0, 0) lambda_min = 0.7 # minimum source wavelength lambda_max = 1.0 # maximum source wavelength fmin = 1 / lambda_max fmax = 1 / lambda_min fcen = 0.5 * (fmin + fmax) df = fmax - fmin sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ex, center=mp.Vector3(0, 0, 0.5 * sz - dpml - 0.5 * dair), size=mp.Vector3(sxy, sxy, 0)) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, dimensions=3, k_point=k_point, sources=sources) nfreq = 50 refl = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, 0.5 * sz - dpml), size=mp.Vector3(sxy, sxy, 0))) trans_grid = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.5 * sz + dpml + dsub + wire_h), size=mp.Vector3(sxy, sxy, 0))) trans_sub_top = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.5 * sz + dpml + dsub), size=mp.Vector3(sxy, sxy, 0))) trans_sub_bot = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.5 * sz + dpml), size=mp.Vector3(sxy, sxy, 0))) sim.run(mp.at_beginning(mp.output_epsilon), until=0) if args.substrate: sim.load_minus_flux('refl-flux', refl) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ex, mp.Vector3(0, 0, -0.5 * sz + dpml + 0.5 * dsub), 1e-9)) if not args.substrate: sim.save_flux('refl-flux', refl) sim.display_fluxes(refl, trans_grid, trans_sub_top, trans_sub_bot)
import pinboard job = pinboard.pinboard() nm = 1e-9 um = 1e-6 ### geometry radius = 75 * nm gold = meep_ext.material.Au() gold = meep.Medium(index=3.5) sep = 400 * nm p1 = meep.Vector3(-sep / 2, 0, 0) p2 = meep.Vector3(sep / 2, 0, 0) geometry = [ meep.Sphere(center=p1, radius=radius, material=gold), meep.Sphere(center=p2, radius=radius, material=gold) ] ### source fcen, df = meep_ext.freq_data(1 / (400 * nm), 1 / (1000 * nm)) nfreq = 40 src_time = meep.GaussianSource(frequency=1.3 / um, fwidth=4.0 / um) polarization = meep.Ex # used in convergence check 'decay_by' source = lambda sim: meep_ext.x_polarized_plane_wave(sim, src_time) ### monitor info particle_monitor_gap = 50 * nm pml_monitor_gap = 50 * nm norm_file_ext = 'norm' monitor_size = [
def run_simulation(save_prefix, tip_radius=0.007, cone_height=0.364, trunk_radius=0.275, n_tip=3.694, k_tip=0.0, fcen=1.25, waves=7.5, theta_deg=10, sample=20, sx=0.8, sy=1.5, sz=1.0, dpml=0.1, res=1000, res_factor=0.2, X_1=-0.2, X_2=0.2, Y_1=-0.2, Y_2=0.2, Z_1=-0.2, Z_2=0.2, x_mon_rat=1.0, y_mon_rat=1.0, z_mon_rat=1.0): #Interpolate to next resolution step for high-res region dx = 1 / res X_1 = np.floor(X_1 / dx) * dx X_2 = np.ceil(X_2 / dx) * dx Y_1 = np.floor(Y_1 / dx) * dx Y_2 = np.ceil(Y_2 / dx) * dx Z_1 = np.floor(Z_1 / dx) * dx Z_2 = np.ceil(Z_2 / dx) * dx #Dump all the settings to a file: settings_file = h5py.File( Path(sys.argv[0]).stem + '-' + save_prefix + '_settings.h5', 'w') settings_file.create_dataset('tip_radius', data=tip_radius) settings_file.create_dataset('cone_height', data=cone_height) settings_file.create_dataset('trunk_radius', data=trunk_radius) settings_file.create_dataset('n_tip', data=n_tip) settings_file.create_dataset('k_tip', data=k_tip) settings_file.create_dataset('fcen', data=fcen) settings_file.create_dataset('waves', data=waves) settings_file.create_dataset('theta_deg', data=theta_deg) settings_file.create_dataset('sample', data=sample) settings_file.create_dataset('sx', data=sx) settings_file.create_dataset('sy', data=sy) settings_file.create_dataset('sz', data=sz) settings_file.create_dataset('dpml', data=dpml) settings_file.create_dataset('res', data=res) settings_file.create_dataset('res_factor', data=res_factor) settings_file.create_dataset('X_1', data=X_1) settings_file.create_dataset('X_2', data=X_2) settings_file.create_dataset('Y_1', data=Y_1) settings_file.create_dataset('Y_2', data=Y_2) settings_file.create_dataset('Z_1', data=Z_1) settings_file.create_dataset('Z_2', data=Z_2) settings_file.create_dataset('x_mon_rat', data=x_mon_rat) settings_file.create_dataset('y_mon_rat', data=y_mon_rat) settings_file.create_dataset('z_mon_rat', data=z_mon_rat) settings_file.close() #Convert theta to radians theta = theta_deg * np.pi / 180 eps_tip = calc_eps_r(n_tip, k_tip) sig_d_tip = calc_sig_d(n_tip, k_tip, fcen) #Create the cell size sX = 2 * dpml + sx sY = 2 * dpml + sy sZ = 2 * dpml + sz #Monitor sizes x_mon = sx * x_mon_rat y_mon = sx * y_mon_rat z_mon = sx * z_mon_rat #Calculate values in prime-space: sx_prime = sx * res_factor + (X_2 - X_1) * (1 - res_factor) sy_prime = sy * res_factor + (Y_2 - Y_1) * (1 - res_factor) sz_prime = sz * res_factor + (Z_2 - Z_1) * (1 - res_factor) dpml_prime = dpml * res_factor sX_prime = 2 * dpml_prime + sx_prime sY_prime = 2 * dpml_prime + sy_prime sZ_prime = 2 * dpml_prime + sz_prime x_mon_prime = (x_mon/2.0 > X_2)*((x_mon/2.0 - X_2)*res_factor + X_2) +\ (x_mon/2.0)*(x_mon/2.0 <= X_2) -\ (-1*x_mon/2.0 < X_1)*((-1*x_mon/2.0 - X_1)*res_factor + X_1) -\ (-1*x_mon/2.0)*(-1*x_mon/2.0 >= X_1) y_mon_prime = (y_mon/2.0 > Y_2)*((y_mon/2.0 - Y_2)*res_factor + Y_2) +\ (y_mon/2.0)*(y_mon/2.0 <= Y_2) -\ (-1*y_mon/2.0 < Y_1)*((-1*y_mon/2.0 - Y_1)*res_factor + Y_1) -\ (-1*y_mon/2.0)*(-1*y_mon/2.0 >= Y_1) z_mon_prime = (z_mon/2.0 > Z_2)*((z_mon/2.0 - Z_2)*res_factor + Z_2) +\ (z_mon/2.0)*(z_mon/2.0 <= Z_2) -\ (-1*z_mon/2.0 < Z_1)*((-1*z_mon/2.0 - Z_1)*res_factor + Z_1) -\ (-1*z_mon/2.0)*(-1*z_mon/2.0 >= Z_1) cell = mp.Vector3(sX_prime, sY_prime, sZ_prime) tip = [ mp.Cylinder(radius=trunk_radius, center=mp.Vector3( 0.0, -1 * sY / 4.0 - cone_height / 2.0 - tip_radius, 0.0), height=(sY / 2.0 - cone_height), axis=mp.Vector3(0.0, 1.0, 0.0)), mp.Cone(center=mp.Vector3(0, -1 * cone_height / 2 - tip_radius, 0), height=cone_height, radius=trunk_radius, radius2=tip_radius, axis=mp.Vector3(0, 1, 0)), mp.Sphere(center=mp.Vector3(0, -1 * tip_radius, 0), radius=tip_radius) ] def mat_func(r_prime): r_x = r_prime.x r_y = r_prime.y r_z = r_prime.z x_fac = 1 y_fac = 1 z_fac = 1 if (r_prime.x < X_1): x_fac = res_factor r_x = X_1 + (r_prime.x - X_1) / res_factor elif (r_prime.x > X_2): x_fac = res_factor r_x = X_2 + (r_prime.x - X_2) / res_factor if (r_prime.y < Y_1): y_fac = res_factor r_y = Y_1 + (r_prime.y - Y_1) / res_factor elif (r_prime.y > Y_2): y_fac = res_factor r_y = Y_2 + (r_prime.y - Y_2) / res_factor if (r_prime.z < Z_1): z_fac = res_factor r_z = Z_1 + (r_prime.z - Z_1) / res_factor elif (r_prime.z > Z_2): z_fac = res_factor r_z = Z_2 + (r_prime.z - Z_2) / res_factor r = mp.Vector3(r_x, r_y, r_z) J = np.matrix([[x_fac, 0, 0], [0, y_fac, 0], [0, 0, z_fac]]) #Loop through all objects inside of tip and see if point is inside. # if yes -- then set eps_point to tip eps # if no -- then leave it as air eps_point = 1.0 for kk in range(len(tip)): if (mp.is_point_in_object(r, tip[kk])): eps_point = eps_tip eps_transform = eps_point * J * J.transpose() / np.linalg.det(J) mu_transform = J * J.transpose() / np.linalg.det(J) eps_diag = eps_transform.diagonal() mu_diag = mu_transform.diagonal() mat = mp.Medium(epsilon_diag=mp.Vector3(eps_diag[0, 0], eps_diag[0, 1], eps_diag[0, 2]), mu_diag=mp.Vector3(mu_diag[0, 0], mu_diag[0, 1], mu_diag[0, 2]), D_conductivity=sig_d_tip) return mat #Create source amplitude function: ky = fcen * np.sin(theta) ky_prime = ky * sY / sY_prime def my_amp_func_y(r_prime): r_y = r_prime.y y_fac = 1 / res_factor if ((r_prime.x >= X_1) and (r_prime.x <= X_2)): x_fac = 1.0 / res_factor else: x_fac = 1.0 if (r_prime.y < Y_1): y_fac = 1.0 r_y = Y_1 + (r_prime.y - Y_1) / res_factor elif (r_prime.y > Y_2): y_fac = 1.0 r_y = Y_2 + (r_prime.y - Y_2) / res_factor if ((r_prime.z >= Z_1) and (r_prime.z <= Z_2)): z_fac = 1.0 / res_factor else: z_fac = 1.0 J = np.matrix([[x_fac, 0, 0], [0, y_fac, 0], [0, 0, z_fac]]) transform = J / np.linalg.det(J) phase_factor = np.exp(-1 * 2 * 1j * np.pi * ky * r_y) amp_factor = transform.diagonal()[0, 1] return amp_factor * phase_factor def my_amp_func_z(r_prime): r_y = r_prime.y y_fac = 1.0 / res_factor if ((r_prime.x >= X_1) and (r_prime.x <= X_2)): x_fac = 1.0 / res_factor else: x_fac = 1.0 if (r_prime.y < Y_1): y_fac = 1.0 r_y = Y_1 + (r_prime.y - Y_1) / res_factor elif (r_prime.y > Y_2): y_fac = 1.0 r_y = Y_2 + (r_prime.y - Y_2) / res_factor if ((r_prime.z >= Z_1) and (r_prime.z <= Z_2)): z_fac = 1.0 / res_factor else: z_fac = 1.0 J = np.matrix([[x_fac, 0, 0], [0, y_fac, 0], [0, 0, z_fac]]) transform = J / np.linalg.det(J) phase_factor = np.exp(-1 * 2 * 1j * np.pi * ky * r_y) amp_factor = transform.diagonal()[0, 2] return amp_factor * phase_factor #Create PMLs pml_layers = [ mp.PML(thickness=dpml_prime, direction=mp.X), mp.PML(thickness=dpml_prime, direction=mp.Y), mp.PML(thickness=dpml_prime, direction=mp.Z) ] symmetry = [mp.Mirror(direction=mp.X)] #Sources Ey_source = mp.Source(mp.ContinuousSource(frequency=fcen), component=mp.Ey, center=mp.Vector3(0, 0, -1 * sz_prime * 0.5), size=mp.Vector3(sX_prime, sY_prime, 0), amp_func=my_amp_func_y, amplitude=np.cos(theta)) Ez_source = mp.Source(mp.ContinuousSource(frequency=fcen), component=mp.Ez, center=mp.Vector3(0, 0, -1 * sz_prime * 0.5), size=mp.Vector3(sX_prime, sY_prime, 0), amp_func=my_amp_func_z, amplitude=np.sin(theta)) sources = [Ey_source, Ez_source] monitor_xy = mp.Volume(center=mp.Vector3(0, 0, 0), size=mp.Vector3(x_mon_prime, y_mon_prime, 0)) monitor_yz = mp.Volume(center=mp.Vector3(0, 0, 0), size=mp.Vector3(0, y_mon_prime, z_mon_prime)) #Now make the simulation sim = mp.Simulation( cell_size=cell, boundary_layers=pml_layers, geometry=[], sources=sources, resolution=res, symmetries=symmetry, dimensions=3, k_point=mp.Vector3(0, -1 * ky_prime, 0), material_function=mat_func, extra_materials=[mp.Medium(epsilon=eps_tip, mu=4, D_conductivity=1)], verbose=True) sim.run(mp.in_volume( monitor_xy, mp.to_appended(save_prefix + "E_xy", mp.at_every(1 / fcen / sample, mp.output_efield))), mp.in_volume( monitor_yz, mp.to_appended( save_prefix + "E_yz", mp.at_every(1 / fcen / sample, mp.output_efield))), until=waves / fcen)