def __init__( self, shape: goos.Shape, region: goos.Box3d, mesh: simspace.MeshModel, wavelength: float, background: goos.material.Material = None, simulation_symmetry: List[int] = None, num_points_per_arclen: float = 0.084, ) -> None: super().__init__(shape) self._edge_coords = simspace.create_edge_coords( region, mesh.dx, simulation_symmetry) if background: bg_eps = goos.material.get_material(background).permittivity( wavelength) else: bg_eps = 0 self._grid = gridlock.Grid(self._edge_coords, ext_dir=gridlock.Direction.z, initial=bg_eps, num_grids=3) self._render_params = RenderParams( wlen=wavelength, pts_per_arclen=num_points_per_arclen)
def __init__(self, params: optplan.SimulationSpace, filepath: str): if params.mesh.type != "uniform": raise ValueError("Non-uniform meshing not yet supported.") # Setup the grid. self._dx = params.mesh.dx self._edge_coords = _create_edge_coords(params.sim_region, self._dx) self._ext_dir = gridlock.Direction.z # Currently always extrude in z. # TODO(logansu): Factor out grid functionality and drawing. # Create a grid object just so we can calculate dxes. self._grid = gridlock.Grid( self._edge_coords, ext_dir=self._ext_dir, num_grids=3) self._pml_layers = params.pml_thickness self._eps_bg = params.eps_bg self._eps_fg = params.eps_fg self._selmat_type = params.selection_matrix_type self._filepath = filepath # Cache the simulation space instances since they are expensive to make. self._cache = {} # TODO(logansu): Remove this hack. # Call itself to set `self._design_area`. self.__call__(1500)
def eval(self, grid: gridlock.Grid, params: RenderParams): # Draw a cuboid in the original grid to overwrite any shapes in the # shape region, but draw the continuous permittivity on an extra # grid. grid.draw_cuboid(self.shape.pos, self.shape.extents, self.shape.material.permittivity(params.wlen)) new_grid = gridlock.Grid(grid.exyz, ext_dir=grid.ext_dir, initial=0, num_grids=3) contrast = self.shape.material2.permittivity( params.wlen) - self.shape.material.permittivity(params.wlen) shape_coords = self.shape.get_edge_coords() for axis in range(3): grid_coords = new_grid.shifted_exyz( axis, which_grid=gridlock.GridType.COMP) # Remove ghost cells at the end. grid_coords = [ c if c.shape == co.shape else c[:-1] for c, co in zip(grid_coords, grid.exyz) ] mat = get_rendering_matrix(shape_coords, grid_coords) grid_vals = contrast * mat @ self.shape.array.flatten() new_grid.grids[axis] = np.reshape(grid_vals, new_grid.grids[axis].shape) return new_grid
def _create_grid(eps_spec: optplan.EpsilonSpec, edge_coords: fdfd_tools.EdgeCoords, wlen: float, ext_dir: gridlock.Direction, filepath: str) -> gridlock.Grid: if eps_spec.type != "gds": raise NotImplementedError( "Epsilon spec not implemented for type {}".format(eps_spec.type)) # Make grid object. grid = gridlock.Grid(edge_coords, ext_dir=ext_dir, initial=_get_mat_index(eps_spec.mat_stack.background, wlen)**2, num_grids=3) # Draw layers. _draw_gds_on_grid(gds_stack=eps_spec.mat_stack.stack, grid=grid, gds_path=os.path.join(filepath, eps_spec.gds), wlen=wlen) # Make epsilon. grid.render() # Return epsilon and dxes. return grid
def __init__(self, function: problem.OptimizationFunction, sim_space: simspace.SimulationSpace, slice_point: Optional[List[float]] = None, slice_normal: Optional[List[int]] = None) -> None: """Initializes the monitor. If `slice_point` and `slice_normal` are set, then a 2D slice is taken over the 3D vector field. Args: function: Field function to monitor. sim slice_point: Point in the field slice to monitor. slice_normal: Normal of the slice that is evaluated. """ super().__init__(function) self._function = function self._slices = None self._sim_space = sim_space if slice_point and slice_normal: slice_axis = gridlock.axisvec2axis(slice_normal) grid = gridlock.Grid(sim_space.edge_coords, num_grids=3) slice_ind = grid.pos2ind(slice_point, which_shifts=None).astype(int) slice_ind = slice_ind[slice_axis] self._slices = 3 * [slice(0, None)] self._slices[slice_axis] = slice(slice_ind, slice_ind + 1)
def __init__( self, eps: goos.Function, sources: List[SimSource], solver: str, wavelength: float, bloch_vector: List[float], simulation_space: simspace.SimulationSpace, outputs: List[SimOutput], ) -> None: # Determine the output flow types. output_flow_types = [ maxwell.SIM_REGISTRY.get(out.type).meta["output_type"] for out in outputs ] output_names = [out.name for out in outputs] super().__init__([eps], flow_names=output_names, flow_types=output_flow_types) # Create an empty grid to have access to `dxes` and `shape`. self._grid = gridlock.Grid(simspace.create_edge_coords( simulation_space.sim_region, simulation_space.mesh.dx, reflection_symmetry=simulation_space.reflection_symmetry), ext_dir=gridlock.Direction.z, initial=0, num_grids=3) self._dxes = [self._grid.dxyz, self._grid.autoshifted_dxyz()] eigen_solvers = ["maxwell_eig", "local_direct_eig"] if solver in eigen_solvers: self._solver = _create_solver(solver, None, self._grid.shape) else: raise ValueError( "Invalid solver, solver for eigensolves need to be in " + str(eigen_solvers) + ".") self._simspace = simulation_space if bloch_vector: self._bloch_vector = bloch_vector else: self._bloch_vector = np.array([0, 0, 0]) self._wlen = wavelength #this will be used for the initial guess self._pml_layers = [ int(length / self._simspace.mesh.dx) for length in self._simspace.pml_thickness ] self._symmetry = simulation_space.reflection_symmetry self._sources = _create_sources(sources) self._outputs = _create_outputs(outputs) # Handle caching of simulation results. self._last_results = None self._last_eps = None
def create_region_slices( edge_coords: fdfd_tools.EdgeCoords, center: fdfd_tools.Vec3d, extents: fdfd_tools.Vec3d, ) -> Tuple[slice, slice, slice]: """Return `slice` objects corresponding to a given rectangular region. `center` and `extents` describe a rectangular prism in the space described by a grid with edge coordinates given by `edge_coords`. This function returns three `slice` objects corresponding to each of the three axes that select out this region. For example, suppose `eps` is a `fdfd_tools.VecField` that lives on a grid described by `edge_coords`. Then `eps[calculate_slices(...)]` would correspond to the portion of `eps` that lies in the rectangular region. Args: center: Three element array indicating the center of the region in the grid's units. extent: Three element array indicating the size of the region in the grid's units. Returns: Three element tuple with the slices. """ # Get min and max position. xyz_min = np.array(center) - np.array(extents) / 2 xyz_max = np.array(center) + np.array(extents) / 2 # Make a dummy grid so we can use `pos2ind`. grid = gridlock.Grid(edge_coords, num_grids=3) grid_min = [v[0] for v in grid.xyz] grid_max = [v[-1] for v in grid.xyz] # Adjust min max. xyz_min_clip = [max(gr, sl) for gr, sl in zip(grid_min, xyz_min)] xyz_max_clip = [min(gr, sl) for gr, sl in zip(grid_max, xyz_max)] # Get the min and max indices. xyz_ind_min = grid.pos2ind(xyz_min_clip, which_shifts=None).astype(int) xyz_ind_max = grid.pos2ind(xyz_max_clip, which_shifts=None).astype(int) # TODO(logansu): Revisit and consider whether `pos2ind` should allow # out of bounds coordinates. # Set slices correctly if clipped. The issue at hand here is that if # `xyz_max` is out of bounds, we actually want to make sure the slice # includes the border. xyz_ind_min[xyz_min_clip > xyz_min] = 0 xyz_ind_max[xyz_max_clip < xyz_max] += 1 # Make sure that slices are nonzero. for i in range(3): xyz_ind_max[i] = max(xyz_ind_max[i], xyz_ind_min[i] + 1) # TODO(logansu): Return tuple instead of array. Only tuple slices work # with np.ndarrays. return [slice(mi, ma) for mi, ma in zip(xyz_ind_min, xyz_ind_max)]
def _create_grid(eps_spec: optplan.EpsilonSpec, edge_coords: fdfd_tools.EdgeCoords, wlen: float, ext_dir: gridlock.Direction, filepath: str) -> gridlock.Grid: if eps_spec.type == "gds": # Make grid object. grid = gridlock.Grid( edge_coords, ext_dir=ext_dir, initial=_get_mat_index(eps_spec.mat_stack.background, wlen)**2, num_grids=3) # Draw layers. _draw_gds_on_grid( gds_stack=eps_spec.mat_stack.stack, grid=grid, gds_path=os.path.join(filepath, eps_spec.gds), wlen=wlen) # Make epsilon. grid.render() elif eps_spec.type == "gds_mesh": # Make grid object. grid = gridlock.Grid( edge_coords, ext_dir=ext_dir, initial=_get_mat_index(eps_spec.background, wlen)**2, num_grids=3) # Load GDS. with open(os.path.join(filepath, eps_spec.gds), "rb") as gds_file: gds = gdslib.GDSImport(gds_file) # Draw meshes. for mesh in eps_spec.mesh_list: _draw_mesh_on_grid(mesh, grid, gds, wlen) # Make epsilon. grid.render() else: raise NotImplementedError( "Epsilon spec not implemented for type {}".format(eps_spec.type)) # Return epsilon and dxes. return grid
def test_draw_slab_2d(): """Tests that drawing a slab in 2D is correct (no aliasing issues).""" edge_coords = [[0, 1.0, 2.0, 3.0, 4, 5, 6], [0, 1.0, 2, 3, 4, 5, 6], [-0.5, 0.5]] grid = gridlock.Grid(edge_coords, num_grids=3, initial=10) grid.draw_slab(gridlock.Direction.y, 4, 3, 1) grid.render() np.testing.assert_array_almost_equal(grid.grids[0][2, :, 0], [10, 10, 10, 1, 1, 1]) np.testing.assert_array_almost_equal(grid.grids[1][2, :, 0], [10, 10, 5.5, 1, 1, 5.5])
def test_gaussian_source_2d(): grid = gridlock.Grid(simspace.create_edge_coords( goos.Box3d(center=[0, 0, 0], extents=[14000, 40, 3000]), 40), ext_dir=gridlock.Direction.z, initial=1, num_grids=3) grid.render() eps = np.array(grid.grids) dxes = [grid.dxyz, grid.autoshifted_dxyz()] sim = maxwell.FdfdSimProp(eps=eps, source=np.zeros_like(eps), wlen=1550, dxes=dxes, pml_layers=[10, 10, 0, 0, 10, 10], grid=grid, solver=maxwell.DIRECT_SOLVER) src = maxwell.GaussianSourceImpl( maxwell.GaussianSource(w0=5200, center=[0, 0, 0], extents=[14000, 0, 0], normal=[0, 0, -1], power=1, theta=0, psi=0, polarization_angle=np.pi / 2, normalize_by_sim=True)) src.before_sim(sim) fields = maxwell.DIRECT_SOLVER.solve( omega=2 * np.pi / sim.wlen, dxes=sim.dxes, epsilon=fdfd_tools.vec(sim.eps), mu=None, J=fdfd_tools.vec(sim.source), pml_layers=sim.pml_layers, bloch_vec=sim.bloch_vec, ) fields = fdfd_tools.unvec(fields, grid.shape) field_y = fields[1].squeeze() np.testing.assert_allclose(field_y[:, 53], np.zeros_like(field_y[:, 53]), atol=1e-4) # Calculate what the amplitude of the Gaussian field should look like. # Amplitude determined empirically through simulation. coords = (np.arange(len(field_y[:, 20])) - len(field_y[:, 20]) / 2) * 40 target_gaussian = np.exp(-coords**2 / 5200**2) * 0.00278 np.testing.assert_allclose(np.abs(field_y[:, 20]), target_gaussian, atol=1e-4)
def _render(self, grid, shape) -> List[gridlock.Grid]: extra_grids = [] # TODO(logansu): Warn about rotation. #if not np.array_equal(shape.rot, [0, 0, 0]): # raise NotImplemented("Render cannot handle objects with rotation.") if isinstance(shape, goos.CuboidFlow): if np.all(shape.extents != 0): grid.draw_cuboid(shape.pos, shape.extents, shape.material.permittivity(self._wlen)) elif isinstance(shape, goos.CylinderFlow): radius = shape.radius.item() num_points = int(np.ceil(self._pts_per_arclen * 2 * np.pi * radius)) grid.draw_cylinder(shape.pos, radius, shape.height.item(), num_points, shape.material.permittivity(self._wlen)) elif isinstance(shape, goos.ArrayFlow): for s in shape: extra_grids += self._render(grid, s) elif isinstance(shape, goos.PixelatedContShapeFlow): # Draw a cuboid in the original grid to overwrite any shapes in the # shape region, but draw the continuous permittivity on an extra # grid. grid.draw_cuboid(shape.pos, shape.extents, shape.material.permittivity(self._wlen)) new_grid = gridlock.Grid(grid.exyz, ext_dir=grid.ext_dir, initial=0, num_grids=3) contrast = shape.material2.permittivity( self._wlen) - shape.material.permittivity(self._wlen) shape_coords = shape.get_edge_coords() for axis in range(3): grid_coords = new_grid.shifted_exyz( axis, which_grid=gridlock.GridType.COMP) # Remove ghost cells at the end. grid_coords = [ c if c.shape == co.shape else c[:-1] for c, co in zip(grid_coords, grid.exyz) ] mat = get_rendering_matrix(shape_coords, grid_coords) grid_vals = contrast * mat @ shape.array.flatten() new_grid.grids[axis] = np.reshape(grid_vals, new_grid.grids[axis].shape) extra_grids.append(new_grid) else: raise ValueError("Encountered unrenderable type, got {}".format( type(shape))) return extra_grids
def __init__(self, filepath, params: optplan.SimulationSpace): # Setup the grid. self._dx = params.mesh.dx from spins.invdes.problem_graph.simspace import _create_edge_coords self._edge_coords = _create_edge_coords(params.sim_region, self._dx) self._ext_dir = gridlock.Direction.z # Currently always extrude in z. # TODO(logansu): Factor out grid functionality and drawing. # Create a grid object just so we can calculate dxes. self._grid = gridlock.Grid( self._edge_coords, ext_dir=self._ext_dir, num_grids=3) self._pml_layers = params.pml_thickness self._filepath = filepath self._eps_bg = params.eps_bg
def __init__( self, eps: goos.Function, sources: List[SimSource], wavelength: float, simulation_space: simspace.SimulationSpace, outputs: List[SimOutput], solver: str = None, solver_info: Solver = None, ) -> None: # Determine the output flow types. output_flow_types = [ maxwell.SIM_REGISTRY.get(out.type).meta["output_type"] for out in outputs ] output_names = [out.name for out in outputs] super().__init__([eps], flow_names=output_names, flow_types=output_flow_types, heavy_compute=True) # Create an empty grid to have access to `dxes` and `shape`. self._grid = gridlock.Grid(simspace.create_edge_coords( simulation_space.sim_region, simulation_space.mesh.dx, reflection_symmetry=simulation_space.reflection_symmetry), ext_dir=gridlock.Direction.z, initial=0, num_grids=3) self._dxes = [self._grid.dxyz, self._grid.autoshifted_dxyz()] self._solver = _create_solver(solver, solver_info, self._grid.shape) self._simspace = simulation_space self._bloch_vector = [0, 0, 0] self._wlen = wavelength self._pml_layers = [ int(length / self._simspace.mesh.dx) for length in self._simspace.pml_thickness ] self._symmetry = simulation_space.reflection_symmetry self._sources = _create_sources(sources) self._outputs = _create_outputs(outputs) # Handle caching of simulation results. self._last_results = None self._last_eps = None