Conventions and conversion to GX, GS2 and stella
This page states GKX’s units and sign conventions and shows how to translate
inputs and outputs to GX, GS2 and stella. Normalization covers the
GKX-internal details (calibration contracts, diagnostic scaling, the stored
bpar field).
Each entry below is marked with how it was checked:
code: read from the GKX source (
src/gkx) or from the other code’s source or official documentation;run: confirmed by running the codes on the same case (the 2026-09-27 cross-code record,
plan/research/2026-09-27-xcode/REPORT.md);derived: follows from the unit definitions but was not checked by a run;
unverified: stated from the literature without a check here.
Reference quantities
All four codes are local flux-tube codes. Each normalizes to a reference species (mass \(m_r\), temperature \(T_r\), density \(n_r\)), a reference length \(L_r\) and a reference field \(B_r\). They differ in the reference speed:
GKX sets vth = sqrt(T/m) and rho = sqrt(T m)/|Z| per species in
gkx.operators.linear.params (code). GX uses the same definition. GS2
8.2.1 and stella v1.0 use \(\sqrt{2T/m}\) (GS2 user manual,
Normalisations; run for both: the cross-code runs agree only with the
\(\sqrt{2}\) below applied). The older norm_option switch described in
the GS2 manual does not exist in the 8.2.1 source.
The gyroradius and time unit follow from \(v_r\):
so \(\rho_r^{\mathrm{GS2}} = \sqrt{2}\,\rho_r^{\mathrm{GKX}}\) and \(t_{\rm unit}^{\mathrm{GS2}} = t_{\rm unit}^{\mathrm{GKX}}/\sqrt{2}\).
Reference length. The shipped tokamak decks use \(L_r = a\), the
minor radius, and give the major radius as R0 = R/a = 2.77778 (code).
GX’s Cyclone decks do the same (Rmaj = 2.77778). GS2 normalizes all lengths
to \(a\) (manual), and in its s-alpha model enters \(a/R\) through
epsl = 2a/R and pk = 2a/(qR). stella’s Miller namelist takes rmaj
in units of \(a\). Nothing forces \(L_r = a\): a deck with
R0 = 1 measures lengths in \(R\), and then gradients and rates are
per \(R\). Always check which length a published number uses.
Reference field. GX and GKX Miller geometry use \(B_r = B_t(R_{\rm geo})\)
(the R_geo key); GS2 uses the toroidal field at \(R_a\), the centre of
the flux surface. The two agree when R_geo equals Rmaj, as in every
Cyclone deck here (code, GX and GS2 docs).
Conversion formulas
Write \(\hat X\) for a normalized quantity. From the definitions above, with the same \(L_r\), \(B_r\) and reference species in both codes:
Quantity |
GKX (= GX) from GS2 / stella |
Status |
|---|---|---|
binormal wave number |
\(\hat k_y^{\rm GKX} = \hat k_y^{\rm GS2}/\sqrt{2}\) |
run |
radial wave number |
\(\hat k_x^{\rm GKX} = \hat k_x^{\rm GS2}/\sqrt{2}\) |
derived |
ballooning angle |
\(\theta_0\) unchanged |
code |
frequency, growth rate |
\(\hat\omega^{\rm GKX} = \sqrt{2}\,\hat\omega^{\rm GS2}\), \(\hat\gamma^{\rm GKX} = \sqrt{2}\,\hat\gamma^{\rm GS2}\) |
run |
time |
\(\hat t^{\rm GKX} = \hat t^{\rm GS2}/\sqrt{2}\) |
derived |
collision frequency |
\(\hat\nu^{\rm GKX} = \sqrt{2}\,\mathtt{vnewk}^{\rm GS2}\) |
derived |
heat and particle flux |
\(\hat Q^{\rm GKX} = 2\sqrt{2}\,\hat Q^{\rm GS2}\), \(\hat\Gamma^{\rm GKX} = 2\sqrt{2}\,\hat\Gamma^{\rm GS2}\) |
derived |
velocity-grid extent |
\(v/v_t^{\rm GKX} = \sqrt{2}\, v/v_r^{\rm stella}\)
( |
derived |
\(\beta\), |
unchanged |
code |
The flux factor comes from the gyro-Bohm unit
\(Q_{gB} = n_r T_r v_r (\rho_r/L_r)^2\), which carries \(v_r^3\), so
\(Q_{gB}^{\rm GS2} = 2\sqrt{2}\,Q_{gB}^{\rm GKX}\). The collision
frequency is a rate, so it converts like \(\gamma\). GS2’s manual defines
vnewk with \(v_{thr} = \sqrt{2T/m}\); GX reads vnewk into its
nu_ss with no rescaling (parameters.cu), so GX’s vnewk is already
in GKX units.
stella follows the same table (run, 2026-10-24). With it, stella’s growth
rate on the adiabatic Cyclone Miller surface is 1.39x GKX’s at
\(k_y\rho_i=0.30\), 1.37–1.49x over 0.25–0.71, 1.6x at 0.18 and 2.0x at
0.11. The
excess is not a missed \(\sqrt2\): GS2 takes the identical mapping and
agrees with GX to 0.3%. Dropping the \(\sqrt2\) on \(\gamma\) while
keeping \(k_y/\sqrt2\) brings \(\gamma\) within 5% for
\(k_y\rho_i\) 0.25–0.71, but not below (0.70 of GX at 0.11) and not
\(\omega\), which stays 5–21% low, nor the W7-X scan (1.2–1.7x). A
two-parameter fit of the rescalings to the stella points leaves 7% residuals.
The stella numbers are in docs/_static/cross_code_linear_points.csv and are
not used as a reference.
GX needs no conversion: GKX was built to reproduce GX, and the certified GKX eigenvalues agree with GX to 0.3% or better on the Cyclone decks (README, Benchmarks).
Gradients and geometry parameters
tprim and fprim are \(L_r/L_T = -L_r\, d\ln T/dr\) and
\(L_r/L_n\), positive for profiles that peak on axis. The names and signs
are the same in all four codes (code). Convert to \(R/L_T\) with
\(R/L_T = (R/a)(a/L_T)\): Cyclone’s tprim = 2.49 is
\(R/L_T = 6.92\).
Safety factor and shear are \(q\) and \(\hat s = (r/q)\,dq/dr\), with
\(r\) the half-diameter (GS2 irho = 2, its default). GKX’s keys are
q and s_hat; GX, GS2 and stella Miller use qinp and shat.
In the circular s-alpha model GKX builds (gkx.geometry.analytic,
code)
and \(k_x(\theta) = k_{x0} - (\hat s\theta - \alpha\sin\theta)k_y\).
GX’s s-alpha branch (geometry.cu) has the same expressions, with
\(\alpha\) entered as shift; GKX calls it alpha. GS2’s
gbdrift/cvdrift are twice GKX’s for the same geometry, because of the
\(\sqrt{2}\) in \(\rho_r\) (run, through the matched growth
rates). The sign of GS2’s s-alpha shift relative to \(\alpha\) was not
checked (unverified).
What \(\alpha\) does and does not do in GKX’s s-alpha model (code,
gkx/geometry/analytic.py): it enters the local shear
\(\hat s\theta - \alpha\sin\theta\) in \(k_x(\theta)\) (line 242),
in the metric gds2/gds21 that build \(k_\perp^2\) (line 266), and
in the drift numerator (line 284). The curvature and \(\nabla B\) drifts
are set equal, gb = cv (line 288), exactly as in GX’s s-alpha branch
(gbdrift = cvdrift). There is no separate \(\beta'\) correction that
would make the curvature drift differ from the \(\nabla B\) drift, as in
Hastie-Hesketh-type or Aleynikova-Zocco analytic models. A benchmark that
needs that difference must use Miller geometry with betaprim, or supply
the drift arrays through an imported geometry file.
The Miller keys follow GX: rhoc, R0 (GX Rmaj), R_geo,
shift, akappa, akappri, tri, tripri, betaprim. stella
spells two of them kappa and kapprim, and triprim; GS2 uses
rmaj and r_geo.
Parallel coordinate
GKX uses \(z \in [-\pi Z_p, \pi Z_p)\) with \(Z_p = 2n_{\rm period}-1\)
and ntheta points per \(2\pi\) (GridConfig). GS2’s theta and
stella’s zed span the same \(2n_{\rm period}-1\) poloidal turns for the
same nperiod. The linked boundary condition is boundary = "linked" in
GKX and boundary_option = "linked" in GS2 and stella.
The ballooning angle is \(\theta_0 = k_x/(\hat s k_y)\) in GKX (from the
\(k_x(\theta)\) above), GS2 (kt_grids_box.f90) and stella
(geometry_miller.f90). It is a ratio of wave numbers, so the
\(\sqrt{2}\) cancels.
Perpendicular grid
\(k_y = n/y_0\) and \(k_x = n/x_0\) with \(L_y = 2\pi y_0\) and
\(L_x = 2\pi x_0\) in units of \(\rho_r\) (gkx.core_grid, GX
documentation). GKX’s ky output is \(k_y\rho_i\) with
\(\rho_i = \sqrt{T_i m_i}/(eB)\); GS2’s aky is \(k_y\rho_i\) with
the \(\sqrt{2}\) gyroradius. Cyclone at ky = 0.3 in GKX is
aky = 0.4243 in GS2 and stella.
The ky-resolved spectra in GKX’s NetCDF output hold one row of each
\((k_y,-k_y)\) pair; GX writes the pair sum, twice these values for
\(k_y>0\) (Saved Outputs and Restart Files, code).
Fourier and frequency signs
Fields are expanded as \(\phi = \sum_{\mathbf k} \hat\phi_{\mathbf k}
e^{i(k_x x + k_y y)}\) (jnp.fft.fftfreq ordering), and growth rates are
fitted to \(\hat\phi \propto e^{(\gamma - i\omega)t}\)
(fit_growth_rate, code). With this time convention the Cyclone ITG mode
has \(\omega > 0\) in all four codes: GKX and GX (tracked reference CSV),
GS2 (\(\hat\omega = 0.153\) at aky = 0.4243, Miller) and stella
(\(\hat\omega = 0.178\)) (run). Positive \(\omega\) is the
ion-diamagnetic direction for \(k_y > 0\): the tracked goldens that GKX
reproduces have \(\omega > 0\) for ITG
(src/gkx/data/cyclone_reference_adiabatic.csv) and KBM, and
\(\omega < 0\) for ETG (src/gkx/data/etg_reference.csv, e.g.
\(\omega = -8.73\) at \(k_y = 10\)). The drive enters as
\(i\,k_y\) times the species factor tz \(= Z_s/T_s\)
(_diamagnetic_scalar_factors in gkx.terms.linear_terms), so the
charge sign fixes the direction. No sign flip is needed between the codes for
omega; compare signed frequencies, not \(|\omega|\).
Fields
GKX evolves \(\hat\phi = (e\phi/T_r)(L_r/\rho_r)\), the standard
gyrokinetic ordering. The GS2 and stella fields carry their own
\(\rho_r\), so \(\hat\phi^{\rm GKX} = \sqrt{2}\,\hat\phi^{\rm GS2}\)
at equal physical amplitude (derived). Linear amplitudes are arbitrary, so
this only matters for nonlinear spectra. The normalizations of
\(A_\parallel\) and \(\delta B_\parallel\) in GS2 and stella were not
compared here (unverified); GKX’s bpar convention is in
Normalization.
\(\beta_r = 8\pi n_r T_r/B_r^2\) in GKX ([physics] beta), GX
(beta, Inputs documentation), GS2 and stella (beta). It contains no
velocity, so it needs no conversion.
Velocity space
GKX and GX expand the distribution in Hermite polynomials of
\(v_\parallel/v_t\) and Laguerre polynomials of \(\mu B/T\), with
\(v_t = \sqrt{T/m}\), truncated at Nm and Nl (Theory). GS2
uses energy and pitch-angle grids (negrid, ngauss); stella uses
\((v_\parallel, \mu)\) grids (nvgrid, nmu) with extents
vpa_max and vperp_max in units of \(\sqrt{2T/m}\). There is no
one-to-one map between these resolutions; converge each code separately.
GKX’s model collision operator takes nu per species plus the
[collisions] Hermite/Laguerre coefficients, following GX. The collision
model itself differs between codes (GS2 and stella default to their own
operators), so matching \(\hat\nu\) does not guarantee matching damping.
Input keys
Quantity |
GKX TOML |
GX TOML |
GS2 namelist |
stella namelist |
|---|---|---|---|---|
binormal wave number |
|
|
|
|
T, n gradients |
|
|
|
|
collision frequency |
|
|
|
|
safety factor, shear |
|
|
|
|
inverse aspect ratio |
|
|
|
via |
reference beta |
|
|
|
|
T_i/T_e (adiabatic e) |
|
|
|
|
parallel extent |
|
|
|
|
GX keys are those of the GX Inputs documentation. tau_e in GKX is the
temperature ratio used by the Boltzmann closure (Normalization); check
the direction of the ratio in each code before copying a value other than 1.
Worked example: Cyclone base case
Circular geometry, adiabatic electrons, \(L_r = a\), one mode at \(k_y\rho_i = 0.3\) in GKX units.
GKX (examples/01_linear_tokamak/case_full.toml, abridged):
[[species]]
tprim = 2.49
fprim = 0.8
nu = 0.0
[geometry]
q = 1.4
s_hat = 0.8
epsilon = 0.18
R0 = 2.77778
[grid]
ntheta = 32
nperiod = 2
boundary = "linked"
[run]
ky = 0.3
GS2 s-alpha (aky is \(\sqrt{2}\times 0.3\)):
&kt_grids_single_parameters aky = 0.4242640687 theta0 = 0.0 /
&theta_grid_parameters eps = 0.18 epsl = 0.72 shat = 0.8
pk = 0.5142857 ntheta = 32 nperiod = 2 /
&theta_grid_knobs equilibrium_option = "s-alpha" /
&species_parameters_1 z = 1.0 mass = 1.0 dens = 1.0 temp = 1.0
tprim = 2.49 fprim = 0.8 vnewk = 0.0 /
stella circular Miller:
&geometry_miller rhoc = 0.5 qinp = 1.4 shat = 0.8
rmaj = 2.77778 rgeo = 2.77778 shift = 0.0
kappa = 1.0 tri = 0.0 /
&kxky_grid_range naky = 1 aky_min = 0.4242640687
aky_max = 0.4242640687 /
&species_parameters_1 tprim = 2.49 fprim = 0.8 /
GX uses the GKX numbers unchanged (tprim = 2.49, qinp = 1.4,
shat = 0.8, eps = 0.18, Rmaj = 2.77778, \(k_y\) from y0).
Measured growth rates, circular Miller, \(k_y\rho_i = 0.3\) (cross-code record, section 2):
Code |
\(\hat\gamma\) (own units) |
\(\hat\gamma\) (GKX units) |
|---|---|---|
GKX, certified eigenpair |
0.1259 |
0.1259 |
GS2 8.2.1 |
0.0887 |
0.1255 |
stella v1.0 |
0.1236 |
0.1748 |
GS2 agrees with GKX to 0.3% after the \(\sqrt{2}\). stella is 1.4 times higher on this case; the record traces the gap to stella’s mirror term, not to a unit conversion.