waveguides¶
meanas.fdfd.waveguide_2d
¶
Operators and helper functions for waveguides with unchanging cross-section.
The propagation direction is chosen to be along the z axis, and all fields
are given an implicit z-dependence of the form exp(-1 * wavenumber * z).
As the z-dependence is known, all the functions in this file assume a 2D grid
(i.e. dxes = [[[dx_e[0], dx_e[1], ...], [dy_e[0], ...]], [[dx_h[0], ...], [dy_h[0], ...]]]).
===============
Consider Maxwell's equations in continuous space, in the frequency domain. Assuming a structure with some (x, y) cross-section extending uniformly into the z dimension, with a diagonal \(\epsilon\) tensor, we have
Expanding the first two equations into vector components, we get
Substituting in our expressions for \(\vec{E}\), \(\vec{H}\) and discretizing:
Rewrite the last three equations as
Now apply \(\imath \beta \tilde{\partial}_x\) to the last equation, then substitute in for \(\imath \beta H_x\) and \(\imath \beta H_y\):
With a similar approach (but using \(\imath \beta \tilde{\partial}_y\) instead), we can get
We can combine this equation for \(\imath \beta \tilde{\partial}_y E_z\) with the unused \(\imath \omega \mu_{xx} H_x\) and \(\imath \omega \mu_{yy} H_y\) equations to get
and
However, based on our rewritten equation for \(\imath \beta H_x\) and the so-far unused equation for \(\imath \omega \mu_{zz} H_z\) we can also write
and, similarly,
By combining both pairs of expressions, we get
Using these, we can construct the eigenvalue problem
In the literature, \(\beta\) is usually used to denote the lossless/real part of the propagation constant,
but in meanas it is allowed to be complex.
An equivalent eigenvalue problem can be formed using the \(H_x\) and \(H_y\) fields, if those are more convenient.
Note that \(E_z\) was never discretized, so \(\beta\) will need adjustment to account for numerical dispersion if the result is introduced into a space with a discretized z-axis.
operator_e
¶
operator_e(
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> sparse.sparray
Waveguide operator of the form
omega**2 * mu * epsilon +
mu * [[-Dy], [Dx]] / mu * [-Dy, Dx] +
[[Dx], [Dy]] / epsilon * [Dx, Dy] * epsilon
for use with a field vector of the form cat([E_x, E_y]).
More precisely, the operator is
\(\tilde{\partial}_x\) and \(\hat{\partial}_x\) are the forward and backward derivatives along x, and each \(\epsilon_{xx}\), \(\mu_{yy}\), etc. is a diagonal matrix containing the vectorized material property distribution.
This operator can be used to form an eigenvalue problem of the form
operator_e(...) @ [E_x, E_y] = wavenumber**2 * [E_x, E_y]
which can then be solved for the eigenmodes of the system (an exp(-i * wavenumber * z)
z-dependence is assumed for the fields).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
omega
|
complex
|
The angular frequency of the system. |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
operator_h
¶
operator_h(
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> sparse.sparray
Waveguide operator of the form
omega**2 * epsilon * mu +
epsilon * [[-Dy], [Dx]] / epsilon * [-Dy, Dx] +
[[Dx], [Dy]] / mu * [Dx, Dy] * mu
for use with a field vector of the form cat([H_x, H_y]).
More precisely, the operator is
\(\tilde{\partial}_x\) and \(\hat{\partial}_x\) are the forward and backward derivatives along x, and each \(\epsilon_{xx}\), \(\mu_{yy}\), etc. is a diagonal matrix containing the vectorized material property distribution.
This operator can be used to form an eigenvalue problem of the form
operator_h(...) @ [H_x, H_y] = wavenumber**2 * [H_x, H_y]
which can then be solved for the eigenmodes of the system (an exp(-i * wavenumber * z)
z-dependence is assumed for the fields).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
omega
|
complex
|
The angular frequency of the system. |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
normalized_fields_e
¶
normalized_fields_e(
e_xy: vcfdfield2,
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
prop_phase: float = 0,
) -> tuple[vcfdslice_t, vcfdslice_t]
Given a vector e_xy containing the vectorized E_x and E_y fields,
returns normalized, vectorized E and H fields for the system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e_xy
|
vcfdfield2
|
Vector containing E_x and E_y fields |
required |
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
prop_phase
|
float
|
Phase shift |
0
|
Returns:
| Type | Description |
|---|---|
vcfdslice_t
|
|
vcfdslice_t
|
and contains all three vector components. |
Notes
e_xy is only the transverse electric eigenvector. This helper first
reconstructs the full three-component E and H fields with exy2e(...)
and exy2h(...), then normalizes them to unit forward power using
_normalized_fields(...).
The normalization target is
so the returned fields represent a unit-power forward mode under the discrete Yee-grid Poynting inner product.
normalized_fields_h
¶
normalized_fields_h(
h_xy: vcfdfield2,
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
prop_phase: float = 0,
) -> tuple[vcfdslice_t, vcfdslice_t]
Given a vector h_xy containing the vectorized H_x and H_y fields,
returns normalized, vectorized E and H fields for the system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
h_xy
|
vcfdfield2
|
Vector containing H_x and H_y fields |
required |
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
prop_phase
|
float
|
Phase shift |
0
|
Returns:
| Type | Description |
|---|---|
vcfdslice_t
|
|
vcfdslice_t
|
and contains all three vector components. |
Notes
This is the H_x/H_y analogue of normalized_fields_e(...). The final
normalized mode should describe the same physical solution, but because
the overall complex phase and sign are chosen heuristically,
normalized_fields_e(...) and normalized_fields_h(...) need not return
identical representatives for nearly symmetric modes.
exy2h
¶
exy2h(
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> sparse.sparray
Operator which transforms the vector e_xy containing the vectorized E_x and E_y fields,
into a vectorized H containing all three H components
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representing the operator. |
hxy2e
¶
hxy2e(
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> sparse.sparray
Operator which transforms the vector h_xy containing the vectorized H_x and H_y fields,
into a vectorized E containing all three E components
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representing the operator. |
hxy2h
¶
Operator which transforms the vector h_xy containing the vectorized H_x and H_y fields,
into a vectorized H containing all three H components
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representing the operator. |
exy2e
¶
Operator which transforms the vector e_xy containing the vectorized E_x and E_y fields,
into a vectorized E containing all three E components
From the operator derivation (see module docs), we have
as well as the intermediate equations
Combining these, we get
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representing the operator. |
e2h
¶
e2h(
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
mu: vfdslice | None = None,
) -> sparse.sparray
Returns an operator which, when applied to a vectorized E eigenfield, produces the vectorized H eigenfield slice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
h2e
¶
Returns an operator which, when applied to a vectorized H eigenfield, produces the vectorized E eigenfield slice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
curl_e
¶
Discretized curl operator for use with the waveguide E field slice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
curl_h
¶
Discretized curl operator for use with the waveguide H field slice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
h_err
¶
h_err(
h: vcfdslice,
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> float
Calculates the relative error in the H field
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
h
|
vcfdslice
|
Vectorized H field |
required |
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Relative error |
e_err
¶
e_err(
e: vcfdslice,
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> float
Calculates the relative error in the E field
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e
|
vcfdslice
|
Vectorized E field |
required |
wavenumber
|
complex
|
Wavenumber assuming fields have z-dependence of |
required |
omega
|
complex
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Relative error |
sensitivity
¶
sensitivity(
e_norm: vcfdslice,
h_norm: vcfdslice,
wavenumber: complex,
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> vcfdslice_t
Given a waveguide structure (dxes, epsilon, mu) and mode fields
(e_norm, h_norm, wavenumber, omega), calculates the sensitivity of the wavenumber
\(\beta\) to changes in the dielectric structure \(\epsilon\).
The output is a vector of the same size as vec(epsilon), with each element specifying the
sensitivity of wavenumber to changes in the corresponding element in vec(epsilon), i.e.
An adjoint approach is used to calculate the sensitivity; the derivation is provided here:
Starting with the eigenvalue equation
where \(A_E\) is the waveguide operator from operator_e(), and \(E_{xy} = \begin{bmatrix} E_x \\
E_y \end{bmatrix}\),
we can differentiate with respect to one of the \(\epsilon\) elements (i.e. at one Yee grid point), \(\epsilon_i\):
We then multiply by \(H_{yx}^\star = \begin{bmatrix}H_y^\star \\ -H_x^\star \end{bmatrix}\) from the left:
However, \(H_{yx}^\star\) is actually a left-eigenvector of \(A_E\). This can be verified by inspecting
the form of operator_h (\(A_H\)) and comparing its conjugate transpose to operator_e (\(A_E\)). Also, note
\(H_{yx}^\star \cdot E_{xy} = H^\star \times E\) recalls the mode orthogonality relation. See doi:10.5194/ars-9-85-201
for a similar approach. Therefore,
and we can simplify to
This expression can be quickly calculated for all \(i\) by writing out the various terms of \(\partial_{\epsilon_i} A_E\) and recognizing that the vector-matrix-vector products (i.e. scalars) \(sens_i = \vec{v}_{left} \partial_{\epsilon_i} (\epsilon_{xyz}) \vec{v}_{right}\), indexed by \(i\), can be expressed as elementwise multiplications \(\vec{sens} = \vec{v}_{left} \star \vec{v}_{right}\)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e_norm
|
vcfdslice
|
Normalized, vectorized E_xyz field for the mode. E.g. as returned by |
required |
h_norm
|
vcfdslice
|
Normalized, vectorized H_xyz field for the mode. E.g. as returned by |
required |
wavenumber
|
complex
|
Propagation constant for the mode. The z-axis is assumed to be continuous (i.e. without numerical dispersion). |
required |
omega
|
complex
|
The angular frequency of the system. |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
vcfdslice_t
|
Sparse matrix representation of the operator. |
solve_modes
¶
solve_modes(
mode_numbers: Sequence[int],
omega: complex,
dxes: dx_lists2_t,
epsilon: vfdslice,
mu: vfdslice | None = None,
mode_margin: int = 2,
) -> tuple[
NDArray[numpy.complex128], NDArray[numpy.complex128]
]
Given a 2D region, attempts to solve for the eigenmode with the specified mode numbers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode_numbers
|
Sequence[int]
|
List of 0-indexed mode numbers to solve for |
required |
omega
|
complex
|
Angular frequency of the simulation |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Dielectric constant |
required |
mu
|
vfdslice | None
|
Magnetic permeability (default 1 everywhere) |
None
|
mode_margin
|
int
|
The eigensolver will actually solve for |
2
|
Returns:
| Name | Type | Description |
|---|---|---|
e_xys |
NDArray[complex128]
|
NDArray of vfdfield_t specifying fields. First dimension is mode number. |
wavenumbers |
NDArray[complex128]
|
list of wavenumbers |
solve_mode
¶
Wrapper around solve_modes() that solves for a single mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode_number
|
int
|
0-indexed mode number to solve for |
required |
*args
|
Any
|
passed to |
()
|
**kwargs
|
Any
|
passed to |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[vcfdfield2_t, complex]
|
(e_xy, wavenumber) |
inner_product
¶
inner_product(
e1: vcfdfield2,
h2: vcfdfield2,
dxes: dx_lists2_t,
prop_phase: float = 0,
conj_h: bool = False,
trapezoid: bool = False,
) -> complex
Compute the discrete waveguide overlap / Poynting inner product.
This is the 2D transverse integral corresponding to the time-averaged longitudinal Poynting flux,
with the Yee-grid staggering and optional propagation-phase adjustment used by the waveguide helpers in this module.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e1
|
vcfdfield2
|
Vectorized electric field, typically from |
required |
h2
|
vcfdfield2
|
Vectorized magnetic field, typically from |
required |
dxes
|
dx_lists2_t
|
Two-dimensional Yee-grid spacing lists |
required |
prop_phase
|
float
|
Phase advance over one propagation cell. This is used to shift the H field into the same longitudinal reference plane as the E field. |
0
|
conj_h
|
bool
|
Whether to conjugate |
False
|
trapezoid
|
bool
|
Whether to use trapezoidal quadrature instead of the default rectangular Yee-cell sum. |
False
|
Returns:
| Type | Description |
|---|---|
complex
|
Complex overlap / longitudinal power integral. |
meanas.fdfd.waveguide_3d
¶
Tools for working with waveguide modes in 3D domains.
This module relies heavily on waveguide_2d and mostly just transforms
its parameters into 2D equivalents and expands the results back into 3D.
The intended workflow is:
- Select a single-cell slice normal to the propagation axis.
- Solve the corresponding 2D mode problem with
solve_mode(...). - Turn that mode into a one-sided source with
compute_source(...). - Build an overlap window with
compute_overlap_e(...)for port readout.
polarity is part of the public convention throughout this module:
+1means forward propagation toward increasing index alongaxis-1means backward propagation toward decreasing index alongaxis
That same convention controls which side of the selected slice is used for the overlap window and how the expanded field is phased.
solve_mode
¶
solve_mode(
mode_number: int,
omega: complex,
dxes: dx_lists_t,
axis: int,
polarity: int,
slices: Sequence[slice],
epsilon: fdfield,
mu: fdfield | None = None,
) -> dict[str, complex | NDArray[complexfloating]]
Given a 3D grid, selects a slice from the grid and attempts to solve for an eigenmode propagating through that slice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode_number
|
int
|
Number of the mode, 0-indexed |
required |
omega
|
complex
|
Angular frequency of the simulation |
required |
dxes
|
dx_lists_t
|
Grid parameters |
required |
axis
|
int
|
Propagation axis (0=x, 1=y, 2=z) |
required |
polarity
|
int
|
Propagation direction (+1 for +ve, -1 for -ve) |
required |
slices
|
Sequence[slice]
|
|
required |
epsilon
|
fdfield
|
Dielectric constant |
required |
mu
|
fdfield | None
|
Magnetic permeability (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, complex | NDArray[complexfloating]]
|
Dictionary containing: |
dict[str, complex | NDArray[complexfloating]]
|
|
dict[str, complex | NDArray[complexfloating]]
|
|
dict[str, complex | NDArray[complexfloating]]
|
|
dict[str, complex | NDArray[complexfloating]]
|
|
Notes
The returned fields are normalized through the waveguide_2d
normalization convention before being expanded back to 3D.
compute_source
¶
compute_source(
E: cfdfield,
wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
axis: int,
polarity: int,
slices: Sequence[slice],
epsilon: fdfield,
mu: fdfield | None = None,
) -> cfdfield_t
Given an eigenmode obtained by solve_mode, returns the current source distribution
necessary to position a unidirectional source at the slice location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
E
|
cfdfield
|
E-field of the mode |
required |
wavenumber
|
complex
|
Wavenumber of the mode |
required |
omega
|
complex
|
Angular frequency of the simulation |
required |
dxes
|
dx_lists_t
|
Grid parameters |
required |
axis
|
int
|
Propagation axis (0=x, 1=y, 2=z) |
required |
polarity
|
int
|
Propagation direction (+1 for +ve, -1 for -ve) |
required |
slices
|
Sequence[slice]
|
|
required |
mu
|
fdfield | None
|
Magnetic permeability (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
cfdfield_t
|
|
Notes
The source is built from the expanded mode field and a boundary-source operator. The resulting current is intended to be injected with the same sign convention used elsewhere in the package:
E -= dt * J / epsilon
compute_overlap_e
¶
compute_overlap_e(
E: cfdfield_t,
wavenumber: complex,
dxes: dx_lists_t,
axis: int,
polarity: int,
slices: Sequence[slice],
omega: float,
) -> cfdfield_t
Build an overlap field for projecting another 3D electric field onto a mode.
The returned field is intended for the discrete overlap expression
where the sum is over the full Yee-grid field storage.
The construction uses a two-cell window immediately upstream of the selected slice:
- for
polarity=+1, the two cells just beforeslices[axis].start - for
polarity=-1, the two cells just afterslices[axis].stop
The window is clipped to the simulation domain if necessary. A clipped but
non-empty window raises RuntimeWarning; an empty window raises
ValueError.
The derivation below assumes reflection symmetry and the standard waveguide overlap relation involving
E x H_mode + E_mode x H -> Ex Hmy - EyHmx + Emx Hy - Emy Hx (Z-prop) Ex we/B Emx + Ex i/B dy Hmz - Ey (-we/B Emy) - Ey i/B dx Hmz we/B (Ex Emx + Ey Emy) + i/B (Ex dy Hmz - Ey dx Hmz) we/B (Ex Emx + Ey Emy) + i/B (Ex dy (dx Emy - dy Emx) - Ey dx (dx Emy - dy Emx)) we/B (Ex Emx + Ey Emy) + i/B (Ex dy dx Emy - Ex dy dy Emx - Ey dx dx Emy - Ey dx dy Emx)
Ex j/wu (-jB Emx - dx Emz) - Ey j/wu (dy Emz + jB Emy) B/wu (Ex Emx + Ey Emy) - j/wu (Ex dx Emz + Ey dy Emz)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
E
|
cfdfield_t
|
E-field of the mode |
required |
wavenumber
|
complex
|
Wavenumber of the mode |
required |
dxes
|
dx_lists_t
|
Grid parameters |
required |
axis
|
int
|
Propagation axis (0=x, 1=y, 2=z) |
required |
polarity
|
int
|
Propagation direction (+1 for +ve, -1 for -ve) |
required |
slices
|
Sequence[slice]
|
|
required |
Returns:
| Type | Description |
|---|---|
cfdfield_t
|
|
cfdfield_t
|
over the retained overlap window. |
expand_e
¶
expand_e(
E: cfdfield,
wavenumber: complex,
dxes: dx_lists_t,
axis: int,
polarity: int,
slices: Sequence[slice],
) -> cfdfield_t
Given an eigenmode obtained by solve_mode, expands the E-field from the 2D
slice where the mode was calculated to the entire domain (along the propagation
axis). This assumes the epsilon cross-section remains constant throughout the
entire domain; it is up to the caller to truncate the expansion to any regions
where it is valid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
E
|
cfdfield
|
E-field of the mode |
required |
wavenumber
|
complex
|
Wavenumber of the mode |
required |
dxes
|
dx_lists_t
|
Grid parameters |
required |
axis
|
int
|
Propagation axis (0=x, 1=y, 2=z) |
required |
polarity
|
int
|
Propagation direction (+1 for +ve, -1 for -ve) |
required |
slices
|
Sequence[slice]
|
|
required |
Returns:
| Type | Description |
|---|---|
cfdfield_t
|
|
Notes
This helper assumes that the waveguide cross-section remains constant along the propagation axis and applies the phase factor
to each copied slice.
meanas.fdfd.waveguide_cyl
¶
Operators and helper functions for cylindrical waveguides with unchanging cross-section.
Waveguide operator is derived according to 10.1364/OL.33.001848.
As in waveguide_2d, the propagation dependence is separated from the
transverse solve. Here the propagation coordinate is the bend angle \theta,
and the fields are assumed to have the form
where m is the angular wavenumber returned by solve_mode(s). It is often
convenient to introduce the corresponding linear wavenumber
so that the cylindrical problem resembles the straight-waveguide problem with additional metric factors.
Those metric factors live on the staggered radial Yee grids. If the left edge of
the computational window is at r = r_{\min}, define the electric-grid and
magnetic-grid radial sample locations by
and from them the diagonal metric matrices
With the same forward/backward derivative notation used in waveguide_2d, the
coordinate-transformed discrete curl equations used here are
The first three equations are the cylindrical analogue of the straight-guide
relations for H_r, H_y, and H_z. The next two are the metric-weighted
versions of the straight-guide identities for \imath \beta H_y and
\imath \beta H_r, and the last equation plays the same role as the
longitudinal E_z reconstruction in waveguide_2d.
Following the same elimination steps as in waveguide_2d, apply
\imath \beta \tilde{\partial}_r and \imath \beta \tilde{\partial}_y to the
equation for E_z, substitute for \imath \beta H_r and \imath \beta H_y,
and then eliminate H_z with
This yields the transverse electric eigenproblem implemented by
cylindrical_operator(...):
Since \beta = m / r_{\min}, the solver implemented in this file returns the
angular wavenumber m, while the operator itself is most naturally written in
terms of the linear quantity \beta. The helpers below reconstruct the full
field components from the solved transverse eigenvector and then normalize the
mode to unit forward power with the same discrete longitudinal Poynting inner
product used by waveguide_2d.
As in the straight-waveguide case, all functions here assume a 2D grid:
dxes = [[[dr_e_0, dr_e_1, ...], [dy_e_0, ...]], [[dr_h_0, ...], [dy_h_0, ...]]].
cylindrical_operator
¶
cylindrical_operator(
omega: float,
dxes: dx_lists2_t,
epsilon: vfdslice,
rmin: float,
) -> sparse.sparray
Cylindrical coordinate waveguide operator of the form
for use with a field vector of the form [E_r, E_y].
This operator can be used to form an eigenvalue problem of the form A @ [E_r, E_y] = beta**2 * [E_r, E_y]
which can then be solved for the eigenmodes of the system
(an exp(-i * angular_wavenumber * theta) theta-dependence is assumed for
the fields, with beta = angular_wavenumber / rmin).
(NOTE: See module docs and 10.1364/OL.33.001848)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
omega
|
float
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator |
solve_modes
¶
solve_modes(
mode_numbers: Sequence[int],
omega: float,
dxes: dx_lists2_t,
epsilon: vfdslice,
rmin: float,
mode_margin: int = 2,
) -> tuple[
NDArray[numpy.complex128], NDArray[numpy.complex128]
]
Given a 2d (r, y) slice of epsilon, attempts to solve for the eigenmode of the bent waveguide with the specified mode number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode_numbers
|
Sequence[int]
|
Mode numbers to solve, 0-indexed. |
required |
omega
|
float
|
Angular frequency of the simulation |
required |
dxes
|
dx_lists2_t
|
Grid parameters [dx_e, dx_h] as described in meanas.fdmath.types. The first coordinate is assumed to be r, the second is y. |
required |
epsilon
|
vfdslice
|
Dielectric constant |
required |
rmin
|
float
|
Radius of curvature for the simulation. This should be the minimum value of r within the simulation domain. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
e_xys |
NDArray[complex128]
|
NDArray of vfdfield_t specifying fields. First dimension is mode number. |
angular_wavenumbers |
NDArray[complex128]
|
list of wavenumbers in 1/rad units. |
solve_mode
¶
Wrapper around solve_modes() that solves for a single mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode_number
|
int
|
0-indexed mode number to solve for |
required |
*args
|
Any
|
passed to |
()
|
**kwargs
|
Any
|
passed to |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[vcfdslice, complex]
|
(e_xy, angular_wavenumber) |
linear_wavenumbers
¶
linear_wavenumbers(
e_xys: list[vcfdfield2_t],
angular_wavenumbers: ArrayLike,
epsilon: vfdslice,
dxes: dx_lists2_t,
rmin: float,
) -> NDArray[numpy.complex128]
Calculate linear wavenumbers (1/distance) based on angular wavenumbers (1/rad) and the mode's energy distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e_xys
|
list[vcfdfield2_t]
|
Vectorized mode fields with shape (num_modes, 2 * x *y) |
required |
angular_wavenumbers
|
ArrayLike
|
Wavenumbers assuming fields have theta-dependence of
|
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid with shape (3, x, y) |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex128]
|
NDArray containing the calculated linear (1/distance) wavenumbers |
exy2h
¶
exy2h(
angular_wavenumber: complex,
omega: float,
dxes: dx_lists2_t,
rmin: float,
epsilon: vfdslice,
mu: vfdslice | None = None,
) -> sparse.sparray
Operator which transforms the vector e_xy containing the vectorized E_r and E_y fields,
into a vectorized H containing all three H components
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
angular_wavenumber
|
complex
|
Wavenumber assuming fields have theta-dependence of
|
required |
omega
|
float
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representing the operator. |
exy2e
¶
exy2e(
angular_wavenumber: complex,
omega: float,
dxes: dx_lists2_t,
rmin: float,
epsilon: vfdslice,
) -> sparse.sparray
Operator which transforms the vector e_xy containing the vectorized E_r and E_y fields,
into a vectorized E containing all three E components
Unlike the straight waveguide case, the H_z components do not cancel and must be calculated from E_r and E_y in order to then calculate E_z.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
angular_wavenumber
|
complex
|
Wavenumber assuming fields have theta-dependence of
|
required |
omega
|
float
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representing the operator. |
e2h
¶
e2h(
angular_wavenumber: complex,
omega: float,
dxes: dx_lists2_t,
rmin: float,
mu: vfdslice | None = None,
) -> sparse.sparray
Returns an operator which, when applied to a vectorized E eigenfield, produces the vectorized H eigenfield.
This operator is created directly from the initial coordinate-transformed equations:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
angular_wavenumber
|
complex
|
Wavenumber assuming fields have theta-dependence of
|
required |
omega
|
float
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
Returns:
| Type | Description |
|---|---|
sparray
|
Sparse matrix representation of the operator. |
dxes2T
¶
Construct the cylindrical metric matrices \(T_a\) and \(T_b\).
T_a is sampled on the E-grid radial locations, while T_b is sampled on
the staggered H-grid radial locations. These are the diagonal matrices that
convert the straight-waveguide algebra into its cylindrical counterpart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dxes
|
dx_lists2_t
|
Grid parameters |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
Returns:
| Type | Description |
|---|---|
tuple[NDArray[float64], NDArray[float64]]
|
Sparse diagonal matrices |
normalized_fields_e
¶
normalized_fields_e(
e_xy: vcfdfield2,
angular_wavenumber: complex,
omega: float,
dxes: dx_lists2_t,
rmin: float,
epsilon: vfdslice,
mu: vfdslice | None = None,
prop_phase: float = 0,
) -> tuple[vcfdslice_t, vcfdslice_t]
Given a vector e_xy containing the vectorized E_r and E_y fields,
returns normalized, vectorized E and H fields for the system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e_xy
|
vcfdfield2
|
Vector containing E_r and E_y fields |
required |
angular_wavenumber
|
complex
|
Wavenumber assuming fields have theta-dependence of
|
required |
omega
|
float
|
The angular frequency of the system |
required |
dxes
|
dx_lists2_t
|
Grid parameters |
required |
rmin
|
float
|
Radius at the left edge of the simulation domain (at minimum 'x') |
required |
epsilon
|
vfdslice
|
Vectorized dielectric constant grid |
required |
mu
|
vfdslice | None
|
Vectorized magnetic permeability grid (default 1 everywhere) |
None
|
prop_phase
|
float
|
Phase shift |
0
|
Returns:
| Type | Description |
|---|---|
vcfdslice_t
|
|
vcfdslice_t
|
and contains all three vector components. |
Notes
The normalization step is delegated to _normalized_fields(...), which
enforces unit forward power under the discrete inner product
The angular wavenumber m is first converted into the full three-component
fields, then the overall complex phase and sign are fixed so the result is
reproducible for symmetric modes.