The gap
Nothing in VAFT computes q(psi) from a psi map and an F(psi) profile.
Verified by searching claude/vaft-vfit-foundation for safety factor,
q_profile, def q, qpsi, q_95: everything that exists runs the other
way, or consumes a q that came from somewhere else.
vaft/formula/equilibrium.py:335 q_from_phi(psi, phi) — needs the toroidal
flux already.
vaft/omas/update.py:629 update_equilibrium_profiles_1d_toroidal_flux — goes
q -> Phi -> rho_tor, i.e. it consumes q.
vaft/omas/update.py:270 update_equilibrium_global_quantities_q_min — reads
profiles_1d.q.min().
vaft/data/eqdsk.py:937,964 — q and q_95 are only ever read in from a
g-file's QPSI.
So any ODS whose equilibrium was computed natively rather than imported from a
g-file has no route to q at all.
Proposed addition
q(psi) = sigma * F(psi) * <1/R^2> * (dV/dpsi) / (2*pi)^2
All three inputs are already outputs of
vaft.process.equilibrium.flux_surface_quantities
(vaft/process/equilibrium.py:1385): gm1 = <1/R^2>, dvolume_dpsi, and
f_profile is already an accepted keyword argument on the same call. So this is
a small, well-posed addition on top of machinery that exists and is tested.
Points to settle in review:
- Units.
flux_surface_quantities returns dvolume_dpsi per radian, and
its psi_grid contract is Wb/rad. The (2*pi)^2 above assumes that; state the
convention explicitly and test it, since a 2π slip here is invisible in the
profile shape and only shows up in the magnitude.
- Sign (
sigma) follows the COCOS convention already resolved by
vaft.process.equilibrium.as_equilibrium(...).convention.
- On-axis limit.
flux_surface_quantities documents that its innermost one
or two levels are the least accurate anywhere, and that volume/area are
exactly 0 at levels_norm == 0. q_axis needs an explicit extrapolation
policy rather than whatever falls out of a 0/0.
Why now
Three consumers, immediately:
- VFIT's
FluxGSResult.q_profile is a nan placeholder
(vfit/flux_gs.py:769) with nothing to fill it.
update_equilibrium_derived_profiles chains through the toroidal-flux step,
which consumes q — so a natively-computed equilibrium may not complete the
chain (see VEST-Tokamak/vfit#43).
- A
q-on-a-surface reconstruction constraint (EFIT-style) needs it.
Per VFIT's CLAUDE.md responsibility split, this is a physics formula useful to any
VEST analysis code, so it belongs here rather than in VFIT.
The gap
Nothing in VAFT computes
q(psi)from a psi map and anF(psi)profile.Verified by searching
claude/vaft-vfit-foundationforsafety factor,q_profile,def q,qpsi,q_95: everything that exists runs the otherway, or consumes a
qthat came from somewhere else.vaft/formula/equilibrium.py:335q_from_phi(psi, phi)— needs the toroidalflux already.
vaft/omas/update.py:629update_equilibrium_profiles_1d_toroidal_flux— goesq -> Phi -> rho_tor, i.e. it consumes q.vaft/omas/update.py:270update_equilibrium_global_quantities_q_min— readsprofiles_1d.q.min().vaft/data/eqdsk.py:937,964—qandq_95are only ever read in from ag-file's
QPSI.So any ODS whose equilibrium was computed natively rather than imported from a
g-file has no route to
qat all.Proposed addition
All three inputs are already outputs of
vaft.process.equilibrium.flux_surface_quantities(
vaft/process/equilibrium.py:1385):gm1 = <1/R^2>,dvolume_dpsi, andf_profileis already an accepted keyword argument on the same call. So this isa small, well-posed addition on top of machinery that exists and is tested.
Points to settle in review:
flux_surface_quantitiesreturnsdvolume_dpsiper radian, andits
psi_gridcontract is Wb/rad. The(2*pi)^2above assumes that; state theconvention explicitly and test it, since a 2π slip here is invisible in the
profile shape and only shows up in the magnitude.
sigma) follows the COCOS convention already resolved byvaft.process.equilibrium.as_equilibrium(...).convention.flux_surface_quantitiesdocuments that its innermost oneor two levels are the least accurate anywhere, and that
volume/areaareexactly 0 at
levels_norm == 0.q_axisneeds an explicit extrapolationpolicy rather than whatever falls out of a 0/0.
Why now
Three consumers, immediately:
FluxGSResult.q_profileis ananplaceholder(
vfit/flux_gs.py:769) with nothing to fill it.update_equilibrium_derived_profileschains through the toroidal-flux step,which consumes
q— so a natively-computed equilibrium may not complete thechain (see VEST-Tokamak/vfit#43).
q-on-a-surface reconstruction constraint (EFIT-style) needs it.Per VFIT's CLAUDE.md responsibility split, this is a physics formula useful to any
VEST analysis code, so it belongs here rather than in VFIT.