Formula reference: utils

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

Overview

This module provides common utility functions and fitting utilities used throughout the formula module.

Functions

calculate_peaking_factorPeaking factor, central value over volume average.
calculate_poloidal_fluxPoloidal flux per radian from a line integral of $RB_\theta$. Convention
calculate_toroidal_fluxToroidal flux as a sum of $B_\varphi$ over area elements. Convention
calculate_volume_weighted_averageVolume-weighted average of a sampled profile.
eped_tanh_boundsParameter box the eped_tanh fit uses for this data.
fit_profileFit a 1-D profile with a selectable model and evaluate it on a grid.
gp_fitFit a squared-exponential Gaussian process and evaluate it on a grid.
gradientDerivative $dy/dx$ along the first axis of a sampled profile or map.
make_fit_functionBuild a 1-D parametric model $f(x; c_0, c_1, \dots)$ for profile fitting.
normalize_profileLinear normalisation of a profile between its axis and boundary values.
normalized_gradient_scale_lengthNormalised inverse gradient scale length $a/L_y$ of a sampled profile. Convention
trapz_integralDefinite integral $\int y\,dx$ by the trapezoidal rule.

calculate_peaking_factor

source
calculate_peaking_factor(central, volume_avg)

Reduction: scalar 0d → scalar 0d · dimensionless normalization · global · profile descriptor (taxonomy)

Show source vaft/formula/utils.py, lines 447–479
def calculate_peaking_factor(central: float,
                           volume_avg: float) -> float:
    r"""Peaking factor, central value over volume average.

    $$\mathrm{PF} = \frac{X(0)}{\langle X\rangle}$$

    Parameters
    ----------
    central : float
        Value on the magnetic axis [any].
    volume_avg : float
        Volume average of the same quantity, same unit [any].

    Returns
    -------
    float
        Peaking factor [-].

    Numerical notes
    ---------------
    A zero volume average warns and returns ``nan``.

    Reduction
    ---------
    input: scalar_0d
    output: scalar_0d
    kind: dimensionless_normalization
    locality: global
    role: profile_descriptor
    """
    return _guarded_ratio(
        central, volume_avg, what="calculate_peaking_factor", because="volume_avg"
    )

Peaking factor, central value over volume average.

\[\mathrm{PF} = \frac{X(0)}{\langle X\rangle}\]
NameTypeUnitDescription
centralfloatany

Value on the magnetic axis.

volume_avgfloatany

Volume average of the same quantity, same unit.

Returns
—float-

Peaking factor.

Numerical notes

Numerical notes

A zero volume average warns and returns nan.

calculate_poloidal_flux

Convention-sensitive source
calculate_poloidal_flux(R, B_theta, l, psi_axis=0.0)
Show source vaft/formula/utils.py, lines 512–546
def calculate_poloidal_flux(R: np.ndarray,
                          B_theta: np.ndarray,
                          l: np.ndarray,
                          psi_axis: float = 0.0) -> float:
    r"""Poloidal flux per radian from a line integral of $RB_\theta$.

    $$\psi(l) = \int_0^{l} R\,B_\theta\,dl' + \psi_a$$

    Parameters
    ----------
    R : np.ndarray
        Major radius along the path [m].
    B_theta : np.ndarray
        Poloidal field normal to the path [T].
    l : np.ndarray
        Path coordinate, monotonic [m].
    psi_axis : float, optional
        Offset added to the integral; default 0 [Wb/rad].

    Returns
    -------
    float
        Flux at the end of the path [Wb/rad].

    Convention
    ----------
    Flux per radian (COCOS 1-8 storage); multiply by $2\pi$ for the IMAS
    full-weber flux.  Sign follows ``B_theta`` and the direction of ``l``.
    The physics wrapper is :func:`vaft.formula.equilibrium.psi_from_RBtheta`.

    Numerical notes
    ---------------
    Trapezoidal rule over the whole path; returns the end value only.
    """
    return trapz_integral(l, R * B_theta) + psi_axis

Poloidal flux per radian from a line integral of $RB_\theta$.

\[\psi(l) = \int_0^{l} R\,B_\theta\,dl' + \psi_a\]
NameTypeUnitDescription
Rnp.ndarraym

Major radius along the path.

B_thetanp.ndarrayT

Poloidal field normal to the path.

lnp.ndarraym

Path coordinate, monotonic.

psi_axisfloat, optionalWb/rad

Offset added to the integral; default 0.

Returns
—floatWb/rad

Flux at the end of the path.

Convention

Flux per radian (COCOS 1-8 storage); multiply by $2\pi$ for the IMAS full-weber flux. Sign follows B_theta and the direction of l. The physics wrapper is vaft.formula.equilibrium.psi_from_RBtheta.

Numerical notes

Numerical notes

Trapezoidal rule over the whole path; returns the end value only.

calculate_toroidal_flux

Convention-sensitive source
calculate_toroidal_flux(B_phi, dA)
Show source vaft/formula/utils.py, lines 549–577
def calculate_toroidal_flux(B_phi: np.ndarray,
                          dA: np.ndarray) -> float:
    r"""Toroidal flux as a sum of $B_\varphi$ over area elements.

    $$\Phi = \sum_i B_{\varphi,i}\,\Delta A_i$$

    Parameters
    ----------
    B_phi : np.ndarray
        Toroidal field on the area elements [T].
    dA : np.ndarray
        Area of each element, same shape [m^2].

    Returns
    -------
    float
        Toroidal flux [Wb].

    Convention
    ----------
    Full weber (toroidal flux has no per-radian form); sign of $B_\varphi$.
    The physics wrapper is :func:`vaft.formula.equilibrium.phi_from_Bphi`.

    Numerical notes
    ---------------
    A Riemann sum with caller-supplied areas, not a quadrature rule; first
    order in the cell size.  Tracked in #358.
    """
    return np.sum(B_phi * dA)

Toroidal flux as a sum of $B_\varphi$ over area elements.

\[\Phi = \sum_i B_{\varphi,i}\,\Delta A_i\]
NameTypeUnitDescription
B_phinp.ndarrayT

Toroidal field on the area elements.

dAnp.ndarraym^2

Area of each element, same shape.

Returns
—floatWb

Toroidal flux.

Convention

Full weber (toroidal flux has no per-radian form); sign of $B_\varphi$. The physics wrapper is vaft.formula.equilibrium.phi_from_Bphi.

Numerical notes

Numerical notes

A Riemann sum with caller-supplied areas, not a quadrature rule; first order in the cell size. Tracked in #358.

calculate_volume_weighted_average

source
calculate_volume_weighted_average(x, V)
Show source vaft/formula/utils.py, lines 482–509
def calculate_volume_weighted_average(x: np.ndarray,
                                    V: np.ndarray) -> float:
    r"""Volume-weighted average of a sampled profile.

    $$\langle X\rangle = \frac{\sum_i X_i\,V_i}{\sum_i V_i}$$

    Parameters
    ----------
    x : np.ndarray
        Profile values at each cell [any].
    V : np.ndarray
        Volume of each cell, same shape [m^3].

    Returns
    -------
    float
        Volume-weighted average in the unit of ``x`` [any].

    Numerical notes
    ---------------
    A zero total volume warns and returns ``nan``.
    """
    return _guarded_ratio(
        np.sum(x * V),
        np.sum(V),
        what="calculate_volume_weighted_average",
        because="sum(V)",
    )

Volume-weighted average of a sampled profile.

\[\langle X\rangle = \frac{\sum_i X_i\,V_i}{\sum_i V_i}\]
NameTypeUnitDescription
xnp.ndarrayany

Profile values at each cell.

Vnp.ndarraym^3

Volume of each cell, same shape.

Returns
—floatany

Volume-weighted average in the unit of x.

Numerical notes

Numerical notes

A zero total volume warns and returns nan.

eped_tanh_bounds

source
eped_tanh_bounds(x, y)
Show source vaft/formula/utils.py, lines 746–785
def eped_tanh_bounds(x, y):
    """Parameter box the ``eped_tanh`` fit uses for this data.

    Public so that a caller checking whether a fitted parameter came back
    resting on a bound compares against the same box the fit used, rather than
    re-deriving one that can drift out of step with it.  The amplitude bounds
    scale with the data's magnitude; the shape bounds are fixed.

    Parameters
    ----------
    x : array_like
        Radial coordinate; used only for its length [-].
    y : array_like
        Profile samples, whose magnitude sets the amplitude bounds [any].

    Returns
    -------
    tuple of list
        ``(lower, upper)``, each seven entries ordered as the model's
        arguments ``f0, f_ped, f_sep, x_ped, width, alpha, beta``.  The
        amplitudes are ``+-5 max|y|`` [any]; ``x_ped`` is in ``[0.75, 0.999]``
        and ``width`` in ``[0.01, 0.3]`` [-]; ``alpha`` and ``beta`` are in
        ``[1, 8]`` [-].

    Notes
    -----
    ``alpha`` and ``beta`` stay at or above one because below it the gated
    core term has an infinite slope -- at ``x_ped`` for ``beta``, at the
    magnetic axis for ``alpha`` -- which no flux-function profile has, and
    which would make any gradient taken from the fit depend on the caller's
    grid spacing.

    References
    ----------
    .. [EPED] The seven-parameter form and the ``x_ped``/``width`` box follow
       the pedestal-fitting study ported in migration decision D-05;
       :func:`vaft.process.profile.pedestal_top` is the consumer.
    """
    _, lower, upper = _initial_eped_tanh_guess(x, y)
    return lower, upper

Parameter box the eped_tanh fit uses for this data.

Public so that a caller checking whether a fitted parameter came back resting on a bound compares against the same box the fit used, rather than re-deriving one that can drift out of step with it. The amplitude bounds scale with the data’s magnitude; the shape bounds are fixed.

NameTypeUnitDescription
xarray_like-

Radial coordinate; used only for its length.

yarray_likeany

Profile samples, whose magnitude sets the amplitude bounds.

Returns
—tuple of list-

(lower, upper), each seven entries ordered as the model’s arguments f0, f_ped, f_sep, x_ped, width, alpha, beta. The amplitudes are +-5 max|y| [any]; x_ped is in [0.75, 0.999] and width in [0.01, 0.3] [-]; alpha and beta are in [1, 8].

Notes · References

Notes

alpha and beta stay at or above one because below it the gated core term has an infinite slope – at x_ped for beta, at the magnetic axis for alpha – which no flux-function profile has, and which would make any gradient taken from the fit depend on the caller’s grid spacing.

References

  1. The seven-parameter form and the x_ped/width box follow the pedestal-fitting study ported in migration decision D-05; vaft.process.profile.pedestal_top is the consumer.

fit_profile

source
fit_profile(x, y, y_std, x_eval, order=3, uncertainty_option=1, fitting_function='polynomial', gp_kernel=None, gp_anchor=None, n_restarts_optimizer=5, *, random_state=0)

Fit a 1-D profile with a selectable model and evaluate it on a grid.

Least-squares (scipy.optimize.curve_fit) for the parametric modes, Gaussian-process regression (sklearn) for 'gp', linear interpolation for 'linear', a core-polynomial/edge-exponential blend with a $\tanh$ transition for 'core_poly_edge_exp', and square-root modes that fit $y^2$ and return $\sqrt{f}$.

NameTypeUnitDescription
xarray-likeany

Data abscissae, 1-D.

yarray-likeany

Data values.

y_stdarray-like or Noneany

Per-point uncertainty, same unit as y; None for unweighted.

x_evalarray-likeany

Evaluation grid.

orderint, optional-

Number of polynomial coefficients (degree order - 1); default 3.

uncertainty_optionint, optional-

1 (default) weights by y_std when given; 0 ignores it.

fitting_functionstr, optionalstr

Model name, default 'polynomial'. One of 'gp' (scipy), 'gp_sklearn', 'polynomial', 'free_polynomial', 'exponential', 'free_exponential', 'linear', 'core_poly_edge_exp', 'eped_tanh', 'sqrt', 'sqrt_exponential'. 'eped_tanh' fits the seven-parameter pedestal model (f0, f_ped, f_sep, x_ped, width, alpha, beta) and is what vaft.process.profile.pedestal_top uses.

gp_kernelsklearn kernel or None, optionaln/a

Kernel for 'gp_sklearn' only; ignored, with a warning, by the scipy 'gp' mode; default constant times RBF.

gp_anchortuple or None, optionaln/a

(x_anchor, y_anchor, y_std_anchor) extra points for the GP.

n_restarts_optimizerint, optional-

GP hyperparameter restarts; default 5.

random_stateint, optional-

Seed for the restart starting points of both GP backends; default 0. Without it the 'gp_sklearn' backend would draw its restarts from numpy’s global RandomState and the fit would depend on what ran before it in the process.

Returns
y_evalnp.ndarrayany

Fitted values on x_eval.

y_std_evalnp.ndarrayany

Fitted uncertainty; non-zero for the GP mode only.

fit_functioncallablen/a

f(x) evaluating the fit at arbitrary x.

coeffsnp.ndarray or Noneany

Fitted coefficients; None for the GP and linear modes.

Raises

  • ValueError Fewer than two valid points after masking, or an unknown mode.
  • RuntimeError When curve_fit does not converge.
Assumptions · Limitations · Numerical notes · Examples

Assumptions

x is a normalised radius on $[0, 1]$ for the constrained modes (they force zero at $x = 1$); uncertainties are one-sigma and independent.

Limitations

Non-finite points and non-positive y_std are dropped with a warning. The 'linear' mode uses numpy.interp, which holds the end values constant outside the data range (silent constant extrapolation; tracked in #359). The square-root modes clip negative data to zero before squaring, biasing the fit where the data cross zero, and report zero uncertainty. A fit that returns its initial guess unchanged is reported by a warning, not an exception.

Numerical notes

The initial guess is scaled to max|y| so raw densities in m^-3 do not stall the optimiser; maxfev=20000.

Examples

fit_profile(x, y, y_std, x_eval, fitting_function=’gp’) fit_profile(x, y, y_std, x_eval, order=3, fitting_function=’polynomial’) fit_profile(x, y, y_std, x_eval, fitting_function=’linear’) fit_profile(x, y, y_std, x_eval, order=3, fitting_function=’core_poly_edge_exp’)

gp_fit

source
gp_fit(x, y, y_std, x_eval, *, n_restarts_optimizer=5, random_state=0)
Show source vaft/formula/utils.py, lines 337–381
def gp_fit(x, y, y_std, x_eval, *, n_restarts_optimizer: int = 5, random_state: int = 0):
    """Fit a squared-exponential Gaussian process and evaluate it on a grid.

    Parameters
    ----------
    x : array-like
        Data abscissae, 1-D [any].
    y : array-like
        Data values [any].
    y_std : array-like or None
        Per-point one-sigma uncertainty, entering as the diagonal noise;
        ``None`` uses a numerical jitter [any].
    x_eval : array-like
        Evaluation grid [any].
    n_restarts_optimizer : int, optional
        Extra random restarts of the marginal-likelihood optimisation, on top of
        the one from the default hyperparameters; default 5 [-].
    random_state : int, optional
        Seed for the restart draws, so a fit is reproducible; default 0 [-].

    Returns
    -------
    mean : np.ndarray
        Posterior mean on ``x_eval`` [any].
    std : np.ndarray
        Posterior standard deviation on ``x_eval`` [any].

    Numerical notes
    ---------------
    ``y`` is standardised before fitting and the posterior is mapped back,
    which is what makes one set of hyperparameter bounds work for data in
    m^-3 and in keV alike.  The marginal likelihood is optimised with
    ``scipy.optimize.minimize`` (L-BFGS-B) over the log-hyperparameters.

    References
    ----------
    .. [1] C. E. Rasmussen and C. K. I. Williams, *Gaussian Processes for
           Machine Learning*, MIT Press (2006), Algorithm 2.1 and Eq. (5.8).
    """
    state = _gp_train(
        x, y, y_std,
        n_restarts_optimizer=n_restarts_optimizer,
        random_state=random_state,
    )
    return _gp_evaluate(state, x_eval)

Fit a squared-exponential Gaussian process and evaluate it on a grid.

NameTypeUnitDescription
xarray-likeany

Data abscissae, 1-D.

yarray-likeany

Data values.

y_stdarray-like or Noneany

Per-point one-sigma uncertainty, entering as the diagonal noise; None uses a numerical jitter.

x_evalarray-likeany

Evaluation grid.

n_restarts_optimizerint, optional-

Extra random restarts of the marginal-likelihood optimisation, on top of the one from the default hyperparameters; default 5.

random_stateint, optional-

Seed for the restart draws, so a fit is reproducible; default 0.

Returns
meannp.ndarrayany

Posterior mean on x_eval.

stdnp.ndarrayany

Posterior standard deviation on x_eval.

Numerical notes · References

Numerical notes

y is standardised before fitting and the posterior is mapped back, which is what makes one set of hyperparameter bounds work for data in m^-3 and in keV alike. The marginal likelihood is optimised with scipy.optimize.minimize (L-BFGS-B) over the log-hyperparameters.

References

  1. C. E. Rasmussen and C. K. I. Williams, Gaussian Processes for Machine Learning, MIT Press (2006), Algorithm 2.1 and Eq. (5.8).

gradient

source
gradient(x, y)
Show source vaft/formula/utils.py, lines 42–72
def gradient(x: np.ndarray, y: np.ndarray) -> np.ndarray:
    r"""Derivative $dy/dx$ along the first axis of a sampled profile or map.

    $$\frac{dy}{dx}\Big|_i \approx \frac{y_{i+1} - y_{i-1}}{x_{i+1} - x_{i-1}}$$

    (second-order central difference on a possibly non-uniform grid).

    Parameters
    ----------
    x : np.ndarray
        Independent variable, monotonic [any].
    y : np.ndarray
        Dependent variable whose first axis has the length of ``x``: a 1-D
        profile or an N-D map indexed ``[x, ...]`` [any].

    Returns
    -------
    np.ndarray
        $dy/dx$ at every sample, of the shape of ``y``, in units of ``y`` per
        unit ``x`` [any].

    Numerical notes
    ---------------
    Wraps ``numpy.gradient(y, x, axis=0)``: second-order accurate in the
    interior, first-order one-sided at the two end points, and
    noise-amplifying (a relative noise $\delta$ on ``y`` becomes
    $\delta/\Delta x$ on the derivative).  Needs at least two samples; only
    the first axis of an N-D ``y`` is differentiated, each column
    independently, so the result equals stacking the 1-D derivatives.
    """
    return np.gradient(y, x, axis=0)

Derivative $dy/dx$ along the first axis of a sampled profile or map.

\[\frac{dy}{dx}\Big|_i \approx \frac{y_{i+1} - y_{i-1}}{x_{i+1} - x_{i-1}}\]

(second-order central difference on a possibly non-uniform grid).

NameTypeUnitDescription
xnp.ndarrayany

Independent variable, monotonic.

ynp.ndarrayany

Dependent variable whose first axis has the length of x: a 1-D profile or an N-D map indexed [x, ...].

Returns
—np.ndarrayany

$dy/dx$ at every sample, of the shape of y, in units of y per unit x.

Numerical notes

Numerical notes

Wraps numpy.gradient(y, x, axis=0): second-order accurate in the interior, first-order one-sided at the two end points, and noise-amplifying (a relative noise $\delta$ on y becomes $\delta/\Delta x$ on the derivative). Needs at least two samples; only the first axis of an N-D y is differentiated, each column independently, so the result equals stacking the 1-D derivatives.

make_fit_function

source
make_fit_function(mode)
Show source vaft/formula/utils.py, lines 584–653
def make_fit_function(mode):
    r"""Build a 1-D parametric model $f(x; c_0, c_1, \dots)$ for profile fitting.

    $$\begin{aligned}
      \text{polynomial:}\ & (1-x)\,\textstyle\sum_k c_kx^k &
      \text{free\_polynomial:}\ & \textstyle\sum_k c_kx^k \\
      \text{exponential:}\ & (1-x)\exp\big(\textstyle\sum_k c_kx^k\big) &
      \text{free\_exponential:}\ & \exp\big(\textstyle\sum_k c_kx^k\big)
    \end{aligned}$$

    Parameters
    ----------
    mode : str
        Model name, case-insensitive [str].
        One of ``'polynomial'``, ``'free_polynomial'``, ``'exponential'``,
        ``'free_exponential'`` (a few aliases are accepted).

    Returns
    -------
    callable
        ``f(x, *coeffs)`` evaluating the model, in the unit of the data [any].

    Raises
    ------
    ValueError
        For an unknown mode.

    Assumptions
    -----------
    ``x`` is a normalised radius on $[0, 1]$: the $(1 - x)$ factor forces the
    constrained modes to zero at $x = 1$; the exponential modes are strictly
    positive and decay monotonically when the polynomial is decreasing.
    """
    mode = mode.lower()
    if mode == 'polynomial':
        # (1-x)*poly(x) -> enforces value -> 0 at x=1
        def func(x, *coeffs):
            x = np.asarray(x, dtype=float)
            s = 0.0
            for k in range(len(coeffs)):
                s = s + coeffs[k] * x**k
            return (1.0 - x) * s
    elif mode in {'free_polynomial', 'polynomial_unconstrained', 'unconstrained_polynomial'}:
        # plain poly(x) -> no boundary constraint at x=1
        def func(x, *coeffs):
            x = np.asarray(x, dtype=float)
            s = 0.0
            for k in range(len(coeffs)):
                s = s + coeffs[k] * x**k
            return s
    elif mode == 'exponential':
        # (1-x)*exp(poly(x)) -> goes to 0 at x=1, stays positive
        def func(x, *coeffs):
            x = np.asarray(x, dtype=float)
            s = 0.0
            for k in range(len(coeffs)):
                s = s + coeffs[k] * x**k
            return (1.0 - x) * np.exp(s)
    elif mode in {'free_exponential', 'exp_free', 'exponential_unconstrained'}:
        # exp(poly(x)) -> always > 0, NO edge-zero; monotonic decay if poly decreasing.
        # Edge value exp(poly(1)) stays small-but-finite (never exactly 0, never rises if c1<0).
        def func(x, *coeffs):
            x = np.asarray(x, dtype=float)
            s = 0.0
            for k in range(len(coeffs)):
                s = s + coeffs[k] * x**k
            return np.exp(s)
    else:
        raise ValueError(f"Invalid fitting function: {mode}")
    return func

Build a 1-D parametric model $f(x; c_0, c_1, \dots)$ for profile fitting.

\[\begin{aligned} \text{polynomial:}\ & (1-x)\,\textstyle\sum_k c_kx^k & \text{free\_polynomial:}\ & \textstyle\sum_k c_kx^k \\ \text{exponential:}\ & (1-x)\exp\big(\textstyle\sum_k c_kx^k\big) & \text{free\_exponential:}\ & \exp\big(\textstyle\sum_k c_kx^k\big) \end{aligned}\]
NameTypeUnitDescription
modestrstr

Model name, case-insensitive. One of 'polynomial', 'free_polynomial', 'exponential', 'free_exponential' (a few aliases are accepted).

Returns
—callableany

f(x, *coeffs) evaluating the model, in the unit of the data.

Raises

  • ValueError For an unknown mode.
Assumptions

Assumptions

x is a normalised radius on $[0, 1]$: the $(1 - x)$ factor forces the constrained modes to zero at $x = 1$; the exponential modes are strictly positive and decay monotonically when the polynomial is decreasing.

normalize_profile

source
normalize_profile(x, x_axis, x_boundary)
Show source vaft/formula/utils.py, lines 412–444
def normalize_profile(x: Union[float, np.ndarray],
                     x_axis: float,
                     x_boundary: float) -> Union[float, np.ndarray]:
    r"""Linear normalisation of a profile between its axis and boundary values.

    $$x_N = \frac{x - x_{\mathrm{axis}}}{x_{\mathrm{boundary}} - x_{\mathrm{axis}}}$$

    Parameters
    ----------
    x : float or np.ndarray
        Values to normalise [any].
    x_axis : float
        Value mapped to 0 [any].
    x_boundary : float
        Value mapped to 1 [any].

    Returns
    -------
    float or np.ndarray
        Normalised values, 0 at the axis value and 1 at the boundary value [-].

    Numerical notes
    ---------------
    A degenerate profile whose axis and boundary values are equal -- which
    packaged VEST samples do contain -- warns and returns ``nan`` rather than
    propagating ``inf``.
    """
    return _guarded_ratio(
        x - x_axis,
        x_boundary - x_axis,
        what="normalize_profile",
        because="x_boundary - x_axis",
    )

Linear normalisation of a profile between its axis and boundary values.

\[x_N = \frac{x - x_{\mathrm{axis}}}{x_{\mathrm{boundary}} - x_{\mathrm{axis}}}\]
NameTypeUnitDescription
xfloat or np.ndarrayany

Values to normalise.

x_axisfloatany

Value mapped to 0.

x_boundaryfloatany

Value mapped to 1.

Returns
—float or np.ndarray-

Normalised values, 0 at the axis value and 1 at the boundary value.

Numerical notes

Numerical notes

A degenerate profile whose axis and boundary values are equal – which packaged VEST samples do contain – warns and returns nan rather than propagating inf.

normalized_gradient_scale_length

Convention-sensitive source
normalized_gradient_scale_length(x, y, a)

Reduction: profile 1d → profile 1d · differential · flux surface local · closure input (taxonomy)

Show source vaft/formula/utils.py, lines 75–139
def normalized_gradient_scale_length(x: np.ndarray, y: np.ndarray, a: float) -> np.ndarray:
    r"""Normalised inverse gradient scale length $a/L_y$ of a sampled profile.

    $$\frac{a}{L_y} = -\frac{a}{y}\,\frac{dy}{dx}$$

    Parameters
    ----------
    x : np.ndarray
        Radial coordinate, 1-D and monotonic, e.g. minor radius $r$ [m].
    y : np.ndarray
        Profile sampled on ``x`` along its last axis, e.g. $T_e$ or $n_e$;
        shape ``(..., len(x))`` [any].
    a : float
        Normalising length, in the unit of ``x``; for a gyrokinetic input the
        minor radius [m].

    Returns
    -------
    np.ndarray
        $a/L_y$ at every sample, of the shape of ``y``; ``nan`` where ``y``
        is zero or non-finite, with a ``RuntimeWarning`` [-].

    Convention
    ----------
    Positive for a profile that falls with ``x`` -- the usual peaked core
    profile -- so a larger number is a steeper drive.  ``x`` and ``a`` must
    share a unit and ``x`` must be the radius $a$ normalises: on $\rho_N$ or
    $\sqrt{\psi_N}$ pass ``a = 1`` and the result is $-d\ln y/d\rho$, which
    differs from $a/L_y$ by the Jacobian $d\rho/dr$.  The same holds for
    $a/L_{T_e}$, $a/L_{n_e}$ and $a/L_{p}$: only the profile changes.

    Numerical notes
    ---------------
    The derivative is ``numpy.gradient(y, x, axis=-1)``, identical to
    :func:`gradient` for a 1-D profile: second-order central differences in
    the interior, first-order one-sided at the two ends.  Differentiation
    amplifies noise, and dividing by ``y`` amplifies it again where the
    profile is small -- the edge -- so smooth or fit the profile first.

    Reduction
    ---------
    input: profile_1d
    output: profile_1d
    kind: differential
    locality: flux_surface_local
    role: closure_input

    References
    ----------
    .. [1] G. M. Staebler, J. E. Kinsey and R. E. Waltz, Phys. Plasmas 14
           (2007) 055909 (the $a/L_T$, $a/L_n$ drive inputs of TGLF).

    See Also
    --------
    gradient
    """
    x = np.asarray(x, dtype=float)
    y = np.asarray(y, dtype=float)
    derivative = np.gradient(y, x, axis=-1)
    return _guarded_ratio(
        -float(a) * derivative,
        y,
        what="normalized_gradient_scale_length",
        because="the profile value y",
    )

Normalised inverse gradient scale length $a/L_y$ of a sampled profile.

\[\frac{a}{L_y} = -\frac{a}{y}\,\frac{dy}{dx}\]
NameTypeUnitDescription
xnp.ndarraym

Radial coordinate, 1-D and monotonic, e.g. minor radius $r$.

ynp.ndarrayany

Profile sampled on x along its last axis, e.g. $T_e$ or $n_e$; shape (..., len(x)).

afloatm

Normalising length, in the unit of x; for a gyrokinetic input the minor radius.

Returns
—np.ndarray-

$a/L_y$ at every sample, of the shape of y; nan where y is zero or non-finite, with a RuntimeWarning.

Convention

Positive for a profile that falls with x – the usual peaked core profile – so a larger number is a steeper drive. x and a must share a unit and x must be the radius $a$ normalises: on $\rho_N$ or $\sqrt{\psi_N}$ pass a = 1 and the result is $-d\ln y/d\rho$, which differs from $a/L_y$ by the Jacobian $d\rho/dr$. The same holds for $a/L_{T_e}$, $a/L_{n_e}$ and $a/L_{p}$: only the profile changes.

Numerical notes · See Also · References

Numerical notes

The derivative is numpy.gradient(y, x, axis=-1), identical to gradient for a 1-D profile: second-order central differences in the interior, first-order one-sided at the two ends. Differentiation amplifies noise, and dividing by y amplifies it again where the profile is small – the edge – so smooth or fit the profile first.

See Also

gradient

References

  1. G. M. Staebler, J. E. Kinsey and R. E. Waltz, Phys. Plasmas 14 (2007) 055909 (the $a/L_T$, $a/L_n$ drive inputs of TGLF).

trapz_integral

source
trapz_integral(x, y)
Show source vaft/formula/utils.py, lines 142–167
def trapz_integral(x: np.ndarray, y: np.ndarray) -> float:
    r"""Definite integral $\int y\,dx$ by the trapezoidal rule.

    $$\int y\,dx \approx \sum_i \frac{y_i + y_{i+1}}{2}\,(x_{i+1} - x_i)$$

    Parameters
    ----------
    x : np.ndarray
        Sample abscissae, monotonic [any].
    y : np.ndarray
        Integrand at the samples [any].

    Returns
    -------
    float
        Integral over the sampled range, in units of ``y`` times ``x`` [any].

    Numerical notes
    ---------------
    ``numpy.trapezoid`` through :func:`vaft.compat.trapz_compat`; second-order
    accurate in the spacing, exact for piecewise-linear integrands, and
    sign-reversed for a decreasing ``x``.  A same-named helper in
    :mod:`vaft.formula.green` shadows this one on the package namespace
    (``vaft.formula.trapz_integral`` is the Green's-function copy).
    """
    return trapz_compat(y, x=x)

vaft.formula.trapz_integral resolves to the green copy; reach this one as vaft.formula.utils.trapz_integral.

Definite integral $\int y\,dx$ by the trapezoidal rule.

\[\int y\,dx \approx \sum_i \frac{y_i + y_{i+1}}{2}\,(x_{i+1} - x_i)\]
NameTypeUnitDescription
xnp.ndarrayany

Sample abscissae, monotonic.

ynp.ndarrayany

Integrand at the samples.

Returns
—floatany

Integral over the sampled range, in units of y times x.

Numerical notes

Numerical notes

numpy.trapezoid through vaft.compat.trapz_compat; second-order accurate in the spacing, exact for piecewise-linear integrands, and sign-reversed for a decreasing x. A same-named helper in vaft.formula.green shadows this one on the package namespace (vaft.formula.trapz_integral is the Green’s-function copy).

results matching ""

    No results matching ""