Process reference: equilibrium
Generated from the docstrings of vaft.process.equilibrium:
80 public functions. Each entry is what
vaft.process.describe("equilibrium.<name>") prints.
All process categories
Overview
Physics only. Nothing here reads or writes an ODS: every function takes arrays
and returns arrays or plain records, so the same code serves a GEQDSK, an ODS
slice and an analytic model. The adapters that turn those into one record, and
the parametric models fitted to them, live in _equilibrium_parametric and
are re-exported through this module, which is their stable import location.
What the module does, in the order a flux map is usually consumed::
psi(R, Z) on a grid
-> radial coordinates: psi_norm, and the toroidal radius through q
-> flux surfaces: contours, their shape, their averages
-> fields: the poloidal field from psi, the toroidal field from F
-> global scalars: diamagnetism, the Shafranov integrals, force balance
| Symbol | Meaning | Unit |
|---|---|---|
| psi | poloidal flux | Wb/rad |
| psi_norm | (psi - psi_axis)/(psi_boundary - psi_axis) | - |
| F | poloidal current function, R*B_phi | T m |
| B_p | poloidal field magnitude | T |
| q | safety factor | - |
| k | Sauter Eq. 20 prefactor carrying sign and 2*pi | - |
Flux is per radian unless a function says otherwise. The poloidal field
from psi carries a factor k = sigma_RphiZ * sigma_Bp / (2*pi)**e_Bp [1],
which folds the orientation sign and the 2pi normalization together. A
function that forms a field from psi takes a COCOS index, and with none it
uses the historical weber-per-radian form. Getting this wrong scales the field
by 2pi and the poloidal beta by its square, plausibly and silently. A function
that takes a precomputed field instead inherits whatever convention produced
it and cannot check it.
Three radial coordinates, never interchangeable. rho_tor_norm equals
sqrt(psi_norm), which is rho_pol_norm, only for a flat safety factor.
derive_radial_coordinates returns all three with an explicit record of
which is unavailable and why.
Flux maps are indexed (R, Z), matching the IMAS two-dimensional profile layout. Boundary contours run counter-clockwise, because the Shafranov outward normal is only outward then.
Functions
as_equilibrium | Adapt a GEQDSK, ODS, IMAS handle, or native model to one equilibrium record. Convention |
calculate_average_boundary_poloidal_field | Contour-length average of the poloidal field around a closed boundary. Convention |
calculate_diamagnetism | Diamagnetism from its volume-integral definition. Convention |
calculate_q_profile_from_psi | Calculate the safety factor profile q(psi) from a 2D poloidal flux map and F(psi). Convention |
calculate_reconstructed_diamagnetic_flux | Diamagnetic flux reconstructed from the equilibrium’s own toroidal field. Convention |
check_equilibrium_requirements | Whether an equilibrium carries what a named algorithm needs to run. Convention |
compare_contours | How far apart two closed contours are, point to curve. Convention |
computed_diamagnetism_from_phi | EFIT-style diamagnetism from an already-computed diamagnetic flux. Convention |
connection_length_map | Connection length of the field line through each of many starting points. Convention |
contour_shape_parameters | Shape parameters of one closed flux-surface contour. Convention |
contour_shaping_observables | Model-independent shaping observables of one closed contour: indentation and asymmetry. Convention |
convert_cocos | Convert an equilibrium between two explicitly known COCOS conventions. Convention |
current_centroid | Total toroidal current and its centroid, from a gridded current density. Convention |
current_covariance | Second central moment tensor of a gridded current density. Convention |
current_moment | One moment integral (R - R_0)**p (Z - Z_0)**q J_phi dA of a gridded current density.
Convention |
derive_boundary_representation | Classify the plasma boundary and derive its geometric diagnostics. Convention |
derive_current_moments | Reduce an equilibrium’s toroidal current density to its low-order moments. Convention |
derive_global_descriptors | Global scalar descriptors of one equilibrium, each with its derivation record. Convention |
derive_radial_coordinates | The three normalized radial coordinates, each with its derivation record. Convention |
efit_virial_volume_integrals | EFIT-style weighted volume integrals for the virial closures. Convention |
ejiri_mirror_geometry | Ejiri mirror-confinement proxy for one magnetic snapshot. Convention |
equilibrium_field_on_grid | (B_R, B_Z, B_phi) on the whole (R, Z) grid, each (nR, nZ).
Convention |
evaluate_fourier_surface | Points on a Fourier-represented flux surface at given arc-length angles. Convention |
evaluate_guazzotto_freidberg | Normalized flux and its analytic derivatives of a Guazzotto-Freidberg equilibrium. Convention |
evaluate_miller | Points on a Miller-parameterized flux surface at given poloidal angles. Convention |
evaluate_mxh_chebyshev | Flux-surface points of an MXH-Chebyshev representation. Convention |
evaluate_solovev | Evaluate an analytic Solov’ev equilibrium on a set of points. Convention |
extract_flux_surface_contours | Iso-flux contours at requested normalized levels, by marching squares. Convention |
find_rational_surfaces | Where a toroidal mode resonates: the surfaces with q = m / n. |
find_stationary_points | Locate the O-points and saddles of psi to sub-grid accuracy. Convention |
fit_fourier_surface | Represent one closed flux-surface contour as a truncated Fourier series. Convention |
fit_fourier_surface_sequence | Represent a sequence of internal flux surfaces as Fourier series. Convention |
fit_free_boundary_coils | Fit physical PF circuit currents to a prescribed LCFS in free space. Convention |
fit_miller_sequence | Fit Miller parameters across a sequence of flux surfaces, with radial derivatives. Convention |
fit_miller_surface | Fit the Miller shape parameters to one closed flux-surface contour. Convention |
fit_mxh_chebyshev | Represent an equilibrium’s flux surfaces by MXH shapes with Chebyshev radial profiles. Convention |
fit_solovev | Represent an equilibrium by the Solov’ev solution closest to its flux map. Convention |
flux_surface_quantities | Flux-surface averages and shape parameters on each normalized flux level. Convention |
fractional_cell_weights_from_boundary | Fractional area of each grid cell that lies inside the plasma boundary. Convention |
grad_shafranov_operator | The Grad-Shafranov elliptic operator on a rectangular grid. Convention |
grad_shafranov_residual | How far an equilibrium is from satisfying its own force balance, surface by surface. Convention |
grad_shafranov_residual_modes | Project the Grad-Shafranov residual onto poloidal harmonics on nested flux surfaces. Convention |
guazzotto_freidberg_parameters | The dimensionless plasma parameters of Section 6 for a Guazzotto-Freidberg equilibrium. Convention |
guazzotto_freidberg_to_equilibrium | Export a Guazzotto-Freidberg equilibrium as a gridded record in physical units. Convention |
integrate_romero_closure | Integrate Romero’s first-order closure from a boundary-voltage history. Convention |
lab_to_straight_field_line | Relabel a geometric poloidal angle into the straight-field-line one. Convention |
make_equilibrium_field_interpolator | Build a callable giving the full magnetic field anywhere on one time slice. Convention |
make_vacuum_field_interpolator | Build a callable giving the vacuum magnetic field anywhere on one grid. Convention |
miller_surfaces | A family of Miller surfaces for a shape scan. Convention |
parallel_current_from_toroidal | Convert a shell toroidal driven current to the IMAS parallel current. Convention |
pest_angle_from_jacobian_angle | Relabel a DCON flux-surface angle into the PEST angle, from R alone.
Convention |
plasma_cell_weights | Fraction of each grid cell that is plasma, from the boundary outline when there is one. Convention |
poloidal_field_at_boundary | Poloidal field on a boundary contour, from the flux map by bicubic spline. Convention |
prepare_boundary_for_shafranov | Condition a boundary contour into the form the Shafranov integrals require. Convention |
psi_to_radial | Inboard and outboard major radius of each flux value, along the axis row. Convention |
psi_to_rho | Normalized toroidal-flux radius at one poloidal flux value, by integrating q. Convention |
psi_to_rz | Map a flux-surface profile onto the two-dimensional grid through psi. Convention |
r_at_z_extremum | Major radius where a contour reaches its highest or lowest point. Convention |
radial_to_psi | Poloidal flux at a major radius on the midplane row of the flux map. Convention |
rational_surfaces | Every flux surface where the safety factor takes a requested rational value. Convention |
resistive_layer_at | Resistivity and mass density at flux surfaces someone else located. |
resistive_layer_parameters | Resistivity and mass density at each rational surface of a toroidal mode. |
rho_to_psi | Poloidal flux at one normalized toroidal-flux radius, by root finding. Convention |
romero_flux_balance | Romero’s exact voltage and volt-second balance over a sampled discharge. Convention |
scale_boundary_conformal | Scale a closed boundary about a centre, giving a contour conformal to it. Convention |
shafranov_integrals | The three Shafranov boundary integrals and the virial coefficient. Convention |
solovev_example | Build a shaped analytic Solov’ev equilibrium in a chosen boundary topology. Convention |
solovev_shape_constraints | The Cerfon-Freidberg boundary conditions for a shaped, possibly diverted, plasma. Convention |
solovev_to_equilibrium | Export an analytic Solov’ev model as a gridded equilibrium record. Convention |
solovev_xpoint_constraints | The three conditions that make a point an X-point on the boundary flux surface. |
solve_guazzotto_freidberg | Solve the Guazzotto-Freidberg analytic equilibrium for a shape and a beta parameter. Convention |
solve_solovev_constraints | Solve for the Solov’ev coefficients that satisfy a set of geometric constraints. Convention |
straight_field_line_angle_on_grid | The PEST straight-field-line angle on every node of a flux map. Convention |
straight_field_line_map | The PEST straight-field-line poloidal angle of a flux map, built once. Convention |
straight_field_line_tables | Per-surface tables taking the geometric poloidal angle to the code’s own. Convention |
trace_field_line | Trace one magnetic field line through the equilibrium field. Convention |
validate_equilibrium | Deprecated compatibility wrapper for check_equilibrium_requirements.
Deprecated |
virial_alpha_conformal_annulus | Virial closure coefficient from a thin annulus conformal to the LCFS. Convention |
virial_alpha_thin_annulus | Virial closure coefficient in the thin-annulus limit, from the boundary field. Convention |
volume_average | Volume average of a gridded quantity over the confined region. Convention |
No function matches.
as_equilibrium
Machine-independentConvention-sensitive
source
as_equilibrium(source, *, time_index=0, profile_index=0, convention=None)
Show source vaft/process/_equilibrium_parametric.py, lines 301–376
def as_equilibrium(
source: Any, *, time_index: int = 0, profile_index: int = 0,
convention: int | None = None,
) -> EquilibriumData:
"""Adapt a GEQDSK, ODS, IMAS handle, or native model to one equilibrium record.
The single entry point every algorithm here goes through, so that they operate
on one normalized shape regardless of where the equilibrium came from.
Parameters
----------
source : EquilibriumData, ODS, GEQDSK, IMAS equilibrium IDS, path or mapping
A record is returned as is, or converted when a convention is asserted; a
path or path-like is read as a GEQDSK; an object exposing an OMAS export
is exported first; a native IDS root is wrapped [-].
time_index : int, optional
Which time slice to take from a multi-slice source [-].
profile_index : int, optional
Which 2-D profile grid to take from a slice carrying more than one [-].
convention : int, optional
A COCOS index the caller asserts for *source*, 1 to 18. When given it
always wins over identification, and the result is converted to it [-].
Returns
-------
EquilibriumData
The normalized record, with its ``convention`` set from the assertion when
one was made and from identification otherwise [-].
Convention
----------
Normalizes *shape*, not *convention*: the returned record is not converted to
any internal standard unless *convention* asks for it, so its psi may be in
weber or weber per radian and only ``convention.psi_per_radian`` says which.
A record built without a convention reports neither, and downstream field
calculations then fall back to the historical per-radian form.
Applicability
-------------
Machine-independent.
Limitations
-----------
A GEQDSK is identified as a two-dimensional grid by the presence of its
characteristic keys, so a mapping that happens to carry them is treated as
one. Only the requested time slice and profile grid are read; everything else
in a multi-slice source is discarded.
Provenance
----------
.. [1] The per-source adapters below preserve each format's own layout
conventions; see :func:`vaft.process.cocos.identify_convention` for how the
convention is inferred when the caller does not assert one.
"""
if isinstance(source, EquilibriumData):
return convert_cocos(source, convention) if convention is not None else source
if isinstance(source, (str, bytes)) or hasattr(source, "__fspath__"):
from vaft.data.eqdsk import read_geqdsk
source = read_geqdsk(source)
if hasattr(source, "to_omas") and not hasattr(source, "mapping"):
source = source.to_omas()
# A native equilibrium IDS is already the authoritative root object but,
# unlike ODS, it does not contain an outer ``equilibrium`` mapping. Wrap
# that root and let the same path adapter traverse native attributes/AoS.
metadata = getattr(source, "metadata", None)
if getattr(metadata, "name", None) == "equilibrium":
source = {"equilibrium": source}
mapping = source.mapping if hasattr(source, "mapping") else source
try:
keys = set(mapping.keys())
except Exception:
keys = set()
if {"NW", "NH", "PSIRZ"}.issubset(keys):
return _from_geqdsk(source, convention)
return _from_ods(source, time_index, profile_index, convention)Adapt a GEQDSK, ODS, IMAS handle, or native model to one equilibrium record.
The single entry point every algorithm here goes through, so that they operate on one normalized shape regardless of where the equilibrium came from.
| Name | Type | Unit | Description |
|---|---|---|---|
source | EquilibriumData, ODS, GEQDSK, IMAS equilibrium IDS, path or mapping | - | A record is returned as is, or converted when a convention is asserted; a path or path-like is read as a GEQDSK; an object exposing an OMAS export is exported first; a native IDS root is wrapped. |
time_index | int, optional | - | Which time slice to take from a multi-slice source. |
profile_index | int, optional | - | Which 2-D profile grid to take from a slice carrying more than one. |
convention | int, optional | - | A COCOS index the caller asserts for source, 1 to 18. When given it always wins over identification, and the result is converted to it. |
| Returns | |||
| — | EquilibriumData | - | The normalized record, with its |
Convention
Normalizes shape, not convention: the returned record is not converted to
any internal standard unless convention asks for it, so its psi may be in
weber or weber per radian and only convention.psi_per_radian says which.
A record built without a convention reports neither, and downstream field
calculations then fall back to the historical per-radian form.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
A GEQDSK is identified as a two-dimensional grid by the presence of its characteristic keys, so a mapping that happens to carry them is treated as one. Only the requested time slice and profile grid are read; everything else in a multi-slice source is discarded.
Provenance
- The per-source adapters below preserve each format’s own layout conventions; see
vaft.process.cocos.identify_conventionfor how the convention is inferred when the caller does not assert one.
calculate_average_boundary_poloidal_field
Machine-independentConvention-sensitive
source
calculate_average_boundary_poloidal_field(R_bdry, Z_bdry, B_p_bdry)
Show source vaft/process/equilibrium.py, lines 1078–1140
def calculate_average_boundary_poloidal_field(R_bdry, Z_bdry, B_p_bdry):
"""Contour-length average of the poloidal field around a closed boundary.
Parameters
----------
R_bdry : array_like
Major radius of the contour points [m].
Z_bdry : array_like
Height of the contour points [m].
B_p_bdry : array_like
Poloidal field magnitude at those points [T].
Returns
-------
float
The average poloidal field, the contour integral of the field over the
contour length [T].
Convention
----------
Weighted by arc length, not by point count, so an unevenly sampled contour
still gives the physical average. The midpoint rule is used between
consecutive points, and the contour is treated as closed. This is the
normalizing field that the poloidal beta and the diamagnetism are taken
against, so it must come from the same convention as the field that produced
it.
Applicability
-------------
Machine-independent.
Limitations
-----------
Midpoint rule on the given points, so a coarsely sampled contour underweights
regions of high curvature. Takes the field magnitudes as given and cannot
check they were computed in a consistent convention.
Provenance
----------
.. [1] The definition used with :func:`shafranov_integrals` and
:func:`calculate_diamagnetism` as their reference field.
"""
# 1. 배열이 닫혀있는지 확인 (마지막 점 != 첫 점이면 닫아줌)
if (R_bdry[0] != R_bdry[-1]) or (Z_bdry[0] != Z_bdry[-1]):
R_bdry = np.append(R_bdry, R_bdry[0])
Z_bdry = np.append(Z_bdry, Z_bdry[0])
B_p_bdry = np.append(B_p_bdry, B_p_bdry[0])
# 2. 미소 길이 성분 계산 (dl)
dR = np.diff(R_bdry)
dZ = np.diff(Z_bdry)
dl = np.sqrt(dR**2 + dZ**2)
# 3. 적분 구간의 대푯값 (Midpoint rule or Trapezoidal)
B_p_mid = 0.5 * (B_p_bdry[:-1] + B_p_bdry[1:])
# 4. 선적분 수행
L_total = np.sum(dl) # ∮ dl
integral_Bp = np.sum(B_p_mid * dl) # ∮ B_p dl
B_pa = integral_Bp / L_total
return B_paContour-length average of the poloidal field around a closed boundary.
| Name | Type | Unit | Description |
|---|---|---|---|
R_bdry | array_like | m | Major radius of the contour points. |
Z_bdry | array_like | m | Height of the contour points. |
B_p_bdry | array_like | T | Poloidal field magnitude at those points. |
| Returns | |||
| — | float | T | The average poloidal field, the contour integral of the field over the contour length. |
Convention
Weighted by arc length, not by point count, so an unevenly sampled contour still gives the physical average. The midpoint rule is used between consecutive points, and the contour is treated as closed. This is the normalizing field that the poloidal beta and the diamagnetism are taken against, so it must come from the same convention as the field that produced it.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Midpoint rule on the given points, so a coarsely sampled contour underweights regions of high curvature. Takes the field magnitudes as given and cannot check they were computed in a consistent convention.
Provenance
- The definition used with
shafranov_integralsandcalculate_diamagnetismas their reference field.
calculate_diamagnetism
Machine-independentConvention-sensitive
source
calculate_diamagnetism(R_grid, Z_grid, psi_RZ, psi_axis, psi_lcfs, psiN_1d, f_1d, f_vac_val, B_pa, V_p=None, weights=None)
Diamagnetism from its volume-integral definition.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid | array_like | m | Major-radius grid axis. |
Z_grid | array_like | m | Height grid axis. |
psi_RZ | array_like | Wb/rad | Poloidal flux on the grid, indexed |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis. |
psi_lcfs | float | Wb/rad | Poloidal flux at the last closed flux surface. |
psiN_1d | array_like | - | Normalized poloidal flux the poloidal-current profile is given on. |
f_1d | array_like | T m | Poloidal current function |
f_vac_val | float | T m | The same function at the boundary, standing in for the vacuum field |
B_pa | float | T | Boundary-average poloidal field, the normalizing scale, strictly positive |
V_p | float, optional | m^3 | Plasma volume. Computed from the same grid and mask when not given |
weights | array_like, optional | - | Fraction of each cell inside the plasma, from |
| Returns | |||
| — | float | - | The diamagnetism, positive for a diamagnetic plasma and negative for a paramagnetic one. |
Convention
mu_i = (1/(B_pa^2 * V)) * integral (B_tv^2 - B_t^2) dV, with the toroidal
fields taken as the poloidal current function over the major radius and the
axisymmetric volume element. Positive means the plasma reduced the toroidal
field. A result of the unexpected sign usually means the vacuum proxy was
taken on axis rather than at the boundary, or that the poloidal current
profile’s sign convention disagrees with the equilibrium it came from.
Raises
ValueErrorThe boundary-average poloidal field is not positive.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
The same per-cell boundary mask as volume_average, so the boundary is
resolved only to the grid. The vacuum field is a single boundary value rather
than a profile.
Provenance
- The volume-integral definition of diamagnetism; the surface-integral form is
calculate_reconstructed_diamagnetic_flux, and the two should agree for a consistent equilibrium.
calculate_q_profile_from_psi
Machine-independentConvention-sensitive
source
calculate_q_profile_from_psi(psi_grid, R, Z, f_profile, psi_axis=None, psi_boundary=None, levels_norm=None, *, axis_rz=None, boundary=None, cocos=11, sigma_ip=1, sigma_b0=1, min_points=16, return_details=False)
Calculate the safety factor profile q(psi) from a 2D poloidal flux map and F(psi).
| Name | Type | Unit | Description |
|---|---|---|---|
psi_grid | array_like | Wb or Wb/rad | Poloidal flux on the grid, shaped |
R | array_like | m | Major-radius grid axis. |
Z | array_like | m | Height grid axis. |
f_profile | float, array_like, callable, or tuple of array_like | T m | Poloidal current function |
psi_axis | float, optional | Wb or Wb/rad | Poloidal flux at the magnetic axis, in the convention |
psi_boundary | float, optional | Wb or Wb/rad | Poloidal flux at the plasma boundary/LCFS, in the convention |
levels_norm | sequence of float, optional | - | Normalized flux levels to evaluate on, 0 on axis and 1 at the boundary. |
axis_rz | tuple of float, optional | m | Magnetic axis coordinates |
boundary | tuple of np.ndarray, optional | m | Boundary outline |
cocos | int, optional | - | COCOS coordinate convention index (1-8, 11-18) describing the input conventions and target sign for |
sigma_ip | int, optional | - | Sign of plasma current (+1 or -1) in the equilibrium coordinate system. |
sigma_b0 | int, optional | - | Sign of toroidal field (+1 or -1) in the equilibrium coordinate system. |
min_points | int, optional | - | Fewest contour vertices a level needs before it is treated as resolved. |
return_details | bool, optional | - | Whether to return diagnostic parameters and the full flux-surface quantities alongside the safety factor array. |
| Returns | |||
| — | np.ndarray or dict of str to Any | - | If |
Convention
In Sauter and Medvedev (2013), the safety factor is defined as:
\(q(\psi) = \frac{F(\psi)}{2\pi} \oint \frac{dl_p}{R^2 B_p}
= \frac{F(\psi)}{2\pi} (2\pi)^{e_{B_p}} \oint \frac{dl_p}{R |\nabla\psi|}\)
Expressed in terms of the geometric flux-surface averages computed by
flux_surface_quantities (which operates in Wb/rad, $e_{B_p} = 0$, $dV/d\psi$ per radian):
\(q(\psi) = \sigma \cdot \frac{F(\psi)}{(2\pi)^2} \left\langle \frac{1}{R^2} \right\rangle \frac{dV}{d\psi}\)
where $\langle 1/R^2 \rangle$ is gm1, $dV/d\psi$ is dvolume_dpsi, and $\sigma$ is
the orientation sign from Sauter Eq. 23: $\sigma_q = \sigma_{Ip} \sigma_{B0} \sigma_{\rho\theta\varphi}$.
When cocos indicates full Weber storage ($e_{B_p} = 1$, COCOS 11-18), the input fluxes are
divided by $2\pi$ to enter the contour tracing engine, and the resulting profile carries the
exact COCOS-mandated sign.
Raises
ValueErrorThe flux map is not shaped to(len(R), len(Z)),psi_axisorpsi_boundarycannot be determined or are equal, orf_profileformat is invalid.
Processing steps · Defaults · Applicability · Provenance
Processing steps
- Validate grid dimensions and identify the flux conversion factor from
cocos. - Normalize or deduce
psi_axisandpsi_boundary, converting to Wb/rad if necessary. - Evaluate and interpolate
f_profileonto the requestedlevels_norm. - Call
flux_surface_quantitiesto trace contours and compute geometric averages. - Apply on-axis quadratic extrapolation in radius (linear in normalized flux $\psi_N$) to resolve $q_0$.
- Attach the COCOS sign factor $\sigma_q$ and package the output.
Defaults
levels_norm defaults to 65 points from 0 to 1 (numerical convenience).
cocos=11 matches the IMAS standard data dictionary convention.
Applicability
Machine-independent. Works for any 2D tokamak poloidal flux map.
Provenance
- O. Sauter and S. Yu. Medvedev, Comput. Phys. Commun. 184 (2013) 293, Eq. (17) and Table I for COCOS relations and safety factor definitions.
- J. Wesson, Tokamaks, 4th ed., Oxford University Press (2011), Sec. 3.4.
calculate_reconstructed_diamagnetic_flux
Machine-independentConvention-sensitive
source
calculate_reconstructed_diamagnetic_flux(R_grid, Z_grid, psi_RZ, psi_axis, psi_lcfs, psiN_1d, f_1d, f_vac_val, weights=None)
Diamagnetic flux reconstructed from the equilibrium’s own toroidal field.
The surface integral of the difference between the plasma toroidal field and the vacuum field it displaces. Physics only: it reads no ODS and writes none.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid | array_like | m | Major-radius grid axis. |
Z_grid | array_like | m | Height grid axis. |
psi_RZ | array_like | Wb/rad | Poloidal flux on the grid, indexed |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis. |
psi_lcfs | float | Wb/rad | Poloidal flux at the last closed flux surface. |
psiN_1d | array_like | - | Normalized poloidal flux the poloidal-current profile is given on. |
f_1d | array_like | T m | Poloidal current function |
f_vac_val | float | T m | The same function at the boundary, standing in for the vacuum field |
weights | array_like, optional | - | Fraction of each cell inside the plasma, from |
| Returns | |||
| — | float | Wb | The reconstructed diamagnetic flux, negative for a diamagnetic plasma |
Convention
The sign follows the physics, not a storage choice: a diamagnetic plasma reduces the toroidal field inside it, so the integral is negative. Only the normalized flux enters, so the absolute unit of the three flux arguments cancels as long as they agree. The vacuum proxy must be taken at the boundary, not on axis, or the difference is offset everywhere.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Map the poloidal current profile onto the grid through
psi_to_rz. - Form the plasma and vacuum toroidal fields as that function over the major radius.
- Integrate their difference over the cross-section inside the boundary.
Applicability
Machine-independent.
Limitations
A cross-sectional integral on the grid, so the boundary is resolved only to the cell. The vacuum field is taken as a single boundary value rather than a profile, which is exact only where the poloidal current is flat outside the plasma.
Provenance
- The definition of diamagnetic flux as the toroidal-field deficit integrated over the plasma cross-section, in the form the EFIT-style workflow this package reproduces uses.
check_equilibrium_requirements
Machine-independentConvention-sensitive
source
check_equilibrium_requirements(equilibrium, *, required_for='general')
Show source vaft/process/_equilibrium_parametric.py, lines 379–454
def check_equilibrium_requirements(
equilibrium: EquilibriumData, *, required_for: str = "general"
) -> ValidationReport:
"""Whether an equilibrium carries what a named algorithm needs to run.
A **precondition**, not a scientific verdict (issue #337): it answers "can the
global, Miller, edge or Solov'ev algorithms be applied to this input", which is
why the answer depends on which one is asked about. Whether the equilibrium is
*credible* is a different question, asked by :mod:`vaft.validation` (#72).
Renamed from ``validate_equilibrium``, which read as the latter and is now the
name of the latter.
Parameters
----------
equilibrium : EquilibriumData
The record to check [-].
required_for : str, optional
Which requirement set to apply: ``general``, ``global``, ``miller``,
``edge`` or ``solovev`` [-].
Returns
-------
ValidationReport
One issue per unmet requirement, at error severity for a missing or
malformed field and warning severity for an ambiguous or self-contradictory
convention. An empty report means the algorithm can run [-].
Convention
----------
Reports two convention problems without resolving either: an ambiguous COCOS,
where identification left more than one candidate, and a declared COCOS that
contradicts the signs actually observed. Both are warnings, because an
algorithm can still run on a wrongly labelled equilibrium; it will simply be
wrong in a way this function refuses to hide.
Applicability
-------------
Machine-independent.
Limitations
-----------
*required_for* is a free string; an unrecognized value applies the general
requirements rather than being rejected. A clean report says the fields are
present and shaped correctly, not that their values are physical.
Provenance
----------
.. [1] Issue #337 for the split between a precondition and a scientific
verdict, and #253 for the validation layer that owns the latter.
"""
issues: list[ValidationIssue] = []
if equilibrium.psi is not None and equilibrium.r is not None and equilibrium.z is not None:
expected = (equilibrium.r.size, equilibrium.z.size)
if equilibrium.psi.shape != expected:
issues.append(ValidationIssue("error", "psi_shape", "psi", f"psi shape {equilibrium.psi.shape} must be {expected}"))
elif required_for in {"global", "miller", "edge", "solovev"}:
issues.append(ValidationIssue("error", "missing_grid", "psi", "R, Z, and 2-D psi are required"))
if equilibrium.psi_axis is None or equilibrium.psi_boundary is None:
issues.append(ValidationIssue("error", "missing_flux_bounds", "psi_axis", "psi_axis and psi_boundary are required"))
elif equilibrium.psi_axis == equilibrium.psi_boundary:
issues.append(ValidationIssue("error", "degenerate_flux", "psi_boundary", "psi_boundary must differ from psi_axis"))
if equilibrium.lcfs is None or equilibrium.lcfs.r.size < 3:
issues.append(ValidationIssue("error" if required_for in {"global", "miller", "edge"} else "warning", "missing_lcfs", "lcfs", "a closed LCFS with at least three points is required"))
if equilibrium.convention.ambiguous:
candidates = equilibrium.convention.candidates or ("none",)
issues.append(ValidationIssue("warning", "ambiguous_cocos", "convention", f"COCOS is ambiguous ({candidates}); pass convention= explicitly before conversion"))
if equilibrium.convention.contradicted:
# A declaration the data does not support: a stale GEQDSK CASE token, a
# mislabelled ODS, or an explicit argument that does not match the file.
issues.append(ValidationIssue(
"warning", "cocos_declared_conflicts_with_signs", "convention",
f"COCOS {equilibrium.convention.cocos} is declared ({equilibrium.convention.source}) "
f"but the observable signs and flux scale support "
f"{equilibrium.convention.identified}; the declaration is being used as given",
))
return ValidationReport(tuple(issues))Whether an equilibrium carries what a named algorithm needs to run.
A precondition, not a scientific verdict (issue #337): it answers “can the
global, Miller, edge or Solov’ev algorithms be applied to this input”, which is
why the answer depends on which one is asked about. Whether the equilibrium is
credible is a different question, asked by vaft.validation (#72).
Renamed from validate_equilibrium, which read as the latter and is now the
name of the latter.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData | - | The record to check. |
required_for | str, optional | - | Which requirement set to apply: |
| Returns | |||
| — | ValidationReport | - | One issue per unmet requirement, at error severity for a missing or malformed field and warning severity for an ambiguous or self-contradictory convention. An empty report means the algorithm can run. |
Convention
Reports two convention problems without resolving either: an ambiguous COCOS, where identification left more than one candidate, and a declared COCOS that contradicts the signs actually observed. Both are warnings, because an algorithm can still run on a wrongly labelled equilibrium; it will simply be wrong in a way this function refuses to hide.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
required_for is a free string; an unrecognized value applies the general requirements rather than being rejected. A clean report says the fields are present and shaped correctly, not that their values are physical.
Provenance
- Issue #337 for the split between a precondition and a scientific verdict, and #253 for the validation layer that owns the latter.
compare_contours
Machine-independentConvention-sensitive
source
compare_contours(reference_r, reference_z, other_r, other_z)
How far apart two closed contours are, point to curve.
Every vertex of each contour is measured to the nearest point on the other contour’s polyline – to a segment, not to a vertex – and the averages weight each vertex by the arc length it stands for, half of each adjoining segment. So the result does not depend on where either contour starts, which way it runs, or how densely or unevenly it is sampled, beyond the sampling’s own resolution of the curve.
| Name | Type | Unit | Description |
|---|---|---|---|
reference_r | array_like | m | Major radius of the reference contour. |
reference_z | array_like | m | Height of the reference contour. |
other_r | array_like | m | Major radius of the contour compared against it. |
other_z | array_like | m | Height of the contour compared against it. |
| Returns | |||
| — | dict of str to float | - |
|
Convention
Both contours are treated as closed: the last point joins the first whether or not it repeats it. The measure is symmetric – both contours’ vertices contribute – and orientation-independent.
Raises
ValueErrorEither contour has fewer than three points.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
A separation is not a correspondence: two contours can be close everywhere and still differ in where along the curve a feature sits. Time grows as the product of the two point counts; memory is bounded by working through the vertices in blocks.
Provenance
- Point-to-segment distance and the symmetric Hausdorff distance are standard computational geometry.
computed_diamagnetism_from_phi
Machine-independentConvention-sensitive
source
computed_diamagnetism_from_phi(phi_dia_comp, B_t0, R_0, volume, B_ref)
Show source vaft/process/equilibrium.py, lines 2291–2353
def computed_diamagnetism_from_phi(
phi_dia_comp: float,
B_t0: float,
R_0: float,
volume: float,
B_ref: float,
) -> float:
"""EFIT-style diamagnetism from an already-computed diamagnetic flux.
Parameters
----------
phi_dia_comp : float
The computed diamagnetic flux [Wb].
B_t0 : float
Vacuum toroidal field at the reference radius [T].
R_0 : float
Reference major radius [m].
volume : float
Plasma volume, strictly positive [m^3].
B_ref : float
Reference field the result is normalized by, normally the boundary-average
poloidal field, strictly positive [T].
Returns
-------
float
The EFIT-style diamagnetism [-].
Raises
------
ValueError
The volume or the reference field is not positive.
Convention
----------
``4*pi*B_t0*R_0/(V*B_ref^2)`` times the flux. The arithmetic itself lives in
:func:`vaft.formula.equilibrium.virial_muihat_from_Bt_R0_dphi`; this wrapper
adds only the positivity precondition its callers rely on. Keeping one
definition matters because the validation layer compares a diamagnetism from
this path against one from the formula path, and a correction applied to only
one of them would desynchronize them silently.
Applicability
-------------
Machine-independent.
Limitations
-----------
Takes the flux as given and cannot check that it was computed against the
same reference radius and field used here.
Provenance
----------
.. [1] The EFIT ``xmui`` definition, evaluated through
:func:`vaft.formula.equilibrium.virial_muihat_from_Bt_R0_dphi`.
"""
if volume <= 0.0 or B_ref <= 0.0:
raise ValueError("volume and B_ref must be positive.")
from vaft.formula.equilibrium import virial_muihat_from_Bt_R0_dphi
return float(
virial_muihat_from_Bt_R0_dphi(B_t0, R_0, phi_dia_comp, B_ref, volume)
)EFIT-style diamagnetism from an already-computed diamagnetic flux.
| Name | Type | Unit | Description |
|---|---|---|---|
phi_dia_comp | float | Wb | The computed diamagnetic flux. |
B_t0 | float | T | Vacuum toroidal field at the reference radius. |
R_0 | float | m | Reference major radius. |
volume | float | m^3 | Plasma volume, strictly positive. |
B_ref | float | T | Reference field the result is normalized by, normally the boundary-average poloidal field, strictly positive. |
| Returns | |||
| — | float | - | The EFIT-style diamagnetism. |
Convention
4*pi*B_t0*R_0/(V*B_ref^2) times the flux. The arithmetic itself lives in
vaft.formula.equilibrium.virial_muihat_from_Bt_R0_dphi; this wrapper
adds only the positivity precondition its callers rely on. Keeping one
definition matters because the validation layer compares a diamagnetism from
this path against one from the formula path, and a correction applied to only
one of them would desynchronize them silently.
Raises
ValueErrorThe volume or the reference field is not positive.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Takes the flux as given and cannot check that it was computed against the same reference radius and field used here.
Provenance
- The EFIT
xmuidefinition, evaluated throughvaft.formula.equilibrium.virial_muihat_from_Bt_R0_dphi.
connection_length_map
Machine-independentConvention-sensitive
source
connection_length_map(seed_r, seed_z, b_field, *, wall_r, wall_z, phi0=0.0, dphi=np.float64(0.03490658503988659), max_length_m=150.0)
Connection length of the field line through each of many starting points.
How far a field line runs in both directions before it strikes the wall, which is what decides whether an electron accelerating along it avalanches or is lost. Every line is stepped together rather than one at a time, so a whole grid costs one pass instead of one pass per point.
| Name | Type | Unit | Description |
|---|---|---|---|
seed_r | np.ndarray | m | Major radius of each starting point. |
seed_z | np.ndarray | m | Height of each starting point, broadcastable against |
b_field | callable | T | A function of |
wall_r | np.ndarray | m | Major radius of the limiting polygon. |
wall_z | np.ndarray | m | Height of the limiting polygon. |
phi0 | float, optional | rad | Starting toroidal angle, the same for every line. |
dphi | float, optional | rad | Fixed step in toroidal angle. |
max_length_m | float, optional | m | Path length at which to stop each direction. |
| Returns | |||
| — | dict of str to np.ndarray | m |
|
Convention
The toroidal angle is the integration variable, as in
trace_field_line, so a vanishing toroidal field ends a line rather
than slowing it. The length is the sum of both directions from the
seed, which is the quantity a Townsend avalanche sees; a caller wanting one
branch should read forward_m or backward_m. A saturated line
reports max_length_m, not infinity, and saturated is how a consumer
tells the difference.
Raises
ValueErrorA non-positivedphiormax_length_m, or seed arrays whose shapes do not match [-].
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Drop the seeds that are already outside the limiting polygon; they have
no connection length and are reported as
nan. - Step every surviving line together by fourth-order Runge-Kutta in the
toroidal angle, once forward and once backward, retiring a line as it
leaves the polygon or reaches
max_length_m. - Add the two branches. A line retired on length contributes
max_length_mand is flagged rather than being read as a wall hit.
Defaults
The two-degree step is a numerical convenience, chosen by convergence against a half-degree trace on VEST’s vacuum field: the median length moves by 7e-5 and the 95th percentile by 4e-3 of itself. The 150 m limit is an assumed value, long enough that a Lloyd threshold evaluated on it has already saturated and short enough that a line circulating near a null terminates.
Applicability
Machine-independent. The wall polygon and the field are both the caller’s.
Limitations
The wall test is applied once per step, so a line ends at the last point
inside the polygon rather than at the true crossing – of order
$R\,\mathrm{d}\varphi$, millimetres against the tens of metres the length
itself runs to. Fixed step, so accuracy is set by the step alone and there
is no error control. A field line that closes on itself never strikes the
wall, and this returns max_length_m for it, flagged; distinguishing a
closed line from a merely long one is not attempted.
Provenance
- The field-line equations in toroidal-angle form, as in
trace_field_line, stepped over many seeds at once. - B. Lloyd et al., Nucl. Fusion 31 (1991) 2031, Sec. 2, for the role the connection length plays in the breakdown threshold this feeds.
contour_shape_parameters
Machine-independentConvention-sensitive
source
contour_shape_parameters(r_seg, z_seg)
Show source vaft/process/equilibrium.py, lines 2566–2645
def contour_shape_parameters(r_seg: np.ndarray, z_seg: np.ndarray) -> dict[str, float]:
"""Shape parameters of one closed flux-surface contour.
Parameters
----------
r_seg : array_like
Major radius of the contour points [m].
z_seg : array_like
Height of the contour points [m].
Returns
-------
dict of str to float
``volume`` in cubic metres; ``area`` and ``surface`` in square metres;
``elongation``, ``triangularity_upper`` and ``triangularity_lower``
dimensionless; ``r_inboard`` and ``r_outboard`` in metres [-].
Raises
------
ValueError
The contour is degenerate, having zero minor radius.
Convention
----------
Volume is the exact revolution ``pi * closed_integral(R^2 dZ)``, not Pappus's
approximation, so it is right even for a contour whose centroid is far from
its geometric centre. Area is the shoelace formula and surface is
``closed_integral 2*pi*R dl``.
Elongation and both triangularities are taken against the geometric centre
``(R_out + R_in)/2``, which is what the IMAS data dictionary reports for the
boundary. Positive triangularity means the extremum of height sits inboard of
that centre. The extremum's major radius comes from
:func:`r_at_z_extremum` rather than from the nearest vertex.
Applicability
-------------
Machine-independent.
Limitations
-----------
Assumes one simple closed contour; a self-intersecting or open segment gives
a signed area that does not mean what the formulas take it to mean. Every
quantity is only as accurate as the contour's sampling.
Provenance
----------
.. [1] IMAS data dictionary definitions for the boundary's geometric axis and
triangularity, which fix the centre these are measured against.
.. [2] The exact revolution volume is
:func:`vaft.formula.equilibrium.exact_volume_from_RZ_contour`.
"""
from vaft.formula.equilibrium import exact_volume_from_RZ_contour
r_seg = np.asarray(r_seg, dtype=float).reshape(-1)
z_seg = np.asarray(z_seg, dtype=float).reshape(-1)
r_min, r_max = float(np.min(r_seg)), float(np.max(r_seg))
z_min, z_max = float(np.min(z_seg)), float(np.max(z_seg))
minor = 0.5 * (r_max - r_min)
r_geo = 0.5 * (r_max + r_min)
if minor <= 0.0:
raise ValueError("degenerate contour")
r_closed = np.r_[r_seg, r_seg[0]]
z_closed = np.r_[z_seg, z_seg[0]]
segment_length = np.hypot(np.diff(r_closed), np.diff(z_closed))
r_mid = 0.5 * (r_closed[1:] + r_closed[:-1])
return {
"volume": exact_volume_from_RZ_contour(r_seg, z_seg),
# Poloidal cross-section area, by the shoelace formula.
"area": 0.5
* abs(
float(np.dot(r_seg, np.roll(z_seg, 1)) - np.dot(z_seg, np.roll(r_seg, 1)))
),
"surface": 2.0 * np.pi * float(np.sum(r_mid * segment_length)),
"elongation": (z_max - z_min) / (2.0 * minor),
"triangularity_upper": (r_geo - r_at_z_extremum(r_seg, z_seg, upper=True)) / minor,
"triangularity_lower": (r_geo - r_at_z_extremum(r_seg, z_seg, upper=False)) / minor,
"r_inboard": r_min,
"r_outboard": r_max,
}Shape parameters of one closed flux-surface contour.
| Name | Type | Unit | Description |
|---|---|---|---|
r_seg | array_like | m | Major radius of the contour points. |
z_seg | array_like | m | Height of the contour points. |
| Returns | |||
| — | dict of str to float | - |
|
Convention
Volume is the exact revolution pi * closed_integral(R^2 dZ), not Pappus’s
approximation, so it is right even for a contour whose centroid is far from
its geometric centre. Area is the shoelace formula and surface is
closed_integral 2*pi*R dl.
Elongation and both triangularities are taken against the geometric centre
(R_out + R_in)/2, which is what the IMAS data dictionary reports for the
boundary. Positive triangularity means the extremum of height sits inboard of
that centre. The extremum’s major radius comes from
r_at_z_extremum rather than from the nearest vertex.
Raises
ValueErrorThe contour is degenerate, having zero minor radius.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Assumes one simple closed contour; a self-intersecting or open segment gives a signed area that does not mean what the formulas take it to mean. Every quantity is only as accurate as the contour’s sampling.
Provenance
- IMAS data dictionary definitions for the boundary’s geometric axis and triangularity, which fix the centre these are measured against.
- The exact revolution volume is
vaft.formula.equilibrium.exact_volume_from_RZ_contour.
contour_shaping_observables
Machine-independentConvention-sensitive
source
contour_shaping_observables(r_seg, z_seg)
Model-independent shaping observables of one closed contour: indentation and asymmetry.
Measurable from any valid boundary, whatever model – Miller, Fourier or
none – describes it, so a model coefficient such as
MillerSurface.indentation can be checked against a geometric fact
rather than against another model.
| Name | Type | Unit | Description |
|---|---|---|---|
r_seg | array_like | m | Major radius of the contour points. |
z_seg | array_like | m | Height of the contour points. |
| Returns | |||
| — | dict of str to float or bool | - |
|
Convention
Inboard means major radius below the geometric centre (R_out +
R_in)/2; the minor radius is (R_out - R_in)/2 and the mirror plane
is Z = (Z_max + Z_min)/2, the same references as
contour_shape_parameters. Distances are measured against the
contour polyline resampled to 4096 points of equal arc length.
Raises
ValueErrorFewer than four points, or a contour with zero minor radius.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
The 1e-3 concavity threshold sits above the chord error of a smooth contour sampled with about a hundred points; a coarser contour can read a small spurious depth. Outboard concavity is not reported.
Provenance
- The observables proposed in #942, section 7, kept separate from the coefficients of any one parameterization.
convert_cocos
Machine-independentConvention-sensitive
source
convert_cocos(equilibrium, target_cocos)
Convert an equilibrium between two explicitly known COCOS conventions.
The only COCOS transform in the package. vaft.process.cocos identifies
and validates conventions but never changes data; this is where a sign or a
factor of 2*pi is actually applied.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData | - | The record to convert. Its source convention must be known: either declared, or identified down to exactly one candidate. |
target_cocos | int | - | The convention to convert to, 1 to 18. |
| Returns | |||
| — | EquilibriumData | - | A copy with every convention-dependent field transformed and its |
Convention
Direction is source to target, and both must be in 1 to 18; 9 and 10 do not
exist. The flux factor is where a sign flip and a factor of 2*pi land
together. Pressure and the magnetic axis are deliberately untouched: they are
physical quantities that no convention changes. The pressure gradient scales
by its own factor rather than by the flux factor, because a derivative against
flux scales by the inverse of what flux does.
Raises
ValueErrortarget_cocos is outside 1 to 18, or the source convention is unknown or ambiguous.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Resolve the source convention from the declared index, or from a single surviving identification candidate.
- Take the per-quantity factors for the pair from OMAS.
- Scale the flux quantities, the poloidal current, the safety factor, the two source gradients, the plasma current and the vacuum field, each by its own factor.
- Record the target index on the returned convention.
Applicability
Machine-independent.
Limitations
Refuses rather than guesses when the source convention is ambiguous, which is the common case for a file that declares nothing and whose flux exponent could not be identified. The identified-candidate list carried on the result is shifted on the assumption that every candidate shares the source’s storage family, which is not guaranteed and can produce an out-of-range index; tracked in #601.
Provenance
- Sauter and Medvedev (2013) for the conventions; the per-quantity factors are
omas.cocos_transform.
current_centroid
Machine-independentConvention-sensitive
source
current_centroid(j_tor, r, z, *, weights=None)
Show source vaft/process/_equilibrium_moments.py, lines 44–95
def current_centroid(j_tor: Any, r: Any, z: Any, *, weights: Any = None) -> tuple[float, float, float]:
"""Total toroidal current and its centroid, from a gridded current density.
The zeroth and first current moments: ``I_p = integral J_phi dA`` and
``(R_c, Z_c) = (1/I_p) integral (R, Z) J_phi dA``. A single filament at
the centroid carrying ``I_p`` is the lowest-order representation of the
distribution.
Parameters
----------
j_tor : array_like
Toroidal current density, indexed ``(R, Z)`` [A/m^2].
r : array_like
Major-radius grid axis [m].
z : array_like
Height grid axis [m].
weights : array_like, optional
Fraction of each cell to integrate, such as
:func:`fractional_cell_weights_from_boundary` gives; all ones when not
given [-].
Returns
-------
tuple of float
``(I_p, R_c, Z_c)`` in ampere, metre and metre [-].
Raises
------
ValueError
The shapes disagree, or the integrated current is zero, which leaves
the centroid undefined.
Convention
----------
The area element is the cell area ``dR dZ`` times its weight, not
``2 pi R dR dZ``: these are moments of the current through the poloidal
cross-section, whose zeroth moment is the plasma current. The sign of
``I_p`` is that of ``j_tor``; the centroid does not depend on it.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1] Definition of the current moments; see issue #943.
"""
element, rm, zm, _ = _grid(j_tor, r, z, weights)
total = float(np.sum(element))
if total == 0.0 or not np.isfinite(total):
raise ValueError("the integrated current is zero or not finite; the centroid is undefined")
return total, float(np.sum(rm*element)/total), float(np.sum(zm*element)/total)Total toroidal current and its centroid, from a gridded current density.
The zeroth and first current moments: I_p = integral J_phi dA and
(R_c, Z_c) = (1/I_p) integral (R, Z) J_phi dA. A single filament at
the centroid carrying I_p is the lowest-order representation of the
distribution.
| Name | Type | Unit | Description |
|---|---|---|---|
j_tor | array_like | A/m^2 | Toroidal current density, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
weights | array_like, optional | - | Fraction of each cell to integrate, such as |
| Returns | |||
| — | tuple of float | - |
|
Convention
The area element is the cell area dR dZ times its weight, not
2 pi R dR dZ: these are moments of the current through the poloidal
cross-section, whose zeroth moment is the plasma current. The sign of
I_p is that of j_tor; the centroid does not depend on it.
Raises
ValueErrorThe shapes disagree, or the integrated current is zero, which leaves the centroid undefined.
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- Definition of the current moments; see issue #943.
current_covariance
Machine-independentConvention-sensitive
source
current_covariance(j_tor, r, z, *, weights=None)
Show source vaft/process/_equilibrium_moments.py, lines 167–212
def current_covariance(j_tor: Any, r: Any, z: Any, *, weights: Any = None) -> np.ndarray:
"""Second central moment tensor of a gridded current density.
``[[mu_20, mu_11], [mu_11, mu_02]]`` about the current centroid,
normalized by the total current. Its eigenvalues are the squared
principal widths of the current distribution and its eigenvectors their
directions.
Parameters
----------
j_tor : array_like
Toroidal current density, indexed ``(R, Z)`` [A/m^2].
r : array_like
Major-radius grid axis [m].
z : array_like
Height grid axis [m].
weights : array_like, optional
Fraction of each cell to integrate [-].
Returns
-------
np.ndarray
The symmetric 2 x 2 tensor [m^2].
Raises
------
ValueError
As for :func:`current_moment`.
Convention
----------
Central and normalized, as :func:`current_moment` with its defaults. A
width of the *current*, not of the plasma boundary: a peaked current
profile has a smaller covariance than its LCFS suggests.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1] Definition of the current moments; see issue #943.
"""
_, r0, z0 = current_centroid(j_tor, r, z, weights=weights)
mu = {pq: current_moment(j_tor, r, z, *pq, weights=weights, center=(r0, z0)) for pq in ((2, 0), (1, 1), (0, 2))}
return np.array([[mu[(2, 0)], mu[(1, 1)]], [mu[(1, 1)], mu[(0, 2)]]], dtype=float)Second central moment tensor of a gridded current density.
[[mu_20, mu_11], [mu_11, mu_02]] about the current centroid,
normalized by the total current. Its eigenvalues are the squared
principal widths of the current distribution and its eigenvectors their
directions.
| Name | Type | Unit | Description |
|---|---|---|---|
j_tor | array_like | A/m^2 | Toroidal current density, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
weights | array_like, optional | - | Fraction of each cell to integrate. |
| Returns | |||
| — | np.ndarray | m^2 | The symmetric 2 x 2 tensor. |
Convention
Central and normalized, as current_moment with its defaults. A
width of the current, not of the plasma boundary: a peaked current
profile has a smaller covariance than its LCFS suggests.
Raises
ValueErrorAs forcurrent_moment.
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- Definition of the current moments; see issue #943.
current_moment
Machine-independentConvention-sensitive
source
current_moment(j_tor, r, z, p, q, *, weights=None, center=None, normalize=True)
Show source vaft/process/_equilibrium_moments.py, lines 98–164
def current_moment(
j_tor: Any, r: Any, z: Any, p: int, q: int, *, weights: Any = None,
center: tuple[float, float] | None = None, normalize: bool = True,
) -> float:
"""One moment ``integral (R - R_0)**p (Z - Z_0)**q J_phi dA`` of a gridded current density.
Parameters
----------
j_tor : array_like
Toroidal current density, indexed ``(R, Z)`` [A/m^2].
r : array_like
Major-radius grid axis [m].
z : array_like
Height grid axis [m].
p : int
Order in R, non-negative [-].
q : int
Order in Z, non-negative [-].
weights : array_like, optional
Fraction of each cell to integrate [-].
center : tuple of float, optional
Reference point ``(R_0, Z_0)``; the current centroid when not given,
which makes the moment a central one [m].
normalize : bool, optional
Divide by the total current, giving m**(p+q) instead of A m**(p+q) [-].
Returns
-------
float
The moment, in m**(p+q) when normalized and A m**(p+q) otherwise [-].
Raises
------
ValueError
A negative order, disagreeing shapes, or a zero total current where
the centroid or the normalization needs it.
Convention
----------
``center=None`` means *central*: about ``(R_c, Z_c)`` from
:func:`current_centroid`, so the first central moments vanish. Pass
``center=(0.0, 0.0)`` for raw moments about the origin of the (R, Z)
plane. The area element is ``dR dZ`` times the weight.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1] Definition of the current moments; see issue #943.
"""
p, q = int(p), int(q)
if p < 0 or q < 0:
raise ValueError("moment orders must be non-negative")
element, rm, zm, _ = _grid(j_tor, r, z, weights)
if center is None:
_, r0, z0 = current_centroid(j_tor, r, z, weights=weights)
else:
r0, z0 = map(float, center)
value = float(np.sum((rm - r0)**p*(zm - z0)**q*element))
if not normalize:
return value
total = float(np.sum(element))
if total == 0.0 or not np.isfinite(total):
raise ValueError("the integrated current is zero or not finite; cannot normalize")
return value/totalOne moment integral (R - R_0)**p (Z - Z_0)**q J_phi dA of a gridded current density.
| Name | Type | Unit | Description |
|---|---|---|---|
j_tor | array_like | A/m^2 | Toroidal current density, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
p | int | - | Order in R, non-negative. |
q | int | - | Order in Z, non-negative. |
weights | array_like, optional | - | Fraction of each cell to integrate. |
center | tuple of float, optional | m | Reference point |
normalize | bool, optional | - | Divide by the total current, giving m(p+q) instead of A m(p+q). |
| Returns | |||
| — | float | - | The moment, in m(p+q) when normalized and A m(p+q) otherwise. |
Convention
center=None means central: about (R_c, Z_c) from
current_centroid, so the first central moments vanish. Pass
center=(0.0, 0.0) for raw moments about the origin of the (R, Z)
plane. The area element is dR dZ times the weight.
Raises
ValueErrorA negative order, disagreeing shapes, or a zero total current where the centroid or the normalization needs it.
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- Definition of the current moments; see issue #943.
derive_boundary_representation
Machine-independentConvention-sensitive
source
derive_boundary_representation(equilibrium, *, gap_angles=None, flux_tolerance=None, fourier_modes=16)
Classify the plasma boundary and derive its geometric diagnostics.
Topology is decided from the flux map alone. Stationary points of psi are located and split into O-points and saddles by the sign of the Hessian determinant; a saddle counts as a physical X-point only when it is relevant to the boundary, meaning its flux agrees with the boundary flux to within what the grid resolves and the confined region’s level set reaches it.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
gap_angles | mapping of str to float, optional | rad | Named directions from the geometric centre along which to measure the plasma-wall gap. |
flux_tolerance | float, optional | - | Overrides the normalized-flux window of the saddle test with one value for every saddle. |
fourier_modes | int, optional | - | How many harmonics to fit to the boundary contour. |
| Returns | |||
| — | BoundaryRepresentation | - | The topology and the reason for it; the X-points with their active flags; the strike points with their flux expansion and incidence; the named gaps; the outer-midplane separation between the first two separatrices; and the Fourier coefficients with their fit error. Distances in metres, angles in radians, everything else dimensionless. |
Convention
At least one boundary-relevant X-point makes the equilibrium diverted; none, together with a last closed flux surface in contact with the wall, makes it limited. Anything that cannot be settled – a grid-clipped confined region, no wall to test against, or a surface bounded by neither – is reported ambiguous with a reason rather than forced into one of the two.
Defaults · Applicability · Limitations · Provenance
Defaults
Leaving flux_tolerance at None is the justified choice, and an override
is a numerical convenience only: each saddle otherwise gets its own window
from its Hessian curvature and the grid spacing, because psi is stationary
there and a one-cell position error perturbs its flux only at second order.
A single global tolerance cannot be right for saddles of different sharpness.
fourier_modes = 16 is likewise a numerical convenience, enough harmonics
to represent a tokamak boundary without fitting the contour’s own sampling.
The coefficients are fit_fourier_surface’s, one convention for
every contour (#945). Before #945 the phase origin was the sampled point
of largest R; it is now the phase of R’s first harmonic, so individual
cosine and sine values differ from older results while the per-harmonic
amplitudes and the reconstruction error do not.
Applicability
Machine-independent. The gap directions are where a machine’s own geometry enters, and they are supplied by the caller.
Limitations
Topology needs a wall or limiter to distinguish limited from ambiguous; without one, a plasma with no X-point is ambiguous rather than limited. Strike points and gaps require the corresponding geometry and are simply absent otherwise. The Fourier fit assumes a single closed contour.
Provenance
- The Hessian classification of stationary points of psi is the standard O-point and X-point test; the boundary-relevance condition is what distinguishes a physical X-point from a saddle elsewhere in the domain.
derive_current_moments
Machine-independentConvention-sensitive
source
derive_current_moments(equilibrium, *, max_order=4, j_tor=None)
Reduce an equilibrium’s toroidal current density to its low-order moments.
Between a single filament and the full J_phi(R, Z): the total
current, its centroid, and every normalized central moment up to
max_order, integrated over the plasma with fractional LCFS cells. Two
equilibria on different grids can then be compared moment by moment.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
max_order | int, optional | - | Highest total order |
j_tor | array_like, optional | A/m^2 | A canonical toroidal current density on the record’s grid, indexed |
| Returns | |||
| — | CurrentMomentRepresentation | - | Total current, centroid, the central moments, the order, where the current density came from, and the derivation record. |
Convention
The derived current density is the same expression, and the same resolved
psi convention, as vaft.omas.update.update_equilibrium_profiles_2d_j_tor
uses for an ODS, resolved the same way (the 2*pi exponent from
psi_per_radian, sigma_Bp from agreeing candidates, else +1), so
the two paths agree; it is never a dpsi/dR
pseudo-current. Moments are central about the current centroid and
normalized by the total current (m**(p+q)); the area element is
dR dZ, so the zeroth moment is the plasma current.
Raises
ValueErrormax_order below two, no psi map or LCFS, a record with neither a j_tor nor the convention and profiles to derive one, or zero integrated current.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Take j_tor as given, or evaluate the Grad-Shafranov current
J_phi = -sigma_Bp (2 pi)**e_Bp (R p'(psi) + FF'(psi)/(mu0 R))from the record’s flux-function profiles in its declared COCOS. - Weight each cell by the fraction of it inside the LCFS.
- Integrate the total current and the centroid, then every central
moment with
2 <= p + q <= max_order.
Defaults
max_order = 4 is a numerical convenience: second order gives the
widths and tilt, third the skewness and up-down asymmetry, fourth the
first shape beyond them; higher orders grow more sensitive to the edge.
Applicability
Machine-independent.
Limitations
An ODS’s own profiles_2d.j_tor is not read automatically –
as_equilibrium does not carry it – so pass it as j_tor; the
derived density agrees with update_equilibrium_profiles_2d_j_tor to
about 1e-10 inside the LCFS. NaN cells of a supplied j_tor that the
fractional weights still count are filled from p'/FF' and the
source label says how many. The integrated current is checked against
the record’s ip, and a
disagreement beyond five percent – typically a factor 2*pi or -1 from a
wrongly declared convention – is warned about, not corrected. Current
outside the LCFS, such as a halo or a vessel current, is excluded
by the weighting. The Shafranov (magnetic) moments of #943 are a separate
representation and are not computed here.
Provenance
- Definition of the current moments; see issue #943.
- O. Sauter and S. Yu. Medvedev, Tokamak coordinate conventions: COCOS, Comput. Phys. Commun. 184, 293 (2013), Eq. 12, for the sign and 2*pi of the Grad-Shafranov current.
derive_global_descriptors
Machine-independentConvention-sensitive
source
derive_global_descriptors(equilibrium, *, rational_q=(1.0, 1.5, 2.0, 3.0))
Global scalar descriptors of one equilibrium, each with its derivation record.
Geometry from the boundary, energy from a grid quadrature, the safety factor
from the stored profile, and the Shafranov virial closures from the boundary
integrals. Every quantity is a vaft.data.equilibrium.DerivedValue
carrying the method it came from, the fields it read, and the convention it
was computed under, so a number that could not be formed says why instead of
being absent.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
rational_q | sequence of float, optional | - | Safety-factor values whose surfaces to locate, reported as the normalized radii where |
| Returns | |||
| — | GlobalEquilibriumDescriptors | - | A mapping of name to |
Convention
Shape descriptors use the geometric centre (R_out+R_in)/2, which is what
the IMAS data dictionary reports for the boundary’s geometric axis and
triangularity. The area centroid is a different quantity, used only where
Pappus’s theorem needs it for the volume, and at low aspect ratio the two sit
centimetres apart. The poloidal field is computed through the equilibrium’s
own declared COCOS and per-radian flag, so a wrongly declared convention
propagates into every field-derived quantity here. The magnetic shear is
taken against the poloidal radius. The normalized beta follows the Troyon
convention beta_N = 100 * beta_t * a * |B_t0| / |I_p| (with a in
metres, B_t0 in tesla, and I_p in megamperes); its published unit
is "1" (dimensionless) conforming to IMAS global_quantities.beta_normal,
with the conventional % * m * T / MA scaling factor folded into the value
and recorded in the derivation quality metadata.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Check preconditions for the
globalrequirement set and keep the report. - Boundary geometry: major and minor radius, elongation, upper and lower triangularity, volume and surface area from the last closed flux surface.
- Energy: integrate pressure over the plasma volume by grid quadrature for the pressure integral and the thermal energy.
- Betas: toroidal from the volume-average pressure and the vacuum field, normalized from that and the current, poloidal from the boundary-average poloidal field.
- Virial: the Shafranov boundary integrals and the internal inductance from the Lao closure.
- Safety factor: on-axis, at 95 percent of the normalized flux, at the edge, the magnetic shear, and the radius of each requested rational surface.
Applicability
Machine-independent.
Limitations
Every quantity degrades independently: a missing pressure profile costs the energies and the betas but not the geometry. A failure inside the pressure integration is captured as a warning on the returned report rather than raised. The virial internal inductance is ill-conditioned as its coefficient approaches one.
Provenance
- IMAS data dictionary definitions for the boundary geometric axis, triangularity, and the global quantities this mirrors.
- Lao’s virial closure for the internal inductance from the Shafranov boundary integrals, evaluated by
vaft.formula.equilibrium.virial_li_from_S_alpha_rt. - The boundary integrals themselves come from
vaft.process.equilibrium.shafranov_integralsandvaft.process.equilibrium.efit_virial_volume_integrals, the EFIT-style weighted forms.
derive_radial_coordinates
Machine-independentConvention-sensitive
source
derive_radial_coordinates(equilibrium)
The three normalized radial coordinates, each with its derivation record.
psi_norm, rho_pol_norm and rho_tor_norm are three different
coordinates and are not interchangeable; rho_tor_norm equals
sqrt(psi_norm) only for a flat-q cylinder. A coordinate that cannot be
derived is returned as an explicit unavailable record naming the reason, so a
caller can refuse rather than substitute a different radius.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
| Returns | |||
| — | Mapping of str to DerivedValue | - | Keys |
Convention
All three are dimensionless and run 0 on axis to 1 at the boundary.
rho_tor_n is COCOS-independent: it is a ratio of two integrals of the same
q over the same variable, so the factor of 2*pi and the sign of psi
both cancel. psi_n is likewise invariant under the storage choice, being
normalized by its own span. This function therefore needs no COCOS and
imposes none.
Processing steps · Applicability · Limitations · Provenance
Processing steps
psi_n = (psi_1d - psi_axis)/(psi_boundary - psi_axis).rho_pol_n = sqrt(psi_n), by definition of that coordinate.rho_tor_n = sqrt(Phi/Phi_boundary)withPhithe cumulative trapezoidal integral ofqover the normalized flux, returned only ifqis finite, of matching length, and produces a monotonic result.
Applicability
Machine-independent.
Limitations
rho_tor_n is unavailable when q is missing, non-finite, of the wrong
length, or gives a non-monotonic toroidal flux, and when psi_n is not
strictly monotonic. The integral is trapezoidal on the stored grid, so a
coarse or axis-clustered q limits its accuracy near the axis. The
published definition string names dpsi where the integration variable
is psi_n; the value is unaffected and the wording is tracked in #602.
Provenance
- The toroidal flux definition
Phi = int q dpsiand its normalized radius, as used by the IMAS data dictionary forrho_tor_norm. vaft.process.profile.core_profilesconsumes this to evaluate kinetic fits on the equilibrium grid, and refuses by name when the record here is unavailable (issue #420).
efit_virial_volume_integrals
Machine-independentConvention-sensitive
source
efit_virial_volume_integrals(R_grid, Z_grid, R_bdry, Z_bdry, B_R_grid, B_Z_grid, p_tot_grid=None, B_phi_grid=None, B_phi_vac_grid=None, F_grid=None, F_boundary=None, cell_weights=None)
EFIT-style weighted volume integrals for the virial closures.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid | array_like | m | Major-radius grid, as an axis or a mesh. |
Z_grid | array_like | m | Height grid, as an axis or a mesh. |
R_bdry | array_like | m | Major radius of the boundary contour. |
Z_bdry | array_like | m | Height of the boundary contour. |
B_R_grid | array_like | T | Major-radius field component on the grid. |
B_Z_grid | array_like | T | Height field component on the grid. |
p_tot_grid | array_like, optional | Pa | Total pressure on the grid. |
B_phi_grid | array_like, optional | T | Plasma toroidal field on the grid. |
B_phi_vac_grid | array_like, optional | T | Vacuum toroidal field on the same grid. |
F_grid | array_like, optional | T m | Poloidal current function on the grid. |
F_boundary | float, optional | T m | That function at the boundary. |
cell_weights | array_like, optional | - | Per-cell plasma area fractions; computed from the boundary when not given |
| Returns | |||
| — | dict of str to float | - |
|
Convention
Weighted by fractional cell area rather than a per-cell mask, which is what
makes these the EFIT-style forms rather than a plain grid sum. Like
shafranov_integrals, the COCOS is inherited from the field arrays
passed in and is neither declared nor checked here.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
The reference radius is guarded against a near-singular denominator, so a degenerate geometry returns a finite but meaningless value rather than diverging. Every optional input governs one output: omit the pressure and the pressure-dependent terms are simply absent.
Provenance
- EFIT’s weighted volume integrals, with the fractional cell weights standing in for its externally supplied weight map; see
fractional_cell_weights_from_boundary.
ejiri_mirror_geometry
Machine-independentConvention-sensitive
source
ejiri_mirror_geometry(r_start, b_field, *, wall_r, wall_z, z_fit=None, dphi=np.float64(0.017453292519943295), max_length_m=150.0)
Ejiri mirror-confinement proxy for one magnetic snapshot.
Traces the field line through $(R_S, 0)$ and reads off the four lengths the Ejiri low-energy orbit model needs – the starting radius, the inboard limiter, the curvature radius and the vertical extent over which the line keeps raising $|B|$ – then evaluates the boundary slope $\alpha$ and the geometry factor $F_3$.
| Name | Type | Unit | Description |
|---|---|---|---|
r_start | float | m | Major radius the electron starts at, on the midplane. |
b_field | callable | T | A function of |
wall_r | np.ndarray | m | Major radius of the limiting polygon. |
wall_z | np.ndarray | m | Height of the limiting polygon. |
z_fit | float, optional | m | Half-height of the window for the diagnostic local parabola fit; default a quarter of |
dphi | float, optional | rad | Fixed step in toroidal angle for the trace. |
max_length_m | float, optional | m | Path length at which to stop each branch. |
| Returns | |||
| — | dict of str to Any | - |
|
Convention
$R_C$ is the secant through the mirror point, not a local fit. Ejiri’s
curvature term needs $R_C$ only through $Z_{\max}^2/2R_C = R_S - R_m$, so
defining $R_C$ that way makes the term exactly $1/\sqrt{M - 1}$ for the
line’s true mirror ratio $M = R_S/R_m$ – the inboard term with the limiter
replaced by the line’s own mirror point – whatever shape the line has. On
an exact parabola it equals the parabola’s $R_C$. A local fit, which is
what the model’s derivation suggests, is not stable on a real field: on the
same VEST case it gives $R_C$ from 0.07 to 0.34 m as the window widens from
a tenth of $Z_{\max}$ to all of it, and had it fed $\alpha$, $F_3$ would
have run from 0.99 to 0.62 on that choice alone. The secant is 0.52 m for
every window. curvature_radius_local still reports the fit, so how far
the line is from Ejiri’s parabola stays visible.
The mirror point is the global minimum of $R$ along the branch, not the first local one. An escaping electron has to pass the largest field on its way to the wall, wherever it is. The distinction is not academic: at the breakdown onset of VEST’s packaged shot the line through the electron-cyclotron resonance is tilted at the midplane, so on one side $R$ rises for a few steps before dipping to 0.60 m. A first-local-minimum rule gives up on that side, reads the wall end at 0.76 m as the mirror point, and reports no confinement at all.
The weaker mirror decides. An electron bouncing between the two mirror points escapes through the one with the smaller field rise, which is the one at larger $R$, not necessarily the one at smaller $|Z|$.
No inward dip means nothing is confined, not an error. When $R_m \ge
R_S$ the line is straight or bows outward, $|B|$ does not rise away from the
midplane, and curvature traps nothing: this returns $\alpha = \infty$,
$F_3 = 0$ and mirror false without calling the formulas, which reject a
non-positive curvature radius.
The mirror ratio is read in $R$, which assumes $|B| \propto 1/R$: true when the toroidal field dominates, as it does in a pre-breakdown vacuum field.
Raises
ValueErrorA start point outside the wall, no wall crossing of the midplane inboard of it, or a non-positivez_fit[-].
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Trace the line through $(R_S, 0)$ forward and backward against the wall, and label the branch that rises the upper one.
- $R_{\mathrm{LIN}}$: the wall polygon’s midplane crossing nearest the start on its inboard side.
- Mirror point of each branch: the smallest $R$ the branch reaches before it ends, excluding the seed – the strongest field an electron escaping that way has to pass. Record $|Z|$ and $R$ there.
- Keep the weaker mirror: the branch whose mirror point sits at the larger $R$, so the smaller field rise. Its $|Z|$ is $Z_{\max}$ and its $R$ is $R_m$. If $R_m \ge R_S$ the line never dips inward.
- $R_C = Z_{\max}^2 / \bigl(2(R_S - R_m)\bigr)$, the parabola through the
start and the mirror point, then $\alpha$ and $F_3$ from
vaft.formula.startup.ejiri_mirror_alpha_from_R_S_R_LIN_R_C_Z_maxandvaft.formula.startup.ejiri_f3_from_alpha. - For comparison, fit $R - R_S = c_1 Z + c_2 Z^2$ over $|Z| \le$
z_fitand report $-1/(2c_2)$ ascurvature_radius_local.
Defaults
The 150 m limit is a numerical convenience matching
connection_length_map, and it is where the result converges on VEST:
at the breakdown onset of the packaged shot, from the electron-cyclotron
resonance, 50 m stops both branches short and gives $Z_{\max} = 0.13$ m and
$F_3 = 0.38$, while 150, 500 and 1500 m all give $0.30$ m and $0.51$. The
quarter-$Z_{\max}$ window and the one-degree step are numerical
conveniences: at 2, 1, 0.5 and 0.25 degrees $F_3$ agrees to six figures and
$R_C$ and $Z_{\max}$ to four.
Applicability
Machine-independent. The wall polygon and the field are both the caller’s,
and so is r_start – normally the electron-cyclotron resonance, from
vaft.formula.startup.electron_cyclotron_resonance_radius.
Limitations
An Ejiri-inspired geometric proxy, not the numerical orbit boundary: it
says nothing about EC power, collisions or breakdown itself. A branch whose
first step already leaves the wall is read as an immediate loss on that
side, so a start point within one step of the wall reports no confinement
– shorten dphi if that is not the answer wanted. A branch that
stops on max_length_m sets saturated and raises a
RuntimeWarning: that result is not converged and is not a bound, since a
longer trace can move both the mirror point and the branch that binds.
Provenance
- A. Ejiri and Y. Takase, Nucl. Fusion 47 (2007) 403, Sec. 3, for the orbit-boundary slope and the geometry factor.
- The trace is
trace_field_line, and the two relations are thevaft.formula.startupkernels named in the processing steps.
equilibrium_field_on_grid
Machine-independentConvention-sensitive
source
equilibrium_field_on_grid(R_grid_1d, Z_grid_1d=None, psi_grid=None, psi_1d=None, f_1d=None, cocos=None, *, psi_per_radian=None)
(B_R, B_Z, B_phi) on the whole (R, Z) grid, each (nR, nZ).
The vectorised twin of make_equilibrium_field_interpolator, for a
caller that wants the field everywhere rather than at a point: the same
bicubic psi spline, the same Sauter Eq. 20 prefactor, and the same
F(psi)/R with F clipped to the profile’s own range outside the
confined region. Evaluating the point interpolator over a 129x129 grid
would be sixteen thousand Python calls; this is one spline evaluation.
Called as equilibrium_field_on_grid(equilibrium) with an
vaft.data.equilibrium.EquilibriumData, the grid, profiles and
flux convention all come from the record, which is the form to prefer;
the record is only as right as its convention, which nothing checks
against the arrays.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid_1d | array_like or EquilibriumData | - | Major-radius grid axis [m], or the whole equilibrium record, in which case every other argument must be left unset. |
Z_grid_1d | array_like, optional | m | Height grid axis; required with arrays. |
psi_grid | array_like, optional | Wb or Wb/rad | Poloidal flux on |
psi_1d | array_like, optional | same psi unit |
|
f_1d | array_like, optional | T m |
|
cocos | int or None, optional | - | COCOS index of psi_grid. |
psi_per_radian | bool or None, optional | - | Storage family of the flux when the index is not known: |
| Returns | |||
| — | tuple of numpy.ndarray | T |
|
Convention
The same prefactor as make_equilibrium_field_interpolator, per
Sauter Eq. 20: B_R = k (1/R) dpsi/dZ and B_Z = -k (1/R) dpsi/dR
with k = sigma_RphiZ * sigma_Bp / (2*pi)**e_Bp, evaluated by
vaft.formula.equilibrium.poloidal_field_factor from cocos or,
failing that, psi_per_radian. A record supplies both from its
vaft.data.equilibrium.EquilibriumData.convention. With neither,
the weber-per-radian k = -1 is assumed, with a warning. The toroidal
field is the poloidal current function over the major radius and inherits
the sign of f_1d. The flux map is indexed major radius first.
Raises
ValueErrorThe flux map is not shaped to the two grid axes, the two profile arrays have different lengths, the record lacks a field, cocos and psi_per_radian contradict each other, or the record’s open COCOS candidates disagree on the field direction.TypeErrorA record is combined with array arguments, or the array form is missing one.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
With neither cocos nor psi_per_radian known, from the arguments or the
record, the flux is taken as weber per radian and a UserWarning says
so (#1313); a weber flux then gives a field 2*pi too large. With the
storage family but no index, the orientation is assumed (k < 0) and
the warning says that instead; a record’s open candidates settle it when
they agree, and leave it to the same announced assumption when they do
not (the packaged g-file, open between COCOS 1 and 2, warns like its
arrays do).
Outside the confined region the poloidal current function clips to its
nearest edge value, the clip-and-interpolate convention
psi_to_rz uses, so the toroidal field there is that clipped
function over the major radius rather than the true vacuum field. The
spline extrapolates beyond the grid, where the field should not be read.
Provenance
- Sauter and Medvedev (2013), Eq. 20, for the prefactor.
- The point-wise twin
make_equilibrium_field_interpolatorin this module, which this routine is pinned to by test.
evaluate_fourier_surface
Machine-independentConvention-sensitive
source
evaluate_fourier_surface(surface, theta)
Show source vaft/process/_equilibrium_parametric.py, lines 3022–3060
def evaluate_fourier_surface(surface: FourierSurface, theta: Any) -> tuple[np.ndarray, np.ndarray]:
"""Points on a Fourier-represented flux surface at given arc-length angles.
The forward evaluation of the spectral surface representation;
:func:`fit_fourier_surface` is its inverse.
Parameters
----------
surface : FourierSurface
The cosine and sine coefficients of each harmonic of R and Z [m].
theta : array_like
Arc-length angles ``2 pi s / L`` at which to evaluate [rad].
Returns
-------
tuple of np.ndarray
``(R, Z)`` on the surface, same shape as *theta* [m].
Convention
----------
``R = sum_m r_cos[m] cos(m theta) + r_sin[m] sin(m theta)`` and likewise
``Z``, with ``theta`` increasing counter-clockwise in (R, Z) from the
phase origin of :class:`~vaft.data.equilibrium.FourierSurface`.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1] Truncated Fourier series of a closed curve; the arc-length angle
and phase are VAFT's convention (#945).
"""
theta = np.asarray(theta, dtype=float)
m = np.arange(surface.modes+1).reshape((-1,)+(1,)*theta.ndim)
cos, sin = np.cos(m*theta), np.sin(m*theta)
r = np.tensordot(surface.r_cos, cos, axes=(0, 0)) + np.tensordot(surface.r_sin, sin, axes=(0, 0))
z = np.tensordot(surface.z_cos, cos, axes=(0, 0)) + np.tensordot(surface.z_sin, sin, axes=(0, 0))
return r, zPoints on a Fourier-represented flux surface at given arc-length angles.
The forward evaluation of the spectral surface representation;
fit_fourier_surface is its inverse.
| Name | Type | Unit | Description |
|---|---|---|---|
surface | FourierSurface | m | The cosine and sine coefficients of each harmonic of R and Z. |
theta | array_like | rad | Arc-length angles |
| Returns | |||
| — | tuple of np.ndarray | m |
|
Convention
R = sum_m r_cos[m] cos(m theta) + r_sin[m] sin(m theta) and likewise
Z, with theta increasing counter-clockwise in (R, Z) from the
phase origin of vaft.data.equilibrium.FourierSurface.
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- Truncated Fourier series of a closed curve; the arc-length angle and phase are VAFT’s convention (#945).
evaluate_guazzotto_freidberg
Machine-independentConvention-sensitive
source
evaluate_guazzotto_freidberg(model, x, y)
Show source vaft/process/_equilibrium_guazzotto_freidberg.py, lines 539–584
def evaluate_guazzotto_freidberg(model: GuazzottoFreidbergEquilibrium, x: Any, y: Any) -> Mapping[str, np.ndarray]:
"""Normalized flux and its analytic derivatives of a Guazzotto-Freidberg equilibrium.
Parameters
----------
model : GuazzottoFreidbergEquilibrium
A solved equilibrium [-].
x : array_like
Normalized radial coordinate, ``-1`` inboard and ``1`` outboard on the
midplane [-].
y : array_like
Normalized height ``Z/a`` [-].
Returns
-------
Mapping of str to np.ndarray
``psi`` and its derivatives ``psi_x``, ``psi_y``, ``psi_xx``,
``psi_yy``, ``psi_xy``; ``psi_j``, the shifted flux ``psi + f_J/(1 -
f_J)`` of Part 2 (equal to psi without a current pedestal); and
``gs_residual``, the Grad-Shafranov equation evaluated as ``(1 +
eps_hat x) psi_xx + psi_yy/(1 + eps**2) + alpha**2 (1 + nu G(x))
psi_J``, zero to round-off (Eq. 2.9; Part 2, Eq. 5.14) [-].
Convention
----------
The normalized coordinates and flux of Eq. (2.8); physical fields follow
from :func:`guazzotto_freidberg_to_equilibrium`.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1] Guazzotto and Freidberg (2021), Eqs. (2.9), (3.2) and (A4); Part 2,
Eqs. (5.12)-(5.14).
"""
values = _evaluate(model.topology, model.inverse_aspect_ratio, _model_source(model), model.alpha,
model.coefficients, x, y, model.series_terms)
eps = model.inverse_aspect_ratio; eps_hat = 2*eps/(1 + eps**2)
xx = np.asarray(x, dtype=float)
s1, s2, s3 = _model_source(model)
residual = ((1 + eps_hat*xx)*values["xx"] + values["yy"]/(1 + eps**2)
+ model.alpha**2*(1 + s1*xx + s2*xx**2 + s3*xx**3)*values["psi"])
return {"psi": values["psi"] - _flux_offset(model), "psi_j": values["psi"], "psi_x": values["x"], "psi_y": values["y"], "psi_xx": values["xx"],
"psi_yy": values["yy"], "psi_xy": values["xy"], "gs_residual": residual}Normalized flux and its analytic derivatives of a Guazzotto-Freidberg equilibrium.
| Name | Type | Unit | Description |
|---|---|---|---|
model | GuazzottoFreidbergEquilibrium | - | A solved equilibrium. |
x | array_like | - | Normalized radial coordinate, |
y | array_like | - | Normalized height |
| Returns | |||
| — | Mapping of str to np.ndarray | - |
|
Convention
The normalized coordinates and flux of Eq. (2.8); physical fields follow
from guazzotto_freidberg_to_equilibrium.
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- Guazzotto and Freidberg (2021), Eqs. (2.9), (3.2) and (A4); Part 2, Eqs. (5.12)-(5.14).
evaluate_miller
Machine-independentConvention-sensitive
source
evaluate_miller(surface, theta)
Points on a Miller-parameterized flux surface at given poloidal angles.
The Miller parameterization writes a shaped, up-down symmetric surface in a
handful of numbers: minor radius, centre, elongation, triangularity and –
optionally – squareness. This is the forward evaluation;
fit_miller_surface is the inverse.
| Name | Type | Unit | Description |
|---|---|---|---|
surface | MillerSurface | - | The shape parameters. |
theta | array_like | rad | Poloidal angles at which to evaluate. |
| Returns | |||
| — | tuple of np.ndarray | m |
|
Convention
R = r0 + r*(cos(theta + arcsin(delta)*sin(theta)) + b*sin(theta)**2*cos(theta))
and Z = z0 + kappa*r*sin(theta + zeta*sin(2*theta)), with b the
indentation. theta is the parameter
of the parameterization, not a geometric poloidal angle about the centre, and
the two differ once the surface is shaped. Positive delta shifts the
extremum of Z inboard. Positive zeta squares the surface off,
negative rounds it. zeta = 0 is the five-parameter surface exactly, to
the bit, so nothing that does not ask for squareness moves; the same holds
for indentation = 0. The indentation term vanishes at the four
cardinal points, and with alpha = arcsin(delta) the inboard side turns
concave – a bean – once b > (1 - alpha)**2/2. The surface is
up-down symmetric by construction, squareness included.
Raises
ValueErrorThe geometry is degenerate: minor radius or elongation not positive, triangularity of magnitude one or more, squareness of magnitude one half or more, or an indentation at or below-sqrt(1 - delta**2).
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Up-down symmetric and single-valued in theta, so it cannot represent a
separatrix, an X-point, or an asymmetric boundary at all. Triangularity is
bounded by one because the arcsine of it must exist. Squareness is bounded
by one half because d(theta + zeta*sin(2*theta))/dtheta = 1 +
2*zeta*cos(2*theta) changes sign beyond it: the parameterization then
doubles back and the “surface” crosses itself, so a larger magnitude does not
describe a squarer plasma but a curve that is not one. The indentation is
bounded below for the same reason: at equal height the two sides are
2 r cos(theta) (cos(alpha sin(theta)) + b sin(theta)**2) apart, which
stays positive for every theta exactly when b > -cos(alpha) =
-sqrt(1 - delta**2).
Provenance
- Miller, Chu, Greene, Lin-Liu and Waltz, Noncircular, finite aspect ratio, local equilibrium model, Phys. Plasmas 5, 973 (1998), which introduced this parameterization.
- The squareness term follows the extended form used for shape comparison, e.g. Barr, Boyer, Humphreys et al., ShapeFIT, Nucl. Fusion 58, 076011 (2018), App. A.
- The inboard-indentation term for bean-shaped plasmas (PBX/PBX-M) is VAFT’s, specified with its bean-onset criterion in issue #941.
evaluate_mxh_chebyshev
Machine-independentConvention-sensitive
source
evaluate_mxh_chebyshev(representation, rho, theta)
Show source vaft/process/_equilibrium_compact.py, lines 290–333
def evaluate_mxh_chebyshev(representation: MXHChebyshevRepresentation, rho: Any, theta: Any) -> tuple[np.ndarray, np.ndarray]:
"""Flux-surface points of an MXH-Chebyshev representation.
Parameters
----------
representation : MXHChebyshevRepresentation
A fitted representation [-].
rho : array_like
``sqrt(psi_N)``, in (0, 1] [-].
theta : array_like
Geometric poloidal angle of Eq. (7), broadcast against *rho* [rad].
Returns
-------
tuple of np.ndarray
``(R, Z)`` [m].
Convention
----------
Eqs. (6)-(11) of Xie and Li: ``R = R0 + h + rho a cos(theta_bar)``,
``Z = Z0 + v + kappa rho a sin(theta)``, every profile ``f(rho) = f_edge +
sum_l f_l (1 - rho**2) T_l(2 rho**2 - 1)``.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1] H. Xie and Y. Li, arXiv:2601.02942 (2026), Eqs. (6)-(11).
"""
rho, theta = np.broadcast_arrays(np.asarray(rho, dtype=float), np.asarray(theta, dtype=float))
basis = _radial_basis(rho.ravel(), representation.radial_order)
def profile(name: str) -> np.ndarray:
edge, coefficients = representation.profiles[name]
return (edge + basis @ np.asarray(coefficients)).reshape(rho.shape)
theta_bar = theta + profile("c0")
for m in range(1, representation.harmonics + 1):
theta_bar = theta_bar + profile(f"c{m}")*np.cos(m*theta) + profile(f"s{m}")*np.sin(m*theta)
radius = rho*profile("a")
return (representation.r0 + profile("h") + radius*np.cos(theta_bar),
representation.z0 + profile("v") + profile("kappa")*radius*np.sin(theta))Flux-surface points of an MXH-Chebyshev representation.
| Name | Type | Unit | Description |
|---|---|---|---|
representation | MXHChebyshevRepresentation | - | A fitted representation. |
rho | array_like | - |
|
theta | array_like | rad | Geometric poloidal angle of Eq. (7), broadcast against rho. |
| Returns | |||
| — | tuple of np.ndarray | m |
|
Convention
Eqs. (6)-(11) of Xie and Li: R = R0 + h + rho a cos(theta_bar),
Z = Z0 + v + kappa rho a sin(theta), every profile f(rho) = f_edge +
sum_l f_l (1 - rho**2) T_l(2 rho**2 - 1).
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- H. Xie and Y. Li, arXiv:2601.02942 (2026), Eqs. (6)-(11).
evaluate_solovev
Machine-independentConvention-sensitive
source
evaluate_solovev(model, r, z, *, cocos=11, convention=None)
Evaluate an analytic Solov’ev equilibrium on a set of points.
The Solov’ev solution is the Grad-Shafranov equation’s closed form for a pressure and squared poloidal current that are both linear in the poloidal flux. It is exact, which makes it the reference against which numerical machinery here is checked.
| Name | Type | Unit | Description |
|---|---|---|---|
model | SolovevEquilibrium | - | The homogeneous basis coefficients, the basis they expand in, and the two linear source slopes. |
r | array_like | m | Major radius, strictly positive. |
z | array_like | m | Height. |
cocos | int or None, optional | - | COCOS coordinate convention index (1-8, 11-18) setting the poloidal field orientation factor sigma = sigma_RphiZ * sigma_Bp; None selects the historical orientation fallback (k = -1). |
convention | int or None, optional | - | Alias for cocos. |
| Returns | |||
| — | Mapping of str to np.ndarray | - |
|
Convention
Works in poloidal flux per radian, with the poloidal field taken as
B_R = (sigma / R) dpsi/dz and B_Z = -(sigma / R) dpsi/dR, where
sigma = sigma_RphiZ * sigma_Bp is determined by cocos. The default
is vaft.data.cocos.VAFT_INTERNAL_COCOS (11), which has sigma = +1;
passing cocos=None retains the historical fallback sigma = -1
(the COCOS 2/3/6/7 orientation family).
j_phi is the component along the convention’s own toroidal unit
vector, -sigma_Bp (R dp/dpsi + F dF/dpsi / (mu0 R)) per Sauter Eq. 12,
which is what Ampere’s law gives on the returned field; the historical
fallback takes the right-handed member of its family (COCOS 3/7,
sigma_Bp = -1). Before #966 the sign was dropped, so a COCOS 11
model reported the current opposite to its own field.
solovev_to_equilibrium is where this is reconciled with a declared
convention, scaling the flux by 2*pi for full-weber conventions and
transforming signs appropriately. Pressure and the squared poloidal
current are linear in psi by construction, so their gradients are the
model’s own constants.
Raises
ValueErrorAny point has a non-positive major radius, or an invalid COCOS index is provided.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Linear sources only, so it cannot represent a peaked pressure profile or a realistic current density. The poloidal current is recovered as a square root, clipped at zero, so a model whose flux excursion drives the square negative silently flattens rather than failing.
Provenance
- Solov’ev, Hydromagnetic stability of closed plasma configurations (1968), for the linear-source closed form.
- The five-term homogeneous basis and the particular integral follow the standard polynomial construction for that solution.
- A. J. Cerfon and J. P. Freidberg, Phys. Plasmas 17, 032502 (2010), for the seven- and twelve-term bases of
basis="cerfon_freidberg_even"and"cerfon_freidberg".
extract_flux_surface_contours
Machine-independentConvention-sensitive
source
extract_flux_surface_contours(psi_grid, R, Z, psi_axis, psi_boundary, levels_norm)
Iso-flux contours at requested normalized levels, by marching squares.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_grid | array_like | Wb/rad | Poloidal flux on the grid, indexed |
R | array_like | m | Major-radius grid axis. |
Z | array_like | m | Height grid axis. |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis, the zero of the normalization |
psi_boundary | float | Wb/rad | Poloidal flux at the boundary, the one. |
levels_norm | sequence of float | - | Normalized flux levels to trace, 0 on axis and 1 at the boundary. |
| Returns | |||
| — | dict of float to list of tuple of np.ndarray | m | Each requested level mapped to its contour segments as |
Convention
The flux map is indexed major radius first, matching the IMAS data dictionary’s two-dimensional profile layout with the first and second grid dimensions as major radius and height. Levels are normalized, so the absolute unit of the three flux arguments cancels as long as they agree.
A level may have more than one contour. Inside a diverted plasma a single normalized level can trace disconnected pieces, so the value is always a list and a caller wanting “the” surface must choose among them, normally the closed one containing the magnetic axis.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Marching squares on the grid, so a contour is resolved only to the cell and a surface smaller than a few cells is poorly represented. Contours are returned in the order the algorithm finds them, not sorted by size or by containment.
Provenance
- The IMAS data dictionary layout for a two-dimensional flux profile, which fixes the axis order this assumes.
find_rational_surfaces
Machine-independent
source
find_rational_surfaces(psi_norm, q, n, *, m_range=None)
Where a toroidal mode resonates: the surfaces with q = m / n.
A perturbation with toroidal mode number n resonates where the safety
factor equals m / n for an integer m. This finds those crossings
by linear interpolation on the q profile, so the result is the
equilibrium’s own answer and needs no perturbed-equilibrium code.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_norm | array_like | - | Normalized poloidal flux, increasing. |
q | array_like | - | Safety factor on |
n | int | - | Toroidal mode number; its sign is ignored. |
m_range | tuple of int, optional | - |
|
| Returns | |||
| — | dict of str to ndarray | - |
|
Raises
ValueErrorpsi_normandqdiffer in length,psi_normdoes not increase, ornis zero.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Drop non-finite samples.
- For each integer
min range, find every bracket whereq - m/nchanges sign and interpolate the crossing linearly. - Sort the crossings outward.
Applicability
Machine-independent. Any monotonic radial coordinate; the name says
psi_norm because that is what the perturbed-equilibrium codes report
their rational surfaces on.
Limitations
Accuracy is that of linear interpolation, so the error scales with the
square of the local grid spacing and with the curvature of q – which
means it is worst exactly where q is steepest, at the edge. On the
grid GPEC itself writes, which is refined around the rational surfaces
(a spacing of 3.6e-06 at the outermost surface of the DIII-D example
against 7.4e-03 in the core), the surfaces come back to 1.4e-09 in
psi_norm. On a uniform grid of the kind an equilibrium reconstruction
produces they do not: 6.5e-04 at 129 points and 4.0e-05 at 513, in both
cases dominated by the outermost surface. Interpolate q onto a finer
grid before asking, if the answer has to be better than that.
A reversed-shear q resonates twice on the same m, and both
crossings are returned. A root that sits exactly on a grid point, or a
q that is flat at m / n over an interval, is returned once – at
the node and at the start of the flat region respectively – rather than
once per sign change, which would double-count it.
Provenance
gpec_multimode_metrics.rational_surface_tablederived the same surfaces from GPEC output; this takes them from the equilibrium, so they can be known before a run – at the accuracy the equilibrium’s own grid supports, which the Limitations quantify.
find_stationary_points
Machine-independentConvention-sensitive
source
find_stationary_points(equilibrium, *, kind=None)
Show source vaft/process/_equilibrium_parametric.py, lines 2732–2806
def find_stationary_points(equilibrium: Any, *, kind: str | None = None) -> tuple[StationaryPoint, ...]:
"""Locate the O-points and saddles of psi to sub-grid accuracy.
Every point where grad(psi) vanishes inside the grid is found by root finding
on a bicubic spline of psi, seeded from the local minima of |grad psi| on the
grid, and classified by the sign of its Hessian determinant. This is the
search :func:`derive_boundary_representation` runs before it decides the
topology, exposed on its own for a consumer that needs the magnetic axis or
the saddles without the rest of the boundary analysis.
Parameters
----------
equilibrium : EquilibriumData, ODS, GEQDSK or path
Adapted through :func:`as_equilibrium`; needs a psi map on an R-Z grid of
at least four points each way [-].
kind : {"o", "x"}, optional
Keep only extrema (``"o"``) or only saddles (``"x"``); both when not
given [-].
Returns
-------
tuple of StationaryPoint
Position, psi, normalized psi, kind, Hessian determinant and flattest
curvature of each point. O-points come first, nearest the axis flux
first; then saddles, nearest the boundary flux first; points without a
normalized flux last within their kind. Empty when the record has no
usable psi map. Positions in metres; psi in the record's flux unit;
the determinant in that unit squared per m^4 and the curvature in that
unit per m^2, so both scale with the record's flux normalization [-].
Raises
------
ValueError
*kind* is neither ``None``, ``"o"`` nor ``"x"``.
Convention
----------
Classification uses the sign of the Hessian determinant, which no COCOS
transform changes: a sign flip of psi negates both second derivatives and
leaves the determinant alone. An O-point may therefore be a maximum or a
minimum of psi. ``psi_n`` is ``(psi - psi_axis) / (psi_boundary - psi_axis)``
in the record's own convention, and NaN when the record lacks those two
values.
Applicability
-------------
Machine-independent.
Limitations
-----------
A saddle is only a candidate X-point: numerical saddles far from the plasma
are common on reconstructed maps. Whether one bounds the plasma is decided
by :func:`derive_boundary_representation`. Searches start only from
interior grid nodes and keep only roots strictly inside the grid, so a point
in the outermost cell may be missed; two closer than half a cell are
reported once.
Provenance
----------
.. [1] The Hessian classification of stationary points of psi is the standard
O-point and X-point test.
"""
if kind not in (None, "o", "x"):
raise ValueError(f"kind must be None, 'o' or 'x'; got {kind!r}")
points = _stationary_points(as_equilibrium(equilibrium))
if kind is not None:
points = tuple(point for point in points if point.kind == kind)
def order(point: StationaryPoint) -> tuple[int, int, float]:
target = 0.0 if point.kind == "o" else 1.0
known = bool(np.isfinite(point.psi_n))
return (0 if point.kind == "o" else 1, 0 if known else 1,
abs(point.psi_n - target) if known else 0.0)
return tuple(sorted(points, key=order))Locate the O-points and saddles of psi to sub-grid accuracy.
Every point where grad(psi) vanishes inside the grid is found by root finding
on a bicubic spline of psi, seeded from the local minima of |grad psi| on the
grid, and classified by the sign of its Hessian determinant. This is the
search derive_boundary_representation runs before it decides the
topology, exposed on its own for a consumer that needs the magnetic axis or
the saddles without the rest of the boundary analysis.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
kind | {"o", "x"}, optional | - | Keep only extrema ( |
| Returns | |||
| — | tuple of StationaryPoint | - | Position, psi, normalized psi, kind, Hessian determinant and flattest curvature of each point. O-points come first, nearest the axis flux first; then saddles, nearest the boundary flux first; points without a normalized flux last within their kind. Empty when the record has no usable psi map. Positions in metres; psi in the record’s flux unit; the determinant in that unit squared per m^4 and the curvature in that unit per m^2, so both scale with the record’s flux normalization. |
Convention
Classification uses the sign of the Hessian determinant, which no COCOS
transform changes: a sign flip of psi negates both second derivatives and
leaves the determinant alone. An O-point may therefore be a maximum or a
minimum of psi. psi_n is (psi - psi_axis) / (psi_boundary - psi_axis)
in the record’s own convention, and NaN when the record lacks those two
values.
Raises
ValueErrorkind is neitherNone,"o"nor"x".
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
A saddle is only a candidate X-point: numerical saddles far from the plasma
are common on reconstructed maps. Whether one bounds the plasma is decided
by derive_boundary_representation. Searches start only from
interior grid nodes and keep only roots strictly inside the grid, so a point
in the outermost cell may be missed; two closer than half a cell are
reported once.
Provenance
- The Hessian classification of stationary points of psi is the standard O-point and X-point test.
fit_fourier_surface
Machine-independentConvention-sensitive
source
fit_fourier_surface(contour, *, modes=6, radial_value=None, radial_coordinate='psi_n', max_normalized_rms=0.02)
Represent one closed flux-surface contour as a truncated Fourier series.
The general counterpart of fit_miller_surface: where Miller holds a
surface in a few interpretable, up-down symmetric numbers, this keeps every
harmonic up to modes in both R and Z, sine and cosine, so up-down
asymmetry and higher-order shaping are represented rather than left in the
residual. The truncation error it reports answers how many harmonics the
surface needs.
| Name | Type | Unit | Description |
|---|---|---|---|
contour | Contour or tuple of array_like | m | The closed surface, as a contour record or an |
modes | int, optional | - | Highest poloidal harmonic kept, at least one. |
radial_value | float, optional | - | The surface’s radial coordinate, carried onto the result for the caller’s bookkeeping. |
radial_coordinate | str, optional | - | Which coordinate radial_value is in. |
max_normalized_rms | float, optional | - | Largest geometric RMS error, divided by the contour’s half radial extent, that still counts as accepted. |
| Returns | |||
| — | FourierFitResult | - | The surface, the resampled observed contour, the reconstruction on the same angles, the symmetric geometric RMS, its normalized form, the maximum and Hausdorff distances, acceptance with a reason, and the derivation record. |
Convention
The angle is theta = 2 pi s / L, arc length counter-clockwise over the
perimeter, with its origin where the first harmonic of R peaks (so
r_sin[1] = 0); the m = 0 terms are the perimeter centroid. An up-down symmetric surface therefore has r_sin = 0 and
z_cos[1:] = 0, and a translation changes the m = 0 terms only.
This is the same definition derive_boundary_representation uses
for the LCFS, so there is one Fourier convention for a contour, not two.
Purely geometric: no flux, field or COCOS enters.
Raises
ValueErrormodes is below one, or the contour has zero arc length or zero radial extent.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Resample the contour to
max(256, 8*modes)points uniform in arc length. - Orient it counter-clockwise and start at its largest R.
- Solve the cosine and sine coefficients of R and Z by least squares.
- Rotate the angle origin so the first harmonic of R is a pure cosine
(
r_sin[1] = 0,r_cos[1] > 0), which fixes the phase independently of the contour’s sampling and starting point. - Score the reconstruction geometrically: contour-to-model and model-to-contour nearest distances on a dense evaluation.
Defaults
modes = 6 and max_normalized_rms = 0.02 are numerical conveniences:
six harmonics resolve the shaped surfaces of a tokamak to well under a
percent, and the two-percent threshold is the one the Miller fit uses, so
the two acceptances are comparable.
Applicability
Machine-independent.
Limitations
The phase origin is undefined only for a contour with no first harmonic in
R, which no closed plasma surface is. max_error and
hausdorff_distance are the same number, the larger of the two directed
maxima, kept separately to parallel MillerFitResult. Arc length is not a flux-coordinate angle, so the
coefficients describe shape, not straight-field-line harmonics.
Provenance
- Least-squares Fourier series of a closed curve; the convention is VAFT’s (#945), shared with the LCFS representation.
fit_fourier_surface_sequence
Machine-independentConvention-sensitive
source
fit_fourier_surface_sequence(equilibrium, levels, *, radial_coordinate='psi_n', modes=6, max_normalized_rms=0.02)
Represent a sequence of internal flux surfaces as Fourier series.
Traces each requested surface and fits it with fit_fourier_surface,
so each harmonic becomes a radial profile such as R_2^c(rho) that can
be compared between equilibria independently of their grids.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
levels | sequence of float | - | Radial positions to represent, in radial_coordinate. |
radial_coordinate | str, optional | - | Coordinate of levels; anything other than normalized poloidal flux is converted through |
modes | int, optional | - | Highest harmonic kept on every surface. |
max_normalized_rms | float, optional | - | Passed to |
| Returns | |||
| — | FourierSequenceResult | - | The fits sorted by radial value, the levels that gave no closed surface, and the derivation record. |
Convention
Every surface uses the arc-length convention of fit_fourier_surface,
so coefficients of one harmonic are comparable across the sequence. The
radial coordinate follows derive_radial_coordinates.
Raises
ValueErrorradial_coordinate is not available for this equilibrium.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Convert the levels to normalized poloidal flux when needed.
- Trace the closed surface at each level; a level outside the coordinate’s range, or whose surface is open or missing, is recorded as skipped rather than dropped or clamped silently.
- Fit each traced surface and sort the fits by radial value.
Applicability
Machine-independent.
Limitations
Near an X-point the arc-length series needs many harmonics to follow the corner, which the acceptance threshold reports rather than hides. Radial derivatives of the coefficients are not computed yet.
Provenance
- See
fit_fourier_surface; the surface tracing is the onefit_miller_sequenceuses.
fit_free_boundary_coils
Machine-independentConvention-sensitive
source
fit_free_boundary_coils(equilibrium, machine, *, method='flux', boundary_samples=96, x_points=None, regularization=0.0, current_penalties=None, current_bounds=None, flux_weight=None, field_weight=None, current_scale_A=None, samples_per_axis=5, flux_tolerance=0.02, max_flux_tolerance=0.04, field_tolerance_T=0.01, ip_tolerance=0.05)
Fit physical PF circuit currents to a prescribed LCFS in free space.
flux_normal adds B dot n to relative-flux rows. Prescribed X-points
always use separate Br=Bz=0 rows rather than a singular tangent.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData | - | Target with a closed LCFS, grid, source profiles and declared COCOS. |
machine | ODS-like | - | Machine |
method | str, optional | - |
|
boundary_samples | int, optional | - | Number of equally spaced LCFS points. |
x_points | sequence, optional | m | Prescribed saddle coordinates as |
regularization | float, optional | - | Tikhonov coefficient on scaled circuit currents. |
current_penalties | mapping, optional | - | Nonnegative penalty multiplier for each named circuit. |
current_bounds | mapping, optional | A | Per-circuit lower and upper physical currents. |
flux_weight | float, optional | 1/Wb | Objective multiplier for relative flux. |
field_weight | float, optional | 1/T | Objective multiplier for normal and saddle fields. |
current_scale_A | float, optional | A | Current dividing the regularization variables. |
samples_per_axis | int, optional | - | Subsamples per plasma-cell axis for LCFS area weights. |
flux_tolerance | float, optional | - | Acceptance limit for RMS relative flux divided by target span. |
max_flux_tolerance | float, optional | - | Acceptance limit for maximum relative flux divided by target span. |
field_tolerance_T | float, optional | T | Acceptance limit for RMS normal and maximum saddle field. |
ip_tolerance | float, optional | - | Acceptance limit for integrated versus declared plasma current. |
| Returns | |||
| — | CoilFitResult | - | Currents, separate plasma/coil fields, residuals and diagnostics. |
Convention
All flux responses are full weber in the Green orientation; COCOS 11
target psi has the opposite field-derivative orientation. Circuit currents
are A in the signed-turn pf_active convention. Relative flux discards
the additive gauge. accepted requires both residual limits and finite
caller-supplied bounds for every circuit.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
Convert the source profiles to COCOS 11, integrate toroidal plasma current inside fractional LCFS cells, and evaluate exact ring Green responses for the plasma and signed-turn PF rectangles. Assemble weighted boundary and saddle rows, then solve bounded, optionally regularized least squares.
Defaults
As a numerical convenience, omitted flux and field weights divide by the target flux span and peak plasma boundary field, and the current scale defaults to |Ip|/number of circuits. No hardware current limits are inferred from geometry.
Applicability
Machine-independent. Axisymmetric free-space coils and static equilibria.
Limitations
Passive currents, iron, surface-current sheets and toroidal flow are not modeled. Highly conditioned current vectors need hardware bounds and later free-boundary closure for a physical interpretation.
Provenance
- Exact kernels in
vaft.process.electromagneticsand the COCOS-aware source profiles invaft.data.equilibrium.EquilibriumData.
fit_miller_sequence
Machine-independentConvention-sensitive
source
fit_miller_sequence(equilibrium, levels, *, radial_coordinate='psi_n', max_normalized_rms=0.02)
Fit Miller parameters across a sequence of flux surfaces, with radial derivatives.
A local equilibrium model needs not only the shape of each surface but how the shape changes outward, so this traces every requested level, fits each, and differentiates the accepted ones against the fitted minor radius.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
levels | sequence of float | - | The radial positions to fit, in radial_coordinate. |
radial_coordinate | str, optional | - | Which coordinate levels are given in. Anything other than the normalized poloidal flux is converted through |
max_normalized_rms | float, optional | - | Passed to |
| Returns | |||
| — | MillerSequenceResult | - | Every fit in the order requested, including the rejected ones; the reason radial derivatives could not be formed, if they could not; and the derivation record. Accepted surfaces additionally carry the derivatives of centre, elongation and triangularity against minor radius, plus the safety factor, the magnetic shear and the ballooning parameter. |
Convention
The magnetic shear is (r/q) dq/dr and the ballooning parameter is
-2*mu0*q^2*R0*(dp/dr)/Bt0^2, both against the fitted minor radius, not
against a flux coordinate. Only an X-point that is active, meaning relevant
to the boundary, vetoes a fit; a saddle elsewhere in the domain must not.
The safety factor and pressure are taken from the equilibrium as stored, in
whatever sign convention it carries.
Raises
ValueErrorThe requested radial coordinate cannot be derived from this equilibrium.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Convert the requested levels into normalized poloidal flux when they are given in another coordinate.
- Trace the contour at each level and keep the closed one containing the magnetic axis.
- Fit each with
fit_miller_surface, vetoing surfaces within five percent of a minor radius of an active X-point. - On the accepted surfaces sorted by minor radius, spline-differentiate the three shape parameters, then the safety factor for the magnetic shear, and the pressure for the ballooning parameter.
Defaults
The five percent X-point proximity is a numerical convenience marking where the surface stops being locally Miller-like; so is the requirement of four accepted surfaces for radial derivatives, which is the smallest set a cubic spline can use.
Applicability
Machine-independent.
Limitations
Rejected surfaces are returned with the rest, so a caller must filter on acceptance before using them. Derivatives are unavailable when fewer than four surfaces are accepted or when the fitted minor radii are not strictly increasing, and the reason says which. The shear and the ballooning term additionally need the safety factor, the pressure and the vacuum field, and are simply absent without them.
Provenance
- Miller et al., Phys. Plasmas 5, 973 (1998); the radial derivatives are what makes the parameterization a local equilibrium model rather than a shape description.
fit_miller_surface
Machine-independentConvention-sensitive
source
fit_miller_surface(contour, *, radial_value=None, radial_coordinate='psi_n', max_normalized_rms=0.02, near_xpoint=False, squareness=False, indentation=False)
Fit the Miller shape parameters to one closed flux-surface contour.
Answers “how Miller-like is this surface, and with what parameters”, and says when the answer is that it is not Miller-like enough to use. Acceptance is a property of the fit, reported on the result rather than raised.
| Name | Type | Unit | Description |
|---|---|---|---|
contour | Contour or tuple of array_like | m | The closed surface to fit, as a contour record or an |
radial_value | float, optional | - | The radial coordinate of this surface, carried onto the fitted surface for the caller’s bookkeeping and used for the near-boundary veto. |
radial_coordinate | str, optional | - | Which radial coordinate radial_value is in. |
max_normalized_rms | float, optional | - | Largest residual, as a root-mean-square distance divided by the fitted minor radius, that still counts as accepted. |
near_xpoint | bool, optional | - | Caller’s determination that this surface lies close to an active X-point, which vetoes acceptance regardless of the residual. |
squareness | bool, optional | - | Fit the sixth parameter, |
indentation | bool, optional | - | Fit the inboard indentation as well, for bean-shaped surfaces. Off by default for the same reason as squareness. |
| Returns | |||
| — | MillerFitResult | - | The fitted surface, the observed and reconstructed contours, the residual statistics in metres, whether the solve converged, whether the fit is accepted, the reason if not, and the derivation record. |
Convention
Purely geometric: it reads a contour and returns shape parameters, touching no flux, no field and no COCOS. radial_value is carried, never interpreted. The residual is a symmetric point-set distance in metres, and the normalized form divides it by the fitted minor radius so the threshold means the same thing on every surface.
Raises
ValueErrorThe contour has zero arc length or zero radial extent.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Resample the contour to a uniform arc-length parameterization.
- Seed the five parameters from the contour’s bounding box.
- Solve a bounded least squares on point-to-surface distances, refining each point’s local angle by five Newton steps per evaluation so the residual is a true nearest-point distance rather than an index-matched one.
- Score with a symmetric distance: contour-to-model and model-to-contour, giving a root-mean-square, a maximum, and a Hausdorff distance.
- Reject, in order, a surface too near the boundary, a surface near an X-point, a fitted indentation that makes the surface cross itself, a residual over the threshold, or a solve that did not converge.
Defaults
max_normalized_rms = 0.02 is a validated-workflow threshold, not a
physical constant: two percent of the minor radius is where a fitted surface
stops being a useful stand-in for the traced one. The parameter bounds,
elongation in 0.05 to 10 and triangularity within 0.999, are numerical
conveniences that keep the solve inside the parameterization’s own domain;
so is the indentation box of -0.95 to 2, whose delta-dependent lower limit
is enforced after the solve instead.
Applicability
Machine-independent.
Limitations
Miller is up-down symmetric, so a genuinely asymmetric surface fits poorly by construction and is rejected rather than approximated. Without squareness a surface whose corners are squarer or rounder than a sine can express has nowhere to put that shape either: it goes into the residual, and past max_normalized_rms the fit is rejected as “not Miller-like enough to use” when what is true is “not five-parameter Miller-like” (#867). Surfaces at or beyond 95.5 percent of the normalized flux are rejected outright, because separatrix geometry is not locally Miller-like there. The X-point proximity has to be determined by the caller; this function only honours the flag. Unlike every other derived quantity in this module, the provenance recorded here carries no convention, tracked in #604.
Provenance
- Miller et al., Phys. Plasmas 5, 973 (1998), for the parameterization being fitted.
fit_mxh_chebyshev
Machine-independentConvention-sensitive
source
fit_mxh_chebyshev(equilibrium, *, harmonics=2, radial_order=4, surfaces=24, tolerance=0.01, boundary_tolerance=0.02)
Represent an equilibrium’s flux surfaces by MXH shapes with Chebyshev radial profiles.
Every surface rho = sqrt(psi_N) is an MXH shape – centre, minor radius,
elongation and a harmonic distortion of the angle – and each of those
numbers is a shifted-Chebyshev series in rho anchored to its LCFS
value. The coefficient vector is a compact, grid-independent stand-in for
the flux map, and the fit reports how faithful it is.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
harmonics | int, optional | - | MXH harmonics |
radial_order | int, optional | - | Chebyshev order |
surfaces | int, optional | - | Traced surfaces the profiles are fitted to. |
tolerance | float, optional | - | Largest RMS normalized-flux error at which the fit is accepted. |
boundary_tolerance | float, optional | - | Largest RMS boundary error, in minor radii, at which it is accepted. |
| Returns | |||
| — | MXHChebyshevRepresentation | - |
|
Convention
The radial coordinate is rho_psi = sqrt(psi_N) (Eq. 4), so the flux
is psi_axis + (psi_boundary - psi_axis) rho**2 exactly and the
geometric profiles carry the shape; R0 and Z0 are the LCFS centre,
so h and v vanish there. Purely geometric: no COCOS enters.
Raises
ValueErrorharmonics below one, a negative radial_order, fewer surfaces than the radial order needs, or no psi map or LCFS.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Trace surfaces at
rhofrom 0.15 to 0.97 and take the LCFS asrho = 1. - Extract each surface’s MXH parameters: centre and minor radius from its radial extent, elongation from its height, the geometric angle from Eq. (7), the distorted angle from Eq. (6), and their difference as a harmonic series (Eq. 8).
- Fit every parameter’s radial profile as
f_edge + sum_l f_l u_l(rho)withu_lof Eq. (10), the edge value fixed to the LCFS. - Evaluate the representation on a (rho, theta) grid and measure
psi_N(R, Z) - rho**2on the equilibrium’s own flux map.
Defaults
harmonics = 2 and radial_order = 4 give 9 x 5 = 45 parameters,
inside the paper’s “fewer than 100” for high fidelity; tolerance =
0.01 matches its 1e-2 accuracy level and boundary_tolerance = 0.02
leaves room for a diverted corner. All are numerical conveniences.
Applicability
Machine-independent.
Limitations
Xie and Li use this basis as the unknown of a Grad-Shafranov solver; here
it only represents an existing equilibrium, so no force balance is
imposed and the source profiles are not fitted. A diverted LCFS has a
corner an MXH shape of low order cannot follow; the boundary error says
so. The profiles are anchored at rho = 1 only, so the axis position
is an extrapolation, reported as a metric.
Provenance
- H. Xie and Y. Li, What is the minimum number of parameters required to represent solutions of the Grad-Shafranov equation?, arXiv:2601.02942 (2026), Eqs. (4), (6)-(11).
fit_solovev
Machine-independentConvention-sensitive
source
fit_solovev(equilibrium, *, basis='cerfon_freidberg', psi_n_max=1.0, tolerance=0.02)
Represent an equilibrium by the Solov’ev solution closest to its flux map.
The inverse of solve_solovev_constraints: instead of shaping a
Solov’ev model to requested boundary points, find the constant p' and
FF' and the homogeneous coefficients whose flux best matches a given
equilibrium inside its boundary. The flux is linear in all of them, so the
fit is one linear least squares; how far the result is from the
equilibrium is reported, not assumed.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
basis | str, optional | - | Homogeneous basis of |
psi_n_max | float, optional | - | Outermost normalized flux included in the fit. |
tolerance | float, optional | - | Largest RMS flux error, and boundary error in minor radii, at which the fit is accepted. |
| Returns | |||
| — | SolovevFit | - | The Solov’ev model (per-radian flux, |
Convention
The fit is done in per-radian flux, so the returned pprime and
ffprime are per weber per radian whatever the input convention. The
model carries the record’s orientation, which the metrics do not depend
on; exporting it with solovev_to_equilibrium needs the record’s
COCOS as the target convention, not the default. Errors are relative to the flux span
|psi_boundary - psi_axis| and to the minor radius.
Raises
ValueErrorAn unknown basis, or a record without psi, an LCFS or a declared flux unit.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Convert the flux to per radian and select the grid points inside the
LCFS with
psi_N <= psi_n_max. - Solve for the basis coefficients,
p'andFF'by least squares on the flux there. - Evaluate the fitted model on the grid and measure the flux, field, axis, boundary and topology errors against the equilibrium.
Defaults
basis = "cerfon_freidberg" (twelve terms, up-down asymmetry allowed)
and tolerance = 0.02 are numerical conveniences: two percent of the
flux span is roughly where a Solov’ev stand-in stops being useful for
shape and field studies.
Applicability
Machine-independent.
Limitations
A Solov’ev model has constant sources, so a reconstruction with peaked or
hollow profiles fits only approximately, and the fitted p' and FF'
are effective values, not the equilibrium’s. A poor fit is returned with
status poor_fidelity, not rejected.
Provenance
- Solov’ev (1968) and Cerfon and Freidberg, Phys. Plasmas 17, 032502 (2010), for the model; the fit is the linear inverse of #1166.
flux_surface_quantities
Machine-independentConvention-sensitive
source
flux_surface_quantities(psi_grid, R, Z, psi_axis, psi_boundary, levels_norm, *, f_profile=None, axis_rz=None, boundary=None, min_points=16)
Flux-surface averages and shape parameters on each normalized flux level.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_grid | array_like | Wb/rad | Poloidal flux on the grid, indexed |
R | array_like | m | Major-radius grid axis. |
Z | array_like | m | Height grid axis. |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis. |
psi_boundary | float | Wb/rad | Poloidal flux at the boundary. |
levels_norm | sequence of float | - | Normalized flux levels to evaluate on, 0 on axis and 1 at the boundary |
f_profile | array_like, optional | T m | Poloidal current function |
axis_rz | tuple of float, optional | m | Magnetic axis position, used to pick the confined segment when a level traces several. |
boundary | tuple of np.ndarray, optional | m | Boundary outline to use at the outermost level instead of tracing it. |
min_points | int, optional | - | Fewest contour vertices a level needs before it is treated as resolved |
| Returns | |||
| — | dict of str to np.ndarray | - | One array per name in |
Convention
Per radian. The normalization by axis and boundary flux is scale-free, but
the flux gradient that weights every average is not, which is why the
per-radian contract matters. dvolume_dpsi and darea_dpsi are likewise
per radian; a caller storing them against a weber flux divides by 2*pi.
The average weights by the volume element,
<X> = closed_integral(X R dl / |grad psi|) / closed_integral(R dl / |grad psi|),
giving gm1 = <1/R^2>, gm8 = <R>, gm9 = <1/R> and, with
f_profile, gm5 = <B^2> from |B|^2 = (|grad psi|^2 + F^2)/R^2.
bp_dl is instead the plain contour integral of the poloidal field, which is
what an integral of that field squared over volume needs, since the volume
element cancels one power of it. The flux map is indexed major radius first,
matching extract_flux_surface_contours.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Trace the contour at each level, taking the confined segment when several are returned, and substituting the supplied outline at the boundary.
- Form the averages and the shape parameters on each resolved contour.
- Return NaN for a level whose contour is missing or below min_points, then fill those by interpolation against the square root of the normalized flux, the coordinate in which near-axis geometry is linear.
- Set the on-axis level to its exact point limits.
Defaults
min_points is a numerical convenience tied to what a flux map can resolve;
see MIN_FLUX_SURFACE_POINTS for the measurement behind its value.
Applicability
Machine-independent.
Limitations
The innermost one or two levels are the least accurate everywhere, because the
flux gradient is small and varies fastest there. That is a property of the map,
not of this routine: on the packaged OMFIT reference the stored
dvolume_dpsi runs 8.53, 22.85, 29.59, 29.05, 28.85 from the axis out, a
ramp that cannot be physical since the derivative approaches a finite limit,
while the trace here gives a smooth 30.8, 29.3, 29.3. Compare against a
reference only outside a normalized flux of about 0.05, and check the
near-axis values against the integral of the derivative recovering the volume
instead.
On axis the surface is a point: the volume, area and surface are exactly zero and the three geometric averages take their axis limits, but the shape parameters and the two derivatives are undefined there and are extrapolated from the innermost resolved surface.
Provenance
- The flux-surface average as the IMAS data dictionary defines the
gmfamily, and the same contour extractionextract_flux_surface_contoursprovides.
fractional_cell_weights_from_boundary
Machine-independentConvention-sensitive
source
fractional_cell_weights_from_boundary(R_grid, Z_grid, R_bdry, Z_bdry, samples_per_axis=5)
Fractional area of each grid cell that lies inside the plasma boundary.
An internal replacement for the externally supplied weight map EFIT expects, computed from the boundary polygon so that no external file is needed.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid | array_like | m | Major-radius grid, as an axis or a mesh. |
Z_grid | array_like | m | Height grid, as an axis or a mesh. |
R_bdry | array_like | m | Major radius of the boundary contour. |
Z_bdry | array_like | m | Height of the boundary contour. |
samples_per_axis | int, optional | - | Sub-samples per axis within each cell, at least one. |
| Returns | |||
| — | np.ndarray | - | Area fraction in |
Convention
A fraction, not a mask: a cell the boundary crosses gets its area share rather than a zero or a one. That is what makes an integral weighted by these converge with grid resolution instead of stepping.
Raises
ValueErrorsamples_per_axis is less than one.
Defaults · Applicability · Limitations · Provenance
Defaults
samples_per_axis = 5 is a numerical convenience: twenty-five sub-samples
per cell resolve the boundary to about a fifth of a cell, which is finer than
the flux map that produced the boundary in the first place.
Applicability
Machine-independent.
Limitations
Sub-sampling, not exact polygon clipping, so a cell is resolved only to the sub-sample spacing and a boundary feature smaller than a cell is missed. Assumes a single closed polygon.
Provenance
- Replaces EFIT’s externally provided cell-weight map; consumed by
shafranov_integralsandefit_virial_volume_integrals.
grad_shafranov_operator
Machine-independentConvention-sensitive
source
grad_shafranov_operator(psi, r, z)
Show source vaft/process/equilibrium.py, lines 4793–4851
def grad_shafranov_operator(psi: Any, r: Any, z: Any) -> np.ndarray:
"""The Grad-Shafranov elliptic operator on a rectangular grid.
Parameters
----------
psi : array_like
Poloidal flux on the grid, indexed ``(R, Z)`` [Wb/rad].
r : array_like
Major-radius grid axis [m].
z : array_like
Height grid axis [m].
Returns
-------
np.ndarray
The operator applied to the flux, on the same grid [Wb/(rad m^2)].
Raises
------
ValueError
The flux is not indexed ``(R, Z)`` with the given axes.
Convention
----------
``Delta* psi = d2psi/dR2 - (1/R) dpsi/dR + d2psi/dZ2``, the axisymmetric
elliptic operator whose source is the Grad-Shafranov right-hand side. The flux
is indexed major radius first, the orientation an EFIT-sourced ODS stores.
The result scales with the flux unit, so it is per radian for a per-radian
flux; comparing it against a source term requires both in the same convention.
Applicability
-------------
Machine-independent.
Limitations
-----------
Second-order central differences, one-sided at the edges, so the outermost two
cells inherit that lower accuracy. A caller comparing against a source term
should ignore that frame, as :func:`grad_shafranov_residual` does.
Provenance
----------
.. [1] The Grad-Shafranov equation in its standard axisymmetric form.
"""
psi = np.asarray(psi, dtype=float)
r = np.asarray(r, dtype=float).ravel()
z = np.asarray(z, dtype=float).ravel()
if psi.ndim != 2:
raise ValueError(f"psi must be a 2-D (R, Z) grid; got shape {psi.shape}")
if psi.shape != (r.size, z.size):
raise ValueError(
f"psi has shape {psi.shape} but the grid is (R={r.size}, Z={z.size}); "
"psi is indexed (R, Z)"
)
dpsi_dr = np.gradient(psi, r, axis=0, edge_order=2)
d2psi_dr2 = np.gradient(dpsi_dr, r, axis=0, edge_order=2)
d2psi_dz2 = np.gradient(np.gradient(psi, z, axis=1, edge_order=2), z, axis=1, edge_order=2)
return d2psi_dr2 - dpsi_dr / r[:, None] + d2psi_dz2The Grad-Shafranov elliptic operator on a rectangular grid.
| Name | Type | Unit | Description |
|---|---|---|---|
psi | array_like | Wb/rad | Poloidal flux on the grid, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
| Returns | |||
| — | np.ndarray | Wb/(rad m^2) | The operator applied to the flux, on the same grid. |
Convention
Delta* psi = d2psi/dR2 - (1/R) dpsi/dR + d2psi/dZ2, the axisymmetric
elliptic operator whose source is the Grad-Shafranov right-hand side. The flux
is indexed major radius first, the orientation an EFIT-sourced ODS stores.
The result scales with the flux unit, so it is per radian for a per-radian
flux; comparing it against a source term requires both in the same convention.
Raises
ValueErrorThe flux is not indexed(R, Z)with the given axes.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Second-order central differences, one-sided at the edges, so the outermost two
cells inherit that lower accuracy. A caller comparing against a source term
should ignore that frame, as grad_shafranov_residual does.
Provenance
- The Grad-Shafranov equation in its standard axisymmetric form.
grad_shafranov_residual
Machine-independentConvention-sensitive
source
grad_shafranov_residual(psi, r, z, *, psi_1d, pprime, ffprime, psi_axis=None, psi_boundary=None, boundary_r=None, boundary_z=None, n_surfaces=32, psi_norm_max=0.99, edge_cells=2)
How far an equilibrium is from satisfying its own force balance, surface by surface.
The Grad-Shafranov equation says a flux map and its own pressure and poloidal current gradients are not independent. Evaluating both sides on the stored grid says how well a particular reconstruction closed it, which is a different and stricter question than whether a solver’s iteration converged. The number EFIT reports as its Grad-Shafranov error is the latter.
| Name | Type | Unit | Description |
|---|---|---|---|
psi | array_like | Wb/rad | Poloidal flux on the grid, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
psi_1d | array_like | Wb/rad | Flux abscissa the two source profiles are given on. |
pprime | array_like | Pa rad/Wb | Pressure gradient against flux. |
ffprime | array_like | T^2 m^2 rad/Wb | Poloidal current term’s gradient against flux. |
psi_axis | float, optional | Wb/rad | Poloidal flux on the magnetic axis. |
psi_boundary | float, optional | Wb/rad | Poloidal flux at the boundary. |
boundary_r | array_like, optional | m | Major radius of the separatrix, used to exclude vacuum points. |
boundary_z | array_like, optional | m | Height of the separatrix. |
n_surfaces | int, optional | - | How many flux surfaces to reduce the residual onto. |
psi_norm_max | float, optional | - | Outermost normalized flux included. |
edge_cells | int, optional | - | Grid cells framed off the border, where the stencil is one-sided. |
| Returns | |||
| — | GradShafranovResidual | - | The residual reduced along the flux coordinate, with the normalization it was scaled by and the point count that survived the masks. |
Convention
Every flux quantity here must be per radian, COCOS 1 to 8. The textbook
equation takes this form only in that convention: rescaling to full weber
multiplies the left side by 2*pi and divides each right-hand derivative by
it, so the two sides move apart by (2*pi)**2. A caller holding a
dictionary-conformant ODS converts first; the wrapper
vaft.omas.compute_grad_shafranov_residual does it for them.
Raises
ValueErrorThe three profiles are not the same length, or no grid point survives the boundary, the flux-range trim, and the edge frame together.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
The two source profiles are interpolated onto the grid through the flux itself, so the residual is meaningful only where they are the equilibrium’s own.
Pass the boundary. Outside the separatrix the flux is not monotonic; it folds back into the normalized range near the poloidal field coils, so a flux-value cutoff alone re-admits vacuum points where the two profiles are extrapolation and the operator is reading coil current. On a free-boundary grid that is a third of the points and two orders of magnitude of spurious residual, and it does not affect a fixed-boundary grid at all, so comparing the two without it is not comparing like with like.
Provenance
- The Grad-Shafranov equation; the operator is
grad_shafranov_operatorand the source term is formed from the two profiles the equilibrium stores.
grad_shafranov_residual_modes
Machine-independentConvention-sensitive
source
grad_shafranov_residual_modes(equilibrium, levels, *, max_mode=8, radial_coordinate='psi_n', residual_map=None)
Project the Grad-Shafranov residual onto poloidal harmonics on nested flux surfaces.
A pointwise or surface-RMS residual says that force balance fails; the
harmonics say how: a large m = 0 term is a radial offset of the
whole surface’s balance, while m >= 1 terms follow the shaping. The
angle is the arc-length angle of fit_fourier_surface, so residual
and shape harmonics of the same surface are directly comparable.
| Name | Type | Unit | Description |
|---|---|---|---|
equilibrium | EquilibriumData, ODS, GEQDSK or path | - | Adapted through |
levels | sequence of float | - | Surfaces to analyse, in radial_coordinate. |
max_mode | int, optional | - | Highest harmonic kept, at least zero. |
radial_coordinate | str, optional | - | Coordinate of levels, converted through |
residual_map | array_like, optional | T/m | A residual on the record’s grid, indexed |
| Returns | |||
| — | GradShafranovResidualModes | - | Per surface: the radial value, the cosine and sine coefficients of the residual for |
Convention
The residual is grad_shafranov_residual’s delta_star - source
in per-radian flux (T/m); a full-weber record is converted by its declared
convention. Coefficients are raw, in T/m: G(theta) = sum_m a_m cos(m
theta) + b_m sin(m theta) with the arc-length angle and phase of
vaft.data.equilibrium.FourierSurface. Divide by scale for a
normalized form.
Raises
ValueErrorA negative max_mode, a record without the profiles or without a known flux unit, an unavailable radial coordinate, or a residual map of the wrong shape.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Evaluate
G = Delta* psi - (-mu0 R^2 p' - FF')on the grid withgrad_shafranov_residual, in per-radian flux. - Trace each requested surface and resample it uniformly in arc length
from its outboard side, as
fit_fourier_surfacedoes. - Interpolate
Gonto the samples and take its discrete Fourier coefficients, then rotate them by the surface’s canonical phase.
Defaults
max_mode = 8 is a numerical convenience; the surface is sampled at
max(256, 8*max_mode) points, at least four per wavelength of the
highest harmonic, so the kept modes do not alias.
Applicability
Machine-independent.
Limitations
The angle is geometric (arc length), not a straight-field-line angle; a PEST projection waits on the flux-coordinate framework (#472). The residual is a finite-difference quantity, so near a sharp boundary or on a coarse grid its harmonics carry discretization error of the grid scale.
Provenance
- The projection of a force-balance residual onto poloidal harmonics follows the moment step of variational-moment equilibrium methods (VMOMS); here it is a diagnostic of an existing equilibrium (#948).
guazzotto_freidberg_parameters
Machine-independentConvention-sensitive
source
guazzotto_freidberg_parameters(model, *, q0=1.0, beta0=None, resolution=500)
The dimensionless plasma parameters of Section 6 for a Guazzotto-Freidberg equilibrium.
The flux surfaces fix alpha; one more number – the on-axis toroidal
beta, or equivalently the safety factor on axis – fixes the pressure and
the diamagnetism, and with them every global quantity.
| Name | Type | Unit | Description |
|---|---|---|---|
model | GuazzottoFreidbergEquilibrium | - | A solved equilibrium. |
q0 | float or None, optional | - | Safety factor on the magnetic axis, converted to |
beta0 | float or None, optional | - | Toroidal beta on axis, |
resolution | int, optional | - | Grid points across |
| Returns | |||
| — | Mapping of str to float | - |
|
Convention
beta_p is the paper’s volume-averaged form, Eq. (6.7), not the IMAS
DD definition. For a diverted equilibrium q_star uses kappa95,
the height of the psi = 0.05 surface over the full plasma width
2a – the reading that reproduces the published values – and for a
limited one the input elongation. With nu = 0 (force free) beta0
is zero and only q0 can fix the normalization. beta_t is
normalized to the exterior vacuum field B_hat0 (Part 2, Eq. 6.7
prints the ratio B_hat0**2/B0**2 inverted), and li weights the
current with 1 + nu G where Part 2, Eq. (6.10) prints 1 + nu
eps_hat x; the two readings coincide without flow.
Raises
ValueErrorNeither q0 nor beta0, a q0 that Eq. (6.12) cannot reach, or a surface that does not close on the grid.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Take beta0, or obtain it from q0 by Eq. (6.12) (Part 2, Eq. 6.1).
- Integrate
psi_J,psi_J**2and their(1 + nu G)/(1 + eps_hat x)and(1 + G)/(1 + eps_hat x)weighted forms over the plasma in(x, y), where the volume element is uniform. - Iterate the exterior field ratio
Delta_Buntil the surface current carries the bootstrap fraction (Part 2, Eqs. 3.9 and 6.3). - Evaluate Eqs. (6.4)-(6.10) (Part 2, 6.4-6.11), and q95 as the line
integral
F/(2 pi) closed_integral dl/(R**2 B_p)on thepsi = 0.05surface.
Defaults
q0 = 1 is the paper’s choice for Table 4, a conventional value.
resolution = 500 is a numerical convenience giving about three
significant figures, the precision the paper publishes.
Applicability
Machine-independent.
Limitations
Integrals are on a Cartesian grid masked by the surface, so they
carry an error of order the cell size. Part 2’s Table 2 is reproduced
to 0.5 % in beta_p and 1.1 % in q_star. The surface-current
jump uses Eq. (3.9), whose toroidal-field term carries R0**2/R**2;
Eq. (6.2) prints it without, a 0.2 % difference in q*.
Provenance
- Guazzotto and Freidberg (2021), Section 6, Eqs. (6.3)-(6.12).
- Guazzotto and Freidberg (2021), Part 2, doi:10.1017/S0022377821000118, Eqs. (3.6)-(3.10) and Section 6, Eqs. (6.1)-(6.12).
guazzotto_freidberg_to_equilibrium
Machine-independentConvention-sensitive
source
guazzotto_freidberg_to_equilibrium(model, *, major_radius, toroidal_field, q0=1.0, beta0=None, resolution=129, convention=11)
Export a Guazzotto-Freidberg equilibrium as a gridded record in physical units.
| Name | Type | Unit | Description |
|---|---|---|---|
model | GuazzottoFreidbergEquilibrium | - | A solved equilibrium. |
major_radius | float | m |
|
toroidal_field | float | T | Vacuum field |
q0 | float or None, optional | - | Safety factor on axis, fixing |
beta0 | float or None, optional | - | Toroidal beta on axis, taking precedence over q0. |
resolution | int, optional | - | Grid points across the major radius. |
convention | int, optional | - | COCOS index to export in, 1 to 18. |
| Returns | |||
| — | EquilibriumData | - | psi, axis and boundary flux, LCFS, pressure, F, p’, FF’ (psi = 0 on the boundary), the core plasma current and the vacuum field inside the surface, with |
Convention
Built in COCOS 11 for a positive current: the stored flux is
-2 pi Psi_0 psi, a minimum on the axis rising to zero at the
boundary, as solovev_example stores it. p' and FF' are per
weber of that stored flux.
Raises
ValueErrorInvalid physical inputs or COCOS index, an unreachable q0, a boundary that does not close on the grid, or toroidal flow, whose pressure is not a flux function.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Fix
beta0and from itp0(6.3),dB/B0(6.4) and the axis fluxPsi_0(6.5). - Evaluate
psion an R-Z grid throughx = (R**2/R0**2 - 1 - eps**2)/(2 eps). - Build
p = p0 (f_P + (1 - f_P) psi**2 + A1 (psi - psi**2))andF**2 = R0**2 B0**2 (1 + 2 (dB/B0) psi**2 + 2 A2 (psi - psi**2))(Part 2, Eqs. 4.1, 4.3, 4.5;A1 = A2 = 0without pedestals), their flux derivatives, andI_pfrom Ampere’s law on J_phi. - Store in COCOS 11 and convert when another convention is asked for.
Defaults
q0 = 1 as in the paper’s Table 4, a conventional value;
resolution = 129 is a numerical convenience.
Applicability
Machine-independent.
Limitations
q is not filled; compute it with calculate_q_profile_from_psi
or take q95 from guazzotto_freidberg_parameters. The surface
currents of Part 2 are not on the grid: ip is the core current I
and bt0 the interior B0; I_hat/I and B_hat0/B0 are in
metadata["parameters"].
Provenance
- Guazzotto and Freidberg (2021), Eqs. (2.2), (2.8), (6.3)-(6.8); Part 2, Eqs. (4.1)-(4.6) and (6.5)-(6.6).
integrate_romero_closure
Machine-independentConvention-sensitive
source
integrate_romero_closure(time, V_B, R_p, I_ni, *, I_p0, L_i0, V_CB0=0.0, k, tau, rtol=1e-08)
Integrate Romero’s first-order closure from a boundary-voltage history.
Evolves the plasma current $I_p$, the internal inductance $L_i$ and
$V = V_C - V_B$ under a prescribed boundary loop voltage, resistance and
non-inductive current, with the rates of
vaft.formula.transformer.romero_closure_rates_from_I_p_L_i_V_CB_V_B_V_R_k_tau.
| Name | Type | Unit | Description |
|---|---|---|---|
time | np.ndarray | s | Output times, strictly increasing, at least two; the inputs are given on the same axis. |
V_B | np.ndarray | V | Boundary loop voltage, Romero’s sign, one per time. |
R_p | np.ndarray | Ohm | Plasma resistance, one per time or one value. |
I_ni | np.ndarray | A | Non-inductively driven current, one per time or one value; |
I_p0 | float | A | Plasma current at the first time, non-zero. |
L_i0 | float | H | Internal inductance at the first time, positive. |
V_CB0 | float, optional | V | $V_C - V_B$ at the first time. |
k | float | - | Closure gain. |
tau | float | s | Closure time constant, positive. |
rtol | float, optional | - | Relative tolerance of the integrator. |
| Returns | |||
| — | dict of str to np.ndarray | - |
|
Convention
Romero’s signs: full-weber flux, $V = -\dot\psi$, and a positive $V_B$ driving a positive $I_p$. The first two state equations are exact; the third is the closure, so every departure of the trajectory from a measured one is attributable to $k$, $\tau$ or the inputs.
Raises
ValueErrorInputs of the wrong length, a time axis that does not strictly increase, a non-finite input, or a trajectory that reaches zero current or non-positive inductance [-].
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Interpolate
V_B,R_pandI_nilinearly in time. - Integrate the three rates with
scipy.integrate.solve_ivp(RK45) from the initial state, reporting attime. - Rebuild $V_C$ and $V_R$ from the state and the inputs.
Defaults
V_CB0 = 0 starts from a flat loop-voltage profile, a modelling choice
the caller should replace with a measured $V_C - V_B$ when there is one.
rtol = 1e-8 is a numerical convenience; the absolute tolerances are
scaled from the initial state.
Applicability
Machine-independent. $k$ and $\tau$ are the caller’s, identified for the machine and regime; none are supplied.
Limitations
Linear interpolation of the inputs, so a voltage history sampled more coarsely than $\tau$ is smoothed. The integration stops with an error if the current falls to 0.1 % of its initial value: $\dot L_i \propto 1/I_p$ is undefined at a reversal, and the step size collapses before one.
Provenance
- J. A. Romero and JET-EFDA contributors, Nucl. Fusion 50 (2010) 115002, eqs. (41)-(45).
lab_to_straight_field_line
Machine-independentConvention-sensitive
source
lab_to_straight_field_line(theta_lab, table)
Show source vaft/process/equilibrium.py, lines 5875–5929
def lab_to_straight_field_line(theta_lab, table: tuple[np.ndarray, np.ndarray]):
"""Relabel a geometric poloidal angle into the straight-field-line one.
Parameters
----------
theta_lab : float or array_like
Geometric angle about the magnetic axis, any branch [rad].
table : tuple of ndarray
One ``(lab, sfl)`` pair from :func:`straight_field_line_tables` [rad].
Returns
-------
float or ndarray
The straight-field-line angle, wrapped to ``[0, 2 pi)`` [rad].
Raises
------
ValueError
The table's two arrays disagree in length, or it is too short to
interpolate.
Convention
----------
The input is wrapped onto the table's own branch before interpolating.
The table spans one period starting wherever the surface's outboard
midplane fell, **not** at zero, so wrapping to ``[0, 2 pi)`` and
interpolating would read off the end for every angle below that start.
Applicability
-------------
Machine-independent.
Processing steps
----------------
1. Wrap the angle onto the table's branch.
2. Interpolate, and wrap the result into ``[0, 2 pi)``.
Provenance
----------
.. [legacy] ``library/flare_plotting.py::_interp_periodic_inverse``.
"""
lab_grid, sfl_grid = (np.asarray(part, dtype=float) for part in table)
if lab_grid.size != sfl_grid.size:
raise ValueError(
f"the table pairs {lab_grid.size} geometric angles with "
f"{sfl_grid.size} straight-field-line ones"
)
if lab_grid.size < 3:
raise ValueError("a relabelling table needs at least three entries")
angle = np.mod(np.asarray(theta_lab, dtype=float), 2.0 * np.pi)
start = float(lab_grid[0])
angle = angle + 2.0 * np.pi * np.ceil((start - angle) / (2.0 * np.pi))
angle = np.where(angle >= start + 2.0 * np.pi, angle - 2.0 * np.pi, angle)
result = np.mod(np.interp(angle, lab_grid, sfl_grid), 2.0 * np.pi)
return float(result) if np.ndim(theta_lab) == 0 else resultRelabel a geometric poloidal angle into the straight-field-line one.
| Name | Type | Unit | Description |
|---|---|---|---|
theta_lab | float or array_like | rad | Geometric angle about the magnetic axis, any branch. |
table | tuple of ndarray | rad | One |
| Returns | |||
| — | float or ndarray | rad | The straight-field-line angle, wrapped to |
Convention
The input is wrapped onto the table’s own branch before interpolating.
The table spans one period starting wherever the surface’s outboard
midplane fell, not at zero, so wrapping to [0, 2 pi) and
interpolating would read off the end for every angle below that start.
Raises
ValueErrorThe table’s two arrays disagree in length, or it is too short to interpolate.
Processing steps · Applicability · Provenance
Processing steps
- Wrap the angle onto the table’s branch.
- Interpolate, and wrap the result into
[0, 2 pi).
Applicability
Machine-independent.
Provenance
library/flare_plotting.py::_interp_periodic_inverse.
make_equilibrium_field_interpolator
Machine-independentConvention-sensitive
source
make_equilibrium_field_interpolator(R_grid_1d, Z_grid_1d=None, psi_grid=None, psi_1d=None, f_1d=None, cocos=None, *, psi_per_radian=None)
Build a callable giving the full magnetic field anywhere on one time slice.
Pass an vaft.data.equilibrium.EquilibriumData –
make_equilibrium_field_interpolator(equilibrium) – and the grid, the
profiles and the flux convention are read from the record, so the caller
need not restate the unit of psi. The record is only as right as its
convention, which nothing checks against the arrays. The array form
remains for flux maps that have no record, and then the convention must be
stated.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid_1d | array_like or EquilibriumData | - | Major-radius grid axis [m], or the whole equilibrium record, in which case every other argument must be left unset. |
Z_grid_1d | array_like, optional | m | Height grid axis; required with arrays. |
psi_grid | array_like, optional | Wb or Wb/rad | Poloidal flux on the grid, indexed |
psi_1d | array_like, optional | Wb or Wb/rad | Flux abscissa the poloidal current function is given on, in the same unit as psi_grid. |
f_1d | array_like, optional | T m | Poloidal current function |
cocos | int, optional | - | COCOS index of psi_grid. |
psi_per_radian | bool or None, optional | - | Storage family of the flux when the index is not known: |
| Returns | |||
| — | callable | T | A function of |
Convention
Same prefactor as poloidal_field_at_boundary, per Sauter Eq. 20:
B_R = k (1/R) dpsi/dZ and B_Z = -k (1/R) dpsi/dR, with
k = sigma_RphiZ * sigma_Bp / (2*pi)**e_Bp. The toroidal field is the
poloidal current function over the major radius. The flux map is indexed major
radius first, matching extract_flux_surface_contours.
k comes from cocos, or from psi_per_radian when the index is open,
exactly as for poloidal_field_at_boundary; a record supplies both
from its convention. Supplying neither assumes weber per radian and warns,
because a weber flux then gives a field 2*pi too large (#1313).
Raises
ValueErrorThe flux map is not shaped to the two grid axes, the record lacks a field, cocos and psi_per_radian contradict each other, or the record’s open COCOS candidates disagree on the field direction.TypeErrorA record is combined with array arguments, or the array form is missing one.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
With neither cocos nor psi_per_radian known, from the arguments or the
record, the flux is taken as weber per radian, the historical default, and
a UserWarning says so (#1313); a weber flux, such as every ODS
equilibrium carries, then gives a field 2*pi too large. With the
storage family but no index, the orientation is assumed (k < 0) and
the warning says that instead; a record’s open candidates settle it when
they agree, and leave it to the same announced assumption when they do
not (the packaged g-file, open between COCOS 1 and 2, warns like its
arrays do).
The poloidal current function is defined only from axis to boundary. Points
outside that range, in the scrape-off layer, clip to the nearest edge value,
the same clip-and-interpolate convention psi_to_rz uses. That is an
approximation for a field line that leaves the confined region: the toroidal
field there is this clipped function over the major radius rather than the
true vacuum field. The spline is built once over the whole map and reused, so
evaluation is cheap but the map must not change.
Provenance
- Sauter and Medvedev (2013), Eq. 20, for the prefactor.
- The clip-and-interpolate convention of
psi_to_rz, kept deliberately the same.
make_vacuum_field_interpolator
Machine-independentConvention-sensitive
source
make_vacuum_field_interpolator(R_grid_1d, Z_grid_1d, b_r, b_z, r0_b0)
Build a callable giving the vacuum magnetic field anywhere on one grid.
The twin of make_equilibrium_field_interpolator for a machine with no
plasma in it. That one reads the poloidal field off a flux map and the
toroidal field off profiles_1d.f, both of which a reconstruction
supplies; before breakdown there is no reconstruction, and the field the
coils and the vessel make is all there is.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid_1d | np.ndarray | m | Major radius of the grid columns, increasing. |
Z_grid_1d | np.ndarray | m | Height of the grid rows, increasing. |
b_r | np.ndarray | T | Radial field on the |
b_z | np.ndarray | T | Vertical field on the same grid. |
r0_b0 | float | T m | The vacuum toroidal field product $R_0 B_0$, as |
| Returns | |||
| — | callable | T | A function of |
Convention
The toroidal field is $B_\varphi = R_0 B_0 / R$ exactly, not interpolated: in a vacuum it is a pure $1/R$ and the grid would only add error to it. The poloidal components are interpolated, so they carry the grid’s resolution – which is the caller’s choice, not this function’s.
Raises
ValueErrorGrids and field arrays whose shapes disagree [-].
Assumptions · Applicability · Limitations · Provenance
Assumptions
The grid is the one vaft.omas.compute_vacuum_field_map builds, whose
axes are monotonic and whose fields already include the vessel’s eddy
currents. Nothing here checks that the field is curl-free.
Applicability
Machine-independent. The grid and the toroidal product are the caller’s.
Limitations
Bicubic over the supplied grid, so the field between nodes is as good as the grid is fine: poor within a coil’s near field, where the true field varies on the scale of the conductor, and good near a null, where it varies on the scale of the machine. Outside the grid the spline extrapolates rather than raising, so a caller that can leave the grid must terminate on its own bound.
Provenance
- The vacuum field decomposition: the poloidal part from the Green’s function response of the coils and the passive structure, the toroidal part from the TF product.
miller_surfaces
Machine-independentConvention-sensitive
source
miller_surfaces(r_minor, *, r0, z0=0.0, kappa=1.0, delta=0.0, zeta=0.0)
Show source vaft/process/_equilibrium_parametric.py, lines 2557–2632
def miller_surfaces(
r_minor: Any,
*,
r0: Any,
z0: Any = 0.0,
kappa: Any = 1.0,
delta: Any = 0.0,
zeta: Any = 0.0,
) -> tuple[MillerSurface, ...]:
"""A family of Miller surfaces for a shape scan.
A teaching helper: scan the minor radius at fixed shape, or vary any shape
parameter along the family, and feed the result to :func:`evaluate_miller`.
Parameters
----------
r_minor : float or sequence of float
Minor radius of each surface, positive [m].
r0 : float or sequence of float
Major radius of each surface's centre [m].
z0 : float or sequence of float, optional
Height of each surface's centre [m].
kappa : float or sequence of float, optional
Elongation, positive [-].
delta : float or sequence of float, optional
Triangularity, of magnitude below one [-].
zeta : float or sequence of float, optional
Squareness, of magnitude below one half [-].
Returns
-------
tuple of MillerSurface
One surface per entry after broadcasting the arguments against each
other, in order [-].
Raises
------
ValueError
Sequences of unequal length (other than length one), or a surface that
:func:`evaluate_miller` would reject.
Convention
----------
The parameters mean what :func:`evaluate_miller` means by them:
``R = r0 + r cos(theta + arcsin(delta) sin(theta))``, ``Z = z0 + kappa r
sin(theta + zeta sin(2 theta))``, so the elongation is height over width and
the triangularity is ``(r0 - R at Z max) / r`` exactly. Scalars broadcast
against sequences.
Applicability
-------------
Machine-independent.
Limitations
-----------
Only the shape is filled in; the radial label and the local-equilibrium
fields of :class:`MillerSurface` (q, shear, alpha, radial derivatives) are
left empty, so these surfaces are geometry, not an equilibrium.
Provenance
----------
.. [1] Miller, Chu, Greene, Lin-Liu and Waltz, Phys. Plasmas 5, 973 (1998),
through :func:`evaluate_miller`.
"""
arrays = [np.atleast_1d(np.asarray(value, dtype=float)).reshape(-1) for value in (r_minor, r0, z0, kappa, delta, zeta)]
lengths = {array.size for array in arrays if array.size != 1}
if len(lengths) > 1:
raise ValueError(f"sequence arguments must share one length, got lengths {sorted(lengths)}")
count = lengths.pop() if lengths else 1
arrays = [np.broadcast_to(array, (count,)) for array in arrays]
surfaces = []
for r, rc, zc, k, d, s in zip(*arrays):
surface = MillerSurface(r=float(r), r0=float(rc), z0=float(zc), kappa=float(k), delta=float(d), zeta=float(s))
evaluate_miller(surface, 0.0) # the same validity rules, raised here
surfaces.append(surface)
return tuple(surfaces)A family of Miller surfaces for a shape scan.
A teaching helper: scan the minor radius at fixed shape, or vary any shape
parameter along the family, and feed the result to evaluate_miller.
| Name | Type | Unit | Description |
|---|---|---|---|
r_minor | float or sequence of float | m | Minor radius of each surface, positive. |
r0 | float or sequence of float | m | Major radius of each surface’s centre. |
z0 | float or sequence of float, optional | m | Height of each surface’s centre. |
kappa | float or sequence of float, optional | - | Elongation, positive. |
delta | float or sequence of float, optional | - | Triangularity, of magnitude below one. |
zeta | float or sequence of float, optional | - | Squareness, of magnitude below one half. |
| Returns | |||
| — | tuple of MillerSurface | - | One surface per entry after broadcasting the arguments against each other, in order. |
Convention
The parameters mean what evaluate_miller means by them:
R = r0 + r cos(theta + arcsin(delta) sin(theta)), Z = z0 + kappa r
sin(theta + zeta sin(2 theta)), so the elongation is height over width and
the triangularity is (r0 - R at Z max) / r exactly. Scalars broadcast
against sequences.
Raises
ValueErrorSequences of unequal length (other than length one), or a surface thatevaluate_millerwould reject.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Only the shape is filled in; the radial label and the local-equilibrium
fields of MillerSurface (q, shear, alpha, radial derivatives) are
left empty, so these surfaces are geometry, not an equilibrium.
Provenance
- Miller, Chu, Greene, Lin-Liu and Waltz, Phys. Plasmas 5, 973 (1998), through
evaluate_miller.
parallel_current_from_toroidal
Machine-independentConvention-sensitive
source
parallel_current_from_toroidal(shell_current_tor, *, f, gm1, gm5, shell_volume, b0, shell_area=None)
Convert a shell toroidal driven current to the IMAS parallel current.
A code that drives current usually reports it as a toroidal current per
flux shell, or as a profile of current enclosed by each surface. IMAS asks
for something different: j_parallel is <J.B>/B0. The two are not
interchangeable, and in a spherical tokamak they differ by tens of percent
because <B^2> is much larger than B0^2.
| Name | Type | Unit | Description |
|---|---|---|---|
shell_current_tor | array_like | A | Toroidal current in each flux shell. Not the enclosed profile: pass |
f | array_like | T.m |
|
gm1 | array_like | m^-2 | Flux-surface-averaged |
gm5 | array_like | T^2 | Flux-surface-averaged |
shell_volume | array_like | m^3 | Volume of each shell. |
b0 | float | T | The vacuum toroidal field IMAS normalizes by. For a |
shell_area | array_like, optional | m^2 | Cross-sectional area of each shell. Supplying it also returns |
| Returns | |||
| — | ParallelCurrentResult | - |
|
Convention
Sign is inherited from shell_current_tor and f, not imposed. The result therefore carries the sign convention of whatever produced them, which is the caller’s to reconcile with the equilibrium it will sit beside.
Assumptions · Applicability · Limitations · Notes · Provenance
Assumptions
The driven current is field-aligned on each flux surface,
J = lambda(psi) B. This is the usual statement for a current driven by
parallel momentum input – beams, EC, LH – and it is what makes the
conversion possible at all from a toroidal quantity. It is an assumption,
not an identity: it omits any perpendicular (diamagnetic,
Pfirsch-Schlueter) part, so a caller converting a total plasma current
with this routine will see that part as a residual.
Applicability
Machine-independent. Requires an axisymmetric equilibrium and a driven current that is field-aligned. Nothing here is specific to a solver: the inputs are physics quantities, so any code reporting a toroidal driven current and an equilibrium can use it.
Limitations
Shells where F <R^-2> dV vanishes – a degenerate or zero-width shell,
typically at the magnetic axis – yield zero rather than a division by
zero. The field-aligned assumption is the dominant error for a total
current and a much smaller one for a driven current.
Notes
With J = lambda B and B_phi = F/R, the toroidal current through a
shell is dI = lambda * int B_phi dA. Writing the poloidal area element
as dA = dV / (2 pi R) turns that integral into flux-surface averages::
int B_phi dA = F <R^-2> dV / (2 pi)
lambda = 2 pi dI / (F <R^-2> dV)
j_parallel = lambda <B^2> / B0
In the large-aspect-ratio limit – F -> R0 B0, <B^2> -> B0^2,
<R^-2> -> R0^-2, dV -> 2 pi R0 dA – this reduces to dI / dA,
the intuitive toroidal current density. That limit is a check on the
result, never a substitute for it.
Provenance
- IMAS definitions read from the data dictionary:
j_parallelisaverage(J.B)/B0andcurrent_parallel_insideits cumulative surface integral.
pest_angle_from_jacobian_angle
Machine-independentConvention-sensitive
source
pest_angle_from_jacobian_angle(theta_norm, r, jacobian)
Relabel a DCON flux-surface angle into the PEST angle, from R alone.
| Name | Type | Unit | Description |
|---|---|---|---|
theta_norm | array_like | - | One poloidal circuit of DCON’s own angle, normalized so that a full circuit spans 1, ascending, the last sample repeating the first ( |
r | array_like | m | Major radius at those samples, on one flux surface. |
jacobian | str | - | The |
| Returns | |||
| — | np.ndarray | rad | The PEST angle at the same samples, ascending from |
Convention
The PEST angle, referenced to the input angle’s own origin. Both
angles run in the same direction and start at the same point on the
surface, so theta_pest[0] = 0 wherever theta_norm[0] = 0 is:
the reparametrisation is monotonic and fixes no new origin. That matters
because a resonant harmonic phase GPEC reports – arg(Phi_res) – is
referenced to GPEC’s angle origin, and a conversion that silently moved
the origin would rotate every pattern built on it.
Only the poloidal angle is converted. Hamada, PEST and Boozer are all
straight-field-line systems, but each pairs its poloidal angle with its
own toroidal angle; PEST is the one whose toroidal angle is the machine
angle phi. So m theta - n phi is the helical phase in the PEST
angle and in no other, which is the reason to convert rather than to
accept any straight-field-line angle.
Raises
ValueErrortheta_normandrdisagree in shape,theta_normdoes not span one circuit normalized to 1,theta_normis not strictly increasing,ris not positive and finite, or jacobian is a DCON jacobian whose weight needs a field this signature does not take (boozer,park,equal_arc), one whose powers only the namelist knows (other), or not a DCON jacobian at all.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Two angles on one surface satisfy
J_1 d theta_1 = J_2 d theta_2, because the volume element between two surfaces does not depend on how the surface is parametrised. DCON’s angle advances asw = R jacfac * B_p**power_bp * |B|**power_b / R**power_r, which is proportional to1 / J(seeDCON_JACOBIAN_ANGLE_WEIGHTS– the powers sit in the reciprocal, soJ ~ R**power_r / (B_p**power_bp |B|**power_b)). The ratio of the two weights is therefored theta_pest / d theta_in = J_in / J_pest = R**(power_r - 2) |B|**-power_b B_p**-power_bp. - For
hamada(0, 0, 0) that weight isR**-2and no field is needed; forpestit is 1 and the angle passes through unchanged. - Integrate the weight over the input angle by the trapezoid rule and
normalize the circuit to
2 pi.
Applicability
Machine-independent. One flux surface of a DCON or GPEC run whose
jac_type is recorded. It says nothing about the radial label.
Limitations
Exact to the quadrature only: the weight is integrated on the samples
given, so a coarse circuit converts coarsely. Measured against
straight_field_line_map on the DIII-D GPEC example’s 513-point
Hamada circuits, the agreement is 7.5e-06 rad at psi_N = 0.594 and
9.2e-06 rad at 0.819, degrading to 1.9e-03 at 0.928 and 0.25 at 0.988 –
where the comparison, not this conversion, is the weaker side, because a
ray-traced angle map loses accuracy against the separatrix. Taking the
Hamada angle as the PEST angle, the control for those four numbers, is
off by 0.49 to 0.65 rad on the same surfaces.
It is a relabelling, not a check: it cannot tell that the mesh really
is in the jacobian it was told, and a mesh labelled hamada that is
not will be converted confidently and wrongly.
Provenance
- GPEC
equil/equil.f,SELECT CASE(jac_type): the(power_r, power_b, power_bp)of each named jacobian. - GPEC
equil/inverse.f: DCON’s poloidal angle isspl%fsi(:,5) / spl%fsi(mtheta,5), the normalized cumulative integral ofr*jacfac * bp**power_bp * b**power_b / r**power_r. - Grimm, Dewar and Manickam, J. Comput. Phys. 49, 94 (1983), the PEST coordinate system.
- The agreement quoted under Limitations, measured on GPEC’s own DIII-D example (publishable under D-13, 2026-09-28, which requires this record): DIII-D 147131 @ 2300 ms, equilibrium
g147131.02300_DIIID_KEFIT(SHA-25635bf902f1d02759ad655f7b5315e3e2b4bc96d88ffc9753f261d48e86dbfe579) andgpec_profile_output_n1.nc(SHA-2568c17f9675c7046331130d0e6315b38c9e6642c832d3598129c087939072a16b5) from GPECv1.5.5-378-gf06e6abwithjac_type = "hamada". The comparison is this function applied to the file’stheta_dconandR, againststraight_field_line_mapbuilt from the equilibrium’s ownPSIRZ, on each of the four rational surfaces the run reports; no solver runs. Same record astest/test_gpec_island_geometry.py::R01_PROVENANCE.
plasma_cell_weights
Machine-independentConvention-sensitive
source
plasma_cell_weights(R, Z, psiN_RZ, outline_r=None, outline_z=None)
Show source vaft/process/equilibrium.py, lines 802–876
def plasma_cell_weights(
R: np.ndarray,
Z: np.ndarray,
psiN_RZ: np.ndarray,
outline_r: np.ndarray | None = None,
outline_z: np.ndarray | None = None,
) -> np.ndarray:
"""Fraction of each grid cell that is plasma, from the boundary outline when there is one.
Parameters
----------
R : array_like
Major-radius axis, or the full mesh [m].
Z : array_like
Height axis, or the full mesh [m].
psiN_RZ : array_like
Normalized poloidal flux on the grid, used only when there is no
outline [-].
outline_r : array_like, optional
Major radius of the last closed flux surface outline [m].
outline_z : array_like, optional
Height of the same outline [m].
Returns
-------
np.ndarray
Area fraction in ``[0, 1]`` on the grid's ``(R, Z)`` shape [-].
Raises
------
ValueError
The outline coordinates differ in length [-].
Convention
----------
With at least three finite outline points this is
:func:`fractional_cell_weights_from_boundary`: containment in the boundary,
with each crossed cell's area share. Otherwise it falls back to the
normalized-flux threshold ``0 <= psiN <= 1`` as zeros and ones, and logs a
warning, because that threshold also admits exterior cells wherever psi
turns over near the coils (see :func:`volume_average`).
Applicability
-------------
Machine-independent.
Limitations
-----------
The outline is trusted as given: an open or self-intersecting polygon gives
whatever point-in-polygon makes of it.
"""
R = np.asarray(R, float)
Z = np.asarray(Z, float)
if R.ndim == 1 and Z.ndim == 1:
Rm, Zm = np.meshgrid(R, Z, indexing="ij")
else:
Rm, Zm = R, Z
if outline_r is not None and outline_z is not None:
outline_r = np.asarray(outline_r, float).reshape(-1)
outline_z = np.asarray(outline_z, float).reshape(-1)
if outline_r.size != outline_z.size:
raise ValueError("outline_r and outline_z differ in length")
finite = np.isfinite(outline_r) & np.isfinite(outline_z)
if finite.sum() >= 3:
return fractional_cell_weights_from_boundary(
Rm, Zm, outline_r[finite], outline_z[finite]
)
warnings.warn(
"no usable boundary outline; taking the plasma as 0 <= psiN <= 1, which "
"also admits cells outside the boundary",
RuntimeWarning,
stacklevel=2,
)
psiN = np.asarray(psiN_RZ, float)
return ((psiN >= 0.0) & (psiN <= 1.0)).astype(float)Fraction of each grid cell that is plasma, from the boundary outline when there is one.
| Name | Type | Unit | Description |
|---|---|---|---|
R | array_like | m | Major-radius axis, or the full mesh. |
Z | array_like | m | Height axis, or the full mesh. |
psiN_RZ | array_like | - | Normalized poloidal flux on the grid, used only when there is no outline. |
outline_r | array_like, optional | m | Major radius of the last closed flux surface outline. |
outline_z | array_like, optional | m | Height of the same outline. |
| Returns | |||
| — | np.ndarray | - | Area fraction in |
Convention
With at least three finite outline points this is
fractional_cell_weights_from_boundary: containment in the boundary,
with each crossed cell’s area share. Otherwise it falls back to the
normalized-flux threshold 0 <= psiN <= 1 as zeros and ones, and logs a
warning, because that threshold also admits exterior cells wherever psi
turns over near the coils (see volume_average).
Raises
ValueErrorThe outline coordinates differ in length [-].
Applicability · Limitations
Applicability
Machine-independent.
Limitations
The outline is trusted as given: an open or self-intersecting polygon gives whatever point-in-polygon makes of it.
poloidal_field_at_boundary
Machine-independentConvention-sensitive
source
poloidal_field_at_boundary(R_grid_1d, Z_grid_1d, psi_grid, R_bdry, Z_bdry, cocos=None, psi_per_radian=None)
Poloidal field on a boundary contour, from the flux map by bicubic spline.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid_1d | array_like | m | Major-radius grid axis of the flux map. |
Z_grid_1d | array_like | m | Height grid axis of the flux map. |
psi_grid | array_like | Wb/rad | Poloidal flux on the grid, indexed |
R_bdry | array_like | m | Major radius of the contour points. |
Z_bdry | array_like | m | Height of the contour points. |
cocos | int, optional | - | COCOS index of psi_grid. |
psi_per_radian | bool, optional | - | Whether psi_grid is per radian, when the index alone does not settle it; given together with cocos it must agree with that index. |
| Returns | |||
B_p_bdry | np.ndarray | T | Poloidal field magnitude at each contour point. |
B_R_bdry | np.ndarray | T | Its major-radius component. |
B_Z_bdry | np.ndarray | T | Its height component. |
Convention
B_R = k * (1/R) * dpsi/dZ and B_Z = -k * (1/R) * dpsi/dR, where
k = sigma_RphiZ * sigma_Bp / (2*pi)**e_Bp carries both the orientation
sign and the 2*pi normalization together, per Sauter Eq. 20.
With cocos unset the historical EFIT weber-per-radian form k = -1 is
used. Passing psi_per_radian alone leaves the sign untouched and applies
only the 2*pi, which is what a weber-stored flux needs. The flux map is
indexed major radius first.
Raises
ValueErrorcocos and psi_per_radian are both given and disagree on the storage family.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
A bicubic spline over the whole map, so contour points must lie inside the
grid and the field near a steep edge is only as good as the map’s resolution.
The convention must be supplied; a weber flux passed without either argument
gives a field too large by 2*pi, and a poloidal beta too small by its
square.
Provenance
- Sauter and Medvedev (2013), Eq. 20, for the prefactor that carries the orientation sign and the
2*pitogether; evaluated byvaft.formula.equilibrium.poloidal_field_factor. - Plays the role of EFIT’s
seva2dsubroutine, from which the bicubic-spline-on-the-flux-map approach is taken.
prepare_boundary_for_shafranov
Machine-independentConvention-sensitive
source
prepare_boundary_for_shafranov(R_bdry, Z_bdry, n_points=256, enforce_ccw=True, eps=1e-12)
Condition a boundary contour into the form the Shafranov integrals require.
| Name | Type | Unit | Description |
|---|---|---|---|
R_bdry | array_like | m | Major radius of the contour points. |
Z_bdry | array_like | m | Height of the contour points. |
n_points | int, optional | - | How many points to resample the contour to. |
enforce_ccw | bool, optional | - | Whether to reverse the contour when it runs clockwise. |
eps | float, optional | m | Distance below which two consecutive points count as duplicates. |
| Returns | |||
| — | tuple of np.ndarray | m | The conditioned |
Convention
Counter-clockwise is not cosmetic. shafranov_integrals takes the
outward unit normal as (dZ/dl, -dR/dl), which points outward only for a
counter-clockwise contour; a clockwise one silently flips the sign of every
integral that uses it. That is why the orientation is enforced here rather
than assumed there.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Drop non-finite points.
- Close the contour if it is not already closed.
- Remove consecutive duplicates within eps.
- Reverse the contour if its signed area says it runs clockwise.
- Resample uniformly in arc length to n_points.
Defaults
n_points = 256 and eps = 1e-12 are numerical conveniences: enough
points to resolve a tokamak boundary’s curvature, and a duplicate threshold
far below any real machine dimension.
Applicability
Machine-independent.
Limitations
Assumes a single simple closed curve. A self-intersecting contour, or two disjoint pieces, gives a signed area that does not mean what the orientation test takes it to mean.
Provenance
- The outward-normal convention of
shafranov_integrals, which is what this exists to guarantee.
psi_to_radial
Machine-independentConvention-sensitive
source
psi_to_radial(psi_1d, psi_2d_slice, grid_r, boundary_r, r_axis)
Inboard and outboard major radius of each flux value, along the axis row.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_1d | array_like | Wb/rad | The poloidal flux profile to map. |
psi_2d_slice | array_like | Wb/rad | Poloidal flux along the grid row at the magnetic axis height. |
grid_r | array_like | m | Major-radius grid points that row is on. |
boundary_r | array_like | m | Major radius of the boundary, used for the inboard and outboard limits |
r_axis | float | m | Major radius of the magnetic axis, where the row is split. |
| Returns | |||
r_inboard | np.ndarray | m | Major radius on the high-field side for each flux value. |
r_outboard | np.ndarray | m | Major radius on the low-field side. |
Convention
Two answers per flux value, because a flux surface crosses the axis height twice. The split is at the magnetic axis, which is where the flux turns, so each branch is single-valued. Everything is in the flux unit the caller supplied; nothing is rescaled.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Split the axis row at the magnetic axis into an inboard and an outboard branch, on each of which the flux is monotonic.
- Build an interpolation of major radius against flux on each branch.
- Evaluate both at every value of the profile, clipped to the boundary.
Applicability
Machine-independent.
Limitations
Only the row nearest the axis height is used, so this describes the midplane and not the surface as a whole. A flux value outside the row’s own span is clipped to the boundary rather than extrapolated. Assumes the flux is monotonic on each branch, which fails for a non-nested equilibrium.
Provenance
- The inboard and outboard midplane radii as the IMAS data dictionary defines them for a one-dimensional profile.
psi_to_rho
Machine-independentConvention-sensitive
source
psi_to_rho(psi_val, q_profile, psi_axis, psi_boundary)
Show source vaft/process/equilibrium.py, lines 245–309
def psi_to_rho(psi_val, q_profile, psi_axis, psi_boundary):
"""Normalized toroidal-flux radius at one poloidal flux value, by integrating q.
Parameters
----------
psi_val : float
Poloidal flux at which to evaluate [Wb/rad].
q_profile : callable
Safety factor as a function of *normalized* poloidal flux, called on
``[0, 1]`` [-].
psi_axis : float
Poloidal flux on the magnetic axis [Wb/rad].
psi_boundary : float
Poloidal flux at the plasma boundary [Wb/rad].
Returns
-------
float
The normalized toroidal-flux radius, 0 on axis and 1 at the boundary [-].
Convention
----------
This is **rho_tor_norm**, the toroidal coordinate, not the poloidal one:
``sqrt(int q dpsi_n / int_0^1 q dpsi_n)``. It equals ``sqrt(psi_norm)``, which
is ``rho_pol_norm``, only for a flat safety factor. The parameter name says
"rho" for historical reasons and the two must not be interchanged; see
:func:`vaft.process.equilibrium.derive_radial_coordinates` for the record-based
form that returns all three coordinates at once and says which is unavailable.
Because both integrals are of the same profile over the same variable, the
result is independent of the flux storage convention: the sign and any factor
of ``2*pi`` cancel in the ratio.
Applicability
-------------
Machine-independent.
Limitations
-----------
Takes the safety factor as a callable on normalized flux, so a caller holding
it on a grid must interpolate first. Adaptive quadrature is evaluated afresh
for every call, which makes converting a whole profile point by point far more
expensive than :func:`derive_radial_coordinates`, and it assumes the profile
is integrable to the boundary.
Provenance
----------
.. [1] The toroidal flux definition ``Phi = int q dpsi`` and its normalized
radius, as the IMAS data dictionary defines ``rho_tor_norm``.
"""
from scipy.integrate import quad
# First normalize psi
psi_N = (psi_val - psi_axis) / (psi_boundary - psi_axis)
# Define the integration for numerator and denominator
def integrand(x):
return q_profile(x)
# Compute the integrals
numerator, _ = quad(integrand, 0, psi_N)
denominator, _ = quad(integrand, 0, 1.0)
# Return normalized radius
return np.sqrt(numerator / denominator)Normalized toroidal-flux radius at one poloidal flux value, by integrating q.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_val | float | Wb/rad | Poloidal flux at which to evaluate. |
q_profile | callable | - | Safety factor as a function of normalized poloidal flux, called on |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis. |
psi_boundary | float | Wb/rad | Poloidal flux at the plasma boundary. |
| Returns | |||
| — | float | - | The normalized toroidal-flux radius, 0 on axis and 1 at the boundary. |
Convention
This is rho_tor_norm, the toroidal coordinate, not the poloidal one:
sqrt(int q dpsi_n / int_0^1 q dpsi_n). It equals sqrt(psi_norm), which
is rho_pol_norm, only for a flat safety factor. The parameter name says
“rho” for historical reasons and the two must not be interchanged; see
vaft.process.equilibrium.derive_radial_coordinates for the record-based
form that returns all three coordinates at once and says which is unavailable.
Because both integrals are of the same profile over the same variable, the
result is independent of the flux storage convention: the sign and any factor
of 2*pi cancel in the ratio.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Takes the safety factor as a callable on normalized flux, so a caller holding
it on a grid must interpolate first. Adaptive quadrature is evaluated afresh
for every call, which makes converting a whole profile point by point far more
expensive than derive_radial_coordinates, and it assumes the profile
is integrable to the boundary.
Provenance
- The toroidal flux definition
Phi = int q dpsiand its normalized radius, as the IMAS data dictionary definesrho_tor_norm.
psi_to_rz
Machine-independentConvention-sensitive
source
psi_to_rz(psiN_1d, f_1d, psi_RZ, psi_axis, psi_lcfs, fill_outside='zero')
Also exported as psi_to_RZ.
Map a flux-surface profile onto the two-dimensional grid through psi.
| Name | Type | Unit | Description |
|---|---|---|---|
psiN_1d | array_like | - | Normalized poloidal flux the profile is given on. |
f_1d | array_like | any | The profile, in whatever unit it has. |
psi_RZ | array_like | Wb/rad | Poloidal flux on the grid, indexed |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis. |
psi_lcfs | float | Wb/rad | Poloidal flux at the last closed flux surface. |
fill_outside | str, optional | - |
|
| Returns | |||
f_RZ | np.ndarray | any | The profile on the grid, zero outside the boundary unless |
psiN_RZ | np.ndarray | - | Normalized poloidal flux on the grid. |
Convention
The flux map is indexed major radius first. Outside the boundary the value is
zero, not the edge value and not a NaN, so a sum over the grid is already a
plasma-only integral – as far as 0 <= psiN <= 1 is the plasma, which
near the coils it is not (see volume_average). Pass
fill_outside="edge" when the map is to be weighted by
plasma_cell_weights: an outline-weighted edge cell can sit just past
psiN = 1, and a zero there biases the average low (0.5-0.8 % for a
profile whose edge value is 30 % of its core, on the packaged samples). Because only the normalized flux is used, the absolute
unit of the three flux arguments cancels: weber and weber per radian give the
same answer as long as all three agree.
Raises
ValueErrorThe profile and its abscissa are not one-dimensional and of equal length, orfill_outsideis neither"zero"nor"edge".
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Normalize the flux map to
(psi - psi_axis)/(psi_lcfs - psi_axis). - Sort the profile by its abscissa, clamp the normalized map into the profile’s own span, and interpolate linearly.
- Zero every cell outside the closed region.
Applicability
Machine-independent.
Limitations
Linear interpolation, so a profile sampled coarsely near a steep gradient is smoothed. Clamping means a grid cell outside the profile’s own span takes the nearest profile value before the zeroing mask is applied, which matters only for a profile that does not reach the boundary.
Provenance
- The sort, clamp and interpolate sequence reproduces the legacy MATLAB workflow’s mapping, kept so that results match it cell for cell.
r_at_z_extremum
Machine-independentConvention-sensitive
source
r_at_z_extremum(r_seg, z_seg, *, upper)
Show source vaft/process/equilibrium.py, lines 2740–2784
def r_at_z_extremum(r_seg: np.ndarray, z_seg: np.ndarray, *, upper: bool) -> float:
"""Major radius where a contour reaches its highest or lowest point.
Parameters
----------
r_seg : array_like
Major radius of the contour points [m].
z_seg : array_like
Height of the contour points [m].
upper : bool
``True`` for the highest point, ``False`` for the lowest [-].
Returns
-------
float
The major radius at that extremum [m].
Convention
----------
Sub-vertex, not nearest-vertex. Taking the major radius at the sampled vertex
of extreme height is off by several percent in triangularity, because the true
extremum falls between vertices. A parabola is fitted to height over the three
points around the extreme vertex, and the major radius is interpolated there.
Applicability
-------------
Machine-independent.
Limitations
-----------
Falls back to the extreme vertex itself for a contour of fewer than three
points. The parabola is local, so a contour sampled too coarsely to resolve
its own curvature near the extremum is still limited by that sampling.
Provenance
----------
.. [1] Consumed by :func:`contour_shape_parameters` for both triangularities,
which is where the several-percent error would otherwise land.
.. [2] The arithmetic is
:func:`vaft.formula.equilibrium.r_at_z_extremum_from_RZ_contour`; this is
the process-layer name for it, so the two layers cannot drift apart.
"""
from vaft.formula.equilibrium import r_at_z_extremum_from_RZ_contour
return r_at_z_extremum_from_RZ_contour(r_seg, z_seg, upper=upper)Major radius where a contour reaches its highest or lowest point.
| Name | Type | Unit | Description |
|---|---|---|---|
r_seg | array_like | m | Major radius of the contour points. |
z_seg | array_like | m | Height of the contour points. |
upper | bool | - |
|
| Returns | |||
| — | float | m | The major radius at that extremum. |
Convention
Sub-vertex, not nearest-vertex. Taking the major radius at the sampled vertex of extreme height is off by several percent in triangularity, because the true extremum falls between vertices. A parabola is fitted to height over the three points around the extreme vertex, and the major radius is interpolated there.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Falls back to the extreme vertex itself for a contour of fewer than three points. The parabola is local, so a contour sampled too coarsely to resolve its own curvature near the extremum is still limited by that sampling.
Provenance
- Consumed by
contour_shape_parametersfor both triangularities, which is where the several-percent error would otherwise land. - The arithmetic is
vaft.formula.equilibrium.r_at_z_extremum_from_RZ_contour; this is the process-layer name for it, so the two layers cannot drift apart.
radial_to_psi
Machine-independentConvention-sensitive
source
radial_to_psi(r, psi_R, psi_Z, psi)
Show source vaft/process/equilibrium.py, lines 191–243
def radial_to_psi(r, psi_R, psi_Z, psi):
"""Poloidal flux at a major radius on the midplane row of the flux map.
Parameters
----------
r : float
Major radius at which to evaluate [m].
psi_R : array_like
Major-radius grid axis of the flux map, increasing [m].
psi_Z : array_like
Height grid axis of the flux map [m].
psi : array_like
Poloidal flux on the grid, indexed ``(R, Z)`` [Wb/rad].
Returns
-------
float
Poloidal flux at *r*, in the same unit the input map was in [Wb/rad].
Convention
----------
The flux map is indexed major radius first. The row taken is the one whose
height is nearest zero, not an interpolation between rows, so on a grid with
no node at the midplane the result is the flux on the nearest row rather than
on the midplane itself. The unit is whatever the caller's map carries; nothing
here rescales it, so a weber map returns weber.
Applicability
-------------
Machine-independent.
Limitations
-----------
Cubic interpolation along one row, so *r* must lie inside the grid's radial
span and the result is only as smooth as the map. A machine whose midplane is
not at zero height needs the row selected before calling.
Provenance
----------
.. [1] The midplane-row convention follows the EFIT-style flux maps this
package reads, where the grid is centred on the machine midplane.
"""
# Find the index of Z=0 in psi_Z array
z0_idx = np.argmin(np.abs(psi_Z))
# Extract the psi values at Z=0
psi_at_z0 = psi[:, z0_idx]
# Create 1D interpolation function
psi_interp = interp1d(psi_R, psi_at_z0, kind='cubic')
# Return interpolated value
return float(psi_interp(r))Poloidal flux at a major radius on the midplane row of the flux map.
| Name | Type | Unit | Description |
|---|---|---|---|
r | float | m | Major radius at which to evaluate. |
psi_R | array_like | m | Major-radius grid axis of the flux map, increasing. |
psi_Z | array_like | m | Height grid axis of the flux map. |
psi | array_like | Wb/rad | Poloidal flux on the grid, indexed |
| Returns | |||
| — | float | Wb/rad | Poloidal flux at r, in the same unit the input map was in. |
Convention
The flux map is indexed major radius first. The row taken is the one whose height is nearest zero, not an interpolation between rows, so on a grid with no node at the midplane the result is the flux on the nearest row rather than on the midplane itself. The unit is whatever the caller’s map carries; nothing here rescales it, so a weber map returns weber.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Cubic interpolation along one row, so r must lie inside the grid’s radial span and the result is only as smooth as the map. A machine whose midplane is not at zero height needs the row selected before calling.
Provenance
- The midplane-row convention follows the EFIT-style flux maps this package reads, where the grid is centred on the machine midplane.
rational_surfaces
Machine-independentConvention-sensitive
source
rational_surfaces(psi_norm, q, *, q_targets=(), resonances=(), rho_tor_norm=None)
Every flux surface where the safety factor takes a requested rational value.
The one place a q_target -> radius question is answered: the 2-D and
1-D rational-surface overlays of vaft.plot and any mode analysis
that needs q = m/n read their locations from here. Requests come as
plain values (q_targets) or as magnetic harmonics (resonances);
harmonics with the same m/n share one surface. Every crossing is
returned, so a reversed-shear profile reports both of its q = 2
surfaces.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_norm | array_like | - | Normalized poloidal flux, increasing over its finite samples. |
q | array_like | - | Safety factor on |
q_targets | iterable of float, optional | - | Rational values to locate, compared with |
resonances | iterable of (int, int), optional | - | Magnetic harmonics |
rho_tor_norm | array_like, optional | - | An authentic normalized toroidal-flux radius on the same grid; the roots carry no toroidal radius without it. |
| Returns | |||
| — | tuple of RationalSurface | - | One record per distinct requested value, ascending in |
Convention
Resonance is |q| = |m/n|: the sign the source’s COCOS gives q
(sgn(Ip) sgn(B0) in the 11 family) never decides whether a surface
exists. rho_pol_norm is sqrt(psi_norm); rho_tor_norm is never
derived from psi_norm here, since the two agree only for a flat q.
Raises
ValueErrorNothing is requested, a request is not a positive value or a whole non-zero(m, n), the arrays are not one-dimensional and of equal length, fewer than two samples are finite, orpsi_normdoes not increase.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Merge the requests into distinct values of
|q|, keeping which harmonics named each. - Split the profile into runs of finite samples; a gap is never bridged.
- In each run, take every node where
|q|equals the value and every bracket where|q| - q_targetchanges sign, interpolated linearly (the bracketing offind_rational_surfaces). - Map each root to
rho_pol_norm = sqrt(psi_norm)and, when given, torho_tor_normby the same interpolation weight.
Applicability
Machine-independent. Any equilibrium profile on an increasing flux grid.
Limitations
Linear interpolation, with the accuracy find_rational_surfaces
quantifies; interpolate q onto a finer grid first if that is not
enough. A sample within a relative 1e-9 of the value
(RATIONAL_SURFACE_RTOL) is taken to equal it, so rounding noise on a
|q| that hovers at the value yields one root, not one per sign flip.
Nothing is extrapolated: a value the profile reaches only beyond
its last finite sample is reported absent. A q flat at the value over
an interval yields one root, at its start. Roots are not associated across
time slices; each slice is resolved on its own.
Provenance
find_rational_surfacesfor the bracketing, which this shares rather than repeats; issue #506 for the request and record semantics.
resistive_layer_at
Machine-independent
source
resistive_layer_at(psi_n_surfaces, *, psi_norm, t_e, n_e, ion_mass_amu=1.0, z_eff=None, ln_lambda=None)
Resistivity and mass density at flux surfaces someone else located.
The companion to resistive_layer_parameters, for when the surfaces
are already known – read back from a solver that reported its own, which
is the only way to be sure the values line up with what that solver will
index.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_n_surfaces | array_like | - | Normalized poloidal flux of each surface, ordered as the consumer expects them. |
psi_norm | array_like | - | Normalized poloidal flux of |
t_e | array_like | eV | Electron temperature. |
n_e | array_like | m^-3 | Electron density. |
ion_mass_amu | float, optional | - | Mass of the bulk ion in atomic mass units; 1 (hydrogen) by default. |
z_eff | float | - | Effective ion charge; omitting it is deprecated as in |
ln_lambda | float | - | Coulomb logarithm; omitting it is deprecated as in |
| Returns | |||
| — | dict of str to ndarray | Ohm m |
|
Raises
ValueErrorA kinetic profile does not match its coordinate in length,psi_normis not strictly increasing, orion_mass_amuis not positive.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Interpolate
t_eandn_elinearly ontopsi_n_surfaces. - Evaluate the Spitzer resistivity at each, and take the mass density as
n_e * ion_mass_amu * m_p.
Applicability
Machine-independent.
Limitations
As resistive_layer_parameters: Spitzer carries no neoclassical
correction and diverges as T_e goes to zero, and the mass density
ignores impurity dilution.
Provenance
- #716.
resistive_layer_parameters
Machine-independent
source
resistive_layer_parameters(psi_norm, q, n, *, t_e, n_e, psi_norm_kinetic=None, ion_mass_amu=1.0, z_eff=None, ln_lambda=None, m_range=None)
Resistivity and mass density at each rational surface of a toroidal mode.
RDCON’s asymptotic matching (rmatch) asks for one resistivity and one
mass density per rational surface; STRIDE, an ideal outer-region code,
takes neither. Matching needs them per surface because the
resistive layer width and the reconnection rate are set locally. This
composes the two things needed to answer that from an equilibrium and its
kinetic profiles: where the surfaces are, and what the plasma is like
there.
| Name | Type | Unit | Description |
|---|---|---|---|
psi_norm | array_like | - | Normalized poloidal flux of the |
q | array_like | - | Safety factor on |
n | int | - | Toroidal mode number; its sign is ignored. |
t_e | array_like | eV | Electron temperature. |
n_e | array_like | m^-3 | Electron density. |
psi_norm_kinetic | array_like, optional | - | Normalized poloidal flux of |
ion_mass_amu | float, optional | - | Mass of the bulk ion in atomic mass units; 1 (hydrogen) by default, which is what VEST runs. |
z_eff | float | - | Effective ion charge, passed to the Spitzer resistivity. Omitting it is deprecated (#1188): it falls back to 2 with a |
ln_lambda | float | - | Coulomb logarithm, passed to the Spitzer resistivity. Omitting it is deprecated (#1188): it falls back to 17 with a |
m_range | tuple of int, optional | - | Forwarded to |
| Returns | |||
| — | dict of str to ndarray | Ohm m |
|
Raises
ValueErrorA kinetic profile does not match its coordinate in length, orion_mass_amuis not positive. Propagatesfind_rational_surfaces’s own validation.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Locate the rational surfaces with
find_rational_surfaces. - Interpolate
t_eandn_elinearly onto those surfaces. - Evaluate
vaft.formula.equilibrium.spitzer_resistivity_from_T_e_Z_eff_ln_Lambdaat each, and take the mass density asn_e * ion_mass_amu * m_p, which assumes quasineutrality with a single bulk ion species.
Applicability
Machine-independent. The ion_mass_amu default of 1 is the only VEST
choice, and it is a keyword.
Limitations
Spitzer resistivity carries no neoclassical trapped-particle correction,
so it underestimates the parallel resistivity of a spherical tokamak,
where the trapped fraction is large; the returned eta is a lower
bound in that sense.
It also diverges as T_e goes to zero. A reconstruction whose
T_e reaches zero at the separatrix therefore yields a non-finite
eta at any rational surface that sits on that point, and surfaces
within a few percent of it are dominated by however the profile was
extrapolated rather than by measurement. Both are reported rather than
clipped, because the right floor is a property of the discharge and not
of this function.
The mass density ignores impurities: with a non-unit z_eff the bulk
ion density is below n_e, so this overestimates it by roughly the
dilution factor.
Provenance
- #716 – the packaged
rmatch.insupplied a single scalar where the code wants one value per rational surface, sormatchstopped before writing its global solution.
rho_to_psi
Machine-independentConvention-sensitive
source
rho_to_psi(rho, q_profile, psi_axis, psi_boundary, tol=1e-06)
Show source vaft/process/equilibrium.py, lines 311–374
def rho_to_psi(rho, q_profile, psi_axis, psi_boundary, tol=1e-6):
"""Poloidal flux at one normalized toroidal-flux radius, by root finding.
The inverse of :func:`psi_to_rho`, obtained numerically because the forward
map is an integral with no closed-form inverse.
Parameters
----------
rho : float
Normalized toroidal-flux radius, between 0 and 1 [-].
q_profile : callable
Safety factor as a function of normalized poloidal flux [-].
psi_axis : float
Poloidal flux on the magnetic axis, one end of the bracket [Wb/rad].
psi_boundary : float
Poloidal flux at the plasma boundary, the other end [Wb/rad].
tol : float, optional
Relative tolerance passed to the root finder [-].
Returns
-------
float
The poloidal flux whose toroidal radius is *rho* [Wb/rad].
Convention
----------
*rho* is the toroidal coordinate, matching :func:`psi_to_rho`, not
``sqrt(psi_norm)``. The returned flux is in whatever unit *psi_axis* and
*psi_boundary* were given in, since the bracket sets the scale.
Defaults
--------
``tol = 1e-6`` is a numerical convenience: the forward map is monotonic and
smooth, so Brent's method reaches it in few iterations, and a tighter value
buys accuracy the quadrature underneath does not have.
Applicability
-------------
Machine-independent.
Limitations
-----------
Brackets on the axis and boundary flux, so it assumes the forward map is
monotonic between them, which needs a safety factor of one sign. Each
iteration runs the forward quadrature, making this the most expensive way to
convert a coordinate; invert an interpolated table instead when converting
many points.
Provenance
----------
.. [1] :func:`psi_to_rho`, whose definition this inverts.
"""
from scipy.optimize import root_scalar
def objective(psi):
return psi_to_rho(psi, q_profile, psi_axis, psi_boundary) - rho
# Find psi value that gives desired rho
result = root_scalar(objective,
bracket=[psi_axis, psi_boundary],
method='brentq',
rtol=tol)
return result.rootPoloidal flux at one normalized toroidal-flux radius, by root finding.
The inverse of psi_to_rho, obtained numerically because the forward
map is an integral with no closed-form inverse.
| Name | Type | Unit | Description |
|---|---|---|---|
rho | float | - | Normalized toroidal-flux radius, between 0 and 1. |
q_profile | callable | - | Safety factor as a function of normalized poloidal flux. |
psi_axis | float | Wb/rad | Poloidal flux on the magnetic axis, one end of the bracket. |
psi_boundary | float | Wb/rad | Poloidal flux at the plasma boundary, the other end. |
tol | float, optional | - | Relative tolerance passed to the root finder. |
| Returns | |||
| — | float | Wb/rad | The poloidal flux whose toroidal radius is rho. |
Convention
rho is the toroidal coordinate, matching psi_to_rho, not
sqrt(psi_norm). The returned flux is in whatever unit psi_axis and
psi_boundary were given in, since the bracket sets the scale.
Defaults · Applicability · Limitations · Provenance
Defaults
tol = 1e-6 is a numerical convenience: the forward map is monotonic and
smooth, so Brent’s method reaches it in few iterations, and a tighter value
buys accuracy the quadrature underneath does not have.
Applicability
Machine-independent.
Limitations
Brackets on the axis and boundary flux, so it assumes the forward map is monotonic between them, which needs a safety factor of one sign. Each iteration runs the forward quadrature, making this the most expensive way to convert a coordinate; invert an interpolated table instead when converting many points.
Provenance
psi_to_rho, whose definition this inverts.
romero_flux_balance
Machine-independentConvention-sensitive
source
romero_flux_balance(time, I_p, psi_boundary, psi_equilibrium, R_p, I_ni)
Romero’s exact voltage and volt-second balance over a sampled discharge.
Differentiates the boundary and equilibrium fluxes and the plasma current,
evaluates every term of Romero’s balance with vaft.formula.transformer,
and integrates the three volt-second budgets, so the residual of each
identity is visible sample by sample and cumulatively.
| Name | Type | Unit | Description |
|---|---|---|---|
time | np.ndarray | s | Sample times, strictly increasing, at least three. |
I_p | np.ndarray | A | Plasma current at each time, non-zero, same sense throughout. |
psi_boundary | np.ndarray | Wb | Boundary flux $\psi_B$, full flux in Romero’s sign. |
psi_equilibrium | np.ndarray | Wb | Current-weighted flux $\psi_C$ from |
R_p | np.ndarray | Ohm | Plasma resistance at each time, or one value for all. |
I_ni | np.ndarray | A | Non-inductively driven current at each time, or one value; |
| Returns | |||
| — | dict of str to np.ndarray | - | Per sample: |
Convention
Romero’s signs: full-weber flux and $V = -\dot\psi$. This is not
vaft.formula.equilibrium.loop_voltage_from_total_flux’s
convention (#354); a COCOS-11 equilibrium has to be brought to this one
before the call, and step 1 refuses fluxes that were not.
What each residual measures. With $L_i$ read from the same fluxes,
$V_B - V_C = \mathrm{d}(L_i I_p)/\mathrm{d}t$ holds to differencing error
on its own, so the three $V_C$ and balance_residual all disagree only
through $V_R$ – they test the resistance estimate and $\hat I$, not the
equilibria. Phi_B against Phi_B_direct tests the differencing and
the integration alone: the two agree to the trapezoid error whatever the
physics.
Raises
ValueErrorArrays of different lengths, fewer than three samples, a time axis that does not strictly increase, a non-finite input, a zero plasma current or one that changes sign, or fluxes whose sign says they are not in Romero’s convention [-].
Processing steps · Applicability · Limitations · Provenance
Processing steps
- $L_i = (\psi_C - \psi_B)/I_p$ at every sample
(
vaft.formula.transformer.internal_inductance_from_psi_C_psi_B_I_p), which refuses a flux in the opposite convention. - $\dot I_p$, $\dot L_i$, $V_B = -\dot\psi_B$ and $V_C = -\dot\psi_C$ with
vaft.process.numerical.time_derivative. - $V_R = R_p(I_p - \hat I)$ and $V_I = L_i\dot I_p + \tfrac12 I_p\dot L_i$.
- The two inferred $V_C$: eq. (39) from $\dot L_i$ and $V_R$, eq. (40) from $\dot I_p$, $V_B$ and $V_R$.
- Cumulative trapezoidal integrals of $V_B$, $V_R$ and $V_I$ from the first sample, and the direct flux change $-\Delta\psi_B$ beside them.
Applicability
Machine-independent. Closed-flux phase only: $\psi_C$ and $L_i$ need an equilibrium with a boundary, so a start-up window before flux closure must be cut off by the caller, and a zero $I_p$ is refused rather than divided by.
Limitations
All inputs must already be on one time axis, matched by time and not by index; nothing here resamples. Differentiation amplifies equilibrium noise, and $\dot L_i$ from reconstructed fluxes is the noisiest term – smooth upstream if the residuals are dominated by it. $R_p$ and $\hat I$ are the caller’s estimates, and the balance cannot tell which one is wrong.
Provenance
- J. A. Romero and JET-EFDA contributors, Nucl. Fusion 50 (2010) 115002, eqs. (12), (23)-(27), (34)-(40); the volt-second split follows issue #781, Sec. 5.
scale_boundary_conformal
Machine-independentConvention-sensitive
source
scale_boundary_conformal(R_bdry, Z_bdry, scale, center=None)
Show source vaft/process/equilibrium.py, lines 1573–1647
def scale_boundary_conformal(
R_bdry: np.ndarray,
Z_bdry: np.ndarray,
scale: float,
center: tuple[float, float] | None = None,
) -> tuple[np.ndarray, np.ndarray]:
"""Scale a closed boundary about a centre, giving a contour conformal to it.
``(R, Z) -> center + scale * ((R, Z) - center)``, so the result has the same
shape as the input and lies inside it for ``scale < 1``.
Parameters
----------
R_bdry : array_like
Major radius of the boundary points [m].
Z_bdry : array_like
Height of the boundary points [m].
scale : float
Similarity factor; below 1 shrinks, above 1 grows [-].
center : tuple of float, optional
Centre to scale about. The boundary's bounding-box centre when not
given [m].
Returns
-------
tuple of numpy.ndarray
The scaled ``(R, Z)``, closed, in the input's point order [m].
Raises
------
ValueError
``R_bdry`` and ``Z_bdry`` differ in length, or ``scale`` is not finite
and positive.
Convention
----------
The default centre is the bounding-box centre rather than the area
centroid, matching what the shape code reports as the geometric axis.
Applicability
-------------
Machine-independent. Any closed contour; nothing here is specific to a
boundary or to an equilibrium.
Limitations
-----------
A purely geometric offset. It does not consult the flux map, which is the
point when the contour is a reconstructed LCFS -- a psi_N band would carry
the interior psi's error into the result, and this does not. The scaled
contour lies strictly inside the original only for a boundary star-shaped
about ``center``; otherwise it can fold over itself, and the caller has to
decide what that means.
Provenance
----------
.. [1] The conformal-annulus construction of M. W. Bongard et al., Phys.
Plasmas 23 (2016), as used by
:func:`virial_alpha_conformal_annulus`.
"""
R_bdry = np.asarray(R_bdry, dtype=float).reshape(-1)
Z_bdry = np.asarray(Z_bdry, dtype=float).reshape(-1)
if R_bdry.size != Z_bdry.size:
raise ValueError("R_bdry and Z_bdry must have the same length.")
scale = float(scale)
if not np.isfinite(scale) or scale <= 0.0:
raise ValueError(f"scale must be finite and positive; got {scale}.")
R_bdry, Z_bdry = _ensure_closed_boundary(R_bdry, Z_bdry)
if center is None:
R_c = 0.5 * (float(np.min(R_bdry)) + float(np.max(R_bdry)))
Z_c = 0.5 * (float(np.min(Z_bdry)) + float(np.max(Z_bdry)))
else:
R_c, Z_c = float(center[0]), float(center[1])
return R_c + scale * (R_bdry - R_c), Z_c + scale * (Z_bdry - Z_c)Scale a closed boundary about a centre, giving a contour conformal to it.
(R, Z) -> center + scale * ((R, Z) - center), so the result has the same
shape as the input and lies inside it for scale < 1.
| Name | Type | Unit | Description |
|---|---|---|---|
R_bdry | array_like | m | Major radius of the boundary points. |
Z_bdry | array_like | m | Height of the boundary points. |
scale | float | - | Similarity factor; below 1 shrinks, above 1 grows. |
center | tuple of float, optional | m | Centre to scale about. The boundary’s bounding-box centre when not given. |
| Returns | |||
| — | tuple of numpy.ndarray | m | The scaled |
Convention
The default centre is the bounding-box centre rather than the area centroid, matching what the shape code reports as the geometric axis.
Raises
ValueErrorR_bdryandZ_bdrydiffer in length, orscaleis not finite and positive.
Applicability · Limitations · Provenance
Applicability
Machine-independent. Any closed contour; nothing here is specific to a boundary or to an equilibrium.
Limitations
A purely geometric offset. It does not consult the flux map, which is the
point when the contour is a reconstructed LCFS – a psi_N band would carry
the interior psi’s error into the result, and this does not. The scaled
contour lies strictly inside the original only for a boundary star-shaped
about center; otherwise it can fold over itself, and the caller has to
decide what that means.
Provenance
- The conformal-annulus construction of M. W. Bongard et al., Phys. Plasmas 23 (2016), as used by
virial_alpha_conformal_annulus.
shafranov_integrals
Machine-independentConvention-sensitive
source
shafranov_integrals(R_bdry, Z_bdry, B_p_bdry, R_grid, Z_grid, B_R_grid, B_Z_grid, R_0=None, Z_0=None, p_boundary=0.0, B_ref=None, cell_weights=None, volume=None)
The three Shafranov boundary integrals and the virial coefficient.
| Name | Type | Unit | Description |
|---|---|---|---|
R_bdry | array_like | m | Major radius of the boundary contour. |
Z_bdry | array_like | m | Height of the boundary contour. |
B_p_bdry | array_like | T | Poloidal field magnitude on that contour. |
R_grid | array_like | m | Major-radius grid, as an axis or a mesh. |
Z_grid | array_like | m | Height grid, as an axis or a mesh. |
B_R_grid | array_like | T | Major-radius field component on the grid. |
B_Z_grid | array_like | T | Height field component on the grid. |
R_0 | float, optional | m | Reference major radius. The boundary’s geometric centre when not given |
Z_0 | float, optional | m | Reference height, likewise. |
p_boundary | float, optional | Pa | Pressure at the boundary. |
B_ref | float, optional | T | Field the integrals are normalized by. The boundary-average poloidal field when not given. |
cell_weights | array_like, optional | - | Per-cell plasma area fractions, from |
volume | float, optional | m^3 | Plasma volume, computed from the boundary when not given. |
| Returns | |||
| — | tuple of float | - |
|
Convention
The boundary must run counter-clockwise, because the outward unit normal
is taken as (dZ/dl, -dR/dl), which points outward only then. The contour
is conditioned through prepare_boundary_for_shafranov on entry, so a
clockwise input is corrected rather than silently inverting every integral.
The COCOS is inherited, not declared: the poloidal field arrays are taken as given, so whatever convention produced them is the convention these integrals are in. Passing a field computed in the wrong one gives plausible numbers that are wrong by that factor.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Returns zeros rather than raising when the geometry is degenerate, so a caller must distinguish a real zero from a refusal. Without cell_weights the plasma mask is per-cell and the boundary is resolved only to the grid.
Provenance
- The Shafranov boundary integrals as EFIT evaluates them; the virial internal inductance built from them is
vaft.formula.equilibrium.virial_li_from_S_alpha_rt.
solovev_example
Machine-independentConvention-sensitive
source
solovev_example(topology='limited', *, major_radius=0.4, aspect_ratio=1.7, elongation=1.7, triangularity=0.35, plasma_current=100000.0, toroidal_field=0.1, a_parameter=0.9, resolution=129, convention=11)
Build a shaped analytic Solov’ev equilibrium in a chosen boundary topology.
A teaching example: the same exactly-known kind of equilibrium as
solovev_to_equilibrium, but with a basis rich enough to put an
X-point on the boundary, so that a limited, a lower single-null and a
double-null plasma can be compared on otherwise equal footing. The shape
is prescribed and the flux map is the exact solution that has it.
| Name | Type | Unit | Description |
|---|---|---|---|
topology | str, optional | - |
|
major_radius | float, optional | m | Geometric major radius R0 of the boundary. |
aspect_ratio | float, optional | - | R0 over the minor radius, greater than one. |
elongation | float, optional | - | Boundary elongation kappa, positive. |
triangularity | float, optional | - | Boundary triangularity delta, of magnitude below one. |
plasma_current | float, optional | A | Toroidal plasma current the flux is scaled to; its sign is the current direction. |
toroidal_field | float, optional | T | Vacuum toroidal field at R0, sets F at the boundary. |
a_parameter | float, optional | - | Cerfon-Freidberg A: the normalized source is |
resolution | int, optional | - | Grid points across the major radius; the height axis gets the same spacing. |
convention | int, optional | - | COCOS index to export in, 1 to 18. |
| Returns | |||
| — | EquilibriumData | - | A gridded record with psi, the magnetic axis, the last closed flux surface, a limiter (touching the boundary for |
Convention
Built in COCOS 11 – flux in full weber, psi increasing outward for a
positive current, B_Z = -(1/R) dpsi/dR per radian – and converted by
convert_cocos when another convention is asked for. In per-radian
units Psi = psi0 * psi(x, y) solves Delta* Psi = -mu0 R**2 p' - F F'
with p' = -psi0 (1 - A)/(mu0 R0**4) and F F' = -psi0 A / R0**2;
psi_boundary = 0, p = p' Psi and F**2 = (B0 R0)**2 + 2 F F' Psi.
The toroidal current density is Delta* Psi / (mu0 R), the sign
consistent with this convention’s poloidal field. The requested X-points are
at (R0 - 1.1 delta a, -+1.1 kappa a).
Raises
ValueErrorAn unknown topology, a non-physical shape, a non-positive radius or resolution, an invalid COCOS index, a constraint set that is singular for the shape, or a boundary that does not close around the axis on the grid.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Solve the Cerfon-Freidberg constraint set for the topology in normalized
coordinates
x = R/R0,y = Z/R0: seven up-down symmetric terms for limited and double-null, twelve for the asymmetric single-null. - Evaluate the normalized flux on a grid framing the plasma and the X-points, and locate the magnetic axis as the O-point of the analytic field.
- Extract the closed boundary surface, stepping a hair inside the separatrix when it passes through an X-point.
- Scale the flux so the toroidal current integrated over the boundary interior equals plasma_current, which fixes both sources.
Defaults
The shape defaults are an assumed value chosen to look like a small
spherical tokamak such as VEST: R0 = 0.4 m, aspect ratio 1.7 (a = 0.235 m),
elongation 1.7, triangularity 0.35, 100 kA and 0.1 T. a_parameter =
0.9 is likewise an assumed value that keeps the central toroidal beta
near five percent at those settings (about 0.2 kPa on axis);
resolution = 129 is a numerical convenience.
Applicability
Machine-independent. The defaults merely resemble VEST; nothing here reads VEST data.
Limitations
Linear sources only, so profiles are not realistic, and beta, current and shape are not independent: a_parameter trades pressure against poloidal current at fixed shape. The shape parameters are imposed on the boundary at its outboard, inboard and top points; elsewhere the boundary is whatever the basis gives. On an X-point side the separatrix follows Cerfon and Freidberg in putting the X-point at 1.1 times the nominal height and triangularity, so a double-null boundary measures about 1.1 kappa and 1.1 delta, and a lower single-null one is 1.1 times the request below the midplane only. The limiter is a construction to make the topology classifiable, not a machine wall.
Provenance
- A. J. Cerfon and J. P. Freidberg, “One size fits all” analytic solutions to the Grad-Shafranov equation, Phys. Plasmas 17, 032502 (2010): the particular solution, the twelve homogeneous polynomials and the limited, double-null and single-null constraint sets used here.
- Solov’ev (1968), for the linear-source closed form.
solovev_shape_constraints
Machine-independentConvention-sensitive
source
solovev_shape_constraints(*, major_radius, minor_radius, elongation, triangularity, topology='smooth', x_point=None, psi_boundary=0.0)
The Cerfon-Freidberg boundary conditions for a shaped, possibly diverted, plasma.
Turns an elongation, triangularity and minor radius into the point, slope
and curvature conditions that make an analytic Solov’ev boundary have that
shape, with the X-points the topology asks for. Solving them with
solve_solovev_constraints gives the equilibrium.
| Name | Type | Unit | Description |
|---|---|---|---|
major_radius | float | m | Geometric major radius R0 of the boundary, positive. |
minor_radius | float | m | Minor radius a, positive and below major_radius. |
elongation | float | - | Boundary elongation kappa, positive. |
triangularity | float | - | Boundary triangularity delta, of magnitude below one. |
topology | str, optional | - |
|
x_point | tuple of float, optional | m |
|
psi_boundary | float, optional | Wb/rad | Flux the boundary and the separatrix carry. |
| Returns | |||
| — | tuple of SolovevConstraint | - | Seven conditions for |
Convention
The outboard, inboard and top points of the boundary are
(R0 +- a, 0) and (R0 - delta a, kappa a). The curvature
conditions are Cerfon and Freidberg’s in metres: psi_ZZ + N1 psi_R = 0
outboard with N1 = -(1 + alpha)**2 / (a kappa**2), N2 = (1 -
alpha)**2 / (a kappa**2) inboard, and psi_RR + N3 psi_Z = 0 at the
top with N3 = -kappa / (a cos(alpha)**2), where alpha =
arcsin(delta). An upper single null is the lower one mirrored in Z.
Raises
ValueErrorAn unknown topology, a non-physical shape, an X-point given for a smooth boundary, or one on the wrong side of the midplane for the topology.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
The shape is imposed at three or four points only; elsewhere the boundary is what the basis gives, so a measured elongation or triangularity differs slightly from the request, and on an X-point side the boundary follows the X-point instead. The conditions are exact for the basis sizes above; with the classic five-term basis they are only met in a least-squares sense.
Provenance
- A. J. Cerfon and J. P. Freidberg, “One size fits all” analytic solutions to the Grad-Shafranov equation, Phys. Plasmas 17, 032502 (2010), Secs. III and IV: the point, slope and curvature conditions and the X-point placement at 1.1 times the nominal height and triangularity.
solovev_to_equilibrium
Machine-independentConvention-sensitive
source
solovev_to_equilibrium(model, r, z, *, magnetic_axis=None, limiter=None, convention=11)
Export an analytic Solov’ev model as a gridded equilibrium record.
Turns the closed-form solution into the same record shape a GEQDSK or an ODS produces, so that an exactly known equilibrium can be fed to the same machinery as a measured one. That is what makes it useful as a reference.
| Name | Type | Unit | Description |
|---|---|---|---|
model | SolovevEquilibrium | - | The analytic model to evaluate. |
r | array_like | m | Major-radius grid axis, strictly positive and increasing. |
z | array_like | m | Height grid axis, increasing. |
magnetic_axis | tuple of float, optional | m | The axis position, as |
limiter | Contour, optional | m | A limiting surface to carry onto the record. |
convention | int, optional | - | The COCOS index to declare and export in, 1 to 18. |
| Returns | |||
| — | EquilibriumData | - | A record on the requested grid carrying psi, its axis and boundary values, the flux surface, the profiles, the plasma current, the vacuum field, and the declared convention. |
Convention
evaluate_solovev works in weber per radian under the requested
convention’s orientation. The exported equilibrium is natively constructed
in vaft.data.cocos.VAFT_INTERNAL_COCOS (11, full weber) and converted
to convention via convert_cocos if different, reconciling both the
2*pi normalisation and the orientation sign across all COCOS conventions.
Pressure and the magnetic axis are invariant under convention transforms.
The 2pi factor multiplies psi, its axis and boundary values and psi_1d,
and divides the two d/dpsi sources pprime and ffprime. Nothing else
is rescaled, deliberately: ip is integrated from the analytic j_phi,
a current density that no flux-storage choice changes, and f, bt0
and pressure are physical fields too. Any orientation sign they carry
under another *convention is applied by convert_cocos. That says
nothing about whether the sign of evaluate_solovev’s j_phi is
consistent with its poloidal field; that is a separate question (#966).
Raises
ValueErrorconvention is outside 1 to 18, or the boundary flux does not form a closed contour enclosing the magnetic axis on this grid.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
The closed-boundary requirement is checked explicitly, stepping inward to 99 percent of the normalized flux before giving up, because an open boundary would silently corrupt the plasma current, the pressure integrals and every shape descriptor rather than failing. The grid must resolve the boundary well enough for that contour to close.
Provenance
- Solov’ev (1968) through
evaluate_solovev, which supplies every field this exports.
solovev_xpoint_constraints
Machine-independent
source
solovev_xpoint_constraints(r, z, *, psi=0.0)
Show source vaft/process/_equilibrium_parametric.py, lines 1841–1890
def solovev_xpoint_constraints(r: float, z: float, *, psi: float = 0.0) -> tuple[SolovevConstraint, ...]:
"""The three conditions that make a point an X-point on the boundary flux surface.
A prescribed magnetic null is not a new kind of equilibrium, only three
linear conditions on the flux: it takes the boundary value there and both
of its first derivatives vanish. This expands one X-point into those
conditions for :func:`solve_solovev_constraints`.
Parameters
----------
r : float
Major radius of the X-point, strictly positive [m].
z : float
Height of the X-point [m].
psi : float, optional
Flux the separatrix carries, normally the model's boundary flux
[Wb/rad].
Returns
-------
tuple of SolovevConstraint
The flux, radial-slope and vertical-slope conditions at the point [-].
Raises
------
ValueError
*r* is not positive.
Applicability
-------------
Machine-independent.
Limitations
-----------
A vanishing gradient is necessary, not sufficient: the solved point can
still be an extremum. Confirm the saddle from the sign of the Hessian
determinant returned by :func:`evaluate_solovev`.
Provenance
----------
.. [1] A. J. Cerfon and J. P. Freidberg, Phys. Plasmas 17, 032502 (2010),
Sec. IV, for imposing the X-point as a flux and two slope conditions.
"""
if r <= 0:
raise ValueError("the X-point radius must be positive")
return (
SolovevConstraint(float(r), float(z), "psi", float(psi)),
SolovevConstraint(float(r), float(z), "dpsi_dr", 0.0),
SolovevConstraint(float(r), float(z), "dpsi_dz", 0.0),
)The three conditions that make a point an X-point on the boundary flux surface.
A prescribed magnetic null is not a new kind of equilibrium, only three
linear conditions on the flux: it takes the boundary value there and both
of its first derivatives vanish. This expands one X-point into those
conditions for solve_solovev_constraints.
| Name | Type | Unit | Description |
|---|---|---|---|
r | float | m | Major radius of the X-point, strictly positive. |
z | float | m | Height of the X-point. |
psi | float, optional | Wb/rad | Flux the separatrix carries, normally the model’s boundary flux |
| Returns | |||
| — | tuple of SolovevConstraint | - | The flux, radial-slope and vertical-slope conditions at the point. |
Raises
ValueErrorr is not positive.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
A vanishing gradient is necessary, not sufficient: the solved point can
still be an extremum. Confirm the saddle from the sign of the Hessian
determinant returned by evaluate_solovev.
Provenance
- A. J. Cerfon and J. P. Freidberg, Phys. Plasmas 17, 032502 (2010), Sec. IV, for imposing the X-point as a flux and two slope conditions.
solve_guazzotto_freidberg
Machine-independentConvention-sensitive
source
solve_guazzotto_freidberg(topology='limited', *, inverse_aspect_ratio, nu, elongation=None, triangularity=None, x_point_elongation=None, x_point_triangularity=None, alpha_range=(0.5, 6.0), series_terms=250, current_pedestal=0.0, pressure_pedestal=0.0, bootstrap_fraction=0.0, mach_number=0.0, adiabatic_index=None, allow_current_reversal=False)
Solve the Guazzotto-Freidberg analytic equilibrium for a shape and a beta parameter.
An exact solution of the Grad-Shafranov equation whose pressure and
F**2 are quadratic in the flux, so that – unlike Solov’ev – the
pressure, its gradient and the toroidal current all vanish at the plasma
surface. The shape is imposed through the flux, slope and curvature at
the midplane and top points (or an X-point), and the eigenvalue alpha
that makes a non-trivial solution exist is found by scanning.
| Name | Type | Unit | Description |
|---|---|---|---|
topology | str, optional | - |
|
inverse_aspect_ratio | float | - |
|
nu | float | - | The source-balance parameter |
elongation | float, optional | - | Elongation of the smooth (upper) boundary; required for limited and single-null. |
triangularity | float, optional | - | Triangularity of the smooth (upper) boundary, magnitude below one. |
x_point_elongation | float, optional | - |
|
x_point_triangularity | float, optional | - |
|
alpha_range | tuple of float, optional | - | Interval scanned for the lowest eigenvalue. |
series_terms | int, optional | - | Terms kept in the radial power series. |
current_pedestal | float, optional | - |
|
pressure_pedestal | float, optional | - |
|
bootstrap_fraction | float, optional | - |
|
mach_number | float, optional | - |
|
adiabatic_index | float or None, optional | - |
|
allow_current_reversal | bool, optional | - | Accept |
| Returns | |||
| — | GuazzottoFreidbergEquilibrium | - | The eigenvalue, the coefficients normalized so psi is one on the magnetic axis, the separation constants, the axis in (x, y), the eigen-condition residual and conditioning, and the inputs. |
Convention
Normalized coordinates of Eq. (2.8): R = R0 sqrt(1 + eps**2 + 2 eps x),
Z = a y, psi = Psi/Psi_0 with psi = 1 on the axis and 0 on
the surface; eps_hat = 2 eps/(1 + eps**2). With flow the source is
alpha**2 (1 + nu G(x)), G = (1 + eps_hat x)(1 + 2 M0**2 eps x/(1 +
M0**2 eps**2))**(gamma/(gamma - 1)) - 1 (Part 2, Eq. 5.6). The lower single null
places its X-point at (-x_X, -kappa_X) with x_X = delta_X + (eps/2)
(1 - delta_X**2).
Raises
ValueErrorAn unknown topology or missing shape input; invalid geometry, includingkappa_X <= 2 sqrt(1 - delta_X**2)(Eq. 4.4);nubeyondnu_max(1/eps_hat, or-1/G(-1)with flow), where the inboard current reverses, unless allow_current_reversal; a pedestal or bootstrap fraction outside [0, 1); flow withoutadiabatic_index2 or inf; or no eigenvalue in alpha_range, or no pedestal root below it (no physical root).
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Fix the separation constants by Eq. (3.3) and build the radial functions by the power series of Appendix A.
- For a trial
alpha, setc_1 = 1and solve all but the last matching condition; the last one’s normalized miss is Eq. (3.11). - Scan alpha_range for the lowest minimum of that error, refine it, and accept it only if the error is at round-off level.
- Locate the magnetic axis as the maximum of psi and normalize psi to one there.
- With a current pedestal (Part 2, Eqs. 4.8-4.11), solve the
inhomogeneous system
psi_J = f_J/(1 - f_J)on the surface instead, and find thealphabelow the pedestal-free eigenvalue at whichpsi_J(axis) = 1/(1 - f_J).
Defaults
series_terms = 250 follows the paper, which keeps 150 routinely and
needs about 220 at eps = 0.75; alpha_range brackets every case
the paper publishes (1.88 to 2.38). Both are numerical conveniences.
Applicability
Machine-independent.
Limitations
The paper’s Eq. (5.4 h, i) evaluate the single-null X-point conditions at
(-delta_X, -kappa_X); this uses the X-point itself, (-x_X,
-kappa_X), which is the reading that reproduces the published
eigenvalues. Only the lowest eigenvalue is sought; the model surface is
matched at three or four points only. With a current pedestal the
midplane curvature conditions keep their pedestal-free values, so the
X-point angle is no longer the model’s right angle (Part 2, Section 4.3).
Part 2’s Table 2 cases C and D keep nu = 1.04 above the flow-reduced
nu_max = 1.025; they need allow_current_reversal.
Provenance
- L. Guazzotto and J. P. Freidberg, J. Plasma Phys. 87, 905870303 (2021), doi:10.1017/S002237782100009X: Eqs. (2.8)-(2.9), (3.2)-(3.11), (4.3)-(4.7), (5.3)-(5.8) and Appendix A.
- L. Guazzotto and J. P. Freidberg, J. Plasma Phys. 87, 905870305 (2021), doi:10.1017/S0022377821000118: Eqs. (3.1)-(3.5), (4.1)-(4.11), (5.6)-(5.14) and Appendix B.
solve_solovev_constraints
Machine-independentConvention-sensitive
source
solve_solovev_constraints(constraints, *, pprime, ffprime, rref, psi_boundary=0.0, pressure_boundary=0.0, f_boundary=1.0, f_sign=None, basis='classic', max_condition_number=1000000000000.0)
Solve for the Solov’ev coefficients that satisfy a set of geometric constraints.
The inverse of evaluate_solovev: given where the flux surface should
pass, where it should be flat and how it should curve, recover the basis
coefficients that put it there. This is how a Solov’ev model is shaped to a
wanted boundary, and, with the richer bases, how an X-point is prescribed.
| Name | Type | Unit | Description |
|---|---|---|---|
constraints | sequence of SolovevConstraint | - | At least as many as the basis has terms, each pinning the flux, one of its first or second derivatives, or a weighted combination of them at a point. Flux in Wb/rad, derivatives in Wb/(rad m^n). |
pprime | float | Pa rad/Wb | The pressure gradient against flux, constant by construction |
ffprime | float | T^2 m^2 rad/Wb | The poloidal-current term’s gradient against flux, likewise constant |
rref | float | m | Reference major radius that sets the logarithm’s origin in the basis and, for the Cerfon-Freidberg bases, the normalization length. |
psi_boundary | float, optional | Wb/rad | Flux value taken as the boundary, the zero point of the linear sources |
pressure_boundary | float, optional | Pa | Pressure at that boundary. |
f_boundary | float, optional | T m | Poloidal current function at that boundary, signed: its sign is the direction of B_phi, both in F(psi) and in |
f_sign | int or None, optional | - | Sign given to the poloidal current when the square root is taken. |
basis | str, optional | - |
|
max_condition_number | float, optional | - | Largest condition number of the column-normalized constraint matrix accepted before the system is refused as ill-conditioned. |
| Returns | |||
| — | SolovevEquilibrium | - | The coefficients in basis, the inputs echoed, the numerical rank of the solve, the residual norm, and |
Convention
Same per-radian flux and same orientation as evaluate_solovev, whose
basis this inverts. The basis size is not a tolerance but the dimension of
the homogeneous solution space kept; anything less leaves the surface
unpinned in some direction.
Raises
ValueErrorA zero f_boundary, an f_sign that contradicts the sign of f_boundary (#1307), an unknown basis, fewer constraints than basis terms, an unrecognized constraint kind, a constraint set whose rank is below the basis size, or one whose condition number exceeds max_condition_number.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- For each constraint, evaluate the analytic derivative the constraint names of every basis function, and of the particular integral of the two sources, at its point; a combination sums them with its weights.
- Move the particular integral to the right-hand side, so the unknowns are the homogeneous coefficients alone.
- Normalize each column, since the basis functions differ by powers of the radius, and solve by least squares.
- Refuse a rank-deficient or ill-conditioned system rather than returning an answer that is underdetermined in some direction.
Defaults
The boundary values default to a zero-flux, zero-pressure, unit-current
reference, which is the natural normalization for a shape-only model.
f_sign=None makes f_boundary the single source of the B_phi sign, so
reversing the toroidal field is a matter of negating f_boundary alone.
basis="classic" keeps the original five-term model. The condition
limit of 1e12 is a numerical convenience: about four digits short of
double precision, below which the least-squares answer still means
something.
Applicability
Machine-independent.
Limitations
Constraints that are geometrically distinct can still be linearly dependent
in the basis – an up-down symmetric basis gives an all-zero row for a
vertical derivative on the midplane, for instance – in which case the rank
check rejects them and the caller must move or add points. With more
constraints than terms the solve is a least squares and the result
satisfies none exactly; the residual norm reports how far off. A
derivative-free point is not by itself an X-point: check the sign of the
Hessian determinant from evaluate_solovev.
Provenance
- Solov’ev (1968); see
evaluate_solovevfor the basis being solved. - A. J. Cerfon and J. P. Freidberg, Phys. Plasmas 17, 032502 (2010), for the extended bases and the point, slope and curvature constraints.
straight_field_line_angle_on_grid
Machine-independentConvention-sensitive
source
straight_field_line_angle_on_grid(psi, r, z, psi_axis, psi_boundary, magnetic_axis, *, n_theta=256, n_surfaces=96, sqrt_psi_norm_min=0.02)
Show source vaft/process/equilibrium.py, lines 6490–6557
def straight_field_line_angle_on_grid(
psi,
r,
z,
psi_axis: float,
psi_boundary: float,
magnetic_axis,
*,
n_theta: int = 256,
n_surfaces: int = 96,
sqrt_psi_norm_min: float = 0.02,
) -> np.ndarray:
"""The PEST straight-field-line angle on every node of a flux map.
Parameters
----------
psi : array_like
Poloidal flux on the grid, indexed ``(R, Z)`` [Wb or Wb/rad].
r : array_like
Major-radius grid axis [m].
z : array_like
Height grid axis [m].
psi_axis : float
Flux on the magnetic axis, in the unit of *psi* [Wb or Wb/rad].
psi_boundary : float
Flux on the boundary surface, in the unit of *psi* [Wb or Wb/rad].
magnetic_axis : sequence of float
``(R, Z)`` of the magnetic axis [m].
n_theta : int, optional
Geometric-angle samples per surface, as in
:func:`straight_field_line_map` [-].
n_surfaces : int, optional
Tabulated surfaces, as in :func:`straight_field_line_map` [-].
sqrt_psi_norm_min : float, optional
Innermost tabulated surface, as ``sqrt(psi_norm)`` [-].
Returns
-------
numpy.ndarray
``theta*`` shaped ``(len(r), len(z))``, in ``[0, 2 pi)`` inside the
boundary and NaN outside it [rad].
Convention
----------
The angle of :func:`straight_field_line_map`: PEST, zero on the outboard
midplane, increasing in the sense of ``atan2(Z - Z_axis, R - R_axis)``.
The array is indexed major radius first, like the flux map.
Applicability
-------------
Machine-independent.
Limitations
-----------
Node values only; a caller that needs the angle between nodes should keep
the map from :func:`straight_field_line_map` and evaluate it there, since
interpolating a wrapped angle across its ``2 pi`` seam is wrong.
Provenance
----------
.. [1] Grimm, Dewar and Manickam, J. Comput. Phys. 49, 94 (1983), through
:func:`straight_field_line_map`.
"""
sfl = straight_field_line_map(psi, r, z, psi_axis, psi_boundary, magnetic_axis,
n_theta=n_theta, n_surfaces=n_surfaces,
sqrt_psi_norm_min=sqrt_psi_norm_min)
rr, zz = np.meshgrid(np.asarray(r, float).reshape(-1), np.asarray(z, float).reshape(-1), indexing="ij")
return sfl.theta_star(rr, zz)The PEST straight-field-line angle on every node of a flux map.
| Name | Type | Unit | Description |
|---|---|---|---|
psi | array_like | Wb or Wb/rad | Poloidal flux on the grid, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
psi_axis | float | Wb or Wb/rad | Flux on the magnetic axis, in the unit of psi. |
psi_boundary | float | Wb or Wb/rad | Flux on the boundary surface, in the unit of psi. |
magnetic_axis | sequence of float | m |
|
n_theta | int, optional | - | Geometric-angle samples per surface, as in |
n_surfaces | int, optional | - | Tabulated surfaces, as in |
sqrt_psi_norm_min | float, optional | - | Innermost tabulated surface, as |
| Returns | |||
| — | numpy.ndarray | rad |
|
Convention
The angle of straight_field_line_map: PEST, zero on the outboard
midplane, increasing in the sense of atan2(Z - Z_axis, R - R_axis).
The array is indexed major radius first, like the flux map.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Node values only; a caller that needs the angle between nodes should keep
the map from straight_field_line_map and evaluate it there, since
interpolating a wrapped angle across its 2 pi seam is wrong.
Provenance
- Grimm, Dewar and Manickam, J. Comput. Phys. 49, 94 (1983), through
straight_field_line_map.
straight_field_line_map
Machine-independentConvention-sensitive
source
straight_field_line_map(psi, r, z, psi_axis, psi_boundary, magnetic_axis, *, n_theta=256, n_surfaces=96, sqrt_psi_norm_min=0.02)
The PEST straight-field-line poloidal angle of a flux map, built once.
| Name | Type | Unit | Description |
|---|---|---|---|
psi | array_like | Wb or Wb/rad | Poloidal flux on the grid, indexed |
r | array_like | m | Major-radius grid axis. |
z | array_like | m | Height grid axis. |
psi_axis | float | Wb or Wb/rad | Flux on the magnetic axis, in the unit of psi. |
psi_boundary | float | Wb or Wb/rad | Flux on the boundary surface, in the unit of psi. |
magnetic_axis | sequence of float | m |
|
n_theta | int, optional | - | Geometric-angle samples per surface, uniform on one period. |
n_surfaces | int, optional | - | Tabulated surfaces, uniform in |
sqrt_psi_norm_min | float, optional | - | Innermost tabulated surface, as |
| Returns | |||
| — | StraightFieldLineMap | - | An object evaluating |
Convention
The PEST angle, in which field lines are straight:
theta* = 2 pi int dl/(R |grad psi|) / oint dl/(R |grad psi|) along a
flux surface, so d phi / d theta* = q on every surface. The poloidal
current function and the flux normalisation are constant on a surface and
cancel, which is why neither F nor the COCOS storage family is needed.
Origin and direction are geometric, not COCOS. theta* = 0 on the
outboard midplane ray (Z = Z_axis, R > R_axis) and it increases in
the same sense as atan2(Z - Z_axis, R - R_axis): outboard, top,
inboard, bottom. Whether that is the direction of the poloidal field
depends on the equilibrium’s current direction, which a caller forming a
helical phase must take from the field, not from this angle.
Raises
ValueErrorThe flux map is not shaped(len(r), len(z)), the axis and boundary flux coincide, the boundary surface leaves the grid, or a surface is not star-shaped about the axis.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Fit a bicubic spline to the flux.
- Move the axis to the spline’s O-point. Along each geometric-angle ray from it, find where the normalized flux reaches one, rejecting a ray on which it is not monotonic.
- On every tabulated surface, solve the ray radius by Newton iteration on
the spline and form
rho / (R d psi_norm / d rho), which isdl / (R |grad psi|)per unit geometric angle. - Integrate the rate spectrally into
theta* - theta, zero at the outboard midplane. - Spline that periodic offset over
(sqrt(psi_norm), theta).
Applicability
Machine-independent.
Limitations
The supplied axis is moved to the flux spline’s own O-point when that is
within two cells of it, so the angle’s origin is the axis of the map as
gridded, not the solver’s; StraightFieldLineMap.magnetic_axis records
where it went. Needs surfaces that every ray from the axis crosses once, which holds for
the nested surfaces of a tokamak inside its boundary but not beyond an
X-point, so the boundary surface itself must be closed on the grid. Inside
the innermost tabulated surface the angle offset is frozen at that
surface’s value; the angle is undefined on the axis itself. Accuracy is
set by the flux spline, not by the table, because every surface is solved
on the spline rather than on grid contours.
Provenance
- Grimm, Dewar and Manickam, J. Comput. Phys. 49, 94 (1983), the PEST coordinate system this angle belongs to.
- Sauter and Medvedev, Comput. Phys. Commun. 184, 293 (2013), for the straight-field-line relation
d phi / d theta* = q.
straight_field_line_tables
Machine-independentConvention-sensitive
source
straight_field_line_tables(theta_turns, r, z, magnetic_axis, *, jacobian, minimum_separation=1e-10)
Per-surface tables taking the geometric poloidal angle to the code’s own.
| Name | Type | Unit | Description |
|---|---|---|---|
theta_turns | array_like | - | The code’s poloidal angle in turns, one value per row of |
r, z | array_like | m | Flux-surface geometry, |
magnetic_axis | sequence of float | m |
|
jacobian | str | n/a | Which straight-field-line angle these are – |
minimum_separation | float, optional | rad | Geometric-angle spacing below which two samples are treated as one |
| Returns | |||
| — | tuple of (ndarray, ndarray) | rad | One |
Convention
The angle arrives in turns, and nothing in the file says so. GPEC
writes theta_dcon from 0 to 1 with no units attribute, so a caller
that took it for radians would be wrong by 2 pi and the error would
look like a mis-shaped plasma rather than a unit mistake. It is converted
here, once, and the parameter is named for what it holds.
The geometric angle is atan2(z - z_axis, r - r_axis), which is the lab
angle FLARE’s Poincare output is in; the straight-field-line angle is what
GPEC and DCON label their harmonics by. The two agree only on a circular
concentric equilibrium.
Raises
ValueErrorrandzdisagree in shape,theta_turnsdoes not match their first axis, an input is not finite,jacobianis empty, or a surface collapses to fewer than three distinct angles.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- Drop the closing duplicate, which GPEC writes so its grid is periodic.
- Per surface, take the geometric angle about the axis.
- Roll so the table starts near the outboard midplane, then unwrap both angles together.
- Reverse when the geometric angle runs backwards, so the table increases whichever way the surface was traced.
- Drop samples closer than
minimum_separation, which a near-stagnation point in the geometric angle can produce. - Close the period by repeating the first entry shifted by
2 pi, in the direction the straight-field-line angle runs along the table.
Applicability
Machine-independent.
Limitations
The relabelling is one flux surface at a time and says nothing about the radial coordinate: a caller with points between surfaces has to decide how to interpolate across them, and doing that in the lab angle is not the same as doing it in the straight-field-line one.
Provenance
library/flare_plotting.py::_build_lab_to_sfl_inverse_tablesand_interp_periodic_inverse.
trace_field_line
Machine-independentConvention-sensitive
source
trace_field_line(R0, Z0, phi0, b_field, *, dphi=np.float64(0.017453292519943295), max_length_m=50.0, direction='forward', wall_r=None, wall_z=None, r_bounds=None, z_bounds=None)
Trace one magnetic field line through the equilibrium field.
| Name | Type | Unit | Description |
|---|---|---|---|
R0 | float | m | Starting major radius. |
Z0 | float | m | Starting height. |
phi0 | float | rad | Starting toroidal angle. |
b_field | callable | - | A function of |
dphi | float, optional | rad | Fixed step in toroidal angle. |
max_length_m | float, optional | m | Path length at which to stop. |
direction | str, optional | - |
|
wall_r | array_like, optional | m | Major radius of the limiting polygon. |
wall_z | array_like, optional | m | Height of the limiting polygon. |
r_bounds | tuple of float, optional | m | Major-radius range outside which to stop. |
z_bounds | tuple of float, optional | m | Height range outside which to stop. |
| Returns | |||
| — | dict of str to Any | - |
|
Convention
The toroidal angle is the integration variable, not path length or time,
which is why a vanishing toroidal field terminates the trace rather than
slowing it. Points are always returned in order of increasing angle whatever
the direction traced. The arc length is cumulative along the returned array
from zero at its first point, so for "both" it measures from the backward
end and not from the start point.
Processing steps · Defaults · Applicability · Limitations · Provenance
Processing steps
- Integrate
dR/dphi = R*B_R/B_phianddZ/dphi = R*B_Z/B_phiby fixed-step fourth-order Runge-Kutta in the toroidal angle. - After each step, test the termination conditions in order: leaving the limiting polygon, exceeding the path length, leaving the coordinate bounds, or a numerically zero toroidal field.
- For
"both", trace each branch and concatenate them anchored at the start.
Defaults
The one-degree step and the fifty-metre limit are numerical conveniences: fine enough to resolve a tokamak field line’s pitch, and long enough to reach a wall from anywhere inside a small machine.
Applicability
Machine-independent. The wall polygon is where a machine’s own geometry enters, and it is supplied by the caller.
Limitations
Fixed step, so accuracy is set by the step alone and there is no error control; a line near a separatrix, where the pitch changes fastest, is the least accurate. Terminating on the coordinate bounds is a safety net against extrapolating far outside the equilibrium grid, not a physical condition.
Provenance
- The field-line equations in toroidal-angle form; the field itself comes from
make_equilibrium_field_interpolator.
validate_equilibrium
Deprecated
source
validate_equilibrium(equilibrium, *, required_for='general')
Show source vaft/process/_equilibrium_parametric.py, lines 457–468
def validate_equilibrium(
equilibrium: EquilibriumData, *, required_for: str = "general"
) -> ValidationReport:
"""Deprecated compatibility wrapper for :func:`check_equilibrium_requirements`."""
warnings.warn(
"vaft.process.validate_equilibrium() is deprecated; use "
"check_equilibrium_requirements(). The name now belongs to the scientific "
"equilibrium assessment in vaft.validation (issues #253, #337).",
DeprecationWarning,
stacklevel=2,
)
return check_equilibrium_requirements(equilibrium, required_for=required_for)Deprecated compatibility wrapper for check_equilibrium_requirements.
virial_alpha_conformal_annulus
Machine-independentConvention-sensitive
source
virial_alpha_conformal_annulus(R_grid, Z_grid, B_R_grid, B_Z_grid, R_bdry, Z_bdry, thickness=0.1, samples_per_axis=5)
Virial closure coefficient from a thin annulus conformal to the LCFS.
alpha_2 = 2 * sum(R Bz^2 w dA) / sum(R Bp^2 w dA) over an annulus bounded
by the boundary and a copy of it scaled by 1 - thickness. Obtains the
closure coefficient without the volume integral over the plasma interior,
which a reconstruction that fixes the boundary but not the internal field
cannot supply.
| Name | Type | Unit | Description |
|---|---|---|---|
R_grid | array_like | m | Major-radius grid, as an axis or a mesh. |
Z_grid | array_like | m | Height grid, as an axis or a mesh. |
B_R_grid | array_like | T | Major-radius field component on that grid. |
B_Z_grid | array_like | T | Vertical field component on that grid. |
R_bdry | array_like | m | Major radius of the boundary points. |
Z_bdry | array_like | m | Height of the boundary points. |
thickness | float, optional | - | Annulus width as a fraction of the distance from the centre to the boundary, not an absolute length; default 0.1. |
samples_per_axis | int, optional | - | Sub-samples per cell axis in the area-fraction weighting; default 5. |
| Returns | |||
| — | dict | - |
|
Convention
alpha = 2 <R Bz^2> / <R Bp^2>, 1 for a symmetric circular cross-section
and 2 in the infinitely elongated limit. Only squares of the field enter, so
the sign convention the components arrive in does not matter.
Processing steps · Defaults · Assumptions · Applicability · Limitations · Provenance
Processing steps
Orient the boundary counter-clockwise, build the inner contour with
scale_boundary_conformal, take the annulus weight map as the
difference of two fractional_cell_weights_from_boundary maps so that
cells cut by either contour carry their area fraction, reject an annulus too
small or too narrow for the grid, then integrate over the cells that survive
a shared finite-field mask.
Defaults
thickness=0.1 is a validated workflow default: near the accuracy optimum
on analytic Solov’ev equilibria and still thousands of cells on a
reconstruction-sized grid. samples_per_axis=5 is a numerical convenience
matching fractional_cell_weights_from_boundary, and it sets how thin
an annulus can be resolved.
Assumptions
The annulus is built by geometric conformal scaling, not as a psi_N band: a psi_N band would reintroduce the dependence on reconstructed interior flux that this estimate exists to avoid.
Applicability
Machine-independent. Any boundary with a poloidal-field map covering it.
Limitations
Abstains – NaN with a reason – rather than returning a number when the
boundary leaves the grid, when the annulus holds fewer than
MIN_ANNULUS_CELLS, or when it is narrower than
MIN_ANNULUS_WIDTH_SUBSAMPLES sub-samples at its narrowest point, in
which case the integral would report the grid phase rather than the field.
Cells whose field is only partly finite are dropped from both integrals
together, since dropping one from the denominator alone would bias alpha up.
As thickness -> 0 this tends to
virial_alpha_thin_annulus, whose accuracy against the true alpha is
slightly worse – a finite annulus samples some interior, and that helps.
Provenance
- M. W. Bongard et al., Phys. Plasmas 23 (2016), the low-aspect-ratio virial closure and its annulus estimate of alpha.
- Measured against analytic Solov’ev equilibria in
test/test_virial_alpha_approximations.py.
virial_alpha_thin_annulus
Machine-independentConvention-sensitive
source
virial_alpha_thin_annulus(R_bdry, Z_bdry, B_R_bdry, B_Z_bdry, center=None, mode='conformal', n_points=512)
Virial closure coefficient in the thin-annulus limit, from the boundary field.
The exact thickness -> 0 limit of virial_alpha_conformal_annulus,
evaluating 2 * closed_integral(R Bz^2 w dl) / closed_integral(R Bp^2 w dl).
Needs only the field on the LCFS, so unlike the volume integral it asks
nothing of the plasma interior.
| Name | Type | Unit | Description |
|---|---|---|---|
R_bdry | array_like | m | Major radius of the boundary points. |
Z_bdry | array_like | m | Height of the boundary points. |
B_R_bdry | array_like | T | Major-radius field component at those points. |
B_Z_bdry | array_like | T | Vertical field component at those points. |
center | tuple of float, optional | m | Centre the conformal annulus shrinks towards. The boundary’s bounding-box centre when not given. |
mode | str, optional | - |
|
n_points | int, optional | - | Segments in the arc-length resampling; default 512. |
| Returns | |||
| — | float | - | The closure coefficient, or NaN when the contour is degenerate or folds over the centre. |
Convention
alpha = 2 <R Bz^2> / <R Bp^2>, which is 1 for a symmetric circular
cross-section and 2 in the infinitely elongated limit. Only squares of the
field enter, so the result does not depend on the sign convention the
components arrive in.
Raises
ValueErrormodeis neither"conformal"nor"uniform", or the boundary and field arrays differ in length.
Processing steps · Assumptions · Applicability · Limitations · Provenance
Processing steps
Drop non-finite points, close the contour, remove zero-length segments,
orient counter-clockwise so the normal (dZ, -dR)/dl points outward,
resample to n_points by arc length, then take the segment-midpoint
quadrature through
vaft.formula.equilibrium.virial_alpha_from_R_Bz_Bp_dl.
Assumptions
mode="conformal" assumes the boundary is star-shaped about center,
so that shrinking it about that point sweeps a simple annulus. A conformal
annulus has local width t * (x - c).n, so its area element is
t * (x - c).n * dl and the constant t cancels; that support function
is the weight. Weighting uniformly instead is a different region and on a
VEST-like boundary a ~5% different answer, so the two modes are not
interchangeable.
Applicability
Machine-independent. Any closed boundary with its poloidal field, from a reconstruction or an analytic equilibrium.
Limitations
Returns NaN rather than a number when more than
MAX_NONCONVEX_ARC_FRACTION of the arc length has negative support:
the conformal annulus folds over itself there, and an unclamped negative
weight would subtract from both integrals. mode="uniform" makes no such
assumption and still answers. How closely the limit matches a finite
annulus is a property of the equilibrium, not of this quadrature.
Provenance
- M. W. Bongard et al., Phys. Plasmas 23 (2016), the low-aspect-ratio virial closure and its annulus estimate of alpha.
- Measured against analytic Solov’ev equilibria in
test/test_virial_alpha_approximations.py.
volume_average
Machine-independentConvention-sensitive
source
volume_average(f_RZ, psiN_RZ, R, Z, weights=None)
Volume average of a gridded quantity over the confined region.
| Name | Type | Unit | Description |
|---|---|---|---|
f_RZ | array_like | any | The quantity on the grid, in whatever unit it has. |
psiN_RZ | array_like | - | Normalized poloidal flux on the same grid, used only as the mask. |
R | array_like | m | Major-radius axis, or the full mesh. |
Z | array_like | m | Height axis, or the full mesh. |
weights | array_like, optional | - | Fraction of each cell inside the plasma, from |
| Returns | |||
favg | float | any | The volume-weighted average of f_RZ inside the boundary. |
volume | float | m^3 | The volume the average was taken over. |
Convention
The volume element is the axisymmetric 2*pi*R dR dZ, so cells at large
major radius weigh proportionally more; this is a torus average, not a
cross-sectional one. Accepts either one-dimensional axes or a full mesh.
Pass weights whenever the slice has a boundary outline. Without
them the plasma is every cell with normalized flux between zero and one and
a positive major radius – a flux threshold, not a containment test. Outside
the plasma psi is not monotonic and turns over by the coils, so the
threshold admits exterior cells: on the packaged VEST samples the flux-mask
volume is 1.7 to 18 times the plasma’s, and a volume-averaged pressure
comes out 40-50 % low. With plasma_cell_weights from the outline
both match the contour-traced reference to 0.1 % and 1 %.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Cell areas come from a gradient of the axes, so the outermost cells are
one-sided and a strongly non-uniform grid is approximated. The flux mask is a
per-cell test with no sub-cell weighting; weights carry each boundary
cell’s area fraction instead.
Provenance
- The axisymmetric volume element, and the same normalized-flux mask
psi_to_rzapplies.