-
Antoine Cyril David Hoffmann authored
checked sign of kxarray update when sheared
Antoine Cyril David Hoffmann authoredchecked sign of kxarray update when sheared
grid_mod.F90 28.12 KiB
MODULE grid
! Grid module for spatial discretization
USE prec_const, ONLY: xp
USE basic, ONLY: allocate_array, speak
USE parallel, ONLY: my_id, comm_ky
USE iso_c_binding
IMPLICIT NONE
PRIVATE
! GRID parameters
INTEGER, PUBLIC, PROTECTED :: pmax = 2 ! The maximal Hermite-moment computed
INTEGER, PUBLIC, PROTECTED :: jmax = 1 ! The maximal Laguerre-moment computed
INTEGER, PUBLIC, PROTECTED :: dmax = 2 ! The maximal full GF set of i-moments v^dmax
INTEGER, PUBLIC, PROTECTED :: Nx = 64 ! Number of total internal grid points in x
REAL(xp), PUBLIC, PROTECTED :: Lx = 120_xp ! horizontal length of the spatial box
INTEGER, PUBLIC, PROTECTED :: Nexc = 1 ! factor to increase Lx when shear>0 (Lx = Nexc/kymin/shear)
INTEGER, PUBLIC, PROTECTED :: Ny = 64 ! Number of total internal grid points in y
REAL(xp), PUBLIC, PROTECTED :: Ly = 120_xp ! vertical length of the spatial box
INTEGER, PUBLIC, PROTECTED :: Nz = 1 ! Number of total perpendicular planes
! Modes number (determined from Nx and Ny)
INTEGER, PUBLIC, PROTECTED :: Nkx ! Number of total internal grid points in kx
INTEGER, PUBLIC, PROTECTED :: Nky ! Number of total internal grid points in ky
! Grid arrays
INTEGER, DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: parray, parray_full
INTEGER, DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: jarray, jarray_full
REAL(xp), DIMENSION(:,:), ALLOCATABLE, PUBLIC,PROTECTED :: kxarray ! moving kx grid (ExB)
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: kxarray0 ! inirial kyarray
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: kxarray_full
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: xarray
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: kyarray, kyarray_full
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: ikyarray, inv_ikyarray !mode indices arrays
REAL(xp), DIMENSION(:,:), ALLOCATABLE, PUBLIC,PROTECTED :: zarray
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: zarray_full
REAL(xp), DIMENSION(:,:,:,:), ALLOCATABLE, PUBLIC,PROTECTED :: kparray !moving kperp grid
REAL(xp), DIMENSION(:,:,:,:), ALLOCATABLE, PUBLIC,PROTECTED :: kparray0 !initial kperp grid
REAL(xp), DIMENSION(:,:,:,:), ALLOCATABLE, PUBLIC,PROTECTED :: kp2array !moving kperp^2 grid
! Kronecker delta for p=0, p=1, p=2, j=0, j=1
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: kroneck_p0, kroneck_p1, kroneck_p2, kroneck_p3
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: kroneck_j0, kroneck_j1
! Indexation variables
INTEGER, PUBLIC, PROTECTED :: ias ,iae ! species index
INTEGER, PUBLIC, PROTECTED :: ips ,ipe ! Hermite
INTEGER, PUBLIC, PROTECTED :: ijs ,ije ! Laguerre
INTEGER, PUBLIC, PROTECTED :: ikys,ikye ! Fourier y mode
INTEGER, PUBLIC, PROTECTED :: ikxs,ikxe ! Fourier x mode
INTEGER, PUBLIC, PROTECTED :: izs ,ize ! z-grid
INTEGER, PUBLIC, PROTECTED :: ieven, iodd ! indices for the staggered grids
INTEGER, PUBLIC, PROTECTED :: ip0, ip1, ip2, ip3, ij0, ij1, pp2
INTEGER, PUBLIC, PROTECTED :: ikx0, iky0, ikx_max, iky_max ! Indices of k-grid origin and max
! Total numbers of points for Hermite and Laguerre
INTEGER, PUBLIC, PROTECTED :: total_na
INTEGER, PUBLIC, PROTECTED :: total_np
INTEGER, PUBLIC, PROTECTED :: total_nj
INTEGER, PUBLIC, PROTECTED :: total_nky
INTEGER, PUBLIC, PROTECTED :: total_nkx
INTEGER, PUBLIC, PROTECTED :: total_nz
! Local numbers of points (without ghosts)
INTEGER, PUBLIC, PROTECTED :: local_na
INTEGER, PUBLIC, PROTECTED :: local_np
INTEGER, PUBLIC, PROTECTED :: local_nj
INTEGER, PUBLIC, PROTECTED :: local_nky
INTEGER, PUBLIC, PROTECTED :: local_nkx ! = total_Nkx = Nx, not parallel
INTEGER, PUBLIC, PROTECTED :: local_nx ! = local_nx_ptr from FFTW (used only for Fourier)
INTEGER, PUBLIC, PROTECTED :: local_nz
INTEGER, PUBLIC, PROTECTED :: local_nkp
INTEGER, PUBLIC, PROTECTED :: ngp, ngj, ngx, ngy, ngz ! number of ghosts points
INTEGER, PUBLIC, PROTECTED :: nzgrid ! one or two depending on the staggered grid option
! Local offsets
INTEGER, PUBLIC, PROTECTED :: local_na_offset
INTEGER, PUBLIC, PROTECTED :: local_np_offset
INTEGER, PUBLIC, PROTECTED :: local_nj_offset
INTEGER, PUBLIC, PROTECTED :: local_nky_offset
INTEGER, PUBLIC, PROTECTED :: local_nkx_offset
INTEGER, PUBLIC, PROTECTED :: local_nx_offset
INTEGER, PUBLIC, PROTECTED :: local_nz_offset
! C-pointer type for FFTW3
integer(C_INTPTR_T), PUBLIC,PROTECTED :: local_nx_ptr, local_nky_ptr
integer(C_INTPTR_T), PUBLIC,PROTECTED :: local_nx_ptr_offset, local_nky_ptr_offset
! Grid spacing and limits
INTEGER , PUBLIC, PROTECTED :: deltap
REAL(xp), PUBLIC, PROTECTED :: deltaz, inv_deltaz, inv_dkx
REAL(xp), PUBLIC, PROTECTED :: deltakx, deltaky, deltax, kx_max, ky_max, kx_min, ky_min!, kp_max
INTEGER , PUBLIC, PROTECTED :: local_pmin, local_pmax
INTEGER , PUBLIC, PROTECTED :: local_jmin, local_jmax
REAL(xp), PUBLIC, PROTECTED :: local_kymin, local_kymax
REAL(xp), PUBLIC, PROTECTED :: local_kxmin, local_kxmax
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC, PROTECTED :: local_zmin, local_zmax
! local z weights for computing simpson rule
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: zweights_SR
! Numerical diffusion scaling
REAL(xp), PUBLIC, PROTECTED :: diff_p_coeff, diff_j_coeff
REAL(xp), PUBLIC, PROTECTED :: diff_kx_coeff, diff_ky_coeff, diff_dz_coeff
LOGICAL, PUBLIC, PROTECTED :: SG = .true.! shifted grid flag
! Array to know the distribution of data among all processes (for MPI comm)
INTEGER, DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: counts_total_nkx, counts_nky, counts_nz
INTEGER, DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: displs_total_nkx, displs_nky, displs_nz
! Kperp array depends on kx, ky, z (geometry), eo (even or odd zgrid)
LOGICAL, PUBLIC, PROTECTED :: contains_kx0 = .false. ! flag if the proc contains kx=0 index
LOGICAL, PUBLIC, PROTECTED :: contains_ky0 = .false. ! flag if the proc contains ky=0 index
LOGICAL, PUBLIC, PROTECTED :: contains_kymax = .false. ! flag if the proc contains kx=kxmax index
LOGICAL, PUBLIC, PROTECTED :: contains_zmax = .false. ! flag if the proc contains z=pi-dz index
LOGICAL, PUBLIC, PROTECTED :: contains_zmin = .false. ! flag if the proc contains z=-pi index
LOGICAL, PUBLIC, PROTECTED :: SINGLE_KY = .false. ! to check if it is a single non 0 ky simulation
! Grid containing the polynomials degrees
LOGICAL, PUBLIC, PROTECTED :: CONTAINSp0, CONTAINSp1, CONTAINSp2, CONTAINSp3
LOGICAL, PUBLIC, PROTECTED :: SOLVE_POISSON, SOLVE_AMPERE
! Usefull inverse numbers
REAL(xp), PUBLIC, PROTECTED :: inv_Nx, inv_Ny, inv_Nz
! For Orszag filter
REAL(xp), PUBLIC, PROTECTED :: two_third_kxmax
REAL(xp), PUBLIC, PROTECTED :: two_third_kymax
REAL(xp), PUBLIC, PROTECTED :: two_third_kpmax
! 1D Antialiasing arrays (2/3 rule)
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: AA_x
REAL(xp), DIMENSION(:), ALLOCATABLE, PUBLIC,PROTECTED :: AA_y
! Public Functions
PUBLIC :: init_grids_data, set_grids, update_grids
PUBLIC :: set_kxgrid, set_kparray ! Can be called from geometry to include shear effects
PUBLIC :: grid_readinputs, grid_outputinputs
PUBLIC :: bar
! Precomputations
real(xp), PUBLIC, PROTECTED :: pmax_xp, jmax_xp
CONTAINS
SUBROUTINE grid_readinputs
! Read the input parameters
USE prec_const
IMPLICIT NONE
INTEGER :: lun = 90 ! File duplicated from STDIN
NAMELIST /GRID/ pmax, jmax, Nx, Lx, Ny, Ly, Nz, SG, Nexc
READ(lun,grid)
IF(Nz .EQ. 1) & ! overwrite SG option if Nz = 1 for safety of use
SG = .FALSE.
!! Compute the maximal degree of full GF moments set
! i.e. : all moments N_a^pj s.t. p+2j<=d are simulated (see GF closure)
dmax = min(pmax,2*jmax+1)
! Usefull precomputations
inv_Nx = 1._xp/REAL(Nx,xp)
inv_Ny = 1._xp/REAL(Ny,xp)
END SUBROUTINE grid_readinputs
!! Init the local and global number of points in all directions
SUBROUTINE init_grids_data(Na,EM,LINEARITY)
USE fourier, ONLY: init_grid_distr_and_plans
USE parallel, ONLY: num_procs_p, rank_p, num_procs_ky, rank_ky, num_procs_z, rank_z
USE utility, ONLY: decomp1D
IMPLICIT NONE
INTEGER, INTENT(IN) :: Na ! number of species coming from the model module
LOGICAL, INTENT(IN) :: EM ! electromagnetic effects (to skip odd Hermite or not)
CHARACTER(len=*), INTENT(IN) :: LINEARITY ! Linear or nonlinear run
INTEGER :: in, istart, iend
!!----------------- SPECIES INDICES (not parallelized)
ias = 1
iae = Na
total_Na = Na
local_Na = Na
local_Na_offset = ias - 1
!!----------------- HERMITE INDICES (parallelized)
! If no parallel dim (Nz=1) and no EM effects (beta=0), the moment hierarchy
!! is separable between odds and even P
IF((Nz .EQ. 1) .AND. .NOT. EM) THEN
deltap = 2
Ngp = 2 ! two ghosts cells for p+/-2 only
pp2 = 1 ! index p+2 is ip+1
ELSE
deltap = 1
Ngp = 4 ! four ghosts cells for p+/-1 and p+/-2 terms
pp2 = 2 ! index p+2 is ip+2
ENDIF
! Total number of Hermite polynomials we will evolve
total_np = (Pmax/deltap) + 1
! Local data distribution
CALL decomp1D(total_np, num_procs_p, rank_p, ips, ipe)
local_np = ipe - ips + 1
local_np_offset = ips - 1
! Allocate the grid arrays
ALLOCATE(parray_full(total_np))
ALLOCATE(parray(local_np+ngp))
!!----------------- LAGUERRE INDICES (not parallelized)
! Total number of J degrees
total_nj = jmax+1
local_jmax = jmax
Ngj= 2 ! 2-points ghosts for j+\-1 terms
! Indices of local data
ijs = 1; ije = jmax + 1
! Local number of J
local_nj = ije - ijs + 1
local_nj_offset = ijs - 1
! allocate global and local
ALLOCATE(jarray_full(total_nj))
ALLOCATE(jarray(local_nj+ngj))
ALLOCATE(kroneck_j0(local_nj+ngj));
ALLOCATE(kroneck_j1(local_nj+ngj));
!!----------------- SPATIAL GRIDS (parallelized)
!! Parallel distribution of kx ky grid
IF (LINEARITY .NE. 'linear') THEN ! we let FFTW distribute if we use it
IF (my_id .EQ. 0) write(*,*) 'FFTW3 y-grid distribution'
CALL init_grid_distr_and_plans(Nx,Ny,comm_ky,local_nx_ptr,local_nx_ptr_offset,local_nky_ptr,local_nky_ptr_offset)
ELSE ! otherwise we distribute equally
IF (my_id .EQ. 0) write(*,*) 'Manual y-grid distribution'
! balanced distribution among the processes
CALL decomp1D( Ny/2+1, num_procs_ky, rank_ky, ikys, ikye )
local_nky_ptr = ikye - ikys + 1
local_nky_ptr_offset = ikys - 1
ENDIF
!!----------------- BINORMAL KY INDICES (parallelized)
Nky = Ny/2+1 ! Defined only on positive kx since fields are real
total_nky = Nky
Ngy = 0 ! no ghosts cells in ky
ikys = INT(local_nky_ptr_offset + 1)
ikye = INT(ikys + local_nky_ptr - 1)
local_nky = ikye - ikys + 1
local_nky_offset = INT(local_nky_ptr_offset)
ALLOCATE(kyarray_full(Nky))
ALLOCATE(kyarray(local_nky))
ALLOCATE(ikyarray(Nky))
ALLOCATE(inv_ikyarray(Nky))
ALLOCATE(AA_y(local_nky))
!!---------------- RADIAL KX INDICES (not parallelized)
Nkx = Nx
total_nkx = Nx
ikxs = 1
ikxe = total_nkx
local_nkx = ikxe - ikxs + 1
local_nkx_offset = ikxs - 1
ALLOCATE(kxarray_full(total_nkx))
ALLOCATE(kxarray0(local_Nkx))
ALLOCATE(kxarray(local_nky,local_Nkx))
ALLOCATE(AA_x(local_nkx))
!!---------------- RADIAL X GRID (only for Fourier routines)
local_nx = INT(local_nx_ptr)
local_nx_offset = INT(local_nx_ptr_offset)
ALLOCATE(xarray(Nx))
!!---------------- PARALLEL Z GRID (parallelized)
total_nz = Nz
IF (SG) THEN
CALL speak('--2 staggered z grids--')
! indices for even p and odd p grids (used in kernel, jacobian, gij etc.)
ieven = 1
iodd = 2
nzgrid = 2
ELSE
ieven = 1
iodd = 1
nzgrid = 1
ENDIF
!! Parallel data distribution
IF( (Nz .EQ. 1) .AND. (num_procs_z .GT. 1) ) &
ERROR STOP '>> ERROR << Cannot have multiple core in z-direction (Nz = 1)'
! Local data distribution
CALL decomp1D(total_nz, num_procs_z, rank_z, izs, ize)
local_nz = ize - izs + 1
local_nz_offset = izs - 1
! Ghosts boundaries (depend on the order of z operators)
IF(Nz .EQ. 1) THEN
ngz = 0
ELSEIF(Nz .GE. 4) THEN
ngz =4
IF(mod(Nz,2) .NE. 0 ) THEN
ERROR STOP '>> ERROR << Nz must be an even number for Simpson integration rule !!!!'
ENDIF
ELSE
ERROR STOP '>> ERROR << Nz is not appropriate!!'
ENDIF
ALLOCATE(zarray(local_nz+ngz,nzgrid))
ALLOCATE(zarray_full(total_nz))
ALLOCATE(counts_nz (num_procs_z))
ALLOCATE(displs_nz (num_procs_z))
CALL allocate_array(local_zmax,1,nzgrid)
CALL allocate_array(local_zmin,1,nzgrid)
ALLOCATE(zweights_SR(local_nz))
! List of shift and local numbers between the different processes (used in scatterv and gatherv)
DO in = 0,num_procs_z-1
CALL decomp1D(total_nz, num_procs_z, in, istart, iend)
counts_nz(in+1) = iend-istart+1
displs_nz(in+1) = istart-1
ENDDO
!!---------------- Kperp grid
CALL allocate_array( kparray, 1,local_nky, 1,local_nkx, 1,local_nz+ngz, 1,nzgrid)
CALL allocate_array(kparray0, 1,local_nky, 1,local_nkx, 1,local_nz+ngz, 1,nzgrid)
CALL allocate_array(kp2array, 1,local_nky, 1,local_nkx, 1,local_nz+ngz, 1,nzgrid)
END SUBROUTINE
!!!! GRID REPRESENTATION
! We define the grids that contain ghosts (p,j,z) with indexing from 1 to Nlocal + nghost
! e.g. nghost = 4, nlocal = 4
! |x|x|_|_|_|_|x|x|
! 1 2 3 4 5 6 7 8 (x are ghosts)
! the interior points are contained between 1+Ng/2 and Nlocal+Ng/2
! The other grids are simply
! |_|_|_|_|
! 1 2 3 4
SUBROUTINE set_grids(shear,ExBrate,Npol,LINEARITY,N_HD,EM,Na)
REAL(xp), INTENT(IN) :: shear, ExBrate, Npol
CHARACTER(len=*), INTENT(IN) :: LINEARITY
INTEGER, INTENT(IN) :: N_HD
LOGICAL, INTENT(IN) :: EM
INTEGER, INTENT(IN) :: Na
CALL set_agrid(Na)
CALL set_pgrid(EM)
CALL set_jgrid
CALL set_kygrid(LINEARITY,N_HD)
CALL set_kxgrid(shear,ExBrate,Npol,1._xp,LINEARITY,N_HD) ! this will be redone after geometry if Cyq0_x0 .NE. 1
CALL set_xgrid
CALL set_zgrid (Npol)
END SUBROUTINE set_grids
SUBROUTINE set_agrid(Na) ! you're a sorcerer harry
IMPLICIT NONE
INTEGER, INTENT(IN) :: Na
ias = 1
iae = Na
total_Na = Na
local_Na = Na
local_Na_offset = ias - 1
END SUBROUTINE
SUBROUTINE set_pgrid(EM)
USE prec_const
IMPLICIT NONE
LOGICAL, INTENT(IN) :: EM
INTEGER :: ip
! Build the full grids on process 0 to diagnose it without comm
! P
DO ip = 1,total_np; parray_full(ip) = (ip-1)*deltap; END DO
!! local grid computations
! Fill pgrid array
DO ip = 1,local_np+ngp
parray(ip) = (ip-1-ngp/2+local_np_offset)*deltap
ENDDO
local_pmax = parray(local_np+ngp/2)
local_pmin = parray(1+ngp/2)
! Allocate kronecker arrays for p=0,1,2,3
ALLOCATE(kroneck_p0(local_np+ngp)); kroneck_p0 = 0._xp
ALLOCATE(kroneck_p1(local_np+ngp)); kroneck_p1 = 0._xp
ALLOCATE(kroneck_p2(local_np+ngp)); kroneck_p2 = 0._xp
ALLOCATE(kroneck_p3(local_np+ngp)); kroneck_p3 = 0._xp
DO ip = 1,local_np+ngp
SELECT CASE (parray(ip))
CASE(0)
ip0 = ip
kroneck_p0(ip) = 1._xp
CASE(1)
ip1 = ip
kroneck_p1(ip) = 1._xp
CASE(2)
ip2 = ip
kroneck_p2(ip) = 1._xp
CASE(3)
ip3 = ip
kroneck_p3(ip) = 1._xp
END SELECT
END DO
! Set local flags to avoid unnecessary logical operations
CONTAINSp0 = .FALSE.; CONTAINSp1 = .FALSE.
CONTAINSp2 = .FALSE.; CONTAINSp3 = .FALSE.
DO ip = 1,local_np+ngp
SELECT CASE (parray(ip))
CASE(0); CONTAINSp0 = .TRUE.
CASE(1); CONTAINSp1 = .TRUE.
CASE(2); CONTAINSp2 = .TRUE.
CASE(3); CONTAINSp3 = .TRUE.
END SELECT
END DO
! Flags that sets if Poisson and Ampere have to be solved locally
SOLVE_POISSON = .FALSE.; SOLVE_AMPERE = .FALSE.
DO ip = 1+ngp/2,local_np+ngp/2
SELECT CASE (parray(ip))
CASE(0); SOLVE_POISSON = .TRUE.
CASE(1); SOLVE_AMPERE = .TRUE.
END SELECT
END DO
! Precomputations
pmax_xp = real(pmax,xp)
diff_p_coeff = pmax_xp*(1._xp/(pmax_xp+1._xp))**6
! Overwrite SOLVE_AMPERE flag if beta is zero
IF(.NOT. EM) THEN
SOLVE_AMPERE = .FALSE.
ENDIF
END SUBROUTINE set_pgrid
SUBROUTINE set_jgrid
USE prec_const
IMPLICIT NONE
INTEGER :: ij
! Build the full grids on process 0 to diagnose it without comm
! J
DO ij = 1,total_nj
jarray_full(ij) = (ij-1)
END DO
DO ij = 1,local_nj+ngj
jarray(ij) = ij-1-ngj/2+local_nj_offset
END DO
local_jmax = jarray(local_nj+ngj/2)
local_jmin = jarray(1+ngj/2)
! Precomputations
jmax_xp = real(jmax,xp)
diff_j_coeff = jmax_xp*(1._xp/(jmax_xp+1._xp))**6
! j=0 and j=1 indices
DO ij = 1,local_nj+ngj
IF(jarray(ij) .EQ. 0) ij0 = ij
IF(jarray(ij) .EQ. 1) ij1 = ij
END DO
! Kronecker arrays for j
kroneck_j0 = 0._xp
kroneck_j1 = 0._xp
DO ij = 1,local_nj+ngj
SELECT CASE(jarray(ij))
CASE(0)
kroneck_j0(ij) = 1._xp
CASE(1)
kroneck_j1(ij) = 1._xp
END SELECT
END DO
END SUBROUTINE set_jgrid
SUBROUTINE set_kygrid(LINEARITY,N_HD)
USE prec_const
IMPLICIT NONE
CHARACTER(len=*), INTENT(IN) ::LINEARITY
INTEGER, INTENT(IN) :: N_HD
INTEGER :: iky
! Grid spacings
IF (Ny .EQ. 1) THEN
ERROR STOP "Gyacomo cannot run with only one ky"
ELSE
deltaky = 2._xp*PI/Ly
ky_max = (Nky-1)*deltaky
ky_min = deltaky
ENDIF
! Build the full grids on process 0 to diagnose it without comm
DO iky = 1,Nky
kyarray_full(iky) = REAL(iky-1,xp) * deltaky
! full indices grid (for ExB NL factor)
ikyarray(iky) = REAL((iky)-1,xp)
IF(ikyarray(iky) .GT. 0) THEN
inv_ikyarray(iky) = 1._xp/ikyarray(iky)
ELSE
inv_ikyarray(iky) = 0._xp
ENDIF
END DO
local_kymax = 0._xp
! Creating a grid ordered as dk*(0 1 2 3)
! We loop over the natural iky numbers (|1 2 3||4 5 6||... Nky|)
DO iky = 1,local_nky
! We shift the natural iky index by the offset to obtain the mpi dependent
! indexation (|1 2 3||1 2 3|... local_nky|)
IF(Ny .EQ. 1) THEN
kyarray(iky) = deltaky
kyarray_full(iky) = deltaky
SINGLE_KY = .TRUE.
ELSE
kyarray(iky) = kyarray_full(iky+local_nky_offset)
ENDIF
! Finding kx=0
IF (kyarray(iky) .EQ. 0) THEN
iky0 = iky
contains_ky0 = .true.
ENDIF
! Finding local kxmax value
IF (ABS(kyarray(iky)) .GT. local_kymax) THEN
local_kymax = ABS(kyarray(iky))
ENDIF
! Finding kxmax idx
IF (kyarray(iky) .EQ. ky_max) THEN
iky_max = iky
contains_kymax = .true.
ENDIF
END DO
! Orszag 2/3 filter
two_third_kymax = 2._xp/3._xp*deltaky*(Nky-1)
DO iky = 1,local_nky
IF ( (kyarray(iky) .LT. two_third_kymax) .OR. (LINEARITY .EQ. 'linear')) THEN
AA_y(iky) = 1._xp;
ELSE
AA_y(iky) = 0._xp;
ENDIF
END DO
! For hyperdiffusion
IF(LINEARITY.EQ.'linear') THEN
diff_ky_coeff= (1._xp/ky_max)**N_HD
ELSE
diff_ky_coeff= (1._xp/two_third_kymax)**N_HD
ENDIF
END SUBROUTINE set_kygrid
SUBROUTINE set_kxgrid(shear,ExBrate,Npol,Cyq0_x0,LINEARITY,N_HD)
USE prec_const, ONLY: xp, pi
USE basic, ONLY: speak, str
IMPLICIT NONE
REAL(xp), INTENT(IN) :: shear, Npol, Cyq0_x0, ExBrate
CHARACTER(len=*), INTENT(IN) ::LINEARITY
INTEGER, INTENT(IN) :: N_HD
INTEGER :: ikx, iky
REAL(xp):: Lx_adapted
IF(shear .GT. 0) THEN
CALL speak('Magnetic shear detected: set up sheared kx grid..')
! mininal size of box in x to respect dkx = 2pi shear dky
Lx_adapted = Ly/(2._xp*pi*shear*Npol*Cyq0_x0)
! Put Nexc to 0 so that it is computed from a target value Lx
IF(Nexc .EQ. 0) THEN
Nexc = CEILING(0.9 * Lx/Lx_adapted)
CALL speak('Adapted Nexc ='//str(Nexc))
ENDIF
! x length is adapted
Lx = Lx_adapted*Nexc
CALL speak('Simulation Lx ='//str(Lx))
ENDIF
deltakx = 2._xp*PI/Lx
inv_dkx = 1._xp/deltakx
IF(MODULO(total_nkx,2) .EQ. 0) THEN ! Even number of kx (-2 -1 0 1 2 3)
! Creating a grid ordered as dk*(0 1 2 3 -2 -1)
DO ikx = 1,total_nkx
kxarray_full(ikx) = deltakx*REAL(MODULO(ikx-1,total_nkx/2)-(total_nkx/2)*FLOOR(2.*real(ikx-1)/real(total_nkx)),xp)
IF (ikx .EQ. total_nkx/2+1) kxarray_full(ikx) = -kxarray_full(ikx)
END DO
kx_max = MAXVAL(kxarray_full)!(total_nkx/2)*deltakx
kx_min = MINVAL(kxarray_full)!-kx_max+deltakx
! Set local grid (not parallelized so same as full one)
local_kxmax = 0._xp
DO iky = 1,local_nky
DO ikx = 1,local_nkx
kxarray0(ikx) = kxarray_full(ikx+local_nkx_offset)
kxarray(iky,ikx) = kxarray_full(ikx+local_nkx_offset)
! Finding kx=0
IF (kxarray(1,ikx) .EQ. 0) THEN
ikx0 = ikx
contains_kx0 = .true.
ENDIF
! Finding local kxmax
IF (ABS(kxarray(iky,ikx)) .GT. local_kxmax) THEN
local_kxmax = ABS(kxarray(iky,ikx))
ikx_max = ikx
ENDIF
END DO
END DO
ELSE ! Odd number of kx (-2 -1 0 1 2)
ERROR STOP "Gyacomo is safer with an even Kx number"
ENDIF
! Orszag 2/3 filter
! We remove one point more if ExB is on since the moving grid
! can go up to kx=+-(kx_max+deltakx/2)
IF (ABS(ExBrate) .GT. 0) THEN
two_third_kxmax = 2._xp/3._xp*(kx_max)
ELSE
two_third_kxmax = 2._xp/3._xp*kx_max
ENDIF
! Antialiasing filter
DO iky = 1,local_nky
DO ikx = 1,local_nkx
IF ( ((kxarray(iky,ikx) .GT. -two_third_kxmax) .AND. &
(kxarray(iky,ikx) .LT. two_third_kxmax)) .OR. (LINEARITY .EQ. 'linear')) THEN
AA_x(ikx) = 1._xp;
ELSE
AA_x(ikx) = 0._xp;
ENDIF
END DO
END DO
! For hyperdiffusion
IF(LINEARITY.EQ.'linear') THEN
diff_kx_coeff= (1._xp/kx_max)**N_HD
ELSE
diff_kx_coeff= (1._xp/two_third_kxmax)**N_HD
ENDIF
END SUBROUTINE set_kxgrid
!----------- Radial x grid
! used only for the computation of the ExB shear NL factor
SUBROUTINE set_xgrid
USE prec_const, ONLY: xp, pi
IMPLICIT NONE
INTEGER :: ix
REAL(xp):: L_
L_ = 2._xp*pi/deltakx
deltax = L_/REAL(Nx,xp)
! full xgrid
DO ix = 1,Nx
xarray(ix) = REAL(ix-1,xp)*deltax
ENDDO
END SUBROUTINE set_xgrid
!----------- Parallel z grid
SUBROUTINE set_zgrid(Npol)
USE prec_const
IMPLICIT NONE
REAL(xp):: grid_shift, Lz, zmax, zmin, Npol
INTEGER :: iz, ig, eo
! Length of the flux tube (in ballooning angle)
Lz = 2._xp*pi*Npol
! Z stepping (#interval = #points since periodic)
deltaz = Lz/REAL(Nz,xp)
inv_deltaz = 1._xp/deltaz
! Parallel hyperdiffusion coefficient
diff_dz_coeff = (deltaz/2._xp)**4 ! adaptive fourth derivative (~GENE)
IF (SG) THEN
grid_shift = deltaz/2._xp
ELSE
grid_shift = 0._xp
ENDIF
! Build the full grids on process 0 to diagnose it without comm
IF (Nz .EQ. 1) &
Npol = 0._xp
zmax = 0; zmin = 0;
DO iz = 1,total_nz ! z in [-pi pi-dz] x Npol
zarray_full(iz) = REAL(iz-1,xp)*deltaz - Lz/2._xp
IF(zarray_full(iz) .GT. zmax) zmax = zarray_full(iz)
IF(zarray_full(iz) .LT. zmin) zmin = zarray_full(iz)
END DO
IF(Nz .EQ. 1) &
zarray_full(izs) = 0
! Local z array
DO iz = 1,local_nz
DO eo = 1,nzgrid
zarray(iz+ngz/2,eo) = zarray_full(iz+local_nz_offset) + REAL(eo-1,xp)*grid_shift
ENDDO
ENDDO
DO eo = 1,nzgrid
! Find local extrema
local_zmax(eo) = zarray(local_nz+ngz/2,eo)
local_zmin(eo) = zarray(1+ngz/2,eo)
! continue in z<pi and z>pi
DO ig = 1,ngz/2
zarray(local_nz+ngz/2+ig,eo) = zarray(local_nz+ngz/2,eo) + ig*deltaz
zarray(ig,eo) = zarray(1+ngz/2,eo) - (3-ig)*deltaz
ENDDO
! Set up the flags to know if the process contains the tip and/or the tail
! of the z domain (important for z-boundary condition)
IF(abs(local_zmin(eo) - (zmin+REAL(eo-1,xp)*grid_shift)) .LT. EPSILON(zmin)) &
contains_zmin = .TRUE.
IF(abs(local_zmax(eo) - (zmax+REAL(eo-1,xp)*grid_shift)) .LT. EPSILON(zmax)) &
contains_zmax = .TRUE.
ENDDO
! local weights for Simpson rule
IF(total_nz .EQ. 1) THEN
zweights_SR = 1._xp
ELSE
DO iz = 1,local_nz
IF(MODULO(iz+local_nz_offset,2) .EQ. 1) THEN ! odd iz
zweights_SR(iz) = onethird*deltaz*2._xp
ELSE ! even iz
zweights_SR(iz) = onethird*deltaz*4._xp
ENDIF
ENDDO
ENDIF
END SUBROUTINE set_zgrid
SUBROUTINE set_kparray(gxx, gxy, gyy, inv_hatB2)
IMPLICIT NONE
REAL(xp), DIMENSION(local_nz+ngz,nzgrid), INTENT(IN) :: gxx,gxy,gyy,inv_hatB2
INTEGER :: eo,iz,iky,ikx
REAL(xp) :: kx, ky
DO eo = 1,nzgrid
DO iz = 1,local_nz+ngz
DO iky = 1,local_nky
ky = kyarray(iky)
DO ikx = 1,local_nkx
kx = kxarray(iky,ikx)
! there is a factor 1/B from the normalization; important to match GENE
! this factor comes from $b_a$ argument in the Bessel. Kperp is not used otherwise.
kp2array(iky, ikx, iz, eo) = &
(gxx(iz,eo)*kx**2 + 2._xp*gxy(iz,eo)*kx*ky + gyy(iz,eo)*ky**2)*inv_hatB2(iz,eo)
kparray (iky, ikx, iz, eo) = SQRT(kp2array(iky, ikx, iz, eo))
kparray0(iky, ikx, iz, eo) = SQRT(kp2array(iky, ikx, iz, eo))
ENDDO
ENDDO
ENDDO
ENDDO
two_third_kpmax = 2._xp/3._xp * MAXVAL(kparray)
END SUBROUTINE
SUBROUTINE update_grids (sky_ExB,gxx,gxy,gyy,inv_hatB2)
IMPLICIT NONE
REAL(xp), DIMENSION(local_nky),INTENT(IN) :: sky_ExB ! ExB correction dkx = gamma_E ky dtshift
REAL(xp), DIMENSION(local_nz+ngz,nzgrid), INTENT(IN) :: gxx,gxy,gyy,inv_hatB2
INTEGER :: eo,iz,iky,ikx
REAL(xp) :: kx, ky
! Update the kx grid
DO ikx = 1,total_Nkx
DO iky = 1,local_nky
! For positive ExBrate, sky_ExB is negative
kxarray(iky,ikx) = kxarray0(ikx) + sky_ExB(iky)
ENDDO
ENDDO
! Update the kperp grid
DO eo = 1,nzgrid
DO iz = 1,local_nz+ngz
DO ikx = 1,local_nkx
DO iky = 1,local_nky
ky = kyarray(iky)
kx = kxarray(iky,ikx)
kp2array(iky,ikx,iz,eo) = (gxx(iz,eo)*kx**2 + 2._xp*gxy(iz,eo)*kx*ky + gyy(iz,eo)*ky**2)*inv_hatB2(iz,eo)
ENDDO
ENDDO
ENDDO
ENDDO
END SUBROUTINE
SUBROUTINE grid_outputinputs(fid)
! Write the input parameters to the results_xx.h5 file
USE futils, ONLY: attach, creatd
IMPLICIT NONE
INTEGER, INTENT(in) :: fid
CHARACTER(len=256) :: str
WRITE(str,'(a)') '/data/input/grid'
CALL creatd(fid, 0,(/0/),TRIM(str),'Grid Input')
CALL attach(fid, TRIM(str), "pmax", pmax)
CALL attach(fid, TRIM(str),"deltap", deltap)
CALL attach(fid, TRIM(str), "Np", total_np)
CALL attach(fid, TRIM(str), "jmax", jmax)
CALL attach(fid, TRIM(str), "Nj", total_nj)
CALL attach(fid, TRIM(str), "Nkx", Nkx)
CALL attach(fid, TRIM(str), "Nx", Nx)
CALL attach(fid, TRIM(str), "Lx", Lx)
CALL attach(fid, TRIM(str), "Nexc", Nexc)
CALL attach(fid, TRIM(str), "Ny", Ny)
CALL attach(fid, TRIM(str), "Nky", Nky)
CALL attach(fid, TRIM(str), "Ly", Ly)
CALL attach(fid, TRIM(str), "Nz", Nz)
CALL attach(fid, TRIM(str), "total_nkx", total_nkx)
CALL attach(fid, TRIM(str), "Nky", Nky)
CALL attach(fid, TRIM(str), "SG", SG)
END SUBROUTINE grid_outputinputs
FUNCTION bar(p_,j_)
IMPLICIT NONE
INTEGER :: bar, p_, j_
bar = (jmax+1)*p_ + j_ + 1
END FUNCTION
END MODULE grid