Skip to content

DCON/RDCON/STRIDE: put the vacuum matrix and norm in the plasma's Fourier frame - #295

Open
jhalpern30 wants to merge 3 commits into
developfrom
bugfix/dcon-vacuum-theta-frame
Open

jhalpern30 wants to merge 3 commits into
developfrom
bugfix/dcon-vacuum-theta-frame

Conversation

@jhalpern30

@jhalpern30 jhalpern30 commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

DCON's free-boundary energy depends on where θ = 0 sits on an up-down asymmetric equilibrium. Moving θ = 0 only relabels the coordinates, so it can't change δW. This PR fixes that by putting the vacuum matrix wv and the eigenvector norm in the same Fourier frame as the plasma matrix wp. The change is a few lines each in dcon/free.f, rdcon/free.f and stride/free.f. It is the Fortran counterpart of OpenFUSIONToolkit/GPEC#503.

Same result, different mechanism. The Julia PR removes the θ reversal, so its vacuum runs in GPEC's own frame. This PR keeps VACUUM's reversed θ and conjugates wv afterwards. In JPEC both routes give the same wv and I_v to ≤ 4e‑14 (PEST, Hamada, no wall and conformal wall). I chose different routes because the Julia change is more extensive but has longer term benefits of clarity, whereas this is the most direct fix to obtain a correct result.

The bug

  • free.f passes VACUUM a θ-reversed boundary. Reversing θ complex-conjugates every harmonic, so wv comes back in the conjugate frame of wp.
  • gpvacuum.f already conjugates the surface current for this reason; wv was never conjugated.
  • The norm summed jmat(jpert-ipert)*wt(ipert)*CONJG(wt(jpert)), the transpose of ∮J|ξ|²: amat puts that coefficient on CONJG(ξ_i)*ξ_j.

The fix

  • DCON: wv=CONJG(wv) after mscvac in free_run and in free_wvmats (the psiedge scan); jmat(jpert-ipert) → jmat(ipert-jpert) in both normalizations.
  • RDCON: the same in free_run and free_get_wvac (Galerkin).
  • STRIDE: the same, plus complex_flag=.TRUE. for its mscvac call. STRIDE kept only Re(wv), which is itself origin-dependent, so the conjugation alone does nothing there.

Evidence, all from Fortran DCON (PEST, n = 1, no wall); least-stable δW over 16 θ origins:

case develop this PR
up-down asymmetric 0.2003 – 0.3998 0.199490 ± 4e‑6
up-down symmetric (D shape), rolled 0.2628 – 0.4665 0.262787 ± 3e‑6
  • At the usual origin the symmetric case is unchanged (0.262786963 on both).
  • The psiedge scan is now independent of the origin too: the end point is 0.1995 at both origins tested (develop 0.2003 and 0.3607).
  • Stock examples:
    • solovev_ideal and a10_ideal are unchanged (δW to 1e‑10).
    • DIIID_ideal moves 0.772038 → 0.771843; the vacuum and plasma parts each move about 1.5 %.
  • This PR and JPEC's fix agree to 0.42 % on the asymmetric case. The two codes already differed by that much before either fix (0.41 %), from resolution.
  • GPEC reads wt0 from DCON, so the default energy route for Λ and P picks up the fix with no patch of its own. The surface-current route was already in the right frame.

RDCON and STRIDE, same asymmetric / symmetric decks, 5 origins:

code develop this PR
RDCON asymmetric 0.2018 – 0.3837 0.201005 ± 1e‑5
RDCON symmetric 0.2641 – 0.4351 0.264112 ± 1e‑5
STRIDE asymmetric 0.2001 – 0.3406 0.199749 ± 3e‑6
STRIDE symmetric 0.2630 – 0.3967 0.263044 ± 6e‑6

STRIDE with the conjugation and norm only (Re(wv) kept) still spans 0.2008 – 0.2982. Stock examples: STRIDE a10_ideal and solovev_ideal unchanged, DIIID_resistive 0.7798 → 0.7795. RDCON solovev_resistive unchanged, DIIID_resistive 0.7644 → 0.7653, and its Galerkin Δ′ output is byte-identical.

Reviewer page (figures): https://claude.ai/artifact/2SA9fP18tmWWQ4WgqwvmoB

…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.
@jhalpern30 jhalpern30 changed the title DCON: put the vacuum matrix and norm in the plasma's Fourier frame DCON/RDCON/STRIDE: put the vacuum matrix and norm in the plasma's Fourier frame Oct 5, 2026
@jhalpern30
jhalpern30 requested a review from logan-nc October 5, 2026 20:05
@jhalpern30

Copy link
Copy Markdown
Collaborator Author

@logan-nc the Fortran equivalent of the Julia fix - probably easiest to review them together

@matt-pharr

Copy link
Copy Markdown
Collaborator

@jhalpern30 FYI any delta W numbers you are getting from RDCON are actually just an old fork of regular DCON, there is no path in RDCON fortran from the galerkin solution to delta W. The delta W it gives comes from the regular forward integration solve happening in RDCON.

@jhalpern30
jhalpern30 marked this pull request as ready for review October 6, 2026 12:19
@jhalpern30

Copy link
Copy Markdown
Collaborator Author

@matt-pharr Yeah agreed, it was more so I wanted the free.f files between the branches to all reflect the fix, even if the paths are actually stale

@logan-nc

logan-nc commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

@claude review this pr

@logan-nc logan-nc self-assigned this Oct 9, 2026
@claude

claude Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Claude finished @logan-nc's task in 24s —— View job


Review of PR #295

The fix looks correct to me, and it matches the described mechanism: VACUUM runs on a θ-reversed grid, so wv comes back in the conjugate Fourier frame of wp, and the fix conjugates it. The jmat(ipert-jpert) norm change is also consistent with amat putting that coefficient on CONJG(ξ_i)*ξ_j. I only read the diff and surrounding code. I did not build or run anything, so the numbers in the PR description are unverified by me.

Checked

  • dcon/free.f: the wv=CONJG(wv) in free_run comes after the wv_farwall_flag / temp assignment, so it applies to both wall and no-wall paths. It also covers the free_wvmats psiedge scan. Both norm sites are updated.
  • rdcon/free.f: the free_run conjugation and the norm are updated.
  • stride/free.f: complex_flag=.TRUE. is needed. With .FALSE. only Re(wv) is kept, so conjugating would do nothing. The conjugation comes before the singfac scaling, and singfac is real, so the order is fine. The later wvt and zheev uses of wv now see the complex matrix, which is the intended change.
  • Every modified line is under the 72-column limit. They are short comments and wv=CONJG(wv) lines, and I confirmed this by reading the diff, not by running awk.

Minor points (non-blocking)

  1. rdcon/free.f:673 has complex_flag=.FALSE. in free_get_wvac, so wvac=CONJG(wvac) at line ~697 conjugates a purely real matrix and has no effect. Your note that the Galerkin Δ′ output is byte-identical fits this. It is harmless, and you said it is there to keep the files in sync. A one-line comment saying the call is a no-op until complex_flag is .TRUE. would help the next reader. Alternatively, set .TRUE. for consistency with the other two codes, but that would change Galerkin results and needs its own validation. Matt's comment suggests this path is stale anyway.
  2. The three copies of the comment vacuum used the reversed theta grid: conjugate to dcon frame. are good. The free_wvmats (dcon/free.f ~832) and free_get_wvac sites have none. Adding it there would make the reason easier to find.
  3. There is no regression test. A θ-origin-roll check on an up-down symmetric case, where δW should be invariant, would catch this class of bug if it comes back. The PR evidence is good, but it lives only in the PR text. A docs/examples or CI check would be a possible follow-up, not a blocker for this PR.

Verdict: LGTM. This is a small, well-targeted fix with strong evidence in the PR description. Logan has already approved it.

@logan-nc logan-nc added the bug label Oct 9, 2026
@logan-nc

logan-nc commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

@jhalpern30 let me know if you want to address claude points 1 and 2 before merging. You can ignore 3.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants