[waveguide_real / phasor] more work towards real-FDTD to FDFD equivalence
This commit is contained in:
parent
f7aa21a42a
commit
e50637dc1c
7 changed files with 213 additions and 25 deletions
|
|
@ -10,7 +10,8 @@ FDFD" workflow:
|
|||
4. compare late real monitor slices against `fdtd.reconstruct_real_e/h(...)`.
|
||||
|
||||
Unlike the complex-source examples, this script does not use phasor extraction
|
||||
as the main output. The comparison target is the real field itself.
|
||||
as the main output. The comparison target is the real field itself, with both
|
||||
full-plane and mode-weighted monitor errors reported.
|
||||
"""
|
||||
|
||||
import numpy
|
||||
|
|
@ -28,6 +29,8 @@ SHAPE = (3, 37, 13, 13)
|
|||
SOURCE_SLICES = (slice(5, 6), slice(None), slice(None))
|
||||
MONITOR_SLICES = (slice(30, 31), slice(None), slice(None))
|
||||
WARMUP_PERIODS = 16
|
||||
SOURCE_PHASE = 0.4
|
||||
CORE_SLICES = (slice(None), slice(None), slice(4, 9), slice(4, 9))
|
||||
|
||||
|
||||
def build_uniform_dxes(shape: tuple[int, int, int, int]) -> list[list[numpy.ndarray]]:
|
||||
|
|
@ -65,6 +68,17 @@ def build_cpml_params() -> list[list[dict[str, numpy.ndarray | float]]]:
|
|||
]
|
||||
|
||||
|
||||
def weighted_rel_err(observed: numpy.ndarray, reference: numpy.ndarray, weight: numpy.ndarray) -> float:
|
||||
return numpy.linalg.norm((observed - reference) * weight) / numpy.linalg.norm(reference * weight)
|
||||
|
||||
|
||||
def project_onto_mode(observed: numpy.ndarray, mode: numpy.ndarray) -> tuple[complex, numpy.ndarray, numpy.ndarray]:
|
||||
coefficient = numpy.vdot(mode, observed) / numpy.vdot(mode, mode)
|
||||
guided = coefficient * mode
|
||||
residual = observed - guided
|
||||
return coefficient, guided, residual
|
||||
|
||||
|
||||
def main() -> None:
|
||||
epsilon = build_epsilon(SHAPE)
|
||||
base_dxes = build_uniform_dxes(SHAPE)
|
||||
|
|
@ -89,6 +103,22 @@ def main() -> None:
|
|||
slices=SOURCE_SLICES,
|
||||
epsilon=epsilon,
|
||||
)
|
||||
# A small global phase aligns the real-valued source with the late-cycle
|
||||
# monitor comparison. The underlying phasor problem is unchanged.
|
||||
j_mode *= numpy.exp(1j * SOURCE_PHASE)
|
||||
monitor_mode = waveguide_3d.solve_mode(
|
||||
0,
|
||||
omega=OMEGA,
|
||||
dxes=base_dxes,
|
||||
axis=0,
|
||||
polarity=1,
|
||||
slices=MONITOR_SLICES,
|
||||
epsilon=epsilon,
|
||||
)
|
||||
e_weight = numpy.abs(monitor_mode['E'][:, MONITOR_SLICES[0], :, :])
|
||||
h_weight = numpy.abs(monitor_mode['H'][:, MONITOR_SLICES[0], :, :])
|
||||
e_mode = monitor_mode['E'][:, MONITOR_SLICES[0], :, :]
|
||||
h_mode = monitor_mode['H'][:, MONITOR_SLICES[0], :, :]
|
||||
|
||||
e_fdfd = unvec(
|
||||
fdfd.solvers.generic(
|
||||
|
|
@ -114,6 +144,14 @@ def main() -> None:
|
|||
total_steps = (WARMUP_PERIODS + 1) * PERIOD_STEPS
|
||||
e_errors: list[float] = []
|
||||
h_errors: list[float] = []
|
||||
e_core_errors: list[float] = []
|
||||
h_core_errors: list[float] = []
|
||||
e_weighted_errors: list[float] = []
|
||||
h_weighted_errors: list[float] = []
|
||||
e_guided_errors: list[float] = []
|
||||
h_guided_errors: list[float] = []
|
||||
e_residual_errors: list[float] = []
|
||||
h_residual_errors: list[float] = []
|
||||
|
||||
for step in range(total_steps):
|
||||
update_e(e_field, h_field, epsilon)
|
||||
|
|
@ -127,25 +165,51 @@ def main() -> None:
|
|||
|
||||
if step >= total_steps - PERIOD_STEPS // 4:
|
||||
reconstructed_e = fdtd.reconstruct_real_e(
|
||||
e_fdfd[:, MONITOR_SLICES[0], :, :][numpy.newaxis, ...],
|
||||
e_fdfd[:, MONITOR_SLICES[0], :, :],
|
||||
OMEGA,
|
||||
DT,
|
||||
step + 1,
|
||||
)[0]
|
||||
)
|
||||
reconstructed_h = fdtd.reconstruct_real_h(
|
||||
h_fdfd[:, MONITOR_SLICES[0], :, :][numpy.newaxis, ...],
|
||||
h_fdfd[:, MONITOR_SLICES[0], :, :],
|
||||
OMEGA,
|
||||
DT,
|
||||
step + 1,
|
||||
)[0]
|
||||
)
|
||||
|
||||
e_monitor = e_field[:, MONITOR_SLICES[0], :, :]
|
||||
h_monitor = h_field[:, MONITOR_SLICES[0], :, :]
|
||||
e_errors.append(numpy.linalg.norm(e_monitor - reconstructed_e) / numpy.linalg.norm(reconstructed_e))
|
||||
h_errors.append(numpy.linalg.norm(h_monitor - reconstructed_h) / numpy.linalg.norm(reconstructed_h))
|
||||
e_core_errors.append(
|
||||
numpy.linalg.norm(e_monitor[CORE_SLICES] - reconstructed_e[CORE_SLICES])
|
||||
/ numpy.linalg.norm(reconstructed_e[CORE_SLICES]),
|
||||
)
|
||||
h_core_errors.append(
|
||||
numpy.linalg.norm(h_monitor[CORE_SLICES] - reconstructed_h[CORE_SLICES])
|
||||
/ numpy.linalg.norm(reconstructed_h[CORE_SLICES]),
|
||||
)
|
||||
e_weighted_errors.append(weighted_rel_err(e_monitor, reconstructed_e, e_weight))
|
||||
h_weighted_errors.append(weighted_rel_err(h_monitor, reconstructed_h, h_weight))
|
||||
e_guided_coeff, _, e_residual = project_onto_mode(e_monitor, e_mode)
|
||||
e_guided_coeff_ref, _, e_residual_ref = project_onto_mode(reconstructed_e, e_mode)
|
||||
h_guided_coeff, _, h_residual = project_onto_mode(h_monitor, h_mode)
|
||||
h_guided_coeff_ref, _, h_residual_ref = project_onto_mode(reconstructed_h, h_mode)
|
||||
e_guided_errors.append(abs(e_guided_coeff - e_guided_coeff_ref) / abs(e_guided_coeff_ref))
|
||||
h_guided_errors.append(abs(h_guided_coeff - h_guided_coeff_ref) / abs(h_guided_coeff_ref))
|
||||
e_residual_errors.append(numpy.linalg.norm(e_residual - e_residual_ref) / numpy.linalg.norm(e_residual_ref))
|
||||
h_residual_errors.append(numpy.linalg.norm(h_residual - h_residual_ref) / numpy.linalg.norm(h_residual_ref))
|
||||
|
||||
print(f'late-cycle monitor E errors: min={min(e_errors):.4f} max={max(e_errors):.4f}')
|
||||
print(f'late-cycle monitor H errors: min={min(h_errors):.4f} max={max(h_errors):.4f}')
|
||||
print(f'late-cycle core-window E errors: min={min(e_core_errors):.4f} max={max(e_core_errors):.4f}')
|
||||
print(f'late-cycle core-window H errors: min={min(h_core_errors):.4f} max={max(h_core_errors):.4f}')
|
||||
print(f'late-cycle mode-weighted E errors: min={min(e_weighted_errors):.4f} max={max(e_weighted_errors):.4f}')
|
||||
print(f'late-cycle mode-weighted H errors: min={min(h_weighted_errors):.4f} max={max(h_weighted_errors):.4f}')
|
||||
print(f'late-cycle guided-mode E coefficient errors: min={min(e_guided_errors):.4f} max={max(e_guided_errors):.4f}')
|
||||
print(f'late-cycle guided-mode H coefficient errors: min={min(h_guided_errors):.4f} max={max(h_guided_errors):.4f}')
|
||||
print(f'late-cycle orthogonal-residual E errors: min={min(e_residual_errors):.4f} max={max(e_residual_errors):.4f}')
|
||||
print(f'late-cycle orthogonal-residual H errors: min={min(h_residual_errors):.4f} max={max(h_residual_errors):.4f}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue