ESPResSo 3.2.0-167-g2c9ead1-git
Extensible Simulation Package for Soft Matter Research
fft.c
Go to the documentation of this file.
00001 /*
00002   Copyright (C) 2010,2011,2012,2013 The ESPResSo project
00003   Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 
00004     Max-Planck-Institute for Polymer Research, Theory Group
00005   
00006   This file is part of ESPResSo.
00007   
00008   ESPResSo is free software: you can redistribute it and/or modify
00009   it under the terms of the GNU General Public License as published by
00010   the Free Software Foundation, either version 3 of the License, or
00011   (at your option) any later version.
00012   
00013   ESPResSo is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016   GNU General Public License for more details.
00017   
00018   You should have received a copy of the GNU General Public License
00019   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
00020 */
00021 /** \file fft.c
00022  *
00023  *  Routines, row decomposition, data structures and communication for the 3D-FFT. 
00024  *
00025 */
00026 
00027 #include "fft.h"
00028 
00029 #ifdef P3M
00030 
00031 #include <fftw3.h>
00032 /* our remapping of malloc interferes with fftw3's name mangling. */
00033 void *fftw_malloc(size_t n);
00034 
00035 #include <mpi.h>
00036 #include "communication.h"
00037 #include "grid.h"
00038 #include "fft-common.h"
00039 
00040 /************************************************
00041  * variables
00042  ************************************************/
00043 fft_data_struct fft;
00044 
00045 /** communicate the grid data according to the given fft_forw_plan. 
00046  * \param plan communication plan (see \ref fft_forw_plan).
00047  * \param in   input mesh.
00048  * \param out  output mesh.
00049 */
00050 static void 
00051 fft_forw_grid_comm(fft_forw_plan plan, double *in, double *out);
00052 
00053 /** communicate the grid data according to the given fft_forw_plan/fft_bakc_plan. 
00054  * \param plan_f communication plan (see \ref fft_forw_plan).
00055  * \param plan_b additional back plan (see \ref fft_back_plan).
00056  * \param in     input mesh.
00057  * \param out    output mesh.
00058 */
00059 static void 
00060 fft_back_grid_comm(fft_forw_plan plan_f, fft_back_plan plan_b, double *in, double *out);
00061 
00062 void fft_pre_init() {
00063   fft_common_pre_init(&fft);
00064 }
00065 
00066 int fft_init(double **data, int *ca_mesh_dim, int *ca_mesh_margin, 
00067              int* global_mesh_dim, double *global_mesh_off,
00068              int *ks_pnum)
00069 {
00070   int i,j;
00071   /* helpers */
00072   int mult[3];
00073 
00074   int n_grid[4][3]; /* The four node grids. */
00075   int my_pos[4][3]; /* The position of this_node in the node grids. */
00076   int *n_id[4];     /* linear node identity lists for the node grids. */
00077   int *n_pos[4];    /* positions of nodes in the node grids. */
00078   /* FFTW WISDOM stuff. */
00079   char wisdom_file_name[255];
00080   FILE *wisdom_file;
00081   int wisdom_status;
00082 
00083   FFT_TRACE(fprintf(stderr,"%d: fft_init():\n",this_node));
00084 
00085 
00086   fft.max_comm_size=0; fft.max_mesh_size=0;
00087   for(i=0;i<4;i++) {
00088     n_id[i]  = malloc(1*n_nodes*sizeof(int));
00089     n_pos[i] = malloc(3*n_nodes*sizeof(int));
00090   }
00091 
00092   /* === node grids === */
00093   /* real space node grid (n_grid[0]) */
00094   for(i=0;i<3;i++) {
00095     n_grid[0][i] = node_grid[i];
00096     my_pos[0][i] = node_pos[i];
00097   }
00098   for(i=0;i<n_nodes;i++) {
00099     int lin_ind;
00100     map_node_array(i, &(n_pos[0][3*i+0])); 
00101     lin_ind = get_linear_index( n_pos[0][3*i+0],n_pos[0][3*i+1],n_pos[0][3*i+2], n_grid[0]);
00102     n_id[0][lin_ind] = i;
00103   }
00104     
00105   /* FFT node grids (n_grid[1 - 3]) */
00106   calc_2d_grid(n_nodes,n_grid[1]);
00107   /* resort n_grid[1] dimensions if necessary */
00108   fft.plan[1].row_dir = map_3don2d_grid(n_grid[0], n_grid[1], mult);
00109   fft.plan[0].n_permute = 0;
00110   for(i=1;i<4;i++) fft.plan[i].n_permute = (fft.plan[1].row_dir+i)%3;
00111   for(i=0;i<3;i++) {
00112     n_grid[2][i] = n_grid[1][(i+1)%3];
00113     n_grid[3][i] = n_grid[1][(i+2)%3];
00114   }
00115   fft.plan[2].row_dir = (fft.plan[1].row_dir-1)%3;
00116   fft.plan[3].row_dir = (fft.plan[1].row_dir-2)%3;
00117 
00118 
00119 
00120   /* === communication groups === */
00121   /* copy local mesh off real space charge assignment grid */
00122   for(i=0;i<3;i++) fft.plan[0].new_mesh[i] = ca_mesh_dim[i];
00123   for(i=1; i<4;i++) {
00124     fft.plan[i].g_size=fft_find_comm_groups(n_grid[i-1], n_grid[i], n_id[i-1], n_id[i], 
00125                                         fft.plan[i].group, n_pos[i], my_pos[i]);
00126     if(fft.plan[i].g_size==-1) {
00127       /* try permutation */
00128       j = n_grid[i][(fft.plan[i].row_dir+1)%3];
00129       n_grid[i][(fft.plan[i].row_dir+1)%3] = n_grid[i][(fft.plan[i].row_dir+2)%3];
00130       n_grid[i][(fft.plan[i].row_dir+2)%3] = j;
00131       fft.plan[i].g_size=fft_find_comm_groups(n_grid[i-1], n_grid[i], n_id[i-1], n_id[i], 
00132                                           fft.plan[i].group, n_pos[i], my_pos[i]);
00133       if(fft.plan[i].g_size==-1) {
00134         fprintf(stderr,"%d: INTERNAL ERROR: fft_find_comm_groups error\n", this_node);
00135         errexit();
00136       }
00137     }
00138 
00139     fft.plan[i].send_block = (int *)realloc(fft.plan[i].send_block, 6*fft.plan[i].g_size*sizeof(int));
00140     fft.plan[i].send_size  = (int *)realloc(fft.plan[i].send_size, 1*fft.plan[i].g_size*sizeof(int));
00141     fft.plan[i].recv_block = (int *)realloc(fft.plan[i].recv_block, 6*fft.plan[i].g_size*sizeof(int));
00142     fft.plan[i].recv_size  = (int *)realloc(fft.plan[i].recv_size, 1*fft.plan[i].g_size*sizeof(int));
00143 
00144     fft.plan[i].new_size = fft_calc_local_mesh(my_pos[i], n_grid[i], global_mesh_dim,
00145                                            global_mesh_off, fft.plan[i].new_mesh, 
00146                                            fft.plan[i].start);  
00147     permute_ifield(fft.plan[i].new_mesh,3,-(fft.plan[i].n_permute));
00148     permute_ifield(fft.plan[i].start,3,-(fft.plan[i].n_permute));
00149     fft.plan[i].n_ffts = fft.plan[i].new_mesh[0]*fft.plan[i].new_mesh[1];
00150 
00151     /* FFT_TRACE( printf("%d: comm_group( ",this_node )); */
00152     /* FFT_TRACE( for(j=0; j< fft.plan[i].g_size; j++) printf("%d ")); */
00153     /* FFT_TRACE( printf(")\n")); */
00154 
00155     /* === send/recv block specifications === */
00156     for(j=0; j<fft.plan[i].g_size; j++) {
00157       int k, node;
00158       /* send block: this_node to comm-group-node i (identity: node) */
00159       node = fft.plan[i].group[j];
00160       fft.plan[i].send_size[j] 
00161         = fft_calc_send_block(my_pos[i-1], n_grid[i-1], &(n_pos[i][3*node]), n_grid[i],
00162                               global_mesh_dim, global_mesh_off, &(fft.plan[i].send_block[6*j]));
00163       permute_ifield(&(fft.plan[i].send_block[6*j]),3,-(fft.plan[i-1].n_permute));
00164       permute_ifield(&(fft.plan[i].send_block[6*j+3]),3,-(fft.plan[i-1].n_permute));
00165       if(fft.plan[i].send_size[j] > fft.max_comm_size) 
00166         fft.max_comm_size = fft.plan[i].send_size[j];
00167       /* First plan send blocks have to be adjusted, since the CA grid
00168          may have an additional margin outside the actual domain of the
00169          node */
00170       if(i==1) {
00171         for(k=0;k<3;k++) 
00172           fft.plan[1].send_block[6*j+k  ] += ca_mesh_margin[2*k];
00173       }
00174       /* recv block: this_node from comm-group-node i (identity: node) */
00175       fft.plan[i].recv_size[j] 
00176         = fft_calc_send_block(my_pos[i], n_grid[i], &(n_pos[i-1][3*node]), n_grid[i-1],
00177                               global_mesh_dim, global_mesh_off, &(fft.plan[i].recv_block[6*j]));
00178       permute_ifield(&(fft.plan[i].recv_block[6*j]),3,-(fft.plan[i].n_permute));
00179       permute_ifield(&(fft.plan[i].recv_block[6*j+3]),3,-(fft.plan[i].n_permute));
00180       if(fft.plan[i].recv_size[j] > fft.max_comm_size) 
00181         fft.max_comm_size = fft.plan[i].recv_size[j];
00182     }
00183 
00184     for(j=0;j<3;j++) fft.plan[i].old_mesh[j] = fft.plan[i-1].new_mesh[j];
00185     if(i==1) 
00186       fft.plan[i].element = 1; 
00187     else {
00188       fft.plan[i].element = 2;
00189       for(j=0; j<fft.plan[i].g_size; j++) {
00190         fft.plan[i].send_size[j] *= 2;
00191         fft.plan[i].recv_size[j] *= 2;
00192       }
00193     }
00194     /* DEBUG */
00195     for(j=0;j<n_nodes;j++) {
00196       /* MPI_Barrier(comm_cart); */
00197       if(j==this_node) FFT_TRACE(fft_print_fft_plan(fft.plan[i]));
00198     }
00199   }
00200 
00201   /* Factor 2 for complex fields */
00202   fft.max_comm_size *= 2;
00203   fft.max_mesh_size = (ca_mesh_dim[0]*ca_mesh_dim[1]*ca_mesh_dim[2]);
00204   for(i=1;i<4;i++) 
00205     if(2*fft.plan[i].new_size > fft.max_mesh_size) fft.max_mesh_size = 2*fft.plan[i].new_size;
00206 
00207   FFT_TRACE(fprintf(stderr,"%d: fft.max_comm_size = %d, fft.max_mesh_size = %d\n",
00208                     this_node,fft.max_comm_size,fft.max_mesh_size));
00209 
00210   /* === pack function === */
00211   for(i=1;i<4;i++) {
00212     fft.plan[i].pack_function = fft_pack_block_permute2; 
00213     FFT_TRACE(fprintf(stderr,"%d: forw plan[%d] permute 2 \n",this_node,i));
00214   }
00215   (*ks_pnum)=6;
00216   if(fft.plan[1].row_dir==2) {
00217     fft.plan[1].pack_function = fft_pack_block;
00218     FFT_TRACE(fprintf(stderr,"%d: forw plan[%d] permute 0 \n",this_node,1));
00219     (*ks_pnum)=4;
00220   }
00221   else if(fft.plan[1].row_dir==1) {
00222     fft.plan[1].pack_function = fft_pack_block_permute1;
00223     FFT_TRACE(fprintf(stderr,"%d: forw plan[%d] permute 1 \n",this_node,1));
00224     (*ks_pnum)=5;
00225   }
00226   
00227   /* Factor 2 for complex numbers */
00228   fft.send_buf = (double *)realloc(fft.send_buf, fft.max_comm_size*sizeof(double));
00229   fft.recv_buf = (double *)realloc(fft.recv_buf, fft.max_comm_size*sizeof(double));
00230   if (*data) fftw_free(*data);
00231   (*data)  = (double *)fftw_malloc(fft.max_mesh_size*sizeof(double));
00232   if (fft.data_buf) fftw_free(fft.data_buf);
00233   fft.data_buf = (double *)fftw_malloc(fft.max_mesh_size*sizeof(double));
00234   if(!(*data) || !fft.data_buf || !fft.recv_buf || !fft.send_buf) {
00235     fprintf(stderr,"%d: Could not allocate FFT data arays\n",this_node);
00236     errexit();
00237   }
00238 
00239   fftw_complex *c_data     = (fftw_complex *) (*data);
00240 
00241   /* === FFT Routines (Using FFTW / RFFTW package)=== */
00242   for(i=1;i<4;i++) {
00243     fft.plan[i].dir = FFTW_FORWARD;   
00244     /* FFT plan creation. 
00245        Attention: destroys contents of c_data/data and c_fft.data_buf/data_buf. */
00246     wisdom_status   = FFTW_FAILURE;
00247     sprintf(wisdom_file_name,"fftw3_1d_wisdom_forw_n%d.file",
00248             fft.plan[i].new_mesh[2]);
00249     if( (wisdom_file=fopen(wisdom_file_name,"r"))!=NULL ) {
00250       wisdom_status = fftw_import_wisdom_from_file(wisdom_file);
00251       fclose(wisdom_file);
00252     }
00253     if(fft.init_tag==1) fftw_destroy_plan(fft.plan[i].fftw_plan);
00254 //printf("fft.plan[%d].n_ffts=%d\n",i,fft.plan[i].n_ffts);
00255     fft.plan[i].fftw_plan =
00256       fftw_plan_many_dft(1,&fft.plan[i].new_mesh[2],fft.plan[i].n_ffts,
00257                          c_data,NULL,1,fft.plan[i].new_mesh[2],
00258                          c_data,NULL,1,fft.plan[i].new_mesh[2],
00259                          fft.plan[i].dir,FFTW_PATIENT);
00260     if( wisdom_status == FFTW_FAILURE && 
00261         (wisdom_file=fopen(wisdom_file_name,"w"))!=NULL ) {
00262       fftw_export_wisdom_to_file(wisdom_file);
00263       fclose(wisdom_file);
00264     }
00265     fft.plan[i].fft_function = fftw_execute;       
00266   }
00267 
00268   /* === The BACK Direction === */
00269   /* this is needed because slightly different functions are used */
00270   for(i=1;i<4;i++) {
00271     fft.back[i].dir = FFTW_BACKWARD;
00272     wisdom_status   = FFTW_FAILURE;
00273     sprintf(wisdom_file_name,"fftw3_1d_wisdom_back_n%d.file",
00274             fft.plan[i].new_mesh[2]);
00275     if( (wisdom_file=fopen(wisdom_file_name,"r"))!=NULL ) {
00276       wisdom_status = fftw_import_wisdom_from_file(wisdom_file);
00277       fclose(wisdom_file);
00278     }    
00279     if(fft.init_tag==1) fftw_destroy_plan(fft.back[i].fftw_plan);
00280     fft.back[i].fftw_plan =
00281       fftw_plan_many_dft(1,&fft.plan[i].new_mesh[2],fft.plan[i].n_ffts,
00282                          c_data,NULL,1,fft.plan[i].new_mesh[2],
00283                          c_data,NULL,1,fft.plan[i].new_mesh[2],
00284                          fft.back[i].dir,FFTW_PATIENT);
00285     if( wisdom_status == FFTW_FAILURE && 
00286         (wisdom_file=fopen(wisdom_file_name,"w"))!=NULL ) {
00287       fftw_export_wisdom_to_file(wisdom_file);
00288       fclose(wisdom_file);
00289     }
00290     fft.back[i].fft_function = fftw_execute;
00291     fft.back[i].pack_function = fft_pack_block_permute1;
00292     FFT_TRACE(fprintf(stderr,"%d: back plan[%d] permute 1 \n",this_node,i));
00293   }
00294   if(fft.plan[1].row_dir==2) {
00295     fft.back[1].pack_function = fft_pack_block;
00296     FFT_TRACE(fprintf(stderr,"%d: back plan[%d] permute 0 \n",this_node,1));
00297   }
00298   else if(fft.plan[1].row_dir==1) {
00299     fft.back[1].pack_function = fft_pack_block_permute2;
00300     FFT_TRACE(fprintf(stderr,"%d: back plan[%d] permute 2 \n",this_node,1));
00301   }
00302   fft.init_tag=1;
00303   /* free(data); */
00304   for(i=0;i<4;i++) { free(n_id[i]); free(n_pos[i]); }
00305   return fft.max_mesh_size; 
00306 }
00307 
00308 void fft_perform_forw(double *data)
00309 {
00310   int i;
00311 
00312   /* int m,n,o; */
00313   /* ===== first direction  ===== */
00314   FFT_TRACE(fprintf(stderr,"%d: fft_perform_forw: dir 1:\n",this_node));
00315 
00316   fftw_complex *c_data     = (fftw_complex *) data;
00317   fftw_complex *c_data_buf = (fftw_complex *) fft.data_buf;
00318 
00319   /* communication to current dir row format (in is data) */
00320   fft_forw_grid_comm(fft.plan[1], data, fft.data_buf);
00321 
00322 
00323   /*
00324     fprintf(stderr,"%d: start grid \n",this_node);
00325     i=0;
00326     for(m=0;m<8;m++) {
00327     for(n=0;n<8;n++) {
00328     for(o=0;o<8;o++) {
00329     fprintf(stderr,"%.3f ",fft.data_buf[i++]);
00330     }
00331     fprintf(stderr,"\n");
00332     }
00333     fprintf(stderr,"\n");
00334     }
00335   */
00336 
00337   /* complexify the real data array (in is fft.data_buf) */
00338   for(i=0;i<fft.plan[1].new_size;i++) {
00339     data[2*i]     = fft.data_buf[i];     /* real value */
00340     data[(2*i)+1] = 0;       /* complex value */
00341   }
00342   /* perform FFT (in/out is data)*/
00343   fftw_execute_dft(fft.plan[1].fftw_plan,c_data,c_data);
00344   /* ===== second direction ===== */
00345   FFT_TRACE(fprintf(stderr,"%d: fft_perform_forw: dir 2:\n",this_node));
00346   /* communication to current dir row format (in is data) */
00347   fft_forw_grid_comm(fft.plan[2], data, fft.data_buf);
00348   /* perform FFT (in/out is fft.data_buf)*/
00349   fftw_execute_dft(fft.plan[2].fftw_plan,c_data_buf,c_data_buf);
00350   /* ===== third direction  ===== */
00351   FFT_TRACE(fprintf(stderr,"%d: fft_perform_forw: dir 3:\n",this_node));
00352   /* communication to current dir row format (in is fft.data_buf) */
00353   fft_forw_grid_comm(fft.plan[3], fft.data_buf, data);
00354   /* perform FFT (in/out is data)*/
00355   fftw_execute_dft(fft.plan[3].fftw_plan,c_data,c_data);
00356   //fft_print_global_fft_mesh(fft.plan[3],data,1,0);
00357 
00358   /* REMARK: Result has to be in data. */
00359 }
00360 
00361 void fft_perform_back(double *data)
00362 {
00363   int i;
00364   
00365   fftw_complex *c_data     = (fftw_complex *) data;
00366   fftw_complex *c_data_buf = (fftw_complex *) fft.data_buf;
00367   
00368   /* ===== third direction  ===== */
00369   FFT_TRACE(fprintf(stderr,"%d: fft_perform_back: dir 3:\n",this_node));
00370 
00371 
00372   /* perform FFT (in is data) */
00373   fftw_execute_dft(fft.back[3].fftw_plan,c_data,c_data);
00374   /* communicate (in is data)*/
00375   fft_back_grid_comm(fft.plan[3],fft.back[3],data,fft.data_buf);
00376  
00377   /* ===== second direction ===== */
00378   FFT_TRACE(fprintf(stderr,"%d: fft_perform_back: dir 2:\n",this_node));
00379   /* perform FFT (in is fft.data_buf) */
00380   fftw_execute_dft(fft.back[2].fftw_plan,c_data_buf,c_data_buf);
00381   /* communicate (in is fft.data_buf) */
00382   fft_back_grid_comm(fft.plan[2],fft.back[2],fft.data_buf,data);
00383 
00384   /* ===== first direction  ===== */
00385   FFT_TRACE(fprintf(stderr,"%d: fft_perform_back: dir 1:\n",this_node));
00386   /* perform FFT (in is data) */
00387   fftw_execute_dft(fft.back[1].fftw_plan,c_data,c_data);
00388   /* throw away the (hopefully) empty complex component (in is data)*/
00389   for(i=0;i<fft.plan[1].new_size;i++) {
00390     fft.data_buf[i] = data[2*i]; /* real value */
00391     //Vincent:
00392     if (data[2*i+1]>1e-5) {
00393       printf("Complex value is not zero (i=%d,data=%g)!!!\n",i,data[2*i+1]);
00394       if (i>100) exit(-1);
00395       } 
00396   }
00397   /* communicate (in is fft.data_buf) */
00398   fft_back_grid_comm(fft.plan[1],fft.back[1],fft.data_buf,data);
00399 
00400 
00401   /* REMARK: Result has to be in data. */
00402 }
00403 
00404 void fft_forw_grid_comm(fft_forw_plan plan, double *in, double *out)
00405 {
00406   int i;
00407   MPI_Status status;
00408   double *tmp_ptr;
00409 
00410   for(i=0;i<plan.g_size;i++) {   
00411     plan.pack_function(in, fft.send_buf, &(plan.send_block[6*i]), 
00412                        &(plan.send_block[6*i+3]), plan.old_mesh, plan.element);
00413 
00414     if(plan.group[i]<this_node) {       /* send first, receive second */
00415       MPI_Send(fft.send_buf, plan.send_size[i], MPI_DOUBLE, 
00416                plan.group[i], REQ_FFT_FORW, comm_cart);
00417       MPI_Recv(fft.recv_buf, plan.recv_size[i], MPI_DOUBLE, 
00418                plan.group[i], REQ_FFT_FORW, comm_cart, &status);        
00419     }
00420     else if(plan.group[i]>this_node) {  /* receive first, send second */
00421       MPI_Recv(fft.recv_buf, plan.recv_size[i], MPI_DOUBLE, 
00422                plan.group[i], REQ_FFT_FORW, comm_cart, &status);        
00423       MPI_Send(fft.send_buf, plan.send_size[i], MPI_DOUBLE, 
00424                plan.group[i], REQ_FFT_FORW, comm_cart);      
00425     }
00426     else {                              /* Self communication... */   
00427       tmp_ptr  = fft.send_buf;
00428       fft.send_buf = fft.recv_buf;
00429       fft.recv_buf = tmp_ptr;
00430     }
00431     fft_unpack_block(fft.recv_buf, out, &(plan.recv_block[6*i]), 
00432                  &(plan.recv_block[6*i+3]), plan.new_mesh, plan.element);
00433   }
00434 }
00435 
00436 void fft_back_grid_comm(fft_forw_plan plan_f,  fft_back_plan plan_b, double *in, double *out)
00437 {
00438   int i;
00439   MPI_Status status;
00440   double *tmp_ptr;
00441 
00442   /* Back means: Use the send/recieve stuff from the forward plan but
00443      replace the recieve blocks by the send blocks and vice
00444      versa. Attention then also new_mesh and old_mesh are exchanged */
00445 
00446   for(i=0;i<plan_f.g_size;i++) {
00447     
00448     plan_b.pack_function(in, fft.send_buf, &(plan_f.recv_block[6*i]), 
00449                        &(plan_f.recv_block[6*i+3]), plan_f.new_mesh, plan_f.element);
00450 
00451     if(plan_f.group[i]<this_node) {       /* send first, receive second */
00452       MPI_Send(fft.send_buf, plan_f.recv_size[i], MPI_DOUBLE, 
00453                plan_f.group[i], REQ_FFT_BACK, comm_cart);
00454       MPI_Recv(fft.recv_buf, plan_f.send_size[i], MPI_DOUBLE, 
00455                plan_f.group[i], REQ_FFT_BACK, comm_cart, &status);      
00456     }
00457     else if(plan_f.group[i]>this_node) {  /* receive first, send second */
00458       MPI_Recv(fft.recv_buf, plan_f.send_size[i], MPI_DOUBLE, 
00459                plan_f.group[i], REQ_FFT_BACK, comm_cart, &status);      
00460       MPI_Send(fft.send_buf, plan_f.recv_size[i], MPI_DOUBLE, 
00461                plan_f.group[i], REQ_FFT_BACK, comm_cart);      
00462     }
00463     else {                                /* Self communication... */   
00464       tmp_ptr  = fft.send_buf;
00465       fft.send_buf = fft.recv_buf;
00466       fft.recv_buf = tmp_ptr;
00467     }
00468     fft_unpack_block(fft.recv_buf, out, &(plan_f.send_block[6*i]), 
00469                  &(plan_f.send_block[6*i+3]), plan_f.old_mesh, plan_f.element);
00470   }
00471 }
00472 
00473 #endif