Skip to content

Commit 629f62d

Browse files
committed
PE - MINOR - more cleanups
1 parent eb0bc12 commit 629f62d

5 files changed

Lines changed: 40 additions & 119 deletions

File tree

src/GeneralizedPerturbedEquilibrium.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,12 +471,30 @@ function main_from_inputs(
471471
return
472472
end
473473

474+
# No perturbed equilibrium calculations if vacuum data is not available
475+
if !ctrl.vac_flag
476+
@warn "Vacuum data not available. Skipping perturbed equilibrium calculations. Set vac_flag=true in [ForceFreeStates] section."
477+
return
478+
end
479+
474480
# No perturbed equilibrium calculations if free-boundary mode is unstable
475481
if real(vac_data.et[1]) < 0
476482
@warn "Since a free-boundary mode is unstable, perturbed equilibrium calculations will not run."
477483
return
478484
end
479485

486+
# No perturbed equilibrium calculations if wall shape is not nowall
487+
if intr.wall_settings.shape !== "nowall"
488+
@warn "Perturbed equilibrium calculations are only supported for nowall cases - skipping perturbed equilibrium calculations."
489+
return
490+
end
491+
492+
# Perturbed equilibrium calculations still need multi-n support added
493+
if intr.npert > 1
494+
@warn "Perturbed equilibrium calculations are only supported for single-n cases - skipping perturbed equilibrium calculations."
495+
return
496+
end
497+
480498
# ----------------------------------------------------------------
481499
# Perturbed Equilibrium
482500
# ----------------------------------------------------------------

src/PerturbedEquilibrium/PerturbedEquilibrium.jl

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,20 +117,12 @@ function compute_perturbed_equilibrium(
117117

118118
# Compute plasma response
119119
if ctrl.compute_response
120-
if vac_data === nothing
121-
@warn "Vacuum data not available. Skipping plasma response calculation. Set vac_flag=true in [ForceFreeStates] section."
122-
else
123-
compute_plasma_response!(state, equil, ForceFreeStates_results, vac_data, ffs_intr, intr, ctrl, metric, ffit)
124-
end
120+
compute_plasma_response!(state, equil, ForceFreeStates_results, vac_data, ffs_intr, intr, ctrl, metric, ffit)
125121
end
126122

127123
# Compute singular coupling metrics
128124
if ctrl.compute_singular_coupling
129-
if vac_data === nothing
130-
@warn "Vacuum data not available. Skipping singular coupling calculation. Set vac_flag=true in [ForceFreeStates] section."
131-
else
132-
compute_singular_coupling_metrics!(state, equil, ForceFreeStates_results, vac_data, ffs_intr, intr, ctrl)
133-
end
125+
compute_singular_coupling_metrics!(state, equil, ForceFreeStates_results, vac_data, ffs_intr, intr, ctrl)
134126
end
135127

136128
return state

src/PerturbedEquilibrium/Response.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,11 @@ function compute_plasma_response!(
3333
# Compute plasma inductance
3434
plasma_inductance = calc_plasma_inductance(ffs_intr, vac_data.wt0, equil.psio)
3535

36-
# Surface inductance L from Green's functions at psilim - requires no wall limit
36+
# Surface inductance L from Green's functions
3737
nn = ffs_intr.nlow
3838
vac_input_2d = Vacuum.VacuumInput(equil, ffs_intr.psilim, vac_data.mthvac, 1, ffs_intr.mlow:ffs_intr.mhigh, [nn])
39-
wall_nowall = Vacuum.WallShapeSettings(; shape="nowall")
40-
_, grri_2d, grre_2d, _, _ = Vacuum.compute_vacuum_response(vac_input_2d, wall_nowall)
4139
ν_vac = Vacuum.PlasmaGeometry(vac_input_2d).ν
42-
surface_inductance = compute_surface_inductance_from_greens(grri_2d, grre_2d, ffs_intr, nn, ν_vac)
40+
surface_inductance = compute_surface_inductance_from_greens(vac_data.grri, vac_data.grre, ffs_intr, nn, ν_vac)
4341

4442
# Compute permeability P = Λ·L⁻¹ and store in internal state for singular coupling / field reconstruction.
4543
permeability = plasma_inductance / surface_inductance

src/PerturbedEquilibrium/ResponseMatrices.jl

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -192,21 +192,11 @@ function map_forcing_to_eigenmodes(
192192
numpert_total = intr.mpert * intr.npert
193193
forcing_vector = zeros(ComplexF64, numpert_total)
194194

195-
# Create mode index map: (m,n) -> linear index
195+
# Combine all forcing modes into a single vector
196196
for forcing_mode in forcing_modes
197-
# Find matching mode in eigenmode basis
198-
for i in 1:numpert_total
199-
# Calculate m and n for this index
200-
# Using 0-based indexing converted to 1-based:
201-
# m = (i-1) % mpert + mlow
202-
# n = (i-1) ÷ mpert + nlow
203-
m_mode = (i - 1) % intr.mpert + intr.mlow
204-
n_mode = (i - 1) ÷ intr.mpert + intr.nlow
205-
206-
if m_mode == forcing_mode.m && n_mode == forcing_mode.n
207-
forcing_vector[i] = forcing_mode.amplitude
208-
break
209-
end
197+
i = (forcing_mode.m - intr.mlow) + (forcing_mode.n - intr.nlow) * intr.mpert + 1
198+
if 1 <= i <= numpert_total
199+
forcing_vector[i] = forcing_mode.amplitude
210200
end
211201
end
212202

src/PerturbedEquilibrium/SingularCoupling.jl

Lines changed: 14 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ function compute_singular_coupling_metrics!(
9999
end
100100

101101
chi1 = 2π * equil.psio
102-
twopi = 2π
103102
mtheta = vac_data.mthvac
104103
wall_settings = Vacuum.WallShapeSettings(; shape="nowall")
105104

@@ -162,9 +161,7 @@ function compute_singular_coupling_metrics!(
162161

163162
# Compute Green's functions at this surface for this n (once per pair)
164163
vac_input = Vacuum.VacuumInput(equil, sing_surf.psifac, mtheta, 1, mlow:mhigh, [nn])
165-
_, grri_raw, grre_raw, _, _ = Vacuum.compute_vacuum_response(vac_input, wall_settings)
166-
grri = Matrix{ComplexF64}(grri_raw)
167-
grre = Matrix{ComplexF64}(grre_raw)
164+
_, grri, grre, _, _ = Vacuum.compute_vacuum_response(vac_input, wall_settings)
168165
ffs_intr.sing[s].grri = grri
169166
ffs_intr.sing[s].grre = grre
170167

@@ -177,11 +174,8 @@ function compute_singular_coupling_metrics!(
177174
L_mm = L_surf[m_idx, m_idx]
178175

179176
j_c = compute_current_density(equil, sing_surf.psifac)
180-
area = compute_surface_area(equil, sing_surf.psifac)
181-
# Matches Fortran gpout_resp: shear = m*dq/dψ / q² = n*dq/dψ / q (since m=n*q).
182-
# Uses abs(nn) because island_half_width = sqrt(abs(island_width_sq)), so the sign
183-
# of shear only affects the sign of C_island_width_sq, not the physical island width.
184-
shear = abs(nn) * sing_surf.q1 / sing_surf.q
177+
area = Equilibrium.flux_surface_area(equil, sing_surf.psifac, length(equil.rzphi_ys) - 1)
178+
shear = nn * sing_surf.q1 / sing_surf.q # m*dq/dψ / q² = n*dq/dψ / q (since m=n*q)
185179

186180
# Evaluate bwp1_mn = ∂b^ψ/∂ψ at lpsi and rpsi using permeability-weighted eigenstates.
187181
# Matches Fortran gpout_resp: evaluate bwp1_mn at lpsi/rpsi via gpeq_sol
@@ -254,14 +248,14 @@ function compute_singular_coupling_metrics!(
254248
# - resonant (shielding) current: j_c already integrates jac·|∇ψ| over the surface, so the
255249
# Jacobian weighting is carried inside j_c — no separate area factor needed.
256250
# - resonant flux → field: Φ^r/A^r [T], invariant [Park 2008; Pharr 2026].
257-
state.C_delta_prime[row, :] = jump_vec ./ (twopi * chi1)
258-
state.C_resonant_current[row, :] = jump_vec .* (-j_c / (twopi * m_res))
251+
state.C_delta_prime[row, :] = jump_vec ./ (2π * chi1)
252+
state.C_resonant_current[row, :] = jump_vec .* (-j_c / (2π * m_res))
259253
# Matches Fortran gpout_resp: singflx = L·fkaxmn, resonant area-weighted field = singflx/area,
260254
# islandhwids = 4·singflx/(2π·shear·q·chi1)
261-
singflx_pre = (L_mm / (twopi * nn)) .* state.C_resonant_current[row, :]
255+
singflx_pre = (L_mm / (2π * nn)) .* state.C_resonant_current[row, :]
262256
state.C_resonant_area_weighted_field[row, :] = singflx_pre ./ area
263257
if abs(shear) > 1e-10
264-
state.C_island_width_sq[row, :] = (4.0 / (twopi * shear * sing_surf.q * chi1)) .* singflx_pre
258+
state.C_island_width_sq[row, :] = abs.(4.0 / (2π * shear * sing_surf.q * chi1)) .* singflx_pre
265259
end
266260

267261
state.rational_psi[row] = sing_surf.psifac
@@ -329,24 +323,6 @@ j_c = χ₁² * q / (μ₀ * integral)
329323
where the integral is computed via flux surface integration:
330324
integral = ∫ (jac * |∇ψ| * sqreqb / |∇ψ|³) dθ
331325
332-
## GPEC Formula
333-
334-
```fortran
335-
DO itheta=0,mthsurf
336-
CALL bicube_eval(rzphi,respsi,theta(itheta),1)
337-
rfac=SQRT(rzphi%f(1))
338-
jac=rzphi%f(4)
339-
w(1,1)=(1+rzphi%fy(2))*twopi**2*rfac*r(itheta)/jac
340-
w(1,2)=-rzphi%fy(1)*pi*r(itheta)/(rfac*jac)
341-
delpsi(itheta)=SQRT(w(1,1)**2+w(1,2)**2)
342-
sqreqb(itheta)=(sq%f(1)**2+chi1**2*delpsi(itheta)**2)/(twopi*r(itheta))**2
343-
jcfun(itheta)=sqreqb(itheta)/(delpsi(itheta)**3)
344-
j_c(ising)=j_c(ising)+jac*delpsi(itheta)*jcfun(itheta)/mthsurf
345-
ENDDO
346-
j_c(ising)=j_c(ising)-jac*delpsi(mthsurf)*jcfun(mthsurf)/mthsurf ! trapezoidal rule
347-
j_c(ising)=1.0/j_c(ising)*chi1**2*sq%f(4)/mu0
348-
```
349-
350326
## Implementation
351327
352328
Uses trapezoidal rule integration around the flux surface with metric quantities
@@ -362,14 +338,11 @@ function compute_current_density(
362338
)::Float64
363339
# Physical constants
364340
chi1 = 2π * equil.psio
365-
twopi = 2π
366341

367342
# Get equilibrium quantities at this surface
368343
F_tor = equil.profiles.F_spline(psi) # Toroidal field function (2π·R·B_tor in GPEC convention)
369344
q = equil.profiles.q_spline(psi) # Safety factor
370345

371-
ro = equil.ro
372-
373346
# Number of theta points for integration
374347
# Match GPEC's mthsurf (typically 101 points from theta=0 to theta=1)
375348
mthsurf = length(equil.rzphi_ys) - 1
@@ -392,7 +365,7 @@ function compute_current_density(
392365
delpsi = m.delpsi # flux gradient magnitude |∇ψ|
393366

394367
# sqreqb = (F² + χ₁²|∇ψ|²) / (2πR)² where F = R·B_tor (Fortran sq%f(1))
395-
sqreqb = (F_tor^2 + chi1^2 * delpsi^2) / (twopi * m.r)^2
368+
sqreqb = (F_tor^2 + chi1^2 * delpsi^2) / (2π * m.r)^2
396369

397370
# Integrand function
398371
jcfun = sqreqb / (delpsi^3)
@@ -469,10 +442,12 @@ Surface inductance matrix [mpert × mpert]
469442

470443
for i in 1:mpert
471444
# Complex grri/e stores exp(i(mθ-nν)) projection, need conjugate for exp(-i(mθ-nν))
445+
# Eq. 10 of Park 2007
472446
kax .= conj.(grri_surf[:, i] .+ grre_surf[:, i]) ./ (μ0 * (2π)^2)
473447

474448
# Apply toroidal phase, reverse theta, forward-DFT.
475449
g_phased = kax .* phase
450+
# Eq. 21b of Park 2007
476451
current_matrix[:, i] = ft(_reverse_theta(g_phased))
477452
end
478453

@@ -487,67 +462,15 @@ Surface inductance matrix [mpert × mpert]
487462
L_surf[i, i] = μ0 * 1e-6
488463
end
489464
else
490-
try
491-
regularization = 1e-12 * current_mag
492-
current_reg = current_matrix + regularization * I
493-
494-
L_surf = inv(current_reg)
495-
hermitianpart!(L_surf)
496-
catch e
497-
@warn "Surface inductance inversion failed: $e" maxlog=1
498-
for i in 1:mpert
499-
L_surf[i, i] = μ0 * 1e-6
500-
end
501-
end
465+
# Add a small regularization to the current matrix to avoid division by zero
466+
current_matrix += 1e-12 * current_mag * I
467+
L_surf .= inv(current_matrix)
468+
hermitianpart!(L_surf)
502469
end
503470

504471
return L_surf
505472
end
506473

507-
"""
508-
compute_surface_area(
509-
equil::Equilibrium.PlasmaEquilibrium,
510-
psi::Float64
511-
)::Float64
512-
513-
Compute flux surface area at given ψ.
514-
515-
Implements GPEC's area calculation (Fortran `gpout_respinfo`):
516-
area = ∫ jac * |∇ψ| dθ
517-
518-
where the integral is computed around the flux surface.
519-
520-
## GPEC Formula
521-
522-
```fortran
523-
DO itheta=0,mthsurf
524-
CALL bicube_eval(rzphi,respsi,theta(itheta),1)
525-
rfac=SQRT(rzphi%f(1))
526-
jac=rzphi%f(4)
527-
w(1,1)=(1+rzphi%fy(2))*twopi**2*rfac*r(itheta)/jac
528-
w(1,2)=-rzphi%fy(1)*pi*r(itheta)/(rfac*jac)
529-
delpsi(itheta)=SQRT(w(1,1)**2+w(1,2)**2)
530-
area(ising)=area(ising)+jac*delpsi(itheta)/mthsurf
531-
ENDDO
532-
area(ising)=area(ising)-jac*delpsi(mthsurf)/mthsurf ! trapezoidal rule
533-
```
534-
535-
## Implementation
536-
537-
Uses trapezoidal rule integration around the flux surface with:
538-
539-
- jac: Jacobian of flux coordinates from rzphi
540-
- |∇ψ|: Flux gradient magnitude (delpsi) from metric tensor
541-
"""
542-
function compute_surface_area(
543-
equil::Equilibrium.PlasmaEquilibrium,
544-
psi::Float64
545-
)::Float64
546-
# mthsurf matches GPEC's flux-surface theta resolution
547-
mthsurf = length(equil.rzphi_ys) - 1
548-
return Equilibrium.flux_surface_area(equil, psi, mthsurf)
549-
end
550-
551474
"""
552475
compute_island_diagnostics!(state::PerturbedEquilibriumState, n_rational::Int)
553476

0 commit comments

Comments
 (0)