/* diffusion2d_cb.c: parallel 2d-diffusion equation solver with constant boundaries * slicing matrix as a checker board. * To execute: mpicc diffusion2d_cb.c ; mpiexec -n 4 ./a.out 2 2 * To verify: civl verify diffusion2d_cb.c */ #include #include #include #include /* Message tags */ #define FROMLEFT 0 #define FROMRIGHT 1 #define FROMTOP 2 #define FROMBOTTOM 3 #define DATAPASS 4 #define comm MPI_COMM_WORLD #ifdef _CIVL $input int NXB = 5; // nx upper bound $input int nx; // global number of columns in matrix $assume 1 <= nx && nx <= NXB; $input int NYB = 5; // ny upper bound $input int ny; // global number of rows of matrix $assume 1 <= ny && ny <= NYB; $input double u_init[ny+2][nx+2]; // initial value of temperatures, includes 4 // strings of constant boundries $input double k; // constant coefficient $assume k > 0.0 && k < 0.5; $input int NSTEPSB = 5; // boundary of number of steps $input int nsteps; // number of steps $assume 1<=nsteps && nsteps<=NSTEPSB; $input int wstep = 1; // write frame every this many time steps double oracle[nsteps][ny+2][nx+2]; // solution computed sequentially, done by proc 0 only $input int XPROCSB; // Bound number of components of columns $input int xProcs; // Number of components of columns $assume xProcs > 1 && xProcs <= XPROCSB; $input int YPROCSB; // Bound number of components of rows $input int yProcs; // Number of components of rows $assume yProcs > 1 && yProcs <= YPROCSB; $input int _NPROCS; #else int nx, ny, nsteps, wstep; int xProcs, yProcs; double constTemp, initTemp; // values of constant boundaries and // initial tempretures double k; #endif /* Global variables */ double ** u_curr; double ** u_next; int nprocs, rank, left, right, top, bottom; int nxl, nyl, firstCol, firstRow; /* Compute the global column index of cells owned by the process */ int firstColForProc(int rank) { return (rank - (rank / xProcs)*xProcs)*nx/xProcs; } /* Compute the global row index of cells owned by the process */ int firstRowForProc(int rank) { return ((rank / xProcs)*ny)/yProcs; } /* Computes the number of columns owned by the process */ int countColForProc(int rank) { int a = firstColForProc(rank); int b; if((rank / xProcs) == ((rank+1) / xProcs)) b = firstColForProc(rank+1); else b = nx; return b - a; } /* Computes the number of rows owned by the process */ int countRowForProc(int rank) { int a = firstRowForProc(rank); int b = firstRowForProc(rank+xProcs); return b - a; } /* Get the owner process of the given cell */ int OWNER(int col, int row) { int procRow = ((yProcs * (row+1))-1) / ny; int procCol = ((col + 1) * xProcs - 1) / nx; return procRow * xProcs + procCol; } void setConstBoundaries() { int i; // sets vertical constant boundaries if(left == MPI_PROC_NULL) for(i=0; i xProcs * yProcs)?xProcs*yProcs:nprocs; constTemp = 0.0; initTemp = 100.0; k = 0.13; printf("Diffusion2d with k=%f, nx=%d, ny=%d, nsteps=%d, wstep=%d\n", k, nx, ny, nsteps, wstep); #endif nxl = countColForProc(rank); nyl = countRowForProc(rank); u_curr = (double **)malloc((nyl + 2) * sizeof(double *)); assert(u_curr); u_next = (double **)malloc((nyl + 2) * sizeof(double *)); assert(u_next); for(i=0; i < nyl + 2; i++){ u_curr[i] = (double *)malloc((nxl + 2) * sizeof(double)); assert(u_curr[i]); u_next[i] = (double *)malloc((nxl + 2) * sizeof(double)); assert(u_next[i]); } firstCol = firstColForProc(rank); firstRow = firstRowForProc(rank); // computes neighbors if(firstCol != 0) left = OWNER(firstCol - 1, firstRow); else left = MPI_PROC_NULL; if(firstRow != 0) top = OWNER(firstCol, firstRow - 1); else top = MPI_PROC_NULL; if(firstCol + nxl < nx) right = OWNER(firstCol + nxl, firstRow); else right = MPI_PROC_NULL; if(firstRow + nyl < ny) bottom = OWNER(firstCol, firstRow + nyl); else bottom = MPI_PROC_NULL; setConstBoundaries(); #ifdef _CIVL // In CIVL mode process with rank 0 will be responsible for computing the diffusion2d equation // sequentially such that the results can be used to compare with the ones of parallel run. if(rank == 0) { for(i = 0; i < ny + 2; i++) for(j = 0; j < nx + 2; j++) oracle[0][i][j] = u_init[i][j]; for(int t=1; t < nsteps; t++) for(i = 0; i < ny + 2; i++) for(j = 0; j < nx + 2; j++) if(i==0 || j==0 || i == ny + 1 || j == nx + 1) oracle[t][i][j] = oracle[t-1][i][j]; else oracle[t][i][j] = oracle[t-1][i][j] + k*(oracle[t-1][i+1][j] + oracle[t-1][i-1][j] + oracle[t-1][i][j+1] + oracle[t-1][i][j-1] - 4*oracle[t-1][i][j]); } #endif } void update() { int i, j; double **tmp; for(i = 1; i < nyl + 1; i++) for(j = 1; j < nxl + 1; j++) { u_next[i][j] = u_curr[i][j] + k*(u_curr[i+1][j] + u_curr[i-1][j] + u_curr[i][j+1] + u_curr[i][j-1] - 4*u_curr[i][j]); } //swap two pointers tmp = u_curr; u_curr = u_next; u_next = tmp; } void exchange() { double sendbuf[nyl]; double recvbuf[nyl]; // sends top string, receives bottom string MPI_Sendrecv(&u_curr[1][1], nxl, MPI_DOUBLE, top, FROMBOTTOM, &u_curr[nyl+1][1], nxl, MPI_DOUBLE, bottom, FROMBOTTOM, comm, MPI_STATUS_IGNORE); // sends bottom string, receives top string MPI_Sendrecv(&u_curr[nyl][1], nxl, MPI_DOUBLE, bottom, FROMTOP, &u_curr[0][1], nxl, MPI_DOUBLE, top, FROMTOP, comm, MPI_STATUS_IGNORE); // sends left most string, receives right most string for(int i = 0; i < nyl; i++) sendbuf[i] = u_curr[i+1][1]; MPI_Sendrecv(sendbuf, nyl, MPI_DOUBLE, left, FROMRIGHT, recvbuf, nyl, MPI_DOUBLE, right, FROMRIGHT, comm, MPI_STATUS_IGNORE); if(right != MPI_PROC_NULL) for(int i = 0; i < nyl; i++) u_curr[i+1][nxl+1] = recvbuf[i]; // sends right most string, receives left most string for(int i = 0; i < nyl; i++) sendbuf[i] = u_curr[i+1][nxl]; MPI_Sendrecv(sendbuf, nyl, MPI_DOUBLE, right, FROMLEFT, recvbuf, nyl, MPI_DOUBLE, left, FROMLEFT, comm, MPI_STATUS_IGNORE); if(left != MPI_PROC_NULL) for(int i = 0; i < nyl; i++) u_curr[i+1][0] = recvbuf[i]; } void printData(int time, int firstCol, int nxl, int firstRow, int nyl, double * buf) { for(int i=0; i