From c868d32628f5d4bae7f9f0784c39873192865d16 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:04:05 -0400 Subject: [PATCH 01/16] EQUIL - NEW FEATURE - Add override_psi_nodes injection to all equilibrium solver paths Adds an override_psi_nodes keyword to setup_equilibrium and threads it through the direct, arclength, inverse, and by-inversion solvers, bypassing the config-driven grid with a validated externally supplied node vector. This is the injection point for the two-pass auto-grid refinement. Co-Authored-By: Claude Fable 5 --- src/Equilibrium/DirectEquilibrium.jl | 7 +++-- .../DirectEquilibriumByInversion.jl | 9 ++++--- src/Equilibrium/Equilibrium.jl | 16 ++++++++---- src/Equilibrium/GridRefinement.jl | 26 +++++++++++++++++++ src/Equilibrium/InverseEquilibrium.jl | 7 +++-- 5 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 src/Equilibrium/GridRefinement.jl diff --git a/src/Equilibrium/DirectEquilibrium.jl b/src/Equilibrium/DirectEquilibrium.jl index 8d3d08a4..8c4976cd 100644 --- a/src/Equilibrium/DirectEquilibrium.jl +++ b/src/Equilibrium/DirectEquilibrium.jl @@ -553,7 +553,8 @@ robustness. including the profile spline (`sq`), the coordinate mapping spline (`rzphi`), and the physics quantity spline (`eqfun`). """ -@with_pool pool function equilibrium_solver(raw_profile::DirectRunInput, fieldline_int=direct_fieldline_int) +@with_pool pool function equilibrium_solver(raw_profile::DirectRunInput, fieldline_int=direct_fieldline_int; + override_psi_nodes::Union{Nothing,Vector{Float64}}=nothing) equil_params = raw_profile.config psio = raw_profile.psio @@ -564,7 +565,9 @@ robustness. # direct_position! must run before building psi_nodes: probe integrations need ro, zo, rs2 ro, zo, _, rs2 = direct_position!(raw_profile) - psi_nodes = _build_psi_grid(equil_params, psilow, psihigh, fieldline_int, raw_profile, ro, zo, rs2) + psi_nodes = override_psi_nodes === nothing ? + _build_psi_grid(equil_params, psilow, psihigh, fieldline_int, raw_profile, ro, zo, rs2) : + _validate_psi_nodes(override_psi_nodes, psilow, psihigh) mpsi = length(psi_nodes) - 1 theta_nodes = range(0.0, 1.0; length=mtheta + 1) diff --git a/src/Equilibrium/DirectEquilibriumByInversion.jl b/src/Equilibrium/DirectEquilibriumByInversion.jl index 3ba70280..87308efe 100644 --- a/src/Equilibrium/DirectEquilibriumByInversion.jl +++ b/src/Equilibrium/DirectEquilibriumByInversion.jl @@ -401,7 +401,8 @@ function equilibrium_solver_by_inversion( resolution_factor::Float64=4.0, refine::Union{Nothing,Int}=nothing, β_r::Union{Nothing,Float64}=nothing, - β_z::Union{Nothing,Float64}=nothing + β_z::Union{Nothing,Float64}=nothing, + override_psi_nodes::Union{Nothing,Vector{Float64}}=nothing ) equil_params = raw_profile.config psio = raw_profile.psio @@ -412,7 +413,9 @@ function equilibrium_solver_by_inversion( # Find magnetic axis and separatrix before building psi_nodes (needed for probe integrations) ro, zo, _, rs2 = direct_position!(raw_profile) - psi_nodes = _build_psi_grid(equil_params, psilow, psihigh, direct_fieldline_int, raw_profile, ro, zo, rs2) + psi_nodes = override_psi_nodes === nothing ? + _build_psi_grid(equil_params, psilow, psihigh, direct_fieldline_int, raw_profile, ro, zo, rs2) : + _validate_psi_nodes(override_psi_nodes, psilow, psihigh) mpsi = length(psi_nodes) - 1 # Detect plasma topology for sinh-stretching direction @@ -671,7 +674,7 @@ function equilibrium_solver_by_inversion( inv_input = InverseRunInput(raw_profile.config, raw_profile.sq_in, rz_in_xs, rz_in_ys, rz_in_R, rz_in_Z, ro, zo, psio, nothing) - pe = equilibrium_solver(inv_input) + pe = equilibrium_solver(inv_input; override_psi_nodes) # Round-trip validation: (ψ,θ) → (R,Z) → ψ_spline − ψ_target. # Checks 4 angles at the outermost surface and at 75% of the radial grid. diff --git a/src/Equilibrium/Equilibrium.jl b/src/Equilibrium/Equilibrium.jl index 13e478e2..142ce0ff 100644 --- a/src/Equilibrium/Equilibrium.jl +++ b/src/Equilibrium/Equilibrium.jl @@ -12,6 +12,7 @@ import ..Utilities # --- Internal Module Structure --- include("EquilibriumTypes.jl") +include("GridRefinement.jl") include("FluxSurfaceMetrics.jl") include("CoordinateInvariant.jl") include("ReadEquilibrium.jl") @@ -67,15 +68,20 @@ const ANALYTIC_EQ = Dict( ) """ - setup_equilibrium(eq_config::EquilibriumConfig) + setup_equilibrium(eq_config::EquilibriumConfig, additional_input=nothing; override_psi_nodes=nothing) Read an equilibrium file, run the appropriate solver, and return the processed `PlasmaEquilibrium` with global parameters, q-profile, and GSE diagnostics. + +`override_psi_nodes` bypasses the config-driven radial grid (`grid_type`/`mpsi`) and forms +the equilibrium on the given strictly increasing ψ_N node vector, whose endpoints must match +`psilow`/`psihigh`. Used by the two-pass auto-grid refinement in the main driver; standalone +callers with `mpsi=0` get the coarse first-pass grid unless they refine and re-form. """ function setup_equilibrium(path::String="equil.toml") return setup_equilibrium(EquilibriumConfig(path)) end -function setup_equilibrium(eq_config::EquilibriumConfig, additional_input=nothing) +function setup_equilibrium(eq_config::EquilibriumConfig, additional_input=nothing; override_psi_nodes::Union{Nothing,Vector{Float64}}=nothing) eq_type = eq_config.eq_type @@ -130,11 +136,11 @@ function setup_equilibrium(eq_config::EquilibriumConfig, additional_input=nothin end if eq_type == "efit_by_inversion" - plasma_equilibrium = equilibrium_solver_by_inversion(eq_input) + plasma_equilibrium = equilibrium_solver_by_inversion(eq_input; override_psi_nodes) elseif eq_type == "efit_arclength" - plasma_equilibrium = equilibrium_solver(eq_input, arclength_fieldline_int) + plasma_equilibrium = equilibrium_solver(eq_input, arclength_fieldline_int; override_psi_nodes) else - plasma_equilibrium = equilibrium_solver(eq_input) + plasma_equilibrium = equilibrium_solver(eq_input; override_psi_nodes) end # Forward the captured ingest so the gpec.h5 writer can snapshot it (nothing for analytic). diff --git a/src/Equilibrium/GridRefinement.jl b/src/Equilibrium/GridRefinement.jl new file mode 100644 index 00000000..032d3c0a --- /dev/null +++ b/src/Equilibrium/GridRefinement.jl @@ -0,0 +1,26 @@ +""" +Radial grid refinement utilities for the two-pass auto ψ grid. + +Pass 1 forms the equilibrium on a coarse grid; `refined_psi_grid` then derives a knot +density from the measured nodal data (profiles, 2D geometry channels, and optional kinetic +profiles) and equidistributes it, pinning mandatory knots (e.g. rational surfaces) via +`merge_mandatory_nodes`. Pass 2 re-forms the equilibrium on the refined grid through the +`override_psi_nodes` keyword of `setup_equilibrium`. +""" + +""" + _validate_psi_nodes(psi_nodes, psilow, psihigh) -> Vector{Float64} + +Check that an externally supplied ψ node vector is strictly increasing and spans exactly +[`psilow`, `psihigh`]. Errors loudly on violation (e.g. a psihigh separatrix re-clamp +between grid construction and equilibrium formation). +""" +function _validate_psi_nodes(psi_nodes::Vector{Float64}, psilow::Real, psihigh::Real) + length(psi_nodes) >= 2 || error("override_psi_nodes must contain at least 2 nodes") + all(diff(psi_nodes) .> 0) || error("override_psi_nodes must be strictly increasing") + isapprox(psi_nodes[1], psilow; atol=1e-12) || + error("override_psi_nodes[1]=$(psi_nodes[1]) must equal psilow=$psilow") + isapprox(psi_nodes[end], psihigh; atol=1e-12) || + error("override_psi_nodes[end]=$(psi_nodes[end]) must equal psihigh=$psihigh") + return psi_nodes +end diff --git a/src/Equilibrium/InverseEquilibrium.jl b/src/Equilibrium/InverseEquilibrium.jl index c1ae99d6..0447933f 100644 --- a/src/Equilibrium/InverseEquilibrium.jl +++ b/src/Equilibrium/InverseEquilibrium.jl @@ -41,7 +41,7 @@ end -function equilibrium_solver(input::InverseRunInput) +function equilibrium_solver(input::InverseRunInput; override_psi_nodes::Union{Nothing,Vector{Float64}}=nothing) @info "Starting Inverse Equilibrium Processing" # Extract input parameters @@ -135,7 +135,10 @@ function equilibrium_solver(input::InverseRunInput) # c----------------------------------------------------------------------- # c set up radial grid # c----------------------------------------------------------------------- - if grid_type == "log_asymptotic" + if override_psi_nodes !== nothing + sq_xs = _validate_psi_nodes(override_psi_nodes, psilow, psihigh) + mpsi = length(sq_xs) - 1 + elseif grid_type == "log_asymptotic" n_core_mid_edge = nothing if mpsi == 0 && config.psi_accuracy > 0 # Estimate A from q profile near psihigh (q is column 3 in sq_in) From 24b1309e24b9e3638cc1db7ecab01524ec33844b Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:11:38 -0400 Subject: [PATCH 02/16] EQUIL - NEW FEATURE - Measured-curvature knot density, equidistribution, and mandatory-node merge GridRefinement.jl derives the pass-2 knot density from the formed pass-1 equilibrium using the cubic h^4 error model on nodal fourth divided differences (1D profiles, rzphi geometry channels at sampled theta lines, and kinetic profiles when present), with a-priori edge/core geometric floors, then equidistributes and pins mandatory knots with a delta_min snap guard. The log_asymptotic auto path now forms a coarse fixed-128 pass-1 layout; the one-pass a-priori heuristics (make_optimal_mpsi, probe log-slope, 300-point mid-spacing sampling) are removed. All regions now scale as psi_accuracy^(-1/4), so tightening the tolerance refines edge and mid proportionally. Co-Authored-By: Claude Fable 5 --- src/Equilibrium/DirectEquilibrium.jl | 128 ++------ .../DirectEquilibriumByInversion.jl | 4 +- src/Equilibrium/Equilibrium.jl | 1 + src/Equilibrium/GridRefinement.jl | 280 +++++++++++++++++- src/Equilibrium/InverseEquilibrium.jl | 42 +-- 5 files changed, 312 insertions(+), 143 deletions(-) diff --git a/src/Equilibrium/DirectEquilibrium.jl b/src/Equilibrium/DirectEquilibrium.jl index 8c4976cd..ccb002d4 100644 --- a/src/Equilibrium/DirectEquilibrium.jl +++ b/src/Equilibrium/DirectEquilibrium.jl @@ -383,89 +383,6 @@ function direct_refine(rfac::Float64, eta::Float64, psi0::Float64, params::Field atol=1e-12*abs(psi0), rtol=1e-12, maxevals=50) end -""" - _estimate_log_slope(fieldline_int, raw_profile, ro, zo, rs2, psihigh) - -Estimate the logarithmic slope A from two field-line probe integrations near the separatrix, -using the asymptotic form q(ψ) ≃ −A·ln(1−ψ) (Fitzpatrick 2024, eq. 19). - -Returns A = |Δq| / ln(2). Falls back to A=2.0 on integration failure. -""" -function _estimate_log_slope(fieldline_int, raw_profile, ro, zo, rs2, psihigh) - eps_sep = max(1.0 - psihigh, 0.001) - psi1 = clamp(1.0 - 3 * eps_sep, raw_profile.config.psilow + 0.01, 0.999) - psi2 = clamp(1.0 - 1.5 * eps_sep, raw_profile.config.psilow + 0.01, 0.999) - try - out1 = fieldline_int(psi1, raw_profile, ro, zo, rs2) - q1 = out1[2].f * out1[1][end, 4] / (2π) - out2 = fieldline_int(psi2, raw_profile, ro, zo, rs2) - q2 = out2[2].f * out2[1][end, 4] / (2π) - A = max(abs(q2 - q1) / log(2), 0.1) - @info "Estimated separatrix log slope A = $(@sprintf("%.3f", A)) from probe integrations at psi = $(@sprintf("%.4f", psi1)), $(@sprintf("%.4f", psi2))" - return A - catch err - @warn "Failed to estimate log slope from probe integrations, using default A=2.0: $err" - return 2.0 - end -end - -""" - _estimate_mid_spacing(sq_in, psi_split_core, psi_split_edge, tau) - -Estimate the uniform knot spacing needed in the middle ψ region to resolve the sharpest -profile feature (usually the pressure pedestal) to relative accuracy `tau`. - -Uses central-difference second derivatives of all 4 sq_in profiles on a 300-point sample. -The required spacing for profile k is h_k = sqrt(8τ / d2_norm_k) where d2_norm_k is -max|f''| / max|f| over [psi_split_core, psi_split_edge]. -""" -function _estimate_mid_spacing(sq_in, psi_split_core, psi_split_edge, tau) - n_samp = 300 - psi_samp = range(psi_split_core, psi_split_edge; length=n_samp) - h_samp = step(psi_samp) - h_min = Inf - # Size the buffer to the actual profile count, not a hardcoded 4 (cf. InverseEquilibrium.jl). - nq = size(sq_in.y, 2) - buf = zeros(nq) - all_vals = [ - begin - sq_in(buf, ψ) - copy(buf) - end for ψ in psi_samp - ] - for k in 1:nq - vals = [all_vals[i][k] for i in 1:n_samp] - f_scale = max(maximum(abs.(vals)), 1e-12) - d2_max = 0.0 - for i in 2:(n_samp-1) - d2 = abs(vals[i+1] - 2vals[i] + vals[i-1]) / (h_samp^2 * f_scale) - d2_max = max(d2_max, d2) - end - d2_max < 1e-10 && continue - h_min = min(h_min, sqrt(8 * tau / d2_max)) - end - return clamp(h_min, 1e-3, 0.2) -end - -""" - make_optimal_mpsi(psilow, psihigh, A, sq_in; tau, psi_split_core, psi_split_edge) - -Compute the minimum number of radial knots needed to achieve target accuracy τ in q, -given the separatrix log slope A. Three-region geometric grid: core, pedestal, far edge. -The middle-region spacing is driven by profile curvature (P, F, dV/dψ, q) via sq_in. -""" -function make_optimal_mpsi(psilow, psihigh, A, sq_in; - tau=0.005, psi_split_core=0.03, psi_split_edge=0.98) - dlog = (13.0 * tau / A)^(1/4) - N_edge = ceil(Int, log((1.0 - psi_split_edge) / (1.0 - psihigh)) / dlog) + 1 - h_mid = _estimate_mid_spacing(sq_in, psi_split_core, psi_split_edge, tau) - N_mid = ceil(Int, (psi_split_edge - psi_split_core) / h_mid) - N_core = ceil(Int, log(psi_split_core / psilow) / dlog) - mpsi = N_core + N_mid + N_edge - @info "Auto-mpsi: N_core=$N_core + N_mid=$N_mid + N_edge=$N_edge = $mpsi (A=$(@sprintf("%.3f",A)), h_mid=$(@sprintf("%.4f",h_mid)), tau=$tau)" - return N_core, N_mid, N_edge -end - """ make_optimal_psi_grid(psilow, psihigh, N_core, N_mid, N_edge; psi_split_core, psi_split_edge) @@ -486,39 +403,36 @@ function make_optimal_psi_grid(psilow, psihigh, N_core, N_mid, N_edge; end """ - _build_psi_grid(equil_params, psilow, psihigh, fieldline_int, raw_profile, ro, zo, rs2) + _build_psi_grid(equil_params, psilow, psihigh) Resolve `mpsi` and build `psi_nodes` for any supported `grid_type`. -For `"log_asymptotic"` with `mpsi=0`, estimates the separatrix log slope from two probe -integrations and computes the minimum knot count for `psi_accuracy`. For `"ldp"`, uses -the sin²-spaced grid. Shared by `direct_fieldline_int` and `efit_by_inversion` solvers. +For `"log_asymptotic"` with `mpsi=0` (the auto grid), this is the coarse pass-1 layout of +the two-pass refinement — the main driver measures the formed equilibrium and re-forms on +the `refined_psi_grid` result. Shared by `direct_fieldline_int` and `efit_by_inversion` +solvers. """ -function _build_psi_grid(equil_params, psilow, psihigh, fieldline_int, raw_profile, ro, zo, rs2) +function _build_psi_grid(equil_params, psilow, psihigh) mpsi = equil_params.mpsi - n_core_mid_edge = nothing if equil_params.grid_type == "log_asymptotic" && mpsi == 0 && equil_params.psi_accuracy > 0 - A = _estimate_log_slope(fieldline_int, raw_profile, ro, zo, rs2, psihigh) - n_core_mid_edge = make_optimal_mpsi(psilow, psihigh, A, raw_profile.sq_in; tau=equil_params.psi_accuracy) - mpsi = sum(n_core_mid_edge) + # Two-pass auto grid: this is the coarse pass-1 layout; the driver measures the + # formed equilibrium's curvature and re-forms on a refined grid (GridRefinement.jl). + mpsi = 128 + @info "Auto psi grid: forming pass-1 equilibrium on coarse $(mpsi)-interval log_asymptotic grid pending curvature-based refinement" elseif mpsi == 0 mpsi = 128 end psi_nodes = if equil_params.grid_type == "log_asymptotic" - if n_core_mid_edge !== nothing - make_optimal_psi_grid(psilow, psihigh, n_core_mid_edge...;) - else - # Fixed mpsi specified: distribute by log-weights - log_core = log(0.03 / psilow) - log_mid = log(0.98 / 0.03) - log_edge = log((1.0 - 0.98) / (1.0 - psihigh)) - log_total = log_core + log_mid + log_edge - N_edge = clamp(round(Int, mpsi * log_edge / log_total), 2, mpsi ÷ 2) - N_core = round(Int, mpsi * log_core / log_total) - N_mid = mpsi - N_edge - N_core - make_optimal_psi_grid(psilow, psihigh, N_core, N_mid, N_edge) - end + # Distribute mpsi across the three regions by log-weights + log_core = log(0.03 / psilow) + log_mid = log(0.98 / 0.03) + log_edge = log((1.0 - 0.98) / (1.0 - psihigh)) + log_total = log_core + log_mid + log_edge + N_edge = clamp(round(Int, mpsi * log_edge / log_total), 2, mpsi ÷ 2) + N_core = round(Int, mpsi * log_core / log_total) + N_mid = mpsi - N_edge - N_core + make_optimal_psi_grid(psilow, psihigh, N_core, N_mid, N_edge) elseif equil_params.grid_type == "ldp" [psilow + (psihigh - psilow) * sin((ipsi / mpsi) * (π / 2))^2 for ipsi in 0:mpsi] elseif equil_params.grid_type == "pow1" @@ -562,11 +476,11 @@ robustness. psilow = equil_params.psilow psihigh = equil_params.psihigh - # direct_position! must run before building psi_nodes: probe integrations need ro, zo, rs2 + # Locate the magnetic axis and separatrix for the field-line integrations ro, zo, _, rs2 = direct_position!(raw_profile) psi_nodes = override_psi_nodes === nothing ? - _build_psi_grid(equil_params, psilow, psihigh, fieldline_int, raw_profile, ro, zo, rs2) : + _build_psi_grid(equil_params, psilow, psihigh) : _validate_psi_nodes(override_psi_nodes, psilow, psihigh) mpsi = length(psi_nodes) - 1 theta_nodes = range(0.0, 1.0; length=mtheta + 1) diff --git a/src/Equilibrium/DirectEquilibriumByInversion.jl b/src/Equilibrium/DirectEquilibriumByInversion.jl index 87308efe..252ae515 100644 --- a/src/Equilibrium/DirectEquilibriumByInversion.jl +++ b/src/Equilibrium/DirectEquilibriumByInversion.jl @@ -410,11 +410,11 @@ function equilibrium_solver_by_inversion( psilow = equil_params.psilow psihigh = equil_params.psihigh - # Find magnetic axis and separatrix before building psi_nodes (needed for probe integrations) + # Locate the magnetic axis and separatrix for the contour tracing ro, zo, _, rs2 = direct_position!(raw_profile) psi_nodes = override_psi_nodes === nothing ? - _build_psi_grid(equil_params, psilow, psihigh, direct_fieldline_int, raw_profile, ro, zo, rs2) : + _build_psi_grid(equil_params, psilow, psihigh) : _validate_psi_nodes(override_psi_nodes, psilow, psihigh) mpsi = length(psi_nodes) - 1 diff --git a/src/Equilibrium/Equilibrium.jl b/src/Equilibrium/Equilibrium.jl index 142ce0ff..3ff48d46 100644 --- a/src/Equilibrium/Equilibrium.jl +++ b/src/Equilibrium/Equilibrium.jl @@ -29,6 +29,7 @@ export setup_equilibrium, EquilibriumConfig, PlasmaEquilibrium, EquilibriumParam ProfileSplines, GeometryProfileSplines, compute_geometry_profiles, KineticProfileSplines, load_kinetic_profiles export flux_surface_metric, flux_surface_area +export wants_two_pass, refined_psi_grid, merge_mandatory_nodes, implied_knot_count export compute_sqrt_jac_delpsi, compute_sqrtamat, rootarea_to_area_weight, area_to_rootarea_weight # --- Constants --- diff --git a/src/Equilibrium/GridRefinement.jl b/src/Equilibrium/GridRefinement.jl index 032d3c0a..6085719d 100644 --- a/src/Equilibrium/GridRefinement.jl +++ b/src/Equilibrium/GridRefinement.jl @@ -2,11 +2,36 @@ Radial grid refinement utilities for the two-pass auto ψ grid. Pass 1 forms the equilibrium on a coarse grid; `refined_psi_grid` then derives a knot -density from the measured nodal data (profiles, 2D geometry channels, and optional kinetic -profiles) and equidistributes it, pinning mandatory knots (e.g. rational surfaces) via -`merge_mandatory_nodes`. Pass 2 re-forms the equilibrium on the refined grid through the -`override_psi_nodes` keyword of `setup_equilibrium`. +density from the measured nodal data (1D profiles, 2D geometry channels, and optional +kinetic profiles) and equidistributes it, pinning mandatory knots (e.g. rational surfaces) +via `merge_mandatory_nodes`. Pass 2 re-forms the equilibrium on the refined grid through +the `override_psi_nodes` keyword of `setup_equilibrium`. + +The density uses the cubic-interpolation error model err ≈ h⁴|f''''|/384, so the knot +count of every region scales as psi_accuracy^(-1/4) and tightening the tolerance refines +all regions proportionally. Fourth derivatives are estimated by divided differences on the +pass-1 nodes only — nodal values come from independent field-line integrations and are +immune to inter-knot spline ringing. +""" + +# Cubic spline interpolation error constant: err ≈ h⁴|f''''|/384 +const CUBIC_ERR_CONST = 384.0 +# |f''''|/f_scale below this is treated as integrator noise, not curvature +const D4_NOISE_FLOOR = 1e3 +# Per-quantity target spacing clamp (ψ_N units) +const H_TARGET_MIN = 1e-4 +const H_TARGET_MAX = 0.2 +# θ-lines subsample stride for the 2D geometry channels +const THETA_STRIDE = 8 + """ + wants_two_pass(config::EquilibriumConfig) -> Bool + +True when the config requests the automatic two-pass grid: `grid_type = "log_asymptotic"` +with `mpsi = 0` and a positive `psi_accuracy`. +""" +wants_two_pass(config::EquilibriumConfig) = + config.grid_type == "log_asymptotic" && config.mpsi == 0 && config.psi_accuracy > 0 """ _validate_psi_nodes(psi_nodes, psilow, psihigh) -> Vector{Float64} @@ -24,3 +49,250 @@ function _validate_psi_nodes(psi_nodes::Vector{Float64}, psilow::Real, psihigh:: error("override_psi_nodes[end]=$(psi_nodes[end]) must equal psihigh=$psihigh") return psi_nodes end + +""" + _fourth_derivative_nodes(xs, ys) -> Vector{Float64} + +Estimate |f''''| at each node of a (possibly nonuniform) grid by 5-point Newton divided +differences: f'''' ≈ 4!·f[x_{i-2},…,x_{i+2}]. The two nodes at each boundary reuse the +nearest interior stencil. Returns zeros when fewer than 5 nodes are available. +""" +function _fourth_derivative_nodes(xs::AbstractVector{<:Real}, ys::AbstractVector{<:Real}) + n = length(xs) + d4 = zeros(Float64, n) + n < 5 && return d4 + @inbounds for i in 1:n + j = clamp(i - 2, 1, n - 4) # stencil start: centered where possible, one-sided at ends + # Newton divided-difference table over xs[j:j+4] + c = Float64[ys[j+k] for k in 0:4] + for order in 1:4 + for k in 4:-1:order + c[k+1] = (c[k+1] - c[k]) / (xs[j+k] - xs[j+k-order]) + end + end + d4[i] = abs(24.0 * c[5]) + end + return d4 +end + +""" + _density_from_curvature!(rho, xs, d4, f_scale, tau) + +Accumulate (in place, by max) the knot density implied by the h⁴ error model for one +quantity: ρ = 1/h_target with h_target = (384·τ·f_scale/|f''''|)^(1/4), clamped to +[`H_TARGET_MIN`, `H_TARGET_MAX`]. Normalized curvature below `D4_NOISE_FLOOR` is ignored. +""" +function _density_from_curvature!(rho::Vector{Float64}, d4::Vector{Float64}, f_scale::Float64, tau::Float64) + @inbounds for i in eachindex(rho) + d4n = d4[i] / f_scale + d4n < D4_NOISE_FLOOR && continue + h = clamp((CUBIC_ERR_CONST * tau / d4n)^0.25, H_TARGET_MIN, H_TARGET_MAX) + rho[i] = max(rho[i], 1.0 / h) + end + return rho +end + +""" + _measured_edge_log_slope(xs, q_vals; nk=5) -> Float64 + +Estimate the separatrix log slope A of q ≈ −A·ln(1−ψ) from the outermost pass-1 q nodes. +Returns the fallback A=2.0 when the estimate is non-finite or non-positive (e.g. reversed +edge shear); analytic equilibria with finite edge q give a genuinely small A and hence a +weak edge floor. +""" +function _measured_edge_log_slope(xs::Vector{Float64}, q_vals::AbstractVector{<:Real}; nk::Int=5) + n = length(xs) + n < nk + 1 && return 2.0 + j = n - nk + denom = log((1.0 - xs[j]) / (1.0 - xs[n])) + A = (q_vals[n] - q_vals[j]) / denom + return (isfinite(A) && A > 0) ? A : 2.0 +end + +""" + _knot_density(equil::PlasmaEquilibrium; tau, kin=nothing) -> Vector{Float64} + +Knot density ρ(ψ) (knots per unit ψ_N) at the pass-1 nodes `equil.profiles.xs`, combining +by max: + + - measured h⁴-model curvature of the 1D profiles F, P, dV/dψ, q; + - curvature of the four rzphi geometry channels along every `THETA_STRIDE`-th θ-line + (max over θ) — the bicubic ψ-axis shares these knots, so geometry sharpness must + attract knots just like the 1D profiles; + - curvature of the kinetic profiles n_i, n_e, T_i, T_e, ω_E when `kin` is given + (their own grid, rebroadcast by linear interpolation) — steep pedestal gradients in + the kinetic data pull knots into the pedestal; + - a-priori floors: geometric-in-log(1−ψ) at the edge with slope A measured from the + pass-1 q nodes, geometric-in-log(ψ) in the core, and a global minimum density. + +A running max over ±1 node smooths single-stencil dropouts. +""" +function _knot_density(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing) + xs = equil.profiles.xs + n = length(xs) + rho = zeros(Float64, n) + + # 1D profiles + for spl in (equil.profiles.F_spline, equil.profiles.P_spline, equil.profiles.dVdpsi_spline, equil.profiles.q_spline) + vals = spl.y + f_scale = max(maximum(abs, vals), 1e-12) + _density_from_curvature!(rho, _fourth_derivative_nodes(xs, vals), f_scale, tau) + end + + # 2D geometry channels: per-θ-line curvature along ψ, max over θ, plane-wide scale + for interp in (equil.rzphi_rsquared, equil.rzphi_offset, equil.rzphi_nu, equil.rzphi_jac) + vals2d = @view interp.nodal_derivs.partials[1, :, :] + f_scale = max(maximum(abs, vals2d), 1e-12) + for itheta in 1:THETA_STRIDE:size(vals2d, 2) + _density_from_curvature!(rho, _fourth_derivative_nodes(xs, @view(vals2d[:, itheta])), f_scale, tau) + end + end + + # Kinetic profiles on their own grid, rebroadcast onto xs + if kin !== nothing + rho_kin = zeros(Float64, length(kin.xs)) + for spl in (kin.ni_spline, kin.ne_spline, kin.Ti_spline, kin.Te_spline, kin.omegaE_spline) + vals = spl.y + f_scale = max(maximum(abs, vals), 1e-12) + _density_from_curvature!(rho_kin, _fourth_derivative_nodes(kin.xs, vals), f_scale, tau) + end + kin_itp = cubic_interp(kin.xs, rho_kin; extrap=ExtendExtrap()) + @inbounds for i in 1:n + (kin.xs[1] <= xs[i] <= kin.xs[end]) || continue + rho[i] = max(rho[i], kin_itp(xs[i])) + end + end + + # Running max over ±1 node + rho_s = copy(rho) + @inbounds for i in 1:n + i > 1 && (rho_s[i] = max(rho_s[i], rho[i-1])) + i < n && (rho_s[i] = max(rho_s[i], rho[i+1])) + end + + # A-priori floors: edge/core geometric packing and a global minimum + A = _measured_edge_log_slope(xs, equil.profiles.q_spline.y) + dlog = (13.0 * tau / A)^0.25 + @inbounds for i in 1:n + xs[i] >= 0.9 && (rho_s[i] = max(rho_s[i], 1.0 / (dlog * (1.0 - xs[i])))) + xs[i] <= 0.03 && (rho_s[i] = max(rho_s[i], 1.0 / (dlog * xs[i]))) + rho_s[i] = max(rho_s[i], 1.0 / H_TARGET_MAX) + end + return rho_s +end + +""" + _equidistribute(xs, rho, N) -> Vector{Float64} + +Place N+1 nodes over [xs[1], xs[end]] by equidistributing the density integral +M(ψ) = ∫ρ dψ (trapezoid on the sample nodes, piecewise-linear inversion). Endpoints are +exactly xs[1] and xs[end]; interior nodes are strictly increasing since ρ > 0. +""" +function _equidistribute(xs::Vector{Float64}, rho::Vector{Float64}, N::Int) + n = length(xs) + M = zeros(Float64, n) + @inbounds for i in 2:n + M[i] = M[i-1] + 0.5 * (rho[i] + rho[i-1]) * (xs[i] - xs[i-1]) + end + nodes = Vector{Float64}(undef, N + 1) + nodes[1] = xs[1] + nodes[end] = xs[end] + k = 2 + @inbounds for j in 1:(N-1) + target = M[end] * j / N + while M[k] < target + k += 1 + end + frac = (target - M[k-1]) / (M[k] - M[k-1]) + nodes[j+1] = xs[k-1] + frac * (xs[k] - xs[k-1]) + end + return nodes +end + +""" + merge_mandatory_nodes(grid, mandatory; delta_frac=0.25) -> Vector{Float64} + +Insert mandatory knots (e.g. rational surfaces) into a base grid with a minimum-spacing +guard: for each mandatory node, δ_min = `delta_frac` × (containing base-grid interval), +and any pre-existing non-mandatory node within δ_min is dropped (snap — the mandatory node +is never moved). Endpoints always win: mandatory nodes outside the open span or within +δ_min of an endpoint are discarded. Mandatory nodes within δ_min of an earlier mandatory +node are collapsed onto it — the same physical surface can be found through several (m, n) +pairs differing only by root-finder noise, and a near-zero interval would ring the +reconstructed splines; two genuinely distinct surfaces closer than δ_min cannot be +resolved separately at the local grid resolution anyway. + +The function is agnostic to node provenance, so future mandatory sources (e.g. kinetic +resonance surfaces) plug in without change. +""" +function merge_mandatory_nodes(grid::Vector{Float64}, mandatory::Vector{Float64}; delta_frac::Float64=0.25) + isempty(mandatory) && return copy(grid) + lo, hi = grid[1], grid[end] + mand = sort(unique(mandatory)) + + # Local base spacing of the interval containing x (from the original base grid) + base_h = function (x) + k = clamp(searchsortedlast(grid, x), 1, length(grid) - 1) + return grid[k+1] - grid[k] + end + + kept = Tuple{Float64,Bool}[(g, false) for g in grid] # (value, is_mandatory) + last_mand = -Inf + for m in mand + (lo < m < hi) || continue + δ = delta_frac * base_h(m) + (m - lo < δ || hi - m < δ) && continue + if m - last_mand < δ + @debug "merge_mandatory_nodes: collapsing mandatory node ψ=$m onto ψ=$last_mand (spacing < δ_min)" + continue + end + # Snap: drop non-mandatory, non-endpoint nodes within δ of the mandatory node + filter!(t -> t[2] || t[1] == lo || t[1] == hi || abs(t[1] - m) >= δ, kept) + push!(kept, (m, true)) + last_mand = m + end + sort!(kept; by=first) + + merged = [t[1] for t in kept] + all(diff(merged) .> 0) || error("merge_mandatory_nodes produced a non-increasing grid") + return merged +end + +""" + refined_psi_grid(equil::PlasmaEquilibrium; tau, kin=nothing, mandatory=Float64[], + delta_frac=0.25, N_cap=1024) -> Vector{Float64} + +Build the refined pass-2 ψ grid from a formed pass-1 equilibrium: measured-curvature knot +density (`_knot_density`), equidistribution, and mandatory-knot insertion +(`merge_mandatory_nodes`). `tau` is the target interpolation accuracy (`psi_accuracy`); +`kin` optionally supplies kinetic profiles whose pedestal gradients attract knots; +`mandatory` lists ψ values that must appear as knots (e.g. rational surfaces). +""" +function refined_psi_grid(equil::PlasmaEquilibrium; + tau::Float64, + kin::Union{Nothing,KineticProfileSplines}=nothing, + mandatory::Vector{Float64}=Float64[], + delta_frac::Float64=0.25, + N_cap::Int=1024) + xs = equil.profiles.xs + rho = _knot_density(equil; tau, kin) + M_total = sum(0.5 * (rho[i] + rho[i-1]) * (xs[i] - xs[i-1]) for i in 2:length(xs)) + N = clamp(ceil(Int, M_total), 32, N_cap) + N == N_cap && M_total > N_cap && + @warn "refined_psi_grid: knot count capped at $N_cap (density integral wants $(ceil(Int, M_total))); psi_accuracy=$tau may not be attainable" + grid = _equidistribute(xs, rho, N) + return merge_mandatory_nodes(grid, mandatory; delta_frac) +end + +""" + implied_knot_count(equil::PlasmaEquilibrium; tau, kin=nothing) -> Int + +Knot count the measured-curvature density model implies for a formed equilibrium. Used as +a post-refinement consistency diagnostic: if this differs substantially from the actual +grid size, the pass-1 grid under- or over-sampled a feature. +""" +function implied_knot_count(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing) + xs = equil.profiles.xs + rho = _knot_density(equil; tau, kin) + return ceil(Int, sum(0.5 * (rho[i] + rho[i-1]) * (xs[i] - xs[i-1]) for i in 2:length(xs))) +end diff --git a/src/Equilibrium/InverseEquilibrium.jl b/src/Equilibrium/InverseEquilibrium.jl index 0447933f..42bb598b 100644 --- a/src/Equilibrium/InverseEquilibrium.jl +++ b/src/Equilibrium/InverseEquilibrium.jl @@ -139,40 +139,22 @@ function equilibrium_solver(input::InverseRunInput; override_psi_nodes::Union{No sq_xs = _validate_psi_nodes(override_psi_nodes, psilow, psihigh) mpsi = length(sq_xs) - 1 elseif grid_type == "log_asymptotic" - n_core_mid_edge = nothing if mpsi == 0 && config.psi_accuracy > 0 - # Estimate A from q profile near psihigh (q is column 3 in sq_in) - ε = max(1.0 - psihigh, 0.001) - ψ₁ = clamp(1.0 - 3ε, psilow + 0.01, 0.999) - ψ₂ = clamp(1.0 - 1.5ε, psilow + 0.01, 0.999) - A = try - buf = zeros(size(sq_in.y, 2)) - sq_in(buf, ψ₁) - q1 = buf[3] - sq_in(buf, ψ₂) - q2 = buf[3] - max(abs(q2 - q1) / log(2), 0.1) - catch - @warn "Could not estimate log slope from q profile, using default A=2.0" - 2.0 - end - n_core_mid_edge = make_optimal_mpsi(psilow, psihigh, A, sq_in; tau=config.psi_accuracy) - mpsi = sum(n_core_mid_edge) + # Two-pass auto grid: coarse pass-1 layout; the driver refines and re-forms + # on the measured-curvature grid (GridRefinement.jl). + mpsi = 128 + @info "Auto psi grid: forming pass-1 equilibrium on coarse $(mpsi)-interval log_asymptotic grid pending curvature-based refinement" elseif mpsi == 0 mpsi = 128 end - sq_xs = if n_core_mid_edge !== nothing - make_optimal_psi_grid(psilow, psihigh, n_core_mid_edge...) - else - log_core = log(0.03 / psilow) - log_mid = log(0.98 / 0.03) - log_edge = log((1.0 - 0.98) / (1.0 - psihigh)) - log_total = log_core + log_mid + log_edge - N_edge = clamp(round(Int, mpsi * log_edge / log_total), 2, mpsi ÷ 2) - N_core = round(Int, mpsi * log_core / log_total) - N_mid = mpsi - N_edge - N_core - make_optimal_psi_grid(psilow, psihigh, N_core, N_mid, N_edge) - end + log_core = log(0.03 / psilow) + log_mid = log(0.98 / 0.03) + log_edge = log((1.0 - 0.98) / (1.0 - psihigh)) + log_total = log_core + log_mid + log_edge + N_edge = clamp(round(Int, mpsi * log_edge / log_total), 2, mpsi ÷ 2) + N_core = round(Int, mpsi * log_core / log_total) + N_mid = mpsi - N_edge - N_core + sq_xs = make_optimal_psi_grid(psilow, psihigh, N_core, N_mid, N_edge) elseif grid_type == "ldp" if mpsi == 0 mpsi = 128 From 7bbc3de38b0eae6b192b6e2421e2f98f44c27573 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:13:45 -0400 Subject: [PATCH 03/16] ForceFreeStates - REFACTOR - Extract stateless rational-surface core; add rational_psi_nodes The qextrema-interval Brent walk moves from sing_find! into _find_rational_surfaces, which returns (m, n, psifac) tuples; sing_find! rebuilds its SingType multiplicity bookkeeping on top. rational_psi_nodes exposes the unique surface locations for the two-pass grid refinement. Co-Authored-By: Claude Fable 5 --- src/ForceFreeStates/Sing.jl | 87 ++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/src/ForceFreeStates/Sing.jl b/src/ForceFreeStates/Sing.jl index a45b6664..211e6f30 100644 --- a/src/ForceFreeStates/Sing.jl +++ b/src/ForceFreeStates/Sing.jl @@ -1,16 +1,17 @@ """ - sing_find!(intr::ForceFreeStatesInternal, equil::Equilibrium.PlasmaEquilibrium) + _find_rational_surfaces(equil::Equilibrium.PlasmaEquilibrium, nlow::Int, nhigh::Int) -Locate singular rational q-surfaces (q = m/nn) using a bisection method -between extrema of the q-profile, and store their properties in `intr.sing`. -Performs the same function as `sing_find` in the Fortran code. +Locate all rational q-surfaces q = m/n for n in `nlow:nhigh` by Brent bisection between +consecutive extrema of the q-profile (reverse shear gives one root per monotone segment). +Returns a vector of `(m, n, psifac)` named tuples in discovery order (n outer, ψ-interval +inner). Requires `equilibrium_qfind!` to have populated `equil.params.qextrema_*`. """ -function sing_find!(intr::ForceFreeStatesInternal, equil::Equilibrium.PlasmaEquilibrium) - +function _find_rational_surfaces(equil::Equilibrium.PlasmaEquilibrium, nlow::Int, nhigh::Int) profiles = equil.profiles + surfaces = @NamedTuple{m::Int, n::Int, psifac::Float64}[] # Loop over all toroidal mode numbers - for n in intr.nlow:intr.nhigh + for n in nlow:nhigh hint = Ref(1) # Loop over extrema of q, find all rational values in between for iex in 2:equil.params.mextrema @@ -25,30 +26,66 @@ function sing_find!(intr::ForceFreeStatesInternal, equil::Equilibrium.PlasmaEqui while (m - n * equil.params.qextrema_q[iex-1]) * (m - n * equil.params.qextrema_q[iex]) <= 0 psi0 = equil.params.qextrema_psi[iex-1] psi1 = equil.params.qextrema_psi[iex] - psifac = (psi0 + psi1) / 2 # initial guess for bisection psifac = find_zero(psi -> m - n * profiles.q_spline(psi; hint=hint), (psi0, psi1), Roots.Brent()) - - if any(s -> isapprox(s.q, m / n; atol=1e-8), intr.sing) - # Rational surface with multiplicity > 1, add this m,n to the resonant mode numbers - # Technically only need m or n, but simplifies some later code and cheap to store both - push!(intr.sing[findfirst(s -> isapprox(s.q, m / n; atol=1e-8), intr.sing)].m, m) - push!(intr.sing[findfirst(s -> isapprox(s.q, m / n; atol=1e-8), intr.sing)].n, n) - else - push!(intr.sing, SingType(; - m=[m], - n=[n], - psifac=psifac, - rho=sqrt(psifac), - q=m / n, - q1=profiles.q_deriv(psifac; hint=hint) - )) - intr.msing += 1 - end + push!(surfaces, (m=m, n=n, psifac=psifac)) m += dm end end end + return surfaces +end + +""" + rational_psi_nodes(equil::Equilibrium.PlasmaEquilibrium; nlow::Int, nhigh::Int=nlow) + +Unique ψ_N locations of all rational surfaces q = m/n for n in `nlow:nhigh`, sorted +increasing. Used as mandatory knots for the two-pass equilibrium grid refinement (the +same physical surface reached through several (m, n) pairs is deduplicated by q value). +""" +function rational_psi_nodes(equil::Equilibrium.PlasmaEquilibrium; nlow::Int, nhigh::Int=nlow) + surfaces = _find_rational_surfaces(equil, nlow, nhigh) + nodes = Float64[] + qs = Float64[] + for s in surfaces + any(q -> isapprox(q, s.m / s.n; atol=1e-8), qs) && continue + push!(qs, s.m / s.n) + push!(nodes, s.psifac) + end + return sort!(nodes) +end + +""" + sing_find!(intr::ForceFreeStatesInternal, equil::Equilibrium.PlasmaEquilibrium) + +Locate singular rational q-surfaces (q = m/nn) using a bisection method +between extrema of the q-profile, and store their properties in `intr.sing`. +Performs the same function as `sing_find` in the Fortran code. +""" +function sing_find!(intr::ForceFreeStatesInternal, equil::Equilibrium.PlasmaEquilibrium) + profiles = equil.profiles + hint = Ref(1) + + for s in _find_rational_surfaces(equil, intr.nlow, intr.nhigh) + m, n, psifac = s.m, s.n, s.psifac + if any(sg -> isapprox(sg.q, m / n; atol=1e-8), intr.sing) + # Rational surface with multiplicity > 1, add this m,n to the resonant mode numbers + # Technically only need m or n, but simplifies some later code and cheap to store both + idx = findfirst(sg -> isapprox(sg.q, m / n; atol=1e-8), intr.sing) + push!(intr.sing[idx].m, m) + push!(intr.sing[idx].n, n) + else + push!(intr.sing, SingType(; + m=[m], + n=[n], + psifac=psifac, + rho=sqrt(psifac), + q=m / n, + q1=profiles.q_deriv(psifac; hint=hint) + )) + intr.msing += 1 + end + end # Sort singular surfaces by increasing ψ intr.sing = sort(intr.sing; by=s -> s.psifac) end From 9c3f944263b2294d9e488917d8afff582407f59a Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:53:15 -0400 Subject: [PATCH 04/16] EQUIL - IMPROVEMENT - Derivative-error density in rho-space with rational-surface packing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validation on the DIIID-like example drove four corrections to the knot density model: - h^3 derivative error model (err(f') ~ h^3|f''''|/24) replaces the h^4 value model: the stability physics consumes spline derivatives (q' at rational surfaces, p' and V' in the EL and ballooning coefficients), and the value model under-resolved delta-prime by 2x at q=2. - Curvature is measured against rho = sqrt(psi), where the equilibrium is regular at the axis; the psi-space geometry channels diverge as psi^(k/2-4) there and made the implied knot count grow without bound under refinement. - Local packing around mandatory (rational) surfaces: spacing 0.06*tau^(1/3) at the surface with geometric growth, within radius 0.05 — converges the delta-prime BVP, which samples the psi-splined coefficient matrices around each singular surface. - The core below psi=0.03 uses the a-priori geometric density exclusively: nodal data on the smallest flux surfaces is dominated by integration and axis-extrapolation error. Near-duplicate mandatory nodes (same surface via different m,n) collapse onto one knot. Noise floor scales as eps/h^4 on tightly packed sample grids. Adds test/runtests_grid_refinement.jl covering the merge invariants, equidistribution, quartic-exact divided differences, layer concentration, tau^(-1/3) scaling, and a Solovev two-pass round-trip. Co-Authored-By: Claude Fable 5 --- src/Equilibrium/GridRefinement.jl | 148 +++++++++++++++++++----------- test/runtests.jl | 1 + test/runtests_grid_refinement.jl | 130 ++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 55 deletions(-) create mode 100644 test/runtests_grid_refinement.jl diff --git a/src/Equilibrium/GridRefinement.jl b/src/Equilibrium/GridRefinement.jl index 6085719d..85668614 100644 --- a/src/Equilibrium/GridRefinement.jl +++ b/src/Equilibrium/GridRefinement.jl @@ -7,22 +7,40 @@ kinetic profiles) and equidistributes it, pinning mandatory knots (e.g. rational via `merge_mandatory_nodes`. Pass 2 re-forms the equilibrium on the refined grid through the `override_psi_nodes` keyword of `setup_equilibrium`. -The density uses the cubic-interpolation error model err ≈ h⁴|f''''|/384, so the knot -count of every region scales as psi_accuracy^(-1/4) and tightening the tolerance refines -all regions proportionally. Fourth derivatives are estimated by divided differences on the -pass-1 nodes only — nodal values come from independent field-line integrations and are -immune to inter-knot spline ringing. +The density uses the cubic *derivative* interpolation error model err(f′) ≈ h³|f''''|/24 — +the stability physics consumes spline derivatives (q′ at rational surfaces, p′ and V′ in +the Euler-Lagrange and ballooning coefficients), whose error at a given spacing is far +larger than the value error. Every region's knot count then scales as +psi_accuracy^(-1/3), so tightening the tolerance refines edge, pedestal, and mid +proportionally. Fourth derivatives are estimated by divided differences on the pass-1 +nodes only — nodal values come from independent field-line integrations and are immune to +inter-knot spline ringing. + +Curvature is measured with respect to ρ = √ψ_N, in which the equilibrium is regular at +the magnetic axis (R−R₀ ~ √ψ_N makes the geometry channels behave as ψ_N^(k/2) there, so +their ψ-derivatives diverge but their ρ-derivatives do not); the resulting ρ-spacing maps +back to the ψ density through dψ/dρ = 2√ψ_N, giving natural √ψ core packing. """ -# Cubic spline interpolation error constant: err ≈ h⁴|f''''|/384 -const CUBIC_ERR_CONST = 384.0 -# |f''''|/f_scale below this is treated as integrator noise, not curvature +# Cubic spline derivative interpolation error constant: err(f′) ≈ h³|f''''|/24 +const CUBIC_DERIV_ERR_CONST = 24.0 +# |f''''|/f_scale below this is treated as integrator noise, not curvature. The absolute +# floor covers well-spaced grids; a relative-noise term ε/h⁴ dominates where the sample +# grid is tightly packed, since divided differences amplify per-node noise as h⁻⁴ there +# (a-priori floors carry those regions instead). const D4_NOISE_FLOOR = 1e3 +const NODE_NOISE_REL = 1e-8 # Per-quantity target spacing clamp (ψ_N units) const H_TARGET_MIN = 1e-4 const H_TARGET_MAX = 0.2 # θ-lines subsample stride for the 2D geometry channels const THETA_STRIDE = 8 +# Local packing around mandatory (rational) surfaces: knot spacing h_s = coef·τ^(1/3) at +# the surface, geometric growth away from it, applied within the given radius. The Δ' +# asymptotic matching samples the ψ-splined coefficient matrices around each singular +# surface, so their local interpolation error controls Δ' convergence there. +const SING_PACK_COEF = 0.06 +const SING_PACK_RADIUS = 0.05 """ wants_two_pass(config::EquilibriumConfig) -> Bool @@ -76,93 +94,98 @@ function _fourth_derivative_nodes(xs::AbstractVector{<:Real}, ys::AbstractVector end """ - _density_from_curvature!(rho, xs, d4, f_scale, tau) + _density_from_curvature!(rho, d4, f_scale, tau; xs=nothing) -Accumulate (in place, by max) the knot density implied by the h⁴ error model for one -quantity: ρ = 1/h_target with h_target = (384·τ·f_scale/|f''''|)^(1/4), clamped to -[`H_TARGET_MIN`, `H_TARGET_MAX`]. Normalized curvature below `D4_NOISE_FLOOR` is ignored. +Accumulate (in place, by max) the knot density implied by the h³ derivative error model +for one quantity: ρ = 1/h_target with h_target = (24·τ·f_scale/|f''''|)^(1/3), clamped to +[`H_TARGET_MIN`, `H_TARGET_MAX`]. Normalized curvature below the noise floor is ignored; +when `xs` is given the floor grows as `NODE_NOISE_REL`/h_local⁴ on tightly packed sample +grids, where divided differences amplify nodal noise. """ -function _density_from_curvature!(rho::Vector{Float64}, d4::Vector{Float64}, f_scale::Float64, tau::Float64) - @inbounds for i in eachindex(rho) +function _density_from_curvature!(rho::Vector{Float64}, d4::Vector{Float64}, f_scale::Float64, tau::Float64; + xs::Union{Nothing,Vector{Float64}}=nothing) + n = length(rho) + @inbounds for i in 1:n + noise_floor = D4_NOISE_FLOOR + if xs !== nothing + h_loc = (xs[min(i + 2, n)] - xs[max(i - 2, 1)]) / 4 + noise_floor = max(noise_floor, NODE_NOISE_REL / h_loc^4) + end d4n = d4[i] / f_scale - d4n < D4_NOISE_FLOOR && continue - h = clamp((CUBIC_ERR_CONST * tau / d4n)^0.25, H_TARGET_MIN, H_TARGET_MAX) + d4n < noise_floor && continue + h = clamp((CUBIC_DERIV_ERR_CONST * tau / d4n)^(1 / 3), H_TARGET_MIN, H_TARGET_MAX) rho[i] = max(rho[i], 1.0 / h) end return rho end """ - _measured_edge_log_slope(xs, q_vals; nk=5) -> Float64 - -Estimate the separatrix log slope A of q ≈ −A·ln(1−ψ) from the outermost pass-1 q nodes. -Returns the fallback A=2.0 when the estimate is non-finite or non-positive (e.g. reversed -edge shear); analytic equilibria with finite edge q give a genuinely small A and hence a -weak edge floor. -""" -function _measured_edge_log_slope(xs::Vector{Float64}, q_vals::AbstractVector{<:Real}; nk::Int=5) - n = length(xs) - n < nk + 1 && return 2.0 - j = n - nk - denom = log((1.0 - xs[j]) / (1.0 - xs[n])) - A = (q_vals[n] - q_vals[j]) / denom - return (isfinite(A) && A > 0) ? A : 2.0 -end - -""" - _knot_density(equil::PlasmaEquilibrium; tau, kin=nothing) -> Vector{Float64} + _knot_density(equil::PlasmaEquilibrium; tau, kin=nothing, mandatory=Float64[]) -> Vector{Float64} Knot density ρ(ψ) (knots per unit ψ_N) at the pass-1 nodes `equil.profiles.xs`, combining by max: - - measured h⁴-model curvature of the 1D profiles F, P, dV/dψ, q; + - measured h³-derivative-model curvature of the 1D profiles F, P, dV/dψ, q; - curvature of the four rzphi geometry channels along every `THETA_STRIDE`-th θ-line (max over θ) — the bicubic ψ-axis shares these knots, so geometry sharpness must attract knots just like the 1D profiles; - curvature of the kinetic profiles n_i, n_e, T_i, T_e, ω_E when `kin` is given (their own grid, rebroadcast by linear interpolation) — steep pedestal gradients in the kinetic data pull knots into the pedestal; - - a-priori floors: geometric-in-log(1−ψ) at the edge with slope A measured from the - pass-1 q nodes, geometric-in-log(ψ) in the core, and a global minimum density. + - a-priori floors: geometric-in-log(1−ψ) at the edge and geometric-in-log(ψ) in the + core, plus a global minimum density. For the separatrix asymptote q ≈ −A·ln(1−ψ) + (q′ = A/(1−ψ)), geometric packing with dlog = (4τ)^(1/3) gives uniform *relative* q′ + error dlog³/4 = τ independent of A — guarding pass-1 undersampling of the divergence + without over-packing equilibria whose edge q is finite; + - local packing around each `mandatory` (rational) surface: spacing `SING_PACK_COEF`·τ^(1/3) + at the surface with geometric growth away from it, within `SING_PACK_RADIUS`. A running max over ±1 node smooths single-stencil dropouts. """ -function _knot_density(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing) +function _knot_density(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing, + mandatory::Vector{Float64}=Float64[]) xs = equil.profiles.xs n = length(xs) - rho = zeros(Float64, n) + # Curvature is measured against ρ = √ψ (regular at the axis); the ρ-space density + # rho_r converts to the ψ density at the end via dρ/dψ = 1/(2√ψ). + rs = sqrt.(xs) + rho_r = zeros(Float64, n) # 1D profiles for spl in (equil.profiles.F_spline, equil.profiles.P_spline, equil.profiles.dVdpsi_spline, equil.profiles.q_spline) vals = spl.y f_scale = max(maximum(abs, vals), 1e-12) - _density_from_curvature!(rho, _fourth_derivative_nodes(xs, vals), f_scale, tau) + _density_from_curvature!(rho_r, _fourth_derivative_nodes(rs, vals), f_scale, tau; xs=rs) end - # 2D geometry channels: per-θ-line curvature along ψ, max over θ, plane-wide scale + # 2D geometry channels: per-θ-line curvature along ρ, max over θ, plane-wide scale for interp in (equil.rzphi_rsquared, equil.rzphi_offset, equil.rzphi_nu, equil.rzphi_jac) vals2d = @view interp.nodal_derivs.partials[1, :, :] f_scale = max(maximum(abs, vals2d), 1e-12) for itheta in 1:THETA_STRIDE:size(vals2d, 2) - _density_from_curvature!(rho, _fourth_derivative_nodes(xs, @view(vals2d[:, itheta])), f_scale, tau) + _density_from_curvature!(rho_r, _fourth_derivative_nodes(rs, @view(vals2d[:, itheta])), f_scale, tau; xs=rs) end end # Kinetic profiles on their own grid, rebroadcast onto xs if kin !== nothing + rs_kin = sqrt.(kin.xs) rho_kin = zeros(Float64, length(kin.xs)) for spl in (kin.ni_spline, kin.ne_spline, kin.Ti_spline, kin.Te_spline, kin.omegaE_spline) vals = spl.y f_scale = max(maximum(abs, vals), 1e-12) - _density_from_curvature!(rho_kin, _fourth_derivative_nodes(kin.xs, vals), f_scale, tau) + _density_from_curvature!(rho_kin, _fourth_derivative_nodes(rs_kin, vals), f_scale, tau; xs=rs_kin) end - kin_itp = cubic_interp(kin.xs, rho_kin; extrap=ExtendExtrap()) + kin_itp = cubic_interp(rs_kin, rho_kin; extrap=ExtendExtrap()) @inbounds for i in 1:n - (kin.xs[1] <= xs[i] <= kin.xs[end]) || continue - rho[i] = max(rho[i], kin_itp(xs[i])) + (rs_kin[1] <= rs[i] <= rs_kin[end]) || continue + rho_r[i] = max(rho_r[i], kin_itp(rs[i])) end end + # Convert the ρ-space density to ψ space: ρ_ψ(ψ) = ρ_ρ(ρ)·dρ/dψ = ρ_ρ/(2√ψ) + rho = [rho_r[i] / (2.0 * rs[i]) for i in 1:n] + # Running max over ±1 node rho_s = copy(rho) @inbounds for i in 1:n @@ -170,14 +193,28 @@ function _knot_density(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothin i < n && (rho_s[i] = max(rho_s[i], rho[i+1])) end - # A-priori floors: edge/core geometric packing and a global minimum - A = _measured_edge_log_slope(xs, equil.profiles.q_spline.y) - dlog = (13.0 * tau / A)^0.25 + # A-priori regions: geometric edge floor, and a pure a-priori geometric core — the + # nodal data of the smallest flux surfaces is dominated by integration and axis + # extrapolation error, so measured curvature is not trusted below the core split. + dlog = (4.0 * tau)^(1 / 3) @inbounds for i in 1:n - xs[i] >= 0.9 && (rho_s[i] = max(rho_s[i], 1.0 / (dlog * (1.0 - xs[i])))) - xs[i] <= 0.03 && (rho_s[i] = max(rho_s[i], 1.0 / (dlog * xs[i]))) + if xs[i] <= 0.03 + rho_s[i] = 1.0 / (dlog * xs[i]) + elseif xs[i] >= 0.9 + rho_s[i] = max(rho_s[i], 1.0 / (dlog * (1.0 - xs[i]))) + end rho_s[i] = max(rho_s[i], 1.0 / H_TARGET_MAX) end + + # Local packing around mandatory (rational) surfaces + h_s = max(SING_PACK_COEF * tau^(1 / 3), H_TARGET_MIN) + for psi_m in mandatory + @inbounds for i in 1:n + d = abs(xs[i] - psi_m) + d > SING_PACK_RADIUS && continue + rho_s[i] = max(rho_s[i], 1.0 / max(dlog * d, h_s)) + end + end return rho_s end @@ -275,7 +312,7 @@ function refined_psi_grid(equil::PlasmaEquilibrium; delta_frac::Float64=0.25, N_cap::Int=1024) xs = equil.profiles.xs - rho = _knot_density(equil; tau, kin) + rho = _knot_density(equil; tau, kin, mandatory) M_total = sum(0.5 * (rho[i] + rho[i-1]) * (xs[i] - xs[i-1]) for i in 2:length(xs)) N = clamp(ceil(Int, M_total), 32, N_cap) N == N_cap && M_total > N_cap && @@ -285,14 +322,15 @@ function refined_psi_grid(equil::PlasmaEquilibrium; end """ - implied_knot_count(equil::PlasmaEquilibrium; tau, kin=nothing) -> Int + implied_knot_count(equil::PlasmaEquilibrium; tau, kin=nothing, mandatory=Float64[]) -> Int Knot count the measured-curvature density model implies for a formed equilibrium. Used as a post-refinement consistency diagnostic: if this differs substantially from the actual grid size, the pass-1 grid under- or over-sampled a feature. """ -function implied_knot_count(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing) +function implied_knot_count(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing, + mandatory::Vector{Float64}=Float64[]) xs = equil.profiles.xs - rho = _knot_density(equil; tau, kin) + rho = _knot_density(equil; tau, kin, mandatory) return ceil(Int, sum(0.5 * (rho[i] + rho[i-1]) * (xs[i] - xs[i-1]) for i in 2:length(xs))) end diff --git a/test/runtests.jl b/test/runtests.jl index 2ae4125e..9bd7b0ed 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,6 +24,7 @@ else include("./runtests_fouriertransforms.jl") include("./runtests_vacuum.jl") include("./runtests_equil.jl") + include("./runtests_grid_refinement.jl") include("./runtests_coordinate_invariant.jl") include("./runtests_eulerlagrange.jl") include("./runtests_riccati.jl") diff --git a/test/runtests_grid_refinement.jl b/test/runtests_grid_refinement.jl new file mode 100644 index 00000000..0b1f6a26 --- /dev/null +++ b/test/runtests_grid_refinement.jl @@ -0,0 +1,130 @@ +using Test +using GeneralizedPerturbedEquilibrium +const GridRef = GeneralizedPerturbedEquilibrium.Equilibrium + +@testset "Grid Refinement" begin + + @testset "_fourth_derivative_nodes" begin + # Exact on a quartic over nonuniform nodes: f'''' = 24 everywhere + xs = [0.0, 0.1, 0.25, 0.33, 0.5, 0.62, 0.8, 0.93, 1.0] + d4 = GridRef._fourth_derivative_nodes(xs, xs .^ 4) + @test all(isapprox.(d4, 24.0; rtol=1e-8)) + + # Zero on a cubic (divided differences of order 4 vanish) + d4c = GridRef._fourth_derivative_nodes(xs, 2.0 .* xs .^ 3 .- xs) + @test all(abs.(d4c) .< 1e-8) + + # Fewer than 5 nodes: returns zeros + @test GridRef._fourth_derivative_nodes([0.0, 0.5, 1.0], [1.0, 2.0, 3.0]) == zeros(3) + end + + @testset "_equidistribute" begin + xs = collect(range(0.0, 1.0; length=101)) + + # Constant density -> uniform grid + nodes = GridRef._equidistribute(xs, fill(10.0, 101), 20) + @test length(nodes) == 21 + @test nodes[1] == 0.0 && nodes[end] == 1.0 + @test all(isapprox.(diff(nodes), 0.05; atol=1e-10)) + + # Localized density bump attracts a proportional share of the knots + rho = fill(1.0, 101) + rho[41:60] .= 100.0 # bump on [0.4, 0.6) + nodes = GridRef._equidistribute(xs, rho, 50) + @test all(diff(nodes) .> 0) + in_bump = count(x -> 0.4 <= x <= 0.6, nodes) + @test in_bump > 40 # bump carries ~95% of the density integral + end + + @testset "merge_mandatory_nodes" begin + grid = collect(range(0.0, 1.0; length=11)) # spacing 0.1, delta_min = 0.025 + + # Empty mandatory is the identity + @test GridRef.merge_mandatory_nodes(grid, Float64[]) == grid + + # Plain insertion away from existing nodes + merged = GridRef.merge_mandatory_nodes(grid, [0.55]) + @test 0.55 in merged + @test length(merged) == 12 + @test all(diff(merged) .> 0) + + # Snap: existing node within delta_min of the mandatory one is dropped + merged = GridRef.merge_mandatory_nodes(grid, [0.51]) + @test (0.51 in merged) && !(0.5 in merged) + @test length(merged) == 11 + + # Endpoints always win: out-of-span and near-endpoint mandatory nodes are discarded + merged = GridRef.merge_mandatory_nodes(grid, [-0.1, 0.0, 1.0, 1.2, 0.001, 0.999]) + @test merged == grid + + # Near-duplicate mandatory pair collapses to the first (same physical surface) + merged = GridRef.merge_mandatory_nodes(grid, [0.55, 0.55 + 1e-15]) + @test count(x -> abs(x - 0.55) < 0.01, merged) == 1 + @test minimum(diff(merged)) > 1e-3 + + # Distinct mandatory nodes farther apart than delta_min both survive + merged = GridRef.merge_mandatory_nodes(grid, [0.42, 0.47]) + @test (0.42 in merged) && (0.47 in merged) + @test all(diff(merged) .> 0) + end + + @testset "_validate_psi_nodes" begin + grid = collect(range(0.01, 0.99; length=10)) + @test GridRef._validate_psi_nodes(grid, 0.01, 0.99) === grid + @test_throws ErrorException GridRef._validate_psi_nodes(reverse(grid), 0.01, 0.99) + @test_throws ErrorException GridRef._validate_psi_nodes(grid, 0.02, 0.99) + @test_throws ErrorException GridRef._validate_psi_nodes(grid, 0.01, 0.995) + @test_throws ErrorException GridRef._validate_psi_nodes([0.5], 0.5, 0.5) + end + + @testset "density concentrates knots at a synthetic layer" begin + # tanh pedestal at psi=0.9 on a nonuniform grid; density must peak in the layer + xs = collect(range(0.01, 0.9995; length=201)) .^ 0.8 + sort!(xs) + vals = tanh.((xs .- 0.9) ./ 0.05) + rho = zeros(length(xs)) + GridRef._density_from_curvature!(rho, GridRef._fourth_derivative_nodes(xs, vals), 1.0, 3e-3) + # Density peaks inside the layer and vanishes (noise floor) far from it + layer = [0.8 <= x <= 0.99 for x in xs] + far = [x < 0.5 for x in xs] + @test maximum(rho[layer]) == maximum(rho) + @test maximum(rho[layer]) > 100.0 + @test maximum(rho[far]) == 0.0 + + # tau^(-1/3) scaling of the density (h^3 derivative error model); taus chosen so + # neither endpoint hits the H_TARGET_MIN/MAX clamps + rho_tight = zeros(length(xs)) + GridRef._density_from_curvature!(rho_tight, GridRef._fourth_derivative_nodes(xs, vals), 1.0, 3e-6) + ratio = maximum(rho_tight) / maximum(rho) + @test isapprox(ratio, 10.0; rtol=0.3) # (3e-3/3e-6)^(1/3) = 10 + end + + @testset "Solovev two-pass integration" begin + eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(; + eq_type="sol", grid_type="log_asymptotic", mpsi=0, psi_accuracy=1e-3, + psilow=0.01, psihigh=0.995) + sol_config = GeneralizedPerturbedEquilibrium.Equilibrium.SolovevConfig() + + @test GridRef.wants_two_pass(eq_config) + + equil1 = GridRef.setup_equilibrium(eq_config, sol_config) + + # Rational nodes land exactly on q = m/n surfaces + rat = GeneralizedPerturbedEquilibrium.ForceFreeStates.rational_psi_nodes(equil1; nlow=1, nhigh=2) + @test !isempty(rat) && issorted(rat) + for p in rat + q = equil1.profiles.q_spline(p) + @test isapprox(q, round(2q) / 2; atol=1e-6) # m/n with n in 1:2 is a half-integer + end + + grid = GridRef.refined_psi_grid(equil1; tau=eq_config.psi_accuracy, mandatory=rat) + @test all(diff(grid) .> 0) + @test grid[1] == eq_config.psilow && grid[end] == eq_config.psihigh + @test all(p -> p in grid, rat) + + # Pass 2 honors the refined grid exactly + equil2 = GridRef.setup_equilibrium(eq_config, sol_config; override_psi_nodes=grid) + @test equil2.profiles.xs == grid + @test equil2.rzphi_xs == grid + end +end From bd87fd8cb2164d0674f8864a58c12785b43b6ef2 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:53:15 -0400 Subject: [PATCH 05/16] GPEC - NEW FEATURE - Two-pass auto psi grid with rational-surface knots in main driver When grid_type=log_asymptotic with mpsi=0, the driver forms a coarse pass-1 equilibrium, pins knots on all rational surfaces in the requested n range, derives the refined grid from the measured curvature (including kinetic profiles when loaded), and re-forms from the in-memory input. The nn range validation and the kinetic-profile load are hoisted above equilibrium formation to feed the refinement; a consistency check warns when the refined equilibrium implies substantially more knots than used. Co-Authored-By: Claude Fable 5 --- src/GeneralizedPerturbedEquilibrium.jl | 134 +++++++++++++++---------- 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/src/GeneralizedPerturbedEquilibrium.jl b/src/GeneralizedPerturbedEquilibrium.jl index 51bd422b..c6c809e5 100755 --- a/src/GeneralizedPerturbedEquilibrium.jl +++ b/src/GeneralizedPerturbedEquilibrium.jl @@ -171,8 +171,89 @@ function main_from_inputs( ffs_table = inputs["ForceFreeStates"] _drop_deprecated_ffs_keys!(ffs_table) ctrl = ForceFreeStatesControl(; (Symbol(k) => v for (k, v) in ffs_table)...) + + # Determine toroidal mode numbers (n >= 1 required; 0 means "not specified"). + # Validated before equilibrium formation: the two-pass grid refinement needs the + # n range to pin rational-surface knots. + if ctrl.nn_low == 0 && ctrl.nn_high == 0 + error("Either nn_low or nn_high must be set in [ForceFreeStates] (both are 0)") + elseif ctrl.nn_low == 0 + ctrl.nn_low = ctrl.nn_high + elseif ctrl.nn_high == 0 + ctrl.nn_high = ctrl.nn_low + end + if ctrl.nn_low > ctrl.nn_high + error("nn_low=$(ctrl.nn_low) cannot be greater than nn_high=$(ctrl.nn_high)") + end + # checks for negative n + # note that negative n in fortran had code adding the identitiy matrix to grad Green for n=0 + # and some n, nu sign switching in vacuum but was not actually supported by DCON sing_find, etc. + if ctrl.nn_high < 1 + error("All requested toroidal modes (n=$(ctrl.nn_low):$(ctrl.nn_high)) are below 1; " * + "n < 1 modes are not supported") + end + if ctrl.nn_low < 1 + @warn "Clamping nn_low from $(ctrl.nn_low) to 1; n < 1 modes are not supported" + ctrl.nn_low = 1 + end + intr.nlow = ctrl.nn_low + intr.nhigh = ctrl.nn_high + intr.npert = intr.nhigh - intr.nlow + 1 + nstring = intr.npert == 1 ? "$(intr.nlow)" : "$(intr.nlow):$(intr.nhigh)" + equil = Equilibrium.setup_equilibrium(eq_config, additional_input) + # Build KineticForces control and load kinetic profiles once — reused by the grid + # refinement below, the stability kinetic callback (via `calculated_cb`), and the + # post-PE torque diagnostics block. The `"fixed"` kinetic source path in stability + # does not need kinetic_profiles, but the post-PE block always does, so we load + # whenever a [KineticForces] section is present or the stability path requests the + # calculated source. psio is invariant across grid re-formation. + kf_ctrl = + haskey(inputs, "KineticForces") ? + KineticForces.KineticForcesControl(; + (Symbol(k) => v for (k, v) in inputs["KineticForces"])...) : + KineticForces.KineticForcesControl() + + kinetic_profiles = nothing + needs_kinetic_profiles = haskey(inputs, "KineticForces") || + (ctrl.kinetic_factor > 0 && ctrl.kinetic_source == "calculated") + if needs_kinetic_profiles + kinetic_file = joinpath(intr.dir_path, kf_ctrl.kinetic_file) + kinetic_profiles = Equilibrium.load_kinetic_profiles( + kinetic_file; + zi=kf_ctrl.zi, zimp=kf_ctrl.zimp, + mi=kf_ctrl.mi, mimp=kf_ctrl.mimp, + density_factor=kf_ctrl.density_factor, temperature_factor=kf_ctrl.temperature_factor, + ExB_rotation_factor=kf_ctrl.ExB_rotation_factor, toroidal_rotation_factor=kf_ctrl.toroidal_rotation_factor, + chi1=2π * equil.psio) + end + + # Two-pass auto grid: measure the pass-1 equilibrium's curvature (profiles, geometry, + # kinetic profiles), pin knots on rational surfaces, and re-form on the refined grid + # from the in-memory input — no file re-read. + if Equilibrium.wants_two_pass(eq_config) + mandatory = ForceFreeStates.rational_psi_nodes(equil; nlow=ctrl.nn_low, nhigh=ctrl.nn_high) + psi_nodes = Equilibrium.refined_psi_grid(equil; + tau=eq_config.psi_accuracy, kin=kinetic_profiles, mandatory=mandatory) + rerun_input = if additional_input !== nothing + additional_input # analytic *Config, IMAS dd, or prebuilt RunInput — all re-formable + elseif equil.ingest isa Equilibrium.DirectIngest + Equilibrium.build_direct_from_ingest(eq_config, equil.ingest) + elseif equil.ingest isa Equilibrium.InverseIngest + Equilibrium.build_inverse_from_ingest(eq_config, equil.ingest) + else + nothing # fall back to re-reading the input file + end + equil = Equilibrium.setup_equilibrium(eq_config, rerun_input; override_psi_nodes=psi_nodes) + implied = Equilibrium.implied_knot_count(equil; tau=eq_config.psi_accuracy, kin=kinetic_profiles, mandatory) + if implied > 1.5 * (length(psi_nodes) - 1) + @warn "Two-pass psi grid: refined equilibrium implies $implied knots vs $(length(psi_nodes) - 1) used — " * + "pass 1 may have under-sampled a feature; consider tightening psi_accuracy" + end + @info "Two-pass psi grid: $(length(psi_nodes)) knots, $(length(mandatory)) rational surfaces pinned (n=$nstring)" + end + @info "Equilibrium construction completed in $(@sprintf("%.3f", time() - equil_start)) s" # Early exit if user only requested equilibrium setup @@ -253,33 +334,6 @@ function main_from_inputs( # Fit data to splines intr.locstab = cubic_interp(profiles_xs, Series(locstab_fs); extrap=ExtendExtrap()) - # Determine toroidal mode numbers (n >= 1 required; 0 means "not specified") - if ctrl.nn_low == 0 && ctrl.nn_high == 0 - error("Either nn_low or nn_high must be set in [ForceFreeStates] (both are 0)") - elseif ctrl.nn_low == 0 - ctrl.nn_low = ctrl.nn_high - elseif ctrl.nn_high == 0 - ctrl.nn_high = ctrl.nn_low - end - if ctrl.nn_low > ctrl.nn_high - error("nn_low=$(ctrl.nn_low) cannot be greater than nn_high=$(ctrl.nn_high)") - end - # checks for negative n - # note that negative n in fortran had code adding the identitiy matrix to grad Green for n=0 - # and some n, nu sign switching in vacuum but was not actually supported by DCON sing_find, etc. - if ctrl.nn_high < 1 - error("All requested toroidal modes (n=$(ctrl.nn_low):$(ctrl.nn_high)) are below 1; " * - "n < 1 modes are not supported") - end - if ctrl.nn_low < 1 - @warn "Clamping nn_low from $(ctrl.nn_low) to 1; n < 1 modes are not supported" - ctrl.nn_low = 1 - end - intr.nlow = ctrl.nn_low - intr.nhigh = ctrl.nn_high - intr.npert = intr.nhigh - intr.nlow + 1 - nstring = intr.npert == 1 ? "$(intr.nlow)" : "$(intr.nlow):$(intr.nhigh)" - # Find all singular surfaces in the equilibrium sing_find!(intr, equil) @@ -328,32 +382,6 @@ function main_from_inputs( intr.mpert = intr.mhigh - intr.mlow + 1 intr.numpert_total = intr.mpert * intr.npert - # Build KineticForces control and load kinetic profiles once — reused by - # both the stability kinetic callback (via `calculated_cb` below) and the - # post-PE torque diagnostics block. The `"fixed"` kinetic source path in - # stability does not need kinetic_profiles, but the post-PE block always - # does, so we load whenever a [KineticForces] section is present or the - # stability path requests the calculated source. - kf_ctrl = - haskey(inputs, "KineticForces") ? - KineticForces.KineticForcesControl(; - (Symbol(k) => v for (k, v) in inputs["KineticForces"])...) : - KineticForces.KineticForcesControl() - - kinetic_profiles = nothing - needs_kinetic_profiles = haskey(inputs, "KineticForces") || - (ctrl.kinetic_factor > 0 && ctrl.kinetic_source == "calculated") - if needs_kinetic_profiles - kinetic_file = joinpath(intr.dir_path, kf_ctrl.kinetic_file) - kinetic_profiles = Equilibrium.load_kinetic_profiles( - kinetic_file; - zi=kf_ctrl.zi, zimp=kf_ctrl.zimp, - mi=kf_ctrl.mi, mimp=kf_ctrl.mimp, - density_factor=kf_ctrl.density_factor, temperature_factor=kf_ctrl.temperature_factor, - ExB_rotation_factor=kf_ctrl.ExB_rotation_factor, toroidal_rotation_factor=kf_ctrl.toroidal_rotation_factor, - chi1=2π * equil.psio) - end - # Fit equilibrium quantities to Fourier-spline functions. if ctrl.mat_flag || ctrl.ode_flag if ctrl.verbose From 97f02790a31d0db7e5058c3570d446173683f013 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:53:15 -0400 Subject: [PATCH 06/16] ForceFreeStates - IMPROVEMENT - Default psi window for ballooning alpha-boundary scan drivers The alpha-boundary drivers now scan only psi_N in [0.1, min(0.99, psi_edge)], reusing the existing NaN sentinel for skipped surfaces. Ballooning boundaries are physically relevant in the mid-radius and pedestal; the packed axis and far-edge surfaces dominated the scan cost. The locstab path (compute_ballooning_stability!) is unchanged. Co-Authored-By: Claude Fable 5 --- src/ForceFreeStates/Ballooning.jl | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ForceFreeStates/Ballooning.jl b/src/ForceFreeStates/Ballooning.jl index ebdfb768..d24f9ff8 100644 --- a/src/ForceFreeStates/Ballooning.jl +++ b/src/ForceFreeStates/Ballooning.jl @@ -8,6 +8,13 @@ using StaticArrays: SVector const BALLOONING_THETA_MAX_CAP = 16.5 const BALLOONING_THETA_SCALE_MULTIPLIER = 10.0 const MATCHING_POINT = 1e-3 +# Default ψ_N window for the α-boundary scan drivers: ballooning boundaries matter in the +# mid-radius and pedestal; the packed axis and far-edge surfaces only add scan cost. +const BALLOONING_SCAN_PSI_WINDOW = (0.1, 0.99) + +# Effective window [0.1, min(0.99, ψ_edge)]; the grid never extends past psihigh/psi_edge. +_in_ballooning_scan_window(psi::Float64, psi_edge::Float64) = + BALLOONING_SCAN_PSI_WINDOW[1] <= psi <= min(BALLOONING_SCAN_PSI_WINDOW[2], psi_edge) """ compute_ballooning_stability!(ctrl, locstab_fs, plasma_eq) @@ -835,7 +842,10 @@ Use this (not [`ballooning_alpha_boundaries`](@ref)) when only the 1st boundary needed, e.g. a pedestal-stability constraint. `n_scan` sets the scan resolution; raise it (e.g. 64) on edge surfaces where Δ' poles can shift a coarse first crossing. -Per-surface failures and surfaces with no boundary within range are returned as `NaN`. +The scan is evaluated only inside `BALLOONING_SCAN_PSI_WINDOW` — ballooning boundaries +are physically relevant in the mid-radius and pedestal, and the tightly packed axis and +far-edge surfaces dominate the scan cost. Per-surface failures, skipped surfaces, and +surfaces with no boundary within range are returned as `NaN`. """ function ballooning_alpha_boundary( ctrl::ForceFreeStatesControl, @@ -850,7 +860,7 @@ function ballooning_alpha_boundary( alpha_critical = fill(NaN, npsi) Threads.@threads :greedy for i in 1:npsi - xs[i] > 1.0 && continue + _in_ballooning_scan_window(xs[i], xs[end]) || continue try alpha[i] = salpha_reference(i, plasma_eq).alpha_ref alpha_critical[i] = critical_ballooning_alpha(i, plasma_eq; theta_k=theta_k, n_scan=n_scan).alpha_crit @@ -897,7 +907,8 @@ boundary exists. `n_scan` sets the scan resolution of the crossing search; crossings closer together than the scan step can be missed, which shows up as isolated outliers on otherwise -smooth boundary curves. +smooth boundary curves. Surfaces outside `BALLOONING_SCAN_PSI_WINDOW` are skipped +(returned as `NaN`), like per-surface failures. """ function ballooning_alpha_boundaries( ctrl::ForceFreeStatesControl, @@ -914,7 +925,7 @@ function ballooning_alpha_boundaries( alpha_critical2 = fill(NaN, npsi) Threads.@threads :greedy for i in 1:npsi - xs[i] > 1.0 && continue + _in_ballooning_scan_window(xs[i], xs[end]) || continue try cr = ballooning_alpha_crossings(i, plasma_eq; theta_k=theta_k, max_alpha_scale=max_alpha_scale, n_scan=n_scan) alpha[i] = cr.reference.alpha_ref From 2db28150a71ffb60dabf7fd2020665e9c6cae47f Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:53:15 -0400 Subject: [PATCH 07/16] EQUIL - MINOR - Add q-vs-iota edge representation benchmark Quantifies knots-vs-accuracy for cubic splines of q versus iota = 1/q on the DIII-D-like example: iota gives a modest constant-factor improvement (~1.2-2x at coarse N) but the same convergence order, confirming that wholesale iota replacement is not warranted for grids ending inside the separatrix. Co-Authored-By: Claude Fable 5 --- benchmarks/benchmark_q_vs_iota_edge.jl | 108 +++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 benchmarks/benchmark_q_vs_iota_edge.jl diff --git a/benchmarks/benchmark_q_vs_iota_edge.jl b/benchmarks/benchmark_q_vs_iota_edge.jl new file mode 100644 index 00000000..e258fb45 --- /dev/null +++ b/benchmarks/benchmark_q_vs_iota_edge.jl @@ -0,0 +1,108 @@ +# Quantifies whether splining iota = 1/q instead of q would reduce the knot count needed +# for a given edge accuracy (GitHub discussion around the auto-grid redesign; PR #179 +# lineage owns any production iota work). For a grid ending at psihigh < 1, interpolation +# error transforms as err_q ≈ err_iota · q², cancelling the flattening of the profile, so +# no leading-order gain is expected — this script documents that with numbers on the +# DIII-D-like example equilibrium. +# +# Usage: julia --project=. benchmarks/benchmark_q_vs_iota_edge.jl +# Outputs (not committed): benchmarks/q_vs_iota_edge.csv, benchmarks/q_vs_iota_edge.png + +using Pkg; +Pkg.activate(joinpath(@__DIR__, "..")) +using GeneralizedPerturbedEquilibrium +using FastInterpolations +using Printf +using Plots + +const GPE = GeneralizedPerturbedEquilibrium +const EXAMPLE_DIR = joinpath(@__DIR__, "..", "examples", "DIIID-like_ideal_example") + +# Dense ldp reference equilibrium: treat its q(ψ) as ground truth +function reference_q() + _, eq_config, additional_input = GPE.build_inputs_from_toml(EXAMPLE_DIR) + eq_config.grid_type = "ldp" + eq_config.mpsi = 1024 + equil = GPE.Equilibrium.setup_equilibrium(eq_config, additional_input) + return equil, eq_config +end + +# Rational surface ψ_s(q = m/n) and q'(ψ_s) recovered from a fitted spline by bisection +function rational_locations(q_itp, dq_itp, psis, q_targets) + out = Dict{Float64,Tuple{Float64,Float64}}() + for qt in q_targets + lo, hi = psis[1], psis[end] + (q_itp(lo) - qt) * (q_itp(hi) - qt) > 0 && continue + for _ in 1:200 + mid = 0.5 * (lo + hi) + (q_itp(lo) - qt) * (q_itp(mid) - qt) <= 0 ? (hi = mid) : (lo = mid) + end + ps = 0.5 * (lo + hi) + out[qt] = (ps, dq_itp(ps)) + end + return out +end + +function main() + equil, eq_config = reference_q() + xs_ref = equil.profiles.xs + q_ref_itp = equil.profiles.q_spline + dq_ref_itp = equil.profiles.q_deriv + psilow, psihigh = xs_ref[1], xs_ref[end] + + q_lo = ceil(q_ref_itp(psilow)) + q_hi = floor(q_ref_itp(psihigh)) + q_targets = collect(q_lo:q_hi) # n=1 rationals + ref_rats = rational_locations(q_ref_itp, dq_ref_itp, xs_ref, q_targets) + + psi_eval = collect(range(psilow, psihigh; length=4001)) + edge_band = psi_eval .> 0.9 + + rows = String[] + push!(rows, "N,repr,max_rel_q_err,edge_rel_q_err,edge_rel_qprime_err,max_psi_s_err,max_rel_q1_err") + results = Dict{String,Vector{NTuple{2,Float64}}}("q" => [], "iota" => []) + + for N in (16, 32, 64, 128, 256) + # Same log_asymptotic knot layout the auto grid uses for fixed mpsi + cfg = deepcopy(eq_config) + cfg.mpsi = N + knots = GPE.Equilibrium._build_psi_grid(cfg, psilow, psihigh) + q_nodes = [q_ref_itp(p) for p in knots] + + for repr in ("q", "iota") + itp = repr == "q" ? cubic_interp(knots, q_nodes; extrap=ExtendExtrap()) : + cubic_interp(knots, 1.0 ./ q_nodes; extrap=ExtendExtrap()) + ditp = deriv1(itp) + qf = repr == "q" ? (p -> itp(p)) : (p -> 1.0 / itp(p)) + dqf = repr == "q" ? (p -> ditp(p)) : (p -> -ditp(p) / itp(p)^2) # q' = -ι'/ι² + + q_err = [abs(qf(p) - q_ref_itp(p)) / abs(q_ref_itp(p)) for p in psi_eval] + dq_err = [abs(dqf(p) - dq_ref_itp(p)) / max(abs(dq_ref_itp(p)), 1e-10) for p in psi_eval] + rats = rational_locations(qf, dqf, knots, q_targets) + psi_s_err = maximum(abs(rats[qt][1] - ref_rats[qt][1]) for qt in keys(ref_rats); init=0.0) + q1_err = maximum(abs(rats[qt][2] - ref_rats[qt][2]) / abs(ref_rats[qt][2]) for qt in keys(ref_rats); init=0.0) + + push!(rows, @sprintf("%d,%s,%.3e,%.3e,%.3e,%.3e,%.3e", + N, repr, maximum(q_err), maximum(q_err[edge_band]), maximum(dq_err[edge_band]), psi_s_err, q1_err)) + push!(results[repr], (Float64(N), maximum(q_err[edge_band]))) + end + end + + csv_path = joinpath(@__DIR__, "q_vs_iota_edge.csv") + open(csv_path, "w") do io + foreach(r -> println(io, r), rows) + end + println("Wrote ", abspath(csv_path)) + foreach(println, rows) + + p = plot(; xscale=:log10, yscale=:log10, xlabel="knots N", ylabel="max relative q error, ψ > 0.9", + title="q-spline vs ι-spline edge accuracy", legend=:topright, left_margin=12Plots.mm, bottom_margin=4Plots.mm) + for (repr, pts) in results + plot!(p, first.(pts), last.(pts); marker=:circle, lw=2, label="$repr-spline") + end + png_path = joinpath(@__DIR__, "q_vs_iota_edge.png") + savefig(p, png_path) + println("Wrote ", abspath(png_path)) +end + +main() From 84e88273d4700cb796fd39e3a1f08aa7f5515e65 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:53:16 -0400 Subject: [PATCH 08/16] EQUIL - MINOR - Document two-pass auto grid (docstrings, equilibrium.md, example annotations) Co-Authored-By: Claude Fable 5 --- docs/src/equilibrium.md | 46 +++++++++++++------ .../gpec.toml | 4 +- .../gpec.toml | 4 +- examples/DIIID-like_ideal_example/gpec.toml | 4 +- .../DIIID-like_ideal_example_IMAS/gpec.toml | 2 +- examples/LAR_beta_scan/gpec.toml | 2 +- examples/LAR_epsilon_scan/gpec.toml | 2 +- examples/Solovev_ideal_example/gpec.toml | 2 +- examples/Solovev_ideal_example_3D/gpec.toml | 2 +- .../Solovev_ideal_example_multi_n/gpec.toml | 2 +- .../single_n_1/gpec.toml | 2 +- .../single_n_2/gpec.toml | 2 +- src/Equilibrium/EquilibriumTypes.jl | 10 ++-- .../gpec.toml | 2 +- .../gpec.toml | 2 +- .../gpec.toml | 2 +- .../gpec.toml | 2 +- .../gpec.toml | 2 +- 18 files changed, 57 insertions(+), 37 deletions(-) diff --git a/docs/src/equilibrium.md b/docs/src/equilibrium.md index eefd9983..379984e6 100644 --- a/docs/src/equilibrium.md +++ b/docs/src/equilibrium.md @@ -57,21 +57,37 @@ tuning is needed. ## Radial grid packing -The default `grid_type = "log_asymptotic"` uses a three-region grid that respects -the asymptotic behavior of q near both the magnetic axis and the separatrix: - -- **Core** (ψ < 0.15): geometric spacing in log(ψ) — handles the axis where profiles - behave as ψⁿ -- **Middle** (0.15 ≤ ψ ≤ 0.95): uniform spacing — preserves resolution through the - pedestal region -- **Edge** (ψ > 0.95): geometric spacing in log(1−ψ) — tracks the logarithmic - divergence q ~ −A·ln(1−ψ) near a diverted separatrix - -With `mpsi = 0` (the default), the number of radial knots is chosen automatically -from the `psi_accuracy` parameter (target absolute error in q). Two probe field-line -integrations near psihigh estimate the local log-slope A, and the knot count is set so -the cubic spline error stays below `psi_accuracy` throughout the domain. The legacy -`grid_type = "ldp"` (sin²-spaced) and explicit `mpsi` are still supported. +With `grid_type = "log_asymptotic"` and `mpsi = 0` (the defaults), the radial grid is +built by a **two-pass measured-curvature refinement** driven by the single accuracy knob +`psi_accuracy` (τ): + +1. **Pass 1** forms the equilibrium on a coarse three-region layout (geometric in + log(ψ) at the core, uniform in the middle, geometric in log(1−ψ) at the edge). +2. A knot density is derived from the *measured* pass-1 data using the cubic-spline + derivative error model err(f′) ≈ h³|f''''|/24 ≤ τ·|f|. Fourth derivatives are + estimated by divided differences on the pass-1 nodes only — nodal values come from + independent field-line integrations, so the estimate is immune to inter-knot spline + ringing. The density combines, by max: + - the 1D profiles F, P, dV/dψ, and q; + - the 2D geometry channels (r², η-offset, ν, Jacobian) along sampled θ-lines, since + the bicubic ψ-axis shares the same knots; + - the kinetic profiles (n, T, ω_E) when loaded, so steep pedestal gradients attract + knots; + - a-priori geometric floors at the core and edge (uniform relative q′ error against + the separatrix asymptote q ~ −A·ln(1−ψ), independent of A); + - local packing around every rational surface q = m/n in the requested n range, + which the main driver pins as mandatory knots. +3. The density integral is equidistributed into the knot vector, mandatory + rational-surface knots are inserted with a minimum-spacing snap guard, and + **pass 2** re-forms the equilibrium on the refined grid from the in-memory input + (no file re-read). + +Every region's knot count scales as τ^(-1/3), so tightening `psi_accuracy` refines +the core, pedestal, and edge proportionally. The legacy `grid_type = "ldp"` +(sin²-spaced), `"pow1"`, `"uniform"`, and explicit `mpsi > 0` (single-pass, fixed +layout) are still supported. Library users calling `setup_equilibrium` directly with +`mpsi = 0` receive the coarse pass-1 grid; use `refined_psi_grid` and the +`override_psi_nodes` keyword to apply the refinement manually. ## API Reference diff --git a/examples/DIIID-like_gal_resistive_example/gpec.toml b/examples/DIIID-like_gal_resistive_example/gpec.toml index 4a816e50..5e58f915 100644 --- a/examples/DIIID-like_gal_resistive_example/gpec.toml +++ b/examples/DIIID-like_gal_resistive_example/gpec.toml @@ -8,8 +8,8 @@ power_r = 0 # Major radius power exponent for Jacobian grid_type = "ldp" # Radial grid packing type (ldp = linear-derivative packing toward rationals) psilow = 1e-4 # Lower limit of normalized flux coordinate psihigh = 0.993 # Upper limit of normalized flux coordinate (0.993 stays clear of the separatrix; truncating at ≳0.998 is numerically unreasonable here) -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) -psi_accuracy = 0.001 # Target absolute error in q for auto-mpsi +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) +psi_accuracy = 0.001 # Target relative accuracy of splined profile derivatives for the auto grid mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-10 # Error tolerance for equilibrium solver diff --git a/examples/DIIID-like_gal_resistive_pe_example/gpec.toml b/examples/DIIID-like_gal_resistive_pe_example/gpec.toml index 66345e63..1fa209f0 100644 --- a/examples/DIIID-like_gal_resistive_pe_example/gpec.toml +++ b/examples/DIIID-like_gal_resistive_pe_example/gpec.toml @@ -8,8 +8,8 @@ power_r = 0 # Major radius power exponent for Jacobian grid_type = "ldp" # Radial grid packing type (ldp = linear-derivative packing toward rationals) psilow = 1e-4 # Lower limit of normalized flux coordinate psihigh = 0.993 # Upper limit of normalized flux coordinate (0.993 stays clear of the separatrix; truncating at ≳0.998 is numerically unreasonable here) -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) -psi_accuracy = 0.001 # Target absolute error in q for auto-mpsi +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) +psi_accuracy = 0.001 # Target relative accuracy of splined profile derivatives for the auto grid mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-10 # Error tolerance for equilibrium solver diff --git a/examples/DIIID-like_ideal_example/gpec.toml b/examples/DIIID-like_ideal_example/gpec.toml index ed99887a..54fa6cbb 100644 --- a/examples/DIIID-like_ideal_example/gpec.toml +++ b/examples/DIIID-like_ideal_example/gpec.toml @@ -10,8 +10,8 @@ jac_type = "hamada" # Coordinate system (hamada, pest, boozer, equal_ grid_type = "log_asymptotic" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 0 # Number of radial grid points (0 = auto-compute from psi_accuracy) -psi_accuracy = 0.001 # Target absolute error in q for auto-mpsi +mpsi = 0 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) +psi_accuracy = 0.001 # Target relative accuracy of splined profile derivatives for the auto grid mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-10 # Error tolerance for equilibrium solver diff --git a/examples/DIIID-like_ideal_example_IMAS/gpec.toml b/examples/DIIID-like_ideal_example_IMAS/gpec.toml index dbf4f872..72be8515 100644 --- a/examples/DIIID-like_ideal_example_IMAS/gpec.toml +++ b/examples/DIIID-like_ideal_example_IMAS/gpec.toml @@ -13,7 +13,7 @@ eq_filename = "from_dd" # Path to equilibrium file (placeholder: equilibr jac_type = "boozer" # Coordinate system (hamada, pest, boozer, equal_arc, park, other) psilow = 0.01 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points imas_cocos = 11 # Expected COCOS convention of the input IMAS dd (11 = IMAS standard) diff --git a/examples/LAR_beta_scan/gpec.toml b/examples/LAR_beta_scan/gpec.toml index c4d1f316..37ca130b 100644 --- a/examples/LAR_beta_scan/gpec.toml +++ b/examples/LAR_beta_scan/gpec.toml @@ -13,7 +13,7 @@ jac_type = "hamada" # Coordinate system (hamada, pest, boozer, equal_ grid_type = "ldp" # Radial grid packing type psilow = 0.01 # Lower limit of normalized poloidal flux psihigh = 0.995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 512 # Number of poloidal grid points # TJ-analytic equilibrium parameters (cf. R. Fitzpatrick, TJ code, https://github.com/rfitzp/TJ). diff --git a/examples/LAR_epsilon_scan/gpec.toml b/examples/LAR_epsilon_scan/gpec.toml index 0c8fceb3..e8d6bfbd 100644 --- a/examples/LAR_epsilon_scan/gpec.toml +++ b/examples/LAR_epsilon_scan/gpec.toml @@ -13,7 +13,7 @@ jac_type = "hamada" # Coordinate system (hamada, pest, boozer, equal_ grid_type = "ldp" # Radial grid packing type psilow = 0.01 # Lower limit of normalized poloidal flux psihigh = 0.995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 512 # Number of poloidal grid points # TJ-analytic equilibrium parameters (cf. R. Fitzpatrick, TJ code, https://github.com/rfitzp/TJ). diff --git a/examples/Solovev_ideal_example/gpec.toml b/examples/Solovev_ideal_example/gpec.toml index d9fc70d2..5dc4fbc9 100644 --- a/examples/Solovev_ideal_example/gpec.toml +++ b/examples/Solovev_ideal_example/gpec.toml @@ -9,7 +9,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/examples/Solovev_ideal_example_3D/gpec.toml b/examples/Solovev_ideal_example_3D/gpec.toml index 3d1caa78..db42a7b1 100644 --- a/examples/Solovev_ideal_example_3D/gpec.toml +++ b/examples/Solovev_ideal_example_3D/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/examples/Solovev_ideal_example_multi_n/gpec.toml b/examples/Solovev_ideal_example_multi_n/gpec.toml index b17ab043..745dcb07 100644 --- a/examples/Solovev_ideal_example_multi_n/gpec.toml +++ b/examples/Solovev_ideal_example_multi_n/gpec.toml @@ -9,7 +9,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/examples/Solovev_ideal_example_multi_n/single_n_1/gpec.toml b/examples/Solovev_ideal_example_multi_n/single_n_1/gpec.toml index 650cfe31..1ce1fc73 100644 --- a/examples/Solovev_ideal_example_multi_n/single_n_1/gpec.toml +++ b/examples/Solovev_ideal_example_multi_n/single_n_1/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/examples/Solovev_ideal_example_multi_n/single_n_2/gpec.toml b/examples/Solovev_ideal_example_multi_n/single_n_2/gpec.toml index e95f0cc9..14ea5d64 100644 --- a/examples/Solovev_ideal_example_multi_n/single_n_2/gpec.toml +++ b/examples/Solovev_ideal_example_multi_n/single_n_2/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 128 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 128 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/src/Equilibrium/EquilibriumTypes.jl b/src/Equilibrium/EquilibriumTypes.jl index 6fa0d9af..9bf1ca76 100644 --- a/src/Equilibrium/EquilibriumTypes.jl +++ b/src/Equilibrium/EquilibriumTypes.jl @@ -19,11 +19,15 @@ Bundles all necessary settings originally specified in the equil fortran namelis - `power_rc::Int` - Minor radius (rfac = √((R-R₀)²+(Z-Z₀)²)) power exponent for Jacobian - `r0exp::Float64` - Major radius normalization for CHEASE/EQDSK [m] - `b0exp::Float64` - On-axis toroidal field normalization for CHEASE/EQDSK [T] - - `grid_type::String` - Grid type for flux surface discretization ("log_asymptotic", "ldp", "pow1") + - `grid_type::String` - Grid type for flux surface discretization ("log_asymptotic", "ldp", "pow1", "uniform") - `psilow::Float64` - Lower limit of normalized flux coordinate - `psihigh::Float64` - Upper limit of normalized flux coordinate - - `mpsi::Int` - Number of radial grid points (0 = auto-compute from psi_accuracy) - - `psi_accuracy::Float64` - Target absolute error in q for auto-mpsi (used when mpsi=0 and grid_type="log_asymptotic") + - `mpsi::Int` - Number of radial grid intervals; 0 with grid_type="log_asymptotic" selects the + two-pass auto grid: the main driver forms a coarse pass-1 equilibrium, measures its curvature, + pins knots on rational surfaces, and re-forms on the refined grid. Standalone `setup_equilibrium` + callers get the coarse pass-1 grid unless they refine via `refined_psi_grid` + `override_psi_nodes`. + - `psi_accuracy::Float64` - Target relative accuracy τ of splined profile derivatives for the + two-pass auto grid (knot count scales as τ^(-1/3)) - `mtheta::Int` - Number of poloidal grid points - `newq0::Int` - Override for on-axis safety factor (0 = use input value) - `etol::Float64` - Error tolerance for equilibrium solver diff --git a/test/test_data/regression_solovev_ideal_example/gpec.toml b/test/test_data/regression_solovev_ideal_example/gpec.toml index e0b34677..f99079f3 100644 --- a/test/test_data/regression_solovev_ideal_example/gpec.toml +++ b/test/test_data/regression_solovev_ideal_example/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 16 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 16 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/test/test_data/regression_solovev_ideal_example_multi_n/gpec.toml b/test/test_data/regression_solovev_ideal_example_multi_n/gpec.toml index 02e3fcd1..d048740a 100644 --- a/test/test_data/regression_solovev_ideal_example_multi_n/gpec.toml +++ b/test/test_data/regression_solovev_ideal_example_multi_n/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 16 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 16 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/test/test_data/regression_solovev_kinetic_calculated/gpec.toml b/test/test_data/regression_solovev_kinetic_calculated/gpec.toml index 27224997..215c7d1e 100644 --- a/test/test_data/regression_solovev_kinetic_calculated/gpec.toml +++ b/test/test_data/regression_solovev_kinetic_calculated/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 16 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 16 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/test/test_data/regression_solovev_kinetic_example/gpec.toml b/test/test_data/regression_solovev_kinetic_example/gpec.toml index 8f3369c1..26396d75 100644 --- a/test/test_data/regression_solovev_kinetic_example/gpec.toml +++ b/test/test_data/regression_solovev_kinetic_example/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 16 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 16 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver diff --git a/test/test_data/regression_solovev_kinetic_multi_n/gpec.toml b/test/test_data/regression_solovev_kinetic_multi_n/gpec.toml index e318749f..b4244ef1 100644 --- a/test/test_data/regression_solovev_kinetic_multi_n/gpec.toml +++ b/test/test_data/regression_solovev_kinetic_multi_n/gpec.toml @@ -8,7 +8,7 @@ jac_type = "pest" # Coordinate system (hamada, pest, booze grid_type = "ldp" # Radial grid packing type psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux -mpsi = 16 # Number of radial grid points (0 = auto-compute from psi_accuracy) +mpsi = 16 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) mtheta = 256 # Number of poloidal grid points newq0 = 0 # Override for on-axis safety factor (0 = use input value) etol = 1e-7 # Error tolerance for equilibrium solver From 9ba8725de280b4e286865918c6ae3abe4d9083c5 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:58:30 -0400 Subject: [PATCH 09/16] TEST - IMPROVEMENT - Pin DIIID parallel-integration testsets to ldp/mpsi=256 grid These testsets target the bidirectional FM integration and STRIDE BVP machinery, so they now pin the radial grid instead of inheriting the example default (mpsi=0), which previously baked the defective one-pass auto grid into the pinned values and would otherwise move whenever the auto grid evolves. Re-pins et_par and the delta-prime diagonals to the ldp-256 values: q=2,3,4 real parts are grid-converged and pinned tightly; the near-separatrix q=5,6 entries are not grid-converged (value and sign vary O(1) between grids) and are now asserted finite and non-zero only. Co-Authored-By: Claude Fable 5 --- test/runtests_parallel_integration.jl | 65 ++++++++++++++------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/test/runtests_parallel_integration.jl b/test/runtests_parallel_integration.jl index 26afbbd6..e6df22a9 100644 --- a/test/runtests_parallel_integration.jl +++ b/test/runtests_parallel_integration.jl @@ -284,6 +284,11 @@ using TOML inputs["ForceFreeStates"]["verbose"] = false inputs["ForceFreeStates"]["use_parallel"] = use_parallel inputs["ForceFreeStates"]["write_outputs_to_HDF5"] = false + # Pin the grid: this testset targets the integration machinery, so it must not + # move when the auto-grid (two-pass, driver-orchestrated) evolves. The example + # ships mpsi=0, which standalone setup_equilibrium forms as the coarse pass-1 grid. + inputs["Equilibrium"]["grid_type"] = "ldp" + inputs["Equilibrium"]["mpsi"] = 256 intr = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesInternal(; dir_path=ex) ctrl = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesControl(; (Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...) @@ -309,15 +314,13 @@ using TOML et_par, intr_par = run_diiid(true) - # Parallel FM et[1] regression. The bidirectional fix gives et ≈ 1.5–1.6 with - # set_psilim_via_dmlim = true (production diverted convention; DIIID-like example - # sets it explicitly). With the previous default (false) this was ≈ 1.29. Single- - # point pinning of et_par is platform-sensitive at the few-percent level (BLAS - # variant / FP rounding through the BVP solve and outer-plasma Riccati pass shift - # the eigenvalue ~5-10 %), so we bracket the eigenvalue rather than pin a tight - # value. A true regression of the bidirectional assembly (et ≈ 1.29 or ≈ 2+) still - # fails this bracket loudly. - @test 1.4 < et_par < 1.7 + # Parallel FM et[1] regression on the pinned ldp/mpsi=256 grid, where the + # bidirectional assembly gives et ≈ 1.20. Single-point pinning of et_par is + # platform-sensitive at the few-percent level (BLAS variant / FP rounding through + # the BVP solve and outer-plasma Riccati pass shift the eigenvalue ~5-10 %), so we + # bracket the eigenvalue rather than pin a tight value. A true regression of the + # bidirectional assembly (an O(10%)+ energy error) still fails this bracket loudly. + @test 1.1 < et_par < 1.3 # Per-surface Δ' assertions removed (stub calculation; see Solovev testset # comment above). BVP Δ' matrix regression for DIIID-like is in the # `delta_prime_matrix — STRIDE BVP DIIID-like regression (large N)` testset. @@ -490,6 +493,11 @@ using TOML inputs["ForceFreeStates"]["verbose"] = false inputs["ForceFreeStates"]["use_parallel"] = true inputs["ForceFreeStates"]["write_outputs_to_HDF5"] = false + # Pin the grid: this testset targets the BVP assembly, so it must not move when + # the auto-grid (two-pass, driver-orchestrated) evolves. The example ships mpsi=0, + # which standalone setup_equilibrium forms as the coarse pass-1 grid. + inputs["Equilibrium"]["grid_type"] = "ldp" + inputs["Equilibrium"]["mpsi"] = 256 intr = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesInternal(; dir_path=ex) ctrl = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesControl(; (Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...) @@ -533,27 +541,24 @@ using TOML end # Pinned diagonal `delta_prime_matrix` values for the DIIID-like case (msing = 5), - # PEST3-convention self-response Δ' from the STRIDE BVP with vacuum coupling. - # Tolerances are split by entry magnitude / |Im|/|Re| ratio (audit V4): - # - dpm[1], dpm[2]: nearly-real entries (|Im|/|Re| < 0.02). Platform-stable; rtol=1e-2. - # - dpm[3]: complex entry with |Im| ≈ |Re| (both ~10). Modest FP sensitivity in the - # PEST3 cancellation. rtol=5e-2 catches sign/normalization regressions while - # accepting ~2-3% imaginary-part drift across BLAS variants. - # - dpm[4], dpm[5]: |Im| is highly sensitive to FP round-off in the PEST3 four-term - # cancellation (dp_raw entries can be 10⁴–10⁵× larger than the result). The - # imaginary part drifts by 2–5× across platforms even with `extended_precision_bvp=true`. - # Pin only the real part tightly; bracket |dpm| to catch sign/normalization errors. - # dpm[1], dpm[2] re-pinned when FourierCoefficients began dropping the duplicated θ=2π - # endpoint before the FFT (faithful Fortran fspline_fit_2, equil/fspline.f:293): the old - # un-trimmed FFT double-counted θ=0, biasing the metric DC coefficient. dpm[3]–dpm[5] - # shifted too but stay within their wider tolerances. - @test isapprox(dpm[1, 1], +8.672812e+00 + 2.139354e-02im; rtol=1e-2) - @test isapprox(dpm[2, 2], -3.890689e+00 - 5.121123e-02im; rtol=1e-2) - @test isapprox(dpm[3, 3], -9.137656e+00 + 7.704888e+00im; rtol=5e-2) - @test isapprox(real(dpm[4, 4]), +5.790777e+03; rtol=5e-2) - @test isapprox(real(dpm[5, 5]), -2.940021e+02; rtol=5e-2) - @test 1e3 < abs(dpm[4, 4]) < 1e5 # |dpm[4,4]| ≈ 6e3; catches sign/normalization errors - @test 1e2 < abs(dpm[5, 5]) < 1e3 # |dpm[5,5]| ≈ 3e2; catches sign/normalization errors + # PEST3-convention self-response Δ' from the STRIDE BVP with vacuum coupling, on + # the pinned ldp/mpsi=256 grid. Tolerances split by grid- and FP-sensitivity: + # - dpm[1]–dpm[3] (q=2,3,4): real parts are grid-converged (agree with denser + # ldp-512 and refined two-pass grids to a few %); pin Re at rtol=2e-2. The + # imaginary parts arise from the PEST3 four-term cancellation and drift in + # magnitude and sign with grid and BLAS variant; bound |Im| relative to |Re|. + # - dpm[4], dpm[5] (q=5,6 near-separatrix): not grid-converged — value and sign + # vary O(1) between ldp-256/512 and refined grids. Assert finite and non-zero + # only (the earlier tight pins tracked a single grid's noise, not physics). + @test isapprox(real(dpm[1, 1]), +8.787850e+00; rtol=2e-2) + @test isapprox(real(dpm[2, 2]), -3.883926e+00; rtol=2e-2) + @test isapprox(real(dpm[3, 3]), -1.021928e+01; rtol=5e-2) + @test abs(imag(dpm[1, 1])) < 0.05 * abs(real(dpm[1, 1])) + @test abs(imag(dpm[2, 2])) < 0.05 * abs(real(dpm[2, 2])) + for j in 4:5 + @test isfinite(dpm[j, j]) + @test abs(dpm[j, j]) > 1.0 + end end end From 0f0d9bba439157ced9fdc3ef3f70765ef5245947 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 19:11:54 -0400 Subject: [PATCH 10/16] EQUIL - MINOR - Physics-review robustness: positivity precondition in equidistribution, IMAS rerun note Co-Authored-By: Claude Fable 5 --- src/Equilibrium/GridRefinement.jl | 4 +++- src/GeneralizedPerturbedEquilibrium.jl | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Equilibrium/GridRefinement.jl b/src/Equilibrium/GridRefinement.jl index 85668614..1f5e3190 100644 --- a/src/Equilibrium/GridRefinement.jl +++ b/src/Equilibrium/GridRefinement.jl @@ -223,9 +223,11 @@ end Place N+1 nodes over [xs[1], xs[end]] by equidistributing the density integral M(ψ) = ∫ρ dψ (trapezoid on the sample nodes, piecewise-linear inversion). Endpoints are -exactly xs[1] and xs[end]; interior nodes are strictly increasing since ρ > 0. +exactly xs[1] and xs[end]; interior nodes are strictly increasing since ρ > 0, which is +a required precondition (a zero density would make M non-invertible). """ function _equidistribute(xs::Vector{Float64}, rho::Vector{Float64}, N::Int) + all(>(0), rho) || error("_equidistribute requires strictly positive density") n = length(xs) M = zeros(Float64, n) @inbounds for i in 2:n diff --git a/src/GeneralizedPerturbedEquilibrium.jl b/src/GeneralizedPerturbedEquilibrium.jl index c6c809e5..2350a08f 100755 --- a/src/GeneralizedPerturbedEquilibrium.jl +++ b/src/GeneralizedPerturbedEquilibrium.jl @@ -237,7 +237,10 @@ function main_from_inputs( psi_nodes = Equilibrium.refined_psi_grid(equil; tau=eq_config.psi_accuracy, kin=kinetic_profiles, mandatory=mandatory) rerun_input = if additional_input !== nothing - additional_input # analytic *Config, IMAS dd, or prebuilt RunInput — all re-formable + # Analytic *Config, IMAS dd, or prebuilt RunInput — all re-formable. The IMAS + # path re-runs read_imas, which must resolve the same psihigh both passes; + # _validate_psi_nodes errors loudly if it does not. + additional_input elseif equil.ingest isa Equilibrium.DirectIngest Equilibrium.build_direct_from_ingest(eq_config, equil.ingest) elseif equil.ingest isa Equilibrium.InverseIngest From d4af63825ae932fe536f1ad3d460e888afeb0e26 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 20:54:00 -0400 Subject: [PATCH 11/16] EQUIL - REFACTOR - Rename auto grid_type from log_asymptotic to auto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two-pass grid is a measured spline-derivative-error density with geometric floors, not a log-asymptotics model — q stays finite everywhere on the grid — so the old name misdescribed it. grid_type="auto" is the new default; "log_asymptotic" remains a working alias (two-pass when mpsi=0, the three-region log layout when mpsi>0), so no existing TOML breaks. Co-Authored-By: Claude Fable 5 --- docs/src/equilibrium.md | 9 ++++++++- examples/DIIID-like_ideal_example/gpec.toml | 2 +- src/Equilibrium/DirectEquilibrium.jl | 6 +++--- src/Equilibrium/EquilibriumTypes.jl | 8 +++++--- src/Equilibrium/GridRefinement.jl | 6 +++--- src/Equilibrium/InverseEquilibrium.jl | 2 +- test/runtests_grid_refinement.jl | 6 +++++- 7 files changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/src/equilibrium.md b/docs/src/equilibrium.md index 379984e6..01e72a6d 100644 --- a/docs/src/equilibrium.md +++ b/docs/src/equilibrium.md @@ -57,7 +57,7 @@ tuning is needed. ## Radial grid packing -With `grid_type = "log_asymptotic"` and `mpsi = 0` (the defaults), the radial grid is +With `grid_type = "auto"` and `mpsi = 0` (the defaults; `"log_asymptotic"` is a legacy alias), the radial grid is built by a **two-pass measured-curvature refinement** driven by the single accuracy knob `psi_accuracy` (τ): @@ -89,6 +89,13 @@ layout) are still supported. Library users calling `setup_equilibrium` directly `mpsi = 0` receive the coarse pass-1 grid; use `refined_psi_grid` and the `override_psi_nodes` keyword to apply the refinement manually. +The packing on the DIII-D-like example (n=1) compared to fixed `ldp` grids — note the +coarse spacing across the smooth mid-radius, the spacing dips at each rational surface, +and the core/pedestal/edge packing (`benchmarks/plot_grid_knot_placement.jl` regenerates +this figure): + +![Radial knot packing: auto two-pass vs ldp](assets/grid_knot_placement.png) + ## API Reference ```@autodocs diff --git a/examples/DIIID-like_ideal_example/gpec.toml b/examples/DIIID-like_ideal_example/gpec.toml index 54fa6cbb..e4f26f23 100644 --- a/examples/DIIID-like_ideal_example/gpec.toml +++ b/examples/DIIID-like_ideal_example/gpec.toml @@ -7,7 +7,7 @@ eq_filename = "TkMkr_D3Dlike_Hmode.geqdsk" # Path to equilibrium file eq_type = "efit" # Type of the input 2D equilibrium file jac_type = "hamada" # Coordinate system (hamada, pest, boozer, equal_arc, park, other) -grid_type = "log_asymptotic" # Radial grid packing type +grid_type = "auto" # Radial grid packing type ("auto" = two-pass measured-curvature grid) psilow = 1e-4 # Lower limit of normalized poloidal flux psihigh = 0.9995 # Upper limit of normalized poloidal flux mpsi = 0 # Number of radial grid intervals (0 = two-pass auto grid from psi_accuracy) diff --git a/src/Equilibrium/DirectEquilibrium.jl b/src/Equilibrium/DirectEquilibrium.jl index ccb002d4..02f18410 100644 --- a/src/Equilibrium/DirectEquilibrium.jl +++ b/src/Equilibrium/DirectEquilibrium.jl @@ -407,14 +407,14 @@ end Resolve `mpsi` and build `psi_nodes` for any supported `grid_type`. -For `"log_asymptotic"` with `mpsi=0` (the auto grid), this is the coarse pass-1 layout of +For `"auto"` (or the legacy alias `"log_asymptotic"`) with `mpsi=0`, this is the coarse pass-1 layout of the two-pass refinement — the main driver measures the formed equilibrium and re-forms on the `refined_psi_grid` result. Shared by `direct_fieldline_int` and `efit_by_inversion` solvers. """ function _build_psi_grid(equil_params, psilow, psihigh) mpsi = equil_params.mpsi - if equil_params.grid_type == "log_asymptotic" && mpsi == 0 && equil_params.psi_accuracy > 0 + if equil_params.grid_type in ("auto", "log_asymptotic") && mpsi == 0 && equil_params.psi_accuracy > 0 # Two-pass auto grid: this is the coarse pass-1 layout; the driver measures the # formed equilibrium's curvature and re-forms on a refined grid (GridRefinement.jl). mpsi = 128 @@ -423,7 +423,7 @@ function _build_psi_grid(equil_params, psilow, psihigh) mpsi = 128 end - psi_nodes = if equil_params.grid_type == "log_asymptotic" + psi_nodes = if equil_params.grid_type in ("auto", "log_asymptotic") # Distribute mpsi across the three regions by log-weights log_core = log(0.03 / psilow) log_mid = log(0.98 / 0.03) diff --git a/src/Equilibrium/EquilibriumTypes.jl b/src/Equilibrium/EquilibriumTypes.jl index 9bf1ca76..40dce718 100644 --- a/src/Equilibrium/EquilibriumTypes.jl +++ b/src/Equilibrium/EquilibriumTypes.jl @@ -19,10 +19,12 @@ Bundles all necessary settings originally specified in the equil fortran namelis - `power_rc::Int` - Minor radius (rfac = √((R-R₀)²+(Z-Z₀)²)) power exponent for Jacobian - `r0exp::Float64` - Major radius normalization for CHEASE/EQDSK [m] - `b0exp::Float64` - On-axis toroidal field normalization for CHEASE/EQDSK [T] - - `grid_type::String` - Grid type for flux surface discretization ("log_asymptotic", "ldp", "pow1", "uniform") + - `grid_type::String` - Grid type for flux surface discretization ("auto" — two-pass measured-curvature + refinement when mpsi=0, three-region log layout when mpsi>0; "ldp", "pow1", "uniform"; + "log_asymptotic" is a legacy alias for "auto") - `psilow::Float64` - Lower limit of normalized flux coordinate - `psihigh::Float64` - Upper limit of normalized flux coordinate - - `mpsi::Int` - Number of radial grid intervals; 0 with grid_type="log_asymptotic" selects the + - `mpsi::Int` - Number of radial grid intervals; 0 with grid_type="auto" selects the two-pass auto grid: the main driver forms a coarse pass-1 equilibrium, measures its curvature, pins knots on rational surfaces, and re-forms on the refined grid. Standalone `setup_equilibrium` callers get the coarse pass-1 grid unless they refine via `refined_psi_grid` + `override_psi_nodes`. @@ -46,7 +48,7 @@ Bundles all necessary settings originally specified in the equil fortran namelis power_r::Int = 0 power_rc::Int = 0 - grid_type::String = "log_asymptotic" + grid_type::String = "auto" psilow::Float64 = 1e-2 psihigh::Float64 = 0.9995 mpsi::Int = 0 diff --git a/src/Equilibrium/GridRefinement.jl b/src/Equilibrium/GridRefinement.jl index 1f5e3190..2fce25f5 100644 --- a/src/Equilibrium/GridRefinement.jl +++ b/src/Equilibrium/GridRefinement.jl @@ -45,11 +45,11 @@ const SING_PACK_RADIUS = 0.05 """ wants_two_pass(config::EquilibriumConfig) -> Bool -True when the config requests the automatic two-pass grid: `grid_type = "log_asymptotic"` -with `mpsi = 0` and a positive `psi_accuracy`. +True when the config requests the automatic two-pass grid: `grid_type = "auto"` (or its +legacy alias `"log_asymptotic"`) with `mpsi = 0` and a positive `psi_accuracy`. """ wants_two_pass(config::EquilibriumConfig) = - config.grid_type == "log_asymptotic" && config.mpsi == 0 && config.psi_accuracy > 0 + config.grid_type in ("auto", "log_asymptotic") && config.mpsi == 0 && config.psi_accuracy > 0 """ _validate_psi_nodes(psi_nodes, psilow, psihigh) -> Vector{Float64} diff --git a/src/Equilibrium/InverseEquilibrium.jl b/src/Equilibrium/InverseEquilibrium.jl index 42bb598b..01256e8c 100644 --- a/src/Equilibrium/InverseEquilibrium.jl +++ b/src/Equilibrium/InverseEquilibrium.jl @@ -138,7 +138,7 @@ function equilibrium_solver(input::InverseRunInput; override_psi_nodes::Union{No if override_psi_nodes !== nothing sq_xs = _validate_psi_nodes(override_psi_nodes, psilow, psihigh) mpsi = length(sq_xs) - 1 - elseif grid_type == "log_asymptotic" + elseif grid_type in ("auto", "log_asymptotic") if mpsi == 0 && config.psi_accuracy > 0 # Two-pass auto grid: coarse pass-1 layout; the driver refines and re-forms # on the measured-curvature grid (GridRefinement.jl). diff --git a/test/runtests_grid_refinement.jl b/test/runtests_grid_refinement.jl index 0b1f6a26..5c266fa8 100644 --- a/test/runtests_grid_refinement.jl +++ b/test/runtests_grid_refinement.jl @@ -101,11 +101,15 @@ const GridRef = GeneralizedPerturbedEquilibrium.Equilibrium @testset "Solovev two-pass integration" begin eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(; - eq_type="sol", grid_type="log_asymptotic", mpsi=0, psi_accuracy=1e-3, + eq_type="sol", grid_type="auto", mpsi=0, psi_accuracy=1e-3, psilow=0.01, psihigh=0.995) sol_config = GeneralizedPerturbedEquilibrium.Equilibrium.SolovevConfig() @test GridRef.wants_two_pass(eq_config) + # Legacy alias still selects the two-pass grid + alias_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(; + eq_type="sol", grid_type="log_asymptotic", mpsi=0, psi_accuracy=1e-3) + @test GridRef.wants_two_pass(alias_config) equil1 = GridRef.setup_equilibrium(eq_config, sol_config) From 42d9cf4a7b0cbcd25c8ec980c03b93750c7e80b8 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 20:54:00 -0400 Subject: [PATCH 12/16] TEST - IMPROVEMENT - Pin DIIID parallel-integration testsets to the production auto grid Per review: the auto grid is the production default, so the machinery testsets now build it exactly as the main driver does (rational_psi_nodes + refined_psi_grid + ingest re-form) and pin its values. The auto-vs-ldp512 convergence evidence is recorded on the PR; tightening psi_accuracy converges the pinned delta-prime entries toward the dense-reference values. Co-Authored-By: Claude Fable 5 --- test/runtests_parallel_integration.jl | 58 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/test/runtests_parallel_integration.jl b/test/runtests_parallel_integration.jl index e6df22a9..119001b2 100644 --- a/test/runtests_parallel_integration.jl +++ b/test/runtests_parallel_integration.jl @@ -284,16 +284,21 @@ using TOML inputs["ForceFreeStates"]["verbose"] = false inputs["ForceFreeStates"]["use_parallel"] = use_parallel inputs["ForceFreeStates"]["write_outputs_to_HDF5"] = false - # Pin the grid: this testset targets the integration machinery, so it must not - # move when the auto-grid (two-pass, driver-orchestrated) evolves. The example - # ships mpsi=0, which standalone setup_equilibrium forms as the coarse pass-1 grid. - inputs["Equilibrium"]["grid_type"] = "ldp" - inputs["Equilibrium"]["mpsi"] = 256 intr = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesInternal(; dir_path=ex) ctrl = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesControl(; (Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...) eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(inputs["Equilibrium"], ex) equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config, haskey(inputs, "SOL_INPUT") ? GeneralizedPerturbedEquilibrium.Equilibrium.SolovevConfig(inputs["SOL_INPUT"]) : nothing) + # Apply the two-pass auto grid exactly as the main driver does (the example ships + # grid_type="auto", mpsi=0): rational-surface knots + measured-curvature refinement, + # re-formed from the captured ingest. The pinned values below are for this grid, + # which is the production default. + if GeneralizedPerturbedEquilibrium.Equilibrium.wants_two_pass(eq_config) + mand = GeneralizedPerturbedEquilibrium.ForceFreeStates.rational_psi_nodes(equil; nlow=ctrl.nn_low, nhigh=ctrl.nn_high) + psi_nodes = GeneralizedPerturbedEquilibrium.Equilibrium.refined_psi_grid(equil; tau=eq_config.psi_accuracy, mandatory=mand) + rerun_input = GeneralizedPerturbedEquilibrium.Equilibrium.build_direct_from_ingest(eq_config, equil.ingest) + equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config, rerun_input; override_psi_nodes=psi_nodes) + end intr.wall_settings = GeneralizedPerturbedEquilibrium.Vacuum.WallShapeSettings(; (Symbol(k) => v for (k, v) in inputs["Wall"])...) GeneralizedPerturbedEquilibrium.ForceFreeStates.sing_lim!(intr, ctrl, equil) @@ -314,13 +319,14 @@ using TOML et_par, intr_par = run_diiid(true) - # Parallel FM et[1] regression on the pinned ldp/mpsi=256 grid, where the - # bidirectional assembly gives et ≈ 1.20. Single-point pinning of et_par is - # platform-sensitive at the few-percent level (BLAS variant / FP rounding through - # the BVP solve and outer-plasma Riccati pass shift the eigenvalue ~5-10 %), so we - # bracket the eigenvalue rather than pin a tight value. A true regression of the + # Parallel FM et[1] regression on the production-default two-pass auto grid, where + # the bidirectional assembly gives et ≈ 1.31 (ldp/mpsi=256 gives ≈ 1.20 — the + # eigenvalue is grid-sensitive at the ~10% level at these knot counts). Single-point + # pinning of et_par is platform-sensitive at the few-percent level (BLAS variant / + # FP rounding through the BVP solve and outer-plasma Riccati pass), so we bracket + # the eigenvalue rather than pin a tight value. A true regression of the # bidirectional assembly (an O(10%)+ energy error) still fails this bracket loudly. - @test 1.1 < et_par < 1.3 + @test 1.2 < et_par < 1.45 # Per-surface Δ' assertions removed (stub calculation; see Solovev testset # comment above). BVP Δ' matrix regression for DIIID-like is in the # `delta_prime_matrix — STRIDE BVP DIIID-like regression (large N)` testset. @@ -493,16 +499,19 @@ using TOML inputs["ForceFreeStates"]["verbose"] = false inputs["ForceFreeStates"]["use_parallel"] = true inputs["ForceFreeStates"]["write_outputs_to_HDF5"] = false - # Pin the grid: this testset targets the BVP assembly, so it must not move when - # the auto-grid (two-pass, driver-orchestrated) evolves. The example ships mpsi=0, - # which standalone setup_equilibrium forms as the coarse pass-1 grid. - inputs["Equilibrium"]["grid_type"] = "ldp" - inputs["Equilibrium"]["mpsi"] = 256 intr = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesInternal(; dir_path=ex) ctrl = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesControl(; (Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...) eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(inputs["Equilibrium"], ex) equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config, haskey(inputs, "SOL_INPUT") ? GeneralizedPerturbedEquilibrium.Equilibrium.SolovevConfig(inputs["SOL_INPUT"]) : nothing) + # Apply the two-pass auto grid exactly as the main driver does (see the FM testset + # above); the pinned values below are for this grid, the production default. + if GeneralizedPerturbedEquilibrium.Equilibrium.wants_two_pass(eq_config) + mand = GeneralizedPerturbedEquilibrium.ForceFreeStates.rational_psi_nodes(equil; nlow=ctrl.nn_low, nhigh=ctrl.nn_high) + psi_nodes = GeneralizedPerturbedEquilibrium.Equilibrium.refined_psi_grid(equil; tau=eq_config.psi_accuracy, mandatory=mand) + rerun_input = GeneralizedPerturbedEquilibrium.Equilibrium.build_direct_from_ingest(eq_config, equil.ingest) + equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config, rerun_input; override_psi_nodes=psi_nodes) + end intr.wall_settings = GeneralizedPerturbedEquilibrium.Vacuum.WallShapeSettings(; (Symbol(k) => v for (k, v) in inputs["Wall"])...) GeneralizedPerturbedEquilibrium.ForceFreeStates.sing_lim!(intr, ctrl, equil) @@ -542,16 +551,19 @@ using TOML # Pinned diagonal `delta_prime_matrix` values for the DIIID-like case (msing = 5), # PEST3-convention self-response Δ' from the STRIDE BVP with vacuum coupling, on - # the pinned ldp/mpsi=256 grid. Tolerances split by grid- and FP-sensitivity: - # - dpm[1]–dpm[3] (q=2,3,4): real parts are grid-converged (agree with denser - # ldp-512 and refined two-pass grids to a few %); pin Re at rtol=2e-2. The - # imaginary parts arise from the PEST3 four-term cancellation and drift in - # magnitude and sign with grid and BLAS variant; bound |Im| relative to |Re|. + # the production-default two-pass auto grid (τ=1e-3, ~200 knots). Tolerances split + # by grid- and FP-sensitivity: + # - dpm[1]–dpm[3] (q=2,3,4): real parts approach the grid-converged values + # (ldp-512 / two-pass τ=1e-4 give ≈ 8.90, −3.86, −10.2) to within ~5%; pin the + # auto-grid values at rtol=2e-2 so a behavior change is caught, and tighten + # psi_accuracy to converge further. The imaginary parts arise from the PEST3 + # four-term cancellation and drift in magnitude and sign with grid and BLAS + # variant; bound |Im| relative to |Re|. # - dpm[4], dpm[5] (q=5,6 near-separatrix): not grid-converged — value and sign # vary O(1) between ldp-256/512 and refined grids. Assert finite and non-zero # only (the earlier tight pins tracked a single grid's noise, not physics). - @test isapprox(real(dpm[1, 1]), +8.787850e+00; rtol=2e-2) - @test isapprox(real(dpm[2, 2]), -3.883926e+00; rtol=2e-2) + @test isapprox(real(dpm[1, 1]), +8.459458e+00; rtol=2e-2) + @test isapprox(real(dpm[2, 2]), -4.035389e+00; rtol=2e-2) @test isapprox(real(dpm[3, 3]), -1.021928e+01; rtol=5e-2) @test abs(imag(dpm[1, 1])) < 0.05 * abs(real(dpm[1, 1])) @test abs(imag(dpm[2, 2])) < 0.05 * abs(real(dpm[2, 2])) From 77c4e0ef1abc37910945ba9c29e62db989c84784 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Thu, 2 Jul 2026 20:54:00 -0400 Subject: [PATCH 13/16] DOCS - MINOR - Knot-placement comparison figure (auto two-pass vs ldp) and generator script Co-Authored-By: Claude Fable 5 --- benchmarks/plot_grid_knot_placement.jl | 67 ++++++++++++++++++++++++ docs/src/assets/grid_knot_placement.png | Bin 0 -> 120343 bytes 2 files changed, 67 insertions(+) create mode 100644 benchmarks/plot_grid_knot_placement.jl create mode 100644 docs/src/assets/grid_knot_placement.png diff --git a/benchmarks/plot_grid_knot_placement.jl b/benchmarks/plot_grid_knot_placement.jl new file mode 100644 index 00000000..b8a66323 --- /dev/null +++ b/benchmarks/plot_grid_knot_placement.jl @@ -0,0 +1,67 @@ +# Visualizes where the two-pass auto grid places its radial knots compared to the fixed +# ldp (sin²) grids, on the DIII-D-like example. Two panels: cumulative knot fraction +# index/mpsi vs ψ_N (packing profile), and local knot spacing Δψ vs ψ_N (log scale), +# with the n=1 rational surfaces marked. Documents the auto-grid PR. +# +# Usage: julia --project=. benchmarks/plot_grid_knot_placement.jl +# Output (not committed): benchmarks/grid_knot_placement.png + +using Pkg; +Pkg.activate(joinpath(@__DIR__, "..")) +using GeneralizedPerturbedEquilibrium +using Plots + +const GPE = GeneralizedPerturbedEquilibrium +const EXAMPLE_DIR = joinpath(@__DIR__, "..", "examples", "DIIID-like_ideal_example") + +function build_grids() + _, eq_config, additional_input = GPE.build_inputs_from_toml(EXAMPLE_DIR) + tau = eq_config.psi_accuracy + + # Pass 1 (coarse layout) and the two-pass refined grid, as the driver builds them + equil1 = GPE.Equilibrium.setup_equilibrium(eq_config, additional_input) + pass1 = copy(equil1.profiles.xs) + rationals = GPE.ForceFreeStates.rational_psi_nodes(equil1; nlow=1, nhigh=1) + auto = GPE.Equilibrium.refined_psi_grid(equil1; tau, mandatory=rationals) + + ldp(mpsi) = [eq_config.psilow + (eq_config.psihigh - eq_config.psilow) * sin((i / mpsi) * (π / 2))^2 for i in 0:mpsi] + + grids = [ + ("auto two-pass, τ=$(tau), mpsi=$(length(auto) - 1)", auto), + ("pass-1 layout, mpsi=$(length(pass1) - 1)", pass1), + ("ldp, mpsi=256", ldp(256)), + ("ldp, mpsi=512", ldp(512)) + ] + return grids, rationals +end + +function main() + grids, rationals = build_grids() + # Okabe-Ito colorblind-safe hues, fixed order; pass-1 recessive gray + colors = ["#0072B2", "#999999", "#E69F00", "#009E73"] + styles = [:solid, :dash, :solid, :solid] + + p1 = plot(; xlabel="ψ_N", ylabel="knot index / mpsi", title="Radial knot packing — DIIID-like example (n=1)", + legend=:bottomright, left_margin=12Plots.mm, bottom_margin=4Plots.mm, size=(900, 420)) + p2 = plot(; xlabel="ψ_N", ylabel="local knot spacing Δψ_N", yscale=:log10, + legend=:bottomleft, left_margin=12Plots.mm, bottom_margin=6Plots.mm, size=(900, 420)) + + for (i, (label, g)) in enumerate(grids) + n = length(g) - 1 + plot!(p1, g, (0:n) ./ n; lw=2, color=colors[i], ls=styles[i], label=label) + mids = 0.5 .* (g[1:(end-1)] .+ g[2:end]) + plot!(p2, mids, diff(g); lw=2, color=colors[i], ls=styles[i], label=label) + end + for (j, r) in enumerate(rationals) + for p in (p1, p2) + vline!(p, [r]; color="#CC79A7", ls=:dot, lw=1.5, label=(j == 1 && p === p1) ? "rational surfaces q=m/1" : "") + end + end + + fig = plot(p1, p2; layout=(2, 1), size=(900, 840)) + out = joinpath(@__DIR__, "grid_knot_placement.png") + savefig(fig, out) + println("Wrote ", abspath(out)) +end + +main() diff --git a/docs/src/assets/grid_knot_placement.png b/docs/src/assets/grid_knot_placement.png new file mode 100644 index 0000000000000000000000000000000000000000..584b705e0da85566adb357dbf1041dfee7c63415 GIT binary patch literal 120343 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 GL;nLE!hco( literal 0 HcmV?d00001 From 0e8acc6d4d46e258400388af3decf7d3736cd3ff Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Fri, 3 Jul 2026 12:22:05 -0400 Subject: [PATCH 14/16] EQUIL - MINOR - Expose sing_pack_coef for grid-convergence studies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Threads the rational-surface packing coefficient through refined_psi_grid and _knot_density as a keyword (default unchanged, scan-calibrated const). A (psi_accuracy, sing_pack_coef) scan on the DIII-D example showed the default grid's residual delta-prime spread at q=2,3 tracks the edge-truncation branch (correlated with the q=6 bistability), not knot count — denser grids up to 574 knots do not reduce it — so the lean default stands. Co-Authored-By: Claude Fable 5 --- src/Equilibrium/GridRefinement.jl | 67 ++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/src/Equilibrium/GridRefinement.jl b/src/Equilibrium/GridRefinement.jl index 2fce25f5..e134d77c 100644 --- a/src/Equilibrium/GridRefinement.jl +++ b/src/Equilibrium/GridRefinement.jl @@ -7,19 +7,34 @@ kinetic profiles) and equidistributes it, pinning mandatory knots (e.g. rational via `merge_mandatory_nodes`. Pass 2 re-forms the equilibrium on the refined grid through the `override_psi_nodes` keyword of `setup_equilibrium`. -The density uses the cubic *derivative* interpolation error model err(f′) ≈ h³|f''''|/24 — -the stability physics consumes spline derivatives (q′ at rational surfaces, p′ and V′ in +There is ONE sizing rule everywhere — the cubic *derivative* interpolation error model +err(f′) ≈ h³|f''''|/24 ≤ τ·f_scale — applied to three sources of curvature |f''''|: + + 1. *Measured* (mid-radius and edge): 5-point divided differences of the pass-1 nodal + values. Nodal values come from independent field-line integrations, so the estimate + is immune to inter-knot spline ringing. This source dominates wherever pass-1 data + supports it — including the edge, where the pass-1 layout is already log-packed + (on the DIII-D example the measured density supplies ~3× what the edge model demands). + 2. *Separatrix model* (edge floor, ψ ≥ 0.9): q ≈ −A·ln(1−ψ) fed through the same rule + gives geometric packing with uniform relative q′ error, independent of A. Normally + inactive; insurance against a pass-1 that under-sampled the divergence. + 3. *Axis model* (core, ψ ≤ 0.03): the equilibrium is a power law in ψ (smooth in + ρ = √ψ) near the axis, and the same rule on that form gives geometric-in-log(ψ) + packing. Here the model *replaces* measurement rather than flooring it — nodal data + from the smallest flux surfaces is dominated by integration and axis-extrapolation + error, which refinement amplifies rather than resolves. + +The stability physics consumes spline derivatives (q′ at rational surfaces, p′ and V′ in the Euler-Lagrange and ballooning coefficients), whose error at a given spacing is far -larger than the value error. Every region's knot count then scales as -psi_accuracy^(-1/3), so tightening the tolerance refines edge, pedestal, and mid -proportionally. Fourth derivatives are estimated by divided differences on the pass-1 -nodes only — nodal values come from independent field-line integrations and are immune to -inter-knot spline ringing. - -Curvature is measured with respect to ρ = √ψ_N, in which the equilibrium is regular at -the magnetic axis (R−R₀ ~ √ψ_N makes the geometry channels behave as ψ_N^(k/2) there, so -their ψ-derivatives diverge but their ρ-derivatives do not); the resulting ρ-spacing maps -back to the ψ density through dψ/dρ = 2√ψ_N, giving natural √ψ core packing. +larger than the value error — hence the derivative model. Every region's knot count +scales as psi_accuracy^(-1/3), so tightening the tolerance refines edge, pedestal, and +mid proportionally. + +Measured curvature is taken with respect to ρ = √ψ_N, in which the equilibrium is regular +at the magnetic axis (R−R₀ ~ √ψ_N makes the geometry channels behave as ψ_N^(k/2) there, +so their ψ-derivatives diverge under refinement but their ρ-derivatives do not); the +resulting ρ-spacing maps back to the ψ density through dψ/dρ = 2√ψ_N, giving natural √ψ +core packing outside the modeled core region as well. """ # Cubic spline derivative interpolation error constant: err(f′) ≈ h³|f''''|/24 @@ -132,18 +147,19 @@ by max: - curvature of the kinetic profiles n_i, n_e, T_i, T_e, ω_E when `kin` is given (their own grid, rebroadcast by linear interpolation) — steep pedestal gradients in the kinetic data pull knots into the pedestal; - - a-priori floors: geometric-in-log(1−ψ) at the edge and geometric-in-log(ψ) in the - core, plus a global minimum density. For the separatrix asymptote q ≈ −A·ln(1−ψ) - (q′ = A/(1−ψ)), geometric packing with dlog = (4τ)^(1/3) gives uniform *relative* q′ - error dlog³/4 = τ independent of A — guarding pass-1 undersampling of the divergence - without over-packing equilibria whose edge q is finite; - - local packing around each `mandatory` (rational) surface: spacing `SING_PACK_COEF`·τ^(1/3) + - the model-curvature regions (same h³ rule on analytic |f''''| — see the module + docstring): the edge floor for ψ ≥ 0.9, geometric-in-log(1−ψ) with dlog = (4τ)^(1/3) + from uniform relative q′ error on q ≈ −A·ln(1−ψ), independent of A and normally + inactive; and the core for ψ ≤ 0.03, geometric-in-log(ψ) from the power-law axis + form, which replaces the (noise-dominated) measurement there. A global minimum + density applies everywhere; + - local packing around each `mandatory` (rational) surface: spacing `sing_pack_coef`·τ^(1/3) at the surface with geometric growth away from it, within `SING_PACK_RADIUS`. A running max over ±1 node smooths single-stencil dropouts. """ function _knot_density(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing, - mandatory::Vector{Float64}=Float64[]) + mandatory::Vector{Float64}=Float64[], sing_pack_coef::Float64=SING_PACK_COEF) xs = equil.profiles.xs n = length(xs) # Curvature is measured against ρ = √ψ (regular at the axis); the ρ-space density @@ -207,7 +223,7 @@ function _knot_density(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothin end # Local packing around mandatory (rational) surfaces - h_s = max(SING_PACK_COEF * tau^(1 / 3), H_TARGET_MIN) + h_s = max(sing_pack_coef * tau^(1 / 3), H_TARGET_MIN) for psi_m in mandatory @inbounds for i in 1:n d = abs(xs[i] - psi_m) @@ -299,22 +315,25 @@ end """ refined_psi_grid(equil::PlasmaEquilibrium; tau, kin=nothing, mandatory=Float64[], - delta_frac=0.25, N_cap=1024) -> Vector{Float64} + delta_frac=0.25, N_cap=1024, sing_pack_coef=SING_PACK_COEF) -> Vector{Float64} Build the refined pass-2 ψ grid from a formed pass-1 equilibrium: measured-curvature knot density (`_knot_density`), equidistribution, and mandatory-knot insertion (`merge_mandatory_nodes`). `tau` is the target interpolation accuracy (`psi_accuracy`); `kin` optionally supplies kinetic profiles whose pedestal gradients attract knots; -`mandatory` lists ψ values that must appear as knots (e.g. rational surfaces). +`mandatory` lists ψ values that must appear as knots (e.g. rational surfaces); +`sing_pack_coef` scales the knot spacing at those surfaces (convergence studies only — +the default is scan-calibrated). """ function refined_psi_grid(equil::PlasmaEquilibrium; tau::Float64, kin::Union{Nothing,KineticProfileSplines}=nothing, mandatory::Vector{Float64}=Float64[], delta_frac::Float64=0.25, - N_cap::Int=1024) + N_cap::Int=1024, + sing_pack_coef::Float64=SING_PACK_COEF) xs = equil.profiles.xs - rho = _knot_density(equil; tau, kin, mandatory) + rho = _knot_density(equil; tau, kin, mandatory, sing_pack_coef) M_total = sum(0.5 * (rho[i] + rho[i-1]) * (xs[i] - xs[i-1]) for i in 2:length(xs)) N = clamp(ceil(Int, M_total), 32, N_cap) N == N_cap && M_total > N_cap && From 053526adb9b97080750d342f6df172cebd4e7a07 Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Fri, 3 Jul 2026 12:22:05 -0400 Subject: [PATCH 15/16] DOCS - MINOR - Present the auto-grid density as one h3 error model with three curvature sources One sizing rule (cubic derivative error h^3|f''''|/24 <= tau*scale) fed by measured divided differences (mid + edge), the separatrix log model (edge floor, normally inactive), or the power-law axis model (core, replacing the noise-dominated smallest-surface measurements). Adds the psi->0 explanation: the bicubic channels' psi-space blow-up is a coordinate artifact handled by measuring in rho = sqrt(psi) and by the geometric core packing. Co-Authored-By: Claude Fable 5 --- docs/src/equilibrium.md | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/docs/src/equilibrium.md b/docs/src/equilibrium.md index 01e72a6d..064dfdc5 100644 --- a/docs/src/equilibrium.md +++ b/docs/src/equilibrium.md @@ -63,20 +63,33 @@ built by a **two-pass measured-curvature refinement** driven by the single accur 1. **Pass 1** forms the equilibrium on a coarse three-region layout (geometric in log(ψ) at the core, uniform in the middle, geometric in log(1−ψ) at the edge). -2. A knot density is derived from the *measured* pass-1 data using the cubic-spline - derivative error model err(f′) ≈ h³|f''''|/24 ≤ τ·|f|. Fourth derivatives are - estimated by divided differences on the pass-1 nodes only — nodal values come from - independent field-line integrations, so the estimate is immune to inter-knot spline - ringing. The density combines, by max: - - the 1D profiles F, P, dV/dψ, and q; - - the 2D geometry channels (r², η-offset, ν, Jacobian) along sampled θ-lines, since - the bicubic ψ-axis shares the same knots; - - the kinetic profiles (n, T, ω_E) when loaded, so steep pedestal gradients attract - knots; - - a-priori geometric floors at the core and edge (uniform relative q′ error against - the separatrix asymptote q ~ −A·ln(1−ψ), independent of A); - - local packing around every rational surface q = m/n in the requested n range, - which the main driver pins as mandatory knots. +2. A knot density is derived from **one sizing rule** — the cubic-spline derivative + error model err(f′) ≈ h³|f''''|/24 ≤ τ·|f| — applied to three sources of + curvature |f''''|: + - **Measured** (mid-radius and edge): divided differences on the pass-1 nodes of the + 1D profiles (F, P, dV/dψ, q), the 2D geometry channels (r², η-offset, ν, Jacobian) + along sampled θ-lines (the bicubic ψ-axis shares the same knots), and the kinetic + profiles (n, T, ω_E) when loaded, so steep pedestal gradients attract knots. Nodal + values come from independent field-line integrations, so the estimate is immune to + inter-knot spline ringing. Curvature is measured against ρ = √ψ, in which the + equilibrium is regular at the magnetic axis — in ψ itself the geometry channels + behave as ψ^(k/2) (R−R₀ ~ √ψ), so their ψ-curvature diverges under refinement while + their ρ-curvature converges; the ρ-spacing maps back through dψ/dρ = 2√ψ, which by + itself packs √ψ-tight toward the axis. + - **Separatrix model** (edge floor, ψ ≥ 0.9): the same rule on q ≈ −A·ln(1−ψ) gives + geometric-in-log(1−ψ) packing with uniform relative q′ error, independent of A. + Normally inactive — the pass-1 layout is already log-packed at the edge, so the + measured density dominates — it only guards a pass-1 that under-sampled the + divergence. + - **Axis model** (core, ψ ≤ 0.03): the same rule on the power-law axis form gives + geometric-in-log(ψ) packing (constant-ratio spacing ∝ ψ down to `psilow`). Here the + model *replaces* measurement: nodal data from the smallest flux surfaces is + dominated by integration and axis-extrapolation noise, which grid refinement + amplifies rather than resolves. + - **Rational surfaces**: local packing around every q = m/n in the requested n range + (pinned as mandatory knots by the main driver), at any ψ including inside the + modeled core region. This local resolution of the ψ-splined Euler-Lagrange + coefficient matrices is what converges the Δ′ boundary-value problem. 3. The density integral is equidistributed into the knot vector, mandatory rational-surface knots are inserted with a minimum-spacing snap guard, and **pass 2** re-forms the equilibrium on the refined grid from the in-memory input From a145a4735b5829e6d6f7975b0354e99249b2511a Mon Sep 17 00:00:00 2001 From: logan-nc <6198372+logan-nc@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:28:58 -0400 Subject: [PATCH 16/16] DOCS - MINOR - Knot-density decomposition figure: pedestal packing is measured, not floor-driven MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-source decomposition on the DIII-D example shows the pedestal band is packed by measured curvature — P, q, and dV/dpsi contribute comparably and the rzphi geometry channels (the GS response to the same pedestal p') dominate at every node in psi 0.8-0.98, all above the edge floor. Co-Authored-By: Claude Fable 5 --- docs/src/assets/density_decomposition.png | Bin 0 -> 89922 bytes docs/src/equilibrium.md | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 docs/src/assets/density_decomposition.png diff --git a/docs/src/assets/density_decomposition.png b/docs/src/assets/density_decomposition.png new file mode 100644 index 0000000000000000000000000000000000000000..8ae2c7659b40130e92146cc131c053d902233f21 GIT binary patch literal 89922 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%0Dq2MO5FnTY9%4tCK7l)d!$9i?t}3qc~n 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 literal 0 HcmV?d00001 diff --git a/docs/src/equilibrium.md b/docs/src/equilibrium.md index 064dfdc5..ecff9767 100644 --- a/docs/src/equilibrium.md +++ b/docs/src/equilibrium.md @@ -109,6 +109,15 @@ this figure): ![Radial knot packing: auto two-pass vs ldp](assets/grid_knot_placement.png) +Decomposing the density by source on the same example shows the pedestal band +(ψ_N ≈ 0.85–0.98) is driven by *measured* curvature, not the edge floor: the pressure, +q, and dV/dψ profiles contribute comparably, and the rzphi geometry channels — the +Grad-Shafranov response to the same pedestal p′ (Shafranov-shift / angle-offset +steepening) — contribute the most at every node in the band. Because the density is +measured from the formed solution, the packing follows the pedestal wherever it sits: + +![Knot-density decomposition by source](assets/density_decomposition.png) + ## API Reference ```@autodocs