问题
I am writing a pseudo-spectral CFD code in Fortran, which is essentially a time-stepper of the Navier-Stokes equations in a plane layer. It is really a 3d code in my case, but the problem can be very well understood in 2d, so I will stick to this case. Geometrically, my 2d plane layer is bounded by y=0
and y=1
, and is periodic along the other direction x
. Without going too much into the weeds, an efficient discretisation is to decompose fields (e.g., velocity) on Chebyshev polynomials along y
and Fourier modes along x
. Chebyshev polynomials are essentially cosines in disguise on a distorded grid. The Navier-Stokes equations have a simple form in spectral space, with the exception of the nonlinear term. Therefore, most of the computations are carried on in spectral space, with occasional excursions to physical space to compute the nonlinear term: one needs to transform a 2d array of complex Chebyshev-Fourier coefficients to the corresponding 2d field (i.e., array of real values on a grid). Without other constraint, this transform is relatively easy to implement. For instance, starting from complex spectral coefficients -- let us call them c_in(NX, NY/2+1)
-- one may take a complex to real, dicrete Fourier transform along x
for each value of y
to obtain a 2d array of real Chebyshev coefficients. Then, one may perform a discrete cosine transform (FFTW_REDFT
in FFTW) along y
for all x
and voila, one finally gets the real field, r_out(NX,NY)
.
The root of all troubles is that for some reasons, I need to compute the DCT first. This is a problem because cosine transforms are only implemented for real data in FFTW. Various constraints result in me not wanting to split my complex array of spectral coefficients into two real arrays for the real and imaginary parts. Given these constraint on data structure, the question is: how to efficiently get FFTW compute several DCTs along the first index of an array of complex numbers.
So far, my solution consists in using the plan_many_r2r
advanced interface to define a transform that leap-frogs over the imaginary values: I set idist
to 2. As a result, if I use this plan with a pointer ptr2real_in
associated with the real part of c_in(1,1)
, a cosine transform of all the real parts is computed. Then I repeat the execution of the plan with a pointer ptr2imag_in
associated with the imaginary part of c_in(1,1)
. After that, computing a complex to real DFT along the second dimension is easy.
So the crux of this approach is to define ptr2imag_in
, which is really the memory address of c_in(1,1)
shifted by the size in memory of a C_double
. I include a minimal example below which works, but looks clumsy to me. In particular, I define the pointer to the imaginary part of the complex array in this way
cptr = C_loc(c_in(1,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
cptr = C_loc(ptr2imag_in(2,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
It seems to me that all I need to do would be to shift cptr
by 8 bytes. How could I do that? The following code fails:
cptr = C_loc(c_in(1,1))
cptr = cptr + 8
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
The full minimal example for taking a DCT followed by a complex to real DFT is below:
Program monkeying_with_dct
Use, Intrinsic :: ISO_C_BINDING
Implicit None
include 'fftw3.f03'
Integer, Parameter :: dp = C_Double
Complex(C_double), Pointer :: c_in (:,:)
Complex(C_double), Pointer :: c_out(:,:)
Real(C_Double), Pointer :: r_out(:,:)
Real(C_Double), Pointer :: ptr2real_in (:,:)
Real(C_Double), Pointer :: ptr2real_out(:,:)
Real(C_Double), Pointer :: ptr2imag_in (:,:)
Real(C_Double), Pointer :: ptr2imag_out(:,:)
Type(C_ptr) :: cptr
Type(C_ptr) :: plan_IDCT
Type(C_ptr) :: plan_C2R
Type(C_ptr) :: pdum
Integer, Parameter :: NX = 512
Integer, Parameter :: NY = 1024
print *,'... allocating memory ...'
pdum = fftw_alloc_complex(int((NY/2+1)*NX, C_size_T))
Call C_F_Pointer(pdum, c_in , [NX, NY/2+1])
pdum = fftw_alloc_complex(int((NY/2+1)*NX, C_size_T))
Call C_F_Pointer(pdum, c_out, [NX, NY/2+1])
pdum = fftw_alloc_real(int(NY*NX, C_size_T))
Call C_F_Pointer(pdum, r_out, [NX, NY])
print *,'... initializing data ...'
c_in = Cmplx(0._dp, 0._dp, Kind=dp)
c_in(2,3) = Cmplx(1._dp, 0.5_dp, Kind=dp)
print *, '... defining a pointer to the real part of input complex data ...'
cptr = C_loc(c_in(1,1))
Call C_F_Pointer(cptr, ptr2real_in, [2*NX, (NY/2+1)])
print *, '... defining a pointer to the imag part of input complex data ...'
cptr = C_loc(c_in(1,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
cptr = C_loc(ptr2imag_in(2,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
print *, '... defining a pointer to the real part of transformed complex data ...'
cptr = C_loc(c_out(1,1))
Call C_F_Pointer(cptr, ptr2real_out, [2*NX, (NY/2+1)])
print *, '... defining a pointer to the imag part of transformed complex data ...'
cptr = C_loc(c_out(1,1))
Call C_F_Pointer(cptr, ptr2imag_out, [2*NX, (NY/2+1)])
cptr = C_loc(ptr2imag_out(2,1))
Call C_F_Pointer(cptr, ptr2imag_out, [2*NX, (NY/2+1)])
print*, '... planning IDCT ...'
plan_IDCT = fftw_plan_many_r2r(1, [NX], (NY/2+1), &
ptr2real_in, [2*NX], 2, 2*NX, &
ptr2real_out, [2*NX], 2, 2*NX, &
[FFTW_REDFT01] , FFTW_MEASURE)
print*, '... planning C2R DFT ...'
plan_C2R = fftw_plan_many_dft_c2r(1, [NY], NX, &
c_out, [NY/2+1], NX, 1, &
r_out, [NY], NX, 1, &
FFTW_MEASURE)
print*, '... DCT of the real part ...'
Call fftw_execute_r2r(plan_IDCT, ptr2real_in, ptr2real_out)
print*, '... DCT of the imaginary part ...'
Call fftw_execute_r2r(plan_IDCT, ptr2imag_in, ptr2imag_out)
print*, '... DFT Complex to real ...'
Call fftw_execute_dft_c2r(plan_C2R, c_out,r_out)
End Program monkeying_with_dct
回答1:
One can alwas transfer()
the pointer to integer, do the shift and transfer()
back, if that is what you want.
cptr = transfer( transfer(cptr, 1_c_intptr_t) + c_sizeof(1._c_double) , cptr)
or one can just call a small C function that does the pointer arithmetic in a better controlled way. But I am not sure it is really what you need.
In Fortran 2008 one should be able to just use the %im
syntax, but the compiler support isn't good yet, gfortran does not support it at all..
回答2:
You may try something in the sense of the following trial 1-dim example (I am not sure how efficient the compiled code is ...).
program mapctor
implicit none
integer, parameter :: n = 10
integer :: i
complex :: cx(n) ! the couplex array
real :: rx(2,n) ! mapped to real
EQUIVALENCE(cx(1), rx(1,1)) ! some old fortran stuff !
! fill in some test values
do i=1,n
cx(i) = cmplx(i,-i)
enddo
write(6,*)"REAL PART:"
call xfft(rx(1,:),n)
write(6,*)"IMAG PART:"
call xfft(rx(2,:),n)
end program mapctor
subroutine xfft(x,n) ! mock-up fft routine, or whatever
implicit none
real, intent(in) :: x(n)
integer, intent(in) :: n
write(6,'(f12.6)') x
end subroutine xfft
来源:https://stackoverflow.com/questions/50280739/dct-of-complex-arrays-with-fftw-in-fortran-how-to-point-to-the-imaginary-part-a