solprop.driver.scaffold#

A fixed-step interaction-picture integrator for the mode-averaged GNLSE.

Not the solver, and not how a simulation is run: that is solprop.driver.interface.prop_gnlse() and solprop.driver.simulation.Simulation. This is a second, independent integrator of the same physics, which is why it stays now that the driver has landed: it composes the nonlinear term from the physics layer and steps it with the classical interaction-picture RK4, sharing nothing with the reference backend’s passes but the response itself, so agreement between the two checks the stage combination and the propagator factors rather than the physics they share (tests/backends/test_numpy_ref_soliton.py). It is plain NumPy, fixed step, envelope grid, mode-averaged, Kerr only, and allocates freely.

The scheme is the classical fourth-order Runge-Kutta method in the interaction picture [1]: the linear flow exp(D h) is applied exactly, which the diagonal operator allows (docs/guide/numerics/linear-operator.md), and only the nonlinear term is integrated numerically.

References#

[1]

J. Hult, “A fourth-order Runge-Kutta in the interaction picture method for simulating supercontinuum generation in optical fibers”, J. Lightwave Technol. 25, 3770 (2007). doi:10.1109/JLT.2007.909373

Functions

gnlse_source(problem, grid)

Return the scaffold's nonlinear term of the mode-averaged GNLSE.

propagate_fixed_rk4(problem, grid, spectrum, ...)

Propagate a mode-averaged envelope spectrum with fixed-step IP-RK4.

solprop.driver.scaffold.gnlse_source(problem, grid)[source]#

Return the scaffold’s nonlinear term of the mode-averaged GNLSE.

The term propagate_fixed_rk4() integrates, composed from the physics layer alone (docs/dev/architecture.md section 2): the spectrum is oversampled onto the fine grid, transformed, scaled to volts per metre, handed to the Kerr response, and the polarisation is transformed back, cropped and multiplied by solprop.nonlinear.normalisation.mode_average_prefactor(). Exposed so that one evaluation can be compared with a backend’s (SEP 24 section 7.1).

Parameters:
problemsolprop.nonlinear.gnlse.GnlseProblem

The fibre and the nonlinearity.

gridsolprop.grid.TimeGrid

An envelope grid.

Returns:
collections.abc.Callable

N[state]: a stored spectrum of shape (n_omega,) in the square root of watts to the nonlinear term, same shape, in the square root of watts per metre.

Raises:
GridError

If the grid is not an envelope grid.

Examples

>>> import numpy as np
>>> from solprop.driver.scaffold import gnlse_source
>>> from solprop.grid import EnvelopeGrid
>>> from solprop.nonlinear.gnlse import problem_from
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (500e-9, 2e-6), 4e-12)
>>> problem = problem_from([0.0, 0.0, -1.2e-26], omega_ref=grid.omega0,
...                        gamma=0.11, shock=False)
>>> source = gnlse_source(problem, grid)
>>> bool(np.all(source(np.zeros(grid.n_omega, dtype=complex)) == 0.0))
True
solprop.driver.scaffold.propagate_fixed_rk4(problem, grid, spectrum, z_end, n_steps, *, n_save=1)[source]#

Propagate a mode-averaged envelope spectrum with fixed-step IP-RK4.

The state is the stored spectrum in the internal FFT convention, in the square root of watts, as solprop.pulses builds it. Each step applies the exact linear factor exp(D h / 2) around the four nonlinear evaluations of the classical Runge-Kutta scheme. The nonlinear term is gnlse_source()’s. The linear operator is evaluated once, at z = 0, which is exact for a dispersion-specified mode because it does not vary along the fibre.

The state is masked to the grid’s active band once at the start. Outside the band both the operator and the prefactor are zero, so nothing there ever changes.

A fibre that varies along z is refused: the operator and the area are evaluated once here, and a varying effective area belongs to the tabulated route of solprop.driver.interface.prop_gnlse().

Parameters:
problemsolprop.nonlinear.gnlse.GnlseProblem

The fibre and the nonlinearity, from solprop.nonlinear.gnlse.problem_from().

gridsolprop.grid.TimeGrid

An envelope grid. A field grid is refused: this scaffold has no field path.

spectrumnumpy.ndarray

Initial stored spectrum, shape (n_omega,), complex, in the square root of watts and the internal convention.

z_endfloat

Propagation length, m, positive.

n_stepsint

Number of equal steps, at least 1 and a multiple of n_save.

n_saveint, optional

Number of planes to save after z = 0, at least 1. Default 1: the end only.

Returns:
tuple

(z, spectra): the saved positions, shape (n_save + 1,), m, starting at zero; and the stored spectra there, shape (n_save + 1, n_omega).

Raises:
GridError

If the grid is not an envelope grid, or the spectrum does not match it.

StepperError

If the fibre varies along z, z_end is not positive and finite, n_steps or n_save is below 1, or n_steps is not a multiple of n_save.

Examples

A fundamental soliton is stationary up to the phase -i xi / 2 of the internal convention, xi being the distance in dispersion lengths:

>>> import numpy as np
>>> from solprop.driver.scaffold import propagate_fixed_rk4
>>> from solprop.grid import EnvelopeGrid
>>> from solprop.nonlinear.gnlse import problem_from
>>> from solprop.pulses import SechPulse
>>> beta2, gamma, tau0 = -11.83e-27, 0.11, 100e-15
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (500e-9, 2e-6), 4e-12)
>>> problem = problem_from(
...     [0.0, 0.0, beta2], omega_ref=grid.omega0, gamma=gamma, shock=False
... )
>>> peak = abs(beta2) / (gamma * tau0**2)
>>> start = SechPulse(wavelength=835e-9, width=tau0, peak_power=peak).build(grid)
>>> z, spectra = propagate_fixed_rk4(
...     problem, grid, start[0, 0], tau0**2 / abs(beta2), 200
... )
>>> error = np.linalg.norm(spectra[-1] - np.exp(-0.5j) * spectra[0])
>>> bool(error / np.linalg.norm(spectra[0]) < 1e-9)
True