Skip to content

InnerLayer.GGJ - REFACTOR! - Solve the :ray BVP with the Galerkin assembly and use one parity convention - #536

Merged
logan-nc merged 13 commits into
developfrom
refactor/ggj-ray-galerkin-bvp
Oct 22, 2026
Merged

logan-nc merged 13 commits into
developfrom
refactor/ggj-ray-galerkin-bvp

Conversation

@matt-pharr

@matt-pharr matt-pharr commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #507 (merged).

Release note

  • Audience: users
  • Numerical impact: :ray inner-layer Δ moves at ≲1e-6 relative (Hermite-cubic BVP in place of spectral collocation): Δ₊ by 9e-7 at q = 4, Q = 500i, and the matched DIII-D ‖Δ‖ by 2e-9, with bpen within 3e-8. With real_profiles=true, bpen now comes from the rotated solve as well. ggj_reference now runs at a physical τ_A, τ_R (Q ≈ 0.22i), so its history restarts; the GGJ harness quantities are renamed delta_interchange_* / delta_tearing_* (same values as the old delta_odd_* / delta_even_*, whose names were the wrong way round). (harness @ 128540b)
  • Migration:
    • solve_ray: the p, refine_tol, max_rounds, max_cells and boundary keywords are gone; the mesh is set by cfl, min_cells and nq. RaySolveResult now carries the Galerkin workspace ws and the Hermite breaks.
    • Removed: pick_xmax (use pick_smax), solve_inner_converged, cheblobatto, parity_rows, and the two-argument solve_inner_profile(params, γ).
    • No longer exported: build_asymptotics, evaluate_asymptotics, InnerAsymptoticsCache, physical_ua_dua, RaySolveResult, q4_surface_benchmark and glasser_wang_2020_eq55; use InnerLayer.GGJ.<name>.
    • Matching with the default GGJModel() no longer writes the SingularSurfaces/Match/Inner/* profiles; pass GGJModel(; real_profiles=true) or use :galerkin to get them.
    • Every positional parity pair is now ordered (Δ₊, Δ₋) = (interchange, tearing). Δ, deltar and Delta_r already were; RaySolveResult.Δraw and the Ψ/Ξ profile columns flip.

The :ray inner-layer backend now solves its boundary-value problem with the same Hermite-cubic Galerkin assembly as :galerkin, keeping the far-field march that makes it accurate at large |Q|; the collocation solver is gone. Matching takes the penetrated field from the rotated solve, so each surface is solved once, and every GGJ backend follows one documented parity convention.

Regression report

regress --db <fresh> --cases ggj_reference,ggj_ray_q500i,gal_resistive_diiid,gal_resistive_diiid_galinner,gal_resistive_pe --refs a5097803e,128540bc5
(run from this branch; a5097803e = develop; both refs: julia 1.12.6, arm64-apple-darwin24.0.0, manifest 0b0a1dc1 pinned, 6 threads)

Regression Report: ggj_reference
=================================================================
Ref 1: a5097803e  @ a5097803e (2026-10-21)
Ref 2: 128540bc5  @ 128540bc5 (2026-10-21)
-----------------------------------------------------------------
Quantity            a5097803e      128540bc5      Diff     Status
-----------------------------------------------------------------
Re(Δ₊ interchange)  1.225728e+03   1.225728e+03   0.0e+00  OK
Im(Δ₊ interchange)  2.064823e+04   2.064823e+04   0.0e+00  OK
Re(Δ₋ tearing)      -5.345663e+00  -5.345663e+00  0.0e+00  OK
Im(Δ₋ tearing)      2.592091e+01   2.592091e+01   0.0e+00  OK
Runtime (s)         5.7s           5.9s                    --
=================================================================
Summary: 4 unchanged


================================================================
Case: ggj_ray_q500i — GGJ inner-layer :ray backend, q=4 surface, Q = 500i (beyond :galerkin)
================================================================

Regression Report: ggj_ray_q500i
===============================================================
Ref 1: a5097803e  @ a5097803e (2026-10-21)
Ref 2: 128540bc5  @ 128540bc5 (2026-10-21)
---------------------------------------------------------------
Quantity            a5097803e     128540bc5     Diff     Status
---------------------------------------------------------------
Re(Δ₊ interchange)  2.472008e+00  2.472006e+00  2.3e-06  OK
Im(Δ₊ interchange)  1.335402e+01  1.335402e+01  1.5e-06  OK
Re(Δ₋ tearing)      1.374966e-01  1.374966e-01  6.7e-15  OK
Im(Δ₋ tearing)      7.427542e-01  7.427542e-01  6.9e-13  OK
Runtime (s)         14.3s         14.7s                  --
===============================================================
Summary: 4 unchanged


================================================================
Case: gal_resistive_diiid — DIII-D-like, n=1, RDCON outer-region Galerkin Δ′ with rpec coil columns (delta_coil)
================================================================

Regression Report: gal_resistive_diiid
=====================================================================================
Ref 1: a5097803e  @ a5097803e (2026-10-21)
Ref 2: 128540bc5  @ 128540bc5 (2026-10-21)
-------------------------------------------------------------------------------------
Quantity                 a5097803e     128540bc5     Diff               Status
-------------------------------------------------------------------------------------
gal # singular surfaces  4             4             0.0e+00            OK
gal singular q values    [4 elem]      [4 elem]      0.0e+00            OK
gal PEST3 Δ diagonal     [4 elem]      [4 elem]      0.0e+00            OK
||gal Δ′ matrix||        1.243810e+04  1.243810e+04  0.0e+00            OK
||gal Δ_coil block||     2.437604e+02  2.437604e+02  0.0e+00            OK
gal D_I per surface      [4 elem]      [4 elem]      0.0e+00            OK
gal α per surface        [4 elem]      [4 elem]      0.0e+00            OK
||gal inner-layer Δ||    1.474576e+06  1.474576e+06  2.534e-03 (0.00%)  ** CHANGED **
||gal match cout||       3.062025e-02  3.062025e-02  8.1e-11            OK
||gal match bpen||       3.778818e+00  3.778818e+00  2.9e-08            OK
gal match residual       2.335239e-16  1.757385e-16  5.8e-17            OK
Runtime (s)              141.6s        131.4s                           --
=====================================================================================
Summary: 1 changed, 10 unchanged


================================================================
Case: gal_resistive_diiid_galinner — DIII-D-like, n=1, Galerkin Δ′ matched with the Galerkin inner-layer backend
================================================================

Regression Report: gal_resistive_diiid_galinner
====================================================================
Ref 1: a5097803e  @ a5097803e (2026-10-21)
Ref 2: 128540bc5  @ 128540bc5 (2026-10-21)
--------------------------------------------------------------------
Quantity                 a5097803e     128540bc5     Diff     Status
--------------------------------------------------------------------
gal # singular surfaces  4             4             0.0e+00  OK
gal singular q values    [4 elem]      [4 elem]      0.0e+00  OK
gal PEST3 Δ diagonal     [4 elem]      [4 elem]      0.0e+00  OK
||gal Δ′ matrix||        1.243810e+04  1.243810e+04  0.0e+00  OK
||gal Δ_coil block||     2.437604e+02  2.437604e+02  0.0e+00  OK
gal D_I per surface      [4 elem]      [4 elem]      0.0e+00  OK
gal α per surface        [4 elem]      [4 elem]      0.0e+00  OK
||gal inner-layer Δ||    1.474564e+06  1.474564e+06  0.0e+00  OK
||gal match cout||       3.062025e-02  3.062025e-02  0.0e+00  OK
||gal match bpen||       3.778818e+00  3.778818e+00  0.0e+00  OK
gal match residual       1.958446e-16  1.958446e-16  0.0e+00  OK
Runtime (s)              132.8s        139.4s                 --
====================================================================
Summary: 11 unchanged


================================================================
Case: gal_resistive_pe — DIII-D-like, n=1, DRIVEN/RPEC: gal-matched resistive ξ → PerturbedEquilibrium (coil-driven singular coupling)
================================================================

Regression Report: gal_resistive_pe
==========================================================
Ref 1: a5097803e  @ a5097803e (2026-10-21)
Ref 2: 128540bc5  @ 128540bc5 (2026-10-21)
----------------------------------------------------------
Quantity                a5097803e  128540bc5  Diff  Status
----------------------------------------------------------
rational q values       N/A        N/A        N/A   N/A
island half-widths      N/A        N/A        N/A   N/A
Chirikov parameter      N/A        N/A        N/A   N/A
||resonant b field||    N/A        N/A        N/A   N/A
resonant b field (all)  N/A        N/A        N/A   N/A
penetrated b field      N/A        N/A        N/A   N/A
PE Δ' per surface       N/A        N/A        N/A   N/A
||C resonant b field||  N/A        N/A        N/A   N/A
Runtime (s)             146.6s     145.4s           --
==========================================================
Summary: 8 missing/N/A

Notes for reviewers

Why the collocation solver could go. An ablation (5400 solves: 3 surfaces × 100 Q values × 18 variants) found that :ray's accuracy comes from its far-field treatment: series tolerance at the matching point, the inward Radau march with purges against a carried decaying frame, and Double64 at |Q| ≳ 100. Spectral order, residual-driven refinement, and collocation vs. Hermite Galerkin did not move Δ where the boundary data were good. The stock :galerkin bias at |Q| > 1 comes from matching where the series is only good to 1e-7, not from its interior.

Commits

  1. InnerLayer.GGJ - REFACTOR! Solve the :ray BVP with Galerkin.jl's building blocks and a point far-field condition; merge the duplicated real/complex asymptotic evaluators. Stock :galerkin and the evaluators are bitwise unchanged (checked). New :ray vs. old collocation: ≤3.5e-7; vs. a strict reference: worst 1e-6.
  2. InnerLayer.GGJ - API! bpen comes from the rotated solve (x = 0 lies on every contour), so MatchProblem runs one inner solve per surface; the θ = 0 real-ψ profiles become opt-in (real_profiles=true) with a θ = 0 vs θ = arg(Q)/4 certificate. Corrects the march comments (Radau IIA stability function, turning point).
  3. InnerLayer - REFACTOR! Removes dead code and duplicated solve paths in Galerkin.jl, Ray.jl and InnerAsymptotics.jl. Bitwise identical on 65 checked outputs.
  4. InnerLayer - API! One parity convention, from Pletzer, Bondeson & Dewar, J. Comput. Phys. 115, 530 (1994) and GWP2016 Eq. 34: parity is that of the displacement Ξ, Δ₊ even = interchange, Δ₋ odd = tearing, positional pairs ordered (Δ₊, Δ₋). Fixes reversed ± or odd/even labels in the InnerLayerResponse docstring, docs tables, harness names, the convergence figure, and comments in CoupledFullMatch, run_slayer.jl and SLAYER. Bitwise identical with parity columns mapped, including the full match path on the Solovev fixture. Follow-up for SLAYER and the InnerLayerResponse field order: InnerLayer - Order InnerLayerResponse and SLAYER as (Δ₊, Δ₋) = (interchange, tearing) #538.
  5. InnerLayer - DOCS Trims the GGJ comments and inner_layer.md to what the code does, with paper math replaced by equation pointers. Code unchanged (syntax trees compared).
  6. InnerLayer - DOCS Corrects paper citations after an audit against the PDFs: GWP2016 Appendix numbering (Q₀ A14, X₀ A15, S and X₀ = S^{−1/3} A16, Q = s/Q₀ A11), Wang et al. 2020 is Phys. Plasmas 27, 122503, the shooting method is Glasser, Jardin & Tesauro, Phys. Fluids 27, 1225 (1984) Sec. V rather than the DCON paper, the rescale exponent sign, the C_R sign in CoupledFullMatch, and GGJ 1975 Eqs. 16, 24 for D_I. Documents how :ray differs from :galerkin (where and how the far-field condition is imposed, both radii computed per solve) and the measured small-|Q| behavior of :galerkin. Only code change: kl = mpert*(np+1) + 1 - 1 → mpert*(np+1).
  7. InnerLayer - MINOR JuliaFormatter over the files this PR changes; code unchanged (syntax trees compared).
  8. InnerLayer - API! Drops nine InnerAsymptoticsCache fields nothing reads (λ, Tinv, B, K2, Cmat, Dmat, Y0, Y0inv, Z) and an unused K6 array, and un-exports the GGJ internals and benchmark fixtures listed under Migration. delta_convergence stays exported as the documented error-bar tool. No numbers move.
  9. InnerLayer.GGJ - BUGFIX! (review) With real_profiles=true the x = 0 row of Ψ, Ξ comes from the rotated solve, so bpen and deltar no longer depend on the option; certΔ covers both Δ₊ and Δ₋. New MatchProblem-level tests: bpen/deltar identical with profiles on and off, inner_* empty by default, and :galerkin vs :ray (real profiles) agreeing on the composite ξ and b at ψ_s, which catches a parity swap. The solution-profile figure plots the tearing solution again.
  10. Repo - BUGFIX The figure tooling crashed on a clean tree (a SubString commit hash) and counted its own outputs as uncommitted changes, so figures could only be recorded as -dirty. Fixed; both inner-layer figures are regenerated from a clean commit (5d11aad3e).

:galerkin at small |Q|. Against a tightened :ray reference, :galerkin is not uniformly safe at small |Q|: on the GW2020 Eq. 55 surface it errs by 7e-5 at Q = 0.1 and 3e-3 at Q = 0.3, and near arg Q = 157.5° by order unity at |Q| = 0.1 (0.4 on the q = 4 surface at |Q| = 0.3). :ray held 2e-6 for |Q| from 0.01 to 1 on all three surfaces tested, at 3–60× the cost (12 ms to 0.24 s per solve). The docs now say so.

Harness notes. gal_resistive_pe is N/A on both refs: its PE stage skips by design until the Galerkin free-boundary δW lands. The single CHANGED flag is ‖inner-layer Δ‖ in gal_resistive_diiid at 2e-9 relative.

Known limits, not addressed here

  • At small |Q| the cubic BVP is accurate to 1e-8–2e-7, against 1e-9–1e-11 for the old spectral solver; conditioning of the inner LU is a separate investigation.
  • The opt-in θ = 0 profile solve can still fail at large |Q| near the imaginary axis; certΔ warns when it does.
  • real_profiles is a diagnostic and deliberately not a deck key, so deck runs with gal_match_flag omit Inner/*.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K8p6NQZooWYJLqjayYjwcJ

@matt-pharr
matt-pharr changed the base branch from develop to refactor/post-solve-matching October 19, 2026 07:39
@matt-pharr matt-pharr added this to the GPEC v2.0.0 milestone Oct 19, 2026
@matt-pharr
matt-pharr added this pull request to stack #539 October 19, 2026 10:52
@matt-pharr
matt-pharr requested review from d-burg and logan-nc October 19, 2026 10:58
@matt-pharr matt-pharr self-assigned this Oct 19, 2026
@matt-pharr matt-pharr changed the title Refactor/ggj ray galerkin bvp InnerLayer.GGJ - REFACTOR! - Solve the :ray BVP with the Galerkin assembly and use one parity convention Oct 19, 2026
@github-actions github-actions Bot added changed-results Results move or an interface breaks - read before upgrading refactor Behavior-preserving restructuring labels Oct 19, 2026
@matt-pharr
matt-pharr marked this pull request as ready for review October 19, 2026 10:59
Base automatically changed from refactor/post-solve-matching to develop October 19, 2026 13:57
@matt-pharr
matt-pharr force-pushed the refactor/ggj-ray-galerkin-bvp branch from 18c260b to ff6feea Compare October 19, 2026 13:57
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

@logan-nc this is a cleanup of the GGJ side of InnerLayer. We revert back to using galerkin for the BVP part of the ray solver, and delete all the unused code. We also clean up exports. High line count but should be an easy review for that reason.

@logan-nc

Copy link
Copy Markdown
Collaborator

@claude review this

@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

⚠️ HUMAN REVIEW IS REQUIRED BEFORE MERGE. THIS PR MUST NOT BE MERGED INTO develop WITHOUT A THIRD-PARTY HUMAN REVIEWER'S APPROVAL. ⚠️

An assignee and two requested reviewers are set. Approval from one of those reviewers is still non-negotiable, because this PR flips the parity convention across the public API and the matching code.

Summary

The PR moves :ray onto the Hermite-Galerkin assembly, deletes the collocation code (about -1000 lines in Ray.jl/RayAsymptotics.jl), and standardizes every parity pair as (Δ₊, Δ₋) = (interchange, tearing). It is a net reduction in machinery. The regression report is attached, and the shifts it shows are small (≲1e-6 relative, bpen within 3e-8).

Issues

  1. Stale parity label after the flip. docs/src/figures/inner_layer/make_solution_profiles.jl still has isol = 1 # "odd" parity solution (Ψ'(0)=Ξ(0)=Υ(0)=0). Column 1 is now the even/interchange solution (Ψ(0)=Ξ'(0)=Υ'(0)=0). The comment is wrong, and the regenerated solution_profiles.png may now show a different channel than before. Fix the comment, and decide whether the figure should show interchange or tearing.
  2. Figure provenance. manifest.toml records solution_profiles as commit 20887ed8c-dirty (2026-10-16) and convergence_Sinvariance as 5b51d871f-dirty. Neither is a clean commit on this branch, so the figures cannot be tied to the final source. Regenerate them from a clean commit once the code is final.
  3. Unrelated formatter churn in MatchProblem.jl. The docstring gains blank lines and re-indented bullets, and empty!(inner_psi); empty!(inner_xi); empty!(inner_b) is split across three lines with trailing semicolons, in two places. Please revert these so the diff only shows the parity/profile logic.
  4. The profiles flag in _compute_match is set per surface but used globally. One surface returning a length-1 profile flips it for all surfaces, and the continue leaves inner_psi[i] and the other per-surface profile entries undefined for that i. This is harmless today, since every surface uses the same model. Still, derive the flag once from the model (real_profiles/solver) before the loop rather than from length(xg) == 1 inside it.
  5. The riskiest edit has thin test coverage. The swaps in _compute_match (bpen index order, inner_odd/inner_even, inner_bodd/inner_beven) are only checked through bpen in the regression cases. I found no assertion on the composite inner_xi and inner_b profiles, which also flip, and test/runtests_matching_models.jl has a one-line change. Please add:
    • a test that bpen and deltar are identical for GGJModel() and GGJModel(; real_profiles=true);
    • a test that the inner_* fields are empty by default and populated with real_profiles=true or :galerkin;
    • a check that :galerkin and :ray with real_profiles=true give the same inner_xi and inner_b at a small real Q, which would catch a wrong parity swap.
  6. The docs state accuracy and speed numbers with no reproducer in the repo. Examples are "3-60 times less than :ray", 7e-5 at Q=0.1, and 3e-3 at Q=0.3. Cite the script or harness case that produces them, or soften the wording. As written they will go stale.
  7. The tests mostly pin the implementation. The new internal test only checks that refining the grid does not move Δ at Q=0.01, and the test comment says the method is validated "outside the test suite". A small fixed-value pin against a known reference (e.g. the GW2020 Eq. 55 values in the docs table) would guard against regressions without that external reference.
  8. The regression harness was run on one machine only (arm64 macOS, 6 threads). ggj_ray_q500i documents platform-dependent roundoff at about 1e-5 relative, so a CI-platform confirmation of the new :ray numbers would be useful. ggj_reference changes its inputs and restarts its history; the release note says so, which is good.

Minor

  • The docs/src/inner_layer.md rewrite drops the governing-equation block and the Mercier/scale derivations in favor of equation-number citations. This is consistent with CLAUDE.md's citation guidance, but readers now need both papers open to follow the page.
  • Several names are no longer exported from InnerLayer. Check that the @ref links (e.g. InnerLayer.GGJ.q4_surface_benchmark, pick_smax) resolve and that no docstring lost its @autodocs coverage, since Documenter's missing_docs check will fail CI otherwise. I did not build the docs.

Positive

Removing the dual solver paths and using one documented parity convention will be useful for future inner-layer models.

I reviewed the diff only; I did not run the tests or the regression harness.

🤖 Generated with Claude Code

@matt-pharr
matt-pharr force-pushed the refactor/ggj-ray-galerkin-bvp branch from b877fae to c8b68e4 Compare October 20, 2026 12:22
… in the match profiles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF

@logan-nc logan-nc 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.

Nice cleanup. The single parity convention reads much better, and I checked that it holds everywhere I followed it: pen/bpen, the inner ξ and b profiles, the cin rows in MatchProblem, and all three solvers (interchange = Δ₊ = isol 1). The rotated weak form, the far-end IBP term and the DOF conversion agree with each other. Nothing still references the removed or un-exported names. I pushed one cosmetic commit (cf33eb8) that drops stray semicolons on the empty!(…) lines in MatchProblem.jl.

Four points for you to take or leave:

1. real_profiles=true takes bpen from the θ=0 solve, not the rotated one. solve_inner_profile(:ray) returns Ψ/Ξ from the θ=0 solve r0 (src/InnerLayer/GGJ/Ray.jl:449). MatchProblem reads pen from profΨ[1, :] (src/ForceFreeStates/Matching/MatchProblem.jl:150-151), so with profiles on, bpen comes from the very solve the PR says can fail at large |Q|. That contradicts the description's claim that bpen comes from the rotated solve. Suggested fix: overwrite row 1 (s = 0) of Ψ and Ξ with the rotated solve's values, which is about three lines, plus a test asserting that bpen is the same with real_profiles on and off.

2. certΔ checks only Δ₊ (Ray.jl:446). Δ₋ is the tearing channel bpen depends on, so it should be covered as well, e.g. certΔ = max(relerr(Δ[1]), relerr(Δ[2])).

3. The harness stamp b877faece no longer exists after the rebase, so the release-note check fails. I ran the harness at cf33eb8 against develop 84b7e48 and you're welcome to paste these results:

Case Result
ggj_reference 4 unchanged
ggj_ray_q500i 4 OK: Δ₊ diffs 5.1e-7 / 3.2e-7, Δ₋ ~1e-13
gal_resistive_diiid 1 CHANGED: ‖inner-layer Δ‖ 1.474579e6, diff 4.7e-3 (0.00%); bpen diff 2.8e-8
gal_resistive_diiid_galinner 11 unchanged
gal_resistive_pe N/A on both refs

Two caveats for whoever re-runs it:

  • The harness cache is keyed only on (commit, case). This PR changes what ggj_reference computes, so develop's cached rows are from the old problem. I used a fresh --db.
  • Run the harness from this branch's checkout. Develop's runner calls glasser_wang_2020_eq55 and q4_surface_benchmark unqualified, and this PR un-exports them.

4. A design question. With the default model, deck runs that use gal_match_flag no longer write SingularSurfaces/Match/Inner/*, and no deck key turns them back on. You could add a ForceFreeStates control key (something like gal_inner_real_profiles), or keep it as a documented limitation. Either is fine with me, but it should be one or the other on purpose.

Minor things I'm not asking you to change:

  • Passing real_profiles to :galerkin/:shooting throws an unsupported-kwarg error. That's fine since it's documented as ray-only.
  • The x=0 IBP term in _parity_bcs_and_solve! is dead code, but it predates this PR.
  • The positional order of InnerLayerResponse is already tracked in #538.

I did not review the InnerAsymptotics.jl deletions or the internals of _assemble_and_solve! line by line. For those I'm relying on your bitwise checks and the tests.

@logan-nc logan-nc 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.

See comments posted

@matt-pharr
matt-pharr requested a review from logan-nc October 21, 2026 08:27
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

@logan-nc addressed your comments:

  • with real_profiles=true, the x = 0 row of Ψ and Ξ now comes from the rotated solve, so bpen and deltar are identical with or without profiles.
  • certΔ is the maximum relative difference over both Delta + and Delta- now
  • Addressed the claude items on tests & plotting
  • I did not add something to the deck to compute the inner region profiles as this is purely diagnostic and will not be used in 99.9% of resistive cases.

Regression harness is running, but the changes are final and ready for your approval.

@logan-nc logan-nc 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.

Thanks for the quick response!

@logan-nc
logan-nc merged commit 9365f0d into develop Oct 22, 2026
11 of 13 checks passed
@logan-nc
logan-nc deleted the refactor/ggj-ray-galerkin-bvp branch October 22, 2026 07:19
@d-burg

d-burg commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Thanks @matt-pharr . One of my claudes had a few thoughts: "A post-merge read turned up a few things, none of which affect the parity convention: Δ± are paired consistently in every consumer, and gal_resistive_diiid_galinner is bitwise unchanged.

  • Run-time accuracy check. solve_ray used to refine until the residual met refine_tol = 1e-8 (up to 8 rounds, warning at 6000 cells); it now uses a fixed mesh (cfl = 0.2, min_cells = 1024) and no longer reports a residual. The body's "Known limits" notes the small-|Q| accuracy drop, from 1e-9–1e-11 to 1e-8–2e-7, but the code and docstrings don't. Could the docstring say so, and would a cheap residual estimate in RaySolveResult be worth keeping, so a user can see when a point is off?
  • Model options. solve_inner(::GGJModel{:ray}, ...) drops real_profiles but forwards npc and certify_rtol to solve_ray, which doesn't accept them, so a model built with either throws a MethodError when solved (Ray.jl:420). Dropping them in the signature, as is done for real_profiles, fixes it.
  • Minor: nothing bounds the cell count any more (the old code warned at 6000), and ggj_reference's 1e-10 absolute floor is ~5e-15 relative at the new Q ≈ 0.22i, which may flag noise across machines."

I'm happy to add the small fixes unless you'd rather, or neither if these are just superfluous?

@matt-pharr

Copy link
Copy Markdown
Collaborator Author

Thanks @d-burg, good catches. Yes please, go ahead with the fixes: the npc/certify_rtol forwarding is a real bug, so please drop them from solve_inner(::GGJModel{:ray})'s signature. Please also note the fixed-mesh accuracy in the solve_ray docstring, warn on a large cell count, and make ggj_reference's floor relative. A cheap residual estimate in RaySolveResult sounds useful if it's cheap; your call. Tag me for review.

matt-pharr added a commit that referenced this pull request Oct 23, 2026
The :ray model returns no inner profiles by default since #536, so the
cut-solution drift only shows with real-ψ profiles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
matt-pharr added a commit that referenced this pull request Oct 23, 2026
Brings in develop (#536, #547). run_slayer_stage keeps #547's unguarded body
without the stage timer, which main now owns, and run_kinetic_forces keeps the
new axis_psi_c boundary without its own banner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
matt-pharr added a commit that referenced this pull request Oct 23, 2026
…-in-main

Brings in develop (#536, #547) through the stack. The deck/API match parity
test compares on the galerkin inner model, the only one that still returns
inner profiles by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
matt-pharr added a commit that referenced this pull request Oct 26, 2026
The :ray model returns no inner profiles by default since #536, so the
cut-solution drift only shows with real-ψ profiles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changed-results Results move or an interface breaks - read before upgrading refactor Behavior-preserving restructuring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants