Skip to content
Snippets Groups Projects
numerics_mod.F90 12.1 KiB
Newer Older
!! MODULE NUMERICS
!   The module numerics contains a set of routines that are called only once at
! the beginng of a run. These routines do not need to be optimzed
    PUBLIC :: build_dnjs_table, evaluate_kernels, evaluate_EM_op
    PUBLIC :: compute_lin_coeff, build_dv4Hp_table

CONTAINS

!******************************************************************************!
!!!!!!! Build the Laguerre-Laguerre coupling coefficient table for nonlin
!******************************************************************************!
SUBROUTINE build_dnjs_table
  USE array, ONLY : dnjs
  USE FMZM,  ONLY : TO_DP
  USE coeff, ONLY : ALL2L
  IMPLICIT NONE

  INTEGER :: in, ij, is, J
  INTEGER :: n_, j_, s_

  DO in = 1,J+1 ! Nested dependent loops to make benefit from dnjs symmetrys
    n_ = in - 1
    DO ij = in,J+1
      j_ = ij - 1
      DO is = ij,J+1
        s_ = is - 1

        dnjs(in,ij,is) = TO_DP(ALL2L(n_,j_,s_,0))
        ! By symmetry
        dnjs(in,is,ij) = dnjs(in,ij,is)
        dnjs(ij,in,is) = dnjs(in,ij,is)
        dnjs(ij,is,in) = dnjs(in,ij,is)
        dnjs(is,ij,in) = dnjs(in,ij,is)
        dnjs(is,in,ij) = dnjs(in,ij,is)
      ENDDO
    ENDDO
  ENDDO
END SUBROUTINE build_dnjs_table

!******************************************************************************!
!!!!!!! Build the fourth derivative Hermite coefficient table
!******************************************************************************!
SUBROUTINE build_dv4Hp_table
  USE array,      ONLY: dv4_Hp_coeff
  USE grid,       ONLY: pmax
  USE prec_const, ONLY: dp, PI
  IMPLICIT NONE
    if (p_ < 4) THEN
      dv4_Hp_coeff(p_) = 0._dp
    ELSE
      dv4_Hp_coeff(p_) = 4_dp*SQRT(REAL((p_-3)*(p_-2)*(p_-1)*p_,dp))
    ENDIF
  ENDDO
   !we scale it w.r.t. to the max degree since
   !D_4^{v}\sim (\Delta v/2)^4 and \Delta v \sim 2pi/kvpar = pi/\sqrt{2P}
   ! dv4_Hp_coeff = dv4_Hp_coeff*(1._dp/2._dp/SQRT(REAL(pmax,dp)))**4
   dv4_Hp_coeff = dv4_Hp_coeff*(PI/2._dp/SQRT(2._dp*REAL(pmax,dp)))**4
END SUBROUTINE build_dv4Hp_table
!******************************************************************************!

!******************************************************************************!
!!!!!!! Evaluate the kernels once for all
!******************************************************************************!
SUBROUTINE evaluate_kernels
  USE basic
  USE array,   ONLY : kernel, HF_phi_correction_operator
  USE grid,    ONLY : local_Na, local_Nj,Ngj, local_nkx, local_nky, local_nz, Ngz, jarray, kparray
  USE species, ONLY : sigma2_tau_o2
  USE prec_const, ONLY: dp
  INTEGER    :: j_int, ia, eo, ikx, iky, iz, ij
DO ia  = 1,local_Na
  DO eo  = 1,2
  DO ikx = 1,local_nkx
  DO iky = 1,local_nky
  DO iz  = 1,local_nz + Ngz
    DO ij = 1, local_Nj + Ngj
      j_int = jarray(ij)
      j_dp  = REAL(j_int,dp)
      y_    =  sigma2_tau_o2(ia) * kparray(iky,ikx,iz,eo)**2
      IF(j_int .LT. 0) THEN
        kernel(ia,ij,iky,ikx,iz,eo) = 0._dp
      ELSE
        factj = GAMMA(j_dp+1._dp)
        kernel(ia,ij,iky,ikx,iz,eo) = y_**j_int*EXP(-y_)/factj
      ENDIF
    ENDDO
    ! IF (ijs .EQ. 1) & ! if ijs is 1, the ghost kernel has negative index
      ! kernel(ia,ijgs,iky,ikx,iz,eo) = 0._dp
  ENDDO
  ENDDO
  !! Correction term for the evaluation of the heat flux
  HF_phi_correction_operator(:,:,:) = &
         2._dp * Kernel(ia,1,:,:,:,1) &
        -1._dp * Kernel(ia,2,:,:,:,1)
  DO ij = 1, local_Nj
    j_int = jarray(ij)
    j_dp  = REAL(j_int,dp)
    HF_phi_correction_operator(:,:,:) = HF_phi_correction_operator(:,:,:) &
    - Kernel(ia,ij,:,:,:,1) * (&
        2._dp*(j_dp+1.5_dp) * Kernel(ia,ij  ,:,:,:,1) &
        -     (j_dp+1.0_dp) * Kernel(ia,ij+1,:,:,:,1) &
        -              j_dp * Kernel(ia,ij-1,:,:,:,1))
  ENDDO
END SUBROUTINE evaluate_kernels
!******************************************************************************!

!******************************************************************************!
SUBROUTINE evaluate_EM_op
  IMPLICIT NONE

  CALL evaluate_poisson_op
  CALL evaluate_ampere_op

END SUBROUTINE evaluate_EM_op
!!!!!!! Evaluate inverse polarisation operator for Poisson equation
!******************************************************************************!
SUBROUTINE evaluate_poisson_op
  USE basic
  USE array,   ONLY : kernel, inv_poisson_op, inv_pol_ion
  USE grid,    ONLY : local_Na, local_nkx, local_nky, local_nz,&
                      kxarray, kyarray, jmax
  USE species, ONLY : q2_tau
  USE model,   ONLY : ADIAB_E, tau_e
  USE prec_const, ONLY: dp
  REAL(dp)    :: pol_ion, pol_tot, operator, operator_ion     ! (Z^2/tau (1-sum_n kernel_na^2))
  INTEGER     :: in,ikx,iky,iz,ia
  ! This term has no staggered grid dependence. It is evalued for the
  ! even z grid since poisson uses p=0 moments and phi only.
  kxloop: DO ikx = 1,local_nkx
  kyloop: DO iky = 1,local_nky
  zloop:  DO iz  = 1,local_nz
  IF( (kxarray(ikx).EQ.0._dp) .AND. (kyarray(iky).EQ.0._dp) ) THEN
      inv_poisson_op(iky, ikx, iz) =  0._dp
    operator = 0._dp
    DO ia = 1,local_na ! sum over species
      pol_tot = 0._dp  ! total polarisation term
      pol_ion = 0._dp  ! sum of ion polarisation term
      ! loop over n only up to the max polynomial degree
      DO in=1,jmax+1
        pol_tot = pol_tot  + q2_tau(ia)*kernel(ia,in,iky,ikx,iz,0)**2 ! ... sum recursively ...
        pol_ion = pol_ion  + q2_tau(ia)*kernel(ia,in,iky,ikx,iz,0)**2 !
      END DO
      operator = operator + q2_tau(ia) - pol_tot
    ENDDO
    IF(ADIAB_E) THEN ! Adiabatic model
      pol_tot = pol_tot +  1._dp/tau_e - 1._dp
    inv_poisson_op(iky, ikx, iz) =  1._dp/operator
    inv_pol_ion   (iky, ikx, iz) =  1._dp/operator_ion
  ENDIF
  END DO zloop
  END DO kyloop
  END DO kxloop

END SUBROUTINE evaluate_poisson_op
!******************************************************************************!

!******************************************************************************!
!!!!!!! Evaluate inverse polarisation operator for Poisson equation
!******************************************************************************!
SUBROUTINE evaluate_ampere_op
  USE prec_const,   ONLY : dp
  USE array,    ONLY : kernel, inv_ampere_op
  USE grid,     ONLY : local_Na, local_nkx, local_nky, local_nz, &
                       jmax, kparray, kxarray, kyarray, SOLVE_AMPERE
  USE model,    ONLY : beta
  USE species,  ONLY : q, sigma
  USE geometry, ONLY : hatB
  REAL(dp)    :: pol_tot, kperp2     ! (Z^2/tau (1-sum_n kernel_na^2))
  INTEGER     :: in,ikx,iky,iz,ia
  ! We do not solve Ampere if beta = 0 to spare waste of ressources
  IF(SOLVE_AMPERE) THEN
    DO ikx = 1,local_nkx
    DO iky = 1,local_nky
    DO iz  = 1,local_nz
    kperp2 = kparray(iky,ikx,iz,1)**2
    IF( (kxarray(ikx).EQ.0._dp) .AND. (kyarray(iky).EQ.0._dp) ) THEN
        inv_ampere_op(iky, ikx, iz) =  0._dp
    ELSE
      !!!!!!!!!!!!!!!!! Ion contribution
      pol_tot = 0._dp
      DO ia  = 1,local_na
        ! loop over n only up to the max polynomial degree
        DO in=1,jmax+1
          pol_tot = pol_tot  + q(ia)**2/(sigma(ia)**2)*kernel(ia,in,iky,ikx,iz,1)**2 ! ... sum recursively ...
        END DO
      inv_ampere_op(iky, ikx, iz) =  1._dp/(2._dp*kperp2*hatB(iz,0)**2 + beta*pol_tot)
  ENDIF
END SUBROUTINE evaluate_ampere_op
!******************************************************************************!

SUBROUTINE compute_lin_coeff
  USE array, ONLY:  xnapj, &
                    ynapp1j, ynapm1j, ynapp1jm1, ynapm1jm1,&
                    zNapm1j, zNapm1jp1, zNapm1jm1,&
                    xnapj, xnapjp1, xnapjm1,&
                    xnapp1j, xnapm1j, xnapp2j, xnapm2j,&
                    xphij, xphijp1, xphijm1,&
                    xpsij, xpsijp1, xpsijm1
  USE species, ONLY: k_T, k_N, tau, q, sqrtTau_q, tau_q
  USE model,   ONLY: k_cB, k_gB
  USE prec_const, ONLY: dp, SQRT2, SQRT3
  USE grid,  ONLY: parray, jarray, local_na, local_np, local_nj
  INTEGER     :: ia,ip,ij,p_int, j_int ! polynom. dagrees
  REAL(dp)    :: p_dp, j_dp
  !! linear coefficients for moment RHS !!!!!!!!!!
  DO ia = 1,local_na
    DO ip = 1, local_np
      p_int= parray(ip)   ! Hermite degree
      p_dp = REAL(p_int,dp) ! REAL of Hermite degree
      DO ij = 1, local_nj
        j_int= jarray(ij)   ! Laguerre degree
        j_dp = REAL(j_int,dp) ! REAL of Laguerre degree
        ! All Napj terms
        xnapj(ia,ip,ij) = tau(ia)/q(ia)*(k_cB*(2._dp*p_dp + 1._dp) &
                               +k_gB*(2._dp*j_dp + 1._dp))
        ! Mirror force terms
        ynapp1j  (ia,ip,ij) = -sqrtTau_q(ia) *      (j_dp+1._dp)*SQRT(p_dp+1._dp)
        ynapm1j  (ia,ip,ij) = -sqrtTau_q(ia) *      (j_dp+1._dp)*SQRT(p_dp)
        ynapp1jm1(ia,ip,ij) = +sqrtTau_q(ia) *              j_dp*SQRT(p_dp+1._dp)
        ynapm1jm1(ia,ip,ij) = +sqrtTau_q(ia) *              j_dp*SQRT(p_dp)
        ! Trapping terms
        zNapm1j  (ia,ip,ij) = +sqrtTau_q(ia) *(2._dp*j_dp+1._dp)*SQRT(p_dp)
        zNapm1jp1(ia,ip,ij) = -sqrtTau_q(ia) *      (j_dp+1._dp)*SQRT(p_dp)
        zNapm1jm1(ia,ip,ij) = -sqrtTau_q(ia) *              j_dp*SQRT(p_dp)
    ENDDO
    DO ip = 1, local_np
      p_int= parray(ip)   ! Hermite degree
      p_dp = REAL(p_int,dp) ! REAL of Hermite degree
      ! Landau damping coefficients (ddz napj term)
      xnapp1j(ia,ip) = sqrtTau_q(ia) * SQRT(p_dp+1._dp)
      xnapm1j(ia,ip) = sqrtTau_q(ia) * SQRT(p_dp)
      ! Magnetic curvature term
      xnapp2j(ia,ip) = tau_q(ia) * k_cB * SQRT((p_dp+1._dp)*(p_dp + 2._dp))
      xnapm2j(ia,ip) = tau_q(ia) * k_cB * SQRT( p_dp       *(p_dp - 1._dp))
    ENDDO
    DO ij = 1, local_nj
      j_int= jarray(ij)   ! Laguerre degree
      j_dp = REAL(j_int,dp) ! REAL of Laguerre degree
      ! Magnetic gradient term
      xnapjp1(ia,ij) = -tau_q(ia) * k_gB * (j_dp + 1._dp)
      xnapjm1(ia,ij) = -tau_q(ia) * k_gB *  j_dp
    ENDDO
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !! ES linear coefficients for moment RHS !!!!!!!!!!
    DO ip = 1, local_np
      p_int= parray(ip)   ! Hermite degree
      DO ij = 1, local_nj
        j_int= jarray(ij)   ! REALof Laguerre degree
        j_dp = REAL(j_int,dp) ! REALof Laguerre degree
        !! Electrostatic potential pj terms
        IF (p_int .EQ. 0) THEN ! kronecker p0
          xphij  (ia,ip,ij)  = +k_N(ia) + 2._dp*j_dp*k_T(ia)
          xphijp1(ia,ip,ij)  = -k_T(ia)*(j_dp+1._dp)
          xphijm1(ia,ip,ij)  = -k_T(ia)* j_dp
        ELSE IF (p_int .EQ. 2) THEN ! kronecker p2
          xphij(ia,ip,ij)    = +k_T(ia)/SQRT2
          xphijp1(ia,ip,ij)  = 0._dp; xphijm1(ia,ip,ij)  = 0._dp;
        ELSE
          xphij  (ia,ip,ij)  = 0._dp; xphijp1(ia,ip,ij)  = 0._dp
          xphijm1(ia,ip,ij)  = 0._dp;
        ENDIF
    ENDDO
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !! Electromagnatic linear coefficients for moment RHS !!!!!!!!!!
    DO ip = 1, local_np
      p_int= parray(ip)   ! Hermite degree
      DO ij = 1, local_nj
        j_int= jarray(ij)   ! REALof Laguerre degree
        j_dp = REAL(j_int,dp) ! REALof Laguerre degree
        IF (p_int .EQ. 1) THEN ! kronecker p1
          xpsij  (ia,ip,ij)  = +(k_N(ia) + (2._dp*j_dp+1._dp)*k_T(ia))* sqrtTau_q(ia)
          xpsijp1(ia,ip,ij)  = - k_T(ia)*(j_dp+1._dp)              * sqrtTau_q(ia)
          xpsijm1(ia,ip,ij)  = - k_T(ia)* j_dp                     * sqrtTau_q(ia)
        ELSE IF (p_int .EQ. 3) THEN ! kronecker p3
          xpsij  (ia,ip,ij)  = + k_T(ia)*SQRT3/SQRT2               * sqrtTau_q(ia)
          xpsijp1(ia,ip,ij)  = 0._dp; xpsijm1(ia,ip,ij)  = 0._dp;
        ELSE
          xpsij  (ia,ip,ij)  = 0._dp; xpsijp1(ia,ip,ij)  = 0._dp
          xpsijm1(ia,ip,ij)  = 0._dp;
        ENDIF
END SUBROUTINE compute_lin_coeff

END MODULE numerics