diff --git a/.flake8 b/.flake8 deleted file mode 100644 index e18673e..0000000 --- a/.flake8 +++ /dev/null @@ -1,26 +0,0 @@ -[flake8] -ignore = - # E501 line too long - E501, - # W391 newlines at EOF - W391, - # E241 multiple spaces after comma - E241, - # E302 expected 2 newlines - E302, - # W503 line break before binary operator (to be deprecated) - W503, - # E265 block comment should start with '# ' - E265, - # E123 closing bracket does not match indentation of opening bracket's line - E123, - # E124 closing bracket does not match visual indentation - E124, - # E221 multiple spaces before operator - E221, - # E201 whitespace after '[' - E201, - -per-file-ignores = - # F401 import without use - */__init__.py: F401, diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f06c106..0000000 --- a/.gitignore +++ /dev/null @@ -1,73 +0,0 @@ -# ---> Python -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# documentation -doc/ -site/ -_doc_mathimg/ -doc.md -doc.htex - -# PyBuilder -target/ - - -.idea/ -.mypy_cache/ - - -.*.sw[op] - -*.svg -*.html diff --git a/meanas/py.typed b/.nojekyll similarity index 100% rename from meanas/py.typed rename to .nojekyll diff --git a/404.html b/404.html new file mode 100644 index 0000000..0c72acb --- /dev/null +++ b/404.html @@ -0,0 +1,614 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + meanas.eigensolvers
+
+
+¶Solvers for eigenvalue / eigenvector problems
+ + + + + + + + + + + power_iteration
+
+
+¶power_iteration(
+ operator: spmatrix,
+ guess_vector: NDArray[complex128] | None = None,
+ iterations: int = 20,
+) -> tuple[complex, NDArray[numpy.complex128]]
+Use power iteration to estimate the dominant eigenvector of a matrix.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ operator
+ |
+
+ spmatrix
+ |
+
+
+
+ Matrix to analyze. + |
+ + required + | +
+ guess_vector
+ |
+
+ NDArray[complex128] | None
+ |
+
+
+
+ Starting point for the eigenvector. Default is a randomly chosen vector. + |
+
+ None
+ |
+
+ iterations
+ |
+
+ int
+ |
+
+
+
+ Number of iterations to perform. Default 20. + |
+
+ 20
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ tuple[complex, NDArray[complex128]]
+ |
+
+
+
+ (Largest-magnitude eigenvalue, Corresponding eigenvector estimate) + |
+
rayleigh_quotient_iteration
+
+
+¶rayleigh_quotient_iteration(
+ operator: spmatrix | LinearOperator,
+ guess_vector: NDArray[complex128],
+ iterations: int = 40,
+ tolerance: float = 1e-13,
+ solver: Callable[..., NDArray[complex128]]
+ | None = None,
+) -> tuple[complex, NDArray[numpy.complex128]]
+Use Rayleigh quotient iteration to refine an eigenvector guess.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ operator
+ |
+
+ spmatrix | LinearOperator
+ |
+
+
+
+ Matrix to analyze. + |
+ + required + | +
+ guess_vector
+ |
+
+ NDArray[complex128]
+ |
+
+
+
+ Eigenvector to refine. + |
+ + required + | +
+ iterations
+ |
+
+ int
+ |
+
+
+
+ Maximum number of iterations to perform. Default 40. + |
+
+ 40
+ |
+
+ tolerance
+ |
+
+ float
+ |
+
+
+
+ Stop iteration if |
+
+ 1e-13
+ |
+
+ solver
+ |
+
+ Callable[..., NDArray[complex128]] | None
+ |
+
+
+
+ Solver function of the form |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ tuple[complex, NDArray[complex128]]
+ |
+
+
+
+ (eigenvalues, eigenvectors) + |
+
signed_eigensolve
+
+
+¶signed_eigensolve(
+ operator: spmatrix | LinearOperator,
+ how_many: int,
+ negative: bool = False,
+) -> tuple[
+ NDArray[numpy.complex128], NDArray[numpy.complex128]
+]
+Find the largest-magnitude positive-only (or negative-only) eigenvalues and + eigenvectors of the provided matrix.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ operator
+ |
+
+ spmatrix | LinearOperator
+ |
+
+
+
+ Matrix to analyze. + |
+ + required + | +
+ how_many
+ |
+
+ int
+ |
+
+
+
+ How many eigenvalues to find. + |
+ + required + | +
+ negative
+ |
+
+ bool
+ |
+
+
+
+ Whether to find negative-only eigenvalues. + Default False (positive only). + |
+
+ False
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ NDArray[complex128]
+ |
+
+
+
+ (sorted list of eigenvalues, 2D ndarray of corresponding eigenvectors) + |
+
+ NDArray[complex128]
+ |
+
+
+
+
|
+
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
resulting in
+Maxwell's equations are then
+With \(\Delta_t \to 0\), this simplifies to
+and then
+ 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 |
+ + required + | +
+ epsilon
+ |
+
+ fdfield
+ |
+
+
+
+ Dielectric constant + |
+ + required + | +
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability (default 1 everywhere) + |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ cfdfield_updater_t
+ |
+
+
+
+ Function |
+
+ cfdfield_updater_t
+ |
+
+
+
+
|
+
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 |
+ + 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 |
+
+ Callable[[cfdfield, cfdfield], tuple[cfdfield_t, cfdfield_t]]
+ |
+
+
+
+
|
+
e2h
+
+
+¶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 |
+ + required + | +
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability (default 1 everywhere) + |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ cfdfield_updater_t
+ |
+
+
+
+ Function |
+
+ cfdfield_updater_t
+ |
+
+
+
+
|
+
m2j
+
+
+¶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 |
+ + required + | +
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability (default 1 everywhere) + |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ cfdfield_updater_t
+ |
+
+
+
+ Function |
+
+ cfdfield_updater_t
+ |
+
+
+
+
|
+
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
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. |
+ + required + | +
+ omega
+ |
+
+ complex
+ |
+
+
+
+ Angular frequency of the simulation + |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t
+ |
+
+
+
+ Grid parameters |
+ + required + | +
+ epsilon
+ |
+
+ fdfield
+ |
+
+
+
+ Dielectric constant distribution + |
+ + required + | +
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability (default 1 everywhere) + |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ cfdfield_updater_t
+ |
+
+
+
+ Function |
+
+ cfdfield_updater_t
+ |
+
+
+
+
|
+
poynting_e_cross_h
+
+
+¶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(...).
This function also shifts the input E field by one cell as required
+for computing the Poynting cross product (see meanas.fdfd module docs).
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 |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ Callable[[cfdfield, cfdfield], cfdfield_t]
+ |
+
+
+
+ Function |
+
+ Callable[[cfdfield, cfdfield], cfdfield_t]
+ |
+
+
+
+ For time-average power, call it as |
+
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_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 + +
del x (1/mu * del x) - omega**2 * epsilon
+for use with the E-field, with wave equation + +
(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 |
+ + 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 |
+
+ None
+ |
+
+ pmc
+ |
+
+ vfdfield | None
+ |
+
+
+
+ Vectorized mask specifying PMC cells. Any cells where |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix containing the wave operator. + |
+
e_full_preconditioners
+
+
+¶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 |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ tuple[sparray, sparray]
+ |
+
+
+
+ Preconditioner matrices |
+
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 + +
del x (1/epsilon * del x) - omega**2 * mu
+for use with the H-field, with wave equation + +
(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 |
+ + 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 |
+
+ None
+ |
+
+ pmc
+ |
+
+ vfdfield | None
+ |
+
+
+
+ Vectorized mask specifying PMC cells. Any cells where |
+
+ 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
[[-i * omega * epsilon, del x ],
+ [del x, i * omega * mu]]
+for use with a field vector of the form cat(vec(E), vec(H)):
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ omega
+ |
+
+ complex
+ |
+
+
+
+ Angular frequency of the simulation + |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t
+ |
+
+
+
+ Grid parameters |
+ + 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 |
+
+ None
+ |
+
+ pmc
+ |
+
+ vfdfield | None
+ |
+
+
+
+ Vectorized mask specifying PMC cells. Any cells where |
+
+ 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 |
+ + required + | +
+ mu
+ |
+
+ vfdfield | None
+ |
+
+
+
+ Vectorized magnetic permeability (default 1 everywhere) + |
+
+ None
+ |
+
+ pmc
+ |
+
+ vfdfield | None
+ |
+
+
+
+ Vectorized mask specifying PMC cells. Any cells where |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for converting E to H. + |
+
m2j
+
+
+¶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 |
+ + 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
+
+
+¶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 |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix containing the |
+
+ sparray
+ |
+
+
+
+ cross product. + |
+
poynting_h_cross
+
+
+¶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,
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 |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix containing the |
+
+ 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
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 |
+ + 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 |
+ + 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
+ |
+
+
+
+
|
+ + 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 |
+
+ _scipy_qmr
+ |
+
+ matrix_solver_opts
+ |
+
+ dict[str, Any] | None
+ |
+
+
+
+ Passed as kwargs to |
+
+ None
+ |
+
+ E_guess
+ |
+
+ vcfdfield | None
+ |
+
+
+
+ Guess at the solution E-field. |
+
+ 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
+
+
+¶Typedef for s-functions, see prepare_s_function()
prepare_s_function
+
+
+¶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 |
+
+ 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]
+ |
+
+
+
+
|
+ + 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 |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ list[list[NDArray[float64]]]
+ |
+
+
+
+ Complex cell widths (dx_lists_mut) as discussed in |
+
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 |
+ + 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 |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ list[list[NDArray[float64]]]
+ |
+
+
+
+ Complex cell widths (dx_lists_mut) as discussed in |
+
+ 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: transverse_slice_pair,
+ H_near: transverse_slice_pair,
+ dx: float,
+ dy: float,
+ padded_size: Sequence[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
+ |
+
+ transverse_slice_pair
+ |
+
+
+
+ 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
+ |
+
+ transverse_slice_pair
+ |
+
+
+
+ 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
+ |
+
+ Sequence[int] | int | None
+ |
+
+
+
+ Shape of the output. A single integer |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ dict[str, Any]
+ |
+
+
+
+ Dict with keys + |
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
far_to_nearfield
+
+
+¶far_to_nearfield(
+ E_far: transverse_slice_pair,
+ H_far: transverse_slice_pair,
+ dkx: float,
+ dky: float,
+ padded_size: Sequence[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
+ |
+
+ transverse_slice_pair
+ |
+
+
+
+ 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
+ |
+
+ transverse_slice_pair
+ |
+
+
+
+ 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
+ |
+
+ Sequence[int] | int | None
+ |
+
+
+
+ Shape of the output. A single integer |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ dict[str, Any]
+ |
+
+
+
+ Dict with keys + |
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
meanas.fdmath
+
+
+¶Basic discrete calculus for finite difference (fd) simulations.
+Discrete fields are stored in one of two forms:
+fdfield_t form is a multidimensional numpy.NDArrayU[m, n, p], where m, n, and p are
+ discrete indices referring to positions on the x, y, and z axes respectively.E[:, m, n, p] = [Ex[m, n, p], Ey[m, n, p], Ez[m, n, p]].vfdfield_t form is simply a vectorzied (i.e. 1D) version of the fdfield_t,
+ as obtained by meanas.fdmath.vectorization.vec (effectively just numpy.ravel)meanas.fdmath.functional.
+ The generated functions act on fields in the fdfield_t form.scipy.sparse, created
+ by meanas.fdmath.operators. These operators act on vectorized fields in the
+ vfdfield_t form.The operations performed should be equivalent: functional.op(*args)(E) should be
+equivalent to unvec(operators.op(*args) @ vec(E), E.shape[1:]).
Generally speaking the field_t form is easier to work with, but can be harder or less
+efficient to compose (e.g. it is easy to generate a single matrix by multiplying a
+series of other matrices).
This documentation and approach is roughly based on W.C. Chew's excellent +"Electromagnetic Theory on a Lattice" (doi:10.1063/1.355770), +which covers a superset of this material with similar notation and more detail.
+Define the discrete forward derivative as + +
where \(f\) is a function defined at discrete locations on the x-axis (labeled using \(m\)). + The value at \(m\) occupies a length \(\Delta_{x, m}\) along the x-axis. Note that \(m\) + is an index along the x-axis, not necessarily an x-coordinate, since each length + \(\Delta_{x, m}, \Delta_{x, m+1}, ...\) is independently chosen.
+If we treat f as a 1D array of values, with the i-th value f[i] taking up a length dx[i]
+along the x-axis, the forward derivative is
deriv_forward(f)[i] = (f[i + 1] - f[i]) / dx[i]
+Likewise, discrete reverse derivative is + +
or
+deriv_back(f)[i] = (f[i] - f[i - 1]) / dx[i]
+The derivatives' values are shifted by a half-cell relative to the original function, and
+will have different cell widths if all the dx[i] ( \(\Delta_{x, m}\) ) are not
+identical:
[figure: derivatives and cell sizes]
+ dx0 dx1 dx2 dx3 cell sizes for function
+ ----- ----- ----------- -----
+ ______________________________
+ | | | |
+ f0 | f1 | f2 | f3 | function
+ _____|_____|___________|_____|
+ | | | |
+ | Df0 | Df1 | Df2 | Df3 forward derivative (periodic boundary)
+ __|_____|________|________|___
+
+ dx'3] dx'0 dx'1 dx'2 [dx'3 cell sizes for forward derivative
+ -- ----- -------- -------- ---
+ dx'0] dx'1 dx'2 dx'3 [dx'0 cell sizes for reverse derivative
+ ______________________________
+ | | | |
+ | df1 | df2 | df3 | df0 reverse derivative (periodic boundary)
+ __|_____|________|________|___
+
+Periodic boundaries are used here and elsewhere unless otherwise noted.
+In the above figure,
+ f0 = \(f_0\), f1 = \(f_1\)
+ Df0 = \([\tilde{\partial}f]_{0 + \frac{1}{2}}\)
+ Df1 = \([\tilde{\partial}f]_{1 + \frac{1}{2}}\)
+ df0 = \([\hat{\partial}f]_{0 - \frac{1}{2}}\)
+ etc.
The fractional subscript \(m + \frac{1}{2}\) is used to indicate values defined + at shifted locations relative to the original \(m\), with corresponding lengths + +
Just as \(m\) is not itself an x-coordinate, neither is \(m + \frac{1}{2}\); +carefully note the positions of the various cells in the above figure vs their labels. +If the positions labeled with \(m\) are considered the "base" or "original" grid, +the positions labeled with \(m + \frac{1}{2}\) are said to lie on a "dual" or +"derived" grid.
+For the remainder of the Discrete calculus section, all figures will show
+constant-length cells in order to focus on the vector derivatives themselves.
+See the Grid description section below for additional information on this topic
+and generalization to three dimensions.
Expanding to three dimensions, we can define two gradients
+
+
or
+[code: gradients]
+grad_forward(f)[i,j,k] = [Dx_forward(f)[i, j, k],
+ Dy_forward(f)[i, j, k],
+ Dz_forward(f)[i, j, k]]
+ = [(f[i + 1, j, k] - f[i, j, k]) / dx[i],
+ (f[i, j + 1, k] - f[i, j, k]) / dy[i],
+ (f[i, j, k + 1] - f[i, j, k]) / dz[i]]
+
+grad_back(f)[i,j,k] = [Dx_back(f)[i, j, k],
+ Dy_back(f)[i, j, k],
+ Dz_back(f)[i, j, k]]
+ = [(f[i, j, k] - f[i - 1, j, k]) / dx[i],
+ (f[i, j, k] - f[i, j - 1, k]) / dy[i],
+ (f[i, j, k] - f[i, j, k - 1]) / dz[i]]
+The three derivatives in the gradient cause shifts in different +directions, so the x/y/z components of the resulting "vector" are defined +at different points: the x-component is shifted in the x-direction, +y in y, and z in z.
+We call the resulting object a "fore-vector" or "back-vector", depending
+on the direction of the shift. We write it as
+
+
[figure: gradient / fore-vector]
+ (m, n+1, p+1) ______________ (m+1, n+1, p+1)
+ /: /|
+ / : / |
+ / : / |
+ (m, n, p+1)/_____________/ | The forward derivatives are defined
+ | : | | at the Dx, Dy, Dz points,
+ | :.........|...| but the forward-gradient fore-vector
+ z y Dz / | / is the set of all three
+ |/_x | Dy | / and is said to be "located" at (m,n,p)
+ |/ |/
+ (m, n, p)|_____Dx______| (m+1, n, p)
+There are also two divergences,
+or
+[code: divergences]
+div_forward(g)[i,j,k] = Dx_forward(gx)[i, j, k] +
+ Dy_forward(gy)[i, j, k] +
+ Dz_forward(gz)[i, j, k]
+ = (gx[i + 1, j, k] - gx[i, j, k]) / dx[i] +
+ (gy[i, j + 1, k] - gy[i, j, k]) / dy[i] +
+ (gz[i, j, k + 1] - gz[i, j, k]) / dz[i]
+
+div_back(g)[i,j,k] = Dx_back(gx)[i, j, k] +
+ Dy_back(gy)[i, j, k] +
+ Dz_back(gz)[i, j, k]
+ = (gx[i, j, k] - gx[i - 1, j, k]) / dx[i] +
+ (gy[i, j, k] - gy[i, j - 1, k]) / dy[i] +
+ (gz[i, j, k] - gz[i, j, k - 1]) / dz[i]
+where g = [gx, gy, gz] is a fore- or back-vector field.
Since we applied the forward divergence to the back-vector (and vice-versa), the resulting scalar value +is defined at the back-vector's (fore-vector's) location \((m,n,p)\) and not at the locations of its components +\((m \pm \frac{1}{2},n,p)\) etc.
+[figure: divergence]
+ ^^
+ (m-1/2, n+1/2, p+1/2) _____||_______ (m+1/2, n+1/2, p+1/2)
+ /: || ,, /|
+ / : || // / | The divergence at (m, n, p) (the center
+ / : // / | of this cube) of a fore-vector field
+ (m-1/2, n-1/2, p+1/2)/_____________/ | is the sum of the outward-pointing
+ | : | | fore-vector components, which are
+ z y <==|== :.........|.====> located at the face centers.
+ |/_x | / | /
+ | / // | / Note that in a nonuniform grid, each
+ |/ // || |/ dimension is normalized by the cell width.
+ (m-1/2, n-1/2, p-1/2)|____//_______| (m+1/2, n-1/2, p-1/2)
+ '' ||
+ VV
+The two curls are then
+and
+where \(\hat{g}\) and \(\tilde{g}\) are located at \((m,n,p)\) + with components at \((m \pm \frac{1}{2},n,p)\) etc., + while \(\hat{h}\) and \(\tilde{h}\) are located at \((m \pm \frac{1}{2}, n \pm \frac{1}{2}, p \pm \frac{1}{2})\) + with components at \((m, n \pm \frac{1}{2}, p \pm \frac{1}{2})\) etc.
+[code: curls]
+curl_forward(g)[i,j,k] = [Dy_forward(gz)[i, j, k] - Dz_forward(gy)[i, j, k],
+ Dz_forward(gx)[i, j, k] - Dx_forward(gz)[i, j, k],
+ Dx_forward(gy)[i, j, k] - Dy_forward(gx)[i, j, k]]
+
+curl_back(g)[i,j,k] = [Dy_back(gz)[i, j, k] - Dz_back(gy)[i, j, k],
+ Dz_back(gx)[i, j, k] - Dx_back(gz)[i, j, k],
+ Dx_back(gy)[i, j, k] - Dy_back(gx)[i, j, k]]
+For example, consider the forward curl, at (m, n, p), of a back-vector field g, defined
+ on a grid containing (m + 1/2, n + 1/2, p + 1/2).
+ The curl will be a fore-vector, so its z-component will be defined at (m, n, p + 1/2).
+ Take the nearest x- and y-components of g in the xy plane where the curl's z-component
+ is located; these are
[curl components]
+(m, n + 1/2, p + 1/2) : x-component of back-vector at (m + 1/2, n + 1/2, p + 1/2)
+(m + 1, n + 1/2, p + 1/2) : x-component of back-vector at (m + 3/2, n + 1/2, p + 1/2)
+(m + 1/2, n , p + 1/2) : y-component of back-vector at (m + 1/2, n + 1/2, p + 1/2)
+(m + 1/2, n + 1 , p + 1/2) : y-component of back-vector at (m + 1/2, n + 3/2, p + 1/2)
+These four xy-components can be used to form a loop around the curl's z-component; its magnitude and sign + is set by their loop-oriented sum (i.e. two have their signs flipped to complete the loop).
+[figure: z-component of curl]
+ : |
+ z y : ^^ |
+ |/_x :....||.<.....| (m+1, n+1, p+1/2)
+ / || /
+ | v || | ^
+ |/ |/
+ (m, n, p+1/2) |_____>______| (m+1, n, p+1/2)
+If we discretize both space (m,n,p) and time (l), Maxwell's equations become
+with
+where the spatial subscripts are abbreviated as \(\vec{r} = (m, n, p)\) and +\(\vec{r} + \frac{1}{2} = (m + \frac{1}{2}, n + \frac{1}{2}, p + \frac{1}{2})\), +\(\tilde{E}\) and \(\hat{H}\) are the electric and magnetic fields, +\(\tilde{J}\) and \(\hat{M}\) are the electric and magnetic current distributions, +and \(\epsilon\) and \(\mu\) are the dielectric permittivity and magnetic permeability.
+The above is Yee's algorithm, written in a form analogous to Maxwell's equations. +The time derivatives can be expanded to form the update equations:
+[code: Maxwell's equations updates]
+H[i, j, k] -= dt * (curl_forward(E)[i, j, k] + M[t, i, j, k]) / mu[i, j, k]
+E[i, j, k] += dt * (curl_back( H)[i, j, k] + J[t, i, j, k]) / epsilon[i, j, k]
+Note that the E-field fore-vector and H-field back-vector are offset by a half-cell, resulting +in distinct locations for all six E- and H-field components:
+[figure: Field components]
+
+ (m - 1/2,=> ____________Hx__________[H] <= r + 1/2 = (m + 1/2,
+ n + 1/2, /: /: /| n + 1/2,
+ z y p + 1/2) / : / : / | p + 1/2)
+ |/_x / : / : / |
+ / : Ez__________Hy | Locations of the E- and
+ / : : : /| | H-field components for the
+ (m - 1/2, / : : Ey...../.|..Hz [E] fore-vector at r = (m,n,p)
+ n - 1/2, =>/________________________/ | /| (the large cube's center)
+ p + 1/2) | : : / | | / | and [H] back-vector at r + 1/2
+ | : :/ | |/ | (the top right corner)
+ | : [E].......|.Ex |
+ | :.................|......| <= (m + 1/2, n + 1/2, p + 1/2)
+ | / | /
+ | / | /
+ | / | / This is the Yee discretization
+ | / | / scheme ("Yee cell").
+r - 1/2 = | / | /
+ (m - 1/2, |/ |/
+ n - 1/2,=> |________________________| <= (m + 1/2, n - 1/2, p - 1/2)
+ p - 1/2)
+Each component forms its own grid, offset from the others:
+[figure: E-fields for adjacent cells]
+
+ H1__________Hx0_________H0
+ z y /: /|
+ |/_x / : / | This figure shows H back-vector locations
+ / : / | H0, H1, etc. and their associated components
+ Hy1 : Hy0 | H0 = (Hx0, Hy0, Hz0) etc.
+ / : / |
+ / Hz1 / Hz0
+ H2___________Hx3_________H3 | The equivalent drawing for E would have
+ | : | | fore-vectors located at the cube's
+ | : | | center (and the centers of adjacent cubes),
+ | : | | with components on the cube's faces.
+ | H5..........Hx4...|......H4
+ | / | /
+ Hz2 / Hz2 /
+ | / | /
+ | Hy6 | Hy4
+ | / | /
+ |/ |/
+ H6__________Hx7__________H7
+The divergence equations can be derived by taking the divergence of the curl equations +and combining them with charge continuity, + +
implying that the discrete Maxwell's equations do not produce spurious charges.
+Taking the backward curl of the \(\tilde{\nabla} \times \tilde{E}\) equation and +replacing the resulting \(\hat{\nabla} \times \hat{H}\) term using its respective equation, +and setting \(\hat{M}\) to zero, we can form the discrete wave equation:
+We can substitute in a time-harmonic fields
+resulting in
+This gives the frequency-domain wave equation,
+With uniform material distribution and no sources
+the frequency domain wave equation simplifies to
+Since \(\hat{\nabla} \cdot \tilde{E}_{\vec{r}} = 0\), we can simplify
+and we get
+We can convert this to three scalar-wave equations of the form
+with \(K^2 = \Omega^2 \mu \epsilon\). Now we let
+resulting in
+with similar expressions for the y and z dimnsions (and \(K_y, K_z\)).
+This implies
+where \(c = \sqrt{\mu \epsilon}\).
+Assuming real \((k_x, k_y, k_z), \omega\) will be real only if
+If \(\Delta_x = \Delta_y = \Delta_z\), this simplifies to \(c \Delta_t < \Delta_x / \sqrt{3}\). +This last form can be interpreted as enforcing causality; the distance that light +travels in one timestep (i.e., \(c \Delta_t\)) must be less than the diagonal +of the smallest cell ( \(\Delta_x / \sqrt{3}\) when on a uniform cubic grid).
+As described in the section on scalar discrete derivatives above, cell widths
+(dx[i], dy[j], dz[k]) along each axis can be arbitrary and independently
+defined. Moreover, all field components are actually defined at "derived" or "dual"
+positions, in-between the "base" grid points on one or more axes.
To get a better sense of how this works, let's start by drawing a grid with uniform
+dy and dz and nonuniform dx. We will only draw one cell in the y and z dimensions
+to make the illustration simpler; we need at least two cells in the x dimension to
+demonstrate how nonuniform dx affects the various components.
Place the E fore-vectors at integer indices \(r = (m, n, p)\) and the H back-vectors +at fractional indices \(r + \frac{1}{2} = (m + \frac{1}{2}, n + \frac{1}{2}, +p + \frac{1}{2})\). Remember that these are indices and not coordinates; they can +correspond to arbitrary (monotonically increasing) coordinates depending on the cell widths.
+Draw lines to denote the planes on which the H components and back-vectors are defined. +For simplicity, don't draw the equivalent planes for the E components and fore-vectors, +except as necessary to show their locations -- it's easiest to just connect them to their +associated H-equivalents.
+The result looks something like this:
+[figure: Component centers]
+ p=
+ [H]__________Hx___________[H]_____Hx______[H] __ +1/2
+ z y /: /: /: /: /| | |
+ |/_x / : / : / : / : / | | |
+ / : / : / : / : / | | |
+ Hy : Ez...........Hy : Ez......Hy | | |
+ /: : : : /: : : : /| | | |
+ / : Hz : Ey....../.:..Hz : Ey./.|..Hz __ 0 | dz[0]
+ / : /: : / / : /: : / / | /| | |
+ /_________________________/_______________/ | / | | |
+ | :/ : :/ | :/ : :/ | |/ | | |
+ | Ex : [E].......|..Ex : [E]..|..Ex | | |
+ | : | : | | | |
+ | [H]..........Hx....|......[H].....H|x.....[H] __ --------- (n=+1/2, p=-1/2)
+ | / | / | / / /
+ Hz / Hz / Hz / / /
+ | / | / | / / /
+ | Hy | Hy | Hy __ 0 / dy[0]
+ | / | / | / / /
+ | / | / | / / /
+ |/ |/ |/ / /
+ [H]__________Hx___________[H]_____Hx______[H] __ -1/2 /
+ =n
+ |------------|------------|-------|-------|
+ -1/2 0 +1/2 +1 +3/2 = m
+
+ ------------------------- ----------------
+ dx[0] dx[1]
+
+ Part of a nonuniform "base grid", with labels specifying
+ positions of the various field components. [E] fore-vectors
+ are at the cell centers, and [H] back-vectors are at the
+ vertices. H components along the near (-y) top (+z) edge
+ have been omitted to make the insides of the cubes easier
+ to visualize.
+The above figure shows where all the components are located; however, it is also useful to show
+what volumes those components correspond to. Consider the Ex component at m = +1/2: it is
+shifted in the x-direction by a half-cell from the E fore-vector at m = 0 (labeled [E]
+in the figure). It corresponds to a volume between m = 0 and m = +1 (the other
+dimensions are not shifted, i.e. they are still bounded by n, p = +-1/2). (See figure
+below). Since m is an index and not an x-coordinate, the Ex component is not necessarily
+at the center of the volume it represents, and the x-length of its volume is the derived
+quantity dx'[0] = (dx[0] + dx[1]) / 2 rather than the base dx.
+(See also Scalar derivatives and cell shifts).
[figure: Ex volumes]
+ p=
+ <_________________________________________> __ +1/2
+ z y << /: / /: >> | |
+ |/_x < < / : / / : > > | |
+ < < / : / / : > > | |
+ < < / : / / : > > | |
+ <: < / : : / : >: > | |
+ < : < / : : / : > : > __ 0 | dz[0]
+ < : < / : : / :> : > | |
+ <____________/____________________/_______> : > | |
+ < : < | : : | > : > | |
+ < Ex < | : Ex | > Ex > | |
+ < : < | : : | > : > | |
+ < : <....|.......:........:...|.......>...:...> __ --------- (n=+1/2, p=-1/2)
+ < : < | / : /| /> : > / /
+ < : < | / : / | / > : > / /
+ < :< | / :/ | / > :> / /
+ < < | / : | / > > _ 0 / dy[0]
+ < < | / | / > > / /
+ < < | / | / > > / /
+ << |/ |/ >> / /
+ <____________|____________________|_______> __ -1/2 /
+ =n
+ |------------|------------|-------|-------|
+ -1/2 0 +1/2 +1 +3/2 = m
+
+ ~------------ -------------------- -------~
+ dx'[-1] dx'[0] dx'[1]
+
+ The Ex values are positioned on the x-faces of the base
+ grid. They represent the Ex field in volumes shifted by
+ a half-cell in the x-dimension, as shown here. Only the
+ center cell (with width dx'[0]) is fully shown; the
+ other two are truncated (shown using >< markers).
+
+ Note that the Ex positions are the in the same positions
+ as the previous figure; only the cell boundaries have moved.
+ Also note that the points at which Ex is defined are not
+ necessarily centered in the volumes they represent; non-
+ uniform cell sizes result in off-center volumes like the
+ center cell here.
+The next figure shows the volumes corresponding to the Hy components, which +are shifted in two dimensions (x and z) compared to the base grid.
+[figure: Hy volumes]
+ p=
+ z y mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm __ +1/2 s
+ |/_x << m: m: >> | |
+ < < m : m : > > | | dz'[1]
+ < < m : m : > > | |
+ Hy........... m........Hy...........m......Hy > | |
+ < < m : m : > > | |
+ < ______ m_____:_______________m_____:_>______ __ 0
+ < < m /: m / > > | |
+ mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm > | |
+ < < | / : | / > > | | dz'[0]
+ < < | / : | / > > | |
+ < < | / : | / > > | |
+ < wwwww|w/wwwwwwwwwwwwwwwwwww|w/wwwww>wwwwwwww __ s
+ < < |/ w |/ w> > / /
+ _____________|_____________________|________ > / /
+ < < | w | w > > / /
+ < Hy........|...w........Hy.......|...w...>..Hy _ 0 / dy[0]
+ < < | w | w > > / /
+ << | w | w > > / /
+ < |w |w >> / /
+ wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww __ -1/2 /
+
+ |------------|------------|--------|-------|
+-1/2 0 +1/2 +1 +3/2 = m
+
+ ~------------ --------------------- -------~
+ dx'[-1] dx'[0] dx'[1]
+
+ The Hy values are positioned on the y-edges of the base
+ grid. Again here, the 'Hy' labels represent the same points
+ as in the basic grid figure above; the edges have shifted
+ by a half-cell along the x- and z-axes.
+
+ The grid lines _|:/ are edges of the area represented by
+ each Hy value, and the lines drawn using <m>.w represent
+ edges where a cell's faces extend beyond the drawn area
+ (i.e. where the drawing is truncated in the x- or z-
+ directions).
+In this documentation, the E fore-vectors are placed on the base grid. An +equivalent formulation could place the H back-vectors on the base grid instead. +However, in the case of a non-uniform grid, the operation to get from the "base" +cell widths to "derived" ones is not its own inverse.
+The base grid's cell sizes could be fully described by a list of three 1D arrays, +specifying the cell widths along all three axes:
+[dx, dy, dz] = [[dx[0], dx[1], ...], [dy[0], ...], [dz[0], ...]]
+Note that this is a list-of-arrays rather than a 2D array, as the simulation domain +may have a different number of cells along each axis.
+Knowing the base grid's cell widths and the boundary conditions (periodic unless
+otherwise noted) is enough information to calculate the cell widths dx', dy',
+and dz' for the derived grids.
However, since most operations are trivially generalized to allow either E or H +to be defined on the base grid, they are written to take the a full set of base +and derived cell widths, distinguished by which field they apply to rather than +their "base" or "derived" status. This removes the need for each function to +generate the derived widths, and makes the "base" vs "derived" distinction +unnecessary in the code.
+The resulting data structure containing all the cell widths takes the form of a +list-of-lists-of-arrays. The first list-of-arrays provides the cell widths for +the E-field fore-vectors, while the second list-of-arrays does the same for the +H-field back-vectors:
+ [[[dx_e[0], dx_e[1], ...], [dy_e[0], ...], [dz_e[0], ...]],
+ [[dx_h[0], dx_h[1], ...], [dy_h[0], ...], [dz_h[0], ...]]]
+where dx_e[0] is the x-width of the m=0 cells, as used when calculating dE/dx,
+ and dy_h[0] is the y-width of the n=0 cells, as used when calculating dH/dy, etc.
Since each vector component of E and H is defined in a different location and represents
+a different volume, the value of the spatially-discrete epsilon and mu can also be
+different for all three field components, even when representing a simple planar interface
+between two isotropic materials.
As a result, epsilon and mu are taken to have the same dimensions as the field, and
+composed of the three diagonal tensor components:
[equations: epsilon_and_mu]
+epsilon = [epsilon_xx, epsilon_yy, epsilon_zz]
+mu = [mu_xx, mu_yy, mu_zz]
+or
+where the off-diagonal terms (e.g. epsilon_xy) are assumed to be zero.
High-accuracy volumetric integration of shapes on multiple grids can be performed +by the gridlock module.
+The values of the vacuum permittivity and permability effectively become scaling +factors that appear in several locations (e.g. between the E and H fields). In +order to limit floating-point inaccuracy and simplify calculations, they are often +set to 1 and relative permittivities and permeabilities are used in their places; +the true values can be multiplied back in after the simulation is complete if non- +normalized results are needed.
+ + + + + + + + + + + meanas.fdmath.functional
+
+
+¶Math functions for finite difference simulations
+Basic discrete calculus etc.
+ + + + + + + + + + + deriv_forward
+
+
+¶deriv_forward(
+ dx_e: Sequence[NDArray[floating | complexfloating]]
+ | None = None,
+) -> tuple[
+ fdfield_updater_t, fdfield_updater_t, fdfield_updater_t
+]
+Utility operators for taking discretized derivatives (backward variant).
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_e
+ |
+
+ Sequence[NDArray[floating | complexfloating]] | None
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ tuple[fdfield_updater_t, fdfield_updater_t, fdfield_updater_t]
+ |
+
+
+
+ List of functions for taking forward derivatives along each axis. + |
+
deriv_back
+
+
+¶deriv_back(
+ dx_h: Sequence[NDArray[floating | complexfloating]]
+ | None = None,
+) -> tuple[
+ fdfield_updater_t, fdfield_updater_t, fdfield_updater_t
+]
+Utility operators for taking discretized derivatives (forward variant).
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_h
+ |
+
+ Sequence[NDArray[floating | complexfloating]] | None
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ tuple[fdfield_updater_t, fdfield_updater_t, fdfield_updater_t]
+ |
+
+
+
+ List of functions for taking forward derivatives along each axis. + |
+
curl_forward
+
+
+¶curl_forward(
+ dx_e: Sequence[NDArray[floating | complexfloating]]
+ | None = None,
+) -> Callable[[TT], TT]
+Curl operator for use with the E field.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_e
+ |
+
+ Sequence[NDArray[floating | complexfloating]] | None
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ Callable[[TT], TT]
+ |
+
+
+
+ Function |
+
+ Callable[[TT], TT]
+ |
+
+
+
+
|
+
curl_back
+
+
+¶curl_back(
+ dx_h: Sequence[NDArray[floating | complexfloating]]
+ | None = None,
+) -> Callable[[TT], TT]
+Create a function which takes the backward curl of a field.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_h
+ |
+
+ Sequence[NDArray[floating | complexfloating]] | None
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ Callable[[TT], TT]
+ |
+
+
+
+ Function |
+
+ Callable[[TT], TT]
+ |
+
+
+
+
|
+
meanas.fdmath.operators
+
+
+¶Matrix operators for finite difference simulations
+Basic discrete calculus etc.
+ + + + + + + + + + + shift_circ
+
+
+¶Utility operator for performing a circular shift along a specified axis by a + specified number of elements.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ axis
+ |
+
+ int
+ |
+
+
+
+ Axis to shift along. x=0, y=1, z=2 + |
+ + required + | +
+ shape
+ |
+
+ Sequence[int]
+ |
+
+
+
+ Shape of the grid being shifted + |
+ + required + | +
+ shift_distance
+ |
+
+ int
+ |
+
+
+
+ Number of cells to shift by. May be negative. Default 1. + |
+
+ 1
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for performing the circular shift. + |
+
shift_with_mirror
+
+
+¶shift_with_mirror(
+ axis: int, shape: Sequence[int], shift_distance: int = 1
+) -> sparse.sparray
+Utility operator for performing an n-element shift along a specified axis, with mirror +boundary conditions applied to the cells beyond the receding edge.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ axis
+ |
+
+ int
+ |
+
+
+
+ Axis to shift along. x=0, y=1, z=2 + |
+ + required + | +
+ shape
+ |
+
+ Sequence[int]
+ |
+
+
+
+ Shape of the grid being shifted + |
+ + required + | +
+ shift_distance
+ |
+
+ int
+ |
+
+
+
+ Number of cells to shift by. May be negative. Default 1. + |
+
+ 1
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for performing the shift-with-mirror. + |
+
deriv_forward
+
+
+¶Utility operators for taking discretized derivatives (forward variant).
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_e
+ |
+
+ Sequence[NDArray[floating | complexfloating]]
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ list[sparray]
+ |
+
+
+
+ List of operators for taking forward derivatives along each axis. + |
+
deriv_back
+
+
+¶Utility operators for taking discretized derivatives (backward variant).
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_h
+ |
+
+ Sequence[NDArray[floating | complexfloating]]
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ list[sparray]
+ |
+
+
+
+ List of operators for taking forward derivatives along each axis. + |
+
cross
+
+
+¶Cross product operator
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ B
+ |
+
+ Sequence[sparray]
+ |
+
+
+
+ List |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix corresponding to (B x), where x is the cross product. + |
+
vec_cross
+
+
+¶Vector cross product operator
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ b
+ |
+
+ vfdfield
+ |
+
+
+
+ Vector on the left side of the cross product. + |
+ + required + | +
Returns:
+Sparse matrix corresponding to (b x), where x is the cross product.
+ avg_forward
+
+
+¶Forward average operator (x4 = (x4 + x5) / 2)
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ axis
+ |
+
+ int
+ |
+
+
+
+ Axis to average along (x=0, y=1, z=2) + |
+ + required + | +
+ shape
+ |
+
+ Sequence[int]
+ |
+
+
+
+ Shape of the grid to average + |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for forward average operation. + |
+
avg_back
+
+
+¶Backward average operator (x4 = (x4 + x3) / 2)
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ axis
+ |
+
+ int
+ |
+
+
+
+ Axis to average along (x=0, y=1, z=2) + |
+ + required + | +
+ shape
+ |
+
+ Sequence[int]
+ |
+
+
+
+ Shape of the grid to average + |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for backward average operation. + |
+
curl_forward
+
+
+¶Curl operator for use with the E field.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_e
+ |
+
+ Sequence[NDArray[floating | complexfloating]]
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for taking the discretized curl of the E-field + |
+
curl_back
+
+
+¶Curl operator for use with the H field.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dx_h
+ |
+
+ Sequence[NDArray[floating | complexfloating]]
+ |
+
+
+
+ Lists of cell sizes for all axes
+ |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ sparray
+ |
+
+
+
+ Sparse matrix for taking the discretized curl of the H-field + |
+
meanas.fdmath.vectorization
+
+
+¶Functions for moving between a vector field (list of 3 ndarrays, [f_x, f_y, f_z])
+and a 1D array representation of that field [f_x0, f_x1, f_x2,... f_y0,... f_z0,...].
+Vectorized versions of the field use row-major (ie., C-style) ordering.
vec
+
+
+¶vec(
+ f: fdfield_t
+ | cfdfield_t
+ | fdfield2_t
+ | cfdfield2_t
+ | fdslice_t
+ | cfdslice_t
+ | ArrayLike
+ | None,
+) -> (
+ vfdfield_t
+ | vcfdfield_t
+ | vfdfield2_t
+ | vcfdfield2_t
+ | vfdslice_t
+ | vcfdslice_t
+ | NDArray
+ | None
+)
+Create a 1D ndarray from a vector field which spans a 1-3D region.
+Returns None if called with f=None.
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ f
+ |
+
+ fdfield_t | cfdfield_t | fdfield2_t | cfdfield2_t | fdslice_t | cfdslice_t | ArrayLike | None
+ |
+
+
+
+ A vector field, e.g. |
+ + required + | +
Returns:
+| Type | +Description | +
|---|---|
+ vfdfield_t | vcfdfield_t | vfdfield2_t | vcfdfield2_t | vfdslice_t | vcfdslice_t | NDArray | None
+ |
+
+
+
+ 1D ndarray containing the linearized field (or |
+
unvec
+
+
+¶unvec(
+ v: vfdfield_t
+ | vcfdfield_t
+ | vfdfield2_t
+ | vcfdfield2_t
+ | vfdslice_t
+ | vcfdslice_t
+ | ArrayLike
+ | None,
+ shape: Sequence[int],
+ nvdim: int = 3,
+) -> (
+ fdfield_t
+ | cfdfield_t
+ | fdfield2_t
+ | cfdfield2_t
+ | fdslice_t
+ | cfdslice_t
+ | NDArray
+ | None
+)
+Perform the inverse of vec(): take a 1D ndarray and output an nvdim-component field
+ of form e.g. [f_x, f_y, f_z] (nvdim=3) where each of f_* is a len(shape)-dimensional
+ ndarray.
Returns None if called with v=None.
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ v
+ |
+
+ vfdfield_t | vcfdfield_t | vfdfield2_t | vcfdfield2_t | vfdslice_t | vcfdslice_t | ArrayLike | None
+ |
+
+
+
+ 1D ndarray representing a vector field of shape shape (or None) + |
+ + required + | +
+ shape
+ |
+
+ Sequence[int]
+ |
+
+
+
+ shape of the vector field + |
+ + required + | +
+ nvdim
+ |
+
+ int
+ |
+
+
+
+ Number of components in each vector + |
+
+ 3
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t | cfdfield_t | fdfield2_t | cfdfield2_t | fdslice_t | cfdslice_t | NDArray | None
+ |
+
+
+
+
|
+
meanas.fdmath.types
+
+
+¶Types shared across multiple submodules
+ + + + + + + + + + + dx_lists_t
+
+
+
+ module-attribute
+
+
+¶'dxes' datastructure which contains grid cell width information in the following format:
+[[[dx_e[0], dx_e[1], ...], [dy_e[0], ...], [dz_e[0], ...]],
+ [[dx_h[0], dx_h[1], ...], [dy_h[0], ...], [dz_h[0], ...]]]
+where dx_e[0] is the x-width of the x=0 cells, as used when calculating dE/dx,
+ and dy_h[0] is the y-width of the y=0 cells, as used when calculating dH/dy, etc.
dx_lists2_t
+
+
+
+ module-attribute
+
+
+¶2D 'dxes' datastructure which contains grid cell width information in the following format:
+[[[dx_e[0], dx_e[1], ...], [dy_e[0], ...]],
+ [[dx_h[0], dx_h[1], ...], [dy_h[0], ...]]]
+where dx_e[0] is the x-width of the x=0 cells, as used when calculating dE/dx,
+ and dy_h[0] is the y-width of the y=0 cells, as used when calculating dH/dy, etc.
dx_lists_mut
+
+
+
+ module-attribute
+
+
+¶Mutable version of dx_lists_t
dx_lists2_mut
+
+
+
+ module-attribute
+
+
+¶Mutable version of dx_lists2_t
fdfield_updater_t
+
+
+
+ module-attribute
+
+
+¶Convenience type for functions which take and return a real fdfield
cfdfield_updater_t
+
+
+
+ module-attribute
+
+
+¶Convenience type for functions which take and return a complex cfdfield
fdfield
+
+
+¶Vector field with shape (3, X, Y, Z) (e.g. [E_x, E_y, E_z])
vfdfield
+
+
+¶Linearized vector field (single vector of length 3XY*Z)
+ cfdfield
+
+
+¶Complex vector field with shape (3, X, Y, Z) (e.g. [E_x, E_y, E_z])
vcfdfield
+
+
+¶Linearized complex vector field (single vector of length 3XY*Z)
+ fdslice
+
+
+¶Vector field slice with shape (3, X, Y) (e.g. [E_x, E_y, E_z] at a single Z position)
vfdslice
+
+
+¶Linearized vector field slice (single vector of length 3XY)
+ cfdslice
+
+
+¶Complex vector field slice with shape (3, X, Y) (e.g. [E_x, E_y, E_z] at a single Z position)
vcfdslice
+
+
+¶Linearized complex vector field slice (single vector of length 3XY)
+ fdfield2
+
+
+¶2D Vector field with shape (2, X, Y) (e.g. [E_x, E_y])
vfdfield2
+
+
+¶2D Linearized vector field (single vector of length 2XY)
+ cfdfield2
+
+
+¶2D Complex vector field with shape (2, X, Y) (e.g. [E_x, E_y])
vcfdfield2
+
+
+¶2D Linearized complex vector field (single vector of length 2XY)
+ meanas.fdtd
+
+
+¶Utilities for running finite-difference time-domain (FDTD) simulations
+See the discussion of Maxwell's Equations in meanas.fdmath for basic
+mathematical background.
From the discussion of "Plane waves and the Dispersion relation" in meanas.fdmath,
+we have
or, if \(\Delta_x = \Delta_y = \Delta_z\), then \(c \Delta_t < \frac{\Delta_x}{\sqrt{3}}\).
+Based on this, we can set
+dt = sqrt(mu.min() * epsilon.min()) / sqrt(1/dx_min**2 + 1/dy_min**2 + 1/dz_min**2)
+The dx_min, dy_min, dz_min should be the minimum value across both the base and derived grids.
Let
+where \(\vec{r} = (m, n, p)\) and \(\otimes\) is a modified cross product +in which the \(\tilde{E}\) terms are shifted as indicated.
+By taking the divergence and rearranging terms, we can show that
+where in the last line the spatial subscripts have been dropped to emphasize +the time subscripts \(l, l'\), i.e.
+etc. +For \(l' = l + \frac{1}{2}\) we get
+and for \(l' = l - \frac{1}{2}\),
+These two results form the discrete time-domain analogue to Poynting's theorem. +They hint at the expressions for the energy, which can be calculated at the same +time-index as either the E or H field:
+Rewriting the Poynting theorem in terms of the energy expressions,
+This result is exact and should practically hold to within numerical precision. No time- +or spatial-averaging is necessary.
+Note that each value of \(J\) contributes to the energy twice (i.e. once per field update) +despite only causing the value of \(E\) to change once (same for \(M\) and \(H\)).
+It is often useful to excite the simulation with an arbitrary broadband pulse and then +extract the frequency-domain response by performing an on-the-fly Fourier transform +of the time-domain fields.
+accumulate_phasor in meanas.fdtd.phasor performs the phase accumulation for one
+or more target frequencies, while leaving source normalization and simulation-loop
+policy to the caller. temporal_phasor(...) and temporal_phasor_scale(...)
+apply the same Fourier sum to a scalar waveform, which is useful when a pulsed
+source envelope must be normalized before being applied to a point source or
+mode source. real_injection_scale(...) is the corresponding helper for the
+common real-valued injection pattern numpy.real(scale * waveform). Convenience
+wrappers accumulate_phasor_e, accumulate_phasor_h, and accumulate_phasor_j
+apply the usual Yee time offsets. reconstruct_real(...) and the corresponding
+reconstruct_real_e/h/j(...) wrappers perform the inverse operation, converting
+phasors back into real-valued field snapshots at explicit Yee-aligned times.
+For scalar omega, the reconstruction helpers accept either a plain field
+phasor or the batched (1, *sample_shape) form used by the accumulator helpers.
+The helpers accumulate
with caller-provided sample times and weights. In this codebase the matching
+electric-current convention is typically E -= dt * J / epsilon in FDTD and
+-i \omega J on the right-hand side of the FDFD wave equation.
For FDTD/FDFD equivalence, this phasor path is the primary comparison workflow.
+It isolates the guided +\omega response that the frequency-domain solver
+targets directly, regardless of whether the underlying FDTD run used real- or
+complex-valued fields.
For exact pulsed FDTD/FDFD equivalence it is often simplest to keep the
+injected source, fields, and CPML auxiliary state complex-valued. The
+real_injection_scale(...) helper is instead for the more ordinary one-run
+real-valued source path, where the intended positive-frequency waveform is
+injected through numpy.real(scale * waveform) and any remaining negative-
+frequency leakage is controlled by the pulse bandwidth and run length.
reconstruct_real(...) is for a different question: given a phasor, what late
+real-valued field snapshot should it produce? That raw-snapshot comparison is
+stricter and noisier because a monitor plane generally contains both the guided
+field and the remaining orthogonal content,
Phasor/modal comparisons mostly validate the guided +\omega term. Raw
+real-field comparisons expose both terms at once, so they should be treated as
+secondary diagnostics rather than the main solver-equivalence benchmark.
The Ricker wavelet (normalized second derivative of a Gaussian) is commonly used for the pulse +shape. It can be written
+with \(\tau > \frac{2 * \pi}{\omega}\) as a minimum delay to avoid a discontinuity at +t=0 (assuming the source is off for t<0 this gives \(\sim 10^{-3}\) error at t=0).
+meanas.fdtd exposes two boundary-related building blocks:
conducting_boundary(...) for simple perfect-electric-conductor style field
+ clamping at one face of the domain.cpml_params(...) and updates_with_cpml(...) for convolutional perfectly
+ matched layers (CPMLs) attached to one or more faces of the Yee grid.updates_with_cpml(...) accepts a three-by-two table of CPML parameter blocks:
where axis is 0, 1, or 2 and polarity_index corresponds to (-1, +1).
+Passing None for one entry disables CPML on that face while leaving the other
+directions unchanged. This is how mixed boundary setups such as "absorbing in x,
+periodic in y/z" are expressed.
When comparing an FDTD run against an FDFD solve, use the same stretched +coordinate system in both places:
+dxes with the matching SCPML transform.dxes.The electric-current sign convention used throughout the examples and tests is
+which matches the FDFD right-hand side
+ meanas.fdtd.base
+
+
+¶Basic FDTD field updates
+ + + + + + + + + + + maxwell_e
+
+
+¶Build a function which performs a portion the time-domain E-field update,
+E += curl_back(H[t]) / epsilon
+The full update should be
+E += (curl_back(H[t]) + J) / epsilon
+which requires an additional step of E += J / epsilon which is not performed
+by the generated function.
See meanas.fdmath for descriptions of
dxes: "Datastructure: dx_lists_t" sectionepsilon: "Permittivity and Permeability" sectionAlso see the "Timestep" section of meanas.fdtd for a discussion of
+the dt parameter.
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dt
+ |
+
+ float
+ |
+
+
+
+ Timestep. See |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_updater_t
+ |
+
+
+
+ Function |
+
maxwell_h
+
+
+¶Build a function which performs part of the time-domain H-field update,
+H -= curl_forward(E[t]) / mu
+The full update should be
+H -= (curl_forward(E[t]) + M) / mu
+which requires an additional step of H -= M / mu which is not performed
+by the generated function; this step can be omitted if there is no magnetic
+current M.
See meanas.fdmath for descriptions of
dxes: "Datastructure: dx_lists_t" sectionmu: "Permittivity and Permeability" sectionAlso see the "Timestep" section of meanas.fdtd for a discussion of
+the dt parameter.
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ dt
+ |
+
+ float
+ |
+
+
+
+ Timestep. See |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_updater_t
+ |
+
+
+
+ Function |
+
meanas.fdtd.pml
+
+
+¶Convolutional perfectly matched layer (CPML) support for FDTD updates.
+The helpers in this module construct per-face CPML parameters and then wrap the
+standard Yee updates with the additional auxiliary psi fields needed by the
+CPML recurrence.
The intended call pattern is:
+cpml_params[axis][polarity_index] table with cpml_params(...).updates_with_cpml(...) together with dt, dxes, and
+ epsilon.update_E / update_H closures in the simulation loop.Each face can be enabled or disabled independently by replacing one table entry
+with None.
cpml_params
+
+
+¶cpml_params(
+ axis: int,
+ polarity: int,
+ dt: float,
+ thickness: int = 8,
+ ln_R_per_layer: float = -1.6,
+ epsilon_eff: float = 1,
+ mu_eff: float = 1,
+ m: float = 3.5,
+ ma: float = 1,
+ cfs_alpha: float = 0,
+) -> dict[str, Any]
+Construct the parameter block for one CPML face.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ axis
+ |
+
+ int
+ |
+
+
+
+ Which Cartesian axis the CPML is normal to ( |
+ + required + | +
+ polarity
+ |
+
+ int
+ |
+
+
+
+ Which face along that axis ( |
+ + required + | +
+ dt
+ |
+
+ float
+ |
+
+
+
+ Timestep used by the Yee update. + |
+ + required + | +
+ thickness
+ |
+
+ int
+ |
+
+
+
+ Number of Yee cells occupied by the CPML region. + |
+
+ 8
+ |
+
+ ln_R_per_layer
+ |
+
+ float
+ |
+
+
+
+ Logarithmic attenuation target per layer. + |
+
+ -1.6
+ |
+
+ epsilon_eff
+ |
+
+ float
+ |
+
+
+
+ Effective permittivity used when choosing the CPML scaling. + |
+
+ 1
+ |
+
+ mu_eff
+ |
+
+ float
+ |
+
+
+
+ Effective permeability used when choosing the CPML scaling. + |
+
+ 1
+ |
+
+ m
+ |
+
+ float
+ |
+
+
+
+ Polynomial grading exponent for |
+
+ 3.5
+ |
+
+ ma
+ |
+
+ float
+ |
+
+
+
+ Polynomial grading exponent for the complex-frequency shift |
+
+ 1
+ |
+
+ cfs_alpha
+ |
+
+ float
+ |
+
+
+
+ Maximum complex-frequency shift parameter. + |
+
+ 0
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ dict[str, Any]
+ |
+
+
+
+ Dictionary with: + |
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
+ dict[str, Any]
+ |
+
+
+
+
|
+
updates_with_cpml
+
+
+¶updates_with_cpml(
+ cpml_params: Sequence[Sequence[dict[str, Any] | None]],
+ dt: float,
+ dxes: dx_lists_t,
+ epsilon: fdfield,
+ *,
+ dtype: DTypeLike = numpy.float32,
+) -> tuple[Callable[..., None], Callable[..., None]]
+Build Yee-step update closures augmented with CPML terms.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ cpml_params
+ |
+
+ Sequence[Sequence[dict[str, Any] | None]]
+ |
+
+
+
+ Three-by-two sequence indexed as |
+ + required + | +
+ dt
+ |
+
+ float
+ |
+
+
+
+ Timestep. + |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t
+ |
+
+
+
+ Yee-grid spacing lists |
+ + required + | +
+ epsilon
+ |
+
+ fdfield
+ |
+
+
+
+ Electric material distribution used by the E update. + |
+ + required + | +
+ dtype
+ |
+
+ DTypeLike
+ |
+
+
+
+ Storage dtype for the auxiliary CPML state arrays. + |
+
+ float32
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ Callable[..., None]
+ |
+
+
+
+
|
+
+ Callable[..., None]
+ |
+
+
+
+ Yee updates: + |
+
+ tuple[Callable[..., None], Callable[..., None]]
+ |
+
+
+
+
|
+
+ tuple[Callable[..., None], Callable[..., None]]
+ |
+
+
+
+
|
+
+ tuple[Callable[..., None], Callable[..., None]]
+ |
+
+
+
+ The closures retain the CPML auxiliary state internally. + |
+
meanas.fdtd.boundaries
+
+
+¶Boundary conditions
+ meanas.fdtd.energy
+
+
+¶ poynting
+
+
+¶Calculate the poynting vector S (\(S\)).
This is the energy transfer rate (amount of energy U per dt transferred
+between adjacent cells) in each direction that happens during the half-step
+bounded by the two provided fields.
The returned vector field S is the energy flow across +x, +y, and +z
+boundaries of the corresponding U cell. For example,
mx = numpy.roll(mask, -1, axis=0)
+ my = numpy.roll(mask, -1, axis=1)
+ mz = numpy.roll(mask, -1, axis=2)
+
+ u_hstep = fdtd.energy_hstep(e0=es[ii - 1], h1=hs[ii], e2=es[ii], **args)
+ u_estep = fdtd.energy_estep(h0=hs[ii], e1=es[ii], h2=hs[ii + 1], **args)
+ delta_j_B = fdtd.delta_energy_j(j0=js[ii], e1=es[ii], dxes=dxes)
+ du_half_h2e = u_estep - u_hstep - delta_j_B
+
+ s_h2e = -fdtd.poynting(e=es[ii], h=hs[ii], dxes=dxes) * dt
+ planes = [s_h2e[0, mask].sum(), -s_h2e[0, mx].sum(),
+ s_h2e[1, mask].sum(), -s_h2e[1, my].sum(),
+ s_h2e[2, mask].sum(), -s_h2e[2, mz].sum()]
+
+ assert_close(sum(planes), du_half_h2e[mask])
+(see meanas.tests.test_fdtd.test_poynting_planes)
The full relationship is
+These equalities are exact and should practically hold to within numerical precision.
+No time- or spatial-averaging is necessary. (See meanas.fdtd docs for derivation.)
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ e
+ |
+
+ fdfield
+ |
+
+
+
+ E-field + |
+ + required + | +
+ h
+ |
+
+ fdfield
+ |
+
+
+
+ H-field (one half-timestep before or after |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Name | Type | +Description | +
|---|---|---|
s |
+ fdfield_t
+ |
+
+
+
+ Vector field. Components indicate the energy transfer rate from the +corresponding energy cell into its +x, +y, and +z neighbors during +the half-step from the time of the earlier input field until the +time of later input field. + |
+
poynting_divergence
+
+
+¶poynting_divergence(
+ s: fdfield | None = None,
+ *,
+ e: fdfield | None = None,
+ h: fdfield | None = None,
+ dxes: dx_lists_t | None = None,
+) -> fdfield_t
+Calculate the divergence of the poynting vector.
+This is the net energy flow for each cell, i.e. the change in energy U
+per dt caused by transfer of energy to nearby cells (rather than
+absorption/emission by currents J or M).
See poynting and meanas.fdtd for more details.
+Args:
+ s: Poynting vector, as calculated with poynting. Optional; caller
+ can provide e and h instead.
+ e: E-field (optional; need either s or both e and h)
+ h: H-field (optional; need either s or both e and h)
+ dxes: Grid description; see meanas.fdmath.
Returns:
+| Name | Type | +Description | +
|---|---|---|
ds |
+ fdfield_t
+ |
+
+
+
+ Divergence of the poynting vector. +Entries indicate the net energy flow out of the corresponding +energy cell. + |
+
energy_hstep
+
+
+¶energy_hstep(
+ e0: fdfield,
+ h1: fdfield,
+ e2: fdfield,
+ epsilon: fdfield | None = None,
+ mu: fdfield | None = None,
+ dxes: dx_lists_t | None = None,
+) -> fdfield_t
+Calculate energy U at the time of the provided H-field h1.
TODO: Figure out what this means spatially.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ e0
+ |
+
+ fdfield
+ |
+
+
+
+ E-field one half-timestep before the energy. + |
+ + required + | +
+ h1
+ |
+
+ fdfield
+ |
+
+
+
+ H-field (at the same timestep as the energy). + |
+ + required + | +
+ e2
+ |
+
+ fdfield
+ |
+
+
+
+ E-field one half-timestep after the energy. + |
+ + required + | +
+ epsilon
+ |
+
+ fdfield | None
+ |
+
+
+
+ Dielectric constant distribution. + |
+
+ None
+ |
+
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability distribution. + |
+
+ None
+ |
+
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t
+ |
+
+
+
+ Energy, at the time of the H-field |
+
energy_estep
+
+
+¶energy_estep(
+ h0: fdfield,
+ e1: fdfield,
+ h2: fdfield,
+ epsilon: fdfield | None = None,
+ mu: fdfield | None = None,
+ dxes: dx_lists_t | None = None,
+) -> fdfield_t
+Calculate energy U at the time of the provided E-field e1.
TODO: Figure out what this means spatially.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ h0
+ |
+
+ fdfield
+ |
+
+
+
+ H-field one half-timestep before the energy. + |
+ + required + | +
+ e1
+ |
+
+ fdfield
+ |
+
+
+
+ E-field (at the same timestep as the energy). + |
+ + required + | +
+ h2
+ |
+
+ fdfield
+ |
+
+
+
+ H-field one half-timestep after the energy. + |
+ + required + | +
+ epsilon
+ |
+
+ fdfield | None
+ |
+
+
+
+ Dielectric constant distribution. + |
+
+ None
+ |
+
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability distribution. + |
+
+ None
+ |
+
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t
+ |
+
+
+
+ Energy, at the time of the E-field |
+
delta_energy_h2e
+
+
+¶delta_energy_h2e(
+ dt: float,
+ e0: fdfield,
+ h1: fdfield,
+ e2: fdfield,
+ h3: fdfield,
+ epsilon: fdfield | None = None,
+ mu: fdfield | None = None,
+ dxes: dx_lists_t | None = None,
+) -> fdfield_t
+Change in energy during the half-step from h1 to e2.
This is just from (e2 * e2 + h3 * h1) - (h1 * h1 + e0 * e2)
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ e0
+ |
+
+ fdfield
+ |
+
+
+
+ E-field one half-timestep before the start of the energy delta. + |
+ + required + | +
+ h1
+ |
+
+ fdfield
+ |
+
+
+
+ H-field at the start of the energy delta. + |
+ + required + | +
+ e2
+ |
+
+ fdfield
+ |
+
+
+
+ E-field at the end of the energy delta (one half-timestep after |
+ + required + | +
+ h3
+ |
+
+ fdfield
+ |
+
+
+
+ H-field one half-timestep after the end of the energy delta. + |
+ + required + | +
+ epsilon
+ |
+
+ fdfield | None
+ |
+
+
+
+ Dielectric constant distribution. + |
+
+ None
+ |
+
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability distribution. + |
+
+ None
+ |
+
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t
+ |
+
+
+
+ Change in energy from the time of |
+
delta_energy_e2h
+
+
+¶delta_energy_e2h(
+ dt: float,
+ h0: fdfield,
+ e1: fdfield,
+ h2: fdfield,
+ e3: fdfield,
+ epsilon: fdfield | None = None,
+ mu: fdfield | None = None,
+ dxes: dx_lists_t | None = None,
+) -> fdfield_t
+Change in energy during the half-step from e1 to h2.
This is just from (h2 * h2 + e3 * e1) - (e1 * e1 + h0 * h2)
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ h0
+ |
+
+ fdfield
+ |
+
+
+
+ E-field one half-timestep before the start of the energy delta. + |
+ + required + | +
+ e1
+ |
+
+ fdfield
+ |
+
+
+
+ H-field at the start of the energy delta. + |
+ + required + | +
+ h2
+ |
+
+ fdfield
+ |
+
+
+
+ E-field at the end of the energy delta (one half-timestep after |
+ + required + | +
+ e3
+ |
+
+ fdfield
+ |
+
+
+
+ H-field one half-timestep after the end of the energy delta. + |
+ + required + | +
+ epsilon
+ |
+
+ fdfield | None
+ |
+
+
+
+ Dielectric constant distribution. + |
+
+ None
+ |
+
+ mu
+ |
+
+ fdfield | None
+ |
+
+
+
+ Magnetic permeability distribution. + |
+
+ None
+ |
+
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; see |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t
+ |
+
+
+
+ Change in energy from the time of |
+
delta_energy_j
+
+
+¶Calculate the electric-current work term \(J \cdot E\) on the Yee grid.
+This is the source contribution that appears beside the flux divergence in
+the discrete Poynting identities documented in meanas.fdtd.
Note that each value of J contributes twice in a full Yee cycle (once per
+half-step energy balance) even though it directly changes E only once.
Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ j0
+ |
+
+ fdfield
+ |
+
+
+
+ Electric-current density sampled at the same half-step as the +current work term. + |
+ + required + | +
+ e1
+ |
+
+ fdfield
+ |
+
+
+
+ Electric field sampled at the matching integer timestep. + |
+ + required + | +
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; defaults to unit spacing. + |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t
+ |
+
+
+
+ Per-cell source-work contribution with the scalar field shape. + |
+
dxmul
+
+
+¶dxmul(
+ ee: fdfield,
+ hh: fdfield,
+ epsilon: fdfield | float | None = None,
+ mu: fdfield | float | None = None,
+ dxes: dx_lists_t | None = None,
+) -> fdfield_t
+Multiply E- and H-like field products by material weights and cell volumes.
+ + +Parameters:
+| Name | +Type | +Description | +Default | +
|---|---|---|---|
+ ee
+ |
+
+ fdfield
+ |
+
+
+
+ Three-component electric-field product, such as |
+ + required + | +
+ hh
+ |
+
+ fdfield
+ |
+
+
+
+ Three-component magnetic-field product, such as |
+ + required + | +
+ epsilon
+ |
+
+ fdfield | float | None
+ |
+
+
+
+ Electric material weight; defaults to |
+
+ None
+ |
+
+ mu
+ |
+
+ fdfield | float | None
+ |
+
+
+
+ Magnetic material weight; defaults to |
+
+ None
+ |
+
+ dxes
+ |
+
+ dx_lists_t | None
+ |
+
+
+
+ Grid description; defaults to unit spacing. + |
+
+ None
+ |
+
Returns:
+| Type | +Description | +
|---|---|
+ fdfield_t
+ |
+
+
+
+ Scalar field containing the weighted electric plus magnetic contribution + |
+
+ fdfield_t
+ |
+
+
+
+ for each Yee cell. + |
+
meanas.fdtd.phasor
+
+
+¶Helpers for extracting single- or multi-frequency phasors from FDTD samples.
+These helpers are intentionally low-level: callers own the accumulator arrays and +the sampling policy. The accumulated quantity is
+dt * sum(weight * exp(-1j * omega * t_step) * sample_step)
+where t_step = (step + offset_steps) * dt.
The usual Yee offsets are:
+accumulate_phasor_e(..., step=l) for E_laccumulate_phasor_h(..., step=l) for H_{l + 1/2}accumulate_phasor_j(..., step=l) for J_{l + 1/2}temporal_phasor(...) and temporal_phasor_scale(...) apply the same Fourier
+sum to a 1D scalar waveform. They are useful for normalizing a pulsed source
+before that scalar waveform is applied to a point source or spatial mode source.
+real_injection_scale(...) is a companion helper for the common real-valued
+injection pattern numpy.real(scale * waveform), where waveform is the
+analytic positive-frequency signal and the injected real current should still
+produce a desired phasor response.
+reconstruct_real(...) and its E/H/J wrappers perform the inverse operation:
+they turn one or more phasors back into real-valued field snapshots at explicit
+Yee-aligned sample times. For a scalar target frequency they accept either a
+plain field phasor or the batched (1, *sample_shape) form used elsewhere in
+this module.
These helpers do not choose warmup/accumulation windows or pulse-envelope
+normalization. They also do not impose a current sign convention. In this
+codebase, electric-current injection normally appears as E -= dt * J / epsilon,
+which matches the FDFD right-hand side -1j * omega * J.
accumulate_phasor
+
+
+¶accumulate_phasor(
+ accumulator: NDArray[complexfloating],
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ sample: ArrayLike,
+ step: int,
+ *,
+ offset_steps: float = 0.0,
+ weight: ArrayLike = 1.0,
+) -> NDArray[numpy.complexfloating]
+Add one time-domain sample into a phasor accumulator.
+The added quantity is
+dt * weight * exp(-1j * omega * t_step) * sample
+where t_step = (step + offset_steps) * dt.
This helper already multiplies by dt. If the caller's normalization
+factor was derived from a discrete sum that already includes dt, pass
+weight / dt here.
temporal_phasor
+
+
+¶temporal_phasor(
+ samples: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ *,
+ start_step: int = 0,
+ offset_steps: float = 0.0,
+) -> NDArray[numpy.complexfloating]
+Fourier-project a 1D temporal waveform onto one or more angular frequencies.
+The returned quantity is
+dt * sum(exp(-1j * omega * t_step) * samples[step_index])
+where t_step = (start_step + step_index + offset_steps) * dt.
temporal_phasor_scale
+
+
+¶temporal_phasor_scale(
+ samples: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ *,
+ start_step: int = 0,
+ offset_steps: float = 0.0,
+ target: ArrayLike = 1.0,
+) -> NDArray[numpy.complexfloating]
+Return the scalar multiplier that gives a desired temporal phasor response.
+The returned scale satisfies
+temporal_phasor(scale * samples, omegas, dt, ...) == target
+for each target frequency. The result keeps a leading frequency axis even
+when omegas is scalar.
real_injection_scale
+
+
+¶real_injection_scale(
+ samples: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ *,
+ start_step: int = 0,
+ offset_steps: float = 0.0,
+ target: ArrayLike = 1.0,
+) -> NDArray[numpy.complexfloating]
+Return the scale for a real-valued injection built from an analytic waveform.
+If the time-domain source is applied as
+numpy.real(scale * samples[step])
+then the desired positive-frequency phasor is obtained by compensating for +the 1/2 factor between the real-valued source and its analytic component:
+scale = 2 * target / temporal_phasor(samples, ...)
+This helper normalizes only the intended positive-frequency component. Any +residual negative-frequency leakage is controlled by the waveform design and +the accumulation window.
+ + + reconstruct_real
+
+
+¶reconstruct_real(
+ phasors: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ step: int,
+ *,
+ offset_steps: float = 0.0,
+) -> NDArray[numpy.floating]
+Reconstruct a real-valued field snapshot from one or more phasors.
+The returned quantity is
+real(phasor * exp(1j * omega * t_step))
+where t_step = (step + offset_steps) * dt.
For multi-frequency inputs, the leading frequency axis is preserved. For a
+scalar omega, callers may pass either (1, *sample_shape) or
+sample_shape; the return shape matches that choice.
accumulate_phasor_e
+
+
+¶accumulate_phasor_e(
+ accumulator: NDArray[complexfloating],
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ sample: ArrayLike,
+ step: int,
+ *,
+ weight: ArrayLike = 1.0,
+) -> NDArray[numpy.complexfloating]
+Accumulate an E-field sample taken at integer timestep step.
accumulate_phasor_h
+
+
+¶accumulate_phasor_h(
+ accumulator: NDArray[complexfloating],
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ sample: ArrayLike,
+ step: int,
+ *,
+ weight: ArrayLike = 1.0,
+) -> NDArray[numpy.complexfloating]
+Accumulate an H-field sample corresponding to H_{step + 1/2}.
accumulate_phasor_j
+
+
+¶accumulate_phasor_j(
+ accumulator: NDArray[complexfloating],
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ sample: ArrayLike,
+ step: int,
+ *,
+ weight: ArrayLike = 1.0,
+) -> NDArray[numpy.complexfloating]
+Accumulate a current sample corresponding to J_{step + 1/2}.
reconstruct_real_e
+
+
+¶reconstruct_real_e(
+ phasors: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ step: int,
+) -> NDArray[numpy.floating]
+Reconstruct a real E-field snapshot taken at integer timestep step.
reconstruct_real_h
+
+
+¶reconstruct_real_h(
+ phasors: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ step: int,
+) -> NDArray[numpy.floating]
+Reconstruct a real H-field snapshot corresponding to H_{step + 1/2}.
reconstruct_real_j
+
+
+¶reconstruct_real_j(
+ phasors: ArrayLike,
+ omegas: float
+ | complex
+ | Sequence[float | complex]
+ | NDArray,
+ dt: float,
+ step: int,
+) -> NDArray[numpy.floating]
+Reconstruct a real current snapshot corresponding to J_{step + 1/2}.
The package is documented directly from its docstrings. The most useful entry +points are:
+The waveguide and FDTD pages are the best places to start if you want the +mathematical derivations rather than just the callable reference.
+ + + + + + + + + + + + + + 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. + |
+
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. + |
+
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
+
+
+¶hxy2h(
+ wavenumber: complex,
+ dxes: dx_lists2_t,
+ 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 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
+
+
+¶h2e(
+ wavenumber: complex,
+ omega: complex,
+ dxes: dx_lists2_t,
+ epsilon: vfdslice,
+) -> sparse.sparray
+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:
+solve_mode(...).compute_source(...).compute_overlap_e(...) for port readout.polarity is part of the public convention throughout this module:
+1 means forward propagation toward increasing index along axis-1 means backward propagation toward decreasing index along axisThat 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,
+) -> Waveguide3DMode
+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 | +
|---|---|
+ Waveguide3DMode
+ |
+
+
+
+ Dictionary containing: + |
+
+ Waveguide3DMode
+ |
+
+
+
+
|
+
+ Waveguide3DMode
+ |
+
+
+
+
|
+
+ Waveguide3DMode
+ |
+
+
+
+
|
+
+ Waveguide3DMode
+ |
+
+
+
+
|
+
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
+ |
+
+
+
+
|
+
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,
+ 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:
+polarity=+1, the two cells just before slices[axis].startpolarity=-1, the two cells just after slices[axis].stopThe 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
+ |
+
+
+
+ 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
+ |
+
+
+
+
|
+
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[vcfdfield2, complex]
+ |
+
+
+
+ (e_xy, angular_wavenumber) + |
+
linear_wavenumbers
+
+
+¶linear_wavenumbers(
+ e_xys: Sequence[vcfdfield2] | NDArray[complex128],
+ 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
+ |
+
+ Sequence[vcfdfield2] | NDArray[complex128]
+ |
+
+
+
+ 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
+
+
+¶dxes2T(
+ dxes: dx_lists2_t, rmin: float
+) -> tuple[NDArray[numpy.float64], NDArray[numpy.float64]]
+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. + |
+
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.
0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o