Skip to content

GNLSE validation & convergence harness

Grid/step convergence studies and cited analytical validation checks for the GNLSE. Docstrings are the source of truth, rendered with mkdocstrings (numpydoc style). See GNLSE physics for the physics background and references.

photonics_helper.gnlse_validation

GNLSE validation and convergence harness.

Two related tools for demonstrating numerical credibility on the user's own configuration (the "research-first" validation stance of this library):

  1. :func:convergence_study — run a caller-supplied GNLSE factory at a sequence of refined temporal grids / step sizes and report, per caller-selected observable, the value at each resolution, the relative change between successive refinements, and a converged verdict at a caller tolerance (Sinkin, Holzlöhner, Zweck & Menyuk, J. Lightwave Technol. 21, 61 (2003); Agrawal, Nonlinear Fiber Optics, 5th ed., §2.4).

  2. Cited analytical checks — :func:check_spm, :func:check_mi, :func:check_soliton, :func:check_gordon_ssfs — compare a propagation result against a closed-form reference for the canonical limits:

  3. SPM: Stolen & Lin, Phys. Rev. A 17, 1448 (1978); Agrawal §4.1 (N_peaks = floor(φ_max/π) + 1).

  4. MI: Agrawal §5.1 (g(Ω) = |β₂Ω|√(Ω_c²−Ω²), Ω_c² = 4γP/|β₂|).
  5. Soliton: Agrawal §5.2 (fundamental soliton, z_sol = (π/2) L_D).
  6. SSFS: Gordon, Opt. Lett. 11, 662 (1986); dΩ/dz = −8|β₂|T_R/(15T₀⁴), T_R = f_R ∫t·h_R(t) dt.

The checks raise :class:ValidationFailure when the documented tolerance is exceeded, so they can be wired directly into application-level test suites.

Worked example: examples/35_gnlse_validation_harness.py runs all four checks, prints each measured metric beside the closed form it is compared against, runs :func:convergence_study over a z-step ladder (including the mapping form of observables), and demonstrates both failure paths — an under-resolved ladder with converged=False rows and a caught :class:ValidationFailure.

Importing this module pulls the solver stack only — no plotting backends.

ObservableFn module-attribute

ObservableFn = Callable[[Any], float]

DEFAULT_OBSERVABLES module-attribute

DEFAULT_OBSERVABLES: dict[str, ObservableFn] = {'peak_intensity': _obs_peak_intensity, 'pulse_energy': _obs_pulse_energy, 'rms_bandwidth': _obs_rms_bandwidth, 'rms_width': _obs_rms_width}

ValidationFailure

ValidationFailure(case: str, message: str, metrics: dict[str, Any] | None = None)

Bases: AssertionError

Raised by the check_* helpers when a closed-form check fails.

case instance-attribute

case = case

metrics instance-attribute

metrics = metrics or {}

ObservableReport dataclass

ObservableReport(name: str, values: list[float], changes: list[float | None], change_last: float | None, converged: bool)

Per-observable convergence data (Sinkin et al. 2003).

name instance-attribute

name: str

values instance-attribute

values: list[float]

changes instance-attribute

changes: list[float | None]

change_last instance-attribute

change_last: float | None

converged instance-attribute

converged: bool

ConvergenceReport dataclass

ConvergenceReport(refinements: list[dict[str, Any]] = list(), observables: list[ObservableReport] = list())

Result of :func:convergence_study.

refinements class-attribute instance-attribute

refinements: list[dict[str, Any]] = field(default_factory=list)

observables class-attribute instance-attribute

observables: list[ObservableReport] = field(default_factory=list)

converged property

converged: bool

True when every selected observable converged at the finest pair.

summary

summary() -> str

convergence_study

convergence_study(build_solver: Callable[..., Any], refinements: Sequence[Mapping[str, Any]] | None = None, *, observables: Sequence[str] | Mapping[str, ObservableFn] | None = None, tolerance: float = 0.001, shared: Mapping[str, Any] | None = None) -> ConvergenceReport

Run a GNLSE factory at successive refinements and report convergence.

Parameters:

Name Type Description Default
build_solver callable

build_solver(**refinement) -> result — the factory receives the refinement's keyword arguments (plus every entry of shared) and must return a propagated result exposing the final field (a Wave, an evolved solver with .A, or anything with .envelope_field).

required
refinements sequence of mappings

One kwargs dict per resolution, ordered coarse → fine.

None
observables sequence[str] | mapping[str, callable]

Caller-selected observables: names into :data:DEFAULT_OBSERVABLES or a custom {name: callable(result) -> float} mapping. Default: all defaults.

None
tolerance float

Relative-change threshold applied between the two finest resolutions.

0.001
shared mapping

Factory kwargs forwarded to every refinement.

None

Returns:

Type Description
ConvergenceReport

check_spm

check_spm(gamma: float, peak_power: float, t0: float, wavelength_m: float, phi_max: float, grid: Any, tolerance: float = 0.005) -> dict[str, Any]

Kerr-only SPM: compare the propagated spectrum with the closed form.

For a Gaussian input with zero dispersion the exact output field is A(L,t) = √P₀ e^{−t²/2T₀²} e^{i φ_max e^{−t²/T₀²}} with φ_max = γP₀L (Stolen & Lin 1978; Agrawal §4.1). Checks (i) the simulated spectrum against the closed-form Fourier integral and (ii) the fringe rule N_peaks = floor(φ_max/π) + 1.

Parameters:

Name Type Description Default
grid TemporalGrid
required
tolerance float

Max absolute deviation between normalized spectra.

0.005

Returns:

Type Description
dict — the validation metrics (also returned on success).

Raises:

Type Description
ValidationFailure — when either check fails.

mi_gain_of

mi_gain_of(beta2: float, gamma: float, P: float, omega: float) -> float

Exact linear-stability MI gain g(Ω) at a single offset (Agrawal 5.1.9).

g(Ω) = |β₂Ω|√(Ω_c²−Ω²), Ω_c² = 4γP/|β₂|, in the power-gain convention (sideband intensity grows as e^{g z}) used throughout this library — validated by :func:check_mi and the Dudley Fig. 23 reproduction (g_max = 2γP).

gordon_ssfs_rate

gordon_ssfs_rate(beta2: float, gamma: float, peak_power: float, t0: float, t_raman: float) -> float

Gordon's linear SSFS rate dΩ/dz (rad/s/m, negative = red shift).

Gordon, Opt. Lett. 11, 662 (1986), for a fundamental soliton:

dΩ/dz = −(8/15)·|β₂|·T_R/T₀⁴, T_R = f_R ∫t·h_R(t)dt.

The peak_power/gamma inputs are used to assert the soliton-order condition γP₀T₀²/|β₂| = 1 (within 1 %); pass the soliton's peak power. Dimensions: β₂ (s²/m) × T_R (s) / T₀⁴ (s⁴) → 1/(m·s³).

check_mi

check_mi(beta2: float, gamma: float, pump_power: float, wavelength_m: float, probe_omega: float, grid: Any, length: float | None = None, rel_tolerance: float = 0.15) -> dict[str, Any]

Modulation-instability gain vs the exact linear-stability result.

Seeds a CW pump with a weak real probe perturbation ε·cos(probe_omega·t) (one sideband pair on the periodic grid) and measures the exponential power-gain coefficient of the sideband pair from the late-time slope of ln(P_side(z)); compares against the closed form g(Ω) = |β₂Ω|√(Ω_c²−Ω²), Ω_c² = 4γP/|β₂| (Agrawal Eq. 5.1.9, in the power-gain convention validated against Dudley Fig. 23 by the fix-mi-gain-convention change: sideband intensity grows as e^{g z}, so the field amplitude grows as e^{g z/2}). Requires anomalous dispersion (β₂ < 0) and Ω_probe < Ω_c.

Parameters:

Name Type Description Default
grid TemporalGrid
required
length float | None — propagation length (m). When ``None``, chosen so

the analytic sideband power gain reaches a factor ≈ e⁴.

None
rel_tolerance float — allowed relative deviation of the measured gain.
0.15

Raises:

Type Description
ValidationFailure — when the measured gain deviates beyond tolerance.

check_soliton

check_soliton(beta2: float, gamma: float, t0: float, wavelength_m: float, grid: Any, periods: float = 1.0, tolerance: float = 0.02) -> dict[str, float]

Fundamental soliton: after z_sol = (π/2)·L_D the shape returns.

Propagates an N = 1 soliton over periods × z_sol and checks the field-weighted shape overlap with the input (Agrawal §5.2).

Parameters:

Name Type Description Default
grid TemporalGrid
required
periods float — number of soliton periods to propagate (default 1).
1.0
tolerance float — maximum allowed overlap deficit (1 − overlap).
0.02

Raises:

Type Description
ValidationFailure — when the overlap drops below ``1 − tolerance``.

check_gordon_ssfs

check_gordon_ssfs(beta2: float, gamma: float, peak_power: float, t0: float, wavelength_m: float, raman_response: Any, grid: Any, length: float, ratio_tolerance: float = 0.25, num_steps: int = 4000) -> dict[str, float]

Raman soliton self-frequency shift vs Gordon's analytic law.

Propagates a soliton with the fiber's Raman response and compares the measured red shift of the spectral peak against Gordon's analytic rate dΩ/dz = −8|β₂|T_R/(15 T₀⁴) with T_R = f_R·∫t·h_R(t)dt (Gordon 1986). The measured/analytic ratio must be within ratio_tolerance of unity; see reproductions/gordon_1986_ssfs (ratio 1.19 on the published benchmark configuration).

Parameters:

Name Type Description Default
grid TemporalGrid
required
length float — fibre length (m).
required
raman_response RamanResponse (or any object with ``fR`` and a public

h_R(t) / private _h_R(t)).

required

Raises:

Type Description
ValidationFailure — when the shift direction or rate is wrong.