|
| 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