Skip to content

EQUIL - FEATURE - Optional profile_source = "integrate": build F and P in the sq spline from FF′ and p′ (values stays the default) - #297

Open
StuartBenjamin wants to merge 27 commits into
developfrom
spline_improvements
Open

StuartBenjamin wants to merge 27 commits into
developfrom
spline_improvements

Conversation

@StuartBenjamin

@StuartBenjamin StuartBenjamin commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Builds on #296. Adds an opt-in profile_source = "integrate" that builds F and p from the g-file's FF′ and p′ columns, the method of Julia GPEC #506. The default, values, is unchanged.

Problem

read_eq_efit reads FFPRIM and PPRIME but never uses them: it splines F and p and differentiates them instead. When a g-file holds single-precision values, as TokaMaker's do, and F varies by only ~2 % across the plasma, differentiating twice turns the rounding into tens of percent of noise in F″, which Δ′ depends on.

Changes

  • spline_fit_hermite(spl, endmode, keep) (equil/spline.f): the usual fit, but keeps caller-supplied knot slopes for the columns flagged in keep.
  • profile_source in equil.in, for efit and for ldp_i files with FF′/p′ records:
    • values (default): as before.
    • integrate: F²/2 and p integrated inward from the boundary, with the file's derivatives kept as knot slopes. Falls back to values with a warning if the derivatives are non-finite, F = 0 somewhere, the integrated F² ≤ 0, or only one of FF′, p′ has the opposite sign to its values.
  • The slopes are carried through direct_run/inverse_run, newq0, sq_out and the dump file.
  • read_eq_ldp_i reads optional FF′/p′ records. read_eq_efit no longer writes out of bounds when the g-file has no q column.
  • eq_type = "dump" failed for direct equilibria ("I/O past end of record": a second, 4-entry sq%title assignment in direct_run shortened the record). Fixed; dumps now also store the slopes and eqfun, and older dumps still read.

Results

Default (values): bit-identical to #296 on DIIID_ideal (DCON), DIIID_resistive (RDCON) and the CI Solovev chains.

integrate: rms error of F″ (0.1 < ψ_N < 0.85) against an exact TokaMaker equilibrium written as g-files (Python replica of GPEC's sq fit, in the zip):

g-file mpsi values integrate
257 128 1.8 % 0.8 %
257 512 12 % ~0.2 %
513 512 29 % ~0.1 %

values gets worse as the grid is refined; integrate converges. On STRIDE Δ′(2/1) it cuts the spread across three g-file resolutions from 1.43 to 0.96 (mpsi 256) and from 0.80 to 0.54 (mpsi 512). On double-precision i-files the two settings give STRIDE Δ′ within 0.6 % (2/1 within 0.2 %). When the values are already accurate there is no noise to remove, and integrate can shift pedestal surfaces, which is why values stays the default.

Tests

  • make -C regression/spline_tests run (manual, not in CI): spline_fit_hermite keeps the supplied slopes exactly, and unflagged columns equal spline_fit.
  • Comparisons above: spline_improvements_scripts.zip (commands in its README).

Dependencies

Contains #296 except its last commit (6eda483, RDCON only; no overlap). Conflicts with #300 only where both append code (equil/spline.f: keep both, renumber spline_fit_pchip to subprogram 25; the spline-test makefile and .gitignore: build both tests).


🤖 Generated with Claude Code

StuartBenjamin and others added 22 commits June 21, 2026 21:12
…explitcit indexing control to match Zeff spline assignment
…r frame

VACUUM runs on a theta-reversed boundary, so wv comes back complex-conjugated
relative to wp; conjugate it after mscvac (free_run and free_wvmats). The
eigenvector norm used jmat(jpert-ipert), the transpose of the plasma-matrix
index order; use jmat(ipert-jpert). Up-down symmetric cases are unchanged;
asymmetric ones no longer depend on the theta origin.
…er frame

Same change as DCON: conjugate wv after mscvac (free_run and
free_get_wvac, used by the Galerkin solve) and transpose the norm.
…ier frame

STRIDE called mscvac with complex_flag false, keeping only Re(wv), which is
itself origin-dependent. Keep the full matrix, conjugate it as in DCON, and
transpose the norm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ler-supplied knot derivatives

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…' and p' (efit, ldp_i)

New equil.in variable profile_source:
  values    - tabulated f, p; spline-fitted derivatives (default, unchanged)
  hermite   - tabulated f, p with the tabulated derivatives as knot slopes
  integrate - f**2/2 and p integrated inward from the boundary values
              (as in Julia GPEC PR #506), tabulated derivatives as slopes
read_eq_efit no longer discards FFPRIM and PPRIME, and fixes an
out-of-bounds index when the g-file has no q profile. read_eq_ldp_i
reads optional trailing FF' and p' records. Falls back to values with a
warning when the derivatives are non-finite or disagree in sign.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nverse sq construction

sq_in and sq are fit with spline_fit_hermite so the profile slopes
from profile_source are not re-derived by spline differentiation.
newq0 rescaling updates the f slope consistently (f' -> f'/ffac).
With profile_source = values, output is bit-for-bit unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Against a TokaMaker equilibrium with known profiles, integrate gives F'' errors
of 0.1-0.8% (0.1 < psi_n < 0.85) that fall with resolution, where values and
hermite give 2-47% that grow with mpsi (single-precision f amplified by
differentiation). STRIDE Delta' is correspondingly more consistent across
g-file resolutions. Set profile_source="values" to recover the previous
behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… end-slope order

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dcon-vacuum-theta-frame)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second assignment of 4 titles reallocated sq%title, so the dump record
was 6 bytes short and read_eq_dump failed (I/O past end of record).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
equil_out_dump appends the supplied-derivative flags with sq%fs1(:,1:2), then
eqfun; read_eq_dump reads them when present (older dumps read as before) and
no longer fails when the equilibrium is re-read (STRIDE psilim reform).
DCON efit -> dump -> dcon reproduces f, mu0p, q, D_I, D_R exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@StuartBenjamin StuartBenjamin changed the title Spline improvements Equilibrium - BUGFIX! - Build F and P values in sq spline from FF′ and p′ infromation Oct 7, 2026
@StuartBenjamin StuartBenjamin changed the title Equilibrium - BUGFIX! - Build F and P values in sq spline from FF′ and p′ infromation Equilibrium - BUGFIX! - Build F and P values in sq spline from FF′ and p′ information Oct 7, 2026
@StuartBenjamin StuartBenjamin changed the title Equilibrium - BUGFIX! - Build F and P values in sq spline from FF′ and p′ information Equilibrium - BUGFIX - Build F and P values in sq spline from FF′ and p′ information Oct 7, 2026
@d-burg

d-burg commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

A flag before this is reviewed: the Julia PR this ports (OpenFUSIONToolkit/GPEC#506) is now parked, and I would not make integrate the default here. Your OFT change (OpenFUSIONToolkit/OpenFUSIONToolkit#358) turns out to be the cleaner fix. Details are in OpenFUSIONToolkit/GPEC#504.

I ran this branch (a95a365b, STRIDE, psilow 0.01, psihigh 0.998, mtheta 512) on bouquet's public D3D-like example: four equilibria, each written by the single-precision writer and by the #358 writer from the same solver state.

1. With double-precision files, values is already clean. Same state, two g-files whose inputs differ only in the last digits; rms gap in Δ′(2/1) over three states:

mpsi single, values single, integrate double, values double, integrate
256 0.18 0.026 0.023 0.034
512 1.10 0.018 0.067 0.022

2. integrate changes files that have no noise to remove. integrate minus values on the double-precision files, mpsi 256:

state 2/1 3/1 4/1 5/1 6/1
A +0.04 +0.04 0.00 +7.4 (of 182) +19.1 (of −63)
B −0.04 −0.04 +0.02 −4.6 (of 896)
draw 1 −0.01 +0.03 +0.14 −0.7 (of 245)
draw 2 +0.54 −0.01 +0.61 −2.9 (of 636)

At mpsi 512 draw 2 gives +0.15 on 2/1 and +1.5 on 4/1.

The cause is the quadrature. Integrating FF′ and p′ needs an assumed shape between nodes (a cubic spline here and in #506). Near steep or kinked pedestal profiles that differs from the file's own F and P. EFIT-family files tabulate FPOL and PRES as the trapezoid integral of FFPRIM and PPRIME, so no single rule matches every writer. In the Julia tests the cubic, trapezoid and Akima rules agree on Δ′(2/1) to 0.1 but differ by several units on surfaces in the pedestal foot.

Suggestion. Keep profile_source = "values" as the default, and keep integrate as an opt-in for g-files already written in single precision, with a note that outer surfaces can move. The rest of the PR (the Hermite fit, the dump fix, pchip for tabulated inputs, the reader bug fix) is not affected by this.

I can share the eight g-files and the run script if you want to reproduce it.

Derivative sign taken from f and p together, so a flat p (or f) no longer
forces the values fallback; the dump writes eqfun only when it exists;
shorter equil.in entry; unit test drops redundant wrappers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StuartBenjamin and others added 3 commits October 12, 2026 22:51
Tabulated values paired with tabulated derivatives put the values' rounding
noise into f'' (the worst option in the comparison). spline_fit_hermite stays,
used by integrate to keep the derivatives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review (d-burg): with double-precision g-files the tabulated f, p are
clean, and integrate can move outer surfaces because the quadrature
assumes a profile shape between knots near pedestal kinks. Keep the
previous behaviour (values) as the default and offer integrate for
single-precision g-files, where differentiating rounded f, p sets f''.

With the default, the DIIID_ideal (DCON) and DIIID_resistive (RDCON)
examples are bit-identical to the base branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pes, ldp_i profile_source warning

- inverse_run evaluates sq_in once per surface with mode 1 instead of a
  third evaluation at the same psifac.
- Every spline_fit_hermite call on sq, sq_in and sq_out passes
  sq_in_slopes; entries 3:4 are always false.
- ldp_i warns when profile_source is not "values" and the file has no
  ffp, pp records, as the other fallbacks do.

Default (values) DCON DIIID_ideal, RDCON DIIID_resistive and ldp_i
outputs are bit-identical to a4b144d5, as are sq_out.bin, the dump and
dump re-reads (newq0 = 0 and 1.1), and ldp_i with integrate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@StuartBenjamin StuartBenjamin changed the title Equilibrium - BUGFIX - Build F and P values in sq spline from FF′ and p′ information EQUIL - FEATURE - Optional profile_source = "integrate": build F and P in the sq spline from FF′ and p′ (values stays the default) Oct 13, 2026
@StuartBenjamin

StuartBenjamin commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks, agreed. values is the default again (a47fc020), and integrate stays as an opt-in for single-precision g-files; the equil.in entry says that outer surfaces can move. With the default, the DIIID examples and the CI Solovev chains are bit-identical to the base branch. I also moved the pchip fit for tabulated Zeff and kinetic profiles to its own PR (#300), since it changes default results for runs that read a kinetic_file and has nothing to do with profile_source. 78977ab9 adds small review cleanups (bit-identical). The branch was force-pushed for the split.

@StuartBenjamin
StuartBenjamin requested a review from d-burg October 13, 2026 06:40
@StuartBenjamin StuartBenjamin self-assigned this Oct 13, 2026
@StuartBenjamin
StuartBenjamin marked this pull request as draft October 13, 2026 07:25
@StuartBenjamin
StuartBenjamin marked this pull request as ready for review October 13, 2026 07:38
@StuartBenjamin
StuartBenjamin requested review from matt-pharr and removed request for d-burg October 13, 2026 08:08
…file_source comments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants