#include #include #include #define FROMLEFT 0 #define FROMRIGHT 1 #define FROMBOTTOM 2 #define FROMTOP 3 #define comm MPI_COMM_WORLD #define FIRSTCOL(rank) \ ((((rank)-((rank)/nprocsx)*nprocsx)*nx)/nprocsx) #define FIRSTROW(rank) \ ((((rank)/nprocsx)*ny)/nprocsy) #define OWNER(col,row) \ (((nprocsy*(row+1)-1)/ny)*nprocsx+((((col)+1)*nprocsx-1)/nx)) #define LEFT(rank) \ (FIRSTCOL(rank)!=0?OWNER(FIRSTCOL(rank)-1,FIRSTROW(rank)):MPI_PROC_NULL) #define TOP(rank) \ (FIRSTROW(rank)!=0?OWNER(FIRSTCOL(rank),FIRSTROW(rank)-1):MPI_PROC_NULL) #define RIGHT(rank) \ (((FIRSTCOL(rank)+nxl) 0; @ requires 0 <= nxl && nxl < 4; @ requires 0 <= nyl && nyl < 4; @ assigns u[1 .. nyl][1 .. nxl]; @ ensures \forall int i, j; 1 <= i && i <= nyl && @ 1 <= j && j <= nxl ==> @ u[i][j] == \old(u[i][j] + @ k*(u[i+1][j] + u[i-1][j] + @ u[i][j+1] + u[i][j-1] - 4*u[i][j])); @*/ void update() { double (* tmp)[]; for (int i = 1; i < nyl + 1; i++) for (int j = 1; j < nxl + 1; j++) { u_new[i][j] = u[i][j] + k*(u[i+1][j] + u[i-1][j] + u[i][j+1] + u[i][j-1] - 4*u[i][j]); } tmp = u; u = u_new; u_new = tmp; } /* The processes are arranged geometrically as follows for the case * NPROCSX = 3: * row 0: 0 1 2 * row 1: 3 4 5 * ... */ /*@ requires \valid((double (*)[nxl+2])u + (0 .. (nyl + 1))); @ requires \valid((double (*)[nxl+2])u_new + (0 .. (nyl + 1))); @ \mpi_collective(MPI_COMM_WORLD, P2P): @ requires rank == \mpi_comm_rank; @ requires nprocsx * nprocsy == \mpi_comm_size && @ nprocsx > 0 && nprocsx <= \mpi_comm_size && @ nprocsy > 0 && nprocsy <= \mpi_comm_size; @ requires 0 < nxl && nxl < 3 && 0 < nyl && nyl < 3; @ requires nxl < nx < 4 && nyl < ny < 4; @ requires \mpi_agree(nprocsx) && \mpi_agree(nprocsy); @ requires \mpi_agree(nx) && \mpi_agree(ny); @ requires left == LEFT(rank); @ requires right == RIGHT(rank); @ requires top == TOP(rank); @ requires bottom == BOTTOM(rank); @ behavior assign_by_left: @ assumes FIRSTCOL(rank)*nprocsx != 0; @ requires nyl == \on(left, nyl); @ assigns u[1 .. nyl][0]; @ ensures \forall int i; 1 <= i && i <= nyl ==> @ u[i][0] == \on(left, u[i][nxl]); @ waitsfor left; @ behavior assign_by_right: @ assumes FIRSTCOL(rank) + nxl < nx; @ requires nyl == \on(right, nyl); @ assigns u[1 .. nyl][nxl+1]; @ ensures \forall int i; 1 <= i && i <= nyl ==> @ u[i][nxl+1] == \on(right, u[i][1]); @ waitsfor right; @ behavior assign_by_top: @ assumes FIRSTROW(rank) != 0; @ requires nxl == \on(top, nxl); @ assigns u[0][1 .. nxl]; @ ensures \mpi_equals(&u[0][1], nxl, MPI_DOUBLE, \on(top, &u[nyl][1])); @ waitsfor top; @ behavior assign_by_bottom: @ assumes FIRSTROW(rank) + nyl < ny; @ requires nxl == \on(bottom, nxl); @ assigns u[nyl+1][1 .. nxl]; @ ensures \mpi_equals(&u[nyl+1][1], nxl, MPI_DOUBLE, \on(bottom, &u[1][1])); @ waitsfor bottom; @*/ void exchange() { // sends top border row, receives into bottom ghost cell row MPI_Sendrecv(&u[1][1], nxl, MPI_DOUBLE, top, FROMBOTTOM, &u[nyl+1][1], nxl, MPI_DOUBLE, bottom, FROMBOTTOM, comm, MPI_STATUS_IGNORE); // sends bottom border row, receives into top ghost cell row MPI_Sendrecv(&u[nyl][1], nxl, MPI_DOUBLE, bottom, FROMTOP, &u[0][1], nxl, MPI_DOUBLE, top, FROMTOP, comm, MPI_STATUS_IGNORE); double * sendbuf; double * recvbuf; sendbuf = (double *)malloc(sizeof(double) * nyl); recvbuf = (double *)malloc(sizeof(double) * nyl); // sends left border column, receives into temporary buffer for (int i = 0; i < nyl; i++) sendbuf[i] = u[i+1][1]; MPI_Sendrecv(sendbuf, nyl, MPI_DOUBLE, left, FROMRIGHT, recvbuf, nyl, MPI_DOUBLE, right, FROMRIGHT, comm, MPI_STATUS_IGNORE); // copies temporary buffer into right ghost cell column if (right != MPI_PROC_NULL) for (int i = 0; i < nyl; i++) u[i+1][nxl+1] = recvbuf[i]; // sends right border column, receives into temporary buffer for (int i = 0; i < nyl; i++) sendbuf[i] = u[i+1][nxl]; MPI_Sendrecv(sendbuf, nyl, MPI_DOUBLE, right, FROMLEFT, recvbuf, nyl, MPI_DOUBLE, left, FROMLEFT, comm, MPI_STATUS_IGNORE); // copies temporary buffer into left ghost cell column if (left != MPI_PROC_NULL) for (int i = 0; i < nyl; i++) u[i+1][0] = recvbuf[i]; free(sendbuf); free(recvbuf); }