Skip to content

Multimode (few-mode) coupled GNLSE

Split-step Fourier solver for N coupled modal channels: per-mode dispersion and group delay, LP/isotropic SPM-XPM coefficients, opt-in pump-driven inter-modal FWM with angular-momentum gating. Docstrings are the source of truth, rendered with mkdocstrings (numpydoc style). See multimode GNLSE for the physics and references.

photonics_helper.multimode_gnlse

Multimode (few-mode fiber) coupled GNLSE.

Split-step Fourier solver for N simultaneously-guided spatial modes: structured.py gives static LG/OAM portraits; this module propagates their modal envelopes nonlinearly.

Model (all channels coupled through one shared scalar γ):

  • linear: per-mode Taylor dispersion β_k^(m), shared scalar loss e^{−α dz/2}, and modal group delay (walk-off in the retarded frame of channel 0);
  • nonlinear SPM/XPM with a selectable coefficient set — degenerate linearly-polarized (LP) spatial modes: SPM 1, XPM 2/3 (Agrawal §6.4 LP limit) — or the isotropic all-ones model (Manakov-like, for randomly-coupled bases);
  • opt-in inter-modal four-wave mixing iγ·f·A_n A_p A_q* (RK4IP frequency-domain substep, Strang-split around the diagonal phase), restricted by angular momentum conservation ℓ_m = ℓ_n + ℓ_p − ℓ_q when OAM indices are supplied;
  • optional pump depletion: the full Manley–Rowe-consistent exchange (creation arms +iγ f A_n²A_q* / +iγ f A_n²A_m* and the back-conversion pump arm +2iγ f* A_m A_q A_n*), which conserves Σ|A|² to RK4 round-off (Mumtaz Eq. 6 three-recoupling structure);
  • optional mode-specific nonlinear overlap weights (Mumtaz Eq. 8 / Poletti & Horak Eq. 7): xpm_weights (N×N, SPM/XPM slot) and fwm_weights (N×N×N×N, FWM slot) override the uniform coef_model factors mode-pair-wise.

The degenerate two-channel strip-down of this engine is the variety implemented in :mod:photonics_helper.vector_gnlse (the two polarization axes of one mode); the multimode engine generalizes the same machinery to arbitrary mode counts.

References

G. P. Agrawal, Nonlinear Fiber Optics, 5th ed. §6.4 (scalar XPM 2/3, FWM); M. Poletti & P. Horak, "Description of ultrashort pulse propagation in multimode optical fibers," J. Opt. Soc. Am. B 25, 1645 (2008), doi:10.1364/JOSAB.25.001645 (vector modal equations, overlap tensors, photon-number conservation Eq. 15); S. Mumtaz, R.-J. Essiambre & G. P. Agrawal, "Nonlinear propagation in multimode and multicore fibers: generalization of the Manakov equations," J. Lightwave Technol. 31, 398 (2013), doi:10.1109/JLT.2012.2235414 (Eq. 6/8: f_lmnp overlap tensor, γ/3 coherent mixing + 2γ/3 SPM/XPM structure; arXiv:1207.6645); L. G. Wright et al., Nat. Commun. 6, 6682 (2015) (resonant inter-modal FWM).

CoeffModel module-attribute

CoeffModel = Literal['lp_degenerate', 'isotropic']

MultimodeSplitStepEngine

MultimodeSplitStepEngine(waves, fiber: FiberProfile, betas, *, betas_unit: BetasUnit = 'ps^k/m', group_delays: list[float] | None = None, phase_offsets: list[float] | None = None, coef_model: CoeffModel = 'lp_degenerate', include_fwm: bool = False, oam_l: list[int] | None = None, xpm_weights=None, fwm_weights=None, fwm_pump_depletion: bool = False, step_size: Length | None = None)

Split-step engine for N coupled modal channels (few-mode GNLSE).

Parameters:

Name Type Description Default
waves sequence of Wave

One input envelope per guided mode. All channels must share one :class:TemporalGrid (each wave's grid is the same object) and equal-length fields; each may carry its own central wavelength but the engine treats them all as co-polarized modal envelopes.

required
fiber FiberProfile

Shared scalar parameters (n2, alpha, A_eff, confinement_factor, length). One scalar γ is applied in every channel; mode-specific area/overlap corrections are to be handled at the caller by scaling fiber.A_eff.

required
betas array_like or sequence of array_like

Taylor coefficients [β₂, β₃, …] per mode (ps^k/m with Ω in rad/ps), or one shared array for every channel.

required
betas_unit BetasUnit

Unit of the dispersion coefficients. Default "ps^k/m".

'ps^k/m'
group_delays list[float] | None

Modal group delay β₁⁽ᵐ⁾ − β₁⁽⁰⁾ (SI, s/m) in the retarded frame of channel 0. None (default) = co-riding modes; forced group_delays[0] == 0.

None
phase_offsets sequence of float

Modal propagation-constant offset Δβ₀⁽ᵐ⁾ = β₀⁽ᵐ⁾ − β₀⁽⁰⁾ (rad/m) in the retarded frame of channel 0. Frequency-independent phase accumulated as e^{i Δβ₀ z} in each channel's linear step; this is the absolute modal phase the retarded-frame Taylor expansion omits, and it is what provides discrete intermodal quasi-phase matching (e.g. the GRIN geometric-parametric- instability ladder, where Δβ₀⁽ᵖ⁾ = −2πp/ξ from self-imaging).

None
coef_model ('lp_degenerate', 'isotropic')

Nonlinear coefficients:

  • "lp_degenerate" (default) — degenerate-LP spatial-mode model: SPM 1, XPM 2/3, inter-modal FWM 2/3.
  • "isotropic" — all-ones coefficients (Manakov-like spatially averaged modes; use for a non-degenerate basis).
"lp_degenerate"
include_fwm bool

Opt-in inter-modal four-wave mixing. Default False — the standard averaged few-mode model keeps only SPM/XPM (the heterodyne FWM beats wash out over the group-delay walk-off in real fiber).

False
oam_l sequence of int

OAM azimuthal order ℓ of each channel. When supplied (and FWM is on) FWM triples are restricted to ℓ_m = ℓ_n + ℓ_p − ℓ_q (transverse-photon-momentum balance for the A_n A_p A_q* term feeding channel m); otherwise every triplet is allowed.

This gate is the Poletti & Horak Eq. (18) type-2 spatial selection rule, not merely an approximation of it: their Q^(2) term in Eq. (6) is Q^(2)_plmn A_l* A_m A_n feeding channel p with l the conjugated field, so their balance m_m + m_n = m_p + m_l is the printed rule −m_p − m_l + m_m + m_n = 0; under the index map (p, l, m, n) = (m, q, n, p) it becomes ℓ_m + ℓ_q = ℓ_n + ℓ_p, which is this gate (verified element-wise in reproductions/poletti_2008_multimode).

Two caveats:

  • The gate carries the spatial rule only. The polarisation rule Eq. (19) and the exact magnitudes are in the overlap tensor — pass them via fwm_weights / xpm_weights when a real fibre's mode functions are known.
  • Uniform entries make the gate inert: if all labels are equal (e.g. [0, 0, 0], the common "these channels have no distinguished azimuthal order" case) the condition holds for every triplet and the gate is exactly the isotropic fallback of oam_l=None. It is a selection filter, not a cost control — it never reduces the engine's work, only the number of permitted exchanges.
None
xpm_weights array_like

Mode-specific SPM/XPM overlap weights (Mumtaz Eq. 8 style), an N×N real array. w[i, j] multiplies |A_j|² entering channel i (including i == j, i.e. the SPM slot). When given, this overrides the uniform coef_model factors.

None
fwm_weights array_like

Mode-specific FWM overlap weights, an N×N×N×N real array; w[m, n, p, q] weights the → m transition pumped by (n, p) consuming q. When given, it multiplies every allowed triple (OAM gating, if any, still applies first).

None
fwm_pump_depletion bool

Include the Manley–Rowe-consistent back-conversion pump arm +2iγ f* A_m A_q A_n* in the FWM substep (requires include_fwm=True). With this on, Σ|A|² is conserved to RK4 round-off and strong pumps show the parametric back-conversion oscillation. Default False (pump-driven approximation: the pump evolves only through SPM/XPM).

False
step_size Length | None

Fixed step size (m); None uses length/num_steps.

None

betas instance-attribute

betas = [_normalize_betas(b, betas_unit) for b in per_mode]

waves instance-attribute

waves = waves

fiber instance-attribute

fiber = fiber

group_delays instance-attribute

group_delays = None if group_delays is None else list(group_delays)

phase_offsets instance-attribute

phase_offsets = None if phase_offsets is None else list(phase_offsets)

coef_model instance-attribute

coef_model: CoeffModel = coef_model

include_fwm instance-attribute

include_fwm = include_fwm

oam_l instance-attribute

oam_l = None if oam_l is None else list(oam_l)

xpm_weights instance-attribute

xpm_weights = None if xpm_weights is None else np.asarray(xpm_weights, dtype=float).copy()

fwm_weights instance-attribute

fwm_weights = None if fwm_weights is None else np.asarray(fwm_weights, dtype=float).copy()

fwm_pump_depletion instance-attribute

fwm_pump_depletion = fwm_pump_depletion

step_size instance-attribute

step_size = step_size

grid instance-attribute

grid: TemporalGrid = waves[0].grid

omega0 instance-attribute

omega0 = waves[0].central_frequency

A instance-attribute

A: list[NDArray] = []

evolution instance-attribute

evolution: list[list[Wave]] = []

z_array property

z_array: NDArray

Propagation distances (m) for each saved snapshot.

energy_vs_z property

energy_vs_z: NDArray

Total photon number Σᵢ∫|A_i|²dt at every snapshot.

spectra_vs_z property

spectra_vs_z: tuple[NDArray, NDArray]

(ω [rad/s], total summed spectrum over all channels) per snapshot.

propagate

propagate(num_steps: int, *, nsaves: int | None = None, show_progress: bool = False) -> None

Run the split-step propagation for num_steps steps.

Snapshot semantics match the scalar engine: nsaves evenly spaced snapshots (including z=0 and z=L), every step when nsaves=None; a total-photon-number monitor warns on >5% drift in lossless runs.

fields_vs_z

fields_vs_z() -> list[NDArray]

Complex field histories: one (n_saves, N) array per channel.

nonlinear_phase_measure

nonlinear_phase_measure(m: int) -> float

Nonlinear phase of channel m measured against its own input CW.

Returns arg(A_m(t₀, z) / A_m(t₀, 0)) at the last snapshot for CW or constant-amplitude inputs.