Skip to content

Commit 16f51bf

Browse files
Merge pull request #12 from FormingWorlds/tl/interior-refactor
This branch is a rewrite of Aragog around a pressure-entropy formulation. Specific entropy at staggered nodes becomes the prognostic variable; T, ρ, φ, c_p, α, and (∂T/∂P)_S are read from SPIDER-format P-S tables generated by Zalmoxis from PALEOS. The earlier P-T path is gone, the mush zone is now resolved continuously across the solidus and liquidus crossings, and the energy budget is re-derived around the lever-rule blender (latent-augmented c_p, harmonic-mean ρ). Production integration. SUNDIALS CVODE drives a JAX-derived analytic Jacobian as the default; numpy and JAX paths agree to float-64 machine precision on the production mid-solidification and near-solid regimes. Four core-BC modes with SPIDER bit-parity at energy_balance, a per-call mass-weighted ΔΦ cap as a SUNDIALS root function for the mush regime, scipy Radau / BDF as fall-backs. AragogRunner makes Aragog a first-class citizen in the PROTEUS coupled loop, with a documented hf_row contract, a Zalmoxis mesh hand-off, and Newton-solve mass coordinates. Energy bookkeeping rewritten end to end. Per-call J integrals replace a state-mass residual that grew with mantle cooling, and a solver-residual diagnostic closes at ~1e-7 of total cooling on production trajectories. Configuration, tests, docs. Parameters.from_file dispatches on file suffix (tomllib for .toml, typed_configparser for .cfg) so quote and inline-comment leakage on TOML inputs no longer corrupts strings; [scalings] is hard-rejected on both paths. The test surface is rebuilt to PROTEUS Ecosystem Testing Standard v1: 4-marker scheme with --strict-markers, marker-validation gate, 95% coverage gate with one-way ratchet, Codecov nightly upload, auto-refreshing test-count badges. Currently 575 tests at 96.81% nightly coverage. Documentation restructured per Diátaxis, verification figure suite under docs/figures/vv/, HPC-cluster guidance in installation, tutorial that runs end-to-end against the bundled EOS-tables tarball. CI. ci_tests.yml runs the unit tier on ubuntu + macos × py3.12 / 3.13, scoped to push:main + pull_request:main with concurrency cancellation; nightly.yml runs the full marker set with the 95% gate enforced and uploads coverage to Codecov on schedule and on every push to main. Python floor tightened to >=3.12 to match the matrix. Aragog is now the production interior-energetics backend of PROTEUS, with the regression infrastructure to keep it that way.
2 parents 40bf227 + 39826f6 commit 16f51bf

228 files changed

Lines changed: 43839 additions & 7112 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/proteus-tests.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../.claude/rules/proteus-tests.md

.codecov.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
target: auto
6+
threshold: 1%
7+
patch:
8+
default:
9+
target: auto
10+
threshold: 1%
11+
12+
comment:
13+
layout: "reach, diff, flags, files"
14+
require_changes: true
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"schemaVersion": 1,
3+
"label": "integration tests",
4+
"message": "65",
5+
"color": "blue"
6+
}

.github/badges/tests-total.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"schemaVersion": 1,
3+
"label": "tests",
4+
"message": "575",
5+
"color": "blue"
6+
}

.github/badges/tests-unit.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"schemaVersion": 1,
3+
"label": "unit tests",
4+
"message": "514",
5+
"color": "blue"
6+
}

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../CLAUDE.md

.github/workflows/ci_tests.yml

Lines changed: 164 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2-
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3-
4-
name: Tests for Aragog
1+
name: Aragog CI Test Suite
52

63
on:
74
push:
@@ -17,41 +14,177 @@ on:
1714
- ready_for_review
1815
workflow_dispatch:
1916

17+
# Push and PR CI: unit tests only, on ubuntu + macos x py 3.12 / 3.13.
18+
# Targets <5 min wall. Slow tests (full multi-Myr CVODE solves) live in
19+
# the scheduled nightly workflow `nightly.yml` instead, alongside coverage
20+
# enforcement against the 95% floor. Running the slow suite or coverage
21+
# instrumentation on every push burns ~10 min CI time per commit and gives
22+
# no bug-finding signal that the unit suite doesn't already cover.
23+
#
24+
# Trigger scoping: push fires only on main (release-merge sanity check),
25+
# pull_request fires on PRs into main (feature-branch validation). With
26+
# both, every commit on a PR runs exactly one matrix sweep instead of two
27+
# (push + pull_request would otherwise both fire on a feature-branch
28+
# push that has an open PR).
29+
#
30+
# Uses micromamba + conda-forge for the env so SUNDIALS, gfortran, and the
31+
# scikits-odes binary stack come prebuilt instead of being compiled from
32+
# source against stock apt/brew (which lag the SUNDIALS major version
33+
# scikits-odes-sundials 3.x requires).
34+
2035
permissions:
21-
actions: write
22-
contents: write
36+
contents: read
2337

24-
jobs:
25-
build:
38+
# Cancel superseded runs on the same ref so a stack of feature-branch
39+
# pushes does not pile up macOS runners (free-tier concurrency is tight).
40+
# The base ref keeps push to main and pull_request to main in separate
41+
# groups so the latter cannot cancel a release sanity check.
42+
concurrency:
43+
group: ci-tests-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
44+
cancel-in-progress: true
2645

46+
jobs:
47+
test-fast:
48+
name: Aragog-check (${{ matrix.os }}, py${{ matrix.python-version }})
2749
strategy:
2850
fail-fast: false
2951
matrix:
30-
python-version: ["3.11", "3.12","3.13"]
3152
os: [ubuntu-latest, macos-latest]
53+
python-version: ["3.12", "3.13"]
3254

3355
runs-on: ${{ matrix.os }}
3456

57+
defaults:
58+
run:
59+
shell: bash -el {0}
60+
3561
steps:
36-
- uses: actions/checkout@v5
37-
38-
- name: Set up Python ${{ matrix.python-version }}
39-
uses: actions/setup-python@v5
40-
with:
41-
python-version: ${{ matrix.python-version }}
42-
43-
- name: Install dependencies
44-
run: |
45-
python -m pip install --upgrade pip
46-
python -m pip install flake8 pytest pytest-cov pytest-dependency
47-
if [ -f pyproject.toml ]; then pip install .; fi
48-
49-
- name: Lint with flake8
50-
run: |
51-
# stop the build if there are Python syntax errors or undefined names
52-
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
53-
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
54-
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
55-
56-
- name: Test with pytest
57-
run: pytest --cov-report term --cov-report markdown-append:$GITHUB_STEP_SUMMARY --cov=aragog tests/
62+
- uses: actions/checkout@v5
63+
with:
64+
fetch-depth: 0
65+
66+
- name: Set up conda env (sundials from conda-forge)
67+
uses: mamba-org/setup-micromamba@v2
68+
with:
69+
environment-name: aragog-test
70+
create-args: >-
71+
python=${{ matrix.python-version }}
72+
sundials
73+
condarc: |
74+
channels:
75+
- conda-forge
76+
77+
- name: Install pip-only test deps (scikits-odes-sundials builds against conda SUNDIALS)
78+
run: |
79+
pip install pytest pytest-xdist pytest-dependency ruff
80+
# Install scikits-odes-sundials directly (skip the scikits-odes
81+
# metapackage). This avoids scikits-odes-daepack, which is a
82+
# transitive Fortran dep with f2py incompatibility with numpy
83+
# 2.3+ and needs gfortran on macOS. aragog imports CVODE
84+
# directly from scikits_odes_sundials.cvode and has no use for
85+
# the DAE solvers in the metapackage.
86+
pip install scikits-odes-sundials jax equinox
87+
88+
- name: Install aragog
89+
run: |
90+
pip install -e .
91+
92+
- name: Fetch SPIDER EOS test data
93+
run: |
94+
mkdir -p /tmp/aragog-test-data
95+
curl -sL -o /tmp/spider_eos.tar.gz \
96+
https://github.com/FormingWorlds/aragog/releases/download/test-data-v1/spider_eos_test_data.tar.gz
97+
tar xzf /tmp/spider_eos.tar.gz -C /tmp/aragog-test-data/
98+
echo "ARAGOG_TEST_EOS_DIR=/tmp/aragog-test-data/spider_eos" >> $GITHUB_ENV
99+
100+
- name: Validate test markers
101+
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' }}
102+
run: bash tools/validate_test_structure.sh
103+
104+
- name: Lint with Ruff
105+
run: |
106+
ruff check src/ tests/
107+
ruff format --check src/ tests/
108+
109+
- name: Pre-flight fail_under ratchet check
110+
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' }}
111+
env:
112+
BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }}
113+
run: |
114+
# Reject PRs that lower [tool.coverage.report] fail_under below
115+
# the value on the base branch. The 95% ceiling is the maximum
116+
# ratchet per ecosystem policy; this check enforces the
117+
# one-way-only direction (down forbidden) so the gate cannot be
118+
# weakened to land an under-tested change.
119+
git fetch --no-tags --depth=1 origin "$BASE_REF" || true
120+
python - <<'PY'
121+
import os
122+
import pathlib
123+
import subprocess
124+
import sys
125+
import tomllib
126+
127+
base_ref = os.environ.get('BASE_REF', 'main')
128+
out = subprocess.run(
129+
['git', 'show', f'origin/{base_ref}:pyproject.toml'],
130+
capture_output=True,
131+
text=True,
132+
check=False,
133+
)
134+
if out.returncode != 0:
135+
print(f'Could not read pyproject.toml from origin/{base_ref}; skipping check.')
136+
sys.exit(0)
137+
138+
base = tomllib.loads(out.stdout)
139+
head = tomllib.loads(pathlib.Path('pyproject.toml').read_text())
140+
141+
def fail_under(d):
142+
try:
143+
return float(d['tool']['coverage']['report']['fail_under'])
144+
except KeyError:
145+
return None
146+
147+
base_v = fail_under(base)
148+
head_v = fail_under(head)
149+
print(f'fail_under: base={base_v} head={head_v}')
150+
151+
# Base has no [tool.coverage.report].fail_under: nothing to
152+
# ratchet against. The first PR that introduces the gate is
153+
# allowed regardless of head_v.
154+
if base_v is None:
155+
print(
156+
f'origin/{base_ref} has no [tool.coverage.report].fail_under; '
157+
'no ratchet to enforce. PASS.'
158+
)
159+
sys.exit(0)
160+
161+
# Head removed the gate that base had: that's a decrease.
162+
if head_v is None:
163+
print(
164+
f'ERROR: head removed [tool.coverage.report].fail_under '
165+
f'(base had {base_v}). The gate is one-way only.'
166+
)
167+
sys.exit(1)
168+
169+
if head_v < base_v:
170+
print(
171+
f'ERROR: fail_under decreased ({head_v} < {base_v}). '
172+
'The coverage gate is one-way only; ask a maintainer if a '
173+
'downward adjustment is required.'
174+
)
175+
sys.exit(1)
176+
print('OK: fail_under is non-decreasing.')
177+
PY
178+
179+
- name: Run unit tests
180+
run: |
181+
pytest -m "unit and not slow" -n auto -o "addopts=" \
182+
-o "junit_family=legacy" --junitxml=junit.xml
183+
184+
- name: Upload test results to Codecov
185+
if: ${{ !cancelled() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' }}
186+
uses: codecov/test-results-action@v1
187+
with:
188+
token: ${{ secrets.CODECOV_TOKEN }}
189+
slug: FormingWorlds/Aragog
190+
files: junit.xml

.github/workflows/docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Documentation
1+
name: Docs
22
on:
33
push:
44
branches:

.github/workflows/nightly.yml

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
schedule:
55
# 02:30 UTC daily (offset from Zalmoxis 02:00 UTC to spread runner load).
66
- cron: "30 2 * * *"
7+
# Refresh the canonical (full-suite) Codecov upload on every push to main
8+
# so the badge tracks the merged state of the repo, not just the last cron.
9+
push:
10+
branches: [main]
711
# Manual trigger for ad-hoc runs (e.g. before cutting a release, or to
812
# verify the suite on a feature branch via
913
# `gh workflow run "Aragog nightly" -r <branch>`).
@@ -15,36 +19,85 @@ on:
1519
permissions:
1620
contents: read
1721

18-
# Nightly: full unit + slow suite with coverage instrumentation and the
19-
# 85% floor enforced via [tool.coverage.report].fail_under in pyproject.toml.
20-
# Excluded from push and PR CI because the slow tier alone takes ~110 s and
21-
# coverage instrumentation pushes the full run to ~10 min on a 2-vCPU
22-
# runner, over the 5 min budget for fast CI.
22+
# Nightly: full unit + smoke + slow suite with coverage instrumentation
23+
# and the 95% floor enforced via [tool.coverage.report].fail_under in
24+
# pyproject.toml. Excluded from push and PR CI because the slow tier
25+
# alone takes ~110 s and coverage instrumentation pushes the full run
26+
# to ~10 min on a 2-vCPU runner, over the 5 min budget for fast CI.
27+
#
28+
# `smoke` is included because the integration tests in
29+
# test_entropy_advanced.py, test_entropy_verification.py and
30+
# test_entropy_solver_integration.py exercise the production
31+
# EntropySolver.solve() path and the JAX RHS at full physical scale —
32+
# without them, entropy_solver.py / cvode_jax.py / jax/solver.py sit
33+
# at ~33 % / ~51 % / ~63 % coverage respectively.
34+
#
35+
# Uses micromamba + conda-forge for the env so SUNDIALS, gfortran, and the
36+
# scikits-odes binary stack come prebuilt.
2337
jobs:
2438
nightly:
2539
name: Aragog-nightly (ubuntu-latest)
2640
runs-on: ubuntu-latest
27-
timeout-minutes: 30
41+
# 90 min: full suite is ~12 min on a 12-core Mac Studio, ~55 min on
42+
# the 2-vCPU runner with --quiet + xdist. Leaves headroom for JAX
43+
# warmup, conda env cache misses, and the additional smoke tests
44+
# added for the 95 % coverage gate.
45+
timeout-minutes: 90
46+
defaults:
47+
run:
48+
shell: bash -el {0}
2849
steps:
2950
- name: Checkout repository
3051
uses: actions/checkout@v5
3152
with:
3253
fetch-depth: 0
3354

34-
- name: Set up Python 3.12
35-
uses: actions/setup-python@v5
55+
- name: Set up conda env (sundials from conda-forge)
56+
uses: mamba-org/setup-micromamba@v2
3657
with:
37-
python-version: "3.12"
58+
environment-name: aragog-nightly
59+
create-args: >-
60+
python=3.12
61+
sundials
62+
condarc: |
63+
channels:
64+
- conda-forge
3865
39-
- name: Install dependencies
66+
- name: Install pip-only test deps (scikits-odes-sundials builds against conda SUNDIALS)
4067
run: |
41-
python -m pip install --upgrade pip
42-
pip install .
4368
pip install pytest pytest-cov pytest-xdist pytest-dependency
69+
# Direct install of scikits-odes-sundials only; aragog imports
70+
# CVODE directly from scikits_odes_sundials.cvode and skips the
71+
# daepack-pulling metapackage. See ci_tests.yml for details.
72+
# diffrax is the JAX-native ODE solver used by
73+
# tests/test_jax_entropy.py::TestJAXSolverIntegration; without
74+
# it those 8 smoke tests fail with ModuleNotFoundError.
75+
pip install scikits-odes-sundials jax equinox diffrax
4476
45-
- name: Run unit + slow tests with coverage
77+
- name: Install aragog
4678
run: |
47-
pytest -m "unit or slow" -o "addopts=" \
79+
pip install -e .
80+
81+
- name: Fetch SPIDER EOS test data
82+
run: |
83+
mkdir -p /tmp/aragog-test-data
84+
curl -sL -o /tmp/spider_eos.tar.gz \
85+
https://github.com/FormingWorlds/aragog/releases/download/test-data-v1/spider_eos_test_data.tar.gz
86+
tar xzf /tmp/spider_eos.tar.gz -C /tmp/aragog-test-data/
87+
echo "ARAGOG_TEST_EOS_DIR=/tmp/aragog-test-data/spider_eos" >> $GITHUB_ENV
88+
89+
- name: Run unit + smoke + slow tests with coverage
90+
run: |
91+
# -n auto runs xdist workers; coverage's parallel mode collects
92+
# per-worker .coverage.* files which pytest-cov auto-combines
93+
# before the report. Without xdist the serial run hit the
94+
# 30-minute timeout at 23 % of the 271-test suite, dominated by
95+
# JAX-traced functions where line tracing is the bottleneck.
96+
# -ra prints a short summary of failed/error/skipped tests at
97+
# the end of the run; without it --quiet suppresses the
98+
# FAILED list entirely if there are no other final messages,
99+
# making post-mortem on cancelled runs near-impossible.
100+
pytest -m "unit or smoke or integration or slow" -n auto -o "addopts=" -ra \
48101
--cov=src/aragog --cov-report=term-missing --cov-report=xml
49102
50103
- name: Upload coverage to Codecov

0 commit comments

Comments
 (0)