ec34cb56c4
Also fix deadlocks that occur when portals are not destroyed in time.
1037 lines
49 KiB
C++
1037 lines
49 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_contourtree_augmented_contourtreemaker_h
|
|
#define vtk_m_worklet_contourtree_augmented_contourtreemaker_h
|
|
|
|
#include <iomanip>
|
|
|
|
// local includes
|
|
#include <vtkm/worklet/contourtree_augmented/ArrayTransforms.h>
|
|
#include <vtkm/worklet/contourtree_augmented/ContourTree.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/PrintVectors.h>
|
|
#include <vtkm/worklet/contourtree_augmented/Types.h>
|
|
|
|
// contourtree_maker_inc includes
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/AugmentMergeTrees_InitNewJoinSplitIDAndSuperparents.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/AugmentMergeTrees_SetAugmentedMergeArcs.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/CompressTrees_Step.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ComputeHyperAndSuperStructure_HypernodesSetFirstSuperchild.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ComputeHyperAndSuperStructure_PermuteArcs.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ComputeHyperAndSuperStructure_ResetHyperparentsId.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ComputeHyperAndSuperStructure_SetNewHypernodesAndArcs.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ComputeRegularStructure_LocateSuperarcs.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ComputeRegularStructure_SetArcs.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ContourTreeNodeComparator.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/ContourTreeSuperNodeComparator.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/FindDegrees_FindRHE.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/FindDegrees_ResetUpAndDowndegree.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/FindDegrees_SubtractLHE.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/TransferLeafChains_CollapsePastRegular.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/TransferLeafChains_InitInAndOutbound.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/TransferLeafChains_TransferToContourTree.h>
|
|
#include <vtkm/worklet/contourtree_augmented/contourtreemaker/WasNotTransferred.h>
|
|
|
|
#include <vtkm/worklet/contourtree_augmented/activegraph/SuperArcNodeComparator.h>
|
|
|
|
|
|
//VTKM includes
|
|
#include <vtkm/Types.h>
|
|
#include <vtkm/cont/Algorithm.h>
|
|
#include <vtkm/cont/ArrayHandleCast.h>
|
|
#include <vtkm/cont/ArrayHandleConstant.h>
|
|
#include <vtkm/cont/ArrayHandleImplicit.h>
|
|
#include <vtkm/cont/ArrayHandleIndex.h>
|
|
#include <vtkm/cont/ArrayHandlePermutation.h>
|
|
#include <vtkm/cont/ArrayHandleTransform.h>
|
|
#include <vtkm/cont/Invoker.h>
|
|
|
|
|
|
|
|
namespace contourtree_maker_inc_ns = vtkm::worklet::contourtree_augmented::contourtree_maker_inc;
|
|
namespace active_graph_inc_ns = vtkm::worklet::contourtree_augmented::active_graph_inc;
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace worklet
|
|
{
|
|
namespace contourtree_augmented
|
|
{
|
|
|
|
|
|
class ContourTreeMaker
|
|
{ // class MergeTree
|
|
public:
|
|
vtkm::cont::Invoker Invoke;
|
|
|
|
// the contour tree, join tree & split tree to use
|
|
ContourTree& ContourTreeResult;
|
|
MergeTree& JoinTree;
|
|
MergeTree& SplitTree;
|
|
|
|
// vectors of up and down degree kept during the computation
|
|
IdArrayType Updegree;
|
|
IdArrayType Downdegree;
|
|
|
|
// vectors for tracking merge superarcs
|
|
IdArrayType AugmentedJoinSuperarcs;
|
|
IdArrayType AugmentedSplitSuperarcs;
|
|
|
|
// vector for the active set of supernodes
|
|
IdArrayType ActiveSupernodes;
|
|
|
|
// counter for the number of iterations it took
|
|
vtkm::Id NumIterations;
|
|
|
|
// constructor does the real work does the real work but mostly, it just calls the following two routines
|
|
ContourTreeMaker(ContourTree& theContourTree, MergeTree& joinTree, MergeTree& splitTree);
|
|
|
|
// computes the hyperarcs in the contour tree
|
|
void ComputeHyperAndSuperStructure();
|
|
|
|
// computes the regular arcs in the contour tree. Augment the contour tree with all regular vertices.
|
|
void ComputeRegularStructure(MeshExtrema& meshExtrema);
|
|
|
|
// compute the parital regular arcs by augmenting the contour tree with the relevant vertices on the boundary
|
|
template <class Mesh, class MeshBoundaryExecObj>
|
|
void ComputeBoundaryRegularStructure(MeshExtrema& meshExtrema,
|
|
const Mesh& mesh,
|
|
const MeshBoundaryExecObj& meshBoundary);
|
|
|
|
// routine that augments the join & split tree with each other's supernodes
|
|
// the augmented trees will be stored in the joinSuperarcs / mergeSuperarcs arrays
|
|
// the sort IDs will be stored in the ContourTree's arrays, &c.
|
|
void AugmentMergeTrees();
|
|
|
|
// routine to transfer leaf chains to contour tree
|
|
void TransferLeafChains(bool isJoin);
|
|
|
|
// routine to collapse regular vertices
|
|
void CompressTrees();
|
|
|
|
// compresses active set of supernodes
|
|
void CompressActiveSupernodes();
|
|
|
|
// finds the degree of each supernode from the merge trees
|
|
void FindDegrees();
|
|
|
|
// debug routine
|
|
void DebugPrint(const char* message, const char* fileName, long lineNum);
|
|
|
|
}; // class ContourTreeMaker
|
|
|
|
|
|
// TODO we should add an Init function to move the heavy-weight computions out of the constructor
|
|
// constructor
|
|
ContourTreeMaker::ContourTreeMaker(ContourTree& contourTree,
|
|
MergeTree& joinTree,
|
|
MergeTree& splitTree)
|
|
: ContourTreeResult(contourTree)
|
|
, JoinTree(joinTree)
|
|
, SplitTree(splitTree)
|
|
, Updegree()
|
|
, Downdegree()
|
|
, AugmentedJoinSuperarcs()
|
|
, AugmentedSplitSuperarcs()
|
|
, ActiveSupernodes()
|
|
, NumIterations(0)
|
|
{ // constructor
|
|
} //MakeContourTree()
|
|
|
|
|
|
void ContourTreeMaker::ComputeHyperAndSuperStructure()
|
|
{ // ComputeHyperAndSuperStructure()
|
|
|
|
// augment the merge trees & establish the list of supernodes
|
|
AugmentMergeTrees();
|
|
|
|
// track how many iterations it takes
|
|
this->NumIterations = 0;
|
|
|
|
// loop until no arcs remaining to be found
|
|
// tree can end with either 0 or 1 vertices unprocessed
|
|
// 0 means the last edge was pruned from both ends
|
|
// 1 means that there were two final edges meeting at a vertex
|
|
while (this->ActiveSupernodes.GetNumberOfValues() > 1)
|
|
{ // loop until no active vertices remaining
|
|
// recompute the vertex degrees
|
|
FindDegrees();
|
|
|
|
// alternate iterations between upper & lower
|
|
if (this->NumIterations % 2 == 0)
|
|
TransferLeafChains(true);
|
|
else
|
|
TransferLeafChains(false);
|
|
|
|
// compress join & split trees
|
|
CompressTrees();
|
|
// compress the active list of supernodes
|
|
CompressActiveSupernodes();
|
|
this->NumIterations++;
|
|
} // loop until no active vertices remaining
|
|
|
|
// test for final edges meeting
|
|
if (this->ActiveSupernodes.GetNumberOfValues() == 1)
|
|
{ // meet at a vertex
|
|
vtkm::Id superID = this->ActiveSupernodes.ReadPortal().Get(0);
|
|
this->ContourTreeResult.Superarcs.WritePortal().Set(superID,
|
|
static_cast<vtkm::Id>(NO_SUCH_ELEMENT));
|
|
this->ContourTreeResult.Hyperarcs.WritePortal().Set(superID,
|
|
static_cast<vtkm::Id>(NO_SUCH_ELEMENT));
|
|
this->ContourTreeResult.Hyperparents.WritePortal().Set(superID, superID);
|
|
this->ContourTreeResult.WhenTransferred.WritePortal().Set(superID,
|
|
this->NumIterations | IS_HYPERNODE);
|
|
} // meet at a vertex
|
|
DebugPrint("Contour Tree Constructed. Now Swizzling", __FILE__, __LINE__);
|
|
|
|
|
|
// next, we have to set up the hyper and super structure arrays one at a time
|
|
// at present, all superarcs / Hyperarcs are expressed in terms of supernode IDs
|
|
// but we will want to move supernodes around.
|
|
// the first step is therefore to find the new order of supernodes by sorting
|
|
// we will use the hypernodes array for this, as we will want a copy to end up there
|
|
|
|
// create linear sequence of numbers 0, 1, .. NumSupernodes
|
|
vtkm::cont::ArrayHandleIndex initContourTreeHypernodes(
|
|
this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::Copy(initContourTreeHypernodes, this->ContourTreeResult.Hypernodes);
|
|
|
|
// now we sort hypernodes array with a comparator
|
|
vtkm::cont::Algorithm::Sort(this->ContourTreeResult.Hypernodes,
|
|
contourtree_maker_inc_ns::ContourTreeSuperNodeComparator(
|
|
this->ContourTreeResult.Hyperparents,
|
|
this->ContourTreeResult.Supernodes,
|
|
this->ContourTreeResult.WhenTransferred));
|
|
|
|
// we have to permute a bunch of arrays, so let's have some temporaries to store them
|
|
IdArrayType permutedHyperparents;
|
|
PermuteArray<vtkm::Id>(
|
|
this->ContourTreeResult.Hyperparents, this->ContourTreeResult.Hypernodes, permutedHyperparents);
|
|
IdArrayType permutedSupernodes;
|
|
PermuteArray<vtkm::Id>(
|
|
this->ContourTreeResult.Supernodes, this->ContourTreeResult.Hypernodes, permutedSupernodes);
|
|
IdArrayType permutedSuperarcs;
|
|
PermuteArray<vtkm::Id>(
|
|
this->ContourTreeResult.Superarcs, this->ContourTreeResult.Hypernodes, permutedSuperarcs);
|
|
|
|
// now we establish the reverse index array
|
|
IdArrayType superSortIndex;
|
|
superSortIndex.Allocate(this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
// The following copy is equivalent to
|
|
// for (vtkm::Id supernode = 0; supernode < this->ContourTreeResult.Supernodes.size(); supernode++)
|
|
// superSortIndex[this->ContourTreeResult.Hypernodes[supernode]] = supernode;
|
|
|
|
//typedef vtkm::cont::ArrayHandlePermutation<IdArrayType, vtkm::cont::ArrayHandleIndex> PermuteArrayHandleIndex;
|
|
vtkm::cont::ArrayHandlePermutation<IdArrayType, IdArrayType> permutedSuperSortIndex(
|
|
this->ContourTreeResult.Hypernodes, // index array
|
|
superSortIndex); // value array
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleIndex(
|
|
this->ContourTreeResult.Supernodes.GetNumberOfValues()), // source value array
|
|
permutedSuperSortIndex); // target array
|
|
|
|
// we then copy the supernodes & hyperparents back to the main array
|
|
vtkm::cont::Algorithm::Copy(permutedSupernodes, this->ContourTreeResult.Supernodes);
|
|
vtkm::cont::Algorithm::Copy(permutedHyperparents, this->ContourTreeResult.Hyperparents);
|
|
|
|
|
|
// we need an extra permutation to get the superarcs correct
|
|
contourtree_maker_inc_ns::ComputeHyperAndSuperStructure_PermuteArcs permuteSuperarcsWorklet;
|
|
this->Invoke(permuteSuperarcsWorklet,
|
|
permutedSuperarcs, // (input)
|
|
superSortIndex, // (input)
|
|
this->ContourTreeResult.Superarcs); // (output)
|
|
|
|
// we will permute the hyperarcs & copy them back with the new supernode target IDs
|
|
IdArrayType permutedHyperarcs;
|
|
PermuteArray<vtkm::Id>(
|
|
this->ContourTreeResult.Hyperarcs, this->ContourTreeResult.Hypernodes, permutedHyperarcs);
|
|
contourtree_maker_inc_ns::ComputeHyperAndSuperStructure_PermuteArcs permuteHyperarcsWorklet;
|
|
this->Invoke(
|
|
permuteHyperarcsWorklet, permutedHyperarcs, superSortIndex, this->ContourTreeResult.Hyperarcs);
|
|
|
|
// now swizzle the WhenTransferred value
|
|
IdArrayType permutedWhenTransferred;
|
|
PermuteArray<vtkm::Id>(this->ContourTreeResult.WhenTransferred,
|
|
this->ContourTreeResult.Hypernodes,
|
|
permutedWhenTransferred);
|
|
vtkm::cont::Algorithm::Copy(permutedWhenTransferred, this->ContourTreeResult.WhenTransferred);
|
|
|
|
// now we compress both the hypernodes & Hyperarcs
|
|
IdArrayType newHypernodePosition;
|
|
OnefIfHypernode oneIfHypernodeFunctor;
|
|
auto oneIfHypernodeArrayHandle = vtkm::cont::ArrayHandleTransform<IdArrayType, OnefIfHypernode>(
|
|
this->ContourTreeResult.WhenTransferred, oneIfHypernodeFunctor);
|
|
vtkm::cont::Algorithm::ScanExclusive(oneIfHypernodeArrayHandle, newHypernodePosition);
|
|
|
|
vtkm::Id nHypernodes = 0;
|
|
{
|
|
vtkm::cont::ArrayHandle<vtkm::Id> temp;
|
|
temp.Allocate(2);
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
newHypernodePosition, newHypernodePosition.GetNumberOfValues() - 1, 1, temp);
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
this->ContourTreeResult.WhenTransferred,
|
|
this->ContourTreeResult.WhenTransferred.GetNumberOfValues() - 1,
|
|
1,
|
|
temp,
|
|
1);
|
|
auto portal = temp.ReadPortal();
|
|
nHypernodes = portal.Get(0) + oneIfHypernodeFunctor(portal.Get(1));
|
|
}
|
|
|
|
IdArrayType newHypernodes;
|
|
newHypernodes.Allocate(nHypernodes);
|
|
IdArrayType newHyperarcs;
|
|
newHyperarcs.Allocate(nHypernodes);
|
|
|
|
contourtree_maker_inc_ns::ComputeHyperAndSuperStructure_SetNewHypernodesAndArcs
|
|
setNewHypernodesAndArcsWorklet;
|
|
this->Invoke(setNewHypernodesAndArcsWorklet,
|
|
this->ContourTreeResult.Supernodes,
|
|
this->ContourTreeResult.WhenTransferred,
|
|
this->ContourTreeResult.Hypernodes,
|
|
this->ContourTreeResult.Hyperarcs,
|
|
newHypernodePosition,
|
|
newHypernodes,
|
|
newHyperarcs);
|
|
// swap in the new computed arrays.
|
|
// vtkm ArrayHandles are smart so we can just swap the new data in here rather than copy
|
|
//vtkm::cont::Algorithm::Copy(newHypernodes, this->ContourTreeResult.Hypernodes);
|
|
//vtkm::cont::Algorithm::Copy(newHyperarcs, this->ContourTreeResult.Hyperarcs);
|
|
this->ContourTreeResult.Hypernodes.ReleaseResources();
|
|
this->ContourTreeResult.Hypernodes = newHypernodes;
|
|
this->ContourTreeResult.Hyperarcs.ReleaseResources();
|
|
this->ContourTreeResult.Hyperarcs = newHyperarcs;
|
|
|
|
|
|
// now reuse the superSortIndex array for hypernode IDs
|
|
// The following copy is equivalent to
|
|
// for (vtkm::Id hypernode = 0; hypernode < this->ContourTreeResult.Hypernodes.size(); hypernode++)
|
|
// superSortIndex[this->ContourTreeResult.Hypernodes[hypernode]] = hypernode;
|
|
// source data array is a simple linear index from 0 to #Hypernodes
|
|
vtkm::cont::ArrayHandleIndex tempHypernodeIndexArray(
|
|
this->ContourTreeResult.Hypernodes.GetNumberOfValues());
|
|
// target data array for the copy operation is superSortIndex permuted by this->ContourTreeResult.Hypernodes
|
|
permutedSuperSortIndex = vtkm::cont::ArrayHandlePermutation<IdArrayType, IdArrayType>(
|
|
this->ContourTreeResult.Hypernodes, superSortIndex);
|
|
vtkm::cont::Algorithm::Copy(tempHypernodeIndexArray, permutedSuperSortIndex);
|
|
|
|
// loop through the hyperparents array, setting the first one for each
|
|
contourtree_maker_inc_ns::ComputeHyperAndSuperStructure_HypernodesSetFirstSuperchild
|
|
hypernodesSetFirstSuperchildWorklet;
|
|
this->Invoke(hypernodesSetFirstSuperchildWorklet,
|
|
this->ContourTreeResult.Hyperparents,
|
|
superSortIndex,
|
|
this->ContourTreeResult.Hypernodes);
|
|
|
|
// do a separate loop to reset the hyperparent's ID
|
|
// This does the following
|
|
// for (vtkm::Id supernode = 0; supernode < this->ContourTreeResult.Supernodes.size(); supernode++)
|
|
// this->ContourTreeResult.Hyperparents[supernode] = superSortIndex[MaskedIndex(this->ContourTreeResult.Hyperparents[supernode])];
|
|
contourtree_maker_inc_ns::ComputeHyperAndSuperStructure_ResetHyperparentsId
|
|
resetHyperparentsIdWorklet;
|
|
this->Invoke(resetHyperparentsIdWorklet, superSortIndex, this->ContourTreeResult.Hyperparents);
|
|
|
|
DebugPrint("Contour Tree Super Structure Constructed", __FILE__, __LINE__);
|
|
} // ComputeHyperAndSuperStructure()
|
|
|
|
|
|
// computes the regular arcs in the contour tree
|
|
void ContourTreeMaker::ComputeRegularStructure(MeshExtrema& meshExtrema)
|
|
{ // ComputeRegularStructure()
|
|
// First step - use the superstructure to set the superparent for all supernodes
|
|
auto supernodesIndex =
|
|
vtkm::cont::ArrayHandleIndex(this->ContourTreeResult.Supernodes
|
|
.GetNumberOfValues()); // Counting array of length #supernodes to
|
|
auto permutedSuperparents = vtkm::cont::make_ArrayHandlePermutation(
|
|
this->ContourTreeResult.Supernodes,
|
|
this->ContourTreeResult.Superparents); // superparents array permmuted by the supernodes array
|
|
vtkm::cont::Algorithm::Copy(supernodesIndex, permutedSuperparents);
|
|
// The above copy is equivlant to
|
|
// for (indexType supernode = 0; supernode < this->ContourTreeResult.supernodes.size(); supernode++)
|
|
// this->ContourTreeResult.superparents[this->ContourTreeResult.Supernodes[supernode]] = supernode;
|
|
|
|
// Second step - for all remaining (regular) nodes, locate the superarc to which they belong
|
|
contourtree_maker_inc_ns::ComputeRegularStructure_LocateSuperarcs locateSuperarcsWorklet(
|
|
this->ContourTreeResult.Hypernodes.GetNumberOfValues(),
|
|
this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
this->Invoke(locateSuperarcsWorklet,
|
|
this->ContourTreeResult.Superparents, // (input/output)
|
|
this->ContourTreeResult.WhenTransferred, // (input)
|
|
this->ContourTreeResult.Hyperparents, // (input)
|
|
this->ContourTreeResult.Hyperarcs, // (input)
|
|
this->ContourTreeResult.Hypernodes, // (input)
|
|
this->ContourTreeResult.Supernodes, // (input)
|
|
meshExtrema.Peaks, // (input)
|
|
meshExtrema.Pits); // (input)
|
|
|
|
// We have now set the superparent correctly for each node, and need to sort them to get the correct regular arcs
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleIndex(this->ContourTreeResult.Arcs.GetNumberOfValues()),
|
|
this->ContourTreeResult.Nodes);
|
|
|
|
vtkm::cont::Algorithm::Sort(
|
|
this->ContourTreeResult.Nodes,
|
|
contourtree_maker_inc_ns::ContourTreeNodeComparator(this->ContourTreeResult.Superparents,
|
|
this->ContourTreeResult.Superarcs));
|
|
|
|
// now set the arcs based on the array
|
|
contourtree_maker_inc_ns::ComputeRegularStructure_SetArcs setArcsWorklet(
|
|
this->ContourTreeResult.Arcs.GetNumberOfValues());
|
|
this->Invoke(setArcsWorklet,
|
|
this->ContourTreeResult.Nodes, // (input) arcSorter array
|
|
this->ContourTreeResult.Superparents, // (input)
|
|
this->ContourTreeResult.Superarcs, // (input)
|
|
this->ContourTreeResult.Supernodes, // (input)
|
|
this->ContourTreeResult.Arcs); // (output)
|
|
|
|
DebugPrint("Regular Structure Computed", __FILE__, __LINE__);
|
|
} // ComputeRegularStructure()
|
|
|
|
struct ContourTreeNoSuchElementSuperParents
|
|
{
|
|
template <typename T>
|
|
VTKM_EXEC_CONT bool operator()(const T& x) const
|
|
{
|
|
return (!NoSuchElement(x));
|
|
}
|
|
};
|
|
|
|
void InitIdArrayTypeNoSuchElement(IdArrayType& idArray, vtkm::Id size)
|
|
{
|
|
idArray.Allocate(size);
|
|
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id> noSuchElementArray((vtkm::Id)NO_SUCH_ELEMENT, size);
|
|
vtkm::cont::Algorithm::Copy(noSuchElementArray, idArray);
|
|
}
|
|
|
|
template <class Mesh, class MeshBoundaryExecObj>
|
|
void ContourTreeMaker::ComputeBoundaryRegularStructure(
|
|
MeshExtrema& meshExtrema,
|
|
const Mesh& mesh,
|
|
const MeshBoundaryExecObj& meshBoundaryExecObj)
|
|
{ // ComputeRegularStructure()
|
|
// First step - use the superstructure to set the superparent for all supernodes
|
|
auto supernodesIndex =
|
|
vtkm::cont::ArrayHandleIndex(this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
IdArrayType superparents;
|
|
InitIdArrayTypeNoSuchElement(superparents, mesh.GetNumberOfVertices());
|
|
// superparents array permmuted by the supernodes array
|
|
auto permutedSuperparents =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->ContourTreeResult.Supernodes, superparents);
|
|
vtkm::cont::Algorithm::Copy(supernodesIndex, permutedSuperparents);
|
|
// The above copy is equivlant to
|
|
// for (indexType supernode = 0; supernode < this->ContourTreeResult.Supernodes.size(); supernode++)
|
|
// superparents[this->ContourTreeResult.Supernodes[supernode]] = supernode;
|
|
|
|
// Second step - for all remaining (regular) nodes, locate the superarc to which they belong
|
|
contourtree_maker_inc_ns::ComputeRegularStructure_LocateSuperarcsOnBoundary
|
|
locateSuperarcsOnBoundaryWorklet(this->ContourTreeResult.Hypernodes.GetNumberOfValues(),
|
|
this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
this->Invoke(locateSuperarcsOnBoundaryWorklet,
|
|
superparents, // (input/output)
|
|
this->ContourTreeResult.WhenTransferred, // (input)
|
|
this->ContourTreeResult.Hyperparents, // (input)
|
|
this->ContourTreeResult.Hyperarcs, // (input)
|
|
this->ContourTreeResult.Hypernodes, // (input)
|
|
this->ContourTreeResult.Supernodes, // (input)
|
|
meshExtrema.Peaks, // (input)
|
|
meshExtrema.Pits, // (input)
|
|
meshBoundaryExecObj); // (input)
|
|
|
|
// We have now set the superparent correctly for each node, and need to sort them to get the correct regular arcs
|
|
// DAVID "ContourTreeMaker.h" line 338
|
|
IdArrayType node;
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(superparents.GetNumberOfValues()),
|
|
this->ContourTreeResult.Augmentnodes);
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(superparents.GetNumberOfValues()), node);
|
|
vtkm::cont::Algorithm::CopyIf(node,
|
|
superparents,
|
|
this->ContourTreeResult.Augmentnodes,
|
|
ContourTreeNoSuchElementSuperParents());
|
|
|
|
IdArrayType toCompressed;
|
|
InitIdArrayTypeNoSuchElement(toCompressed, superparents.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleIndex(this->ContourTreeResult.Augmentnodes.GetNumberOfValues()), node);
|
|
auto permutedToCompressed =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->ContourTreeResult.Augmentnodes, // index array
|
|
toCompressed); // value array
|
|
vtkm::cont::Algorithm::Copy(node, // source value array
|
|
permutedToCompressed); // target array
|
|
|
|
// Make superparents correspond to nodes
|
|
IdArrayType tmpsuperparents;
|
|
vtkm::cont::Algorithm::CopyIf(
|
|
superparents, superparents, tmpsuperparents, ContourTreeNoSuchElementSuperParents());
|
|
vtkm::cont::Algorithm::Copy(tmpsuperparents, superparents);
|
|
|
|
// Create array for sorting
|
|
IdArrayType augmentnodes_sorted;
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleIndex(this->ContourTreeResult.Augmentnodes.GetNumberOfValues()),
|
|
augmentnodes_sorted);
|
|
|
|
// use a comparator to do the sort
|
|
vtkm::cont::Algorithm::Sort(augmentnodes_sorted,
|
|
contourtree_maker_inc_ns::ContourTreeNodeComparator(
|
|
superparents, this->ContourTreeResult.Superarcs));
|
|
// now set the arcs based on the array
|
|
InitIdArrayTypeNoSuchElement(this->ContourTreeResult.Augmentarcs,
|
|
this->ContourTreeResult.Augmentnodes.GetNumberOfValues());
|
|
contourtree_maker_inc_ns::ComputeRegularStructure_SetAugmentArcs setAugmentArcsWorklet(
|
|
this->ContourTreeResult.Augmentarcs.GetNumberOfValues());
|
|
this->Invoke(setAugmentArcsWorklet,
|
|
augmentnodes_sorted, // (input) arcSorter array
|
|
superparents, // (input)
|
|
this->ContourTreeResult.Superarcs, // (input)
|
|
this->ContourTreeResult.Supernodes, // (input)
|
|
toCompressed, // (input)
|
|
this->ContourTreeResult.Augmentarcs); // (output)
|
|
DebugPrint("Regular Boundary Structure Computed", __FILE__, __LINE__);
|
|
} // ComputeRegularStructure()
|
|
|
|
|
|
// routine that augments the join & split tree with each other's supernodes
|
|
// the augmented trees will be stored in the joinSuperarcs / mergeSuperarcs arrays
|
|
// the sort IDs will be stored in the ContourTree's arrays, &c.
|
|
void ContourTreeMaker::AugmentMergeTrees()
|
|
{ // ContourTreeMaker::AugmentMergeTrees()
|
|
// in this version, we know that only connectivity-critical points are used
|
|
// so we want to combine the lists of supernodes.
|
|
// but they are not in sorted order, so some juggling is required.
|
|
|
|
// NOTE: The following code block is a direct port from the original PPP2 code using std::set_union
|
|
// In the main code below we replaced steps 1-4 with a combination of VTKM copy, sort, and union operators instead
|
|
/*
|
|
// 1. Allocate an array that is guaranteed to be big enough
|
|
// - the sum of the sizes of the trees or the total size of the data
|
|
vtkm::Id nJoinSupernodes = this->JoinTree.Supernodes.GetNumberOfValues();
|
|
vtkm::Id nSplitSupernodes = this->SplitTree.Supernodes.GetNumberOfValues();
|
|
vtkm::Id nSupernodes = nJoinSupernodes + nSplitSupernodes;
|
|
if (nSupernodes > this->JoinTree.Arcs.GetNumberOfValues())
|
|
nSupernodes = this->JoinTree.Arcs.GetNumberOfValues();
|
|
this->ContourTreeResult.Supernodes.Allocate(nSupernodes);
|
|
|
|
// 2. Make copies of the lists of join & split supernodes & sort them
|
|
IdArrayType joinSort;
|
|
joinSort.Allocate(nJoinSupernodes);
|
|
vtkm::cont::Algorithm::Copy(this->JoinTree.Supernodes, joinSort);
|
|
vtkm::cont::Algorithm::Sort(joinSort);
|
|
IdArrayType splitSort;
|
|
splitSort.Allocate(nSplitSupernodes);
|
|
vtkm::cont::Algorithm::Copy(this->SplitTree.Supernodes, splitSort);
|
|
vtkm::cont::Algorithm::Sort(splitSort);
|
|
|
|
// 3. Use set_union to combine the lists
|
|
auto contTreeSuperNodesBegin = vtkm::cont::ArrayPortalToIteratorBegin(this->ContourTreeResult.Supernodes.WritePortal());
|
|
auto tail = std::set_union(vtkm::cont::ArrayPortalToIteratorBegin(joinSort.WritePortal()),
|
|
vtkm::cont::ArrayPortalToIteratorEnd(joinSort.WritePortal()),
|
|
vtkm::cont::ArrayPortalToIteratorBegin(splitSort.WritePortal()),
|
|
vtkm::cont::ArrayPortalToIteratorEnd(splitSort.WritePortal()),
|
|
contTreeSuperNodesBegin);
|
|
// compute the true number of supernodes
|
|
nSupernodes = tail - contTreeSuperNodesBegin;
|
|
// and release the memory
|
|
joinSort.ReleaseResources();
|
|
splitSort.ReleaseResources();
|
|
|
|
// 4. Resize the supernode array accordingly
|
|
this->ContourTreeResult.Supernodes.Shrink(nSupernodes);
|
|
*/
|
|
|
|
// 1. Allocate an array that is guaranteed to be big enough
|
|
// - the sum of the sizes of the trees or the total size of the data
|
|
vtkm::Id nJoinSupernodes = this->JoinTree.Supernodes.GetNumberOfValues();
|
|
vtkm::Id nSplitSupernodes = this->SplitTree.Supernodes.GetNumberOfValues();
|
|
vtkm::Id nSupernodes = nJoinSupernodes + nSplitSupernodes;
|
|
|
|
// TODO Check whether this replacement for Step 2 to 4 is a problem in terms of performance
|
|
// Step 2 - 4 in original PPP2. Create a sorted list of all unique supernodes from the Join and Split tree.
|
|
this->ContourTreeResult.Supernodes.Allocate(nSupernodes);
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
this->JoinTree.Supernodes, 0, nJoinSupernodes, this->ContourTreeResult.Supernodes, 0);
|
|
vtkm::cont::Algorithm::CopySubRange(this->SplitTree.Supernodes,
|
|
0,
|
|
nSplitSupernodes,
|
|
this->ContourTreeResult.Supernodes,
|
|
nJoinSupernodes);
|
|
|
|
// Need to sort before Unique because VTKM only guarantees to find neighboring duplicates
|
|
vtkm::cont::Algorithm::Sort(this->ContourTreeResult.Supernodes);
|
|
vtkm::cont::Algorithm::Unique(this->ContourTreeResult.Supernodes);
|
|
nSupernodes = this->ContourTreeResult.Supernodes.GetNumberOfValues();
|
|
|
|
// 5. Create lookup arrays for the join & split supernodes' new IDs
|
|
IdArrayType newJoinID;
|
|
newJoinID.Allocate(nJoinSupernodes);
|
|
IdArrayType newSplitID;
|
|
newSplitID.Allocate(nSplitSupernodes);
|
|
|
|
// 6. Each supernode is listed by it's regular ID, so we can use the regular arrays
|
|
// to look up the corresponding supernode IDs in the merge trees, and to transfer
|
|
// the superparent for each
|
|
IdArrayType joinSuperparents;
|
|
joinSuperparents.Allocate(nSupernodes);
|
|
IdArrayType splitSuperparents;
|
|
splitSuperparents.Allocate(nSupernodes);
|
|
|
|
contourtree_maker_inc_ns::AugmentMergeTrees_InitNewJoinSplitIDAndSuperparents
|
|
initNewJoinSplitIDAndSuperparentsWorklet;
|
|
this->Invoke(initNewJoinSplitIDAndSuperparentsWorklet,
|
|
this->ContourTreeResult.Supernodes, //input
|
|
this->JoinTree.Superparents, //input
|
|
this->SplitTree.Superparents, //input
|
|
this->JoinTree.Supernodes, //input
|
|
this->SplitTree.Supernodes, //input
|
|
joinSuperparents, //output
|
|
splitSuperparents, //output
|
|
newJoinID, //output
|
|
newSplitID); //output
|
|
|
|
// 7. use the active supernodes array for sorting
|
|
// create linear sequence of numbers 0, 1, .. nSupernodes
|
|
vtkm::cont::ArrayHandleIndex initActiveSupernodes(nSupernodes);
|
|
vtkm::cont::Algorithm::Copy(initActiveSupernodes, this->ActiveSupernodes);
|
|
|
|
// 8. Once we have got the superparent for each, we can sort by superparents and set
|
|
// the augmented superarcs. We start with the join superarcs
|
|
vtkm::cont::Algorithm::Sort(
|
|
this->ActiveSupernodes,
|
|
active_graph_inc_ns::SuperArcNodeComparator(joinSuperparents, this->JoinTree.IsJoinTree));
|
|
|
|
// 9. Set the augmented join superarcs
|
|
this->AugmentedJoinSuperarcs.Allocate(nSupernodes);
|
|
contourtree_maker_inc_ns::AugmentMergeTrees_SetAugmentedMergeArcs setAugmentedJoinArcsWorklet;
|
|
this->Invoke(setAugmentedJoinArcsWorklet,
|
|
this->ActiveSupernodes, // (input domain)
|
|
joinSuperparents, // (input)
|
|
this->JoinTree.Superarcs, // (input)
|
|
newJoinID, // (input)
|
|
this->AugmentedJoinSuperarcs); // (output)
|
|
|
|
// 10. Now we repeat the process for the split superarcs
|
|
vtkm::cont::Algorithm::Copy(initActiveSupernodes, this->ActiveSupernodes);
|
|
// now sort by the split superparent
|
|
vtkm::cont::Algorithm::Sort(
|
|
this->ActiveSupernodes,
|
|
active_graph_inc_ns::SuperArcNodeComparator(splitSuperparents, this->SplitTree.IsJoinTree));
|
|
|
|
// 11. Set the augmented split superarcs
|
|
this->AugmentedSplitSuperarcs.Allocate(nSupernodes);
|
|
contourtree_maker_inc_ns::AugmentMergeTrees_SetAugmentedMergeArcs setAugmentedSplitArcsWorklet;
|
|
this->Invoke(setAugmentedSplitArcsWorklet,
|
|
this->ActiveSupernodes, // (input domain)
|
|
splitSuperparents, // (input)
|
|
this->SplitTree.Superarcs, // (input)
|
|
newSplitID, // (input)
|
|
this->AugmentedSplitSuperarcs); // (output)
|
|
|
|
// 12. Lastly, we can initialise all of the remaining arrays
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id> noSuchElementArray((vtkm::Id)NO_SUCH_ELEMENT,
|
|
nSupernodes);
|
|
vtkm::cont::Algorithm::Copy(noSuchElementArray, this->ContourTreeResult.Superarcs);
|
|
vtkm::cont::Algorithm::Copy(noSuchElementArray, this->ContourTreeResult.Hyperparents);
|
|
vtkm::cont::Algorithm::Copy(noSuchElementArray, this->ContourTreeResult.Hypernodes);
|
|
vtkm::cont::Algorithm::Copy(noSuchElementArray, this->ContourTreeResult.Hyperarcs);
|
|
vtkm::cont::Algorithm::Copy(noSuchElementArray, this->ContourTreeResult.WhenTransferred);
|
|
|
|
// TODO We should only need to allocate the this->Updegree/Downdegree arrays. We initialize them with 0 here to ensure consistency of debug output
|
|
//this->Updegree.Allocate(nSupernodes);
|
|
//this->Downdegree.Allocate(nSupernodes);
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleConstant<vtkm::Id>(0, nSupernodes),
|
|
this->Updegree);
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleConstant<vtkm::Id>(0, nSupernodes),
|
|
this->Downdegree);
|
|
|
|
DebugPrint("Supernodes Found", __FILE__, __LINE__);
|
|
} // ContourTreeMaker::AugmentMergeTrees()
|
|
|
|
|
|
|
|
namespace details
|
|
{
|
|
|
|
struct LeafChainsToContourTree
|
|
{
|
|
LeafChainsToContourTree(const vtkm::Id nIterations,
|
|
const bool isJoin,
|
|
const IdArrayType& outdegree,
|
|
const IdArrayType& indegree,
|
|
const IdArrayType& outbound,
|
|
const IdArrayType& inbound,
|
|
const IdArrayType& inwards)
|
|
: NumIterations(nIterations)
|
|
, IsJoin(isJoin)
|
|
, Outdegree(outdegree)
|
|
, Indegree(indegree)
|
|
, Outbound(outbound)
|
|
, Inbound(inbound)
|
|
, Inwards(inwards)
|
|
{
|
|
}
|
|
|
|
template <typename DeviceAdapter, typename... Args>
|
|
bool operator()(DeviceAdapter device, Args&&... args) const
|
|
{
|
|
vtkm::cont::Token token;
|
|
contourtree_maker_inc_ns::TransferLeafChains_TransferToContourTree<DeviceAdapter> worklet(
|
|
this->NumIterations, // (input)
|
|
this->IsJoin, // (input)
|
|
this->Outdegree, // (input)
|
|
this->Indegree, // (input)
|
|
this->Outbound, // (input)
|
|
this->Inbound, // (input)
|
|
this->Inwards, // (input)
|
|
token);
|
|
vtkm::worklet::DispatcherMapField<decltype(worklet)> dispatcher(worklet);
|
|
dispatcher.SetDevice(device);
|
|
dispatcher.Invoke(std::forward<Args>(args)...);
|
|
return true;
|
|
}
|
|
|
|
|
|
const vtkm::Id NumIterations;
|
|
const bool IsJoin;
|
|
const IdArrayType& Outdegree;
|
|
const IdArrayType& Indegree;
|
|
const IdArrayType& Outbound;
|
|
const IdArrayType& Inbound;
|
|
const IdArrayType& Inwards;
|
|
};
|
|
}
|
|
|
|
// routine to transfer leaf chains to contour tree
|
|
void ContourTreeMaker::TransferLeafChains(bool isJoin)
|
|
{ // ContourTreeMaker::TransferLeafChains()
|
|
// we need to compute the chains in both directions, so we have two vectors:
|
|
// TODO below we initialize the outbound and inbound arrays with 0 to ensure consistency of debug output. Check if this is needed.
|
|
IdArrayType outbound;
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleConstant<vtkm::Id>(
|
|
0, this->ContourTreeResult.Supernodes.GetNumberOfValues()),
|
|
outbound);
|
|
//outbound.Allocate(this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
IdArrayType inbound;
|
|
//inbound.Allocate(this->ContourTreeResult.Supernodes.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleConstant<vtkm::Id>(
|
|
0, this->ContourTreeResult.Supernodes.GetNumberOfValues()),
|
|
inbound);
|
|
|
|
|
|
// a reference for the inwards array we use to initialise
|
|
IdArrayType& inwards = isJoin ? this->AugmentedJoinSuperarcs : this->AugmentedSplitSuperarcs;
|
|
// and references for the degrees
|
|
IdArrayType& indegree = isJoin ? this->Downdegree : this->Updegree;
|
|
IdArrayType& outdegree = isJoin ? this->Updegree : this->Downdegree;
|
|
|
|
// loop to each active node to copy join/split to outbound and inbound arrays
|
|
contourtree_maker_inc_ns::TransferLeafChains_InitInAndOutbound initInAndOutboundWorklet;
|
|
this->Invoke(initInAndOutboundWorklet,
|
|
this->ActiveSupernodes, // (input)
|
|
inwards, // (input)
|
|
outdegree, // (input)
|
|
indegree, // (input)
|
|
outbound, // (output)
|
|
inbound); // (output)
|
|
|
|
DebugPrint("Init in and outbound -- Step 1", __FILE__, __LINE__);
|
|
|
|
// Compute the number of log steps required in this pass
|
|
vtkm::Id numLogSteps = 1;
|
|
for (vtkm::Id shifter = this->ActiveSupernodes.GetNumberOfValues(); shifter != 0; shifter >>= 1)
|
|
{
|
|
numLogSteps++;
|
|
}
|
|
|
|
|
|
// loop to find the now-regular vertices and collapse past them without altering
|
|
// the existing join & split arcs
|
|
for (vtkm::Id iteration = 0; iteration < numLogSteps; iteration++)
|
|
{ // per iteration
|
|
// loop through the vertices, updating outbound
|
|
contourtree_maker_inc_ns::TransferLeafChains_CollapsePastRegular collapsePastRegularWorklet;
|
|
this->Invoke(collapsePastRegularWorklet,
|
|
this->ActiveSupernodes, // (input)
|
|
outbound, // (input/output)
|
|
inbound); // (input/output)
|
|
|
|
} // per iteration
|
|
|
|
DebugPrint("Init in and outbound -- Step 2", __FILE__, __LINE__);
|
|
|
|
// at this point, the outbound vector chains everything outwards to the leaf
|
|
// any vertices on the last outbound leaf superarc point to the leaf
|
|
// and the leaf itself will point to its saddle, identifying the hyperarc
|
|
|
|
// what we want to do is:
|
|
// a. for leaves (tested by degree),
|
|
// i. we use inbound as the hyperarc
|
|
// ii. we use inwards as the superarc
|
|
// iii.we use self as the hyperparent
|
|
// b. for regular vertices pointing to a leaf (test by outbound's degree),
|
|
// i. we use outbound as the hyperparent
|
|
// ii. we use inwards as the superarc
|
|
// c. for all other vertics
|
|
// ignore
|
|
|
|
|
|
// loop through the active vertices
|
|
details::LeafChainsToContourTree task(this->NumIterations, // (input)
|
|
isJoin, // (input)
|
|
outdegree, // (input)
|
|
indegree, // (input)
|
|
outbound, // (input)
|
|
inbound, // (input)
|
|
inwards); // (input)
|
|
vtkm::cont::TryExecute(task,
|
|
this->ActiveSupernodes, // (input)
|
|
this->ContourTreeResult.Hyperparents, // (output)
|
|
this->ContourTreeResult.Hyperarcs, // (output)
|
|
this->ContourTreeResult.Superarcs, // (output)
|
|
this->ContourTreeResult.WhenTransferred); // (output)
|
|
|
|
DebugPrint(isJoin ? "Upper Regular Chains Transferred" : "Lower Regular Chains Transferred",
|
|
__FILE__,
|
|
__LINE__);
|
|
} // ContourTreeMaker::TransferLeafChains()
|
|
|
|
|
|
// routine to compress trees by removing regular vertices as well as Hypernodes
|
|
void ContourTreeMaker::CompressTrees()
|
|
{ // ContourTreeMaker::CompressTrees()
|
|
|
|
// Compute the number of log steps required in this pass
|
|
vtkm::Id numLogSteps = 1;
|
|
for (vtkm::Id shifter = this->ActiveSupernodes.GetNumberOfValues(); shifter != 0; shifter >>= 1)
|
|
{
|
|
numLogSteps++;
|
|
}
|
|
|
|
// loop to update the merge trees
|
|
for (vtkm::Id logStep = 0; logStep < numLogSteps; logStep++)
|
|
{ // iteration log times
|
|
contourtree_maker_inc_ns::CompressTrees_Step compressTreesStepWorklet;
|
|
this->Invoke(compressTreesStepWorklet,
|
|
this->ActiveSupernodes, // (input)
|
|
this->ContourTreeResult.Superarcs, // (input)
|
|
this->AugmentedJoinSuperarcs, // (input/output)
|
|
this->AugmentedSplitSuperarcs // (input/output)
|
|
);
|
|
|
|
} // iteration log times
|
|
|
|
DebugPrint("Trees Compressed", __FILE__, __LINE__);
|
|
} // ContourTreeMaker::CompressTrees()
|
|
|
|
|
|
// compresses trees to remove transferred vertices
|
|
void ContourTreeMaker::CompressActiveSupernodes()
|
|
{ // ContourTreeMaker::CompressActiveSupernodes()
|
|
// copy only if this->ContourTreeResult.WhenTransferred has been set
|
|
IdArrayType compressedActiveSupernodes;
|
|
|
|
// Transform the WhenTransferred array to return 1 if the index was not transferred and 0 otherwise
|
|
auto wasNotTransferred =
|
|
vtkm::cont::ArrayHandleTransform<IdArrayType, contourtree_maker_inc_ns::WasNotTransferred>(
|
|
this->ContourTreeResult.WhenTransferred, contourtree_maker_inc_ns::WasNotTransferred());
|
|
// Permute the wasNotTransferred array handle so that the lookup is based on the value of the indices in the active supernodes array
|
|
auto notTransferredActiveSupernodes =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->ActiveSupernodes, wasNotTransferred);
|
|
// Keep only the indices of the active supernodes that have not been transferred yet
|
|
vtkm::cont::Algorithm::CopyIf(
|
|
this->ActiveSupernodes, notTransferredActiveSupernodes, compressedActiveSupernodes);
|
|
// Copy the data into the active supernodes
|
|
ActiveSupernodes.ReleaseResources();
|
|
ActiveSupernodes =
|
|
compressedActiveSupernodes; // vtkm ArrayHandles are smart, so we can just swap it in without having to copy
|
|
|
|
DebugPrint("Active Supernodes Compressed", __FILE__, __LINE__);
|
|
} // ContourTreeMaker::CompressActiveSupernodes()
|
|
|
|
|
|
void ContourTreeMaker::FindDegrees()
|
|
{ // ContourTreeMaker::FindDegrees()
|
|
using PermuteIndexArray = vtkm::cont::ArrayHandlePermutation<IdArrayType, IdArrayType>;
|
|
|
|
// retrieve the size to register for speed
|
|
vtkm::Id nActiveSupernodes = this->ActiveSupernodes.GetNumberOfValues();
|
|
|
|
// reset the Updegree & Downdegree
|
|
contourtree_maker_inc_ns::FindDegrees_ResetUpAndDowndegree resetUpAndDowndegreeWorklet;
|
|
this->Invoke(
|
|
resetUpAndDowndegreeWorklet, this->ActiveSupernodes, this->Updegree, this->Downdegree);
|
|
|
|
DebugPrint("Degrees Set to 0", __FILE__, __LINE__);
|
|
|
|
// now we loop through every join & split arc, updating degrees
|
|
// to minimise memory footprint, we will do two separate loops
|
|
// but we could combine the two into paired loops
|
|
// first we establish an array of destination vertices (since outdegree is always 1)
|
|
IdArrayType inNeighbour;
|
|
//inNeighbour.Allocate(nActiveSupernodes);
|
|
//PermuteIndexArray permuteInNeighbour(this->ActiveSupernodes, inNeighbour);
|
|
PermuteIndexArray permuteAugmentedJoinSuperarcs(this->ActiveSupernodes,
|
|
this->AugmentedJoinSuperarcs);
|
|
vtkm::cont::Algorithm::Copy(permuteAugmentedJoinSuperarcs, inNeighbour);
|
|
// now sort to group copies together
|
|
vtkm::cont::Algorithm::Sort(inNeighbour);
|
|
|
|
// there's probably a smarter scatter-gather solution to this, but this should work
|
|
// find the RHE of each segment
|
|
contourtree_maker_inc_ns::FindDegrees_FindRHE joinFindRHEWorklet(nActiveSupernodes);
|
|
this->Invoke(joinFindRHEWorklet, inNeighbour, this->Updegree);
|
|
|
|
// now subtract the LHE to get the size
|
|
contourtree_maker_inc_ns::FindDegrees_SubtractLHE joinSubractLHEWorklet;
|
|
this->Invoke(joinSubractLHEWorklet, inNeighbour, this->Updegree);
|
|
|
|
// now repeat the same process for the split neighbours
|
|
PermuteIndexArray permuteAugmentedSplitSuperarcs(this->ActiveSupernodes,
|
|
this->AugmentedSplitSuperarcs);
|
|
vtkm::cont::Algorithm::Copy(permuteAugmentedSplitSuperarcs, inNeighbour);
|
|
// now sort to group copies together
|
|
vtkm::cont::Algorithm::Sort(inNeighbour);
|
|
|
|
// there's probably a smarter scatter-gather solution to this, but this should work
|
|
// find the RHE of each segment
|
|
contourtree_maker_inc_ns::FindDegrees_FindRHE splitFindRHEWorklet(nActiveSupernodes);
|
|
this->Invoke(splitFindRHEWorklet, inNeighbour, this->Downdegree);
|
|
|
|
// now subtract the LHE to get the size
|
|
contourtree_maker_inc_ns::FindDegrees_SubtractLHE splitSubractLHEWorklet;
|
|
this->Invoke(splitSubractLHEWorklet, inNeighbour, this->Downdegree);
|
|
|
|
DebugPrint("Degrees Computed", __FILE__, __LINE__);
|
|
} // ContourTreeMaker::FindDegrees()
|
|
|
|
|
|
|
|
void ContourTreeMaker::DebugPrint(const char* message, const char* fileName, long lineNum)
|
|
{ // ContourTreeMaker::DebugPrint()
|
|
#ifdef DEBUG_PRINT
|
|
std::string childString = std::string(message);
|
|
std::cout
|
|
<< "==========================================================================================="
|
|
"==============================================="
|
|
<< "=============================================="
|
|
<< //============================================================================================" <<
|
|
"=============================================================================================="
|
|
"============================================"
|
|
<< std::endl;
|
|
std::cout
|
|
<< "==========================================================================================="
|
|
"==============================================="
|
|
<< "=============================================="
|
|
<< //============================================================================================" <<
|
|
"=============================================================================================="
|
|
"============================================"
|
|
<< std::endl;
|
|
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;
|
|
|
|
// 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(
|
|
(childString + std::string(": Contour Tree")).c_str(), fileName, lineNum);
|
|
std::cout
|
|
<< "==========================================================================================="
|
|
"==============================================="
|
|
<< "=============================================="
|
|
<< //============================================================================================" <<
|
|
"=============================================================================================="
|
|
"============================================"
|
|
<< std::endl;
|
|
|
|
// 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 Maker Contains: " << std::endl;
|
|
std::cout << "------------------------------------------------------" << std::endl;
|
|
std::cout << "NumIterations: " << this->NumIterations << std::endl;
|
|
|
|
PrintHeader(this->Updegree.GetNumberOfValues());
|
|
PrintIndices("Updegree", this->Updegree);
|
|
PrintIndices("Downdegree", this->Downdegree);
|
|
PrintIndices("Aug Join SArcs", this->AugmentedJoinSuperarcs);
|
|
PrintIndices("Aug Split SArcs", this->AugmentedSplitSuperarcs);
|
|
|
|
PrintHeader(this->ActiveSupernodes.GetNumberOfValues());
|
|
PrintIndices("Active SNodes", this->ActiveSupernodes);
|
|
#else
|
|
// Avoid unused parameter warnings
|
|
(void)message;
|
|
(void)fileName;
|
|
(void)lineNum;
|
|
#endif
|
|
} // ContourTreeMaker::DebugPrint()
|
|
|
|
|
|
} // namespace contourtree_augmented
|
|
} // worklet
|
|
} // vtkm
|
|
|
|
#endif
|