solprop.backends.low_storage#
The low-storage interaction-picture RK4 march: replay-only, accepted in place.
SEP 24 (docs/dev/seps/archive/,
sep-0024-low-storage-interaction-picture-runge-kutta.md).
Classical fourth-order Runge-Kutta in the interaction picture anchored at the step’s
midpoint, Lawson’s transformation [1] in Hult’s arrangement [2]. For a step
[z_n, z_n + h] with z_m = z_n + h/2, P = U(z_m, z_n), Q = U(z_n + h,
z_m) and U(z_2, z_1) = exp(Phi(z_2) - Phi(z_1)), the step is
g1 = P F(z_n, A_n), F2 = F(z_m, P A_n + h/2 g1),
F3 = F(z_m, P A_n + h/2 F2), F4 = F(z_n + h, Q (P A_n + h F3)),
A_{n+1} = Q (P A_n + h/6 g1 + h/3 F2 + h/3 F3) + h/6 F4,
with F the complete source (the response, the in-stage absorbers and the
modified noise draw at the evaluation’s position). Only forward flows are applied,
four per element, and on a constant operator P = Q = exp(h D / 2).
The register form (SEP 24 section 4.3) holds three field-sized registers beside the
source pipeline’s scratch S: the state A, overwritten in place by P A and
then by the accepted step, the accumulator B and the stage register C. A step
is four passes, each forming an input, evaluating F into S and folding it into
the registers:
pass |
evaluation |
finish |
|---|---|---|
1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
The march (LowStorageMarch) replays a prescribed schedule and never
rejects, so it accepts each step in place: from the step’s start to its commit the
backend is in flight (StepDriver._in_flight) and every entry that reads or
advances the state refuses it, until StepDriver.set_initial loads a state again
(SEP 24 section 6.2, ADR-050). It records no error estimate: the step history’s norm
is 0.0, “not estimated” (section 6.3). Divergence is detected on the accepted state’s
norm instead.
The engine holds the registers and makes the passes (LowStorageEngine); a
backend may keep its own where its state lives (StepDriver._low_storage_engine).
HostLowStorage is the host engine and the oracle, over the backend’s
_adams_lab_source and _adams_propagator.
References#
J. D. Lawson, “Generalized Runge-Kutta processes for stable systems with large Lipschitz constants”, SIAM Journal on Numerical Analysis 4, 372 (1967). doi:10.1137/0704033
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
|
Return the table key of the schedule step from z, from |
|
Return the step each of a schedule's steps builds and keys the flow table on. |
Classes
|
The host engine: the four register passes over host arrays (SEP 24 6.5). |
|
What |
|
Replay a schedule with the low-storage RK4IP march, accepting in place. |
- class solprop.backends.low_storage.HostLowStorage(backend, method)[source]#
Bases:
objectThe host engine: the four register passes over host arrays (SEP 24 6.5).
The backend’s state is the register
Aand is updated in place;BandCare this engine’s host arrays, allocated at the first step. Each pass makes one source evaluation throughStepDriver._adams_lab_source(the complete source, the noise draw advanced to the pass’s position and the in-stage absorbers included) and takesPandQfromStepDriver._adams_propagatoror the table, allocating transients as the reference does. The step’s three positions are known before its first pass, so their host scalars are read together (StepDriver._stage_bundles) and the hooks take the kept bundles.The flows.
"tabulate"on a constant operator holds one volume,exp(h_t D / 2), built at the first step after a load and reused while the steps keep the same keyh_t, their run’s mean step (table_steps(), SEP 24 Q10): a uniform schedule’s steps are differences of rounded positions and differ in their last bits. The table is built from the key alone, from the operator’s origin toh_t / 2– a constant operator’s increment is the distance itself – so its exponent carries one rounding ofD h_t / 2and none of the step’s position (fl(z + h/2) - zwould be off by up to half an ulp ofz, an error that accumulates). Each reused flow’s exponent is then off by|D| |h - h_t| / 2, a few ulps of the position times|D|, which does not accumulate over the run. A varying operator’sPandQchange every step, and"tabulate"forms the pair once per step;"recompute"forms each flow in the finish that applies it, from the positions, whose differences telescope. The treatments agree to rounding, not bit for bit.The stiffness indicator (SEP 24 section 6.3, Q1):
h rho = 2 ||F3 - F2|| / ||F2 - g1||over the norm mask estimates|h lambda|of the linearised source on the content the step integrates (foru' = lambda uit is exactly|h lambda|), the quantity the stability interval of section 4.6 bounds. It is kept instiffnessand never enters the norm column. It reads the content’s margin, not the worst case: measured on an ionising capillary it passed 2.785 on the step before the replay diverged, and beside an in-stage collar it read 0.17 to 0.65 ofh alpha_max / 2as the collar held less or more of the field.- Parameters:
- backendsolprop.backends.driver.StepDriver
The backend whose state, hooks and boundary treatments are used; its state is a host array in the canonical layout.
- methodsolprop.stepper.low_storage.LowStorageRungeKutta
The march.
- Attributes:
- stiffnessfloat
h rhoof the last completed step, dimensionless; NaN before one, or whenF2 - g1vanished on the norm mask.- atomicbool
Always
False: the passes are Python calls a signal may interrupt.
Notes
What the engine holds between steps is the registers
BandCand, on a constant operator tabulating, the table:held()names them for the backend’s memory ledger, andclose()releases them.
- class solprop.backends.low_storage.LowStorageEngine(*args, **kwargs)[source]#
Bases:
ProtocolWhat
LowStorageMarchasks of the engine that holds its registers.Part of the kernel seam (schema 29): the march keeps the schedule, the commit and the records; an engine keeps the registers and the flow table and does every state-sized operation of a step.
HostLowStorageis the reference.- atomic: bool#
Whether
step()is one native call that no signal interrupts (schema 32).A signal’s Python handler runs at the first bytecode after such a call returns, which is before the commit, so the march then takes the step, the backend’s
_finishand the position move in onecommitting()block (SEP 24 section 6.7): only a native exception can abandon the step in flight. An engine whose step runs Python between its passes is not atomic, and a signal there abandons the step at once rather than waiting up to a step for the commit (section 6.2, Q6).
- step(z, dz)[source]#
Take one step in place, leaving the raw accepted state in the backend.
- Parameters:
- zfloat
Where the step starts, m.
- dzfloat
The step, m, positive.
- Returns:
- float
The accepted state’s squared norm over the norm mask, before the boundary treatments, in the state’s units squared; not finite when the field has diverged.
- class solprop.backends.low_storage.LowStorageMarch(backend, method)[source]#
Bases:
objectReplay a schedule with the low-storage RK4IP march, accepting in place.
Follows
solprop.backends.multistep.AdamsMarch: it owns its registers through an engine and replays a prescribed schedule, which is the only thing it does (SEP 24 section 6.2). Each step calls the engine, applies the backend’s_finish(the hard band mask, and the split-step collar where chosen) and moves the position in one commit, then records the step through the backend.- Parameters:
- backendsolprop.backends.driver.StepDriver
The backend whose state is marched; it owns the step history and the statistics rows.
- methodsolprop.stepper.low_storage.LowStorageRungeKutta
The march.
- Attributes:
- source_evaluationsint
Source evaluations of the committed steps since the state was last loaded (
StepDriver.set_initial, which sets it to zero), four a step: a physical count, never a logical response call (SEP 24 section 5). A step that did not commit – abandoned in flight, or diverged – adds none.
Notes
The march has no error estimate to warn on. It logs a warning, once per call of
replay_to(), when the engine’s stiffness indicator passes RK4’s negative-real stability interval (LOW_STORAGE_REAL_INTERVAL): an explicitly integrated damping term the translation cannot bound in advance – plasma loss – is then near the step’s stability limit.- property engine: LowStorageEngine[source]#
Return the engine that holds the registers.
- Returns:
- LowStorageEngine
The host engine, or the backend’s own.
- replay_step(dz, end, slack)[source]#
Take one prescribed step from the backend’s position and accept it.
The unit a lockstep batch advances each member by (
solprop.backends.batch.LockstepRunner), withsolprop.backends.multistep.AdamsMarch.replay_step()’s signature.- Parameters:
- dzfloat
The step, m.
- endfloat
The schedule’s edge it ends on, m.
- slackfloat
How far an edge may sit from a target it lands on, m; unused, since the march restarts nothing at a kink.
- Returns:
- float
The norm the history records: 0.0, “not estimated”.
- Raises:
- BackendError
If an earlier step was abandoned in flight.
- ConvergenceError
If the accepted state is not finite: the field has diverged, and a replay cannot reject the step. Raised in flight, so the state is lost with it.
- solprop.backends.low_storage.schedule_key(keys, edges, z, dz)[source]#
Return the table key of the schedule step from z, from
table_steps().Found by position rather than by a step counter, which a lockstep member does not advance (
LockstepRunnerkeeps the batch’s).- Parameters:
- keysnumpy.ndarray
table_steps()of the schedule.- edgesnumpy.ndarray
The schedule’s edges, m.
- zfloat
Where the step starts, m.
- dzfloat
The step, m.
- Returns:
- float
The key, m.
- solprop.backends.low_storage.table_steps(schedule)[source]#
Return the step each of a schedule’s steps builds and keys the flow table on.
SEP 24 Q10, decided in phase 2: the mean step of the step’s run, the longest stretch of consecutive steps within
LANDING_ULPSulps of the run’s first, the ulps being of the run’s farthest position (or of the step, near the origin).uniform()takes a segment’s steps from differences of rounded positions, which differ in their last bits by up to an ulp of the position: far from the origin that is many ulps of the step, and a table keyed on one step’s own value within ulps of the step was rebuilt nearly every step. Keyed on the run’s meanh_barinstead, a uniform segment builds one table and reuses it; each flow’s exponent is then off by|D| |h - h_bar| / 2, a few ulps of the position times|D|, and the error does not drift: the steps of a run sum ton h_barto rounding, so the phase the flows apply over a run is the run’s own. A schedule whose steps all differ (a discovered one) has runs of one step and keys each step on itself.- Parameters:
- schedulesolprop.stepper.schedule.FixedSchedule
The schedule replayed.
- Returns:
- numpy.ndarray
One key per step, m, shape
(n_steps,): constant over a run.
Examples
>>> from solprop.backends.low_storage import table_steps >>> from solprop.stepper import FixedSchedule >>> schedule = FixedSchedule.uniform(0.0, 1.0, 0.1, save_planes=[0.25, 1.0]) >>> keys = table_steps(schedule) >>> len(set(keys.tolist())), round(float(keys[0]), 12), round(float(keys[-1]), 12) (2, 0.083333333333, 0.09375)