Add support for boundary augmentation for MPI optimization

This commit is contained in:
Oliver Ruebel 2019-09-06 17:48:45 -07:00 committed by Allison Vacanti
parent 2a1e1dcfd9
commit 1d9f6b37cf
15 changed files with 681 additions and 194 deletions

@ -138,10 +138,10 @@ public:
std::string getOption(const std::string& option) const
{
auto index = this->findOption(option);
std::size_t index = static_cast<std::size_t>(this->findOption(option));
if (index >= 0)
{
std::string val = this->mCLOptions[static_cast<std::size_t>(index)];
std::string val = this->mCLOptions[index];
auto valPos = val.find("=");
if (valPos)
{
@ -201,19 +201,21 @@ int main(int argc, char* argv[])
ParseCL parser;
parser.parse(argc, argv);
std::string filename = parser.getOptions().back();
bool computeRegularStructure = true;
unsigned int computeRegularStructure = 1; // 1=fully augmented
bool useMarchingCubes = false;
bool computeBranchDecomposition = true;
bool printContourTree = false;
if (parser.hasOption("--augmentTree"))
computeRegularStructure = std::stoi(parser.getOption("--augmentTree"));
computeRegularStructure =
static_cast<unsigned int>(std::stoi(parser.getOption("--augmentTree")));
if (parser.hasOption("--mc"))
useMarchingCubes = true;
if (parser.hasOption("--printCT"))
printContourTree = true;
if (parser.hasOption("--branchDecomp"))
computeBranchDecomposition = std::stoi(parser.getOption("--branchDecomp"));
if (computeBranchDecomposition && (!computeRegularStructure))
// We need the fully augmented tree to compute the branch decomposition
if (computeBranchDecomposition && (computeRegularStructure != 1))
{
std::cout << "Regular structure is required for branch decomposition."
" Disabling branch decomposition"
@ -318,7 +320,12 @@ int main(int argc, char* argv[])
std::cout << "--mc Use marching cubes interpolation for contour tree calculation. "
"(Default=False)"
<< std::endl;
std::cout << "--augmentTree Compute the augmented contour tree. (Default=True)"
std::cout << "--augmentTree 1 = compute the fully augmented contour tree (Default)"
<< std::endl;
std::cout << " 2 = compute the boundary augmented contour tree " << std::endl;
std::cout << " 0 = no augmentation. NOTE: When using MPI, local ranks use"
<< std::endl;
std::cout << " boundary augmentation to support parallel merge of blocks"
<< std::endl;
std::cout << "--branchDecomp Compute the volume branch decomposition for the contour tree. "
"Requires --augmentTree (Default=True)"
@ -358,6 +365,7 @@ int main(int argc, char* argv[])
{
std::cout << "Settings:" << std::endl;
std::cout << " filename=" << filename << std::endl;
std::cout << " device=" << device << std::endl;
std::cout << " mc=" << useMarchingCubes << std::endl;
std::cout << " augmentTree=" << computeRegularStructure << std::endl;
std::cout << " branchDecomp=" << computeBranchDecomposition << std::endl;
@ -367,7 +375,7 @@ int main(int argc, char* argv[])
#ifdef ENABLE_SET_NUM_THREADS
std::cout << " numThreads=" << numThreads << std::endl;
#endif
std::cout << " computeIsovalyes=" << (numLevels > 0) << std::endl;
std::cout << " computeIsovalues=" << (numLevels > 0) << std::endl;
if (numLevels > 0)
{
std::cout << " levels=" << numLevels << std::endl;
@ -450,7 +458,7 @@ int main(int argc, char* argv[])
// Print the mesh metadata
if (rank == 0)
{
std::cout << " Number of dimensions: " << nDims << std::endl;
std::cout << "Number of dimensions: " << nDims << std::endl;
std::cout << "Number of mesh vertices: " << nVertices << std::endl;
}
// Check the the number of dimensiosn is either 2D or 3D
@ -543,7 +551,7 @@ int main(int argc, char* argv[])
{
if (rank == 0)
{
std::cout << "Number of ranks too large for data. Use " << lastDimSize / 2
std::cout << "Number of ranks to large for data. Use " << lastDimSize / 2
<< "or fewer ranks" << std::endl;
}
MPI_Finalize();
@ -555,11 +563,6 @@ int main(int argc, char* argv[])
nDims == 2 ? static_cast<vtkm::Id>(dims[0]) : static_cast<vtkm::Id>((dims[0] * dims[1]));
vtkm::Id blockNumValues = blockSize * blockSliceSize;
/*//Print the input values
for(auto i = values.begin(); i != values.end(); ++i)
std::cout << *i << ' ';
std::cout<<std::endl;*/
vtkm::Id startBlock = blocksPerRank * rank;
vtkm::Id endBlock = startBlock + blocksPerRank;
for (vtkm::Id blockIndex = startBlock; blockIndex < endBlock; ++blockIndex)
@ -578,7 +581,6 @@ int main(int argc, char* argv[])
vtkm::Id currBlockSize = (vtkm::Id)((blockEnd - blockStart) / blockSliceSize);
vtkm::cont::DataSet ds;
//std::cout<<"Block index: "<<blockIndex<<" start="<<blockStart<<" end="<<blockEnd<<" (rank="<<rank<<")"<<std::endl;
// 2D data
if (nDims == 2)
@ -607,10 +609,6 @@ int main(int argc, char* argv[])
vtkm::Vec<ValueType, 3> spacing(1, 1, 1);
ds = dsb.Create(vdims, origin, spacing);
//localBlockIndicesPortal.Set(localBlockIndex, vtkm::Id3(blockIndex, 0, 0));
//localBlockOriginsPortal.Set(localBlockIndex, vtkm::Id3((blockStart / blockSliceSize), 0, 0));
//localBlockSizesPortal.Set(localBlockIndex, vtkm::Id3(currBlockSize, dims[1], dims[2]));
localBlockIndicesPortal.Set(localBlockIndex, vtkm::Id3(0, 0, blockIndex));
localBlockOriginsPortal.Set(localBlockIndex,
vtkm::Id3(0, 0, (blockStart / blockSliceSize)));

@ -80,7 +80,7 @@ class ContourTreePPP2 : public vtkm::filter::FilterCell<ContourTreePPP2>
public:
using SupportedTypes = vtkm::TypeListTagScalarAll;
VTKM_CONT
ContourTreePPP2(bool useMarchingCubes = false, bool computeRegularStructure = true);
ContourTreePPP2(bool useMarchingCubes = false, unsigned int computeRegularStructure = 1);
// Define the spatial decomposition of the data in case we run in parallel with a multi-block dataset
VTKM_CONT
@ -131,8 +131,8 @@ private:
vtkm::Id nslices) const;
bool UseMarchingCubes;
// 0=no augmentation, 1=full augmentation
bool ComputeRegularStructure;
// 0=no augmentation, 1=full augmentation, 2=boundary augmentation
unsigned int ComputeRegularStructure;
// Store timings about the contour tree computation
std::vector<std::pair<std::string, vtkm::Float64>> Timings;

@ -93,9 +93,10 @@ struct ContourTreeBlockData
vtkm::Id maxNeighbours;
// Block metadata
vtkm::Id3 blockOrigin; // Origin of the data block
vtkm::Id3 blockSize; // Extends of the data block
vtkm::Id3 globalSize; // Extends of the global mesh
vtkm::Id3 blockOrigin; // Origin of the data block
vtkm::Id3 blockSize; // Extends of the data block
vtkm::Id3 globalSize; // Extends of the global mesh
unsigned int computeRegularStructure; // pass through augmentation setting
};
namespace vtkmdiy
@ -117,6 +118,7 @@ struct Serialization<ContourTreeBlockData<FieldType>>
vtkmdiy::save(bb, block.blockOrigin);
vtkmdiy::save(bb, block.blockSize);
vtkmdiy::save(bb, block.globalSize);
vtkmdiy::save(bb, block.computeRegularStructure);
}
static void load(vtkmdiy::BinaryBuffer& bb, ContourTreeBlockData<FieldType>& block)
@ -131,6 +133,7 @@ struct Serialization<ContourTreeBlockData<FieldType>>
vtkmdiy::load(bb, block.blockOrigin);
vtkmdiy::load(bb, block.blockSize);
vtkmdiy::load(bb, block.globalSize);
vtkmdiy::load(bb, block.computeRegularStructure);
}
};
@ -253,24 +256,18 @@ public:
mLocalSortOrders.clear();
}
inline static void GetGlobalMeshIndex(
vtkm::worklet::contourtree_augmented::IdArrayType& globalMeshIndex,
const vtkm::worklet::contourtree_augmented::IdArrayType& sortOrder,
vtkm::Id startRow,
vtkm::Id startCol,
vtkm::Id startSlice,
vtkm::Id numRows,
vtkm::Id numCols,
vtkm::Id totalNumRows,
vtkm::Id totalNumCols)
inline static vtkm::Bounds getGlobalBounds(const vtkm::cont::PartitionedDataSet& input)
{
auto transformedIndex =
vtkm::cont::ArrayHandleTransform<vtkm::worklet::contourtree_augmented::IdArrayType,
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabler>(
sortOrder,
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabler(
startRow, startCol, startSlice, numRows, numCols, totalNumRows, totalNumCols));
vtkm::cont::Algorithm::Copy(transformedIndex, globalMeshIndex);
// Get the spatial bounds of a multi -block data set
vtkm::Bounds bounds = vtkm::cont::BoundsGlobalCompute(input);
return bounds;
}
inline static vtkm::Bounds getLocalBounds(const vtkm::cont::PartitionedDataSet& input)
{
// Get the spatial bounds of a multi -block data set
vtkm::Bounds bounds = vtkm::cont::BoundsCompute(input);
return bounds;
}
inline vtkm::Id getLocalNumberOfBlocks() const
@ -288,10 +285,17 @@ public:
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
vtkm::Id localSize = input.GetNumberOfPartitions();
vtkm::Id globalSize = 0;
#ifdef VTKM_ENABLE_MPI
vtkmdiy::mpi::all_reduce(comm, localSize, globalSize, std::plus<vtkm::Id>{});
#else
globalSize = localSize;
#endif
return globalSize;
}
// Used to compute the local contour tree mesh in after DoExecute. I.e., the function is
// used in PostExecute to construct the initial set of local ContourTreeMesh blocks for
// DIY. Subsequent construction of updated ContourTreeMeshes is handled separately.
template <typename T>
inline static vtkm::worklet::contourtree_augmented::ContourTreeMesh<T>*
computeLocalContourTreeMesh(const vtkm::Id3 localBlockOrigin,
@ -299,7 +303,8 @@ public:
const vtkm::Id3 globalSize,
const vtkm::cont::ArrayHandle<T>& field,
const vtkm::worklet::contourtree_augmented::ContourTree& contourTree,
const vtkm::worklet::contourtree_augmented::IdArrayType& sortOrder)
const vtkm::worklet::contourtree_augmented::IdArrayType& sortOrder,
unsigned int computeRegularStructure)
{
vtkm::Id startRow = localBlockOrigin[0];
@ -309,35 +314,49 @@ public:
vtkm::Id numCols = localBlockSize[1];
vtkm::Id totalNumRows = globalSize[0];
vtkm::Id totalNumCols = globalSize[1];
// compute the global mesh index
vtkm::worklet::contourtree_augmented::IdArrayType localGlobalMeshIndex;
MultiBlockContourTreeHelper::GetGlobalMeshIndex(localGlobalMeshIndex,
sortOrder,
startRow,
startCol,
startSlice,
numRows,
numCols,
totalNumRows,
totalNumCols);
// Compute the local contour tree mesh
auto localContourTreeMesh = new vtkm::worklet::contourtree_augmented::ContourTreeMesh<T>(
contourTree.arcs, sortOrder, field, localGlobalMeshIndex);
return localContourTreeMesh;
}
template <typename FieldType>
inline static vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>*
computeMergedContourTreeMesh(
const vtkm::worklet::contourtree_augmented::IdArrayType& contourTreeArcs,
const vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& contourTreeMesh)
{
auto localContourTreeMesh =
new vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>(contourTreeArcs,
contourTreeMesh);
return localContourTreeMesh;
// compute the global mesh index and initalize the local contour tree mesh
if (computeRegularStructure == 1)
{
// Compute the global mesh index
vtkm::worklet::contourtree_augmented::IdArrayType localGlobalMeshIndex;
auto transformedIndex = vtkm::cont::ArrayHandleTransform<
vtkm::worklet::contourtree_augmented::IdArrayType,
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabler>(
sortOrder,
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabler(
startRow, startCol, startSlice, numRows, numCols, totalNumRows, totalNumCols));
vtkm::cont::Algorithm::Copy(transformedIndex, localGlobalMeshIndex);
// Compute the local contour tree mesh
auto localContourTreeMesh = new vtkm::worklet::contourtree_augmented::ContourTreeMesh<T>(
contourTree.arcs, sortOrder, field, localGlobalMeshIndex);
return localContourTreeMesh;
}
else if (computeRegularStructure == 2)
{
// Compute the global mesh index for the partially augmented contour tree. I.e., here we
// don't need the global mesh index for all nodes, but only for the augmented nodes from the
// tree. We, hence, permute the sortOrder by contourTree.augmentednodes and then compute the
// globalMeshIndex by tranforming those indices with our IdRelabler
vtkm::worklet::contourtree_augmented::IdArrayType localGlobalMeshIndex;
vtkm::cont::ArrayHandlePermutation<vtkm::worklet::contourtree_augmented::IdArrayType,
vtkm::worklet::contourtree_augmented::IdArrayType>
permutedSortOrder(contourTree.augmentnodes, sortOrder);
auto transformedIndex = vtkm::cont::make_ArrayHandleTransform(
permutedSortOrder,
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabler(
startRow, startCol, startSlice, numRows, numCols, totalNumRows, totalNumCols));
vtkm::cont::Algorithm::Copy(transformedIndex, localGlobalMeshIndex);
// Compute the local contour tree mesh
auto localContourTreeMesh = new vtkm::worklet::contourtree_augmented::ContourTreeMesh<T>(
contourTree.augmentnodes, contourTree.augmentarcs, sortOrder, field, localGlobalMeshIndex);
return localContourTreeMesh;
}
else
{
// We should not be able to get here
throw vtkm::cont::ErrorFilterExecution(
"Parallel contour tree requires at least parial boundary augmentation");
}
}
SpatialDecomposition mSpatialDecomposition;
@ -380,7 +399,6 @@ void merge_block_functor(
// Here we do the deque first before the send due to the way the iteration is handled in DIY, i.e., in each iteration
// A block needs to first collect the data from its neighours and then send the combined block to its neighbours
// for the next iteration.
// 1. dequeue the block and compute the new contour tree and contour tree mesh for the block if we have the hight GID
std::vector<int> incoming;
rp.incoming(incoming);
@ -409,7 +427,6 @@ void merge_block_functor(
contourTreeMeshOut.neighbours = block->neighbours;
contourTreeMeshOut.firstNeighbour = block->firstNeighbour;
contourTreeMeshOut.maxNeighbours = block->maxNeighbours;
// Merge the two contour tree meshes
vtkm::cont::TryExecute(MergeFunctor{}, contourTreeMeshIn, contourTreeMeshOut);
@ -455,21 +472,43 @@ void merge_block_functor(
vtkm::worklet::contourtree_augmented::IdArrayType currSortOrder;
vtkm::worklet::ContourTreePPP2 worklet;
vtkm::cont::ArrayHandle<FieldType> currField;
// Run the contour tree compute. NOTE, the first paramerter is unused here. We need to provide
// something for the field to keep the API happy
vtkm::Id3 maxIdx(currBlockOrigin[0] + currBlockSize[0] - 1,
currBlockOrigin[1] + currBlockSize[1] - 1,
currBlockOrigin[2] + currBlockSize[2] - 1);
auto meshBoundaryExecObj =
contourTreeMeshOut.GetMeshBoundaryExecutionObject(globalSize[0], // totalNRows
globalSize[1], // totalNCols
currBlockOrigin, // minIdx
maxIdx // maxIdx
);
worklet.Run(
contourTreeMeshOut.sortedValues,
contourTreeMeshOut.sortedValues, // Unused param. Provide something to keep the API happy
contourTreeMeshOut,
currTimings,
currContourTree,
currSortOrder,
currNumIterations,
1 // TODO eventually replace with boundary augmentation but for now compute the full regular structure
);
auto newContourTreeMesh =
vtkm::filter::detail::MultiBlockContourTreeHelper::computeMergedContourTreeMesh(
block->computeRegularStructure,
meshBoundaryExecObj);
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>* newContourTreeMesh = 0;
if (block->computeRegularStructure == 1)
{
// If we have the fully augmented contour tree
newContourTreeMesh = new vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>(
currContourTree.arcs, contourTreeMeshOut);
}
else if (block->computeRegularStructure == 2)
{
// If we have the partially augmented (e.g., boundary augmented) contour tree
newContourTreeMesh = new vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>(
currContourTree.augmentnodes, currContourTree.augmentarcs, contourTreeMeshOut);
}
else
{
// We should not be able to get here
throw vtkm::cont::ErrorFilterExecution(
"Parallel contour tree requires at least parial boundary augmentation");
}
// Copy the data from newContourTreeMesh into block
block->nVertices = newContourTreeMesh->nVertices;
@ -482,13 +521,11 @@ void merge_block_functor(
block->blockOrigin = currBlockOrigin;
block->blockSize = currBlockSize;
block->globalSize = globalSize;
// TODO delete newContourTreeMesh
// TODO Clean up the pointers from the local data blocks from the previous iteration
}
}
}
// Send our current block (which is either our original block or the one we just combined from the ones we received) to our next neighbour.
// Once a rank has send his block (either in its orignal or merged form) it is done with the reduce
for (int cc = 0; cc < rp.out_link().size(); ++cc)
@ -506,7 +543,7 @@ void merge_block_functor(
//-----------------------------------------------------------------------------
ContourTreePPP2::ContourTreePPP2(bool useMarchingCubes, bool computeRegularStructure)
ContourTreePPP2::ContourTreePPP2(bool useMarchingCubes, unsigned int computeRegularStructure)
: vtkm::filter::FilterCell<ContourTreePPP2>()
, UseMarchingCubes(useMarchingCubes)
, ComputeRegularStructure(computeRegularStructure)
@ -579,9 +616,23 @@ vtkm::cont::DataSet ContourTreePPP2::DoExecute(const vtkm::cont::DataSet& input,
const auto& cells = input.GetCellSet();
vtkm::filter::ApplyPolicyCellSet(cells, policy)
.CastAndCall(GetRowsColsSlices(), nRows, nCols, nSlices);
// TODO blockIndex=0 --- This needs to change if we have multiple blocks per MPI rank and DoExecute is called for multiple blocks
// TODO blockIndex needs to change if we have multiple blocks per MPI rank and DoExecute is called for multiple blocks
std::size_t blockIndex = 0;
// Determine if and what augmentation we need to do
unsigned int compRegularStruct = this->ComputeRegularStructure;
// When running in parallel we need to at least augment with the boundary vertices
if (compRegularStruct == 0)
{
if (this->MultiBlockTreeHelper)
{
if (this->MultiBlockTreeHelper->getGlobalNumberOfBlocks() > 1)
{
compRegularStruct = 2; // Compute boundary augmentation
}
}
}
// Run the worklet
worklet.Run(field,
this->Timings,
@ -594,7 +645,7 @@ vtkm::cont::DataSet ContourTreePPP2::DoExecute(const vtkm::cont::DataSet& input,
nCols,
nSlices,
this->UseMarchingCubes,
this->ComputeRegularStructure);
compRegularStruct);
// Update the total timings
vtkm::Float64 totalTimeWorklet = 0;
@ -683,6 +734,9 @@ VTKM_CONT void ContourTreePPP2::DoPostExecute(
localDataBlocks.resize(static_cast<size_t>(input.GetNumberOfPartitions()));
std::vector<vtkmdiy::Link*> localLinks; // dummy links needed to make DIY happy
localLinks.resize(static_cast<size_t>(input.GetNumberOfPartitions()));
// We need to augment at least with the boundary vertices when running in parallel, even if the user requested at the end only the unaugmented contour tree
unsigned int compRegularStruct =
(this->ComputeRegularStructure > 0) ? this->ComputeRegularStructure : 2;
for (std::size_t bi = 0; bi < static_cast<std::size_t>(input.GetNumberOfPartitions()); bi++)
{
// create the local contour tree mesh
@ -702,7 +756,8 @@ VTKM_CONT void ContourTreePPP2::DoPostExecute(
this->MultiBlockTreeHelper->mSpatialDecomposition.mGlobalSize,
fieldData,
MultiBlockTreeHelper->mLocalContourTrees[bi],
MultiBlockTreeHelper->mLocalSortOrders[bi]);
MultiBlockTreeHelper->mLocalSortOrders[bi],
compRegularStruct);
localContourTreeMeshes[bi] = currContourTreeMesh;
// create the local data block structure
localDataBlocks[bi] = new ContourTreeBlockData<T>();
@ -720,8 +775,9 @@ VTKM_CONT void ContourTreePPP2::DoPostExecute(
this->MultiBlockTreeHelper->mSpatialDecomposition.mLocalBlockSizes.GetPortalConstControl()
.Get(static_cast<vtkm::Id>(bi));
localDataBlocks[bi]->globalSize = this->MultiBlockTreeHelper->mSpatialDecomposition.mGlobalSize;
// We need to augment at least with the boundary vertices when running in parallel
localDataBlocks[bi]->computeRegularStructure = compRegularStruct;
}
// Setup vtkmdiy to do global binary reduction of neighbouring blocks. See also RecuctionOperation struct for example
// Create the vtkmdiy master
@ -784,7 +840,6 @@ VTKM_CONT void ContourTreePPP2::DoPostExecute(
2, // raix of k-ary reduction. TODO check this value
true // contiguous: true=distance doubling , false=distnace halving TODO check this value
);
// reduction
vtkmdiy::reduce(master, assigner, partners, &detail::merge_block_functor<T>);
@ -808,23 +863,32 @@ VTKM_CONT void ContourTreePPP2::DoPostExecute(
contourTreeMeshOut.neighbours = localDataBlocks[0]->neighbours;
contourTreeMeshOut.firstNeighbour = localDataBlocks[0]->firstNeighbour;
contourTreeMeshOut.maxNeighbours = localDataBlocks[0]->maxNeighbours;
//auto fieldPermutted = vtkm::cont::make_ArrayHandlePermutation(contourTreeMeshOut.sortOrder, contourTreeMeshOut.sortedValues);
//vtkm::cont::Algorithm::Copy(fieldPermutted, currField);
// Construct the mesh boundary exectuion object needed for boundary augmentation
vtkm::Id3 minIdx(0, 0, 0);
vtkm::Id3 maxIdx = this->MultiBlockTreeHelper->mSpatialDecomposition.mGlobalSize;
maxIdx[0] = maxIdx[0] - 1;
maxIdx[1] = maxIdx[1] - 1;
maxIdx[2] = maxIdx[2] > 0 ? (maxIdx[2] - 1) : 0;
auto meshBoundaryExecObj = contourTreeMeshOut.GetMeshBoundaryExecutionObject(
this->MultiBlockTreeHelper->mSpatialDecomposition.mGlobalSize[0],
this->MultiBlockTreeHelper->mSpatialDecomposition.mGlobalSize[1],
minIdx,
maxIdx);
// Run the worklet to compute the final contour tree
worklet.Run(
contourTreeMeshOut
.sortedValues, // This array is not really used we just need to provide something to keep the API happy currField,
contourTreeMeshOut.sortedValues, // Unused param. Provide something to keep API happy
contourTreeMeshOut,
currTimings,
this->ContourTreeData,
this->MeshSortOrder,
currNumIterations,
this
->ComputeRegularStructure // true // TODO eventually replace with boundary augmentation but for now compute the full regular structure
);
this->MeshSortOrder =
contourTreeMeshOut.globalMeshIndex; // Set the final mesh sort order we need to use
this->NumIterations = currNumIterations; // Remeber the number of iterations for the output
this->ComputeRegularStructure,
meshBoundaryExecObj);
// Set the final mesh sort order we need to use
this->MeshSortOrder = contourTreeMeshOut.globalMeshIndex;
// Remeber the number of iterations for the output
this->NumIterations = currNumIterations;
// Return the sorted values of the contour tree as the result
// TODO the result we return for the parallel and serial case are different right now. This should be made consistent. However, only in the parallel case are we useing the result output

@ -76,6 +76,7 @@
#include <vtkm/worklet/contourtree_augmented/Mesh_DEM_Triangulation.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/ContourTreeMesh.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshBoundary.h>
namespace vtkm
{
@ -87,6 +88,11 @@ class ContourTreePPP2
public:
/*!
* Run the contour tree to merge an existing set of contour trees
*
* meshBoundary : Is computed by calling mesh.GetMeshBoundaryExecutionObject.
* It is technically only needed if computeRegularStructure==2.
* computeRegularStructure : 0=Off, 1=full augmentation with all vertices
* 2=boundary augmentation using meshBoundary
*/
template <typename FieldType, typename StorageType>
void Run(const vtkm::cont::ArrayHandle<FieldType, StorageType>
@ -96,7 +102,8 @@ public:
contourtree_augmented::ContourTree& contourTree,
contourtree_augmented::IdArrayType sortOrder,
vtkm::Id& nIterations,
bool computeRegularStructure = true)
unsigned int computeRegularStructure,
const contourtree_augmented::MeshBoundaryContourTreeMeshExec& meshBoundary)
{
RunContourTree(
fieldArray, // Just a place-holder to fill the required field. Used when calling SortData on the contour tree which is a no-op
@ -105,7 +112,8 @@ public:
sortOrder,
nIterations,
mesh,
computeRegularStructure);
computeRegularStructure,
meshBoundary);
return;
}
@ -114,6 +122,9 @@ public:
* allow one to run the contour tree in a consistent fashion independent
* of whether the data is 2D, 3D, or 3D_MC. This function just calls
* Run2D, Run3D, or Run3D_MC depending on the type
*
* computeRegularStructure : 0=Off, 1=full augmentation with all vertices
* 2=boundary augmentation using meshBoundary
*/
template <typename FieldType, typename StorageType>
void Run(const vtkm::cont::ArrayHandle<FieldType, StorageType> fieldArray,
@ -125,7 +136,7 @@ public:
const vtkm::Id nCols,
const vtkm::Id nSlices = 1,
bool useMarchingCubes = false,
bool computeRegularStructure = true)
unsigned int computeRegularStructure = 1)
{
using namespace vtkm::worklet::contourtree_augmented;
// 2D Contour Tree
@ -134,8 +145,14 @@ public:
// Build the mesh and fill in the values
Mesh_DEM_Triangulation_2D_Freudenthal<FieldType, StorageType> mesh(nRows, nCols);
// Run the contour tree on the mesh
RunContourTree(
fieldArray, timings, contourTree, sortOrder, nIterations, mesh, computeRegularStructure);
RunContourTree(fieldArray,
timings,
contourTree,
sortOrder,
nIterations,
mesh,
computeRegularStructure,
mesh.GetMeshBoundaryExecutionObject());
return;
}
// 3D Contour Tree using marching cubes
@ -144,8 +161,14 @@ public:
// Build the mesh and fill in the values
Mesh_DEM_Triangulation_3D_MarchingCubes<FieldType, StorageType> mesh(nRows, nCols, nSlices);
// Run the contour tree on the mesh
RunContourTree(
fieldArray, timings, contourTree, sortOrder, nIterations, mesh, computeRegularStructure);
RunContourTree(fieldArray,
timings,
contourTree,
sortOrder,
nIterations,
mesh,
computeRegularStructure,
mesh.GetMeshBoundaryExecutionObject());
return;
}
// 3D Contour Tree with Freudenthal
@ -154,8 +177,14 @@ public:
// Build the mesh and fill in the values
Mesh_DEM_Triangulation_3D_Freudenthal<FieldType, StorageType> mesh(nRows, nCols, nSlices);
// Run the contour tree on the mesh
RunContourTree(
fieldArray, timings, contourTree, sortOrder, nIterations, mesh, computeRegularStructure);
RunContourTree(fieldArray,
timings,
contourTree,
sortOrder,
nIterations,
mesh,
computeRegularStructure,
mesh.GetMeshBoundaryExecutionObject());
return;
}
}
@ -165,16 +194,32 @@ private:
/*!
* Run the contour tree for the given mesh. This function implements the main steps for
* computing the contour tree after the mesh has been constructed using the approbrite
* contour tree mesh class
* contour tree mesh class.
*
* meshBoundary : This parameter is generated by calling mesh.GetMeshBoundaryExecutionObject
* For regular 2D/3D meshes this required no extra parameters, however, for a
* ContourTreeMesh additional information about the block must be given. Rather
* than generating the MeshBoundary descriptor here, we therefore, require it
* as an input. The MeshBoundary is used to augment the contour tree with the
* mesh boundary vertices. It is needed only if we want to augement by the
* mesh boundary and computeRegularStructure is False (i.e., if we compute
* the full regular strucuture this is not needed because all vertices
* (including the boundary) will be addded to the tree anyways.
* computeRegularStructure : 0=Off, 1=full augmentation with all vertices
* 2=boundary augmentation using meshBoundary
*/
template <typename FieldType, typename StorageType, typename MeshClass>
template <typename FieldType,
typename StorageType,
typename MeshClass,
typename MeshBoundaryClass>
void RunContourTree(const vtkm::cont::ArrayHandle<FieldType, StorageType> fieldArray,
std::vector<std::pair<std::string, vtkm::Float64>>& timings,
contourtree_augmented::ContourTree& contourTree,
contourtree_augmented::IdArrayType& sortOrder,
vtkm::Id& nIterations,
MeshClass& mesh,
bool computeRegularStructure)
unsigned int computeRegularStructure,
const MeshBoundaryClass& meshBoundary)
{
using namespace vtkm::worklet::contourtree_augmented;
// Stage 1: Load the data into the mesh. This is done in the Run() method above and accessible
@ -259,12 +304,18 @@ private:
timer.Start();
// 9.2 Then we compute the regular structure
if (computeRegularStructure)
if (computeRegularStructure == 1) // augment with all vertices
{
treeMaker.ComputeRegularStructure(extrema);
timings.push_back(std::pair<std::string, vtkm::Float64>("Contour Tree Regular Structure",
timer.GetElapsedTime()));
}
else if (computeRegularStructure == 2) // augment by the mesh boundary
{
treeMaker.ComputeBoundaryRegularStructure(extrema, mesh, meshBoundary);
timings.push_back(std::pair<std::string, vtkm::Float64>(
"Contour Tree Boundary Regular Structure", timer.GetElapsedTime()));
}
// Collect the output data
nIterations = treeMaker.nIterations;

@ -211,7 +211,9 @@ inline void ContourTree::PrintContent() const
printHeader(hypernodes.GetNumberOfValues());
printIndices("Hypernodes", hypernodes);
printIndices("Hyperarcs", hyperarcs);
std::cout << std::endl;
printHeader(augmentnodes.GetNumberOfValues());
printIndices("Augmentnodes", augmentnodes);
printIndices("Augmentarcs", augmentarcs);
}
void ContourTree::DebugPrint(const char* message, const char* fileName, long lineNum)

@ -90,7 +90,6 @@
//VTKM includes
#include <vtkm/Types.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayGetValues.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleImplicit.h>
@ -139,11 +138,14 @@ public:
// computes the hyperarcs in the contour tree
void ComputeHyperAndSuperStructure();
// computes the regular arcs in the contour tree
// computes the regular arcs in the contour tree. Augment the contour tree with all regular vertices.
void ComputeRegularStructure(MeshExtrema& meshExtrema);
template <class Mesh>
void ComputeRegularStructure(MeshExtrema& meshExtrema, const Mesh& mesh);
// compute the parital regular arcs by augmenting the contour tree with the relevant vertices on the boundary
template <class Mesh, class MeshBoundaryExecObj>
void ComputeBoundaryRegularStructure(MeshExtrema& meshExtrema,
const Mesh& mesh,
const MeshBoundaryExecObj& meshBoundary);
// routine that augments the join & split tree with each other's supernodes
// the augmented trees will be stored in the joinSuperarcs / mergeSuperarcs arrays
@ -306,12 +308,17 @@ void ContourTreeMaker::ComputeHyperAndSuperStructure()
contourTree.whenTransferred, oneIfHypernodeFunctor);
vtkm::cont::Algorithm::ScanExclusive(oneIfHypernodeArrayHandle, newHypernodePosition);
auto getLastValue = [](const IdArrayType& ah) -> vtkm::Id {
return vtkm::cont::ArrayGetValue(ah.GetNumberOfValues() - 1, ah);
};
vtkm::Id nHypernodes = getLastValue(newHypernodePosition) +
oneIfHypernodeFunctor(getLastValue(contourTree.whenTransferred));
vtkm::Id nHypernodes = 0;
{
vtkm::cont::ArrayHandle<vtkm::Id> temp;
temp.Allocate(2);
vtkm::cont::Algorithm::CopySubRange(
newHypernodePosition, newHypernodePosition.GetNumberOfValues() - 1, 1, temp);
vtkm::cont::Algorithm::CopySubRange(
contourTree.whenTransferred, contourTree.whenTransferred.GetNumberOfValues() - 1, 1, temp, 1);
auto portal = temp.GetPortalControl();
nHypernodes = portal.Get(0) + oneIfHypernodeFunctor(portal.Get(1));
}
IdArrayType newHypernodes;
newHypernodes.Allocate(nHypernodes);
@ -434,10 +441,12 @@ void InitIdArrayTypeNoSuchElement(IdArrayType& idArray, vtkm::Id size)
vtkm::cont::Algorithm::Copy(noSuchElementArray, idArray);
}
template <class Mesh>
void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema, const Mesh& mesh)
template <class Mesh, class MeshBoundaryExecObj>
void ContourTreeMaker::ComputeBoundaryRegularStructure(
MeshExtrema& meshExtrema,
const Mesh& mesh,
const MeshBoundaryExecObj& meshBoundaryExecObj)
{ // ComputeRegularStructure()
// First step - use the superstructure to set the superparent for all supernodes
auto supernodesIndex = vtkm::cont::ArrayHandleIndex(contourTree.supernodes.GetNumberOfValues());
IdArrayType superparents;
@ -453,11 +462,7 @@ void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema, const M
// Second step - for all remaining (regular) nodes, locate the superarc to which they belong
contourtree_maker_inc_ns::ComputeRegularStructure_LocateSuperarcsOnBoundary
locateSuperarcsOnBoundaryWorklet(contourTree.hypernodes.GetNumberOfValues(),
contourTree.supernodes.GetNumberOfValues(),
mesh.nRows,
mesh.nCols,
mesh.nSlices,
mesh.nDims);
contourTree.supernodes.GetNumberOfValues());
this->Invoke(locateSuperarcsOnBoundaryWorklet,
superparents, // (input/output)
contourTree.whenTransferred, // (input)
@ -467,7 +472,7 @@ void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema, const M
contourTree.supernodes, // (input)
meshExtrema.peaks, // (input)
meshExtrema.pits, // (input)
mesh.sortOrder); // (input)
meshBoundaryExecObj); // (input)
// We have now set the superparent correctly for each node, and need to sort them to get the correct regular arcs
// DAVID "ContourTreeMaker.h" line 338
@ -493,7 +498,6 @@ void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema, const M
vtkm::cont::Algorithm::CopyIf(
superparents, superparents, tmpsuperparents, ContourTreeNoSuchElementSuperParents());
vtkm::cont::Algorithm::Copy(tmpsuperparents, superparents);
//superparents.erase(std::remove(superparents.begin(), superparents.end(), NO_SUCH_ELEMENT), superparents.end());
// Create array for sorting
IdArrayType augmentnodes_sorted;
@ -505,7 +509,6 @@ void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema, const M
vtkm::cont::Algorithm::Sort(
augmentnodes_sorted,
contourtree_maker_inc_ns::ContourTreeNodeComparator(superparents, contourTree.superarcs));
// now set the arcs based on the array
InitIdArrayTypeNoSuchElement(contourTree.augmentarcs,
contourTree.augmentnodes.GetNumberOfValues());
@ -518,12 +521,10 @@ void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema, const M
contourTree.supernodes, // (input)
toCompressed, // (input)
contourTree.augmentarcs); // (output)
DebugPrint("Regular Structure Computed", __FILE__, __LINE__);
DebugPrint("Regular Boundary Structure Computed", __FILE__, __LINE__);
} // ComputeRegularStructure()
// routine that augments the join & split tree with each other's supernodes
// the augmented trees will be stored in the joinSuperarcs / mergeSuperarcs arrays
// the sort IDs will be stored in the ContourTree's arrays, &c.

@ -398,6 +398,10 @@ public:
}; // ComputeRegularStructure_LocateSuperarcs.h
// TODO This algorithm looks to be a 3d/2d volume algorithm that is iterating 'points' and concerned about being on the 'boundary'. This would be better suited as WorkletMapPointNeighborhood as it can provide the boundary condition logic for you.
// Worklet for the second step of template <class Mesh> void ContourTreeMaker::ComputeRegularStructure ---
// for all remaining (regular) nodes on the boundary, locate the superarc to which they belong
class ComputeRegularStructure_LocateSuperarcsOnBoundary : public vtkm::worklet::WorkletMapField
@ -411,7 +415,7 @@ public:
WholeArrayIn contourTreeSupernodes, // (input)
WholeArrayIn meshExtremaPeaks, // (input)
WholeArrayIn meshExtremaPits, // (input)
FieldIn meshSortOrderPortal); // (input)
ExecObject meshBoundary); // (input)
typedef void ExecutionSignature(_1, InputIndex, _2, _3, _4, _5, _6, _7, _8, _9);
using InputDomain = _1;
@ -424,52 +428,13 @@ public:
// Default Constructor
VTKM_EXEC_CONT
ComputeRegularStructure_LocateSuperarcsOnBoundary(vtkm::Id NumHypernodes,
vtkm::Id NumSupernodes,
vtkm::Id _nRows,
vtkm::Id _nCols,
vtkm::Id _nSlices,
vtkm::Id _nDims)
ComputeRegularStructure_LocateSuperarcsOnBoundary(vtkm::Id NumHypernodes, vtkm::Id NumSupernodes)
: numHypernodes(NumHypernodes)
, numSupernodes(NumSupernodes)
, nRows(_nRows)
, nCols(_nCols)
, nSlices(_nSlices)
, nDims(_nDims)
{
}
vtkm::Id vertexRow(const vtkm::Id v) const { return (v % (nRows * nCols)) / nCols; }
vtkm::Id vertexColumn(const vtkm::Id v) const { return v % nCols; }
vtkm::Id vertexSlice(const vtkm::Id v) const { return v / (nRows * nCols); }
bool liesOnBoundary(const vtkm::Id meshSortOrderValue) const
{
if (nDims == 2)
return liesOnBoundary2(meshSortOrderValue);
else
return liesOnBoundary3(meshSortOrderValue);
}
bool liesOnBoundary2(const vtkm::Id meshSortOrderValue) const
{
const vtkm::Id v = meshSortOrderValue;
const vtkm::Id row = vertexRow(v);
const vtkm::Id col = vertexColumn(v);
return (row == 0) || (col == 0) || (row == nRows - 1) || (col == nCols - 1);
}
bool liesOnBoundary3(const vtkm::Id meshSortOrderValue) const
{
const vtkm::Id v = meshSortOrderValue;
const vtkm::Id row = vertexRow(v);
const vtkm::Id col = vertexColumn(v);
const vtkm::Id sli = vertexSlice(v);
return (row == 0) || (col == 0) || (sli == 0) || (row == nRows - 1) || (col == nCols - 1) ||
(sli == nSlices - 1);
}
template <typename InOutFieldPortalType, typename InFieldPortalType>
template <typename InOutFieldPortalType, typename InFieldPortalType, typename MeshBoundaryType>
VTKM_EXEC void operator()(const InOutFieldPortalType& contourTreeSuperparentsPortal,
const vtkm::Id node,
const InFieldPortalType& contourTreeWhenTransferredPortal,
@ -479,12 +444,11 @@ public:
const InFieldPortalType& contourTreeSupernodesPortal,
const InFieldPortalType& meshExtremaPeaksPortal,
const InFieldPortalType& meshExtremaPitsPortal,
const vtkm::Id& meshSortOrderValue) const
const MeshBoundaryType& meshBoundary) const
{
// per node
// if the superparent is already set, it's a supernode, so skip it.
if (noSuchElement(contourTreeSuperparentsPortal.Get(node)) &&
liesOnBoundary(meshSortOrderValue))
if (noSuchElement(contourTreeSuperparentsPortal.Get(node)) && meshBoundary.liesOnBoundary(node))
{ // regular nodes only
// we will need to prune top and bottom until one of them prunes past the node
vtkm::Id top = meshExtremaPeaksPortal.Get(node);

@ -16,6 +16,7 @@ set(headers
MeshStructureFreudenthal3D.h
MeshStructureMarchingCubes.h
MeshStructureContourTreeMesh.h
MeshBoundary.h
)
#----------------------------------------------------------------------------

@ -74,6 +74,7 @@
#include <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/contourtree_augmented/ArrayTransforms.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshBoundary.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshStructureContourTreeMesh.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/contourtreemesh/ArcComparator.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/contourtreemesh/CombinedOtherStartIndexNNeighboursWorklet.h>
@ -97,7 +98,6 @@
#include <algorithm>
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/diy/Configure.h>
#include <vtkm/thirdparty/diy/diy.h>
@ -127,12 +127,19 @@ public:
ContourTreeMesh() {}
// Constructors
// Constructor
ContourTreeMesh(const IdArrayType& arcs,
const IdArrayType& inSortOrder,
const vtkm::cont::ArrayHandle<FieldType>& values,
const IdArrayType& inGlobalMeshIndex);
// Constructor
ContourTreeMesh(const IdArrayType& nodes,
const IdArrayType& arcs,
const IdArrayType& inSortOrder,
const vtkm::cont::ArrayHandle<FieldType>& values,
const IdArrayType& inGlobalMeshIndex);
// Construct a ContourTreeMesh from nodes/arcs and another ContourTreeMesh (instead of a Mesh_DEM_Triangulation)
// nodes/arcs: From the contour tree
// ContourTreeMesh: the contour tree mesh used to compute the contour tree described by nodes/arcs
@ -152,6 +159,8 @@ public:
this->nVertices = this->sortedValues.GetNumberOfValues();
}
vtkm::Id GetNumberOfVertices() const { return this->nVertices; }
// Combine two ContourTreeMeshes
template <typename DeviceTag>
void mergeWith(ContourTreeMesh<FieldType>& other);
@ -193,6 +202,12 @@ public:
// Debug print routine
void DebugPrint(const char* message, const char* fileName, long lineNum);
// Get boundary execution object
MeshBoundaryContourTreeMeshExec GetMeshBoundaryExecutionObject(vtkm::Id totalNRows,
vtkm::Id totalNCols,
vtkm::Id3 minIdx,
vtkm::Id3 maxIdx) const;
private:
vtkm::cont::Invoker Invoke;
@ -255,6 +270,7 @@ ContourTreeMesh<FieldType>::ContourTreeMesh(const IdArrayType& arcs,
const vtkm::cont::ArrayHandle<FieldType>& values,
const IdArrayType& inGlobalMeshIndex)
: sortOrder(inSortOrder)
, sortedValues()
, globalMeshIndex(inGlobalMeshIndex)
, neighbours()
, firstNeighbour()
@ -265,6 +281,39 @@ ContourTreeMesh<FieldType>::ContourTreeMesh(const IdArrayType& arcs,
// TODO check if we actually need to make this copy here. we could just store the permutedValues array to save memory
vtkm::cont::Algorithm::Copy(permutedValues, this->sortedValues);
this->InitialiseNeighboursFromArcs(arcs);
#ifdef DEBUG_PRINT
// Print the contents fo this for debugging
DebugPrint("ContourTreeMesh Initialized", __FILE__, __LINE__);
#endif
}
template <typename FieldType>
ContourTreeMesh<FieldType>::ContourTreeMesh(const IdArrayType& nodes,
const IdArrayType& arcs,
const IdArrayType& inSortOrder,
const vtkm::cont::ArrayHandle<FieldType>& values,
const IdArrayType& inGlobalMeshIndex)
: globalMeshIndex(inGlobalMeshIndex)
, neighbours()
, firstNeighbour()
{
// Initialize the sortedValues array the values permutted by the sortOrder permutted by the nodes, i.e.,
// this->sortedValues[v] = values[inSortOrder[nodes[v]]];
vtkm::cont::ArrayHandlePermutation<IdArrayType, IdArrayType> permutedSortOrder(nodes,
inSortOrder);
auto permutedValues = vtkm::cont::make_ArrayHandlePermutation(permutedSortOrder, values);
vtkm::cont::Algorithm::Copy(permutedValues, this->sortedValues);
vtkm::cont::Algorithm::Copy(
permutedSortOrder,
this
->sortOrder); // TODO Check if the sortOrder needs to be set form the input or the permutted sortOrder
this->nVertices = this->sortedValues.GetNumberOfValues();
this->InitialiseNeighboursFromArcs(arcs);
#ifdef DEBUG_PRINT
// Print the contents fo this for debugging
DebugPrint("ContourTreeMesh Initialized", __FILE__, __LINE__);
#endif
}
template <typename FieldType>
@ -278,6 +327,10 @@ ContourTreeMesh<FieldType>::ContourTreeMesh(const IdArrayType& arcs,
{
this->nVertices = this->sortedValues.GetNumberOfValues();
this->InitialiseNeighboursFromArcs(arcs);
#ifdef DEBUG_PRINT
// Print the contents fo this for debugging
DebugPrint("ContourTreeMesh Initialized", __FILE__, __LINE__);
#endif
}
@ -285,7 +338,7 @@ template <typename FieldType>
ContourTreeMesh<FieldType>::ContourTreeMesh(const IdArrayType& nodes,
const IdArrayType& arcs,
const ContourTreeMesh<FieldType>& mesh)
: sortOrder(mesh.SortOrder)
: sortOrder(mesh.sortOrder)
, neighbours()
, firstNeighbour()
{
@ -294,12 +347,15 @@ ContourTreeMesh<FieldType>::ContourTreeMesh(const IdArrayType& nodes,
nodes, mesh.globalMeshIndex);
vtkm::cont::Algorithm::Copy(permutedGlobalMeshIndex, this->globalMeshIndex);
// Initialize the sortedValues array with the sortedValues permutted by the nodes
vtkm::cont::ArrayHandlePermutation<IdArrayType, IdArrayType> permutedSortedValues(
nodes, mesh.sortedValues);
auto permutedSortedValues = vtkm::cont::make_ArrayHandlePermutation(nodes, mesh.sortedValues);
vtkm::cont::Algorithm::Copy(permutedSortedValues, this->sortedValues);
// Initialize the neighbours from the arcs
this->nVertices = this->sortedValues.GetNumberOfValues();
this->InitialiseNeighboursFromArcs(arcs);
#ifdef DEBUG_PRINT
// Print the contents fo this for debugging
DebugPrint("ContourTreeMesh Initialized", __FILE__, __LINE__);
#endif
}
@ -598,7 +654,7 @@ void ContourTreeMesh<FieldType>::mergeWith(ContourTreeMesh<FieldType>& other)
std::inplace_merge(
neighboursBegin, neighboursBegin + combinedOtherStartIndexPortal.Get(vtx), neighboursEnd);
auto it = std::unique(neighboursBegin, neighboursEnd);
combinedOtherStartIndexPortal.Set(vtx, static_cast<vtkm::Id>(neighboursEnd - it));
combinedOtherStartIndexPortal.Set(vtx, neighboursEnd - it);
while (it != neighboursEnd)
*(it++) = NO_SUCH_ELEMENT;
}
@ -725,6 +781,17 @@ void ContourTreeMesh<FieldType>::loadVector(std::istream& is,
}
}
template <typename FieldType>
MeshBoundaryContourTreeMeshExec ContourTreeMesh<FieldType>::GetMeshBoundaryExecutionObject(
vtkm::Id totalNRows,
vtkm::Id totalNCols,
vtkm::Id3 minIdx,
vtkm::Id3 maxIdx) const
{
return MeshBoundaryContourTreeMeshExec(
this->globalMeshIndex, totalNRows, totalNCols, minIdx, maxIdx);
}
} // namespace contourtree_augmented
} // worklet
} // vtkm

@ -57,6 +57,7 @@
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Mesh_DEM_Triangulation.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshBoundary.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshStructureFreudenthal2D.h>
#include <vtkm/cont/ExecutionObjectBase.h>
@ -84,6 +85,8 @@ public:
Mesh_DEM_Triangulation_2D_Freudenthal(vtkm::Id nrows, vtkm::Id ncols);
MeshBoundary2DExec GetMeshBoundaryExecutionObject() const;
private:
bool useGetMax; // Define the behavior ofr the PrepareForExecution function
}; // class Mesh_DEM_Triangulation
@ -122,6 +125,12 @@ MeshStructureFreudenthal2D<DeviceTag>
edgeBoundaryDetectionMasks);
}
template <typename T, typename StorageType>
MeshBoundary2DExec
Mesh_DEM_Triangulation_2D_Freudenthal<T, StorageType>::GetMeshBoundaryExecutionObject() const
{
return MeshBoundary2DExec(this->nRows, this->nCols, this->sortOrder);
}
} // namespace contourtree_augmented
} // worklet

@ -61,6 +61,7 @@
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Mesh_DEM_Triangulation.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshBoundary.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshStructureFreudenthal3D.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/freudenthal_3D/Types.h>
@ -89,6 +90,8 @@ public:
Mesh_DEM_Triangulation_3D_Freudenthal(vtkm::Id nrows, vtkm::Id ncols, vtkm::Id nslices);
MeshBoundary3DExec GetMeshBoundaryExecutionObject() const;
private:
bool useGetMax; // Define the behavior ofr the PrepareForExecution function
}; // class Mesh_DEM_Triangulation
@ -138,6 +141,13 @@ MeshStructureFreudenthal3D<DeviceTag>
}
template <typename T, typename StorageType>
MeshBoundary3DExec
Mesh_DEM_Triangulation_3D_Freudenthal<T, StorageType>::GetMeshBoundaryExecutionObject() const
{
return MeshBoundary3DExec(this->nRows, this->nCols, this->nSlices, this->sortOrder);
}
} // namespace contourtree_augmented
} // worklet
} // vtkm

@ -61,6 +61,7 @@
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Mesh_DEM_Triangulation.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshBoundary.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/MeshStructureMarchingCubes.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/marchingcubes_3D/Types.h>
@ -93,6 +94,8 @@ public:
Mesh_DEM_Triangulation_3D_MarchingCubes(vtkm::Id nrows, vtkm::Id ncols, vtkm::Id nslices);
MeshBoundary3DExec GetMeshBoundaryExecutionObject() const;
private:
bool useGetMax; // Define the behavior ofr the PrepareForExecution function
}; // class Mesh_DEM_Triangulation
@ -167,6 +170,13 @@ MeshStructureMarchingCubes<DeviceTag>
inCubeConnectionsEighteen);
}
template <typename T, typename StorageType>
MeshBoundary3DExec
Mesh_DEM_Triangulation_3D_MarchingCubes<T, StorageType>::GetMeshBoundaryExecutionObject() const
{
return MeshBoundary3DExec(this->nRows, this->nCols, this->nSlices, this->sortOrder);
}
} // namespace contourtree_augmented
} // worklet
} // vtkm

@ -0,0 +1,301 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//=============================================================================
//
// This code is an extension of the algorithm presented in the paper:
// Parallel Peak Pruning for Scalable SMP Contour Tree Computation.
// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens.
// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization
// (LDAV), October 2016, Baltimore, Maryland.
//
// The PPP2 algorithm and software were jointly developed by
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
// This header contains a collection of classes used to describe the boundary
// of a mesh, for each main mesh type (i.e., 2D, 3D, and ContourTreeMesh).
// For each mesh type, there are two classes, the actual boundary desriptor
// class and an ExectionObject class with the PrepareForInput function that
// VTKm expects to generate the object for the execution environment.
#ifndef vtkm_worklet_contourtree_augmented_mesh_boundary_h
#define vtkm_worklet_contourtree_augmented_mesh_boundary_h
#include <cstdlib>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem/MeshStructure2D.h>
#include <vtkm/worklet/contourtree_augmented/mesh_dem/MeshStructure3D.h>
#include <vtkm/cont/ExecutionObjectBase.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_augmented
{
template <typename DeviceTag>
class MeshBoundary2D
{
public:
// Sort indicies types
using sortOrderPortalType = typename IdArrayType::template ExecutionTypes<DeviceTag>::PortalConst;
VTKM_EXEC_CONT
MeshBoundary2D()
: MeshStructure(mesh_dem::MeshStructure2D<DeviceTag>(0, 0))
{
}
VTKM_EXEC_CONT
MeshBoundary2D(vtkm::Id nrows, vtkm::Id ncols, const IdArrayType& sortOrder)
: MeshStructure(mesh_dem::MeshStructure2D<DeviceTag>(nrows, ncols))
{
sortOrderPortal = sortOrder.PrepareForInput(DeviceTag());
}
VTKM_EXEC_CONT
bool liesOnBoundary(const vtkm::Id index) const
{
vtkm::Id meshSortOrderValue = this->sortOrderPortal.Get(index);
const vtkm::Id row = MeshStructure.vertexRow(meshSortOrderValue);
const vtkm::Id col = MeshStructure.vertexColumn(meshSortOrderValue);
return (row == 0) || (col == 0) || (row == MeshStructure.nRows - 1) ||
(col == MeshStructure.nCols - 1);
}
private:
// 2D Mesh size parameters
mesh_dem::MeshStructure2D<DeviceTag> MeshStructure;
sortOrderPortalType sortOrderPortal;
};
class MeshBoundary2DExec : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_EXEC_CONT
MeshBoundary2DExec(vtkm::Id nrows, vtkm::Id ncols, const IdArrayType& inSortOrder)
: nRows(nrows)
, nCols(ncols)
, sortOrder(inSortOrder)
{
}
VTKM_CONT
template <typename DeviceTag>
MeshBoundary2D<DeviceTag> PrepareForExecution(DeviceTag) const
{
return MeshBoundary2D<DeviceTag>(this->nRows, this->nCols, this->sortOrder);
}
private:
// 2D Mesh size parameters
vtkm::Id nRows;
vtkm::Id nCols;
const IdArrayType& sortOrder;
};
template <typename DeviceTag>
class MeshBoundary3D : public vtkm::cont::ExecutionObjectBase
{
public:
// Sort indicies types
using sortOrderPortalType = typename IdArrayType::template ExecutionTypes<DeviceTag>::PortalConst;
VTKM_EXEC_CONT
MeshBoundary3D()
: MeshStructure(mesh_dem::MeshStructure3D<DeviceTag>(0, 0, 0))
{
}
VTKM_EXEC_CONT
MeshBoundary3D(vtkm::Id nrows, vtkm::Id ncols, vtkm::Id nslices, const IdArrayType& sortOrder)
: MeshStructure(mesh_dem::MeshStructure3D<DeviceTag>(nrows, ncols, nslices))
{
sortOrderPortal = sortOrder.PrepareForInput(DeviceTag());
}
VTKM_EXEC_CONT
bool liesOnBoundary(const vtkm::Id index) const
{
vtkm::Id meshSortOrderValue = this->sortOrderPortal.Get(index);
const vtkm::Id row = MeshStructure.vertexRow(meshSortOrderValue);
const vtkm::Id col = MeshStructure.vertexColumn(meshSortOrderValue);
const vtkm::Id sli = MeshStructure.vertexSlice(meshSortOrderValue);
return (row == 0) || (col == 0) || (sli == 0) || (row == MeshStructure.nRows - 1) ||
(col == MeshStructure.nCols - 1) || (sli == MeshStructure.nSlices - 1);
}
protected:
// 3D Mesh size parameters
mesh_dem::MeshStructure3D<DeviceTag> MeshStructure;
sortOrderPortalType sortOrderPortal;
};
class MeshBoundary3DExec : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_EXEC_CONT
MeshBoundary3DExec(vtkm::Id nrows,
vtkm::Id ncols,
vtkm::Id nslices,
const IdArrayType& inSortOrder)
: nRows(nrows)
, nCols(ncols)
, nSlices(nslices)
, sortOrder(inSortOrder)
{
}
VTKM_CONT
template <typename DeviceTag>
MeshBoundary3D<DeviceTag> PrepareForExecution(DeviceTag) const
{
return MeshBoundary3D<DeviceTag>(this->nRows, this->nCols, this->nSlices, this->sortOrder);
}
protected:
// 3D Mesh size parameters
vtkm::Id nRows;
vtkm::Id nCols;
vtkm::Id nSlices;
const IdArrayType& sortOrder;
};
template <typename DeviceTag>
class MeshBoundaryContourTreeMesh
{
public:
using IndicesPortalType = typename IdArrayType::template ExecutionTypes<DeviceTag>::PortalConst;
VTKM_EXEC_CONT
MeshBoundaryContourTreeMesh() {}
VTKM_EXEC_CONT
MeshBoundaryContourTreeMesh(const IdArrayType& globalMeshIndex,
vtkm::Id totalNRows,
vtkm::Id totalNCols,
vtkm::Id3 minIdx,
vtkm::Id3 maxIdx)
: TotalNRows(totalNRows)
, TotalNCols(totalNCols)
, MinIdx(minIdx)
, MaxIdx(maxIdx)
{
assert(TotalNRows > 0 && TotalNCols > 0);
this->GlobalMeshIndexPortal = globalMeshIndex.PrepareForInput(DeviceTag());
}
VTKM_EXEC_CONT
bool liesOnBoundary(const vtkm::Id index) const
{
vtkm::Id idx = GlobalMeshIndexPortal.Get(index);
vtkm::Id3 rcs;
rcs[0] = vtkm::Id((idx % (this->TotalNRows * this->TotalNCols)) / this->TotalNCols);
rcs[1] = vtkm::Id(idx % this->TotalNCols);
rcs[2] = vtkm::Id(idx / (this->TotalNRows * this->TotalNCols));
for (int d = 0; d < 3; ++d)
{
if (this->MinIdx[d] != this->MaxIdx[d] &&
(rcs[d] == this->MinIdx[d] || rcs[d] == this->MaxIdx[d]))
{
return true;
}
}
return false;
}
private:
// mesh block parameters
vtkm::Id TotalNRows;
vtkm::Id TotalNCols;
vtkm::Id3 MinIdx;
vtkm::Id3 MaxIdx;
IndicesPortalType GlobalMeshIndexPortal;
};
class MeshBoundaryContourTreeMeshExec : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_EXEC_CONT
MeshBoundaryContourTreeMeshExec(const IdArrayType& globalMeshIndex,
vtkm::Id totalNRows,
vtkm::Id totalNCols,
vtkm::Id3 minIdx,
vtkm::Id3 maxIdx)
: GlobalMeshIndex(globalMeshIndex)
, TotalNRows(totalNRows)
, TotalNCols(totalNCols)
, MinIdx(minIdx)
, MaxIdx(maxIdx)
{
}
VTKM_CONT
template <typename DeviceTag>
MeshBoundaryContourTreeMesh<DeviceTag> PrepareForExecution(DeviceTag) const
{
return MeshBoundaryContourTreeMesh<DeviceTag>(
GlobalMeshIndex, this->TotalNRows, this->TotalNCols, this->MinIdx, this->MaxIdx);
}
private:
const IdArrayType& GlobalMeshIndex;
vtkm::Id TotalNRows;
vtkm::Id TotalNCols;
vtkm::Id3 MinIdx;
vtkm::Id3 MaxIdx;
};
} // namespace contourtree_augmented
} // worklet
} // vtkm
#endif

@ -98,8 +98,12 @@ public:
{
return isThis(idx) ? this->thisVectorPortal.Get(maskedIndex(idx))
: this->otherVectorPortal.Get(maskedIndex(idx));
//std::cout<<"CV: idx="<<idx<<" isThis(idx)="<<isThis(idx)<<" maskedIndex(idx)="<<maskedIndex(idx)<<" thisSize="<<thisVectorPortal.GetNumberOfValues()<<" otherSize="<<otherVectorPortal.GetNumberOfValues()<<" thisVal="<< this->thisVectorPortal.Get(maskedIndex(idx))<<" otherVal="<<this->otherVectorPortal.Get(maskedIndex(idx))<< " result="<<val<<std::endl;
//return val;
}
VTKM_EXEC_CONT
vtkm::Id GetNumberOfValues() const
{
return thisVectorPortal.GetNumberOfValues() + otherVectorPortal.GetNumberOfValues();
}
private:
@ -124,6 +128,11 @@ public:
return CombinedVector<T, DeviceTag>(this->thisVector, this->otherVector);
}
vtkm::Id GetNumberOfValues() const
{
return thisVector.GetNumberOfValues() + otherVector.GetNumberOfValues();
}
private:
const vtkm::cont::ArrayHandle<T>& thisVector;
const vtkm::cont::ArrayHandle<T>& otherVector;

@ -100,7 +100,7 @@ public:
vtkm::worklet::contourtree_augmented::IdArrayType meshSortOrder;
vtkm::Id numIterations;
const bool useMarchingCubes = false;
const bool computeRegularStructure = true;
const int computeRegularStructure = 1;
contourTreeWorklet.Run(field,
timings,
@ -173,7 +173,7 @@ public:
vtkm::worklet::contourtree_augmented::IdArrayType meshSortOrder;
vtkm::Id numIterations;
const bool useMarchingCubes = false;
const bool computeRegularStructure = true;
const int computeRegularStructure = 1;
contourTreeWorklet.Run(field,
timings,
@ -253,7 +253,7 @@ public:
vtkm::worklet::contourtree_augmented::IdArrayType meshSortOrder;
vtkm::Id numIterations;
const bool useMarchingCubes = true;
const bool computeRegularStructure = true;
const int computeRegularStructure = 1;
contourTreeWorklet.Run(field,
timings,