// create a matrix from the key file Matrix* assignMatrixFromFile(char* fileName) { int sideLength, i, j; int **myArray; Matrix* myMatrix = allocMatrix(); FILE *ifp = fopen(fileName, "r"); // assign side length from file fscanf(ifp, "%d", &sideLength); // create matrix with given dimensions myArray = alloc2DArray(sideLength, sideLength); // assign values to matrix for(i = 0; i < sideLength; ++i) { for(j = 0; j < sideLength; ++j) { if(j == sideLength - 1) { fscanf(ifp, "%d", &myArray[i][j]); } else { fscanf(ifp, "%d %*c", &myArray[i][j]); } } } fclose(ifp); myMatrix->grid = myArray; myMatrix->rows = sideLength; myMatrix->cols = sideLength; return myMatrix; }
// create a column of fragmented text from the plaintext for multiplication against the key Matrix* createColumn(char* text, int rowNum) { int i; Matrix* newColumn = allocMatrix(); newColumn->cols = 1; newColumn->rows = rowNum; newColumn->grid = alloc2DArray(newColumn->rows, newColumn->cols); for(i = 0; i < rowNum; ++i) { (newColumn->grid)[i][0] = text[i]; } return newColumn; }
// auxillary function for encipherText(), which performs matrix multiplication Matrix* hillProduct(Matrix* keyMatrix, Matrix* columnVector) { int i; Matrix* C = allocMatrix(); C->cols = 1; C->rows = keyMatrix->rows; C->grid = alloc2DArray(C->rows, C->cols); for(i = 0; i < C->rows; ++i) { (C->grid)[i][0] = (dotProduct(keyMatrix->grid, columnVector->grid, i, columnVector->rows) % 26 + 'a'); } return C; }
int main(int argc, char **argv) { // timing double tInit = 0; //double tComm = 0; double tCalc = 0; double startTimeInit, endTimeInit; //double startTimeComm, endTimeComm; double startTimeCalc, endTimeCalc; //========================================================================= // MPI and subdomain basic setup //========================================================================= //int rank, P, p; int n, N; MPI_Init(&argc, &argv); // ################################### // Start timing INITIALIZATION startTimeInit = MPI_Wtime(); // ################################### /* MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &P); */ // set size of global domain and grid if (argc > 1 && atoi(argv[1]) > DOMAINSIZE ) { N = atoi(argv[1]); // use argument if big enough fprintf(stdout, "Setting N and n to: %d x %d.\n", N, N ); } else { N = DOMAINSIZE; fprintf(stdout, "Setting N to DOMAINSIZE: %d x %d.\n", N, N ); fflush(stdout); } /*p = (int) sqrt(P); // size of process grid n = (int) N / p; // size of subdomain*/ n = N; // size of subdomain // check grid parameters /*if (badGridParams(rank, P, p, N, n)) // TODO: this function is just a hack { MPI_Finalize(); return 0; } else if (!rank) { fprintf(stdout, "P: %d, p: %d, N: %d, n: %d\n", P, p, N, n ); fflush(stdout); }*/ // TODO: add an ID argument so we can identify the particular run // if no ID given, create an ID based on system time (timestamp) // suggestion concatenate this ID to all filenames so we get // filename_ID.txt //========================================================================= // MPI cartesian process mesh //========================================================================= /* // create mpi process mesh int meshCoords[2]; // my mesh coordinates int meshRank; // my meshRank int dimensions[2] = {p, p}; // dimensions of mesh int wraparound[2] = {1, 1}; // wraparound MPI_Comm processMesh; MPI_Cart_create(MPI_COMM_WORLD, 2, dimensions, wraparound, 1, &processMesh); MPI_Comm_rank(processMesh, &meshRank); // get my mesh rank MPI_Cart_coords(processMesh, meshRank, 2, meshCoords); // get my i,j coordinates in process grid //printf("Rank: %d, meshRank: %d, i,j: %d,%d\n", rank, meshRank, meshCoords[0], meshCoords[1]); // What color am I // even row AND even process = red, even row AND odd process = black // odd row AND even process = black, odd row AND odd prcocess = red int red; if ( (meshCoords[I] % 2) == 0 ) // even row { red = (meshRank % 2) ? 0 : 1; // even process = red, else black } else // odd row { red = (meshRank % 2) ? 1 : 0; // even process = black, else red } // printf("MeshRank %d with coords [%d %d] is red? %d\n", meshRank, meshCoords[0], meshCoords[1], red); // gfigure out my neighbourgood int kernel[3][3]; // hood[i][j] = meshRank, hood[1][1] = my meshRank int neighbour[NDIRS]; // neighbour[DIRECTION] = rank of neighbour //int nRank, eRank, sRank, wRank, neRank, seRank, swRank, nwRank; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { int xof = meshCoords[I] - 1; int yof = meshCoords[J] - 1; int crd[2] = {xof + i, yof + j}; int rnk; MPI_Cart_rank(processMesh, crd, &rnk); // get rank kernel[i][j] = rnk; } } neighbour[NORTH] = kernel[0][1]; neighbour[EAST] = kernel[1][2]; neighbour[SOUTH] = kernel[2][1]; neighbour[WEST] = kernel[1][0]; neighbour[NORTHEAST] = kernel[0][2]; neighbour[SOUTHEAST] = kernel[2][2]; neighbour[SOUTHWEST] = kernel[2][0]; neighbour[NORTHWEST] = kernel[0][0]; printf("MeshRank %d, red: %d with coords [%d %d] has hood N E S W NE SE SW NW [%d %d %d %d %d %d %d %d].\n", meshRank, red, meshCoords[I], meshCoords[J], neighbour[NORTH], neighbour[EAST], neighbour[SOUTH], neighbour[WEST], neighbour[NORTHEAST], neighbour[SOUTHEAST], neighbour[SOUTHWEST], neighbour[NORTHWEST] ); //========================================================================= // Submatrices for border(outgoing) and ghost(incoming) //========================================================================= // ghost border and corners //MPI_Datatype north, east, south, west; //MPI_Datatype northEast, southEast, southWest, northWest; // array of the ghost(incoming) submatrices numbered clockwise N E S W NE SE SW NW MPI_Datatype ghost[NDIRS]; // list of coordinates for ghost parts int ghostSizes[] = { 1, n, // north size n, 1, // east size 1, n, // south size n, 1, // west size 1, 1, // NE size 1, 1, // SE size 1, 1, // SW size 1, 1 // NW size }; // create the border subarrays for (int i = 0; i < NDIRS; ++i) { int totalDim[2] = {n + 2, n + 2}; // dim of local data array int starts[2] = {0, 0}; int subsizes[2] = {ghostSizes[(i * 2)], ghostSizes[(i * 2) + 1]}; MPI_Type_create_subarray(2, totalDim, subsizes, starts, MPI_ORDER_C, MPI_DOUBLE, &ghost[i]); MPI_Type_commit(&ghost[i]); } // array of the border(outgoing) submatrices numbered clockwise N E S W NE SE SW NW MPI_Datatype border[NDIRS]; // list of coordinates for border parts int borderSizes[] = { 1, n, // north size n, 1, // east size 1, n, // south size n, 1, // west size 1, 1, // NE size 1, 1, // SE size 1, 1, // SW size 1, 1 // NW size }; // create the border subarrays for (int i = 0; i < NDIRS; ++i) { int sizes[2] = {n + 2, n + 2}; // dim of local data array int starts[2] = {0, 0}; int subsizes[2] = {borderSizes[(i * 2)], borderSizes[(i * 2) + 1]}; MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_DOUBLE, &border[i]); MPI_Type_commit(&border[i]); } */ //========================================================================= // Computation //========================================================================= double hx = 1.0 / (N - 1); double ht = HT; double ru = (ht * DU) / (hx * hx); double rv = (ht * DV) / (hx * hx); /*if (meshRank == 0) { printf("hx: %.15f, ht: %.15f, ru: %.15f, rv: %.15f\n", hx, ht, ru, rv ); }*/ // allocate data vectors double **u = alloc2DArray(n); double **unew = alloc2DArray(n); double **v = alloc2DArray(n); double **vnew = alloc2DArray(n); // fprintf(stdout, "Starting init.\n" ); // init for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { u[i][j] = 1.0; unew[i][j] = 1.0; v[i][j] = 0.0; vnew[i][j] = 0.0; } } // fprintf(stdout, "Starting initial blob.\n" ); // For one process: int c = floor(n / 2); int cc = floor(n / 4); for (int i = cc ; i < (c + cc); i++) { for (int j = cc ; j < (c + cc); j++) { u[i][j] = U0; v[i][j] = V0; } } // fprintf(stdout, "Done init.\n" ); // For four processes: /*if (Psq == 4) { int r = 50; if (rank == 0) { for (int i = n_inner - r; i < n_inner; i++) { for (int j = n_inner - r; j < n_inner; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } if (rank == 1) { for (int i = 0; i < r; i++) { for (int j = n_inner - r; j < n_inner; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } if (rank == 2) { for (int i = n_inner - r; i < n_inner; i++) { for (int j = 0; j < r; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } if (rank == 3) { for (int i = 0; i < r; i++) { for (int j = 0; j < r; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } }*/ // For sixteen processes: /*if (P == 16) { if(meshRank == 0) { printf("Creating initial condition for 16 processes.\n"); } // int r = 50; if (meshRank == 5 || meshRank == 6 || meshRank == 9 || meshRank == 10) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { u[i][j] = 0.5; v[i][j] = 0.25; } } } }*/ // For sixty-four processes /*if (Psq == 64) { int r = 50; if (rank == 27) { for (int i = n_inner - r; i < n_inner; i++) { for (int j = n_inner - r; j < n_inner; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } if (rank == 28) { for (int i = 0; i < r; i++) { for (int j = n_inner - r; j < n_inner; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } if (rank == 35) { for (int i = n_inner - r; i < n_inner; i++) { for (int j = 0; j < r; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } if (rank == 36) { for (int i = 0; i < r; i++) { for (int j = 0; j < r; j++) { u[i + j * n_inner] = 0.5; v[i + j * n_inner] = 0.25; } } } }*/ /*// inital blobs if (meshRank == 0) { for (int i = 1; i <= BLOPSIZE; ++i) { for (int j = 1; j <= BLOPSIZE; ++j) { u[i][j] = 0.5; v[i][j] = 0.25; unew[i][j] = 0.5; vnew[i][j] = 0.25; } } } if (meshRank == P - 1) { for (int i = 1; i <= BLOPSIZE; ++i) { for (int j = 1; j <= BLOPSIZE; ++j) { u[i][j] = 0.5; v[i][j] = 0.25; unew[i][j] = 0.5; vnew[i][j] = 0.25; } } }*/ // ################################### // END timing INITIALIZATION endTimeInit = MPI_Wtime(); tInit = endTimeInit - startTimeInit; // ################################### // fprintf(stdout, "Starting iteration.\n" ); for (int iter = 0; iter < MAXITER; ++iter) // begin computation iterations { /* // ################################### // START timing COMMUNICATION startTimeComm = MPI_Wtime(); // ################################### // exchange data int err = 0; MPI_Status status; if (red) { // talk to north err = MPI_Sendrecv( &(u BORDER_NORTH), 1, border[NORTH], neighbour[NORTH], VERTICAL, &(u GHOST_NORTH), 1, ghost[NORTH], neighbour[NORTH], VERTICAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_NORTH), 1, border[NORTH], neighbour[NORTH], VERTICAL, &(v GHOST_NORTH), 1, ghost[NORTH], neighbour[NORTH], VERTICAL, processMesh, &status ); // talk to south err = MPI_Sendrecv( &(u BORDER_SOUTH), 1, border[SOUTH], neighbour[SOUTH], VERTICAL, &(u GHOST_SOUTH), 1, ghost[SOUTH], neighbour[SOUTH], VERTICAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_SOUTH), 1, border[SOUTH], neighbour[SOUTH], VERTICAL, &(v GHOST_SOUTH), 1, ghost[SOUTH], neighbour[SOUTH], VERTICAL, processMesh, &status ); // talk to east err = MPI_Sendrecv( &(u BORDER_EAST), n, border[EAST], neighbour[EAST], HORIZONTAL, &(u GHOST_EAST), n, ghost[EAST], neighbour[EAST], HORIZONTAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_EAST), n, border[EAST], neighbour[EAST], HORIZONTAL, &(v GHOST_EAST), n, ghost[EAST], neighbour[EAST], HORIZONTAL, processMesh, &status ); // talk to west err = MPI_Sendrecv( &(u BORDER_WEST), n, border[WEST], neighbour[WEST], HORIZONTAL, &(u GHOST_WEST), n, ghost[WEST], neighbour[WEST], HORIZONTAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_WEST), n, border[WEST], neighbour[WEST], HORIZONTAL, &(v GHOST_WEST), n, ghost[WEST], neighbour[WEST], HORIZONTAL, processMesh, &status ); } else // black { // talk to south err = MPI_Sendrecv( &(u BORDER_SOUTH), 1, border[SOUTH], neighbour[SOUTH], VERTICAL, &(u GHOST_SOUTH), 1, ghost[SOUTH], neighbour[SOUTH], VERTICAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_SOUTH), 1, border[SOUTH], neighbour[SOUTH], VERTICAL, &(v GHOST_SOUTH), 1, ghost[SOUTH], neighbour[SOUTH], VERTICAL, processMesh, &status ); // talk to north err = MPI_Sendrecv( &(u BORDER_NORTH), 1, border[NORTH], neighbour[NORTH], VERTICAL, &(u GHOST_NORTH), 1, ghost[NORTH], neighbour[NORTH], VERTICAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_NORTH), 1, border[NORTH], neighbour[NORTH], VERTICAL, &(v GHOST_NORTH), 1, ghost[NORTH], neighbour[NORTH], VERTICAL, processMesh, &status ); // talk to west err = MPI_Sendrecv( &(u BORDER_WEST), n, border[WEST], neighbour[WEST], HORIZONTAL, &(u GHOST_WEST), n, ghost[WEST], neighbour[WEST], HORIZONTAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_WEST), n, border[WEST], neighbour[WEST], HORIZONTAL, &(v GHOST_WEST), n, ghost[WEST], neighbour[WEST], HORIZONTAL, processMesh, &status ); // talk to east err = MPI_Sendrecv( &(u BORDER_EAST), n, border[EAST], neighbour[EAST], HORIZONTAL, &(u GHOST_EAST), n, ghost[EAST], neighbour[EAST], HORIZONTAL, processMesh, &status ); err = MPI_Sendrecv( &(v BORDER_EAST), n, border[EAST], neighbour[EAST], HORIZONTAL, &(v GHOST_EAST), n, ghost[EAST], neighbour[EAST], HORIZONTAL, processMesh, &status ); } // end data exchange // ################################### // END timing COMMUNICATION endTimeComm = MPI_Wtime(); tComm += (endTimeComm - startTimeComm); // add to total // ################################### */ // ################################### // START timing CALCULATION startTimeCalc = MPI_Wtime(); // ################################### /*int firstelem = 0; int lastelem = n - 1;*/ // compute inner for (int i = 1; i <= n - 2; ++i) { for (int j = 1; j <= n - 2; ++j) { unew[i][j] = u[i][j] + ru * ( u[i - 1][j] - (4 * u[i][j]) + u[i + 1][j] + u[i][j - 1] + u[i][j + 1] ) + ht * ( -u[i][j] * v[i][j] * v[i][j] + F * (1.0 - u[i][j]) ); vnew[i][j] = v[i][j] + rv * ( v[i - 1][j] - (4 * v[i][j]) + v[i + 1][j] + v[i][j - 1] + v[i][j + 1] ) + ht * ( u[i][j] * v[i][j] * v[i][j] - (F + K) * v[i][j] ); } } // compute non-corner top row: i = 0, i-1 = n-1 int topRow = 0; int topRowWrap = n - 1; for (int j = 1; j <= n - 2; ++j) { unew[topRow][j] = u[topRow][j] + ru * ( u[topRowWrap][j] - (4 * u[topRow][j]) + u[topRow + 1][j] + u[topRow][j - 1] + u[topRow][j + 1] ) + ht * ( -u[topRow][j] * v[topRow][j] * v[topRow][j] + F * (1.0 - u[topRow][j]) ); vnew[topRow][j] = v[topRow][j] + rv * ( v[topRowWrap][j] - (4 * v[topRow][j]) + v[topRow + 1][j] + v[topRow][j - 1] + v[topRow][j + 1] ) + ht * ( u[topRow][j] * v[topRow][j] * v[topRow][j] - (F + K) * v[topRow][j] ); } // compute non-corner bottom row: i = n-1, i+1 = 0 int botRow = n - 1; int botRowWrap = 0; for (int j = 1; j <= n - 2; ++j) { unew[botRow][j] = u[botRow][j] + ru * ( u[botRow - 1][j] - (4 * u[botRow][j]) + u[botRowWrap][j] + u[botRow][j - 1] + u[botRow][j + 1] ) + ht * ( -u[botRow][j] * v[botRow][j] * v[botRow][j] + F * (1.0 - u[botRow][j]) ); vnew[botRow][j] = v[botRow][j] + rv * ( v[botRow - 1][j] - (4 * v[botRow][j]) + v[botRowWrap][j] + v[botRow][j - 1] + v[botRow][j + 1] ) + ht * ( u[botRow][j] * v[botRow][j] * v[botRow][j] - (F + K) * v[botRow][j] ); } // compute non-corner left column: j = 0, j-1 = n-1 int leftCol = 0; int leftColWrap = n - 1; for (int i = 1; i <= n - 2; ++i) { unew[i][leftCol] = u[i][leftCol] + ru * ( u[i - 1][leftCol] - (4 * u[i][leftCol]) + u[i + 1][leftCol] + u[i][leftColWrap] + u[i][leftCol + 1] ) + ht * ( -u[i][leftCol] * v[i][leftCol] * v[i][leftCol] + F * (1.0 - u[i][leftCol]) ); vnew[i][leftCol] = v[i][leftCol] + rv * ( v[i - 1][leftCol] - (4 * v[i][leftCol]) + v[i + 1][leftCol] + v[i][leftColWrap] + v[i][leftCol + 1] ) + ht * ( u[i][leftCol] * v[i][leftCol] * v[i][leftCol] - (F + K) * v[i][leftCol] ); } // compute non-corner right column: j = n-1, j+1 = 0 int rightCol = n - 1; int rightColWrap = 0; for (int i = 1; i <= n - 2; ++i) { unew[i][rightCol] = u[i][rightCol] + ru * ( u[i - 1][rightCol] - (4 * u[i][rightCol]) + u[i + 1][rightCol] + u[i][rightCol - 1] + u[i][rightColWrap] ) + ht * ( -u[i][rightCol] * v[i][rightCol] * v[i][rightCol] + F * (1.0 - u[i][rightCol]) ); vnew[i][rightCol] = v[i][rightCol] + rv * ( v[i - 1][rightCol] - (4 * v[i][rightCol]) + v[i + 1][rightCol] + v[i][rightCol - 1] + v[i][rightColWrap] ) + ht * ( u[i][rightCol] * v[i][rightCol] * v[i][rightCol] - (F + K) * v[i][rightCol] ); } // compute upper left corner // i = 0, j = 0, i-1 = n-1, j-1 = n-1 unew[0][0] = u[0][0] + ru * ( u[n - 1][0] - (4 * u[0][0]) + u[0 + 1][0] + u[0][n - 1] + u[0][0 + 1] ) + ht * ( -u[0][0] * v[0][0] * v[0][0] + F * (1.0 - u[0][0]) ); vnew[0][0] = v[0][0] + rv * ( v[n - 1][0] - (4 * v[0][0]) + v[0 + 1][0] + v[0][n - 1] + v[0][0 + 1] ) + ht * ( u[0][0] * v[0][0] * v[0][0] - (F + K) * v[0][0] ); // compute upper right corner // i = 0, j = n-1, i-1 = n-1, j+1 = 0 unew[0][rightCol] = u[0][rightCol] + ru * ( u[n - 1][rightCol] - (4 * u[0][rightCol]) + u[0 + 1][rightCol] + u[0][rightCol - 1] + u[0][0] ) + ht * ( -u[0][rightCol] * v[0][rightCol] * v[0][rightCol] + F * (1.0 - u[0][rightCol]) ); vnew[0][rightCol] = v[0][rightCol] + rv * ( v[n - 1][rightCol] - (4 * v[0][rightCol]) + v[0 + 1][rightCol] + v[0][rightCol - 1] + v[0][0] ) + ht * ( u[0][rightCol] * v[0][rightCol] * v[0][rightCol] - (F + K) * v[0][rightCol] ); // compute lower right corner // i = n-1, i+1 = 0, j = n-1, j+1 = 0 unew[botRow][rightCol] = u[botRow][rightCol] + ru * ( u[botRow - 1][rightCol] - (4 * u[botRow][rightCol]) + u[0][rightCol] + u[botRow][rightCol - 1] + u[botRow][0] ) + ht * ( -u[botRow][rightCol] * v[botRow][rightCol] * v[botRow][rightCol] + F * (1.0 - u[botRow][rightCol]) ); vnew[botRow][rightCol] = v[botRow][rightCol] + rv * ( v[botRow - 1][rightCol] - (4 * v[botRow][rightCol]) + v[0][rightCol] + v[botRow][rightCol - 1] + v[botRow][0] ) + ht * ( u[botRow][rightCol] * v[botRow][rightCol] * v[botRow][rightCol] - (F + K) * v[botRow][rightCol] ); // compute lower left corner // i = n-1, i+1 = 0, j = 0, j-1 = n-1 unew[botRow][0] = u[botRow][0] + ru * ( u[botRow - 1][0] - (4 * u[botRow][0]) + u[0][0] + u[botRow][0 - 1] + u[botRow][n - 1] ) + ht * ( -u[botRow][0] * v[botRow][0] * v[botRow][0] + F * (1.0 - u[botRow][0]) ); vnew[botRow][0] = v[botRow][0] + rv * ( v[botRow - 1][0] - (4 * v[botRow][0]) + v[0][0] + v[botRow][0 - 1] + v[botRow][n - 1] ) + ht * ( u[botRow][0] * v[botRow][0] * v[botRow][0] - (F + K) * v[botRow][0] ); // swap pointers double **tmpnew; tmpnew = unew; unew = u; u = tmpnew; // u = unew tmpnew = vnew; vnew = v; v = tmpnew; // u = unew // ################################### // END timing CALCULATION endTimeCalc = MPI_Wtime(); tCalc += (endTimeCalc - startTimeCalc); // ################################### } // end computation iteration printf("Sequential t_init: %f, t_comm: %f, t_calc: %f.\n", tInit, 0.0, tCalc); //========================================================================= // Output to file //========================================================================= FILE *fp; // TODO: concatenate all filenames with a unique run ID size_t firstelem = 0; // we use all domain, no ghosts size_t lastelem = n - 1; // write u fp = fopen("u_seq.txt", "w"); // open new file to write for (int i = firstelem; i <= lastelem; ++i) { for (int j = firstelem; j <= lastelem; ++j) { fprintf(fp, "%.15f ", u[i][j]); } fprintf(fp, "\n"); } fclose(fp); // write v fp = fopen("v_seq.txt", "w"); // open new file to write for (int i = firstelem; i <= lastelem; ++i) { for (int j = firstelem; j <= lastelem; ++j) { fprintf(fp, "%.15f ", v[i][j]); } fprintf(fp, "\n"); } fclose(fp); printf("Seq wrote [%d %d] elements.\n", n, n); // TODO: write timing info //========================================================================= // Cleanup //========================================================================= // TODO: right now there is no proper cleanup, fix this // free the ghost spirits /*for (int i = 0; i < NDIRS; ++i) MPI_Type_free(&ghost[i]);*/ MPI_Finalize(); // free local array /*free(u[0]); free(u);*/ return 0; }