Plan for the first ErrorFields follow-up after the current stack (#449 … #462, #473) merges into develop. Branch from develop then; do not stack it on the open PRs, since every item touches _resolve_terms in MonteCarlo.jl. Items come from the capability audit of the OMFIT error-field scripts against the Julia stack; each is small, and together they are one PR. Estimates are lines of source.
1. Range-linearity check (~40). The linear sensitivity model is asserted only locally: Sensitivity.jl stores a second-difference ratio at the finite-difference step, normalized by the set's largest first difference. Nothing checks that δ is linear out to the tolerance edge (and the cylinder tilt reach, and a coherent group's combined displacement), which is the premise every post-hoc sweep rests on; the reference scripts found S-curves at large tilts. Add linearity_check(ctx, coil_sets, tolerances; scales=(1, 2)) that evaluates applied_spectrum at ±scale·tol per degree of freedom and reports the relative error of the stored linear prediction, plus a plot sharing the loader of plot_linearity_residuals. Linearity in current needs no check (Biot–Savart). Open choice: check at each coil's own tolerance and twice it, or also at the largest scale of the tolerance scan; leaning to both ends of the scan.
2. Per-coil scan controls (~10). tolerance_scale multiplies every active coil and coil_subset sets the inactive ones to zero tolerance (_resolve_terms, active(t.name) || continue), so "scan class A while class B holds its own tolerance" is not expressible, and that is the reference's primary product plot. Let tolerance_scale accept a per-coil map (or add scale_subset: the coils the scale applies to, the rest at 1×), and make inactive coils hold their tolerance instead of dropping it. Open choice, to decide when implementing: key the map by coil name (no schema change) or add a class field to the tolerance TOML so it is keyed by class.
3. Coherent-group tilt_lever_arm_m (~5). A group tilt tolerance given in metres is converted through a member's major radius (group_tilt_deg, which now requires the members' radii to agree within 1 %). For a group representing a reference structure, the lever arm is a property of that structure, not of any member coil. Add an optional tilt_lever_arm_m key on [[ErrorFields.coherent_group]] that takes precedence over the member radius.
4. Per-coil current_factor (~10). A map applied to delta_nominal per coil in _resolve_terms, so the risk against the current in an uncorrectable coil (the reference's risk_vs_remc) is a user loop over run_monte_carlo. Also needs a keyword copy of ToleranceSet (or map_tolerances(f, ts), ~20) so sigmas and the unattributed budget can be varied programmatically; tolerance_scale deliberately does not touch them.
5. Monte Carlo validity tests (~30 of tests). None of the current tests would catch a systematic binning bias, which is exactly the bug the reference scripts had (risk moved with nsample while the batch spread said it should not). Add: Monte Carlo against the closed-form erfc risk for a Gaussian |δ| (synthetic table) × Gaussian threshold at several amplitudes where the true risk is well below a percent, agreement within batch spread; plock at nbins = 300 vs 3000 within spread; nsample convergence with the spread shrinking as √N. A shipped risk_convergence(...) diagnostic returning plock against nsample and nbins with spread is the same 30 lines.
Verification: the tolerance TOML, Monte Carlo and error-fields test files; the regression harness on diiid_n1 and diiid_error_field must be unchanged (defaults keep today's behaviour). Review package with the linearity and convergence figures on the DIII-D-like example.
Plan for the first ErrorFields follow-up after the current stack (#449 … #462, #473) merges into develop. Branch from develop then; do not stack it on the open PRs, since every item touches
_resolve_termsinMonteCarlo.jl. Items come from the capability audit of the OMFIT error-field scripts against the Julia stack; each is small, and together they are one PR. Estimates are lines of source.1. Range-linearity check (~40). The linear sensitivity model is asserted only locally:
Sensitivity.jlstores a second-difference ratio at the finite-difference step, normalized by the set's largest first difference. Nothing checks that δ is linear out to the tolerance edge (and the cylinder tilt reach, and a coherent group's combined displacement), which is the premise every post-hoc sweep rests on; the reference scripts found S-curves at large tilts. Addlinearity_check(ctx, coil_sets, tolerances; scales=(1, 2))that evaluatesapplied_spectrumat ±scale·tol per degree of freedom and reports the relative error of the stored linear prediction, plus a plot sharing the loader ofplot_linearity_residuals. Linearity in current needs no check (Biot–Savart). Open choice: check at each coil's own tolerance and twice it, or also at the largest scale of the tolerance scan; leaning to both ends of the scan.2. Per-coil scan controls (~10).
tolerance_scalemultiplies every active coil andcoil_subsetsets the inactive ones to zero tolerance (_resolve_terms,active(t.name) || continue), so "scan class A while class B holds its own tolerance" is not expressible, and that is the reference's primary product plot. Lettolerance_scaleaccept a per-coil map (or addscale_subset: the coils the scale applies to, the rest at 1×), and make inactive coils hold their tolerance instead of dropping it. Open choice, to decide when implementing: key the map by coil name (no schema change) or add aclassfield to the tolerance TOML so it is keyed by class.3. Coherent-group
tilt_lever_arm_m(~5). A group tilt tolerance given in metres is converted through a member's major radius (group_tilt_deg, which now requires the members' radii to agree within 1 %). For a group representing a reference structure, the lever arm is a property of that structure, not of any member coil. Add an optionaltilt_lever_arm_mkey on[[ErrorFields.coherent_group]]that takes precedence over the member radius.4. Per-coil
current_factor(~10). A map applied todelta_nominalper coil in_resolve_terms, so the risk against the current in an uncorrectable coil (the reference'srisk_vs_remc) is a user loop overrun_monte_carlo. Also needs a keyword copy ofToleranceSet(ormap_tolerances(f, ts), ~20) so sigmas and the unattributed budget can be varied programmatically;tolerance_scaledeliberately does not touch them.5. Monte Carlo validity tests (~30 of tests). None of the current tests would catch a systematic binning bias, which is exactly the bug the reference scripts had (risk moved with
nsamplewhile the batch spread said it should not). Add: Monte Carlo against the closed-form erfc risk for a Gaussian |δ| (synthetic table) × Gaussian threshold at several amplitudes where the true risk is well below a percent, agreement within batch spread;plockatnbins = 300vs3000within spread;nsampleconvergence with the spread shrinking as √N. A shippedrisk_convergence(...)diagnostic returningplockagainstnsampleandnbinswith spread is the same 30 lines.Verification: the tolerance TOML, Monte Carlo and error-fields test files; the regression harness on
diiid_n1anddiiid_error_fieldmust be unchanged (defaults keep today's behaviour). Review package with the linearity and convergence figures on the DIII-D-like example.