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_factor | Peaking factor, central value over volume average. |
calculate_poloidal_flux | Poloidal flux per radian from a line integral of $RB_\theta$. Convention |
calculate_toroidal_flux | Toroidal flux as a sum of $B_\varphi$ over area elements. Convention |
calculate_volume_weighted_average | Volume-weighted average of a sampled profile. |
eped_tanh_bounds | Parameter box the eped_tanh fit uses for this data.
|
fit_profile | Fit a 1-D profile with a selectable model and evaluate it on a grid. |
gp_fit | Fit a squared-exponential Gaussian process and evaluate it on a grid. |
gradient | Derivative $dy/dx$ along the first axis of a sampled profile or map. |
make_fit_function | Build a 1-D parametric model $f(x; c_0, c_1, \dots)$ for profile fitting. |
normalize_profile | Linear normalisation of a profile between its axis and boundary values. |
normalized_gradient_scale_length | Normalised inverse gradient scale length $a/L_y$ of a sampled profile. Convention |
trapz_integral | Definite integral $\int y\,dx$ by the trapezoidal rule. |
No function matches.
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}\]| Name | Type | Unit | Description |
|---|---|---|---|
central | float | any | Value on the magnetic axis. |
volume_avg | float | any | 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_axisPoloidal flux per radian from a line integral of $RB_\theta$.
\[\psi(l) = \int_0^{l} R\,B_\theta\,dl' + \psi_a\]| Name | Type | Unit | Description |
|---|---|---|---|
R | np.ndarray | m | Major radius along the path. |
B_theta | np.ndarray | T | Poloidal field normal to the path. |
l | np.ndarray | m | Path coordinate, monotonic. |
psi_axis | float, optional | Wb/rad | Offset added to the integral; default 0. |
| Returns | |||
| — | float | Wb/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\]| Name | Type | Unit | Description |
|---|---|---|---|
B_phi | np.ndarray | T | Toroidal field on the area elements. |
dA | np.ndarray | m^2 | Area of each element, same shape. |
| Returns | |||
| — | float | Wb | 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}\]| Name | Type | Unit | Description |
|---|---|---|---|
x | np.ndarray | any | Profile values at each cell. |
V | np.ndarray | m^3 | Volume of each cell, same shape. |
| Returns | |||
| — | float | any | Volume-weighted average in the unit of |
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, upperParameter 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.
| Name | Type | Unit | Description |
|---|---|---|---|
x | array_like | - | Radial coordinate; used only for its length. |
y | array_like | any | Profile samples, whose magnitude sets the amplitude bounds. |
| Returns | |||
| — | tuple of list | - |
|
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
- The seven-parameter form and the
x_ped/widthbox follow the pedestal-fitting study ported in migration decision D-05;vaft.process.profile.pedestal_topis 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}$.
| Name | Type | Unit | Description |
|---|---|---|---|
x | array-like | any | Data abscissae, 1-D. |
y | array-like | any | Data values. |
y_std | array-like or None | any | Per-point uncertainty, same unit as |
x_eval | array-like | any | Evaluation grid. |
order | int, optional | - | Number of polynomial coefficients (degree |
uncertainty_option | int, optional | - | 1 (default) weights by |
fitting_function | str, optional | str | Model name, default |
gp_kernel | sklearn kernel or None, optional | n/a | Kernel for |
gp_anchor | tuple or None, optional | n/a |
|
n_restarts_optimizer | int, optional | - | GP hyperparameter restarts; default 5. |
random_state | int, optional | - | Seed for the restart starting points of both GP backends; default 0. Without it the |
| Returns | |||
y_eval | np.ndarray | any | Fitted values on |
y_std_eval | np.ndarray | any | Fitted uncertainty; non-zero for the GP mode only. |
fit_function | callable | n/a |
|
coeffs | np.ndarray or None | any | Fitted coefficients; |
Raises
ValueErrorFewer than two valid points after masking, or an unknown mode.RuntimeErrorWhencurve_fitdoes 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.
| Name | Type | Unit | Description |
|---|---|---|---|
x | array-like | any | Data abscissae, 1-D. |
y | array-like | any | Data values. |
y_std | array-like or None | any | Per-point one-sigma uncertainty, entering as the diagonal noise; |
x_eval | array-like | any | Evaluation grid. |
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 | any | Posterior mean on |
std | np.ndarray | any | Posterior standard deviation on |
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
- 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).
| Name | Type | Unit | Description |
|---|---|---|---|
x | np.ndarray | any | Independent variable, monotonic. |
y | np.ndarray | any | Dependent variable whose first axis has the length of |
| Returns | |||
| — | np.ndarray | any | $dy/dx$ at every sample, of the shape of |
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 funcBuild 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}\]| Name | Type | Unit | Description |
|---|---|---|---|
mode | str | str | Model name, case-insensitive. One of |
| Returns | |||
| — | callable | any |
|
Raises
ValueErrorFor 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}}}\]| Name | Type | Unit | Description |
|---|---|---|---|
x | float or np.ndarray | any | Values to normalise. |
x_axis | float | any | Value mapped to 0. |
x_boundary | float | any | 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}\]| Name | Type | Unit | Description |
|---|---|---|---|
x | np.ndarray | m | Radial coordinate, 1-D and monotonic, e.g. minor radius $r$. |
y | np.ndarray | any | Profile sampled on |
a | float | m | Normalising length, in the unit of |
| Returns | |||
| — | np.ndarray | - | $a/L_y$ at every sample, of the shape of |
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
- 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)\]| Name | Type | Unit | Description |
|---|---|---|---|
x | np.ndarray | any | Sample abscissae, monotonic. |
y | np.ndarray | any | Integrand at the samples. |
| Returns | |||
| — | float | any | Integral over the sampled range, in units of |
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).