#include<stdlib.h>
#pragma TASS input
int NX_BOUND;
#pragma TASS input
int NSTEPS_BOUND;
#pragma TASS input {nx>=2 && nx<=NX_BOUND}
int nx;
#pragma TASS input
double u0[nx];
#pragma TASS input {nsteps>=0 && nsteps<=NSTEPS_BOUND}
int nsteps;
#pragma TASS input {dt>0}
double dt;
#pragma TASS input
double kappa;
#pragma TASS output
double data[nsteps+1][nx];
double u[nx];
void init() {
int i;
for (i=0; i<nx; i++) u[i] = u0[i];
}
void write(int time) {
int i;
for (i=0; i<nx; i++) data[time][i] = u[i];
}
void update() {
double u_new[nx];
int i;
for (i=1; i<nx-1; i++) u_new[i] = u[i] + kappa*(u[i+1] + u[i-1] - 2*u[i]);
for (i=1; i<nx-1; i++) u[i] = u_new[i];
}
void main() {
int iter;
init();
write(0);
for (iter = 1; iter <= nsteps; iter++) {
update();
write(iter);
}
}
#include<stdlib.h>
#include<mpi.h>
#pragma TASS input
int NX_BOUND;
#pragma TASS input
int NSTEPS_BOUND;
#pragma TASS input {nx>=2 && nx<=NX_BOUND}
int nx;
#pragma TASS input
double u0[nx];
#pragma TASS input {nsteps>=0 && nsteps<=NSTEPS_BOUND}
int nsteps;
#pragma TASS input {dt>0}
double dt;
#pragma TASS input
double kappa;
#pragma TASS output
double data[nsteps+1][nx];
int myrank;
int nprocs;
int left; /* rank of left neighbor */
int right; /* rank of right neighbor on torus */
int nxl; /* number cells on this process (excluding ghost cells) */
int first; /* global index for local index 0 */
int start; /* first local index to update */
int stop; /* last local index to update */
double* buf; /* temp. buffer used on proc 0 only */
double* v; /* temperature function */
double* v_new; /* temp. used to update u */
int owner(int index) { return (nprocs*(index+1)-1)/nx; }
int firstForProc(int rank) { return (rank*nx)/nprocs; }
int countForProc(int rank) {
int a;
int b;
a = firstForProc(rank+1);
b = firstForProc(rank);
return a-b;
}
/* init: initializes global variables. */
void init() {
int i;
int owner;
int bufSize;
for (i=0; i<nx; i++) ;
first = firstForProc(myrank);
nxl = countForProc(myrank);
if (first == 0 || nxl == 0)
left = MPI_PROC_NULL;
else
left = owner(first-1);
if (first+nxl >= nx || nxl == 0)
right = MPI_PROC_NULL;
else
right = owner(first+nxl);
bufSize = nx/nprocs;
/* if (nx%nprocs != 0) */ bufSize++;
buf = (double *) malloc(bufSize * sizeof(double));
v = (double *) malloc((nxl + 2) * sizeof(double));
v_new = (double *) malloc((nxl + 2) * sizeof(double));
for (i=1; i<=nxl; i++) v[i] = u0[first+i-1];
owner = owner(0);
if (myrank == owner) start=2; else start=1;
owner = owner(nx-1);
if (myrank == owner) stop=nxl-1; else stop=nxl;
}
void cleanup() {
free(buf);
free(v);
free(v_new);
}
void write(int time) {
int source;
int i;
int j;
int count;
if (myrank != 0) {
MPI_Send(v+1, nxl, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
} else {
j=0;
for (source=0; source<nprocs; source++) {
count = countForProc(source);
if (source != 0)
MPI_Recv(buf, count, MPI_DOUBLE, source, 0, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
else
for (i=0; i<count; i++) buf[i] = v[i+1];
for (i=0; i<count; i++) {
data[time][j] = buf[i];
j++;
}
}
}
}
/* exchange_ghost_cells: updates ghost cells using MPI communication */
void exchange_ghost_cells() {
MPI_Send(v+1, 1, MPI_DOUBLE, left, 0, MPI_COMM_WORLD);
MPI_Recv(v+(nxl+1), 1, MPI_DOUBLE, right, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Send(v+nxl, 1, MPI_DOUBLE, right, 0, MPI_COMM_WORLD);
MPI_Recv(v, 1, MPI_DOUBLE, left, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
/* update: updates v. Uses ghost cells. Purely local operation. Do not update the
* first and last global cells: they are the constant boundary cells. */
void update() {
int i;
for (i=start; i<=stop; i++) v_new[i] = v[i] + kappa*(v[i+1] + v[i-1] - 2*v[i]);
for (i=start; i<=stop; i++) v[i] = v_new[i];
}
/* main: executes simulation, creates one output file for each time step */
void main() {
int argc;
char **argv;
int iter;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
init();
write(0);
for (iter = 1; iter <= nsteps; iter++) {
exchange_ghost_cells();
update();
write(iter);
}
cleanup();
MPI_Finalize();
}
$ ./tass compare -np2=4 -reduce=urgent -inputNX_BOUND=10 -inputNSTEPS_BOUND=4
examples/diffusion/diffusion_seq.c examples/diffusion/diffusion_par.c
+----------------------------------------------------------------------+
| TASS: Toolkit for Accurate Scientific Software |
| version 1.0_r1777 (2010-07-12) http://vsl.cis.udel.edu/tass |
+----------------------------------------------------------------------+
specification : diffusion_seq (numProcs = 1)
specSourceFile : examples/diffusion/diffusion_seq.c
implementation : diffusion_par (numProcs = 4)
implSourceFile : examples/diffusion/diffusion_par.c
mode : COMPARE
prover : CVC3
deadlock : ABSOLUTE
reduction : URGENT
simplify : true
simplifyProver : false
bufferBound : 10
verbose : false
loop method : false
repository : ./TASSREP
frontend : MINIMP
errorBound : 1
NX_BOUND = 10
NSTEPS_BOUND = 4
Comparing diffusion_seq and diffusion_par...
STATS:
statesSeen : 2292 (spec) + 62970 (impl) = 65262
statesMatched : 0 (spec) + 0 (impl) = 0
statesSaved : 732 (spec) + 18570 (impl) = 19302
transitions : 2291 (spec) + 62925 (impl) = 65216
numValues : 6788
numQueries : 81
numMessages : 268
numValidCalls : 0
numSimplifyCalls : 0
time : 11.156s.
RESULT: diffusion_seq and diffusion_par are functionally equivalent.