///////////////////////////////////////////////////////////////////////////////
///
/// \file main.cpp
/// \author John Yawney
/// \version July 17, 2014
///
///
/// Test case to demonstrate PETSc local and global vector definitions
/// under the DMDA data structures. Uses global vectors to compute the
/// dot product of 2 vectors; one defined as all 1s the other as all 2s.
/// As a second test, we exchange halo elements of the first vector and
/// define a new derivative vector that stores the FD derivatives of
/// this first vector.
/// Test outputs displayed: dot product, derivative vector min & max
///
#include "petsc.h"
#include
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
// Initialize Petsc
PetscInitialize(&argc, &argv, 0, 0);
// Push an abort error handler
PetscPushErrorHandler(&PetscAbortErrorHandler, PETSC_NULL);
//////////////////////////////////////////////////////////////////////////
// DMDA Environment
//////////////////////////////////////////////////////////////////////////
// Boundary Conditions
DMDABoundaryType bx = DMDA_BOUNDARY_GHOSTED;
DMDABoundaryType by = DMDA_BOUNDARY_NONE; // no boundary points since nGx = 1
DMDABoundaryType bz = DMDA_BOUNDARY_NONE; // no boundary points since nGz = 1
//////////////////////////////////////////////////////////////////////////
// Stencil type
DMDAStencilType st = DMDA_STENCIL_BOX;
//DMDAStencilType st = DMDA_STENCIL_STAR;
//////////////////////////////////////////////////////////////////////////
// Number of global elements
int nGx = 256;
int nGy = 1;
int nGz = 1;
//////////////////////////////////////////////////////////////////////////
// Number of processors in each direction
int nSize;
MPI_Comm_size(MPI_COMM_WORLD, &nSize);
int nPx = nSize;
int nPy = 1;
int nPz = 1;
if (nGx % nPx != 0) {
PetscPrintf(MPI_COMM_WORLD, "\nERROR: Number of processors in x must divide number of elements in x.\n\n");
MPI_Barrier(MPI_COMM_WORLD);
return -1;
}
//////////////////////////////////////////////////////////////////////////
// Number of halo elements
int nh = 3;
//////////////////////////////////////////////////////////////////////////
// Number of degrees of freedom/layers
int dof = 1;
//////////////////////////////////////////////////////////////////////////
// Define 3D distributed array environment
DM m_3da;
DMDACreate3d(MPI_COMM_WORLD, bx, by, bz, st,
nGx, nGy, nGz, nPx, nPy, nPz,
dof, nh, PETSC_NULL, PETSC_NULL, PETSC_NULL, &m_3da);
//////////////////////////////////////////////////////////////////////////
// Initialize global temp vectors for exchange
Vec m_gTempA, m_gTempB;
DMCreateGlobalVector(m_3da, &m_gTempA);
DMCreateGlobalVector(m_3da, &m_gTempB);
//////////////////////////////////////////////////////////////////////////
// Initialize local vectors (with halo elements accessible)
Vec m_vecA, m_vecB;
DMCreateLocalVector(m_3da, &m_vecA);
VecZeroEntries(m_vecA);
VecDuplicate(m_vecA, &m_vecB);
//////////////////////////////////////////////////////////////////////////
// Store some values in the interior of each processor's data
int i, si, ei;
DMDAGetCorners(m_3da, &si, 0, 0, &ei, 0, 0);
// Access the elements of the local arrays as C++ multi-dim. array structures
double ***pvecA, ***pvecB;
DMDAVecGetArray(m_3da, m_vecA, &pvecA);
DMDAVecGetArray(m_3da, m_vecB, &pvecB);
// Hold k and j constant at 0 since nGy = nGz = 0
int k, j;
k = j = 0;
for (i = si; i < si+ei; i++) {
//////////////////////////////////////////////////////////////////////////
// NOTE: Adding in some random values.
// These values will be defined by the problem of interest.
pvecA[k][j][i] = 1.0;
pvecB[k][j][i] = 2.0;
}
// Release pointers (and memory)
DMDAVecRestoreArray(m_3da, m_vecA, &pvecA);
DMDAVecRestoreArray(m_3da, m_vecB, &pvecB);
// Moves data from local vectors to global vectors
DMLocalToGlobalBegin(m_3da, m_vecA, INSERT_VALUES, m_gTempA);
DMLocalToGlobalEnd(m_3da, m_vecA, INSERT_VALUES, m_gTempA);
DMLocalToGlobalBegin(m_3da, m_vecB, INSERT_VALUES, m_gTempB);
DMLocalToGlobalEnd(m_3da, m_vecB, INSERT_VALUES, m_gTempB);
// Assembly for use in PETSc functions
VecAssemblyBegin(m_gTempA);
VecAssemblyEnd(m_gTempA);
VecAssemblyBegin(m_gTempB);
VecAssemblyEnd(m_gTempB);
// Take dot product
double dDotProduct;
VecDot(m_gTempA, m_gTempB, &dDotProduct);
// Output dot product to check
PetscPrintf(MPI_COMM_WORLD, "Dot Product Check... \n\n");
PetscPrintf(MPI_COMM_WORLD, "\tDot Product (should be 2*%d) = %f\n\n", nGx, dDotProduct);
PetscPrintf(MPI_COMM_WORLD, "Done.\n\n");
//////////////////////////////////////////////////////////////////////////
// Computing centered derivative / first-order at boundaries
//////////////////////////////////////////////////////////////////////////
// In order to compute derivatives we need to move the data from the
// global vectors to the local. This will ensure that the ghost cells
// are updated correctly.
// Moves data from global vectors to local vectors
DMGlobalToLocalBegin(m_3da, m_gTempA, INSERT_VALUES, m_vecA);
DMGlobalToLocalEnd(m_3da, m_gTempA, INSERT_VALUES, m_vecA);
// Define grid spacing for derivatives
double dLx = 1.0;
double dDeltaX = dLx / static_cast(nGx);
// Define a new global vector to store the derivatives.
// Alternatively, we could define a local vector if we care about
// accessing the ghost cells of the derivative vector.
Vec m_vecDx;
VecDuplicate(m_gTempA, &m_vecDx);
// Alternative (local vector): VecDuplicate(m_vecA, &m_vecDx);
// Access the data in the vectors by using pointers
double ***pDx;
DMDAVecGetArray(m_3da, m_vecA, &pvecA);
DMDAVecGetArray(m_3da, m_vecDx, &pDx);
//////////////////////////////////////////////////////////////////////////
// NOTE: It is easy to check boundaries in PETSc DMDAs since the local i
// values refer to global positioning. Therefore, we only need to
// check if i == 0 or i == nGx-1 in order to determine if we are
// at a boundary.
for (i = si; i < si+ei; i++) {
if (i == 0) {
pDx[k][j][i] = (pvecA[k][j][i+1] - pvecA[k][j][i]) / dDeltaX;
} else if (i == nGx-1) {
pDx[k][j][i] = (pvecA[k][j][i] - pvecA[k][j][i-1]) / dDeltaX;
} else {
pDx[k][j][i] = 0.5 * (pvecA[k][j][i+1] - pvecA[k][j][i-1]) / dDeltaX;
}
}
// Release pointers (and memory)
DMDAVecRestoreArray(m_3da, m_vecA, &pvecA);
DMDAVecRestoreArray(m_3da, m_vecDx, &pDx);
// Compute min and max of derivative vector (should be 0 if halo defined correctly)
double dMin, dMax;
VecMin(m_vecDx, PETSC_NULL, &dMin);
VecMax(m_vecDx, PETSC_NULL, &dMax);
// Derivative check
PetscPrintf(MPI_COMM_WORLD, "Derivative Check... \n\n");
PetscPrintf(MPI_COMM_WORLD, "\tMin (should be 0; if halos not defined properly, would be -1.0/dDeltaX) = %f\n", dMin);
PetscPrintf(MPI_COMM_WORLD, "\tMax (should be 0; if halos not defined properly, would be +1.0/dDeltaX) = %f\n\n", dMax);
PetscPrintf(MPI_COMM_WORLD, "Done.\n");
// Destroy temporary PETSc structures
// Destroy DMDA Environment variable
DMDestroy(&m_3da);
// Destroy vectors
VecDestroy(&m_vecA);
VecDestroy(&m_vecB);
VecDestroy(&m_gTempA);
VecDestroy(&m_gTempB);
VecDestroy(&m_vecDx);
// Finalize Petsc
PetscFinalize();
}