def add_eigenmode_source(self, src, port, eig_band=1, eig_parity=mp.NO_PARITY, z=None, height=None): """ Adds an eigenmode source at the given port. :param src: Meep-Source (e.g. GaussianSource or ContinuousSource) :param port: Port at which the source is added :param eig_band: Number of the excited mode. The mode with the highest eigenvalue has the number 1 :param eig_parity: Parity of the eigenmodes :param z: Z-position of the source :param height: Height of the area for calculating the eigenmode :return: Added Meep-Source """ size = [ 0, port.total_width * 2 ] if port.angle % np.pi < np.pi / 4 else [port.total_width * 2, 0] source = mp.EigenModeSource(src, mp.Vector3(*port.origin, z), size=mp.Vector3(*size, height), eig_band=eig_band, eig_parity=eig_parity) self.sources.append(source) return source
def setUp(self): cell = mp.Vector3(16, 8) geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 1, mp.inf), material=mp.Medium(epsilon=12)), mp.Block(center=mp.Vector3(y=0.3), size=mp.Vector3(mp.inf, 0.1, mp.inf), material=mp.Medium()) ] sources = [ mp.EigenModeSource(src=mp.ContinuousSource(0.15), size=mp.Vector3(y=6), center=mp.Vector3(x=-5), component=mp.Dielectric, eig_parity=mp.ODD_Z) ] pml_layers = [mp.PML(1.0)] self.sim = mp.Simulation(cell_size=cell, geometry=geometry, sources=sources, boundary_layers=pml_layers, force_complex_fields=True, resolution=10)
def create_waveguide_source(self, wavelength, pos, width, direction='+x', comp='y'): # parity parity = mp.ODD_Z if comp == 'z' else mp.EVEN_Z # size size = mp.Vector3(y=width) if 'x' in direction else mp.Vector3(x=width) # kpoints angles = {'+x': 0, '+y': 90, '-x': 180, '-y': 270} if direction in angles: angle = np.radians(angles[direction]) direction = mp.Vector3(0.4).rotate(mp.Vector3(z=1), angle) s = mp.EigenModeSource(src=mp.ContinuousSource(wavelength=wavelength), center=mp.Vector3(*pos), size=size, direction=mp.NO_DIRECTION, eig_kpoint=direction, eig_band=1, eig_parity=parity, eig_match_freq=True, component=mp.ALL_COMPONENTS) self.sources.append(s)
def create_sim(self, beta_vector, vacuum=False): args=self.args sx=self.cell_size.x wvg=mp.Block(center=origin, material=mp.Medium(epsilon=args.eps_wvg), size=mp.Vector3(self.cell_size.x,args.w_wvg)) disc=mp.Cylinder(center=self.design_center, radius=args.r_disc, epsilon_func=ParameterizedDielectric(self.design_center, self.basis, beta_vector)) geometry=[wvg] if vacuum else [wvg, disc] envelope = mp.GaussianSource(args.fcen,fwidth=args.df) amp=1.0 if callable(getattr(envelope, "fourier_transform", None)): amp /= envelope.fourier_transform(args.fcen) sources=[mp.EigenModeSource(src=envelope, center=self.source_center, size=self.source_size, eig_band=self.args.source_mode, amplitude=amp ) ] sim=mp.Simulation(resolution=args.res, cell_size=self.cell_size, boundary_layers=[mp.PML(args.dpml)], geometry=geometry, sources=sources) if args.complex_fields: sim.force_complex_fields=True return sim
def test_eigsrc_kz(self, kz_2d): resolution = 30 # pixels/um cell_size = mp.Vector3(14, 14) pml_layers = [mp.PML(thickness=2)] geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 1, mp.inf), material=mp.Medium(epsilon=12)) ] fsrc = 0.3 # frequency of eigenmode or constant-amplitude source bnum = 1 # band number of eigenmode kz = 0.2 # fixed out-of-plane wavevector component sources = [ mp.EigenModeSource(src=mp.GaussianSource(fsrc, fwidth=0.2 * fsrc), center=mp.Vector3(), size=mp.Vector3(y=14), eig_band=bnum, eig_parity=mp.EVEN_Y, eig_match_freq=True) ] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, boundary_layers=pml_layers, sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.Y)], k_point=mp.Vector3(z=kz), kz_2d=kz_2d) 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, 2], eig_parity=mp.EVEN_Y) total_flux = mp.get_fluxes(tran)[0] mode1_flux = abs(res.alpha[0, 0, 0])**2 mode2_flux = abs(res.alpha[1, 0, 0])**2 mode1_frac = 0.99 self.assertGreater(mode1_flux / total_flux, mode1_frac) self.assertLess(mode2_flux / total_flux, 1 - mode1_frac) d = 3.5 ez1 = sim.get_field_point(mp.Ez, mp.Vector3(2.3, -5.7, 4.8)) ez2 = sim.get_field_point(mp.Ez, mp.Vector3(2.3, -5.7, 4.8 + d)) ratio_ez = ez2 / ez1 phase_diff = cmath.exp(1j * 2 * cmath.pi * kz * d) self.assertAlmostEqual(ratio_ez.real, phase_diff.real, places=10) self.assertAlmostEqual(ratio_ez.imag, phase_diff.imag, places=10)
def test_waveguide_flux(self): cell_size = mp.Vector3(10, 10, 0) pml_layers = [mp.PML(thickness=2.0)] rot_angles = range( 0, 60, 20) # rotation angle of waveguide, CCW around z-axis fluxes = [] for t in rot_angles: rot_angle = math.radians(t) sources = [ mp.EigenModeSource(src=mp.GaussianSource(1.0, fwidth=0.1), size=mp.Vector3(y=10), center=mp.Vector3(x=-3), direction=mp.NO_DIRECTION, eig_kpoint=mp.Vector3( math.cos(rot_angle), math.sin(rot_angle), 0), eig_band=1, eig_parity=mp.ODD_Z, eig_match_freq=True) ] geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf, 1, mp.inf), e1=mp.Vector3(1, 0, 0).rotate(mp.Vector3(0, 0, 1), rot_angle), e2=mp.Vector3(0, 1, 0).rotate(mp.Vector3(0, 0, 1), rot_angle), material=mp.Medium(index=1.5)) ] sim = mp.Simulation(cell_size=cell_size, resolution=50, boundary_layers=pml_layers, sources=sources, geometry=geometry) tran = sim.add_flux( 1.0, 0, 1, mp.FluxRegion(center=mp.Vector3(x=3), size=mp.Vector3(y=10))) sim.run(until_after_sources=100) fluxes.append(mp.get_fluxes(tran)[0]) print("flux:, {:.2f}, {:.6f}".format(t, fluxes[-1])) self.assertAlmostEqual(fluxes[0], fluxes[1], places=0) self.assertAlmostEqual(fluxes[1], fluxes[2], places=0) # self.assertAlmostEqual(fluxes[0], fluxes[2], places=0) # sadly the above line requires a workaround due to the # following annoying numerical accident: # AssertionError: 100.33815231783535 != 99.81145343586365 within 0 places f0, f2 = fluxes[0], fluxes[2] self.assertLess(abs(f0 - f2), 0.5 * max(abs(f0), abs(f2)))
def place_adjoint_source(self,dJ,dt): '''Places an equivalent eigenmode monitor facing the opposite direction. Calculates the correct scaling/time profile. dJ ........ the user needs to pass the dJ/dMonitor evaluation dt ........ the timestep size from sim.fields.dt of the forward sim ''' dJ = np.atleast_1d(dJ) # determine starting kpoint for reverse mode eigenmode source direction_scalar = 1 if self.forward else -1 if self.kpoint_func is None: if self.normal_direction == 0: k0 = direction_scalar * mp.Vector3(x=1) elif self.normal_direction == 1: k0 = direction_scalar * mp.Vector3(y=1) elif self.normal_direction == 2: k0 == direction_scalar * mp.Vector3(z=1) else: k0 = direction_scalar * self.kpoint_func(self.time_src.frequency,1) # -------------------------------------- # # Get scaling factor # -------------------------------------- # # leverage linearity and combine source for multiple frequencies if dJ.ndim == 2: dJ = np.sum(dJ,axis=1) # Determine the correct resolution scale factor if self.sim.cell_size.y == 0: dV = 1/self.sim.resolution elif self.sim.cell_size.z == 0: dV = 1/self.sim.resolution * 1/self.sim.resolution else: dV = 1/self.sim.resolution * 1/self.sim.resolution * 1/self.sim.resolution da_dE = 0.5*(dV * self.cscale) scale = da_dE * dJ * 1j * 2 * np.pi * self.frequencies / np.array([self.time_src.fourier_transform(f) for f in self.frequencies]) # final scale factor if self.frequencies.size == 1: # Single frequency simulations. We need to drive it with a time profile. src = self.time_src amp = scale else: # TODO: In theory we should be able drive the source without normalizing out the time profile. # But for some reason, there is a frequency dependent scaling discrepency. It works now for # multiple monitors and multiple sources, but we should figure out why this is. src = FilteredSource(self.time_src.frequency,self.frequencies,scale,dt,self.time_src) # generate source from broadband response amp = 1 # generate source object self.source = mp.EigenModeSource(src, eig_band=self.mode, direction=mp.NO_DIRECTION, eig_kpoint=k0, amplitude=amp, eig_match_freq=True, size=self.volume.size, center=self.volume.center, **self.EigenMode_kwargs) return self.source
def place_adjoint_source(self, dJ): '''Places an equivalent eigenmode monitor facing the opposite direction. Calculates the correct scaling/time profile. dJ ........ the user needs to pass the dJ/dMonitor evaluation ''' dJ = np.atleast_1d(dJ) dt = self.sim.fields.dt # the timestep size from sim.fields.dt of the forward sim # determine starting kpoint for reverse mode eigenmode source direction_scalar = 1 if self.forward else -1 if self.kpoint_func is None: if self.normal_direction == 0: k0 = direction_scalar * mp.Vector3(x=1) elif self.normal_direction == 1: k0 = direction_scalar * mp.Vector3(y=1) elif self.normal_direction == 2: k0 == direction_scalar * mp.Vector3(z=1) else: k0 = direction_scalar * self.kpoint_func(self.time_src.frequency, 1) if dJ.ndim == 2: dJ = np.sum(dJ, axis=1) da_dE = 0.5 * self.cscale # scalar popping out of derivative scale = adj_src_scale(self, dt) if self.frequencies.size == 1: # Single frequency simulations. We need to drive it with a time profile. amp = da_dE * dJ * scale # final scale factor src = self.time_src else: # multi frequency simulations scale = da_dE * dJ * scale src = FilteredSource(self.time_src.frequency, self.frequencies, scale, dt) # generate source from broadband response amp = 1 # generate source object self.source = [ mp.EigenModeSource(src, eig_band=self.mode, direction=mp.NO_DIRECTION, eig_kpoint=k0, amplitude=amp, eig_match_freq=True, size=self.volume.size, center=self.volume.center, **self.EigenMode_kwargs) ] return self.source
def test_custom_em_source(self): resolution = 20 dpml = 2 pml_layers = [mp.PML(thickness=dpml)] sx = 40 sy = 12 cell_size = mp.Vector3(sx + 2 * dpml, sy) v0 = 0.15 # pulse center frequency a = 0.2 * v0 # Gaussian envelope half-width b = -0.1 # linear chirp rate (positive: up-chirp, negative: down-chirp) t0 = 15 # peak time chirp = lambda t: np.exp(1j * 2 * np.pi * v0 * (t - t0)) * np.exp(-a * (t - t0)**2 + 1j * b * (t - t0)**2) geometry = [ mp.Block(center=mp.Vector3(0, 0, 0), size=mp.Vector3(mp.inf, 1, mp.inf), material=mp.Medium(epsilon=12)) ] kx = 0.4 # initial guess for wavevector in x-direction of eigenmode kpoint = mp.Vector3(kx) bnum = 1 sources = [ mp.EigenModeSource(src=mp.CustomSource(src_func=chirp, center_frequency=v0), center=mp.Vector3(-0.5 * sx + dpml + 1), size=mp.Vector3(y=sy), eig_kpoint=kpoint, eig_band=bnum, eig_parity=mp.EVEN_Y + mp.ODD_Z, eig_match_freq=True) ] sim = mp.Simulation(cell_size=cell_size, boundary_layers=pml_layers, resolution=resolution, k_point=mp.Vector3(), sources=sources, geometry=geometry, symmetries=[mp.Mirror(mp.Y)]) t = np.linspace(0, 50, 1000) sim.run(until=t0 + 50)
def create_sim(self, beta_vector, vacuum=False): args = self.args sx = self.cell_size.x x_in = -0.5 * (args.l_design + args.l_stub) x_out = +0.5 * (args.l_design + args.l_stub) y_out1 = +0.25 * args.h_design y_out2 = -0.25 * args.h_design wvg_in = mp.Block(center=mp.Vector3(x_in, 0.0), size=mp.Vector3(args.l_stub, args.w_in), material=mp.Medium(epsilon=args.eps_in)) wvg_out1 = mp.Block(center=mp.Vector3(x_out, y_out1), size=mp.Vector3(args.l_stub, args.w_out1), material=mp.Medium(epsilon=args.eps_out1)) wvg_out2 = mp.Block(center=mp.Vector3(x_out, y_out2), size=mp.Vector3(args.l_stub, args.w_out2), material=mp.Medium(epsilon=args.eps_out2)) design = mp.Block(center=origin, size=mp.Vector3(args.l_design, args.h_design), epsilon_func=ParameterizedDielectric( self.design_center, self.basis, beta_vector)) geometry = [wvg_in, wvg_out1, wvg_out2, design] envelope = mp.GaussianSource(args.fcen, fwidth=args.df) amp = 1.0 if callable(getattr(envelope, "fourier_transform", None)): amp /= envelope.fourier_transform(args.fcen) sources = [ mp.EigenModeSource(src=envelope, center=self.source_center, size=self.source_size, eig_band=self.args.source_mode, amplitude=amp) ] sim = mp.Simulation(resolution=args.res, cell_size=self.cell_size, boundary_layers=[mp.PML(args.dpml)], geometry=geometry, sources=sources) if args.complex_fields: sim.force_complex_fields = True return sim
def test_dft_energy(self): resolution = 20 cell = mp.Vector3(10, 5) geom = [ mp.Block(size=mp.Vector3(mp.inf, 1, mp.inf), material=mp.Medium(epsilon=12)) ] pml = [mp.PML(1)] fsrc = 0.15 sources = [ mp.EigenModeSource(src=mp.GaussianSource(frequency=fsrc, fwidth=0.2 * fsrc), center=mp.Vector3(-3), size=mp.Vector3(y=5), eig_band=1, eig_parity=mp.ODD_Z + mp.EVEN_Y, eig_match_freq=True) ] sim = mp.Simulation(resolution=resolution, cell_size=cell, geometry=geom, boundary_layers=pml, sources=sources, symmetries=[mp.Mirror(direction=mp.Y)]) flux = sim.add_flux( fsrc, 0, 1, mp.FluxRegion(center=mp.Vector3(3), size=mp.Vector3(y=5))) energy = sim.add_energy( fsrc, 0, 1, mp.EnergyRegion(center=mp.Vector3(3), size=mp.Vector3(y=5))) sim.run(until_after_sources=100) res = sim.get_eigenmode_coefficients(flux, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) mode_vg = res.vgrp[0] poynting_flux = mp.get_fluxes(flux)[0] e_energy = mp.get_electric_energy(energy)[0] ratio_vg = (0.5 * poynting_flux) / e_energy m_energy = mp.get_magnetic_energy(energy)[0] t_energy = mp.get_total_energy(energy)[0] self.assertAlmostEqual(m_energy + e_energy, t_energy) self.assertAlmostEqual(ratio_vg, mode_vg, places=3)
def place_adjoint_source(self, dJ): dJ = np.atleast_1d(dJ) if dJ.ndim == 2: dJ = np.sum(dJ, axis=1) time_src = self._create_time_profile() da_dE = 0.5 * self._cscale scale = self._adj_src_scale() if self.kpoint_func: eig_kpoint = -1 * self.kpoint_func(time_src.frequency, self.mode) else: center_frequency = 0.5 * (np.min(self.frequencies) + np.max(self.frequencies)) direction = mp.Vector3( *(np.eye(3)[self._monitor.normal_direction] * np.abs(center_frequency))) eig_kpoint = -1 * direction if self.forward else direction if self._frequencies.size == 1: amp = da_dE * dJ * scale src = time_src else: scale = da_dE * dJ * scale src = FilteredSource( time_src.frequency, self._frequencies, scale, self.sim.fields.dt, ) amp = 1 source = mp.EigenModeSource( src, eig_band=self.mode, direction=mp.NO_DIRECTION, eig_kpoint=eig_kpoint, amplitude=amp, eig_match_freq=True, size=self.volume.size, center=self.volume.center, **self.eigenmode_kwargs, ) return [source]
def bragg_source(geo=None, **kwargs): geo = kwargs_to_geo(geo, **kwargs) # sources = [] # sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), # component=mp.Ey, # center=mp.Vector3(-monitor_x(geo)-.5, 0), # size=mp.Vector3(0, geo.sm_width, geo.thickness))] sources = [ mp.EigenModeSource( mp.GaussianSource(fcen, fwidth=df), eig_band=2, direction=mp.X, # eig_parity=mp.ODD_Y, component=mp.Ey, center=mp.Vector3(-monitor_x(geo) - .5, 0), size=mp.Vector3(0, bragg_cell(geo).y, bragg_cell(geo).z)) ] return sources
def test_waveguide_flux(self): cell_size = mp.Vector3(10,10,0) pml_layers = [mp.PML(thickness=2.0)] rot_angles = range(0,60,20) # rotation angle of waveguide, CCW around z-axis fluxes = [] for t in rot_angles: rot_angle = math.radians(t) sources = [mp.EigenModeSource(src=mp.GaussianSource(1.0,fwidth=0.1), size=mp.Vector3(y=10), center=mp.Vector3(x=-3), direction=mp.NO_DIRECTION, eig_kpoint=mp.Vector3(math.cos(rot_angle),math.sin(rot_angle),0), eig_band=1, eig_parity=mp.ODD_Z, eig_match_freq=True)] geometry = [mp.Block(center=mp.Vector3(), size=mp.Vector3(mp.inf,1,mp.inf), e1 = mp.Vector3(1,0,0).rotate(mp.Vector3(0,0,1), rot_angle), e2 = mp.Vector3(0,1,0).rotate(mp.Vector3(0,0,1), rot_angle), material=mp.Medium(index=1.5))] sim = mp.Simulation(cell_size=cell_size, resolution=50, boundary_layers=pml_layers, sources=sources, geometry=geometry) tran = sim.add_flux(1.0, 0, 1, mp.FluxRegion(center=mp.Vector3(x=3), size=mp.Vector3(y=10))) sim.run(until_after_sources=100) fluxes.append(mp.get_fluxes(tran)[0]) print("flux:, {:.2f}, {:.6f}".format(t,fluxes[-1])) self.assertAlmostEqual(fluxes[0], fluxes[1], places=0) self.assertAlmostEqual(fluxes[1], fluxes[2], places=0) self.assertAlmostEqual(fluxes[0], fluxes[2], places=0)
def run_sim(rot_angle=0): resolution = 50 # pixels/μm cell_size = mp.Vector3(14, 10, 0) pml_layers = [mp.PML(thickness=2, direction=mp.X)] fsrc = 1.0 # frequency of planewave (wavelength = 1/fsrc) n = 1.5 # refractive index of homogeneous material default_material = mp.Medium(index=n) k_point = mp.Vector3(fsrc * n).rotate(mp.Vector3(z=1), rot_angle) sources = [ mp.EigenModeSource( src=mp.ContinuousSource(fsrc), center=mp.Vector3(), size=mp.Vector3(y=10), direction=mp.AUTOMATIC if rot_angle == 0 else mp.NO_DIRECTION, eig_kpoint=k_point, eig_band=1, eig_parity=mp.EVEN_Y + mp.ODD_Z if rot_angle == 0 else mp.ODD_Z, eig_match_freq=True) ] sim = mp.Simulation(cell_size=cell_size, resolution=resolution, boundary_layers=pml_layers, sources=sources, k_point=k_point, default_material=default_material, symmetries=[mp.Mirror(mp.Y)] if rot_angle == 0 else []) sim.run(until=100) plt.figure(dpi=100) sim.plot2D(fields=mp.Ez) plt.show()
def create_sim(self, beta_vector, vacuum=False): args = self.args hwvg = mp.Block(center=origin, material=mp.Medium(epsilon=args.eps), size=mp.Vector3(self.cell_size.x, args.wh)) vwvg = mp.Block(center=origin, material=mp.Medium(epsilon=args.eps), size=mp.Vector3(args.wv, self.cell_size.y)) router = mp.Block( center=self.design_center, size=self.design_size, epsilon_func=self.basis.parameterized_function(beta_vector)) geometry = [hwvg, vwvg, router] envelope = mp.GaussianSource(args.fcen, fwidth=args.df) amp = 1.0 if callable(getattr(envelope, "fourier_transform", None)): amp /= envelope.fourier_transform(args.fcen) sources = [ mp.EigenModeSource(src=envelope, center=self.source_center, size=self.source_size, eig_band=args.source_mode, amplitude=amp) ] sim = mp.Simulation(resolution=args.res, cell_size=self.cell_size, boundary_layers=[mp.PML(self.dpml)], geometry=geometry, sources=sources) if args.complex_fields: sim.force_complex_fields = True return sim
def __init__(self, cell_size=None, background_geometry=[], foreground_geometry=[], sources=None, source_region=[], objective_regions=[], basis=None, design_region=None, extra_regions=[], objective_function=None, extra_quantities=[]): """ Parameters: ----------- cell_size: array-like background_geometry: list of meep.GeometricObject foreground_geometry: list of meep.GeometricObject Size of computational cell and lists of GeometricObjects that {precede,follow} the design object in the overall geometry. sources: list of meep.Source source_region: Subregion (*either* `sources` **or** `source_region` should be non-None) Specification of forward source distribution, i.e. the source excitation(s) that produce the fields from which the objective function is computed. In general, sources will be an arbitrary caller-created list of Source objects, in which case source_region, source_component are ignored. As an alternative convenience convention, the caller may omit sources and instead specify source_region; in this case, a source distribution over the given region is automatically created based on the values of the module-wide configuration options fcen, df, source_component, source_mode. objective regions: list of Subregion subregions of the computational cell over which frequency-domain fields are tabulated and used to compute objective quantities basis: (Basis) design_region: (Subregion) (*either* `basis` **or** `design_region` should be non-None) Specification of function space for the design permittivity. In general, basis will be a caller-created instance of some subclass of meep.adjoint.Basis. Then the spatial extent of the design region is determined by basis and the design_region argument is ignored. As an alternative convenience convention, the caller may omit basis and set design_region; in this case, an appropriate basis for the given design_region is automatically created based on the values of various module-wide adj_opt such as 'element_type' and 'element_length'. This convenience convention is only available for box-shaped (hyperrectangular) design regions. extra_regions: list of Subregion Optional list of additional subregions over which to tabulate frequency-domain fields. objective_function: str definition of the quantity to be maximized. This should be a mathematical expression in which the names of one or more objective quantities appear, and which should evaluate to a real number when numerical values are substituted for the names of all objective quantities. extra_quantities: list of str Optional list of additional objective quantities to be computed and reported together with the objective function. """ #----------------------------------------------------------------------- # process convenience arguments: # (a) if no basis was specified, create one using the given design # region plus global option values # (b) if no sources were specified, create one using the given source # region plus global option values #----------------------------------------------------------------------- self.basis = basis or FiniteElementBasis( region=design_region, element_length=adj_opt('element_length'), element_type=adj_opt('element_type')) design_region = self.basis.domain design_region.name = design_region.name or 'design' if not sources: f, df, m, c = [ adj_opt(s) for s in ['fcen', 'df', 'source_mode', 'source_component'] ] envelope = mp.GaussianSource(f, fwidth=df) kws = { 'center': V3(source_region.center), 'size': V3(source_region.size), 'src': envelope } #, 'eig_band': m, 'component': c } sources = [ mp.EigenModeSource(eig_band=m, **kws) if m > 0 else mp.Source(component=c, **kws) ] rescale_sources(sources) #----------------------------------------------------------------------- # initialize lower-level helper classes #----------------------------------------------------------------------- # DFTCells dft_cell_names = [] objective_cells = [DFTCell(r) for r in objective_regions] extra_cells = [DFTCell(r) for r in extra_regions] design_cell = DFTCell(design_region, E_CPTS) dft_cells = objective_cells + extra_cells + [design_cell] # ObjectiveFunction obj_func = ObjectiveFunction(fstr=objective_function, extra_quantities=extra_quantities) # initial values of (a) design variables, (b) the spatially-varying # permittivity function they define (the 'design function'), (c) the # GeometricObject describing a material body with this permittivity # (the 'design object'), and (d) mp.Simulation superposing the design # object with the rest of the caller's geometry. # Note that sources and DFT cells are not added to the Simulation at # this stage; this is done later by internal methods of TimeStepper # on a just-in-time basis before starting a timestepping run. self.beta_vector = self.basis.project(adj_opt('eps_design')) self.design_function = self.basis.parameterized_function( self.beta_vector) design_object = mp.Block(center=V3(design_region.center), size=V3(design_region.size), epsilon_func=self.design_function.func()) geometry = background_geometry + [design_object] + foreground_geometry sim = mp.Simulation(resolution=adj_opt('res'), cell_size=V3(cell_size), boundary_layers=[mp.PML(adj_opt('dpml'))], geometry=geometry) # TimeStepper self.stepper = TimeStepper(obj_func, dft_cells, self.basis, sim, sources) #----------------------------------------------------------------------- # if the 'filebase' configuration option wasn't specified, set it # to the base filename of the caller's script #----------------------------------------------------------------------- if not adj_opt('filebase'): script = inspect.stack()[1][0].f_code.co_filename or 'meep_adjoint' script_base = os.path.basename(script).split('.')[0] set_adjoint_options({'filebase': os.path.basename(script_base)}) if mp.am_master(): init_log(filename=adj_opt('logfile') or adj_opt('filebase') + '.log', usecs=True) self.dashboard_state = None
def main(args): resolution = args.res w1 = 1 # width of waveguide 1 w2 = 2 # 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 = 5.0 sx = dpml + Lw + Lt + Lw + dpml sy = dpml + dair + w2 + dair + dpml cell_size = mp.Vector3(sx, sy, 0) prism_x = sx + 1 half_w1 = 0.5 * w1 half_w2 = 0.5 * w2 half_Lt = 0.5 * Lt if Lt > 0: 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) ] else: 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) ] geometry = [mp.Prism(vertices, height=100, material=Si)] boundary_layers = [mp.PML(dpml)] # mode wavelength lcen = 6.67 # mode frequency fcen = 1 / lcen sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen), component=mp.Ez, size=mp.Vector3(0, sy - 2 * dpml, 0), center=mp.Vector3(-0.5 * sx + dpml + 0.2 * Lw, 0, 0), eig_match_freq=True, eig_parity=mp.ODD_Z + mp.EVEN_Y) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, sources=sources) xm = -0.5 * sx + dpml + 0.5 * Lw # x-coordinate of monitor mode_monitor = 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, 0), 1e-9)) coeffs = sim.get_eigenmode_coefficients(mode_monitor, [1]) print("mode:, {}, {:.8f}, {:.8f}".format(Lt, abs(coeffs[0, 0, 0])**2, abs(coeffs[0, 0, 1])**2))
south_wvg = mp.Block(center=mp.Vector3(-0.25 * sy * mpa.YHAT), material=Si, size=mp.Vector3(waveguide_width, 0.5 * sy, waveguide_h)) geometry = [wvg_horizontal, wvg_vertical] #---------------------------------------------------------------------- # Eigenmode source #---------------------------------------------------------------------- source_center = -d_source * mpa.XHAT source_size = 2.0 * waveguide_width * mpa.YHAT kpoint = 3 * mpa.XHAT sources = [ mp.EigenModeSource(mp.GaussianSource(frequency=fcen, fwidth=fwidth), eig_band=1, direction=mp.NO_DIRECTION, eig_kpoint=kpoint, size=source_size, center=source_center) ] #---------------------------------------------------------------------- #- design region and basis #---------------------------------------------------------------------- design_size = mp.Vector3(l_design, l_design, waveguide_h) design_region = mpa.Subregion(fcen, df, nfreq, name='design', center=mp.Vector3(), size=design_size) element_length = 1
# # Later objects get priority : fix final_geometry = [] # for fix in geometry: # final_geometry.append(fix) for fix in si_layer: final_geometry.append(fix) cell = mp.GDSII_vol(gdsII_file, CELL_LAYER, cell_zmin, cell_zmax) src_vol = mp.GDSII_vol(gdsII_file, SOURCE_LAYER, si_zmin, si_zmax) p1 = mp.GDSII_vol(gdsII_file, 20, si_zmin, si_zmax) p2 = mp.GDSII_vol(gdsII_file, 21, si_zmin, si_zmax) sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=df), size=src_vol.size, center=src_vol.center, eig_band=1, eig_parity=mp.EVEN_Y + mp.ODD_Z, eig_match_freq=True) ] # Display simulation object sim = mp.Simulation(resolution=res, default_material=oxide, eps_averaging=False, subpixel_maxeval=1, subpixel_tol=1, cell_size=cell.size, boundary_layers=[mp.PML(dpml)], sources=sources, geometry=final_geometry, geometry_center=mp.Vector3(ring_radius / 2,
def notch(w): print("#----------------------------------------") print("NOTCH WIDTH: %s nanometers" % (w * 1000)) print("#----------------------------------------") # w is the width of the notch in the waveguide angrad = ang * pi / 180 bottomoffset = e * h / tan(angrad) vertices = [ mp.Vector3(w / 2 + bottomoffset, (.5 + e) * h), mp.Vector3(-w / 2 - bottomoffset, (.5 + e) * h), mp.Vector3(-w / 2 + bottomoffset, (.5 - e) * h), mp.Vector3(w / 2 - bottomoffset, (.5 - e) * h) ] if bottomoffset > w / 2: ratio = (w / 2) / bottomoffset vertices = [ mp.Vector3(w / 2, h / 2), mp.Vector3(-w / 2, h / 2), mp.Vector3(0, (.5 - e * ratio) * h) ] print(vertices) #Waveguide Geometry cell = mp.Vector3(a, H) geometry = [ mp.Block(cell, center=mp.Vector3(0, 0), material=default_material), mp.Block(mp.Vector3(a, hu + h / 2), center=mp.Vector3(0, (hu + h / 2) / 2), material=upper_material), mp.Block(mp.Vector3(a, hl + h / 2), center=mp.Vector3(0, -(hl + h / 2) / 2), material=lower_material), mp.Block(mp.Vector3(a, h), center=mp.Vector3(0, 0), material=core_material) ] if w > 0: geometry.append(mp.Prism(vertices, height=1, material=upper_material)) pml_layers = [mp.Absorber(thickness=dpml)] r00 = None r01 = None r10 = None r11 = None t00 = None t01 = None t10 = None t11 = None su0 = None sd0 = None su1 = None sd1 = None modes = [0, 1] if only_fund: modes = [0] # eig_parity_fund = mp.EVEN_Z+mp.EVEN_Y; # eig_parity_first = mp.EVEN_Z+mp.ODD_Y; eig_parity_fund = mp.EVEN_Y eig_parity_first = mp.ODD_Y # for mode in [0, 1]: for mode in modes: if mode == 0: eig_parity = eig_parity_fund # Fundamental print("-----------") print("MODE TYPE: FUNDAMENTAL") else: eig_parity = eig_parity_first # First Order print("-----------") print("MODE TYPE: FIRST ORDER") sources = [ mp.EigenModeSource(mp.ContinuousSource(frequency=fcen), size=mp.Vector3(0, H), center=mp.Vector3(Ls, 0), eig_parity=eig_parity) ] sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution, force_complex_fields=True) ''' #-------------------------------------------------- #FOR DISPLAYING THE GEOMETRY sim.run(until = 200) eps_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric) plt.figure(dpi=100) plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary') #plt.axis('off') plt.show() quit() #---------------------------------------------------- ''' ''' #------------------------------------------------------ #FOR GENERATING THE ELECTRIC FIELD GIF #Note: After running this program, write the following commands in Terminal: # $ source deactivate mp # $ cd notch-out/ # $ python ../NotchIP.py sim.use_output_directory() sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("ez", mp.at_every(0.6, mp.output_efield_z)), until = 200) #sim.run(mp.at_every(0.6 , mp.output_png(mp.Ez, "-Zc dkbluered")), until=200) #--------------------------------------------------------- ''' #--------------------------------------------------------- # FOR GENERATING THE TRANSMITTANCE SPECTRUM nfreq = 1 # number of frequencies at which to compute flux refl_fr1 = mp.FluxRegion(center=mp.Vector3(Lr1, 0), size=mp.Vector3( 0, monitorheight)) # Reflected flux 1 refl_fr2 = mp.FluxRegion(center=mp.Vector3(Lr2, 0), size=mp.Vector3( 0, monitorheight)) # Reflected flux 2 tran_fr = mp.FluxRegion(center=mp.Vector3(Lt, 0), size=mp.Vector3( 0, monitorheight)) # Transmitted flux su_fr = mp.FluxRegion(center=mp.Vector3(0, monitorheight / 2), size=mp.Vector3( a, 0)) # Flux loss above the waveguide sd_fr = mp.FluxRegion(center=mp.Vector3(0, -monitorheight / 2), size=mp.Vector3( a, 0)) # Flux loss below the waveguide # refl1 = sim.add_flux(fcen, df, nfreq, refl_fr1) # refl2 = sim.add_flux(fcen, df, nfreq, refl_fr2) # tran = sim.add_flux(fcen, df, nfreq, tran_fr) # su = sim.add_flux(fcen, df, nfreq, su_fr) # sd = sim.add_flux(fcen, df, nfreq, sd_fr) # ------------------------ CODE FOR SEPARATING FUND AND FIRST ORDER MODE STARTS HERE ------------------------ refl_vals = [] tran_vals = [] def get_refl_slice(sim): # print(sim.get_array(center=mp.Vector3(Lr1,0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True)) # refl_val = sim.get_array(center=mp.Vector3(Lr1,0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True) refl_vals.append( sim.get_array(center=mp.Vector3(Lr1, 0), size=mp.Vector3(0, monitorheight - 2 / resolution), component=mp.Ez, cmplx=True)) def get_tran_slice(sim): # print(sim.get_array(center=mp.Vector3(Lt, 0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True)) # tran_val = sim.get_array(center=mp.Vector3(Lt, 0), size=mp.Vector3(0,H), component=mp.Ez, cmplx=True) tran_vals.append( sim.get_array(center=mp.Vector3(Lt, 0), size=mp.Vector3(0, monitorheight - 2 / resolution), component=mp.Ez, cmplx=True)) gif = True if gif: # and w == 0.1: sim.use_output_directory() sim.run(mp.at_beginning(mp.output_epsilon), mp.at_end(get_refl_slice), mp.at_end(get_tran_slice), until=100) refl1 = sim.add_flux(fcen, df, nfreq, refl_fr1) refl2 = sim.add_flux(fcen, df, nfreq, refl_fr2) tran = sim.add_flux(fcen, df, nfreq, tran_fr) su = sim.add_flux(fcen, df, nfreq, su_fr) sd = sim.add_flux(fcen, df, nfreq, sd_fr) # sim.run(mp.at_every(wavelength / 20, mp.output_efield_z), until=wavelength) # sim.run(mp.at_every(wavelength/20 , mp.output_png(mp.Ez, "-RZc bluered -A notch-out/notch-eps-000000000.h5 -a gray:.2")), until=19*wavelength/20) sim.run(mp.at_every( wavelength / 20, mp.output_png( mp.Ez, "-RZc bluered -A notch-out/notch-eps-000000.00.h5 -a gray:.2" )), until=19 * wavelength / 20) sim.run(until=50) else: sim.run(mp.at_end(get_refl_slice), mp.at_end(get_tran_slice), until=100) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mp.Vector3(), 1e-5)) os.system( "h5topng notch-out/notch-eps-000000.00.h5; mv notch-out/notch-eps-000000.00.png " + case + "-" + str(int(w * 1000)) + "-" + str(mode) + "-eps.png") os.system("cp notch-out/notch-ez-000100.00.png " + case + "-" + str(int(w * 1000)) + "-" + str(mode) + ".png") os.system("convert notch-out/notch-ez-*.png " + case + "-" + str(int(w * 1000)) + "-" + str(mode) + ".gif") # get_eigenmode(fcen, mp., refl_fr1, 1, kpoint) # v = mp.volume(mp.vec(Lr1, -monitorheight/2), mp.vec(Lr1, monitorheight/2)) # mode = get_eigenmode(fcen, mp.X, v, v, 1, mp.vec(0, 0, 0), True, 0, 0, 1e-7, True) # print(mode.amplitude) # coef_refl_fund = mp.get_eigenmode_coefficients_and_kpoints(refl_fr1, 1, eig_parity=eig_parity_fund, eig_resolution=resolution, eig_tolerance=1e-7) # coef_refl_first = mp.get_eigenmode_coefficients_and_kpoints(refl_fr1, 1, eig_parity=eig_parity_first, eig_resolution=resolution, eig_tolerance=1e-7) # # coef_tran_fund = mp.get_eigenmode_coefficients_and_kpoints(tran_fr, 1, eig_parity=eig_parity_fund, eig_resolution=resolution, eig_tolerance=1e-7) # coef_tran_first = mp.get_eigenmode_coefficients_and_kpoints(tran_fr, 1, eig_parity=eig_parity_first, eig_resolution=resolution, eig_tolerance=1e-7) ep = mp.ODD_Z #coef_refl_fund, vgrp, kpoints_fund = sim.get_eigenmode_coefficients(refl1, [1], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) #coef_refl_first, vgrp, kpoints_first = sim.get_eigenmode_coefficients(refl1, [2], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) #coef_tran_fund, vgrp, kpoints_fund = sim.get_eigenmode_coefficients(tran, [1], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) #coef_tran_first, vgrp, kpoints_first = sim.get_eigenmode_coefficients(tran, [2], eig_parity=ep, eig_resolution=resolution, eig_tolerance=1e-7) # print(kpoints_fund) # print(kpoints_first) # print(kpoints_fund[0]) # print(type(kpoints_fund[0])) # print(dir(kpoints_fund[0])) # n_eff_fund = wavelength*kpoints_fund[0].x # n_eff_first = wavelength*kpoints_first[0].x n_eff_fund = neff n_eff_first = 1 print(n_eff_fund) print(n_eff_first) # print(coef_refl_fund) # print(type(coef_refl_fund)) # print(dir(coef_refl_fund)) # print(coef_refl_fund[0]) # print(coef_refl_fund[0][0,0,:]) # # fund_refl_amp = coef_refl_fund[0][0,0,1]; # first_order_refl_amp = coef_refl_first[0][0,0,1]; # fund_tran_amp = coef_tran_fund[0][0,0,0]; # first_order_tran_amp = coef_tran_first[0][0,0,0]; print("get_eigenmode_coefficients:\n") #print(coef_refl_fund) #print(coef_refl_first) #print(coef_tran_fund) #print(coef_tran_first) print("\n") # print(coef_refl_fund[0,0,:]) #fund_refl_amp = coef_refl_fund[0,0,1]; #first_order_refl_amp = coef_refl_first[0,0,1]; #fund_tran_amp = coef_tran_fund[0,0,0]; #first_order_tran_amp = coef_tran_first[0,0,0]; refl_val = refl_vals[0] tran_val = tran_vals[0] # n_eff must satisfy n_e <= n_eff <= n_c for the mode to be bound. def fund_func(n_eff): if n_eff >= n_c and n_eff <= n_e: return sqrt(n_eff**2 - n_c**2) - sqrt(n_e**2 - n_eff**2) * tan( pi * h / wavelength * sqrt(n_e**2 - n_eff**2)) def first_order_func(n_eff): if n_eff >= n_c and n_eff <= n_e: return sqrt(n_eff**2 - n_c**2) - sqrt(n_e**2 - n_eff**2) * tan( pi * h / wavelength * sqrt(n_e**2 - n_eff**2) - pi / 2) initial_guess = (n_c + n_e) / 2 # n_eff_fund = fsolve(fund_func, initial_guess) # n_eff_first = fsolve(first_order_func, n_c) print(n_eff_fund, n_eff_first) assert (n_eff_fund > n_eff_first) if len(n_eff_funds) == 0: n_eff_funds.append(n_eff_fund) if len(n_eff_firsts) == 0: n_eff_firsts.append(n_eff_first) ky0_fund = np.abs(2 * pi / wavelength * sqrt(n_e**2 - n_eff_fund**2)) ky0_first = np.abs(2 * pi / wavelength * sqrt(n_e**2 - n_eff_first**2)) ky1_fund = ky0_fund # np.abs(2 * pi / wavelength * sqrt(n_eff_fund **2 - n_c**2)) ky1_first = ky0_first # np.abs(2 * pi / wavelength * sqrt(n_eff_first**2 - n_c**2)) E_fund = lambda y: cos(ky0_fund * y) if np.abs(y) < h / 2 else cos( ky0_fund * h / 2) * np.exp(-ky1_fund * (np.abs(y) - h / 2)) E_first_order = lambda y: sin(ky0_first * y) if np.abs( y) < h / 2 else sin(ky0_first * h / 2) * np.exp(-ky1_first * ( np.abs(y) - h / 2)) * np.sign(y) # y_list = np.arange(-H/2+.5/resolution, H/2-.5/resolution, 1/resolution) #print("Y LIST: ", y_list) #print("SIZE OF Y LIST: ", y_list.size) E_fund_vec = np.zeros(y_list.size) E_first_order_vec = np.zeros(y_list.size) for index in range(y_list.size): y = y_list[index] E_fund_vec[index] = E_fund(y) E_first_order_vec[index] = E_first_order(y) # print(dir(sim)) # print(type(sim.get_eigenmode_coefficients)) # print(dir(sim.get_eigenmode_coefficients)) # print(type(sim.get_eigenmode)) # print(dir(sim.get_eigenmode)) # print(sim.get_eigenmode.__code__.co_varnames) # print(sim.get_eigenmode.__defaults__) # E1 = sim.get_eigenmode(fcen, mp.X, refl1.where, 1, None) # E2 = sim.get_eigenmode(fcen, mp.X, refl1.where, 2, None) # E3 = sim.get_eigenmode(fcen, mp.X, refl1.where, 3, None) # E4 = sim.get_eigenmode(fcen, mp.X, refl1.where, 4, None) # E1 = sim.get_eigenmode(fcen, mp.X, refl1.where, 1, mp.Vector3(0, 0, 0)) # E2 = sim.get_eigenmode(fcen, mp.X, refl1.where, 2, mp.Vector3(0, 0, 0)) # E3 = sim.get_eigenmode(fcen, mp.X, refl1.where, 3, mp.Vector3(0, 0, 0)) # E4 = sim.get_eigenmode(fcen, mp.X, refl1.where, 4, mp.Vector3(0, 0, 0)) # print(refl1.where) # numEA = 0 # E1 = sim.fields.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 1, mp.vec(0,0,0), True, 0, numEA, 1e-7, True) # print(type(E1)) # print(dir(E1)) # # print(refl1.where) # # print(E1, E2, E3, E4) # print(E1.amplitude, E1.band_num, E1.group_velocity, E1.k) # print(type(E1.amplitude)) # print(dir(E1.amplitude)) # print(doc(E1.amplitude)) # print(self(E1.amplitude)) # print(E1.amplitude(y_list)) # print(type(E1.amplitude(y_list))) # print(dir(E1.amplitude(y_list))) # print(E1.amplitude, E2.amplitude, E3.amplitude, E4.amplitude) # E1 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 1, mp.vec(0, 0, 0)) # E2 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 2, mp.vec(0, 0, 0)) # E3 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 3, mp.vec(0, 0, 0)) # E4 = sim.get_eigenmode(fcen, mp.X, refl1.where, refl1.where, 4, mp.vec(0, 0, 0)) # print(y_list) # # E1a = np.zeros(y_list.size, dtype='Complex128'); # E2a = np.zeros(y_list.size, dtype='Complex128'); # E3a = np.zeros(y_list.size, dtype='Complex128'); # E4a = np.zeros(y_list.size, dtype='Complex128'); # # # print(mp.eigenmode_amplitude.__code__.co_varnames) # # print(mp.eigenmode_amplitude.__defaults__) # # for i in range(y_list.size): # # print(E1) # # print(E1.swigobj) # # print(E1.amplitude) # # print(mp.vec(Lr1, y_list[i], 0)) # # print(mp.Vector3(Lr1, y_list[i], 0)) # # print(mp.Ez) # # E1a[i] = mp.eigenmode_amplitude(E1.swigobj, mp.vec(Lr1, y_list[i], 0), mp.Ez); # eval_point = mp.vec(Lr1, y_list[i], 0) # # print(eval_point) # E1a[i] = mp.eigenmode_amplitude(E1.swigobj, eval_point, mp.Ex) # E2a[i] = mp.eigenmode_amplitude(E2.swigobj, eval_point, mp.Ex) # E3a[i] = mp.eigenmode_amplitude(E3.swigobj, eval_point, mp.Ex) # E4a[i] = mp.eigenmode_amplitude(E4.swigobj, eval_point, mp.Ex) # # E1a[i] = mp.eigenmode_amplitude(E1.amplitude, mp.Vector3(Lr1, y_list[i], 0), mp.Ez); # # plt.plot(y_list, np.abs(E1a)**2, 'bo-', label='E1') # plt.plot(y_list, np.abs(E2a)**2, 'ro-', label='E2') # plt.plot(y_list, np.abs(E3a)**2, 'go-', label='E3') # plt.plot(y_list, np.abs(E4a)**2, 'co-', label='E4') # # plt.axis([40.0, 300.0, 0.0, 100.0]) # plt.xlabel("y (um)") # plt.ylabel("Field (a.u.)") # plt.legend(loc="center right") # plt.show() # print("r VECTOR: ", refl_val) # print("t VECTOR: ", tran_val) # print("E0 VECTOR: ", E_fund_vec) # print("E1 VECTOR: ", E_first_order_vec) # fund_refl_amp_2 = np.conj(np.dot(refl_val, E_fund_vec) / np.dot(E_fund_vec, E_fund_vec)) # Conjugate becasue MEEP uses physics exp(kz-wt) rather than engineering exp(wt-kz) # first_order_refl_amp_2 = np.conj(np.dot(refl_val, E_first_order_vec) / np.dot(E_first_order_vec, E_first_order_vec)) # fund_tran_amp_2 = np.conj(np.dot(tran_val, E_fund_vec) / np.dot(E_fund_vec, E_fund_vec)) # first_order_tran_amp_2 = np.conj(np.dot(tran_val, E_first_order_vec) / np.dot(E_first_order_vec, E_first_order_vec)) fund_refl_amp = np.conj( np.dot(refl_val, E0) / np.dot(E0, E0) ) # Conjugate becasue MEEP uses physics exp(kz-wt) rather than engineering exp(wt-kz) first_order_refl_amp = 0 # np.conj(np.dot(refl_val, E1[:,2]) / np.dot(E1[:,2], E1[:,2])) fund_tran_amp = np.conj(np.dot(tran_val, E0) / np.dot(E0, E0)) first_order_tran_amp = 0 # np.conj(np.dot(tran_val, E1[:,2]) / np.dot(E1[:,2], E1[:,2])) fund_refl = np.conj(fund_refl_amp) * E0 not_fund_refl = refl_val - fund_refl fund_tran = np.conj(fund_tran_amp) * E0 not_fund_tran = tran_val - fund_tran fund_refl_power = np.dot(np.conj(fund_refl), fund_refl) not_fund_refl_power = np.dot(np.conj(not_fund_refl), not_fund_refl) fund_tran_power = np.dot(np.conj(fund_tran), fund_tran) not_fund_tran_power = np.dot(np.conj(not_fund_tran), not_fund_tran) fund_refl_ratio = np.abs(fund_refl_power / (fund_refl_power + not_fund_refl_power)) first_order_refl_ratio = 0 fund_tran_ratio = np.abs(fund_tran_power / (fund_tran_power + not_fund_tran_power)) first_order_tran_ratio = 0 # plt.plot(y_list, np.abs(refl_val), 'bo-',label='reflectance') # plt.plot(y_list, np.abs(tran_val), 'ro-',label='transmittance') # plt.plot(y_list, E0, 'go-',label='E0') # plt.plot(y_list, fund_refl_amp*E0, 'co-',label='over') # # plt.axis([40.0, 300.0, 0.0, 100.0]) # plt.xlabel("y (um)") # plt.ylabel("Field") # plt.legend(loc="center right") # plt.show() # # print("\n") # # print(tran_val.size, refl_val.size) # # print("\n") # # print(tran_val, refl_val, E0) # # print("\n") # # print(np.conj(tran_val), tran_val, E1[:,2]) # # # # print(np.conj(refl_val), refl_val, E1[:,2]) # # refl_tot_power = np.abs(np.dot(np.conj(refl_val), refl_val)) # tran_tot_power = np.abs(np.dot(np.conj(tran_val), tran_val)) # # print(fund_refl_amp, refl_tot_power, fund_tran_amp, tran_tot_power) # print(fund_refl_amp , fund_refl_amp_2 ) # print(first_order_refl_amp , first_order_refl_amp_2) # print(fund_tran_amp , fund_tran_amp_2 ) # print(first_order_tran_amp , first_order_tran_amp_2) # # print(np.angle(fund_refl_amp), np.angle(fund_refl_amp_2)) # print(np.angle(first_order_refl_amp), np.angle(first_order_refl_amp_2)) # print(np.angle(fund_tran_amp), np.angle(fund_tran_amp_2)) # print(np.angle(first_order_tran_amp), np.angle(first_order_tran_amp_2)) # fund_refl_power = np.abs(fund_tran_amp) ** 2 # fund_refl_power = np.abs(fund_refl_amp) ** 2 # first_order_refl_power = np.abs(first_order_refl_amp) ** 2 # fund_tran_power = np.abs(fund_tran_amp) ** 2 # first_order_tran_power = np.abs(first_order_tran_amp) ** 2 # print(fund_refl_power, first_order_refl_power, fund_tran_power, first_order_tran_power) # fund_refl_ratio = fund_refl_power / (fund_refl_power + first_order_refl_power) # first_order_refl_ratio = first_order_refl_power / (fund_refl_power + first_order_refl_power) # fund_tran_ratio = fund_tran_power / (fund_tran_power + first_order_tran_power) # first_order_tran_ratio = first_order_tran_power / (fund_tran_power + first_order_tran_power) # fund_refl_power = np.abs(fund_refl_amp) ** 2 # first_order_refl_power = np.abs(first_order_refl_amp) ** 2 # fund_tran_power = np.abs(fund_tran_amp) ** 2 # first_order_tran_power = np.abs(first_order_tran_amp) ** 2 # # fund_refl_ratio = fund_refl_power / refl_tot_power # first_order_refl_ratio = first_order_refl_power / refl_tot_power # fund_tran_ratio = fund_tran_power / tran_tot_power # first_order_tran_ratio = first_order_tran_power / tran_tot_power # # fund_refl_ratio = 1 # first_order_refl_ratio = 0 # fund_tran_ratio = 1 # first_order_tran_ratio = 0 print("Percentage of reflected light in fundamental mode: ", fund_refl_ratio * 100) print("Percentage of reflected light in first order mode: ", first_order_refl_ratio * 100) print("Percentage of transmitted light in fundamental mode: ", fund_tran_ratio * 100) print("Percentage of transmitted light in first order mode: ", first_order_tran_ratio * 100) # ------------------------ CODE FOR SEPARATING FUND AND FIRST ORDER MODE ENDS HERE ------------------------ wl = [] #list of wavelengths refl1_flux = mp.get_fluxes(refl1) refl2_flux = mp.get_fluxes(refl2) tran_flux = mp.get_fluxes(tran) su_flux = mp.get_fluxes(su) sd_flux = mp.get_fluxes(sd) flux_freqs = mp.get_flux_freqs(refl1) for i in range(nfreq): wl = np.append(wl, 1 / flux_freqs[i]) print(1 / flux_freqs[i]) # for ind, elt in enumerate(wl): # #print(round(elt, 4)) # if round(elt, 3) == 0.637: # #print("ALERT: MATCH FOUND") # index = ind index = 0 rp = refl1_flux[index] tp = tran_flux[index] # print("rp/tp:\n") # print(rp) # print(tp) # print("\n") R = -refl1_flux[index] / (refl2_flux[index] - refl1_flux[index]) T = tran_flux[index] / (refl2_flux[index] - refl1_flux[index]) S = (refl2_flux[index] - tran_flux[index]) / (refl2_flux[index] - refl1_flux[index]) Su = su_flux[index] / (refl2_flux[index] - refl1_flux[index]) Sd = -sd_flux[index] / (refl2_flux[index] - refl1_flux[index]) S_correction = (1 - R - T) / (Su + Sd) # print(R, T, S, Su, Sd) r = sqrt(R) t = sqrt(T) # The amplitude ... times the phase ... accounting for the distance to the detector (Reverse exp(-kz) of phase). # r_fund = (r * fund_refl_ratio) * (fund_refl_amp / np.abs(fund_refl_amp)) * (np.exp( 2j*pi * (-Lr1 - w/2) * n_eff_fund / wavelength)) # Lr1 is negative because it is the (negative) position of the monitor, not the distace from the monitor to the center. # r_first = (r * first_order_refl_ratio) * (first_order_refl_amp / np.abs(first_order_refl_amp)) * (np.exp( 2j*pi * (-Lr1 - w/2) * n_eff_first / wavelength)) # t_fund = (t * fund_tran_ratio) * (fund_tran_amp / np.abs(fund_tran_amp)) * (np.exp( 2j*pi * ( Lt - w/2) * n_eff_fund / wavelength)) # t_first = (t * first_order_tran_ratio) * (first_order_tran_amp / np.abs(first_order_tran_amp)) * (np.exp( 2j*pi * ( Lt - w/2) * n_eff_first / wavelength)) r_fund = (r * fund_refl_ratio) * np.exp( 1j * np.angle(fund_refl_amp) + 2j * pi * (-Lr1 - w / 2) * n_eff_fund / wavelength ) # Lr1 is negative because it is the (negative) position of the monitor, not the distace from the monitor to the center. r_first = (r * first_order_refl_ratio ) * np.exp(1j * np.angle(first_order_refl_amp) + 2j * pi * (-Lr1 - w / 2) * n_eff_first / wavelength) t_fund = (t * fund_tran_ratio ) * np.exp(1j * np.angle(fund_tran_amp) + 2j * pi * (Lt - w / 2) * n_eff_fund / wavelength) t_first = (t * first_order_tran_ratio ) * np.exp(1j * np.angle(first_order_tran_amp) + 2j * pi * (Lt - w / 2) * n_eff_first / wavelength) if mode == 0: r00 = r_fund r01 = r_first t00 = t_fund t01 = t_first su0 = sqrt(np.abs(Su * S_correction)) sd0 = sqrt(np.abs(Sd * S_correction)) # su0 = sqrt(Su) # sd0 = sqrt(Sd) r00s.append(r00) r01s.append(r01) t00s.append(t00) t01s.append(t01) su0s.append(su0) sd0s.append(sd0) if only_fund: r10s.append(0) r11s.append(0) t10s.append(0) t11s.append(1) su1s.append(0) sd1s.append(0) else: r10 = r_fund r11 = r_first t10 = t_fund t11 = t_first su1 = sqrt(np.abs(Su * S_correction)) sd1 = sqrt(np.abs(Sd * S_correction)) # su1 = sqrt(Su) # sd1 = sqrt(Sd) r10s.append(r10) r11s.append(r11) t10s.append(t10) t11s.append(t11) su1s.append(su1) sd1s.append(sd1) norm_Su = S * Su / (Su + Sd) NET = round((R + T + S) * 100, 0) if NET > 100.0: NET = 100.0 NET_LOSS = round((Su + Sd) / S * 100, 0) if NET_LOSS > 100.0: NET_LOSS = 100.0 ''' np.append(ws, [w * 1000]) np.append(Rs, [R * 100]) np.append(Ts, [T * 100]) np.append(Ss, [S * 100]) np.append(NET_LIST, [NET]) np.append(Sus, [Su * 100]) np.append(Sds, [Sd * 100]) np.append(NET_LOSS_LIST, [NET_LOSS]) np.append(norm_Sus, [norm_Su * 100]) ''' if mode == 0: ws.append(w * 1000) Rs.append(R * 100) Ts.append(T * 100) Ss.append(S * 100) NET_LIST.append(NET) Sus.append(Su * 100) Sds.append(Sd * 100) NET_LOSS_LIST.append(NET_LOSS) norm_Sus.append(norm_Su * 100) if mode == 0: f1.write("--------------------------------------------------- \n") f1.write("Notch Width: %s nanometers \n" % (w * 1000)) f1.write("Reflection Percentage: %s\n" % (R * 100)) f1.write("Transmission Percentage: %s\n" % (T * 100)) f1.write("Total Loss Percentage: %s\n" % (S * 100)) f1.write("Percentage of Light Accounted For: %s\n" % (NET)) f1.write("Upper Loss Percentage: %s\n" % (Su * 100)) f1.write("Lower Loss Percentage: %s\n" % (Sd * 100)) f1.write("Percentage of Total Loss Accounted For: %s\n" % (NET_LOSS)) f1.write("Normalized Upper Loss Percentage: %s\n" % (norm_Su * 100)) f1.write("\n \n") f1.write("FUNDAMENTAL MODE \n") f1.write("n_eff: %s\n" % (n_eff_fund)) f1.write("Re(r00): %s\n" % (np.real(r00))) f1.write("Im(r00): %s\n" % (np.imag(r00))) f1.write("Re(r01): %s\n" % (np.real(r01))) f1.write("Im(r01): %s\n" % (np.imag(r01))) f1.write("Re(t00): %s\n" % (np.real(t00))) f1.write("Im(t00): %s\n" % (np.imag(t00))) f1.write("Re(t01): %s\n" % (np.real(t01))) f1.write("Im(t01): %s\n" % (np.imag(t01))) f1.write("Re(su0): %s\n" % (np.real(su0))) f1.write("Im(su0): %s\n" % (np.imag(su0))) f1.write("Re(sd0): %s\n" % (np.real(sd0))) f1.write("Im(sd0): %s\n" % (np.imag(sd0))) f1.write("\n") else: f1.write("FIRST ORDER MODE \n") f1.write("n_eff: %s\n" % (n_eff_first)) f1.write("Re(r10): %s\n" % (np.real(r10))) f1.write("Im(r10): %s\n" % (np.imag(r10))) f1.write("Re(r11): %s\n" % (np.real(r11))) f1.write("Im(r11): %s\n" % (np.imag(r11))) f1.write("Re(t10): %s\n" % (np.real(t10))) f1.write("Im(t10): %s\n" % (np.imag(t10))) f1.write("Re(t11): %s\n" % (np.real(t11))) f1.write("Im(t11): %s\n" % (np.imag(t11))) f1.write("Re(su1): %s\n" % (np.real(su1))) f1.write("Im(su1): %s\n" % (np.imag(su1))) f1.write("Re(sd1): %s\n" % (np.real(sd1))) f1.write("Im(sd1): %s\n" % (np.imag(sd1))) f1.write("--------------------------------------------------- \n") sim.reset_meep()
time = 500 #---------------------------------------------------------------------- # Eigenmode source #---------------------------------------------------------------------- fcen = 1/1.55 width = 0.2 fwidth = width * fcen source_center = [-1,0,0] source_size = mp.Vector3(0,2,0) kpoint = mp.Vector3(1,0,0) src = mp.GaussianSource(frequency=fcen,fwidth=fwidth) source = [mp.EigenModeSource(src, eig_band = 1, direction=mp.NO_DIRECTION, eig_kpoint=kpoint, size = source_size, center=source_center)] #---------------------------------------------------------------------- #- geometric objects #---------------------------------------------------------------------- Nx = 10 Ny = 10 design_region = mp.Volume(center=mp.Vector3(), size=mp.Vector3(1, 1, 0)) rho_vector = 11*np.random.rand(Nx*Ny) + 1 basis = mpa.BilinearInterpolationBasis(volume=design_region,Nx=Nx,Ny=Ny,rho_vector=rho_vector) geometry = [ mp.Block(center=mp.Vector3(x=-Sx/4), material=mp.Medium(index=3.45), size=mp.Vector3(Sx/2, 0.5, 0)), # horizontal waveguide
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
center=mp.Vector3(x, h * (1 - e) / 2), material=default_material)) Ls = -2 * x # Position of source Lr1 = -2 * x - 1.25 # Position of reflection monitor 1 Lr2 = -2 * x + 1.25 # Position of reflection monitor 2 Lt = -Lr2 # Position of transmission monitor scatter_monitor_size = 2 * Lt assert (Lr1 > -a / 2 + dpml ) # Make sure that nothing we care about is sitting inside the PML sources = [ mp.EigenModeSource(mp.ContinuousSource(frequency=fcen), size=mp.Vector3(0, H), center=mp.Vector3(Ls, 0)) ] pml_layers = [mp.Absorber(thickness=dpml)] sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution) sim.reset_meep() nfreq = 1
def main(args): cell_zmax = 0.5 * cell_thickness if args.three_d else 0 cell_zmin = -0.5 * cell_thickness if args.three_d else 0 si_zmax = t_Si if args.three_d else 0 # read cell size, volumes for source region and flux monitors, # and coupler geometry from GDSII file upper_branch = mp.get_GDSII_prisms(silicon, gdsII_file, UPPER_BRANCH_LAYER, si_zmin, si_zmax) lower_branch = mp.get_GDSII_prisms(silicon, gdsII_file, LOWER_BRANCH_LAYER, si_zmin, si_zmax) cell = mp.GDSII_vol(gdsII_file, CELL_LAYER, cell_zmin, cell_zmax) p1 = mp.GDSII_vol(gdsII_file, PORT1_LAYER, si_zmin, si_zmax) p2 = mp.GDSII_vol(gdsII_file, PORT2_LAYER, si_zmin, si_zmax) p3 = mp.GDSII_vol(gdsII_file, PORT3_LAYER, si_zmin, si_zmax) p4 = mp.GDSII_vol(gdsII_file, PORT4_LAYER, si_zmin, si_zmax) src_vol = mp.GDSII_vol(gdsII_file, SOURCE_LAYER, si_zmin, si_zmax) # displace upper and lower branches of coupler (as well as source and flux regions) if args.d != default_d: delta_y = 0.5 * (args.d - default_d) delta = mp.Vector3(y=delta_y) p1.center += delta p2.center -= delta p3.center += delta p4.center -= delta src_vol.center += delta cell.size += 2 * delta for np in range(len(lower_branch)): lower_branch[np].center -= delta for nv in range(len(lower_branch[np].vertices)): lower_branch[np].vertices[nv] -= delta for np in range(len(upper_branch)): upper_branch[np].center += delta for nv in range(len(upper_branch[np].vertices)): upper_branch[np].vertices[nv] += delta geometry = upper_branch + lower_branch if args.three_d: oxide_center = mp.Vector3(z=-0.5 * t_oxide) oxide_size = mp.Vector3(cell.size.x, cell.size.y, t_oxide) oxide_layer = [ mp.Block(material=oxide, center=oxide_center, size=oxide_size) ] geometry = geometry + oxide_layer sources = [ mp.EigenModeSource( src=mp.GaussianSource(fcen, fwidth=df), volume=src_vol, eig_band=1, eig_parity=mp.NO_PARITY if args.three_d else mp.EVEN_Y + mp.ODD_Z, eig_match_freq=True) ] sim = mp.Simulation(resolution=args.res, cell_size=cell.size, boundary_layers=[mp.PML(dpml)], sources=sources, geometry=geometry) mode1 = sim.add_mode_monitor(fcen, 0, 1, mp.ModeRegion(volume=p1)) mode2 = sim.add_mode_monitor(fcen, 0, 1, mp.ModeRegion(volume=p2)) mode3 = sim.add_mode_monitor(fcen, 0, 1, mp.ModeRegion(volume=p3)) mode4 = sim.add_mode_monitor(fcen, 0, 1, mp.ModeRegion(volume=p4)) sim.run(until_after_sources=100) # S parameters p1_coeff = sim.get_eigenmode_coefficients( mode1, [1], eig_parity=mp.NO_PARITY if args.three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, 0] p2_coeff = sim.get_eigenmode_coefficients( mode2, [1], eig_parity=mp.NO_PARITY if args.three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, 1] p3_coeff = sim.get_eigenmode_coefficients( mode3, [1], eig_parity=mp.NO_PARITY if args.three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, 0] p4_coeff = sim.get_eigenmode_coefficients( mode4, [1], eig_parity=mp.NO_PARITY if args.three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, 0] # transmittance p2_trans = abs(p2_coeff)**2 / abs(p1_coeff)**2 p3_trans = abs(p3_coeff)**2 / abs(p1_coeff)**2 p4_trans = abs(p4_coeff)**2 / abs(p1_coeff)**2 print("trans:, {:.2f}, {:.6f}, {:.6f}, {:.6f}".format( args.d, p2_trans, p3_trans, p4_trans))
] geometry = [ mp.Prism(vertices,height=thickness,material=Si) # taper structure ] # Setup domain resolution = 20 cell_size = mp.Vector3(12,4,0) boundary_layers = [mp.PML(1.0)] # Blast it with TE polarized source. Don't worry about an eigenmode source, # since we want to measure multiple modes. sources = [ mp.EigenModeSource( src=mp.GaussianSource(1/1.55,fwidth=0.1/1.55), center=[-length/2 - 2], size=[0,cell_size.y,cell_size.y], eig_parity=mp.ODD_Z+mp.EVEN_Y ) ] # Set up simulation object sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=boundary_layers, geometry=geometry, default_material=SiO2, sources=sources) # Add the mode monitors fcen = 1/1.55 # 1.55 microns df = 0.1*fcen # 10% bandwidth
def main(args): resolution = 30 # pixels/μm Si = mp.Medium(index=3.45) dpml = 1.0 pml_layers = [mp.PML(dpml)] sx = 5 sy = 3 cell = mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, 0) 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) ] k_point = mp.Vector3(z=0.5) 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) 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)), 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)), 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=5000) sim.display_fluxes(wvg_flux) sim.display_forces(wvg_force)
lcen = 6.67 # mode frequency fcen = 1 / lcen symmetries = [mp.Mirror(mp.Y)] for m in range(5): Lt = 2**m sx = dpml_x + Lw + Lt + Lw + dpml_x cell_size = mp.Vector3(sx, sy, 0) src_pt = mp.Vector3(-0.5 * sx + dpml_x + 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_y, 0), eig_match_freq=True, eig_parity=mp.ODD_Z + mp.EVEN_Y) ] # straight waveguide vertices = [ mp.Vector3(-0.5 * sx - 1, 0.5 * w1), mp.Vector3(0.5 * sx + 1, 0.5 * w1), mp.Vector3(0.5 * sx + 1, -0.5 * w1), mp.Vector3(-0.5 * sx - 1, -0.5 * w1) ] sim = mp.Simulation( resolution=resolution, cell_size=cell_size,
deps = 1e-5 dp = deps * np.random.rand(Nx * Ny) w = 1.0 waveguide_geometry = [ mp.Block(material=silicon, center=mp.Vector3(), size=mp.Vector3(mp.inf, w, mp.inf)) ] fcen = 1 / 1.55 df = 0.23 * fcen sources = [ mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=df), center=mp.Vector3(-0.5 * sxy + dpml, 0), size=mp.Vector3(0, sxy), eig_band=1, eig_parity=eig_parity) ] def forward_simulation(design_params, mon_type, frequencies=None): matgrid = mp.MaterialGrid(mp.Vector3(Nx, Ny), mp.air, silicon, weights=design_params.reshape(Nx, Ny), grid_type='U_MEAN') matgrid_geometry = [ mp.Block(center=mp.Vector3(), size=mp.Vector3(design_shape.x, design_shape.y, 0),
def __init__(self, objective_regions=[], objective=None, basis=None, design_region=None, cell_size=None, background_geometry=[], foreground_geometry=[], sources=None, source_region=[], extra_quantities=[], extra_regions=[]): """ Parameters: objective regions (list of Subregion): subregions of the computational cell over which frequency-domain fields are tabulated and used to compute objective quantities objective (str): definition of the quantity to be maximized. This should be a mathematical expression in which the names of one or more objective quantities appear, and which should evaluate to a real number when numerical values are substituted for the names of all objective quantities. basis (Basis): design_region (Subregion): (precisely one of these should be non-None) Specification of function space for the design permittivity. In general, basis will be a caller-created instance of some subclass of meep.adjoint.Basis. Then the spatial extent of the design region is determined by basis and the design_region argument is ignored. As an alternative convenience convention, the caller may omit basis and set design_region; in this case, an appropriate basis for the given design_region is automatically created based on the values of various module-wide options such as 'element_type' and 'element_length'. This convenience convention is only available for box-shaped (hyperrectangular) design regions. cell_size (Vector3) background_geometry (list of GeometricObject) foreground_geometry (list of GeometricObject) Size of computational cell and lists of GeometricObjects that {precede,follow} the design object in the overall geometry. sources (list of Source) source_region (Subregion) (Specify either sources OR source_region) Specification of forward source distribution, i.e. the source excitation(s) that produce the fields from which the objective function is computed. In general, sources will be an arbitrary caller-created list of Source objects, in which case source_region, source_component are ignored. As an alternative convenience convention, the caller may omit sources and instead specify source_region; in this case, a source distribution over the given region is automatically created based on the values of module-wide options (such as fcen, df, source_component, source_mode) extra_quantities (list of str) extra_regions (list of Subregion) By default, the module will compute only those DFT fields and objective quantities needed to evaluate the specified objective function. These arguments may be used to specify lists of ancillary quantities and/or ancillary DFT cells (where 'ancillary' means 'not needed to compute the objective function value) to be computed and reported/plotted as well. """ from meep_adjoint import options #----------------------------------------------------------------------- # process convenience arguments: # (a) if no basis was specified, create one using the given design # region plus global option values # (b) if no sources were specified, create one using the given source # region plus global option values #----------------------------------------------------------------------- if basis is None: basis = FiniteElementBasis(region=design_region, element_length=options('element_length'), element_type=options('element_type')) design_region = basis.domain if not sources: f, df, m, c = [ options(s) for s in ['fcen', 'df', 'source_mode', 'source_component'] ] envelope = mp.GaussianSource(f, fwidth=df) kws = { 'center': V3(source_region.center), 'size': V3(source_region.size), 'src': envelope} #, 'eig_band': m, 'component': c } sources = [ mp.EigenModeSource(eig_band=m,**kws) if m>0 else mp.Source(component=c, **kws) ] adjust_sources(sources) #----------------------------------------------------------------------- #initialize helper classes #----------------------------------------------------------------------- # DFTCells objective_cells = [ DFTCell(r) for r in objective_regions ] extra_cells = [ DFTCell(r) for r in extra_regions ] design_cell = DFTCell(design_region, E_CPTS) dft_cells = objective_cells + extra_cells + [design_cell] # ObjectiveFunction obj_func = ObjectiveFunction(fstr=objective, extra_quantities=extra_quantities) # initial values of (a) design variables, (b) the spatially-varying # permittivity function they define (the 'design function'), (c) the # GeometricObject describing a material body with this permittivity # (the 'design object'), and (d) mp.Simulation superposing the design # object on the rest of the caller's geometry. # Note that sources are not added to the simulation at this stage; # that is done on a just-in-time basis by internal methods of TimeStepper. beta_vector = basis.project(options('eps_func')) # eps_func = basis.parameterized_function(beta_vector) eps_func, set_coefficients = parameterized_function2(basis,beta_vector) design_object = mp.Block(center=V3(design_region.center), size=V3(design_region.size), epsilon_func=eps_func) geometry = background_geometry + [design_object] + foreground_geometry sim = mp.Simulation(resolution=options['res'], boundary_layers=[mp.PML(options['dpml'])], cell_size=V3(cell_size), geometry=geometry) # TimeStepper self.stepper = TimeStepper(obj_func, dft_cells, basis, eps_func, set_coefficients, sim, fwd_sources=sources)