def run_all(self, time_after_sources=150, nfreq_ldos=200): self.time_after_sources = time_after_sources self.nfreq_ldos = nfreq_ldos self.harminv_instance = mp.Harminv( mp.Er, mp.Vector3(0, 0, self.source_position + self.shift), self.fcen, self.df) self.ldos_instance = mp.Ldos(self.fcen, self.df, self.nfreq_ldos) self._sim.run(mp.after_sources(self.harminv_instance), mp.dft_ldos(ldos=self.ldos_instance), until_after_sources=self.time_after_sources) self.ldos_results = np.transpose( np.array([self.ldos_instance.freqs(), self._sim.ldos_data])) self.q_results = [] for mode in self.harminv_instance.modes: self.q_results.append( [1000 / mode.freq, mode.decay, mode.Q, abs(mode.amp)]) self.q_results = np.array(self.q_results) self.print_qs() self.plot_power() self.plot_ldos() self.plot_er_field()
def test_ldos(self): self.sim.run( mp.dft_ldos(self.fcen, 0, 1), until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-6) ) self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368)
def test_ldos_user_object(self): ldos = mp.Ldos(self.fcen, 0, 1) self.sim.run(mp.dft_ldos(ldos=ldos), until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(), 1e-6)) self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368) self.assertEqual(len(mp.get_ldos_freqs(ldos)), 1)
def main(args): resolution = 200 sxy = 2 dpml = 1 sxy = sxy + 2 * dpml cell = mp.Vector3(sxy, sxy, 0) pml_layers = [mp.PML(dpml)] a = 1 t = 0.1 geometry = [ mp.Block(mp.Vector3(a + 2 * t, a + 2 * t, 1e20), material=mp.Medium(epsilon=-1e20)), mp.Block(mp.Vector3(a, a, 1e20), material=mp.Medium(epsilon=1.0)) ] w = args.w if w > 0: geometry.append( mp.Block(center=mp.Vector3(a / 2), size=mp.Vector3(2 * t, w, 1e20), material=mp.Medium(epsilon=1.0))) fcen = math.sqrt(0.5) / a df = 0.2 sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3()) ] symmetries = [mp.Mirror(mp.Y)] Th = args.Th sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=sources, symmetries=symmetries, resolution=resolution) h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df) sim.run(mp.after_sources(h), until_after_sources=Th) m = h.modes[0] f = m.freq Q = m.Q Vmode = 0.25 * a * a print("ldos0:, {}".format(Q / Vmode / (2 * math.pi * f * math.pi * 0.5))) sim.reset_meep() T = 2 * Q * (1 / f) sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)
def test_ldos_user_object(self): ldos = mp.Ldos(self.fcen, 0, 1) self.sim.run(mp.dft_ldos(ldos=ldos), until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(), 1e-6)) self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368) freqs = ldos.freqs() self.assertEqual(ldos.freq_min, freqs[0] * 2 * math.pi) self.assertEqual(ldos.nfreq, 1) self.assertEqual(ldos.dfreq, 0)
def metal_cavity(w): resolution = 50 sxy = 2 dpml = 1 sxy = sxy + 2 * dpml cell = mp.Vector3(sxy, sxy) pml_layers = [mp.PML(dpml)] a = 1 t = 0.1 geometry = [ mp.Block(mp.Vector3(a + 2 * t, a + 2 * t, mp.inf), material=mp.metal), mp.Block(mp.Vector3(a, a, mp.inf), material=mp.air) ] geometry.append( mp.Block(center=mp.Vector3(a / 2), size=mp.Vector3(2 * t, w, mp.inf), material=mp.air)) fcen = math.sqrt(0.5) / a df = 0.2 sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3()) ] symmetries = [mp.Mirror(mp.Y)] sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=sources, symmetries=symmetries, resolution=resolution) h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df) sim.run(mp.after_sources(h), until_after_sources=500) m = h.modes[0] f = m.freq Q = m.Q Vmode = 0.25 * a * a ldos_1 = Q / Vmode / (2 * math.pi * f * math.pi * 0.5) sim.reset_meep() T = 2 * Q * (1 / f) sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T) ldos_2 = sim.ldos_data[0] return ldos_1, ldos_2
def test_ldos_user_object(self): ldos = mp.Ldos(self.fcen, 0, 1) self.sim.run( mp.dft_ldos(ldos=ldos), until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-6) ) self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368) freqs = ldos.freqs() self.assertEqual(ldos.freq_min, freqs[0] * 2 * math.pi) self.assertEqual(ldos.nfreq, 1) self.assertEqual(ldos.dfreq, 0)
def main(args): resolution = 200 sxy = 2 dpml = 1 sxy = sxy + 2 * dpml cell = mp.Vector3(sxy, sxy, 0) pml_layers = [mp.PML(dpml)] a = 1 t = 0.1 geometry = [mp.Block(mp.Vector3(a + 2 * t, a + 2 * t, mp.inf), material=mp.metal), mp.Block(mp.Vector3(a, a, mp.inf), material=mp.air)] w = args.w if w > 0: geometry.append(mp.Block(center=mp.Vector3(a / 2), size=mp.Vector3(2 * t, w, mp.inf), material=mp.air)) fcen = math.sqrt(0.5) / a df = 0.2 sources = [mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3())] symmetries = [mp.Mirror(mp.Y)] Th = args.Th sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=sources, symmetries=symmetries, resolution=resolution) h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df) sim.run(mp.after_sources(h), until_after_sources=Th) m = h.modes[0] f = m.freq Q = m.Q Vmode = 0.25 * a * a print("ldos0:, {}".format(Q / Vmode / (2 * math.pi * f * math.pi * 0.5))) sim.reset_meep() T = 2 * Q * (1 / f) sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)
def get_ldos(self, time_after_sources=150, nfreq_ldos=200): self.nfreq_ldos = nfreq_ldos self.time_after_sources = time_after_sources self.ldos_instance = mp.Ldos(self.fcen, self.df, self.nfreq_ldos) self._sim.run(mp.dft_ldos(ldos=self.ldos_instance), until_after_sources=self.time_after_sources) self.ldos_results = np.transpose( np.array([self.ldos_instance.freqs(), self._sim.ldos_data])) maximum = max(self.ldos_results[:, 1]) index = np.where(self.ldos_results[:, 1] == maximum) mode_wvl = 1000 / self.ldos_results[index, 0] self.mode_wvl = mode_wvl
def bulk_ldos(self): s = self.L + 2 * self.dpml cell_size = mp.Vector3(s, s, s) pml_layers = [mp.PML(self.dpml)] sim = mp.Simulation(resolution=self.resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=self.sources, symmetries=self.symmetries, default_material=mp.Medium(index=self.n)) sim.run(mp.dft_ldos(self.fcen, 0, 1), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ex, mp.Vector3(), 1e-6)) return sim.ldos_data[0]
def main(args): sx = 4.0 #spatial extent along x including pmls (μm) sy = 4 sz = 0 dpml = 1.0 cell = mp.Vector3(sx, sy, sz) pml_layers = [mp.PML(dpml)] geometry = [ mp.Block(mp.Vector3(mp.inf, 0.050, 0), center=mp.Vector3(0, 0.025, 0), material=Ag) ] resolution = args.res wvlmax = 1.0 fmin = 1 / wvlmax wvlmin = 0.100 fmax = 1 / wvlmin wvl = args.wvl fcen = 1 / wvl df = fmax - fmin nfreq = 1 print("wavelength =", wvl, "μm") print("center frequency =", fcen, "1/μm") source = [ mp.Source(mp.GaussianSource(fcen, 0, nfreq), component=mp.Ex, center=mp.Vector3(0, -0.050, 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.050, 0) sim.run(mp.dft_ldos(fcen, 0, nfreq), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ex, pt, 1e-3)) eps_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) ey_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Ey)
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 cavity_ldos(self, sz): sxy = self.L + 2 * self.dpml cell_size = mp.Vector3(sxy, sxy, sz) boundary_layers = [ mp.PML(self.dpml, direction=mp.X), mp.PML(self.dpml, direction=mp.Y) ] sim = mp.Simulation(resolution=self.resolution, cell_size=cell_size, boundary_layers=boundary_layers, sources=self.sources, symmetries=self.symmetries, default_material=mp.Medium(index=self.n)) sim.run(mp.dft_ldos(ldos=mp.Ldos(self.fcen, 0, 1)), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ex, mp.Vector3(), 1e-6)) return sim.ldos_data[0]
def main(args): sx = 3.0 #spatial extent along x including pmls (μm) sy = 3.0 sz = 3.0 dpml = 1.0 cell = mp.Vector3(sx, sy, sy) pml_layers = [mp.PML(dpml)] geometry = [ mp.Cylinder(center=mp.Vector3(0, 0, 0), height=mp.inf, radius=0.505, axis=mp.Vector3(0, 0, 1), material=Graph), mp.Cylinder(center=mp.Vector3(0, 0, 0), height=mp.inf, radius=0.5, axis=mp.Vector3(0, 0, 1), material=mp.Medium(epsilon=3.9)) ] resolution = args.res wvlmax = 40 fmin = 1 / 40 wvlmin = 20 fmax = 1 / 20 wvl = args.wvl fcen = 1 / wvl df = fmax - fmin 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.510, 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.510, 0) sim.run(mp.dft_ldos(fcen, df, nfreq), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ey, pt, 1e-3)) eps_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) #plot eps_data plt.figure(dpi=160) plt.imshow(eps.data.transpose(), interpolation='spline36', cmap='binary') plt.axis('off') plt.show
def main(args): sx = 8.0 #spatial extent along x including pmls (μm) sy = 4 sz = 0 dpml = 1.0 cell = mp.Vector3(sx, sy, sz) pml_layers = [mp.PML(dpml)] resolution = args.res wvl = args.wvl #source wavelength fcen = 1 / wvl #center frequency df = 0 #frequency bandwidth nfreq = 1 #number of frequencies dw = args.dw # slab thickness increment(um) w_init = args.w_init # initial slab thickness(μm) n = args.n w = w_init + n * dw #next slab thicknessw slab_center_y = 0.5 * w #slab center y coordinate sfor_y = w + 0.005 #sfor_y = y coordinate of the slit_flux_output_region s = 0.030 #slit width print("wavelength =", wvl, "um") print("slab size =", w, "um") print("center frequency =", fcen, "1/um") print("slit_flux_output_region y coordinate =", sfor_y, "um") print("slab center y coordinate =", slab_center_y, "um") geometry = [ mp.Block(mp.Vector3(mp.inf, w, mp.inf), center=mp.Vector3(0, slab_center_y), material=Ag) ] source = [ mp.Source(mp.GaussianSource(fcen, df, nfreq), component=mp.Ex, center=mp.Vector3(0, -0.5 * (sy - 2.1), 0), size=mp.Vector3(sx - 2.0, 0, 0)) ] symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)] sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=source, resolution=resolution, boundary_layers=pml_layers, filename_prefix='slab_noslit') sim.use_output_directory() pt = mp.Vector3( 0, -0.5 * (sy - 2.1), 0 ) #point where field intensity is measured (usually at source position) slab_flux_in_region = mp.FluxRegion( center=mp.Vector3(0, -0.005, 0), size=mp.Vector3(sx, 0, 0)) #incident flux monitor 5nm above slab. slab_flux_in = sim.add_flux(fcen, df, nfreq, slab_flux_in_region) slab_flux_in_data = sim.get_flux_data( slab_flux_in) #data are the E and H fields used to calculate S=ExH slab_flux_out_region = mp.FluxRegion( center=mp.Vector3(0, sfor_y, 0), size=mp.Vector3(sx, 0, 0)) #trans flux monitor 5nm below slab. slab_flux_out = sim.add_flux(fcen, df, nfreq, slab_flux_out_region) slab_flux_out_data = sim.get_flux_data(slab_flux_out) sim.run(mp.dft_ldos(fcen, df, nfreq), mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 10, mp.Hz, pt, 1e-5)) slab_flux_input = [] slab_flux_output = [] slab_flux_input = mp.get_fluxes(slab_flux_in) slab_flux_output = mp.get_fluxes(slab_flux_out) print('incident flux slab =', slab_flux_input, ' ', 'transmitted flux slab =', slab_flux_output, ' ', 'normed transmitted flux=', np.divide(slab_flux_output, slab_flux_input)) eps_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Dielectric) ex_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Ex) hz_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Hz) sim.reset_meep() geometry = [ mp.Block(mp.Vector3(mp.inf, w, mp.inf), center=mp.Vector3(0, 0.5 * (w)), material=Ag), mp.Block(mp.Vector3(s, w, 0), center=mp.Vector3(0, 0.5 * (w), 0), material=mp.Medium(epsilon=1)) ] sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=source, resolution=resolution, boundary_layers=pml_layers, filename_prefix="slab_slit") sim.use_output_directory() slit_flux_in_region = mp.FluxRegion(center=mp.Vector3(0, -0.005, 0), size=mp.Vector3( sx, 0, 0)) #same as slab_flux_in_region slit_flux_in = sim.add_flux(fcen, df, nfreq, slit_flux_in_region) slit_flux_in_data = sim.get_flux_data(slit_flux_in) slit_flux_out_region = mp.FluxRegion(center=mp.Vector3(0, w + 0.005, 0), size=mp.Vector3( sx, 0, 0)) #same as slab_flux_out_region slit_flux_out = sim.add_flux(fcen, df, nfreq, slit_flux_out_region) slit_flux_out_data = sim.get_flux_data(slit_flux_out) sim.load_minus_flux_data(slit_flux_out, slab_flux_out_data) #subract transmitted slab flux data (slab_flux_out_data) from slit_flux_out. This is a correction so that flux only #from the slit is registered in slit_flux_out. sim.run(mp.dft_ldos(fcen, 0, nfreq), mp.at_beginning(mp.output_epsilon), until_after_sources=mp.stop_when_fields_decayed( 10, mp.Hz, pt, 1e-4)) slit_flux_input = [] slit_flux_output = [] slit_flux_input = mp.get_fluxes(slit_flux_in) slit_flux_output = mp.get_fluxes(slit_flux_out) print('slit flux net =', slit_flux_output, ' ', 'slit_flux_normed =', np.divide(slit_flux_output, slab_flux_input)) eps_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Dielectric) ex_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Ex) hz_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Hz) slabFlux = [] slabFlux = np.append(slabFlux, slab_flux_input) slitFlux = [] slitFlux = np.append(slitFlux, slit_flux_output) fracFlux = np.divide(slitFlux, slabFlux) print('normalised slit flux,', end=" ") #suppress the print newline function with end=" " np.savetxt( sys.stdout, fracFlux, fmt='%7.5f' ) #print to stdout transmitted slit flux normalised to input flux. print('slab thickness,', end=" ") print(w) #-------------------------------- Next three lines ok for data file in mp meep, but writes data np times (np no. processors) in pmp. #f=open('fracFL.dat','ab') #np.savetxt(f,fracFl,fmt='%6.5f') #f.close() #---------------------------------------------------- eps_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Dielectric) hz_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, sz), component=mp.Hz) hz_scat = hz_data_slit - hz_data_slab
def test_invalid_dft_ldos(self): with self.assertRaises(ValueError): self.sim.run(mp.dft_ldos(mp.Ldos(self.fcen, 0, 1)), until=200)
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.Source(mp.GaussianSource(fcen, df, nfreq), component=mp.Ey, 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.705, 0) sim.run(mp.dft_ldos(fcen, 0, nfreq), until_after_sources=mp.stop_when_fields_decayed(20, mp.Ey, pt, 1e-3)) eps_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) ey_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Ey) #plot eps_data # from mayavi import mlab # s = mlab.contour3d(eps_data, colormap="YlGnBu") # mlab.show() plt.figure(dpi=160) plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary')
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"] 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_functions = 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 + 2 * self.CL_thickness ) * simulation_ratio #size of the cell in xy-plane is measured as a fraction of pyramid width sy = sx sz = ( self.pyramid_height + 1 * self.CL_thickness ) * simulation_ratio #z-"height" of sim. cell measured as a fraction of pyramid height. sh = substrate_height 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 == "Pd": CL_material = Pd elif self.CL_material == "Pt": CL_material = Pt elif self.CL_material == "In": #we have n,k assuming relationship between n,k, and epsilon (er, ei) is er = n^2-k^2 and ei = 2nk if self.frequency_center == 1.85: #green, wavelength 541 n = 1.0346 #n=4.9 k = 4.8714 epsilon_r = n**2 - k**2 epsilon_im = 2 * n * k elif self.frequency_center == 2.04: #blue, wavelength 449 n = 0.75754 k = 4.1783 epsilon_r = n**2 - k**2 epsilon_im = 2 * n * k elif self.frequency_center == 1.58: #red, wavelength 632 n = 1.2568 k = 5.5422 epsilon_r = n**2 - k**2 epsilon_im = 2 * n * k else: print('Warning: Indium not defined properly.') Indium = mp.Medium(epsilon=epsilon_r, D_conductivity=2 * math.pi * self.frequency_center * epsilon_im / epsilon_r) CL_material = Indium else: print( 'WARNING: Capping Layer material is in GaN, are you sure about that?' ) 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 ###SYMMETRIES######################################################### #"Symmetry logic." if use_symmetries: symmetry = create_symmetry(self) else: 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 SiC = mp.Medium(epsilon=2.6465**2) SubstrateEps = SiC #substrate epsilon #"Geometry to define the substrate and block of air to truncate the pyramid if self.truncation =/= 0" geometries = [] #Substrate geometries.append( mp.Block(center=mp.Vector3(0, 0, sz / 2 - sh / 2 + dpml / 2), size=mp.Vector3(2 * sx + 2 * dpml, 2 * sy + 2 * dpml, sh + dpml), material=SubstrateEps)) if geometry == False: geometries = [] print('Using geometries: ', geometries, 'argument: ', geometry) if (self.truncation_width > 0): #calculate height of truncation. i.e height of the pyramid that gets removed when truncated. truncation_height = (self.truncation_width / 2) * math.tan( (math.pi * 62) / 180) print('trunc h,w', truncation_height, self.truncation_width) else: truncation_height = 0 #variables used to keep track of pyramid size given a coating layer if (self.CL_thickness > 0): coating = True #TODO: Make pyramid angle an input in sim_spec.TODO: for th_tot, is it correct to take - CL_thickness (?). Yes as it should at the CL_thickness of 0.1 at top #increase size of pyramid to account for metal coating. Easier calculating material function this way. pyramid_width_tot = self.pyramid_width + 2 * self.CL_thickness #pyramid height assuming capping layer and no truncation pyramid_height_tot = self.pyramid_height + self.CL_thickness * math.tan( (math.pi * 62) / 180) #height of truncation assuming capping layer truncation_height_tot = truncation_height + self.CL_thickness * math.tan( (math.pi * 62) / 180) - self.CL_thickness else: coating = False # "Function for creating pyramid" #TODO: Move function definitions to pyramid. #paints out a hexagon with the help of 4 straight lines in the big if statement #here, v is measured from center to vertice. h is measured from center to edge. def isInsidexy2(vec): while (vec.z <= sz / 2 - sh and vec.z >= sz / 2 - sh - self.pyramid_height): v = (self.pyramid_width / (2 * self.pyramid_height)) * vec.z + ( self.pyramid_width / (2 * self.pyramid_height)) * ( sh + self.pyramid_height - sz / 2) h = math.cos(math.pi / 6) * v k = 1 / (2 * math.cos(math.pi / 6)) if (-h <= vec.x <= h and vec.y <= k * vec.x + v and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v and vec.y >= -k * vec.x - v): return GaN else: return air else: return air #function to create truncated pyramid def truncPyramid(vec): while (vec.z <= sz / 2 - sh and vec.z >= sz / 2 - sh - self.pyramid_height + truncation_height): v = (self.pyramid_width / (2 * self.pyramid_height)) * vec.z + ( self.pyramid_width / (2 * self.pyramid_height)) * ( sh + self.pyramid_height - sz / 2) h = math.cos(math.pi / 6) * v k = 1 / (2 * math.cos(math.pi / 6)) if (-h <= vec.x <= h and vec.y <= k * vec.x + v and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v and vec.y >= -k * vec.x - v): return GaN else: return air else: return air #function to create truncated pyramid with coating on def truncPyramidWithCoating(vec): #TODO: Optimize function while (vec.z <= sz / 2 - sh and vec.z >= sz / 2 - sh - pyramid_height_tot + truncation_height_tot): v = (pyramid_width_tot / (2 * pyramid_height_tot)) * vec.z + ( pyramid_width_tot / (2 * pyramid_height_tot)) * (sh + pyramid_height_tot - sz / 2) h = math.cos(math.pi / 6) * v k = 1 / (2 * math.cos(math.pi / 6)) v_inner = v - self.CL_thickness h_inner = h - self.CL_thickness if (-h <= vec.x <= h and vec.y <= k * vec.x + v and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v and vec.y >= -k * vec.x - v): while (vec.z >= sz / 2 - sh - self.pyramid_height + truncation_height): if (-h_inner <= vec.x <= h_inner and vec.y <= k * vec.x + v_inner and vec.y <= -k * vec.x + v_inner and vec.y >= k * vec.x - v_inner and vec.y >= -k * vec.x - v_inner): #inner pyramid, inside capping layer return GaN else: return CL_material return CL_material else: return air else: return air if material_functions == "truncPyramidwithMultiCoating": #cumulative width #thickness per layer, 0.005, 0.005, 0.3 CL_width = [0.005, 0.01, 0.015] if CL_width[2] - self.CL_thickness != 0: print( 'WARNING: Diff between CL_width and CL_thickness. They should be the same.' ) CL_material_in = [Ni, Au, Au] #CL_material_in = [mp.Medium(epsilon=10),mp.Medium(epsilon=20),mp.Medium(epsilon=30)] #function to create truncated pyramid with mutliple coatings def truncPyramidwithMultiCoating(vec): #TODO: Optimize function while (vec.z <= sz / 2 - sh and vec.z >= sz / 2 - sh - pyramid_height_tot + truncation_height_tot): v = (pyramid_width_tot / (2 * pyramid_height_tot)) * vec.z + ( pyramid_width_tot / (2 * pyramid_height_tot)) * (sh + pyramid_height_tot - sz / 2) h = math.cos(math.pi / 6) * v k = 1 / (2 * math.cos(math.pi / 6)) v_inner = v - self.CL_thickness h_inner = h - self.CL_thickness if (-h <= vec.x <= h and vec.y <= k * vec.x + v and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v and vec.y >= -k * vec.x - v): while (vec.z >= sz / 2 - sh - pyramid_height_tot + truncation_height_tot): if (-h_inner <= vec.x <= h_inner and vec.y <= k * vec.x + v_inner and vec.y <= -k * vec.x + v_inner and vec.y >= k * vec.x - v_inner and vec.y >= -k * vec.x - v_inner and vec.z >= sz / 2 - sh - pyramid_height_tot + truncation_height_tot + self.CL_thickness): #inner pyramid, inside capping layer return GaN else: if (-h_inner - CL_width[0] <= vec.x <= h_inner + CL_width[0] and vec.y <= k * vec.x + v_inner + CL_width[0] and vec.y <= -k * vec.x + v_inner + CL_width[0] and vec.y >= k * vec.x - v_inner - CL_width[0] and vec.y >= -k * vec.x - v_inner - CL_width[0] and vec.z >= sz / 2 - sh - pyramid_height_tot + truncation_height_tot + (CL_width[2] - CL_width[0])): #most inner capping layer return CL_material_in[0] if (-h_inner - CL_width[1] <= vec.x <= h_inner + CL_width[1] and vec.y <= k * vec.x + v_inner + CL_width[1] and vec.y <= -k * vec.x + v_inner + CL_width[1] and vec.y >= k * vec.x - v_inner - CL_width[1] and vec.y >= -k * vec.x - v_inner - CL_width[1] and vec.z >= sz / 2 - sh - pyramid_height_tot + truncation_height_tot + (CL_width[1] - CL_width[0])): #middle layer return CL_material_in[1] #outmost capping layer return CL_material_in[2] else: return air else: return air #TODO: Clean up and move these functions materialFunction = None if material_functions == "truncPyramidWithCoating": materialFunction = truncPyramidWithCoating if self.CL_thickness <= 0: print( '###SIMULATION STOPPED###################################################################################' ) print( 'WARNING: You are attempting to run material function ' + material_functions, ' with no capping layer depth.') sys.exit() elif material_functions == "isInsidexy2": materialFunction = isInsidexy2 elif material_functions == "truncPyramid": materialFunction = truncPyramid elif material_functions == "truncPyramidwithMultiCoating": materialFunction = truncPyramidwithMultiCoating else: materialFunction = None print('materialfunction in use: ', materialFunction) #TODO: Clean up, create more warning messages and move these functions if self.CL_thickness > 0 and material_functions != "truncPyramidWithCoating": print( 'WARNING: You have defined a Capping Layer (CL) thickness without using a pyramid creator function with a capping layer.' ) ###PML_LAYERS################################################################### pml_layer = [mp.PML(dpml)] ###SOURCE####################################################################### #"A gaussian with pulse source proportional to exp(-iwt-(t-t_0)^2/(2w^2))" #Assuming a (MC_thickness) nm capping layer, (that is included in the total pyramid height and width) #I can assume the inner pyramid to be pyramid height - 100 nm and pyramid width - 2*100 nm #"Source position" if self.source_on_wall == True: if self.truncation_width > 0: inner_pyramid_height = self.pyramid_height * ( 1 - self.truncation_width / self.pyramid_width) #if source is placed on wall we need to convert position to MEEP coordinates abs_source_position_x = ( inner_pyramid_height * self.source_position[2] * math.cos(math.pi / 6)) / math.tan(62 * math.pi / 180) abs_source_position_y = self.source_position[1] * math.tan( math.pi / 6) * (inner_pyramid_height * self.source_position[2] * math.cos(math.pi / 6)) / math.tan(62 * math.pi / 180) #abs_source_position_z=sz/2-sh-inner_pyramid_height+inner_pyramid_height*(self.source_position[2]) abs_source_position_z = sz / 2 - sh - inner_pyramid_height + self.source_position[ 2] print('spos with source_on_wall:', abs_source_position_x, abs_source_position_y, abs_source_position_z) else: #else it is assumed source is placed on top of pyramid #TODO: implement function that can take of case with both truncated and non-truncated pyramid AND move source in x,y on truncated source if self.truncation_width > 0: inner_pyramid_height = self.pyramid_height - truncation_height abs_source_position_x = 0 abs_source_position_y = 0 print('inn pyr, ph, th, sz/2, sh, szpos', inner_pyramid_height, self.pyramid_height, truncation_height, sz / 2, sh, self.source_position[2]) abs_source_position_z = sz / 2 - sh - inner_pyramid_height + self.source_position[ 2] print('spos with source_on_top:', abs_source_position_x, abs_source_position_y, abs_source_position_z) if True: abs_source_position_z = abs_source_position_z + 1 / ( 2 * resolution ) #add 0.5 pixel to truncate source 1 pixel down #Sets the polarization of the dipole to be in the plane, that is the pyramid wall #TODO: Move to pyramid.py if polarization_in_plane == True: #change source direction to be parallell with pyramid wall. self.source_direction = PolarizeInPlane(self.source_direction, self.pyramid_height, self.pyramid_width) print('new source dir', self.source_direction) source = [ mp.Source( mp.GaussianSource( frequency=self.frequency_center, fwidth=self.frequency_width, cutoff=self.cutoff), #gaussian current-source component=mp.Ex, amplitude=self.source_direction[0], 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.25, extra_materials=[CL_material], material_function=materialFunction, boundary_layers=pml_layer, split_chunks_evenly=False, resolution=resolution) ###SOURCE REGION################################################### pixels = 2 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_bottom = sim.add_flux(self.frequency_center, self.frequency_width, self.number_of_freqs, fr5) flux_total = sim.add_flux(self.frequency_center, self.frequency_width, self.number_of_freqs, fr1, fr2, fr3, fr4, fr6) #calculate flux for flux regions print('freqs', mp.get_flux_freqs(flux_total)) 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) ###FIELD CALCULATIONS########################################################### #"Tells meep with the function 'add_flux' to collect and calculate the flux in the corresponding regions and put them in a flux data object" #flux_data_tot=sim.get_flux_data(flux_total) #get flux data for later reloading ###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-5)) ###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 = [0] * 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 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: flux_tot_value = [ 0 ] * self.number_of_freqs #total flux out from box flux_tot_ff_ratio = [0] * self.number_of_freqs flux_tot_out = list( np.add(mp.get_fluxes(flux_total), mp.get_fluxes(flux_bottom))) #save total flux data flux_tot_bottom = mp.get_fluxes(flux_bottom) freqs_out = mp.get_flux_freqs(flux_total) print('freqs', freqs_out, 'tot_out', flux_tot_out, 'bottom', flux_tot_bottom) if calculate_source_flux: source_flux_out = mp.get_fluxes(flux_source) elapsed_time = round((time.time() - start) / 60, 1) output_fields = None for n in range(len(flux_tot_out)): flux_tot_out[n] = round(flux_tot_out[n], 11) ##Some processing to calculate the flux ratios per frequency 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 ff_calculations: for n in range(len(flux_tot_out)): 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) if output_ff: output_fields = fields 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, "fields": output_fields, "flux_bottom": flux_tot_bottom, "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, "fields": output_fields, "flux_bottom": flux_tot_bottom, "Elapsed time (min)": elapsed_time }
def test_invalid_dft_ldos(self): with self.assertRaises(TypeError): self.sim.run(mp.dft_ldos(mp.Ldos(self.fcen, 0, 1)), until=200)
df = 0.2 sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3()) ] symmetries = [mp.Mirror(mp.Y)] Th = 500 sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=sources, symmetries=symmetries, resolution=resolution) h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df) sim.run(mp.after_sources(h), until_after_sources=Th) m = h.modes[0] f = m.freq Q = m.Q Vmode = 0.25 * a * a print("ldos0:, {}".format(Q / Vmode / (2 * math.pi * f * math.pi * 0.5))) sim.reset_meep() T = 2 * Q * (1 / f) sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)