66c96a983f
This merge request is Phase 1 of several to implement the distributed parallel contour tree in VTKm. This merge requests adds the base outline for the algorithm. The implementation of the details of the algorithm in the BoundaryRestrictedAugmentedContourTree.h is currently still missing. However, these will require a substantial (~3000) lines of additional code. The goal is to stage the integration process across merge requests to make the review process simpler.
372 lines
18 KiB
C++
372 lines
18 KiB
C++
//============================================================================
|
|
// 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_ContourTreeUniformAugmented_h
|
|
#define vtk_m_worklet_ContourTreeUniformAugmented_h
|
|
|
|
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
// VTKM includes
|
|
#include <vtkm/Math.h>
|
|
#include <vtkm/Types.h>
|
|
#include <vtkm/cont/ArrayHandle.h>
|
|
#include <vtkm/cont/ArrayHandleCounting.h>
|
|
#include <vtkm/cont/Field.h>
|
|
#include <vtkm/cont/Timer.h>
|
|
#include <vtkm/worklet/DispatcherMapField.h>
|
|
#include <vtkm/worklet/WorkletMapField.h>
|
|
|
|
// Contour tree worklet includes
|
|
#include <vtkm/worklet/contourtree_augmented/ActiveGraph.h>
|
|
#include <vtkm/worklet/contourtree_augmented/ContourTree.h>
|
|
#include <vtkm/worklet/contourtree_augmented/ContourTreeMaker.h>
|
|
#include <vtkm/worklet/contourtree_augmented/MergeTree.h>
|
|
#include <vtkm/worklet/contourtree_augmented/MeshExtrema.h>
|
|
#include <vtkm/worklet/contourtree_augmented/Mesh_DEM_Triangulation.h>
|
|
#include <vtkm/worklet/contourtree_augmented/Types.h>
|
|
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/ContourTreeMesh.h>
|
|
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/mesh_boundary/MeshBoundary2D.h>
|
|
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/mesh_boundary/MeshBoundary3D.h>
|
|
#include <vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes/mesh_boundary/MeshBoundaryContourTreeMesh.h>
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace worklet
|
|
{
|
|
|
|
/// Compute the contour tree for 2d and 3d uniform grids and arbitrary topology graphs
|
|
class ContourTreeAugmented
|
|
{
|
|
public:
|
|
/*!
|
|
* Run the contour tree to merge an existing set of contour trees
|
|
*
|
|
* fieldArray : Needed only as a pass-through value but not used in this case
|
|
* mesh : The ContourTreeMesh for which the contour tree should be computed
|
|
* contourTree : The output contour tree to be computed (output)
|
|
* sortOrder : The sort order for the mesh vertices (output)
|
|
* nIterations : The number of iterations used to compute the contour tree (output)
|
|
* computeRegularStructure : 0=Off, 1=full augmentation with all vertices
|
|
* 2=boundary augmentation using meshBoundary
|
|
* meshBoundary : This parameter is generated by calling mesh.GetMeshBoundaryExecutionObject
|
|
* For regular 2D/3D meshes this required no extra parameters, however, for a
|
|
* ContourTreeMesh additional information about the block must be given. Rather
|
|
* than generating the MeshBoundary descriptor here, we therefore, require it
|
|
* as an input. The MeshBoundary is used to augment the contour tree with the
|
|
* mesh boundary vertices. It is needed only if we want to augement by the
|
|
* mesh boundary and computeRegularStructure is False (i.e., if we compute
|
|
* the full regular strucuture this is not needed because all vertices
|
|
* (including the boundary) will be addded to the tree anyways.
|
|
*/
|
|
template <typename FieldType, typename StorageType>
|
|
void Run(const vtkm::cont::ArrayHandle<FieldType, StorageType>
|
|
fieldArray, // TODO: We really should not need this
|
|
contourtree_augmented::ContourTreeMesh<FieldType>& mesh,
|
|
contourtree_augmented::ContourTree& contourTree,
|
|
contourtree_augmented::IdArrayType sortOrder,
|
|
vtkm::Id& nIterations,
|
|
unsigned int computeRegularStructure,
|
|
const contourtree_augmented::MeshBoundaryContourTreeMeshExec& meshBoundary)
|
|
{
|
|
RunContourTree(
|
|
fieldArray, // Just a place-holder to fill the required field. Used when calling SortData on the contour tree which is a no-op
|
|
contourTree,
|
|
sortOrder,
|
|
nIterations,
|
|
mesh,
|
|
computeRegularStructure,
|
|
meshBoundary);
|
|
return;
|
|
}
|
|
|
|
/*!
|
|
* Run the contour tree analysis. This helper function is used to
|
|
* allow one to run the contour tree in a consistent fashion independent
|
|
* of whether the data is 2D, 3D, or 3D_MC. This function initalizes
|
|
* the approbritate mesh class from the contourtree_augmented worklet
|
|
* and constructs ths mesh boundary exectuion object to be used. It the
|
|
* subsequently calls RunContourTree method to compute the actual contour tree.
|
|
*
|
|
* fieldArray : Needed only as a pass-through value but not used in this case
|
|
* mesh : The ContourTreeMesh for which the contour tree should be computed
|
|
* contourTree : The output contour tree to be computed (output)
|
|
* sortOrder : The sort order for the mesh vertices (output)
|
|
* nIterations : The number of iterations used to compute the contour tree (output)
|
|
* nRows : Number of rows (i.e, x values) in the input mesh
|
|
* nCols : Number of columns (i.e, y values) in the input mesh
|
|
* nSlices : Number of slicex (i.e, z values) in the input mesh. Default is 1
|
|
* to avoid having to set the nSlices for 2D input meshes
|
|
* useMarchingCubes : Boolean indicating whether marching cubes (true) or freudenthal (false)
|
|
* connectivity should be used. Valid only for 3D input data. Default is false.
|
|
* computeRegularStructure : 0=Off, 1=full augmentation with all vertices
|
|
* 2=boundary augmentation using meshBoundary.
|
|
*/
|
|
template <typename FieldType, typename StorageType>
|
|
void Run(const vtkm::cont::ArrayHandle<FieldType, StorageType> fieldArray,
|
|
contourtree_augmented::ContourTree& contourTree,
|
|
contourtree_augmented::IdArrayType& sortOrder,
|
|
vtkm::Id& nIterations,
|
|
const vtkm::Id nRows,
|
|
const vtkm::Id nCols,
|
|
const vtkm::Id nSlices = 1,
|
|
bool useMarchingCubes = false,
|
|
unsigned int computeRegularStructure = 1)
|
|
{
|
|
using namespace vtkm::worklet::contourtree_augmented;
|
|
// 2D Contour Tree
|
|
if (nSlices == 1)
|
|
{
|
|
// Build the mesh and fill in the values
|
|
Mesh_DEM_Triangulation_2D_Freudenthal<FieldType, StorageType> mesh(nRows, nCols);
|
|
// Run the contour tree on the mesh
|
|
RunContourTree(fieldArray,
|
|
contourTree,
|
|
sortOrder,
|
|
nIterations,
|
|
mesh,
|
|
computeRegularStructure,
|
|
mesh.GetMeshBoundaryExecutionObject());
|
|
return;
|
|
}
|
|
// 3D Contour Tree using marching cubes
|
|
else if (useMarchingCubes)
|
|
{
|
|
// Build the mesh and fill in the values
|
|
Mesh_DEM_Triangulation_3D_MarchingCubes<FieldType, StorageType> mesh(nRows, nCols, nSlices);
|
|
// Run the contour tree on the mesh
|
|
RunContourTree(fieldArray,
|
|
contourTree,
|
|
sortOrder,
|
|
nIterations,
|
|
mesh,
|
|
computeRegularStructure,
|
|
mesh.GetMeshBoundaryExecutionObject());
|
|
return;
|
|
}
|
|
// 3D Contour Tree with Freudenthal
|
|
else
|
|
{
|
|
// Build the mesh and fill in the values
|
|
Mesh_DEM_Triangulation_3D_Freudenthal<FieldType, StorageType> mesh(nRows, nCols, nSlices);
|
|
// Run the contour tree on the mesh
|
|
RunContourTree(fieldArray,
|
|
contourTree,
|
|
sortOrder,
|
|
nIterations,
|
|
mesh,
|
|
computeRegularStructure,
|
|
mesh.GetMeshBoundaryExecutionObject());
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
/*!
|
|
* Run the contour tree for the given mesh. This function implements the main steps for
|
|
* computing the contour tree after the mesh has been constructed using the approbrite
|
|
* contour tree mesh class.
|
|
*
|
|
* fieldArray : The values of the mesh
|
|
* contourTree : The output contour tree to be computed (output)
|
|
* sortOrder : The sort order for the mesh vertices (output)
|
|
* nIterations : The number of iterations used to compute the contour tree (output)
|
|
* mesh : The specific mesh (see vtkm/worklet/contourtree_augmented/mesh_dem_meshtypes
|
|
* computeRegularStructure : 0=Off, 1=full augmentation with all vertices
|
|
* 2=boundary augmentation using meshBoundary
|
|
* meshBoundary : This parameter is generated by calling mesh.GetMeshBoundaryExecutionObject
|
|
* For regular 2D/3D meshes this required no extra parameters, however, for a
|
|
* ContourTreeMesh additional information about the block must be given. Rather
|
|
* than generating the MeshBoundary descriptor here, we therefore, require it
|
|
* as an input. The MeshBoundary is used to augment the contour tree with the
|
|
* mesh boundary vertices. It is needed only if we want to augement by the
|
|
* mesh boundary and computeRegularStructure is False (i.e., if we compute
|
|
* the full regular strucuture this is not needed because all vertices
|
|
* (including the boundary) will be addded to the tree anyways.
|
|
*/
|
|
template <typename FieldType,
|
|
typename StorageType,
|
|
typename MeshClass,
|
|
typename MeshBoundaryClass>
|
|
void RunContourTree(const vtkm::cont::ArrayHandle<FieldType, StorageType> fieldArray,
|
|
contourtree_augmented::ContourTree& contourTree,
|
|
contourtree_augmented::IdArrayType& sortOrder,
|
|
vtkm::Id& nIterations,
|
|
MeshClass& mesh,
|
|
unsigned int computeRegularStructure,
|
|
const MeshBoundaryClass& meshBoundary)
|
|
{
|
|
using namespace vtkm::worklet::contourtree_augmented;
|
|
// Stage 1: Load the data into the mesh. This is done in the Run() method above and accessible
|
|
// here via the mesh parameter. The actual data load is performed outside of the
|
|
// worklet in the example contour tree app (or whoever uses the worklet)
|
|
|
|
// Stage 2 : Sort the data on the mesh to initialize sortIndex & indexReverse on the mesh
|
|
// Start the timer for the mesh sort
|
|
vtkm::cont::Timer timer;
|
|
timer.Start();
|
|
std::stringstream timingsStream; // Use a string stream to log in one message
|
|
timingsStream << std::endl;
|
|
timingsStream << " ------------------- Contour Tree Worklet Timings ----------------------"
|
|
<< std::endl;
|
|
|
|
// Sort the mesh data
|
|
mesh.SortData(fieldArray);
|
|
timingsStream << " " << std::setw(38) << std::left << "Sort Data"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
timer.Start();
|
|
|
|
// Stage 3: Assign every mesh vertex to a peak
|
|
MeshExtrema extrema(mesh.NumVertices);
|
|
extrema.SetStarts(mesh, true);
|
|
extrema.BuildRegularChains(true);
|
|
timingsStream << " " << std::setw(38) << std::left << "Join Tree Regular Chains"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
timer.Start();
|
|
|
|
// Stage 4: Identify join saddles & construct Active Join Graph
|
|
MergeTree joinTree(mesh.NumVertices, true);
|
|
ActiveGraph joinGraph(true);
|
|
joinGraph.Initialise(mesh, extrema);
|
|
timingsStream << " " << std::setw(38) << std::left << "Join Tree Initialize Active Graph"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
|
|
#ifdef DEBUG_PRINT
|
|
joinGraph.DebugPrint("Active Graph Instantiated", __FILE__, __LINE__);
|
|
#endif
|
|
timer.Start();
|
|
|
|
// Stage 5: Compute Join Tree Hyperarcs from Active Join Graph
|
|
joinGraph.MakeMergeTree(joinTree, extrema);
|
|
timingsStream << " " << std::setw(38) << std::left << "Join Tree Compute"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
#ifdef DEBUG_PRINT
|
|
joinTree.DebugPrint("Join tree Computed", __FILE__, __LINE__);
|
|
joinTree.DebugPrintTree("Join tree", __FILE__, __LINE__, mesh);
|
|
#endif
|
|
timer.Start();
|
|
|
|
// Stage 6: Assign every mesh vertex to a pit
|
|
extrema.SetStarts(mesh, false);
|
|
extrema.BuildRegularChains(false);
|
|
timingsStream << " " << std::setw(38) << std::left << "Split Tree Regular Chains"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
timer.Start();
|
|
|
|
// Stage 7: Identify split saddles & construct Active Split Graph
|
|
MergeTree splitTree(mesh.NumVertices, false);
|
|
ActiveGraph splitGraph(false);
|
|
splitGraph.Initialise(mesh, extrema);
|
|
timingsStream << " " << std::setw(38) << std::left << "Splot Tree Initialize Active Graph"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
#ifdef DEBUG_PRINT
|
|
splitGraph.DebugPrint("Active Graph Instantiated", __FILE__, __LINE__);
|
|
#endif
|
|
timer.Start();
|
|
|
|
// Stage 8: Compute Split Tree Hyperarcs from Active Split Graph
|
|
splitGraph.MakeMergeTree(splitTree, extrema);
|
|
timingsStream << " " << std::setw(38) << std::left << "Split Tree Compute"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
#ifdef DEBUG_PRINT
|
|
splitTree.DebugPrint("Split tree Computed", __FILE__, __LINE__);
|
|
// Debug split and join tree
|
|
joinTree.DebugPrintTree("Join tree", __FILE__, __LINE__, mesh);
|
|
splitTree.DebugPrintTree("Split tree", __FILE__, __LINE__, mesh);
|
|
#endif
|
|
timer.Start();
|
|
|
|
// Stage 9: Join & Split Tree are Augmented, then combined to construct Contour Tree
|
|
contourTree.Init(mesh.NumVertices);
|
|
ContourTreeMaker treeMaker(contourTree, joinTree, splitTree);
|
|
// 9.1 First we compute the hyper- and super- structure
|
|
treeMaker.ComputeHyperAndSuperStructure();
|
|
timingsStream << " " << std::setw(38) << std::left
|
|
<< "Contour Tree Hyper and Super Structure"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
timer.Start();
|
|
|
|
// 9.2 Then we compute the regular structure
|
|
if (computeRegularStructure == 1) // augment with all vertices
|
|
{
|
|
treeMaker.ComputeRegularStructure(extrema);
|
|
timingsStream << " " << std::setw(38) << std::left << "Contour Tree Regular Structure"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
}
|
|
else if (computeRegularStructure == 2) // augment by the mesh boundary
|
|
{
|
|
treeMaker.ComputeBoundaryRegularStructure(extrema, mesh, meshBoundary);
|
|
timingsStream << " " << std::setw(38) << std::left
|
|
<< "Contour Tree Boundary Regular Structure"
|
|
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;
|
|
}
|
|
timer.Start();
|
|
|
|
// Collect the output data
|
|
nIterations = treeMaker.ContourTreeResult.NumIterations;
|
|
sortOrder = mesh.SortOrder;
|
|
// ProcessContourTree::CollectSortedSuperarcs<DeviceAdapter>(contourTree, mesh.SortOrder, saddlePeak);
|
|
// contourTree.SortedArcPrint(mesh.SortOrder);
|
|
// contourTree.PrintDotSuperStructure();
|
|
|
|
// Log the collected timing results in one coherent log entry
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Perf, timingsStream.str());
|
|
}
|
|
};
|
|
|
|
} // namespace vtkm
|
|
} // namespace vtkm::worklet
|
|
|
|
#endif // vtk_m_worklet_ContourTreeUniformAugmented_h
|