ErrorFields Module
The ErrorFields module quantifies how sensitive a perturbed equilibrium's resonant drive is to the placement of each coil set. It is the foundation of an error-field tolerance assessment: once one plasma solve exists, the question "how much resonant field does a misaligned coil produce?" is linear in the coil's motion, so the module linearizes it once and everything downstream (tolerance Monte Carlo, locking risk, allowable-tolerance scans) becomes linear algebra on a small table.
What is computed
For every named coil set of a coil-forced run, the module evaluates the root-area-weighted control-surface spectrum $\tilde{b}$ of the set as built and its central-difference derivatives with respect to the six rigid-body degrees of freedom: Cartesian shifts $(\Delta x, \Delta y, \Delta z)$ in metres and rotations $(\theta_x, \theta_y, \theta_z)$ about the machine axes in degrees. The spectra are placed on the run's $(m, n)$ ordering and conformed with the control-surface operator, so they are exactly the vectors the resonant coupling matrix and its singular vectors act on (see Dominant resonant-coupling mode).
The overlap of a coil set with singular mode $k$ of the coupling matrix, normalized by the axis toroidal field, is the dimensionless
\[\delta = \frac{V_k^{\mathrm{H}}\,\tilde{b}}{B_{T0}},\]
and because the projection is linear, the derivatives of $\tilde{b}$ project to the derivatives of $\delta$. A rigid shift $(\Delta x, \Delta y)$ therefore moves the overlap by $S_x \Delta x + S_y \Delta y$ with complex $S_x = \partial\delta/\partial\Delta x$ and $S_y = \partial\delta/\partial\Delta y$, which is exact for any coil shape; for an axisymmetric coil $S_y = \pm i S_x$ and the response reduces to a single magnitude with a free phase, the model the OMFIT tolerance tool used.
The stored primitive is the linearization of the spectrum, not a scalar, so the resonant surfaces retained, the singular mode, and the normalization are all post-hoc choices.
Running it
Add an [ErrorFields] section to a deck whose [ForcingTerms] uses forcing_data_format = "coil" and whose [PerturbedEquilibrium] computes the singular coupling:
[ErrorFields]
fd_step_shift_m = 1e-3 # Central-difference step for the rigid shifts [m]
fd_step_tilt_deg = 0.1 # Central-difference step for the rigid tilts [degrees]
rotation_center = "conductor" # Tilt pivot: each conductor's own centre ("conductor") or the whole set's ("set")
write_outputs_to_HDF5 = true # Write ErrorFields/CoilSensitivities/ to the output file
verbose = false # Log per-coil-set progress and linearity diagnostics
exclude_coils = [] # Coil sets that are not error-field sources (the NTV efc_coils are excluded automatically)The stage runs after the perturbed equilibrium and writes ErrorFields/CoilSensitivities/: the spectra field_as_designed, shift_sensitivity_per_m, tilt_sensitivity_per_deg, a finite-difference curvature diagnostic per tap, the current pattern the spectra were evaluated at, and a DominantMode/ summary projected onto the run's full-window dominant mode (delta_as_designed, its shift and tilt sensitivities, their direction-averaged in-plane magnitudes, and the in-plane shift and tilt that would cancel delta_as_designed).
examples/DIIID-like_error_field_example/ is a complete case: the DIII-D-like equilibrium with the C-coil as the nominal n = 1 source and the eighteen DIII-D F coils added as single-filament hoops at the centroids of their winding packs (from OpenFUSIONToolkit's TokaMaker DIIID_geom.json). An axisymmetric hoop drives no n = 1 field as built, so each F coil's error-field content is entirely its sensitivity to misalignment; analyze_example.jl ranks the coils by error field per millimetre of shift and per tenth of a degree of tilt, over all rational surfaces and over the edge only.
The tilt pivot matters for multi-filament winding packs: "conductor" rotates each filament about its own arc-length centre, the Fortran coil_read convention inherited by the OMFIT tolerance tool, while "set" rotates the pack rigidly about its common centre, which is what an engineering axis-line tolerance constrains. Single-conductor sets give identical results either way.
Tolerance input
Manufacturing tolerances are engineering data, reviewed and versioned independently of any run, so they live in their own TOML file named by tolerance_file in [ErrorFields] (a path relative to the run directory). The run validates the file, echoes its text into Input/RawInputs/ErrorFields/tolerance_toml_raw for replay, and leaves the device numbers where they belong: outside the repository. The sampling and Monte Carlo stages that consume these tolerances follow in later releases; this release fixes the format.
# Fallbacks for keys a coil block leaves out (every key optional)
[ErrorFields.defaults]
shift_sigma_mm = 0.0 # Gaussian uncertainty added to the sampled shift [mm]
tilt_sigma = 0.0 # Gaussian uncertainty added to the sampled tilt, in tilt_units
tilt_units = "deg" # "deg", or "m" for a rim displacement converted through the nominal radius
radial_shape = "hollow" # Radial sampling density on the disk: flat, uniform_area, hollow, or ring
tolerance_model = "additive" # "additive" (independent shift and tilt) or "cylinder" (correlated axis line)
cylinder_half_height_m = 0.0 # Cylinder half-height for the cylinder model [m]
current_factor = 1.0 # Current as built relative to the run's; scales the coil's overlap and sensitivities
# One block per coil set that moves on its own
[[ErrorFields.coil]]
name = "PF1U" # Coil set name; must match a [[ForcingTerms.coil_set]] of the run
shift_tol_mm = 0.5 # Radius of the in-plane displacement disk the coil centre may lie in [mm]
tilt_tol = 0.019 # Tilt tolerance, in tilt_units
tolerance_model = "cylinder" # Axis line confined to a cylinder; shift and tilt drawn together
cylinder_half_height_m = 1.5 # Cylinder half-height; sets the tilt reachable at the given radius [m]
# Coil sets that move together: one shared draw per sample
[[ErrorFields.coherent_group]]
name = "upper_pf_brace" # Label of the coherently moving group
members = ["PF1U", "PF2U"] # Coil sets sharing the draw
shift_tol_mm = 2.0 # Coherent shift amplitude shared by the group [mm]
tilt_tol = 0.0 # Coherent tilt amplitude shared by the group, in tilt_units
phase_group = "braces" # Groups naming the same phase_group share the random direction
rotation_center_z_m = 0.0 # Height of the pivot of the group's rigid rotation on the machine axis [m]
tilt_lever_arm_m = 3.2 # Lever arm converting a tilt_tol in metres to the group's rotation angle [m] (optional)
[ErrorFields.correctability]
uncorrectable_coils = ["EFCC_U"] # Coil sets the error-field correction cannot reduce
efc_factor = 2.0 # Divisor applied to correctable contributions under error-field correction
[ErrorFields.other_field]
magnitude = 8.2e-6 # Overlap budget of sources not attributed to any coil
sigma = 0.0 # Gaussian uncertainty on that budget
radial_shape = "ring" # Radial sampling density of the budget magnitudeThe shift tolerance is one number, the radius of the disk the coil centre may sit in; the direction is sampled. A tilt may be given in degrees or, as legacy tolerance tables do, as a rim displacement in metres, which tilt_tolerance_deg converts through the coil set's arc-length-weighted major radius exactly as the coil loader's tilt_in_meters does. A coherent group's tilt is a rigid rotation of all its members about a pivot on the machine axis at rotation_center_z_m, so a member at height z also shifts laterally by (z − z_pivot)·θ. Groups sharing a phase_group label draw the same random direction with independent amplitudes. A group's tilt given in metres is converted through tilt_lever_arm_m when the group has one (a reference structure's lever arm is its own, not any member coil's radius), and otherwise through the members' common major radius. A group is correctable only if none of its members is listed as uncorrectable. Coil sets of the run without a tolerance block contribute their as-designed overlap only. A coil's current_factor scales its as-designed overlap and its sensitivities together, since the field is linear in current: 0 switches a coil off, 0.5 runs it at half current, a negative value reverses it; this is the knob for a risk against the current in an uncorrectable coil.
Every key is checked against the schema, so a misspelled key is an error rather than a silent default. read_tolerance_toml parses a file into a ToleranceSet, validate_tolerances checks its names against a run's coil sets, and update copies a set, a coil, a group or the unattributed budget with named fields replaced, which is how a programmatic scan over a quantity the file fixes (a sigma, a budget, a current factor) is written:
ts = EF.read_tolerance_snapshot("gpec.h5")
half = EF.update(ts; coils=[c.name == "F6A" ? EF.update(c; current_factor=0.5) : c for c in ts.coils])
tight = EF.update(ts; other_field=EF.update(ts.other_field; magnitude=1e-5))Sampling a tolerance
A tolerance is one number, the radius of the disk the coil centre may lie in; the direction is random and the radial density is a RadialDistribution: Flat (uniform in radius, the OMFIT tool's flat), UniformArea (uniform over the disk), Hollow (peaked toward the edge, the OMFIT default), Ring (always on the edge), or PowerLaw(p). sample_disk draws a point as Δx + iΔy, sample_uncertainty adds the Gaussian uncertainty on where the coil actually sits, and the two tolerance models combine them: sample_additive draws a shift (metres) and a tilt (degrees) independently, while sample_cylinder confines the coil's axis line to a cylinder of radius R and half-height z_top, drawing its two endpoints and deriving the correlated midplane shift and lean. Tilts are the rotation angles θx + iθy about the machine axes in the sense apply_transforms uses. Every sampler takes the random generator and optional fixed directions, so coherent groups sharing a direction pass one phase and draw their own radii.
rng = Random.Xoshiro(1)
Δ = EF.sample_disk(rng, 0.5e-3, EF.Hollow()) # a point in a 0.5 mm disk, m
Δ, θ = EF.sample_cylinder(rng, 0.5e-3, 1.5, EF.randpow(EF.Flat())) # correlated shift [m] and tilt [deg]Tolerance Monte Carlo
With a tolerance_file named, the run samples every coil set's misalignment within its tolerance and histograms the dominant-mode overlap |δ|: per sample and coil set the overlap moves by S·(Δ + u) + T·(θ + v) for the coil's own draw (Δ, θ) and Gaussian placement uncertainties (u, v); coherent groups add one shared draw per group, with the lateral shift a rigid rotation about the group pivot gives each member; the unattributed budget adds a random direction. Because the overlap is linear in the misalignments, a million samples take about a second and no field is recomputed. Two histograms are written to ErrorFields/MonteCarlo/: the intrinsic |δ| and the corrected one, in which every correctable term (coil sets and groups not listed as uncorrectable, and the unattributed budget) is divided by efc_factor. Batches are seeded individually, so results are bit-identical for any thread count, and their spread is the statistical error bar of anything derived from them.
A coil set the run energizes but that is not an error-field source — a correction array named in [ErrorFields.NTV] efc_coils, or anything listed in [ErrorFields] exclude_coils — is swept like the others, so its sensitivities and couplings are tabulated, but it is left out of the as-designed error field, the Monte Carlo, the worst-case bound and the scans, in the run and in every post-hoc entry point that rebuilds the table from the file. A tolerance that names it is an error. without_coils(table, names) is the same operation on a table in memory, and excluded_coil_names("gpec.h5") says which sets a run left out.
[ErrorFields]
tolerance_file = "tolerances.toml" # Manufacturing-tolerance TOML, relative to the run directory
[ErrorFields.MonteCarlo]
nsample = 1000000 # Samples per batch
nbatch = 10 # Independent batches; their spread is the statistical error bar
seed = 1 # Base seed; batch b uses Xoshiro(hash((seed, b)))
nbins = 300 # Histogram bins, linear on [0, delta_max]
delta_max = 0.0 # Upper histogram edge; 0 = 1.5 × the worst-case alignment bound
tolerance_scale = 1.0 # Multiplies every shift and tilt tolerance (for tolerance scans)
coil_subset = [] # Coil sets whose tolerances are sampled; empty = all
scale_subset = [] # Coil sets and groups that tolerance_scale applies to; the rest hold their tolerance (empty = all)
scale_map = {} # Extra multiplier per coil set or group name, e.g. {F6A = 2.0}coil_subset and scale_subset answer different questions. coil_subset samples only the named coils and gives every other coil zero tolerance, which isolates one coil's contribution. scale_subset applies tolerance_scale to the named coils and groups and leaves every other name at its own tolerance, which is the product question of a design review: how far can the tolerance of one class of coils be relaxed while the rest of the machine holds its own? Names in either that are not coil sets of the run or groups of the tolerance file are errors. Neither changes which random numbers are drawn, so runs with the same seed differ only through the tolerances they scale.
The run's histogram is the full-window, dominant-mode summary. Any other window or mode, a tolerance scale, or a coil subset is a post-hoc re-run of the same kernel:
mc = EF.run_monte_carlo("gpec.h5"; psi_low=0.5, tolerance_scale=2.0, coil_subset=["PF1U", "PF2U"])
mc.abs_delta_pdf, mc.abs_delta_bin_edges # intrinsic |δ| density
mc.abs_delta_efc_pdf # corrected
mc.abs_delta_sampled_mean, mc.abs_delta_total_as_designedLocking risk and allowable tolerance
An overlap distribution becomes a locking risk through the empirical ITPA penetration-threshold scalings (citations): the n = 1 fits of Logan et al., Plasma Phys. Control. Fusion 62, 084001 (2020) and of Bursch et al., Plasma Phys. Control. Fusion (2026), doi:10.1088/1361-6587/aea7d6, and the n = 2 fits of Logan et al., Nucl. Fusion 60, 086010 (2020): δ_thresh = 10^α_c · n_e^α_n · B_T^α_B · R_0^α_R · (β_N/l_i)^α_β · I_p^α_I, with n_e in 10¹⁹ m⁻³, B_T in T, R_0 in m and I_p in MA. Each fit is chosen by year, dataset and fit; only the 2026 fits carry the current term:
Fit (year, dataset, fit) | α_c | α_n | α_B | α_R | α_β | α_I |
|---|---|---|---|---|---|---|
2026, "O,L", "OLS" (Eq. 7) | −4.31 ± 0.09 | 0.77 ± 0.08 | 0.19 ± 0.09 | 1.88 ± 0.16 | 0.25 ± 0.08 | −0.97 ± 0.08 |
2026, "O,L", "WLS" (Eq. 8) | −4.26 ± 0.09 | 0.56 ± 0.08 | 0.30 ± 0.10 | 1.57 ± 0.15 | 0.13 ± 0.06 | −1.01 ± 0.07 |
The 2026 fits use only ohmic and L-mode discharges of conventional tokamaks (C-Mod, DIII-D, EAST, JET, J-TEXT and KSTAR; no NSTX or COMPASS), so they suit conventional-aspect-ratio designs in linear ohmic confinement or L-mode. Sampling the fitted exponents within their standard errors turns the threshold into a distribution; its cumulative distribution is the probability that an overlap δ locks, and the locking probability of the assembled machine is 100 ∫ pdf(δ) P(lock|δ) dδ over the Monte Carlo bins, per batch. The operating point is an [ErrorFields.scenario] table: density must be given (it is not an equilibrium output); field, major radius, βN, li and the plasma current default from the equilibrium.
[ErrorFields.scenario]
n_e = 5.0 # Electron density for the threshold scaling [1e19 m^-3]
# i_p = 1.2 # Plasma current magnitude [MA]; defaults to the equilibrium's
[ErrorFields.Risk]
year = 2020 # Publication year of the threshold fit: 2020 (n = 1 and n = 2) or 2026 (n = 1)
dataset = "O,L" # ITPA dataset of the fit: 2020 "O,L" or "O,L,H" (n = 1), "O,L", "O,L,-C", "O,L,N" (n = 2); 2026 "O,L"
fit = "WLS" # Fitting method: "OLS", "DSOLS", or "WLS" (2026: "OLS" or "WLS")
distribution = "normal" # How the fit exponents are sampled: "normal", "flat", or "normal_truncated"
nsample_threshold = 1000000 # Threshold samples
seed = 1 # Seed of the threshold sampling
scan_scales = [0.25, 0.5, 1.0, 2.0, 4.0] # Tolerance multipliers of the allowable-tolerance scan (empty: no scan)ErrorFields/Risk/ holds the threshold density and P(lock|δ) on the Monte Carlo grid, the locking probability of the intrinsic and corrected distributions (with per-batch values), the as-designed risk, and the sharp-threshold risk; ErrorFields/Risk/ToleranceScan/ the risk against tolerance scale. The scan is the stored quantity; the allowable tolerance for a target risk is a post-hoc inversion, and every window or fit choice is re-evaluated from the file:
scan = EF.ToleranceScan("gpec.h5")
EF.allowable_tolerance(scan, 1.0) # tolerance multiplier at 1 % locking risk
EF.allowable_tolerance(scan, 1.0; corrected=true) # with error-field correction
risk = EF.locking_risk("gpec.h5"; n_e=5.0, psi_low=0.7, risk_ctrl=EF.RiskControl(; dataset="O,L,H"))
risk26 = EF.locking_risk("gpec.h5"; n_e=5.0, risk_ctrl=EF.RiskControl(; year=2026, fit="OLS"))
scan2 = EF.tolerance_scan("gpec.h5"; n_e=5.0, scales=[0.5, 1, 2, 4], coil_subset=["F6A", "F7A"])
scan3 = EF.tolerance_scan("gpec.h5"; n_e=5.0, scales=[0.5, 1, 2, 4], scale_subset=["F6A", "F7A"]) # scan the outboard pair, hold the restA threshold scaling is fitted for one toroidal mode number, so the in-run risk and every file-path entry point refuse a run that spans several (single_toroidal_mode); project onto one n's coupling first.
A number quoted at the target risk level should be shown free of sampling and binning bias. risk_convergence repeats the Monte Carlo over a list of sample counts and a list of bin counts, holding the other at its control value, and returns the locking probability with its batch spread for each: the spread must fall as 1/√nsample, and the value must not move with nbins by more than the spread. The spread alone cannot see a binning bias, which is why the bin sweep is there.
conv = EF.risk_convergence(table, ts, coil_sets, sc, scen; nsamples=[10^5, 10^6, 10^7], nbins_list=[100, 300, 3000])
conv.locking_probability_percent_by_nsample, conv.locking_probability_spread_percent_by_nsample
conv.locking_probability_percent_by_nbinsPlots and coil-array phasing
Analysis.ErrorFields plots everything above from gpec.h5, and every function takes a list of label => path pairs so coil-design revisions overplot on one axis:
AEF = GeneralizedPerturbedEquilibrium.Analysis.ErrorFields
AEF.plot_coil_sensitivities(["rev A" => "revA/gpec.h5", "rev B" => "revB/gpec.h5"]; quantity=:shift)
AEF.plot_tolerance_pdf("gpec.h5"; corrected=true)
AEF.plot_locking_risk("gpec.h5"; target_percent=1.0) # marks the allowable scale
AEF.plot_threshold_scaling("gpec.h5")
AEF.plot_dominant_mode_spectrum("gpec.h5")
AEF.plot_error_field_summary("gpec.h5"; save_path="error_field_summary.png")Four more views answer the questions the assessment's single numbers hide. plot_overlap_phasors lays each coil's complex overlap head to tail, so a coil that adds to the error field is told apart from one that cancels another's; plot_tolerance_budget stacks the terms of the worst-case bound abs_delta_worst_case (EF.worst_case_terms) per coil, group and unattributed budget, so the tolerance the budget is spent on is visible; plot_linearity_residuals shows the curvature the linear sensitivity model neglects, at the finite-difference step or rescaled to each coil's own tolerance; and quantity=:fraction on the sensitivity bars is the resonant share of each coil's own spectrum, which separates a coil that is small from one that drives the wrong harmonics.
AEF.plot_overlap_phasors("gpec.h5") # one panel per source
AEF.plot_tolerance_budget("gpec.h5"; sort=:total, top=10) # largest budget terms first
AEF.plot_linearity_residuals("gpec.h5"; at=:tolerance) # curvature over the tolerance range
AEF.plot_coil_sensitivities("gpec.h5"; quantity=:fraction) # resonant fraction, in percent
AEF.plot_coil_sensitivities("gpec.h5"; quantity=:as_designed, yscale=:log10) # stems, never bars, on a log axis
AEF.plot_tolerance_pdf("gpec.h5"; show_batches=true) # batch spread and the clamped fraction
EF.worst_case_terms("gpec.h5").total # = ErrorFields/MonteCarlo/abs_delta_worst_caseWhen several independently powered coil arrays share the job of correcting the error field, the relative phases of their current patterns decide how much dominant-mode field they can drive per ampere-turn. phasing_map evaluates |Σ_k δ_k e^{iφ_k}| per kilo-ampere-turn and the resonant fraction of the applied field on a grid of the N − 1 relative phases from the stored nominal spectra, a closed form with no optimizer, and plot_phasing_map draws it (a line for two arrays, a contour for three):
pmap = EF.phasing_map("gpec.h5", ["EFCC_L", "EFCC_M", "EFCC_U"]; psi_low=0.5)
EF.extreme_phasing(pmap) # best |δ| per kAt and the phases giving it
AEF.plot_phasing_map(pmap; quantity=:resonant_fraction_percent)NTV limits of error-field correction
A correction coil cancels the dominant-mode overlap at C_c per kilo-ampere-turn, but the non-resonant remainder of its field drives a neoclassical toroidal viscosity (NTV) torque T·I² that a perfect correction does not remove. With a torque budget T_0 and the threshold taken to fall in proportion to the torque spent, the current that corrects an intrinsic overlap δ_EF solves δ_EF − C_c I = s δ_thresh (1 − T_residual I²/T_0). This is the model used for SPARC by Logan et al., Nucl. Fusion (2026), doi:10.1088/1741-4326/ae6086, and for ARC by Leuthold et al., J. Plasma Phys. 92, E49 (2026), doi:10.1017/S0022377826101421. It holds only while the plasma still rotates, so the current is capped at I_max = √(T_0/T_residual), and the largest correctable overlap is whichever comes first: the peak of the quadratic, or the overlap cancelled at I_max. max_correctable_overlap returns that limit (with_ntv) together with the two published zero-rotation limits, C_c √(T_0/T_residual) (SPARC, residual_only) and C_c √(T_0/T_full) (ARC, torque_only). [ErrorFields.NTV] names the correction arrays; the run evaluates each one's C_c, resonant fraction, and NTV torque per kAt² for its whole field and for its field with the dominant mode projected out — two plasma-response evaluations of the unit-current spectrum followed by the kinetic torque, which needs a [KineticForces] section — and writes ErrorFields/NTV/. The spectrum is evaluated on the run's [ForcingTerms] boundary grid and normalized by the magnitude of the array's ampere-turns, |nw| × max|I|, so the winding sense and current pattern of the deck stay the phase reference; the torques are stored with their sign, and the limits consume the budget with their magnitude (the sign depends on the rotation and on conventions, so a negative torque is never read as no torque). Torque budget, threshold and safety factor are analysis choices:
[ErrorFields.NTV]
efc_coils = ["d3d_c"] # Coil set names of the correction arrays to evaluate; never error-field sources
method = "fgar" # KineticForces torque method (must be enabled in [KineticForces])couplings = EF.read_efc_couplings("gpec.h5")
curve = EF.efc_current_curve(couplings[1]; delta_threshold=1.4e-4, torque_budget=4.0)
EF.max_correctable_overlap(couplings[1]; delta_threshold=1.4e-4, torque_budget=4.0)
AEF.plot_efc_ntv_limits("gpec.h5"; torque_budget=4.0) # threshold from the run's Risk/ group
# The SPARC paper's uncertainty: ±50 % on the NTV torque, T_0 = 4 ± 2 N·m
AEF.plot_efc_ntv_limits("gpec.h5"; torque_budget=4.0, torque_rtol=0.5, budget_rtol=0.5)
AEF.plot_efc_ntv_limits("gpec.h5"; torque_budget=4.0, profiles=true) # adds T(ψ) integrated from the axisThe torque budget is the plasma's intrinsic torque. The SPARC paper uses 4 N·m for SPARC and the ARC paper 5–20 N·m for ARC; pick a value for the machine being analysed.
Designing the correction
The NTV limits say how much correction current an array may carry; correction_requirement says how much it needs, and on which surfaces the correction falls short. For one array the dominant-mode answer is the ratio −δ_source/δ_array; for several arrays the dominant-mode condition alone does not fix the currents (one complex equation in several unknowns leaves a family of solutions in which any two arrays can cancel each other, of which the result names the member with the least total ampere-turns squared); the question worth a function is the joint one over every rational surface: the complex factors on all arrays that minimize the resonant field C·(b̃_source + Σ_k f_k b̃_k) in the least-squares sense, the field left on each surface after that and after each array's dominant-mode correction alone, and how much of the source's spectrum each array can see. Each current comes twice: as a complex factor on the array's current as given, and in kilo-ampere-turns through the array's ampere_turns_kat (|winding multiplier| × max|I| / 1000, the same normalization as the EFC couplings), which is the form to compare arrays whose patterns were given at different currents and to set beside the NTV-limited current. Any CoilOverlap can be a source or an array, so an as-built assembly (combine_overlaps) can be corrected with arrays that were never part of the run. Over the tolerance samples the needed current is a distribution, and uncorrectable_probability is the fraction of the Monte Carlo's overlap beyond what the NTV-limited array can correct at all: the explicit-correction counterpart of the corrected locking probability's efc_factor model. The Monte Carlo leaves the correction arrays out of its overlap, so its as-designed current and a correction_requirement whose source combines the error-field coils describe the same field.
req = EF.correction_requirement("gpec.h5", "F_coils_as_built", ["d3d_c", "iu", "il"])
req.current_factor_least_squares, req.least_squares_rank # unique when the rank is the number of arrays
req.current_factor_dominant, req.current_factor_dominant_minimum_norm
req.current_least_squares_kat # the same in kilo-ampere-turns, comparable across arrays
abs.(req.resonant_field_least_squares_t) ./ abs.(req.resonant_field_source_t) # what is left, per surface
AEF.plot_correction_requirement(req)
mc = EF.MonteCarloResult("gpec.h5"); c = EF.read_efc_couplings("gpec.h5")[1]
EF.uncorrectable_probability(mc, c; delta_threshold=1.4e-4, torque_budget=4.0)
AEF.plot_needed_current(mc, array; coupling=c, delta_threshold=1.4e-4, torque_budget=4.0)Analysis after the run
Window the coupling to any range of rational surfaces and project onto any singular mode without re-running anything, from memory or from the file:
using GeneralizedPerturbedEquilibrium
EF = GeneralizedPerturbedEquilibrium.ErrorFields
# From the file: the coupling is rebuilt, windowed to 0.5 ≤ ψ_N ≤ 1, and projected onto mode 1
table = EF.sensitivity_table("gpec.h5"; psi_low=0.5)
table.delta_as_designed # complex overlap of each coil set
table.abs_delta_shift_per_mm # direction-averaged |∂δ/∂Δ| per millimetre of shift, per coil set
table.abs_delta_rim_per_mm # the same for tilt, as rim displacement at the coil's major radius
table.tilt_sensitivity_per_deg[1, :] # ∂δ/∂θx per degree, per coil set
# In memory, from the run's returned state (no I/O)
rc = PerturbedEquilibrium.ResonantCoupling(run.pe, run.ffs)
dom = PerturbedEquilibrium.dominant_coupling(rc; psi_low=0.5)
table = EF.sensitivity_table(run.coil_sensitivities, dom; mode=1)How far the linear model holds
Every sweep above assumes the overlap is linear in the misalignments out to the tolerance edge. The run checks that only at the finite-difference step (the stored *_linearity_residual ratios, plot_linearity_residuals). linearity_check displaces each coil set with a tolerance block by ±scale × tolerance along each in-plane shift and tilt axis, rotates each coherent group as one body about its pivot, recomputes the overlap of the displaced geometry and reports the relative error of the linear prediction per row. Include the largest scale a tolerance scan will use, since that is the regime it relies on; the cost is one Biot–Savart pass per row.
lin = EF.linearity_check("gpec.h5"; scales=(1.0, 2.0, 8.0))
maximum(filter(!isnan, lin.relative_error)) # the worst row
AEF.plot_linearity_check(lin) # stems per coil, log axis
AEF.plot_linearity_residuals("gpec.h5"; at=:step, yscale=:log10) # the stored step residuals, readable below 1 %A coil set whose field at the run's toroidal mode is round-off (an axisymmetric hoop at n = 1) has no resonant fraction: coil_overlaps, combine_overlaps and plot_coil_sensitivities(quantity=:fraction) report NaN for it rather than the ratio of two noise vectors, which would read as a fraction between 1 % and 30 %; the floor is 1e-8 of the largest field norm among the coil sets judged together (resonant_fraction_percent).
Comparing coil revisions
Assessing a new coil design against an existing run costs one Biot-Savart pass per coil set and no plasma solve: the equilibrium, the control surface and the resonant coupling all come from the stored file. Gather them once with ResonantDriveContext and hand it whatever geometry you like.
ctx = EF.ResonantDriveContext("gpec.h5") # nzeta_coil=…, dat_dir=… override the deck
old = EF.coil_overlaps(ctx, ForcingTerms.load_coil_sets(old_cfg, 1))
new = EF.coil_overlaps(ctx, ForcingTerms.load_coil_sets(new_cfg, 1))
new[1].delta # dimensionless overlap δ = Vᴴb̃ / B_T0
new[1].resonant_fraction_percent # how much of this coil's own spectrum is resonant
new[1].spectrum # b̃ itself, for the diagnostics belowEvery normalization the quantity is quoted in travels on the CoilOverlap, so a caller never has to work out which one a bare number was in.
A revision can rename or split coils. Because the field is linear in the currents, a design current pattern is applied afterwards rather than by re-running the geometry, and a split is written as the combination it is:
sets = ForcingTerms.conductors(ForcingTerms.read_coil_dat("old_pair.dat")) # one file, two coils
ovs = EF.coil_overlaps(ctx, sets) # each at 1 kA, say
EF.combine_overlaps(ovs, "old_pair_1" => 20.0, "old_pair_2" => -15.0) # weights in kAWeights multiply each spectrum as it was evaluated; they are not absolute currents, since the sets may have been swept at any current. Evaluating every conductor at 1 kA and weighting by the design current in kA is the idiom that makes them read as currents. An unmatched name raises rather than contributing zero, so a coil renamed between revisions cannot quietly drop out of a comparison.
For sensitivities to rigid motion as well, compute_coil_sensitivities takes the same context. It costs thirteen spectrum evaluations per coil set instead of one, so reach for coil_overlaps when only the overlaps are wanted.
Result names
Every result struct in ErrorFields (CoilSensitivities, SensitivityTable, CoilOverlap, MonteCarloResult, RiskResult, ToleranceScan, PhasingMap, EFCCoupling) names its fields by one grammar, and every field name is also its HDF5 dataset name under ErrorFields/:
<quantity>[_<instance>][_efc][_<statistic>][_<unit>]- quantity is the physical thing, spelled out:
deltais the dimensionless dominant-mode overlapv·b̃ / B_T0, a complex phasor per coil set, andabs_deltaits magnitude;thresholdis the penetration threshold in the same units;locking_probabilityis the probability of locking (never abbreviated, and never called a risk in a name);resonant_fractionis100·|v·b̃| / ‖b̃‖;field,spectrum,torque,current,shift_sensitivityandtilt_sensitivityare what they say. - instance says which geometry or value the quantity is evaluated for:
_as_designed, the unperturbed design;_sampled, over the Monte Carlo misalignment samples;_worst_case, every term at its tolerance edge and in phase;_fit, a scaling-law value at the fitted exponents without the fit's scatter;_total, the coherent sum over coil sets (a per-coil vector carries no qualifier and documents its shape as[ncoil_set]). A result evaluated on whatever geometry the caller passed (CoilOverlap,EFCCoupling) carries no instance qualifier. The word nominal is not used in names. _efcmarks a value with error-field correction applied, as in the TOML keysefc_factorandefc_coils.- statistic:
_mean,_pdf,_batches,_spread(range over the Monte Carlo batches),_samples,_bin_edges. - unit is present whenever the quantity has one:
_percent,_t(tesla),_m,_mm,_deg,_per_m,_per_deg,_per_mm,_per_kat,_per_kat2.deltaand probabilities given as fractions carry none; a probability stored as a percentage always carries_percent.
The order is fixed, so locking_probability_efc_batches_percent reads as "the locking probability, corrected, per batch, in percent", and abs_delta_total_as_designed as "the magnitude of the total overlap of the design as built".
API Reference
GeneralizedPerturbedEquilibrium.ErrorFields.MIN_NZETA_PER_PERIOD — Constant
Minimum toroidal boundary points per field period below which a post-hoc evaluation warns.
Helical windings alias on a coarse toroidal grid. A scan on multi-turn coils against a 128-point reference put 16 points per period about 1 % off, 20 about 0.4 %, 24 about 0.1 %, and 32 within one part in 1e5. A few tenths of a percent is the same size as the differences a coil-revision comparison is trying to resolve, so a grid inherited silently from a stored deck and coarser than ForcingTerms.NZETA_POINTS_PER_PERIOD is worth a warning; the default itself is converged.
GeneralizedPerturbedEquilibrium.ErrorFields.TORQUE_ROTATION_SIGN — Constant
Sign relating the kinetic torque to the rotation it acts on: −1 means a positive T_φ lowers the E×B rotation shift Δω, i.e. the kernel reports a braking torque as positive for the co-rotating profiles of the kinetic file. Fixed empirically on the DIII-D error-field example, whose tabulated torque is positive at the nominal rotation and rises through zero at a negative shift, so the balance is restoring only with this sign. It is a property of the kinetic kernel's sign convention, not of a run: a tabulated torque cannot tell a wrong sign from a torque that grows as the rotation brakes, which is a physical regime, so no check of it is made.
GeneralizedPerturbedEquilibrium.ErrorFields.CoherentGroupTolerance — Type
CoherentGroupToleranceTolerance of a set of coils that move together: every member receives the same displacement (and the same rotation about a common pivot) from one random draw per sample. Groups that name the same phase_group share the random direction of that draw while keeping independent amplitudes.
Fields
name: label of the groupmembers: coil set names moving togethershift_tol_m,tilt_tol,tilt_units,radial_shape,tolerance_model,cylinder_half_height_m: as inCoilTolerance, applied to the shared drawphase_group: groups with equal labels share the random direction; defaults tonamerotation_center_z_m: height of the pivot of the group's rigid rotation on the machine axis, metres; a member at heightztilted by θ also shifts laterally by(z − z_pivot)·θtilt_lever_arm_m: the lever arm, metres, that converts a tilt tolerance given in metres into the group's rotation angle (asin(tilt_tol / lever_arm));NaNwhen absent, in which case the members' common major radius is used. A group that stands for a reference structure has the structure's lever arm, not any member coil's radius
GeneralizedPerturbedEquilibrium.ErrorFields.CoilOverlap — Type
CoilOverlapField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
How strongly one coil set drives the resonant field, carrying every normalization the quantity is quoted in so a caller never has to know which one a bare number was.
Fields
coil_name: name of the coil setmode: index of the singular mode projected onto (1 is the dominant mode)delta:Vᴴb̃ / B_T0, the dimensionless overlapresonant_field_t:Vᴴb̃, tesla, the same projection unnormalizedresonant_fraction_percent:100·|Vᴴb̃| / ‖b̃‖, how much of this set's own spectrum is resonant;NaNwhen the set's field at the run's toroidal mode is round-off (seeresonant_fraction_percent)spectrum_norm_t:‖b̃‖in teslab_t0: the axis toroidal field the normalization used, teslaampere_turns_kat: the set's current as given, in kilo-ampere-turns,|winding multiplier| × max |conductor current| / 1000, so a current factor on this set converts to kAt by one multiplication and arrays with different pattern currents compare on one axis;NaNfor a combination of setsspectrum: b̃ itself, on theResonantCouplingcolumn ordering
The spectrum travels with the result so a diagnostic can ask why a coil couples as it does without a second pass over the geometry.
GeneralizedPerturbedEquilibrium.ErrorFields.CoilSensitivities — Type
CoilSensitivitiesField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
Linearization of each coil set's control-surface spectrum with respect to its six rigid-body degrees of freedom, on the (m, n) column ordering of the ResonantCoupling it was built against. Spectra are root-area-weighted fields b̃ in tesla, the vector the coupling matrix and its singular vectors act on, so projecting any of them onto a mode is one inner product.
Shifts are Cartesian translations of the whole set; tilts are rotations about the machine x, y, z axes through the pivot selected by ErrorFieldsControl.rotation_center. A coil set's spectrum is proportional to its currents, so the table is specific to the current pattern it was evaluated with; peak_current and winding_multiplier let a user renormalize per ampere-turn.
Fields
coil_names: name of each coil set[ncoil_set]m_modes,n_modes: poloidal and toroidal mode number of each spectrum entry[numpert_total]b_t0: toroidal field magnitude on axis, tesla — the normalization that makes the overlap a dimensionless δfield_as_designed: b̃ of each set as built, tesla[numpert_total × ncoil_set]shift_sensitivity_per_m: ∂b̃/∂(Δx, Δy, Δz), tesla per metre[numpert_total × 3 × ncoil_set]tilt_sensitivity_per_deg: ∂b̃/∂(θx, θy, θz), tesla per degree[numpert_total × 3 × ncoil_set]shift_linearity_residual,tilt_linearity_residual: ‖b̃(+h) + b̃(−h) − 2b̃(0)‖ of each tap relative to the set's largest first difference ‖b̃(+h) − b̃(−h)‖ over all six taps, a window-independent measure of how much second-order response the linearization drops at the step taken[3 × ncoil_set]peak_current: largest conductor current magnitude of each set, amperes[ncoil_set]winding_multiplier: turns per conductor element of each set[ncoil_set]major_radius_m: arc-length-weighted major radius of each set, metres[ncoil_set]— the lever arm that turns a tilt angle into the rim displacement engineering tolerances are written in
GeneralizedPerturbedEquilibrium.ErrorFields.CoilSensitivities — Method
CoilSensitivities(h5path::AbstractString)Read the coil linearization back from a gpec.h5 written with an [ErrorFields] section, with the mode labels from Info/mn_index and the normalization field from Equilibrium/B_T_axis.
GeneralizedPerturbedEquilibrium.ErrorFields.CoilTolerance — Type
CoilToleranceManufacturing tolerance of one coil set that moves independently. Lengths are in metres and the tilt tolerance in tilt_units; convert it to the degrees the sensitivities are stored in with tilt_tolerance_deg.
Fields
name: coil set name; must match a coil set of the runshift_tol_m: radius of the in-plane displacement disk the coil centre may lie in, metresshift_sigma_m: Gaussian uncertainty added to the sampled shift, metrestilt_tol: tilt tolerance, intilt_unitstilt_sigma: Gaussian uncertainty added to the sampled tilt, intilt_unitstilt_units:"deg", or"m"for a rim displacement converted through the coil's nominal major radiusradial_shape: radial sampling density on the disk:"flat"(uniform in radius),"uniform_area","hollow"(peaked toward the edge), or"ring"(always at the edge)tolerance_model:"additive"(independent shift and tilt draws) or"cylinder"(the coil axis line confined to a cylinder of radiusshift_tol_m, shift and tilt drawn together)cylinder_half_height_m: half-height of that cylinder, metres; sets the tilt reachable at the given radiuscurrent_factor: multiplier on the coil set's current as built relative to the run, applied to its as-designed overlap and its sensitivities alike (the spectrum is linear in current);1is the run's current,0switches the coil off, a negative value reverses it
GeneralizedPerturbedEquilibrium.ErrorFields.CorrectionRequirement — Type
CorrectionRequirementField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
The linear correction requirement of one error-field source against a set of correction arrays, on the rational surfaces of one run. Each needed current is given twice: as a complex factor on the array's current as given (so −1 on an array identical to the source is the exact cancellation), and in kilo-ampere-turns through the array's ampere_turns_kat, which is the form that compares arrays with different pattern currents on one axis and sits next to the NTV-limited current.
Fields
source_name,array_names: the coil sets,[narray]for the arraysrational_psi,rational_q,rational_m,rational_n: the surfaces,[nsurface]current_factor_dominant: the factor on each array alone that cancels the source's dominant-mode overlap,−δ_source / δ_array;NaNfor an array with no overlap[narray]. This is not a solution for the set: the dominant-mode condition is one complex equation innarrayunknowns, so for two or more arrays its solutions form an(narray − 1)-dimensional family in which any two arrays can cancel each othercurrent_factor_dominant_minimum_norm: the one member of that family with the smallest total currentΣ|I_k|²[narray],I_k = −δ_source · conj(δ_k/a_k) / Σ|δ/a|²witha_kthe array's ampere-turns (a_k = 1, a minimum in factor units, when any array's ampere-turns are unknown); every other member adds a current pattern that cancels within the dominant mode and only moves the other surfacescurrent_factor_least_squares: the factors on every array together that minimize the resonant field on every surface,argmin ‖C·(b̃_source + Σ_k f_k b̃_k)‖[narray]; unique whenleast_squares_rank == narray, otherwise the minimizer with the smallest total current in the same senseleast_squares_rank: rank of thensurface × narraysystem the least squares solvescurrent_dominant_kat,current_dominant_minimum_norm_kat,current_least_squares_kat: the three factors times each array'sampere_turns_kat, complex kilo-ampere-turns (magnitude and toroidal phase)[narray];NaNfor an array whose ampere-turns are unknownresonant_field_source_t: the source's resonant field on each surface,C·b̃_source, tesla[nsurface]resonant_field_dominant_t: the same after each array's dominant-mode correction alone, tesla[nsurface × narray]resonant_field_least_squares_t: the same after the joint least-squares correction, tesla[nsurface]cosine_similarity:|⟨b̃_array, b̃_source⟩| / (‖b̃_array‖ ‖b̃_source‖), how much of the source's spectrum each array can see at all;NaNfor a round-off field[narray]
GeneralizedPerturbedEquilibrium.ErrorFields.EFCCoupling — Type
EFCCouplingField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
The couplings of one correction coil array, per kilo-ampere-turn of its current pattern.
Fields
coil_name: the arraydelta_per_kat: dominant-mode overlap|δ|per kAt (C_c)resonant_fraction_percent: resonant fraction of the array's field,100·|Vᴴb̃|/‖b̃‖torque_full_per_kat2: NTV torque of the whole field per kAt², N·m, with its sign, at the nominal rotationtorque_residual_per_kat2: NTV torque of the field with the dominant mode projected out, per kAt², N·m, with its sign, at the nominal rotationrotation_shift: the scanned rigid E×B rotation shiftsΔω, rad/s, sorted and containing 0 (a single 0 when no scan was made)torque_full_scan,torque_residual_scan: the two torques at each shift, N·m per kAt²omega_reference: the reference rotationω_ref, rad/s, the ion toroidal rotation weighted by density and volume;NaNwithout a scanomega_offset_estimate: the rough neoclassical offset used to size the scan, rad/s;NaNwithout a scanpsi: kinetic ψ_N grid of the torque profiles (empty without a scan)torque_full_profile,torque_residual_profile: cumulative torqueT(ψ)at each shift,length(psi) × length(rotation_shift), N·m per kAt²
The constructor that omits the rotation-scan fields builds a coupling with no scan, the nominal torques only.
GeneralizedPerturbedEquilibrium.ErrorFields.ErrorFieldsControl — Type
ErrorFieldsControlControl parameters for the [ErrorFields] TOML section. The sweep needs a coil-forced perturbed-equilibrium solve (forcing_data_format = "coil" with the singular-coupling step on); everything here concerns only how the coil linearization is taken and where it is written. Analysis choices (which resonant surfaces count, which singular mode, which normalization) are never inputs: the full spectra are stored and projected post hoc with sensitivity_table.
Fields
fd_step_shift_m: central-difference step for the rigid shifts, in metresfd_step_tilt_deg: central-difference step for the rigid tilts, in degreesrotation_center: pivot of the tilt taps —"conductor"rotates every conductor of a set about its own arc-length centre (the Fortrancoil_readconvention the OMFIT tolerance tool inherited),"set"rotates the whole set rigidly about its common arc-length centre (a winding pack moving as one body, which is what an axis-line tolerance constrains)tolerance_file: manufacturing-tolerance TOML (seeToleranceTOML), relative to the run directory; empty means none. It is validated against the run's coil sets and echoed intoInput/RawInputs/ErrorFields/tolerance_toml_rawexclude_coils: coil sets of the run that are not error-field sources, such as a correction array energized to tabulate its couplings or a diagnostic coil. Their sensitivities are still tabulated, but they are left out of the as-designed error field, the tolerance Monte Carlo, the worst-case bound and the tolerance scans, and no tolerance may name them. The correction arrays in[ErrorFields.NTV] efc_coilsare excluded whether or not they are listed hereoutput_filename: HDF5 file the results are appended to; empty means the run's main outputwrite_outputs_to_HDF5: writeErrorFields/CoilSensitivities/when trueverbose: log per-coil-set progress and the linearity diagnostics
GeneralizedPerturbedEquilibrium.ErrorFields.Flat — Type
Flat <: RadialDistributionUniform in radius (p = 1): the density of |Δ| is constant on [0, R], the OMFIT tool's flat shape. Note this is not uniform over the disk area — see UniformArea.
GeneralizedPerturbedEquilibrium.ErrorFields.Hollow — Type
Hollow <: RadialDistributionPeaked toward the edge (p = 1/3, radial CDF (r/R)³): the OMFIT tool's default, standing in for a manufacturing process that uses most of its tolerance.
GeneralizedPerturbedEquilibrium.ErrorFields.LinearityCheck — Type
LinearityCheckField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
The linear sensitivity model against finite rigid displacements, one row per coil set (or coherent group), degree of freedom, scale and sign. Vectors are all [nrow].
Fields
name: coil set or coherent groupkind::shiftor:tiltaxis: 1 for x, 2 for y (a group's rows use axis 1)scale: the displacement as a multiple of the tolerancedisplacement: the signed displacement applied, metres for a shift and degrees for a tiltdelta_as_designed: δ of the undisplaced geometrydelta_linear: the model's predictionδ_as_designed + S·Δ(orT·θ, with a group's lateral rotation term as the Monte Carlo applies it)delta_actual: δ of the displaced geometry from a fresh Biot–Savart passrelative_error:|δ_actual − δ_linear| / |δ_linear − δ_as_designed|, the error of the predicted change;NaNwhere the linear change vanishes
GeneralizedPerturbedEquilibrium.ErrorFields.MonteCarloControl — Type
MonteCarloControlSettings of the tolerance Monte Carlo, the [ErrorFields.MonteCarlo] TOML table.
Fields
nsample: samples per batchnbatch: independent batches; their spread is the statistical error bar of every derived quantityseed: base seed; batchbusesXoshiro(hash((seed, b))), so results are bit-identical for any thread countnbins: histogram bins, linear on[0, delta_max]delta_max: upper edge of the histogram;0means1.5 ×the worst-case alignmentΣ(|δ_as_designed| + tolerance × |sensitivity|), which the tolerance draws cannot exceed (only the Gaussian uncertainties and the unattributed budget can, and those land in the last bin)tolerance_scale: multiplies every shift and tilt tolerance, for allowable-tolerance scans; uncertainties and the unattributed budget are not scaledcoil_subset: coil set names whose tolerances are sampled; every other coil set contributes only its as-designed overlap, and a coherent group is sampled only if all its members are listed. Empty means allscale_subset: coil set and group names thattolerance_scaleapplies to; every other name keeps its own tolerance (scale 1). Empty meanstolerance_scaleapplies to all. This is how one class of coils is scanned while another holds its tolerancescale_map: an explicit multiplier per coil set or group name, applied on top oftolerance_scale; names absent from the map are unscaled by it. A name inscale_subsetorscale_mapthat is neither a coil set of the run nor a group of the tolerance file is an error
The effective multiplier of a name is (name in scale_subset or scale_subset empty ? tolerance_scale : 1) × get(scale_map, name, 1). None of these change which random numbers are drawn, so two runs with the same seed differ only through the tolerances they scale.
GeneralizedPerturbedEquilibrium.ErrorFields.MonteCarloResult — Type
MonteCarloResultField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
Histograms of the dominant-mode overlap magnitude over the sampled misalignments.
Fields
abs_delta_bin_edges:|δ|bin edges[nbins + 1]; samples above the last edge are counted in the last binpdf,abs_delta_efc_pdf: probability density of the intrinsic and of the corrected|δ|, averaged over batches[nbins]abs_delta_pdf_batches,abs_delta_efc_pdf_batches: the same per batch[nbins × nbatch]abs_delta_total_as_designed:|Σ δ_as_designed|, the as-designed overlap with every coil at its design positionabs_delta_worst_case: the worst-case alignment bound used to size the histogramabs_delta_sampled_mean,abs_delta_efc_sampled_mean: sample means of the two magnitudesclamped_fraction: fraction of samples whose intrinsic|δ|landed beyondabs_delta_bin_edges[end](the corrected histogram clamps too but is not separately tallied)nsample,nbatch,seed: as run
GeneralizedPerturbedEquilibrium.ErrorFields.MonteCarloResult — Method
MonteCarloResult(h5path::AbstractString)Read the tolerance Monte Carlo of a run back from its gpec.h5, with the sampling settings from the [ErrorFields.MonteCarlo] table of the stored deck.
GeneralizedPerturbedEquilibrium.ErrorFields.NTVControl — Type
NTVControlSettings of the correction-coil NTV evaluation, the [ErrorFields.NTV] TOML table.
Fields
efc_coils: coil set names of the correction arrays to evaluate (empty: stage off)method: KineticForces torque method to use ("fgar"by default; it must be enabled in[KineticForces])rotation_scan: tabulate the torques against a rigid E×B rotation shift (true);falseevaluates only the nominal rotation, which limits the analysis to the:linearmodelrotation_scan_points: points of the initial symmetric shift grid (made odd so that the unshifted point is one of them)rotation_scan_max_points: cap on the number of kinetic evaluations per spectrum after adaptive refinementrotation_scan_tolerance: refinement stops when every interval's midpoint prediction error is below this fraction of the torque rangerotation_span_factor: the scan covers±span_factor × max(|ω_E|, offset_factor·|ω_*T|)evaluated at the innermost kinetic surfacerotation_offset_factor: rough neoclassical offset as a multiple of the ion temperature-gradient diamagnetic frequencyω_*T(about 2 after Park, Boozer and Menard 2009), used only to size the scanverbose: log per-array couplings and every scan point
GeneralizedPerturbedEquilibrium.ErrorFields.OtherFieldBudget — Type
OtherFieldBudgetOverlap budget for error-field sources not attributed to any coil, sampled once per Monte Carlo sample as an incoherent contribution with a random direction.
Fields
magnitude: dimensionless overlap magnitude of the budgetsigma: Gaussian uncertainty on that magnituderadial_shape: radial sampling density of the magnitude, as inCoilTolerance
GeneralizedPerturbedEquilibrium.ErrorFields.PhasingMap — Type
PhasingMapField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
Dominant-mode overlap of N coil arrays against the N − 1 relative phases of their current patterns, from phasing_map. Array 1 sets the reference; array k > 1 is rotated by the cumulative phase Δφ_2 + … + Δφ_k, so axis k − 1 of the maps is the phase of array k relative to array k − 1 (the two-row EFCC convention Δφ_ML, Δφ_UM).
Fields
coil_names: the arrays in order[N]phase_deg: grid of each relative phase, degrees[N − 1]vectorsdelta_per_kat:|δ|per kilo-ampere-turn of every array at each grid point[n_1 × … × n_{N−1}]resonant_fraction_percent:100·|Vᴴb̃| / ‖b̃‖, the fraction of the applied root-area-weighted field that lies along the dominant mode, at each grid pointdelta_per_kat_each:|δ_k|per kilo-ampere-turn of each array alone[N]
GeneralizedPerturbedEquilibrium.ErrorFields.PowerLaw — Type
PowerLaw(p) <: RadialDistributionr = R·u^p for a user-chosen exponent p ≥ 0.
GeneralizedPerturbedEquilibrium.ErrorFields.RadialDistribution — Type
RadialDistributionRadial density of a point drawn in a disk of radius R: r = R·u^p with u uniform on [0, 1] and p = randpow(d). Subtypes: Flat (p = 1, uniform in radius), UniformArea (p = 1/2, uniform over the disk area), Hollow (p = 1/3, peaked toward the edge), Ring (p = 0, always on the edge), and PowerLaw for any exponent. The radial cumulative distribution is (r/R)^(1/p).
GeneralizedPerturbedEquilibrium.ErrorFields.ResonantDriveContext — Type
ResonantDriveContextThe surface a finished run offers for judging coil geometry on, gathered once: the rebuilt equilibrium, the control surface it integrated to, the coil-grid configuration, the resonant coupling, the dominant mode over the chosen ψ window, the axis toroidal field, and the boundary grids. Everything needed to ask how much resonant field a coil drives, and nothing that depends on which coils they are — so the expensive half is built once and any number of geometries are evaluated against it.
Build it with the gpec.h5 constructor and hand it to coil_overlaps or compute_coil_sensitivities.
Fields
equil: the run's equilibrium, rebuilt on its own ψ gridinputs: the parsed TOML the run usedpsilim: the control surface the solve integrated tocfg: coil configuration, with any grid or directory override already appliedrc: the resonant coupling matrix and its mode orderingdom: the singular decomposition over the requested ψ windowb_t0: axis toroidal field magnitude, teslagrids: one boundary sampling per toroidal mode, shared by every coil set evaluated
GeneralizedPerturbedEquilibrium.ErrorFields.ResonantDriveContext — Method
ResonantDriveContext(equil, rc, cfg; psilim, b_t0, psi_low=CORE_PSI_LOW, psi_high=CORE_PSI_HIGH,
mtheta_coil=nothing, nzeta_coil=nothing, inputs=Dict{String,Any}())
ResonantDriveContext(h5path; psi_low=CORE_PSI_LOW, psi_high=CORE_PSI_HIGH, mtheta_coil=nothing,
nzeta_coil=nothing, dat_dir=nothing)Assemble the context. The ψ window selects which rational surfaces the dominant mode is built from. mtheta_coil and nzeta_coil override the boundary resolution the stored deck supplies, which is otherwise inherited silently; the toroidal grid is checked against MIN_NZETA_PER_PERIOD and warns when it is coarser. The h5path method lives in Rerun.jl, where the equilibrium is rebuilt from the file.
GeneralizedPerturbedEquilibrium.ErrorFields.Ring — Type
Ring <: RadialDistributionAlways on the edge (p = 0, r = R): the worst-case magnitude with a random direction.
GeneralizedPerturbedEquilibrium.ErrorFields.RiskControl — Type
RiskControlSettings of the locking-risk evaluation, the [ErrorFields.Risk] TOML table.
Fields
year,dataset,fit: which ITPA threshold fit to use (the toroidal mode number is the run's). n = 1,year = 2020:"O,L"or"O,L,H"with"OLS","DSOLS"or"WLS"; n = 1,year = 2026:"O,L"with"OLS"or"WLS"(with a plasma-current term); n = 2,year = 2020:"O,L","O,L,-C"or"O,L,N"with"WLS"distribution: how the fit exponents are sampled —"normal","flat", or"normal_truncated"nsample_threshold: threshold samplesseed: seed of the threshold samplingscan_scales: tolerance multipliers of the allowable-tolerance scan (empty: no scan); each is a fresh Monte Carlo with every shift and tilt tolerance multiplied by the scale
GeneralizedPerturbedEquilibrium.ErrorFields.RiskResult — Type
RiskResultField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
The locking risk of a Monte Carlo overlap distribution under one threshold scaling. All probabilities are percentages.
Fields
abs_delta_bin_edges: the Monte Carlo's|δ|grid[nbins + 1]threshold_pdf: probability density of the sampled penetration threshold on that grid[nbins]locking_probability_given_delta: probability that an overlap equal to each bin edge locks, the threshold's cumulative distribution[nbins + 1]threshold_fit: the threshold at the fitted exponentslocking_probability_percent,locking_probability_efc_percent: locking probability of the intrinsic and of the corrected distribution,100 ∫ pdf(δ) P(lock|δ) dδ, batch averageslocking_probability_batches_percent,locking_probability_efc_batches_percent: the same per Monte Carlo batch[nbatch]; their ranges are the statistical error barslocking_probability_as_designed_percent: locking probability of the as-designed machine,100 P(lock | |Σ δ_as_designed|)locking_probability_fit_threshold_percent: locking probability if the threshold were exactly its fitted value,100 P(|δ| > threshold_fit)scaling: the threshold fit used
GeneralizedPerturbedEquilibrium.ErrorFields.ScenarioParameters — Type
ScenarioParametersOperating point the threshold scaling is evaluated at. Density is not an ideal-MHD equilibrium output and must be given; the others default from the equilibrium when built with ScenarioParameters(equil; n_e). The plasma current only enters the fits that carry a current term, so the keyword constructor leaves it NaN unless given and those fits refuse to evaluate without it.
Fields
n_e: electron density, 10¹⁹ m⁻³b_t0: toroidal field magnitude on axis, teslar_0: major radius of the magnetic axis, metresbeta_n: normalized betal_i: normalized internal inductance (l_i(1))i_p: plasma current magnitude, MA (NaN: not given)
GeneralizedPerturbedEquilibrium.ErrorFields.ScenarioParameters — Method
ScenarioParameters(equil::PlasmaEquilibrium; n_e, b_t0=equil.params.bt0, r_0=equil.ro, beta_n=equil.params.betan, l_i=equil.params.li1, i_p=equil.params.crnt)
ScenarioParameters(h5path; n_e, kwargs...)Build the operating point from an equilibrium (or a run's Equilibrium/ scalars), with the density supplied and any other parameter overridable. The current is the equilibrium's computed crnt (Equilibrium/I_p), a positive magnitude in MA.
GeneralizedPerturbedEquilibrium.ErrorFields.SensitivityTable — Type
SensitivityTableField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
A CoilSensitivities projected onto one singular mode of a DominantCoupling and normalized by the axis toroidal field: the dimensionless overlap δ of each coil set and its derivatives, which is all a tolerance Monte Carlo consumes. Built by sensitivity_table.
With S = (S_x, S_y) the in-plane shift sensitivities, a rigid shift (Δx, Δy) moves the overlap by S_x·Δx + S_y·Δy; the same holds for the tilts. Singular vectors carry an arbitrary phase, so only magnitudes and relative phases within one table are meaningful.
Fields
coil_names: name of each coil set[ncoil_set]mode: index of the singular mode projected onto (1 is the dominant mode)delta_as_designed: overlap of each set as built[ncoil_set]shift_sensitivity_per_m: ∂δ/∂(Δx, Δy, Δz), per metre[3 × ncoil_set]tilt_sensitivity_per_deg: ∂δ/∂(θx, θy, θz), per degree[3 × ncoil_set]abs_delta_shift_per_mm: direction-averaged in-plane magnitude√((|S_x|² + |S_y|²)/2)of the shift sensitivity, per millimetre[ncoil_set]— the single-number sensitivity to placementabs_delta_tilt_per_deg: the same average over the tilt sensitivities, per degree[ncoil_set]abs_delta_rim_per_mm: that tilt sensitivity expressed as rim displacement atmajor_radius_m, per millimetre[ncoil_set]— the unit mechanical tolerances usually arrive incancelling_shift_m: the in-plane shift(Δx, Δy)that cancelsdelta_as_designed, metres[2 × ncoil_set], seecancelling_offsetcancelling_tilt_deg: the in-plane tilt(θx, θy)that cancelsdelta_as_designed, degrees[2 × ncoil_set]
GeneralizedPerturbedEquilibrium.ErrorFields.ThresholdScaling — Type
ThresholdScalingOne fit of the ITPA error-field penetration threshold, δ_thresh = 10^α_c · n_e^α_n · B_T^α_B · R_0^α_R · (β_N/l_i)^α_β · I_p^α_I, with n_e in 10¹⁹ m⁻³, B_T in tesla, R_0 in metres and I_p in MA. Each exponent carries the fit's standard error. Only the 2026 fits have a current term; the 2020 fits carry α_I = (0, 0).
Fields
n: toroidal mode number of the scalingyear: publication year of the fit,2020or2026; the two years fit different databasesdataset: plasma dataset the fit was made on ("O,L"ohmic and L-mode,"O,L,H"with H-modes, ...)fit: fitting method ("OLS","DSOLS"downsampled,"WLS"weighted)alpha_c,alpha_n,alpha_b,alpha_r,alpha_beta,alpha_ip:(value, standard error)of each exponent
GeneralizedPerturbedEquilibrium.ErrorFields.ToleranceScan — Type
ToleranceScanField names follow the ErrorFields result grammar <quantity>[_instance][_efc][_statistic][_unit] (see the manual's "Result names"); every field name is also its HDF5 dataset name.
Locking risk against a multiplier of every shift and tilt tolerance, from repeated Monte Carlos on the same sensitivities. Percentages throughout.
Fields
tolerance_scale: tolerance multipliers[nscale]locking_probability_percent,locking_probability_efc_percent: locking probability at each scale, batch averageslocking_probability_spread_percent,locking_probability_efc_spread_percent: range of the batch values at each scalelocking_probability_as_designed_percent: risk of the as-designed machine (independent of the scale)
GeneralizedPerturbedEquilibrium.ErrorFields.ToleranceScan — Method
ToleranceScan(h5path::AbstractString)Read a run's tolerance scan back from ErrorFields/Risk/ToleranceScan/.
GeneralizedPerturbedEquilibrium.ErrorFields.ToleranceSet — Type
ToleranceSetEverything a tolerance TOML file specifies, parsed and validated by read_tolerance_toml.
Fields
coils: independently moving coil sets[ncoil]groups: coherently moving groups[ngroup]uncorrectable_coils: coil set names whose error field the correction system cannot reduceefc_factor: divisor applied to the correctable contributions when error-field correction is onother_field: the unattributed budgetraw: the file's text, echoed into the output for replay
GeneralizedPerturbedEquilibrium.ErrorFields.UniformArea — Type
UniformArea <: RadialDistributionUniform over the disk area (p = 1/2): every patch of the tolerance disk is equally likely.
GeneralizedPerturbedEquilibrium.ErrorFields._rigidly_perturbed — Method
_rigidly_perturbed(cs, kind, axis, h, pivot) -> CoilSetThe coil set moved rigidly by h along one degree of freedom: kind = :shift translates every conductor by h metres along axis (x, y, z); kind = :tilt rotates every conductor by h degrees about axis through pivot, or about its own arc-length centre when pivot === nothing. Both go through apply_transforms with the toroidal modulation that makes the motion rigid (n_tilt = 0 for the shift, n_tilt = 1 for the tilt).
GeneralizedPerturbedEquilibrium.ErrorFields._set_center — Method
_set_center(cs::CoilSet) -> (x0, y0, z0)Arc-length-weighted centre of the whole coil set, over every conductor and strand: the pivot of a rigid tilt of a multi-filament winding pack.
GeneralizedPerturbedEquilibrium.ErrorFields.allowable_tolerance — Method
allowable_tolerance(scan::ToleranceScan, target_percent; corrected=false) -> Float64The tolerance multiplier at which the locking risk reaches target_percent, by linear interpolation in the logarithms of both the scale and the risk between the two scan points that bracket the target (the scan need not be monotonic; the first bracketing pair from the smallest scale is used). Returns NaN when no pair brackets the target. corrected reads the error-field-corrected curve.
GeneralizedPerturbedEquilibrium.ErrorFields.applied_spectrum — Method
applied_spectrum(cs::CoilSet, rc::ResonantCoupling, grids) -> Vector{ComplexF64}The root-area-weighted control-surface field b̃ that one coil set drives, on rc's (m, n) column ordering: Biot-Savart on each grid of grids, Fourier decomposition, then the conform through rootarea_field.
This is the conversion every overlap and every finite-difference tap goes through, kept in one place so the conform convention has a single implementation.
GeneralizedPerturbedEquilibrium.ErrorFields.apply_current_factors — Method
apply_current_factors(table::SensitivityTable, ts::ToleranceSet) -> SensitivityTableThe table with every coil set's as-designed overlap and sensitivities multiplied by its current_factor from the tolerance file. The spectrum is linear in current, so the nominal overlap, the worst-case bound and every sampled overlap scale together; the cancelling offsets do not change, since they are ratios. Returns the table itself when every factor is 1.
GeneralizedPerturbedEquilibrium.ErrorFields.cancelling_offset — Method
cancelling_offset(δ_as_designed, S_x, S_y) -> (Δx, Δy)The real in-plane displacement that cancels a coil set's nominal overlap to linear order, S_x·Δx + S_y·Δy = −δ_as_designed, as the least-squares solution of the 2×2 real system on the real and imaginary parts. For an axisymmetric hoop the two sensitivities are one complex number seen twice — S_y = −i·S_x in the sign convention these coils are built with — and the solution is conj(−δ_as_designed / S_x), split into its real and imaginary parts. The conjugate is not decoration: dropping it mirrors the offset about the x axis. A degenerate pair (S_x and S_y real multiples of each other) returns the minimum-norm solution.
GeneralizedPerturbedEquilibrium.ErrorFields.check_excluded_tolerances — Method
check_excluded_tolerances(ts::ToleranceSet, excluded) -> tsThrow if a tolerance, a coherent-group member or an uncorrectable-coil entry names a coil set the run excludes from its error field: a correction array is not an error-field source, so a tolerance on it has nothing to act on.
GeneralizedPerturbedEquilibrium.ErrorFields.coil_overlaps — Method
coil_overlaps(ctx::ResonantDriveContext, coil_sets; mode=1) -> Vector{CoilOverlap}
coil_overlaps(h5path, coil_sets; mode=1, kwargs...) -> Vector{CoilOverlap}Resonant overlap of each coil set against a finished run, evaluated on the run's own control surface. coil_sets is any geometry, not necessarily the run's, so swapping in a new coil revision costs one Biot-Savart pass per set and no stability or perturbed-equilibrium solve.
The h5path method takes ResonantDriveContext's keywords and lives in Rerun.jl. For sensitivities to rigid motion as well, use compute_coil_sensitivities instead; this is the cheaper path when only the overlaps are wanted.
GeneralizedPerturbedEquilibrium.ErrorFields.combine_overlaps — Method
combine_overlaps(overlaps, "A" => 20.0, "B" => -15.0; name="combined") -> CoilOverlapCoherent sum of several coil sets' spectra at relative weights, re-derived into the same CoilOverlap so every normalization stays consistent. The field is linear in the currents, which is the whole reason a spectrum is worth caching: a design current pattern is applied here rather than by re-running the coils.
Weights multiply each spectrum as it was evaluated. They are not absolute currents, because the sets may have been swept at any current. The usual idiom is to evaluate every conductor at 1 kA and then weight by the design current in kA, which makes the weights read as currents without the function having to guess.
Phase is what a coherent sum is for, and it is the first thing an ad-hoc abs() throws away. An unmatched name is an error rather than a silent zero, so a coil renamed between revisions cannot quietly drop out of a comparison.
GeneralizedPerturbedEquilibrium.ErrorFields.compute_coil_sensitivities — Function
compute_coil_sensitivities(coil_sets, rc, equil, cfg, ctrl=ErrorFieldsControl(); psi, b_t0) -> CoilSensitivitiesLinearize the control-surface spectrum of every coil set in coil_sets with respect to its six rigid-body degrees of freedom, by central differences of the Biot-Savart spectrum on the surface psi (the solve's integration limit, ffs.psilim) sampled once per toroidal mode of rc. The nominal spectrum is evaluated too, so the sum over sets reproduces the run's forcing when these are the run's coils.
Shift taps translate the whole set rigidly; tilt taps rotate it about the pivot named by ctrl.rotation_center, in degrees. cfg supplies the boundary-grid resolution, b_t0 the axis toroidal field (tesla) stored for the δ normalization. Errors on a set carrying no current, whose linearization would vanish identically.
The spectra are placed on rc's (m, n) ordering and conformed to root-area-weighted field with rootarea_field, so coupling_overlap applies to them directly. Post-hoc use from a gpec.h5 goes through the compute_coil_sensitivities(h5path, coil_sets; kwargs...) method.
GeneralizedPerturbedEquilibrium.ErrorFields.compute_coil_sensitivities — Function
compute_coil_sensitivities(ctx::ResonantDriveContext, coil_sets, ctrl=ErrorFieldsControl()) -> CoilSensitivitiesSweep coil_sets against an already-assembled ResonantDriveContext, reusing its boundary grids. Equivalent to the five-argument method and the way to apply a grid or ψ-window override.
GeneralizedPerturbedEquilibrium.ErrorFields.correction_current — Method
correction_current(δ_ef, c::EFCCoupling; delta_threshold, torque_budget, safety_factor=1.0, ntv=true,
model=:auto, rotation_exponent=1.0, omega_reference=c.omega_reference) -> Float64Correction current, kAt, that brings an intrinsic overlap δ_ef down to safety_factor × delta_threshold. Without NTV (ntv = false) that is the linear (δ_ef − s·δ_thresh) / C_c, zero when no correction is needed. With NTV the residual torque lowers the threshold by threshold_factor: for the :torque_balance model (the default when the coupling carries a rotation scan) the current is the root of δ_ef − C_c·I − s·δ_thresh·(1 + Δω(I)/ω_ref)^α; for :linear it is the smaller root of the closed-form quadratic, provided the residual torque has not exhausted the budget there. NaN when no such root exists, i.e. the overlap is beyond max_correctable_overlap. The returned current is the lower edge of the correctable window; correction_current_upper gives the upper edge, past which the over-correction is itself above threshold.
GeneralizedPerturbedEquilibrium.ErrorFields.correction_current_upper — Method
correction_current_upper(δ_ef, c::EFCCoupling; delta_threshold, torque_budget, safety_factor=1.0, ntv=true,
model=:auto, rotation_exponent=1.0, omega_reference=c.omega_reference) -> Float64Upper edge of the correctable current window, kAt: the current past which the over-correction C_c·I − δ_ef is itself above safety_factor × delta_threshold, so the field locks again from the other side. Without NTV it is (δ_ef + s·δ_thresh) / C_c. For the :linear model it is the larger root of the same quadratic, capped at I_max; for :torque_balance it is the root of C_c·I − δ_ef − s·δ_thresh·(1 + Δω(I)/ω_ref)^α above the null, or the current at which the balance fails if that comes first. NaN whenever correction_current is NaN.
GeneralizedPerturbedEquilibrium.ErrorFields.correction_requirement — Method
correction_requirement(ctx, source::CoilOverlap, arrays) -> CorrectionRequirement
correction_requirement(h5path, source_name, array_names; mode=1, coil_sets=nothing, kwargs...) -> CorrectionRequirementWhat it takes to correct one error field with a set of arrays, and what is left. The dominant-mode answer for one array is the ratio −δ_source / δ_array; for several arrays the dominant-mode condition alone does not fix the currents, since one complex equation in narray unknowns leaves an (narray − 1)-dimensional family of solutions in which any two arrays can cancel each other, so the result reports each array's solo factor and the family's minimum-current member (least total ampere-turns squared when every array's ampere-turns are known), and the question worth a function is the joint one over every rational surface: the complex factors on all arrays that minimize the resonant field C·(b̃_source + Σ_k f_k b̃_k) in the least-squares sense (\ on the nsurface × narray system, unique when its rank is narray and the minimum-norm minimizer otherwise, which the result states), the resonant field left on each surface after it and after each array's solo dominant-mode correction, and how much of the source's spectrum each array can see. Every input is a CoilOverlap carrying its spectrum, so any geometry can be a source or an array, not only the run's coils. The h5path form takes names among the run's coil sets, or among coil_sets when given, and evaluates them on the run's control surface.
Compare the needed factors with the NTV-limited allowance of the same array through uncorrectable_probability, which asks how often the Monte Carlo's overlap exceeds what the array can correct.
GeneralizedPerturbedEquilibrium.ErrorFields.disk_radius — Method
disk_radius(rng, R, p) -> Float64A radius drawn in [0, R] with density exponent p (see RadialDistribution): R·rand()^p. The common exponents (0, 1/3, 1/2, 1, from Ring, Hollow, UniformArea, Flat) take a sqrt/cbrt/identity fast path instead of the general ^, which is the hot loop's dominant cost otherwise.
GeneralizedPerturbedEquilibrium.ErrorFields.efc_current_curve — Method
efc_current_curve(c::EFCCoupling; delta_threshold, torque_budget, safety_factor=1.0, delta_max=15, npoints=500,
model=:auto, rotation_exponent=1.0, omega_reference=c.omega_reference,
torque_rtol=0.0, budget_rtol=0.0) -> NamedTupleThe correction current against the intrinsic overlap from 0 to delta_max × delta_threshold: current_linear (no NTV), current_ntv and current_ntv_upper (the two edges of the correctable window under the model), the threshold_factor along the lower edge, the resolved model, and the limits of max_correctable_overlap spliced in (with_ntv, residual_only, torque_only). Nonzero torque_rtol and budget_rtol (fractional uncertainties of the torque coefficients and of T_0) add current_ntv_pessimistic/current_ntv_optimistic and limits_pessimistic/limits_optimistic: scaling the torques by 1 ± torque_rtol is the same as scaling the budget by its inverse, since both models depend on T/T_0 only.
GeneralizedPerturbedEquilibrium.ErrorFields.excluded_coil_names — Method
excluded_coil_names(ef_raw::AbstractDict) -> Vector{String}
excluded_coil_names(h5path::AbstractString) -> Vector{String}The coil sets a run leaves out of its error field: [ErrorFields] exclude_coils together with the correction arrays in [ErrorFields.NTV] efc_coils, read from the parsed [ErrorFields] section of a deck or from the deck echoed into a run file (none when the file carries no deck).
GeneralizedPerturbedEquilibrium.ErrorFields.extreme_phasing — Method
extreme_phasing(map::PhasingMap; quantity=:delta_per_kat, which=:max) -> (value, phases_deg)The largest (or smallest) value of a map and the relative phases, in degrees, where it occurs.
GeneralizedPerturbedEquilibrium.ErrorFields.fitted_threshold — Method
fitted_threshold(sc::ThresholdScaling, scen::ScenarioParameters) -> Float64The penetration threshold at the fitted exponents.
GeneralizedPerturbedEquilibrium.ErrorFields.forcing_grids — Method
forcing_grids(rc::ResonantCoupling, equil, cfg::CoilConfig; psi) -> Vector{Tuple{Int,CoilForcingGrid}}One boundary sampling per toroidal mode of rc, on the surface psi. The grids depend only on the equilibrium and the grid dimensions, so building them once and evaluating every coil set against them is what makes a post-hoc coil sweep cheap.
GeneralizedPerturbedEquilibrium.ErrorFields.group_tilt_deg — Method
group_tilt_deg(g::CoherentGroupTolerance, set_of) -> Float64The rigid rotation angle, in degrees, that a coherent group's tilt tolerance stands for.
A group rotates as one body, so it has one angle. Declared in degrees that angle is the tolerance itself. Declared in metres it is a rim displacement, which only names an angle once a radius is chosen — and the members do not have to share one. The reference implementation sidesteps this by converting per coil, giving every member the same rim displacement and therefore a different angle each, which is no longer a rigid rotation of the group.
Rather than pick a member's radius and hide the choice, this errors when the members disagree. Declare such a group in degrees, which is unambiguous and is what the reference's own tables do.
GeneralizedPerturbedEquilibrium.ErrorFields.has_rotation_scan — Method
has_rotation_scan(c::EFCCoupling) -> BoolWhether the coupling carries a torque-versus-rotation table (more than the nominal point and a finite reference rotation).
GeneralizedPerturbedEquilibrium.ErrorFields.linearity_check — Method
linearity_check(ctx, coil_sets, tolerances; scales=(1.0, 2.0), mode=1, ctrl=ErrorFieldsControl()) -> LinearityCheck
linearity_check(h5path; scales=(1.0, 2.0), mode=1, psi_low=0.0, psi_high=CORE_PSI_HIGH, ctrl=ErrorFieldsControl(), kwargs...) -> LinearityCheckHow linear the overlap is out to the tolerance edge, which is the premise every post-hoc sweep rests on and which the stored finite-difference residuals only test at the step. Each coil set with a tolerance block is displaced by ±scale × tolerance along each in-plane shift axis and about each in-plane tilt axis (a cylinder coil's tilt range is the angle its axis line can reach, atan(shift_tol / half_height)); each coherent group is shifted and rigidly rotated about its pivot as one body at its own tolerance; the overlap of the displaced geometry is recomputed and compared with the linear prediction from sensitivity_table. Include the largest scale a tolerance scan will use, since that is the regime it relies on. Cost: one Biot–Savart pass per row, so it is opt-in and not part of the run. The file form rebuilds the context, the coil geometry and the tolerance snapshot from gpec.h5.
GeneralizedPerturbedEquilibrium.ErrorFields.locking_risk — Method
locking_risk(h5path; n_e, psi_low=0.0, psi_high=CORE_PSI_HIGH, mode=1, risk_ctrl=RiskControl(), kwargs...) -> RiskResultPost-hoc locking risk from a run: the Monte Carlo is re-run from the file for the given window and mode with MonteCarloControl kwargs, the threshold fit is chosen by risk_ctrl for the run's toroidal mode number, and the scenario is the run's equilibrium at density n_e.
GeneralizedPerturbedEquilibrium.ErrorFields.locking_risk — Method
locking_risk(mc::MonteCarloResult, sc::ThresholdScaling, scen::ScenarioParameters; ctrl=RiskControl()) -> RiskResult
locking_risk(mc, thresholds::AbstractVector, sc, scen) -> RiskResultConvolve an overlap distribution with the threshold distribution: P(lock|δ) is the fraction of sampled thresholds below δ, and the locking probability is 100 ∫ pdf(δ) P(lock|δ) dδ over the Monte Carlo's bins, evaluated per batch. Thresholds are sampled with ctrl or passed in.
GeneralizedPerturbedEquilibrium.ErrorFields.max_correctable_overlap — Method
max_correctable_overlap(c::EFCCoupling; delta_threshold, torque_budget, safety_factor=1.0, model=:auto,
rotation_exponent=1.0, omega_reference=c.omega_reference) -> (; with_ntv, residual_only, torque_only)The largest intrinsic overlap the array can correct. with_ntv: the overlap beyond which correction_current has no root (for the :linear model in closed form: the quadratic's tangency s·δ_thresh + C_c² T_0 / (4 s δ_thresh T_residual), or C_c √(T_0 / T_residual) when the residual torque exhausts the budget before that; for :torque_balance by bisection). residual_only: the overlap cancelled by the current at which the residual torque alone brings the rotation to rest (C_c √(T_0 / T_residual) for :linear), the zero-rotation limit of Logan et al. (2026) Eq. (7); it coincides with with_ntv whenever the quadratic's vertex lies outside the rotating range. torque_only: the overlap cancelled by the current at which the whole field's torque alone exhausts the budget (C_c √(T_0 / T_full)) or, with the balance, brings the reference rotation to rest or breaks the balance. Inf when there is no limit inside the model's reach.
GeneralizedPerturbedEquilibrium.ErrorFields.needed_current_distribution — Method
needed_current_distribution(mc::MonteCarloResult, array::CoilOverlap) -> NamedTupleThe Monte Carlo's intrinsic |δ| histogram re-expressed as the current on array that cancels each sampled overlap's dominant mode: as the factor |δ| / |δ_array| on the array's current as given, fields current_factor_bin_edges and current_factor_pdf (density per unit factor, integrating to one), and in kilo-ampere-turns through the array's ampere_turns_kat, fields current_bin_edges_kat and current_pdf_per_kat (NaN when the ampere-turns are unknown). The needed current for the as-built machine is one number; over the tolerance samples it is this distribution.
GeneralizedPerturbedEquilibrium.ErrorFields.phasing_map — Method
phasing_map(sens::CoilSensitivities, dom::DominantCoupling, coil_names; mode=1, nphase=180) -> PhasingMap
phasing_map(h5path, coil_names; psi_low=0.0, psi_high=CORE_PSI_HIGH, mode=1, nphase=180) -> PhasingMapScan the relative phases of the current patterns of the named coil arrays and evaluate, at each grid point, the dominant-mode overlap per kilo-ampere-turn and the resonant fraction of the applied field. Rotating a current pattern by Δφ multiplies the spectrum by e^{iΔφ} (the phase of the pattern itself, n times the toroidal angle it is rotated through). nphase points per phase axis, spanning [0, 360) degrees.
Each array's spectrum is its stored nominal spectrum divided by the magnitude of its ampere-turns, |winding_multiplier| × peak_current. The zero of each phase axis is therefore the array's current pattern exactly as the run specified it, with the winding sense of its geometry file included: a negative winding multiplier and a negated current pattern each remain in the map as the half-turn they physically are. Normalizing by the signed product would erase how the device defines positive current in that array, which is device-specific information the map must keep.
GeneralizedPerturbedEquilibrium.ErrorFields.radial_distribution — Method
radial_distribution(name::AbstractString) -> RadialDistributionThe distribution named by a tolerance file's radial_shape: "flat", "uniform_area", "hollow", or "ring" (the RADIAL_SHAPES the parser accepts).
GeneralizedPerturbedEquilibrium.ErrorFields.randpow — Method
randpow(d::RadialDistribution) -> Float64The exponent p in r = R·u^p of a radial distribution.
GeneralizedPerturbedEquilibrium.ErrorFields.read_efc_couplings — Method
read_efc_couplings(h5path::AbstractString) -> Vector{EFCCoupling}Read a run's correction-coil couplings back from ErrorFields/NTV/, with their torque-versus-rotation tables when the run made them.
GeneralizedPerturbedEquilibrium.ErrorFields.read_tolerance_snapshot — Method
read_tolerance_snapshot(h5path) -> ToleranceSetThe tolerance set a run used, parsed from the raw echo in its gpec.h5; nothing when the run named no tolerance file.
GeneralizedPerturbedEquilibrium.ErrorFields.read_tolerance_toml — Method
read_tolerance_toml(path) -> ToleranceSet
parse_tolerance_toml(text) -> ToleranceSetRead and validate a tolerance TOML file (see the module docstring for the schema). Every key is checked against the schema so a misspelled key errors instead of silently taking a default; tolerances must be non-negative, names unique, enumerated fields one of their allowed values, efc_factor ≥ 1, a cylinder model needs a positive half-height, a current_factor must be finite and a tilt_lever_arm_m positive. Names are checked against the run's coil sets separately by validate_tolerances.
GeneralizedPerturbedEquilibrium.ErrorFields.regrid — Method
regrid(cfg::CoilConfig; mtheta_coil=nothing, nzeta_coil=nothing, dat_dir=nothing) -> CoilConfigcfg with the boundary resolution or geometry directory replaced. Each keyword left as nothing keeps the value cfg already carries.
GeneralizedPerturbedEquilibrium.ErrorFields.residual_spectrum — Method
residual_spectrum(dom::DominantCoupling, b̃; mode=1) -> Vector{ComplexF64}The applied root-area-weighted spectrum with singular mode mode projected out, b̃ − V_k (V_kᴴ b̃): what a perfect single-mode correction leaves behind.
GeneralizedPerturbedEquilibrium.ErrorFields.resonant_fraction_percent — Method
resonant_fraction_percent(raw, nrm, nrm_ref) -> Float64100·|raw| / nrm, the share of a coil set's applied field b̃ (norm nrm) that lies along the dominant mode (raw = Vᴴb̃), or NaN when nrm is below 1e-8 × nrm_ref (the largest norm among the coil sets judged together) or below 1e-12 T. An axisymmetric hoop at n = 1 has a field of round-off, and the ratio of two round-off vectors lands anywhere between 1 % and 30 %; that is not a fraction, and 0 would read as a genuine zero overlap of a finite field, which is a different fact.
GeneralizedPerturbedEquilibrium.ErrorFields.risk_convergence — Method
risk_convergence(table, tolerances, coil_sets, sc, scen; nsamples, nbins_list, ctrl=MonteCarloControl(), risk_ctrl=RiskControl()) -> NamedTupleThe locking probability against the Monte Carlo's sample count and bin count, with its batch spread, so a number at the target risk level can be shown to be free of sampling and binning bias before it is quoted: the spread must shrink as 1/√nsample and the value must not move with nbins by more than the spread. Each sweep holds the other control at ctrl's value and reuses one threshold sample. Fields: nsample, locking_probability_percent_by_nsample, locking_probability_spread_percent_by_nsample, nbins, locking_probability_percent_by_nbins, locking_probability_spread_percent_by_nbins.
GeneralizedPerturbedEquilibrium.ErrorFields.rotation_scan_span — Method
rotation_scan_span(omega_e, omega_star_T; span_factor=1.0, offset_factor=2.0) -> (; span, offset_estimate)Half-width of the symmetric rotation-shift scan, span_factor × max(|ω_E|, offset_factor·|ω_*T|), from the E×B and ion temperature-gradient diamagnetic frequencies at the innermost kinetic surface, and the signed rough offset offset_factor·ω_*T it was sized against.
GeneralizedPerturbedEquilibrium.ErrorFields.rotation_shift — Method
rotation_shift(c::EFCCoupling, current; torque_budget, omega_reference=c.omega_reference, field=:residual) -> Float64Steady rotation shift Δω (rad/s) of the zero-dimensional torque balance Δω·T_0/ω_ref = T(Δω)·I² under current kAt of the array, on the branch continuous from the unshifted state: the first root met walking from Δω = 0 in the direction the torque pushes. NaN when no root lies inside the scanned span, which is the rotation bifurcating away (or a span that was too small; the scan settings say which). torque_budget is T_0, N·m; the reference rotation may be overridden, e.g. by the rotation at the dominant rational surface.
GeneralizedPerturbedEquilibrium.ErrorFields.run_monte_carlo — Function
run_monte_carlo(table, tolerances, coil_sets, ctrl=MonteCarloControl()) -> MonteCarloResult
run_monte_carlo(h5path; psi_low=0.0, psi_high=CORE_PSI_HIGH, mode=1, kwargs...) -> MonteCarloResultSample every coil set's misalignment within its tolerance and histogram the dominant-mode overlap. Per sample and coil set c with sensitivities S = (S_x, S_y) per metre and T = (T_x, T_y) per degree,
δ = Σ_c [δ_as_designed,c + S_c·(Δ_c + u_c) + T_c·(θ_c + v_c)] + Σ_g [S_g·(Δ_g + Δ_rot) + T_g·θ_g] + δ_otherwhere (Δ_c, θ_c) is the coil's own draw (additive: independent shift and tilt disks; cylinder: sample_cylinder), u_c, v_c its Gaussian placement uncertainties, (Δ_g, θ_g) one draw shared by every member of coherent group g (groups with equal phase_group share the direction), Δ_rot = −(z_m − z_pivot)·(θy_g + iθx_g) the lateral shift a rigid rotation of the group about its pivot (in the sense apply_transforms uses) gives a member at height z_m, and δ_other the unattributed budget with a random direction. S·Δ means S_x·real(Δ) + S_y·imag(Δ). The corrected histogram divides every correctable term (coils and groups not listed as uncorrectable, and the unattributed budget) by efc_factor.
coil_sets supply the major radii for tilt tolerances given in metres and the heights of group members; coil sets of the run without a tolerance block contribute their nominal overlap only. The file form rebuilds the coupling from gpec.h5, windows it, projects the stored linearization, and reads the tolerance snapshot the run echoed; kwargs are MonteCarloControl fields.
GeneralizedPerturbedEquilibrium.ErrorFields.sample_additive — Method
sample_additive(rng, R_shift, p_shift, R_tilt, p_tilt; phase_shift, phase_tilt) -> (shift, tilt)The additive tolerance model: an in-plane shift drawn in the disk of radius R_shift metres and, independently, a tilt drawn in the disk of radius R_tilt degrees, each with its own radial exponent and an optional shared direction. Returns (Δx + iΔy, θx + iθy).
GeneralizedPerturbedEquilibrium.ErrorFields.sample_cylinder — Method
sample_cylinder(rng, R, z_top, p; phase_top, phase_bot) -> (shift, tilt)The cylinder tolerance model: the coil's axis line must lie inside a cylinder of radius R metres and half-height z_top metres centred on the coil. Its two endpoints are drawn independently in the top and bottom disks (radial exponent p, optional fixed directions); the coil then sits at the axis's midplane crossing and is tilted by the axis's lean:
shift = (p_top + p_bot) / 2 # metres, Δx + iΔy
α = atan(|p_top − p_bot|, 2 z_top) # lean angle
tilt = −α · (sin φ_d + i cos φ_d) in degrees, φ_d = arg(p_top − p_bot)Shift and tilt are therefore correlated, and the tilt magnitude never exceeds atan(R / z_top). The tilt is expressed as the rotation angles (θx, θy) about the machine axes that lean the axis top toward the direction φ_d, in the sense apply_transforms uses: a positive θx leans the top toward −y and a positive θy toward −x.
The OMFIT tool's version of this model measured the endpoint separation in units of the tolerance radius against a height in metres and scaled the result by the tabulated tilt tolerance, and reused the top endpoint's direction for the bottom one; this sampler works in physical units from (R, z_top) alone with independent endpoint directions, so benchmarks against that tool compare the additive model exactly and the cylinder model only in kind.
GeneralizedPerturbedEquilibrium.ErrorFields.sample_disk — Method
sample_disk(rng, R, p; phase=2π·rand(rng)) -> ComplexF64
sample_disk(rng, R, d::RadialDistribution; phase) -> ComplexF64A point in the disk of radius R, as Δx + iΔy: radius from disk_radius and a uniform direction unless phase is given (coherent groups sharing a direction pass one phase and draw their own radii).
GeneralizedPerturbedEquilibrium.ErrorFields.sample_uncertainty — Method
sample_uncertainty(rng, σ; phase=2π·rand(rng)) -> ComplexF64The Gaussian uncertainty on where a coil actually sits, added to the tolerance draw: a signed normal amplitude of standard deviation σ times a uniform direction, σ·randn()·e^{iφ} — the OMFIT tool's construction, whose magnitude is a half-normal rather than the Rayleigh a two-dimensional Gaussian would give. Zero σ returns zero without consuming random numbers.
GeneralizedPerturbedEquilibrium.ErrorFields.scaling_label — Method
scaling_label(sc::ThresholdScaling) -> StringThe fit's key in ITPA_THRESHOLD_SCALINGS, "n=<n> <year> <dataset> <fit>".
GeneralizedPerturbedEquilibrium.ErrorFields.scaling_toroidal_mode — Method
scaling_toroidal_mode(h5path) -> IntThe toroidal mode number whose ITPA threshold scaling applies to a run.
The scalings are fitted per n, but a dominant-mode decomposition spans every n the run carried, so a single scaling only describes the result when the run carried one. Taking the lowest n and saying nothing would apply an n = 1 threshold to a mode that is partly n = 2.
GeneralizedPerturbedEquilibrium.ErrorFields.sensitivity_table — Method
sensitivity_table(sens::CoilSensitivities, dom::DominantCoupling; mode=1) -> SensitivityTable
sensitivity_table(h5path; psi_low=CORE_PSI_LOW, psi_high=CORE_PSI_HIGH, mode=1) -> SensitivityTableProject a coil linearization onto singular mode mode of dom and normalize by the axis toroidal field: δ = dot(V[:, mode], b̃) / B_T0 for the nominal spectrum and each derivative, the dimensionless overlap and its sensitivities per coil set. The window and mode are analysis choices, so re-evaluate freely on the same sens; the file form rebuilds the coupling from gpec.h5, windows it to psi_low ≤ ψ_N ≤ psi_high, and reads the stored linearization.
GeneralizedPerturbedEquilibrium.ErrorFields.single_toroidal_mode — Method
single_toroidal_mode(nlow, nhigh; where="the run") -> nlowThe one toroidal mode number a locking-threshold scaling is fitted for, or an ArgumentError when the run spans several. One rule for the in-run pipeline and the file-path entry points, so a multi-n run cannot have one n's scaling applied to a mode that spans several.
GeneralizedPerturbedEquilibrium.ErrorFields.threshold_factor — Method
threshold_factor(c::EFCCoupling, current; torque_budget, rotation_exponent=1.0, omega_reference=c.omega_reference,
model=:torque_balance, field=:residual) -> Float64Factor by which the penetration threshold is reduced under current kAt: (1 + Δω/ω_ref)^α with the balance's rotation shift for model = :torque_balance (NaN past the bifurcation or once the rotation is brought to rest), or 1 − |T|·I²/T_0 for model = :linear; field selects the residual (default) or the whole field's torque.
GeneralizedPerturbedEquilibrium.ErrorFields.threshold_samples — Method
threshold_samples(rng, sc, scen; nsample=1_000_000, dist="normal") -> Vector{Float64}Thresholds with the exponents drawn around their fitted values: "normal" (Gaussian with the standard error), "flat" (uniform within one standard error), or "normal_truncated" (Gaussian, redrawn beyond 1.5 standard errors). The exponents' uncertainties are far from small relative to the sensitivity of a power law, so the distribution is sampled rather than propagated to first order. The current exponent is drawn only for fits that have one, so the other fits' sample streams do not depend on it.
GeneralizedPerturbedEquilibrium.ErrorFields.threshold_scaling — Method
threshold_scaling(; n=1, year=2020, dataset="O,L", fit="WLS") -> ThresholdScalingLook up a published ITPA fit by toroidal mode number, publication year, dataset and fitting method; the available keys are those of ITPA_THRESHOLD_SCALINGS.
GeneralizedPerturbedEquilibrium.ErrorFields.tilt_tolerance_deg — Method
tilt_tolerance_deg(tilt, tilt_units, cs::CoilSet) -> Float64
tilt_tolerance_deg(tilt, tilt_units, r_nom::Real; name="the coil set") -> Float64
tilt_tolerance_deg(t::CoilTolerance, cs::CoilSet) -> Float64A tilt tolerance in the degrees the stored sensitivities use. "deg" passes through; "m" is a rim displacement converted through the coil set's arc-length-weighted major radius exactly as apply_transforms converts tilt_in_meters: asin(t / R_nom). The radius form takes that radius directly, as a run stores it in ErrorFields/CoilSensitivities/major_radius_m, for readers that have the file but not the geometry.
GeneralizedPerturbedEquilibrium.ErrorFields.tolerance_scan — Method
tolerance_scan(table, ts, coil_sets, mc_ctrl, sc, scen; scales, risk_ctrl=RiskControl()) -> ToleranceScan
tolerance_scan(h5path; scales, psi_low=0.0, psi_high=CORE_PSI_HIGH, mode=1, n_e, risk_ctrl=RiskControl(), kwargs...) -> ToleranceScanRun the Monte Carlo once per tolerance multiplier in scales and evaluate the locking risk of each with one threshold sampling. The file form takes the run's tolerances, coil geometry and equilibrium scalars from gpec.h5; kwargs are MonteCarloControl fields, and the threshold fit is chosen with risk_ctrl.
GeneralizedPerturbedEquilibrium.ErrorFields.torque_at — Method
torque_at(c::EFCCoupling, Δω; field=:residual) -> Float64The tabulated torque, N·m per kAt², at rotation shift Δω from a cubic spline through the scan points; NaN outside the scanned span. Without a scan the nominal torque, for any shift.
GeneralizedPerturbedEquilibrium.ErrorFields.torque_zero_crossings — Method
torque_zero_crossings(c::EFCCoupling; field=:residual) -> Vector{Float64}Rotation shifts at which the tabulated torque changes sign: each sign change between scan points is bracketed and the zero of the cubic spline found inside it. These are the neoclassical offsets of this field, as found rather than estimated.
GeneralizedPerturbedEquilibrium.ErrorFields.uncorrectable_probability — Method
uncorrectable_probability(mc::MonteCarloResult, c::EFCCoupling; delta_threshold, torque_budget, safety_factor=1.0, model=:auto, rotation_exponent=1.0) -> NamedTupleHow often the sampled machine's overlap exceeds what the array can correct: the fraction of the Monte Carlo's intrinsic |δ| beyond max_correctable_overlap's with_ntv limit for the array, with the limit it used, as a percentage. This is the explicit-correction counterpart of the corrected locking probability's efc_factor model: it asks not whether the field divided by a factor still locks, but whether the array, spending its own NTV torque, can reach the field at all. Fields abs_delta_max_correctable, probability_percent, and the model resolved.
GeneralizedPerturbedEquilibrium.ErrorFields.update — Method
update(x; field=value, ...) -> x′A copy of a ToleranceSet, CoilTolerance, CoherentGroupTolerance or OtherFieldBudget with the named fields replaced, for programmatic scans over quantities the TOML fixes: a coil's current_factor or shift_sigma_m, the unattributed magnitude, a group's tilt_lever_arm_m. Unknown fields and an efc_factor below 1 are errors. The copy of a set carries an empty raw text, since the echoed file no longer describes it.
ts2 = update(ts; coils=[c.name == "F6A" ? update(c; current_factor=0.5) : c for c in ts.coils])
ts3 = update(ts; other_field=update(ts.other_field; magnitude=2e-5))GeneralizedPerturbedEquilibrium.ErrorFields.validate_tolerances — Method
validate_tolerances(ts::ToleranceSet, coil_names) -> tsCheck every coil, group member, and uncorrectable-coil name of ts against the coil set names of a run (for instance CoilSensitivities.coil_names), throwing an ArgumentError that lists the unknown names. Coil sets of the run that carry no tolerance are allowed: they contribute only their nominal overlap.
GeneralizedPerturbedEquilibrium.ErrorFields.without_coils — Method
without_coils(table::SensitivityTable, names) -> SensitivityTableThe table with the named coil sets dropped: the error-field coil sets alone, once a correction array or any other energized coil set that is not an error-field source is taken out. Every name must be in the table. This is the table the run's Monte Carlo, worst case and scans use; the per-coil sensitivities of the excluded sets stay in the full table.
GeneralizedPerturbedEquilibrium.ErrorFields.worst_case_terms — Method
worst_case_terms(table, tolerances, coil_sets; tolerance_scale=1.0) -> NamedTuple
worst_case_terms(h5path; psi_low=0.0, psi_high=CORE_PSI_HIGH, mode=1, tolerance_scale=1.0) -> NamedTupleThe worst-case alignment bound abs_delta_worst_case of run_monte_carlo split into the terms that make it up, so the tolerance budget can be read coil by coil. Per coil set: abs_delta_as_designed is |δ_as_designed|, abs_delta_shift_tolerance is max(|S_x|, |S_y|)·(shift_tol + 3σ_shift) and abs_delta_tilt_tolerance is max(|T_x|, |T_y|)·(tilt_reach + 3σ_tilt), where tilt_reach is the tilt tolerance in degrees or, under the cylinder model, the angle the axis line can reach, atan(shift_tol / half_height). Per coherent group (group_names): abs_delta_group_shift_tolerance and abs_delta_group_tilt_tolerance, the latter including the lateral displacement a rigid rotation gives each member. abs_delta_unattributed is the unattributed budget at magnitude + 3σ. abs_delta_worst_case is the bound itself, the sum of every term; uncertainties enter at three standard deviations exactly as in the bound. Every coil set is counted (no coil_subset); tolerance_scale, scale_subset and scale_map multiply the tolerances and the coils' current_factor scales the table, both as in the Monte Carlo. The file form rebuilds the inputs from gpec.h5 as run_monte_carlo does.
GeneralizedPerturbedEquilibrium.ErrorFields.write_to_hdf5! — Method
write_to_hdf5!(h5file::HDF5.File, sens::CoilSensitivities, dom::DominantCoupling)Write the coil linearization to ErrorFields/CoilSensitivities/ — the spectra and their derivatives, plus a DominantMode/ summary projected onto mode 1 of dom (the run's full-window decomposition). An existing group is replaced. The mode labels are the file's Info/mn_index and the field normalization its Equilibrium/B_T_axis, neither duplicated here.
GeneralizedPerturbedEquilibrium.ErrorFields.write_to_hdf5! — Method
write_to_hdf5!(h5file::HDF5.File, mc::MonteCarloResult)Write the tolerance Monte Carlo histograms to ErrorFields/MonteCarlo/. The sampling settings (nsample, nbatch, seed) live in the run's [ErrorFields.MonteCarlo] table under Input/gpec_toml_raw; the tolerances in Input/RawInputs/ErrorFields/tolerance_toml_raw. An existing group is replaced.
GeneralizedPerturbedEquilibrium.ErrorFields.write_to_hdf5! — Method
write_to_hdf5!(h5file::HDF5.File, risk::RiskResult; scan=nothing)Write the locking risk to ErrorFields/Risk/, with the tolerance scan under ErrorFields/Risk/ToleranceScan/ when given. The threshold fit, scenario and sampling settings live in the run's [ErrorFields.Risk] and [ErrorFields.scenario] tables under Input/gpec_toml_raw. An existing group is replaced.
GeneralizedPerturbedEquilibrium.ErrorFields.write_to_hdf5! — Method
write_to_hdf5!(h5file::HDF5.File, couplings::Vector{EFCCoupling})Write the correction-coil couplings to ErrorFields/NTV/, and the torque-versus-rotation tables of the arrays that have one to ErrorFields/NTV/RotationScan/ (one column per array, shorter scans NaN-padded). The torque budget, threshold, rotation exponent and safety factor that turn them into a correction-current curve are analysis choices left to efc_current_curve. An existing group is replaced.
GeneralizedPerturbedEquilibrium.ErrorFields.write_tolerance_snapshot! — Method
write_tolerance_snapshot!(h5file, ts::ToleranceSet)Echo the tolerance file's text into Input/RawInputs/ErrorFields/tolerance_toml_raw, the raw input snapshot a replay reads back with read_tolerance_snapshot. Replaces an existing echo.
GeneralizedPerturbedEquilibrium.equilibrium_from_h5 — Function
equilibrium_from_h5(h5path) -> (; equil, inputs, psilim)Rebuild the PlasmaEquilibrium of a finished run from its gpec.h5 alone — the stored TOML and equilibrium ingest, through setup_equilibrium on the ψ_N grid the run ended with (Equilibrium/Geometry/psi) — without running any stage or writing anything. inputs is the parsed TOML the run used and psilim the control surface the solve integrated to (Info/psilim), so post-hoc analyses can evaluate new coil geometry on the surface the stored response matrices describe.
Rebuilding on the stored grid reproduces the run's equilibrium exactly, including a two-pass run (mpsi = 0) that re-formed its equilibrium on a refined grid.