Skip to content

ForcingTerms - FEATURE - Use the exact straight-segment field in the coil Biot-Savart sum - #590

Merged
matt-pharr merged 1 commit into
developfrom
bugfix/coil-segment-biot-savart-20261025
Oct 26, 2026
Merged

matt-pharr merged 1 commit into
developfrom
bugfix/coil-segment-biot-savart-20261025

Conversation

@krystophny

Copy link
Copy Markdown
Collaborator

Closes #589.

src/ForcingTerms/BiotSavart.jl evaluated every straight segment of a coil polyline as a single current element at its midpoint, a one-point quadrature whose relative error is of order (L/d)^2. This change replaces it, in both accumulate_strand_field! and the threaded kernel behind compute_biot_savart_boundary!, with the exact field of a straight filament segment (Hanson and Hirshman, Phys. Plasmas 9, 4410 (2002), Eq. 8), the same fix as GPEC#361 for the Fortran GPEC#360. Both kernels now call one @inline helper, _segment_field, and PrecomputedSegments stores the segment start points and lengths instead of the midpoints. The exported functions keep their signatures and units. The on-conductor guard still skips a segment whose distance to the observer is below 1 μm, now measured as the effective perpendicular distance ρ² = 2RᵢR_f(Rᵢ + R_f - L)/(Rᵢ + R_f) rather than as the distance to the midpoint.

Release note

  • Audience: users
  • Numerical impact: coil fields from CoilSet polylines are now exact for the given polygon; for coils whose straight segments are long compared with their distance to the plasma boundary, the forcing spectrum changes by up to the former midpoint error (tens of percent at d/L ≲ 1). The DIII-D C-coil case in diiid_error_field moves by 0.01 %. (harness @ abd73d4)
  • Migration: none; re-run coil-driven cases whose coil files have long straight sides, such as window-frame or correction coils given by their corners

Coil fields were computed with a midpoint rule along each straight coil segment, which overestimated or underestimated the field by up to tens of percent near coils with long straight sides. They are now exact for the coil polygon.

Regression report

Regression Report: diiid_error_field
=========================================================================================================================================
Ref 1: 81fb41d03  @ 81fb41d03 (2026-10-25)
       env: julia 1.13.2, x86_64-pc-linux-gnu, manifest cb4201da (pinned), 32 threads/16 BLAS, jit build
Ref 2: bugfix/coil-segment-biot-savart-20261025  @ abd73d47c (2026-10-25)
       env: julia 1.13.2, x86_64-pc-linux-gnu, manifest cb4201da (pinned), 32 threads/16 BLAS, jit build
-----------------------------------------------------------------------------------------------------------------------------------------
Quantity                                         81fb41d03     bugfix/coil-segment-biot-savart-20261025  Diff               Status       
-----------------------------------------------------------------------------------------------------------------------------------------
EFC overlap per kAt                              [1 elem]      [1 elem]                                  9.033e-10 (0.01%)  ** CHANGED **
EFC whole-field NTV torque per kAt² [N·m]        [1 elem]      [1 elem]                                  3.591e-06 (0.01%)  ** CHANGED **
EFC residual-field NTV torque per kAt² [N·m]     [1 elem]      [1 elem]                                  1.467e-07 (0.01%)  ** CHANGED **
EFC reference rotation ω_ref [rad/s]             [1 elem]      [1 elem]                                  0.0e+00            OK           
EFC rotation-scan shifts [rad/s]                 [1 elem]      [1 elem]                                  0.0e+00            OK           
EFC residual torque against rotation [N·m/kAt²]  [1 elem]      [1 elem]                                  5.806e-07 (0.01%)  ** CHANGED **
dominant-coupling singular values                [3 elem]      [3 elem]                                  0.0e+00            OK           
|delta_nominal| of coil set 1                    1.406986e-06  1.406914e-06                              7.2e-11            OK           
|Σ δ_as_designed| of the error-field coils       7.823268e-19  1.278911e-18                              5.0e-19            OK           
Monte Carlo mean |δ| of the error-field coils    4.703713e-05  4.703571e-05                              1.420e-09 (0.00%)  ** CHANGED **
Runtime (s)                                      1097.9s       734.9s                                                       --           
=========================================================================================================================================
Summary: 5 changed, 5 unchanged

The baseline 81fb41d is origin/develop at the time of the run.

Notes for reviewers

  • Tests in test/runtests_coils.jl: a single segment against the textbook finite-wire field at d = 0.05-2 m (rtol 1e-12), @inferred on the kernel, an observer on the conductor, and a rectangle whose sides are split into 1 or 16 collinear pieces (agreement to 1e-12). The new tests fail on develop (single segment off by up to a factor 10 at d = 0.05 m). Full Pkg.test() passes on Julia 1.13.2 (8448 assertions); Julia 1.11/1.12 were not run locally.
  • Reproducer from ForcingTerms - FEATURE - Exact straight-segment field instead of a midpoint current element per polyline segment #589 after the fix: max|B_m - B_256|/max|B_256| = 4.5e-16 to 7.9e-16 for m = 1-16 (develop: 1.4 down to 5.7e-4).
  • Kernel cost, one thread, 2e7 segment-observer pairs: 0.032 s (midpoint) to 0.044 s (exact).
  • The repository's JuliaFormatter hook reformatted the whole of BiotSavart.jl, so some unchanged lines in that file are re-indented.

Chris&AI

@krystophny krystophny self-assigned this Oct 25, 2026
@krystophny
krystophny requested a review from matt-pharr October 25, 2026 19:54
@github-actions github-actions Bot added bugfix Something was wrong and now is not changed-results Results move or an interface breaks - read before upgrading labels Oct 25, 2026
@krystophny
krystophny marked this pull request as ready for review October 25, 2026 19:57
@matt-pharr

Copy link
Copy Markdown
Collaborator

@krystophny Hi, please see my comment on the fortran equivalent PR. How does this affect the convergence of non-straight coils?

@krystophny

Copy link
Copy Markdown
Collaborator Author

The convergence for round coils, with a plot, is answered on the Fortran pull request: PrincetonUniversity/GPEC#361 (comment). It applies unchanged here, since both versions use the same formula.

Chris&AI

@krystophny krystophny changed the title ForcingTerms - BUGFIX! - Use the exact straight-segment field in the coil Biot-Savart sum ForcingTerms - FEATURE - Use the exact straight-segment field in the coil Biot-Savart sum Oct 26, 2026
@github-actions github-actions Bot added feature New capability and removed bugfix Something was wrong and now is not changed-results Results move or an interface breaks - read before upgrading labels Oct 26, 2026
@matt-pharr

Copy link
Copy Markdown
Collaborator

Independent local coil-resolution study at PR head abd73d47c:

I evaluated both methods over the full DIII-D-like example plasma boundary, rather than at a single observation point. The exact-segment calculation uses this PR's Julia kernel; the midpoint calculation uses the previous midpoint expression.

Setup

  • Equilibrium: examples/DIIID-like_ideal_example/TkMkr_D3Dlike_Hmode.geqdsk, using the example's equilibrium settings and outermost computed surface, psi_N = 0.995.
  • Filament hoop at the DIII-D F6A centerline location: radius 2.6124 m, Z = 0.4376 m, shifted horizontally by 1 mm. Coordinates come from OMFIT's DIII-D PF hardware description. Closest hoop-to-surface distance is approximately 0.400 m.
  • Surface sampling: 256 poloidal points in Hamada coordinates × 128 toroidal points, with GPEC's poloidally varying physical toroidal-angle offset included.
  • Reference: the analytic field of the displaced smooth circular loop, evaluated using complete elliptic integrals, independently of coil polygon resolution.
  • Uniformly spaced vertices on the same circle for each polygon. Unit ampere-turn; all errors below are relative and independent of current.

Error definitions

  • Total |B|: maximum over the sampled surface of abs(|B_N| - |B_ref|) / |B_ref|.
  • n=1 vector: relative surface-area-weighted L2 error in the complex n=1 cylindrical field vector (B_R, B_phi, B_Z) as a function of poloidal position.
  • n=1 normal: relative surface-area-weighted L2 error in the complex n=1 normal field, B·n_hat.
  • Poloidal vector: norm(Phi_N - Phi_ref) / norm(Phi_ref) for the full complex vector of n=1 poloidal coefficients, m = -128:127. This uses GPEC's unit-norm normal-flux convention, Phi = 2*pi*R*(B_R*dZ/dtheta_norm - B_Z*dR/dtheta_norm), and Fourier basis exp(-i*(m*theta - zeta)). Errors include both amplitude and phase, rather than comparing only coefficient magnitudes.

Full convergence table — all error columns are percentages

Segments Chord length (m) Method Total |B| error (%) n=1 vector error (%) n=1 normal error (%) Poloidal-vector error (%)
8 1.9994 Exact segment 62.6456 107.253 110.762 114.375
8 1.9994 Midpoint 552.58 202.864 209.73 216.718
16 1.0193 Exact segment 8.09947 16.3437 16.7751 17.2334
16 1.0193 Midpoint 54.056 26.6621 27.3461 28.0758
32 0.51212 Exact segment 1.714 3.70592 3.79846 3.89729
32 0.51212 Midpoint 5.51588 5.89227 6.03246 6.1832
64 0.25637 Exact segment 0.418235 0.905201 0.927493 0.951332
64 0.25637 Midpoint 0.712299 1.43066 1.46405 1.50003
128 0.12822 Exact segment 0.104107 0.225005 0.230527 0.236434
128 0.12822 Midpoint 0.176326 0.355095 0.363344 0.372236
256 0.064116 Exact segment 0.0259988 0.0561708 0.0575481 0.0590216
256 0.064116 Midpoint 0.0440234 0.0886146 0.0906705 0.0928871
512 0.032059 Exact segment 0.00649795 0.0140377 0.0143818 0.01475
512 0.032059 Midpoint 0.0110022 0.0221437 0.0226573 0.0232111
1024 0.016029 Exact segment 0.00162438 0.0035091 0.00359512 0.00368716
1024 0.016029 Midpoint 0.00275034 0.00553531 0.00566368 0.0058021
2048 0.0080147 Exact segment 0.000406088 0.000877257 0.00089876 0.000921768
2048 0.0080147 Midpoint 0.00068757 0.00138379 0.00141588 0.00145048

Both methods converge as N^-2 once sufficiently resolved, consistent with the curved-coil explanation in the Fortran PR discussion. Exact segment integration improves the error coefficient but leaves the geometric error from replacing a circle with straight chords. At 64 segments, the full poloidal-vector error is 0.951% with exact segments versus 1.500% with midpoint evaluation. At high resolution, exact segments reduce that error by about 36% at fixed segment count in this case.

Checks and scope: The elliptic-integral reference agrees with independent direct numerical Biot–Savart integration at three sampled surface locations to relative errors below 8e-16. Doubling toroidal sampling from 128 to 256 for N = 32, 64, 128, 256 changes the n=1 field vector by less than 1.4e-7 of the reference vector norm; the analytic reference's n=1 vector changes by about 1.1e-13. The harmonic comparison includes every mode resolved by the 256-point poloidal grid; this is a coil-resolution scan on a fixed equilibrium/surface grid, not a poloidal-grid convergence study. This is one filament hoop without finite conductor cross-section or plasma response, so it supports the improvement for a representative PF-coil placement rather than establishing a universal resolution threshold.

@matt-pharr matt-pharr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good contribution and is physically valid. It improves convergence moderately in number of coil points required for an accurate field. See my above comment with a table doing a PF error field study on the DIII-D-like plasma. This is to be merged after the stack including #584.

@matt-pharr
matt-pharr enabled auto-merge October 26, 2026 15:41
@matt-pharr
matt-pharr disabled auto-merge October 26, 2026 15:41
@matt-pharr
matt-pharr merged commit 72f8e23 into develop Oct 26, 2026
13 of 17 checks passed
@matt-pharr
matt-pharr deleted the bugfix/coil-segment-biot-savart-20261025 branch October 26, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ForcingTerms - FEATURE - Exact straight-segment field instead of a midpoint current element per polyline segment

2 participants