Ported distributed contour tree prototype to vtk-m

This commit is contained in:
Gunther H. Weber 2020-09-15 10:57:20 -07:00
parent ea3f101953
commit f433790a17
80 changed files with 11132 additions and 936 deletions

@ -854,7 +854,7 @@ int main(int argc, char* argv[])
ctaug_ns::EdgePairArray saddlePeak;
ctaug_ns::ProcessContourTree::CollectSortedSuperarcs(
filter.GetContourTree(), filter.GetSortOrder(), saddlePeak);
ctaug_ns::PrintEdgePairArray(saddlePeak);
ctaug_ns::PrintEdgePairArrayColumnLayout(saddlePeak, std::cout);
}
#ifdef WITH_MPI

@ -50,7 +50,7 @@
## Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
## Oliver Ruebel (LBNL)
##==============================================================================
cmake_minimum_required(VERSION 3.8...3.15 FATAL_ERROR)
cmake_minimum_required(VERSION 3.12...3.15 FATAL_ERROR)
# Find the VTK-m package
find_package(VTKm REQUIRED QUIET)
@ -60,11 +60,11 @@ find_package(VTKm REQUIRED QUIET)
####################################
if (VTKm_ENABLE_MPI)
add_executable(ContourTree_Distributed ContourTreeApp.cxx)
target_link_libraries(ContourTree_Distributed vtkm_filter MPI::MPI_CXX)
target_link_libraries(ContourTree_Distributed vtkm_filter vtkm_io MPI::MPI_CXX)
vtkm_add_target_information(ContourTree_Distributed
MODIFY_CUDA_FLAGS
DEVICE_SOURCES ContourTreeApp.cxx)
target_compile_definitions(ContourTree_Distributed PRIVATE)
target_compile_definitions(ContourTree_Distributed PRIVATE "WITH_MPI")
option (VTKM_EXAMPLE_CONTOURTREE_ENABLE_DEBUG_PRINT Off)
mark_as_advanced(VTKM_EXAMPLE_CONTOURTREE_ENABLE_DEBUG_PRINT)

@ -65,9 +65,12 @@
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/cont/DataSetFieldAdd.h>
#include <vtkm/cont/DeviceAdapterTag.h>
#include <vtkm/cont/Initialize.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/io/BOVDataSetReader.h>
#include <vtkm/filter/ContourTreeUniformDistributed.h>
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
#include <vtkm/worklet/contourtree_augmented/ProcessContourTree.h>
@ -90,11 +93,12 @@ VTKM_THIRDPARTY_POST_INCLUDE
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <cstdio>
#include <string>
#include <utility>
#include <vector>
#define PRESPLIT_FILE
using ValueType = vtkm::Float32;
namespace ctaug_ns = vtkm::worklet::contourtree_augmented;
@ -126,7 +130,7 @@ public:
}
else
{
return (it - this->mCLOptions.begin());
return static_cast<vtkm::Id>(it - this->mCLOptions.begin());
}
}
@ -166,8 +170,6 @@ int main(int argc, char* argv[])
int rank, size;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &size);
int numBlocks = size;
int blocksPerRank = 1;
// initialize vtkm-m (e.g., logging via -v and device via the -d option)
vtkm::cont::InitializeOptions vtkm_initialize_options =
@ -217,33 +219,54 @@ int main(int argc, char* argv[])
}
#endif
if (rank == 0 && (argc < 2 || parser.hasOption("--help") || parser.hasOption("-h")))
int numBlocks = size;
int blocksPerRank = 1;
if (parser.hasOption("--numBlocks"))
{
std::cout << "ContourTreeAugmented <options> <fileName>" << std::endl;
std::cout << std::endl;
std::cout << "<fileName> Name of the input data file." << std::endl;
std::cout << "The file is expected to be ASCII with either: " << std::endl;
std::cout << " - xdim ydim integers for 2D or" << std::endl;
std::cout << " - xdim ydim zdim integers for 3D" << std::endl;
std::cout << "followed by vector data last dimension varying fastest" << std::endl;
std::cout << std::endl;
std::cout << "----------------------------- VTKM Options -----------------------------"
<< std::endl;
std::cout << vtkm_config.Usage << std::endl;
std::cout << std::endl;
std::cout << "------------------------- Contour Tree Options -------------------------"
<< std::endl;
std::cout << "Options: (Bool options are give via int, i.e. =0 for False and =1 for True)"
<< std::endl;
std::cout << "--mc Use marching cubes interpolation for contour tree calculation. "
"(Default=False)"
<< std::endl;
#ifdef ENABLE_SET_NUM_THREADS
std::cout << "--numThreads Specifiy the number of threads to use. Available only with TBB."
<< std::endl;
#endif
std::cout << std::endl;
numBlocks = std::stoi(parser.getOption("--numBlocks"));
if (numBlocks % size == 0)
blocksPerRank = numBlocks / size;
else
{
std::cerr << "Error: Number of blocks must be divisible by number of ranks." << std::endl;
MPI_Finalize();
return EXIT_FAILURE;
}
}
if (argc < 2 || parser.hasOption("--help") || parser.hasOption("-h"))
{
if (rank == 0)
{
std::cout << "ContourTreeAugmented <options> <fileName>" << std::endl;
std::cout << std::endl;
std::cout << "<fileName> Name of the input data file." << std::endl;
std::cout << "The file is expected to be ASCII with either: " << std::endl;
std::cout << " - xdim ydim integers for 2D or" << std::endl;
std::cout << " - xdim ydim zdim integers for 3D" << std::endl;
std::cout << "followed by vector data last dimension varying fastest" << std::endl;
std::cout << std::endl;
std::cout << "----------------------------- VTKM Options -----------------------------"
<< std::endl;
std::cout << vtkm_config.Usage << std::endl;
std::cout << std::endl;
std::cout << "------------------------- Contour Tree Options -------------------------"
<< std::endl;
std::cout << "Options: (Bool options are give via int, i.e. =0 for False and =1 for True)"
<< std::endl;
std::cout
<< "--mc Use marching cubes interpolation for contour tree calculation. "
"(Default=False)"
<< std::endl;
#ifdef ENABLE_SET_NUM_THREADS
std::cout
<< "--numThreads Specifiy the number of threads to use. Available only with TBB."
<< std::endl;
#endif
std::cout << "--numBlocks Number of blocks to load. (Default=number of MPI ranks.)"
<< std::endl;
std::cout << std::endl;
}
MPI_Finalize();
return EXIT_SUCCESS;
}
@ -265,11 +288,11 @@ int main(int argc, char* argv[])
vtkm::Float64 startUpTime = currTime - prevTime;
prevTime = currTime;
// Redirect stdout to file if we are using MPI with Debugging
#ifdef DEBUG_PRINT
// Redirect stdout to file if we are using MPI with Debugging
//#ifdef DEBUG_PRINT
// From https://www.unix.com/302983597-post2.html
char* cstr_filename = new char[15];
snprintf(cstr_filename, sizeof(filename), "cout_%d.log", rank);
char cstr_filename[255];
std::snprintf(cstr_filename, sizeof(cstr_filename), "cout_%d.log", rank);
int out = open(cstr_filename, O_RDWR | O_CREAT | O_APPEND, 0600);
if (-1 == out)
{
@ -277,13 +300,15 @@ int main(int argc, char* argv[])
return 255;
}
snprintf(cstr_filename, sizeof(cstr_filename), "cerr_%d.log", rank);
/*
std::snprintf(cstr_filename, sizeof(cstr_filename), "cerr_%d.log", rank);
int err = open(cstr_filename, O_RDWR | O_CREAT | O_APPEND, 0600);
if (-1 == err)
{
perror("opening cerr.log");
return 255;
}
*/
int save_out = dup(fileno(stdout));
int save_err = dup(fileno(stderr));
@ -293,37 +318,240 @@ int main(int argc, char* argv[])
perror("cannot redirect stdout");
return 255;
}
if (-1 == dup2(err, fileno(stderr)))
//if (-1 == dup2(err, fileno(stderr)))
if (-1 == dup2(out, fileno(stderr)))
{
perror("cannot redirect stderr");
return 255;
}
delete[] cstr_filename;
#endif
//#endif
///////////////////////////////////////////////
// Read the input data
///////////////////////////////////////////////
std::ifstream inFile(filename);
if (inFile.bad())
return 0;
vtkm::Float64 dataReadTime = 0;
vtkm::Float64 buildDatasetTime = 0;
std::vector<vtkm::Float32>::size_type nDims = 0;
// Read the dimensions of the mesh, i.e,. number of elementes in x, y, and z
std::vector<std::size_t> dims;
std::string line;
getline(inFile, line);
std::istringstream linestream(line);
std::size_t dimVertices;
while (linestream >> dimVertices)
#ifdef PRESPLIT_FILE
// Multi-block dataset for multi-block DIY-paralle processing
vtkm::cont::PartitionedDataSet useDataSet;
// Domain decomposition information for DIY/contour tree
vtkm::Id3 globalSize;
vtkm::Id3 blocksPerDim;
vtkm::cont::ArrayHandle<vtkm::Id3> localBlockIndices;
vtkm::cont::ArrayHandle<vtkm::Id3> localBlockOrigins;
vtkm::cont::ArrayHandle<vtkm::Id3> localBlockSizes;
localBlockIndices.Allocate(blocksPerRank);
localBlockOrigins.Allocate(blocksPerRank);
localBlockSizes.Allocate(blocksPerRank);
auto localBlockIndicesPortal = localBlockIndices.WritePortal();
auto localBlockOriginsPortal = localBlockOrigins.WritePortal();
auto localBlockSizesPortal = localBlockSizes.WritePortal();
for (int blockNo = 0; blockNo < blocksPerRank; ++blockNo)
{
dims.push_back(dimVertices);
// Translate pattern into filename for this block
char block_filename[256];
snprintf(block_filename,
sizeof(block_filename),
filename.c_str(),
static_cast<int>(rank * blocksPerRank + blockNo));
std::cout << "Reading file " << block_filename << std::endl;
// Open file
std::ifstream inFile(block_filename);
if (!inFile.is_open() || inFile.bad())
{
std::cerr << "Error: Couldn't open file " << block_filename << std::endl;
MPI_Finalize();
return EXIT_FAILURE;
}
// Read header with dimensions
std::string line;
std::string tag;
vtkm::Id dimVertices;
getline(inFile, line);
std::istringstream global_extents_stream(line);
global_extents_stream >> tag;
if (tag != "#GLOBAL_EXTENTS")
{
std::cerr << "Error: Expected #GLOBAL_EXTENTS, got " << tag << std::endl;
MPI_Finalize();
return EXIT_FAILURE;
}
std::vector<vtkm::Id> global_extents;
while (global_extents_stream >> dimVertices)
global_extents.push_back(dimVertices);
// Swap dimensions so that they are from fastest to slowest growing
// dims[0] -> col; dims[1] -> row, dims[2] ->slice
std::swap(global_extents[0], global_extents[1]);
if (blockNo == 0)
{ // First block: Set globalSize
globalSize =
vtkm::Id3{ static_cast<vtkm::Id>(global_extents[0]),
static_cast<vtkm::Id>(global_extents[1]),
static_cast<vtkm::Id>(global_extents.size() > 2 ? global_extents[2] : 1) };
}
else
{ // All other blocks: Consistency check of globalSize
if (globalSize !=
vtkm::Id3{ static_cast<vtkm::Id>(global_extents[0]),
static_cast<vtkm::Id>(global_extents[1]),
static_cast<vtkm::Id>(global_extents.size() > 2 ? global_extents[2] : 1) })
{
std::cerr << "Error: Global extents mismatch between blocks!" << std::endl;
MPI_Finalize();
return EXIT_FAILURE;
}
}
getline(inFile, line);
std::istringstream offset_stream(line);
offset_stream >> tag;
if (tag != "#OFFSET")
{
std::cerr << "Error: Expected #OFFSET, got " << tag << std::endl;
MPI_Finalize();
return EXIT_FAILURE;
}
std::vector<vtkm::Id> offset;
while (offset_stream >> dimVertices)
offset.push_back(dimVertices);
// Swap dimensions so that they are from fastest to slowest growing
// dims[0] -> col; dims[1] -> row, dims[2] ->slice
std::swap(offset[0], offset[1]);
getline(inFile, line);
std::istringstream linestream(line);
std::vector<vtkm::Id> dims;
while (linestream >> dimVertices)
{
dims.push_back(dimVertices);
}
std::cout << dims.size() << " " << global_extents.size() << " " << offset.size() << std::endl;
if (dims.size() != global_extents.size() || dims.size() != offset.size())
{
std::cerr << "Error: Dimension mismatch" << std::endl;
MPI_Finalize();
return EXIT_FAILURE;
}
// Swap dimensions so that they are from fastest to slowest growing
// dims[0] -> col; dims[1] -> row, dims[2] ->slice
std::swap(dims[0], dims[1]);
// Compute the number of vertices, i.e., xdim * ydim * zdim
nDims = static_cast<unsigned short>(dims.size());
std::size_t numVertices = static_cast<std::size_t>(
std::accumulate(dims.begin(), dims.end(), std::size_t(1), std::multiplies<std::size_t>()));
// Check for fatal input errors
// Check that the number of dimensiosn is either 2D or 3D
bool invalidNumDimensions = (nDims < 2 || nDims > 3);
// Log any errors if found on rank 0
VTKM_LOG_IF_S(vtkm::cont::LogLevel::Error,
invalidNumDimensions && (rank == 0),
"The input mesh is " << nDims
<< "D. "
"The input data must be either 2D or 3D.");
// If we found any errors in the setttings than finalize MPI and exit the execution
if (invalidNumDimensions)
{
MPI_Finalize();
return EXIT_FAILURE;
}
// Read data
std::vector<ValueType> values(numVertices);
if (filename.compare(filename.length() - 5, 5, ".bdem") == 0)
{
inFile.read(reinterpret_cast<char*>(values.data()),
static_cast<std::streamsize>(numVertices * sizeof(ValueType)));
}
else
{
for (std::size_t vertex = 0; vertex < numVertices; ++vertex)
{
inFile >> values[vertex];
}
}
// Create vtk-m data set
vtkm::cont::DataSetBuilderUniform dsb;
vtkm::cont::DataSet ds;
if (nDims == 2)
{
const vtkm::Id2 v_dims{
static_cast<vtkm::Id>(dims[0]),
static_cast<vtkm::Id>(dims[1]),
};
const vtkm::Vec<ValueType, 2> v_origin{ static_cast<ValueType>(offset[0]),
static_cast<ValueType>(offset[1]) };
const vtkm::Vec<ValueType, 2> v_spacing{ 1, 1 };
ds = dsb.Create(v_dims, v_origin, v_spacing);
}
else
{
VTKM_ASSERT(nDims == 3);
const vtkm::Id3 v_dims{ static_cast<vtkm::Id>(dims[0]),
static_cast<vtkm::Id>(dims[1]),
static_cast<vtkm::Id>(dims[2]) };
const vtkm::Vec<ValueType, 3> v_origin{ static_cast<ValueType>(offset[0]),
static_cast<ValueType>(offset[1]),
static_cast<ValueType>(offset[2]) };
vtkm::Vec<ValueType, 3> v_spacing(1, 1, 1);
ds = dsb.Create(v_dims, v_origin, v_spacing);
}
ds.AddPointField("values", values);
// and add to partition
useDataSet.AppendPartition(ds);
// FIXME: Hack: Approximate block index (better to put this into file)
localBlockIndicesPortal.Set(
blockNo,
vtkm::Id3{ static_cast<vtkm::Id>(
std::ceil(static_cast<float>(offset[0]) / static_cast<float>(dims[0]))),
static_cast<vtkm::Id>(
std::ceil(static_cast<float>(offset[1]) / static_cast<float>(dims[1]))),
static_cast<vtkm::Id>(nDims == 3 ? std::ceil(static_cast<float>(offset[2]) /
static_cast<float>(dims[2]))
: 0) });
localBlockOriginsPortal.Set(blockNo,
vtkm::Id3{ static_cast<vtkm::Id>(offset[0]),
static_cast<vtkm::Id>(offset[1]),
static_cast<vtkm::Id>(nDims == 3 ? offset[2] : 0) });
localBlockSizesPortal.Set(blockNo,
vtkm::Id3{ static_cast<vtkm::Id>(dims[0]),
static_cast<vtkm::Id>(dims[1]),
static_cast<vtkm::Id>(nDims == 3 ? dims[2] : 0) });
if (blockNo == 0)
{
// FIXME: Hack: Approximate blocks per dim
blocksPerDim = vtkm::Id3{
static_cast<vtkm::Id>(
std::ceil(static_cast<float>(global_extents[0]) / static_cast<float>(dims[0]))),
static_cast<vtkm::Id>(
std::ceil(static_cast<float>(global_extents[1]) / static_cast<float>(dims[1]))),
static_cast<vtkm::Id>(nDims == 3 ? std::ceil(static_cast<float>(global_extents[2]) /
static_cast<float>(dims[2]))
: 1)
};
}
}
// Compute the number of vertices, i.e., xdim * ydim * zdim
unsigned short nDims = static_cast<unsigned short>(dims.size());
std::size_t numVertices = static_cast<std::size_t>(
std::accumulate(dims.begin(), dims.end(), std::size_t(1), std::multiplies<std::size_t>()));
currTime = totalTime.GetElapsedTime();
dataReadTime = currTime - prevTime;
prevTime = currTime;
// Print the mesh metadata
if (rank == 0)
@ -331,50 +559,106 @@ int main(int argc, char* argv[])
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
std::endl
<< " ---------------- Input Mesh Properties --------------" << std::endl
<< " Number of dimensions: " << nDims << std::endl
<< " Number of mesh vertices: " << numVertices << std::endl);
<< " Number of dimensions: " << nDims << std::endl);
}
// Check for fatal input errors
// Check the the number of dimensiosn is either 2D or 3D
bool invalidNumDimensions = (nDims < 2 || nDims > 3);
// Check if marching cubes is enabled for non 3D data
bool invalidMCOption = (useMarchingCubes && nDims != 3);
// Log any errors if found on rank 0
VTKM_LOG_IF_S(vtkm::cont::LogLevel::Error,
invalidNumDimensions && (rank == 0),
"The input mesh is " << nDims
<< "D. "
"The input data must be either 2D or 3D.");
VTKM_LOG_IF_S(vtkm::cont::LogLevel::Error,
invalidMCOption && (rank == 0),
"The input mesh is "
<< nDims << "D. "
<< "Contour tree using marching cubes is only supported for 3D data.");
// If we found any errors in the setttings than finalize MPI and exit the execution
if (invalidNumDimensions || invalidMCOption)
#else
vtkm::cont::DataSet inDataSet;
std::vector<ValueType> values;
std::vector<vtkm::Id> dims;
if (filename.compare(filename.length() - 3, 3, "bov") == 0)
{
std::cout << "Reading BOV file" << std::endl;
vtkm::io::BOVDataSetReader reader(filename);
inDataSet = reader.ReadDataSet();
nDims = 3;
currTime = totalTime.GetElapsedTime();
dataReadTime = currTime - prevTime;
prevTime = currTime;
// Copy the data into the values array so we can construct a multiblock dataset
// TODO All we should need to do to implement BOV support is to copy the values
// in the values vector and copy the dimensions in the dims vector
vtkm::Id nRows, nCols, nSlices;
vtkm::worklet::contourtree_augmented::GetRowsColsSlices temp;
temp(inDataSet.GetCellSet(), nRows, nCols, nSlices);
dims[0] = nRows;
dims[1] = nCols;
dims[2] = nSlices;
auto tempField = inDataSet.GetField("values").GetData();
values.resize(static_cast<std::size_t>(tempField.GetNumberOfValues()));
auto tempFieldHandle = tempField.AsVirtual<ValueType>().ReadPortal();
for (vtkm::Id i = 0; i < tempField.GetNumberOfValues(); i++)
{
values[static_cast<std::size_t>(i)] = static_cast<ValueType>(tempFieldHandle.Get(i));
}
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"BOV reader not yet support in MPI mode by this example");
MPI_Finalize();
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
// Read data
std::vector<ValueType> values(numVertices);
for (std::size_t vertex = 0; vertex < numVertices; ++vertex)
else // Read ASCII data input
{
inFile >> values[vertex];
std::cout << "Reading ASCII file" << std::endl;
std::ifstream inFile(filename);
if (inFile.bad())
return 0;
// Read the dimensions of the mesh, i.e,. number of elementes in x, y, and z
std::string line;
getline(inFile, line);
std::istringstream linestream(line);
vtkm::Id dimVertices;
while (linestream >> dimVertices)
{
dims.push_back(dimVertices);
}
// Compute the number of vertices, i.e., xdim * ydim * zdim
nDims = static_cast<unsigned short>(dims.size());
std::size_t numVertices = static_cast<std::size_t>(
std::accumulate(dims.begin(), dims.end(), std::size_t(1), std::multiplies<std::size_t>()));
// Check the the number of dimensiosn is either 2D or 3D
bool invalidNumDimensions = (nDims < 2 || nDims > 3);
// Log any errors if found on rank 0
VTKM_LOG_IF_S(vtkm::cont::LogLevel::Error,
invalidNumDimensions && (rank == 0),
"The input mesh is " << nDims << "D. The input data must be either 2D or 3D.");
// If we found any errors in the setttings than finalize MPI and exit the execution
if (invalidNumDimensions)
{
MPI_Finalize();
return EXIT_FAILURE;
}
// Read data
values.resize(numVertices);
for (std::size_t vertex = 0; vertex < numVertices; ++vertex)
{
inFile >> values[vertex];
}
// finish reading the data
inFile.close();
currTime = totalTime.GetElapsedTime();
dataReadTime = currTime - prevTime;
prevTime = currTime;
} // END ASCII Read
// Print the mesh metadata
if (rank == 0)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
std::endl
<< " ---------------- Input Mesh Properties --------------" << std::endl
<< " Number of dimensions: " << nDims);
}
// finish reading the data
inFile.close();
currTime = totalTime.GetElapsedTime();
vtkm::Float64 dataReadTime = currTime - prevTime;
prevTime = currTime;
vtkm::cont::DataSetBuilderUniform dsb;
// Create a multi-block dataset for multi-block DIY-paralle processing
vtkm::cont::PartitionedDataSet inDataSet; // Partitioned variant of the input dataset
vtkm::cont::PartitionedDataSet useDataSet; // Partitioned variant of the input dataset
vtkm::Id3 blocksPerDim =
nDims == 3 ? vtkm::Id3(1, 1, numBlocks) : vtkm::Id3(1, numBlocks, 1); // Decompose the data into
vtkm::Id3 globalSize = nDims == 3 ? vtkm::Id3(static_cast<vtkm::Id>(dims[0]),
@ -428,6 +712,7 @@ int main(int argc, char* argv[])
}
vtkm::Id currBlockSize = (vtkm::Id)((blockEnd - blockStart) / blockSliceSize);
vtkm::cont::DataSetBuilderUniform dsb;
vtkm::cont::DataSet ds;
// 2D data
@ -468,31 +753,59 @@ int main(int argc, char* argv[])
std::vector<vtkm::Float32> subValues((values.begin() + blockStart),
(values.begin() + blockEnd));
//vtkm::cont::DataSetFieldAdd dsf;
ds.AddPointField("values", subValues);
inDataSet.AppendPartition(ds);
useDataSet.AppendPartition(ds);
}
}
#endif
// Check if marching cubes is enabled for non 3D data
bool invalidMCOption = (useMarchingCubes && nDims != 3);
VTKM_LOG_IF_S(vtkm::cont::LogLevel::Error,
invalidMCOption && (rank == 0),
"The input mesh is "
<< nDims << "D. "
<< "Contour tree using marching cubes is only supported for 3D data.");
// If we found any errors in the setttings than finalize MPI and exit the execution
if (invalidMCOption)
{
MPI_Finalize();
return EXIT_FAILURE;
}
currTime = totalTime.GetElapsedTime();
vtkm::Float64 buildDatasetTime = currTime - prevTime;
buildDatasetTime = currTime - prevTime;
prevTime = currTime;
// Convert the mesh of values into contour tree, pairs of vertex ids
vtkm::filter::ContourTreeUniformDistributed filter(useMarchingCubes);
filter.SetSpatialDecomposition(
blocksPerDim, globalSize, localBlockIndices, localBlockOrigins, localBlockSizes);
vtkm::filter::ContourTreeUniformDistributed filter(blocksPerDim,
globalSize,
localBlockIndices,
localBlockOrigins,
localBlockSizes,
useMarchingCubes);
filter.SetActiveField("values");
// Execute the contour tree analysis. NOTE: If MPI is used the result will be
// a vtkm::cont::PartitionedDataSet instead of a vtkm::cont::DataSet
auto result = filter.Execute(inDataSet);
auto result = filter.Execute(useDataSet);
currTime = totalTime.GetElapsedTime();
vtkm::Float64 computeContourTreeTime = currTime - prevTime;
prevTime = currTime;
//std::cout << std::flush; close(out);
//std::cerr << std::flush; close(err);
std::cout << std::flush;
std::cerr << std::flush;
close(out);
dup2(save_out, fileno(stdout));
dup2(save_err, fileno(stderr));
close(save_out);
close(save_err);
// Force a simple round-robin on the ranks for the summary prints. Its not perfect for MPI but
// it works well enough to sort the summaries from the ranks for small-scale debugging.
if (rank > 0)

@ -57,7 +57,11 @@
#include <vtkm/Types.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/worklet/contourtree_augmented/ContourTree.h>
#include <vtkm/worklet/contourtree_distributed/MultiBlockContourTreeHelper.h>
#include <vtkm/worklet/contourtree_augmented/DataSetMesh.h>
#include <vtkm/worklet/contourtree_distributed/BoundaryTree.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/InteriorForest.h>
#include <vtkm/worklet/contourtree_distributed/SpatialDecomposition.h>
#include <vtkm/filter/FilterField.h>
@ -90,15 +94,6 @@ public:
using SupportedTypes = vtkm::TypeListScalarAll;
///
/// Create the contour tree filter
/// @param[in] useMarchingCubes Boolean indicating whether marching cubes (true) or freudenthal (false)
/// connectivity should be used. Valid only for 3D input data. Default is false.
VTKM_CONT
ContourTreeUniformDistributed(bool useMarchingCubes = false);
///
/// Define the spatial decomposition of the data in case we run in parallel with a multi-block dataset
///
/// Note: Only used when running on a multi-block dataset.
/// @param[in] blocksPerDim Number of data blocks used in each data dimension
/// @param[in] globalSize Global extends of the input mesh (i.e., number of mesh points in each dimension)
/// @param[in] localBlockIndices Array with the (x,y,z) index of each local data block with
@ -107,19 +102,42 @@ public:
/// local data block
/// @param[in] localBlockSizes Array with the sizes (i.e., extends in number of mesh points) of each
/// local data block
/// @param[in] useMarchingCubes Boolean indicating whether marching cubes (true) or freudenthal (false)
/// connectivity should be used. Valid only for 3D input data. Default is false.
VTKM_CONT
void SetSpatialDecomposition(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);
ContourTreeUniformDistributed(
vtkm::Id3 blocksPerDim, // TODO/FIXME: Possibly pass SpatialDecomposition object instead
vtkm::Id3 globalSize,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockIndices,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockOrigins,
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockSizes,
bool useMarchingCubes = false);
template <typename DerivedPolicy>
VTKM_CONT vtkm::cont::PartitionedDataSet PrepareForExecution(
const vtkm::cont::PartitionedDataSet& input,
const vtkm::filter::PolicyBase<DerivedPolicy>& policy);
/// Output field "saddlePeak" wich is pairs of vertex ids indicating saddle and peak of contour
template <typename T, typename StorageType, typename DerivedPolicy>
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input,
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy> policy);
VTKM_CONT void ComputeLocalTree(const vtkm::Id blockIndex,
const vtkm::cont::DataSet& input,
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy> policy);
/// Implement per block contour tree computation after the MeshType has been discovered
template <typename T,
typename StorageType,
typename DerivedPolicy,
typename MeshType,
typename MeshBoundaryExecType>
VTKM_CONT void ComputeLocalTreeImpl(const vtkm::Id blockIndex,
const vtkm::cont::DataSet& input,
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy> policy,
MeshType& mesh,
MeshBoundaryExecType& meshBoundaryExecObject);
//@{
/// when operating on vtkm::cont::MultiBlock we want to
@ -153,16 +171,24 @@ private:
/// Use marching cubes connectivity for computing the contour tree
bool UseMarchingCubes;
/// The contour tree computed by the filter
vtkm::worklet::contourtree_augmented::ContourTree ContourTreeData;
/// Information about the spatial decomposition
vtkm::worklet::contourtree_distributed::SpatialDecomposition MultiBlockSpatialDecomposition;
/// Intermediate results (one per local data block)...
/// ... local mesh information needed at end of fan out
std::vector<vtkm::worklet::contourtree_augmented::DataSetMesh> LocalMeshes;
/// ... local contour trees etc. computed during fan in and used during fan out
std::vector<vtkm::worklet::contourtree_augmented::ContourTree> LocalContourTrees;
std::vector<vtkm::worklet::contourtree_distributed::BoundaryTree> LocalBoundaryTrees;
std::vector<vtkm::worklet::contourtree_distributed::InteriorForest> LocalInteriorForests;
/// The hierarchical trees computed by the filter (array with one entry per block)
// TODO/FIXME: We need to find a way to store the final hieararchical trees somewhere.
// Currently we cannot do this here as it is a template on FieldType
//
//std::vector<vtkm::worklet::contourtree_distributed::HierarchicalContourTree> HierarchicalContourTrees;
/// Number of iterations used to compute the contour tree
vtkm::Id NumIterations;
/// Array with the sorted order of the mesh vertices
vtkm::worklet::contourtree_augmented::IdArrayType MeshSortOrder;
/// Helper object to help with the parallel merge when running with DIY in parallel with MulitBlock data
std::unique_ptr<vtkm::worklet::contourtree_distributed::MultiBlockContourTreeHelper>
MultiBlockTreeHelper;
};
} // namespace filter

@ -59,14 +59,20 @@
// single-node augmented contour tree includes
#include <vtkm/filter/ContourTreeUniformDistributed.h>
#include <vtkm/worklet/ContourTreeUniformAugmented.h>
#include <vtkm/worklet/contourtree_augmented/DataSetMesh.h>
#include <vtkm/worklet/contourtree_augmented/meshtypes/ContourTreeMesh.h>
#include <vtkm/worklet/contourtree_augmented/meshtypes/mesh_boundary/MeshBoundaryContourTreeMesh.h>
// distributed contour tree includes
#include <vtkm/worklet/contourtree_distributed/BoundaryRestrictedAugmentedContourTree.h>
#include <vtkm/worklet/contourtree_distributed/BoundaryRestrictedAugmentedContourTreeMaker.h>
#include <vtkm/worklet/contourtree_distributed/ContourTreeBlockData.h>
#include <vtkm/worklet/contourtree_distributed/MergeBlockFunctor.h>
#include <vtkm/worklet/contourtree_distributed/BoundaryTree.h>
#include <vtkm/worklet/contourtree_distributed/BoundaryTreeMaker.h>
#include <vtkm/worklet/contourtree_distributed/ComputeDistributedContourTreeFunctor.h>
#include <vtkm/worklet/contourtree_distributed/DistributedContourTreeBlockData.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/InteriorForest.h>
#include <vtkm/worklet/contourtree_distributed/PrintGraph.h>
#include <vtkm/worklet/contourtree_distributed/SpatialDecomposition.h>
#include <vtkm/worklet/contourtree_distributed/TreeGrafter.h>
// DIY includes
// clang-format off
@ -82,6 +88,20 @@ namespace filter
{
namespace contourtree_distributed_detail
{
struct ComputeLocalTree
{
template <typename T, typename Storage, typename DerivedPolicy>
void operator()(const vtkm::cont::ArrayHandle<T, Storage>& field,
ContourTreeUniformDistributed* self,
const vtkm::Id blockIndex,
const vtkm::cont::DataSet& inputData,
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy> policy)
{
self->ComputeLocalTree(blockIndex, inputData, field, fieldMeta, policy);
}
};
//----Helper struct to call DoPostExecute. This is used to be able to
// wrap the PostExecute work in a functor so that we can use VTK-M's
// vtkm::cont::CastAndCall to infer the FieldType template parameters
@ -104,42 +124,71 @@ struct PostExecuteCaller
//-----------------------------------------------------------------------------
ContourTreeUniformDistributed::ContourTreeUniformDistributed(bool useMarchingCubes)
: vtkm::filter::FilterField<ContourTreeUniformDistributed>()
, UseMarchingCubes(useMarchingCubes)
, MultiBlockTreeHelper(nullptr)
{
this->SetOutputFieldName("resultData");
}
void ContourTreeUniformDistributed::SetSpatialDecomposition(
ContourTreeUniformDistributed::ContourTreeUniformDistributed(
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)
const vtkm::cont::ArrayHandle<vtkm::Id3>& localBlockSizes,
bool useMarchingCubes)
: vtkm::filter::FilterField<ContourTreeUniformDistributed>()
, UseMarchingCubes(useMarchingCubes)
, MultiBlockSpatialDecomposition(blocksPerDim,
globalSize,
localBlockIndices,
localBlockOrigins,
localBlockSizes)
, LocalMeshes(static_cast<std::size_t>(localBlockSizes.GetNumberOfValues()))
, LocalContourTrees(static_cast<std::size_t>(localBlockSizes.GetNumberOfValues()))
, LocalBoundaryTrees(static_cast<std::size_t>(localBlockSizes.GetNumberOfValues()))
, LocalInteriorForests(static_cast<std::size_t>(localBlockSizes.GetNumberOfValues()))
{
if (this->MultiBlockTreeHelper)
this->SetOutputFieldName("resultData");
}
template <typename DerivedPolicy>
vtkm::cont::PartitionedDataSet ContourTreeUniformDistributed::PrepareForExecution(
const vtkm::cont::PartitionedDataSet& input,
const vtkm::filter::PolicyBase<DerivedPolicy>& policy)
{
// Time execution
vtkm::cont::Timer timer;
timer.Start();
for (vtkm::Id blockNo = 0; blockNo < input.GetNumberOfPartitions(); ++blockNo)
{
this->MultiBlockTreeHelper.reset();
auto dataset = input.GetPartition(blockNo);
auto field = dataset.GetField(this->GetActiveFieldName(), this->GetActiveFieldAssociation());
vtkm::filter::FieldMetadata metaData(field);
vtkm::filter::FilterTraits<ContourTreeUniformDistributed> traits;
vtkm::cont::CastAndCall(vtkm::filter::ApplyPolicyFieldActive(field, policy, traits),
vtkm::filter::contourtree_distributed_detail::ComputeLocalTree{},
this,
blockNo,
dataset,
metaData,
policy);
}
this->MultiBlockTreeHelper =
std::unique_ptr<vtkm::worklet::contourtree_distributed::MultiBlockContourTreeHelper>(
new vtkm::worklet::contourtree_distributed::MultiBlockContourTreeHelper(
blocksPerDim, globalSize, localBlockIndices, localBlockOrigins, localBlockSizes));
// Print timing statistics
VTKM_LOG_S(vtkm::cont::LogLevel::Perf,
std::endl
<< " " << std::setw(38) << std::left << "Contour Tree Filter PrepareForExecution"
<< ": " << timer.GetElapsedTime() << " seconds");
return input; // TODO/FIXME: What to return?
}
//-----------------------------------------------------------------------------
template <typename T, typename StorageType, typename DerivedPolicy>
vtkm::cont::DataSet ContourTreeUniformDistributed::DoExecute(
void ContourTreeUniformDistributed::ComputeLocalTree(
const vtkm::Id blockIndex,
const vtkm::cont::DataSet& input,
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy> policy)
{
vtkm::cont::Timer timer;
timer.Start();
// Check that the field is Ok
if (fieldMeta.IsPointField() == false)
{
@ -151,58 +200,159 @@ vtkm::cont::DataSet ContourTreeUniformDistributed::DoExecute(
const auto& cells = input.GetCellSet();
vtkm::filter::ApplyPolicyCellSet(cells, policy, *this)
.CastAndCall(vtkm::worklet::contourtree_augmented::GetPointDimensions(), meshSize);
// TODO blockIndex needs to change if we have multiple blocks per MPI rank and DoExecute is called for multiple blocks
std::size_t blockIndex = 0;
// We always need to compute the fully augemented contour tree for our local data block
unsigned int compRegularStruct = 1;
// Create the mesh we need for the contour tree computation so that we have access to it
// afterwards to compute the BRACT for each data block as well
if (meshSize[2] == 1) // 2D mesh
{
vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation2DFreudenthal mesh(
vtkm::Id2{ meshSize[0], meshSize[1] });
this->LocalMeshes[static_cast<std::size_t>(blockIndex)] = mesh;
auto meshBoundaryExecObject = mesh.GetMeshBoundaryExecutionObject();
this->ComputeLocalTreeImpl(
blockIndex, input, field, fieldMeta, policy, mesh, meshBoundaryExecObject);
}
else if (this->UseMarchingCubes) // 3D marching cubes mesh
{
vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation3DMarchingCubes mesh(meshSize);
this->LocalMeshes[static_cast<std::size_t>(blockIndex)] = mesh;
auto meshBoundaryExecObject = mesh.GetMeshBoundaryExecutionObject();
this->ComputeLocalTreeImpl(
blockIndex, input, field, fieldMeta, policy, mesh, meshBoundaryExecObject);
}
else // Regular 3D mesh
{
vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation3DFreudenthal mesh(meshSize);
this->LocalMeshes[static_cast<std::size_t>(blockIndex)] = mesh;
auto meshBoundaryExecObject = mesh.GetMeshBoundaryExecutionObject();
this->ComputeLocalTreeImpl(
blockIndex, input, field, fieldMeta, policy, mesh, meshBoundaryExecObject);
}
} // ContourTreeUniformDistributed::ComputeLocalTree
// Run the worklet
//-----------------------------------------------------------------------------
template <typename T,
typename StorageType,
typename DerivedPolicy,
typename MeshType,
typename MeshBoundaryExecType>
void ContourTreeUniformDistributed::ComputeLocalTreeImpl(
const vtkm::Id blockIndex,
const vtkm::cont::DataSet&, // input,
const vtkm::cont::ArrayHandle<T, StorageType>& field,
const vtkm::filter::FieldMetadata&, // fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy>, // policy,
MeshType& mesh,
MeshBoundaryExecType& meshBoundaryExecObject)
{
// We always need to compute the fully augmented contour tree for our local data block
const unsigned int compRegularStruct = 1;
// Set up the worklet
vtkm::worklet::ContourTreeAugmented worklet;
worklet.Run(field,
MultiBlockTreeHelper ? MultiBlockTreeHelper->LocalContourTrees[blockIndex]
: this->ContourTreeData,
MultiBlockTreeHelper ? MultiBlockTreeHelper->LocalSortOrders[blockIndex]
: this->MeshSortOrder,
mesh,
this->LocalContourTrees[static_cast<std::size_t>(blockIndex)],
this->LocalMeshes[static_cast<std::size_t>(blockIndex)].SortOrder,
this->NumIterations,
meshSize,
this->UseMarchingCubes,
compRegularStruct);
compRegularStruct,
meshBoundaryExecObject);
// If we run in parallel but with only one global block, then we need set our outputs correctly
// here to match the expected behavior in parallel
if (this->MultiBlockTreeHelper)
{
if (this->MultiBlockTreeHelper->GetGlobalNumberOfBlocks() == 1)
{
// Copy the contour tree and mesh sort order to the output
this->ContourTreeData = this->MultiBlockTreeHelper->LocalContourTrees[0];
this->MeshSortOrder = this->MultiBlockTreeHelper->LocalSortOrders[0];
// In parallel we need the sorted values as output resulti
// Construct the sorted values by permutting the input field
auto fieldPermutted = vtkm::cont::make_ArrayHandlePermutation(this->MeshSortOrder, field);
vtkm::cont::ArrayHandle<T> sortedValues;
vtkm::cont::Algorithm::Copy(fieldPermutted, sortedValues);
// Create the result object
vtkm::cont::DataSet result;
vtkm::cont::Field rfield(
this->GetOutputFieldName(), vtkm::cont::Field::Association::WHOLE_MESH, sortedValues);
result.AddField(rfield);
return result;
}
}
// Now we compute the BRACT for our data block. We do this here because we know the MeshType
// here and we don't need to store the mesh separately any more since it is stored in the BRACT
VTKM_LOG_S(vtkm::cont::LogLevel::Perf,
std::endl
<< " " << std::setw(38) << std::left << "Contour Tree Filter DoExecute"
<< ": " << timer.GetElapsedTime() << " seconds");
// Get the mesh information needed to create an IdRelabeler to relable local to global ids
// Create an IdRelabeler since we are using a DataSetMesh type here, we don't need
// the IdRelabeler for the BRACT construction when we are using a ContourTreeMesh.
auto localToGlobalIdRelabeler = vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler(
this->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal().Get(blockIndex),
this->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(blockIndex),
this->MultiBlockSpatialDecomposition.GlobalSize);
// Initialize the BoundaryTreeMaker
auto boundaryTreeMaker =
vtkm::worklet::contourtree_distributed::BoundaryTreeMaker<MeshType, MeshBoundaryExecType>(
&mesh, // The input mesh
meshBoundaryExecObject, // The mesh boundary
this->LocalContourTrees[static_cast<std::size_t>(blockIndex)], // The contour tree
&this->LocalBoundaryTrees[static_cast<std::size_t>(
blockIndex)], // The boundary tree (a.k.a BRACT) to be computed
&this->LocalInteriorForests[static_cast<std::size_t>(
blockIndex)] // The interior forest (a.k.a. Residue) to be computed
);
// Execute the BRACT construction, including the compute of the InteriorForest
boundaryTreeMaker.Construct(&localToGlobalIdRelabeler);
// Construct the expected result for serial execution. Note, in serial the result currently
// not actually being used, but in parallel we need the sorted mesh values as output
// This part is being hit when we run in serial or parallel with more then one rank
return CreateResultFieldPoint(input, ContourTreeData.Arcs, this->GetOutputFieldName());
} // ContourTreeUniformDistributed::DoExecute
// Print BRACT for debug
vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"Rank_%d_Block_%d_Initial_BRACT.gv",
static_cast<int>(rank),
static_cast<int>(blockIndex));
std::ofstream os(buffer);
os << this->LocalBoundaryTrees[static_cast<std::size_t>(blockIndex)].PrintGlobalDot(
"Before Fan In",
mesh,
field,
this->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal().Get(blockIndex),
this->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(blockIndex),
this->MultiBlockSpatialDecomposition.GlobalSize)
<< std::endl;
// TODO: Fix calling conventions so dot print works here
// At this point, I'm reasonably certain that the contour tree has been computed regardless of data push/pull
// So although it might be logical to print things out earlier, I'll do it here
// save the regular structure
// TODO: Oliver Fix and renable the following print calls
/*
std::string regularStructureFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(blockIndex)) + std::string("_Initial_Step_0_Contour_Tree_Regular_Structure.gv");
std::ofstream regularStructureFile(regularStructureFileName);
regularStructureFile << worklet::contourtree_distributed::ContourTreeDotGraphPrint<T, MeshType, vtkm::worklet::contourtree_augmented::IdArrayType>
( std::string("Block ") + std::to_string(static_cast<std::size_t>(blockIndex)) + " Initial Step 0 Contour Tree Regular Structure",
this->LocalMeshes[static_cast<std::size_t>(blockIndex)],
this->LocalContourTrees[static_cast<std::size_t>(blockIndex)],
worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE|worklet::contourtree_distributed::SHOW_ALL_IDS);
*/
/*
std::string superStructureFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(blockIndex)) + std::string("_Initial_Step_1_Contour_Tree_Super_Structure.gv");
std::ofstream superStructureFile(superStructureFileName);
vtkm::Id ctPrintSettings = worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE|worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE|worklet::contourtree_distributed::SHOW_ALL_IDS|worklet::contourtree_distributed::SHOW_ALL_SUPERIDS|worklet::contourtree_distributed::SHOW_ALL_HYPERIDS;
std::string ctPrintLabel = std::string("Block ") + std::to_string(static_cast<size_t>(blockIndex)) + " Initial Step 1 Contour Tree Super Structure";
superStructureFile << vtkm::worklet::contourtree_distributed::ContourTreeDotGraphPrint<T, StorageType, MeshType, vtkm::worklet::contourtree_augmented::IdArrayType>
( ctPrintLabel,
dynamic_cast<MeshType&>(this->LocalMeshes[static_cast<std::size_t>(blockIndex)]),
&localToGlobalIdRelabeler,
field,
this->LocalContourTrees[static_cast<std::size_t>(blockIndex)],
ctPrintSettings);
// save the Boundary Tree as a dot file
std::string boundaryTreeFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<size_t>(blockIndex)) + std::string("_Initial_Step_3_Boundary_Tree.gv");
std::ofstream boundaryTreeFile(boundaryTreeFileName);
boundaryTreeFile << vtkm::worklet::contourtree_distributed::BoundaryTreeDotGraphPrint
( std::string("Block ") + std::to_string(static_cast<size_t>(blockIndex)) + std::string(" Initial Step 3 Boundary Tree"),
dynamic_cast<MeshType&>(this->LocalMeshes[static_cast<std::size_t>(blockIndex)]),
meshBoundaryExecObject,
this->LocalBoundaryTrees[static_cast<std::size_t>(blockIndex)],
&localToGlobalIdRelabeler,
field);
// and save the Interior Forest as another dot file
std::string interiorForestFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(blockIndex)) + std::string("_Initial_Step_4_Interior_Forest.gv");
std::ofstream interiorForestFile(interiorForestFileName);
interiorForestFile << worklet::contourtree_distributed::InteriorForestDotGraphPrint
( std::string("Block ") + std::to_string(rank) + " Initial Step 4 Interior Forest",
this->LocalInteriorForests[static_cast<std::size_t>(blockIndex)],
this->LocalContourTrees[static_cast<std::size_t>(blockIndex)],
this->LocalBoundaryTrees[static_cast<std::size_t>(blockIndex)],
dynamic_cast<MeshType&>(this->LocalMeshes[static_cast<std::size_t>(blockIndex)]),
meshBoundaryExecObject,
&localToGlobalIdRelabeler,
field);
*/
}
//-----------------------------------------------------------------------------
template <typename DerivedPolicy>
@ -210,93 +360,109 @@ inline VTKM_CONT void ContourTreeUniformDistributed::PreExecute(
const vtkm::cont::PartitionedDataSet& input,
const vtkm::filter::PolicyBase<DerivedPolicy>&)
{
if (this->MultiBlockTreeHelper)
if (vtkm::worklet::contourtree_distributed::SpatialDecomposition::GetGlobalNumberOfBlocks(
input) != this->MultiBlockSpatialDecomposition.GetGlobalNumberOfBlocks())
{
if (this->MultiBlockTreeHelper->GetGlobalNumberOfBlocks(input) !=
this->MultiBlockTreeHelper->GetGlobalNumberOfBlocks())
{
throw vtkm::cont::ErrorFilterExecution(
"Global number of block in MultiBlock dataset does not match the SpatialDecomposition");
}
if (this->MultiBlockTreeHelper->GetLocalNumberOfBlocks() != input.GetNumberOfPartitions())
{
throw vtkm::cont::ErrorFilterExecution(
"Global number of block in MultiBlock dataset does not match the SpatialDecomposition");
}
throw vtkm::cont::ErrorFilterExecution(
"Global number of blocks in MultiBlock dataset does not match the SpatialDecomposition");
}
if (this->MultiBlockSpatialDecomposition.GetLocalNumberOfBlocks() !=
input.GetNumberOfPartitions())
{
throw vtkm::cont::ErrorFilterExecution(
"Local number of blocks in MultiBlock dataset does not match the SpatialDecomposition");
}
}
//-----------------------------------------------------------------------------
template <typename T, typename StorageType, typename DerivedPolicy>
template <typename FieldType, typename StorageType, typename DerivedPolicy>
VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
const vtkm::cont::PartitionedDataSet& input,
vtkm::cont::PartitionedDataSet& output,
const vtkm::filter::FieldMetadata& fieldMeta,
const vtkm::cont::ArrayHandle<T, StorageType>&, // dummy parameter to get the type
vtkm::filter::PolicyBase<DerivedPolicy> policy)
vtkm::cont::PartitionedDataSet&, // temporarily commented out: output,
const vtkm::filter::FieldMetadata&, // dummy parameter for field meta data
const vtkm::cont::ArrayHandle<FieldType, StorageType>&, // dummy parameter to get the type
vtkm::filter::PolicyBase<DerivedPolicy>) // dummy parameter for policy
{
(void)fieldMeta; // avoid unused parameter warning
(void)policy; // avoid unused parameter warning
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
vtkm::Id size = comm.size();
vtkm::Id rank = comm.rank();
std::vector<vtkm::worklet::contourtree_augmented::ContourTreeMesh<T>*> localContourTreeMeshes;
localContourTreeMeshes.resize(static_cast<std::size_t>(input.GetNumberOfPartitions()));
// TODO need to allocate and free these ourselves. May need to update detail::MultiBlockContourTreeHelper::ComputeLocalContourTreeMesh
std::vector<vtkm::worklet::contourtree_distributed::ContourTreeBlockData<T>*> localDataBlocks;
localDataBlocks.resize(static_cast<size_t>(input.GetNumberOfPartitions()));
std::vector<vtkmdiy::Link*> localLinks; // dummy links needed to make DIY happy
localLinks.resize(static_cast<size_t>(input.GetNumberOfPartitions()));
// TODO Check setting. In parallel we should need to augment the tree in order to be able to do the merging
unsigned int compRegularStruct = 1;
std::vector<vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<FieldType>*>
localDataBlocks(static_cast<size_t>(input.GetNumberOfPartitions()), nullptr);
for (std::size_t bi = 0; bi < static_cast<std::size_t>(input.GetNumberOfPartitions()); bi++)
{
// create the local contour tree mesh
localLinks[bi] = new vtkmdiy::Link;
// Create the local data block structure and set extents
localDataBlocks[bi] =
new vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<FieldType>();
localDataBlocks[bi]->BlockIndex = static_cast<vtkm::Id>(bi);
localDataBlocks[bi]->BlockOrigin =
this->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal().Get(
static_cast<vtkm::Id>(bi));
localDataBlocks[bi]->BlockSize =
this->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(
static_cast<vtkm::Id>(bi));
// Save local tree information for fan out FIXME: Try to avoid copy
localDataBlocks[bi]->ContourTrees.push_back(this->LocalContourTrees[bi]);
localDataBlocks[bi]->InteriorForests.push_back(this->LocalInteriorForests[bi]);
// ... Compute arrays needed for constructing contour tree mesh
const auto sortOrder = this->LocalMeshes[bi].SortOrder;
// ... Compute the global mesh index for the partially augmented contour tree. I.e., here we
// don't need the global mesh index for all nodes, but only for the augmented nodes from the
// tree. We, hence, permute the sortOrder by contourTree.augmentednodes and then compute the
// GlobalMeshIndex by tranforming those indices with our IdRelabler
vtkm::worklet::contourtree_augmented::IdArrayType localGlobalMeshIndex;
vtkm::cont::ArrayHandlePermutation<vtkm::worklet::contourtree_augmented::IdArrayType,
vtkm::worklet::contourtree_augmented::IdArrayType>
permutedSortOrder(this->LocalBoundaryTrees[bi].VertexIndex, sortOrder);
auto transformedIndex = vtkm::cont::make_ArrayHandleTransform(
permutedSortOrder,
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler(
this->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal().Get(
static_cast<vtkm::Id>(bi)),
this->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(
static_cast<vtkm::Id>(bi)),
this->MultiBlockSpatialDecomposition.GlobalSize));
vtkm::cont::Algorithm::Copy(transformedIndex, localGlobalMeshIndex);
// ... get data values
auto currBlock = input.GetPartition(static_cast<vtkm::Id>(bi));
auto currField =
currBlock.GetField(this->GetActiveFieldName(), this->GetActiveFieldAssociation());
//const vtkm::cont::ArrayHandle<T,StorageType> &fieldData = currField.GetData().Cast<vtkm::cont::ArrayHandle<T,StorageType> >();
vtkm::cont::ArrayHandle<T> fieldData;
vtkm::cont::ArrayCopy(currField.GetData().AsVirtual<T>(), fieldData);
auto currContourTreeMesh = vtkm::worklet::contourtree_distributed::MultiBlockContourTreeHelper::
ComputeLocalContourTreeMesh<T>(
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal()
.Get(static_cast<vtkm::Id>(bi)),
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(
static_cast<vtkm::Id>(bi)),
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize,
fieldData,
MultiBlockTreeHelper->LocalContourTrees[bi],
MultiBlockTreeHelper->LocalSortOrders[bi],
compRegularStruct);
localContourTreeMeshes[bi] = currContourTreeMesh;
// create the local data block structure
localDataBlocks[bi] = new vtkm::worklet::contourtree_distributed::ContourTreeBlockData<T>();
localDataBlocks[bi]->NumVertices = currContourTreeMesh->NumVertices;
// FIXME: Commented out to avoid compile error, full fix to follow in subsequent merge request
// localDataBlocks[bi]->SortOrder = currContourTreeMesh->SortOrder;
localDataBlocks[bi]->SortedValue = currContourTreeMesh->SortedValues;
localDataBlocks[bi]->GlobalMeshIndex = currContourTreeMesh->GlobalMeshIndex;
localDataBlocks[bi]->Neighbours = currContourTreeMesh->Neighbours;
localDataBlocks[bi]->FirstNeighbour = currContourTreeMesh->FirstNeighbour;
localDataBlocks[bi]->MaxNeighbours = currContourTreeMesh->MaxNeighbours;
localDataBlocks[bi]->BlockOrigin =
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal().Get(
static_cast<vtkm::Id>(bi));
localDataBlocks[bi]->BlockSize =
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(
static_cast<vtkm::Id>(bi));
localDataBlocks[bi]->GlobalSize =
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize;
// We need to augment at least with the boundary vertices when running in parallel
localDataBlocks[bi]->ComputeRegularStructure = compRegularStruct;
}
// Setup vtkmdiy to do global binary reduction of neighbouring blocks. See also RecuctionOperation struct for example
vtkm::cont::ArrayHandle<FieldType> fieldData;
vtkm::cont::ArrayCopy(currField.GetData().template AsVirtual<FieldType>(), fieldData);
// ... compute and store the actual mesh
localDataBlocks[bi]->ContourTreeMeshes.emplace_back(this->LocalBoundaryTrees[bi].VertexIndex,
this->LocalBoundaryTrees[bi].Superarcs,
sortOrder,
fieldData,
localGlobalMeshIndex);
// ... save for debugging in text and .gv/.dot format
char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"Rank_%d_Block_%d_Initial_Step_3_BRACT_Mesh.txt",
static_cast<int>(rank),
static_cast<int>(bi));
localDataBlocks[bi]->ContourTreeMeshes.back().Save(buffer);
// save the corresponding .gv file
std::string boundaryTreeMeshFileName = std::string("Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(bi)) + std::string("_Initial_Step_5_BRACT_Mesh.gv");
std::ofstream boundaryTreeMeshFile(boundaryTreeMeshFileName);
boundaryTreeMeshFile
<< vtkm::worklet::contourtree_distributed::ContourTreeMeshDotGraphPrint<FieldType>(
std::string("Block ") + std::to_string(static_cast<int>(rank)) +
std::string(" Initial Step 5 BRACT Mesh"),
localDataBlocks[bi]->ContourTreeMeshes.back(),
worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_ALL);
}
// Setup vtkmdiy to do global binary reduction of neighbouring blocks. See also RecuctionOperation struct for example
// Create the vtkmdiy master
vtkmdiy::Master master(comm,
1, // Use 1 thread, VTK-M will do the treading
@ -305,7 +471,7 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
// Compute the gids for our local blocks
const vtkm::worklet::contourtree_distributed::SpatialDecomposition& spatialDecomp =
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition;
this->MultiBlockSpatialDecomposition;
auto localBlockIndicesPortal = spatialDecomp.LocalBlockIndices.ReadPortal();
std::vector<vtkm::Id> vtkmdiyLocalBlockGids(static_cast<size_t>(input.GetNumberOfPartitions()));
for (vtkm::Id bi = 0; bi < input.GetNumberOfPartitions(); bi++)
@ -325,9 +491,12 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
vtkmdiy::RegularDecomposer<vtkmdiy::DiscreteBounds>::coords_to_gid(tempCoords, tempDivisions);
}
std::vector<vtkmdiy::Link*> localLinks(static_cast<std::vector<vtkmdiy::Link>::size_type>(
input.GetNumberOfPartitions())); // dummy links needed to make DIY happy
// Add my local blocks to the vtkmdiy master.
for (std::size_t bi = 0; bi < static_cast<std::size_t>(input.GetNumberOfPartitions()); bi++)
{
localLinks[bi] = new vtkmdiy::Link;
master.add(static_cast<int>(vtkmdiyLocalBlockGids[bi]), // block id
localDataBlocks[bi],
localLinks[bi]);
@ -348,87 +517,168 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
static_cast<int>(vtkmdiyLocalBlockGids[static_cast<size_t>(bi)]));
}
// Fix the vtkmdiy links. TODO Remove changes to the vtkmdiy in VTKM when we update to the new VTK-M
// Fix the vtkmdiy links.
vtkmdiy::fix_links(master, assigner);
// partners for merge over regular block grid
vtkmdiy::RegularMergePartners partners(
vtkmdiy::RegularSwapPartners partners(
decomposer, // domain decomposition
2, // raix of k-ary reduction. TODO check this value
true // contiguous: true=distance doubling , false=distnace halving TODO check this value
);
// reduction
vtkmdiy::reduce(
master, assigner, partners, &vtkm::worklet::contourtree_distributed::MergeBlockFunctor<T>);
const vtkm::worklet::contourtree_distributed::ComputeDistributedContourTreeFunctor<FieldType>
computeDistributedContourTreeFunctor{ this->MultiBlockSpatialDecomposition.GlobalSize };
vtkmdiy::reduce(master, assigner, partners, computeDistributedContourTreeFunctor);
comm.barrier(); // Be safe!
if (rank == 0)
{
// Now run the contour tree algorithm on the last block to compute the final tree
vtkm::Id currNumIterations;
vtkm::worklet::contourtree_augmented::ContourTree currContourTree;
vtkm::worklet::contourtree_augmented::IdArrayType currSortOrder;
vtkm::worklet::ContourTreeAugmented worklet;
vtkm::cont::ArrayHandle<T> currField;
// Construct the contour tree mesh from the last block
vtkm::worklet::contourtree_augmented::ContourTreeMesh<T> contourTreeMeshOut;
contourTreeMeshOut.NumVertices = localDataBlocks[0]->NumVertices;
// FIXME: Commented out to avoid compile error, full fix to follow in subsequent merge request
// contourTreeMeshOut.SortOrder = localDataBlocks[0]->SortOrder;
contourTreeMeshOut.SortedValues = localDataBlocks[0]->SortedValue;
contourTreeMeshOut.GlobalMeshIndex = localDataBlocks[0]->GlobalMeshIndex;
contourTreeMeshOut.Neighbours = localDataBlocks[0]->Neighbours;
contourTreeMeshOut.FirstNeighbour = localDataBlocks[0]->FirstNeighbour;
contourTreeMeshOut.MaxNeighbours = localDataBlocks[0]->MaxNeighbours;
// Construct the mesh boundary exectuion object needed for boundary augmentation
vtkm::Id3 minIdx(0, 0, 0);
vtkm::Id3 maxIdx = this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize;
maxIdx[0] = maxIdx[0] - 1;
maxIdx[1] = maxIdx[1] - 1;
maxIdx[2] = maxIdx[2] > 0 ? (maxIdx[2] - 1) : 0;
auto meshBoundaryExecObj = contourTreeMeshOut.GetMeshBoundaryExecutionObject(
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize, minIdx, maxIdx);
// Run the worklet to compute the final contour tree
worklet.Run(
contourTreeMeshOut.SortedValues, // Unused param. Provide something to keep API happy
contourTreeMeshOut,
this->ContourTreeData,
this->MeshSortOrder,
currNumIterations,
compRegularStruct,
meshBoundaryExecObj);
master.foreach ([&](vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<
FieldType>* b,
const vtkmdiy::Master::ProxyWithLink&) {
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
"Fan In Complete" << std::endl
<< "# of CTs: " << b->ContourTrees.size() << std::endl
<< "# of CTMs: " << b->ContourTreeMeshes.size() << std::endl
<< "# of IFs: " << b->InteriorForests.size() << std::endl);
// Set the final mesh sort order we need to use
this->MeshSortOrder = contourTreeMeshOut.GlobalMeshIndex;
// Remeber the number of iterations for the output
this->NumIterations = currNumIterations;
char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"AfterFanInResults_Rank%d_Block%d.txt",
static_cast<int>(rank),
static_cast<int>(b->BlockIndex));
std::ofstream os(buffer);
os << "Contour Trees" << std::endl;
os << "=============" << std::endl;
for (const auto& ct : b->ContourTrees)
ct.PrintContent(os);
os << std::endl;
os << "Contour Tree Meshes" << std::endl;
os << "===================" << std::endl;
for (const auto& cm : b->ContourTreeMeshes)
cm.PrintContent(os);
os << std::endl;
os << "Interior Forests" << std::endl;
os << "===================" << std::endl;
for (const auto& info : b->InteriorForests)
info.PrintContent(os);
os << std::endl;
// Return the sorted values of the contour tree as the result
// TODO the result we return for the parallel and serial case are different right now. This should be made consistent. However, only in the parallel case are we useing the result output
vtkm::cont::DataSet temp;
vtkm::cont::Field rfield(this->GetOutputFieldName(),
vtkm::cont::Field::Association::WHOLE_MESH,
contourTreeMeshOut.SortedValues);
temp.AddField(rfield);
output = vtkm::cont::PartitionedDataSet(temp);
}
else
{
this->ContourTreeData = MultiBlockTreeHelper->LocalContourTrees[0];
this->MeshSortOrder = MultiBlockTreeHelper->LocalSortOrders[0];
// Fan out
auto nRounds = b->ContourTrees.size() - 1;
// Free allocated temporary pointers
for (std::size_t bi = 0; bi < static_cast<std::size_t>(input.GetNumberOfPartitions()); bi++)
VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Fan out. nRounds=" << nRounds);
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType> hierarchicalTree;
hierarchicalTree.Initialize(
static_cast<vtkm::Id>(nRounds), b->ContourTrees[nRounds], b->ContourTreeMeshes[nRounds - 1]);
// TODO: GET THIS COMPILING
// save the corresponding .gv file
// std::string hierarchicalTreeFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(b->BlockIndex)) + "_Round_" + std::to_string(nRounds) + "_Hierarchical_Tree.gv";
// std::ofstream hierarchicalTreeFile(hierarchicalTreeFileName);
// hierarchicalTreeFile << vtkm::worklet::contourtree_distributed::HierarchicalContourTreeDotGraphPrint<FieldType>
// ( std::string("Block ") + std::to_string(static_cast<int>(b->BlockIndex)) + " Round " + std::to_string(nRounds) + " Hierarchical Tree", hierarchicalTree,
// vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE|vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE|vtkm::worklet::contourtree_distributed::SHOW_ALL_IDS|vtkm::worklet::contourtree_distributed::SHOW_ALL_SUPERIDS|vtkm::worklet::contourtree_distributed::SHOW_ALL_HYPERIDS
// );
for (auto round = nRounds - 1; round > 0; round--)
{
delete localContourTreeMeshes[bi];
delete localDataBlocks[bi];
// delete localLinks[bi];
vtkm::worklet::contourtree_distributed::
TreeGrafter<vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>, FieldType>
grafter(&(b->ContourTreeMeshes[round - 1]),
b->ContourTrees[round],
&(b->InteriorForests[round]));
grafter.GraftInteriorForests(static_cast<vtkm::Id>(round),
hierarchicalTree,
b->ContourTreeMeshes[round - 1].SortedValues);
// TODO: GET THIS COMPILING
// save the corresponding .gv file
// std::string hierarchicalTreeFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(b->BlockIndex)) + "_Round_" + std::to_string(nRounds) + "_Hierarchical_Tree.gv";
// std::ofstream hierarchicalTreeFile(hierarchicalTreeFileName);
// hierarchicalTreeFile << vtkm::worklet::contourtree_distributed::HierarchicalContourTreeDotGraphPrint<FieldType>
// ( std::string("Block ") + std::to_string(static_cast<int>(b->BlockIndex)) + " Round " + std::to_string(nRounds) + " Hierarchical Tree", hierarchicalTree,
// vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE|vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE|vtkm::worklet::contourtree_distributed::SHOW_ALL_IDS|vtkm::worklet::contourtree_distributed::SHOW_ALL_SUPERIDS|vtkm::worklet::contourtree_distributed::SHOW_ALL_HYPERIDS
// );
}
}
localContourTreeMeshes.clear();
localDataBlocks.clear();
localLinks.clear();
// bottom level
vtkm::worklet::contourtree_distributed::
TreeGrafter<vtkm::worklet::contourtree_augmented::DataSetMesh, FieldType>
grafter(&(this->LocalMeshes[static_cast<std::size_t>(b->BlockIndex)]),
b->ContourTrees[0],
&(b->InteriorForests[0]));
auto currBlock = input.GetPartition(b->BlockIndex);
auto currField =
currBlock.GetField(this->GetActiveFieldName(), this->GetActiveFieldAssociation());
vtkm::cont::ArrayHandle<FieldType> fieldData;
vtkm::cont::ArrayCopy(currField.GetData().template AsVirtual<FieldType>(), fieldData);
auto localToGlobalIdRelabeler = vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler(
this->MultiBlockSpatialDecomposition.LocalBlockOrigins.ReadPortal().Get(b->BlockIndex),
this->MultiBlockSpatialDecomposition.LocalBlockSizes.ReadPortal().Get(b->BlockIndex),
this->MultiBlockSpatialDecomposition.GlobalSize);
grafter.GraftInteriorForests(0, hierarchicalTree, fieldData, &localToGlobalIdRelabeler);
// TODO: GET THIS COMPILING
// save the corresponding .gv file
// std::string hierarchicalTreeFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(b->BlockIndex)) + "_Round_" + std::to_string(nRounds) + "_Hierarchical_Tree.gv";
// std::ofstream hierarchicalTreeFile(hierarchicalTreeFileName);
// hierarchicalTreeFile << vtkm::worklet::contourtree_distributed::HierarchicalContourTreeDotGraphPrint<FieldType>
// ( std::string("Block ") + std::to_string(static_cast<int>(b->BlockIndex)) + " Round " + std::to_string(nRounds) + " Hierarchical Tree", hierarchicalTree,
// vtkm::worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE|vtkm::worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE|vtkm::worklet::contourtree_distributed::SHOW_ALL_IDS|vtkm::worklet::contourtree_distributed::SHOW_ALL_SUPERIDS|vtkm::worklet::contourtree_distributed::SHOW_ALL_HYPERIDS
// );
});
// FIXME: The following is still old code and needs to be changed!!!
#if 0
// Now run the contour tree algorithm on the last block to compute the final tree
vtkm::Id currNumIterations;
vtkm::worklet::contourtree_augmented::ContourTree currContourTree;
vtkm::worklet::contourtree_augmented::IdArrayType currSortOrder;
vtkm::worklet::ContourTreeAugmented worklet;
vtkm::cont::ArrayHandle<T> currField;
// Construct the mesh boundary exectuion object needed for boundary augmentation
vtkm::Id3 minIdx(0, 0, 0);
vtkm::Id3 maxIdx = this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize;
maxIdx[0] = maxIdx[0] - 1;
maxIdx[1] = maxIdx[1] - 1;
maxIdx[2] = maxIdx[2] > 0 ? (maxIdx[2] - 1) : 0;
// TODO Check setting. In parallel we should need to augment the tree in order to be able to do the merging
const unsigned int computeRegularStructure = 1;
auto meshBoundaryExecObj = localDataBlocks[0]->ContourTreeMeshes.back().GetMeshBoundaryExecutionObject(
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize[0],
this->MultiBlockTreeHelper->MultiBlockSpatialDecomposition.GlobalSize[1],
minIdx,
maxIdx);
// Run the worklet to compute the final contour tree
worklet.Run(
localDataBlocks[0]->ContourTreeMeshes.back().SortedValues, // Unused param. Provide something to keep API happy
localDataBlocks[0]->ContourTreeMeshes.back(),
this->ContourTreeData,
this->MeshSortOrder,
currNumIterations,
computeRegularStructure,
meshBoundaryExecObj);
// Set the final mesh sort order we need to use
this->MeshSortOrder = localDataBlocks[0]->ContourTreeMeshes.back().GlobalMeshIndex;
// Remeber the number of iterations for the output
this->NumIterations = currNumIterations;
// Return the sorted values of the contour tree as the result
// TODO the result we return for the parallel and serial case are different right now. This should be made consistent. However, only in the parallel case are we useing the result output
vtkm::cont::DataSet temp;
vtkm::cont::Field rfield(this->GetOutputFieldName(),
vtkm::cont::Field::Association::WHOLE_MESH,
localDataBlocks[0]->ContourTreeMeshes.back().SortedValues);
temp.AddField(rfield);
output = vtkm::cont::PartitionedDataSet(temp);
/* Not necessary?
this->ContourTreeData = MultiBlockTreeHelper->LocalContourTrees[0];
this->MeshSortOrder = MultiBlockTreeHelper->LocalSortOrders[0];
*/
#endif
}
@ -439,36 +689,32 @@ inline VTKM_CONT void ContourTreeUniformDistributed::PostExecute(
vtkm::cont::PartitionedDataSet& result,
const vtkm::filter::PolicyBase<DerivedPolicy>& policy)
{
if (this->MultiBlockTreeHelper)
vtkm::cont::Timer timer;
timer.Start();
// We are running in parallel and need to merge the contour tree in PostExecute
// TODO/FIXME: Make sure this still makes sense
if (this->MultiBlockSpatialDecomposition.GetGlobalNumberOfBlocks() == 1)
{
vtkm::cont::Timer timer;
timer.Start();
// We are running in parallel and need to merge the contour tree in PostExecute
if (MultiBlockTreeHelper->GetGlobalNumberOfBlocks() == 1)
{
return;
}
auto field =
input.GetPartition(0).GetField(this->GetActiveFieldName(), this->GetActiveFieldAssociation());
vtkm::filter::FieldMetadata metaData(field);
vtkm::filter::FilterTraits<ContourTreeUniformDistributed> traits;
vtkm::cont::CastAndCall(vtkm::filter::ApplyPolicyFieldActive(field, policy, traits),
vtkm::filter::contourtree_distributed_detail::PostExecuteCaller{},
this,
input,
result,
metaData,
policy);
this->MultiBlockTreeHelper.reset();
VTKM_LOG_S(vtkm::cont::LogLevel::Perf,
std::endl
<< " " << std::setw(38) << std::left << "Contour Tree Filter PostExecute"
<< ": " << timer.GetElapsedTime() << " seconds");
return;
}
}
auto field = // TODO/FIXME: Correct for more than one block per rank?
input.GetPartition(0).GetField(this->GetActiveFieldName(), this->GetActiveFieldAssociation());
vtkm::filter::FieldMetadata metaData(field);
vtkm::filter::FilterTraits<ContourTreeUniformDistributed> traits;
vtkm::cont::CastAndCall(vtkm::filter::ApplyPolicyFieldActive(field, policy, traits),
vtkm::filter::contourtree_distributed_detail::PostExecuteCaller{},
this,
input,
result,
metaData,
policy);
VTKM_LOG_S(vtkm::cont::LogLevel::Perf,
std::endl
<< " " << std::setw(38) << std::left << "Contour Tree Filter PostExecute"
<< ": " << timer.GetElapsedTime() << " seconds");
}
} // namespace filter
} // namespace vtkm::filter

@ -130,7 +130,7 @@ public:
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 12" << std::endl;
@ -177,7 +177,7 @@ public:
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 10 20" << std::endl;
@ -229,7 +229,7 @@ public:
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 67" << std::endl;
@ -285,7 +285,7 @@ public:
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 112" << std::endl;
@ -341,7 +341,7 @@ public:
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 118" << std::endl;
@ -402,7 +402,7 @@ public:
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 203" << std::endl;

@ -166,10 +166,10 @@ public:
inline void Init(vtkm::Id dataSize);
// debug routine
inline void DebugPrint(const char* message, const char* fileName, long lineNum) const;
inline std::string DebugPrint(const char* message, const char* fileName, long lineNum) const;
// print contents
inline void PrintContent() const;
inline void PrintContent(std::ostream& outStream = std::cout) const;
// print routines
inline void PrintDotSuperStructure() const;
@ -201,49 +201,47 @@ void ContourTree::Init(vtkm::Id dataSize)
} // Init()
inline void ContourTree::PrintContent() const
inline void ContourTree::PrintContent(std::ostream& outStream /*= std::cout*/) const
{
PrintHeader(this->Arcs.GetNumberOfValues());
PrintIndices("Arcs", this->Arcs);
PrintIndices("Superparents", this->Superparents);
std::cout << std::endl;
PrintHeader(this->Supernodes.GetNumberOfValues());
PrintIndices("Supernodes", this->Supernodes);
PrintIndices("Superarcs", this->Superarcs);
PrintIndices("Hyperparents", this->Hyperparents);
PrintIndices("When Xferred", this->WhenTransferred);
std::cout << std::endl;
PrintHeader(this->Hypernodes.GetNumberOfValues());
PrintIndices("Hypernodes", this->Hypernodes);
PrintIndices("Hyperarcs", this->Hyperarcs);
PrintHeader(Augmentnodes.GetNumberOfValues());
PrintIndices("Augmentnodes", Augmentnodes);
PrintIndices("Augmentarcs", this->Augmentarcs);
std::cout << std::endl;
std::cout << "NumIterations: " << this->NumIterations << std::endl;
PrintHeader(this->FirstSupernodePerIteration.GetNumberOfValues());
PrintIndices("First SN Per Iter", this->FirstSupernodePerIteration);
PrintIndices("First HN Per Iter", this->FirstHypernodePerIteration);
PrintHeader(this->Arcs.GetNumberOfValues(), outStream);
PrintIndices("Arcs", this->Arcs, -1, outStream); // -1 -> thisArcs.size()
PrintIndices("Superparents", this->Superparents, -1, outStream);
outStream << std::endl;
PrintHeader(this->Supernodes.GetNumberOfValues(), outStream);
PrintIndices("Supernodes", this->Supernodes, -1, outStream);
PrintIndices("Superarcs", this->Superarcs, -1, outStream);
PrintIndices("Hyperparents", this->Hyperparents, -1, outStream);
PrintIndices("When Xferred", this->WhenTransferred, -1, outStream);
outStream << std::endl;
PrintHeader(this->Hypernodes.GetNumberOfValues(), outStream);
PrintIndices("Hypernodes", this->Hypernodes, -1, outStream);
PrintIndices("Hyperarcs", this->Hyperarcs, -1, outStream);
PrintHeader(Augmentnodes.GetNumberOfValues(), outStream);
PrintIndices("Augmentnodes", Augmentnodes, -1, outStream);
PrintIndices("Augmentarcs", this->Augmentarcs, -1, outStream);
outStream << std::endl;
outStream << "NumIterations: " << this->NumIterations << std::endl;
PrintHeader(this->FirstSupernodePerIteration.GetNumberOfValues(), outStream);
PrintIndices("First SN Per Iter", this->FirstSupernodePerIteration, -1, outStream);
PrintIndices("First HN Per Iter", this->FirstHypernodePerIteration, -1, outStream);
}
void ContourTree::DebugPrint(const char* message, const char* fileName, long lineNum) const
std::string ContourTree::DebugPrint(const char* message, const char* fileName, long lineNum) const
{ // DebugPrint()
#ifdef DEBUG_PRINT
std::cout << "---------------------------" << std::endl;
std::cout << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4)
<< lineNum << std::endl;
std::cout << std::left << std::string(message) << std::endl;
std::cout << "Contour Tree Contains: " << std::endl;
std::cout << "---------------------------" << std::endl;
std::cout << std::endl;
std::stringstream resultStream;
resultStream << std::endl;
resultStream << "---------------------------" << std::endl;
resultStream << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4)
<< lineNum << std::endl;
resultStream << std::left << std::string(message) << std::endl;
resultStream << "Contour Tree Contains: " << std::endl;
resultStream << "---------------------------" << std::endl;
resultStream << std::endl;
this->PrintContent(resultStream);
return resultStream.str();
this->PrintContent();
#else
// Avoid unused parameter warnings
(void)message;
(void)fileName;
(void)lineNum;
#endif
} // DebugPrint()
void ContourTree::PrintDotSuperStructure() const

@ -1051,7 +1051,7 @@ void ContourTreeMaker::DebugPrint(const char* message, const char* fileName, lon
// this->JoinTree.DebugPrint((childString + std::string(": Join Tree")).c_str(), fileName, lineNum);
// this->SplitTree.DebugPrint((childString + std::string(": Split Tree")).c_str(), fileName, lineNum);
this->ContourTreeResult.DebugPrint(
std::cout << this->ContourTreeResult.DebugPrint(
(childString + std::string(": Contour Tree")).c_str(), fileName, lineNum);
std::cout
<< "==========================================================================================="

@ -78,7 +78,7 @@ namespace contourtree_augmented
// local constants to allow changing the spacing as needed
constexpr int PRINT_WIDTH = 12;
constexpr int PREFIX_WIDTH = 24;
constexpr int PREFIX_WIDTH = 30;
template <typename T, typename StorageType>
void PrintValues(std::string label,
@ -263,9 +263,45 @@ inline void PrintLabelledDataBlock(std::string label,
} // PrintLabelledDataBlock()
// routine for printing list of edge pairs. Used, e.g., to print the sorted list of saddle peaks from the ContourTree
inline void PrintEdgePairArray(const EdgePairArray& edgePairArray,
// routine for printing list of edge pairs in row format, i.e., First and Second of the Edge
// are printed separated. Used, e.g.,in standard debug.
inline void PrintEdgePairArray(std::string label,
const EdgePairArray& edgePairArray,
vtkm::Id nIndices,
std::ostream& outStream = std::cout)
{ // PrintEdgePairArray()
// -1 means full size
if (nIndices == -1)
{
nIndices = edgePairArray.GetNumberOfValues();
}
// now print them out
auto edgePairArrayConstPortal = edgePairArray.ReadPortal();
// print the high end
PrintLabel(label + " First", outStream);
for (vtkm::Id superarc = 0; superarc < nIndices; superarc++)
{ // per edge
outStream << std::right << std::setw(PRINT_WIDTH)
<< edgePairArrayConstPortal.Get(superarc).first << " ";
}
outStream << std::endl;
// print the low end
PrintLabel(label + " Second", outStream);
for (vtkm::Id superarc = 0; superarc < nIndices; superarc++)
{ // per superarc
outStream << std::right << std::setw(PRINT_WIDTH)
<< edgePairArrayConstPortal.Get(superarc).second << std::endl;
} // per superarc
outStream << std::endl;
} // PrintEdgePairArray()
// routine for printing list of edge pairs in column format (first, second).
// Used, e.g., to print the sorted list of saddle peaks from the ContourTree
inline void PrintEdgePairArrayColumnLayout(const EdgePairArray& edgePairArray,
std::ostream& outStream = std::cout)
{ // PrintEdgePairArray()
// now print them out
auto edgePairArrayConstPortal = edgePairArray.ReadPortal();

@ -75,8 +75,15 @@ constexpr vtkm::Id IS_ASCENDING = std::numeric_limits<vtkm::Id>::max() / 16 + 1;
constexpr vtkm::Id INDEX_MASK = std::numeric_limits<vtkm::Id>::max() / 16; //0x07FFFFFF || 0x07FFFFFFFFFFFFFF
constexpr vtkm::Id CV_OTHER_FLAG = std::numeric_limits<vtkm::Id>::max() / 8 + 1; //0x10000000 || 0x1000000000000000
constexpr vtkm::Id ELEMENT_EXISTS = std::numeric_limits<vtkm::Id>::max() / 4 + 1; //0x20000000 || 0x2000000000000000 , same as IS_SUPERNODE
// clang-format on
// flags for testing regular vertices
constexpr vtkm::Id IS_LOWER_LEAF = static_cast<vtkm::Id>(0);
constexpr vtkm::Id IS_UPPER_LEAF = static_cast<vtkm::Id>(1);
constexpr vtkm::Id IS_REGULAR = static_cast<vtkm::Id>(2);
constexpr vtkm::Id IS_SADDLE = static_cast<vtkm::Id>(3);
constexpr vtkm::Id IS_ATTACHMENT = static_cast<vtkm::Id>(4);
// clang-format on
using IdArrayType = vtkm::cont::ArrayHandle<vtkm::Id>;
using EdgePair = vtkm::Pair<vtkm::Id, vtkm::Id>; // here EdgePair.first=low and EdgePair.second=high

@ -50,8 +50,8 @@
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_boundary_restricted_augmented_contour_tree_h
#define vtk_m_worklet_contourtree_distributed_boundary_restricted_augmented_contour_tree_h
#ifndef vtk_m_worklet_contourtree_distributed_boundary_tree_h
#define vtk_m_worklet_contourtree_distributed_boundary_tree_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
@ -74,11 +74,11 @@ namespace contourtree_distributed
///
/// A contour tree for boundary vertices with the interior abstracted.
/// This is primarily a data storage class. The actual constuction of
/// the BRACT is performed by the BoundaryRestrictedAugmentedContourTreeMaker
/// the BRACT is performed by the BoundaryTreeMaker
/// (BRACTMaker). As a data store, this class primarily stores a set of
/// arrays and provides convenience functions for interacting with the
/// the data, e.g., to export the data to dot.
class BoundaryRestrictedAugmentedContourTree
class BoundaryTree
{ // class BRACT
public:
// for each vertex, we store the index
@ -87,6 +87,9 @@ public:
// and the ID of the vertex it connects to (or NO_SUCH_ELEMENT)
vtkm::worklet::contourtree_augmented::IdArrayType Superarcs;
// constructor
BoundaryTree() {}
// prints the contents of the BRACT for comparison with sweep and merge
std::string Print();
@ -96,21 +99,23 @@ public:
const Mesh& mesh,
const FieldArrayType& fieldArray,
const vtkm::Id3 blockOrigin,
const vtkm::Id3 blockSize,
const vtkm::Id3 globalSize) const;
// prints the contents of the BRACT as a dot file using global IDs (version for CT mesh)
template <typename FieldType>
std::string PrintGlobalDot(
const char* label,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh);
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh) const;
// prints the contents of the BRACT in debug format
std::string DebugPrint(const char* message, const char* fileName, long lineNum);
void PrintContent(std::ostream& outStream) const;
std::string DebugPrint(const char* message, const char* fileName, long lineNum) const;
};
// prints the contents of the BRACT for comparison with sweep and merge
std::string BoundaryRestrictedAugmentedContourTree::Print()
std::string BoundaryTree::Print()
{ // Print
// Use string steam to record text so the user can print it however they like
std::stringstream resultStream;
@ -138,25 +143,22 @@ std::string BoundaryRestrictedAugmentedContourTree::Print()
// secondary version which takes the mesh as a parameter
template <typename Mesh, typename FieldArrayType>
std::string BoundaryRestrictedAugmentedContourTree::PrintGlobalDot(const char* label,
const Mesh& mesh,
const FieldArrayType& fieldArray,
const vtkm::Id3 blockOrigin,
const vtkm::Id3 globalSize) const
std::string BoundaryTree::PrintGlobalDot(const char* label,
const Mesh& mesh,
const FieldArrayType& fieldArray,
const vtkm::Id3 blockOrigin,
const vtkm::Id3 blockSize,
const vtkm::Id3 globalSize) const
{ // PrintGlobalDot
VTKM_IS_ARRAY_HANDLE(FieldArrayType);
std::stringstream resultStream;
// print the header information
resultStream << "digraph BRACT" << std::endl;
resultStream << "\t{" << std::endl;
resultStream << "\tlabel=\"" << label << "\"\n\tlabelloc=t\n\tfontsize=30" << std::endl;
// create a relabeler
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler relabeler(blockOrigin[0],
blockOrigin[1],
blockOrigin[2],
mesh.NumRows,
mesh.NumCols,
globalSize[0],
globalSize[1]);
vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler relabeler(
blockOrigin, blockSize, globalSize);
// loop through all nodes
auto vertexIndexPortal = this->VertexIndex.ReadPortal();
@ -203,9 +205,9 @@ std::string BoundaryRestrictedAugmentedContourTree::PrintGlobalDot(const char* l
// prints the contents of the BRACT as a dot file using global IDs (version for CT mesh)
template <typename FieldType>
std::string BoundaryRestrictedAugmentedContourTree::PrintGlobalDot(
std::string BoundaryTree::PrintGlobalDot(
const char* label,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh)
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh) const
{ //PrintGlobalDot
std::stringstream resultStream;
// print the header information
@ -216,7 +218,7 @@ std::string BoundaryRestrictedAugmentedContourTree::PrintGlobalDot(
// loop through all nodes
auto vertexIndexPortal = this->VertexIndex.ReadPortal();
auto globalMeshIndexPortal = mesh.GlobalMeshIndex.ReadPortal();
auto sortedValuesPortal = mesh.SortedValued.ReadPortal();
auto sortedValuesPortal = mesh.SortedValues.ReadPortal();
auto superarcsPortal = this->Superarcs.ReadPortal();
for (vtkm::Id node = 0; node < this->VertexIndex.GetNumberOfValues(); node++)
{ // per node
@ -255,11 +257,21 @@ std::string BoundaryRestrictedAugmentedContourTree::PrintGlobalDot(
} //PrintGlobalDot
// debug routine
inline std::string BoundaryRestrictedAugmentedContourTree::DebugPrint(const char* message,
const char* fileName,
long lineNum)
void BoundaryTree::PrintContent(std::ostream& outStream) const
{
vtkm::worklet::contourtree_augmented::PrintHeader(this->VertexIndex.GetNumberOfValues(),
outStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Vertex Index", this->VertexIndex, -1, outStream);
vtkm::worklet::contourtree_augmented::PrintIndices("Superarcs", this->Superarcs, -1, outStream);
}
inline std::string BoundaryTree::DebugPrint(const char* message,
const char* fileName,
long lineNum) const
{ // DebugPrint
std::stringstream resultStream;
#ifdef DEBUG_PRINT
resultStream << "[CUTHERE]-------------------------------------------------------" << std::endl;
resultStream << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4)
<< lineNum << std::endl;
@ -267,16 +279,16 @@ inline std::string BoundaryRestrictedAugmentedContourTree::DebugPrint(const char
resultStream << "Boundary Restricted Augmented Contour Tree Contains: " << std::endl;
resultStream << "----------------------------------------------------------------" << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(this->VertexIndex.GetNumberOfValues(),
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Vertex Index", this->VertexIndex, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Superarcs", this->Superarcs, -1, resultStream);
this->PrintContent(resultStream);
resultStream << "---------------------------" << std::endl;
resultStream << std::endl;
resultStream << std::flush;
#else
(void)message;
(void)fileName;
(void)lineNum;
#endif
return resultStream.str();
} // DebugPrint

@ -13,11 +13,19 @@ set(headers
SpatialDecomposition.h
MultiBlockContourTreeHelper.h
MergeBlockFunctor.h
BoundaryRestrictedAugmentedContourTree.h
BoundaryRestrictedAugmentedContourTreeMaker.h
InteriorForest.h
BoundaryTree.h
BoundaryTreeMaker.h
HierarchicalContourTree.h
HierarchicalContourTreeDeviceData.h
TreeGrafter.h
ComputeDistributedContourTreeFunctor.h
DistributedContourTreeBlockData.h
PrintGraph.h
)
vtkm_declare_headers(${headers})
add_subdirectory(bract_maker)
add_subdirectory(boundary_tree_maker)
add_subdirectory(tree_grafter)
add_subdirectory(hierarchical_contour_tree)

@ -0,0 +1,386 @@
//============================================================================
// 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_computedistributedcontourtreefunctor_h
#define vtk_m_worklet_contourtree_distributed_computedistributedcontourtreefunctor_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Types.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 contourtree_distributed
{
// Functor needed so we can discover the FieldType and DeviceAdapter template parameters to call MergeWith
struct MergeContourTreeMeshFunctor
{
template <typename DeviceAdapterTag, typename FieldType>
bool operator()(DeviceAdapterTag,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& in,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& out) const
{
out.template MergeWith<DeviceAdapterTag>(in);
return true;
}
};
// Functor used by DIY reduce the merge data blocks in parallel
template <typename FieldType>
class ComputeDistributedContourTreeFunctor
{
public:
ComputeDistributedContourTreeFunctor(vtkm::Id3 globalSize)
: GlobalSize(globalSize)
{
}
void operator()(
vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<FieldType>*
block, // local Block.
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) const
{
const auto selfid = rp.gid();
//DEBUGOUT//std::cout << "Entering ComputeDistributedContourTreeFunctor::operator() for GID " << selfid << std::endl;
// Get rank (for debug output only)
const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank();
// Here we do the deque first before the send due to the way the iteration is handled in DIY, i.e., in each iteration
// A block needs to first collect the data from its neighours and then send the combined block to its neighbours
// for the next iteration.
// 1. dequeue the block and compute the new contour tree and contour tree mesh for the block if we have the hight GID
std::vector<int> incoming;
rp.incoming(incoming);
//std::cout << "Incoming size is " << incoming.size() << std::endl;
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.
if (ingid != selfid)
{
vtkm::Id3 otherBlockOrigin;
rp.dequeue(ingid, otherBlockOrigin);
vtkm::Id3 otherBlockSize;
rp.dequeue(ingid, otherBlockSize);
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType> otherContourTreeMesh;
rp.dequeue(ingid, otherContourTreeMesh);
#if 0
// FIXME: Delete after debugging
std::cout << "Our block has extents: " << block->BlockOrigin << " " << block->BlockSize << std::endl;
std::cout << "Received block from " << ingid << " with extents: " << otherBlockOrigin << " " << otherBlockSize << std::endl;
std::cout << "=================== BEFORE COMBINING =====================" << std::endl;
block->ContourTreeMeshes.back().DebugPrint("OUR CTM", __FILE__, __LINE__);
otherContourTreeMesh.DebugPrint("OTHER CTM", __FILE__, __LINE__);
#endif
#if 0
char buffer[256];
std::snprintf(buffer, sizeof(buffer), "BeforeCombine_MyMesh_ContourTreeMesh_Rank%lld_Round%d.txt", rank, rp.round());
block->ContourTreeMeshes.back().Save(buffer);
std::snprintf(buffer, sizeof(buffer), "BeforeCombine_Other_ContourTreeMesh_Rank%lld_Round%d.txt", rank, rp.round());
otherContourTreeMesh.Save(buffer);
#endif
// Merge the two contour tree meshes
vtkm::cont::TryExecute(
MergeContourTreeMeshFunctor{}, otherContourTreeMesh, block->ContourTreeMeshes.back());
char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"AfterCombine_ContourTreeMesh_Rank%d_Block%d_Round%d.txt",
static_cast<int>(rank),
static_cast<int>(block->BlockIndex),
rp.round());
block->ContourTreeMeshes.back().Save(buffer);
#if 0
// FIXME: Delete after debugging
std::cout << "================== AFTER COMBINING =================" << std::endl;
std::cout << "OUR CTM" << std::endl;
block->ContourTreeMeshes.back().DebugPrint("OUR CTM", __FILE__, __LINE__);
#endif
// Compute the origin and size of the new block
vtkm::Id3 currBlockOrigin{
std::min(otherBlockOrigin[0], block->BlockOrigin[0]),
std::min(otherBlockOrigin[1], block->BlockOrigin[1]),
std::min(otherBlockOrigin[2], block->BlockOrigin[2]),
};
vtkm::Id3 currBlockMaxIndex{ // Needed only to compute the block size
std::max(otherBlockOrigin[0] + otherBlockSize[0],
block->BlockOrigin[0] + block->BlockSize[0]),
std::max(otherBlockOrigin[1] + otherBlockSize[1],
block->BlockOrigin[1] + block->BlockSize[1]),
std::max(otherBlockOrigin[2] + otherBlockSize[2],
block->BlockOrigin[2] + block->BlockSize[2])
};
vtkm::Id3 currBlockSize{ currBlockMaxIndex[0] - currBlockOrigin[0],
currBlockMaxIndex[1] - currBlockOrigin[1],
currBlockMaxIndex[2] - currBlockOrigin[2] };
// Compute the contour tree from our merged mesh
vtkm::Id currNumIterations;
block->ContourTrees.emplace_back(); // Create new empty contour tree object
vtkm::worklet::contourtree_augmented::IdArrayType currSortOrder;
vtkm::worklet::ContourTreeAugmented worklet;
vtkm::Id3 maxIdx{ currBlockOrigin[0] + currBlockSize[0] - 1,
currBlockOrigin[1] + currBlockSize[1] - 1,
currBlockOrigin[2] + currBlockSize[2] - 1 };
auto meshBoundaryExecObj = block->ContourTreeMeshes.back().GetMeshBoundaryExecutionObject(
this->GlobalSize, currBlockOrigin, maxIdx);
worklet.Run(block->ContourTreeMeshes.back()
.SortedValues, // Unused param. Provide something to keep the API happy
block->ContourTreeMeshes.back(),
block->ContourTrees.back(),
currSortOrder,
currNumIterations,
1, // Fully augmented FIXME: Verify
meshBoundaryExecObj);
#if 0
// FIXME: Delete after debugging
std::cout << "=================== BEGIN: COMBINED CONTOUR TREE =========================" << std::endl;
block->ContourTrees.back().PrintContent();
std::cout << "=================== END: COMBINED CONTOUR TREE =========================" << std::endl;
#endif
// Update block extents
block->BlockOrigin = currBlockOrigin;
block->BlockSize = currBlockSize;
}
}
// If we are not in the first round (contour tree mesh for that round was pre-computed
// in filter outside functor) and if we are sending to someone else (i.e., not in
// last round) then compute contour tree mesh to send and save it.
if (rp.round() != 0 && rp.out_link().size() != 0)
{
#if 0
// If we have the fully augmented contour tree
block->ContourTreeMeshes.emplace_back(
block->ContourTrees.back().Arcs, block->ContourTreeMeshes.back()
);
char buffer[256];
std::snprintf(buffer, sizeof(buffer), "CombinedMeshes_GID%d_Round%d.txt", selfid, rp.round());
block->ContourTreeMeshes.back().Save(buffer);
#endif
vtkm::Id3 maxIdx{ block->BlockOrigin[0] + block->BlockSize[0] - 1,
block->BlockOrigin[1] + block->BlockSize[1] - 1,
block->BlockOrigin[2] + block->BlockSize[2] - 1 };
char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"BRACTInputCTM_Rank%d_Block%d_Round%d.ctm_txt",
static_cast<int>(rank),
static_cast<int>(block->BlockIndex),
static_cast<int>(block->ContourTreeMeshes.size() - 1));
block->ContourTreeMeshes.back().Save(buffer);
std::snprintf(buffer,
sizeof(buffer),
"BRACTComputation_Rank%d_Block%d_Round%d.txt",
static_cast<int>(rank),
static_cast<int>(block->BlockIndex),
rp.round());
std::ofstream os(buffer);
os << "Block Origin: " << block->BlockOrigin << " Block Size: " << block->BlockSize
<< " Block MaxIdx: " << maxIdx << std::endl;
os << "================= INPUT ===================" << std::endl;
os << "+++++++++++++++++ Contour Tree +++++++++++++++++" << std::endl;
block->ContourTrees.back().PrintContent(os);
os << "+++++++++++++++++ Contour Tree Mesh +++++++++++++++++" << std::endl;
block->ContourTreeMeshes.back().PrintContent(os);
// TODO: GET THIS COMPILING
// save the corresponding .gv file for the contour tree mesh
// std::string contourTreeMeshFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(block->BlockIndex)) + "_Round_" + std::to_string(rp.round()) + "_Partner_" + std::to_string(ingid) + std::string("_Step_0_Combined_Mesh.gv");
// std::ofstream contourTreeMeshFile(contourTreeMeshFileName);
// contourTreeMeshFile << vtkm::worklet::contourtree_distributed::ContourTreeMeshDotGraphPrint<FieldType>
// ( std::string("Block ") + std::to_string(static_cast<int>(block->BlockIndex)) + " Round " + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string(" Step 0 Combined Mesh"),
// block->ContourTreeMeshes.back(), worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_ALL);
//
// // and the ones for the contour tree regular and superstructures
// std::string regularStructureFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(block->BlockIndex)) + "_Round_" + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string("_Step_1_Contour_Tree_Regular_Structure.gv");
// std::ofstream regularStructureFile(regularStructureFileName);
// regularStructureFile << worklet::contourtree_distributed::ContourTreeDotGraphPrint<T, MeshType, vtkm::worklet::contourtree_augmented::IdArrayType()>
// ( std::string("Block ") + std::to_string(static_cast<int>(block->BlockIndex)) + " Round " + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string(" Step 1 Contour Tree Regular Structure"),
// block->Meshes.back(),
// block->ContourTrees.back(),
// worklet::contourtree_distributed::SHOW_REGULAR_STRUCTURE|worklet::contourtree_distributed::SHOW_ALL_IDS);
//
// std::string superStructureFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(block->BlockIndex)) + "_Round_" + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string("_Step_2_Contour_Tree_Super_Structure.gv");
// std::ofstream superStructureFile(superStructureFileName);
// superStructureFile << worklet::contourtree_distributed::ContourTreeDotGraphPrint<T, MeshType, vtkm::worklet::contourtree_augmented::IdArrayType()>
// ( std::string("Block ") + std::to_string(static_cast<int>(block->BlockIndex)) + " Round " + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string(" Step 2 Contour Tree Super Structure"),
// block->Meshes.back(),
// block->ContourTrees.back(),
// worklet::contourtree_distributed::SHOW_SUPER_STRUCTURE|worklet::contourtree_distributed::SHOW_HYPER_STRUCTURE|worklet::contourtree_distributed::SHOW_ALL_IDS|worklet::contourtree_distributed::SHOW_ALL_SUPERIDS|worklet::contourtree_distributed::SHOW_ALL_HYPERIDS);
// Compute BRACT
vtkm::worklet::contourtree_distributed::BoundaryTree boundaryTree;
// ... Get the mesh boundary object
auto meshBoundaryExecObj = block->ContourTreeMeshes.back().GetMeshBoundaryExecutionObject(
this->GlobalSize, block->BlockOrigin, maxIdx);
// Make the BRACT and InteriorForest (i.e., residue)
block->InteriorForests.emplace_back();
std::cout << "Calling BRACT Maker" << std::endl;
auto boundaryTreeMaker = vtkm::worklet::contourtree_distributed::BoundaryTreeMaker<
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>,
vtkm::worklet::contourtree_augmented::MeshBoundaryContourTreeMeshExec>(
&(block->ContourTreeMeshes.back()),
meshBoundaryExecObj,
block->ContourTrees.back(),
&boundaryTree,
&(block->InteriorForests.back()));
// Construct the BRACT and InteriorForest. Since we are working on a ContourTreeMesh we do
// not need to provide and IdRelabeler here in order to compute the InteriorForest
boundaryTreeMaker.Construct();
os << "================= OUTPUT ===================" << std::endl;
os << "+++++++++++++++++ BRACT +++++++++++++++++++" << std::endl;
os << boundaryTree.DebugPrint("validate", __FILE__, __LINE__);
os << "+++++++++++++++++ BRACT Contour Tree Mesh +++++++++++++++++++" << std::endl;
//char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"GID %d, Round %d, Block %d, Computed by BRACTMaker",
selfid,
rp.round(),
static_cast<int>(block->BlockIndex));
std::string debug_dot = boundaryTree.PrintGlobalDot(buffer, block->ContourTreeMeshes.back());
std::snprintf(buffer,
sizeof(buffer),
"BRACT_Rank%d__Block%d_Round%d.gv",
static_cast<int>(rank),
static_cast<int>(block->BlockIndex),
rp.round());
std::ofstream dotStream(buffer);
dotStream << debug_dot;
// Construct contour tree mesh from BRACT
block->ContourTreeMeshes.emplace_back(
boundaryTree.VertexIndex, boundaryTree.Superarcs, block->ContourTreeMeshes.back());
block->ContourTreeMeshes.back().PrintContent(os);
//char buffer[256];
std::snprintf(buffer,
sizeof(buffer),
"CombinedMeshes_Rank%d_Block%d_Round%lu.ctm_txt",
static_cast<int>(rank),
static_cast<int>(block->BlockIndex),
block->ContourTreeMeshes.size() - 1);
block->ContourTreeMeshes.back().Save(buffer);
}
// TODO: GET THIS COMPILING
// save the Boundary Tree as a dot file
// std::string boundaryTreeFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(block->BlockIndex)) + "_Round_" + std::to_string(rp.round()) + "_Partner_" + std::to_string(ingid) + std::string("_Step_3_Boundary_Tree.gv");
// std::ofstream boundaryTreeFile(boundaryTreeFileName);
// boundaryTreeFile << vtkm::worklet::contourtree_distributed::BoundaryTreeDotGraphPrint
// ( std::string("Block ") + std::to_string(static_cast<int>(block->BlockIndex)) + " Round " + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string(" Step 3 Boundary Tree"),
// block->Meshes.back()],
// block->BoundaryTrees.back());
//
// // and save the Interior Forest as another dot file
// std::string interiorForestFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(block->BlockIndex)) + "_Round_" + std::to_string(rp.round()) + "_Partner_" + std::to_string(ingid) + std::string("_Step_4_Interior_Forest.gv");
// std::ofstream interiorForestFile(interiorForestFileName);
// interiorForestFileName << InteriorForestDotGraphPrintFile<MeshType>
// ( std::string("Block ") + std::to_string(static_cast<int>(block->BlockIndex)) + " Round " + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string(" Step 4 Interior Forest"),
// block->InteriorForests.back(),
// block->ContourTrees.back(),
// block->BoundaryTrees.back(),
// block->Meshes.back());
//
// // save the corresponding .gv file
// std::string boundaryTreeMeshFileName = std::string("Rank_") + std::to_string(static_cast<int>(rank)) + std::string("_Block_") + std::to_string(static_cast<int>(block->BlockIndex)) + "_Round_" + std::to_string(rp.round()) + "_Partner_" + std::to_string(ingid) + std::string("_Step_5_Boundary_Tree_Mesh.gv");
// std::ofstream boundaryTreeMeshFile(boundaryTreeMeshFileName);
// boundaryTreeMeshFile << vtkm::worklet::contourtree_distributed::ContourTreeMeshDotGraphPrint<FieldType>
// ( std::string("Block ") + std::to_string(static_cast<int>(block->BlockIndex)) + " Round " + std::to_string(rp.round()) + " Partner " + std::to_string(ingid) + std::string(" Step 5 Boundary Tree Mesh"),
// block->ContourTreeMeshes.back(),
// worklet::contourtree_distributed::SHOW_CONTOUR_TREE_MESH_ALL);
// Send our current block (which is either our original block or the one we just combined from the ones we received) to our next neighbour.
// Once a rank has send his block (either in its orignal or merged form) it is done with the reduce
for (int cc = 0; cc < rp.out_link().size(); ++cc)
{
auto target = rp.out_link().target(cc);
if (target.gid != selfid)
{
rp.enqueue(target, block->BlockOrigin);
rp.enqueue(target, block->BlockSize);
rp.enqueue(target, block->ContourTreeMeshes.back());
}
}
} //end ComputeDistributedContourTreeFunctor
private:
vtkm::Id3 GlobalSize; // Extends of the global mesh
};
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -78,7 +78,7 @@ struct ContourTreeBlockData
// ContourTreeMesh data
vtkm::Id NumVertices;
// TODO Should be able to remove sortOrder here, but we need to figure out what to return in the worklet instead
vtkm::worklet::contourtree_augmented::IdArrayType SortOrder;
// vtkm::worklet::contourtree_augmented::IdArrayType SortOrder;
vtkm::cont::ArrayHandle<FieldType> SortedValue;
vtkm::worklet::contourtree_augmented::IdArrayType GlobalMeshIndex;
vtkm::worklet::contourtree_augmented::IdArrayType Neighbours;
@ -108,7 +108,6 @@ struct Serialization<vtkm::worklet::contourtree_distributed::ContourTreeBlockDat
const vtkm::worklet::contourtree_distributed::ContourTreeBlockData<FieldType>& block)
{
vtkmdiy::save(bb, block.NumVertices);
vtkmdiy::save(bb, block.SortOrder);
vtkmdiy::save(bb, block.SortedValue);
vtkmdiy::save(bb, block.GlobalMeshIndex);
vtkmdiy::save(bb, block.Neighbours);
@ -124,7 +123,6 @@ struct Serialization<vtkm::worklet::contourtree_distributed::ContourTreeBlockDat
vtkm::worklet::contourtree_distributed::ContourTreeBlockData<FieldType>& block)
{
vtkmdiy::load(bb, block.NumVertices);
vtkmdiy::load(bb, block.SortOrder);
vtkmdiy::load(bb, block.SortedValue);
vtkmdiy::load(bb, block.GlobalMeshIndex);
vtkmdiy::load(bb, block.Neighbours);

@ -0,0 +1,131 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//=============================================================================
//
// 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_contourtreeblockdata_h
#define vtk_m_worklet_contourtree_distributed_contourtreeblockdata_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_augmented/meshtypes/ContourTreeMesh.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 contourtree_distributed
{
template <typename FieldType>
struct DistributedContourTreeBlockData
{
static void* create() { return new DistributedContourTreeBlockData<FieldType>; }
static void destroy(void* b)
{
delete static_cast<DistributedContourTreeBlockData<FieldType>*>(b);
}
vtkm::Id BlockIndex;
std::vector<vtkm::worklet::contourtree_augmented::ContourTree> ContourTrees;
std::vector<vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>> ContourTreeMeshes;
std::vector<vtkm::worklet::contourtree_distributed::InteriorForest> InteriorForests;
// Block metadata
vtkm::Id3 BlockOrigin; // Origin of the data block
vtkm::Id3 BlockSize; // Extends of the data block
};
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
namespace vtkmdiy
{
// Struct to serialize ContourTreeMesh objects (i.e., load/save) needed in parralle for DIY
template <typename FieldType>
struct Serialization<vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>>
{
static void save(vtkmdiy::BinaryBuffer& bb,
const vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& ctm)
{
vtkmdiy::save(bb, ctm.NumVertices);
//vtkmdiy::save(bb, ctm.SortOrder);
vtkmdiy::save(bb, ctm.SortedValues);
vtkmdiy::save(bb, ctm.GlobalMeshIndex);
vtkmdiy::save(bb, ctm.Neighbours);
vtkmdiy::save(bb, ctm.FirstNeighbour);
vtkmdiy::save(bb, ctm.MaxNeighbours);
}
static void load(vtkmdiy::BinaryBuffer& bb,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& ctm)
{
vtkmdiy::load(bb, ctm.NumVertices);
//vtkmdiy::load(bb, ctm.SortOrder);
vtkmdiy::load(bb, ctm.SortedValues);
vtkmdiy::load(bb, ctm.GlobalMeshIndex);
vtkmdiy::load(bb, ctm.Neighbours);
vtkmdiy::load(bb, ctm.FirstNeighbour);
vtkmdiy::load(bb, ctm.MaxNeighbours);
}
};
} // namespace mangled_vtkmdiy_namespace
#endif

@ -39,23 +39,42 @@
//
//=============================================================================
//
// 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.
// Parallel Peak Pruning v. 2.0
//
// The PPP2 algorithm and software were jointly developed by
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
// Started June 15, 2017
//
// Copyright Hamish Carr, University of Leeds
//
// HierarchicalContourTree.cpp - Hierarchical version of contour tree that captures all of the
// superarcs relevant for a particular block. It is constructed by grafting missing edges
// into the tree at all levels
//
//=======================================================================================
//
// COMMENTS:
//
// There are several significant differences from the ContourTree class, in particular the
// semantics of storage:
// i. Hyper arcs are processed inside to outside instead of outside to inside
// This is to allow the superarcs in higher blocks to be a prefix of those in lower blocks
// We can do this by inverting the loop order and processing each level separately, so
// we don't need to renumber (whew!)
// ii. If the superarc is -1, it USED to mean the root of the tree. Now it can also mean
// the root of a lower-level subtree. in this case, the superparent will show which
// existing superarc it inserts into.
//
//=======================================================================================
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_contour_tree_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_contour_tree_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/contourtree_augmented/meshtypes/ContourTreeMesh.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/FindRegularByGlobal.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/InitalizeSuperchildrenWorklet.h>
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/PermuteComparator.h>
namespace vtkm
{
@ -67,14 +86,741 @@ namespace contourtree_distributed
/// \brief Hierarchical Contour Tree data structure
///
/// TODO this class still needs to be implemented
/// This class contains all the structures to construct/store the HierarchicalContourTree.
/// Functions used on the Device are then implemented in the HierarchicalContourTreeDeviceData
/// class which stores the prepared array portals we need for those functions.
///
template <typename FieldType>
class HierarchicalContourTree
{
public:
VTKM_EXEC_CONT
HierarchicalContourTree();
// REGULAR arrays: i.e. over all nodes in the tree, including regular
// the full list of global IDs for the regular nodes
vtkm::worklet::contourtree_augmented::IdArrayType RegularNodeGlobalIds;
// we will also need to track the data values
vtkm::cont::ArrayHandle<FieldType> DataValues;
// an array to support searching by global ID
// given a global ID, find its position in the regular node index
// To do so, we keep an index by global ID of their positions in the array
vtkm::worklet::contourtree_augmented::IdArrayType RegularNodeSortOrder;
// the supernode ID for each regular node: for most, this will be NO_SUCH_ELEMENT
// but this makes lookups for supernode ID a lot easier
vtkm::worklet::contourtree_augmented::IdArrayType Regular2Supernode;
// the superparent for each regular node
vtkm::worklet::contourtree_augmented::IdArrayType Superparents;
// SUPER arrays: i.e. over all supernodes in the tree
// the ID in the globalID array
vtkm::worklet::contourtree_augmented::IdArrayType Supernodes;
// where the supernode connects to
vtkm::worklet::contourtree_augmented::IdArrayType Superarcs;
// the hyperparent for each supernode
vtkm::worklet::contourtree_augmented::IdArrayType Hyperparents;
// the hypernode ID for each supernode: often NO_SUCH_ELEMENT
// but it makes lookups easier
vtkm::worklet::contourtree_augmented::IdArrayType Super2Hypernode;
// which iteration & round the vertex is transferred in
// the second of these is the same as "whenTransferred", but inverted order
vtkm::worklet::contourtree_augmented::IdArrayType WhichRound;
vtkm::worklet::contourtree_augmented::IdArrayType WhichIteration;
// HYPER arrays: i.e. over all hypernodes in the tree
// the ID in the supernode array
vtkm::worklet::contourtree_augmented::IdArrayType Hypernodes;
// where the hypernode connects to
vtkm::worklet::contourtree_augmented::IdArrayType Hyperarcs;
// the number of child supernodes on the superarc (including the start node)
// and not including any inserted in the hierarchy
vtkm::worklet::contourtree_augmented::IdArrayType Superchildren;
// how many rounds of fan-in were used to construct it
vtkm::Id NumRounds;
// TODO: HAC to confirm where the next 6 arrays are used.
// HAC: these arrays store the numbers of reg/super/hyper nodes at each level of the hierarchy
// They are filled in from the top down, and are fundamentally CPU side control variables
// They will be needed for hypersweeps.
// May be for hypersweeps later on. SHOULD be primarily CPU side
/// arrays holding the logical size of the arrays at each level
vtkm::worklet::contourtree_augmented::IdArrayType NumRegularNodesInRound;
vtkm::worklet::contourtree_augmented::IdArrayType NumSupernodesInRound;
vtkm::worklet::contourtree_augmented::IdArrayType NumHypernodesInRound;
/// how many iterations needed for the hypersweep at each level
vtkm::worklet::contourtree_augmented::IdArrayType NumIterations;
/// vectors tracking the segments used in each iteration of the hypersweep
std::vector<vtkm::worklet::contourtree_augmented::IdArrayType> FirstSupernodePerIteration;
std::vector<vtkm::worklet::contourtree_augmented::IdArrayType> FirstHypernodePerIteration;
/// routine to create a FindRegularByGlobal object that we can use as an input for worklets to call the function
FindRegularByGlobal GetFindRegularByGlobal()
{
return FindRegularByGlobal(this->RegularNodeSortOrder, this->RegularNodeGlobalIds);
}
/// routine to create a FindSuperArcForUnknownNode object that we can use as an input for worklets to call the function
FindSuperArcForUnknownNode<FieldType> GetFindSuperArcForUnknownNode()
{
return FindSuperArcForUnknownNode<FieldType>(this->Superparents,
this->Supernodes,
this->Superarcs,
this->Superchildren,
this->WhichRound,
this->WhichIteration,
this->Hyperparents,
this->Hypernodes,
this->Hyperarcs,
this->RegularNodeGlobalIds,
this->DataValues);
}
/// routine to initialize the hierarchical tree with the top level tree
VTKM_CONT
HierarchicalContourTree() {}
void Initialize(vtkm::Id numRounds,
vtkm::worklet::contourtree_augmented::ContourTree& tree,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh);
/// utility routines for the path probes
std::string RegularString(const vtkm::Id regularId) const;
std::string SuperString(const vtkm::Id superId) const;
std::string HyperString(const vtkm::Id hyperId) const;
/// routine to probe a given node and trace it's hyperpath to the root
std::string ProbeHyperPath(const vtkm::Id regularId, const vtkm::Id maxLength = -1) const;
/// routine to probe a given node and trace it's superpath to the root
std::string ProbeSuperPath(const vtkm::Id regularId, const vtkm::Id maxLength = -1) const;
/// Outputs the Hierarchical Tree in Dot format for visualization
std::string PrintDotSuperStructure(const char* label) const;
/// debug routine
std::string DebugPrint(const char* message, const char* fileName, long lineNum) const;
private:
/// Used internally to Invoke worklets
vtkm::cont::Invoker Invoke;
};
VTKM_EXEC_CONT
template <typename FieldType>
HierarchicalContourTree<FieldType>::HierarchicalContourTree()
{ // constructor
NumRegularNodesInRound.ReleaseResources();
NumSupernodesInRound.ReleaseResources();
NumHypernodesInRound.ReleaseResources();
NumIterations.ReleaseResources();
} // constructor
/// routine to initialize the hierarchical tree with the top level tree
VTKM_CONT
template <typename FieldType>
void HierarchicalContourTree<FieldType>::Initialize(
vtkm::Id numRounds,
vtkm::worklet::contourtree_augmented::ContourTree& tree,
vtkm::worklet::contourtree_augmented::ContourTreeMesh<FieldType>& mesh)
{ // Initialize(..)
// TODO: If any other arrays are only copied in this function but will not be modified then we could just assign instead of copy them and make them const
// set the initial logical size of the arrays: note that we need to keep level 0 separate, so have an extra level at the top
this->NumRounds = numRounds;
{
auto tempZeroArray = vtkm::cont::ArrayHandleConstant<vtkm::Id>(0, this->NumRounds + 1);
vtkm::cont::Algorithm::Copy(tempZeroArray, this->NumIterations);
vtkm::cont::Algorithm::Copy(tempZeroArray, this->NumRegularNodesInRound);
this->NumRegularNodesInRound.WritePortal().Set(this->NumRounds, tree.Nodes.GetNumberOfValues());
vtkm::cont::Algorithm::Copy(tempZeroArray, this->NumSupernodesInRound);
this->NumSupernodesInRound.WritePortal().Set(this->NumRounds,
tree.Supernodes.GetNumberOfValues());
vtkm::cont::Algorithm::Copy(tempZeroArray, this->NumHypernodesInRound);
this->NumHypernodesInRound.WritePortal().Set(this->NumRounds,
tree.Hypernodes.GetNumberOfValues());
}
// copy the iterations of the top level hypersweep - this is +2: one because we are counting inclusively, the second because we need an
// extra one at the end to compute sizes with deltas
this->NumIterations.WritePortal().Set(this->NumRounds, tree.NumIterations);
this->FirstSupernodePerIteration.resize(static_cast<std::size_t>(this->NumRounds + 1));
this->FirstSupernodePerIteration[static_cast<std::size_t>(this->NumRounds)].Allocate(
this->NumIterations.ReadPortal().Get(this->NumRounds) + 2);
this->FirstHypernodePerIteration.resize(static_cast<std::size_t>(this->NumRounds + 1));
this->FirstHypernodePerIteration[static_cast<std::size_t>(this->NumRounds)].Allocate(
this->NumIterations.ReadPortal().Get(this->NumRounds) + 2);
// now copy in the details
// TODO: These arrays are allocated above. Make sure Copy does not shrink the size of the array if it is allocated larger
vtkm::cont::Algorithm::Copy(
tree.FirstSupernodePerIteration,
this->FirstSupernodePerIteration[static_cast<std::size_t>(this->NumRounds)]);
vtkm::cont::Algorithm::Copy(
tree.FirstHypernodePerIteration,
this->FirstHypernodePerIteration[static_cast<std::size_t>(this->NumRounds)]);
// set the sizes for the arrays
// vtkm::Id treeSize = tree.Superparents.GetNumberOfValues(); // TODO: defined in original code but seems to be unused
this->RegularNodeGlobalIds.Allocate(tree.Nodes.GetNumberOfValues());
this->DataValues.Allocate(mesh.SortedValues.GetNumberOfValues());
this->RegularNodeSortOrder.Allocate(tree.Nodes.GetNumberOfValues());
this->Superparents.Allocate(tree.Superparents.GetNumberOfValues());
{
auto tempNSE = vtkm::cont::ArrayHandleConstant<vtkm::Id>(
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT, tree.Nodes.GetNumberOfValues());
vtkm::cont::Algorithm::Copy(tempNSE, this->Regular2Supernode);
}
this->Supernodes.Allocate(tree.Supernodes.GetNumberOfValues());
this->Superarcs.Allocate(tree.Superarcs.GetNumberOfValues());
this->Hyperparents.Allocate(tree.Hyperparents.GetNumberOfValues());
{
auto tempNSE = vtkm::cont::ArrayHandleConstant<vtkm::Id>(
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT, tree.Supernodes.GetNumberOfValues());
vtkm::cont::Algorithm::Copy(tempNSE, this->Super2Hypernode);
}
this->WhichRound.Allocate(tree.Supernodes.GetNumberOfValues());
this->WhichIteration.Allocate(tree.Supernodes.GetNumberOfValues());
this->Hypernodes.Allocate(tree.Hypernodes.GetNumberOfValues());
this->Hyperarcs.Allocate(tree.Hyperarcs.GetNumberOfValues());
this->Superchildren.Allocate(tree.Hyperarcs.GetNumberOfValues());
//copy the regular nodes
vtkm::cont::Algorithm::Copy(mesh.GlobalMeshIndex, this->RegularNodeGlobalIds);
vtkm::cont::Algorithm::Copy(mesh.SortedValues, this->DataValues);
// we want to be able to search by global mesh index. That means we need to have an index array, sorted indirectly on globalMeshIndex
vtkm::cont::Algorithm::Copy(
vtkm::cont::ArrayHandleIndex(RegularNodeSortOrder.GetNumberOfValues()), RegularNodeSortOrder);
vtkm::cont::Algorithm::Sort(RegularNodeSortOrder, PermuteComparator(this->RegularNodeGlobalIds));
vtkm::cont::Algorithm::Copy(tree.Superparents, this->Superparents);
// copy in the supernodes
vtkm::cont::Algorithm::Copy(tree.Supernodes, this->Supernodes);
vtkm::cont::Algorithm::Copy(tree.Superarcs, this->Superarcs);
vtkm::cont::Algorithm::Copy(tree.Hyperparents, this->Hyperparents);
vtkm::cont::Algorithm::Copy(
vtkm::cont::ArrayHandleConstant<vtkm::Id>(numRounds, this->WhichRound.GetNumberOfValues()),
this->WhichRound);
vtkm::cont::Algorithm::Copy(tree.WhenTransferred, this->WhichIteration);
// now set the regular to supernode array up: it's already been set to NO_SUCH_ELEMENT
{
auto regular2SupernodePermuted =
vtkm::cont::make_ArrayHandlePermutation(this->Supernodes, this->Regular2Supernode);
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(this->Supernodes.GetNumberOfValues()),
regular2SupernodePermuted);
}
// copy in the hypernodes
vtkm::cont::Algorithm::Copy(tree.Hypernodes, this->Hypernodes);
vtkm::cont::Algorithm::Copy(tree.Hyperarcs, this->Hyperarcs);
// now set the supernode to hypernode array up: it's already been set to NO_SUCH_ELEMENT
{
auto super2HypernodePermuted =
vtkm::cont::make_ArrayHandlePermutation(this->Hypernodes, this->Super2Hypernode);
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(this->Hypernodes.GetNumberOfValues()),
super2HypernodePermuted);
}
{
auto initalizeSuperchildrenWorklet = InitalizeSuperchildrenWorklet();
this->Invoke(initalizeSuperchildrenWorklet,
this->Hyperarcs, // Input
this->Hypernodes, // Input
this->Superchildren // Output
);
}
} // Initialize(..)
/// utility routine for the path probes
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::RegularString(const vtkm::Id regularId) const
{ // RegularString()
std::stringstream resultStream;
// this can get called before the regular ID is fully stored
if (regularId >= this->DataValues.GetNumberOfValues())
{
resultStream << "Regular ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(regularId, resultStream);
resultStream << " Value: N/A Global ID: N/A Regular ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(regularId, resultStream);
resultStream << " SNode ID: N/A Superparent: N/A";
}
else
{
resultStream << "Regular ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(regularId, resultStream);
resultStream << " Value: " << this->DataValues.ReadPortal.Get(regularId);
resultStream << " Global ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->RegularNodeGlobalIds.ReadPortal().Get(regularId), resultStream);
resultStream << " Regular ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(regularId, resultStream);
resultStream << " SNode ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Regular2Supernode.ReadPortal().Get(regularId), resultStream);
resultStream << "Superparents: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Superparents.ReadPortal().Get(regularId));
}
return resultStream.str();
} // RegularString()
/// utility routine for the path probes
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::SuperString(const vtkm::Id superId) const
{ // SuperString()
std::stringstream resultStream;
if (vtkm::worklet::contourtree_augmented::NoSuchElement(superId))
{
resultStream << "Super ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(superId, resultStream);
}
else
{
resultStream << "Super ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(superId, resultStream);
resultStream << " Value: "
<< this->DataValues.ReadPortal.Get(this->Supernodes.ReadPortal().Get(
vtkm::worklet::contourtree_augmented::MaskedIndex(superId)));
resultStream << " Global ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->RegularNodeGlobalIds.ReadPortal().Get(this->Supernodes.ReadPortal().Get(
vtkm::worklet::contourtree_augmented::MaskedIndex(superId))),
resultStream);
resultStream << " Regular Id: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Supernodes.ReadPortal(vtkm::worklet::contourtree_augmented::MaskedIndex(superId)),
resultStream);
resultStream << " Superarc: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Superarcs.ReadPortal(vtkm::worklet::contourtree_augmented::MaskedIndex(superId)),
resultStream);
resultStream << " HNode ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Super2Hypernode.ReadPortal(vtkm::worklet::contourtree_augmented::MaskedIndex(superId)),
resultStream);
resultStream << " Hyperparent: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Hyperparents.ReadPortal(vtkm::worklet::contourtree_augmented::MaskedIndex(superId)),
resultStream);
resultStream << " Round: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->WhichRound.ReadPortal(vtkm::worklet::contourtree_augmented::MaskedIndex(superId)),
resultStream);
resultStream << " Iteration: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->WhichIteration.ReadPortal(vtkm::worklet::contourtree_augmented::MaskedIndex(superId)),
resultStream);
}
return resultStream.str();
} // SuperString()
/// utility routine for the path probes
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::HyperString(const vtkm::Id hyperId) const
{ // HyperString()
std::stringstream resultStream;
if (vtkm::worklet::contourtree_augmented::NoSuchElement(hyperId))
{
resultStream << "Hyper ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(hyperId, resultStream);
}
else
{
resultStream << "Hyper Id: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(hyperId, resultStream);
resultStream << " Value: "
<< this->DataValues.ReadPortal.Get(
this->Supernodes.ReadPortal().Get(this->Hypernodes.ReadPortal.Get(
vtkm::worklet::contourtree_augmented::MaskedIndex(hyperId))));
resultStream << " Global ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->RegularNodeGlobalIds.ReadPortal().Get(
this->Supernodes.ReadPortal().Get(this->Hypernodes.ReadPortal.Get(
vtkm::worklet::contourtree_augmented::MaskedIndex(hyperId)))),
resultStream);
resultStream << " Regular ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Supernodes.ReadPortal().Get(this->Hypernodes.ReadPortal.Get(
vtkm::worklet::contourtree_augmented::MaskedIndex(hyperId))),
resultStream);
resultStream << " Super ID: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Hypernodes.ReadPortal.Get(vtkm::worklet::contourtree_augmented::MaskedIndex(hyperId)),
resultStream);
resultStream << " Hyperarc: ";
vtkm::worklet::contourtree_augmented::PrintIndexType(
this->Hyperarcs.ReadPortal.Get(vtkm::worklet::contourtree_augmented::MaskedIndex(hyperId)),
resultStream);
resultStream << " Superchildren: "
<< this->Superchildren.ReadPortal().Get(
vtkm::worklet::contourtree_augmented::MaskedIndex(hyperId));
}
return resultStream.str();
} // HyperString()
/// routine to probe a given node and trace it's hyperpath to the root
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::ProbeHyperPath(const vtkm::Id regularId,
const vtkm::Id maxLength) const
{ // ProbeHyperPath()
std::stringstream resultStream;
resultStream << "Probing HyperPath\n";
resultStream << "Node: " << this->RegularString(regularId) << std::endl;
// find the superparent
vtkm::Id superparent = this->Superparents.ReadPortal().Get(regularId);
resultStream << "Superparent: " << SuperString(superparent) << std::endl;
// and the hyperparent
vtkm::Id hyperparent = this->Hyperparents.ReadPortal().Get(superparent);
// now trace the path inwards: terminate on last round when we have null hyperarc
vtkm::Id length = 0;
while (true)
{ // loop inwards
length++;
if (length > maxLength && maxLength > 0)
{
break;
}
resultStream << "Hyperparent: " << this->HyperString(hyperparent) << std::endl;
// retrieve the target of the hyperarc
vtkm::Id hypertarget = this->Hyperarcs.ReadPortal().Get(hyperparent);
resultStream << "Hypertarget: "
<< SuperString(vtkm::worklet::contourtree_augmented::MaskedIndex(hypertarget))
<< std::endl;
// mask the hypertarget
vtkm::Id maskedHypertarget = vtkm::worklet::contourtree_augmented::MaskedIndex(hypertarget);
// test for null superarc: can only be root or attachment point
if (vtkm::worklet::contourtree_augmented::NoSuchElement(hypertarget))
{ // root or attachment point
// we're done
break;
} // root or attachment point
else
{ // ordinary supernode
hyperparent = this->Hyperparents.ReadPortal().Get(maskedHypertarget);
} // ordinary supernode
// now take the new superparent's hyperparent/hypertarget
hypertarget = this->Hyperarcs.ReadPortal().Get(hyperparent);
} // loop inwards
resultStream << "Probe Complete" << std::endl << std::endl;
return resultStream.str();
} // ProbeHyperPath()
/// routine to probe a given node and trace it's superpath to the root
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::ProbeSuperPath(const vtkm::Id regularId,
const vtkm::Id maxLength) const
{
std::stringstream resultStream;
// find the superparent
vtkm::Id superparent = this->Superparents.ReadPortal.Get(regularId);
auto superarcsPortal = this->Superarcs.ReadPortal();
auto whichRoundPortal = this->WhichRound.ReadPortal();
// now trace the path inwards: terminate on last round when we have null hyperarc
vtkm::Id length = 0;
while (true)
{ // loop inwards
length++;
if (length > maxLength && maxLength > 0)
{
break;
}
// retrieve the target of the superarc
vtkm::Id supertarget = superarcsPortal.Get(superparent);
resultStream << "Superparent: " << this->SuperString(superparent) << std::endl;
resultStream << "Supertarget: "
<< this->SuperString(
vtkm::worklet::contourtree_augmented::MaskedIndex(supertarget))
<< std::endl;
// mask the supertarget
vtkm::Id maskedSupertarget = vtkm::worklet::contourtree_augmented::MaskedIndex(supertarget);
// and retrieve it's supertarget
vtkm::Id nextSupertarget = superarcsPortal.Get(maskedSupertarget);
vtkm::Id maskedNextSupertarget =
vtkm::worklet::contourtree_augmented::MaskedIndex(nextSupertarget);
resultStream << "Next target: " << this->SuperString(nextSupertarget) << std::endl;
// test for null superarc: can only be root or attachment point
if (vtkm::worklet::contourtree_augmented::NoSuchElement(nextSupertarget))
{ // root or attachment point
// test round: if it's the last one, only the root has a null edge
if (whichRoundPortal.Get(maskedNextSupertarget) == this->NumRounds)
// we're done
break;
else // attachment point
superparent = maskedNextSupertarget;
} // root or attachment point
else
{ // ordinary supernode
superparent = maskedSupertarget;
} // ordinary supernode
} // loop inwards
resultStream << "Probe Complete" << std::endl << std::endl;
return resultStream.str();
}
/// Outputs the Hierarchical Tree in Dot format for visualization
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::PrintDotSuperStructure(const char* label) const
{ // PrintDotSuperStructure
// make a copy of the label
std::string filename("temp/");
filename += label;
// replace spaces with underscores
for (unsigned int strChar = 0; strChar < filename.length(); strChar++)
if (filename[strChar] == ' ')
filename[strChar] = '_';
// add the .gv suffix
filename += ".gv";
// generate an output stream
std::ofstream outstream(filename);
// print the header information
outstream << "digraph RegularTree\n\t{\n";
outstream << "\tsize=\"6.5, 9\"\n\tratio=\"fill\"\n";
outstream << "\tlabel=\"" << label << "\"\n\tlabelloc=t\n\tfontsize=30\n";
// create the NULL (root) node
outstream << "\t// NULL node to use as a root for the tree\n";
outstream << "\tNULL [style=filled,fillcolor=white,shape=point,label=\"NULL\"];\n";
outstream << "\t// Supernodes\n";
// loop through all supernodes
for (vtkm::Id supernode = 0; supernode < this->Supernodes.GetNumberOfValues; supernode++)
{ // per supernode
vtkm::Id regularID = this->Supernodes[supernode];
// print the supernode, making hypernodes double octagons
outstream
<< " SN" << std::setw(1) << supernode
<< " [style=filled,fillcolor=white,shape=" << std::setw(1)
<< ((this->Hypernodes[this->Hyperparents[supernode]] == supernode)
? "doublecircle"
: "circle") // hypernodes are double-circles
<< ",label=\"sn" << std::setw(4) << supernode << " h" << std::setw(1)
<< ((this->Hypernodes[this->Hyperparents[supernode]] == supernode)
? "n"
: "p") // hypernodes show "hn001" (their own ID), supernodes show "hp001" (their hyperparent)
<< std::setw(4) << this->Hyperparents[supernode] << "\\nm" << std::setw(1) << regularID
<< " g" << std::setw(4) << this->RegularNodeGlobalIDs[regularID] << "\\nrd" << std::setw(1)
<< this->WhichRound[supernode] << " it" << std::setw(4)
<< contourtree_augmented::MaskedIndex(this->WhichIteration[supernode]) << "\"];\n";
} // per supernode
outstream << "\t// Superarc nodes\n";
// now repeat to create nodes for the middle of each superarc (to represent the superarcs themselves)
for (vtkm::Id superarc = 0; superarc < this->Superarcs.GetNumberOfValues; superarc++)
{ // per superarc
// print the superarc vertex
outstream
<< "\tSA" << std::setw(1) << superarc
<< " [shape=circle,fillcolor=white,fixedsize=true,height=0.5,width=0.5,label=\"\"];\n";
} // per superarc
outstream << "\t// Superarc edges\n";
// loop through all superarcs to draw them
for (vtkm::Id superarc = 0; superarc < this->Superarcs.GetNumberOfValues; superarc++)
{ // per superarc
// retrieve ID of target supernode
vtkm::Id superarcFrom = superarc;
vtkm::Id superarcTo = this->Superarcs[superarcFrom];
// if this is true, it may be the last pruned vertex
if (contourtree_augmented::NoSuchElement(superarcTo))
{ // no superarc
// if it occurred on the final round, it's the global root and is shown as the NULL node
if (this->WhichRound[superarcFrom] == this->NRounds)
{ // root node
outstream << "\tSN" << std::setw(1) << superarcFrom << " -> SA" << std::setw(1) << superarc
<< " [label=\"S" << std::setw(1) << superarc << "\",style=dotted]\n";
outstream << "\tSN" << std::setw(1) << superarc << " -> NULL[label=\"S" << std::setw(1)
<< superarc << "\",style=dotted]\n";
} // root node
else
{ // attachment point
// otherwise, the target is actually a superarc vertex not a supernode vertex
// so we use the regular ID to retrieve the superparent which tells us which superarc we insert into
vtkm::Id regularFrom = this->Supernodes[superarcFrom];
superarcTo = this->Superparents[regularFrom];
// output a suitable edge
outstream << "\tSN" << std::setw(1) << superarcFrom << " -> SA" << std::setw(1)
<< superarcTo << "[label=\"S" << std::setw(1) << superarc << "\",style=dotted]\n";
} // attachment point
} // no superarc
else
{ // there is a superarc
// retrieve the ascending flag
bool ascendingSuperarc = contourtree_augmented::IsAscending(superarcTo);
// strip out the flags
superarcTo = contourtree_augmented::MaskedIndex(superarcTo);
// how we print depends on whether the superarc ascends
outstream << "\tSN" << std::setw(1) << (ascendingSuperarc ? superarcTo : superarcFrom)
<< " -> SA" << std::setw(1) << superarc << " [label=\"S" << std::setw(1) << superarc
<< "\"" << (ascendingSuperarc ? ",dir=\"back\"" : "") << ",arrowhead=\"none\"]\n";
outstream << "\tSA" << std::setw(1) << superarc << " -> SN" << std::setw(1)
<< (ascendingSuperarc ? superarcFrom : superarcTo) << " [label=\"S" << std::setw(1)
<< superarc << "\"" << (ascendingSuperarc ? ",dir=\"back\"" : "")
<< ",arrowhead=\"none\"]\n";
} // there is a superarc
} // per superarc
outstream << "\t// Hyperarcs\n";
// now loop through the hyperarcs to draw them
for (vtkm::Id hyperarc = 0; hyperarc < this->Hyperarcs.GetNumberOfValues; hyperarc++)
{ // per hyperarc
// retrieve ID of target hypernode
vtkm::Id hyperarcFrom = this->Hypernodes[hyperarc];
vtkm::Id hyperarcTo = this->Hyperarcs[hyperarc];
// if this is true, it is the last pruned vertex & needs a hyperarc to the root
if (contourtree_augmented::NoSuchElement(hyperarcTo))
outstream << "\tSN" << std::setw(1) << hyperarcFrom << " -> NULL[label=\"H" << std::setw(1)
<< hyperarc << "\",penwidth=5.0,style=dotted]\n";
else
{ // not the last one
// otherwise, retrieve the ascending flag
bool ascendingHyperarc = contourtree_augmented::IsAscending(hyperarcTo);
// strip out the flags
hyperarcTo = contourtree_augmented::MaskedIndex(hyperarcTo);
// how we print depends on whether the hyperarc ascends
outstream << "\tSN" << std::setw(1) << (ascendingHyperarc ? hyperarcTo : hyperarcFrom)
<< " -> SN" << std::setw(1) << (ascendingHyperarc ? hyperarcFrom : hyperarcTo)
<< "[label=\"H" << std::setw(1) << hyperarc << "\",penwidth=5.0,dir=\"back\"]\n";
} // not the last one
} // per hyperarc
// print the footer information
outstream << "\t}\n";
return std::string("HierarchicalContourTree<FieldType>::PrintDotSuperStructure() Complete");
} // PrintDotSuperStructure
/// debug routine
VTKM_CONT
template <typename FieldType>
std::string HierarchicalContourTree<FieldType>::DebugPrint(const char* message,
const char* fileName,
long lineNum) const
{
std::stringstream resultStream;
resultStream << std::endl;
resultStream << "[CUTHERE]-------------------------------" << std::endl;
resultStream << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4)
<< lineNum << std::endl;
resultStream << std::left << std::string(message) << std::endl;
resultStream << "Hierarchical Contour Tree Contains: " << std::endl;
resultStream << "----------------------------------------" << std::endl;
resultStream << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(this->RegularNodeGlobalIds.GetNumberOfValues(),
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Regular Nodes (global ID)", this->RegularNodeGlobalIds, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintValues(
"Data Values", this->DataValues, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Regular Node Sort Order", this->RegularNodeSortOrder, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Superparents (unsorted)", this->Superparents, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Supernode ID (if any)", this->Regular2Supernode, -1, resultStream);
resultStream << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(this->Supernodes.GetNumberOfValues(),
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Supernodes (regular index)", this->Supernodes, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Superarcs (supernode index)", this->Superarcs, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Hyperparents (hypernode index)", this->Hyperparents, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Hypernode ID (if any)", this->Super2Hypernode, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Which Round", this->WhichRound, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Which Iteration", this->WhichIteration, -1, resultStream);
resultStream << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(this->Hypernodes.GetNumberOfValues(),
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Hypernodes (supernode index)", this->Hypernodes, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Hyperarcs (supernode index)", this->Hyperarcs, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"Superchildren", this->Superchildren, -1, resultStream);
resultStream << std::endl;
resultStream << "nRounds: " << this->NumRounds << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(
this->NumRegularNodesInRound.GetNumberOfValues(), resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"nRegular Nodes In Round", this->NumRegularNodesInRound, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"nSupernodes In Round", this->NumSupernodesInRound, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"nHypernodes In Round", this->NumHypernodesInRound, -1, resultStream);
vtkm::worklet::contourtree_augmented::PrintHeader(this->NumIterations.GetNumberOfValues(),
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"nIterations", this->NumIterations, -1, resultStream);
for (vtkm::Id whichRound = 0; whichRound < this->NumIterations.GetNumberOfValues(); whichRound++)
{ // per round
resultStream << "Round " << whichRound << std::endl;
vtkm::worklet::contourtree_augmented::PrintHeader(
this->FirstSupernodePerIteration[static_cast<std::size_t>(whichRound)].GetNumberOfValues(),
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"First Supernode Per Iteration",
this->FirstSupernodePerIteration[static_cast<std::size_t>(whichRound)],
-1,
resultStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"First Hypernode Per Iteration",
this->FirstHypernodePerIteration[static_cast<std::size_t>(whichRound)],
-1,
resultStream);
resultStream << std::endl;
} // per round
return resultStream.str();
}
} // namespace contourtree_distributed
} // namespace worklet

@ -0,0 +1,151 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//================================================================================
//
// Parallel Peak Pruning v. 2.0
//
// Started June 15, 2017
//
// Copyright Hamish Carr, University of Leeds
//
// TreeResidue.h - A data structure storing the residue information for transfer
// to the grafting phase
//
//================================================================================
//
// COMMENTS:
//
//
//================================================================================
#ifndef vtk_m_worklet_contourtree_distributed_interior_forest_h
#define vtk_m_worklet_contourtree_distributed_interior_forest_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
#include <sstream>
#include <string>
#include <utility>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
/// \brief The contour tree of a data block restricted to the interior of a data block
///
/// The Boundary Restricted Augemented Contour Tree (BRACT) represents the contours
/// that cross the boundary of a data block. In contrast, this class represents all contours
/// that remain in the interior. Alternatively, the interior tree can be interpreted as the
/// residue (i.e., the part that are left over) from removing the BRACT from the full contour tree.
/// of a data block
class InteriorForest
{ // class InteriorForest
public:
// array of vertices in the bract (by mesh index)
vtkm::worklet::contourtree_augmented::IdArrayType BractMeshIndices;
// array of flags for whether necessary (i.e. needed in the BRACT)
vtkm::worklet::contourtree_augmented::IdArrayType IsNecessary;
// arrays of nodes above and below supernodes for hierarchical search
// stored as global IDs
vtkm::worklet::contourtree_augmented::IdArrayType Above;
vtkm::worklet::contourtree_augmented::IdArrayType Below;
// constructor
InteriorForest() {}
// prints the contents of the object in a standard format
void PrintContent(std::ostream& outStream) const;
std::string DebugPrint(const char* message, const char* fileName, long lineNum) const;
};
// debug routine
inline void InteriorForest::PrintContent(std::ostream& outStream) const
{
// Per Supernode Arrays
vtkm::worklet::contourtree_augmented::PrintHeader(this->IsNecessary.GetNumberOfValues(),
outStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"IsNecessary", this->IsNecessary, -1, outStream);
vtkm::worklet::contourtree_augmented::PrintIndices("Above", this->Above, -1, outStream);
vtkm::worklet::contourtree_augmented::PrintIndices("Below", this->Below, -1, outStream);
// BRACT Sized Arrays
vtkm::worklet::contourtree_augmented::PrintHeader(this->BractMeshIndices.GetNumberOfValues(),
outStream);
vtkm::worklet::contourtree_augmented::PrintIndices(
"BRACT Mesh Indices", this->BractMeshIndices, -1, outStream);
}
inline std::string InteriorForest::DebugPrint(const char* message,
const char* fileName,
long lineNum) const
{ // DebugPrint
std::stringstream resultStream;
resultStream << "[CUTHERE]---------------------------------------------" << std::endl;
resultStream << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4)
<< lineNum << " ";
resultStream << std::left << std::string(message) << std::endl;
resultStream << "------------------------------------------------------" << std::endl;
resultStream << "Residue Contains: " << std::endl;
resultStream << "------------------------------------------------------" << std::endl;
this->PrintContent(resultStream);
resultStream << "------------------------------------------------------" << std::endl;
resultStream << std::endl;
return resultStream.str();
} // DebugPrint
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -66,7 +66,6 @@
#include <vtkm/cont/PartitionedDataSet.h>
#include <vtkm/worklet/contourtree_augmented/data_set_mesh/IdRelabeler.h>
namespace vtkm
{
namespace worklet
@ -98,6 +97,7 @@ public:
VTKM_CONT
~MultiBlockContourTreeHelper(void)
{
// FIXME: This shouldn't be necessary as arrays will get deleted anyway
LocalContourTrees.clear();
LocalSortOrders.clear();
}
@ -201,7 +201,6 @@ public:
SpatialDecomposition MultiBlockSpatialDecomposition;
std::vector<vtkm::worklet::contourtree_augmented::ContourTree> LocalContourTrees;
std::vector<vtkm::worklet::contourtree_augmented::IdArrayType> LocalSortOrders;
}; // end MultiBlockContourTreeHelper
} // namespace contourtree_distributed

File diff suppressed because it is too large Load Diff

@ -54,6 +54,11 @@
#define vtk_m_worklet_contourtree_distributed_spatialdecomposition_h
#include <vtkm/Types.h>
#include <vtkm/cont/BoundsCompute.h>
#include <vtkm/cont/BoundsGlobalCompute.h>
#include <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/cont/PartitionedDataSet.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
// clang-format off
@ -91,8 +96,7 @@ public:
{
if (this->NumberOfDimensions() == 2)
{
// may need to change back when porting ot later verison of VTKM/vtkmdiy
vtkmdiy::DiscreteBounds domain(0); //(2);
vtkmdiy::DiscreteBounds domain(2);
domain.min[0] = domain.min[1] = 0;
domain.max[0] = static_cast<int>(this->GlobalSize[0]);
domain.max[1] = static_cast<int>(this->GlobalSize[1]);
@ -100,8 +104,7 @@ public:
}
else
{
// may need to change back when porting to later version of VTMK/vtkmdiy
vtkmdiy::DiscreteBounds domain(0); //(3);
vtkmdiy::DiscreteBounds domain(3);
domain.min[0] = domain.min[1] = domain.min[2] = 0;
domain.max[0] = static_cast<int>(this->GlobalSize[0]);
domain.max[1] = static_cast<int>(this->GlobalSize[1]);
@ -119,6 +122,33 @@ public:
inline vtkm::Id GetLocalNumberOfBlocks() const { return LocalBlockSizes.GetNumberOfValues(); }
inline static vtkm::Bounds GetGlobalBounds(const vtkm::cont::PartitionedDataSet& input)
{
// Get the spatial bounds of a multi -block data set
vtkm::Bounds bounds = vtkm::cont::BoundsGlobalCompute(input);
return bounds;
}
inline static vtkm::Bounds GetLocalBounds(const vtkm::cont::PartitionedDataSet& input)
{
// Get the spatial bounds of a multi -block data set
vtkm::Bounds bounds = vtkm::cont::BoundsCompute(input);
return bounds;
}
inline static vtkm::Id GetGlobalNumberOfBlocks(const vtkm::cont::PartitionedDataSet& input)
{
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
vtkm::Id localSize = input.GetNumberOfPartitions();
vtkm::Id globalSize = 0;
#ifdef VTKM_ENABLE_MPI
vtkmdiy::mpi::all_reduce(comm, localSize, globalSize, std::plus<vtkm::Id>{});
#else
globalSize = localSize;
#endif
return globalSize;
}
// Number of blocks along each dimension
vtkm::Id3 BlocksPerDimension;
// Size of the global mesh

File diff suppressed because it is too large Load Diff

@ -84,7 +84,7 @@ public:
VTKM_EXEC void operator()(const vtkm::Id& returnIndex,
const vtkm::Id& newVertexIdValue,
const OutFieldPortalType& upNeighbourPortal,
const OutFieldPortalType& downNeighbourPortal)
const OutFieldPortalType& downNeighbourPortal) const
{
// per vertex
// necessary vertices

@ -95,7 +95,7 @@ public:
const vtkm::Id boundaryNecessaryId,
const InFieldPortalType meshSortOrderPortal,
const OutFieldPortalType& boundaryIndicesPortal,
const OutFieldPortalType& bractVertexSupersetPortal)
const OutFieldPortalType& bractVertexSupersetPortal) const
{
// per supernode
if (isNecessaryAndInterior)

@ -89,7 +89,7 @@ public:
VTKM_EXEC void operator()(const vtkm::Id& boundaryVertexSortID,
const InFieldPortalType& superparentsPortal,
const InFieldPortalType& supernodesPortal,
const OutFieldPortalType& isNecessaryAndInteriorPortal)
const OutFieldPortalType& isNecessaryAndInteriorPortal) const
{
vtkm::Id superparent = superparentsPortal.Get(boundaryVertexSortID);
// if the superparent's supernode is the vertex, they match

@ -54,9 +54,11 @@
#define vtk_m_worklet_contourtree_distributed_bract_maker_bract_node_comparator_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleVirtual.h>
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
@ -67,18 +69,20 @@ namespace bract_maker
{
// device implementation of the comparator used for sorting hyperarcs
/// device implementation of the comparator used for sorting hyperarcs
template <typename DeviceAdapter>
class BRACTNodeComparatorImpl
{
public:
using IdArrayPortalType =
typename vtkm::cont::ArrayHandle<vtkm::Id>::template ExecutionTypes<DeviceAdapter>::PortalConst;
/// The ContourTreeMesh uses a smart ArrayHandleIndex instead of a regular IdArrayType array that is why we use a ArrayHandleVirtual here
using SortIndexPortalType = typename vtkm::cont::ArrayHandleVirtual<
vtkm::Id>::template ExecutionTypes<DeviceAdapter>::PortalConst;
// constructor - takes vectors as parameters
VTKM_CONT
BRACTNodeComparatorImpl(const IdArrayPortalType& regularIdPortal,
const IdArrayPortalType& meshSortIndexPortal)
const SortIndexPortalType& meshSortIndexPortal)
: RegularIdPortal(regularIdPortal)
, MeshSortIndexPortal(meshSortIndexPortal)
{ // constructor
@ -109,7 +113,7 @@ public:
private:
IdArrayPortalType RegularIdPortal;
IdArrayPortalType MeshSortIndexPortal;
SortIndexPortalType MeshSortIndexPortal;
}; // BRACTNodeComparatorImpl
/// comparator used to compare hyperarcs for sort
@ -119,7 +123,7 @@ public:
// constructor - takes vectors as parameters
VTKM_CONT
BRACTNodeComparator(const vtkm::worklet::contourtree_augmented::IdArrayType& regularId,
const vtkm::worklet::contourtree_augmented::IdArrayType& meshSortIndex)
const vtkm::cont::ArrayHandleVirtual<vtkm::Id>& meshSortIndex)
: RegularId(regularId)
, MeshSortIndex(meshSortIndex)
{ // constructor
@ -137,7 +141,8 @@ public:
private:
vtkm::worklet::contourtree_augmented::IdArrayType RegularId;
vtkm::worklet::contourtree_augmented::IdArrayType MeshSortIndex;
/// The ContourTreeMesh uses a smart ArrayHandleIndex instead of a regular IdArrayType array that is why we use a ArrayHandleVirtual here
vtkm::cont::ArrayHandleVirtual<vtkm::Id> MeshSortIndex;
}; // BRACTNodeComparator
} // namespace bract_maker

@ -84,7 +84,7 @@ public:
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const InFieldPortalType& boundarySuperparentsPortal,
const vtkm::Id& boundaryVertex,
const OutFieldPortalType& superarcIntrinsicBoundaryCountPortal)
const OutFieldPortalType& superarcIntrinsicBoundaryCountPortal) const
{
if (boundaryVertex == 0)
{
@ -103,7 +103,7 @@ public:
// ii. Now set the last one explicitly
if (boundaryVertex == (this->NumBoundary - 1))
{
superarcIntrinsicBoundaryCountPortal.Set(superarcId) = this->NumBoundary;
superarcIntrinsicBoundaryCountPortal.Set(superarcId, this->NumBoundary);
}
// In serial this worklet implements the following operation
/*
@ -143,7 +143,7 @@ public:
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const InFieldPortalType& boundarySuperparentsPortal,
const vtkm::Id& boundaryVertex,
const OutFieldPortalType& superarcIntrinsicBoundaryCountPortal)
const OutFieldPortalType& superarcIntrinsicBoundaryCountPortal) const
{
if (boundaryVertex == 0)
{

@ -29,6 +29,7 @@ set(headers
CompressRegularisedNodesResolveRootWorklet.h
CompressRegularisedNodesTransferVerticesWorklet.h
CompressRegularisedNodesFillBractSuperarcsWorklet.h
SetInteriorForestWorklet.h
ArraySumFunctor.h
NoSuchElementFunctor.h
SelectRangeFunctor.h

@ -73,14 +73,14 @@ public:
using ControlSignature = void(FieldInOut newVertexId, // Input/Output
FieldIn keptInBract // input
);
using ExecutionSignature = void(_1, _2);
using ExecutionSignature = _1(_1, _2);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CompressRegularisedNodesCopyNecessaryRegularNodesWorklet() {}
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& newVertexIdIn, const vtkm::Id& keptInBract)
VTKM_EXEC vtkm::Id operator()(vtkm::Id& newVertexIdIn, const vtkm::Id& keptInBract) const
{
return (!vtkm::worklet::contourtree_augmented::NoSuchElement(newVertexIdIn)) ? (keptInBract - 1)
: newVertexIdIn;

@ -73,7 +73,7 @@ public:
WholeArrayIn reverseSorter,
FieldIn vertexSorter,
FieldOut bractSuperarcs);
using ExecutionSignature = void(_1, _2, _3);
using ExecutionSignature = _4(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
@ -83,7 +83,7 @@ public:
template <typename InFieldPortalType>
VTKM_EXEC vtkm::Id operator()(const InFieldPortalType newSuperarcPortal,
const InFieldPortalType reverseSorterPortal,
const vtkm::Id& vertexSorterIndex)
const vtkm::Id& vertexSorterIndex) const
{
vtkm::Id newSuperarcIndex = newSuperarcPortal.Get(vertexSorterIndex);
if (vtkm::worklet::contourtree_augmented::NoSuchElement(newSuperarcIndex))

@ -88,7 +88,7 @@ public:
const vtkm::Id& bractSuperarcIdIn,
const InFieldPortalType& downNeighbourPortal,
const InFieldPortalType& upNeighbourPortal,
const OutFieldPortalType& newSuperarcPortal)
const OutFieldPortalType& newSuperarcPortal) const
{
// per vertex
// skip all unnecessary vertices

@ -80,7 +80,8 @@ public:
CompressRegularisedNodesResolveRootWorklet() {}
template <typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& newIndex, const InOutFieldPortalType& newSuperarcPortal)
VTKM_EXEC void operator()(const vtkm::Id& newIndex,
const InOutFieldPortalType& newSuperarcPortal) const
{
// per kept vertex
// retrieve it to register

@ -71,20 +71,24 @@ class CompressRegularisedNodesTransferVerticesWorklet : public vtkm::worklet::Wo
public:
using ControlSignature = void(FieldIn bractVertexSuperset, // input
FieldIn newVertexId, // input
FieldOut newVertexIndex);
using ExecutionSignature = void(_1, _2);
WholeArrayOut newVertexIndex);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CompressRegularisedNodesTransferVerticesWorklet() {}
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& bractVertexSupersetIndex,
const vtkm::Id& newVertexIdIn)
template <typename OutPortalType>
VTKM_EXEC void operator()(const vtkm::Id& bractVertexSupersetIndex,
const vtkm::Id& newVertexIdIn,
const OutPortalType& newVertexIndexPortal) const
{
return (!vtkm::worklet::contourtree_augmented::NoSuchElement(newVertexIdIn))
? bractVertexSupersetIndex
: 0;
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(newVertexIdIn))
{
newVertexIndexPortal.Set(newVertexIdIn, bractVertexSupersetIndex);
}
// In serial this worklet implements the following operation
/*
for (indexType returnIndex = 0; returnIndex < bractVertexSuperset.size(); returnIndex++)

@ -88,7 +88,9 @@ public:
VTKM_EXEC_CONT
FindBractSuperarcsSuperarcToWorklet() {}
template <typename InFieldPortalType, typename OutFieldPortalType>
template <typename InFieldPortalType,
typename MeshSortOrderPortalType,
typename OutFieldPortalType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& from,
const InFieldPortalType& bractVertexSupersetPortal,
const InFieldPortalType& boundaryIndicesPortal,
@ -97,8 +99,8 @@ public:
const InFieldPortalType& contourtreeHyperparentsPortal,
const InFieldPortalType& contourtreeHyperarcsPortal,
const InFieldPortalType& contourtreeSupernodesPortal,
const InFieldPortalType& meshSortOrderPortal,
const OutFieldPortalType& treeToSupersetPortal)
const MeshSortOrderPortalType& meshSortOrderPortal,
const OutFieldPortalType& treeToSupersetPortal) const
{
// find the sort order, super- and hyper- parent
// vtkm::Id fromIndex = bractVertexSupersetPortal.Get(from);

@ -86,7 +86,7 @@ public:
VTKM_EXEC void operator()(const vtkm::Id& boundaryIndex,
const InFieldPortalType superparentsPortal,
const InFieldPortalType superarcsPortal,
const OutFieldPortalType& isNecessaryPortal)
const OutFieldPortalType& isNecessaryPortal) const
{
// per boundary node
// find the superparent

@ -76,7 +76,7 @@ public:
FieldIn superarcDependentBoundaryCount, // (input)
WholeArrayOut isNecessary // (output)
);
using ExecutionSignature = void(_1, _2, _3, _4);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
@ -89,7 +89,7 @@ public:
template <typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& superarc,
const vtkm::Id& dependentWeight,
const OutFieldPortalType& isNecessaryPortal)
const OutFieldPortalType& isNecessaryPortal) const
{
// skip the stub superarc at the root
if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarc))

@ -84,14 +84,19 @@ public:
VTKM_EXEC_CONT
IdentifyRegularisedSupernodesStepOneWorklet() {}
template <typename InFieldPortalType, typename OutFieldPortalType>
// We use a separate MeshSortIndexPortalType (also it may strictly not be necessary) because
// the ContourTreeMehs uses a ArrayHandleIndex for its SortIndex and the other mesh types
// use a IdArrayType (i.e., ArrayHandle<vtkm::Id>
template <typename InFieldPortalType,
typename MeshSortIndexPortalType,
typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& from,
const vtkm::Id& to,
const InFieldPortalType& bractVertexSupersetPortal,
const InFieldPortalType& meshSortIndexPortal,
const MeshSortIndexPortalType& meshSortIndexPortal,
const InFieldPortalType& upNeighbourPortal,
const InFieldPortalType& downNeighbourPortal,
const OutFieldPortalType& newVertexIdPortal)
const OutFieldPortalType& newVertexIdPortal) const
{
// per vertex
if (vtkm::worklet::contourtree_augmented::NoSuchElement(to))

@ -75,7 +75,6 @@ public:
FieldIn downNeighbour, // input
ExecObject meshBoundary, // input
WholeArrayOut newVertexId // output
);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5);
using InputDomain = _1;
@ -84,13 +83,13 @@ public:
VTKM_EXEC_CONT
IdentifyRegularisedSupernodesStepTwoWorklet() {}
template <typename InFieldPortalType, typename MeshBoundaryType, typename OutFieldPortalType>
template <typename MeshBoundaryType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& boundaryNode,
const vtkm::Id& bractVertexSupersetValue,
const vtkm::Id& upNeighbourValue,
const vtkm::Id& downNeighbourValue,
const MeshBoundaryType& meshBoundary,
const OutFieldPortalType& newVertexIdPortal)
const OutFieldPortalType& newVertexIdPortal) const
{
// per vertex
// check for leaf criticality
@ -100,7 +99,9 @@ public:
newVertexIdPortal.Set(boundaryNode, vtkm::worklet::contourtree_augmented::ELEMENT_EXISTS);
}
// retrieve mesh index (i.e., bractVertexSupersetValue) & check whether on the boundary
if (meshBoundary.liesOnBoundary(bractVertexSupersetValue))
if (
meshBoundary.LiesOnBoundary(
bractVertexSupersetValue)) // TODO: This may need to change to isNecessary when we change to boundary critical points
{
newVertexIdPortal.Set(boundaryNode, vtkm::worklet::contourtree_augmented::ELEMENT_EXISTS);
}

@ -80,9 +80,9 @@ public:
PointerDoubleUpDownNeighboursWorklet() {}
template <typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id* returnIndex,
VTKM_EXEC void operator()(const vtkm::Id returnIndex,
const InOutFieldPortalType& upNeighbourPortal,
const InOutFieldPortalType& downNeighbourPortal)
const InOutFieldPortalType& downNeighbourPortal) const
{
// per vertex
// retrieve nbr & see if it's terminal

@ -78,7 +78,7 @@ public:
WholeArrayIn accumulatedBoundaryCountPortal, // (input)
WholeArrayInOut supernodeTransferBoundaryCountPortal // (input/output)
);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4);
using InputDomain = _1;
// Default Constructor
@ -90,7 +90,7 @@ public:
const InFieldPortalType& hyperarcTargetSortPermutationPortal,
const InFieldPortalType& hyperarcsPortal,
const InFieldPortalType& accumulatedBoundaryCountPortal,
const InOutFieldPortalType& supernodeTransferBoundaryCountPortal)
const InOutFieldPortalType& supernodeTransferBoundaryCountPortal) const
{
// the prefix sum of the first element is the correct value anyway so we can ignore it
if (hyperarc == 0)

@ -89,11 +89,12 @@ public:
}
template <typename InFieldPortalType, typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& supernode,
const InFieldPortalType& hyperparentsPortal,
const InFieldPortalType& hypernodesPortal,
const InFieldPortalType& superarcDependentBoundaryCountPortal,
const InOutFieldPortalType& newSuperArcDependentBoundaryCountPortal)
VTKM_EXEC void operator()(
const vtkm::Id& supernode,
const InFieldPortalType& hyperparentsPortal,
const InFieldPortalType& hypernodesPortal,
const InFieldPortalType& superarcDependentBoundaryCountPortal,
const InOutFieldPortalType& newSuperArcDependentBoundaryCountPortal) const
{
// A. Retrieve hyperparent & convert to supernode ID
vtkm::Id hyperparent = hyperparentsPortal.Get(supernode);

@ -79,7 +79,7 @@ public:
WholeArrayIn accumulatedBoundaryCountPortal, // (input)
WholeArrayInOut supernodeTransferBoundaryCountPortal // (input/output)
);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4);
using InputDomain = _1;
// Default Constructor
@ -91,7 +91,7 @@ public:
const InFieldPortalType& hyperarcTargetSortPermutationPortal,
const InFieldPortalType& hyperarcsPortal,
const InFieldPortalType& accumulatedBoundaryCountPortal,
const InOutFieldPortalType& supernodeTransferBoundaryCountPortal)
const InOutFieldPortalType& supernodeTransferBoundaryCountPortal) const
{
// per hyperarc
// retrieve the hypertarget

@ -91,7 +91,7 @@ public:
VTKM_EXEC void operator()(const vtkm::Id& hypernode,
const InFieldPortalType& hypernodesPortal,
const InFieldPortalType& superarcDependentBoundaryCountPortal,
const OutFieldPortalType& hyperarcDependentBoundaryCountPortal)
const OutFieldPortalType& hyperarcDependentBoundaryCountPortal) const
{
// v. per hypernode
// A.Transfer dependent count from last superarc to hyperarc

@ -70,7 +70,7 @@ class SelectRangeFunctor
{
public:
VTKM_EXEC_CONT
SelectRangeFunctor();
SelectRangeFunctor() {}
VTKM_EXEC_CONT
SelectRangeFunctor(const vtkm::worklet::contourtree_augmented::IdArrayType& dataArray,

@ -0,0 +1,207 @@
//============================================================================
// 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_bract_maker_set_interior_forest_worklet_h
#define vtk_m_worklet_contourtree_distributed_bract_maker_set_interior_forest_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace bract_maker
{
/// Worklet to transfer the dependent counts for hyperarcs
/// Part of the BoundaryRestrictedAugmentedContourTree.PropagateBoundaryCounts function
class SetInteriorForestWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn contourTreeSupernodes, // input
FieldIn interiorForestIsNecessary, // input
FieldIn boundaryTreeMakerTree2Superset, // input
WholeArrayIn meshGlobalIdsFromMeshIndices, // input
WholeArrayIn boundaryTreeMakerUpNeighbour, // input
WholeArrayIn boundaryTreeMakerDownNeighbour, // input
FieldOut interiorForestAbove, // output
FieldOut interiorForestBelow // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7, _8);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
SetInteriorForestWorklet() {}
// Allow for different portal type for the meshGlobalIds as they may be a fancy
// array handle rather than a portal direclty to a IdArrayType
template <typename InFieldPortalType, typename GlobalIdFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& sortId,
const vtkm::Id& isNecessary,
const vtkm::Id& supersetId,
const GlobalIdFieldPortalType& meshGlobalIdsPortal,
const InFieldPortalType& upNeighbourPortal,
const InFieldPortalType& downNeighbourPortal,
vtkm::Id& interiorForestAbove,
vtkm::Id& interiorForestBelow) const
{
(void)
sortId; // TODO: Remove if not needed. This was included in original code but seems unused. Avoid compiler warning.
// per supernode
// ignore supernodes that weren't marked necessary, since they will never be searched for
// all nodes to be searched for are necessary, but not all necessary nodes will be searched for
if (isNecessary)
{ // necessary supernode
// first, convert it to a sort ID: Asignement of sortId from contourTreeSupernodes done on input
// now find it in the superset: Assignment of supersetId from boundaryTreeMakerTree2Superset done on input
// find the up neighbour and convert it to a global ID: note that we may have a leaf
// in which case this may be NO_SUCH_ELEMENT. This will not be searched for, but for safety,
// we will test for it explicitly
vtkm::Id upSupersetId = upNeighbourPortal.Get(supersetId);
if (vtkm::worklet::contourtree_augmented::NoSuchElement(upSupersetId))
{ // no up neighbour
interiorForestAbove = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
} // no up neighbour
else
{ // up neighbour exists
// mask it to get a superset ID
upSupersetId = vtkm::worklet::contourtree_augmented::MaskedIndex(upSupersetId);
// look up the mesh ID. NOTE: meshGlobalIdsPortal is already indexed by bractVertexSuperset
// so we no longer need to do the bractVertexSupersetPortal.Get(upSupersetId); lookup here
vtkm::Id upMeshId = upSupersetId;
// then store the global ID in the "above" array
interiorForestAbove = meshGlobalIdsPortal.Get(upMeshId);
} // up neighbour exists
// do the same for the down neighbour
vtkm::Id downSupersetId = downNeighbourPortal.Get(supersetId);
if (vtkm::worklet::contourtree_augmented::NoSuchElement(downSupersetId))
{ // no down neighbour
interiorForestBelow = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
} // no down neighbour
else
{ // down neighbour exists
// mask it to get a superset ID
downSupersetId = vtkm::worklet::contourtree_augmented::MaskedIndex(downSupersetId);
// look up the mesh ID. NOTE: meshGlobalIdsPortal is already indexed by bractVertexSuperset
// so we no longer need to do the bractVertexSupersetPortal.Get(downSupersetId); lookup here
vtkm::Id downMeshId = downSupersetId;
// then store the global ID in the "above" array
interiorForestBelow = meshGlobalIdsPortal.Get(downMeshId);
} // up neighbour exists
} // necessary supernode
// In serial this worklet implements the following operation
/*
for (indexType supernode = 0; supernode < contourTree->supernodes.size(); supernode++)
{ // per supernode
// ignore supernodes that weren't marked necessary, since they will never be searched for
// all nodes to be searched for are necessary, but not all necessary nodes will be searched for
if (residue->isNecessary[supernode])
{ // necessary supernode
// first, convert it to a sort ID
indexType sortID = contourTree->supernodes[supernode];
// now find it in the superset
indexType supersetID = tree2Superset[supernode];
// find the up neighbour and convert it to a global ID: note that we may have a leaf
// in which case this may be NO_SUCH_ELEMENT. This will not be searched for, but for safety,
// we will test for it explicitly
indexType upSupersetID = upNeighbour[supersetID];
if (noSuchElement(upSupersetID))
{ // no up neighbour
residue->above[supernode] = NO_SUCH_ELEMENT;
} // no up neighbour
else
{ // up neighbour exists
// mask it to get a superset ID
upSupersetID = maskedIndex(upSupersetID);
// look up the mesh ID
indexType upMeshID = bractVertexSuperset[upSupersetID];
// then store the global ID in the "above" array
residue->above[supernode] = mesh->GetGlobalIDFromMeshIndex(upMeshID);
} // up neighbour exists
// do the same for the down neighbour
indexType downSupersetID = downNeighbour[supersetID];
if (noSuchElement(downSupersetID))
{ // no down neighbour
residue->below[supernode] = NO_SUCH_ELEMENT;
} // no down neighbour
else
{ // down neighbour exists
// mask it to get a superset ID
downSupersetID = maskedIndex(downSupersetID);
// look up the mesh ID
indexType downMeshID = bractVertexSuperset[downSupersetID];
// then store the global ID in the "above" array
residue->below[supernode] = mesh->GetGlobalIDFromMeshIndex(downMeshID);
} // up neighbour exists
} // necessary supernode
} // per supernode
*/
}
}; // SetInteriorForestWorklet
} // namespace bract_maker
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -83,13 +83,18 @@ public:
VTKM_EXEC_CONT
SetUpAndDownNeighboursWorklet() {}
template <typename InFieldPortalType, typename OutFieldPortalType>
// We use a separate MeshSortIndexPortalType (also it may strictly not be necessary) because
// the ContourTreeMehs uses a ArrayHandleIndex for its SortIndex and the other mesh types
// use a IdArrayType (i.e., ArrayHandle<vtkm::Id>
template <typename InFieldPortalType,
typename MeshSortIndexPortalType,
typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& from,
vtkm::Id& to,
InFieldPortalType bractVertexSupersetPortal,
InFieldPortalType meshSortIndexPortal,
OutFieldPortalType upNeighbourPortal,
OutFieldPortalType downNeighbourPortal)
const vtkm::Id& to,
const InFieldPortalType& bractVertexSupersetPortal,
const MeshSortIndexPortalType& meshSortIndexPortal,
const OutFieldPortalType& upNeighbourPortal,
const OutFieldPortalType& downNeighbourPortal) const
{
// per vertex
// ignore the last terminating edge

@ -0,0 +1,18 @@
##============================================================================
## 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
FindRegularByGlobal.h
FindSuperArcForUnknownNode.h
PermuteComparator.h
InitalizeSuperchildrenWorklet.h
)
vtkm_declare_headers(${headers})

@ -0,0 +1,200 @@
//============================================================================
// 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_find_regular_by_global_device_data_h
#define vtk_m_worklet_contourtree_distributed_find_regular_by_global_device_data_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
/// Device implementation of FindRegularByGlobal for the HierarchicalContourTree
template <typename DeviceTag>
class FindRegularByGlobalDeviceData
{
public:
using IndicesPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceTag>::PortalConst;
VTKM_EXEC_CONT
FindRegularByGlobalDeviceData(
vtkm::cont::Token& token,
const vtkm::worklet::contourtree_augmented::IdArrayType& regularNodeSortOrder,
const vtkm::worklet::contourtree_augmented::IdArrayType& regularNodeGlobalIds)
{
// Prepare the arrays for input and store the array portals
// so that they can be used inside a workelt
this->RegularNodeSortOrder = regularNodeSortOrder.PrepareForInput(DeviceTag(), token);
this->RegularNodeGlobalIds = regularNodeGlobalIds.PrepareForInput(DeviceTag(), token);
}
/// Define also as an operator so that we can use it in ArrayHandleTransform directly
VTKM_EXEC_CONT
vtkm::Id operator()(vtkm::Id globalId) const { return this->FindRegularByGlobal(globalId); }
// TODO: This is just a binary search. Does VTKm have an implementation we can use to replace this with?
/// routine to search the array of regular nodes for a particular global ID
VTKM_EXEC_CONT
vtkm::Id FindRegularByGlobal(vtkm::Id globalId) const
{ // FindRegularByGlobal()
// this is just a binary search, but the C++ STL doesn't seem to implement it . . .
vtkm::Id left = 0;
vtkm::Id right = this->RegularNodeSortOrder.GetNumberOfValues() - 1;
// pull LHE into register & check whether target is in range
vtkm::Id leftId = this->RegularNodeGlobalIds.Get(this->RegularNodeSortOrder.Get(left));
if (leftId > globalId)
{
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
// exact match on the left
if (leftId == globalId)
{
return this->RegularNodeSortOrder.Get(left);
}
// pull RHE into register & check whether target is in range
vtkm::Id rightId = this->RegularNodeGlobalIds.Get(this->RegularNodeSortOrder.Get(right));
// RHE is less than target, so not present
if (rightId < globalId)
{
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
// exact match on the right
if (rightId == globalId)
{
return this->RegularNodeSortOrder.Get(right);
}
// loop until the two meet
while (left <= right)
{ // while loop
// compute the midpoint
vtkm::Id mid = (left + right) / 2;
vtkm::Id midId = this->RegularNodeGlobalIds.Get(this->RegularNodeSortOrder.Get(mid));
// compare with the value
if (midId == globalId)
{ // exact match
return this->RegularNodeSortOrder.Get(mid);
} // exact match
if (midId < globalId)
{ // mid is lower: global is in right half
left = mid + 1;
leftId = this->RegularNodeGlobalIds.Get(this->RegularNodeSortOrder.Get(left));
} // mid is lower: global is in right half
if (midId > globalId)
{ // mid is higher: global is in left half
right = mid - 1;
rightId = this->RegularNodeGlobalIds.Get(this->RegularNodeSortOrder.Get(right));
} // mid is higher: global is in left half
} // while loop
// if we fell through, we didn't find it
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
} // FindRegularByGlobal()
private:
// Array portals needed by FindRegularByGlobal
IndicesPortalType RegularNodeSortOrder;
IndicesPortalType RegularNodeGlobalIds;
};
/// ExecutionObject to generate a device object to use FindRegularByGlobal for the HierarchicalContourTree
class FindRegularByGlobal : public vtkm::cont::ExecutionObjectBase
{
public:
/// constructor
VTKM_EXEC_CONT
FindRegularByGlobal(const vtkm::worklet::contourtree_augmented::IdArrayType& regularNodeSortOrder,
const vtkm::worklet::contourtree_augmented::IdArrayType& regularNodeGlobalIds)
: RegularNodeSortOrder(regularNodeSortOrder)
, RegularNodeGlobalIds(regularNodeGlobalIds)
{
}
VTKM_CONT
template <typename DeviceTag>
FindRegularByGlobalDeviceData<DeviceTag> PrepareForExecution(DeviceTag,
vtkm::cont::Token& token) const
{
return FindRegularByGlobalDeviceData<DeviceTag>(
token, this->RegularNodeSortOrder, this->RegularNodeGlobalIds);
}
private:
// Array portals needed by FindRegularByGlobal
vtkm::worklet::contourtree_augmented::IdArrayType RegularNodeSortOrder;
vtkm::worklet::contourtree_augmented::IdArrayType RegularNodeGlobalIds;
};
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,504 @@
//============================================================================
// 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_find_superarc_for_unknown_node_h
#define vtk_m_worklet_contourtree_distributed_find_superarc_for_unknown_node_h
#include <vtkm/Types.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
/// Device implementation of FindSuperArcForUnknownNode for the HierarchicalContourTree
template <typename FieldType, typename DeviceTag>
class FindSuperArcForUnknownNodeDeviceData
{
public:
using IndicesPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceTag>::PortalConst;
using DataPortalType =
typename vtkm::cont::ArrayHandle<FieldType>::template ExecutionTypes<DeviceTag>::PortalConst;
VTKM_EXEC_CONT
FindSuperArcForUnknownNodeDeviceData(
vtkm::cont::Token& token,
const vtkm::worklet::contourtree_augmented::IdArrayType& superparents,
const vtkm::worklet::contourtree_augmented::IdArrayType& supernodes,
const vtkm::worklet::contourtree_augmented::IdArrayType& superarcs,
const vtkm::worklet::contourtree_augmented::IdArrayType& superchildren,
const vtkm::worklet::contourtree_augmented::IdArrayType& whichRound,
const vtkm::worklet::contourtree_augmented::IdArrayType& whichIteration,
const vtkm::worklet::contourtree_augmented::IdArrayType& hyperparents,
const vtkm::worklet::contourtree_augmented::IdArrayType& hypernodes,
const vtkm::worklet::contourtree_augmented::IdArrayType& hyperarcs,
const vtkm::worklet::contourtree_augmented::IdArrayType& regularNodeGlobalIds,
const vtkm::cont::ArrayHandle<FieldType>& dataValues)
{
// Prepare the arrays for input and store the array portals
// so that they can be used inside a workelt
this->Superparents = superparents.PrepareForInput(DeviceTag(), token);
this->Supernodes = supernodes.PrepareForInput(DeviceTag(), token);
this->Superarcs = superarcs.PrepareForInput(DeviceTag(), token);
this->Superchildren = superchildren.PrepareForInput(DeviceTag(), token);
this->WhichRound = whichRound.PrepareForInput(DeviceTag(), token);
this->WhichIteration = whichIteration.PrepareForInput(DeviceTag(), token);
this->Hyperparents = hyperparents.PrepareForInput(DeviceTag(), token);
this->Hypernodes = hypernodes.PrepareForInput(DeviceTag(), token);
this->Hyperarcs = hyperarcs.PrepareForInput(DeviceTag(), token);
this->RegularNodeGlobalIds = regularNodeGlobalIds.PrepareForInput(DeviceTag(), token);
this->DataValues = dataValues.PrepareForInput(DeviceTag(), token);
}
/// routine to find the superarc to which a given global Id/value pair maps
/// given a known pair of vertices by their regular Ids, one above, one below
/// assumes that the vertex being searched for is NOT in the hierarchical tree (at all)
/// and that the above/below pair ARE in the hierarchical tree
VTKM_EXEC
vtkm::Id FindSuperArcForUnknownNode(vtkm::Id nodeGlobalId,
FieldType nodeValue,
vtkm::Id above,
vtkm::Id below) const
{ // FindSuperArcForUnknownNode()
// the hyperparent which we need to search along
vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// to find the superarc, we will first have to convert the above / below to a pair of super/hypernodes
vtkm::Id aboveSuperparent = this->Superparents.Get(above);
vtkm::Id belowSuperparent = this->Superparents.Get(below);
// if the two superparents match, we must be on the same superarc, and we are done
if (aboveSuperparent == belowSuperparent)
{
return aboveSuperparent;
}
// now it gets slightly tricky. While we know that the above / below pair straddle the node of interest, it is not guaranteed that
// their superparents will. What we do is to take the two ends of the "above" superarc - one of which is guaranteed to be at least as high
// as the above node. We choose that, and the inverse at the lower end. We can determine this from the ascending flag for the superarc
vtkm::Id aboveSuperarc = this->Superarcs.Get(aboveSuperparent);
// there are two possibilities here.
// I. It could be null, which means that "aboveSuperparent" is either the root of the tree or an attachment point. This can only happen
// if "above" IS the root/attachment point, so we can safely keep it.
// II. In all other cases, there is a superarc, and if it's ascending, we take the destination instead of the source.
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(aboveSuperarc) &&
(vtkm::worklet::contourtree_augmented::IsAscending(aboveSuperarc)))
{
aboveSuperparent = vtkm::worklet::contourtree_augmented::MaskedIndex(aboveSuperarc);
}
// and the same at the lower end
vtkm::Id belowSuperarc = this->Superarcs.Get(belowSuperparent);
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(belowSuperarc) &&
(!vtkm::worklet::contourtree_augmented::IsAscending(belowSuperarc)))
{
belowSuperparent = vtkm::worklet::contourtree_augmented::MaskedIndex(belowSuperarc);
}
// we now have as an invariant that the above, below supernodes straddle the node of interest
// retrieve the corresponding hyperparents: we no longer need to worry whether we straddle, as the iteration takes care of pruning in
// the correct direction
vtkm::Id aboveHyperparent = this->Hyperparents.Get(aboveSuperparent);
vtkm::Id belowHyperparent = this->Hyperparents.Get(belowSuperparent);
// now test to see if we have the same hyperparent:
// if we do, choose it and fall through the following while loop
if (aboveHyperparent == belowHyperparent)
{
hyperparent = aboveHyperparent;
}
// loop until we have matching hyperparents - i.e. until we're on the same hyperarc
while (aboveHyperparent != belowHyperparent)
{ // different hyperparents
// otherwise, they must be different, and we can ask which prunes first
// the rule is that we do it by hierarchical round first, iteration second, so
vtkm::Id belowRound = this->WhichRound.Get(this->Hypernodes.Get(belowHyperparent));
vtkm::Id belowIteration = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhichIteration.Get(this->Hypernodes.Get(belowHyperparent)));
vtkm::Id aboveRound = this->WhichRound.Get(this->Hypernodes.Get(aboveHyperparent));
vtkm::Id aboveIteration = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhichIteration.Get(this->Hypernodes.Get(aboveHyperparent)));
// and a variable for which end prunes first
vtkm::Id pruningEnd = PRUNE_LOW;
// now search until we find a hyperarc
while (vtkm::worklet::contourtree_augmented::NoSuchElement(hyperparent))
{ // until we have set a hyperparent
// decide which end prunes first
// low round #'s prune first
if (belowRound < aboveRound)
{
pruningEnd = PRUNE_LOW;
}
else if (belowRound > aboveRound)
{
pruningEnd = PRUNE_HIGH;
}
// in the same round, low iterations prune first
else if (belowIteration < aboveIteration)
{
pruningEnd = PRUNE_LOW;
}
else if (belowIteration > aboveIteration)
{
pruningEnd = PRUNE_HIGH;
}
// perfect match
else if (aboveHyperparent == belowHyperparent)
{
pruningEnd = PRUNE_FINAL;
}
else // prune either end first
{
pruningEnd = PRUNE_LOW;
}
// now, depending on the case
switch (pruningEnd)
{ // switch on pruning end
case PRUNE_FINAL:
{ // last hyperarc left can prune both ends simultaneously
// in this case, both have the same hyperparent set (& its arbitrary between them)
// this will cause the loop to exit
hyperparent = aboveHyperparent;
break;
} // last hyperarc left can prune both ends simultaneously
case PRUNE_LOW:
{ // low end prunes first
// here, we test the hyperarc to see if the upper end is higher than the target
// if it is, we've overshot, but at least we now know which hyperarc
vtkm::Id hyperTarget = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->Hyperarcs.Get(belowHyperparent));
vtkm::Id hyperTargetRegularId = this->Supernodes.Get(hyperTarget);
vtkm::Id hyperTargetGlobalId = this->RegularNodeGlobalIds.Get(hyperTargetRegularId);
FieldType hyperTargetValue = this->DataValues.Get(hyperTargetRegularId);
// now compare, with simulation of simplicity
// success means we've found the hyperarc
if ((hyperTargetValue > nodeValue) ||
((hyperTargetValue == nodeValue) && (hyperTargetGlobalId > nodeGlobalId)))
{ // overshoot
hyperparent = belowHyperparent;
} // overshoot
// failure means we update the low end and keep going
else
{ // no overshoot
// the next hyperarc is always the hyperparent, even for attachment points
belowHyperparent = this->Hyperparents.Get(hyperTarget);
// the round and iteration, however, need to be set from the hyperparent
// since an attachment point will have a different round / iteration from it's hyperparent
belowSuperparent = this->Hypernodes.Get(belowHyperparent);
belowRound = this->WhichRound.Get(belowSuperparent);
belowIteration = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhichIteration.Get(belowSuperparent));
} // no overshoot
break;
} // low end prunes first
case PRUNE_HIGH:
{ // high end prunes first
// here, we test the hyperarc to see if the lower end is lower than the target
// if it is, we've overshot, but at least we now know which hyperarc
// this differs from the hypersweep logic in the regular tree
vtkm::Id hyperTarget = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->Hyperarcs.Get(aboveHyperparent));
vtkm::Id hyperTargetRegularId = this->Supernodes.Get(hyperTarget);
vtkm::Id hyperTargetGlobalId = this->RegularNodeGlobalIds.Get(hyperTargetRegularId);
FieldType hyperTargetValue = this->DataValues.Get(hyperTargetRegularId);
// now compare, with simulation of simplicity
// success means we've found the hyperarc
if ((hyperTargetValue < nodeValue) ||
((hyperTargetValue == nodeValue) && (hyperTargetGlobalId < nodeGlobalId)))
{ // overshoot
hyperparent = aboveHyperparent;
} // overshoot
// failure means we update the low end and keep going
else
{ // no overshoot
// the next hyperarc is always the hyperparent, even for attachment points
aboveHyperparent = this->Hyperparents.Get(hyperTarget);
// the round and iteration, however, need to be set from the hyperparent
// since an attachment point will have a different round / iteration from it's hyperparent
// this differs from the hypersweep logic in the regular tree
aboveSuperparent = this->Hypernodes.Get(aboveHyperparent);
aboveRound = this->WhichRound.Get(aboveSuperparent);
aboveIteration = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhichIteration.Get(aboveSuperparent));
} // no overshoot
break;
} // high end prunes first
} // switch on pruning end
} // until we have set a hyperparent
// if we found one, then we exit this loop too
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(hyperparent))
{
break;
}
} // different hyperparents
// We are now on the correct hyperarc and "merely" need to find the correct superarc with a binary search.
// We are, however, guaranteed to have a data value strictly in the range of the hyperarc
// Moreover, we are already guaranteed that the data value is strictly in the range on the hyperarc
if (vtkm::worklet::contourtree_augmented::IsAscending(this->Hyperarcs.Get(hyperparent)))
{ // ascending hyperarc
// the supernodes on the hyperarc are in sorted low-high order
vtkm::Id lowSupernode = this->Hypernodes.Get(hyperparent);
// now that we have stored "superchildren", this next bit is easier than it used to be
vtkm::Id highSupernode =
this->Hypernodes.Get(hyperparent) + this->Superchildren.Get(hyperparent) - 1;
// now, the high supernode may be lower than the element, because the node belongs
// between it and the high end of the hyperarc. In this case, the high supernode's ascending superarc is the correct one
vtkm::Id highSupernodeRegularId = this->Supernodes.Get(highSupernode);
vtkm::Id highSupernodeGlobalId = this->RegularNodeGlobalIds.Get(highSupernodeRegularId);
FieldType highValue = this->DataValues.Get(highSupernodeRegularId);
// simulation of simplicity
if ((highValue < nodeValue) ||
((highValue == nodeValue) && (highSupernodeGlobalId < nodeGlobalId)))
{ // last superarc
return highSupernode;
} // last superarc
// otherwise, we do a binary search of the superarcs
else
{ // node between high & low
// keep going until we span exactly
while (highSupernode - lowSupernode > 1)
{ // binary search
// find the midway supernode
vtkm::Id midSupernode = (lowSupernode + highSupernode) / 2;
vtkm::Id midSupernodeRegularId = this->Supernodes.Get(midSupernode);
vtkm::Id midSupernodeGlobalId = this->RegularNodeGlobalIds.Get(midSupernodeRegularId);
FieldType midValue = this->DataValues.Get(midSupernodeRegularId);
// test against the node (with simulation of simplicity)
if ((midValue > nodeValue) ||
((midValue == nodeValue) && (midSupernodeGlobalId > nodeGlobalId)))
{ // mid higher
highSupernode = midSupernode;
} // mid higher
// == can't happen since node is regular
else
{ // mid lower
lowSupernode = midSupernode;
} // mid lower
} // binary search
// we've now narrowed down the search and found the superarc we wanted, so return it
// for an ascending arc, the Id is that of the lower end
return lowSupernode;
} // node between high & low
} // ascending hyperarc
else
{ // descending hyperarc
// the supernodes on the hyperarc are in sorted high-low order
vtkm::Id highSupernode = this->Hypernodes.Get(hyperparent);
// now that we have stored "superchildren", this next bit is easier than it used to be
vtkm::Id lowSupernode =
this->Hypernodes.Get(hyperparent) + this->Superchildren.Get(hyperparent) - 1;
// now, the low supernode may be higher than the element, because the node belongs
// between it and the low end of the hyperarc. In this case, the low supernode's descending superarc is the correct one
vtkm::Id lowSupernodeRegularId = this->Supernodes.Get(lowSupernode);
vtkm::Id lowSupernodeGlobalId = this->RegularNodeGlobalIds.Get(lowSupernodeRegularId);
FieldType lowValue = this->DataValues.Get(lowSupernodeRegularId);
// simulation of simplicity
if ((lowValue > nodeValue) ||
((lowValue == nodeValue) && (lowSupernodeGlobalId >= nodeGlobalId)))
{
return lowSupernode;
}
// otherwise, we do a binary search of the superarcs
else
{ // node between low & high
// keep going until we span exactly
while (lowSupernode - highSupernode > 1)
{ // binary search
// find the midway supernode
vtkm::Id midSupernode = (highSupernode + lowSupernode) / 2;
vtkm::Id midSupernodeRegularId = this->Supernodes.Get(midSupernode);
vtkm::Id midSupernodeGlobalId = this->RegularNodeGlobalIds.Get(midSupernodeRegularId);
FieldType midValue = this->DataValues.Get(midSupernodeRegularId);
// test against the node (with simulation of simplicity)
if ((midValue > nodeValue) ||
((midValue == nodeValue) && (midSupernodeGlobalId > nodeGlobalId)))
{ // mid higher
highSupernode = midSupernode;
} // mid higher
// == can't happen since node is regular
else
{ // mid lower
lowSupernode = midSupernode;
} // mid lower
} // binary search
// we've now narrowed down the search and found the superarc we wanted, so return it
// for an ascending arc, the Id is that of the lower end
return highSupernode;
} // node between low & high
} // descending hyperarc
// paranoia - return a known bad value
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
} // FindSuperArcForUnknownNode()
private:
// these are used to make it simpler to search through the hierarchy
static constexpr vtkm::Id PRUNE_LOW = static_cast<vtkm::Id>(0);
static constexpr vtkm::Id PRUNE_HIGH = static_cast<vtkm::Id>(1);
static constexpr vtkm::Id PRUNE_FINAL = static_cast<vtkm::Id>(2);
// Array portals needed by FindSuperArcForUnknownNode
// These arrays all originate from the HierarchicalContourTree
IndicesPortalType Superparents;
IndicesPortalType Supernodes;
IndicesPortalType Superarcs;
IndicesPortalType Superchildren;
IndicesPortalType WhichRound;
IndicesPortalType WhichIteration;
IndicesPortalType Hyperparents;
IndicesPortalType Hypernodes;
IndicesPortalType Hyperarcs;
IndicesPortalType RegularNodeGlobalIds;
DataPortalType DataValues;
};
/// ExecutionObject to generate a device object to use FindSuperArcForUnknownNode for the HierarchicalContourTree
///
/// This is a routine to find the superarc to which a given global Id/value pair maps
/// given a known pair of vertices by their regular Ids, one above, one below
/// assumes that the vertex being searched for is NOT in the hierarchical tree (at all)
/// and that the above/below pair ARE in the hierarchical tree
template <typename FieldType>
class FindSuperArcForUnknownNode : public vtkm::cont::ExecutionObjectBase
{
public:
/// constructor
VTKM_EXEC_CONT
FindSuperArcForUnknownNode(
const vtkm::worklet::contourtree_augmented::IdArrayType& superparents,
const vtkm::worklet::contourtree_augmented::IdArrayType& supernodes,
const vtkm::worklet::contourtree_augmented::IdArrayType& superarcs,
const vtkm::worklet::contourtree_augmented::IdArrayType& superchildren,
const vtkm::worklet::contourtree_augmented::IdArrayType& whichRound,
const vtkm::worklet::contourtree_augmented::IdArrayType& whichIteration,
const vtkm::worklet::contourtree_augmented::IdArrayType& hyperparents,
const vtkm::worklet::contourtree_augmented::IdArrayType& hypernodes,
const vtkm::worklet::contourtree_augmented::IdArrayType& hyperarcs,
const vtkm::worklet::contourtree_augmented::IdArrayType& regularNodeGlobalIds,
const vtkm::cont::ArrayHandle<FieldType>& dataValues)
: Superparents(superparents)
, Supernodes(supernodes)
, Superarcs(superarcs)
, Superchildren(superchildren)
, WhichRound(whichRound)
, WhichIteration(whichIteration)
, Hyperparents(hyperparents)
, Hypernodes(hypernodes)
, Hyperarcs(hyperarcs)
, RegularNodeGlobalIds(regularNodeGlobalIds)
, DataValues(dataValues)
{
}
VTKM_CONT
template <typename DeviceTag>
FindSuperArcForUnknownNodeDeviceData<FieldType, DeviceTag> PrepareForExecution(
DeviceTag,
vtkm::cont::Token& token) const
{
return FindSuperArcForUnknownNodeDeviceData<FieldType, DeviceTag>(token,
this->Superparents,
this->Supernodes,
this->Superarcs,
this->Superchildren,
this->WhichRound,
this->WhichIteration,
this->Hyperparents,
this->Hypernodes,
this->Hyperarcs,
this->RegularNodeGlobalIds,
this->DataValues);
}
private:
// Arrays needed by FindSuperArcForUnknownNode
// These arrays all originate from the HierarchicalContourTree
vtkm::worklet::contourtree_augmented::IdArrayType Superparents;
vtkm::worklet::contourtree_augmented::IdArrayType Supernodes;
vtkm::worklet::contourtree_augmented::IdArrayType Superarcs;
vtkm::worklet::contourtree_augmented::IdArrayType Superchildren;
vtkm::worklet::contourtree_augmented::IdArrayType WhichRound;
vtkm::worklet::contourtree_augmented::IdArrayType WhichIteration;
vtkm::worklet::contourtree_augmented::IdArrayType Hyperparents;
vtkm::worklet::contourtree_augmented::IdArrayType Hypernodes;
vtkm::worklet::contourtree_augmented::IdArrayType Hyperarcs;
vtkm::worklet::contourtree_augmented::IdArrayType RegularNodeGlobalIds;
vtkm::cont::ArrayHandle<FieldType> DataValues;
};
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,122 @@
//============================================================================
// 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_hierarchical_contour_tree_initialize_superchildren_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_contour_tree_initialize_superchildren_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
/// Compute the superarc "to" for every bract node
/// Part of the BoundaryRestrictedAugmentedContourTree.FindBractSuperarcs function
class InitalizeSuperchildrenWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn hyperarcs, // input
WholeArrayIn hypernodes, // input
FieldOut superchildren // output
);
using ExecutionSignature = _3(InputIndex, _1, _2);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
InitalizeSuperchildrenWorklet() {}
template <typename InFieldPortalType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& hyperarc,
const vtkm::Id& hyperarcVal,
const InFieldPortalType& hypernodesPortal) const
{
// per hyperarc
// test for root element
if (vtkm::worklet::contourtree_augmented::NoSuchElement(hyperarcVal))
{
// root has one superchild: the root supernode
return static_cast<vtkm::Id>(1);
}
else
{
// otherwise, compute a delta
return hypernodesPortal.Get(hyperarc + 1) - hypernodesPortal.Get(hyperarc);
}
// In serial this worklet implements the following operation
/*
for (indexType hyperarc = 0; hyperarc < hyperarcs.size(); hyperarc++)
{ // per hyperarc
// test for root element
if (noSuchElement(hyperarcs[hyperarc]))
// root has one superchild: the root supernode
superchildren[hyperarc] = 1;
else
// otherwise, compute a delta
superchildren[hyperarc] = hypernodes[hyperarc+1] - hypernodes[hyperarc];
} // per hyperarc
*/
} // operator()()
}; // InitalizeSuperchildrenWorklet
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,148 @@
//============================================================================
// 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_permute_comparator_h
#define vtk_m_worklet_contourtree_distributed_permute_comparator_h
#include <vtkm/Types.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
// comparator used for initial sort of data values
template <typename DeviceAdapter>
class PermuteComparatorImpl
{
public:
using IdPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceAdapter>::PortalConst;
// constructor - takes vectors as parameters
VTKM_CONT
PermuteComparatorImpl(const vtkm::worklet::contourtree_augmented::IdArrayType& lookup,
vtkm::cont::Token& token)
{ // constructor
LookupPortal = lookup.PrepareForInput(DeviceAdapter(), token);
} // constructor
// () operator - gets called to do comparison
VTKM_EXEC
bool operator()(const vtkm::Id& i, const vtkm::Id& j) const
{ // operator()
// first make sure we have the "top" end set correctly
vtkm::Id indI = this->LookupPortal.Get(i);
vtkm::Id indJ = this->LookupPortal.Get(j);
// primary sort on indexed value
if (indI < indJ)
{
return true;
}
if (indI > indJ)
{
return false;
}
// secondart sort on index
if (i < j)
{
return true;
}
if (j < i)
{
return false;
}
// fallback just in case
return false;
} // operator()
private:
IdPortalType LookupPortal;
}; // PermuteComparatorImpl
class PermuteComparator : public vtkm::cont::ExecutionObjectBase
{
public:
// constructor - takes vectors as parameters
VTKM_CONT
PermuteComparator(const vtkm::worklet::contourtree_augmented::IdArrayType& lookup)
: Lookup(lookup)
{
}
template <typename DeviceAdapter>
VTKM_CONT PermuteComparatorImpl<DeviceAdapter> PrepareForExecution(DeviceAdapter,
vtkm::cont::Token& token) const
{
return PermuteComparatorImpl<DeviceAdapter>(this->Lookup, token);
}
private:
vtkm::worklet::contourtree_augmented::IdArrayType Lookup;
}; // PermuteComparator
} // namespace contourtree_augmented
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,37 @@
##============================================================================
## 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
GetHierarchicalIdsWorklet.h
InitActiceSuperarcIdWorklet.h
InitActiceSuperarcsWorklet.h
GraftInteriorForestsSetTransferIterationWorklet.h
FindCriticalPointsSetUpDownNeighboursWorklet.h
FindCriticalPointsFindSaddlesWorklet.h
FindCriticalPointsFindLeafsWorklet.h
FindCriticalPointsFindTerminalElementsWorklet.h
CollapseRegularChainsWorklet.h
IdentifyLeafHyperarcsWorklet.h
SuperarcWasTransferredPredicate.h
NewHypernodePredicate.h
HyperNodeWhenComparator.h
SuperNodeWhenComparator.h
NewNodePredicate.h
ListNewNodesCopyIdsWorklet.h
CopyNewHypernodesWorklet.h
CopyNewSupernodesWorklet.h
CopyNewSupernodesSetSuperchildrenWorklet.h
PermuteComparator.h
CopyNewNodesSetSuperparentsWorklet.h
CopyFirstSupernodePerIterationWorklet.h
CopyFirstHypernodePerIterationWorklet.h
)
vtkm_declare_headers(${headers})

@ -0,0 +1,141 @@
//============================================================================
// 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_tree_grafter_collapse_regular_chains_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_collapse_regular_chains_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet implementing the inner parallel loop to collaps the regular chains in TreeGrafter.CollapseRegularChains
class CollapseRegularChainsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
activeSuperarcs, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayInOut upNeighbour, // output/input
WholeArrayInOut downNeighbour // output/input
);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CollapseRegularChainsWorklet() {}
template <typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::worklet::contourtree_augmented::EdgePair& activeSuperarc,
const InOutFieldPortalType& upNeighbourPortal,
const InOutFieldPortalType& downNeighbourPortal) const
{ // operator ()
// per active superarc
vtkm::Id lowEnd = activeSuperarc.first;
vtkm::Id highEnd = activeSuperarc.second;
// retrieve both ends
vtkm::Id lowEndUpNeighbour = upNeighbourPortal.Get(lowEnd);
vtkm::Id highEndDownNeighbour = downNeighbourPortal.Get(highEnd);
// if the lower end's up is not terminal, update
if (!vtkm::worklet::contourtree_augmented::IsTerminalElement(lowEndUpNeighbour))
{
upNeighbourPortal.Set(lowEnd, upNeighbourPortal.Get(lowEndUpNeighbour));
}
// if the upper end's down is not terminal, update
if (!vtkm::worklet::contourtree_augmented::IsTerminalElement(highEndDownNeighbour))
{
downNeighbourPortal.Set(highEnd, downNeighbourPortal.Get(highEndDownNeighbour));
}
// In serial this worklet implements the following operation
/*
// loop through the vertices, updating up and down
for (indexType activeSuper = 0; activeSuper < activeSuperarcs.size(); activeSuper++)
{ // per active superarc
indexType lowEnd = activeSuperarcs[activeSuper].low, highEnd = activeSuperarcs[activeSuper].high;
// retrieve both ends
indexType lowEndUpNeighbour = upNeighbour[lowEnd];
indexType highEndDownNeighbour = downNeighbour[highEnd];
// if the lower end's up is not terminal, update
if (!isTerminalElement(lowEndUpNeighbour))
upNeighbour[lowEnd] = upNeighbour[lowEndUpNeighbour];
// if the upper end's down is not terminal, update
if (!isTerminalElement(highEndDownNeighbour))
downNeighbour[highEnd] = downNeighbour[highEndDownNeighbour];
} // per active superarc
*/
} // operator ()
}; // CollapseRegularChainsWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,147 @@
//============================================================================
// 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_tree_grafter_copy_first_hypernode_per_iteration_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_copy_first_hypernode_per_iteration_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Worklet implementing the copy of the first supernode per iteration in TreeGrafter::CopyIterationDetails
class CopyFirstHypernodePerIterationWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
newHypernode, // input. Set to ArrayHandleCounting<vtkm::Id>(nOldHypernodes, 1, nTotalHypernodes-nOldHypernodes)
WholeArrayIn hierarchicalTreeHypernodes, // input
WholeArrayIn hierarchicalTreeWhichIteration, //input
WholeArrayInOut
hierarchicalTreeFirstHypernodePerIteration //output. should be set to hierarchicalTree.firstHypernodePerIteration[theRound]. // TODO: is it sufficient to use WholeArrayOut?
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CopyFirstHypernodePerIterationWorklet(vtkm::Id numOldHypernodes)
: NumOldHypernodes(numOldHypernodes)
{
}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(
const vtkm::Id& newHypernode,
const InFieldPortalType& hierarchicalTreeHypernodesPortal,
const InFieldPortalType& hierarchicalTreeWhichIterationPortal,
const OutFieldPortalType& hierarchicalTreeFirstHypernodePerIterationPortal) const
{ // operator ()
// per new hypernode
vtkm::Id supernodeId = hierarchicalTreeHypernodesPortal.Get(newHypernode);
vtkm::Id wasTransferred = vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeWhichIterationPortal.Get(supernodeId));
// the first one defines the zeroth iteration: changes in iteration number define the others
if (newHypernode == this->NumOldHypernodes)
{ // LHE
hierarchicalTreeFirstHypernodePerIterationPortal.Set(0, newHypernode);
} // LHE
else if (wasTransferred !=
vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeWhichIterationPortal.Get(
hierarchicalTreeHypernodesPortal.Get(newHypernode - 1))))
{ // other breakpoint
hierarchicalTreeFirstHypernodePerIterationPortal.Set(wasTransferred, newHypernode);
} // other breakpoint
// In serial this worklet implements the following operation
/*
for (indexType newHypernode = nOldHypernodes; newHypernode < nTotalHypernodes; newHypernode++)
{ // per new hypernode
indexType supernodeID = hierarchicalTree.hypernodes[newHypernode];
indexType wasTransferred = maskedIndex(hierarchicalTree.whichIteration[supernodeID]);
// the first one defines the zeroth iteration: changes in iteration number define the others
if (newHypernode == nOldHypernodes)
{ // LHE
hierarchicalTree.firstHypernodePerIteration[theRound][0] = newHypernode;
} // LHE
else if (wasTransferred != maskedIndex(hierarchicalTree.whichIteration[hierarchicalTree.hypernodes[newHypernode-1]]))
{ // other breakpoint
hierarchicalTree.firstHypernodePerIteration[theRound][wasTransferred] = newHypernode;
} // other breakpoint
} // per new hypernode
*/
} // operator ()
private:
vtkm::Id NumOldHypernodes;
}; // CopyNewHypernodes
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,147 @@
//============================================================================
// 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_tree_grafter_copy_first_supernode_per_iteration_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_copy_first_supernode_per_iteration_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Worklet implementing the copy of the first supernode per iteration in TreeGrafter::CopyIterationDetails
class CopyFirstSupernodePerIterationWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
newSupernode, // fancy ArrayHandleCounting(nOldSupernodes, 1, nTotalSupernodes - nOldSupernodes)
WholeArrayIn hierarchicalTreeWhichIteration, // input
WholeArrayInOut
hierarchicalTreeFirstSupernodePerIteration // output. Should be set to hierarchicalTree.firstSupernodePerIteration[theRound]/ // TODO: is it sufficient to use WholeArrayOut?
);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CopyFirstSupernodePerIterationWorklet(vtkm::Id numOldSupernodes)
: NumOldSupernodes(numOldSupernodes)
{
}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(
const vtkm::Id& newSupernode,
const InFieldPortalType& hierarchicalTreeWhichIterationPortal,
const OutFieldPortalType& hierarchicalTreeFirstSupernodePerIterationPortal
) const
{ // operator ()
// per new supernode
// the LHE is the first new supernode: it just stores
if (newSupernode == this->NumOldSupernodes)
{
hierarchicalTreeFirstSupernodePerIterationPortal.Set(0, newSupernode);
}
// otherwise we only write if we detect a change in iterations
else
{ // not LHE
if (vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeWhichIterationPortal.Get(newSupernode)) !=
vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeWhichIterationPortal.Get(newSupernode - 1)))
{
hierarchicalTreeFirstSupernodePerIterationPortal.Set(
vtkm::worklet::contourtree_augmented::MaskedIndex(
hierarchicalTreeWhichIterationPortal.Get(newSupernode)),
newSupernode);
}
} // not LHE
// In serial this worklet implements the following operation
/*
for (indexType newSupernode = nOldSupernodes; newSupernode < nTotalSupernodes; newSupernode++)
{ // per new supernode
// the LHE is the first new supernode: it just stores
if (newSupernode == nOldSupernodes)
hierarchicalTree.firstSupernodePerIteration[theRound][0] = newSupernode;
// otherwise we only write if we detect a change in iterations
else
{ // not LHE
if (maskedIndex(hierarchicalTree.whichIteration[newSupernode]) != maskedIndex(hierarchicalTree.whichIteration[newSupernode-1]))
hierarchicalTree.firstSupernodePerIteration[theRound][maskedIndex(hierarchicalTree.whichIteration[newSupernode])] = newSupernode;
} // not LHE
} // per new supernode
*/
} // operator ()
private:
vtkm::Id NumOldSupernodes;
}; // CopyNewHypernodes
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,161 @@
//============================================================================
// 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_tree_grafter_copy_new_hypernodes_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_copy_new_hypernodes_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet implementing the inner parallel loop to collaps the regular chains in TreeGrafter.CollapseRegularChains
class CopyNewHypernodesWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn newHypernodes, // input iteration index.
WholeArrayIn hierarchicalSuperId, // input
WholeArrayIn hierarchicalHyperarc, // input
WholeArrayOut hierarchicalTreeHypernodes, // output
WholeArrayOut hierarchicalTreeHyperarcs // output
);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CopyNewHypernodesWorklet(vtkm::Id numOldHypernodes)
: NumOldHypernodes(numOldHypernodes)
{
}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(
const vtkm::Id& newHypernode, // position in newHypernodes arrays
const vtkm::Id&
oldSupernodeId, // old hypernode ID & super ID, i.e., newHypernodes[newHypernode]
const InFieldPortalType& hierarchicalSuperIdPortal, // input
const InFieldPortalType& hierarchicalHyperarcPortal, // input
const OutFieldPortalType& hierarchicalTreeHypernodesPortal, // output
const OutFieldPortalType& hierarchicalTreeHyperarcsPortal // output
) const
{ // operator ()
// per new hypernode
// retrieve the old hypernode ID & super ID (DONE as part of the worklet invocation)
// convert into new IDs
vtkm::Id newHypernodeId = this->NumOldHypernodes + newHypernode;
vtkm::Id newHypernodeSuperId = hierarchicalSuperIdPortal.Get(oldSupernodeId);
// store the new hypernode ID
hierarchicalTreeHypernodesPortal.Set(newHypernodeId, newHypernodeSuperId);
// retrieve the hyperarc and convert, carrying masking bits
vtkm::Id newHyperarcOldSuperId = hierarchicalHyperarcPortal.Get(oldSupernodeId);
vtkm::Id isAscendingHyperarc =
vtkm::worklet::contourtree_augmented::IsAscending(newHyperarcOldSuperId)
? vtkm::worklet::contourtree_augmented::IS_ASCENDING
: 0x0;
newHyperarcOldSuperId =
vtkm::worklet::contourtree_augmented::MaskedIndex(newHyperarcOldSuperId);
vtkm::Id newHyperarcNewSuperId =
hierarchicalSuperIdPortal.Get(newHyperarcOldSuperId) | isAscendingHyperarc;
// and store it
hierarchicalTreeHyperarcsPortal.Set(newHypernodeId, newHyperarcNewSuperId);
// In serial this worklet implements the following operation
/*
// B. Copy in the hypernodes & hyperarcs
for (indexType newHypernode = 0; newHypernode < nNewHypernodes; newHypernode++)
{ // per new hypernode
// retrieve the old hypernode ID & super ID
indexType oldSupernodeID = newHypernodes[newHypernode];
// convert into new IDs
indexType newHypernodeID = nOldHypernodes + newHypernode;
indexType newHypernodeSuperID = hierarchicalSuperID[oldSupernodeID];
// store the new hypernode ID
hierarchicalTree.hypernodes[newHypernodeID] = newHypernodeSuperID;
// retrieve the hyperarc and convert, carrying masking bits
indexType newHyperarcOldSuperID = hierarchicalHyperarc[oldSupernodeID];
indexType isAscendingHyperarc = isAscending(newHyperarcOldSuperID) ? IS_ASCENDING : 0x0;
newHyperarcOldSuperID = maskedIndex(newHyperarcOldSuperID);
indexType newHyperarcNewSuperID = hierarchicalSuperID[newHyperarcOldSuperID] | isAscendingHyperarc;
// and store it
hierarchicalTree.hyperarcs[newHypernodeID] = newHyperarcNewSuperID;
} // per new hypernode
*/
} // operator ()
private:
vtkm::Id NumOldHypernodes;
}; // CopyNewHypernodes
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,226 @@
//============================================================================
// 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_tree_grafter_copy_new_nodes_set_superparents_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_copy_new_nodes_set_superparents_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet implementing the sorting out the superparents as part of TreeGrafter::CopyNewNodes
class CopyNewNodesSetSuperparentsWorklet : public vtkm::worklet::WorkletMapField
{
public:
// TODO: Some WholeArrayIn could potentially be made FieldIn if we reshuffeled the arrays via newNodes beforehand
using ControlSignature = void(FieldIn newNodes, // input and iteration index
WholeArrayIn meshSortIndex, // input
WholeArrayIn meshSortOrder, // input
WholeArrayIn contourTreeSuperparents, // input
WholeArrayIn contourTreeSuperarcs, // input
WholeArrayIn contourTreeSupernodes, // input
WholeArrayIn hierarchicalRegularId, // input
WholeArrayIn hierarchicalTreeId, // input
WholeArrayIn hierarchicalTreeRegularNodeGlobalIds, // input
WholeArrayIn hierarchicalTreeDataValues, // input
ExecObject findSuperArcForUnknownNode, // input
WholeArrayOut hierarchicalTreeSuperparents // output
);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CopyNewNodesSetSuperparentsWorklet(const vtkm::Id& numOldNodes)
: NumOldNodes(numOldNodes)
{
}
template <typename InFieldPortalType,
typename MeshSortIndexPortalType,
typename MeshSortOrderPortalType,
typename DataValuePortalType,
typename FindSuperExecType,
typename OutFieldPortalType>
VTKM_EXEC void operator()(
const vtkm::Id& newNode,
const vtkm::Id&
oldNodeId, // convert to a node Id in the current level's tree when calling the worklet
const MeshSortIndexPortalType& meshSortIndexPortal,
const MeshSortOrderPortalType& meshSortOrderPortal,
const InFieldPortalType& contourTreeSuperparentsPortal,
const InFieldPortalType& contourTreeSuperarcsPortal,
const InFieldPortalType& contourTreeSupernodesPortal,
const InFieldPortalType& hierarchicalRegularIdPortal,
const InFieldPortalType& hierarchicalTreeIdPortal,
const InFieldPortalType& hierarchicalTreeRegularNodeGlobalIdsPortal,
const DataValuePortalType& hierarchicalTreeDataValuesPortal,
const FindSuperExecType& findSuperArcForUnknownNode,
const OutFieldPortalType& hierarchicalTreeSuperparentsPortal
) const
{ // operator ()
// per new node
// index into hierarchical tree
vtkm::Id newNodeId = this->NumOldNodes + newNode;
// retrieve the old parent superarc
vtkm::Id oldSortIndex = meshSortIndexPortal.Get(oldNodeId);
vtkm::Id oldSuperparent = contourTreeSuperparentsPortal.Get(oldSortIndex);
// and to a regular Id
vtkm::Id oldSuperparentNewRegularId = hierarchicalRegularIdPortal.Get(oldSuperparent);
// Assuming that the new supernodes & hypernodes have been transferred, EVERY supernode in the old tree
// now has hierarchicalRegularId set correctly. Since every regular node belongs on a superarc in the old tree,
// we can use the ends of the superarc to invoke a search in the hierarchical tree for the superparent.
// This is therefore logically dependent on having the superstructure & hyperstructure updated first
// supernodes will already have their superparent set in CopyNewSupernodes()
if (vtkm::worklet::contourtree_augmented::NoSuchElement(
hierarchicalTreeSuperparentsPortal.Get(newNodeId)))
{ // it's not a supernode
// retrieve the end of the superarc & convert to hierarchical regular Id, plus identify whether it ascends
vtkm::Id oldSupertargetSuperId = contourTreeSuperarcsPortal.Get(oldSuperparent);
bool oldSuperarcAscends =
vtkm::worklet::contourtree_augmented::IsAscending(oldSupertargetSuperId);
oldSupertargetSuperId =
vtkm::worklet::contourtree_augmented::MaskedIndex(oldSupertargetSuperId);
vtkm::Id oldSupertargetOldSortId = contourTreeSupernodesPortal.Get(oldSupertargetSuperId);
vtkm::Id oldSupertargetOldRegularId = meshSortOrderPortal.Get(oldSupertargetOldSortId);
vtkm::Id oldSupertargetNewRegularId =
hierarchicalTreeIdPortal.Get(oldSupertargetOldRegularId);
// set up variables for our pruning search
// collect the low end's values
vtkm::Id lowEndRegularId =
oldSuperarcAscends ? oldSuperparentNewRegularId : oldSupertargetNewRegularId;
vtkm::Id highEndRegularId =
oldSuperarcAscends ? oldSupertargetNewRegularId : oldSuperparentNewRegularId;
// pull the data value at the node
vtkm::Id nodeGlobalId = hierarchicalTreeRegularNodeGlobalIdsPortal.Get(newNodeId);
auto nodeValue = hierarchicalTreeDataValuesPortal.Get(newNodeId);
// now ask the hierarchical tree for the correct superparent
hierarchicalTreeSuperparentsPortal.Set(
newNodeId,
findSuperArcForUnknownNode.FindSuperArcForUnknownNode(
nodeGlobalId, nodeValue, highEndRegularId, lowEndRegularId));
} // it's not a supernode
// In serial this worklet implements the following operation
/*
for (indexType newNode = 0; newNode < newNodes.size(); newNode++)
{ // per new node
// convert to a node ID in the current level's tree
indexType oldNodeID = newNodes[newNode];
// index into hierarchical tree
indexType newNodeID = nOldNodes + newNode;
// retrieve the old parent superarc
indexType oldSortIndex = mesh->SortIndex(oldNodeID);
indexType oldSuperparent = contourTree->superparents[oldSortIndex];
// and to a regular ID
indexType oldSuperparentNewRegularID = hierarchicalRegularID[oldSuperparent];
// Assuming that the new supernodes & hypernodes have been transferred, EVERY supernode in the old tree
// now has hierarchicalRegularID set correctly. Since every regular node belongs on a superarc in the old tree,
// we can use the ends of the superarc to invoke a search in the hierarchical tree for the superparent.
// This is therefore logically dependent on having the superstructure & hyperstructure updated first
// supernodes will already have their superparent set in CopyNewSupernodes()
if (noSuchElement(hierarchicalTree.superparents[newNodeID]))
{ // it's not a supernode
// retrieve the end of the superarc & convert to hierarchical regular ID, plus identify whether it ascends
indexType oldSupertargetSuperID = contourTree->superarcs[oldSuperparent];
bool oldSuperarcAscends = isAscending(oldSupertargetSuperID);
oldSupertargetSuperID = maskedIndex(oldSupertargetSuperID);
indexType oldSupertargetOldSortID = contourTree->supernodes[oldSupertargetSuperID];
indexType oldSupertargetOldRegularID = mesh->SortOrder(oldSupertargetOldSortID);
indexType oldSupertargetNewRegularID = hierarchicalTreeID[oldSupertargetOldRegularID];
// set up variables for our pruning search
// collect the low end's values
indexType lowEndRegularID = oldSuperarcAscends ? oldSuperparentNewRegularID : oldSupertargetNewRegularID;
indexType highEndRegularID = oldSuperarcAscends ? oldSupertargetNewRegularID : oldSuperparentNewRegularID;
// pull the data value at the node
indexType nodeGlobalID = hierarchicalTree.regularNodeGlobalIDs[newNodeID];
dataType nodeValue = hierarchicalTree.dataValues[newNodeID];
// now ask the hierarchical tree for the correct superparent
hierarchicalTree.superparents[newNodeID] = hierarchicalTree.FindSuperArcForUnknownNode(nodeGlobalID, nodeValue, highEndRegularID, lowEndRegularID);
} // it's not a supernode
} // per new node
*/
} // operator ()
private:
vtkm::Id NumOldNodes;
}; // CopyNewHypernodes
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,161 @@
//============================================================================
// 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_tree_grafter_copy_new_supernodes_set_superchildren_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_copy_new_supernodes_set_superchildren_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Worklet to loop to set the number of superchildren per hyperarc as part of TreeGrafter::CopyNewSupernodes
class CopyNewSupernodesSetSuperchildrenWorklet : public vtkm::worklet::WorkletMapField
{
public:
// TODO: Access to hierarchicalTreeSuperarcs and hierarchicalTreeHyperparents could potentially be imporved by using an ArrayView instead
using ControlSignature = void(
FieldIn newSupernodeIndex, // input array starting at 0 to NewSupernodes.GetNumberOfValues();
WholeArrayIn hierarchicalTreeSuperarcs, //input
WholeArrayIn hierarchicalTreeHyperparents, // input
WholeArrayIn hierarchicalTreeHypernodes, //input
WholeArrayOut hierarchicalTreeSuperchildren // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
/// Default Constructor
/// @param[in]numhierarchicalTreeSupernodes should be set to hierarchicalTree.Supernodes.GetNumberOfValues()
VTKM_EXEC_CONT
CopyNewSupernodesSetSuperchildrenWorklet(vtkm::Id numHierarchicalTreeSupernodes)
: NumHierarchicalTreeSupernodes(numHierarchicalTreeSupernodes)
{
}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& newSupernodeIndex,
const InFieldPortalType& hierarchicalTreeSuperarcsPortal,
const InFieldPortalType& hierarchicalTreeHyperparentsPortal,
const InFieldPortalType& hierarchicalTreeHypernodesPortal,
const OutFieldPortalType hierarchicalTreeSuperchildrenPortal
) const
{ // operator ()
// convert from 0...NumNewSupernodes index to [hierarchicalTree.supernodes.size() - newSupernodes.size(), hierarchicalTree.supernodes.size())
vtkm::Id newSupernode = (this->NumHierarchicalTreeSupernodes - 1) - newSupernodeIndex;
// per new supernode
// attachment points have NULL superarcs and can be ignored
if (vtkm::worklet::contourtree_augmented::NoSuchElement(
hierarchicalTreeSuperarcsPortal.Get(newSupernode)))
{
return;
}
// OK: we are now guaranteed to have a valid hyperparent
vtkm::Id hyperparent = hierarchicalTreeHyperparentsPortal.Get(newSupernode);
// we could still be at the end of the array, so we have to test explicitly
if (newSupernode == NumHierarchicalTreeSupernodes - 1)
{
// compute the delta and store it
hierarchicalTreeSuperchildrenPortal.Set(hyperparent,
NumHierarchicalTreeSupernodes -
hierarchicalTreeHypernodesPortal.Get(hyperparent));
}
else if (hyperparent != hierarchicalTreeHyperparentsPortal.Get(newSupernode + 1))
{
hierarchicalTreeSuperchildrenPortal.Set(
hyperparent, newSupernode + 1 - hierarchicalTreeHypernodesPortal.Get(hyperparent));
}
// In serial this worklet implements the following operation
/*
for (indexType newSupernode = hierarchicalTree.supernodes.size() - newSupernodes.size(); newSupernode < hierarchicalTree.supernodes.size(); newSupernode++)
{ // per new supernode
// attachment points have NULL superarcs and can be ignored
if (noSuchElement(hierarchicalTree.superarcs[newSupernode]))
continue;
// OK: we are now guaranteed to have a valid hyperparent
indexType hyperparent = hierarchicalTree.hyperparents[newSupernode];
// we could still be at the end of the array, so we have to test explicitly
if (newSupernode == hierarchicalTree.supernodes.size() - 1)
// compute the delta and store it
hierarchicalTree.superchildren[hyperparent] = hierarchicalTree.supernodes.size() - hierarchicalTree.hypernodes[hyperparent];
else if (hyperparent != hierarchicalTree.hyperparents[newSupernode + 1])
hierarchicalTree.superchildren[hyperparent] = newSupernode + 1 - hierarchicalTree.hypernodes[hyperparent];
} // per new supernode
*/
} // operator ()
private:
vtkm::Id NumHierarchicalTreeSupernodes; /// hierarchicalTree.supernodes.size()
}; // CopyNewHypernodes
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,417 @@
//============================================================================
// 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_tree_grafter_copy_new_supernodes_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_copy_new_supernodes_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet implementing TreeGrafter.CopyNewSupernodes
class CopyNewSupernodesWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(WholeArrayIn newSupernode, // input and iteration index
WholeArrayIn contourTreeSupernodes, // input
WholeArrayIn meshSorterOrder, // input
WholeArrayIn hierarchicalTreeId, // input
WholeArrayIn whenTransferred, // input
WholeArrayIn hierarchicalSuperparent, // input
WholeArrayIn hierarchicalHyperparent, //input
WholeArrayIn hierarchicalSuperId, // input
WholeArrayIn hierarchicalHyperId, // input
WholeArrayIn hierarchicalHyperarc, // input
WholeArrayOut hierarchicalTreeSupernodes, // output
WholeArrayOut hierarchicalTreeWhichRound, // output
WholeArrayOut hierarchicalTreeWhichIteration, // output
WholeArrayOut hierarchicalTreeSuperarcs, // output
WholeArrayInOut hierarchicalRegularId, // input/output
WholeArrayInOut hierarchicalTreeHyperparents, // input/output
WholeArrayInOut hierarchicalTreeSuperparents // input/output
);
using ExecutionSignature =
void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CopyNewSupernodesWorklet(vtkm::Id theRound, vtkm::Id numOldSupernodes)
: TheRound(theRound)
, NumOldSupernodes(numOldSupernodes)
{
}
template <typename InFieldPortalType,
typename SortOrderPortalType,
typename OutFieldPortalType,
typename InOutFieldPortalType>
VTKM_EXEC void operator()(
const vtkm::Id& newSupernode,
const InFieldPortalType& newSupernodesPortal,
const InFieldPortalType& contourTreeSupernodesPortal,
const SortOrderPortalType&
meshSortOrderPortal, // depending on the mesh type these may be differnt fancy arrays
const InFieldPortalType& hierarchicalTreeIdPortal,
const InFieldPortalType& whenTransferredPortal,
const InFieldPortalType& hierarchicalSuperparentPortal,
const InFieldPortalType& hierarchicalHyperparentPortal,
const InFieldPortalType& hierarchicalSuperIdPortal,
const InFieldPortalType& hierarchicalHyperIdPortal,
const InFieldPortalType& hierarchicalHyperarcPortal,
const OutFieldPortalType& hierarchicalTreeSupernodesPortal,
const OutFieldPortalType& hierarchicalTreeWhichRoundPortal,
const OutFieldPortalType& hierarchicalTreeWhichIterationPortal,
const OutFieldPortalType& hierarchicalTreeSuperarcsPortal,
const InOutFieldPortalType& hierarchicalRegularIdPortal,
const InOutFieldPortalType& hierarchicalTreeHyperparentsPortal,
const InOutFieldPortalType& hierarchicalTreeSuperparentsPortal
) const
{ // operator ()
// per new supernode
// retrieve the old supernode & regular node Ids
vtkm::Id oldSupernodeId = newSupernodesPortal.Get(newSupernode);
vtkm::Id oldSortId = contourTreeSupernodesPortal.Get(oldSupernodeId);
vtkm::Id oldRegularId = meshSortOrderPortal.Get(oldSortId);
// convert to new Ids
vtkm::Id newRegularId = hierarchicalTreeIdPortal.Get(oldRegularId);
vtkm::Id newSupernodeId = this->NumOldSupernodes + newSupernode;
// set the supernode accordingly
hierarchicalTreeSupernodesPortal.Set(newSupernodeId, newRegularId);
// and set the round and iteration
hierarchicalTreeWhichRoundPortal.Set(newSupernodeId, this->TheRound);
hierarchicalTreeWhichIterationPortal.Set(newSupernodeId,
whenTransferredPortal.Get(oldSupernodeId));
// We want to set the superarc and hyperparent. At this point, supernodes fall into four groups:
// I. Present in the hierarchical tree as supernodes No work required (not in newSupernodes)
// II. Present in the hierarchical tree as regular nodes only Added as supernode. Hyperparent only needs to be set.
// III. Not present in the hierarchical tree: attachment point Super/hyper parent stored in hierarchical Ids
// IV. Not present, and not an attachment point Super/hyper parent stored in local Ids
// Note that I. is already taken care of, so we test whether the supernode was previously in the hierarchical tree at all
vtkm::Id storedRegularId = hierarchicalRegularIdPortal.Get(oldSupernodeId);
// and set the regular Id in the hierarchical tree (even if it is already set)
hierarchicalRegularIdPortal.Set(oldSupernodeId, newRegularId);
// now we sort out hyperparent
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(storedRegularId))
{ // present: II
// if it isn't already a supernode, it is "only" a regular node
if (newSupernodeId >= this->NumOldSupernodes)
{ // regular but not super
// in this case, it already has a superparent (because it is already present in the tree as a regular node)
// so we look up the relevant hyperparent
hierarchicalTreeHyperparentsPortal.Set(
newSupernodeId,
hierarchicalTreeHyperparentsPortal.Get(
hierarchicalTreeSuperparentsPortal.Get(storedRegularId)));
// we set this to indicate that it's an attachment point
hierarchicalTreeSuperarcsPortal.Set(newSupernodeId,
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
} // regular but not super
} // present: actually II
else
{ // not present: III or IV
// attachment point (III) or free point (IV)
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(
hierarchicalSuperparentPortal.Get(oldSupernodeId)))
{ // attachment point
// we've already captured the super-/hyper- parent in an earlier stage
hierarchicalTreeSuperparentsPortal.Set(newRegularId,
hierarchicalSuperparentPortal.Get(oldSupernodeId));
hierarchicalTreeHyperparentsPortal.Set(newSupernodeId,
hierarchicalHyperparentPortal.Get(oldSupernodeId));
// and the superarc should indicate an attachment point
hierarchicalTreeSuperarcsPortal.Set(newSupernodeId,
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
} // attachment point
// otherwise, we have a brand new free supernode, which is it's own superparent
else
{ // free point
// this is a supernode that was never in the hierarchical tree in the first place
// it is its own superparent, and has a new hyperparent in old supernode Ids (often itself)
// and can use that to look up the new hyperId
vtkm::Id hierarchicalHyperparentOldSuperId =
hierarchicalHyperparentPortal.Get(oldSupernodeId);
vtkm::Id hierarchicalHyperparentNewHyperId =
hierarchicalHyperIdPortal.Get(hierarchicalHyperparentOldSuperId);
hierarchicalTreeHyperparentsPortal.Set(newSupernodeId, hierarchicalHyperparentNewHyperId);
// since it is its own superparent, this is easy . . .
hierarchicalTreeSuperparentsPortal.Set(newRegularId, newSupernodeId);
// now the hard part: fill in the superarc
vtkm::Id hierarchicalHyperarcOldSuperId =
hierarchicalHyperarcPortal.Get(hierarchicalHyperparentOldSuperId);
vtkm::Id isAscendingHyperarc =
vtkm::worklet::contourtree_augmented::IsAscending(hierarchicalHyperarcOldSuperId)
? vtkm::worklet::contourtree_augmented::IS_ASCENDING
: 0x0;
hierarchicalHyperarcOldSuperId =
vtkm::worklet::contourtree_augmented::MaskedIndex(hierarchicalHyperarcOldSuperId);
vtkm::Id hierarchicalHyperarcNewSuperId =
hierarchicalSuperIdPortal.Get(hierarchicalHyperarcOldSuperId);
// we have located each supernode on a hyperarc
// and we have to work out the supernode each connects to
// unfortunately, the attachment points complicate this compared to the old code
// for sweeping later, we will set the # of superchildren, but we don't have that yet
// So the test will have to be the following:
// i. the "neighbour" is the +1 index
// ii. if the neighbour is off the end, we take the end of the hyperarc
// iii. if the neighbour has flagged as an attachment point, we take the end of the hyperarc
// iv. in all other cases, we take the neighbour
// Note that we are saved some trouble by the fact that this code only applies to free points
// the superarc is now set by checking to see if the neighbour has the same hyperparent:
// if it does, our superarc goes to the next element
// if not (or we're at array end), we go to the hyperarc's target
// NOTE: we will store the OLD superarc Id at this stage, since we need it to sort out regular arcs
// this means we will have to add a final loop to reset to hierarchical Ids
vtkm::Id neighbour = newSupernode + 1;
// special case at end of array: map the old hyperarc Id to a new one
if (neighbour >= newSupernodesPortal.GetNumberOfValues())
{ // end of array
hierarchicalTreeSuperarcsPortal.Set(newSupernodeId,
hierarchicalHyperarcNewSuperId | isAscendingHyperarc);
} // end of array
else
{ // not at the end of the array
vtkm::Id nbrSuperId = newSupernodesPortal.Get(neighbour);
// immediately check for case iii. by looking at the hierarchicalSuperparent of the neighbour's old Id
// if it's already set, it's because it's an attachment point
if (!vtkm::worklet::contourtree_augmented::NoSuchElement(
hierarchicalSuperparentPortal.Get(nbrSuperId)))
{ // attachment point
hierarchicalTreeSuperarcsPortal.Set(
newSupernodeId, hierarchicalHyperarcNewSuperId | isAscendingHyperarc);
} // attachment point
else
{ // not attachment point
vtkm::Id nbrHyperparent = hierarchicalHyperparentPortal.Get(nbrSuperId);
// if they share a hyperparent, just take the neighbour
if (nbrHyperparent == hierarchicalHyperparentOldSuperId)
{ // shared hyperparent
hierarchicalTreeSuperarcsPortal.Set(
newSupernodeId, hierarchicalSuperIdPortal.Get(nbrSuperId) | isAscendingHyperarc);
} // shared hyperparent
// if not, take the target of the hyperarc
else
{ // not shared hyperparent
hierarchicalTreeSuperarcsPortal.Set(
newSupernodeId, hierarchicalHyperarcNewSuperId | isAscendingHyperarc);
} // not shared hyperparent
} // not attachment point
} // not at the end of the array
} // free point
} // attachment point (III) or free point (IV)
// In serial this worklet implements the following operation
/*
for (indexType newSupernode = 0; newSupernode < nNewSupernodes; newSupernode++)
{ // per new supernode
// retrieve the old supernode & regular node IDs
indexType oldSupernodeID = newSupernodes[newSupernode];
indexType oldSortID = contourTree->supernodes[oldSupernodeID];
indexType oldRegularID = mesh->SortOrder(oldSortID);
// convert to new IDs
indexType newRegularID = hierarchicalTreeID[oldRegularID];
indexType newSupernodeID = nOldSupernodes + newSupernode;
// set the supernode accordingly
hierarchicalTree.supernodes[newSupernodeID] = newRegularID;
// and set the round and iteration
hierarchicalTree.whichRound[newSupernodeID] = theRound;
hierarchicalTree.whichIteration[newSupernodeID] = whenTransferred[oldSupernodeID];
// We want to set the superarc and hyperparent. At this point, supernodes fall into four groups:
// I. Present in the hierarchical tree as supernodes No work required (not in newSupernodes)
// II. Present in the hierarchical tree as regular nodes only Added as supernode. Hyperparent only needs to be set.
// III. Not present in the hierarchical tree: attachment point Super/hyper parent stored in hierarchical IDs
// IV. Not present, and not an attachment point Super/hyper parent stored in local IDs
// Note that I. is already taken care of, so we test whether the supernode was previously in the hierarchical tree at all
indexType storedRegularID = hierarchicalRegularID[oldSupernodeID];
// and set the regular ID in the hierarchical tree (even if it is already set)
hierarchicalRegularID[oldSupernodeID] = newRegularID;
// now we sort out hyperparent
if (!noSuchElement(storedRegularID))
{ // present: II
// if it isn't already a supernode, it is "only" a regular node
if (newSupernodeID >= nOldSupernodes)
{ // regular but not super
// in this case, it already has a superparent (because it is already present in the tree as a regular node)
// so we look up the relevant hyperparent
hierarchicalTree.hyperparents[newSupernodeID] = hierarchicalTree.hyperparents[hierarchicalTree.superparents[storedRegularID]];
// we set this to indicate that it's an attachment point
hierarchicalTree.superarcs[newSupernodeID] = NO_SUCH_ELEMENT;
} // regular but not super
} // present: actually II
else
{ // not present: III or IV
// attachment point (III) or free point (IV)
if (!noSuchElement(hierarchicalSuperparent[oldSupernodeID]))
{ // attachment point
// we've already captured the super-/hyper- parent in an earlier stage
hierarchicalTree.superparents[newRegularID] = hierarchicalSuperparent[oldSupernodeID];
hierarchicalTree.hyperparents[newSupernodeID] = hierarchicalHyperparent[oldSupernodeID];
// and the superarc should indicate an attachment point
hierarchicalTree.superarcs[newSupernodeID] = NO_SUCH_ELEMENT;
} // attachment point
// otherwise, we have a brand new free supernode, which is it's own superparent
else
{ // free point
// this is a supernode that was never in the hierarchical tree in the first place
// it is its own superparent, and has a new hyperparent in old supernode IDs (often itself)
// and can use that to look up the new hyperID
indexType hierarchicalHyperparentOldSuperID = hierarchicalHyperparent[oldSupernodeID];
indexType hierarchicalHyperparentNewHyperID = hierarchicalHyperID[hierarchicalHyperparentOldSuperID];
hierarchicalTree.hyperparents[newSupernodeID] = hierarchicalHyperparentNewHyperID;
// since it is its own superparent, this is easy . . .
hierarchicalTree.superparents[newRegularID] = newSupernodeID;
// now the hard part: fill in the superarc
indexType hierarchicalHyperarcOldSuperID = hierarchicalHyperarc[hierarchicalHyperparentOldSuperID];
indexType isAscendingHyperarc = isAscending(hierarchicalHyperarcOldSuperID) ? IS_ASCENDING : 0x0;
hierarchicalHyperarcOldSuperID = maskedIndex(hierarchicalHyperarcOldSuperID);
indexType hierarchicalHyperarcNewSuperID = hierarchicalSuperID[hierarchicalHyperarcOldSuperID];
// we have located each supernode on a hyperarc
// and we have to work out the supernode each connects to
// unfortunately, the attachment points complicate this compared to the old code
// for sweeping later, we will set the # of superchildren, but we don't have that yet
// So the test will have to be the following:
// i. the "neighbour" is the +1 index
// ii. if the neighbour is off the end, we take the end of the hyperarc
// iii. if the neighbour has flagged as an attachment point, we take the end of the hyperarc
// iv. in all other cases, we take the neighbour
// Note that we are saved some trouble by the fact that this code only applies to free points
// the superarc is now set by checking to see if the neighbour has the same hyperparent:
// if it does, our superarc goes to the next element
// if not (or we're at array end), we go to the hyperarc's target
// NOTE: we will store the OLD superarc ID at this stage, since we need it to sort out regular arcs
// this means we will have to add a final loop to reset to hierarchical IDs
indexType neighbour = newSupernode+1;
// special case at end of array: map the old hyperarc ID to a new one
if (neighbour >= newSupernodes.size())
{ // end of array
hierarchicalTree.superarcs[newSupernodeID] = hierarchicalHyperarcNewSuperID | isAscendingHyperarc;
} // end of array
else
{ // not at the end of the array
indexType nbrSuperID = newSupernodes[neighbour];
// immediately check for case iii. by looking at the hierarchicalSuperparent of the neighbour's old ID
// if it's already set, it's because it's an attachment point
if (!noSuchElement(hierarchicalSuperparent[nbrSuperID]))
{ // attachment point
hierarchicalTree.superarcs[newSupernodeID] = hierarchicalHyperarcNewSuperID | isAscendingHyperarc;
} // attachment point
else
{ // not attachment point
indexType nbrHyperparent = hierarchicalHyperparent[nbrSuperID];
// if they share a hyperparent, just take the neighbour
if (nbrHyperparent == hierarchicalHyperparentOldSuperID)
{ // shared hyperparent
hierarchicalTree.superarcs[newSupernodeID] = hierarchicalSuperID[nbrSuperID] | isAscendingHyperarc;
} // shared hyperparent
// if not, take the target of the hyperarc
else
{ // not shared hyperparent
hierarchicalTree.superarcs[newSupernodeID] = hierarchicalHyperarcNewSuperID | isAscendingHyperarc;
} // not shared hyperparent
} // not attachment point
} // not at the end of the array
} // free point
} // attachment point (III) or free point (IV)
} // per new supernode
*/
} // operator ()
private:
vtkm::Id TheRound;
vtkm::Id NumOldSupernodes;
}; // CopyNewHypernodes
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,147 @@
//============================================================================
// 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_tree_grafter_find_critical_points_find_leafs_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_find_critical_points_find_leafs_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet used in TreeGrafter.FindCriticalPoints to flag the leafs
class FindCriticalPointsFindLeafsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
activeSuperarcs, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn interiorForstIsNecessary, // input
WholeArrayIn upNeighbour, // input
WholeArrayIn downNeighbour, // input
WholeArrayInOut supernodeType // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
FindCriticalPointsFindLeafsWorklet() {}
template <typename InFieldPortalType, typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::worklet::contourtree_augmented::EdgePair& activeSuperarc,
const InFieldPortalType& isNecessaryPortal,
const InFieldPortalType& upNeighbourPortal,
const InFieldPortalType& downNeighbourPortal,
const InOutFieldPortalType& supernodeTypePortal) const
{ // operator ()
// flag the leaves
// per active superarc
vtkm::Id lowEnd = activeSuperarc.first;
vtkm::Id highEnd = activeSuperarc.second;
// if the low end thinks it ascends to the high end and does
// not have a descent, then it must be a lower leaf, unless it's
// necessary (i.e. an attachment)
if ((supernodeTypePortal.Get(lowEnd) != vtkm::worklet::contourtree_augmented::IS_SADDLE) &&
(upNeighbourPortal.Get(lowEnd) == highEnd) &&
vtkm::worklet::contourtree_augmented::NoSuchElement(downNeighbourPortal.Get(lowEnd)) &&
(!isNecessaryPortal.Get(lowEnd)))
{
supernodeTypePortal.Set(lowEnd, vtkm::worklet::contourtree_augmented::IS_LOWER_LEAF);
}
// symmetrically for the high end
if ((supernodeTypePortal.Get(highEnd) != vtkm::worklet::contourtree_augmented::IS_SADDLE) &&
(downNeighbourPortal.Get(highEnd) == lowEnd) &&
vtkm::worklet::contourtree_augmented::NoSuchElement(upNeighbourPortal.Get(highEnd)) &&
(!isNecessaryPortal.Get(highEnd)))
{
supernodeTypePortal.Set(highEnd, vtkm::worklet::contourtree_augmented::IS_UPPER_LEAF);
}
// In serial this worklet implements the following operation
/*
// flag the leaves
for (indexType activeSuper = 0; activeSuper < activeSuperarcs.size(); activeSuper++)
{ // per active superarc
indexType lowEnd = activeSuperarcs[activeSuper].low, highEnd = activeSuperarcs[activeSuper].high;
// if the low end thinks it ascends to the high end and does not have a descent, then it must be a lower leaf, unless it's necessary (ie an attachment)
if ((supernodeType[lowEnd] != IS_SADDLE) && (upNeighbour[lowEnd] == highEnd) && noSuchElement(downNeighbour[lowEnd]) && (!residue->isNecessary[lowEnd]))
supernodeType[lowEnd] = IS_LOWER_LEAF;
// symmetrically for the high end
if ((supernodeType[highEnd] != IS_SADDLE) && (downNeighbour[highEnd] == lowEnd) && noSuchElement(upNeighbour[highEnd]) && (!residue->isNecessary[highEnd]))
supernodeType[highEnd] = IS_UPPER_LEAF;
} // per active superarc
*/
} // operator ()
}; // FindCriticalPointsFindLeafsWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,142 @@
//============================================================================
// 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_tree_grafter_find_critical_points_find_saddles_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_find_critical_points_find_saddles_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet used in TreeGrafter.FindCriticalPoints to find and flag saddles
class FindCriticalPointsFindSaddlesWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
activeSuperarcs, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn interiorForstIsNecessary, // input
WholeArrayIn upNeighbour, // input
WholeArrayIn downNeighbour, // input
WholeArrayOut supernodeType // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
FindCriticalPointsFindSaddlesWorklet() {}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::worklet::contourtree_augmented::EdgePair& activeSuperarc,
const InFieldPortalType& isNecessaryPortal,
const InFieldPortalType& upNeighbourPortal,
const InFieldPortalType& downNeighbourPortal,
const OutFieldPortalType& supernodeTypePortal) const
{ // operator ()
// per active superarc
// retrieve the ends again
vtkm::Id lowEnd = activeSuperarc.first;
vtkm::Id highEnd = activeSuperarc.second;
// if the low end doesn't have the high end as the up neighbour, another arc succeeded in writing to it
// which guarantees that it is a saddle (or an attachment - but we exclude that by testing it)
if (!isNecessaryPortal.Get(lowEnd) && (upNeighbourPortal.Get(lowEnd) != highEnd))
{
supernodeTypePortal.Set(lowEnd, vtkm::worklet::contourtree_augmented::IS_SADDLE);
}
// symmetrically for the high end
if (!isNecessaryPortal.Get(highEnd) && (downNeighbourPortal.Get(highEnd) != lowEnd))
{
supernodeTypePortal.Set(highEnd, vtkm::worklet::contourtree_augmented::IS_SADDLE);
}
// In serial this worklet implements the following operation
/*
// now test whether they match what we expect: if not, we've found a saddle
for (indexType activeSuper = 0; activeSuper < activeSuperarcs.size(); activeSuper++)
{ // per active superarc
// retrieve the ends again
indexType lowEnd = activeSuperarcs[activeSuper].low;
indexType highEnd = activeSuperarcs[activeSuper].high;
// if the low end doesn't have the high end as the up neighbour, another arc succeeded in writing to it
// which guarantees that it is a saddle (or an attachment - but we exclude that by testing it)
if (!residue->isNecessary[lowEnd] && (upNeighbour[lowEnd] != highEnd))
supernodeType[lowEnd] = IS_SADDLE;
// symmetrically for the high end
if (!residue->isNecessary[highEnd] && (downNeighbour[highEnd] != lowEnd))
supernodeType[highEnd] = IS_SADDLE;
} // per active superarc
*/
} // operator ()
}; // FindCriticalPointsFindSaddlesWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,140 @@
//============================================================================
// 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_tree_grafter_find_critical_points_find_terminal_elements_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_find_critical_points_find_terminal_elements_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet used in TreeGrafter.FindCriticalPoints to flag the terminal elements
class FindCriticalPointsFindTerminalElementsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
activeSuperarcs, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn supernodeType, // input
WholeArrayInOut upNeighbour, // output/input
WholeArrayInOut downNeighbour // output/input
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
FindCriticalPointsFindTerminalElementsWorklet() {}
template <typename InFieldPortalType, typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::worklet::contourtree_augmented::EdgePair& activeSuperarc,
const InFieldPortalType& supernodeTypePortal,
const InOutFieldPortalType& upNeighbourPortal,
const InOutFieldPortalType& downNeighbourPortal
) const
{ // operator ()
// per active superarc
vtkm::Id lowEnd = activeSuperarc.first;
vtkm::Id highEnd = activeSuperarc.second;
// test to see whether the top end is critical
if ((upNeighbourPortal.Get(lowEnd) == highEnd) &&
(supernodeTypePortal.Get(highEnd) != vtkm::worklet::contourtree_augmented::IS_REGULAR))
{
upNeighbourPortal.Set(lowEnd,
upNeighbourPortal.Get(lowEnd) |
vtkm::worklet::contourtree_augmented::TERMINAL_ELEMENT);
}
// same for bottom end
if ((downNeighbourPortal.Get(highEnd) == lowEnd) &&
(supernodeTypePortal.Get(lowEnd) != vtkm::worklet::contourtree_augmented::IS_REGULAR))
{
downNeighbourPortal.Set(highEnd,
downNeighbourPortal.Get(highEnd) |
vtkm::worklet::contourtree_augmented::TERMINAL_ELEMENT);
}
// In serial this worklet implements the following operation
/*
// one more pass to set terminal flags
for (indexType activeSuper = 0; activeSuper < activeSuperarcs.size(); activeSuper++)
{ // per active superarc
indexType lowEnd = activeSuperarcs[activeSuper].low, highEnd = activeSuperarcs[activeSuper].high;
// test to see whether the top end is critical
if ((upNeighbour[lowEnd] == highEnd) && (supernodeType[highEnd] != IS_REGULAR))
upNeighbour[lowEnd] |= TERMINAL_ELEMENT;
// same for bottom end
if ((downNeighbour[highEnd] == lowEnd) && (supernodeType[lowEnd] != IS_REGULAR))
downNeighbour[highEnd] |= TERMINAL_ELEMENT;
} // per active superarc
*/
} // operator ()
}; // FindCriticalPointsFindTerminalElementsWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,146 @@
//============================================================================
// 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_tree_grafter_find_critical_points_set_up_down_neighbours_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_find_critical_points_set_up_down_neighbours_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// In TreeGrafter.InitializeActiveSuperarcs set TreeGrafter.ActiveSuperarcs
class FindCriticalPointsSetUpDownNeighboursWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
activeSuperarcs, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn interiorForstIsNecessary, // input
WholeArrayOut upNeighbour, // output
WholeArrayOut downNeighbour, // output
WholeArrayOut supernodeType // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
FindCriticalPointsSetUpDownNeighboursWorklet() {}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::worklet::contourtree_augmented::EdgePair& activeSuperarc,
const InFieldPortalType& isNecessaryPortal,
const OutFieldPortalType& upNeighbourPortal,
const OutFieldPortalType& downNeighbourPortal,
const OutFieldPortalType& supernodeTypePortal) const
{ // operator ()
// per active superarc
// make local copies of the ends
vtkm::Id lowEnd = activeSuperarc.first;
vtkm::Id highEnd = activeSuperarc.second;
// set the up/down neighbours at each end
upNeighbourPortal.Set(lowEnd, highEnd);
downNeighbourPortal.Set(highEnd, lowEnd);
// and set criticality flags at both ends to attachment if they are necessary, regular otherwise
vtkm::Id highType = isNecessaryPortal.Get(highEnd)
? vtkm::worklet::contourtree_augmented::IS_ATTACHMENT
: vtkm::worklet::contourtree_augmented::IS_REGULAR;
vtkm::Id lowType = isNecessaryPortal.Get(lowEnd)
? vtkm::worklet::contourtree_augmented::IS_ATTACHMENT
: vtkm::worklet::contourtree_augmented::IS_REGULAR;
supernodeTypePortal.Set(lowEnd, lowType);
supernodeTypePortal.Set(highEnd, highType);
// In serial this worklet implements the following operation
/*
// fill in the up/down neighbour arrays
for (indexType activeSuper = 0; activeSuper < activeSuperarcs.size(); activeSuper++)
{ // per active superarc
// make local copies of the ends
indexType lowEnd = activeSuperarcs[activeSuper].low;
indexType highEnd = activeSuperarcs[activeSuper].high;
// set the up/down neighbours at each end
upNeighbour[lowEnd] = highEnd;
downNeighbour[highEnd] = lowEnd;
// and set criticality flags at both ends to attachment if they are necessary, regular otherwise
indexType highType = residue->isNecessary[highEnd] ? IS_ATTACHMENT : IS_REGULAR;
indexType lowType = residue->isNecessary[lowEnd] ? IS_ATTACHMENT : IS_REGULAR;
supernodeType[lowEnd] = lowType;
supernodeType[highEnd] = highType;
} // per active superarc
*/
} // operator ()
}; // FindCriticalPointsSetUpDownNeighboursWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,289 @@
//============================================================================
// 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_tree_grafter_get_hierarchical_ids_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_get_hierarchical_ids_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Worklet for retrieving correct Ids from hierarchical tree
class GetHierarchicalIdsWorklet : public vtkm::worklet::WorkletMapField
{
public:
// TODO: Need to decide for which we can use FieldIn/FieldOut instead of requiring transfer of the WholeArrayIn/Out
using ControlSignature = void(
// input (read-only) array
FieldIn supernodes,
// reference (read-only) arrays
FieldIn supernodeGlobalId,
WholeArrayIn sortOrder,
WholeArrayIn dataValue,
FieldIn necessary,
FieldIn above,
FieldIn below,
WholeArrayIn superparents,
WholeArrayIn hyperparents,
WholeArrayIn regular2Supernode,
WholeArrayIn super2Hypernode,
// Execution object to use the FindRegularByGlobal and FindSuperArcForUnknownNode for the hierarchical tree.
ExecObject findRegularByGlobal,
ExecObject findSuperArcForUnknownNode,
// output (write-only) arrays
// NOTE: hierarchicalSuperId and hierarchicalHyperId are not always updated in the operator.
// We, therfore, need to use FieldInOut to make sure the original value (typical NO_SUCH_ELEMTENT)
// is not being overwritten (when just using FieldOut VTKm sets the value to 0)
// TODO: We could potentially avoid the need for FieldInOut by always setting hierarchicalSuperId and hierarchicalHyperId to NO_SUCH_ELEMENT at the beginning
FieldOut hierarchicalRegularId,
FieldInOut hierarchicalSuperId,
FieldInOut hierarchicalHyperId,
FieldOut hierarchicalSuperparent,
FieldOut hierarchicalHyperparent);
using ExecutionSignature =
void(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
GetHierarchicalIdsWorklet() {}
// TODO: Discuss with Oliver when it's a ID, when it's a portal and when it's a reference
template <typename InFieldPortalType,
typename SortOrderPortalType,
typename InFieldDataPortalType,
typename FindRegularExecType,
typename FindSuperExecType>
VTKM_EXEC void operator()(
// const vtkm::Id& supernode, // not used
const vtkm::Id& oldSortId, // i.e, supernodes[supernode]
const vtkm::Id&
supernodeGlobalIdVal, // Depending on the mesh we may get a different fany array handle
const SortOrderPortalType&
sortOrder, // Depending on the mesh we may get a different fany array handle
const InFieldDataPortalType& dataValues,
const vtkm::Id& necessary,
const vtkm::Id& upGlobalId, // = above[supernode]
const vtkm::Id& dnGlobalId, // = below[supernode]
const InFieldPortalType& superparents,
const InFieldPortalType& hyperparents,
const InFieldPortalType& regular2Supernode,
const InFieldPortalType& super2Hypernode,
const FindRegularExecType& findRegularByGlobal,
const FindSuperExecType& findSuperArcForUnknownNode,
vtkm::Id& hierarchicalRegularId,
vtkm::Id& hierarchicalSuperId,
vtkm::Id& hierarchicalHyperId,
vtkm::Id& hierarchicalSuperparent,
vtkm::Id& hierarchicalHyperparent) const
{ // operator ()
// TODO: Need to check that this code is correct
// TODO: Discuss with Oliver how to use the FindRegularByGlobal function
vtkm::Id regularId = findRegularByGlobal.FindRegularByGlobal(supernodeGlobalIdVal);
// save the regular Id
hierarchicalRegularId = regularId; // set return value
// Free supernode: if it was necessary, then it's potentially an attachment point
if (vtkm::worklet::contourtree_augmented::NoSuchElement(regularId))
// Super = NSE, Regular = NSE:
{ // no such element regular or super
// not marked as necessary: cannot be an attachment point
if (!necessary)
{
return;
}
// it was marked as necessary, but got regularised & therefore not a regular point in the parent
// this means we are guaranteed that we have a valid up/down neighbour pair from our BRACT construction
// and that the up/down neighbour pair are guaranteed to be at least regular in the hierarchical tree
// vtkm::Id oldSortId = supernodes.Get(supernode);
vtkm::Id oldRegularId = sortOrder.Get(oldSortId);
auto dataValue = dataValues.Get(oldRegularId);
vtkm::Id upHierarchicalId = findRegularByGlobal.FindRegularByGlobal(upGlobalId);
vtkm::Id dnHierarchicalId = findRegularByGlobal.FindRegularByGlobal(dnGlobalId);
vtkm::Id superparentInHierarchy = findSuperArcForUnknownNode.FindSuperArcForUnknownNode(
supernodeGlobalIdVal, dataValue, upHierarchicalId, dnHierarchicalId);
hierarchicalSuperparent = superparentInHierarchy; // set return value
hierarchicalHyperparent = hyperparents.Get(superparentInHierarchy); // set return value
} // no such element regular or super
else
{ // regular Id exists
// we want to know if the regular node has a superId. This is straightforward, since we have a lookup array
vtkm::Id supernodeId = regular2Supernode.Get(regularId);
vtkm::Id superparent = superparents.Get(regularId);
// if there is no superId yet, we will need to set one up
if (vtkm::worklet::contourtree_augmented::NoSuchElement(supernodeId))
{ // no supernode Id: regular but not super
// We have a supernode in the lower level tree (which excludes boundary regular vertices from consideration)
// This occurred AFTER regularisation, so either:
// a) it was regularised, but is a boundary vertex and was therefore kept, or
// b) it was not regularised, but is not critical once other blocks have been considered
// In either case, it is now needed and must be inserted as an attachment point
// Note that regularised interior attachment points are dealt with later
// We assume as an invariant that all regular points in the hierarchical tree already have superparent correctly set
// This means we know which superarc it belongs to, and therefore which hyperarc, so we take that as the insertion parent
vtkm::Id hierSuperparent =
vtkm::worklet::contourtree_augmented::MaskedIndex(superparents.Get(regularId));
// this is logically redundant since we won't use it, but it'll make debug easier
hierarchicalSuperparent = hierSuperparent; // set return value
hierarchicalHyperparent = hyperparents.Get(hierSuperparent); // set return value
} // no supernode Id: regular but not super
else
{ // supernode Id exists
// save the supernode Id
hierarchicalSuperId = supernodeId; // Set return value
// we can also set the superparent & hyperparent at this point
hierarchicalSuperparent = superparent; // set return value
hierarchicalHyperparent = hyperparents.Get(supernodeId); // set return value
// now retrieve the hypernode Id (if any) & store it (even if it is already NO_SUCH_ELEMENT)
hierarchicalHyperId = super2Hypernode.Get(supernodeId); // set return value
} // supernode Id exists
} // regular Id exists
// In serial this worklet implements the following operation
/*
// down-convert to a global ID
// search in the hierarchy to find the regular ID
indexType regularID = hierTree.FindRegularByGlobal(supernodeGlobalID[supernode]);
hierarchicalRegularID[supernode] = regularID;
// Free supernode: if it was necessary, then it's potentially an attachment point
if (noSuchElement(regularID))
// Super = NSE, Regular = NSE:
{ // no such element regular or super
// not marked as necessary: cannot be an attachment point
if (!residue->isNecessary[supernode])
continue;
// it was marked as necessary, but got regularised & therefore not a regular point in the parent
// this means we are guaranteed that we have a valid up/down neighbour pair from our BRACT construction
// and that the up/down neighbour pair are guaranteed to be at least regular in the hierarchical tree
indexType oldSortID = contourTree->supernodes[supernode];
indexType oldRegularID = mesh->SortOrder(oldSortID);
dataType dataValue = mesh->DataValue(oldRegularID);
indexType upGlobalID = residue->above[supernode];
indexType upHierarchicalID = hierTree.FindRegularByGlobal(upGlobalID);
indexType dnGlobalID = residue->below[supernode];
indexType dnHierarchicalID = hierTree.FindRegularByGlobal(dnGlobalID);
indexType superparentInHierarchy = hierTree.FindSuperArcForUnknownNode(supernodeGlobalID[supernode], dataValue, upHierarchicalID, dnHierarchicalID);
hierarchicalSuperparent[supernode] = superparentInHierarchy;
hierarchicalHyperparent[supernode] = hierTree.hyperparents[superparentInHierarchy];
} // no such element regular or super
else
{ // regular ID exists
// we want to know if the regular node has a superID. This is straightforward, since we have a lookup array
indexType supernodeID = hierTree.regular2supernode[regularID];
indexType superparent = hierTree.superparents[regularID];
// if there is no superID yet, we will need to set one up
if (noSuchElement(supernodeID))
{ // no supernode ID: regular but not super
// We have a supernode in the lower level tree (which excludes boundary regular vertices from consideration)
// This occurred AFTER regularisation, so either:
// a) it was regularised, but is a boundary vertex and was therefore kept, or
// b) it was not regularised, but is not critical once other blocks have been considered
// In either case, it is now needed and must be inserted as an attachment point
// Note that regularised interior attachment points are dealt with later
// We assume as an invariant that all regular points in the hierarchical tree already have superparent correctly set
// This means we know which superarc it belongs to, and therefore which hyperarc, so we take that as the insertion parent
indexType hierSuperparent = maskedIndex(hierTree.superparents[regularID]);
// this is logically redundant since we won't use it, but it'll make debug easier
hierarchicalSuperparent[supernode] = hierSuperparent;
hierarchicalHyperparent[supernode] = hierTree.hyperparents[hierSuperparent];
} // no supernode ID: regular but not super
else
{ // supernode ID exists
// save the supernode ID
hierarchicalSuperID[supernode] = supernodeID;
// we can also set the superparent & hyperparent at this point
hierarchicalSuperparent[supernode] = superparent;
hierarchicalHyperparent[supernode] = hierTree.hyperparents[supernodeID];
// now retrieve the hypernode ID (if any) & store it (even if it is already NO_SUCH_ELEMENT)
hierarchicalHyperID[supernode] = hierTree.super2hypernode[supernodeID];
} // supernode ID exists
} // regular ID exists
} // per tree supernode
*/
} // operator ()
}; // BoundaryVerticiesPerSuperArcStepOneWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,126 @@
//============================================================================
// 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_tree_grafter_graft_interior_forests_set_trandfer_iteration_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_graft_interior_forests_set_trandfer_iteration_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Now set the transfer iteration for all attachment points
/// If there were no supernodes to transfer, their types are all NO_SUCH_ELEMENT
class GraftInteriorForestsSetTransferIterationWorklet : public vtkm::worklet::WorkletMapField
{
public:
// NOTE: supernodeType is sized to ContourTree.Supernodes.GetNumberOfValues() so we can use it for our iteration
// NOTE: for whenTransferred we neeed need FieldInOut type to avoid overwrite of existing value as not all values will be updated
using ControlSignature = void(FieldIn supernodeType, // input
FieldIn hierarchicalSuperId, // input
FieldInOut whenTransferred // output
);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
GraftInteriorForestsSetTransferIterationWorklet(const vtkm::Id& numTransferIterations)
: NumTransferIterations(numTransferIterations)
{
}
VTKM_EXEC void operator()(const vtkm::Id& supernodeType,
const vtkm::Id& hierarchicalSuperId,
vtkm::Id& whenTransferred) const
{ // operator ()
if ((supernodeType == vtkm::worklet::contourtree_augmented::IS_ATTACHMENT) &&
vtkm::worklet::contourtree_augmented::NoSuchElement(hierarchicalSuperId))
{ // not a supernode in the hierarchical tree yet
whenTransferred =
(this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_SUPERNODE);
} // not a supernode in the hierarchical tree yet
// In serial this worklet implements the following operation
/*
// Now set the transfer iteration for all attachment points
// If there were no supernodes to transfer, their types are all NO_SUCH_ELEMENT
#pragma omp parallel for
for (indexType supernode = 0; supernode < contourTree->supernodes.size(); supernode++)
{ // per supernode
// std::cout << "Supernode " << supernode << std::endl;
if ((supernodeType[supernode] == IS_ATTACHMENT) && noSuchElement(hierarchicalSuperID[supernode]))
{ // not a supernode in the hierarchical tree yet
whenTransferred[supernode] = nTransferIterations | IS_SUPERNODE;
} // not a supernode in the hierarchical tree yet
} // per supernode
*/
} // operator ()
private:
vtkm::Id NumTransferIterations;
}; // BoundaryVerticiesPerSuperArcStepOneWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,142 @@
//============================================================================
// 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_tree_grafter_hypernode_when_comparator_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_hypernode_when_comparator_h
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Comparator used in TreeGrafter::ListNewHypernodes to sort the NewHypernodes arrays
template <typename DeviceAdapter>
class HyperNodeWhenComparatorImpl : public vtkm::worklet::WorkletMapField
{
public:
using IdArrayPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceAdapter>::PortalConst;
// Default Constructor
VTKM_EXEC_CONT
HyperNodeWhenComparatorImpl(const IdArrayPortalType& whenTransferredPortal)
: WhenTransferredPortal(whenTransferredPortal)
{
}
VTKM_EXEC bool operator()(const vtkm::Id& leftSuperId, const vtkm::Id& rightSuperId) const
{ // operator ()
vtkm::Id maskedLeftWhen = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhenTransferredPortal.Get(leftSuperId));
vtkm::Id maskedRightWhen = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhenTransferredPortal.Get(rightSuperId));
if (maskedLeftWhen < maskedRightWhen)
{
return true;
}
else if (maskedLeftWhen > maskedRightWhen)
{
return false;
}
// tie break on ID to make it canonical
else
{
return (leftSuperId < rightSuperId);
}
} // operator ()
private:
IdArrayPortalType WhenTransferredPortal;
}; // HyperNodeWhenComparator
class HyperNodeWhenComparator : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_CONT
HyperNodeWhenComparator(const vtkm::worklet::contourtree_augmented::IdArrayType& whenTransferred)
: WhenTransferred(whenTransferred)
{
}
template <typename DeviceAdapter>
VTKM_CONT HyperNodeWhenComparatorImpl<DeviceAdapter> PrepareForExecution(
DeviceAdapter device,
vtkm::cont::Token& token) const
{
return HyperNodeWhenComparatorImpl<DeviceAdapter>(
this->WhenTransferred.PrepareForInput(device, token));
}
private:
const vtkm::worklet::contourtree_augmented::IdArrayType& WhenTransferred;
};
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,329 @@
//============================================================================
// 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_tree_grafter_identify_leaf_hyperarcs_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_identify_leaf_hyperarcs_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// \brief Worklet implementing the TreeGrafter.IdentifyLeafHyperarcs function
///
/// At this stage, we have:
/// i. hierarchicalRegularID set for any supernode stored at all in the parent
/// ii. hierarchicalSuperID set for any supernode that is a supernode in the parent
/// iii. hierarchicalHyperParent set for any attachment point
/// iv. supernodeType set to indicate what type of supernode
/// v. up/dn neighbours set for all supernodes
///
/// at the end of the chain collapse, the up/down neighbours define the start & end of the hyperarc
/// one end may be a leaf, in which case we can transfer the hyperarc
/// note that because we are grafting, we have a guarantee that they can't both be leaves
/// we therefore:
/// a. for leaves, determine whether up or down hyperarc, create hyperarc
/// b. for regular vertices pointing to a leaf hyperarc, set superarc / hyperparent
/// c. for other vertices, ignore
class IdentifyLeafHyperarcsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
activeSuperarcs, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn supernodeType, // input
WholeArrayIn upNeighbour, // input
WholeArrayIn downNeighbour, // input
WholeArrayOut hierarchicalHyperparent, //output
WholeArrayOut hierarchicalHyperarcPortal, // output
WholeArrayOut whenTransferredPortal // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
IdentifyLeafHyperarcsWorklet(const vtkm::Id& numTransferIterations)
: NumTransferIterations(numTransferIterations)
{
}
template <typename InFieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::worklet::contourtree_augmented::EdgePair& activeSuperarc,
const InFieldPortalType supernodeTypePortal,
const InFieldPortalType upNeighbourPortal,
const InFieldPortalType downNeighbourPortal,
const OutFieldPortalType& hierarchicalHyperparentPortal,
const OutFieldPortalType& hierarchicalHyperarcPortal,
const OutFieldPortalType& whenTransferredPortal) const
{ // operator ()
// per active superarc
// retrieve the supernode IDs for the two ends
vtkm::Id low = activeSuperarc.first;
vtkm::Id high = activeSuperarc.second;
// test whether the top end is an upper leaf
switch (supernodeTypePortal.Get(high))
{ // switch on upper end
case vtkm::worklet::contourtree_augmented::IS_UPPER_LEAF:
{ // upper end is a leaf
// in lower leaf rounds, never recognise these
hierarchicalHyperparentPortal.Set(high, high);
hierarchicalHyperarcPortal.Set(
high, vtkm::worklet::contourtree_augmented::MaskedIndex(downNeighbourPortal.Get(high)));
whenTransferredPortal.Set(
high, this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_HYPERNODE);
break;
} // upper end is a leaf
case vtkm::worklet::contourtree_augmented::IS_REGULAR:
{ // upper end is regular
// notice that this is redundant, so will be set from both arcs
// this is parallel safe, because it sets the same value anyway
// testing would be more complex
// find the up & down neighbours
vtkm::Id upNbr =
vtkm::worklet::contourtree_augmented::MaskedIndex(upNeighbourPortal.Get(high));
vtkm::Id downNbr =
vtkm::worklet::contourtree_augmented::MaskedIndex(downNeighbourPortal.Get(high));
// test the up neighbour first for leaf-hood
// but only if the corresponding flag is true
if (supernodeTypePortal.Get(upNbr) == vtkm::worklet::contourtree_augmented::IS_UPPER_LEAF)
{ // up neighbour is an upper leaf
hierarchicalHyperparentPortal.Set(high, upNbr);
whenTransferredPortal.Set(
high, this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_SUPERNODE);
} // up neighbour is an upper leaf
// then the down neighbour (cannot both be true)
else if (supernodeTypePortal.Get(downNbr) ==
vtkm::worklet::contourtree_augmented::IS_LOWER_LEAF)
{ // down neighbour is a lower leaf
hierarchicalHyperparentPortal.Set(high, downNbr);
whenTransferredPortal.Set(
high, this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_SUPERNODE);
} // down neighbour is a lower leaf
break;
} // case: upper end is regular
// all other cases do nothing
case vtkm::worklet::contourtree_augmented::IS_SADDLE:
case vtkm::worklet::contourtree_augmented::IS_ATTACHMENT:
case vtkm::worklet::contourtree_augmented::IS_LOWER_LEAF:
default:
break;
} // switch on upper end
// test whether the bottom end is a lower leaf
switch (supernodeTypePortal.Get(low))
{ // switch on lower end
case vtkm::worklet::contourtree_augmented::IS_LOWER_LEAF:
{ // lower end is a leaf
hierarchicalHyperparentPortal.Set(low, low);
hierarchicalHyperarcPortal.Set(
low,
vtkm::worklet::contourtree_augmented::MaskedIndex(upNeighbourPortal.Get(low)) |
vtkm::worklet::contourtree_augmented::IS_ASCENDING);
whenTransferredPortal.Set(
low, this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_HYPERNODE);
break;
} // lower end is a leaf
case vtkm::worklet::contourtree_augmented::IS_REGULAR:
{ // lower end is regular
// notice that this is redundant, so will be set from both arcs
// this is parallel safe, because it sets the same value anyway
// testing would be more complex
// find the up & down neighbours
vtkm::Id upNbr =
vtkm::worklet::contourtree_augmented::MaskedIndex(upNeighbourPortal.Get(low));
vtkm::Id downNbr =
vtkm::worklet::contourtree_augmented::MaskedIndex(downNeighbourPortal.Get(low));
// test the up neighbour first for leaf-hood
if (supernodeTypePortal.Get(upNbr) == vtkm::worklet::contourtree_augmented::IS_UPPER_LEAF)
{ // up neighbour is an upper leaf
hierarchicalHyperparentPortal.Set(low, upNbr);
whenTransferredPortal.Set(
low, this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_SUPERNODE);
} // up neighbour is an upper leaf
// then the down neighbour (cannot both be true)
else if (supernodeTypePortal.Get(downNbr) ==
vtkm::worklet::contourtree_augmented::IS_LOWER_LEAF)
{ // down neighbour is a lower leaf
hierarchicalHyperparentPortal.Set(low, downNbr);
whenTransferredPortal.Set(
low, this->NumTransferIterations | vtkm::worklet::contourtree_augmented::IS_SUPERNODE);
} // down neighbour is a lower leaf
break;
} // lower end is regular
// all other cases do nothing
case vtkm::worklet::contourtree_augmented::IS_SADDLE:
case vtkm::worklet::contourtree_augmented::IS_ATTACHMENT:
case vtkm::worklet::contourtree_augmented::IS_UPPER_LEAF:
default:
break;
} // switch on lower end
// In serial this worklet implements the following operation
/*
#pragma omp parallel for
for (indexType activeSuper = 0; activeSuper < activeSuperarcs.size(); activeSuper++)
{ // per active superarc
// retrieve the supernode IDs for the two ends
indexType low = activeSuperarcs[activeSuper].low;
indexType high = activeSuperarcs[activeSuper].high;
// test whether the top end is an upper leaf
switch (supernodeType[high])
{ // switch on upper end
case IS_UPPER_LEAF:
{ // upper end is a leaf
// in lower leaf rounds, never recognise these
hierarchicalHyperparent[high] = high;
hierarchicalHyperarc[high] = maskedIndex(downNeighbour[high]);
whenTransferred[high] = nTransferIterations | IS_HYPERNODE;
break;
} // upper end is a leaf
case IS_REGULAR:
{ // upper end is regular
// notice that this is redundant, so will be set from both arcs
// this is parallel safe, because it sets the same value anyway
// testing would be more complex
// find the up & down neighbours
indexType upNbr = maskedIndex(upNeighbour[high]);
indexType downNbr = maskedIndex(downNeighbour[high]);
// test the up neighbour first for leaf-hood
// but only if the corresponding flag is true
if (supernodeType[upNbr] == IS_UPPER_LEAF)
{ // up neighbour is an upper leaf
hierarchicalHyperparent[high] = upNbr;
whenTransferred[high] = nTransferIterations | IS_SUPERNODE;
} // up neighbour is an upper leaf
// then the down neighbour (cannot both be true)
else if (supernodeType[downNbr] == IS_LOWER_LEAF)
{ // down neighbour is a lower leaf
hierarchicalHyperparent[high] = downNbr;
whenTransferred[high] = nTransferIterations | IS_SUPERNODE;
} // down neighbour is a lower leaf
break;
} // upper end is regular
// all other cases do nothing
case IS_SADDLE:
case IS_ATTACHMENT:
case IS_LOWER_LEAF:
default:
break;
} // switch on upper end
// test whether the bottom end is a lower leaf
switch (supernodeType[low])
{ // switch on lower end
case IS_LOWER_LEAF:
{ // lower end is a leaf
hierarchicalHyperparent[low] = low;
hierarchicalHyperarc[low] = maskedIndex(upNeighbour[low]) | IS_ASCENDING;
whenTransferred[low] = nTransferIterations | IS_HYPERNODE;
break;
} // lower end is a leaf
case IS_REGULAR:
{ // lower end is regular
// notice that this is redundant, so will be set from both arcs
// this is parallel safe, because it sets the same value anyway
// testing would be more complex
// find the up & down neighbours
indexType upNbr = maskedIndex(upNeighbour[low]);
indexType downNbr = maskedIndex(downNeighbour[low]);
// test the up neighbour first for leaf-hood
if (supernodeType[upNbr] == IS_UPPER_LEAF)
{ // up neighbour is an upper leaf
hierarchicalHyperparent[low] = upNbr;
whenTransferred[low] = nTransferIterations | IS_SUPERNODE;
} // up neighbour is an upper leaf
// then the down neighbour (cannot both be true)
else if (supernodeType[downNbr] == IS_LOWER_LEAF)
{ // down neighbour is a lower leaf
hierarchicalHyperparent[low] = downNbr;
whenTransferred[low] = nTransferIterations | IS_SUPERNODE;
} // down neighbour is a lower leaf
break;
} // lower end is regular
// all other cases do nothing
case IS_SADDLE:
case IS_ATTACHMENT:
case IS_UPPER_LEAF:
default:
break;
} // switch on lower end
} // per active superarc
*/
} // operator ()
private:
vtkm::Id NumTransferIterations;
}; // IdentifyLeafHyperarcsWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,119 @@
//============================================================================
// 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_tree_grafter_init_active_superarc_id_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_init_active_superarc_id_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// In TreeGrafter.InitializeActiveSuperarcs set the temporary variable activeSuperarcId
class InitActiceSuperarcIdWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
superarcIndex, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn contourtreeSuperarcs, // input
WholeArrayIn interiorForestIsNecessary, // input
FieldOut activeSuperarcId // output
);
using ExecutionSignature = _4(_1, _2, _3);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
InitActiceSuperarcIdWorklet() {}
template <typename InFieldPortalType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& superarcIndex,
const InFieldPortalType& contourtreeSuperarcPortal,
const InFieldPortalType& isNecessaryPortal) const
{ // operator ()
// retrieve the target
vtkm::Id superarcTo = vtkm::worklet::contourtree_augmented::MaskedIndex(
contourtreeSuperarcPortal.Get(superarcIndex));
// now set the flag for which ones are needed
return (!isNecessaryPortal.Get(superarcIndex) || !isNecessaryPortal.Get(superarcTo));
// In serial this worklet implements the following operation
/*
// loop to one less, i.e. excluding null superarc from root
for (indexType superarc = 0; superarc < contourTree->supernodes.size() - 1; superarc++)
{ // per superarc
// retrieve the target
indexType superarcTo = maskedIndex(contourTree->superarcs[superarc]);
// now set the flag for which ones are needed
activeSuperarcID[superarc] = !residue->isNecessary[superarc] || !residue->isNecessary[superarcTo];
} // per superarc
*/
} // operator ()
}; // BoundaryVerticiesPerSuperArcStepOneWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,164 @@
//============================================================================
// 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_tree_grafter_init_active_superarcs_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_init_active_superarcs_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// In TreeGrafter.InitializeActiveSuperarcs set TreeGrafter.ActiveSuperarcs
class InitActiceSuperarcsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn
superarcIndex, // input iteration index. loop to one less than ContourTree->Supernodes.GetNumberOfValues()
WholeArrayIn contourTreeSuperarcs, // input
WholeArrayIn interiorForestIsNecessary, // input
FieldIn activeSuperarcId, // input
WholeArrayOut activeSuperarcs // output
);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
InitActiceSuperarcsWorklet() {}
template <typename InFieldPortalType, typename SuperarcsPortalType>
VTKM_EXEC void operator()(
const vtkm::Id& from, // same as superarc
const InFieldPortalType& contourTreeSuperarcsPortal, // ContourTree->Superarcs[from]
const InFieldPortalType& isNecessaryPortal,
const vtkm::Id& activeSuperarcId,
const SuperarcsPortalType& activeSuperarcsPortal) const
{ // operator ()
// per active supernode
// retrieve the supernode ID in the CT
// retrieve the superarc target
vtkm::Id unmaskedTo = contourTreeSuperarcsPortal.Get(from);
vtkm::Id to = vtkm::worklet::contourtree_augmented::MaskedIndex(unmaskedTo);
// test to see whether the superarc needs to be transferred
if (isNecessaryPortal.Get(from) && isNecessaryPortal.Get(to))
{
return;
}
// retrieve the position (note that -1 converts partial sum to prefix sum, since all values were 1's originally)
vtkm::Id activeSuperarc = activeSuperarcId - 1;
// if to is higher
if (vtkm::worklet::contourtree_augmented::IsAscending(unmaskedTo))
{ // to is higher
activeSuperarcsPortal.Set(activeSuperarc,
vtkm::worklet::contourtree_augmented::EdgePair(from, to));
} // to is higher
else
{ // to is lower
activeSuperarcsPortal.Set(activeSuperarc,
vtkm::worklet::contourtree_augmented::EdgePair(to, from));
} // to is lower
// In serial this worklet implements the following operation
/*
// loop does not include the terminal superarc: hence the -1
for (indexType superarc = 0; superarc < contourTree->supernodes.size() - 1; superarc++)
{ // per active supernode
// retrieve the supernode ID in the CT
indexType from = superarc;
// retrieve the superarc target
indexType unmaskedTo = contourTree->superarcs[from];
indexType to = maskedIndex(unmaskedTo);
// test to see whether the superarc needs to be transferred
if (residue->isNecessary[from] && residue->isNecessary[to])
continue;
// retrieve the position (note that -1 converts partial sum to prefix sum, since all values were 1's originally)
indexType activeSuperarc = activeSuperarcID[superarc] - 1;
// if to is higher
if (isAscending(unmaskedTo))
{ // to is higher
activeSuperarcs[activeSuperarc].low = from;
activeSuperarcs[activeSuperarc].high = to;
} // to is higher
else
{ // to is lower
activeSuperarcs[activeSuperarc].high = from;
activeSuperarcs[activeSuperarc].low = to;
} // to is lower
} // per active supernode
*/
} // operator ()
}; // BoundaryVerticiesPerSuperArcStepOneWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,120 @@
//============================================================================
// 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_tree_grafter_list_new_nodes_copy_ids_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_list_new_nodes_copy_ids_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// In TreeGrafter.InitializeActiveSuperarcs set TreeGrafter.ActiveSuperarcs
class ListNewNodesCopyIdsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn interiorForestBractMeshIndices, // input mesh ids
FieldIn globalIdsForBractMeshIndices, // input iteration index.
ExecObject findRegularByGlobal, // input to findRegularByGlobal
WholeArrayOut
hierarchicalTreeId // output (need WholeArrayOut because globalIdsForBractMeshIndices is 1 smaller and need to avoid false resize
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
ListNewNodesCopyIdsWorklet() {}
template <typename ExecObjectType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& meshId,
const vtkm::Id& globalId,
const ExecObjectType& findRegularByGlobal,
const OutFieldPortalType& hierarchicalTreeIdPortal) const
{ // operator ()
// the lookup to mesh->GetGlobalIDFromMeshIndex is done outside the worklet
// for all mesh ids so all we need to do here is call FindRegularByGlobal
hierarchicalTreeIdPortal.Set(meshId, findRegularByGlobal.FindRegularByGlobal(globalId));
// In serial this worklet implements the following operation
/*
for (indexType bractVertex = 0; bractVertex < residue->bractMeshIndices.size(); bractVertex++)
{ // per vertex in the bract
// retrieve mesh ID
indexType meshID = residue->bractMeshIndices[bractVertex];
// now convert to a global index
indexType globalID = mesh->GetGlobalIDFromMeshIndex(meshID);
// look that one up and store the result (NO_SUCH_ELEMENT is acceptable, but should never occur)
hierarchicalTreeID[meshID] = hierarchicalTree.FindRegularByGlobal(globalID);
} // per vertex in the bract
*/
} // operator ()
}; // BoundaryVerticiesPerSuperArcStepOneWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,90 @@
//============================================================================
// 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_tree_grafter_new_hypernode_predicate_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_new_hypernode_predicate_h
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
//Simple functor to subset a VTKm ArrayHandle
class NewHypernodePredicate
{
public:
VTKM_EXEC_CONT
NewHypernodePredicate() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id& hierarchicalHyperarcValue) const
{
return !static_cast<bool>(
vtkm::worklet::contourtree_augmented::NoSuchElement(hierarchicalHyperarcValue));
}
private:
};
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,89 @@
//============================================================================
// 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_tree_grafter_new_node_predicate_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_new_node_predicate_h
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
//Simple functor to subset a VTKm ArrayHandle
class NewNodePredicate
{
public:
VTKM_EXEC_CONT
NewNodePredicate() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id& hierarchicalTreeIdValue) const
{
return static_cast<bool>(
vtkm::worklet::contourtree_augmented::NoSuchElement(hierarchicalTreeIdValue));
}
private:
};
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,142 @@
//============================================================================
// 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_tree_grafter_permute_comparator_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_permute_comparator_h
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Comparator used in TreeGrafter::ListNewSupernodes to sort the NewSupernodes arrays
template <typename DeviceAdapter>
class PermuteComparatorImpl : public vtkm::worklet::WorkletMapField
{
public:
using IdArrayPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceAdapter>::PortalConst;
// Default Constructor
VTKM_EXEC_CONT
PermuteComparatorImpl(const IdArrayPortalType& lookupArrayPortal)
: LookupArrayPortal(lookupArrayPortal)
{
}
VTKM_EXEC bool operator()(const vtkm::Id& i, const vtkm::Id& j) const
{ // operator ()
// primary sort on indexed value
if (this->LookupArrayPortal.Get(i) < this->LookupArrayPortal.Get(j))
{
return true;
}
if (this->LookupArrayPortal.Get(i) > this->LookupArrayPortal.Get(j))
{
return false;
}
// secondary sort on index
if (i < j)
{
return true;
}
if (j < i)
{
return false;
}
// fallback just in case
return false;
} // operator ()
private:
IdArrayPortalType LookupArrayPortal;
}; // PermuteComparator
class PermuteComparator : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_CONT
PermuteComparator(const vtkm::worklet::contourtree_augmented::IdArrayType& lookupArray)
: LookupArray(lookupArray)
{
}
template <typename DeviceAdapter>
VTKM_CONT PermuteComparatorImpl<DeviceAdapter> PrepareForExecution(DeviceAdapter device,
vtkm::cont::Token& token) const
{
return PermuteComparatorImpl<DeviceAdapter>(this->LookupArray.PrepareForInput(device, token));
}
private:
const vtkm::worklet::contourtree_augmented::IdArrayType& LookupArray;
};
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,216 @@
//============================================================================
// 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_tree_grafter_supernode_when_comparator_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_supernode_when_comparator_h
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
/// Comparator used in TreeGrafter::ListNewSupernodes to sort the NewSupernodes arrays
template <typename DeviceAdapter>
class SuperNodeWhenComparatorImpl : public vtkm::worklet::WorkletMapField
{
public:
using IdArrayPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceAdapter>::PortalConst;
// Default Constructor
VTKM_EXEC_CONT
SuperNodeWhenComparatorImpl(const IdArrayPortalType& whenTransferredPortal,
const IdArrayPortalType& hierarchicalHyperparentPortal,
const IdArrayPortalType& hierarchicalHyperIdPortal,
const IdArrayPortalType& hierarchicalHyperarcPortal,
const IdArrayPortalType& contourTreeSupernodesPortal,
const IdArrayPortalType& supernodeTypePortal)
: WhenTransferredPortal(whenTransferredPortal)
, HierarchicalHyperparentPortal(hierarchicalHyperparentPortal)
, HierarchicalHyperIdPortal(hierarchicalHyperIdPortal)
, HierarchicalHyperarcPortal(hierarchicalHyperarcPortal)
, ContourTreeSupernodesPortal(contourTreeSupernodesPortal)
, SupernodeTypePortal(supernodeTypePortal)
{
}
VTKM_EXEC bool operator()(const vtkm::Id& leftSuperId, const vtkm::Id& rightSuperId) const
{ // operator ()
vtkm::Id maskedLeftWhen = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhenTransferredPortal.Get(leftSuperId));
vtkm::Id maskedRightWhen = vtkm::worklet::contourtree_augmented::MaskedIndex(
this->WhenTransferredPortal.Get(rightSuperId));
if (maskedLeftWhen < maskedRightWhen)
{
return true;
}
else if (maskedLeftWhen > maskedRightWhen)
{
return false;
}
// tie break on hyperparent
vtkm::Id leftHyperparent = this->HierarchicalHyperparentPortal.Get(leftSuperId);
if (this->SupernodeTypePortal.Get(leftSuperId) !=
vtkm::worklet::contourtree_augmented::IS_ATTACHMENT)
{
leftHyperparent = this->HierarchicalHyperIdPortal.Get(leftHyperparent);
}
vtkm::Id rightHyperparent = this->HierarchicalHyperparentPortal.Get(rightSuperId);
// if it's an attachment, it's already been converted to the new ID. If not, we need to
if (this->SupernodeTypePortal.Get(rightSuperId) !=
vtkm::worklet::contourtree_augmented::IS_ATTACHMENT)
{
rightHyperparent = this->HierarchicalHyperIdPortal.Get(rightHyperparent);
}
if (leftHyperparent < rightHyperparent)
{
return true;
}
else if (leftHyperparent > rightHyperparent)
{
return false;
}
// OK, they have the same hyperparent. But there are two possibilities:
// A. they are both attachment points, and the hyperparent is already in the hierarchical tree
// B. neither is an attachment point (same hyperparent implies this)
// In A., we can't look up ascent / descent, so we will sort only on sort index - ie always ascending
// In B., we look up the hyperarc and use it to bias the sort
bool sortAscending = true;
// if it's not an attachment, pull the hyperparent in old supernode IDs and use to retrieve hyperarc, then take flag
if (this->SupernodeTypePortal.Get(leftSuperId) !=
vtkm::worklet::contourtree_augmented::IS_ATTACHMENT)
{
sortAscending = vtkm::worklet::contourtree_augmented::IsAscending(
this->HierarchicalHyperarcPortal.Get(HierarchicalHyperparentPortal.Get(leftSuperId)));
}
// if they have the same hyperparent and the hyperarc is ascending, use usual test. If not, invert
if (sortAscending)
{
return (this->ContourTreeSupernodesPortal.Get(leftSuperId) <
this->ContourTreeSupernodesPortal.Get(rightSuperId));
}
else
{
return (this->ContourTreeSupernodesPortal.Get(leftSuperId) >
ContourTreeSupernodesPortal.Get(rightSuperId));
}
} // operator ()
private:
IdArrayPortalType WhenTransferredPortal;
IdArrayPortalType HierarchicalHyperparentPortal;
IdArrayPortalType HierarchicalHyperIdPortal;
IdArrayPortalType HierarchicalHyperarcPortal;
IdArrayPortalType ContourTreeSupernodesPortal;
IdArrayPortalType SupernodeTypePortal;
}; // SuperNodeWhenComparator
class SuperNodeWhenComparator : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_CONT
SuperNodeWhenComparator(
const vtkm::worklet::contourtree_augmented::IdArrayType& whenTransferred,
const vtkm::worklet::contourtree_augmented::IdArrayType& hierarchicalHyperparent,
const vtkm::worklet::contourtree_augmented::IdArrayType& hierarchicalHyperId,
const vtkm::worklet::contourtree_augmented::IdArrayType& hierarchicalHyperarc,
const vtkm::worklet::contourtree_augmented::IdArrayType& contourTreeSupernodes,
const vtkm::worklet::contourtree_augmented::IdArrayType& supernodeType)
: WhenTransferred(whenTransferred)
, HierarchicalHyperparent(hierarchicalHyperparent)
, HierarchicalHyperId(hierarchicalHyperId)
, HierarchicalHyperarc(hierarchicalHyperarc)
, ContourTreeSupernodes(contourTreeSupernodes)
, SupernodeType(supernodeType)
{
}
template <typename DeviceAdapter>
VTKM_CONT SuperNodeWhenComparatorImpl<DeviceAdapter> PrepareForExecution(
DeviceAdapter device,
vtkm::cont::Token& token) const
{
return SuperNodeWhenComparatorImpl<DeviceAdapter>(
this->WhenTransferred.PrepareForInput(device, token),
this->HierarchicalHyperparent.PrepareForInput(device, token),
this->HierarchicalHyperId.PrepareForInput(device, token),
this->HierarchicalHyperarc.PrepareForInput(device, token),
this->ContourTreeSupernodes.PrepareForInput(device, token),
this->SupernodeType.PrepareForInput(device, token));
}
private:
const vtkm::worklet::contourtree_augmented::IdArrayType& WhenTransferred;
const vtkm::worklet::contourtree_augmented::IdArrayType& HierarchicalHyperparent;
const vtkm::worklet::contourtree_augmented::IdArrayType& HierarchicalHyperId;
const vtkm::worklet::contourtree_augmented::IdArrayType& HierarchicalHyperarc;
const vtkm::worklet::contourtree_augmented::IdArrayType& ContourTreeSupernodes;
const vtkm::worklet::contourtree_augmented::IdArrayType& SupernodeType;
};
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,130 @@
//============================================================================
// 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_tree_grafter_superarc_was_transferred_predicate_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_superarc_was_transferred_predicate_h
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// Uniary predicate used in TreeGrafter.CompressActiveArrays to decide which active superarcs to keep
template <typename DeviceAdapter>
class SuperarcWasTransferredPredicateImpl : public vtkm::worklet::WorkletMapField
{
public:
using IdArrayPortalType =
typename vtkm::worklet::contourtree_augmented::IdArrayType::template ExecutionTypes<
DeviceAdapter>::PortalConst;
// Default Constructor
VTKM_EXEC_CONT
SuperarcWasTransferredPredicateImpl(const IdArrayPortalType& whenTransferredPortal)
: WhenTransferredPortal(whenTransferredPortal)
{
}
VTKM_EXEC bool operator()(const vtkm::worklet::contourtree_augmented::EdgePair& superarc) const
{ // operator ()
// if either end is marked as transferred, the arc must be gone already
return ((!vtkm::worklet::contourtree_augmented::NoSuchElement(
this->WhenTransferredPortal.Get(superarc.first))) ||
(!vtkm::worklet::contourtree_augmented::NoSuchElement(
this->WhenTransferredPortal.Get(superarc.second))));
} // operator ()
private:
IdArrayPortalType WhenTransferredPortal;
}; // SuperarcWasTransferredPredicate
class SuperarcWasTransferredPredicate : public vtkm::cont::ExecutionObjectBase
{
public:
VTKM_CONT
SuperarcWasTransferredPredicate(
const vtkm::worklet::contourtree_augmented::IdArrayType& whenTransferred)
: WhenTransferred(whenTransferred)
{
}
template <typename DeviceAdapter>
VTKM_CONT SuperarcWasTransferredPredicateImpl<DeviceAdapter> PrepareForExecution(
DeviceAdapter device,
vtkm::cont::Token& token) const
{
return SuperarcWasTransferredPredicateImpl<DeviceAdapter>(
this->WhenTransferred.PrepareForInput(device, token));
}
private:
const vtkm::worklet::contourtree_augmented::IdArrayType& WhenTransferred;
};
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif

@ -750,7 +750,7 @@ public:
contourTree, meshSortOrder, saddlePeak);
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 12" << std::endl;
@ -816,7 +816,7 @@ public:
contourTree, meshSortOrder, saddlePeak);
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 67" << std::endl;
@ -889,7 +889,7 @@ public:
contourTree, meshSortOrder, saddlePeak);
// Print the contour tree we computed
std::cout << "Computed Contour Tree" << std::endl;
vtkm::worklet::contourtree_augmented::PrintEdgePairArray(saddlePeak);
vtkm::worklet::contourtree_augmented::PrintEdgePairArrayColumnLayout(saddlePeak);
// Print the expected contour tree
std::cout << "Expected Contour Tree" << std::endl;
std::cout << " 0 118" << std::endl;