Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
 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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
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)]
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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])
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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