def init(self, no_bend=False): sx = 16 sy = 32 cell = mp.Vector3(sx, sy, 0) pad = 4 w = 1 wvg_ycen = -0.5 * (sy - w - (2 * pad)) wvg_xcen = 0.5 * (sx - w - (2 * pad)) height = 100 if no_bend: no_bend_vertices = [mp.Vector3(-0.5 * sx - 5, wvg_ycen - 0.5 * w), mp.Vector3(+0.5 * sx + 5, wvg_ycen - 0.5 * w), mp.Vector3(+0.5 * sx + 5, wvg_ycen + 0.5 * w), mp.Vector3(-0.5 * sx - 5, wvg_ycen + 0.5 * w)] geometry = [mp.Prism(no_bend_vertices, height, material=mp.Medium(epsilon=12))] else: bend_vertices = [mp.Vector3(-0.5 * sx, wvg_ycen - 0.5 * w), mp.Vector3(wvg_xcen + 0.5 * w, wvg_ycen - 0.5 * w), mp.Vector3(wvg_xcen + 0.5 * w, 0.5 * sy), mp.Vector3(wvg_xcen - 0.5 * w, 0.5 * sy), mp.Vector3(wvg_xcen - 0.5 * w, wvg_ycen + 0.5 * w), mp.Vector3(-0.5 * sx, wvg_ycen + 0.5 * w)] geometry = [mp.Prism(bend_vertices, height, material=mp.Medium(epsilon=12))] fcen = 0.15 df = 0.1 sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(1 + (-0.5 * sx), wvg_ycen), size=mp.Vector3(0, w))] pml_layers = [mp.PML(1.0)] resolution = 10 nfreq = 100 self.sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution) if no_bend: fr = mp.FluxRegion(center=mp.Vector3((sx / 2) - 1.5, wvg_ycen), size=mp.Vector3(0, w * 2)) else: fr = mp.FluxRegion(center=mp.Vector3(wvg_xcen, (sy / 2) - 1.5), size=mp.Vector3(w * 2, 0)) self.trans = self.sim.add_flux(fcen, df, nfreq, fr) refl_fr = mp.FluxRegion(center=mp.Vector3((-0.5 * sx) + 1.5, wvg_ycen), size=mp.Vector3(0, w * 2)) self.refl = self.sim.add_flux(fcen, df, nfreq, refl_fr) if no_bend: self.pt = mp.Vector3((sx / 2) - 1.5, wvg_ycen) else: self.pt = mp.Vector3(wvg_xcen, (sy / 2) - 1.5)
def nonconvex_marching_squares(self, idx, npts): resolution = 25 cell = mp.Vector3(10, 10) data_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), 'data')) vertices_file = os.path.join( data_dir, 'nonconvex_prism_vertices{}.npz'.format(idx)) vertices_obj = np.load(vertices_file) ## prism verticies precomputed from analytic "blob" shape using ## marching squares algorithm of skimage.measure.find_contours ## ref: https://github.com/NanoComp/meep/pull/1142 vertices_data = vertices_obj["N{}".format(npts)] vertices = [mp.Vector3(v[0], v[1], 0) for v in vertices_data] geometry = [ mp.Prism(vertices, height=mp.inf, material=mp.Medium(epsilon=12)) ] sim = mp.Simulation(cell_size=cell, geometry=geometry, resolution=resolution) sim.init_sim() prism_eps = sim.integrate_field_function([mp.Dielectric], lambda r, eps: eps) print("epsilon-sum:, {} (prism-msq)".format(abs(prism_eps))) return prism_eps
def init_sim(self, **kwargs): """ Initializes the simulation. This has to be done after adding all structures in order to correctly determine the size of the simulation. :param kwargs: Parameters which are directly passed to Meep """ z_min = np.min([ structure['z_min'] for structure in self.structures if structure['structure'] ]) z_max = np.max([ structure['z_max'] for structure in self.structures if structure['structure'] ]) bounds = geometric_union( (geometric_union(x['structure']) for x in self.structures)).bounds size = np.array( (bounds[2] - bounds[0], bounds[3] - bounds[1], (z_max - z_min))) self.center = np.round([(bounds[2] + bounds[0]) / 2, (bounds[3] + bounds[1]) / 2, (z_max + z_min) / 2]) self.size = np.ceil(size + self.padding * 2 + self.pml_thickness * 2) if self.reduce_to_2d: self.center[2] = self.size[2] = 0 structures = [] for structure in self.structures: polygon = geometric_union(structure['structure'] + structure['extra_structures']) \ .buffer(np.finfo(np.float32).eps, resolution=0).simplify(np.finfo(np.float32).eps) objs = shapely_collection_to_basic_objs(polygon) for obj in objs: if obj.is_empty: continue for polygon in fracture_intelligently(obj, np.inf, np.inf): structures += [ mp.Prism(vertices=[ mp.Vector3( *point, 0 if self.reduce_to_2d else structure['z_min']) for point in polygon.exterior.coords[:-1] ], material=structure['material'], height=structure['z_max'] - structure['z_min']) ] self.sim = mp.Simulation(mp.Vector3(*self.size), self.resolution, geometry=structures, geometry_center=mp.Vector3(*self.center), sources=self.sources, boundary_layers=[mp.PML(self.pml_thickness)], **kwargs) self.sim.init_sim()
def convex_circle(self, npts, r, sym): resolution = 50 cell = mp.Vector3(3, 3) ### prism vertices computed as equally-spaced points ### along the circumference of a circle with radius r angles = 2 * np.pi / npts * np.arange(npts) vertices = [ mp.Vector3(r * np.cos(ang), r * np.sin(ang)) for ang in angles ] geometry = [ mp.Prism(vertices, height=mp.inf, material=mp.Medium(epsilon=12)) ] sim = mp.Simulation( cell_size=cell, geometry=geometry, symmetries=[mp.Mirror(direction=mp.X), mp.Mirror(direction=mp.Y)] if sym else [], resolution=resolution) sim.init_sim() prism_eps = sim.integrate_field_function([mp.Dielectric], lambda r, eps: eps) sim.reset_meep() geometry = [ mp.Cylinder(radius=r, center=mp.Vector3(), height=mp.inf, material=mp.Medium(epsilon=12)) ] sim = mp.Simulation( cell_size=cell, geometry=geometry, symmetries=[mp.Mirror(direction=mp.X), mp.Mirror(direction=mp.Y)] if sym else [], resolution=resolution) sim.init_sim() cyl_eps = sim.integrate_field_function([mp.Dielectric], lambda r, eps: eps) print( "epsilon-sum:, {} (prism-cyl), {} (cylinder), {} (relative error)". format(abs(prism_eps), abs(cyl_eps), abs((prism_eps - cyl_eps) / cyl_eps))) return abs((prism_eps - cyl_eps) / cyl_eps)
def convex_marching_squares(self, npts): resolution = 50 cell = mp.Vector3(3, 3) data_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), 'data')) vertices_file = os.path.join(data_dir, 'convex_prism_vertices.npz') vertices_obj = np.load(vertices_file) ## prism vertices precomputed for a circle of radius 1.0 using ## marching squares algorithm of skimage.measure.find_contours ## ref: https://github.com/NanoComp/meep/issues/1060 vertices_data = vertices_obj["N{}".format(npts)] vertices = [mp.Vector3(v[0], v[1], 0) for v in vertices_data] geometry = [ mp.Prism(vertices, height=mp.inf, material=mp.Medium(epsilon=12)) ] sim = mp.Simulation(cell_size=cell, geometry=geometry, resolution=resolution) sim.init_sim() prism_eps = sim.integrate_field_function([mp.Dielectric], lambda r, eps: eps) sim.reset_meep() geometry = [ mp.Cylinder(radius=1.0, center=mp.Vector3(), height=mp.inf, material=mp.Medium(epsilon=12)) ] sim = mp.Simulation(cell_size=cell, geometry=geometry, resolution=resolution) sim.init_sim() cyl_eps = sim.integrate_field_function([mp.Dielectric], lambda r, eps: eps) print( "epsilon-sum:, {} (prism-msq), {} (cylinder), {} (relative error)". format(abs(prism_eps), abs(cyl_eps), abs((prism_eps - cyl_eps) / cyl_eps))) return abs((prism_eps - cyl_eps) / cyl_eps)
def setUp(self): resolution = 60 self.cell_size = mp.Vector3(1.0, 1.0, 0) matgrid_resolution = 200 matgrid_size = mp.Vector3(1.0, 1.0, mp.inf) Nx, Ny = int(matgrid_size.x * matgrid_resolution), int( matgrid_size.y * matgrid_resolution) x = np.linspace(-0.5 * matgrid_size.x, 0.5 * matgrid_size.x, Nx) y = np.linspace(-0.5 * matgrid_size.y, 0.5 * matgrid_size.y, Ny) xv, yv = np.meshgrid(x, y) rad = 0.201943 w = 0.104283 weights = np.logical_and( np.sqrt(np.square(xv) + np.square(yv)) > rad, np.sqrt(np.square(xv) + np.square(yv)) < rad + w, dtype=np.double) matgrid = mp.MaterialGrid(mp.Vector3(Nx, Ny), mp.air, mp.Medium(index=3.5), weights=weights, do_averaging=False, beta=0, eta=0.5) geometry = [ mp.Cylinder(center=mp.Vector3(0.35, 0.1), radius=0.1, height=mp.inf, material=mp.Medium(index=1.5)), mp.Block(center=mp.Vector3(-0.15, -0.2), size=mp.Vector3(0.2, 0.24, mp.inf), material=SiN), mp.Block(center=mp.Vector3(-0.2, 0.2), size=mp.Vector3(0.4, 0.4, mp.inf), material=matgrid), mp.Prism(vertices=[ mp.Vector3(0.05, 0.45), mp.Vector3(0.32, 0.22), mp.Vector3(0.15, 0.10) ], height=0.5, material=Co) ] self.sim = mp.Simulation(resolution=resolution, cell_size=self.cell_size, geometry=geometry, eps_averaging=False) self.sim.init_sim()
def device_to_meep(device, mapping): # converts PHIDL to MEEP. You must give a layer mapping that can be derived from get_layer_mapping # TODO: partial etches. Currently this is only 2D geometry = list() for poly_grp in device.polygons: layer = poly_grp.layers[0] try: material = mapping[layer] except KeyError: print('layer {} not in meep mapping'.format(layer)) continue if material is cell_material: cell = mp.Vector3(poly_grp.xsize, poly_grp.ysize) continue elif material is port_source: continue for poly in poly_grp.polygons: vertex_list = list() for vertex in poly: vertex_list.append(mp.Vector3(vertex[0], vertex[1])) geometry.append(mp.Prism(vertex_list, height=0, material=material)) return cell, geometry
def get_simulation( component: Component, extend_ports_length: Optional[float] = 4.0, layer_stack: LayerStack = LAYER_STACK, res: int = 20, t_clad_top: float = 1.0, t_clad_bot: float = 1.0, tpml: float = 1.0, clad_material: str = "SiO2", is_3d: bool = False, wl_min: float = 1.5, wl_max: float = 1.6, wl_steps: int = 50, dfcen: float = 0.2, port_source_name: str = 1, port_field_monitor_name: str = 2, port_margin: float = 0.5, distance_source_to_monitors: float = 0.2, ) -> Dict[str, Any]: """Returns Simulation dict from gdsfactory.component based on meep directional coupler example https://meep.readthedocs.io/en/latest/Python_Tutorials/GDSII_Import/ https://support.lumerical.com/hc/en-us/articles/360042095873-Metamaterial-S-parameter-extraction Args: component: gf.Component extend_ports_function: function to extend the ports for a component to ensure it goes beyond the PML layer_to_thickness: Dict of layer number (int, int) to thickness (um) res: resolution (pixels/um) For example: (10: 100nm step size) t_clad_top: thickness for cladding above core t_clad_bot: thickness for cladding below core tpml: PML thickness (um) clad_material: material for cladding is_3d: if True runs in 3D wavelengths: iterable of wavelengths to simulate dfcen: delta frequency sidewall_angle: in degrees port_source_name: input port name port_field_monitor_name: port_margin: margin on each side of the port distance_source_to_monitors: in (um) source goes before Returns: sim: simulation object Make sure you visualize the simulation region with gf.before you simulate a component .. code:: import gdsfactory as gf import gmeep as gm c = gf.components.bend_circular() margin = 2 cm = gm.add_monitors(c) gf.show(cm) """ layer_to_thickness = layer_stack.get_layer_to_thickness() layer_to_material = layer_stack.get_layer_to_material() layer_to_zmin = layer_stack.get_layer_to_zmin() layer_to_sidewall_angle = layer_stack.get_layer_to_sidewall_angle() wavelengths = np.linspace(wl_min, wl_max, wl_steps) if port_source_name not in component.ports: warnings.warn( f"port_source_name={port_source_name} not in {component.ports.keys()}" ) port_source = component.get_ports_list()[0] port_source_name = port_source.name warnings.warn(f"Selecting port_source_name={port_source_name} instead.") if port_field_monitor_name not in component.ports: warnings.warn( f"port_field_monitor_name={port_field_monitor_name} not in {component.ports.keys()}" ) port_field_monitor = ( component.get_ports_list()[0] if len(component.ports) < 2 else component.get_ports_list()[1] ) port_field_monitor_name = port_field_monitor.name warnings.warn( f"Selecting port_field_monitor_name={port_field_monitor_name} instead." ) assert isinstance( component, Component ), f"component needs to be a gf.Component, got Type {type(component)}" component_extended = ( gf.components.extension.extend_ports( component=component, length=extend_ports_length, centered=True ) if extend_ports_length else component ) component = component.ref() component.x = 0 component.y = 0 gf.show(component_extended) component_extended.flatten() component_extended = component_extended.ref() # geometry_center = [component_extended.x, component_extended.y] # geometry_center = [0, 0] # print(geometry_center) layers_thickness = [ layer_to_thickness[layer] for layer in component.get_layers() if layer in layer_to_thickness ] t_core = max(layers_thickness) cell_thickness = tpml + t_clad_bot + t_core + t_clad_top + tpml if is_3d else 0 cell_size = mp.Vector3( component.xsize + 2 * tpml, component.ysize + 2 * tpml, cell_thickness, ) geometry = [] layer_to_polygons = component_extended.get_polygons(by_spec=True) for layer, polygons in layer_to_polygons.items(): if layer in layer_to_thickness and layer in layer_to_material: height = layer_to_thickness[layer] if is_3d else mp.inf zmin_um = layer_to_zmin[layer] if is_3d else 0 # center = mp.Vector3(0, 0, (zmin_um + height) / 2) for polygon in polygons: vertices = [mp.Vector3(p[0], p[1], zmin_um) for p in polygon] material_name = layer_to_material[layer] material = get_material(name=material_name) geometry.append( mp.Prism( vertices=vertices, height=height, sidewall_angle=layer_to_sidewall_angle[layer], material=material, # center=center ) ) freqs = 1 / wavelengths fcen = np.mean(freqs) frequency_width = dfcen * fcen # Add source port = component.ports[port_source_name] angle = port.orientation width = port.width + 2 * port_margin size_x = width * abs(np.sin(angle * np.pi / 180)) size_y = width * abs(np.cos(angle * np.pi / 180)) size_x = 0 if size_x < 0.001 else size_x size_y = 0 if size_y < 0.001 else size_y size_z = cell_thickness - 2 * tpml if is_3d else 20 size = [size_x, size_y, size_z] center = port.center.tolist() + [0] # (x, y, z=0) field_monitor_port = component.ports[port_field_monitor_name] field_monitor_point = field_monitor_port.center.tolist() + [0] # (x, y, z=0) sources = [ mp.EigenModeSource( src=mp.GaussianSource(fcen, fwidth=frequency_width), size=size, center=center, eig_band=1, eig_parity=mp.NO_PARITY if is_3d else mp.EVEN_Y + mp.ODD_Z, eig_match_freq=True, ) ] sim = mp.Simulation( resolution=res, cell_size=cell_size, boundary_layers=[mp.PML(tpml)], sources=sources, geometry=geometry, default_material=get_material(name=clad_material), # geometry_center=geometry_center, ) # Add port monitors dict monitors = {} for port_name in component.ports.keys(): port = component.ports[port_name] angle = port.orientation width = port.width + 2 * port_margin size_x = width * abs(np.sin(angle * np.pi / 180)) size_y = width * abs(np.cos(angle * np.pi / 180)) size_x = 0 if size_x < 0.001 else size_x size_y = 0 if size_y < 0.001 else size_y size = mp.Vector3(size_x, size_y, size_z) size = [size_x, size_y, size_z] # if monitor has a source move monitor inwards length = -distance_source_to_monitors if port_name == port_source_name else 0 xy_shifted = move_polar_rad_copy( np.array(port.center), angle=angle * np.pi / 180, length=length ) center = xy_shifted.tolist() + [0] # (x, y, z=0) m = sim.add_mode_monitor(freqs, mp.ModeRegion(center=center, size=size)) m.z = 0 monitors[port_name] = m return dict( sim=sim, cell_size=cell_size, freqs=freqs, monitors=monitors, sources=sources, field_monitor_point=field_monitor_point, port_source_name=port_source_name, )
os.makedirs(dFolder) w = w_actual/a_actual a = 1 hx = hx_actual/a_actual hy = hy_actual/a_actual h = w/2/np.tan(theta*np.pi/180) #################################################################### #sets size of lattice to be 3D; 1by1by1 geometry_lattice = mp.Lattice(size=mp.Vector3(a, w*3, h*3)) #https://meep.readthedocs.io/en/latest/Python_User_Interface/#prism beam = mp.Prism([mp.Vector3(0,-w/2, h/2), mp.Vector3(0,w/2, h/2), mp.Vector3(0,0, -h/2)], a, axis=mp.Vector3(1,0,0), center=None, material=mp.Medium(epsilon=n**2)) #Diamond: n = 2.4063; n^2 = ep_r hole = mp.Ellipsoid(size=[hx, hy, mp.inf], material=mp.Medium(epsilon=1)) geometry = [beam, hole] #Symmetry points k_points = [ mp.Vector3(0,0,0), # Gamma mp.Vector3(0.5,0,0), # X (normalized to a?) ] #how many points to solve for between each specified point above k_points = mp.interpolate(kpt_resolution, k_points)
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))
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, 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_x + 0.7 * Lw, 0, 0) flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy - 2 * dpml_y, 0))) sim.run(until_after_sources=mp.stop_when_fields_decayed( 50, mp.Ez, mon_pt, 1e-9)) res = sim.get_eigenmode_coefficients(flux, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) incident_coeffs = res.alpha incident_flux = mp.get_fluxes(flux)
mp.Vector3(-2 / 3 * r_cen1, 2 / 3 * r_cen1), mp.Vector3(2 / 3 * r_cen1, 4 / 3 * r_cen1) ] vertices_cen2 = [ mp.Vector3(4 / 3 * r_cen2, 2 / 3 * r_cen2), mp.Vector3(2 / 3 * r_cen2, -2 / 3 * r_cen2), mp.Vector3(-2 / 3 * r_cen2, -4 / 3 * r_cen2), mp.Vector3(-4 / 3 * r_cen2, -2 / 3 * r_cen2), mp.Vector3(-2 / 3 * r_cen2, 2 / 3 * r_cen2), mp.Vector3(2 / 3 * r_cen2, 4 / 3 * r_cen2) ] geometry = [ mp.Prism(vertices, center=mp.Vector3(1 / 3, 1 / 3), height=mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices, center=mp.Vector3(1 / 3, 0), height=mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices, center=mp.Vector3(0, -1 / 3), height=mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices, center=mp.Vector3(-1 / 3, -1 / 3), height=mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices, center=mp.Vector3(-1 / 3, 0),
geometry = [] q = miepy.quaternion.from_spherical_coords(0, 0) R = miepy.quaternion.as_rotation_matrix(q) X = W / np.sqrt(2 * (1 - np.cos(2 * np.pi / 3))) vertices = [ meep.Vector3(X, 0, 0), meep.Vector3(-X / 2, np.sqrt(3) / 2 * X, 0), meep.Vector3(-X / 2, -np.sqrt(3) / 2 * X, 0) ] geometry.append( meep.Prism(center=meep.Vector3(0, 0, 0), height=H, vertices=vertices, axis=meep.Vector3(0, 0, 1), material=material)) D = W * 3**.5 / 2 box = [D, D, H] resolution = 1 / (8 * nm) medium = meep.Medium(index=1) fcen, df = meep_ext.freq_data(1 / (400 * nm), 1 / (1000 * nm)) nfreq = 40 polarization = 'x' src_time = meep.GaussianSource(frequency=1.3 / um, fwidth=4.0 / um) if polarization == 'x': source = lambda sim: meep_ext.x_polarized_plane_wave(sim, src_time)
def get_mode_solver_coupler( wg_width: float = 0.5, gap: float = 0.2, wg_widths: Optional[Floats] = None, gaps: Optional[Floats] = None, wg_thickness: float = 0.22, slab_thickness: float = 0.0, ncore: float = 3.47, nclad: float = 1.44, nslab: Optional[float] = None, ymargin: float = 2.0, sz: float = 2.0, resolution: int = 32, nmodes: int = 4, sidewall_angles: Union[Tuple[float, ...], float] = None, # sidewall_taper: int = 1, ) -> mpb.ModeSolver: """Returns a mode_solver simulation. Args: wg_width: wg_width (um) gap: wg_widths: list or tuple of waveguide widths. gaps: list or tuple of waveguide gaps. wg_thickness: wg height (um) slab_thickness: thickness for the waveguide slab ncore: core material refractive index nclad: clad material refractive index nslab: Optional slab material refractive index. Defaults to ncore. ymargin: margin in y. sz: simulation region thickness (um) resolution: resolution (pixels/um) nmodes: number of modes sidewall_angles: waveguide sidewall angle (radians), tapers from wg_width at top of slab, upwards, to top of waveguide :: _____________________________________________________ | | | widths[0] widths[1] | <----------> gaps[0] <----------> | ___________ <-------------> ___________ _ | | | | | | sz|_____| |_______________| |_____| | | wg_thickness |slab_thickness | |___________________________________________________| | |<---> <---> |ymargin ymargin |____________________________________________________ <---------------------------------------------------> sy """ wg_widths = wg_widths or (wg_width, wg_width) gaps = gaps or (gap, ) material_core = mp.Medium(index=ncore) material_clad = mp.Medium(index=nclad) material_slab = mp.Medium(index=nslab or ncore) # Define the computational cell. We'll make x the propagation direction. # the other cell sizes should be big enough so that the boundaries are # far away from the mode field. sy = np.sum(wg_widths) + np.sum(gaps) + 2 * ymargin geometry_lattice = mp.Lattice(size=mp.Vector3(0, sy, sz)) geometry = [] y = -sy / 2 + ymargin gaps = list(gaps) + [0] for i, wg_width in enumerate(wg_widths): if sidewall_angles: geometry.append( mp.Prism( vertices=[ mp.Vector3(y=y, z=slab_thickness), mp.Vector3(y=y + wg_width, z=slab_thickness), mp.Vector3(x=1, y=y + wg_width, z=slab_thickness), mp.Vector3(x=1, y=y, z=slab_thickness), ], height=wg_thickness - slab_thickness, center=mp.Vector3( y=y + wg_width / 2, z=slab_thickness + (wg_thickness - slab_thickness) / 2, ), # If only 1 angle is specified, use it for all waveguides sidewall_angle=sidewall_angles if len( np.unique(sidewall_angles)) == 1 else sidewall_angles[i], # axis=mp.Vector3(z=sidewall_taper), material=material_core, )) else: geometry.append( mp.Block( size=mp.Vector3(mp.inf, wg_width, wg_thickness), material=material_core, center=mp.Vector3(y=y + wg_width / 2, z=wg_thickness / 2), )) y += gaps[i] + wg_width # define the 2D blocks for the strip and substrate geometry += [ mp.Block( size=mp.Vector3(mp.inf, mp.inf, slab_thickness), material=material_slab, center=mp.Vector3(z=slab_thickness / 2), ), ] # The k (i.e. beta, i.e. propagation constant) points to look at, in # units of 2*pi/um. We'll look at num_k points from k_min to k_max. num_k = 9 k_min = 0.1 k_max = 3.0 k_points = mp.interpolate(num_k, [mp.Vector3(k_min), mp.Vector3(k_max)]) # Increase this to see more modes. (The guided ones are the ones below the # light line, i.e. those with frequencies < kmag / 1.45, where kmag # is the corresponding column in the output if you grep for "freqs:".) # use this prefix for output files wg_widths_str = "_".join([str(i) for i in wg_widths]) gaps_str = "_".join([str(i) for i in gaps]) filename_prefix = ( tmp / f"coupler_{wg_widths_str}_{gaps_str}_{wg_thickness}_{slab_thickness}") mode_solver = mpb.ModeSolver( geometry_lattice=geometry_lattice, geometry=geometry, k_points=k_points, resolution=resolution, num_bands=nmodes, filename_prefix=str(filename_prefix), default_material=material_clad, ) mode_solver.nmodes = nmodes mode_solver.info = dict( wg_widths=wg_widths, gaps=gaps, wg_thickness=wg_thickness, slab_thickness=slab_thickness, ncore=ncore, nclad=nclad, sy=sy, sz=sz, resolution=resolution, nmodes=nmodes, ) return mode_solver
def get_mode_solver_rib( wg_width: float = 0.45, wg_thickness: float = 0.22, slab_thickness: float = 0.0, ncore: float = 3.47, nclad: float = 1.44, nslab: Optional[float] = None, sy: float = 2.0, sz: float = 2.0, resolution: int = 32, nmodes: int = 4, sidewall_angle: float = None, # sidewall_taper: int = 1, ) -> mpb.ModeSolver: """Returns a mode_solver simulation. Args: wg_width: wg_width (um) wg_thickness: wg height (um) slab_thickness: thickness for the waveguide slab ncore: core material refractive index nclad: clad material refractive index nslab: Optional slab material refractive index. Defaults to ncore. sy: simulation region width (um) sz: simulation region height (um) resolution: resolution (pixels/um) nmodes: number of modes sidewall_angle: waveguide sidewall angle (radians), tapers from wg_width at top of slab, upwards, to top of waveguide :: . = origin __________________________ | | | width | <----------> | ___________ _ _ _ | | | | sz|_____| |_______| | | wg_thickness |slab_thickness | |___________._____________| | | |__________________________ <------------------------> sy """ material_core = mp.Medium(index=ncore) material_clad = mp.Medium(index=nclad) material_slab = mp.Medium(index=nslab or ncore) # Define the computational cell. We'll make x the propagation direction. # the other cell sizes should be big enough so that the boundaries are # far away from the mode field. geometry_lattice = mp.Lattice(size=mp.Vector3(0, sy, sz)) geometry = [] # define the 2d blocks for the strip and substrate if sidewall_angle: geometry.append( mp.Prism( vertices=[ mp.Vector3(y=-wg_width / 2, z=slab_thickness), mp.Vector3(y=wg_width / 2, z=slab_thickness), mp.Vector3(x=1, y=wg_width / 2, z=slab_thickness), mp.Vector3(x=1, y=-wg_width / 2, z=slab_thickness), ], height=wg_thickness - slab_thickness, center=mp.Vector3(z=slab_thickness + (wg_thickness - slab_thickness) / 2, ), # If only 1 angle is specified, use it for all waveguides sidewall_angle=sidewall_angle, # axis=mp.Vector3(z=sidewall_taper), material=material_core, )) else: geometry.append( mp.Block( size=mp.Vector3(mp.inf, wg_width, wg_thickness), material=material_core, center=mp.Vector3(z=wg_thickness / 2), )) # uncomment this for not oxide cladded waveguides # geometry.append( # mp.Block( # size=mp.Vector3(mp.inf, mp.inf, 0.5 * (sz - wg_thickness)), # center=mp.Vector3(z=0.25 * (sz + wg_thickness)), # material=material_clad, # ), # ) geometry += [ mp.Block( size=mp.Vector3(mp.inf, mp.inf, slab_thickness), material=material_slab, center=mp.Vector3(z=slab_thickness / 2), ), ] # The k (i.e. beta, i.e. propagation constant) points to look at, in # units of 2*pi/um. We'll look at num_k points from k_min to k_max. num_k = 9 k_min = 0.1 k_max = 3.0 k_points = mp.interpolate(num_k, [mp.Vector3(k_min), mp.Vector3(k_max)]) # Increase this to see more modes. (The guided ones are the ones below the # light line, i.e. those with frequencies < kmag / 1.45, where kmag # is the corresponding column in the output if you grep for "freqs:".) # use this prefix for output files filename_prefix = tmp / f"rib_{wg_width}_{wg_thickness}_{slab_thickness}" mode_solver = mpb.ModeSolver( geometry_lattice=geometry_lattice, geometry=geometry, k_points=k_points, resolution=resolution, num_bands=nmodes, default_material=material_clad, filename_prefix=str(filename_prefix), ) mode_solver.nmodes = nmodes mode_solver.info = dict( wg_width=wg_width, wg_thickness=wg_thickness, slab_thickness=slab_thickness, ncore=ncore, nclad=nclad, sy=sy, sz=sz, resolution=resolution, nmodes=nmodes, ) return mode_solver
mp.Vector3(-r_cen1, 1/3**0.5*r_cen1)] vertices_cen2 = [mp.Vector3( 0, 2/(3**0.5)*r_cen2), mp.Vector3( r_cen2, 1/3**0.5*r_cen2), mp.Vector3( r_cen2, -1/3**0.5*r_cen2), mp.Vector3( 0, -2/3**0.5*r_cen2), mp.Vector3(-r_cen2, -1/3**0.5*r_cen2), mp.Vector3(-r_cen2, 1/3**0.5*r_cen2)] geometry = [] for i in range(2): for j in range(N): center = (-1/2+i+1/2*(j%2),(-N+j)*3**0.5/2) geometry +=[mp.Prism(vertices_air, center=mp.Vector3( center[0]+1/3,center[1]), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]-1/3,center[1]), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]+1/6,center[1]+3**.5/6), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]-1/6,center[1]+3**.5/6), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]+1/6,center[1]-3**.5/6), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]-1/6,center[1]-3**.5/6), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_cen1,center=mp.Vector3( center[0] + 0,center[1]+ 0), height = mp.inf, material=mp.Medium(epsilon=eps_outer_1)), mp.Prism(vertices_cen2,center=mp.Vector3( center[0] + 0,center[1]+ 0), height = mp.inf, material=mp.Medium(epsilon=eps_inner_1))] for i in range(2): for j in range(N): center = (-1/2+i+1/2*(j%2),(j)*3**0.5/2) geometry +=[mp.Prism(vertices_air, center=mp.Vector3( center[0]+1/3,center[1]), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]-1/3,center[1]), height = mp.inf, material=mp.Medium(epsilon=1)), mp.Prism(vertices_air, center=mp.Vector3( center[0]+1/6,center[1]+3**.5/6), height = mp.inf, material=mp.Medium(epsilon=1)),
mp.Vector3(r_air, 1 / 3**0.5 * r_air) ] vertices_ll = [ mp.Vector3(0, 0), mp.Vector3(0, 2 / (3**0.5) * r_air), mp.Vector3(-r_air, 1 / 3**0.5 * r_air), mp.Vector3(-r_air, -1 / 3**0.5 * r_air) ] for i in range(2): for j in range(N): center = (-1 / 2 + i + 1 / 2 * (j % 2), (-N + j) * 3**0.5 / 2) geometry += [ mp.Prism(vertices_hex, center=mp.Vector3(center[0], center[1]), height=mp.inf, material=mp.Medium(epsilon=eps_cen_1)), mp.Prism(vertices_l, center=mp.Vector3( center[0], center[1] - 3**.5 / 3 + 1 / 3**0.5 * r_air), height=mp.inf, material=mp.Medium(epsilon=eps_back_1)), mp.Prism(vertices_r, center=mp.Vector3( center[0], center[1] + 3**.5 / 3 - 1 / 3**0.5 * r_air), height=mp.inf, material=mp.Medium(epsilon=eps_back_1)), mp.Prism(vertices_ul, center=mp.Vector3(
def main(args): """ Args: * **fields** (boolean): If true, outputs the fields at the relevant waveguide cross-sections (top-down and side-view) * **output_directory** (string): Name of the output directory (for storing the fields) * **eps_input_file** (string): Name of the hdf5 file that defines the geometry through prisms * **input_pol** (string): Either "TE", or "TM", corresponding to the desired input mode. Defaults to "TE" * **res** (int): Resolution of the MEEP simulation * **nfreq** (int): The number of wavelength points to record in the transmission/reflection spectra * **input_direction** (1 or -1): Direction of propagation for the input eigenmode. If +1, goes in +x, else if -1, goes in -x. Defaults to +1. * **dpml** (float): Length (in microns) of the perfectly-matched layer (PML) at simulation boundaries. Defaults to 0.5 um. * **wl_center** (float): Center wavelength (in microns) * **wl_span** (float): Wavelength span (determines the pulse width) * **port_vcenter** (float): Vertical center of the waveguide * **port_height** (float): Height of the port cross-section (flux plane) * **port_width** (float): Width of the port cross-section (flux plane) * **source_offset** (float): Offset (in x-direction) between reflection monitor and source. Defaults to 0.1 um. * **center_x** (float): x-coordinate of the center of the simulation region * **center_y** (float): y-coordinate of the center of the simulation region * **center_z** (float): z-coordinate of the center of the simulation region * **sx** (float): Size of the simulation region in x-direction * **sx** (float): Size of the simulation region in y-direction * **sz** (float): Size of the simulation region in z-direction * **port_coords** (list): List of the port coordinates (variable length), in the format [x1, y1, x2, y2, x3, y3, ...] (*must* be even) """ #Boolean inputs fields = args.fields #String inputs output_directory = args.output_directory eps_input_file = args.eps_input_file input_pol = args.input_pol #Int inputs res = args.res nfreq = args.nfreq input_direction = args.input_direction #Float inputs dpml = args.dpml wl_center = args.wl_center wl_span = args.wl_span port_vcenter = args.port_vcenter port_height = args.port_height port_width = args.port_width source_offset = args.source_offset center_x, center_y, center_z = args.center_x, args.center_y, args.center_z sx, sy, sz = args.sx, args.sy, args.sz #List of floats port_coords = [float(x) for x in args.port_coords[0].split(" ")] ports = [(port_coords[2 * i], port_coords[2 * i + 1]) for i in range(int(len(port_coords) / 2))] if input_pol == "TE": parity = mp.ODD_Z elif input_pol == "TM": parity = mp.EVEN_Z else: raise ValueError( "Warning! Improper value of 'input_pol' was passed to mcts.py (input_pol given =" + str(input_pol) + ")") if len(port_coords) % 2 != 0: raise ValueError( "Warning! Improper port_coords was passed to `meep_compute_transmission_spectra`. Must be even number of port_coords in [x1, y1, x2, y2, ..] format." ) # Setup the simulation geometries prism_objects = get_prism_objects(eps_input_file) geometry = [] for p in prism_objects: # print('vertices='+str(p['vlist'])) # print('axis = '+str(mp.Vector3(0,1,0))) # print('height = '+str(p['height'])) print('material = ' + str(p['eps'])) # print('\n') geometry.append( mp.Prism(p['vlist'], axis=mp.Vector3(0, 1, 0), height=p['height'], material=mp.Medium(epsilon=p['eps']))) # Setup the simulation sources fmax = 1.0 / (wl_center - 0.5 * wl_span) fmin = 1.0 / (wl_center + 0.5 * wl_span) fcen = (fmax + fmin) / 2.0 df = fmax - fmin if abs(abs(input_direction) - 1) > 1E-6: print(input_direction) raise ValueError("Warning! input_direction is not +1 or -1.") # Use first port in 'ports' as the location of the eigenmode source sources = [ mp.EigenModeSource( src=mp.GaussianSource(fcen, fwidth=df, cutoff=30), component=mp.ALL_COMPONENTS, size=mp.Vector3(0, 3 * float(port_height), 3 * float(port_width)), center=mp.Vector3(ports[0][0] + source_offset - center_x, float(port_vcenter) - center_y, ports[0][1] - center_z), eig_match_freq=True, eig_parity=parity, eig_kpoint=mp.Vector3(float(input_direction) * wl_center, 0, 0), eig_resolution=2 * res if res > 16 else 32, ) ] # Setup the simulation sim = mp.Simulation(cell_size=mp.Vector3(sx, sy, sz), boundary_layers=[mp.PML(dpml)], geometry=geometry, sources=sources, dimensions=3, resolution=res, filename_prefix=False) """ Add power flux monitors """ print("ADDING FLUX MONITORS") flux_plane_objects = [] for port in ports: flux_region = mp.FluxRegion(size=mp.Vector3(0, float(port_height), float(port_width)), center=mp.Vector3( float(port[0]) - center_x, float(port_vcenter) - center_y, float(port[1]) - center_z)) fpo = sim.add_flux(fcen, df, nfreq, flux_region) flux_plane_objects.append(fpo) sim.use_output_directory(str(output_directory)) """ Run the simulation """ """ Monitor the amplitude in the center of the structure """ decay_pt = mp.Vector3(0, port_vcenter, 0) sv = mp.Volume(size=mp.Vector3(sx, sy, 0), center=mp.Vector3(0, 0, 0)) tv = mp.Volume(size=mp.Vector3(sx, 0, sz), center=mp.Vector3(0, port_vcenter, 0)) print("RUNNING SIMULATION") if fields: sim.run(mp.at_beginning(mp.output_epsilon), mp.at_beginning( mp.with_prefix(str("sideview-"), mp.in_volume(sv, mp.output_epsilon))), mp.at_beginning( mp.with_prefix(str("topview-"), mp.in_volume(tv, mp.output_epsilon))), mp.at_every( 1.0, mp.to_appended(str("ez-sideview"), mp.in_volume(sv, mp.output_efield_z))), mp.at_every( 1.0, mp.to_appended(str("ez-topview"), mp.in_volume(tv, mp.output_efield_z))), until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ez, decay_pt, 1e-4)) else: sim.run(until_after_sources=mp.stop_when_fields_decayed( 20, mp.Ez, decay_pt, 1e-4)) sim.display_fluxes(*flux_plane_objects) print("FINISHED SIMULATION")
def geometry(self): return [ mp.Prism([Vector3(*xy) for xy in p.exterior.coords[:-1]], mp.inf, material=self.shape_material) for p in self.polygons ]
def sim(queryrow, src_angle, nm_val, src_pol): global resolution result_df = pd.DataFrame(columns=result.columns) dpml = 1.0 # PML length dair = 4.0 # padding length between PML and grating dsub = 3.0 # substrate thickness d = queryrow["period"] # grating period h = queryrow["height"] # grating height g = queryrow["gap"] # grating gap theta_1 = math.radians(queryrow["theta_1"]) # grating sidewall angle #1 theta_2 = math.radians(queryrow["theta_2"]) # grating sidewall angle #2 transmittance = 0 sx = dpml + dair + h + dsub + dpml sy = d cell_size = mp.Vector3(sx, sy, 0) pml_layers = [mp.Absorber(thickness=dpml, direction=mp.X)] wvl = 0.5 # center wavelength fcen = 1 / wvl # center frequency df = 0.05 * fcen # frequency width ng = 1.716 # episulfide refractive index @ 0.532 um glass = mp.Medium(index=ng) if src_pol == 1: src_cmpt = mp.Ez eig_parity = mp.ODD_Z elif src_pol == 2: src_cmpt = mp.Hz eig_parity = mp.EVEN_Z else: sys.exit("error: src_pol={} is invalid".format(args.src_pol)) # rotation angle of incident planewave source; CCW about Z axis, 0 degrees along +X axis theta_src = math.radians(src_angle) # k (in source medium) with correct length (plane of incidence: XY) k = mp.Vector3(math.cos(theta_src), math.sin(theta_src), 0).scale(fcen) if theta_src == 0: k = mp.Vector3(0, 0, 0) def pw_amp(k, x0): def _pw_amp(x): return cmath.exp(1j * 2 * math.pi * k.dot(x + x0)) return _pw_amp src_pt = mp.Vector3(-0.5 * sx + dpml + 0.2 * dair, 0, 0) sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=src_cmpt, center=src_pt, size=mp.Vector3(0, sy, 0), amp_func=pw_amp(k, src_pt)) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, k_point=k, sources=sources) refl_pt = mp.Vector3(-0.5 * sx + dpml + 0.7 * dair, 0, 0) refl_flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=refl_pt, size=mp.Vector3(0, sy, 0))) sim.run(until_after_sources=100) input_flux = mp.get_fluxes(refl_flux) input_flux_data = sim.get_flux_data(refl_flux) 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.Prism(material=glass, height=mp.inf, vertices=[ mp.Vector3(0.5 * sx - dpml - dsub, 0.5 * sy, 0), mp.Vector3(0.5 * sx - dpml - dsub - h, 0.5 * sy - h * math.tan(theta_2), 0), mp.Vector3(0.5 * sx - dpml - dsub - h, -0.5 * sy + g - h * math.tan(theta_1), 0), mp.Vector3(0.5 * sx - dpml - dsub, -0.5 * sy + g, 0) ]) ] sim = mp.Simulation(resolution=resolution, cell_size=cell_size, boundary_layers=pml_layers, k_point=k, sources=sources, geometry=geometry) refl_flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=refl_pt, size=mp.Vector3(0, sy, 0))) sim.load_minus_flux_data(refl_flux, input_flux_data) tran_pt = mp.Vector3(0.5 * sx - dpml - 0.5 * dsub, 0, 0) tran_flux = sim.add_flux( fcen, 0, 1, mp.FluxRegion(center=tran_pt, size=mp.Vector3(0, sy, 0))) sim.run(until_after_sources=500) kdom_tol = 1e-2 angle_tol = 1e-6 Rsum = 0 Tsum = 0 if theta_src == 0: nm_r = int(0.5 * (np.floor((fcen - k.y) * d) - np.ceil( (-fcen - k.y) * d))) # number of reflected orders res = sim.get_eigenmode_coefficients(refl_flux, range(1, nm_r + 1), eig_parity=eig_parity + mp.EVEN_Y) r_coeffs = res.alpha r_kdom = res.kdom for nm in range(nm_r): if nm != nm_val: continue if r_kdom[nm].x > kdom_tol: r_angle = np.sign(r_kdom[nm].y) * math.acos( r_kdom[nm].x / fcen) if ( r_kdom[nm].x % fcen > angle_tol) else 0 Rmode = abs(r_coeffs[nm, 0, 1])**2 / input_flux[0] print("refl: (even_y), {}, {:.2f}, {:.8f}".format( nm, math.degrees(r_angle), Rmode)) Rsum += Rmode res = sim.get_eigenmode_coefficients(refl_flux, range(1, nm_r + 1), eig_parity=eig_parity + mp.ODD_Y) r_coeffs = res.alpha r_kdom = res.kdom for nm in range(nm_r): if nm != nm_val: continue if r_kdom[nm].x > kdom_tol: r_angle = np.sign(r_kdom[nm].y) * math.acos( r_kdom[nm].x / fcen) if ( r_kdom[nm].x % fcen > angle_tol) else 0 Rmode = abs(r_coeffs[nm, 0, 1])**2 / input_flux[0] print("refl: (odd_y), {}, {:.2f}, {:.8f}".format( nm, math.degrees(r_angle), Rmode)) Rsum += Rmode nm_t = int(0.5 * (np.floor((fcen * ng - k.y) * d) - np.ceil( (-fcen * ng - k.y) * d))) # number of transmitted orders res = sim.get_eigenmode_coefficients(tran_flux, range(1, nm_t + 1), eig_parity=eig_parity + mp.EVEN_Y) t_coeffs = res.alpha t_kdom = res.kdom for nm in range(nm_t): if nm != nm_val: continue if t_kdom[nm].x > kdom_tol: t_angle = np.sign(t_kdom[nm].y) * math.acos( t_kdom[nm].x / (ng * fcen)) if ( t_kdom[nm].x % ng * fcen > angle_tol) else 0 Tmode = abs(t_coeffs[nm, 0, 0])**2 / input_flux[0] transmittance = transmittance + Tmode Tsum += Tmode res = sim.get_eigenmode_coefficients(tran_flux, range(1, nm_t + 1), eig_parity=eig_parity + mp.ODD_Y) t_coeffs = res.alpha t_kdom = res.kdom for nm in range(nm_t): if nm != nm_val: continue if t_kdom[nm].x > kdom_tol: t_angle = np.sign(t_kdom[nm].y) * math.acos( t_kdom[nm].x / (ng * fcen)) if ( t_kdom[nm].x % ng * fcen > angle_tol) else 0 Tmode = abs(t_coeffs[nm, 0, 0])**2 / input_flux[0] transmittance = transmittance + Tmode Tsum += Tmode else: nm_r = int(np.floor((fcen - k.y) * d) - np.ceil( (-fcen - k.y) * d)) # number of reflected orders res = sim.get_eigenmode_coefficients(refl_flux, range(1, nm_r + 1), eig_parity=eig_parity) r_coeffs = res.alpha r_kdom = res.kdom for nm in range(nm_r): if nm != nm_val: continue if r_kdom[nm].x > kdom_tol: r_angle = np.sign(r_kdom[nm].y) * math.acos( r_kdom[nm].x / fcen) if ( r_kdom[nm].x % fcen > angle_tol) else 0 Rmode = abs(r_coeffs[nm, 0, 1])**2 / input_flux[0] Rsum += Rmode nm_t = int( np.floor((fcen * ng - k.y) * d) - np.ceil( (-fcen * ng - k.y) * d)) # number of transmitted orders res = sim.get_eigenmode_coefficients(tran_flux, range(1, nm_t + 1), eig_parity=eig_parity) t_coeffs = res.alpha t_kdom = res.kdom for nm in range(nm_t): if nm != nm_val: continue if t_kdom[nm].x > kdom_tol: t_angle = np.sign(t_kdom[nm].y) * math.acos( t_kdom[nm].x / (ng * fcen)) if ( t_kdom[nm].x % ng * fcen > angle_tol) else 0 Tmode = abs(t_coeffs[nm, 0, 0])**2 / input_flux[0] transmittance = transmittance + Tmode return transmittance
[point0.y, cpoint0.y, cpoint1.y, point1.y]]) curve = bezier.Curve(nodes, degree=3) points1 = curve.evaluate_multi(factor) new_points = points1.transpose() wg = waveguide.Waveguide(new_points, width) poly2 = wg.poly() tmp_poly = np.asarray(poly) # plt.plot(tmp_poly[:,0],tmp_poly[:,1],'-') vertices1 = [mp.Vector3(tmp[0], tmp[1]) for tmp in poly] vertices2 = [mp.Vector3(tmp[0], tmp[1]) for tmp in poly2] #vertices.extend([tmp for tmp in reversed(tmp_ver)]) geometry = [ mp.Prism(vertices1, height=mp.inf, center=mp.Vector3(0.0, 2.0), material=mp.Medium(epsilon=12)), mp.Prism(vertices2, height=mp.inf, center=mp.Vector3(1.0, -2.0), material=mp.Medium(epsilon=12)) ] # sources = [mp.Source(mp.ContinuousSource(frequency=0.15), # component=mp.Ez, # center=mp.Vector3(-7,0))] sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources,
def test_linear_taper_2d(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), 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)) res = sim.get_eigenmode_coefficients(flux, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) incident_coeffs = res.alpha 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)) res = sim.get_eigenmode_coefficients(refl_flux, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y) coeffs = res.alpha 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)
def init(self, no_bend=False, gdsii=False): sx = 16 sy = 32 cell = mp.Vector3(sx, sy, 0) pad = 4 w = 1 wvg_ycen = -0.5 * (sy - w - (2 * pad)) wvg_xcen = 0.5 * (sx - w - (2 * pad)) height = mp.inf data_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), 'data')) gdsii_file = os.path.join(data_dir, 'bend-flux.gds') if no_bend: if gdsii: geometry = mp.get_GDSII_prisms(mp.Medium(epsilon=12), gdsii_file, 1, 0, height) else: no_bend_vertices = [ mp.Vector3(-0.5 * sx - 5, wvg_ycen - 0.5 * w), mp.Vector3(+0.5 * sx + 5, wvg_ycen - 0.5 * w), mp.Vector3(+0.5 * sx + 5, wvg_ycen + 0.5 * w), mp.Vector3(-0.5 * sx - 5, wvg_ycen + 0.5 * w) ] geometry = [ mp.Prism(no_bend_vertices, height, material=mp.Medium(epsilon=12)) ] else: if gdsii: geometry = mp.get_GDSII_prisms(mp.Medium(epsilon=12), gdsii_file, 2, 0, height) else: bend_vertices = [ mp.Vector3(-0.5 * sx, wvg_ycen - 0.5 * w), mp.Vector3(wvg_xcen + 0.5 * w, wvg_ycen - 0.5 * w), mp.Vector3(wvg_xcen + 0.5 * w, 0.5 * sy), mp.Vector3(wvg_xcen - 0.5 * w, 0.5 * sy), mp.Vector3(wvg_xcen - 0.5 * w, wvg_ycen + 0.5 * w), mp.Vector3(-0.5 * sx, wvg_ycen + 0.5 * w) ] geometry = [ mp.Prism(bend_vertices, height, material=mp.Medium(epsilon=12)) ] fcen = 0.15 df = 0.1 sources = [ mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ez, center=mp.Vector3(1 + (-0.5 * sx), wvg_ycen), size=mp.Vector3(0, w)) ] pml_layers = [mp.PML(1.0)] resolution = 10 nfreq = 100 self.sim = mp.Simulation(cell_size=cell, boundary_layers=pml_layers, geometry=geometry, sources=sources, resolution=resolution) if no_bend: fr = mp.FluxRegion(center=mp.Vector3((sx / 2) - 1.5, wvg_ycen), size=mp.Vector3(0, w * 2)) else: fr = mp.FluxRegion(center=mp.Vector3(wvg_xcen, (sy / 2) - 1.5), size=mp.Vector3(w * 2, 0)) self.trans = self.sim.add_flux(fcen, df, nfreq, fr) refl_fr = mp.FluxRegion(center=mp.Vector3((-0.5 * sx) + 1.5, wvg_ycen), size=mp.Vector3(0, w * 2)) self.refl = self.sim.add_flux(fcen, df, nfreq, refl_fr) if no_bend: self.pt = mp.Vector3((sx / 2) - 1.5, wvg_ycen) else: self.pt = mp.Vector3(wvg_xcen, (sy / 2) - 1.5)
cell_size = mp.Vector3(2, 2, 2) # A hexagon is defined as a prism with six vertices centered on the origin vertices = [ mp.Vector3(-1, 0), mp.Vector3(-0.5, math.sqrt(3) / 2), mp.Vector3(0.5, math.sqrt(3) / 2), mp.Vector3(1, 0), mp.Vector3(0.5, -math.sqrt(3) / 2), mp.Vector3(-0.5, -math.sqrt(3) / 2) ] geometry = [ mp.Prism(vertices, height=1.0, material=mp.Medium(index=3.5)), mp.Cone(radius=1.0, radius2=0.1, height=2.0, material=mp.air) ] sim = mp.Simulation(resolution=50, cell_size=cell_size, geometry=geometry) #%% sim.init_sim() x, y, z, *more = sim.get_array_metadata( ) # (x,y,z,w) = sim.get_array_metadata() # Returns coordinates and interpolation weights of the fields :) del more eps_data = sim.get_epsilon()
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 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=100) ################################################## # 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 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()
def build_geom(sx, sy, dsub, gp, gh, lw, tr, br, sa, material): tw = gh / np.tan( sa * 2 * np.pi / 360) # part to subtract from top of grating width a = (gh - tr) / np.tan( sa * 2 * np.pi / 360) # intermediate for vertex calculation #vertices for trapezoids approximating grating cross-section vtx = [ mp.Vector3(-0.5 * lw - 0.5 * a + gp / 2, -1 * (-0.5 * gh), 0), mp.Vector3(0.5 * lw + 0.5 * a + gp / 2, -1 * (-0.5 * gh), 0), mp.Vector3(0.5 * lw - 0.5 * a + gp / 2, -1 * (0.5 * gh - tr), 0), mp.Vector3(-0.5 * lw + 0.5 * a + gp / 2, -1 * (0.5 * gh - tr), 0) ] vtx2 = [ mp.Vector3(-0.5 * lw - 0.5 * a - gp / 2, -1 * (-0.5 * gh), 0), mp.Vector3(0.5 * lw + 0.5 * a - gp / 2, -1 * (-0.5 * gh), 0), mp.Vector3(0.5 * lw - 0.5 * a - gp / 2, -1 * (0.5 * gh - tr), 0), mp.Vector3(-0.5 * lw + 0.5 * a - gp / 2, -1 * (0.5 * gh - tr), 0) ] #rounded corners for top of trapezoids c1 = mp.Cylinder(radius=tr, height=mp.inf, axis=mp.Vector3(0, 0, 1), center=mp.Vector3(-gp / 2 - lw / 2 + tw / 2 + tr, -1 * (-sy / 2 + dsub + gh - tr), 0), material=material) c2 = mp.Cylinder(radius=tr, height=mp.inf, axis=mp.Vector3(0, 0, 1), center=mp.Vector3(-gp / 2 + lw / 2 - tw / 2 - tr, -1 * (-sy / 2 + dsub + gh - tr), 0), material=material) c3 = mp.Cylinder(radius=tr, height=mp.inf, axis=mp.Vector3(0, 0, 1), center=mp.Vector3(gp / 2 - lw / 2 + tw / 2 + tr, -1 * (-sy / 2 + dsub + gh - tr), 0), material=material) c4 = mp.Cylinder(radius=tr, height=mp.inf, axis=mp.Vector3(0, 0, 1), center=mp.Vector3(gp / 2 + lw / 2 - tw / 2 - tr, -1 * (-sy / 2 + dsub + gh - tr), 0), material=material) #blocks for top of trapezoids inbetween rounded corners b1 = mp.Block(center=mp.Vector3(-gp / 2, -1 * (-sy / 2 + dsub + gh - tr / 2)), size=mp.Vector3(lw - tw - 2 * tr, tr, mp.inf), material=material) b2 = mp.Block(center=mp.Vector3(gp / 2, -1 * (-sy / 2 + dsub + gh - tr / 2)), size=mp.Vector3(lw - tw - 2 * tr, tr, mp.inf), material=material) #ellipsoid cutout to make bottom of grating round e1 = mp.Ellipsoid(center=mp.Vector3(0, -1 * (-sy / 2 + dsub), 0), size=mp.Vector3(gp - lw - a, br, mp.inf), material=mp.Medium(epsilon=1)) e2 = mp.Ellipsoid(center=mp.Vector3(-gp, -1 * (-sy / 2 + dsub), 0), size=mp.Vector3(gp - lw - a, br, mp.inf), material=mp.Medium(epsilon=1)) e3 = mp.Ellipsoid(center=mp.Vector3(gp, -1 * (-sy / 2 + dsub), 0), size=mp.Vector3(gp - lw - a, br, mp.inf), material=mp.Medium(epsilon=1)) geometry = [ mp.Block(material=material, size=mp.Vector3(sx, dsub, mp.inf), center=mp.Vector3(0, -1 * (-0.5 * sy + 0.5 * dsub), 0)), mp.Prism(vtx, height=mp.inf, center=mp.Vector3(0, -1 * (-0.5 * sy + dsub + 0.5 * gh), 0), material=material), mp.Prism(vtx2, height=mp.inf, center=mp.Vector3(0, -1 * (-0.5 * sy + dsub + 0.5 * gh), 0), material=material), c1, c2, c3, c4, b1, b2, e1, e2, e3 ] return geometry
width1 = 0.5 width2 = 1 length = 4 span = 6 vertices = [ mp.Vector3(-length/2-span,width1/2), mp.Vector3(-length/2,width1/2), mp.Vector3(length/2,width2/2), mp.Vector3(length/2+span,width2/2), mp.Vector3(length/2+span,-width2/2), mp.Vector3(length/2,-width2/2), mp.Vector3(-length/2,-width1/2), mp.Vector3(-length/2-span,-width1/2) ] 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
# A hexagon is defined as a prism with six vertices centered on the origin vertices = [ mp.Vector3(-1, 0), mp.Vector3(-0.5, math.sqrt(3) / 2), mp.Vector3(0.5, math.sqrt(3) / 2), mp.Vector3(1, 0), mp.Vector3(0.5, -math.sqrt(3) / 2), mp.Vector3(-0.5, -math.sqrt(3) / 2) ] geometry = [ #mp.Block(center=mp.Vector3(0,0,0), size=cell_size, material=mp.air), mp.Prism(vertices, height=1.9, material=mp.metal, center=mp.Vector3(0, 0, 0)) ] sim = mp.Simulation(resolution=50, cell_size=cell_size, geometry=geometry) sim.init_sim() eps_data = sim.get_epsilon() #eps_data = sim.get_array() #sim.plot3D(eps_data) #from mayavi import mlab #s = mlab.contour3d() #mlab.show()