Load-balance, active-box windowing, block-structured AMR - #1628
Load-balance, active-box windowing, block-structured AMR#1628sbryngelson wants to merge 532 commits into
Conversation
There was a problem hiding this comment.
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. |
| 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 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1628 +/- ##
==========================================
+ Coverage 60.77% 61.98% +1.21%
==========================================
Files 83 93 +10
Lines 20872 25637 +4765
Branches 3101 4206 +1105
==========================================
+ Hits 12685 15892 +3207
- Misses 6121 6993 +872
- Partials 2066 2752 +686 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Upstream latent gap found during the MHD+AMR investigation (independent of this PR): 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). |
…blocks) launches -> O(1) per category; byte-identical)
… (fm host array referenced in GPU_PARALLEL_LOOP bounds) s_amr_fine_slice (the c8b1006 device-packed seam halo) used the host local array fm(TB)/fm(TA) as GPU_PARALLEL_LOOP bounds, so nvfortran/Cray demanded fm PRESENT on device -> runtime 'FATAL ERROR: data in PRESENT clause was not found on device: name=fm(2:)'. AMD flang tolerated it; NVHPC (Phoenix) and CCE (Frontier) gpu-acc lanes aborted every np>=2 AMR test. Use the scalars na-1/nb-1 (na=fm(TA)+1 already computed on host) as the bounds - byte-identical, no host-array reference in the device region. Validated: 6 np=2 AMR goldens (244B1E42 F57C3A5B ADA042A2 EF58E377 4644A339 B7704247) pass on 2x H200 gpu-acc.
… (fm host array referenced in GPU_PARALLEL_LOOP bounds) s_amr_fine_slice (the c8b1006 device-packed seam halo) used the host local array fm(TB)/fm(TA) as GPU_PARALLEL_LOOP bounds, so nvfortran/Cray demanded fm PRESENT on device -> runtime 'FATAL ERROR: data in PRESENT clause was not found on device: name=fm(2:)'. AMD flang tolerated it; NVHPC (Phoenix) and CCE (Frontier) gpu-acc lanes aborted every np>=2 AMR test. Use the scalars na-1/nb-1 (na=fm(TA)+1 already computed on host) as the bounds - byte-identical, no host-array reference in the device region. Validated: 6 np=2 AMR goldens (244B1E42 F57C3A5B ADA042A2 EF58E377 4644A339 B7704247) pass on 2x H200 gpu-acc.
…d) dense tag field (per-rank memory now O(#tagged), byte-identical boxes)
…~1290^3 (2048^3+ target) The sparse-clustering encode gi+(mg+1)*(gj+(ng+1)*gk) in default integer overflows once (m_glb+1)*(n_glb+1)*(p_glb+1) >= 2^31 (~1290^3, and the 2048^3 this scaling work targets), silently corrupting decoded box coordinates. Widen locidx/allidx to integer(8) with int8 encode/decode and gather via MPI_INTEGER8; counts/displacements stay int32 (they count tagged cells, < 2^31). Byte-identical at tested grids (7 regrid/multilevel goldens pass).
…ustering compute O(nboxes*ntag) -> O(ntag*depth); byte-identical boxes)
Bumps the ffmt formatter pin in toolchain/pyproject.toml from 0.4.3 to 0.4.4 (fixes single-line-construct indent leak, sbryngelson/ffmt#5). Verified as a 0-change no-op on master formatting; Formatting/Lint Toolchain/Python 3.9-3.14 CI all green.
ffmt 0.4.4 (pinned by the prior commit, fixes sbryngelson/ffmt#5) corrects the pre-existing +4 over-indentation of s_amr_cluster/s_amr_regrid (a 0.3.x single-line 'do ...; end do' indent-leak that 0.4.3 tolerated as a buggy fixed point). Whitespace + doc-comment re-wrap only, zero logic change (git diff -w is comment-rewraps); m_amr.fpp and m_sfc_partition.fpp are the only two files affected. Unblocks clean formatting of subsequent edits.
…r level (was per parent-box; byte-identical)
…slice-copy The hybrid-WENO use_central branch set the central (linear) weights with a runtime-bounded whole-array slice assignment, omega(0:weno_num_stencils) = d_cbL/R(:,SV). On gfx90a OpenMP offload, amdflang lowers that array-slice copy through the Fortran runtime _FortranAAssign path, which spills the WENO kernel to scratch (17.9 KB/work-item on AFAR 23.1.0; AGPRs pinned, occupancy collapse) and runs it ~10x slower -- the register/scratch-spill class tracked in ROCm/llvm-project#2909 (upstream llvm/llvm-project#203890). hybrid_weno is a runtime parameter and AMD builds are non-case-optimized, so the branch is compiled even when hybrid_weno is off (the default). Every WENO case on the Frontier gpu-omp (amdflang 23.1.0) benchmark therefore paid the spill, appearing as a ~3-4x per-step slowdown vs master (which lacks the branch). Reconstruct directly from the central weights (weno3/5), or fill omega with an explicit scalar-store loop (weno7), keeping the copy off the _FortranAAssign path. Numerically identical: the non-hybrid else path is unchanged and the central path is algebraically the same. On AFAR 23.1.0 this drops WENO-kernel scratch 17920 B -> 92 B and restores PR per-step to master's level (0.37 -> 0.28 s). Cray, nvfortran, and newer amdflang (23.2.x) were unaffected. Refs: ROCm/llvm-project#2909
…s (serial path) The serial AMR restart reader assumed every block's fine extent was ref_ratio*(region_width)-1 (single level), so it rejected every level>=2 block as 'block fine extents disagree with the region (corrupt file)' -- multi-level (amr_max_level>=2) runs could not restart. A level-l block covers ref_ratio**l fine cells per L0 cell of its region, not ref_ratio (see the work-weight at m_amr.fpp:1173 and s_set_amr_fine_geometry:1297-1321). The parallel_io read path also sizes each block from the region before extents are read, so the level cannot be back-derived -- it must be stored. Fix (serial path): the writer stores amr_block_level(k) in each per-block header; the reader reads it, sets amr_block_level(k) before the owner/geometry rebuild (s_amr_assign_block_owners and s_set_amr_fine_geometry key off it to place L>=2 blocks under their parent), and generalizes the extent check to ref_ratio**level. New test AMR -> 1D -> multi-level restart (4AF96C49) restart_checks the static L2 hierarchy; validated on gpu-acc np=1 (fix passes; single-level AMR restart 21C71558, 1D restart 1A379909, multi-level advance 75AD6885 unchanged). parallel_io path left unchanged (still fails-closed on size mismatch, no regression) -- a clean second increment.
# Conflicts: # src/simulation/m_amr.fpp
… MPI code (PR #7 review C1+I1) C1 (Critical): the parallel_io (MPI-IO) AMR restart path stored no per-block level and hard-coded a level-1 fine extent (ref_ratio*width), so multi-level (amr_max_level>1) restart read every L>=2 block at the wrong resolution and mislaid all downstream block offsets -- caught only by a fragile total-size tripwire. Mirror the serial fix onto the MPI-IO path: the per-block header is now 7 ints (region box + refinement level), the reader sets amr_block_level(k) before the owner/geometry rebuild and sizes each block with ref_ratio**level, with a level-range guard. New restart_check test 'AMR -> 1D -> multi-level restart parallel_io np=2' (78314D65). Single-level MPI-IO restart (5EFB3277) and serial multi-level restart (4AF96C49) unchanged; validated np=2 gpu-acc. I1 (Important): delete dead code. s_mpi_sendrecv_amr_fine_halo (~150 lines + GPU pack/unpack kernels) had zero call sites under the whole-block ownership model that superseded the continuation-face halo, yet s_initialize_amr_mpi_buffers allocated its device buffers (amr_buff_send/recv) on every AMR run. Removed both routines, the buffers, the finalize dealloc, and the call site (max_f1/2/3 stay -- used for slot coordinate sizing); fixed the stale comment referencing the nonexistent s_mpi_bcast_amr_reflux_faces. Net -159 lines.
review Perf-2) s_amr_fine_fine_halo rescanned all O(nblocks^2) block pairs with f_amr_seam every RK stage (6x per fine step) to find same-level adjacent seams. Block topology (regions/levels/count) changes only at regrid/restart, so cache the (xb, yb, seam-dim) list once via s_amr_build_seam_pairs and iterate O(#seams) per stage. Rebuilt when amr_seam_pairs_dirty is set (regrid, restart, init) with a block-count tripwire as backup. The list preserves the original (xb, yb) nested-loop order on all ranks (replicated region metadata), so the paired MPI_SENDRECVs stay matched -> byte-identical. New pure helper f_amr_seam_dim shares the adjacency logic between builder and halo. Validated: full AMR suite 57/57 byte-identical, np=2 gpu-acc.
…bug print (PR #7 review)
…e load_balance debug behind MFC_DEBUG (PR #7 review)
…ratio + np-flexible restart (PR #7 review)
…phic callees it forced
… linear in region count
… the art, AMR captures ~11% of its potential
…d 6.6x per-region overhead
… loss for register-heavy kernels
…ies per launch vs 0.00 is
…ers directly s_finalize_riemann_solver copied flux_rsx_vf into flux_n and flux_gsrc_rsx_vf into flux_gsrc_n. Both copies were vestigial: the rsx buffers are already flat, natural-order (x, y, z, var) module arrays allocated ONCE for all three directions, and both sides of the copy carried an identical index expression in every norm_dir branch. Lifetimes matched too - flux_n(2) and flux_n(3) aliased flux_n(1), so it was already one shared buffer per sweep. Consumers now read the rsx buffers directly. Deletes 4 x sys_size full 3D field arrays (flux_gsrc_n allocated per direction, unaliased), the copy kernel, three deep scalar_field dummies from four solver signatures plus s_cbc and s_compute_qbmm_rhs, and two GPU_ENTER_DATA(attach=) users. Bounds are safe by construction: finalize only ever wrote the is1/is2/is3 window, which is inside the rsx buffers because the solvers write rsx over that same window - so no correct consumer could read outside it. flux_gsrc_n's zero-init moves to s_initialize_riemann_solvers_module; collapsing its per-direction storage is equivalent because finalize already copied the whole shared rsx buffer into each direction's array. s_amr_capture_creg_dense_batch gains a 'flat' selector because it serves both the advective capture (now flux_rsx_vf) and the viscous one (still flux_src_n). It must be a branch, not a merge: merge evaluates both arms, and flux_src_n allocates only mom..E, so touching it across the full advective band would dereference null %sf pointers. Its flux reads stay INSIDE the bclo/bchi guards - a slot activates when either face is owned, and the unowned face's index lies a block width outside this rank's subdomain, which flux_n's wide ghost bounds tolerated and flux_rsx_vf's (-1:m_alloc) does not. scalar_field%sf is real(stp) and flux_rsx_vf is real(wp), so this also removes a lossy wp->stp roundtrip under --mixed. Verified: 706 passed / 0 failed (including AMR 2D axisymmetric, the only flux_gsrc coverage), plus sc3dx_amr 400^3 at np=8 clean - the goldens are all 1D/2D and cannot see rank-boundary memory safety.
… is written Completes the flux-family removal begun in 724ef4f. flux_src_rsx_vf already held the advection-source band in the same flat (x, y, z, var) layout, so s_finalize_riemann_solver had nothing left to copy and is deleted outright; its viscous mom..E band moves to the same array, which retires flux_src_n. m_cbc, all four Riemann solvers, s_compute_advection_source_term and s_amr_capture_boundary_flux now take no scalar_field flux dummy at all - the AMR capture, the second-most-launched kernel in the code, reaches zero field dummies. The adv band ALIASES adv%beg when riemann_solver /= HLL, and %vf(j_adv) is read in the non-HLL branch too (m_rhs 1254/1330/1403), so every non-HLL band read is collapsed to adv%beg explicitly. Getting that wrong is a silent wrong answer, not a crash. m_chemistry keeps a dummy because it lives in src/common and so cannot use m_riemann_state; it takes a flat real(wp) array instead of a scalar_field, which is measurably cheaper per launch. Sizing: these are FULL-DOMAIN arrays, so an unused component is pure waste. flux_gsrc_rsx_vf is not allocated at all in Cartesian runs (every write in the solvers and both reads in m_rhs sit under cyl_coord, and grid_geometry == 3 implies cyl_coord); flux_src_rsx_vf starts at the band actually written; qL_prim/qR_prim allocate their momentum payload only when viscous, since s_get_viscous and s_compute_viscous_source_flux are the only readers. creg/freg now grow geometrically instead of being dimensioned to amr_max_blocks - that is a safety CAP, not a block count, and at 400^3 it was a 34x over-provision costing 1.41 MB of device memory per unused slot. Measured at 400^3 np=8: peak device memory 49.83 -> 36.48 GiB per GCD (-26.8%). Verified 706 passed / 0 failed plus sc3dx_amr 400^3 np=8 clean - goldens are all 1D/2D and cannot see rank-boundary memory safety.
s_amr_gather_coarse_patch is called once per BOX - 794 of them per rebuild at 400^3 - and the contributing side used a BLOCKING MPI_SEND, so every rank rendezvoused with each box's owner in lockstep. Profiling put this routine at 45% of regrid and ~25% of total runtime across its two call sites, which is also why the AMReX head-to-head measured our MPI at 7.6x the calls. Sends are now non-blocking, completed by one WAITALL after the rebuild loop instead of a rendezvous per box. The pool owns the buffers because MPI_ISEND requires them live until completion - the old code freed sbuf immediately after the blocking send, which is exactly what must not happen here - and drains every 64 pending sends to bound both pool memory and outstanding requests. Tags are the slot index, so they were already unique per box. Only the rebuild_slots call site changes semantics; the other six flush immediately and behave exactly as before, so the blast radius is one loop. The owner still posts IRECV and waits per box, which is what preserves patch-assembly order. Measured over 3 reps at 400^3 np=8: gather 10.43 -> 8.43 s (-19%), regrid total 23.14 -> 21.23 s (-8%). The remaining cost is owner-side (per-box IRECV/WAITALL, sys_size GPU_UPDATEs per box), which needs the fully batched exchange. Verified 706 passed / 0 failed, 101/101 AMR including 8 np=2 cases, and sc3dx_amr 400^3 np=8 clean - a mis-ordered non-blocking send corrupts the receiver's patch silently, so the multi-rank cases are the real gate.
Reverses the 2026-08-02 conclusion that batching is ruled out because the tax is per-dispatch argument mapping. That rested on temporal copy attribution, which credits a kernel with copies emitted by intervening data regions - it made capture_boundary_flux look like 0.0 copies/dispatch when it does 8.73, and capture_creg look like 33 when it does 2.67. Controlled 2-factor microbenchmark (amr-bench/scaling) varying launch count and entity count independently, R^2 1.0000 on every HSA-call fit: private/local fixed-size ARRAYS and assumed-shape dummies cost exactly 2.00 copies, ~27 HSA calls and ~31 us per entity per launch, independent of array size. Private scalars, module arrays used directly, explicit-shape dummies and BLOCK-scoped arrays cost zero. The per-launch floor is only 6.2 HSA calls. Also records the measured phase budget, the runtime-knob null (12 settings, two of them 1.8-3.2x slower), and that copy COUNT converts to wall while copy BYTES does not.
…n the AMR arm) A fixed-size private ARRAY in a target region costs 2 copies and ~27 HSA calls per launch on amdflang, because the compiler materialises a per-thread private copy; the entity is never read on entry, so the transfer is pure waste. map(alloc:) keeps the privatisation and drops the transfer. Measured in a controlled microbenchmark at 2.00 copies per entity, R^2 1.0000, independent of array size. hllc:1013 names its 23 private arrays, weno:1117 its 6. Only ARRAYS - naming a scalar would demote it from pass-by-value to a real device allocation and cost more than it saves. Measured 3D 256^3 np=8 restart at steady state, interleaved triplicates, settled tail: 13.357 -> 9.827 s/step, -26.4% (1.36x), ranges non-overlapping. Copy count fell exactly as predicted (hllc 52.7 -> 6.67 per launch, weno 25.00 -> 13.00). Checkpoints after the change are BYTE-IDENTICAL to the baseline binary, both files. NOT VERIFIED on the other CI compilers: a list item appearing in both private() and map() on a combined construct is accepted by amdflang, where private binds to the loop and map to the target, but nvfortran, Cray ftn and ifx are untested here. CI must gate this. A third site (m_variables_conversion) removed its predicted copies exactly and bought no measurable wall, so it was reverted - removed copies only pay when they are on the critical path, and per-copy value ranges from ~0 to 26.6 us across sites.
…size, b is not a fixed toll
… not constant, both models dead
…eady retracted elsewhere in the file
… retiring Tier 1.1-1.3
…cepts data symbols, delay varies 2x
…d negatively, skew explains only 7.6% of MPI
… path The regrid phase budget bottomed out with 12% of wall unattributed: rb:gath's children summed to under half of it, and the per-block grid-state swap was never bracketed at all (s_amr_restore_coarse sat inside PH_RHS while s_amr_swap_to_fine sat outside, so that bracket was charged half a swap pair). Adds 15 brackets and a per-phase call count. Measured, cap 64 400^3 np=8: the swap is 0.8%/0.5% of wall; PH_RHS is 54-57% GPU-busy; and s_amr_gather_coarse_patch returns at its FIRST branch for level>=2 blocks, so every existing bracket covered only 64 of 224 boxes - the other 71% went through s_amr_gather_from_parent unmeasured. With pg:all added, regrid, rg:build, rb:gath and rb:tail all account to 100.0%. The call count is what separates a slow region from one entered more often than assumed: rg:build fires 5 times per rank, not the 20 that regrid does.
s_amr_gather_from_parent_field_* used a blocking MPI_SEND, so the parent's owner rendezvoused with the child's owner once per box, in lockstep. That is the defect m_amr.fpp:256 already records - and fixes - for the level-1 path, via the deferred amr_gsnd_pool drained once after the rebuild's box loop; it was never applied here, even though level>=2 is 160 of 224 boxes at cap 64. Route it through the same pool. Measured, cap 64 400^3 np=8, from-scratch: wall -17.3% at regrid_int=2 (WENO1+LF) and -22.2% at regrid_int=20 (WENO5+HLLC); regrid -39.3%; pg:send 206 -> 52 ms/call. Call counts are byte-identical before and after (89 sends, 63 recvs), the untouched physics phase moves -0.9%, and rb:xchg - a single-flag allreduce this patch never touches - falls 54% and 84%, confirming the per-box serialisation was producing the arrival skew rather than merely correlating with it. s_amr_subcycle_setup_level calls the two Fypp-generated variants directly, bypassing s_amr_gather_from_parent, and that loop has no drain: deferred sends would accumulate to the pool's 64-slot force-drain, whose WAITALL can close a deadlock cycle. No test covers it - every subcycle golden is 1D at np=1, where powner == cowner takes the local-copy branch and the send is never reached - so both sites drain immediately, as seven other runtime sites already do. Goldens 76/76, including four multi-level np=2 cases that genuinely execute the converted line.
The 2026-08-15 plan pointed at batching the fine-block advance. G0 measured that premise and it does not hold: the per-block swap is 0.8% of wall and PH_RHS is 54-57% GPU-busy, so batching's ceiling is ~1.09x, while regrid is ~38% of wall at BOTH the AMReX-comparison point and the production point (WENO5+HLLC, regrid_int=20) - the priority flip the review predicted at a realistic interval does not happen. Rewrites the plan around a G0 gate and Tracks R/B/M/C, with every number carrying its operating point. Adds amr_tax_review.md, an audit of the campaign, and records its own corrections: a first draft claimed PH_RHS was 62-74% busy by charging the coarse s_compute_rhs to the fine bracket, and the correction to that was itself off until a third pass - which is why the plan now forbids composing a ratio from two runs and why G0 opens with direct measurement. Also corrects 'caps 96 and 128 OOM', which was never tested from scratch and descends from the same table that wrongly listed cap 64.
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 newm_boxmodule (byte-identical; covered by the existing suite).m_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).m_active_box/active_box: restricts reconstruction/Riemann/RK windows to a light-cone-grown box around non-ambient flow; strict-subset golden-tested.hybrid_wenoandhybrid_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).m_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-blockpb/mvside-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_subcycleunder IGR · stretched grids with Lagrangian/IB-regrid (uniform-spacing index conversions).Validation evidence
Known issues (all non-gating or in progress)
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.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_validatorentries, runtime checker gates, andcase.md/amr.mddocumentation.