Appearance
The well-balanced mode
| ID | Group | Milestone | Backends | Figure | Regression |
|---|---|---|---|---|---|
| GV-2b, GV-5, GV-8, GV-10, GV-7 (G2b) | External gravity | M7 (#73) | CPU, FNL | yes | — |
Case and scope
Without balancing, the truncation errors of the flux difference and of the gravity source do not cancel on a stratified atmosphere at rest: they drive a stationary flow (GV-2: Mach
What the case catches:
- an equilibrium that is not an exact discrete steady state (GV-2b, GV-5): isothermal, polytropic and magnetised atmospheres at rest must stay at rest,
, on both face families; - a seam that breaks the balance (GV-8): across 2:1 seams the residual and the reflux must both cancel the equilibrium's share;
- a balance that hides the physics (GV-10): the response to a small pressure pulse must be linear in its amplitude, the purpose of the mode;
- backend divergence (GV-7) on the balanced pulse and the balanced AMR box.
Models and numerics
| Item | Choice | Code |
|---|---|---|
| Equations | compressible Euler, ideal gas, glm_ch = 3, glm_alpha = 0.18, glm_damping_length = 1) | models, EGLM |
| Well-balanced mode | [gravity] equilibrium = atmosphere: the initial atmosphere at rest | refresh_equilibrium_residual, subtract_equilibrium_residual, adam_flume_cpu_object.F90; set_equilibrium, adam_flume_ic_object.F90; subtract_equilibrium_residual_dev, adam_flume_fnl_kernels.F90; refresh_equilibrium_residual, adam_flume_fnl_object.F90 |
| Reflux at 2:1 seams | the equilibrium's coarse–fine flux mismatch is stored per register face (seam_e) and the end-of-step reflux subtracts it | apply_reflux_to_stage_forest, reflux_stage, adam_flume_cpu_object.F90; numerics |
| Refusals | multi-realm runs; reflux_policy = troubled | initialize_flume, adam_flume_cpu_object.F90; check_gravity, adam_flume_common_object.F90 |
| Gravity sources | momentum | add_gravity_sources, compute_gravity_correction, adam_flume_cpu_object.F90 |
Space, weno | WENO5 (weno-u-5) flux splitting on characteristic variables | compute_residuals_weno, adam_flume_cpu_object.F90; numerics |
Space, weno-riemann (GV-5) | WENO5 interpolation, HLLD, sixth-order flux correction | compute_residuals_riemann, adam_flume_cpu_object.F90; adam_flume_cpu_mhd_eglm_hlld_kernels.F90; numerics |
| Time | SSP Runge–Kutta, 3 stages (runge-kutta-ssp-33), CFL 0.4 | adam_rk_object.F90 |
| Boundaries | x periodic, y hydrostatic | hydrostatic_ghost, adam_flume_bc_object.F90 |
Initial and boundary conditions
Common: ngc = 4, np 2, CFL 0.4, region 1 periodic, y hydrostatic; initial condition isothermal-atmosphere or polytropic-atmosphere.
| Leg | Case | Atmosphere | Gravity | Extra | Final time |
|---|---|---|---|---|---|
| GV-2b | strat2d | isothermal, ten scale heights (density ratio | — | 0.5 | |
| GV-2b | polystrat2d | polytropic, | — | 0.5 | |
| GV-5 | strat2d-mhdy | as strat2d, EGLM | 0.2 | ||
| GV-5 | strat2d-mhdx | as strat2d, EGLM | 0.2 | ||
| GV-8 | strat2d-amr | as strat2d | the box | 0.5 | |
| GV-10 | strat2d + pulse | as strat2d | relative pressure pulse $p,(1 + A e^{- | \mathbf x - \mathbf x_0 |
With --balanced; the unbalanced runs of strat2d and strat2d-amr are reported for comparison.
Input files
| File | Role |
|---|---|
src/tests/flume/verification/gravity/make_atmosphere.py | writes the cases (--balanced, --pulse A, --time-max, --riemann hlld) |
src/tests/flume/verification/gravity/check.sh | runs the G2b legs with the force legs (--legs gv2b,gv5,gv8,gv10); --agree TAG adds GV-7 |
src/tests/flume/verification/gravity/atmosphere_oracle.py | rest ($\max |
src/tests/flume/verification/make_doc_runs.sh (group gravity) | re-runs {strat2d,polystrat2d,strat2d-amr}-{unb,bal} saved every 5 steps and lin-{unb,bal}-{6,4} |
src/tests/flume/verification/make_doc_figures.py (fig_gravity_balance) | draws the figure |
bash
cd src/tests/flume/verification/gravity
./check.sh --np 2 --cases iso2d --keep # CPU
FLUME_EXE=$PWD/../../../../../exe/adam_flume_fnl ./check.sh --np 2 --cases iso2d --keep \
--agree adam_flume_cpu # FNL, and GV-7 against the CPU runsResults

Top:
Top left, strat2d levels at polystrat2d at strat2d and polystrat2d stay exactly at rest (drawn at
| Leg | Asserted | Bound | Unbalanced | Balanced, CPU | Balanced, FNL |
|---|---|---|---|---|---|
GV-2b strat2d, | $\max | \mathbf u | /c$ | ||
GV-2b polystrat2d, | as above | 0 | 0 | ||
GV-5 strat2d-mhdy, weno / weno-riemann HLLD | as above | 0 / 0 | 0 / 0 | ||
GV-5 strat2d-mhdx, weno / weno-riemann HLLD | as above | 0 / 0 | 0 / 0 | ||
GV-8 strat2d-amr, | as above | ||||
GV-10 pulses strat2d, | responses per unit amplitude agree | differ by 97 (reported) | the same | ||
GV-7 FNL against CPU, the strat2d-amr | per field | — | — |
The unbalanced MHD values are at check.sh runs the MHD legs to
Related cases: stratified atmospheres (the equilibria and their ghosts); the gravity force (GV-2, the unbalanced baseline); Rayleigh–Taylor keeps its unseeded layers with the same mode (GV-4b); conservation across AMR seams for the reflux without gravity.
Findings
- The balanced rest is exactly zero where the state equals the stored equilibrium bit for bit; on the AMR box the reflux arithmetic leaves round-off.
- GV-10 shows what the mode is for: the balanced response is linear in the amplitude to its second order (
against ), while the unbalanced one is dominated by its own stationary flow. - The seam mismatch belongs to the reflux, not to the residual. At
the residual and the subtracted are the same operator on the same state, at 2:1 seams too. The end-of-step reflux would still add the equilibrium's coarse–fine flux mismatch, so it is stored per register face and the reflux subtracts it. Folding it into instead was tried and fails: the coarse seam cells then carry a stage residual, the stages leave the equilibrium and the fine ghosts follow (Mach on the AMR box; exact with the reflux off). The residual and the reflux each conserve for the state and for the equilibrium, so the difference conserves. - GV-7 momentum scale. GV-7 scales the momentum by at least
: a balanced run's momentum is the perturbation, round-off at rest, and the backends' round-off of the residuals that the subtraction cancels ( ) would read against its own maximum. - The alternative. Xing & Shu 2013 and Li & Xing 2018 (a modified Lax–Friedrichs split with the source reconstructed by the same WENO operator) was prototyped in NumPy and matched the deviation method to three digits, but covers the flux-splitting path only.
- Limits. The subtracted residual is outside the positivity limiter's factor: at the equilibrium the factor is 1 and the balance exact, but the limiter's guarantee does not cover that term (the equilibrium's truncation error, small where the atmosphere is resolved). Refused: multi-realm runs (the peer realms' equilibrium would be needed at the seams) and
reflux_policy = troubled(its reflux cells change per step). Cost: one field and one residual evaluation per grid. - Found on the way (FNL, nvfortran 26.1 with FiNeR): a character
getright after the failedgetof an absent numeric key returns an empty value with no error;equilibrium = atmosphereread as "" after the absentfroude. The gravity section now reads its string keys first (load_from_file,adam_flume_gravity_object.F90).