FlowField::save/load serialize the complete field state bit-exact
(all twelve matrices including *_old, predictors and sources, so a
load is a true restart state), with a roundtrip test asserting
to_bits equality on every value and rejection of truncated/corrupt
files.
The march gains an ECSW snapshot knob (RTX_FSI{2,3}_SNAP path,
SNAPEVERY, default off): every N committed steps it appends an FSNP
record — t, full-DOF displacement/velocity/acceleration (what
rtx_fea::mor's pod_basis/train_ecsw consume, plus what the phase-4
dynamic reduction will need) and the committed sparse nodal load for
the offline full-vs-reduced replay. Reporting-only: reads committed
state after acceptance, no float ops on the solver path. Verified:
smoke run's FSNP parsed by an independent reader (570 DOFs, correct
record count, physical values); FSI2 committed default
digit-identical with the knob off.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
`get_velocity_at` averaged u-faces `i - 1` and `i` to report the velocity
at cell `i`. On this staggered layout `u` is `(ny, nx + 1)` and cell `i`
is bounded by faces `i` and `i + 1` -- which is the convention
`compute_mass_source` uses to form the divergence, and therefore the one
that defines the grid. The two disagreed by one index.
Consequences: every profile read through this function was shifted half a
cell west of the field the solver actually computed, the first and last
cells were special-cased to a single face, and the outermost face was
never read at all.
It is a diagnostic path rather than a solve path -- the residuals are
byte-identical before and after -- but the cavity comparison against Ghia
is taken through it, so the reported vortex position was affected. The
corrected grid study, unchanged in the solve:
n u_min y
17^2 -0.1257 0.4375
33^2 -0.1550 0.4688
65^2 -0.1743 0.4844
97^2 -0.1825 0.5000
Ghia -0.2109 0.4531
The shift matters most where the grid is coarse and washes out under
refinement, which is what a half-cell offset should do.
Found while establishing where each staggered variable physically sits, a
prerequisite for applying the method of manufactured solutions to this
solver.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>