src/polarization.f90: rewrite

- Remove the Stokes parameters as an intermediate step in the
  conversion between Jones vectors and polarisation ellipses.

- Document every single step performed when converting between
  different parametrisations and how the polarisation at the
  plasma boundary is computed. This includes how everything
  was derived from first principles.

- Mark the subroutines as pure.

- Remove `set_pol` entirely.
This commit is contained in:
Michele Guerini Rocco 2024-03-12 16:42:19 +01:00
parent f82f91bc8d
commit 0a87a3ef76
Signed by: rnhmjoj
GPG Key ID: BFBAF4C975F76450
3 changed files with 277 additions and 116 deletions

View File

@ -11,6 +11,7 @@ contains
use const_and_precisions, only : zero, one, degree, comp_tiny
use coreprofiles, only : temp, fzeff
use dispersion, only : expinit
use polarization, only : ellipse_to_field
use gray_params, only : gray_parameters, gray_data, gray_results, &
print_parameters, EQ_VACUUM
use beams, only : xgygcoeff, launchangles2n
@ -296,7 +297,8 @@ contains
if(ent_pl) then ! ray enters plasma
write (msg, '(" ray ",g0," entered plasma (",g0," steps)")') jk, i
call log_debug(msg, mod='gray_core', proc='gray_main')
call set_pol(psipv(parent_index_rt), chipv(parent_index_rt), ext, eyt) ! compute polarisation and couplings
call ellipse_to_field(psipv(parent_index_rt), chipv(parent_index_rt), & ! compute polarisation and couplings
ext, eyt)
call plasma_in(jk, xv, anv, bres, sox, cpl, psipol, chipol, iop, ext, eyt, &
perfect=.not. params%raytracing%ipol &
.and. params%antenna%iox == iox &
@ -1838,30 +1840,6 @@ contains
end subroutine alpha_effj
subroutine set_pol(psi, chi, e_x, e_y)
! Compute the polarisation vector for each ray from the ellipse angles ψ, χ
use const_and_precisions, only : degree, im
use polarization, only : stokes_ell
! subroutine arguments
real(wp_), intent(in) :: psi, chi
complex(wp_), intent(out) :: e_x(:), e_y(:)
! local variables
real(wp_) :: qq, uu, vv, deltapol
call stokes_ell(chi*degree, psi*degree, qq, uu, vv)
if(qq**2 < 1) then
deltapol = asin(vv/sqrt(1 - qq**2))
e_x = sqrt((1 + qq)/2)
e_y = sqrt((1 - qq)/2) * exp(-im * deltapol)
else
e_x = 1
e_y = 0
end if
end subroutine set_pol
subroutine cniteq(rqgrid,zqgrid,matr2dgrid,nr,nz,h,ncon,npts,icount,rcon,zcon)
! v2.01 12/07/95 -- written by d v bartlett, jet joint undertaking.
! (based on an older code)

View File

@ -2,7 +2,7 @@ module multipass
use const_and_precisions, only : wp_, zero, half, one, degree, czero
use beamdata, only : dst, nray
use gray_params, only : ipass
use polarization, only : pol_limit, stokes_ce, polellipse
use polarization, only : pol_limit, field_to_ellipse
use reflections, only : wall_refl
use equilibrium, only : bfield
@ -52,12 +52,7 @@ contains
if(i == 1) then
! For the central ray, compute the polarization ellipse
chi = asin(2*imag(e_mode(1) * conjg(e_mode(2)))) / 2
psi = atan2(real(2 * e_mode(1) * conjg(e_mode(2))), &
real(dot_product(e_mode, conjg(e_mode)))) / 2
! and convert from radians to degree
psi = psi / degree
chi = chi / degree
call field_to_ellipse(e_mode(1), e_mode(2), psi, chi)
else
psi = 0
chi = 0
@ -124,7 +119,6 @@ contains
complex(wp_), dimension(:), intent(inout), pointer :: ext,eyt
! local variables
integer :: irfl
real(wp_) :: qq1,uu1,vv1
real(wp_), dimension(3) :: xvrfl,anvrfl,walln
complex(wp_) :: ext1,eyt1
!
@ -136,11 +130,8 @@ contains
xv = xvrfl
anv = anvrfl
if(i.eq.1) then ! polarization angles at wall reflection for central ray
call stokes_ce(ext1,eyt1,qq1,uu1,vv1)
call polellipse(qq1,uu1,vv1,psipol1,chipol1)
psipol1=psipol1/degree ! convert from rad to degree
chipol1=chipol1/degree
if(i == 1) then ! polarization angles at wall reflection for central ray
call field_to_ellipse(ext1, eyt1, psipol1, chipol1)
else
psipol1 = zero
chipol1 = zero

View File

@ -1,100 +1,292 @@
! This module contains subroutines to convert between different descriptions
! of the wave polarisation and to compute the polarisation of the plasma modes
module polarization
use const_and_precisions, only : wp_, pi, im
implicit none
interface stokes
module procedure stokes_ce,stokes_ell
end interface
private
public ellipse_to_field, field_to_ellipse ! Converting between descriptions
public pol_limit ! Plasma modes polarisations
contains
subroutine stokes_ce(ext,eyt,qq,uu,vv)
use const_and_precisions, only : wp_
! arguments
complex(wp_), intent(in) :: ext,eyt
real(wp_), intent(out) :: qq,uu,vv
qq = abs(ext)**2 - abs(eyt)**2
uu = 2*real(ext*conjg(eyt))
vv = 2*imag(ext*conjg(eyt))
end subroutine stokes_ce
pure subroutine ellipse_to_field(psi, chi, e_x, e_y)
! Computes the normalised Jones vector from the
! polarisation ellipse angles ψ, χ
!
! Notes:
! - ψ[-π/2, π/2] is the angle between the x and the major axis
! - χ[-π/4, π/4] is defined by tan(χ) = b/a, where a,b are the
! major,minor semi-axis, respectively; χ>0 for positive helicity
! (left-handed wave), χ<0 for negative helicity (right-handed wave).
! subroutine arguments
real(wp_), intent(in) :: psi, chi
complex(wp_), intent(out) :: e_x(:), e_y(:)
! The Eikonal ansatz is:
!
! (, t) = Re () exp(-ikS() + iωt)
!
! where () = [|e|exp(), |e|exp(), 0], since the wave
! is transversal in vacuum. At a fixed position =0, ignoring
! the third component, we have:
!
! (0, t) = [|e|cos(φ + ωt), |e|cos(φ + ωt)]
! = [|e|cos(φ)cos(ωt) - |e|sin(φ)sin(ωt),
! |e|cos(φ)cos(ωt) - |e|sin(φ)sin(ωt)]
!
! Then, we compare this to the parametric equation of
! an ellipse rotated by ψ through the origin,
!
! (t) = R(ψ) [acos(ωt), bsin(ωt)]
! = [cos(ψ)acos(ωt), -sin(ψ)bsin(ωt),
! sin(ψ)acos(ωt), cos(ψ)bsin(ωt)]
!
! at ωt=0 and ωt=π/2, so:
!
! 1. |e|cos(φ) = acos(ψ)
! 2. |e|cos(φ) = asin(ψ)
! 3. |e|sin(φ) = bsin(ψ)
! 4. |e|sin(φ) = -bcos(ψ)
!
! From 1²+3² and 2²+4² we have
!
! |e|² = a²cos(ψ)²+b²sin(ψ)²
! |e|² = a²sin(ψ)²+b²cos(ψ)²
!
! |e|² + |e|² = a² + b²
!
! Assuming is normalised, that is *=|e|²+|e|²=1,
! we have a²+b²=1, so we can define a=cos(χ), b=sin(χ).
!
! We can then rewrite:
!
! 1. Re e = cos(χ)cos(ψ)
! 2. Re e = cos(χ)sin(ψ)
! 3. Im e = +sin(χ)sin(ψ)
! 4. Im e = -sin(χ)cos(ψ)
!
! from which:
!
! e = cos(χ)cos(ψ) + isin(χ)sin(ψ)
! e = cos(χ)sin(ψ) - isin(χ)cos(ψ)
!
e_x = cosd(chi)*cosd(psi) + im * sind(chi)*sind(psi)
e_y = cosd(chi)*sind(psi) - im * sind(chi)*cosd(psi)
end subroutine ellipse_to_field
subroutine stokes_ell(chi,psi,qq,uu,vv)
use const_and_precisions, only : wp_,two
! arguments
real(wp_), intent(in) :: chi,psi
real(wp_), intent(out) :: qq,uu,vv
pure subroutine field_to_ellipse(e_x, e_y, psi, chi)
! Computes the polarisation ellipse angles ψ, χ
! from the normalised Jones vector
qq=cos(two*chi)*cos(two*psi)
uu=cos(two*chi)*sin(two*psi)
vv=sin(two*chi)
end subroutine stokes_ell
! subroutine arguments
complex(wp_), intent(in) :: e_x, e_y
real(wp_), intent(out) :: psi, chi
! First, to make the map (ψ, χ) unit ellipse
! unique we restricted its domain to
! [-π/2, π/2]×[-π/4, π/4]. Then, starting from
!
! e = cos(χ)cos(ψ) + isin(χ)sin(ψ)
! e = cos(χ)sin(ψ) - isin(χ)cos(ψ)
!
! (see `ellipse_to_field`), we obtain χ doing
!
! 2ee* = cos(2χ)sin(2ψ) + i sin(2χ)
! χ = ½ asin(Im(2ee*))
!
! This gives the angle χ in the correct interval since
! the range of asin is [-π/2, π/2] by definition.
!
! For ψ notice that:
!
! ee = e²-e² = cos(χ)²cos(2ψ) - sin(χ)²cos(2ψ)
! = cos(2χ)cos(2ψ)
!
! Combining this to the previous expression:
!
! tan(2ψ) = Re(2ee*)/(ee)
!
! Finally, since atan2 gives the principal branch
! (-π, π], ψ is given within the correct interval as:
!
! ψ = ½ atan2(Re(2ee*), (ee))
!
chi = asind(imag(2 * e_x * conjg(e_y))) / 2
psi = atan2d(real(2 * e_x * conjg(e_y)), abs(e_x)**2 - abs(e_y)**2) / 2
end subroutine field_to_ellipse
subroutine polellipse(qq,uu,vv,psi,chi)
use const_and_precisions, only : wp_,half
! arguments
real(wp_), intent(in) :: qq,uu,vv
real(wp_), intent(out) :: psi,chi
! real(wp_) :: ll,aa,bb,ell
pure subroutine pol_limit(N, B, Bres, sox, e_x, e_y)
! Computes the Jones vectors of the cold plasma dispersion
! relation in the limit of vanishing electron density
!
! Note: the Jones vectors are given in the local beam frame,
! that is, the z axis is aligned with the wave vector and x axis
! lies in the tokamak equatorial plane.
! This allows to directly compare the beam polarisation with
! the plasma modes Jones vectors to obtain the power couplings.
! ll = sqrt(qq**2 + uu**2)
! aa = sqrt(half*(1 + ll))
! bb = sqrt(half*(1 - ll))
! ell = bb/aa
psi = half*atan2(uu,qq)
chi = half*asin(vv)
end subroutine polellipse
! subroutine arguments
real(wp_), intent(in) :: N(3) ! refractive index
real(wp_), intent(in) :: B(3) ! magnetic field
real(wp_), intent(in) :: Bres ! resonant magnetic field
! sign of polarisation mode: -1 O, +1 X
integer, intent(in) :: sox
! Components of the Jones vector
complex(wp_), intent(out) :: e_x, e_y
subroutine pol_limit(anv,bv,bres,sox,ext,eyt) !,gam)
use const_and_precisions, only : wp_,ui=>im,zero,one
! arguments
real(wp_), dimension(3), intent(in) :: anv,bv
real(wp_), intent(in) :: bres
integer, intent(in) :: sox
complex(wp_), intent(out) :: ext,eyt
! real(wp_), optional, intent(out) :: gam
! local variables
real(wp_), dimension(3) :: bnv
real(wp_) :: anx,any,anz,an2,an,anpl2,anpl,anpr,anxy, &
btot,yg,den,dnl,del0,ff,ff2,sngam,csgam
!
btot = sqrt(bv(1)**2+bv(2)**2+bv(3)**2)
bnv = bv/btot
yg = btot/bres
! local variables
real(wp_) :: Y, Npl, delta
real(wp_) :: z(3), R(2,2), gamma
complex(wp_) :: f, e(2)
anx = anv(1)
any = anv(2)
anz = anv(3)
an2 = anx**2 + any**2 + anz**2
an = sqrt(an2)
anxy = sqrt(anx**2 + any**2)
Y = norm2(B) / Bres ! Y = ω_ce/ω
Npl = dot_product(N, B) / norm2(B) ! N = /B
anpl = (anv(1)*bnv(1) + anv(2)*bnv(2) + anv(3)*bnv(3))
anpl2= anpl**2
anpr = sqrt(an2 - anpl2)
! Coordinate systems in use
! 1. plasma basis, definition of dielectric tensor
! = /N
! = /B×/N
! = /B
! 2. polarisation basis, definition of /
! = -/N×(/B×/N)= -/B
! = /B×/N
! = /N
! 3. beam basis, definition of rays initial conditions
! = /N×
! = /N×(/N×)
! = /N
dnl = one - anpl2
del0 = sqrt(dnl**2 + 4.0_wp_*anpl2/yg**2)
sngam = (anz*anpl - an2*bnv(3))/(an*anxy*anpr)
csgam = -(any*bnv(1) - anx*bnv(2))/ (anxy*anpr)
ff = 0.5_wp_*yg*(dnl - sox*del0)
ff2 = ff**2
den = ff2 + anpl2
if (den>zero) then
ext = (ff*csgam - ui*anpl*sngam)/sqrt(den)
eyt = (-ff*sngam - ui*anpl*csgam)/sqrt(den)
den = sqrt(abs(ext)**2+abs(eyt)**2)
ext = ext/den
eyt = eyt/den
else ! only for XM (sox=+1) when N//=0
ext = -ui*sngam
eyt = -ui*csgam
! The cold plasma dispersion relation tensor in the plasma
! basis (the usual one) is:
!
! [ (R+L)/2 - N² -i(R-L)/2 NN ]
! Λ = [i(R-L)/2 (R+L)/2 - N² 0 ]
! [NN 0 P - N²]
!
! where P = 1 - X
! L = 1 - X/(1+Y)
! R = 1 - X/(1-Y)
! X = (ω_p/ω)²
! Y = ω_c/ω
!
! To compute the polarisation (the ratio / in the wave
! transverse plane) we switch to the polarisation basis.
! The relations between the new and old unit vectors are:
!
! ' =
! ' = /N = ( + )/N = (N/N) + (N/N)
! ' = y̅'×' = y̅×z̅' = (N/N) - (N/N)
!
! so the change of basis matrix is:
!
! [ N/N 0 N/N]
! M = [ 0 1 0 ]
! [-N/N 0 N/N]
!
! which is equivalent to a clockwise rotation of the
! plane around by the pitch angle θ: = Ncosθ.
!
! Consequently the tensor Λ in this basis is given by:
!
! Λ' = M¹ Λ M
!
! Substituting the solution of the dispersion relation
! N=N(X, Y, N, ±) into Λ' and taking lim X0 yields:
!
! [ ½XY²(N²-1Δ) -iXYN XY²N(1-N²)]
! Λ' = [ iXYN ½XY²(1-N²Δ) iXY(1-N²)] + o(X²)
! [XY²N(1-N²) -iXY(1-N²) -XY²N²+X+Y²-1]
!
! where Δ = [4N²/Y² + (1 - N²)²]
!
! At exactly X=0, Λ' reduces to diag(0,0,1), so for Λ'E=0 we must
! have E=0, meaning we can effectively ignore the third column.
!
! The polarisation is determined by the ratio
!
! / = -Λ/Λ = -i Y[N² -1 Δ(X=0)]/(2N)
! = i [Y(N²-1) ± Δ']/(2N)
! i f±
!
! where Δ' = (X=0) = [4N² + Y²(1 - N²)²]
!
if (sox == -1 .and. Npl == 0) then
! If =0, the direction of the electric field is = (1, 0).
! This happens for O mode at N=0, the polarisation here is
! linear with .
! This case is handled separately to avoid a division by zero.
e = [1, 0]
else
! In general, if 0, the direction of the electric field is
!
! (, ) ~ (/, 1) ~ (iẼ/, i) = (f±, i)
!
! so, the polarisation is elliptical and the Jones vector is:
!
! = (f±, i) / (1 + f±²)
!
delta = sqrt(4*Npl**2 + Y**2*(1 - Npl**2)**2)
f = (Y*(Npl**2 - 1) + sox * delta) / (2*Npl)
e = [f, im] / sqrt(1 + f**2)
end if
! gam = atan2(sngam,csgam)/degree
! To obtain the polarisation in the beam basis we must compute the
! angle γ such that a rotation of γ around =/N aligns the axis
! of the wave transverse plane to the equatorial plane of the
! tokamak, that is:
!
! R(γ,)() = 0
!
! where R(γ,) is given by Rodrigues' rotation formula;
! = -/B (x axis in polarisation basis);
! = /N is (z axis in polarization basis)
! is the vertical direction (standard basis).
!
! Since the rotation is a linear operation,
!
! R(γ,)(-/B) = 0 R(γ,) = 0
!
! and the condition simplifies to:
!
! R(γ,)
! = [cosγ + ×sinγ + ()(1-cosγ)]
! = (cosγ + ×sinγ)
! = Bcosγ + (×)sinγ
! = 0
!
! By definition = ×(×)=-(), so:
!
! B = - ()
!
! (×) = [×(×(×))]
! = [-(×(×))×]
! = -(×)
! = (zB-zB)
!
! Substituting, we obtain:
!
! [ - ()]cosγ + (zB-zB)sinγ = 0
!
! γ = atan2(()-, zB-zB)
!
z = N / norm2(N)
gamma = atan2(dot_product(B, z) * z(3) - B(3), z(1)*B(2) - z(2)*B(1))
! Apply the rotation R(γ,) to the Jones vector
!
! Note: Jones vectors transform as normal vectors under rotations
! in physical space (SO(3) group); but as spinors on the Poincaré
! sphere (SU(2) group). For example, a rotation by γ around
! changes the longitude φ=2ψ by 2γ.
R = reshape([cos(gamma), -sin(gamma), sin(gamma), cos(gamma)], [2, 2])
e = matmul(R, e)
e_x = e(1)
e_y = e(2)
end subroutine pol_limit
end module polarization