Process reference: resonant_coil_response
Generated from the docstrings of vaft.process.resonant_coil_response:
6 public functions. Each entry is what
vaft.process.describe("resonant_coil_response.<name>") prints.
All process categories
Overview
On a fixed equilibrium, toroidal mode n and plasma-response model, the
complex resonant flux GPEC writes at the rational surfaces (Phi_res total,
Phi_res_v vacuum; area-normalized, tesla) responds linearly to the coil
excitation. This module holds that operator, built from per-group unit runs
and verified on held-out runs rather than assumed, and the optimizations
issue #1884 asks of it (issue #1178 umbrella). It runs no solver.
resonant_response_operator ─▶ H from the unit runs e_k, Phi = H conj(c)
qualify_response_operator ──▶ held-out complex runs -> qualified / rejected / insufficient
window_weights ─────────────▶ W: mean |Phi|^2 over a resonant window (RMS^2)
ResonantResponseOperator.quadratic(W) ─▶ Q with J = c^H Q c, for the generic optima of
vaft.process.ntv_coil_response (two-group, budgeted, fixed-amplitude)
match_resonant_target ──────▶ stage B: regularized complex least squares, with rank and SVD
constrained_window_extremum ─▶ stage D: maximize one window under a cap on another and a budget
| Symbol | Meaning | Unit |
|---|---|---|
| K | number of independently driven coil groups | - |
| N_s | number of rational surfaces | - |
| c | VAFT excitation phasor per group, c = A exp(+i delta) | phasor |
| Phi | complex resonant flux per surface, area-normalized | T |
| H | N_s x K operator, Phi = H conj(c) | T / phasor |
| W | N_s x N_s Hermitian positive-semidefinite surface weights | - |
| J | quadratic resonant metric, (H conj c)^H W (H conj c) = c^H Q c | T^2 |
| D | K x K Hermitian positive-definite excitation metric | - |
The response is linear in conj(c). VAFT’s excitation
(vaft.process.coil_operating_space.COIL_EXCITATION_CONVENTION) is
I_k = A cos(n phi_k + delta) = Re(c exp(+i n phi_k)); GPEC decomposes in
exp(-i n phi), whose coefficient is conj(c) / 2. Measured on the
DIII-D-like example (#1884): Phi(i e_k) = -i Phi(e_k) exactly, and
Phi = H conj(c) reproduces every held-out run to 4e-6. Hence Q =
conj(H^H W H) for a quadratic metric in c; using H^H W H instead
would mirror every optimal relative phase.
Squared, not RMS, is optimized. J with window_weights is the
window’s mean squared magnitude; its square root is
vaft.process.perturbation.rms_resonant_field for the same surfaces,
so the ordering is the same for a fixed non-empty window. Results report
which one they hold.
Total and vacuum stay apart. Phi_res and Phi_res_v get separate
operators; nothing here subtracts them.
Functions
constrained_window_extremum | Stage D: maximize c^H Q_o c with c^H Q_c c <= limit and c^H D c = budget.
Convention |
match_resonant_target | Stage B: the excitation whose resonant response best matches a complex target. Convention |
qualify_response_operator | Qualify or reject Phi = H conj(c) against held-out runs.
Convention |
resonant_operating_space | Direct resonant-field RMS per window (and a qualified operator’s predictions) as a #1178 result. Convention |
resonant_response_operator | The operator H from the responses to the unit excitations c = e_k.
Convention |
window_weights | Surface weights whose quadratic metric is the window’s mean squared |Phi|.
Convention |
No function matches.
constrained_window_extremum
Machine-independentConvention-sensitive
source
constrained_window_extremum(Q_objective, Q_constraint, *, metric, budget, limit, tolerance=1e-10)
Stage D: maximize c^H Q_o c with c^H Q_c c <= limit and c^H D c = budget.
| Name | Type | Unit | Description |
|---|---|---|---|
Q_objective | numpy.ndarray | T^2 / phasor^2 | Hermitian positive-semidefinite metric to maximize, e.g. the edge window’s |
Q_constraint | numpy.ndarray | T^2 / phasor^2 | Hermitian positive-semidefinite metric to cap, e.g. the core window’s. |
metric | numpy.ndarray | - | Hermitian positive-definite excitation metric |
budget | float | phasor^2 |
|
limit | float | T^2 | Cap on |
tolerance | float, optional | - | Relative tolerance of the multiplier bisection; a numerical-method control. |
| Returns | |||
solution | dict | - |
|
Convention
Squared metrics; report sqrt for an RMS. The answer is defined up to
a common phase.
Raises
ValueErrorShapes disagree, a matrix is not Hermitian,Dis not positive definite, orbudget/limitis not positive / negative.
Processing steps · Applicability · Limitations · Provenance
Processing steps
infeasiblewhen even the leastc^H Q_c con the budget (the bottom generalized eigenvector of(Q_c, D)) exceeds the cap.- For a multiplier
mu, the top generalized eigenspace of(Q_o - mu Q_c, D);mu = 0if some excitation in it meets the cap. - Otherwise bisect
muon the least constraint within that space, and at the convergedmucombine the space’s extreme directions so the constraint lands on the cap (the objective is then maximal).
Applicability
Machine-independent.
Limitations
The multiplier route is exact for two Hermitian quadratic forms on a
complex sphere (no duality gap); degenerate top eigenvalues, which occur
whenever the cap’s null space lies inside the objective’s (e.g. more
groups than capped surfaces), are handled by working on the eigenspace.
Eigenvalues within 1e-9 of the spectral scale are treated as equal.
Sector-current limits are not imposed.
Provenance
- Issue #1884, Sec. 4 stage D.
match_resonant_target
Machine-independentConvention-sensitive
source
match_resonant_target(operator, target, *, weights, regularization, metric)
Stage B: the excitation whose resonant response best matches a complex target.
| Name | Type | Unit | Description |
|---|---|---|---|
operator | ResonantResponseOperator | - | A qualified operator. |
target | sequence of complex | T | Target |
weights | numpy.ndarray | - | Hermitian positive-semidefinite surface weights |
regularization | float | T^2 / phasor^2 |
|
metric | numpy.ndarray | - | Hermitian positive-definite excitation metric |
| Returns | |||
solution | dict | phasor |
|
Convention
The target is complex: its phase is physical here, so no group’s phase is
fixed. With lambda = 0 and a rank-deficient W^1/2 H the returned
c is the minimum-norm one, and the null space says what else fits.
Raises
ValueErrorShapes disagree,WorDis not Hermitian,Dis not positive definite, orregularizationis negative.
Processing steps · Applicability · Limitations · Provenance
Processing steps
- With
g = conj(c): minimize||W^1/2 (H g - t)||^2 + lambda g^H conj(D) g. - Solve the stacked system
[W^1/2 H; sqrt(lambda) L^H] g = [W^1/2 t; 0](conj(D) = L L^H) by least squares, never by an explicit inverse. c = conj(g); report the SVD ofW^1/2 Hand its null space.
Applicability
Machine-independent.
Limitations
A phasor-space answer: actual sector currents, winding signs and circuit
limits must be checked separately. A zero target with lambda > 0
returns c = 0; ask for a nontrivial target instead.
Provenance
- Issue #1884, Sec. 4 stage B.
qualify_response_operator
Machine-independentConvention-sensitive
source
qualify_response_operator(operator, phasors, responses, *, sample_ids, rtol)
Show source vaft/process/resonant_coil_response.py, lines 205–283
def qualify_response_operator(
operator: ResonantResponseOperator,
phasors: np.ndarray,
responses: Sequence[Sequence[complex]],
*,
sample_ids: Sequence[str],
rtol: float,
) -> ResponseOperatorQualification:
"""Qualify or reject ``Phi = H conj(c)`` against held-out runs.
Parameters
----------
operator : ResonantResponseOperator
From :func:`resonant_response_operator` [-].
phasors : numpy.ndarray
``(N, K)`` excitations not among the unit runs, complex ones included [phasor].
responses : sequence of sequence of complex
Their direct ``Phi``; an all-NaN row marks a failed run [T].
sample_ids : sequence of str
One id per run [-].
rtol : float
Largest accepted ``||Phi_model - Phi_direct|| / ||Phi_direct||`` [-].
Returns
-------
qualification : ResponseOperatorQualification
``rejected`` when any run exceeds ``rtol``; ``insufficient`` when any
failed or none was given, or no run had a non-real phasor (the
conjugate relation would be untested); otherwise ``qualified`` [-].
Raises
------
ValueError
Shapes disagree or ``rtol`` is negative.
Processing steps
----------------
1. Set aside failed (non-finite) runs and count them.
2. Relative vector error of ``H conj(c)`` against each direct ``Phi``.
3. One status, requiring at least one run with a non-real phasor.
Convention
----------
The comparison is on the complex vector, phase included; matching the
magnitudes alone would not catch the ``conj`` relation.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1884] Issue #1884, independent combined-current validation.
"""
if not rtol >= 0:
raise ValueError("rtol must not be negative")
c = np.atleast_2d(np.asarray(phasors, dtype=complex))
direct = np.atleast_2d(np.asarray(responses, dtype=complex))
ids = [str(s) for s in sample_ids]
if c.shape != (len(ids), operator.H.shape[1]) or direct.shape != (len(ids), operator.H.shape[0]):
raise ValueError("phasors, responses and sample_ids disagree with the operator")
finite = np.all(np.isfinite(direct), axis=1)
model = operator.response(c[finite])
errors = {i: float(np.linalg.norm(m - d) / np.linalg.norm(d))
for i, m, d in zip(np.asarray(ids)[finite], model, direct[finite])}
reasons: list[str] = []
failed = [i for i, e in errors.items() if e > rtol]
if failed:
status = "rejected"
reasons.append(f"relative error above {rtol:g}: {failed}")
elif not finite.all() or not finite.any():
status = "insufficient"
reasons.append(f"{int((~finite).sum())} run(s) failed; the operator is identified, not validated")
elif not np.any(np.abs(c[finite].imag) > 0):
status = "insufficient"
reasons.append("no held-out run had a non-real phasor, so Phi = H conj(c) is untested")
else:
status = "qualified"
return ResponseOperatorQualification(status=status, relative_errors=errors, reasons=tuple(reasons))Qualify or reject Phi = H conj(c) against held-out runs.
| Name | Type | Unit | Description |
|---|---|---|---|
operator | ResonantResponseOperator | - | From |
phasors | numpy.ndarray | phasor |
|
responses | sequence of sequence of complex | T | Their direct |
sample_ids | sequence of str | - | One id per run. |
rtol | float | - | Largest accepted |
| Returns | |||
qualification | ResponseOperatorQualification | - |
|
Convention
The comparison is on the complex vector, phase included; matching the
magnitudes alone would not catch the conj relation.
Raises
ValueErrorShapes disagree orrtolis negative.
Processing steps · Applicability · Provenance
Processing steps
- Set aside failed (non-finite) runs and count them.
- Relative vector error of
H conj(c)against each directPhi. - One status, requiring at least one run with a non-real phasor.
Applicability
Machine-independent.
Provenance
- Issue #1884, independent combined-current validation.
resonant_operating_space
Machine-independentConvention-sensitive
source
resonant_operating_space(groups, n, phasors, responses, *, psi_n_rational, windows, sample_ids, roles, amplitude_unit, frame, provenance, operators=None)
Direct resonant-field RMS per window (and a qualified operator’s predictions) as a #1178 result.
| Name | Type | Unit | Description |
|---|---|---|---|
groups | sequence of str | - | Coil-group names in phasor order. |
n | int | - | Toroidal mode number. |
phasors | numpy.ndarray | phasor |
|
responses | mapping of str to numpy.ndarray | T |
|
psi_n_rational | sequence of float | - | Surface positions. |
windows | mapping of str to ResonantWindow | - | Windows to reduce over, e.g. |
sample_ids | sequence of str | - | One id per sample. |
roles | sequence of str | - | Role per sample. |
amplitude_unit | float | A per turn | Cosine amplitude of one phasor unit. |
frame | str | - | The toroidal frame of the sector angles. |
provenance | mapping | - | Equilibrium, code, pedestal source. |
operators | mapping, optional | - | Per field, a |
| Returns | |||
result | vaft.process.coil_operating_space.CoilOperatingSpace | - | Metrics |
Convention
Reported statistic is the RMS (tesla), the square root of the optimized mean square; measured and modelled values never share a metric.
Raises
ValueErrorLengths disagree, an unknown field, or an operator that is not qualified.
Processing steps · Applicability · Provenance
Processing steps
- Per sample, field and window,
vaft.process.perturbation.rms_resonant_fieldof the direct response;failed(NaN) for a failed run,undefinedfor a window with no surface. - With a qualified operator, the same reduction of
H conj(c). - Window bounds and sources go into provenance.
Applicability
Machine-independent.
Provenance
- Issue #1884; [1886] the result schema’s
phi_res_rms_edgenaming.
resonant_response_operator
Machine-independentConvention-sensitive
source
resonant_response_operator(unit_responses, *, field, psi_n_rational, m_rational, n, groups, amplitude_unit, provenance=None)
Show source vaft/process/resonant_coil_response.py, lines 124–202
def resonant_response_operator(
unit_responses: Sequence[Sequence[complex]],
*,
field: str,
psi_n_rational: Sequence[float],
m_rational: Sequence[int],
n: int,
groups: Sequence[str],
amplitude_unit: float,
provenance: Mapping[str, object] | None = None,
) -> ResonantResponseOperator:
"""The operator ``H`` from the responses to the unit excitations ``c = e_k``.
Parameters
----------
unit_responses : sequence of sequence of complex
``Phi`` of the run with ``c = e_k`` (group ``k`` at one phasor unit,
phase 0, the others off), one per group, in group order [T].
field : str
``Phi_res`` or ``Phi_res_v`` [-].
psi_n_rational : sequence of float
Surface positions, the same in every run [-].
m_rational : sequence of int
Poloidal mode number per surface [-].
n : int
Toroidal mode number [-].
groups : sequence of str
Coil-group names in phasor order [-].
amplitude_unit : float
Cosine amplitude of one phasor unit [A per turn].
provenance : mapping, optional
Equilibrium, code version, run identities [-].
Returns
-------
operator : ResonantResponseOperator
``H[:, k] = Phi(e_k)``, so ``Phi = H conj(c)`` [-].
Raises
------
ValueError
Unknown field, mismatched lengths, a non-finite response, or no group.
Convention
----------
Because ``e_k`` is real, ``Phi(e_k) = H e_k``; the conjugate shows only
for complex excitations. :func:`qualify_response_operator` is what
establishes the relation for complex ``c``.
Applicability
-------------
Machine-independent.
Limitations
-----------
Built from ``K`` runs; trustworthy only once qualified on held-out complex
runs at the same equilibrium, ``n`` and numerical settings.
Provenance
----------
.. [1884] Issue #1884, the verified linear operator.
"""
if field not in RESONANT_FIELDS:
raise ValueError(f"field must be one of {RESONANT_FIELDS}, got {field!r}")
columns = [np.asarray(r, dtype=complex).ravel() for r in unit_responses]
psi = np.asarray(psi_n_rational, dtype=float).ravel()
m = np.asarray(m_rational, dtype=int).ravel()
if not columns or len(columns) != len(groups):
raise ValueError(f"{len(columns)} unit responses for {len(groups)} groups")
if any(col.shape != psi.shape for col in columns) or m.shape != psi.shape:
raise ValueError("every unit response must have one value per rational surface")
h = np.column_stack(columns)
if not np.all(np.isfinite(h)):
raise ValueError("a unit response is not finite")
if not amplitude_unit > 0:
raise ValueError("amplitude_unit must be positive")
return ResonantResponseOperator(field=field, H=h, psi_n_rational=psi, m_rational=m, n=int(n),
groups=tuple(str(g) for g in groups), amplitude_unit=float(amplitude_unit),
provenance=dict(provenance or {}))The operator H from the responses to the unit excitations c = e_k.
| Name | Type | Unit | Description |
|---|---|---|---|
unit_responses | sequence of sequence of complex | T |
|
field | str | - |
|
psi_n_rational | sequence of float | - | Surface positions, the same in every run. |
m_rational | sequence of int | - | Poloidal mode number per surface. |
n | int | - | Toroidal mode number. |
groups | sequence of str | - | Coil-group names in phasor order. |
amplitude_unit | float | A per turn | Cosine amplitude of one phasor unit. |
provenance | mapping, optional | - | Equilibrium, code version, run identities. |
| Returns | |||
operator | ResonantResponseOperator | - |
|
Convention
Because e_k is real, Phi(e_k) = H e_k; the conjugate shows only
for complex excitations. qualify_response_operator is what
establishes the relation for complex c.
Raises
ValueErrorUnknown field, mismatched lengths, a non-finite response, or no group.
Applicability · Limitations · Provenance
Applicability
Machine-independent.
Limitations
Built from K runs; trustworthy only once qualified on held-out complex
runs at the same equilibrium, n and numerical settings.
Provenance
- Issue #1884, the verified linear operator.
window_weights
Machine-independentConvention-sensitive
source
window_weights(operator, window)
Show source vaft/process/resonant_coil_response.py, lines 286–323
def window_weights(operator: ResonantResponseOperator, window) -> np.ndarray:
"""Surface weights whose quadratic metric is the window's mean squared ``|Phi|``.
Parameters
----------
operator : ResonantResponseOperator
Supplies the surface positions [-].
window : vaft.process.perturbation.ResonantWindow
From :func:`vaft.process.perturbation.resonant_windows` [-].
Returns
-------
W : numpy.ndarray
Diagonal ``N_s x N_s``, ``1 / N_in`` on the surfaces inside [-].
Raises
------
ValueError
The window holds no surface: an empty window is not a zero response.
Convention
----------
Endpoints included, as :meth:`ResonantWindow.mask`; a surface on the
core/edge boundary is in both. ``sqrt(c^H Q c)`` with ``Q =
operator.quadratic(W)`` is :func:`vaft.process.perturbation.rms_resonant_field`.
Applicability
-------------
Machine-independent.
Provenance
----------
.. [1884] Issue #1884, Sec. 3 (core/edge metrics).
"""
inside = window.mask(operator.psi_n_rational)
if not inside.any():
raise ValueError(f"the {window.name} window [{window.low:g}, {window.high:g}] holds no rational surface")
return np.diag(inside / inside.sum()).astype(float)Surface weights whose quadratic metric is the window’s mean squared |Phi|.
| Name | Type | Unit | Description |
|---|---|---|---|
operator | ResonantResponseOperator | - | Supplies the surface positions. |
window | vaft.process.perturbation.ResonantWindow | - | From |
| Returns | |||
W | numpy.ndarray | - | Diagonal |
Convention
Endpoints included, as ResonantWindow.mask; a surface on the
core/edge boundary is in both. sqrt(c^H Q c) with Q =
operator.quadratic(W) is vaft.process.perturbation.rms_resonant_field.
Raises
ValueErrorThe window holds no surface: an empty window is not a zero response.
Applicability · Provenance
Applicability
Machine-independent.
Provenance
- Issue #1884, Sec. 3 (core/edge metrics).