dns-hit3d-fdm/x_fftw.f90
2017-12-24 20:19:22 +09:00

1286 lines
46 KiB
Fortran

!==============================================================================!
!
! Fast Fourier Transform that uses FFTW3 library,
! pseudospectral DNS code
! Copyright (C) 2006 Sergei Chumakov, Natalia Vladimirova, Misha Stepanov
!
! This program is free software; you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation; either version 2 of the License, or
! (at your option) any later version.
!
! This program is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with this program; if not, write to the
! Free Software Foundation, Inc.,
! 51 Franklin Street, Fifth Floor,
! Boston, MA 02110-1301, USA
!
!==============================================================================!
MODULE x_fftw
!==============================================================================!
! VARIABLES
!==============================================================================!
use m_parameters
use m_io
use m_fields
use m_work
use m_timing
implicit none
! FFTW parameters that do not change
integer(kind=8), parameter :: FFTW_ESTIMATE = 0
integer(kind=8), parameter :: FFTW_FORWARD = -1
integer(kind=8), parameter :: FFTW_BACKWARD = 1
! dedicated arrays for parallel FFT
real(kind=8), allocatable :: xy_sheet(:, :), buff(:, :, :), z_stick(:)
real*8, allocatable :: buff2(:,:,:)
! order of message passing between processors
integer(kind=4), allocatable :: order(:), order_matrix(:, :)
! the arrays to store FFTW plans for the 2D r2c or c2r steps
! plan_r2c(1..nz, 1..n_scalars)
integer(kind=8), allocatable :: plan_r2c(:, :), plan_c2r(:, :)
integer(kind=8), allocatable :: plan_r2c_f(:, :), plan_c2r_f(:, :)
! FFTW plans for the 1D c2c forward/backward steps
integer(kind=8) :: plan_f_c2c, plan_b_c2c
! k-vectors ("a" added as arrays are real
real(kind=8), allocatable :: akx(:), aky(:), akz(:)
real(kind=8), allocatable :: coskx2(:), cosky2(:), coskz2(:)
real(kind=8), allocatable :: sinkx2(:), sinky2(:), sinkz2(:)
integer(kind=4), allocatable :: rezkax(:), rezkay(:), rezkaz(:)
! auxiliary parameters
integer(kind=4) :: nx21
real(kind=8) :: norm
! array that contains indicator of aliasing when products are taken
integer(kind=1), allocatable :: ialias(:,:,:)
!==============================================================================!
!==============================================================================!
CONTAINS
!==============================================================================!
!==============================================================================!
! SUBROUTINES
!==============================================================================!
!==============================================================================!
! Subroutine that allocates/deallocates the FFTW arrays
!==============================================================================!
subroutine X_FFTW_ALLOCATE(flag)
implicit none
integer :: flag
if (flag == 1) then
!!$ print *,'FFTW_ALLOCATE: size(wrk,4) = ',size(wrk,4)
!!$ print *,'FFTW_ALLOCATE: bounds(wrk,4) = ',LBOUND(wrk,4),UBOUND(wrk,4)
!!$ write(out,*) 'FFTW_ALLOCATE: size(wrk,4) = ',size(wrk,4)
!!$ write(out,*) 'FFTW_ALLOCATE: bounds(wrk,4) = ',LBOUND(wrk,4),UBOUND(wrk,4)
!!$ call flush(out)
allocate(&
plan_r2c(nz, LBOUND(wrk,4):UBOUND(wrk,4)), &
plan_c2r(nz, LBOUND(wrk,4):UBOUND(wrk,4)), &
plan_r2c_f(nz, LBOUND(fields,4):UBOUND(fields,4)), &
plan_c2r_f(nz, LBOUND(fields,4):UBOUND(fields,4)), &
xy_sheet(nx, ny), buff(nx + 2, nz, nz), &
z_stick(2 * nz_all), akx(nx + 2), aky(nz), akz(nz_all), &
rezkax(nx + 2), rezkay(nz), rezkaz(nx), &
coskx2(nx + 2), cosky2(nz), coskz2(nx), &
sinkx2(nx + 2), sinky2(nz), sinkz2(nx), &
order(numprocs - 1), &
buff2(nx+2, nz, nz), &
ialias(nx+2, ny, nz), stat = ierr)
!!$ write(out,*) 'Size of plan_r2c:',SIZE(plan_r2c,1),SIZE(plan_r2c,2)
!!$ write(out,*) 'Size of plan_c2r:',SIZE(plan_c2r,1),SIZE(plan_c2r,2)
!!$ call flush(out)
if (ierr /= 0) then
write (out, *) '*** X_FFTW_ALLOCATE: cannot allocate'
call my_exit(-1)
end if
write(out,*) "x_fftw_allocated."
call flush(out)
! assigning temporary values to allocated arrays
plan_r2c = 0
plan_c2r = 0
xy_sheet = zip
buff = zip
buff2 = zip
z_stick = zip
order = 0
ialias = 0
elseif (flag == -1) then
if (allocated(plan_r2c)) then
deallocate(plan_r2c, plan_c2r, plan_r2c_f, plan_c2r_f, &
xy_sheet, buff, z_stick, order, &
akx, aky, akz, rezkax, rezkay, rezkaz, coskx2, cosky2, coskz2,&
sinkx2, sinky2, sinkz2, buff2, ialias)
end if
write(out,*) "x_fftw_deallocated."
call flush(out)
else
write (out, *) '*** X_FFTW_ALLOCATE: Wrong value of flag:', flag
call my_exit(-1)
end if
return
end subroutine X_FFTW_ALLOCATE
!==============================================================================!
! Program that initializes the auxilary arrays for FFT
!==============================================================================!
subroutine x_fftw_init
implicit none
integer :: itmp, ix, iy, iz, n, i, j, k
real *8 :: rnx3
! write(out, *) 'Initializing FFT arrays.'
! call flush(out)
!------------------------------------------------------------------------------!
! filling up the array order_matrix
!------------------------------------------------------------------------------!
allocate(order_matrix(numprocs, numprocs), stat = ierr)
if (ierr /= 0) then
write(out,*) '*** X_FFTW_INIT: Cannot allocate order_matrix'
call my_exit(-1)
end if
order_matrix(1, 1) = 0
itmp = 1
do while (itmp < numprocs)
do ix = 1, itmp
do iy = 1, itmp
order_matrix(ix + itmp, iy) = order_matrix(ix, iy) + itmp
order_matrix(ix, iy + itmp) = order_matrix(ix, iy) + itmp
order_matrix(ix + itmp, iy + itmp) = order_matrix(ix, iy)
end do
end do
itmp = 2 * itmp
end do
!------------------------------------------------------------------------------!
! filling the array order and deallocating order_matrix
!------------------------------------------------------------------------------!
do ix = 1, numprocs
if (order_matrix(ix, myid + 1) /= 0) then
order(order_matrix(ix, myid + 1)) = ix - 1
end if
end do
deallocate(order_matrix)
!------------------------------------------------------------------------------!
! initializing FFTW plans for the 1st step in FFT --- 2D r2c
!------------------------------------------------------------------------------!
!!$ print *,'FFTW_INIT: size(wrk,4) = ',size(wrk,4)
!!$ print *,'FFTW_INIT: bounds(wrk,4) = ',LBOUND(wrk,4),UBOUND(wrk,4)
do n = LBOUND(wrk,4),UBOUND(wrk,4)
do iz = 1, nz
call DFFTW_PLAN_DFT_R2C_2D(plan_r2c(iz, n), nx, ny, &
xy_sheet, wrk(1, 1, iz, n), &
FFTW_ESTIMATE)
end do
end do
! separately initializing the plans for FFT of the array "fields"
do n = LBOUND(fields,4),UBOUND(fields,4)
do iz = 1, nz
call DFFTW_PLAN_DFT_R2C_2D(plan_r2c_f(iz, n), nx, ny, &
xy_sheet, fields(1, 1, iz, n), &
FFTW_ESTIMATE)
end do
end do
!------------------------------------------------------------------------------!
! initializing FFTW plan for the 2nd step in FFT --- 1D c2c
!------------------------------------------------------------------------------!
call DFFTW_PLAN_DFT_1D(plan_f_c2c, nz_all, z_stick, z_stick, &
FFTW_FORWARD, FFTW_ESTIMATE)
!------------------------------------------------------------------------------!
! initializing FFTW plan for the 1st step in IFFT --- 1D c2c
!------------------------------------------------------------------------------!
call DFFTW_PLAN_DFT_1D(plan_b_c2c, nz_all, z_stick, z_stick, &
FFTW_BACKWARD, FFTW_ESTIMATE)
!------------------------------------------------------------------------------!
! initializing FFTW plans for the 2nd step in IFFT --- 2D c2r
!------------------------------------------------------------------------------!
do n = LBOUND(wrk,4),UBOUND(wrk,4)
do iz = 1, nz
call DFFTW_PLAN_DFT_C2R_2D(plan_c2r(iz, n), nx, ny, &
wrk(1, 1, iz, n), xy_sheet, &
FFTW_ESTIMATE)
end do
end do
! separately intializing the plans for FFT of the array "fields"
do n = LBOUND(fields,4),UBOUND(fields,4)
do iz = 1, nz
call DFFTW_PLAN_DFT_C2R_2D(plan_c2r_f(iz, n), nx, ny, &
fields(1, 1, iz, n), xy_sheet, &
FFTW_ESTIMATE)
end do
end do
!------------------------------------------------------------------------------!
! initializing some useful constants
!------------------------------------------------------------------------------!
nx21 = nx / 2 + 1
norm = one / real(nx * ny * nz_all, 8)
!------------------------------------------------------------------------------!
! filling up the wavenumber arrays akx, aky, akz
! filling up the wavenumber arrays rezkax, rezkay, rezkaz
!------------------------------------------------------------------------------!
! in Fourier space it is (nx / 2 + 1) complex numbers along kx-axis
do ix = 1, nx + 1, 2
akx(ix) = real((ix - 1) / 2, 8) * (two / lx) !ksj modification1
akx(ix + 1) = akx(ix)
coskx2(ix) = dcos(half * akx(ix))
sinkx2(ix) = dsin(half * akx(ix))
coskx2(ix + 1) = coskx2(ix)
sinkx2(ix + 1) = sinkx2(ix)
rezkax(ix) = 0
if (dabs(akx(ix)) > (real(nz_all, 8)) / 3.0D0) rezkax(ix) = 1
end do
! in Fourier space ky-axis is distributed among the processors
do iy = 1, nz
aky(iy) = real(myid * nz + iy - 1, 8) * (two / ly) !ksj modification1
if (aky(iy) > (0.5D0 * real(ny, 8) * (two / ly))) aky(iy) = aky(iy) - real(ny, 8) * (two / ly)
cosky2(iy) = dcos(half * aky(iy))
sinky2(iy) = dsin(half * aky(iy))
rezkay(iy) = 0
if (dabs(aky(iy)) > (real(ny, 8) * (two / ly)) / 3.0D0) rezkay(iy) = 1
end do
! in Fourier space the z wavenumbers are aligned along the second index
do iz = 1, ny
akz(iz) = real(iz - 1, 8) * (two / lz) !ksj modification1
if (akz(iz) > (0.5D0 * real(nz_all, 8) * (two / lz))) akz(iz) = akz(iz) - real(nz_all, 8) * (two / lz)
coskz2(iz) = dcos(half * akz(iz))
sinkz2(iz) = dsin(half * akz(iz))
rezkaz(iz) = 0
if (dabs(akz(iz)) > (real(nz_all, 8) * (two / lz)) / 3.0D0) rezkaz(iz) = 1
end do
! Definition of the array ialias.
! The array ialias is just the number of wavenumbers at (i,j,k) that have
! their magnitude higher than nx/3. This is needed in dealiasing procedures.
rnx3 = real(2*nx, 8) / real(3*lx, 8)
do k = 1,nz
if (abs(aky(k)) .gt. rnx3) ialias(:,:,k) = 1
do j = 1,ny
if (abs(akz(j)) .gt. rnx3) ialias(:,j,k) = ialias(:,j,k) + 1
do i = 1,nx+2
if (abs(akx(i)) .gt. rnx3) ialias(i,j,k) = ialias(i,j,k) + 1
end do
end do
end do
write(out,*) "x_fftw arrays are intiialized."
call flush(out)
return
end subroutine x_fftw_init
!==============================================================================!
! Subroutine that performs the FFT of a 3-D variable. The variable is
! contained within the array "wrk(:, :, :, n)". Note that the
! result of FFT has different coordinate arrangement: in physical
! space it is (x, y, z), and in Fourier space it is (kx, kz, ky).
! Details can be extracted from very graphic comments in the body of
! the subroutine.
!==============================================================================!
subroutine xxxFFT3d(flag, n)
use m_openmpi
implicit none
integer :: flag, n
integer :: ix, iy, iz
real(kind=8) :: rtmp
integer(kind=MPI_INTEGER_KIND) :: iproc
if (flag == 1) then
!------------------------------------------------------------------------------!
! Direct FFT, step 1: 2-D real-to-complex transform
!------------------------------------------------------------------------------!
!
! R2C (# = ny) A y A y (# = ny) A k_y
! | | |
! +---+---+---+---+ + +---+---+---+---+
! / / / / /| /| / / / / /|
! / / / / wrk xy_sheet / / / / wrk
! / / / / / | / | / / / / / |
! +---+---+---+---+ | + | +---+---+---+---+ |
! | | | | | | | | R2C | | | | | |
! | | | | | | -----> | | -----> | | | | | |
! | | | | | | | | | | | | | |
! <---| | | | | + | + <---| | | | | +
! z | | | | | / | / z | | | | | /
! | | | | | / | / | | | | | /
! | | | | |/ |/ | | | | |/
! +---+---+---+---+ + +---+---+---+---+
! / / /
! (# = nx) V x V x (# = nx / 2 + 1) V k_x
!
! 3 2 1 0 --- myid 3 2 1 0 --- myid
!
!------------------------------------------------------------------------------!
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
xy_sheet(ix, iy) = wrk(ix, iy, iz, n)
end do
end do
!!$ write(out,*) 'before r2c',iz,n,plan_r2c(iz, n),size(plan_r2c,1),size(plan_r2c,2)
!!$ call flush(out)
call DFFTW_EXECUTE(plan_r2c(iz, n))
end do
!------------------------------------------------------------------------------!
! Direct FFT, step 2: transposing the variable via MPI messaging
!------------------------------------------------------------------------------!
!
! MPI (# = ny) A k_y +---+ +---+ (# = nz_all) A z
! | /buff| /buff| |
! +---+---+---+---+ / / + / / + +---+---+---+---+
! / /+++/ / /| / / / MPI / / / / / / / /|
! / /+++/ / wrk +---+ / ----> +---+ / / ../. / / wrk
! / /+++/ / / | | |/ | |/ / ../.. / / / |
! +---+---+---+---+ | +---+ +---+ +---+---+---+---+ |
! | |+++| | | | A \ |...|. | | | |
! | | \____ | ____/ `--->+++| | | | |
! | | | |\_____/| |+++| | | | |
! <---| | | | | + <---| | | | | +
! z | | | | | / k_y | | | | | /
! (nz_all / numprocs)| | / (# = ny / numprocs)| | /
! | | | | |/ | | | | |/
! +---+---+---+---+ +---+---+---+---+
! / /
! V k_x V k_x
!
! 3 2 1 0 --- myid 3 2 1 0 --- myid
!
!------------------------------------------------------------------------------!
! - "diagonal" messages, no need to use MPI
!!$ write(out,*) 'before diagonal message'
!!$ call flush(out)
do iz = 1, nz - 1
do iy = iz + 1, nz
do ix = 1, nx + 2
rtmp = wrk(ix, myid * nz + iy, iz, n)
wrk(ix, myid * nz + iy , iz, n) = &
& wrk(ix, myid * nz + iz, iy, n)
wrk(ix, myid * nz + iz, iy, n) = rtmp
end do
end do
end do
! - sending and receiving MPI messages
!!$ write(out,*) 'before MPI message'
!!$ call flush(out)
count = (nx+2) * ny * nz
do iproc = 1, numprocs - 1
do iy = 1, nz
do iz = 1, nz
buff(:, iz, iy) = wrk(:, order(iproc) * nz + iy, iz, n)
end do
end do
call MPI_SENDRECV_REPLACE(buff, &
& (nx + 2) * nz * nz, MPI_REAL8, &
& order(iproc), myid * numprocs + order(iproc), &
& order(iproc), order(iproc) * numprocs + myid, &
& MPI_COMM_TASK, mpi_status, mpi_err)
do iy = 1, nz
do iz = 1, nz
wrk(:, order(iproc) * nz + iz, iy, n) = buff(:, iz, iy)
end do
end do
end do
!------------------------------------------------------------------------------!
! Direct FFT, step 3: one-dimensional complex-to-complex FFT
!------------------------------------------------------------------------------!
!
! C2C-> A z A k_z
! | A z A k_z |
! +---+---+---+---+ | | +---+---+---+---+
! / / / / /| + + / / / / /|
! / / / / wrk | | / / / / wrk
! / / / / / | z_stick z_stick / / / / / |
! +---+---+---+---+ | | | +---+---+---+---+ |
! | | | | | | | C2C | | | | | | |
! | | | | | | -----> | -----> | -----> | | | | | |
! | | | | | | | | | | | | | |
! <---| | | | | + | | <---| | | | | +
! k_y | | | | | / + + k_y | | | | | /
! | | | | | / | | | | | /
! | | | | |/ | | | | |/
! +---+---+---+---+ +---+---+---+---+
! / /
! V k_x V k_x
!
! 3 2 1 0 --- myid 3 2 1 0 --- myid
!
!------------------------------------------------------------------------------!
!!$ write(out,*) 'before 1d fft'
!!$ call flush(out)
do iy = 1, nz
do ix = 1, nx21
do iz = 1, nz_all
z_stick(2 * iz - 1) = wrk(2 * ix - 1, iz, iy, n)
z_stick(2 * iz) = wrk(2 * ix, iz, iy, n)
end do
!!$ write(out,*) 'before 1d fft',iy
!!$ call flush(out)
call DFFTW_EXECUTE(plan_f_c2c)
do iz = 1, nz_all
wrk(2 * ix - 1, iz, iy, n) = z_stick(2 * iz - 1)
wrk(2 * ix, iz, iy, n) = z_stick(2 * iz)
end do
end do
end do
!------------------------------------------------------------------------------!
elseif (flag == -1) then
!------------------------------------------------------------------------------!
! Inverse FFT, step 1: one-dimensionsal complex-to-complex transform
!------------------------------------------------------------------------------!
!
! <-C2C A k_z A z
! | A k_z A z |
! +---+---+---+---+ | | +---+---+---+---+
! / / / / /| + + / / / / /|
! / / / / wrk | | / / / / wrk
! / / / / / | z_stick z_stick / / / / / |
! +---+---+---+---+ | | | +---+---+---+---+ |
! | | | | | | | C2C | | | | | | |
! | | | | | | -----> | -----> | -----> | | | | | |
! | | | | | | | | | | | | | |
! <---| | | | | + | | <---| | | | | +
! k_y | | | | | / + + k_y | | | | | /
! | | | | | / | | | | | /
! | | | | |/ | | | | |/
! +---+---+---+---+ +---+---+---+---+
! / /
! V k_x V k_x
!
! 3 2 1 0 --- myid 3 2 1 0 --- myid
!
!------------------------------------------------------------------------------!
do iy = 1, nz
do ix = 1, nx21
do iz = 1, nz_all
z_stick(2 * iz - 1) = wrk(2 * ix - 1, iz, iy, n)
z_stick(2 * iz) = wrk(2 * ix, iz, iy, n)
end do
call DFFTW_EXECUTE(plan_b_c2c)
do iz = 1, nz_all
wrk(2 * ix - 1, iz, iy, n) = z_stick(2 * iz - 1)
wrk(2 * ix, iz, iy, n) = z_stick(2 * iz)
end do
end do
end do
!------------------------------------------------------------------------------!
! Inverse FFT, step 2: transposing the variable via MPI messaging
!------------------------------------------------------------------------------!
!
! MPI (# = nz_all) A z +---+ +---+ (# = ny) A k_y
! | /buff| /buff| |
! +---+---+---+---+ / / + / / + +---+---+---+---+
! / /+++/ / /| / / / MPI / / / / / / / /|
! / /+++/ / wrk +---+ / ----> +---+ / / ../. / / wrk
! / /+++/ / / | | |/ | |/ / ../.. / / / |
! +---+---+---+---+ | +---+ +---+ +---+---+---+---+ |
! | |+++| | | | A \ |...|. | | | |
! | | \____ | ____/ `--->+++| | | | |
! | | | |\_____/| |+++| | | | |
! <---| | | | | + <---| | | | | +
! k_y | | | | | / z | | | | | /
! (# = ny / numprocs)| | / (# = nz_all / numprocs)| | /
! | | | | |/ | | | | |/
! +---+---+---+---+ +---+---+---+---+
! / /
! V k_x V k_x
!
! 3 2 1 0 --- myid 3 2 1 0 --- myid
!
!------------------------------------------------------------------------------!
! - "diagonal" messages, no need to use MPI
do iy = 1, nz - 1
do iz = iy + 1, nz
do ix = 1, nx + 2
rtmp = wrk(ix, myid * nz + iz, iy, n)
wrk(ix, myid * nz + iz , iy, n) = &
& wrk(ix, myid * nz + iy, iz, n)
wrk(ix, myid * nz + iy, iz, n) = rtmp
end do
end do
end do
! - sending and receiving MPI messages
do iproc = 1, numprocs - 1
do iz = 1, nz
do iy = 1, nz
buff(:, iy, iz) = wrk(:, order(iproc) * nz + iz, iy, n)
end do
end do
call MPI_SENDRECV_REPLACE(buff, &
& (nx + 2) * nz * nz, MPI_REAL8, &
& order(iproc), myid * numprocs + order(iproc), &
& order(iproc), order(iproc) * numprocs + myid, &
& MPI_COMM_TASK, mpi_status, mpi_err)
do iz = 1, nz
do iy = 1, nz
wrk(:, order(iproc) * nz + iy, iz, n) = buff(:, iy, iz)
end do
end do
end do
!------------------------------------------------------------------------------!
! Inverse FFT, step 3: 2-D complex-to-real transform
!------------------------------------------------------------------------------!
!
! C2R (# = ny) A k_y A y (# = ny) A y
! | | |
! +---+---+---+---+ + +---+---+---+---+
! / / / / /| /| / / / / /|
! / / / / wrk xy_sheet / / / / wrk
! / / / / / | / | / / / / / |
! +---+---+---+---+ | + | +---+---+---+---+ |
! | | | | | | C2R | | | | | | | |
! | | | | | | -----> | | -----> | | | | | |
! | | | | | | | | | | | | | |
! <---| | | | | + | + <---| | | | | +
! z | | | | | / | / z | | | | | /
! | | | | | / | / | | | | | /
! | | | | |/ |/ | | | | |/
! +---+---+---+---+ + +---+---+---+---+
! / / /
! (# = nx / 2 + 1) V k_x V x (# = nx) V x
!
! 3 2 1 0 --- myid 3 2 1 0 --- myid
!
!------------------------------------------------------------------------------!
do iz = 1, nz
call DFFTW_EXECUTE(plan_c2r(iz, n))
do iy = 1, ny
do ix = 1, nx
wrk(ix, iy, iz, n) = norm * xy_sheet(ix, iy)
end do
end do
end do
!------------------------------------------------------------------------------!
end if
return
end subroutine xxxFFT3d
!==============================================================================!
! Subroutine that calculates the derivative.
!
! Takes variable from wrk(:,:,:,n), differentiates it and put into
! wrk(:,:,:,nto). All happens in Fourier space
!==============================================================================!
subroutine x_derivative(n,axis,nto)
implicit none
integer :: ix, iy, iz, n, nto
real(kind=8) :: rtmp
character :: axis
! in Fourier space it is (kx, kz, ky)
! here we multiply by k-vector, will multiply by i later
select case (axis)
case ('x')
do iy = 1, nz
do iz = 1, nz_all
do ix = 1, nx + 2
wrk(ix, iz, iy, nto) = akx(ix) * wrk(ix, iz, iy, n)
end do
end do
end do
case ('y')
do iy = 1, nz
wrk(:, :, iy, nto) = aky(iy) * wrk(:, :, iy, n)
end do
case ('z')
do iy = 1, nz
do iz = 1, nz_all
wrk(:, iz, iy, nto) = akz(iz) * wrk(:, iz, iy, n)
end do
end do
case default
write (out, *) '*** x_derivative: wrong value of axis: ', axis
call my_exit(-1)
end select
! multiplying wrk(ix) + i wrk(ix + 1) by i
do iy = 1, nz
do iz = 1, nz_all
do ix = 1, nx + 1, 2
rtmp = -wrk(ix + 1, iz, iy, nto)
wrk(ix + 1, iz, iy, nto) = wrk(ix, iz, iy, nto)
wrk(ix, iz, iy, nto) = rtmp
end do
end do
end do
end subroutine x_derivative
!==============================================================================!
! Subroutine --- dealiasing.
!==============================================================================!
subroutine x_dealiasing(n1, n2)
implicit none
integer :: n1, n2, tmp1, tmp2, tmp
integer :: ix, iy, iz
tmp = 0
tmp1 = 1
tmp2 = 2
do iy = 1, nz
do iz = 1, nz_all
do ix = 1, nx + 2
if (rezkax(ix) + rezkay(iy) + rezkaz(iz) > 1) then
wrk(ix, iz, iy, n1) = zip
wrk(ix, iz, iy, n2) = zip
end if
wrk(ix, iz, iy, tmp1) = wrk(ix, iz, iy, n1)
wrk(ix, iz, iy, tmp2) = wrk(ix, iz, iy, n2)
end do
end do
end do
call xFFT3d(-1, tmp1)
call xFFT3d(-1, tmp2)
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
wrk(ix, iy, iz, tmp) = wrk(ix, iy, iz, tmp1) * &
& wrk(ix, iy, iz, tmp2)
end do
end do
end do
! phase-shift, x direction
do iy = 1, nz
do iz = 1, nz_all
do ix = 1, nx21, 2
wrk(ix, iz, iy, tmp1) = coskx2(ix) * &
wrk(ix, iz, iy, n1) - &
& sinkx2(ix) * &
wrk(ix + 1, iz, iy, n1)
wrk(ix + 1, iz, iy, tmp1) = coskx2(ix) * &
wrk(ix + 1, iz, iy, n1) + &
& sinkx2(ix) * &
wrk(ix, iz, iy, n1)
wrk(ix, iz, iy, tmp2) = coskx2(ix) * &
wrk(ix, iz, iy, n2) - &
& sinkx2(ix) * &
wrk(ix + 1, iz, iy, n2)
wrk(ix + 1, iz, iy, tmp2) = coskx2(ix) * &
wrk(ix + 1, iz, iy, n2) + &
& sinkx2(ix) * &
wrk(ix, iz, iy, n2)
end do
end do
end do
call xFFT3d(-1, tmp1)
call xFFT3d(-1, tmp2)
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
wrk(ix, iy, iz, tmp) = wrk(ix, iy, iz, tmp) + &
wrk(ix, iy, iz, tmp1) * &
& wrk(ix, iy, iz, tmp2)
end do
end do
end do
! phase-shift, y direction
do iy = 1, nz
do iz = 1, nz_all
do ix = 1, nx21, 2
wrk(ix, iz, iy, tmp1) = cosky2(iy) * &
wrk(ix, iz, iy, n1) - &
& sinky2(iy) * &
wrk(ix + 1, iz, iy, n1)
wrk(ix + 1, iz, iy, tmp1) = cosky2(iy) * &
wrk(ix + 1, iz, iy, n1) + &
& sinky2(iy) * &
wrk(ix, iz, iy, n1)
wrk(ix, iz, iy, tmp2) = cosky2(iy) * &
wrk(ix, iz, iy, n2) - &
& sinky2(iy) * &
wrk(ix + 1, iz, iy, n2)
wrk(ix + 1, iz, iy, tmp2) = cosky2(iy) * &
wrk(ix + 1, iz, iy, n2) + &
& sinky2(iy) * &
wrk(ix, iz, iy, n2)
end do
end do
end do
call xFFT3d(-1, tmp1)
call xFFT3d(-1, tmp2)
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
wrk(ix, iy, iz, tmp) = wrk(ix, iy, iz, tmp) + &
wrk(ix, iy, iz, tmp1) * &
& wrk(ix, iy, iz, tmp2)
end do
end do
end do
! phase-shift, z direction
do iy = 1, nz
do iz = 1, nz_all
do ix = 1, nx21, 2
wrk(ix, iz, iy, tmp1) = coskz2(iz) * &
wrk(ix, iz, iy, n1) - &
& sinkz2(iz) * &
wrk(ix + 1, iz, iy, n1)
wrk(ix + 1, iz, iy, tmp1) = coskz2(iz) * &
wrk(ix + 1, iz, iy, n1) + &
& sinkz2(iz) * &
wrk(ix, iz, iy, n1)
wrk(ix, iz, iy, tmp2) = coskz2(iz) * &
wrk(ix, iz, iy, n2) - &
& sinkz2(iz) * &
wrk(ix + 1, iz, iy, n2)
wrk(ix + 1, iz, iy, tmp2) = coskz2(iz) * &
wrk(ix + 1, iz, iy, n2) + &
& sinkz2(iz) * &
wrk(ix, iz, iy, n2)
end do
end do
end do
call xFFT3d(-1, tmp1)
call xFFT3d(-1, tmp2)
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
wrk(ix, iy, iz, tmp) = 0.25D0 * (wrk(ix, iy, iz, tmp) + &
wrk(ix, iy, iz, tmp1) * &
& wrk(ix, iy, iz, tmp2))
end do
end do
end do
call xFFT3d(1, tmp)
end subroutine x_dealiasing
!==============================================================================!
!==============================================================================!
!==============================================================================!
!==============================================================================!
! Subroutine that performs the FFT of a 3-D variable. The variable is
! contained within the array "wrk(:, :, :, n)". Note that the
! result of FFT has different coordinate arrangement: in physical
! space it is (x, y, z), and in Fourier space it is (kx, kz, ky).
! Details can be extracted from very graphic comments in the body of
! the subroutine.
!==============================================================================!
subroutine xFFT3d(flag, n)
use m_openmpi
use m_io
implicit none
integer :: flag, n
integer :: ix, iy, iz, i, j, k
INTEGER (KIND=MPI_INTEGER_KIND) :: tag1, tag2, iproc
real(kind=8) :: rtmp
if (flag == 1) then
!------------------------------------------------------------------------------!
! Direct FFT, step 1: 2-D real-to-complex transform
!------------------------------------------------------------------------------!
if(benchmarking) then
call system_clock(i81,dcpu)
bm(11) = bm(11) - i81
end if
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
xy_sheet(ix, iy) = wrk(ix, iy, iz, n)
end do
end do
call DFFTW_EXECUTE(plan_r2c(iz, n))
end do
if (benchmarking) then
call system_clock(i82,dcpu)
bm(1) = bm(1) + i82 - i81
i81 = i82
end if
!------------------------------------------------------------------------------!
! Direct FFT, step 2: transposing the variable via MPI messaging
!------------------------------------------------------------------------------!
! - "diagonal" messages, no need to use MPI
do iz = 1, nz - 1
do iy = iz + 1, nz
do ix = 1, nx + 2
rtmp = wrk(ix, myid * nz + iy, iz, n)
wrk(ix, myid * nz + iy , iz, n) = &
& wrk(ix, myid * nz + iz, iy, n)
wrk(ix, myid * nz + iz, iy, n) = rtmp
end do
end do
end do
! - sending and receiving MPI messages
count = (nx+2) * nz * nz
do iproc = 1, numprocs - 1
!!$ ! The following order should be implemented in the future,
!!$ ! because it shaves off about 4% of time
!!$ id_to = mod(myid+iproc,numprocs)
!!$ id_from = mod(myid-iproc+numprocs,numprocs)
id_to = order(iproc)
id_from = id_to
tag1 = myid*numprocs + iproc
tag2 = id_from*numprocs + iproc
do iy = 1, nz
do iz = 1, nz
buff(:, iz, iy) = wrk(:, id_to * nz + iy, iz, n)
end do
end do
call MPI_SENDRECV(&
buff, count, MPI_REAL8, id_to, tag1, &
buff2, count, MPI_REAL8, id_from, tag2, &
MPI_COMM_TASK, mpi_status, mpi_err)
do iy = 1, nz
do iz = 1, nz
wrk(:, id_from * nz + iz, iy, n) = buff2(:, iz, iy)
end do
end do
end do
if (benchmarking) then
call system_clock(i82,dcpu)
bm(2) = bm(2) + i82 - i81
i81 = i82
end if
!------------------------------------------------------------------------------!
! Direct FFT, step 3: one-dimensional complex-to-complex FFT
!------------------------------------------------------------------------------!
do iy = 1, nz
do ix = 1, nx21
do iz = 1, nz_all
z_stick(2 * iz - 1) = wrk(2 * ix - 1, iz, iy, n)
z_stick(2 * iz) = wrk(2 * ix, iz, iy, n)
end do
call DFFTW_EXECUTE(plan_f_c2c)
do iz = 1, nz_all
wrk(2 * ix - 1, iz, iy, n) = z_stick(2 * iz - 1)
wrk(2 * ix, iz, iy, n) = z_stick(2 * iz)
end do
end do
end do
if (benchmarking) then
call system_clock(i82,dcpu)
bm(3) = bm(3) + i82 - i81
bm(11) = bm(11) + i82
end if
!------------------------------------------------------------------------------!
elseif (flag == -1) then
!------------------------------------------------------------------------------!
! Inverse FFT, step 1: one-dimensionsal complex-to-complex transform
!------------------------------------------------------------------------------!
if(benchmarking) then
call system_clock(i81,dcpu)
bm(12) = bm(12) - i81
end if
do iy = 1, nz
do ix = 1, nx21
do iz = 1, nz_all
z_stick(2 * iz - 1) = wrk(2 * ix - 1, iz, iy, n)
z_stick(2 * iz) = wrk(2 * ix, iz, iy, n)
end do
call DFFTW_EXECUTE(plan_b_c2c)
do iz = 1, nz_all
wrk(2 * ix - 1, iz, iy, n) = z_stick(2 * iz - 1)
wrk(2 * ix, iz, iy, n) = z_stick(2 * iz)
end do
end do
end do
if (benchmarking) then
call system_clock(i82,dcpu)
bm(4) = bm(4) + i82 - i81
i81 = i82
end if
!------------------------------------------------------------------------------!
! Inverse FFT, step 2: transposing the variable via MPI messaging
!------------------------------------------------------------------------------!
! - "diagonal" messages, no need to use MPI
do iy = 1, nz - 1
do iz = iy + 1, nz
do ix = 1, nx + 2
rtmp = wrk(ix, myid * nz + iz, iy, n)
wrk(ix, myid * nz + iz , iy, n) = &
& wrk(ix, myid * nz + iy, iz, n)
wrk(ix, myid * nz + iy, iz, n) = rtmp
end do
end do
end do
! - sending and receiving MPI messages
count = (nx+2) * nz * nz
do iproc = 1, numprocs - 1
!!$ ! This order should be implemented in the future because this saves
!!$ ! about 4% of wallclock time for FFT
!!$ id_to = mod(myid+iproc,numprocs)
!!$ id_from = mod(myid-iproc+numprocs,numprocs)
id_to = order(iproc)
id_from = order(iproc)
tag1 = myid*numprocs + iproc
tag2 = id_from*numprocs + iproc
do iz = 1, nz
do iy = 1, nz
buff(:, iy, iz) = wrk(:, id_to * nz + iz, iy, n)
end do
end do
call MPI_SENDRECV(&
buff, count, MPI_REAL8, id_to, tag1, &
buff2, count, MPI_REAL8, id_from, tag2, &
MPI_COMM_TASK, mpi_status, mpi_err)
do iz = 1, nz
do iy = 1, nz
wrk(:, id_from * nz + iy, iz, n) = buff2(:, iy, iz)
end do
end do
end do
if (benchmarking) then
call system_clock(i82,dcpu)
bm(5) = bm(5) + i82 - i81
i81 = i82
end if
!------------------------------------------------------------------------------!
! Inverse FFT, step 3: 2-D complex-to-real transform
!------------------------------------------------------------------------------!
do iz = 1, nz
call DFFTW_EXECUTE(plan_c2r(iz, n))
do iy = 1, ny
do ix = 1, nx
wrk(ix, iy, iz, n) = norm * xy_sheet(ix, iy)
end do
end do
end do
if (benchmarking) then
call system_clock(i82,dcpu)
bm(6) = bm(6) + i82 - i81
bm(12) = bm(12) + i82
end if
!------------------------------------------------------------------------------!
end if
return
end subroutine xFFT3d
!================================================================================
!==============================================================================!
! Subroutine that performs the FFT of a 3-D variable. The variable is
! contained within the array "fields(:, :, :, n)". Note that the
! result of FFT has different coordinate arrangement: in physical
! space it is (x, y, z), and in Fourier space it is (kx, kz, ky).
!==============================================================================!
subroutine xFFT3d_fields(flag, n)
use m_openmpi
use m_io
use m_fields
implicit none
integer :: flag, n
integer :: ix, iy, iz, i, j, k
INTEGER (KIND=MPI_INTEGER_KIND) :: tag1, tag2, iproc
real(kind=8) :: rtmp
if (flag == 1) then
!------------------------------------------------------------------------------!
! Direct FFT, step 1: 2-D real-to-complex transform
!------------------------------------------------------------------------------!
do iz = 1, nz
do iy = 1, ny
do ix = 1, nx
xy_sheet(ix, iy) = fields(ix, iy, iz, n)
end do
end do
call DFFTW_EXECUTE(plan_r2c_f(iz, n))
end do
!------------------------------------------------------------------------------!
! Direct FFT, step 2: transposing the variable via MPI messaging
!------------------------------------------------------------------------------!
! - "diagonal" messages, no need to use MPI
do iz = 1, nz - 1
do iy = iz + 1, nz
do ix = 1, nx + 2
rtmp = fields(ix, myid * nz + iy, iz, n)
fields(ix, myid * nz + iy , iz, n) = &
& fields(ix, myid * nz + iz, iy, n)
fields(ix, myid * nz + iz, iy, n) = rtmp
end do
end do
end do
! - sending and receiving MPI messages
count = (nx+2) * nz * nz
do iproc = 1, numprocs - 1
!!$ ! The following order should be implemented in the future,
!!$ ! because it shaves off about 4% of time
!!$ id_to = mod(myid+iproc,numprocs)
!!$ id_from = mod(myid-iproc+numprocs,numprocs)
id_to = order(iproc)
id_from = id_to
tag1 = myid*numprocs + iproc
tag2 = id_from*numprocs + iproc
do iy = 1, nz
do iz = 1, nz
buff(:, iz, iy) = fields(:, id_to * nz + iy, iz, n)
end do
end do
call MPI_SENDRECV(&
buff, count, MPI_REAL8, id_to, tag1, &
buff2, count, MPI_REAL8, id_from, tag2, &
MPI_COMM_TASK, mpi_status, mpi_err)
do iy = 1, nz
do iz = 1, nz
fields(:, id_from * nz + iz, iy, n) = buff2(:, iz, iy)
end do
end do
end do
!------------------------------------------------------------------------------!
! Direct FFT, step 3: one-dimensional complex-to-complex FFT
!------------------------------------------------------------------------------!
do iy = 1, nz
do ix = 1, nx21
do iz = 1, nz_all
z_stick(2 * iz - 1) = fields(2 * ix - 1, iz, iy, n)
z_stick(2 * iz ) = fields(2 * ix , iz, iy, n)
end do
call DFFTW_EXECUTE(plan_f_c2c)
do iz = 1, nz_all
fields(2 * ix - 1, iz, iy, n) = z_stick(2 * iz - 1)
fields(2 * ix , iz, iy, n) = z_stick(2 * iz)
end do
end do
end do
!------------------------------------------------------------------------------!
elseif (flag == -1) then
!------------------------------------------------------------------------------!
! Inverse FFT, step 1: one-dimensionsal complex-to-complex transform
!------------------------------------------------------------------------------!
do iy = 1, nz
do ix = 1, nx21
do iz = 1, nz_all
z_stick(2 * iz - 1) = fields(2 * ix - 1, iz, iy, n)
z_stick(2 * iz ) = fields(2 * ix , iz, iy, n)
end do
call DFFTW_EXECUTE(plan_b_c2c)
do iz = 1, nz_all
fields(2 * ix - 1, iz, iy, n) = z_stick(2 * iz - 1)
fields(2 * ix , iz, iy, n) = z_stick(2 * iz)
end do
end do
end do
!------------------------------------------------------------------------------!
! Inverse FFT, step 2: transposing the variable via MPI messaging
!------------------------------------------------------------------------------!
! - "diagonal" messages, no need to use MPI
do iy = 1, nz - 1
do iz = iy + 1, nz
do ix = 1, nx + 2
rtmp = fields(ix, myid * nz + iz, iy, n)
fields(ix, myid * nz + iz , iy, n) = &
& fields(ix, myid * nz + iy, iz, n)
fields(ix, myid * nz + iy, iz, n) = rtmp
end do
end do
end do
! - sending and receiving MPI messages
count = (nx+2) * nz * nz
do iproc = 1, numprocs - 1
!!$ ! This order should be implemented in the future because this saves
!!$ ! about 4% of wallclock time for FFT
!!$ id_to = mod(myid+iproc,numprocs)
!!$ id_from = mod(myid-iproc+numprocs,numprocs)
id_to = order(iproc)
id_from = order(iproc)
tag1 = myid*numprocs + iproc
tag2 = id_from*numprocs + iproc
do iz = 1, nz
do iy = 1, nz
buff(:, iy, iz) = fields(:, id_to * nz + iz, iy, n)
end do
end do
call MPI_SENDRECV(&
buff, count, MPI_REAL8, id_to, tag1, &
buff2, count, MPI_REAL8, id_from, tag2, &
MPI_COMM_TASK, mpi_status, mpi_err)
do iz = 1, nz
do iy = 1, nz
fields(:, id_from * nz + iy, iz, n) = buff2(:, iy, iz)
end do
end do
end do
!------------------------------------------------------------------------------!
! Inverse FFT, step 3: 2-D complex-to-real transform
!------------------------------------------------------------------------------!
do iz = 1, nz
call DFFTW_EXECUTE(plan_c2r_f(iz, n))
do iy = 1, ny
do ix = 1, nx
fields(ix, iy, iz, n) = norm * xy_sheet(ix, iy)
end do
end do
end do
!------------------------------------------------------------------------------!
end if
return
end subroutine xFFT3d_fields
!================================================================================
end MODULE X_FFTW