|
/* FILE: fft.cc -*-Mode: c++-*-
|
|
*
|
|
* C++ code to do 1 and 2 dimensional FFT's.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "nb.h"
|
|
#include "fft.h"
|
|
|
|
#ifdef USE_MPI
|
|
#include "mmsmpi.h"
|
|
#endif /* USE_MPI */
|
|
|
|
/* End includes */
|
|
|
|
#undef USE_COMPLEX
|
|
|
|
#ifndef OLD_CODE
|
|
#define CMULT(xr,xi,yr,yi,zr,zi) (zr) = (xr)*(yr)-(xi)*(yi), \
|
|
(zi) = (xr)*(yi)+(xi)*(yr)
|
|
#else
|
|
extern inline void CMULT(const MY_COMPLEX_REAL_TYPE &xr,
|
|
const MY_COMPLEX_REAL_TYPE &xi,
|
|
const MY_COMPLEX_REAL_TYPE &yr,
|
|
const MY_COMPLEX_REAL_TYPE &yi,
|
|
MY_COMPLEX_REAL_TYPE &zr,
|
|
MY_COMPLEX_REAL_TYPE &zi)
|
|
{
|
|
zr = xr*yr-xi*yi;
|
|
zi = xr*yi+xi*yr;
|
|
}
|
|
#endif // OLD_CODE
|
|
|
|
void FFT::ReleaseMemory(void)
|
|
{
|
|
if(vecsize>0) {
|
|
if(Uforward!=(MyComplex *)NULL) delete[] Uforward;
|
|
if(Uinverse!=(MyComplex *)NULL) delete[] Uinverse;
|
|
if(permindex!=(int *)NULL) delete[] permindex;
|
|
}
|
|
vecsize=0;
|
|
Uforward=Uinverse=(MyComplex *)NULL;
|
|
permindex=(int *)NULL;
|
|
}
|
|
|
|
FFT::~FFT(void)
|
|
{
|
|
ReleaseMemory();
|
|
}
|
|
|
|
void FFT::Setup(int size)
|
|
{
|
|
if(size==vecsize) return;
|
|
if(size<1) PlainError(1,"Error in FFT::Setup(int): "
|
|
"Requested length (%d) must be >0",size);
|
|
int k;
|
|
// Check that size is power of 2
|
|
for(k=size;k>2;k/=2)
|
|
if(k%2!=0) PlainError(1,"Error in FFT::Setup(int): "
|
|
"Requested length (%d) is not a power of 2",size);
|
|
ReleaseMemory();
|
|
vecsize=size;
|
|
|
|
// Allocate and setup MyComplex arrays
|
|
if((Uforward=new MyComplex[size])==0)
|
|
PlainError(1,"Error in FFT::Setup %s",ErrNoMem);
|
|
if((Uinverse=new MyComplex[size])==0)
|
|
PlainError(1,"Error in FFT::Setup %s",ErrNoMem);
|
|
#ifdef ORIG_CODE
|
|
double baseang= -2*PI/double(size);
|
|
Uforward[0]=Uinverse[0]=MyComplex(1,0);
|
|
double x,y;
|
|
for(k=1;k<size/2;k++) {
|
|
x = cos(baseang*k);
|
|
y = -sqrt(1-x*x);
|
|
y += (1-(x*x+y*y))/(2*y); // Tiny error correction
|
|
x += (1-(x*x+y*y))/(2*x);
|
|
Uforward[k]=Uinverse[size-k]=MyComplex(x,y);
|
|
Uforward[size-k]=Uinverse[k]=MyComplex(x,-y);
|
|
}
|
|
if(size>1) {
|
|
Uforward[size/2]=Uinverse[size/2]=MyComplex(-1,0);
|
|
}
|
|
#else
|
|
double baseang = -2*PI/double(size);
|
|
for(k=1;k<size/8;k++) {
|
|
double angle=k*baseang;
|
|
double y=sin(angle);
|
|
double x=cos(angle);
|
|
|
|
|
|
Uforward[k]=Uinverse[size-k]=MyComplex(x,y);
|
|
Uforward[size/2-k]=Uinverse[size/2+k]=MyComplex(-x,y);
|
|
Uforward[size/2+k]=Uinverse[size/2-k]=MyComplex(-x,-y);
|
|
Uforward[size-k]=Uinverse[k]=MyComplex(x,-y);
|
|
|
|
Uforward[size/4-k]=Uinverse[3*size/4+k]=MyComplex(-y,-x);
|
|
Uforward[size/4+k]=Uinverse[3*size/4-k]=MyComplex(y,-x);
|
|
Uforward[3*size/4-k]=Uinverse[size/4+k]=MyComplex(y,x);
|
|
Uforward[3*size/4+k]=Uinverse[size/4-k]=MyComplex(-y,x);
|
|
}
|
|
Uforward[0]=Uinverse[0]=MyComplex(1,0);
|
|
if(size>1) {
|
|
Uforward[size/2]=Uinverse[size/2]=MyComplex(-1,0);
|
|
}
|
|
if(size>3) {
|
|
Uforward[size/4]=Uinverse[3*size/4]=MyComplex(0,-1);
|
|
Uforward[3*size/4]=Uinverse[size/4]=MyComplex(0,1);
|
|
}
|
|
if(size>7) {
|
|
double x=SQRT1_2; // 1/sqrt(2)
|
|
double y=-x;
|
|
Uforward[size/8]=Uinverse[7*size/8]=MyComplex(x,y);
|
|
Uforward[3*size/8]=Uinverse[5*size/8]=MyComplex(-x,y);
|
|
Uforward[5*size/8]=Uinverse[3*size/8]=MyComplex(-x,-y);
|
|
Uforward[7*size/8]=Uinverse[size/8]=MyComplex(x,-y);
|
|
}
|
|
#endif // ORIG_CODE
|
|
|
|
// Allocate and setup (bit-reversal) permutation index
|
|
if((permindex=new int[size])==0)
|
|
PlainError(1,"Error in FFT::Setup %s",ErrNoMem);
|
|
permindex[0]=0;
|
|
int m,n; // The following code relies heavily on size==2^log2vecsize
|
|
for(k=1,n=size>>1;k<size;k++) {
|
|
// At each step, n is bit-reversed pattern of k
|
|
if(n>k) permindex[k]=n; // Swap index
|
|
else permindex[k]=0; // Do nothing: Index already swapped or the same
|
|
// Calculate next n
|
|
m=size>>1;
|
|
while(m>0 && n&m) { n-=m; m>>=1; }
|
|
n+=m;
|
|
}
|
|
}
|
|
|
|
void FFT::ForwardDecFreq(int size,MyComplex *vec,FFT_REAL_TYPE divisor)
|
|
{
|
|
if(divisor==0) divisor=1.0; // Default is no normalization on forward FFT
|
|
Setup(size);
|
|
BaseDecFreqForward(vec);
|
|
Permute(vec);
|
|
if(divisor!=0. && divisor!=1.) {
|
|
MY_COMPLEX_REAL_TYPE mult=1./divisor;
|
|
for(int k=0;k<size;k++) vec[k]*=mult;
|
|
}
|
|
}
|
|
|
|
void FFT::InverseDecTime(int size,MyComplex *vec,FFT_REAL_TYPE divisor)
|
|
{
|
|
if(divisor==0) divisor=(FFT_REAL_TYPE)size;
|
|
/// Default divisor on iFFT is 'size'
|
|
Setup(size);
|
|
Permute(vec);
|
|
BaseDecTimeInverse(vec);
|
|
if(divisor!=0. && divisor!=1.) {
|
|
MY_COMPLEX_REAL_TYPE mult=1./divisor;
|
|
for(int k=0;k<size;k++) vec[k]*=mult;
|
|
}
|
|
|
|
}
|
|
|
|
inline void Swap(MyComplex &a,MyComplex &b)
|
|
{ MyComplex c(a); a=b; b=c; }
|
|
|
|
void FFT::Permute(MyComplex *vec)
|
|
{ /* Bit reversal permutation */
|
|
int i,j;
|
|
for(i=0;i<vecsize;i++) {
|
|
if((j=permindex[i])!=0) Swap(vec[i],vec[j]);
|
|
}
|
|
}
|
|
|
|
void FFT::BaseDecFreqForward(MyComplex *vec)
|
|
{ // In-place forward FFT using Decimation in Frequency technique,
|
|
// *WITHOUT* resuffling of indices.
|
|
// NOTE 1: This code does not use the Complex class, because
|
|
// some compilers do not effectively optimize around
|
|
// Complex operations such as multiplication. So this
|
|
// routine just makes use of ordinary type "FFT_REAL_TYPE"
|
|
// variables, and assumes each Complex variable is
|
|
// actually two consecutive "MY_COMPLEX_REAL_TYPE" variables.
|
|
// NOTE 2: See notes in MJD's micromagnetics notebook, 11-Sep-96
|
|
// (p. 62) and 29-Sep-96 (p. 69).
|
|
// NOTE 3: This code has been optimized for performance on cascade.cam,
|
|
// a PentiumPro 200 MHz machine using a stock gcc 2.7.2
|
|
// compiler. In particular, the x86 chips suffer from a
|
|
// shortage of registers.
|
|
// NOTE 4: Some compromise made to RISC architectures on 27-May-1997,
|
|
// by moving all loads before any stores in main loop. As
|
|
// done, it hurts performance on PentiumPro-200 only a couple
|
|
// of percent. (mjd)
|
|
|
|
#define datat float /*Added by Guru on 11/10/2010: created data type datat; specified datat as required*/
|
|
typedef datat FFT_REAL_TYPE; /*Added by Guru on 11/10/2010: converted FFT_REAL_TYPE to datat from double*/
|
|
|
|
|
|
if(vecsize==1) return; // Nothing to do
|
|
|
|
MY_COMPLEX_REAL_TYPE *v;
|
|
|
|
MY_COMPLEX_REAL_TYPE const *const U=(MY_COMPLEX_REAL_TYPE *)Uforward;
|
|
MY_COMPLEX_REAL_TYPE *const dvec=(MY_COMPLEX_REAL_TYPE *)vec;
|
|
|
|
int block,blocksize,blockcount,offset,uoff1;
|
|
int halfbs,threehalfbs; // Half blocksize,3/2 blocksize
|
|
FFT_REAL_TYPE m1x,m1y,m2x,m2y,m3x,m3y;
|
|
FFT_REAL_TYPE x0,y0,x1,y1,x2,y2,x3,y3;
|
|
FFT_REAL_TYPE xs02,ys02,xd02,yd02,xs13,ys13,xd13,yd13;
|
|
FFT_REAL_TYPE t1x,t1y,t2x,t2y,t3x,t3y;
|
|
|
|
|
|
// Blocksize>4
|
|
for(blocksize=vecsize,blockcount=1;blocksize>4;
|
|
blocksize/=4,blockcount*=4) {
|
|
// Loop through double-step matrix multiplications
|
|
halfbs=blocksize/2; threehalfbs=blocksize+halfbs;
|
|
for(block=0,v=dvec;block<blockcount;block++,v+=2*blocksize) {
|
|
for(offset=0;offset<halfbs;offset+=2) {
|
|
uoff1=offset*blockcount;
|
|
m1x=U[uoff1]; m1y=U[uoff1+1];
|
|
m2x=U[2*uoff1]; m2y=U[2*uoff1+1];
|
|
m3x=U[3*uoff1]; m3y=U[3*uoff1+1];
|
|
|
|
x0=v[offset];
|
|
y0=v[offset+1];
|
|
x1=v[halfbs+offset];
|
|
y1=v[halfbs+offset+1];
|
|
x2=v[blocksize+offset];
|
|
y2=v[blocksize+offset+1];
|
|
x3=v[threehalfbs+offset];
|
|
y3=v[threehalfbs+offset+1];
|
|
|
|
xs02=x0+x2; xs13=x1+x3;
|
|
v[offset]=xs02+xs13;
|
|
t1x=xs02-xs13;
|
|
|
|
ys02=y0+y2; ys13=y1+y3;
|
|
v[offset+1]=ys02+ys13;
|
|
t1y=ys02-ys13;
|
|
|
|
v[halfbs+offset] =t1x*m2x-t1y*m2y;
|
|
v[halfbs+offset+1] =t1y*m2x+t1x*m2y;
|
|
|
|
xd02=x0-x2; yd13=y1-y3;
|
|
t3x=xd02-yd13; t2x=xd02+yd13;
|
|
yd02=y0-y2; xd13=x1-x3;
|
|
t3y=yd02+xd13; t2y=yd02-xd13;
|
|
v[blocksize+offset] =t2x*m1x-t2y*m1y;
|
|
v[blocksize+offset+1]=t2y*m1x+t2x*m1y;
|
|
v[threehalfbs+offset] =t3x*m3x-t3y*m3y;
|
|
v[threehalfbs+offset+1]=t3y*m3x+t3x*m3y;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do smallest blocks; size is either 4 or 2
|
|
if(blocksize==4) {
|
|
blockcount=vecsize/4;
|
|
for(block=0,v=dvec;block<blockcount;block++,v+=8) {
|
|
x0=v[0]; y0=v[1]; x1=v[2]; y1=v[3];
|
|
x2=v[4]; y2=v[5]; x3=v[6]; y3=v[7];
|
|
|
|
xs02=x0+x2;
|
|
xs13=x1+x3;
|
|
v[0]=xs02+xs13;
|
|
v[2]=xs02-xs13;
|
|
|
|
ys02=y0+y2;
|
|
ys13=y1+y3;
|
|
v[1]=ys02+ys13;
|
|
v[3]=ys02-ys13;
|
|
|
|
xd02=x0-x2;
|
|
yd13=y1-y3;
|
|
v[4]=xd02+yd13;
|
|
v[6]=xd02-yd13;
|
|
|
|
yd02=y0-y2;
|
|
xd13=x1-x3;
|
|
v[5]=yd02-xd13;
|
|
v[7]=yd02+xd13;
|
|
}
|
|
}
|
|
else { // blocksize==2
|
|
blockcount=vecsize/2;
|
|
for(block=0,v=dvec;block<blockcount;block++,v+=4) {
|
|
x0=v[0]; y0=v[1];
|
|
x1=v[2]; y1=v[3];
|
|
v[0]=x0+x1; v[2]=x0-x1;
|
|
v[1]=y0+y1; v[3]=y0-y1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FFT::BaseDecTimeInverse(MyComplex *vec)
|
|
{ // In-place inverse FFT using Decimation in Time technique,
|
|
// *WITHOUT* resuffling of indices.
|
|
// NOTE 1: This code does not use the Complex class, because
|
|
// some compilers do not effectively optimize around
|
|
// Complex operations such as multiplication. So this
|
|
// routine just makes use of ordinary type "FFT_REAL_TYPE"
|
|
// variables, and assumes each Complex variable is
|
|
// actually two consecutive "MY_COMPLEX_REAL_TYPE" variables.
|
|
// NOTE 2: See notes in MJD's micromagnetics notebook, 11-Sep-96
|
|
// (p. 62) and 29-Sep-96 (p. 69).
|
|
// NOTE 3: This code has been optimized for performance on cascade.cam,
|
|
// a PentiumPro 200 MHz machine using a stock gcc 2.7.2
|
|
// compiler. In particular, the x86 chips suffer from a
|
|
// shortage of registers.
|
|
// NOTE 4: Some compromise made to RISC architectures on 27-May-1997,
|
|
// by moving all loads before any stores in main loop. As
|
|
// done, it hurts performance on PentiumPro-200 only a couple
|
|
// of percent. (mjd)
|
|
|
|
#define datat float /*Added by Guru on 11/10/2010: created data type datat; specified datat as required*/
|
|
typedef datat FFT_REAL_TYPE; /*Added by Guru on 11/10/2010: converted FFT_REAL_TYPE to datat from double*/
|
|
|
|
(vecsize==1) return; // Nothing to do
|
|
|
|
MY_COMPLEX_REAL_TYPE *v;
|
|
|
|
MY_COMPLEX_REAL_TYPE const *const U=(MY_COMPLEX_REAL_TYPE *)Uinverse;
|
|
MY_COMPLEX_REAL_TYPE *const dvec=(MY_COMPLEX_REAL_TYPE *)vec;
|
|
|
|
int block,blocksize,blockcount,offset,uoff1;
|
|
int halfbs,threehalfbs; // Half blocksize,3/2 blocksize
|
|
FFT_REAL_TYPE m1x,m1y,m2x,m2y,m3x,m3y;
|
|
FFT_REAL_TYPE x0,y0,x1,y1,x2,y2,x3,y3;
|
|
FFT_REAL_TYPE xs01,ys01,xd01,yd01,xs23,ys23,xd23,yd23;
|
|
FFT_REAL_TYPE t1x,t1y,t2x,t2y,t3x,t3y;
|
|
|
|
// Do smallest blocks; size is either 4 or 2
|
|
if(vecsize>2) {
|
|
blockcount=vecsize/4;
|
|
for(block=0,v=dvec;block<blockcount;block++,v+=8) {
|
|
x0=v[0]; y0=v[1];
|
|
x1=v[2]; y1=v[3];
|
|
x2=v[4]; y2=v[5];
|
|
x3=v[6]; y3=v[7];
|
|
xs01=x0+x1; xs23=x2+x3; // See NOTE 3 above
|
|
v[0]=xs01+xs23; v[4]=xs01-xs23;
|
|
ys01=y0+y1; ys23=y2+y3;
|
|
v[1]=ys01+ys23; v[5]=ys01-ys23;
|
|
xd01=x0-x1; yd23=y2-y3;
|
|
v[2]=xd01-yd23; v[6]=xd01+yd23;
|
|
yd01=y0-y1; xd23=x2-x3;
|
|
v[3]=yd01+xd23; v[7]=yd01-xd23;
|
|
}
|
|
}
|
|
else { // vecsize==2
|
|
x0=dvec[0]; y0=dvec[1];
|
|
x1=dvec[2]; y1=dvec[3];
|
|
dvec[0]=x0+x1;
|
|
dvec[2]=x0-x1;
|
|
dvec[1]=y0+y1;
|
|
dvec[3]=y0-y1;
|
|
return;
|
|
}
|
|
|
|
// Blocksize>4
|
|
for(blocksize=16,blockcount=vecsize/16;blocksize<=vecsize;
|
|
blocksize*=4,blockcount/=4) {
|
|
// Loop through double-step matric multiplications
|
|
halfbs=blocksize/2; threehalfbs=blocksize+halfbs;
|
|
for(block=0,v=dvec;block<blockcount;block++,v+=2*blocksize) {
|
|
for(offset=0;offset<blocksize/2;offset+=2) {
|
|
x0=v[offset];
|
|
y0=v[offset+1];
|
|
t2x=v[blocksize+offset];
|
|
t2y=v[blocksize+offset+1];
|
|
uoff1=offset*blockcount;
|
|
m1x=U[uoff1]; m1y=U[uoff1+1];
|
|
x2=t2x*m1x-t2y*m1y;
|
|
y2=t2y*m1x+t2x*m1y;
|
|
|
|
m2x=U[2*uoff1]; m2y=U[2*uoff1+1];
|
|
t1x=v[halfbs+offset];
|
|
t1y=v[halfbs+offset+1];
|
|
x1=t1x*m2x-t1y*m2y;
|
|
y1=t1y*m2x+t1x*m2y;
|
|
|
|
t3x=v[threehalfbs+offset];
|
|
t3y=v[threehalfbs+offset+1];
|
|
m3x=U[3*uoff1]; m3y=U[3*uoff1+1];
|
|
x3=t3x*m3x-t3y*m3y;
|
|
y3=t3y*m3x+t3x*m3y;
|
|
|
|
xs01=x0+x1; xs23=x2+x3;
|
|
v[ offset ] = xs01+xs23;
|
|
v[ blocksize+offset ] = xs01-xs23;
|
|
|
|
ys01=y0+y1; ys23=y2+y3;
|
|
v[ offset+1] = ys01+ys23;
|
|
v[ blocksize+offset+1] = ys01-ys23;
|
|
|
|
yd01=y0-y1; xd23=x2-x3;
|
|
v[ halfbs +offset+1] = yd01+xd23;
|
|
v[threehalfbs+offset+1] = yd01-xd23;
|
|
|
|
xd01=x0-x1; yd23=y2-y3;
|
|
v[ halfbs +offset ] = xd01-yd23;
|
|
v[threehalfbs+offset ] = xd01+yd23;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(blocksize==2*vecsize) {
|
|
// We still have to do one single-step matrix multiplication
|
|
blocksize=vecsize; v=dvec;
|
|
for(offset=0;offset<blocksize;offset+=2,v+=2) {
|
|
x0=v[0]; y0=v[1];
|
|
t1x=v[vecsize]; t1y=v[vecsize+1];
|
|
m1x=U[offset]; m1y=U[offset+1];
|
|
x1=t1x*m1x-t1y*m1y; y1=t1y*m1x+t1x*m1y;
|
|
v[0] = x0+x1;
|
|
v[vecsize] = x0-x1;
|
|
v[1] = y0+y1;
|
|
v[vecsize+1] = y0-y1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
const REALWIDE FFTReal2D::CRRCspeedratio=1.10;
|
|
/// Relative speed of ForwardCR as compared to ForwardRC.
|
|
/// If bigger than 1, then ForwardCR is faster. This will
|
|
/// be machine & compiler dependent...oh well.
|
|
|
|
void FFTReal2D::Setup(int size1,int size2)
|
|
{ // Note: This routine is also called by FFTReal2D::SetupInverse()
|
|
if(size1==vecsize1 && size2==vecsize2) return; // Nothing to do
|
|
|
|
// Check that sizes are powers of 2, and >= 1. Also extract
|
|
// base-2 log of sizes
|
|
if(size1<1) PlainError(1,"Error in FFTReal2D::Setup(int): "
|
|
"Requested size1 (%d) must be >=1",size1);
|
|
if(size2<1) PlainError(1,"Error in FFTReal2D::Setup(int): "
|
|
"Requested size2 (%d) must be >=1",size2);
|
|
|
|
int k;
|
|
if(size1==1) {
|
|
logsize1=0;
|
|
} else {
|
|
for(k=size1,logsize1=1;k>2;k/=2,logsize1++)
|
|
if(k%2!=0) PlainError(1,"Error in FFTReal2D::Setup(int): "
|
|
"Requested size1 (%d) is not a power of 2",size1);
|
|
}
|
|
if(size2==1) {
|
|
logsize2=0;
|
|
} else {
|
|
for(k=size2,logsize2=1;k>2;k/=2,logsize2++)
|
|
if(k%2!=0) PlainError(1,"Error in FFTReal2D::Setup(int): "
|
|
"Requested size2 (%d) is not a power of 2",size2);
|
|
}
|
|
|
|
// Allocate new space
|
|
ReleaseMemory();
|
|
scratch=new MyComplex[OC_MAX(size1,size2)];
|
|
scratchb=new MyComplex[OC_MAX(size1,size2)];
|
|
vecsize1=size1; vecsize2=size2;
|
|
}
|
|
|
|
void FFTReal2D::SetupInverse(int size1,int size2)
|
|
{
|
|
if(size1==vecsize1 && size2==vecsize2 && workarr!=NULL)
|
|
return; // Nothing to do
|
|
Setup(size1,size2);
|
|
if(workarr!=NULL) { delete[] workarr[0]; delete[] workarr; } // Safety
|
|
int rowcount=(vecsize1/2)+1;
|
|
workarr=new MyComplex*[rowcount];
|
|
workarr[0]=new MyComplex[rowcount*vecsize2];
|
|
for(int i=1;i<rowcount;i++) workarr[i]=workarr[i-1]+vecsize2;
|
|
}
|
|
|
|
void FFTReal2D::ReleaseMemory()
|
|
{
|
|
if(vecsize1==0 || vecsize2==0) return;
|
|
delete[] scratch; scratch=NULL;
|
|
delete[] scratchb; scratchb=NULL;
|
|
if(workarr!=NULL) {
|
|
delete[] workarr[0];
|
|
delete[] workarr;
|
|
workarr=NULL;
|
|
}
|
|
vecsize1=0; vecsize2=0;
|
|
fft1.ReleaseMemory(); fft2.ReleaseMemory();
|
|
}
|
|
|
|
void FFTReal2D::FillOut(int csize1,int csize2,MyComplex** carr)
|
|
{ // This routine assumes carr is a top-half-filled DFT of
|
|
// a real function, and fills in the bottom half using the
|
|
// relation
|
|
// carr[csize1-i][csize2-j]=conj(carr[i][j])
|
|
// for i>csize1/2, with the second indices interpreted 'mod csize2'.
|
|
int i,j;
|
|
for(i=1;i<csize1/2;i++) {
|
|
carr[csize1-i][0]=conj(carr[i][0]);
|
|
for(j=1;j<csize2;j++) {
|
|
carr[csize1-i][j]=conj(carr[i][csize2-j]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void FFTReal2D::ForwardCR(int rsize1,int rsize2,
|
|
const double* const* rarr,
|
|
int csize1,int csize2,MyComplex** carr)
|
|
{
|
|
Setup(OC_MAX(1,2*(csize1-1)),csize2); // Safety
|
|
if(vecsize1<2 || vecsize2<2)
|
|
PlainError(1,"Error in FFTReal2D::ForwardCR(...): "
|
|
"Full array dimensions (%dx%d) must be both >=2",
|
|
vecsize1,vecsize2);
|
|
|
|
int i,j;
|
|
FFT_REAL_TYPE x1,x2,y1,y2;
|
|
FFT_REAL_TYPE xb1,xb2,yb1,yb2;
|
|
|
|
// Do FFT on columns, 2 at a time
|
|
for(j=0;j+3<rsize2;j+=4) {
|
|
// Pack into MyComplex scratch array
|
|
for(i=0;i<rsize1;i++) {
|
|
scratch[i]=MyComplex(rarr[i][j],rarr[i][j+1]);
|
|
scratchb[i]=MyComplex(rarr[i][j+2],rarr[i][j+3]);
|
|
}
|
|
// Zero pad scratch space
|
|
for(i=rsize1;i<vecsize1;i++) scratch[i]=MyComplex(0.,0.);
|
|
for(i=rsize1;i<vecsize1;i++) scratchb[i]=MyComplex(0.,0.);
|
|
// Do complex FFT
|
|
fft1.ForwardDecFreq(vecsize1,scratchb);
|
|
fft1.ForwardDecFreq(vecsize1,scratch);
|
|
// Unpack into top half of 2D complex array
|
|
// Rows 0 & vecsize1/2 are real-valued, so pack them together
|
|
// into row 0 (row 0 as real part, row vecsize1/2 as imag. part).
|
|
carr[0][j] =MyComplex(scratch[0].real(),scratch[vecsize1/2].real());
|
|
carr[0][j+1]=MyComplex(scratch[0].imag(),scratch[vecsize1/2].imag());
|
|
carr[0][j+2]=MyComplex(scratchb[0].real(),scratchb[vecsize1/2].real());
|
|
carr[0][j+3]=MyComplex(scratchb[0].imag(),scratchb[vecsize1/2].imag());
|
|
for(i=1;i<vecsize1/2;i++) { // ASSUMES vecsize1 is even!
|
|
x1=scratch[i].real()/2; y1=scratch[i].imag()/2;
|
|
x2=scratch[vecsize1-i].real()/2; y2=scratch[vecsize1-i].imag()/2;
|
|
xb1=scratchb[i].real()/2; yb1=scratchb[i].imag()/2;
|
|
xb2=scratchb[vecsize1-i].real()/2; yb2=scratchb[vecsize1-i].imag()/2;
|
|
carr[i][j] =MyComplex(x1+x2,y1-y2);
|
|
carr[i][j+1] =MyComplex(y1+y2,x2-x1);
|
|
carr[i][j+2] =MyComplex(xb1+xb2,yb1-yb2);
|
|
carr[i][j+3] =MyComplex(yb1+yb2,xb2-xb1);
|
|
}
|
|
}
|
|
// Case rsize2 not divisible by 4
|
|
for(;j<rsize2;j+=2) {
|
|
// Pack into complex scratch array
|
|
if(j+1<rsize2) {
|
|
for(i=0;i<rsize1;i++) scratch[i]=MyComplex(rarr[i][j],rarr[i][j+1]);
|
|
}
|
|
else { // rsize2 == 1 mod 2.
|
|
for(i=0;i<rsize1;i++) scratch[i]=MyComplex(rarr[i][j],0.);
|
|
}
|
|
for(i=rsize1;i<vecsize1;i++) scratch[i]=MyComplex(0.,0.);
|
|
fft1.ForwardDecFreq(vecsize1,scratch);
|
|
carr[0][j] =MyComplex(scratch[0].real(),scratch[vecsize1/2].real());
|
|
carr[0][j+1]=MyComplex(scratch[0].imag(),scratch[vecsize1/2].imag());
|
|
for(i=1;i<vecsize1/2;i++) { // ASSUMES vecsize1 is even!
|
|
x1=scratch[i].real()/2; y1=scratch[i].imag()/2;
|
|
x2=scratch[vecsize1-i].real()/2; y2=scratch[vecsize1-i].imag()/2;
|
|
carr[i][j] =MyComplex(x1+x2,y1-y2);
|
|
carr[i][j+1] =MyComplex(y1+y2,x2-x1);
|
|
}
|
|
}
|
|
// Zero-pad remaining columns
|
|
if(rsize2<csize2) {
|
|
for(i=0;i<csize1;i++) for(j=rsize2;j<csize2;j++)
|
|
carr[i][j]=MyComplex(0.,0.);
|
|
// Note: One _may_ be able to gain a few percent speedup
|
|
// by using the 'memcpy' C-library routine.
|
|
}
|
|
|
|
// Do FFT on top half of rows
|
|
for(i=0;i<csize1-1;i++) fft2.ForwardDecFreq(csize2,carr[i]);
|
|
|
|
// Pull out row 0 & row csize1-1 from (packed) row 0
|
|
carr[csize1-1][0] = MyComplex(carr[0][0].imag(),0.);
|
|
carr[0][0] = MyComplex(carr[0][0].real(),0.);
|
|
for(j=1;j<csize2/2;j++) {
|
|
x1=carr[0][j].real()/2; y1=carr[0][j].imag()/2;
|
|
x2=carr[0][csize2-j].real()/2; y2=carr[0][csize2-j].imag()/2;
|
|
MyComplex temp1(x1+x2,y1-y2);
|
|
MyComplex temp2(y1+y2,x2-x1);
|
|
carr[0][j] = temp1;
|
|
carr[0][csize2-j] = conj(temp1);
|
|
carr[csize1-1][j] = temp2;
|
|
carr[csize1-1][csize2-j] = conj(temp2);
|
|
}
|
|
carr[csize1-1][csize2/2] = MyComplex(carr[0][csize2/2].imag(),0.);
|
|
carr[0][csize2/2] = MyComplex(carr[0][csize2/2].real(),0.);
|
|
}
|
|
|
|
void FFTReal2D::ForwardRC(int rsize1,int rsize2,
|
|
const double* const* rarr,
|
|
int csize1,int csize2,MyComplex** carr)
|
|
{
|
|
Setup(OC_MAX(1,2*(csize1-1)),csize2); // Safety
|
|
if(vecsize1<2 || vecsize2<2)
|
|
PlainError(1,"Error in FFTReal2D::ForwardRC(...): "
|
|
"Full array dimensions (%dx%d) must be both >=2",
|
|
vecsize1,vecsize2);
|
|
|
|
int i,j;
|
|
FFT_REAL_TYPE x1,x2,y1,y2;
|
|
FFT_REAL_TYPE xb1,xb2,yb1,yb2;
|
|
|
|
// Do row FFT's
|
|
for(i=0;i+1<rsize1;i+=2) {
|
|
// Pack 'MyComplex' row
|
|
for(j=0;j<rsize2;j++)
|
|
carr[i/2][j]=MyComplex(rarr[i][j],rarr[i+1][j]);
|
|
for(j=rsize2;j<csize2;j++) carr[i/2][j]=MyComplex(0.,0.); // Zero pad
|
|
// Do FFT
|
|
fft2.ForwardDecFreq(csize2,carr[i/2]);
|
|
}
|
|
for(;i<rsize1;i+=2) { // In case rsize1 == 1 mod 2
|
|
for(j=0;j<rsize2;j++)
|
|
carr[i/2][j]=MyComplex(rarr[i][j],0.);
|
|
for(j=rsize2;j<csize2;j++) carr[i/2][j]=MyComplex(0.,0.); // Zero pad
|
|
// Do FFT
|
|
fft2.ForwardDecFreq(csize2,carr[i/2]);
|
|
}
|
|
// Any remaining rows are zero padding on the fly during
|
|
// the column FFT's (see below).
|
|
|
|
// Do column FFT's
|
|
// Do column 0 and csize2/2, making use of the fact that
|
|
// these 2 columns are 'real'.
|
|
for(i=0;i<(rsize1+1)/2;i++) {
|
|
x1=carr[i][0].real(); x2=carr[i][0].imag();
|
|
y1=carr[i][csize2/2].real(); y2=carr[i][csize2/2].imag();
|
|
scratch[2*i] = MyComplex(x1,y1);
|
|
scratch[(2*i)+1] = MyComplex(x2,y2);
|
|
}
|
|
for(i*=2;i<vecsize1;i++) scratch[i]=MyComplex(0.,0.); // Zero pad
|
|
fft1.ForwardDecFreq(vecsize1,scratch);
|
|
carr[0][0] = MyComplex(scratch[0].real(),0.);
|
|
carr[0][csize2/2] = MyComplex(scratch[0].imag(),0.);
|
|
for(i=1;i<csize1-1;i++) {
|
|
x1=scratch[i].real()/2; y1=scratch[i].imag()/2;
|
|
x2=scratch[vecsize1-i].real()/2; y2=scratch[vecsize1-i].imag()/2;
|
|
carr[i][0] = MyComplex(x1+x2,y1-y2);
|
|
carr[i][csize2/2] = MyComplex(y1+y2,x2-x1);
|
|
}
|
|
carr[csize1-1][0] = MyComplex(scratch[csize1-1].real(),0.);
|
|
carr[csize1-1][csize2/2] = MyComplex(scratch[csize1-1].imag(),0.);
|
|
|
|
// Do remaining columns
|
|
for(j=1;j+1<csize2/2;j+=2) {
|
|
for(i=0;i<(rsize1+1)/2;i++) {
|
|
x1 =carr[i][j].real()/2; y1 =carr[i][j].imag()/2;
|
|
xb1=carr[i][j+1].real()/2; yb1=carr[i][j+1].imag()/2;
|
|
xb2=carr[i][csize2-1-j].real()/2; yb2=carr[i][csize2-1-j].imag()/2;
|
|
x2 =carr[i][csize2-j].real()/2; y2 =carr[i][csize2-j].imag()/2;
|
|
scratch[2*i] = MyComplex(x1+x2,y1-y2);
|
|
scratch[(2*i)+1] = MyComplex(y1+y2,x2-x1);
|
|
scratchb[2*i] = MyComplex(xb1+xb2,yb1-yb2);
|
|
scratchb[(2*i)+1] = MyComplex(yb1+yb2,xb2-xb1);
|
|
}
|
|
for(i*=2;i<vecsize1;i++)
|
|
scratch[i]= scratchb[i]=MyComplex(0.,0.); // Zero pad
|
|
fft1.ForwardDecFreq(vecsize1,scratchb);
|
|
fft1.ForwardDecFreq(vecsize1,scratch);
|
|
carr[0][j]=scratch[0]; carr[0][csize2-j]=conj(scratch[0]);
|
|
carr[0][j+1]=scratchb[0]; carr[0][csize2-1-j]=conj(scratchb[0]);
|
|
for(i=1;i<csize1;i++) {
|
|
carr[i][j]=scratch[i];
|
|
carr[i][j+1]=scratchb[i];
|
|
carr[i][csize2-1-j]=conj(scratchb[vecsize1-i]);
|
|
carr[i][csize2-j]=conj(scratch[vecsize1-i]);
|
|
}
|
|
}
|
|
// There should be 1 column left over
|
|
if(j<csize2/2) {
|
|
for(i=0;i<(rsize1+1)/2;i++) {
|
|
x1=carr[i][j].real()/2; y1=carr[i][j].imag()/2;
|
|
x2=carr[i][csize2-j].real()/2; y2=carr[i][csize2-j].imag()/2;
|
|
scratch[2*i] = MyComplex(x1+x2,y1-y2);
|
|
scratch[(2*i)+1] = MyComplex(y1+y2,x2-x1);
|
|
}
|
|
for(i*=2;i<vecsize1;i++) scratch[i]=MyComplex(0.,0.); // Zero pad
|
|
fft1.ForwardDecFreq(vecsize1,scratch);
|
|
carr[0][j]=scratch[0]; carr[0][csize2-j]=conj(scratch[0]);
|
|
for(i=1;i<csize1;i++) {
|
|
carr[i][j]=scratch[i];
|
|
carr[i][csize2-j]=conj(scratch[vecsize1-i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FFTReal2D::InverseRC(int csize1,int csize2,
|
|
const MyComplex* const* carr,
|
|
int rsize1,int rsize2,double** rarr)
|
|
{
|
|
SetupInverse(OC_MAX(1,2*(csize1-1)),csize2); // Safety.
|
|
if(vecsize1<2 || vecsize2<2)
|
|
PlainError(1,"Error in FFTReal2D::InverseRC(...): "
|
|
"Full array dimensions (%dx%d) must be both >=2",
|
|
vecsize1,vecsize2);
|
|
|
|
int i,j;
|
|
FFT_REAL_TYPE x1,y1,x2,y2;
|
|
FFT_REAL_TYPE xb1,yb1,xb2,yb2;
|
|
|
|
// Do row inverse FFT's
|
|
// Handle the first & csize1'th row specially. These rows are
|
|
// the DFT's of real sequences, so they each satisfy the conjugate
|
|
// symmetry condition
|
|
workarr[0][0]=MyComplex(carr[0][0].real(),carr[csize1-1][0].real());
|
|
for(j=1;j<csize2/2;j++) {
|
|
x1=carr[0][j].real(); y1=carr[0][j].imag();
|
|
x2=carr[csize1-1][j].real(); y2=carr[csize1-1][j].imag();
|
|
workarr[0][j] = MyComplex(x1-y2,x2+y1);
|
|
workarr[0][csize2-j] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
workarr[0][csize2/2]=MyComplex(carr[0][csize2/2].real(),
|
|
carr[csize1-1][csize2/2].real());
|
|
fft2.InverseDecTime(csize2,workarr[0],1.);
|
|
|
|
// iFFT the remaining rows
|
|
for(i=1;i<csize1-1;i++) {
|
|
for(j=0;j<csize2;j++) workarr[i][j]=carr[i][j];
|
|
fft2.InverseDecTime(csize2,workarr[i],1.);
|
|
}
|
|
|
|
// Now do iFFT's on columns. These are conj. symmetric, so we
|
|
// process them 2 at a time. Also, recall the 1st row of workarr
|
|
// contains the iFFT's of the 1st and csize1'th row of the given carr.
|
|
for(j=0;j+3<rsize2;j+=4) {
|
|
scratch[0]=
|
|
MyComplex(workarr[0][j].real(),workarr[0][j+1].real());
|
|
scratch[csize1-1]=
|
|
MyComplex(workarr[0][j].imag(),workarr[0][j+1].imag());
|
|
scratchb[0]=
|
|
MyComplex(workarr[0][j+2].real(),workarr[0][j+3].real());
|
|
scratchb[csize1-1]=
|
|
MyComplex(workarr[0][j+2].imag(),workarr[0][j+3].imag());
|
|
for(i=1;i<csize1-1;i++) {
|
|
x1 =workarr[i][j].real(); y1 =workarr[i][j].imag();
|
|
x2 =workarr[i][j+1].real(); y2 =workarr[i][j+1].imag();
|
|
xb1=workarr[i][j+2].real(); yb1=workarr[i][j+2].imag();
|
|
xb2=workarr[i][j+3].real(); yb2=workarr[i][j+3].imag();
|
|
scratch[i] = MyComplex(x1-y2,x2+y1);
|
|
scratch[vecsize1-i] = MyComplex(x1+y2,x2-y1);
|
|
scratchb[i] = MyComplex(xb1-yb2,xb2+yb1);
|
|
scratchb[vecsize1-i] = MyComplex(xb1+yb2,xb2-yb1);
|
|
}
|
|
fft1.InverseDecTime(vecsize1,scratchb,FFT_REAL_TYPE(vecsize1*vecsize2));
|
|
fft1.InverseDecTime(vecsize1,scratch,FFT_REAL_TYPE(vecsize1*vecsize2));
|
|
for(i=0;i<rsize1;i++) {
|
|
rarr[i][j] =scratch[i].real();
|
|
rarr[i][j+1]=scratch[i].imag();
|
|
rarr[i][j+2]=scratchb[i].real();
|
|
rarr[i][j+3]=scratchb[i].imag();
|
|
}
|
|
}
|
|
// Remaining columns if rsize2 is not divisible by 4. OTOH, csize2
|
|
// *is* divisible by 2, so we can assume workarr[i][j+1] exists.
|
|
for(;j<rsize2;j+=2) {
|
|
scratch[0]=
|
|
MyComplex(workarr[0][j].real(),workarr[0][j+1].real());
|
|
scratch[csize1-1]=
|
|
MyComplex(workarr[0][j].imag(),workarr[0][j+1].imag());
|
|
for(i=1;i<csize1-1;i++) {
|
|
x1 =workarr[i][j].real(); y1 =workarr[i][j].imag();
|
|
x2 =workarr[i][j+1].real(); y2 =workarr[i][j+1].imag();
|
|
scratch[i] = MyComplex(x1-y2,x2+y1);
|
|
scratch[vecsize1-i] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
fft1.InverseDecTime(vecsize1,scratch,FFT_REAL_TYPE(vecsize1*vecsize2));
|
|
for(i=0;i<rsize1;i++) {
|
|
rarr[i][j] =scratch[i].real();
|
|
if(j+1<rsize2) rarr[i][j+1]=scratch[i].imag();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FFTReal2D::InverseCR(int csize1,int csize2,
|
|
const MyComplex* const* carr,
|
|
int rsize1,int rsize2,double** rarr)
|
|
{
|
|
SetupInverse(OC_MAX(1,2*(csize1-1)),csize2); // Safety
|
|
if(vecsize1<2 || vecsize2<2)
|
|
PlainError(1,"Error in FFTReal2D::InverseCR(...): "
|
|
"Full array dimensions (%dx%d) must be both >=2",
|
|
vecsize1,vecsize2);
|
|
|
|
int i,j;
|
|
FFT_REAL_TYPE x1,y1,x2,y2,xb1,yb1,xb2,yb2;
|
|
|
|
// Column iFFT's
|
|
// Handle the first & csize2/2'th column specially. These cols are
|
|
// the DFT's of real sequences, so they each satisfy the conjugate
|
|
// symmetry condition
|
|
scratch[0]=MyComplex(carr[0][0].real(),carr[0][csize2/2].real());
|
|
for(i=1;i<csize1-1;i++) {
|
|
x1=carr[i][0].real(); y1=carr[i][0].imag();
|
|
x2=carr[i][csize2/2].real(); y2=carr[i][csize2/2].imag();
|
|
scratch[i] = MyComplex(x1-y2,x2+y1);
|
|
scratch[vecsize1-i] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
scratch[csize1-1]=MyComplex(carr[csize1-1][0].real(),
|
|
carr[csize1-1][csize2/2].real());
|
|
fft1.InverseDecTime(vecsize1,scratch,1);
|
|
for(i=0;i<vecsize1;i+=2) { // ASSUMES vecsize1 is even
|
|
// See packing note below.
|
|
workarr[i/2][0] = MyComplex(scratch[i].real(),scratch[i+1].real());
|
|
workarr[i/2][csize2/2] = MyComplex(scratch[i].imag(),scratch[i+1].imag());
|
|
}
|
|
//
|
|
// Do remaining column iFFT's, two at a time for better memory
|
|
// access locality.
|
|
for(j=1;j+1<csize2/2;j+=2) {
|
|
scratch[0]=carr[0][j];
|
|
scratchb[0]=carr[0][j+1];
|
|
for(i=1;i<csize1-1;i++) {
|
|
scratch[i]=carr[i][j];
|
|
scratchb[i]=carr[i][j+1];
|
|
scratchb[vecsize1-i]=conj(carr[i][csize2-1-j]);
|
|
scratch[vecsize1-i]=conj(carr[i][csize2-j]);
|
|
}
|
|
scratch[csize1-1]=carr[csize1-1][j];
|
|
scratchb[csize1-1]=carr[csize1-1][j+1];
|
|
fft1.InverseDecTime(vecsize1,scratchb,1.);
|
|
fft1.InverseDecTime(vecsize1,scratch,1.);
|
|
// Pack into workarr. Rows will be conjugate symmetric, so we
|
|
// can pack two rows into 1 via r[k]+i.r[k+1] -> workarr[k/2].
|
|
for(i=0;i<rsize1;i+=2) {
|
|
// CAREFUL! The above 'rsize1' bound may depend on how the
|
|
// iFFT's are calculated in the 'Row iFFT's' code section,
|
|
// and how 'i' is initialized.
|
|
x1=scratch[i].real(); y1=scratch[i].imag();
|
|
x2=scratch[i+1].real(); y2=scratch[i+1].imag();
|
|
xb1=scratchb[i].real(); yb1=scratchb[i].imag();
|
|
xb2=scratchb[i+1].real(); yb2=scratchb[i+1].imag();
|
|
workarr[i/2][j] = MyComplex(x1-y2,x2+y1);
|
|
workarr[i/2][j+1] = MyComplex(xb1-yb2,xb2+yb1);
|
|
workarr[i/2][csize2-j-1] = MyComplex(xb1+yb2,xb2-yb1);
|
|
workarr[i/2][csize2-j] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
}
|
|
// There should be 1 column left over
|
|
if((j=(csize2/2)-1)%2==1) {
|
|
// Column (csize2/2)-1 *not* processed above
|
|
scratch[0]=carr[0][j];
|
|
for(i=1;i<csize1-1;i++) {
|
|
scratch[i]=carr[i][j];
|
|
scratch[vecsize1-i]=conj(carr[i][csize2-j]);
|
|
}
|
|
scratch[csize1-1]=carr[csize1-1][j];
|
|
fft1.InverseDecTime(vecsize1,scratch,1);
|
|
for(i=0;i<rsize1;i+=2) {
|
|
// CAREFUL! The above 'rsize1' bound may depend on how the
|
|
// iFFT's are calculated in the 'Row iFFT's' code section,
|
|
// and how 'i' is initialized.
|
|
x1=scratch[i].real(); y1=scratch[i].imag();
|
|
x2=scratch[i+1].real(); y2=scratch[i+1].imag();
|
|
workarr[i/2][j] = MyComplex(x1-y2,x2+y1);
|
|
workarr[i/2][csize2-j] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
}
|
|
|
|
// Row iFFT's
|
|
for(i=0;i<rsize1;i+=2) {
|
|
fft2.InverseDecTime(vecsize2,workarr[i/2],
|
|
FFT_REAL_TYPE(vecsize1*vecsize2));
|
|
for(j=0;j<rsize2;j++) rarr[i][j] = workarr[i/2][j].real();
|
|
if(i+1<rsize1) {
|
|
for(j=0;j<rsize2;j++) rarr[i+1][j] = workarr[i/2][j].imag();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FFTReal2D::Forward1D(int rsize1,int rsize2,
|
|
const double* const* rarr,
|
|
int csize1,int csize2,MyComplex** carr)
|
|
{
|
|
// The ForwardRC/CR routines assume full array dimensions >1.
|
|
// This routine handles the special case where (at least) one of
|
|
// the dimension is 1, which degenerates into a simple 1D FFT.
|
|
if(csize1==1) { // Single row FFT
|
|
int j;
|
|
for(j=0;j<rsize2;j++)
|
|
carr[0][j]=MyComplex(rarr[0][j],0.);
|
|
for(;j<csize2;j++)
|
|
carr[0][j]=MyComplex(0.,0.);
|
|
fft2.ForwardDecFreq(csize2,carr[0]);
|
|
} else if(csize2==1) { // Single column FFT
|
|
int i;
|
|
for(i=0;i<rsize1;i++)
|
|
scratch[i]=MyComplex(rarr[i][0],0.0);
|
|
for(;i<vecsize1;i++)
|
|
scratch[i]=MyComplex(0.0,0.0);
|
|
fft1.ForwardDecFreq(vecsize1,scratch);
|
|
for(i=0;i<csize1;i++)
|
|
carr[i][0]=scratch[i]; // Last half, from csize1 to vecsize1
|
|
/// is conj. sym. since input is real.
|
|
} else {
|
|
PlainError(1,"Error in FFTReal2D::Forward1D(...): "
|
|
"One array dimension (of %dx%d) must be==1",
|
|
vecsize1,vecsize2);
|
|
}
|
|
}
|
|
|
|
void FFTReal2D::Inverse1D(int csize1,int csize2,
|
|
const MyComplex* const* carr,
|
|
int rsize1,int rsize2,double** rarr)
|
|
{
|
|
// The InverseRC/CR routines assume full array dimensions >1.
|
|
// This routine handles the special case where (at least) one of
|
|
// the dimension is 1, which degenerates into a simple 1D FFT.
|
|
if(csize1==1) { // Single row iFFT
|
|
int j;
|
|
for(j=0;j<csize2;j++)
|
|
scratch[j]=carr[0][j];
|
|
fft2.InverseDecTime(csize2,scratch);
|
|
for(j=0;j<rsize2;j++)
|
|
rarr[0][j]=scratch[j].real();
|
|
} else if(csize2==1) { // Single column iFFT
|
|
int i;
|
|
scratch[0]=carr[0][0];
|
|
for(i=1;i<csize1-1;i++) {
|
|
scratch[i]=carr[i][0];
|
|
scratch[vecsize1-i]=conj(carr[i][0]); // Last half obtained
|
|
/// by using conjugate symmetry of real data FFT.
|
|
}
|
|
scratch[csize1-1]=carr[csize1-1][0];
|
|
fft2.InverseDecTime(vecsize1,scratch);
|
|
for(i=0;i<rsize1;i++)
|
|
rarr[i][0]=scratch[i].real();
|
|
} else {
|
|
PlainError(1,"Error in FFTReal2D::Inverse1D(...): "
|
|
"One array dimension (of %dx%d) must be==1",
|
|
vecsize1,vecsize2);
|
|
}
|
|
}
|
|
|
|
void FFTReal2D::Forward(int rsize1,int rsize2,
|
|
const double* const* rarr,
|
|
int csize1,int csize2,MyComplex** carr)
|
|
{
|
|
if(csize2<rsize2)
|
|
PlainError(1,"Error in FFTRealD::Forward(int,int,OC_REAL8,**,int,int,"
|
|
"MyComplex**): csize2 (=%d) *must* be >= rsize2 (=%d)\n",
|
|
csize2,rsize2);
|
|
|
|
if(csize1<(rsize1/2)+1)
|
|
PlainError(1,"Error in FFTRealD::Forward(int,int,double**,int,int,"
|
|
"MyComplex**): csize1 (=%d) *must* be >= (rsize1/2)+1"
|
|
" (=%d)\n",csize1,(rsize1/2)+1);
|
|
|
|
Setup(OC_MAX(1,2*(csize1-1)),csize2);
|
|
if(vecsize1<1 || vecsize2<1) return; // Nothing to do
|
|
|
|
// Check for 1D degenerate cases
|
|
if(vecsize1==1 || vecsize2==1) {
|
|
Forward1D(rsize1,rsize2,rarr,csize1,csize2,carr);
|
|
} else {
|
|
// Determine which Forward routine to call (ForwardCR or ForwardRC)
|
|
// (Use double arithmetic to protect against integer overflow. If
|
|
// the two times are very close, then the choice doesn't really
|
|
// matter.)
|
|
// 1) Estimated (proportional) time for ForwardCR
|
|
REALWIDE crtime=REALWIDE(vecsize1*vecsize2)*REALWIDE(logsize2)
|
|
+ REALWIDE(vecsize1*rsize2)*REALWIDE(logsize1);
|
|
// 2) Estimated (proportional) time for ForwardRC
|
|
REALWIDE rctime=REALWIDE(rsize1*vecsize2)*REALWIDE(logsize2)
|
|
+ REALWIDE(vecsize1*vecsize2)*REALWIDE(logsize1);
|
|
// Introduce empirical adjustment factor
|
|
rctime*=CRRCspeedratio;
|
|
if(crtime<=rctime) ForwardCR(rsize1,rsize2,rarr,csize1,csize2,carr);
|
|
else ForwardRC(rsize1,rsize2,rarr,csize1,csize2,carr);
|
|
}
|
|
}
|
|
|
|
void FFTReal2D::Inverse(int csize1,int csize2,
|
|
const MyComplex* const* carr,
|
|
int rsize1,int rsize2,double** rarr)
|
|
{
|
|
if(csize2<rsize2)
|
|
PlainError(1,"Error in FFTRealD::Inverse(int,int,double**,"
|
|
"int,int,MyComplex**): csize2 (=%d) *must* be >="
|
|
" rsize2 (=%d)\n",csize2,rsize2);
|
|
|
|
if(csize1<(rsize1/2)+1)
|
|
PlainError(1,"Error in FFTRealD::Inverse(int,int,double**,"
|
|
"int,int,MyComplex**): csize1 (=%d) *must* be >="
|
|
" (rsize1/2)+1 (=%d)\n",csize1,(rsize1/2)+1);
|
|
|
|
SetupInverse(OC_MAX(1,2*(csize1-1)),csize2);
|
|
if(vecsize1<1 || vecsize2<1) return; // Nothing to do
|
|
|
|
// Check for 1D degenerate cases
|
|
if(vecsize1==1 || vecsize2==1) {
|
|
Inverse1D(csize1,csize2,carr,rsize1,rsize2,rarr);
|
|
} else {
|
|
// Determine which Inverse routine to call (InverseRC or InverseCR)
|
|
// (Use double arithmetic to protect against integer overflow.
|
|
// If the two times are very close, then the choice doesn't really
|
|
// matter.)
|
|
// 1) Estimated (proportional) time for InverseRC (==ForwardCR)
|
|
REALWIDE irctime=REALWIDE(vecsize1*vecsize2)*REALWIDE(logsize2)
|
|
+ REALWIDE(vecsize1*rsize2)*REALWIDE(logsize1);
|
|
// 2) Estimated (proportional) time for InverseCR (==ForwardRC)
|
|
REALWIDE icrtime=REALWIDE(rsize1*vecsize2)*REALWIDE(logsize2)
|
|
+ REALWIDE(vecsize1*vecsize2)*REALWIDE(logsize1);
|
|
// Introduce empirical adjustment factor
|
|
icrtime*=CRRCspeedratio;
|
|
if(irctime<=icrtime) InverseRC(csize1,csize2,carr,rsize1,rsize2,rarr);
|
|
else InverseCR(csize1,csize2,carr,rsize1,rsize2,rarr);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_MPI
|
|
|
|
static FFT fft1_mpi,fft2_mpi;
|
|
static int vecsize1_mpi(0),vecsize2_mpi(0);
|
|
static MyComplex* scratch_mpi(NULL);
|
|
static MyComplex** workarr_mpi(NULL);
|
|
|
|
FFTReal2D_mpi::FFTReal2D_mpi()
|
|
{
|
|
}
|
|
|
|
void SetupMemory_mpi(int size1,int size2)
|
|
{
|
|
if(size1!=vecsize1_mpi) {
|
|
if(scratch_mpi!=NULL) delete[] scratch_mpi;
|
|
vecsize1_mpi=size1;
|
|
scratch_mpi=new MyComplex[vecsize1_mpi];
|
|
}
|
|
if(size2!=vecsize2_mpi) {
|
|
if(workarr_mpi!=NULL) {
|
|
delete[] workarr_mpi[0];
|
|
delete[] workarr_mpi;
|
|
}
|
|
vecsize2_mpi=size2;
|
|
int rowcount=(vecsize1_mpi/2)+1;
|
|
workarr_mpi=new MyComplex*[rowcount];
|
|
workarr_mpi[0]=new MyComplex[rowcount*vecsize2_mpi];
|
|
for(int i=1;i<rowcount;i++)
|
|
workarr_mpi[i]=workarr_mpi[i-1]+vecsize2_mpi;
|
|
}
|
|
}
|
|
|
|
void ReleaseMemory_mpi()
|
|
{
|
|
fft1_mpi.ReleaseMemory();
|
|
fft2_mpi.ReleaseMemory();
|
|
if(scratch_mpi!=NULL) delete[] scratch_mpi;
|
|
scratch_mpi=NULL;
|
|
vecsize1_mpi=0;
|
|
if(workarr_mpi!=NULL) {
|
|
delete[] workarr_mpi[0];
|
|
delete[] workarr_mpi;
|
|
}
|
|
workarr_mpi=NULL;
|
|
vecsize2_mpi=0;
|
|
}
|
|
|
|
void FFTReal2D_mpi::ReleaseMemory()
|
|
{
|
|
Mmsolve_MpiWakeUp(ReleaseMemory_mpi); // On slaves
|
|
ReleaseMemory_mpi(); // On master
|
|
}
|
|
|
|
static int vecsize1_mpi_b(0),vecsize2_mpi_b(0);
|
|
static MyComplex **work1_mpi(NULL),**work2_mpi(NULL);
|
|
|
|
static void SetupMemory_mpi_base_b(int size1,int size2)
|
|
{
|
|
int i;
|
|
if(size1!=vecsize1_mpi_b || size2!=vecsize2_mpi_b) {
|
|
if(work1_mpi!=NULL) {
|
|
delete[] work1_mpi[0];
|
|
delete[] work1_mpi;
|
|
}
|
|
if(work2_mpi!=NULL) {
|
|
delete[] work2_mpi[0];
|
|
delete[] work2_mpi;
|
|
}
|
|
vecsize1_mpi_b=size1;
|
|
vecsize2_mpi_b=size2;
|
|
work1_mpi=new MyComplex*[vecsize1_mpi_b];
|
|
work1_mpi[0]=new MyComplex[vecsize1_mpi_b*vecsize2_mpi_b];
|
|
for(i=1;i<vecsize1_mpi_b;i++)
|
|
work1_mpi[i]=work1_mpi[i-1]+vecsize2_mpi_b;
|
|
int rowcount=vecsize2_mpi_b/2;
|
|
work2_mpi=new MyComplex*[rowcount];
|
|
work2_mpi[0]=new MyComplex[rowcount*vecsize1_mpi_b];
|
|
for(i=1;i<rowcount;i++)
|
|
work2_mpi[i]=work2_mpi[i-1]+vecsize1_mpi_b;
|
|
}
|
|
}
|
|
|
|
static void SetupMemory_mpi_slave_b()
|
|
{
|
|
int size1,size2;
|
|
MPI_Bcast(&size1,1,MPI_INT,0,MPI_COMM_WORLD);
|
|
MPI_Bcast(&size2,1,MPI_INT,0,MPI_COMM_WORLD);
|
|
if(size1<1 || size2<1)
|
|
PlainError(1,"Error propagating array size info in "
|
|
"SetupMemory_mpi_slave_b(): size1=%d, size2=%d",
|
|
size1,size2);
|
|
SetupMemory_mpi_base_b(size1,size2);
|
|
}
|
|
|
|
static void SetupMemory_mpi_master_b(int size1,int size2)
|
|
{
|
|
Mmsolve_MpiWakeUp(SetupMemory_mpi_slave_b);
|
|
MPI_Bcast(&size1,1,MPI_INT,0,MPI_COMM_WORLD);
|
|
MPI_Bcast(&size2,1,MPI_INT,0,MPI_COMM_WORLD);
|
|
SetupMemory_mpi_base_b(size1,size2);
|
|
}
|
|
|
|
void ForwardFFT1_mpi_slave_b()
|
|
{
|
|
// Get data
|
|
int rowcount,colcount;
|
|
colcount=vecsize2_mpi_b;
|
|
MPI_Request request[1];
|
|
MPI_Status status[1];
|
|
MPI_Irecv(&rowcount,1,MPI_INT,0,1,MPI_COMM_WORLD,request);
|
|
MPI_Waitall(1,request,status);
|
|
MPI_Irecv(work1_mpi[0],rowcount*colcount,MMS_COMPLEX,0,2,
|
|
MPI_COMM_WORLD,request);
|
|
MPI_Waitall(1,request,status);
|
|
|
|
// Transform rows
|
|
for(int i=0;i<rowcount;i++) {
|
|
fft1_mpi.ForwardDecFreq(colcount,work1_mpi[i]);
|
|
}
|
|
|
|
// Return results
|
|
MPI_Isend(work1_mpi[0],rowcount*colcount,MMS_COMPLEX,0,3,
|
|
MPI_COMM_WORLD,request);
|
|
MPI_Waitall(1,request,status);
|
|
}
|
|
|
|
void ForwardFFT2_mpi_slave_b()
|
|
{
|
|
// Get data
|
|
int rowcount,colcount;
|
|
colcount=vecsize1_mpi_b;
|
|
MPI_Request request[1];
|
|
MPI_Status status[1];
|
|
MPI_Irecv(&rowcount,1,MPI_INT,0,1,MPI_COMM_WORLD,request);
|
|
MPI_Waitall(1,request,status);
|
|
MPI_Irecv(work2_mpi[0],rowcount*colcount,MMS_COMPLEX,0,2,
|
|
MPI_COMM_WORLD,request);
|
|
MPI_Waitall(1,request,status);
|
|
|
|
// Transform rows
|
|
for(int i=0;i<rowcount;i++) {
|
|
fft2_mpi.ForwardDecFreq(colcount,work2_mpi[i]);
|
|
}
|
|
|
|
// Return results
|
|
MPI_Isend(work2_mpi[0],rowcount*colcount,MMS_COMPLEX,0,3,
|
|
MPI_COMM_WORLD,request);
|
|
MPI_Waitall(1,request,status);
|
|
}
|
|
|
|
void FFTReal2D_mpi::Forward(int rsize1,int rsize2,
|
|
const double* const* rarr,
|
|
int csize1,int csize2,MyComplex** carr)
|
|
{ // Computes FFT of rsize1 x rsize2 double** rarr, leaving result in
|
|
// csize1 x csize2 MyComplex** carr. rsize2 *must* be <= csize2, and
|
|
// rsize1 *must* be <= 2*(csize1-1). This routine returns only the
|
|
// top half +1 of the transform. The bottom half is given by
|
|
// carr[2*(csize1-1)-i][csize2-j]=conj(carr[i][j])
|
|
// for i>=csize1, with the second indices interpreted 'mod csize2'.
|
|
// The dimensions csize2 and 2*(csize1-1) *must* be powers of 2,
|
|
// but rsize1 and rsize2 do not. On import, the rarr will be
|
|
// implicitly zero-padded as necessary to fit into the specified
|
|
// output array carr. To get a non-periodic transform, set
|
|
//
|
|
// 2*(csize1-1) to 2*(first power of 2 >= rsize1), and
|
|
// csize2 to 2*(first power of 2 >= rsize2).
|
|
|
|
int i,j;
|
|
FFT_REAL_TYPE x1,y1,x2,y2;
|
|
int vecsize1=OC_MAX(1,2*(csize1-1));
|
|
int vecsize2=csize2;
|
|
for(i=vecsize1;i>2;i/=2)
|
|
if(i%2!=0) PlainError(1,"Error in FFTReal2D_mpi::Forward(int): "
|
|
"Requested csize1 - 1 (%d - 1) is not a power of 2",
|
|
csize1);
|
|
for(j=vecsize2;j>2;j/=2)
|
|
if(j%2!=0) PlainError(1,"Error in FFTReal2D_mpi::Forward(int): "
|
|
"Requested csize2 (%d) is not a power of 2",
|
|
csize2);
|
|
if(vecsize1==0 || vecsize2==0) return; // Nothing to do
|
|
SetupMemory_mpi_master_b(vecsize1,vecsize2);
|
|
|
|
// Copy input data into packed complex array
|
|
for(i=0;i<rsize1/2;i++) {
|
|
for(j=0;j<rsize2;j++)
|
|
work1_mpi[i][j]=MyComplex(rarr[2*i][j],rarr[2*i+1][j]);
|
|
for(;j<vecsize2;j++)
|
|
work1_mpi[i][j]=MyComplex(0,0); // Zero pad
|
|
}
|
|
if(2*i<rsize1) {
|
|
// Odd number of rows. Pack last with zeros.
|
|
for(j=0;j<rsize2;j++)
|
|
work1_mpi[i][j]=MyComplex(rarr[2*i][j],0.0);
|
|
for(;j<vecsize2;j++)
|
|
work1_mpi[i][j]=MyComplex(0,0); // Zero pad
|
|
}
|
|
|
|
// Do FFT across rows
|
|
int proc,proc_count,base_size,base_orphan,whole_size,chunk_size;
|
|
proc_count=mms_mpi_size;
|
|
Mmsolve_MpiWakeUp(ForwardFFT1_mpi_slave_b);
|
|
whole_size=(rsize1+1)/2;
|
|
base_size=whole_size/proc_count;
|
|
base_orphan=whole_size%proc_count;
|
|
i=0;
|
|
chunk_size=base_size+(0<base_orphan?1:0);
|
|
int msg_count=3*(proc_count-1);
|
|
MPI_Request *request=new MPI_Request[msg_count];
|
|
MPI_Status *status=new MPI_Status[msg_count];
|
|
for(proc=1;proc<proc_count;proc++) {
|
|
i+=chunk_size;
|
|
chunk_size=base_size+(proc<base_orphan?1:0);
|
|
MPI_Isend(&chunk_size,1,MPI_INT,proc,1,MPI_COMM_WORLD,
|
|
request+3*(proc-1));
|
|
MPI_Isend(work1_mpi[i],chunk_size*vecsize2,MMS_COMPLEX,proc,2,
|
|
MPI_COMM_WORLD,request+3*(proc-1)+1);
|
|
MPI_Irecv(work1_mpi[i],chunk_size*vecsize2,MMS_COMPLEX,proc,3,
|
|
MPI_COMM_WORLD,request+3*(proc-1)+2);
|
|
}
|
|
chunk_size=base_size+(0<base_orphan?1:0);
|
|
for(i=0;i<chunk_size;i++) {
|
|
fft1_mpi.ForwardDecFreq(vecsize2,work1_mpi[i]);
|
|
}
|
|
MPI_Waitall(msg_count,request,status);
|
|
|
|
// Unpack and transpose to prepare for FFT in cross dimension.
|
|
// We fill the first row of work2_mpi with the first and middle
|
|
// columns from unpacked work1_mpi, because these are real-valued.
|
|
for(i=0;i<(rsize1+1)/2;i++) {
|
|
work2_mpi[0][2*i]
|
|
=MyComplex(work1_mpi[i][0].real(),work1_mpi[i][vecsize2/2].real());
|
|
work2_mpi[0][2*i+1]
|
|
=MyComplex(work1_mpi[i][0].imag(),work1_mpi[i][vecsize2/2].imag());
|
|
}
|
|
for(i=2*i;i<vecsize1;i++) work2_mpi[0][i]=MyComplex(0.,0.); // Zero pad
|
|
// Process rest of the rows.
|
|
for(j=1;j<vecsize2/2;j++) {
|
|
for(i=0;i<(rsize1+1)/2;i++) {
|
|
x1=work1_mpi[i][j].real()/2.;
|
|
y1=work1_mpi[i][j].imag()/2.;
|
|
x2=work1_mpi[i][vecsize2-j].real()/2.;
|
|
y2=work1_mpi[i][vecsize2-j].imag()/2.;
|
|
work2_mpi[j][2*i]=MyComplex(x1+x2,y1-y2);
|
|
work2_mpi[j][2*i+1]=MyComplex(y1+y2,x2-x1);
|
|
}
|
|
for(i=2*i;i<vecsize1;i++) work2_mpi[j][i]=MyComplex(0.,0.); // Zero pad
|
|
}
|
|
|
|
|
|
// Do FFT's on transposed matrix
|
|
Mmsolve_MpiWakeUp(ForwardFFT2_mpi_slave_b);
|
|
whole_size=vecsize2/2;
|
|
base_size=whole_size/proc_count;
|
|
base_orphan=whole_size%proc_count;
|
|
i=0;
|
|
chunk_size=base_size+(0<base_orphan?1:0);
|
|
msg_count=3*(proc_count-1);
|
|
for(proc=1;proc<proc_count;proc++) {
|
|
i+=chunk_size;
|
|
chunk_size=base_size+(proc<base_orphan?1:0);
|
|
MPI_Isend(&chunk_size,1,MPI_INT,proc,1,MPI_COMM_WORLD,
|
|
request+3*(proc-1));
|
|
MPI_Isend(work2_mpi[i],chunk_size*vecsize1,MMS_COMPLEX,proc,2,
|
|
MPI_COMM_WORLD,request+3*(proc-1)+1);
|
|
MPI_Irecv(work2_mpi[i],chunk_size*vecsize1,MMS_COMPLEX,proc,3,
|
|
MPI_COMM_WORLD,request+3*(proc-1)+2);
|
|
}
|
|
chunk_size=base_size+(0<base_orphan?1:0);
|
|
for(i=0;i<chunk_size;i++) {
|
|
fft2_mpi.ForwardDecFreq(vecsize1,work2_mpi[i]);
|
|
}
|
|
MPI_Waitall(msg_count,request,status);
|
|
delete[] request;
|
|
delete[] status;
|
|
|
|
|
|
// Un-transpose and pack into carr.
|
|
// First row of work2_mpi needs to be handled separately, because
|
|
// of unique packing described above.
|
|
carr[0][0]=MyComplex(work2_mpi[0][0].real(),0.);
|
|
carr[0][vecsize2/2]=MyComplex(work2_mpi[0][0].imag(),0.);
|
|
carr[vecsize1/2][0]=MyComplex(work2_mpi[0][vecsize1/2].real(),0.);
|
|
carr[vecsize1/2][vecsize2/2]=MyComplex(work2_mpi[0][vecsize1/2].imag(),0.);
|
|
for(i=1;i<vecsize1/2;i++) {
|
|
x1=work2_mpi[0][i].real()/2.;
|
|
y1=work2_mpi[0][i].imag()/2.;
|
|
x2=work2_mpi[0][vecsize1-i].real()/2.;
|
|
y2=work2_mpi[0][vecsize1-i].imag()/2.;
|
|
carr[i][0]=MyComplex(x1+x2,y1-y2);
|
|
carr[i][vecsize2/2]=MyComplex(y1+y2,x2-x1);
|
|
}
|
|
// Process remaining rows
|
|
for(j=1;j<vecsize2/2;j++) {
|
|
carr[0][j]=work2_mpi[j][0];
|
|
carr[0][csize2-j]=conj(work2_mpi[j][0]);
|
|
for(i=1;i<vecsize1/2;i++)
|
|
carr[i][j]=work2_mpi[j][i];
|
|
carr[i][j]=work2_mpi[j][i];
|
|
carr[i][csize2-j]=conj(work2_mpi[j][i]);
|
|
for(i=csize1;i<vecsize1;i++)
|
|
carr[vecsize1-i][csize2-j]=conj(work2_mpi[j][i]);
|
|
}
|
|
}
|
|
|
|
void FFTReal2D_mpi::Inverse(int csize1,int csize2,
|
|
const MyComplex* const* carr,
|
|
int rsize1,int rsize2,double** rarr)
|
|
{
|
|
// Initialization
|
|
int i,j;
|
|
int vecsize1=OC_MAX(1,2*(csize1-1));
|
|
int vecsize2=csize2;
|
|
FFT_REAL_TYPE x1,y1,x2,y2; // FFT unpacking scratch vars
|
|
for(i=vecsize1;i>2;i/=2)
|
|
if(i%2!=0) PlainError(1,"Error in FFTReal2D_mpi::Inverse(int): "
|
|
"Requested csize1 - 1 (%d - 1) is not a power of 2",
|
|
csize1);
|
|
for(j=vecsize2;j>2;j/=2)
|
|
if(j%2!=0) PlainError(1,"Error in FFTReal2D_mpi::Inverse(int): "
|
|
"Requested csize2 (%d) is not a power of 2",
|
|
csize2);
|
|
if(vecsize1==0 || vecsize2==0) return; // Nothing to do
|
|
SetupMemory_mpi(vecsize1,vecsize2);
|
|
|
|
|
|
// Do row inverse FFT's
|
|
// Handle the first & csize1'th row specially. These rows are
|
|
// the DFT's of real sequences, so they each satisfy the conjugate
|
|
// symmetry condition
|
|
workarr_mpi[0][0]=MyComplex(carr[0][0].real(),carr[csize1-1][0].real());
|
|
for(j=1;j<csize2/2;j++) {
|
|
x1=carr[0][j].real(); y1=carr[0][j].imag();
|
|
x2=carr[csize1-1][j].real(); y2=carr[csize1-1][j].imag();
|
|
workarr_mpi[0][j] = MyComplex(x1-y2,x2+y1);
|
|
workarr_mpi[0][csize2-j] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
workarr_mpi[0][csize2/2]=MyComplex(carr[0][csize2/2].real(),
|
|
carr[csize1-1][csize2/2].real());
|
|
fft2_mpi.InverseDecTime(csize2,workarr_mpi[0],1.);
|
|
|
|
// iFFT the remaining rows
|
|
for(i=1;i<csize1-1;i++) {
|
|
for(j=0;j<csize2;j++) workarr_mpi[i][j]=carr[i][j];
|
|
fft2_mpi.InverseDecTime(csize2,workarr_mpi[i],1.);
|
|
}
|
|
|
|
// Now do iFFT's on columns. These are conj. symmetric, so we
|
|
// process them 2 at a time. Also, recall the 1st row of workarr
|
|
// contains the iFFT's of the 1st and csize1'th row of the given carr.
|
|
// Note that csize is guaranteed divisible by 2, so if rsize2 is odd
|
|
// then rsize2+1<=csize2.
|
|
for(j=0;j<rsize2;j+=2) {
|
|
scratch_mpi[0]=
|
|
MyComplex(workarr_mpi[0][j].real(),workarr_mpi[0][j+1].real());
|
|
scratch_mpi[csize1-1]=
|
|
MyComplex(workarr_mpi[0][j].imag(),workarr_mpi[0][j+1].imag());
|
|
for(i=1;i<csize1-1;i++) {
|
|
x1 =workarr_mpi[i][j].real();
|
|
y1 =workarr_mpi[i][j].imag();
|
|
x2 =workarr_mpi[i][j+1].real();
|
|
y2 =workarr_mpi[i][j+1].imag();
|
|
scratch_mpi[i] = MyComplex(x1-y2,x2+y1);
|
|
scratch_mpi[vecsize1-i] = MyComplex(x1+y2,x2-y1);
|
|
}
|
|
fft1_mpi.InverseDecTime(vecsize1,scratch_mpi,
|
|
FFT_REAL_TYPE(vecsize1*vecsize2));
|
|
if(j+1<rsize2) {
|
|
for(i=0;i<rsize1;i++) {
|
|
rarr[i][j]=scratch_mpi[i].real();
|
|
rarr[i][j+1]=scratch_mpi[i].imag();
|
|
}
|
|
} else {
|
|
for(i=0;i<rsize1;i++) rarr[i][j]=scratch_mpi[i].real();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#endif /* USE_MPI */
|