Regression Tests
This page covers complete ERF regression cases. For focused GoogleTest cases, see Unit Testing.
ERF runs a set of CMake regression tests on every pull request. These tests use the AMReX version in the ERF submodule. Nightly GNU Make tests use an AMReX build configured for the nightly environment.
Results from the nightly CPU tests can be found here: CPU tests
Results from the nightly GPU tests can be found here: GPU tests
The following problems are currently tested in the CI:
Test |
nx ny nz |
xbc |
ybc |
zbc |
Ext |
Other |
|---|---|---|---|---|---|---|
Bubble_Density_Current |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
moist bubble |
AnelasticWallDiffusion_X/Y/Z |
8 12 17 |
NoSlipWall |
NoSlipWall |
NoSlipWall |
None |
rotated stationary linear-theta wall-diffusion tests |
CouetteFlow_x |
32 4 16 |
Periodic |
Periodic |
NoSlipWall NoSlipWall |
None |
inhomogeneous bc at zhi (u = 2) |
CouetteFlow_y |
4 32 16 |
Periodic |
Periodic |
NoSlipWall NoSlipWall |
None |
inhomogeneous bc at zhi (v = 2) |
CouetteFlow_ImplicitCF |
4 4 32 |
Periodic |
Periodic |
NoSlipWall NoSlipWall |
None |
two levels: fine patch over the lower half; implicit vertical diffusion closed at the c/f bdy |
MoistDiffusion_ImplicitCF |
4 4 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
as CouetteFlow_ImplicitCF, for the implicit moisture solve |
DensityCurrent |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
+gravity |
DensityCurrent_detJ2 |
256 4 64 |
Symmetry Outflow Outflow |
Periodic |
SlipWall SlipWall SlipWall |
None |
terrain_type = StaticFittedMesh uses zlevels detJ = 2 everywhere |
DensityCurrent_detJ2_nosub |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
terrain_type = StaticFittedMesh uses zlevels detJ = 2 everywhere no substepping |
DensityCurrent_detJ2_MT |
256 4 64 |
Symmetry Outflow Outflow |
Periodic |
SlipWall SlipWall SlipWall |
None |
terrain_type = MovingFittedMesh uses zlevels detJ = 2 everywhere |
EkmanSpiral |
4 4 400 |
Periodic |
Periodic |
NoSlipWall SlipWall |
Geo |
+Coriolis +gravity |
IsentropicVortexAdvecting |
48 48 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
IsentropicVortexStationary |
48 48 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
MSF_NoSub_IsentropicVortexAdv |
48 48 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
tests map factors without substepping |
MSF_Sub_IsentropicVortexAdv |
48 48 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
tests map factors with substepping |
PoiseuilleFlow_x |
32 4 16 |
Periodic |
Periodic |
NoSlipWall NoSlipWall |
GradP in x |
|
PoiseuilleFlow_y |
4 32 16 |
Periodic |
Periodic |
NoSlipWall NoSlipWall |
GradP in y |
|
RayleighDamping |
64 4 64 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
Rayleigh damping |
ScalarAdvectionUniformU |
64 64 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
ScalarAdvectionShearedU |
64 4 64 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
ScalarAdvDiff_order2 |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
advection + diffusion “Centered_2nd” |
ScalarAdvDiff_order3 |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
advection + diffusion “Upwind_3rd” |
ScalarAdvDiff_order4 |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
advection + diffusion “Centered_4th” |
ScalarAdvDiff_order5 |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
advection + diffusion “Upwind_5th” |
ScalarAdvDiff_order6 |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
advection + diffusion “Centered_6th” |
ScalarDiffusionGaussian |
16 16 16 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
ScalarDiffusionSine |
16 16 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
TaylorGreenAdvecting |
16 16 16 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
TaylorGreenAdvectingDiffusing |
16 16 16 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
ParticleAdvect_AMR1_box |
128 4 32 |
Inflow Outflow |
Periodic |
SlipWall SlipWall |
None |
particle advection, 1 AMR level static box tagging, partial z |
ParticleAdvect_AMR1_pcount |
128 4 32 |
Inflow Outflow |
Periodic |
SlipWall SlipWall |
None |
particle advection, 1 AMR level particle-count tagging |
ParticleAdvect_AMR2_pcount |
128 4 32 |
Inflow Outflow |
Periodic |
SlipWall SlipWall |
None |
particle advection, 2 AMR levels particle-count tagging |
while the following tests are run nightly:
Test |
nx ny nz |
xbc |
ybc |
zbc |
Ext |
Other |
|---|---|---|---|---|---|---|
ABL-Deardorff |
64 64 64 |
Periodic |
Periodic |
NoSlipWall SlipWall |
None |
LES |
ABL-Deardorff-OMP |
64 64 64 |
Periodic |
Periodic |
NoSlipWall SlipWall |
None |
LES uses OpenMP |
ABL-MOST |
64 64 64 |
Periodic |
Periodic |
SurfLay SlipWall |
None |
LES with MOST bc |
ABL-MOST-OMP |
64 64 64 |
Periodic |
Periodic |
SurfLay SlipWall |
None |
LES with MOST bc uses OpenMP |
ABL-MYNN |
2 2 64 |
Periodic |
Periodic |
SurfLay SlipWall |
None |
MYNN2.5 Model |
ABL-Smag |
64 64 64 |
Periodic |
Periodic |
NoSlipWall SlipWall |
None |
LES |
ABL-Smag-OMP |
64 64 64 |
Periodic |
Periodic |
NoSlipWall SlipWall |
None |
LES uses OpenMP |
Bomex |
32 32 100 |
Periodic |
Periodic |
SurfLay SlipWall |
None |
Kessler_NoRain |
Bubble_Kessler |
100 4 100 |
SlipWall |
Periodic |
SlipWall SlipWall |
None |
Kessler |
Bubble_Kessler_NoRain |
200 4 100 |
SlipWall |
Periodic |
SlipWall SlipWall |
None |
Kessler_NoRain |
DensityCurrent |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
+gravity |
DensityCurrent-OMP |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
+gravity uses OpenMP |
DensityCurrent_Terrain |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
+gravity terrain_type = StaticFittedMesh |
DensityCurrent_Terrain-OMP |
256 4 64 |
Symmetry Outflow Outflow |
Periodic |
SlipWall SlipWall SlipWall |
None |
+gravity terrain_type = StaticFittedMesh uses OpenMP |
DensityCurrent_anelastic |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
+gravity anelastic |
DensityCurrent_detJ2 |
256 4 64 |
Symmetry Outflow |
Periodic |
SlipWall SlipWall |
None |
+gravity uses z_levels |
EkmanSpiral_custom |
12 10 400 |
Periodic |
Periodic |
NoSlipWall SlipWall |
Geo Cor |
custom init |
EkmanSpiral_ideal |
12 10 400 |
Periodic |
Periodic |
NoSlipWall SlipWall |
Geo Cor |
init from ideal wrfinput file |
EkmanSpiral_input_sounding |
4 4 400 |
Periodic |
Periodic |
NoSlipWall SlipWall |
Geo Cor |
init from input_sounding |
EkmanSpiral_restart |
4 4 400 |
Periodic |
Periodic |
NoSlipWall SlipWall |
Geo Cor |
restart test |
IsentropicVortexAdvecting |
48 48 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
IsentropicVortexStationary |
48 48 4 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
MetGrid |
140 80 100 |
Outflow |
Outflow |
SurfLay SlipWall |
None |
init from metgrid file |
MovingTerrain_nosub |
40 8 79 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
terrain_type = MovingFittedMesh |
NoahMP_Ideal |
4 4 32 |
Periodic |
Periodic |
SurfLay SlipWall |
None |
Noah-MP land model, init from input_sounding |
ParticlesOverWoA |
256 8 64 |
Inflow Outflow |
Periodic |
SlipWall SlipWall |
None |
particle advection |
ScalarAdvecDiffDoubleDen |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
Density = 2 |
ScalarAdvecDiffUniformU |
32 32 32 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
TaylorGreenAdvecting |
64 64 64 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
|
TaylorGreenAdvDiffDoubleDen |
64 64 64 |
Periodic |
Periodic |
SlipWall SlipWall |
None |
Density = 2 |
TurbulentInflow |
64 16 32 |
Inflow Outflow |
Periodic |
SurfLay SlipWall |
None |
LES |
TurbulentInflow_anelastic |
64 16 32 |
Inflow Outflow |
Periodic |
SurfLay SlipWall |
None |
LES |
WPS_Test |
200 200 176 |
wrfbdy wrfbdy |
wrfbdy wrfbdy |
NoSlipWall SlipWall |
None |
init from wrfinput |
WPS_Test-OMP |
200 200 176 |
wrfbdy wrfbdy |
wrfbdy wrfbdy |
NoSlipWall SlipWall |
None |
init from wrfinput |
WPS_Test_restart |
200 200 176 |
wrfbdy wrfbdy |
wrfbdy wrfbdy |
NoSlipWall SlipWall |
None |
init from wrfinput |
WPS_Test-RRTMGP+NOAHMP |
100 100 79 |
wrfbdy wrfbdy |
wrfbdy wrfbdy |
NoSlipWall SlipWall |
None |
init from wrfinput uses RRTMGP+NOAHMP |
WPS_Test-RRTMGP+SLM |
100 100 79 |
wrfbdy wrfbdy |
wrfbdy wrfbdy |
NoSlipWall SlipWall |
None |
init from wrfinput uses RRTMGP+SLM |
WPS_Test-ML_Interp |
100 100 79 |
wrfbdy wrfbdy |
wrfbdy wrfbdy |
NoSlipWall SlipWall |
None |
init from wrfinput; max_level=1 |
More details about the CI tests are given below.
Scalar Advection by Uniform Flow in XY Plane
This tests scalar advection with periodic boundaries in the lateral directions and slip walls at low and high z.
Test Location: Tests/test_files/ScalarAdvectionUniformU
Problem Location: Exec/RegTests/ScalarAdvDiff
Scalar concentration at t=0. |
Scalar concentration at 20 steps. |
Scalar Advection by Sheared Flow
This tests scalar advection with periodic boundaries in the lateral directions and slip walls at low and high z.
Test Location: Tests/test_files/ScalarAdvectionShearedU
Problem Location: Exec/RegTests/ScalarAdvDiff
Scalar concentration at t=0. |
Scalar concentration at 80 steps |
Scalar Diffusion: Sphere of Scalar
This tests scalar diffusion with periodic boundaries in the lateral directions and slip walls at low and high z.
Test Location: Tests/test_files/ScalarDiffusionGaussian
Problem Location: Exec/RegTests/ScalarAdvDiff
Scalar concentration at t=0. |
Scalar concentration at 20 steps (t = 0.01). |
Scalar Diffusion: Sinusoidal Variation of Scalar
This tests scalar diffusion with periodic boundaries in the lateral directions and slip walls at low and high z.
Test Location: Tests/test_files/ScalarDiffusionSine
Problem Location: Exec/RegTests/ScalarAdvDiff
Scalar concentration at t=0. |
Scalar concentration at 20 steps (t = 0.2). |
Scalar Advection/Diffusion by Uniform Flow With Different Spatial Orders
This tests scalar advection and diffusion with periodic boundaries in the lateral directions and slip walls at low and high z.
Test Location (for 2nd order): Tests/test_files/ScalarAdvDiff_order2
Problem Location: Exec/RegTests/ScalarAdvDiff
Scalar concentration at t=0. |
Scalar concentration at 20 steps (t = 0.01). |
Rayleigh Damping
This tests Rayleigh damping. The problem is initialized as in the shear flow case, then Rayleigh damping is applied with a target mean profile of (2,1,0).
Test Location: Tests/test_files/RayleighDamping
Problem Location: Exec/RegTests/ScalarAdvDiff
Isentropic Vortex: Stationary
This tests advection of an isentropic vortex with triply periodic boundaries.
Test Location: Tests/test_files/IsentropicVortexStationary
Problem Location: Exec/RegTests/IsentropicVortex
Isentropic Vortex: Advecting
This tests advection of an isentropic vortex with triply periodic boundaries.
Test Location: Tests/test_files/IsentropicVortexAdvecting
Problem Location: Exec/RegTests/IsentropicVortex
Taylor Green Vortex: Advection
This tests advection and diffusion with triply periodic boundaries.
Test Location: Tests/test_files/TaylorGreenAdvecting
Problem Location: Exec/RegTests/TaylorGreenVortex
Taylor Green Vortex: Advection and Diffusion
This tests advection and diffusion with triply periodic boundaries.
Test Location: Tests/test_files/TaylorGreenAdvectingDiffusing
Problem Location: Exec/RegTests/TaylorGreenVortex
Flow field at t=0. |
Flow field at 10 steps (t = 1.6). |
Couette Flow (x-direction)
This tests Couette flow in a channel. The domain is periodic in the x- and y-directions, and has NoSlipWall bc’s on the low-z and high-z faces. At the high-z boundary the velocity is specified to be \(U = (2,0,0)\). The steady solution for this problem is \(U = (z/8,0,0)\) in the domain which is 16 units high in z.
Test Location: Tests/test_files/CouetteFlow_x
Problem Location: Exec/RegTests/Couette_Poiseuille
Couette Flow (y-direction)
This tests Couette flow in a channel. The domain is periodic in the x- and y-directions, and has NoSlipWall bc’s on the low-z and high-z faces. At the high-z boundary the velocity is specified to be \(U = (0,2,0)\). The steady solution for this problem is \(U = (0,z/8,0)\) in the domain which is 16 units high in z.
Test Location: Tests/test_files/CouetteFlow_y
Problem Location: Exec/RegTests/Couette_Poiseuille
Couette Flow with a coarse/fine boundary in z
This is Couette flow started from rest, driven by the lid at the high-z boundary
(\(U = (2,0,0)\)), with a static refined patch that covers only the lower half of the column
(\(0 \le z \le 8\) of 16). The diffusion front from the lid therefore crosses a coarse/fine
boundary in z. The vertical implicit solve is used in all three RK stages
(erf.vert_implicit_fac = 1) with a fine-level diffusion number of about 6, and each fine column
is closed at the coarse/fine boundary with the coarse data at the stage time. The test compares
the plotfile after 50 coarse steps against a gold file.
Test Location: Tests/test_files/CouetteFlow_ImplicitCF
Problem Location: Exec/RegTests/Couette_Poiseuille
Moisture diffusion with a coarse/fine boundary in z
The companion of the test above for the implicit moisture solve, which is done after the
acoustic substeps (ERF_ImplicitPost.H) rather than before them. A moist layer
(\(q_v = 10\) g/kg above \(z = 12\) of 16, dry below) at rest diffuses down
(erf.alpha_C = 4) through a static refined patch over the lower half of the column,
with the vertical implicit solve in all three RK stages and two-way coupling. Each fine
column is closed at the coarse/fine boundary with the coarse data at the stage time. The
test compares the plotfile after 200 coarse steps against a gold file.
Test Location: Tests/test_files/MoistDiffusion_ImplicitCF
Poiseuille Flow (x-direction)
This tests Poiseuille flow in a channel. The domain is periodic in the x- and y-directions, and has NoSlipWall bc’s on the low-z and high-z faces. We initialize the solution with the steady parabolic profile \(U = (1-z^2,0,0)\) in the domain which runs from -1. to 1. in z. The viscosity is specified to be 0.1 and the imposed pressure gradient is \(Gp = (-0.2,0,0)\).
Test Location: Tests/test_files/PoiseuilleFlow_x
Problem Location: Exec/RegTests/Couette_Poiseuille
Poiseuille Flow (y-direction)
This tests Poiseuille flow in a channel. The domain is periodic in the x- and y-directions, and has NoSlipWall bc’s on the low-z and high-z faces. We initialize the solution with the steady parabolic profile \(U = (0,1-z^2,0)\) in the domain which runs from -1. to 1. in z. The viscosity is specified to be 0.1 and the imposed pressure gradient is \(Gp = (0,-0.2,0)\).
Test Location: Tests/test_files/PoiseuilleFlow_y
Problem Location: Exec/RegTests/Couette_Poiseuille
Nonlinear Density Current
The density current problem tests the effects of gravity and the behavior at a slip wall.
See Verification for more information.
Test Location: Tests/test_files/DensityCurrent
Problem Location: Exec/CanonicalTests/DensityCurrent
Canonical RANS
Five cases exercise the one-equation \(k\) RANS closure (see One-equation RANS closure)
on flat ground (neutral, stable and convective boundary layers) and on
terrain-fitted meshes (a 2D ridge and a 3D hill), each for 40 steps. Every
entry then runs the case’s Python check script, which reads the plotfile
with a standard-library AMReX reader, averages or samples the fields, and
compares numbers against stated targets with tolerances: wall distance
against the exact distance to the terrain, length scale against its bounds,
the wall value of \(k\) against \(u_*^2 / (c_\mu^0)^2\), dissipation
against AL01 Eq. 19, and, in the longer physics runs documented in each
case’s README, the log law, GABLS1 depths and jets, the convective heat
budget and the hill-top speed-up. The script’s exit code is the verdict; a
clean exit alone never passes a test. The _Poisson variants run the
terrain cases with the Poisson wall distance instead of the terrain height.
Test names: RANS_Neutral_ABL_Flat, RANS_Stable_ABL_Flat,
RANS_Convective_ABL_Flat, RANS_Neutral_Hill_2D (and _Poisson),
RANS_Flat_Fitted_2D (and _Poisson), RANS_Neutral_Hill_3D (and
_Poisson); label rans. RANS_WallDist_RaisedBase and
RANS_WallDist_RaisedBase_StretchedZSplit run the neutral flat case for one
step with the bottom at 300 m, on a constant-dz mesh and on a stretched mesh
whose boxes are split in z, and check that the wall distance is the height
above the bottom wall. RANS_Neutral_Hill_2D_Realizability runs the
ridge twice, without and with the realizability cap of the closure
(erf.rans_realizability), and pins the capped viscosity and diffusivity
to their closed form cell by cell (check_realizability.py). The flat cases run with the MLMG projection
(erf.use_fft=false); the terrain-fitted cases need the FFT-preconditioned
projection and are registered only when the build enables FFT
(ERF_ENABLE_FFT).
RANS_Checks_SelfTest tests the check scripts’ own verdict logic rather
than any physics: it states, for each kind of comparison the shared
rans_checks.py offers, what the check must decide for values inside and
just outside the stated tolerance or band, and fails when a check disagrees.
It also calls check_implicit_explicit_ke.py with malformed arguments (a
missing or non-numeric --tol, too few plotfiles), each of which must print
the usage and exit with status 2 rather than fail with a traceback.
It runs no ERF executable; labels rans and unit. It is registered
only when CMake finds a Python 3 interpreter, so a configuration without
one simply does not have the test rather than failing the unit stage.
Problem Location: Exec/CanonicalTests/Canonical_RANS
Restart parity
MoistBubble_Kessler_Restart (MPI builds, not Windows) runs the moist bubble
deck with Kessler rain (erf.moisture_model=Kessler, the rain fields in the
plotfile) straight to step 8,
again to a checkpoint at step 4, and from that checkpoint to step 8, and
requires the two plotfiles at step 8 to be identical (Tests/RunRestartParity.cmake,
no gold file; label restart-parity). Every run has a time limit of its own,
so a restart whose first step never finishes fails with a message. Until
September 2026 the restart path handed the microphysics its minimum cell
height only on terrain-fitted meshes; on a constant-dz mesh the sedimentation
substep count of the first restarted step was computed from an uninitialised
value and the step never finished. Only the schemes that size their
sedimentation substeps from that height are affected, namely Kessler
(ERF_Kessler.cpp) and SAM (ERF_PrecipFall.cpp, ERF_IceFall.cpp);
Morrison, WSM6 and WDM6 store the minimum cell height but never read it.
Test Location: Tests/test_files/MoistBubble_Kessler_Restart
IBSEB_Cube_Restart, IBSEB_RefinedLevels_Restart,
ImmersedTerrain_Hill_Restart and ImmersedTerrain_Hill_TwoLevel_Restart
(MPI builds, not Windows) do the same for immersed forcing: the
IBSEB_Cube deck through a checkpoint at step 17 to step 40, with the
velocities added to its plotfile; the two-level IBSEB_RefinedLevels deck
through step 7 to step 20; and the TerrainHill deck with
erf.terrain_type = ImmersedForcing through step 7 to step 20, on one level
and on the deck’s two. All but one compare with zero tolerance.
IBSEB_RefinedLevels_Restart compares to 1e-8 relative: the restart leg
writes a plotfile at the restart step and the straight leg does not, and on two
levels writing a plotfile changes the solution at round-off (erf-model/ERF#4224;
5e-15 relative in theta without buildings, 1.7e-10 relative in w in this deck by
step 20); with plotfiles at the same steps in every leg that restart is
bit-exact too.
A restart rebuilds the immersed blanking (terrain_IB_mask) rather than
reading it from the checkpoint, and erf.ibseb builds its faces from it.
Until October 2026 the restart path built it without clearing the almost-fluid
cells (solid fraction below eb2.small_volfrac) as a fresh start does, so the
restarted run applied the wall law in cells the straight run leaves alone: 0.2
m/s in u on one level and 0.85 m/s on the refined level of the hill, and 6e-5
m/s in u and 8e-6 K in the face skin temperatures of the cube (the faces
themselves were the same; they take the cells with a blanking of at least 0.5).
A fresh start, a regrid and a restart now build the blanking with the same
function, ERF::make_terrain_blanking.
Test Location: Tests/test_files/IBSEB_Cube, Tests/test_files/IBSEB_RefinedLevels, Tests/test_files/TerrainHill
Closure box, rank and tiling parity
The Closure_BoxParity_* tests (MPI builds, not Windows; label box-parity)
run the unstable, perturbed ABL deck of ABL_MRF_Tiling twice: on one box, on
one rank, without tiling, and on four 16 x 16 x 32 boxes on two ranks with 8 x 8
tiles. The plotfiles after 10 steps must agree to a relative tolerance of 1e-9
(Tests/RunBoxParity.cmake, no gold file). The entries choose the physics on
the command line: the Deardorff closure, once with erf.vert_implicit = false
and once with the implicit vertical solve that ERF uses by default; the k-eqn
closure with its PBL-height length cap; the MYNN25, MYNNEDMF, MYJ and native
SHOC PBL schemes; Kessler microphysics on a moist sounding; and Smagorinsky on a
stretched mesh whose levels are chosen so that they sum to the top of the domain.
With the FFT build, two more run the anelastic MidPoint integrator with the
Deardorff closure and with Kessler microphysics.
Until September 2026 the anelastic integrator copied the projected momentum
into the fluxes of the slow scalars tile by tile inside the loop that advects
them, so the turbulent kinetic energy, moisture and passive scalars of anelastic
runs depended on the tile size. The boxes are never split in z, so the column
solves apply. The moist sounding is supersaturated below 150 m so that Kessler
condenses, autoconverts and sediments within the ten steps of the run; otherwise
qc and qp would be compared as zero against zero. MYNNEDMF computed its
diffusivities on a box grown by one cell in the vertical, so its vertical-derivative
stencil reached two cells outside the domain; it now uses the valid box, as MYNN25
does. The ghost values it computed were discarded in any case, since
ComputeTurbulentViscosity refills every eddy-viscosity ghost cell after the
scheme returns, and dropping them leaves the answer bit for bit unchanged.
Test Location: Tests/test_files/Closure_BoxParity
Conservation across coarse/fine boundaries
The Conservation_* tests (label conservation, no gold file) run a two-level deck with
two-way coupling in a closed domain: slip walls in z, periodic in x and y. They require the
composite integral of a scalar to be the same at the start and the end of the run
(Tests/RunConservation.cmake). The integral is the one ERF prints every step with
erf.sum_interval = 1 (RHO SCALAR SL/ML or RHO QTOTAL SL/ML, see
erf.sum_interval in Inputs). It weights each cell by
\(\det J/(m_x m_y)\) and masks the covered cells, so it is the quantity the discretization
conserves on any mesh and with any refinement ratio. The two values must agree to 12
significant digits in double precision and to 5 in single precision.
That holds only if the reflux at the coarse/fine faces includes the diffusive fluxes, both the explicit ones and the implicit part of the vertical ones (erf-model/ERF#4323). Before that fix every one of these tests failed: the moisture integral changed by about 1e-3 relative, and the scalar integral by 1e-4 to 3e-4 (2e-8 with Smagorinsky diffusion alone).
The four Conservation_MoistDiffusion_* tests run 100 steps of the MoistDiffusion_ImplicitCF
deck, whose patch has a coarse/fine face normal to z, and check the total moisture.
Implicit, ImplicitStretched and ImplicitTerrain diffuse it with the implicit vertical
solve in every RK stage, on a flat mesh, a stretched mesh (erf.initial_dz = 0.4) and a
terrain-fitted one (erf.terrain_type = StaticFittedMesh). Explicit diffuses it
explicitly (erf.vert_implicit = false, erf.alpha_C = 0.4).
The Conservation_ScalarDiffusion_* and Conservation_ScalarAdvection_* tests run the
ScalarDiffusion_Reflux deck: a scalar blob diffusing for 50 steps through a patch over the
middle half of the domain in x and y and the lower half in z, so through faces normal to all
three directions. Flat runs it as it stands. Terrain uses a terrain-fitted mesh over flat
terrain and Hill one over a hill (a radial Witch of Agnesi, 0.25 high, half-width 0.15),
Stretched a stretched mesh (erf.initial_dz = 0.025), MapFactor map factors
(erf.test_mapfactor = true), and Ratio221 2 x 2 x 1 refinement. Anelastic uses the
anelastic MidPoint integrator, which refluxes its second RK stage rather than the third.
AnelasticRK2 uses the anelastic RK2 integrator, whose update averages its two stages, so it
refluxes half the fluxes of each; Conservation_ScalarAdvection_AnelasticRK2 runs it with the
scalar advected (prob.U_0 = 10, prob.V_0 = 5) and not diffused. Until erf-model/ERF#4331
was fixed the RK2 integrator refluxed its second stage alone, at full weight, and both RK2 tests
failed: the scalar integral changed by 2.6e-6 relative with diffusion and 1.4e-5 with advection.
Smagorinsky advects the scalar with a sheared wind and diffuses it with Smagorinsky diffusion
alone.
Until erf-model/ERF#4330 was fixed the hill test failed even on one level: the scalar diffused through the bottom wall where the terrain slopes (5.9e-5 relative in this run).
A Deardorff or k-equation TKE always dissipates, so the implicit diffusion of TKE is not tested here. It goes through the same functions as that of moisture.
EB runs do not reflux diffusive fluxes, and ERF warns when two-way coupling is used with EB and diffusion.
Test Location: Tests/test_files/MoistDiffusion_ImplicitCF, Tests/test_files/ScalarDiffusion_Reflux
Terrain Poisson operator at Neumann faces
The TerrainPoisson_* tests (MPI builds, not Windows) check the anelastic projection on a
terrain-fitted mesh where the terrain slopes at a Neumann face of the region being solved
(Neumann faces on terrain-fitted meshes). The deck is a 100 m hill on 32 x 32 x 16 cells with one
refined level (ratio 2 2 1) whose four coarse/fine faces lie on the hill’s flanks; run on one
level inside slip walls, the hill’s slopes reach the walls instead. ERF prints the divergence
before and after every solve (erf.mg_v = 1), and Tests/check_terrain_poisson_divergence.py
requires each solve on the checked level to leave no more than the constant that a singular
solve cannot remove, within the solver tolerance. TerrainPoisson_TwoLevelHill checks the refined level and
TerrainPoisson_SlopingWalls the walled level: each leaves at most 1e-9, where the operator
before erf-model/ERF#4279 (no zero-flux faces; development at 905623352) left up to 1.6e-2 and
7.6e-4.
TerrainPoisson_TwoPatches refines two separate patches on the flanks, so that level 1 is
solved as two regions (development stopped there with a segmentation fault).
TerrainPoisson_MapFactors and TerrainPoisson_MapFactors_Walls run the two decks with
erf.test_mapfactor (map factors 0.5 and 0.25), where the projection used to leave 75 % of
the divergence after every solve.
TerrainPoisson_Hill_BoxParity (one box on one rank against split boxes) and
TerrainPoisson_Hill_Restart check the decomposition and a restart;
TerrainPoisson_DivergenceChecker_SelfTest feeds the checker made-up logs on both sides of
its limit. These runs need the FFT build; the self-test does not.
The same checks run with erf.terrain_poisson_solver = mlmg and gmres_mlmg
(Multigrid on terrain-fitted meshes), which need no FFT and so run in every MPI build:
TerrainPoisson_TwoLevelHill_{mlmg,gmres_mlmg}, TerrainPoisson_SlopingWalls_{mlmg,gmres_mlmg},
TerrainPoisson_TwoPatches_mlmg, TerrainPoisson_MapFactors_mlmg,
TerrainPoisson_OutflowFace_{mlmg,gmres_mlmg} (an Outflow face, so a non-singular solve),
TerrainPoisson_OneCellStrip_{mlmg,gmres_mlmg} (one cell wide between a wall and an Outflow
face over sloping terrain) and TerrainPoisson_PeriodicApart_mlmg (two patches at the two
ends of a periodic direction that do not face each other) (at a solver
tolerance of 1e-10, leaving at most 1.2e-11 on the checked level);
TerrainPoisson_Hill_BoxParity_MLMG and TerrainPoisson_Hill_Restart_MLMG; and three start-up
or first-solve aborts (an unknown solver, a refined region that is not a rectangle, refined
patches meeting across a periodic boundary), and in double precision
TerrainPoisson_Abort_MLMGNotConverged (MLMG stops with a message naming the inputs to
change). In FFT builds TerrainPoisson_Hill_SolverAgreement,
TerrainPoisson_SlopingWalls_SolverAgreement and TerrainPoisson_OutflowFace_SolverAgreement
compare FFT-GMRES on one box with MLMG on split boxes, to 1e-8.
Test Location: Tests/test_files/TerrainPoisson_Hill
Station time series
StationSampling_BoxParity and StationSampling_Restart cover the station
time series written by erf.station_names (see Inputs). A station
value is an interpolation from whichever level and whichever box happens to
cover the point, so the two things most likely to break it are a change of
decomposition and a restart, and both are checked against the run that does it
in one piece. Beyond the plotfile comparison every parity test makes, each
compares Output_Stations/Center.dat line by line, to the ten significant
digits the series prints rather than the six a data log prints
(DATALOG_SIGDIGITS in Tests/RunBoxParity.cmake and
Tests/RunRestartParity.cmake). Center.dat is the series compared because
it is the one that varies; the stations in the still air away from the bubble
would compare a constant against a constant. The restart comparison strips the
comment lines first, since the restarted run marks the seam with a comment the
straight run does not have.
The deck is the Straka density current refined over the lower middle of the
domain, with four stations chosen so that the run touches every path the sampler
has: Center, two locations inside the refined region with two heights each,
so the values come from level 1 and the vertical interpolation runs; Edge,
inside the outer half cell of the non-periodic x boundary, where the
horizontal stencil collapses onto the edge cell; Wrap, inside the outer half
cell of the periodic y boundary, where the stencil reaches across the
periodic image; and Surface, a 2D diagnostic, which has no height and is
filled by the 2D plotfile path rather than the 3D one. The refined box stops
halfway up the domain, so the deck also pins down the level test: a level
supplies a station when it covers the column from the bottom of the domain up
through the cells the vertical interpolation reads, not when it covers the whole
column, and with erf.v = 1 the sampler prints the level it chose for each
station. The deck lowers erf.station_buffer_steps to 2, well below its
default of 100, so that a ten-step run exercises the flush path and, on the
restart, the header check that fires with the first flush of the restarted run.
Test Location: Tests/test_files/StationSampling
Station output does not change the answer
The two StationSampling_AnswerParity* tests (label option-parity) hold
the sampler to the claim made in Inputs, that turning station output
on does not change the solution. Each runs one deck twice, once with the
stations off and once with them on, and requires the plotfile to be identical
bit for bit – --rel_tol 0 --abs_tol 0, not the tolerances the gold-file
tests use, because a diagnostic that moves the answer at all is a bug rather
than a tolerance question (Tests/RunOptionParity.cmake, no gold file). Both
legs run on the same number of ranks with the same decomposition, so anything
that survives is the sampler’s own doing. Each names a file the “on” leg must
write and the “off” leg must not, so a misspelled option cannot pass as
agreement, and the harness refuses two legs given the same options.
The claim is not free, which is why it is tested. The sampler asks
BuildPlot3DScratch not to average the microphysics state down
(sync_solution = false), since that call modifies the coarse solution. What
it still does at every sampled step is fillpatch the state on every level up to
the highest one a station is on, re-point the qmoist pointers on every
level, and fill the requested variables over whole levels. Two decks cover the
paths that could break:
StationSampling_AnswerParityon theStationSamplingdeck – two levels, dry. The one of the two whose station resolves to level 1, so it is the case that exercisesFillPatchFineLevel.StationSampling_AnswerParity_MOSTonABL_MOST– the surface layer.u_starandt_starare 2D diagnostics of the MOST path, so the “on” leg reads what the surface layer computed as well as the 3D state.
Two gaps are left open. The first is the configuration the
sync_solution = false argument exists for: Lagrangian microphysics on two
levels with CouplingType::TwoWay, where BuildPlot3DScratch would
otherwise average the microphysics state down onto the coarse solution. No
answer-parity test runs it, so that argument is held by reading rather than by
measurement.
The second is a run driven by time-dependent lateral boundary data, the
remaining case where an extra fill at t_new could in principle matter.
There is no nc_bdy_file fixture under Tests/test_files, and the decks
that read one
(Exec/RegTests/WPS_Test, Exec/RegTests/MetGrid, the Katrina inputs under
Exec/CanonicalTests/Hurricanes) need NetCDF input that CI does not have. The
argument that it is safe is that the boundary path reaches
ReadBndryPlanes::interp_in_time, which memoizes on the requested time and is
otherwise a pure function of it, so a second fill at t_new re-derives what
the step already wrote rather than consuming a read. That is an argument, not a
measurement.
Ekman Spiral
The Ekman spiral problem tests the computation of the stress term internally and at no-slip walls, as well as Coriolis and geostrophic forcing.
See Verification for more information.
Test Location: Tests/test_files/EkmanSpiral
Problem Location: Exec/CanonicalTests/EkmanSpiral











