PKR Core
Technical Explanation 4 min read

Checking the numbers: a solver measured against cases with known answers

An effective-property solver that returns a number is easy to build. One that returns the right number is the only kind worth using. Three arrangements have answers you can write down in closed form, so those are the ones to send through the API first.

  • Analyze Properties
Cross section of the two-slab test structure, showing the low-conductivity half above the high-conductivity half.

Why it matters

HTTP 200 means the request was well formed and the solver finished. It says nothing about whether the value is right. For a real microstructure there is nothing to compare against — that is the whole reason you are computing it. So the check has to happen on structures where the answer is already known.

Three of them cover a useful span. A block of one material must return that material’s conductivity: no geometry, no excuse. Two slabs stacked and measured across the stack are resistances in series, so the effective value is the harmonic mean. The same two slabs measured along the layers are in parallel, so it is the arithmetic mean. Series and parallel are the two extremes any two-phase mixture must fall between, which makes them the right bracket to test.

The fixtures

These are not generated from a recipe. The voxels are written directly and posted inline, so no packing algorithm and no random seed sits between the geometry and the answer. A 16³ grid is enough — the arrangement, not the resolution, is what carries the analytic result.

Cross section of the two-slab fixture: the lower half in one material, the upper half in another, with a flat interface between them.
The two-slab fixture. Measured top to bottom the layers are in series; measured left to right they are in parallel. Same voxels, two different known answers.

The low material is set to 1 W/m·K and the high one to 100 — a factor of 100 apart, because a small contrast would let a wrong model look right. Series gives 2 / (1/1 + 1/100) = 1.98020; parallel gives (1 + 100) / 2 = 50.5.

What the API returned

Twelve calls answered, in 8.7 seconds all told.

CaseExpectedMeasuredRelative error
Homogeneous block, z76.99864−0.019 %
Two slabs in series (z)1.980201.98001−0.0097 %
Two slabs in parallel (x)50.550.49074−0.018 %
Two slabs in parallel (y)50.550.49074−0.018 %

Every case lands within 0.02 % of the closed-form answer, and every one lands slightly below it. A consistent sign is more informative than the magnitude: this is a discretisation bias from the boundary layer of voxels, not scatter. It also means the solver is not silently normalising its output to hit the analytic value.

Two more checks have no closed form but still have a right answer. Generating the same recipe with the same seed twice returned solid fractions identical to the last bit (0.12677001953125 both times) — reproducibility is not approximate here. And a randomly packed sphere structure has no preferred axis, so its three directional conductivities should agree.

DirectionMeasured (W/m·K)Against the three-axis mean
x4.4558+4.18 %
y4.2561−0.49 %
z4.1198−3.68 %

An 8 % spread between the fastest and slowest axis, on a structure that has no reason to prefer one. That is not solver error so much as the structure itself: a 32³ box holding a few thousand spheres is a small sample, and a single percolating path that happens to run one way tilts the result. It is a useful number to know before reading meaning into a 5 % difference between two design candidates.

What this does not prove

Agreement on layered geometry does not transfer to a random packing. The analytic cases test the solver’s bookkeeping — units, direction handling, how a boundary is imposed, how two phases combine — and they test it sharply. They say nothing about whether the reduced graph model captures the transport physics of a real packing.

The point of keeping these as a standing run rather than a one-off is regression, not reassurance. The expected values come from physics, not from a previous run, so they cannot drift to match a bug. If a change to the solver moves the homogeneous block off 7, that shows up the same day.

Try it in PKR Core.

Open PKR Core