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).

SymbolMeaningUnit
n_eelectron densitym^-3
n_impimpurity densitym^-3
T_eelectron temperatureeV
L_Zline-radiation cooling coefficient of species ZW m^3
epsilonline emissivity, n_e n_imp L_ZW/m^3
P_lineradiated line power, int epsilon dVW
Vcumulative enclosed plasma volumem^3
rho_tor_normnormalized toroidal-flux radius-

Functions

compute_line_radiation_power_seriesLine-radiation power for matched equilibrium slices, from the stored kinetic profiles. Convention
compute_time_match_atolAn absolute time-matching tolerance adapted to the native time spacing.
find_time_match_indexIndex of the time closest to target_time, within the adaptive tolerance.
integrate_emissivity_profileIntegrate an emissivity profile to a total radiated power. Convention
normalize_atomic_symbolNormalize an element or ion label to a supported atomic symbol.

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.

NameTypeUnitDescription
odsODS-

Carries core_profiles.profiles_1d and equilibrium.time_slice.

eq_indiceslist of int-

Equilibrium slice indices to evaluate.

eq_timesarray_likes

Time of each of those slices.

volume_seriesarray_likem^3

Total plasma volume of each slice, for the integration fallback.

line_radiation_specieslist of str, optional-

Species to include; None for every species that has a configured fraction or an ion density profile in the ODS.

impurity_fractionsdict of str to float, optional-

n_imp / n_e per species, used when the ODS carries no profile for it; None means no assumption.

Z_efffloat, optional-

Effective charge, used only to infer a single species’ fraction when nothing else is known.

return_assumptionsbool, 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 eq_indices entry, and with return_assumptions a dict of species, per-species impurity_source (profile, configured, inferred_from_zeff or none), the impurity_fraction used, and z_eff.

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

  • ValueError Mismatched eq_times / volume_series lengths, 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

  1. Match each equilibrium time to a core_profiles slice (find_time_match_index); an unmatched slice radiates nothing.
  2. Interpolate n_e, T_e from the slice’s rho_tor_norm onto the equilibrium’s rho_tor_norm (constant edge extrapolation).
  3. For each species, take n_imp / n_e from, in order: the slice’s own ion density profile; impurity_fractions; a single-species inference from Z_eff; else zero with one warning per species.
  4. L_Z from the ADAS tables [FORMULA]; epsilon = L_Z n_e n_imp.
  5. integrate_emissivity_profile per 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

  1. 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.

NameTypeUnitDescription
time_arraynp.ndarrays

The time coordinate to be matched against.

base_atolfloat, optionals

Floor on the tolerance.

Returns
—floats

The tolerance; base_atol when fewer than two distinct finite times exist.

Raises

  • ValueError base_atol non-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.

NameTypeUnitDescription
time_arraynp.ndarrays

The time coordinate.

target_timefloats

The time to match.

Returns
—int or None-

Index of the closest time within compute_time_match_atol; None for an empty array, a non-finite target, or no candidate within tolerance.

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.0

Integrate an emissivity profile to a total radiated power.

NameTypeUnitDescription
emissivity_profilenp.ndarrayW/m^3

Local emissivity on the radial grid.

volume_profilenp.ndarray or Nonem^3

Cumulative enclosed volume on the same grid, or None.

total_volumefloatm^3

Total plasma volume, for the fallback.

Returns
—floatW

P = int epsilon dV, or the fallback, or 0.

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

  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.

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 None

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.

NameTypeUnitDescription
labelstr, 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 · 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.

results matching ""

    No results matching ""