Expand description
§Verification method
Verification asks whether the equations are solved correctly; validation asks whether the right equations are solved. For a hyperbolic solver the first question has sharp answers, because exact solutions and exact invariants exist. This page defines the measurements used in the rest of the book (Roache 1998).
Test paths below are relative to crates/<crate>/tests/ unless they start with crates/.
§Observed order of convergence
A scheme of order $p$ has error $e(h)\approx Ch^p$ for small spacing $h$, so
$\ln e=\ln C+p\ln h$ and the order is a slope. Two estimates are used:
p_k=\frac{\ln\bigl(e_k/e_{k+1}\bigr)}{\ln\bigl(h_k/h_{k+1}\bigr)}\quad\text{(between successive resolutions)},
\qquad
p_{\text{fit}}=\text{least-squares slope of }\ln e\text{ against }\ln h .The pairwise values show whether the scheme has reached its asymptotic range. The fit
smooths the pre-asymptotic pairs. A study passes only if both the fit and the finest pair
are within a stated tolerance of the expected order, and a failure prints the whole table
of spacings, errors and orders, so a test log shows what the scheme did.
The harness is [merlin_core::convergence]: error_norm,
observed_orders,
fitted_order,
run_study and
assert_order.
The harness is itself tested on error sequences with known order
(crates/merlin-core/tests/convergence/validation.rs: midpoint_values_are_second_order_in_every_norm,
left_edge_values_are_first_order_in_every_norm), so a wrong slope computation cannot make a bad
scheme look good.
§Norms
With cell error $e_i=u_i-u^{\text{exact}}_i$ and cell volume $V$ ($\Delta x$ in 1D, $\Delta x\Delta y$
in 2D), the volume-weighted norms approximate the continuous ones and so compare across resolutions
(LeVeque 2002, §8.1):
\|e\|_1=\sum_i|e_i|V,\qquad \|e\|_2=\Bigl(\sum_ie_i^2V\Bigr)^{1/2},\qquad \|e\|_\infty=\max_i|e_i| .Both numbers matter. A limited second-order scheme is second order in $L_1$ and
below second order in $L_2$ and $L_\infty$ at smooth extrema, which is why the tables
in this book give the norm next to each order. At a shock no scheme is better than first order in
$L_1$, so a shock problem is judged by the position of the shock and the plateau values,
not by an order.
§Cell averages against point values
The finite-volume unknown is a cell average. A cell average and the value at the cell
centre differ by $\tfrac{h^2}{24}u''$, the same order as the error of a second-order
scheme, so comparing averages with point values would mix the scheme’s error with the
comparison’s own. Where a reference is cheap to average it is: the Burgers references
return exact cell averages (riemann_cell_averages, sine_cell_averages in
crates/merlin-finite-volume/tests/common/burgers_exact.rs), as do the Euler shock-tube and density-wave
studies and the advection study (fill_from_cell_averages). The 2D vortex and the sound waves use values at cell centres for both the initial
data and the reference, which still measures the order correctly but means its absolute errors include that
$h^2$ term.
§Conservation
For a conservative scheme the total of each conserved quantity changes only by the
flux through the domain boundary. The tests report the relative drift
$|Q(t)-Q(0)|/|Q(0)|$, with the volume-weighted integral on curvilinear grids, and
require
- round-off (
$\sim10^{-14}$and below) on periodic and closed domains, even through many reflections off walls; - on open domains, equality with the time integral of the boundary flux, e.g.
the Sod tube’s momentum rises by exactly
$(1-0.1)\times0.25=0.225$, which is the pressure difference across the two ends times the time.
A closed-form check of this kind is stronger than a small drift, because it fails if the scheme leaks even when the leak is small.
§Invariants and exact equilibria
Some properties have a precise statement that a correct implementation satisfies exactly and that an approximation does not, so a single comparison separates them:
- a gas at rest has
$L(u)=0$exactly, in any geometry, with any scheme; - a stationary contact (a density jump at uniform pressure and velocity) stays exactly as it is under HLLC and the exact flux;
- mirror and axis symmetry: a mirrored problem gives the mirrored solution, and the unsplit method is symmetric under swapping the axes to the last bit;
- the maximum principle and total-variation bounds for scalar monotone schemes.
§Bit comparisons
Where two computations must be the same, the test demands equality of every value, not closeness:
| Equal | Test |
|---|---|
| Any number of threads (1 to 8), any block layout | solver/threads.rs, solver/multidim.rs, geometry/conservation.rs |
| A run from a TOML file and the library-driven run | run/run_from_file.rs, run/euler_problems.rs, run/curvilinear_problems.rs |
| The 1D problem embedded in 2D along either axis | equations/euler_multid.rs |
The 2D flow without $z$ dependence and the 1D cylinder | geometry/operator.rs |
A scheme in a box (Box<dyn FluxScheme>) and the scheme itself, for first order, MUSCL, WENO5, upwind 5, Lax-Wendroff, for the ideal gas in 1D, 2D and 3D, a stiffened gas, and the HLL, HLLC, local Lax-Friedrichs and exact fluxes | solver/boxed.rs; in the materials build the file runs above go through the box |
| HDF5 and CSV output of one problem | run/hdf5_run.rs; pymerlin check with h5py in CI |
| A restarted run and the uninterrupted run | crates/merlin-driver/tests/drive/restart.rs, run/restart.rs |
The comparison itself, [merlin_core::blocks::BlockedField::compare], counts the values that differ at all and the largest
difference, and is tested too. Bit equality is possible only because the design forbids anything whose result
depends on thread timing; see Numerical methods.
§Independent reference solutions
A solver compared with its own other half proves little, so the references are written
separately, in Python, in src/pymerlin/exact_solutions/:
| Module | Provides | Checked against |
|---|---|---|
euler.py | the exact Riemann solution (star_region, sample) | Toro’s tabulated star states, by pymerlin check --self-test euler, which also checks that mass, momentum and energy are conserved by the sampled solution |
burgers.py | shock, fan and transonic fan; the smooth sine solution by characteristics | the Rankine-Hugoniot speed, the characteristic relation |
acoustic.py | sound waves in a cylinder (Hankel transform) and a sphere | the wave-equation residual, and the initial data of the shipped problem files, by pymerlin check --self-test acoustic |
The Rust exact solver is tested against the same published table
(crates/merlin-finite-volume/tests/riemann/euler_exact.rs:
the_star_region_matches_the_published_values_for_all_five_tests), so the Rust and Python
implementations agree with the literature and therefore with each other.
§How the tests are organised
Integration tests mirror the source: crates/<crate>/tests/<area>/main.rs is one test
program per area of src/ (schemes, riemann, equations, geometry, solver, time, …), with one file
per topic. Every scheme is accompanied by a convergence test, and every conservative scheme by a conservation test; a
feature with neither is not considered finished. Checks outside cargo test
(the Python self-tests and convergence studies) also run in continuous integration.
§What this does not establish
Agreement with exact solutions and invariants shows the discretisation is implemented as intended and has the claimed order. It does not show that every flow of interest is resolved on a given grid, and for problems without exact solutions (the 2D Riemann problems, the shock-bubble interaction) only derivable properties are asserted. See Known limitations.
§References
- Roache, P. J. (1998), Verification and Validation in Computational Science and Engineering, Hermosa.
- LeVeque, R. J. (2002), Finite Volume Methods for Hyperbolic Problems, Cambridge, Chapter 8.
- Toro, E. F. (2009), Riemann Solvers and Numerical Methods for Fluid Dynamics, Springer, Tables 4.1 and 4.2.