HBF Scientific Lab

Reproducible case study · 01

Cross-code validation of the equilibrium solver

The Torus Lab Observatory solves a fixed-boundary Grad–Shafranov problem in the browser. This case study tests that solver against an independent Python/SciPy implementation that never reads its output, reports every numerical error, and gives you the commands to reproduce each figure on your own machine in under a minute.

independent implementation manufactured solution second-order scheme fixed boundary only

The question

A numerical kernel that runs inside a web page is easy to distrust, and reasonably so. The question this study answers is narrow and falsifiable:

Does the browser fixed-boundary Grad–Shafranov solver produce the same flux field as an independently written solver, to what tolerance, and does that agreement survive a change of JavaScript engine?

Three things are measured. First, whether the discretisation converges at its designed order against an exact manufactured solution. Second, whether the converged field agrees node-for-node with a separate Python implementation of the same problem. Third, whether the whole benchmark suite reproduces when the engine underneath it changes — because a result that only holds on one runtime is not a result.

Method

Problem

The Grad–Shafranov operator is solved on a rectangular domain R ∈ [1.5, 2.5] m, Z ∈ [−0.5, 0.5] m with constant Dirichlet flux ψ = 0 on the complete boundary, discretised with second-order centred differences on an n × n grid. The cylindrical −ψR/R term is included explicitly rather than absorbed.

ψRR − ψR/R + ψZZ = S(R, Z)

Two source terms

The independent implementation

The reference is a separate Python program using SciPy sparse direct solves and a Newton iteration with an analytic Jacobian. It shares no code with the browser kernel, is written against the problem statement rather than against the JavaScript, and reads no JavaScript output — its first line records that constraint. The browser kernel uses nonlinear point relaxation, a different algorithm reaching the same discrete system.

Agreement is measured as the maximum absolute nodal difference in ψ over the whole grid, plus an exact-match test on the located magnetic axis. Both are compared against declared tolerances fixed before the comparison ran.

Result 1 · Convergence against the manufactured solution

Halving the grid spacing should reduce the error by a factor of four for a second-order scheme. It does.

Independent Python solve, both grids. Errors are against the exact manufactured solution.
GridMax residual (T)Max |ψ − ψexact| (Wb/rad)Ratio
n = 173.130829 × 10⁻¹⁴3.241862 × 10⁻⁴—
n = 331.394440 × 10⁻¹³8.093579 × 10⁻⁵4.0056

observed order of accuracy = log₂(3.241862 × 10⁻⁴ / 8.093579 × 10⁻⁵) = 2.0020

The browser kernel is held to the same standard by benchmark NUM-11, whose acceptance interval for the observed order was fixed at [1.95, 2.05] before the run. Its measured relative residual on the fine grid is 4.616059 × 10⁻¹², against a tolerance of 1 × 10⁻¹¹, and its boundary error is exactly 0 Wb/rad.

Result 2 · Node-for-node agreement between the two codes

Benchmark NUM-12 compares the browser field against the Python field at every interior node, for all four cases. The declared tolerance is a maximum nodal difference below 2 × 10⁻¹¹ Wb/rad with an exact magnetic-axis match.

Cross-code flux comparison. Reference residuals are from the Python solve; relative residuals are the browser kernel's own convergence measure.
CaseGridReference residual (T)Browser rel. residualMax nodal Δψ (Wb/rad)Axis
Manufacturedn = 173.130829 × 10⁻¹⁴4.616059 × 10⁻¹²8.100326 × 10⁻¹³exact
Manufacturedn = 331.394440 × 10⁻¹³9.026586 × 10⁻¹²1.821057 × 10⁻¹²exact
Profile polynomialn = 171.075529 × 10⁻¹⁶4.014248 × 10⁻¹²7.137086 × 10⁻¹⁵exact
Profile polynomialn = 335.342948 × 10⁻¹⁶—below toleranceexact

The largest disagreement anywhere in the comparison is 1.82 × 10⁻¹² Wb/rad, an order of magnitude inside the declared tolerance and comparable to the accumulated round-off of the two different algorithms. The located magnetic axis matches exactly in every case, at R = 2.0 m, Z = 0.0 m for the n = 33 manufactured grid.

What this establishes: two independently written solvers, in two languages, using two different algorithms, agree on this discrete problem to within round-off. That is a software correctness result for the discretisation and its implementation. It is not a statement about physical equilibria — see the limitations below.

Result 3 · Reproducibility across engines

A benchmark that only reproduces on the machine that produced it is not evidence. The full 14-case suite was regenerated on a different JavaScript engine — Node v22.22.2 against the v24.19.0 that produced the shipped record — with every source file verified byte-identical first.

All 14 benchmarks, Node v24.19.0 → v22.22.2. Deviation is the largest relative change in any recorded numeric field, ignoring timestamps and environment.
IDBenchmarkReproductionMax relative deviation
NUM-03Analytic field component and flux oraclebit-identical0
NUM-04Circular geometry closed-form constantsbit-identical0
NUM-05Flux preservation along adaptive field linesdrift3.7 × 10⁻²
NUM-06Signed winding over complete poloidal turnsdrift3.7 × 10⁻²
NUM-08Filament polygon vs circular-loop axis fielddrift2.3 × 10⁻⁹
NUM-13Poincaré count and event localisationdrift3.5 × 10⁻¹
NUM-14Boris energy and phase after 100 gyroperiodsbit-identical0
NUM-16Boris trajectory timestep refinementbit-identical0
NUM-23Profile Jacobian, neutrality and energy inventorybit-identical0
NUM-19Synthetic Maxwellian quadrature oraclebit-identical0
NUM-22NRL cm-based to SI radiation equivalencebit-identical0
NUM-11Manufactured GS residual and spatial refinementbit-identical0
NUM-12Fixed-boundary cross-code flux comparisonbit-identical0
NUM-25Run package reopens exact canonical recordsdrift5.9 × 10⁻⁴

Nine of fourteen benchmarks reproduce bit-identically. All fourteen still pass. Both equilibrium benchmarks — the subject of this study — are in the bit-identical set.

What the five drifting cases actually are

The strict --check mode fails on a different engine by design, because it hashes the complete report including the environment block. That is the correct behaviour for a release gate and the reason this study reports the per-record comparison instead.

The Python side reproduces exactly

The independent reference was also regenerated on newer libraries — numpy 2.4.4 and scipy 1.17.1 against the bundled numpy 2.3.5 and scipy 1.17.0. Every ψ value in all four cases came back bitwise identical, maximum |Δψ| = 0. Runtime was 0.7 s.

Limitations

These are the boundaries of the claim, stated so they can be checked rather than assumed.

Code and data

Everything needed to reproduce this study ships inside the package you are reading. No download, account or network access is required.

The benchmark record carries its own sourceInventory: sixteen files with their SHA-256 hashes, recorded at the moment the benchmarks ran. Verifying those hashes proves the numbers on this page belong to the code in this package.

Reproduce it yourself

From the extracted package directory. Nothing writes outside it.

1 · Confirm the code under test is unmodified

Every file listed in the benchmark's source inventory should match its recorded hash.

sha256sum -c SHA256SUMS --quiet

2 · Re-run the independent Python solver

Requires Python 3 with numpy and scipy. It writes reference.json beside itself, so copy it elsewhere first if you want to keep the original for comparison.

cp docs/observatory-equilibrium-reference/reference.py /tmp/
cd /tmp && python3 reference.py

Compare the psi_WbPerRad arrays in your output against the bundled file. Result 3 above reports zero difference across a numpy and scipy version change.

3 · Re-run the browser kernel benchmarks

Requires Node.js. No npm install — the runner imports only Node built-ins and the package's own modules.

node tools/verify_observatory.mjs

This rewrites docs/v748-verification/numerical-benchmarks.json. Copy the original aside first, then compare the two files field by field to reproduce the table in Result 3. Expect the generatedAt, executionTimestamp and environment fields to differ.

4 · Strict gate

node tools/verify_observatory.mjs --check

Passes only on the recording environment. On any other engine it fails by design, which is what Result 3 measures.

5 · In the browser

Open Torus Lab and use the Observatory equilibrium controls to solve the same fixed-boundary problem interactively, then export the run package to inspect the numerical record directly.

Falsify this

Run it against your code

If you maintain an equilibrium code, the most useful thing you can do with this page is disagree with it. The problem statement is fully specified above and the fixtures are two small files.

Hardip Bhesaniya · info@hbfnetwork.deAlso reachable at hardipbhesaniya03@gmail.com