Recipes and run provenance #12

Closed
opened 2026-09-30 11:08:03 -04:00 by connor · 2 comments
Owner

recipes::leo_standard and friends: constructors returning the plain force list, not an opaque mode. A serializable provenance record carrying the resolved force composition, integrator, tolerances, crate versions and loaded kernels.

M1.

`recipes::leo_standard` and friends: constructors returning the plain force list, not an opaque mode. A serializable provenance record carrying the resolved force composition, integrator, tolerances, crate versions and loaded kernels. M1.
connor added this to the M1 milestone 2026-09-30 11:24:57 -04:00
connor added the size/M label 2026-09-30 11:24:57 -04:00
Author
Owner

Two things this needs to carry, both found while doing #14 rather than here.

Which measurement corrections were applied. Observed::with_light_time and
Observed::with_aberration are ordinary methods, deliberately not Cargo features, because
a feature is global to a build and so cannot be part of a recipe. The consequence lands
here: the record has to say which corrections a set of measurements was produced with, or
two runs of the same recorded recipe can legitimately disagree. See the reasoning on #14.

Alongside that, the record now also has a station list and a measurement list to carry,
which did not exist when this issue was written.

What provenance means here was settled: record what was used, not a byte-exact replay.
serde_json's float parser is not correctly rounded and can read a value back a ULP off
what was written — observed with 0.12217304763960307, where str::parse and a correctly
rounded reference parser both return the bits that were written. Chasing bit-identical
replay through floating-point propagation is not a goal worth having, so a recorded run
promises that the same inputs, models and settings were used, not that every number comes
back identical. Worth stating in the record's own documentation so nobody reads more into
it than it offers.

Two things this needs to carry, both found while doing #14 rather than here. **Which measurement corrections were applied.** `Observed::with_light_time` and `Observed::with_aberration` are ordinary methods, deliberately not Cargo features, because a feature is global to a build and so cannot be part of a recipe. The consequence lands here: the record has to say which corrections a set of measurements was produced with, or two runs of the same recorded recipe can legitimately disagree. See the reasoning on #14. Alongside that, the record now also has a station list and a measurement list to carry, which did not exist when this issue was written. **What provenance means here was settled: record what was used, not a byte-exact replay.** `serde_json`'s float parser is not correctly rounded and can read a value back a ULP off what was written — observed with `0.12217304763960307`, where `str::parse` and a correctly rounded reference parser both return the bits that were written. Chasing bit-identical replay through floating-point propagation is not a goal worth having, so a recorded run promises that the same inputs, models and settings were used, not that every number comes back identical. Worth stating in the record's own documentation so nobody reads more into it than it offers.
Author
Owner

Landed in 82074b7, with a round of review changes on top.

Recipes are Regime x Fidelity, not a list of free functions. Regime::forces(fidelity, epoch) returns the plain Vec<Dynamics> the issue asks for, so it stays readable, editable and recordable. Two knobs rather than one because neither alone is enough: a single "high fidelity" dial that added forces would be wrong in a way that depends on the regime.

A Propagation::for_regime constructor was built and then removed — it saved one line and charged a five-argument signature with two same-shaped enums adjacent, which is easy to transpose.

Density selection is epoch-aware. NRLMSISE-00 inside the measured space weather record, Harris-Priester outside it, with a logged warning on the fallback. This matters more than it sounds: the usable record ends 41 days before its last measured day, because the 81-day centered average needs that many days after the epoch, so an arc at or near the present is always outside it. A recipe that returned MSIS regardless would hand almost every forward-looking run a force list that cannot be propagated. msis is now a default feature.

Provenance carries the crate version, epoch, duration, frame, layout and dimension, the resolved force list, integrator, both tolerances, the vehicle, and the kernel manifest with digests. It has a Display as well as serde: Debug renders a Body as its four constants, so a force list naming the Sun and Moon came out as a wall of numbers. Per-component tolerances are run-length grouped, since a Full layout is thirteen identical floors and one different one for energy.

Two things it deliberately does not promise, both documented rather than papered over: dependency versions are not reachable at runtime, and the kernel list is the manifest rather than an inventory of the attached almanac.

Ready.

Landed in `82074b7`, with a round of review changes on top. **Recipes are `Regime` x `Fidelity`**, not a list of free functions. `Regime::forces(fidelity, epoch)` returns the plain `Vec<Dynamics>` the issue asks for, so it stays readable, editable and recordable. Two knobs rather than one because neither alone is enough: a single "high fidelity" dial that added forces would be wrong in a way that depends on the regime. A `Propagation::for_regime` constructor was built and then removed — it saved one line and charged a five-argument signature with two same-shaped enums adjacent, which is easy to transpose. **Density selection is epoch-aware.** NRLMSISE-00 inside the measured space weather record, Harris-Priester outside it, with a logged warning on the fallback. This matters more than it sounds: the usable record ends 41 days before its last measured day, because the 81-day centered average needs that many days after the epoch, so an arc at or near the present is *always* outside it. A recipe that returned MSIS regardless would hand almost every forward-looking run a force list that cannot be propagated. `msis` is now a default feature. **`Provenance` carries** the crate version, epoch, duration, frame, layout and dimension, the resolved force list, integrator, both tolerances, the vehicle, and the kernel manifest with digests. It has a `Display` as well as serde: `Debug` renders a `Body` as its four constants, so a force list naming the Sun and Moon came out as a wall of numbers. Per-component tolerances are run-length grouped, since a `Full` layout is thirteen identical floors and one different one for energy. Two things it deliberately does not promise, both documented rather than papered over: dependency versions are not reachable at runtime, and the kernel list is the manifest rather than an inventory of the attached almanac. Ready.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: connor/astrokit#12