diff --git a/examples/contour_tree_distributed/ContourTreeApp.cxx b/examples/contour_tree_distributed/ContourTreeApp.cxx index b3ff96568..d0917bbef 100644 --- a/examples/contour_tree_distributed/ContourTreeApp.cxx +++ b/examples/contour_tree_distributed/ContourTreeApp.cxx @@ -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(rank)) + std::string("_Block_") + std::to_string(static_cast(ds_no)) + std::string(".txt"); std::ofstream topVolumeBranchStream(topVolumeBranchFileName.c_str()); - auto topVolBranchGRId = ds.GetField("TopVolumeBranchGlobalRegularIds") - .GetData() - .AsArrayHandle>() - .ReadPortal(); + auto topVolBranchUpperEnd = ds.GetField("TopVolumeBranchUpperEnd") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto topVolBranchLowerEnd = ds.GetField("TopVolumeBranchLowerEnd") + .GetData() + .AsArrayHandle>() + .ReadPortal(); auto topVolBranchVolume = ds.GetField("TopVolumeBranchVolume") .GetData() .AsArrayHandle>() @@ -761,10 +772,11 @@ int main(int argc, char* argv[]) .AsArrayHandle>() .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(rank)) + std::string("_Block_") + + std::to_string(static_cast(ds_no)) + std::string(".txt"); + std::ofstream bdVolStream(branchDecompositionVolumeFileName.c_str()); + + auto upperEndGRId = bd_ds.GetField("UpperEndGlobalRegularIds") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto lowerEndGRId = bd_ds.GetField("LowerEndGlobalRegularIds") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto branchSaddleEpsilon = ds.GetField("BranchSaddleEpsilon") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto branchVolume = ds.GetField("BranchVolume") + .GetData() + .AsArrayHandle>() + .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(rank)) + std::string("_Block_") + + std::to_string(static_cast(ds_no)) + std::string(".txt"); + std::ofstream isosurfaceStream(isosurfaceFileName.c_str()); + + auto isosurfaceEdgesFrom = ds.GetField("IsosurfaceEdgesFrom") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesTo = ds.GetField("IsosurfaceEdgesTo") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesLabels = ds.GetField("IsosurfaceEdgesLabels") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesOrders = ds.GetField("IsosurfaceEdgesOrders") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesOffset = ds.GetField("IsosurfaceEdgesOffset") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceIsoValue = ds.GetField("IsosurfaceIsoValue") + .GetData() + .AsArrayHandle>() + .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" diff --git a/vtkm/filter/scalar_topology/CMakeLists.txt b/vtkm/filter/scalar_topology/CMakeLists.txt index 5603ebaa1..6a7c44298 100644 --- a/vtkm/filter/scalar_topology/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/CMakeLists.txt @@ -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 diff --git a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx index 9992fd64a..57203719c 100644 --- a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx +++ b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.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; diff --git a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx index 0f522424b..59ae5b965 100644 --- a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx +++ b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx @@ -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 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()); diff --git a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h index be1b677a0..1cefb13f0 100644 --- a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h +++ b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h @@ -63,11 +63,18 @@ public: const vtkm::cont::ArrayHandle&, const vtkm::cont::ArrayHandle&, const vtkm::cont::ArrayHandle&); + 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 diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx index c7d0b8f31..cd84d7ced 100644 --- a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx +++ b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx @@ -11,10 +11,11 @@ #include #include #include +#include #include #include #include - +#include // vtkm includes #include @@ -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( @@ -76,6 +87,12 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut globalNumberOfBlocks *= static_cast(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::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - b->BranchVolume, topVolumeBranch, b->TopVolumeBranchVolume); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - b->BranchSaddleEpsilon, topVolumeBranch, b->TopVolumeBranchSaddleEpsilon); - - auto resolveArray = [&](const auto& inArray) { - using InArrayHandleType = std::decay_t; - InArrayHandleType topVolBranchSaddleIsoValue; - vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( - inArray, topVolumeBranch, topVolBranchSaddleIsoValue); - b->TopVolumeBranchSaddleIsoValue = topVolBranchSaddleIsoValue; - }; - - b->BranchSaddleIsoValue - .CastAndCallForTypes(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 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(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(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 }; } diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h index cdc8c2f3f..86d6d640d 100644 --- a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h +++ b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h @@ -44,7 +44,6 @@ #include #include -#include 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 BranchVolume; + vtkm::cont::ArrayHandle BranchSaddleEpsilon; + vtkm::cont::ArrayHandle 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 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 diff --git a/vtkm/filter/scalar_topology/internal/CMakeLists.txt b/vtkm/filter/scalar_topology/internal/CMakeLists.txt index 3280d72b8..f3e24e83d 100644 --- a/vtkm/filter/scalar_topology/internal/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/internal/CMakeLists.txt @@ -13,6 +13,7 @@ set(headers SelectTopVolumeContoursBlock.h ComputeBlockIndices.h ComputeDistributedBranchDecompositionFunctor.h + ParentBranchExtremaFunctor.h SelectTopVolumeContoursFunctor.h ExchangeBranchEndsFunctor.h ) diff --git a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx index 3223beb39..45a277997 100644 --- a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx +++ b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx @@ -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 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 diff --git a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h index c9a51ea1d..6061b1b1a 100644 --- a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h +++ b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h @@ -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 diff --git a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx index d0b95f109..2a5b409ce 100644 --- a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx +++ b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx @@ -54,6 +54,7 @@ #include #include +#include #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); diff --git a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h index 9eace991a..f5165dc50 100644 --- a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h +++ b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h @@ -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 diff --git a/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx b/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx new file mode 100644 index 000000000..fa0443674 --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx @@ -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 +#include +#include +#include + +#include + +#include + +#ifdef DEBUG_PRINT +#define DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE +#include +#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; + 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(target, extraMaxBranchIsoValuePortal.Get(branch)); + }; + if (b->tData.ExtraMaximaBranchIsoValue.GetNumberOfValues()) + b->tData.ExtraMaximaBranchIsoValue + .CastAndCallForTypes( + 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; + 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(target, extraMinBranchIsoValuePortal.Get(branch)); + }; + if (b->tData.ExtraMinimaBranchIsoValue.GetNumberOfValues()) + b->tData.ExtraMinimaBranchIsoValue + .CastAndCallForTypes( + 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; + 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(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(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( + "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( + "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 + 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( + "selfMaxBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + }; + if (nSelfMaxBranch > 0 && nIncomingMaxBranch > 0) + b->tData.ExtraMaximaBranchIsoValue + .CastAndCallForTypes( + 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; + 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(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(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( + "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( + "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 + 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( + "selfMinBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + }; + if (nSelfMinBranch > 0 && nIncomingMinBranch > 0) + b->tData.ExtraMinimaBranchIsoValue + .CastAndCallForTypes( + 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 diff --git a/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.h b/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.h new file mode 100644 index 000000000..eef069981 --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.h @@ -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 + +// clang-format off +VTKM_THIRDPARTY_PRE_INCLUDE +#include +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 diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx index 9a3e9d2ca..dc8a1f3ad 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx @@ -52,8 +52,16 @@ #include #include +#include +#include +#include +#include #include +#include #include +#include +#include +#include #include #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 isLowerLeaf; vtkm::cont::ArrayHandle isUpperLeaf; - auto upperEndIntrinsicVolume = hierarchicalTreeDataSet.GetField("UpperEndIntrinsicVolume") + auto upperEndIntrinsicVolume = bdDataSet.GetField("UpperEndIntrinsicVolume") .GetData() .AsArrayHandle>(); - auto upperEndDependentVolume = hierarchicalTreeDataSet.GetField("UpperEndDependentVolume") + auto upperEndDependentVolume = bdDataSet.GetField("UpperEndDependentVolume") .GetData() .AsArrayHandle>(); - auto lowerEndIntrinsicVolume = hierarchicalTreeDataSet.GetField("LowerEndIntrinsicVolume") + auto lowerEndIntrinsicVolume = bdDataSet.GetField("LowerEndIntrinsicVolume") .GetData() .AsArrayHandle>(); - auto lowerEndDependentVolume = hierarchicalTreeDataSet.GetField("LowerEndDependentVolume") + auto lowerEndDependentVolume = bdDataSet.GetField("LowerEndDependentVolume") .GetData() .AsArrayHandle>(); - auto lowerEndSuperarcId = hierarchicalTreeDataSet.GetField("LowerEndSuperarcId") + + auto lowerEndSuperarcId = bdDataSet.GetField("LowerEndSuperarcId") .GetData() .AsArrayHandle>(); - auto upperEndSuperarcId = hierarchicalTreeDataSet.GetField("UpperEndSuperarcId") + auto upperEndSuperarcId = bdDataSet.GetField("UpperEndSuperarcId") .GetData() .AsArrayHandle>(); - auto branchRoot = hierarchicalTreeDataSet.GetField("BranchRoot") + auto branchRoot = bdDataSet.GetField("BranchRootByBranch") .GetData() .AsArrayHandle>(); @@ -138,8 +146,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 +155,12 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume( vtkm::cont::ArrayHandle 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>(); @@ -162,9 +169,9 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume( isUpperLeaf, inArray, lowerEndValue, - this->BranchSaddleEpsilon, + tData.BranchSaddleEpsilon, branchSaddleIsoValue); - this->BranchSaddleIsoValue = branchSaddleIsoValue; + tData.BranchSaddleIsoValue = branchSaddleIsoValue; }; upperEndValue.CastAndCallForTypes( @@ -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>(); + + const vtkm::Id nBranches = branchRootByBranch.GetNumberOfValues(); + + auto branchRootGRId = bdDataset.GetField("BranchRootGRId") + .GetData() + .AsArrayHandle>(); + + auto upperEndGRId = bdDataset.GetField("UpperEndGlobalRegularIds") + .GetData() + .AsArrayHandle>(); + + auto lowerEndGRId = bdDataset.GetField("LowerEndGlobalRegularIds") + .GetData() + .AsArrayHandle>(); + + 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( + branchRootGRId, topVolumeBranch, tData.TopVolumeBranchRootGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperEndGRId, topVolumeBranch, tData.TopVolumeBranchUpperEndGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerEndGRId, topVolumeBranch, tData.TopVolumeBranchLowerEndGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + tData.BranchVolume, topVolumeBranch, tData.TopVolumeBranchVolume); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + tData.BranchSaddleEpsilon, topVolumeBranch, tData.TopVolumeBranchSaddleEpsilon); + + auto resolveArray = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + InArrayHandleType topVolBranchSaddleIsoValue; + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + inArray, topVolumeBranch, topVolBranchSaddleIsoValue); + tData.TopVolumeBranchSaddleIsoValue = topVolBranchSaddleIsoValue; + }; + + tData.BranchSaddleIsoValue + .CastAndCallForTypes(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(), + 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(); + auto lowerEndLocalIds = + bdDataSet.GetField("LowerEndLocalIds").GetData().AsArrayHandle(); + + // global regular ids + auto globalRegularIds = + bdDataSet.GetField("RegularNodeGlobalIds").GetData().AsArrayHandle(); + + vtkm::Id3 globalPointDimensions; + vtkm::Id3 pointDimensions, globalPointIndexStart; + + bdDataSet.GetCellSet().CastAndCallForTypes( + 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 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; + 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(); + + 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 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( + localIdsWithinBlock, sortedGlobalIds, sortedLocalNodeInfoIdsWithinBlock); + + // sorted data values + InArrayHandleType sortedDataValuesWithinBlock; + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + 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(); + auto supernodes = bdDataSet.GetField("Supernodes").GetData().AsArrayHandle(); + auto superarcs = bdDataSet.GetField("Superarcs").GetData().AsArrayHandle(); + auto superchildren = bdDataSet.GetField("Superchildren").GetData().AsArrayHandle(); + auto whichRound = bdDataSet.GetField("WhichRound").GetData().AsArrayHandle(); + auto whichIteration = + bdDataSet.GetField("WhichIteration").GetData().AsArrayHandle(); + auto hyperparents = bdDataSet.GetField("Hyperparents").GetData().AsArrayHandle(); + auto hypernodes = bdDataSet.GetField("Hypernodes").GetData().AsArrayHandle(); + auto hyperarcs = bdDataSet.GetField("Hyperarcs").GetData().AsArrayHandle(); + + // filtered + sorted superparents of nodes + IdArrayType superparentsWithinBlock; + vtkm::cont::Algorithm::CopyIf( + superparents, globalIdsWithinBlockStencil, superparentsWithinBlock); + IdArrayType sortedSuperparentsWithinBlock; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + 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( + 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 topVolIsParent; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperEndLocalIds, tData.TopVolBranchInfoActualIndex, topVolLocalBranchUpperEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerEndLocalIds, tData.TopVolBranchInfoActualIndex, topVolLocalBranchLowerEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex>( + 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(); + + invoke(branchIsoSuperarcWorklet, + tData.ExtraMaximaBranchUpperEnd, + tData.ExtraMaximaBranchLowerEnd, + extraMaximaBranchIsoValue, + vtkm::cont::ArrayHandleConstant(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( + "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(); + + invoke(branchIsoSuperarcWorklet, + tData.ExtraMinimaBranchUpperEnd, + tData.ExtraMinimaBranchLowerEnd, + extraMinimaBranchIsoValue, + vtkm::cont::ArrayHandleConstant(-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( + "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( + 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( + numBoundTable, caseCells, numBoundariesInCell); + + vtkm::cont::Algorithm::Transform(numBoundariesInCell, + globalPointDimensions[2] <= 1 + ? vtkm::cont::ArrayHandleConstant(1, nCells) + : vtkm::cont::ArrayHandleConstant(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(numEdgesInCell, vtkm::Id(0)); + nContourCandidateMeshes += globalPointDimensions[2] <= 1 ? nEdges : nEdges / 3; + IdArrayType edgesOffset; + vtkm::cont::Algorithm::ScanExclusive(numEdgesInCell, edgesOffset); + + vtkm::cont::ArrayHandle isosurfaceEdgesFrom; + vtkm::cont::ArrayHandle 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 validEdgesFrom; + vtkm::cont::ArrayHandle 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( + "edgeOffset", this->IsosurfaceEdgesOffset, -1, edgeInfoStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "edgeOrders", this->IsosurfaceEdgesOrders, -1, edgeInfoStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "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( + "EdgesFrom", validEdgesFrom, -1, contourStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "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(resolveArray); } } // namespace internal diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h index 7d08433ed..839aa780e 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h @@ -55,6 +55,8 @@ #include #include +#include +#include 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 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 IsosurfaceEdgesFrom; + vtkm::cont::ArrayHandle 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(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 diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx index 485c14d2e..c22939c8f 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx @@ -49,7 +49,7 @@ // Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and // Oliver Ruebel (LBNL) //============================================================================== - +#include #include #include #include @@ -57,6 +57,8 @@ #include +#include + #ifdef DEBUG_PRINT #define DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH #include @@ -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; @@ -120,11 +128,11 @@ void SelectTopVolumeContoursFunctor::operator()( for (vtkm::Id branch = 0; branch < nBranches; ++branch) rp.enqueue(target, topVolBranchSaddleIsoValuePortal.Get(branch)); }; - b->TopVolumeBranchSaddleIsoValue + b->tData.TopVolumeBranchSaddleIsoValue .CastAndCallForTypes(resolveArray); - // rp.enqueue(target, b->TopVolumeBranchRootGRId); - // rp.enqueue(target, b->TopVolumeBranchVolume); + // rp.enqueue(target, b->tData.TopVolumeBranchRootGRId); + // rp.enqueue(target, b->tData.TopVolumeBranchVolume); } } } @@ -199,6 +207,34 @@ void SelectTopVolumeContoursFunctor::operator()( // Replace with dequeuing ArrayHandles once bug is fixed. // rp.dequeue(ingid, incomingTopVolBranchSaddleEpsilon); + 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); + auto resolveArray = [&](auto& inArray) { using InArrayHandleType = std::decay_t; using ValueType = typename InArrayHandleType::ValueType; @@ -213,11 +249,25 @@ void SelectTopVolumeContoursFunctor::operator()( incomingTopVolBranchSaddleIsoValuePortal.Set(branch, incomingSaddleValue); } + 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()); + // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. // Replace with dequeuing ArrayHandles once bug is fixed. // rp.dequeue(ingid, incomingTopVolBranchSaddleIsoValue); - vtkm::Id nSelf = b->TopVolumeBranchRootGRId.GetNumberOfValues(); + vtkm::Id nSelf = b->tData.TopVolumeBranchRootGRId.GetNumberOfValues(); #ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH VTKM_LOG_S(vtkm::cont::LogLevel::Info, @@ -231,14 +281,22 @@ void SelectTopVolumeContoursFunctor::operator()( "incomingTopBranchVol", incomingTopVolBranchVolume, -1, rs); vtkm::worklet::contourtree_augmented::PrintValues( "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->TopVolumeBranchRootGRId, -1, rs); + "selfTopBranchId", b->tData.TopVolumeBranchRootGRId, -1, rs); vtkm::worklet::contourtree_augmented::PrintIndices( - "selfTopBranchVol", b->TopVolumeBranchVolume, -1, rs); + "selfTopBranchVol", b->tData.TopVolumeBranchVolume, -1, rs); vtkm::worklet::contourtree_augmented::PrintValues( "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 @@ -246,10 +304,14 @@ void SelectTopVolumeContoursFunctor::operator()( IdArrayType mergedTopVolBranchGRId; IdArrayType mergedTopVolBranchVolume; IdArrayType mergedTopVolBranchSaddleEpsilon; + IdArrayType mergedTopVolBranchUpperEnd; + IdArrayType mergedTopVolBranchLowerEnd; InArrayHandleType mergedTopVolBranchSaddleIsoValue; mergedTopVolBranchGRId.Allocate(nIncoming + nSelf); mergedTopVolBranchVolume.Allocate(nIncoming + nSelf); mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf); + mergedTopVolBranchUpperEnd.Allocate(nIncoming + nSelf); + mergedTopVolBranchLowerEnd.Allocate(nIncoming + nSelf); mergedTopVolBranchSaddleIsoValue.Allocate(nIncoming + nSelf); vtkm::cont::Algorithm::CopySubRange( @@ -258,14 +320,25 @@ void SelectTopVolumeContoursFunctor::operator()( 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( incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0); vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchRootGRId, 0, nSelf, mergedTopVolBranchGRId, nIncoming); + b->tData.TopVolumeBranchRootGRId, 0, nSelf, mergedTopVolBranchGRId, nIncoming); vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming); + b->tData.TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming); + vtkm::cont::Algorithm::CopySubRange(b->tData.TopVolumeBranchSaddleEpsilon, + 0, + nSelf, + mergedTopVolBranchSaddleEpsilon, + nIncoming); vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchSaddleEpsilon, 0, nSelf, mergedTopVolBranchSaddleEpsilon, nIncoming); + b->tData.TopVolumeBranchUpperEndGRId, 0, nSelf, mergedTopVolBranchUpperEnd, nIncoming); + vtkm::cont::Algorithm::CopySubRange( + b->tData.TopVolumeBranchLowerEndGRId, 0, nSelf, mergedTopVolBranchLowerEnd, nIncoming); vtkm::cont::Algorithm::CopySubRange( inArray, 0, nSelf, mergedTopVolBranchSaddleIsoValue, nIncoming); @@ -289,6 +362,12 @@ void SelectTopVolumeContoursFunctor::operator()( IdArrayType permutedTopVolBranchSaddleEpsilon; vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon); + IdArrayType permutedTopVolBranchUpperEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchUpperEnd, sortedBranchId, permutedTopVolBranchUpperEnd); + IdArrayType permutedTopVolBranchLowerEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchLowerEnd, sortedBranchId, permutedTopVolBranchLowerEnd); InArrayHandleType permutedTopVolBranchSaddleIsoValue; vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( mergedTopVolBranchSaddleIsoValue, sortedBranchId, permutedTopVolBranchSaddleIsoValue); @@ -301,6 +380,10 @@ void SelectTopVolumeContoursFunctor::operator()( "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( "permutedTopSaddleVal", permutedTopVolBranchSaddleIsoValue, -1, rs); } @@ -318,6 +401,8 @@ void SelectTopVolumeContoursFunctor::operator()( IdArrayType mergedUniqueBranchGRId; IdArrayType mergedUniqueBranchVolume; IdArrayType mergedUniqueBranchSaddleEpsilon; + IdArrayType mergedUniqueBranchUpperEnd; + IdArrayType mergedUniqueBranchLowerEnd; InArrayHandleType mergedUniqueBranchSaddleIsoValue; vtkm::cont::Algorithm::CopyIf( @@ -326,6 +411,10 @@ void SelectTopVolumeContoursFunctor::operator()( 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::cont::Algorithm::CopyIf( permutedTopVolBranchSaddleIsoValue, oneIfUniqueBranch, mergedUniqueBranchSaddleIsoValue); @@ -339,6 +428,10 @@ void SelectTopVolumeContoursFunctor::operator()( "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( "mergedUniqueSaddleVal", mergedUniqueBranchSaddleIsoValue, -1, rs); } @@ -349,13 +442,21 @@ void SelectTopVolumeContoursFunctor::operator()( if (nMergedUnique > this->nSavedBranches) { vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchGRId, 0, this->nSavedBranches, b->TopVolumeBranchRootGRId); + mergedUniqueBranchGRId, 0, this->nSavedBranches, b->tData.TopVolumeBranchRootGRId); vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchVolume, 0, this->nSavedBranches, b->TopVolumeBranchVolume); + mergedUniqueBranchVolume, 0, this->nSavedBranches, b->tData.TopVolumeBranchVolume); vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchSaddleEpsilon, 0, this->nSavedBranches, - b->TopVolumeBranchSaddleEpsilon); + 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); // InArrayHandleType subRangeUniqueBranchSaddleIsoValue; inArray.Allocate(this->nSavedBranches); vtkm::cont::Algorithm::CopySubRange( @@ -364,15 +465,19 @@ void SelectTopVolumeContoursFunctor::operator()( } else { - vtkm::cont::Algorithm::Copy(mergedUniqueBranchGRId, b->TopVolumeBranchRootGRId); - vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->TopVolumeBranchVolume); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchGRId, b->tData.TopVolumeBranchRootGRId); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->tData.TopVolumeBranchVolume); vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon, - b->TopVolumeBranchSaddleEpsilon); + b->tData.TopVolumeBranchSaddleEpsilon); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchUpperEnd, + b->tData.TopVolumeBranchUpperEndGRId); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchLowerEnd, + b->tData.TopVolumeBranchLowerEndGRId); inArray.Allocate(nMergedUnique); vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleIsoValue, inArray); } }; - b->TopVolumeBranchSaddleIsoValue + b->tData.TopVolumeBranchSaddleIsoValue .CastAndCallForTypes(resolveArray); } } diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h index 023047def..4bcb5682a 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h @@ -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 diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h index 35a8b8609..92733a649 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h @@ -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::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( diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h index 498f7eb5f..d16e8b108 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h @@ -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 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 diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h index 19caa736e..847b90a36 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h @@ -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; diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h index a9ad55f7f..7f51b2632 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,11 @@ template 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* BaseTree; /// the tree that it is building @@ -198,7 +204,10 @@ public: void Initialize( vtkm::Id blockId, vtkm::worklet::contourtree_distributed::HierarchicalContourTree* inBaseTree, - vtkm::worklet::contourtree_distributed::HierarchicalContourTree* inAugmentedTree); + vtkm::worklet::contourtree_distributed::HierarchicalContourTree* 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 void HierarchicalAugmenter::Initialize( vtkm::Id blockId, vtkm::worklet::contourtree_distributed::HierarchicalContourTree* baseTree, - vtkm::worklet::contourtree_distributed::HierarchicalContourTree* augmentedTree) + vtkm::worklet::contourtree_distributed::HierarchicalContourTree* 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::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::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::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::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; diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h index bd2dcdcba..6db7713ec 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h @@ -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(); } } diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h index acd5eca2b..19842aa84 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h @@ -81,6 +81,7 @@ #include #include #include +#include namespace vtkm { @@ -1024,9 +1025,18 @@ void HierarchicalContourTree::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::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 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 diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h index da7126a9b..ff55edd52 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h @@ -200,7 +200,6 @@ private: }; // class HierarchicalHyperSweeper - template HierarchicalHyperSweeper::HierarchicalHyperSweeper( vtkm::Id blockId, @@ -360,6 +359,7 @@ void HierarchicalHyperSweeper::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:: 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:: 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 diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt index 56a3b61c1..408d3fd10 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt @@ -22,6 +22,7 @@ set(headers SetSuperparentSetDecorator.h AttachmentAndSupernodeComparator.h ResizeArraysBuildNewSupernodeIdsWorklet.h + FillEmptyIterationWorklet.h CreateSuperarcsWorklet.h HierarchicalAugmenterInOutData.h ) diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h index 32bcc6037..40f6ec36d 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.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 diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h new file mode 100644 index 000000000..8b76070cb --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h @@ -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 +#include + +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 + 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 diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h index f4c745478..04186c708 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h @@ -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 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 diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h index a9de61546..e5292b12e 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h @@ -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); diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchDecompositionTreeMaker.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchDecompositionTreeMaker.h new file mode 100644 index 000000000..9373ca726 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchDecompositionTreeMaker.h @@ -0,0 +1,524 @@ +//============================================================================ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + + +/// +/// Pipeline to compute the hierarchy of top branches by volume +/// +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>(); + auto lowerLocalEndIds = bdDataSet.GetField("LowerEndLocalIds") + .GetData() + .AsArrayHandle>(); + auto globalRegularIds = bdDataSet.GetField("RegularNodeGlobalIds") + .GetData() + .AsArrayHandle>(); + IdArrayType upperEndGRIds = + bdDataSet.GetField("UpperEndGlobalRegularIds").GetData().AsArrayHandle(); + IdArrayType lowerEndGRIds = + bdDataSet.GetField("LowerEndGlobalRegularIds").GetData().AsArrayHandle(); + + // 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( + 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( + upperLocalEndIds, tData.TopVolBranchInfoActualIndex, topVolBranchUpperLocalEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerLocalEndIds, tData.TopVolBranchInfoActualIndex, topVolBranchLowerLocalEnd); + + IdArrayType topVolLowerLocalEndGRId; + IdArrayType topVolUpperLocalEndGRId; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + globalRegularIds, topVolBranchLowerLocalEnd, topVolLowerLocalEndGRId); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + 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>(); + auto branchRoots = + bdDataSet.GetField("BranchRoots").GetData().AsArrayHandle>(); + 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( + superarcs, superarcsByTarget, permutedSuperarcs); + + IdArrayType permutedBranchRoots; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchRoots, superarcsByTarget, permutedBranchRoots); + + // the branch root of the superarc of the branch saddle supernode + IdArrayType topVolChildBranchSaddleBranchRoot; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchRoots, topVolChildBranchSaddle, topVolChildBranchSaddleBranchRoot); + + // the GR Ids of the superarc of the branch saddle supernode + IdArrayType topVolChildBranchSaddleGRIds; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + 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(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; + using ValueType = typename InArrayHandleType::ValueType; + + vtkm::worklet::scalar_topology::select_top_volume_contours::BranchParentComparator + parentComparator(tData.TopVolumeBranchParent, inArray, tData.TopVolumeBranchRootGRId); + + // sort index for all top volume branches + vtkm::cont::Algorithm::Sort(topVolSortForOuterSaddleIdx, parentComparator); + }; + tData.TopVolumeBranchSaddleIsoValue + .CastAndCallForTypes( + resolveBranchParentSorter); + + IdArrayType parentPermutation; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + 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( + 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( + upperLocalEndIds, extraMaximaParentBranch, tData.ExtraMaximaBranchUpperEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerLocalEndIds, extraMaximaParentBranch, tData.ExtraMaximaBranchLowerEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + tData.BranchRootGRId, extraMaximaParentBranch, extraMaximaParentBranchRootGRId); + + //InArrayHandleType extraMaximaBranchIsoValue; + //vtkm::cont::Algorithm::CopyIf( + // tData.TopVolumeBranchSaddleIsoValue.AsArrayHandle(), + // IsOuterSaddle, + // extraMaximaBranchIsoValue, + // vtkm::worklet::scalar_topology::select_top_volume_contours::IsExtraMaxima()); + //tData.ExtraMaximaBranchIsoValue = extraMaximaBranchIsoValue; + + // 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 discard; + discard.Allocate(extraMaximaParentBranchRootGRId.GetNumberOfValues()); + invoke(getParentBranchOrder, + extraMaximaParentBranchRootGRId, + allBranchGRIdByVolume, + discard, + permutedExtraMaximaBranchOrder); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchGRIdByVolumeIdx, permutedExtraMaximaBranchOrder, tData.ExtraMaximaBranchOrder); + } + + if (extraMinimaParentBranch.GetNumberOfValues()) + { + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperLocalEndIds, extraMinimaParentBranch, tData.ExtraMinimaBranchUpperEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerLocalEndIds, extraMinimaParentBranch, tData.ExtraMinimaBranchLowerEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + tData.BranchRootGRId, extraMinimaParentBranch, extraMinimaParentBranchRootGRId); + + //InArrayHandleType extraMinimaBranchIsoValue; + //vtkm::cont::Algorithm::CopyIf( + // tData.TopVolumeBranchSaddleIsoValue.AsArrayHandle(), + // IsOuterSaddle, + // extraMinimaBranchIsoValue, + // vtkm::worklet::scalar_topology::select_top_volume_contours::IsExtraMinima()); + //tData.ExtraMinimaBranchIsoValue = extraMinimaBranchIsoValue; + + vtkm::worklet::scalar_topology::select_top_volume_contours::IdxIfWithinBlockWorklet + getParentBranchOrder; + IdArrayType permutedExtraMinimaBranchOrder; + permutedExtraMinimaBranchOrder.Allocate(extraMinimaParentBranchRootGRId.GetNumberOfValues()); + vtkm::cont::ArrayHandleDiscard discard; + discard.Allocate(extraMinimaParentBranchRootGRId.GetNumberOfValues()); + invoke(getParentBranchOrder, + extraMinimaParentBranchRootGRId, + allBranchGRIdByVolume, + discard, + permutedExtraMinimaBranchOrder); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchGRIdByVolumeIdx, permutedExtraMinimaBranchOrder, tData.ExtraMinimaBranchOrder); + } + + auto resolveExtraContourSaddleValue = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + using ValueType = typename InArrayHandleType::ValueType; + + 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( + resolveExtraContourSaddleValue); +} + + +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchParentComparator.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchParentComparator.h new file mode 100644 index 000000000..4a008fd7e --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchParentComparator.h @@ -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 + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_contours +{ + +using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + +// Implementation of BranchParentComparator +template +class BranchParentComparatorImpl +{ +public: + using ValueArrayType = typename vtkm::cont::ArrayHandle; + 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 + +/// +/// Comparator of branch parent. Lower parent comes first +/// +template +class BranchParentComparator : public vtkm::cont::ExecutionObjectBase +{ + using ValueArrayType = typename vtkm::cont::ArrayHandle; + +public: + // constructor + VTKM_CONT + BranchParentComparator(const IdArrayType& branchParent, + const ValueArrayType& saddleIsoValue, + const IdArrayType& branchRootGRId) + : BranchParent(branchParent) + , SaddleIsoValue(saddleIsoValue) + , BranchRootGRId(branchRootGRId) + { + } + + VTKM_CONT BranchParentComparatorImpl PrepareForExecution( + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const + { + return BranchParentComparatorImpl( + 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 + +/// +/// Comparator of superarc target. The NULL superarc always comes first. +/// +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 diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt index 234966323..d76dfb1c9 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt @@ -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 ) #----------------------------------------------------------------------------- diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.h new file mode 100644 index 000000000..6749d4c06 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.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 + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_contours +{ + +constexpr vtkm::IdComponent MAX_CONNECTIVITY_3D = static_cast(14); +using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + +/// +/// 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 +/// +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 + + +/// +/// worklet to compute the parent branch of branches +/// +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 + 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 + 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 + 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 + + +/// +/// 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 +/// +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 + 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 + + +/// +/// 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 +/// +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 + + +/// +/// worklet to update the value of outer saddles for parent branches +/// +template +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 + 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 diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h index 4d808f8ed..1d14aae06 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h @@ -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; diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h new file mode 100644 index 000000000..d92352feb --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h @@ -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 +#include + + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_contours +{ + +constexpr vtkm::IdComponent MAX_MARCHING_CUBE_TRIANGLES = static_cast(5); +constexpr vtkm::IdComponent MAX_LINEAR_INTERPOLATION_TRIANGLES = static_cast(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 + 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 + 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 +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::ReadPortalType; + using ValueArrayPortalType = typename vtkm::cont::ArrayHandle::ReadPortalType; + + /// + /// Constructor + /// + /// dimension of points in the grid + /// the direction for tiebreaking when comparing values + /// isovalue for the isosurface to extract + /// + 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; + } + + /// + /// Computes the polarity case of cells + /// + /// the local index of the point in the local grid + /// all data values on the local grid points + /// integer indicating the polarity case of the cell originating at the input point + 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; + + + /// + /// Constructor + /// + /// total number of points within the local grid + /// We only need a number that is larger than any grid index + VTKM_EXEC_CONT + GetSuperarcByIsoValueWorklet(const vtkm::Id totNumPoints) + : TotalNumPoints(totNumPoints) + { + } + + /// + /// Implementation of GetSuperarcByIsoValueWorklet. + /// Check vtkm::worklet::contourtree_distributed::FindSuperArcForUnknownNode + /// for the execution object description. + /// + /// data value type + /// execution object type of findSuperarc + /// local id of the upper end vertex of the branch + /// local id of the lower end vertex of the branch + /// isovalue + /// the direction for tiebreaking when comparing values + /// execution object + /// + template + 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 +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::ReadPortalType; + using IdArrayWritePortalType = typename vtkm::cont::ArrayHandle::WritePortalType; + using ValueArrayPortalType = typename vtkm::cont::ArrayHandle::ReadPortalType; + using EdgePointArrayPortalType = + typename vtkm::cont::ArrayHandle::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(vertOffset.Get(cellIndex * 2) + localPt[0]), + static_cast(vertOffset.Get(cellIndex * 2 + 1) + localPt[1]), + static_cast(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(vertOffset.Get(cellIndex * 3) + localPt[0]), + static_cast(vertOffset.Get(cellIndex * 3 + 1) + localPt[1]), + static_cast(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 + 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 diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/MarchingCubesDataTables.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/MarchingCubesDataTables.h new file mode 100644 index 000000000..08f5e906e --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/MarchingCubesDataTables.h @@ -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 + +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 vertexOffset2d = + vtkm::cont::make_ArrayHandle({ 0, 0, 1, 0, 1, 1, 0, 1 }); + +const vtkm::cont::ArrayHandle edgeTable2d = + vtkm::cont::make_ArrayHandle({ 0, 1, 1, 2, 3, 2, 0, 3, 0, 2 }); + +const vtkm::cont::ArrayHandle numLinesTable2d = + vtkm::cont::make_ArrayHandle({ 0, 2, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 2, 0 }); + +// size: nCase2d * nLineTableElemSize2d +const vtkm::cont::ArrayHandle lineTable2d = vtkm::cont::make_ArrayHandle({ +#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 vertexOffset3d = vtkm::cont::make_ArrayHandle( + { 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 edgeTableMC3d = vtkm::cont::make_ArrayHandle( + { 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 edgeTableLT3d = vtkm::cont::make_ArrayHandle( + { 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 numTrianglesTableMC3d = + vtkm::cont::make_ArrayHandle({ + 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 numTrianglesTableLT3d = + vtkm::cont::make_ArrayHandle( + { 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 triTableMC3d = vtkm::cont::make_ArrayHandle({ +#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 triTableLT3d = vtkm::cont::make_ArrayHandle({ +#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 labelEdgeTableMC3d = + vtkm::cont::make_ArrayHandle({ +#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 labelEdgeTableLT3d = + vtkm::cont::make_ArrayHandle({ +#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 diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/Predicates.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/Predicates.h new file mode 100644 index 000000000..98c50572b --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/Predicates.h @@ -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 + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_contours +{ + +constexpr vtkm::Id BRANCH_SADDLE = static_cast(1); // 1 << 0 +constexpr vtkm::Id BRANCH_COVER = static_cast(2); // 1 << 1 +constexpr vtkm::Id MAXIMA_CONTOUR = static_cast(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 diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/TopVolumeBranchData.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/TopVolumeBranchData.h new file mode 100644 index 000000000..a9b00ad6e --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/TopVolumeBranchData.h @@ -0,0 +1,131 @@ +//============================================================================ +// 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 + +#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 + 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 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; +}; // class TopVolumeBranchData + + + +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm + + +#endif