Repository navigation
ErrorFields/ForcingTerms - FEATURE - Add the tolerance TOML schema and parser - #449
Conversation
…ivities New src/ErrorFields module: for every coil set of a coil-forced run, central-difference linearization of the root-area-weighted control-surface spectrum with respect to rigid shifts (m) and tilts (deg), on one shared boundary grid per toroidal mode, conformed with R⁻¹ through ResonantCoupling. The stored primitive is the spectrum linearization (CoilSensitivities) so the ψ window, singular mode, and B_T0 normalization remain post-hoc analysis choices; sensitivity_table projects it (in memory or from gpec.h5) to the dimensionless overlap δ and its derivatives, with the in-plane RMS and the closed-form cancelling offsets. Writes ErrorFields/CoilSensitivities/ with a DominantMode/ full-window summary, driven by an [ErrorFields] TOML section (fd steps, rotation_center = conductor|set). Rerun gains a side-effect-free equilibrium_from_h5 (equilibrium rebuilt on the run's stored ψ grid; shared input rebuild factored out of build_inputs_from_h5) and the post-hoc entry point compute_coil_sensitivities(h5path, coil_sets) that sweeps any coil geometry against a stored solve. The driver runs the stage after KineticForces; the DIII-D example carries the section and the diiid_n1 harness case tracks |δ_nominal| and the sensitivity norms. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
… set's largest tap A tap whose first difference vanishes by symmetry (vertical shift of an axisymmetric hoop, in-plane shift of an n=1-phased array) still has a genuine second-order response; dividing it by its own near-zero first difference flagged every such coil. The residual is now the second difference relative to the set's largest first difference over all six taps, which is the scale the linear tolerance model actually keeps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
DIIID-like_error_field_example: the DIII-D-like ideal solve 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 (OpenFUSIONToolkit TokaMaker DIIID_geom.json), run through the [ErrorFields] stage. analyze_example.jl ranks the coils by error field per millimetre of shift and per tenth of a degree of tilt on the dominant mode, over all rational surfaces and over the edge, and plots the stored spectra behind the ranking. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…d parser The manufacturing-tolerance input of an error-field assessment is a TOML file separate from gpec.toml, named by the new ErrorFieldsControl.tolerance_file: [[ErrorFields.coil]] blocks (shift disk radius, tilt in degrees or metres, Gaussian uncertainties, radial sampling shape, additive or cylinder model), [[ErrorFields.coherent_group]] blocks (members sharing one draw, phase_group labels sharing a direction, rigid-rotation pivot height), the correctability split and the unattributed budget, with an [ErrorFields.defaults] table for omitted keys. Parsing is schema-checked (unknown keys error), validate_tolerances cross-checks names against a run's coil sets, and the driver echoes the file into Input/RawInputs/ErrorFields/tolerance_toml_raw for replay (read_tolerance_snapshot). tilt_tolerance_deg converts a rim-displacement tilt through the coil set's arc-length-weighted major radius, now ForcingTerms.nominal_major_radius, factored out of apply_transforms unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
Stack summary — OMFIT → Julia error-field tolerance migration (updated 2026-09-17)Nothing in this set merges without a third-party human review, the three small bugfix PRs included. Full page with the diagram, per-PR table, review order and links to every review package (a private claude.ai artifact until the author shares it; DIII-D-like and synthetic data only): https://claude.ai/code/artifact/408ddd36-e52c-42d8-8f5c-52f680f41da9
Benchmark, qualitatively. The original OMFIT project's case, rebuilt from that project's own run inputs, is reproduced once #458, #457 and #460 are in: dominant-mode singular values agree to 1e-4, singular-coupling rows match Fortran GPEC at 1.0000 correlation and within 0.5 % in norm, and every coil set's dominant-mode overlap agrees within 1 % once both codes use the same converged toroidal coil grid. The DIII-D-like example agrees with Fortran to 1e-4 throughout. Any comparison shown on these PRs uses the DIII-D-like examples only. Design issue for what comes after the stack: #461 (momentum-balance module fed by tabulated torque surfaces; stationary solvers only). |
…lds-sensitivities
…y point to the core window Follows the PerturbedEquilibrium change that makes psi_N <= 0.9 (CORE_PSI_HIGH) the default window of the dominant-mode SVD: sensitivity_table(h5path) now defaults to the same window, so in-memory and post-hoc tables agree, and the example's analysis uses the core window alone instead of contrasting it with an edge-only window. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…elds-tolerance-toml
…lds-sensitivities
… log line The curvature and sign-convention comments explained their derivations at length where the claim itself is the point; the non-positive b_t0 guard and the sensitivity table's basis-length guard had no test; one log line ran past the 180-column margin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…elds-tolerance-toml
The error-field stage appends its own output to what main returns, which the result-struct test asserted exactly. It now pins the three stages it is about, so stages added above it in the stack do not break it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…elds-tolerance-toml
…s-sensitivities # Conflicts: # src/ForcingTerms/CoilFourier.jl
…elds-tolerance-toml
…errorfields-sensitivities
…errorfields-sensitivities
…gineering-unit sensitivities Comparing coil revisions against a stored solve is what this module was built for, but doing it needed convention knowledge the package did not hand over. The chain from a run and a directory of coil files to a table of overlaps existed only as a private closure inside the sensitivity sweep, so a user either reimplemented it or paid for thirteen finite-difference taps per set and discarded twelve. Four different quantities were all called "overlap", differing by factors of B_T0 and the spectrum norm, none distinguished by name or signature. - `CoilOverlap` carries every normalization at once — δ, the raw projection, the resonant fraction and the spectrum norm — so a caller never has to work out which one a bare number was in. It also carries the spectrum itself, which is what lets a diagnostic ask why a coil couples as it does. - `coil_overlaps` is the one-call path, and the sensitivity sweep now shares its `applied_spectrum`, so the conform-and-project convention has a single implementation. - `combine_overlaps` applies a design current pattern by scaling, which is the whole reason a spectrum is worth caching. An unmatched name raises rather than contributing zero, so a coil renamed between revisions cannot quietly drop out of a comparison. - `PostHocContext` gathers the five objects that always travel together and is where the overrides hang: the boundary resolution a stored deck otherwise supplies silently, and its `dat_dir`, which records an absolute path from whichever machine ran it. - `SensitivityTable` reports `delta_per_mm_shift`, `delta_per_deg_tilt` and `delta_per_mm_rim` in place of the SI direction-RMS pair, the units mechanical tolerances actually arrive in. That needs `CoilSensitivities.nominal_radius`, computed where the coil sets are in hand. `MIN_NZETA_PER_PERIOD` is the package's own default rather than a magic number: against a 128-point reference, 16 points per period is about 1 % off, 20 about 0.4 %, 24 about 0.1 %, and the default 32 is within one part in 1e5. A deck coarser than that now warns. Tests assert the one-call path against the chain assembled by hand, and that combining the run's own coil sets at unit weight reproduces the forcing the run wrote out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…lds-sensitivities
…arries `PostHocContext` said when it is built, not what it holds. It carries the dominant resonant mode and the field normalization alongside the equilibrium and boundary grids, which is error-field work specifically: everything needed to ask how much resonant field a coil drives, and nothing that depends on which coils they are. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…elds-tolerance-toml Resolved: the duplicate nominal_major_radius, which moved up to the ForcingTerms branch, and the ErrorFields export list, which gained both sides.
…page prose Documenter resolves `[`X`](@ref)` in a markdown page against `CurrentModule`, which this page does not set, so the two links pointing at the new types failed the cross-reference pass and terminated the build. No other prose page in `docs/src/` uses them; plain backticks match the convention, and the `@ref`s inside docstrings still resolve because those carry their module context. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
…elds-tolerance-toml
Picks up the rebuilt sensitivities branch as it landed on develop. The branch was based on the pre-rebuild tip, so the two lineages had no common ancestor for the ErrorFields files; merged against the real base so the accepted review changes carry through rather than being reverted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
|
@ebursch heads up on one commit added after your approval, since it touches files you reviewed.
Merged against the real base instead, which resolved cleanly with no conflicts. Verified two ways: Tests on the merged branch: |
Carries the develop merge down the chain after the sensitivities and mode-basis branches landed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzKDmtKYDotJWrcxWf1oJu
Fourth PR of the OMFIT → Julia error-field tolerance migration, stacked on #448. It fixes the tolerance input format: a TOML file, separate from
gpec.toml, holding the manufacturing tolerances of each coil set and coherent group, the correctability split, and the unattributed error-field budget. The sampling, Monte Carlo, and risk stages that consume it follow in PR5–PR7; this PR gives them a validated, replayable input and keeps device numbers out of the repository.What it does
src/ErrorFields/ToleranceTOML.jl:read_tolerance_toml/parse_tolerance_toml→ToleranceSet(CoilTolerance,CoherentGroupTolerance,OtherFieldBudget), with an[ErrorFields.defaults]table for per-coil fallbacks. Every key is schema-checked (a misspelled key errors), tolerances non-negative, enumerations normalized,efc_factor ≥ 1, a cylinder model needs a positive half-height.validate_tolerances(ts, coil_names)cross-checks coil, member, and uncorrectable names against a run's coil sets;tilt_tolerance_degconverts a rim-displacement tilt tolerance in metres to degrees through the coil set's arc-length-weighted major radius, exactly as the coil loader'stilt_in_metersdoes — that radius is nowForcingTerms.nominal_major_radius, factored out ofapply_transforms.ErrorFieldsControl.tolerance_file(relative to the run directory): the driver reads, validates against the run's coil sets, and echoes the text intoInput/RawInputs/ErrorFields/tolerance_toml_raw;read_tolerance_snapshot(h5path)reads it back.test/test_data/ErrorFields/tolerances_two_hoops.toml; 45 parser tests plus the echo round trip inside the existing Solovev error-fields run.Three schema points that differ from the plan sketch, taken from the OMFIT source rather than the plan (worth a look, @logan-nc):
phasing_indexin OMFIT is a per-row label: coherent rows sharing a label share the random direction while keeping independent amplitudes. The schema therefore hasphase_group = "<label>"per group (default: the group's own name), not per-member phases in radians.(z − z_pivot)·θto each member, with a code note that a specifiable centre was wanted.rotation_center_z_m(default 0) carries that for PR6."hollow"(OMFIT'smisalignment_pdf_typesdefault, used by the SPP risk scans),other_field.radial_shapedefaults to"ring"(OMFIT'sother_delta_pdf_type).flatkeeps its OMFIT meaning (uniform in radius);uniform_areais the new evenly-sampled option.One thing noticed for PR5, not encoded here: OMFIT's cylinder sampler sets
phase_bot = phase_unique_top[...]— the bottom-disk phase it computes is never used, so both axis endpoints share a direction and the sampled axis line is always coplanar with ẑ. That looks like a typo rather than a model; PR5 will sample the endpoints independently and can carry a reproduction toggle if you want the benchmark to match it.cc @matt-pharr
Release note
Error-field tolerances can now be given in a TOML file named by
tolerance_filein[ErrorFields]: per-coil shift and tilt tolerances (with sampling shape and additive or cylinder model), coherent groups, the correctable/uncorrectable split, and an unattributed budget. The file is validated against the run's coil sets and echoed intogpec.h5for replay.Regression report
Harness, re-run at this branch head
69a41a912compared againstdevelop(9578c9b86), casediiid_n1:Every tracked quantity is bit-identical; only wall-clock
Runtimediffers, which theharness does not track. The branch carries a merge of the current
develop.