Pointers Fortran中FFTW复数数组的DCT:如何指向虚部数组?

Pointers Fortran中FFTW复数数组的DCT:如何指向虚部数组?,pointers,fortran,fftw,fortran-iso-c-binding,Pointers,Fortran,Fftw,Fortran Iso C Binding,我正在用Fortran编写一个伪谱CFD代码,它本质上是平面层中Navier-Stokes方程的时间步进器。在我的例子中,这确实是一个3d代码,但是这个问题在2d中可以很好地理解,所以我将坚持这个例子。从几何角度看,我的二维平面层以y=0和y=1为边界,沿另一个方向x呈周期性。在不深入杂草的情况下,一种有效的离散化方法是沿y分解切比雪夫多项式上的场(例如速度),沿x分解傅里叶模式。切比雪夫多项式本质上是离散网格上伪装的余弦。除非线性项外,Navier-Stokes方程在谱空间中的形式很简单。因此

我正在用Fortran编写一个伪谱CFD代码,它本质上是平面层中Navier-Stokes方程的时间步进器。在我的例子中,这确实是一个3d代码,但是这个问题在2d中可以很好地理解,所以我将坚持这个例子。从几何角度看,我的二维平面层以
y=0
y=1
为边界,沿另一个方向
x
呈周期性。在不深入杂草的情况下,一种有效的离散化方法是沿
y
分解切比雪夫多项式上的场(例如速度),沿
x
分解傅里叶模式。切比雪夫多项式本质上是离散网格上伪装的余弦。除非线性项外,Navier-Stokes方程在谱空间中的形式很简单。因此,大多数计算是在光谱空间中进行的,偶尔会偏移到物理空间来计算非线性项:需要将复切比雪夫-傅里叶系数的二维数组转换为相应的二维场(即网格上的实值数组)。如果没有其他约束,此转换相对容易实现。例如,从复光谱系数开始——让我们称它们为(NX,NY/2+1)——我们可以对
y
的每个值沿
x
进行复到实的离散傅里叶变换,以获得实切比雪夫系数的二维数组。然后,我们可以沿着
y
对所有
x
执行离散余弦变换(
FFTW\u REDFT
,在FFTW中),然后瞧,我们最终得到实场,
r\u out(NX,NY)

所有问题的根源在于,出于某些原因,我需要首先计算DCT。这是一个问题,因为余弦变换仅对FFTW中的真实数据实现。各种各样的限制导致我不想把复杂的光谱系数数组分成实数部分和虚部的两个实数数组。考虑到数据结构上的这些约束,问题是:如何高效地让FFTW沿着复数数组的第一个索引计算多个DCT

到目前为止,我的解决方案是使用
plan\u many\r2r
高级接口定义一个跳过虚拟值的转换:我将
idist
设置为2。因此,如果我使用这个计划,在中有一个与(1,1)中的c_的实部相关联的指针ptr2real_,则计算所有实部的余弦变换。然后,我使用与(1,1)中的
c\u的虚部相关联的指针
ptr2imag\u,重复执行计划。之后,沿着第二维计算复到实DFT就很容易了

因此,这种方法的关键是定义
ptr2imag\u in
,它实际上是
c\u in(1,1)
的内存地址,被
c\u double
的内存大小移位。下面我举了一个很小的例子,这个例子很有效,但在我看来很笨拙。特别是,我用这种方式定义了指向复数数组虚部的指针

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)])
在我看来,我所需要做的就是将
cptr
移位8个字节。我怎么能这么做?以下代码失败:

cptr = C_loc(c_in(1,1))
cptr = cptr + 8 
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])

下面是采用DCT和复到实DFT的完整最小示例:

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 

如果你想要的话,你可以把指针移到整数上,然后把指针移回去

cptr = transfer( transfer(cptr, 1_c_intptr_t) + c_sizeof(1._c_double) , cptr)
或者可以调用一个小的C函数,以更好的控制方式执行指针运算。但我不确定这是否真的是你所需要的


在Fortran 2008中,应该可以只使用
%im
语法,但编译器的支持还不好,gfortran根本不支持它。

可以使用
transfer()
指向整数的指针,执行移位和
transfer()
返回,如果这是您想要的

cptr = transfer( transfer(cptr, 1_c_intptr_t) + c_sizeof(1._c_double) , cptr)
或者可以调用一个小的C函数,以更好的控制方式执行指针运算。但我不确定这是否真的是你所需要的


在Fortran 2008中,应该可以只使用
%im
语法,但编译器支持还不好,gfortran根本不支持它。

您可以尝试以下试用1-dim示例的意义上的东西 (我不确定编译后的代码有多高效…)


您可以尝试以下试用1-dim示例的含义 (我不确定编译后的代码有多高效…)


也许你应该先解释一下什么是DCT,好的。希望它现在更清楚。也许你应该解释什么是DCT是第一个。好的观点。希望现在更清楚。谢谢,听起来第二段代码成功编译所缺少的是
transfer()
。当我回到我的机器上时,我会试试它。从哪个意义上说,这不是一种“可控的方式”做事?像我在第一段代码中所做的那样乱搞指针是否更好?您的解决方案看起来更加优雅。在C语言中,您使用了正确的无符号算术,并添加了数组元素的数量(以sizeof为单位),而不是字节数。工作起来很有魅力。谢谢,听起来好像
transfer()
是成功编译第二段代码所缺少的。当我回到我的机器上时,我会试试它。从哪个意义上说,这不是一种“可控的方式”做事?像我在第一段代码中所做的那样乱搞指针是否更好?您的解决方案看起来更优雅。在C中,您使用了正确的无符号算术,并添加了数组元素数(以sizeof为单位),而不是字节数。工作起来很有魅力。上面的数组是由C分配的,使用起来有点棘手(需要多一个子例程).上面的数组是由C共同分配的,使用起来有点棘手(需要多一个子例程)。