[docs] expand API and derivation docs

This commit is contained in:
Jan Petykiewicz 2026-04-18 14:24:18 -07:00
commit 5e95d66a7e
12 changed files with 608 additions and 127 deletions

View file

@ -1,7 +1,12 @@
"""
Example code for running an FDTD simulation
Example code for a broadband FDTD run with phasor extraction.
See main() for simulation setup.
This script shows the intended low-level workflow for:
1. building a Yee-grid simulation with CPML on all faces,
2. driving it with an electric-current pulse,
3. extracting a single-frequency phasor on the fly, and
4. checking that phasor against the matching stretched-grid FDFD operator.
"""
import sys
@ -150,7 +155,8 @@ def main():
# print(f'Save time interval would be {sample_interval} * dt = {sample_interval * dt:3g}')
# Source parameters and function
# Source parameters and function. The pulse normalization is kept outside
# accumulate_phasor(); the helper only performs the Fourier sum.
source_phasor, delay = gaussian_packet(wl=wl, dwl=100, dt=dt, turn_on=1e-5)
aa, cc, ss = source_phasor(numpy.arange(max_t))
srca_real = aa * cc
@ -170,7 +176,8 @@ def main():
update_E(ee, hh, epsilon)
if tt < src_maxt:
# This codebase uses E -= dt * J / epsilon for electric-current injection.
# Electric-current injection uses E -= dt * J / epsilon, which is
# the same sign convention used by the matching FDFD right-hand side.
ee[1, *(grid.shape // 2)] -= srca_real[tt]
update_H(ee, hh)
@ -193,9 +200,11 @@ def main():
dt,
ee,
tt,
# The pulse is delayed relative to t=0, so the readout needs the same phase shift.
# The pulse is delayed relative to t=0, so the extracted phasor
# needs the same phase offset in its sample times.
offset_steps=0.5 - delay / dt,
# accumulate_phasor() already includes dt, so undo the dt in phasor_norm here.
# accumulate_phasor() already multiplies by dt, so pass the
# discrete-sum normalization without its extra dt factor.
weight=phasor_norm / dt,
)
@ -205,6 +214,8 @@ def main():
for pp in (-1, +1):
for dd in range(3):
stretch_with_scpml(dxes_fdfd, axis=dd, polarity=pp, omega=omega, epsilon_effective=n_air ** 2, thickness=pml_thickness)
# Compare the extracted phasor to the FDFD operator on the stretched grid,
# not the unstretched Yee spacings used by the raw time-domain update.
A = e_full(omega=omega, dxes=dxes_fdfd, epsilon=epsilon)
residual = norm(A @ vec(Eph) - vec(b)) / norm(vec(b))
print(f'FDFD residual is {residual}')