int gmx_parallel_3dfft(gmx_parallel_3dfft_t pfft_setup, enum gmx_fft_direction dir, void * in_data, void * out_data) { int i,j,k; int nx,ny,nz,nzc,nzr; int local_x_start,local_nx; int local_y_start,local_ny; t_complex * work; real * rdata; t_complex * cdata; t_complex * ctmp; work = pfft_setup->work; /* When we do in-place FFTs the data need to be embedded in the z-dimension, * so there is room for the complex data. This means the direct space * _grid_ (not data) dimensions will be nx*ny*(nzc*2), where nzc=nz/2+1. * If we do out-of-place transforms the direct space dimensions are simply * nx*ny*nz, and no embedding is used. * The complex dimensions are always ny*nx*nzc (note the transpose). * * The direct space _grid_ dimension is nzr. */ nx = pfft_setup->nx; ny = pfft_setup->ny; nz = pfft_setup->nz; nzc = pfft_setup->nzc; if(in_data == out_data) { nzr = 2*nzc; } else { nzr = nz; } gmx_parallel_3dfft_limits(pfft_setup, &local_x_start, &local_nx, &local_y_start, &local_ny); if(dir == GMX_FFT_REAL_TO_COMPLEX) { rdata = (real *)in_data + local_x_start*ny*nzr; cdata = (t_complex *)out_data + local_x_start*ny*nzc; /* Perform nx local 2D real-to-complex FFTs in the yz slices. * When the input data is "embedded" for 3D-in-place transforms, this * must also be done in-place to get the data embedding right. * * Note that rdata==cdata when we work in-place. */ for(i=0;i<local_nx;i++) { gmx_fft_2d_real(pfft_setup->fft_yz, GMX_FFT_REAL_TO_COMPLEX, rdata + i*ny*nzr, cdata + i*ny*nzc); } /* Transpose to temporary work array */ gmx_parallel_transpose_xy(cdata, work, nx, ny, pfft_setup->local_slab, pfft_setup->slab2grid_x, pfft_setup->slab2grid_y, nzc, pfft_setup->nnodes, pfft_setup->node2slab, pfft_setup->aav, pfft_setup->comm); /* Transpose from temporary work array in order YXZ to * the output array in order YZX. */ /* output cdata changes when nx or ny not divisible by nnodes */ cdata = (t_complex *)out_data + local_y_start*nx*nzc; for(j=0;j<local_ny;j++) { gmx_fft_transpose_2d(work + j*nzc*nx, cdata + j*nzc*nx, nx, nzc); } /* Perform local_ny*nzc complex FFTs along the x dimension */ for(i=0;i<local_ny*nzc;i++) { gmx_fft_1d(pfft_setup->fft_x, GMX_FFT_FORWARD, cdata + i*nx, work + i*nx); } /* Transpose back from YZX to YXZ. */ for(j=0;j<local_ny;j++) { gmx_fft_transpose_2d(work + j*nzc*nx, cdata + j*nzc*nx, nzc, nx); } } else if(dir == GMX_FFT_COMPLEX_TO_REAL) { cdata = (t_complex *)in_data + local_y_start*nx*nzc; rdata = (real *)out_data + local_x_start*ny*nzr; /* If we are working in-place it doesn't matter that we destroy * input data. Otherwise we use an extra temporary workspace array. */ if(in_data == out_data) { ctmp = cdata; } else { ctmp = pfft_setup->work2; } /* Transpose from YXZ to YZX. */ for(j=0;j<local_ny;j++) { gmx_fft_transpose_2d(cdata + j*nzc*nx, work + j*nzc*nx, nx, nzc); } /* Perform local_ny*nzc complex FFTs along the x dimension */ for(i=0;i<local_ny*nzc;i++) { gmx_fft_1d(pfft_setup->fft_x, GMX_FFT_BACKWARD, work + i*nx, ctmp + i*nx); } /* Transpose from YZX to YXZ. */ for(j=0;j<local_ny;j++) { gmx_fft_transpose_2d(ctmp + j*nzc*nx, work + j*nzc*nx, nzc, nx); } if(in_data == out_data) { /* output cdata changes when nx or ny not divisible by nnodes */ ctmp = (t_complex *)in_data + local_x_start*ny*nzc; } gmx_parallel_transpose_xy(work, ctmp, ny, nx, pfft_setup->local_slab, pfft_setup->slab2grid_y, pfft_setup->slab2grid_x, nzc, pfft_setup->nnodes, pfft_setup->node2slab, pfft_setup->aav, pfft_setup->comm); /* Perform nx local 2D complex-to-real FFTs in the yz slices. * The 3D FFT is done in-place, so we need to do this in-place too in order * to get the data organization right. */ for(i=0;i<local_nx;i++) { gmx_fft_2d_real(pfft_setup->fft_yz, GMX_FFT_COMPLEX_TO_REAL, ctmp + i*ny*nzc, rdata + i*ny*nzr); } } else { gmx_fatal(FARGS,"Incorrect FFT direction."); } /* Skip the YX backtranspose to save communication! Grid is now YXZ */ return 0; }
void powerspectavg(real ***intftab, int tsteps, int xbins, int ybins, char **outfiles) { /*Fourier plans and output;*/ gmx_fft_t fftp; t_complex *ftspect1; /* Spatial FFT of interface for each time frame and interface ftint[time,xycoord][0], ftintf[time,xycoord][1] for interface 1 and 2 respectively */ t_complex *ftspect2; real *pspectavg1; /*power -spectrum 1st interface*/ real *pspectavg2; /* -------------- 2nd interface*/ real *temp; FILE *datfile1, *datfile2; /*data-files with interface data*/ int n; /*time index*/ int fy = ybins/2+1; /* number of (symmetric) fourier y elements; */ int rfl = xbins*fy; /*length of real - DFT == Symmetric 2D matrix*/ int status; /*Prepare data structures for FFT, with time averaging of power spectrum*/ if ( (status = gmx_fft_init_2d_real(&fftp, xbins, ybins, GMX_FFT_FLAG_NONE) ) != 0) { free(fftp); gmx_fatal(status, __FILE__, __LINE__, "Error allocating FFT"); } /*Initialize arrays*/ snew(ftspect1, rfl); snew(ftspect2, rfl); snew(temp, rfl); snew(pspectavg1, rfl); snew(pspectavg2, rfl); /*Fouriertransform directly (no normalization or anything)*/ /*NB! Check carefully indexes here*/ for (n = 0; n < tsteps; n++) { gmx_fft_2d_real(fftp, GMX_FFT_REAL_TO_COMPLEX, intftab[0][n], ftspect1); gmx_fft_2d_real(fftp, GMX_FFT_REAL_TO_COMPLEX, intftab[1][n], ftspect2); /*Add to average for interface 1 here*/ addtoavgenergy(ftspect1, pspectavg1, rfl, tsteps); /*Add to average for interface 2 here*/ addtoavgenergy(ftspect2, pspectavg2, rfl, tsteps); } /*Print out average energy-spectrum to outfiles[0] and outfiles[1];*/ datfile1 = ffopen(outfiles[0], "w"); datfile2 = ffopen(outfiles[1], "w"); /*Filling dat files with spectral data*/ fprintf(datfile1, "%s\n", "kx\t ky\t\tPower(kx,ky)"); fprintf(datfile2, "%s\n", "kx\t ky\t\tPower(kx,ky)"); for (n = 0; n < rfl; n++) { fprintf(datfile1, "%d\t%d\t %8.6f\n", (n / fy), (n % fy), pspectavg1[n]); fprintf(datfile2, "%d\t%d\t %8.6f\n", (n /fy), (n % fy), pspectavg2[n]); } ffclose(datfile1); ffclose(datfile2); free(ftspect1); free(ftspect2); }