Input Files and Executable
Every GKX input is one runtime TOML deck, loaded by
gkx.load_runtime_from_toml into a RuntimeConfig. The [grid],
[time], [geometry] and [init] tables merge into GridConfig,
TimeConfig, GeometryConfig and InitializationConfig; species,
physics toggles, collisions, normalization, operator terms, output, quasilinear
and parallel policy have their own tables (listed under Runtime sections).
The same deck drives the executable and the Python API, for Cyclone, ETG, KBM
or any other case, through one solver path.
Unified Runtime Schema
Resolution and time-step defaults
Velocity resolution. When neither the call nor the deck names
Nl(Laguerre) andNm(Hermite), the fallback is(Nl, Nm) = (12, 24)for linear runs and(4, 8)for nonlinear runs. Set both explicitly for any result you report.ky layout.
[grid] ky_layout = "half"(default) stores the1 + Ny // 2non-negative rows;"full"stores the two-sidedkyaxis of lengthNy(gkx.core_ky_layout). Results do not depend on the choice: they agree to round-off, and"half"is faster and smaller, in the nonlinear step and in the heat-flux window gradient.Time stepping. A deck that sets
[time] dtrunsrk2at that fixed step unless it names anothermethod. A deck withoutdtgetsmethod = "rk4"withfixed_dt = false: the CFL controller chooses the step withcfl = 0.9. An explicitmethodorfixed_dtin the deck always wins.
Runtime precision
The default is float32 (complex64 states).
JAX_ENABLE_X64=true, set before JAX is imported, selects double precision.
The runtime’s own initial state is then built in complex128, so the Krylov
basis, the eigenvector and the certification gate are all float64. This is what
the shipped decks mean when their header says to run them under
JAX_ENABLE_X64 for parity reproduction.
What x64 buys is the gate, not necessarily the eigenvalue.
certifiable_residual_tolerance floors the eigenpair residual gate at
1e3 * eps(dtype), so a float32 run certifies against 1.19e-4 while a float64
run certifies against 1e-9. Use float64 when the number you are quoting has to
carry a float64-scale residual.
A float64 run on a deck too coarse to support a certifiable eigenpair raises
instead of returning an uncertified number. If that happens, raise the
velocity resolution (Nl, Nm).
GKX_X64 is a test-harness variable, not a runtime switch: it only selects
the precision banner in tests/conftest.py. Setting it without
JAX_ENABLE_X64 does not change any run’s precision.
run_runtime_linear(cfg, initial_state=G128, ...) accepts an explicit
seed and preserves its dtype. It requires shape
(kinetic_species, Nl, Nm, grid.ky.size, grid.kx.size, grid.z.size) on the
selected-ky runtime grid. Record the seed dtype and hash; casting a rounded f32
seed does not recover lost input precision. There is no TOML dtype option, and
prepared linear solve(initial_state=...) raises NotImplementedError.
Minimal runtime TOML example
schema_version = 1
[[species]]
name = "ion"
charge = 1.0
mass = 1.0
density = 1.0
temperature = 1.0
tprim = 2.49
fprim = 0.8
kinetic = true
[physics]
linear = true
nonlinear = false
electrostatic = true
electromagnetic = false
adiabatic_electrons = true
tau_e = 1.0
[normalization]
contract = "cyclone"
diagnostic_norm = "rho_star"
[terms]
streaming = 1.0
mirror = 1.0
curvature = 1.0
gradb = 1.0
diamagnetic = 1.0
collisions = 1.0
hypercollisions = 1.0
end_damping = 1.0
apar = 0.0
bpar = 0.0
nonlinear = 0.0
[run]
ky = 0.3
Nl = 16
Nm = 48
solver = "auto"
fit_signal = "auto"
Nl = 16, Nm = 48 is the velocity resolution of the shipped Cyclone deck
examples/01_linear_tokamak/case_full.toml.
Schema compatibility
Maintained runtime decks declare schema_version = 1 at the document root.
Versionless GKX 1.8.2 decks remain readable as legacy schema 0 and are not
rewritten in place. Resolved decks written by the equilibrium shorthand are
schema 1. A non-integer or unsupported future version fails before numerical
work and points to this migration contract; upgrade GKX before reading a newer
schema. The removed Diffrax keys (use_diffrax, diffrax_solver,
diffrax_adaptive, diffrax_rtol, diffrax_atol,
diffrax_max_steps) in [time] are refused with the native replacement
named in the error.
Quasilinear diagnostics
Linear runtime runs can compute electrostatic quasilinear transport weights directly from the final linear state or Krylov eigenvector:
[quasilinear]
enabled = true
mode = "weights" # weights | saturated
saturation_rule = "none" # none | mixing_length | lapillonne_2011
amplitude_normalization = "phi_rms" # phi_rms | phi_midplane | field_energy
kperp_average = "phi_weighted"
csat = 1.0
gamma_floor = 0.0
include_stable_modes = false
channels = ["es"]
The validated output level is linear weights plus optional
uncalibrated electrostatic saturation rules. Electromagnetic quasilinear
channels are rejected. The runtime writes
*.quasilinear.summary.json and *.quasilinear_species.csv when
[output].path or --out is set. Serial scan-runtime-linear runs also
write *.quasilinear_spectrum.csv; combined-ky batched scans refuse
quasilinear output. In scan spectra, the ky column is the requested scan coordinate and
mode_ky records the signed selected grid-mode coordinate; they can differ
for linked-boundary grids, so publication plots should use ky while audits
can inspect mode_ky.
Equivalent executable flags are available for single-point runtime runs:
gkx run-runtime-linear \
--config examples/01_linear_tokamak/case_full.toml \
--quasilinear \
--ql-mode saturated \
--ql-saturation-rule mixing_length \
--ql-csat 1.0 \
--out tools_out/cyclone_quasilinear
Time-integration controls
Keys in [time] besides t_max, dt and method:
compressed_real_fft(defaulttrue): compressed real-FFT nonlinear bracket;falseuses a full complex FFT for the nonlinear term.sample_stride(record everyNsteps) anddiagnostics_stride(compute streaming diagnostics everyNsteps).diagnostics = false(or--no-diagnostics) disables diagnostics entirely.fixed_dt = falsewithcfl(default0.9) and optionalcfl_fac/dt_min/dt_max: CFL-controlled steps. Whencfl_facis omitted,rk3/sspx3use1.73,rk4uses2.82, and other methods1.0. Adaptive runs recorddt_t(per-sample step history) anddt_mean(average effective step). The nonlinear adaptivedtestimate combines the linear frequency cap with the instantaneous nonlinear cap. Leavingdt_maxunset keepsdt_max = dt; raise it explicitly only when you trade strict comparison matching for throughput.method = "rk4-pi"(nonlinear,fixed_dt = false): RK4 whose step is chosen from an embedded error estimate and capped at twice the CFL step; see Solvers.cfl_facdefaults to2.82as forrk4.laguerre_nonlinear_mode:"grid"(reference quadrature, default) or"spectral"(spectralJlwithout the quadrature transform) for the Laguerre handling in nonlinear brackets.nonlinear_dealias(defaulttrue): 2/3 dealias masking. With masking on, nonlinear mode selection is dealias-aware: a requestedkyremoved by the mask is replaced by the nearest retainedky, and the executable prints the effectiveky_sel/kx_sel.falsekeeps all configured modes for reference/debug runs.state_sharding:"auto"or"ky"shards the packed state over multiple JAX devices alongky;"kx","z","l","m"and"species"shard along that axis. Sharding-aware integration paths honor it; unsupported solver paths and one-device runs fall back to single-device execution. Treat it as a correctness-gated option unless the run also has a matched scaling artifact.
Diagonal nonlinear hypercollision splitting is enabled with
collision_split = true. The conserving collision operator remains in the
RHS because its field-particle correction is not diagonal. The
collision_scheme key selects the hypercollision update:
implicit (backward-Euler), exp (exact diagonal exponential), and
sts/rkc aliases (treated as stabilized explicit/exponential updates for
diagonal operators).
Geometry controls
[geometry] drift_scale selects the benchmark-compatible drift convention
(1.0, the default) or the alternate doubled-drift convention (2.0).
The physical meaning of the runtime terms and geometry coefficients is detailed
in Theory and Operators And Terms; the TOML layer here documents how those
implemented models are selected and parameterized.
For slab benchmarks, set model = "slab". Optional slab-specific keys are
z0 (sets gradpar = 1/z0 when positive, matching the reference slab domain
normalization) and zero_shat = true (forces the zero-shear slab metric
gds2 = 1, gds21 = 0, gds22 = 1).
It also accepts model = "imported-netcdf" with
geometry_file = "external_geometry.nc" to run from imported sampled
field-line geometry instead of the analytic s-alpha model. The imported
file can be a tracked benchmark *.out.nc or a root-level *.eik.nc geometry
file produced by the VMEC workflow. When that imported geometry is used with a
linked boundary, GKX follows the file’s own theta range,
jtwist/x0 geometry factor, and kxfac metadata instead of forcing the
analytic s-alpha grid defaults.
For direct VMEC workflows, the runtime also accepts model = "vmec".
In that mode GKX calls the VMEC geometry helper to generate a
matching *.eik.nc file on demand, then immediately reuses the same imported
geometry path as the VMEC examples. Set vmec_file plus the flux-tube keys
torflux, npol and optionally alpha. geometry_file can be used
as an explicit output path for the generated *.eik.nc file, and
geometry_helper_repo can point to a non-default helper checkout if needed.
The VMEC path uses the booz_xform_jax backend, which is installed with
GKX; GKX_BOOZ_XFORM_JAX_PATH or BOOZ_XFORM_JAX_PATH point to a source
checkout instead. This is the recommended imported-geometry route for new
stellarator cases. The shipped VMEC TOMLs
point to locally generated files under examples/vmec; generate those files
with vmex input.<case> before running the examples.
vmec_file supports $ENV_VAR expansion, and relative paths are resolved
against the TOML directory first. Command-line overrides are resolved from the
shell working directory.
Use geometry_helper_repo and geometry_helper_python when an imported
geometry helper checkout or interpreter must be selected explicitly.
When geometry_file is set for model = "vmec", GKX regenerates
that target instead of reusing a stale file from an older VMEC conversion.
For VMEC fix aspect runs, GKX follows the helper default contract and
does not inject x0 from the runtime Lx. That keeps the generated
*.eik.nc file aligned with the imported W7-X/HSX geometry output.
For Miller tokamak workflows, the runtime also accepts model = "miller".
In that mode GKX uses its in-package Miller backend to generate a matching
root-level *.eiknc.nc file, then re-enters the same imported
geometry path used for VMEC eik.nc files.
Set the Miller inputs directly in [geometry]:
rhoc, q, s_hat, R0, optional R_geo, shift,
akappa, akappri, tri, tripri, and betaprim.
The generated Miller surface has inverse aspect ratio rhoc / R0. Set that
ratio explicitly when reproducing a benchmark; the separate epsilon field
belongs to analytic geometry metadata and does not replace the surface radius.
geometry_file can be used as an explicit output path for the generated
Miller *.eiknc.nc file. Existing output is reused by default; pass
--force to gkx geometry miller to regenerate it. The
geometry_helper_python and geometry_helper_repo settings apply to VMEC
helper discovery, not to this in-package Miller backend.
Executable path overrides
The gkx executable accepts path overrides:
gkx run --config case.toml --vmec-file examples/vmec/wout_circular_tokamak.nc # built by generate_wouts.sh
gkx run --config case.toml --geometry-file external_geometry.eik.nc
gkx run-runtime-nonlinear --config case.toml --init-file ~/restart.nc
These override paths expand ~ and environment variables and are resolved
against the shell’s current working directory. TOML paths still resolve against
the config file directory. --vmec-file replaces [geometry].vmec_file for
VMEC-backed configs. --geometry-file replaces [geometry].geometry_file
only; it does not change [geometry].model. For imported-geometry configs,
that file is the imported EIK/NetCDF input. For model = "vmec", it remains
the generated EIK target/cache path.
Solver and fit-signal keys
The [run] and [scan] sections accept solver and fit_signal keys:
solver = "auto"(default): choose time vs Krylov and fall back if neededsolver = "time": always use time integrationsolver = "explicit_time": force the explicit single-mode time integrator used by controlled explicit-time comparisonssolver = "krylov": always use the eigensolver. Its default route isKrylovConfig(method="auto"): the Hermite-banded direct shift-invert (banded) when its block arrays fit in memory, elseadaptive. Either returns an eigenpair only when its residual passes the certification gate (see Runtime precision). OtherKrylovConfig.methodvalues areadaptive,power,propagator,arnoldi,shift_invert(matrix-free GMRES inner solves) andsparse_shift_invert(assembles the sparse operator and factors it once on the host).fit_signal = "auto"(default): pickphivs density based on fit qualityfit_signal = "phi": use the electrostatic potential time tracefit_signal = "density": use the density moment time trace
For large ky scans, scan-runtime-linear --batch-ky integrates all ky values
in a single time integration pass (time integrator only) and then extracts the
growth rates from the per-ky traces.
For quasilinear spectra, use scan-runtime-linear --workers N instead of
--batch-ky. This runs independent per-ky solves, computes the
quasilinear state extraction for each mode, preserves serial spectrum ordering,
and records the worker identity contract in the scan summary JSON.
--batch-ky with [quasilinear] enabled = true raises ValueError;
quasilinear scans require per-ky evaluation.
Executable usage
gkx examples/01_linear_tokamak/case_full.toml # same as: gkx run --config ...
gkx scan-runtime-linear --config benchmarks/cases/etg_linear_scan.toml --out tools_out/runtime_etg_scan
gkx plot tools_out/runtime_etg_scan.scan.csv
gkx run-runtime-linear --config examples/01_linear_tokamak/case_full.toml --out tools_out/cyclone_runtime
gkx scan-runtime-linear --config benchmarks/cases/etg_linear_scan.toml --batch-ky
gkx run-runtime-nonlinear --config examples/03_nonlinear_tokamak/case_full.toml --sample-stride 5 --out tools_out/nonlinear_cyclone_diag.csv
examples/01_linear_tokamak/case_full.toml is a production-length run (32168 rk4 steps at
Nl = 16, Nm = 48); see Quickstart for a ten-second demo.
gkx CASE.toml dispatches to gkx run --config CASE.toml, which picks the
linear or nonlinear command from [physics].
For run-runtime-nonlinear with fixed_dt = false, omit --steps unless
you want a capped step count: the runtime then integrates in chunks until it
reaches t_max (or saturates, below). With fixed_dt = true and no
--steps, the step count is round(t_max / dt).
Diagnosed nonlinear runs stop at saturation by default ([time] run_to =
"saturation"): the runtime integrates in chunks and, after each chunk, tests
the accumulated heat-flux trace with spin-up excluded. It stops once the
autocorrelation-corrected relative SEM of the windowed mean is at or below
saturation_rel_sem, the retained window has at least 256 samples and spans
at least 20 integrated autocorrelation times, any larger
saturation_min_window is met, and the first and second halves agree within
twice their combined SEM. The same drift-consistency guard is applied to the
electrostatic field energy Wphi and gyrokinetic free energy Wg. These
operational finite-sample safeguards provide neither a universal sequential-
confidence guarantee nor qualification for irregular adaptive-time sampling.
t_max remains the hard cap, so a run that never saturates integrates the
full horizon.
The chosen window and its mean +/- SEM are printed in the run summary and
recorded under saturation in the summary JSON. Pass
--no-until-saturated (or set run_to = "t_max") for a fixed horizon.
For single-point runtime commands, artifact output can be requested either with
--out or directly in the runtime TOML:
[output]
path = "tools_out/runtime_case"
--out takes precedence over [output].path when both are provided.
A plain prefix writes a JSON summary plus CSV sidecars; a nonlinear target
ending in .out.nc (or another .nc suffix) writes the NetCDF bundle
*.out.nc / *.big.nc / *.restart.nc instead. See Saved Outputs and Restart Files for
every file, CSV column and NetCDF variable.
Python driver
# point CONFIG at the top of the script at benchmarks/cases/etg_linear_scan.toml
python examples/12_restart_and_analysis/run.py
The canonical KBM TOML is a controlled comparison input rather than
a promoted standalone solve: its experimental shift-invert branch raises when
the physical eigenpair-residual gate is not met. Reproduce the reviewed result
with scripts/benchmarks/linear_benchmark.py kbm (retired; Retired generators) (removed from the tree;
git show df2953026:scripts/benchmarks/linear_benchmark.py).
Runtime sections
load_runtime_from_toml reads these tables. Unknown keys in [grid],
[time], [geometry] and [init] are ignored (the removed Diffrax
[time] keys are refused). An unknown key in [[species]], [physics],
[collisions], [normalization], [terms], [expert], [output],
[quasilinear] or [parallel] raises TypeError. [run], [scan]
and [fit] are read by the commands that use them.
schema_version(document root; see Schema compatibility)[grid](GridConfig),[time](TimeConfig),[geometry](GeometryConfig),[init](InitializationConfig)[[species]](kinetic species definitions)[physics](electrostatic/electromagnetic, adiabatic/kinetic, linear/nonlinear)[collisions](collision and hypercollision controls)[normalization](contract key + optional overrides)[terms](term toggles used by modular RHS assembly)[expert](advanced fixed-mode controls for specialized workflows)[output](artifact path and restart controls)[quasilinear](quasilinear transport diagnostics)[parallel](parallelization policy; defaults to serial)[damping_reference](provenance written bymigrate_end_damping_reference; see Explicit reference migration)[run]/[scan]/[fit](driver controls:ky,Nl,Nm,mu_vperp_max(nodal magnetic-moment grid, see Numerics),solver,method,dt,steps, the scankylist, and the growth-rate window optionsauto_window,tmin,tmax,window_fraction,min_points,start_fraction,growth_weight,require_positive,min_amp_fraction,window_method,mode_method,fit_signal)
Notable runtime-only keys:
[time] damp_ends_rate: the field-line end-absorber rate \(\nu_{end}\) in inverse simulation-time units, finite and nonnegative. Every route (time, Krylov, eigen, shift-invert, nonlinear) applies the same rate; no timestep enters. Omit it for the default, 30 thermal transits of the fastest kinetic species across the end layer.0turns the absorber off. See Parallel boundary and end damping.[collisions] damp_ends_widthfrac: layer width as a fraction of the chain (default0.125).[physics] reduced_model: physics-family selector for runtime inputs. The maintained runtime supports full gyrokinetics via"gyrokinetic"and its full-GK aliases. Non-promoted reduced-model values fail closed withNotImplementedErrorinstead of silently routing through the wrong equations.[collisions] damp_ends_amp(removed): a positive value withoutdamp_ends_rateon a non-periodic deck is refused, because GKX < 2.6 read it asdamp_ends_amp/dton the time route and as a rate elsewhere.damp_ends_amp = 0still means no damping.damp_ends_scale_by_dt = trueis refused too.[collisions] hypercollisions_const/hypercollisions_kz: defaults are0.0/1.0(kz-proportional hypercollisions enabled by default, constant hypercollisions off). The parallel multiplier is spectral: \(|\partial_z|G=\mathcal{F}^{-1}(|k_z|\mathcal{F}G)\), on each periodic line or linked chain. It is not pointwise multiplication of physical-z samples by the wavenumber array. GKX releases before 2.1.0 applied a pointwise multiplier on periodic lines: rerun periodic transport results from those versions that used nonzero kz damping and Hermite orders above two. Linked runs are unaffected.[collisions] p_hyper_m: when omitted, the runtime path uses the resolution-aware defaultmin(20, Nm/2)instead of a fixed exponent across Hermite resolutions.[collisions] nu_hermite/nu_laguerre: coefficients entering the Lenard-Bernstein collision eigenvalue used by the modular collision kernel.[collisions] nu_hyper/p_hyper: isotropic hypercollision amplitude and exponent.[collisions] nu_hyper_l/nu_hyper_m/nu_hyper_lmandp_hyper_l/p_hyper_m/p_hyper_lm: Laguerre-only, Hermite-only, and mixed hypercollision channels.nu_hyper_landnu_hyper_lmact only in the constant-coefficient branch – the \(|k_z|\) kernel carries no Laguerre index – so a deck that declares either whilehypercollisions_const = 0.0(the default) is refused rather than run without the sink it asked for. Case validation also refuses either declaration when[physics] hypercollisions = falseor[terms] hypercollisions = 0.0disables the whole operator.[collisions] nu_hyper_m_const: the constant branch’s Hermite rate. Omitted (the default) it reusesnu_hyper_m, which damps Hermite in both branches when both are on. Set it to0.0alongsidehypercollisions_const = 1.0to declare a pure Laguerre sink while the \(|k_z|\) Hermite hypercollisions stay exactly as the reference code sets them. See Declared velocity-space regularization.[collisions] D_hyper/p_hyper_kperp: perpendicular hyperdiffusion amplitude and exponent.[normalization] flux_scale: multiplicative factor applied to heat/particle flux diagnostics (tracked comparison default1.0).[normalization] wphi_scale: multiplicative factor applied toWphidiagnostics (default1.0; no shipped configuration overrides it).[normalization] rho_star: rescales the operator wavenumbers before building the drift/Bessel terms.[normalization] omega_d_scale/omega_star_scale: multiplicative normalization factors for magnetic-drift and diamagnetic-drive terms.[init] init_singlewithgaussian_init = falseandinit_single = false: initialize a random perturbation across the configured startup-loop bounds in(ky,kx).[init] init_singlewithgaussian_init = trueandinit_single = false: initialize a Gaussian envelope across the same startup-loop bounds.[init] init_single = true: initialize only the selected(ky,kx)mode. When combined withinit_field = "phi"andgaussian_init = true, the selected electrostatic-potential mode is initialized with a Gaussian profile along the flux tube; this is the contract used by the W7-X zonal-flow response benchmark.[init] init_field = "all": the runtime/TOML path uses Hermite/Laguerre moment-normalized scaling for this initializer, with reduced amplitudes fortpar(1/sqrt(2)) andqpar(1/sqrt(6)).[init] init_field = "phi": initialize a requested electrostatic potential profile by inverting the same quasineutrality solve used during time advance. This is the appropriate contract for zonal-response literature tests that prescribephi(t=0)rather than a density-moment perturbation; the maskedky=0, kx=0gauge mode remains unavailable.[init] init_electrons_only: iftruein multispecies runs, initialize only electron species. Iffalse(default), initialize all kinetic species.[init] random_seed: RNG seed used for reference-compatible random initial conditions. The runtime follows a Linuxglibcrand()sequence with positive-kx-major loop order and exact startup loop bounds, so random multi-mode perturbations are host-platform independent and reproduce the same seeded pattern on macOS and Linux.[init] init_file: load a saved complex state from either the full-kyGKX layout or a packed positive-kyinterchange layout.[init] init_file_scale/init_file_mode: scale a loaded restart state and eitherreplacethe analytic seed (default) oraddit to the fresh perturbation. This is the general runtime equivalent of restart-scaling and restart-with-perturb workflows.[expert] fixed_modewithiky_fixed/ikx_fixed: keep one Fourier mode exactly frozen during nonlinear evolution; this is theeqfix-style contract used by thesecondarybenchmark.[expert] source/phi_ext: runtime-only benchmark hooks for external electrostatic forcing.source = "phiext_full"with a smallphi_extinjects the source into the solvedphifield before the RHS is assembled, so it changes the actual evolution rather than only the saved diagnostics. The Merlo Case-III Rosenbluth-Hinton/GAM example uses the literature protocol instead:source = "default"plus an initial ion density perturbation.[time] run_to: nonlinear stop policy."saturation"(the default) ends a diagnosed nonlinear run as soon as the post-spin-up heat-flux window has converged, witht_maxkept as the hard cap;"t_max"always integrates the full horizon. Linear runs and nonlinear runs withdiagnostics = falseignore the key, because the stop test needs the streamed heat-flux trace. The executable overrides are--until-saturatedand--no-until-saturated. Set"t_max"explicitly on any deck whose heat flux is not the observable it exists to produce – zonal-flow and secondary-instability responses, linear-response diagnostics – because a horizon chosen by a quantity that is zero by construction is not a horizon. Which shipped decks do that, and the measurement clearing the rest to run under the default, are held by therun_toaudit intests/release/test_release_gates.py.[time] saturation_rel_sem: target relative standard error of the mean on the windowed heat flux (default0.05, i.e. 5%). The SEM is corrected for autocorrelation using a first-negative-lag truncation estimate (functionsokal_autocorrelation_time). It is not a calibrated sequential confidence guarantee. Fixed-horizon AR(1) coverage and a drifting-prefix rejection control are tested intests/unit/quasilinear/test_quasilinear.py.[time] saturation_min_window: minimum averaging-window span in time units before a run may stop. The effective requirement is the larger of this value and twenty integrated autocorrelation times; omitting it retains the correlation-time floor. Every accepted window also retains at least 256 samples. These are finite-sample safeguards calibrated on regularly sampled synthetic traces, not a guarantee for irregular adaptive-time sampling or arbitrary nonstationarity.[time] nstep_restart: when writing a nonlinear NetCDF bundle, checkpoint everynstep_restartsteps instead of waiting for the end of the run. This is useful for long adaptive runs and batch jobs.[time] method = "sspx3": use the SSPx3 explicit scheme directly. This is the relevant explicit method forsecondaryand collisional-ETG benchmark families. Plainrk3is the three-stage Heun-style scheme. The nameseuler,k10,rk3_heunandrk3_classicwere retired and raise an error naming the replacement (rk2,rk4,rk3,rk3). Linear integration also acceptsrk2,rk4, and the diagonal IMEX schemesimexandimex2(ARS(2,2,2), second order).[terms]: each key is a pure multiplicative operator weight:streaming,mirror,curvature,gradb,diamagnetic,collisions,hypercollisions,hyperdiffusion,end_damping,apar,bpar,nonlinear.
Runtime parallelization controls
The [parallel] section is parsed by RuntimeParallelConfig and is
serial by default:
[parallel]
strategy = "serial"
axis = "ky"
batch_size = 2
num_devices = 2
strict_identity = true
profile = false
backend = "auto"
Accepted strategies are "serial", "batch",
"combined_ky", "device_batch", "pmap", "pjit",
"shard_map", "state", and "velocity". "batch-ky" is an alias
for "combined_ky" (the combined-k_y time-integration scan path), and
"none"/"off" are aliases for "serial". Quasilinear scan artifacts
require per-k_y evaluation. auto = true resolves to
strategy = "shard_map" with axis = "species_hermite".
For runtime k_y scans, strategy = "batch" with axis = "ky" selects
the production independent-worker path. num_devices is interpreted as the
requested worker count when the executable call does not explicitly pass
workers; backend = "thread" or "process" selects the executor, and
"auto" keeps the executable default. The runtime output records whether
the worker policy came from the TOML file or from explicit executable
arguments, together with the requested/effective worker counts and the
ordering-preservation identity contract.
A k_y scan reads strategy at two levels, and each strategy belongs to
exactly one of them. "batch", "combined_ky", "device_batch",
"pmap" and "pjit" are scan-level: they say how the k_y points are
dispatched, the scan honours them itself, and each worker then runs an ordinary
single-k_y solve with strategy = "serial". Their reported growth rates
are bit-identical to a serial scan. "velocity", "state" and
"shard_map" are solver-level: they say how one solve is decomposed, so
they reach the per-k_y solve unchanged and are honoured or refused there on
the solver’s own terms. Only "velocity" has a linear RHS route today, so a
k_y scan requesting "state" or "shard_map" raises rather than
quietly running unsharded.
The velocity-space RHS route is diagnostic: strategy = "velocity", axis = "hermite", and
backend = "streaming_only", backend = "streaming_electrostatic", or
backend = "electrostatic_linear_slices". backend = "auto" selects the
electrostatic-slices route when the active terms satisfy that gate; otherwise
the runtime raises instead of silently falling back to an unvalidated path.
These backends are accepted only by gkx.linear_rhs_parallel_cached.
The first two require all non-streaming linear terms disabled. The
electrostatic-slices backend allows only streaming, mirror, curvature, grad-B,
and diamagnetic-drive weights; collisions, linked boundaries, electromagnetic
terms, and nonlinear terms are refused. Velocity RHS routes are limited to
single-species periodic 5D electrostatic states.
For full runtime TOML files this velocity-space route is exposed only through
the fixed-step linear executable path with fit_signal = "phi".
Density-assisted automatic fitting is serial only, so requesting velocity
parallelization there raises an error.
For independent scan, sensitivity, and UQ workloads, use
gkx.batch_map for JAX-array maps and gkx.independent_map
for file-backed Python tasks such as calibration rows or leave-one-out UQ
holdouts. Require a serial identity artifact before using timing results in a
publication claim. The helpers preserve ordering, so diagnostics such as growth
rates, frequencies, quasilinear weights, and covariance summaries can be
carried through one parallel map.
Runtime output and restart controls
The [output] section controls runtime artifact layout and restart behavior:
path: artifact target. Use a plain prefix such astools_out/runtime_casefor JSON/CSV sidecars, or*.out.ncfor a nonlinear NetCDF restart bundle.restart: force loading fromrestart_from_fileor from the derived sibling*.restart.ncnext topath. Raise an error if the restart file is missing.restart_if_exists: opportunistically resume from an existing restart file without requiring one to be present.save_for_restart: write the*.restart.nccheckpoint when a nonlinear NetCDF bundle is requested.restart_to_file/restart_from_file: explicit checkpoint paths when the default sibling naming is not desired.restart_with_perturb: combine the loaded restart state with a fresh analytic seed instead of fully replacing it. Internally this maps ontoinit_file_mode = "add".restart_scale: multiplicative scale applied to the loaded restart state.append_on_restart: append continued diagnostic history to the existing*.out.ncfile instead of replacing it.warm_start: carry a converged state from one point of a repeated workload to the next instead of restarting every point cold. It is opt-in (defaultfalse) and has no effect on a single run, which has no predecessor. See Warm-started scans below.resolved_diagnostics: materialize mode-resolved spectra in nonlinear diagnostic history. It defaults totruefor publication and restart artifacts. Set it tofalsewhen only scalar time traces such as heat and particle fluxes, free energy, field energy, growth rate, and frequency are required. The compact path preserves those scalar channels while avoiding the larger spectral-history arrays.nsave: checkpoint cadence fallback, in steps, for nonlinear NetCDF bundles whentime.nstep_restartis not set.plots: draw the run’s figures beside its output once it finishes. It defaults totrue; set it tofalse(or pass--no-plots) on batch surfaces that only want the data. See Saved Outputs and Restart Files for the figure set each run kind writes.
For direct restart control outside the [output] helper path, the generic
[init] init_file / init_file_scale / init_file_mode keys remain the
lower-level mechanism.
Warm-started scans
[output] restart reuses a state written to disk by an earlier run.
[output] warm_start is its in-memory sibling for work that repeats inside
one invocation: a converged eigenmode at one ky is an excellent initial
guess at a neighbouring ky, so the next point can start from the answer
instead of rediscovering it from the analytic seed. How much that is worth
depends entirely on the solver, and Performance measures both cases
before you decide.
gkx scan-runtime-linear therefore walks the requested ky list in
monotone order – so that neighbours follow neighbours regardless of the order
you asked for – and seeds each point from the previous point’s final state.
On the eigensolver paths that state is the converged eigenvector and becomes
the Krylov starting vector; on the time-integration paths it is the final
distribution function. Results are written back into the order you requested,
so the reported arrays are unchanged in shape, ordering, and meaning.
The carried state is rescaled by an exact power of two before reuse. Both the
eigensolve and the growth-rate fit are invariant to the overall scale of the
initial condition, so this loses nothing, and it stops a state that has grown
by exp(gamma T) from overflowing. A state that comes back non-finite or
zero is refused and the next point starts cold.
Turn it on with --warm-start, with [scan] warm_start = true, or with
[output] warm_start = true; the flag wins over [scan], which wins over
[output]. It is opt-in rather than the default because it is not free and
not always identical – see Performance for the measurements. GKX also
declines to warm start on its own when carrying state would break a contract
you asked for:
--workersgreater than one, or[parallel] strategy = "batch", because independent workers must stay independent;solver = "explicit_time", which cannot return a final state;--batch-kyand[parallel] strategy = "combined_ky", which already integrate everykyin one pass and so have nothing to carry.
Two things to know before switching it on. First, on a fixed horizon a
warm-seeded time integration reports a different growth rate than a cold one,
because it has removed a startup transient the cold run still contains; the two
agree only once the horizon is long enough that both are converged, which is
also why the GX-parity decks are left cold. Second, an eigensolver started
inside one branch can converge to that branch. On a case whose branch structure
you do not already know, run the scan once with --no-warm-start and compare
point by point before trusting the warm numbers.
run_runtime_parameter_scan takes the same idea as an opt-in
warm_start=True: the previous point’s state is reused only while the
parameter moves by no more than warm_start_max_step (relative, default
0.25) from the point before it. That is weaker than continuation=True,
which follows one branch and requires every point to return a state.
For the explicit equations attached to these controls, see Operators And Terms.
Explicit reference migration
For a resolved legacy deck with a known execution route:
from gkx.workflows.runtime.toml import migrate_end_damping_reference
case = migrate_end_damping_reference(
"legacy.toml", route="fixed_linear", dt_step=0.02,
) # actual reference step, not an assumed input default
case.to_toml("fixed_rate.toml")
Use route="timestep_free" without dt_step for a reference RHS that
received no solver timestep. The helper does not run a simulation or overwrite
the source. It records [damping_reference]: source path/SHA-256, route,
amplitude, input timestep, actual reference step and old scaling flag. The
record survives gkx.load / case.to_toml; no source file is needed to
reload it. A changed rate with stale provenance is rejected at runtime: clear
damping_reference when intentionally defining a new model.
Migration is host-side bookkeeping; differentiate the resulting
LinearParams.damp_ends_rate, not the file-reading helper.
Let \(s=1\) for the old true scaling flag and \(s=0\) otherwise:
This preserves the isolated RHS and RK stage map at the reference step.
Subsequent timestep refinement holds \(\nu_{ref}\) fixed; it does not
preserve the legacy per-step model at every timestep. Adaptive linear sources,
conflicting run.dt overrides and already-converted decks are rejected.
Resolve any command-line timestep override into the source deck first.
This is provenance and algebraic equivalence, not benchmark recertification.