[fdtd] reject zero and invalid CPML attenuation
Require a finite negative ln_R_per_layer before coefficient evaluation. Document None entries as the supported way to disable a face, and cover zero, signed zero, positive, and nonfinite targets with and without frequency shift. Validation: 209 focused tests passed; Ruff passed.
This commit is contained in:
parent
553cdf948c
commit
7b01ddd789
3 changed files with 17 additions and 1 deletions
|
|
@ -18,3 +18,7 @@ CPML derivatives use electric widths for forward differences and magnetic
|
|||
widths for backward differences. `cpml_params(..., dx=1.0)` specifies the uniform
|
||||
physical cell width normal to a face; arbitrary nonuniform PML grading is not
|
||||
implemented by this constructor. Scale the timestep consistently with the grid.
|
||||
|
||||
`ln_R_per_layer` must be finite and strictly negative. Zero attenuation raises
|
||||
`ValueError`; disable individual CPML faces with `None` in the parameter table
|
||||
passed to `updates_with_cpml`.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ def cpml_params(
|
|||
`+1` for the high-index face).
|
||||
dt: Timestep used by the Yee update.
|
||||
thickness: Number of Yee cells occupied by the CPML region.
|
||||
ln_R_per_layer: Logarithmic attenuation target per layer.
|
||||
ln_R_per_layer: Finite, strictly negative logarithmic attenuation target
|
||||
per layer. Zero attenuation is not supported; disable a face with
|
||||
``None`` in the updates_with_cpml parameter table instead.
|
||||
epsilon_eff: Effective permittivity used when choosing the CPML scaling.
|
||||
mu_eff: Effective permeability used when choosing the CPML scaling.
|
||||
m: Polynomial grading exponent for `sigma` and `kappa`.
|
||||
|
|
@ -82,6 +84,9 @@ def cpml_params(
|
|||
if epsilon_eff <= 0:
|
||||
raise ValueError('epsilon_eff must be positive')
|
||||
|
||||
if not numpy.isfinite(ln_R_per_layer) or ln_R_per_layer >= 0:
|
||||
raise ValueError('ln_R_per_layer must be finite and negative; use None to disable a CPML face')
|
||||
|
||||
if not numpy.isfinite(dx) or dx <= 0:
|
||||
raise ValueError('dx must be finite and positive')
|
||||
|
||||
|
|
|
|||
|
|
@ -400,3 +400,10 @@ def test_scpml_builders_agree_including_real_profiles_and_touching_faces(shape,
|
|||
for family in range(2):
|
||||
for axis in range(3):
|
||||
assert_allclose(uniform[family][axis], explicit[family][axis], rtol=1e-14, atol=1e-14)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('attenuation', [0.0, -0.0, .1, np.inf, -np.inf, np.nan])
|
||||
@pytest.mark.parametrize('alpha', [0, .2])
|
||||
def test_cpml_rejects_zero_or_invalid_attenuation_before_coefficient_evaluation(attenuation, alpha):
|
||||
with np.errstate(divide='raise', invalid='raise'), pytest.raises(ValueError, match='ln_R_per_layer must be finite and negative'):
|
||||
pml.cpml_params(0, 1, .1, ln_R_per_layer=attenuation, cfs_alpha=alpha)
|
||||
|
|
|
|||
Loading…
Reference in a new issue