Skip to content

Commit bb59565

Browse files
authored
FFS - REFACTOR - Publish solves as ForceFreeStatesResult; staged main; solve API (refactor plan interface PR) (#393)
2 parents 9491f89 + 3ed2365 commit bb59565

76 files changed

Lines changed: 3183 additions & 1068 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,5 @@ repos:
6969
- id: toml-no-deprecated-keys
7070
name: 'TOML conventions: no deprecated config keys'
7171
language: pygrep
72-
entry: '^(mer_flag|force_wv_symmetry|ode_flag|cyl_flag|mat_flag|reform_eq_with_psilim|use_riccati|use_parallel|parallel_threads|populate_dense_xi|power_bp|power_b|power_r|power_rc)\s*='
72+
entry: '^(mer_flag|force_wv_symmetry|ode_flag|cyl_flag|mat_flag|reform_eq_with_psilim|use_riccati|use_parallel|parallel_threads|populate_dense_xi|gal_flag|power_bp|power_b|power_r|power_rc)\s*='
7373
files: ^(examples/.*\.toml|test/test_data/.*\.toml)$

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ version = "0.1.0"
66

77
[deps]
88
AdaptiveArrayPools = "4f381ef7-9af0-4cbe-99d4-cf36d7b0f233"
9+
CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2"
910
Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
1011
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1112
DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df"
@@ -38,6 +39,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3839

3940
[compat]
4041
AdaptiveArrayPools = "0.3.5"
42+
CommonSolve = "0.2"
4143
Contour = "0.6.3"
4244
DelaunayTriangulation = "1.6.6"
4345
DelimitedFiles = "1.9.1"

REFACTOR_PLAN.md

Lines changed: 537 additions & 103 deletions
Large diffs are not rendered by default.

benchmarks/benchmark_diiid_ideal_ntv_torque.jl

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ using Plots
3131
# Load GPEC
3232
using GeneralizedPerturbedEquilibrium
3333
const GPE = GeneralizedPerturbedEquilibrium
34-
const FFS = GPE.ForceFreeStates
3534
const KF = GPE.KineticForces
3635
const Eq = GPE.Equilibrium
3736
const PE = GPE.PerturbedEquilibrium
@@ -237,15 +236,12 @@ function run_benchmark(fortran_dir::String=default_fortran_dir())
237236
_p("\n--- Equilibrium + ForceFreeStates (via main()) ---")
238237
t0 = time()
239238
result = GPE.main([tomldir])
240-
equil = result.equil
241-
intr = result.intr
242-
ctrl = result.ctrl
239+
ffs = result.ffs
240+
equil = ffs.equil
241+
metric = ffs.metric
243242

244243
_pf(" FFS completed in %.1f s\n", time() - t0)
245-
_pf(" mpert=%d, mlow=%d, mhigh=%d\n", intr.mpert, intr.mlow, intr.mhigh)
246-
247-
# Build metric (needed for JBB deweighting)
248-
metric = FFS.make_metric(equil, intr.mpert)
244+
_pf(" mpert=%d, mlow=%d, mhigh=%d\n", ffs.mpert, ffs.mlow, ffs.mhigh)
249245

250246
# Load Fortran xclebsch data
251247
_p("\n--- Load Fortran xclebsch ---")
@@ -257,8 +253,8 @@ function run_benchmark(fortran_dir::String=default_fortran_dir())
257253
npsi_f, mpert_f, mlow_f)
258254
_pf(" ψ range: [%.6f, %.6f]\n", psi_grid_f[1], psi_grid_f[end])
259255

260-
if mpert_f != intr.mpert || mlow_f != intr.mlow
261-
@warn "Mode ranges differ: Fortran mpert=$mpert_f,mlow=$mlow_f vs Julia mpert=$(intr.mpert),mlow=$(intr.mlow)"
256+
if mpert_f != ffs.mpert || mlow_f != ffs.mlow
257+
@warn "Mode ranges differ: Fortran mpert=$mpert_f,mlow=$mlow_f vs Julia mpert=$(ffs.mpert),mlow=$(ffs.mlow)"
262258
end
263259

264260
# Build PE state from Fortran data and run JBB deweighting
@@ -291,7 +287,7 @@ function run_benchmark(fortran_dir::String=default_fortran_dir())
291287
kf_intr = KF.KineticForcesInternal(equil; verbose=false)
292288

293289
# Run set_perturbation_data! — builds dbob_m, divx_m, xs_m via JBB deweighting
294-
KF.set_perturbation_data!(kf_intr, pe_state, intr, equil, metric)
290+
KF.set_perturbation_data!(kf_intr, pe_state, ffs, equil, metric)
295291

296292
_pf(" JBB deweighting completed in %.1f s\n", time() - t1)
297293

benchmarks/compare_gal_vs_el.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ ksel = length(ARGS) >= 3 ? ARGS[3] : "highest"
1818

1919
to_c(a) = eltype(a) <: Complex ? ComplexF64.(a) : map(x -> ComplexF64(x.re, x.im), a)
2020

21-
et, wt, u1, psiE, gxi, psiG, issing, mlow, sing_psi = h5open(h5path) do f
21+
et, wt, u1, psiE, gxi, psiG, mlow, sing_psi = h5open(h5path) do f
2222
(to_c(read(f["ForceFreeStates/FreeBoundaryStability/eigenmode_energies"])),
2323
to_c(read(f["ForceFreeStates/FreeBoundaryStability/W_freeboundary_eigenmodes"])),
2424
to_c(read(f["ForceFreeStates/Solutions/ForwardIntegration/xi_psi"])), read(f["ForceFreeStates/Solutions/ForwardIntegration/psi"]),
25-
to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Match/xi"])), read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/psi"]),
26-
Bool.(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/is_rational"])), read(f["Info/mlow"]),
27-
read(f["SingularSurfaces/GalerkinDeltaPrime/rational_psi"]))
25+
to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/xi_psi"])), read(f["ForceFreeStates/Solutions/GalerkinIntegration/psi"]),
26+
read(f["Info/mlow"]),
27+
read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]))
2828
end
2929

3030
mpert = size(u1, 1)
@@ -38,10 +38,10 @@ w = wt[:, k]
3838
cEL = u1[:, :, end] \ w
3939
xiE = reduce(hcat, (u1[:, :, ip] * cEL for ip in 1:size(u1, 3))) # (mpert, nE)
4040

41-
# gal ideal profile (identity-at-edge ⇒ coefficient = w); drop on-surface points
42-
keep = .!issing
43-
psiGk = psiG[keep]
44-
xiG = reduce(hcat, (gxi[:, ip, :] * w for ip in findall(keep))) # (mpert, nGk)
41+
# gal ideal profile (identity-at-edge ⇒ coefficient = w); the closed-profile grid
42+
# already excludes on-surface points
43+
psiGk = psiG
44+
xiG = reduce(hcat, (gxi[:, :, ip] * w for ip in eachindex(psiG))) # (mpert, nGk)
4545

4646
ms = mlow .+ (0:mpert-1)
4747
peak = [maximum(abs, @view xiE[i, :]) for i in 1:mpert]

benchmarks/compare_jbgradpsi_m2.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# (1) IDEAL galerkin matched ξ (gal_match_flag=true, gal_ideal_flag=true)
44
# (2) FORWARD ξ (gal_match_flag=false)
55
#
6-
# PE writes no ψ grid, so it's reconstructed: gal-ideal → ForceFreeStates/Solutions/GalerkinIntegration/Solution/psi minus issing points;
6+
# PE writes no ψ grid, so it's reconstructed: gal-ideal → ForceFreeStates/Solutions/GalerkinIntegration/psi (already excludes on-surface points);
77
# forward → ForceFreeStates/Solutions/ForwardIntegration/psi.
88
# Usage: julia --project=. benchmarks/compare_jbgradpsi_m2.jl [gal_h5] [shoot_h5] [out.png] [m]
99

@@ -19,9 +19,8 @@ to_c(a) = eltype(a) <: Complex ? ComplexF64.(a) : map(x -> ComplexF64(x.re, x.im
1919
# gal-ideal run: PE grid = gal solution grid with the on-surface (issing) points dropped
2020
pa_g, psi_g, mlow, sing_psi, sing_m = h5open(gal_h5) do f
2121
pa = to_c(read(f["PerturbedEquilibrium/Response/psi_area"])) # [npsi, mpert]
22-
iss = Bool.(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/is_rational"]))
23-
(pa, read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/psi"])[.!iss], read(f["Info/mlow"]),
24-
read(f["SingularSurfaces/GalerkinDeltaPrime/rational_psi"]), read(f["SingularSurfaces/GalerkinDeltaPrime/rational_m"]))
22+
(pa, read(f["ForceFreeStates/Solutions/GalerkinIntegration/psi"]), read(f["Info/mlow"]),
23+
read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]), read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_m"]))
2524
end
2625
# forward run: PE grid = ForceFreeStates/Solutions/ForwardIntegration/psi
2726
pa_s, psi_s = h5open(sh_h5) do f

benchmarks/plot_xi_eigenmode.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ et, wt, u1, psi, mlow, sing_psi = h5open(h5path) do f
2222
to_c(read(f["ForceFreeStates/Solutions/ForwardIntegration/xi_psi"])),
2323
read(f["ForceFreeStates/Solutions/ForwardIntegration/psi"]),
2424
read(f["Info/mlow"]),
25-
haskey(f, "SingularSurfaces/GalerkinDeltaPrime/rational_psi") ? read(f["SingularSurfaces/GalerkinDeltaPrime/rational_psi"]) : Float64[])
25+
haskey(f, "ForceFreeStates/Solutions/GalerkinIntegration/rational_psi") ? read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]) : Float64[])
2626
end
2727

2828
mpert, _, nstep = size(u1)

benchmarks/scan_resistivity_m2.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function read_m2(h5; gal::Bool)
1616
h5open(h5) do f
1717
pa = to_c(read(f["PerturbedEquilibrium/Response/psi_area"])) # [npsi, mpert]
1818
col = mtarget - read(f["Info/mlow"]) + 1
19-
psi = gal ? read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/psi"])[.!Bool.(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/is_rational"]))] :
19+
psi = gal ? read(f["ForceFreeStates/Solutions/GalerkinIntegration/psi"]) :
2020
read(f["ForceFreeStates/Solutions/ForwardIntegration/psi"])
2121
(psi, pa[:, col])
2222
end
@@ -34,7 +34,7 @@ eta_ref = 8e-8
3434

3535
# rational surface for m=target
3636
sing_psi, sing_m = h5open(joinpath(scandirs[1], "gpec.h5")) do f
37-
(read(f["SingularSurfaces/GalerkinDeltaPrime/rational_psi"]), read(f["SingularSurfaces/GalerkinDeltaPrime/rational_m"]))
37+
(read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]), read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_m"]))
3838
end
3939
psi_res = mtarget in sing_m ? sing_psi[findfirst(==(mtarget), sing_m)] : NaN
4040

benchmarks/scan_rotation_m2.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function read_m2(h5; gal::Bool)
1515
h5open(h5) do f
1616
pa = to_c(read(f["PerturbedEquilibrium/Response/psi_area"]))
1717
col = mtarget - read(f["Info/mlow"]) + 1
18-
psi = gal ? read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/psi"])[.!Bool.(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/is_rational"]))] :
18+
psi = gal ? read(f["ForceFreeStates/Solutions/GalerkinIntegration/psi"]) :
1919
read(f["ForceFreeStates/Solutions/ForwardIntegration/psi"])
2020
(psi, pa[:, col])
2121
end
@@ -29,7 +29,7 @@ scandirs, rots = scandirs[ord], rots[ord]
2929
@printf("%d scan runs: rotation f = %s Hz (η fixed = 8e-8)\n", length(rots), join((@sprintf("%g", r) for r in rots), ", "))
3030

3131
sing_psi, sing_m = h5open(joinpath(scandirs[1], "gpec.h5")) do f
32-
(read(f["SingularSurfaces/GalerkinDeltaPrime/rational_psi"]), read(f["SingularSurfaces/GalerkinDeltaPrime/rational_m"]))
32+
(read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]), read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_m"]))
3333
end
3434
psi_res = mtarget in sing_m ? sing_psi[findfirst(==(mtarget), sing_m)] : NaN
3535

benchmarks/verify_gal_ideal.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,30 @@
22
# 1. cout / cin / deltar all zero (no resistive plasma combination, no inner layer)
33
# 2. matched ξ_j == the gal coil column sols(:,:,2·msing+j) (and ξ′ likewise)
44
# Usage: julia --project=. benchmarks/verify_gal_ideal.jl [path/to/gpec.h5]
5+
# Requires [DEBUG] gal_basis_output = true in the deck (compares against the raw-basis dump).
56
using HDF5, Printf, LinearAlgebra
67

78
h5path = length(ARGS) >= 1 ? ARGS[1] : "/tmp/gal_ideal_test/gpec.h5"
89
to_c(a) = eltype(a) <: Complex ? ComplexF64.(a) : map(x -> ComplexF64(x.re, x.im), a)
910

10-
cout, deltar, mxi, mdxi, sols, sols_d, sing_psi = h5open(h5path) do f
11+
cout, deltar, mxi, mdxi, sols, sols_d, iss, sing_psi = h5open(h5path) do f
1112
(to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Match/cout"])), to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Match/Delta_r"])),
12-
to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Match/xi"])), to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Match/dxidpsi"])),
13-
to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/xi_psi"])), to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Solution/dxi_psidpsi"])),
14-
read(f["SingularSurfaces/GalerkinDeltaPrime/rational_psi"]))
13+
to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/xi_psi"])), to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/dxi_psidpsi"])),
14+
to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Basis/xi_psi"])), to_c(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Basis/dxi_psidpsi"])),
15+
Bool.(read(f["ForceFreeStates/Solutions/GalerkinIntegration/Basis/is_rational"])),
16+
read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]))
1517
end
1618
msing = length(sing_psi)
17-
mpert, ngrid, mcoil = size(mxi)
19+
mpert, mcoil, ngrid = size(mxi)
20+
keep = .!iss
1821

1922
@printf("[1] ‖cout‖ = %.2e, ‖deltar‖ = %.2e %s\n", norm(cout), norm(deltar),
2023
(norm(cout) == 0 && norm(deltar) == 0) ? "✓ no resistive combination (ideal)" : "")
2124

2225
# matched ξ_j should equal the coil column sols(:,:,2msing+j)
2326
err = maximum(1:mcoil) do j
2427
csol = 2msing + j
25-
max(norm(mxi[:, :, j] - sols[:, :, csol]), norm(mdxi[:, :, j] - sols_d[:, :, csol]))
28+
max(norm(mxi[:, j, :] - sols[:, csol, keep]), norm(mdxi[:, j, :] - sols_d[:, csol, keep]))
2629
end
2730
@printf("[2] max‖ξ_matched − coil column‖ = %.2e %s\n", err,
2831
err < 1e-12 ? "✓ matched ξ == bare ideal coil column" : "")

0 commit comments

Comments
 (0)