Recording and inspecting provenance#
A saved result needs the grid, field convention, material models, response choices and software identities that produced it. Inspect these before comparing runs or reusing a field. A record identifies the declared model and code version; it does not embed all source code or establish physical accuracy.
The metadata schema#
RunMetadata is a frozen record with these parts:
field |
what it holds |
|---|---|
|
A |
|
One |
|
Which transform the run used: |
|
The identity of a general modal guide, and |
|
For a radial, free-space or planar run, the spatial geometry, the meaning of each
axis, the integration measures and the field units; |
|
The transverse rule of a resolved modal run: its family, counts, nodes and
physical weights, included in the resume key. |
|
The run’s material manifest, a
|
|
Every physics object that built the run, serialised. |
|
Absolute launch position in metres, zero by default. Profiles and modal normalisation retain this absolute coordinate. |
|
The resolved scientific TDSE descriptor, or |
|
A |
|
A |
|
A |
|
Provenance: package versions, the commit and dirty flag of the running solprop checkout when there is one, and when the run started. |
|
The plane-reference record (output schema 24,
SEP 31): the carrier, the
frame integral of the terms the linear operator removed (exact Chebyshev
antiderivatives of the frame’s |
|
The version of this record layout (currently 24), the sign convention of the
stored field (always |
backend, versions, git, created and reference are outside the
resume key: the plane-reference record is derived from the hashed
problem, and a run launched from another plane carries its inherited origin and parent
in configuration["coupling"], which is inside it. Every other part is inside it, the material manifest’s identities and policies included.
versions names optmat’s full version string beside solprop’s, NumPy’s, SciPy’s,
h5py’s and Python’s.
The material manifest#
A run that takes a material from optmat – a gas, an air composition or a solid,
selected with materials= (materials) or built
by the caller – records what it took. The manifest is collected once per run, from the
run’s own objects, wherever an optmat-backed one sits: the mixture, a mode’s fill and
cladding, a planar grid’s slab modes, a step-index core, a general guide’s material
regions, a bulk medium. The walk enters any object with fields or attributes –
solprop’s, a caller’s subclass, a mode of the caller’s own –, a functools.partial
and a bound method, but not a function’s closure, nor numpy’s, SciPy’s or optmat’s own
objects (optmat’s are recorded through the solprop adapters that hold them). It
holds two kinds of entry.
Identities and policies, which are inside the resume key:
optmat_version, the installed optmat’s full version string;records, every resolved model’s optmat identity (MaterialRecord: name, quantity, material, content digest, status, overrides and their base), each digest once, keyedmaterial:kind:name;species, each optmat population’s label (or a planar slab’s single-species fill) with the digests of its models;compositions, the named optmat composition a mixture’s fractions came from, such as"dry air (N2, O2)", with its labels and fractions;solids, each optmat solid’s material, axis, model and digest, and whether its index is taken as transparent, with the absorption bound of that approximation;policies, the approximations in force: linear density scaling of a gas susceptibility, with its warning threshold; the electronic policy and the carrier wavelength; the Raman line-shift policy and reference temperature; and the plasma energy-loss convention. A policy the run does not use (a Raman shift without a shifting response, a GNLSE fraction off the GNLSE) is recorded asNone.
Measurements, which are recorded beside them and are not in the key:
selection, theMaterialSelectionas it was given. What a name resolved to is inrecords, so naming a model"recommended"or by its own name keys alike;dilute_scaling, the error of linear density scaling for each optmat population (or a planar slab’s single-species fill) at its largest density, measured once over the run’s whole band, collars included, and recorded whatever its size;each solid’s
transparency, the largest extinction and absorption coefficient a transparent index dropped in this run (an adapter shared by several runs records each run’s own);ranges, the parts of the evaluated bands that lay outside a model’s qualified intervals, as the run’s range warnings named them;citations, the citation of every other material object of the run, supplied or solprop’s own.
A run that takes nothing from optmat has the empty manifest — a run of supplied
materials only: its key holds materials: null. Every run of the built-in materials is an optmat
run, with a manifest.
from solprop.driver import prop_capillary
run = prop_capillary(..., "Ar", 1.0, output="run.h5") # materials=MaterialSelection()
manifest = run.metadata.materials
[record.name for record in manifest.records]
# ['eos/Ar', 'hyperpolarizability/Ar', 'fit/Ar', 'rate/default/Ar@800nm',
# 'transport/Ar', 'fit/SiO2']: the gas, its Kerr fit and rate at the carrier, the wall
manifest.dilute_scaling[0]["relative_error"] # about -2e-4 for argon at 1 bar
Post-run annotations#
Some of what a run learns about its materials is known only once it has closed. The
record is written before the first plane, so it cannot hold it, and it must not: the
record is the run’s identity, and the identity of a run cannot depend on how far it
got. Such results are post-run annotations: plain JSON, by name, written beside the
record when the run closes and outside the configuration hash, so the resume gate,
a scan’s reuse of a completed point and the record’s digest are exactly what they were.
Both handlers carry them (handler.annotations, a mapping by name;
handler.annotate(name, value) writes one, replacing one of the same name, and a
closed file is reopened briefly for it), MemoryOutput.to_file copies them, and
open_file() reads them back. A file without any reads with
an empty mapping.
One is defined, PEAK_FIELDS_ANNOTATION
("material_peak_fields"): the material manifest’s post-run diagnostic of a run with
optmat ionisation rates (ionisation), one mapping
per population — the largest field it reached against its model’s maximum field, and
the rate-weighted energy per event there against the constant the run paid. The run
writes it when it closes; the record carries only its inputs. A run without an optmat
rate writes none.
from solprop.output import open_file
from solprop.output.schema import PEAK_FIELDS_ANNOTATION
with open_file("run.h5") as stored:
(report,) = stored.annotations.get(PEAK_FIELDS_ANNOTATION, [])
report["largest_field"], report["reached_maximum"], report["departure"]
Serialising the physics, and admitting what will not serialise#
serialise() is total and idempotent. Dataclasses become
tagged dictionaries of their constructor fields, enumerations carry their qualified class
name as well as their value, NumPy arrays become nested lists with their dtype (a complex
array splitting into real and imaginary parts, since JSON has no complex number), and
mappings and sequences recurse.
What cannot be reduced is not dropped. A function, a lambda, a spline closure — the
beta(omega) a SpecifiedMode was built from, say — is recorded as its repr
with reproducible: False beside it. A configuration that is silently incomplete is
worse than one that says where it stops.
Fields declared init=False are skipped: they are derived in __post_init__ from the
ones that are not, so recording them would store the same information twice.
Tabulated materials#
Cubic splines in material definitions are recorded by their knots, polynomial coefficients, interpolation axis and extrapolation setting. Independently created copies therefore have the same configuration hash, and changing any of those values changes the hash. This covers tabulated cladding glasses, among others.
Provenance, not a source snapshot#
A file records the resolved configuration, the package versions, and the commit with a
dirty flag — never the source text, which would make every file carry a copy of the
package. When there is no checkout, or git is absent, or the
command fails, detect() returns None
rather than a sentence dressed up as a hash.