solprop.output.memory#

The in-memory output handler.

Everything in RAM: the handler for fast, small problems and for the members of a scan, where a file per run would cost more than the run. It stores exactly what solprop.output.hdf5.HDF5Output stores, in the same layout and the same sign convention – both go through solprop.output.schema.PlaneStore – so a test can compare the two bitwise and MemoryOutput.to_file() writes the file the other handler would have written.

The plane-indexed arrays are allocated once, for the planes the run reserved (reserve(); the run loop reserves the entrance and every save plane), or for a block of INITIAL_BLOCK planes when nothing was reserved, and grow by doubling past that rather than by concatenating on every save, which would be quadratic in the number of planes. The field block follows the run’s field retention (retain()): every plane, exactly the named planes, or none at all, in which case no field block is allocated. The step history is variable in length per plane, so it is kept as a list of pieces, concatenated when read, with a per-plane count beside it.

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

Module Attributes

INITIAL_BLOCK

Planes preallocated when no run size was reserved (architecture.md §13).

Classes

MemoryOutput([block])

Hold a whole run in memory, in the layout an HDF5 file would use.

solprop.output.memory.INITIAL_BLOCK = 16#

Planes preallocated when no run size was reserved (architecture.md §13).

class solprop.output.memory.MemoryOutput(block=16)[source]#

Bases: PlaneStore

Hold a whole run in memory, in the layout an HDF5 file would use.

Reading stays available after close(), unlike the file handler: nothing is released, so a driver may close in a finally and a caller keep reading.

Parameters:
blockint, optional

Planes to preallocate when the run reserved none (reserve()); a reserved count takes its place. The store doubles whenever it is full.

Raises:
OutputError

If block is not positive.

Examples

>>> import numpy as np
>>> from solprop.grid import EnvelopeGrid
>>> from solprop.output import MemoryOutput
>>> from solprop.output.schema import (
...     GridRecord, RunMetadata, SavedPlane, StepperRecord
... )
>>> from solprop.stepper import DOPRI5, PIController
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (400e-9, 2e-6), 2e-12)
>>> metadata = RunMetadata(
...     grid=GridRecord.from_grid(grid),
...     modes=(),
...     simulation_kind="mode_averaged",
...     configuration={},
...     stepper=StepperRecord.from_settings(
...         DOPRI5, PIController(rtol=1e-8, atol=0.0)
...     ),
... )
>>> handler = MemoryOutput()
>>> handler.open(metadata)
>>> handler.save(SavedPlane(z=0.0, field=np.ones((1, 1, grid.n_omega), complex)))
>>> handler.n_saved, handler["field"].shape
(1, (1, 1, 1, 2560))
>>> handler.close()
field_destination(plane, shape)[source]#

Return the block’s row that the next plane’s field is to be exported into.

The row is allocated with the block, for the planes the run reserved, so a kept field is written once, where it is kept (SEP 47 section 5.4). It is never given to another plane, and nothing writes to it once the plane is stored.

Parameters:
planeSavedPlane

The plane about to be stored, without its field.

shapetuple of int

(n_modes, n_pol, n_omega) of the field.

Returns:
numpy.ndarray

The row: writeable, C-contiguous complex128 of shape once the block is allocated for that shape.

Raises:
OutputError

If the handler is closed or was never opened, the run keeps no field at this plane, or shape is not the run’s field shape.

Examples

>>> from solprop.grid import EnvelopeGrid
>>> from solprop.output import MemoryOutput
>>> from solprop.output.schema import (
...     GridRecord, RunMetadata, SavedPlane, StepperRecord, StoredField
... )
>>> from solprop.stepper import DOPRI5, PIController
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (400e-9, 2e-6), 2e-12)
>>> handler = MemoryOutput()
>>> handler.open(RunMetadata(
...     grid=GridRecord.from_grid(grid),
...     modes=(),
...     simulation_kind="mode_averaged",
...     configuration={},
...     stepper=StepperRecord.from_settings(
...         DOPRI5, PIController(rtol=1e-8, atol=0.0)
...     ),
... ))
>>> plane = SavedPlane(z=0.0, field=None)
>>> row = handler.field_destination(plane, (1, 1, grid.n_omega))
>>> row[...] = 1.0
>>> handler.save_exported(plane, StoredField(row))
>>> stored = handler.fields("optics")
>>> stored.shape, bool((stored == 1.0).all())
((1, 1, 1, 2560), True)
property noise_phase: ndarray | None[source]#

Return the stored noise phases, or None when the run had no noise.

Returns:
numpy.ndarray or None

Shape (n_z, n_modes, n_pol, n_omega), rad, in the internal FFT convention, which is where the noise field lives.

Raises:
OutputError

If the run did not keep its field, and so its noise phase, on every plane.

planes()[source]#

Return every stored plane, back in the internal convention.

The inverse of the whole save path: the field comes back through solprop.fourier.spectrum_to_internal(), so a plane that comes out is the plane that went in. This is how to_file() re-feeds the file handler. A plane that kept no field comes back with field=None.

Returns:
list

One solprop.output.schema.SavedPlane per saved plane, in order.

Examples

>>> from solprop.output import MemoryOutput
>>> MemoryOutput().planes()
[]
to_file(path, *, compression=None, compression_opts=None)[source]#

Write the file an solprop.output.hdf5.HDF5Output would have written.

Parameters:
pathpathlib.Path or str

Where to write. An existing file is replaced.

compression{None, ‘gzip’, ‘lzf’}, optional

Dataset compression; None for none.

compression_optsint or None, optional

Level for 'gzip', 0 to 9; not accepted for the other choices.

Raises:
OutputError

If the handler was never opened.

Examples

>>> import numpy as np, tempfile
>>> from pathlib import Path
>>> from solprop.grid import EnvelopeGrid
>>> from solprop.output import HDF5Output, MemoryOutput
>>> from solprop.output.hdf5 import resume
>>> from solprop.output.schema import (
...     GridRecord, RunMetadata, SavedPlane, StepperRecord
... )
>>> from solprop.stepper import DOPRI5, PIController
>>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (400e-9, 2e-6), 2e-12)
>>> metadata = RunMetadata(
...     grid=GridRecord.from_grid(grid),
...     modes=(),
...     simulation_kind="mode_averaged",
...     configuration={},
...     stepper=StepperRecord.from_settings(
...         DOPRI5, PIController(rtol=1e-8, atol=0.0)
...     ),
... )
>>> handler = MemoryOutput()
>>> handler.open(metadata)
>>> start = np.ones((1, 1, grid.n_omega), complex)
>>> handler.save(SavedPlane(z=0.5, field=start))
>>> with tempfile.TemporaryDirectory() as folder:
...     path = Path(folder) / "run.h5"
...     handler.to_file(path)
...     resume(path).plane.z
0.5