Gowdy solver
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
sys/mpiWorld.h
Go to the documentation of this file.
00001 /**
00002  *  @file      mpiWorld.h
00003  *  @brief     The MPI implementation that splits a grid among the ranks.
00004  *  @author    Mikica Kocic
00005  *  @copyright GNU General Public License (GPLv3).
00006  */
00007 
00008 #include <mpi.h>
00009 
00010 #ifdef _STANDALONEMPI
00011     #include <cstdio>
00012     #include <cstdlib>
00013     #include <cmath>
00014     #define MPIWorld_sanityCheck main
00015 #endif
00016 
00017 /** Implements the message passing interface (MPI) for high-performance computing (HPC).
00018  *
00019  *  In the MPI environment, the calculations on the grid are split among the ranks
00020  *  so the edges are exchanged between each two neighbors in the following way:
00021  *  <pre>
00022  *        .---- left -----.-------.---- right ----.
00023  *        | ghost | edge  |       | edge  | ghost |
00024  *        +-------+-----------------------+-------+
00025  *   ...  |       |     grid chunk i      |       |
00026  *        +-------+-----------------------+-------+------------+-------+
00027  *                                |       |   grid chunk i+1   |       |  ...
00028  *                                +-------+--------------------+-------+
00029  *                                :     left      :
00030  *                                : ghost | edge  :
00031  *                                `-------v-------ยด
00032  *                         exchanged between two neighbors              </pre>
00033  *
00034  *  The overlapping edges of one rank become other's ghosts (of the same size).
00035  *  The ghost of the left-most rank is determined from the inner boundary conditions and
00036  *  the ghost of the right-most rank is determined from the outer boundary conditions.
00037  *
00038  *  When compiling, use `mpic++` with `-D_USEMPI`. To run, use `mpiexec`.
00039  *
00040  *  @warning The maximal slicing is not compliant with MPI since the boundary value
00041  *  problem for the slicing differential equation requires access to the whole grid!
00042  */
00043 class MPIWorld
00044 {
00045     int worldSize;         //!< The size of the MPI world
00046     int myRank;            //!< Our rank in the MPI world
00047     int leftRank;          //!< The rank of the left neighbor
00048     int rightRank;         //!< The rank of the right neighbor
00049     MPI_Request waitLeft;  //!< Outstanding MPI_Isend request to the left rank
00050     MPI_Request waitRight; //!< Outstanding MPI_Isend request to the right rank
00051 
00052 public:
00053 
00054     /** Initializes the MPI environment and finds our rank in the MPI world.
00055      */
00056     MPIWorld ()
00057     {
00058         // Initialize the MPI environment
00059         //
00060         MPI_Init( NULL, NULL );
00061 
00062         // Find out the rank and the size of the MPI
00063         //
00064         MPI_Comm_rank( MPI_COMM_WORLD, &myRank );
00065         MPI_Comm_size( MPI_COMM_WORLD, &worldSize );
00066 
00067         leftRank  = isFirstInRank() ? -1 : myRank - 1;
00068         rightRank = isLastInRank()  ? -1 : myRank + 1;
00069 
00070         waitLeft = waitRight = MPI_REQUEST_NULL;
00071 
00072         // std::printf( "my rank %d, left %d, right %d\n", myRank, leftRank, rightRank );
00073     }
00074 
00075     ~MPIWorld ()
00076     {
00077          MPI_Finalize ();
00078     }
00079 
00080     int size () const {
00081         return worldSize;
00082     }
00083 
00084     int rank () const {
00085         return myRank;
00086     }
00087 
00088     bool isFirstInRank () const {
00089         return myRank == 0;
00090     }
00091 
00092     bool isLastInRank () const {
00093         return myRank == worldSize - 1;
00094     }
00095 
00096     /** Send our edges (they become other's ghosts) and
00097      *  receive other's edges (they become our ghosts).
00098      */
00099     void exchangeBoundaries
00100     (
00101         void* left_ghost,  //!< Where to put the right edge of the left neighbor
00102         void* left_edge,   //!< Pointer to the left edge of our data
00103         void* right_edge,  //!< Pointer to the right edge of our data
00104         void* right_ghost, //!< Where to put the left edge of the right neighbor
00105         int   edge_size    //!< The edge (or ghost) size
00106         )
00107     {
00108         waitLeft = waitRight = MPI_REQUEST_NULL;
00109 
00110         MPI_Datatype data_type = sizeof(Real) == sizeof(double)
00111                                ? MPI_DOUBLE : MPI_LONG_DOUBLE;
00112 
00113         // Send our edges (they become other's ghosts)
00114 
00115         if ( leftRank >= 0 ) {
00116             MPI_Isend( left_edge, edge_size, data_type, leftRank, 0,
00117                        MPI_COMM_WORLD, &waitLeft );
00118         }
00119         if ( rightRank >= 0 ) {
00120             MPI_Isend( right_edge, edge_size, data_type, rightRank, 0,
00121                        MPI_COMM_WORLD, &waitRight );
00122         }
00123 
00124         // Receive other's edges (they become our ghosts)
00125         //
00126         if ( leftRank >= 0 ) {
00127             MPI_Recv( left_ghost, edge_size, data_type, leftRank, 0,
00128                       MPI_COMM_WORLD, MPI_STATUS_IGNORE );
00129         }
00130         if ( rightRank >= 0 ) {
00131             MPI_Recv( right_ghost, edge_size, data_type, rightRank, 0,
00132                       MPI_COMM_WORLD, MPI_STATUS_IGNORE );
00133         }
00134     }
00135 
00136     /** Wait the other ranks to have received our edges.
00137      */
00138     void waitExchange ()
00139     {
00140         if ( waitLeft != NULL ) {
00141             MPI_Status status;
00142             MPI_Wait( &waitLeft, &status );
00143         }
00144         if ( waitRight != NULL ) {
00145             MPI_Status status;
00146             MPI_Wait( &waitRight, &status );
00147         }
00148     }
00149 };
00150 
00151 /** A sanity check test-bed with benchmarking.
00152  *  To test, replace `main` by MPIWorld_sanityCheck then invoke
00153  *  `mpiexec -n 4 bim-solver 10 10 10 1`.
00154  */
00155 int MPIWorld_sanityCheck( int argc, char** argv )
00156 {
00157     const Int  nGhost = argc >= 2 ? atol ( argv[1] ) : 1;
00158     const Int  mLen   = argc >= 3 ? atol ( argv[2] ) : 1;
00159     const Int  nTotal = argc >= 4 ? atol ( argv[2] ) : 80000;
00160     const bool debug  = argc >= 5;
00161 
00162     MPIWorld mpi;
00163 
00164     Int   nLen        = nTotal / mpi.size(); // nLen should be a multiple of mpi.size()
00165     Real* gridRow     = new Real[ nLen + 2 * nGhost ];
00166     Real* myData      = gridRow + nGhost;
00167     Real* left_ghost  = gridRow;
00168     Real* left_edge   = gridRow + nGhost;
00169     Real* right_edge  = gridRow + nGhost + nLen - nGhost;
00170     Real* right_ghost = gridRow + nGhost + nLen;
00171 
00172     for ( Int m = 0; m <  mLen; ++m )
00173     {
00174         left_ghost  [0] = -1;
00175         left_edge   [0] = ( mpi.rank () + 1 ) * 1000 + m * 100 + 1;
00176         myData      [0] = ( mpi.rank () + 1 ) * 1000 + m * 100 + 2;
00177         right_edge  [0] = ( mpi.rank () + 1 ) * 1000 + m * 100 + 3;
00178         right_ghost [0] = -1;
00179 
00180         if ( debug ) {
00181             std::printf( "PRE %*s %5.0lf|%5.0lf%5.0lf%5.0lf|%5.0lf\n", mpi.rank() * 16,
00182                 "", *left_ghost, *left_edge, *myData, *right_edge, *right_ghost );
00183         }
00184 
00185         mpi.exchangeBoundaries(
00186             left_ghost, left_edge, right_edge, right_ghost, nGhost );
00187 
00188         // Do a sample calculation (equally divided among the ranks)
00189         // The calculation depends on the left ghost.
00190         for ( Int n = nGhost; n < nGhost + nLen; ++n ) {
00191             gridRow[n] = gridRow[n-1] + cos(n) * exp( n * sin(n) );
00192         }
00193 
00194         mpi.waitExchange ();
00195 
00196         if ( debug ) {
00197             printf( "POST%*s %5.0lf|%5.0lf%5.0lf%5.0lf|%5.0lf\n", mpi.rank() * 16,
00198                    "", *left_ghost, *left_edge, *myData, *right_edge, *right_ghost );
00199         }
00200     }
00201 
00202     return 0;
00203 }