Keep every column of probe output in golden packs - #1712
Conversation
The packer treats each .dat file under D/ as a spatial field of the form
<x> [<y> <z>] <value>, inferring the dimensionality from the first line and
keeping only the last column of each row:
ndims = len(_extract_doubles(content.split(chr(10), 1)[0])) - 1
doubles = _extract_doubles(content)[ndims :: ndims + 1]
Probe output is not a spatial field. It is a multi-column time series whose
columns are distinct physical quantities, and the set varies by configuration:
1D, general nondim_time, rho, vel(1), pres
bubbles nondim_time, rho, vel(1), pres, alf, R, Rdot, nR, nRdot
hypoelastic nondim_time, rho, vel(1), vel(2), pres, tau_e(1..3)
3D nondim_time, rho, vel(1..3), pres, gamma, pi_inf, qv, c, accel
Under the field interpretation only the final column survived, so the 3D golden
validated the acceleration magnitude alone and discarded density, velocity,
pressure and the sound speed. lag_bubble files were already special-cased for
the same reason; probe files are the remaining case.
Goldens regenerate in a follow-up commit, from a clean tree.
5CAA4E68 and FBB296DA go from 50 stored values to 450 (nine columns per row instead of one); AE9A7D73 from 1 to 9. The added values are the columns the field interpretation was discarding: density, velocity, pressure, void fraction, and the bubble radius/velocity moments. The recorded provenance says (dirty) because regenerating tracked goldens dirties the tree before the metadata is stamped; this is inherent to regenerating existing goldens rather than adding new ones.
There was a problem hiding this comment.
Pull request overview
This PR updates the golden packer so probe .dat outputs under D/ are treated as multi-column time series (retaining all columns) instead of being misinterpreted as spatial fields (which previously retained only the last column). This improves regression coverage for probe diagnostics that are not in the final column.
Changes:
- Special-case probe outputs in
toolchain/mfc/packer/pack.pyto retain all numeric columns. - Regenerate affected probe-based golden packs to include the full probe column set.
- Update golden metadata files produced during regeneration.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| toolchain/mfc/packer/pack.py | Adds probe-specific parsing path to retain all columns for probe outputs. |
| tests/FBB296DA/golden.txt | Updated golden pack content reflecting full probe columns. |
| tests/FBB296DA/golden-metadata.txt | Updated provenance metadata for regenerated golden. |
| tests/AE9A7D73/golden.txt | Updated golden pack content reflecting full probe columns. |
| tests/AE9A7D73/golden-metadata.txt | Updated provenance metadata for regenerated golden. |
| tests/5CAA4E68/golden-metadata.txt | Updated provenance metadata for regenerated golden. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| lines = content.splitlines() | ||
| content = "\n".join(lines[1:]) # Skip the first line | ||
| doubles = _extract_doubles(content) | ||
| elif "probe" in short_filepath: |
| OpenMP : OFF | ||
|
|
||
| Fypp : /home/bok/dev/MFC/build/venv/bin/fypp | ||
| Fypp : /private/tmp/claude-501/-Users-spencer-Downloads/2d95ba68-dea2-407b-8791-a954495b3fb2/scratchpad/mfc/build/venv/bin/fypp |
| OpenMP : OFF | ||
|
|
||
| Fypp : /home/bok/dev/MFC/build/venv/bin/fypp | ||
| Fypp : /private/tmp/claude-501/-Users-spencer-Downloads/2d95ba68-dea2-407b-8791-a954495b3fb2/scratchpad/mfc/build/venv/bin/fypp |
| OpenMP : OFF | ||
|
|
||
| Fypp : /Users/hyeoksu/MyWork/MFC-local/MFC/bubnorm/build/venv/bin/fypp | ||
| Fypp : /private/tmp/claude-501/-Users-spencer-Downloads/2d95ba68-dea2-407b-8791-a954495b3fb2/scratchpad/mfc/build/venv/bin/fypp |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1712 +/- ##
==========================================
+ Coverage 60.77% 61.24% +0.46%
==========================================
Files 83 83
Lines 20872 20700 -172
Branches 3101 3072 -29
==========================================
- Hits 12685 12677 -8
+ Misses 6121 5969 -152
+ Partials 2066 2054 -12 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
s_compute_speed_of_sound took ten arguments, and the contract between two of them was invisible at the call site: H must include qv, because the routine subtracts qv/rho internally. Nothing said so, and four call sites got it wrong (#1707) while a fifth was right only because two separate omissions cancelled. Introduces type(eos_state) in m_derived_types carrying the scalars the routine needs, and two constructors: - s_eos_state derives H from the other members, so it cannot disagree with qv. The defect in #1707 is unrepresentable through this path. - s_eos_state_roe takes H explicitly, for the Roe-averaged Riemann paths, the chemistry Roe branch and the relativistic branch, all of which pass an H that is deliberately not the exact state enthalpy. The call becomes (state, adv, c) instead of ten positional arguments. All 24 call sites across nine files are converted, and the state is added to the private() clause of every GPU parallel loop that builds one. adv stays a separate argument rather than a component. A derived-type component cannot have a runtime extent, and num_fluids is a parameter only under case optimization, so dimension(num_fluids) does not compile in a general build; padding to num_fluids_max would compile but place ten reals in a per-cell private struct on device. The three probe sites in simulation/m_data_output that open-coded H now use s_eos_state, which incorporates the #1707 correction as a consequence of the interface rather than as a separate patch. That changes the reported probe sound speed where qv /= 0; no golden observes it, because the packer keeps only the last column of probe output (#1711, fixed separately in #1712). Verified: builds clean; full suite 627 passed, 0 failed, no golden regenerated. A scan confirms no GPU parallel loop uses a state without declaring it private.
Found while reviewing this PR: probe files are not the only multi-column time
series under D/. s_write_integral_files emits
write (i + 70, '(6x,f12.6,f24.8)') nondim_time, int_pres
write (i + 70, '(6x,f12.6,f24.8,f24.8)') nondim_time, int_pres, max_pres
Under the field interpretation the three-column form keeps only max_pres and
silently drops int_pres, exactly the defect this PR fixes for probes.
Latent rather than active: no golden captures integral output today, because no
test enables it. Confirmed by running the full suite after the change -- 627
passed, 0 failed, no golden regenerated. Fixing it here means the coverage is
already correct whenever a test does enable it.
probe and integral are now the complete set of non-field .dat outputs under D/.
|
Ordering note: #1716 removes integral output entirely (unused since it was added; confirmed with its author). Once both land, the The two PRs do not conflict and can merge in either order. If #1716 goes first I will drop the clause from this branch before merge; if this goes first I will drop it in a follow-up. |
Fixes #1711.
The defect
toolchain/mfc/packer/pack.pytreats every.datfile underD/as a spatial field of the form<x> [<y> <z>] <value>, infers dimensionality from the first line, and keeps only the last column of each row:Probe output is not a spatial field. It is a multi-column time series whose columns are distinct physical quantities, and the set varies by configuration:
m_data_output.fpp:1519)nondim_time, rho, vel(1), presnondim_time, rho, vel(1), pres, alf, R, Rdot, nR, nRdotnondim_time, rho, vel(1), vel(2), pres, tau_e(1..3)nondim_time, rho, vel(1..3), pres, gamma, pi_inf, qv, c, accelUnder the field interpretation only the final column survived. In 3D that means the golden validated the acceleration magnitude and discarded density, velocity, pressure,
gamma,pi_inf,qv, and the sound speed.lag_bubblefiles were already special-cased for exactly this reason. Probe files are the remaining case.Change
Retain every column for probe files. No header line to skip —
s_write_probe_fileswrites data rows only.Effect on existing goldens
tests/5CAA4E68(1D exp_bubscreen)tests/FBB296DA(1D bubblescreen)tests/AE9A7D73(1D poly_bubscreen)Nine columns per row instead of one. The added values are what was being dropped: density, velocity, pressure, void fraction, and the bubble radius/velocity moments. That widening is the point of the change — it is coverage these cases should always have had.
The regenerated metadata records
(dirty). That is inherent to regenerating tracked goldens: writing them dirties the tree before the metadata is stamped. Newly added goldens do not have this problem.Verification
The three probe cases pass against their regenerated goldens. The change is gated on
"probe" in short_filepath, so non-probe files take the original code path unchanged and no other golden should move. A full local suite run confirms it: 627 passed, 0 failed.An earlier run of the same suite reported the three probe cases failing with "Variable count didn't match". That did not reproduce — the three pass in isolation, pass on consecutive repeat runs, and pass in the clean full-suite run above. It matches non-reproducible flakiness seen on an unrelated branch this session (two chemistry cases, likewise green individually), and appears to be local contention at high
-jrather than anything in this change. Worth knowing it has been seen, in case CI shows it.Why now
#1707 is a defect in the probe sound speed. It could not be given a regression test through the normal golden path, because
cis never the last column in any configuration — a case could exercise the defective code, emit visibly wrong output, and still pass. This PR is a prerequisite for testing that fix, and stands on its own regardless.Note that even with this change, observing
cspecifically requires a 3D probe case; the 1D writes do not emit it at all.Handover notes
Branch and commits
Split into two commits so the goldens are regenerated from a clean checkout of the packer change. Note the regenerated metadata still records
(dirty)— that is unavoidable when regenerating tracked goldens, because writing them dirties the tree before the metadata is stamped. Newly added goldens do not have this problem. Do not chase it.Environment
GNU 15.2.0, MPI, no GPU, macOS arm64.
The mechanism, precisely
toolchain/mfc/packer/pack.py, incompile():Every
.datunderD/is assumed to be<x> [<y> <z>] <value>, so only the last column of each row survives.lag_bubblefiles were already special-cased above this for the same reason; probe files were the remaining case. The fix adds anelif "probe" in short_filepathbranch keeping all columns.s_write_probe_fileswrites data rows only — there is no header to skip.Why this was needed
#1707 is a defect in the probe sound speed. It could not be given a regression test through the normal golden path, because
cis never the last column in any configuration:m_data_output.fpp)nondim_time, rho, vel(1), prespresnondim_time, rho, vel(1), pres, alf, R, Rdot, nR, nRdotnRdotnondim_time, rho, vel(1), vel(2), pres, tau_e(1..3)tau_e(3)nondim_time, rho, vel(1..3), pres, gamma, pi_inf, qv, c, accelaccelA case could exercise the defective code, emit visibly wrong output, and still pass its golden. I confirmed this empirically: a purpose-built
qv /= 0probe case passed with the bug present.Even after this PR, observing
crequires a 3D probe case — the 1D writes do not emit it at all. Anyone writing the #1707 regression test needs 3D plusqv /= 0.Golden impact
tests/5CAA4E68(1D exp_bubscreen)tests/FBB296DA(1D bubblescreen)tests/AE9A7D73(1D poly_bubscreen)Nine columns per row instead of one. The widening is the point.
Verification, and a flake to expect
An earlier full-suite run reported these same three cases failing with
Variable count didn't match. It did not reproduce: they pass in isolation, pass on two consecutive repeat runs, and pass in a clean full-suite run. It matches non-reproducible flakiness seen on unrelated branches in the same session (two chemistry cases, likewise green individually) and appears to be local contention at high-j. If CI shows it, re-run before investigating.Related work
qv /= 0.eos_staterefactor, which subsumes the bug: simulation probe sound speed omits qv from enthalpy, disagreeing with post-process #1707 code fix via the interface.Purpose in the series
Test-infrastructure prerequisite, not a feature.
#1707 is a defect in the probe sound speed that could not be given a regression test —
cis never the last column of probe output, and the packer kept only the last column. A case could exercise the defective code, emit visibly wrong output, and pass its golden. I confirmed that empirically before writing this.So this exists to make a class of regression testable at all. The widened goldens are the point, not a side effect: three cases went from validating one column to validating nine.
Note #1714 fixes the #1707 code path via the interface, so this is no longer strictly blocking that fix — but it remains the only way a probe regression becomes visible to the suite.
Working conventions and hazards (shared across this series)
Collected from the work that produced #1705, #1709, #1712, #1713, #1714, #1716. Every one of these cost real time or produced a wrong result before being caught.
Testing
A regression test that cannot fail is worse than no test. Always verify the negative: revert the fix, rebuild, confirm the case fails, restore. Two ways this silently broke here:
git stash push -- <file>has nothing to stash once the fix is committed, so the "reverted" run tests the fixed binary and reports a pass identical to a real one. Usegit checkout master -- <file>, rebuild, test, thengit checkout HEAD -- <file>.model_eqns = 3cannot detect a sound-speed defect, because the six-equation branch ofs_compute_speed_of_soundtouches neitherHnorqv.The golden packer discards data.
toolchain/mfc/packer/pack.pytreated every.datunderD/as<x> [<y> <z>] <value>and kept only the last column of each row. Probe and integral output are multi-column time series, so most columns were never compared (#1711, fixed in #1712). Before asserting that a golden covers something, check it is actually ingolden.txt.Case labels are load-bearing. The golden UUID is
crc32(sha1(str(trace)))— the label chain determines the directory name. Renaming a label renames the golden. Avoid!in labels (history expansion in interactive bash).Local suite runs are flaky at high
-j. Non-reproducible failures appeared on several unrelated branches at-j 12–16(chemistry cases, probe cases) that passed individually and in clean reruns. Re-run before investigating.Removing parameters or features
Deregistering a parameter breaks things that are not the source tree. Removing
pref/rhoreffrom the registry broke the entire suite becauseBASE_CFGintoolchain/mfc/test/case.pyset them for every case. Also checkfp_stability.py,params_tests/mutation_tests.py, and lint fixtures that use real parameter names as examples.Grep the generated artifacts, not just the sources. A stale
TYPED_DECLSentry naming a deleted type survived removal and did not break the build only because the parameter had also left every target's namelist vars, so it was never emitted. Checkgenerated_decls.fpp,generated_constants.fpp,SIM_GPU_DECL_VARS, and the MPI broadcast generators.Dead-local tell: after removing a block, a local with exactly one remaining occurrence in its file is almost certainly its own declaration. Two occurrences often means declaration plus a
private()entry.Fortran is case-insensitive. A local
pRefshadowed the module globalprefin the hardcoded-IC files; the read site was spelledprefand looked like a reference to the global. It is not. Confirm scope before concluding a global is live.GPU
A CPU test run cannot catch a missing
private(). It is a silent device race. Audit by hand or by script when adding per-cell state.Do not match
GPU_PARALLEL_LOOPnaively —END_GPU_PARALLEL_LOOPcontains the same substring and will register as a loop start, producing false positives. Exclude it explicitly.Derived-type components cannot have runtime extents.
dimension(num_fluids)in a type fails to compile outside case-optimized builds, wherenum_fluidsis aparameter.Benchmarking
Run
./mfc.sh benchin the foreground on an idle machine. Running it in the background while a pre-commit precheck ran at-j 12produced a bogus +50% regression on a case that executes none of the changed code. Baseline noise here is ±3.5%; run the baseline twice before trusting any delta, and sanity-check that the regressing cases actually execute the modified code.GitHub mechanics
--force-with-leaseneeds an explicit SHA (--force-with-lease=<branch>:<sha>) when the ref has not been fetched in the current clone; the bare form fails with "stale info".gh run view --log-failedcan miss the real output entirely. On the Frontier jobs the failing step carried only a non-zero exit while the actual test output lived in a separatePrint Logsstep that succeeded. Fetch the full log.gh run rerun <id> --failedrefuses while the workflow is still running; retry later.file INSTALL cannot set modification time ... No such file or directory, exit 143). Check for a real error before assuming a code fault.