Trajectory¶
Time-dependent data consist of a series of samples (frames) referring to
multiple time steps. They are stored in the trajectory group of the
root. The logical model is an ordered sequence of frames
sharing a declared set of blocks and columns, together with an integer step
index and an optional physical time.
trajectory is a record section. The canonical entity remains the
frame.
trajectory
+-- sequence_schema
+-- nstep
+-- (step_progression: {start, stride})
+-- (time_progression: {start, stride})
\-- (step: i64[nstep])
\-- (time: f64[nstep])
\-- (meta)
| \-- <key>: <dtype>[nstep][...]
\-- (box)
\-- <block>
\-- ...
This is the logical picture; the full on-disk tree, with the elisions that make the common run cost one array per column, is Ragged trajectory.
step
The producer's iteration counter at each committed frame, strictly
increasing — a repeated or smaller step number is rejected when the frame is
appended. On the reference binding it is the step_progression attribute
while the numbering is arithmetic and a step array otherwise; the number
of committed frames is the trajectory group's nstep attribute, written
last in a commit (Ragged trajectory).
time
An optional dataset that is the same as step, except it is f64-valued
and contains the simulation time. It is all-or-nothing: a run either
supplies a time for every frame or for none, and a writer refuses a frame
that breaks either way.
Two integers index a trajectory and they are not the same one. The frame
ordinal i is a frame's position in the sequence, 0 <= i < nstep. The
step number is the value stored at step[i]. It may start anywhere and
may skip values.
meta
Per-step scalars and small fixed vectors, one typed array per key. The
standard keys — pe, ke, etotal, temp, press, volume, all
f64 — are standardized identifiers.
Their declaration and tag set are in Ragged trajectory.
Blocks over time¶
Every block the run may carry is declared when the trajectory is created. At each frame a declared block is present (it has rows), empty (its most recent update had zero rows) or absent (it has not appeared yet). A frame that omits a block does not change it: the block carries forward from the previous frame. A frame that presents a zero-row block makes it empty from then on. Once a block has appeared it is never absent again. The cell carries forward the same way. The full rules are The three states of a block.
A block may be declared aligned with another: its rows are the other block's rows, so it can change rarely beside a block that changes every frame, and it is restated whenever the other's row count changes.
With and without system¶
A record may omit system and still carry trajectory (frames may embed
full blocks, including topology). When both system and trajectory are
present, trajectory should update state only (coordinates, instantaneous
properties, instantaneous box) and not restate topology held in system.
When system/<block> and trajectory/<block> coexist, they are aligned
1:1 by row order:
- every update of
trajectory/<block>holds exactly as many rows assystem/<block>; - if both carry an
idcolumn, the values are equal row for row, so a reader may join onidas well as on position; - a ragged trajectory block (row count varying per frame) MUST NOT
share a name with a
systemblock.
Evolving frame-like state belongs in trajectory. Reduced scientific
statistics belong in observables; run-local monitoring
belongs in metrics.
Time-independent data are stored as arrays or document objects without a
leading [nstep] axis. A frame section is one snapshot. Topology and
types that do not change in time belong in system.
The reference binding stores each block as a sparse update series (an
append-first CSR layout). That encoding is specified under
Ragged trajectory; the reference streaming writer is
molrs.io.mrec.TrajectoryWriter, the whole-sequence doors
molrs.io.write_mrec_trajectory / read_mrec_trajectory.