Validation and parity with VMEC2000¶
vmec_jax uses a layered validation matrix. Required CI fetches released
VMEC2000-produced wout fixtures before the parity gates, while a fresh git
clone keeps only the small input decks needed to generate new wout files.
The validation matrix includes no-solve profile/current gates,
convergence-only end-to-end gates, and focused physics regressions. Direct
comparisons against a local VMEC2000 Fortran executable are opt-in
vmec2000 tests because they require an external executable and are not part
of the default PR gate.
Parity means: given the same input namelist and convergence settings, the
wout_*.nc output of vmec_jax agrees with the relevant VMEC2000 reference
or executable run to within tolerances set by the convergence level (not by
implementation error).
Reference data¶
Eleven wout reference files are pre-computed with VMEC2000 and shipped as
release assets restored by python tools/fetch_assets.py. A stable subset is used for strict field-by-field
end-to-end parity in tests/test_wout_comprehensive_parity.py; the remaining
references are covered by no-solve profile/current gates, convergence-only
end-to-end gates, or optional refreshed-reference lanes until their released
references are promoted.
Input |
Coverage |
lasym |
bdy |
|---|---|---|---|
|
axisymmetric, no pressure |
False |
fixed |
|
axisymmetric, pressure profile |
False |
fixed |
|
axisymmetric D-shape (STELLOPT) |
False |
fixed |
|
3D quasi-helical (nfp=4) |
False |
fixed |
|
3D quasi-axisymmetric (nfp=2) |
False |
fixed |
|
3D quasi-isodynamic (nfp=3) |
False |
fixed |
|
QI far-seed solved-state fixture |
False |
fixed |
|
3D SIMSOPT reference (nfp=3) |
False |
fixed |
|
3D current-driven (CTH-like) |
False |
fixed |
|
zero-current special case |
False |
fixed |
|
lasym=True SIMSOPT reference |
True |
fixed |
Reference WOUT fixtures and large mgrid files not shipped with the git repo can be fetched once:
python tools/fetch_assets.py
Automated parity tests¶
Required CI includes a no-executable residual parity gate:
PYTHONDONTWRITEBYTECODE=1 JAX_ENABLE_X64=1 pytest -q -p no:cacheprovider \
tests/test_residue_getfsq_parity.py \
tests/test_wout_profiles_currents_bundled_parity.py \
tests/test_physics_parity_helper_gates.py \
tests/test_vmec_parity_physics_fast_gates.py \
tests/test_wout_physics_gates.py \
tests/test_converged_wout_matrix_parity.py \
tests/test_wout_fixture_inventory.py \
tests/test_vmec2000_exec_threed1.py \
tests/test_parity_sweep_manifest_thresholds.py
tests/test_residue_getfsq_parity.py reads released VMEC2000 wout
files, reconstructs the solved state, recomputes the
bcovar -> forces -> tomnsps -> getfsq scalar-residual path, and compares
fsqr, fsqz, and fsql to the VMEC2000-stored values. It currently
covers circular_tokamak and shaped_tokamak_pressure without running
VMEC2000 or a full vmec_jax solve. tests/test_vmec2000_exec_threed1.py
keeps the executable trace parser covered with a bundled threed1 fixture
when xvmec2000 is absent from CI.
tests/test_wout_profiles_currents_bundled_parity.py is a second required
no-solve wout-field gate. It checks converged released equilibria directly:
phipf and phi follow the input flux profile and VMEC half-mesh
integration, finite-beta pres/presf follow the VMEC radial stencil,
iotaf follows the iotas full-to-half mesh convention, and the stored
surface-averaged current profiles jcuru/jcurv match the VMEC finite
difference of bvco/buco divided by mu0. The covered fixtures include
axisymmetric finite-beta, non-axisymmetric current-driven, 3D finite-beta, and
lasym=True solved wouts.
tests/test_converged_wout_matrix_parity.py keeps a CI-safe converged-wout
matrix over released VMEC2000 outputs. The representative fixtures cover
fixed-boundary and free-boundary outputs, axisymmetric and non-axisymmetric
geometry, lasym=False and lasym=True channels, and single-grid plus
multigrid input decks. The gate checks metadata consistency against the
input.* files, final residual RSS limits, flux and iota mesh conventions,
finite stored geometry/field blocks, and the presence or absence of asymmetric
Fourier channels. It also asserts finite-pressure cases have nonzero stored
pressure, positive pressure energy, and positive beta scalars, including the
fetched single-grid lasym=True finite-beta reference.
The full test tier runs vmec_jax end-to-end. Promoted strict-parity cases
compare the standard wout field set against VMEC2000 references, while
known-drift or convergence-only cases use explicitly documented finite-output
and physics-gate checks instead of silent promotion. Run with:
RUN_FULL=1 pytest tests/test_wout_comprehensive_parity.py -v
The promoted strict-parity cases pass with the following tolerances per field category:
Field category |
rtol |
atol |
|---|---|---|
Geometry Fourier coefficients (rmnc, zmns, lmns, gmnc) |
1×10⁻⁶ |
1×10⁻⁷ |
Magnetic-field Fourier coefficients (bmnc, bsup*, bsub*) |
5×10⁻⁵ |
1×10⁻⁷ |
1-D profiles (phi, iotas, iotaf, pres, vp, phipf, chipf) |
1×10⁻⁶ |
1×10⁻⁷ |
Scalar energy/shape (wb, wp, volume_p) |
1×10⁻⁶ |
1×10⁻⁷ |
Current/field diagnostics (bvco, bdotb, bdotgradv) |
5×10⁻⁵ |
1×10⁻⁷ |
Near-zero or cancellation-limited (buco, jcuru, jcurv, jdotb) |
1×10⁻² |
1×10⁻⁸ |
MHD stability coefficients (DMerc, D_R, Dshear, Dwell, Dcurr, Dgeod) |
1×10⁻³ |
1×10⁻⁸ |
Equilibrium force residual (equif) |
1×10⁻³ |
1×10⁻⁸ |
Convergence is also verified (fsqr, fsqz, fsql < 10⁻¹⁰) on every
case before the field comparisons.
Convergence-only tests¶
For input files without a VMEC2000 reference wout, the test suite still
verifies that vmec_jax converges and produces finite, physically consistent
wout fields. The convergence-only cases extend coverage to:
Stellarator-asymmetric (lasym=True) fixed-boundary:
basic_non_stellsym_pressure,LandremanSenguptaPlunk_section5p3_low_res,up_down_asymmetric_tokamak.basic_non_stellsym_simsopt.Free-boundary:
cth_like_free_bdy(requires mgrid fromfetch_assets.py).
These cases are exercised by:
RUN_FULL=1 pytest tests/test_wout_comprehensive_parity.py -v -k "convergence_only"
QI diagnostics and case coverage¶
The required QI tests currently validate the diagnostic definitions and metadata contracts rather than claiming global optimizer robustness from every possible seed. The fast local QI gate is:
pytest -q tests/test_quasi_isodynamic.py tests/test_qi_legacy.py tests/test_qi_diagnostics.py tests/test_qi_objective_component_report.py tests/test_qi_seed_suitability_audit.py tests/test_booz_input.py
This gate covers smooth Boozer-space QI residuals, the legacy branch/shuffle diagnostic used for ranking, mirror-ratio and elongation records, Boozer input handling, including stellarator-asymmetric geometry and magnetic channels, and synthetic ranking consistency. It is intentionally cheap enough for ordinary development.
The current QI NFP 1-4 coverage panel and CSV document reviewed NFP=1, NFP=2,
seed-3127, and minimal-seed NFP=4 lanes under their case-specific aspect
targets:
docs/_static/figures/readme_qi_optimization_cases.png and
docs/_static/figures/readme_qi_optimization_cases.csv. These artifacts are
case-gated coverage rows, not a global seed-robustness claim. The plotted
initial panels are raw/source input WOUTs that match the paired input decks;
the plotted final panels are the accepted audited WOUTs. The NFP=4 row is a
case-gated minimal-seed lane that uses a same-NFP reference-family proposal
plus an exact audit, not a long local descent; its CSV row records one
preconditioner point and one short history segment along with the exact
qi_seed_gate_passed/qi_engineering_gate_passed/qi_gate_failures
fields. It is separate from the common-minimal showcase, whose current
checked-in summary does not contain non-stale QI completions. The separate
nfp4_qi_finite_beta and nfp4_qh_warm_to_qi cases remain stress lanes
rather than broad arbitrary-seed NFP=4 robustness claims. These rows are not
additional aspect-5 README best-row promotions.
May 2026 bounded NFP=4 reruns that reproduce this metric envelope should be
recorded as QA/provenance checks unless they provide a new reviewed
docs/_static/qi_readme_cases/nfp4_minimal replacement bundle. Do not
regenerate the README/docs NFP=4 panel from scratch solely from a scratch
result directory; keep the row documented as case-gated and
reference-preconditioned.
examples/optimization/QI_optimization.py
is the editable entry point for extending this to other inputs: change the
top-level INPUT_FILE and OUTPUT_DIR for a new minimal/circular-like VMEC
deck. For archived rendered case lanes or stress tests, use
VMEC_JAX_QI_RUN_CASE with examples/optimization/qi_optimization_cases.py
(nfp1_qi through nfp4_qi are public minimal-seed aliases; named far
seeds such as qi_stel_seed_3127 are explicit diagnostics). Use
nfp4_qi_finite_beta or nfp4_qh_warm_to_qi only as NFP=4 stress lanes
until their independent diagnostics pass the QI, mirror, engineering, and
multi-seed gates.
The current
qi_stel_seed_3127 far-seed lane first runs a deterministic same-NFP
reference-family boundary preconditioner, records the selected candidate as an
accepted baseline when the independent gates pass, and then runs guarded
QI/iota cleanup. Review
boundary_reference_preconditioner/summary.json to see which interpolation
point was selected, and review mirror_ramp_promotion_log.json because
failed cleanup stages are not silently promoted. The older ESS-scaled basin
prefilter remains available as an opt-in diagnostic and writes
basin_prefilter/top_candidates.json when enabled. Far-seed stages may use
lower Boozer/QI resolution during the optimization and a higher-resolution
final audit; both resolutions are written to diagnostics.json so promotion
claims can be traced. Far seeds may use a solved same-NFP QI wout through
boozer_target_wout/boozer_target_weight as an opt-in homotopy
experiment, but that term is not a final acceptance diagnostic. A seed-robust
QI claim still requires the constrained objective to be run and visually
audited from QI, QP, QH, QA, and simple non-omnigenous starting boundaries.
Accept a row only when the final state satisfies all of: low legacy QI
diagnostic, closed-looking Boozer |B| contours, mirror ratio at target,
acceptable elongation, abs(mean_iota) >= 0.41, and aspect ratio near the
configured target.
Before launching expensive optimization sweeps, rank available solved seeds with:
PYTHONPATH=. python examples/optimization/audit_qi_seed_suitability.py --quick --csv results/qi_seed_audit.csv
PYTHONPATH=. python examples/optimization/audit_qi_seed_suitability.py --quick \
--case qi_stel_seed_3127:qi:examples/data/input.QI_stel_seed_3127:examples/data/wout_QI_stel_seed_3127.nc \
--output results/qi_seed_audit/qi_stel_seed_3127.json \
--csv results/qi_seed_audit/qi_stel_seed_3127.csv
The audit performs no optimization. It reads solved input/wout pairs
and reports smooth QI, legacy QI, mirror ratio, elongation, aspect ratio, and
mean iota. Mirror ratio is evaluated over all selected Boozer surfaces by
default. Optional reference cases from omnigenity_optimization are used
when OMNIGENITY_OPTIMIZATION_ROOT points to that checkout; missing optional
cases are recorded as skipped rather than failing the audit.
The bundled default set includes input.QI_stel_seed_3127 when its matching
wout_QI_stel_seed_3127.nc fixture is present. On 2026-05-12 this seed
audited as a useful but not already-accepted QI robustness start:
smooth/legacy QI were about 5.0e-2/5.0e-2 before optimization, with
mirror ratio far above the default target and aspect, iota, and elongation
still requiring optimization.
The smooth QI diagnostic includes normalized bounce endpoints by default so the
ranked smooth metric samples the same level range as the legacy Goodman-style
branch-shuffle diagnostic; pass --no-include-bounce-endpoints only for
interior-level ablation studies.
Rows are ranked by the combined smooth-plus-legacy QI score, while engineering
constraint failures are reported separately so a QI-like seed with a fixable
mirror/aspect violation is not hidden behind a non-QI seed that merely satisfies
the engineering constraints.
Far-seed basin survey¶
For inputs that are not already close to the desired QI basin, a purely local least-squares run can spend its budget improving the wrong basin. The intended workflow is therefore global-local: first use a bounded basin survey to try larger ESS-scaled boundary perturbations, then promote the best candidates into the differentiable local QI optimizer. This is closer to basin-hopping than to a replacement optimizer: random/axis-aligned jumps survey basins, while the accepted candidates still rely on VMEC/JAX diagnostics and exact local derivatives for refinement.
Plan a deterministic survey without running VMEC:
PYTHONPATH=. python tools/diagnostics/qi_basin_survey.py \
--input examples/data/input.QI_stel_seed_3127 \
--output-dir results/diagnostics/qi_basin_survey
Run the bounded survey after reviewing the plan:
PYTHONPATH=. python tools/diagnostics/qi_basin_survey.py \
--input examples/data/input.QI_stel_seed_3127 \
--output-dir results/diagnostics/qi_basin_survey \
--execute --save-candidate-inputs
The survey writes plan.json, candidates.json, top_candidates.json,
and candidates.csv. Candidates are ranked by smooth QI, legacy QI, mirror
ratio, elongation, mean-iota floor, and aspect-ratio proximity. The top
input.candidate files are not final equilibria; they are starting points for
short local constrained QI optimizations. Bayesian optimization and
population/evolutionary optimizers are useful future lanes, but they are not the
first production default here because a mode-3 QI boundary has dozens of active
DOFs and each accepted evaluation is expensive. A cheap deterministic basin
survey gives most of the immediate benefit while preserving the differentiable
local-refinement path.
Promote the top surveyed candidates through bounded differentiable local refinements:
PYTHONPATH=. python tools/diagnostics/qi_basin_promote.py \
--candidates results/diagnostics/qi_basin_survey/top_candidates.json \
--out-root results/diagnostics/qi_basin_promotion
After reviewing promotion_plan.json, run:
PYTHONPATH=. python tools/diagnostics/qi_basin_promote.py \
--candidates results/diagnostics/qi_basin_survey/top_candidates.json \
--out-root results/diagnostics/qi_basin_promotion \
--execute
The promotion matrix tries direct mode-3 refinement, repeated mode continuation, QI-then-augmented-Lagrangian cleanup, and a soft-wall mirror/elongation cleanup. A promoted row must pass the independent QI+iota gate and the engineering gate; otherwise it remains diagnostic evidence about the local basin.
When penalty-based promotion jumps between incompatible basins, use the filter-search diagnostic. It accepts a trial only if the already-satisfied gates are preserved while the active failed gate improves:
PYTHONPATH=. python tools/diagnostics/qi_filter_search.py \
--input results/diagnostics/qi_basin_survey/top_candidate/input.candidate \
--output-dir results/diagnostics/qi_filter_search
After reviewing plan.json, add --execute. The search phase order is
QI, then iota, then mirror/elongation. The output history is checkpointed
after every evaluated trial, and --max-trials-per-iteration can bound an
interactive batch. This makes it clear whether the seed has a nearby feasible
path or whether a broader global/basin method is required, even if a long
diagnostic run is interrupted.
To audit a new input deck, first run VMEC once so the audit has a matching
wout file:
vmec /path/to/input.my_seed
PYTHONPATH=. python examples/optimization/audit_qi_seed_suitability.py \
--quick \
--case my_seed:qi:/path/to/input.my_seed:/path/to/wout_my_seed.nc \
--output results/qi_seed_audit/my_seed_summary.json \
--csv results/qi_seed_audit/my_seed_summary.csv
The --case format is label:family:input_path:wout_path. The family is
one of qi, qp, qh, qa, or simple and is used only for
ranking/reporting. This is the correct first step for arbitrary inputs such as
examples/data/input.QI_stel_seed_3127: audit the solved seed, inspect the
reported QI and engineering metrics, then launch a bounded QI optimization only
if the seed is plausible.
To turn the audit into a bounded seed-robustness worklist without launching a full sweep, add a dry-run prefine manifest:
PYTHONPATH=. python examples/optimization/audit_qi_seed_suitability.py --quick --prefine-probes plan --prefine-manifest results/qi_seed_audit/prefine_manifest.json
The manifest records top-ranked seeds plus one best-ranked representative from
each available seed family, hard-capped QI-only prefine settings, expected
output files, and exact commands for running one tiny probe at a time. The
default probe is now a bounded repeated-stage continuation,
--prefine-stage-modes 1,1,2,2,3, with explicit per-stage and total
nfev caps recorded in the manifest. Each plan also records the selected
phimin value, its source, the endpoint mode, and the QI options used by the
probe.
Use --prefine-probes run --prefine-reviewed only after reviewing the
manifest and deliberately executing those capped probes. Unless explicitly
overridden, prefine probes inherit the audit endpoint setting and record the
alignment in the manifest; by default this is
endpoint_mode=include_bounce_endpoints. Passing
--no-prefine-include-bounce-endpoints is an explicit interior-level
ablation, not the seed-robustness default.
The prefine manifest summary is deterministic and JSON-only: it reports status
counts, completed stage modes, best finite candidate by final objective, best
objective improvement, failed and timed-out probes, objective-history
regressions when compact histories are present, automatic acceptance status,
and one recommended next probe action. These summaries are audit aids, not a
substitute for final QI physics and plot review.
For tiny smoke probes, automatic acceptance does not require artificial
movement if the seed is already stable and has low objective: a completed,
monotone, finite probe with final QI objective at or below 5e-2 is marked
accepted_stable_low_objective rather than rejected for having no measurable
two-evaluation improvement.
Prefine manifests also record ESS controls. Use --no-prefine-use-ess and
--prefine-ess-alpha VALUE for bounded ablations when a seed fails at a
higher continuation mode; the selected settings are written into both the
manifest and generated run command.
Mode-continuation stages are stateful: every repeated or higher-mode stage is
rebuilt from the previous stage’s optimized VMEC input and starts with a zero
increment vector. This is part of the validation contract because lower-mode
QI probes can intentionally project high-order seed modes out; later stages
must not silently reintroduce those original high modes from the deck.
Exact optimizer histories are also filtered through an exact-replay acceptance
guard. Trial residuals may use a cheaper VMEC solve for memory/runtime
reasons, but final outputs and accepted objective histories use the best exact
accepted-point residual seen by the Jacobian path. Any trial-accepted point
that replays worse is counted in rejected_trial_exact_history_count rather
than plotted as a monotone accepted step. Non-finite exact residuals are
discarded before they can become the selected final point.
If SciPy later aborts on a non-finite trust-region linear algebra step after a
finite exact point has already been accepted, the optimizer returns that best
exact point with success=False and records optimizer_exception in
history.json. This preserves scientifically useful QI-prefine artifacts
without hiding that the optimizer terminated abnormally.
By default the audit uses --phimin-policy well-phase: each seed is scored at
both phimin=0 and phimin=pi/nfp and the better QI well phase is used for
ranking and prefine planning. Use --phimin-policy fixed --phimin VALUE when
you need a strict single-phase comparison against a legacy run.
For the current optional validation plan, including CI verification commands, family-representative QI probe workflow, VMEC2000/SIMSOPT optional lanes, and deferred parity gates, see Optional validation plan.
Validated wout fields¶
Every run produces a NetCDF3-classic wout_*.nc compatible with VMEC2000
tools. All of the following fields are written and tested:
Geometry Fourier:
rmnc,zmns,lmns(andrmns,zmnc,lmncfor lasym).Nyquist Fourier:
gmnc,bmnc,bsupumnc,bsupvmnc,bsubumnc,bsubvmnc,bsubsmns(andgmns,bmns,bsupumns,bsupvmns,bsubumns,bsubvmnsfor lasym).1-D profiles:
phi,phipf,phips,chipf,iotas,iotaf,pres,presf,vp.Scalar diagnostics:
wb,wp,volume_p,ctor,signgs,ns,nfp,mpol,ntor,lasym,gamma.Current/field diagnostics:
buco,bvco,jcuru,jcurv,jdotb,bdotb,bdotgradv,equif.Axis geometry:
raxis_cc,zaxis_cs(andraxis_cs,zaxis_ccfor lasym).MHD stability coefficients:
DMerc,D_R,HGlasser,GlasserCorrection,GlasserShearValid,DShear,DWell,DCurr,DGeod.Convergence scalars:
fsqr,fsqz,fsql.
Current parity status¶
- Fixed boundary
Strict field-by-field parity is established for the promoted comprehensive cases. Other shipped or fetched references are covered by no-solve profile, current, b-field, converged-wout matrix, or convergence-only gates until they are promoted.
- Stellarator-asymmetric (lasym=True)
lasym=Truechannels are covered by bundled/fetched reference physics gates and convergence tests. Thebasic_non_stellsym_pressureexecutable-backed finite-beta comparison passes the optional nightly converged-WOUT matrix after reconstructing the asymmetricbsubvmnschannel from VMEC’s corrected half-mesh IEQUI source. Strict external LASYM parity is still not promoted broadly: the axisymmetric zero-pressureup_down_asymmetric_tokamaknightly comparison remains a known residual gap, led bylmnsand the near-zerobsubvmnssine covariant channel. The Boozer input adapter is required to preserve the asymmetric geometry/lambda channels (rmns,zmnc,lmnc) and magnetic sine channels throughbooz_xform_jaxfor QI and LASYM Boozer diagnostics.- Free boundary
vmec_jax produces converged free-boundary equilibria for the bundled CTH-like and D3D cases. Quantitative parity requires
fetch_assets.pyfor the mgrid files. The free-boundary coil-optimization validation page records the current high-resolution finite-beta WOUT-panel evidence: DIII-D VMEC2000-compatiblemgridrows through actual beta 3.33% atns=101and a strict LP-QA direct-coil stellarator forward lane through actual beta 1.93%. The same page records the current phase-2 adjoint evidence: accepted-trace replay gates for current-only, Fourier-only, and mixed coil-control perturbations, accepted-statebsqvacderivatives with respect to the VMEC state, and JAX-visible masked nonlinear-controller AD-vs-FD checks. These are validation primitives for the full-loop refactor, not a promoted productionrun_free_boundaryexact-adjoint claim. See Free-Boundary Coil Optimization for the artifact links, reproduction commands, and phase-2 adjoint limitations.- Near-zero diagnostics
Quantities like
jdotband Mercier coefficients involve finite-difference postprocessing where relative error can be inflated near zero even when both codes agree in absolute terms. See JXBFORCE / Mercier Diagnostics (jdotb, DMerc, D_R) for details.
Per-iteration trace parity¶
For the highest-fidelity parity (matching VMEC2000 iteration-by-iteration), use the executable comparator tools:
python tools/diagnostics/vmec2000_exec_stage_trace_compare.py \
--case circular_tokamak --max-iter 10 --single-ns 13
python tools/diagnostics/parity_sweep_manifest.py --tier smoke
python tools/diagnostics/wout_compare_axis_mask.py \
--a /path/to/vmec2000/wout_case.nc \
--b /path/to/vmec_jax/wout_case.nc \
--rtol 1e-4 --atol 1e-12
Regenerate converged-wout benchmark summaries with:
python tools/diagnostics/converged_wout_parity_benchmark.py --all-discovered-execs
python tools/diagnostics/converged_wout_parity_benchmark.py --nightly --all-discovered-execs
python tools/diagnostics/converged_wout_parity_benchmark.py --dry-run --scan-local-execs --all-discovered-execs
The first command runs the bounded circular end-state comparison. The nightly
variant adds the slower representative non-axisymmetric, lasym=True,
multigrid, and free-boundary cases. The runner discovers
$VMEC2000_EXEC, ~/bin/xvmec2000, xvmec2000 on PATH, and the
standard adjacent STELLOPT build path by default, then de-duplicates symlinks
before running. Use --scan-local-execs when you also want to recursively
inventory older local benchmark-tree executables before deciding which ones are
safe to run.
Manifest-driven sweep (fixed + free boundary)¶
The canonical parity matrix is defined in tools/diagnostics/parity_manifest.toml:
python tools/diagnostics/parity_sweep_manifest.py --tier smoke
python tools/diagnostics/parity_sweep_manifest.py --tier full
The manifest covers: fixed-boundary axisymmetric and non-axisymmetric,
lasym=False and lasym=True, free-boundary axisymmetric and
non-axisymmetric. Required CI only smoke-tests the manifest schema and bounded
dry-run wiring; executing the matrix against VMEC2000 remains an optional local
or scheduled lane.
Latest local executable rerun¶
The 2026-05-25 local rerun under outputs/rerun_20260525_123334 used
/Users/rogeriojorge/local/STELLOPT/VMEC2000/Release/xvmec2000 and passed
all selected stage-trace comparisons:
Matrix |
Cases |
Failed |
Representative coverage |
|---|---|---|---|
|
|
|
circular tokamak, ITERModel, up/down asymmetric tokamak,
Landreman-Paul QA low resolution, |
|
|
|
|
These are bounded stage-trace checks, not a replacement for the optional converged-WOUT nightly matrix. They are useful release-candidate evidence that the latest dirty performance/refactor work has not broken the short VMEC2000 trace path.
Optional VMEC2000 executable checks¶
The default required test suite does not need a local VMEC2000 build. CI
fetches released wout references for parity rows; local runs without the
bundle skip those optional fixture tests cleanly:
pytest -q -m "not full and not vmec2000"
RUN_FULL=1 pytest tests/test_wout_comprehensive_parity.py -v
Direct executable comparisons are opt-in because they require a VMEC2000
Fortran executable, and some checks also require mpi4py and the VMEC2000
Python extension. Prefer the bounded commands below before broadening to the
full marker suite.
The fastest executable-backed stage-trace validation is:
VMEC2000_EXEC=/path/to/xvmec2000 \
VMEC2000_INTEGRATION=1 \
pytest -q tests/test_vmec2000_exec_fast_validation.py::test_fast_vmec2000_stage_trace_validation_cases
This command uses bundled fixed-boundary inputs, a single ns=13 grid,
max_iter=2, lite dump output, and a 60 second VMEC2000 timeout per case.
The same optional file includes a stock-executable free-boundary
LASYM=true smoke that caps the bundled synthetic CTH-like deck at 120
iterations and verifies VMEC2000 reaches the vacuum solve without an
I_TOR mismatch:
VMEC2000_EXEC=/path/to/xvmec2000 \
VMEC2000_INTEGRATION=1 \
pytest -q tests/test_vmec2000_exec_fast_validation.py::test_vmec2000_free_boundary_lasym_true_reaches_vacuum_solve
For a short CLI comparison against the executable:
VMEC2000_EXEC=/path/to/xvmec2000 \
VMEC2000_INTEGRATION=1 \
VMEC2000_CLI_NITER=5 \
pytest -q tests/test_cli_vmec2000_exec.py
This caps both VMEC2000 and vmec_jax CLI runs at five iterations. To run
the whole executable-backed suite after the bounded checks are green:
VMEC2000_EXEC=/path/to/xvmec2000 \
VMEC2000_INTEGRATION=1 \
pytest -q -m vmec2000
Converged end-state VMEC2000-vs-vmec_jax comparisons are in
tests/test_vmec2000_converged_parity.py. By default this runs only the
bounded fixed-boundary circular case when VMEC2000_INTEGRATION=1 is set.
Set VMEC2000_NIGHTLY=1 as well to include the slower non-axisymmetric,
lasym=True, multigrid, and free-boundary representatives:
VMEC2000_EXEC=/path/to/xvmec2000 \
VMEC2000_INTEGRATION=1 \
VMEC2000_NIGHTLY=1 \
pytest -q tests/test_vmec2000_converged_parity.py
On 2026-05-29 this nightly command passed locally with
~/bin/xvmec2000: 4 passed, 1 skipped, 1 xfailed in 15:06. The
skipped row is the intentionally deferred converged free-boundary WOUT parity
case, and the xfail is the documented zero-pressure axisymmetric LASYM gap.
The fetched single-grid lasym=True finite-beta fixture is a required
bundled-reference physics gate, and the executable-backed
basic_non_stellsym_pressure converged-WOUT row passes locally against
~/bin/xvmec2000 after the asymmetric bsubvmns output channel was switched
to VMEC’s corrected half-mesh IEQUI source. The separate zero-pressure,
axisymmetric up_down_asymmetric_tokamak strict external LASYM gap remains
non-promoted: a 2026-05-19 rerun showed lmns=1.78e-2 relRMS,
bsupumns=1.05e-2 relRMS, and bsubvmns diff_rms=5.72e-4 against a
near-zero ref_rms=4.10e-5. The reference_state_roundtrip_rel_rms split
from the converged-wout benchmark keeps the remaining lambda work focused on
the m=1,3,4 LASYM channels and the near-zero bsubvmns comparison on
absolute error. The free-boundary converged-WOUT row is skipped until it is
reduced to a bounded nightly gate; use the promoted stage-trace free-boundary
smoke for routine executable parity.
Optional SIMSOPT formula parity is similarly guarded and targeted:
RUN_SIMSOPT_VALIDATION=1 \
pytest -q tests/test_simsopt_optional_validation.py::test_qh_quasisymmetry_residual_matches_simsopt_wout_formula
The machine-readable list of these bounded parity commands is emitted by:
python validation/qi_seed_robustness_plan.py --output results/qi_seed_audit/validation_plan.json
The emitted plan intentionally does not embed a stale green CI run by default.
Verify the current main CI run with gh run list/gh run view and pass
the run metadata to the helper when preparing release-validation artifacts.
Skip behavior is intentional. Tests marked vmec2000 skip unless
VMEC2000_INTEGRATION=1 is set. They also skip, rather than fail, when the
VMEC2000 executable, VMEC2000 Python extension, mpi4py, netCDF4, an
input deck, or a VMEC2000-produced wout is unavailable. Required PR CI
therefore excludes vmec2000 tests; optional scheduled/manual CI can enable
them after installing VMEC2000 and exporting VMEC2000_EXEC.
VMECPlot2 compatibility¶
vmec_jax writes NetCDF3-classic wout_*.nc files compatible with
vmecPlot2.py. Any workflow that reads VMEC2000 output can consume
vmec_jax output without modification.
The showcase scripts generate side-by-side comparison figures using the same VMECPlot2-style grids (theta/zeta resolution, toroidal angle conventions):
python examples/showcase_axisym_input_to_wout.py --suite