Exemple #1
0
def create_waveguide_multi_phase_function(params: optplan.ProblemGraphNode,
                                          work: workspace.Workspace):
    sim_space = work.get_object(params.simulation.simulation_space)
    wavelength = params.simulation.wavelength
    overlap_in = fdfd_tools.vec(
        work.get_object(params.overlap_in)(sim_space, wavelength))
    path = fdfd_tools.vec(work.get_object(params.path)(sim_space, wavelength))

    slice_in = grid_utils.create_region_slices(sim_space.edge_coords,
                                               params.overlap_in.center,
                                               params.overlap_in.extents)
    slice_out = grid_utils.create_region_slices(sim_space.edge_coords,
                                                params.overlap_out.center,
                                                params.overlap_out.extents)
    shape = sim_space.dims

    return WaveguideMultiPhaseFunction(input_function=work.get_object(
        params.simulation),
                                       overlap_model=overlap_in,
                                       slice_model=slice_in,
                                       slice_in=slice_in,
                                       slice_out=slice_out,
                                       path=path,
                                       wavelength=wavelength,
                                       shape=shape)
Exemple #2
0
    def __call__(self, simspace: SimulationSpace, wlen: float,
                 **kwargs) -> fdfd_tools.VecField:
        """Creates the source vector.

        Args:
            simspace: Simulation space object to use.
            wlen: Wavelength to operate source.

        Returns:
            The vector field corresponding to the source.
        """
        space_inst = simspace(wlen)

        return fdfd_solvers.waveguide_mode.build_waveguide_source(
            omega=2 * np.pi / wlen,
            dxes=simspace.dxes,
            eps=space_inst.eps_bg.grids,
            mu=None,
            mode_num=self._params.mode_num,
            waveguide_slice=grid_utils.create_region_slices(
                simspace.edge_coords, self._params.center,
                self._params.extents),
            axis=gridlock.axisvec2axis(self._params.normal),
            polarity=gridlock.axisvec2polarity(self._params.normal),
            power=self._params.power)
Exemple #3
0
    def __init__(self, input_function: problem.OptimizationFunction,
                 simulation_space: SimulationSpace, center: np.ndarray,
                 extents: np.ndarray, epsilon: problem.OptimizationFunction):
        """Constructs the Stored Energy Fn

        Args:
            input_function: Input objectives (typically a simulation from which
                            we get the e-field).
            simulation_space: sim space we are simulating
            centre:  Centre of integration region
            extents: extends of integration region
            epsilon: Permittivity
        """
        # Call superclass initialisor. Nb: this defines the fn inputs for eval etc
        super().__init__([input_function, epsilon])

        region_slice = grid_utils.create_region_slices(
            simulation_space.edge_coords, center, extents)

        # Create a selection filter for vectorised fields and permittivities
        filter_grid = [np.zeros(simulation_space.dims) for i in range(3)]
        for i in range(3):
            filter_grid[i][
                tuple(region_slice
                      )] = 1  # X,Y,Z components set to 1 for region slice
        self._filter_vec = fdfd_tools.vec(filter_grid)
Exemple #4
0
    def __call__(
        self, simspace: SimulationSpace, wlen: float, solver, **kwargs
    ) -> Union[fdfd_tools.VecField, Tuple[fdfd_tools.VecField,
                                          fdfd_tools.Vec3d]]:
        """Creates the plane wave source.

        Args:
            simspace: Simulation space to use for the source.
            wlen: Wavelength of source.

        Returns:
            If `overwrite_bloch_vector` is `True`, a tuple containing the source
            field and the Bloch vector corresponding to the plane wave source.
            Otherwise, only the source field is returned.
        """
        space_inst = simspace(wlen)

        # Calculate the border in gridpoints and igore the border if it's larger then the simulation.
        dx = simspace.dx
        border = [int(b // dx) for b in self._params.border]
        # The plane wave is assumed to be in the z direction so the border is 0 for z.
        border.append(0)

        source, kvector = fdfd_tools.free_space_sources.build_plane_wave_source(
            omega=2 * np.pi / wlen,
            eps_grid=space_inst.eps_bg,
            mu=None,
            axis=gridlock.axisvec2axis(self._params.normal),
            polarity=gridlock.axisvec2polarity(self._params.normal),
            slices=grid_utils.create_region_slices(simspace.edge_coords,
                                                   self._params.center,
                                                   self._params.extents),
            theta=self._params.theta,
            psi=self._params.psi,
            polarization_angle=self._params.polarization_angle,
            border=border,
            power=self._params.power)

        if self._params.normalize_by_sim:
            source = fdfd_tools.free_space_sources.normalize_source_by_sim(
                omega=2 * np.pi / wlen,
                source=source,
                eps=space_inst.eps_bg.grids,
                dxes=simspace.dxes,
                pml_layers=simspace.pml_layers,
                solver=solver,
                power=self._params.power)

        if self._params.overwrite_bloch_vector:
            # TODO(logansu): Figure out what's wrong with mixing PMLs and
            # Bloch vector. It seems to create problems.
            # For now, we manually set Bloch to zero is PML is nonzero.
            if simspace.pml_layers[0] != 0 or simspace.pml_layers[1] != 0:
                kvector[0] = 0
            if simspace.pml_layers[2] != 0 or simspace.pml_layers[3] != 0:
                kvector[1] = 0
            if simspace.pml_layers[4] != 0 or simspace.pml_layers[5] != 0:
                kvector[2] = 0
            return source, kvector
        return source
Exemple #5
0
def create_power_transmission_function(
        params: PowerTransmission,
        context: workspace.Workspace) -> PowerTransmissionFunction:
    simspace = context.get_object(params.field.simulation_space)
    return PowerTransmissionFunction(
        field=context.get_object(params.field),
        simspace=simspace,
        wlen=params.field.wavelength,
        plane_slice=grid_utils.create_region_slices(simspace.edge_coords,
                                                    params.center,
                                                    params.extents),
        axis=gridlock.axisvec2axis(params.normal),
        polarity=gridlock.axisvec2polarity(params.normal))
Exemple #6
0
    def __call__(self, shape: List[int]) -> np.ndarray:
        region_slice = grid_utils.create_region_slices(
            self._params.edge_coords, self._params.center,
            self._params.extents)
        region = np.zeros(shape)
        region[tuple(region_slice)] = 1

        outside_region = np.ones_like(self._region) - self._region
        lower_random = np.random.uniform(self._params.lower_min,
                                         self._params.lower_max, shape)
        upper_random = np.random.uniform(self._params.upper_min,
                                         self._params.upper_max, shape)
        return self._region * upper_random + outside_region * lower_random
Exemple #7
0
 def __call__(self, simspace: SimulationSpace, wlen: float,
              **kwargs) -> fdfd_tools.VecField:
     space_inst = simspace(wlen)
     return fdfd_solvers.waveguide_mode.build_overlap(
         omega=2 * np.pi / wlen,
         dxes=simspace.dxes,
         eps=space_inst.eps_bg.grids,
         mu=None,
         mode_num=self._params.mode_num,
         waveguide_slice=grid_utils.create_region_slices(
             simspace.edge_coords, self._params.center,
             self._params.extents),
         axis=gridlock.axisvec2axis(self._params.normal),
         polarity=gridlock.axisvec2polarity(self._params.normal),
         power=self._params.power)
Exemple #8
0
    def __call__(self, simspace: SimulationSpace, wlen: float,
                 **kwargs) -> fdfd_tools.VecField:
        space_inst = simspace(wlen)
        region_slice = grid_utils.create_region_slices(simspace.edge_coords,
                                                       self._params.center,
                                                       self._params.extents)
        eps = space_inst.eps_bg.grids

        region = [np.zeros_like(eps[0])] * 3

        for i in range(3):
            region[i][tuple(region_slice)] = np.ones_like(
                eps[0])[tuple(region_slice)]

        return np.multiply(region, self._params.power)
Exemple #9
0
def test_plane_power_grad():
    space = Simspace(
        TESTDATA,
        optplan.SimulationSpace(
            pml_thickness=[0, 0, 0, 0, 0, 0],
            mesh=optplan.UniformMesh(dx=40),
            sim_region=optplan.Box3d(
                center=[0, 0, 0],
                extents=[80, 80, 80],
            ),
            eps_bg=optplan.GdsEps(
                gds="straight_waveguide.gds",
                mat_stack=optplan.GdsMaterialStack(
                    background=optplan.Material(mat_name="air"),
                    stack=[
                        optplan.GdsMaterialStackLayer(
                            gds_layer=[100, 0],
                            extents=[-80, 80],
                            foreground=optplan.Material(mat_name="Si"),
                            background=optplan.Material(mat_name="air"),
                        ),
                    ],
                ),
            ),
        ))

    wlen = 1550
    power_fun = poynting.PowerTransmissionFunction(
        field=problem.Variable(1),
        simspace=space,
        wlen=wlen,
        plane_slice=grid_utils.create_region_slices(space.edge_coords,
                                                    [0, 0, 0], [40, 80, 80]),
        axis=gridlock.axisvec2axis([1, 0, 0]),
        polarity=gridlock.axisvec2polarity([1, 0, 0]))

    field = np.arange(np.prod(space.dims) * 3).astype(np.complex128) * 1j

    grad_actual = power_fun.grad([field], 1)
    fun = lambda vec: power_fun.eval([vec])
    grad_brute = eval_grad_brute_wirt(field, fun)

    np.testing.assert_array_almost_equal(grad_actual[0], grad_brute, decimal=4)
Exemple #10
0
    def __call__(self, simspace: SimulationSpace, wlen: float,
                 solver: Callable, **kwargs) -> fdfd_tools.VecField:
        """Creates the source vector.

        Args:
            simspace: Simulation space.
            wlen: Wavelength of source.
            solver: If `normalize_by_source` is `True`, `solver` will be used
                to run an EM simulation to normalize the source power.

        Returns:
            The source.
        """
        space_inst = simspace(wlen)
        source, _ = fdfd_tools.free_space_sources.build_gaussian_source(
            omega=2 * np.pi / wlen,
            eps_grid=space_inst.eps_bg,
            mu=None,
            axis=gridlock.axisvec2axis(self._params.normal),
            polarity=gridlock.axisvec2polarity(self._params.normal),
            slices=grid_utils.create_region_slices(simspace.edge_coords,
                                                   self._params.center,
                                                   self._params.extents),
            theta=self._params.theta,
            psi=self._params.psi,
            polarization_angle=self._params.polarization_angle,
            w0=self._params.w0,
            center=self._params.beam_center,
            power=self._params.power)

        if self._params.normalize_by_sim:
            source = fdfd_tools.free_space_sources.normalize_source_by_sim(
                omega=2 * np.pi / wlen,
                source=source,
                eps=space_inst.eps_bg.grids,
                dxes=simspace.dxes,
                pml_layers=simspace.pml_layers,
                solver=solver,
                power=self._params.power)

        return source
Exemple #11
0
    def __call__(
        self, simspace: SimulationSpace, wlen: float, **kwargs
    ) -> Union[fdfd_tools.VecField, Tuple[fdfd_tools.VecField,
                                          fdfd_tools.Vec3d]]:
        """Creates the plane wave source.

        Args:
            simspace: Simulation space to use for the source.
            wlen: Wavelength of source.

        Returns:
            If `overwrite_bloch_vector` is `True`, a tuple containing the source
            field and the Bloch vector corresponding to the plane wave source.
            Otherwise, only the source field is returned.
        """
        space_inst = simspace(wlen)

        # Calculate the border in gridpoints and igore the border if it's larger then the simulation.
        dx = simspace.dx
        border = [int(b // dx) for b in self._params.border]
        # The plane wave is assumed to be in the z direction so the border is 0 for z.
        border.append(0)

        source, kvector = fdfd_tools.free_space_sources.build_plane_wave_source(
            omega=2 * np.pi / wlen,
            eps_grid=space_inst.eps_bg,
            mu=None,
            axis=gridlock.axisvec2axis(self._params.normal),
            polarity=gridlock.axisvec2polarity(self._params.normal),
            slices=grid_utils.create_region_slices(simspace.edge_coords,
                                                   self._params.center,
                                                   self._params.extents),
            theta=self._params.theta,
            psi=self._params.psi,
            polarization_angle=self._params.polarization_angle,
            border=border,
            power=self._params.power)

        if self._params.overwrite_bloch_vector:
            return source, kvector
        return source
Exemple #12
0
    def __call__(self, simspace: SimulationSpace, wlen: float,
                 **kwargs) -> fdfd_tools.VecField:
        space_inst = simspace(wlen)
        region_slice = grid_utils.create_region_slices(simspace.edge_coords,
                                                       self._params.center,
                                                       self._params.extents)
        eps = space_inst.eps_bg.grids
        eps_min = eps.min()
        eps_max = eps.max()
        eps_norm = (eps - eps_min) / (eps_max - eps_min) + 1

        overlap = [None] * 3
        for i in range(3):
            overlap[i] = np.zeros_like(eps_norm[0], dtype=complex)
            overlap_i = overlap[i]
            eps_i = eps_norm[i]
            eps_i_slice = eps_i[tuple(region_slice)]
            overlap_i[tuple(region_slice)] = eps_i_slice

            overlap[i] = overlap_i

        return np.multiply(overlap, self._params.power)
Exemple #13
0
def test_stored_energy_grad():
    space = Simspace(
        TESTDATA,
        optplan.SimulationSpace(
            pml_thickness=[0, 0, 0, 0, 0, 0],
            mesh=optplan.UniformMesh(dx=40),
            sim_region=optplan.Box3d(
                center=[0, 0, 0],
                extents=[80, 80, 80],
            ),
            eps_bg=optplan.GdsEps(
                gds="straight_waveguide.gds",
                mat_stack=optplan.GdsMaterialStack(
                    background=optplan.Material(mat_name="air"),
                    stack=[
                        optplan.GdsMaterialStackLayer(
                            gds_layer=[100, 0],
                            extents=[-80, 80],
                            foreground=optplan.Material(mat_name="Si"),
                            background=optplan.Material(mat_name="air"),
                        ),
                    ],
                ),
            ),
        ))

    wlen = 1550
    energy_fun = stored_energy.StoredEnergyFunction(
        input_function=problem.Variable(1),
        simspace=space,
        center=[0,0,0],
        extents=[0,0,0],
        epsilon=space._eps_bg)

        plane_slice=grid_utils.create_region_slices(space.edge_coords,
                                                    [0, 0, 0], [40, 80, 80]),
        axis=gridlock.axisvec2axis([1, 0, 0]),
        polarity=gridlock.axisvec2polarity([1, 0, 0]))
Exemple #14
0
def test_straight_waveguide_power_poynting():
    """Tests that total through straight waveguide is unity."""
    space = Simspace(
        TESTDATA,
        optplan.SimulationSpace(
            pml_thickness=[10, 10, 10, 10, 0, 0],
            mesh=optplan.UniformMesh(dx=40),
            sim_region=optplan.Box3d(
                center=[0, 0, 0],
                extents=[5000, 5000, 40],
            ),
            eps_bg=optplan.GdsEps(
                gds="straight_waveguide.gds",
                mat_stack=optplan.GdsMaterialStack(
                    background=optplan.Material(mat_name="air"),
                    stack=[
                        optplan.GdsMaterialStackLayer(
                            gds_layer=[100, 0],
                            extents=[-80, 80],
                            foreground=optplan.Material(mat_name="Si"),
                            background=optplan.Material(mat_name="air"),
                        ),
                    ],
                ),
            ),
        ))

    source = creator_em.WaveguideModeSource(
        optplan.WaveguideModeSource(
            power=1.0,
            extents=[40, 1500, 600],
            normal=[1.0, 0.0, 0.0],
            center=[-1770, 0, 0],
            mode_num=0,
        ))

    wlen = 1550
    eps_grid = space(wlen).eps_bg.grids
    source_grid = source(space, wlen)

    eps = problem.Constant(fdfd_tools.vec(eps_grid))
    sim = creator_em.FdfdSimulation(
        eps=eps,
        solver=local_matrix_solvers.DirectSolver(),
        wlen=wlen,
        source=fdfd_tools.vec(source_grid),
        simspace=space,
    )
    power_fun = poynting.PowerTransmissionFunction(
        field=sim,
        simspace=space,
        wlen=wlen,
        plane_slice=grid_utils.create_region_slices(
            space.edge_coords, [1770, 0, 0], [40, 1500, 600]),
        axis=gridlock.axisvec2axis([1, 0, 0]),
        polarity=gridlock.axisvec2polarity([1, 0, 0]))
    # Same as `power_fun` but with opposite normal vector. Should give same
    # answer but with a negative sign.
    power_fun_back = poynting.PowerTransmissionFunction(
        field=sim,
        simspace=space,
        wlen=wlen,
        plane_slice=grid_utils.create_region_slices(
            space.edge_coords, [1770, 0, 0], [40, 1500, 600]),
        axis=gridlock.axisvec2axis([-1, 0, 0]),
        polarity=gridlock.axisvec2polarity([-1, 0, 0]))

    efield_grid = fdfd_tools.unvec(
        graph_executor.eval_fun(sim, None), eps_grid[0].shape)

    # Calculate emitted power.
    edotj = np.real(
        fdfd_tools.vec(efield_grid) * np.conj(
            fdfd_tools.vec(source_grid))) * 40**3
    power = -0.5 * np.sum(edotj)

    np.testing.assert_almost_equal(
        graph_executor.eval_fun(power_fun, None), power, decimal=4)
    np.testing.assert_almost_equal(
        graph_executor.eval_fun(power_fun_back, None), -power, decimal=4)