From 887bd8fc76757b2b5fdc4d0a936e40772f1e160f Mon Sep 17 00:00:00 2001 From: priyanshlunia <40486607+priyanshlunia@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:34:06 -0400 Subject: [PATCH 1/2] DOCS - IMPROVEMENT - Fix TOML annotation drift (headers, inline comments, deprecated keys) Restore the shared annotation convention across the config-style TOMLs that drifted since the standardization sweep: add missing 2-10 line purpose headers (gal-resistive examples, nuzero fixture, four regression cases), complete the missing inline descriptions (SLAYER section, canonical DIIID ideal example, Solovev kinetic examples), strip decorative dividers from the SLAYER regression case, correct the inaccurate psiedge truncation comment, and remove deprecated keys (mat_flag, ode_flag, power_bp/b/r, truncate_at_dW_peak, force_wv_symmetry) that the loader warns about and discards. Parsed values are unchanged except the removed no-op keys. Co-Authored-By: Claude Fable 5 --- examples/DIIID-like_SLAYER_example/gpec.toml | 36 ++++++++----------- .../gpec.toml | 8 ++++- .../gpec.toml | 7 +++- examples/DIIID-like_ideal_example/gpec.toml | 8 ++--- .../Solovev_kinetic_NTV_example/gpec.toml | 1 - .../gpec.toml | 4 +-- regression-harness/cases/diiid_slayer_n1.toml | 14 +++----- .../cases/gal_resistive_diiid.toml | 6 ++++ .../cases/gal_resistive_pe.toml | 6 ++++ .../cases/solovev_kinetic_nuzero.toml | 6 ++++ .../gpec.toml | 11 ++++-- 11 files changed, 65 insertions(+), 42 deletions(-) diff --git a/examples/DIIID-like_SLAYER_example/gpec.toml b/examples/DIIID-like_SLAYER_example/gpec.toml index 2374ea514..46a52e049 100644 --- a/examples/DIIID-like_SLAYER_example/gpec.toml +++ b/examples/DIIID-like_SLAYER_example/gpec.toml @@ -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 @@ -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) @@ -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 @@ -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 diff --git a/examples/DIIID-like_gal_resistive_example/gpec.toml b/examples/DIIID-like_gal_resistive_example/gpec.toml index 62b7930f5..e296d0585 100644 --- a/examples/DIIID-like_gal_resistive_example/gpec.toml +++ b/examples/DIIID-like_gal_resistive_example/gpec.toml @@ -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 @@ -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) diff --git a/examples/DIIID-like_gal_resistive_pe_example/gpec.toml b/examples/DIIID-like_gal_resistive_pe_example/gpec.toml index a6c6612a9..2d2cbe86b 100644 --- a/examples/DIIID-like_gal_resistive_pe_example/gpec.toml +++ b/examples/DIIID-like_gal_resistive_pe_example/gpec.toml @@ -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 @@ -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) diff --git a/examples/DIIID-like_ideal_example/gpec.toml b/examples/DIIID-like_ideal_example/gpec.toml index a36d21271..451a7a81f 100644 --- a/examples/DIIID-like_ideal_example/gpec.toml +++ b/examples/DIIID-like_ideal_example/gpec.toml @@ -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 diff --git a/examples/Solovev_kinetic_NTV_example/gpec.toml b/examples/Solovev_kinetic_NTV_example/gpec.toml index f1d3ee96c..7812d4f9b 100644 --- a/examples/Solovev_kinetic_NTV_example/gpec.toml +++ b/examples/Solovev_kinetic_NTV_example/gpec.toml @@ -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) diff --git a/examples/Solovev_kinetic_calculated_example/gpec.toml b/examples/Solovev_kinetic_calculated_example/gpec.toml index ac27543cb..149f40af7 100644 --- a/examples/Solovev_kinetic_calculated_example/gpec.toml +++ b/examples/Solovev_kinetic_calculated_example/gpec.toml @@ -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] diff --git a/regression-harness/cases/diiid_slayer_n1.toml b/regression-harness/cases/diiid_slayer_n1.toml index 13167efe5..c1009fcbf 100644 --- a/regression-harness/cases/diiid_slayer_n1.toml +++ b/regression-harness/cases/diiid_slayer_n1.toml @@ -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" @@ -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/ω/γ @@ -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" @@ -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" @@ -150,9 +149,6 @@ label = "SLAYER enabled flag" noise_threshold = 0 order = 90 -# --------------------------------------------------------------------- -# Runtime -# --------------------------------------------------------------------- [quantities.runtime] h5path = "" type = "runtime" diff --git a/regression-harness/cases/gal_resistive_diiid.toml b/regression-harness/cases/gal_resistive_diiid.toml index 9ec61109e..6811d113a 100644 --- a/regression-harness/cases/gal_resistive_diiid.toml +++ b/regression-harness/cases/gal_resistive_diiid.toml @@ -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)" diff --git a/regression-harness/cases/gal_resistive_pe.toml b/regression-harness/cases/gal_resistive_pe.toml index 7a26d7563..c2b059d7e 100644 --- a/regression-harness/cases/gal_resistive_pe.toml +++ b/regression-harness/cases/gal_resistive_pe.toml @@ -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)" diff --git a/regression-harness/cases/solovev_kinetic_nuzero.toml b/regression-harness/cases/solovev_kinetic_nuzero.toml index e51efe873..078b9c363 100644 --- a/regression-harness/cases/solovev_kinetic_nuzero.toml +++ b/regression-harness/cases/solovev_kinetic_nuzero.toml @@ -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." diff --git a/test/test_data/regression_solovev_kinetic_nuzero/gpec.toml b/test/test_data/regression_solovev_kinetic_nuzero/gpec.toml index 3aab75163..ca6eeb003 100644 --- a/test/test_data/regression_solovev_kinetic_nuzero/gpec.toml +++ b/test/test_data/regression_solovev_kinetic_nuzero/gpec.toml @@ -1,3 +1,8 @@ +# Solovev n=1 collisionless-kinetic smoke fixture (nutype = "zero"). +# Intentionally coarse (mpsi = 16, delta_m = 0) so the full-run test suite can +# exercise the real-x-space collisionless energy-integral path end-to-end; the +# test asserts only finiteness/sign of et[1]. The mode-converged eigenvalue is +# tracked separately by the solovev_kinetic_nuzero regression case. [Equilibrium] eq_type = "sol" # Type of the input 2D equilibrium file jac_type = "pest" # Coordinate system (hamada, pest, boozer, equal_arc, park, custom) @@ -40,11 +45,11 @@ 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 [KineticForces] -nutype = "zero" # Collisionless operator — isolates the real-x-space collisionless energy integral (issue #281) +nutype = "zero" # Collisionless operator — isolates the real-x-space collisionless energy integral # Solovev analytic equilibrium parameters (eq_type = "sol"); see SolovevConfig in src/Equilibrium. [SOL_INPUT] From af540d57248c3d4803cceda6d0cf93e991ab30cc Mon Sep 17 00:00:00 2001 From: priyanshlunia <40486607+priyanshlunia@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:34:14 -0400 Subject: [PATCH 2/2] DOCS - NEW FEATURE - Add pre-commit checks enforcing TOML annotation conventions Four declarative pygrep hooks (no external scripts) lint the covered TOMLs on commit: header block present, no decorative dividers, inline description on every variable line in examples/ and test/test_data/, and no deprecated config keys (pattern mirrors _DEPRECATED_FFS_KEYS/_DEPRECATED_EQUIL_KEYS). Document the enforcement in toml-conventions.md, codify the regression-case exemption for the repetitive quantity schema keys, and list the new checks in the developer setup guide. Co-Authored-By: Claude Fable 5 --- .pre-commit-config.yaml | 26 ++++++++++++++++++++++++++ docs/development/toml-conventions.md | 24 +++++++++++++++++++++++- docs/src/set_up.md | 2 ++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bce5d8f93..54e179385 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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)$ diff --git a/docs/development/toml-conventions.md b/docs/development/toml-conventions.md index e5d3b3316..5ee2c3d5d 100644 --- a/docs/development/toml-conventions.md +++ b/docs/development/toml-conventions.md @@ -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. @@ -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 +``` diff --git a/docs/src/set_up.md b/docs/src/set_up.md index 38c9dcebf..a7499ea78 100644 --- a/docs/src/set_up.md +++ b/docs/src/set_up.md @@ -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:**