def run_mode_coeffs(self, mode_num, kpoint_func): resolution = 15 w = 1 # width of waveguide L = 10 # length of waveguide Si = mp.Medium(epsilon=12.0) dair = 3.0 dpml = 3.0 sx = dpml + L + dpml sy = dpml + dair + w + dair + dpml cell_size = mp.Vector3(sx, sy, 0) prism_x = sx + 1 prism_y = w / 2 vertices = [ mp.Vector3(-prism_x, prism_y), mp.Vector3(prism_x, prism_y), mp.Vector3(prism_x, -prism_y), mp.Vector3(-prism_x, -prism_y) ] geometry = [mp.Prism(vertices, height=mp.inf, material=Si)] boundary_layers = [mp.PML(dpml)] # mode frequency fcen = 0.20 # > 0.5/sqrt(11) to have at least 2 modes sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=0.5 * fcen), eig_band=mode_num, size=mp.Vector3(0, sy - 2 * dpml, 0), center=mp.Vector3(-0.5 * sx + dpml, 0, 0), eig_match_freq=True, eig_resolution=32) ] sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=sources, symmetries=[mp.Mirror(mp.Y, phase=1 if mode_num % 2 == 1 else -1)]) xm = 0.5 * sx - dpml # x-coordinate of monitor mflux = sim.add_mode_monitor( fcen, 0, 1, mp.ModeRegion(center=mp.Vector3(xm, 0), size=mp.Vector3(0, sy - 2 * dpml))) mode_flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mp.Vector3(xm, 0), size=mp.Vector3(0, sy - 2 * dpml))) # sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(-0.5*sx+dpml,0), 1e-10)) sim.run(until_after_sources=100) modes_to_check = [ 1, 2 ] # indices of modes for which to compute expansion coefficients res = sim.get_eigenmode_coefficients(mflux, modes_to_check, kpoint_func=kpoint_func) self.assertTrue(res.kpoints[0].close(mp.Vector3(0.604301, 0, 0))) self.assertTrue(res.kpoints[1].close(mp.Vector3(0.494353, 0, 0), tol=1e-2)) self.assertTrue(res.kdom[0].close(mp.Vector3(0.604301, 0, 0))) self.assertTrue(res.kdom[1].close(mp.Vector3(0.494353, 0, 0), tol=1e-2)) mode_power = mp.get_fluxes(mode_flux)[0] TestPassed = True TOLERANCE = 5.0e-3 c0 = res.alpha[ mode_num - 1, 0, 0] # coefficient of forward-traveling wave for mode #mode_num for nm in range(1, len(modes_to_check) + 1): if nm != mode_num: cfrel = np.abs(res.alpha[nm - 1, 0, 0]) / np.abs(c0) cbrel = np.abs(res.alpha[nm - 1, 0, 1]) / np.abs(c0) if cfrel > TOLERANCE or cbrel > TOLERANCE: TestPassed = False self.sim = sim # test 1: coefficient of excited mode >> coeffs of all other modes self.assertTrue(TestPassed, msg="cfrel: {}, cbrel: {}".format(cfrel, cbrel)) # test 2: |mode coeff|^2 = power self.assertAlmostEqual(mode_power / abs(c0**2), 1.0, places=1) return res
def main(args): # Some parameters to describe the geometry: eps = 13 # dielectric constant of waveguide w = 1.2 # width of waveguide r = 0.36 # radius of holes d = 1.4 # defect spacing (ordinary spacing = 1) N = args.N # number of holes on either side of defect # The cell dimensions sy = args.sy # size of cell in y direction (perpendicular to wvg.) pad = 2 # padding between last hole and PML edge dpml = 1 # PML thickness sx = 2 * (pad + dpml + N) + d - 1 # size of cell in x direction cell = mp.Vector3(sx, sy, 0) blk = mp.Block(size=mp.Vector3(1e20, w, 1e20), material=mp.Medium(epsilon=eps)) geometry = [blk] for i in range(N): geometry.append(mp.Cylinder(r, center=mp.Vector3(d / 2 + i))) geometry.append(mp.Cylinder(r, center=mp.Vector3(-(d / 2 + i)))) fcen = args.fcen # pulse center frequency df = args.df # pulse frequency width nfreq = 500 # number of frequencies at which to compute flux sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[], boundary_layers=[mp.PML(dpml)], resolution=20) if args.resonant_modes: sim.sources.append( mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Hz, mp.Vector3())) sim.symmetries.append(mp.Mirror(mp.Y, phase=-1)) sim.symmetries.append(mp.Mirror(mp.X, phase=-1)) sim.run( # mp.at_beginning(mp.output_epsilon), mp.after_sources(mp.Harminv(mp.Hz, mp.Vector3(), fcen, df)), until_after_sources=400) # sim.run(mp.at_every(1 / fcen / 20, mp.output_hfield_z), until=1 / fcen) else: sim.sources.append( mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ey, mp.Vector3(dpml + (-0.5 * sx)), size=mp.Vector3(0, w))) sim.symmetries.append(mp.Mirror(mp.Y, phase=-1)) freg = mp.FluxRegion(center=mp.Vector3((0.5 * sx) - dpml - 0.5), size=mp.Vector3(0, 2 * w)) # transmitted flux trans = sim.add_flux(fcen, df, nfreq, freg) vol = mp.Volume(mp.Vector3(), size=mp.Vector3(sx)) sim.run(mp.at_beginning(mp.output_epsilon), mp.during_sources( mp.in_volume( vol, mp.to_appended("hz-slice", mp.at_every(0.4, mp.output_hfield_z)))), until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ey, mp.Vector3((0.5 * sx) - dpml - 0.5, 0), 1e-3)) sim.display_fluxes(trans) # print out the flux spectrum
def main(args=None): fcen = 1 / 1.54 df = 0.1 wvg_width = .7 wvg_height = .22 dpml = 1 dpad = 2 sim2d = False time_after_source = 500 eps_only = False subz = True resolution = 30 _nSi = 3.45 mode = get_excitation_mode("Ey") # we want to investigate tuning. Change this to add a different material instead of air: cover_material = mp.Medium(index=1.11) if args is not None: hx = args.diameter hy = args.diameter h_rel = args.h_rel n = args.n _nSi = args.n_si nSi = mp.Medium(index=_nSi) wvg = waveguide_1d( wvg_width=wvg_width, wvg_height=wvg_height, material=nSi, ) cav, cav_length = quadratic_radius_1d_symmetric( n_segments=n, hx=hx, hy=hy, h_rel=h_rel, material_tuning=cover_material) sx = dpml + dpad + cav_length + dpad + dpml sy = dpml + dpad + wvg_width + dpad + dpml sz = dpml + dpad + wvg_height + dpad + dpml subs_height = (sz - wvg_height) / 2 subs = substrate(substrate_height=subs_height, center=mp.Vector3(0, 0, -sz / 2 + subs_height / 2)) geometry = wvg + cav symmetries = [ mp.Mirror(mp.X, +1), mp.Mirror(mp.Y, -1), mp.Mirror(mp.Z, +1) ] if subz: geometry = wvg + cav + subs symmetries = [mp.Mirror(mp.X, +1), mp.Mirror(mp.Y, -1)] boundary = get_boundary_layer(dpml, sim2d=sim2d) if sim2d: sz = 0 cell_size = mp.Vector3(sx, sy, sz) sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mode, center=mp.Vector3()) ] sim = mp.Simulation(cell_size=cell_size, geometry=geometry, sources=sources, boundary_layers=boundary, symmetries=symmetries, resolution=resolution, progress_interval=100, default_material=cover_material) f = plt.figure(figsize=(10, 10)) sim.plot2D(ax=f.gca(), output_plane=mp.Volume(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, sy, 0))) f.savefig("xy_plane.pdf", format="PDF") f = plt.figure(figsize=(10, 10)) sim.plot2D(ax=f.gca(), output_plane=mp.Volume(center=mp.Vector3(0, 0, 0), size=mp.Vector3(sx, 0, sz))) f.savefig("xz_plane.pdf", format="PDF") f = plt.figure(figsize=(10, 10)) sim.plot2D(ax=f.gca(), output_plane=mp.Volume(center=mp.Vector3(0, 0, 0), size=mp.Vector3(0, sy, sz))) f.savefig("yz_plane.pdf", format="PDF") h = mp.Harminv(mode, mp.Vector3(0, 0, 0), fcen, df) # h_displaced = mp.Harminv(mode, mp.Vector3(0.05, 0.1, 0.02), fcen, df) if eps_only: sim.run(mp.at_beginning(mp.output_epsilon), until=0) else: # Don't output eps anymore to save disk space sim.run( mp.after_sources(h), # mp.after_sources(h_displaced), until_after_sources=time_after_source)
lcen = 0.5 # center wavelength fcen = 1/lcen # center frequency df = 0.2*fcen # frequency width focal_length = 200 # focal length of metalens spot_length = 100 # far field line length ff_res = 10 # far field resolution (points/μm) k_point = mp.Vector3(0,0,0) glass = mp.Medium(index=1.5) pml_layers = [mp.PML(thickness=dpml,direction=mp.X)] symmetries=[mp.Mirror(mp.Y)] def grating(gp,gh,gdc_list): sx = dpml+dsub+gh+dpad+dpml src_pt = mp.Vector3(-0.5*sx+dpml+0.5*dsub) mon_pt = mp.Vector3(0.5*sx-dpml-0.5*dpad) geometry = [mp.Block(material=glass, size=mp.Vector3(dpml+dsub,mp.inf,mp.inf), center=mp.Vector3(-0.5*sx+0.5*(dpml+dsub)))] num_cells = len(gdc_list) if num_cells == 1: sy = gp cell_size = mp.Vector3(sx,sy,0) sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df),
] else: sources = [ mp.Source(src=mp.GaussianSource(fsrc, fwidth=0.2 * fsrc) if compute_flux else mp.ContinuousSource(fsrc), center=mp.Vector3(), size=mp.Vector3(y=3 * w), component=mp.Ez) ] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, boundary_layers=pml_layers, sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.Y)] if rot_angle == 0 else []) if compute_flux: tran = sim.add_flux( fsrc, 0, 1, mp.FluxRegion(center=mp.Vector3(x=5), size=mp.Vector3(y=14))) sim.run(until_after_sources=50) res = sim.get_eigenmode_coefficients( tran, [1], eig_parity=mp.EVEN_Y + mp.ODD_Z if rot_angle == 0 else mp.ODD_Z, direction=mp.NO_DIRECTION, kpoint_func=lambda f, n: kpoint) print("flux:, {:.6f}, {:.6f}".format( mp.get_fluxes(tran)[0], abs(res.alpha[0, 0, 0])**2)) else:
def test_3d_with_symmetry(self): self._test_3d([mp.Mirror(mp.X), mp.Mirror(mp.Y), mp.Mirror(mp.Z)])
def pec_ground_plane_radiation(self): L = 8.0 # length of non-PML region dpml = 1.0 # thickness of PML sxy = dpml + L + dpml cell_size = mp.Vector3(sxy, sxy, 0) boundary_layers = [mp.PML(dpml)] fcen = 1 / self.wvl # The near-to-far field transformation feature only supports # homogeneous media which means it cannot explicitly take into # account the ground plane. As a workaround, we use two antennas # of _opposite_ sign surrounded by a single near2far box which # encloses both antennas. We then use an odd mirror symmetry to # cut the computational cell in half which is effectively # equivalent to a PEC boundary condition on one side. # Note: This setup means that the radiation pattern can only # be measured in the top half above the dipole. sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), component=self.src_cmpt, center=mp.Vector3(0, +self.h)), mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), component=self.src_cmpt, center=mp.Vector3(0, -self.h), amplitude=-1 if self.src_cmpt == mp.Ez else +1) ] symmetries = [ mp.Mirror(direction=mp.X, phase=+1 if self.src_cmpt == mp.Ez else -1), mp.Mirror(direction=mp.Y, phase=-1 if self.src_cmpt == mp.Ez else +1) ] sim = mp.Simulation(resolution=self.resolution, cell_size=cell_size, boundary_layers=boundary_layers, sources=sources, symmetries=symmetries, default_material=mp.Medium(index=self.n)) nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0, 2 * self.h), size=mp.Vector3(2 * self.h, 0)), mp.Near2FarRegion(center=mp.Vector3(0, -2 * self.h), size=mp.Vector3(2 * self.h, 0), weight=-1), mp.Near2FarRegion(center=mp.Vector3(self.h, 0), size=mp.Vector3(0, 4 * self.h)), mp.Near2FarRegion(center=mp.Vector3(-self.h, 0), size=mp.Vector3(0, 4 * self.h), weight=-1)) sim.run(until_after_sources=mp.stop_when_dft_decayed()) Pr = self.radial_flux(sim, nearfield_box, self.r) return Pr
dpml = 1 cell = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml) pml_layers = [mp.PML(dpml)] fcen = 1.0 df = 0.4 src_cmpt = mp.Ez sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), center=mp.Vector3(), component=src_cmpt) ] if src_cmpt == mp.Ex: symmetries = [mp.Mirror(mp.X, phase=-1), mp.Mirror(mp.Y, phase=+1)] elif src_cmpt == mp.Ey: symmetries = [mp.Mirror(mp.X, phase=+1), mp.Mirror(mp.Y, phase=-1)] elif src_cmpt == mp.Ez: symmetries = [mp.Mirror(mp.X, phase=+1), mp.Mirror(mp.Y, phase=+1)] sim = mp.Simulation(cell_size=cell, resolution=resolution, sources=sources, symmetries=symmetries, boundary_layers=pml_layers) nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(center=mp.Vector3(0, +0.5 * sxy), size=mp.Vector3(sxy, 0)),
def test_get_point(self): sxy = 6 # cell size dpml = 1 # thickness of PML def sinusoid(p): r = (p.x**2+p.y**2)**0.5 return mp.Medium(index=1.0+math.sin(2*math.pi*r)**2) geometry = [mp.Block(center=mp.Vector3(), size=mp.Vector3(sxy,sxy), material=sinusoid)] src = [mp.Source(mp.GaussianSource(1.0, fwidth=0.1), component=mp.Ez, center=mp.Vector3())] sim = mp.Simulation(cell_size=mp.Vector3(sxy,sxy), geometry=geometry, sources=src, k_point=mp.Vector3(), resolution=20, symmetries=[mp.Mirror(mp.X),mp.Mirror(mp.Y)], boundary_layers=[mp.PML(dpml)]) sim.run(until_after_sources=100) ## reference values for Ez and epsilon from serial run ez_ref = [ -0.0002065983, -0.0001954795, -0.0000453570, 0.0000311267, -0.0000121473, -0.0000410032, -0.0000341301, -0.0000275021, -0.0000397990, -0.0000351730, 0.0000079602, 0.0000227437, -0.0001092821, -0.0002202751, -0.0001408186, 0.0006325076, 0.0024890489, 0.0027476069, 0.0014815873, 0.0004714913, -0.0004332029, -0.0007101315, -0.0003818581, -0.0000748507, 0.0001408819, 0.0001119776, 0.0000395008, 0.0000078844, -0.0000010431 ] eps_ref = [ 1.6458346134, 1.2752837068, 1.0974010956, 1.0398089537, 1.0465784716, 1.0779924737, 1.1059439286, 1.1135579291, 1.0971979186, 1.0653178566, 1.0391657283, 1.0513779677, 1.1466009312, 1.3882154483, 1.8496939317, 2.5617731415, 3.3788212533, 3.9019494270, 3.6743431894, 2.7285622651, 1.6635165033, 1.0891237010, 1.1485969863, 1.9498398061, 3.3100416367, 3.9038800599, 2.8471862395, 1.4742605488, 1.0370162714 ] x = np.linspace(-0.865692,2.692867,29) places = 5 if mp.is_single_precision() else 10 for j in range(x.size): self.assertAlmostEqual(np.real(sim.get_field_point(mp.Ez, mp.Vector3(x[j],-0.394862))), ez_ref[j], places=places) self.assertAlmostEqual(sim.get_epsilon_point(mp.Vector3(x[j],2.967158)), eps_ref[j], places=places)
frq_min = 1/wvl_max frq_max = 1/wvl_min frq_cen = 0.5*(frq_min+frq_max) dfrq = frq_max-frq_min nfrq = 100 ## at least 8 pixels per smallest wavelength, i.e. np.floor(8/wvl_min) resolution = 25 dpml = 0.5*wvl_max dair = 0.5*wvl_max pml_layers = [mp.PML(thickness=dpml)] symmetries = [mp.Mirror(mp.Y), mp.Mirror(mp.Z,phase=-1)] s = 2*(dpml+dair+r) cell_size = mp.Vector3(s,s,s) # is_integrated=True necessary for any planewave source extending into PML sources = [mp.Source(mp.GaussianSource(frq_cen,fwidth=dfrq,is_integrated=True), center=mp.Vector3(-0.5*s+dpml), size=mp.Vector3(0,s,s), component=mp.Ez)] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, sources=sources,
# moving point charge with superluminal phase velocity in dielectric media emitting Cherenkov radiation import meep as mp sx = 60 sy = 60 cell_size = mp.Vector3(sx, sy, 0) dpml = 1.0 pml_layers = [mp.PML(thickness=dpml)] v = 0.7 # velocity of point charge symmetries = [mp.Mirror(direction=mp.Y)] sim = mp.Simulation(resolution=10, cell_size=cell_size, default_material=mp.Medium(index=1.5), symmetries=symmetries, boundary_layers=pml_layers) def move_source(sim): sim.change_sources([ mp.Source(mp.ContinuousSource(frequency=1e-10), component=mp.Ex, center=mp.Vector3(-0.5 * sx + dpml + v * sim.meep_time())) ]) sim.run(move_source,
def run_mode_coeffs(self, mode_num, kpoint_func, nf=1, resolution=15): w = 1 # width of waveguide L = 10 # length of waveguide Si = mp.Medium(epsilon=12.0) dair = 3.0 dpml = 3.0 sx = dpml + L + dpml sy = dpml + dair + w + dair + dpml cell_size = mp.Vector3(sx, sy, 0) prism_x = sx + 1 prism_y = w / 2 vertices = [ mp.Vector3(-prism_x, prism_y), mp.Vector3(prism_x, prism_y), mp.Vector3(prism_x, -prism_y), mp.Vector3(-prism_x, -prism_y) ] geometry = [mp.Prism(vertices, height=mp.inf, material=Si)] boundary_layers = [mp.PML(dpml)] # mode frequency fcen = 0.20 # > 0.5/sqrt(11) to have at least 2 modes df = 0.5 * fcen source = mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=df), eig_band=mode_num, size=mp.Vector3(0, sy - 2 * dpml, 0), center=mp.Vector3(-0.5 * sx + dpml, 0, 0), eig_match_freq=True, eig_resolution=2 * resolution) sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=[source], symmetries=[mp.Mirror(mp.Y, phase=1 if mode_num % 2 == 1 else -1)]) xm = 0.5 * sx - dpml # x-coordinate of monitor mflux = sim.add_mode_monitor( fcen, df, nf, mp.ModeRegion(center=mp.Vector3(xm, 0), size=mp.Vector3(0, sy - 2 * dpml))) mode_flux = sim.add_flux( fcen, df, nf, mp.FluxRegion(center=mp.Vector3(xm, 0), size=mp.Vector3(0, sy - 2 * dpml))) # sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(-0.5*sx+dpml,0), 1e-10)) sim.run(until_after_sources=200) ################################################## # If the number of analysis frequencies is >1, we # are testing the unit-power normalization # of the eigenmode source: we observe the total # power flux through the mode_flux monitor (which # equals the total power emitted by the source as # there is no scattering in this ideal waveguide) # and check that it agrees with the prediction # of the eig_power() class method in EigenmodeSource. ################################################## if nf > 1: power_observed = mp.get_fluxes(mode_flux) freqs = mp.get_flux_freqs(mode_flux) power_expected = [source.eig_power(f) for f in freqs] return freqs, power_expected, power_observed modes_to_check = [ 1, 2 ] # indices of modes for which to compute expansion coefficients res = sim.get_eigenmode_coefficients(mflux, modes_to_check, kpoint_func=kpoint_func) self.assertTrue(res.kpoints[0].close(mp.Vector3(0.604301, 0, 0))) self.assertTrue(res.kpoints[1].close(mp.Vector3(0.494353, 0, 0), tol=1e-2)) self.assertTrue(res.kdom[0].close(mp.Vector3(0.604301, 0, 0))) self.assertTrue(res.kdom[1].close(mp.Vector3(0.494353, 0, 0), tol=1e-2)) self.assertAlmostEqual(res.cscale[0], 0.50000977, places=5) self.assertAlmostEqual(res.cscale[1], 0.50096888, places=5) mode_power = mp.get_fluxes(mode_flux)[0] TestPassed = True TOLERANCE = 5.0e-3 c0 = res.alpha[ mode_num - 1, 0, 0] # coefficient of forward-traveling wave for mode #mode_num for nm in range(1, len(modes_to_check) + 1): if nm != mode_num: cfrel = np.abs(res.alpha[nm - 1, 0, 0]) / np.abs(c0) cbrel = np.abs(res.alpha[nm - 1, 0, 1]) / np.abs(c0) if cfrel > TOLERANCE or cbrel > TOLERANCE: TestPassed = False self.sim = sim # test 1: coefficient of excited mode >> coeffs of all other modes self.assertTrue(TestPassed, msg="cfrel: {}, cbrel: {}".format(cfrel, cbrel)) # test 2: |mode coeff|^2 = power self.assertAlmostEqual(mode_power / abs(c0**2), 1.0, places=1) return res
def wvg_flux(self, res, att_coeff): """ Computes the Poynting flux in a single-mode waveguide at two locations (5 and 10 μm) downstream from the source given the grid resolution res (pixels/μm) and material attenuation coefficient att_coeff (dB/cm). """ cell_size = mp.Vector3(14., 14.) pml_layers = [mp.PML(thickness=2.)] w = 1. # width of waveguide fsrc = 0.15 # frequency (in vacuum) # note: MPB can only compute modes of lossless material systems. # The imaginary part of ε is ignored and the launched # waveguide mode is therefore inaccurate. For small values # of imag(ε) (which is proportional to att_coeff), this # inaccuracy tends to be insignificant. sources = [ mp.EigenModeSource(src=mp.GaussianSource(fsrc, fwidth=0.2 * fsrc), center=mp.Vector3(-5.), size=mp.Vector3(y=10.), eig_parity=mp.EVEN_Y + mp.ODD_Z) ] # ref: https://en.wikipedia.org/wiki/Mathematical_descriptions_of_opacity # Note that this is the loss of a planewave, which is only approximately # the loss of a waveguide mode. In principle, we could compute the latter # semi-analytically if we wanted to run this unit test to greater accuracy # (e.g. to test convergence with resolution). n_eff = np.sqrt(12.) + 1j * (1 / fsrc) * (dB_cm_to_dB_um * att_coeff) / (4 * np.pi) eps_eff = n_eff * n_eff # ref: https://meep.readthedocs.io/en/latest/Materials/#conductivity-and-complex sigma_D = 2 * np.pi * fsrc * np.imag(eps_eff) / np.real(eps_eff) geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, w, mp.inf), material=mp.Medium(epsilon=np.real(eps_eff), D_conductivity=sigma_D)) ] sim = mp.Simulation(cell_size=cell_size, resolution=res, boundary_layers=pml_layers, sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.Y)]) tran1 = sim.add_flux( fsrc, 0, 1, mp.FluxRegion(center=mp.Vector3(x=0.), size=mp.Vector3(y=10.))) tran2 = sim.add_flux( fsrc, 0, 1, mp.FluxRegion(center=mp.Vector3(x=5.), size=mp.Vector3(y=10.))) sim.run(until_after_sources=20) return mp.get_fluxes(tran1)[0], mp.get_fluxes(tran2)[0]
def test_mode_decomposition(self): resolution = 10 w1 = 1 w2 = 2 Lw = 2 dair = 3.0 dpml = 5.0 sy = dpml + dair + w2 + dair + dpml half_w1 = 0.5 * w1 half_w2 = 0.5 * w2 Si = mp.Medium(epsilon=12.0) boundary_layers = [mp.PML(dpml)] lcen = 6.67 fcen = 1 / lcen symmetries = [mp.Mirror(mp.Y)] Lt = 2 sx = dpml + Lw + Lt + Lw + dpml cell_size = mp.Vector3(sx, sy, 0) prism_x = sx + 1 half_Lt = 0.5 * Lt src_pt = mp.Vector3(-0.5 * sx + dpml + 0.2 * Lw, 0, 0) sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), component=mp.Ez, center=src_pt, size=mp.Vector3(0, sy - 2 * dpml, 0), eig_match_freq=True, eig_parity=mp.ODD_Z + mp.EVEN_Y) ] vertices = [ mp.Vector3(-prism_x, half_w1), mp.Vector3(prism_x, half_w1), mp.Vector3(prism_x, -half_w1), mp.Vector3(-prism_x, -half_w1) ] sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=[mp.Prism(vertices, height=mp.inf, material=Si)], sources=sources, symmetries=symmetries) mon_pt = mp.Vector3(-0.5 * sx + dpml + 0.5 * Lw, 0, 0) flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy - 2 * dpml, 0))) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, src_pt, 1e-9)) incident_coeffs, vgrp, kpoints = sim.get_eigenmode_coefficients( flux, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) incident_flux = mp.get_fluxes(flux) incident_flux_data = sim.get_flux_data(flux) sim.reset_meep() vertices = [ mp.Vector3(-prism_x, half_w1), mp.Vector3(-half_Lt, half_w1), mp.Vector3(half_Lt, half_w2), mp.Vector3(prism_x, half_w2), mp.Vector3(prism_x, -half_w2), mp.Vector3(half_Lt, -half_w2), mp.Vector3(-half_Lt, -half_w1), mp.Vector3(-prism_x, -half_w1) ] sim = mp.Simulation( resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=[mp.Prism(vertices, height=mp.inf, material=Si)], sources=sources, symmetries=symmetries) refl_flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy - 2 * dpml, 0))) sim.load_minus_flux_data(refl_flux, incident_flux_data) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, src_pt, 1e-9)) coeffs, vgrp, kpoints = sim.get_eigenmode_coefficients( refl_flux, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) taper_flux = mp.get_fluxes(refl_flux) self.assertAlmostEqual(abs(coeffs[0, 0, 1])**2 / abs(incident_coeffs[0, 0, 0])**2, -taper_flux[0] / incident_flux[0], places=4)
wvl_min = 0.400 wvl_max = 0.700 frq_min = 1 / wvl_max frq_max = 1 / wvl_min frq_cen = 0.5 * (frq_min + frq_max) dfrq = frq_max - frq_min nfrq = 100 Material = Au resolution = 300 dpml = 0.11 pml_layers = [ mp.PML(dpml, direction=mp.X, side=mp.High), mp.Absorber(dpml, direction=mp.X, side=mp.Low) ] if (k == mp.Ey): symmetries = [mp.Mirror(mp.Y, phase=-1)] else: symmetries = [mp.Mirror(mp.Y)] celly = (spacing_thickness + block_thicknessy) cellx = block_thicknessx + 2 * dpml + 2 * offsetx sources = [ mp.Source(mp.GaussianSource(frq_cen, fwidth=dfrq, is_integrated=True), center=mp.Vector3(-0.5 * cellx + dpml + 0.01), size=mp.Vector3(0, celly), component=k) ]
def main(args): resolution = 20 # pixels/um eps = 13 # dielectric constant of waveguide w = 1.2 # width of waveguide r = 0.36 # radius of holes d = 1.4 # defect spacing (ordinary spacing = 1) N = args.N # number of holes on either side of defect sy = args.sy # size of cell in y direction (perpendicular to wvg.) pad = 2 # padding between last hole and PML edge dpml = 1 # PML thickness sx = 2 * (pad + dpml + N) + d - 1 # size of cell in x direction cell = mp.Vector3(sx, sy, 0) blk = mp.Block(size=mp.Vector3(mp.inf, w, mp.inf), material=mp.Medium(epsilon=eps)) geometry = [blk] for i in range(N): geometry.append(mp.Cylinder(r, center=mp.Vector3(d / 2 + i))) geometry.append(mp.Cylinder(r, center=mp.Vector3(-(d / 2 + i)))) pml_layers = [mp.PML(1.0)] fcen = args.fcen # pulse center frequency df = args.df # pulse frequency width src = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(-0.5 * sx + dpml), size=mp.Vector3(0, w)) ] sym = [mp.Mirror(mp.Y, phase=-1)] sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=src, symmetries=sym, resolution=resolution) freg = mp.FluxRegion(center=mp.Vector3(0.5 * sx - dpml - 0.5), size=mp.Vector3(0, 2 * w)) nfreq = 500 # number of frequencies at which to compute flux # transmitted flux trans = sim.add_flux(fcen, df, nfreq, freg) vol = mp.Volume(mp.Vector3(0), size=mp.Vector3(sx)) plt.figure(dpi=150) sim.plot2D() #plt.show() plt.savefig('2Dstructure.png') sim.run(mp.at_beginning(mp.output_epsilon), mp.during_sources( mp.in_volume( vol, mp.to_appended("hz-slice", mp.at_every(0.4, mp.output_hfield_z)))), until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ey, mp.Vector3(0.5 * sx - dpml - 0.5), 1e-3)) sim.display_fluxes(trans) # print out the flux spectrum
def test_1d_with_symmetry(self): self._test_1d([mp.Mirror(mp.X)])
def run_test(self, nfreqs): eps = 13 w = 1.2 r = 0.36 d = 1.4 N = 3 sy = 6 pad = 2 dpml = 1 sx = 2 * (pad + dpml + N) + d - 1 cell = mp.Vector3(sx, sy, 0) geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, w, mp.inf), material=mp.Medium(epsilon=eps)) ] for i in range(N): geometry.append(mp.Cylinder(r, center=mp.Vector3(d / 2 + i))) geometry.append(mp.Cylinder(r, center=mp.Vector3(d / -2 - i))) pml_layers = mp.PML(dpml) resolution = 10 fcen = 0.25 df = 0.2 sources = mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Hz, center=mp.Vector3()) symmetries = [mp.Mirror(mp.Y, phase=-1), mp.Mirror(mp.X, phase=-1)] d1 = 0.2 sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=[sources], symmetries=symmetries, boundary_layers=[pml_layers], resolution=resolution) nearfield = sim.add_near2far( fcen, 0 if nfreqs == 1 else 0.1, nfreqs, mp.Near2FarRegion(mp.Vector3(0, 0.5 * w + d1), size=mp.Vector3(2 * dpml - sx)), mp.Near2FarRegion(mp.Vector3(-0.5 * sx + dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(0, d1), weight=-1.0), mp.Near2FarRegion(mp.Vector3(0.5 * sx - dpml, 0.5 * w + 0.5 * d1), size=mp.Vector3(0, d1))) sim.run(until=200) d2 = 20 h = 4 vol = mp.Volume(mp.Vector3(0, (0.5 * w) + d2 + (0.5 * h)), size=mp.Vector3(sx - 2 * dpml, h)) result = sim.get_farfields(nearfield, resolution, where=vol) fname = 'cavity-farfield.h5' if nfreqs == 1 else 'cavity-farfield-4-freqs.h5' ref_file = os.path.join(self.data_dir, fname) with h5py.File(ref_file, 'r') as f: # Get reference data into memory ref_ex = mp.complexarray(f['ex.r'][()], f['ex.i'][()]) ref_ey = mp.complexarray(f['ey.r'][()], f['ey.i'][()]) ref_ez = mp.complexarray(f['ez.r'][()], f['ez.i'][()]) ref_hx = mp.complexarray(f['hx.r'][()], f['hx.i'][()]) ref_hy = mp.complexarray(f['hy.r'][()], f['hy.i'][()]) ref_hz = mp.complexarray(f['hz.r'][()], f['hz.i'][()]) np.testing.assert_allclose(ref_ex, result['Ex']) np.testing.assert_allclose(ref_ey, result['Ey']) np.testing.assert_allclose(ref_ez, result['Ez']) np.testing.assert_allclose(ref_hx, result['Hx']) np.testing.assert_allclose(ref_hy, result['Hy']) np.testing.assert_allclose(ref_hz, result['Hz'])
def test_poynting_theorem(self): """Unit test for near-to-far field transformation in 2d. Verifies that the Poynting flux of an Ez-polarized point dipole source in vacuum is independent of the shape of the enclosing measurement box due to Poynting's theorem by considering three arrangements: (1) bounding box of thenear fields, (2) bounding circle of the far fields, and (3) bounding box of the far fields. """ resolution = 50 sxy = 4 dpml = 1 cell = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml) pml_layers = mp.PML(dpml) fcen = 1.0 df = 0.4 sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), center=mp.Vector3(), component=mp.Ez) ] symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Y)] sim = mp.Simulation(cell_size=cell, resolution=resolution, sources=sources, symmetries=symmetries, boundary_layers=[pml_layers]) nearfield_box = sim.add_near2far( fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(y=0.5 * sxy), size=mp.Vector3(sxy)), mp.Near2FarRegion(mp.Vector3(y=-0.5 * sxy), size=mp.Vector3(sxy), weight=-1), mp.Near2FarRegion(mp.Vector3(0.5 * sxy), size=mp.Vector3(y=sxy)), mp.Near2FarRegion(mp.Vector3(-0.5 * sxy), size=mp.Vector3(y=sxy), weight=-1)) flux_box = sim.add_flux( fcen, 0, 1, mp.FluxRegion(mp.Vector3(y=0.5 * sxy), size=mp.Vector3(sxy)), mp.FluxRegion(mp.Vector3(y=-0.5 * sxy), size=mp.Vector3(sxy), weight=-1), mp.FluxRegion(mp.Vector3(0.5 * sxy), size=mp.Vector3(y=sxy)), mp.FluxRegion(mp.Vector3(-0.5 * sxy), size=mp.Vector3(y=sxy), weight=-1)) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(), 1e-8)) near_flux = mp.get_fluxes(flux_box)[0] r = 1000 / fcen # radius of far field circle Pr = self.radial_flux(sim, nearfield_box, r) far_flux_circle = 4 * np.sum(Pr) * 0.5 * np.pi * r / len(Pr) rr = 20 / fcen # length of far-field square box res_far = 20 # resolution of far-field square box far_flux_square = (nearfield_box.flux( mp.Y, mp.Volume(center=mp.Vector3(y=0.5 * rr), size=mp.Vector3(rr)), res_far)[0] - nearfield_box.flux( mp.Y, mp.Volume(center=mp.Vector3(y=-0.5 * rr), size=mp.Vector3(rr)), res_far)[0] + nearfield_box.flux( mp.X, mp.Volume(center=mp.Vector3(0.5 * rr), size=mp.Vector3(y=rr)), res_far)[0] - nearfield_box.flux( mp.X, mp.Volume(center=mp.Vector3(-0.5 * rr), size=mp.Vector3(y=rr)), res_far)[0]) print("flux:, {:.6f}, {:.6f}, {:.6f}".format(near_flux, far_flux_circle, far_flux_square)) self.assertAlmostEqual(near_flux, far_flux_circle, places=2) self.assertAlmostEqual(far_flux_circle, far_flux_square, places=2) self.assertAlmostEqual(far_flux_square, near_flux, places=2)
def main(args): resolution = 30 nSi = 3.45 Si = mp.Medium(index=nSi) dpml = 1.0 sx = 5 sy = 3 cell = mp.Vector3(sx + 2 * dpml, sy + 2 * dpml) pml_layers = mp.PML(dpml) a = 1.0 # waveguide width s = args.s # waveguide separation distance geometry = [ mp.Block(center=mp.Vector3(-0.5 * (s + a)), size=mp.Vector3(a, a, mp.inf), material=Si), mp.Block(center=mp.Vector3(0.5 * (s + a)), size=mp.Vector3(a, a, mp.inf), material=Si) ] xodd = args.xodd symmetries = [ mp.Mirror(mp.X, phase=-1.0 if xodd else 1.0), mp.Mirror(mp.Y, phase=-1.0) ] beta = 0.5 k_point = mp.Vector3(z=beta) fcen = 0.22 df = 0.06 sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(-0.5 * (s + a)), size=mp.Vector3(a, a)), mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(0.5 * (s + a)), size=mp.Vector3(a, a), amplitude=-1.0 if xodd else 1.0) ] sim = mp.Simulation(resolution=resolution, cell_size=cell, boundary_layers=[pml_layers], geometry=geometry, symmetries=symmetries, k_point=k_point, sources=sources, dimensions=3) h = mp.Harminv(mp.Ey, mp.Vector3(0.5 * (s + a)), fcen, df) sim.run(mp.after_sources(h), until_after_sources=200) f = h.modes[0].freq print("freq:, {}, {}".format(s, f)) sim.reset_meep() new_sources = [ mp.EigenModeSource(src=mp.GaussianSource(f, fwidth=df), component=mp.Ey, size=mp.Vector3(a, a), center=mp.Vector3(-0.5 * (s + a)), eig_kpoint=k_point, eig_match_freq=True, eig_parity=mp.ODD_Y), mp.EigenModeSource(src=mp.GaussianSource(f, fwidth=df), component=mp.Ey, size=mp.Vector3(a, a), center=mp.Vector3(0.5 * (s + a)), eig_kpoint=k_point, eig_match_freq=True, eig_parity=mp.ODD_Y, amplitude=-1.0 if xodd else 1.0) ] sim.change_sources(new_sources) flx_reg = mp.FluxRegion(direction=mp.Z, center=mp.Vector3(), size=mp.Vector3(1.2 * (2 * a + s), 1.2 * a)) wvg_pwr = sim.add_flux(f, 0, 1, flx_reg) frc_reg1 = mp.ForceRegion(mp.Vector3(0.5 * s), mp.X, weight=1.0, size=mp.Vector3(y=a)) frc_reg2 = mp.ForceRegion(mp.Vector3(0.5 * s + a), mp.X, weight=-1.0, size=mp.Vector3(y=a)) wvg_force = sim.add_force(f, 0, 1, frc_reg1, frc_reg2) runtime = 5000 sim.run(until_after_sources=runtime) sim.display_fluxes(wvg_pwr) sim.display_forces(wvg_force)
def grating(gp, gh, gdc, oddz): sx = dpml + dsub + gh + dpad + dpml sy = gp cell_size = mp.Vector3(sx, sy, 0) pml_layers = [mp.PML(thickness=dpml, direction=mp.X)] src_pt = mp.Vector3(-0.5 * sx + dpml + 0.5 * dsub, 0, 0) sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez if oddz else mp.Hz, center=src_pt, size=mp.Vector3(0, sy, 0)) ] symmetries = [mp.Mirror(mp.Y, phase=+1 if oddz else -1)] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, k_point=k_point, default_material=glass, sources=sources, symmetries=symmetries) mon_pt = mp.Vector3(0.5 * sx - dpml - 0.5 * dpad, 0, 0) flux_mon = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy, 0))) sim.run(until_after_sources=100) input_flux = mp.get_fluxes(flux_mon) sim.reset_meep() geometry = [ mp.Block(material=glass, size=mp.Vector3(dpml + dsub, mp.inf, mp.inf), center=mp.Vector3(-0.5 * sx + 0.5 * (dpml + dsub), 0, 0)), mp.Block(material=glass, size=mp.Vector3(gh, gdc * gp, mp.inf), center=mp.Vector3(-0.5 * sx + dpml + dsub + 0.5 * gh, 0, 0)) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, geometry=geometry, k_point=k_point, sources=sources, symmetries=symmetries) mode_mon = sim.add_flux( fcen, df, nfreq, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy, 0))) sim.run(until_after_sources=300) freqs = mp.get_eigenmode_freqs(mode_mon) res = sim.get_eigenmode_coefficients( mode_mon, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y if oddz else mp.EVEN_Z + mp.ODD_Y) coeffs = res.alpha mode_wvl = [1 / freqs[nf] for nf in range(nfreq)] mode_tran = [ abs(coeffs[0, nf, 0])**2 / input_flux[nf] for nf in range(nfreq) ] mode_phase = [np.angle(coeffs[0, nf, 0]) for nf in range(nfreq)] return mode_wvl, mode_tran, mode_phase
# The source will now be the usual Gaussian pulse centered at `fcen`, located at one edge of the cell just outside the PML, at `x = - 0.5 * sx + dpml`. Ideally, we would excite exactly the fundamental mode of the waveguide, but it is good enough to just excite it with a line source. Moreover, since we are interested in the $P$ polarization (electric field in the plane), we will excite it with a $J_y$ current source (transverse to the propagation direction), which is specified as $E_y$: # In[8]: src = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(-0.5 * sx + dpml), size=mp.Vector3(0, w)) ] # The structure has mirror symmetry planes through the $X$ and $Y$ axes. The source breaks the mirror symmetry through the $Y$ axis, but we still have odd mirror symmetry through the $Z$ axis: # In[9]: sym = [mp.Mirror(mp.Y, phase=-1)] # Note that we specify the plane by its normal, the $Y$ direction. See also Exploiting Symmetry. Putting all these objects together via the `Simulation` object: # In[10]: sim = mp.Simulation(cell_size=cell, geometry=geometry, boundary_layers=pml_layers, sources=src, symmetries=sym, resolution=resolution) # We need to compute the flux spectrum at the other end of the computational cell, after the holes but before the PML: # In[11]:
def main(args): resolution = 20 # pixels/unit length (1 um) h = args.hh w = args.w a = args.a d = args.d N = args.N N = N + 1 nSi = 3.45 Si = mp.Medium(index=nSi) nSiO2 = 1.45 SiO2 = mp.Medium(index=nSiO2) sxy = 16 sz = 4 cell_size = mp.Vector3(sxy, sxy, sz) geometry = [] # rings of Bragg grating for n in range(N,0,-1): geometry.append(mp.Cylinder(material=Si, center=mp.Vector3(0,0,0), radius=n*a, height=h)) geometry.append(mp.Cylinder(material=mp.air, center=mp.Vector3(0,0,0), radius=n*a-d, height=h)) # remove left half of Bragg grating rings to form semi circle geometry.append(mp.Block(material=mp.air, center=mp.Vector3(-0.5*N*a,0,0), size=mp.Vector3(N*a,2*N*a,h))) geometry.append(mp.Cylinder(material=Si, center=mp.Vector3(0,0,0), radius=a-d, height=h)) # angle sides of Bragg grating # rotation angle of sides relative to Y axis (degrees) rot_theta = -1*math.radians(args.rot_theta) pvec = mp.Vector3(0, 0.5*w, 0) cvec = mp.Vector3(-0.5*N*a, 0.5*N*a+0.5*w, 0) rvec = cvec-pvec rrvec = rvec.rotate(mp.Vector3(0,0,1), rot_theta) geometry.append(mp.Block(material=mp.air, center=pvec+rrvec, size=mp.Vector3(N*a,N*a,h), e1=mp.Vector3(0.9396926207859084,-0.3420201433256687,0), e2=mp.Vector3(0.3420201433256687,0.9396926207859084,0), e3=mp.Vector3(0,0,1))) pvec = mp.Vector3(0, -0.5*w, 0) cvec = mp.Vector3(-0.5*N*a, -1*(0.5*N*a+0.5*w), 0) rvec = cvec-pvec rrvec = rvec.rotate(mp.Vector3(0,0,1), -1*rot_theta) geometry.append(mp.Block(material=mp.air, center=pvec+rrvec, size=mp.Vector3(N*a,N*a,h), e1=mp.Vector3(0.9396926207859084,0.3420201433256687,0), e2=mp.Vector3(-0.3420201433256687,0.9396926207859084,0), e3=mp.Vector3(0,0,1))) # input waveguide geometry.append(mp.Block(material=mp.air, center=mp.Vector3(-0.25*sxy,0.5*w+0.5*a,0), size=mp.Vector3(0.5*sxy,a,h))) geometry.append(mp.Block(material=mp.air, center=mp.Vector3(-0.25*sxy,-1*(0.5*w+0.5*a),0), size=mp.Vector3(0.5*sxy,a,h))) geometry.append(mp.Block(material=Si, center=mp.Vector3(-0.25*sxy,0,0), size=mp.Vector3(0.5*sxy,w,h))) # substrate geometry.append(mp.Block(material=SiO2, center=mp.Vector3(0,0,-0.5*sz+0.25*(sz-h)), size=mp.Vector3(mp.inf,mp.inf,0.5*(sz-h)))) dpml = 1.0 boundary_layers = [ mp.PML(dpml) ] # mode frequency fcen = 1/1.55 sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=0.2*fcen), component=mp.Ey, size=mp.Vector3(0,sxy-2*dpml,sz-2*dpml), center=mp.Vector3(-0.5*sxy+dpml,0,0), eig_match_freq=True, eig_parity=mp.ODD_Y, eig_kpoint=mp.Vector3(1.5,0,0), eig_resolution=32) ] symmetries = [ mp.Mirror(mp.Y,-1) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=sources, dimensions=3, symmetries=symmetries) nearfield = sim.add_near2far(fcen, 0, 1, mp.Near2FarRegion(mp.Vector3(0,0,0.5*sz-dpml), size=mp.Vector3(sxy-2*dpml,sxy-2*dpml,0))) sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ey, mp.Vector3(), 1e-6)) r = 1000 * (1 / fcen) # 1000 wavelengths out from the source npts = 100 # number of points in [0,2*pi) range of angles for n in range(npts): ff = sim.get_farfield(nearfield, mp.Vector3(r * math.cos(math.pi * (n / npts)), 0, r * math.sin(math.pi * (n / npts)))) print("farfield: {}, {}, ".format(n, math.pi * n / npts), end='') print(", ".join([str(f).strip('()').replace('j', 'i') for f in ff]))
def simulate_metalens(metalens): # Setup the MEEP objects cell = mp.Vector3(metalens['sim_cell_width'], metalens['sim_cell_height']) # All around the simulation cell pml_layers = [mp.PML(metalens['pml_width'])] # Set up the sources sources = [ mp.Source(src=mp.ContinuousSource(wavelength=metalens['wavelength'], width=metalens['source_width']), component=mp.Ez, center=mp.Vector3(0, metalens['source_coordinate']), size=mp.Vector3(2 * metalens['ws'], 0), amp_func=out_of_plane_dip_amp_func_Ez(metalens)), mp.Source(src=mp.ContinuousSource(wavelength=metalens['wavelength'], width=metalens['source_width']), component=mp.Hx, center=mp.Vector3(0, metalens['source_coordinate']), size=mp.Vector3(2 * metalens['ws'], 0), amp_func=out_of_plane_dip_amp_func_Hx(metalens)) ] # Set up the symmetries syms = [] if metalens['x_mirror_symmetry']: syms.append(mp.Mirror(mp.X)) sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=metalens['geometry'], force_complex_fields=metalens['complex_fields'], symmetries=syms, sources=sources, resolution=metalens['resolution']) start_time = time.time() metalens['run_date'] = ( datetime.datetime.now().strftime("%b %d %Y at %H:%M:%S")) mp.quiet(metalens['quiet']) sim.init_sim() # Branch if saving for making an animation if metalens['save_output']: sim.run(mp.to_appended( "ez-{sim_id}".format(**metalens), mp.at_every(metalens['simulation_time'] / 1000., mp.output_efield_z)), until=metalens['simulation_time']) else: sim.run(until=metalens['simulation_time']) # Compute the clock run time and grab the fields metalens['array_metadata'] = sim.get_array_metadata() metalens['run_time_in_s'] = time.time() - start_time metalens['fields'] = { 'Ez': sim.get_array(component=mp.Ez).transpose(), 'Hx': sim.get_array(component=mp.Hx).transpose(), 'Hy': sim.get_array(component=mp.Hy).transpose() } metalens['eps'] = sim.get_epsilon().transpose() # Dump the result to disk if metalens['log_to_pkl'][0]: if metalens['log_to_pkl'][1] == '': pkl_fname = '%smetalens-%s.pkl' % (datadir, metalens['sim_id']) else: pkl_fname = metalens['log_to_pkl'][1] print(pkl_fname) pickle.dump(metalens, open(pkl_fname, 'wb')) return metalens
def test_transmission_spectrum(self): expected = [ (0.15, 7.218492264696595e-6), (0.1504008016032064, 6.445696315927592e-6), (0.1508016032064128, 5.140949243632777e-6), (0.15120240480961922, 3.6159747936427164e-6), (0.15160320641282563, 2.263940553705969e-6), (0.15200400801603203, 1.4757165844336744e-6), (0.15240480961923844, 1.5491803919142815e-6), (0.15280561122244485, 2.612053246626972e-6), (0.15320641282565126, 4.577504371188737e-6), (0.15360721442885766, 7.1459089162998185e-6), (0.15400801603206407, 9.856622013418823e-6), (0.15440881763527048, 1.2182309227954296e-5), (0.1548096192384769, 1.3647726444709649e-5), (0.1552104208416833, 1.3947420613633674e-5), (0.1556112224448897, 1.303466755716231e-5), (0.1560120240480961, 1.115807915037775e-5), (0.15641282565130252, 8.832335196969796e-6), (0.15681362725450892, 6.743645773127985e-6), (0.15721442885771533, 5.605913756087576e-6), (0.15761523046092174, 5.996668564026961e-6), (0.15801603206412815, 8.209400611614078e-6), (0.15841683366733456, 1.2158641936828497e-5), (0.15881763527054096, 1.73653230513453e-5), (0.15921843687374737, 2.303382576477893e-5), (0.15961923847695378, 2.821180350795834e-5), (0.1600200400801602, 3.200359292911769e-5), (0.1604208416833666, 3.3792624373001934e-5), (0.160821643286573, 3.342171394788991e-5), (0.1612224448897794, 3.1284866146526904e-5), (0.16162324649298582, 2.830022088581398e-5), (0.16202404809619222, 2.5758413657344014e-5), (0.16242484969939863, 2.506899997971769e-5), (0.16282565130260504, 2.7453508915303887e-5), (0.16322645290581145, 3.365089813497114e-5), (0.16362725450901786, 4.370486834112e-5), (0.16402805611222426, 5.689050715055283e-5), (0.16442885771543067, 7.181133157470506e-5), (0.16482965931863708, 8.666168027415369e-5), (0.16523046092184349, 9.961094123261317e-5), (0.1656312625250499, 1.0923388232657953e-4), (0.1660320641282563, 1.1489334204708105e-4), (0.1664328657314627, 1.1698318060032011e-4), (0.16683366733466912, 1.169621456132733e-4), (0.16723446893787552, 1.1714995241571987e-4), (0.16763527054108193, 1.2030783847222252e-4), (0.16803607214428834, 1.2907652919660887e-4), ] self.sim.sources = [ mp.Source(mp.GaussianSource(self.fcen, fwidth=self.df), mp.Ey, mp.Vector3(self.dpml + (-0.5 * self.sx)), size=mp.Vector3(0, self.w)) ] self.sim.symmetries = [mp.Mirror(mp.Y, phase=-1)] freg = mp.FluxRegion(center=mp.Vector3((0.5 * self.sx) - self.dpml - 0.5), size=mp.Vector3(0, 2 * self.w)) trans = self.sim.add_flux(self.fcen, self.df, self.nfreq, freg, decimation_factor=1) self.sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ey, mp.Vector3((0.5 * self.sx) - self.dpml - 0.5, 0), 1e-1)) res = zip(mp.get_flux_freqs(trans), mp.get_fluxes(trans)) tol = 1e-8 if mp.is_single_precision() else 1e-10 for e, r in zip(expected, res): self.assertClose(e, r, epsilon=tol)
def test_array_metadata(self): resolution = 25 n = 3.4 w = 1 r = 1 pad = 4 dpml = 2 sxy = 2 * (r + w + pad + dpml) cell_size = mp.Vector3(sxy, sxy) nonpml_vol = mp.Volume(mp.Vector3(), size=mp.Vector3(sxy - 2 * dpml, sxy - 2 * dpml)) geometry = [ mp.Cylinder(radius=r + w, material=mp.Medium(index=n)), mp.Cylinder(radius=r) ] fcen = 0.118 df = 0.08 symmetries = [mp.Mirror(mp.X, phase=-1), mp.Mirror(mp.Y, phase=+1)] pml_layers = [mp.PML(dpml)] # CW source src = [ mp.Source(mp.ContinuousSource(fcen, fwidth=df), mp.Ez, mp.Vector3(r + 0.1)), mp.Source(mp.ContinuousSource(fcen, fwidth=df), mp.Ez, mp.Vector3(-(r + 0.1)), amplitude=-1) ] sim = mp.Simulation(cell_size=cell_size, geometry=geometry, sources=src, resolution=resolution, force_complex_fields=True, symmetries=symmetries, boundary_layers=pml_layers) sim.init_sim() sim.solve_cw(1e-5 if mp.is_single_precision() else 1e-6, 1000, 10) def electric_energy(r, ez, eps): return np.real(eps * np.conj(ez) * ez) def vec_func(r): return r.x**2 + 2 * r.y**2 electric_energy_total = sim.integrate_field_function( [mp.Ez, mp.Dielectric], electric_energy, nonpml_vol) electric_energy_max = sim.max_abs_field_function( [mp.Ez, mp.Dielectric], electric_energy, nonpml_vol) vec_func_total = sim.integrate_field_function([], vec_func, nonpml_vol) cw_modal_volume = (electric_energy_total / electric_energy_max) * vec_func_total sim.reset_meep() # pulsed source src = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ez, mp.Vector3(r + 0.1)), mp.Source(mp.GaussianSource(fcen, fwidth=df), mp.Ez, mp.Vector3(-(r + 0.1)), amplitude=-1) ] sim = mp.Simulation(cell_size=cell_size, geometry=geometry, k_point=mp.Vector3(), sources=src, resolution=resolution, symmetries=symmetries, boundary_layers=pml_layers) dft_obj = sim.add_dft_fields([mp.Ez], fcen, 0, 1, where=nonpml_vol) sim.run(until_after_sources=100) Ez = sim.get_dft_array(dft_obj, mp.Ez, 0) (X, Y, Z, W) = sim.get_array_metadata(dft_cell=dft_obj) Eps = sim.get_array(vol=nonpml_vol, component=mp.Dielectric) EpsE2 = np.real(Eps * np.conj(Ez) * Ez) xm, ym = np.meshgrid(X, Y) vec_func_sum = np.sum(W * (xm**2 + 2 * ym**2)) pulse_modal_volume = np.sum(W * EpsE2) / np.max(EpsE2) * vec_func_sum tol = 5e-2 if mp.is_single_precision() else 1e-2 self.assertClose(cw_modal_volume / pulse_modal_volume, 1.0, epsilon=tol)
] 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)) ] 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)
def main(args): resolution = args.res w1 = 1 # width of waveguide 1 w2 = args.w2 # width of waveguide 2 Lw = 10 # length of waveguide 1 and 2 Lt = args.Lt # taper length Si = mp.Medium(epsilon=12.0) dair = 3.0 dpml = 3.0 sx = dpml + Lw + Lt + Lw + dpml sy = dpml + dair + w2 + dair + dpml cell_size = mp.Vector3(sx, sy, 0) geometry = [ mp.Block(material=Si, center=mp.Vector3(0, 0, 0), size=mp.Vector3(mp.inf, w1, mp.inf)), mp.Block(material=Si, center=mp.Vector3(0.5 * sx - 0.5 * (Lt + Lw + dpml), 0, 0), size=mp.Vector3(Lt + Lw + dpml, w2, mp.inf)) ] # form linear taper hh = w2 ww = 2 * Lt # taper angle (CCW, relative to +X axis) rot_theta = math.atan(0.5 * (w2 - w1) / Lt) pvec = mp.Vector3(-0.5 * sx + dpml + Lw, 0.5 * w1, 0) cvec = mp.Vector3(-0.5 * sx + dpml + Lw + 0.5 * ww, 0.5 * hh + 0.5 * w1, 0) rvec = cvec - pvec rrvec = rvec.rotate(mp.Vector3(0, 0, 1), rot_theta) geometry.append( mp.Block(material=mp.air, center=pvec + rrvec, size=mp.Vector3(ww, hh, mp.inf), e1=mp.Vector3(1, 0, 0).rotate(mp.Vector3(0, 0, 1), rot_theta), e2=mp.Vector3(0, 1, 0).rotate(mp.Vector3(0, 0, 1), rot_theta), e3=mp.Vector3(0, 0, 1))) pvec = mp.Vector3(-0.5 * sx + dpml + Lw, -0.5 * w1, 0) cvec = mp.Vector3(-0.5 * sx + dpml + Lw + 0.5 * ww, -(0.5 * hh + 0.5 * w1), 0) rvec = cvec - pvec rrvec = rvec.rotate(mp.Vector3(0, 0, 1), -rot_theta) geometry.append( mp.Block(material=mp.air, center=pvec + rrvec, size=mp.Vector3(ww, hh, mp.inf), e1=mp.Vector3(1, 0, 0).rotate(mp.Vector3(0, 0, 1), -rot_theta), e2=mp.Vector3(0, 1, 0).rotate(mp.Vector3(0, 0, 1), -rot_theta), e3=mp.Vector3(0, 0, 1))) boundary_layers = [mp.PML(dpml)] # mode frequency fcen = 0.15 sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=0.5 * fcen), component=mp.Ez, size=mp.Vector3(0, sy - 2 * dpml, 0), center=mp.Vector3(-0.5 * sx + dpml, 0, 0), eig_match_freq=True, eig_parity=mp.ODD_Z, eig_kpoint=mp.Vector3(0.4, 0, 0), eig_resolution=32) ] symmetries = [mp.Mirror(mp.Y, +1)] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=sources, symmetries=symmetries) xm = -0.5 * sx + dpml + 0.5 * Lw # x-coordinate of monitor mflux = sim.add_eigenmode( fcen, 0, 1, mp.FluxRegion(center=mp.Vector3(xm, 0), size=mp.Vector3(0, sy - 2 * dpml))) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(xm, 0), 1e-10)) bands = [1] # indices of modes for which to compute expansion coefficients alpha = sim.get_eigenmode_coefficients(mflux, bands) alpha0Plus = alpha[2 * 0 + 0] # coefficient of forward-traveling fundamental mode alpha0Minus = alpha[ 2 * 0 + 1] # coefficient of backward-traveling fundamental mode print("refl:, {}, {:.8f}".format(Lt, abs(alpha0Minus)**2))
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, "μm") print("slab size =", w, "μm") print("center frequency =", fcen, "1/μm") print("slit_flux_output_region y coordinate =", sfor_y, "μm") print("slab center y coordinate =", slab_center_y, "μm") 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 parallel_waveguide(s, xodd): geometry = [ mp.Block(center=mp.Vector3(-0.5 * (s + a)), size=mp.Vector3(a, a, mp.inf), material=Si), mp.Block(center=mp.Vector3(0.5 * (s + a)), size=mp.Vector3(a, a, mp.inf), material=Si) ] symmetries = [ mp.Mirror(mp.X, phase=-1.0 if xodd else 1.0), mp.Mirror(mp.Y, phase=-1.0) ] sources = [ mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(-0.5 * (s + a)), size=mp.Vector3(a, a)), mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ey, center=mp.Vector3(0.5 * (s + a)), size=mp.Vector3(a, a), amplitude=-1.0 if xodd else 1.0) ] sim = mp.Simulation(resolution=resolution, cell_size=cell, boundary_layers=pml_layers, geometry=geometry, symmetries=symmetries, k_point=k_point, sources=sources) h = mp.Harminv(mp.Ey, mp.Vector3(0.5 * (s + a)), fcen, df) sim.run(mp.after_sources(h), until_after_sources=200) f = h.modes[0].freq print("freq:, {}, {}".format(s, f)) sim.reset_meep() eig_sources = [ mp.EigenModeSource(src=mp.GaussianSource(f, fwidth=df), size=mp.Vector3(a, a), center=mp.Vector3(-0.5 * (s + a)), direction=mp.Z, eig_kpoint=k_point, eig_match_freq=True, eig_parity=mp.ODD_Y), mp.EigenModeSource(src=mp.GaussianSource(f, fwidth=df), size=mp.Vector3(a, a), center=mp.Vector3(0.5 * (s + a)), direction=mp.Z, eig_kpoint=k_point, eig_match_freq=True, eig_parity=mp.ODD_Y, amplitude=-1.0 if xodd else 1.0) ] sim.change_sources(eig_sources) flux_reg = mp.FluxRegion(direction=mp.Z, center=mp.Vector3(), size=mp.Vector3(1.2 * (2 * a + s), 1.2 * a)) wvg_flux = sim.add_flux(f, 0, 1, flux_reg) force_reg1 = mp.ForceRegion(mp.Vector3(0.5 * s), direction=mp.X, weight=1.0, size=mp.Vector3(y=a)) force_reg2 = mp.ForceRegion(mp.Vector3(0.5 * s + a), direction=mp.X, weight=-1.0, size=mp.Vector3(y=a)) wvg_force = sim.add_force(f, 0, 1, force_reg1, force_reg2) sim.run(until_after_sources=500) flux = mp.get_fluxes(wvg_flux)[0] force = mp.get_forces(wvg_force)[0] sim.reset_meep() return flux, force