Standalone signed TGLF fluxes¶
The installed package reads retained GACODE output: read_tglf_fluxes returns
signed normalised species fluxes with all reported spectral modes from a run
directory, and needs no provider.
The launcher that produces such a directory is TGLFFluxSolver in
validation/tglf_launcher.py, a validation command of the repository and not
part of the installed package. It executes an existing GACODE key=value input
deck, creates a fresh directory for each execution and retains the input,
output, stdout, stderr and a hashed execution receipt. It requires POSIX, a
repository checkout and a configured GACODE installation. It lives outside the
package because the hosted test environments have no provider, so its code
cannot be exercised there.
The output is electron-first, followed by ions. Particle, energy, momentum
and exchange fluxes retain their signs. They are not diffusivities.
Physical conversion requires the deck's reference density, temperature,
length, mass, magnetic field and coordinate convention. physical_tglf_flux converts particle and energy moments to SI when given
explicit TGLFReferenceUnits matching the GYRO-normalised deck. It uses
Gamma_GB = n0cs(rho_s/a0)^2 and Q_GB = T0*Gamma_GB, retains signs, and
returns flux across physical minor-radius surfaces. A different coordinate
requires its matching derivative and volume Jacobian. It does not infer the
reference magnetic field from toroidal field or separate diffusion from pinch.
No automatic GKLocalParams mapping or TransportSolver coupling is supplied.
The existing coefficient-only external_gk path remains unqualified;
this raw-flux interface provides the provider boundary needed for its repair.
Passing file checks establishes structural consistency, not physical
calibration or proof of numerical convergence.
from pathlib import Path
from scpn_control.core.tglf_flux import read_tglf_fluxes
from validation.tglf_launcher import TGLFFluxSolver # repository checkout only
solver = TGLFFluxSolver(
work_dir=Path("/path/to/owned/run-evidence"),
binary="/path/to/configured/tglf",
)
flux = solver.run(Path("/path/to/input.tglf"), timeout_s=30)
print(flux.particle_flux_gb)
# Any later reader needs only the retained directory.
assert read_tglf_fluxes(flux.run_dir) == flux
An optional environment mapping supplies child-only GACODE/dependency
settings. Environment values are not included in execution receipts.
Timeouts kill the process group; failures retain their run directories.
Callers own evidence retention. The captured test specimen comes from an
actual upstream default run; its provenance and SHA-256 values accompany it.
Real execution tests are in tests/test_tglf_launcher.py. They require
SCPN_TGLF_BINARY and optionally SCPN_TGLF_ENV_JSON; without them those
tests are explicitly skipped. The reader tests in tests/test_tglf_flux.py
run everywhere on the captured specimen.
See the TGLF flux API for the reader and result contracts.
See the TGLF unit API for reference units and SI conversion contracts.
Execution and retained evidence¶
Logs go directly to files. Every completed launch attempts to terminate its
owned POSIX process group, including after success, timeout or interruption.
Leader reaping has a separate two-second cleanup deadline. Original wait
exceptions and cancellation propagate after cleanup; cleanup failures are
recorded in execution.json and attached to the exception. A descendant that
creates another session or process group is outside this termination contract.
Its inherited log descriptors do not hold the call open. This is not a process
sandbox or a guarantee that escaped descendants terminate.
The receipt hashes the same immutable byte snapshots that the parser consumes.
Retained outputs, the copied input and launcher identity are checked before
publishing success. The launcher hash is captured before launch and does not
authenticate the dependent executable or libraries. Evidence directories remain
mutable: read_tglf_fluxes verifies a present successful UTF-8 execution receipt
against captured input/output bytes. After parsing it rechecks every captured
output, the copied input and the receipt itself; changed or unreadable admission
members are refused. A receipt-free directory is parsed for structural
consistency only. These sequential point-in-time custody checks do not lock the
filesystem or prevent replacement after a member's last check or after return.
A nonzero normalised particle or energy moment that rounds to zero during SI conversion is rejected. Exact zero and representable subnormal results remain valid; no epsilon threshold or clipping is applied.
Moment precision and supported output contract¶
Flux moments come from out.tglf.sum_flux_spectrum. In GACODE's
write_tglf_sum_flux_spectrum, each row already contains its ky quadrature
weight and the sum over modes. The reader sums these increments once across
ky and all configured fields. It returns particle flux, energy flux, toroidal
stress and exchange; the separate parallel-stress column is validated but
is not returned as toroidal momentum.
out.tglf.gbflux uses the Fortran 1pe11.4 format: five significant digits.
Rounding species independently can spoil ambipolarity in a multi-ion case.
The reader uses this file only to cross-check each integrated moment against
its printed decimal half-unit rounding interval. It does not relax the
transport charge tolerance, overwrite electron flux, or fall back to coarse
moments. A disagreement is refused; a passing comparison is an artifact
consistency check, not an error bound on the physical turbulence model.
The resolved input.tglf.gen is also required and hashed. The supported
contract is GYRO normalisation, kinetic electrons, transport-model flux
output, matching species/spatial/mode dimensions, and complete ordered
species/field blocks. USE_BPER selects two fields and USE_BPAR selects
three, matching the upstream writer. Each block must contain exactly the
reported number of ky rows and five finite moments. Missing, duplicate,
reordered, nonfinite or overflowing spectra are refused. Archives and older
receipts lacking either new required artifact must be reacquired or migrated
with verifiable source evidence; the reader does not silently upgrade them.
Real-provider regressions cover an explicit local three-species Miller case through SI conversion and a conservative face step, plus two- and three-field electromagnetic executions. The face-step case repeats prescribed local flux across faces, uses a common ion temperature and a charge-two ion with mass/reference ratio two. It establishes this numerical boundary, not a calibrated helium model, automatic radial sampling or coupled convergence.
Physical Miller input construction¶
miller_tglf_deck constructs a nonrotating, isotropic GYRO SAT0 input from
TGLFSpecies, TGLFMillerGeometry and explicit TGLFReferenceUnits. It
accepts physical dn/dr and dT/dr, includes all supplied species in the
pressure derivative and effective charge, and checks both density and density-
gradient charge neutrality. Absent species must be omitted explicitly.
Electron collision frequency is an explicit input in the TGLF convention;
the builder does not guess it from nu_star or choose a collision formula.
For reference length a0, local minor radius r, and pressure p in Pa:
RLNS = -a0/n * dn/dr,RLTS = -a0/T * dT/dr;Q_PRIME_LOC = q*a0²/r * dq/dr;P_PRIME_LOC = μ0/(4π Bunit²) * q*a0²/r * dp/dr;S_KAPPA_LOC = r/kappa * dkappa/dr,S_DELTA_LOC = r * ddelta/dr.
The last two follow the current geometry kernel and
GACODE input definitions.
The legacy tglf_TM_driver.f90 example uses different shape-shear formulas;
copying those formulas would be wrong when the shape derivatives are nonzero.
Finite-difference tests of physical Miller surfaces check both radial shape
derivatives, alongside real executions with nonzero shape gradients.
The caller supplies Bunit from matching magnetic geometry; toroidal field is
not silently substituted. All velocities and shears, elevation, squareness and
higher shape harmonics are explicitly zero. Both supported SAT0 mode fits
(two and four modes) are available, including electromagnetic field switches.
Other saturation models and rotation need their own qualified contract.
Unlisted numerical defaults remain provider-owned and are retained with the
execution. The provider may apply presets after generating input.tglf.gen;
the builder selects SAT0/GYRO with two/four modes to avoid the known unit/mode
rewrites. A generic supplied deck's generated input is not proof that all
internal provider settings remain unchanged.
from scpn_control.core.tglf_miller import (
TGLFMillerGeometry, TGLFSpecies, miller_tglf_deck,
)
from scpn_control.core.tglf_units import TGLFReferenceUnits
reference = TGLFReferenceUnits(5e19, 2.0, 2.0, 3.34524384738e-27, 2.0)
electron = TGLFSpecies(-1, 9.1093837139e-31, 5e19, 2.0, -2.5e19, -3.0)
ion = TGLFSpecies(1, reference.mass_kg, 5e19, 3.0, -2.5e19, -3.0)
geometry = TGLFMillerGeometry(1.0, 6.0, 2.0, 2.0, elongation=1.7, triangularity=0.3)
deck_text = miller_tglf_deck(
reference, (electron, ion), geometry,
electron_collision_rate_s=34195.32091515598,
)
This is a prescribed local example. Constructing a deck does not certify nested surfaces, force balance or machine calibration. Radial sampling, profile interpolation, rotating/anisotropic species and coupled convergence remain separate work; repeating a local flux at every face does not supply them. The current conservative regression uses a cylindrical volume/area harness; the explicit metric route additionally supports Miller dV/dr and cell volumes. A separate real four-face regression uses distinct local TGLF inputs and Miller metrics for three successive frozen-flux stages. Each later stage reconstructs positive face profiles and physical radial gradients from the updated nodes, then executes four fresh provider runs with matching local reference scales. The twelve retained runs check input/output custody, adjacent inventories and accumulated boundary exchange. This is a prescribed collisionless electron/ion case with fixed shape and magnetic reference, not a reusable orchestration API, coupled temporal-convergence study or self-consistent equilibrium.
See the Miller geometry API, species API and input-deck factory for the corresponding input contracts.
out.tglf.scalar_saturation_parameters is required as the seventh captured
artifact. Its effective SAT_RULE must match the request and its effective
UNITS must remain GYRO. Real SAT2/3 executions demonstrate why this matters:
the provider can retain requested GYRO in input.tglf.gen while reporting
CGYRO after startup presets. Those runs are refused by this GYRO-only
boundary; real SAT1 retaining GYRO remains admissible. This check establishes
the declared model convention and does not assert that every numerical default
is unchanged. Missing or duplicate effective settings are refused, and the
whole scalar-output file participates in receipt custody.
Numeric decoding refuses a nonzero decimal token that would round to binary64 zero, including Fortran D exponents. This applies to flux moments and numerical grid/ky/eigenvalue inputs. Exact zeros and representable signed subnormals remain valid. Deliberately corrupted-output tests exercise this boundary after real GACODE execution; they do not imply that its REAL64 writer normally emits out-of-range decimal tokens. Such runs retain a failed validation receipt.