Validation/Scalar transport
Advected scalar front
The width is the measurement, not the position. Numerical diffusion is indistinguishable from physical diffusion in the answer, and the fixture that proves it is a real afternoon spent chasing a diffusivity that never reached the solve.
Why this case
A step in a scalar, carried along by a uniform flow while it spreads, has an exact answer. Two numbers come out of it and they fail independently: where the front is, which is advection, and how wide it is, which is diffusion.
The width is why this case exists. Numerical diffusion is indistinguishable from physical diffusion in the answer, so a scheme that smears reports a larger diffusivity than the case was given — with the front in exactly the right place. A check on position alone passes a solver that has quietly changed the physics, and the section below shows that exact failure measured rather than imagined.
| Physics | One-dimensional advection–diffusion of a step, laminar |
|---|---|
| Solver | scalarTransportFoam; Euler in time, Gauss limitedLinear01 1 on the divergence |
| Domain | 1.0 m long, 4 × 4 cells across, with the step at x₀ = 0.3 m |
| Flow | Uniform U = 2 m/s; diffusivity D = 0.06 m²/s |
| Compared at | t = 0.1 s, so the front centre has reached 0.5 m |
| Meshes | 40, 80 and 160 cells along the flow; time steps 8, 4 and 2 × 10⁻⁴ s |
| Reference | The erfc closed form for a step advected and diffusing in one dimension |
| Agreement | Front 0.862 % from the exact position and 0.914 % from the exact width on the finest mesh |
The exact solution
A scalar initially 1 for x < x₀ and 0 beyond it, carried at a uniform speed U through a medium of diffusivity D, is
c(x, t) = 0.5 erfc( (x - x0 - U t) / (2 sqrt(D t)) )
on an infinite domain. The front translates at U and spreads as √(Dt), and there is nothing to transcribe from a paper and nothing that can be mis-transcribed. The two measurements are read off the profile the same way, by linear interpolation between the cells that straddle a level: the position is the c = 0.5 crossing, and the width is the distance between the c = 0.75 and c = 0.25 crossings. At t = 0.1 s those are 0.5 m exactly and 0.14777330 m.
The domain is finite and the reference is not, so the gap is measured rather than waved away. At t = 0.1 s the exact solution differs from 1 at the inlet and from 0 at the outlet by 2.505 × 10⁻⁶ — three decades below the agreements this page reports, so it contributes nothing to them. The run refuses to be judged if that number ever grows.
Result
| Cells | Courant | Front (m) | vs exact 0.5 | Width (m) | vs exact 0.147773 | L1 | L∞ |
|---|---|---|---|---|---|---|---|
| 40 | 0.008 | 0.4961988 | −0.760 % | 0.1480803 | +0.208 % | 2.826 × 10⁻³ | 1.401 × 10⁻² |
| 80 | 0.016 | 0.4970152 | −0.597 % | 0.1470993 | −0.456 % | 2.508 × 10⁻³ | 1.110 × 10⁻² |
| 160 | 0.032 | 0.4956901 | −0.862 % | 0.1464228 | −0.914 % | 4.109 × 10⁻³ | 1.617 × 10⁻² |
| Time step (s) | Courant | Front (m) | vs exact | Width (m) | vs exact | L1 |
|---|---|---|---|---|---|---|
| 8 × 10⁻⁴ | 0.256 | 0.4774457 | −4.511 % | 0.1405640 | −4.879 % | 2.185 × 10⁻² |
| 4 × 10⁻⁴ | 0.128 | 0.4899376 | −2.012 % | 0.1448675 | −1.966 % | 9.605 × 10⁻³ |
| 2 × 10⁻⁴ | 0.064 | 0.4919144 | −1.617 % | 0.1452042 | −1.739 % | 7.800 × 10⁻³ |
The direction is the part that matters more than either number. Halving the time step moves the front closer to the exact position every time — 4.511 % to 2.012 % to 1.617 % — and the harness refuses the run if it ever does not, because a time integration that degrades under refinement is broken in a way no band on a single run would catch.
Why the width is the measurement
This is not a hypothetical failure mode. It is what this benchmark measured for an afternoon, and it is kept as a fixture.
| Quantity | Exact | Measured | Difference |
|---|---|---|---|
| Front position (m) | 0.5 | 0.4958508 | −0.830 % — inside every band |
| Front width (m) | 0.1477733 | 0.0445448 | −69.86 % — out by a factor of three |
| L1 over the profile | — | 6.123 × 10⁻² | 15 times the healthy run’s |
The cause is worth stating because it is a trap the case shape sets rather than a mistake anyone could see coming. A scalar case carries its diffusivity in two places: DT in constant/transportProperties, which scalarTransportFoam reads for its own transport equation, and D inside the scalar-transport function object the product writes into the control dictionary. The function object runs after the solver each step and writes its own answer over the solver’s. So for a case whose solver already transports the scalar, the field on disk is the function object’s and DT is inert.
Both are written from the same setting, so in the product’s own hands they agree and the answer is right. Edit one of them by hand — which is what a harness naturally does — and the run silently uses the other. That fixture is a DT of 0.06 against a function object still at the default 10⁻⁵, and everything else about the run looks healthy. The diffusivity is now set through the product, and both copies are asserted to carry it before anything is measured.
What this page does not establish
- One scheme pairing, not a survey. Everything here is Euler in time and
Gauss limitedLinear01 1on the divergence, which is what the product writes. limitedLinear01 is a limited — and therefore nonlinear — scheme, and a formal order is not expected from it in the first place. No other divergence scheme was run. - The velocity field is imposed, not solved. The flow is a uniform 2 m/s everywhere, so nothing here says anything about scalar transport through a velocity field the solver had to compute. What is exercised is the divergence term and the Laplacian beside it.
- The problem is one-dimensional, and the mesh is not. The domain is 4 cells across in each transverse direction with nothing to drive a variation, and the largest spread across a transverse row is 3.97 × 10⁻⁵ on the judged run. That is reported as a check that the case really is one-dimensional, not as a result about three-dimensional transport.
- A turbulence model would add a second diffusivity. The case is asked for as laminar through the product’s own setting, because the default eddy viscosity would put a diffusivity into the problem that the closed form knows nothing about. Nothing here is a statement about turbulent scalar transport.
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.
Other cases
- Two miscible liquids across a stepThe exact solution says three separate things — where the front is, how wide it is, and how much mixture there is — and they fail separately. A run can be right about two of them and a third too wide.
- How much of the answer is the meshEvery other page here asks how far the answer is from a reference. This one asks how much of it is the mesh — and puts an uncertainty on a result that has no reference at all.
- A manufactured solutionThe 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.
All validation cases · Written by the team building SHD Sim.