/*--------------------------------------------------------------------- parallel.laplace.c Finite Difference (parallel) program Author: Robb Newman Converted to MPI: 11/12/94 by Xianneng Shen Last revised: 3/14/97 RYL Last revised: 3/20/97 Saleh Elmohamed This program uses a finite difference scheme to solve Laplace's equation for a square matrix distributed over a square (logical) processor topology. A complete description of the algorithm is found in Fox, et al "Solving problems on concurrent Processors, Volume 1: General Techniques and Regular Problems, Prentice Hall, Englewood Cliffs, New Jersey. This program works on the SPMD (single program, multiple data) paradigm. It illustrates 2-d block decomposition, nodes exchanging edge values, and convergence checking. Each matrix element is updated based on the values of the four neighboring matrix elements. This process is repeated until the data converges -- until the average change in any matrix element (compared to the value 20 iterations previous) is smaller than a specified value. To ensure reproducible results between runs, a red/black checkerboard algorithm is used. Each process exchanges edge values with its four neighbors. Then new values are calculated for the upper left and lower right corners (the "red" corners) of each node's matrix. The processes exchange edge values again. The upper right and lower left corners (the "black" corners) are then calculated. The program is currently configured for a 48x48 matrix distributed over four processors. It can be edited to handle different matrix sizes or number of processors, as long as the matrix can be divided evenly between the processors. -----------------------------------------------------------------------*/ #include #include #include "mpi.h" #define n 250 /* matrix is nxn, excluding boundary values */ #define nodeedge 125 /* a task works on a nodeedge x nodeedge matrix */ #define nblock n/nodeedge /* number of tasks per row of matrix */ #define nproc nblock*nblock /* total number of tasks (processors) */ #define w (double)1.2 /* convergence factor */ void setmatrix(double M[][nodeedge+2]); void setcomm(int rank, int comm[]); void iterate(double M[][nodeedge+2],double result[][n],int rank,int comm[]); void setex(double ex[], double M[][nodeedge+2], int which); void unpack(double M[][nodeedge+2],int where,double in[]); void exchange(double M[][nodeedge+2], int comm[], int rank); void dored(double M[][nodeedge+2]); void doblack(double M[][nodeedge+2]); main(int argc, char **argv) { double M[nodeedge+2][nodeedge+2], /* my block of the array */ result[n][n], /* will store all final values */ start, etime; /* initial, elapsed wallclock time (sec)*/ int ntasks, /* number of tasks started */ rank, /* rank (process ID) */ comm[4], /* directions requiring communication */ i,j; FILE *fp; /* file pointer to the output file */ MPI_Init(&argc, &argv); start = MPI_Wtime(); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &ntasks); if (ntasks != nproc) { if (rank == 0) printf("error: MP_PROCS should be set to %i!\n",nproc); exit(1); } if (rank == 0) printf("Everything's okay so far\n"); /* set initial values of M */ setmatrix(M); if (rank == 0) printf("Matrix is set\n"); /* figure out who I communicate with */ setcomm(rank, comm); if (rank == 0) { printf("Communications are set up\n"); printf("Beginning to iterate\n"); } /* update M until convergence */ iterate(M, result, rank, comm); /* report results and timing */ etime = (MPI_Wtime() - start); printf("task %i took %6.3f seconds\n", rank, etime); if (rank == 0) { fp = fopen("parallel.laplace.out", "w"); for (i=0; i (nblock*(nblock-1)-1)) comm[2] = 0; if (fmod((double)rank, (double)(nblock)) == 0.0) comm[3] = 0; } /*------------------------------------------------------------------------- iterate takes care of everything to do with iteration, including the convergence checking -------------------------------------------------------------------------*/ void iterate(double M[][nodeedge+2], double result[][n], int rank, int comm[]) { int it, /* current iteration */ i,j,k,l,index, /* index variables */ done, /* loop control variable */ count; /* length (in elements) of messages */ double mold[nodeedge+2][nodeedge+2], /* my matrix, 20 iterations ago */ /* used to check convergence */ diff, /* the average absolute difference in elements */ /* of M and mold */ in; /* sum of diff from each task */ double MM[n*n], /* initially holds results from all tasks */ ediff, /* the difference in two elements of M and mold */ send[nodeedge][nodeedge]; /* the result I will send to task 0 */ it = 0; done = 0; for (i=1; i<=nodeedge; i++) for (j=1; j<=nodeedge; j++) mold[i][j] = M[i][j]; while (done == 0) { it++; /* exchange values with neighbors, update red squares, exchange values */ /* with neighbors, update black squares */ exchange(M, comm, rank); dored(M); exchange(M, comm, rank); doblack(M); /* check for convergence every 20 iterations */ /* find the average absolute change in elements of M */ /* maximum iterations is 50000 */ if (it>50000) done=1; if ((fmod((double)it, (double)20.0) == 0.0) && (done != 1)) { diff = 0.0; for (i=1; i<=nodeedge; i++) for (j=1; j<=nodeedge; j++) { ediff = M[i][j] - mold[i][j]; if (ediff < 0.0) ediff = -ediff; diff += ediff; mold[i][j] = M[i][j]; } diff = diff/((double)(nodeedge*nodeedge)); MPI_Allreduce(&diff, &in, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); if (in < (double)nproc*.001) done = 1; } } /* send results to task 0 */ /* don't need to include boundary points */ for (i=0; i