solprop.output.records#

The record types of a saved run: its grid, modes, noise, stepper and saved planes.

Each is a frozen dataclass that a handler writes and reads back. SavedPlane carries one saved position with its field, its statistics and the step history since the previous plane; RunOutcome and TerminalCheckpoint how a run ended.

Conventions on the way out. A SavedPlane holds its field in the internal FFT convention, as a backend produces it; a handler converts it with solprop.fourier.spectrum_to_optics() on the way to storage, so that what is stored is the window-centred optics spectrum a spectrometer would agree with, marked spectral_convention = "optics" (SPECTRAL_CONVENTION; docs/dev/conventions.md §2). The noise realisation and noise_phase are internal objects and stay in the FFT convention, marked so by INTERNAL_CONVENTION.

The manual page is docs/guide/results/output.md.

Module Attributes

SPECTRAL_CONVENTION

Sign convention of every spectrum a handler stores.

INTERNAL_CONVENTION

Sign convention of the arrays that stay internal: the noise field and its phase.

FIELD_LAYOUT

Axis order of the stored field, as docs/dev/conventions.md §6 fixes it.

SimulationKind

Which transform the run used.

Convention

Which sign convention an accessor should hand back.

HISTORY_FIELDS

Dtype of each StepHistory field, in the order they are laid out.

STEP_STATS_INDEX

Dtype of each StepStats index column, beside the statistics themselves.

Classes

DefaultsResolution(route[, defaulted])

Which of a run's numerics took its route's default, and which route that was.

GridRecord(*, kind, n_t, n_to, n_omega, ...)

The scalars that identify the grid a run was computed on.

MarchSnapshot(z, state, proposal, method, ...)

An Adams march's state at an accepted position, for a resume to continue.

ModeRecord(*, kind, effective_area, ...[, label])

One propagated mode, by class, effective area, channel label and configuration.

NoiseRecord(*, model, seed, spectrum[, ...])

The shot-noise realisation a run started from, and the seed that drew it.

NoiseResolution(requested, resolved[, reason])

How a run's noise model was chosen: what was asked for, what it carries, and why.

RecipeFile(path, sha256[, data])

A data file a recorded recipe refers to (SEP 41, S2).

RecipeRecord(format, authored, resolved[, files])

The recipe a run was built from, as its output records it once (SEP 41, S2).

RunOutcome(status, z[, message, ...])

Record completion and cumulative attempted work separately from run identity.

SavedPlane(*, z, field[, stats, ...])

One saved position: the field there, its statistics and how it was reached.

StepHistory(*, z, h, norm, accepted, wall_time)

What the stepper did between two saved planes.

StepLimits([dz_min, dz_max, dz_init, ...])

The caps a run put on the controller's step proposal.

StepStats(*, z, step[, values])

The statistics a backend evaluated between two saved planes.

StepperRecord(*, tableau, controller[, ...])

The solver settings a run used, including the schedule it actually took.

StoredField(values[, noise_phase])

A kept plane's field, exported straight into a plane store's stored form.

TerminalCheckpoint(plane, next_save_index[, ...])

Keep an accepted field between scheduled saves without adding an observation.

solprop.output.records.Convention#

Which sign convention an accessor should hand back.

alias of Literal[‘optics’, ‘fft’]

class solprop.output.records.DefaultsResolution(route, defaulted=())[source]#

Bases: object

Which of a run’s numerics took its route’s default, and which route that was.

The companion of NoiseResolution for the stepping (docs/guide/numerics/ choosing-a-stepper.md): the integrator, the step controller and the temporal collar’s scheme are each either the caller’s or the route’s default from solprop.driver.defaults.ROUTE_DEFAULTS. Outside the hash: what the run computes is in the stepper record already, and how it was chosen is not physics.

Attributes:
routestr or None

The row of the defaults table the constructor read, or None for settings built by hand.

defaultedsequence of str

Which of "integrator", "rtol" and "absorber_scheme" took the route’s default, in that order, adopted as a tuple.

Examples

>>> from solprop.output.records import DefaultsResolution
>>> DefaultsResolution("capillary", ["integrator", "rtol"])
DefaultsResolution(route='capillary', defaulted=('integrator', 'rtol'))
defaulted: Sequence[str] = ()#
route: str | None#
solprop.output.records.FIELD_LAYOUT = '(n_z, n_modes, n_pol, n_omega)'#

Axis order of the stored field, as docs/dev/conventions.md §6 fixes it.

class solprop.output.records.GridRecord(*, kind, n_t, n_to, n_omega, n_omega_fine, dt, dt_fine, ffac, omega0, d_omega, length, reference_wavelength, omega_min, omega_max, omega_max_window, t_window_min, t_window_max)[source]#

Bases: object

The scalars that identify the grid a run was computed on.

Only scalars: the four axis arrays a solprop.grid.TimeGrid holds are reconstructible from these, and keeping them out of the record keeps the metadata JSON, and the configuration hash taken over it, small.

Attributes:
kind{‘field’, ‘envelope’}

Spectral layout, which fixes what the stored field means and which conversion the storage boundary applies.

n_tint

Coarse time samples.

n_toint

Fine time samples.

n_omegaint

Coarse spectral bins, the length of the stored field’s last axis.

n_omega_fineint

Fine spectral bins.

dtfloat

Coarse sample spacing, s.

dt_finefloat

Fine sample spacing, s.

ffacfloat

Oversampling factor, dimensionless.

omega0float

Reference angular frequency, rad/s.

d_omegafloat

Spectral bin spacing, rad/s.

lengthfloat

Propagation length the grid was built for, m.

reference_wavelengthfloat

Wavelength of omega0, m.

omega_minfloat

Lower in-band angular frequency, rad/s.

omega_maxfloat

Upper in-band angular frequency, rad/s.

omega_max_windowfloat

Upper edge of the active band, rad/s.

t_window_minfloat

Lower inner edge of the temporal collar, s: the window is flat between this and t_window_max and tapers to the ends of the axis outside them.

t_window_maxfloat

Upper inner edge of the temporal collar, s.

Examples

>>> from solprop.grid import EnvelopeGrid
>>> from solprop.output.records import GridRecord
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (400e-9, 2e-6), 2e-12)
>>> record = GridRecord.from_grid(grid)
>>> record.kind, record.n_omega == grid.n_omega
('envelope', True)
d_omega: float#
dt: float#
dt_fine: float#
ffac: float#
classmethod from_grid(grid)[source]#

Return the record of a grid.

Parameters:
gridsolprop.grid.TimeGrid

The grid the run used.

Returns:
GridRecord

Its scalar description.

Examples

>>> from solprop.grid import FieldGrid
>>> from solprop.output.records import GridRecord
>>> grid = FieldGrid.from_limits(1.0, 800e-9, (400e-9, 2e-6), 1e-12)
>>> GridRecord.from_grid(grid).kind
'field'
kind: Literal['field', 'envelope']#
length: float#
n_omega: int#
n_omega_fine: int#
n_t: int#
n_to: int#
omega0: float#
omega_max: float#
omega_max_window: float#
omega_min: float#
reference_wavelength: float#
t_window_max: float#
t_window_min: float#
solprop.output.records.HISTORY_FIELDS: Mapping[str, type] = {'accepted': <class 'numpy.bool'>, 'h': <class 'numpy.float64'>, 'norm': <class 'numpy.float64'>, 'wall_time': <class 'numpy.float64'>, 'z': <class 'numpy.float64'>}#

Dtype of each StepHistory field, in the order they are laid out.

solprop.output.records.INTERNAL_CONVENTION = 'fft'#

Sign convention of the arrays that stay internal: the noise field and its phase.

class solprop.output.records.MarchSnapshot(z, state, proposal, method, order, rejections, last_rejected, positions, identities, slots, rows, collar_was_quiet=False, history_key=None)[source]#

Bases: object

An Adams march’s state at an accepted position, for a resume to continue.

What a resume needs to carry on as the uninterrupted run would: the accepted state exactly as the backend held it, the history’s samples in their engine rows, which sample each history entry is, the order, the rejection count and the step the controller proposed next. A snapshot is never the record of what was computed, only of where a march stood: a resume continues it when its history key matches the resuming method’s, and otherwise starts the history afresh.

Equality is by identity (eq=False) because the fields hold arrays.

Attributes:
zfloat

The accepted position, m.

statenumpy.ndarray

The accepted state, shape (n_modes, n_pol, n_omega), in the internal FFT convention, exactly as the backend held it. A file stores it with a leading axis of one, the layout it has always had (SEP 47 section 6.7).

proposalfloat

The step the controller proposed next, m.

methodstr

The method’s representation, kept as provenance.

orderint

The order chosen for the next step.

rejectionsint

Consecutive rejections so far.

last_rejectedbool

Whether the last trial was rejected.

positionsnumpy.ndarray

Each history entry’s position, m, oldest first.

identitiestuple of str

Each entry’s kind ("accepted" or "predicted").

slotsnumpy.ndarray

Each entry’s engine row.

rowscollections.abc.Mapping

The engine’s rows by name: the samples ("store") and, for a compensated exponential march, the sampled states and rates and each sample’s flow weights, in the engine’s own layout, which keeps a leading axis of one after the row axis.

collar_was_quietbool

Whether the in-stage collar was left out, quiet, at the last attempt, which holds the first step after it returns to its stable step (SEP 39 section 6.5.9).

history_keystr or None

What defines the history (solprop.stepper.multistep.Multistep.history_key), which the resuming method’s must equal; None for a snapshot that names none, which starts the history afresh.

Raises:
OutputError

If the entries disagree in number, or a position or the state is not finite.

Examples

>>> import numpy as np
>>> from solprop.output.records import MarchSnapshot
>>> snapshot = MarchSnapshot(
...     z=0.5, state=np.ones((1, 1, 4), complex), proposal=0.1, method="M()",
...     order=2, rejections=0, last_rejected=False,
...     positions=np.array([0.4, 0.5]), identities=("accepted", "predicted"),
...     slots=np.array([0, 1]), rows={"store": np.zeros((4, 1, 1, 1, 4), complex)},
... )
>>> snapshot.nbytes
320
collar_was_quiet: bool = False#
history_key: str | None = None#
identities: tuple[str, ...]#
last_rejected: bool#
method: str#
property nbytes: int[source]#

The bytes the snapshot’s arrays hold.

The state and every engine row.

order: int#
positions: Float[ndarray, 'n_entries']#
proposal: float#
rejections: int#
rows: Mapping[str, ndarray]#
slots: Int[ndarray, 'n_entries']#
state: Complex[ndarray, 'n_modes n_pol n_omega']#
z: float#
class solprop.output.records.ModeRecord(*, kind, effective_area, configuration, label=None)[source]#

Bases: object

One propagated mode, by class, effective area, channel label and configuration.

Attributes:
kindstr

Qualified class name of the mode, 'module.ClassName'.

effective_areafloat

A_eff in m^2 at the reference position, the one number processing needs most often and the one a plot label carries.

configurationdict

The mode object as serialise() records it.

labelcollections.abc.Mapping or None

The physical channel this row of the mode axis is, as solprop.modes.protocol.ChannelLabel.as_mapping() writes it: the family, the azimuthal and radial orders, the parity, the handedness and the display label. from_mode() always records one; None on a record built without it, which names its channel only by class name and position, and which solprop.processing.modal.channel_labels() refuses. Part of hashed().

Examples

>>> from solprop.modes import MarcatiliMode
>>> from solprop.output.records import ModeRecord
>>> record = ModeRecord.from_mode(MarcatiliMode(125e-6, "He", 1.0))
>>> record.kind
'solprop.modes.capillary.MarcatiliMode'
>>> None if record.label is None else record.label["display_label"]
'HE1,1:even'
configuration: Mapping[str, JsonValue]#
effective_area: float#
classmethod from_mode(mode, z=0.0)[source]#

Return the record of a mode.

Parameters:
modesolprop.modes.protocol.Mode

The mode.

zfloat, optional

Position along the waveguide the effective area is read at, m.

Returns:
ModeRecord

Its description.

Examples

>>> from solprop.modes import MarcatiliMode
>>> from solprop.output.records import ModeRecord
>>> record = ModeRecord.from_mode(MarcatiliMode(75e-6, "He", 1.0))
>>> f"{record.effective_area:.6e}"
'8.421575e-09'
kind: str#
label: Mapping[str, JsonValue] | None = None#
class solprop.output.records.NoiseRecord(*, model, seed, spectrum, spectral_convention='fft')[source]#

Bases: object

The shot-noise realisation a run started from, and the seed that drew it.

Equality is by identity (eq=False) because spectrum is an array; compare the seeds, which is what reproducibility means.

The spectrum is stored in the internal FFT convention, unlike the field: it is not a user-facing spectrum but the object the stepper adds at every stage, and conjugating it would make it disagree with the file it has to be read back into. spectral_convention says so explicitly.

Attributes:
model{‘input’, ‘modified’}

Which noise model the realisation belongs to.

seedint or None

Entropy of the generator, or None when it could not be recovered.

spectrumnumpy.ndarray

The realisation, shape (n_modes, n_pol, n_omega), complex, in the square root of watts.

spectral_conventionstr

Always 'fft'.

Examples

>>> import numpy as np
>>> from solprop.grid import EnvelopeGrid
>>> from solprop.noise import input_noise
>>> from solprop.output.records import NoiseRecord
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (400e-9, 2e-6), 2e-12)
>>> realisation = input_noise(grid, np.random.default_rng(3))
>>> record = NoiseRecord.from_realisation(realisation)
>>> record.seed, record.spectral_convention
(3, 'fft')
classmethod from_realisation(realisation)[source]#

Return the record of a drawn realisation.

Parameters:
realisationsolprop.noise.NoiseRealisation

The realisation the run used.

Returns:
NoiseRecord

Its description.

Examples

>>> import numpy as np
>>> from solprop.grid import EnvelopeGrid
>>> from solprop.noise import modified_noise
>>> from solprop.output.records import NoiseRecord
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (400e-9, 2e-6), 2e-12)
>>> NoiseRecord.from_realisation(modified_noise(grid, np.random.default_rng(1)))
...
NoiseRecord(model='modified', seed=1, ...)
model: str#
seed: int | None#
spectral_convention: str = 'fft'#
spectrum: Complex[ndarray, 'n_modes n_pol n_omega']#
class solprop.output.records.NoiseResolution(requested, resolved, reason=None)[source]#

Bases: object

How a run’s noise model was chosen: what was asked for, what it carries, and why.

Attributes:
requested{‘default’, ‘input’, ‘modified’} or None

The model the caller asked for: "default" when the keyword was omitted, None when noise was switched off explicitly.

resolved{‘input’, ‘modified’} or None

The model the run carries, or None for a noiseless run.

reasonstr or None

Why a default resolved to no noise, for example that the hybrid planar grid declares no photon measure; None otherwise.

Examples

>>> from solprop.output.records import NoiseResolution
>>> NoiseResolution("default", "input")
NoiseResolution(requested='default', resolved='input', reason=None)
reason: str | None = None#
requested: str | None#
resolved: str | None#
class solprop.output.records.RecipeFile(path, sha256, data=None)[source]#

Bases: object

A data file a recorded recipe refers to (SEP 41, S2).

Attributes:
pathstr

The path the recipe wrote, relative to its directory.

sha256str

Its SHA-256, as the recipe recorded it.

databytes or None

Its contents, embedded under the generated name it is filed by; None when the run’s files together passed the embedding cap, and only the path and hash are kept.

data: bytes | None = None#
path: str#
sha256: str#
class solprop.output.records.RecipeRecord(format, authored, resolved, files=<factory>)[source]#

Bases: object

The recipe a run was built from, as its output records it once (SEP 41, S2).

Provenance outside the run record and its hash: a file without one is a file from a script, and a reader that does not know it ignores it.

Attributes:
formatint

The recipe format.

authoredstr

The recipe’s text as loaded, or its canonical TOML if it was edited.

resolvedstr

The recipe to rebuild the run from: the authored keys with the drawn seed, the keywords whose value written back gives the same record, at the values used, and the execution settings at run start.

filescollections.abc.Mapping

The data files the recipe refers to, by the generated name each is filed by.

Examples

>>> from solprop.output.records import RecipeRecord
>>> record = RecipeRecord(1, "[recipe]", "[recipe]", {})
>>> record.authored_sha256 == record.resolved_sha256
True
authored: str#
property authored_sha256: str[source]#

SHA-256 of the authored text, in hexadecimal.

files: Mapping[str, RecipeFile]#
format: int#
resolved: str#
property resolved_sha256: str[source]#

SHA-256 of the resolved text, in hexadecimal.

class solprop.output.records.RunOutcome(status, z, message='', logical_calls=<factory>, atomic_calls=<factory>, execution=<factory>, previous_executions=())[source]#

Bases: object

Record completion and cumulative attempted work separately from run identity.

Attributes:
status{‘running’, ‘complete’, ‘budget_exhausted’, ‘failed’}

Current execution status. A budget stop is explicitly unfinished.

zfloat

Last accepted or saved position, m.

messagestr

Explanation of a stop or failure; empty on ordinary progress.

logical_callscollections.abc.Mapping

Cumulative logical response ledger, including startup and failed attempts.

atomic_callscollections.abc.Mapping

Cumulative provider ledger; empty when no atomic service is bound.

executioncollections.abc.Mapping

Current backend and provider facts, captured before resources close. Kept separately from the original run metadata so resumes retain their actual device, plan and diagnostic provenance without changing scientific identity.

previous_executionstuple of collections.abc.Mapping

Closing records of earlier executions of the same resumed run, in order.

atomic_calls: Mapping[str, object]#
execution: Mapping[str, object]#
logical_calls: Mapping[str, object]#
message: str = ''#
previous_executions: tuple[Mapping[str, object], ...] = ()#
status: Literal['running', 'complete', 'budget_exhausted', 'failed']#
z: float#
solprop.output.records.SPECTRAL_CONVENTION = 'optics'#

Sign convention of every spectrum a handler stores.

solprop.output.records.STEP_STATS_INDEX: Mapping[str, type] = {'step': <class 'numpy.int64'>, 'z': <class 'numpy.float64'>}#

Dtype of each StepStats index column, beside the statistics themselves.

class solprop.output.records.SavedPlane(*, z, field, stats=<factory>, step_history=<factory>, step_stats=<factory>, noise_phase=None)[source]#

Bases: Displayed

One saved position: the field there, its statistics and how it was reached.

The field is in the internal FFT convention, exactly as a backend produces it; a handler converts it on the way to storage. Equality is by identity (eq=False) because the field is an array. A field-free plane, of a run that keeps no field there (FieldRetention), carries field=None and no noise phase: its position, statistics and step history only.

Attributes:
zfloat

Position along the waveguide, m.

fieldnumpy.ndarray or None

Stored spectrum, shape (n_modes, n_pol, n_omega), complex, in the square root of watts, internal convention; None on a field-free plane.

statsdict

Statistics evaluated at this plane, keyed by catalogue name; the values are the arrays solprop.stats.collect() returns.

step_historyStepHistory

Every attempt the stepper made since the previous plane.

step_statsStepStats

The rows a periodic statistics subset produced since the previous plane, empty unless the run asked for one.

noise_phasenumpy.ndarray or None

Accumulated unitary phase of the noise field at this plane, shape (n_modes, n_pol, n_omega), rad, internal convention, so that processing can restore the background; None when no noise model is active, and on a field-free plane.

Examples

>>> import numpy as np
>>> from solprop.output.records import SavedPlane, StepHistory
>>> plane = SavedPlane(
...     z=0.0,
...     field=np.ones((1, 1, 5), dtype=complex),
...     stats={"energy": np.ones((1, 1))},
...     step_history=StepHistory.empty(),
... )
>>> plane.shape
(1, 1, 5)
field: Complex[ndarray, 'n_modes n_pol n_omega'] | None#
noise_phase: Float[ndarray, 'n_modes_phase n_pol_phase n_omega_phase'] | None = None#
property shape: tuple[int, ...] | None[source]#

The field’s shape, or None on a field-free plane.

(n_modes, n_pol, n_omega).

stats: Mapping[str, ndarray]#
step_history: StepHistory#
step_stats: StepStats#
z: float#
solprop.output.records.SimulationKind#

Which transform the run used.

alias of Literal[‘mode_averaged’, ‘multimode’, ‘radial’, ‘free’, ‘slab’]

class solprop.output.records.StepHistory(*, z, h, norm, accepted, wall_time)[source]#

Bases: Displayed

What the stepper did between two saved planes.

Recorded whether the run was adaptive or replayed a schedule, because the embedded error estimate costs nothing extra and a fixed run still needs it: a largest scaled norm above one means a step was too coarse for the tolerance the norm is scaled by (docs/dev/stepper.md §8).

Equality is by identity (eq=False) because every field is an array.

Attributes:
znumpy.ndarray

Position at the start of each attempt, shape (n_steps,), m.

hnumpy.ndarray

Length of each attempt, shape (n_steps,), m.

normnumpy.ndarray

Scaled error norm each attempt scored as executed, shape (n_steps,), dimensionless; the controller’s rescaled equivalent of a landing step is its own and is not recorded. The low-storage march estimates none and records 0.0, “not estimated”, never a step that met its tolerance exactly.

acceptednumpy.ndarray

Whether each attempt was accepted, shape (n_steps,), boolean.

wall_timenumpy.ndarray

Wall-clock time each attempt took, shape (n_steps,), s.

Raises:
OutputError

If the five arrays do not have equal shapes.

Examples

>>> import numpy as np
>>> from solprop.output.records import StepHistory
>>> history = StepHistory(
...     z=np.array([0.0, 0.5]),
...     h=np.array([0.5, 0.5]),
...     norm=np.array([0.3, 0.4]),
...     accepted=np.array([True, True]),
...     wall_time=np.array([1e-3, 1e-3]),
... )
>>> history.n_steps, history.n_rejected
(2, 0)
accepted: Bool[ndarray, 'n_history_accepted']#
classmethod empty()[source]#

Return the history of a plane no step led to, such as the starting plane.

Returns:
StepHistory

Five empty arrays.

Examples

>>> from solprop.output.records import StepHistory
>>> StepHistory.empty().n_steps
0
h: Float[ndarray, 'n_history_h']#
property n_rejected: int[source]#

Number of rejected attempts.

property n_steps: int[source]#

Number of attempts recorded.

Accepted and rejected attempts together.

norm: Float[ndarray, 'n_history_norm']#
wall_time: Float[ndarray, 'n_history_wall']#
z: Float[ndarray, 'n_history_z']#
class solprop.output.records.StepLimits(dz_min=0.0, dz_max=inf, dz_init=0.0, stretch=1.1, interval_cap=True, auto_init=False, interpolate_planes=False, step_planes=0)[source]#

Bases: object

The caps a run put on the controller’s step proposal.

Grouped into one record because they travel together.

Attributes:
dz_minfloat

Smallest step the controller was allowed to propose, m. Zero when the driver left it to solprop.stepper.controller.default_dz_min().

dz_maxfloat

Largest step, m; infinite when uncapped.

dz_initfloat

First step, m; zero when the driver chose it.

stretchfloat

Landing stretch factor, dimensionless; 1 switches landing stretch off.

interval_capbool

Whether a step was capped at its operator-table interval’s width.

auto_initbool

Whether the start-up rule took the step after the first from the first step’s estimate.

interpolate_planesbool

Whether the save planes but the end and the hard kinks were interpolated inside accepted steps; False when every plane was landed on.

step_planesint

A positive k when the end of every k-th accepted step was saved too; 0 for the requested planes only.

Examples

>>> from solprop.output.records import StepLimits
>>> StepLimits(dz_max=0.01).dz_max
0.01
auto_init: bool = False#
dz_init: float = 0.0#
dz_max: float = inf#
dz_min: float = 0.0#
interpolate_planes: bool = False#
interval_cap: bool = True#
step_planes: int = 0#
stretch: float = 1.1#
class solprop.output.records.StepStats(*, z, step, values=<factory>)[source]#

Bases: Displayed

The statistics a backend evaluated between two saved planes.

A run whose solprop.stats.StatsSpec names a periodic subset gets one row every period accepted steps, evaluated inside the backend’s step loop and flushed here at each plane. Empty for a run that asked for none, which is the default.

Stored the way the step history is – flat columns concatenated over planes with a per-plane counts beside them – so that a resume appends and a scan needs no NaN padding, which growing datasets would force on it.

Equality is by identity (eq=False) because every field is an array.

Attributes:
znumpy.ndarray

Position of each row, m, float64, shape (n_rows,).

stepnumpy.ndarray

Index of the accepted step each row was taken after, into this plane’s StepHistory, int64, shape (n_rows,). The history counts rejected attempts, so this is not row * period.

valuesdict

The statistics, keyed by catalogue name, each shaped (n_rows, *shape) with the statistic’s own shape after the row axis, as the backend’s rows are.

Raises:
OutputError

If the columns do not all have the same number of rows.

Examples

>>> import numpy as np
>>> from solprop.output.records import StepStats
>>> rows = StepStats(z=np.array([0.1, 0.2]), step=np.array([0, 2]),
...                  values={"energy": np.ones((2, 1, 1))})
>>> rows.n_rows, rows.names
(2, ('energy',))
classmethod empty()[source]#

Return the channel a run without a periodic subset carries.

Returns:
StepStats

No rows and no statistics.

property n_rows: int[source]#

How many rows belong to this plane.

property names: tuple[str, ...][source]#

The statistics the channel carries, sorted.

step: Int[ndarray, 'n_rows_step']#
values: Mapping[str, ndarray]#
z: Float[ndarray, 'n_rows_z']#
class solprop.output.records.StepperRecord(*, tableau, controller, limits=<factory>, schedule=None, method=None)[source]#

Bases: object

The solver settings a run used, including the schedule it actually took.

Equality is by identity (eq=False) because schedule is an array. Schedules are first class and re-loadable (see docs/dev/decisions.md): a replayed run is bit-identical, and every ensemble member then carries the same numerical error as a common systematic.

Build one from the objects a run held with from_settings(), and from a stored document with from_record().

Attributes:
tableaustr

Name of the Runge-Kutta scheme.

methodstr or None

The integrator that stepped the run instead of the tableau, as its repr: a low-call Adams method (SEP 13) or the low-storage march (SEP 24); None for the Runge-Kutta march.

controllerdict

The step controller as serialise() records it: tolerances, safety and gain factors, lag and the norm weights.

limitsStepLimits

The caps on the controller’s proposal.

schedulenumpy.ndarray or None

Step lengths of shape (n_steps,), m, when a fixed schedule was replayed or recorded; None for a purely adaptive run.

Examples

>>> import numpy as np
>>> from solprop.output.records import StepLimits, StepperRecord
>>> from solprop.stepper import DOPRI5, FixedSchedule, PIController
>>> record = StepperRecord.from_settings(
...     DOPRI5,
...     PIController(rtol=1e-8, atol=0.0),
...     schedule=FixedSchedule(np.array([0.5, 0.5])),
...     limits=StepLimits(dz_max=0.1),
... )
>>> record.tableau, record.limits.dz_max
('DOPRI5', 0.1)
controller: Mapping[str, JsonValue]#
classmethod from_record(document)[source]#

Return the record a stored document describes.

Parameters:
documentcollections.abc.Mapping

The form serialise() produced from a record.

Returns:
StepperRecord

The record.

Examples

>>> from solprop.output.records import StepperRecord
>>> from solprop.output.serialisation import serialise
>>> from solprop.stepper import TSIT5, PIController
>>> one = StepperRecord.from_settings(TSIT5, PIController(rtol=1e-6, atol=0.0))
>>> StepperRecord.from_record(serialise(one)).tableau
'Tsit5'
classmethod from_settings(tableau, controller, *, schedule=None, limits=None, method=None)[source]#

Return the record of the objects a run stepped with.

Parameters:
tableausolprop.stepper.tableau.Tableau

The scheme.

controllersolprop.stepper.controller.PIController

The step controller, which carries the tolerances and the norm choice.

schedulesolprop.stepper.schedule.FixedSchedule, optional

The fixed schedule replayed, if any.

limitsStepLimits, optional

The caps on the controller’s proposal; the defaults when omitted.

methodstr or None, optional

The Adams method that stepped the run instead of the tableau, as its repr; None for the Runge-Kutta march.

Returns:
StepperRecord

The record.

Examples

>>> from solprop.output.records import StepperRecord
>>> from solprop.stepper import TSIT5, PIController
>>> record = StepperRecord.from_settings(
...     TSIT5, PIController(rtol=1e-6, atol=0.0)
... )
>>> record.tableau, record.schedule is None
('Tsit5', True)
limits: StepLimits#
method: str | None = None#
schedule: Float[ndarray, 'n_steps'] | None = None#
tableau: str#
class solprop.output.records.StoredField(values, noise_phase=None)[source]#

Bases: object

A kept plane’s field, exported straight into a plane store’s stored form.

What a run’s service hands save_exported() beside a plane that carries no field of its own (SEP 47 section 5.4): the field as the store keeps it, and the noise phase that a plane with its field would carry.

Attributes:
valuesnumpy.ndarray

The field in the stored form, (n_modes, n_pol, n_omega): true-power amplitudes (or a spatial route’s field measure), sqrt(W), in the optics convention, as save() writes a plane’s field.

noise_phasenumpy.ndarray or None

The noise field’s accumulated phase at the plane, rad, internal convention, the shape of values; None without a noise model.

Raises:
OutputError

If noise_phase does not have the shape of values.

Examples

>>> import numpy as np
>>> from solprop.output.records import StoredField
>>> StoredField(np.ones((1, 1, 4), complex)).values.shape
(1, 1, 4)
noise_phase: Float[ndarray, 'n_modes_phase n_pol_phase n_omega_phase'] | None = None#
values: Complex[ndarray, 'n_modes n_pol n_omega']#
class solprop.output.records.TerminalCheckpoint(plane, next_save_index, landed=True)[source]#

Bases: object

Keep an accepted field between scheduled saves without adding an observation.

Attributes:
planeSavedPlane

Accepted field in the internal FFT convention and the output amplitude convention, with already available history and periodic statistics. Creating a checkpoint never requests fresh material statistics. It always carries the field, also in a run that keeps none at its planes, so that a stopped run resumes (SEP 22, Q1).

next_save_indexint

Index of the first scheduled save strictly beyond this field.

landedbool

Whether the field is an accepted endpoint rather than a dense observation.

landed: bool = True#
next_save_index: int#
plane: SavedPlane#