Scientific diagrams
vaft.diagram draws concepts: topology, coordinate systems, workflows. It does not draw data. The
three layers split the work:
| Layer | Owns | Example |
|---|---|---|
vaft.formula |
the physics: every equation a diagram depends on | helical_phase, island_pendulum_hamiltonian |
vaft.diagram |
explanatory geometry: sampling, projection, camera, labels | magnetic_island(projection="3d") |
vaft.plot |
data and numerical results from an ODS | equilibrium_2d_profiles |
A diagram never restates an equation. It calls the formula function, so the picture and the formula reference cannot drift apart.
This page explains the diagrams family by family. The complete list – every builder, every committed SVG and the exact call that draws it – is the generated diagram gallery.
Particle motion and computed animation
The particle-motion guide describes VAFT’s native Lorentz and guiding-center experiments, physical-time animation, stationary mirror wells and synchronized invariant diagnostics. Existing static references retain their default behavior; computed replay accepts an explicit trajectory and time coordinate.
Magnetic island
import vaft
d = vaft.diagram.magnetic_island(
m=3, n=2, width=0.16, phase=0.0, projection="poloidal",
r_s=0.55, elongation=1.7, triangularity=0.4, # D-shaped plasma; defaults give a circle
)
d.tikz # the LaTeX/TikZ source; needs no TeX installation
# With latex and dvisvgm installed:
# d -> displays inline in Jupyter (SVG)
# d.save("island.svg")
| Projection | Poloidal section at $\phi=0$ | Top view $(R,\phi)$ | 3-D view of the O and X helices |
|---|---|---|---|
All three views are drawn from one model, and the convention is the same in each:
| Quantity | Definition |
|---|---|
| helical phase | $\xi = m\theta^* - n\phi - \phi_0$, where $\theta^*$ is the straight-field-line (PEST) angle of each surface, running from the outboard midplane towards the top, and $\phi$ runs counter-clockwise seen from above |
| straight-field-line angle | $\theta^* = 2\pi\int \mathcal{J}/R^2\,d\theta / \oint \mathcal{J}/R^2\,d\theta$ (vaft.formula.straight_field_line_angle), taken from the surface geometry alone. O- and X-points are evenly spaced in $\theta^*$, so they spread out on the low-field side even on circular surfaces |
| flux function | $\mathcal{H}(x,\xi) = \tfrac12 x^2 - (w/4)^2\cos\xi$, with $x = r - r_s$ |
| O-points / X-points | $\xi = 0$ (minimum of $\mathcal{H}$) / $\xi = \pi$ (saddle): $m$ of each per poloidal section, alternating |
| separatrix | $\mathcal{H} = (w/4)^2$, so $x_\mathrm{sep} = \tfrac{w}{2}\lvert\cos(\xi/2)\rvert$ |
width |
the full radial width at the O-point, in units of the minor radius |
| geometry | Miller shaping from vaft.formula.miller_surface: $R = R_0 + r\cos(\theta + \arcsin\delta(r)\,\sin\theta)$ and $Z = \kappa r\sin\theta$, with $\delta(r) = \delta\,r$ so the surfaces stay nested and become circular towards the axis. Lengths are in units of the minor radius $a$ |
| caveats | The surfaces are prescribed, with no Shafranov shift, so $\theta^*$ is exact for these surfaces rather than for a Grad-Shafranov equilibrium. width is a width in the flux label $r$ and is a physical distance only on the outboard midplane |
The top view suppresses $Z$, so crossings of the projected O and X loci there are not reconnection points. The separatrix and $w$ are only visible in the poloidal section.
Animating the island: a phase scan or a rigid rotation
The same call draws the island over a trajectory of its phase when it is asked to with
animation=True (issues #1049, #1053). It returns the animation result that
plot_*(..., animation=True) returns. An array argument never animates by itself.
import numpy as np
import vaft
# A helical-phase scan: one frame per phi_0, the same model as the static call.
scan = vaft.diagram.magnetic_island(m=2, n=1, phase=np.linspace(0, 2 * np.pi, 120), animation=True, fps=30)
scan.save("island.mp4") # or .webm (both need vaft[video]) or .gif (needs nothing)
# A rigid rotation at f = 5 kHz: phi_0(t) = phi_0 + 2*pi*f*(t - t_0), from phase= at the first time.
rotation = vaft.diagram.magnetic_island(
m=2, n=1, time=np.linspace(0, 2e-3, 120), rotation_frequency=5e3, animation=True,
)
rotation.metadata["driver"] # name "time", unit "s", and every frame's time
- Exactly one trajectory. A trajectory is either
phaseas an array (driverphase[rad]) ortimewithrotation_frequency(drivertime[s]).- A phase array combined with
timeis refused, because it is ambiguous. - So is a single phase with
animation=True, which is a static diagram.
- A phase array combined with
- Units and direction.
rotation_frequencyis a frequency $f$ in Hz. The synthetic island of #886 (vaft.process.magnetic_island.MagneticIslandSpec) takes the angular frequency $\omega = 2\pi f$ in rad/s. A positive $f$ moves the O-points towards $+\theta^$ on a section. At fixed $\theta^$, it moves them towards $-\phi$, which is clockwise seen from above. - Physics, not pictures. Every state is the static diagram at that phase, with the same O/X topology, width and shaping. The rotation is the phase relation evaluated at each time; the drawing is never rotated as an image.
- Presentation only.
fpsordurationset only how fast the frames are shown, never the rotation rate. Each frame’s phase or time is kept inmetadataand in the.jsonsidecar ofsave(). - A preview of the canonical figure. The frames are a Matplotlib drawing of each state’s scene (the template’s colours and line styles, with labels through mathtext). The canonical static figure remains the SVG above.
Stability and operational-space diagrams
Textbook 2-D charts: two axes, the boundaries that divide the plane, and one label per region. A
boundary that physics defines is computed by vaft.formula. Only the peeling–ballooning boundary,
which has no closed form, is a schematic, and the figure says so.
vaft.diagram.peeling_ballooning()
vaft.diagram.s_alpha_ballooning(s_max=1.5, alpha_max=3.5)
vaft.diagram.hugill(elongation=1.0) # no size parameter: R, a, B cancel
vaft.diagram.hugill_st(elongation=1.8) # spherical-tokamak q_cyl (Sykes et al. 2000, MAST)
vaft.diagram.troyon(aspect_ratio=3.0, elongation=1.7) # Troyon stability reference ~2.76, experimental levels dashed
vaft.diagram.li_qa(reference="wesson_1989") # JET empirical l_i-q_psi space
vaft.diagram.li_qa(reference="cheng_1987") # theoretical MHD-stable l_i-q(a) domain
| Diagram | Question it answers | Axes | Boundaries |
|---|---|---|---|
| Peeling–ballooning | Which edge instability limits the pedestal? | $\alpha_\mathrm{max}$, $J_{B,\mathrm{max}}$ (arbitrary units) | Schematic. Two linear margins joined by a smooth maximum. The ★, where the peeling and ballooning limits meet (typical ELM onset), is computed where the two margins are equal |
| $s$–$\alpha$ | How does shear set the ballooning limit, and where is second stability? | $\alpha$, $s$ | The first and second stability boundaries come from s_alpha_marginal_alpha, which applies Newcomb’s criterion to the Connor–Hastie–Taylor equation. The dashed line is the $0.6\,s$ approximation of ballooning_stability_criterion. Not resolved below $s \approx 0.05$ |
| Hugill | Where is the density limit? | $\bar n_e R/B_T$, $1/q_\mathrm{cyl}$ | The registered greenwald_hugill line (slope $\pi/50\kappa_a$ in $1/q_\mathrm{cyl}$ against $\bar n_e R/B_T$) and murakami_hugill ($\bar n_e R/B_T = 1$). The registered low_q is on the equilibrium $q_\psi$, not $q_\mathrm{cyl}$, so it is not drawn; q_limit= adds a dashed reference $q_\mathrm{cyl}$ line |
| Hugill (ST) | Where is the density limit in a spherical tokamak? | $\bar n_e R/B_T$, $1/q^{ST}\mathrm{cyl}$ with $q^{ST}\mathrm{cyl} = 2.5a^2(1+\kappa^2)B_T/(RI_p)$ | vaft.diagram.hugill_st(elongation=1.8): the Hugill limit as Sykes et al. (2000, MAST) define it (sykes_2000_st_hugill) and the Greenwald limit on these axes (greenwald_hugill_st, derived); Murakami dashed as a historical conventional-tokamak reference. Not substitutable with the conventional hugill axes (#1602) |
| $l_i$–$q$ | Where do current-profile peaking and edge q allow stable operation? | Wesson: $q_\psi$, $l_i(3)$. Cheng: cylinder $q(a)$, $l_i$ | Two separate references, never mixed. Wesson 1989 Fig. 6: the JET empirical boundaries (kink and double tearing below, density-limit disruptions above), with the registered low_q closing $q_\psi = 2$. Cheng 1987 Fig. 4: the theoretical MHD-stable domain of a cylinder with $q(0) = 1.01$ (ideal kink below, resistive kinks above), plotted as $l_i$ rather than $l_i/2$ |
| Troyon | How much pressure has the current held, against which references? | $I_p/(aB_T)$, $\beta_T$ | The registered troyon stability reference, $\beta_N = 2.2\,\mu_0 10^6 \approx 2.76$, through $\beta_T = \beta_N I_p/(aB_T)$, with the experimental $\beta_N$ levels of the troyon projection dashed (DIII-D envelope and record, ST reference, NSTX record; #1691). None is a limit, so no side is shaded or called stable. beta_N_max= draws a what-if value and the note says so; q_limit= adds a dashed reference $q_\mathrm{cyl}$ cutoff |
These charts show the boundaries of an operating space. Each one reads its lines from
vaft.formula.boundaries through a canonical projection (#1425), and a population of measured or
modelled states goes on the same projection with
vaft.plot.operational_space.operational_space_population(table, "hugill"). Table columns are named
by quantity identity (murakami_parameter, inverse_cylindrical_q, normalized_beta, …) with
units in table.attrs["units"]. A boundary is drawn only when both plotted columns are exactly the
projection’s quantities in its units: a q95 column never carries a $q_\psi$ or $q_\mathrm{cyl}$
boundary. Projections: hugill, troyon, beta_n_li, q95_li, greenwald_fraction_power,
li_qa_wesson, li_qa_cheng, beta_n_li1 (Strait’s $\beta_N = 4\,l_i(1)$ envelope), and the #1691 beta-normalisation views volume_beta_in,
beta_n_normalizations (with a $y = x$ guide), aspect_ratio_beta_n, aspect_ratio_volume_beta_n;
list_projections() gives the full set. See
#944 and #636.
Dimensionless similarity spaces
The limit diagrams above ask whether a state crosses a boundary. A similarity space asks where a state sits relative to other machines and to reactor designs, and which extrapolation separates them. In $\nu_$–$\rho_$, $\rho_$ is the size / finite-gyroradius direction ($1/\rho_$ is roughly the number of ion gyroradii across the minor radius) and $\nu_$ the collisionality direction ($1/\nu_$ is roughly the number of trapped-ion bounce orbits before a collision). $\beta_N$–$\rho_$ splits MHD-normalised pressure from machine scale. $\Omega_{ci}\tau_{E,\mathrm{th}}$–$\rho_$ splits confinement time, counted in gyro-orbits, from size. All three come from the ARC physics-basis comparison (Hillesheim et al., J. Plasma Phys. 92 (2026) E69, Fig. 6). They are zero-dimensional and draw no boundary by default (Luce, Petty and Cordey, PPCF 50 (2008) 043001, explain why these coordinates separate transport mechanisms).
| Projection | Question it answers | Axes (exact quantity) |
|---|---|---|
rho_star_nu_star |
Is the extrapolation in size, in collisionality, or both? | rho_star_verdoolaege_2021, nu_star_verdoolaege_2021 |
rho_star_beta_n |
How much is pressure, how much is scale? | rho_star_verdoolaege_2021, normalized_beta |
rho_star_omega_ci_tau_e |
Size against confinement time in gyro-orbits | rho_star_verdoolaege_2021, omega_ci_tau_e_th |
The $\rho_$ and $\nu_$ axes follow the ITPA confinement-database convention, Verdoolaege et al., Nucl. Fusion 61 (2021) 076006, Eqs. (1a) and (1c), which Hillesheim et al. use. $n$ and $T$ are volume averages with $T_e = T_i$. The functions that evaluate them are rho_star_from_M_T_B_R_epsilon, nu_star_from_n_T_B_R_epsilon_kappa_I and omega_i_tau_E_from_B_tau_E_M in vaft.formula.equilibrium. VAFT has other $\nu_$ and $\rho_$ definitions (issue 353): Sauter’s local $\nu_$, pedestal $\nu^_e$, edge and separatrix collisionalities. Each is a different quantity, so it cannot be drawn on these axes. A missing input leaves the state unassessed: it is counted per group, in a warning and in the legend, and is never estimated. Each projection carries its meaning as metadata:
from vaft.diagram import _op_space
from vaft.plot.dimensionless_space import dimensionless_similarity
print(_op_space.get_projection("rho_star_nu_star").interpretation.describe())
fig, ax = dimensionless_similarity(table, "rho_star_nu_star", group="machine") # ARC V3A overlaid
ax.vaft_exclusions.summary()
The only built-in reference point is ARC V3A ($\beta_N = 1.8$, $\rho_* = 0.0017$, $\nu_* = 0.031$, $\Omega_{ci}\tau_{E,\mathrm{th}} = 4.1\times10^8$), the values Hillesheim et al. state in Sec. 4. Their SPARC, ITER and EU-DEMO points appear only as plotted markers, so they are not digitised. Pass such points as reference_table= with a source column. See #1624.
Reduced stability diagnostics
What each analytic or reduced criterion is, and what it proves, is on Reduced stability diagnostics (#1635).
vaft.diagram.stability_diagnostic_taxonomy()
vaft.diagram.interchange_criteria()
| Diagram | Concept |
|---|---|
stability_diagnostic_taxonomy |
VAFT’s criteria by physical problem (rows) and logical status (columns): exact definition, reduced model, empirical or semi-empirical boundary, heuristic, and solver-derived. It also lists what is still absent. The reduced columns are compared with the solvers, not used as a gate |
interchange_criteria |
suydam_criterion and mercier_criterion_circular on one schematic profile. The toroidal $p’(1 - q^2)$ stabilises outside $q = 1$, where Suydam’s criterion still fails |
Single-particle motion
Gyration and guiding-centre drifts, drawn in the island family’s style and 3-D camera. Every orbit
is integrated from the Lorentz force by vaft.formula.boris_orbit, and every drift arrow is the
drift formula of vaft.formula.particle. The tests require the integrated guiding centre to move at
the formula’s velocity, so the figure cannot show a drift that the orbit does not make.
vaft.diagram.exb_drift(mass_ratio=4.0)
vaft.diagram.curvature_drift()
vaft.diagram.magnetization_current()
vaft.diagram.toroidal_drift(aspect_ratio=2.2)
| Diagram | Shows | Arrows from |
|---|---|---|
| E×B drift | An ion and an electron at equal energy in crossed uniform fields. The orbits differ in size, but the drift is the same, so no current flows | exb_drift_velocity |
| Curvature and ∇B drift | An ion spirals along a field line of $B_0R_0/R\,\hat\phi$ and drifts along $+z$ | grad_b_drift_velocity + curvature_drift_velocity |
| Magnetization current | Gyro-currents cancel inside a region. At its edge the diamagnetic $\mathbf{J}_M = \nabla\times\mathbf{M}$ survives | the binned current of the integrated orbits |
| Toroidal drift | ∇B and curvature drifts separate charge. The resulting vertical $\mathbf{E}$ drives an outward $\mathbf{E}\times\mathbf{B}$, so a purely toroidal field cannot confine | the drift formulas at the drawn cross-section |
Every view shows the relevant equations in a box. They are read from the $$…$$ definition in each
formula’s docstring, so the figure shows exactly what the formula documents and implements. There is
no second copy of any equation.
Each has further projections of the same computed orbits, chosen with projection=:
| Diagram | Projections (default first) | What the extra views add |
|---|---|---|
exb_drift |
perpendicular, 3d |
Helices along $\mathbf{B}$ drifting sideways. The parallel velocity does not change the perpendicular motion, so both views show the same orbits |
curvature_drift |
3d, poloidal, top |
poloidal looks along the field line, where the gyration circle climbs at the drift velocity. top shows the curved line and the inward $\nabla B$ |
magnetization_current |
perpendicular, 3d |
Helical columns along $\mathbf{B}$ with the edge current looping around them |
toroidal_drift |
3d, poloidal, top |
poloidal is the textbook $(R, z)$ cross-section. top shows the circular field lines, with the vertical drifts pointing out of the page |
The units are normalised ($|q| = 1$, $m_e = 1$, fields of order one). The ion-to-electron mass ratio is reduced (4 by default) so that both orbits are visible; the figures state this.
Tearing physics
The ideas upstream of an island, one concept per diagram, so each can be used on its own in a page, notebook or slide. None takes an equilibrium, shot or solver output: the curves are schematic.
vaft.diagram.rational_surface(m=2, n=1)
vaft.diagram.delta_prime(sign="positive") # "positive", "zero" or "negative"
vaft.diagram.tearing_layer_matching()
| Diagram | Question | What is schematic |
|---|---|---|
rational_surface |
Where does a perturbation resonate with the field-line pitch, $q(r_s) = m/n$? | The monotonic $q(r)$; it is not an equilibrium profile |
delta_prime |
What does the tearing stability index measure? | The outer solutions: quadratics that vanish on the axis and at the edge. Their slopes at $r_s$ go through vaft.formula.delta_prime_from_outer_derivatives, and only the sign of $\Delta’$ is meaningful |
tearing_layer_matching |
Why are the ideal outer regions and the non-ideal inner layer solved separately? | The layer width and the layer solution, which only joins the outer solutions in value and slope |
The index drawn here is the definition, not a stability result: no outer equation is solved. Solver
values of $\Delta’$ (RDCON, STRIDE) belong to vaft.plot, and they are not the RDCON $D_R$ or the
Modified Rutherford terms. The object these lead to is magnetic_island.
3-D perturbation harmonics
How a linear 3-D perturbation is written as complex Fourier harmonics, and what the complex numbers mean. Each diagram is one concept and needs no equilibrium, shot or GPEC output.
vaft.diagram.normal_field_component()
vaft.diagram.complex_harmonic(amplitude=1.0, phase=1.05)
vaft.diagram.toroidal_harmonic_phase(n=1)
vaft.diagram.harmonic_real_space_projection(m=2, n=1, phase=1.05)
vaft.diagram.complex_field_superposition(case="screening") # "amplification", "phase_shift"
| Diagram | Concept |
|---|---|
normal_field_component |
$\delta B_n = \delta\mathbf B\cdot\hat{\mathbf n}$ is the part of a perturbation that crosses a magnetic surface. It is geometric only, with no code-specific normalisation |
complex_harmonic |
One harmonic is $\hat b = b_R + i\,b_I = A e^{i\alpha}$. $b_R$ and $b_I$ are the cosine and sine quadratures of one pattern, not two fields |
toroidal_harmonic_phase |
Moving the toroidal origin by $\Delta\phi$ turns $\hat b$ by $-n\Delta\phi$. $\lvert\hat b\rvert$ is invariant; $b_R$ and $b_I$ are not |
harmonic_real_space_projection |
The physical field is real, $\delta b = \mathrm{Re}[\hat b\,e^{i(m\theta - n\phi)}]$ (vaft.formula.helical_harmonic): stripes of slope $n/m$ on the unwrapped $(\phi, \theta)$ plane |
complex_field_superposition |
External and plasma-response fields add as complex numbers, so screening, amplification and phase shift all come from one vector sum |
The phase convention is helical_phase’s $\xi = m\theta - n\phi$, with both mode numbers positive and
the helicity in the minus sign. vaft.code.gpec stores each complex quantity as a real/imaginary pair
(i = 0 real, i = 1 imaginary) and rebuilds it as real + 1j * imag, deciding no convention. For
GPEC’s spectral outputs that pair is the $(b_R, b_I)$ of complex_harmonic, and it becomes a field only
through the real-space reconstruction. Two sources differ:
- the
*_funquantities (b_n_fun,xi_n_fun) are already real-space in $\theta$, and GPEC writes them as $(\mathrm{Re}, -h\,\mathrm{Im})$ with its helicity $h$; vaft.process.toroidal_mode_decompositionreturns the conjugate, $\hat b = 2\,\overline{C_n}$.
helical_harmonic’s Convention section states both. Amplitudes, phases and responses in these
figures are schematic.
Collision processes
A classification of the interactions in a fusion plasma, built from the concept-diagram primitives. Coulomb collisions between charged particles relax the distribution. Atomic processes change charge states and bound electrons. Nuclear reactions change nuclei.
vaft.diagram.collision_processes()
The diagram contains no numbers except the D–T alpha energy, which is vaft.formula.constants.E_ALPHA. Collision frequencies, the
Coulomb logarithm and collisionality regimes are left to formula-backed diagrams (#1111).
Geometric approximations
How slab, cylindrical and toroidal models relate, keeping geometry and ordering on separate axes. The
physics is in vaft.formula.geometry. Geometric approximations
explains each representation.
vaft.diagram.geometry_ordering_map()
vaft.diagram.field_line_geometry(geometry="toroidal") # "cylindrical", "slab"
vaft.diagram.mode_number_mapping(m=2, n=1)
vaft.diagram.mhd_mode_geometry_map()
| Diagram | Concept |
|---|---|
geometry_ordering_map |
Geometries are columns and orderings are bands. Each reduction arrow names what it keeps or drops |
field_line_geometry |
The same $q$ field line on a torus and on the cylinder straightened at $R_0$, and the tilt of the sheared-slab field lines growing with $x$ |
mode_number_mapping |
The cylinder’s $k_\parallel(r)$ crosses zero at $q(r_s) = m/n$. The local slab of local_slab_from_cylinder is its tangent there |
mhd_mode_geometry_map |
Pressure-driven, current-driven, resonant and $n = 0$ mode families in slab, cylinder and torus. Exact relabelling, limits, analogues and branches are drawn as four different arrows. The text is MHD mode representations across geometries |
Asymptotic orderings
The scale separations a reduced model assumes, evaluated rather than assumed. The kernels are in
vaft.formula.ordering, and Asymptotic orderings (#1627)
explains them. Which diagrams the scale-to-model chain needs, and which are still planned, is
Characteristic scales and orderings, “Diagram contracts”.
vaft.diagram.timescale_hierarchy()
vaft.diagram.spatial_scale_orderings()
vaft.diagram.ordering_contract_map()
vaft.diagram.adapter_contract_map()
| Diagram | Concept |
|---|---|
timescale_hierarchy |
Inverse gyrofrequencies, collision, Alfvén, sound-crossing, inverse ion transit and bounce frequencies, evolution, wall, pulse and resistive times of one illustrative state on one logarithmic axis, each from a formula kernel. Below it are the ordering ratios: $S$, $\tau_{evol}/\tau_A$, $\tau_{pulse}/\tau_R$, $d_i/a$. Drift precession (no kernel) and $\tau_E$ (no inventory) are left out |
spatial_scale_orderings |
Electron and ion Larmor radii, $\rho_s$, $d_i$, the banana width, an illustrative gradient length, $a$, $R_0$ and $qR_0$ of the same state on one logarithmic axis. Below it are $\rho_i/L$, $\Delta_b/L$, $d_i/a$, with brackets for the ratios that need a mode ($k_\perp\rho$, $k_\parallel/k_\perp$, $\omega/\Omega_c$) |
ordering_contract_map |
Which model needs which ordering, read from vaft.validation.orderings.CONTRACTS. Quantities are grouped by scale (foundational, global, profile, perturbation, inner layer, time history) and models by family (fluid, reduced MHD, kinetic, neoclassical, evolution). Each cell is a separate assumption with its own margin, and an empty cell is not ordered. |
adapter_contract_map |
Which ordering contracts each VAFT code-adapter mode consumes, and how each enters the calculation: governing (filled), RDCON’s outer solution and layer solution, DCON-kinetic’s fluid part and kinetic part, PENTRC’s closure, inherited by a reduced model that does not solve its parent theory (dashed). An empty cell is no registered contract, not physical independence (vaft.validation.model_contracts, #1823) |
Plasma-model guide
Figures for the plasma-model guide (#1739). Every node is
read from the formalism records: a node lists the adapter:mode ids of vaft.validation.model_contracts.ADAPTER_MODES
whose record matches it, and codes not in VAFT (vaft.diagram.EXTERNAL_CODES) are hollow and labelled so. Four edge
kinds are used: asymptotic reduction (solid), model coupling (double-headed), software dependency (dotted) and
surrogate ancestry (dashed). No direction is a fidelity axis.
vaft.diagram.plasma_model_reduction_map()
vaft.diagram.formalism_axes_map()
vaft.diagram.solver_placement_map()
vaft.diagram.kinetic_mhd_coupling()
vaft.diagram.reduced_model_ancestry()
| Diagram | Concept |
|---|---|
plasma_model_reduction_map |
One starting point (Vlasov / Fokker–Planck + Maxwell, no VAFT solver) and the reductions to the fluid, drift-kinetic, gyrokinetic, particle, hybrid, reduced and equilibrium descriptions, each edge labelled with its ordering, each node with its VAFT modes |
formalism_axes_map |
The independent axes of vaft.code.formalism.AXES, one column each with an N/A cell, and three runs (CGYRO nonlinear, DCON kinetic, NUBEAM) drawn across them. The numerical representation is a separate box, not an axis |
solver_placement_map |
Every adapter mode against bulk, model, spatial domain and regime, filled from its record, unclassified modes greyed; then the codes not in VAFT, hollow |
kinetic_mhd_coupling |
Kinetic-MHD as a coupling loop between an ideal-MHD bulk and a drift-kinetic population: energy closes it (DCON, GPEC kinetic), closure is one-way (PENTRC), and RDCON’s T_e, n_e input is a coefficient, not a coupling |
reduced_model_ancestry |
Parent theory, model reduction and VAFT realization of every reduced mode; TGLF-NN descends from TGLF. Every row inherits its parent’s contract and adds a closure no contract covers |
Reduced representations
How formulas compress plasma information, from fields to profiles to scalars and from dimensional to
dimensionless quantities, is classified on
Reduced representations (#1626). The figures
are built from the formulas’ Reduction docstring sections and the relation metadata in
vaft.formula._taxonomy.
vaft.diagram.reduced_representation_hierarchy()
vaft.diagram.reduced_representation_hierarchy_compact()
vaft.diagram.reduction_graph(family="current_q") # "pressure_energy", "kinetic_profiles", "dimensionless_similarity"
| Diagram | Concept |
|---|---|
reduced_representation_hierarchy |
Spatial reduction (field, profile, scalar) down, dimensionless normalisation across, so a dimensionless quantity can still be a profile. Each cell counts the catalogued formulas that land there |
reduced_representation_hierarchy_compact |
The same axes and arrows with only the example symbols in each box, without the counts, at reduced box height and spacing, for slides and overview pages |
reduction_graph |
One family as a layered graph of quantities. A solid edge is a formula, and its catalogued reduction kind is shown as via. A dashed edge is a step VAFT performs elsewhere |
Tokamak geometry and flux coordinates
The parent geometry that the cylindrical and slab reductions start from. Surfaces are
miller_surface, the shift is shafranov_shift_from_r_a_R0_beta_p_li, the field is
vacuum_toroidal_field, and $\theta^*$ is straight_field_line_angle.
vaft.diagram.tokamak_torus(projection="3d") # "poloidal"
vaft.diagram.flux_surfaces(shape="circular") # "shifted"
vaft.diagram.shaping_family()
vaft.diagram.hfs_lfs_field()
vaft.diagram.safety_factor_winding(q=3)
vaft.diagram.flux_coordinates()
vaft.diagram.poloidal_angle_comparison()
vaft.diagram.unwrapped_flux_surface(q=2.5)
vaft.diagram.field_line_pitch(q=1.0)
| Diagram | Concept |
|---|---|
tokamak_torus |
$R_0$, $a$, $\phi$ (counter-clockwise from above) and $\theta$ (from the outboard midplane), with $R = R_0 + r\cos\theta$ |
flux_surfaces |
Concentric surfaces, then the Shafranov shift: $\Delta(r)$ is zero at the edge and largest on axis, so the magnetic axis sits outside the geometric axis |
shaping_family |
Circular, $\kappa$, $\delta$, and both. Positive triangularity pulls the top in to $R_0 - \delta r$ |
hfs_lfs_field |
$B_\phi = B_0R_0/R$ is stronger on the inboard (high-field) side |
field_line_pitch |
$\mathbf B = B_\phi\hat{\boldsymbol\phi} + B_\theta\hat{\boldsymbol\theta}$ at a point of a field line, with $B_\theta/B_\phi = r/(qR)$ so that $\mathbf B$ lies along the line |
safety_factor_winding |
$q$ toroidal turns per poloidal turn, counted at one cross-section |
flux_coordinates |
$(\psi, \theta, \phi)$, with $+\phi$ into the page when $R$ is to the right and $Z$ is up |
poloidal_angle_comparison |
On a D shape, equal steps of $\theta^*$ are not rays of the geometric angle |
unwrapped_flux_surface |
A field line is straight, $d\phi/d\theta^* = q$, in straight-field-line coordinates, and not in the parametrisation angle $\theta$ |
The field line on a torus, its cylindrical and slab reductions, and the geometry/ordering map are in
the geometric-approximations section above. The toroidal → cylindrical → slab bridge is
geometry_ordering_map together with field_line_geometry.
Toroidicity and TF ripple
From the $1/R$ mirror to ripple-induced fast-particle transport. The formulas are in
vaft.formula.ripple, and each one names its diagram
under See Also. $B_\phi \propto 1/R$ itself is hfs_lfs_field, in the tokamak-geometry section.
vaft.diagram.trapped_and_passing_orbits()
vaft.diagram.toroidal_field_ripple(n_tf=16)
vaft.diagram.ripple_well_formation()
vaft.diagram.stochastic_ripple_orbit()
| Diagram | Concept | Formula |
|---|---|---|
trapped_and_passing_orbits |
$\mu$ and energy conservation in $B \propto 1/R$: small pitches bounce as bananas, large ones pass | parallel_speed_from_mu, vacuum_toroidal_field |
toroidal_field_ripple |
$N_\mathrm{TF}$ coils corrugate $B(\phi)$: maximal under a coil, minimal between | toroidal_ripple_field, ripple_amplitude |
ripple_well_formation |
Along a field line the ripple makes local wells where $\alpha^* \lesssim 1$, near the midplanes (to first order in $\epsilon$) | ripple_well_parameter |
stochastic_ripple_orbit |
Ripple kicks at banana tips decorrelate above $\delta_\mathrm{GWB}$. Drawn as the standard map with $K \sim \delta/\delta_\mathrm{GWB}$ | gwb_stochastic_threshold, gwb_stochasticity_parameter |
These are regime indicators, not a loss calculation. Orbit following (ASCOT, NUBEAM) is the quantitative check. Low-$n$ error fields, NTV and locking are separate topics.
Guiding-centre invariants and toroidal symmetry
This is the global view of the orbit physics that curvature_drift and toroidal_drift show locally.
The grad-B and curvature drifts say why a guiding centre moves at each instant. Conservation of
$P_\phi$ constrains the whole orbit in an axisymmetric field. These are the same physics seen two
ways, not competing explanations.
vaft.diagram.guiding_center_invariants()
vaft.diagram.canonical_toroidal_momentum(phase=0.45)
vaft.diagram.toroidal_symmetry_breaking()
| Diagram | Concept | Formula |
|---|---|---|
guiding_center_invariants |
Gyration, bounce and toroidal drift, with invariants $\mu$, $J_\parallel = \oint p_\parallel\,dl$ and $P_\phi$, valid for $\Omega_c \gg \omega_b \gg \omega_d$ | magnetic_moment |
canonical_toroidal_momentum |
A banana built from $\mu$ and $P_\phi$ conservation. From the bounce tip, $\Delta(q\psi)$ and $\Delta(mv_\parallel Rb_\phi)$ cancel, so $\psi$ moves with $v_\parallel$: this is the orbit width. phase is the state a future animation steps |
guiding_center_toroidal_momentum, parallel_speed_from_mu |
toroidal_symmetry_breaking |
A 3-D field changes $P_\phi$. Away from resonance the change oscillates; where $\Delta\omega_\mathrm{BH} = 0$ it is secular | bounce_harmonic_detuning |
Conventions.
- $P_\phi = mRv_\phi + qRA_\phi$ (
canonical_toroidal_momentum) uses physical components and the IMAS $\phi$. - The guiding-centre form uses $\psi = RA_\phi$ in Wb per radian, largest on the axis for a current
along $+\phi$.
psi_per_radian_from_cocosconverts a stored flux: $-\psi/2\pi$ for COCOS 11 (IMAS DD3) and $+\psi/2\pi$ for COCOS 17 (DD4). Used as stored, the flux has the wrong sign or a $2\pi$ error. - $J_\parallel$ is documented but deliberately not a numerical helper: its bounce interval and orientation depend on the orbit.
- The drift of one orbit’s $P_\phi$ is not NTV. Torque and transport are #1111’s, and need the kinetic response of the whole distribution.
Straight-field-line coordinates
Straight field lines are a condition, not a coordinate system. The freedom the condition leaves is what
PEST, Boozer, Hamada and equal-arc fix in different ways. All four are members of one generalised
family, $\mathcal{J} \propto R^{p_R}/(B_p^{p_{Bp}}B^{p_B})$ (vaft.formula.generalized_straight_field_line_angle,
as in DCON/GPEC). The unwrapped picture of a straight field line is unwrapped_flux_surface.
Clebsch, field-aligned and ballooning coordinates are #1075’s.
vaft.diagram.sfl_coordinate_grids()
vaft.diagram.sfl_coordinate_taxonomy()
vaft.diagram.sfl_fourier_convergence()
| Coordinate | $(p_{Bp}, p_B, p_R)$ | Geometric $\phi$ kept | Also simplified | Typical use |
|---|---|---|---|---|
| PEST | $(0, 0, 2)$ | yes | the toroidal angle | classical MHD stability |
| Boozer | $(0, 2, 0)$ | no ($\zeta = \phi + \nu$) | $\mathcal{J} \propto B^{-2}$, the $B$ spectrum | orbits, neoclassical, 3-D |
| Hamada | $(0, 0, 0)$ | no | flux-function Jacobian; current lines straight too | MHD stability |
| equal-arc | $(1, 0, 0)$ | no ($\zeta = \phi + \nu$) | uniform poloidal sampling | numerical representation |
- Same equilibrium, different grids. All four panels of
sfl_coordinate_gridsshare one equilibrium: $R_0/a = 1.7$, $\kappa = 2$, $\delta = 0.45$. The surfaces are identical and only the angle changes. A test checks this. - Different Fourier costs. In
sfl_fourier_convergence, one outboard-localised, axisymmetric perturbation needs anywhere from 9 to 26 poloidal harmonics, depending on the angle it is expanded in. Moved inboard, the ranking reverses: which angle is compact depends on where the structure sits. For $n \ne 0$, the toroidal shift $\nu$ of every angle except PEST couples harmonics further. The figure leaves that out. - Only PEST keeps $\phi$. Keeping the geometric $\phi$ forces $\mathcal{J} \propto R^2$, so every other member has $\zeta = \phi + \nu$.
- Boozer ≈ PEST here. The two nearly coincide at this $B_p \ll B_\phi$, where $B^2 \propto R^{-2}$.
- COCOS is not a coordinate choice. It fixes signs and orientations across every node of the taxonomy, independently of which angle is chosen.
Toroidal shift, action angles, validity, and COCOS
Every straight-field-line angle except PEST needs its own toroidal angle $\zeta = \phi + \nu$.
vaft.formula.equilibrium.sfl_toroidal_angle_shift gives the shift as $\nu = q(\theta_\mathrm{sfl} -
\theta_\mathrm{PEST})$, which follows from $d\phi = q\,d\theta_\mathrm{PEST}$ and $d\zeta = q\,d\theta_\mathrm{sfl}$
along a field line. For a toroidal mode number $n \neq 0$, a mode aligned with the nearest rational surface
$q = m_0/n$, read at fixed $\zeta$, has in every angle the $n = 0$ spectrum moved to $m \approx m_0$ with its width
unchanged. sfl_fourier_convergence(n=2) shows this: the shift $\nu$ is what keeps such a mode compact. A
structure fixed in the geometric $\phi$ is instead moved by the angle-dependent $\nu$, and stays unshifted only
in PEST (spectra_with_toroidal_mode).
field_line_action_anglefollows one field line over a poloidal turn. Against the geometric angles $(\phi, \vartheta)$ it bends; against $(\zeta, \theta_\mathrm{PEST})$ it is the straight line of slope $1/q$. Nested surfaces make the field-line flow integrable, so SFL coordinates are its action-angle variables; the action is the toroidal flux, and the poloidal flux is the Hamiltonian. Canonical SFL coordinates, which also give the guiding-centre Hamiltonian its canonical form, stay a branch ofsfl_coordinate_taxonomy; no transformation is implemented here.sfl_coordinate_validitycomputes $|q|$ toward the last closed surface. On a limited Solov’ev equilibrium it settles; on a single-null one it grows as $-\ln(1 - \psi_N)$, because $B_p \to 0$ at the X-point and $\theta^*$ degenerates there. Islands and stochastic fields have no global surfaces at all (seemagnetic_islandandstochastic_layer).coordinates_vs_cocosdraws the coordinate choice and the COCOS convention as orthogonal axes: every combination is valid, and a coordinate system is not a COCOS convention.
DCON and GPEC use the generalized family (PEST, Boozer, Hamada, equal-arc, and $J \propto R^{p_R}/(B_p^{p_{B_p}}
B^{p_B})$). An input or output handled in VAFT should therefore keep four things together: the coordinate type,
its powers, the Fourier convention $e^{i(m\theta - n\zeta)}$, and where the mapping came from. Two analyses in
different SFL coordinates describe the same equilibrium with different harmonic content. DCON output
already keeps jac_type and the powers (vaft.code.gpec).
vaft.diagram.sfl_fourier_convergence(n=2)
vaft.diagram.field_line_action_angle()
vaft.diagram.sfl_coordinate_validity()
vaft.diagram.coordinates_vs_cocos()
Clebsch labels and the ballooning representation
These figures go from straight-field-line coordinates to the local, field-aligned and ballooning
descriptions that high-$n$ stability and turbulence models use. The formulas are
field_line_label, s_alpha_curvature_drive and s_alpha_ballooning_solution in
vaft.formula.stability, on the circular $s$–$\alpha$ model.
vaft.diagram.clebsch_field_line_label(q=2.5)
vaft.diagram.ballooning_curvature_drive()
vaft.diagram.ballooning_newcomb_test()
vaft.diagram.ballooning_harmonic_envelope(n=20)
vaft.diagram.ballooning_workflow()
| Diagram | Concept |
|---|---|
clebsch_field_line_label |
A field line is where $\psi$ = const meets $\alpha = \phi - q\theta$ = const. In helical_phase’s left-handed $(\psi, \theta, \phi)$, with $\psi$ rising outward and $\mathbf B$ along $+\phi$, $\mathbf B \propto \nabla\psi\times\nabla\alpha$. Right-handed coordinates give the Connor–Hastie–Taylor form $\nabla\alpha\times\nabla\psi$ |
ballooning_curvature_drive |
Normal curvature $\cos\theta > 0$ (bad, outboard, shaded) once per $2\pi$ period of the covering space, and the total drive $K = \cos\theta + \Lambda\sin\theta$. Its outer lobes come from the geodesic term, which grows with the local shear. Also shown with $\theta_0$ |
ballooning_newcomb_test |
Newcomb’s test on the marginal solution $F(\theta)$: it stays positive (stable), crosses zero (unstable), or is positive again beyond $\alpha_2$ (second stability). These are not localised eigenfunctions |
ballooning_harmonic_envelope |
$a_m = \hat F(m - nq)$ for a stated model envelope: many coupled harmonics around $nq$. Their sum oscillates at $m \approx nq$ under the envelope, localised outboard |
ballooning_workflow |
Straight-field-line coordinates → field-line label → field-aligned → ballooning / flux tube, and the infinite-$n$ path. A finite-$n$ global calculation keeps what that path drops |
The straight-field-line coordinates they start from are in the section above (sfl_coordinate_taxonomy).
The resulting $(s, \alpha)$ stability diagram is s_alpha_ballooning.
Field-aligned basis, flux tube, shear, and the ballooning eigenfunction
field_aligned_basisunrolls one flux surface. Its field lines are the lines of constant $\alpha = \phi - q\theta$. With $x = \psi$, $y = \alpha$ and $z = \theta$, two coordinates are constant along $\mathbf B$, so $\mathbf B\cdot\nabla = (\mathbf B\cdot\nabla z)\,\partial_z$.flux_tube_patchgoes from a flux surface to one field line, then to its thin neighbourhood, then to the local $(x, y, z)$ box used by flux-tube gyrokinetic codes and by the sheared slab.magnetic_shear_field_alignedfollows a mode along the line. With $k_x = k_y\hat s\theta$ (ballooning_radial_wavenumberat $\alpha = 0$, the $\Lambda$ of the $s$-$\alpha$ model), its phase fronts rotate: the sheared slab’s $k_x(z) = k_{x0} + k_y\hat s z$, seen in the tokamak. The binormal period stays fixed, so the spacing across the fronts shrinks as $1/\sqrt{1+\hat s^2\theta^2}$; that growth of $k_\perp$ is the $(1+\Lambda^2)$ of line bending and inertia.-
ballooning_eigenfunctionsolves the $s$-$\alpha$ equation with inertia on the extended angle (s_alpha_ballooning_eigenmode). An unstable surface has a mode peaked at the outboard midplane (bad curvature) that decays within a few transits; a stable surface has only the continuum. ballooning_transit_mapties the extended angle to the cross-section: one poloidal circle per transit $k$, under $\theta = 2\pi k$, its outboard point (bad curvature) labelled with $F(2\pi k)$. The same point, revisited on every transit, carries less of the mode each time.ballooning_boundary_conditionsputs the two ways of closing the field line side by side. The ballooning representation requires decay on the covering space, $F \to 0$ as $|\theta| \to \infty$. A flux tube joins its ends after one poloidal turn, and because $k_x = k_y\hat s\theta$ the rejoined end has a shifted $k_x$: twist and shift, named and not derived.field_aligned_xpoint_limitationdraws lines of constant straight-field-line angle $\theta^$ ($d\theta^/dl \propto 1/(R^2B_p)$) on the diverted toy equilibrium of the Grad–Shafranov diagrams. They are evenly spread in the core and crowd into the X-point near the separatrix, where $B_p \to 0$ and $q \propto \oint dl/(R^2B_p)$ diverges; outside it the lines are open and X-point-adapted coordinates take over. See alsosfl_coordinate_validity(#1074).
vaft.diagram.field_aligned_basis(q=2.5)
vaft.diagram.flux_tube_patch()
vaft.diagram.magnetic_shear_field_aligned(shear=1.0)
vaft.diagram.ballooning_eigenfunction()
vaft.diagram.ballooning_transit_map(transits=2)
vaft.diagram.ballooning_boundary_conditions(shear=1.0)
vaft.diagram.field_aligned_xpoint_limitation(n_theta=24)
Ballooning formulations
How the reduced $s$–$\alpha$ equation follows from the general ballooning equation, and how DCON’s $C_A$ and GPEC.jl’s ballooning $\Delta’$ relate to it, is on Ballooning formulations (#1637).
vaft.diagram.ballooning_formulation_hierarchy()
| Diagram | Concept |
|---|---|
ballooning_formulation_hierarchy |
The general equation either loses its geometry step by step (large aspect ratio, shifted circles, the $1 + \Lambda^2$ metric, the $\cos\theta + \Lambda\sin\theta$ curvature) to become the CHT equation VAFT solves, or keeps it, as DCON ($C_A$, stable when positive) and GPEC.jl ($\Delta’$, stable when negative, with poles) do. All three must share the normalisation of shear_from_volume and ballooning_alpha_from_volume before their boundaries can be compared |
Slab resonant layers: tearing and twisting parity
How a global harmonic becomes a local layer response. The mapping $(m, n) \to (k_y, k_z)$ and
$q = m/n \Leftrightarrow k_\parallel = 0$ is mode_number_mapping. The flux is
vaft.formula.slab_perturbed_flux; its tearing form is the island pendulum of magnetic_island.
vaft.diagram.slab_parity(parity="tearing") # "twisting"
vaft.diagram.slab_parity_comparison()
vaft.diagram.poloidal_harmonic_coupling(m=3)
vaft.diagram.resonant_layer_matching()
| Diagram | Concept |
|---|---|
slab_parity |
Contours of $\Psi_T = B_s’x^2/2 + \psi_0\cos k_yy$: an island of width $4\sqrt{\psi_0/B_s’}$, O- and X-points, and $\delta B_x(0) \ne 0$. Contours of $\Psi_W = B_s’x^2/2 + \psi_1x\cos k_yy$, drawn with its $O(\psi_1^2)$ completion: no normal field at the layer ($k_\parallel = 0$) and no reconnection, and every surface, the rational one included, displaced together by $\xi = -\psi_1\cos k_yy/B_s’$ |
slab_parity_comparison |
Both side by side, with the parity of $\tilde\psi$ and $\tilde\phi$, $\delta B_x(0)$ and the topology |
poloidal_harmonic_coupling |
$\cos\theta$ (toroidicity) couples $m \to m \pm 1$ and $\cos 2\theta$ (elongation) couples $m \to m \pm 2$, at fixed $n$. The harmonic index $m$ is not the parity |
resonant_layer_matching |
Every rational surface of one $n$ has a T and a W channel. The outer region couples them all into one $2N\times2N$ matrix (RDCON/STRIDE), and each layer is solved on its own (SLAYER) |
Cylindrical geometry: profiles, mode shapes and matching
The screw-pinch picture between the torus and the slab. The profile is Wesson’s peaked current
$j \propto (1 - x^2)^\nu$, given by peaked_current_safety_factor together with
cylindrical_poloidal_field (Ampère). The screw-pinch field line is
field_line_geometry("cylindrical"), and the cylinder-versus-torus harmonic contrast is
poloidal_harmonic_coupling.
vaft.diagram.current_to_q_profile(nu=1.0, q_a=3.5)
vaft.diagram.cylindrical_rational_surfaces(n=1)
vaft.diagram.cylindrical_mode_morphology()
vaft.diagram.internal_external_kink()
vaft.diagram.plasma_vacuum_wall(m=2)
vaft.diagram.cylindrical_tearing_outer(m=2, n=1)
| Diagram | Concept |
|---|---|
current_to_q_profile |
$j \to B_\theta \to q$. More peaked current means lower $q_0 = q_a/(\nu+1)$ and stronger shear |
cylindrical_rational_surfaces |
For one $n$, one surface $q(r_s) = m/n$ for each $m$ between $q_0$ and $q_a$ |
cylindrical_mode_morphology |
$m = 0$ sausage, $m = 1$ kink (a rigid shift), $m = 2, 3$ helical distortions |
internal_external_kink |
The $m = 1$ internal kink lives inside $q = 1$. An external kink reaches the boundary. Kruskal–Shafranov is a heuristic |
plasma_vacuum_wall |
One harmonic matched across plasma, vacuum ($Ar^m + Br^{-m}$) and an ideal wall |
cylindrical_tearing_outer |
The outer solutions at $r_s$ and their $\Delta’$, for the outer, ideal problem. The inner layer at $r_s$ is slab_parity |
From the cylinder to the slab: mode_number_mapping expands $k_\parallel(r)$ about $r_s$ into the local
slab of local_slab_from_cylinder, and resonant_layer_matching couples several such layers.
The screw-pinch field line itself is field_line_geometry("cylindrical"), and the cylinder-vs-torus harmonic
picture (independent $m$ vs toroidally coupled $m, m\pm1$) is poloidal_harmonic_coupling.
Current-profile and q topology
What the shape of the current does to the poloidal field, the internal inductance and the safety
factor, and how the shape of $q$ decides how many rational surfaces one $m/n$ has (#1604). All four
diagrams use one reduced model: a straight cylinder whose current is peaked
($j \propto (1 - x^2)^2$), broad ($1 - x^4$) or hollow / off-axis ($(0.1 + x^2)(1 - x^2)^{3/2}$), each
normalized to the same $I_p$. The chain is cylindrical_enclosed_current, cylindrical_poloidal_field
(Ampère), cylindrical_safety_factor_from_r_B, cylindrical_poloidal_flux (for $\psi_N$),
cylindrical_internal_inductance and shear_from_r_q. The crossings come from
vaft.process.equilibrium.find_rational_surfaces, the routine that finds them on a reconstructed $q$.
These are concept figures, not equilibria and not stability results.
vaft.diagram.current_profile_shapes()
vaft.diagram.q_profile_topologies()
vaft.diagram.q_profile_landmarks(profile="monotonic") # or "reversed_shear"
vaft.diagram.rational_surface_topology(profile="reversed_shear", m=2, n=1)
| Diagram | Concept |
|---|---|
current_profile_shapes |
$j(r) \to I(r) \to B_\theta(r) \to l_i$ at fixed $I_p$. Peaked, broad and hollow are shapes of the current density. $l_i$ is one number for the whole profile: a more centrally enclosed current tends to a larger $l_i$, but different profiles can share one $l_i$ |
q_profile_topologies |
Peaked current gives a monotonic $q$, broad current a wide weak-shear core ($s \approx 0$), hollow current a reversed-shear $q$ with $s < 0$ inside $q_{\min}$. Every $q$ has $s \to 0$ on axis, so each column carries a narrow $s \approx 0$ band there; the width of the flat core is the discriminator. Not every hollow current reverses the shear |
q_profile_landmarks |
$q_0$ on axis; $q_{\min}$, equal to $q_0$ only when $q$ is monotonic; $q_{95} = q(\psi_N = 0.95)$, which is not at $r/a = 0.95$; $q_a$, the boundary value of a cylinder or limited plasma. In a diverted equilibrium $q \to \infty$ at the separatrix, so $q_{95}$ is quoted. In the cylinder $q_a = q_\mathrm{cyl}$; in a shaped torus $q_{95}$ and the edge $q$ differ from $q_\mathrm{cyl}$ and $q^*$ |
rational_surface_topology |
A monotonic $q$ crosses one $m/n$ once. A reversed-shear $q$ can cross it twice, $q(r_1) = q(r_2) = m/n$ with $r_1 < r_{\min} < r_2$: a double-resonant configuration. A double tearing mode is the instability in which tearing layers on the two surfaces couple, and only a stability calculation can say whether it grows |
These diagrams sit upstream of the others. current_to_q_profile keeps the analytic peaked
family $j \propto (1 - x^2)^\nu$ and cylindrical_rational_surfaces the monotonic surfaces of one $n$
(#1072). rational_surface keeps the single-crossing definition, and delta_prime and
tearing_layer_matching follow it (#1039). The $l_i$–$q$ diagrams of li_qa (#1603) are
operating spaces: one scalar $l_i$ against one scalar edge $q$, from the literature. They are not
plots of a current profile. The data-side counterparts are vaft.plot.equilibrium_profile_j_tor and
equilibrium_profile_q (#1505).
Current diffusion and current drive
How the current-density and safety-factor profiles above form in time (#1605). Both diagrams evolve one
reduced model: the enclosed current $I(\rho, t)$ of a straight cylinder under
cylindrical_current_diffusion_rate (Faraday, Ampère and Ohm’s law with a non-inductive source
$j_\mathrm{ni}$), integrated implicitly. The resistivity is a fixed Spitzer
$\eta(\rho)$ (spitzer_resistivity_from_T_e_Z_eff_ln_Lambda) on a hot-core, cold-edge $T_e(\rho)$, and
time is in units of the core resistive_diffusion_time $\tau_R = \mu_0 a^2/\eta(0)$. The profiles are
representative, not universal: there is no transport, bootstrap current or toroidal geometry.
vaft.diagram.current_diffusion()
vaft.diagram.current_drive_profiles(deposition="off_axis") # or "on_axis"
| Diagram | Concept |
|---|---|
current_diffusion |
A fast ohmic ramp, $t_\mathrm{ramp} = 0.01\,\tau_R \ll \tau_R$ (the ordering of a fast ramp such as VEST’s; the ratio is illustrative), leaves the current in an off-axis shell, depleted at the cold edge: hollow $j_\phi$, $q_{\min}$ off axis and $s < 0$ inside it. The current then penetrates and relaxes towards $j_\phi \propto 1/\eta$ with one $E_\phi$ across the radius. The relaxed profile is peaked only because the core is hotter; with a uniform $\eta$ (dashed) the same diffusion relaxes to a flat current |
current_drive_profiles |
Ohmic, ECCD and NBCD rows: actuator, the source $j_\mathrm{drive}(\rho)$ it drives, the total $j_\phi(\rho, t)$ before, shortly after and long after switch-on, and $q(\rho, t)$. The driven source persists where it is deposited while the ohmic current around it readjusts resistively; the source itself does not diffuse. ECCD is narrow and modifies the local shear; NBCD is broad. NBI’s pressure, rotation, fast-ion and bootstrap effects and counter-drive are not drawn |
Field configurations, reconnection and MHD waves
The canonical slab configurations, the topology of reconnection, and the linear ideal-MHD waves.
All are drawn in the slab frame of vaft.formula.geometry: $x$ is the sheet normal (radial), $y$ the
reconnecting (binormal) direction, and $z$ the current and guide-field direction. The geometry itself
(slab, sheared slab, cylinder, torus) belongs to Geometric approximations.
The formulas are harris_sheet_field, harris_sheet_current_density and x_point_flux in geometry,
and shear_alfven_frequency and magnetosonic_phase_speeds in stability.
vaft.diagram.slab_field_configuration(kind="sheared") # "uniform", "reversed", "guide"
vaft.diagram.current_sheet(guide_field=False)
vaft.diagram.harris_sheet()
vaft.diagram.x_point()
vaft.diagram.magnetic_reconnection()
vaft.diagram.island_formation()
vaft.diagram.shear_alfven_wave()
vaft.diagram.fast_magnetosonic_wave()
vaft.diagram.mhd_wave_family()
| Diagram | Concept |
|---|---|
slab_field_configuration |
The field on stacked $x$ = const sheets. Uniform; sheared, where the direction rotates and $\lvert\mathbf B\rvert = B_0$ to first order in $x/L_s$; reversed, where $B_y(-x) = -B_y(x)$ with a null at $x = 0$; and reversed with a guide field $B_g$, which rotates with no null. Shear and reversal are different things |
current_sheet |
The reversing Harris field seen along the current, with lines at equal flux spacing (spacing $\propto 1/\lvert B_y\rvert$), the sheet of thickness $2a$, its normal, and $\otimes J_z$. With guide_field=True, $B_g\hat{\mathbf z}$ removes the null and leaves $J_z$ unchanged |
harris_sheet |
$B_y = B_0\tanh(x/a)$ and $J_z = (B_0/\mu_0a)\,\mathrm{sech}^2(x/a)$ on one chart: the reversal and the localized current are the same layer |
x_point |
The current-free null $\psi = B’(x^2 - y^2)/2$: four branches and two separatrices at right angles. Geometry only |
magnetic_reconnection |
Model-neutral reconnection: inflow, outflow jets, diffusion region, upstream and reconnected field lines about a stretched X-point. No Sweet–Parker, Petschek, Hall or kinetic assumption |
island_formation |
slab_perturbed_flux with growing $\psi_0$: straight sheared lines, then X- and O-points, then an island of width $w = 4\sqrt{\psi_0/B’}$. The growth is delta_prime, slab_parity and tearing_layer_matching |
shear_alfven_wave |
Field-line bending with equally spaced lines, so $\lvert\mathbf B\rvert$ is unchanged to first order. $\delta\mathbf v_\perp$ and $\delta\mathbf B_\perp = -(B_0/v_A)\delta\mathbf v_\perp$ lie normal to the $\mathbf k$-$\mathbf B_0$ plane, and $\omega = \lvert k_\parallel\rvert v_A$ |
fast_magnetosonic_wave |
At $\mathbf k \perp \mathbf B_0$ the lines bunch and spread, $\delta B_z = -B_0\partial_x\xi_x$, and $v_f = (v_A^2 + c_s^2)^{1/2}$. It is the compressional Alfvén wave only in the limit $c_s \ll v_A$ |
mhd_wave_family |
The Friedrichs diagram of fast, shear-Alfvén and slow phase speeds against the angle to $\mathbf B_0$ ($v_s \le v_A\lvert\cos\theta\rvert \le v_f$), with each branch’s restoring force, compressibility and polarization |
From these to the tokamak: the current sheet and the island lead to the tearing layer (slab_parity),
then to the cylindrical and toroidal tearing mode (delta_prime, resonant_layer_matching). Here
$q(r_s) = m/n$ globally is $k_\parallel = 0$ locally (mode_number_mapping). The uniform-slab shear
Alfvén wave leads to the Alfvén continuum, where $v_A(r)$ and $k_\parallel(r)$ vary. Toroidal coupling
(poloidal_harmonic_coupling) then opens the gaps of the TAE and EAE. Those are not computed here.
The Grad–Shafranov problem: regions, boundaries and problem classes
The axisymmetric field is $\mathbf B = R^{-1}\nabla\psi\times\hat{\boldsymbol\phi} + F(\psi)R^{-1}\hat{\boldsymbol\phi}$,
with $\psi$ the poloidal flux per radian and $F = RB_\phi$ the poloidal-current function. Its components are
radial_magnetic_field_from_psi and vertical_magnetic_field_from_psi (COCOS-aware); in vacuum $F$ is constant, and
$B_\phi = F/R$ is vacuum_toroidal_field. One flux function $\psi(R, Z)$ is solved across the plasma, vacuum and coil regions, each with its own
source. vaft.formula.grad_shafranov_source gives the Ampère form $\Delta^*\psi = -\mu_0RJ_\phi$,
which holds in every region. toroidal_current_density_from_p_prime_ff_prime gives the plasma current
that force balance allows, $J_\phi = Rp’ + FF’/(\mu_0R)$. On a grid, the operator is
vaft.process.equilibrium.grad_shafranov_operator. The flux maps are a toy: prescribed ring currents
and three coils superposed through green_psi_exact. The topology (axis, X-point, limiter contact, LCFS)
is then found from the total flux, as a free-boundary code finds it. A production solver would also make
the plasma current consistent with $p’$ and $FF’$.
vaft.diagram.grad_shafranov_domain_decomposition()
vaft.diagram.fixed_vs_free_boundary_equilibrium()
vaft.diagram.limiter_and_diverted_topologies()
vaft.diagram.equilibrium_problem_taxonomy()
vaft.diagram.poloidal_flux_source_decomposition()
| Diagram | Concept |
|---|---|
grad_shafranov_domain_decomposition |
Same $\psi$, different $J_\phi$. The plasma source comes from force balance. The vacuum is homogeneous, $\Delta^*\psi = 0$: Laplace-type, but not $\nabla^2$. A coil carries its prescribed current. All of this sits inside the computational boundary |
fixed_vs_free_boundary_equilibrium |
The boundary is an input (LCFS and $\psi_b$ given, only the inside solved) or it is part of the solution (coils and sources given, $\psi$ everywhere, LCFS read from the topology) |
limiter_and_diverted_topologies |
Limited: the LCFS is the surface through the limiter tip. Diverted: the separatrix through the X-point ($\nabla\psi = 0$), with SOL and private flux. The boundary is the larger of $\psi_\mathrm{lim}$ and $\psi_X$ |
equilibrium_problem_taxonomy |
Forward/inverse and fixed/free are separate axes. CHEASE is forward and fixed, TokaMaker forward and free, EFIT inverse and free. Free boundary and inverse are not synonyms |
poloidal_flux_source_decomposition |
$\psi_\mathrm{plasma} + \psi_\mathrm{coil} = \psi_\mathrm{total}$. Only the sum has the X-point and the LCFS. $\psi_\mathrm{passive}$ (eddy currents) is a further term, not drawn |
Equilibrium-aware phenomena: kink displacement and sawtooth
This layer sits between the reference diagrams and result plotting. The geometry comes from an
equilibrium (any vaft.data.equilibrium.EquilibriumData); the physical state is a prescribed, documented
model. The default equilibrium is the exact Solov’ev (Cerfon–Freidberg) equilibrium of
vaft.process.equilibrium.solovev_example with $A = 0$, where $q$ rises from about 0.8 to 3. Surfaces,
normals and the PEST angle $\theta^$ come from straight_field_line_map, and $q(\rho)$ from
calculate_q_profile_from_psi, with $\rho = \sqrt{\psi_N}$. The mode phase uses $\theta^$. The drawing
uses the real $(R, Z)$ surfaces.
vaft.diagram.kink_mode(equilibrium=None, m=1, n=1, amplitude=0.06, radial_profile="internal")
vaft.diagram.kink_mode(m=2, n=1, radial_profile="global", harmonics={2: 1.0, 3: 0.3})
vaft.diagram.sawtooth(stage="precursor") # "reconnection", "post_crash"
| Diagram | Model class | Concept |
|---|---|---|
kink_mode |
synthetic parameterization | Each surface moves along its normal by $\xi_n = A\,a\,F(\rho)\,\mathrm{Re}\sum c_m e^{i(m\theta^* - n\phi)}$, with a named envelope. internal is a top hat inside $q = m/n$, global is $\rho^{m-1}$, and edge is $\rho^{4m}$. Under flux freezing this is flux_perturbation_from_normal_displacement, $\delta\psi = -\xi_n\lvert\nabla\psi\rvert$. It is not an eigenfunction. The cylindrical reference view is internal_external_kink |
sawtooth |
reduced model | precursor: the 1/1 internal kink inside $q = 1$, with nested topology kept. reconnection: a hot core of radius $\rho_1(1-f)$ pushed against an outer separatrix, with the X-point where they touch and the 1/1 island in the crescent between. post_crash: nested surfaces again, with the region inside the Kadomtsev mixing radius flattened at conserved $\int T\rho\,d\rho$ |
Two reduced Hamiltonian models extend the vocabulary of magnetic_island to several resonances, and to the
X-point.
vaft.diagram.stochastic_layer(regime="touching") # "isolated", "overlapping"; or overlap=, perturbations=
vaft.diagram.separatrix_lobes(perturbation=0.02, m=8, n=4) # or separatrix_lobes(equilibrium, ...)
| Diagram | Model class | Concept |
|---|---|---|
stochastic_layer |
reduced Hamiltonian | A Poincaré section of $H = \int\iota\,d\psi_N - \sum_k\epsilon_k\cos(m_k\theta^* - n_k\phi)$ on the equilibrium’s $q$ (default 3/2 and 2/1). Each resonance alone is the pendulum of island_pendulum_hamiltonian, width $4\sqrt{\epsilon/\lvert\iota’\rvert}$. The pair overlap $\sigma$ (vaft.process.perturbation.chirikov) is 0.5, 1 or 1.6. The inset shows where the section sits |
separatrix_lobes |
reduced Hamiltonian | A lower-single-null equilibrium (by default the single-null Solov’ev) plus a prescribed $\delta\psi \propto (r/r_X)^m\cos(m\vartheta - n\phi)$. The field-line map over $2\pi/n$ has a hyperbolic fixed point (Newton, multipliers $\lambda$ and $1/\lambda$). Its unstable and stable manifolds split from the unperturbed separatrix and cross each other, which makes lobes, and one strike point on the target becomes several |
In stochastic_layer, $x = \psi_N$ stands in for the toroidal-flux action, so $\epsilon$ is a model amplitude and area in the section is not flux. separatrix_lobes takes any lower-single-null EquilibriumData: the X-point must be a saddle of the flux on the boundary value, below the axis, and the window scales with the minor radius; a limited equilibrium is refused. Neither diagram is a GPEC, MARS or vacuum-field trace: those belong to result plotting.
The mixing radius comes from vaft.formula.kadomtsev_mixing_radius: the 1/1 helical flux
$\psi_* \propto \int r(1/q - 1)\,dr$ returns to its axis value there. It equals $\sqrt2\,r_1$ when
$1/q - 1$ is parabolic. Complete (Kadomtsev) reconnection is the $f \to 1$ limit, not a claim about every
crash.
MARFE
marfe puts a prescribed radiation condensation on the edge of an equilibrium and shows the condition that
makes it.
- On the high-field side (
localization="hfs", the usual location) the band straddles the last closed surface. It is about 30° wide poloidally and 0.1$a$ deep, as Lipschultz (1987) reports. - Next to the X-point (
"xpoint", Greenwald 2002 p. R35) the band is drawn on the closed side with the same sizes, which are borrowed, not measured there. - Its $T_e$ is below about 10 eV (Greenwald p. R34), and it is toroidally symmetric: a ring.
The right panel is Drake’s (1987) constant-pressure criterion in dimensionless form, computed from
vaft.formula.sol.radiative_condensation_growth_rate. For $k_\parallel > 0$ the boundary is
$k_\parallel^2\kappa_\parallel T/L = 2 - \partial\ln L/\partial\ln T$, so condensation does not need a
falling radiation curve. On the $k_\parallel = 0$ axis only the constant-density flute limit
(radiative_thermal_instability_growth_rate) applies, and it is unstable where $L$ falls with $T$.
The band is prescribed, not solved, and no cooling curve or density-limit formula is built in; MARFE onset
and density-limit semantics belong to #1068. The API names its sizes poloidal_width_deg and
radial_fraction; the issue’s "prescribed" localization is not implemented.
vaft.diagram.marfe(localization="hfs") # or "xpoint"; poloidal_width_deg=30, radial_fraction=0.1
Divertor heat-flux footprint
divertor_heat_footprint maps an Eich target profile onto a diverted equilibrium. The geometry comes from
the equilibrium itself: the X-point is the zero of $\nabla\psi$ next to the lowest boundary point, the
strike point is where the separatrix leg crosses a horizontal target, and the total flux expansion
$f_x = (\partial\psi/\partial R)\mathrm{OMP} / (\partial\psi/\partial s)\mathrm{target}$ is
evaluated on the flux, not assumed. The profile is vaft.formula.sol.eich_target_heat_flux_profile with
that $f_x$. $\lambda_q$ (at the outer midplane) and $S$ (at the target) are illustrative inputs: this is a
schematic, and measured IR profiles belong in vaft.plot. The SOL surfaces one, two and three $\lambda_q$
outside the separatrix at the midplane fan out to about $k\lambda_q f_x$ on the target. The equilibrium must be
lower single null, with an X-point on its boundary flux and legs that reach the target inside the limiter;
anything else is refused rather than drawn.
vaft.diagram.divertor_heat_footprint(lambda_q=0.004, spreading=0.0015, target="outer")
Disruption physics: quench sequence, runaways and energy paths
The chain from loss of confinement to a runaway plateau, each link a relation in the new
vaft.formula.disruption category or an existing one:
thermal_quench_temperature,current_quench_currentandinductive_parallel_electric_field;connor_hastie_critical_field,dreicer_field,runaway_critical_momentumandrelativistic_collision_time;dreicer_generation_rate,avalanche_growth_rate,avalanche_efolds_from_current_dropandrunaway_current_from_density;- the Spitzer resistivity, and the
startupplasma resistance, inductance and L/R time.
Detecting a disruption in data stays in vaft.process.transients. Simulating one belongs to kinetic or
integrated codes.
vaft.diagram.disruption_timeline()
vaft.diagram.disruption_causal_chain()
vaft.diagram.runaway_generation()
vaft.diagram.disruption_energy_pathways()
| Diagram | Concept |
|---|---|
disruption_timeline |
A 0-D reference model built from the formulas. A prescribed thermal quench raises the Spitzer $\eta$. The L/R current quench then induces $E_\parallel \approx 10^3E_c$ ($\approx 2\,\%$ of $E_D$). A Dreicer seed of a few kA is multiplied about 25-fold by the avalanche (at 1 MA, only a few e-folds) into a runaway plateau. Magnitudes are illustrative: no universal waveform |
disruption_causal_chain |
The same sequence as cause and effect, each arrow labelled by its formula |
runaway_generation |
The avalanche rate (per runaway) and the Dreicer rate (per electron) against $E/E_c$. The normalisations differ, so the two magnitudes are not compared. Nothing runs away below $E_c$, and Dreicer is drawn only within its asymptotic range, $E \le 0.1E_D$. Hot-tail seeding is not drawn |
disruption_energy_pathways |
Thermal energy leaves by conduction and radiation. Magnetic energy $\tfrac12L_pI_p^2$ goes to ohmic heating, the vessel and coils, runaway kinetic energy and halo currents (#1042). The existing thermal_energy_from_p_V, virial_thermal_energy and magnetic_energy_from_li_B_pa_V_p compute the two pools |
Vertical displacement events: hot and cold VDE, halo currents
A hot VDE moves a still-hot plasma into the wall, and the scraping can trigger the thermal quench. A cold
VDE follows the quench, as the decaying current loses its centred vertical equilibrium. Wall contact then
drives halo currents through the scrape-off layer and the wall. The model-neutral reference quantities are
in the new vaft.formula.vde category:
vertical_velocityandvde_growth_rate(a local $d\ln\lvert\Delta Z\rvert/dt$);thin_wall_timeandwall_mode_decay_time;halo_current_fractionandtoroidal_peaking_factor.
A wall element’s $L/R$ is lr_time_from_L_R. The named reduced VDE models (edge-current loss,
filament-plus-wall, analytic halo) are not chosen yet. The disruption chain these diagrams couple to is
vaft.formula.disruption.
vaft.diagram.hot_vde_sequence()
vaft.diagram.cold_vde_bifurcation()
vaft.diagram.plasma_wall_halo_current()
vaft.diagram.vde_timescales()
| Diagram | Concept |
|---|---|
hot_vde_sequence |
The Solov’ev plasma moved 0, 8 and 16 cm into its limiter. The limiting surface shrinks, so the edge moves inward to lower $q$ (the equilibrium’s own profile; the cylindrical estimate at fixed $I_p$ falls as $a^2$). Below, the causal chain towards the thermal quench |
cold_vde_bifurcation |
A schematic normal form: below a critical current the centred equilibrium is lost, and the plasma follows an off-centre branch into the wall as $I_p$ decays. The real branches come from a model not chosen here |
plasma_wall_halo_current |
Poloidal halo current through the scrape-off layer and the wall, toroidal eddy currents in the wall, and $\mathbf J_\mathrm{halo}\times\mathbf B_\phi$ on the floor, for one sign of $I_p$ and $B_\phi$ |
vde_timescales |
$a/v_A$, the $m = 1$ wall time, and the L/R current-quench time at 5 and 20 eV for one medium-tokamak parameter set. The ordering is not universal |
Plasma-wall interaction
The plasma-wall vocabulary: one impact and its outcomes, reflection (particle versus energy), physical
sputtering, recycling versus retention, and particle versus energy balance. This is level 0 of the
issue’s enrichment, semantics only. No reflection or sputtering coefficient, yield or threshold is drawn
unless it is computed by a vaft.formula.pwi relation from inputs the caller supplies:
binary_collision_energy_transfer_factor, the exact elastic kinematics;mean_reflected_energy_fraction, which is $R_E/R_N$;recycling_coefficient;sputtering_threshold_bohdansky, a named empirical fit that needs the surface binding energy.
Projectile and target species go through vaft.data.atomic and are drawn apart: projectile blue,
target dark. Each diagram’s model names the IMAS paths of the quantities it shows, under
wall.global_quantities.neutral[:]: the recycling particle and energy coefficients, the fluxes from the
plasma and from the wall, the wall inventory, and the per-incident-species sputtering coefficients.
IMAS’s recycling energy coefficient covers all recycling channels, so it is not the prompt-reflection
$R_E$. The canonical sputtering figure uses $E_s = 8.68$ eV, the sublimation energy of W, as a stated input.
vaft.diagram.plasma_wall_interaction_processes(projectile="D", target="W")
vaft.diagram.plasma_wall_interaction_reflection()
vaft.diagram.plasma_wall_interaction_sputtering(surface_binding_energy=8.68) # threshold only if E_s given
vaft.diagram.plasma_wall_interaction_recycling()
vaft.diagram.plasma_wall_interaction_energy_partition()
vaft.diagram.plasma_wall_interaction_reflection_energy()
vaft.diagram.plasma_wall_interaction_angle_dependence()
vaft.diagram.plasma_wall_interaction_sputtering_threshold(surface_binding_energy=8.68) # E_th only if E_s given
vaft.diagram.plasma_wall_interaction_particle_balance()
vaft.diagram.plasma_wall_interaction_surface_response()
| Diagram | Concept |
|---|---|
plasma_wall_interaction_processes |
Reflection (fast atom), implantation and retention, re-emission (thermal molecule), sputtering (target atom), and heat |
plasma_wall_interaction_reflection |
$E_\mathrm{in}$, $E_\mathrm{refl}$, $\theta_\mathrm{in}$ and $\theta_\mathrm{refl}$ as separate quantities; $R_N$ is not $R_E$ |
plasma_wall_interaction_sputtering |
A collision cascade ejects a target atom. One collision passes at most $\gamma E$ (D on W: $\gamma = 0.043$), hence the high threshold |
plasma_wall_interaction_recycling |
Prompt reflection plus delayed re-emission make recycling; retention is the rest |
plasma_wall_interaction_energy_partition |
Particle balance and energy balance side by side. They are not the same bookkeeping |
plasma_wall_interaction_reflection_energy |
Incident $f_i(E_i)$ to reflected $f_r(E_r)$ through the surface. Only the binary-collision limit $(1-\gamma)E_i$ is stated; $R_N(E_i)$, $R_E(E_i)$, $\langle E_r\rangle(E_i)$ are empty enrichment slots |
plasma_wall_interaction_angle_dependence |
Normal and grazing incidence, angles from the surface normal; $R_N$, $R_E$, $Y$ as functions of $(E_i, \theta_i)$ are named, not drawn |
plasma_wall_interaction_sputtering_threshold |
A schematic $Y(E_i)$ with no scale: a threshold exists. $E_\mathrm{th}$ is printed only for a supplied $E_s$ (Bohdansky) |
plasma_wall_interaction_particle_balance |
The projectile balance (reflected + re-emitted + retained) with the sputtered target atoms drawn apart, as another species’ source |
plasma_wall_interaction_surface_response |
Inputs, surface response ($R_N$, $R_E$, $Y_\mathrm{sput}$), outputs: the extension point for tabulated data or an external PWI code (levels 2–4) |
Spectroscopy and ionization
Concept diagrams in the vocabulary of vaft.data.atomic and vaft.data.spectroscopy. parse_emission_term and parse_line_label are
the same parsers emission= uses in vaft.plot, so a term that selects a trace selects the same diagram.
Metadata is progressive, and nothing is fabricated:
- level 0 is the semantic identity (stage, charge, element);
- level 1 is what the data declare (the wavelength in an IMAS
processed_linelabel such asOI_7770); - hydrogenic lines add Bohr-model levels and Rydberg vacuum wavelengths with the isotope’s reduced mass
(
hydrogenic_energy_levelandhydrogenic_transition_wavelengthinvaft.formula.atomic). For one-electron systems this model is the authoritative source; each model records it undersource; - many-electron levels and photon emissivities would need OPEN-ADAS ADF04 and ADF15. Those are extension
points and are not loaded; ADF11 stays in
vaft.formula.atomic.
vaft.diagram.spectroscopy_ionization_stages("C III") # "C2+", "carbon", "CIII_1909" too
vaft.diagram.spectroscopy_transitions("H-alpha") # "OI_7770": declared wavelength only
vaft.diagram.spectroscopy_energy_levels("D-alpha")
vaft.diagram.spectroscopy_spectrum() # the labels VEST's spectrometer declares
| Diagram | Concept |
|---|---|
spectroscopy_ionization_stages |
Every stage of an element, with the named one outlined. Stage $s$ is charge $s - 1$, and D and T are hydrogen with a mass number. Semantic only |
spectroscopy_transitions |
A hydrogen series member gets Bohr-model levels and its vacuum wavelength (an unspecified isotope is taken as protium, and the title says so). Fully stripped ions are refused, since they have no lines. Any other line gets unnamed levels, and a wavelength only if its label declares one |
spectroscopy_energy_levels |
The hydrogenic ladder with the Lyman, Balmer and Paschen series. Hydrogenic only: other species need ADF04 |
spectroscopy_spectrum |
Declared lines, each at its label’s wavelength (air above 200 nm by convention). Computed hydrogenic lines are dashed and in vacuum. Lines with no wavelength are listed, not placed |
Neutral beam injection: lifecycle and reduced attenuation
A small, machine-independent NBI layer. It is not NUBEAM, ASCOT5 or BEAMS3D, and it never replaces
vaft.code.nubeam.
- Formulas (
vaft.formula.nbi):beam_particle_rate_from_power_energy(per energy component, in eV),neutral_beam_optical_depth,neutral_survival_fraction_from_optical_depth,beam_birth_probability_density(a density along the path, not a volumetric deposition),shine_through_fraction, andinjected_toroidal_angular_momentum_rate. The last is the ideal rate carried in, not the torque on the plasma. - Process (
vaft.process.nbi.neutral_beam_attenuation_along_path): composes these along a prescribed 1-D path and adds the particle and power bookkeeping. Births are counted per path cell as $S_i - S_{i+1}$, so $\sum_i + f_\mathrm{shine} = 1$ and $P_\mathrm{birth} + P_\mathrm{shine} = P_\mathrm{injected}$ hold exactly on any grid. The “power birth profile” is where neutrals become fast ions, not where the plasma is heated. The process module’s docstring tabulates what this layer answers and what needs a full solver; related work is #265 (VEST NBI description), #592 (NUBEAM → IMAS), #1064 and #1092 (scales, orbits).
The attenuation coefficient $\alpha = \sum_j n_j\sigma_j$ is always an input; no beam-stopping data are built in. Orbits, trapped and passing fast ions, and $P_\phi$ are the particle-motion diagrams’, and are not redrawn here.
vaft.diagram.nbi_particle_lifecycle() # shine-through / prompt loss / delayed loss kept apart
vaft.diagram.nbi_neutral_attenuation() # S(s), b(s), births and f_shine from vaft.formula.nbi
Iteration behaviour, branch bifurcation and branch selection
Non-convergence can arise from true branch structure or from numerical cycling. A solver that does not converge has not necessarily found an unphysical solution, and these diagrams separate the cases. They are generic concept diagrams for EFIT convergence, grid, weighting and continuation studies. No shot, residual or grid comparison is shown or implied, and none of them claims that a physical bifurcation exists in VEST equilibria.
Every curve is computed:
- the iteration panels come from the logistic map $x_{k+1} = rx_k(1 - x_k)$ at $r = 2.8$, $3.2$ and $3.5$, and from an expanding linear map;
- the branch diagram is the saddle-node normal form $\dot x = \lambda + x - x^3$;
- the basins are its exact relaxation at $\lambda = 0$.
The bifurcation is drawn once. The basin diagram is the same model at one control parameter, not a second bifurcation figure.
vaft.diagram.iteration_behavior() # fixed point, divergence, 2-cycle, period-4 limit cycle
vaft.diagram.branch_bifurcation() # stable (solid) / unstable (dashed), folds, jumps, hysteresis
vaft.diagram.basin_of_attraction() # initial condition selects branch A or B
vaft.diagram.grid_induced_two_cycle() # each re-solve lands nearer the other node: a one-cell hop
vaft.diagram.branch_selection() # the last two side by side under one caption
| Diagram | Concept |
|---|---|
iteration_behavior |
$x_k$ against $k$ in one format. The fixed point $x^*$ (thin line) exists in every panel but is stable only in the first; guides mark the four levels of the period-4 cycle |
branch_bifurcation |
Stable and unstable branches, the two folds, the jump at each fold, and the hysteresis loop; three equilibria coexist between the folds |
basin_of_attraction |
Two stable solutions at one control parameter. The unstable equilibrium is the basin boundary, and the start decides the branch |
grid_induced_two_cycle |
A numerical artifact. Solved from A the optimum lands nearer B, and from B nearer A, so the index hops by one grid cell and the pattern changes with the grid. The fit stays nearly flat (a secondary cue). It is not a second physical branch |
branch_selection |
basin_of_attraction beside grid_induced_two_cycle: physical branch structure against numerical cycling |
Cold-plasma waves: dispersion, cutoffs, resonances and the CMA diagram
Each diagram is drawn from the cold-plasma equations in vaft.formula.waves:
plasma_frequency;stix_parameters, giving $R, L, S, D, P$ with the signed cyclotron frequency, so $\Omega_e < 0$, anddielectric_tensor;cold_plasma_refractive_index_squared, the two roots of $An^4 - Bn^2 + C = 0$, in a cancellation-free form that keeps the finite root at a resonance cone;perpendicular_refractive_index_squared, giving $n_O^2 = P$ and $n_X^2 = RL/S$;-
cma_coordinates, giving $X = \omega_{pe}^2/\omega^2$ and $Y =\Omega_e /\omega$; propagation_regime, which classifies propagating, evanescent, cutoff and resonance.
Boundaries are zeros or poles of the Stix parameters, located by bracketing the formulas; no closed form is typed into a drawing. The $\pm$ roots are algebraic branches, not mode names, so O/X and R/L are named only where the mode is tracked: at $\theta = \pi/2$ and $\theta = 0$. Electrons only, ions immobile: the electron-cyclotron range. Warm-plasma effects, damping, ray tracing and full-wave solutions are out of scope.
import math
vaft.diagram.o_mode_cutoff() # n_O^2 = P, cutoff at omega_pe
vaft.diagram.x_mode_dispersion(omega_pe_over_omega_ce=1.2) # L, R cutoffs; upper-hybrid resonance
vaft.diagram.cma_diagram() # P, R, L, S = 0 and Y = 1 in (X, Y)
vaft.diagram.profile_propagation() # layers along an example midplane
vaft.diagram.profile_propagation(theta=math.radians(60)) # oblique: both roots, A = 0 resonances
vaft.diagram.wave_dispersion_omega_k(omega_pe_over_omega_ce=1.2) # branches in the omega-k plane
vaft.diagram.refractive_index_vs_X(Y=0.5) # rising density at fixed field
vaft.diagram.refractive_index_vs_Y(X=0.5) # rising field at fixed density
Every view takes theta, from $5^\circ$ to $\pi/2$ (below that, parallel propagation is the picture). At $\theta = \pi/2$ the branches are named O (blue) and X (red). At any other
angle both roots of cold_plasma_refractive_index_squared are drawn in one colour, because the algebraic
$\pm$ branches swap at the cyclotron layer, and the resonance moves from $S = 0$ to the cone
$A = S\sin^2\theta + P\cos^2\theta = 0$, which is marked.
Neoclassical and NTV collisionality regimes
Two different regime families that share the word “collisionality”. Axisymmetric neoclassical transport orders the collision frequency against the transit and bounce frequencies. Neoclassical toroidal viscosity (NTV) in broken symmetry orders it against the bounce-averaged precession $\omega_d = \omega_E + \omega_B$:
Coulomb collisions -> transit / bounce motion -> nu_hat = qR nu/v -> banana / plateau / Pfirsch-Schlueter
3-D delta B + precession omega_d -> 1/nu / nu-sqrt(nu) / superbanana-plateau / nu -> NTV torque
The axis of the first diagram is $\hat\nu = qR_0\nu/v$ (collisions_per_transit), not a $\nu_$. VAFT’s
several $\nu_$ conventions (issue 353) share the symbol but not the value. The boundaries
$\hat\nu = \epsilon^{3/2}$ and $1$ come from neoclassical_regime_boundaries. The orbit scales behind
them are transit_frequency, deeply_trapped_bounce_frequency,
trapped_particle_effective_collision_frequency and banana_width in vaft.formula.neoclassical.
vaft.formula.ntv holds the exact, convention-bearing relations:
ntv_precession_frequency, whose zero is the superbanana-plateau resonance. $\omega_E$ is the $E\times B$ frequencyomega_exb, never the toroidal rotation, and both $\omega_E$ and $\omega_B$ are measured along the plasma current;nonambipolar_torque_density, the torque on the plasma: the $\mathbf J\times\mathbf B$ of the return current that cancels the non-ambipolar flux. Ion loss in a co-current plasma drives counter-current rotation.
The size of the flux needs a drift-kinetic code (vaft.code), and Shaing’s connected formula is not
implemented, so the NTV regime diagram shows slopes only.
vaft.diagram.neoclassical_collisionality(epsilon=0.1)
vaft.diagram.ntv_collisionality()
vaft.diagram.ntv_precession_regimes(omega_magnetic=1.0)
| Diagram | Concept |
|---|---|
nbi_particle_lifecycle |
Injection, ionisation, fast-ion birth, confinement, slowing down, heating and drive, then thermalisation. Each loss branches at its own stage: shine-through while neutral, prompt orbit loss after ionisation, delayed loss after confinement |
nbi_neutral_attenuation |
Survival $e^{-\tau}$ and birth density $\alpha S$ along a path through a parabolic plasma (an illustrative $\alpha$). Births are drawn at equal probability steps, and the shine-through is marked at the exit |
| Diagram | Concept |
|---|---|
o_mode_cutoff |
Evanescent below $\omega_{pe}$, propagating above; the cutoff $P = 0$ does not depend on $B$ |
x_mode_dispersion |
Evanescent below $\omega_L$, propagating to the upper-hybrid pole, evanescent to $\omega_R$, then propagating. Poles are masked |
cma_diagram |
Cutoffs (solid) and resonances (dashed) of a cold electron plasma in the CMA plane |
profile_propagation |
$n_O^2$ and $n_X^2$ along $R$, with strips where each mode propagates (propagation_regime), for an example tokamak (not a device) at the on-axis electron cyclotron frequency: O cutoffs, L and R cutoffs, the upper-hybrid layer behind the R cutoff, and the ECR. With theta, both oblique roots and the $A = 0$ layers |
wave_dispersion_omega_k |
Each branch in the $\omega$-$k$ plane ($k = n\omega/c$ where $n^2 > 0$): it starts at $k = 0$ on its cutoff ($P$, $R$ or $L = 0$) – the oblique whistler leaves the origin instead – runs to $k \to \infty$ at a resonance, and approaches the light line at high frequency |
refractive_index_vs_X |
$n^2$ against $X$ at fixed $Y$: cutoffs at $X = 1 - Y$ ($R$), $1$ ($P$), $1 + Y$ ($L$), the upper hybrid at $X = 1 - Y^2$, all located by bracketing |
refractive_index_vs_Y |
$n^2$ against $Y$ at fixed $X$: at $\theta = \pi/2$ the O branch does not depend on $Y$; the other has the $R$ cutoff at $Y = 1 - X$, the upper hybrid at $\sqrt{1 - X}$, and $Y = 1$ is the cyclotron layer |
| Diagram | Concept | ||
|---|---|---|---|
neoclassical_collisionality |
$D/D_\mathrm{plateau}$ against $\hat\nu$: asymptotes $\hat\nu/\epsilon^{3/2}$, 1 and $\hat\nu$ meeting at the formula’s boundaries. Orderings, not phase boundaries | ||
ntv_collisionality |
Non-resonant ($1/\nu$, then $\nu$–$\sqrt\nu$) and resonant ($1/\nu$, superbanana plateau, superbanana $\nu$) branches, with Shaing’s exponents. Schematic breakpoints | ||
ntv_precession_regimes |
$\nu_\mathrm{eff}$ against $\omega_E/\omega_B$: the resonance $\omega_d = 0$ and the ordering $\nu_\mathrm{eff} = | \omega_d | $ from ntv_precession_frequency. A schematic resonant band holds the superbanana plateau and $\nu$ regimes |
Canonical transport concepts
Three concept figures for the transport reference relations in vaft.formula.transport:
- how a diffusivity is defined;
- how gradients drive fluxes;
- how the classical, Bohm and gyro-Bohm reference scales relate.
They explain definitions. None predicts the transport of a plasma.
radial motion -> mean-square displacement (late slope) -> D
-> velocity autocorrelation (area) -> D
gradient -> diffusive term + convective term -> flux (one flux does not fix D and V)
random_walk_diffusion draws one exponentially correlated velocity three ways, with a fixed seed:
- sample trajectories;
- the mean-square displacement, which is ballistic at first and diffusive later;
- the autocorrelation.
Both definitions, diffusivity_from_mean_square_displacement on the late slope and
diffusivity_from_velocity_autocorrelation on the area, give $D = \langle v_r^2\rangle\tau_c$.
transport_flux_gradient keeps the diffusive and convective terms apart for particles, heat and toroidal
momentum (per unit volume in $v_\phi$). The momentum flux’s residual stress is a third term, neither diffusive nor
convective. Its inset shows the inverse problem: one
$\Gamma$ at one $\partial_r n$ is the line $V = (\Gamma + D\,\partial_r n)/n$, checked against
particle_flux_from_D_grad_n_V_n.
transport_reference_scales sorts the scales by role:
- collisional: classical and neoclassical;
- empirical reference: Bohm;
- turbulence normalisation: gyro-Bohm.
It draws no magnitude axis, because their sizes are not universally ordered. The banana, plateau and
Pfirsch–Schlüter regimes are neoclassical_collisionality above. It states its $\hat\nu$ convention, and
since #1112 it shades a transition range, a factor of 3 either side of each boundary, rather than
drawing exact lines.
vaft.diagram.random_walk_diffusion()
vaft.diagram.transport_flux_gradient()
vaft.diagram.transport_reference_scales()
| Diagram | Concept |
|---|---|
random_walk_diffusion |
Trajectories, mean-square displacement ($\propto t^2$, then $\propto 2Dt$) and velocity autocorrelation of one Ornstein–Uhlenbeck velocity. The Einstein slope and the Taylor–Green–Kubo area give the same $D$ |
transport_flux_gradient |
Gradient, then diffusive and convective terms, then flux, for particles, heat and momentum. One measured flux is a line of $(D, V)$, not a point |
transport_reference_scales |
Classical and neoclassical (collisional), Bohm (empirical) and gyro-Bohm (turbulence normalisation) with their vaft.formula.transport definitions. A taxonomy, not a ranking |
Wall conditioning
Baking, glow-discharge cleaning and boronization, each drawn as a transition of the wall state
$S^{(0)}\mathrm{wall} \to S^{(1)}\mathrm{wall}$ (WALL_STATE_CHANGE in the module), not only as
“cleaning”. The diagrams are reduced and semantic, and share one vessel with an inlet port, a pump port
and a wall-surface primitive:
- baking is thermal desorption only: no glow, anode, ion bombardment or coating;
- the glow discharges share one apparatus template: gas feed, glow, anode, the wall as cathode, and ions accelerated across the cathode sheath onto the whole wall. H$_2$/D$_2$ is reactive cleaning, with volatile O/C products that match the feed isotope. He is ion-induced release of retained H/D, drawn with its own arrow style;
- boronization names a “B-containing precursor” unless one is passed, and leaves a B-rich layer.
Species are examples. No temperature, precursor, pressure or thickness is built in. A temperature (in K or °C) or a thickness is drawn only when the caller passes it together with its source. The sequence ends in plasma operation; how the conditioned wall responds then is the plasma-wall interaction section.
vaft.diagram.wall_conditioning_baking() # temperature=, temperature_unit="K"|"degC", temperature_source=
vaft.diagram.wall_conditioning_gdc("D2") # "H2", "D2" or "He"
vaft.diagram.wall_conditioning_boronization(precursor="B$_2$H$_6$")
vaft.diagram.wall_conditioning_sequence(("baking", "D2_gdc", "He_gdc", "boronization"))
| Diagram | Concept |
|---|---|
wall_conditioning_baking |
External heat drives adsorbed water and gases off the wall into the pump |
wall_conditioning_gdc |
One glow-discharge template. H$_2$/D$_2$: O and C leave as volatile products. He: He$^+$ bombardment releases retained H/D |
wall_conditioning_boronization |
A B-containing precursor in a deposition plasma leaves a B-rich surface layer, a change of surface state rather than cleaning |
wall_conditioning_sequence |
The single stages in the caller’s order, each arrow a wall-state transition, ending in plasma operation. The order is not a recommended procedure |
Canonical turbulence concepts
Six concept figures built on the fluctuation-driven transport definitions in vaft.formula.turbulence. Geometry
is slab: $x$ radial (outward), $y$ binormal, $\mathbf B = B\hat z$. They explain mechanisms; none is a turbulence
model, a stability classifier or a transport prediction.
gradient -> omega_* -> drive and kinetic response -> gamma (if > 0) -> n~, phi~ -> v_E -> flux <n~ v_r>
flux -> profile relaxation (slow, transport time scale)
turbulence -> Reynolds stress -> d_r R != 0 -> zonal flow -> E x B shear --| turbulence (fast, at fixed gradients)
Two feedbacks are kept apart. Profile feedback is slow: the flux changes the gradients on the transport time scale, which is set by the particle and energy balance. Flow feedback is fast: zonal flows saturate the turbulence at fixed gradients, as in a flux-tube simulation.
drift_wave_transport_cycledraws that loop. A gradient is a possible free-energy source; whether a mode grows is decided by the instability drive and kinetic response, and $\omega_*$ is a frequency scale, not $\gamma$. Each step names its definition:- $\omega_* = \mathbf k\cdot\mathbf v_*$;
- $\mathbf v_E = \mathbf B\times\nabla\phi/B^2$ (it calls
particle.exb_drift_velocity); - $\Gamma = \langle\tilde n\tilde v_r\rangle$.
cross_phase_transportshows one mode at $\theta_{n\phi} = 0, \pm\pi/2$. Each flux comes fromcoherent_particle_flux_from_amplitudes_phaseand is checked against a direct average of $\tilde n\,\tilde v_x$. The convention ($\theta_{n\phi} = \alpha_\phi - \alpha_n$, real cosine fields, $x$ up, $y$ right, $\mathbf B$ into the page) is in the model and on the figure. It covers one Fourier component, and a zero particle flux does not imply a zero heat flux. Changing every convention consistently changes the intermediate signs, not the physical flux.mixing_length_saturation: growth $\gamma$ against an estimated decorrelation rate $k_\perp\tilde v$. Balancing them at $\tilde v = \gamma/k_\perp$ gives the phenomenological scalingmixing_length_diffusivity_from_gamma_k_perp. It is not a predictive saturation rule:- one mode’s $E\times B$ velocity does not advect its own potential ($\tilde{\mathbf v}_E\cdot\nabla\tilde\phi = 0$);
- saturation therefore needs coupling between modes (zonal flows, stable modes, spectral transfer);
- quasilinear models need a separate saturation rule (Staebler, Bourdelle, Citrin and Waltz, Nucl. Fusion 64 (2024) 103001);
- $D_\mathrm{ML}$ is not $D_n$ or $\chi$ of any channel.
exb_shear_decorrelation: a round eddy, and the same eddy after the shear map $y \to y + St\,x$.- The cut through its centre at fixed $y$ shrinks by $\sqrt{1 + (St)^2}$, while its total radial extent is unchanged. This is geometry, not the statistical correlation length; measured $E_r$ and shearing-rate profiles are plots.
- The decorrelation is a separate, following step: $k_x(t) = k_x(0) - Sk_yt$ (Biglari, Diamond and Terry, Phys. Fluids B 2 (1990) 1).
- $\gamma_E$ above $\gamma_\mathrm{lin}$ is a rule of thumb, not a universal threshold.
zonal_flow_feedback: the self-regulation loop with the Reynolds-stress and zonal-flow-drive definitions:- only the stress divergence $\partial_rR_{r\theta} \neq 0$ drives the flow;
- the shear changes the turbulence through mode coupling and decorrelation, which often but not always suppresses it;
- the damping $\mu$ is a stated choice, and no coefficient model is assumed;
- the slab has constant density, and a tokamak adds curvature, GAMs, collisional damping and Maxwell stress.
The loop follows Diamond et al., Plasma Phys. Control. Fusion 47 (2005) R35.
turbulence_scale_hierarchy: representative perpendicular scales $1/k_\perp$ against the lengths of thespatial_scale_orderingsstate:- ITG/TEM at $k_\perp\rho_i \sim 0.1$–$1$;
- ETG at $k_\perp\rho_e \sim 0.1$–$1$;
- a dashed TEM extension to $k_\perp\rho_s \sim 5$.
The conventions:
- the bands are conventions, not thresholds;
- the state is illustrative hydrogen, not a VEST measurement;
- it uses $v_t = \sqrt{T/m}$, and deuterium makes $\rho_i$ and $\rho_s$ larger by $\sqrt2$;
- the axis length is $1/k_\perp$, not the wavelength;
- the ITG/TEM and ETG bands are in $k_\perp\rho_i$ and $k_\perp\rho_e$;
- only the TEM extension uses the literature’s $k_y\rho_s$, taken as $k_\perp$ with $k_x = 0$, and it is one regime’s example, not a bound.
vaft.diagram.drift_wave_transport_cycle()
vaft.diagram.cross_phase_transport()
vaft.diagram.mixing_length_saturation()
vaft.diagram.exb_shear_decorrelation(shear=1.6)
vaft.diagram.zonal_flow_feedback()
vaft.diagram.turbulence_scale_hierarchy()
| Diagram | Concept |
|---|---|
drift_wave_transport_cycle |
Gradient, drift frequency, instability drive and kinetic response, growth (if $\gamma > 0$), fluctuations, $E\times B$ velocity and radial flux. Fast saturation at fixed gradients is kept apart from slow profile relaxation |
cross_phase_transport |
Zero flux in phase, outward at $+\pi/2$, inward at $-\pi/2$, with the sign convention stated |
mixing_length_saturation |
$\gamma$ against an estimated $k_\perp\tilde v$: the phenomenological scaling $D \sim \gamma/k_\perp^2$, not a predictive saturation rule |
exb_shear_decorrelation |
An unsheared and a sheared eddy: tilted, stretched, with a shorter fixed-$y$ cut (geometry), then the separate statistical decorrelation $k_x(t) = k_x(0) - Sk_yt$ |
zonal_flow_feedback |
Turbulence, Reynolds stress, zonal flow, shear, with the inhibition of the turbulence drawn without an arrowhead |
turbulence_scale_hierarchy |
Electron- and ion-scale bands and an open TEM range against the equilibrium lengths |
SOL blobs and filaments
A blob is a localized positive density (pressure) perturbation in the scrape-off layer, and a hole is a negative one. The filament is the field-aligned structure whose perpendicular cross-section is the blob.
How a blob moves:
- curvature and $\nabla B$ drift ions and electrons apart;
- the density monopole becomes a charge dipole, $+$ above and $-$ below;
- the dipole’s $E$ field drives an $E\times B$ drift outward on the low-field side, down $\nabla B$ (a hole moves inward).
How fast it moves depends on where the polarization current closes:
- along the field to the sheaths (sheath-connected, $v \propto \delta^{-2}$);
- across the field by ion inertia (inertial or resistive-ballooning, $v \propto \delta^{1/2}$);
- with resistivity and X-point fanning, in between.
vaft.formula.sol holds the prescribed filament state blob_density_perturbation (a blob, or a hole below
the background), the reference size $\delta_* = \rho_s^{4/5}L_\parallel^{2/5}/R^{1/5}$, the
reference velocity $v_$, the collisionality $\Lambda$, both limits, the interpolation, and the four regime
scalings. All follow D’Ippolito, Myra and Zweben (2011), with a Gaussian of radius $\delta$. The $O(1)$
prefactors in Krasheninnikov (2001) and Theiler et al. (2011) belong to their own size definitions. The NSTX
parameters of Myra et al. (2006) give $\hat a \approx 1.3$, as in their Fig. 1, and $v_ \approx 3$ km/s from their
symbolic Eq. (3); their text quotes $v_* \sim 2$ km/s. Cold ions throughout: $c_s = (T_e/m_i)^{1/2}$.
vaft.diagram.blob_polarization() # the mechanism: dipole, E, E x B; perturbation="hole" reverses it
vaft.diagram.blob_current_closure(regime="sheath") # or "inertial": where the current closes
vaft.diagram.blob_velocity_scaling() # v/v* against delta/delta*: both limits and Eq. (9)
vaft.diagram.blob_regimes(epsilon_x=0.1) # Lambda against Theta = delta_hat^{5/2}: RB, RX, C_i, C_s
The VAFT framework: pillars, research cycle, managed pipeline, provenance
These diagrams describe what VAFT is and how scientific data moves through it (#1090). They work at the level of scientific capabilities, not deployment: no storage backend, endpoint, path or module name appears in them, and a test enforces this. The README uses the four pillars, the research cycle and the managed pipeline.
vaft.diagram.vaft_four_pillars()
vaft.diagram.fusion_science_knowledge_lifecycle()
vaft.diagram.scientific_workflow()
vaft.diagram.interoperability_layers()
vaft.diagram.scientific_provenance_chain()
vaft.diagram.plasma_state_provenance()
vaft.diagram.scientific_infrastructure_principles()
vaft.diagram.machine_agnostic_architecture()
vaft.diagram.experiment_modeling_theory_data_network() # "point_to_point", "common_model", "equilibrium"
vaft.diagram.integrated_scientific_framework() # domain=None or "equilibrium"
vaft.diagram.human_ai_interface()
vaft.diagram.machine_research_archive()
| Diagram | Concept |
|---|---|
vaft_four_pillars |
Four capabilities at one level: the Standardized Data Interface, the Traceable & Reproducible Pipeline, the FAIR Scientific Data Repository and the Machine Knowledge Archive. They sit under the purpose (integrate fusion experiment, modelling, data and knowledge, so that plasma states are findable, comparable, reproducible and testable) and stand on the shared design principles. VEST is labelled as the reference implementation, not the foundation |
fusion_science_knowledge_lifecycle |
A research-learning cycle: Experiment → Machine Description & Raw Data → Data Processing & Qualification → Modelling & Analysis → Physical Interpretation → Comparison & Synthesis → Discovery & New Questions → Experiment. New questions return only to the experiment |
scientific_workflow |
A managed pipeline. Heterogeneous machine and experimental sources feed ingestion and orchestration, then diagnostic processing → equilibrium reconstruction and profile fitting → interpretive simulation, all reading and writing the standardized scientific state held in the Common Data Model (IMAS). Configuration and description, provenance and versioning, and V&V with quality assessment cut across it, and V&V feeds back to the configurations. The product is qualified, analysis-ready data |
interoperability_layers |
From machine to scientific workflows in both directions, through the native representation, validation/standardization, the Common Data Model (IMAS) and the IMAS database. Native artifacts are stored alongside the standard (the dashed path) |
scientific_provenance_chain |
An example tokamak analysis chain: raw signal → processed data → equilibrium reconstruction and profile fitting → derived physics quantities → analysis and visualization. Versioned inputs and configurations are kept apart from the cross-cutting quality metadata |
plasma_state_provenance |
The information layers of one plasma state (Tutorial 03, #1714): measured quantities ($I_p$, magnetics, diamagnetic flux, Thomson and charge-exchange profiles) and assumed priors (profile model, weights and uncertainties, $Z_\mathrm{eff}$, boundary conditions) both enter the reconstruction or fit, whose fields and profiles feed the derived descriptors ($\kappa$, $\delta$, $q_{95}$, $\beta$, $\ell_i$, pressures, $\nu^$, $\rho^$, $a/L_T$). A derived number is never itself a measurement |
scientific_infrastructure_principles |
Two foundations, both converging on VAFT. On one side are the common principles for modern scientific infrastructure (FAIR, W3C PROV, TRUST). On the other are three fusion-community requirements: verification and validation, integrated modelling and data analysis, and multi-machine comparison and extrapolation. Each side’s references, FAIR4RS among them, sit beneath it |
machine_agnostic_architecture |
Theory, experiment, modelling and simulation, and data-driven methods share one scientific framework and one Common Data Model (IMAS), which holds design, experimental and simulation data and is stored in the IMAS database. Machine-specific data access and mapping absorbs device differences, so the same architecture serves existing fusion experiments and future devices and reactor concepts. No device is named |
experiment_modeling_theory_data_network |
A three-step argument for a common data model. Point to point needs $N(N-1)/2$ pairwise adapters, and a new mode needs $N-1$ more. The Common Data Model (IMAS) needs $N$ adapters, and a new mode needs one. The IMAS equilibrium IDS, a standardized equilibrium representation, then serves as a tokamak example with representative routes and references |
integrated_scientific_framework |
The common-model network one level up (#1698). The same four research modes and Common Data Model (IMAS), placed exactly as in the network, sit inside an Integrated Framework boundary, and the shared state feeds one Analysis node. The Common Data Model is the shared representation; the framework connects, runs, compares and reproduces research through it; analysis is the scientific use. With domain="equilibrium" the routes and the IMAS equilibrium IDS return, and the analysis reads MHD parameters, plasma shape and operational space. These are equilibrium-derived descriptors; stability codes such as DCON and RDCON are downstream models and stay out |
human_ai_interface |
Three layers: actors, shared access interfaces and one backend. Human researchers and AI agents collaborate through the Python API, CLI, GUI, repository and docs, and MCP (planned). The interface layer reaches the framework and the IMAS database through one common connection |
machine_research_archive |
VEST’s institutional and scientific memory since 2012: machine history, research on VEST and research knowledge feed one living archive, which new analyses and research build on. No dates are drawn beyond the start of operation |
The four pillars, the research cycle, the managed pipeline and the human-AI interface carry semantic pictograms (#2101, #2109), one symbol per concept:
| Figure | Concept → symbol |
|---|---|
vaft_four_pillars |
interface → interface, pipeline → workflow, repository → database, archive → archive (a common row above each title) |
human_ai_interface |
human researchers → researcher, AI agents → ai_agent, Python API → code, CLI → terminal, GUI → monitor, repository & docs → repository, MCP → integration, muted and dashed because it is planned |
fusion_science_knowledge_lifecycle |
seven distinct symbols: experiment, signal, processing, computation, analysis, comparison, discovery |
scientific_workflow |
machine sources → tokamak; diagnostic processing → processing, reconstruction → reconstruction, interpretive simulation → computation, V&V → validation |
The symbols name a kind of activity or component only. They do not mark anything as implemented or validated, and the labels stay authoritative.
The interoperability figures read as one progression, from data interoperability through scientific integration to infrastructure and implementation. The four nodes of the network and framework figures are research modes, not physics domains; Research domains sets out how the two classifications cross:
- pairwise interfaces,
experiment_modeling_theory_data_network("point_to_point"); - a shared scientific representation,
experiment_modeling_theory_data_network("common_model"); - an integrated scientific framework,
integrated_scientific_framework(), and its equilibrium example,integrated_scientific_framework(domain="equilibrium"); - the broader research infrastructure,
experimental_research_infrastructure()(#1636); - the managed workflow,
scientific_workflow(); - the VEST implementation,
vest_data_platform().
The pillar names are the four README sections.
The diagrams are built from the concept primitives in vaft.diagram._concept: box, connector, band,
and database, a drum drawn as polylines. They use the concept … and connector … styles of the template.
Research infrastructure: four levels of integration, the community and ownership
Why VAFT is built the way it is, in four levels (#1641, #1636, #1638, #1640). Each level is a pair of figures
from one builder: organization="fragmented" draws the problem and organization="integrated" the
architecture that answers it. The pair grammar is shared:
- the fragmented figure draws research paths as dashed silos, joined only by red, dashed ad-hoc links (level 3 is one chain of steps instead, with the evidence each step loses beneath it);
- the integrated figure draws the same entities around the shared layer that replaces those links;
- both carry the level tag at the top left and numbered notes at the bottom, set as columns of text. The fragmented figure lists what goes wrong, each in plain words with its technical term and, where there is one, a reference: ^n plain words (technical term) [reference]. The integrated figure lists what answers each one, under the same number;
- the numbers are cited like footnotes, as a grey superscript after the text they belong to. In the figure they mark where a symptom arises and which part of the architecture answers it, and they run in the order the problem figure cites them.
vaft.diagram.scientific_representation("fragmented") # level 1, #1641
vaft.diagram.scientific_representation() # "integrated" is the default
vaft.diagram.experimental_research_infrastructure("fragmented") # level 2, #1636
vaft.diagram.scientific_credibility("fragmented") # level 3, #1638
vaft.diagram.research_modality_architecture("fragmented") # level 4, #1640
vaft.diagram.fusion_research_ecosystem() # #1643, "full" or "presentation"
vaft.diagram.scientific_ownership_architecture() # #1645
Read in order, the figures make one argument. A Common Data Model answers the representation problem
(experiment_modeling_theory_data_network). It is not enough on its own: a research infrastructure also
needs a FAIR repository and a research framework. Reproducibility is not enough either: a result also needs
traceable justification. The same science must then serve every researcher, interface and environment. The
managed pipeline is scientific_workflow, and the VEST implementation is vest_data_platform.
| Level | Question | Without | With |
|---|---|---|---|
| 1 Representation | What does this information mean? | One quantity held five ways, and each of four layers fragments on its own: the spoken term (“Ip”, “plasma current”), the stored name (a DAQ channel, a NetCDF variable, a struct field), the structure and the encoding. Stored names are mapped pairwise by hand | Format and structure mappings feed the Common Data Model (IMAS), which owns meaning. Taxonomy, strict aliases and discovery connect researchers and agents to it. Storage and access (local or remote; eager, lazy, partial, cached) sit beside it |
| 2 Infrastructure | How is a state produced, stored and reused? | Five silos, each running source → ad-hoc step → activity with private calibration copies; the reconstruction is copied by hand into other paths | Sources and activities meet only in three complementary capabilities: the Common Data Model, the FAIR Scientific Data Repository and the Research Framework |
| 3 Credibility | Why should this result be trusted for this use? | Locally reasonable steps lose evidence, ending in an apparently precise result. Twelve losses, each with a plain label and the literature term (parametric entanglement, fortuitous agreement, primacy hierarchy, domain of applicability, …) | Provenance, uncertainty and assumptions feed one credibility and traceability structure, which verification, validation and sensitivity examine. The assessment is a profile, not a score, and yields a qualified scientific state |
| 4 Modality & portability | Can every researcher, language and environment use the same science? | The science is copied into a GUI, a notebook, a MATLAB workflow and HPC scripts, and AI gets no capability interface | Sibling interfaces (Python, Jupyter, CLI, GUI, documentation, MCP for agents) sit over shared public APIs and one modular core. Below it are language and runtime interoperability (MATLAB and Julia bindings marked as future) and portable execution. The versioned lifecycle runs beside them |
The figures keep several distinctions visible, and the tests check them:
- HDF5 is a serialization format, not the scientific model. The level-1 figure shows it only as one encoding beside NetCDF, MATLAB files and native outputs, and names no storage service;
- provenance is not validity, numerical verification is not physical applicability, and a surrogate’s training domain is checked apart from its physics model;
- GUI, CLI and MCP are interfaces, never part of the core;
- external solvers keep their own platform limits.
fusion_research_ecosystem asks a different question: who does fusion research, what they do, and which
shared scientific states connect it (#1643). The "full" figure shows research roles a person may combine,
with graduate researchers and learners across every activity. Planning leads to a planned shot and a planned
simulation run. Experiment and simulation are parallel, epistemically distinct producers. Comparison,
validation and synthesis yield qualified states and feed new questions back to planning. Knowledge is
preserved and transferred, and the states serve generic research contexts; only the reference
implementation, VEST, is named. The "presentation" figure is a one-slide projection of the same model,
with shared scientific states feeding experimental planning and operation, physics interpretation and
discovery, and modelling, prediction and validation. Curved arrows show exchange between neighbouring uses;
they do not prescribe a fixed sequence. A test checks that its roles, verbs, states and uses map back to
the detailed figure’s roles, activities and states.
scientific_ownership_architecture shows where scientific logic lives as research software matures (#1645).
Research groups Studies by membership, not by execution order, and neither executes anything. A workflow or
notebook composes computation and matures reusable logic out of itself. Data and the database, and Formula,
Process, Code and learned models, produce results and evidence. Validation interprets that evidence, and an
optional use policy decides what a workflow does about it. The Actor contract is an optional overlay, off
every edge. The graduation rule promotes matured logic by meaning. Computational
layers is the zoomed view of the computation band, and
Scientific architecture uses this figure as VAFT’s
normative architecture.
Related issues: #1090 (the concept family), #1550 (the VEST workflow), #497 (placement of canonical visuals), #248, #252, #1505, #1626-#1629 (provenance, contracts and applicability), #1077, #1165, #1170, #1639, #1642, #669 (ownership, Study, Research, learned models), #1174, #1086, #188, #1423, #1002, #1012, #1013, #1016 (interfaces and runtimes).
References for the terminology in the level-3 and community figures:
- R. Fischer and A. Dinklage, Integrated data analysis of fusion diagnostics by means of the Bayesian probability theory, Rev. Sci. Instrum. 75, 4237 (2004): data inconsistency, parametric entanglement, diagnostic interdependencies, complex error propagation.
- P. W. Terry et al., Validation in fusion research: towards guidelines and best practices, Phys. Plasmas 15, 062503 (2008): qualification, fortuitous agreement, primacy hierarchy.
- M. Greenwald, Verification and validation for magnetic fusion, Phys. Plasmas 17, 058101 (2010).
- W3C, PROV-DM: the PROV data model, W3C Recommendation (2013).
- M. D. Wilkinson et al., The FAIR guiding principles for scientific data management and stewardship, Sci. Data 3, 160018 (2016).
- D. Lin et al., The TRUST principles for digital repositories, Sci. Data 7, 144 (2020).
- F. Imbeaux et al., Design and first applications of the ITER integrated modelling & analysis suite, Nucl. Fusion 55, 123006 (2015).
- ITER Organization, ITER Research Plan within the Staged Approach, ITR-18-03 (2018).
- ITER Physics Basis, Nucl. Fusion 39, 2137 (1999); Progress in the ITER Physics Basis, Nucl. Fusion 47, S1 (2007).
Both infrastructure figures mark each source with the same semantic pictogram
(#2109): DAQ signals → signal, machine database → database, diagnostic files → file,
machine geometry → tokamak, operation & maintenance records → history, calibration &
configuration → parameters. Only the integrated figure has the last one as a source, because the
fragmented figure keeps calibration as private copies.
The four concept figures embedded in the README and guides carry a few semantic pictograms (#2113), placed where they help a reader tell kinds of things apart and left out elsewhere:
| Figure | Concept → symbol | Deliberately without |
|---|---|---|
fusion_research_ecosystem(detail="presentation") |
experiment & engineering → tokamak, theory & modelling → model, data, AI & software → computation |
the full rendering, the activities, states and uses |
scientific_provenance_chain |
raw signal → signal, processed data → processing, reconstruction & profile fitting → reconstruction, derived quantities → calculation, analysis → analysis |
quality and versioned metadata: provenance is not validation |
external_code_integration |
upstream project → source_code, installation → package, adapter → adapter, local → laptop, Slurm → cluster, native result → output_file, standardized state → standardized |
obtain, provenance, environment resolution, mapping |
machine_research_archive |
machine history → tokamak, research on VEST → experiment, research knowledge → library |
individual entries |
A symbol never claims that a capability is implemented or a product is validated. The upstream versus VAFT ownership brackets, the execution routes and the mapping step keep their labels.
The content is the data at the top of each section of vaft.diagram._research_concepts (INFRA_*,
CREDIBILITY_*, MODALITY_*, REPRESENTATION_*, ECOSYSTEM_*, PRESENTATION_*, OWNERSHIP_*), so a
figure is changed by editing data.
The VEST data platform
The VEST data platform as a database-centred scientific workflow (#1550), laid out as a cross about the
database. The VEST machine, a CAD render packaged with vaft.diagram and embedded in the SVG, sits outside
the platform server and feeds experimental data processing
(the render). A per-shot directory is the hub: reconstruction and physics inference (above) and simulation
(right) read from it and write back to it. Users reach it from below, and the whole runs on Windows,
macOS and Linux, locally or on an HPC cluster. The content is declared in vaft.diagram._platform
(EXPERIMENTAL_PROCESSING, DATABASE_LAYOUT, RECONSTRUCTION, DERIVED_PHYSICS, SIMULATION, ACCESS,
EXECUTION_*), so the figure is updated by editing data. The database technology appears once, as a muted
caption under its title (DATABASE_TECHNOLOGY: IMAS · HDF5 · HSDS); no other backend or workflow-engine names
are drawn.
vaft.diagram.vest_data_platform() # the reference architecture view
vaft.diagram.vest_data_platform_overview() # five stages, for papers and slides
| Area | Content |
|---|---|
| Experimental data processing | Machine Model & History, Signal Processing, Quality & Validation, Fault & Anomaly Detection, Shot Classification, Event Detection |
| Database | {shot}/: master.h5; experimental files; reconstructed state; physics products; each group open-ended |
| Reconstruction & physics inference | Reconstruction (Eddy Current Model, Magnetic EFIT, Profile Fitting, Plasma Parameter Inference, Kinetic EFIT); Derived Physics (Vacuum Field Proxies, MHD Parameters, Synthetic Diagnostics, Coordinate Conversion, Power Balance) |
| Simulation | Each entry is the concept, with its code or model authors beneath. Equilibrium: Fixed Boundary (CHEASE), Free Boundary (TokaMaker), Analytic GS (Solov’ev · Guazzotto & Freidberg). Stability: Ideal (DCON), Resistive (RDCON). 3D Response & Topology: Plasma Response (GPEC), Field-Line Following (FLARE). Transport: Classical (Braginskii), Neoclassical (NEO / Sauter & Redl), Turbulent (TGLF / CGYRO) |
| Access & analysis | Python API, CLI, GUI, MCP, Documentation; Data Access · Search · Visualization · Comparison · Statistics · Export · Tutorials · Research Archive |
Integrated modeling: knowledge basis, realization, abstraction
A single “analytic / numerical / empirical / data-driven” list mixes three independent questions. These diagrams keep them apart (#1085):
- Knowledge basis: where does the model’s knowledge come from, from first-principles to data-driven?
- Computational realization: how is the model evaluated, from analytical to fully numerical?
- Physical abstraction: at what description level is the system represented, from particle orbits to static equilibrium?
The diagrams then combine the three axes into one space and connect models by typed couplings. They show location and character, not ranking: neither end of an axis is better.
vaft.diagram.knowledge_basis()
vaft.diagram.computational_realization()
vaft.diagram.physical_abstraction()
vaft.diagram.integrated_modeling_space() # "fusion", "tearing"
vaft.diagram.integrated_modeling_process()
| Diagram | Concept |
|---|---|
knowledge_basis |
Mechanistic/first-principles, semi-empirical/closure, empirical, data-driven. Physics-informed models (physical constraints with fitted or learned parts) are a bridge over the axis, not one point on it |
computational_realization |
Analytical, semi-analytical, reduced numerical, fully numerical. Learned surrogates are not a rung: they are placed by knowledge basis and role |
physical_abstraction |
Particle/orbit, kinetic, moment/fluid, MHD, equilibrium/static. Each arrow names its reduction: ensemble average, velocity moments + closure, single fluid at low frequency, stationary force balance |
integrated_modeling_space |
Knowledge basis across, realization up. The physical abstraction is shown by the fill colour of each model and repeated in its border pattern, so it survives grayscale. Conceptual and heuristic models (physical picture, cartoon, scaling argument, toy model) are an explanatory layer beside the space, not a fourth axis |
integrated_modeling_process |
Experiment, measurement, processing, inverse model (parameter inference), physical state, forward and data-driven models, prediction, and validation/control, with the eight coupling types of the legend |
Semi-empirical is a knowledge basis: a physical form with fitted coefficients. Semi-analytical is a computational realization: an asymptotic expansion, a Green-function reduction or a quadrature of a closed form. The words look alike but belong to different axes.
Positions are semantic, not layout. Models with the same knowledge basis share an x and are separated in y. A learned model is not a realization rung. A surrogate takes the y of the model it emulates and is linked to it; a model that emulates nothing, such as a classifier, sits at the rung of its evaluation.
The "fusion" overlay places familiar codes, from Solov’ev, EFIT and CHEASE to TGLF, TRANSP and ASCOT5, with an
empirical confinement scaling, a neural-operator surrogate of TGLF and an event classifier. These placements are
illustrative, not a classification:
- EFIT is an inverse model: it solves the Grad–Shafranov equation numerically inside a fit to measurements.
- TRANSP is mainly used interpretively, so it is also an inverse model.
- DCON integrates the Newcomb ODE, which is a reduced numerical method.
- TGLF is gyro-Landau-fluid approximating gyrokinetics, with a saturation rule fitted to nonlinear gyrokinetic
runs, so it sits at semi-empirical/closure. The
"tearing"variant follows one phenomenon from an island picture in the explanatory layer, through the Rutherford equation, to a nonlinear resistive-MHD simulation.
The coupling types are data flow, closure, parameter inference/calibration, surrogate replacement, residual correction, validation/benchmarking, feedback/control and iterative coupling. A coupling is a label on an edge. The data-driven model’s three uses are alternatives: a closure inside the forward model, or a surrogate replacement of it, or a residual correction of its prediction (the hybrid mode). Validation compares the prediction with the processed measurement, and control acts on the experiment.
The vocabulary and the example placements are data in vaft.diagram._modeling_schema: the axis stations,
ModelDescriptor, ModelCoupling and the coupling types. They are kept apart from the drawing so that
documentation and provenance tooling can reuse them. They are not a stable public API yet.
Spatial vocabulary: coordinates, geometry, meshes, mappings and topology
Concept-oriented schematics of the spatial ideas VAFT works with, organised by the concept rather than
by a code: a structured grid or a logical mapping is drawn as the numerical idea several equilibrium,
transport and MHD codes share. They need no shot data; vaft.plot draws a particular equilibrium,
mesh or field. vaft.diagram._spatial.SPATIAL_FAMILIES files every builder below by family.
vaft.diagram.tokamak_top_view(cocos=11)
vaft.diagram.cocos_orientation(11) # one panel
vaft.diagram.cocos_orientation(range(1, 9)) # one panel per index, one scale
vaft.diagram.machine_and_equilibrium_geometry()
vaft.diagram.structured_rz_grid(n_r=13, n_z=21)
vaft.diagram.geometry_to_mesh()
vaft.diagram.logical_to_physical_mapping()
vaft.diagram.physical_to_flux_mapping()
| Family | Concept | Builder |
|---|---|---|
| coordinate | cylindrical $(R, \phi, Z)$ and toroidal $(r, \theta, \phi)$ | tokamak_torus, tokamak_top_view |
| coordinate | COCOS orientation, generic: $\phi$, $B_\phi$ and $I_p$ (on the magnetic axis) in or out of the page, the sense of $\theta$, the direction $\psi$ increases and its unit; titled by the index and $(\sigma_{B_p}, \sigma_{R\phi Z}, \sigma_{\rho\theta\phi}, \psi)$ | cocos_orientation (signs from vaft.data.cocos.cocos_spec); coordinate choice against COCOS: coordinates_vs_cocos |
| coordinate | flux coordinates $(\psi, \theta, \phi)$ and poloidal angles | flux_coordinates, poloidal_angle_comparison |
| geometry | machine geometry (wall, limiter, coils, passive structure) against equilibrium geometry (axis, surfaces, separatrix, X-point) | machine_and_equilibrium_geometry |
| geometry | limited and diverted equilibria; shaping | limiter_and_diverted_topologies, shaping_family |
| mesh | structured $(R, Z)$ grid with the plasma boundary between nodes | structured_rz_grid |
| mesh | unstructured mesh with region-dependent resolution | geometry_to_mesh |
| mesh | flux-aligned grid; logical $(\xi, \eta) \mapsto (R, Z)$ | logical_to_physical_mapping, sfl_coordinate_grids |
| mapping | measurement at $(R, Z)$ → $\psi$ → $\psi_N$ → $\rho_{\mathrm{tor},N}$ → profile coordinate | physical_to_flux_mapping |
| mapping | geometry → regions → mesh | geometry_to_mesh |
| topology | nested flux surfaces | flux_surfaces |
| topology | X-point and separatrix | x_point, separatrix_lobes |
| topology | magnetic island, stochastic layer | magnetic_island, stochastic_layer |
Conventions shared by every figure: $R$ to the right and $Z$ up in the poloidal plane; $\odot$ out of and
$\otimes$ into the page; the magnetic axis is a filled blue dot, an X-point a cross; the LCFS a black
line, the separatrix blue; the computational boundary dashed; the machine (vessel, limiter, coils) in
black outline, faint where it is shown only for reference; regions shaded plasma blue, vacuum green,
conductor grey; measurements red (outboard) and orange (inboard). The machine and the flux are the toy
free-boundary model of the Grad–Shafranov diagrams, not any device. The sense of $\theta$ differs on
purpose: the cocos_orientation panels draw it as the index fixes it (COCOS 11, $\sigma_{R\phi Z} = +1$
and $\sigma_{\rho\theta\phi} = +1$ with $\phi$ into the page, puts $\theta$ clockwise in the $(R, Z)$ plane
as drawn), while the torus, mesh and mapping figures (tokamak_torus, logical_to_physical_mapping,
sfl_coordinate_grids) are convention-free sketches that use the mathematical angle, counter-clockwise
from the outboard midplane.
Physics workflows: derivation, inference and solver coupling
The level-2 view below the platform overview (#1585). Each figure is drawn from one declarative
WorkflowSpec (vaft.diagram._workflow); the same record produces the figure and the table under it, and
the tests require every API and equation it names to exist on this tree. The figures follow the current
implementation: where VAFT does not yet implement the physically complete step, or has no IMAS mapping, the
node carries a red IMAS mapping TODO and the workflow lists its follow-up TODOs instead of drawing an
unsupported capability. Node style says how a quantity was obtained:
| Kind | Legend symbol | Meaning |
|---|---|---|
| measured | signal |
experimentally observed |
| reconstructed | reconstruction |
the solution of an inverse problem |
| derived | calculation |
computed deterministically from existing state |
| inferred | inference |
estimated with a model, prior or closure |
| synthetic (assumption-completed) | synthetic |
completed from an equilibrium and explicit assumptions with no kinetic data; never a measurement |
| model assumption / prior | parameters |
an assumed value or prior the result depends on; drawn entering from the side |
| model choice / convention | coordinates |
a model choice or convention (basis, sign, conductivity model, mode numbers); drawn from the side |
| machine geometry / static data | tokamak |
static machine data: geometry, Green tables, circuits; drawn from the side |
| code input | input_file |
a code-specific projection of the canonical state |
| solver / model | computation |
an external or internal physical model |
| native result | output_file |
the solver’s own output, before any mapping |
| standardized IMAS | standardized |
the IMAS / OMAS representation VAFT stores |
Each legend entry draws the kind’s semantic pictogram inside its colour swatch (#2114),
followed by its label. The swatch and label stay authoritative, and the symbol never encodes implementation
status: a computation symbol on solver / model does not mean the solver runs, and standardized
does not mean a product passed validation. The symbols appear only in the legend, not inside the nodes.
Node-level icons were compared on magnetic_efit, kinetic_efit, plasma_parameter_inference,
tokamaker_coupling and tglf_cgyro_local_transport: they crowded the bold titles and equations
(Measurement constraints, the side inputs) and repeated what the node’s style already says, so they
were not adopted.
Inputs and outputs carry their representative variables under the label, and standardized nodes their
IMAS path. A step’s equation is drawn inside it: the vaft.formula catalog definition (formula_equation)
where one exists, otherwise a relation restating the expression the named API implements.
vaft.diagram.plasma_parameter_inference()
vaft.diagram.romero_transformer_balance()
vaft.diagram.resistive_zeff_inference()
vaft.diagram.magnetic_efit()
vaft.diagram.kinetic_efit()
vaft.diagram.analytic_mhd_equilibrium()
vaft.diagram.chease_coupling()
vaft.diagram.tokamaker_coupling()
vaft.diagram.dcon_rdcon_stability()
vaft.diagram.gpec_plasma_response()
vaft.diagram.flare_field_line_topology()
vaft.diagram.neo_neoclassical()
vaft.diagram.tglf_cgyro_local_transport()
vaft.diagram.parameter_inference_overview()
vaft.diagram.parameter_inference_dependency_graph()
vaft.diagram.eddy_current_model()
vaft.diagram.kinetic_profile_fitting()
vaft.diagram.transport_hierarchy()
vaft.diagram.neoclassical_model_comparison()
vaft.diagram.machine_model_history()
vaft.diagram.signal_processing()
vaft.diagram.quality_validation()
vaft.diagram.fault_anomaly_detection()
vaft.diagram.shot_classification()
vaft.diagram.event_detection()
vaft.diagram.vacuum_field_proxies()
vaft.diagram.derived_mhd_parameters()
vaft.diagram.synthetic_diagnostics()
vaft.diagram.coordinate_cocos_conversions()
vaft.diagram.power_balance_confinement()
Architecture coverage: every science box of the platform figure
Each science box of the platform figure (vest_data_platform, #1550) maps to the workflow diagrams that
draw it, or to a tracked gap. The table is generated from vaft.diagram._architecture_coverage, which reads
the boxes from vaft.diagram._platform; a test keeps it equal to the figure and requires every named diagram
to exist.
| Area | Box | Detailed diagram | Tracked gaps |
|---|---|---|---|
| Experimental data processing | Machine Model & History | machine_model_history |
no single shot-to-machine object; VEST COCOS index unresolved |
| Experimental data processing | Signal Processing | signal_processing |
no shared drift-correction, synchronization or integration stage |
| Experimental data processing | Quality & Validation | quality_validation |
only source validity reaches IMAS |
| Experimental data processing | Fault & Anomaly Detection | fault_anomaly_detection |
no general anomaly model runs in the pipeline |
| Experimental data processing | Shot Classification | shot_classification |
the four-way physics class is not implemented (#57); no disruption labels |
| Experimental data processing | Event Detection | event_detection |
no breakdown, flat-top, IRE, sawtooth or disruption detector |
| Reconstruction | Eddy Current Model | eddy_current_model |
– |
| Reconstruction | Magnetic EFIT | magnetic_efit |
– |
| Reconstruction | Profile Fitting | kinetic_profile_fitting |
no joint multi-diagnostic fit (#1096) |
| Reconstruction | Plasma Parameter Inference | plasma_parameter_inference, parameter_inference_overview, parameter_inference_dependency_graph, romero_transformer_balance, resistive_zeff_inference |
E_r and gamma_E not inferred: gamma_E = 0 is an assumption |
| Reconstruction | Kinetic EFIT | kinetic_efit |
– |
| Derived physics | Vacuum Field Proxies | vacuum_field_proxies |
vacuum products are returned, not written to IMAS (no issue yet) |
| Derived physics | MHD Parameters | derived_mhd_parameters |
two poloidal-beta definitions (#318); corrupt vacuum_toroidal_field.r0 (#325) |
| Derived physics | Synthetic Diagnostics | synthetic_diagnostics |
no synthetic Thomson, interferometer or camera (#2046); no IMAS storage of synthetic signals (#2053) |
| Derived physics | Coordinate Conversion | coordinate_cocos_conversions |
the stored flux-surface grid is geometric, not straight-field-line (#472) |
| Derived physics | Power Balance | power_balance_confinement |
no measured P_rad (no bolometer); nothing written to the summary IDS (#1060, #1905) |
| Simulation | Equilibrium: Fixed Boundary (CHEASE) | chease_coupling |
– |
| Simulation | Equilibrium: Free Boundary (TokaMaker) | tokamaker_coupling |
– |
| Simulation | Equilibrium: Analytic GS (Solov’ev · Guazzotto & Freidberg) | analytic_mhd_equilibrium |
– |
| Simulation | Stability: Ideal (DCON) | dcon_rdcon_stability |
– |
| Simulation | Stability: Resistive (RDCON) | dcon_rdcon_stability |
– |
| Simulation | 3D Response & Topology: Plasma Response (GPEC) | gpec_plasma_response |
– |
| Simulation | 3D Response & Topology: Field-Line Following (FLARE) | flare_field_line_topology |
– |
| Simulation | Transport: Classical (Braginskii) | transport_hierarchy |
classical transport has a private identifier.index |
| Simulation | Transport: Neoclassical (NEO / Sauter & Redl) | transport_hierarchy, neo_neoclassical, neoclassical_model_comparison |
Sauter / Redl results have no core_transport writer |
| Simulation | Transport: Turbulent (TGLF / CGYRO) | transport_hierarchy, tglf_cgyro_local_transport |
the TGLF-NN surrogate has no IMAS writer |
Plasma parameter inference: ion temperature and species
Measured electron profiles and the equilibrium resolved into an ion temperature, a species mix and the local quantities transport codes read. Each ion-temperature branch is kept apart, and the resolved state is held in memory (ResolvedTransportState), not written back to IMAS.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Electron profiles | measured | $T_e(\rho),\ n_e(\rho)$ | core_profiles.profiles_1d[:].electrons.{temperature, density_thermal} |
|
| Equilibrium | reconstructed | $p_{\mathrm{eq}}(\psi),\ r(\psi)$ | equilibrium.time_slice[:].profiles_1d.{pressure, r_inboard, r_outboard} |
|
| Measured ion temperature (CX) | measured | $T_i^{\mathrm{CX}}(\rho)$ | core_profiles.profiles_1d[:].ion[:].temperature |
|
| Pressure-partition ion temperature | inferred | vaft.process.kinetic_closure.infer_ti_pressure_partition |
||
| Temperature ratio | model assumption / prior | vaft.machine_mapping.core_profiles.vest_core_profiles_policy |
||
| Ion-temperature branch selection | derived | $\mathrm{measured} \succ \mathrm{pressure\ partition} \succ \mathrm{ratio}$ | vaft.process.transport_state.resolve_transport_state |
|
| Zeff and one impurity species | model assumption / prior (enters Species resolution) | $Z_{\mathrm{eff}},\ Z_I\ (\mathrm{default\ C})$ | core_profiles.profiles_1d[:].zeff |
|
| Species resolution | derived | vaft.code.gacode.inputs.impurity_fractions |
||
| Normalized gradients | derived | vaft.code.gacode.tglf.prepare_tglf_input |
||
| Electron collision rate | derived | vaft.code.gacode.tglf.prepare_tglf_input |
||
| Resolved local state | derived | $T_e,\ T_i,\ n_e,\ n_H,\ n_I,\ a/L_{T,n},\ \hat\nu_{ee}$ | vaft.process.transport_state.resolve_transport_state |
IMAS mapping TODO: in memory only; not written to core_profiles |
Follow-up TODOs (implementation or IMAS mapping):
- The resolved state (T_i choice, n_H, n_I) is not written back to core_profiles.
- Normalized gradients and the GACODE collision rate are computed inside the TGLF input projection, not as vaft.formula definitions.
Plasma resistance from Romero’s transformer balance
The reconstructed boundary flux, plasma current and internal inductance close Romero’s voltage balance; what the inductive part does not explain is the resistive voltage, from which the plasma resistance follows.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Reconstructed equilibrium | reconstructed | $I_p(t),\ \psi_B(t),\ l_{i,3}(t)$ | equilibrium.time_slice[:].global_quantities.{ip, psi_boundary, psi_axis, li_3} |
|
| Romero convention: full flux | model choice / convention (enters Loop voltages) | $V = -\dot\psi,\ \psi\ \mathrm{in\ Wb}, \mathrm{sign\ from}\ (\psi_a - \psi_B)I_p$ | ||
| Internal inductance and equilibrium flux | derived | vaft.omas.process_wrapper.compute_romero_flux_balance_ods |
||
| Loop voltages | derived | vaft.process.equilibrium.romero_flux_balance |
||
| Non-inductive current | model assumption / prior (enters Plasma resistance) | $I_{\mathrm{ni}}\ (0\ \mathrm{stated\ for\ Ohmic})$ | ||
| Resistive voltage | derived | $V_R = V_B - V_I$ | ||
| Plasma resistance | inferred | $R_p = V_R/(I_p - I_{\mathrm{ni}})$ | vaft.omas.process_wrapper.compute_romero_flux_balance_ods |
IMAS mapping TODO: returned dict only; no IDS path |
Follow-up TODOs (implementation or IMAS mapping):
- psi_C is formed as psi_B + L_i I_p from li_3; the current-weighted integral (transformer.current_weighted_flux_from_psi_j_dS) exists but is not used here.
- No IMAS storage for V_B, V_I, V_R or R_p (e.g. summary.global_quantities.v_loop).
Resistively equivalent effective charge
One scalar Zeff over a time window: the effective charge a chosen parallel-conductivity model needs to reproduce the plasma resistance observed through Romero’s balance. It is a model-inferred, resistively equivalent value, not a measured or radially resolved Zeff(rho).
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Electron profiles | measured | $T_e(\rho),\ n_e(\rho)$ | core_profiles.profiles_1d[:].electrons.{temperature, density} |
|
| Flux-surface geometry | reconstructed | $\langle J!\cdot!B\rangle,\ \langle B^2\rangle,\ V(\psi),\ f_t$ | equilibrium.time_slice[:].profiles_1d.{gm5, volume, f, trapped_fraction} |
|
| Observed resistive voltage | inferred | $V_R^{\mathrm{obs}}(t),\ R_p^{\mathrm{obs}}(t)\ \ (\mathrm{Romero\ balance})$ | vaft.process.resistive_zeff.observed_resistance |
|
| Conductivity model | model choice / convention (enters Parallel conductivity) | $\mathrm{spitzer_nrl \mid sauter_spitzer \mid sauter \mid redl}$ | ||
| Coulomb logarithm prescription | model choice / convention (enters Parallel conductivity) | $\ln\Lambda\ \mathrm{fixed\ or\ Sauter}$ | ||
| Parallel conductivity | derived | $\sigma_\parallel(\rho; Z)$ | vaft.process.resistive_zeff.parallel_conductivity |
|
| Model resistance | derived | vaft.process.resistive_zeff.model_resistance |
||
| Fit bounds and weights | model assumption / prior (enters Bounded scalar fit) | $Z_{\min} \le Z \le Z_{\max}\ (\mathrm{caller\ set}),\ \ w_t\ \mathrm{uniform}$ | ||
| Bounded scalar fit | derived | vaft.process.resistive_zeff.infer_resistive_zeff |
||
| Resistive Zeff (scalar) | inferred | $Z_{\mathrm{eff}}^{\mathrm{res}} \pm \sigma_Z, \mathrm{residual\ rms},\ \mathrm{bound\ hit}$ | IMAS mapping TODO: CSV/JSON product; core_profiles.zeff untouched |
Follow-up TODOs (implementation or IMAS mapping):
- The observed resistance carries no propagated uncertainty (smoothing ‘none’ by default); the Zeff uncertainty is the fit’s sqrt(J/(n-1)/sum w (dV/dZ)^2).
- No IMAS mapping for the scalar resistive Zeff.
Magnetic equilibrium reconstruction (EFIT)
A free-boundary Grad-Shafranov inverse problem: magnetic measurements with their uncertainties, modelled vessel currents and the machine’s Green-function tables constrain a low-order p’ and FF’ basis. EFIT fits; it does not forward-solve.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| PF coil currents | measured | $I_{\mathrm{PF},j}(t)$ | pf_active.coil[:].current.data |
|
| Magnetic diagnostics | measured | $I_p,\ B_{p,k},\ \psi_{\mathrm{FL},k},\ \Phi_{\mathrm{dia}}$ | magnetics.{ip, b_field_pol_probe, flux_loop, diamagnetic_flux} |
|
| Vessel circuit model | machine geometry / static data (enters Vessel eddy currents (modelled)) | $R,\ L,\ M\ \mathrm{of\ the\ passive\ loops}$ | ||
| Vessel eddy currents (modelled) | derived | vaft.process.electromagnetics.solve_eddy_currents |
pf_passive.loop[:].current |
|
| Weights, uncertainty floor, exclusions | model assumption / prior (enters Measurement constraints) | $\sigma_k = \max(\sigma_k^{\mathrm{meas}}/s_g,\ 0.02\,\mathrm{median}|y|), \mathrm{diagonal\ weights}$ | ||
| Measurement constraints | code input | $y_k \pm \sigma_k,\ \ w_k \in {0, 1}$ | vaft.code.efit.generate_constraints_ods |
|
| Profile basis | model choice / convention (enters k-file) | $p’(\psi): 2,\ FF’(\psi): 1\ \mathrm{polynomial\ terms}, \mathrm{zero\ at\ the\ edge}$ | ||
| Green tables, limiter | machine geometry / static data (enters EFIT inverse solve) | $G(R,Z;R’,Z’),\ \mathrm{limiter\ (free\ boundary)}$ | ||
| k-file | code input | vaft.code.efit.prepare_efit_inputs |
||
| EFIT inverse solve | solver / model | vaft.code.efit.run_efit |
||
| g-, a-, m-files | native result | $\psi(R,Z),\ p’(\psi),\ FF’(\psi),\ \chi^2$ | vaft.code.efit.collect_efit_outputs |
|
| Equilibrium | standardized IMAS | $\psi,\ q,\ p,\ \beta_p,\ l_i$ | vaft.code.efit.gfile_to_omas |
equilibrium.time_slice[:].{profiles_1d, profiles_2d, boundary, global_quantities} |
Follow-up TODOs (implementation or IMAS mapping):
- Vessel currents are modelled from a circuit driven by measured PF currents and I_p filaments, not fitted (IFITVS=0 by default); no flux loop constrains them.
- Measurement weighting is diagonal; no covariance enters EFIT.
- Native k/g/a/m files are recorded only by path and hash under equilibrium.code.parameters.
Kinetically constrained equilibrium reconstruction
The magnetic constraints plus a kinetic pressure profile: Thomson T_e, n_e and a measured or ratio-assumed T_i give pressure points with propagated uncertainty, placed in real space so EFIT maps them to psi on its own solution.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Thomson scattering | measured | $T_e \pm \sigma_{T_e},\ n_e \pm \sigma_{n_e}\ \mathrm{at}\ R_k$ | thomson_scattering.channel[:].{t_e, n_e, position.r} |
|
| Ion temperature | measured | $T_i \pm \sigma_{T_i}, \mathrm{or}\ T_i = rT_e,\ r \pm \sigma_r\ \mathrm{from\ machine\ policy}$ | charge_exchange.channel[:].ion[0].t_i |
|
| Major radius to normalized flux | derived | $\psi_N(R, Z{=}0)\ \mathrm{for\ the}\ T_i\ \mathrm{fit}$ | equilibrium.time_slice[:].profiles_2d[0].psi |
|
| Kinetic pressure points | derived | vaft.code.efit.kinetic_pressure_points |
||
| Minimum pressure uncertainty | model assumption / prior (enters Kinetic pressure points) | $\sigma_p \ge 0.05\,p$ | ||
| Magnetic constraints | code input | $y_k \pm \sigma_k$ | vaft.code.efit.generate_constraints_ods |
|
| k-file with pressure block | code input | $\mathrm{KPRFIT}=1:\ (R_k, 0, p_k, \sigma_{p,k}), \mathrm{separatrix}\ p = 0 \pm 0.05\,p_{\max}$ | vaft.code.efit.inject_pressure_constraint |
|
| EFIT inverse solve | solver / model | vaft.code.efit.run_kinetic_efit |
||
| Kinetic equilibrium | standardized IMAS | $\psi,\ p(\psi),\ q$ | vaft.code.efit.run_kinetic_chain |
equilibrium.time_slice[:].{profiles_1d.pressure, profiles_2d} |
Follow-up TODOs (implementation or IMAS mapping):
- Pressure assumes one ion species with n_i = n_e: no impurity dilution or Zeff enters p_kin.
- The pressure points and the Ip scale run_kinetic_efit settles on are not stored in IMAS (manifest only); the work directory with the k-file is deleted.
- Single pass against the magnetic equilibrium: no kinetic <-> equilibrium iteration.
Analytic MHD equilibrium models
Two uses of closed-form Grad-Shafranov solutions. Forward generation builds an equilibrium from shape parameters and a profile class; fitting projects a reconstructed equilibrium onto a Solov’ev basis and reports how well it is represented.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Shape and topology | model assumption / prior | $R_0,\ a,\ \kappa,\ \delta,\ \mathrm{limited\ /\ single\ /\ double\ null}$ | ||
| Reconstructed equilibrium | reconstructed | $\psi(R,Z)\ \mathrm{inside\ the\ LCFS}$ | equilibrium.time_slice[:].profiles_2d[0].psi |
|
| Solov’ev / Cerfon-Freidberg | solver / model | vaft.process.solve_solovev_constraints |
||
| Guazzotto-Freidberg | solver / model | vaft.process.solve_guazzotto_freidberg |
||
| Solov’ev basis | model choice / convention (enters Least-squares projection) | $\mathrm{classic}\ 5 \mid \mathrm{CF\ even}\ 7, \mathrm{CF}\ 12\ \mathrm{terms}$ | ||
| Least-squares projection | derived | vaft.process.fit_solovev |
||
| Analytic flux | native result | $\psi(R,Z),\ c_k$ | ||
| Fit fidelity | native result | $\psi_{\mathrm{rms}},\ \mathrm{boundary\ rms},\ \mathrm{topology\ match}$ | ||
| Equilibrium | standardized IMAS | $\psi,\ q,\ p$ | vaft.data.eqdsk.to_omas |
equilibrium.time_slice[:].{profiles_1d, profiles_2d} |
Follow-up TODOs (implementation or IMAS mapping):
- No direct ODS writer: forward results reach IMAS through EquilibriumData -> GEQDSK -> to_omas.
- Fit results (SolovevFit) are not written to IMAS.
Fixed-boundary equilibrium refinement (CHEASE)
A reconstructed boundary and profiles re-solved at fixed boundary; the COCOS transform into and out of CHEASE is explicit.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Reconstructed equilibrium | reconstructed | $R_b,\ Z_b,\ p’(\psi),\ FF’(\psi)$ | equilibrium.time_slice[:].{boundary.outline, profiles_1d} |
|
| COCOS transform | model choice / convention (enters EXPEQ, namelist) | $\mathrm{COCOS}\ 11 \to 2 \to 11$ | ||
| EXPEQ, namelist | code input | vaft.code.chease.prepare_chease_inputs |
||
| CHEASE fixed-boundary solve | solver / model | vaft.code.chease.run_chease |
||
| EQDSK, output files | native result | $\psi(R,Z),\ q(\psi)$ | vaft.code.chease.collect_chease_outputs |
|
| Refined equilibrium | standardized IMAS | $\psi,\ q,\ \langle\cdot\rangle_\psi$ | vaft.code.chease.refine_equilibrium |
equilibrium.time_slice[:].{profiles_1d, profiles_2d} |
Free-boundary equilibrium (TokaMaker)
Machine geometry, measured coil currents and power-law profile shapes solved at free boundary on a finite-element mesh; the plasma boundary is part of the solution.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Coil currents | measured | $I_{\mathrm{PF},j}$ | pf_active.coil[:].current.data |
|
| Plasma current and vacuum field | measured | $I_p,\ F_0 = R_0 B_0$ | {equilibrium | magnetics}.ip; tf |
|
| Wall, coils, vessel | machine geometry / static data (enters TokaMaker inputs) | {wall, pf_active.coil, pf_passive.loop} |
||
| TokaMaker inputs | code input | vaft.code.tokamaker.prepare_tokamaker_inputs |
||
| Finite-element mesh | code input | vaft.code.tokamaker.build_tokamaker_mesh |
||
| Profile shape | model assumption / prior (enters TokaMaker free-boundary solve) | $p’,\ FF’ \propto (1-\hat\psi^{\alpha_a})^{\alpha_b},\ \ p_{\mathrm{ax}},\ I_{FF’}/I_{p’}$ | ||
| TokaMaker free-boundary solve | solver / model | vaft.code.tokamaker.run_tokamaker |
||
| Flux, boundary, statistics | native result | $\psi(R,Z),\ R_b,\ Z_b$ | vaft.code.tokamaker.collect_tokamaker_outputs |
|
| Equilibrium | standardized IMAS | $\psi,\ q,\ \beta_p$ | equilibrium.time_slice[:].{profiles_1d, profiles_2d, boundary} |
Ideal and resistive MHD stability (DCON / RDCON)
An equilibrium tested for ideal stability (DCON energy principle) and for tearing (RDCON matching at the rational surfaces); the solver-native energies and Delta-prime come before any verdict.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Equilibrium | reconstructed | $\psi,\ q(\psi),\ p(\psi)$ | equilibrium.time_slice[:] |
|
| Toroidal modes and flux range | model choice / convention (enters DCON / RDCON inputs) | $n = 1, 2,\ \ \psi_N \in [0.01, 0.994], \Delta m = 8\ (\mathrm{RDCON}\ 16)$ | ||
| DCON / RDCON inputs | code input | vaft.code.gpec.prepare_gpec_suite_case |
||
| Vacuum boundary | model choice / convention (enters DCON ideal energy principle) | $\mathrm{free\ boundary,\ no\ wall\ (far\ wall)}$ | ||
| Resistive inner layers | derived | $\eta(T_e, Z_{\mathrm{eff}}, \ln\Lambda),\ \rho_m\ \mathrm{at}\ q = m/n$ | vaft.code.gpec._solvers.write_rmatch_resistive_layers |
|
| DCON ideal energy principle | solver / model | vaft.code.gpec.run_gpec_suite_case |
||
| RDCON resistive matching | solver / model | vaft.code.gpec.run_gpec_suite_case |
||
| Energy and eigenfunctions | native result | $\delta W_n = \delta W_p + \delta W_v,\ \ \xi_{m,n}(\psi)$ | vaft.code.gpec.read_dcon_output |
|
| Delta-prime at rational surfaces | native result | $\Delta’_{m/n}\ \mathrm{at}\ q = m/n$ | vaft.code.gpec.read_pest3_matching_output |
|
| Ideal mode | standardized IMAS | $n,\ \delta W_n,\ \xi_\perp$ | mhd_linear.time_slice[:].toroidal_mode[:].{energy_perturbed, plasma} |
|
| Tearing stability index | standardized IMAS | $\Delta’_{m/n}$ | ntms.time_slice[:].mode[:].deltaw[0] |
Follow-up TODOs (implementation or IMAS mapping):
- energy_perturbed carries the DCON-normalised total delta W, not joules; the plasma/vacuum split and the full Delta-prime matrices stay native.
- Wall position, qlow and delta_mlow/high come from the packaged templates, not from VAFT options.
Ideal plasma response to 3-D fields (GPEC)
An applied non-axisymmetric coil field and the plasma’s ideal response to it: coil geometry is machine data, the excitation is prescribed, and the total, plasma and resonant fields are kept apart.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Equilibrium | reconstructed | $\psi,\ q,\ p,\ F$ | equilibrium.time_slice[:] |
|
| 3-D coil geometry | machine geometry / static data (enters GPEC inputs) | $\mathrm{VEST\ UP/MID/LOW\ coils,\ coil.in}$ | ||
| Coil current and phasing | model assumption / prior (enters GPEC inputs) | vaft.machine_mapping.coils_non_axisymmetric_geometry.CoilExcitation.from_mode |
||
| GPEC inputs | code input | vaft.code.gpec.prepare_gpec_suite_case |
||
| Response model | model choice / convention (enters GPEC ideal response) | $\mathrm{ideal,\ static}\ (\omega = 0), \mathrm{no\ rotation,\ no\ kinetic\ terms}$ | ||
| GPEC ideal response | solver / model | vaft.code.gpec.run_gpec_suite_case |
||
| Perturbed fields | native result | $\delta\mathbf B_{\mathrm{total}} = \delta\mathbf B_{\mathrm{vac}} + \delta\mathbf B_{\mathrm{plasma}}$ | vaft.code.gpec.read_gpec_netcdf |
|
| Resonant response | native result | $\Phi_{\mathrm{res}},\ w_{\mathrm{isl}}, K_{\mathrm{Chirikov}},\ \delta W$ | vaft.code.gpec.read_gpec_netcdf |
|
| Perturbed normal field | standardized IMAS | $\delta\mathbf B\cdot\nabla\psi$ | mhd_linear.time_slice[:].toroidal_mode[:].plasma.b_field_perturbed; IMAS mapping TODO: dB(R,Z,phi), its vacuum part and resonant quantities |
Follow-up TODOs (implementation or IMAS mapping):
- GPEC returns total and plasma fields; the vacuum part (total minus plasma) is not computed by any mapping.
- No frequency, rotation or kinetic response inputs: the response is ideal and static.
- Energies go to code.parameters; resonant quantities and cylindrical fields are not mapped.
Magnetic field-line topology (FLARE)
Field lines traced through the axisymmetric background plus the 3-D perturbation: Poincare maps, connection lengths and strike-point footprints are the native products; a heat load needs an explicit model, here a relative proxy.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Axisymmetric background field | reconstructed | $\mathbf B_0(R,Z)$ | equilibrium.time_slice[:] |
|
| 3-D perturbation from GPEC | native result | $\delta\mathbf B(R,Z,\phi)$ | vaft.code.flare.write_helicity_flipped_field |
|
| Field-direction convention | model choice / convention (enters FLARE field-line tracing) | $\mathrm{scale}{I_p},\ \mathrm{scale}{B_t}\ \mathrm{from\ COCOS}$ | vaft.code.flare.flare_equilibrium_scales |
|
| Wall and target geometry | machine geometry / static data (enters FLARE field-line tracing) | $\mathrm{in\ the\ FLARE\ control\ file}$ | ||
| Tracing configuration | model choice / convention (enters FLARE field-line tracing) | $\mathrm{in\ the\ FLARE\ control\ file}$ | ||
| FLARE field-line tracing | solver / model | vaft.code.flare.run_flare |
||
| Poincare map | native result | $(R, Z)\ \mathrm{crossings\ at\ fixed}\ \phi$ | vaft.data.flare_products.read_flare_product |
IMAS mapping TODO: no IMAS path |
| Connection length | native result | $L_c(R,Z)$ | plasma_initiation.b_field_lines |
|
| Strike-point footprint | native result | $\psi_{\min},\ \alpha\ \mathrm{at\ the\ target}$ | ||
| Relative heat-load proxy | derived | vaft.process.field_line_topology.footprint_heat_load_proxy |
||
| Incident power fractions | standardized IMAS | $f_{\mathrm{inc}}$ | divertors[:].target[:].power_incident_fraction |
Follow-up TODOs (implementation or IMAS mapping):
- Wall/target geometry and tracing settings live in the user’s FLARE control file, not in VAFT.
- No island width or stochasticity metric is computed from FLARE output (GPEC’s Chirikov K is the only one in VAFT).
- The heat load is a relative proxy; no parallel-transport or q_perp model is implemented.
Neoclassical transport: closed-form fits and drift-kinetic solution
The same resolved local state through the Sauter and Redl fits and the drift-kinetic solver NEO, so the two can be compared at one state.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Resolved local state | derived | $T_s,\ n_s,\ q,\ \epsilon,\ f_t$ | vaft.process.transport_state.resolve_transport_state |
|
| Sauter / Redl fits | derived | vaft.formula.neoclassical.redl_bootstrap_current |
||
| NEO input | code input | vaft.code.gacode.neo.prepare_neo_case |
||
| NEO drift-kinetic solve | solver / model | vaft.code.gacode.neo.run_neo |
||
| Fluxes, bootstrap current | native result | $\Gamma_s,\ Q_s,\ \langle j_{\mathrm{bs}}B\rangle$ | vaft.code.gacode.neo.collect_neo_outputs |
|
| Neoclassical transport | standardized IMAS | $\Gamma_s,\ Q_s,\ j_{\mathrm{bs}}$ | core_transport.model[:].profiles_1d[:] |
Local turbulent transport: quasilinear and gyrokinetic
One local gyrokinetic state projected into TGLF (quasilinear) and CGYRO (local delta-f, linear or nonlinear); their outputs are different physical objects and are kept apart.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Equilibrium | reconstructed | $q,\ r,\ R,\ \kappa,\ \delta$ | equilibrium.time_slice[:].profiles_1d |
|
| Kinetic profiles | measured | $T_s,\ n_s,\ Z_{\mathrm{eff}}$ | core_profiles.profiles_1d[:] |
|
| Resolved local gyrokinetic state | derived | $n_s, T_s, Z_s, m_s,\ a/L_{n_s}, a/L_{T_s},\ T_i/T_e, Z_{\mathrm{eff}},\ \hat\nu_{ee},\ \beta_e,\ q,\ \hat s,\ \kappa, s_\kappa,\ \delta, s_\delta$ | vaft.process.transport_state.resolve_transport_state |
|
| Rotation and ExB shear | model assumption / prior (enters Resolved local gyrokinetic state) | $\gamma_E = 0,\ M = 0\ (\mathrm{not\ derived})$ | ||
| input.tglf | code input | vaft.code.gacode.tglf.prepare_tglf_input |
||
| input.cgyro | code input | vaft.code.gacode.cgyro.prepare_cgyro_input |
||
| TGLF model choices | model choice / convention (enters TGLF quasilinear model) | $\mathrm{SAT_RULE},\ \delta B_\perp, \delta B_\parallel, \mathrm{XNU_MODEL},\ k_y\ \mathrm{grid}$ | ||
| TGLF quasilinear model | solver / model | vaft.code.gacode.tglf.run_tglf |
||
| CGYRO local delta-f | solver / model | vaft.code.gacode.cgyro.run_cgyro |
||
| CGYRO model choices | model choice / convention (enters CGYRO local delta-f) | $\mathrm{linear \mid nonlinear},\ N_{\mathrm{field}}, \mathrm{Sugama\ collisions},\ \mathrm{Miller\ geometry}$ | ||
| Quasilinear fluxes and spectrum | native result | $Q_s,\ \Gamma_s;\ \gamma(k_y),\ \omega(k_y)$ | vaft.code.gacode.tglf.collect_tglf_outputs |
|
| Linear eigenmodes | native result | $\gamma,\ \omega,\ \phi(\theta)$ | vaft.code.gacode.cgyro.collect_cgyro_outputs |
|
| Saturated fluxes (nonlinear) | native result | $\langle Q_s\rangle_t,\ \langle\Gamma_s\rangle_t$ | vaft.code.gacode.cgyro.collect_cgyro_outputs |
|
| Turbulent transport (TGLF) | standardized IMAS | $Q_s,\ \Gamma_s\ \mathrm{(quasilinear)}$ | vaft.machine_mapping.turbulence.core_transport_from_tglf |
core_transport.model[:]; IMAS mapping TODO: gyrokinetics_local (via gyrokinetics_local_from_tglf) not stored by any stage (#1128/#1129) |
| Gyrokinetic run (CGYRO) | standardized IMAS | $\gamma,\ \omega;\ \langle Q_s\rangle_t,\ \langle\Gamma_s\rangle_t$ | vaft.machine_mapping.gyrokinetics.gyrokinetics_local_from_cgyro |
gyrokinetics_local.{linear.wavevector[:].eigenmode[:], non_linear.fluxes_1d}; IMAS mapping TODO: gyrokinetics_local not stored by any stage (#1128/#1129) |
Follow-up TODOs (implementation or IMAS mapping):
- Rotation and ExB shear are zero in both projections (TGLF VEXB_SHEAR, CGYRO GAMMA_E, MACH): their derivation from data is not implemented (#553).
- Squareness zeta enters as 0 (VEST equilibria carry no squareness); Z_EFF is not written to input.cgyro by design: CGYRO recomputes it from the species list (Z_EFF_METHOD=2).
- No implemented local-gyrokinetic validity criterion (rho*): compare_with_oracle checks only the input translation against CGYRO’s own projection.
- gyrokinetics_local_from_tglf and gyrokinetics_local_from_cgyro write gyrokinetics_local, but no pipeline stage stores that IDS: turbulent_transport owns core_transport only, and one occurrence per flux surface awaits the #1128/#1129 occurrence model (#2020).
Plasma parameter inference: completing the kinetic profiles
Where inference sits: diagnostics and the equilibrium give reconstructed but incomplete kinetic profiles; explicit assumptions and the closures VAFT implements complete them, and every completed quantity keeps its origin on the way to simulation.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Thomson scattering | measured | $T_e,\ n_e\ \mathrm{at}\ (R, Z)$ | thomson_scattering.channel[:] |
|
| Charge exchange (optional) | measured | $T_i^{\mathrm{CX}}\ \mathrm{at}\ (R, Z)$ | charge_exchange.channel[:].ion[0].t_i |
|
| Magnetic equilibrium | reconstructed | $p_{\mathrm{eq}}(\psi),\ q,\ \langle j\cdot B\rangle,\ \mathrm{geometry}$ | equilibrium.time_slice[:].profiles_1d |
|
| Reconstructed kinetic profiles | reconstructed | $T_e(\rho),\ n_e(\rho)\ (\mathrm{fitted}),\ [T_i(\rho)], p_{\mathrm{eq}}(\rho),\ q(\rho)$ | core_profiles.profiles_1d[:].electrons |
|
| Fitting assumptions | model choice / convention (enters Reconstructed kinetic profiles) | $\mathrm{polynomial\ |\ core\text{-}poly/edge\text{-}exp\ |\ GP}, \mathrm{order}\ N,\ x = \rho_{\mathrm{tor},N}\ \mathrm{or}\ \psi_N, T, n > 0,\ \sigma\text{-}\mathrm{weighted}$ | vaft.process.profile.profile_fitting_thomson_scattering |
|
| Closure assumptions | model assumption / prior (enters Reconstructed kinetic profiles) | $\mathrm{common}\ T_i,\ \mathrm{impurity\ species},\ Z_{\mathrm{eff}}, \sigma_\parallel:\ \mathrm{Spitzer\ or\ neoclassical},\ \ln\Lambda$ | ||
| Thermodynamic closure | inferred | vaft.process.kinetic_closure.infer_ti_pressure_partition |
||
| Impurity closure | inferred | vaft.process.impurity.resolve_impurity_composition |
||
| Resistive closure | inferred | $V_R^{\mathrm{obs}} \to Z_{\mathrm{eff}}^{\mathrm{res}}\ (\mathrm{scalar}), \sigma_\parallel:\ \mathrm{Spitzer\ |\ neoclassical\ (Sauter,\ Redl)}$ | vaft.process.resistive_zeff.infer_resistive_zeff |
|
| Completed kinetic profiles | derived | $n_e, T_e, T_i, n_s, Z_{\mathrm{eff}}, \mathrm{each\ with\ origin=\ldots;\ method=\ldots}$ | core_profiles.profiles_1d[:].{electrons, ion[:], zeff} |
|
| Simulation-ready | code input | $\mathrm{every\ input\ quantity\ with\ its\ origin}$ |
References:
- L. L. Lao et al., Nucl. Fusion 25 (1985) 1611
- J. Wesson, Tokamaks, 4th ed., Oxford University Press (2011), Sec. 4.25
- J. A. Romero and JET-EFDA contributors, Nucl. Fusion 50 (2010) 115002
- L. Spitzer and R. Harm, Phys. Rev. 89 (1953) 977
- O. Sauter, C. Angioni and Y. R. Lin-Liu, Phys. Plasmas 6 (1999) 2834; 9 (2002) 5140 (erratum)
- A. Redl et al., Phys. Plasmas 28 (2021) 022502
Follow-up TODOs (implementation or IMAS mapping):
- Atomic-model-constrained Z_eff(rho) (transient charge states projected through the resistive closure) is on develop as resolve_radial_composition(normalization=’resistive_closure’) (#1565, #1566, PR #1659); this diagram still draws the scalar resistive closure only.
- Rotation, E_r and the ExB shear are not inferred: downstream codes receive gamma_E = 0 as an explicit assumption (#553).
- Provenance is recorded per quantity where it exists (core_profiles origin=…; method=… records for composition, the T_i result’s origin/method fields); there is no single runtime provenance object, by design (#1601 is documentation-first).
Inferred quantities depend on inferred quantities
Provenance propagates: a composition assumption fixes the ion densities, which set the pressure-partition T_i, whose gradients reach the gyrokinetic input beside an assumed zero ExB shear. Every link below the measurements carries the assumptions above it.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Electron profiles | measured | $T_e(\rho),\ n_e(\rho)$ | core_profiles.profiles_1d[:].electrons.{temperature, density_thermal} |
|
| Equilibrium pressure | reconstructed | $p_{\mathrm{eq}}(\psi)\ \ (\mathrm{independent\ lineage})$ | equilibrium.time_slice[:].profiles_1d.pressure |
|
| Impurity source | model assumption / prior (enters Ion densities) | $\mathrm{measured} \succ \mathrm{explicit} \succ \mathrm{derived} \succ \mathrm{assumed}$ | ||
| Ion densities | derived | vaft.process.impurity.resolve_impurity_composition |
||
| Pressure-partition ion temperature | inferred | vaft.process.kinetic_closure.infer_ti_pressure_partition |
||
| Ion pressure | derived | $p_i(\rho),\ dp_i/dr$ | ||
| Normalized gradients | derived | $a/L_{T_i},\ a/L_{n_i},\ T_i/T_e$ | vaft.code.gacode.tglf.prepare_tglf_input |
|
| ExB shear | model assumption / prior (enters Gyrokinetic input) | $\gamma_E = 0\ \ (\mathrm{assumed})$ | ||
| Gyrokinetic input | code input | $\mathrm{local\ state\ with\ origins}$ | vaft.code.gacode.cgyro.prepare_cgyro_input |
References:
- L. L. Lao et al., Nucl. Fusion 25 (1985) 1611
- J. Wesson, Tokamaks, 4th ed., Oxford University Press (2011), Sec. 4.25
Follow-up TODOs (implementation or IMAS mapping):
- E_r, rotation and gamma_E are not inferred from data (#553): gamma_E = 0 enters the gyrokinetic input as an assumption, and force balance is future scope.
Vessel eddy currents: a circuit driven by the coils and the plasma
The passive structure is a set of resistive loops driven by the changing PF and plasma currents. VAFT solves that circuit forward from rest – it does not fit the vessel currents to the magnetics – and checks the result against the pre-plasma flux loops and probes.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| PF coil currents | measured | $I_{\mathrm{PF},j}(t)$ | vaft.machine_mapping.pf_active.vfit_pf_active_dynamic |
pf_active.coil[:].current.data |
| Plasma current | measured | $I_p(t)$ | magnetics.ip[0].data |
|
| Plasma filaments | model assumption / prior (enters Circuit matrices) | $(R_k, Z_k),\ f_k\ \mathrm{set\ by\ the\ caller}, I_k = f_k I_p$ | ||
| Loops and mutual inductances | machine geometry / static data (enters Circuit matrices) | $R = \mathrm{diag}(R_k),\ M_{\mathrm{pp}},\ M_{\mathrm{pa}}$ | vaft.omas.process_wrapper.ensure_em_coupling |
{pf_passive.loop[:].resistance, em_coupling.mutual_passive_passive} |
| Circuit matrices | derived | $M,\ R,\ L = [M_{\mathrm{pa}},\ M_{\mathrm{plasma}}], M_{\mathrm{plasma}}\ \mathrm{from\ Green’s\ functions}$ | vaft.process.electromagnetics.compute_impedance_matrices |
|
| Forward circuit solve | solver / model | vaft.process.electromagnetics.solve_eddy_currents |
||
| Vessel loop currents | standardized IMAS | $I_{\mathrm{loop},k}(t)$ | vaft.omas.vest_upstream.build_eddy_ods |
pf_passive.loop[:].current |
| Pre-plasma magnetics | measured | $\psi_{\mathrm{FL}},\ B_p\ \mathrm{before\ breakdown}$ | magnetics.{flux_loop, b_field_pol_probe} |
|
| Eddy improvement | derived | vaft.omas.vacuum_magnetics.eddy_improvement |
Follow-up TODOs (implementation or IMAS mapping):
- Vessel currents are a forward model, not fitted to the magnetics; EFIT then treats them as known (IFITVS=0).
- The plasma filaments are placed by the caller, not taken from the equilibrium.
- The circuit is constant in time and starts from zero current; the 50 us substep is coarser than the 40 us diagnostics grid (#425).
- A missing PF channel (PF5 48644-48760, PF6 48534-48537) leaves NaN currents and the stage refuses to run.
Kinetic profile fitting: point measurements to profiles
Thomson and charge-exchange points are placed on flux surfaces by the equilibrium and fitted to smooth profiles with declared fitting choices; each fitted profile keeps its origin record.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Thomson scattering | measured | $T_e,\ n_e \pm \sigma\ \mathrm{at}\ (R, Z)$ | thomson_scattering.channel[:].{t_e, n_e, position} |
|
| Magnetic equilibrium | reconstructed | $\psi(R, Z),\ q(\psi)$ | equilibrium.time_slice[:].{profiles_2d, profiles_1d.q} |
|
| Charge exchange (optional) | measured | $T_i,\ v_\phi\ \mathrm{at}\ (R, Z)$ | charge_exchange.channel[:].ion[:].{t_i, velocity_tor} |
|
| Flux-surface mapping (Thomson) | derived | $(R,Z) \to \psi_N \to \rho_{\mathrm{pol}} = \sqrt{\psi_N}, \rho_{\mathrm{tor},N}\ \mathrm{from}\ \Phi = \int q\,d\psi;\ \mathrm{outside\ the\ LCFS:\ NaN}$ | vaft.process.profile.equilibrium_mapping_thomson_scattering |
|
| Flux-surface mapping (CX) | derived | $\mathrm{same\ coordinates};\ \mathrm{outside\ the\ LCFS:\ NaN}$ | vaft.process.profile.equilibrium_mapping_charge_exchange |
|
| Fitting coordinate (both fits) | model choice / convention (enters Flux-surface mapping (Thomson)) | $x = \rho_{\mathrm{tor},N}\ (\mathrm{default})\ |\ \psi_N\ |\ \rho_{\mathrm{pol},N}$ | ||
| Electron profile fit | derived | $T_e(x),\ n_e(x)$ | vaft.process.profile.profile_fitting_thomson_scattering |
|
| Ion profile fit | derived | $T_i(x),\ v_\phi(x)$ | vaft.process.profile.profile_fitting_charge_exchange |
|
| Fit model and order | model choice / convention (enters Electron profile fit) | $\mathrm{polynomial\ |\ core\text{-}poly/edge\text{-}exp\ |\ tanh\ |\ GP},\ N = 3, \sigma\text{-}\mathrm{weighted};\ T > 0:\ \mathrm{order\ stepped\ down}$ | vaft.formula.utils.fit_profile |
|
| Ion temperature without CX | model assumption / prior (enters Ion profile fit) | $T_i = r\,T_e,\ r\ \mathrm{fitted\ or\ assumed}$ | vaft.process.profile.fit_ti_te_ratio |
|
| Kinetic profiles | standardized IMAS | $T_e, n_e, T_i, v_\phi, \mathrm{each\ with\ origin=\ldots;\ method=\ldots}$ | vaft.process.profile.core_profiles |
core_profiles.profiles_1d[:].{electrons, ion[0], *_fit} |
Follow-up TODOs (implementation or IMAS mapping):
- Thomson and charge exchange are fitted separately and combined only in core_profiles; there is no joint multi-diagnostic fit.
- The kinetic-profile evidence framework (#1204) is not yet referenced by the fit records.
Transport hierarchy: classical, neoclassical, turbulent
One resolved local state and five independently computed routes; none feeds another. ‘Always available’ means the route needs no external code, not that its physics applies everywhere. Each route with a writer lands in its own core_transport.model entry, found by its identifier.index and code name – the array position k is assigned at write time.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Resolved local state | derived | $T_s,\ n_s,\ a/L_{T,n},\ q,\ \hat s,\ \hat\nu$ | vaft.process.transport_state.resolve_transport_state |
|
| Classical: Braginskii | derived | $\mathrm{perpendicular\ conductive}\ q_{\perp,e},\ q_{\perp,i}, \mathrm{strongly\ magnetized}:\ \kappa_\perp \propto nT/(m\Omega^2\tau), \mathrm{software:\ always\ available}$ | vaft.process.transport_state.classical_heat_fluxes |
|
| Neoclassical closures: Sauter / Redl | derived | $j_{\mathrm{bs}};\ \sigma_\parallel\ \mathrm{from\ a\ separate\ conductivity\ call};\ \mathrm{no}\ \Gamma_s,\ Q_s, \mathrm{software:\ always\ available}$ | vaft.omas.neoclassical.compute_bootstrap_current |
|
| Neoclassical: NEO | solver / model | $\Gamma_s,\ Q_s,\ j_{\mathrm{bs}}\ (\mathrm{transport\ run}), \sigma_\parallel\ (\mathrm{separate}\ E_\parallel\ \mathrm{run}), \mathbf{optional}\ \mathrm{external\ code}$ | vaft.code.gacode.neo.runner.run_neo_pair |
|
| Turbulent: TGLF | solver / model | $Q_s,\ \Gamma_s\ (\mathrm{quasilinear}), \mathbf{optional}\ \mathrm{external\ code}$ | vaft.code.gacode.tglf.run_tglf |
|
| Turbulent: CGYRO | solver / model | $\mathrm{nonlinear}\ Q_s(t),\ \Gamma_s(t), \mathbf{optional}\ \mathrm{external\ code}$ | vaft.code.gacode.cgyro.run_cgyro |
|
| Saturation window | model assumption / prior (enters Anomalous transport (CGYRO)) | $(t_0, t_1)\ [a/c_s],\ \mathrm{chosen\ by\ the\ caller}$ | ||
| Classical transport | standardized IMAS | $\chi_e,\ \chi_i\ (\mathrm{private\ index})$ | vaft.machine_mapping.classical.core_transport_from_classical |
core_transport.model[k] (identifier.index -1) |
| Neoclassical transport | standardized IMAS | $\Gamma_s,\ Q_s\ (\mathrm{NEO})$ | vaft.machine_mapping.neoclassical.core_transport_from_neo |
core_transport.model[k] (identifier.index 5) |
| Anomalous transport (TGLF) | standardized IMAS | $\Gamma_s,\ Q_s,\ \Pi_\phi$ | vaft.machine_mapping.turbulence.core_transport_from_tglf |
core_transport.model[k] (identifier.index 6, code TGLF) |
| Anomalous transport (CGYRO) | standardized IMAS | $\langle\Gamma_s\rangle_t,\ \langle Q_s\rangle_t\ \mathrm{over}\ (t_0, t_1)$ | vaft.machine_mapping.gyrokinetics.core_transport_from_cgyro |
core_transport.model[k] (identifier.index 6, code CGYRO) |
References:
- S. I. Braginskii, Rev. Plasma Phys. 1 (1965) 205
- O. Sauter, C. Angioni and Y. R. Lin-Liu, Phys. Plasmas 6 (1999) 2834; 9 (2002) 5140 (erratum)
- A. Redl et al., Phys. Plasmas 28 (2021) 022502
- E. A. Belli and J. Candy, Plasma Phys. Control. Fusion 50 (2008) 095010
- E. A. Belli and J. Candy, Plasma Phys. Control. Fusion 54 (2012) 015015
- G. M. Staebler, J. E. Kinsey and R. E. Waltz, Phys. Plasmas 14 (2007) 055909
- J. Candy, E. A. Belli and R. V. Bravenec, J. Comput. Phys. 324 (2016) 73
Follow-up TODOs (implementation or IMAS mapping):
- The closed-form Sauter/Redl results have no core_transport writer (and carry no particle or heat flux to write); they are returned, not stored.
- Classical transport uses a private identifier.index (-1): the data dictionary has no classical entry. The Braginskii forms assume a strongly magnetized plasma, and ln Lambda is flagged invalid below 10 eV.
- core_transport.model[k]: k is the array position the writer finds or appends (matching identifier.index and code.name); it is not the identifier.index.
- A nonlinear CGYRO run writes no fluxes without a saturation window: choosing it is the caller’s judgement.
- The TGLF-NN surrogate (vaft.code.gacode.tglf.surrogate) refuses VEST inputs as out of domain and has no IMAS writer.
Sauter / Redl against NEO at one state
One shared source state, two independently prepared input paths, and an end-to-end comparison against NEO as the numerical reference – not as ground truth. The bootstrap current is compared in one definition,
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Shared source state | reconstructed | $T_s,\ n_s,\ Z_{\mathrm{eff}},\ \mathrm{impurity},\ \psi,\ q\ (\mathrm{one\ time\ slice})$ | {core_profiles.profiles_1d[:], equilibrium.time_slice[:]} |
|
| Analytic-model inputs (VAFT) | derived | $\rho_{\mathrm{tor},N}\ \mathrm{from\ the\ equilibrium};\ f_t\ \mathrm{from\ the\ equilibrium}, \nu^*\ (\mathrm{Sauter\ definition});\ Z_{\mathrm{eff}},\ n_i/n_e$ | vaft.omas.neoclassical.compute_bootstrap_current |
|
| NEO inputs (GACODE conversion) | code input | $\mathrm{input.gacode\ grid\ and\ geometry};\ f_t,\ \nu\ \mathrm{as\ NEO\ forms\ them}, \mathrm{species\ list}\ (Z_{\mathrm{eff}}\ \mathrm{from\ it})$ | vaft.code.gacode.neo.inputs.prepare_neo_case |
|
| Sauter closure | derived | vaft.validation.neoclassical.bootstrap_models |
||
| Redl closure | derived | $j_{\mathrm{bs}}\ \mathrm{with\ Redl’s\ refitted}\ L_{31}, L_{32}, \alpha$ | vaft.validation.neoclassical.bootstrap_models |
|
| NEO transport solve | solver / model | $j_{\mathrm{bs}},\ \Gamma_s,\ Q_s, \mathbf{optional}\ \mathrm{external\ code}$ | vaft.code.gacode.neo.runner.run_neo_case |
|
| NEO conductivity solve | solver / model | $\sigma_\parallel = j_\parallel / E_\parallel, E_\parallel = 1,\ \mathrm{all\ gradients\ off}$ | vaft.code.gacode.neo.runner.run_neo_conductivity_case |
|
| NEO into the IMAS definition | standardized IMAS | $\langle J\cdot B\rangle/B_{\mathrm{unit}} \to \langle J\cdot B\rangle/B_0, \mathrm{signed}\ B_{\mathrm{unit}}\ (\mathrm{GACODE\ COCOS\ 2})$ | vaft.machine_mapping.neoclassical.core_profiles_from_neo |
core_profiles.profiles_1d[:].{j_bootstrap, conductivity_parallel} |
| Comparison basis | model choice / convention (enters Model agreement) | $j_{\mathrm{bs}} = \langle J\cdot B\rangle/B_0,\ \mathrm{vacuum}\ B_0,\ \mathrm{signed}, \mathrm{same}\ \rho_{\mathrm{tor},N}\ \mathrm{grid};\ Z_{\mathrm{eff}},\ n_i/n_e\ \mathrm{must\ match}$ | ||
| Model agreement | derived | $\mathrm{reference = NEO\ (numerical,\ not\ ground\ truth)}, \mathrm{integrated,\ RMS/peak,\ peak\ differences}$ | vaft.validation.neoclassical.model_comparison |
References:
- O. Sauter, C. Angioni and Y. R. Lin-Liu, Phys. Plasmas 6 (1999) 2834; 9 (2002) 5140 (erratum)
- A. Redl et al., Phys. Plasmas 28 (2021) 022502
- E. A. Belli and J. Candy, Plasma Phys. Control. Fusion 50 (2008) 095010
- E. A. Belli and J. Candy, Plasma Phys. Control. Fusion 54 (2012) 015015
Follow-up TODOs (implementation or IMAS mapping):
- End-to-end, not like-for-like: the two input paths differ in grid construction, trapped fraction and collisionality definition, so part of any gap is the input path. The like-for-like check against NEO’s own Sauter/Redl columns (agreement to 1e-7) is a test, not a product.
- A stored NEO profile on a different rho_tor_norm grid is refused, not interpolated; a run whose Z_eff or n_i/n_e differs is refused when the solver’s values are passed.
- Without NEO there is no reference and no comparison; the closed-form results are not written to IMAS.
Machine model and shot history: one shot number selects the machine
The shot number is the only key. It selects the machine era (PF and wall geometry), each diagnostic’s revision (wiring, gains, baseline windows) and the shot-log record; the static machine description is built once per era and the processing era is reported per diagnostic.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Shot number | measured | $s$ | dataset_description.data_entry.pulse |
|
| Machine era | machine geometry / static data | $\mathrm{PF\ 1906\ /\ 2507},\ \mathrm{wall\ 1512\ /\ 2409}$ | vaft.omas.vest_upstream.machine_era_for_shot |
|
| Diagnostic revisions | machine geometry / static data | $\mathrm{from_shot} \le s \le \mathrm{to_shot}, \mathrm{wiring,\ gain,\ baseline}$ | vaft.machine_mapping.utils.resolve_vest_diagnostic |
|
| Shot log | measured | $\mathrm{set\ points,\ gas,\ timing}$ | vaft.machine_mapping.pulse_schedule.mapping.pulse_schedule |
pulse_schedule |
| IMAS sign and angle conventions | model choice / convention (enters Static machine description) | $\phi = (-30^\circ\,h) \bmod 360^\circ, \mathrm{discharge\ sign\ contract}$ | vaft.machine_mapping.registry.port_phi |
|
| Static machine description | standardized IMAS | $\mathrm{geometry},\ M_{ij},\ \mathrm{sensor\ positions}$ | vaft.omas.vest_upstream.build_static_ods |
{wall | pf_active | pf_passive | em_coupling | magnetics | tf} |
| Processing era per diagnostic | derived | $\mathrm{calibration\ lineage}$ | vaft.machine_mapping.provenance.vest_processing_provenance |
IMAS mapping TODO: returned as a dict; not written to an IDS |
Follow-up TODOs (implementation or IMAS mapping):
- The VEST COCOS index is not resolved yet (vaft.machine_mapping.conventions.equilibrium_orientation_is_resolved); the discharge sign contract stands in for it.
- The shot-log mapper writes pulse_schedule when called (vaft shotlog CLI) but build_diagnostics_ods does not call it.
- Wall and limiter history is the era table only; there is no per-component calibration lineage.
- There is no single shot-to-machine object: era, revisions and provenance are separate lookups.
- The static description is built per era, not per shot: PF, passive structure and coupling at the era’s reference shot, magnetics and TF as the un-gated inventory. Per-shot wiring and revisions apply when the diagnostics IDS is built.
Signal processing: raw digitizer records to a diagnostic IDS
A representative chain, in the order the magnetic probes use it: anti-alias low-pass on the native grid, calibration, time integration of the inductive voltage, a linear baseline over the era’s leading samples, then interpolation onto the analysis grid. Other diagnostics reorder or skip steps.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Raw digitizer record | measured | $V(t)\ \mathrm{per\ DAQ\ field}$ | vaft.database.raw.load |
|
| DAQ field codes | machine geometry / static data (enters Raw digitizer record) | $(\mathrm{diagnostic},\ s) \rightarrow \mathrm{field\ codes}$ | vaft.machine_mapping.raw_daq_sources.raw_sources |
|
| Anti-alias low-pass | derived | $\mathrm{FIR\ on\ the\ native\ fast\ grid}$ | vaft.process.signal_processing.fir_filter |
|
| Calibration | derived | vaft.machine_mapping.utils.calibrate_vest_signal |
||
| Calibration revision | machine geometry / static data (enters Calibration) | $c,\ \mathrm{sign},\ \mathrm{baseline\ window}$ | vaft.machine_mapping.utils.resolve_vest_diagnostic |
|
| Time integration | derived | vaft.process.magnetics.vest_b_field_pol_probe_legacy |
||
| Baseline subtraction | derived | $\mathrm{linear\ fit\ over\ the\ era’s\ leading\ samples}$ | vaft.process.signal_processing.subtract_baseline |
|
| Resampling | derived | $t_k = t_{\mathrm{start}} + k\,\Delta t$ | vaft.process.signal_processing.resample_to_time |
|
| Analysis time grid | model choice / convention (enters Resampling) | $t_{\mathrm{start}},\ t_{\mathrm{end}},\ \Delta t$ | vaft.machine_mapping.utils.resolve_diagnostics_time_policies |
|
| Diagnostic IDS | standardized IMAS | $\psi_{\mathrm{FL}},\ B_p,\ I_p,\ I_{\mathrm{PF}}$ | vaft.omas.vest_upstream.build_diagnostics_ods |
{magnetics | pf_active | tf | spectrometer_uv | barometry} |
Follow-up TODOs (implementation or IMAS mapping):
- The order is per diagnostic: PF coils subtract a baseline mean, filter, scale and align; H-alpha shifts in time and resamples with an anti-aliased resampler; flux loops integrate per radian.
- There is no drift-correction stage separate from the baseline fit, and no cross-DAQ synchronization module: each mapper applies its own time shift and interpolation.
- Integration is inline in each mapper; vaft.process has no shared integration helper.
- Thomson scattering and the interferometer read post-processed files, so this chain does not apply to them.
Quality and validation: measurements against references to evidence
Three separate kinds of evidence: per-sample source validity (written to the IDS validity nodes), agreement with the vacuum model before the plasma (a report), and equilibrium fit quality turned into a slice class. Model disagreement never sets a channel’s validity.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Processed magnetics | measured | $m_i(t)$ | magnetics.{flux_loop, b_field_pol_probe, ip} |
|
| Synthetic vacuum magnetics | derived | $\hat m_i = m_{\mathrm{coil}} + m_{\mathrm{eddy}}$ | vaft.omas.vacuum_magnetics.synthetic_vacuum_magnetics |
|
| Magnetic equilibrium | reconstructed | $\psi(R, Z),\ \chi^2$ | equilibrium.time_slice[:] |
|
| Source validity checks | derived | $\mathrm{per\ sample,\ per\ channel}$ | vaft.validation.magnetics.validate_magnetics_signals |
|
| Residual metrics | derived | $m_i - \hat m_i:\ \mathrm{RMS},\ \mathrm{correlation}$ | vaft.omas.vacuum_magnetics.vacuum_magnetics_metrics |
|
| Fit and convergence quality | derived | $\chi^2_\nu,\ \mathrm{residual\ bias},\ \mathrm{convergence}$ | vaft.omas.efit_quality.efit_quality_metrics |
|
| Channel validity | standardized IMAS | $0\ \mathrm{valid},\ 1\ \mathrm{flagged},\ -2\ \mathrm{rejected}$ | vaft.validation.imas.write_validity |
magnetics.{flux_loop[:].flux | b_field_pol_probe[:].field | ip[:]}.validity_timed |
| Vacuum-model quality gate | derived | $\mathrm{pass\ /\ fail\ per\ sensor\ group}$ | vaft.omas.vacuum_magnetics.quality_gate |
IMAS mapping TODO: a report; not written to an IDS |
| Slice quality class | derived | $\mathrm{good\ /\ admissible\ /\ unreconstructible}$ | vaft.validation.equilibrium_quality.equilibrium_quality_table |
IMAS mapping TODO: a table; not written to an IDS |
| Acceptance criteria | model choice / convention (enters Slice quality class) | $\mathrm{versioned\ thresholds}$ |
Follow-up TODOs (implementation or IMAS mapping):
- Only source validity reaches IMAS; model-agreement and fit-quality metrics are returned as dicts and tables in the stage manifests.
- Flux-loop agreement with the vacuum model is report-only by default; only source validity can reject a loop.
- The vacuum check covers the pre-plasma window only; there is no plasma-phase synthetic magnetics check outside the EFIT residuals.
Fault and anomaly evidence: diagnostic-specific checks
There is no single anomaly model: each diagnostic has its own checks. Magnetics separate hard faults from soft flags and honour the recorded fault list; the Rogowski coil has a square-wave test; clipped PF, diamagnetic and TF records are repaired over the clipped interval; H-alpha channels are screened before they time the plasma.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Processed channels | measured | $x_i(t)$ | {magnetics | pf_active | tf | spectrometer_uv} |
|
| Recorded faults | machine geometry / static data (enters Magnetics channel checks) | $(\mathrm{kind},\ i) \rightarrow \mathrm{reason}$ | vaft.machine_mapping.magnetics.known_magnetics_faults |
|
| Magnetics channel checks | derived | $\mathrm{hard:\ non\text{-}finite,\ flatline,\ magnitude,\ outlier},\mathrm{soft:\ spike,\ offset\ jump,\ drift,\ noise}$ | vaft.validation.magnetics.validate_magnetics_signals |
|
| Rogowski square-wave test | derived | $I_p(t)\ \mathrm{shape}$ | vaft.validation.plasma_current.assess_plasma_rogowski |
|
| Clipping detection | derived | $\mathrm{PF,\ diamagnetic,\ TF}$ | vaft.process.signal_processing.detect_clipped_samples |
|
| H-alpha usability | derived | $\mathrm{finite,\ not\ railed,\ live\ baseline}$ | vaft.omas.plasma_timing.halpha_usability |
|
| Channel validity | standardized IMAS | $0,\ 1,\ -2$ | vaft.validation.magnetics.project_validity |
magnetics.{flux_loop[:].flux | b_field_pol_probe[:].field | ip[:]}.validity_timed |
| Plasma-current Rogowski validity | standardized IMAS | $\mathrm{one\ value\ per\ record}$ | magnetics.rogowski_coil[0].current.validity |
|
| Repaired interval | derived | $x_i(t)\ \mathrm{across\ the\ clipped\ samples}$ | vaft.process.signal_processing.repair_clipped_interval |
|
| Light sources for timing | derived | $\mathrm{usable}\ H_\alpha\ \mathrm{channels}$ | vaft.omas.plasma_timing.plasma_timing |
Follow-up TODOs (implementation or IMAS mapping):
- No general anomaly model runs in the pipeline: vaft.process.ml provides the framework (PCA autoencoder, k-NN, one-class SVM) but no trained model ships with the package.
- The spectrometer and barometry fault registry (known_diagnostic_faults) has no pipeline consumer.
- Missing raw data marks the component unavailable or partial; it is not a per-channel fault flag.
Shot classification: rules over derived features
A shot class is a rule applied to derived features – the plasma window, the gauge-pressure response, the peak current – under stated thresholds, and it records which rule decided. It is not a measurement. A separate pre-run check classifies DAQ completeness, and equilibrium slices get a phase label from their own rule.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Raw field inventory | measured | $\mathrm{field\ codes\ in\ the\ DAQ\ database}$ | ||
| Gauge pressure | measured | $p(t)$ | barometry.gauge[0].pressure.data |
|
| Plasma current and H-alpha | measured | $I_p(t),\ H_\alpha(t)$ | magnetics.ip[0].data; spectrometer_uv |
|
| Equilibrium slices | reconstructed | $I_p(t_k)$ | equilibrium.time_slice[:].global_quantities.ip |
|
| DAQ completeness | derived | $\mathrm{daq_missing\ /\ raw_incomplete}$ | vaft.database.worker.classify.RawFieldClassifier |
|
| Pressure response | derived | $\Delta p\ \mathrm{during\ the\ shot}$ | vaft.omas.shot_class.pressure_response |
|
| Plasma window | derived | $t_{\mathrm{start}},\ t_{\mathrm{end}}$ | vaft.omas.plasma_timing.plasma_timing |
|
| Slice phase | derived | $\mathrm{ramp\text{-}up\ /\ flat\ /\ ramp\text{-}down}:\ \mathrm{level}\ 0.9\,|I_p|_{\max}$ | vaft.validation.equilibrium_regime.classify_equilibrium_regimes |
|
| Shot class | derived | $\mathrm{Plasma\ /\ BD\ failure\ /\ Vacuum},\ \mathrm{decided\ by}$ | vaft.omas.shot_class.shot_class |
IMAS mapping TODO: optionally kept in the database worker record (record_shot_class), not in an IDS |
| Default thresholds | model choice / convention (enters Shot class) | $p_{\mathrm{th}} = 10^{-2},\ I_{p,\min} = 2\ \mathrm{kA}$ |
Follow-up TODOs (implementation or IMAS mapping):
- The four-way physics class of #57 is not implemented; the class is Plasma, BD failure or Vacuum.
- There are no disruption labels and no label-provenance store; vaft.process.ml datasets take targets from the caller.
- Events passed to the slice classifier (VDE, IRE, disruption) are caller annotations, not detections.
Event detection: onsets and windows from a robust baseline
Each waveform gets a robust baseline and spread from its quiet reference, a threshold that is the larger of a fraction of the peak and a number of robust sigmas, and an active window that must stay above it. The plasma window is then confirmed across signals: H-alpha light against the plasma current. Coil onsets anchor the loop-voltage time base.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| H-alpha and plasma current | measured | $H_\alpha(t),\ I_p(t)$ | spectrometer_uv; magnetics.ip[0].data |
|
| Coil currents | measured | $I_{\mathrm{PF},j}(t)$ | pf_active.coil[:].current.data |
|
| Robust baseline | derived | $b,\ \sigma\ \mathrm{from\ the\ median\ and\ MAD}$ | vaft.process.onset.robust_baseline |
|
| Detection rules | model choice / convention (enters Activity threshold) | $f,\ k,\ t_{\mathrm{hold}}\ \mathrm{per\ signal}$ | ||
| Activity threshold | derived | vaft.process.onset.excess_threshold |
||
| Active window | derived | $x > x_{\mathrm{th}}\ \mathrm{for\ at\ least}\ t_{\mathrm{hold}}$ | vaft.process.onset.active_window |
|
| Light against current | derived | $\mathrm{consistent},\ I_p\ \mathrm{before}\ H_\alpha,\ \mathrm{offset\ disagreement}$ | vaft.omas.plasma_timing.plasma_timing |
|
| Plasma window | derived | $t_{\mathrm{start}},\ t_{\mathrm{end}}$ | IMAS mapping TODO: a dataclass; not written to an IDS | |
| Coil onsets | derived | $t_{\mathrm{on},j},\ t_{\mathrm{OH}}$ | vaft.omas.discharge_timing.coil_onsets |
|
| Loop-voltage event | derived | $\mathrm{zero\ crossing\ after\ the\ OH\ onset}$ | vaft.omas.discharge_timing.loop_voltage_event |
IMAS mapping TODO: a dataclass; not written to an IDS |
Follow-up TODOs (implementation or IMAS mapping):
- Breakdown time is the plasma-window start; there is no separate breakdown detector.
- There is no flat-top detector (only the post-hoc slice phase) and no IRE, sawtooth or disruption detector.
- current_quench and current_spike (vaft.process.transients) measure a quench or spike in a given window; nothing in the pipeline calls them, and they do not label it an IRE or a disruption.
Vacuum fields and startup proxies: coils and vessel, no plasma
The field of the PF coils and the vessel currents alone, evaluated from geometric Green’s functions on a grid or at a point, and the breakdown proxies read from it. Everything is returned to the caller; nothing is written to an IDS except the vessel currents the evaluators solve when the ODS has none.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| PF coil currents | measured | $I_{\mathrm{PF},j}(t)$ | pf_active.coil[:].current.data |
|
| Toroidal field | measured | $R_0 B_0\ [\mathrm{T\,m}]$ | tf.b_field_tor_vacuum_r.data |
|
| Prefill pressure | measured | $p\ [\mathrm{Pa}],\ \mathrm{median\ over\ 20\ ms\ before\ the\ onset}$ | vaft.omas.process_wrapper.compute_prefill_pressure_ods |
barometry.gauge[:].pressure.data |
| Launched EC power | measured | $P_{\mathrm{EC}}\ \mathrm{at\ the\ onset} \pm 1\ \mathrm{ms}$ | ec_launchers.beam[0].power_launched.data |
|
| Vessel loop currents | standardized IMAS | $I_{\mathrm{loop},k}(t), \mathrm{solved\ and\ written\ when}\ \mathtt{pf_passive}\ \mathrm{has\ no\ time}$ | vaft.omas.process_wrapper.compute_eddy_currents |
pf_passive.{time, loop[:].current} |
| Conductor and limiter geometry | machine geometry / static data (enters Vacuum field) | $(R, Z)\ \mathrm{of\ every\ filament},\ \mathrm{limiter\ unit\ 0}$ | {pf_active.coil[:].element, pf_passive.loop[:].element, wall.description_2d[0].limiter} |
|
| Vacuum field | derived | $\psi\ [\mathrm{Wb,\ not\ Wb/rad}],\ B_R,\ B_Z,\ \partial\psi/\partial t\ \mathrm{on}\ (R, Z), \mathrm{time\ snapped\ to\ a\ PF\ sample}$ | vaft.omas.process_wrapper.compute_vacuum_field_map |
|
| Point proxies | derived | vaft.omas.process_wrapper.compute_startup_proxies_ods |
||
| Connection length | derived | $L\ \mathrm{to\ the\ limiter,\ both\ directions},\ \le 150\ \mathrm{m\ each}$ | vaft.omas.process_wrapper.compute_connection_length_map_ods |
|
| Lloyd figure of merit | derived | $|E_\phi| = |\partial\psi/\partial t|/(2\pi R),\ B_\phi = R_0 B_0/R$ | vaft.omas.process_wrapper.compute_vacuum_midplane_profiles_ods |
|
| Lloyd breakdown margin | derived | $E_{BD}(p, L)\ \mathrm{(Lloyd)};\ \mathrm{NaN\ where}\ A p L \le 1$ | vaft.omas.process_wrapper.compute_vacuum_midplane_profiles_ods |
|
| Startup summary | derived | $\mathrm{point\ proxies;\ null\ area\ and\ Lloyd\ margin\ fraction\ of\ the\ map}, R_{\mathrm{ECR}}\ \mathrm{from\ the\ toroidal\ field},\ P_{\mathrm{EC}}, \mathrm{one\ flat\ dict;\ evaluated\ on\ a\ private\ copy}$ | vaft.omas.startup_summary.startup_summary |
IMAS mapping TODO: maps, profiles and proxies are returned, never written to an IDS |
Follow-up TODOs (implementation or IMAS mapping):
- No vacuum field, map, proxy or breakdown margin is written to an IDS: every evaluator returns a dict, and the startup summary is a dict too.
- compute_vacuum_field_map and compute_startup_proxies_ods solve the vessel currents and store them in the caller’s pf_passive when it has no time base; startup_summary avoids that by evaluating on a private copy.
- Only vacuum sources contribute (coils and vessel, no plasma); above roughly 10 kA of plasma current the map no longer describes the machine.
- The loop voltage is a model quantity from the coil and vessel flux; it is to be compared with the measured flux-loop voltage, not substituted for it.
- No empirical threshold is applied to the Lloyd figure of merit, and the startup summary does not report it: the summary counts the null area and the Lloyd margin on the 2-D map itself.
- The point proxies come from their own point response (compute_point_response_matrices_ods), not from the grid map; the two agree to about 1e-7 relative.
Derived MHD parameters: global quantities of an equilibrium
Global quantities computed from a reconstructed equilibrium. The DD-defined betas, l_i3 and W_MHD are written to equilibrium.global_quantities; the descriptor set and the edge-q proxies are returned with their derivation records. Two poloidal betas exist on purpose: the DD leaf and the circumference form EFIT reports.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Magnetic equilibrium | reconstructed | $p(\psi),\ V(\psi),\ \psi(R, Z),\ I_p$ | equilibrium.time_slice[:].{profiles_1d.{pressure, volume}, profiles_2d, boundary} |
|
| Plasma current | measured | $I_p(t)$ | magnetics.ip[0].data |
|
| Toroidal field | measured | $R_0 B_0\ [\mathrm{T\,m}]$ | tf.b_field_tor_vacuum_r.data |
|
| Reference radius and field | model choice / convention (enters Volume and surface integrals) | $R_0:\ \mathtt{tf.r0}\ \mathrm{when\ the\ stored}\ r_0\ \mathrm{is\ corrupt},\ B_0 = \mathtt{b0}$ | vaft.omas.update.resolve_reference_major_radius |
{equilibrium.vacuum_toroidal_field.r0, equilibrium.vacuum_toroidal_field.b0, tf.r0} |
| Volume and surface integrals | derived | $\int p\,dV\ \mathrm{(trapezoid\ over}\ V(\psi)), \int B_p^2\,dV,\ L_{\mathrm{pol}}\ \mathrm{from\ the\ surface\ traces}$ | vaft.process.equilibrium.flux_surface_quantities |
|
| Global descriptors | derived | $\kappa,\ \delta,\ V,\ \beta_t,\ \beta_N,\ q_{95},\ \mathrm{virial}\ l_i, \mathrm{each\ a\ DerivedValue\ with\ its\ record}$ | vaft.process.equilibrium.derive_global_descriptors |
IMAS mapping TODO: returned, not written |
| Edge safety-factor proxies | derived | $q_{95}^{\mathrm{est}},\ q^_{\mathrm{Menard}},\ q^_{\mathrm{Freidberg}},\ I_N, \mathrm{source:\ equilibrium\ |\ magnetics}$ | vaft.omas.edge_q.edge_q_estimate |
IMAS mapping TODO: returned (EdgeQEstimate), not written |
| Machine default shape | model assumption / prior (enters Edge safety-factor proxies) | $a,\ R_0,\ \kappa,\ \delta\ \mathrm{from\ vest.yaml\ without\ an\ equilibrium}$ | vaft.machine_mapping.edge_q_estimate.vest_edge_q_estimate_policy |
|
| Poloidal beta (DD) | standardized IMAS | vaft.omas.update.update_equilibrium_global_quantities_beta_li |
equilibrium.time_slice[:].global_quantities.beta_pol |
|
| Toroidal beta | standardized IMAS | vaft.omas.update.update_equilibrium_global_quantities_beta_li |
equilibrium.time_slice[:].global_quantities.beta_tor |
|
| Internal inductance | standardized IMAS | vaft.omas.update.update_equilibrium_global_quantities_beta_li |
equilibrium.time_slice[:].global_quantities.li_3 |
|
| Normalized beta | standardized IMAS | $\beta_N\ [-]$ | vaft.omas.update.update_equilibrium_global_quantities_beta_li |
equilibrium.time_slice[:].global_quantities.beta_normal |
| Stored energy | standardized IMAS | $W_{\mathrm{MHD}}\ [\mathrm{J}]\ \mathrm{from\ the\ reconstructed}\ p$ | vaft.omas.update.update_equilibrium_stored_energy |
equilibrium.time_slice[:].global_quantities.energy_mhd |
Follow-up TODOs (implementation or IMAS mapping):
- beta_pol holds the DD definition; EFIT normalizes by the LCFS-circumference field instead and differs by 26% on the packaged reference (#318). derive_global_descriptors reports the boundary-average form as beta_p_boundary_average; the DD leaf is not matched to OMFIT.
- equilibrium.vacuum_toroidal_field.r0 is corrupt on the VEST database and b0 drifts within a shot (#325); R_0 is replaced by tf.r0, loudly, and b0 is used as stored.
- beta_tor with the stored b0 does not reproduce the OMFIT reference (ratio 1.041); the reference field is unidentified (#238).
- li_3 is the ITER/Jackson third definition: the DD documents no formula for it.
- energy_mhd is (3/2) int p dV of the reconstructed pressure: a reconstruction-derived energy, not a kinetic measurement.
- The global descriptors and the edge-q proxies are returned, not written to equilibrium or summary.
- edge_q_estimate’s equilibrium source takes R_0 B_0 from the stored vacuum_toroidal_field.r0 * b0, not from resolve_reference_major_radius; the magnetics source takes it from tf.
Synthetic diagnostics: predicted signals from a modelled state
A synthetic diagnostic predicts what an instrument would record from a modelled or reconstructed state. VAFT has three: vacuum magnetics from the coil and vessel currents, the equilibrium’s diamagnetic flux, and soft-X-ray chords through a prescribed island. Assumption-completed kinetic profiles (generate_synthetic_kinetic_profiles) are a synthetic state, not a diagnostic, and are not drawn here.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| PF coil currents | measured | $I_{\mathrm{PF},j}(t)$ | pf_active.coil[:].current.data |
|
| Vessel loop currents | standardized IMAS | $I_{\mathrm{loop},k}(t)\ \mathrm{(forward\ circuit\ model)}$ | pf_passive.loop[:].current |
|
| Magnetic equilibrium | reconstructed | $\psi(R, Z),\ F(\psi),\ \psi_{\mathrm{axis}},\ \psi_{\mathrm{b}}$ | equilibrium.time_slice[:].{profiles_2d[0].psi, profiles_1d.f, global_quantities} |
|
| Diamagnetic flux | measured | $\Phi_{\mathrm{dia}}^{\mathrm{meas}}(t)$ | magnetics.diamagnetic_flux[0].data |
|
| Magnetic sensor positions | machine geometry / static data (enters Measured magnetics) | $(R, Z)\ \mathrm{of\ each\ selected\ channel}$ | magnetics.{flux_loop[:].position, b_field_pol_probe[:].position} |
|
| Measured magnetics | measured | $\psi_{\mathrm{FL}},\ B_p\ \mathrm{with\ validity}$ | magnetics.{flux_loop, b_field_pol_probe} |
|
| Vacuum magnetics forward model | derived | $m_{\mathrm{coil}},\ m_{\mathrm{coil+eddy}}\ \mathrm{on\ the\ PF\ time\ grid}, \mathrm{measured\ interpolated\ onto\ it,\ no\ extrapolation}$ | vaft.omas.vacuum_magnetics.synthetic_vacuum_magnetics |
|
| Diamagnetic flux of the equilibrium | derived | vaft.omas.process_wrapper.compute_reconstructed_diamagnetic_flux |
||
| Prescribed island | model assumption / prior (enters Island soft-X-ray forward model) | $(m, n),\ W\ \mathrm{at\ the\ outboard\ midplane},\ \mathrm{phase},\ \omega, \epsilon_0(\psi_N)\ \mathrm{background}$ | ||
| Island soft-X-ray forward model | derived | $\epsilon\ \mathrm{flattened\ in\ the\ island},\ \mathrm{integrated\ along\ each\ chord}$ | vaft.process.soft_x_rays.synthetic_island_soft_x_rays |
|
| SXR chord geometry | machine geometry / static data (enters Island soft-X-ray forward model) | $\mathrm{pencil\ chords,\ one\ poloidal\ plane\ each;\ optional\ clipping\ domain}$ | ||
| Synthetic vacuum magnetics | derived | $\mathrm{measured},\ m_{\mathrm{coil}},\ m_{\mathrm{coil+eddy}}\ \mathrm{per\ channel}$ | IMAS mapping TODO: returned as VacuumChannel records, not written | |
| Diamagnetic constraint | standardized IMAS | $\Phi_{\mathrm{dia}}^{\mathrm{meas}}\ \mathrm{vs}\ \Phi_{\mathrm{dia}}^{\mathrm{rec}}$ | vaft.omas.update.update_equilibrium_constraints_diamagnetic_flux |
equilibrium.time_slice[:].constraints.diamagnetic_flux.{measured, reconstructed} |
| Synthetic SXR brightness | derived | $b(t, \mathrm{chord})\ [\epsilon\cdot\mathrm{m}],\ \mathrm{before\ any\ detector\ response}$ | IMAS mapping TODO: returned (SyntheticSXRResult), not written to soft_x_rays |
Follow-up TODOs (implementation or IMAS mapping):
- There is no synthetic Thomson, interferometer, visible-camera or bolometer model: those measurements cannot be compared with a prediction of the reconstructed state.
- The vacuum magnetics are returned with their measured counterparts and written nowhere; EFIT’s own fitted magnetics (equilibrium constraints.*.reconstructed) are the solver’s native output, not a VAFT forward model.
- The vacuum forward model has no plasma current: it predicts the magnetics only before breakdown or in plasma-free windows.
- The SXR model is forward only: a rigidly rotating island of fixed width, pencil-beam chords, no filter, etendue or noise; it is comparable with a measurement only up to a per-channel calibration, and is not written to soft_x_rays.
Coordinates and COCOS: one transform per layer
Two COCOS transforms, at two layers: the ODS is normalized in place to weber (COCOS 1-8 and per-radian files become 11; a declared 11-18 is left as it is), and an in-memory equilibrium record is converted to any target COCOS. The ODS coordinate writers resolve the flux storage of each slice themselves (as_equilibrium), so they also run on an unnormalized ODS; the record-level radii and angle are COCOS-independent and returned.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Equilibrium as stored | reconstructed | $\psi\ [\mathrm{Wb\ or\ Wb/rad}],\ I_p,\ B_0,\ q,\ F$ | equilibrium.time_slice[:].{profiles_1d.psi, profiles_2d[0].psi, global_quantities} |
|
| Declared COCOS | model choice / convention (enters ODS normalization to weber) | $\mathrm{trusted\ as\ provenance;\ absent\ before}\ #236$ | {equilibrium.ids_properties.cocos, equilibrium.code.parameters.cocos} |
|
| ODS normalization to weber | derived | $\mathrm{declared\ 1\text{-}8}:\ \mathrm{OMAS\ cocos_transform\ to\ 11}, \mathrm{declared\ 11\text{-}18:\ unchanged}, \mathrm{undeclared:\ storage\ probed,\ Wb/rad\ taken\ as\ COCOS\ 1}, \mathrm{equilibrium\ and\ core_profiles\ leaves}$ | vaft.omas.general.equilibrium_psi_to_weber |
|
| Equilibrium in weber (COCOS 11-18) | standardized IMAS | $\psi\ [\mathrm{Wb}];\ \mathrm{declared}\ 11\ \mathrm{only\ after\ a\ transform}, \mathrm{constraints\ untouched}$ | vaft.omas.general.set_ods_cocos |
{equilibrium.time_slice[:], core_profiles.profiles_1d[:], equilibrium.code.parameters.cocos} |
| Normalized poloidal flux | standardized IMAS | $\psi_N\ [-]$ | vaft.omas.update.update_equilibrium_profiles_1d_normalized_psi |
equilibrium.time_slice[:].profiles_1d.psi_norm |
| Toroidal flux coordinate | standardized IMAS | $\rho_{\mathrm{tor}} = \sqrt{|\Phi|/(\pi |B_0|)}$ | vaft.omas.update.update_equilibrium_profiles_1d_toroidal_flux |
equilibrium.time_slice[:].profiles_1d.{phi, rho_tor, rho_tor_norm} |
| Flux-surface grid | standardized IMAS | $(\psi_N, \theta)\ \mathrm{grid},\ \mathrm{surfaces\ =\ scaled\ LCFS}$ | vaft.omas.update.update_equilibrium_profiles_2d_sfl_coordinates |
equilibrium.time_slice[:].profiles_2d[1].{grid.dim1, grid.dim2, grid_type, r, z, psi, theta}; IMAS mapping TODO: stored as grid_type 11 (straight field line), though theta is geometric |
| Equilibrium record | derived | $\mathrm{Wb\ vs\ Wb/rad\ from\ Amp\grave{e}re’s\ law\ round\ the\ LCFS}, \mathrm{COCOS\ declared,\ or\ candidates\ from\ the\ signs\ of}\ B_0, I_p, q$ | vaft.process.equilibrium.as_equilibrium |
|
| Target COCOS | model choice / convention (enters Record conversion) | $1\text{-}18\ (\mathrm{no}\ 9, 10),\ \mathrm{chosen\ by\ the\ caller}$ | ||
| Record conversion | derived | $\psi,\ F,\ q,\ p’,\ FF’,\ I_p,\ B_0\ \times\ \mathrm{their\ cocos_transform\ factors}, p\ \mathrm{and\ the\ axis\ untouched;\ ambiguous\ source\ refused}, \mathrm{returns\ a\ copy\ in\ the\ target\ COCOS,\ in\ memory}$ | vaft.process.equilibrium.convert_cocos |
|
| Normalized radii | derived | $\psi_N,\ \rho_{\mathrm{pol},N} = \sqrt{\psi_N},\ \rho_{\mathrm{tor},N}, \mathrm{COCOS\text{-}independent;\ unavailable\ with\ a\ reason}$ | vaft.process.equilibrium.derive_radial_coordinates |
|
| Straight-field-line angle | derived | $\theta^*\ (\mathrm{PEST})\ \mathrm{at\ any}\ (R, Z)$ | vaft.process.equilibrium.straight_field_line_map |
Follow-up TODOs (implementation or IMAS mapping):
- The ODS writer of the 2-D flux-surface grid uses a geometric approximation (the boundary scaled by sqrt(psi_N) about the axis, the geometric angle) for every requested convention, yet labels it grid_type 11; the true PEST angle exists only in memory (vaft.process.equilibrium.straight_field_line_map).
- equilibrium_psi_to_weber leaves equilibrium.time_slice[:].constraints untouched: VAFT copies them from the DD-conformant magnetics and pf_active IDSs.
- rho_tor integrates q from the axis, so EFIT’s often outlying q[0] moves rho_tor_norm by up to about 0.04 on the innermost surfaces; packaged samples older than #192 still hold the sqrt(psi_N) proxy.
Power balance and confinement: experimental and modelled
Two power balances answer different questions. The experimental Ohmic chain splits the measured loop voltage with l_i3 and takes the stored energy of the equilibrium; the modelled budget uses Spitzer heating and modelled radiation. Neither writes to the summary IDS; the experimental chain is tabulated in the database summary sheet.
| Node | Kind | Variables | API | IDS |
|---|---|---|---|---|
| Plasma current | measured | $I_p(t),\ dI_p/dt$ | magnetics.ip[0].{data, time} |
|
| Loop voltage | measured | $V_{\mathrm{loop}}\ \mathrm{(inboard\ midplane\ loop,\ off\ the\ boundary)}$ | vaft.omas.discharge_timing.inboard_midplane_loop |
magnetics.flux_loop[:].{voltage, flux} |
| Magnetic equilibrium | reconstructed | $l_{i,3},\ W = \tfrac32\int p_{\mathrm{EFIT}}\,dV\ \mathrm{(grid\ quadrature)}$ | equilibrium.time_slice[:].{global_quantities.li_3, profiles_1d.pressure, profiles_2d[0].psi} |
|
| Kinetic profiles | standardized IMAS | $n_e(\rho),\ T_e(\rho)$ | core_profiles.profiles_1d[:].electrons |
|
| Stored-energy source | model choice / convention (enters Ohmic power balance) | $\mathrm{efit_pressure\ (default)\ |\ core_profiles_thermal}$ | ||
| Resistive loop voltage | derived | $V_{\mathrm{res}} = V_{\mathrm{loop}} - V_{\mathrm{ind}},\ L_i = \mu_0 R_0 l_{i,3}/2$ | vaft.process.confinement.resistive_loop_voltage |
|
| Ohmic power balance | derived | $P_{\mathrm{net}} = P_{\mathrm{OH}} - dW/dt\ (\mathrm{DB5\ PLTH}), P_{\mathrm{transport}}\ \mathrm{needs\ a\ measured}\ P_{\mathrm{rad}}:\ \mathrm{NaN}$ | vaft.process.confinement.confinement_power_balance |
|
| Confinement time | derived | $\tau_E\ \mathrm{per\ slice\ the\ caller\ passes\ (default\ every\ slice)}$ | vaft.omas.confinement.ohmic_confinement_ods |
IMAS mapping TODO: not written to the summary IDS; tabulated in the database summary sheet |
| Slice evidence | derived | $\mathrm{rates\ and\ ratios\ per\ slice;\ thresholds\ are\ the\ caller’s}$ | vaft.process.confinement.confinement_slice_evidence |
|
| Confinement scaling fit | inferred | vaft.process.confinement.fit_confinement_scaling |
||
| Impurity policy | model assumption / prior (enters Modelled radiation) | $\mathrm{C,\ O\ at}\ n_I/n_e = 1/86,\ Z_{\mathrm{eff}} = 2\ (\mathrm{assumed})$ | vaft.machine_mapping.core_profiles.vest_core_profiles_policy |
|
| Spitzer Ohmic dissipation | derived | vaft.omas.process_wrapper.compute_ohmic_heating_power_from_core_profiles |
||
| Modelled radiation | derived | $P_{\mathrm{line}} = \int n_e n_I L_Z\,dV\ \mathrm{(ADF11)}$ | vaft.process.atomic.compute_line_radiation_power_series |
|
| Modelled power budget | derived | $P_{\mathrm{rad}} = P_{\mathrm{line}} + P_{\mathrm{sync}} + P_{\mathrm{Br}},\ P_{\mathrm{heat}} = P_{\mathrm{ohm,diss}}, W\ \mathrm{from\ core_profiles}\ (T_i = T_e),\ V\ \mathrm{and}\ I_p\ \mathrm{from\ the\ equilibrium}, dW/dt = 0\ \mathrm{for\ one\ slice}$ | vaft.omas.formula_wrapper.compute_power_balance |
IMAS mapping TODO: returned as a dict (plots only), not written to the summary IDS |
Follow-up TODOs (implementation or IMAS mapping):
- Neither power balance nor the confinement time is written to the summary IDS. The experimental chain’s v_res, P_OH, P_net, W and tau_E,net are tabulated in the database summary sheet (vaft.database); the modelled budget feeds plots only.
- VEST has no bolometer, so no measured P_rad enters: the experimental chain’s transport loss power is NaN and its default is p_net (DB5 PLTH); the modelled budget always subtracts a modelled P_rad, which is a sensitivity study, not a measurement.
- The experimental chain pairs the measured current with EFIT’s l_i3 (a hybrid), and its flux loop is not on the plasma boundary; both are recorded assumptions.
- The stored energy is reconstruction-derived (efit_pressure) or assumes T_i = T_e (core_profiles_thermal); neither is a global energy (#1713).
Semantic pictograms
Concept diagrams can carry small stroke pictograms that name a kind of thing – a database, a
solver, a researcher – so a figure reads at a glance (#2101). They come from one registry,
vaft.diagram.pictograms, so the same concept is drawn the same way in every figure.
A pictogram is a presentation component, not a model output. Its geometry carries no physical
dimension, position or relationship. It never replaces model-derived geometry – flux surfaces,
islands, coordinates, field lines, diagnostic sight lines – which stays with the builders and
vaft.formula. It supplements a label and never replaces one, and it says nothing about
implementation maturity, validation or provenance: a computation icon does not mean a solver
runs, a standardized icon does not mean a product passed validation.
from types import SimpleNamespace
from vaft.diagram.pictograms import badge, corner, pictogram
items = [] # a builder's scene items; the box is any object with x, y, width, height (cm)
vv_box = SimpleNamespace(x=6.0, y=3.0, width=3.2, height=1.2)
items += pictogram("database", (2.0, 3.0), 0.8, role="symbol:stage:database").items
items += badge("validation", corner(vv_box), 0.62, role="symbol:node:vv").items
print(len(items), items[0].role)
pictogram(name, at, size=0.6, *, role, style=PICTOGRAM)returns scene polylines centred onat,sizecm across.badgeputs the icon on a white disc;corner(box)is where a badge clips onto a box’s top-left corner without taking the space of a first text line that spans the box.- Roles start with
symbol:and name the item they annotate (symbol:node:vv), so pictograms never collide with thenode:/edge:roles the topology tests read, and adding one never changes the order of a builder’s existing items. - Style: one stroke weight at every size (
PICTOGRAM, dark blue, 0.7 pt);PICTOGRAM_MUTED(grey, dashed) marks something planned. The styles are inline TikZ options, not template styles, so adding a pictogram re-renders only the diagrams that draw it. - Optical size: every symbol is scaled so the larger side of its drawing fills the same 20/24 of its box, so a narrow icon (a terminal prompt, a flat trace) does not read smaller than a full-frame one.
- Dashes are reserved for state, never part of a symbol’s shape: only
PICTOGRAM_MUTED(planned) is dashed. Generated or assumed data is drawn as what produces it (synthetic: a model box emitting a signal), not with dotted strokes. - Distinct metaphors for neighbouring concepts:
repository(version-controlled source),library(reusable knowledge),archive(long-term preservation),database(structured storage);processing(transformation),workflow(execution order),integration(combining systems),adapter(compatibility),interface(two-way exchange);model(a function),reconstruction(flux surfaces recovered from measurements),inference(an estimate with its uncertainty),synthetic(model-generated data),validation(computed against independent measurements),analysis(results plotted),comparison(results side by side). - Names: figures and the gallery show the display name (
display_name("input_file")is “Input file”); the identifier is what code passes topictogram(). - When not to use one: on every box for symmetry, on a box whose label already is the picture (the database drum), inside physics schematics, or where it would crowd an equation. A figure without icons is often the clearer one.
vaft.diagram.pictogram_catalog() draws the gallery above.
| Identifier | Display name | Meaning | Source |
|---|---|---|---|
experiment |
Experiment | an experiment or laboratory activity | Lucide flask-conical |
tokamak |
Tokamak | a tokamak device or its machine description | VAFT |
signal |
Signal | a raw measured signal | VAFT |
database |
Database | structured data storage | Lucide database |
archive |
Archive | a long-term archive | Lucide archive |
file |
File | a data file or document | Lucide file-text |
input_file |
Input file | an input file for a solver | Lucide file-input |
output_file |
Output file | a solver’s native output file | Lucide file-output |
standardized |
Standardized | a standardized, structured scientific representation (an IMAS-like tree) | Lucide list-tree |
history |
History | a dated record or history | Lucide history |
library |
Library | a body of reusable knowledge | Lucide library |
processing |
Processing | data processing: transforming raw data into qualified data | VAFT |
computation |
Computation | a numerical computation, solver or simulation | Lucide cpu |
cluster |
Cluster | a compute server or HPC cluster | Lucide server |
laptop |
Laptop | a local workstation | Lucide laptop |
calculation |
Calculation | a deterministic calculation of derived quantities | Lucide calculator |
model |
Model | a mathematical or theoretical model (a function, not a summation) | Lucide square-function |
reconstruction |
Reconstruction | a state (flux surfaces) reconstructed from measurements | VAFT |
inference |
Inference | an estimate of an unknown quantity, with its uncertainty | VAFT |
synthetic |
Synthetic | data generated by a model or completed by assumptions, never measured | VAFT |
parameters |
Parameters | chosen parameters, settings or prior assumptions | Lucide sliders-horizontal |
coordinates |
Coordinates | a coordinate, sign or model convention | Lucide move-3d |
code |
Code | a programming interface | Lucide code |
source_code |
Source code | an externally maintained source code | Lucide file-code |
terminal |
Terminal | a command-line interface | Lucide terminal |
monitor |
Monitor | a graphical interface | Lucide monitor |
repository |
Repository | a version-controlled code repository and its documentation | Lucide folder-git-2 |
integration |
Integration | the combination of different systems or data structures | Lucide puzzle |
adapter |
Adapter | an adapter mediating compatibility between two systems | Lucide cable |
interface |
Interface | a two-way data exchange between two systems | VAFT |
package |
Package | an installable package or environment | Lucide package |
researcher |
Researcher | a human researcher | Lucide user |
collaboration |
Collaboration | a group of researchers | Lucide users |
ai_agent |
AI agent | an AI agent | Lucide bot |
workflow |
Workflow | a traceable workflow or pipeline | Lucide workflow |
validation |
Validation | agreement of computed values with independent measurements | VAFT |
comparison |
Comparison | results compared side by side | VAFT |
analysis |
Analysis | analysis and visualization of results | Lucide chart-line |
discovery |
Discovery | a discovery or new question | Lucide lightbulb |
Geometry and provenance. General-purpose icons are vendored verbatim from a pinned
Lucide release (lucide-static 1.52.0, ISC; the Feather-derived icons
MIT) in vaft/diagram/_lucide.py, each with its sha256; the licence is reproduced in
THIRD_PARTY_NOTICES.md.
Where a generic icon would mislead, VAFT draws its own scientific symbol in the same 24-unit grid
(tokamak, signal, processing, reconstruction, inference, synthetic, interface, validation, comparison). Only a restricted SVG subset is
converted (path with M L H V C S Q A Z, line, polyline, circle, ellipse, rect); curves are
flattened within a fixed tolerance, so a symbol always yields the same points and the output stays
vector-native and offline in TikZ, SVG, PDF and the Matplotlib animation path.
Adding a symbol. Pick a semantic name for the concept, not for the picture. Prefer an icon from
the pinned Lucide release (re-extract it with its hash into _lucide.py); draw a VAFT symbol only
when a generic icon would misstate the science. Register it in pictograms._REGISTRY with its
meaning, add it to the table above, and run python -m vaft.diagram.build – the gallery and every
diagram that uses it re-render, nothing else does.
Using the committed assets
The reference SVGs live in docs/assets/diagrams/ and are the artifacts to embed anywhere:
- Documentation pages:
. - The README, notebooks and slides: link or copy the SVG. It is self-contained, with glyphs stored as paths and no external fonts or images.
They do not ship in the wheel. An installed package builds any diagram from its packaged TikZ template.
Regenerating and checking
python -m vaft.diagram.build # re-render diagrams whose TikZ source changed
python -m vaft.diagram.build --check # verify the committed assets; needs no TeX
Each SVG carries its own build record: one comment line after the XML declaration holding the call,
the SHA-256 of the generated TikZ document it was rendered from, and the SHA-256 of the SVG without
that line. Because the record lives in the diagram’s own file, two pull requests that change different
diagrams share no generated file and do not conflict (#1750). --check rebuilds the TikZ in pure
Python and fails in five cases: an asset is stale, an asset was edited by hand, an asset is missing,
an asset has no record, or an asset is orphaned. CI runs it on every pull request. Freshness is judged on the source
and not on SVG bytes, because two dvisvgm releases write different but equally correct SVG for the
same picture. Rendering needs latex and dvisvgm, which TeX Live and MacTeX provide. The generated
.tex, PDF, PNG and LaTeX by-products are never committed.