Compare commits

...

4 Commits

Author SHA1 Message Date
Mingzhe Li
8e9bef5fb1 Merge branch 'distributed-contours' into 'master'
Distributed Isosurface extraction from branch decomposition

See merge request vtk/vtk-m!3226
2024-07-09 19:28:42 -04:00
Mingzhe Li
c6136dd501 bug fix for not covering the end of 3D HDF5 datasets 2024-07-09 16:28:00 -07:00
Mingzhe Li
e22051a0bc Reduce function size called by CastAndCallForTypes in SelectTopVolumeFunctor 2024-07-09 16:11:11 -07:00
Gunther H. Weber
0425cc7996 Added distributed individual contour extraction 2024-07-09 11:55:46 -07:00
40 changed files with 5353 additions and 273 deletions

@ -669,6 +669,7 @@ int main(int argc, char* argv[])
if (computeHierarchicalVolumetricBranchDecomposition)
{
vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter;
bd_filter.SetTimingsLogLevel(timingsLogLevel);
bd_result = bd_filter.Execute(result);
}
currTime = totalTime.GetElapsedTime();
@ -681,8 +682,13 @@ int main(int argc, char* argv[])
{
vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter tp_filter;
tp_filter.SetSavedBranches(numBranches);
tp_filter.SetMarchingCubes(useMarchingCubes);
tp_filter.SetTimingsLogLevel(timingsLogLevel);
tp_result = tp_filter.Execute(bd_result);
}
currTime = totalTime.GetElapsedTime();
vtkm::Float64 topVolBranchTime = currTime - prevTime;
prevTime = currTime;
// Save output
if (saveOutputData)
@ -740,14 +746,19 @@ int main(int argc, char* argv[])
for (vtkm::Id ds_no = 0; print_to_files && ds_no < max_blocks_to_print; ++ds_no)
{
auto ds = tp_result.GetPartition(ds_no);
std::string topVolumeBranchFileName = std::string("TopVolumeBranch_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream topVolumeBranchStream(topVolumeBranchFileName.c_str());
auto topVolBranchGRId = ds.GetField("TopVolumeBranchGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto topVolBranchUpperEnd = ds.GetField("TopVolumeBranchUpperEnd")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto topVolBranchLowerEnd = ds.GetField("TopVolumeBranchLowerEnd")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto topVolBranchVolume = ds.GetField("TopVolumeBranchVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
@ -761,10 +772,11 @@ int main(int argc, char* argv[])
.AsArrayHandle<vtkm::cont::ArrayHandle<ValueType>>()
.ReadPortal();
vtkm::Id nSelectedBranches = topVolBranchGRId.GetNumberOfValues();
vtkm::Id nSelectedBranches = topVolBranchUpperEnd.GetNumberOfValues();
for (vtkm::Id branch = 0; branch < nSelectedBranches; ++branch)
{
topVolumeBranchStream << std::setw(12) << topVolBranchGRId.Get(branch)
topVolumeBranchStream << std::setw(12) << topVolBranchUpperEnd.Get(branch)
<< std::setw(12) << topVolBranchLowerEnd.Get(branch)
<< std::setw(14) << topVolBranchVolume.Get(branch)
<< std::setw(5) << topVolBranchSaddleEpsilon.Get(branch)
<< std::setw(14) << topVolBranchSaddleIsoValue.Get(branch)
@ -785,6 +797,111 @@ int main(int argc, char* argv[])
isoValuesStream << std::endl;
}
for (vtkm::Id ds_no = 0; ds_no < tp_result.GetNumberOfPartitions(); ++ds_no)
{
auto bd_ds = bd_result.GetPartition(ds_no);
auto ds = tp_result.GetPartition(ds_no);
std::string branchDecompositionVolumeFileName =
std::string("BranchDecompositionVolume_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream bdVolStream(branchDecompositionVolumeFileName.c_str());
auto upperEndGRId = bd_ds.GetField("UpperEndGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto lowerEndGRId = bd_ds.GetField("LowerEndGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto branchSaddleEpsilon = ds.GetField("BranchSaddleEpsilon")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto branchVolume = ds.GetField("BranchVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
vtkm::Id nBranches = upperEndGRId.GetNumberOfValues();
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
{
bdVolStream << std::setw(12) << upperEndGRId.Get(branch) << std::setw(12)
<< lowerEndGRId.Get(branch) << std::setw(3)
<< branchSaddleEpsilon.Get(branch) << std::setw(18)
<< branchVolume.Get(branch) << std::endl;
}
std::string isosurfaceFileName = std::string("Isosurface_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream isosurfaceStream(isosurfaceFileName.c_str());
auto isosurfaceEdgesFrom = ds.GetField("IsosurfaceEdgesFrom")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Vec3f_64>>()
.ReadPortal();
auto isosurfaceEdgesTo = ds.GetField("IsosurfaceEdgesTo")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Vec3f_64>>()
.ReadPortal();
auto isosurfaceEdgesLabels = ds.GetField("IsosurfaceEdgesLabels")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto isosurfaceEdgesOrders = ds.GetField("IsosurfaceEdgesOrders")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto isosurfaceEdgesOffset = ds.GetField("IsosurfaceEdgesOffset")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto isosurfaceIsoValue = ds.GetField("IsosurfaceIsoValue")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<ValueType>>()
.ReadPortal();
vtkm::Id nIsosurfaceEdges = isosurfaceEdgesFrom.GetNumberOfValues();
vtkm::Id isoSurfaceCount = 0;
if (nDims == 2)
for (vtkm::Id edge = 0; edge < nIsosurfaceEdges; ++edge)
{
while (isoSurfaceCount < isosurfaceEdgesLabels.GetNumberOfValues() &&
edge == isosurfaceEdgesOffset.Get(isoSurfaceCount))
{
isosurfaceStream << "Isosurface Info:" << std::setw(5)
<< isosurfaceEdgesLabels.Get(isoSurfaceCount) << std::setw(10)
<< isosurfaceEdgesOrders.Get(isoSurfaceCount) << std::setw(10)
<< isosurfaceIsoValue.Get(isoSurfaceCount) << std::endl;
isoSurfaceCount++;
}
isosurfaceStream << std::setw(6) << isosurfaceEdgesFrom.Get(edge) << std::setw(6)
<< isosurfaceEdgesTo.Get(edge) << std::endl;
}
else if (nDims == 3)
{
VTKM_ASSERT(nIsosurfaceEdges % 3 == 0);
for (vtkm::Id edge = 0; edge < nIsosurfaceEdges; edge += 3)
{
while (isoSurfaceCount < isosurfaceEdgesLabels.GetNumberOfValues() &&
edge == isosurfaceEdgesOffset.Get(isoSurfaceCount))
{
isosurfaceStream << "Isosurface Info:" << std::setw(5)
<< isosurfaceEdgesLabels.Get(isoSurfaceCount) << std::setw(10)
<< isosurfaceEdgesOrders.Get(isoSurfaceCount) << std::setw(10)
<< isosurfaceIsoValue.Get(isoSurfaceCount) << std::endl;
isoSurfaceCount++;
}
isosurfaceStream << std::setw(6) << isosurfaceEdgesFrom.Get(edge) << std::setw(6)
<< isosurfaceEdgesTo.Get(edge) << std::setw(6)
<< isosurfaceEdgesTo.Get(edge + 1) << std::endl;
}
}
}
}
}
else
@ -892,6 +1009,8 @@ int main(int argc, char* argv[])
<< ": " << postFilterSyncTime << " seconds" << std::endl
<< std::setw(42) << std::left << " Branch Decomposition"
<< ": " << branchDecompTime << " seconds" << std::endl
<< std::setw(42) << std::left << " Top Volume Branch Extraction"
<< ": " << topVolBranchTime << " seconds" << std::endl
<< std::setw(42) << std::left << " Save Tree Compiler Data"
<< ": " << saveOutputDataTime << " seconds" << std::endl
<< std::setw(42) << std::left << " Total Time"

@ -233,6 +233,19 @@ bool read3DHDF5File(const int& mpi_rank,
{
count[2] = globalSize[2] - offset[2];
}
// Check that we cover the end of the dataset
if (blockIndex[0] == blocksPerDim[0] - 1 && (vtkm::Id(offset[0] + count[0]) != globalSize[0]))
{
count[0] = globalSize[0] - offset[0];
}
if (blockIndex[1] == blocksPerDim[1] - 1 && (vtkm::Id(offset[1] + count[1]) != globalSize[1]))
{
count[1] = globalSize[1] - offset[1];
}
if (blockIndex[2] == blocksPerDim[2] - 1 && (vtkm::Id(offset[2] + count[2]) != globalSize[2]))
{
count[2] = globalSize[2] - offset[2];
}
blockSize = vtkm::Id3{ static_cast<vtkm::Id>(count[0]),
static_cast<vtkm::Id>(count[1]),
static_cast<vtkm::Id>(count[2]) };

@ -21,6 +21,7 @@ set(scalar_topology_sources
internal/ComputeBlockIndices.cxx
internal/ComputeDistributedBranchDecompositionFunctor.cxx
internal/SelectTopVolumeContoursFunctor.cxx
internal/ParentBranchExtremaFunctor.cxx
internal/ExchangeBranchEndsFunctor.cxx
ContourTreeUniform.cxx
ContourTreeUniformAugmented.cxx

@ -1138,11 +1138,17 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
// ******** 3. Augment the hierarchical tree if requested ********
if (this->AugmentHierarchicalTree)
{
master.foreach (
[](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) {
blockData->HierarchicalAugmenter.Initialize(
blockData->GlobalBlockId, &blockData->HierarchicalTree, &blockData->AugmentedTree);
});
master.foreach ([globalPointDimensions](DistributedContourTreeBlockData* blockData,
const vtkmdiy::Master::ProxyWithLink&) {
blockData->HierarchicalAugmenter.Initialize(
blockData->GlobalBlockId,
&blockData->HierarchicalTree,
&blockData->AugmentedTree,
blockData->BlockOrigin, // Origin of the data block
blockData->BlockSize, // Extends of the data block
globalPointDimensions // global point dimensions
);
});
timingsStream << " " << std::setw(38) << std::left << "Initalize Hierarchical Trees"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;

@ -80,6 +80,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
timingsStream << " " << std::setw(60) << std::left
<< "Create DIY Master and Assigner (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Compute global ids (gids) for our local blocks
@ -116,6 +117,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
timingsStream << " " << std::setw(60) << std::left
<< "Get DIY Information (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
@ -237,7 +239,8 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
branch_decomposition_master,
assigner,
partners,
vtkm::filter::scalar_topology::internal::ComputeDistributedBranchDecompositionFunctor{});
vtkm::filter::scalar_topology::internal::ComputeDistributedBranchDecompositionFunctor(
this->TimingsLogLevel));
timingsStream << " " << std::setw(60) << std::left
<< "Exchanging best up/down supernode and volume"
@ -314,12 +317,19 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
b->VolumetricBranchDecomposer.CollectBranches(ds, b->BranchRoots);
});
timingsStream << " " << std::setw(38) << std::left << "CollectBranchEnds"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Now we have collected the branches, we do a global reduction to exchance branch end information
// across all compute ranks
vtkmdiy::reduce(branch_decomposition_master,
assigner,
partners,
vtkm::filter::scalar_topology::internal::ExchangeBranchEndsFunctor{});
auto exchangeBranchEndsFunctor =
vtkm::filter::scalar_topology::internal::ExchangeBranchEndsFunctor(this->TimingsLogLevel);
vtkmdiy::reduce(branch_decomposition_master, assigner, partners, exchangeBranchEndsFunctor);
timingsStream << " " << std::setw(38) << std::left << "ExchangeBranchEnds"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
std::vector<vtkm::cont::DataSet> outputDataSets(input.GetNumberOfPartitions());
// Copy input data set to output
@ -347,6 +357,15 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
b->VolumetricBranchDecomposer.LowerEndGRId);
outputDataSets[b->LocalBlockNo].AddField(LowerEndGRIdField);
vtkm::cont::Field UpperEndLocalIdField("UpperEndLocalIds",
vtkm::cont::Field::Association::WholeDataSet,
b->VolumetricBranchDecomposer.UpperEndLocalId);
outputDataSets[b->LocalBlockNo].AddField(UpperEndLocalIdField);
vtkm::cont::Field LowerEndLocalIdField("LowerEndLocalIds",
vtkm::cont::Field::Association::WholeDataSet,
b->VolumetricBranchDecomposer.LowerEndLocalId);
outputDataSets[b->LocalBlockNo].AddField(LowerEndLocalIdField);
vtkm::cont::Field UpperEndIntrinsicVolume(
"UpperEndIntrinsicVolume",
vtkm::cont::Field::Association::WholeDataSet,
@ -383,7 +402,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
vtkm::cont::Field::Association::WholeDataSet,
b->VolumetricBranchDecomposer.UpperEndValue);
outputDataSets[b->LocalBlockNo].AddField(UpperEndValue);
vtkm::cont::Field BranchRoot("BranchRoot",
vtkm::cont::Field BranchRoot("BranchRootByBranch",
vtkm::cont::Field::Association::WholeDataSet,
b->VolumetricBranchDecomposer.BranchRoot);
outputDataSets[b->LocalBlockNo].AddField(BranchRoot);
@ -391,13 +410,74 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
vtkm::cont::Field::Association::WholeDataSet,
b->VolumetricBranchDecomposer.BranchRootGRId);
outputDataSets[b->LocalBlockNo].AddField(BranchRootGRId);
/* To select top volume branches,
we need to carry over the input fields below to the output*/
using vtkm::worklet::contourtree_augmented::IdArrayType;
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
// RegularNodeGlobalIds
vtkm::cont::Field RegularNodeGlobalIds("RegularNodeGlobalIds",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("RegularNodeGlobalIds").GetData());
outputDataSets[b->LocalBlockNo].AddField(RegularNodeGlobalIds);
// DataValues
vtkm::cont::Field DataValues("DataValues",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("DataValues").GetData());
outputDataSets[b->LocalBlockNo].AddField(DataValues);
// Superparents
vtkm::cont::Field Superparents("Superparents",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Superparents").GetData());
outputDataSets[b->LocalBlockNo].AddField(Superparents);
// Supernodes
vtkm::cont::Field Supernodes("Supernodes",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Supernodes").GetData());
outputDataSets[b->LocalBlockNo].AddField(Supernodes);
// Superarcs
vtkm::cont::Field Superarcs("Superarcs",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Superarcs").GetData());
outputDataSets[b->LocalBlockNo].AddField(Superarcs);
// Superchildren
vtkm::cont::Field Superchildren("Superchildren",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Superchildren").GetData());
outputDataSets[b->LocalBlockNo].AddField(Superchildren);
// WhichRound
vtkm::cont::Field WhichRound("WhichRound",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("WhichRound").GetData());
outputDataSets[b->LocalBlockNo].AddField(WhichRound);
// WhichIteration
vtkm::cont::Field WhichIteration("WhichIteration",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("WhichIteration").GetData());
outputDataSets[b->LocalBlockNo].AddField(WhichIteration);
// Hyperparents
vtkm::cont::Field Hyperparents("Hyperparents",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Hyperparents").GetData());
outputDataSets[b->LocalBlockNo].AddField(Hyperparents);
// Hypernodes
vtkm::cont::Field Hypernodes("Hypernodes",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Hypernodes").GetData());
outputDataSets[b->LocalBlockNo].AddField(Hypernodes);
// Hyperarcs
vtkm::cont::Field Hyperarcs("Hyperarcs",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Hyperarcs").GetData());
outputDataSets[b->LocalBlockNo].AddField(Hyperarcs);
});
timingsStream << " " << std::setw(38) << std::left
<< "Creating Branch Decomposition Output Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
VTKM_LOG_S(vtkm::cont::LogLevel::Perf,
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< "----------- DoExecutePartitions Timings ------------" << std::endl
<< timingsStream.str());

@ -63,11 +63,18 @@ public:
const vtkm::cont::ArrayHandle<vtkm::Id3>&,
const vtkm::cont::ArrayHandle<vtkm::Id3>&,
const vtkm::cont::ArrayHandle<vtkm::Id3>&);
VTKM_CONT void SetTimingsLogLevel(vtkm::cont::LogLevel timingsLogLevel)
{
this->TimingsLogLevel = timingsLogLevel;
}
private:
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&) override;
VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& inData) override;
/// Log level to be used for outputting timing information. Default is vtkm::cont::LogLevel::Perf
vtkm::cont::LogLevel TimingsLogLevel = vtkm::cont::LogLevel::Perf;
};
} // namespace scalar_topology

@ -11,10 +11,11 @@
#include <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/cont/ErrorFilterExecution.h>
#include <vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h>
#include <vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.h>
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h>
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/DataSetMesh.h>
// vtkm includes
#include <vtkm/cont/Timer.h>
@ -43,10 +44,15 @@ VTKM_CONT vtkm::cont::DataSet SelectTopVolumeContoursFilter::DoExecute(const vtk
VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& input)
{
vtkm::cont::Timer timer;
timer.Start();
std::stringstream timingsStream;
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
int rank = comm.rank();
int size = comm.size();
using SelectTopVolumeContoursBlock =
vtkm::filter::scalar_topology::internal::SelectTopVolumeContoursBlock;
vtkmdiy::Master branch_top_volume_master(comm,
@ -55,6 +61,11 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut
0, // No create function
SelectTopVolumeContoursBlock::Destroy);
timingsStream << " " << std::setw(60) << std::left
<< "Create DIY Master and Assigner (Branch Selection)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
auto firstDS = input.GetPartition(0);
vtkm::Id3 firstPointDimensions, firstGlobalPointDimensions, firstGlobalPointIndexStart;
firstDS.GetCellSet().CastAndCallForTypes<VTKM_DEFAULT_CELL_SET_LIST_STRUCTURED>(
@ -76,6 +87,12 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut
globalNumberOfBlocks *= static_cast<int>(vtkmBlocksPerDimensionRP.Get(d));
}
// Record time to compute the local block ids
timingsStream << " " << std::setw(60) << std::left << "Get DIY Information (Branch Selection)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
vtkmdiy::DynamicAssigner assigner(comm, size, globalNumberOfBlocks);
for (vtkm::Id localBlockIndex = 0; localBlockIndex < input.GetNumberOfPartitions();
++localBlockIndex)
@ -94,86 +111,150 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut
assigner.set_rank(rank, globalBlockId);
}
// Log time to copy the data to the block data objects
timingsStream << " " << std::setw(60) << std::left << "Initialize Branch Selection Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
vtkmdiy::fix_links(branch_top_volume_master, assigner);
timingsStream << " " << std::setw(60) << std::left << "Fix DIY Links (Branch Selection)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
branch_top_volume_master.foreach (
[&](SelectTopVolumeContoursBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
using vtkm::worklet::contourtree_augmented::IdArrayType;
const auto& globalSize = firstGlobalPointDimensions;
vtkm::Id totalVolume = globalSize[0] * globalSize[1] * globalSize[2];
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
b->SortBranchByVolume(ds, totalVolume);
// copy the top volume branches into a smaller array
// we skip index 0 because it must be the main branch (which has the highest volume)
vtkm::Id nActualSavedBranches =
std::min(this->nSavedBranches, b->SortedBranchByVolume.GetNumberOfValues() - 1);
vtkm::worklet::contourtree_augmented::IdArrayType topVolumeBranch;
vtkm::cont::Algorithm::CopySubRange(
b->SortedBranchByVolume, 1, nActualSavedBranches, topVolumeBranch);
auto branchRootGRId =
ds.GetField("BranchRootGRId").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id>(
branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id>(
branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id>(
b->BranchVolume, topVolumeBranch, b->TopVolumeBranchVolume);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id>(
b->BranchSaddleEpsilon, topVolumeBranch, b->TopVolumeBranchSaddleEpsilon);
auto resolveArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
InArrayHandleType topVolBranchSaddleIsoValue;
vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex<InArrayHandleType>(
inArray, topVolumeBranch, topVolBranchSaddleIsoValue);
b->TopVolumeBranchSaddleIsoValue = topVolBranchSaddleIsoValue;
};
b->BranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
b->SelectLocalTopVolumeBranch(ds, this->GetSavedBranches());
});
timingsStream << " " << std::setw(60) << std::left << "SelectBranchByVolume"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// We apply all-to-all broadcast to collect the top nSavedBranches branches by volume
// TODO: change all_to_all to swap with partner? It does not seem necessary to use all_to_all
vtkmdiy::all_to_all(branch_top_volume_master,
assigner,
vtkm::filter::scalar_topology::internal::SelectTopVolumeContoursFunctor(
this->nSavedBranches, this->TimingsLogLevel));
timingsStream << " " << std::setw(60) << std::left << "SelectGlobalTopVolumeBranches"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// we compute the hierarchy of selected branches adding the root branch for each block
branch_top_volume_master.foreach (
[&](SelectTopVolumeContoursBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
b->ComputeTopVolumeBranchHierarchy(ds);
});
timingsStream << " " << std::setw(60) << std::left << "ComputeTopVolumeBranchHierarchy"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// We apply all-to-all broadcast to update the outer-most isovalue on all parent branches
vtkmdiy::all_to_all(
branch_top_volume_master,
assigner,
vtkm::filter::scalar_topology::internal::SelectTopVolumeContoursFunctor(this->nSavedBranches));
vtkm::filter::scalar_topology::internal::ParentBranchIsoValueFunctor(this->TimingsLogLevel));
timingsStream << " " << std::setw(60) << std::left << "Update Parent Branch IsoValue"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// For each block, we compute the get the extracted isosurface for every selected branch
// storing format: key (branch ID) - Value (list of meshes in the isosurface)
branch_top_volume_master.foreach ([&](SelectTopVolumeContoursBlock* b,
const vtkmdiy::Master::ProxyWithLink&) {
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
b->ExtractIsosurfaceOnSelectedBranch(ds, this->GetMarchingCubes(), this->GetTimingsLogLevel());
});
timingsStream << " " << std::setw(60) << std::left << "Draw Contours By Branches"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
std::vector<vtkm::cont::DataSet> outputDataSets(input.GetNumberOfPartitions());
branch_top_volume_master.foreach (
[&](SelectTopVolumeContoursBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
vtkm::cont::Field TopVolBranchGRIdField("TopVolumeBranchGlobalRegularIds",
vtkm::cont::Field::Association::WholeDataSet,
b->TopVolumeBranchRootGRId);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchGRIdField);
vtkm::cont::Field TopVolBranchVolumeField("TopVolumeBranchVolume",
branch_top_volume_master.foreach ([&](SelectTopVolumeContoursBlock* b,
const vtkmdiy::Master::ProxyWithLink&) {
vtkm::cont::Field BranchVolumeField(
"BranchVolume", vtkm::cont::Field::Association::WholeDataSet, b->tData.BranchVolume);
outputDataSets[b->LocalBlockNo].AddField(BranchVolumeField);
vtkm::cont::Field BranchSaddleEpsilonField("BranchSaddleEpsilon",
vtkm::cont::Field::Association::WholeDataSet,
b->tData.BranchSaddleEpsilon);
outputDataSets[b->LocalBlockNo].AddField(BranchSaddleEpsilonField);
vtkm::cont::Field TopVolBranchUpperEndField("TopVolumeBranchUpperEnd",
vtkm::cont::Field::Association::WholeDataSet,
b->TopVolumeBranchVolume);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchVolumeField);
vtkm::cont::Field TopVolBranchSaddleEpsilonField("TopVolumeBranchSaddleEpsilon",
vtkm::cont::Field::Association::WholeDataSet,
b->TopVolumeBranchSaddleEpsilon);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleEpsilonField);
b->tData.TopVolumeBranchUpperEndGRId);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchUpperEndField);
vtkm::cont::Field TopVolBranchLowerEndField("TopVolumeBranchLowerEnd",
vtkm::cont::Field::Association::WholeDataSet,
b->tData.TopVolumeBranchLowerEndGRId);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchLowerEndField);
vtkm::cont::Field TopVolumeBranchGRIdsField("TopVolumeBranchGlobalRegularIds",
vtkm::cont::Field::Association::WholeDataSet,
b->tData.TopVolumeBranchRootGRId);
outputDataSets[b->LocalBlockNo].AddField(TopVolumeBranchGRIdsField);
vtkm::cont::Field TopVolBranchVolumeField("TopVolumeBranchVolume",
vtkm::cont::Field::Association::WholeDataSet,
b->tData.TopVolumeBranchVolume);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchVolumeField);
vtkm::cont::Field TopVolBranchSaddleEpsilonField("TopVolumeBranchSaddleEpsilon",
vtkm::cont::Field::Association::WholeDataSet,
b->tData.TopVolumeBranchSaddleEpsilon);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleEpsilonField);
auto resolveArray = [&](const auto& inArray) {
vtkm::cont::Field TopVolBranchSaddleIsoValueField(
"TopVolumeBranchSaddleIsoValue", vtkm::cont::Field::Association::WholeDataSet, inArray);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleIsoValueField);
};
b->TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
});
vtkm::cont::Field IsosurfaceEdgeFromField(
"IsosurfaceEdgesFrom", vtkm::cont::Field::Association::WholeDataSet, b->IsosurfaceEdgesFrom);
outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeFromField);
vtkm::cont::Field IsosurfaceEdgeToField(
"IsosurfaceEdgesTo", vtkm::cont::Field::Association::WholeDataSet, b->IsosurfaceEdgesTo);
outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeToField);
vtkm::cont::Field IsosurfaceEdgeLabelField("IsosurfaceEdgesLabels",
vtkm::cont::Field::Association::WholeDataSet,
b->IsosurfaceEdgesLabels);
outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeLabelField);
vtkm::cont::Field IsosurfaceEdgeOffsetField("IsosurfaceEdgesOffset",
vtkm::cont::Field::Association::WholeDataSet,
b->IsosurfaceEdgesOffset);
outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeOffsetField);
vtkm::cont::Field IsosurfaceEdgeOrderField("IsosurfaceEdgesOrders",
vtkm::cont::Field::Association::WholeDataSet,
b->IsosurfaceEdgesOrders);
outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeOrderField);
vtkm::cont::Field IsosurfaceIsoValueField(
"IsosurfaceIsoValue", vtkm::cont::Field::Association::WholeDataSet, b->IsosurfaceIsoValue);
outputDataSets[b->LocalBlockNo].AddField(IsosurfaceIsoValueField);
auto resolveArray = [&](const auto& inArray) {
vtkm::cont::Field TopVolBranchSaddleIsoValueField(
"TopVolumeBranchSaddleIsoValue", vtkm::cont::Field::Association::WholeDataSet, inArray);
outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleIsoValueField);
};
b->tData.TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
});
timingsStream << " " << std::setw(38) << std::left << "Creating Branch Selection Output Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< "----------- DoExecutePartitions Timings ------------" << std::endl
<< timingsStream.str());
return vtkm::cont::PartitionedDataSet{ outputDataSets };
}

@ -44,7 +44,6 @@
#include <vtkm/filter/Filter.h>
#include <vtkm/filter/scalar_topology/vtkm_filter_scalar_topology_export.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
@ -52,28 +51,51 @@ namespace filter
{
namespace scalar_topology
{
/// \brief Compute branch decompostion from distributed contour tree
/// \brief Compute branch decompostion from distributed contour tree
class VTKM_FILTER_SCALAR_TOPOLOGY_EXPORT SelectTopVolumeContoursFilter : public vtkm::filter::Filter
{
public:
VTKM_CONT SelectTopVolumeContoursFilter() = default;
VTKM_CONT void SetTimingsLogLevel(vtkm::cont::LogLevel timingsLogLevel)
{
this->TimingsLogLevel = timingsLogLevel;
}
VTKM_CONT void SetSavedBranches(const vtkm::Id& numBranches)
{
this->nSavedBranches = numBranches;
}
VTKM_CONT void SetMarchingCubes(const bool& marchingCubes)
{
this->isMarchingCubes = marchingCubes;
}
VTKM_CONT vtkm::Id GetSavedBranches() { return this->nSavedBranches; }
VTKM_CONT bool GetMarchingCubes() { return this->isMarchingCubes; }
VTKM_CONT vtkm::cont::LogLevel GetTimingsLogLevel() { return this->TimingsLogLevel; }
private:
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&) override;
VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& inData) override;
bool isMarchingCubes = false;
vtkm::Id nSavedBranches;
vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume;
vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume;
vtkm::cont::ArrayHandle<vtkm::Id> BranchVolume;
vtkm::cont::ArrayHandle<vtkm::Id> BranchSaddleEpsilon;
vtkm::cont::ArrayHandle<vtkm::Id> SortedBranchByVolume;
vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue;
// the parent branch for top volume branches
// we only care about the parent branch for top volume branches at the moment
// as a result, the index stored in this array follows the descending order of branch volumes
vtkm::cont::ArrayHandle<vtkm::Id> TopVolBranchParent;
/// Log level to be used for outputting timing information. Default is vtkm::cont::LogLevel::Perf
vtkm::cont::LogLevel TimingsLogLevel = vtkm::cont::LogLevel::Perf;
};
} // namespace scalar_topology

@ -13,6 +13,7 @@ set(headers
SelectTopVolumeContoursBlock.h
ComputeBlockIndices.h
ComputeDistributedBranchDecompositionFunctor.h
ParentBranchExtremaFunctor.h
SelectTopVolumeContoursFunctor.h
ExchangeBranchEndsFunctor.h
)

@ -75,7 +75,7 @@ void ComputeDistributedBranchDecompositionFunctor::operator()(
) const
{
// Get our rank and DIY id
//const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
const auto selfid = rp.gid();
// Aliases to reduce verbosity
@ -110,6 +110,20 @@ void ComputeDistributedBranchDecompositionFunctor::operator()(
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestDownSupernode;
rp.dequeue(ingid, incomingBestDownSupernode);
std::stringstream dataSizeStream;
// Log the amount of exchanged data
dataSizeStream << " " << std::setw(38) << std::left << "Incoming data size"
<< ": " << incomingBestUpSupernode.GetNumberOfValues() << std::endl;
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ---------------- Compute Branch Decomposition Step ---------------------"
<< std::endl
<< " Rank : " << rank << std::endl
<< " DIY Id : " << selfid << std::endl
<< " Inc Id : " << ingid << std::endl
<< dataSizeStream.str());
vtkm::Id prefixLength = b->FirstSupernodePerIteration.ReadPortal().Get(rp.round() - 1)[0];
#ifdef DEBUG_PRINT

@ -73,10 +73,17 @@ namespace internal
struct ComputeDistributedBranchDecompositionFunctor
{
ComputeDistributedBranchDecompositionFunctor(const vtkm::cont::LogLevel& timingsLogLevel)
: TimingsLogLevel(timingsLogLevel)
{
}
void operator()(BranchDecompositionBlock* b,
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) const;
const vtkm::cont::LogLevel TimingsLogLevel;
};
} // namespace internal

@ -54,6 +54,7 @@
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/BranchEndGlobalUpdateWorklet.h>
#include <vtkm/Types.h>
#include <vtkm/cont/Logging.h>
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_COMBINED_BLOCK_IDS
@ -75,6 +76,7 @@ void ExchangeBranchEndsFunctor::operator()(
) const
{
// Get our rank and DIY id
const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
const auto selfid = rp.gid();
// Aliases to reduce verbosity
@ -93,6 +95,7 @@ void ExchangeBranchEndsFunctor::operator()(
// Otherwise, we may need to process more than one incoming block
if (ingid != selfid)
{
#ifdef DEBUG_PRINT_COMBINED_BLOCK_IDS
int incomingGlobalBlockId;
rp.dequeue(ingid, incomingGlobalBlockId);
@ -126,6 +129,20 @@ void ExchangeBranchEndsFunctor::operator()(
IdArrayType incomingLowerEndDependentVolume;
rp.dequeue(ingid, incomingLowerEndDependentVolume);
std::stringstream dataSizeStream;
// Log the amount of exchanged data
dataSizeStream << " " << std::setw(38) << std::left << "Incoming branch size"
<< ": " << incomingBranchRootGRId.GetNumberOfValues() << std::endl;
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ---------------- Exchange Branch Ends Step ---------------------"
<< std::endl
<< " Rank : " << rank << std::endl
<< " DIY Id : " << selfid << std::endl
<< " Inc Id : " << ingid << std::endl
<< dataSizeStream.str());
/// Superarc and Branch IDs are given based on the hierarchical level
/// Shared branches should lie on the smaller ID side of the branch array consecutively
/// We filter out shared branches first
@ -307,7 +324,8 @@ void ExchangeBranchEndsFunctor::operator()(
{
#ifdef DEBUG_PRINT_COMBINED_BLOCK_IDS
rp.enqueue(target, b->GlobalBlockId);
#endif
#endif // DEBUG_PRINT_COMBINED_BLOCK_IDS
rp.enqueue(target, branchDecomposer.BranchRootGRId);
rp.enqueue(target, branchDecomposer.UpperEndGRId);
rp.enqueue(target, branchDecomposer.LowerEndGRId);

@ -73,11 +73,19 @@ namespace internal
struct ExchangeBranchEndsFunctor
{
ExchangeBranchEndsFunctor(vtkm::cont::LogLevel timingsLogLevel = vtkm::cont::LogLevel::Perf)
: TimingsLogLevel(timingsLogLevel)
{
}
VTKM_CONT void operator()(
BranchDecompositionBlock* b,
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) const;
private:
vtkm::cont::LogLevel TimingsLogLevel;
};
} // namespace internal

@ -0,0 +1,355 @@
//============================================================================
// 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)
//==============================================================================
#include <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.h>
#include <vtkm/Types.h>
#include <iomanip>
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h>
#endif
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
namespace internal
{
void ParentBranchIsoValueFunctor::operator()(SelectTopVolumeContoursBlock* b,
const vtkmdiy::ReduceProxy& rp // communication proxy
) const
{
// Get our rank and
const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
const auto selfid = rp.gid();
// Aliases to reduce verbosity
using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType;
vtkm::cont::Invoker invoke;
if (rp.in_link().size() == 0)
{
for (int cc = 0; cc < rp.out_link().size(); ++cc)
{
auto target = rp.out_link().target(cc);
if (target.gid != selfid)
{
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
rp.enqueue(target, b->GlobalBlockId);
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Block " << b->GlobalBlockId << " enqueue to Block " << target.gid);
#endif
auto extraMaximaBranchOrderPortal = b->tData.ExtraMaximaBranchOrder.ReadPortal();
vtkm::Id nExtraMaxBranches = extraMaximaBranchOrderPortal.GetNumberOfValues();
rp.enqueue(target, nExtraMaxBranches);
auto resolveMaxArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
// the global order of the branch can be the identity (unique for each branch)
auto extraMaxBranchIsoValuePortal = inArray.ReadPortal();
for (vtkm::Id branch = 0; branch < nExtraMaxBranches; ++branch)
rp.enqueue(target, extraMaximaBranchOrderPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nExtraMaxBranches; ++branch)
rp.enqueue<ValueType>(target, extraMaxBranchIsoValuePortal.Get(branch));
};
if (b->tData.ExtraMaximaBranchIsoValue.GetNumberOfValues())
b->tData.ExtraMaximaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMaxArray);
// rp.enqueue(target, b->tData.ExtraMaximaBranchOrder);
// rp.enqueue(target, b->tData.ExtraMaximaBranchIsoValue);
auto extraMinimaBranchOrderPortal = b->tData.ExtraMinimaBranchOrder.ReadPortal();
vtkm::Id nExtraMinBranches = extraMinimaBranchOrderPortal.GetNumberOfValues();
rp.enqueue(target, nExtraMinBranches);
auto resolveMinArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
// the global order of the branch can be the identity (unique for each branch)
auto extraMinBranchIsoValuePortal = inArray.ReadPortal();
for (vtkm::Id branch = 0; branch < nExtraMinBranches; ++branch)
rp.enqueue(target, extraMinimaBranchOrderPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nExtraMinBranches; ++branch)
rp.enqueue<ValueType>(target, extraMinBranchIsoValuePortal.Get(branch));
};
if (b->tData.ExtraMinimaBranchIsoValue.GetNumberOfValues())
b->tData.ExtraMinimaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMinArray);
// rp.enqueue(target, b->tData.ExtraMinimaBranchOrder);
// rp.enqueue(target, b->tData.ExtraMinimaBranchIsoValue);
}
}
}
else
{
for (int i = 0; i < rp.in_link().size(); ++i)
{
int ingid = rp.in_link().target(i).gid;
if (ingid == selfid)
continue;
// copy incoming to the block
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
int incomingGlobalBlockId;
rp.dequeue(ingid, incomingGlobalBlockId);
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Combining local block " << b->GlobalBlockId << " with incoming block "
<< incomingGlobalBlockId);
#endif
vtkm::Id nSelfMaxBranch = b->tData.ExtraMaximaBranchOrder.GetNumberOfValues();
vtkm::Id nSelfMinBranch = b->tData.ExtraMinimaBranchOrder.GetNumberOfValues();
// dequeue the data from other blocks.
// nExtraMaximaBranches (incoming)
// array of incoming maxima branch order
// array of incoming maxima branch isovalue
// nExtraMinimaBranches (incoming)
// array of incoming minima branch order
// array of incoming minima branch isovalue
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
vtkm::Id nIncomingMaxBranch;
rp.dequeue(ingid, nIncomingMaxBranch);
auto resolveMaxArray = [&](auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
IdArrayType incomingMaxBranchOrder;
incomingMaxBranchOrder.Allocate(nIncomingMaxBranch);
auto incomingMaxBranchOrderPortal = incomingMaxBranchOrder.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMaxBranch; ++branch)
{
vtkm::Id incomingTmpBranchOrder;
rp.dequeue(ingid, incomingTmpBranchOrder);
incomingMaxBranchOrderPortal.Set(branch, incomingTmpBranchOrder);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingMaxBranchOrder);
InArrayHandleType incomingMaxBranchIsoValue;
incomingMaxBranchIsoValue.Allocate(nIncomingMaxBranch);
auto incomingMaxBranchIsoValuePortal = incomingMaxBranchIsoValue.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMaxBranch; ++branch)
{
ValueType incomingBranchValue;
rp.dequeue<ValueType>(ingid, incomingBranchValue);
incomingMaxBranchIsoValuePortal.Set(branch, incomingBranchValue);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue<InArrayHandleType>(ingid, incomingMaxBranchIsoValue);
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMaxBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingMaxBranchOrder", incomingMaxBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"incomingMaxBranchVal", incomingMaxBranchIsoValue, -1, rs);
vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfMaxBranchOrder", b->tData.ExtraMaximaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMaxBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
vtkm::cont::Algorithm::SortByKey(incomingMaxBranchOrder, incomingMaxBranchIsoValue);
vtkm::worklet::scalar_topology::select_top_volume_contours::UpdateOuterSaddle<true>
updateValueOnMaxBranch;
invoke(updateValueOnMaxBranch,
b->tData.ExtraMaximaBranchOrder,
inArray,
incomingMaxBranchOrder,
incomingMaxBranchIsoValue);
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
{
std::stringstream rs;
rs << "After update, block " << b->LocalBlockNo << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfMaxBranchOrder", b->tData.ExtraMaximaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMaxBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
};
if (nSelfMaxBranch > 0 && nIncomingMaxBranch > 0)
b->tData.ExtraMaximaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMaxArray);
/* Apply the same pipeline for branches with minima to extract */
vtkm::Id nIncomingMinBranch;
rp.dequeue(ingid, nIncomingMinBranch);
auto resolveMinArray = [&](auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
IdArrayType incomingMinBranchOrder;
incomingMinBranchOrder.Allocate(nIncomingMinBranch);
auto incomingMinBranchOrderPortal = incomingMinBranchOrder.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMinBranch; ++branch)
{
vtkm::Id incomingTmpBranchOrder;
rp.dequeue(ingid, incomingTmpBranchOrder);
incomingMinBranchOrderPortal.Set(branch, incomingTmpBranchOrder);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingMinBranchOrder);
InArrayHandleType incomingMinBranchIsoValue;
incomingMinBranchIsoValue.Allocate(nIncomingMinBranch);
auto incomingMinBranchIsoValuePortal = incomingMinBranchIsoValue.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMinBranch; ++branch)
{
ValueType incomingBranchValue;
rp.dequeue<ValueType>(ingid, incomingBranchValue);
incomingMinBranchIsoValuePortal.Set(branch, incomingBranchValue);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue<InArrayHandleType>(ingid, incomingMinBranchIsoValue);
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMinBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingMinBranchOrder", incomingMinBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"incomingMinBranchVal", incomingMinBranchIsoValue, -1, rs);
vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfMinBranchOrder", b->tData.ExtraMinimaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMinBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
vtkm::cont::Algorithm::SortByKey(incomingMinBranchOrder, incomingMinBranchIsoValue);
vtkm::worklet::scalar_topology::select_top_volume_contours::UpdateOuterSaddle<false>
updateValueOnMinBranch;
invoke(updateValueOnMinBranch,
b->tData.ExtraMinimaBranchOrder,
inArray,
incomingMinBranchOrder,
incomingMinBranchIsoValue);
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
{
std::stringstream rs;
rs << "After update, block " << b->LocalBlockNo << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfMinBranchOrder", b->tData.ExtraMinimaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMinBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
};
if (nSelfMinBranch > 0 && nIncomingMinBranch > 0)
b->tData.ExtraMinimaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMinArray);
// The logging is commented because the size of exchange is limited by K,
// the number of top-volume branches, which is usually small
std::stringstream dataSizeStream;
// Log the amount of exchanged data
dataSizeStream << " " << std::setw(38) << std::left << "Incoming branch size"
<< ": " << nIncomingMaxBranch + nIncomingMinBranch << std::endl;
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ---------------- Exchange Parent Branch Step ---------------------"
<< std::endl
<< " Rank : " << rank << std::endl
<< " DIY Id : " << selfid << std::endl
<< " Inc Id : " << ingid << std::endl
<< dataSizeStream.str());
}
}
}
} // namespace internal
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm

@ -0,0 +1,93 @@
//============================================================================
// 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)
//==============================================================================
#ifndef vtk_m_filter_scalar_topology_internal_ParentBranchExtremaFunctor_h
#define vtk_m_filter_scalar_topology_internal_ParentBranchExtremaFunctor_h
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h>
// clang-format off
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/diy/diy.h>
VTKM_THIRDPARTY_POST_INCLUDE
// clang-format on
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
namespace internal
{
struct ParentBranchIsoValueFunctor
{
ParentBranchIsoValueFunctor(const vtkm::cont::LogLevel& timingsLogLevel)
: TimingsLogLevel(timingsLogLevel)
{
}
void operator()(SelectTopVolumeContoursBlock* b,
const vtkmdiy::ReduceProxy& rp // communication proxy
) const;
const vtkm::cont::LogLevel TimingsLogLevel;
};
} // namespace internal
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm
#endif

@ -52,8 +52,16 @@
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/DataSetMesh.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchParentComparator.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/ClarifyBranchEndSupernodeTypeWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/MarchingCubesDataTables.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/Predicates.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/UpdateInfoByBranchDirectionWorklet.h>
#ifdef DEBUG_PRINT
@ -75,9 +83,8 @@ SelectTopVolumeContoursBlock::SelectTopVolumeContoursBlock(vtkm::Id localBlockNo
{
}
void SelectTopVolumeContoursBlock::SortBranchByVolume(
const vtkm::cont::DataSet& hierarchicalTreeDataSet,
const vtkm::Id totalVolume)
void SelectTopVolumeContoursBlock::SortBranchByVolume(const vtkm::cont::DataSet& bdDataSet,
const vtkm::Id totalVolume)
{
/// Pipeline to compute the branch volume
/// 1. check both ends of the branch. If both leaves, then main branch, volume = totalVolume
@ -91,25 +98,26 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
vtkm::cont::ArrayHandle<bool> isLowerLeaf;
vtkm::cont::ArrayHandle<bool> isUpperLeaf;
auto upperEndIntrinsicVolume = hierarchicalTreeDataSet.GetField("UpperEndIntrinsicVolume")
auto upperEndIntrinsicVolume = bdDataSet.GetField("UpperEndIntrinsicVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto upperEndDependentVolume = hierarchicalTreeDataSet.GetField("UpperEndDependentVolume")
auto upperEndDependentVolume = bdDataSet.GetField("UpperEndDependentVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto lowerEndIntrinsicVolume = hierarchicalTreeDataSet.GetField("LowerEndIntrinsicVolume")
auto lowerEndIntrinsicVolume = bdDataSet.GetField("LowerEndIntrinsicVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto lowerEndDependentVolume = hierarchicalTreeDataSet.GetField("LowerEndDependentVolume")
auto lowerEndDependentVolume = bdDataSet.GetField("LowerEndDependentVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto lowerEndSuperarcId = hierarchicalTreeDataSet.GetField("LowerEndSuperarcId")
auto lowerEndSuperarcId = bdDataSet.GetField("LowerEndSuperarcId")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto upperEndSuperarcId = hierarchicalTreeDataSet.GetField("UpperEndSuperarcId")
auto upperEndSuperarcId = bdDataSet.GetField("UpperEndSuperarcId")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto branchRoot = hierarchicalTreeDataSet.GetField("BranchRoot")
auto branchRoot = bdDataSet.GetField("BranchRootByBranch")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
@ -118,10 +126,9 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
vtkm::cont::Algorithm::Transform(
lowerEndIntrinsicVolume, lowerEndDependentVolume, isLowerLeaf, vtkm::Equal());
// NOTE: special cases (one-superarc branches) exist
// if the upper end superarc == lower end superarc == branch root superarc
// then it's probably not a leaf-leaf branch (Both equality has to be satisfied!)
// then it's probably not a leaf-leaf branch (Both equality have to be satisfied!)
// exception: the entire domain has only one superarc (intrinsic == dependent == total - 1)
// then it is a leaf-leaf branch
vtkm::cont::Invoker invoke;
@ -138,8 +145,7 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
isLowerLeaf,
isUpperLeaf);
vtkm::cont::UnknownArrayHandle upperEndValue =
hierarchicalTreeDataSet.GetField("UpperEndValue").GetData();
vtkm::cont::UnknownArrayHandle upperEndValue = bdDataSet.GetField("UpperEndValue").GetData();
// Based on the direction info of the branch, store epsilon direction and isovalue of the saddle
auto resolveArray = [&](const auto& inArray) {
@ -148,12 +154,12 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
vtkm::cont::ArrayHandle<ValueType> branchSaddleIsoValue;
branchSaddleIsoValue.Allocate(isLowerLeaf.GetNumberOfValues());
this->BranchSaddleEpsilon.Allocate(isLowerLeaf.GetNumberOfValues());
tData.BranchSaddleEpsilon.Allocate(isLowerLeaf.GetNumberOfValues());
vtkm::worklet::scalar_topology::select_top_volume_contours::UpdateInfoByBranchDirectionWorklet<
ValueType>
updateInfoWorklet;
auto lowerEndValue = hierarchicalTreeDataSet.GetField("LowerEndValue")
auto lowerEndValue = bdDataSet.GetField("LowerEndValue")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<ValueType>>();
@ -162,14 +168,15 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
isUpperLeaf,
inArray,
lowerEndValue,
this->BranchSaddleEpsilon,
tData.BranchSaddleEpsilon,
branchSaddleIsoValue);
this->BranchSaddleIsoValue = branchSaddleIsoValue;
tData.BranchSaddleIsoValue = branchSaddleIsoValue;
};
upperEndValue.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveArray);
// Compute the branch volume based on the upper/lower end superarc volumes
vtkm::worklet::contourtree_augmented::IdArrayType branchVolume;
vtkm::worklet::scalar_topology::select_top_volume_contours::GetBranchVolumeWorklet
getBranchVolumeWorklet(totalVolume);
@ -211,7 +218,7 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
VTKM_LOG_S(vtkm::cont::LogLevel::Info, resultStream.str());
#endif
vtkm::cont::Algorithm::Copy(branchVolume, this->BranchVolume);
vtkm::cont::Algorithm::Copy(branchVolume, tData.BranchVolume);
const vtkm::Id nBranches = lowerEndSuperarcId.GetNumberOfValues();
vtkm::cont::ArrayHandleIndex branchesIdx(nBranches);
@ -220,7 +227,687 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
// sort the branch volume
vtkm::cont::Algorithm::SortByKey(branchVolume, sortedBranches, vtkm::SortGreater());
vtkm::cont::Algorithm::Copy(sortedBranches, this->SortedBranchByVolume);
vtkm::cont::Algorithm::Copy(sortedBranches, tData.SortedBranchByVolume);
}
// Select the local top K branches by volume
void SelectTopVolumeContoursBlock::SelectLocalTopVolumeBranch(const vtkm::cont::DataSet& bdDataset,
const vtkm::Id nSavedBranches)
{
using vtkm::worklet::contourtree_augmented::IdArrayType;
// copy the top volume branches into a smaller array
// we skip index 0 because it must be the main branch (which has the highest volume)
vtkm::Id nActualSavedBranches =
std::min(nSavedBranches, tData.SortedBranchByVolume.GetNumberOfValues() - 1);
vtkm::worklet::contourtree_augmented::IdArrayType topVolumeBranch;
vtkm::cont::Algorithm::CopySubRange(
tData.SortedBranchByVolume, 1, nActualSavedBranches, topVolumeBranch);
auto branchRootByBranch = bdDataset.GetField("BranchRootByBranch")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
const vtkm::Id nBranches = branchRootByBranch.GetNumberOfValues();
auto branchRootGRId = bdDataset.GetField("BranchRootGRId")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto upperEndGRId = bdDataset.GetField("UpperEndGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto lowerEndGRId = bdDataset.GetField("LowerEndGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
vtkm::cont::Algorithm::Copy(branchRootByBranch, tData.BranchRootByBranch);
vtkm::cont::Algorithm::Copy(branchRootGRId, tData.BranchRootGRId);
// This seems weird, but we temporarily put the initialization of computing the branch decomposition tree here
tData.IsParentBranch.AllocateAndFill(nBranches, false);
// we permute all branch information to align with the order by volume
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
branchRootGRId, topVolumeBranch, tData.TopVolumeBranchRootGRId);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
upperEndGRId, topVolumeBranch, tData.TopVolumeBranchUpperEndGRId);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
lowerEndGRId, topVolumeBranch, tData.TopVolumeBranchLowerEndGRId);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
tData.BranchVolume, topVolumeBranch, tData.TopVolumeBranchVolume);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
tData.BranchSaddleEpsilon, topVolumeBranch, tData.TopVolumeBranchSaddleEpsilon);
auto resolveArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
InArrayHandleType topVolBranchSaddleIsoValue;
vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex<InArrayHandleType>(
inArray, topVolumeBranch, topVolBranchSaddleIsoValue);
tData.TopVolumeBranchSaddleIsoValue = topVolBranchSaddleIsoValue;
};
tData.BranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
}
void SelectTopVolumeContoursBlock::ComputeTopVolumeBranchHierarchy(
const vtkm::cont::DataSet& bdDataSet)
{
this->bdtMaker.ComputeTopVolumeBranchHierarchy(bdDataSet, this->tData);
}
void SelectTopVolumeContoursBlock::ExtractIsosurfaceOnSelectedBranch(
const vtkm::cont::DataSet& bdDataSet,
const bool isMarchingCubes,
const vtkm::cont::LogLevel timingsLogLevel)
{
using vtkm::worklet::contourtree_augmented::IdArrayType;
// if no branch to extract from
if (tData.TopVolumeBranchRootGRId.GetNumberOfValues() < 1)
return;
// Let's create a mesh dataset to extract all the contours first
// branch root global regular ID
// size: nBranches
// usage: identifier of the branch
vtkm::cont::Algorithm::Copy(
bdDataSet.GetField("BranchRootGRId").GetData().AsArrayHandle<IdArrayType>(),
tData.BranchRootGRId);
// branch local upper end and lower end
// size: nBranches
// usage: search for the superarc of an arbitrary point (not necessarily on grid)
auto upperEndLocalIds =
bdDataSet.GetField("UpperEndLocalIds").GetData().AsArrayHandle<IdArrayType>();
auto lowerEndLocalIds =
bdDataSet.GetField("LowerEndLocalIds").GetData().AsArrayHandle<IdArrayType>();
// global regular ids
auto globalRegularIds =
bdDataSet.GetField("RegularNodeGlobalIds").GetData().AsArrayHandle<IdArrayType>();
vtkm::Id3 globalPointDimensions;
vtkm::Id3 pointDimensions, globalPointIndexStart;
bdDataSet.GetCellSet().CastAndCallForTypes<VTKM_DEFAULT_CELL_SET_LIST_STRUCTURED>(
vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions(),
pointDimensions,
globalPointDimensions,
globalPointIndexStart);
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Block size info");
std::stringstream rs;
{
rs << "globalPointDimensions: " << globalPointDimensions << std::endl;
rs << "pointDimensions: " << pointDimensions << std::endl;
rs << "globalPointIndexStart: " << globalPointIndexStart << std::endl;
rs << "globalRegularIDs: " << globalRegularIds.GetNumberOfValues() << std::endl;
// ds.PrintSummary(rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
// Tool to relabel local mesh ids to global ids
auto localToGlobalIdRelabeler = vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler(
globalPointIndexStart, pointDimensions, globalPointDimensions);
IdArrayType globalIdsByMesh;
// Note: the cell set is different from the mesh structure
// Here, we assume that the cell set is structured grid
// A more general way to do this is to use CellSet().GetCellPointIds(i) to extract all the local ids and keep unique ones
// local ids in the mesh
IdArrayType localIdsByMesh;
vtkm::cont::Algorithm::Copy(
vtkm::cont::ArrayHandleIndex(pointDimensions[0] * pointDimensions[1] * pointDimensions[2]),
localIdsByMesh);
// then, we transform the local ids to global ids
auto localTransformToGlobalId =
vtkm::cont::make_ArrayHandleTransform(localIdsByMesh, localToGlobalIdRelabeler);
vtkm::cont::ArrayCopyDevice(localTransformToGlobalId, globalIdsByMesh);
// detect whether the element in globalRegularIds are in the block
// globalIdsDiscard is just a filler for the worklet format. We do not use it.
// The last slot for the worklet is useful in a later step.
// Here we just reuse the worklet
IdArrayType globalIdsWithinBlockStencil;
vtkm::cont::ArrayHandleDiscard<vtkm::Id> globalIdsDiscard;
globalIdsWithinBlockStencil.Allocate(globalRegularIds.GetNumberOfValues());
globalIdsDiscard.Allocate(globalRegularIds.GetNumberOfValues());
vtkm::cont::Invoker invoke;
// stencil is 1 if the global regular id is within the block, 0 otherwise
auto idxIfWithinBlock =
vtkm::worklet::scalar_topology::select_top_volume_contours::IdxIfWithinBlockWorklet();
invoke(idxIfWithinBlock,
globalRegularIds,
globalIdsByMesh,
globalIdsWithinBlockStencil,
globalIdsDiscard);
auto resolveArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
// we need to sort all values based on the global ids
// and remove values of points that do not belong to the local block
auto dataValues = bdDataSet.GetField("DataValues").GetData().AsArrayHandle<InArrayHandleType>();
IdArrayType globalIdsWithinBlock;
IdArrayType localIdsWithinBlock;
InArrayHandleType dataValuesWithinBlock;
// filter global regular ids, array ids, and data values
vtkm::cont::Algorithm::CopyIf(
globalRegularIds, globalIdsWithinBlockStencil, globalIdsWithinBlock);
vtkm::cont::Algorithm::CopyIf(
vtkm::cont::ArrayHandleIndex(globalRegularIds.GetNumberOfValues()),
globalIdsWithinBlockStencil,
localIdsWithinBlock);
vtkm::cont::Algorithm::CopyIf(dataValues, globalIdsWithinBlockStencil, dataValuesWithinBlock);
// sorted index based on global regular ids
IdArrayType sortedGlobalIds;
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(globalIdsByMesh.GetNumberOfValues()),
sortedGlobalIds);
vtkm::cont::Algorithm::SortByKey(globalIdsWithinBlock, sortedGlobalIds);
// globalIdsWithinBlock (sorted) and globalIdsByMesh should be identical.
// computing globalIdsWithinBlock ensures the input data is correct
bool identical =
globalIdsWithinBlock.GetNumberOfValues() == globalIdsByMesh.GetNumberOfValues();
if (identical)
{
vtkm::cont::ArrayHandle<bool> globalIdsIdentical;
vtkm::cont::Algorithm::Transform(
globalIdsWithinBlock, globalIdsByMesh, globalIdsIdentical, vtkm::Equal());
identical = vtkm::cont::Algorithm::Reduce(globalIdsIdentical, true, vtkm::LogicalAnd());
}
if (!identical)
{
vtkm::worklet::contourtree_augmented::PrintHeader(globalIdsByMesh.GetNumberOfValues());
vtkm::worklet::contourtree_augmented::PrintIndices("globalIdsByMesh", globalIdsByMesh);
vtkm::worklet::contourtree_augmented::PrintHeader(globalIdsWithinBlock.GetNumberOfValues());
vtkm::worklet::contourtree_augmented::PrintIndices("globalIdsWithinBlock",
globalIdsWithinBlock);
}
VTKM_ASSERT(identical);
// filtered and sorted local node info ids
// i.e. index of global regular ids, data values, and superparents
// Note: This is not local mesh id! Make sure to distinguish them
IdArrayType sortedLocalNodeInfoIdsWithinBlock;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
localIdsWithinBlock, sortedGlobalIds, sortedLocalNodeInfoIdsWithinBlock);
// sorted data values
InArrayHandleType sortedDataValuesWithinBlock;
vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex<InArrayHandleType>(
dataValuesWithinBlock, sortedGlobalIds, sortedDataValuesWithinBlock);
// create an execution object to find the superarc for an arbitrary point within the mesh
// all information below are required to initialize the execution object
auto superparents = bdDataSet.GetField("Superparents").GetData().AsArrayHandle<IdArrayType>();
auto supernodes = bdDataSet.GetField("Supernodes").GetData().AsArrayHandle<IdArrayType>();
auto superarcs = bdDataSet.GetField("Superarcs").GetData().AsArrayHandle<IdArrayType>();
auto superchildren = bdDataSet.GetField("Superchildren").GetData().AsArrayHandle<IdArrayType>();
auto whichRound = bdDataSet.GetField("WhichRound").GetData().AsArrayHandle<IdArrayType>();
auto whichIteration =
bdDataSet.GetField("WhichIteration").GetData().AsArrayHandle<IdArrayType>();
auto hyperparents = bdDataSet.GetField("Hyperparents").GetData().AsArrayHandle<IdArrayType>();
auto hypernodes = bdDataSet.GetField("Hypernodes").GetData().AsArrayHandle<IdArrayType>();
auto hyperarcs = bdDataSet.GetField("Hyperarcs").GetData().AsArrayHandle<IdArrayType>();
// filtered + sorted superparents of nodes
IdArrayType superparentsWithinBlock;
vtkm::cont::Algorithm::CopyIf(
superparents, globalIdsWithinBlockStencil, superparentsWithinBlock);
IdArrayType sortedSuperparentsWithinBlock;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
superparentsWithinBlock, sortedGlobalIds, sortedSuperparentsWithinBlock);
// initialize the exec object
// note: terms should include the contour tree as much as possible
// should pass the full arrays to the object instead of the filtered ones
auto findSuperarcForNode =
vtkm::worklet::contourtree_distributed::FindSuperArcForUnknownNode<ValueType>(
superparents,
supernodes,
superarcs,
superchildren,
whichRound,
whichIteration,
hyperparents,
hypernodes,
hyperarcs,
globalRegularIds,
dataValues);
// let's check which branches are known by the block
// We check the branchGRId of top volume branches to see whether there are matches within the block
vtkm::Id nIsoValues = inArray.GetNumberOfValues();
vtkm::Id totalNumPoints =
globalPointDimensions[0] * globalPointDimensions[1] * globalPointDimensions[2];
// dropping out top-volume branches that are not known by the block
// index of top-volume branches within the block among all top-volume branches
IdArrayType topVolBranchWithinBlockId;
vtkm::cont::Algorithm::CopyIf(vtkm::cont::ArrayHandleIndex(nIsoValues),
tData.TopVolBranchKnownByBlockStencil,
topVolBranchWithinBlockId);
auto topVolBranchWithinBlockIdPortal = topVolBranchWithinBlockId.ReadPortal();
vtkm::Id nTopVolBranchWithinBlock = topVolBranchWithinBlockId.GetNumberOfValues();
// filtered branch saddle values
InArrayHandleType isoValues;
vtkm::cont::Algorithm::CopyIf(inArray, tData.TopVolBranchKnownByBlockStencil, isoValues);
auto isoValuePortal = isoValues.ReadPortal();
// filtered branch saddle epsilons
IdArrayType topVolBranchSaddleEpsilons;
vtkm::cont::Algorithm::CopyIf(tData.TopVolumeBranchSaddleEpsilon,
tData.TopVolBranchKnownByBlockStencil,
topVolBranchSaddleEpsilons);
auto topVolBranchSaddleEpsilonPortal = topVolBranchSaddleEpsilons.ReadPortal();
// for each top-vol branch in the block
// we get their upper end and lower end local ids
IdArrayType topVolLocalBranchUpperEnd;
IdArrayType topVolLocalBranchLowerEnd;
vtkm::cont::ArrayHandle<bool> topVolIsParent;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
upperEndLocalIds, tData.TopVolBranchInfoActualIndex, topVolLocalBranchUpperEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
lowerEndLocalIds, tData.TopVolBranchInfoActualIndex, topVolLocalBranchLowerEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex<vtkm::cont::ArrayHandle<bool>>(
tData.IsParentBranch, tData.TopVolBranchInfoActualIndex, topVolIsParent);
auto topVolIsParentPortal = topVolIsParent.ReadPortal();
// we compute the superarc of the branch within the block
// around the given isovalue
IdArrayType branchIsoSuperarcs;
branchIsoSuperarcs.Allocate(nTopVolBranchWithinBlock);
auto branchIsoSuperarcWorklet =
vtkm::worklet::scalar_topology::select_top_volume_contours::GetSuperarcByIsoValueWorklet(
totalNumPoints);
invoke(branchIsoSuperarcWorklet,
topVolLocalBranchUpperEnd,
topVolLocalBranchLowerEnd,
isoValues,
topVolBranchSaddleEpsilons,
branchIsoSuperarcs,
findSuperarcForNode);
auto branchIsoSuperarcsPortal = branchIsoSuperarcs.ReadPortal();
#ifdef DEBUG_PRINT
std::stringstream branchStream;
branchStream << "Debug for branch info, Block " << this->LocalBlockNo << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(sortedBranchGRId.GetNumberOfValues(),
branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Raw Branch GR", tData.BranchRootGRId, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Raw Upper End", upperLocalEndIds, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Raw Lower End", lowerLocalEndIds, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Sorted Branch GR", sortedBranchGRId, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Sorted Branch Id", sortedBranchOrder, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintHeader(nIsoValues, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Top Branch GR", tData.TopVolumeBranchRootGRId, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Top Branch Stencil", topVolBranchWithinBlockStencil, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Top Branch Idx", topVolBranchInfoIdx, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintHeader(nTopVolBranchKnownByBlock, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Sorted Upper End", topVolBranchUpperLocalEnd, -1, branchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Sorted Lower End", topVolBranchLowerLocalEnd, -1, branchStream);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, branchStream.str());
#endif
const vtkm::Id nExtraMaximaBranch = tData.ExtraMaximaBranchLowerEnd.GetNumberOfValues();
const vtkm::Id nExtraMinimaBranch = tData.ExtraMinimaBranchLowerEnd.GetNumberOfValues();
InArrayHandleType extraMaximaBranchIsoValue;
InArrayHandleType extraMinimaBranchIsoValue;
IdArrayType extraMaximaBranchSuperarcs;
IdArrayType extraMinimaBranchSuperarcs;
extraMaximaBranchSuperarcs.Allocate(nExtraMaximaBranch);
extraMinimaBranchSuperarcs.Allocate(nExtraMinimaBranch);
if (nExtraMaximaBranch)
{
extraMaximaBranchIsoValue =
tData.ExtraMaximaBranchIsoValue.AsArrayHandle<InArrayHandleType>();
invoke(branchIsoSuperarcWorklet,
tData.ExtraMaximaBranchUpperEnd,
tData.ExtraMaximaBranchLowerEnd,
extraMaximaBranchIsoValue,
vtkm::cont::ArrayHandleConstant<vtkm::Id>(1, nExtraMaximaBranch),
extraMaximaBranchSuperarcs,
findSuperarcForNode);
#ifdef DEBUG_PRINT
std::stringstream extraMaxStream;
extraMaxStream << "Debug for Extra Maxima Branch, Block " << this->LocalBlockNo << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(nExtraMaximaBranch, extraMaxStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Upper End", tData.ExtraMaximaBranchUpperEnd, -1, extraMaxStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Lower End", tData.ExtraMaximaBranchLowerEnd, -1, extraMaxStream);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"Max Branch IsoValue", extraMaximaBranchIsoValue, -1, extraMaxStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Superarc", extraMaximaBranchSuperarcs, -1, extraMaxStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Order", tData.ExtraMaximaBranchOrder, -1, extraMaxStream);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, extraMaxStream.str());
#endif // DEBUG_PRINT
}
if (nExtraMinimaBranch)
{
extraMinimaBranchIsoValue =
tData.ExtraMinimaBranchIsoValue.AsArrayHandle<InArrayHandleType>();
invoke(branchIsoSuperarcWorklet,
tData.ExtraMinimaBranchUpperEnd,
tData.ExtraMinimaBranchLowerEnd,
extraMinimaBranchIsoValue,
vtkm::cont::ArrayHandleConstant<vtkm::Id>(-1, nExtraMinimaBranch),
extraMinimaBranchSuperarcs,
findSuperarcForNode);
#ifdef DEBUG_PRINT
std::stringstream extraMinStream;
extraMaxStream << "Debug for Extra Maxima Branch, Block " << this->LocalBlockNo << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(nExtraMinimaBranch, extraMinStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Upper End", tData.ExtraMinimaBranchUpperEnd, -1, extraMinStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Lower End", tData.ExtraMinimaBranchLowerEnd, -1, extraMinStream);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"Max Branch IsoValue", extraMinimaBranchIsoValue, -1, extraMinStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Superarc", extraMinimaBranchSuperarcs, -1, extraMinStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Max Branch Order", tData.ExtraMinimaBranchOrder, -1, extraMinStream);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, extraMinStream.str());
#endif // DEBUG_PRINT
}
auto extraMaximaBranchSuperarcPortal = extraMaximaBranchSuperarcs.ReadPortal();
auto extraMinimaBranchSuperarcPortal = extraMinimaBranchSuperarcs.ReadPortal();
auto extraMaximaBranchIsoValuePortal = extraMaximaBranchIsoValue.ReadPortal();
auto extraMinimaBranchIsoValuePortal = extraMinimaBranchIsoValue.ReadPortal();
auto extraMaximaBranchOrderPortal = tData.ExtraMaximaBranchOrder.ReadPortal();
auto extraMinimaBranchOrderPortal = tData.ExtraMinimaBranchOrder.ReadPortal();
const vtkm::Id nContours = nTopVolBranchWithinBlock + nExtraMaximaBranch + nExtraMinimaBranch;
this->IsosurfaceEdgesOffset.AllocateAndFill(nContours, 0);
this->IsosurfaceEdgesLabels.AllocateAndFill(nContours, 0);
this->IsosurfaceEdgesOrders.AllocateAndFill(nContours, 0);
InArrayHandleType isosurfaceIsoValue;
isosurfaceIsoValue.AllocateAndFill(nContours, ValueType(0));
auto edgeOffsetWritePortal = this->IsosurfaceEdgesOffset.WritePortal();
auto edgeLabelWritePortal = this->IsosurfaceEdgesLabels.WritePortal();
auto edgeOrderWritePortal = this->IsosurfaceEdgesOrders.WritePortal();
auto isosurfaceValuePortal = isosurfaceIsoValue.WritePortal();
vtkm::Id nContourCandidateMeshes = 0;
// NOTE: nContours denotes the number of isosurfaces for visualization.
// The number is usually small, so linear loop is not too costly.
// NOTE update 06/16/2024: We always need the isovalue of the contour.
// As a result, we iterate through nContours (=O(k)).
// This may be parallelizable in future work
for (vtkm::Id branchIdx = 0; branchIdx < nContours; branchIdx++)
{
ValueType isoValue;
vtkm::Id currBranchSaddleEpsilon, branchSuperarc, branchOrder, branchLabel = 0;
using vtkm::worklet::scalar_topology::select_top_volume_contours::BRANCH_SADDLE;
using vtkm::worklet::scalar_topology::select_top_volume_contours::BRANCH_COVER;
using vtkm::worklet::scalar_topology::select_top_volume_contours::MAXIMA_CONTOUR;
if (branchIdx < nTopVolBranchWithinBlock)
{
isoValue = isoValuePortal.Get(branchIdx);
currBranchSaddleEpsilon = topVolBranchSaddleEpsilonPortal.Get(branchIdx);
branchSuperarc = branchIsoSuperarcsPortal.Get(branchIdx);
branchOrder = topVolBranchWithinBlockIdPortal.Get(branchIdx) + 1;
branchLabel |= BRANCH_SADDLE;
branchLabel |= topVolIsParentPortal.Get(branchIdx) ? BRANCH_COVER : 0;
branchLabel |= currBranchSaddleEpsilon > 0 ? MAXIMA_CONTOUR : 0;
}
else if (branchIdx < nTopVolBranchWithinBlock + nExtraMaximaBranch)
{
const vtkm::Id idx = branchIdx - nTopVolBranchWithinBlock;
isoValue = extraMaximaBranchIsoValuePortal.Get(idx);
currBranchSaddleEpsilon = 1;
branchSuperarc = extraMaximaBranchSuperarcPortal.Get(idx);
branchOrder = extraMaximaBranchOrderPortal.Get(idx);
branchLabel |= MAXIMA_CONTOUR;
}
else
{
const vtkm::Id idx = branchIdx - nTopVolBranchWithinBlock - nExtraMaximaBranch;
VTKM_ASSERT(idx < nExtraMinimaBranch);
isoValue = extraMinimaBranchIsoValuePortal.Get(idx);
currBranchSaddleEpsilon = -1;
branchSuperarc = extraMinimaBranchSuperarcPortal.Get(idx);
branchOrder = extraMinimaBranchOrderPortal.Get(idx);
}
edgeOffsetWritePortal.Set(branchIdx, this->IsosurfaceEdgesFrom.GetNumberOfValues());
edgeLabelWritePortal.Set(branchIdx, branchLabel);
edgeOrderWritePortal.Set(branchIdx, branchOrder);
isosurfaceValuePortal.Set(branchIdx, isoValue);
if (vtkm::worklet::contourtree_augmented::NoSuchElement(branchSuperarc))
{
continue;
}
// Note: by concept, there is no 3D cell if pointDimensions[2] <= 1
vtkm::Id nCells = globalPointDimensions[2] <= 1
? (pointDimensions[0] - 1) * (pointDimensions[1] - 1)
: (pointDimensions[0] - 1) * (pointDimensions[1] - 1) * (pointDimensions[2] - 1);
// we get the polarity cases of cells
// we use lookup tables to for fast processing
IdArrayType vertexOffset = globalPointDimensions[2] <= 1
? vtkm::worklet::scalar_topology::select_top_volume_contours::vertexOffset2d
: vtkm::worklet::scalar_topology::select_top_volume_contours::vertexOffset3d;
IdArrayType edgeTable = globalPointDimensions[2] <= 1
? vtkm::worklet::scalar_topology::select_top_volume_contours::edgeTable2d
: (isMarchingCubes
? vtkm::worklet::scalar_topology::select_top_volume_contours::edgeTableMC3d
: vtkm::worklet::scalar_topology::select_top_volume_contours::edgeTableLT3d);
IdArrayType numBoundTable = globalPointDimensions[2] <= 1
? vtkm::worklet::scalar_topology::select_top_volume_contours::numLinesTable2d
: (isMarchingCubes
? vtkm::worklet::scalar_topology::select_top_volume_contours::numTrianglesTableMC3d
: vtkm::worklet::scalar_topology::select_top_volume_contours::numTrianglesTableLT3d);
IdArrayType boundaryTable = globalPointDimensions[2] <= 1
? vtkm::worklet::scalar_topology::select_top_volume_contours::lineTable2d
: (isMarchingCubes
? vtkm::worklet::scalar_topology::select_top_volume_contours::triTableMC3d
: vtkm::worklet::scalar_topology::select_top_volume_contours::triTableLT3d);
IdArrayType labelEdgeTable = isMarchingCubes
? vtkm::worklet::scalar_topology::select_top_volume_contours::labelEdgeTableMC3d
: vtkm::worklet::scalar_topology::select_top_volume_contours::labelEdgeTableLT3d;
IdArrayType caseCells;
caseCells.Allocate(nCells);
IdArrayType numEdgesInCell;
numEdgesInCell.Allocate(nCells);
auto caseCellsWorklet =
vtkm::worklet::scalar_topology::select_top_volume_contours::GetCellCasesWorklet<ValueType>(
pointDimensions, currBranchSaddleEpsilon, isoValue);
invoke(caseCellsWorklet,
vtkm::cont::ArrayHandleIndex(nCells),
sortedDataValuesWithinBlock,
vertexOffset,
caseCells);
// we compute the number of edges for each cell
// to initialize the array size of edges
IdArrayType numBoundariesInCell;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
numBoundTable, caseCells, numBoundariesInCell);
vtkm::cont::Algorithm::Transform(numBoundariesInCell,
globalPointDimensions[2] <= 1
? vtkm::cont::ArrayHandleConstant<vtkm::Id>(1, nCells)
: vtkm::cont::ArrayHandleConstant<vtkm::Id>(3, nCells),
numEdgesInCell,
vtkm::Multiply());
// use prefix sum to get the offset of the starting edge in each cell/cube
vtkm::Id nEdges =
vtkm::cont::Algorithm::Reduce<vtkm::Id, vtkm::Id>(numEdgesInCell, vtkm::Id(0));
nContourCandidateMeshes += globalPointDimensions[2] <= 1 ? nEdges : nEdges / 3;
IdArrayType edgesOffset;
vtkm::cont::Algorithm::ScanExclusive(numEdgesInCell, edgesOffset);
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> isosurfaceEdgesFrom;
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> isosurfaceEdgesTo;
// IdArrayType isosurfaceEdgesLabels;
IdArrayType isValidEdges;
isosurfaceEdgesFrom.Allocate(nEdges);
isosurfaceEdgesTo.Allocate(nEdges);
// isosurfaceEdgesLabels.Allocate(nEdges);
isValidEdges.Allocate(nEdges);
// draw isosurface
auto getEdgesInCellWorklet =
vtkm::worklet::scalar_topology::select_top_volume_contours::GetEdgesInCellWorklet<
ValueType>(pointDimensions,
globalPointIndexStart,
isoValue,
branchSuperarc,
currBranchSaddleEpsilon,
totalNumPoints,
isMarchingCubes);
invoke(getEdgesInCellWorklet,
edgesOffset,
caseCells,
sortedLocalNodeInfoIdsWithinBlock,
sortedDataValuesWithinBlock,
vertexOffset,
edgeTable,
numBoundTable,
boundaryTable,
labelEdgeTable,
isosurfaceEdgesFrom,
isosurfaceEdgesTo,
isValidEdges,
findSuperarcForNode);
// isValidEdges: stencil about whether the edge is on the desired superarc
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> validEdgesFrom;
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> validEdgesTo;
// we remove edges that are not on the desired superarc
vtkm::cont::Algorithm::CopyIf(isosurfaceEdgesFrom, isValidEdges, validEdgesFrom);
vtkm::cont::Algorithm::CopyIf(isosurfaceEdgesTo, isValidEdges, validEdgesTo);
// append edges into the result array
vtkm::Id nValidEdges = validEdgesFrom.GetNumberOfValues();
vtkm::Id nExistEdges = branchIdx == 0 ? 0 : this->IsosurfaceEdgesFrom.GetNumberOfValues();
this->IsosurfaceEdgesFrom.Allocate(nValidEdges + nExistEdges, vtkm::CopyFlag::On);
this->IsosurfaceEdgesTo.Allocate(nValidEdges + nExistEdges, vtkm::CopyFlag::On);
#ifdef DEBUG_PRINT
std::stringstream edgeInfoStream;
contourStream << "Debug for Contour Info, Block " << this->LocalBlockNo << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(nContours, edgeInfoStream);
vtkm::worklet::contourtree_augmented::PrintValues<vtkm::Id>(
"edgeOffset", this->IsosurfaceEdgesOffset, -1, edgeInfoStream);
vtkm::worklet::contourtree_augmented::PrintValues<vtkm::Id>(
"edgeOrders", this->IsosurfaceEdgesOrders, -1, edgeInfoStream);
vtkm::worklet::contourtree_augmented::PrintValues<vtkm::Id>(
"edgeLabels", this->IsosurfaceEdgesLabels, -1, edgeInfoStream);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, edgeInfoStream.str());
#endif // DEBUG_PRINT
vtkm::cont::Algorithm::CopySubRange(
validEdgesFrom, 0, nValidEdges, this->IsosurfaceEdgesFrom, nExistEdges);
vtkm::cont::Algorithm::CopySubRange(
validEdgesTo, 0, nValidEdges, this->IsosurfaceEdgesTo, nExistEdges);
#ifdef DEBUG_PRINT
std::stringstream contourStream;
contourStream << "Debug for Contour, Block " << this->LocalBlockNo << std::endl;
contourStream << "Branch Superarc = " << branchSuperarc << ", isoValue = " << isoValue
<< std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(nCells, contourStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Case of Cells", caseCells, -1, contourStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"# of Edges", numEdgesInCell, -1, contourStream);
vtkm::worklet::contourtree_augmented::PrintHeader(nEdges, contourStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Edge Stencil", isValidEdges, -1, contourStream);
vtkm::worklet::contourtree_augmented::PrintHeader(nValidEdges, contourStream);
vtkm::worklet::contourtree_augmented::PrintValues<vtkm::Vec3f_64>(
"EdgesFrom", validEdgesFrom, -1, contourStream);
vtkm::worklet::contourtree_augmented::PrintValues<vtkm::Vec3f_64>(
"EdgesTo", validEdgesTo, -1, contourStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"EdgesLabels", validEdgesLabels, -1, contourStream);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, contourStream.str());
#endif // DEBUG_PRINT
}
this->IsosurfaceIsoValue = isosurfaceIsoValue;
const vtkm::Id nMeshesOnBranches = globalPointDimensions[2] <= 1
? this->IsosurfaceEdgesFrom.GetNumberOfValues()
: this->IsosurfaceEdgesFrom.GetNumberOfValues() / 3;
VTKM_LOG_S(timingsLogLevel,
std::endl
<< "----------- Draw Isosurface (block=" << this->LocalBlockNo << ")------------"
<< std::endl
<< " " << std::setw(60) << std::left << "Number of Contours: " << nContours
<< std::endl
<< " " << std::setw(60) << std::left
<< "Number of Isosurface Meshes: " << nContourCandidateMeshes << std::endl
<< " " << std::setw(60) << std::left
<< "Number of Meshes On Branches: " << nMeshesOnBranches << std::endl);
};
tData.TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
}
} // namespace internal

@ -55,6 +55,8 @@
#include <vtkm/cont/DataSet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchDecompositionTreeMaker.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/TopVolumeBranchData.h>
namespace vtkm
{
@ -69,26 +71,76 @@ struct SelectTopVolumeContoursBlock
{
SelectTopVolumeContoursBlock(vtkm::Id localBlockNo, int globalBlockId);
void SortBranchByVolume(const vtkm::cont::DataSet& hierarchicalTreeDataSet,
const vtkm::Id totalVolume);
// Block metadata
vtkm::Id LocalBlockNo;
int GlobalBlockId; // TODO/FIXME: Check whether really needed. Possibly only during debugging
vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume;
vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume;
vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue;
TopVolumeBranchData tData;
// Output Datasets.
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRootGRId;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume;
vtkm::cont::UnknownArrayHandle TopVolumeBranchSaddleIsoValue;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchSaddleEpsilon;
// the factory class to compute the relation of top-volume branches
BranchDecompositionTreeMaker bdtMaker;
//vtkm::worklet::contourtree_augmented::IdArrayType BranchRootByBranch;
//vtkm::worklet::contourtree_augmented::IdArrayType BranchRootGRId;
//vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume;
//vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon;
//vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume;
//vtkm::cont::ArrayHandle<bool> IsParentBranch;
//vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue;
//// Output Datasets.
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRoot;
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRootGRId;
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume;
//// the parent branch of top volume branches (if no parent branch, then NO_SUCH_ELEMENT)
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchParent;
//vtkm::cont::UnknownArrayHandle TopVolumeBranchSaddleIsoValue;
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchSaddleEpsilon;
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchUpperEndGRId;
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchLowerEndGRId;
//// Other top-volume branch information
//// whether the top volume branch is known by the block
//// size: nTopVolBranches
//// value range: [0, 1]
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchKnownByBlockStencil;
//// the branch order (among all branches) by branch root global regular ids
//// size: nTopVolBranches
//// value range: {NO_SUCH_ELEMENT} U [0, nBranches - 1]
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchGROrder;
//// the branch information index (known by the block) of the top volume branch
//// size: nTopVolBranchKnownByBlock
//// value range: [0, nBranches - 1]
//vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchInfoActualIndex;
//// information to extract extra isosurface for extrema
//vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchUpperEnd;
//vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchLowerEnd;
//vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchOrder;
//vtkm::cont::UnknownArrayHandle ExtraMaximaBranchIsoValue;
//vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchUpperEnd;
//vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchLowerEnd;
//vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchOrder;
//vtkm::cont::UnknownArrayHandle ExtraMinimaBranchIsoValue;
// Isosurface output
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> IsosurfaceEdgesFrom;
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> IsosurfaceEdgesTo;
vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesOffset;
vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesLabels;
vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesOrders;
vtkm::cont::UnknownArrayHandle IsosurfaceIsoValue;
// Destroy function allowing DIY to own blocks and clean them up after use
static void Destroy(void* b) { delete static_cast<SelectTopVolumeContoursBlock*>(b); }
void SortBranchByVolume(const vtkm::cont::DataSet& bdDataSet, const vtkm::Id totalVolume);
void SelectLocalTopVolumeBranch(const vtkm::cont::DataSet& bdDataSet,
const vtkm::Id nSavedBranches);
void ComputeTopVolumeBranchHierarchy(const vtkm::cont::DataSet& bdDataSet);
void ExtractIsosurfaceOnSelectedBranch(const vtkm::cont::DataSet& bdDataSet,
const bool isMarchingCubes,
const vtkm::cont::LogLevel timingsLogLevel);
};
} // namespace internal

@ -49,7 +49,7 @@
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
#include <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
@ -57,6 +57,8 @@
#include <vtkm/Types.h>
#include <iomanip>
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h>
@ -79,7 +81,7 @@ void SelectTopVolumeContoursFunctor::operator()(
if (this->nSavedBranches < 1)
return;
// Get our rank and DIY id
//const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
const auto selfid = rp.gid();
// Aliases to reduce verbosity
@ -99,9 +101,11 @@ void SelectTopVolumeContoursFunctor::operator()(
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Block " << b->GlobalBlockId << " enqueue to Block " << target.gid);
#endif
auto topVolBranchRootGRIdPortal = b->TopVolumeBranchRootGRId.ReadPortal();
auto topVolBranchVolumePortal = b->TopVolumeBranchVolume.ReadPortal();
auto topVolBranchSaddleEpsilonPortal = b->TopVolumeBranchSaddleEpsilon.ReadPortal();
auto topVolBranchRootGRIdPortal = b->tData.TopVolumeBranchRootGRId.ReadPortal();
auto topVolBranchVolumePortal = b->tData.TopVolumeBranchVolume.ReadPortal();
auto topVolBranchSaddleEpsilonPortal = b->tData.TopVolumeBranchSaddleEpsilon.ReadPortal();
auto topVolBranchUpperEndPortal = b->tData.TopVolumeBranchUpperEndGRId.ReadPortal();
auto topVolBranchLowerEndPortal = b->tData.TopVolumeBranchLowerEndGRId.ReadPortal();
vtkm::Id nBranches = topVolBranchRootGRIdPortal.GetNumberOfValues();
@ -112,6 +116,10 @@ void SelectTopVolumeContoursFunctor::operator()(
rp.enqueue(target, topVolBranchVolumePortal.Get(branch));
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue(target, topVolBranchSaddleEpsilonPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue(target, topVolBranchUpperEndPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue(target, topVolBranchLowerEndPortal.Get(branch));
auto resolveArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
@ -120,11 +128,11 @@ void SelectTopVolumeContoursFunctor::operator()(
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue<ValueType>(target, topVolBranchSaddleIsoValuePortal.Get(branch));
};
b->TopVolumeBranchSaddleIsoValue
b->tData.TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
// rp.enqueue(target, b->TopVolumeBranchRootGRId);
// rp.enqueue(target, b->TopVolumeBranchVolume);
// rp.enqueue(target, b->tData.TopVolumeBranchRootGRId);
// rp.enqueue(target, b->tData.TopVolumeBranchVolume);
}
}
}
@ -199,9 +207,196 @@ void SelectTopVolumeContoursFunctor::operator()(
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingTopVolBranchSaddleEpsilon);
auto resolveArray = [&](auto& inArray) {
IdArrayType incomingTopVolBranchUpperEnd;
incomingTopVolBranchUpperEnd.Allocate(nIncoming);
auto incomingTopVolBranchUpperEndPortal = incomingTopVolBranchUpperEnd.WritePortal();
for (vtkm::Id branch = 0; branch < nIncoming; ++branch)
{
vtkm::Id incomingTmpBranchUpperEnd;
rp.dequeue(ingid, incomingTmpBranchUpperEnd);
incomingTopVolBranchUpperEndPortal.Set(branch, incomingTmpBranchUpperEnd);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingTopVolBranchUpperEnd);
IdArrayType incomingTopVolBranchLowerEnd;
incomingTopVolBranchLowerEnd.Allocate(nIncoming);
auto incomingTopVolBranchLowerEndPortal = incomingTopVolBranchLowerEnd.WritePortal();
for (vtkm::Id branch = 0; branch < nIncoming; ++branch)
{
vtkm::Id incomingTmpBranchLowerEnd;
rp.dequeue(ingid, incomingTmpBranchLowerEnd);
incomingTopVolBranchLowerEndPortal.Set(branch, incomingTmpBranchLowerEnd);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingTopVolBranchLowerEnd);
std::stringstream dataSizeStream;
// Log the amount of exchanged data
dataSizeStream << " " << std::setw(38) << std::left << "Incoming top volume branch size"
<< ": " << nIncoming << std::endl;
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ---------------- Select Top Volume Branches Step ---------------------"
<< std::endl
<< " Rank : " << rank << std::endl
<< " DIY Id : " << selfid << std::endl
<< " Inc Id : " << ingid << std::endl
<< dataSizeStream.str());
vtkm::Id nSelf = b->tData.TopVolumeBranchRootGRId.GetNumberOfValues();
#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "nIncoming = " << nIncoming << ", nSelf = " << nSelf);
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingTopBranchId", incomingTopVolBranchGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingTopBranchVol", incomingTopVolBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"incomingSaddleVal", incomingTopVolBranchSaddleIsoValue, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingUpperEnd", incomingTopVolBranchUpperEnd, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingLowerEnd", incomingTopVolBranchLowerEnd, -1, rs);
vtkm::worklet::contourtree_augmented::PrintHeader(nSelf, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchId", b->tData.TopVolumeBranchRootGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchVol", b->tData.TopVolumeBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfTopSaddleVal", inArray, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchUpperEnd", b->tData.TopVolumeBranchUpperEndGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchLowerEnd", b->tData.TopVolumeBranchLowerEndGRId, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
// merge incoming branches with self branches
IdArrayType mergedTopVolBranchGRId;
IdArrayType mergedTopVolBranchVolume;
IdArrayType mergedTopVolBranchSaddleEpsilon;
IdArrayType mergedTopVolBranchUpperEnd;
IdArrayType mergedTopVolBranchLowerEnd;
mergedTopVolBranchGRId.Allocate(nIncoming + nSelf);
mergedTopVolBranchVolume.Allocate(nIncoming + nSelf);
mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf);
mergedTopVolBranchUpperEnd.Allocate(nIncoming + nSelf);
mergedTopVolBranchLowerEnd.Allocate(nIncoming + nSelf);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchGRId, 0, nIncoming, mergedTopVolBranchGRId, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchVolume, 0, nIncoming, mergedTopVolBranchVolume, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchSaddleEpsilon, 0, nIncoming, mergedTopVolBranchSaddleEpsilon, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchUpperEnd, 0, nIncoming, mergedTopVolBranchUpperEnd, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchLowerEnd, 0, nIncoming, mergedTopVolBranchLowerEnd, 0);
/*vtkm::cont::Algorithm::CopySubRange<ValueType, ValueType>(
incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0);*/
vtkm::cont::Algorithm::CopySubRange(
b->tData.TopVolumeBranchRootGRId, 0, nSelf, mergedTopVolBranchGRId, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->tData.TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming);
vtkm::cont::Algorithm::CopySubRange(b->tData.TopVolumeBranchSaddleEpsilon,
0,
nSelf,
mergedTopVolBranchSaddleEpsilon,
nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->tData.TopVolumeBranchUpperEndGRId, 0, nSelf, mergedTopVolBranchUpperEnd, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->tData.TopVolumeBranchLowerEndGRId, 0, nSelf, mergedTopVolBranchLowerEnd, nIncoming);
// Sort all branches (incoming + self) based on volume
// sorting key: (volume, branch global regular ID)
// the highest volume comes first, the lowest branch GR ID comes first
vtkm::cont::ArrayHandleIndex mergedBranchId(nIncoming + nSelf);
IdArrayType sortedBranchId;
vtkm::cont::Algorithm::Copy(mergedBranchId, sortedBranchId);
vtkm::worklet::scalar_topology::select_top_volume_contours::BranchVolumeComparator
branchVolumeComparator(mergedTopVolBranchGRId, mergedTopVolBranchVolume);
vtkm::cont::Algorithm::Sort(sortedBranchId, branchVolumeComparator);
// permute the branch information based on sorting
IdArrayType permutedTopVolBranchGRId;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchGRId, sortedBranchId, permutedTopVolBranchGRId);
IdArrayType permutedTopVolBranchVolume;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchVolume, sortedBranchId, permutedTopVolBranchVolume);
IdArrayType permutedTopVolBranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon);
IdArrayType permutedTopVolBranchUpperEnd;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchUpperEnd, sortedBranchId, permutedTopVolBranchUpperEnd);
IdArrayType permutedTopVolBranchLowerEnd;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchLowerEnd, sortedBranchId, permutedTopVolBranchLowerEnd);
#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming + nSelf, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"permutedTopBranchId", permutedTopVolBranchGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"permutedTopBranchVol", permutedTopVolBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"permutedTopBranchUpperEnd", permutedTopVolBranchUpperEnd, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"permutedTopBranchLowerEnd", permutedTopVolBranchLowerEnd, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"permutedTopSaddleVal", permutedTopVolBranchSaddleIsoValue, -1, rs);
}
#endif
// there may be duplicate branches. We remove duplicate branches based on global regular IDs
// We can reuse the filter from removing duplicate branches in the process of collecting branches
IdArrayType oneIfUniqueBranch;
oneIfUniqueBranch.Allocate(nIncoming + nSelf);
vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
OneIfBranchEndWorklet oneIfUniqueWorklet;
invoke(oneIfUniqueWorklet, mergedBranchId, permutedTopVolBranchGRId, oneIfUniqueBranch);
// Remove duplicate
IdArrayType mergedUniqueBranchGRId;
IdArrayType mergedUniqueBranchVolume;
IdArrayType mergedUniqueBranchSaddleEpsilon;
IdArrayType mergedUniqueBranchUpperEnd;
IdArrayType mergedUniqueBranchLowerEnd;
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchGRId, oneIfUniqueBranch, mergedUniqueBranchGRId);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchVolume, oneIfUniqueBranch, mergedUniqueBranchVolume);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchSaddleEpsilon, oneIfUniqueBranch, mergedUniqueBranchSaddleEpsilon);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchUpperEnd, oneIfUniqueBranch, mergedUniqueBranchUpperEnd);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchLowerEnd, oneIfUniqueBranch, mergedUniqueBranchLowerEnd);
vtkm::Id nMergedUnique = mergedUniqueBranchGRId.GetNumberOfValues();
// We move all processing to the TopVolumeBranchSaddleIsoValue here
// to reduce the size of the function
auto resolveMerging = [&](auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
InArrayHandleType incomingTopVolBranchSaddleIsoValue;
incomingTopVolBranchSaddleIsoValue.Allocate(nIncoming);
auto incomingTopVolBranchSaddleIsoValuePortal =
@ -217,163 +412,87 @@ void SelectTopVolumeContoursFunctor::operator()(
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue<InArrayHandleType>(ingid, incomingTopVolBranchSaddleIsoValue);
vtkm::Id nSelf = b->TopVolumeBranchRootGRId.GetNumberOfValues();
#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"nIncoming = " << nIncoming << ", nSelf = " << nSelf);
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingTopBranchId", incomingTopVolBranchGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingTopBranchVol", incomingTopVolBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"incomingSaddleVal", incomingTopVolBranchSaddleIsoValue, -1, rs);
vtkm::worklet::contourtree_augmented::PrintHeader(nSelf, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchId", b->TopVolumeBranchRootGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchVol", b->TopVolumeBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfTopSaddleVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
// merge incoming branches with self branches
IdArrayType mergedTopVolBranchGRId;
IdArrayType mergedTopVolBranchVolume;
IdArrayType mergedTopVolBranchSaddleEpsilon;
InArrayHandleType mergedTopVolBranchSaddleIsoValue;
mergedTopVolBranchGRId.Allocate(nIncoming + nSelf);
mergedTopVolBranchVolume.Allocate(nIncoming + nSelf);
mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf);
mergedTopVolBranchSaddleIsoValue.Allocate(nIncoming + nSelf);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchGRId, 0, nIncoming, mergedTopVolBranchGRId, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchVolume, 0, nIncoming, mergedTopVolBranchVolume, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchSaddleEpsilon, 0, nIncoming, mergedTopVolBranchSaddleEpsilon, 0);
vtkm::cont::Algorithm::CopySubRange<ValueType, ValueType>(
incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0);
vtkm::cont::Algorithm::CopySubRange(
b->TopVolumeBranchRootGRId, 0, nSelf, mergedTopVolBranchGRId, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->TopVolumeBranchSaddleEpsilon, 0, nSelf, mergedTopVolBranchSaddleEpsilon, nIncoming);
vtkm::cont::Algorithm::CopySubRange<ValueType, ValueType>(
inArray, 0, nSelf, mergedTopVolBranchSaddleIsoValue, nIncoming);
// Sort all branches (incoming + self) based on volume
// sorting key: (volume, branch global regular ID)
// the highest volume comes first, the lowest branch GR ID comes first
vtkm::cont::ArrayHandleIndex mergedBranchId(nIncoming + nSelf);
IdArrayType sortedBranchId;
vtkm::cont::Algorithm::Copy(mergedBranchId, sortedBranchId);
vtkm::worklet::scalar_topology::select_top_volume_contours::BranchVolumeComparator
branchVolumeComparator(mergedTopVolBranchGRId, mergedTopVolBranchVolume);
vtkm::cont::Algorithm::Sort(sortedBranchId, branchVolumeComparator);
// permute the branch information based on sorting
IdArrayType permutedTopVolBranchGRId;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchGRId, sortedBranchId, permutedTopVolBranchGRId);
IdArrayType permutedTopVolBranchVolume;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchVolume, sortedBranchId, permutedTopVolBranchVolume);
IdArrayType permutedTopVolBranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon);
InArrayHandleType permutedTopVolBranchSaddleIsoValue;
vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex<InArrayHandleType>(
mergedTopVolBranchSaddleIsoValue, sortedBranchId, permutedTopVolBranchSaddleIsoValue);
#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming + nSelf, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"permutedTopBranchId", permutedTopVolBranchGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"permutedTopBranchVol", permutedTopVolBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"permutedTopSaddleVal", permutedTopVolBranchSaddleIsoValue, -1, rs);
}
#endif
// there may be duplicate branches. We remove duplicate branches based on global regular IDs
// We can reuse the filter from removing duplicate branches in the process of collecting branches
IdArrayType oneIfUniqueBranch;
oneIfUniqueBranch.Allocate(nIncoming + nSelf);
vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
OneIfBranchEndWorklet oneIfUniqueWorklet;
invoke(oneIfUniqueWorklet, mergedBranchId, permutedTopVolBranchGRId, oneIfUniqueBranch);
// Remove duplicate
IdArrayType mergedUniqueBranchGRId;
IdArrayType mergedUniqueBranchVolume;
IdArrayType mergedUniqueBranchSaddleEpsilon;
InArrayHandleType mergedUniqueBranchSaddleIsoValue;
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchGRId, oneIfUniqueBranch, mergedUniqueBranchGRId);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchVolume, oneIfUniqueBranch, mergedUniqueBranchVolume);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchSaddleEpsilon, oneIfUniqueBranch, mergedUniqueBranchSaddleEpsilon);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchSaddleIsoValue, oneIfUniqueBranch, mergedUniqueBranchSaddleIsoValue);
vtkm::Id nMergedUnique = mergedUniqueBranchGRId.GetNumberOfValues();
#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nMergedUnique, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"mergedUniqueBranchId", mergedUniqueBranchGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"mergedUniqueBranchVol", mergedUniqueBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"mergedUniqueSaddleVal", mergedUniqueBranchSaddleIsoValue, -1, rs);
}
#endif
// After removing duplicate, if there are more branches than we need
// We only save the top nSavedBranches branches
if (nMergedUnique > this->nSavedBranches)
{
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchGRId, 0, this->nSavedBranches, b->TopVolumeBranchRootGRId);
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchVolume, 0, this->nSavedBranches, b->TopVolumeBranchVolume);
vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchSaddleEpsilon,
0,
this->nSavedBranches,
b->TopVolumeBranchSaddleEpsilon);
// InArrayHandleType subRangeUniqueBranchSaddleIsoValue;
inArray.Allocate(this->nSavedBranches);
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchSaddleIsoValue, 0, this->nSavedBranches, inArray);
// inArray = subRangeUniqueBranchSaddleIsoValue;
}
else
{
vtkm::cont::Algorithm::Copy(mergedUniqueBranchGRId, b->TopVolumeBranchRootGRId);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->TopVolumeBranchVolume);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon,
b->TopVolumeBranchSaddleEpsilon);
inArray.Allocate(nMergedUnique);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleIsoValue, inArray);
}
};
b->TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveArray);
b->tData.TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(resolveMerging);
#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nMergedUnique, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"mergedUniqueBranchId", mergedUniqueBranchGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"mergedUniqueBranchVol", mergedUniqueBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"mergedUniqueBranchUpperEnd", mergedUniqueBranchUpperEnd, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"mergedUniqueBranchLowerEnd", mergedUniqueBranchLowerEnd, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"mergedUniqueSaddleVal", mergedUniqueBranchSaddleIsoValue, -1, rs);
}
#endif
// After removing duplicate, if there are more branches than we need
// We only save the top nSavedBranches branches
if (nMergedUnique > this->nSavedBranches)
{
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchGRId, 0, this->nSavedBranches, b->tData.TopVolumeBranchRootGRId);
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchVolume, 0, this->nSavedBranches, b->tData.TopVolumeBranchVolume);
vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchSaddleEpsilon,
0,
this->nSavedBranches,
b->tData.TopVolumeBranchSaddleEpsilon);
vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchUpperEnd,
0,
this->nSavedBranches,
b->tData.TopVolumeBranchUpperEndGRId);
vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchLowerEnd,
0,
this->nSavedBranches,
b->tData.TopVolumeBranchLowerEndGRId);
}
else
{
vtkm::cont::Algorithm::Copy(mergedUniqueBranchGRId, b->tData.TopVolumeBranchRootGRId);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->tData.TopVolumeBranchVolume);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon,
b->tData.TopVolumeBranchSaddleEpsilon);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchUpperEnd,
b->tData.TopVolumeBranchUpperEndGRId);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchLowerEnd,
b->tData.TopVolumeBranchLowerEndGRId);
}
}
}
}

@ -73,8 +73,9 @@ namespace internal
struct SelectTopVolumeContoursFunctor
{
SelectTopVolumeContoursFunctor(const vtkm::Id& nSB)
SelectTopVolumeContoursFunctor(const vtkm::Id& nSB, const vtkm::cont::LogLevel& timingsLogLevel)
: nSavedBranches(nSB)
, TimingsLogLevel(timingsLogLevel)
{
}
@ -83,6 +84,7 @@ struct SelectTopVolumeContoursFunctor
) const;
const vtkm::Id nSavedBranches;
const vtkm::cont::LogLevel TimingsLogLevel;
};
} // namespace internal

@ -182,6 +182,11 @@ public:
vtkm::worklet::contourtree_augmented::IdArrayType LowerEndIntrinsicVolume;
vtkm::worklet::contourtree_augmented::IdArrayType UpperEndDependentVolume;
vtkm::worklet::contourtree_augmented::IdArrayType LowerEndDependentVolume;
// This information is only used when extracting isosurfaces
// We need the upper and lower end within the block to determine the superarc containing the isovalue
// The information should NOT be exchanged between blocks, since it's local id
vtkm::worklet::contourtree_augmented::IdArrayType UpperEndLocalId;
vtkm::worklet::contourtree_augmented::IdArrayType LowerEndLocalId;
/// routines to compute branch decomposition by volume
/// WARNING: we now have two types of hierarchical tree sharing a data structure:
@ -507,11 +512,19 @@ inline void HierarchicalVolumetricBranchDecomposer::CollapseBranches(
hierarchicalTreeSuperarcs
};
// Get the number of rounds
auto numRoundsArray = hierarchicalTreeDataSet.GetField("NumRounds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
vtkm::Id numRounds = vtkm::cont::ArrayGetValue(0, numRoundsArray);
using vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
CollapseBranchesWorklet;
this->Invoke(CollapseBranchesWorklet{}, // the worklet
CollapseBranchesWorklet collapseBranchesWorklet(numRounds);
this->Invoke(collapseBranchesWorklet, // the worklet
this->BestUpSupernode, // input
this->BestDownSupernode, // input
hierarchicalTreeSuperarcs, // input
findRegularByGlobal, // input ExecutionObject
findSuperArcBetweenNodes, // input ExecutionObject
hierarchicalTreeRegular2Supernode, // input
@ -770,6 +783,8 @@ inline void HierarchicalVolumetricBranchDecomposer::CollectEndsOfBranches(
vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualBranchRoots);
auto permutedRegularIds =
vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualOuterNodeRegularIds);
auto permutedLocalIds =
vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualOuterNodeLocalIds);
auto permutedDataValues =
vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualOuterNodeValues);
auto permutedIntrinsicVolumes =
@ -817,6 +832,7 @@ inline void HierarchicalVolumetricBranchDecomposer::CollectEndsOfBranches(
{
vtkm::cont::Algorithm::CopyIf(permutedBranchRoots, oneIfBranchEnd, this->BranchRoot);
vtkm::cont::Algorithm::CopyIf(branchRootGRIds, oneIfBranchEnd, this->BranchRootGRId);
vtkm::cont::Algorithm::CopyIf(permutedLocalIds, oneIfBranchEnd, this->LowerEndLocalId);
vtkm::cont::Algorithm::CopyIf(
actualDirectedSuperarcs, oneIfBranchEnd, this->LowerEndSuperarcId);
vtkm::cont::Algorithm::CopyIf(permutedRegularIds, oneIfBranchEnd, this->LowerEndGRId);
@ -857,6 +873,7 @@ inline void HierarchicalVolumetricBranchDecomposer::CollectEndsOfBranches(
vtkm::cont::Algorithm::CopyIf(
actualDirectedSuperarcs, oneIfBranchEnd, this->UpperEndSuperarcId);
vtkm::cont::Algorithm::CopyIf(permutedRegularIds, oneIfBranchEnd, this->UpperEndGRId);
vtkm::cont::Algorithm::CopyIf(permutedLocalIds, oneIfBranchEnd, this->UpperEndLocalId);
vtkm::cont::Algorithm::CopyIf(
permutedIntrinsicVolumes, oneIfBranchEnd, this->UpperEndIntrinsicVolume);
vtkm::cont::Algorithm::CopyIf(

@ -65,6 +65,7 @@ public:
using ControlSignature = void(
FieldIn bestUpSupernode,
FieldIn bestDownSupernode,
FieldIn superarcs,
// Execution objects from the hierarchical tree to use the FindRegularByGlobal function
ExecObject findRegularByGlobal,
// Execution objects from the hierarchical tree to use the FindSuperArcBetweenNodes, function
@ -72,12 +73,15 @@ public:
WholeArrayIn hierarchicalTreeRegular2supernode,
WholeArrayIn hierarchicalTreeWhichRound,
WholeArrayInOut branchRoot);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8);
using InputDomain = _1;
/// Default Constructor
VTKM_EXEC_CONT
CollapseBranchesWorklet() {}
CollapseBranchesWorklet(vtkm::Id numRounds)
: NumRounds(numRounds)
{
}
/// operator() of the workelt
template <typename ExecObjectType1,
@ -88,6 +92,7 @@ public:
const vtkm::Id& supernode, // iteration index
const vtkm::Id& bestUpSupernodeId, // bestUpSupernode[supernode]
const vtkm::Id& bestDownSupernodeId, // bestDownSupernode[supernode]
const vtkm::Id& superarcsId, // hierarchicalTree.superarcs[supernode]
const ExecObjectType1& findRegularByGlobal, // Execution object to call FindRegularByGlobal
const ExecObjectType2&
findSuperArcBetweenNodes, // Execution object to call FindSuperArcBetweenNodes
@ -104,6 +109,18 @@ public:
// If it does exist and is an upwards superarc, then the current supernode must have an ascending arc to it, and we're done
// Also do the same for the best down, then for each supernode, point the higher numbered at the lower
// ADDED 19/07/2023
// If there are any attachment points left in the hierarchical tree, there is an extra edge case we need to deal with.
// It occurs when a supernode is simultaneously the target of an ascending superarc and a descending one
// What we do is to test for this here: if we are an attachment point, we omit connecting the best up and down
// ADDED 19/07/2023
// test for attachment points
if ((hierarchicalTreeWhichRoundPortal.Get(supernode) != this->NumRounds) &&
(vtkm::worklet::contourtree_augmented::NoSuchElement(superarcsId)))
{
return;
}
// if there is no best up, we're at an upper leaf and will not connect up two superarcs anyway, so we can skip the supernode
if (vtkm::worklet::contourtree_augmented::NoSuchElement(bestUpSupernodeId))
{
@ -233,6 +250,8 @@ public:
*/
} // operator()()
private:
vtkm::Id NumRounds;
}; // CollapseBranchesWorklet

@ -111,7 +111,7 @@ public:
using ControlSignature = void(
FieldIn superarcId, // (input) actual ID of superarc
WholeArrayIn branchRoots, // (array input) branch root (superarc) IDs of all superarcs
FieldOut branchEndIndicator // (output) 1 if
FieldOut branchEndIndicator // (output) 1 if the superarc is the last of a branch in the array
);
using ExecutionSignature = _3(_1, _2);
using InputDomain = _1;

@ -102,6 +102,7 @@
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/AttachmentSuperparentAndIndexComparator.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CopyBaseRegularStructureWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/HierarchicalAugmenterInOutData.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/IsAscendingDecorator.h>
@ -127,6 +128,11 @@ template <typename FieldType>
class HierarchicalAugmenter
{ // class HierarchicalAugmenter
public:
/// base mesh variable needs to determine whether a vertex is inside or outside of the block
vtkm::Id3 MeshBlockOrigin;
vtkm::Id3 MeshBlockSize;
vtkm::Id3 MeshGlobalSize;
/// the tree that it hypersweeps over
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* BaseTree;
/// the tree that it is building
@ -198,7 +204,10 @@ public:
void Initialize(
vtkm::Id blockId,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inBaseTree,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inAugmentedTree);
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inAugmentedTree,
vtkm::Id3 meshBlockOrigin,
vtkm::Id3 meshBockSize,
vtkm::Id3 meshGlobalSize);
/// routine to prepare the set of attachment points to transfer
void PrepareOutAttachmentPoints(vtkm::Id round);
@ -225,7 +234,7 @@ public:
void CopyBaseRegularStructure();
// subroutines for CopySuperstructure
/// gets a list of all the old supernodes to transfer at this level (ie except attachment points
/// gets a list of all the old supernodes to transfer at this level (i.e., except attachment points
void RetrieveOldSupernodes(vtkm::Id roundNumber);
/// resizes the arrays for the level
void ResizeArrays(vtkm::Id roundNumber);
@ -249,12 +258,18 @@ template <typename FieldType>
void HierarchicalAugmenter<FieldType>::Initialize(
vtkm::Id blockId,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* baseTree,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* augmentedTree)
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* augmentedTree,
vtkm::Id3 meshBlockOrigin,
vtkm::Id3 meshBockSize,
vtkm::Id3 meshGlobalSize)
{ // Initialize()
// copy the parameters for use
this->BlockId = blockId;
this->BaseTree = baseTree;
this->AugmentedTree = augmentedTree;
this->MeshBlockOrigin = meshBlockOrigin;
this->MeshBlockSize = meshBockSize;
this->MeshGlobalSize = meshGlobalSize;
// now construct a list of all attachment points on the block
// to do this, we construct an index array with all supernode ID's that satisfy:
@ -728,12 +743,13 @@ void HierarchicalAugmenter<FieldType>::CopyBaseRegularStructure()
vtkm::worklet::contourtree_augmented::IdArrayType tempRegularNodesNeeded;
// create the worklet
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet;
FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet(
this->MeshBlockOrigin, this->MeshBlockSize, this->MeshGlobalSize);
// Get a FindRegularByGlobal and FindSuperArcForUnknownNode execution object for our worklet
auto findRegularByGlobal = this->AugmentedTree->GetFindRegularByGlobal();
auto findSuperArcForUnknownNode = this->AugmentedTree->GetFindSuperArcForUnknownNode();
// excute the worklet
// execute the worklet
this->Invoke(findSuperparentForNecessaryNodesWorklet, // the worklet to call
// inputs
this->BaseTree->RegularNodeGlobalIds, // input domain
@ -831,6 +847,11 @@ void HierarchicalAugmenter<FieldType>::CopyBaseRegularStructure()
);
}
// Reset the number of regular nodes in round 0
vtkm::Id regularNodesInRound0 =
numTotalRegular - this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(1);
this->AugmentedTree->NumRegularNodesInRound.WritePortal().Set(0, regularNodesInRound0);
// Finally, we resort the regular node sort order
{
vtkm::worklet::contourtree_distributed::PermuteComparator // hierarchical_contour_tree::
@ -1218,6 +1239,7 @@ void HierarchicalAugmenter<FieldType>::CreateSuperarcs(vtkm::Id roundNumber)
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Super2Hypernode,
numSupernodesAlready,
this->SupernodeSorter.GetNumberOfValues());
// invoke the worklet
this->Invoke(createSuperarcsWorklet, // the worklet
this->SupernodeSorter, // input domain
@ -1297,6 +1319,19 @@ void HierarchicalAugmenter<FieldType>::CreateSuperarcs(vtkm::Id roundNumber)
// But there might be *NO* supernodes in the round, so we check first
vtkm::Id iterationArraySize =
vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations);
// This was added because in rare cases there are no supernodes transferred in an iteration, for example because there
// are no available upper leaves to prune. If this is case, we are guaranteed that there will be available lower leaves
// so the next iteration will have a non-zero number. We had a major bug from this, and it's cropped back up in the.
// Hierarchical Augmentation, so I'm expanding the comment just in case.
// Mingzhe: for any empty iteration, augmentedTree->FirstSupernodePerIteration[round] will be 0
// Fill the 0 out (except when it is leading) by its following number as necessary
// There should never be two consecutive zeros, so running it in parallel should be safe
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::FillEmptyIterationWorklet
fillEmptyIterationWorklet;
this->Invoke(fillEmptyIterationWorklet,
this->AugmentedTree->FirstSupernodePerIteration[roundNumber]);
if (iterationArraySize > 0)
{ // at least one iteration
vtkm::Id lastSupernodeThisLevel = this->AugmentedTree->Supernodes.GetNumberOfValues() - 1;

@ -110,6 +110,15 @@ public:
if (ingid != selfid)
{ // Receive and augment
rp.dequeue(ingid, blockData->HierarchicalAugmenter.InData);
vtkm::Id exchangeSize =
blockData->HierarchicalAugmenter.InData.Superparents.GetNumberOfValues();
exchangeSize =
std::max(exchangeSize,
blockData->HierarchicalAugmenter.InData.GlobalRegularIds.GetNumberOfValues());
timingsStream << " " << std::setw(38) << std::left << "Retrieved Attachment Points"
<< ": " << exchangeSize << std::endl;
blockData->HierarchicalAugmenter.RetrieveInAttachmentPoints();
}
}

@ -81,6 +81,7 @@
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/InitalizeSuperchildrenWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/PermuteComparator.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h>
namespace vtkm
{
@ -1024,9 +1025,18 @@ void HierarchicalContourTree<FieldType>::AddToVTKMDataSet(vtkm::cont::DataSet& d
vtkm::cont::Field superarcsField(
"Superarcs", vtkm::cont::Field::Association::WholeDataSet, this->Superarcs);
ds.AddField(superarcsField);
vtkm::cont::Field superchildrenField(
"Superchildren", vtkm::cont::Field::Association::WholeDataSet, this->Superchildren);
ds.AddField(superchildrenField);
vtkm::cont::Field hyperparentsField(
"Hyperparents", vtkm::cont::Field::Association::WholeDataSet, this->Hyperparents);
ds.AddField(hyperparentsField);
vtkm::cont::Field hypernodesField(
"Hypernodes", vtkm::cont::Field::Association::WholeDataSet, this->Hypernodes);
ds.AddField(hypernodesField);
vtkm::cont::Field hyperarcsField(
"Hyperarcs", vtkm::cont::Field::Association::WholeDataSet, this->Hyperarcs);
ds.AddField(hyperarcsField);
vtkm::cont::Field super2HypernodeField(
"Super2Hypernode", vtkm::cont::Field::Association::WholeDataSet, this->Super2Hypernode);
ds.AddField(super2HypernodeField);
@ -1054,6 +1064,13 @@ void HierarchicalContourTree<FieldType>::AddToVTKMDataSet(vtkm::cont::DataSet& d
ds.AddField(firstSupernodePerIterationOffsetsField);
// TODO/FIXME: It seems we may only need the counts for the first iteration, so check, which
// information we actually need.
// Add the number of rounds as an array of length 1
vtkm::cont::ArrayHandle<vtkm::Id> tempNumRounds;
tempNumRounds.Allocate(1);
vtkm::worklet::contourtree_augmented::IdArraySetValue(0, this->NumRounds, tempNumRounds);
vtkm::cont::Field numRoundsField(
"NumRounds", vtkm::cont::Field::Association::WholeDataSet, tempNumRounds);
ds.AddField(numRoundsField);
}
} // namespace contourtree_distributed

@ -200,7 +200,6 @@ private:
}; // class HierarchicalHyperSweeper
template <typename SweepValueType, typename ContourTreeFieldType>
HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::HierarchicalHyperSweeper(
vtkm::Id blockId,
@ -360,6 +359,7 @@ void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::LocalHyperS
// TODO/FIXME: Use portal? Or is there a more efficient way?
auto firstSupernodePerIterationPortal =
this->HierarchicalTree.FirstSupernodePerIteration[round].ReadPortal();
vtkm::Id firstSupernode = firstSupernodePerIterationPortal.Get(iteration);
vtkm::Id lastSupernode = firstSupernodePerIterationPortal.Get(iteration + 1);
@ -424,7 +424,6 @@ void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::
vtkm::Id lastSupernode)
{ // ComputeSuperarcDependentWeights()
vtkm::Id numSupernodesToProcess = lastSupernode - firstSupernode;
// 2. Use sorted prefix sum to compute the total weight to contribute to the super/hypertarget
// Same as std::partial_sum(sweepValues.begin() + firstSupernode, sweepValues.begin() + lastSupernode, valuePrefixSum.begin() + firstSupernode);
{
@ -442,7 +441,6 @@ void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::
vtkm::cont::Algorithm::ScanInclusive(dependentValuesView, // input
valuePrefixSumView); // result of partial sum
}
// Since the prefix sum is over *all* supernodes in the iteration, we need to break it into segments
// There are two cases we have to worry about:
// a. Hyperarcs made up of multiple supernodes

@ -22,6 +22,7 @@ set(headers
SetSuperparentSetDecorator.h
AttachmentAndSupernodeComparator.h
ResizeArraysBuildNewSupernodeIdsWorklet.h
FillEmptyIterationWorklet.h
CreateSuperarcsWorklet.h
HierarchicalAugmenterInOutData.h
)

@ -175,7 +175,6 @@ public:
// strip the ascending flag from the superparent.
vtkm::Id superparentOldSuperId =
vtkm::worklet::contourtree_augmented::MaskedIndex(superparentSetVal);
// setting the superarc is done the usual way. Our sort routine has ended up
// with the supernodes arranged in either ascending or descending order
// inwards along the parent superarc (as expressed by the superparent Id).
@ -237,8 +236,12 @@ public:
(superarcAscends ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00);
} // not last in the segment
// The following single line sets the first supernode for the iteration for each round
// Why does it execute separately for each supernode? This will kill speed with all the write collisions
// Commented out so that the location of the error is apparent
// It looks like this was set separately in ResizeArrays(), so it's also redundant!!!!
// set the first supernode in the first iteration to the beginning of the round
augmentedTreeFirstSupernodePerIterationPortal.Set(0, this->NumSupernodesAlready);
// augmentedTreeFirstSupernodePerIterationPortal.Set(0, this->NumSupernodesAlready);
// NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs

@ -0,0 +1,95 @@
//============================================================================
// 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.
//
//=============================================================================
// The PPP2 algorithm and software were jointly developed by
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_fill_empty_iteration_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_fill_empty_iteration_worklet_h
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace hierarchical_augmenter
{
// Worklet for a rare case where an iteration has no supernode
// need to update the FirstSupernodePerIteration array to avoid crash
class FillEmptyIterationWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
WholeArrayInOut augmentedTreeFirstSupernodePerIteration // input/output
);
using ExecutionSignature = void(InputIndex, _1);
using InputDomain = _1;
VTKM_EXEC_CONT
FillEmptyIterationWorklet() {}
/// operator() of the worklet
template <typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& inputIndex,
InOutFieldPortalType& firstSupernodePerIteration) const
{
if (inputIndex == 0 || inputIndex == firstSupernodePerIteration.GetNumberOfValues() - 1)
return;
if (firstSupernodePerIteration.Get(inputIndex) == 0)
{
vtkm::Id nextSupernode = firstSupernodePerIteration.Get(inputIndex + 1);
firstSupernodePerIteration.Set(inputIndex, nextSupernode);
}
} // operator()()
}; // FillEmptyIterationWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -83,7 +83,14 @@ public:
/// Default Constructor
VTKM_EXEC_CONT
FindSuperparentForNecessaryNodesWorklet() {}
FindSuperparentForNecessaryNodesWorklet(vtkm::Id3 meshBlockOrigin,
vtkm::Id3 meshBlockSize,
vtkm::Id3 meshGlobalSize)
: MeshBlockOrigin(meshBlockOrigin)
, MeshBlockSize(meshBlockSize)
, MeshGlobalSize(meshGlobalSize)
{
}
/// operator() of the workelt
template <typename InFieldPortalType,
@ -111,6 +118,17 @@ public:
// first check to see if it is already present (newRegularId set on input)
vtkm::Id newRegularId = findRegularByGlobal.FindRegularByGlobal(globalRegularId);
// WARNING: Mingzhe: the code commented out below comes from one of Oliver's fixes
// However, the code fails to work with isosurface extraction code.
// Many regular vertices are missing from the array with the code below
// Explicitly check whether the vertex belongs to the base block. If it doesn't, we ignore it
/* if (!this->IsInMesh(globalRegularId))
{
// Set to NO_SUCH_ELEMENT by default. By doing this in the worklet we an avoid having to
// initialize the output arrays first and we can use FieldIn instead of FieldInOut
regularSuperparentsValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
regularNodesNeededValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
} else */
// if it fails this test, then it's already in tree
if (vtkm::worklet::contourtree_augmented::NoSuchElement(newRegularId))
{ // not yet in tree
@ -200,7 +218,79 @@ public:
*/
} // operator()()
}; // FindSuperparentForNecessaryNodesWorklet
private:
// Mesh data
vtkm::Id3 MeshBlockOrigin;
vtkm::Id3 MeshBlockSize;
vtkm::Id3 MeshGlobalSize;
VTKM_EXEC
bool IsInMesh(vtkm::Id globalId) const
{ // IsInMesh()
if (this->MeshGlobalSize[2] > 1) // 3D
{
// convert from global ID to global coords
vtkm::Id globalSliceSize = this->MeshGlobalSize[0] * this->MeshGlobalSize[1];
vtkm::Id globalSlice = globalId / globalSliceSize;
vtkm::Id globalRow = globalId / this->MeshGlobalSize[0];
vtkm::Id globalCol = globalId % this->MeshGlobalSize[0];
// test validity
if (globalSlice < this->MeshBlockOrigin[2])
{
return false;
}
if (globalSlice >= this->MeshBlockOrigin[2] + this->MeshBlockSize[2])
{
return false;
}
if (globalRow < this->MeshBlockOrigin[1])
{
return false;
}
if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1])
{
return false;
}
if (globalCol < this->MeshBlockOrigin[0])
{
return false;
}
if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0])
{
return false;
}
// it's in the block - return true
return true;
} // end if 3D
else // 2D mesh
{
// convert from global ID to global coords
vtkm::Id globalRow = globalId / this->MeshGlobalSize[0];
vtkm::Id globalCol = globalId % this->MeshGlobalSize[0];
// test validity
if (globalRow < this->MeshBlockOrigin[1])
{
return false;
}
if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1])
{
return false;
}
if (globalCol < this->MeshBlockOrigin[0])
{
return false;
}
if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0])
{
return false;
}
// it's in the block - return true
return true;
}
} // IsInMesh()
}; // FindSuperparentForNecessaryNodesWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed

@ -118,6 +118,16 @@ public:
// the hyperparent which we need to search along
vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// sanity check: if above / below does not satisfy the condition, return NO_SUCH_ELEMENT
FieldType aboveValue = this->DataValues.Get(above);
FieldType belowValue = this->DataValues.Get(below);
vtkm::Id aboveGlobalId = this->RegularNodeGlobalIds.Get(above);
vtkm::Id belowGlobalId = this->RegularNodeGlobalIds.Get(below);
if (nodeValue > aboveValue || (nodeValue == aboveValue && nodeGlobalId > aboveGlobalId))
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
if (nodeValue < belowValue || (nodeValue == belowValue && nodeGlobalId < belowGlobalId))
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// to find the superarc, we will first have to convert the above / below to a pair of super/hypernodes
vtkm::Id aboveSuperparent = this->Superparents.Get(above);
vtkm::Id belowSuperparent = this->Superparents.Get(below);

@ -0,0 +1,508 @@
//============================================================================
// 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.
//
//=======================================================================================
//
// Parallel Peak Pruning v. 2.0
//
// Started June 15, 2017
//
// Copyright Hamish Carr, University of Leeds
//
// BranchDecompositionTreeMaker.h
//
//=======================================================================================
//
// COMMENTS:
//
// This class computes the branch decomposition tree of top-volume branches
//
//=======================================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_BranchDecompositionTreeMaker_h
#define vtk_m_filter_scalar_topology_worklet_BranchDecompositionTreeMaker_h
#ifdef DEBUG_PRINT
#define DEBUG_BRANCH_DECOMPOSITION_TREE_MAKER
#endif
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchParentComparator.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/ClarifyBranchEndSupernodeTypeWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/Predicates.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/TopVolumeBranchData.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/UpdateInfoByBranchDirectionWorklet.h>
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
/// Facture class for augmenting the hierarchical contour tree to enable computations of measures, e.g., volumne
class BranchDecompositionTreeMaker
{ // class BranchDecompositionTreeMaker
public:
void ComputeTopVolumeBranchHierarchy(const vtkm::cont::DataSet& bdDataSet,
TopVolumeBranchData& tData);
private:
/// Used internally to Invoke worklets
vtkm::cont::Invoker invoke;
}; // class BranchDecompositionTreeMaker
/// <summary>
/// Pipeline to compute the hierarchy of top branches by volume
/// </summary>
inline void BranchDecompositionTreeMaker::ComputeTopVolumeBranchHierarchy(
const vtkm::cont::DataSet& bdDataSet,
TopVolumeBranchData& tData)
{
using vtkm::worklet::contourtree_augmented::IdArrayType;
// we need upper/lower local ends and global ends for hierarchy of branches
auto upperLocalEndIds = bdDataSet.GetField("UpperEndLocalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto lowerLocalEndIds = bdDataSet.GetField("LowerEndLocalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto globalRegularIds = bdDataSet.GetField("RegularNodeGlobalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
IdArrayType upperEndGRIds =
bdDataSet.GetField("UpperEndGlobalRegularIds").GetData().AsArrayHandle<IdArrayType>();
IdArrayType lowerEndGRIds =
bdDataSet.GetField("LowerEndGlobalRegularIds").GetData().AsArrayHandle<IdArrayType>();
// let's check which top volume branches are known by the block
// We check the branchGRId of top volume branches to see whether there are matches within the block
const vtkm::Id nTopVolBranches = tData.TopVolumeBranchLowerEndGRId.GetNumberOfValues();
// sortedBranchOrder: the branch order (in the ascending order of branch root)
// the high-level idea is to sort the branch root global regular ids
// and for each top-volume branch, we use binary search to get the original branch index
// if the top-volume branch does not exist in the block, it will be dropped out
IdArrayType sortedBranchGRId;
IdArrayType sortedBranchOrder;
vtkm::cont::Algorithm::Copy(
vtkm::cont::ArrayHandleIndex(tData.BranchRootGRId.GetNumberOfValues()), sortedBranchOrder);
vtkm::cont::Algorithm::Copy(tData.BranchRootGRId, sortedBranchGRId);
vtkm::cont::Algorithm::SortByKey(sortedBranchGRId, sortedBranchOrder);
tData.TopVolBranchKnownByBlockStencil.Allocate(nTopVolBranches);
tData.TopVolBranchGROrder.Allocate(nTopVolBranches);
// We reuse the IdxIfWithinBlockWorklet.
// This worklet searches for given values in a sorted array and returns the stencil & index if the value exists in the array.
// tData.TopVolBranchGROrder: the order of the topVolBranch among all known branches if the branch is known by the block.
auto idxIfBranchWithinBlockWorklet =
vtkm::worklet::scalar_topology::select_top_volume_contours::IdxIfWithinBlockWorklet();
invoke(idxIfBranchWithinBlockWorklet,
tData.TopVolumeBranchRootGRId,
sortedBranchGRId,
tData.TopVolBranchKnownByBlockStencil,
tData.TopVolBranchGROrder);
// Dropping out top-volume branches that are not known by the block.
// the index of top-volume branches known by the block among all top-volume branches
IdArrayType topVolBranchKnownByBlockIndex;
vtkm::cont::ArrayHandleIndex topVolBranchesIndex(nTopVolBranches);
vtkm::cont::Algorithm::CopyIf(
topVolBranchesIndex, tData.TopVolBranchKnownByBlockStencil, topVolBranchKnownByBlockIndex);
const vtkm::Id nTopVolBranchKnownByBlock = topVolBranchKnownByBlockIndex.GetNumberOfValues();
// filtered tData.TopVolBranchGROrder, by removing NO_SUCH_ELEMENT
IdArrayType topVolBranchFilteredGROrder;
// tData.TopVolBranchInfoActualIndex: the information index of the top-volume branch
vtkm::cont::Algorithm::CopyIf(
tData.TopVolBranchGROrder, tData.TopVolBranchKnownByBlockStencil, topVolBranchFilteredGROrder);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
sortedBranchOrder, topVolBranchFilteredGROrder, tData.TopVolBranchInfoActualIndex);
// filtered branch saddle epsilons, global lower/upper end GR ids,
IdArrayType topVolFilteredBranchSaddleEpsilon;
IdArrayType topVolFilteredLowerEndGRId;
IdArrayType topVolFilteredUpperEndGRId;
vtkm::cont::Algorithm::CopyIf(tData.TopVolumeBranchSaddleEpsilon,
tData.TopVolBranchKnownByBlockStencil,
topVolFilteredBranchSaddleEpsilon);
vtkm::cont::Algorithm::CopyIf(tData.TopVolumeBranchUpperEndGRId,
tData.TopVolBranchKnownByBlockStencil,
topVolFilteredUpperEndGRId);
vtkm::cont::Algorithm::CopyIf(tData.TopVolumeBranchLowerEndGRId,
tData.TopVolBranchKnownByBlockStencil,
topVolFilteredLowerEndGRId);
// for each top-vol branch known by the block
// we get their upper end and lower end local ids
IdArrayType topVolBranchUpperLocalEnd;
IdArrayType topVolBranchLowerLocalEnd;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
upperLocalEndIds, tData.TopVolBranchInfoActualIndex, topVolBranchUpperLocalEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
lowerLocalEndIds, tData.TopVolBranchInfoActualIndex, topVolBranchLowerLocalEnd);
IdArrayType topVolLowerLocalEndGRId;
IdArrayType topVolUpperLocalEndGRId;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
globalRegularIds, topVolBranchLowerLocalEnd, topVolLowerLocalEndGRId);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
globalRegularIds, topVolBranchUpperLocalEnd, topVolUpperLocalEndGRId);
// Below is the code to compute the branch hierarchy of top-volume branches
// We need this information because we not only want to visualize the contour
// on top-volume branches, but also on their parent branches.
// Because we use volume as the metric, the parent branch of a top-volume branch
// is either a top-volume branch or the root branch (where both ends are leaf nodes)
vtkm::worklet::scalar_topology::select_top_volume_contours::BranchSaddleIsKnownWorklet
branchSaddleIsKnownWorklet;
// the branch saddle local ID if the saddle end is known by the block
IdArrayType branchSaddleIsKnown;
branchSaddleIsKnown.Allocate(nTopVolBranchKnownByBlock);
invoke(branchSaddleIsKnownWorklet, // worklet
topVolFilteredLowerEndGRId, // input
topVolBranchLowerLocalEnd, // input
topVolLowerLocalEndGRId, // input
topVolFilteredUpperEndGRId, // input
topVolBranchUpperLocalEnd, // input
topVolUpperLocalEndGRId, // input
topVolFilteredBranchSaddleEpsilon, // input
branchSaddleIsKnown); // output
// the order of top volume branches with parents known by the block
IdArrayType topVolChildBranch;
IdArrayType topVolChildBranchSaddle;
vtkm::cont::Algorithm::CopyIf(
topVolBranchKnownByBlockIndex,
branchSaddleIsKnown,
topVolChildBranch,
vtkm::worklet::scalar_topology::select_top_volume_contours::IsNonNegative());
vtkm::cont::Algorithm::CopyIf(
branchSaddleIsKnown,
branchSaddleIsKnown,
topVolChildBranchSaddle,
vtkm::worklet::scalar_topology::select_top_volume_contours::IsNonNegative());
const vtkm::Id nChildBranch = topVolChildBranch.GetNumberOfValues();
// to compute the parent branch, we need to
// 1. for the branch saddle end, collect all superarcs involving it
// 2. get the branch information for selected superarcs
// 3. eliminate branch information for branches sharing the same saddle end
auto superarcs =
bdDataSet.GetField("Superarcs").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto branchRoots =
bdDataSet.GetField("BranchRoots").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
VTKM_ASSERT(superarcs.GetNumberOfValues() == branchRoots.GetNumberOfValues());
// we sort all superarcs by target to allow binary search
IdArrayType superarcsByTarget;
vtkm::worklet::scalar_topology::select_top_volume_contours::SuperarcTargetComparator
superarcComparator(superarcs);
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(superarcs.GetNumberOfValues()),
superarcsByTarget);
vtkm::cont::Algorithm::Sort(superarcsByTarget, superarcComparator);
IdArrayType permutedSuperarcs;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
superarcs, superarcsByTarget, permutedSuperarcs);
IdArrayType permutedBranchRoots;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
branchRoots, superarcsByTarget, permutedBranchRoots);
// the branch root of the superarc of the branch saddle supernode
IdArrayType topVolChildBranchSaddleBranchRoot;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
branchRoots, topVolChildBranchSaddle, topVolChildBranchSaddleBranchRoot);
// the GR Ids of the superarc of the branch saddle supernode
IdArrayType topVolChildBranchSaddleGRIds;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
globalRegularIds, topVolChildBranchSaddle, topVolChildBranchSaddleGRIds);
// there is a debate to find all superarcs connect to a supernode
// strategy 1. iterate through saddles and parallelize over superarcs for search
// time complexity: O(nTopVolBranches)
// (nTopVolBranches usually <= 100, based on input parameter setting)
//
// strategy 2. parallelize over all saddles and use binary search to find superarcs
// time complexity: O(log_2(nSuperarcs)) (nSuperarcs can be considerably large)
//
// here, we choose strategy 2 for better extendability to high nTopVolBranches
// but in tests using nTopVolBranches <= 10, strategy 1 is generally faster
// note: after getting the branch root superarc, we use binary search to get the branch order
// because BranchRootByBranch is sorted by branch root (superarc) id
#ifdef DEBUG_PRINT
std::stringstream parentBranchStream;
vtkm::worklet::contourtree_augmented::PrintHeader(nChildBranch, parentBranchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Child Branch Saddle", topVolChildBranchSaddle, -1, parentBranchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Child Saddle Root", topVolChildBranchSaddleBranchRoot, -1, parentBranchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Child Saddle GR Id", topVolChildBranchSaddleGRIds, -1, parentBranchStream);
vtkm::worklet::contourtree_augmented::PrintHeader(superarcs.GetNumberOfValues(),
parentBranchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Permuted Superarcs", permutedSuperarcs, -1, parentBranchStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Permuted Branch roots", permutedBranchRoots, -1, parentBranchStream);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, parentBranchStream.str());
#endif // DEBUG_PRINT
// the corresponding parent branch of child branches
IdArrayType topVolChildBranchParent;
topVolChildBranchParent.AllocateAndFill(nChildBranch,
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
vtkm::worklet::scalar_topology::select_top_volume_contours::GetParentBranchWorklet
getParentBranchWorklet;
invoke(getParentBranchWorklet,
topVolChildBranchSaddle,
topVolChildBranchSaddleBranchRoot,
topVolChildBranchSaddleGRIds,
permutedSuperarcs,
permutedBranchRoots,
tData.BranchRootByBranch,
upperEndGRIds,
lowerEndGRIds,
topVolChildBranchParent);
tData.TopVolumeBranchParent.AllocateAndFill(
nTopVolBranches, vtkm::Id(vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT));
vtkm::worklet::scalar_topology::select_top_volume_contours::AssignValueByIndex assignParentBranch;
// for each top volume branch, assign the parent branch info id in the block
invoke(
assignParentBranch, topVolChildBranch, topVolChildBranchParent, tData.TopVolumeBranchParent);
// for each branch, assign true if it is a parent branch
invoke(assignParentBranch,
topVolChildBranchParent,
vtkm::cont::ArrayHandleConstant<bool>(true, nChildBranch),
tData.IsParentBranch);
// sort all top-volume branches based on
// 1. parent branch info id: tData.TopVolumeBranchParent
// 2. saddle-end value: tData.TopVolumeBranchSaddleIsovalue
// 3. branch root global regular id (anything that can break tie)
IdArrayType topVolSortForOuterSaddleIdx;
vtkm::cont::Algorithm::Copy(topVolBranchesIndex, topVolSortForOuterSaddleIdx);
auto resolveBranchParentSorter = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
vtkm::worklet::scalar_topology::select_top_volume_contours::BranchParentComparator<ValueType>
parentComparator(tData.TopVolumeBranchParent, inArray, tData.TopVolumeBranchRootGRId);
// sort index for all top volume branches
vtkm::cont::Algorithm::Sort(topVolSortForOuterSaddleIdx, parentComparator);
};
tData.TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveBranchParentSorter);
IdArrayType parentPermutation;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
tData.TopVolumeBranchParent, topVolSortForOuterSaddleIdx, parentPermutation);
// warning: when parent is NO_SUCH_ELEMENT, parentSaddleEps obtains 0
// However, the corresponding element will be discarded in collecting outer saddles
IdArrayType parentSaddleEpsPermutation;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
tData.BranchSaddleEpsilon, parentPermutation, parentSaddleEpsPermutation);
// Some branches have parent=NO_SUCH_ELEMENT (no parent)
// we collect the isovalue of the first and/or the last branches for each parent branch
// we collect the first if branchSaddleEpsilon(parent) < 0
// or the last if branchSaddleEpsilon(parent) > 0
// or both if branchSaddleEpsilon(parent) == 0
IdArrayType IsOuterSaddle;
IsOuterSaddle.Allocate(nTopVolBranches);
vtkm::worklet::scalar_topology::select_top_volume_contours::CollectOuterSaddle
collectOuterSaddleWorklet;
invoke(collectOuterSaddleWorklet, parentSaddleEpsPermutation, parentPermutation, IsOuterSaddle);
// after sorting by index back
// each top volume branch know whether it is the outer saddle of its parent
vtkm::cont::Algorithm::SortByKey(topVolSortForOuterSaddleIdx, IsOuterSaddle);
// collect branches that need contours on extra minima/maxima
// we store the information of the parent branches (on both directions)
IdArrayType extraMaximaParentBranch;
IdArrayType extraMinimaParentBranch;
IdArrayType extraMaximaParentBranchRootGRId;
IdArrayType extraMinimaParentBranchRootGRId;
IdArrayType allBranchGRIdByVolume;
IdArrayType branchGRIdByVolumeIdx;
// we need global branch order including the root branch
// this information should be consistent globally
allBranchGRIdByVolume.Allocate(nTopVolBranches + 1);
vtkm::cont::Algorithm::CopySubRange(
tData.TopVolumeBranchRootGRId, 0, nTopVolBranches, allBranchGRIdByVolume, 1);
auto topBranchGRIdWritePortal = allBranchGRIdByVolume.WritePortal();
auto sortedBranchByVolPortal = tData.SortedBranchByVolume.ReadPortal();
auto branchGRIdReadPortal = tData.BranchRootGRId.ReadPortal();
topBranchGRIdWritePortal.Set(0, branchGRIdReadPortal.Get(sortedBranchByVolPortal.Get(0)));
vtkm::cont::Algorithm::Copy(
vtkm::cont::ArrayHandleIndex(allBranchGRIdByVolume.GetNumberOfValues()), branchGRIdByVolumeIdx);
vtkm::cont::Algorithm::SortByKey(allBranchGRIdByVolume, branchGRIdByVolumeIdx);
vtkm::cont::Algorithm::CopyIf(
tData.TopVolumeBranchParent,
IsOuterSaddle,
extraMaximaParentBranch,
vtkm::worklet::scalar_topology::select_top_volume_contours::IsExtraMaxima());
vtkm::cont::Algorithm::CopyIf(
tData.TopVolumeBranchParent,
IsOuterSaddle,
extraMinimaParentBranch,
vtkm::worklet::scalar_topology::select_top_volume_contours::IsExtraMinima());
if (extraMaximaParentBranch.GetNumberOfValues())
{
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
upperLocalEndIds, extraMaximaParentBranch, tData.ExtraMaximaBranchUpperEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
lowerLocalEndIds, extraMaximaParentBranch, tData.ExtraMaximaBranchLowerEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
tData.BranchRootGRId, extraMaximaParentBranch, extraMaximaParentBranchRootGRId);
// a worklet to binary search a number in a sorted array and return the index
vtkm::worklet::scalar_topology::select_top_volume_contours::IdxIfWithinBlockWorklet
getParentBranchOrder;
IdArrayType permutedExtraMaximaBranchOrder;
permutedExtraMaximaBranchOrder.Allocate(extraMaximaParentBranchRootGRId.GetNumberOfValues());
vtkm::cont::ArrayHandleDiscard<vtkm::Id> discard;
discard.Allocate(extraMaximaParentBranchRootGRId.GetNumberOfValues());
invoke(getParentBranchOrder,
extraMaximaParentBranchRootGRId,
allBranchGRIdByVolume,
discard,
permutedExtraMaximaBranchOrder);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
branchGRIdByVolumeIdx, permutedExtraMaximaBranchOrder, tData.ExtraMaximaBranchOrder);
}
if (extraMinimaParentBranch.GetNumberOfValues())
{
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
upperLocalEndIds, extraMinimaParentBranch, tData.ExtraMinimaBranchUpperEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
lowerLocalEndIds, extraMinimaParentBranch, tData.ExtraMinimaBranchLowerEnd);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
tData.BranchRootGRId, extraMinimaParentBranch, extraMinimaParentBranchRootGRId);
vtkm::worklet::scalar_topology::select_top_volume_contours::IdxIfWithinBlockWorklet
getParentBranchOrder;
IdArrayType permutedExtraMinimaBranchOrder;
permutedExtraMinimaBranchOrder.Allocate(extraMinimaParentBranchRootGRId.GetNumberOfValues());
vtkm::cont::ArrayHandleDiscard<vtkm::Id> discard;
discard.Allocate(extraMinimaParentBranchRootGRId.GetNumberOfValues());
invoke(getParentBranchOrder,
extraMinimaParentBranchRootGRId,
allBranchGRIdByVolume,
discard,
permutedExtraMinimaBranchOrder);
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
branchGRIdByVolumeIdx, permutedExtraMinimaBranchOrder, tData.ExtraMinimaBranchOrder);
}
// Update saddle isovalues for extra contours
auto resolveExtraContourSaddleValue = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
if (extraMaximaParentBranch.GetNumberOfValues())
{
InArrayHandleType extraMaximaBranchIsoValue;
vtkm::cont::Algorithm::CopyIf(
inArray,
IsOuterSaddle,
extraMaximaBranchIsoValue,
vtkm::worklet::scalar_topology::select_top_volume_contours::IsExtraMaxima());
tData.ExtraMaximaBranchIsoValue = extraMaximaBranchIsoValue;
}
if (extraMinimaParentBranch.GetNumberOfValues())
{
InArrayHandleType extraMinimaBranchIsoValue;
vtkm::cont::Algorithm::CopyIf(
inArray,
IsOuterSaddle,
extraMinimaBranchIsoValue,
vtkm::worklet::scalar_topology::select_top_volume_contours::IsExtraMinima());
tData.ExtraMinimaBranchIsoValue = extraMinimaBranchIsoValue;
}
};
tData.TopVolumeBranchSaddleIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveExtraContourSaddleValue);
}
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm
#endif

@ -0,0 +1,236 @@
//============================================================================
// 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)
//==============================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_contours_BranchParentComparator_h
#define vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_contours_BranchParentComparator_h
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType;
// Implementation of BranchParentComparator
template <typename ValueType>
class BranchParentComparatorImpl
{
public:
using ValueArrayType = typename vtkm::cont::ArrayHandle<ValueType>;
using IdPortalType = typename IdArrayType::ReadPortalType;
using ValuePortalType = typename ValueArrayType::ReadPortalType;
// constructor
VTKM_CONT
BranchParentComparatorImpl(const IdArrayType& branchParent,
const ValueArrayType& saddleIsoValue,
const IdArrayType& branchRootGRId,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
: branchParentPortal(branchParent.PrepareForInput(device, token))
, saddleIsoValuePortal(saddleIsoValue.PrepareForInput(device, token))
, branchRootGRIdPortal(branchRootGRId.PrepareForInput(device, token))
{ // constructor
} // constructor
// () operator - gets called to do comparison
VTKM_EXEC
bool operator()(const vtkm::Id& i, const vtkm::Id& j) const
{ // operator()
vtkm::Id parentI = this->branchParentPortal.Get(i);
vtkm::Id parentJ = this->branchParentPortal.Get(j);
// primary sort on branch parent
if (parentI < parentJ)
return true;
if (parentI > parentJ)
return false;
ValueType valueI = this->saddleIsoValuePortal.Get(i);
ValueType valueJ = this->saddleIsoValuePortal.Get(j);
// secondary sort on branch saddle isovalue
if (valueI < valueJ)
return true;
if (valueI > valueJ)
return false;
vtkm::Id rootI = this->branchRootGRIdPortal.Get(i);
vtkm::Id rootJ = this->branchRootGRIdPortal.Get(j);
return (rootI < rootJ);
} // operator()
private:
IdPortalType branchParentPortal;
ValuePortalType saddleIsoValuePortal;
IdPortalType branchRootGRIdPortal;
}; // BranchParentComparatorImpl
/// <summary>
/// Comparator of branch parent. Lower parent comes first
/// </summary>
template <typename ValueType>
class BranchParentComparator : public vtkm::cont::ExecutionObjectBase
{
using ValueArrayType = typename vtkm::cont::ArrayHandle<ValueType>;
public:
// constructor
VTKM_CONT
BranchParentComparator(const IdArrayType& branchParent,
const ValueArrayType& saddleIsoValue,
const IdArrayType& branchRootGRId)
: BranchParent(branchParent)
, SaddleIsoValue(saddleIsoValue)
, BranchRootGRId(branchRootGRId)
{
}
VTKM_CONT BranchParentComparatorImpl<ValueType> PrepareForExecution(
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token) const
{
return BranchParentComparatorImpl<ValueType>(
this->BranchParent, this->SaddleIsoValue, this->BranchRootGRId, device, token);
}
private:
IdArrayType BranchParent;
ValueArrayType SaddleIsoValue;
IdArrayType BranchRootGRId;
}; // BranchParentComparator
// Implementation of SuperarcTargetComparator
class SuperarcTargetComparatorImpl
{
public:
using IdPortalType = typename IdArrayType::ReadPortalType;
// constructor
VTKM_CONT
SuperarcTargetComparatorImpl(const IdArrayType& superarcTarget,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
: superarcPortal(superarcTarget.PrepareForInput(device, token))
{ // constructor
} // constructor
// () operator - gets called to do comparison
VTKM_EXEC
bool operator()(const vtkm::Id& i, const vtkm::Id& j) const
{ // operator()
VTKM_ASSERT(i < superarcPortal.GetNumberOfValues() && i >= 0);
VTKM_ASSERT(j < superarcPortal.GetNumberOfValues() && j >= 0);
vtkm::Id superarcI = this->superarcPortal.Get(i);
vtkm::Id superarcJ = this->superarcPortal.Get(j);
bool isNullI = vtkm::worklet::contourtree_augmented::NoSuchElement(superarcI);
bool isNullJ = vtkm::worklet::contourtree_augmented::NoSuchElement(superarcJ);
// let the NULL superarc always go first
if (isNullI)
return true;
if (isNullJ)
return false;
vtkm::Id targetI = vtkm::worklet::contourtree_augmented::MaskedIndex(superarcI);
vtkm::Id targetJ = vtkm::worklet::contourtree_augmented::MaskedIndex(superarcJ);
// primary sort on the superarc target
return (targetI < targetJ);
} // operator()
private:
IdPortalType superarcPortal;
}; // SuperarcTargetComparatorImpl
/// <summary>
/// Comparator of superarc target. The NULL superarc always comes first.
/// </summary>
class SuperarcTargetComparator : public vtkm::cont::ExecutionObjectBase
{
public:
// constructor
VTKM_CONT
SuperarcTargetComparator(const IdArrayType& superarcTarget)
: SuperarcTarget(superarcTarget)
{
}
VTKM_CONT SuperarcTargetComparatorImpl PrepareForExecution(vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token) const
{
return SuperarcTargetComparatorImpl(this->SuperarcTarget, device, token);
}
private:
IdArrayType SuperarcTarget;
}; // SuperarcTargetComparator
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -9,10 +9,17 @@
##============================================================================
set(headers
Predicates.h
TopVolumeBranchData.h
ClarifyBranchEndSupernodeTypeWorklet.h
UpdateInfoByBranchDirectionWorklet.h
GetBranchHierarchyWorklet.h
GetBranchVolumeWorklet.h
BranchParentComparator.h
BranchVolumeComparator.h
MarchingCubesDataTables.h
LocalIsosurfaceExtractWorklet.h
BranchDecompositionTreeMaker.h
)
#-----------------------------------------------------------------------------

@ -0,0 +1,401 @@
//============================================================================
// 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)
//==============================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_GetBranchHierarchyWorklet_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_GetBranchHierarchyWorklet_h
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
constexpr vtkm::IdComponent MAX_CONNECTIVITY_3D = static_cast<vtkm::IdComponent>(14);
using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType;
/// <summary>
/// worklet to check whether the saddle end of branch is known by the block
/// if true, we return the saddle end supernode id
/// if false (or main branch), we return NO_SUCH_ELEMENT
/// </summary>
class BranchSaddleIsKnownWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn lowerEndGRId, // (input) branch lower end global regular id
FieldIn lowerLocalEnd, // (input) branch local lower end
FieldIn lowerLocalEndGRId, // (input) branch local lower end global regular id
FieldIn upperEndGRId, // (input) branch upper end global regular id
FieldIn upperLocalEnd, // (input) branch local upper end
FieldIn upperLocalEndGRId, // (input) branch local upper end global regular id
FieldIn branchSaddleEps, // (input) branch saddle epsilon
FieldOut branchSaddle // (output) the branch saddle (if known by the block)
);
using ExecutionSignature = _8(_1, _2, _3, _4, _5, _6, _7);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
BranchSaddleIsKnownWorklet() {}
/// The functor checks the direction of the branch
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& lowerEndGRId,
const vtkm::Id& lowerLocalEnd,
const vtkm::Id& lowerLocalEndGRId,
const vtkm::Id& upperEndGRId,
const vtkm::Id& upperLocalEnd,
const vtkm::Id& upperLocalEndGRId,
const vtkm::Id& branchSaddleEps) const
{
// if main branch
if (branchSaddleEps == 0)
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// if the branch is a minimum-saddle branch
if (branchSaddleEps > 0)
return lowerEndGRId == lowerLocalEndGRId
? lowerLocalEnd
: vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// if the branch is a maximum-saddle branch
if (branchSaddleEps < 0)
return upperEndGRId == upperLocalEndGRId
? upperLocalEnd
: vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// in case of fallout, should never reach
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
}; // BranchSaddleIsKnownWorklet
/// <summary>
/// worklet to compute the parent branch of branches
/// </summary>
class GetParentBranchWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn branchSaddle, // (input) branch saddle supernode id
FieldIn saddleBranchRoot, // (input) the branch root of the superarc starting from the saddle
FieldIn saddleGRId, // (input) branch saddle supernode global regular id
WholeArrayIn superarcs, // (array input) all superarc targets in ascending order
WholeArrayIn branchRoots, // (array input) all branchRoots of superarcs
WholeArrayIn branchRootByBranch, // (array input) branch roots of branches in ascending order
WholeArrayIn upperEndGRIds, // (array input) upper local end of branches
WholeArrayIn lowerEndGRIds, // (array input) lower local end of branches
FieldOut parentBranch // (output) the information index of the parent branch
);
using ExecutionSignature = _9(_1, _2, _3, _4, _5, _6, _7, _8);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
GetParentBranchWorklet() {}
template <typename IdArrayPortalType>
VTKM_EXEC vtkm::Id GetSuperarcEndPoint(const vtkm::Id& branchSaddle,
const IdArrayPortalType& sortedSuperarcs,
const bool isStart) const
{
VTKM_ASSERT(vtkm::worklet::contourtree_augmented::NoSuchElement(sortedSuperarcs.Get(0)));
using vtkm::worklet::contourtree_augmented::MaskedIndex;
vtkm::Id nSuperarcs = sortedSuperarcs.GetNumberOfValues();
vtkm::Id endpoint = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
vtkm::Id head = 1;
vtkm::Id tail = nSuperarcs - 1;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midSuperarc = MaskedIndex(sortedSuperarcs.Get(mid));
if (midSuperarc > branchSaddle)
tail = mid - 1;
else if (midSuperarc < branchSaddle)
head = mid + 1;
else if (isStart &&
(mid == 1 || (mid > 1 && MaskedIndex(sortedSuperarcs.Get(mid - 1)) < branchSaddle)))
{
endpoint = mid;
break;
}
else if (!isStart &&
(mid == nSuperarcs - 1 ||
(mid < nSuperarcs - 1 && MaskedIndex(sortedSuperarcs.Get(mid + 1)) > branchSaddle)))
{
endpoint = mid;
break;
}
else if (isStart)
tail = mid - 1;
else
head = mid + 1;
}
VTKM_ASSERT(endpoint >= 1);
return endpoint;
}
template <typename IdArrayPortalType>
VTKM_EXEC vtkm::Id GetBranchRootIdx(const vtkm::Id& branchRoot,
const IdArrayPortalType& branchRootByBranch) const
{
vtkm::Id nBranchRoot = branchRootByBranch.GetNumberOfValues();
vtkm::Id head = 0;
vtkm::Id tail = nBranchRoot - 1;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midBranchRoot = branchRootByBranch.Get(mid);
if (midBranchRoot == branchRoot)
{
return mid;
}
else if (midBranchRoot > branchRoot)
tail = mid - 1;
else
head = mid + 1;
}
// should always find the branch root index
// if not, report error
VTKM_ASSERT(false && "Cannot find the branch root known by the block!");
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
template <typename IdArrayPortalType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& branchSaddle,
const vtkm::Id& saddleBranchRoot,
const vtkm::Id& saddleGRId,
const IdArrayPortalType& sortedSuperarcs,
const IdArrayPortalType& permutedBranchRoots,
const IdArrayPortalType& branchRootByBranch,
const IdArrayPortalType& upperEndGRIds,
const IdArrayPortalType& lowerEndGRIds) const
{
// there are at most MAX_CONNECTIVITY_3D superarcs connected to the branchSaddle
vtkm::Id candidateBranchRoot[MAX_CONNECTIVITY_3D];
vtkm::Id nCandidate = 1;
candidateBranchRoot[0] = saddleBranchRoot;
const vtkm::Id superarcStartIdx = GetSuperarcEndPoint(branchSaddle, sortedSuperarcs, true);
const vtkm::Id superarcEndIdx = GetSuperarcEndPoint(branchSaddle, sortedSuperarcs, false);
VTKM_ASSERT(superarcEndIdx >= superarcStartIdx);
VTKM_ASSERT(superarcEndIdx - superarcStartIdx + 2 <= MAX_CONNECTIVITY_3D);
for (vtkm::Id superarc = superarcStartIdx; superarc <= superarcEndIdx; superarc++)
{
candidateBranchRoot[nCandidate++] = permutedBranchRoots.Get(superarc);
}
for (vtkm::Id branchRoot = 0; branchRoot < nCandidate; branchRoot++)
{
// NOTE: we ALWAYS exclude the virtual superarc, which does not belong to any branch
if (candidateBranchRoot[branchRoot] == permutedBranchRoots.Get(0))
continue;
const vtkm::Id branchIdx =
GetBranchRootIdx(candidateBranchRoot[branchRoot], branchRootByBranch);
if (upperEndGRIds.Get(branchIdx) != saddleGRId && lowerEndGRIds.Get(branchIdx) != saddleGRId)
return branchIdx;
}
// Unfortunately, it seems possible that the parent branch cannot be found
// in which case NO_SUCH_ELEMENT is returned
// VTKM_ASSERT(false && "Cannot find the parent branch!");
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
}; // GetParentBranchWorklet
/// <summary>
/// worklet to assign values to arrayhandle with given index
/// this is different from permutation: we do not want to change the size of valueOut
/// we also don't want to touch the default values in valueOut
/// index - value is one to one
/// </summary>
class AssignValueByIndex : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn index, // (input) index
FieldIn value, // (input) value
WholeArrayOut valueOut // (array output) valueOut[index] = value
);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
AssignValueByIndex() {}
template <typename ValueType, typename ValueArrayPortalType>
VTKM_EXEC void operator()(const vtkm::Id& index,
const ValueType& value,
ValueArrayPortalType& valueOut) const
{
if (vtkm::worklet::contourtree_augmented::NoSuchElement(index))
return;
valueOut.Set(index, value);
}
}; // AssignValueByIndex
/// <summary>
/// worklet to get the outer saddles of parent branches from branch-decomposition tree
/// This is to visualize the isosurface belong to the parent branch
/// that is symmetrical to the outer-most child branch
/// we collect the first saddle isovalue if branchSaddleEpsilon(parent) < 0
/// or the last saddle isovalue if branchSaddleEpsilon(parent) > 0
/// or both if branchSaddleEpsilon(parent) == 0
/// </summary>
class CollectOuterSaddle : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn parentSaddleEpsilon, // parent saddle epsilon
WholeArrayIn branchParent, // (array input) parent branch root ID (local)
FieldOut IsOuterSaddle // (output) whether the branch is an outer saddle of the parent
);
using ExecutionSignature = _3(InputIndex, _1, _2);
using InputDomain = _1;
using IdArrayPortalType = typename IdArrayType::ReadPortalType;
/// Constructor
VTKM_EXEC_CONT
CollectOuterSaddle() {}
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& inputIndex,
const vtkm::Id& parentSaddleEpsilon,
const IdArrayPortalType& branchParent) const
{
const vtkm::Id selfParent = branchParent.Get(inputIndex);
vtkm::Id isOuterSaddle = 0;
if (vtkm::worklet::contourtree_augmented::NoSuchElement(selfParent))
{
return isOuterSaddle;
}
const bool isFirst = (inputIndex == 0) || (branchParent.Get(inputIndex - 1) != selfParent);
const bool isLast = (inputIndex == branchParent.GetNumberOfValues() - 1) ||
(branchParent.Get(inputIndex + 1) != selfParent);
if (isFirst && parentSaddleEpsilon <= 0)
{
isOuterSaddle |= 1;
}
if (isLast && parentSaddleEpsilon >= 0)
{
isOuterSaddle |= 2;
}
return isOuterSaddle;
}
}; // CollectOuterSaddle
/// <summary>
/// worklet to update the value of outer saddles for parent branches
/// </summary>
template <bool isMaximum>
class UpdateOuterSaddle : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn branchOrder, // (input) the order of the (top-volume) branch by volume
FieldInOut branchValue, // (input/output) the isovalue to extract
WholeArrayIn incomingOrders, // (array input) (sorted) orders of branches from the other block
WholeArrayIn
incomingValues // (array input) isovalues to extract on branches from the other block
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
using IdArrayPortalType = typename IdArrayType::ReadPortalType;
/// Constructor
VTKM_EXEC_CONT
UpdateOuterSaddle() {}
template <typename ValueType, typename ValuePortalType>
VTKM_EXEC void operator()(const vtkm::Id& branchOrder,
ValueType& branchValue,
const IdArrayPortalType& incomingOrders,
const ValuePortalType& incomingValues) const
{
vtkm::Id head = 0;
vtkm::Id tail = incomingOrders.GetNumberOfValues() - 1;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midOrder = incomingOrders.Get(mid);
if (midOrder == branchOrder)
{
const ValueType midValue = incomingValues.Get(mid);
if (isMaximum && midValue > branchValue)
branchValue = midValue;
else if (!isMaximum && midValue < branchValue)
branchValue = midValue;
return;
}
else if (midOrder > branchOrder)
tail = mid - 1;
else
head = mid + 1;
}
}
}; // UpdateOuterSaddle
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -108,18 +108,19 @@ public:
{
if (isLowerLeaf && isUpperLeaf)
return totalVolume;
// if the branch is a minimum-saddle branch
// if the upper end superarc direction is pointing up, then dependent; otherwise, reverse
if (isLowerLeaf)
return contourtree_augmented::IsAscending(upperDirection)
? upperDependent
: totalVolume - upperDependent + upperIntrinsic;
: totalVolume - upperDependent + upperIntrinsic - 1;
// if the branch is a maximum-saddle branch
// if the lower end superarc direction is pointing down, then true; otherwise, false
if (isUpperLeaf)
return !contourtree_augmented::IsAscending(lowerDirection)
? lowerDependent
: totalVolume - lowerDependent + lowerIntrinsic;
: totalVolume - lowerDependent + lowerIntrinsic - 1;
// in case of fallout, should never reach
return 0;

@ -0,0 +1,751 @@
//============================================================================
// 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)
//==============================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_local_isosurface_extract_worklet_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_local_isosurface_extract_worklet_h
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/MarchingCubesDataTables.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
constexpr vtkm::IdComponent MAX_MARCHING_CUBE_TRIANGLES = static_cast<vtkm::IdComponent>(5);
constexpr vtkm::IdComponent MAX_LINEAR_INTERPOLATION_TRIANGLES = static_cast<vtkm::IdComponent>(12);
/// Worklet to check whether the global regular id in inside the block
/// The global regular Ids in block should be sorted beforehand
class IdxIfWithinBlockWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn regularId, // (input) global regular id
WholeArrayIn idsInBlock, // (array input) all global regular ids within the local block
FieldOut inBlockIndicator, // (output) 1 if the regularId is inside the block
FieldOut inBlockIdx // (output) the index of regularId in idsInBlock
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
IdxIfWithinBlockWorklet() {}
/// The functor uses binary search to locate regularId in idsInBlock
/// if the search fails, return NO_SUCH_ELEMENT; otherwise, return the location
/// idsInBlock should be sorted
template <typename InIdPortalType>
VTKM_EXEC void operator()(const vtkm::Id& regularId,
const InIdPortalType& idsInBlock,
vtkm::Id& inBlockIndicator,
vtkm::Id& inBlockIdx) const
{
vtkm::Id head = 0;
vtkm::Id tail = idsInBlock.GetNumberOfValues() - 1;
inBlockIndicator = 0;
inBlockIdx = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midValue = idsInBlock.Get(mid);
if (regularId == midValue)
{
inBlockIdx = mid;
inBlockIndicator = 1;
return;
}
else if (regularId > midValue)
head = mid + 1;
else
tail = mid - 1;
}
}
}; // IdxIfWithinBlockWorklet
/// Worklet for debug, upon remove when release
class DebugSearchWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature =
void(FieldIn regularId, // (input) global regular id
WholeArrayIn idsInBlock, // (array input) all global regular ids within the local block
FieldOut foundId);
using ExecutionSignature = _3(InputIndex, _1, _2);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
DebugSearchWorklet(const vtkm::Id& superarcSize)
: SuperarcSize(superarcSize)
{
}
template <typename InIdPortalType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& inputIndex,
const vtkm::Id& regularId,
const InIdPortalType& idsInBlock) const
{
vtkm::Id head = 0;
vtkm::Id tail = idsInBlock.GetNumberOfValues() - 1;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midValue = idsInBlock.Get(mid);
if (regularId == midValue)
{
if (inputIndex < SuperarcSize)
return inputIndex;
break;
}
else if (regularId > midValue)
head = mid + 1;
else
tail = mid - 1;
}
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
private:
const vtkm::Id SuperarcSize;
}; // DebugSearchWorklet
/// Worklet for getting the polarity case of a cell compared to the isovalue.
/// Only consider 2D and 3D data.
/// The output for each cell is an integer ([0, 7] if 2D, or [0, 255] if 3D)
/// indicating the polarity at each vertex of the cell compared to the isovalue.
template <typename ValueType>
class GetCellCasesWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn localIdx, // (input) local point index
WholeArrayIn dataValues, // (array input) data values within block
WholeArrayIn vertexOffset, // (array input) vertex offset look-up table
FieldOut caseCell // (output) the polarity case (in binary) of the cell
);
using ExecutionSignature = _4(_1, _2, _3);
using InputDomain = _1;
using IdArrayPortalType = typename vtkm::cont::ArrayHandle<vtkm::Id>::ReadPortalType;
using ValueArrayPortalType = typename vtkm::cont::ArrayHandle<ValueType>::ReadPortalType;
/// <summary>
/// Constructor
/// </summary>
/// <param name="ptDimensions">dimension of points in the grid</param>
/// <param name="branchSaddleEpsilon">the direction for tiebreaking when comparing values</param>
/// <param name="isoValue">isovalue for the isosurface to extract</param>
/// <returns></returns>
VTKM_EXEC_CONT
GetCellCasesWorklet(const vtkm::Id3 ptDimensions,
const vtkm::Id branchSaddleEpsilon,
const ValueType isoValue)
: PointDimensions(ptDimensions)
, BranchSaddleEpsilon(branchSaddleEpsilon)
, IsoValue(isoValue)
{
CellDimensions[0] = ptDimensions[0] - 1;
CellDimensions[1] = ptDimensions[1] - 1;
CellDimensions[2] = ptDimensions[2] - 1;
}
/// <summary>
/// Computes the polarity case of cells
/// </summary>
/// <param name="localIndex">the local index of the point in the local grid</param>
/// <param name="dataValuesPortal">all data values on the local grid points</param>
/// <returns>integer indicating the polarity case of the cell originating at the input point</returns>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id localIndex,
const ValueArrayPortalType& dataValuesPortal,
const IdArrayPortalType& vertexOffset) const
{
const vtkm::Id nPoints = PointDimensions[0] * PointDimensions[1] * PointDimensions[2];
VTKM_ASSERT(dataValuesPortal.GetNumberOfValues() == nPoints);
if (CellDimensions[2] <= 0)
{
// the 2D local coordinate of the input point
vtkm::Id2 localPt(localIndex % CellDimensions[0], localIndex / CellDimensions[0]);
vtkm::Id caseCell = 0;
// iterate over all points of the cell
for (vtkm::Id i = 0; i < nVertices2d; i++)
{
vtkm::Id currPtIdx = i * 2;
vtkm::Id currPt = vertexOffset.Get(currPtIdx) + localPt[0] +
(vertexOffset.Get(currPtIdx + 1) + localPt[1]) * PointDimensions[0];
VTKM_ASSERT(currPt < nPoints);
// when point value == IsoValue
// the index is 1 only if the branch is lower-end
if (dataValuesPortal.Get(currPt) > IsoValue ||
(dataValuesPortal.Get(currPt) == IsoValue && BranchSaddleEpsilon < 0))
caseCell |= vtkm::Id(1) << i;
}
return caseCell;
}
else
{
// the 3D local coordinate of the input point
vtkm::Id3 localPt(localIndex % CellDimensions[0],
(localIndex / CellDimensions[0]) % CellDimensions[1],
localIndex / (CellDimensions[0] * CellDimensions[1]));
vtkm::Id caseCell = 0;
// iterate over all points of the cell
for (vtkm::Id i = 0; i < nVertices3d; i++)
{
vtkm::Id currPtIdx = i * 3;
vtkm::Id currPt = vertexOffset.Get(currPtIdx) + localPt[0] +
(vertexOffset.Get(currPtIdx + 1) + localPt[1]) * PointDimensions[0] +
(vertexOffset.Get(currPtIdx + 2) + localPt[2]) *
(PointDimensions[0] * PointDimensions[1]);
VTKM_ASSERT(currPt < nPoints);
// when point value == IsoValue
// the index is 1 only if the branch is lower-end
if (dataValuesPortal.Get(currPt) > IsoValue ||
(dataValuesPortal.Get(currPt) == IsoValue && BranchSaddleEpsilon < 0))
caseCell |= vtkm::Id(1) << i;
}
return caseCell;
}
}
private:
vtkm::Id3 PointDimensions;
vtkm::Id BranchSaddleEpsilon;
ValueType IsoValue;
vtkm::Id3 CellDimensions;
}; // GetCellCasesWorklet
/// Worklet for getting the superarc of a branch given an isovalue
class GetSuperarcByIsoValueWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature =
void(FieldIn upperEndLocalId, // (input) upper end of the branch
FieldIn lowerEndLocalId, // (input) lower end of the branch
FieldIn isoValue, // (input) isoValue
FieldIn branchSaddleEpsilon, // (input) whether the branch is on top or at the bottom
FieldOut superarc, // (output) local superarc that intersects the isosurface
ExecObject findSuperarcByNode);
using ExecutionSignature = _5(_1, _2, _3, _4, _6);
using InputDomain = _1;
/// <summary>
/// Constructor
/// </summary>
/// <param name="totNumPoints">total number of points within the local grid</param>
/// <hint>We only need a number that is larger than any grid index</hint>
VTKM_EXEC_CONT
GetSuperarcByIsoValueWorklet(const vtkm::Id totNumPoints)
: TotalNumPoints(totNumPoints)
{
}
/// <summary>
/// Implementation of GetSuperarcByIsoValueWorklet.
/// Check vtkm::worklet::contourtree_distributed::FindSuperArcForUnknownNode
/// for the execution object description.
/// </summary>
/// <typeparam name="ValueType">data value type</typeparam>
/// <typeparam name="findSuperarcType">execution object type of findSuperarc</typeparam>
/// <param name="upperEndLocalId">local id of the upper end vertex of the branch</param>
/// <param name="lowerEndLocalId">local id of the lower end vertex of the branch</param>
/// <param name="isoValue">isovalue</param>
/// <param name="branchSaddleEpsilon">the direction for tiebreaking when comparing values</param>
/// <param name="findSuperarc">execution object</param>
/// <returns></returns>
template <typename ValueType, typename findSuperarcType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id upperEndLocalId,
const vtkm::Id lowerEndLocalId,
const ValueType isoValue,
const vtkm::Id branchSaddleEpsilon,
const findSuperarcType& findSuperarc) const
{
VTKM_ASSERT(branchSaddleEpsilon != 0);
if (branchSaddleEpsilon < 0)
return findSuperarc.FindSuperArcForUnknownNode(
-1, isoValue, upperEndLocalId, lowerEndLocalId);
return findSuperarc.FindSuperArcForUnknownNode(
TotalNumPoints, isoValue, upperEndLocalId, lowerEndLocalId);
}
private:
vtkm::Id TotalNumPoints;
}; // GetSuperarcByIsoValueWorklet
/// Worklet for calculating the edges to be drawn in the cell
/// NOTE: this worklet can only work on 2D and 3D data
template <typename ValueType>
class GetEdgesInCellWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn edgeOffset, // (input) offset of output edge in the output array
FieldIn caseCell, // (input) the marching cube case of the cell
WholeArrayIn localIds, // (array input) local ids of points
WholeArrayIn dataValues, // (array input) data values within block
WholeArrayIn vertexOffset, // (array input) vertex offset look-up table
WholeArrayIn edgeTable, // (array input) edge-in-cell look-up table
WholeArrayIn numBoundTable, // (array input) number of boundaries look-up table
WholeArrayIn boundaryTable, // (array input) edge-of-boundary look-up table
WholeArrayIn labelEdgeTable, // (array input) label edge (only for 3D) look-up table
WholeArrayOut edgesFrom, // (array output) array of start-points of edges on the isosurface
WholeArrayOut edgesTo, // (array output) array of end-points of edges on the isosurface
WholeArrayOut
isValidEdges, // (array output) whether the edge plan to draw belongs to the branch
ExecObject
findSuperarcForNode // (execution object) detector for the superarc of interpolated nodes
);
using ExecutionSignature =
void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13);
using InputDomain = _1;
using IdArrayReadPortalType = typename vtkm::cont::ArrayHandle<vtkm::Id>::ReadPortalType;
using IdArrayWritePortalType = typename vtkm::cont::ArrayHandle<vtkm::Id>::WritePortalType;
using ValueArrayPortalType = typename vtkm::cont::ArrayHandle<ValueType>::ReadPortalType;
using EdgePointArrayPortalType =
typename vtkm::cont::ArrayHandle<vtkm::Vec3f_64>::WritePortalType;
/// Constructor
/// ptDimensions: dimension of points in the grid
/// branchSuperarc: the superarc on the given branch intersecting the isosurface
/// isoValue: isovalue for the isosurface to extract
VTKM_EXEC_CONT
GetEdgesInCellWorklet(const vtkm::Id3 ptDimensions,
const vtkm::Id3 globalPointIndexStart,
const ValueType isoValue,
const vtkm::Id branchSuperarc,
const vtkm::Id branchSaddleEpsilon,
const vtkm::Id totNumPoints,
const bool marchingCubes)
: PointDimensions(ptDimensions)
, GlobalPointIndexStart(globalPointIndexStart)
, IsoValue(isoValue)
, BranchSuperarc(branchSuperarc)
, BranchSaddleEpsilon(branchSaddleEpsilon)
, TotalNumPoints(totNumPoints)
, isMarchingCubes(marchingCubes)
{
CellDimensions[0] = ptDimensions[0] - 1;
CellDimensions[1] = ptDimensions[1] - 1;
CellDimensions[2] = ptDimensions[2] - 1;
}
// cell index: the point index within the local cell
VTKM_EXEC vtkm::Id CellIndexToNodeIndex2D(const vtkm::Id2& localPt,
const vtkm::Id cellIndex,
const IdArrayReadPortalType& vertOffset) const
{
return vertOffset.Get(cellIndex * 2) + localPt[0] +
(vertOffset.Get(cellIndex * 2 + 1) + localPt[1]) * PointDimensions[0];
}
// cell index: the point index within the local cell
VTKM_EXEC vtkm::Vec3f_64 CellIndexToNodeCoord2D(const vtkm::Id2& localPt,
const vtkm::Id cellIndex,
const IdArrayReadPortalType& vertOffset) const
{
return vtkm::Vec3f_64(
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 2) + localPt[0]),
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 2 + 1) + localPt[1]),
static_cast<vtkm::Float64>(0));
}
// cell index: the point index within the local cell
VTKM_EXEC vtkm::Id CellIndexToNodeIndex3D(const vtkm::Id3& localPt,
const vtkm::Id cellIndex,
const IdArrayReadPortalType& vertOffset) const
{
return vertOffset.Get(cellIndex * 3) + localPt[0] +
(vertOffset.Get(cellIndex * 3 + 1) + localPt[1]) * PointDimensions[0] +
(vertOffset.Get(cellIndex * 3 + 2) + localPt[2]) * (PointDimensions[0] * PointDimensions[1]);
}
// cell index: the point index within the local cell
VTKM_EXEC vtkm::Vec3f_64 CellIndexToNodeCoord3D(const vtkm::Id3& localPt,
const vtkm::Id cellIndex,
const IdArrayReadPortalType& vertOffset) const
{
return vtkm::Vec3f_64(
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 3) + localPt[0]),
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 3 + 1) + localPt[1]),
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 3 + 2) + localPt[2]));
}
// Implementation to draw isosurface edges
// all hard-coded numbers in this function depends on the dimension of the data
// The number of vertices/lines/faces of a cell is fixed for a certain dimension
// The number of cases for the marching cube algorithm are also hard-coded
// Check MarchingCubesDataTables.h for more details
template <typename FindSuperarcExecType>
VTKM_EXEC void operator()(
const vtkm::Id localIndex, // refers to the index in the grid
const vtkm::Id edgeOffset,
const vtkm::Id caseCell,
const IdArrayReadPortalType& localIdsPortal, // refers to the index in (superarc etc.) arrays
const ValueArrayPortalType& dataValuesPortal,
const IdArrayReadPortalType& vertexOffset,
const IdArrayReadPortalType& edgeTable,
const IdArrayReadPortalType& numBoundTable,
const IdArrayReadPortalType& boundaryTable,
const IdArrayReadPortalType& labelEdgeTable,
EdgePointArrayPortalType& edgesFromPortal,
EdgePointArrayPortalType& edgesToPortal,
IdArrayWritePortalType& isValidEdgesPortal,
const FindSuperarcExecType& findSuperarcForNode) const
{
const vtkm::Id nPoints = PointDimensions[0] * PointDimensions[1] * PointDimensions[2];
// 2D
if (CellDimensions[2] <= 0)
{
const vtkm::Id2 localPt(localIndex % CellDimensions[0], localIndex / CellDimensions[0]);
VTKM_ASSERT(localIdsPortal.GetNumberOfValues() == nPoints);
VTKM_ASSERT(dataValuesPortal.GetNumberOfValues() == nPoints);
const vtkm::Id numEdges = numBoundTable.Get(caseCell);
if (numEdges < 1)
return;
for (vtkm::Id edgeIndex = 0; edgeIndex < numEdges; edgeIndex++)
{
const vtkm::Id lineForCaseOffset = caseCell * nLineTableElemSize2d; // 8;
const vtkm::Id lineOffset = lineForCaseOffset + edgeIndex * 2;
// lineFrom and lineTo are two edges where the isosurface edge intersects
const vtkm::Id lineFrom = boundaryTable.Get(lineOffset);
const vtkm::Id lineTo = boundaryTable.Get(lineOffset + 1);
// We need to assure that both lineFrom and lineTo belong to the branch
// all 0 and 1 in the variables below refer to the two vertices of the line
const vtkm::Id lineFromVert0 =
CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset);
const vtkm::Id lineFromVert1 =
CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset);
VTKM_ASSERT(lineFromVert0 < nPoints);
VTKM_ASSERT(lineFromVert1 < nPoints);
const vtkm::Id lineFromVert0LocalId = localIdsPortal.Get(lineFromVert0);
const vtkm::Id lineFromVert1LocalId = localIdsPortal.Get(lineFromVert1);
const ValueType lineFromVert0Value = dataValuesPortal.Get(lineFromVert0);
const ValueType lineFromVert1Value = dataValuesPortal.Get(lineFromVert1);
// due to simulation of simplicity
// vert0 < vert1 if their values are equal
const vtkm::Id lowVertFrom =
lineFromVert0Value <= lineFromVert1Value ? lineFromVert0LocalId : lineFromVert1LocalId;
const vtkm::Id highVertFrom =
lineFromVert0Value > lineFromVert1Value ? lineFromVert0LocalId : lineFromVert1LocalId;
const vtkm::Id lineToVert0 =
CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineTo * 2), vertexOffset);
const vtkm::Id lineToVert1 =
CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset);
VTKM_ASSERT(lineToVert0 < nPoints);
VTKM_ASSERT(lineToVert1 < nPoints);
const vtkm::Id lineToVert0LocalId = localIdsPortal.Get(lineToVert0);
const vtkm::Id lineToVert1LocalId = localIdsPortal.Get(lineToVert1);
const ValueType lineToVert0Value = dataValuesPortal.Get(lineToVert0);
const ValueType lineToVert1Value = dataValuesPortal.Get(lineToVert1);
// due to simulation of simplicity
// vert0 < vert1 if their values are equal
const vtkm::Id lowVertTo =
lineToVert0Value <= lineToVert1Value ? lineToVert0LocalId : lineToVert1LocalId;
const vtkm::Id highVertTo =
lineToVert0Value > lineToVert1Value ? lineToVert0LocalId : lineToVert1LocalId;
vtkm::Id lineFromSuperarc = -1;
vtkm::Id lineToSuperarc = -1;
// We always extract the isosurface above/below the isovalue by 0+
VTKM_ASSERT(BranchSaddleEpsilon != 0);
// lower end of branch is leaf
// the actual isovalue should be IsoValue-eps
// eps is 0+ (infinitely small)
if (BranchSaddleEpsilon < 0)
{
lineFromSuperarc =
findSuperarcForNode.FindSuperArcForUnknownNode(-1, IsoValue, highVertFrom, lowVertFrom);
lineToSuperarc =
findSuperarcForNode.FindSuperArcForUnknownNode(-1, IsoValue, highVertTo, lowVertTo);
}
// upper end of branch is leaf
// the actual isovalue should be IsoValue+eps
// eps is 0+ (infinitely small)
else if (BranchSaddleEpsilon > 0)
{
lineFromSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode(
TotalNumPoints, IsoValue, highVertFrom, lowVertFrom);
lineToSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode(
TotalNumPoints, IsoValue, highVertTo, lowVertTo);
}
// we only draw the line if both lineFrom and lineTo belongs to the branch of query
if (lineFromSuperarc != BranchSuperarc || lineToSuperarc != BranchSuperarc)
{
isValidEdgesPortal.Set(edgeOffset + edgeIndex, 0);
continue;
}
isValidEdgesPortal.Set(edgeOffset + edgeIndex, 1);
// Now let's draw the line
vtkm::Vec3f_64 lineFromVert0Coord =
CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset);
vtkm::Vec3f_64 lineFromVert1Coord =
CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset);
vtkm::Vec3f_64 lineToVert0Coord =
CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineTo * 2), vertexOffset);
vtkm::Vec3f_64 lineToVert1Coord =
CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset);
vtkm::Vec3f_64 fromPt(lineFromVert0Coord);
vtkm::Vec3f_64 toPt(lineToVert0Coord);
vtkm::Float64 fromRatio =
vtkm::Float64(IsoValue - lineFromVert0Value) / (lineFromVert1Value - lineFromVert0Value);
vtkm::Float64 toRatio =
vtkm::Float64(IsoValue - lineToVert0Value) / (lineToVert1Value - lineToVert0Value);
VTKM_ASSERT(fromRatio >= 0.0 && fromRatio <= 1.0);
VTKM_ASSERT(toRatio >= 0.0 && toRatio <= 1.0);
fromPt += (lineFromVert1Coord - lineFromVert0Coord) * fromRatio;
toPt += (lineToVert1Coord - lineToVert0Coord) * toRatio;
edgesFromPortal.Set(edgeOffset + edgeIndex, fromPt + GlobalPointIndexStart);
edgesToPortal.Set(edgeOffset + edgeIndex, toPt + GlobalPointIndexStart);
}
}
else // 3D
{
vtkm::Id3 localPt(localIndex % CellDimensions[0],
(localIndex / CellDimensions[0]) % CellDimensions[1],
localIndex / (CellDimensions[0] * CellDimensions[1]));
const vtkm::Id numTriangles = numBoundTable.Get(caseCell);
if (numTriangles < 1)
return;
// we check a specific edge to know the superarc of the triangle
// the edge label of the triangle is stored in labelEdgeTable in MarchingCubesDataTables.h
// there are at most 5 triangles to draw in each 3D cell (for marching cubes)
// for linear interpolation, there are at most 12 triangles
if (isMarchingCubes)
VTKM_ASSERT(numTriangles <= MAX_MARCHING_CUBE_TRIANGLES);
else
VTKM_ASSERT(numTriangles <= MAX_LINEAR_INTERPOLATION_TRIANGLES);
vtkm::Id triangleSuperarc[MAX_LINEAR_INTERPOLATION_TRIANGLES + 1];
vtkm::Id triangleLabelIdx = 0;
const vtkm::Id nLabelEdgeElemSize =
isMarchingCubes ? nLabelEdgeTableMC3dElemSize : nLabelEdgeTableLT3dElemSize;
vtkm::Id labelPtr = caseCell * nLabelEdgeElemSize;
while (labelEdgeTable.Get(labelPtr) != -1)
{
vtkm::Id labelCount = labelEdgeTable.Get(labelPtr++);
vtkm::Id labelEdge = labelEdgeTable.Get(labelPtr++);
// compute the superarc of the labelEdge belong to the branch
const vtkm::Id labelEdgeVert0 =
CellIndexToNodeIndex3D(localPt, edgeTable.Get(labelEdge * 2), vertexOffset);
const vtkm::Id labelEdgeVert1 =
CellIndexToNodeIndex3D(localPt, edgeTable.Get(labelEdge * 2 + 1), vertexOffset);
VTKM_ASSERT(labelEdgeVert0 < nPoints);
VTKM_ASSERT(labelEdgeVert1 < nPoints);
const vtkm::Id labelEdgeVert0LocalId = localIdsPortal.Get(labelEdgeVert0);
const vtkm::Id labelEdgeVert1LocalId = localIdsPortal.Get(labelEdgeVert1);
const ValueType labelEdgeVert0Value = dataValuesPortal.Get(labelEdgeVert0);
const ValueType labelEdgeVert1Value = dataValuesPortal.Get(labelEdgeVert1);
// due to simulation of simplicity
// vert0 < vert1 if their values are equal
const vtkm::Id lowVert = labelEdgeVert0Value <= labelEdgeVert1Value ? labelEdgeVert0LocalId
: labelEdgeVert1LocalId;
const vtkm::Id highVert =
labelEdgeVert0Value > labelEdgeVert1Value ? labelEdgeVert0LocalId : labelEdgeVert1LocalId;
vtkm::Id labelEdgeSuperarc = -1;
// We always extract the isosurface above/below the isovalue by 0+
VTKM_ASSERT(BranchSaddleEpsilon != 0);
// lower end of branch is leaf
// the actual isovalue should be IsoValue-(0+)
if (BranchSaddleEpsilon < 0)
{
labelEdgeSuperarc =
findSuperarcForNode.FindSuperArcForUnknownNode(-1, IsoValue, highVert, lowVert);
}
// upper end of branch is leaf
// the actual isovalue should be IsoValue+(0+)
else if (BranchSaddleEpsilon > 0)
{
labelEdgeSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode(
TotalNumPoints, IsoValue, highVert, lowVert);
}
for (vtkm::Id i = 0; i < labelCount; i++)
triangleSuperarc[triangleLabelIdx++] = labelEdgeSuperarc;
}
VTKM_ASSERT(triangleLabelIdx == numTriangles);
const vtkm::Id nTriTableElemSize =
isMarchingCubes ? nTriTableMC3dElemSize : nTriTableLT3dElemSize;
for (vtkm::Id triIndex = 0; triIndex < numTriangles; triIndex++)
{
const vtkm::Id lineFroms[3] = {
boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3),
boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 1),
boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 2)
};
const vtkm::Id lineTos[3] = {
boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 1),
boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 2),
boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3)
};
const vtkm::Id labelEdgeSuperarc = triangleSuperarc[triIndex];
// we only draw the triangle if the triangle lies on the branch of query
if (labelEdgeSuperarc != BranchSuperarc)
{
isValidEdgesPortal.Set(edgeOffset + triIndex * 3, 0);
isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 1, 0);
isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 2, 0);
continue;
}
isValidEdgesPortal.Set(edgeOffset + triIndex * 3, 1);
isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 1, 1);
isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 2, 1);
for (vtkm::Id edgeIndex = 0; edgeIndex < 3; edgeIndex++)
{
// lineFrom and lineTo are two edges where the edge of the triangle intersects
const vtkm::Id lineFrom = lineFroms[edgeIndex];
const vtkm::Id lineTo = lineTos[edgeIndex];
// Now let's draw the line
vtkm::Vec3f_64 lineFromVert0Coord =
CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset);
vtkm::Vec3f_64 lineFromVert1Coord =
CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset);
vtkm::Vec3f_64 lineToVert0Coord =
CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineTo * 2), vertexOffset);
vtkm::Vec3f_64 lineToVert1Coord =
CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset);
vtkm::Vec3f_64 fromPt(lineFromVert0Coord);
vtkm::Vec3f_64 toPt(lineToVert0Coord);
const vtkm::Id lineFromVert0 =
CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset);
const vtkm::Id lineFromVert1 =
CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset);
VTKM_ASSERT(lineFromVert0 < nPoints);
VTKM_ASSERT(lineFromVert1 < nPoints);
const ValueType lineFromVert0Value = dataValuesPortal.Get(lineFromVert0);
const ValueType lineFromVert1Value = dataValuesPortal.Get(lineFromVert1);
const vtkm::Id lineToVert0 =
CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineTo * 2), vertexOffset);
const vtkm::Id lineToVert1 =
CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset);
VTKM_ASSERT(lineToVert0 < nPoints);
VTKM_ASSERT(lineToVert1 < nPoints);
const ValueType lineToVert0Value = dataValuesPortal.Get(lineToVert0);
const ValueType lineToVert1Value = dataValuesPortal.Get(lineToVert1);
vtkm::Float64 fromRatio = vtkm::Float64(IsoValue - lineFromVert0Value) /
(lineFromVert1Value - lineFromVert0Value);
vtkm::Float64 toRatio =
vtkm::Float64(IsoValue - lineToVert0Value) / (lineToVert1Value - lineToVert0Value);
VTKM_ASSERT(fromRatio >= 0.0 && fromRatio <= 1.0);
VTKM_ASSERT(toRatio >= 0.0 && toRatio <= 1.0);
fromPt += (lineFromVert1Coord - lineFromVert0Coord) * fromRatio;
toPt += (lineToVert1Coord - lineToVert0Coord) * toRatio;
edgesFromPortal.Set(edgeOffset + triIndex * 3 + edgeIndex,
fromPt + GlobalPointIndexStart);
edgesToPortal.Set(edgeOffset + triIndex * 3 + edgeIndex, toPt + GlobalPointIndexStart);
}
}
}
}
private:
vtkm::Id3 PointDimensions;
vtkm::Id3 GlobalPointIndexStart;
ValueType IsoValue;
vtkm::Id BranchSuperarc;
vtkm::Id BranchSaddleEpsilon;
vtkm::Id TotalNumPoints;
bool isMarchingCubes;
vtkm::Id3 CellDimensions;
}; // GetEdgesInCellWorklet
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,945 @@
//============================================================================
// 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)
//==============================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_marching_cubes_data_tables_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_marching_cubes_data_tables_h
#include <vtkm/Types.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
// Edges include the diagonal of the triangulation of the mesh
const vtkm::Id nVertices2d = 4;
const vtkm::Id nEdges2d = 5;
const vtkm::Id nCases2d = 16;
const vtkm::Id nLineTableElemSize2d = 8;
const vtkm::cont::ArrayHandle<vtkm::Id> vertexOffset2d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 0, 1, 0, 1, 1, 0, 1 });
const vtkm::cont::ArrayHandle<vtkm::Id> edgeTable2d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 1, 1, 2, 3, 2, 0, 3, 0, 2 });
const vtkm::cont::ArrayHandle<vtkm::Id> numLinesTable2d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 2, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 2, 0 });
// size: nCase2d * nLineTableElemSize2d
const vtkm::cont::ArrayHandle<vtkm::Id> lineTable2d = vtkm::cont::make_ArrayHandle<vtkm::Id>({
#define X -1
X, X, X, X, X, X, X, X, 3, 4, 4, 0, X, X, X, X, 0, 1, X, X, X, X, X, X, 3, 4, 4, 1, X, X, X, X,
2, 4, 4, 1, X, X, X, X, 3, 2, 0, 1, X, X, X, X, 2, 4, 4, 0, X, X, X, X, 3, 2, X, X, X, X, X, X,
3, 2, X, X, X, X, X, X, 2, 4, 4, 0, X, X, X, X, 3, 2, 0, 1, X, X, X, X, 2, 4, 4, 1, X, X, X, X,
3, 4, 4, 1, X, X, X, X, 0, 1, X, X, X, X, X, X, 3, 4, 4, 0, X, X, X, X, X, X, X, X, X, X, X, X
#undef X
});
const vtkm::Id nVertices3d = 8;
const vtkm::Id nEdgesMC3d = 12;
const vtkm::Id nEdgesLT3d = 19;
const vtkm::Id nCasesMC3d = 256;
const vtkm::Id nCasesLT3d = 256;
// size: nVertices3d * nDims (3)
const vtkm::cont::ArrayHandle<vtkm::Id> vertexOffset3d = vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1 });
// size: nEdgesMC3d * 2 (vertices per edge)
const vtkm::cont::ArrayHandle<vtkm::Id> edgeTableMC3d = vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6, 7, 6, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7 });
// size: nEdgesLT3d * 2
const vtkm::cont::ArrayHandle<vtkm::Id> edgeTableLT3d = vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6, 7, 6, 4, 7, 0, 4, 1,
5, 2, 6, 3, 7, 0, 2, 4, 6, 0, 5, 3, 6, 0, 7, 1, 6, 0, 6 });
// size: nCasesMC3d
const vtkm::cont::ArrayHandle<vtkm::Id> numTrianglesTableMC3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, 4, 3, 4, 5, 5, 2,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4,
2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, 3, 4, 4, 3, 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 2, 3, 3, 2, 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1,
3, 4, 4, 5, 4, 5, 3, 4, 4, 5, 5, 2, 3, 4, 2, 1, 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0,
});
// size: nCasesMC3d
const vtkm::cont::ArrayHandle<vtkm::Id> numTrianglesTableLT3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 0, 6, 2, 8, 2, 8, 4, 8, 2, 8, 4, 10, 4, 8, 6, 8, 2, 8, 4, 10, 4, 10,
6, 10, 4, 10, 6, 12, 6, 10, 8, 10, 2, 8, 4, 8, 4, 10, 6, 8, 4, 10, 6, 10,
6, 10, 8, 8, 4, 8, 6, 8, 6, 10, 8, 8, 6, 10, 8, 10, 8, 10, 10, 8, 6, 12,
8, 10, 8, 10, 8, 8, 8, 10, 10, 8, 8, 8, 8, 6, 8, 10, 10, 8, 10, 8, 10, 6,
10, 8, 12, 6, 10, 6, 10, 4, 8, 10, 8, 8, 10, 8, 8, 6, 10, 8, 10, 6, 10, 6,
8, 4, 8, 8, 8, 6, 10, 6, 8, 4, 10, 6, 10, 4, 10, 4, 8, 2, 2, 8, 4, 10,
4, 10, 6, 10, 4, 8, 6, 10, 6, 8, 8, 8, 4, 8, 6, 10, 6, 10, 8, 10, 6, 8,
8, 10, 8, 8, 10, 8, 4, 10, 6, 10, 6, 12, 8, 10, 6, 10, 8, 10, 8, 10, 10, 8,
6, 8, 8, 8, 8, 10, 10, 8, 8, 8, 10, 8, 10, 8, 12, 6, 8, 10, 10, 8, 10, 8,
10, 6, 8, 8, 10, 6, 8, 6, 8, 4, 8, 8, 10, 6, 10, 6, 10, 4, 8, 6, 10, 4,
8, 4, 8, 2, 10, 8, 10, 6, 12, 6, 10, 4, 10, 6, 10, 4, 10, 4, 8, 2, 8, 6,
8, 4, 10, 4, 8, 2, 8, 4, 8, 2, 8, 2, 6, 0 });
const vtkm::Id nTriTableMC3dElemSize = 16; // (at most 5 triangles, each with 3 vertices)
const vtkm::Id nTriTableLT3dElemSize = 37; // (at most 12 triangles, each with 3 vertices)
// size: nCasesMC3d * nTriTableMCElemSize
const vtkm::cont::ArrayHandle<vtkm::Id> triTableMC3d = vtkm::cont::make_ArrayHandle<vtkm::Id>({
#define X -1
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 8, 3, X, X, X, X, X,
X, X, X, X, X, X, X, X, 0, 1, 9, X, X, X, X, X, X, X, X, X, X, X, X, X,
1, 8, 3, 9, 8, 1, X, X, X, X, X, X, X, X, X, X, 1, 2, 10, X, X, X, X, X,
X, X, X, X, X, X, X, X, 0, 8, 3, 1, 2, 10, X, X, X, X, X, X, X, X, X, X,
9, 2, 10, 0, 2, 9, X, X, X, X, X, X, X, X, X, X, 2, 8, 3, 2, 10, 8, 10, 9,
8, X, X, X, X, X, X, X, 3, 11, 2, X, X, X, X, X, X, X, X, X, X, X, X, X,
0, 11, 2, 8, 11, 0, X, X, X, X, X, X, X, X, X, X, 1, 9, 0, 2, 3, 11, X, X,
X, X, X, X, X, X, X, X, 1, 11, 2, 1, 9, 11, 9, 8, 11, X, X, X, X, X, X, X,
3, 10, 1, 11, 10, 3, X, X, X, X, X, X, X, X, X, X, 0, 10, 1, 0, 8, 10, 8, 11,
10, X, X, X, X, X, X, X, 3, 9, 0, 3, 11, 9, 11, 10, 9, X, X, X, X, X, X, X,
9, 8, 10, 10, 8, 11, X, X, X, X, X, X, X, X, X, X, 4, 7, 8, X, X, X, X, X,
X, X, X, X, X, X, X, X, 4, 3, 0, 7, 3, 4, X, X, X, X, X, X, X, X, X, X,
0, 1, 9, 8, 4, 7, X, X, X, X, X, X, X, X, X, X, 4, 1, 9, 4, 7, 1, 7, 3,
1, X, X, X, X, X, X, X, 1, 2, 10, 8, 4, 7, X, X, X, X, X, X, X, X, X, X,
3, 4, 7, 3, 0, 4, 1, 2, 10, X, X, X, X, X, X, X, 9, 2, 10, 9, 0, 2, 8, 4,
7, X, X, X, X, X, X, X, 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, X, X, X, X,
8, 4, 7, 3, 11, 2, X, X, X, X, X, X, X, X, X, X, 11, 4, 7, 11, 2, 4, 2, 0,
4, X, X, X, X, X, X, X, 9, 0, 1, 8, 4, 7, 2, 3, 11, X, X, X, X, X, X, X,
4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, X, X, X, X, 3, 10, 1, 3, 11, 10, 7, 8,
4, X, X, X, X, X, X, X, 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, X, X, X, X,
4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, X, X, X, X, 4, 7, 11, 4, 11, 9, 9, 11,
10, X, X, X, X, X, X, X, 9, 5, 4, X, X, X, X, X, X, X, X, X, X, X, X, X,
9, 5, 4, 0, 8, 3, X, X, X, X, X, X, X, X, X, X, 0, 5, 4, 1, 5, 0, X, X,
X, X, X, X, X, X, X, X, 8, 5, 4, 8, 3, 5, 3, 1, 5, X, X, X, X, X, X, X,
1, 2, 10, 9, 5, 4, X, X, X, X, X, X, X, X, X, X, 3, 0, 8, 1, 2, 10, 4, 9,
5, X, X, X, X, X, X, X, 5, 2, 10, 5, 4, 2, 4, 0, 2, X, X, X, X, X, X, X,
2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, X, X, X, X, 9, 5, 4, 2, 3, 11, X, X,
X, X, X, X, X, X, X, X, 0, 11, 2, 0, 8, 11, 4, 9, 5, X, X, X, X, X, X, X,
0, 5, 4, 0, 1, 5, 2, 3, 11, X, X, X, X, X, X, X, 2, 1, 5, 2, 5, 8, 2, 8,
11, 4, 8, 5, X, X, X, X, 10, 3, 11, 10, 1, 3, 9, 5, 4, X, X, X, X, X, X, X,
4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, X, X, X, X, 5, 4, 0, 5, 0, 11, 5, 11,
10, 11, 0, 3, X, X, X, X, 5, 4, 8, 5, 8, 10, 10, 8, 11, X, X, X, X, X, X, X,
9, 7, 8, 5, 7, 9, X, X, X, X, X, X, X, X, X, X, 9, 3, 0, 9, 5, 3, 5, 7,
3, X, X, X, X, X, X, X, 0, 7, 8, 0, 1, 7, 1, 5, 7, X, X, X, X, X, X, X,
1, 5, 3, 3, 5, 7, X, X, X, X, X, X, X, X, X, X, 9, 7, 8, 9, 5, 7, 10, 1,
2, X, X, X, X, X, X, X, 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, X, X, X, X,
8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, X, X, X, X, 2, 10, 5, 2, 5, 3, 3, 5,
7, X, X, X, X, X, X, X, 7, 9, 5, 7, 8, 9, 3, 11, 2, X, X, X, X, X, X, X,
9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, X, X, X, X, 2, 3, 11, 0, 1, 8, 1, 7,
8, 1, 5, 7, X, X, X, X, 11, 2, 1, 11, 1, 7, 7, 1, 5, X, X, X, X, X, X, X,
9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, X, X, X, X, 5, 7, 0, 5, 0, 9, 7, 11,
0, 1, 0, 10, 11, 10, 0, X, 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, X,
11, 10, 5, 7, 11, 5, X, X, X, X, X, X, X, X, X, X, 10, 6, 5, X, X, X, X, X,
X, X, X, X, X, X, X, X, 0, 8, 3, 5, 10, 6, X, X, X, X, X, X, X, X, X, X,
9, 0, 1, 5, 10, 6, X, X, X, X, X, X, X, X, X, X, 1, 8, 3, 1, 9, 8, 5, 10,
6, X, X, X, X, X, X, X, 1, 6, 5, 2, 6, 1, X, X, X, X, X, X, X, X, X, X,
1, 6, 5, 1, 2, 6, 3, 0, 8, X, X, X, X, X, X, X, 9, 6, 5, 9, 0, 6, 0, 2,
6, X, X, X, X, X, X, X, 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, X, X, X, X,
2, 3, 11, 10, 6, 5, X, X, X, X, X, X, X, X, X, X, 11, 0, 8, 11, 2, 0, 10, 6,
5, X, X, X, X, X, X, X, 0, 1, 9, 2, 3, 11, 5, 10, 6, X, X, X, X, X, X, X,
5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, X, X, X, X, 6, 3, 11, 6, 5, 3, 5, 1,
3, X, X, X, X, X, X, X, 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, X, X, X, X,
3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, X, X, X, X, 6, 5, 9, 6, 9, 11, 11, 9,
8, X, X, X, X, X, X, X, 5, 10, 6, 4, 7, 8, X, X, X, X, X, X, X, X, X, X,
4, 3, 0, 4, 7, 3, 6, 5, 10, X, X, X, X, X, X, X, 1, 9, 0, 5, 10, 6, 8, 4,
7, X, X, X, X, X, X, X, 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, X, X, X, X,
6, 1, 2, 6, 5, 1, 4, 7, 8, X, X, X, X, X, X, X, 1, 2, 5, 5, 2, 6, 3, 0,
4, 3, 4, 7, X, X, X, X, 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, X, X, X, X,
7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, X, 3, 11, 2, 7, 8, 4, 10, 6,
5, X, X, X, X, X, X, X, 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, X, X, X, X,
0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, X, X, X, X, 9, 2, 1, 9, 11, 2, 9, 4,
11, 7, 11, 4, 5, 10, 6, X, 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, X, X, X, X,
5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, X, 0, 5, 9, 0, 6, 5, 0, 3,
6, 11, 6, 3, 8, 4, 7, X, 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, X, X, X, X,
10, 4, 9, 6, 4, 10, X, X, X, X, X, X, X, X, X, X, 4, 10, 6, 4, 9, 10, 0, 8,
3, X, X, X, X, X, X, X, 10, 0, 1, 10, 6, 0, 6, 4, 0, X, X, X, X, X, X, X,
8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, X, X, X, X, 1, 4, 9, 1, 2, 4, 2, 6,
4, X, X, X, X, X, X, X, 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, X, X, X, X,
0, 2, 4, 4, 2, 6, X, X, X, X, X, X, X, X, X, X, 8, 3, 2, 8, 2, 4, 4, 2,
6, X, X, X, X, X, X, X, 10, 4, 9, 10, 6, 4, 11, 2, 3, X, X, X, X, X, X, X,
0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, X, X, X, X, 3, 11, 2, 0, 1, 6, 0, 6,
4, 6, 1, 10, X, X, X, X, 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, X,
9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, X, X, X, X, 8, 11, 1, 8, 1, 0, 11, 6,
1, 9, 1, 4, 6, 4, 1, X, 3, 11, 6, 3, 6, 0, 0, 6, 4, X, X, X, X, X, X, X,
6, 4, 8, 11, 6, 8, X, X, X, X, X, X, X, X, X, X, 7, 10, 6, 7, 8, 10, 8, 9,
10, X, X, X, X, X, X, X, 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, X, X, X, X,
10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, X, X, X, X, 10, 6, 7, 10, 7, 1, 1, 7,
3, X, X, X, X, X, X, X, 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, X, X, X, X,
2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, X, 7, 8, 0, 7, 0, 6, 6, 0,
2, X, X, X, X, X, X, X, 7, 3, 2, 6, 7, 2, X, X, X, X, X, X, X, X, X, X,
2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, X, X, X, X, 2, 0, 7, 2, 7, 11, 0, 9,
7, 6, 7, 10, 9, 10, 7, X, 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, X,
11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, X, X, X, X, 8, 9, 6, 8, 6, 7, 9, 1,
6, 11, 6, 3, 1, 3, 6, X, 0, 9, 1, 11, 6, 7, X, X, X, X, X, X, X, X, X, X,
7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, X, X, X, X, 7, 11, 6, X, X, X, X, X,
X, X, X, X, X, X, X, X, 7, 6, 11, X, X, X, X, X, X, X, X, X, X, X, X, X,
3, 0, 8, 11, 7, 6, X, X, X, X, X, X, X, X, X, X, 0, 1, 9, 11, 7, 6, X, X,
X, X, X, X, X, X, X, X, 8, 1, 9, 8, 3, 1, 11, 7, 6, X, X, X, X, X, X, X,
10, 1, 2, 6, 11, 7, X, X, X, X, X, X, X, X, X, X, 1, 2, 10, 3, 0, 8, 6, 11,
7, X, X, X, X, X, X, X, 2, 9, 0, 2, 10, 9, 6, 11, 7, X, X, X, X, X, X, X,
6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, X, X, X, X, 7, 2, 3, 6, 2, 7, X, X,
X, X, X, X, X, X, X, X, 7, 0, 8, 7, 6, 0, 6, 2, 0, X, X, X, X, X, X, X,
2, 7, 6, 2, 3, 7, 0, 1, 9, X, X, X, X, X, X, X, 1, 6, 2, 1, 8, 6, 1, 9,
8, 8, 7, 6, X, X, X, X, 10, 7, 6, 10, 1, 7, 1, 3, 7, X, X, X, X, X, X, X,
10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, X, X, X, X, 0, 3, 7, 0, 7, 10, 0, 10,
9, 6, 10, 7, X, X, X, X, 7, 6, 10, 7, 10, 8, 8, 10, 9, X, X, X, X, X, X, X,
6, 8, 4, 11, 8, 6, X, X, X, X, X, X, X, X, X, X, 3, 6, 11, 3, 0, 6, 0, 4,
6, X, X, X, X, X, X, X, 8, 6, 11, 8, 4, 6, 9, 0, 1, X, X, X, X, X, X, X,
9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, X, X, X, X, 6, 8, 4, 6, 11, 8, 2, 10,
1, X, X, X, X, X, X, X, 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, X, X, X, X,
4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, X, X, X, X, 10, 9, 3, 10, 3, 2, 9, 4,
3, 11, 3, 6, 4, 6, 3, X, 8, 2, 3, 8, 4, 2, 4, 6, 2, X, X, X, X, X, X, X,
0, 4, 2, 4, 6, 2, X, X, X, X, X, X, X, X, X, X, 1, 9, 0, 2, 3, 4, 2, 4,
6, 4, 3, 8, X, X, X, X, 1, 9, 4, 1, 4, 2, 2, 4, 6, X, X, X, X, X, X, X,
8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, X, X, X, X, 10, 1, 0, 10, 0, 6, 6, 0,
4, X, X, X, X, X, X, X, 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, X,
10, 9, 4, 6, 10, 4, X, X, X, X, X, X, X, X, X, X, 4, 9, 5, 7, 6, 11, X, X,
X, X, X, X, X, X, X, X, 0, 8, 3, 4, 9, 5, 11, 7, 6, X, X, X, X, X, X, X,
5, 0, 1, 5, 4, 0, 7, 6, 11, X, X, X, X, X, X, X, 11, 7, 6, 8, 3, 4, 3, 5,
4, 3, 1, 5, X, X, X, X, 9, 5, 4, 10, 1, 2, 7, 6, 11, X, X, X, X, X, X, X,
6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, X, X, X, X, 7, 6, 11, 5, 4, 10, 4, 2,
10, 4, 0, 2, X, X, X, X, 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, X,
7, 2, 3, 7, 6, 2, 5, 4, 9, X, X, X, X, X, X, X, 9, 5, 4, 0, 8, 6, 0, 6,
2, 6, 8, 7, X, X, X, X, 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, X, X, X, X,
6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, X, 9, 5, 4, 10, 1, 6, 1, 7,
6, 1, 3, 7, X, X, X, X, 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, X,
4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, X, 7, 6, 10, 7, 10, 8, 5, 4,
10, 4, 8, 10, X, X, X, X, 6, 9, 5, 6, 11, 9, 11, 8, 9, X, X, X, X, X, X, X,
3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, X, X, X, X, 0, 11, 8, 0, 5, 11, 0, 1,
5, 5, 6, 11, X, X, X, X, 6, 11, 3, 6, 3, 5, 5, 3, 1, X, X, X, X, X, X, X,
1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, X, X, X, X, 0, 11, 3, 0, 6, 11, 0, 9,
6, 5, 6, 9, 1, 2, 10, X, 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, X,
6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, X, X, X, X, 5, 8, 9, 5, 2, 8, 5, 6,
2, 3, 8, 2, X, X, X, X, 9, 5, 6, 9, 6, 0, 0, 6, 2, X, X, X, X, X, X, X,
1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, X, 1, 5, 6, 2, 1, 6, X, X,
X, X, X, X, X, X, X, X, 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, X,
10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, X, X, X, X, 0, 3, 8, 5, 6, 10, X, X,
X, X, X, X, X, X, X, X, 10, 5, 6, X, X, X, X, X, X, X, X, X, X, X, X, X,
11, 5, 10, 7, 5, 11, X, X, X, X, X, X, X, X, X, X, 11, 5, 10, 11, 7, 5, 8, 3,
0, X, X, X, X, X, X, X, 5, 11, 7, 5, 10, 11, 1, 9, 0, X, X, X, X, X, X, X,
10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, X, X, X, X, 11, 1, 2, 11, 7, 1, 7, 5,
1, X, X, X, X, X, X, X, 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, X, X, X, X,
9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, X, X, X, X, 7, 5, 2, 7, 2, 11, 5, 9,
2, 3, 2, 8, 9, 8, 2, X, 2, 5, 10, 2, 3, 5, 3, 7, 5, X, X, X, X, X, X, X,
8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, X, X, X, X, 9, 0, 1, 5, 10, 3, 5, 3,
7, 3, 10, 2, X, X, X, X, 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, X,
1, 3, 5, 3, 7, 5, X, X, X, X, X, X, X, X, X, X, 0, 8, 7, 0, 7, 1, 1, 7,
5, X, X, X, X, X, X, X, 9, 0, 3, 9, 3, 5, 5, 3, 7, X, X, X, X, X, X, X,
9, 8, 7, 5, 9, 7, X, X, X, X, X, X, X, X, X, X, 5, 8, 4, 5, 10, 8, 10, 11,
8, X, X, X, X, X, X, X, 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, X, X, X, X,
0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, X, X, X, X, 10, 11, 4, 10, 4, 5, 11, 3,
4, 9, 4, 1, 3, 1, 4, X, 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, X, X, X, X,
0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, X, 0, 2, 5, 0, 5, 9, 2, 11,
5, 4, 5, 8, 11, 8, 5, X, 9, 4, 5, 2, 11, 3, X, X, X, X, X, X, X, X, X, X,
2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, X, X, X, X, 5, 10, 2, 5, 2, 4, 4, 2,
0, X, X, X, X, X, X, X, 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, X,
5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, X, X, X, X, 8, 4, 5, 8, 5, 3, 3, 5,
1, X, X, X, X, X, X, X, 0, 4, 5, 1, 0, 5, X, X, X, X, X, X, X, X, X, X,
8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, X, X, X, X, 9, 4, 5, X, X, X, X, X,
X, X, X, X, X, X, X, X, 4, 11, 7, 4, 9, 11, 9, 10, 11, X, X, X, X, X, X, X,
0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, X, X, X, X, 1, 10, 11, 1, 11, 4, 1, 4,
0, 7, 4, 11, X, X, X, X, 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, X,
4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, X, X, X, X, 9, 7, 4, 9, 11, 7, 9, 1,
11, 2, 11, 1, 0, 8, 3, X, 11, 7, 4, 11, 4, 2, 2, 4, 0, X, X, X, X, X, X, X,
11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, X, X, X, X, 2, 9, 10, 2, 7, 9, 2, 3,
7, 7, 4, 9, X, X, X, X, 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, X,
3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, X, 1, 10, 2, 8, 7, 4, X, X,
X, X, X, X, X, X, X, X, 4, 9, 1, 4, 1, 7, 7, 1, 3, X, X, X, X, X, X, X,
4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, X, X, X, X, 4, 0, 3, 7, 4, 3, X, X,
X, X, X, X, X, X, X, X, 4, 8, 7, X, X, X, X, X, X, X, X, X, X, X, X, X,
9, 10, 8, 10, 11, 8, X, X, X, X, X, X, X, X, X, X, 3, 0, 9, 3, 9, 11, 11, 9,
10, X, X, X, X, X, X, X, 0, 1, 10, 0, 10, 8, 8, 10, 11, X, X, X, X, X, X, X,
3, 1, 10, 11, 3, 10, X, X, X, X, X, X, X, X, X, X, 1, 2, 11, 1, 11, 9, 9, 11,
8, X, X, X, X, X, X, X, 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, X, X, X, X,
0, 2, 11, 8, 0, 11, X, X, X, X, X, X, X, X, X, X, 3, 2, 11, X, X, X, X, X,
X, X, X, X, X, X, X, X, 2, 3, 8, 2, 8, 10, 10, 8, 9, X, X, X, X, X, X, X,
9, 10, 2, 0, 9, 2, X, X, X, X, X, X, X, X, X, X, 2, 3, 8, 2, 8, 10, 0, 1,
8, 1, 10, 8, X, X, X, X, 1, 10, 2, X, X, X, X, X, X, X, X, X, X, X, X, X,
1, 3, 8, 9, 1, 8, X, X, X, X, X, X, X, X, X, X, 0, 9, 1, X, X, X, X, X,
X, X, X, X, X, X, X, X, 0, 3, 8, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X
#undef X
});
// size: nCasesLT3d * nTriTableLT3dElemSize
const vtkm::cont::ArrayHandle<vtkm::Id> triTableLT3d = vtkm::cont::make_ArrayHandle<vtkm::Id>({
#define X -1
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 16,
18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, 0, 1, 17, 0, 9, 17, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17,
14, 9, 17, 3, 12, 18, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 1, 10, 12, 2, 10, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0,
14, 18, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, 2, 10, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18,
17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X,
X, X, X, X, X, X, X, X, 3, 2, 15, 3, 11, 15, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18,
0, 14, 18, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 11, 15, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8,
14, 18, 8, 16, 18, X, X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 11, 15,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16,
18, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3,
12, 10, 3, 15, 10, 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, 18,
8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 8, 4, 13, 8, 7, 13, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16,
18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17,
8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 16,
18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12, 1, 10, 12,
2, 10, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10,
3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 12,
10, 0, 17, 10, 0, 9, 17, 12, 2, 10, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3,
2, 10, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X,
3, 2, 15, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2,
15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X,
X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17,
14, 9, 17, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13,
16, 7, 13, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0,
14, 18, 18, 15, 10, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X,
X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 11, 15,
8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18,
17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7,
13, X, X, X, X, X, X, X, 14, 9, 5, 14, 4, 5, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18,
0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 14, 4, 5, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 14, 4, 5,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 18,
5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0,
17, 5, 12, 2, 10, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 18, 5, 8, 4, 5,
8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 11,
15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16,
11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5,
0, 17, 5, 3, 2, 15, 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18,
15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 12, 1, 10, 14,
9, 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10,
16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 0, 12,
10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 14, 4, 5, X, X,
X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 16, 18, 15, 16,
11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X,
14, 9, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12,
18, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X,
X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5,
3, 12, 18, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0,
18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X,
X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 8, 14, 5,
8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17,
5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X,
X, X, X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8,
7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18,
0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13,
16, 7, 13, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 11,
15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16,
18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10,
3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 18, 13,
5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0,
17, 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X,
X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13,
16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15,
10, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3,
16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, 0, 18, 10, 0, 1, 10,
0, 18, 5, 0, 9, 5, 18, 15, 10, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X,
X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15,
6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, 12, 18, 17, 12,
1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X,
X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 16, 6,
3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, 0, 12,
18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X,
X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8,
14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X,
18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17,
5, 12, 2, 10, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X,
X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3,
11, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10,
16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 18, 13,
5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0,
17, 5, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6,
18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 16, 11,
6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 18, 15, 6, 8, 18, 5, 8,
4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10,
0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 14, 4, 5,
16, 7, 6, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15,
10, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12,
1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15,
12, 2, 15, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X,
X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7,
6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12,
18, 15, 12, 2, 15, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X,
X, X, X, 14, 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18,
10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X,
X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 16, 11, 6, 14,
4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10,
0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5,
8, 18, 6, 8, 7, 6, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 16, 11, 6, 14, 4, 5, 16, 7,
6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12,
1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8,
7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 16, 11, 6, 14, 4, 5,
16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12,
18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18,
6, 8, 7, 6, X, X, X, X, X, X, X, 14, 9, 5, 16, 11, 6, 14, 4, 5, 16, 7, 6, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12,
10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X,
X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18,
13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10,
3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 18, 15,
6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3,
2, 15, 3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 14, 18, 13,
14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 3, 2, 15, 3, 16,
6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3,
18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, 0, 12, 10,
0, 17, 10, 0, 9, 17, 12, 2, 10, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2,
10, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, 12,
1, 10, 12, 2, 10, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17,
3, 12, 18, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X,
X, 0, 1, 17, 0, 9, 17, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3,
18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, X,
X, X, X, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9,
17, 18, 15, 10, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3,
15, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10,
0, 14, 18, 18, 15, 10, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X,
X, X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 16, 7,
6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12,
1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8,
7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 16, 6, 3, 15, 6,
16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12,
18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X,
X, X, X, X, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, 6, 3, 15, 6, 16, 7, 6, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 14, 18,
8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, 2,
10, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8,
14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 12, 2, 10, 16, 11, 6,
16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 18, 6, 3, 11,
6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 16,
11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 14, 18,
8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 16, 11, 6, 16, 7,
6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18,
0, 14, 18, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 16, 11, 6, 16, 7, 6, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 14, 18, 8,
18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 12, 2, 10, 16, 11, 6, 16, 7, 6,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 14,
18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12,
2, 10, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6,
8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, 6, 3, 15,
6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 14, 18, 8,
18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17,
3, 2, 15, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2,
15, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 3,
12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 18, 15, 6,
8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12,
10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X,
X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 18,
15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X,
16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 18,
6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X,
X, X, 0, 1, 17, 0, 9, 17, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17,
14, 9, 17, 3, 12, 18, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X,
X, X, X, X, 12, 1, 10, 12, 2, 10, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0,
14, 18, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X,
X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, 2, 10, 16, 11, 6, 8, 4, 13,
8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18,
17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13,
6, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8,
13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18,
0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 16, 6, 3, 15,
6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 18, 15, 6, 14, 18, 13, 14,
4, 13, 18, 13, 6, X, X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 16, 6,
3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13,
6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3,
12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X,
X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13,
18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 16, 11, 6, 14, 4,
5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8,
18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5,
0, 17, 5, 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11,
6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 14,
9, 5, 12, 2, 10, 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10,
3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, 0, 12,
10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X,
X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3,
18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X,
14, 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18,
15, 12, 2, 15, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X,
X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16,
7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5,
12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X,
X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 14, 4,
5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0,
18, 5, 0, 9, 5, 18, 15, 10, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X,
X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10,
3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, 18, 17, 10, 18, 17,
5, 18, 15, 10, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X,
X, X, X, X, X, X, X, X, 14, 9, 5, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8,
13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18,
0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 16, 11, 6, 8, 14,
5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 16, 11, 6,
8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11,
6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0,
17, 5, 12, 2, 10, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X,
X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 18, 13, 5,
18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 16,
6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 18,
13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5,
0, 17, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6,
X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 18, 15,
6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14,
9, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8,
13, 6, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10,
18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12,
10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 14,
5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 18, 15, 6, 18,
13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
18, 17, 10, 18, 17, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13, 16, 7, 13,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17,
5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X,
X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 18,
13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10,
3, 15, 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11,
15, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0,
17, 5, 3, 2, 15, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15,
16, 11, 15, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 14, 9, 5, 3, 2,
15, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 18,
13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10,
0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18,
10, 3, 2, 10, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12,
1, 10, 14, 9, 5, 12, 2, 10, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18,
3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3,
12, 18, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X,
X, X, X, 14, 9, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15,
10, 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X,
X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3,
11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10,
0, 18, 5, 0, 9, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18,
X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 14, 4,
5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12,
1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8,
16, 18, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 11, 15,
14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12,
18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4,
5, 8, 16, 18, X, X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 11, 15, 14, 4, 5, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17,
5, 12, 2, 10, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8,
18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10,
14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5, 8, 4,
5, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0,
17, 5, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5,
8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 14, 4,
5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16,
11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 12, 10,
0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 16, 18,
15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12,
1, 10, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17,
12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13,
X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12,
2, 15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X,
X, X, X, 3, 2, 15, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9,
17, 3, 18, 10, 3, 2, 10, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X,
X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, 2, 10, 8, 4, 13, 8, 7, 13, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10,
0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13,
X, X, X, X, X, X, X, 12, 1, 10, 12, 2, 10, 8, 4, 13, 8, 7, 13, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12,
1, 17, 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16,
7, 13, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 8, 4, 13, 8, 7, 13, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12,
18, 0, 14, 18, 3, 12, 18, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X,
X, X, X, X, X, X, X, X, X, X, X, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18,
X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12,
10, 3, 15, 10, 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8,
16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10,
3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 16, 18,
15, 16, 11, 15, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3,
2, 15, 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15,
8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 3, 2, 15, 3, 11,
15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3,
16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10,
0, 17, 10, 0, 9, 17, 12, 2, 10, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2,
10, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 12,
1, 10, 12, 2, 10, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17,
3, 12, 18, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X,
X, 0, 1, 17, 0, 9, 17, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3,
16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X
#undef X
});
const vtkm::Id nLabelEdgeTableMC3dElemSize = 9; // (at most 4 label edges)
const vtkm::Id nLabelEdgeTableLT3dElemSize = 13; // (at most 6 tetrahedra)
// size: nCasesMC3d * nLabelEdgeTableMC3dElemSize
const vtkm::cont::ArrayHandle<vtkm::Id> labelEdgeTableMC3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({
#define X -1
X, X, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, 2, 1, X, X, X,
X, X, X, X, 1, 1, X, X, X, X, X, X, X, 1, 0, 1, 1, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 3,
2, X, X, X, X, X, X, X, 1, 2, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 1, 0, 1, 2, X, X,
X, X, X, 3, 1, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 3, 0,
X, X, X, X, X, X, X, 2, 8, X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X,
X, X, 1, 0, 1, 4, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 1, 1, 1, 4, X, X, X, X, X, 2, 0, 1,
1, X, X, X, X, X, 2, 0, 1, 4, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 1, 4, 1, 2, X, X, X, X,
X, 3, 0, X, X, X, X, X, X, X, 1, 0, 1, 4, 1, 2, X, X, X, 4, 1, X, X, X, X, X, X, X, 2, 1, 1, 4,
X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 4, 3, 0, X, X, X, X, X, 3, 4, X, X, X, X, X, X, X,
1, 4, X, X, X, X, X, X, X, 1, 4, 1, 0, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 3, 1, X, X, X,
X, X, X, X, 1, 1, 1, 4, X, X, X, X, X, 1, 0, 1, 1, 1, 4, X, X, X, 3, 0, X, X, X, X, X, X, X, 4,
2, X, X, X, X, X, X, X, 1, 4, 1, 2, X, X, X, X, X, 2, 0, 1, 4, X, X, X, X, X, 2, 0, 1, 2, X, X,
X, X, X, 4, 1, X, X, X, X, X, X, X, 2, 1, 1, 4, X, X, X, X, X, 1, 4, 3, 0, X, X, X, X, X, 4, 0,
X, X, X, X, X, X, X, 3, 4, X, X, X, X, X, X, X, 2, 5, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X,
X, X, 3, 0, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 2, 5, 1, 1, X, X, X, X, X, 1, 1, 3,
0, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 3, 2, X, X, X, X, X, X, X, 2, 5, 1, 2, X, X, X, X,
X, 4, 0, X, X, X, X, X, X, X, 1, 2, 3, 0, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 2, 5, 2, 1,
X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 2, 5, X, X, X, X, X, X, X,
1, 5, X, X, X, X, X, X, X, 1, 0, 1, 5, X, X, X, X, X, 1, 0, 1, 5, X, X, X, X, X, 2, 1, 1, 5, X,
X, X, X, X, 2, 1, X, X, X, X, X, X, X, 2, 1, 1, 0, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 4,
2, X, X, X, X, X, X, X, 1, 2, 1, 5, X, X, X, X, X, 2, 0, 1, 5, X, X, X, X, X, 1, 0, 1, 2, 1, 5,
X, X, X, 1, 5, 3, 1, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 4, 0,
X, X, X, X, X, X, X, 3, 5, X, X, X, X, X, X, X, 1, 5, 1, 4, X, X, X, X, X, 2, 0, 1, 5, X, X, X,
X, X, 1, 0, 1, 5, 1, 4, X, X, X, 1, 5, 3, 1, X, X, X, X, X, 2, 1, 1, 4, X, X, X, X, X, 2, 1, 2,
0, X, X, X, X, X, 1, 4, 3, 0, X, X, X, X, X, 5, 2, X, X, X, X, X, X, X, 1, 2, 1, 4, 1, 5, X, X,
X, 1, 5, 3, 0, X, X, X, X, X, 1, 0, 1, 4, 1, 2, 1, 5, X, 4, 1, 1, 5, X, X, X, X, X, 1, 4, 3, 1,
X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 4, 0, 1, 4, X, X, X, X, X, 4, 4, X, X, X, X, X, X, X,
2, 4, X, X, X, X, X, X, X, 2, 4, 1, 0, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 4, 1, X, X, X,
X, X, X, X, 3, 1, X, X, X, X, X, X, X, 1, 0, 3, 1, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 3,
2, X, X, X, X, X, X, X, 2, 4, 1, 2, X, X, X, X, X, 2, 0, 2, 4, X, X, X, X, X, 1, 2, 3, 0, X, X,
X, X, X, 5, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 3, 0,
X, X, X, X, X, X, X, 2, 4, X, X, X, X, X, X, X, 3, 6, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X,
X, X, 4, 0, X, X, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, 0, X,
X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 2, 2, X, X, X, X, X, X, X, 1, 2, 3, 6, X, X, X, X,
X, 5, 0, X, X, X, X, X, X, X, 4, 0, 1, 2, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, 1, X, X,
X, X, X, X, X, 1, 0, 1, 6, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 6, X, X, X, X, X, X, X,
1, 6, X, X, X, X, X, X, X, 1, 0, 1, 6, X, X, X, X, X, 1, 0, 1, 6, X, X, X, X, X, 2, 1, 1, 6, X,
X, X, X, X, 1, 1, 1, 6, X, X, X, X, X, 1, 1, 1, 0, 1, 6, X, X, X, 2, 0, 1, 6, X, X, X, X, X, 1,
6, 3, 2, X, X, X, X, X, 2, 2, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 2, 2, 1, 0, X, X,
X, X, X, 4, 1, X, X, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 4, 0,
X, X, X, X, X, X, X, 3, 6, X, X, X, X, X, X, X, 2, 4, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X,
X, X, 2, 4, 1, 0, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 2, 4, 1, 1, X, X, X, X, X, 1, 1, 3,
0, X, X, X, X, X, 2, 4, 2, 0, X, X, X, X, X, 5, 2, X, X, X, X, X, X, X, 3, 2, X, X, X, X, X, X,
X, 2, 0, X, X, X, X, X, X, X, 1, 0, 3, 2, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 1, X, X,
X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 2, 4, X, X, X, X, X, X, X,
1, 4, 1, 6, X, X, X, X, X, 1, 0, 1, 4, 1, 6, X, X, X, 2, 0, 1, 6, X, X, X, X, X, 1, 6, 3, 1, X,
X, X, X, X, 1, 4, 1, 1, 1, 6, X, X, X, 1, 6, 1, 1, 1, 0, 1, 4, X, 1, 6, 3, 0, X, X, X, X, X, 4,
2, 1, 6, X, X, X, X, X, 2, 2, 1, 4, X, X, X, X, X, 1, 4, 3, 0, X, X, X, X, X, 2, 2, 2, 0, X, X,
X, X, X, 5, 1, X, X, X, X, X, X, X, 1, 4, 3, 1, X, X, X, X, X, 4, 0, 1, 4, X, X, X, X, X, 5, 0,
X, X, X, X, X, X, X, 4, 4, X, X, X, X, X, X, X, 3, 5, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X,
X, X, 4, 0, X, X, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 1, 1, 3, 5, X, X, X, X, X, 4, 0, 1,
1, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X,
X, 3, 0, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 5, 1, X, X,
X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 0, 1, 5, X, X, X, X, X, 1, 5, X, X, X, X, X, X, X,
2, 5, X, X, X, X, X, X, X, 2, 5, 1, 0, X, X, X, X, X, 2, 5, 1, 0, X, X, X, X, X, 2, 5, 2, 1, X,
X, X, X, X, 3, 1, X, X, X, X, X, X, X, 1, 0, 3, 1, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 5,
2, X, X, X, X, X, X, X, 3, 2, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 0, 3, 2, X, X,
X, X, X, 5, 1, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 3, 0,
X, X, X, X, X, X, X, 2, 5, X, X, X, X, X, X, X, 3, 4, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X,
X, X, 1, 0, 3, 4, X, X, X, X, X, 5, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, 0, X,
X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 1, 4, 1, 2, X, X, X, X, X, 4, 2, X, X, X, X, X, X,
X, 3, 0, X, X, X, X, X, X, X, 4, 2, 1, 0, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 3, 1, X, X,
X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, X,
3, 4, X, X, X, X, X, X, X, 1, 0, 3, 4, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 5, 1, X, X, X,
X, X, X, X, 4, 1, X, X, X, X, X, X, X, 4, 1, 1, 0, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 4,
2, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 5, 0, X, X, X, X,
X, X, X, 1, 1, 1, 4, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 2, 0,
X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, X, 2, 8, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X,
X, X, 3, 0, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, X,
X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 1, 2, X, X, X, X, X, X, X, 3, 2, X, X, X, X, X, X,
X, 2, 0, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 1, X, X, X, X, X, X, X, 2, 1, X, X,
X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X
#undef X
});
// size: nCasesLT3d * nLabelEdgeTableLT3dElemSize
// (TODO/FIXME: it can be improved to be shortened a little bit)
const vtkm::cont::ArrayHandle<vtkm::Id> labelEdgeTableLT3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({
#define X -1
X, X, X, X, X, X, X, X, X, X, X, X, X, 6, 18, X, X, X, X, X, X, X, X, X, X, X,
2, 0, X, X, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 4, 18, X, X, X, X, X, X, X,
2, 12, X, X, X, X, X, X, X, X, X, X, X, 2, 0, 1, 18, 2, 3, 3, 18, X, X, X, X, X,
3, 0, 1, 12, X, X, X, X, X, X, X, X, X, 1, 18, 2, 14, 2, 3, 3, 18, X, X, X, X, X,
2, 3, X, X, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 2, 16, 2, 18, X, X, X, X, X,
2, 0, 2, 3, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 2, 16, 2, 18, X, X, X,
1, 12, 3, 3, X, X, X, X, X, X, X, X, X, 2, 0, 2, 18, 2, 16, 2, 18, X, X, X, X, X,
3, 0, 3, 3, X, X, X, X, X, X, X, X, X, 1, 18, 2, 14, 1, 18, 2, 16, 2, 18, X, X, X,
2, 8, X, X, X, X, X, X, X, X, X, X, X, 4, 18, 2, 14, 2, 16, X, X, X, X, X, X, X,
2, 0, 2, 8, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 18, 2, 14, 2, 16, X, X, X,
2, 12, 2, 8, X, X, X, X, X, X, X, X, X, 2, 0, 1, 18, 2, 3, 1, 18, 2, 14, 2, 16, X,
3, 0, 1, 12, 2, 8, X, X, X, X, X, X, X, 1, 18, 2, 14, 2, 3, 1, 18, 2, 14, 2, 16, X,
2, 3, 2, 8, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 2, 16, 2, 14, 2, 16, X, X, X,
2, 0, 2, 3, 2, 8, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 2, 16, 2, 14, 2, 16, X,
1, 12, 3, 3, 2, 8, X, X, X, X, X, X, X, 2, 0, 2, 18, 2, 16, 2, 14, 2, 16, X, X, X,
3, 0, 3, 3, 2, 8, X, X, X, X, X, X, X, 1, 18, 2, 14, 1, 18, 2, 16, 2, 14, 2, 16, X,
2, 14, X, X, X, X, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 18, 2, 8, 1, 18, X, X, X,
3, 0, 1, 14, X, X, X, X, X, X, X, X, X, 2, 12, 3, 18, 2, 8, 1, 18, X, X, X, X, X,
1, 12, 1, 14, 1, 12, 1, 14, X, X, X, X, X, 4, 0, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X,
4, 0, 1, 12, 1, 14, X, X, X, X, X, X, X, 2, 18, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X,
1, 14, 2, 3, 1, 14, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 2, 16, 2, 8, 1, 18, X,
3, 0, 2, 3, 1, 14, X, X, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 2, 16, 2, 8, 1, 18, X,
1, 12, 1, 14, 3, 3, 1, 14, X, X, X, X, X, 4, 0, 1, 18, 2, 16, 2, 8, 1, 18, X, X, X,
4, 0, 3, 3, 1, 14, X, X, X, X, X, X, X, 3, 18, 2, 16, 2, 8, 1, 18, X, X, X, X, X,
1, 14, 3, 8, X, X, X, X, X, X, X, X, X, 1, 18, 2, 0, 3, 18, 2, 16, X, X, X, X, X,
3, 0, 3, 8, X, X, X, X, X, X, X, X, X, 2, 12, 4, 18, 2, 16, X, X, X, X, X, X, X,
1, 12, 1, 14, 1, 12, 3, 8, X, X, X, X, X, 4, 0, 2, 3, 2, 18, 2, 16, X, X, X, X, X,
4, 0, 1, 12, 3, 8, X, X, X, X, X, X, X, 2, 18, 2, 3, 2, 18, 2, 16, X, X, X, X, X,
1, 14, 2, 3, 3, 8, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 2, 16, 1, 18, 2, 16, X,
3, 0, 2, 3, 3, 8, X, X, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 2, 16, 1, 18, 2, 16, X,
1, 12, 1, 14, 3, 3, 3, 8, X, X, X, X, X, 4, 0, 1, 18, 2, 16, 1, 18, 2, 16, X, X, X,
4, 0, 3, 3, 3, 8, X, X, X, X, X, X, X, 3, 18, 2, 16, 1, 18, 2, 16, X, X, X, X, X,
6, 18, X, X, X, X, X, X, X, X, X, X, X, 4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X,
4, 0, 4, 18, X, X, X, X, X, X, X, X, X, 1, 12, 1, 14, 4, 3, 4, 8, X, X, X, X, X,
2, 12, 1, 18, 2, 12, 3, 18, X, X, X, X, X, 3, 0, 3, 3, 4, 8, X, X, X, X, X, X, X,
1, 18, 2, 0, 2, 12, 3, 18, X, X, X, X, X, 1, 14, 3, 3, 4, 8, X, X, X, X, X, X, X,
2, 18, 4, 3, 2, 18, X, X, X, X, X, X, X, 4, 0, 1, 12, 1, 16, 4, 8, X, X, X, X, X,
4, 0, 4, 3, 2, 18, X, X, X, X, X, X, X, 1, 12, 1, 14, 1, 12, 1, 16, 4, 8, X, X, X,
2, 12, 2, 18, 2, 3, 2, 18, X, X, X, X, X, 3, 0, 1, 16, 4, 8, X, X, X, X, X, X, X,
1, 18, 2, 0, 1, 18, 2, 3, 2, 18, X, X, X, 1, 14, 1, 16, 4, 8, X, X, X, X, X, X, X,
4, 18, 4, 8, X, X, X, X, X, X, X, X, X, 4, 0, 4, 3, 1, 14, 1, 16, X, X, X, X, X,
4, 0, 2, 18, 4, 8, X, X, X, X, X, X, X, 1, 12, 1, 14, 4, 3, 1, 14, 1, 16, X, X, X,
2, 12, 1, 18, 2, 12, 1, 18, 4, 8, X, X, X, 3, 0, 3, 3, 1, 14, 1, 16, X, X, X, X, X,
1, 18, 2, 0, 2, 12, 1, 18, 4, 8, X, X, X, 1, 14, 3, 3, 1, 14, 1, 16, X, X, X, X, X,
2, 18, 4, 3, 4, 8, X, X, X, X, X, X, X, 4, 0, 1, 12, 1, 16, 1, 14, 1, 16, X, X, X,
4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X, 1, 12, 1, 14, 1, 12, 1, 16, 1, 14, 1, 16, X,
2, 12, 2, 18, 2, 3, 4, 8, X, X, X, X, X, 3, 0, 1, 16, 1, 14, 1, 16, X, X, X, X, X,
1, 18, 2, 0, 1, 18, 2, 3, 4, 8, X, X, X, 1, 14, 1, 16, 1, 14, 1, 16, X, X, X, X, X,
1, 18, 2, 14, 2, 18, 2, 14, 1, 18, X, X, X, 3, 0, 4, 3, 3, 8, X, X, X, X, X, X, X,
2, 0, 3, 18, 2, 14, 1, 18, X, X, X, X, X, 1, 12, 4, 3, 3, 8, X, X, X, X, X, X, X,
2, 12, 2, 14, 2, 12, 1, 18, 2, 14, 1, 18, X, 2, 0, 3, 3, 3, 8, X, X, X, X, X, X, X,
2, 18, 2, 12, 1, 18, 2, 14, 1, 18, X, X, X, 3, 3, 3, 8, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 4, 3, 2, 14, 1, 18, X, X, X, 3, 0, 1, 12, 1, 16, 3, 8, X, X, X, X, X,
2, 0, 1, 18, 4, 3, 2, 14, 1, 18, X, X, X, 2, 12, 1, 16, 3, 8, X, X, X, X, X, X, X,
2, 12, 2, 14, 1, 18, 2, 3, 2, 14, 1, 18, X, 2, 0, 1, 16, 3, 8, X, X, X, X, X, X, X,
3, 18, 2, 3, 2, 14, 1, 18, X, X, X, X, X, 1, 16, 3, 8, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 3, 18, 2, 8, X, X, X, X, X, 3, 0, 4, 3, 1, 16, X, X, X, X, X, X, X,
2, 0, 4, 18, 2, 8, X, X, X, X, X, X, X, 1, 12, 4, 3, 1, 16, X, X, X, X, X, X, X,
2, 12, 2, 14, 2, 12, 2, 18, 2, 8, X, X, X, 2, 0, 3, 3, 1, 16, X, X, X, X, X, X, X,
2, 18, 2, 12, 2, 18, 2, 8, X, X, X, X, X, 3, 3, 1, 16, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 4, 3, 1, 18, 2, 8, X, X, X, 3, 0, 1, 12, 2, 16, X, X, X, X, X, X, X,
2, 0, 1, 18, 4, 3, 1, 18, 2, 8, X, X, X, 2, 12, 2, 16, X, X, X, X, X, X, X, X, X,
2, 12, 2, 14, 1, 18, 2, 3, 1, 18, 2, 8, X, 2, 0, 2, 16, X, X, X, X, X, X, X, X, X,
3, 18, 2, 3, 1, 18, 2, 8, X, X, X, X, X, 2, 16, X, X, X, X, X, X, X, X, X, X, X,
2, 16, X, X, X, X, X, X, X, X, X, X, X, 3, 18, 2, 3, 1, 18, 2, 8, X, X, X, X, X,
2, 0, 2, 16, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 1, 18, 2, 3, 1, 18, 2, 8, X,
2, 12, 2, 16, X, X, X, X, X, X, X, X, X, 2, 0, 1, 18, 4, 3, 1, 18, 2, 8, X, X, X,
3, 0, 1, 12, 2, 16, X, X, X, X, X, X, X, 1, 18, 2, 14, 4, 3, 1, 18, 2, 8, X, X, X,
3, 3, 1, 16, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 2, 18, 2, 8, X, X, X, X, X,
2, 0, 3, 3, 1, 16, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 2, 18, 2, 8, X, X, X,
1, 12, 4, 3, 1, 16, X, X, X, X, X, X, X, 2, 0, 4, 18, 2, 8, X, X, X, X, X, X, X,
3, 0, 4, 3, 1, 16, X, X, X, X, X, X, X, 1, 18, 2, 14, 3, 18, 2, 8, X, X, X, X, X,
1, 16, 3, 8, X, X, X, X, X, X, X, X, X, 3, 18, 2, 3, 2, 14, 1, 18, X, X, X, X, X,
2, 0, 1, 16, 3, 8, X, X, X, X, X, X, X, 2, 12, 2, 14, 1, 18, 2, 3, 2, 14, 1, 18, X,
2, 12, 1, 16, 3, 8, X, X, X, X, X, X, X, 2, 0, 1, 18, 4, 3, 2, 14, 1, 18, X, X, X,
3, 0, 1, 12, 1, 16, 3, 8, X, X, X, X, X, 1, 18, 2, 14, 4, 3, 2, 14, 1, 18, X, X, X,
3, 3, 3, 8, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 1, 18, 2, 14, 1, 18, X, X, X,
2, 0, 3, 3, 3, 8, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 1, 18, 2, 14, 1, 18, X,
1, 12, 4, 3, 3, 8, X, X, X, X, X, X, X, 2, 0, 3, 18, 2, 14, 1, 18, X, X, X, X, X,
3, 0, 4, 3, 3, 8, X, X, X, X, X, X, X, 1, 18, 2, 14, 2, 18, 2, 14, 1, 18, X, X, X,
1, 14, 1, 16, 1, 14, 1, 16, X, X, X, X, X, 1, 18, 2, 0, 1, 18, 2, 3, 4, 8, X, X, X,
3, 0, 1, 16, 1, 14, 1, 16, X, X, X, X, X, 2, 12, 2, 18, 2, 3, 4, 8, X, X, X, X, X,
1, 12, 1, 14, 1, 12, 1, 16, 1, 14, 1, 16, X, 4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X,
4, 0, 1, 12, 1, 16, 1, 14, 1, 16, X, X, X, 2, 18, 4, 3, 4, 8, X, X, X, X, X, X, X,
1, 14, 3, 3, 1, 14, 1, 16, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 1, 18, 4, 8, X, X, X,
3, 0, 3, 3, 1, 14, 1, 16, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 1, 18, 4, 8, X, X, X,
1, 12, 1, 14, 4, 3, 1, 14, 1, 16, X, X, X, 4, 0, 2, 18, 4, 8, X, X, X, X, X, X, X,
4, 0, 4, 3, 1, 14, 1, 16, X, X, X, X, X, 4, 18, 4, 8, X, X, X, X, X, X, X, X, X,
1, 14, 1, 16, 4, 8, X, X, X, X, X, X, X, 1, 18, 2, 0, 1, 18, 2, 3, 2, 18, X, X, X,
3, 0, 1, 16, 4, 8, X, X, X, X, X, X, X, 2, 12, 2, 18, 2, 3, 2, 18, X, X, X, X, X,
1, 12, 1, 14, 1, 12, 1, 16, 4, 8, X, X, X, 4, 0, 4, 3, 2, 18, X, X, X, X, X, X, X,
4, 0, 1, 12, 1, 16, 4, 8, X, X, X, X, X, 2, 18, 4, 3, 2, 18, X, X, X, X, X, X, X,
1, 14, 3, 3, 4, 8, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 3, 18, X, X, X, X, X,
3, 0, 3, 3, 4, 8, X, X, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 3, 18, X, X, X, X, X,
1, 12, 1, 14, 4, 3, 4, 8, X, X, X, X, X, 4, 0, 4, 18, X, X, X, X, X, X, X, X, X,
4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X, 6, 18, X, X, X, X, X, X, X, X, X, X, X,
3, 18, 2, 16, 1, 18, 2, 16, X, X, X, X, X, 4, 0, 3, 3, 3, 8, X, X, X, X, X, X, X,
4, 0, 1, 18, 2, 16, 1, 18, 2, 16, X, X, X, 1, 12, 1, 14, 3, 3, 3, 8, X, X, X, X, X,
2, 12, 1, 18, 2, 12, 2, 16, 1, 18, 2, 16, X, 3, 0, 2, 3, 3, 8, X, X, X, X, X, X, X,
1, 18, 2, 0, 2, 12, 2, 16, 1, 18, 2, 16, X, 1, 14, 2, 3, 3, 8, X, X, X, X, X, X, X,
2, 18, 2, 3, 2, 18, 2, 16, X, X, X, X, X, 4, 0, 1, 12, 3, 8, X, X, X, X, X, X, X,
4, 0, 2, 3, 2, 18, 2, 16, X, X, X, X, X, 1, 12, 1, 14, 1, 12, 3, 8, X, X, X, X, X,
2, 12, 4, 18, 2, 16, X, X, X, X, X, X, X, 3, 0, 3, 8, X, X, X, X, X, X, X, X, X,
1, 18, 2, 0, 3, 18, 2, 16, X, X, X, X, X, 1, 14, 3, 8, X, X, X, X, X, X, X, X, X,
3, 18, 2, 16, 2, 8, 1, 18, X, X, X, X, X, 4, 0, 3, 3, 1, 14, X, X, X, X, X, X, X,
4, 0, 1, 18, 2, 16, 2, 8, 1, 18, X, X, X, 1, 12, 1, 14, 3, 3, 1, 14, X, X, X, X, X,
2, 12, 1, 18, 2, 12, 2, 16, 2, 8, 1, 18, X, 3, 0, 2, 3, 1, 14, X, X, X, X, X, X, X,
1, 18, 2, 0, 2, 12, 2, 16, 2, 8, 1, 18, X, 1, 14, 2, 3, 1, 14, X, X, X, X, X, X, X,
2, 18, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X, 4, 0, 1, 12, 1, 14, X, X, X, X, X, X, X,
4, 0, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X, 1, 12, 1, 14, 1, 12, 1, 14, X, X, X, X, X,
2, 12, 3, 18, 2, 8, 1, 18, X, X, X, X, X, 3, 0, 1, 14, X, X, X, X, X, X, X, X, X,
1, 18, 2, 0, 2, 18, 2, 8, 1, 18, X, X, X, 2, 14, X, X, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 1, 18, 2, 16, 2, 14, 2, 16, X, 3, 0, 3, 3, 2, 8, X, X, X, X, X, X, X,
2, 0, 2, 18, 2, 16, 2, 14, 2, 16, X, X, X, 1, 12, 3, 3, 2, 8, X, X, X, X, X, X, X,
2, 12, 2, 14, 2, 12, 2, 16, 2, 14, 2, 16, X, 2, 0, 2, 3, 2, 8, X, X, X, X, X, X, X,
2, 18, 2, 12, 2, 16, 2, 14, 2, 16, X, X, X, 2, 3, 2, 8, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 2, 3, 1, 18, 2, 14, 2, 16, X, 3, 0, 1, 12, 2, 8, X, X, X, X, X, X, X,
2, 0, 1, 18, 2, 3, 1, 18, 2, 14, 2, 16, X, 2, 12, 2, 8, X, X, X, X, X, X, X, X, X,
2, 12, 2, 14, 2, 18, 2, 14, 2, 16, X, X, X, 2, 0, 2, 8, X, X, X, X, X, X, X, X, X,
4, 18, 2, 14, 2, 16, X, X, X, X, X, X, X, 2, 8, X, X, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 1, 18, 2, 16, 2, 18, X, X, X, 3, 0, 3, 3, X, X, X, X, X, X, X, X, X,
2, 0, 2, 18, 2, 16, 2, 18, X, X, X, X, X, 1, 12, 3, 3, X, X, X, X, X, X, X, X, X,
2, 12, 2, 14, 2, 12, 2, 16, 2, 18, X, X, X, 2, 0, 2, 3, X, X, X, X, X, X, X, X, X,
2, 18, 2, 12, 2, 16, 2, 18, X, X, X, X, X, 2, 3, X, X, X, X, X, X, X, X, X, X, X,
1, 18, 2, 14, 2, 3, 3, 18, X, X, X, X, X, 3, 0, 1, 12, X, X, X, X, X, X, X, X, X,
2, 0, 1, 18, 2, 3, 3, 18, X, X, X, X, X, 2, 12, X, X, X, X, X, X, X, X, X, X, X,
2, 12, 2, 14, 4, 18, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, X, X, X, X,
6, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X
#undef X
});
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,103 @@
//============================================================================
// 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)
//==============================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_predicates_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_predicates_h
#include <vtkm/Types.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
constexpr vtkm::Id BRANCH_SADDLE = static_cast<vtkm::Id>(1); // 1 << 0
constexpr vtkm::Id BRANCH_COVER = static_cast<vtkm::Id>(2); // 1 << 1
constexpr vtkm::Id MAXIMA_CONTOUR = static_cast<vtkm::Id>(4); // 1 << 2
struct IsNonNegative
{
VTKM_EXEC_CONT
IsNonNegative() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id x) const { return x >= 0; }
};
struct IsExtraMinima
{
VTKM_EXEC_CONT
IsExtraMinima() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id x) const { return (x & vtkm::Id(1)) != 0; }
};
struct IsExtraMaxima
{
VTKM_EXEC_CONT
IsExtraMaxima() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id x) const { return (x & vtkm::Id(2)) != 0; }
};
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,149 @@
//============================================================================
// 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.
//
//=======================================================================================
//
// Parallel Peak Pruning v. 2.0
//
// Started June 15, 2017
//
// Copyright Hamish Carr, University of Leeds
//
// BranchDecompositionTreeMaker.h
//
//=======================================================================================
//
// COMMENTS:
//
// This class computes the branch decomposition tree of top-volume branches
//
//=======================================================================================
#ifndef vtk_m_filter_scalar_topology_worklet_TopVolumeBranchData_h
#define vtk_m_filter_scalar_topology_worklet_TopVolumeBranchData_h
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
#ifdef DEBUG_PRINT
#define DEBUG_BRANCH_DECOMPOSITION_TREE_MAKER
#endif
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
/// Data to store all information about top branches by volume
struct TopVolumeBranchData
{ // struct TopVolumeBranchData
// Data from DHCT. Size: nBranches
vtkm::worklet::contourtree_augmented::IdArrayType BranchRootByBranch;
vtkm::worklet::contourtree_augmented::IdArrayType BranchRootGRId;
vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume;
vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume;
vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue;
// True if it is the parent of a branch in the branch decomposition tree
vtkm::cont::ArrayHandle<bool> IsParentBranch;
// Output Datasets. Information of top branches by volume.
// Size: nTopVolBranches
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRoot;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRootGRId;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume;
// the parent branch of top branches (if no parent branch, then NO_SUCH_ELEMENT)
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchParent;
vtkm::cont::UnknownArrayHandle TopVolumeBranchSaddleIsoValue;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchUpperEndGRId;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchLowerEndGRId;
// Other top-volume branch information
// Whether the top volume branch is known by the block.
// size: nTopVolBranches
// value range: [0, 1]
vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchKnownByBlockStencil;
// Branch order (among all branches) by branch root global regular ids.
// size: nTopVolBranches
// value range: {NO_SUCH_ELEMENT} U [0, nBranches - 1]
vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchGROrder;
// Branch information index (among all branches) of the top volume branch.
// Top-volume branches outside the block are excluded.
// size: nTopVolBranchKnownByBlock
// value range: [0, nBranches - 1]
vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchInfoActualIndex;
// Information to extract extra contours
// For each top-volume branch, we extract a contour on the branch near the saddle,
// and an extra contour on the parent branch near the same saddle
// The extra contour is dependent on the saddle of top-volume branches
// For top-volume branches with a maximum as the end,
// the isovalue of the extra contour will be higher than the saddle end of the top-volume branch
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchUpperEnd;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchLowerEnd;
// We copy the branch order of the top-volume branch
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchOrder;
vtkm::cont::UnknownArrayHandle ExtraMaximaBranchIsoValue;
// For top-volume branches with a minimum as the end,
// the isovalue of the extra contour will be lower than the saddle end of the top-volume branch
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchUpperEnd;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchLowerEnd;
// We copy the branch order of the top-volume branch
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchOrder;
vtkm::cont::UnknownArrayHandle ExtraMinimaBranchIsoValue;
}; // class TopVolumeBranchData
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm
#endif