Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,29 @@ repos:
hooks:
- id: julia-formatter
name: Format Julia code

# Config-style TOML annotation conventions (docs/development/toml-conventions.md).
# pygrep hooks fail when the pattern matches, so each entry matches a violation.
- repo: local
hooks:
- id: toml-header-block
name: 'TOML conventions: file starts with a # header block'
language: pygrep
entry: '\A(?!#[^\n]*\n#)'
args: [--multiline]
files: ^(examples/.*\.toml|test/test_data/.*\.toml|regression-harness/cases/.*\.toml)$
- id: toml-no-decorative-dividers
name: 'TOML conventions: no decorative comment dividers'
language: pygrep
entry: '^#\s*[-=*_~#]{6,}\s*$'
files: ^(examples/.*\.toml|test/test_data/.*\.toml|regression-harness/cases/.*\.toml)$
- id: toml-inline-annotations
name: 'TOML conventions: every variable line has a # description'
language: pygrep
entry: '^[A-Za-z0-9_]+\s*=\s*[^#]*$'
files: ^(examples/.*\.toml|test/test_data/.*\.toml)$
- id: toml-no-deprecated-keys
name: 'TOML conventions: no deprecated config keys'
language: pygrep
entry: '^(mer_flag|force_wv_symmetry|ode_flag|cyl_flag|mat_flag|power_bp|power_b|power_r|power_rc)\s*='
files: ^(examples/.*\.toml|test/test_data/.*\.toml)$
24 changes: 23 additions & 1 deletion docs/development/toml-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ modify them. **Do not invent a new convention** — match what the existing exam
`PerturbedEquilibriumControl`, `SolovevConfig`, `TJAnalyticConfig`, `KineticForcesControl`)
and keep it to one terse line. **Use the same description for the same variable across all
files** — `examples/DIIID-like_ideal_example/gpec.toml` is the canonical reference for the
common sections.
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
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
conformal wall is needed). Never add decorative section dividers.
Expand All @@ -34,3 +37,22 @@ modify them. **Do not invent a new convention** — match what the existing exam
6. **`Project.toml`-family files are exempt.** `Project.toml`, `docs/Project.toml`,
`regression-harness/Project.toml`, and `.JuliaFormatter.toml` are machine-managed; do not
inline-annotate dependency UUIDs or `[compat]` entries.

## Enforcement

Four `pygrep` hooks in `.pre-commit-config.yaml` (no external scripts — a pygrep hook
fails when its pattern matches a violation) lint-check the covered TOMLs on commit:

- `toml-header-block` — the file must open with a `#` header block (all covered files).
- `toml-no-decorative-dividers` — no `# ----`-style divider lines (all covered files).
- `toml-inline-annotations` — every `key = value` line carries a trailing `# description`
(`examples/*` and `test/test_data/*` only, per the rule-2 exception above).
- `toml-no-deprecated-keys` — no deprecated config keys; its key list mirrors
`_DEPRECATED_FFS_KEYS`/`_DEPRECATED_EQUIL_KEYS` in `src/GeneralizedPerturbedEquilibrium.jl`,
so extend the hook's pattern whenever a key is deprecated there.

Run them manually with:

```bash
pre-commit run --all-files
```
2 changes: 2 additions & 0 deletions docs/src/set_up.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ The repository uses pre-commit hooks to maintain code quality. They run automati
- Remove trailing whitespace and fix line endings
- Validate YAML/TOML syntax
- Prevent accidentally committing large files (>5MB)
- Check config-style TOML annotation conventions (header block, inline `# description`
comments, no deprecated keys — see `docs/development/toml-conventions.md`)

**Installation:**

Expand Down
36 changes: 15 additions & 21 deletions examples/DIIID-like_SLAYER_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
eq_filename = "../DIIID-like_ideal_example/TkMkr_D3Dlike_Hmode.geqdsk" # Path to equilibrium file
eq_type = "efit" # Type of the input 2D equilibrium file
jac_type = "hamada" # Coordinate system (hamada, pest, boozer, equal_arc)
power_bp = 0 # Poloidal field power exponent for Jacobian
power_b = 0 # Toroidal field power exponent for Jacobian
power_r = 0 # Major radius power exponent for Jacobian
grid_type = "log_asymptotic" # Radial grid packing type
psilow = 1e-4 # Lower limit of normalized flux coordinate
psihigh = 0.9995 # Upper limit of normalized flux coordinate
Expand All @@ -34,11 +31,9 @@ equal_arc_wall = true # Equal arc length distribution of nodes on wall
[ForceFreeStates]
force_termination = true # Run FFS + SLAYER, skip PerturbedEquilibrium
local_stability_flag = true # Perform local stability analysis (Mercier and ballooning) across the ψ profile
mat_flag = true # Construct coefficient matrices for diagnostic purposes
ode_flag = true # Integrate ODEs for stability of the internal long-wavelength mode (must be true for GPEC)
vac_flag = true # Compute plasma, vacuum, and total energies for free-boundary modes

psiedge = 0.99 # Edge dW scan band: dW(ψ) computed for ψ ∈ [psiedge, psilim], integration truncated at peak
psiedge = 0.99 # Edge dW scan band: diagnostic dW(ψ) computed for ψ ∈ [psiedge, psilim]; integration domain set by qhigh / psihigh / dmlim
qlow = 1.02 # Integration initiated at q determined by min(q0, qlow)...
qhigh = 1e3 # Integration terminated at q limit determined by min(qa, qhigh)...
sing_start = 0 # Start integration at the sing_start'th rational from the axis (psilow)
Expand All @@ -60,7 +55,6 @@ ucrit = 1e4 # Maximum fraction of solutions allowed before re
use_parallel = true # Run parallel FM-propagator BVP path (unlocks singular/delta_prime_matrix)
parallel_threads = 1 # serial/bit-deterministic BVP — keeps the regression Δ' (and hence γ) reproducible
populate_dense_xi = false # No PerturbedEquilibrium here; the dense EL pass has no consumer (auto-disabled under force_termination anyway). SLAYER needs only delta_prime_matrix from the parallel BVP.
truncate_at_dW_peak = false # Edge-dW scan stays diagnostic; integration domain set by qhigh / psihigh / dmlim
set_psilim_via_dmlim = true # TRUE for diverted geqdsks — q → ∞ at separatrix, so dmlim truncation avoids the δW kink instability at negligible domain cost
dmlim = 0.2 # Truncate integration at (last_rational_q + dmlim) / n

Expand All @@ -71,26 +65,26 @@ dmlim = 0.2 # Truncate integration at (last_rational_q + dmli
# kinetic file shared with the kinetic/NTV physics. Per-surface (uncoupled)
# analysis: the headline is the unstable 2/1; the validity gate drops surfaces
# with no real root (e.g. the 5/1, whose Δ' BVP yields a huge uncancellable Δ').
enabled = true
inner_model = "slayer_fitzpatrick"
scan_mode = "amr"
coupling_mode = "uncoupled"
dc_type = "none"
mu_i = 2.0
zeff = 1.0
enabled = true # Run the SLAYER tearing-mode analysis
inner_model = "slayer_fitzpatrick" # Inner-layer Δ(Q) model: "slayer_fitzpatrick", "ggj_shooting", or "ggj_galerkin"
scan_mode = "amr" # Q-plane scan strategy: "amr" (adaptive refinement) or "brute_force"
coupling_mode = "uncoupled" # "uncoupled" (per-surface) or "coupled" (multi-surface determinant)
dc_type = "none" # Critical-Δ offset selector: "none", "lar", "rfitzp", or "toroidal"
mu_i = 2.0 # Ion mass in proton-mass units (2.0 = deuterium)
zeff = 1.0 # Effective charge
chi_perp = 1.0 # fallback only; the kinetic file supplies χ⊥(ψ)
chi_tor = 1.0 # fallback only; the kinetic file supplies χ_φ(ψ)
pole_threshold_adaptive = true # SLAYER |Δ| spans many orders; adapt to 10·median(|Δ|)
filter_above_poles = true
filter_outside_re = true
filter_above_poles = true # Discard roots above the highest pole γ
filter_outside_re = true # Condition the above-pole filter on the +γ step exiting the Re(Δ)=0 contour loop
polish_roots = true # refine each root to the true zero; enables the validity gate
store_scan = false # per-surface scans omitted from HDF5 (uncoupled has one per surface)
# Standardized kinetic-profile file (GPEC HDF5 kinetic schema), shared with the
# sibling ideal example; carries n_e, T_e, T_i, omega_E, chi_e (χ⊥), chi_phi (χ_φ).
profile_file = "../DIIID-like_ideal_example/TkMkr_D3Dlike_Hmode_kinetic.h5"
profile_file = "../DIIID-like_ideal_example/TkMkr_D3Dlike_Hmode_kinetic.h5" # Kinetic-profile file (relative to the run dir), required when SLAYER is enabled

[SLAYER.scan_grid]
Q_re_range = [-2.0, 2.0]
Q_im_range = [-0.5, 3.0]
nre = 41
nim = 31
Q_re_range = [-2.0, 2.0] # Scan box in the normalized Q plane, Re(Q) axis
Q_im_range = [-0.5, 3.0] # Scan box in the normalized Q plane, Im(Q) axis
nre = 41 # Grid resolution along the Re(Q) axis
nim = 31 # Grid resolution along the Im(Q) axis
8 changes: 7 additions & 1 deletion examples/DIIID-like_gal_resistive_example/gpec.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# DIII-D-like H-mode resistive-MHD example: outer-region singular Galerkin Δ′ solve.
# n=1 tearing stability on the bundled geqdsk: the gal solver computes the Δ′
# matching matrix (PEST-3 convention) plus rpec coil-response columns (delta_coil).
# The ldp radial grid packs points toward the rational surfaces; psihigh = 0.993
# stays clear of the separatrix. No forcing and no PerturbedEquilibrium stage —
# see the sibling DIIID-like_gal_resistive_pe_example for the coil-driven continuation.
[Equilibrium]
eq_filename = "TkMkr_D3Dlike_Hmode.geqdsk" # Path to equilibrium file
eq_type = "efit" # Type of the input 2D equilibrium file
Expand Down Expand Up @@ -26,7 +32,7 @@ equal_arc_wall = true # Equal arc length distribution of nodes on wall
local_stability_flag = true # Perform local stability analysis (Mercier and ballooning) across the ψ profile
vac_flag = true # Compute plasma, vacuum, and total energies for free-boundary modes

psiedge = 0.99 # Edge dW scan band: dW(ψ) computed for ψ ∈ [psiedge, psilim], integration truncated at peak
psiedge = 0.99 # Edge dW scan band: diagnostic dW(ψ) computed for ψ ∈ [psiedge, psilim]; integration domain set by qhigh / psihigh / dmlim
qlow = 1.02 # Integration initiated at q determined by min(q0, qlow)...
qhigh = 1e3 # Integration terminated at q limit determined by min(qa, qhigh)...
sing_start = 0 # Start integration at the sing_start'th rational from the axis (psilow)
Expand Down
7 changes: 6 additions & 1 deletion examples/DIIID-like_gal_resistive_pe_example/gpec.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# DIII-D-like H-mode DRIVEN resistive example: gal Δ′ + inner-layer matching → PerturbedEquilibrium.
# Extends the sibling DIIID-like_gal_resistive_example with DIII-D C-coil forcing
# (n=1 cosine phasing): the gal-matched resistive ξ is fed into PerturbedEquilibrium
# for coil-driven singular coupling — island half-widths, Chirikov overlap, resonant flux.
# Same equilibrium and Galerkin settings as the sibling; differences start at [ForcingTerms].
[Equilibrium]
eq_filename = "TkMkr_D3Dlike_Hmode.geqdsk" # Path to equilibrium file
eq_type = "efit" # Type of the input 2D equilibrium file
Expand Down Expand Up @@ -26,7 +31,7 @@ equal_arc_wall = true # Equal arc length distribution of nodes on wall
local_stability_flag = true # Perform local stability analysis (Mercier and ballooning) across the ψ profile
vac_flag = true # Compute plasma, vacuum, and total energies for free-boundary modes

psiedge = 0.99 # Edge dW scan band: dW(ψ) computed for ψ ∈ [psiedge, psilim], integration truncated at peak
psiedge = 0.99 # Edge dW scan band: diagnostic dW(ψ) computed for ψ ∈ [psiedge, psilim]; integration domain set by qhigh / psihigh / dmlim
qlow = 1.02 # Integration initiated at q determined by min(q0, qlow)...
qhigh = 1e3 # Integration terminated at q limit determined by min(qa, qhigh)...
sing_start = 0 # Start integration at the sing_start'th rational from the axis (psilow)
Expand Down
8 changes: 4 additions & 4 deletions examples/DIIID-like_ideal_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ electron = false # Ion-only NTV
nutype = "harmonic" # Collision operator
f0type = "maxwellian" # Equilibrium distribution
moment = "pressure" # Pressure-moment NTV torque
atol_xlmda = 1e-9 # Inner pitch/energy integration tolerances
rtol_xlmda = 1e-5
write_outputs_to_HDF5 = true
verbose = true
atol_xlmda = 1e-9 # Absolute tolerance for inner pitch + energy integrations
rtol_xlmda = 1e-5 # Relative tolerance for inner pitch + energy integrations
write_outputs_to_HDF5 = true # Write outputs to the HDF5 file
verbose = true # Enable verbose logging
1 change: 0 additions & 1 deletion examples/Solovev_kinetic_NTV_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ kinetic_factor = 0.0 # Scaling of kinetic matrices (0 = ideal path; >0
eulerlagrange_tolerance = 1e-7 # Relative tolerance for ODE integration of Euler-Lagrange equations
singfac_min = 1e-4 # Fractional distance from rational q at which ideal jump enforced
ucrit = 1e3 # Column-norm threshold that triggers solution renormalization
force_wv_symmetry = true # Enforce symmetry of the vacuum energy matrix
save_interval = 3 # Save every Nth ODE step (1=all). Always saves near rational surfaces.

# Δ' BVP + parallel integration (see ForceFreeStatesControl docstring for details)
Expand Down
4 changes: 2 additions & 2 deletions examples/Solovev_kinetic_calculated_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ kinetic_factor = 1.0 # Full-strength kinetic matrices (the "calculated"
eulerlagrange_tolerance = 1e-7 # Relative tolerance for ODE integration of Euler-Lagrange equations
singfac_min = 1e-4 # Fractional distance from rational q at which ideal jump enforced
ucrit = 1e3 # Maximum fraction of solutions allowed before re-normalized
write_outputs_to_HDF5 = true
verbose = false
write_outputs_to_HDF5 = true # Write outputs to the HDF5 file
verbose = false # Enable verbose logging

# Solovev analytic equilibrium parameters (eq_type = "sol"); see SolovevConfig in src/Equilibrium.
[SOL_INPUT]
Expand Down
14 changes: 5 additions & 9 deletions regression-harness/cases/diiid_slayer_n1.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# Regression case: DIII-D-like H-mode n=1 SLAYER tearing-mode growth rates.
# Uncoupled per-surface analysis with the AMR Q-plane scan; tracks the per-surface
# layer parameters and the extracted roots (Q, ω, γ) for the inner three rationals.
# Each [quantities.*] block names an HDF5 path in the run output, how to extract it,
# and the noise floor below which a difference is treated as zero.
[case]
name = "diiid_slayer_n1"
description = "DIII-D-like H-mode equilibrium, n=1, SLAYER tearing-mode analysis (uncoupled per-surface, AMR, validity-gated)"
example_dir = "examples/DIIID-like_SLAYER_example"

# ---------------------------------------------------------------------
# Per-surface SLAYER layer parameters (geometry + dimensionless)
# ---------------------------------------------------------------------
[quantities.slayer_ising]
h5path = "slayer/per_surface/ising"
type = "real_vector"
Expand Down Expand Up @@ -86,7 +89,6 @@ label = "SLAYER iota_e"
noise_threshold = 1e-12
order = 19

# ---------------------------------------------------------------------
# Tearing eigenvalue (coupled mode → length 1). The headline deliverable:
# a real, nonzero growth rate on a realistic equilibrium. Root-extraction
# is sensitive to the AMR cell topology and ODE solver, so the Q/ω/γ
Expand All @@ -96,7 +98,6 @@ order = 19
# surfaces only (2/1, 3/1, 4/1) via `first_3`: the Δ'/γ contour search is
# numerically unreliable on the outermost surfaces (e.g. 5/1, 6/1, 7/1 near the
# edge), so those are deliberately not golden-tracked.
# ---------------------------------------------------------------------
[quantities.slayer_Q_re]
h5path = "slayer/roots/Q_root_real"
type = "real_vector"
Expand Down Expand Up @@ -139,9 +140,7 @@ label = "SLAYER no_root flags [2/1,3/1,4/1]"
noise_threshold = 0
order = 34

# ---------------------------------------------------------------------
# Settings (catches accidental config drift)
# ---------------------------------------------------------------------
[quantities.slayer_enabled]
h5path = "slayer/enabled"
type = "int_scalar"
Expand All @@ -150,9 +149,6 @@ label = "SLAYER enabled flag"
noise_threshold = 0
order = 90

# ---------------------------------------------------------------------
# Runtime
# ---------------------------------------------------------------------
[quantities.runtime]
h5path = ""
type = "runtime"
Expand Down
6 changes: 6 additions & 0 deletions regression-harness/cases/gal_resistive_diiid.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Regression case: DIII-D-like n=1 outer-region singular Galerkin Δ′ solve.
# Tracks the resonant-surface inventory, the PEST-3 Δ matching-matrix diagonal
# (the physics-meaningful tearing Δ′), norms of the full outer Δ′ matrix and the
# rpec coil-response (Δ_coil) block, and the inner-layer matching outputs.
# Each [quantities.*] block names an HDF5 path in the run output, how to extract it,
# and the noise floor below which a difference is treated as zero.
[case]
name = "gal_resistive_diiid"
description = "DIII-D-like, n=1, RDCON outer-region Galerkin Δ′ with rpec coil columns (delta_coil)"
Expand Down
6 changes: 6 additions & 0 deletions regression-harness/cases/gal_resistive_pe.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Regression case: DIII-D-like n=1 DRIVEN/RPEC pipeline — gal-matched resistive ξ
# fed into PerturbedEquilibrium with DIII-D C-coil forcing. Tracks the finite
# per-rational-surface singular-coupling scalars (island half-widths, Chirikov
# overlap, resonant flux, penetrated field, Δ′).
# Each [quantities.*] block names an HDF5 path in the run output, how to extract it,
# and the noise floor below which a difference is treated as zero.
[case]
name = "gal_resistive_pe"
description = "DIII-D-like, n=1, DRIVEN/RPEC: gal-matched resistive ξ → PerturbedEquilibrium (coil-driven singular coupling)"
Expand Down
6 changes: 6 additions & 0 deletions regression-harness/cases/solovev_kinetic_nuzero.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Regression case: Solovev n=1 collisionless kinetic run (nutype = "zero").
# Reuses the Solovev_kinetic_calculated_example deck via [overrides] at a
# mode-converged band; locks the collisionless real-x-space energy-integral
# eigenvalue plus equilibrium and singular-surface anchors.
# Each [quantities.*] block names an HDF5 path in the run output, how to extract it,
# and the noise floor below which a difference is treated as zero.
[case]
name = "solovev_kinetic_nuzero"
description = "Solovev, n=1, kinetic_source=calculated, nutype=zero (collisionless energy integral, issue #281), mode-converged band (delta_m=8, mpert=32). Reuses the calculated example deck via override; et[1]≈2.333-2.174i with the pressure-consistent kinetic.dat."
Expand Down
Loading
Loading