First-principles verification
Aragog ships a verification test suite that exercises the entropy solver against analytic limits, conservation laws, and SPIDER-parity references. The suite runs under pytest from the repository root and is the primary way to confirm a build is correct.
Test categories
The tests are organised by the layer they cover:
EOS unit tests (tests/test_entropy_pytest.py)
Exercise EntropyEOS and EntropyPhaseEvaluator:
- Property lookups (temperature, density, heat capacity, thermal expansivity, isentropic temperature gradient) at on-grid and off-grid \((P, S)\) points.
- Solidus and liquidus entropy lookups against the reference
solidus_P-S.datandliquidus_P-S.datfiles. - Smooth-clipped melt fraction \(\phi(P, S)\): monotone in \(S\), clamped to \([0, 1]\), \(C^\infty\) across the solidus and liquidus crossings.
- The two-stage SPIDER-parity blend (cubic-Hermite or tanh smoothing followed by
combine_matprop) for density, heat capacity, and viscosity. - The latent-blend \(c_p\) method against the linear blend.

Figure 1. JAX-traced bilinear interpolation of the EOS table reproduces the numpy reference to float-64 machine precision and supplies finite, non-zero derivatives via jax.jacrev at on- and off-grid \((P, S)\) points. The agreement is the prerequisite for the analytic Jacobian path used by CVODE.
Phase evaluator tests
Test the SPIDER-parity behaviour of EntropyPhaseEvaluator:
- Phase identification at \((P, S)\) in pure solid, pure liquid, and mushy regimes.
- Viscosity tanh blend across the rheological transition \(\phi_\mathrm{rheo}\).
- Gravitational-separation velocity in viscous and inviscid regimes.
- Pressure-dependent latent heat \(L(P) = T_\mathrm{liq}(P) [S_\mathrm{liq}(P) - S_\mathrm{sol}(P)]\).
Solver integration tests
Drive EntropySolver through short integrations and check the resulting profiles:
- Standalone grey-body cooling: a fully-molten initial state cools toward the equilibrium temperature without status flag failures.
- The
set_initial_entropy,set_initial_dSdr_cmb, andset_initial_core_temperaturepaths produce the expected state-vector layouts forquasi_steady,energy_balance, andbower2018modes. - The retry-ladder hooks (
get_current_dSdr_cmb,_atol_sf) round-trip cleanly.
Conservation tests (tests/test_entropy_verification.py)
The conservation tests are the strongest class of verification because they hold under any valid run, not just specific test scenarios:
- Energy conservation. \(E_\mathrm{th}\) change over a closed integration matches the time-integrated surface flux to within solver tolerance.
- Mass conservation. Mantle mass remains constant through phase changes.
- Grey-body cooling. Surface flux follows \(\varepsilon\sigma(T_\mathrm{top}^4 - T_\mathrm{eqm}^4)\) within tolerance once the radiative regime is established.

Figure 2. Mass-coordinate mesh Jacobian. The Newton solve that maps from mass coordinate to spatial radius (used when mass_coordinates = true) is monotone, smooth, and consistent with the analytic Adams-Williamson density profile. Departures here would propagate to mis-aligned cell volumes and a spurious mass-conservation residual in the integrator.

Figure 3. Radioactive heating \(H_\mathrm{radio}(t)\) for the bundled isotope cocktail (Ruedas 2017): present-day specific powers (\(^{40}\mathrm{K}\), \(^{232}\mathrm{Th}\), \(^{235}\mathrm{U}\), \(^{238}\mathrm{U}\)) decayed back to Solar System formation. Used as the source-term verification for the closed-mantle energy balance: the cumulative step_dE_Q_radio_J integral must match the time integral of \(\rho \cdot H_\mathrm{radio}\) over each call to within solver tolerance.
JAX path tests (tests/test_jax_entropy.py)
Verify that the JAX-backed EntropyEOS_JAX and compute_fluxes agree with the numpy path:
- EOS parity:
EntropyEOS_JAXreturns the same temperature, density, heat capacity, thermal expansivity, \(\partial T/\partial P|_S\), and conductivity asEntropyEOSon the same \((P, S)\) inputs. - Phase evaluator parity: the JAX two-stage blend matches
EntropyPhaseEvaluatorto within floating-point tolerance. - JIT and
vmapcompatibility: the JAX flux assembly is differentiable end-to-end withjax.grad/jax.jacrevand survives JIT compilation without recompilation hot-spots. - Boundary-copy tests:
dSdrboundary values copy from the nearest interior node (SPIDERic.c:450parity) instead of being extrapolated. - Solver parity: integrating the same problem with the JAX-RHS-driven CVODE path and the numpy RHS produces matching trajectories.
Numpy ↔ JAX RHS parity at representative magma-ocean states

Figure 4. Numpy \(\leftrightarrow\) JAX RHS parity on an 80-cell Earth mesh at three representative magma-ocean states. Top row, panels (a-c): per-cell absolute residual \(|\dot S_\text{numpy} - \dot S_\text{jax}|\) in J/kg/K/yr; the dotted line is \(\epsilon_\mathrm{mach}\,\max|\dot S|\) (the floating-point round-off floor expected on a single FP operation). Bottom row, panels (d-f): per-cell relative residual \(|\dot S_\text{numpy} - \dot S_\text{jax}|/|\dot S_\text{numpy}|\); the dotted line is float-64 machine epsilon. The three states are (a/d) initial condition (full magma ocean, \(S \approx 3900\) J/kg/K, dimensionally uniform), (b/e) mid-solidification (\(S = 3300 \to 3700\) J/kg/K from CMB to surface), (c/f) near-solid (\(S = 3000 \to 3300\) J/kg/K).
Per-panel residuals:
- Mid-solidification (b, e) and near-solid (c, f). Interior cells sit at the float-64 round-off floor. Maximum absolute residual \(\sim 3\times 10^{-7}\) J/kg/K/yr, dominated by a single spike at the surface boundary cell where the BC assembly is re-derived independently between numpy and JAX. Median relative error \(\le 3\times 10^{-5}\).
- Uniform-entropy IC (a, d). Absolute residual \(\sim 1\) J/kg/K/yr. Both paths run EOS lookup, lever-rule, and table interpolation on a uniform input; the arithmetic differs at the interpolation level. The relative error inflates because \(|\dot S|\) itself is small in equilibrium.
Numpy and JAX paths are independent re-derivations, not a bit-exact port.

Figure 5. Surface-temperature correction from the upper-thermal-boundary-layer parameterisation (solver/boundary.py::_utbl_tsurf). The Cardano-formula solution to \(b x^3 + x - T_\mathrm{int} = 0\) recovers \(T_\mathrm{surf} < T_\mathrm{int}\) for the entire physical range and matches the cubic-equation residual to floating-point tolerance. Verifies the analytic surface boundary condition used when param_utbl = true.
Mesh tests (tests/test_jax_mesh_gravity_fallback.py)
Verify that when the external mesh file (eos_method = 2) supplies an eos_gravity column, the JAX path interpolates the per-node gravity profile onto the basic grid rather than broadcasting a single scalar.
Running the verification suite
The standalone suite uses pytest:
# Run the entire suite
pytest tests/
# Run a specific category
pytest tests/test_entropy_pytest.py
pytest tests/test_entropy_verification.py
pytest tests/test_jax_entropy.py
# Mark-filtered subsets
pytest -m unit tests/ # unit tests only
pytest -m smoke tests/ # smoke tests only
The JAX-path tests require jax and equinox to be installed; tests are skipped (not failed) when the optional dependencies are missing. Some tests also require the SPIDER-format EOS table directory; they skip if FWL_DATA is unset or does not contain the expected files.
Physical constraints checked
The suite asserts the following physical invariants on every solver-driven test run:
- Temperature positivity. \(T > 0\) everywhere at all integration times. EOS lookups outside the table domain raise rather than silently extrapolating.
- Melt fraction bounds. \(0 \le \phi \le 1\) via the smooth-clip; the assertion runs at every staggered node.
- Pressure positivity and monotonicity. \(P(r)\) is positive and decreases monotonically from CMB to surface.
- Entropy gradient continuity. \(\partial S/\partial r\) and the resulting fluxes are \(C^0\) across the solidus and liquidus crossings (the smoothing functions enforce this).
- Energy conservation. Within the integrator tolerance, \(\Delta E_\mathrm{th}\) matches the time-integrated boundary fluxes plus internal heating sources.
- Mass conservation. Total mantle mass is preserved under gravitational separation and chemical mixing (the segregation flux is divergence-free in mass).