Skip to content

Commit 5bbffb6

Browse files
d-burgclaude
andcommitted
ForceFreeStates - TEST - Pin the DCON axis condition on a flat-core diverted fixture
Adds test/runtests_fixed_axis.jl and a 129x129 synthetic DIII-D-like ramp-up fixture (q0 = 3.42, diverted, no wall, n = 1; analytic profiles, 303 kB) on which the Frobenius free-axis start produces et[1] ~ -1e5 and a negative plasma-matrix eigenvalue while the DCON fixed-axis start gives et[1] = +1.50 and a stiff W_p eigenvalue of +1.609e4, matching Fortran DCON v1.5.5 on the 257x257 parent. Asserts: the default is fixed_axis = true; with it et[1] > 0, W_p has no negative eigenvalue and its stiff eigenvalue is within 2 % of the DCON value; the Delta' diagonal is independent of the axis condition (rtol 1e-3). The defect of the Frobenius start is pinned with @test_broken so a repaired compute_axis_init reports an unexpected pass. Runtime ~2 min (two full free-boundary runs at mpsi 128). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent c4276de commit 5bbffb6

6 files changed

Lines changed: 3875 additions & 0 deletions

File tree

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ else
2929
include("./runtests_coordinate_invariant.jl")
3030
include("./runtests_eulerlagrange.jl")
3131
include("./runtests_riccati.jl")
32+
include("./runtests_fixed_axis.jl")
3233
include("./runtests_parallel_integration.jl")
3334
include("./runtests_result_struct.jl")
3435
include("./runtests_solve_api.jl")

test/runtests_fixed_axis.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Regression test for the axis initial condition of the Euler-Lagrange integration.
2+
#
3+
# Free-boundary energies from the Frobenius free-axis start (`fixed_axis = false`, the
4+
# default before this test existed) were wrong on flat-core diverted equilibria: one
5+
# eigenvalue of the plasma response matrix W_p sat ~10x off its Fortran DCON value and,
6+
# where it crossed zero, `et[1]` reported a spurious -1e2 ... -1e5 "instability". The
7+
# fixture is a 129x129 TokaMaker geqdsk of a synthetic DIII-D-like ramp-up slice
8+
# (q0 = 3.42, q95 = 5.9, diverted, no wall, n = 1) on which Fortran DCON (v1.5.5, same
9+
# mlow/mhigh/psilim/dmlim, mpsi 256) gives a stiff W_p eigenvalue of +1.609e4, no
10+
# negative W_p eigenvalue, and et[1] = +1.42; the old default gave et[1] = -8.4e4.
11+
#
12+
# The `@test_broken` lines pin the DEFECT of the Frobenius start: if `compute_axis_init`
13+
# is ever repaired to deliver its documented psilow^(|m|/2) limit, they report an
14+
# "unexpected pass" and should then be promoted to plain `@test`s.
15+
using LinearAlgebra
16+
using GeneralizedPerturbedEquilibrium.ForceFreeStates: ForceFreeStatesControl
17+
18+
const FIXED_AXIS_FIXTURE = joinpath(@__DIR__, "test_data", "regression_rampup_fixed_axis")
19+
# Fortran DCON, GPEC v1.5.5-323, on the 257x257 parent of the fixture at mpsi 256 / mtheta 512.
20+
const WP_STIFF_DCON = 1.6092e4
21+
22+
function run_fixed_axis_case(fixed_axis::Bool)
23+
dir = mktempdir()
24+
for f in readdir(FIXED_AXIS_FIXTURE)
25+
cp(joinpath(FIXED_AXIS_FIXTURE, f), joinpath(dir, f))
26+
end
27+
toml = joinpath(dir, "gpec.toml")
28+
s = read(toml, String)
29+
s = replace(s, r"^fixed_axis.*\n"m => "")
30+
s = replace(s, "[ForceFreeStates]\n" => "[ForceFreeStates]\nfixed_axis = $(fixed_axis)\n")
31+
write(toml, s)
32+
r = GeneralizedPerturbedEquilibrium.main([dir])
33+
fb = r.ffs.free_boundary
34+
wp = Matrix(fb.wp)
35+
wpe = eigvals(Hermitian((wp + wp') / 2))
36+
dp = real.(diag(Matrix(r.ffs.delta_prime.matrix)))
37+
return (et1 = minimum(real.(fb.et)), wp_min = minimum(wpe), wp_max = maximum(wpe),
38+
n_neg = count(<(0), wpe), dprime = dp)
39+
end
40+
41+
@testset "Fixed-axis default (DCON axis condition)" begin
42+
# The default itself is the fix: pin it.
43+
@test ForceFreeStatesControl().fixed_axis == true
44+
45+
fixed = run_fixed_axis_case(true)
46+
@test fixed.et1 > 0 # physical fundamental, not a pole
47+
@test fixed.n_neg == 0 # W_p has no negative eigenvalue (DCON: none)
48+
@test isapprox(fixed.wp_max, WP_STIFF_DCON; rtol=0.02) # stiff eigenvalue on DCON's value (this fixture gives 1.6090e4; 2 % covers 129^2 / mpsi 128 vs DCON's 257^2 / mpsi 256)
49+
50+
# The Frobenius free-axis start, kept as an opt-in: record its defect.
51+
free = run_fixed_axis_case(false)
52+
@test_broken free.et1 > 0
53+
@test_broken free.n_neg == 0
54+
@test_broken isapprox(free.wp_max, WP_STIFF_DCON; rtol=0.02)
55+
56+
# The Delta' BVP does not depend on the axis condition.
57+
@test length(fixed.dprime) == length(free.dprime)
58+
@test isapprox(fixed.dprime, free.dprime; rtol=1e-3)
59+
end

test/test_data/README_test_data.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
Put relevant data for various tests here, most likely outputs from the Fortran code used to validate Julia outputs
22

33
# TODO: store as hdf5 files instead?
4+
5+
## regression_rampup_fixed_axis/
6+
7+
Fixture for `test/runtests_fixed_axis.jl`: a synthetic, diverted DIII-D-like ramp-up
8+
slice (TokaMaker free-boundary solve, 129×129 geqdsk, q0 = 3.42, q95 = 5.9, no wall,
9+
n = 1) on which the Frobenius free-axis start (`fixed_axis = false`) produces a spurious
10+
`et[1]` ≈ −1e5 while the DCON fixed-axis start (`fixed_axis = true`, the default) gives
11+
`et[1]` = +1.50 and a plasma-matrix stiff eigenvalue of +1.609e4, matching Fortran DCON
12+
(v1.5.5-323) on the 257×257 parent equilibrium. `gpec.toml` carries the production settings
13+
(mpsi 128, mtheta 256, mthvac 480, `set_psilim_via_dmlim = true`); the test injects
14+
`fixed_axis` itself. Analytic profiles, not experimental data.

0 commit comments

Comments
 (0)