Skip to content

GNLSE physics: shock invariant, multi-mode Raman, free carriers, validation

This page documents four pieces of solver physics added in 0.1.9, each stated against the literature and validated by an independent analytic or invariant check. Docstrings remain the source of truth; this page gives the derivations and honest caveats in one place.

1. Interaction-picture self-steepening (RK4IP)

The nonlinear GNLSE term (Dudley, Genty & Coen, Rev. Mod. Phys. 78, 1135 (2006), Eq. (3); Blow & Wood, IEEE J. Quantum Electron. 25, 2665 (1989)) is

∂A/∂z |_NL = iγ (1 + (i/ω₀)∂_t) [ A · P_NL ] ,   P_NL = (1−f_R)|A|² + f_R (h_R ⊛ |A|²)

The step integrates the exactly-integrable phase exp(iγP_NL Δz) in two symmetric half-steps (Strang splitting) and advances only the much smaller shock correction iγτ_shock ∂_t(A·P_NL) with frequency-domain RK4 — the interaction-picture (RK4IP) idea of Hult, J. Lightwave Technol. 25, 3770 (2007) and Hochbruck & Ostermann, Acta Numerica 19, 209 (2010). No Runge–Kutta stage ever integrates the stiff nonlinear phase, and no spectral bin is clamped.

Photon-number balance — an honest caveat. A draft of this change claimed the shock term is a total time derivative, making the photon number ∫|A|²dt an exact invariant. That identity is false in general: with a real response,

d/dz ∫|A|² dt = −2γτ_shock ∫ P_NL · Im(A* ∂_t A) dt ,

which vanishes only when the field evolves under a time-independent P_NL (pure SPM with a frozen drive — then the step is exactly conservative, verified to machine precision). In general the residual is a physical error of the first-order ω/ω₀ expansion itself, O(τ_shock·Ω_max) at the band edge: a fully-resolved (heavily substepped) reference of the same flow drifts identically on fissioned states, while the RK4IP step matches that reference to ≈1e-5 in the field. No integrator can conserve what the model does not conserve; the historical max(1 + Ω·τ_shock, 0) clamp, by contrast, was a numerical corruption of valid bins and is removed.

Spectral validity guard

The factor 1 + Ω·τ_shock is the first-order form of ω/ω₀ and is only meaningful for channels with positive absolute frequency, ω = ω₀ + Ω > 0. When self-steepening is enabled the engine therefore requires Ω_max < ω₀ and raises a ValueError naming the grid values (N, Tmax), the ratio Ω_max/ω₀, and the remedy (increase Tmax, reduce N, or disable self-steepening). The grid must resolve only physical frequencies; nothing is silently discarded.

References — Blow & Wood (1989); Agrawal, Nonlinear Fiber Optics, 5th ed., §2.3; Hult (2007).

2. Multi-phonon Raman response

PhononResponse.h_R(t) builds a causal, unit-integral delayed response from the material's phonon modes (a compound-glass / multi-vibrational-mode model, Hollenbeck & Cantrell, JOSA B 19, 2886 (2002)):

h_R(t) = Z⁻¹ Σ_i w_i e^{−t/τ_i} sin(ω_i t) θ(t),   τ_i = 2/γ_i,   ω_i = 2πc·ν̃_i

with γ_i the angular-frequency FWHM of mode i (Lorentzian ↔ damped oscillator relation Δω = 2/τ, Agrawal §2.3.2) and Z fixed by ∫₀^∞ h_R dt = 1. The solver's Raman dispatch reads fR and h_R(t) from whatever response object the FiberProfile carries — the single-mode silica Blow–Wood RamanResponse or a multi-mode PhononResponse — so crystalline materials (LiNbO₃, YAG, KTP, …) can be propagated with their real multi-mode response. Single-mode behaviour is unchanged.

3. Time-resolved TPA / free carriers (opt-in)

The legacy TPA model spatially averages the carrier density (one scalar U per step). SplitStepEngine additionally offers include_free_carriers=True, which resolves the carrier population per retarded-time sample (Soref & Bennett, IEEE J. Quantum Electron. 23, 123 (1987); Cowan, Rieger & Young, Appl. Phys. Lett. 82, 1745 (2003); Yin & Agrawal, Opt. Lett. 32, 2951 (2007)):

|A|²(z+dz) = |A|² / (1 + β_TPA |A|² dz)      (exact TPA attenuation)
∂N/∂z = β_TPA |A|⁴ / (2 ħω₀) − N / (τ_c v)   (generation − recombination)
∂A/∂z|_FCA = −(σ_FCA/2) N A                  (free-carrier absorption)

each integrated exactly per sample within a step. The legacy include_tpa path is untouched; both models are documented side by side. Out of scope by design: free-carrier refraction (the μ index term), carrier diffusion and drift — all listed as follow-ups.

4. Convergence and validation harness

photonics_helper.gnlse_validation (exported from the package root) is the "demonstrate grid independence on your configuration" tool the original code review called out as missing.

Grid/step convergence — convergence_study(build_solver, refinements, observables, tolerance, shared) runs a caller-supplied GNLSE factory at successively refined resolutions and returns, 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 §2.4). Built-in observables: peak_intensity, pulse_energy, rms_bandwidth, rms_width; pass a {name: callable} mapping for anything else.

Cited analytical checks raise ValidationFailure when a closed form is violated, so they can be wired into an application's own test suite:

Check Reference Closed form
check_spm Stolen & Lin, Phys. Rev. A 17, 1448 (1978) Fourier-integral spectrum + N_peaks = ⌊φ_max/π⌋+1
check_mi Agrawal §5.1, Eq. 5.1.9 g(Ω)=\|β₂Ω\|√(Ω_c²−Ω²), Ω_c²=4γP/\|β₂\|, g_max=2γP (power-gain convention, propagated sideband intensity ∝ e^{gz})
check_soliton Agrawal §5.2 fundamental soliton returns after z_sol = (π/2)L_D
check_gordon_ssfs Gordon, Opt. Lett. 11, 662 (1986) dΩ/dz = −8\|β₂\|T_R/(15T₀⁴), T_R = f_R ∫t·h_R dt

Example:

from photonics_helper import convergence_study

report = convergence_study(
    build,  # your factory → propagated GNLSESolver
    refinements=[
        {"N": 8192, "Tmax_s": 8e-12, "num_steps": 1000},
        {"N": 16384, "Tmax_s": 8e-12, "num_steps": 2000},
        {"N": 32768, "Tmax_s": 8e-12, "num_steps": 2000},
    ],
    observables=["peak_intensity", "rms_bandwidth"],
    tolerance=2e-3,
)
assert report.converged, report.summary()

Regression coverage: tests/test_gnlse_convergence.py, tests/test_gnlse_unitarity.py (shock integrator fidelity + the constant-drive conservation limit), tests/test_gnlse_phonon_raman.py (multi-mode response), tests/test_gnlse_free_carrier.py (analytic TPA attenuation I₀/(1+βI₀z) and the legacy-path contract).