# fdfd - meanas
Electromagnetic simulation tools

Skip to content

# fdfd

## `` meanas.fdfd

Tools for finite difference frequency-domain (FDFD) simulations and calculations.

These mostly involve picking a single frequency, then setting up and solving a matrix equation (Ax=b) or eigenvalue problem.

Submodules:

- `operators`, `functional`: General FDFD problem setup.
- `solvers`: Solver interface and reference implementation.
- `scpml`: Stretched-coordinate perfectly matched layer (SCPML) boundary conditions.
- `waveguide_2d`: Operators and mode-solver for waveguides with constant cross-section.
- `waveguide_3d`: Functions for transforming `waveguide_2d` results into 3D, including mode-source and overlap-window construction.
- `farfield`, `bloch`, `eme`: specialized helper modules for near/far transforms, Bloch-periodic problems, and eigenmode expansion.

================================================================

From the "Frequency domain" section of `meanas.fdmath`, we have

\[ \begin{aligned} \tilde{E}_{l, \vec{r}} &= \tilde{E}_{\vec{r}} e^{-\imath \omega l \Delta_t} \\ \tilde{H}_{l - \frac{1}{2}, \vec{r} + \frac{1}{2}} &= \tilde{H}_{\vec{r} + \frac{1}{2}} e^{-\imath \omega (l - \frac{1}{2}) \Delta_t} \\ \tilde{J}_{l, \vec{r}} &= \tilde{J}_{\vec{r}} e^{-\imath \omega (l - \frac{1}{2}) \Delta_t} \\ \tilde{M}_{l - \frac{1}{2}, \vec{r} + \frac{1}{2}} &= \tilde{M}_{\vec{r} + \frac{1}{2}} e^{-\imath \omega l \Delta_t} \\ \hat{\nabla} \times (\mu^{-1}_{\vec{r} + \frac{1}{2}} \cdot \tilde{\nabla} \times \tilde{E}_{\vec{r}}) -\Omega^2 \epsilon_{\vec{r}} \cdot \tilde{E}_{\vec{r}} &= -\imath \Omega \tilde{J}_{\vec{r}} e^{\imath \omega \Delta_t / 2} \\ \Omega &= 2 \sin(\omega \Delta_t / 2) / \Delta_t \end{aligned} \]

resulting in

\[ \begin{aligned} \tilde{\partial}_t &\Rightarrow -\imath \Omega e^{-\imath \omega \Delta_t / 2}\\ \hat{\partial}_t &\Rightarrow -\imath \Omega e^{ \imath \omega \Delta_t / 2}\\ \end{aligned} \]

Maxwell's equations are then

\[ \begin{aligned} \tilde{\nabla} \times \tilde{E}_{\vec{r}} &= \imath \Omega e^{-\imath \omega \Delta_t / 2} \hat{B}_{\vec{r} + \frac{1}{2}} - \hat{M}_{\vec{r} + \frac{1}{2}} \\ \hat{\nabla} \times \hat{H}_{\vec{r} + \frac{1}{2}} &= -\imath \Omega e^{ \imath \omega \Delta_t / 2} \tilde{D}_{\vec{r}} + \tilde{J}_{\vec{r}} \\ \tilde{\nabla} \cdot \hat{B}_{\vec{r} + \frac{1}{2}} &= 0 \\ \hat{\nabla} \cdot \tilde{D}_{\vec{r}} &= \rho_{\vec{r}} \end{aligned} \]

With \(\Delta_t \to 0\), this simplifies to

\[ \begin{aligned} \tilde{E}_{l, \vec{r}} &\to \tilde{E}_{\vec{r}} \\ \tilde{H}_{l - \frac{1}{2}, \vec{r} + \frac{1}{2}} &\to \tilde{H}_{\vec{r} + \frac{1}{2}} \\ \tilde{J}_{l, \vec{r}} &\to \tilde{J}_{\vec{r}} \\ \tilde{M}_{l - \frac{1}{2}, \vec{r} + \frac{1}{2}} &\to \tilde{M}_{\vec{r} + \frac{1}{2}} \\ \Omega &\to \omega \\ \tilde{\partial}_t &\to -\imath \omega \\ \hat{\partial}_t &\to -\imath \omega \\ \end{aligned} \]

and then

\[ \begin{aligned} \tilde{\nabla} \times \tilde{E}_{\vec{r}} &= \imath \omega \hat{B}_{\vec{r} + \frac{1}{2}} - \hat{M}_{\vec{r} + \frac{1}{2}} \\ \hat{\nabla} \times \hat{H}_{\vec{r} + \frac{1}{2}} &= -\imath \omega \tilde{D}_{\vec{r}} + \tilde{J}_{\vec{r}} \\ \end{aligned} \]
\[ \hat{\nabla} \times (\mu^{-1}_{\vec{r} + \frac{1}{2}} \cdot \tilde{\nabla} \times \tilde{E}_{\vec{r}}) -\omega^2 \epsilon_{\vec{r}} \cdot \tilde{E}_{\vec{r}} = -\imath \omega \tilde{J}_{\vec{r}} \\ \]

## Core operator layers

## `` meanas.fdfd.functional

Functional versions of many FDFD operators. These can be useful for performing FDFD calculations without needing to construct large matrices in memory.

The functions generated here expect `cfdfield_t` inputs with shape (3, X, Y, Z), e.g. E = [E_x, E_y, E_z] where each (complex) component has shape (X, Y, Z)

### `` e_full

    e_full(
        omega: complex,
        dxes: dx_lists_t,
        epsilon: fdfield,
        mu: fdfield | None = None,
    ) -> cfdfield_updater_t

Wave operator for use with E-field. See `operators.e_full` for details.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `fdfield` | Dielectric constant | required
`mu` | `fdfield | None` | Magnetic permeability (default 1 everywhere) | `None`

Returns:

Type | Description
--- | ---
`cfdfield_updater_t` | Function `f` implementing the wave operator
`cfdfield_updater_t` | `f(E)` -> `-i * omega * J`

### `` eh_full

    eh_full(
        omega: complex,
        dxes: dx_lists_t,
        epsilon: fdfield,
        mu: fdfield | None = None,
    ) -> Callable[
        [cfdfield, cfdfield], tuple[cfdfield_t, cfdfield_t]
    ]

Wave operator for full (both E and H) field representation. See `operators.eh_full`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `fdfield` | Dielectric constant | required
`mu` | `fdfield | None` | Magnetic permeability (default 1 everywhere) | `None`

Returns:

Type | Description
--- | ---
`Callable[[cfdfield, cfdfield], tuple[cfdfield_t, cfdfield_t]]` | Function `f` implementing the wave operator
`Callable[[cfdfield, cfdfield], tuple[cfdfield_t, cfdfield_t]]` | `f(E, H)` -> `(J, -M)`

### `` e2h

    e2h(
        omega: complex,
        dxes: dx_lists_t,
        mu: fdfield | None = None,
    ) -> cfdfield_updater_t

Utility operator for converting the `E` field into the `H` field. For use with `e_full` -- assumes that there is no magnetic current `M`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`mu` | `fdfield | None` | Magnetic permeability (default 1 everywhere) | `None`

Returns:

Type | Description
--- | ---
`cfdfield_updater_t` | Function `f` for converting `E` to `H`,
`cfdfield_updater_t` | `f(E)` -> `H`

### `` m2j

    m2j(
        omega: complex,
        dxes: dx_lists_t,
        mu: fdfield | None = None,
    ) -> cfdfield_updater_t

Utility operator for converting magnetic current `M` distribution into equivalent electric current distribution `J`. For use with e.g. `e_full`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`mu` | `fdfield | None` | Magnetic permeability (default 1 everywhere) | `None`

Returns:

Type | Description
--- | ---
`cfdfield_updater_t` | Function `f` for converting `M` to `J`,
`cfdfield_updater_t` | `f(M)` -> `J`

### `` e_tfsf_source

    e_tfsf_source(
        TF_region: fdfield,
        omega: complex,
        dxes: dx_lists_t,
        epsilon: fdfield,
        mu: fdfield | None = None,
    ) -> cfdfield_updater_t

Operator that turns an E-field distribution into a total-field/scattered-field (TFSF) source.

If `A` is the full wave operator from `e_full(...)` and `Q` is the diagonal mask selecting the total-field region, then the TFSF source is the commutator

\[ \frac{A Q - Q A}{-i \omega} E. \]

This vanishes in the interior of the total-field and scattered-field regions and is supported only at their shared boundary, where the mask discontinuity makes `A` and `Q` fail to commute. The returned current is therefore the distributed source needed to inject the desired total field without also forcing the scattered-field region.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`TF_region` | `fdfield` | mask which is set to 1 in the total-field region, and 0 elsewhere (i.e. in the scattered-field region). Should have the same shape as the simulation grid, e.g. `epsilon[0].shape`. | required
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `fdfield` | Dielectric constant distribution | required
`mu` | `fdfield | None` | Magnetic permeability (default 1 everywhere) | `None`

Returns:

Type | Description
--- | ---
`cfdfield_updater_t` | Function `f` which takes an E field and returns a current distribution,
`cfdfield_updater_t` | `f(E)` -> `J`

### `` poynting_e_cross_h

    poynting_e_cross_h(
        dxes: dx_lists_t,
    ) -> Callable[[cfdfield, cfdfield], cfdfield_t]

Generates a function that takes the single-frequency `E` and `H` fields and calculates the cross product `E` x `H` = \(E \times H\) as required for the Poynting vector, \(S = E \times H\).

On the Yee grid, the electric and magnetic components are not stored at the same locations. This helper therefore applies the same one-cell electric-field shifts used by the sparse `operators.poynting_e_cross(...)` construction so that the discrete cross product matches the face-centered energy flux used in `meanas.fdtd.energy.poynting(...)`.

Note

This function also shifts the input `E` field by one cell as required for computing the Poynting cross product (see `meanas.fdfd` module docs).

Note

If `E` and `H` are peak amplitudes as assumed elsewhere in this code, the time-average of the poynting vector is `<S> = Re(S)/2 = Re(E x H*) / 2`. The factor of `1/2` can be omitted if root-mean-square quantities are used instead.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required

Returns:

Type | Description
--- | ---
`Callable[[cfdfield, cfdfield], cfdfield_t]` | Function `f` that returns the staggered-grid cross product `E \times H`.
`Callable[[cfdfield, cfdfield], cfdfield_t]` | For time-average power, call it as `f(E, H.conj())` and take `Re(...) / 2`.

## `` meanas.fdfd.operators

Sparse matrix operators for use with electromagnetic wave equations.

These functions return sparse-matrix (`scipy.sparse.sparray`) representations of a variety of operators, intended for use with E and H fields vectorized using the `meanas.fdmath.vectorization.vec()` and `meanas.fdmath.vectorization.unvec()` functions.

E- and H-field values are defined on a Yee cell; `epsilon` values should be calculated for cells centered at each E component (`mu` at each H component).

Many of these functions require a `dxes` parameter, of type `dx_lists_t`; see the `meanas.fdmath.types` submodule for details.

The following operators are included:

- E-only wave operator
- H-only wave operator
- EH wave operator
- Curl for use with E, H fields
- E to H conversion
- M to J conversion
- Poynting cross products
- Circular shifts
- Discrete derivatives
- Averaging operators
- Cross product matrices

### `` e_full

    e_full(
        omega: complex,
        dxes: dx_lists_t,
        epsilon: vfdfield | vcfdfield,
        mu: vfdfield | None = None,
        pec: vfdfield | None = None,
        pmc: vfdfield | None = None,
    ) -> sparse.sparray

Wave operator

\[ \nabla \times (\frac{1}{\mu} \nabla \times) - \Omega^2 \epsilon \]

    del x (1/mu * del x) - omega**2 * epsilon

for use with the E-field, with wave equation

\[ (\nabla \times (\frac{1}{\mu} \nabla \times) - \Omega^2 \epsilon) E = -\imath \omega J \]

    (del x (1/mu * del x) - omega**2 * epsilon) E = -i * omega * J

To make this matrix symmetric, use the preconditioners from `e_full_preconditioners()`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `vfdfield | vcfdfield` | Vectorized dielectric constant | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere). | `None`
`pec` | `vfdfield | None` | Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted as containing a perfect electrical conductor (PEC). The PEC is applied per-field-component (i.e. `pec.size == epsilon.size`) | `None`
`pmc` | `vfdfield | None` | Vectorized mask specifying PMC cells. Any cells where `pmc != 0` are interpreted as containing a perfect magnetic conductor (PMC). The PMC is applied per-field-component (i.e. `pmc.size == epsilon.size`) | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix containing the wave operator.

### `` e_full_preconditioners

    e_full_preconditioners(
        dxes: dx_lists_t,
    ) -> tuple[sparse.sparray, sparse.sparray]

Left and right preconditioners `(Pl, Pr)` for symmetrizing the `e_full` wave operator.

The preconditioned matrix `A_symm = (Pl @ A @ Pr)` is complex-symmetric (non-Hermitian unless there is no loss or PMLs).

The preconditioner matrices are diagonal and complex, with `Pr = 1 / Pl`

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required

Returns:

Type | Description
--- | ---
`tuple[sparray, sparray]` | Preconditioner matrices `(Pl, Pr)`.

### `` h_full

    h_full(
        omega: complex,
        dxes: dx_lists_t,
        epsilon: vfdfield,
        mu: vfdfield | None = None,
        pec: vfdfield | None = None,
        pmc: vfdfield | None = None,
    ) -> sparse.sparray

Wave operator

\[ \nabla \times (\frac{1}{\epsilon} \nabla \times) - \omega^2 \mu \]

    del x (1/epsilon * del x) - omega**2 * mu

for use with the H-field, with wave equation

\[ (\nabla \times (\frac{1}{\epsilon} \nabla \times) - \omega^2 \mu) E = \imath \omega M \]

    (del x (1/epsilon * del x) - omega**2 * mu) E = i * omega * M

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `vfdfield` | Vectorized dielectric constant | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere) | `None`
`pec` | `vfdfield | None` | Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted as containing a perfect electrical conductor (PEC). The PEC is applied per-field-component (i.e. `pec.size == epsilon.size`) | `None`
`pmc` | `vfdfield | None` | Vectorized mask specifying PMC cells. Any cells where `pmc != 0` are interpreted as containing a perfect magnetic conductor (PMC). The PMC is applied per-field-component (i.e. `pmc.size == epsilon.size`) | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix containing the wave operator.

### `` eh_full

    eh_full(
        omega: complex,
        dxes: dx_lists_t,
        epsilon: vfdfield,
        mu: vfdfield | None = None,
        pec: vfdfield | None = None,
        pmc: vfdfield | None = None,
    ) -> sparse.sparray

Wave operator for `[E, H]` field representation. This operator implements Maxwell's equations without cancelling out either E or H. The operator is

\[ \begin{bmatrix} -\imath \omega \epsilon & \nabla \times \\ \nabla \times & \imath \omega \mu \end{bmatrix} \]

    [[-i * omega * epsilon,  del x         ],
     [del x,                 i * omega * mu]]

for use with a field vector of the form `cat(vec(E), vec(H))`:

\[ \begin{bmatrix} -\imath \omega \epsilon & \nabla \times \\ \nabla \times & \imath \omega \mu \end{bmatrix} \begin{bmatrix} E \\ H \end{bmatrix} = \begin{bmatrix} J \\ -M \end{bmatrix} \]

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `vfdfield` | Vectorized dielectric constant | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere) | `None`
`pec` | `vfdfield | None` | Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted as containing a perfect electrical conductor (PEC). The PEC is applied per-field-component (i.e. `pec.size == epsilon.size`) | `None`
`pmc` | `vfdfield | None` | Vectorized mask specifying PMC cells. Any cells where `pmc != 0` are interpreted as containing a perfect magnetic conductor (PMC). The PMC is applied per-field-component (i.e. `pmc.size == epsilon.size`) | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix containing the wave operator.

### `` e2h

    e2h(
        omega: complex,
        dxes: dx_lists_t,
        mu: vfdfield | None = None,
        pmc: vfdfield | None = None,
    ) -> sparse.sparray

Utility operator for converting the E field into the H field. For use with `e_full()` -- assumes that there is no magnetic current M.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere) | `None`
`pmc` | `vfdfield | None` | Vectorized mask specifying PMC cells. Any cells where `pmc != 0` are interpreted as containing a perfect magnetic conductor (PMC). The PMC is applied per-field-component (i.e. `pmc.size == epsilon.size`) | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix for converting E to H.

### `` m2j

    m2j(
        omega: complex,
        dxes: dx_lists_t,
        mu: vfdfield | None = None,
    ) -> sparse.sparray

Operator for converting a magnetic current M into an electric current J. For use with eg. `e_full()`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere) | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix for converting M to J.

### `` poynting_e_cross

    poynting_e_cross(
        e: vcfdfield, dxes: dx_lists_t
    ) -> sparse.sparray

Operator for computing the staggered-grid `(E \times)` part of the Poynting vector.

On the Yee grid the E and H components live on different edges, so the electric field must be shifted by one cell in the appropriate direction before forming the discrete cross product. This sparse operator encodes that shifted cross product directly and is the matrix equivalent of `functional.poynting_e_cross_h(...)`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`e` | `vcfdfield` | Vectorized E-field for the ExH cross product | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix containing the `(E \times)` part of the staggered Poynting
`sparray` | cross product.

### `` poynting_h_cross

    poynting_h_cross(
        h: vcfdfield, dxes: dx_lists_t
    ) -> sparse.sparray

Operator for computing the staggered-grid `(H \times)` part of the Poynting vector.

Together with `poynting_e_cross(...)`, this provides the matrix form of the Yee-grid cross product used in the flux helpers. The two are related by the usual antisymmetry of the cross product,

\[ H \times E = -(E \times H), \]

once the same staggered field placement is used on both sides.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`h` | `vcfdfield` | Vectorized H-field for the HxE cross product | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix containing the `(H \times)` part of the staggered Poynting
`sparray` | cross product.

### `` e_tfsf_source

    e_tfsf_source(
        TF_region: vfdfield,
        omega: complex,
        dxes: dx_lists_t,
        epsilon: vfdfield,
        mu: vfdfield | None = None,
    ) -> sparse.sparray

Operator that turns a desired E-field distribution into a total-field/scattered-field (TFSF) source.

Let `A` be the full wave operator from `e_full(...)`, and let `Q = \mathrm{diag}(TF_region)` be the projector onto the total-field region. Then the TFSF current operator is the commutator

\[ \frac{A Q - Q A}{-i \omega}. \]

Inside regions where `Q` is locally constant, `A` and `Q` commute and the source vanishes. Only cells at the TF/SF boundary contribute nonzero current, which is exactly the desired distributed source for injecting the chosen field into the total-field region without directly forcing the scattered-field region.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`TF_region` | `vfdfield` | Mask, which is set to 1 inside the total-field region and 0 in the scattered-field region | required
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `vfdfield` | Vectorized dielectric constant | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere). | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix that turns an E-field into a current (J) distribution.

### `` e_boundary_source

    e_boundary_source(
        mask: vfdfield,
        omega: complex,
        dxes: dx_lists_t,
        epsilon: vfdfield,
        mu: vfdfield | None = None,
        periodic_mask_edges: bool = False,
    ) -> sparse.sparray

Operator that turns an E-field distrubtion into a current (J) distribution along the edges (external and internal) of the provided mask. This is just an `e_tfsf_source()` with an additional masking step.

Equivalently, this helper first constructs the TFSF commutator source for the full mask and then zeroes out all cells except the mask boundary. The boundary is defined as the set of cells whose mask value changes under a one-cell shift in any Cartesian direction. With `periodic_mask_edges=False` the shifts mirror at the domain boundary; with `True` they wrap periodically.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`mask` | `vfdfield` | The current distribution is generated at the edges of the mask, i.e. any points where shifting the mask by one cell in any direction would change its value. | required
`omega` | `complex` | Angular frequency of the simulation | required
`dxes` | `dx_lists_t` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`epsilon` | `vfdfield` | Vectorized dielectric constant | required
`mu` | `vfdfield | None` | Vectorized magnetic permeability (default 1 everywhere). | `None`

Returns:

Type | Description
--- | ---
`sparray` | Sparse matrix that turns an E-field into a current (J) distribution.

## `` meanas.fdfd.solvers

Solvers and solver interface for FDFD problems.

### `` generic

    generic(
        omega: complex,
        dxes: dx_lists_t,
        J: vcfdfield,
        epsilon: vfdfield,
        mu: vfdfield | None = None,
        *,
        pec: vfdfield | None = None,
        pmc: vfdfield | None = None,
        adjoint: bool = False,
        matrix_solver: Callable[..., ArrayLike] = _scipy_qmr,
        matrix_solver_opts: dict[str, Any] | None = None,
        E_guess: vcfdfield | None = None,
    ) -> vcfdfield_t

Conjugate gradient FDFD solver using CSR sparse matrices.

All ndarray arguments should be 1D arrays, as returned by `meanas.fdmath.vectorization.vec()`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`omega` | `complex` | Complex frequency to solve at. | required
`dxes` | `dx_lists_t` | `[[dx_e, dy_e, dz_e], [dx_h, dy_h, dz_h]]` (complex cell sizes) as discussed in `meanas.fdmath.types` | required
`J` | `vcfdfield` | Electric current distribution (at E-field locations) | required
`epsilon` | `vfdfield` | Dielectric constant distribution (at E-field locations) | required
`mu` | `vfdfield | None` | Magnetic permeability distribution (at H-field locations) | `None`
`pec` | `vfdfield | None` | Perfect electric conductor distribution (at E-field locations; non-zero value indicates PEC is present) | `None`
`pmc` | `vfdfield | None` | Perfect magnetic conductor distribution (at H-field locations; non-zero value indicates PMC is present) | `None`
`adjoint` | `bool` | If true, solves the adjoint problem. | `False`
`matrix_solver` | `Callable[..., ArrayLike]` | Called as `matrix_solver(A, b, **matrix_solver_opts) -> x`, where `A`: `scipy.sparse.csr_array`; `b`: `ArrayLike`; `x`: `ArrayLike`; Default is a wrapped version of `scipy.sparse.linalg.qmr()` which doesn't return convergence info and logs the residual every 100 iterations. | `_scipy_qmr`
`matrix_solver_opts` | `dict[str, Any] | None` | Passed as kwargs to `matrix_solver(...)` | `None`
`E_guess` | `vcfdfield | None` | Guess at the solution E-field. `matrix_solver` must accept an `x0` argument with the same purpose. | `None`

Returns:

Type | Description
--- | ---
`vcfdfield_t` | E-field which solves the system.

## `` meanas.fdfd.scpml

Functions for creating stretched coordinate perfectly matched layer (PML) absorbers.

### `` s_function_t `module-attribute`

    s_function_t = Callable[
        [NDArray[float64]], NDArray[float64]
    ]

Typedef for s-functions, see `prepare_s_function()`

### `` prepare_s_function

    prepare_s_function(
        ln_R: float = -16, m: float = 4
    ) -> s_function_t

Create an s_function to pass to the SCPML functions. This is used when you would like to customize the PML parameters.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`ln_R` | `float` | Natural logarithm of the desired reflectance | `-16`
`m` | `float` | Polynomial order for the PML (imaginary part increases as distance ** m) | `4`

Returns:

Type | Description
--- | ---
`s_function_t` | An s_function, which takes an ndarray (distances) and returns an ndarray (complex part
`s_function_t` | of the cell width; needs to be divided by `sqrt(epilon_effective) * real(omega))`
`s_function_t` | before use.

### `` uniform_grid_scpml

    uniform_grid_scpml(
        shape: Sequence[int],
        thicknesses: Sequence[int],
        omega: float,
        epsilon_effective: float = 1.0,
        s_function: s_function_t | None = None,
    ) -> list[list[NDArray[numpy.float64]]]

Create dx arrays for a uniform grid with a cell width of 1 and a pml.

If you want something more fine-grained, check out `stretch_with_scpml(...)`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`shape` | `Sequence[int]` | Shape of the grid, including the PMLs (which are 2*thicknesses thick) | required
`thicknesses` | `Sequence[int]` | `[th_x, th_y, th_z]` Thickness of the PML in each direction. Both polarities are added. Each th_ of pml is applied twice, once on each edge of the grid along the given axis. `th_*` may be zero, in which case no pml is added. | required
`omega` | `float` | Angular frequency for the simulation | required
`epsilon_effective` | `float` | Effective epsilon of the PML. Match this to the material at the edge of your grid. Default 1. | `1.0`
`s_function` | `s_function_t | None` | created by `prepare_s_function(...)`, allowing customization of pml parameters. Default uses `prepare_s_function()` with no parameters. | `None`

Returns:

Type | Description
--- | ---
`list[list[NDArray[float64]]]` | Complex cell widths (dx_lists_mut) as discussed in `meanas.fdmath.types`.

### `` stretch_with_scpml

    stretch_with_scpml(
        dxes: list[list[NDArray[float64]]],
        axis: int,
        polarity: int,
        omega: float,
        epsilon_effective: float = 1.0,
        thickness: int = 10,
        s_function: s_function_t | None = None,
    ) -> list[list[NDArray[numpy.float64]]]

Stretch dxes to contain a stretched-coordinate PML (SCPML) in one direction along one axis.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`dxes` | `list[list[NDArray[float64]]]` | Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` | required
`axis` | `int` | axis to stretch (0=x, 1=y, 2=z) | required
`polarity` | `int` | direction to stretch (-1 for -ve, +1 for +ve) | required
`omega` | `float` | Angular frequency for the simulation | required
`epsilon_effective` | `float` | Effective epsilon of the PML. Match this to the material at the edge of your grid. Default 1. | `1.0`
`thickness` | `int` | number of cells to use for pml (default 10) | `10`
`s_function` | `s_function_t | None` | Created by `prepare_s_function(...)`, allowing customization of pml parameters. Default uses `prepare_s_function()` with no parameters. | `None`

Returns:

Type | Description
--- | ---
`list[list[NDArray[float64]]]` | Complex cell widths (dx_lists_mut) as discussed in `meanas.fdmath.types`.
`list[list[NDArray[float64]]]` | Multiple calls to this function may be necessary if multiple absorpbing boundaries are needed.

## `` meanas.fdfd.farfield

Functions for performing near-to-farfield transformation (and the reverse).

### `` near_to_farfield

    near_to_farfield(
        E_near: cfdfield_t,
        H_near: cfdfield_t,
        dx: float,
        dy: float,
        padded_size: list[int] | int | None = None,
    ) -> dict[str, Any]

Compute the farfield, i.e. the distribution of the fields after propagation through several wavelengths of uniform medium.

The input fields should be complex phasors.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`E_near` | `cfdfield_t` | List of 2 ndarrays containing the 2D phasor field slices for the transverse E fields (e.g. [Ex, Ey] for calculating the farfield toward the z-direction). | required
`H_near` | `cfdfield_t` | List of 2 ndarrays containing the 2D phasor field slices for the transverse H fields (e.g. [Hx, hy] for calculating the farfield towrad the z-direction). | required
`dx` | `float` | Cell size along x-dimension, in units of wavelength. | required
`dy` | `float` | Cell size along y-dimension, in units of wavelength. | required
`padded_size` | `list[int] | int | None` | Shape of the output. A single integer `n` will be expanded to `(n, n)`. Powers of 2 are most efficient for FFT computation. Default is the smallest power of 2 larger than the input, for each axis. | `None`

Returns:

Type | Description
--- | ---
`dict[str, Any]` | Dict with keys

`dict[str, Any]`

- `E_far`: Normalized E-field farfield; multiply by (i k exp(-i k r) / (4 pi r)) to get the actual field value.

`dict[str, Any]`

- `H_far`: Normalized H-field farfield; multiply by (i k exp(-i k r) / (4 pi r)) to get the actual field value.

`dict[str, Any]`

- `kx`, `ky`: Wavevector values corresponding to the x- and y- axes in E_far and H_far, normalized to wavelength (dimensionless).

`dict[str, Any]`

- `dkx`, `dky`: step size for kx and ky, normalized to wavelength.

`dict[str, Any]`

- `theta`: arctan2(ky, kx) corresponding to each (kx, ky). This is the angle in the x-y plane, counterclockwise from above, starting from +x.

`dict[str, Any]`

- `phi`: arccos(kz / k) corresponding to each (kx, ky). This is the angle away from +z.

### `` far_to_nearfield

    far_to_nearfield(
        E_far: cfdfield_t,
        H_far: cfdfield_t,
        dkx: float,
        dky: float,
        padded_size: list[int] | int | None = None,
    ) -> dict[str, Any]

Compute the farfield, i.e. the distribution of the fields after propagation through several wavelengths of uniform medium.

The input fields should be complex phasors.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`E_far` | `cfdfield_t` | List of 2 ndarrays containing the 2D phasor field slices for the transverse E fields (e.g. [Ex, Ey] for calculating the nearfield toward the z-direction). Fields should be normalized so that E_far = E_far_actual / (i k exp(-i k r) / (4 pi r)) | required
`H_far` | `cfdfield_t` | List of 2 ndarrays containing the 2D phasor field slices for the transverse H fields (e.g. [Hx, hy] for calculating the nearfield toward the z-direction). Fields should be normalized so that H_far = H_far_actual / (i k exp(-i k r) / (4 pi r)) | required
`dkx` | `float` | kx discretization, in units of wavelength. | required
`dky` | `float` | ky discretization, in units of wavelength. | required
`padded_size` | `list[int] | int | None` | Shape of the output. A single integer `n` will be expanded to `(n, n)`. Powers of 2 are most efficient for FFT computation. Default is the smallest power of 2 larger than the input, for each axis. | `None`

Returns:

Type | Description
--- | ---
`dict[str, Any]` | Dict with keys

`dict[str, Any]`

- `E`: E-field nearfield

`dict[str, Any]`

- `H`: H-field nearfield

`dict[str, Any]`

- `dx`, `dy`: spatial discretization, normalized to wavelength (dimensionless)