I’d like to use the MKL FFT routines to apply the Laplace operator on a 2-d periodic function sampled on a N \times N grid. I’ve figured out how to calculate the forward and backward transforms, but I can’t seem to figure out the correct scaling factors of the transformed multi-dimensional complex conjugate-even array:
! dfti.f90 -
! Apply Laplace operator to a periodic function in 2-d using DFT
!
! Compile using:
! ifort -warn all -qmkl dfti.f90 -o dfti
!
include "mkl_dfti.f90"
program dfti
use mkl_dfti
implicit none
integer, parameter :: dp = kind(1.0d0)
type(dfti_descriptor), pointer :: dh
integer, parameter :: n = 16
real(dp), target :: x(n,n), xpp(n,n)
real(dp), pointer :: x1d(:) => null()
integer, parameter :: ny = int(n/2.0) + 1
complex(dp), target :: y(ny,n)
complex(dp), pointer :: y1d(:) => null()
real(dp), parameter :: pi = 4.0_dp*atan(1.0_dp)
real(dp) :: sigma
integer :: xdim(2), i, j, rstrides(3), cstrides(3), istat
call init_x(n,x,xpp)
open(unit=10,file="field_in.txt")
do i = 1, n
do j = 1, n
write(10,*) (i-1)/real(n,dp), (j-1)/real(n,dp), x(i,j)
end do
end do
close(10)
x1d(1:n*n) => x
y1d(1:ny*n) => y
xdim = [n, n]
rstrides = [0,1,n]
cstrides = [0,1,ny]
istat = DftiCreateDescriptor(dh,DFTI_DOUBLE,DFTI_REAL,2,xdim)
call check_dfti_error(istat)
istat = DftiSetValue(dh, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX)
istat = DftiSetValue(dh, DFTI_PLACEMENT,DFTI_NOT_INPLACE)
istat = DftiSetValue(dh, DFTI_INPUT_STRIDES, rstrides)
istat = DftiSetValue(dh, DFTI_OUTPUT_STRIDES, cstrides)
istat = DftiCommitDescriptor(dh)
call check_dfti_error(istat)
! Perform a real to complex conjugate-even transform
istat = DftiComputeForward(dh,x1d,y1d)
call check_dfti_error(istat)
! Calculate second derivative coefficients
! do j = 1, n
! do i = 1, ny
! y(i,j) = -i**2 * y(i,j)
! end do
! end do
istat = DftiSetValue(dh, DFTI_INPUT_STRIDES, cstrides)
istat = DftiSetValue(dh, DFTI_OUTPUT_STRIDES, rstrides)
sigma = 1.0_dp/n**2
istat = DftiSetValue(dh, DFTI_BACKWARD_SCALE, sigma)
istat = DftiCommitDescriptor(dh)
istat = DftiComputeBackward(dh,y1d,x1d)
call check_dfti_error(istat)
open(unit=10,file="field_out.txt")
do i = 1, n
do j = 1, n
write(10,'(*(G0,1X))') (i-1)/real(n,dp), (j-1)/real(n,dp), x(i,j), xpp(i,j)
end do
end do
close(10)
istat = DftiFreeDescriptor(dh)
call check_dfti_error(istat)
contains
subroutine init_x(n,f,fpp)
integer, intent(in) :: n
real(dp), intent(out) :: f(n,n),fpp(n,n)
real(dp), parameter :: pi = 4.0_dp*atan(1.0_dp)
real(dp) :: h, tx, ty
integer :: i, j
h = 1.0_dp/real(n,dp)
do j = 1, n
do i = 1, n
tx = (j-1)*h
ty = (i-1)*h
f(i,j) = sin(2*pi*tx)*cos(2*pi*ty)
! f''
fpp(i,j) = -8.0_dp*pi**2 * sin(2*pi*tx) * cos(2*pi*ty)
end do
end do
end subroutine
subroutine check_dfti_error(status,warn_only)
use, intrinsic :: iso_fortran_env, only: error_unit
integer, intent(in) :: status
logical, intent(in), optional :: warn_only ! default is .false.
character(len=DFTI_MAX_MESSAGE_LENGTH) :: errmsg
logical :: warn_only_
warn_only_ = .false.
if (present(warn_only)) warn_only_ = warn_only
if (status /= 0) then
errmsg = DftiErrorMessage(status)
write(error_unit,'(A)') errmsg
if (.not. warn_only_) stop
end if
end subroutine
end program
I’ve found a few formulas in these documents:
- Notes on FFT-based differentiation | Steven Johnson (see Algorithm 5)
- Multidimensional DFT | Wikipedia
The following slide from a series of lecture notes from the National Taiwan University (I lost the webpage, unfortunately, so I’m not fully certain about the provenance) explains the algorithm in 1-d:
(note the storage of complex values differs between FFT libraries)
An older mirror of the MKL manual, describes the format I’m using in my example above:
The CCE format stores the values of the first half of the output complex conjugate-even signal resulted from the forward FFT. Note that the one-dimensional signal stored in CCE format is one complex element longer. For multi-dimensional real transform,
n1 * n2 * n3 * ... * nk
the size of complex matrix in CCE format is(n1/2+1)* n2 * n3 * ... * nk
for Fortran andn1 * n2 * ... * (nk/2+1)
for C.
Does someone have experience with multi-dimensional DFTs or can recommend a good resource to understand them?