src/equilibrium.f90: give the poloidal flux a definition domain

This change limits the evaluation of the poloidal flux spline ψ(R,z) to
a particular domain where ψ(R,z) is strictly monotonic. This choice
ensures that ψ_n < 1 only inside the plasma boundary; so plasma
parameters like q(ψ), Te(ψ), etc. can be mapped to the physical space
without ambiguities.

Before this change, anywhere ψ_n happened to decrease below 1 (either as
a result of a physical current or simply from the spline extrapolation),
Gray would effectively create a spurious plasma region.
This behavior is seriously problematic because it completely invalidates
the simulation: it can alter the ray directions, the power disribution
and total power if the beam enters one such region.

The domain is computed by applying a transformation to the contour of
the plasma boundary: for each contour point we cast a ray from the
magnetic axis to that point and extend the ray until the restriction of
ψ(R,z) on the ray starts decreasing or reach a maximum scaling factor.
The endpoint of the ray is then taken as the new point.
If ψ(R,z) is globally monotonic, the transformation is a homotety wrt
the magnetic axis, so the domain will be an enlarged boundary; otherwise
the shape will be more irregular (an intersection of the enlarged
boundary and several level curves of ψ).

Finally, each `pol_flux(r, z)` call is now guarded behind a check
`inside(psi_domain, r, z)`. For points outside the domain the subroutine
returns, as usual, -1 for ψ and 0 for derivatives.
This commit is contained in:
Michele Guerini Rocco 2024-01-19 14:36:01 +01:00
parent 6fad08ed7c
commit 26976a31dd
Signed by: rnhmjoj
GPG Key ID: BFBAF4C975F76450

View File

@ -22,6 +22,12 @@ module equilibrium
real(wp_) :: B0 ! Magnetic field at the magnetic axis (T) real(wp_) :: B0 ! Magnetic field at the magnetic axis (T)
end type end type
! A 2D contour in the (R,z) plane
type contour
real(wp_), allocatable :: R(:)
real(wp_), allocatable :: z(:)
end type
! Order of the splines ! Order of the splines
integer, parameter :: kspl=3, ksplp=kspl + 1 integer, parameter :: kspl=3, ksplp=kspl + 1
@ -36,6 +42,11 @@ module equilibrium
! Analytic model ! Analytic model
type(analytic_model), save :: model type(analytic_model), save :: model
! Contour of the region where the ψ(R,z) spline is monotonically
! increasing. The mapping from ψ to other plasma parameters is
! physically meaningful only inside this domain.
type(contour), save :: psi_domain
! More parameters ! More parameters
real(wp_), save :: fpolas ! Poloidal current at the edge (r=a), F_a real(wp_), save :: fpolas ! Poloidal current at the edge (r=a), F_a
real(wp_), save :: psia ! Poloidal flux at the edge (r=a), ψ_a real(wp_), save :: psia ! Poloidal flux at the edge (r=a), ψ_a
@ -45,8 +56,8 @@ module equilibrium
real(wp_), save :: sgnbphi ! Sign of B_φ (>0 means CCW) real(wp_), save :: sgnbphi ! Sign of B_φ (>0 means CCW)
real(wp_), save :: btrcen ! used only for jcd_astra def. real(wp_), save :: btrcen ! used only for jcd_astra def.
real(wp_), save :: rcen ! Major radius R, computed as fpolas/btrcen real(wp_), save :: rcen ! Major radius R, computed as fpolas/btrcen
real(wp_), save :: rmnm, rmxm ! R interval of the equilibrium definition real(wp_), save :: rmnm, rmxm ! R interval of the equilibrium grid
real(wp_), save :: zmnm, zmxm ! z interval of the equilibrium definition real(wp_), save :: zmnm, zmxm ! z interval of the equilibrium grid
real(wp_), save :: zbinf, zbsup ! z interval of the plasma boundary real(wp_), save :: zbinf, zbsup ! z interval of the plasma boundary
private private
@ -538,6 +549,13 @@ contains
call psi_spline%init_deriv(nr, nz, 2, 0) ! ²ψ/R² call psi_spline%init_deriv(nr, nz, 2, 0) ! ²ψ/R²
call psi_spline%init_deriv(nr, nz, 0, 2) ! ²ψ/z² call psi_spline%init_deriv(nr, nz, 0, 2) ! ²ψ/z²
! set the initial ψ(R,z) domain to the grid boundary
!
! Note: this is required to bootstrap `flux_pol` calls
! within this very subroutine.
psi_domain%R = [rmnm, rmnm, rmxm, rmxm]
psi_domain%z = [zmnm, zmxm, zmxm, zmnm]
! Spline interpolation of F(ψ) ! Spline interpolation of F(ψ)
! give a small weight to the last point ! give a small weight to the last point
@ -672,9 +690,67 @@ contains
call setqphi_num(data%psinr, abs(data%qpsi), abs(psia), rhotn) call setqphi_num(data%psinr, abs(data%qpsi), abs(psia), rhotn)
call set_rho_spline(sqrt(data%psinr), rhotn) call set_rho_spline(sqrt(data%psinr), rhotn)
! Compute the domain of the ψ mapping
psi_domain%R = data%rbnd
psi_domain%z = data%zbnd
call rescale_boundary(psi_domain, O=[rmaxis, zmaxis], t0=1.5_wp_)
end subroutine set_equil_spline end subroutine set_equil_spline
subroutine rescale_boundary(cont, O, t0)
! Given the plasma boundary contour `cont`, the position of the
! magnetic axis `O`, and a scaling factor `t0`; this subroutine
! rescales the contour by `t0` about `O` while ensuring the
! psi_spline stays monotonic within the new boundary.
implicit none
! subroutine arguments
type(contour), intent(inout) :: cont ! (R,z) contour
real(wp_), intent(in) :: O(2) ! center point
real(wp_), intent(in) :: t0 ! scaling factor
! subroutine variables
integer :: i
real(wp_) :: t
real(wp_), parameter :: dt = 0.05
real(wp_) :: P(2), N(2)
do i = 1, size(cont%R)
! For each point on the contour compute:
P = [cont%R(i), cont%z(i)] ! point on the contour
N = P - O ! direction of the line from O to P
! Find the max t: s(t) = ψ(O + tN) is monotonic in [1, t]
t = 1
do while (t < t0)
if (s(t + dt) < s(t)) exit
t = t + dt
end do
! The final point is thus O + tN
P = O + t * N
cont%R(i) = P(1)
cont%z(i) = P(2)
end do
contains
function s(t)
! Rescriction of ψ(R, z) on the line Q(t) = O + tN
implicit none
real(wp_), intent(in) :: t
real(wp_) :: s, Q(2)
Q = O + t * N
s = psi_spline%eval(Q(1), Q(2))
end function
end subroutine rescale_boundary
subroutine scatterspl(x,y,z,w,n,kspl,sspl,xmin,xmax,ymin,ymax, & subroutine scatterspl(x,y,z,w,n,kspl,sspl,xmin,xmax,ymin,ymax, &
tx,nknt_x,ty,nknt_y,coeff,ierr) tx,nknt_x,ty,nknt_y,coeff,ierr)
! Computes the spline interpolation of a surface when ! Computes the spline interpolation of a surface when
@ -834,6 +910,7 @@ contains
! !
! Note: all output arguments are optional. ! Note: all output arguments are optional.
use gray_params, only : iequil use gray_params, only : iequil
use reflections, only : inside
use const_and_precisions, only : one, pi use const_and_precisions, only : one, pi
implicit none implicit none
@ -960,8 +1037,7 @@ contains
if (present(ddpsidrz)) ddpsidrz = ddpsidphidr * dphidz + dpsidphi * ddphidrdz if (present(ddpsidrz)) ddpsidrz = ddpsidphidr * dphidz + dpsidphi * ddphidrdz
else else
! Numerical data ! Numerical data
if (R <= rmxm .and. R >= rmnm .and. & if (inside(psi_domain%R, psi_domain%z, psi_domain%npoints, R, z)) then
z <= zmxm .and. z >= zmnm) then
! Within the interpolation range ! Within the interpolation range
if (present(psi_n)) psi_n = psi_spline%eval(R, z) if (present(psi_n)) psi_n = psi_spline%eval(R, z)
if (present(dpsidr)) dpsidr = psi_spline%deriv(R, z, 1, 0) if (present(dpsidr)) dpsidr = psi_spline%deriv(R, z, 1, 0)
@ -1431,6 +1507,9 @@ contains
call q_spline%deinit call q_spline%deinit
call rhop_spline%deinit call rhop_spline%deinit
call rhot_spline%deinit call rhot_spline%deinit
if (allocated(psi_domain%R)) deallocate(psi_domain%R, psi_domain%z)
end subroutine unset_equil_spline end subroutine unset_equil_spline
end module equilibrium end module equilibrium