source: CIVL/examples/translation/mpi/diffusion2d.c@ 0d5d91b

1.23 2.0 acw/focus-triggers main test-branch
Last change on this file since 0d5d91b was 0d5d91b, checked in by Ziqing Luo <ziqing@…>, 12 years ago

NPROCSX > 1 --> NPROCSX >= 1
NPROCSY > 1 --> NPROCSY >= 1

git-svn-id: svn://vsl.cis.udel.edu/civl/trunk@1750 fb995dde-84ed-4084-dfe6-e5aef3e2452c

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/* diffusion2d.c: parallel 2d-diffusion equation solver with constant boundaries
2 * slicing matrix as a checker board.
3 * To execute: mpicc diffusion2d.c ; mpiexec -n 4 ./a.out 2 2
4 * To verify: civl verify diffusion2d.c
5 */
6#include<stdio.h>
7#include<stdlib.h>
8#include<assert.h>
9#include<string.h>
10#include<mpi.h>
11
12/* Message tags */
13#define FROMLEFT 0
14#define FROMRIGHT 1
15#define FROMTOP 2
16#define FROMBOTTOM 3
17#define DATAPASS 4
18#define comm MPI_COMM_WORLD
19
20#ifdef _CIVL
21$input long NXB = 5; // nx upper bound
22$input long nx; // global number of columns in matrix
23$assume 1 <= nx && nx <= NXB;
24$input long NYB = 5; // ny upper bound
25$input long ny; // global number of rows of matrix
26$assume 1 <= ny && ny <= NYB;
27$input double u_init[ny+2][nx+2]; // initial value of temperatures, including boundaries
28$input double k; // constant coefficient
29$assume k > 0.0 && k < 0.5;
30$input int NSTEPSB = 3; // upper bound for nsteps
31$input int nsteps; // number of steps
32$assume 1<=nsteps && nsteps<=NSTEPSB;
33$input int wstep = 1; // write frame every this many time steps
34double oracle[nsteps][ny+2][nx+2]; // solution computed sequentially, done by proc 0 only
35$input int NPROCSXB; // upper bound for NPROCSX
36$input int NPROCSX = 2; // number of procs in x direction
37$assume NPROCSX > =1 && NPROCSX <= NPROCSXB;
38$input int NPROCSYB; // upper bound for NPROCSY
39$input int NPROCSY = 2; // number of procs in y direction
40$assume NPROCSY >= 1 && NPROCSY <= NPROCSYB;
41$input int _NPROCS = NPROCSX * NPROCSY;
42#else
43long nx, ny;
44int nsteps, wstep;
45int NPROCSX, NPROCSY;
46double constTemp; // value of constant boundaries for test
47double initTemp; // value of initial temperature for test
48double k;
49#endif
50
51/* Global variables */
52int nprocs, rank;
53int left, right, top, bottom;// ranks of neighbors
54int nxl; // local dimension of x
55int nyl; // local dimension of y
56long firstCol; // index of the first column in global array
57long firstRow; // index of the first row in global array
58/* dynamically-allocated 2d array of doubles specifying state of local
59 * grid in current time step. Dimensions are u_curr[nyl+2][nxl+2].
60 */
61double ** u_curr;
62/* dynamically-allocated 2d array of doubles specifying state of local
63 * grid in next time step. Dimensions are u_next[nyl+2][nxl+2].
64 */
65double ** u_next;
66/* dynamically-allocated 1d temporary buffer. Length is recvbuf[nxl+1]
67 */
68double * recvbuf;
69
70/* The processes are arranged geometrically as follows for the case
71 * NPROCSX = 3:
72 * row 0: 0 1 2
73 * row 1: 3 4 5
74 * ...
75 * This function computes the global index of the first column
76 * owned by the process of the given rank. rank must be in the
77 * range [0, _NPROCS-1]. The result will in the range [0, nx-1].
78 */
79long firstColForProc(int rank) {
80 long tmp = (rank - (rank / NPROCSX)*NPROCSX)*nx;
81
82 return tmp/NPROCSX;
83}
84
85/* This function computes the global index of the first row owned by
86 the process of the given rank. rank must be in the range[0,
87 _NPROCS-1]. The result will in the range [0, ny-1]. */
88long firstRowForProc(int rank) {
89 long tmp = ((rank / NPROCSX)*ny);
90
91 return tmp/NPROCSY;
92}
93
94/* Computes the number of columns owned by the process. The result
95 will be in the range [0, nx]. */
96int countColForProc(int rank) {
97 long a = firstColForProc(rank);
98 long b;
99
100 if ((rank / NPROCSX) == ((rank+1) / NPROCSX))
101 b = firstColForProc(rank+1);
102 else
103 b = nx;
104 return b - a;
105}
106
107/* Computes the number of rows owned by the process. The result
108 will be in the range [0, ny]. */
109int countRowForProc(int rank) {
110 long a = firstRowForProc(rank);
111 long b = firstRowForProc(rank+NPROCSX);
112
113 return b - a;
114}
115
116/* Get the owner process of the given cell */
117int OWNER(long col, long row) {
118 long tmp = ((NPROCSY * (row+1))-1);
119 int procRow = tmp / ny;
120 int procCol;
121
122 tmp = ((col + 1) * NPROCSX - 1);
123 procCol = tmp / nx;
124 tmp = procRow * NPROCSX;
125 return tmp + procCol;
126}
127
128
129/* initialize all data values owned by each process */
130void initData() {
131#ifdef _CIVL
132 // Data is initialized with totally unconstrained values
133 // for verification.
134 // set the vertical boundary cells:
135 if (left == MPI_PROC_NULL)
136 for (int i=0; i<nyl+2; i++)
137 u_next[i][0] = u_curr[i][0] = u_init[i + firstRow][0];
138 if (right == MPI_PROC_NULL)
139 for (int i=0; i<nyl+2; i++)
140 u_next[i][nxl+1] = u_curr[i][nxl+1] = u_init[i + firstRow][nx+1];
141 // set the horizontal boundary cells:
142 if (top == MPI_PROC_NULL)
143 for (int i=0; i<nxl+2; i++)
144 u_next[0][i] = u_curr[0][i] = u_init[0][i + firstCol];
145 if (bottom == MPI_PROC_NULL)
146 for (int i=0; i<nxl+2; i++)
147 u_next[nyl+1][i] = u_curr[nyl+1][i] = u_init[ny+1][i + firstCol];
148 for (int i=1; i<nyl+1; i++)
149 memcpy(&u_curr[i][1], &u_init[firstRow + i][firstCol + 1], nxl * sizeof(double));
150#else
151 // All boundary cells are set to the same value constTemp.
152 // All cells in the interior region are set to the same value
153 // initTemp.
154 for (int i=0; i < nyl+2; i++)
155 for (int j=0; j < nxl+2; j++)
156 if (i == 0 || j == 0 || i == nyl+1 || j == nxl+1)
157 u_next[i][j] = u_curr[i][j] = constTemp;
158 else
159 u_curr[i][j] = initTemp;
160#endif
161}
162
163/* Initialize all global variables, allocate memory spaces for
164 * pointers and proc 0 will do a sequential run. The results of the
165 * sequential run will be used to compare with parallel run later. */
166void initialization(int argc, char * argv[]) {
167#ifndef _CIVL
168 nsteps = 300;
169 wstep = 5;
170 nx = 15;
171 ny = 15;
172 if (argc != 3) {
173 printf("Usage: mpiexec -n NPROCS diffusion2d NPROCSX NPROCSY\n"
174 " NPROCSX: number of processes in x direction\n"
175 " NPROCSY: number of processes in y direction\n"
176 " NPROCS: the product of NPROCSX and NPROCSY\n");
177 exit(1);
178 }
179 NPROCSX = atoi(argv[1]);
180 NPROCSY = atoi(argv[2]);
181 assert(NPROCSX * NPROCSY == nprocs);
182 constTemp = 0.0;
183 initTemp = 100.0;
184 k = 0.13;
185#endif
186 printf("Diffusion2d with k=%f, nx=%ld, ny=%ld, nsteps=%d, wstep=%d\n",
187 k, nx, ny, nsteps, wstep);
188 nxl = countColForProc(rank);
189 nyl = countRowForProc(rank);
190 if (rank == 0)
191 recvbuf = (double *)malloc((nxl + 1) * sizeof(double));
192 u_curr = (double **)malloc((nyl + 2) * sizeof(double *));
193 assert(u_curr);
194 u_next = (double **)malloc((nyl + 2) * sizeof(double *));
195 assert(u_next);
196 for (int i=0; i < nyl + 2; i++){
197 u_curr[i] = (double *)malloc((nxl + 2) * sizeof(double));
198 assert(u_curr[i]);
199 u_next[i] = (double *)malloc((nxl + 2) * sizeof(double));
200 assert(u_next[i]);
201 }
202 firstCol = firstColForProc(rank);
203 firstRow = firstRowForProc(rank);
204 // computes neighbors
205 if (firstCol != 0)
206 left = OWNER(firstCol - 1, firstRow);
207 else
208 left = MPI_PROC_NULL;
209 if (firstRow != 0)
210 top = OWNER(firstCol, firstRow - 1);
211 else
212 top = MPI_PROC_NULL;
213 if (firstCol + nxl < nx)
214 right = OWNER(firstCol + nxl, firstRow);
215 else
216 right = MPI_PROC_NULL;
217 if (firstRow + nyl < ny)
218 bottom = OWNER(firstCol, firstRow + nyl);
219 else
220 bottom = MPI_PROC_NULL;
221#ifdef _CIVL
222 /* In CIVL mode process with rank 0 will be responsible for
223 * computing the diffusion2d equation sequentially such that the
224 * results can be used to compare with the ones of parallel run. */
225 if (rank == 0) {
226 for (long i = 0; i < ny + 2; i++)
227 for (long j = 0; j < nx + 2; j++)
228 oracle[0][i][j] = u_init[i][j];
229 for (int t=1; t < nsteps; t++)
230 for (long i = 0; i < ny + 2; i++)
231 for (long j = 0; j < nx + 2; j++)
232 if (i==0 || j==0 || i == ny + 1 || j == nx + 1)
233 oracle[t][i][j] = oracle[t-1][i][j];
234 else
235 oracle[t][i][j] = oracle[t-1][i][j] +
236 k*(oracle[t-1][i+1][j] + oracle[t-1][i-1][j] +
237 oracle[t-1][i][j+1] + oracle[t-1][i][j-1] -
238 4*oracle[t-1][i][j]);
239 }
240#endif
241}
242
243/* Update local cells owned by process */
244void update() {
245 double **tmp;
246
247 for (int i = 1; i < nyl + 1; i++)
248 for (int j = 1; j < nxl + 1; j++) {
249 u_next[i][j] = u_curr[i][j] +
250 k*(u_curr[i+1][j] + u_curr[i-1][j] +
251 u_curr[i][j+1] + u_curr[i][j-1] - 4*u_curr[i][j]);
252 }
253 // swap two pointers
254 tmp = u_curr;
255 u_curr = u_next;
256 u_next = tmp;
257}
258
259/* Exchange ghost cells between proceeses */
260void exchange() {
261 double sendbuf[nyl];
262 double recvbuf[nyl];
263
264 // sends top border row, receives into bottom ghost cell row
265 MPI_Sendrecv(&u_curr[1][1], nxl, MPI_DOUBLE, top, FROMBOTTOM, &u_curr[nyl+1][1], nxl,
266 MPI_DOUBLE, bottom, FROMBOTTOM, comm, MPI_STATUS_IGNORE);
267 // sends bottom border row, receives into top ghost cell row
268 MPI_Sendrecv(&u_curr[nyl][1], nxl, MPI_DOUBLE, bottom, FROMTOP, &u_curr[0][1], nxl,
269 MPI_DOUBLE, top, FROMTOP, comm, MPI_STATUS_IGNORE);
270 // sends left border column, receives into temporary buffer
271 for (int i = 0; i < nyl; i++) sendbuf[i] = u_curr[i+1][1];
272 MPI_Sendrecv(sendbuf, nyl, MPI_DOUBLE, left, FROMRIGHT, recvbuf, nyl,
273 MPI_DOUBLE, right, FROMRIGHT, comm, MPI_STATUS_IGNORE);
274 // copies temporary buffer into right ghost cell column
275 if (right != MPI_PROC_NULL)
276 for (int i = 0; i < nyl; i++) u_curr[i+1][nxl+1] = recvbuf[i];
277 // sends right border column, receives into temporary buffer
278 for (int i = 0; i < nyl; i++) sendbuf[i] = u_curr[i+1][nxl];
279 MPI_Sendrecv(sendbuf, nyl, MPI_DOUBLE, right, FROMLEFT, recvbuf, nyl,
280 MPI_DOUBLE, left, FROMLEFT, comm, MPI_STATUS_IGNORE);
281 // copies temporary buffer into left ghost cell column
282 if (left != MPI_PROC_NULL)
283 for (int i = 0; i < nyl; i++) u_curr[i+1][0] = recvbuf[i];
284}
285
286/* Helper function for write_frame(int). In CIVL mode, it takes the
287 index of the first column, the number of columns owned by the
288 process and the index of current row to locate the corresponding
289 cell in oracle and compare it with the given cell's value computed
290 in parallel */
291void printData(int time, int firstCol, int nxl, int currRow, double * buf) {
292 for (int i=0; i<nxl; i++) {
293 printf("%6.2f", *(buf + i));
294#ifdef _CIVL
295 $assert(*(buf + i) == oracle[time][currRow + 1][firstCol + i + 1]) : \
296 "Error: disagreement at time %d position [%d][%d]: saw %lf, expected %lf", \
297 time, currRow, firstCol + i,
298 *(buf + i), oracle[time][currRow + 1][firstCol + i + 1];
299#endif
300 }
301}
302
303/* Print the computed matrix at the given time step all processes
304 * should send their local data to process rank 0 which is responsible
305 * for printing */
306void write_frame(int time) {
307 // sends data row by row
308 if (rank != 0) {
309 for (int i=0; i<nyl; i++)
310 MPI_Send(&u_curr[i+1][1], nxl, MPI_DOUBLE, 0, DATAPASS, comm);
311 } else {
312 printf("\n-------------------- time step:%d --------------------\n", time);
313 for (int i=0; i < NPROCSY; i++) {
314 int numRows = countRowForProc(i*NPROCSX);
315
316 for (int j=0; j < numRows; j++) {
317 for (int k=0; k < NPROCSX; k++) {
318 int curr_rank = i*NPROCSX + k;
319
320 if (curr_rank!=0) {
321 int senderx = firstColForProc(curr_rank);
322 int sendery = firstRowForProc(curr_rank);
323 int senderNxl = countColForProc(curr_rank);
324
325 MPI_Recv(recvbuf, senderNxl, MPI_DOUBLE, curr_rank, DATAPASS, comm, MPI_STATUS_IGNORE);
326 printData(time, senderx, senderNxl, sendery+j, recvbuf);
327 } else {
328 printData(time, firstCol, nxl, firstRow+j, &u_curr[j+1][1]);
329 }
330 }
331 printf("\n");
332 }
333 }
334 }
335}
336
337int main(int argc, char * argv[]) {
338 int i,j;
339
340#ifdef _CIVL
341 // elaborating nx, ny, NPROCSX and NPROCSY...
342 elaborate(nx);
343 elaborate(ny);
344 elaborate(NPROCSX);
345 elaborate(NPROCSY);
346#endif
347 MPI_Init(&argc, &argv);
348 MPI_Comm_rank(comm, &rank);
349 MPI_Comm_size(comm, &nprocs);
350 initialization(argc, argv);
351 initData();
352 for (i=0; i<nsteps; i++) {
353 if (nxl != 0 && nyl != 0) {
354 if (i%wstep == 0)
355 write_frame(i);
356 exchange();
357 update();
358 }
359 }
360 for (i=0; i<nyl+2; i++) {
361 free(u_curr[i]);
362 free(u_next[i]);
363 }
364 free(u_curr);
365 free(u_next);
366 if (rank == 0)
367 free(recvbuf);
368 return 0;
369}
Note: See TracBrowser for help on using the repository browser.