source: CIVL/examples/mpi/diffusion2d.c@ 7d77e64

main test-branch
Last change on this file since 7d77e64 was ea777aa, checked in by Alex Wilton <awilton@…>, 3 years ago

Moved examples, include, build_default.properties, common.xml, and README out from dev.civl.com into the root of the repo.

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

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