Process reference: soft_x_rays

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

Overview

Ported from the validated VEST SXR Viewer analysis tool (vest_sxr_viewer.py v5, module-level science functions; provenance: the 2026 VEST SXR thesis presentation). The 455xx SXR campaign acquires both digitizers – 22577 at the 4 o’clock port (toroidal 0 deg) and 17592 at the 12 o’clock port (120 deg) – at 125 MHz / 128 = 976562.5 Hz starting at the 285 ms trigger.

Every routine here takes plain arrays (time in seconds, data shaped (n_channels, n_samples)); nothing reads a CSV or an ODS except the explicit ODS pairing helper. Vacuum-shot PF-noise removal is a standalone optional step (sxr_subtract_vacuum_reference): no other routine applies it, so the main processing path is always baseline -> window -> filter on exactly the data the caller supplied.

Generic spectral analysis (PSD, spectral index, spectrogram) is not here – use vaft.process.fluctuation on any prepared channel.

The forward direction is here too: synthetic_island_soft_x_rays turns a prescribed magnetic island on an equilibrium into chord signals (issue #886), through vaft.process.magnetic_island and vaft.process.line_of_sight. It is machine-independent; VEST chords come from vaft.machine_mapping.soft_x_rays.sxr_sightlines.

Functions

hilbert_instantaneous_phaseInstantaneous phase and envelope of a band-limited signal at one time. Convention
load_te_ratio_calibrationLoad a ratio-to-temperature table and build its interpolator. Convention
rank_toroidal_mode_numbersRank candidate toroidal mode numbers from phases measured at two locations. Convention
sxr_band_signalsBaseline, window and band-pass chord signals into named frequency bands. Convention
sxr_baseline_correctionZero each channel against the quiet tail after the plasma. Convention
sxr_cwt_spectrogramContinuous-wavelet magnitude scalogram of one trace. Convention
sxr_electron_temperatureElectron temperature from the two-filter signal ratio, chord by chord. Convention
sxr_subtract_vacuum_referenceSubtract a low-passed vacuum shot to remove poloidal-field pickup. Convention
sxr_te_pairs_from_odsPair the two filters’ channels of one array by the chord they view. Convention
synthetic_island_soft_x_raysLine-integrated soft-X-ray signals of a rotating island on an equilibrium.

hilbert_instantaneous_phase

Machine-independentConvention-sensitive source
hilbert_instantaneous_phase(signal, time, t_eval)
Show source vaft/process/soft_x_rays.py, lines 708–759
def hilbert_instantaneous_phase(signal, time, t_eval: float):
    """Instantaneous phase and envelope of a band-limited signal at one time.

    Parameters
    ----------
    signal : array_like
        A band-passed, roughly monochromatic trace [V].
    time : array_like
        Its time base [s].
    t_eval : float
        Time at which to report the phase [s].

    Returns
    -------
    tuple
        The phase in degrees and the envelope in volts at the nearest sample, that
        sample's index, and the full phase and envelope series [-].

    Convention
    ----------
    Phase comes from the analytic signal and is wrapped to the principal branch,
    so it runs within a full turn and jumps rather than accumulating. The sample
    nearest the requested time is used; no interpolation.

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

    Limitations
    -----------
    **Meaningful only for a band-passed input.** The analytic signal of a
    broadband trace has a phase that does not correspond to any single mode, so
    filter first with :func:`sxr_band_signals`. The Hilbert transform is also
    poorly behaved at the record edges, so a time near either end is unreliable.

    Provenance
    ----------
    .. [1] The analytic-signal phase estimate used by the validated VEST SXR
       viewer's mode analysis.
    """
    time = np.asarray(time, dtype=float)
    analytic = hilbert(np.asarray(signal, dtype=float))
    index = int(np.argmin(np.abs(time - float(t_eval))))
    phase_series = np.degrees(np.angle(analytic))
    envelope_series = np.abs(analytic)
    return (
        float(phase_series[index]),
        float(envelope_series[index]),
        index,
        phase_series,
        envelope_series,
    )

Instantaneous phase and envelope of a band-limited signal at one time.

NameTypeUnitDescription
signalarray_likeV

A band-passed, roughly monochromatic trace.

timearray_likes

Its time base.

t_evalfloats

Time at which to report the phase.

Returns
—tuple-

The phase in degrees and the envelope in volts at the nearest sample, that sample’s index, and the full phase and envelope series.

Convention

Phase comes from the analytic signal and is wrapped to the principal branch, so it runs within a full turn and jumps rather than accumulating. The sample nearest the requested time is used; no interpolation.

Applicability · Limitations · Provenance

Applicability

Machine-independent.

Limitations

Meaningful only for a band-passed input. The analytic signal of a broadband trace has a phase that does not correspond to any single mode, so filter first with sxr_band_signals. The Hilbert transform is also poorly behaved at the record edges, so a time near either end is unreliable.

Provenance

  1. The analytic-signal phase estimate used by the validated VEST SXR viewer’s mode analysis.

load_te_ratio_calibration

Machine-independentConvention-sensitive source
load_te_ratio_calibration(path=None)
Show source vaft/process/soft_x_rays.py, lines 383–449
def load_te_ratio_calibration(path=None):
    """Load a ratio-to-temperature table and build its interpolator.

    Parameters
    ----------
    path : str or path-like, optional
        Calibration table with ``te`` and ``ratio`` columns. Defaults to the
        packaged VEST beryllium/aluminium table [-].

    Returns
    -------
    tuple
        The interpolator mapping ratio to temperature, the temperature column in
        electron-volts, and the ratio column, dimensionless [-].

    Raises
    ------
    ValueError
        The table lacks either column, or holds fewer than two valid points.

    Convention
    ----------
    **The interpolator extrapolates rather than clipping**, so a ratio outside the
    table returns an extrapolated temperature, not the nearest tabulated one. A
    caller that needs out-of-range samples rejected must test the ratio itself.
    Column names are matched case-insensitively after stripping.

    Defaults
    --------
    The packaged VEST beryllium/aluminium table is a diagnostic calibration,
    shipped with the package and described in the data directory's own notes.

    Applicability
    -------------
    Machine-independent.  The table is an argument; only the packaged default is
    the VEST filter pair, and a different filter combination supplies its own.

    Limitations
    -----------
    Non-finite rows are dropped silently. The two-filter method assumes a thermal
    spectrum, so the temperature it returns is meaningless for a non-thermal one.

    Provenance
    ----------
    .. [1] The packaged VEST beryllium/aluminium ratio calibration,
       ``vaft/data/legacy/sxr_te_ratio_be_al.csv``.
    """
    if path is None:
        from vaft.data.resources import data_path

        path = data_path(TE_RATIO_TABLE)
    table = pd.read_csv(path)
    columns = {c.strip().lower(): c for c in table.columns}
    if "te" not in columns or "ratio" not in columns:
        raise ValueError(
            "the calibration table needs 'te' and 'ratio' columns; "
            f"found {list(table.columns)}"
        )
    te = table[columns["te"]].to_numpy(dtype=float)
    ratio = table[columns["ratio"]].to_numpy(dtype=float)
    good = np.isfinite(te) & np.isfinite(ratio)
    if good.sum() < 2:
        raise ValueError("the calibration table holds fewer than two valid points.")
    interpolator = interp1d(
        ratio[good], te[good], fill_value="extrapolate", bounds_error=False
    )
    return interpolator, te[good], ratio[good]

Load a ratio-to-temperature table and build its interpolator.

NameTypeUnitDescription
pathstr or path-like, optional-

Calibration table with te and ratio columns. Defaults to the packaged VEST beryllium/aluminium table.

Returns
—tuple-

The interpolator mapping ratio to temperature, the temperature column in electron-volts, and the ratio column, dimensionless.

Convention

The interpolator extrapolates rather than clipping, so a ratio outside the table returns an extrapolated temperature, not the nearest tabulated one. A caller that needs out-of-range samples rejected must test the ratio itself. Column names are matched case-insensitively after stripping.

Raises

  • ValueError The table lacks either column, or holds fewer than two valid points.
Defaults · Applicability · Limitations · Provenance

Defaults

The packaged VEST beryllium/aluminium table is a diagnostic calibration, shipped with the package and described in the data directory’s own notes.

Applicability

Machine-independent. The table is an argument; only the packaged default is the VEST filter pair, and a different filter combination supplies its own.

Limitations

Non-finite rows are dropped silently. The two-filter method assumes a thermal spectrum, so the temperature it returns is meaningless for a non-thermal one.

Provenance

  1. The packaged VEST beryllium/aluminium ratio calibration, vaft/data/legacy/sxr_te_ratio_be_al.csv.

rank_toroidal_mode_numbers

Machine-independentConvention-sensitive source
rank_toroidal_mode_numbers(theta_a_deg, phase_a_deg, theta_b_deg, phase_b_deg, n_max)

Rank candidate toroidal mode numbers from phases measured at two locations.

NameTypeUnitDescription
theta_a_degfloatdeg

Toroidal angle of the first observation point.

phase_a_degfloatdeg

Measured phase there.

theta_b_degfloatdeg

Toroidal angle of the second point.

phase_b_degfloatdeg

Measured phase there.

n_maxint-

Largest magnitude of mode number to consider.

Returns
—tuple of ToroidalModeCandidatedeg

Every candidate with its anchor offset and wrapped residual, best first

Convention

For each candidate the wrapped phase-versus-angle line is anchored exactly at the first point and scored by the wrapped residual at the second. Zero is excluded, since it carries no toroidal structure. Candidates sort by absolute residual, then by absolute mode number, and a positive mode number wins a remaining tie over its negative counterpart, which is a tie-break, not physics.

Raises

  • ValueError The two points share a toroidal angle, so no mode number can be inferred.
Applicability · Limitations · Provenance

Applicability

Machine-independent. The port angles are arguments; the VEST pair used in practice is a worked example, not a built-in.

Limitations

Two points cannot resolve aliasing. A candidate and one differing by 360/|dtheta| produce identical residuals; for the VEST ports at zero and 120 degrees that period is three. Every degenerate candidate is returned with an equal residual and none is physically privileged; choosing among them needs independent information such as frequency scaling, mode structure, or a third port.

The phases must come from the same band and the same filtering convention, or the residual compares two different quantities.

Provenance

  1. The two-point toroidal mode-number estimate of the validated VEST SXR viewer, and its documented aliasing degeneracy.

sxr_band_signals

Machine-independentConvention-sensitive source
sxr_band_signals(time, data, *, baseline_start, bands, fs, order=2, time_range=None, channels=None, dead_channels=(), zero_phase=True)

Baseline, window and band-pass chord signals into named frequency bands.

NameTypeUnitDescription
timearray_likes

Sample times, shared by every channel.

dataarray_likeV

Signals as (n_channels, n_samples), or one trace.

baseline_startfloat or Nones

Start of the quiet tail, or None when the data is already corrected

bandsmapping of str to sequence of floatHz

Named (f_low, f_high) pairs; the names are the caller’s.

fsfloatHz

Sample rate.

orderint, optional-

Butterworth order for every band.

time_rangesequence of float, optionals

Analysis window as (t_min, t_max).

channelssequence of int, optional-

Row indices to process; all rows when omitted.

dead_channelssequence of int, optional-

Rows whose band output is forced to zero.

zero_phasebool, optional-

True for zero-phase forward-backward filtering via scipy.signal.filtfilt, preserving temporal alignment; False for causal filtering introducing filter group delay.

Returns
—SXRBandResultV

The windowed time base, the raw windowed signals, one array per band, and the channel indices selected.

Convention

The band-pass is zero phase by default (zero_phase=True), so no feature moves in time. That matters because the phase read off this output by hilbert_instantaneous_phase feeds a toroidal mode number. Pass zero_phase=False if causal filtering is explicitly required.

A dead channel’s band output is zeroed but its raw trace is kept, so it stays visible in a raw map instead of disappearing.

Raises

  • ValueError The time range selects no samples.
Processing steps · Defaults · Applicability · Limitations · Provenance

Processing steps

  1. Baseline-correct, unless the caller says the data already is.
  2. Restrict to the analysis window.
  3. Select the requested channels.
  4. Band-pass each into every named band, substituting zeros for dead channels.

Defaults

order = 2 is a numerical convenience.

Applicability

Machine-independent.

Limitations

Vacuum-reference correction is not applied; run sxr_subtract_vacuum_reference first when it is wanted. Band edges are passed straight to the filter, so a band reaching the Nyquist frequency raises from SciPy rather than being clipped. Filtering is applied directly to the windowed segment (preserving legacy viewer parity); under zero_phase=True, boundary transients occur at both edges of time_range extending over the filter settling time. Windows shorter than the filter padding length (3 * (2 * order + 1) samples, 15 for order 2) raise from SciPy under zero_phase=True.

Provenance

  1. The validated VEST SXR viewer’s band decomposition.

sxr_baseline_correction

Machine-independentConvention-sensitive source
sxr_baseline_correction(time, data, baseline_start)
Show source vaft/process/soft_x_rays.py, lines 96–156
def sxr_baseline_correction(time, data, baseline_start: float) -> np.ndarray:
    """Zero each channel against the quiet tail after the plasma.

    Parameters
    ----------
    time : array_like
        Sample times, shared by every channel [s].
    data : array_like
        Signals as ``(n_channels, n_samples)``, or one trace [V].
    baseline_start : float
        Time from which the record is taken to be quiet [s].

    Returns
    -------
    np.ndarray
        The signals with each channel's own zero level removed [V].

    Raises
    ------
    ValueError
        The time base and the sample axis disagree in length.

    Convention
    ----------
    The zero level is per channel, not shared, because the detectors have
    different dark levels. The mean of the tail is used, not a fitted trend, so a
    drift through the shot survives.

    Defaults
    --------
    When no sample lies at or beyond *baseline_start* the last 1000 samples are
    used instead. That fallback is a validated-workflow default carried from the
    VEST SXR viewer.

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

    Limitations
    -----------
    Assumes the tail is genuinely quiet. A record that ends while the plasma is
    still radiating has its baseline biased by that signal, and the fallback makes
    that silent rather than an error.

    Provenance
    ----------
    .. [1] The validated VEST SXR viewer, whose baseline convention and
       last-1000-sample fallback this reproduces.
    """
    time = np.asarray(time, dtype=float)
    values = _as_channel_matrix(data)
    if time.size != values.shape[1]:
        raise ValueError(
            f"time and data must share the sample axis; got {time.size} and {values.shape[1]}"
        )
    mask = time >= float(baseline_start)
    if np.any(mask):
        baseline = values[:, mask].mean(axis=1, keepdims=True)
    else:
        baseline = values[:, -1000:].mean(axis=1, keepdims=True)
    return values - baseline

Zero each channel against the quiet tail after the plasma.

NameTypeUnitDescription
timearray_likes

Sample times, shared by every channel.

dataarray_likeV

Signals as (n_channels, n_samples), or one trace.

baseline_startfloats

Time from which the record is taken to be quiet.

Returns
—np.ndarrayV

The signals with each channel’s own zero level removed.

Convention

The zero level is per channel, not shared, because the detectors have different dark levels. The mean of the tail is used, not a fitted trend, so a drift through the shot survives.

Raises

  • ValueError The time base and the sample axis disagree in length.
Defaults · Applicability · Limitations · Provenance

Defaults

When no sample lies at or beyond baseline_start the last 1000 samples are used instead. That fallback is a validated-workflow default carried from the VEST SXR viewer.

Applicability

Machine-independent.

Limitations

Assumes the tail is genuinely quiet. A record that ends while the plasma is still radiating has its baseline biased by that signal, and the fallback makes that silent rather than an error.

Provenance

  1. The validated VEST SXR viewer, whose baseline convention and last-1000-sample fallback this reproduces.

sxr_cwt_spectrogram

Machine-independentConvention-sensitive source
sxr_cwt_spectrogram(signal, fs, f0, f1, n_freq)
Show source vaft/process/soft_x_rays.py, lines 849–919
def sxr_cwt_spectrogram(signal, fs: float, f0: float, f1: float, n_freq: int):
    """Continuous-wavelet magnitude scalogram of one trace.

    Parameters
    ----------
    signal : array_like
        The trace [V].
    fs : float
        Sample rate [Hz].
    f0 : float
        Lowest frequency of the scalogram [Hz].
    f1 : float
        Highest frequency [Hz].
    n_freq : int
        Number of frequencies between them [-].

    Returns
    -------
    tuple of np.ndarray
        The frequency axis, ascending, and the magnitude as
        ``(n_freq, n_samples)`` [-].

    Raises
    ------
    ImportError
        The optional wavelet package is not installed.

    Convention
    ----------
    Frequencies are returned ascending, which reverses the underlying library's
    own descending order, and the magnitude rows are reversed to match. A wavelet
    transform trades frequency resolution for time resolution as frequency rises,
    unlike the fixed-window short-time transform.

    Defaults
    --------
    Single-threaded execution is a numerical convenience, keeping the call
    deterministic and avoiding a thread pool inside a per-channel loop.

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

    Limitations
    -----------
    Needs an optional package that is not a VAFT dependency. The short-time
    transform in :func:`vaft.process.fluctuation.compute_spectrogram` covers the
    default time-frequency need without it. The input is cast to single precision
    by that library.

    Provenance
    ----------
    .. [1] The optional ``fcwt`` continuous-wavelet implementation; the
       alternative path is :mod:`vaft.process.fluctuation`.
    """
    try:
        import fcwt as fcwt_lib
    except ImportError as error:
        raise ImportError(
            "the optional 'fcwt' package is not installed; run 'pip install fcwt' "
            "or use vaft.process.fluctuation.compute_spectrogram (STFT) instead."
        ) from error
    frequencies, out = fcwt_lib.cwt(
        np.asarray(signal, dtype=np.float32),
        int(fs),
        float(f0),
        float(f1),
        int(n_freq),
        nthreads=1,
    )
    return np.asarray(frequencies)[::-1], np.abs(out[::-1, :])

Continuous-wavelet magnitude scalogram of one trace.

NameTypeUnitDescription
signalarray_likeV

The trace.

fsfloatHz

Sample rate.

f0floatHz

Lowest frequency of the scalogram.

f1floatHz

Highest frequency.

n_freqint-

Number of frequencies between them.

Returns
—tuple of np.ndarray-

The frequency axis, ascending, and the magnitude as (n_freq, n_samples).

Convention

Frequencies are returned ascending, which reverses the underlying library’s own descending order, and the magnitude rows are reversed to match. A wavelet transform trades frequency resolution for time resolution as frequency rises, unlike the fixed-window short-time transform.

Raises

  • ImportError The optional wavelet package is not installed.
Defaults · Applicability · Limitations · Provenance

Defaults

Single-threaded execution is a numerical convenience, keeping the call deterministic and avoiding a thread pool inside a per-channel loop.

Applicability

Machine-independent.

Limitations

Needs an optional package that is not a VAFT dependency. The short-time transform in vaft.process.fluctuation.compute_spectrogram covers the default time-frequency need without it. The input is cast to single precision by that library.

Provenance

  1. The optional fcwt continuous-wavelet implementation; the alternative path is vaft.process.fluctuation.

sxr_electron_temperature

Machine-independentConvention-sensitive source
sxr_electron_temperature(time, data, pairs, *, calibration, baseline_start, fs, lowpass_cutoff=50000.0, al_gain=1.07, al_threshold=0.1, detrend_window=400, order=2, zero_phase=True, time_range=None)

Semantics: produces electron_temperature (ontology)

Electron temperature from the two-filter signal ratio, chord by chord.

NameTypeUnitDescription
timearray_likes

Sample times, shared by every channel.

dataarray_likeV

Signals as (n_channels, n_samples).

pairssequence of sequence of int-

(beryllium_index, aluminium_index) pairs, one per chord.

calibrationcallable-

Maps a signal ratio to a temperature; see load_te_ratio_calibration.

baseline_startfloat or Nones

Start of the quiet tail, or None when already corrected.

fsfloatHz

Sample rate.

lowpass_cutofffloat, optionalHz

Conditioning low-pass cut-off.

al_gainfloat, optional-

Relative sensitivity of the aluminium channel.

al_thresholdfloat, optionalV

Conditioned aluminium level below which a sample is invalid.

detrend_windowint, optional-

Width of the centred rolling trend, in samples.

orderint, optional-

Butterworth order.

zero_phasebool, optional-

True for zero-phase forward-backward filtering via scipy.signal.filtfilt, preserving temporal alignment; False for causal filtering introducing filter group delay.

time_rangesequence of float, optionals

Analysis window as (t_min, t_max).

Returns
—SXRTemperatureResult-

The windowed time base, the temperature per chord in electron-volts, and its percentage deviation from the rolling trend.

Convention

The low pass is zero phase by default (zero_phase=True), matching the convention of sxr_band_signals and preserving exact temporal alignment with other diagnostics and fast MHD events. Pass zero_phase=False to reproduce legacy causal filtering behavior with filter group delay.

The relative fluctuation is a percentage of the rolling trend, and the trend is centred, so it uses samples on both sides.

Processing steps · Defaults · Applicability · Limitations · Provenance

Processing steps

  1. Baseline-correct, unless the caller says the data already is.
  2. Restrict to the analysis window.
  3. Low-pass both channels of each pair.
  4. Scale the aluminium channel by its relative sensitivity.
  5. Take the ratio and map it through the calibration.
  6. Subtract a centred rolling trend to get the relative fluctuation.
  7. Invalidate samples whose conditioned aluminium level is at or below the threshold.

Defaults

The 50 kHz cut-off, the 1.07 aluminium gain, the 0.10 V threshold and the 400-sample trend window are validated-workflow defaults, the settings of the validated VEST viewer. order = 2 is a numerical convenience.

Applicability

Machine-independent. The defaults are VEST viewer practice and the calibration is supplied by the caller.

Limitations

The two-filter method assumes a thermal spectrum. Invalid samples are NaN rather than removed, so a caller must honour them. Vacuum-reference correction is not applied here.

Provenance

  1. The validated VEST SXR viewer, whose conditioning settings, validity threshold and detrending this reproduces.

sxr_subtract_vacuum_reference

Machine-independentConvention-sensitive source
sxr_subtract_vacuum_reference(data, reference_data, *, cutoff, fs, order=2, zero_phase=True)

Subtract a low-passed vacuum shot to remove poloidal-field pickup.

An optional step. Nothing else in this module calls it, so the main path is always baseline, then window, then filter, on exactly the data the caller supplied.

NameTypeUnitDescription
dataarray_likeV

Plasma-shot signals as (n_channels, n_samples).

reference_dataarray_likeV

Vacuum-shot signals with the same channel count.

cutofffloatHz

Low-pass cut-off applied to the reference.

fsfloatHz

Sample rate.

orderint, optional-

Butterworth order.

zero_phasebool, optional-

True for zero-phase forward-backward filtering via scipy.signal.filtfilt, preserving temporal alignment; False for causal filtering introducing filter group delay.

Returns
—np.ndarrayV

The plasma signals with the pickup removed, truncated to the shorter record.

Convention

Pickup from the poloidal field coil ramps appears identically in a vacuum shot, so low-passing that record and subtracting it channel-wise removes the common drive while leaving plasma fluctuations untouched.

The low pass is zero phase by default (zero_phase=True), preserving exact temporal alignment with the un-delayed plasma shot. Pass zero_phase=False to reproduce legacy causal filtering behavior.

Both inputs should already be baseline-corrected.

Raises

  • ValueError The two inputs have different channel counts.
Defaults · Applicability · Limitations · Provenance

Defaults

order = 2 is a numerical convenience matching the rest of the module’s conditioning.

Applicability

Machine-independent.

Limitations

Assumes the vacuum shot reproduces the plasma shot’s coil programme. Records are truncated to the shorter of the two rather than aligned in time, so two records with different start triggers are subtracted misaligned. The reference is truncated to length n before filtering, so both leading and trailing edges are subject to filter boundary transients. Inputs shorter than the filter padding length (3 * (order + 1) samples for low-pass) raise from SciPy under zero_phase=True.

Provenance

  1. The validated VEST SXR viewer’s optional PF-noise removal step.

sxr_te_pairs_from_ods

VEST-specificConvention-sensitive source
sxr_te_pairs_from_ods(ods, array)
Show source vaft/process/soft_x_rays.py, lines 452–522
def sxr_te_pairs_from_ods(ods: Any, array: str) -> tuple[tuple[int, int], ...]:
    """Pair the two filters' channels of one array by the chord they view.

    Parameters
    ----------
    ods : ODS
        Carries ``soft_x_rays.channel`` with the identifiers the VEST mapper
        writes [-].
    array : str
        Which array to pair within [-].

    Returns
    -------
    tuple of tuple of int
        One ``(beryllium_index, aluminium_index)`` pair per shared chord, ordered
        by chord number [-].

    Raises
    ------
    ValueError
        No chord in that array carries both filters.

    Convention
    ----------
    Channel identifiers are ``{daq}:{array}:{filter}:{chord}``, written by the
    VEST digitizer mapper. Pairing on the chord number rather than on position in
    the channel list is what absorbs a per-block wiring reversal: the two filter
    blocks need not be ordered the same way.

    Returns index pairs, not data, so it changes no measurement.

    Applicability
    -------------
    VEST-specific.  Depends on the identifier grammar and the filter names written
    by :func:`vaft.machine_mapping.soft_x_rays.soft_x_rays_from_digitizer_csv`;
    another machine's mapper would need its own pairing rule.

    Limitations
    -----------
    A channel whose identifier does not split into four parts, or whose chord is
    not an integer, is skipped silently, so a mis-mapped channel is absent from
    the result rather than reported.

    Provenance
    ----------
    .. [1] :func:`vaft.machine_mapping.soft_x_rays.soft_x_rays_from_digitizer_csv`,
       which writes the identifiers this parses.
    """
    be: dict[int, int] = {}
    al: dict[int, int] = {}
    channels = ods["soft_x_rays.channel"]
    for index in range(len(channels)):
        identifier = str(channels[index]["identifier"])
        parts = identifier.split(":")
        if len(parts) != 4 or parts[1] != str(array):
            continue
        try:
            chord = int(parts[3])
        except ValueError:
            continue
        if parts[2] == "Be":
            be[chord] = index
        elif parts[2] == "Al":
            al[chord] = index
    shared = sorted(set(be) & set(al))
    if not shared:
        raise ValueError(
            f"array {array!r} has no chords with both Be and Al channels; "
            "is this a two-filter array mapped by soft_x_rays_from_digitizer_csv?"
        )
    return tuple((be[chord], al[chord]) for chord in shared)

Pair the two filters’ channels of one array by the chord they view.

NameTypeUnitDescription
odsODS-

Carries soft_x_rays.channel with the identifiers the VEST mapper writes.

arraystr-

Which array to pair within.

Returns
—tuple of tuple of int-

One (beryllium_index, aluminium_index) pair per shared chord, ordered by chord number.

Convention

Channel identifiers are {daq}:{array}:{filter}:{chord}, written by the VEST digitizer mapper. Pairing on the chord number rather than on position in the channel list is what absorbs a per-block wiring reversal: the two filter blocks need not be ordered the same way.

Returns index pairs, not data, so it changes no measurement.

Raises

  • ValueError No chord in that array carries both filters.
Applicability · Limitations · Provenance

Applicability

VEST-specific. Depends on the identifier grammar and the filter names written by vaft.machine_mapping.soft_x_rays.soft_x_rays_from_digitizer_csv; another machine’s mapper would need its own pairing rule.

Limitations

A channel whose identifier does not split into four parts, or whose chord is not an integer, is skipped silently, so a mis-mapped channel is absent from the result rather than reported.

Provenance

  1. vaft.machine_mapping.soft_x_rays.soft_x_rays_from_digitizer_csv, which writes the identifiers this parses.

synthetic_island_soft_x_rays

Machine-independent source
synthetic_island_soft_x_rays(equilibrium, island, sightlines, *, time=0.0, emissivity_profile=None, model='flatten', domain=None, amplitude=1.0, smoothing=0.1, hard_mask=False, keep_fields=True)

Line-integrated soft-X-ray signals of a rotating island on an equilibrium.

NameTypeUnitDescription
equilibriumEquilibriumData or source-

The axisymmetric equilibrium, the only source of plasma geometry; anything vaft.process.equilibrium.as_equilibrium accepts.

islandMagneticIslandSpec-

Mode numbers, outboard-midplane full width, phase and rotation.

sightlinesSightlines-

The chords, each in the poloidal plane at its own toroidal angle.

timefloat or array_like, optionals

Times at which to evaluate the island phase.

emissivity_profilecallable or tuple of array_like, optionalemissivity unit

Axisymmetric background epsilon_0(psi_N).

modelstr, optional-

"flatten" or "island", as in vaft.process.magnetic_island.island_emissivity.

domaintuple of array_like, optionalm

(R, Z) of a closed polygon, normally the limiter, that clips every chord.

amplitudefloat, optional- or emissivity unit

Island amplitude A.

smoothingfloat, optional-

Width of the island mask edge in helical-flux units.

hard_maskbool, optional-

Use a binary island mask.

keep_fieldsbool, optional-

Keep the emissivity fields on the result; False keeps only the brightness, for a long time series where the (time, plane, R, Z) arrays would dominate memory.

Returns
—SyntheticSXRResultemissivity unit times m

Time, the chord planes’ toroidal angles, the emissivity and its island part on the equilibrium grid per time and plane, and the chord brightness per time and channel.

Raises

  • ValueError The island cannot be placed on the equilibrium, or the emissivity model or profile is invalid.
Processing steps · Input semantics · Output semantics · Applicability · Limitations · Provenance

Processing steps

  1. Build the island topology once, which fixes the resonant surface, the straight-field-line angle and the displacement.
  2. Group the chords by toroidal angle and build one path-length operator per plane, clipped to the domain.
  3. For every time and plane, rephase the island, form the emissivity, and integrate it along that plane’s chords.

Input semantics

A prescribed island on a reconstructed or analytic equilibrium, and chord geometry from the machine mapping; no measured signal enters.

Output semantics

Synthetic chord brightness in the emissivity’s unit times metres, before any detector, filter or etendue response, comparable to measured signals only up to a per-channel calibration.

Applicability

Machine-independent.

Limitations

A forward model only: no inversion, no noise, pencil-beam chords. Each chord is assumed to lie in one poloidal plane. The island rotates rigidly at a fixed width.

Provenance

  1. Le, T. X. K. et al., Plasma Phys. Control. Fusion 68, 065003 (2026), a rotating-island SXR forward model of the same scope.
  2. Dreval, M. B. et al., Plasma Phys. Control. Fusion 63, 065006 (2021) and 65, 035001 (2023), for SXR forward modelling of MHD modes.
  3. Jang, J. Y. et al., Rev. Sci. Instrum. 93, 093506 (2022), the VEST SXR arrays.

results matching ""

    No results matching ""