Skip to content

Commit 5aa1122

Browse files
logan-ncclaude
andcommitted
FFS - IMPROVEMENT - Report sub-threshold near-singular kinetic F-bar structure
The cond(F-bar) scan that locates kinetic singular surfaces already sweeps 2000 points and is written to SingularSurfaces/Kinetic/scan_cond, but only peaks above the 1e8 singular threshold were surfaced. On a DIII-D-like case the strongest peaks sit at 3e5-5e6 -- real shifted/split resonance structure (Park & Logan Eq. 70) that stayed silent, and which tracks the NTV torque-density peaks at low collisionality/rotation. Report the strongest few. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LzbLFQKyuRE5DYZmLokKmk
1 parent 168857b commit 5aa1122

1 file changed

Lines changed: 21 additions & 6 deletions

File tree

src/ForceFreeStates/Sing.jl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,11 @@ function sing_lim!(intr::ForceFreeStatesInternal, ctrl::ForceFreeStatesControl,
123123
# strategy. Multi-n runs are not supported — the "outermost rational + dmlim/n" cutoff depends
124124
# on which n is used — and fall back to qhigh / psihigh truncation with a warning.
125125
if ctrl.set_psilim_via_dmlim && intr.nlow <= 0
126-
error("sing_lim!: set_psilim_via_dmlim = true requires a resolved toroidal range, but got intr.nlow=$(intr.nlow). " *
127-
"Assign intr.nlow / intr.nhigh (from ctrl.nn_low / ctrl.nn_high) before calling sing_lim!, " *
128-
"or set set_psilim_via_dmlim = false to truncate via qhigh / psihigh instead.")
126+
error(
127+
"sing_lim!: set_psilim_via_dmlim = true requires a resolved toroidal range, but got intr.nlow=$(intr.nlow). " *
128+
"Assign intr.nlow / intr.nhigh (from ctrl.nn_low / ctrl.nn_high) before calling sing_lim!, " *
129+
"or set set_psilim_via_dmlim = false to truncate via qhigh / psihigh instead."
130+
)
129131
elseif ctrl.set_psilim_via_dmlim && intr.nlow != intr.nhigh
130132
@warn "set_psilim_via_dmlim = true is ignored for multi-n runs (nn_low=$(intr.nlow), nn_high=$(intr.nhigh)); falling back to qhigh / psihigh truncation."
131133
elseif ctrl.set_psilim_via_dmlim
@@ -266,7 +268,7 @@ function compute_sing_asymptotics(
266268

267269
# This is the parameter α but for all modes - α = 0 for non-resonant modes
268270
power[ipert_res] .= -alpha
269-
power[ipert_res .+ intr.numpert_total] .= alpha
271+
power[ipert_res.+intr.numpert_total] .= alpha
270272

271273
# Zeroth-order non-resonant solutions
272274
for ipert in 1:intr.numpert_total
@@ -303,7 +305,7 @@ function compute_sing_asymptotics(
303305
msg *= @sprintf(" m0mat(1,2)= %+.12e %+.12ei\n", real(m0mat[1, 2]), imag(m0mat[1, 2]))
304306
msg *= @sprintf(" m0mat(2,1)= %+.12e %+.12ei\n", real(m0mat[2, 1]), imag(m0mat[2, 1]))
305307
msg *= @sprintf(" m0mat(2,2)= %+.12e %+.12ei\n", real(m0mat[2, 2]), imag(m0mat[2, 2]))
306-
di = m0mat[1, 1]*m0mat[2, 2] - m0mat[2, 1]*m0mat[1, 2]
308+
di = m0mat[1, 1] * m0mat[2, 2] - m0mat[2, 1] * m0mat[1, 2]
307309
msg *= @sprintf(" di= %+.12e, alpha= %+.12e %+.12ei\n", real(di), real(alpha[1]), imag(alpha[1]))
308310
msg *= @sprintf(" psifac= %+.12e, r1=%d, ipert0=%d\n", singp.psifac, r1[1], ipert0)
309311
msg *= @sprintf(" vmat(ip,ip,2,0)= %+.8e %+.8ei\n", real(vmat[ipert0, ipert0, 2, 1]), imag(vmat[ipert0, ipert0, 2, 1]))
@@ -771,7 +773,7 @@ function sing_get_ua(sing_asymp::SingAsymptotics, dpsi::Float64)
771773

772774
# Restore powers (unshear v→u) — matches Fortran STRIDE sing_get_ua
773775
for i in eachindex(r1)
774-
pfac = pfac_base ^ sing_asymp.alpha[i] # dpsi^α
776+
pfac = pfac_base^sing_asymp.alpha[i] # dpsi^α
775777
ua[:, r2[2*i-1], :] ./= pfac # big solution column: /dpsi^α
776778
ua[:, r2[2*i], :] .*= pfac # small solution column: *dpsi^α
777779
ua[r1[i], :, 1] ./= sqrtfac # resonant row ξ: /√dpsi
@@ -1432,6 +1434,19 @@ function find_kinetic_singular_surfaces!(ffit::FourFitVars, equil::Equilibrium.P
14321434
end
14331435
end
14341436

1437+
# Peaks below the threshold are not singular surfaces, but they mark where the kinetic F̄ comes
1438+
# closest to singular — the shifted/split resonances of Park & Logan Eq. (70). Report the
1439+
# strongest few so sharp kinetic structure is visible rather than silent (on a DIII-D-like case
1440+
# these track the NTV torque-density peaks at low collisionality/rotation).
1441+
subthreshold = [i for i in 2:(ngrid-1) if cond_vals[i] > cond_vals[i-1] && cond_vals[i] > cond_vals[i+1] &&
1442+
cond_threshold / 100 < cond_vals[i] <= cond_threshold]
1443+
if !isempty(subthreshold)
1444+
top = sort(subthreshold; by=i -> -cond_vals[i])[1:min(3, length(subthreshold))]
1445+
@info "Kinetic F̄ near-singular structure below the singular threshold at " *
1446+
join(["ψ=$(round(psi_grid[i]; digits=4)) (cond=$(round(cond_vals[i]; sigdigits=3)))" for i in top], ", ") *
1447+
" — full scan in SingularSurfaces/Kinetic/scan_cond; check the ψ grid resolves these if results look grid-sensitive"
1448+
end
1449+
14351450
# Refine each peak to find the precise ψ location
14361451
kinsing_surfaces = SingType[]
14371452
for idx in peak_indices

0 commit comments

Comments
 (0)