Process reference: confinement

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

Overview

The first layer of the #548 hierarchy: from time series of plasma current, resistive loop voltage and stored energy to the loss power and confinement time of each time slice, and the evidence that decides whether a slice enters a scaling fit. It composes the definitions of vaft.formula.equilibrium and adds no physics of its own.

Two loss powers are kept apart, because databases do not agree on one:

================= =========================================== ===================== name definition compares with ================= =========================================== ===================== p_net $P_\mathrm{OH} - dW/dt$ ITPA DB5 PLTH p_transport $P_\mathrm{OH} - dW/dt - P_\mathrm{rad}$ transport analyses ================= =========================================== =====================

p_transport is NaN wherever the radiated power was not measured; a modelled $P_\mathrm{rad}$ is a sensitivity study, never the default.

Slice qualification keeps the evidence (rates and ratios per slice) apart from the decision (thresholds), as #253 asks: thresholds are arguments, never constants copied from another database, and confinement_exclusion_table reports how many slices each rule removes so a threshold sweep can be read off directly.

Functions

assess_predictor_identifiabilityCollinearity of log predictors: correlation, VIF, condition number and rank.
bootstrap_confinement_scalingGroup (shot) bootstrap of the log-linear scaling coefficients.
closure_constraintLinear constraint on I, B, P (and n) exponents fixing the Kadomtsev-completed $\mu_\rho$.
confinement_exclusion_tableHow many slices each quality rule removes, alone and in sequence.
confinement_power_balanceOhmic heating, stored-energy change, loss powers and confinement times of a discharge.
confinement_slice_decisionPass/fail of each slice-quality rule, and their conjunction, for given thresholds.
confinement_slice_evidenceRates and ratios that decide whether a time slice is quasi-stationary enough to fit.
dimensionless_confinement_indicesKadomtsev-completed dimensionless indices $(\mu_\rho, \mu_\beta, \mu_\nu, \mu_q)$ with their covariance.
fit_confinement_scalingLog-linear power-law fit $y = C \prod_j x_j^{\alpha_j}$ with errors clustered by group.
fit_confinement_scaling_odrErrors-in-variables (orthogonal distance) power-law fit in log space.
fit_constrained_confinement_scalingLog-linear power-law fit under one linear equality constraint on the exponents.
infer_kadomtsev_size_exponentSize exponent the Kadomtsev constraint implies for fitted I, B, P (and n) exponents.
leave_one_group_out_scalingLeave-one-group-out refits: coefficient stability and out-of-group prediction error.
ohmic_confinement_seriesOhmic power balance and confinement time from a loop voltage, the internal inductance and a stored energy.
predictor_principal_directionsPrincipal directions of the log predictors: which exponent combinations the data constrain.
resistive_loop_voltageInductive and resistive parts of a loop voltage, and the plasma resistance, from the internal inductance.
resolve_observed_confinementObserved confinement time with the energy basis a scaling was fitted on (issue #1713).
smoothed_time_derivativeTime derivative of a sampled quantity by a local polynomial fit, on any time grid.

assess_predictor_identifiability

Machine-independent source
assess_predictor_identifiability(predictors)

Collinearity of log predictors: correlation, VIF, condition number and rank.

NameTypeUnitDescription
predictorsMappingany

Predictor arrays keyed by name; rows with any non-finite or non-positive value are dropped.

Returns
—dictany

n rows used [-]; correlation, the Pearson matrix of the log predictors as nested dicts [-]; vif, the variance inflation factor per predictor [-]; condition_number of the column-equilibrated log design with intercept [-]; rank of that design [-]; constant, the predictors that do not vary (VIF infinite, correlation NaN) [-]

Raises

  • ValueError No predictor, or mismatched lengths.
Applicability · Limitations · Provenance

Applicability

Machine-independent.

Limitations

Says whether the sample can separate the exponents, not whether a fit did; a predictor that barely varies has a large VIF only if it is also correlated with another, so read the spread of each log predictor too. The condition number follows Belsley (columns scaled to unit length, intercept kept); values above about 30 signal harmful collinearity.

Provenance

  1. Issue #548 Sec. 3 and 6: B_T stays a fitted variable only if its covariance with I_p and P is quantified.
  2. D. A. Belsley, E. Kuh and R. E. Welsch, Regression Diagnostics, Wiley (1980), Ch. 3.

bootstrap_confinement_scaling

Machine-independent source
bootstrap_confinement_scaling(response, predictors, groups, *, n_boot=2000, seed=0)

Group (shot) bootstrap of the log-linear scaling coefficients.

NameTypeUnitDescription
responsenumpy.ndarrayany

The fitted quantity.

predictorsMappingany

Predictor arrays keyed by name.

groupsnumpy.ndarray-

Group label per row; whole groups are resampled.

n_bootint, optional-

Number of resamples.

seedint, optional-

Seed of the random generator.

Returns
—dictany

names (log_C first) [-]; samples, coefficients of every resample whose design had full rank, shape (m, k) [-]; rejected, the number of rank-deficient resamples [-]; percentile95, the 2.5 and 97.5 percentiles per coefficient [-]

Raises

  • ValueError As fit_confinement_scaling for the full sample.
Defaults · Applicability · Limitations · Provenance

Defaults

n_boot = 2000 and seed = 0: numerical convenience; 2000 resamples put the percentile endpoints within about a percent of their limit.

Applicability

Machine-independent.

Limitations

With a handful of groups the bootstrap distribution is lumpy and its intervals undercover; rejected counts resamples that could not identify every exponent (e.g. a single field value drawn), which is itself identifiability evidence.

Provenance

  1. Issue #548 Sec. 6: slices of one shot are not independent, so resampling is by shot.
  2. B. Efron and R. J. Tibshirani, An Introduction to the Bootstrap, Chapman & Hall (1993), Ch. 13.

closure_constraint

Machine-independent source
closure_constraint(mu_rho, names)
Show source vaft/process/confinement.py, lines 1776–1831
def closure_constraint(mu_rho: float, names) -> tuple[dict, float]:
    """Linear constraint on I, B, P (and n) exponents fixing the Kadomtsev-completed $\\mu_\\rho$.

    Parameters
    ----------
    mu_rho : float
        Gyroradius index of the closure: $-2$ Bohm, $-3$ gyro-Bohm, or any
        value between [-].
    names : sequence of str
        The fit's predictor names: ``i_p``, ``b_t``, ``p_net`` and optionally
        ``n_e`` [-].

    Returns
    -------
    coef : dict
        Constraint coefficient per predictor name [-].
    rhs : float
        Right-hand side, so that ``sum(coef[k] * alpha[k]) == rhs`` [-].

    Raises
    ------
    ValueError
        Unknown or missing predictor names.

    Processing steps
    ----------------
    1. Complete the size exponent with the Kadomtsev constraint,
       $\\alpha_R = (\\alpha_I + 5\\alpha_B + 3\\alpha_P + 8\\alpha_n + 5)/4$.
    2. Substitute into
       $\\mu_\\rho(1+\\alpha_P) = \\alpha_B - \\alpha_I - 2\\alpha_R + 2\\alpha_n
       - 3\\alpha_P + 1$, which leaves
       $\\tfrac32\\alpha_I + \\tfrac32\\alpha_B + (\\mu_\\rho + \\tfrac92)\\alpha_P
       + 2\\alpha_n = -(\\mu_\\rho + \\tfrac32)$.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    The closures are alternative hypotheses, not simultaneous constraints;
    the intermediate $\\mu_\\rho = -2.5$ is a single index, not an additive
    Bohm plus gyro-Bohm model (#548 Sec. 8). A density-free fit has
    $\\alpha_n = 0$ by definition, which is part of the hypothesis tested.

    Provenance
    ----------
    .. [548] Issue #548 Sec. 8: Bohm, intermediate and gyro-Bohm closures as
       one parameterized constraint.
    """
    allowed = {"i_p": 1.5, "b_t": 1.5, "p_net": float(mu_rho) + 4.5, "n_e": 2.0}
    names = list(names)
    unknown = [name for name in names if name not in allowed]
    if unknown or not {"i_p", "b_t", "p_net"} <= set(names):
        raise ValueError(f"predictors must be i_p, b_t, p_net and optionally n_e; got {names}")
    return {name: allowed[name] for name in names}, -(float(mu_rho) + 1.5)

Linear constraint on I, B, P (and n) exponents fixing the Kadomtsev-completed $\mu_\rho$.

NameTypeUnitDescription
mu_rhofloat-

Gyroradius index of the closure: $-2$ Bohm, $-3$ gyro-Bohm, or any value between.

namessequence of str-

The fit’s predictor names: i_p, b_t, p_net and optionally n_e.

Returns
coefdict-

Constraint coefficient per predictor name.

rhsfloat-

Right-hand side, so that sum(coef[k] * alpha[k]) == rhs.

Raises

  • ValueError Unknown or missing predictor names.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Complete the size exponent with the Kadomtsev constraint, $\alpha_R = (\alpha_I + 5\alpha_B + 3\alpha_P + 8\alpha_n + 5)/4$.
  2. Substitute into $\mu_\rho(1+\alpha_P) = \alpha_B - \alpha_I - 2\alpha_R + 2\alpha_n
    • 3\alpha_P + 1$, which leaves $\tfrac32\alpha_I + \tfrac32\alpha_B + (\mu_\rho + \tfrac92)\alpha_P
    • 2\alpha_n = -(\mu_\rho + \tfrac32)$.

Applicability

Machine-independent.

Limitations

The closures are alternative hypotheses, not simultaneous constraints; the intermediate $\mu_\rho = -2.5$ is a single index, not an additive Bohm plus gyro-Bohm model (#548 Sec. 8). A density-free fit has $\alpha_n = 0$ by definition, which is part of the hypothesis tested.

Provenance

  1. Issue #548 Sec. 8: Bohm, intermediate and gyro-Bohm closures as one parameterized constraint.

confinement_exclusion_table

Machine-independent source
confinement_exclusion_table(rules)
Show source vaft/process/confinement.py, lines 790–843
def confinement_exclusion_table(rules: Mapping[str, np.ndarray]) -> list[dict]:
    """How many slices each quality rule removes, alone and in sequence.

    Parameters
    ----------
    rules : Mapping
        Boolean pass arrays keyed by rule name, in the order to apply them,
        as :func:`confinement_slice_decision` returns; an ``accepted`` key
        is ignored [bool].

    Returns
    -------
    list of dict
        One row per rule: ``rule``, ``failed`` (slices failing it),
        ``failed_only_this`` (failing this rule and no other),
        ``removed_in_sequence`` (newly removed when the rules are applied in
        order) and ``remaining`` (after it) [-].

    Raises
    ------
    ValueError
        Pass arrays of different length.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [548] Issue #548 Sec. 5: the rules and the number each excludes are
       part of the result.
    """
    names = [name for name in rules if name != "accepted"]
    arrays = [np.asarray(rules[name], dtype=bool) for name in names]
    if len({a.size for a in arrays}) > 1:
        raise ValueError("every rule must judge the same slices")
    if not arrays:
        return []
    passing = np.ones(arrays[0].size, dtype=bool)
    stack = np.vstack(arrays)
    table = []
    for k, (name, passed) in enumerate(zip(names, arrays)):
        others = np.delete(stack, k, axis=0)
        others_pass = others.all(axis=0) if others.size else np.ones_like(passed)
        removed = passing & ~passed
        passing = passing & passed
        table.append({
            "rule": name,
            "failed": int(np.sum(~passed)),
            "failed_only_this": int(np.sum(~passed & others_pass)),
            "removed_in_sequence": int(np.sum(removed)),
            "remaining": int(np.sum(passing)),
        })
    return table

How many slices each quality rule removes, alone and in sequence.

NameTypeUnitDescription
rulesMappingbool

Boolean pass arrays keyed by rule name, in the order to apply them, as confinement_slice_decision returns; an accepted key is ignored.

Returns
—list of dict-

One row per rule: rule, failed (slices failing it), failed_only_this (failing this rule and no other), removed_in_sequence (newly removed when the rules are applied in order) and remaining (after it).

Raises

  • ValueError Pass arrays of different length.
Applicability · Provenance

Applicability

Machine-independent.

Provenance

  1. Issue #548 Sec. 5: the rules and the number each excludes are part of the result.

confinement_power_balance

Machine-independent source
confinement_power_balance(time, i_p, v_res, w_th, *, p_rad=None, dwdt_window_s=None, dwdt_polyorder=2)

Ohmic heating, stored-energy change, loss powers and confinement times of a discharge.

NameTypeUnitDescription
timenumpy.ndarrays

Sample times, strictly increasing, 1-D.

i_pnumpy.ndarrayA

Plasma current, in the orientation of v_res.

v_resnumpy.ndarrayV

Resistive loop voltage, the loop voltage with the inductive part removed.

w_thnumpy.ndarrayJ

Stored (thermal) energy whose change and confinement are wanted.

p_radnumpy.ndarray, optionalW

Measured radiated power; None (or NaN samples) leaves the transport loss power undefined there.

dwdt_window_sfloat, optionals

Window of the local-polynomial $dW/dt$ derivative, see smoothed_time_derivative.

dwdt_polyorderint, optional-

Polynomial order of that derivative.

Returns
—ConfinementPowerBalanceany

p_ohmic, dwdt, p_net and p_transport [W]; tau_e_net and tau_e_transport [s]; time [s].

Raises

  • ValueError Arrays of different length, or what smoothed_time_derivative rejects.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. $P_\mathrm{OH} = I_p V_\mathrm{res}$ (vaft.formula.equilibrium.ohmic_heating_power_from_I_p_V_res).
  2. $dW/dt$ by smoothed_time_derivative on w_th.
  3. $P_\mathrm{net} = P_\mathrm{OH} - dW/dt$ and, where p_rad is finite, $P_\mathrm{transport} = P_\mathrm{net} - P_\mathrm{rad}$ (vaft.formula.equilibrium.loss_power_from_p_heat_dWdt_p_rad).
  4. $\tau_E = W/P$ for each loss power, NaN where that power is not positive.

Applicability

Machine-independent. Ohmic plasmas: no auxiliary heating term.

Limitations

v_res carries every assumption of how the inductive voltage was removed; with a loop voltage taken away from the plasma boundary the flux between loop and boundary is still inductive. The transport loss power needs a measured radiated power, which VEST does not map (no bolometer), so on VEST it is NaN.

Provenance

  1. Issue #548 Sec. 1: P_net and P_transport as separate, named quantities; W = (3/2) int p dV fixed by #1282/#1410.
  2. ITER Physics Expert Groups, Nucl. Fusion 39 (1999) 2175, Ch. 2: the loss power $P_L = P_\mathrm{heat} - dW/dt$ of the confinement database.

confinement_slice_decision

Machine-independent source
confinement_slice_decision(evidence, *, ip_min, max_dwdt_fraction, max_ip_change_per_tau)
Show source vaft/process/confinement.py, lines 730–787
def confinement_slice_decision(
    evidence: ConfinementSliceEvidence,
    *,
    ip_min: float,
    max_dwdt_fraction: float,
    max_ip_change_per_tau: float,
) -> dict[str, np.ndarray]:
    """Pass/fail of each slice-quality rule, and their conjunction, for given thresholds.

    Parameters
    ----------
    evidence : ConfinementSliceEvidence
        Output of :func:`confinement_slice_evidence` [any].
    ip_min : float
        Smallest accepted $|I_p|$ [A].
    max_dwdt_fraction : float
        Largest accepted $|dW/dt|/P_\\mathrm{OH}$ [-].
    max_ip_change_per_tau : float
        Largest accepted fractional current change within one confinement
        time, $\\tau_E |dI_p/dt|/|I_p|$ [-].

    Returns
    -------
    dict
        Boolean arrays keyed ``finite``, ``ip_min``, ``dwdt_fraction``,
        ``ip_change_per_tau`` and ``accepted`` (all rules); a NaN evidence
        value fails its rule [bool].

    Applicability
    -------------
    Machine-independent. The thresholds are the caller's: none is a
    database or machine default.

    Limitations
    -----------
    Ramp phases are judged by rate against the confinement time, not by a
    phase label: a short discharge with no flat top (VEST) has no plateau to
    select, and a rate rule says how far from stationary each slice is.

    Provenance
    ----------
    .. [548] Issue #548 Sec. 5: thresholds as arguments, studied by sweep
       before any is adopted.
    """
    ev = evidence
    rules = {
        "finite": np.asarray(ev.finite, dtype=bool),
        "ip_min": np.nan_to_num(ev.ip_abs, nan=-np.inf) >= float(ip_min),
        "dwdt_fraction": np.nan_to_num(ev.dwdt_fraction, nan=np.inf) <= float(max_dwdt_fraction),
        "ip_change_per_tau": (
            np.nan_to_num(ev.ip_change_per_tau, nan=np.inf) <= float(max_ip_change_per_tau)
        ),
    }
    accepted = np.ones_like(rules["finite"])
    for passed in rules.values():
        accepted = accepted & passed
    rules["accepted"] = accepted
    return rules

Pass/fail of each slice-quality rule, and their conjunction, for given thresholds.

NameTypeUnitDescription
evidenceConfinementSliceEvidenceany

Output of confinement_slice_evidence.

ip_minfloatA
Smallest accepted $ I_p $.
max_dwdt_fractionfloat-
Largest accepted $ dW/dt /P_\mathrm{OH}$.
max_ip_change_per_taufloat-
Largest accepted fractional current change within one confinement time, $\tau_E dI_p/dt / I_p $.
Returns
—dictbool

Boolean arrays keyed finite, ip_min, dwdt_fraction, ip_change_per_tau and accepted (all rules); a NaN evidence value fails its rule.

Applicability · Limitations · Provenance

Applicability

Machine-independent. The thresholds are the caller’s: none is a database or machine default.

Limitations

Ramp phases are judged by rate against the confinement time, not by a phase label: a short discharge with no flat top (VEST) has no plateau to select, and a rate rule says how far from stationary each slice is.

Provenance

  1. Issue #548 Sec. 5: thresholds as arguments, studied by sweep before any is adopted.

confinement_slice_evidence

Machine-independent source
confinement_slice_evidence(i_p, dip_dt, w_th, p_ohmic, dwdt, tau_e)
Show source vaft/process/confinement.py, lines 664–727
def confinement_slice_evidence(
    i_p: np.ndarray,
    dip_dt: np.ndarray,
    w_th: np.ndarray,
    p_ohmic: np.ndarray,
    dwdt: np.ndarray,
    tau_e: np.ndarray,
) -> ConfinementSliceEvidence:
    """Rates and ratios that decide whether a time slice is quasi-stationary enough to fit.

    Parameters
    ----------
    i_p : numpy.ndarray
        Plasma current at the slices [A].
    dip_dt : numpy.ndarray
        Its time derivative at the slices [A/s].
    w_th : numpy.ndarray
        Stored energy at the slices [J].
    p_ohmic : numpy.ndarray
        Ohmic heating power at the slices [W].
    dwdt : numpy.ndarray
        Stored-energy change at the slices [W].
    tau_e : numpy.ndarray
        Confinement time at the slices [s].

    Returns
    -------
    ConfinementSliceEvidence
        ``ip_abs`` [A], ``ip_rate`` [1/s], ``ip_change_per_tau`` and
        ``dwdt_fraction`` [-], ``finite`` [bool] [any].

    Raises
    ------
    ValueError
        Arrays of different length.

    Applicability
    -------------
    Machine-independent.

    Provenance
    ----------
    .. [548] Issue #548 Sec. 5: the slice-quality evidence, kept apart from
       the acceptance decision as #253 asks.
    """
    ip = _series("i_p", i_p)
    n = ip.size
    didt = _series("dip_dt", dip_dt, n)
    w = _series("w_th", w_th, n)
    p = _series("p_ohmic", p_ohmic, n)
    dw = _series("dwdt", dwdt, n)
    tau = _series("tau_e", tau_e, n)
    ip_abs = np.abs(ip)
    with np.errstate(divide="ignore", invalid="ignore"):
        ip_rate = np.abs(didt) / ip_abs
        dw_frac = np.abs(dw) / p
    dw_frac = np.where(p > 0, dw_frac, np.nan)
    finite = (
        np.isfinite(w) & (w > 0) & np.isfinite(p) & (p > 0) & np.isfinite(tau) & (tau > 0)
    )
    return ConfinementSliceEvidence(
        ip_abs=ip_abs, ip_rate=ip_rate, ip_change_per_tau=ip_rate * tau,
        dwdt_fraction=dw_frac, finite=finite,
    )

Rates and ratios that decide whether a time slice is quasi-stationary enough to fit.

NameTypeUnitDescription
i_pnumpy.ndarrayA

Plasma current at the slices.

dip_dtnumpy.ndarrayA/s

Its time derivative at the slices.

w_thnumpy.ndarrayJ

Stored energy at the slices.

p_ohmicnumpy.ndarrayW

Ohmic heating power at the slices.

dwdtnumpy.ndarrayW

Stored-energy change at the slices.

tau_enumpy.ndarrays

Confinement time at the slices.

Returns
—ConfinementSliceEvidenceany

ip_abs [A], ip_rate [1/s], ip_change_per_tau and dwdt_fraction [-], finite [bool].

Raises

  • ValueError Arrays of different length.
Applicability · Provenance

Applicability

Machine-independent.

Provenance

  1. Issue #548 Sec. 5: the slice-quality evidence, kept apart from the acceptance decision as #253 asks.

dimensionless_confinement_indices

Machine-independent source
dimensionless_confinement_indices(alpha, cov)
Show source vaft/process/confinement.py, lines 1694–1773
def dimensionless_confinement_indices(
    alpha: Mapping[str, float],
    cov: np.ndarray,
) -> dict:
    """Kadomtsev-completed dimensionless indices $(\\mu_\\rho, \\mu_\\beta, \\mu_\\nu, \\mu_q)$ with their covariance.

    Parameters
    ----------
    alpha : Mapping
        Fitted exponents keyed ``i_p``, ``b_t``, ``p_net`` and optionally
        ``n_e`` [-].
    cov : numpy.ndarray
        Their covariance in the order of ``alpha`` [-].

    Returns
    -------
    dict
        ``alpha_R`` and ``alpha_R_stderr``, the completed size exponent [-];
        ``names`` (``mu_rho``, ``mu_beta``, ``mu_nu``, ``mu_q``) [-];
        ``value`` and ``cov`` of the indices [-]; ``stderr`` [-] [any].

    Raises
    ------
    ValueError
        As :func:`infer_kadomtsev_size_exponent`, or $\\alpha_P = -1$.

    Processing steps
    ----------------
    1. Complete the size exponent with :func:`infer_kadomtsev_size_exponent`.
    2. Map the completed engineering exponents to dimensionless indices with
       :func:`vaft.formula.equilibrium.dimensionless_scaling_coeffs_from_engineering_scaling_coeffs`
       (exact here: a completed scaling satisfies the constraint), and
       $\\mu_q = -\\alpha_I/(1+\\alpha_P)$.
    3. Propagate ``cov`` through the map's Jacobian (central differences,
       the size exponent completed at every evaluation).

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    Inherits the *assumed* size exponent: the indices are those of the
    Kadomtsev-completed scaling, not measured dimensionless dependences. The
    linear propagation is poor when $1 + \\alpha_P$ is within a few standard
    errors of zero, where every index diverges.

    Provenance
    ----------
    .. [548] Issue #548 Sec. 7 (layer E): dimensionless conversion with
       uncertainties and covariances.
    .. [Luce] T. C. Luce, C. C. Petty and J. G. Cordey, *Plasma Phys. Control.
       Fusion* **50** (2008) 043001.
    """
    from vaft.formula.equilibrium import (
        dimensionless_scaling_coeffs_from_engineering_scaling_coeffs as _to_dimensionless,
    )

    names = list(alpha)
    a0 = np.array([float(alpha[name]) for name in names])
    alpha_r, alpha_r_se = infer_kadomtsev_size_exponent(alpha, cov)

    def indices(vec):
        a = dict(zip(names, vec))
        a_r, _ = infer_kadomtsev_size_exponent(a, np.zeros((len(names), len(names))))
        mu = _to_dimensionless(a["i_p"], a["b_t"], a["p_net"], a.get("n_e", 0.0), 0.0, a_r, 0.0, 0.0)
        return np.array([mu[0], mu[1], mu[2], -a["i_p"] / (1.0 + a["p_net"])])

    value = indices(a0)
    jac = np.zeros((4, len(names)))
    for j in range(len(names)):
        h = 1e-6 * max(1.0, abs(a0[j]))
        up, down = a0.copy(), a0.copy()
        up[j] += h
        down[j] -= h
        jac[:, j] = (indices(up) - indices(down)) / (2 * h)
    c = jac @ np.atleast_2d(np.asarray(cov, dtype=float)) @ jac.T
    return {"alpha_R": alpha_r, "alpha_R_stderr": alpha_r_se,
            "names": ("mu_rho", "mu_beta", "mu_nu", "mu_q"), "value": value, "cov": c,
            "stderr": np.sqrt(np.clip(np.diag(c), 0.0, None))}

Kadomtsev-completed dimensionless indices $(\mu_\rho, \mu_\beta, \mu_\nu, \mu_q)$ with their covariance.

NameTypeUnitDescription
alphaMapping-

Fitted exponents keyed i_p, b_t, p_net and optionally n_e.

covnumpy.ndarray-

Their covariance in the order of alpha.

Returns
—dictany

alpha_R and alpha_R_stderr, the completed size exponent [-]; names (mu_rho, mu_beta, mu_nu, mu_q) [-]; value and cov of the indices [-]; stderr [-].

Raises

  • ValueError As infer_kadomtsev_size_exponent, or $\alpha_P = -1$.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Complete the size exponent with infer_kadomtsev_size_exponent.
  2. Map the completed engineering exponents to dimensionless indices with vaft.formula.equilibrium.dimensionless_scaling_coeffs_from_engineering_scaling_coeffs (exact here: a completed scaling satisfies the constraint), and $\mu_q = -\alpha_I/(1+\alpha_P)$.
  3. Propagate cov through the map’s Jacobian (central differences, the size exponent completed at every evaluation).

Applicability

Machine-independent.

Limitations

Inherits the assumed size exponent: the indices are those of the Kadomtsev-completed scaling, not measured dimensionless dependences. The linear propagation is poor when $1 + \alpha_P$ is within a few standard errors of zero, where every index diverges.

Provenance

  1. Issue #548 Sec. 7 (layer E): dimensionless conversion with uncertainties and covariances.
  2. T. C. Luce, C. C. Petty and J. G. Cordey, Plasma Phys. Control. Fusion 50 (2008) 043001.

fit_confinement_scaling

Machine-independent source
fit_confinement_scaling(response, predictors, groups, *, robust=False)

Log-linear power-law fit $y = C \prod_j x_j^{\alpha_j}$ with errors clustered by group.

NameTypeUnitDescription
responsenumpy.ndarrayany

The fitted quantity, usually $\tau_E$ or $W$.

predictorsMappingany

Predictor arrays keyed by name, same length as response.

groupsnumpy.ndarray-

Group label per row, normally the shot: rows of one group are not independent samples.

robustbool, optionalbool

Huber M-estimation instead of least squares for the coefficients

Returns
—ConfinementScalingFitany

Coefficients in log space (exponents, and $\ln C$ first) [-], their cluster-robust and naive errors, 95 % intervals, $R^2$ and the RMS log residual [-], leverage and Cook’s distance per kept row [-].

Raises

  • ValueError No predictor, mismatched lengths, fewer kept rows than coefficients plus one, fewer than two groups, a predictor that does not vary over the kept rows, or an exactly collinear design: a least-squares solver would otherwise return an arbitrary exponent with a confident error.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Keep rows where the response and every predictor are finite and positive; take logs and prepend an intercept column.
  2. Fit by ordinary least squares (or Huber IRLS with robust).
  3. Covariance clustered by groups (statsmodels cov_type="cluster", CR1 small-sample correction); 95 % intervals from Student’s t with $G - 1$ degrees of freedom.
  4. Leverage and Cook’s distance from the least-squares influence of the same design (also for a robust fit, as a diagnostic).

With robust the cluster covariance is that of a weighted fit with the converged Huber weights held fixed, which slightly understates the error when rows are down-weighted; stderr_iid is then the Huber fit’s own.

Applicability

Machine-independent.

Limitations

With few groups the cluster-robust error is itself noisy (it has $G - 1$ degrees of freedom); read it together with the group bootstrap and leave-one-group-out results. Errors in the predictors are ignored, so exponents are attenuated where a predictor is noisy; with $\tau_E = W/P$ and $P$ a predictor, the $P$ exponent is biased towards $-1$ by any error in $P$.

Provenance

  1. Issue #548 Sec. 6: covariance, intervals, influence and robust regression with grouped samples.
  2. A. C. Cameron and D. L. Miller, J. Human Resources 50 (2015) 317: cluster-robust inference and the few-clusters problem.

fit_confinement_scaling_odr

Machine-independent source
fit_confinement_scaling_odr(response, predictors, *, sigma_log_response, sigma_log_predictors)

Errors-in-variables (orthogonal distance) power-law fit in log space.

NameTypeUnitDescription
responsenumpy.ndarrayany

The fitted quantity. For the $\tau_E = W/P$ coupling, fit $W$, not $\tau_E$, so its error is independent of the power’s.

predictorsMappingany

Predictor arrays keyed by name.

sigma_log_responsefloat or array-like-

Standard deviation of the equation error of $\ln y$: measurement error plus the scaling’s intrinsic scatter, not measurement error alone. One number for every row, or one per input row (e.g. a reconstruction’s model-form spread, #579); a row whose value is not finite and positive is dropped.

sigma_log_predictorsMapping-

Standard deviation of the measurement error of $\ln x_j$ per predictor; zero marks a predictor as exact.

Returns
—dictany

names (log_C first) [-]; coef, the errors-in-variables coefficients [-]; n rows used [-]; mask of the rows used [bool]; ols_coef, the least-squares coefficients of the same data [-]; method, "partial_tls" for one response error or "weighted_odr" for per-row errors [-].

Raises

  • ValueError No predictor, mismatched lengths, a non-positive response error, a negative or non-finite or missing predictor error, no noisy predictor, or fewer rows than coefficients plus one.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Keep rows where everything is finite and positive; take logs.
  2. Partial the exact predictors (zero error) and the intercept out of the response and the noisy predictors by least squares.
  3. Scale each residual column by its error and take the right singular vector $v$ of the smallest singular value (total least squares); the noisy slopes are $b_j = -(v_j/\sigma_j)/(v_y/\sigma_y)$.
  4. The exact predictors’ slopes and the intercept by least squares of $y - \sum_{j\in\mathrm{noisy}} b_j x_j$ on them.
  5. With per-row response errors steps 2-4 have no closed form: the same linear model is solved as weighted orthogonal distance regression: the maximum-likelihood criterion of a linear model with independent Gaussian errors, $\sum_i (y_i - \beta\cdot x_i)^2 / (\sigma_{y,i}^2 + \sum_j \beta_j^2\sigma_{x,j}^2)$, minimised from the least-squares start (exact predictors carry $\sigma = 0$). For a constant per-row error it reproduces steps 2-4.

Applicability

Machine-independent.

Limitations

The answer depends only on the assumed error ratios, so report it as a function of them. Understating the equation error of the response over-corrects the attenuation and inflates the noisy slopes, the classic pitfall of errors-in-variables confinement fits. Errors are assumed independent between variables. Fitting $\tau_E = W/P$ against $P$ violates that, which is why the response should be $W$ (its $P$ exponent is $1 + \alpha_P$). No standard error is returned: rows of one shot are not independent, so use a shot bootstrap around this function.

Provenance

  1. Issue #548 Sec. 6: errors-in-variables regression, and the coupling from $\tau_E = W/P$ with $P$ a predictor.
  2. S. Van Huffel and J. Vandewalle, The Total Least Squares Problem, SIAM (1991), Ch. 3: mixed exact/noisy columns (partial TLS); for a linear model with known error ratios it is the orthogonal distance (maximum-likelihood) solution.

fit_constrained_confinement_scaling

Machine-independent source
fit_constrained_confinement_scaling(response, predictors, groups, constraint, rhs)

Log-linear power-law fit under one linear equality constraint on the exponents.

NameTypeUnitDescription
responsenumpy.ndarrayany

The fitted quantity, usually $\tau_E$.

predictorsMappingany

Predictor arrays keyed by name.

groupsnumpy.ndarray-

Group label per row (the shot).

constraintMapping-

Coefficient per predictor name; omitted predictors (and the intercept) enter with zero.

rhsfloat-

Right-hand side of sum(constraint[k] * alpha[k]) == rhs.

Returns
—ConfinementScalingFitany

As fit_confinement_scaling, with the constrained coefficients, the cluster covariance projected onto the constraint (singular along it) and Student-t intervals with $G - 1$ degrees of freedom.

Raises

  • ValueError As fit_confinement_scaling, or a constraint naming an unknown predictor or with all-zero coefficients.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. The unconstrained least-squares fit and its clustered covariance $V$ (fit_confinement_scaling).
  2. Restricted least squares, $\beta_c = \beta - (X^\top X)^{-1}c^\top(c(X^\top X)^{-1}c^\top)^{-1}(c\beta - r)$.
  3. Covariance $MVM^\top$ with $M = I - (X^\top X)^{-1}c^\top(c(X^\top X)^{-1}c^\top)^{-1}c$.
  4. Residuals, $R^2$, the restricted leverage $H - XAc^\top(cAc^\top)^{-1}cAX^\top$ ($A = (X^\top X)^{-1}$) and Cook’s distance with $k - 1$ free coefficients; stderr_iid from $s^2 MAM^\top$ with the constrained residual variance.

Applicability

Machine-independent.

Limitations

Least squares only (no robust option).

Provenance

  1. Issue #548 Sec. 8: closures fitted as constraints and compared.
  2. W. H. Greene, Econometric Analysis, 7th ed., Pearson (2012), Sec. 5.5: restricted least squares.

infer_kadomtsev_size_exponent

Machine-independent source
infer_kadomtsev_size_exponent(alpha, cov)
Show source vaft/process/confinement.py, lines 1635–1691
def infer_kadomtsev_size_exponent(
    alpha: Mapping[str, float],
    cov: np.ndarray,
) -> tuple[float, float]:
    """Size exponent the Kadomtsev constraint implies for fitted I, B, P (and n) exponents.

    Parameters
    ----------
    alpha : Mapping
        Fitted exponents keyed ``i_p``, ``b_t``, ``p_net`` and, optionally,
        ``n_e`` (absent means zero by model definition, not measured) [-].
    cov : numpy.ndarray
        Their covariance, rows and columns in the order of ``alpha`` [-].

    Returns
    -------
    alpha_R : float
        $\\alpha_R^{\\mathrm{KCT}} = \\tfrac14\\alpha_I + \\tfrac54\\alpha_B
        + \\tfrac34\\alpha_P + 2\\alpha_n + \\tfrac54$ [-].
    stderr : float
        Its standard error, $\\sqrt{g^\\top C g}$ with $g$ the coefficients
        above [-].

    Raises
    ------
    ValueError
        A missing or unknown exponent name, or a covariance of the wrong
        shape.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    An *assumed* size exponent, not a measured one: one machine's shot-to-shot
    radius changes are not a size scan, and a Kadomtsev-completed scaling
    satisfies the constraint by construction, so a zero residual is no
    evidence for it. Label every use accordingly (#548 Sec. 7).

    Provenance
    ----------
    .. [548] Issue #548 Sec. 7: the Connor-Taylor/Kadomtsev completion and its
       covariance propagation.
    .. [Kadomtsev] B. B. Kadomtsev, *Sov. J. Plasma Phys.* **1** (1975) 295.
    """
    weights = {"i_p": 0.25, "b_t": 1.25, "p_net": 0.75, "n_e": 2.0}
    names = list(alpha)
    unknown = [name for name in names if name not in weights]
    if unknown or not {"i_p", "b_t", "p_net"} <= set(names):
        raise ValueError(f"exponents must be i_p, b_t, p_net and optionally n_e; got {names}")
    c = np.atleast_2d(np.asarray(cov, dtype=float))
    if c.shape != (len(names), len(names)):
        raise ValueError(f"cov must be {len(names)}x{len(names)}, got {c.shape}")
    g = np.array([weights[name] for name in names])
    value = float(g @ np.array([float(alpha[name]) for name in names]) + 1.25)
    return value, float(np.sqrt(max(g @ c @ g, 0.0)))

Size exponent the Kadomtsev constraint implies for fitted I, B, P (and n) exponents.

NameTypeUnitDescription
alphaMapping-

Fitted exponents keyed i_p, b_t, p_net and, optionally, n_e (absent means zero by model definition, not measured).

covnumpy.ndarray-

Their covariance, rows and columns in the order of alpha.

Returns
alpha_Rfloat-

$\alpha_R^{\mathrm{KCT}} = \tfrac14\alpha_I + \tfrac54\alpha_B + \tfrac34\alpha_P + 2\alpha_n + \tfrac54$.

stderrfloat-

Its standard error, $\sqrt{g^\top C g}$ with $g$ the coefficients above.

Raises

  • ValueError A missing or unknown exponent name, or a covariance of the wrong shape.
Applicability · Limitations · Provenance

Applicability

Machine-independent.

Limitations

An assumed size exponent, not a measured one: one machine’s shot-to-shot radius changes are not a size scan, and a Kadomtsev-completed scaling satisfies the constraint by construction, so a zero residual is no evidence for it. Label every use accordingly (#548 Sec. 7).

Provenance

  1. Issue #548 Sec. 7: the Connor-Taylor/Kadomtsev completion and its covariance propagation.
  2. B. B. Kadomtsev, Sov. J. Plasma Phys. 1 (1975) 295.

leave_one_group_out_scaling

Machine-independent source
leave_one_group_out_scaling(response, predictors, groups)
Show source vaft/process/confinement.py, lines 1421–1483
def leave_one_group_out_scaling(
    response: np.ndarray,
    predictors: Mapping[str, np.ndarray],
    groups: np.ndarray,
) -> dict:
    """Leave-one-group-out refits: coefficient stability and out-of-group prediction error.

    Parameters
    ----------
    response : numpy.ndarray
        The fitted quantity [any].
    predictors : Mapping
        Predictor arrays keyed by name [any].
    groups : numpy.ndarray
        Group label per row; each group is held out in turn [-].

    Returns
    -------
    dict
        ``names`` (``log_C`` first) [-]; ``groups``, the held-out labels [-];
        ``coef``, the refit coefficients per held-out group, NaN where the
        rest could not identify them, shape ``(G, k)`` [-]; ``log_error``, the
        log prediction error of every kept row when its group was held out
        [-]; ``rmse_log``, its RMS over rows [-] [any].

    Raises
    ------
    ValueError
        As :func:`fit_confinement_scaling` for the full sample.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    A held-out shot whose predictor values lie outside the others' range
    tests extrapolation, not interpolation; the prediction error of such a
    group dominates ``rmse_log``.

    Provenance
    ----------
    .. [548] Issue #548 Sec. 6 and 8: leave-one-shot-out error as a model
       comparison criterion.
    """
    names, log_y, x, g, _ = _log_design(response, predictors, groups)
    labels, codes = np.unique(g, return_inverse=True)
    k = x.shape[1]
    coef = np.full((labels.size, k), np.nan)
    error = np.full(log_y.size, np.nan)
    for i in range(labels.size):
        held = codes == i
        xs, ys = x[~held], log_y[~held]
        if xs.shape[0] <= k or np.linalg.matrix_rank(xs) < k:
            continue
        beta, *_ = np.linalg.lstsq(xs, ys, rcond=None)
        coef[i] = beta
        error[held] = log_y[held] - x[held] @ beta
    finite = np.isfinite(error)
    return {
        "names": ("log_C", *names), "groups": labels, "coef": coef, "log_error": error,
        "rmse_log": float(np.sqrt(np.mean(error[finite] ** 2))) if finite.any() else np.nan,
    }

Leave-one-group-out refits: coefficient stability and out-of-group prediction error.

NameTypeUnitDescription
responsenumpy.ndarrayany

The fitted quantity.

predictorsMappingany

Predictor arrays keyed by name.

groupsnumpy.ndarray-

Group label per row; each group is held out in turn.

Returns
—dictany

names (log_C first) [-]; groups, the held-out labels [-]; coef, the refit coefficients per held-out group, NaN where the rest could not identify them, shape (G, k) [-]; log_error, the log prediction error of every kept row when its group was held out [-]; rmse_log, its RMS over rows [-].

Raises

  • ValueError As fit_confinement_scaling for the full sample.
Applicability · Limitations · Provenance

Applicability

Machine-independent.

Limitations

A held-out shot whose predictor values lie outside the others’ range tests extrapolation, not interpolation; the prediction error of such a group dominates rmse_log.

Provenance

  1. Issue #548 Sec. 6 and 8: leave-one-shot-out error as a model comparison criterion.

ohmic_confinement_series

Machine-independent source
ohmic_confinement_series(time, i_p, dip_dt, v_loop, li_3, r0, w, *, rate_window_s, rate_polyorder=1, p_rad=None, hold_li3_where_missing=True, assumptions=None)

Ohmic power balance and confinement time from a loop voltage, the internal inductance and a stored energy.

NameTypeUnitDescription
timenumpy.ndarrays

Times of the samples, strictly increasing; the slices an energy rate may be taken across, so only validated equilibrium slices.

i_pnumpy.ndarrayA

Plasma current at time, in the orientation of v_loop.

dip_dtnumpy.ndarrayA/s

Its time derivative, from the current record (finer than time).

v_loopnumpy.ndarrayV

Loop voltage at time, measured or flux-derived (say which in assumptions).

li_3numpy.ndarray-

Normalised internal inductance $l_{i,3}$ at time.

r0floatm

Major radius normalising $l_{i,3}$, not the instantaneous $R_\mathrm{geo}$.

wnumpy.ndarrayJ

Stored energy whose rate and confinement are wanted; its definition (EFIT pressure integral, kinetic, global) belongs in assumptions.

rate_window_sfloats

Full width of the local-polynomial window of $dl_{i,3}/dt$ and $dW/dt$.

rate_polyorderint, optional-

Degree of that local polynomial.

p_radnumpy.ndarray, optionalW

Measured radiated power; None leaves p_transport NaN.

hold_li3_where_missingbool, optional-

Hold the inductance fixed where the window holds no $l_{i,3}$ rate, so v_res, r_p and p_ohmic survive (flagged in li3_rate_fallback); False leaves them NaN there.

assumptionsMapping[str, str], optional-

Caller’s physical choices to record (voltage source, energy definition, current source); merged over the defaults.

Returns
—OhmicConfinementany

Every input, rate, voltage term, power and confinement time on time, with missing_reason, settings and assumptions.

Raises

  • ValueError Arrays of different length, a non-increasing time axis, a non-positive r0 or window, or rate_polyorder below 1.
Processing steps · Defaults · Applicability · Limitations · Provenance

Processing steps

  1. $dl_{i,3}/dt$ by smoothed_time_derivative over rate_window_s (NaN with fewer than two samples).
  2. resistive_loop_voltage: $V_\mathrm{ind} = L_i\,dI_p/dt + \tfrac14\mu_0 R_0 I_p\,dl_{i,3}/dt$, $V_\mathrm{res} = V_\mathrm{loop} - V_\mathrm{ind}$, $R_p = V_\mathrm{res}/I_p$.
  3. confinement_power_balance: $P_\mathrm{OH} = I_p V_\mathrm{res}$, $dW/dt$ over the same window, $P_\mathrm{net} = P_\mathrm{OH} - dW/dt$, $P_\mathrm{transport} = P_\mathrm{net} - P_\mathrm{rad}$, $\tau_E = W/P$ where that power is positive.
  4. With a single sample no rate exists: $P_\mathrm{OH}$ is kept (inductance held fixed), $dW/dt$ and everything built on it are NaN. A missing energy rate is never replaced by zero.

Defaults

rate_polyorder = 1 and hold_li3_where_missing = True are the VEST Tier A reference settings of #548 (3 ms holds about three EFIT slices), numerical choices recorded in settings, not physical constants.

Applicability

Machine-independent. Ohmic plasmas: no auxiliary heating and no non-inductive current (with one, $R_p$ needs $I_p - I_\mathrm{ni}$).

Limitations

v_res is inferred, and exact only for a loop on the plasma boundary (recorded in assumptions['v_res']). Holding $l_{i,3}$ fixed is a numerical fallback, not evidence of a stationary current profile. Smoothing windows bias rates that change within them.

Provenance

  1. J. A. Romero et al., Nucl. Fusion 50 (2010) 115002, eq. 24.
  2. Issue #548 Sec. 1 and workflow/confinement_scaling: the VEST Tier A reference chain this function was moved out of.
  3. Issue #1905 Sec. 3: the shared confinement contract and its required result fields.

predictor_principal_directions

Machine-independent source
predictor_principal_directions(predictors, *, groups=None)

Principal directions of the log predictors: which exponent combinations the data constrain.

NameTypeUnitDescription
predictorsMappingany

Predictor arrays keyed by name; rows with any non-finite or non-positive value are dropped.

groupsarray-like, optionalany

Group label per row (e.g. shot); when given, the analysis is repeated on the shot means (between-group) and on the within-group deviations

Returns
—dictany

names [-]; n rows used [-]; scale, the standard deviation of each log predictor [-]; singular_values of the centred, standardised log design, largest first [-]; directions, the unit right-singular vectors as rows, one per singular value, in that design’s coordinates [-]; variance_fraction per direction [-]; effective_rank, the number of directions whose singular value is at least 0.1 of the largest [-]; and, with groups, between and within: the spread of the group means and of the within-group deviations along the same directions, in the same standardised units, as root-sum-square projections over the rows, so that between**2 + within**2 is each singular value squared [-].

Raises

  • ValueError No predictor, mismatched lengths (groups included), fewer than three usable rows, or a predictor that does not vary.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Take logs, centre and divide each column by its standard deviation.
  2. Singular-value decompose; a direction with a small singular value is a combination of predictors the sample barely varies, so the matching combination of exponents is poorly determined (PCR diagnostic). With fewer rows than predictors the missing directions are returned with singular value zero: they are the unconstrained ones.
  3. With groups, split each direction’s spread into the part carried by the group means (each row taking its group’s mean) and the part carried by the within-group deviations, both with the pooled centring and scale.

Applicability

Machine-independent.

Limitations

Directions are in standardised log coordinates; a direction mixes exponents in units of each predictor’s spread, so convert with scale before reading it as a physical scan. It diagnoses the design, not a fit: it says which scan would add information, not which exponent is right.

Provenance

  1. Issue #1621 Secs. 8 and 12: PCR-style identifiability and the weak singular directions that point to the next scan.
  2. D. A. Belsley, E. Kuh and R. E. Welsch, Regression Diagnostics, Wiley (1980), Ch. 3.

resistive_loop_voltage

Machine-independent source
resistive_loop_voltage(v_loop, i_p, dip_dt, li_3, r0, *, dli3_dt=None, hold_li3_where_missing=False)

Inductive and resistive parts of a loop voltage, and the plasma resistance, from the internal inductance.

NameTypeUnitDescription
v_loopnumpy.ndarrayV

Loop voltage, in the orientation of i_p.

i_pnumpy.ndarrayA

Plasma current.

dip_dtnumpy.ndarrayA/s

Its time derivative.

li_3numpy.ndarray-

Normalised internal inductance $l_{i,3}$.

r0floatm

Major radius normalising $l_{i,3}$.

dli3_dtnumpy.ndarray, optional1/s

Time derivative of $l_{i,3}$; None holds the inductance fixed, so only $L_i\,dI_p/dt$ is removed.

hold_li3_where_missingbool, optional-

Where dli3_dt is NaN, hold the inductance fixed there (profile term zero, li3_rate_fallback True) instead of returning NaN voltages.

Returns
—ResistiveLoopVoltageany

v_ind, the inductive voltage $(1/I_p)\,dW_\mathrm{int}/dt$, and v_res = v_loop - v_ind [V]; r_p = v_res / i_p, the plasma resistance seen by the total current (NaN where $I_p = 0$) [Ohm]; l_int, the internal inductance $\mu_0 R_0 l_{i,3}/2$ [H]; v_ind_current and v_ind_profile, the two terms of v_ind [V]; li3_rate_fallback, True where the inductance was held fixed (see ResistiveLoopVoltage) [-].

Raises

  • ValueError Arrays of different length or a non-positive r0.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. $L_i = \mu_0 R_0 l_{i,3}/2$, so $W_\mathrm{int} = L_i I_p^2/2$.
  2. $V_\mathrm{ind} = (1/I_p)\,dW_\mathrm{int}/dt = L_i\,dI_p/dt + \tfrac14\mu_0 R_0 I_p\,dl_{i,3}/dt$, the second term only where dli3_dt is given and finite (or, with hold_li3_where_missing, zero where it is not).
  3. $V_\mathrm{res} = V_\mathrm{loop} - V_\mathrm{ind}$ and $R_p = V_\mathrm{res}/I_p$.

Applicability

Machine-independent.

Limitations

Exact (Romero’s eq. 24) only for the loop voltage at the plasma boundary. A flux loop away from the boundary also sees the external inductive flux between loop and boundary, which this does not remove.

Provenance

  1. J. A. Romero et al., Nucl. Fusion 50 (2010) 115002, eq. 24: the inductive voltage of the internal poloidal-field energy.
  2. Issue #548 Sec. 1: P_OH from the loop voltage with the inductive part removed, the Spitzer path only for comparison (#1188).

resolve_observed_confinement

Machine-independent source
resolve_observed_confinement(tau_thermal, energy_basis, *, tau_global=None, thermal_as_global=False)

Observed confinement time with the energy basis a scaling was fitted on (issue #1713).

NameTypeUnitDescription
tau_thermalarray-likes

Thermal confinement time $W_{th}/P$ per row, NaN where unknown.

energy_basisstrstr

The scaling’s basis, from vaft.formula.equilibrium.confinement_scaling_basis: "thermal", "global" or "unaudited".

tau_globalarray-like, optionals

Global confinement time $W/P$ per row, fast ions included; default none observed.

thermal_as_globalbool, optional-

Use the thermal time where a global (or unaudited) basis has no global observation, recording THERMAL_AS_GLOBAL; default False.

Returns
—ObservedConfinementany

The resolved times and their provenance.

Raises

  • ValueError An unknown energy_basis, or arrays of different lengths.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. A thermal scaling takes tau_thermal; nothing else stands in for it.
  2. A global scaling takes tau_global where it is finite. Elsewhere it is NaN (strict), or the thermal time with THERMAL_AS_GLOBAL recorded when thermal_as_global is set.
  3. An unaudited scaling has no known basis: NaN unless thermal_as_global, when the thermal time is used and recorded.

Applicability

Machine-independent. The thermal-for-global substitution is defensible for ohmic plasmas without fast ions (VEST Tier A) and wrong for beam- or RF-heated plasmas with a fast-ion population.

Limitations

Resolves which time to use; it does not construct $W_{global}$ or $W_{th}$ from a reconstruction. w_mhd_J from a magnetics EFIT is total kinetic pressure and is not by itself a thermal energy.

Provenance

  1. Issue #1713: thermal and global energy confinement must not be silently interchanged in scaling comparisons.

smoothed_time_derivative

Machine-independent source
smoothed_time_derivative(time, data, *, window_s=None, polyorder=2, at=None)

Time derivative of a sampled quantity by a local polynomial fit, on any time grid.

NameTypeUnitDescription
timenumpy.ndarrays

Sample times, strictly increasing, 1-D; gaps are allowed.

datanumpy.ndarrayany

The sampled quantity, same length as time.

window_sfloat, optionals

Full width of the fitting window centred on each evaluation time; None takes the unsmoothed weighted central difference of vaft.process.numerical.time_derivative at the samples.

polyorderint, optional-

Degree of the local polynomial.

atnumpy.ndarray, optionals

Times to evaluate the derivative at; default the samples themselves. Needs window_s.

Returns
—numpy.ndarrayany/s

d(data)/dt at at (or at each sample); NaN where the window holds no more than polyorder finite samples.

Raises

  • ValueError Arrays of different length, fewer than two samples, a non-increasing time axis, a non-positive window, polyorder below 1 with a window, or at without window_s.
Processing steps · Defaults · Applicability · Limitations · Provenance

Processing steps

  1. Without window_s: the weighted central difference at the samples (one-sided at the ends).
  2. With window_s: at each evaluation time $t_0$, the finite samples with $|t - t_0| \le$ window_s/2 are fitted by a degree- polyorder polynomial in $t - t_0$ (least squares, unweighted); the derivative is its linear coefficient. On a uniform grid away from the ends this is the Savitzky-Golay first derivative; at the ends and across gaps the window is simply one-sided or sparser.

Defaults

polyorder = 2: numerical convenience, the lowest degree whose derivative is unbiased for a quantity with constant curvature.

Applicability

Machine-independent.

Limitations

Smoothing biases a derivative that changes within the window; the window width belongs in the provenance of every quantity built on it. Near the ends the window is one-sided and the derivative noisier.

Provenance

  1. A. Savitzky and M. J. E. Golay, Anal. Chem. 36 (1964) 1627: the local least-squares polynomial derivative.

results matching ""

    No results matching ""