[docs] clarify FDFD-to-FDTD field reconstruction
This commit is contained in:
parent
e50637dc1c
commit
40efe7a450
5 changed files with 51 additions and 5 deletions
14
README.md
14
README.md
|
|
@ -194,6 +194,10 @@ For a broadband or continuous-wave FDTD run:
|
||||||
part of the simulation, and compare the extracted phasor to the FDFD field or
|
part of the simulation, and compare the extracted phasor to the FDFD field or
|
||||||
residual.
|
residual.
|
||||||
|
|
||||||
|
This is the primary FDTD/FDFD equivalence workflow. The phasor extraction step
|
||||||
|
filters the time-domain run down to the guided `+\omega` content that FDFD
|
||||||
|
solves for directly, so it is the cleanest apples-to-apples comparison.
|
||||||
|
|
||||||
### Real-field reconstruction workflow
|
### Real-field reconstruction workflow
|
||||||
|
|
||||||
For a continuous-wave real-valued FDTD run:
|
For a continuous-wave real-valued FDTD run:
|
||||||
|
|
@ -211,4 +215,14 @@ For a continuous-wave real-valued FDTD run:
|
||||||
pieces to see whether the remaining mismatch is actually in the mode or in
|
pieces to see whether the remaining mismatch is actually in the mode or in
|
||||||
weak nonguided tails.
|
weak nonguided tails.
|
||||||
|
|
||||||
|
This is a stricter diagnostic, not the primary equivalence benchmark. A raw
|
||||||
|
monitor slice contains both the guided field and the remaining orthogonal
|
||||||
|
content on that plane,
|
||||||
|
|
||||||
|
$$ E_{\text{monitor}} = E_{\text{guided}} + E_{\text{residual}} , $$
|
||||||
|
|
||||||
|
so its full-plane instantaneous error is naturally noisier than the extracted
|
||||||
|
phasor comparison even when the underlying guided `+\omega` content matches
|
||||||
|
well.
|
||||||
|
|
||||||
`examples/waveguide_real.py` is the reference implementation of this workflow.
|
`examples/waveguide_real.py` is the reference implementation of this workflow.
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,15 @@ Relevant starting examples:
|
||||||
mode-weighted, and guided-mode / residual comparisons
|
mode-weighted, and guided-mode / residual comparisons
|
||||||
- `examples/fdfd.py` for direct frequency-domain waveguide excitation
|
- `examples/fdfd.py` for direct frequency-domain waveguide excitation
|
||||||
|
|
||||||
|
For solver equivalence, prefer the phasor-based examples first. They compare
|
||||||
|
the extracted `+\omega` content of the FDTD run directly against the FDFD
|
||||||
|
solution and are the main accuracy benchmarks in the test suite.
|
||||||
|
|
||||||
|
`examples/waveguide_real.py` answers a different, stricter question: how well a
|
||||||
|
late raw real snapshot matches `Re(E_\omega e^{i\omega t})` on a monitor plane.
|
||||||
|
That diagnostic is useful, but it also includes orthogonal residual structure
|
||||||
|
that the phasor comparison intentionally filters out.
|
||||||
|
|
||||||
The API pages are better read as a toolbox map and derivation reference:
|
The API pages are better read as a toolbox map and derivation reference:
|
||||||
|
|
||||||
- Use the [FDTD API](api/fdtd.md) for time-domain stepping, CPML, phasor
|
- Use the [FDTD API](api/fdtd.md) for time-domain stepping, CPML, phasor
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,12 @@ FDFD" workflow:
|
||||||
3. solve the matching FDFD problem from the analytic source phasor, and
|
3. solve the matching FDFD problem from the analytic source phasor, and
|
||||||
4. compare late real monitor slices against `fdtd.reconstruct_real_e/h(...)`.
|
4. compare late real monitor slices against `fdtd.reconstruct_real_e/h(...)`.
|
||||||
|
|
||||||
Unlike the complex-source examples, this script does not use phasor extraction
|
Unlike the phasor-based examples, this script does not use extracted phasors as
|
||||||
as the main output. The comparison target is the real field itself, with both
|
the main output. It is a stricter diagnostic: the comparison target is the raw
|
||||||
full-plane and mode-weighted monitor errors reported.
|
real field itself, with full-plane, mode-weighted, guided-mode, and orthogonal-
|
||||||
|
residual errors reported. Strong phasor agreement can coexist with visibly
|
||||||
|
larger raw-snapshot error because the latter includes weak nonguided tails on
|
||||||
|
the monitor plane.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
@ -104,7 +107,7 @@ def main() -> None:
|
||||||
epsilon=epsilon,
|
epsilon=epsilon,
|
||||||
)
|
)
|
||||||
# A small global phase aligns the real-valued source with the late-cycle
|
# A small global phase aligns the real-valued source with the late-cycle
|
||||||
# monitor comparison. The underlying phasor problem is unchanged.
|
# raw-snapshot diagnostic. The underlying phasor problem is unchanged.
|
||||||
j_mode *= numpy.exp(1j * SOURCE_PHASE)
|
j_mode *= numpy.exp(1j * SOURCE_PHASE)
|
||||||
monitor_mode = waveguide_3d.solve_mode(
|
monitor_mode = waveguide_3d.solve_mode(
|
||||||
0,
|
0,
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,11 @@ with caller-provided sample times and weights. In this codebase the matching
|
||||||
electric-current convention is typically `E -= dt * J / epsilon` in FDTD and
|
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.
|
`-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
|
For exact pulsed FDTD/FDFD equivalence it is often simplest to keep the
|
||||||
injected source, fields, and CPML auxiliary state complex-valued. The
|
injected source, fields, and CPML auxiliary state complex-valued. The
|
||||||
`real_injection_scale(...)` helper is instead for the more ordinary one-run
|
`real_injection_scale(...)` helper is instead for the more ordinary one-run
|
||||||
|
|
@ -172,6 +177,17 @@ real-valued source path, where the intended positive-frequency waveform is
|
||||||
injected through `numpy.real(scale * waveform)` and any remaining negative-
|
injected through `numpy.real(scale * waveform)` and any remaining negative-
|
||||||
frequency leakage is controlled by the pulse bandwidth and run length.
|
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,
|
||||||
|
|
||||||
|
$$ E_{\text{monitor}} = E_{\text{guided}} + E_{\text{residual}} . $$
|
||||||
|
|
||||||
|
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
|
The Ricker wavelet (normalized second derivative of a Gaussian) is commonly used for the pulse
|
||||||
shape. It can be written
|
shape. It can be written
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -711,7 +711,11 @@ def test_straight_waveguide_fdtd_fdfd_overlap_and_flux_agree() -> None:
|
||||||
assert result.overlap_phase_deg < 0.5
|
assert result.overlap_phase_deg < 0.5
|
||||||
|
|
||||||
|
|
||||||
def test_straight_waveguide_real_monitor_fields_match_reconstructed_real_fields() -> None:
|
def test_straight_waveguide_real_snapshot_diagnostic_tracks_guided_content_and_bounded_residual() -> None:
|
||||||
|
# The phasor-based waveguide tests above are the primary FDTD/FDFD
|
||||||
|
# equivalence benchmark. This raw real-field check is intentionally stricter:
|
||||||
|
# it validates that late monitor snapshots keep the guided content close to
|
||||||
|
# the reconstructed FDFD field while the orthogonal residual stays bounded.
|
||||||
result = _run_real_field_straight_waveguide_case()
|
result = _run_real_field_straight_waveguide_case()
|
||||||
ranked_snapshots = sorted(
|
ranked_snapshots = sorted(
|
||||||
result.snapshots,
|
result.snapshots,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue