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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,5 @@ repos:
- 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|reform_eq_with_psilim|use_riccati|use_parallel|parallel_threads|populate_dense_xi|gal_flag|nstep|diagnose_ca|diagnose|ksing|power_bp|power_b|power_r|power_rc|use_galgrid|local_stability_flag)\s*='
entry: '^(mer_flag|force_wv_symmetry|ode_flag|cyl_flag|mat_flag|reform_eq_with_psilim|use_riccati|use_parallel|parallel_threads|populate_dense_xi|gal_flag|nstep|diagnose_ca|diagnose|ksing|power_bp|power_b|power_r|power_rc|use_galgrid|moment|eq_out|theta_out|xlmda_out|eqpsi_out|save_records|gpec_file|fnml_flag|ellip_flag|diag_flag|force_xialpha|fixed_boundary|output_eigenmodes|filter_modes|singular_point_method|local_stability_flag)\s*='
files: ^(examples/.*\.toml|test/test_data/.*\.toml)$
2 changes: 0 additions & 2 deletions benchmarks/benchmark_against_fortran_run.jl
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ function write_gpec_toml_coil(
end
println(io)
println(io, "[PerturbedEquilibrium]")
println(io, "fixed_boundary = false")
println(io, "compute_response = true")
println(io, "compute_singular_coupling = true")
println(io, "verbose = true")
Expand Down Expand Up @@ -493,7 +492,6 @@ function write_gpec_toml_file(
println(io, "forcing_data_format = \"ascii\"")
println(io)
println(io, "[PerturbedEquilibrium]")
println(io, "fixed_boundary = false")
println(io, "compute_response = true")
println(io, "compute_singular_coupling = true")
println(io, "verbose = true")
Expand Down
6 changes: 3 additions & 3 deletions docs/development/hdf5-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Conventions for the structure and naming of the `gpec.h5` output file. Every wri
**The schema must be intuitive to a plasma physicist who is not a developer of this code.** The top level largely mirrors the TOML sections / major `src/` modules — which are themselves organized along physics lines — but **physics intuition wins whenever the two diverge**. Worked examples of that rule:

- All per-rational-surface stability results consolidate under `SingularSurfaces/`, regardless of which algorithm produced them: the GGJ coefficients sit beside the Δ′/PEST-3 matching results, and the Riccati BVP and the Galerkin outer-region solve write the *same* `Delta_prime_matrix`/`Delta_prime_raw`/`Delta_coil`/`pest3_*` paths rather than each owning a producer-named subgroup.
- `EulerLagrangeMatrices` over a bare `Matrices` — group names must say what the data *is*, not which array it came from. Same reasoning renamed `records/` → `EnergyIntegrals/` and `matrices_<method>/` → `KineticMatrices/`.
- `EulerLagrangeMatrices` over a bare `Matrices` — group names must say what the data *is*, not which array it came from. Same reasoning renamed `matrices_<method>/` → `KineticMatrices/`.

Physics-topic groups elevated to top level (rather than nested under their producer): `Info/`, `Input/`, `SingularSurfaces/`, `LocalStability/`, `SurfaceGeometries/`.

Expand Down Expand Up @@ -44,7 +44,7 @@ Top level (11 groups):
| `LocalStability/` | Mercier `D_I`, resistive interchange `D_R`, `ballooning_Delta_prime` on `psi`; the ballooning α boundary on `ballooning_psi` |
| `SingularSurfaces/` | Per-rational-surface data: `rational_psi`/`rational_q`/`rational_m`/`rational_n`, GGJ coefficients, `Delta_prime_matrix`/`Delta_prime_raw`/`Delta_coil`/`pest3_A`/`pest3_B`/`pest3_Gamma` (Riccati or Galerkin alike), the inner-layer `Match/` results, `Kinetic/` |
| `PerturbedEquilibrium/` | `ForcingModes/`, `Response/`, `ResponseMatrices/`, `SingularCoupling/`, `Energies/`, `TorqueResponse/` (kinetic forward solves: ψ-resolved `T_xe`, `T_source`), control-surface spectra |
| `KineticForces/` | `<method>/` (torque/energy profiles, `dTdpsi_ell` resolved by bounce harmonic on the `ell` scale, `EnergyIntegrals/`, `KineticMatrices/`); multi-ion runs add `PerSpecies/<species>/<method>/` with the same per-method layout, summing to the top-level total |
| `KineticForces/` | `<method>/` (torque/energy profiles, `dTdpsi_ell` resolved by bounce harmonic on the `ell` scale, `KineticMatrices/`); multi-ion runs add `PerSpecies/<species>/<method>/` with the same per-method layout, summing to the top-level total |
| `ErrorFields/` | `CoilSensitivities/` (per-coil-set control-surface spectra and their rigid shift/tilt derivatives, `DominantMode/` full-window projection); `MonteCarlo/` (intrinsic and corrected `\|δ\|` histograms over the sampled tolerances, per batch and averaged); `Risk/` (threshold density, `P(lock\|δ)`, locking probabilities, `ToleranceScan/`); `NTV/` (correction-coil overlap and NTV torque couplings per kAt) |
| `Tearing/` | `PerSurface/` (+ `DpMatrix/`), `Roots/`, `LayerWidths/`, `Diagnostics/{ValidRoots,Poles,FilteredRoots}`, `Scan/Surface_<k>/` |
| `SurfaceGeometries/` | `{Plasma,Wall}/{x,y,z}` point clouds |
Expand All @@ -69,7 +69,7 @@ Mechanism: writers stay table-driven — each writer keeps a `path => (; long_na
- Complex quantities are stored as the native HDF5.jl compound type (readable by h5py as a compound dtype) — **never split into `*_real`/`*_imag` dataset pairs**. Sole sanctioned exception: `Input/RawInputs/ForcingTerms/amplitude_{real,imag}`, which mirrors the external forcing ingest-file format and keeps pre-existing snapshots replayable.
- `NaN` is the not-computed sentinel in numeric datasets (e.g. auto-derived settings, rootless growth-rate entries).
- A **zero-extent array** is the not-computed sentinel for whole datasets that a given run never produces (e.g. `SingularSurfaces/ca_left`/`ca_right` on kinetic or galerkin-matched runs, the free-boundary energies when `vac_flag=false`, the on-demand derivative stores). Never write unpopulated (`undef`) memory.
- Ragged (variable-length) data uses the flat-plus-`offsets` companion pattern (`offsets[k+1] - offsets[k]` = length of row `k`) rather than HDF5 VLEN types, e.g. `KineticForces/<method>/EnergyIntegrals/` and `Tearing/Diagnostics/*`.
- Ragged (variable-length) data uses the flat-plus-`offsets` companion pattern (`offsets[k+1] - offsets[k]` = length of row `k`) rather than HDF5 VLEN types, e.g. `Tearing/Diagnostics/*`.

## Back-compatibility policy

Expand Down
6 changes: 3 additions & 3 deletions docs/development/toml-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ fails when its pattern matches a violation) lint-check the covered TOMLs on comm
- `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.
- `toml-no-deprecated-keys` — no deprecated config keys; its key list mirrors the
`_DEPRECATED_*_KEYS` lists in `src/GeneralizedPerturbedEquilibrium.jl`, which stop any run that
sets one, so extend the hook's pattern whenever a key is added there.

Run them manually with:

Expand Down
29 changes: 3 additions & 26 deletions docs/src/kinetic_forces.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ The summed total is written to `KineticForces/<method>/` exactly as in a single-
run, and each species' own contribution to `KineticForces/PerSpecies/<label>/<method>/`
— labels are `ion_z<z>_m<m>`, `impurity_z<z>_m<m>`, and `electron` (a numeric suffix
is appended only if a run repeats a `(z, m)` pair). Every per-species group carries the
same datasets and metadata as the total, so `dTdpsi`, `T`, and `EnergyIntegrals/` are
available per species. The summed cumulative torque profile is a diagnostic (linear
same datasets and metadata as the total, so `dTdpsi` and `T` are available per species. The summed cumulative torque profile is a diagnostic (linear
interpolation onto the union grid); the summed `total_torque` scalar is the exact
Gauss-Kronrod value.

Expand Down Expand Up @@ -183,35 +182,13 @@ dissipation ``-2n\,\mathrm{Im}\langle \xi, u_2\rangle/4\mu_0`` — two independe
of the same quantity — agree to a fraction of a percent with the regularization off, and disagree
by tens of percent with it on.

## HDF5 outputs: complex torque convention and the EnergyIntegrals layout
## HDF5 outputs: complex torque convention

The method level of `KineticForces/<method>/` reports the two physical scalars a user
wants first: `total_torque` = ``T_\phi`` (N·m) and `total_energy` = ``\delta W_k`` (J).
Internally both are halves of one complex quantity ``T = T_\phi + 2in\,\delta W_k``
(Logan 2013), and the ψ-profiles `dTdpsi` and `T` store that complex ``T`` directly —
so `imag(T)` carries the ``2n`` factor while `total_energy` has it divided out. The
per-record `EnergyIntegrals/torque` and `EnergyIntegrals/kinetic_energy` are separate
complex diagnostics of the two integrand halves at each ``(\psi, \lambda, \ell)``
evaluation, which is why they are not packed into one number there.

`EnergyIntegrals/` stores the variable-length integration trajectories in the
flat-plus-offsets ragged layout (chosen over HDF5 VLEN types for cross-language
support; `Tearing/Diagnostics/*` uses the same pattern). Record `k` spans
`offsets[k]+1 : offsets[k+1]` (Julia, 1-based) of each `*_all` array:

```julia
h5open("gpec.h5", "r") do f
g = f["KineticForces/fgar/EnergyIntegrals"]
off = read(g["trajectory_offsets"])
x_k = read(g["x_all"])[off[k]+1:off[k+1]] # record k's abscissae
I_k = read(g["integrand_all"])[off[k]+1:off[k+1]] # its complex integrand
end
```

```python
g = f["KineticForces/fgar/EnergyIntegrals"] # h5py, 0-based
x_k = g["x_all"][g["trajectory_offsets"][k]:g["trajectory_offsets"][k + 1]]
```
so `imag(T)` carries the ``2n`` factor while `total_energy` has it divided out.

```@autodocs
Modules = [GeneralizedPerturbedEquilibrium.KineticForces]
Expand Down
3 changes: 0 additions & 3 deletions examples/DIIID-like_error_field_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,6 @@ height = -1.5780 # Hoop height: centroid Z of the pack cr
currents = [55000.0] # 1 kA per turn × 55 turns; sensitivities scale linearly with current [A]

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
verbose = false # Enable verbose logging
Expand Down Expand Up @@ -260,7 +258,6 @@ impurity_mass = 12 # Impurity mass [proton masses]
electron = false # Ion-only NTV
nutype = "harmonic" # Collision operator
f0type = "maxwellian" # Equilibrium distribution
moment = "pressure" # Pressure-moment NTV torque
atol_pitch = 1e-9 # Absolute tolerance for the pitch integration (median-normalized kernel)
write_outputs_to_HDF5 = true # Write outputs to the HDF5 file
verbose = false # Enable verbose logging
2 changes: 0 additions & 2 deletions examples/DIIID-like_gal_resistive_pe_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ name = "c" # DIII-D C-coil (6 coils); resolves to c
currents = [1000.0, 500.0, -500.0, -1000.0, -500.0, 500.0] # n=1 cosine phasing, 1 kA peak [A]

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = false # Compute plasma response to forcing
compute_singular_coupling = false # Compute singular layer coupling metrics
verbose = true # Enable verbose logging
Expand Down
3 changes: 0 additions & 3 deletions examples/DIIID-like_ideal_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ name = "c" # Coil set; resolves to coil_geometries/
currents = [1000.0, 500.0, -500.0, -1000.0, -500.0, 500.0] # n=1 cosine phasing, 1 kA peak [A]

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
verbose = true # Enable verbose logging
Expand All @@ -101,7 +99,6 @@ impurity_mass = 12 # Impurity mass [proton masses]
electron = false # Ion-only NTV
nutype = "harmonic" # Collision operator
f0type = "maxwellian" # Equilibrium distribution
moment = "pressure" # Pressure-moment NTV torque
atol_pitch = 1e-9 # Absolute tolerance for the pitch integration (median-normalized kernel)
axis_validity_suppression = true # Suppress kinetic terms inside the near-axis orbit-width validity limit
write_outputs_to_HDF5 = true # Write outputs to the HDF5 file
Expand Down
2 changes: 0 additions & 2 deletions examples/DIIID-like_ideal_multi_n_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ name = "c" # Coil set; resolves to coil_geometries/
currents = [1800.0, -50.0, -450.0, -800.0, -450.0, -50.0] # Sum of n=1, 2, 3 cosine phasings with 1000, 500, 300 A peaks; n=3 lies outside nn_high [A]

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
verbose = false # Enable verbose logging
Expand Down
2 changes: 0 additions & 2 deletions examples/Solovev_ideal_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ forcing_data_file = "forcing.dat" # Manual mode table (n, m, amplitude_rea
forcing_data_format = "ascii" # Format: "ascii", "hdf5", or "coil" (Biot-Savart from 3D wires)

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
verbose = true # Enable verbose logging
Expand Down
2 changes: 0 additions & 2 deletions examples/Solovev_ideal_example_3D/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ equal_arc_wall = false # Equal arc length distribution of nodes
# [PerturbedEquilibrium]
# forcing_data_file = "forcing.dat" # Manual mode table (n, m, amplitude_real, amplitude_imag)
# forcing_data_format = "ascii" # Format: "ascii", "hdf5", or "coil" (Biot-Savart from 3D wires)
# fixed_boundary = false # Use fixed boundary conditions
# output_eigenmodes = true # Output eigenmode fields as b-fields
# compute_response = true # Compute plasma response to forcing
# compute_singular_coupling = true # Compute singular layer coupling metrics
# verbose = true # Enable verbose logging
Expand Down
2 changes: 0 additions & 2 deletions examples/Solovev_kinetic_NTV_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ forcing_data_file = "forcing.dat" # Manual mode table (n, m, amplitude_rea
forcing_data_format = "ascii" # Format: "ascii", "hdf5", or "coil" (Biot-Savart from 3D wires)

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
verbose = true # Enable verbose logging
Expand Down
2 changes: 0 additions & 2 deletions examples/Solovev_kinetic_calculated_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ forcing_data_file = "forcing.dat" # Manual mode table (n, m, amplitude_rea
forcing_data_format = "ascii" # Format: "ascii", "hdf5", or "coil" (Biot-Savart from 3D wires)

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
compute_torque_response = true # Build the ψ-resolved torque response matrices T_xe and T_source
Expand Down
2 changes: 0 additions & 2 deletions examples/Solovev_kinetic_multiion_example/gpec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ forcing_data_file = "forcing.dat" # Manual mode table (n, m, amplitude_rea
forcing_data_format = "ascii" # Format: "ascii", "hdf5", or "coil" (Biot-Savart from 3D wires)

[PerturbedEquilibrium]
fixed_boundary = false # Use fixed boundary conditions
output_eigenmodes = true # Output eigenmode fields as b-fields
compute_response = true # Compute plasma response to forcing
compute_singular_coupling = true # Compute singular layer coupling metrics
verbose = true # Enable verbose logging
Expand Down
8 changes: 4 additions & 4 deletions src/Equilibrium/EquilibriumTypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ specified in the input.
- `eq_type::String` - Type of equilibrium file ("efit", "solovev", "lar", etc.)
- `eq_filename::String` - Path to equilibrium input file
- `jac_type::String` - Jacobian coordinate type ("hamada", "pest", "equal_arc", "boozer", "park", "custom")
- `power_bp::Int` - Poloidal field power exponent for Jacobian (internal, derived from `jac_type`; deprecated as a TOML input key)
- `power_b::Int` - Total field power exponent for Jacobian (internal, derived from `jac_type`; deprecated as a TOML input key)
- `power_r::Int` - Major radius power exponent for Jacobian (internal, derived from `jac_type`; deprecated as a TOML input key)
- `power_rc::Int` - Minor radius (rfac = √((R-R₀)²+(Z-Z₀)²)) power exponent for Jacobian (internal, derived from `jac_type`; deprecated as a TOML input key)
- `power_bp::Int` - Poloidal field power exponent for Jacobian (internal, derived from `jac_type`; not a deck key)
- `power_b::Int` - Total field power exponent for Jacobian (internal, derived from `jac_type`; not a deck key)
- `power_r::Int` - Major radius power exponent for Jacobian (internal, derived from `jac_type`; not a deck key)
- `power_rc::Int` - Minor radius (rfac = √((R-R₀)²+(Z-Z₀)²)) power exponent for Jacobian (internal, derived from `jac_type`; not a deck key)
- `jac_custom_power_bp::Int` - Poloidal-field exponent for a custom Jacobian `J = Bp^bp · B^b / (R^r · rfac^rc)`; read only when `jac_type = "custom"`, ignored for named types
- `jac_custom_power_b::Int` - Total-field exponent for a custom Jacobian; read only when `jac_type = "custom"`, ignored for named types
- `jac_custom_power_r::Int` - Major-radius exponent for a custom Jacobian; read only when `jac_type = "custom"`, ignored for named types
Expand Down
Loading
Loading