fdtd¶
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.
Timestep¶
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.
Poynting Vector and Energy Conservation¶
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\)).
Sources¶
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).
Boundary conditions¶
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(...)andupdates_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:
- Build the FDTD update with the desired CPML parameters.
- Stretch the FDFD
dxeswith the matching SCPML transform. - Compare the extracted phasor against the FDFD field or residual on those
stretched
dxes.
The electric-current sign convention used throughout the examples and tests is
which matches the FDFD right-hand side
Core update and analysis helpers¶
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
- This update step: "Maxwell's equations" section
dxes: "Datastructure: dx_lists_t" sectionepsilon: "Permittivity and Permeability" section
Also 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
- This update step: "Maxwell's equations" section
dxes: "Datastructure: dx_lists_t" sectionmu: "Permittivity and Permeability" section
Also 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:
- Build a
cpml_params[axis][polarity_index]table withcpml_params(...). - Pass that table into
updates_with_cpml(...)together withdt,dxes, andepsilon. - Advance the returned
update_E/update_Hclosures 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[[fdfield_t, fdfield_t, fdfield_t], None],
Callable[[fdfield_t, fdfield_t, fdfield_t], 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[[fdfield_t, fdfield_t, fdfield_t], None]
|
|
Callable[[fdfield_t, fdfield_t, fdfield_t], None]
|
Yee updates: |
tuple[Callable[[fdfield_t, fdfield_t, fdfield_t], None], Callable[[fdfield_t, fdfield_t, fdfield_t], None]]
|
|
tuple[Callable[[fdfield_t, fdfield_t, fdfield_t], None], Callable[[fdfield_t, fdfield_t, fdfield_t], None]]
|
|
tuple[Callable[[fdfield_t, fdfield_t, fdfield_t], None], Callable[[fdfield_t, fdfield_t, fdfield_t], None]]
|
The closures retain the CPML auxiliary state internally. |
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)forE_laccumulate_phasor_h(..., step=l)forH_{l + 1/2}accumulate_phasor_j(..., step=l)forJ_{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.
Note
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}.