Formula reference: boundaries

Generated from the docstrings of vaft.formula.boundaries: 15 public functions. Each entry is what vaft.formula.describe("boundaries.<name>") prints. All formula categories

Overview

A published limit is more than its formula. It predicts one specific quantity (line-averaged density, not “the density”). It has a permitted side. It was fitted on some machines under some assumptions. It may or may not carry a quoted uncertainty. And it comes from one paper and equation. A Boundary keeps all of that next to the evaluation rule. Then evaluate_boundary can report how far a state sits from the boundary, on which side, and whether the inputs lie inside the fitted domain. boundary_curve returns the boundary as arrays on a chosen 2-D projection, and a plot draws only that; no plotting code lives here (#1067).

Existing formula functions stay where they are. An entry references one through function instead of restating its coefficients, so the Greenwald entry evaluates stability.greenwald_density itself.

SymbolMeaningUnit
bboundary value of the target quantity, in the target's unitvaries
xoperating value of the same quantity, same unitvaries
ratiox / b-
marginsigned normalised distance, positive on the permitted side-

Every input and target is a BoundaryQuantity with an explicit unit string, and callers pass values in exactly that unit. Nothing is converted behind the caller’s back. For allowed_side = "below" (permitted $x < b$) the margin is $(b - x)/b$. For "above" (permitted $x > b$) it is $(x - b)/b$. A positive margin is always the permitted side, and a zero margin lies on the boundary.

Functions

boundary_curveA boundary sampled along one input, as $(x, y) = (x_\mathrm{sweep}, b)$ arrays. Convention
boundary_valueValue of a boundary’s target quantity at the given inputs. Convention
cylindrical_kink_coordinatesMenard’s cylindrical safety factor, $q^* = \pi a^2 B_{T0}(1+\kappa^2)/(\mu_0 R_0 I_P)$. Convention
evaluate_boundaryDistance and side of an operating value relative to a boundary. Convention
evaluate_windowPosition of an operating value relative to an access window. Convention
get_boundaryA registered boundary or window by key.
hugill_coordinatesHugill-diagram coordinates of an operating state: $(\bar n_e R/B_T,\ 1/q_\mathrm{cyl})$. Convention
hugill_coordinates_stSpherical-tokamak Hugill coordinates $(\bar n_e R/B_T,\ 1/q_\mathrm{cyl}^{ST})$ of Sykes et al. (2000). Convention
iter_q95_coordinatesThe ITER design-guideline $q_{95}$ estimate from global shape, $q_{95} \approx q_* f(\epsilon)$. Convention
kink_coordinatesFreidberg’s kink safety factor of an elongated tokamak, $q_* = 2\pi a^2\kappa B_0/(\mu_0 R_0 I)$. Convention
list_boundariesKeys of the registered boundaries, optionally restricted to one family.
propagate_boundary_uncertaintyPropagate measured-input uncertainty into a boundary value and a state’s margin (#1874).
same_quantityWhether two quantities are the same physical quantity in the same unit. Convention
start_q95_coordinatesThe START low-aspect-ratio $q_{95}$ estimate of Akers et al. (2000) from global shape. Convention
threshold_curveA threshold boundary drawn as a straight line across a 2-D projection. Convention

boundary_curve

Convention-sensitive source
boundary_curve(boundary, sweep, values, swap_axes=False, **fixed)
Show source vaft/formula/boundaries.py, lines 850–905
def boundary_curve(boundary: Boundary, sweep: str, values, swap_axes: bool = False, **fixed) -> BoundaryCurve:
    r"""A boundary sampled along one input, as $(x, y) = (x_\mathrm{sweep}, b)$ arrays.

    $$y_k = b\left(x_\mathrm{sweep} = v_k,\ \text{other inputs fixed}\right)$$

    Parameters
    ----------
    boundary : Boundary
        The boundary to sample [-].
    sweep : str
        Name of the input placed on the x axis [-].
    values : array_like
        Values of the swept input, in its declared unit [varies].
    swap_axes : bool
        Put the boundary's target on x and the swept input on y, as in a
        Hugill diagram, whose x axis is the density-like quantity [-].

    Returns
    -------
    BoundaryCurve
        ``x`` is the swept input and ``y`` the boundary value. The curve
        carries both quantities (identity and unit), the permitted side along
        y, and the fixed inputs [-].

    Raises
    ------
    KeyError
        ``sweep`` is not an input of the boundary.
    TypeError
        A fixed input is missing, unknown, an array, or duplicates the swept
        one; or ``values`` is not 1-D.

    Convention
    ----------
    The permitted side is the boundary's own ``allowed_side``, read along the
    y axis: ``"below"`` means operating points under the curve are permitted.
    With ``swap_axes`` the target is read along x, so ``"below"`` becomes
    ``"left"`` and ``"above"`` becomes ``"right"``.
    """
    x_quantity = boundary.input(sweep)
    if sweep in fixed:
        raise TypeError(f"{sweep!r} is swept; do not also fix it")
    non_scalar = [name for name, value in fixed.items() if np.ndim(value) != 0]
    if non_scalar:
        raise TypeError(f"fixed inputs must be scalars; {non_scalar} are arrays")
    x = np.atleast_1d(np.asarray(values, dtype=float))
    if x.ndim != 1:
        raise TypeError("values must be a 1-D sequence")
    y = np.broadcast_to(np.asarray(boundary_value(boundary, **{sweep: x}, **fixed), dtype=float), x.shape)
    fixed = {name: float(value) for name, value in fixed.items()}
    if swap_axes:
        side = {"below": "left", "above": "right"}[boundary.allowed_side]
        return BoundaryCurve(key=boundary.key, x=y, y=x, x_quantity=boundary.target, y_quantity=x_quantity,
                             allowed_side=side, fixed=fixed)
    return BoundaryCurve(key=boundary.key, x=x, y=y, x_quantity=x_quantity, y_quantity=boundary.target,
                         allowed_side=boundary.allowed_side, fixed=fixed)

A boundary sampled along one input, as $(x, y) = (x_\mathrm{sweep}, b)$ arrays.

\[y_k = b\left(x_\mathrm{sweep} = v_k,\ \text{other inputs fixed}\right)\]
NameTypeUnitDescription
boundaryBoundary-

The boundary to sample.

sweepstr-

Name of the input placed on the x axis.

valuesarray_likevaries

Values of the swept input, in its declared unit.

swap_axesbool-

Put the boundary’s target on x and the swept input on y, as in a Hugill diagram, whose x axis is the density-like quantity.

Returns
—BoundaryCurve-

x is the swept input and y the boundary value. The curve carries both quantities (identity and unit), the permitted side along y, and the fixed inputs.

Convention

The permitted side is the boundary’s own allowed_side, read along the y axis: "below" means operating points under the curve are permitted. With swap_axes the target is read along x, so "below" becomes "left" and "above" becomes "right".

Raises

  • KeyError sweep is not an input of the boundary.
  • TypeError A fixed input is missing, unknown, an array, or duplicates the swept one; or values is not 1-D.

boundary_value

Convention-sensitive source
boundary_value(boundary, **inputs)
Show source vaft/formula/boundaries.py, lines 393–431
def boundary_value(boundary: Boundary, **inputs):
    r"""Value of a boundary's target quantity at the given inputs.

    $$b = C\prod_i x_i^{\alpha_i}\quad\text{(power law)},\qquad b = C\quad\text{(threshold)},\qquad b = f(x_1,\ldots)\quad\text{(function)}$$

    Parameters
    ----------
    boundary : Boundary
        The boundary to evaluate [-].

    Returns
    -------
    float or np.ndarray
        Boundary value, in the unit of ``boundary.target`` [varies].

    Raises
    ------
    TypeError
        An input is missing or not one of the boundary's inputs.

    Convention
    ----------
    Each keyword input is a value in the unit its ``BoundaryQuantity``
    declares. No unit conversion happens here; a caller holding amperes for a
    boundary declared in MA converts first.

    Notes
    -----
    Array inputs broadcast with NumPy rules.
    """
    _check_inputs(boundary, inputs)
    if boundary.form == "threshold":
        return float(boundary.coefficient)
    if boundary.form == "function":
        return _scalar_or_array(boundary.function(**inputs))
    value = boundary.coefficient
    for name, exponent in boundary.exponents.items():
        value = value * np.asarray(inputs[name], dtype=float) ** exponent
    return _scalar_or_array(value)

Value of a boundary’s target quantity at the given inputs.

\[b = C\prod_i x_i^{\alpha_i}\quad\text{(power law)},\qquad b = C\quad\text{(threshold)},\qquad b = f(x_1,\ldots)\quad\text{(function)}\]
NameTypeUnitDescription
boundaryBoundary-

The boundary to evaluate.

Returns
—float or np.ndarrayvaries

Boundary value, in the unit of boundary.target.

Convention

Each keyword input is a value in the unit its BoundaryQuantity declares. No unit conversion happens here; a caller holding amperes for a boundary declared in MA converts first.

Raises

  • TypeError An input is missing or not one of the boundary’s inputs.
Notes

Notes

Array inputs broadcast with NumPy rules.

cylindrical_kink_coordinates

Convention-sensitive source
cylindrical_kink_coordinates(a, R0, B0, kappa, I_p)
Show source vaft/formula/boundaries.py, lines 2301–2348
def cylindrical_kink_coordinates(a, R0, B0, kappa, I_p):
    r"""Menard's cylindrical safety factor, $q^* = \pi a^2 B_{T0}(1+\kappa^2)/(\mu_0 R_0 I_P)$.

    $$q^* = \epsilon\,(1+\kappa^2)\,\frac{\pi a B_{T0}}{\mu_0 I_P}
          = \frac{\pi a^2 B_{T0}\,(1+\kappa^2)}{\mu_0 R_0 I_P}$$

    Parameters
    ----------
    a : float or np.ndarray
        Minor radius [m].
    R0 : float or np.ndarray
        Major radius [m].
    B0 : float or np.ndarray
        Vacuum toroidal field at ``R0``; its sign is dropped [T].
    kappa : float or np.ndarray
        Elongation [-].
    I_p : float or np.ndarray
        Plasma current; its sign is dropped [MA].

    Returns
    -------
    float or np.ndarray
        Cylindrical safety factor $q^*$, NaN where an input is NaN [-].

    Raises
    ------
    ValueError
        A finite non-positive (or infinite) minor radius, major radius, field
        magnitude or elongation.

    Convention
    ----------
    A global, shape-weighted proxy of the edge safety factor, not $q_a$ or $q_{95}$:
    Menard et al. use it because $q(1)$ and $q(0.95)$ at the current limit vary by a
    factor two with aspect ratio and shape while $q^*$ does not. It is the
    $(1+\kappa^2)/2$ form of Freidberg Eq. (13.171), and differs from Freidberg's
    Eq. (13.160) (``kink_coordinates``) by the factor $(1+\kappa^2)/(2\kappa)$.

    References
    ----------
    .. [1] J. E. Menard et al., Phys. Plasmas 11 (2004) 639; preprint PPPL-3908, p. 9.
    .. [2] D. A. D'Ippolito, J. P. Freidberg, J. P. Goedbloed and J. Rem, Phys. Fluids 21 (1978) 1600.
    """
    g = _positive_geometry(a=a, R0=R0, B0=np.abs(np.asarray(B0, dtype=float)), kappa=kappa)
    current = np.abs(np.asarray(I_p, dtype=float)) * 1e6
    with np.errstate(divide="ignore"):
        q = np.pi * g["a"] ** 2 * g["B0"] * (1.0 + g["kappa"] ** 2) / (MU0 * g["R0"] * current)
    return _scalar_or_array(q)

Menard’s cylindrical safety factor, $q^* = \pi a^2 B_{T0}(1+\kappa^2)/(\mu_0 R_0 I_P)$.

\[q^* = \epsilon\,(1+\kappa^2)\,\frac{\pi a B_{T0}}{\mu_0 I_P} = \frac{\pi a^2 B_{T0}\,(1+\kappa^2)}{\mu_0 R_0 I_P}\]
NameTypeUnitDescription
afloat or np.ndarraym

Minor radius.

R0float or np.ndarraym

Major radius.

B0float or np.ndarrayT

Vacuum toroidal field at R0; its sign is dropped.

kappafloat or np.ndarray-

Elongation.

I_pfloat or np.ndarrayMA

Plasma current; its sign is dropped.

Returns
—float or np.ndarray-

Cylindrical safety factor $q^*$, NaN where an input is NaN.

Convention

A global, shape-weighted proxy of the edge safety factor, not $q_a$ or $q_{95}$: Menard et al. use it because $q(1)$ and $q(0.95)$ at the current limit vary by a factor two with aspect ratio and shape while $q^*$ does not. It is the $(1+\kappa^2)/2$ form of Freidberg Eq. (13.171), and differs from Freidberg’s Eq. (13.160) (kink_coordinates) by the factor $(1+\kappa^2)/(2\kappa)$.

Raises

  • ValueError A finite non-positive (or infinite) minor radius, major radius, field magnitude or elongation.
References

References

  1. J. E. Menard et al., Phys. Plasmas 11 (2004) 639; preprint PPPL-3908, p. 9.
  2. D. A. D’Ippolito, J. P. Freidberg, J. P. Goedbloed and J. Rem, Phys. Fluids 21 (1978) 1600.

evaluate_boundary

Convention-sensitive source
evaluate_boundary(boundary, operating_value, **inputs)

Distance and side of an operating value relative to a boundary.

\[\mathrm{margin} = \frac{b - x}{b}\ \ (\text{permitted below}),\qquad \mathrm{margin} = \frac{x - b}{b}\ \ (\text{permitted above})\]
NameTypeUnitDescription
boundaryBoundary-

The boundary to compare against.

operating_valuefloat or np.ndarrayvaries

Operating value $x$ of the boundary’s target quantity, in the target’s unit.

Returns
—BoundaryEvaluation-

Boundary value $b$, ratio $x/b$, difference $x - b$, signed margin (positive on the permitted side), permitted flag, the inputs that lie outside the source’s stated ranges, and warnings.

Convention

The margin is normalised by the boundary value. A positive margin is always the permitted side, whichever way the boundary points, so margins of different boundaries share one sign convention. allowed is strict: a state exactly on the boundary is not counted as permitted. A boundary value that is zero, negative or not finite gives NaN margin and ratio, is never permitted, and adds a warning. An operating value that is not finite (a NaN gap in a trace) is likewise reported as not permitted, with NaN margin and ratio and a warning: allowed is False there, not unknown, so count violations from margin when a trace has gaps. Pass magnitudes where a sign convention could make the boundary negative.

Raises

  • TypeError An input is missing or not one of the boundary’s inputs.
Notes

Notes

Crossing a soft boundary is not a prediction of failure. For example, $f_G > 1$ is routinely reached. The evaluation reports the geometry, and interpreting it is left to the caller.

evaluate_window

Convention-sensitive source
evaluate_window(window, operating_value, **inputs)
Show source vaft/formula/boundaries.py, lines 806–847
def evaluate_window(window: BoundaryWindow, operating_value, **inputs) -> WindowEvaluation:
    r"""Position of an operating value relative to an access window.

    $$b_\mathrm{lower} < x < b_\mathrm{upper}$$

    Parameters
    ----------
    window : BoundaryWindow
        The access window [-].
    operating_value : float or np.ndarray
        Operating value of the window's target quantity, in its unit [varies].

    Returns
    -------
    WindowEvaluation
        One evaluation per edge and ``inside``, true where both are
        permitted [-].

    Raises
    ------
    TypeError
        An input is missing or unused by both edges.

    Convention
    ----------
    ``inputs`` holds the union of both edges' inputs. Each edge receives only
    the inputs it declares.
    """
    known = set(window.lower.input_names) | set(window.upper.input_names)
    extra = [name for name in inputs if name not in known]
    if extra:
        raise TypeError(f"window {window.key!r} does not use inputs {extra}")
    lower = evaluate_boundary(
        window.lower, operating_value, **{k: inputs[k] for k in window.lower.input_names if k in inputs}
    )
    upper = evaluate_boundary(
        window.upper, operating_value, **{k: inputs[k] for k in window.upper.input_names if k in inputs}
    )
    inside = np.logical_and(lower.allowed, upper.allowed)
    return WindowEvaluation(
        key=window.key, lower=lower, upper=upper, inside=bool(inside) if np.ndim(inside) == 0 else inside
    )

Position of an operating value relative to an access window.

\[b_\mathrm{lower} < x < b_\mathrm{upper}\]
NameTypeUnitDescription
windowBoundaryWindow-

The access window.

operating_valuefloat or np.ndarrayvaries

Operating value of the window’s target quantity, in its unit.

Returns
—WindowEvaluation-

One evaluation per edge and inside, true where both are permitted.

Convention

inputs holds the union of both edges’ inputs. Each edge receives only the inputs it declares.

Raises

  • TypeError An input is missing or unused by both edges.

get_boundary

source
get_boundary(key)
Show source vaft/formula/boundaries.py, lines 1063–1084
def get_boundary(key: str):
    """A registered boundary or window by key.

    Parameters
    ----------
    key : str
        Registry key, for example ``"greenwald"`` [-].

    Returns
    -------
    Boundary or BoundaryWindow
        The registered entry [-].

    Raises
    ------
    KeyError
        No entry has that key; the message lists the keys that exist.
    """
    try:
        return _REGISTRY[key]
    except KeyError:
        raise KeyError(f"no boundary {key!r}; registered: {sorted(_REGISTRY)}") from None

A registered boundary or window by key.

NameTypeUnitDescription
keystr-

Registry key, for example "greenwald".

Returns
—Boundary or BoundaryWindow-

The registered entry.

Raises

  • KeyError No entry has that key; the message lists the keys that exist.

hugill_coordinates

Convention-sensitive source
hugill_coordinates(n_e, R_geo, B_t, a, kappa_a, I_p)
Show source vaft/formula/boundaries.py, lines 987–1046
def hugill_coordinates(n_e, R_geo, B_t, a, kappa_a, I_p):
    r"""Hugill-diagram coordinates of an operating state: $(\bar n_e R/B_T,\ 1/q_\mathrm{cyl})$.

    $$x = \frac{\bar n_e R_{geo}}{B_T},\qquad y = \frac{1}{q_{cyl}} = \frac{R_{geo}\,|I_p|}{5\,a^2\kappa_a B_T}\quad(I_p\ \mathrm{in\ MA})$$

    Parameters
    ----------
    n_e : float or np.ndarray
        Line-averaged electron density [1e19 m^-3].
    R_geo : float or np.ndarray
        Geometric major radius [m].
    B_t : float or np.ndarray
        Vacuum toroidal field at ``R_geo``, magnitude [T].
    a : float or np.ndarray
        Minor radius [m].
    kappa_a : float or np.ndarray
        Area elongation $S/(\pi a^2)$ [-].
    I_p : float or np.ndarray
        Plasma current; its sign is dropped [MA].

    Returns
    -------
    tuple of (float or np.ndarray)
        Murakami parameter $\bar n_e R/B_T$ [1e19 m^-2 T^-1] and $1/q_{cyl}$ [-].

    Raises
    ------
    ValueError
        A negative density, or a non-positive field, radius, minor radius or
        elongation.

    Convention
    ----------
    $q_{cyl}$ is ``equilibrium.q_cyl_from_B_R_epsilon_kappa_I`` with
    $\varepsilon = a/R_{geo}$. The ``"greenwald_hugill"`` boundary uses the
    same convention, so a state at $\bar n_e = n_G$ lies exactly on it. This
    is the cylindrical $q$, not $q_{95}$. At VEST aspect ratio the two differ
    substantially, and plotting $1/q_{95}$ against this boundary is only
    approximate.

    Numerical notes
    ---------------
    $1/q_{cyl}$ is formed directly rather than as ``1/q_cyl_from_...``, so a
    zero current maps to the origin $y = 0$ instead of raising. A whole time
    trace, including start-up and termination, can therefore be projected
    without cropping.
    """
    n = np.asarray(n_e, dtype=float)
    B_abs = np.abs(np.asarray(B_t, dtype=float))
    R = np.asarray(R_geo, dtype=float)
    a_arr = np.asarray(a, dtype=float)
    kappa = np.asarray(kappa_a, dtype=float)
    if np.any(n < 0):
        raise ValueError("n_e must be non-negative")
    for name, value in (("B_t", B_abs), ("R_geo", R), ("a", a_arr), ("kappa_a", kappa)):
        if np.any(~(value > 0)):
            raise ValueError(f"{name} must be positive and finite")
    x = n * R / B_abs
    y = np.abs(np.asarray(I_p, dtype=float)) * R / (5.0 * a_arr**2 * kappa * B_abs)
    return _scalar_or_array(x), _scalar_or_array(y)

Hugill-diagram coordinates of an operating state: $(\bar n_e R/B_T,\ 1/q_\mathrm{cyl})$.

\[x = \frac{\bar n_e R_{geo}}{B_T},\qquad y = \frac{1}{q_{cyl}} = \frac{R_{geo}\,|I_p|}{5\,a^2\kappa_a B_T}\quad(I_p\ \mathrm{in\ MA})\]
NameTypeUnitDescription
n_efloat or np.ndarray1e19 m^-3

Line-averaged electron density.

R_geofloat or np.ndarraym

Geometric major radius.

B_tfloat or np.ndarrayT

Vacuum toroidal field at R_geo, magnitude.

afloat or np.ndarraym

Minor radius.

kappa_afloat or np.ndarray-

Area elongation $S/(\pi a^2)$.

I_pfloat or np.ndarrayMA

Plasma current; its sign is dropped.

Returns
—tuple of (float or np.ndarray)-

Murakami parameter $\bar n_e R/B_T$ [1e19 m^-2 T^-1] and $1/q_{cyl}$.

Convention

$q_{cyl}$ is equilibrium.q_cyl_from_B_R_epsilon_kappa_I with $\varepsilon = a/R_{geo}$. The "greenwald_hugill" boundary uses the same convention, so a state at $\bar n_e = n_G$ lies exactly on it. This is the cylindrical $q$, not $q_{95}$. At VEST aspect ratio the two differ substantially, and plotting $1/q_{95}$ against this boundary is only approximate.

Raises

  • ValueError A negative density, or a non-positive field, radius, minor radius or elongation.
Numerical notes

Numerical notes

$1/q_{cyl}$ is formed directly rather than as 1/q_cyl_from_..., so a zero current maps to the origin $y = 0$ instead of raising. A whole time trace, including start-up and termination, can therefore be projected without cropping.

hugill_coordinates_st

Convention-sensitive source
hugill_coordinates_st(n_e, R_geo, B_t, a, kappa, I_p)
Show source vaft/formula/boundaries.py, lines 2736–2786
def hugill_coordinates_st(n_e, R_geo, B_t, a, kappa, I_p):
    r"""Spherical-tokamak Hugill coordinates $(\bar n_e R/B_T,\ 1/q_\mathrm{cyl}^{ST})$ of Sykes et al. (2000).

    $$x = \frac{\bar n_e R}{B_T},\qquad
      y = \frac{1}{q^{ST}_{cyl}} = \frac{R\,|I_p|}{2.5\,a^2(1+\kappa^2)B_T}\quad(I_p\ \mathrm{in\ MA})$$

    Parameters
    ----------
    n_e : float or np.ndarray
        Line-averaged electron density [1e19 m^-3].
    R_geo : float or np.ndarray
        Major radius [m].
    B_t : float or np.ndarray
        Vacuum toroidal field at ``R_geo``; its sign is dropped [T].
    a : float or np.ndarray
        Minor radius [m].
    kappa : float or np.ndarray
        Elongation [-].
    I_p : float or np.ndarray
        Plasma current; its sign is dropped [MA].

    Returns
    -------
    tuple of (float or np.ndarray)
        Murakami parameter $\bar n_e R/B_T$ [1e19 m^-2 T^-1] and $1/q^{ST}_{cyl}$ [-].

    Raises
    ------
    ValueError
        A negative density, or a finite non-positive (or infinite) radius, minor
        radius, field magnitude or elongation.

    Convention
    ----------
    $q^{ST}_{cyl}$ is Menard's cylindrical $q^*$ (``cylindrical_kink_coordinates``),
    as Sykes et al. plot it for MAST; it differs from the conventional Hugill
    $q_{cyl}$ (``hugill_coordinates``) by $(1+\kappa^2)/(2\kappa_a)$, and the two are
    separate quantities. NaN inputs propagate.

    References
    ----------
    .. [1] A. Sykes et al., First results from MAST, IAEA FEC 2000, Sec. 6, Fig. 12;
           Nucl. Fusion 41 (2001) 1423.
    """
    g = _positive_geometry(R_geo=R_geo, B_t=np.abs(np.asarray(B_t, dtype=float)), a=a, kappa=kappa)
    n = np.asarray(n_e, dtype=float)
    if np.any(np.isfinite(n) & (n < 0)):
        raise ValueError("n_e must be non-negative where it is given")
    x = n * g["R_geo"] / g["B_t"]
    y = g["R_geo"] * np.abs(np.asarray(I_p, dtype=float)) / (2.5 * g["a"] ** 2 * (1.0 + g["kappa"] ** 2) * g["B_t"])
    return _scalar_or_array(x), _scalar_or_array(y)

Spherical-tokamak Hugill coordinates $(\bar n_e R/B_T,\ 1/q_\mathrm{cyl}^{ST})$ of Sykes et al. (2000).

\[x = \frac{\bar n_e R}{B_T},\qquad y = \frac{1}{q^{ST}_{cyl}} = \frac{R\,|I_p|}{2.5\,a^2(1+\kappa^2)B_T}\quad(I_p\ \mathrm{in\ MA})\]
NameTypeUnitDescription
n_efloat or np.ndarray1e19 m^-3

Line-averaged electron density.

R_geofloat or np.ndarraym

Major radius.

B_tfloat or np.ndarrayT

Vacuum toroidal field at R_geo; its sign is dropped.

afloat or np.ndarraym

Minor radius.

kappafloat or np.ndarray-

Elongation.

I_pfloat or np.ndarrayMA

Plasma current; its sign is dropped.

Returns
—tuple of (float or np.ndarray)-

Murakami parameter $\bar n_e R/B_T$ [1e19 m^-2 T^-1] and $1/q^{ST}_{cyl}$.

Convention

$q^{ST}{cyl}$ is Menard’s cylindrical $q^*$ (cylindrical_kink_coordinates), as Sykes et al. plot it for MAST; it differs from the conventional Hugill $q{cyl}$ (hugill_coordinates) by $(1+\kappa^2)/(2\kappa_a)$, and the two are separate quantities. NaN inputs propagate.

Raises

  • ValueError A negative density, or a finite non-positive (or infinite) radius, minor radius, field magnitude or elongation.
References

References

  1. A. Sykes et al., First results from MAST, IAEA FEC 2000, Sec. 6, Fig. 12; Nucl. Fusion 41 (2001) 1423.

iter_q95_coordinates

Convention-sensitive source
iter_q95_coordinates(a, R0, B0, kappa, delta, I_p)
Show source vaft/formula/boundaries.py, lines 2381–2440
def iter_q95_coordinates(a, R0, B0, kappa, delta, I_p):
    r"""The ITER design-guideline $q_{95}$ estimate from global shape, $q_{95} \approx q_* f(\epsilon)$.

    $$q_{95} \approx \frac{5a^2B}{R\,I_p[\mathrm{MA}]}\,
      \frac{1+\kappa^2(1+2\delta^2-1.2\delta^3)}{2}\,\frac{1.17-0.65\epsilon}{(1-\epsilon^2)^2},
      \qquad \epsilon = a/R$$

    Parameters
    ----------
    a : float or np.ndarray
        Minor radius [m].
    R0 : float or np.ndarray
        Major radius [m].
    B0 : float or np.ndarray
        Vacuum toroidal field at ``R0``; its sign is dropped [T].
    kappa : float or np.ndarray
        Elongation of the 95 % flux surface, kappa_95 [-].
    delta : float or np.ndarray
        Triangularity of the 95 % flux surface, delta_95; may be zero or negative [-].
    I_p : float or np.ndarray
        Plasma current; its sign is dropped [MA].

    Returns
    -------
    float or np.ndarray
        Estimated $q_{95}$, NaN where an input is NaN [-].

    Raises
    ------
    ValueError
        A finite non-positive (or infinite) minor radius, major radius, field
        magnitude or elongation, or $a \ge R_0$.

    Convention
    ----------
    The formula is calibrated on the 95 % flux-surface shape ($\kappa_{95}$,
    $\delta_{95}$); fed the last-closed-surface $\kappa$ and $\delta$, which are larger,
    it over-estimates $q_{95}$ (for the ITER design point by about 27 %).
    A fit for conventional aspect ratio used for ITER design; at a spherical
    tokamak's $A \approx 1.3$ it is an extrapolation and over-estimates $q_{95}$
    (Akers et al. 2000 give a START-based correction, not registered here). It is an
    estimate from global parameters, not an equilibrium $q_{95}$.

    References
    ----------
    .. [1] D. E. Post et al., *ITER Physics*, ITER Documentation Series No. 21, IAEA (1991),
           Table 1-2.
    .. [2] N. A. Uckan and ITER Physics Group, *ITER Physics Design Guidelines: 1989*,
           IAEA/ITER/DS-10, IAEA (1990), p. 10.
    """
    g = _positive_geometry(a=a, R0=R0, B0=np.abs(np.asarray(B0, dtype=float)), kappa=kappa)
    if np.any(np.isfinite(g["a"] / g["R0"]) & (g["a"] / g["R0"] >= 1.0)):
        raise ValueError("a must be smaller than R0")
    d = np.asarray(delta, dtype=float)
    if np.any(np.isinf(d)):
        raise ValueError("delta must be finite where it is given")
    current = np.abs(np.asarray(I_p, dtype=float))
    with np.errstate(divide="ignore"):
        q = _iter_q95_per_ma(g["a"], g["R0"], g["B0"], g["kappa"], d) / current
    return _scalar_or_array(q)

The ITER design-guideline $q_{95}$ estimate from global shape, $q_{95} \approx q_* f(\epsilon)$.

\[q_{95} \approx \frac{5a^2B}{R\,I_p[\mathrm{MA}]}\, \frac{1+\kappa^2(1+2\delta^2-1.2\delta^3)}{2}\,\frac{1.17-0.65\epsilon}{(1-\epsilon^2)^2}, \qquad \epsilon = a/R\]
NameTypeUnitDescription
afloat or np.ndarraym

Minor radius.

R0float or np.ndarraym

Major radius.

B0float or np.ndarrayT

Vacuum toroidal field at R0; its sign is dropped.

kappafloat or np.ndarray-

Elongation of the 95 % flux surface, kappa_95.

deltafloat or np.ndarray-

Triangularity of the 95 % flux surface, delta_95; may be zero or negative.

I_pfloat or np.ndarrayMA

Plasma current; its sign is dropped.

Returns
—float or np.ndarray-

Estimated $q_{95}$, NaN where an input is NaN.

Convention

The formula is calibrated on the 95 % flux-surface shape ($\kappa_{95}$, $\delta_{95}$); fed the last-closed-surface $\kappa$ and $\delta$, which are larger, it over-estimates $q_{95}$ (for the ITER design point by about 27 %). A fit for conventional aspect ratio used for ITER design; at a spherical tokamak’s $A \approx 1.3$ it is an extrapolation and over-estimates $q_{95}$ (Akers et al. 2000 give a START-based correction, not registered here). It is an estimate from global parameters, not an equilibrium $q_{95}$.

Raises

  • ValueError A finite non-positive (or infinite) minor radius, major radius, field magnitude or elongation, or $a \ge R_0$.
References

References

  1. D. E. Post et al., ITER Physics, ITER Documentation Series No. 21, IAEA (1991), Table 1-2.
  2. N. A. Uckan and ITER Physics Group, ITER Physics Design Guidelines: 1989, IAEA/ITER/DS-10, IAEA (1990), p. 10.

kink_coordinates

Convention-sensitive source
kink_coordinates(a, R0, B0, kappa, I_p)
Show source vaft/formula/boundaries.py, lines 2152–2201
def kink_coordinates(a, R0, B0, kappa, I_p):
    r"""Freidberg's kink safety factor of an elongated tokamak, $q_* = 2\pi a^2\kappa B_0/(\mu_0 R_0 I)$.

    $$q_* = \frac{2\pi a^2 \kappa B_0}{\mu_0 R_0 I_p} = \frac{5\,a^2\kappa B_0}{R_0\,I_p[\mathrm{MA}]}$$

    Parameters
    ----------
    a : float or np.ndarray
        Minor radius [m].
    R0 : float or np.ndarray
        Major radius [m].
    B0 : float or np.ndarray
        Vacuum toroidal field at ``R0``; its sign is dropped [T].
    kappa : float or np.ndarray
        Elongation [-].
    I_p : float or np.ndarray
        Plasma current; its sign is dropped [MA].

    Returns
    -------
    float or np.ndarray
        Kink safety factor $q_*$ of Eq. (13.160), NaN where an input is NaN [-].

    Raises
    ------
    ValueError
        A finite non-positive (or infinite) minor radius, major radius, field
        magnitude or elongation.

    Convention
    ----------
    Eq. (13.160) is the $G(\kappa) \approx 1$ form of Eq. (13.158), the definition with
    which the kink limit ``"freidberg_2008_kink_qstar"`` (Eq. 13.162) is stated. It is
    not ``vaft.formula.equilibrium.kink_safety_factor`` (#1524). A zero current maps to
    an infinite $q_*$.

    References
    ----------
    .. [1] J. P. Freidberg, *Plasma Physics and Fusion Energy*, Cambridge University
           Press (2008), Eqs. (13.158)-(13.160), p. 405.
    """
    a_arr, R, B_abs, k = (np.asarray(v, dtype=float) for v in (a, R0, np.abs(np.asarray(B0, dtype=float)), kappa))
    for name, value in (("a", a_arr), ("R0", R), ("B0", B_abs), ("kappa", k)):
        # NaN propagates (a population table has gaps); a finite non-positive value or an infinity is an error
        if np.any(np.isinf(value)) or np.any(np.isfinite(value) & ~(value > 0)):
            raise ValueError(f"{name} must be positive and finite where it is given")
    current = np.abs(np.asarray(I_p, dtype=float)) * 1e6
    with np.errstate(divide="ignore"):
        q = 2.0 * np.pi * a_arr**2 * k * B_abs / (MU0 * R * current)
    return _scalar_or_array(q)

Freidberg’s kink safety factor of an elongated tokamak, $q_* = 2\pi a^2\kappa B_0/(\mu_0 R_0 I)$.

\[q_* = \frac{2\pi a^2 \kappa B_0}{\mu_0 R_0 I_p} = \frac{5\,a^2\kappa B_0}{R_0\,I_p[\mathrm{MA}]}\]
NameTypeUnitDescription
afloat or np.ndarraym

Minor radius.

R0float or np.ndarraym

Major radius.

B0float or np.ndarrayT

Vacuum toroidal field at R0; its sign is dropped.

kappafloat or np.ndarray-

Elongation.

I_pfloat or np.ndarrayMA

Plasma current; its sign is dropped.

Returns
—float or np.ndarray-

Kink safety factor $q_*$ of Eq. (13.160), NaN where an input is NaN.

Convention

Eq. (13.160) is the $G(\kappa) \approx 1$ form of Eq. (13.158), the definition with which the kink limit "freidberg_2008_kink_qstar" (Eq. 13.162) is stated. It is not vaft.formula.equilibrium.kink_safety_factor (#1524). A zero current maps to an infinite $q_*$.

Raises

  • ValueError A finite non-positive (or infinite) minor radius, major radius, field magnitude or elongation.
References

References

  1. J. P. Freidberg, Plasma Physics and Fusion Energy, Cambridge University Press (2008), Eqs. (13.158)-(13.160), p. 405.

list_boundaries

source
list_boundaries(family=None)
Show source vaft/formula/boundaries.py, lines 1087–1103
def list_boundaries(family: Optional[str] = None) -> tuple:
    """Keys of the registered boundaries, optionally restricted to one family.

    Parameters
    ----------
    family : str, optional
        Family name, for example ``"density_limit"`` [-].

    Returns
    -------
    tuple of str
        Sorted registry keys [-].
    """
    return tuple(sorted(
        key for key, entry in _REGISTRY.items()
        if family is None or getattr(entry, "family", getattr(getattr(entry, "lower", None), "family", None)) == family
    ))

Keys of the registered boundaries, optionally restricted to one family.

NameTypeUnitDescription
familystr, optional-

Family name, for example "density_limit".

Returns
—tuple of str-

Sorted registry keys.

propagate_boundary_uncertainty

source
propagate_boundary_uncertainty(boundary, operating_value, *, inputs, std=None, covariance=None, input_names=None, method='linear', fd_step=None, samples=2000, seed=0)

Propagate measured-input uncertainty into a boundary value and a state’s margin (#1874).

\[\Sigma_b \approx J_b\Sigma J_b^{\mathsf T},\qquad \Sigma_m \approx J_m\Sigma J_m^{\mathsf T},\qquad m = \pm\frac{b - x}{b}\]
NameTypeUnitDescription
boundaryBoundary-

The boundary.

operating_valuefloatvaries

Operating value $x$ of the target quantity, in its unit.

inputsmappingvaries

Every boundary input by name, in the units its BoundaryQuantity declares.

stdarray-like, optionalvaries

Standard deviations in input_names order. Exactly one of std and covariance.

covariancearray-like, optionalvaries

Covariance in input_names order, or 1-D variances; this is how the operating value and the inputs share a measurement (a density and the current from one shot).

input_namessequence of str, optional-

Uncertain quantities: "operating_value" and any boundary inputs. Default ("operating_value", *the boundary inputs).

methodstr, optional-

"linear" or "monte_carlo", as in vaft.formula.sensitivity.propagate_formula_uncertainty.

fd_stepfloat, array-like or mapping, optionalvaries

Finite-difference steps in input_names order (or by name), needed for a quantity whose mean is zero.

samplesint, optional-

Monte Carlo draws.

seedint, optional-

Monte Carlo seed.

Returns
—BoundaryPropagation-

The input-measurement propagation of $b$ and of the margin, and the source-reported spreads kept apart from them.

Raises

  • ValueError Everything vaft.formula.sensitivity.propagate_formula_uncertainty refuses; a non-scalar input or operating value; or a boundary whose value is not positive and finite at the inputs (no margin is defined there).
  • TypeError An input is missing or not one of the boundary’s inputs.
Assumptions · Limitations · References

Assumptions

First order unless method="monte_carlo"; the margin is linear in $x$ and in $1/b$, so near the boundary ($m \to 0$) the linear result stays accurate while $b$ is well determined. The three uncertainty parts are independent by construction: measurement, source parameters, source scatter. Combining them, and deciding what a margin within a few of them means, is an assessment and is not done here.

Limitations

Scalar inputs only. The source parameters are taken as uncorrelated. A boundary with a jump (a branch switch) near the inputs gives a spread a finite difference cannot describe; warnings says so when the margin’s linearity check fails. With method="monte_carlo" the margin and the boundary value are sampled separately, so their draws are not joint.

References

  1. JCGM 100:2008, Guide to the expression of uncertainty in measurement, Sec. 5.2 (first-order propagation with correlations).
  2. Issue #1874, Phase C (measurement, source-parameter and scatter uncertainty kept apart for an operational boundary).

same_quantity

Convention-sensitive source
same_quantity(a, b)
Show source vaft/formula/boundaries.py, lines 908–929
def same_quantity(a: BoundaryQuantity, b: BoundaryQuantity) -> bool:
    r"""Whether two quantities are the same physical quantity in the same unit.

    Parameters
    ----------
    a, b : BoundaryQuantity
        The quantities to compare [-].

    Returns
    -------
    bool
        ``True`` when both ``name`` (the identity) and ``unit`` match. The
        symbol and the free-text definition are not compared [-].

    Convention
    ----------
    Identity is the ``name``: ``edge_safety_factor`` ($q_\psi$),
    ``edge_safety_factor_95`` ($q_{95}$) and ``inverse_cylindrical_q`` are
    different quantities even where their values are close. A boundary may be
    drawn on an axis only when this returns ``True`` for that axis (#1425).
    """
    return a.name == b.name and a.unit == b.unit

Whether two quantities are the same physical quantity in the same unit.

NameTypeUnitDescription
a, bBoundaryQuantity-

The quantities to compare.

Returns
—bool-

True when both name (the identity) and unit match. The symbol and the free-text definition are not compared.

Convention

Identity is the name: edge_safety_factor ($q_\psi$), edge_safety_factor_95 ($q_{95}$) and inverse_cylindrical_q are different quantities even where their values are close. A boundary may be drawn on an axis only when this returns True for that axis (#1425).

start_q95_coordinates

Convention-sensitive source
start_q95_coordinates(a, R0, B0, kappa, delta, I_p, configuration='limiter')
Show source vaft/formula/boundaries.py, lines 2588–2645
def start_q95_coordinates(a, R0, B0, kappa, delta, I_p, configuration="limiter"):
    r"""The START low-aspect-ratio $q_{95}$ estimate of Akers et al. (2000) from global shape.

    $$q_{95} \approx \frac{5a^2B}{R\,I_p[\mathrm{MA}]}\,
      \frac{1+\kappa^2(1+2\delta^2-1.2\delta^3)}{2}\;1.17\,C\sqrt{\frac{A}{A-1}},
      \qquad A = R/a$$

    Parameters
    ----------
    a : float or np.ndarray
        Minor radius [m].
    R0 : float or np.ndarray
        Major radius [m].
    B0 : float or np.ndarray
        Vacuum toroidal field at ``R0``; its sign is dropped [T].
    kappa : float or np.ndarray
        Elongation of the 95 % flux surface, kappa_95 [-].
    delta : float or np.ndarray
        Triangularity of the 95 % flux surface, delta_95 [-].
    I_p : float or np.ndarray
        Plasma current; its sign is dropped [MA].
    configuration : {"limiter", "double_null"}
        Sets C = 1.0 (natural limiter plasma) or 0.77 (double null) [-].

    Returns
    -------
    float or np.ndarray
        Estimated $q_{95}$, NaN where an input is NaN [-].

    Raises
    ------
    ValueError
        A finite non-positive (or infinite) minor radius, major radius, field
        magnitude or elongation, an infinite triangularity, $a \ge R_0$, or an
        unknown configuration.

    Convention
    ----------
    A fit to START equilibria (spherical tokamak), more conservative in aspect ratio
    than the ITER guideline formula (``iter_q95_coordinates``), whose shaping factor and
    normalisation it shares. Like it, it is written for the 95 % surface shape.

    References
    ----------
    .. [1] R. J. Akers et al., Nucl. Fusion 40 (2000) 1223, Sec. 2.1, p. 1227.
    """
    if configuration not in AKERS_2000_C:
        raise ValueError(f"configuration must be one of {sorted(AKERS_2000_C)}, not {configuration!r}")
    g = _positive_geometry(a=a, R0=R0, B0=np.abs(np.asarray(B0, dtype=float)), kappa=kappa)
    if np.any(np.isfinite(g["a"] / g["R0"]) & (g["a"] / g["R0"] >= 1.0)):
        raise ValueError("a must be smaller than R0")
    d = np.asarray(delta, dtype=float)
    if np.any(np.isinf(d)):
        raise ValueError("delta must be finite where it is given")
    current = np.abs(np.asarray(I_p, dtype=float))
    with np.errstate(divide="ignore"):
        q = _start_q95_per_ma(g["a"], g["R0"], g["B0"], g["kappa"], d, AKERS_2000_C[configuration]) / current
    return _scalar_or_array(q)

The START low-aspect-ratio $q_{95}$ estimate of Akers et al. (2000) from global shape.

\[q_{95} \approx \frac{5a^2B}{R\,I_p[\mathrm{MA}]}\, \frac{1+\kappa^2(1+2\delta^2-1.2\delta^3)}{2}\;1.17\,C\sqrt{\frac{A}{A-1}}, \qquad A = R/a\]
NameTypeUnitDescription
afloat or np.ndarraym

Minor radius.

R0float or np.ndarraym

Major radius.

B0float or np.ndarrayT

Vacuum toroidal field at R0; its sign is dropped.

kappafloat or np.ndarray-

Elongation of the 95 % flux surface, kappa_95.

deltafloat or np.ndarray-

Triangularity of the 95 % flux surface, delta_95.

I_pfloat or np.ndarrayMA

Plasma current; its sign is dropped.

configuration{"limiter", "double_null"}-

Sets C = 1.0 (natural limiter plasma) or 0.77 (double null).

Returns
—float or np.ndarray-

Estimated $q_{95}$, NaN where an input is NaN.

Convention

A fit to START equilibria (spherical tokamak), more conservative in aspect ratio than the ITER guideline formula (iter_q95_coordinates), whose shaping factor and normalisation it shares. Like it, it is written for the 95 % surface shape.

Raises

  • ValueError A finite non-positive (or infinite) minor radius, major radius, field magnitude or elongation, an infinite triangularity, $a \ge R_0$, or an unknown configuration.
References

References

  1. R. J. Akers et al., Nucl. Fusion 40 (2000) 1223, Sec. 2.1, p. 1227.

threshold_curve

Convention-sensitive source
threshold_curve(boundary, axis, values, target_axis='y')
Show source vaft/formula/boundaries.py, lines 932–984
def threshold_curve(boundary: Boundary, axis: BoundaryQuantity, values, target_axis: str = "y") -> BoundaryCurve:
    r"""A threshold boundary drawn as a straight line across a 2-D projection.

    $$\text{target} = C \quad\text{for every value of the other axis}$$

    Parameters
    ----------
    boundary : Boundary
        A ``"threshold"`` boundary, for example ``"murakami_hugill"`` [-].
    axis : BoundaryQuantity
        The quantity on the other axis, which the threshold does not depend on [-].
    values : array_like
        Sample points along that axis, in its declared unit [varies].
    target_axis : str
        ``"y"`` draws a horizontal line (target on y); ``"x"`` a vertical one [-].

    Returns
    -------
    BoundaryCurve
        The line, carrying both quantities and the permitted side: ``"below"``
        / ``"above"`` for a horizontal line, ``"left"`` / ``"right"`` for a
        vertical one [-].

    Raises
    ------
    TypeError
        ``boundary`` is not a threshold, or ``values`` is not 1-D.
    ValueError
        ``target_axis`` is neither ``"x"`` nor ``"y"``, or ``axis`` is the
        boundary's own target.

    Convention
    ----------
    :func:`boundary_curve` sweeps one of a boundary's inputs, and a threshold
    has none, so this is its counterpart. The value is the registered
    coefficient; nothing is restated.
    """
    if boundary.form != "threshold":
        raise TypeError(f"boundary {boundary.key!r} is a {boundary.form!r}, not a threshold; use boundary_curve")
    if target_axis not in ("x", "y"):
        raise ValueError(f"target_axis must be 'x' or 'y', not {target_axis!r}")
    if same_quantity(axis, boundary.target):
        raise ValueError(f"the other axis cannot be the threshold's own target {boundary.target.name!r}")
    v = np.atleast_1d(np.asarray(values, dtype=float))
    if v.ndim != 1:
        raise TypeError("values must be a 1-D sequence")
    level = np.full(v.shape, float(boundary_value(boundary)))
    if target_axis == "x":
        side = {"below": "left", "above": "right"}[boundary.allowed_side]
        return BoundaryCurve(key=boundary.key, x=level, y=v, x_quantity=boundary.target, y_quantity=axis,
                             allowed_side=side)
    return BoundaryCurve(key=boundary.key, x=v, y=level, x_quantity=axis, y_quantity=boundary.target,
                         allowed_side=boundary.allowed_side)

A threshold boundary drawn as a straight line across a 2-D projection.

\[\text{target} = C \quad\text{for every value of the other axis}\]
NameTypeUnitDescription
boundaryBoundary-

A "threshold" boundary, for example "murakami_hugill".

axisBoundaryQuantity-

The quantity on the other axis, which the threshold does not depend on.

valuesarray_likevaries

Sample points along that axis, in its declared unit.

target_axisstr-

"y" draws a horizontal line (target on y); "x" a vertical one.

Returns
—BoundaryCurve-

The line, carrying both quantities and the permitted side: "below" / "above" for a horizontal line, "left" / "right" for a vertical one.

Convention

boundary_curve sweeps one of a boundary’s inputs, and a threshold has none, so this is its counterpart. The value is the registered coefficient; nothing is restated.

Raises

  • TypeError boundary is not a threshold, or values is not 1-D.
  • ValueError target_axis is neither "x" nor "y", or axis is the boundary’s own target.

results matching ""

    No results matching ""