Move computing branch decomposition to a separate filter

This commit is contained in:
Gunther H. Weber 2022-05-02 18:15:40 -07:00
parent 4fe495be8d
commit f871f51cb8
29 changed files with 1275 additions and 742 deletions

@ -72,11 +72,12 @@
#include <vtkm/io/BOVDataSetReader.h>
#include <vtkm/filter/ContourTreeUniformDistributed.h>
#include <vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h>
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
#include <vtkm/worklet/contourtree_augmented/ProcessContourTree.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalVolumetricBranchDecomposer.h>
#include <vtkm/worklet/contourtree_distributed/TreeCompiler.h>
// clang-format off
@ -862,19 +863,17 @@ int main(int argc, char* argv[])
prevTime = currTime;
// Convert the mesh of values into contour tree, pairs of vertex ids
vtkm::filter::ContourTreeUniformDistributed filter(
blocksPerDim,
globalSize,
localBlockIndices,
localBlockOrigins,
localBlockSizes,
useBoundaryExtremaOnly,
useMarchingCubes,
augmentHierarchicalTree,
computeHierarchicalVolumetricBranchDecomposition,
saveDotFiles,
timingsLogLevel,
treeLogLevel);
vtkm::filter::ContourTreeUniformDistributed filter(blocksPerDim,
globalSize,
localBlockIndices,
localBlockOrigins,
localBlockSizes,
useBoundaryExtremaOnly,
useMarchingCubes,
augmentHierarchicalTree,
saveDotFiles,
timingsLogLevel,
treeLogLevel);
filter.SetActiveField("values");
// Execute the contour tree analysis
@ -896,17 +895,20 @@ int main(int argc, char* argv[])
{
if (computeHierarchicalVolumetricBranchDecomposition)
{
vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter(
blocksPerDim, globalSize, localBlockIndices, localBlockOrigins, localBlockSizes);
auto bd_result = bd_filter.Execute(result);
for (vtkm::Id ds_no = 0; ds_no < result.GetNumberOfPartitions(); ++ds_no)
{
auto ds = result.GetPartition(ds_no);
auto ds = bd_result.GetPartition(ds_no);
std::string branchDecompositionFileName = std::string("BranchDecomposition_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream treeStream(branchDecompositionFileName.c_str());
treeStream
<< vtkm::worklet::contourtree_distributed::HierarchicalVolumetricBranchDecomposer<
ValueType>::PrintBranches(ds);
treeStream << vtkm::worklet::scalar_topology::HierarchicalVolumetricBranchDecomposer::
PrintBranches(ds);
}
}
else

@ -199,6 +199,7 @@ add_subdirectory(geometry_refinement)
add_subdirectory(mesh_info)
add_subdirectory(multi_block)
add_subdirectory(resampling)
add_subdirectory(scalar_topology)
add_subdirectory(vector_analysis)
add_subdirectory(zfp)

@ -119,7 +119,6 @@ public:
bool useBoundaryExtremaOnly = true,
bool useMarchingCubes = false,
bool augmentHierarchicalTree = false,
bool computeHierarchicalVolumetricBranchDecomposition = false,
bool saveDotFiles = false,
vtkm::cont::LogLevel timingsLogLevel = vtkm::cont::LogLevel::Perf,
vtkm::cont::LogLevel treeLogLevel = vtkm::cont::LogLevel::Info);
@ -173,14 +172,6 @@ public:
std::stringstream& timingsStream,
std::vector<vtkm::cont::DataSet>& hierarchicalTreeOutputDataSet);
template <typename FieldType>
VTKM_CONT void ComputeBranchDecomposition(vtkmdiy::Master& inputContourTreeMaster,
vtkmdiy::DynamicAssigner& assigner,
vtkmdiy::RegularSwapPartners& partners,
const FieldType&, // dummy parameter to get the type
std::stringstream& timingsStream,
std::vector<vtkm::cont::DataSet>& outputDataSet);
///
/// Internal helper function that implements the actual functionality of PostExecute
///
@ -205,9 +196,6 @@ private:
/// Augment hierarchical tree
bool AugmentHierarchicalTree;
/// Compute the hierarchical volumetric branch decomposition
bool ComputeHierarchicalVolumetricBranchDecomposition;
/// Save dot files for all tree computations
bool SaveDotFiles;

@ -66,15 +66,12 @@
// distributed contour tree includes
#include <vtkm/worklet/contourtree_distributed/BoundaryTree.h>
#include <vtkm/worklet/contourtree_distributed/BoundaryTreeMaker.h>
#include <vtkm/worklet/contourtree_distributed/BranchDecompositionBlock.h>
#include <vtkm/worklet/contourtree_distributed/CombineHyperSweepBlockFunctor.h>
#include <vtkm/worklet/contourtree_distributed/ComputeDistributedBranchDecompositionFunctor.h>
#include <vtkm/worklet/contourtree_distributed/ComputeDistributedContourTreeFunctor.h>
#include <vtkm/worklet/contourtree_distributed/DistributedContourTreeBlockData.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalAugmenter.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalHyperSweeper.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalVolumetricBranchDecomposer.h>
#include <vtkm/worklet/contourtree_distributed/HyperSweepBlock.h>
#include <vtkm/worklet/contourtree_distributed/InteriorForest.h>
#include <vtkm/worklet/contourtree_distributed/PrintGraph.h>
@ -211,7 +208,6 @@ ContourTreeUniformDistributed::ContourTreeUniformDistributed(
bool useBoundaryExtremaOnly,
bool useMarchingCubes,
bool augmentHierarchicalTree,
bool computeHierarchicalVolumetricBranchDecomposition,
bool saveDotFiles,
vtkm::cont::LogLevel timingsLogLevel,
vtkm::cont::LogLevel treeLogLevel)
@ -219,8 +215,6 @@ ContourTreeUniformDistributed::ContourTreeUniformDistributed(
, UseBoundaryExtremaOnly(useBoundaryExtremaOnly)
, UseMarchingCubes(useMarchingCubes)
, AugmentHierarchicalTree(augmentHierarchicalTree)
, ComputeHierarchicalVolumetricBranchDecomposition(
computeHierarchicalVolumetricBranchDecomposition)
, SaveDotFiles(saveDotFiles)
, TimingsLogLevel(timingsLogLevel)
, TreeLogLevel(treeLogLevel)
@ -235,13 +229,6 @@ ContourTreeUniformDistributed::ContourTreeUniformDistributed(
, LocalInteriorForests(static_cast<std::size_t>(localBlockSizes.GetNumberOfValues()))
{
this->SetOutputFieldName("resultData");
if (this->ComputeHierarchicalVolumetricBranchDecomposition && !this->AugmentHierarchicalTree)
{
this->AugmentHierarchicalTree = true;
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Volumetric branch decmomposition requires augmentation. "
<< "Enabling the AugmentHierarchicalTree option.");
}
}
@ -842,144 +829,6 @@ inline VTKM_CONT void ContourTreeUniformDistributed::ComputeVolumeMetric(
});
}
//-----------------------------------------------------------------------------
template <typename FieldType>
inline VTKM_CONT void ContourTreeUniformDistributed::ComputeBranchDecomposition(
vtkmdiy::Master& inputContourTreeMaster,
vtkmdiy::DynamicAssigner& assigner,
vtkmdiy::RegularSwapPartners& partners,
const FieldType&, // dummy parameter to get the type
std::stringstream& timingsStream,
std::vector<vtkm::cont::DataSet>& outputDataSet)
{
vtkm::cont::Timer timer;
timer.Start();
using BranchDecompositionBlock =
vtkm::worklet::contourtree_distributed::BranchDecompositionBlock<FieldType>;
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
vtkmdiy::Master branch_decomposition_master(comm,
1, // Use 1 thread, VTK-M will do the treading
-1, // All blocks in memory
0, // No create function
BranchDecompositionBlock::Destroy);
// Log the time to create the DIY master for the hyper sweep
timingsStream << " " << std::setw(38) << std::left
<< "Create DIY Master (Branch Decomposition):" << timer.GetElapsedTime()
<< " seconds" << std::endl;
timer.Start();
// Copy data from hierarchical hypersweep to initialize branch decomposition computation
using DistributedContourTreeBlockData =
vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<FieldType>;
inputContourTreeMaster.foreach (
[&](DistributedContourTreeBlockData* currInBlock, const vtkmdiy::Master::ProxyWithLink&) {
BranchDecompositionBlock* newBlock = new BranchDecompositionBlock(
currInBlock->LocalBlockNo, currInBlock->GlobalBlockId, currInBlock->AugmentedTree);
// NOTE: Use dummy link to make DIY happy. The dummy link is never used, since all
// communication is via RegularDecomposer, which sets up its own links. No need
// to keep the pointer, as DIY will "own" it and delete it when no longer needed.
// NOTE: Since we passed a "Destroy" function to DIY master, it will own the local data
// blocks and delete them when done.
branch_decomposition_master.add(currInBlock->GlobalBlockId, newBlock, new vtkmdiy::Link());
});
// Log time to copy the data to the HyperSweepBlock data objects
timingsStream << " " << std::setw(38) << std::left << "Initialize Branch Decomposition Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
vtkmdiy::fix_links(branch_decomposition_master, assigner);
// Record time to fix the links
timingsStream << " " << std::setw(38) << std::left << "Fix DIY Links (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
branch_decomposition_master.foreach (
[&](BranchDecompositionBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
// Get intrinsic and dependent volume from data set
vtkm::cont::DataSet ds = outputDataSet[b->LocalBlockNo];
vtkm::cont::ArrayHandle<vtkm::Id> intrinsicVolume =
ds.GetField("IntrinsicVolume").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
vtkm::cont::ArrayHandle<vtkm::Id> dependentVolume =
ds.GetField("DependentVolume").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
// Get global size and compute total volume from it
const auto& globalSize = this->MultiBlockSpatialDecomposition.GlobalSize;
vtkm::Id totalVolume = globalSize[0] * globalSize[1] * globalSize[2];
// Compute local best up and down paths by volume
b->HierarchicalVolumetricBranchDecomposer.LocalBestUpDownByVolume(
&b->HierarchicalContourTree, intrinsicVolume, dependentVolume, totalVolume);
#ifdef DEBUG_PRINT
VTKM_LOG_S(this->TreeLogLevel, "Before reduction");
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(
b->HierarchicalVolumetricBranchDecomposer.BestUpSupernode.GetNumberOfValues(), rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpSupernode", b->HierarchicalVolumetricBranchDecomposer.BestUpSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownSupernode", b->HierarchicalVolumetricBranchDecomposer.BestDownSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpVolume", b->HierarchicalVolumetricBranchDecomposer.BestUpVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownVolume", b->HierarchicalVolumetricBranchDecomposer.BestDownVolume, -1, rs);
VTKM_LOG_S(this->TreeLogLevel, rs.str());
}
#endif
});
timingsStream << " " << std::setw(38) << std::left << "LocalBestUpDownByVolume"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Reduce
// partners for merge over regular block grid
vtkmdiy::reduce(
branch_decomposition_master,
assigner,
partners,
vtkm::worklet::contourtree_distributed::ComputeDistributedBranchDecompositionFunctor<
FieldType>{});
timingsStream << " " << std::setw(38) << std::left
<< "Exchanging best up/down supernode and volume"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
branch_decomposition_master.foreach (
[&](BranchDecompositionBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
b->HierarchicalVolumetricBranchDecomposer.CollapseBranches(&b->HierarchicalContourTree,
b->BranchRoots);
});
timingsStream << " " << std::setw(38) << std::left << "CollapseBranches"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
branch_decomposition_master.foreach ([&](BranchDecompositionBlock* b,
const vtkmdiy::Master::ProxyWithLink&) {
#ifdef DEBUG_PRINT
VTKM_LOG_S(this->TreeLogLevel,
b->HierarchicalVolumetricBranchDecomposer.PrintBranches(&b->HierarchicalContourTree,
b->BranchRoots));
#endif
vtkm::cont::Field branchRootField(
"BranchRoots", vtkm::cont::Field::Association::WholeMesh, b->BranchRoots);
outputDataSet[b->LocalBlockNo].AddField(branchRootField);
});
timingsStream << " " << std::setw(38) << std::left
<< "Creating Branch Decomposition Output Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
}
//-----------------------------------------------------------------------------
template <typename FieldType, typename StorageType, typename DerivedPolicy>
VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
@ -1350,77 +1199,61 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
// ******** 5. Create output data set ********
std::vector<vtkm::cont::DataSet> hierarchicalTreeOutputDataSet(master.size());
master.foreach (
[&](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) {
std::stringstream createOutdataTimingsStream;
vtkm::cont::Timer iterationTimer;
iterationTimer.Start();
master.foreach ([&](DistributedContourTreeBlockData* blockData,
const vtkmdiy::Master::ProxyWithLink&) {
std::stringstream createOutdataTimingsStream;
vtkm::cont::Timer iterationTimer;
iterationTimer.Start();
// Use the augmented tree if available or otherwise use the unaugmented hierarchical tree from the current block
auto blockHierarchcialTree = this->AugmentHierarchicalTree
? (*blockData->HierarchicalAugmenter.AugmentedTree)
: blockData->HierarchicalTree;
// Use the augmented tree if available or otherwise use the unaugmented hierarchical tree from the current block
const auto& blockHierarchcialTree = this->AugmentHierarchicalTree
? (*blockData->HierarchicalAugmenter.AugmentedTree)
: blockData->HierarchicalTree;
// Create data set from output
vtkm::cont::Field dataValuesField(
"DataValues", vtkm::cont::Field::Association::WholeMesh, blockHierarchcialTree.DataValues);
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(dataValuesField);
vtkm::cont::Field regularNodeGlobalIdsField("RegularNodeGlobalIds",
vtkm::cont::Field::Association::WholeMesh,
blockHierarchcialTree.RegularNodeGlobalIds);
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(regularNodeGlobalIdsField);
vtkm::cont::Field superarcsField(
"Superarcs", vtkm::cont::Field::Association::WholeMesh, blockHierarchcialTree.Superarcs);
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(superarcsField);
vtkm::cont::Field supernodesField(
"Supernodes", vtkm::cont::Field::Association::WholeMesh, blockHierarchcialTree.Supernodes);
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(supernodesField);
vtkm::cont::Field superparentsField("Superparents",
vtkm::cont::Field::Association::WholeMesh,
blockHierarchcialTree.Superparents);
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(superparentsField);
// Add the information to the output data set
blockHierarchcialTree.AddToVTKMDataSet(hierarchicalTreeOutputDataSet[blockData->LocalBlockNo]);
// Copy cell set from input data set. This is mainly to ensure that the output data set
// has a defined cell set. Without one, serialization for DIY does not work properly.
// Having the extents of the input data set may also help in other use cases.
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].SetCellSet(
input.GetPartition(blockData->LocalBlockNo).GetCellSet());
// Copy cell set from input data set. This is mainly to ensure that the output data set
// has a defined cell set. Without one, serialization for DIY does not work properly.
// Having the extents of the input data set may also help in other use cases.
hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].SetCellSet(
input.GetPartition(blockData->LocalBlockNo).GetCellSet());
// Log the time for each of the iterations of the fan out loop
createOutdataTimingsStream << " Create Output Dataset (block=" << blockData->LocalBlockNo
// Log the time for each of the iterations of the fan out loop
createOutdataTimingsStream << " Create Output Dataset (block=" << blockData->LocalBlockNo
<< ") : " << iterationTimer.GetElapsedTime() << " seconds"
<< std::endl;
iterationTimer.Start();
// save the corresponding .gv file
if (this->SaveDotFiles)
{
auto nRounds = blockData->ContourTrees.size() - 1;
vtkm::filter::contourtree_distributed_detail::SaveHierarchicalTreeDot(
blockData, rank, nRounds);
createOutdataTimingsStream << " Save Dot (block=" << blockData->LocalBlockNo
<< ") : " << iterationTimer.GetElapsedTime() << " seconds"
<< std::endl;
iterationTimer.Start();
} // if(this->SaveDotFiles)
// save the corresponding .gv file
if (this->SaveDotFiles)
{
auto nRounds = blockData->ContourTrees.size() - 1;
vtkm::filter::contourtree_distributed_detail::SaveHierarchicalTreeDot(
blockData, rank, nRounds);
// Log the timing stats we collected
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ------------ Create Output Data (block=" << blockData->LocalBlockNo
<< ") ------------" << std::endl
<< createOutdataTimingsStream.str());
createOutdataTimingsStream << " Save Dot (block=" << blockData->LocalBlockNo
<< ") : " << iterationTimer.GetElapsedTime() << " seconds"
<< std::endl;
iterationTimer.Start();
} // if(this->SaveDotFiles)
// Log the timing stats we collected
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ------------ Create Output Data (block=" << blockData->LocalBlockNo
<< ") ------------" << std::endl
<< createOutdataTimingsStream.str());
// Log the stats from the hierarchical contour tree
VTKM_LOG_S(this->TreeLogLevel,
std::endl
<< " ------------ Hierarchical Tree Construction Stats ------------"
<< std::endl
<< std::setw(42) << std::left << " LocalBlockNo"
<< ": " << blockData->LocalBlockNo << std::endl
<< blockData->HierarchicalTree.PrintTreeStats() << std::endl);
}); // master.foreach
// Log the stats from the hierarchical contour tree
VTKM_LOG_S(this->TreeLogLevel,
std::endl
<< " ------------ Hierarchical Tree Construction Stats ------------"
<< std::endl
<< std::setw(42) << std::left << " LocalBlockNo"
<< ": " << blockData->LocalBlockNo << std::endl
<< blockData->HierarchicalTree.PrintTreeStats() << std::endl);
}); // master.foreach
// 3.1 Log total augmentation time
timingsStream << " " << std::setw(38) << std::left << "Create Output Data"
@ -1433,12 +1266,6 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
master, assigner, partners, FieldType{}, timingsStream, hierarchicalTreeOutputDataSet);
}
if (this->ComputeHierarchicalVolumetricBranchDecomposition)
{
this->ComputeBranchDecomposition(
master, assigner, partners, FieldType{}, timingsStream, hierarchicalTreeOutputDataSet);
}
VTKM_LOG_S(this->TimingsLogLevel,
std::endl
<< " ------------ DoPostExecute Timings ------------" << std::endl

@ -0,0 +1,99 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//=============================================================================
//
// This code is an extension of the algorithm presented in the paper:
// Parallel Peak Pruning for Scalable SMP Contour Tree Computation.
// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens.
// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization
// (LDAV), October 2016, Baltimore, Maryland.
//
// The PPP2 algorithm and software were jointly developed by
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
#include <vtkm/filter/scalar_topology/BranchDecompositionBlock.h>
// Contour tree includes, not ye moved to new filter design
#include <vtkm/worklet/contourtree/Types.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<vtkm::Id>,
vtkm::cont::ArrayHandle<vtkm::Id>>
BranchDecompositionBlock::CreateFirstsupernodePerIterationArrayHandle(
const vtkm::cont::DataSet& hierarchicalContourTreeDataSet)
{
vtkm::cont::ArrayHandle<vtkm::Id> FirstSupernodePerIterationComponents;
vtkm::cont::ArrayHandle<vtkm::Id> FirstSupernodePerIterationOffsets;
vtkm::cont::ArrayCopyShallowIfPossible(
hierarchicalContourTreeDataSet.GetField("FirstSupernodePerIterationComponents").GetData(),
FirstSupernodePerIterationComponents);
vtkm::cont::ArrayCopyShallowIfPossible(
hierarchicalContourTreeDataSet.GetField("FirstSupernodePerIterationOffsets").GetData(),
FirstSupernodePerIterationOffsets);
return vtkm::cont::make_ArrayHandleGroupVecVariable(FirstSupernodePerIterationComponents,
FirstSupernodePerIterationOffsets);
}
BranchDecompositionBlock::BranchDecompositionBlock(
vtkm::Id localBlockNo,
int globalBlockId,
const vtkm::cont::DataSet& hierarchicalTreeDataSet)
: LocalBlockNo(localBlockNo)
, GlobalBlockId(globalBlockId)
, FirstSupernodePerIteration(CreateFirstsupernodePerIterationArrayHandle(hierarchicalTreeDataSet))
{
vtkm::Id nSupernodes =
hierarchicalTreeDataSet.GetField("Supernodes").GetData().GetNumberOfValues();
this->BranchRoots.AllocateAndFill(nSupernodes,
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
}
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -53,55 +53,45 @@
#ifndef vtk_m_worklet_contourtree_distributed_branchdecompositionblock_h
#define vtk_m_worklet_contourtree_distributed_branchdecompositionblock_h
#include <vtkm/worklet/contourtree/Types.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalVolumetricBranchDecomposer.h>
// Contour tree includes
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
template <typename ContourTreeDataFieldType>
struct BranchDecompositionBlock
{
BranchDecompositionBlock(
vtkm::Id localBlockNo,
int globalBlockId,
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<ContourTreeDataFieldType>&
hierarchicalContourTree)
: LocalBlockNo(localBlockNo)
, GlobalBlockId(globalBlockId)
, HierarchicalContourTree(hierarchicalContourTree)
{
// Import/alias for readability
using vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
const vtkm::Id nSupernodes = HierarchicalContourTree.Supernodes.GetNumberOfValues();
//VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Allocating " << nSupernodes << " valies in BranchRoots");
BranchRoots.AllocateAndFill(nSupernodes, NO_SUCH_ELEMENT);
}
static vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<vtkm::Id>,
vtkm::cont::ArrayHandle<vtkm::Id>>
CreateFirstsupernodePerIterationArrayHandle(
const vtkm::cont::DataSet& hierarchicalContourTreeDataSet);
// Block metadata TODO/FIXME: Check whether really needed
BranchDecompositionBlock(vtkm::Id localBlockNo,
int globalBlockId,
const vtkm::cont::DataSet& hierarchicalTreeDataSet);
// Block metadata
vtkm::Id LocalBlockNo;
int GlobalBlockId;
int GlobalBlockId; // TODO/FIXME: Check whether really needed. Possibly only during debugging
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<ContourTreeDataFieldType>&
HierarchicalContourTree;
vtkm::worklet::contourtree_distributed::HierarchicalVolumetricBranchDecomposer<
ContourTreeDataFieldType>
HierarchicalVolumetricBranchDecomposer;
// Data from hierarchical tree needed during reduction
vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<vtkm::Id>,
vtkm::cont::ArrayHandle<vtkm::Id>>
FirstSupernodePerIteration;
// Decomposer used during reduction and output data set
HierarchicalVolumetricBranchDecomposer VolumetricBranchDecomposer;
vtkm::cont::ArrayHandle<vtkm::Id> BranchRoots;
// Destroy function allowing DIY to own blocks and clean them up after use
static void Destroy(void* b)
{
delete static_cast<BranchDecompositionBlock<ContourTreeDataFieldType>*>(b);
}
static void Destroy(void* b) { delete static_cast<BranchDecompositionBlock*>(b); }
};
} // namespace contourtree_distributed
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,49 @@
##============================================================================
## 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.
##============================================================================
set(scalar_topology_headers
BranchDecompositionBlock.h
ComputeDistributedBranchDecompositionFunctor.h
DistributedBranchDecompositionFilter.h
)
set(scalar_topology_sources
BranchDecompositionBlock.cxx
ComputeDistributedBranchDecompositionFunctor.cxx
DistributedBranchDecompositionFilter.cxx
)
# TODO/FIXME: Is the following line needed
# set_source_files_properties(DistributedBranchDecompositionFilter.cxx PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
vtkm_library(
NAME vtkm_filter_scalar_topology
HEADERS ${scalar_topology_headers}
DEVICE_SOURCES ${scalar_topology_sources}
USE_VTKM_JOB_POOL
)
# TODO/FIXME: Are the following lines needed
# set_property(TARGET
# vtkm_filter_scalar_topology
# PROPERTY UNITY_BUILD_MODE GROUP
# )
target_link_libraries(vtkm_filter_scalar_topology PUBLIC vtkm_worklet vtkm_filter_core)
if (VTKm_ENABLE_MPI)
target_link_libraries(vtkm_filter_scalar_topology PUBLIC MPI::MPI_CXX)
endif ()
target_link_libraries(vtkm_filter PUBLIC INTERFACE vtkm_filter_scalar_topology)
add_subdirectory(worklet)
#-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if (VTKm_ENABLE_TESTING)
add_subdirectory(testing)
endif ()

@ -0,0 +1,220 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//=============================================================================
//
// This code is an extension of the algorithm presented in the paper:
// Parallel Peak Pruning for Scalable SMP Contour Tree Computation.
// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens.
// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization
// (LDAV), October 2016, Baltimore, Maryland.
//
// The PPP2 algorithm and software were jointly developed by
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
#include <vtkm/filter/scalar_topology/ComputeDistributedBranchDecompositionFunctor.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/FindBestSupernodeWorklet.h>
#include <vtkm/Types.h>
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_COMBINED_BLOCK_IDS
#endif
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
void ComputeDistributedBranchDecompositionFunctor::operator()(
vtkm::worklet::scalar_topology::BranchDecompositionBlock* b,
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) 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
auto& branchDecomposer = b->VolumetricBranchDecomposer;
std::vector<int> incoming;
rp.incoming(incoming);
for (const int ingid : incoming)
{
// NOTE/IMPORTANT: In each round we should have only one swap partner (despite for-loop here).
// If that assumption does not hold, it will break things.
// NOTE/IMPORTANT: This assumption only holds if the number of blocks is a power of two.
// 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);
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Combining local block " << b->GlobalBlockId << " with incomoing block "
<< incomingGlobalBlockId);
#endif
// Receive data from swap partner
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestUpVolume;
rp.dequeue(ingid, incomingBestUpVolume);
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestUpSupernode;
rp.dequeue(ingid, incomingBestUpSupernode);
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestDownVolume;
rp.dequeue(ingid, incomingBestDownVolume);
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestDownSupernode;
rp.dequeue(ingid, incomingBestDownSupernode);
vtkm::Id prefixLength = b->FirstSupernodePerIteration.ReadPortal().Get(rp.round() - 1)[0];
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Prefix length is " << prefixLength);
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(
incomingBestUpSupernode.GetNumberOfValues(), rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestUpSupernode", incomingBestUpSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestDownSupernode", incomingBestDownSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestUpVolume", incomingBestUpVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestDownVolume", incomingBestDownVolume, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
// NOTE: We are processing input data from the previous round, hence, get
// first supernide per ieteration from previous round
// Create 'views' to restrict worklet to relevant portion of arrays
auto bestUpVolumeView = make_ArrayHandleView(branchDecomposer.BestUpVolume, 0, prefixLength);
auto bestUpSupernodeView =
make_ArrayHandleView(branchDecomposer.BestUpSupernode, 0, prefixLength);
auto bestDownVolumeView =
make_ArrayHandleView(branchDecomposer.BestDownVolume, 0, prefixLength);
auto bestDownSupernodeView =
make_ArrayHandleView(branchDecomposer.BestDownSupernode, 0, prefixLength);
// Check if swap partner knows a better up /down and update
vtkm::cont::Invoker invoke;
invoke(vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
FindBestSupernodeWorklet<true>{},
incomingBestUpVolume,
incomingBestUpSupernode,
bestUpVolumeView,
bestUpSupernodeView);
invoke(vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
FindBestSupernodeWorklet<false>{},
incomingBestDownVolume,
incomingBestDownSupernode,
bestDownVolumeView,
bestDownSupernodeView);
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "After round " << rp.round() - 1);
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(
branchDecomposer.BestUpSupernode.GetNumberOfValues(), rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpSupernode", branchDecomposer.BestUpSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownSupernode", branchDecomposer.BestDownSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpVolume", branchDecomposer.BestUpVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownVolume", branchDecomposer.BestDownVolume, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
}
}
for (int cc = 0; cc < rp.out_link().size(); ++cc)
{
auto target = rp.out_link().target(cc);
if (target.gid != selfid)
{
#ifdef DEBUG_PRINT_COMBINED_BLOCK_IDS
rp.enqueue(target, b->GlobalBlockId);
#endif
// Determine which portion of up/down volume/supernode to send
vtkm::Id prefixLength = b->FirstSupernodePerIteration.ReadPortal().Get(rp.round())[0];
// Create 'views' to restrict sending to relevant portion of arrays
auto bestUpVolumeView = make_ArrayHandleView(branchDecomposer.BestUpVolume, 0, prefixLength);
auto bestUpSupernodeView =
make_ArrayHandleView(branchDecomposer.BestUpSupernode, 0, prefixLength);
auto bestDownVolumeView =
make_ArrayHandleView(branchDecomposer.BestDownVolume, 0, prefixLength);
auto bestDownSupernodeView =
make_ArrayHandleView(branchDecomposer.BestDownSupernode, 0, prefixLength);
// TODO/FIXME: Check if it is possible to send a portion of the arrays
// without copy. enqueue does not accept ArrayHandleView as input as it
// is not trivially copyable
vtkm::cont::ArrayHandle<vtkm::Id> sendBestUpVolume;
vtkm::cont::Algorithm::Copy(bestUpVolumeView, sendBestUpVolume);
vtkm::cont::ArrayHandle<vtkm::Id> sendBestUpSupernode;
vtkm::cont::Algorithm::Copy(bestUpSupernodeView, sendBestUpSupernode);
vtkm::cont::ArrayHandle<vtkm::Id> sendBestDownVolume;
vtkm::cont::Algorithm::Copy(bestDownVolumeView, sendBestDownVolume);
vtkm::cont::ArrayHandle<vtkm::Id> sendBestDownSupernode;
vtkm::cont::Algorithm::Copy(bestDownSupernodeView, sendBestDownSupernode);
rp.enqueue(target, sendBestUpVolume);
rp.enqueue(target, sendBestUpSupernode);
rp.enqueue(target, sendBestDownVolume);
rp.enqueue(target, sendBestDownSupernode);
}
}
}
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -0,0 +1,84 @@
//============================================================================
// 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_worklet_contourtree_distributed__h
#define vtk_m_worklet_contourtree_distributed__h
#include <vtkm/filter/scalar_topology/BranchDecompositionBlock.h>
// clang-format off
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/diy/diy.h>
VTKM_THIRDPARTY_POST_INCLUDE
// clang-format on
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
struct ComputeDistributedBranchDecompositionFunctor
{
void operator()(vtkm::worklet::scalar_topology::BranchDecompositionBlock* b,
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) const;
};
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,281 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/filter/scalar_topology/BranchDecompositionBlock.h>
#include <vtkm/filter/scalar_topology/ComputeDistributedBranchDecompositionFunctor.h>
#include <vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h>
// vtkm includes
#include <vtkm/cont/Timer.h>
// DIY includes
// clang-format off
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/diy/Configure.h>
#include <vtkm/thirdparty/diy/diy.h>
VTKM_THIRDPARTY_POST_INCLUDE
// clang-format on
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
// Constructor to record information about spatial decomposition
// TODO/FIXME: Add this information to PartitionedDataSet, so that we do
// not need to pass it sperately (or check if it can already be derived from
// information stored in PartitionedDataSet)
VTKM_CONT DistributedBranchDecompositionFilter::DistributedBranchDecompositionFilter(
vtkm::Id3 blocksPerDim,
vtkm::Id3 globalSize,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockIndices,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockOrigins,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockSizes)
: MultiBlockSpatialDecomposition(blocksPerDim,
globalSize,
localBlockIndices,
localBlockOrigins,
localBlockSizes)
{
}
VTKM_CONT vtkm::cont::DataSet DistributedBranchDecompositionFilter::DoExecute(
const vtkm::cont::DataSet& input)
{
// TODO/FIXME: Is there a better way to do this?
return input; // Dummy function, not used
}
VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& input)
{
vtkm::cont::Timer timer;
timer.Start();
std::stringstream timingsStream;
// Set up DIY master
// TODO/FIXME: A lot of the code to set up DIY is the same for this filter and
// ContourTreeUniformDistributed. Consolidate? (Which is difficult to do as
// multiple variables are set up with some subtle differences)
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
int rank = comm.rank();
int size = comm.size();
using BranchDecompositionBlock = vtkm::worklet::scalar_topology::BranchDecompositionBlock;
vtkmdiy::Master branch_decomposition_master(comm,
1, // Use 1 thread, VTK-M will do the treading
-1, // All blocks in memory
0, // No create function
BranchDecompositionBlock::Destroy);
timingsStream << " " << std::setw(60) << std::left
<< "Create DIY Master (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Compute global ids (gids) for our local blocks
using RegularDecomposer = vtkmdiy::RegularDecomposer<vtkmdiy::DiscreteBounds>;
int globalNumberOfBlocks =
static_cast<int>(this->MultiBlockSpatialDecomposition.GetGlobalNumberOfBlocks());
int numDims = static_cast<int>(this->MultiBlockSpatialDecomposition.NumberOfDimensions());
// ... compute division vector for global domain
RegularDecomposer::DivisionsVector diyDivisions(numDims);
for (vtkm::IdComponent d = 0; d < static_cast<vtkm::IdComponent>(numDims); ++d)
{
diyDivisions[d] = static_cast<int>(this->MultiBlockSpatialDecomposition.BlocksPerDimension[d]);
}
// ... compute coordinates of local blocks
std::vector<int> vtkmdiyLocalBlockGids(static_cast<size_t>(input.GetNumberOfPartitions()));
auto localBlockIndicesPortal =
this->MultiBlockSpatialDecomposition.LocalBlockIndices.ReadPortal();
for (vtkm::Id bi = 0; bi < input.GetNumberOfPartitions(); bi++)
{
RegularDecomposer::DivisionsVector diyCoords(static_cast<size_t>(numDims));
auto currentCoords = localBlockIndicesPortal.Get(bi);
for (vtkm::IdComponent d = 0; d < numDims; ++d)
{
diyCoords[d] = static_cast<int>(currentCoords[d]);
}
vtkmdiyLocalBlockGids[static_cast<size_t>(bi)] =
RegularDecomposer::coords_to_gid(diyCoords, diyDivisions);
}
// Record time to compute the local block ids
timingsStream << " " << std::setw(60) << std::left
<< "Compute Block Ids and Local Links (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Initialize branch decomposition computation from data in PartitionedDataSet blocks
for (vtkm::Id localBlockIndex = 0; localBlockIndex < input.GetNumberOfPartitions();
++localBlockIndex)
{
int globalBlockId = vtkmdiyLocalBlockGids[localBlockIndex];
const vtkm::cont::DataSet& ds = input.GetPartition(localBlockIndex);
BranchDecompositionBlock* newBlock =
new BranchDecompositionBlock(localBlockIndex, globalBlockId, ds);
// NOTE: Use dummy link to make DIY happy. The dummy link is never used, since all
// communication is via RegularDecomposer, which sets up its own links. No need
// to keep the pointer, as DIY will "own" it and delete it when no longer needed.
// NOTE: Since we passed a "Destroy" function to DIY master, it will own the local data
// blocks and delete them when done.
branch_decomposition_master.add(globalBlockId, newBlock, new vtkmdiy::Link());
}
// Log time to copy the data to the HyperSweepBlock data objects
timingsStream << " " << std::setw(60) << std::left << "Initialize Branch Decomposition Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Set up DIY for binary reduction
RegularDecomposer::BoolVector shareFace(3, true);
RegularDecomposer::BoolVector wrap(3, false);
RegularDecomposer::CoordinateVector ghosts(3, 1);
RegularDecomposer decomposer(numDims,
this->MultiBlockSpatialDecomposition.GetVTKmDIYBounds(),
static_cast<int>(globalNumberOfBlocks),
shareFace,
wrap,
ghosts,
diyDivisions);
// Define which blocks live on which rank so that vtkmdiy can manage them
vtkmdiy::DynamicAssigner assigner(comm, size, globalNumberOfBlocks);
for (vtkm::Id bi = 0; bi < input.GetNumberOfPartitions(); bi++)
{
assigner.set_rank(rank, vtkmdiyLocalBlockGids[static_cast<size_t>(bi)]);
}
timingsStream << " " << std::setw(60) << std::left
<< "Create DIY Decomposer and Assigner (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Fix the vtkmdiy links.
vtkmdiy::fix_links(branch_decomposition_master, assigner);
timingsStream << " " << std::setw(60) << std::left << "Fix DIY Links (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// partners for merge over regular block grid
vtkmdiy::RegularSwapPartners partners(
decomposer, // domain decomposition
2, // radix of k-ary reduction.
true // contiguous: true=distance doubling, false=distance halving
);
timingsStream << " " << std::setw(60) << std::left
<< "Create DIY Swap Partners (Branch Decomposition)"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Compute the initial volumes
branch_decomposition_master.foreach (
[&](BranchDecompositionBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
// Get intrinsic and dependent volume from data set
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
vtkm::cont::ArrayHandle<vtkm::Id> intrinsicVolume =
ds.GetField("IntrinsicVolume").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
vtkm::cont::ArrayHandle<vtkm::Id> dependentVolume =
ds.GetField("DependentVolume").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
// Get global size and compute total volume from it
const auto& globalSize = this->MultiBlockSpatialDecomposition.GlobalSize;
vtkm::Id totalVolume = globalSize[0] * globalSize[1] * globalSize[2];
// Compute local best up and down paths by volume
b->VolumetricBranchDecomposer.LocalBestUpDownByVolume(
ds, intrinsicVolume, dependentVolume, totalVolume);
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Before reduction");
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(
b->HierarchicalVolumetricBranchDecomposer.BestUpSupernode.GetNumberOfValues(), rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpSupernode", b->HierarchicalVolumetricBranchDecomposer.BestUpSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownSupernode", b->HierarchicalVolumetricBranchDecomposer.BestDownSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpVolume", b->HierarchicalVolumetricBranchDecomposer.BestUpVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownVolume", b->HierarchicalVolumetricBranchDecomposer.BestDownVolume, -1, rs);
VTKM_LOG_S(this->TreeLogLevel, rs.str());
}
#endif
});
timingsStream << " " << std::setw(60) << std::left << "LocalBestUpDownByVolume"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
// Reduce
// partners for merge over regular block grid
vtkmdiy::reduce(branch_decomposition_master,
assigner,
partners,
vtkm::worklet::scalar_topology::ComputeDistributedBranchDecompositionFunctor{});
timingsStream << " " << std::setw(60) << std::left
<< "Exchanging best up/down supernode and volume"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
branch_decomposition_master.foreach (
[&](BranchDecompositionBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
b->VolumetricBranchDecomposer.CollapseBranches(ds, b->BranchRoots);
});
timingsStream << " " << std::setw(38) << std::left << "CollapseBranches"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
timer.Start();
std::vector<vtkm::cont::DataSet> outputDataSets(input.GetNumberOfPartitions());
// Copy input data set to output
// TODO/FIXME: Should we really do this? Or just output branchRoots
// and let the application deal with two ParitionedDataSet objects
// if it also needs access to the other contour tree data
for (vtkm::Id ds_no = 0; ds_no < input.GetNumberOfPartitions(); ++ds_no)
{
outputDataSets[ds_no] = input.GetPartition(ds_no);
}
branch_decomposition_master.foreach (
[&](BranchDecompositionBlock* b, const vtkmdiy::Master::ProxyWithLink&) {
vtkm::cont::Field branchRootField(
"BranchRoots", vtkm::cont::Field::Association::WholeMesh, b->BranchRoots);
outputDataSets[b->LocalBlockNo].AddField(branchRootField);
});
timingsStream << " " << std::setw(38) << std::left
<< "Creating Branch Decomposition Output Data"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
VTKM_LOG_S(vtkm::cont::LogLevel::Perf,
std::endl
<< "----------- DoExecutePartitions Timings ------------" << std::endl
<< timingsStream.str());
return vtkm::cont::PartitionedDataSet{ outputDataSets };
}
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm

@ -0,0 +1,82 @@
//============================================================================
// 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.
//
//=============================================================================
#ifndef vtk_m_filter_contour_tree_DistributedBranchDecompositionFilter_h
#define vtk_m_filter_contour_tree_DistributedBranchDecompositionFilter_h
#include <vtkm/filter/NewFilterField.h>
#include <vtkm/filter/scalar_topology/vtkm_filter_scalar_topology_export.h>
#include <vtkm/worklet/contourtree_distributed/SpatialDecomposition.h>
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
/// \brief Compute branch decompostion from distributed contour tree
class VTKM_FILTER_SCALAR_TOPOLOGY_EXPORT DistributedBranchDecompositionFilter
: public vtkm::filter::NewFilter
{
public:
VTKM_CONT DistributedBranchDecompositionFilter(
vtkm::Id3 blocksPerDim,
vtkm::Id3 globalSize,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockIndices,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockOrigins,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockSizes);
private:
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input) override;
VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& inData) override;
/// Information about the spatial decomposition
vtkm::worklet::contourtree_distributed::SpatialDecomposition MultiBlockSpatialDecomposition;
};
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,11 @@
##=============================================================================
##
## 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.
##
##=============================================================================

@ -0,0 +1,11 @@
##============================================================================
## 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.
##============================================================================
add_subdirectory(branch_decomposition)

@ -0,0 +1,17 @@
##============================================================================
## 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.
##============================================================================
set(headers
HierarchicalVolumetricBranchDecomposer.h
)
#-----------------------------------------------------------------------------
add_subdirectory(hierarchical_volumetric_branch_decomposer)
vtkm_declare_headers(${headers})

@ -116,24 +116,27 @@
//=======================================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_h
#include <iomanip>
#include <string>
// Contour tree includes, not yet moved into new filter structure
#include <vtkm/worklet/contourtree_augmented/NotNoSuchElementPredicate.h>
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_distributed/PrintGraph.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/FindRegularByGlobal.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcBetweenNodes.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/CollapseBranchesPointerDoublingWorklet.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/LocalBestUpDownByVolumeBestUpDownEdgeWorklet.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/LocalBestUpDownByVolumeInitSuperarcListWorklet.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/LocalBestUpDownByVolumeWorklet.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/SuperArcVolumetricComparatorIndirectGlobalIdComparator.h>
// Worklets
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesPointerDoublingWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/LocalBestUpDownByVolumeBestUpDownEdgeWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/LocalBestUpDownByVolumeInitSuperarcListWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/LocalBestUpDownByVolumeWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/SuperArcVolumetricComparatorIndirectGlobalIdComparator.h>
#ifdef DEBUG_PRINT
#define DEBUG_HIERARCHICAL_VOLUMETRIC_BRANCH_DECOMPOSER
@ -143,11 +146,10 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
/// Facture class for augmenting the hierarchical contour tree to enable computations of measures, e.g., volumne
template <typename FieldType>
class HierarchicalVolumetricBranchDecomposer
{ // class HierarchicalVolumetricBranchDecomposer
public:
@ -162,11 +164,6 @@ public:
vtkm::worklet::contourtree_augmented::IdArrayType UpVolume;
vtkm::worklet::contourtree_augmented::IdArrayType DownVolume;
/// Because we want to use array allocation in main, we need a default constructor
/// Unfortunately, that means we have problems with references, and the workaround is to have
/// the references passed to the individual functions
HierarchicalVolumetricBranchDecomposer<FieldType>(){};
/// routines to compute branch decomposition by volume
/// WARNING: we now have two types of hierarchical tree sharing a data structure:
/// I. hierarchical tree without augmentation
@ -194,23 +191,22 @@ public:
/// routine that determines the best upwards/downwards edges at each vertex
/// Unlike the local version, the best might only be stored on another rank
/// so we will compute the locally best up or down, then swap until all ranks choose the same best
void LocalBestUpDownByVolume(
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>*
hierarchicalTree,
const vtkm::cont::ArrayHandle<vtkm::Id>& intrinsicValues,
const vtkm::cont::ArrayHandle<vtkm::Id>& dependentValues,
vtkm::Id totalVolume);
void LocalBestUpDownByVolume(const vtkm::cont::DataSet& hierarchicalTreeDataSet,
const vtkm::cont::ArrayHandle<vtkm::Id>& intrinsicValues,
const vtkm::cont::ArrayHandle<vtkm::Id>& dependentValues,
vtkm::Id totalVolume);
/// routine to compute the local set of superarcs that root at a given one
void CollapseBranches(const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<
FieldType>* hierarchicalTree,
void CollapseBranches(const vtkm::cont::DataSet& hierarchicalTreeDataSet,
vtkm::worklet::contourtree_augmented::IdArrayType& branchRoot);
// routine to print branches
std::string PrintBranches(const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<
FieldType>* hierarchicalTree,
const vtkm::worklet::contourtree_augmented::IdArrayType& branchRoot);
/// routines to print branches
template <typename IdArrayHandleType, typename DataValueArrayHandleType>
static std::string PrintBranches(const IdArrayHandleType& hierarchicalTreeSuperarcsAH,
const IdArrayHandleType& hierarchicalTreeSupernodesAH,
const IdArrayHandleType& hierarchicalTreeRegularNodeGlobalIdsAH,
const DataValueArrayHandleType& hierarchicalTreeDataValuesAH,
const IdArrayHandleType& branchRootAH);
static std::string PrintBranches(const vtkm::cont::DataSet& ds);
/// debug routine
@ -223,31 +219,39 @@ private:
}; // class HierarchicalVolumetricBranchDecomposer
template <typename FieldType>
void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>*
hierarchicalTree,
inline void HierarchicalVolumetricBranchDecomposer::LocalBestUpDownByVolume(
const vtkm::cont::DataSet& hierarchicalTreeDataSet,
const vtkm::cont::ArrayHandle<vtkm::Id>& intrinsicValues,
const vtkm::cont::ArrayHandle<vtkm::Id>& dependentValues,
vtkm::Id totalVolume)
{
// Get required arrays for hierarchical tree form data set
auto hierarchicalTreeSupernodes = hierarchicalTreeDataSet.GetField("Supernodes")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeSuperarcs = hierarchicalTreeDataSet.GetField("Superarcs")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeRegularNodeGlobalIds =
hierarchicalTreeDataSet.GetField("RegularNodeGlobalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
// LocalBestUpDownByVolume
// STAGE I: Allocate memory for our arrays
vtkm::Id nSupernodes = hierarchicalTree->Supernodes.GetNumberOfValues();
vtkm::Id nSupernodes = hierarchicalTreeSupernodes.GetNumberOfValues();
// WARNING: This differs from the non-hierarchical version by using the full size *WITH* virtual superarcs
vtkm::Id nSuperarcs = hierarchicalTree->Superarcs.GetNumberOfValues();
vtkm::Id nSuperarcs = hierarchicalTreeSuperarcs.GetNumberOfValues();
// set up a list of superarcs as Edges for reference in our comparator
vtkm::worklet::contourtree_augmented::EdgePairArray superarcList;
superarcList.Allocate(nSuperarcs);
VTKM_ASSERT(hierarchicalTree->Superarcs.GetNumberOfValues() == superarcList.GetNumberOfValues());
this->Invoke(vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
this->Invoke(vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
LocalBestUpDownByVolumeInitSuperarcListWorklet{}, // the worklet
hierarchicalTree->Superarcs, // input
hierarchicalTreeSuperarcs, // input
superarcList // output
);
#ifdef DEBUG_HIERARCHICAL_VOLUMETRIC_BRANCH_DECOMPOSER
{
std::stringstream resultStream;
@ -265,7 +269,7 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
// and fill it up with index values [0, 1, 2 ... nSuperarcs-1] while simultaneously stream compacting the
// values by keeping only those indices where the hierarchicalTree->Superarcs is not NoSuchElement.
vtkm::cont::Algorithm::CopyIf(vtkm::cont::ArrayHandleIndex(nSuperarcs), //input
hierarchicalTree->Superarcs, // stencil
hierarchicalTreeSuperarcs, // stencil
actualSuperarcs, // output target array
vtkm::worklet::contourtree_augmented::NotNoSuchElementPredicate{});
// NOTE: The behavior here is slightly different from the original implementation, as the original code
@ -307,12 +311,12 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
// given that we have already suppressed the non-virtual superarcs
// however, in this case, we need to use the actualSuperarcs array instead of the main array
{
vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
LocalBestUpDownByVolumeBestUpDownEdgeWorklet bestUpDownEdgeWorklet(totalVolume);
// permut input and output arrays here so we can use FieldIn and FieldOut to
// avoid the use of WholeArray access in the worklet
auto permutedHierarchicalTreeSuperarcs = vtkm::cont::make_ArrayHandlePermutation(
actualSuperarcs, hierarchicalTree->Superarcs); // input
auto permutedHierarchicalTreeSuperarcs =
vtkm::cont::make_ArrayHandlePermutation(actualSuperarcs, hierarchicalTreeSuperarcs); // input
auto permutedDependetValues =
vtkm::cont::make_ArrayHandlePermutation(actualSuperarcs, dependentValues); // input
auto permutedIntrinsicValues =
@ -348,10 +352,10 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
// NB: We reuse the actual superarcs list here - this works because we have indexed the volumes on the underlying superarc ID
// NB 2: Notice that we only sort the "actual" ones - this is to avoid unnecessary resize() calls in vtkm later on
{
vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
SuperArcVolumetricComparatorIndirectGlobalIdComparator
SuperArcVolumetricComparatorIndirectGlobalIdComparator(
this->UpVolume, superarcList, hierarchicalTree->RegularNodeGlobalIds, false);
this->UpVolume, superarcList, hierarchicalTreeRegularNodeGlobalIds, false);
vtkm::cont::Algorithm::Sort(actualSuperarcs,
SuperArcVolumetricComparatorIndirectGlobalIdComparator);
}
@ -373,15 +377,15 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
{
auto permutedUpVolume =
vtkm::cont::make_ArrayHandlePermutation(actualSuperarcs, this->UpVolume);
this->Invoke(vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
this->Invoke(vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
LocalBestUpDownByVolumeWorklet<true>{ nActualSuperarcs },
actualSuperarcs, // input
superarcList, // input
permutedUpVolume, // input
hierarchicalTree->RegularNodeGlobalIds, // input
hierarchicalTree->Supernodes, // input
this->BestDownSupernode, // output
this->BestDownVolume // output
actualSuperarcs, // input
superarcList, // input
permutedUpVolume, // input
hierarchicalTreeRegularNodeGlobalIds, // input
hierarchicalTreeSupernodes, // input
this->BestDownSupernode, // output
this->BestDownVolume // output
);
}
#ifdef DEBUG_HIERARCHICAL_VOLUMETRIC_BRANCH_DECOMPOSER
@ -391,10 +395,10 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
// II B 3. Repeat for lower vertex
{
vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
SuperArcVolumetricComparatorIndirectGlobalIdComparator
SuperArcVolumetricComparatorIndirectGlobalIdComparator(
this->DownVolume, superarcList, hierarchicalTree->RegularNodeGlobalIds, true);
this->DownVolume, superarcList, hierarchicalTreeRegularNodeGlobalIds, true);
vtkm::cont::Algorithm::Sort(actualSuperarcs,
SuperArcVolumetricComparatorIndirectGlobalIdComparator);
}
@ -417,15 +421,15 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
{
auto permutedDownVolume =
vtkm::cont::make_ArrayHandlePermutation(actualSuperarcs, this->DownVolume);
this->Invoke(vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
this->Invoke(vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
LocalBestUpDownByVolumeWorklet<false>{ nActualSuperarcs },
actualSuperarcs, // input
superarcList, // input
permutedDownVolume, // input
hierarchicalTree->RegularNodeGlobalIds, // input
hierarchicalTree->Supernodes, // input
this->BestUpSupernode, // output
this->BestUpVolume // output
actualSuperarcs, // input
superarcList, // input
permutedDownVolume, // input
hierarchicalTreeRegularNodeGlobalIds, // input
hierarchicalTreeSupernodes, // input
this->BestUpSupernode, // output
this->BestUpVolume // output
);
}
@ -435,32 +439,57 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::LocalBestUpDownByVolume(
#endif
} // LocalBestUpDownByVolume
template <typename FieldType>
void HierarchicalVolumetricBranchDecomposer<FieldType>::CollapseBranches(
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>*
hierarchicalTree,
inline void HierarchicalVolumetricBranchDecomposer::CollapseBranches(
const vtkm::cont::DataSet& hierarchicalTreeDataSet,
vtkm::worklet::contourtree_augmented::IdArrayType& branchRoot)
{ // CollapseBranches
// Get required arrays for hierarchical tree form data set
auto hierarchicalTreeSupernodes = hierarchicalTreeDataSet.GetField("Supernodes")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeSuperarcs = hierarchicalTreeDataSet.GetField("Superarcs")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeRegularNodeGlobalIds =
hierarchicalTreeDataSet.GetField("RegularNodeGlobalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeRegularNodeSortOrder =
hierarchicalTreeDataSet.GetField("RegularNodeSortOrder")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeRegular2Supernode = hierarchicalTreeDataSet.GetField("Regular2Supernode")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeWhichRound = hierarchicalTreeDataSet.GetField("WhichRound")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
// initialise the superarcs to be their own branch roots
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Initializing branch root with " << branchRoot.GetNumberOfValues() << " values.");
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(branchRoot.GetNumberOfValues()),
branchRoot);
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(branchRoot.GetNumberOfValues()), branchRoot);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Convert to superarc ID.");
// For each supernode, convert the best up into a superarc ID
{
vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
CollapseBranchesWorklet collapseBranchesWorklet;
auto findRegularByGlobal = hierarchicalTree->GetFindRegularByGlobal();
auto findSuperArcBetweenNodes = hierarchicalTree->GetFindSuperArcBetweenNodes();
this->Invoke(collapseBranchesWorklet, // the worklet
this->BestUpSupernode, // input
this->BestDownSupernode, // input
findRegularByGlobal, // input ExecutionObject
findSuperArcBetweenNodes, // input ExecutionObject
hierarchicalTree->Regular2Supernode, // input
hierarchicalTree->WhichRound, // input
vtkm::worklet::contourtree_distributed::FindRegularByGlobal findRegularByGlobal{
hierarchicalTreeRegularNodeSortOrder, hierarchicalTreeRegularNodeGlobalIds
};
vtkm::worklet::contourtree_distributed::FindSuperArcBetweenNodes findSuperArcBetweenNodes{
hierarchicalTreeSuperarcs
};
using vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
CollapseBranchesWorklet;
this->Invoke(CollapseBranchesWorklet{}, // the worklet
this->BestUpSupernode, // input
this->BestDownSupernode, // input
findRegularByGlobal, // input ExecutionObject
findSuperArcBetweenNodes, // input ExecutionObject
hierarchicalTreeRegular2Supernode, // input
hierarchicalTreeWhichRound, // input
branchRoot);
}
@ -477,112 +506,43 @@ void HierarchicalVolumetricBranchDecomposer<FieldType>::CollapseBranches(
for (vtkm::Id iteration = 0; iteration < nLogSteps; iteration++)
{ // per iteration
// loop through the vertices, updating
vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
CollapseBranchesPointerDoublingWorklet collapseBranchesPointerDoublingWorklet;
this->Invoke(collapseBranchesPointerDoublingWorklet, branchRoot);
using vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer::
CollapseBranchesPointerDoublingWorklet;
this->Invoke(CollapseBranchesPointerDoublingWorklet{}, branchRoot);
} // per iteration
} // CollapseBranches
// routine to print branches
template <typename FieldType>
std::string HierarchicalVolumetricBranchDecomposer<FieldType>::PrintBranches(
const vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>*
hierarchicalTree,
const vtkm::worklet::contourtree_augmented::IdArrayType& branchRoot)
{ // PrintBranches
// we want to dump out the branches as viewed by this rank.
// most of the processing will be external, so we keep this simple.
// For each end of the superarc, we print out value & global ID prefixed by global ID of the branch root
// The external processing will then sort them to construct segments (as usual) in the array
// then a post-process can find the first and last in each segment & print out the branch
// In order for the sort to work lexicographically, we need to print out in the following order:
// I Branch Root Global ID
// II Supernode Value
// III Supernode Global ID
std::stringstream resultStream;
// loop through the individual superarcs
auto hierarchicalTreeSuperarcsPortal = hierarchicalTree->Superarcs.ReadPortal();
auto hierarchicalTreeSupernodesPortal = hierarchicalTree->Supernodes.ReadPortal();
auto hierarchicalTreeRegularNodeGlobalIdsPortal =
hierarchicalTree->RegularNodeGlobalIds.ReadPortal();
auto hierarchicalTreeDataValuesPortal = hierarchicalTree->DataValues.ReadPortal();
auto branchRootPortal = branchRoot.ReadPortal();
// PrintBranches
// we want to dump out the branches as viewed by this rank.
// most of the processing will be external, so we keep this simple.
// For each end of the superarc, we print out value & global ID prefixed by global ID of the branch root
// The external processing will then sort them to construct segments (as usual) in the array
// then a post-process can find the first and last in each segment & print out the branch
// In order for the sort to work lexicographically, we need to print out in the following order:
// I Branch Root Global ID
// II Supernode Value
// III Supernode Global ID
for (vtkm::Id superarc = 0; superarc < hierarchicalTree->Superarcs.GetNumberOfValues();
superarc++)
{ // per superarc
// explicit test for root superarc / attachment points
if (vtkm::worklet::contourtree_augmented::NoSuchElement(
hierarchicalTreeSuperarcsPortal.Get(superarc)))
{
continue;
}
// now retrieve the branch root's global ID
vtkm::Id branchRootSuperId = branchRootPortal.Get(superarc);
vtkm::Id branchRootRegularId = hierarchicalTreeSupernodesPortal.Get(branchRootSuperId);
vtkm::Id branchRootGlobalId =
hierarchicalTreeRegularNodeGlobalIdsPortal.Get(branchRootRegularId);
// now retrieve the global ID & value for each end & output them
vtkm::Id superFromRegularId = hierarchicalTreeSupernodesPortal.Get(superarc);
vtkm::Id superFromGlobalId = hierarchicalTreeRegularNodeGlobalIdsPortal.Get(superFromRegularId);
FieldType superFromValue = hierarchicalTreeDataValuesPortal.Get(superFromRegularId);
resultStream << branchRootGlobalId << "\t" << superFromValue << "\t" << superFromGlobalId
<< std::endl;
// now retrieve the global ID & value for each end & output them
vtkm::Id superToRegularId = vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeSuperarcsPortal.Get(superarc));
vtkm::Id superToGlobalId = hierarchicalTreeRegularNodeGlobalIdsPortal.Get(superToRegularId);
FieldType superToValue = hierarchicalTreeDataValuesPortal.Get(superToRegularId);
resultStream << branchRootGlobalId << "\t" << superToValue << "\t" << superToGlobalId
<< std::endl;
} // per superarc
return resultStream.str();
} // PrintBranches
// routine to print branches
template <typename FieldType>
std::string HierarchicalVolumetricBranchDecomposer<FieldType>::PrintBranches(
const vtkm::cont::DataSet& ds)
{ // PrintBranches
// we want to dump out the branches as viewed by this rank.
// most of the processing will be external, so we keep this simple.
// For each end of the superarc, we print out value & global ID prefixed by global ID of the branch root
// The external processing will then sort them to construct segments (as usual) in the array
// then a post-process can find the first and last in each segment & print out the branch
// In order for the sort to work lexicographically, we need to print out in the following order:
// I Branch Root Global ID
// II Supernode Value
// III Supernode Global ID
std::stringstream resultStream;
// loop through the individual superarcs
auto hierarchicalTreeSuperarcsAH =
ds.GetField("Superarcs").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
// Note the following is a template to be called via cast-and-call
template <typename IdArrayHandleType, typename DataValueArrayHandleType>
std::string HierarchicalVolumetricBranchDecomposer::PrintBranches(
const IdArrayHandleType& hierarchicalTreeSuperarcsAH,
const IdArrayHandleType& hierarchicalTreeSupernodesAH,
const IdArrayHandleType& hierarchicalTreeRegularNodeGlobalIdsAH,
const DataValueArrayHandleType& hierarchicalTreeDataValuesAH,
const IdArrayHandleType& branchRootAH)
{
auto hierarchicalTreeSuperarcsPortal = hierarchicalTreeSuperarcsAH.ReadPortal();
vtkm::Id nSuperarcs = hierarchicalTreeSuperarcsAH.GetNumberOfValues();
auto hierarchicalTreeSupernodesPortal = ds.GetField("Supernodes")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto hierarchicalTreeSupernodesPortal = hierarchicalTreeSupernodesAH.ReadPortal();
auto hierarchicalTreeRegularNodeGlobalIdsPortal =
ds.GetField("RegularNodeGlobalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto hierarchicalTreeDataValuesPortal = ds.GetField("DataValues")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<FieldType>>()
.ReadPortal();
auto branchRootPortal = ds.GetField("BranchRoots")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
hierarchicalTreeRegularNodeGlobalIdsAH.ReadPortal();
auto hierarchicalTreeDataValuesPortal = hierarchicalTreeDataValuesAH.ReadPortal();
auto branchRootPortal = branchRootAH.ReadPortal();
std::stringstream resultStream;
// loop through the individual superarcs
for (vtkm::Id superarc = 0; superarc < nSuperarcs; superarc++)
{ // per superarc
@ -602,7 +562,7 @@ std::string HierarchicalVolumetricBranchDecomposer<FieldType>::PrintBranches(
// now retrieve the global ID & value for each end & output them
vtkm::Id superFromRegularId = hierarchicalTreeSupernodesPortal.Get(superarc);
vtkm::Id superFromGlobalId = hierarchicalTreeRegularNodeGlobalIdsPortal.Get(superFromRegularId);
FieldType superFromValue = hierarchicalTreeDataValuesPortal.Get(superFromRegularId);
vtkm::Float64 superFromValue = hierarchicalTreeDataValuesPortal.Get(superFromRegularId);
resultStream << branchRootGlobalId << "\t" << superFromValue << "\t" << superFromGlobalId
<< std::endl;
@ -610,18 +570,51 @@ std::string HierarchicalVolumetricBranchDecomposer<FieldType>::PrintBranches(
vtkm::Id superToRegularId = vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeSuperarcsPortal.Get(superarc));
vtkm::Id superToGlobalId = hierarchicalTreeRegularNodeGlobalIdsPortal.Get(superToRegularId);
FieldType superToValue = hierarchicalTreeDataValuesPortal.Get(superToRegularId);
vtkm::Float64 superToValue = hierarchicalTreeDataValuesPortal.Get(superToRegularId);
resultStream << branchRootGlobalId << "\t" << superToValue << "\t" << superToGlobalId
<< std::endl;
} // per superarc
return resultStream.str();
} // PrintBranches
inline std::string HierarchicalVolumetricBranchDecomposer::PrintBranches(
const vtkm::cont::DataSet& ds)
{
auto hierarchicalTreeSuperarcsAH =
ds.GetField("Superarcs").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeSupernodesAH =
ds.GetField("Supernodes").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeRegularNodeGlobalIdsAH =
ds.GetField("RegularNodeGlobalIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto hierarchicalTreeDataValuesData = ds.GetField("DataValues").GetData();
auto branchRootAH =
ds.GetField("BranchRoots").GetData().AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
std::string result;
hierarchicalTreeDataValuesData.CastAndCallForTypes<TypeListScalarAll, VTKM_DEFAULT_STORAGE_LIST>(
[&](const auto& hierarchicalTreeDataValuesAH) {
result = PrintBranches(hierarchicalTreeSuperarcsAH,
hierarchicalTreeSupernodesAH,
hierarchicalTreeRegularNodeGlobalIdsAH,
hierarchicalTreeDataValuesAH,
branchRootAH);
});
return result;
} // PrintBranches
// debug routine
template <typename FieldType>
std::string HierarchicalVolumetricBranchDecomposer<FieldType>::DebugPrint(std::string message,
const char* fileName,
long lineNum)
inline std::string HierarchicalVolumetricBranchDecomposer::DebugPrint(std::string message,
const char* fileName,
long lineNum)
{ // DebugPrint()
std::stringstream resultStream;
resultStream << "----------------------------------------" << std::endl;
@ -650,7 +643,7 @@ std::string HierarchicalVolumetricBranchDecomposer<FieldType>::DebugPrint(std::s
return resultStream.str();
} // DebugPrint()
} // namespace contourtree_distributed
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -53,7 +53,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -86,7 +86,7 @@ public:
}; // CollapseBranchesPointerDoublingWorklet
} // namespace hierarchical_augmenter
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm

@ -43,8 +43,8 @@
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_collapse_branches_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_collapse_branches_worklet_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_collapse_branches_worklet_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_collapse_branches_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
@ -53,7 +53,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -236,8 +236,8 @@ public:
}; // CollapseBranchesWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -43,8 +43,8 @@
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_combine_branch_decomposers_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_combine_branch_decomposers_worklet_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_combine_branch_decomposers_worklet_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_combine_branch_decomposers_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
@ -52,7 +52,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -89,8 +89,8 @@ public:
} // operator()()
}; // FindBestSupernodeWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -43,8 +43,8 @@
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_best_up_down_edge_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_best_up_down_edge_worklet_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_best_up_down_edge_worklet_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_best_up_down_edge_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
@ -53,7 +53,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -146,8 +146,8 @@ private:
}; // LocalBestUpDownByVolumeBestUpDownEdgeWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -43,8 +43,8 @@
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_init_superarc_list_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_init_superarc_list_worklet_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_init_superarc_list_worklet_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_localbestupdownbyvolume_init_superarc_list_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
@ -53,7 +53,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -96,8 +96,8 @@ public:
}; // LocalBestUpDownByVolumeInitSuperarcListWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -43,8 +43,8 @@
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_local_best_updown_by_volume_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_local_best_updown_by_volume_worklet_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_local_best_updown_by_volume_worklet_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_local_best_updown_by_volume_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
@ -53,7 +53,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -221,8 +221,8 @@ private:
}; // LocalBestUpDownByVolumeWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -60,8 +60,8 @@
//=======================================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_superarc_volumetric_comparator_indirect_globalid_comparator_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_volumetric_branch_decomposer_superarc_volumetric_comparator_indirect_globalid_comparator_h
#ifndef vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_superarc_volumetric_comparator_indirect_globalid_comparator_h
#define vtk_m_worklet_scalar_topology_hierarchical_volumetric_branch_decomposer_superarc_volumetric_comparator_indirect_globalid_comparator_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ExecutionObjectBase.h>
@ -71,7 +71,7 @@ namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
namespace scalar_topology
{
namespace hierarchical_volumetric_branch_decomposer
{
@ -240,7 +240,7 @@ private:
}; // SuperArcVolumetricComparatorIndirectGlobalIdComparator
} // namespace hierarchical_volumetric_branch_decomposer
} // namespace contourtree_distributed
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm

@ -293,7 +293,6 @@ inline vtkm::cont::PartitionedDataSet RunContourTreeDUniformDistributed(
!useMarchingCubes,
useMarchingCubes,
false, // no augmentationa
false, // no branch decmompostion
false, // no save dot
vtkm::cont::LogLevel::UserVerboseLast,
vtkm::cont::LogLevel::UserVerboseLast);

@ -53,6 +53,7 @@
#ifndef vtk_m_worklet_contourtree_augmented_not_no_such_element_predicate_h
#define vtk_m_worklet_contourtree_augmented_not_no_such_element_predicate_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm

@ -125,7 +125,7 @@ public:
// prints the contents of the BRACT for comparison with sweep and merge
std::string BoundaryTree::Print()
inline std::string BoundaryTree::Print()
{ // Print
// Use string steam to record text so the user can print it however they like
std::stringstream resultStream;
@ -270,7 +270,7 @@ std::string BoundaryTree::PrintGlobalDot(
} //PrintGlobalDot
// debug routine
void BoundaryTree::PrintContent(std::ostream& outStream) const
inline void BoundaryTree::PrintContent(std::ostream& outStream) const
{
vtkm::worklet::contourtree_augmented::PrintHeader(this->VertexIndex.GetNumberOfValues(),
outStream);

@ -11,15 +11,12 @@
set(headers
BoundaryTree.h
BoundaryTreeMaker.h
BranchDecompositionBlock.h
CombineHyperSweepBlockFunctor.h
ComputeDistributedBranchDecompositionFunctor.h
ComputeDistributedContourTreeFunctor.h
ContourTreeBlockData.h
DistributedContourTreeBlockData.h
HierarchicalAugmenter.h
HierarchicalAugmenterFunctor.h
HierarchicalVolumetricBranchDecomposer.h
HierarchicalContourTree.h
HierarchicalHyperSweeper.h
HyperSweepBlock.h
@ -39,4 +36,3 @@ add_subdirectory(tree_grafter)
add_subdirectory(hierarchical_contour_tree)
add_subdirectory(hierarchical_hyper_sweeper)
add_subdirectory(hierarchical_augmenter)
add_subdirectory(hierarchical_volumetric_branch_decomposer)

@ -1,238 +0,0 @@
//============================================================================
// 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_worklet_contourtree_distributed__h
#define vtk_m_worklet_contourtree_distributed__h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_distributed/BranchDecompositionBlock.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_volumetric_branch_decomposer/FindBestSupernodeWorklet.h>
// clang-format off
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/diy/diy.h>
VTKM_THIRDPARTY_POST_INCLUDE
// clang-format on
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_COMBINED_BLOCK_IDS
#endif
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
template <typename ContourTreeDataFieldType>
struct ComputeDistributedBranchDecompositionFunctor
{
void operator()(
vtkm::worklet::contourtree_distributed::BranchDecompositionBlock<ContourTreeDataFieldType>* b,
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) 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
auto& branchDecomposer = b->HierarchicalVolumetricBranchDecomposer;
std::vector<int> incoming;
rp.incoming(incoming);
for (const int ingid : incoming)
{
// NOTE/IMPORTANT: In each round we should have only one swap partner (despite for-loop here).
// If that assumption does not hold, it will break things.
// NOTE/IMPORTANT: This assumption only holds if the number of blocks is a power of two.
// 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);
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Combining local block " << b->GlobalBlockId << " with incomoing block "
<< incomingGlobalBlockId);
#endif
// Receive data from swap partner
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestUpVolume;
rp.dequeue(ingid, incomingBestUpVolume);
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestUpSupernode;
rp.dequeue(ingid, incomingBestUpSupernode);
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestDownVolume;
rp.dequeue(ingid, incomingBestDownVolume);
vtkm::cont::ArrayHandle<vtkm::Id> incomingBestDownSupernode;
rp.dequeue(ingid, incomingBestDownSupernode);
vtkm::Id prefixLength = vtkm::cont::ArrayGetValue(
0, b->HierarchicalContourTree.FirstSupernodePerIteration[rp.round() - 1]);
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Prefix length is " << prefixLength);
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(
incomingBestUpSupernode.GetNumberOfValues(), rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestUpSupernode", incomingBestUpSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestDownSupernode", incomingBestDownSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestUpVolume", incomingBestUpVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingBestDownVolume", incomingBestDownVolume, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
// NOTE: We are processing input data from the previous round, hence, get
// first supernide per ieteration from previous round
// Create 'views' to restrict worklet to relevant portion of arrays
auto bestUpVolumeView =
make_ArrayHandleView(branchDecomposer.BestUpVolume, 0, prefixLength);
auto bestUpSupernodeView =
make_ArrayHandleView(branchDecomposer.BestUpSupernode, 0, prefixLength);
auto bestDownVolumeView =
make_ArrayHandleView(branchDecomposer.BestDownVolume, 0, prefixLength);
auto bestDownSupernodeView =
make_ArrayHandleView(branchDecomposer.BestDownSupernode, 0, prefixLength);
// Check if swap partner knows a better up /down and update
vtkm::cont::Invoker invoke;
invoke(vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
FindBestSupernodeWorklet<true>{},
incomingBestUpVolume,
incomingBestUpSupernode,
bestUpVolumeView,
bestUpSupernodeView);
invoke(vtkm::worklet::contourtree_distributed::hierarchical_volumetric_branch_decomposer::
FindBestSupernodeWorklet<false>{},
incomingBestDownVolume,
incomingBestDownSupernode,
bestDownVolumeView,
bestDownSupernodeView);
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "After round " << rp.round() - 1);
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(
branchDecomposer.BestUpSupernode.GetNumberOfValues(), rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpSupernode", branchDecomposer.BestUpSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownSupernode", branchDecomposer.BestDownSupernode, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestUpVolume", branchDecomposer.BestUpVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BestDownVolume", branchDecomposer.BestDownVolume, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
}
}
for (int cc = 0; cc < rp.out_link().size(); ++cc)
{
auto target = rp.out_link().target(cc);
if (target.gid != selfid)
{
#ifdef DEBUG_PRINT_COMBINED_BLOCK_IDS
rp.enqueue(target, b->GlobalBlockId);
#endif
// Determine which portion of up/down volume/supernode to send
vtkm::Id prefixLength = vtkm::cont::ArrayGetValue(
0, b->HierarchicalContourTree.FirstSupernodePerIteration[rp.round()]);
// Create 'views' to restrict sending to relevant portion of arrays
auto bestUpVolumeView =
make_ArrayHandleView(branchDecomposer.BestUpVolume, 0, prefixLength);
auto bestUpSupernodeView =
make_ArrayHandleView(branchDecomposer.BestUpSupernode, 0, prefixLength);
auto bestDownVolumeView =
make_ArrayHandleView(branchDecomposer.BestDownVolume, 0, prefixLength);
auto bestDownSupernodeView =
make_ArrayHandleView(branchDecomposer.BestDownSupernode, 0, prefixLength);
// TODO/FIXME: Check if it is possible to send a portion of the arrays
// without copy. enqueue does not accept ArrayHandleView as input as it
// is not trivially copyable
vtkm::cont::ArrayHandle<vtkm::Id> sendBestUpVolume;
vtkm::cont::Algorithm::Copy(bestUpVolumeView, sendBestUpVolume);
vtkm::cont::ArrayHandle<vtkm::Id> sendBestUpSupernode;
vtkm::cont::Algorithm::Copy(bestUpSupernodeView, sendBestUpSupernode);
vtkm::cont::ArrayHandle<vtkm::Id> sendBestDownVolume;
vtkm::cont::Algorithm::Copy(bestDownVolumeView, sendBestDownVolume);
vtkm::cont::ArrayHandle<vtkm::Id> sendBestDownSupernode;
vtkm::cont::Algorithm::Copy(bestDownSupernodeView, sendBestDownSupernode);
rp.enqueue(target, sendBestUpVolume);
rp.enqueue(target, sendBestUpSupernode);
rp.enqueue(target, sendBestDownVolume);
rp.enqueue(target, sendBestDownSupernode);
}
}
}
};
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -71,6 +71,8 @@
#define VOLUME_PRINT_WIDTH 8
#include <vtkm/Types.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/worklet/contourtree_augmented/ContourTree.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_augmented/meshtypes/ContourTreeMesh.h>
@ -162,6 +164,8 @@ public:
vtkm::worklet::contourtree_augmented::IdArrayType NumIterations;
/// vectors tracking the segments used in each iteration of the hypersweep
// TODO/FIXME: Consider using ArrayHandleGroupVecVariable instead of an STL vector
// of ArrayHandles. (Though that may be a bit tricky with dynamic resizing.)
std::vector<vtkm::worklet::contourtree_augmented::IdArrayType> FirstSupernodePerIteration;
std::vector<vtkm::worklet::contourtree_augmented::IdArrayType> FirstHypernodePerIteration;
@ -242,6 +246,20 @@ public:
const vtkm::worklet::contourtree_augmented::IdArrayType& intrinsicVolume,
const vtkm::worklet::contourtree_augmented::IdArrayType& dependentVolume);
// Helper function to convert an STL vector of VTK-m arrays into components and
// group arrays (for packing into a vtkm::cont::DataSet and using with an
// ArrayHandleGroupVecVariable for access)
// TODO/FIXME: Ultimately, we should get rid of the STL arrays and use an
// ArrayHandleGroupVecVariable in this class.
VTKM_CONT
static void ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray(
const std::vector<vtkm::worklet::contourtree_augmented::IdArrayType>& inputVec,
vtkm::worklet::contourtree_augmented::IdArrayType& outputComponents,
vtkm::cont::ArrayHandle<vtkm::Id>& outputOffsets);
VTKM_CONT
void AddToVTKMDataSet(vtkm::cont::DataSet& ds) const;
private:
/// Used internally to Invoke worklets
vtkm::cont::Invoker Invoke;
@ -946,9 +964,111 @@ std::string HierarchicalContourTree<FieldType>::DumpVolumes(
return outStream.str();
} // DumpVolumes()
template <typename FieldType>
void HierarchicalContourTree<FieldType>::ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray(
const std::vector<vtkm::worklet::contourtree_augmented::IdArrayType>& inputVec,
vtkm::worklet::contourtree_augmented::IdArrayType& outputComponents,
vtkm::cont::ArrayHandle<vtkm::Id>& outputOffsets)
{
// Compute num components vector
vtkm::cont::ArrayHandle<vtkm::IdComponent> numComponents;
numComponents.Allocate(inputVec.size());
auto numComponentsWritePortal = numComponents.WritePortal();
for (vtkm::Id i = 0; i < static_cast<vtkm::Id>(inputVec.size()); ++i)
{
numComponentsWritePortal.Set(i, inputVec[i].GetNumberOfValues());
}
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray: Converting to offsets");
// Convert to offsets and store in output array
vtkm::Id componentsArraySize;
vtkm::cont::ConvertNumComponentsToOffsets(numComponents, outputOffsets, componentsArraySize);
// Copy data to components array
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray: Copying");
auto outputOffsetsReadPortal = outputOffsets.ReadPortal();
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Allocating to " << componentsArraySize);
outputComponents.Allocate(componentsArraySize);
auto numComponentsReadPortal = numComponents.ReadPortal();
for (vtkm::Id i = 0; i < static_cast<vtkm::Id>(inputVec.size()); ++i)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Creating view from " << outputOffsetsReadPortal.Get(i) << " size "
<< numComponentsReadPortal.Get(i));
auto outputView = vtkm::cont::make_ArrayHandleView(
outputComponents, outputOffsetsReadPortal.Get(i), numComponentsReadPortal.Get(i));
vtkm::cont::ArrayCopy(inputVec[i], outputView);
}
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray: Exit");
}
template <typename FieldType>
void HierarchicalContourTree<FieldType>::AddToVTKMDataSet(vtkm::cont::DataSet& ds) const
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "AddToVTKMDataSet");
// Create data set from output
vtkm::cont::Field regularNodeGlobalIdsField(
"RegularNodeGlobalIds", vtkm::cont::Field::Association::WholeMesh, this->RegularNodeGlobalIds);
ds.AddField(regularNodeGlobalIdsField);
vtkm::cont::Field dataValuesField(
"DataValues", vtkm::cont::Field::Association::WholeMesh, this->DataValues);
ds.AddField(dataValuesField);
vtkm::cont::Field regularNodeSortOrderField(
"RegularNodeSortOrder", vtkm::cont::Field::Association::WholeMesh, this->RegularNodeSortOrder);
ds.AddField(regularNodeSortOrderField);
vtkm::cont::Field regular2SupernodeField(
"Regular2Supernode", vtkm::cont::Field::Association::WholeMesh, this->Regular2Supernode);
ds.AddField(regular2SupernodeField);
vtkm::cont::Field superparentsField(
"Superparents", vtkm::cont::Field::Association::WholeMesh, this->Superparents);
ds.AddField(superparentsField);
vtkm::cont::Field supernodesField(
"Supernodes", vtkm::cont::Field::Association::WholeMesh, this->Supernodes);
ds.AddField(supernodesField);
vtkm::cont::Field superarcsField(
"Superarcs", vtkm::cont::Field::Association::WholeMesh, this->Superarcs);
ds.AddField(superarcsField);
vtkm::cont::Field hyperparentsField(
"Hyperparents", vtkm::cont::Field::Association::WholeMesh, this->Hyperparents);
ds.AddField(hyperparentsField);
vtkm::cont::Field super2HypernodeField(
"Super2Hypernode", vtkm::cont::Field::Association::WholeMesh, this->Super2Hypernode);
ds.AddField(super2HypernodeField);
vtkm::cont::Field whichRoundField(
"WhichRound", vtkm::cont::Field::Association::WholeMesh, this->WhichRound);
ds.AddField(whichRoundField);
vtkm::cont::Field whichIterationField(
"WhichIteration", vtkm::cont::Field::Association::WholeMesh, this->WhichIteration);
ds.AddField(whichIterationField);
// TODO/FIXME: See what other fields we need to add
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Calling ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray");
vtkm::worklet::contourtree_augmented::IdArrayType firstSupernodePerIterationComponents;
vtkm::cont::ArrayHandle<vtkm::Id> firstSupernodePerIterationOffsets;
ConvertSTLVecOfHandlesToVTKMComponentsAndOffsetsArray(FirstSupernodePerIteration,
firstSupernodePerIterationComponents,
firstSupernodePerIterationOffsets);
vtkm::cont::Field firstSupernodePerIterationComponentsField(
"FirstSupernodePerIterationComponents",
vtkm::cont::Field::Association::WholeMesh,
firstSupernodePerIterationComponents);
ds.AddField(firstSupernodePerIterationComponentsField);
vtkm::cont::Field firstSupernodePerIterationOffsetsField(
"FirstSupernodePerIterationOffsets",
vtkm::cont::Field::Association::WholeMesh,
firstSupernodePerIterationOffsets);
ds.AddField(firstSupernodePerIterationOffsetsField);
// TODO/FIXME: It seems we may only need the counts for the first iteration, so check, which
// information we actually need.
}
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif