Solve mirror and hybrid equilibria¶
vmex.mirror solves open-mirror equilibria (fixed and free boundary) and
closed stellarator-mirror hybrids in a spline-native basis. This page is the
run recipes; the theory, gate evidence, and lane status are in
Mirror geometry, and the mout_*.nc output format is
MOUT file reference.
Solve a fixed-boundary mirror from one radius¶
The one-call entry point solves an axisymmetric open mirror from a single LCFS radius, picking a default boundary and profiles for the requested resolution:
from vmex.mirror import MirrorConfig, MirrorResolution, solve_fixed_boundary_from_radius
config = MirrorConfig(resolution=MirrorResolution(ns=7, mpol=4, nxi=17))
result = solve_fixed_boundary_from_radius(0.3, config)
MirrorResolution takes ns (radial surfaces), mpol (largest represented
poloidal Fourier mode; the collocation size is the read-only
ntheta = 2*mpol + 1), and nxi (axial nodes); the returned
SplineMirrorSolveResult carries the converged coefficients and the
variational, weak, and pointwise-force residuals.
For a shaped boundary, build SplineMirrorBoundary / SplineMirrorState /
SplineMirrorDiscretization directly and call
vmex.mirror.solve_fixed_boundary().
Run the shipped examples¶
Four runnable examples ship with the package and need no command-line arguments (each has editable inputs at its top):
python examples/mirror/mirror_fixed_boundary_nonaxisymmetric.py # rotating-ellipse fixed boundary
python examples/mirror/mirror_free_boundary_beta_scan.py # axisymmetric free-boundary beta scan
python examples/mirror/stellarator_mirror_hybrid.py # periodic B-spline racetrack hybrid
python examples/mirror/qi_mirror_hybrid_fourier_vs_bspline.py # QI-mirror hybrid: Fourier vs B-spline
The first checks every convergence gate for the rotating ellipse and the
axisymmetric mirror, differentiates rotating-ellipse volume against two fully
reconverged solves, and writes MOUT plus 3-D, cross-section, |B|, residual,
symmetry, and analytic-direction figures. The figures that the README and
these pages embed (the paired 3-D view, the beta-scan composite, and the
hybrid panel) are written by these scripts straight into
docs/_static/figures/ as lossless WebP, so re-running a script reproduces
the committed figure; docs/_static/figures/figures.json records each one.
Plot the results¶
Open-mirror solves write mirror-native mout_*.nc files, which plot with
vmex --plot mout_example.nc
rendering horizontal 3D, coil, cap-to-cap field-line, |B|, pressure,
cross-section, and residual figures.
Run a free-boundary beta scan¶
python examples/mirror/mirror_free_boundary_beta_scan.py
The script solves every beta point from 0 through 50% and writes one MOUT per
state, a compact JSON summary, restart files, and per-state figures under
results/mirror_free_boundary_beta_scan/, and the beta-scan composite under
docs/_static/figures/. The example’s two ESSOS loops are
sized to the plasma: radius 0.5 m at z = +/-1.0 m carrying 3.72e5 A each,
reproducing the central vacuum field B(0) = 0.0836 T of the recorded
benchmark geometry with vacuum mirror ratio 4.58. The axisymmetric
free-boundary lane is supported through 10% requested beta; 25% and 50% are
extended validation (Capability contract).
External fields enter as an ESSOS/SIMSOPT Biot-Savart object, any
vectorized xyz -> B callable, or a shared
MgridField; coil geometry stays in ESSOS. Field
callables that capture committed arrays should use jax.tree_util.Partial
(or another registered pytree) so VMEX can relocate the captured leaves; an
ordinary Python closure is opaque and pins its arrays’ placement.
Resume an interrupted scan¶
Set SAVE_RESTARTS = True in the example to write one compressed .npz
hot-start per beta point
(vmex.mirror.output.save_free_boundary_restart()). To resume, set
RESTART_FROM and trim BETAS to the unfinished suffix;
vmex.mirror.output.load_free_boundary_restart() checks the schema and
coefficient shapes before returning the boundary, plasma state, and
calibrated mass scale. The original beta-zero boundary remains the
pressure-profile reference.
Build a hybrid¶
from vmex.mirror import build_stellarator_mirror_hybrid, solve_fixed_boundary
setup = build_stellarator_mirror_hybrid(axis_coefficient_count=16)
result = solve_fixed_boundary(setup.discretization, setup.state)
build_stellarator_mirror_hybrid constructs the periodic B-spline racetrack
(two exactly straight mirror legs, two stellarator returns); pass
axis_coefficient_count to freeze the leg-return junction while refining the
solve basis — the contract under which the circular-section lane converges
monotonically (Mirror geometry). build_qi_mirror_hybrid
splices straight legs into a QI stellarator axis instead;
examples/mirror/qi_mirror_hybrid_fourier_vs_bspline.py runs that construction end
to end.
Pick the device¶
Mirror fixed/free-boundary solves and beta scans expose the same device=
contract as the toroidal core. On the office host, the corrected 15x15 case
took 35.2 s on CPU and 44.2 s on one RTX A4000, so the mirror-specific
device="auto" policy selects CPU for its SciPy-controlled JAX callbacks.
Explicit device="cpu"/"gpu" always wins and device=None follows
ordinary JAX placement; no environment variable is required.
Differentiate a mirror equilibrium¶
spline_fixed_boundary_adjoint (scalar diagnostics, reverse) and
spline_fixed_boundary_tangent (forward) differentiate through the converged
coefficient residual; free_boundary_adjoint covers the axisymmetric
free-boundary lane through the 10% beta ceiling (validated to 1.1e-10
relative against reconverged finite differences). Scope and validation
evidence: Mirror geometry.