Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c3e5314
docs: chi_tor is Fitzpatrick's chi_phi (momentum viscosity), not heat
d-burg Aug 15, 2026
f027b4f
REGRESSION - NEW FEATURE - Golden values: committed pins with derived…
d-burg Aug 15, 2026
fa66933
REGRESSION - BUG FIX - Fail the golden check on orphaned golden entries
d-burg Aug 15, 2026
2c57945
REGRESSION - BUG FIX - Harden the golden mechanism per adversarial re…
d-burg Aug 15, 2026
bab563a
REGRESSION - IMPROVEMENT - Disambiguate the Δ' variants; pin SLAYER i…
d-burg Aug 15, 2026
44c7f9e
REGRESSION - NEW FEATURE - Fixed-Q probe of the SLAYER inner-layer Δ(Q)
d-burg Aug 16, 2026
cb0df35
DOCS - BUG FIX - Backtick the chi_tor unit label so Documenter stops …
d-burg Aug 17, 2026
bfdbde2
REGRESSION - NEW FEATURE - Pin the declared integrator as Delta-prime…
d-burg Aug 17, 2026
fb8755b
Regression - MINOR - Drop the gal_flag provenance pin now that it is …
d-burg Aug 18, 2026
f01c6a4
Regression - FEATURE - Pin the del_s layer width against Fitzpatrick …
d-burg Aug 18, 2026
ce422f0
Regression - MINOR - Golden values for the Fitzpatrick del_s reproduc…
d-burg Aug 18, 2026
25d6826
Regression - BUGFIX - Guard non-finite tolerances in the golden array…
d-burg Aug 28, 2026
41debc2
Regression - MINOR - Tighten the computed-case provenance comments
d-burg Aug 28, 2026
e4131da
Regression - BUGFIX - Report a crashed golden-check run as a crash, n…
d-burg Aug 31, 2026
cc13288
Regression - TEST - Unit-test the golden comparison logic on syntheti…
d-burg Aug 31, 2026
f00317e
Regression - MINOR - Trim the golden-harness case and template comments
d-burg Sep 23, 2026
67a2c40
Regression - BUGFIX - Close the golden gates that a hand edit or a re…
d-burg Sep 23, 2026
fb01d88
Regression - TEST - Regenerate the Fitzpatrick golden on the branch w…
d-burg Sep 23, 2026
4f45706
Regression - MINOR - Say a dropped runtime pin is intentional in the …
d-burg Sep 23, 2026
ea060a8
Regression - BUGFIX - Drop stale measured evidence on a re-pin and sh…
d-burg Sep 25, 2026
7630099
Regression - DOCS - Document golden-value mode and drop the claim tha…
d-burg Sep 25, 2026
fc1b66c
Regression - MINOR - Declare the probe grid topological and trim gold…
d-burg Sep 25, 2026
2271d25
Regression - BUGFIX - Keep skipping checksum-typed values when buildi…
d-burg Sep 25, 2026
25e94c2
Regression - BUGFIX - Close the journal-mode pragma query so later tr…
d-burg Sep 25, 2026
5684f6f
Regression - FEATURE! - Guard golden re-pins against exceeding moves,…
d-burg Sep 25, 2026
3d85ed7
Regression - API! - Require an explicit tolerance class on every regr…
d-burg Sep 25, 2026
2d8099e
CI - FEATURE - Run the regression harness unit tests on harness changes
d-burg Sep 25, 2026
99170b7
Regression - MINOR - Parse --accept-exceeding with the other CLI options
d-burg Sep 25, 2026
b5e92f4
InnerLayer.SLAYER - DOCS - Move the chi_tor docstring correction to i…
d-burg Sep 25, 2026
c129b9a
Merge remote-tracking branch 'origin/develop' into HEAD
d-burg Oct 3, 2026
33478cd
Regression - MINOR - Declare the tolerance class on the quantities de…
d-burg Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/regression-harness-unit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Regression Harness Unit Tests

# Unit tests for the regression harness's own logic (golden values, case loading), on
# synthetic data only. They load the harness project, not GPEC, so they run in minutes.
on:
pull_request:
branches:
- main
- develop
paths:
- 'regression-harness/**'
- '.github/workflows/regression-harness-unit.yaml'
push:
branches:
- main
- develop
paths:
- 'regression-harness/**'
- '.github/workflows/regression-harness-unit.yaml'

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
unit:
name: regression-harness unit tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Julia
uses: julia-actions/setup-julia@v2
with:
version: '1.11'

- name: Instantiate the harness environment
run: julia --project=regression-harness -e 'using Pkg; Pkg.instantiate()'

- name: Run the harness unit tests
run: julia --project=regression-harness regression-harness/test/runtests.jl
18 changes: 17 additions & 1 deletion docs/development/regression-harness.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,23 @@ re-run once — those are exactly the entries whose provenance cannot be establi

If the two refs in a comparison ran under different thread counts, the report flags it.

## Golden values

`--refs` comparisons say whether a number changed; golden values say whether it still matches a committed, reviewed reference. Each case may have a golden file at `regression-harness/golden/<case>.toml` holding every pinned quantity with its `rtol`, `atol`, tolerance class and the evidence behind the tolerance.

```bash
regress --check --cases slayer_dels_fitzpatrick # working tree vs the committed goldens
regress --update-golden --cases slayer_dels_fitzpatrick --reason "..." # re-pin from a fresh run
```

- **Classes** come only from the `class` key that every `[quantities.*]` block in a case file must declare, never from the golden file or the quantity's name; a missing or unknown class makes the case fail to load. `topological` (exact), `equilibrium_scalar`, `physics_converged` gate; `diagnostic` and `unconverged` are reported only.
- **Tolerances** are either the provisional class default (`tolerance_basis = "class-default (provisional)"`) or `"measured"`, which requires a recorded `plateau_drift` or `platform_spread` no larger than `rtol`. No harness tool measures these yet, so every committed golden currently gates on provisional defaults, and `--check` labels those rows `provisional`.
- **`atol`** must be 0 on a gating entry unless its golden value contains an exact zero (a zero scalar, or an array element that is zero throughout); the relative test is the gate everywhere else. Loading and saving both enforce this.
- **Re-pinning** requires `--cases` and `--reason`, refuses a working tree with uncommitted tracked changes or with untracked files under `examples/`, `src/` or `regression-harness/cases/`, and prints an old → new line per moved quantity, flagging any move the old tolerance would have failed.
- **A move beyond the old tolerance** makes `--update-golden` refuse the whole re-pin unless `--accept-exceeding` is given. When it is, the entry loses its recorded evidence and becomes provisional, with a tolerance no looser than before, and the golden file's `[meta.exceeded.<name>]` table records the previous version's value, the deviation and the old `rtol`/`atol`, so the reviewer sees each accepted move in the git diff.
- A failing `--check` is fixed by explaining the physics or fixing the regression, never by widening `rtol` or `atol`. The unit tests for this logic run with `julia --project=regression-harness regression-harness/test/runtests.jl` and need no GPEC build.

## Exit status

- `0` — every run completed (and, with `--fail-on-change`, nothing changed)
- `1` — a run failed, or a quantity changed under `--fail-on-change`
- `1` — a run failed, a quantity changed under `--fail-on-change`, or a `--check` golden comparison failed or gated nothing
3 changes: 2 additions & 1 deletion docs/development/toml-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ modify them. **Do not invent a new convention** — match what the existing exam
files** — `examples/DIIID-like_ideal_example/gpec.toml` is the canonical reference for the
common sections. Exception: in `regression-harness/cases/*.toml` the repeated
`[quantities.*]` schema keys (`h5path`, `type`, `extract`, `label`, `noise_threshold`,
`order`) are developer metadata and do **not** need inline comments — a case file needs
`order`, and the required golden tolerance `class`) are developer metadata and do **not**
need inline comments (a `class` that is not the obvious one for its quantity should say why) — a case file needs
the header plus an informative block comment per quantity group instead.
3. **Section comments only when informative.** No comment is required above a section. Keep a
block comment only when it carries real information (e.g. the Solovev `[Wall]` note on why a
Expand Down
9 changes: 9 additions & 0 deletions regression-harness/cases/diiid_error_field.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extract = "all_real"
label = "EFC overlap per kAt"
noise_threshold = 1e-10
order = 10
class = "physics_converged"

[quantities.efc_torque_full]
h5path = "ErrorFields/NTV/torque_full_per_kat2"
Expand All @@ -24,6 +25,7 @@ extract = "all_real"
label = "EFC whole-field NTV torque per kAt² [N·m]"
noise_threshold = 1e-8
order = 11
class = "physics_converged"

[quantities.efc_torque_residual]
h5path = "ErrorFields/NTV/torque_residual_per_kat2"
Expand All @@ -32,6 +34,7 @@ extract = "all_real"
label = "EFC residual-field NTV torque per kAt² [N·m]"
noise_threshold = 1e-8
order = 12
class = "physics_converged"

# The torque-versus-rotation table: reference rotation, the adaptive shift grid and the residual torque on it.
[quantities.efc_omega_reference]
Expand All @@ -41,6 +44,7 @@ extract = "all_real"
label = "EFC reference rotation ω_ref [rad/s]"
noise_threshold = 1e-6
order = 13
class = "physics_converged"

[quantities.efc_rotation_shift]
h5path = "ErrorFields/NTV/RotationScan/rotation_shift"
Expand All @@ -49,6 +53,7 @@ extract = "all_real"
label = "EFC rotation-scan shifts [rad/s]"
noise_threshold = 1e-6
order = 14
class = "physics_converged"

[quantities.efc_torque_residual_scan]
h5path = "ErrorFields/NTV/RotationScan/torque_residual"
Expand All @@ -57,6 +62,7 @@ extract = "all_real"
label = "EFC residual torque against rotation [N·m/kAt²]"
noise_threshold = 1e-8
order = 15
class = "physics_converged"

# Anchors of the stages feeding the couplings.
[quantities.dominant_singular_values]
Expand All @@ -66,6 +72,7 @@ extract = "all_real"
label = "dominant-coupling singular values"
noise_threshold = 1e-8
order = 20
class = "physics_converged"

[quantities.ef_delta_nominal]
h5path = "ErrorFields/CoilSensitivities/DominantMode/delta_nominal"
Expand All @@ -74,10 +81,12 @@ extract = "abs_first"
label = "|delta_nominal| of coil set 1"
noise_threshold = 1e-10
order = 21
class = "physics_converged"

[quantities.runtime]
h5path = ""
type = "runtime"
extract = "value"
label = "Runtime (s)"
order = 99
class = "diagnostic"
20 changes: 20 additions & 0 deletions regression-harness/cases/diiid_multi_n.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extract = "real_first"
label = "total energy Re(et[1])"
noise_threshold = 1e-10
order = 10
class = "physics_converged"

[quantities.et_all]
h5path = "ForceFreeStates/FreeBoundaryStability/eigenmode_energies"
Expand All @@ -24,6 +25,7 @@ extract = "all_complex"
label = "total energy (all)"
noise_threshold = 1e-10
order = 20
class = "physics_converged"

# Integration
[quantities.nstep]
Expand All @@ -33,6 +35,7 @@ extract = "value"
label = "ODE steps (saved)"
noise_threshold = 0
order = 30
class = "diagnostic"

# Mode space
[quantities.mpert]
Expand All @@ -42,6 +45,7 @@ extract = "value"
label = "mpert"
noise_threshold = 0
order = 40
class = "topological"

[quantities.npert]
h5path = "Info/npert"
Expand All @@ -50,6 +54,7 @@ extract = "value"
label = "npert"
noise_threshold = 0
order = 41
class = "topological"

[quantities.mode_n]
h5path = "PerturbedEquilibrium/mode_n"
Expand All @@ -58,6 +63,7 @@ extract = "checksum"
label = "toroidal mode label of each mode index (checksum)"
noise_threshold = 0
order = 42
class = "physics_converged"

# Control-surface response, one block per n (b~ space)
[quantities.permeability_diag]
Expand All @@ -67,6 +73,7 @@ extract = "diagonal_complex"
label = "permeability diagonal P~_ii (all n blocks)"
noise_threshold = 1e-8
order = 50
class = "physics_converged"

[quantities.forcing_b_rootarea]
h5path = "PerturbedEquilibrium/forcing_b_root_area"
Expand All @@ -75,6 +82,7 @@ extract = "all_complex"
label = "forcing b~ (root-area-weighted), n = 1, 2"
noise_threshold = 1e-10
order = 51
class = "physics_converged"

[quantities.response_b_rootarea]
h5path = "PerturbedEquilibrium/response_b_root_area"
Expand All @@ -83,6 +91,7 @@ extract = "all_complex"
label = "response b~ (root-area-weighted), n = 1, 2"
noise_threshold = 1e-8
order = 52
class = "physics_converged"

# Perturbed equilibrium energies and the Σ_n torque
[quantities.pe_plasma_energy]
Expand All @@ -92,6 +101,7 @@ extract = "value"
label = "PE plasma energy (sum over n)"
noise_threshold = 1e-10
order = 60
class = "physics_converged"

[quantities.pe_vacuum_energy]
h5path = "PerturbedEquilibrium/Energies/vacuum_energy"
Expand All @@ -100,6 +110,7 @@ extract = "value"
label = "PE vacuum energy (sum over n)"
noise_threshold = 1e-10
order = 61
class = "physics_converged"

[quantities.pe_surface_energy]
h5path = "PerturbedEquilibrium/Energies/surface_energy"
Expand All @@ -108,6 +119,7 @@ extract = "value"
label = "PE surface energy (sum over n)"
noise_threshold = 1e-10
order = 62
class = "physics_converged"

[quantities.pe_toroidal_torque]
h5path = "PerturbedEquilibrium/Energies/toroidal_torque"
Expand All @@ -116,6 +128,7 @@ extract = "value"
label = "PE toroidal torque sum_n -2n Im(Phi_n' Lambda_nn^-1 Phi_n)/4"
noise_threshold = 1e-12
order = 63
class = "physics_converged"

# Singular coupling: one row per resonant (surface, n); integer-q surfaces appear once per n
[quantities.rational_n]
Expand All @@ -125,6 +138,7 @@ extract = "all_real"
label = "resonant n per row (rows sorted by psi across n)"
noise_threshold = 0
order = 70
class = "physics_converged"

[quantities.rational_m]
h5path = "PerturbedEquilibrium/SingularCoupling/rational_m"
Expand All @@ -133,6 +147,7 @@ extract = "all_real"
label = "resonant m per row"
noise_threshold = 0
order = 71
class = "physics_converged"

[quantities.resonant_area_weighted_field_all]
h5path = "PerturbedEquilibrium/SingularCoupling/resonant_area_weighted_field"
Expand All @@ -141,6 +156,7 @@ extract = "all_complex"
label = "resonant area-weighted field b^r per row"
noise_threshold = 1e-8
order = 72
class = "physics_converged"

[quantities.island_half_width]
h5path = "PerturbedEquilibrium/SingularCoupling/island_half_width"
Expand All @@ -149,6 +165,7 @@ extract = "all_real"
label = "island half-widths per row"
noise_threshold = 1e-8
order = 73
class = "physics_converged"

[quantities.chirikov_parameter]
h5path = "PerturbedEquilibrium/SingularCoupling/chirikov_parameter"
Expand All @@ -157,6 +174,7 @@ extract = "all_real"
label = "Chirikov parameter per row"
noise_threshold = 1e-8
order = 74
class = "physics_converged"

[quantities.delta_prime_all]
h5path = "PerturbedEquilibrium/SingularCoupling/Delta_prime"
Expand All @@ -165,6 +183,7 @@ extract = "all_complex"
label = "forcing-driven Delta' per row"
noise_threshold = 1e-8
order = 75
class = "physics_converged"

# Runtime
[quantities.runtime]
Expand All @@ -174,3 +193,4 @@ extract = "value"
label = "Runtime (s)"
noise_threshold = 0.0
order = 999
class = "diagnostic"
Loading
Loading