/* This file is part of the Snoopy code. Snoopy code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Snoopy code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Snoopy code. If not, see . */ #include #include "common.h" #include // Global ifdef. No transpose if no MPI. #ifdef MPI_SUPPORT #ifdef FFTW3_MPI_SUPPORT #include #endif double complex * temp1; double complex * temp2; #ifdef FFTW3_MPI_SUPPORT fftw_plan plan_t_XY, plan_t_YX; #endif double transpose_timer; void init_transpose() { temp1 = (double complex *) fftw_malloc( sizeof(double complex) * NTOTAL_COMPLEX); if (temp1 == NULL) ERROR_HANDLER( ERROR_CRITICAL, "No memory for temp1 allocation"); temp2 = (double complex *) fftw_malloc( sizeof(double complex) * NTOTAL_COMPLEX); if (temp2 == NULL) ERROR_HANDLER( ERROR_CRITICAL, "No memory for temp2 allocation"); #ifdef FFTW3_MPI_SUPPORT #ifdef _OPENMP fftw_plan_with_nthreads( nthreads ); #endif plan_t_XY = fftw_mpi_plan_many_transpose(NX, NY, (NZ+2), NX/NPROC, NY/NPROC, wr1, wr1, MPI_COMM_WORLD, FFT_PLANNING); if (plan_t_XY == NULL) ERROR_HANDLER( ERROR_CRITICAL, "FFTW plan_t_XY plan creation failed"); plan_t_YX = fftw_mpi_plan_many_transpose(NY, NX, (NZ+2), NY/NPROC, NX/NPROC, wr1, wr1, MPI_COMM_WORLD, FFT_PLANNING); if (plan_t_YX == NULL) ERROR_HANDLER( ERROR_CRITICAL, "FFTW plan_t_YX plan creation failed"); #endif transpose_timer = 0.0; return; } void finish_transpose() { fftw_free(temp1); fftw_free(temp2); #ifdef FFTW3_MPI_SUPPORT fftw_destroy_plan(plan_t_XY); fftw_destroy_plan(plan_t_YX); #endif return; } double read_transpose_timer() { return(transpose_timer); } #ifdef FFTW3_MPI_SUPPORT void transpose_complex_XY(double complex *qin, double complex *qout) { transpose_timer = transpose_timer - get_c_time(); fftw_execute_r2r( plan_t_XY, (double *) qin, (double *) qout); transpose_timer = transpose_timer + get_c_time(); return; } void transpose_complex_YX(double complex *qin, double complex *qout) { transpose_timer = transpose_timer - get_c_time(); fftw_execute_r2r( plan_t_YX, (double *) qin, (double *) qout); transpose_timer = transpose_timer + get_c_time(); return; } #else // transpose complex routines are optimized since they are going to be called by ffts routine // the real transpose is not, since it's more a "convenient" routine. It can however be optimized easely... void transpose_complex_XY(double complex *qin, double complex *qout) { // Will transpose qin into qout, // qin have dimensions nxin/nproc, nyin, nzin // Total qin array (all processors) nxin, nyin, nzin // qout have dimensions nyin/nproc, nxin, nzin // Total qout array (all processors) nyin, nxin, nzin int i,j,k,n; const int nxin = NX_COMPLEX; const int nyin = NY_COMPLEX; const int nzin = NZ_COMPLEX; int nproc = NPROC; int local_nxin = nxin / nproc; int local_nyin = nyin / nproc; // First, transpose locally the array in qout (will be erased anyway...) transpose_timer = transpose_timer - get_c_time(); #ifdef _OPENMP #pragma omp parallel for private(i,j,k) schedule(static) #endif for(i=0 ; i < local_nxin ; i++) { for(j=0 ; j < nyin ; j++) { for(k=0 ; k < nzin ; k++) { temp1[j*local_nxin*nzin + i*nzin + k] = qin[i*nyin*nzin + j*nzin + k]; } } } // Next, MPI the whole thing... Have to be out of place // Here we could use qin as destination, if qin could be destroyed (might be an interesting optimisation...) // This step corresponds to an exchange of chuncks of size (local_nyin,local_nxin,nzin) MPI_Alltoall(temp1, local_nxin*local_nyin*nzin*sizeof(double complex), MPI_BYTE, temp2, local_nxin*local_nyin*nzin*sizeof(double complex), MPI_BYTE, MPI_COMM_WORLD); // From here, temp is made of a contiguous array of chunks of size (local_nyin,local_nxin,nzin) // Which can be seen as a 4D Array of size (nproc,local_nyin,local_nxin,nzin); // One have to reorder the chunks to get the array right #ifdef _OPENMP #pragma omp parallel for private(i,j,k,n) schedule(static) #endif for(i=0 ; i < local_nyin ; i++) { for(n=0 ; n < nproc ; n++) { for(j=0 ; j < local_nxin ; j++) { for(k=0 ; k < nzin ; k++) { qout[i*nxin*nzin + (j+n*local_nxin)*nzin + k] = temp2[n*local_nyin*local_nxin*nzin + i*local_nxin*nzin + j*nzin + k]; } } } } transpose_timer = transpose_timer + get_c_time(); return; } void transpose_complex_YX(double complex *qin, double complex *qout) { // Will transpose qin into qout, // qin have dimensions nxin/nproc, nyin, nzin // Total qin array (all processors) nxin, nyin, nzin // qout have dimensions nyin/nproc, nxin, nzin // Total qout array (all processors) nyin, nxin, nzin int i,j,k,n; const int nxin = NY_COMPLEX; const int nyin = NX_COMPLEX; const int nzin = NZ_COMPLEX; int nproc = NPROC; int local_nxin = nxin / nproc; int local_nyin = nyin / nproc; // First, transpose locally the array in qout (will be erased anyway...) transpose_timer = transpose_timer - get_c_time(); #ifdef _OPENMP #pragma omp parallel for private(i,j,k) schedule(static) #endif for(i=0 ; i < local_nxin ; i++) { for(j=0 ; j < nyin ; j++) { for(k=0 ; k < nzin ; k++) { temp1[j*local_nxin*nzin + i*nzin + k] = qin[i*nyin*nzin + j*nzin + k]; } } } // Next, MPI the whole thing... Have to be out of place // Here we could use qin as destination, if qin could be destroyed (might be an interesting optimisation...) // This step corresponds to an exchange of chuncks of size (local_nyin,local_nxin,nzin) MPI_Alltoall(temp1, local_nxin*local_nyin*nzin*sizeof(double complex), MPI_BYTE, temp2, local_nxin*local_nyin*nzin*sizeof(double complex), MPI_BYTE, MPI_COMM_WORLD); // From here, temp is made of a contiguous array of chunks of size (local_nyin,local_nxin,nzin) // Which can be seen as a 4D Array of size (nproc,local_nyin,local_nxin,nzin); // One have to reorder the chunks to get the array right #ifdef _OPENMP #pragma omp parallel for private(i,j,k,n) schedule(static) #endif for(i=0 ; i < local_nyin ; i++) { for(n=0 ; n < nproc ; n++) { for(j=0 ; j < local_nxin ; j++) { for(k=0 ; k < nzin ; k++) { qout[i*nxin*nzin + (j+n*local_nxin)*nzin + k] = temp2[n*local_nyin*local_nxin*nzin + i*local_nxin*nzin + j*nzin + k]; } } } } transpose_timer = transpose_timer + get_c_time(); return; } #endif void transpose_complex_YZ(double complex *qin, double complex *qout) { // this transposition is **out of place** int i,j,k; const int nxin = NX_COMPLEX; const int nyin = NY_COMPLEX; const int nzin = NZ_COMPLEX; int nproc = NPROC; int local_nxin = nxin / nproc; for(i=0 ; i < local_nxin ; i++) { for(j=0 ; j < nyin ; j++) { for(k=0 ; k < nzin ; k++) { qout[i*nzin*nyin + k*nyin + j] = qin[i*nyin*nzin + j*nzin + k]; } } } return; } void transpose_complex_ZY(double complex *qin, double complex *qout) { // this transposition is **out of place** int i,j,k; const int nxin = NX_COMPLEX; const int nyin = NZ_COMPLEX; const int nzin = NY_COMPLEX; int nproc = NPROC; int local_nxin = nxin / nproc; // First, transpose locally the array in qout (will be erased anyway...) for(i=0 ; i < local_nxin ; i++) { for(j=0 ; j < nyin ; j++) { for(k=0 ; k < nzin ; k++) { qout[i*nzin*nyin + k*nyin + j] = qin[i*nyin*nzin + j*nzin + k]; } } } return; } void transpose_real(const int nxin, const int nyin, const int nzin, const int nproc, double *qin, double *qout) { // Will transpose qin into qout, // qin have dimensions nxin/nproc, nyin, nzin // Total qin array (all processors) nxin, nyin, nzin // qout have dimensions nyin/nproc, nxin, nzin // Total qout array (all processors) nyin, nxin, nzin int i,j,k,n; const int local_nxin = nxin / nproc; const int local_nyin = nyin / nproc; // Typecast for compatibility double *tempc1 = (double *) temp1; double *tempc2 = (double *) temp2; // First, transpose locally the array in qout (will be erased anyway...) for(i=0 ; i < local_nxin ; i++) { for(j=0 ; j < nyin ; j++) { for(k=0 ; k < nzin ; k++) { // tempc is seen as a (nyin, local_nxin, nzin) array here tempc1[j*local_nxin*nzin + i*nzin + k] = qin[i*nyin*nzin + j*nzin + k]; } } } // Next, MPI the whole thing... Have to be out of place // Here we could use qin as destination, if qin could be destroyed (might be an interesting optimisation...) // This step corresponds to an exchange of chuncks of size (local_nyin,local_nxin,nzin) MPI_Alltoall(tempc1, local_nxin*local_nyin*nzin*sizeof(double), MPI_BYTE, tempc2,local_nxin*local_nyin*nzin*sizeof(double), MPI_BYTE, MPI_COMM_WORLD); // From here, temp is made of a contiguous array of chunks of size (local_nyin,local_nxin,nzin) // Which can be seen as a 4D Array of size (nproc,local_nyin,local_nxin,nzin); // One have to reorder the chunks to get the array right // for(i=0 ; i < NTOTAL_COMPLEX ; i++) { // qout[i]=w1[i]; // } for(i=0 ; i < local_nyin ; i++) { for(n=0 ; n < nproc ; n++) { for(j=0 ; j < local_nxin ; j++) { for(k=0 ; k < nzin ; k++) { qout[i*nxin*nzin + (j+n*local_nxin)*nzin + k] = tempc2[n*local_nyin*local_nxin*nzin + i*local_nxin*nzin + j*nzin + k]; } } } } return; } #endif