From f852af60c44a7751b77995cd036d83bd71ac0d44 Mon Sep 17 00:00:00 2001 From: logan-nc Date: Fri, 21 Aug 2026 23:13:50 -0400 Subject: [PATCH 1/2] PE - EXPERIMENT - Kinetic runs regularize the stored xi_s (Fortran gpeq.f kin_flag analog) The Clebsch xi_alpha fed to the NTV kernel was re-solved from the IDEAL A,B,C even in kinetic runs, inconsistent with the self-consistent kinetic tangential response exactly where resonant layers live. Mirror Fortran: scale the stored xi_s by the singfac regularization factor and invert nothing (analog of the #407 fix, adapted to the FourFitVars API; kinetic-ness detected via populated kwmats pending proper flag threading). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LzbLFQKyuRE5DYZmLokKmk --- .../FieldReconstruction.jl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/PerturbedEquilibrium/FieldReconstruction.jl b/src/PerturbedEquilibrium/FieldReconstruction.jl index 1b0e5f8e3..2063d10ac 100644 --- a/src/PerturbedEquilibrium/FieldReconstruction.jl +++ b/src/PerturbedEquilibrium/FieldReconstruction.jl @@ -365,6 +365,25 @@ function compute_clebsch_displacements( return clebsch_psi, clebsch_psi1, clebsch_alpha end + # Kinetic runs: mirror Fortran gpeq.f kin_flag — regularize the STORED self-consistent ξ_s + # by the same singfac factor and never re-solve from matrices. The ideal-matrix resolve is + # inconsistent with the kinetic solution's tangential response (the resonant layers live in + # exactly that dynamics), and the kinetic A is non-Hermitian and must not be re-inverted + # here. Kinetic-ness detected by populated kwmats splines (sentinel has 5 knots). + if length(ffit.kwmats[1].cache.x) > 8 + for ipsi in 1:npsi + q = equil.profiles.q_spline(psi_grid[ipsi]) + for ipert in 1:mpert + m = mlow + ipert - 1 + singfac = m - nn * q + reg_factor = singfac^2 / (singfac^2 + reg_spot^2) + clebsch_psi1[ipsi, ipert] = xi_psi1_modes[ipsi, ipert] * reg_factor + clebsch_alpha[ipsi, ipert] = xi_s_modes[ipsi, ipert] * reg_factor / chi1 + end + end + return clebsch_psi, clebsch_psi1, clebsch_alpha + end + # Per-thread workspaces: matrix ops and spline hints are not safe to share across threads. # Size by maxthreadid() and index by threadid() under :static scheduling (GPEC convention). nt = Threads.maxthreadid() From 225ac39d7626fb06976ad4a7fd8f3a21c13571ac Mon Sep 17 00:00:00 2001 From: logan-nc Date: Fri, 21 Aug 2026 23:44:31 -0400 Subject: [PATCH 2/2] KF - BUGFIX! - Static scheduling for the threaded kinetic kernel loop The per-surface kernel loop indexed per-thread scratch (thread_intrs and the four matrix buffers) by threadid() under DEFAULT dynamic scheduling -- the only threaded loop in the codebase missing :static. Task migration at yield points lets two tasks share a buffer and corrupt matrix rows, making every calculated-kinetic run nondeterministic at rare-migration frequency (observed: three runs of an identical DIII-D deck gave et[1] = 1.0053, 1.1355, 0.9895). All other threadid-indexed loops already use :static per the GPEC convention. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LzbLFQKyuRE5DYZmLokKmk --- src/KineticForces/CalculatedKineticMatrices.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KineticForces/CalculatedKineticMatrices.jl b/src/KineticForces/CalculatedKineticMatrices.jl index 492a62403..d1336aa18 100644 --- a/src/KineticForces/CalculatedKineticMatrices.jl +++ b/src/KineticForces/CalculatedKineticMatrices.jl @@ -120,7 +120,7 @@ function compute_calculated_kinetic_matrices( thread_block_w = [zeros(ComplexF64, mpert, mpert, 6) for _ in 1:nthreads] thread_block_t = [zeros(ComplexF64, mpert, mpert, 6) for _ in 1:nthreads] - Threads.@threads for ipsi in 1:mpsi + Threads.@threads :static for ipsi in 1:mpsi tid = Threads.threadid() intr_t = thread_intrs[tid] full_w = thread_full_w[tid]