Equations and derivations¶
This page states the explicit equations solved (or approximated) by
vmec_jax and connects them to the VMEC2000 formulation. The goal is to make
the physics, the coordinate conventions, and the force-balance residuals fully
transparent so that parity checks can be done equation-by-equation.
Notation and conventions¶
We use VMEC’s curvilinear flux coordinates:
\(s \in [0,1]\): normalized toroidal flux label (VMEC radial coordinate).
\(\theta \in [0,2\pi)\): poloidal angle.
\(\zeta \in [0,2\pi)\): field-period toroidal angle.
\(\phi_{\mathrm{phys}} = \zeta/\mathrm{NFP}\): physical toroidal angle.
The Fourier phase convention is:
where \(n\) is the field-period toroidal mode number
(VMEC stores \(xn = n\,\mathrm{NFP}\) in wout).
VMEC weighted DFT tables (fixaray)¶
VMEC does not use a plain FFT for its force/residual transforms. Instead,
fixaray builds weighted trig tables on a symmetry-aware grid and applies
explicit normalization factors. Let \(\theta_i\) be the VMEC theta grid
over \([0,\pi]\) (with endpoint half-weights) and \(\zeta_k\) the zeta
grid over one field period. VMEC defines
and the weighted cosine table
with \(w_0=w_{n_{\theta2}-1}=1/2\) and \(w_i=1\) elsewhere. The sine
table is defined analogously, with the same weights and mscale. Zeta tables
use nscale (also \(\sqrt{2}\) for \(n>0\)) and, for derivative
terms, include the field-period multiplier \(n\,\mathrm{NFP}\):
vmec_jax uses these tables in tomnsps so that the Fourier-space force
arrays exactly match VMEC2000. See References [4-6] for the original VMEC2000
tables and the VMEC++ DFT/basis discussion.
Two-stage DFT for tomnsps¶
VMEC’s tomnsps uses a separable real basis in \(\theta\) and
\(\zeta\). For a real-space kernel \(F(\theta_i,\zeta_k)\) defined on the
VMEC grid, the weighted theta projection is
The zeta projection then yields the Fourier coefficients
Derivative terms in VMEC use the scaled tables
\(\mathrm{cosnvn}_{k,n}=(n\,\mathrm{NFP})\,\mathrm{cosnv}_{k,n}\) and
\(\mathrm{sinnvn}_{k,n}=-(n\,\mathrm{NFP})\,\mathrm{sinnv}_{k,n}\). In
vmec_jax we therefore compute the same base transforms and apply the
analytic factor \(n\,\mathrm{NFP}\) after the zeta contraction for the
derivative blocks. This reduces the number of dot-product contractions while
preserving VMEC2000 parity exactly.
Implementation detail: the theta contractions for multiple force kernels are
stacked into a single batched dot_general call (GEMM), and the zeta
contractions are likewise stacked by basis type (cosine vs sine). This follows
the separable product identities (see Eqs. 5.55–5.56 in the VMEC++ numerics
notes) while keeping the VMEC2000 normalization and parity masks intact.
Ideal MHD equilibrium¶
The ideal MHD equilibrium is defined by:
with Maxwell’s equations (in magnetostatic form):
The pressure is a flux function: \(p = p(s)\) and is specified by the
VMEC input profiles. VMEC (and vmec_jax) use pressure in units of
\(\mu_0\,\mathrm{Pa}\) so that \(p\) has the same units as \(B^2\).
Energy principle (VMEC formulation)¶
VMEC solves for a stationary point of the ideal-MHD energy functional in straight-field-line coordinates:
where \(\gamma\) is the ratio of specific heats (VMEC input GAMMA).
The VMEC fixed-boundary update loop can be viewed as a (preconditioned)
steepest-descent method that drives the force residuals to zero.
See References [1-3] for the original VMEC formulation.
Flux coordinates and straight-field-line angle¶
VMEC introduces a scalar field \(\lambda(s,\theta,\zeta)\) to define the straight-field-line poloidal angle:
Field lines are straight in \((u,\zeta)\):
where \(\iota(s)\) is the rotational transform.
Internal scaling and regularity (scalxc)¶
VMEC enforces regularity at the magnetic axis by storing odd-m contributions in an internal form that factors out \(\sqrt{s}\):
Equivalently,
VMEC implements this via the scalxc array, which is 1 for even-m harmonics
and \(1/\sqrt{s}\) for odd-m harmonics. scalxc is applied when
interpolating coefficients between radial grids and when assembling
preconditioned residuals (VMEC2000 profil3d / interp / scalxc).
On the axis, VMEC applies odd-m rules:
\(m=1\): extrapolate the internal odd field to the axis by copying the first off-axis value,
\(m\ge 2\): force the internal odd field to zero on-axis.
m=1 internal constraint (lconm1)¶
When LCONM1 is enabled (VMEC default for 3D runs), VMEC stores the m=1
boundary coefficients in a constrained internal basis:
This transformation is applied in VMEC2000 readin and inverted when
converting to physical coefficients for diagnostics. vmec_jax uses the same
internal basis so that boundary handling and multigrid interpolation match
VMEC2000.
Magnetic field representation¶
In VMEC’s flux-coordinate representation, the magnetic field has no radial contravariant component:
VMEC therefore stores only the contravariant components in the angular directions:
In terms of VMEC’s flux functions \(\Phi(s)\) (toroidal flux) and \(\chi(s)\) (poloidal flux), we define:
VMEC’s contravariant components (bsupu and bsupv in wout)
are computed as:
Here:
\(\sqrt{g}\) is the signed Jacobian,
signgsis VMEC’s sign convention such thatsigngs*sqrtgis positive away from the magnetic axis,lamscaleis the VMEC scaling applied to \(\lambda\) derivatives (stored inwoutand used byvmec_jaxfor parity).
The covariant components are defined by:
where \(g_{ij}\) is the covariant metric and
\(\mathbf{e}_i = \partial_i \mathbf{r}\).
VMEC stores these as bsub* in wout.
bcovar + add_fluxes (poloidal flux correction)¶
VMEC updates the contravariant \(B^u\) using the full-mesh poloidal
flux function \(\chi'(s)\) (chips). In VMEC2000 add_fluxes,
chips is computed from force balance on each surface:
where the angle brackets denote the VMEC surface quadrature, and
\(I_\varphi(s)\) is the integrated toroidal current (icurv).
VMEC then applies the correction
VMEC stores the half-mesh averaged chipf in wout; vmec_jax
follows VMEC’s averaging rules to convert between chipf and chips.
Current density¶
The current density follows directly from the curl:
VMEC reports covariant current components in wout as jcuru and
jcurv (poloidal and toroidal current densities on the half mesh) and uses
these in the force kernels. The parallel and perpendicular currents satisfy:
For optimization diagnostics, vmec_jax also exposes the JXBFORCE
real-space current channels as
on the full radial mesh. The vj.JVector objective returns these
flux-coordinate components flattened over the selected surfaces and angular
grid. vj.BVector returns the corresponding Cartesian magnetic-field vector
(B_x,B_y,B_z) on one selected radial surface.
Redl Bootstrap-Current Mismatch¶
For finite-beta stage-one studies, vmec_jax exposes a differentiable Redl
bootstrap-current residual. The residual follows the normalized SIMSOPT form
The Redl term uses polynomial density and temperature profiles in the same
ascending-coefficient convention as SIMSOPT ProfilePolynomial. For the
standard finite-beta stage-one examples, vj.standard_finite_beta_profiles
constructs
with ni=ne, Ti=Te, Zeff=1, and
\(p(s)=e(n_eT_e+n_iT_i)\) in Pascals. The amplitudes use the same
scaling as the SIMSOPT finite-beta/bootstrap examples,
vj.with_pressure_profile converts this pressure profile to VMEC AM and
PRES_SCALE input fields while vj.RedlBootstrapMismatch receives the
same density/temperature coefficients. The effective trapped-particle
fraction is evaluated with fixed Gauss-Legendre
quadrature using the substitution
\(y = \sqrt{1-\lambda B_{\max}}\), which removes the endpoint singularity
in the standard integral
This differs from SIMSOPT’s post-processing routine, which refines angular extrema with splines. The vmec_jax form is intentionally fixed-shape and differentiable for use inside exact-Jacobian optimization.
Force balance in VMEC (residual form)¶
VMEC evaluates the force balance in real space, then transforms the residual
forces back to Fourier space. In VMEC2000, these residuals are packaged as
tomnsps Fourier arrays and projected into three directions:
\(F_R\): radial (R) force balance residual,
\(F_Z\): vertical (Z) force balance residual,
\(F_\lambda\): stream-function (lambda) residual.
These are combined into scalar norms that appear in the VMEC screen output:
where fnorm, fnormL, and r1 are the VMEC normalization factors
computed from bcovar (half-mesh metrics + bsup/bsub).
vmec_jax reproduces these scalars from the same internal quantities to
match VMEC2000’s per-iteration printout.
Time-step control (Garabedian update)¶
VMEC’s nonlinear fixed-boundary iteration uses a Garabedian-style conjugate gradient update with a time-step control mechanism. The update computes
and maintains a moving average \(\overline{\tau}\) over ndamp steps.
The damping factor is
The update in VMEC2000 is:
where \(F\) is the preconditioned residual vector (gc).
VMEC’s TimeStepControl tracks the minimum of the preconditioned residual
(res0) and the physical residual (res1). If either grows by a factor of
1e4 after 10 steps, VMEC restores the last good state and reduces
DELT by a factor of 1.03. vmec_jax mirrors this logic to reproduce the
VMEC2000 iteration trace.
Multigrid interpolation (interp.f)¶
VMEC’s multigrid staging interpolates scaled coefficients between grids:
with odd-m extrapolation to the axis performed before interpolation. After linear interpolation on a uniform radial grid, coefficients are unscaled:
vmec_jax implements this exact pipeline so that stage-to-stage coefficient
transfer matches VMEC2000.
Pressure and beta¶
VMEC reports thermal and magnetic energy scalars in wout:
The total volume-averaged beta is computed by VMEC as:
vmec_jax follows the same normalization when emitting wout files.
Geometric embedding and Jacobian¶
Surfaces are represented in cylindrical coordinates by Fourier series:
We embed into Cartesian coordinates using the physical toroidal angle:
The Jacobian is:
VMEC’s sign convention enforces signgs*sqrtg > 0 away from the axis.
Jacobian sign check (tau)¶
VMEC evaluates an auxiliary Jacobian-like scalar \(\tau\) built from
even/odd-m real-space derivatives (see VMEC2000 jacobian.f). In compact
form,
If \(\tau\) changes sign away from the axis, VMEC flags a bad Jacobian and
restarts the iteration with a refined axis guess. vmec_jax reproduces the
same parity split, half-mesh averaging, and sign check so that Jacobian-reset
behavior matches VMEC2000.
Implementation mapping (vmec_jax)¶
Key modules that directly implement the equations above:
vmec_jax/geom.py: geometry, metric, Jacobian.vmec_jax/vmec_bcovar.py: contravariant/covariant field components.vmec_jax/vmec_forces.py: real-space force kernels (A,B,Cblocks).vmec_jax/vmec_tomnsp.py: VMEC-style Fourier transforms of forces.vmec_jax/vmec_residue.py: VMEC scalar residuals (FSQR/FSQZ/FSQL).vmec_jax/solve.py: fixed-boundary iteration loop (VMEC2000 parity path).
References (local)¶
vmecpp/docs/the_numerics_of_vmecpp.pdf(VMEC++ numerics notes; VMEC2000 conventions and formulas).VMEC2000 source in
STELLOPT/VMEC2000/Sources.