Skip to content

Commit 064b627

Browse files
added set_resistive_width_based_psihigh helper for resistive-layer-width-based psihigh truncation
1 parent 6afe97e commit 064b627

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""
2+
set_resistive_width_based_psihigh(r; q_scan=2:1:40, n_tor=1, mu_i=2.0, zeff=1.0)
3+
4+
Given a completed equilibrium/stability run `r`, computes the resistive-layer
5+
based truncation boundary: finds rational surfaces via the iota spline,
6+
computes each surface's real resistive layer width (SLAYER Riccati del_s
7+
solve) and its visco-resistive comparison scale, and returns the more
8+
conservative (smaller) psihigh at which neighboring surfaces first overlap
9+
or a surface's own layer spans past psi=1.
10+
11+
Returns `(psihigh, psihigh_dels, psihigh_visco)`.
12+
"""
13+
function set_resistive_width_based_psihigh(r; q_scan=2:1:40, n_tor=1, mu_i=2.0, zeff=1.0)
14+
r_of_psi = r.equil.geometry.avg_r_spline
15+
drdpsi = ψ -> r_of_psi(ψ; deriv=DerivOp(1))
16+
17+
x_knots = r.equil.profiles.xs
18+
q_knots = r.equil.profiles.q_spline.y
19+
iota_itp_real = cubic_interp(x_knots, 1.0 ./ q_knots; extrap=Extrap(:extend))
20+
q_of_psi_real = ψ -> 1.0 / iota_itp_real(ψ)
21+
dqdpsi_real = ψ -> -iota_itp_real(ψ; deriv=DerivOp(1)) / iota_itp_real(ψ)^2
22+
23+
ψ1, ψ2 = x_knots[end-1], x_knots[end]
24+
q1, q2 = q_of_psi_real(ψ1), q_of_psi_real(ψ2)
25+
A = (q2 - q1) / (log(1-ψ1) - log(1-ψ2))
26+
ψ_last, q_last = ψ2, q2
27+
28+
q_of_psi_hybrid(ψ) = ψ <= ψ_last ? q_of_psi_real(ψ) : q_last - A*log(1-ψ) + A*log(1-ψ_last)
29+
dqdpsi_hybrid(ψ) = ψ <= ψ_last ? dqdpsi_real(ψ) : A/(1-ψ)
30+
31+
function find_psi_for_q(target_q)
32+
if target_q <= q_last
33+
ψgrid = range(x_knots[1], ψ_last, length=200_000)
34+
vals = q_of_psi_real.(ψgrid)
35+
for i in 1:length(ψgrid)-1
36+
y1, y2 = vals[i]-target_q, vals[i+1]-target_q
37+
if y1*y2 <= 0
38+
lo, hi = ψgrid[i], ψgrid[i+1]
39+
flo = y1
40+
for _ in 1:60
41+
mid = (lo+hi)/2
42+
fmid = q_of_psi_real(mid) - target_q
43+
sign(fmid) == sign(flo) ? (lo,flo)=(mid,fmid) : (hi=mid)
44+
end
45+
return (lo+hi)/2, true
46+
end
47+
end
48+
return nothing, false
49+
else
50+
x = (1-ψ_last) * exp((q_last - target_q)/A)
51+
x < 1e-15 && return nothing, false
52+
return 1.0 - x, true
53+
end
54+
end
55+
56+
kfile_path = r.ctrl.kinetic_file
57+
kf = h5open(kfile_path, "r")
58+
psi_k = read(kf["psi"]); ne_k = read(kf["n_e"]); Ti_k = read(kf["T_i"])
59+
Te_k = read(kf["T_e"]); omE_k = read(kf["omega_E"])
60+
chie_k = read(kf["chi_e"]); chip_k = read(kf["chi_phi"])
61+
close(kf)
62+
63+
mk_spline(y) = cubic_interp(psi_k, y; extrap=Extrap(:extend))
64+
ne_spl, Ti_spl, Te_spl = mk_spline(ne_k), mk_spline(Ti_k), mk_spline(Te_k)
65+
omE_spl, chie_spl, chip_spl = mk_spline(omE_k), mk_spline(chie_k), mk_spline(chip_k)
66+
omega_star(ne_val, dne_dr, T_eV, B, Z) = -(T_eV*E_CHG)/(Z*E_CHG*B) * (dne_dr/ne_val)
67+
68+
bt = r.equil.params.bt0
69+
R0 = r.equil.params.rmean
70+
71+
kept_psi = Float64[]; kept_w_dels = Float64[]; kept_w_visco = Float64[]
72+
psihigh_dels, psihigh_visco = nothing, nothing
73+
74+
for m in q_scan
75+
ψ_m, found = find_psi_for_q(Float64(m))
76+
!found && continue
77+
78+
rs_val = r_of_psi(ψ_m); q_val = q_of_psi_hybrid(ψ_m); q1_val = dqdpsi_hybrid(ψ_m)
79+
dr_val_geo = drdpsi(ψ_m)
80+
(!isfinite(dr_val_geo) || dr_val_geo == 0.0 || !isfinite(q1_val) || !isfinite(q_val)) && continue
81+
82+
sval_r = try
83+
r_based_shear(rs_val, q_val, q1_val, dr_val_geo)
84+
catch
85+
continue
86+
end
87+
88+
ne_val = ne_spl(ψ_m); Ti_val = Ti_spl(ψ_m); Te_val = Te_spl(ψ_m)
89+
(!isfinite(ne_val) || !isfinite(Ti_val) || !isfinite(Te_val) || ne_val<=0 || Ti_val<=0 || Te_val<=0) && continue
90+
91+
dne_dr = ne_spl(ψ_m; deriv=DerivOp(1)) / dr_val_geo
92+
omega_e = omega_star(ne_val, dne_dr, Te_val, bt, -1.0)
93+
omega_i = omega_star(ne_val, dne_dr, Ti_val, bt, 1.0)
94+
omega = omE_spl(ψ_m)
95+
chi_perp, chi_tor = chie_spl(ψ_m), chip_spl(ψ_m)
96+
(!isfinite(chi_perp) || !isfinite(chi_tor) || chi_perp<=0 || chi_tor<=0) && continue
97+
98+
p = try
99+
slayer_parameters(; n_e=ne_val, t_e=Te_val, t_i=Ti_val,
100+
omega=omega, omega_e=omega_e, omega_i=omega_i,
101+
qval=q_val, sval_r=sval_r, bt=bt, rs=rs_val, R0=R0,
102+
mu_i=mu_i, zeff=zeff, chi_perp=chi_perp, chi_tor=chi_tor,
103+
m=Int(round(m)), n=n_tor, ising=length(kept_psi)+1)
104+
catch
105+
continue
106+
end
107+
108+
lw = slayer_layer_thickness(p)
109+
isnan(lw.delta_s_m) && continue
110+
111+
push!(kept_psi, ψ_m); push!(kept_w_dels, lw.delta_s_m); push!(kept_w_visco, lw.δ_visco)
112+
113+
n_now = length(kept_psi)
114+
if n_now >= 2
115+
right_prev_dels = kept_psi[n_now-1] + kept_w_dels[n_now-1]/2
116+
left_curr_dels = kept_psi[n_now] - kept_w_dels[n_now]/2
117+
right_prev_visco = kept_psi[n_now-1] + kept_w_visco[n_now-1]/2
118+
left_curr_visco = kept_psi[n_now] - kept_w_visco[n_now]/2
119+
120+
dels_stop = (left_curr_dels < right_prev_dels) || (right_prev_dels > 1.0)
121+
visco_stop = (left_curr_visco < right_prev_visco) || (right_prev_visco > 1.0)
122+
123+
if dels_stop && psihigh_dels === nothing
124+
psihigh_dels = kept_psi[n_now-1] - kept_w_dels[n_now-1]/2
125+
end
126+
if visco_stop && psihigh_visco === nothing
127+
psihigh_visco = kept_psi[n_now-1] - kept_w_visco[n_now-1]/2
128+
end
129+
dels_stop && visco_stop && break
130+
end
131+
end
132+
133+
candidates = filter(!isnothing, [psihigh_dels, psihigh_visco])
134+
psihigh_final = isempty(candidates) ? nothing : minimum(candidates)
135+
136+
return psihigh_final, psihigh_dels, psihigh_visco
137+
end

0 commit comments

Comments
 (0)