Skip to content

Verify theta-reversal / forward-DFT convention in the gpvacuum_flxsurf port (surface inductance) #242

@logan-nc

Description

@logan-nc

Summary

The Julia port of Fortran gpvacuum_flxsurfcompute_surface_inductance_from_greens in src/PerturbedEquilibrium/SingularCoupling.jl — does not reproduce the Fortran theta-reversal / Fourier-transform sequence exactly. The current Julia form was chosen because it reproduces the previously-validated numerical result, but a more faithful port produces significantly different singular-coupling metrics. Which form actually matches Fortran best is unresolved and needs a deeper trace plus broader benchmarking.

Surfaced during the PR #196 review while unifying the FourierTransform sign convention.

What Fortran does

gpvacuum_flxsurf (in gpec/gpvacuum.f) builds the surface-inductance "current" matrix from the vacuum Green's functions. Per column mode, for each theta point it:

  1. Reads the Green's function grri/grre at the reversed theta index (rtheta = mthsurf - itheta).
  2. Forms the complex field grri_real - i*grri_imag (and the exterior analogue).
  3. Multiplies by the toroidal phase EXP(-i*n*dphi) evaluated at the forward index (itheta+1).
  4. Applies iscdftf — the exp(-imθ) forward DFT.

Key point: Fortran reverses only the un-phased Green's function; the toroidal phase is applied un-reversed. The theta reversal exists because the Green's-function rows come from VACUUM/mscvac, whose theta grid runs opposite to GPEC/DCON theta.

What Julia currently does

After PR #196 unified the FourierTransform forward to exp(-imθ) (Fortran iscdftf), compute_surface_inductance_from_greens computes:

g_phased = (kax_re - im*kax_im) .* (cos_nν - im*sin_nν)   # Green's function × phase
current_matrix[:, i] = ft(_reverse_theta(g_phased))        # reverse the PHASED product

i.e. Julia reverses the phased product (Green's function × phase together), not just the un-phased Green's function. This is algebraically equivalent to the historical pre-unification behavior (the old real-input ft used exp(+imθ), which silently absorbed a full reversal), and it reproduces the validated DIII-D benchmark.

There is also a smaller open detail: _reverse_theta uses circshift(reverse(v), 1) vs plain reverse(v) — these differ by a single-grid-step exp(im·2π/N) per-mode phase, and the correct choice depends on the exact index convention of iscdftf / the Julia ft.

Finding: a more faithful port changes the answer significantly

A stricter port matching Fortran — reverse only the un-phased Green's function, apply the phase forward — was benchmarked against Fortran GPEC (DIII-D, n=1):

q ψ Φ_res err, current Julia Φ_res err, faithful port
2 0.594 14.6% 97.2%
3 0.819 9.8% 9.0%
4 0.928 21.2% 38.1%
5 0.988 50.6% 4.3%

The faithful port sharply improves the edge surface (q=5: 50.6% → 4.3%) but badly regresses the core (q=2: 14.6% → 97%). Neither pure form is cleanly correct, so the stricter port was not merged. The mixed result strongly suggests a real theta-convention mismatch somewhere in the chain — most likely the Julia Vacuum module orders the Green's-function rows and/or the SFL toroidal offset ν differently than Fortran orders grri vs dphi, so a single-call-site fix cannot resolve it.

What needs to be done

  1. Trace the Julia Vacuum theta conventions end-to-end. Determine the theta ordering of the grri/grre rows produced by the Julia Vacuum module, and the ordering of ν from Vacuum.PlasmaGeometry, and compare against how Fortran orders grri (VACUUM theta) vs dphi (DCON theta). Confirm whether the Julia reimplementation preserves or removes the VACUUM/DCON theta-order mismatch.
  2. Pin the iscdftf index convention so the _reverse_theta grid-step ambiguity (reverse vs circshift(reverse,1)) is settled analytically, not by trial.
  3. Benchmark with equilibrium scans, not the single DIII-D n=1 case — multiple equilibria and multiple toroidal mode numbers — to determine which form genuinely matches Fortran across the board. The q=2-vs-q=5 split above shows a single case is not enough to decide.

Pointers

  • Julia consumer: src/PerturbedEquilibrium/SingularCoupling.jlcompute_surface_inductance_from_greens and the _reverse_theta helper.
  • Fortran reference: gpec/gpvacuum.fgpvacuum_flxsurf; DFT definitions in gpec/ismath.f (iscdftf/iscdftb).
  • Context: PR Perturbed Equilibrium and Coil Biot-Savart #196 (commit bc321504 unified the FourierTransform convention and made the reversal explicit).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions