solprop.output.metadata#
The run record: what produced an output file, and the resume key it is checked by.
An output file has to answer one question years later: what produced this?
RunMetadata is that answer: the grid, the modes, the serialised physics
configuration, the package versions, the git provenance of the running checkout, the
noise seed and realisation, and the stepper settings.
The resume key. RunMetadata.hashed() is the part of the record that says what
was computed: the schema version, the grid, the modes, the configuration, the noise, the
stepper settings with the stepping method when it is not the tableau (SEP 13), and the
identities and policies of the run’s material manifest, and nothing else: not the
wall-clock time the run started, not the package versions, and not the git provenance.
RunMetadata.configuration_hash() is a SHA-256 of its canonical JSON rather than
hash(), so it is stable across processes; it is stamped in every file and is the
exact identity a resume, a suspended writer, a snapshot and a scan row are checked by
(ADR-066): a resume with different physics or numerics of the same shape is refused,
naming the fields that changed (RunMetadata.summarise_differences()). A record
read from a file is compared as it was written.
The manual page is docs/guide/results/provenance.md.
Module Attributes
Version of the saved-output layout; incremented when a stored field changes meaning. |
|
Name of the post-run annotation holding the material manifest's peak-field report. |
|
Name of the post-run annotation listing where a dense run was resumed, m. |
|
Name of the annotation holding the start-of-run summaries of a plane store. |
Functions
|
Decide whether a handler may write metadata, under one rule for both handlers. |
Classes
|
Everything needed to say what produced a file, and to refuse a wrong resume. |
- solprop.output.metadata.INTERPOLATED_RESUMES_ANNOTATION = 'interpolated_resumes'#
Name of the post-run annotation listing where a dense run was resumed, m.
A list of the positions of the interpolated save planes a run observed densely was continued from with
resume="interpolated": each continuation started afresh from a field interpolated inside a step, not a state the march passed through (SEP 14). Written bysolprop.driver.simulation.Simulation.run(), outside the configuration hash.
- solprop.output.metadata.PEAK_FIELDS_ANNOTATION = 'material_peak_fields'#
Name of the post-run annotation holding the material manifest’s peak-field report.
A list with one plain mapping per population with an optmat ionisation rate: the largest field it reached against its model’s maximum field, and the energy per event there against the constant the run paid (
solprop.nonlinear.optmat_rates.PeakFieldReport). Written bysolprop.driver.simulation.Simulation.run()when a run closes, outside the configuration hash (SEP 3, Q13).
- solprop.output.metadata.RUN_SUMMARY_ANNOTATION = 'run_summary'#
Name of the annotation holding the start-of-run summaries of a plane store.
A list with one mapping per start,
{"title": str, "rows": [[key, value], ...]}: a run that is resumed adds the summary of its continuation to the first one’s. Written when a run starts, not when it closes, and outside the configuration hash.
- class solprop.output.metadata.RunMetadata(*, grid, modes, simulation_kind, configuration, stepper, noise=None, backend=None, versions=<factory>, git=<factory>, created='', schema_version=2, spectral_convention='optics', field_layout='(n_z, n_modes, n_pol, n_omega)', spatial=None, modal=None, materials=<factory>, initial_z=0.0, tdse=None, quadrature=None, reference=None, transverse=None, noise_resolution=None, defaults=None)[source]#
Bases:
DisplayedEverything needed to say what produced a file, and to refuse a wrong resume.
Equality is by identity (
eq=False); two runs are the same run when theirconfiguration_hash()agree, which is what a resume actually asks.- Attributes:
- gridGridRecord
The grid the run was computed on.
- modestuple
One
ModeRecordper propagated mode, in state order.- simulation_kind{‘mode_averaged’, ‘multimode’, ‘radial’, ‘free’, ‘slab’}
Which transform the run used.
- configurationdict
The physics objects that built the run – responses, profiles, pulses, boundaries – as
serialise()records them. Nothing is silently dropped: a callable is present as itsreprwithreproducible: False.- stepperStepperRecord
The solver settings and the schedule taken.
- noiseNoiseRecord or None
The shot-noise realisation, or
Nonefor a noiseless run.- noise_resolutionNoiseResolution or None
How the noise model was chosen (SEP 37): the requested and the resolved model and any fallback’s reason. Outside the hash;
Nonewhen the record’s builder did not say, as for a record built by hand.- defaultsDefaultsResolution or None
Which of the integrator, the controller and the collar scheme took the route’s default. Outside the hash;
Nonewhen the record’s builder did not say.- backendBackendRecord or None
What executed the run, written by the driver from the backend’s own
metadata();Nonefor a record built before a backend existed. Outside the hash, like the versions and the provenance beside it: a run continued on the other backend is the same run.- versionsdict
Package versions, from
package_versions(). Outside the hash.- gitGitProvenance or None
Provenance of the running checkout, or
None. Outside the hash.- createdstr
When the run started, ISO 8601 in UTC. Outside the hash.
- schema_versionint
Version of this record layout,
SCHEMA_VERSION; any other is refused.- spectral_conventionstr
Sign convention of the stored field, always
'optics'.- field_layoutstr
Axis order of the stored field.
- spatialcollections.abc.Mapping or None
Spatial geometry, axis meanings, integration measures, field units and the plane measure (
flux_modelandflux_plan, SEP 34). Required for the radial, free and slab routes andNonefor modal runs. Included in the resume key.- modalcollections.abc.Mapping or None
The identity of a general modal guide: the ordered channel labels, the table, metric, response and provider identities, the sampling the reference is rebuilt from, the gauge, the frame, the active band, the noise reference basis and the accessors this basis admits, as
solprop.translate.general.modal_identity()writes it.Nonefor every other route. Included in the resume key, less the accessor list, so that a run continued against a different guide, a different table or a reordered mode axis is refused by the field that changed (solprop.output.capability).- materialssolprop.physdata.selection.MaterialManifest
The run’s material manifest: the optmat version, the identity of every optmat model the run resolved, its species, compositions and solids, the policies in force and what was measured about them. Empty for a run of solprop’s own materials. Its identities and policies are in the resume key (
hashed()), so a run continued with another model, policy or optmat version is refused by the entry that changed.- initial_zfloat
Absolute launch position in metres; zero for a run launched at the entrance. Included in the resume key.
- tdsecollections.abc.Mapping or None
Resolved scientific atomic descriptor, included in the resume key;
Nonefor a run without a TDSE response. Provider execution and device facts belong to the backend record.- quadraturecollections.abc.Mapping or None
The transverse rule of a resolved modal run, as
solprop.modes.quadrature.TransverseQuadrature.record()writes it: the family, the counts, the nodes and the physical weights, in the resume key (SEP 19).Nonefor the mode-averaged and spatial routes.- referencecollections.abc.Mapping or None
The plane-reference record (SEP 31):
carrier, the grid’s reference frequency in rad/s;origin, the reference of the run’s path origin (phase, delay, calibrated), inherited from a parent plane for a run launched from one;parent, that parent’s provenance orNone;frame, the frame integral ofsolprop.translate.coupling.frame_record(), orNonefor an operator supplied without frame tables; andbasis, the route and the basis identity (solprop.coupling.plane.basis_identity()) at each planned planez, the launch plane first.solprop.coupling.plane.reference_at()rebuilds any saved plane’s reference from it. Derived numbers, so outside the hash; the inherited origin enters it throughconfiguration["coupling"]. Every run records one;Noneon a record built without it, as by hand, whose planes cannot be exchanged (solprop.processing.planes.field_plane()).- transversecollections.abc.Mapping or None
A free-space run’s resolved transverse boundary (SEP 40), as
solprop.spatial_absorber.SpatialAbsorberDesign.record()writes it: the safety ramp’s start and strength, and a collar’s strength, band, threshold and step limit. Derived numbers, so outside the hash; the specification is inconfiguration["spatial_absorber"]where one was given.Nonewithout a transverse boundary.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.output.metadata import RunMetadata >>> from solprop.output.records import GridRecord, 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={"gamma": 0.11}, ... stepper=StepperRecord.from_settings( ... DOPRI5, PIController(rtol=1e-8, atol=0.0) ... ), ... ) >>> len(metadata.configuration_hash()) 64 >>> metadata.spectral_convention 'optics'
- backend: BackendRecord | None = None#
- configuration_hash()[source]#
Return the resume key: a digest of everything that was computed.
A SHA-256 of the canonical JSON of
hashed(), so it is stable across processes – unlikehash(), which is salted per interpreter. It covers the physics and the numerics, and not only the statistic names and the state shape, so a resume continues exactly when the two hashes are equal (ADR-066).- Returns:
- str
Sixty-four lowercase hexadecimal characters.
Examples
>>> from solprop.output.metadata import _example_metadata >>> metadata = _example_metadata() >>> metadata.configuration_hash() == metadata.configuration_hash() True
- defaults: DefaultsResolution | None = None#
- differences(other)[source]#
Return the dotted paths at which two records describe different runs.
This is what a refused resume names, so that the reason is the field that changed rather than “the hash does not agree”.
- Parameters:
- otherRunMetadata
The record to compare against.
- Returns:
- tuple
Dotted paths into
hashed(), in depth-first order; empty when the two describe the same run.
Examples
>>> import dataclasses >>> from solprop.output.metadata import _example_metadata >>> one = _example_metadata() >>> two = dataclasses.replace(one, configuration={"gamma": 0.22}) >>> one.differences(two) ('configuration.gamma',)
- classmethod from_json(text, arrays=None)[source]#
Return the record a JSON document describes.
The configuration comes back as the plain structures
serialise()made of it, not as the physics objects: a callable was recorded byreprand cannot be rebuilt. That is enough for the hash, which is what a resume compares.- Parameters:
- textstr
A document produced by
to_json().- arrayscollections.abc.Mapping or None, optional
What
record_arrays()gave for it, by digest, or an HDF5 group holding them. Each comes back in place of its reference, its digest checked.
- Returns:
- RunMetadata
The record, hashing identically to the one that produced
text.
- Raises:
- OutputError
If the document is not valid JSON, declares an output schema other than
SCHEMA_VERSION, a required field is missing, or an array it names is absent from arrays or does not have its digest.
Examples
>>> from solprop.output.metadata import _example_metadata >>> metadata = _example_metadata() >>> back = RunMetadata.from_json(metadata.to_json()) >>> back.configuration_hash() == metadata.configuration_hash() True
- git: GitProvenance | None#
- grid: GridRecord#
- hashed()[source]#
Return the part of the record the resume key is taken over.
Everything that describes what was computed, and nothing that describes when or with which build: the wall-clock time, the package versions and the git provenance are excluded, so that a resume survives an unrelated commit or a dirty working tree, as long as they move no number the record holds; a new library or platform may move one, and the resume is then refused.
modal.accessorsis excluded for the same reason – it is what this build can compute, not what this run did (_hashed_modal).- Returns:
- dict
The serialised schema version, grid, modes, simulation kind, configuration, noise and stepper settings, the spatial record, the power convention, the general modal identity, the material manifest’s identities and policies, the launch position, the TDSE descriptor and the transverse quadrature.
Examples
>>> from solprop.output.metadata import _example_metadata >>> sorted(_example_metadata().hashed())[:4] ['configuration', 'grid', 'initial_z', 'materials']
- materials: MaterialManifest#
- modes: tuple[ModeRecord, ...]#
- noise: NoiseRecord | None = None#
- noise_resolution: NoiseResolution | None = None#
- property power_convention: str[source]#
Amplitude label of the stored field.
Always
"poynting": the interface modal amplitude, whose squared modulus is the true power in watts (docs/dev/conventions.md§4).
- record_arrays()[source]#
Return the arrays stored beside the record, by digest.
What
to_json()’s references name: each numeric array of more thanRECORD_ARRAY_LIMITelements. A store keeps them with the document, andfrom_json()takes them back.- Returns:
- dict
The arrays, not copied, each by the digest of its bytes with its dtype and shape.
Examples
>>> import dataclasses >>> import numpy as np >>> from solprop.output.metadata import _example_metadata >>> metadata = dataclasses.replace( ... _example_metadata(), configuration={"rate": np.zeros(5000)} ... ) >>> [array.size for array in metadata.record_arrays().values()] [5000]
- stepper: StepperRecord#
- summarise_differences(other, *, shown=4)[source]#
Say how two records differ, briefly enough to read in a refusal.
The paths of
differences(), with every array’s element paths grouped under the array’s name and counted. Every part of the record that changed (configuration,materials,noise,schema_version, …) is named, with its first shown entries and a count of the rest, so that no cause is hidden behind another part’s many entries. Where the noise differs and this record’s realisation was drawn from a known seed, the summary says which seed draws it again.- Parameters:
- otherRunMetadata
The record to compare against; this one is the stored record.
- shownint, optional
How many entries to name in each part of the record.
- Returns:
- str
"these fields changed: ...", empty of entries when the two describe the same run.
Examples
>>> import dataclasses >>> from solprop.output.metadata import _example_metadata >>> one = _example_metadata() >>> two = dataclasses.replace( ... one, configuration={"gamma": 0.22, "betas": [1.0, 2.0, 3.0]} ... ) >>> one.summarise_differences(two) 'these fields changed: configuration.betas, configuration.gamma' >>> three = dataclasses.replace(one, configuration={"gamma": [0.1, 0.2, 0.3]}) >>> three.summarise_differences( ... dataclasses.replace(one, configuration={"gamma": [0.4, 0.5, 0.6]}) ... ) 'these fields changed: configuration.gamma (3 elements)'
- to_json()[source]#
Return the whole record as one JSON document.
An array of more than
RECORD_ARRAY_LIMITelements is a reference to whatrecord_arrays()holds.- Returns:
- str
Canonical JSON carrying the hashed part plus the whole general modal identity, whose published accessor list the hashed part leaves out, the whole material manifest, the plane-reference record, the noise model’s resolution, the conventions, the backend record, the versions, the git provenance and the creation time.
Examples
>>> import json >>> from solprop.output.metadata import _example_metadata >>> sorted(json.loads(_example_metadata().to_json()))[:3] ['backend', 'configuration', 'created']
- property transverse_measure: Literal['area', 'per_unit_width'][source]#
What the run’s transverse integrals measure.
A run on modes uniform in y (
solprop.modes.slab.SlabMode) recordsconfiguration["transverse_measure"] = "per_unit_width": its powers are W/m, its energies J/m and its intensities still W/m². Every other run measures an area. The key sits in the free-form configuration and is written only where it is not the default."area"or"per_unit_width".- Raises:
- OutputError
If the stored value is neither.
Examples
>>> from solprop.output.metadata import _example_metadata >>> _example_metadata().transverse_measure 'area'
- with_backend(backend)[source]#
Return this record with a run’s execution facts, its resume key kept.
The backend record is outside the hash, so the hashed part and the resume key already computed stand: a run opening its output does not serialise and digest its configuration a second time, which cost a capillary run 30 ms and a large launch seconds (solprop issue #347).
- Parameters:
- backendBackendRecord
The execution facts of the prepared backend.
- Returns:
- RunMetadata
A copy of this record with backend in place of its own.
Examples
>>> from solprop.output.metadata import _example_metadata >>> from solprop.output.provenance import BackendRecord >>> metadata = _example_metadata() >>> backend = BackendRecord(name="numpy_ref", propagator_mode="tabulate") >>> executed = metadata.with_backend(backend) >>> executed.backend.name 'numpy_ref' >>> executed.configuration_hash() == metadata.configuration_hash() True
- solprop.output.metadata.SCHEMA_VERSION = 2#
Version of the saved-output layout; incremented when a stored field changes meaning.
Schema 1 is the first released layout, 0.1.0’s: the record types of this module, written by
solprop.output.memory.MemoryOutputandsolprop.output.hdf5.HDF5Outputasdocs/guide/results/output.mddescribes them. Schema 2 recordsDataPulse.spectral_phase. A record that declares any other version is refused by name (RunMetadata): files written by an earlier release or by development versions of solprop, which declared output schemas up to 28, are not read, and nor is a file from a later release. Read such a file with the solprop version that wrote it.
- solprop.output.metadata.resolve_metadata_write(existing, new, *, force=False)[source]#
Decide whether a handler may write metadata, under one rule for both handlers.
Metadata is written once. A second write describing the same run is a no-op; one describing a different run is a mistake and raises, naming the fields that changed, unless the caller insists with
force, in which case the record is replaced and the replacement is logged.“The same run” means the same
RunMetadata.configuration_hash(), so a rerun of identical physics under a newer NumPy, or from a checkout that has since gained a commit, is still the same run.- Parameters:
- existingRunMetadata or None
What the handler already holds, or
Nonewhen it holds nothing.- newRunMetadata
What the caller is offering.
- forcebool, optional
Whether to overwrite a different record instead of refusing.
- Returns:
- bool
Whether the caller should write
new.
- Raises:
- OutputError
If a different record is already held and
forceis false.
Examples
>>> from solprop.output.metadata import _example_metadata, resolve_metadata_write >>> metadata = _example_metadata() >>> resolve_metadata_write(None, metadata) True >>> resolve_metadata_write(metadata, metadata) False