예제 #1
0
    def solve(self,
              omega: complex,
              dxes: fdfd_tools.GridSpacing,
              J: np.ndarray,
              epsilon: np.ndarray,
              pml_layers: Optional[fdfd_tools.PmlLayers] = None,
              mu: np.ndarray = None,
              pec: np.ndarray = None,
              pmc: np.ndarray = None,
              pemc: np.ndarray = None,
              bloch_vec: Optional[np.ndarray] = None,
              symmetry: Optional[np.ndarray] = None,
              adjoint: bool = False,
              E0: np.ndarray = None):
        if (symmetry is not None) and np.any(symmetry):
            raise NotImplementedError("`symmetry` is not implemented.")
        if (pemc is not None) and np.any(pemc):
            raise NotImplementedError("`pemc` is not implemented.")

        if bloch_vec is None:
            bloch_vec = np.zeros(3)

        dxes = fdfd_tools.grid.apply_scpml(dxes, pml_layers, omega)

        b0 = -1j * omega * J
        A0 = operators.e_full(omega,
                              dxes,
                              epsilon=epsilon,
                              mu=mu,
                              pec=pec,
                              pmc=pmc,
                              bloch_vec=bloch_vec)

        Pl, Pr = operators.e_full_preconditioners(dxes)

        if adjoint:
            A = (Pl @ A0 @ Pr).H
            b = Pr.H @ b0
        else:
            A = Pl @ A0 @ Pr
            b = Pl @ b0

        x = self.solve_matrix_equation(A.astype(np.complex128).tocsr(), b)

        if adjoint:
            x0 = Pl.H @ x
        else:
            x0 = Pr @ x

        return x0
예제 #2
0
def build_laguerre_gaussian_source(eps_grid: Grid,
                          omega: float,
                          w0: float,
                          center: np.ndarray,
                          axis: int,
                          slices=List[slice],
                          mu: List[np.ndarray] = None,
                          theta: float = 0,
                          psi: float = 0,
                          polarization_angle: float = 0,
                          m: int = 0,
                          p : int = 0,
                          polarity: int = 1,
                          power: float = 1):
    """Builds a laguerre gaussian beam source.

    By default, the laguerre gaussian beam propagates along polarity of the given axis and
    is linearly polarized along the x-direction if axis is z, y-direction if x and
    z direction if y. `theta` rotates the propagation direction around the E-field,
    then 'psi' rotates source plane normal, and the polarization_angle rotates around
    the propagation direction.

    Args:
        eps_grid: gridlock.grid with the permittivity distribution.
        omega: The frequency of the mode.
        axis: Direction of propagation.
        slices: Source slice which define the position of the source in the grid.
        mu: Permeability distribution.
        theta: Rotation around the default E-component.
        psi: Rotation around the source plane normal.
        polarization_angle: Rotation around the propagation direction.
        m : parameters of the  laguerre gaussian beam
        p : parameters of the  laguerre gaussian beam
        polarity: 1 if forward propagating. -1 if backward propagating.
        power: Power is the gaussian beam.

    Returns:
        Current source J.
    """
    # Make scalar fields.
    eps_val = np.real(np.average(eps_grid.grids[0][tuple(slices)]))
    scalar_field_function = lambda x, y, z, R: laguerre_gaussian_beam_z_axis_x_pol(
        x, y, z, w0, center, R, omega, m, p, polarity, eps_val)

    # Get vector fields.
    fields = scalar2rotated_vector_fields(
        eps_grid=eps_grid,
        scalar_field_function=scalar_field_function,
        mu=mu,
        omega=omega,
        axis=axis,
        slices=slices,
        theta=theta,
        psi=psi,
        polarization_angle=polarization_angle,
        polarity=polarity,
        power=power,
        full_fields=True)

    # Calculate the source.
    dxes = [eps_grid.dxyz, eps_grid.autoshifted_dxyz()]

    # make current
    field_slices = [slice(None, None)] * 3
    J_slices = slices
    if polarity == -1:
        ind = slices[axis].start
        field_slices[axis] = slice(None, ind)
        J_slices[axis] = slice(ind - 1, ind + 1)
    else:
        ind = slices[axis].stop - 1
        field_slices[axis] = slice(ind, None)
        J_slices[axis] = slice(ind - 1, ind + 1)
    E = np.zeros_like(fields['E'])
    for i in range(3):
        E[i][tuple(field_slices)] = fields['E'][i][tuple(field_slices)]

    full = operators.e_full(omega,
                            dxes,
                            vec(eps_grid.grids),
                            bloch_vec=fields['wavevector'])
    J_vec = 1 / (-1j * omega) * full @ vec(E)
    J_temp = unvec(J_vec, E[0].shape)
    J = np.zeros_like(J_temp)
    for i in range(3):
        J[i][tuple(J_slices)] = J_temp[i][tuple(J_slices)]

    return J, fields['wavevector']
예제 #3
0
def build_plane_wave_source(eps_grid: Grid,
                            omega: float,
                            axis: int,
                            slices=List[slice],
                            mu: List[np.ndarray] = None,
                            theta: float = 0,
                            psi: float = 0,
                            polarization_angle: float = 0,
                            polarity: int = 1,
                            border: int or List[int] = 0,
                            power: float = 1):
    """Builds a plane wave source.

    By default, the plane wave propagates along polarity of the given axis and
    is linearly polarized along the x-direction if axis is z, y-direction if x and
    z direction if y. `theta` rotates the propagation direction around the E-field,
    then 'psi' rotates source plane normal, and the polarization_angle rotates around
    the propagation direction.

    Args:
        eps_grid: gridlock.grid with the permittivity distribution.
        omega: The frequency of the mode.
        axis: Direction of propagation.
        slices: Source slice which define the position of the source in the grid.
        mu: Permeability distribution.
        theta: Rotation around the default E-component.
        psi: Rotation around the source plane normal.
        polarization_angle: Rotation around the propagation direction.
        polarity: 1 if forward propagating. -1 if backward propagating.
        border: border in grid points where the intensity of the planewave decreased to 0.
            For example: [10, 10, 10] assuming radiation in the z direction will put 10
                grid border on both sides in the x and y direction and ignore the z.
                (If the border is larger then the simulation it will be ignored aswell.)
        power: power transmitted through the slice.

    Returns:
        Current source J.
    """
    # Perpare arguments.
    shape = eps_grid.shape

    # Make scalar fields.
    eps_val = np.real(np.average(eps_grid.grids[0][tuple(slices)]))
    scalar_field_function = lambda x, y, z, R: plane_wave_z_axis_x_pol(
        x, y, z, R, omega, polarity, eps_val)

    # Get vector fields.
    fields = scalar2rotated_vector_fields(
        eps_grid=eps_grid,
        scalar_field_function=scalar_field_function,
        mu=mu,
        omega=omega,
        axis=axis,
        slices=slices,
        theta=theta,
        psi=psi,
        polarization_angle=polarization_angle,
        polarity=polarity,
        power=power,
        full_fields=True)

    def make_mask(shape: List[int], slices: List, axis: int,
                  border: List[int]) -> np.ndarray:
        """Builds a mask with a border. On the border the intensity goes from 1 to 0.

        Args:
            shape: Shape of the simulation.
            slices: List of slices where the plane wave is.
            axis: Direction of the plane wave.
            border: number of gridpoint that make up the border.

        Returns:
            mask
        """
        size = [s.stop - s.start for s in slices]
        coords = []
        for i in range(3):
            if (size[i] - 1) > 2 * border[i] and border[i] > 0:
                d = 1 / np.array(border[i])
                x = np.hstack([
                    np.arange(1, 0, -d),
                    np.zeros(size[i] - 2 * border[i]),
                    np.arange(d, 1 + d, d)
                ])
            else:
                x = np.zeros(size[i])
            coords.append(x)
        coords[axis] = np.zeros(shape[axis])
        ind = np.meshgrid(coords[0], coords[1], coords[2], indexing='ij')
        co = (ind[0]**2 + ind[1]**2 + ind[2]**2)**0.5
        co[co > 1] = 1
        mask_slice = 1 + 2 * co**3 - 3 * co**2
        mask = np.zeros(shape)
        slices_mask = copy.deepcopy(slices)
        slices_mask[axis] = slice(None, None)
        mask[tuple(slices_mask)] = mask_slice
        return mask

    # Prepare border and the mask for be fields.
    if isinstance(border, int):
        border = 3 * [border]
    border[axis] = 0  # You can not have a border in the axis direction
    size = [s.stop - s.start for s in slices]
    for i, (b, s) in enumerate(zip(border, size)):
        if 2 * b < s:
            border[i] = b
        elif s == 1:
            border[i] = 0
        else:
            raise ValueError("Two times the border is larger then the size" +
                             " of the plane wave in axis {}.".format(i))

    border = [b if 2 * b < s else 0 for b, s in zip(border, size)]
    mask = make_mask(shape, slices, axis, border)

    # Normalize fields
    dxes = [eps_grid.dxyz, eps_grid.autoshifted_dxyz()]
    slices_P = copy.deepcopy(slices)
    slices_P = [
        slice(sl.start + b, sl.stop - b) for sl, b in zip(slices_P, border)
    ]
    P = waveguide_mode.compute_transmission_chew(E=fields['E'],
                                                 H=fields['H'],
                                                 axis=axis,
                                                 omega=omega,
                                                 dxes=dxes,
                                                 slices=slices_P)
    fields['E'] /= np.sqrt(abs(P))
    fields['H'] /= np.sqrt(abs(P))
    fields['E'] = [mask * e for e in fields['E']]
    fields['H'] = [mask * h for h in fields['H']]

    # Calculate the source.
    dxes = [eps_grid.dxyz, eps_grid.autoshifted_dxyz()]

    # make current
    field_slices = [slice(None, None)] * 3
    J_slices = slices
    if polarity == -1:
        ind = slices[axis].start
        field_slices[axis] = slice(None, ind)
        J_slices[axis] = slice(ind - 1, ind + 1)
    else:
        ind = slices[axis].stop - 1
        field_slices[axis] = slice(ind, None)
        J_slices[axis] = slice(ind - 1, ind + 1)
    E = np.zeros_like(fields['E'])
    for i in range(3):
        E[i][tuple(field_slices)] = fields['E'][i][tuple(field_slices)]

    full = operators.e_full(omega,
                            dxes,
                            vec(eps_grid.grids),
                            bloch_vec=fields['wavevector'])
    J_vec = 1 / (-1j * omega) * full @ vec(E)
    J_temp = unvec(J_vec, E[0].shape)
    J = np.zeros_like(J_temp)
    for i in range(3):
        J[i][tuple(J_slices)] = J_temp[i][tuple(J_slices)]

    return J, fields['wavevector']