Process reference: atomic
Generated from the docstrings of vaft.process.atomic:
5 public functions. Each entry is what
vaft.process.describe("atomic.<name>") prints.
All process categories
Overview
This module owns profile selection, time alignment, impurity-density
fallback and volume integration. Numerical atomic physics remains in
vaft.formula.atomic and ADF11 file access in
vaft.data.open_adas. Which impurity species to include and what
fraction to assume for them is machine policy, resolved by the pipeline
(vaft.omas.formula_wrapper.compute_power_balance from vest.yaml
on VEST) and passed in; this module holds no such number (issue #420).
| Symbol | Meaning | Unit |
|---|---|---|
| n_e | electron density | m^-3 |
| n_imp | impurity density | m^-3 |
| T_e | electron temperature | eV |
| L_Z | line-radiation cooling coefficient of species Z | W m^3 |
| epsilon | line emissivity, n_e n_imp L_Z | W/m^3 |
| P_line | radiated line power, int epsilon dV | W |
| V | cumulative enclosed plasma volume | m^3 |
| rho_tor_norm | normalized toroidal-flux radius | - |
Functions
compute_line_radiation_power_series | Line-radiation power for matched equilibrium slices, from the stored kinetic profiles. Convention |
compute_time_match_atol | An absolute time-matching tolerance adapted to the native time spacing. |
find_time_match_index | Index of the time closest to target_time, within the adaptive tolerance.
|
integrate_emissivity_profile | Integrate an emissivity profile to a total radiated power. Convention |
normalize_atomic_symbol | Normalize an element or ion label to a supported atomic symbol. |
No function matches.
compute_line_radiation_power_series
Machine-independentConvention-sensitive
source
compute_line_radiation_power_series(ods, eq_indices, eq_times, volume_series, line_radiation_species=None, impurity_fractions=None, Z_eff=None, *, return_assumptions=False)
Line-radiation power for matched equilibrium slices, from the stored kinetic profiles.
For each species and time slice the local emissivity is
epsilon = n_e n_imp L_Z(n_e, T_e) and the power P = int epsilon dV.
| Name | Type | Unit | Description |
|---|---|---|---|
ods | ODS | - | Carries |
eq_indices | list of int | - | Equilibrium slice indices to evaluate. |
eq_times | array_like | s | Time of each of those slices. |
volume_series | array_like | m^3 | Total plasma volume of each slice, for the integration fallback. |
line_radiation_species | list of str, optional | - | Species to include; |
impurity_fractions | dict of str to float, optional | - |
|
Z_eff | float, optional | - | Effective charge, used only to infer a single species’ fraction when nothing else is known. |
return_assumptions | bool, optional | - | Also return what each species’ density was taken from. |
| Returns | |||
| — | np.ndarray or tuple of (np.ndarray, dict) | W | Non-negative line-radiation power per |
Convention
The radial coordinate is rho_tor_norm throughout: the core_profiles
grid and the equilibrium profiles_1d both carry it and are interpolated
against each other in it. n_i for the impurity is fraction * n_e
where a fraction is used; Z_eff inference assumes hydrogenic main ions,
n_imp/n_e = (Z_eff - 1)/(Z(Z - 1)).
Raises
ValueErrorMismatchedeq_times/volume_serieslengths, non-finite times, an unrecognized species label, or a negative or non-finite fraction.
Processing steps · Input semantics · Output semantics · Defaults · Assumptions · Applicability · Limitations · Provenance
Processing steps
- Match each equilibrium time to a
core_profilesslice (find_time_match_index); an unmatched slice radiates nothing. - Interpolate
n_e,T_efrom the slice’srho_tor_normonto the equilibrium’srho_tor_norm(constant edge extrapolation). - For each species, take
n_imp / n_efrom, in order: the slice’s own ion density profile;impurity_fractions; a single-species inference fromZ_eff; else zero with one warning per species. L_Zfrom the ADAS tables [FORMULA];epsilon = L_Z n_e n_imp.integrate_emissivity_profileper species, summed; clipped at zero.
Input semantics
Stored kinetic profiles (fitted or synthetic) on their core_profiles
grid, plus the equilibrium grid and volume.
Output semantics
Synthetic: a modelled diagnostic-like quantity, not a measurement.
Defaults
impurity_fractions = None and line_radiation_species = None mean
no assumption. The VEST carbon and oxygen fractions that used to be
this function’s defaults are machine-specific settings, each an assumed
value in vest.yaml resolved by the pipeline. Z_eff = None
disables the single-species inference, a numerical convenience.
Assumptions
Coronal equilibrium for L_Z; one impurity species at a time with no
mutual dilution; the core_profiles slice at the matched time describes
the equilibrium slice.
Applicability
Machine-independent. Species and fractions arrive as arguments.
Limitations
Every failure mode – no core_profiles, no matching time, no n_e
or T_e, ADAS data unavailable, no fraction known – yields zero for
that slice or species with a logged warning rather than an error, to keep
an offline power balance running; the returned assumptions are how a
caller can tell. A core_profiles slice stored on rho_pol_norm
only (no equilibrium at its time) has no rho_tor_norm and is skipped.
Provenance
- ADAS tables [ADAS] through the cooling-coefficient kernel [FORMULA].
compute_time_match_atol
Machine-independent
source
compute_time_match_atol(time_array, base_atol=1e-06)
Show source vaft/process/atomic.py, lines 61–105
def compute_time_match_atol(time_array: ndarray, base_atol: float = _DEFAULT_TIME_MATCH_ATOL) -> float:
"""An absolute time-matching tolerance adapted to the native time spacing.
``max(base_atol, 0.25 * min(diff(unique_finite_times)))``, which absorbs
floating-point drift in a time coordinate without ever matching an
adjacent physical slice.
Parameters
----------
time_array : np.ndarray
The time coordinate to be matched against [s].
base_atol : float, optional
Floor on the tolerance [s].
Returns
-------
float
The tolerance; ``base_atol`` when fewer than two distinct finite times
exist [s].
Raises
------
ValueError
``base_atol`` non-finite or negative.
Defaults
--------
``base_atol = 1e-6`` s and the factor ``0.25`` are numerical conveniences:
a quarter of the smallest spacing can never reach the neighbouring slice.
Applicability
-------------
Machine-independent.
"""
if not np.isfinite(base_atol) or base_atol < 0.0:
raise ValueError("base_atol must be finite and non-negative")
arr = np.asarray(time_array, dtype=float).reshape(-1)
arr = arr[np.isfinite(arr)]
if arr.size < 2:
return float(base_atol)
positive = np.diff(np.unique(np.sort(arr)))
positive = positive[positive > 0.0]
return float(max(base_atol, 0.25 * float(np.min(positive)))) if positive.size else float(base_atol)An absolute time-matching tolerance adapted to the native time spacing.
max(base_atol, 0.25 * min(diff(unique_finite_times))), which absorbs
floating-point drift in a time coordinate without ever matching an
adjacent physical slice.
| Name | Type | Unit | Description |
|---|---|---|---|
time_array | np.ndarray | s | The time coordinate to be matched against. |
base_atol | float, optional | s | Floor on the tolerance. |
| Returns | |||
| — | float | s | The tolerance; |
Raises
ValueErrorbase_atolnon-finite or negative.
Defaults · Applicability
Defaults
base_atol = 1e-6 s and the factor 0.25 are numerical conveniences:
a quarter of the smallest spacing can never reach the neighbouring slice.
Applicability
Machine-independent.
find_time_match_index
Machine-independent
source
find_time_match_index(time_array, target_time)
Show source vaft/process/atomic.py, lines 108–136
def find_time_match_index(time_array: ndarray, target_time: float) -> Optional[int]:
"""Index of the time closest to ``target_time``, within the adaptive tolerance.
Parameters
----------
time_array : np.ndarray
The time coordinate [s].
target_time : float
The time to match [s].
Returns
-------
int or None
Index of the closest time within :func:`compute_time_match_atol`;
``None`` for an empty array, a non-finite target, or no candidate within
tolerance [-].
Applicability
-------------
Machine-independent.
"""
arr = np.asarray(time_array, dtype=float).reshape(-1)
if arr.size == 0 or not np.isfinite(target_time):
return None
close = np.where(np.isclose(arr, float(target_time), rtol=0.0, atol=compute_time_match_atol(arr)))[0]
if close.size == 0:
return None
return int(close[np.argmin(np.abs(arr[close] - float(target_time)))])Index of the time closest to target_time, within the adaptive tolerance.
| Name | Type | Unit | Description |
|---|---|---|---|
time_array | np.ndarray | s | The time coordinate. |
target_time | float | s | The time to match. |
| Returns | |||
| — | int or None | - | Index of the closest time within |
Applicability
Applicability
Machine-independent.
integrate_emissivity_profile
Machine-independentConvention-sensitive
source
integrate_emissivity_profile(emissivity_profile, volume_profile, total_volume)
Show source vaft/process/atomic.py, lines 280–342
def integrate_emissivity_profile(
emissivity_profile: ndarray,
volume_profile: Optional[ndarray],
total_volume: float,
) -> float:
"""Integrate an emissivity profile to a total radiated power.
Parameters
----------
emissivity_profile : np.ndarray
Local emissivity on the radial grid [W/m^3].
volume_profile : np.ndarray or None
Cumulative enclosed volume on the same grid, or ``None`` [m^3].
total_volume : float
Total plasma volume, for the fallback [m^3].
Returns
-------
float
``P = int epsilon dV``, or the fallback, or ``0`` [W].
Processing steps
----------------
1. If ``volume_profile`` is given, matches the emissivity's shape and has at
least two finite points spanning a non-zero volume: trapezoidal
integration of ``epsilon`` against ``V``, in volume order.
2. Else, if ``total_volume`` is finite and positive:
``nanmean(epsilon) * total_volume``.
3. Else ``0``.
Convention
----------
``volume_profile`` is the *cumulative enclosed* volume ``V(rho)``, so
``dV`` is its difference; it is not a shell volume.
Applicability
-------------
Machine-independent.
Limitations
-----------
The two fallbacks are silent: a volume profile of the wrong length drops to
the mean-times-volume estimate, and a missing volume to zero, with no
warning from this function.
"""
emissivity = np.asarray(emissivity_profile, dtype=float)
finite_emissivity = np.isfinite(emissivity)
if not np.any(finite_emissivity):
return 0.0
if volume_profile is not None:
volume = np.asarray(volume_profile, dtype=float)
if volume.shape == emissivity.shape:
finite = finite_emissivity & np.isfinite(volume)
if np.count_nonzero(finite) >= 2:
volume_finite, emissivity_finite = volume[finite], emissivity[finite]
order = np.argsort(volume_finite)
volume_finite, emissivity_finite = volume_finite[order], emissivity_finite[order]
if np.ptp(volume_finite) > 0.0:
return float(trapz_compat(emissivity_finite, x=volume_finite))
if np.isfinite(total_volume) and total_volume > 0.0:
return float(np.nanmean(emissivity[finite_emissivity]) * total_volume)
return 0.0Integrate an emissivity profile to a total radiated power.
| Name | Type | Unit | Description |
|---|---|---|---|
emissivity_profile | np.ndarray | W/m^3 | Local emissivity on the radial grid. |
volume_profile | np.ndarray or None | m^3 | Cumulative enclosed volume on the same grid, or |
total_volume | float | m^3 | Total plasma volume, for the fallback. |
| Returns | |||
| — | float | W |
|
Convention
volume_profile is the cumulative enclosed volume V(rho), so
dV is its difference; it is not a shell volume.
Processing steps · Applicability · Limitations
Processing steps
- If
volume_profileis given, matches the emissivity’s shape and has at least two finite points spanning a non-zero volume: trapezoidal integration ofepsilonagainstV, in volume order. - Else, if
total_volumeis finite and positive:nanmean(epsilon) * total_volume. - Else
0.
Applicability
Machine-independent.
Limitations
The two fallbacks are silent: a volume profile of the wrong length drops to the mean-times-volume estimate, and a missing volume to zero, with no warning from this function.
normalize_atomic_symbol
Machine-independent
source
normalize_atomic_symbol(label)
Show source vaft/process/atomic.py, lines 139–179
def normalize_atomic_symbol(label: Any) -> Optional[str]:
"""Normalize an element or ion label to a supported atomic symbol.
Full English names and charge-decorated labels are accepted --
``carbon6+`` gives ``C``, ``neon`` gives ``Ne``.
Parameters
----------
label : str, bytes or None
The label as found in an ODS ``ion.label`` or a species list [-].
Returns
-------
str or None
The symbol, or ``None`` for an unrecognized label so the caller decides
whether absence is fatal [-].
Applicability
-------------
Machine-independent.
Limitations
-----------
Only the elements in the module's table are recognized (H through W, the
common tokamak species); isotopes D and T map to their own symbols.
"""
if label is None:
return None
text = label.decode("utf-8", errors="ignore") if isinstance(label, bytes) else str(label)
match = re.match(r"([A-Za-z]+)", text.strip())
if not match:
return None
token = match.group(1)
named = _ELEMENT_NAMES.get(token.lower())
if named is not None:
return named
exact = token[0].upper() + token[1:].lower()
if exact in _ATOMIC_NUMBERS:
return exact
return NoneNormalize an element or ion label to a supported atomic symbol.
Full English names and charge-decorated labels are accepted –
carbon6+ gives C, neon gives Ne.
| Name | Type | Unit | Description |
|---|---|---|---|
label | str, bytes or None | - | The label as found in an ODS |
| Returns | |||
| — | str or None | - | The symbol, or |
Applicability · Limitations
Applicability
Machine-independent.
Limitations
Only the elements in the module’s table are recognized (H through W, the common tokamak species); isotopes D and T map to their own symbols.