Skip to content
SHD Sim
Menu

Validation/Code verification

A manufactured solution

The only case here that checks the arithmetic rather than the answer. Pick the solution first, work out what source term it needs, and the error is then known exactly on every mesh.

VerificationObserved order of accuracy 2.0001 against a formal 2

Why this case

Every other page in this section asks whether a number is close to a reference — a Nusselt number, a Strouhal number, a published table. The method of manufactured solutions asks a different and harder question: is the discretisation the one that was claimed? It answers it with no reference data at all, on any mesh, to as many digits as you care to ask for.

Pick a function. Substitute it into the governing equation. Whatever is left over is a source term. Add that source, and the function you picked is now the exact solution of the modified problem — so the difference between the computed field and that function is the discretisation error, exactly, with nothing else in it. Measure it on systematically refined meshes and the rate at which it falls is the observed order of accuracy. That rate is the verification; the size of the error, on its own, says very little.

PhysicsSteady conduction on the unit square, two-dimensional
SolverlaplacianFoam, Gauss linear corrected, corrected snGrad
Manufactured solutionT(x, y) = sin(πx) sin(πy), Dirichlet 0 on all four sides
MeshesFour uniform Cartesian, 16 through 128 cells a side, r = 2 exactly
Formal order2
AgreementObserved order 2.0021, 2.0005, 2.0001 on the three refinement pairs
Last run2026-08-27

The solution, and why this one

Each factor contributes −π² on being differentiated twice, so the Laplacian is the solution back again with a constant on it — no series, no special functions, nothing to mis-transcribe:

    T(x,y)  =  sin(pi x) sin(pi y)          on the unit square

    laplacian(T)  =  -2 pi^2 sin(pi x) sin(pi y)   exactly, everywhere

It is zero on all four sides, so the Dirichlet data is exact rather than a quadrature of it. That keeps the boundary out of the measurement and leaves the interior scheme alone under test.

What is actually verified

The source has to reach the solver somehow, and in this bundle it cannot go where it belongs. This changes what the study verifies, and the change is stated here rather than left for a reader to discover.

Result

Error against the manufactured solution, on four meshes
MeshCellshL2 errorL∞ error
16 × 162560.06251.593496 × 10⁻³3.156373 × 10⁻³
32 × 321 0240.031253.978076 × 10⁻⁴7.936996 × 10⁻⁴
64 × 644 0960.0156259.941654 × 10⁻⁵1.987133 × 10⁻⁴
128 × 12816 3840.00781252.485193 × 10⁻⁵4.969637 × 10⁻⁵
The error is the discretisation error and nothing else — there is no reference data in this study, and no experimental uncertainty to argue about.
Observed order of accuracy on each refinement pair
PairRefinement ratioOrder on L2Order on L∞
16 → 3222.00211.9916
32 → 6422.00051.9979
64 → 12822.00011.9995
Formal order 2. Three digits of 2.000 on the finest pair, approached from above as the mesh refines.
L2 and L-infinity error against mesh spacing on logarithmic axes, both parallel to a slope-two guide line
The two error norms against mesh spacing, on logarithmic axes, with a slope-2 guide drawn through the finest L2 point. The guide is the formal order of the scheme, not a fit to the data.

Why the order and not the error

This is easy to say and easy not to believe, so the study carries two deliberately broken runs that make it concrete. Both are real runs, kept as failing fixtures for the automated check.

The same study with the manufactured source scaled by 1.001
MeshL2 error, degradedL2 error, healthyObserved order
16 × 162.095089 × 10⁻³1.593496 × 10⁻³
32 × 328.982054 × 10⁻⁴3.978076 × 10⁻⁴1.2219
64 × 645.995160 × 10⁻⁴9.941654 × 10⁻⁵0.5832
128 × 1285.248768 × 10⁻⁴2.485193 × 10⁻⁵0.1918
One part in a thousand, and nothing else changed.

On the coarsest mesh the degraded run sits within 32 % of the healthy one — no band anybody would write is going to separate those. Its finest error is 5 × 10⁻⁴, which looks entirely respectable for a field of order one. Only the order gives it away, and it gives it away completely: 1.22, 0.58, 0.19, falling towards zero as the mesh refines.

The second broken run writes no source at all. The solution is then identically zero, the error is a flat 0.5 on every mesh, and the observed order is exactly 0. That is what an fvOptions entry which never took effect looks like — and it is what happened in this project when setExprFields was handed an unnamed dictionary entry, warned about it, ignored the whole thing and exited successfully.

What had to be true for the order to mean anything

An observed order is a ratio of two errors, and there are several ways to make that ratio meaningless without making it look wrong. Four things are recorded on every mesh and checked rather than assumed.

  • The source really was written. The field setExprFields wrote is read back and compared against the analytic function. The worst deviation across the four meshes is 5.68 × 10⁻¹⁴, which is double-precision round-off on a field of order 100.
  • Iterative error is negligible. The linear solve is tightened to 1 × 10⁻¹⁴ from the 1 × 10⁻⁶ the product writes. The worst achieved residual is 8.20 × 10⁻¹⁵, against a discretisation error of 2.49 × 10⁻⁵ on the finest mesh. An iterative error near that size would not make the answer slightly worse — it would make the observed order a measurement of where each solve happened to stop.
  • dt and DT did not move. Both are parameters of the manufactured PDE rather than of the discretisation: dt = 126 651.4795529222 s and DT = 4 × 10⁻⁵ on all four meshes. If either changed between meshes the runs would be solving four different problems and their error ratio would not be an order of accuracy.
  • The mesh actually halved. Refinement ratio 2 on every pair, and a largest-to-smallest cell volume ratio of 1.000000000000048 — nothing graded, so r is a number and not an average of one.

The third of those is the mirror image of a trap that has flattened refinement studies in this project before, where a quantity that should have been refined with the mesh was left fixed. Here a quantity that must not move with the mesh is pinned, for the same underlying reason: a refinement study only means something if exactly one thing is changing.

Repeat this yourself

Every case here is set up from the worked examples in the product, with no hand-editing of solver files — so you can run it, and get the same numbers. The free tier runs real cases up to 250,000 cells of fluids, or 100,000 nodes of solid, with no account needed to download and no time limit.

All validation cases · Written by the team building SHD Sim.