@@ -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)
329323where the integral is computed via flux surface integration:
330324integral = ∫ (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
352328Uses 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
505472end
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