Skip to content

Load-balance, active-box windowing, block-structured AMR - #1628

Open
sbryngelson wants to merge 795 commits into
MFlowCode:masterfrom
sbryngelson:up/mega
Open

Load-balance, active-box windowing, block-structured AMR#1628
sbryngelson wants to merge 795 commits into
MFlowCode:masterfrom
sbryngelson:up/mega

Conversation

@sbryngelson

@sbryngelson sbryngelson commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

An opt-in, default-off family of performance features and the measurement infrastructure they rest on. With all flags at their defaults the only touched production path is s_mpi_decompose_computational_domain, refactored through the new m_box module (byte-identical; covered by the existing suite).

  • Load-balance infrastructurem_box (partition arithmetic), m_load_weight/load_weight_wrt (per-cell load-weight field + imbalance metric), m_sfc_partition/sfc_partition_wrt (Morton-SFC predicted-imbalance diagnostic), m_load_balance/load_balance (weighted static decomposition at init; AMR-fine-work-aware), m_rank_timing/rank_time_wrt (per-rank compute-time diagnostic).
  • Active-box windowingm_active_box/active_box: restricts reconstruction/Riemann/RK windows to a light-cone-grown box around non-ambient flow; strict-subset golden-tested.
  • Hybrid reconstruction/flux sensorshybrid_weno and hybrid_riemann (+hybrid_weno_eps, hybrid_smooth_flux): linear-optimal weights / central-or-Rusanov flux in smooth cells, full WENO/HLLC at flagged discontinuities (Jameson sensor, stencil-dilated, per-level under AMR).
  • Block-structured AMRm_amr + m_amr_registers: two-level 2:1 refined block hierarchy; conservative restriction and conservative-linear prolongation with physics-specific closures; per-stage flux registers with Berger–Colella refluxing; Berger–Rigoutsos multi-block dynamic regrid; optional dt/2 subcycling; multi-rank (single-owner blocks assigned by Morton-SFC work balancing at each regrid, with migration; blocks may span rank seams via P2P coarse↔fine gather/scatter; same-level seam halo; distributed registers); restart (both IO modes, regridded-layout persistence); AMR-aware post-processing (fine blocks visualizable as Silo overlay domains); GPU-resident fine level on both OpenACC and OpenMP offload.

Full algorithm and user documentation: docs/documentation/amr.md (support matrix enforced at runtime by the checker — unsupported combinations abort with named messages, never silently).

AMR physics support matrix (abridged; authoritative table in amr.md)

Supported and golden-tested: single- and multi-fluid (5-eq, mpp_lim) · 6-eq with per-block pressure relaxation · viscous (refluxed) · phase change (relax) · chemistry incl. species diffusion · Euler–Euler bubbles (polytropic/non-polytropic, mono/polydisperse, QBMM incl. non-polytropic with per-block pb/mv side-state; dynamic regrid + subcycle) · acoustic sources (coarse-grid support with regrid exclusion) · immersed boundaries (multi-body, static or prescribed-motion, incl. dynamic regrid with body-containment expansion and per-substage guards) · 2D axisymmetric (per-block WENO-coefficient recompute) · stretched grids (exact parent-bisection ghost coordinates + per-swap coefficient recompute) · hybrid WENO/Riemann sensors (per-level) · Lagrangian bubbles (cloud excluded from blocks; two-way coupling on the coarse grid; regrid clips around the moving cloud) · active_box (blocks contained in the growing window; agrees with plain AMR to ~1e-14) · IGR (restriction-only coupling: fine sigma solve seeded/Dirichlet-bounded by the coarse solve; documented truncation-order seam, exact free-stream) · 1D MHD/RMHD (div(B)=0 by construction in 1D; HLL and HLLD, incl. relativistic).

Gated with named aborts (documented rationale): surface tension (seam force imbalance is structural — three fixes attempted and diagnosed in amr.md) · 2D/3D MHD (attempted and measured: the c/f seam is a continuous O(1) div(B) source GLM cleaning cannot remove — needs constrained-transport-class B prolongation/reflux) · hyperelasticity · 3D cylindrical (global azimuthal filter) · force-driven IB (moving_ibm=2) · STL bodies · Riemann-extrapolation BCs (bc=-4) · amr_subcycle under IGR · stretched grids with Lagrangian/IB-regrid (uniform-spacing index conversions).

Validation evidence

  • Conservation: reflux-exact (~1e-15 defects) across single/multi-fluid, viscous, bubbles, chemistry, phase change, through subcycled + regridding advances.
  • Free-stream preservation: exact (0.0) with subcycling + regrid armed, including 3D, IGR, and Lagrangian configs.
  • Decomposition exactness: np=1 == np=2 element-exact with blocks spanning rank seams (fields byte-identical incl. chemistry species).
  • Methodology: every feature validated by AMR-vs-no-AMR reference comparison with a control experiment calibrating the expected error scale, plus mechanism evidence from run logs (regrid counts, box coordinates, ghost-point counts) — a golden that does not mechanically exercise its path is treated as no coverage. Liveness goldens for the hybrid sensors are deliberately sized so a silently dead sensor fails them (~5e-4 signal vs 5e-5 tolerance) — they caught a real dead-sensor bug on Cray GPU during CI hardening.
  • GPU: full test suite with post-process on 2×V100 — OpenACC 644/644 executed pass, OpenMP offload 643/644 (one slurm launch flake). Speed spot-check (800² 2D blast, 2 ranks): AMR + dynamic regrid costs +3% wall over no-AMR at these sizes; ACC ≈ 12% faster than OMP; acc/omp solutions bitwise identical.
  • CI: one fully green 85-lane matrix on record (all GitHub-hosted lanes: gnu/intel × debug/no-debug/reldebug × double/single/mixed, macos, and the NVHPC 23.11–26.3 compat matrix). Fixes landed during hardening were each reproduced locally in the exact failing configuration first (intel FP drift, single-precision tolerance classification, gfortran bounds-check OOB, OpenACC present-table mapping ×3).

Known issues (all non-gating or in progress)

  • Frontier/Cray, in progress: two device-mapping issues in the IB machinery under CCE (plain-IBM descriptor corruption on gpu-omp; a present-table crash in the AMR-IB swap on gpu-acc). Being fixed with direct Cray iteration; the CCE bench/case-opt lane failures share the same root.
  • NVHPC 24.1/24.3 compat lanes (informational, continue-on-error): an intermittent post-detected NaN on the two Lagrangian+AMR goldens. Exhaustively unreproducible off GitHub's runners — the exact failing stack (NVHPC 24.3 SDK, -tp=px -Kieee, HPC-X MPI, and the CI docker image itself under apptainer) passes elsewhere, as do native/zen2 builds; 24.5+ green. Documented at the golden definitions.
  • Accuracy postures documented rather than hidden: multi-fluid alpha-sum deviation bounded ~5.7e-3 at historical block faces during shock crossing (non-growing); viscous prolongation-ghost seam ~1e-6 np-dependence; IGR seam conservation is truncation-order (no reflux capture from the fused IGR kernels — exact-conservation capture is noted future work).

Review guide

The commit history is arc-ordered (active-box → load-weight → SFC → weighted decomposition → rank timing → hybrid → m_box → AMR rungs → physics envelope → CI/GPU hardening); reviewing by arc is much easier than by file. The AMR arc builds stepwise: static hierarchy → restriction/prolongation → fine advance → refluxing → regrid → subcycling → multi-rank → GPU → each physics rung with its own validation. Commit messages carry the validation evidence for their change (measured defects, golden UUIDs, repro details for CI fixes).

All parameters ship default-off with case_validator entries, runtime checker gates, and case.md/amr.md documentation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an opt-in (“default-off”) family of performance/diagnostic features (load-weight and SFC partition diagnostics, weighted init-time decomposition, rank timing), plus major simulation capabilities (active-box RHS windowing and block-structured AMR) and corresponding post-processing support and documentation/validation updates.

Changes:

  • Adds new runtime parameters and toolchain metadata/validation hooks for the experimental performance/AMR feature family.
  • Extends the simulation code with new modules for active-box restriction, load-weight diagnostics, SFC partition reporting, rank timing, and AMR integration points (including restart/output plumbing).
  • Updates post_process to read/write AMR fine-block overlays and adds/updates golden metadata plus documentation/indexing.

Reviewed changes

Copilot reviewed 82 out of 94 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
toolchain/mfc/params/descriptions.py Adds user-facing descriptions for new experimental/performance parameters.
toolchain/mfc/params/definitions.py Registers new parameters (AMR, hybrid sensors, load-balance diagnostics) and target applicability.
toolchain/mfc/lint_docs.py Treats new validator checks as non-physics doc checks.
tests/F980C769/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/ECABA006/golden-metadata.txt Adds golden metadata for active-box test coverage.
tests/DD4CD8F3/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/CC4213FD/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/BD21A5C0/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/BCBA6E74/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/ACE05393/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/987D9025/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/852CCB81/golden-metadata.txt Adds golden metadata for AMR-related golden tests.
tests/65C375B4/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/4DADE04B/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/454C565F/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/3A474BEE/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/2FC423D3/golden-metadata.txt Adds golden metadata for a new/updated test.
tests/13945217/golden-metadata.txt Adds golden metadata for moving-IB under AMR test coverage.
src/simulation/m_viscous.fpp Clamps FD coefficient indexing to avoid ghost-region coefficient OOB in IB drag gradient evaluation.
src/simulation/m_time_steppers.fpp Integrates active-box bounds into RK update loops and interleaves AMR fine-stage/subcycle operations.
src/simulation/m_start_up.fpp Wires up new modules (rank timing, active-box, load-weight, SFC partition, AMR) into init/timestep/finalize and restart I/O.
src/simulation/m_sfc_partition.fpp Adds analysis-only SFC tiling + weighted partition prediction and reporting.
src/simulation/m_rank_timing.fpp Adds per-rank wall-time imbalance measurement helpers and reporting.
src/simulation/m_load_weight.fpp Adds per-cell load-weight field construction and rank-level imbalance reporting.
src/simulation/m_hypoelastic.fpp Refactors FD coefficient setup into a callable update routine (supporting AMR grid swaps).
src/simulation/m_global_parameters.fpp Adds AMR working-state mirrors and slot selection helper plus defaults for new parameters.
src/simulation/m_data_output.fpp Adds output/report hooks for load-weight, SFC partition, and rank-time diagnostics.
src/simulation/m_checker.fpp Adds input validation/prohibits for active-box, hybrid sensors, load-balance, and AMR configurations.
src/simulation/m_active_box.fpp Adds active-box initialization/growth and debug envelope checking.
src/simulation/m_acoustic_src.fpp Adds AMR-aware handling of acoustic source support (bounding boxes and overlap abort).
src/post_process/m_start_up.fpp Calls AMR fine-data reader and AMR overlay writer when amr is enabled.
src/post_process/m_global_parameters.fpp Adds default-off amr flag for post_process overlay behavior.
src/post_process/m_data_output.fpp Implements AMR fine-block overlay mesh/variables output (Silo/binary) and multimesh registration.
src/common/m_phase_change.fpp Exposes per-cell Newton iteration count and threads it through relaxation to support load-weighting.
src/common/m_global_parameters_common.fpp Adjusts start_idx lifecycle/allocation and makes load_weight_wrt visible to GPU macros.
src/common/m_derived_types.fpp Introduces a simple t_box type used by new partitioning infrastructure.
src/common/m_box.fpp Adds box/partition arithmetic helpers (equal/weighted splits, box-from-splits).
src/common/m_boundary_common.fpp Skips BC buffer population during AMR fine advance to rely on coarse-driven ghost fill.
docs/module_categories.json Registers new modules under documentation categories.
docs/documentation/readme.md Adds AMR section link to the documentation index.
.typos.toml Adds project-specific abbreviations to the spelling allowlist.

Comment thread src/common/m_phase_change.fpp Outdated
D = ((gs_min(lp) - 1.0_wp)*cvs(lp))/((gs_min(vp) - 1.0_wp)*cvs(vp))

#ifdef MFC_SIMULATION
if (relax .and. load_weight_wrt) then
Comment thread src/simulation/m_sfc_partition.fpp
Comment thread src/simulation/m_sfc_partition.fpp Outdated
Comment thread src/simulation/m_rank_timing.fpp
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.93163% with 795 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.39%. Comparing base (41df185) to head (7a5fbd1).
⚠️ Report is 6 commits behind head on master.

Files with missing lines Patch % Lines
src/simulation/m_amr_registers.fpp 66.32% 95 Missing and 102 partials ⚠️
src/post_process/m_data_input.f90 51.87% 76 Missing and 14 partials ⚠️
src/simulation/m_amr_restart.fpp 61.18% 57 Missing and 28 partials ⚠️
src/simulation/m_load_balance.fpp 46.80% 42 Missing and 8 partials ⚠️
src/simulation/m_rhs.fpp 71.89% 33 Missing and 10 partials ⚠️
src/simulation/m_ibm.fpp 71.54% 19 Missing and 16 partials ⚠️
src/simulation/m_amr_xchg_audit.fpp 54.16% 29 Missing and 4 partials ⚠️
src/simulation/m_load_weight.fpp 21.05% 23 Missing and 7 partials ⚠️
src/simulation/m_sfc_partition.fpp 62.50% 23 Missing and 7 partials ⚠️
src/simulation/m_data_output.fpp 32.25% 16 Missing and 5 partials ⚠️
... and 27 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1628      +/-   ##
==========================================
+ Coverage   61.67%   62.39%   +0.72%     
==========================================
  Files          84       96      +12     
  Lines       21619    29068    +7449     
  Branches     3196     4778    +1582     
==========================================
+ Hits        13334    18138    +4804     
- Misses       6093     7753    +1660     
- Partials     2192     3177     +985     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sbryngelson

Copy link
Copy Markdown
Member Author

Upstream latent gap found during the MHD+AMR investigation (independent of this PR): m_riemann_solver_hlld.fpp contains no GLM terms, so hyper_cleaning = T with riemann_solver = 4 (HLLD) is silently inert — psi never evolves and no cleaning occurs. The existing hyper_cleaning golden uses HLL, which does carry the GLM coupling, so CI never sees this. Worth either adding GLM terms to HLLD or a validator prohibit on the combination.

Also for the record: MHD+AMR was attempted and re-gated on measured evidence rather than assumption — the coarse/fine seam is a continuous O(1) div(B) source that cleaning spreads but cannot remove (details in the amr.md support matrix row and commit ac203b1).

@sbryngelson sbryngelson changed the title Experimental performance family (default-off): load-balance infrastructure, active-box windowing, block-structured AMR, hybrid WENO/Riemann sensors Load-balance, active-box windowing, block-structured AMR Jul 21, 2026
@sbryngelson

Copy link
Copy Markdown
Member Author

active_box is single-rank-only — make it MPI-real, or defer it?

Raising this as a self-review point. active_box disables itself with a warning at num_procs > 1 and falls back to full-domain compute. Since production runs here are essentially always multi-rank, that makes the optimization a no-op (plus a warning line and some maintenance surface) in exactly the configurations that matter. We should decide what to do with it.

Why it's single-rank today. The active box is a single global, coarse-cell-indexed region. On one rank you just clip the RHS loops (convert / reconstruct / Riemann) to it. Under domain decomposition the geometry is easy — each rank intersects the global box with its subdomain and freezes the exterior — but it creates a load-imbalance problem: if the disturbance occupies only a few ranks' territory, those ranks do all the work while the rest idle, and the slowest rank sets wall-clock, so you save nothing. The current code sidesteps this by disabling rather than solving it.

The enabling pieces are already in this PR. A multi-rank active box needs (a) per-rank clipping of the box (straightforward), and (b) a way to keep otherwise-idle ranks busy — i.e. redistribute the domain so the active region spreads across ranks. That second part is exactly what this PR's load-balance arc (m_load_balance / m_sfc_partition / m_load_weight) provides, and the active box already produces the natural work-weight signal (active vs. frozen cells) that a rebalancer would consume. The two features are built to compose — active box marks where the work is, load-balance moves ranks to it — they're just not wired together yet.

Options:

  1. Make it MPI-real. Per-rank box clipping + feed the active/frozen cell count into the load-weight so load_balance redistributes toward the active region. This is the version that actually helps at scale, and it's the natural payoff of shipping the load-balance infrastructure in the same PR.
  2. Defer / gate it. Drop active_box from this PR (or keep it behind an explicit experimental flag with the single-rank limitation documented up front) and keep the performance family focused on AMR, which is already multi-rank-capable via the P2P device-path gather/scatter. Re-introduce active box once the load-balance integration is designed.

Leaning toward (1) if there's appetite, since the enabling infra is right here; otherwise (2) keeps the PR's surface honest — a default-off single-node optimization advertised in an exascale code invites confusion. At minimum, the docs should state plainly that it is single-rank-only today.

Fixed per-rank work (fine_work imbalance 1.004): per-GCD peak VRAM
49.9 GiB (np=1) -> 56.4 (np=2) -> device OOM (np=4 and np=8) - about
6.5 GiB of replicated per-global-entity device memory per np doubling,
~90 MB per global box per rank. freg/creg are confirmed global-indexed
but only the ~1 GiB class; the remainder is unattributed and must be
found by code audit before pricing any S-track increment. W8 added to
the invariants: the device-memory ceiling, not wire bytes, is what kills
weak scaling first (np=4 on this node).

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
Triple-sourced (code audit, VRAM traces, exact per-slot arithmetic): at
np>=2 a rank's owned slots are a shifting SFC window of the GLOBAL index
space plus received migration slots, so the never-decrementing local-index
high-water ratchets the store capacity at 210.6 MiB per slot while the
compaction gate (cap > 3*nlive) never fires. np=1 traces sit flat from
startup - a single-rank window structurally cannot exhibit the term, which
is why a month of np-invariant-window measurements never saw it. Corrects
the previous revision's freg/creg guess (np-delta ~90 MiB only). Promotes
the already-designed fix - device-side store remap + local-index
derivation (cap == live at every reconcile) - to the first S-track
increment: it is the np>=4 unblocker. Also confirmed: migration spack/
rpack are host-side (per-block bytes x global block count), I4's item.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…ompletes)

The store-capacity ratchet ran over GLOBAL slot indices: at np>=2 a rank's owned set is a shifting SFC window plus received migration slots, so amr_loc_n climbed run-long and the S0 weak-scaling sweep device-OOMed at np=4 at fixed per-rank work. Four mechanisms, each forced by a measurement: (1) in-place index re-densification at every reconcile (s_amr_st_move_slot, ascending-source order, no realloc) - np=2 and np=4 VRAM now plateau flat with live 72/rank at both; (2) growth increments capped at 16 slots (the proportional +25% transient was itself store-scaled and tipped a 59.5 GiB card over 64); (3) early-free of consumed old slots during the rebuild (last_use per old block; np=4 plateau 57.3 -> 51.9 GiB); (4) stash-only replica slots (received old blocks only touch their amr_stor_st half - full slots doubled the migration storm's cost). Adds the [amr-cap] invariant line (live/cap per reconcile, rank_time_wrt-gated). The full device-side remake was built first and refuted: its staging transient OOMed init, and dropping growth's device->host round trip broke the rebuild carry-forward's host reads - both churn goldens caught the NaN. Gates: 27DEC5B6 + D127EC91 pass, AMR subset 67/67, S0 np=2 rc=0 (peak 55.3 GiB), np=4 rc=0 (peak 63.6 GiB - margin-thin; pool q_prim/rhs before growing the operating point).

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…re-sequences

Action plan: the first complete weak-scaling pair (post-fix S0 table) - memory weak scaling holds (live 72/rank, flat VRAM at np=2 and np=4) and TIME is the measured blocker (wall 2.59x per np-doubling at fixed per-rank work, split unmeasured -> phase-diff the np=2/np=4 pair before building anything); W4/S2 baselines now two-point curves (ntag 393.6 -> 787.3 MiB/rank/regrid). Ladder consequences: P1 q_prim/rhs pooling promoted to next memory lever (np=4 margin 0.4 GiB), I1 unchanged but gains two assertable invariants (stash-only replicas, [amr-cap] flatness), np=8 arm and np=1 re-run owed, churn-goldens-first is policy for slot/stash/exchange changes. Endstate: W8 invariant HOLDING, W4 row carries the measured doubling, ladder row 0.4 updated (stale freg/creg guess removed). Implementation reference sec. 5 rewritten to the landed lifecycle (stash-only flavor + upgrade, capped growth, in-place re-densification with the ascending-order safety argument, [amr-cap]); the growth host round trip is documented as a load-bearing host-coherence CONTRACT (the rebuild carry-forward's host reads - the refuted device-side remake NaN'd both churn goldens through it); sec. 9 free-before-allocate note updated to the landed last_use early-free; sec. 11.3 weaknesses 1-2 updated with the 2026-08-21 outcomes.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…egistered M4 rules

M3 (attribution of the np=2 -> np=4 time gap) turned out to cost zero runs: the passing gate logs carried full phase budgets. The +413 s delta splits into three families: wait/skew 38% (reflux +105 s pure wait, imb 1.81 - the sink pattern returns at np=4; coarse +44 s imb 2.85), rhs per-call 27% (ms/call 17.3 -> 31.3 in the mean, mechanism open), regrid scaling 27% (migration +43, build +38 incl. rb:gath +17). The plan pre-registers three discriminators with decision rules before any building: M4a same-card vs cross-card GCD pinning for the rhs doubling (no rhs work until it lands), M4b per-rank work imbalance for the skew source (balancer gated on imb > 1.15), M4c np=8 + np=1 table completion (np=8 OOM promotes P1 pooling to immediate). Increment order: I1 validator now (M4-independent), P1 q_prim/rhs pooling with the carry-forward device conversion in the same increment, then one M4-directed front. Standing do-not list: no reflux optimization, no operating-point growth pre-pooling, no P2 re-litigation, no multi-node before clean np=8, churn-goldens-first, watch CI on the new device paths.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…lus the L0 sites

New module m_amr_xchg_audit: a 30-id site registry over the 35 physical MPI call sites (fypp twins share ids), always-cheap per-site message/word/tag-range counters recorded at the MPI call itself, a per-family global send==recv conservation assert at finalize (collective, before the amr early-return since L0 families fire with amr=F), and a [amr-xa] report under rank_time_wrt. Also: a reconcile-time assert that no stash-only replica slot survives reconcile (a survivor would reach the solver with no q_prim/rhs). This is the I1a half of the exchange validator (amr_plan_based_exchange.md, split recorded there); I1b adds MFC_DEBUG per-xfer identity headers and the destination-coverage tiling assert, gated by a seeded-bug tripwire. Gate: churn goldens 27DEC5B6/D127EC91 pass, AMR subset 67/67 (in 584 s with the newly engaged test GPU pool, -j 8 --gpus 0-7, vs ~20 min serialized on one GCD), no behavior change.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…d allocator state

Three GCD arrangements (baseline / NUMA-spread / one-per-MCM) gave identical walls (593/605/581 s), the same rank-2 straggler (353/357/358 s of rhs), and per-rank VRAM fingerprints that reproduce on whatever GCD the rank lands on - no sick silicon, no NUMA effect, no MCM pairing. The cliff is bounded: 62.5 GiB fast vs 63.0-63.6 slow (97.7 vs 98.4% device-full). Standing hypothesis: cumulative per-slot q_prim/rhs alloc/free churn ratchets the OpenMP runtime's retained device pool to the ceiling (probe m4mem.sh pre-registered; knobs verified present in the runtime binary). np=1 re-run matches history exactly; np=8 fails with all eight cards at 62.3-63.7 GiB, firing the pre-registered rule: P1 q_prim/rhs pooling is promoted to immediate. The uniform +36% rhs floor at np=4 survives every tested hypothesis and is recorded as a named open question.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
… by the consumer audit

m4mem 40-step probe: with LIBOMPTARGET_MEMORY_MANAGER_THRESHOLD=0 the rank-2 straggler vanishes (358.6 -> 152.3 s rhs), the uniform +36% floor vanishes with it (all ranks ~18.5 ms/call, the np=1/np=2 level), the reflux shadow collapses (176 -> 15 s), and wall drops 621.6 -> 405.4 s: the np-doubling factor falls 2.59x -> ~1.59x from one env var. Cross-node reproduction exact. np=8 still fails with the knob (peaks to 63.4) - retention and live footprint are separate terms, so P1 pooling stands per its rule. The pooling increment is redesigned by the consumer-lifetime audit: batch-shaped shared scratch aligned with P2 (not single-block), per-slot rhs kept for L0 tiles only, conditional q_prim; the m_rhs.fpp:772 stale-psi read is unreachable for AMR (checker chain) and filed as an upstream candidate. LIBOMPTARGET_INFO probe closed the micro-model: ~4 descriptor-class (16-120 B) device allocs per launch, concentrated in hllc/weno/conversion regions, 64:1 present-table hit ratio - the allocator-pressure mechanism is measured at every link. Re-audit cadence section added per user directive.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
The fused per-block fine advance leaves no cross-block q_prim/rhs lifetime,
so fine slots share one slot-shaped scratch pair instead of carrying
~2x105 MiB per live slot - the np>=8 live-footprint blocker and the churn
that fed the libomptarget retention plateau. L0 tiles keep per-slot rhs
(all owned tiles' rhs coexist across the MPI reflux point) and per-slot
q_prim exactly when the m_rhs copy-out gate writes it. Stage routines take
the target arrays as dummies; the full-vs-stash discriminator moves from
allocated(q_prim) to allocated(x_cb). Scratch is allocated on every rank at
the one point per mode where mbuf* are final (module init, or after the
tile mbuf union). AMR subset 67/67 incl. churn goldens 27DEC5B6/D127EC91.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…ds through np=8), np=8 phase budget, audit gaps G-A/G-B, increment 3=rb:gath 4=T1

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…; both families in one chunked plan

pg:recv (the level>=2 parent gather's blocking per-box MPI_RECV, the
unconverted half of R1) is 99.2 s at S0 np=8 vs rb:wait 58.9 s; at np=4 it
is essentially all of rb:gath. The old level-1-only scope was
matched-point-specific. Both families now go through one chunked
plan-then-execute; increment 1 validates the plan against the I1a XA
counters before any batching.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
… path against it (gather-batching step 1)

s_amr_build_gather_plan computes, before the rebuild box loop, every level-1
box's contributor ranks and message sizes and every level>=2 box's parent
source and size, from the same replicated caches the per-box path reads.
Five always-on asserts in the per-box gathers verify the inline derivation
matches the plan box by box; exchange behavior is unchanged. Step 2's
chunked exchange may trust the plan only because these asserts prove it
reproduces today's message set. Subset 67/67 asserts-clean; two seeded
tripwires (level-1 size, parent size) each aborted at the first rebuild.

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…th reviewers; amended to parent-position-split sends plus four bindings

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…it per owned box (gather-batching step 2)

Both families through one chunked plan-then-execute (amr_regrid_gather_batching.md,
amended form): phase A pre-posts every owned box's recvs for the chunk from the plan
(level-1 contributor slices + split level>=2 parent patches, tag = slot, box-order
contiguous request runs); phase B issues the plan-driven sends, deferring a level>=2
send to the child's consume position when the parent shares the chunk (its store is
unbuilt until its own consume - the D1 defect both reviewers found, now also guarded
by an always-on parents-first ASSERT); phase C consumes boxes in order with one
WAITALL per owned box. The parent-field gathers take the child block explicitly
(cblk) instead of reading amr_cur, since phase B runs before the consume geometry.

Validated: AMR subset 67/67 goldens green; XA exchange report line-for-line
identical to the per-box path on the S0-style np=4 tripwire (F1 312 msgs /
468087984 words, F2 1448 / 1400815296, F4-F7 untouched).

Claude-Session: https://claude.ai/code/session_01N8xV1fowU5LmyfxCivNLDH
…lement; the alternatives reproduce a map already rejected
…r stage over the plan's flat transfer list (amr_device_pack)
…se-grid halo is 39% of growth, batched advance saves 0.60 s/step, our geo-mean equals the bar rather than beating it
…the batched advance worth 0.38; the uniform denominator re-measured at 240 steps
…gather is 0.224 s/step of non-wait work -- 25% wait, not 12%, after review folded pgather into its own phase bracket
… GB of output byte-identical, the cost removed is per-map not per-launch, and three measurement failures including a retraction of ledger 74's invented bracket-inflation caveat
…receives per box-peer run) and flag the exactly-tiled pool assumption at the fused copyout
…ff amr_device_pack flag (-0.14 s/step, 8.1x fewer device maps, 12 GB of field output byte-identical)
… np8 spread was one outlier, settled by the 2-node follow-on; k004-004 and k004-005 are sick
…overs 7 percent of what the fused packs recover, so the cost is the map machinery not the malloc, and ledger 75's hedge is corrected
…istry-AMR tests needed the bench fftw on LD_LIBRARY_PATH, not a code fix
… session -- it explains the drift, the unusable step column and probably the np16 hang, and the wait-free gather column was immune
…was too weak to see the degradation it exists to catch
…ing has ever set, whose dead brackets owned instrument rows that could never record (-56 LOC, all 70 AMR tests green)
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Lines of Code

File Lines Diff
src/simulation/m_amr.fpp 7696 +7696
src/simulation/m_amr_regrid.fpp 1966 +1966
src/simulation/m_amr_registers.fpp 1041 +1041
src/simulation/m_amr_restart.fpp 383 +383
src/post_process/m_data_input.f90 718 +310
src/simulation/m_phase_timing.fpp 231 +231
src/simulation/m_amr_xchg_audit.fpp 210 +210
src/simulation/m_ibm.fpp 1451 +200
src/simulation/m_sfc_partition.fpp 169 +169
src/simulation/m_time_steppers.fpp 992 +165
src/simulation/m_load_balance.fpp 164 +164
src/post_process/m_data_output.fpp 1328 +146
src/simulation/m_load_weight.fpp 121 +121
src/simulation/m_active_box.fpp 118 +118
src/simulation/m_global_parameters.fpp 832 +84
src/common/m_box.fpp 71 +71
src/simulation/m_rhs.fpp 2020 +69
src/simulation/m_start_up.fpp 1304 +59
src/simulation/m_rank_timing.fpp 50 +50
src/common/m_mpi_common.fpp 1522 +48
src/simulation/m_data_output.fpp 1356 +46
src/simulation/m_acoustic_src.fpp 536 +36
src/simulation/m_riemann_solver_lf.fpp 548 +31
src/simulation/m_riemann_solvers.fpp 139 +27
src/common/m_phase_change.fpp 306 +22
src/common/include/2dHardcodedIC.fpp 453 +21
src/simulation/m_riemann_solver_hllc.fpp 1288 +20
src/simulation/m_bubbles_EL.fpp 1648 +17
src/simulation/m_weno.fpp 1357 +17
src/simulation/m_checker.fpp 84 +14
src/common/include/macros.fpp 141 +13
src/post_process/p_main.fpp 61 +7
src/simulation/m_hypoelastic.fpp 598 +7
src/simulation/m_igr.fpp 2295 +7
src/common/m_constants.fpp 93 +6
src/common/m_derived_types.fpp 444 +4
src/simulation/m_riemann_solver_hll.fpp 599 +4
src/common/include/3dHardcodedIC.fpp 243 +3
src/simulation/m_surface_tension.fpp 266 +3
src/post_process/m_start_up.fpp 768 +2
src/simulation/m_cbc.fpp 1112 -2
src/common/m_boundary_common.fpp 449 +1
src/post_process/m_global_parameters.fpp 359 +1
src/simulation/m_bubbles_EE.fpp 304 +1
src/simulation/m_riemann_solver_hlld.fpp 191 -1
src/simulation/m_thinc.fpp 313 +1
Directory Lines Diff
common 10135 +189
simulation 40622 +12954
post_process 3793 +466
total 59047 +13609

…er-dispatch not per-byte, a quarter to a third of the gap -- two drafts withdrawn on review, the clustering knobs are inert, the cap-32 work saving belonged to a mesh nobody stepped
… gather consume is bit-identical and saves nothing; the census shows the pooled kernels are not slower, the per-block cost in the consume is non-kernel time; parked on task12/batched-gather
…pk/fill) around the per-block gather consume in both waves, same clock as [mpiwait]
…_OMP_PRESENT_ALLOCATABLE); m_amr.fpp opts in

Without it amdflang maps every allocatable array of derived type a kernel touches on every launch, walking and re-attaching
each component (~0.3 ms per launch for a 10-component array, linear in components; a per-element mapper generated for the
type taxes every kernel in that compilation unit). m_amr opts in after an audit of its 95 kernels (bare module allocatables
named: amr_cg, amr_cons_br, both allocated before first use; conditional ones are GPU_DECLARE'd). Not global: a kernel
naming an unallocated allocatable variable or component aborts under present (m_variables_conversion's q_T_sf%sf).
Step-loop wall -8% at cap 64, -18% at cap 32; restart files byte-identical; 70 AMR goldens; np=2 oracle. Ledger 82.
Gated as 00a7c56/dede9e33 on origin/task13/host-profile (squashed here: the intermediate global form does not build).
…f allocatable derived-type arrays; per-file defaultmap(present:allocatable) opt-in for m_amr, -8%/-18% wall, bit-identical, gated
… in every form, null components do not; m_amr re-audited under the corrected rule (comments updated)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants