mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-19 18:45:43 +00:00
714 lines
37 KiB
C++
714 lines
37 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.
|
|
//
|
|
//=======================================================================================
|
|
//
|
|
// Parallel Peak Pruning v. 2.0
|
|
//
|
|
// Started June 15, 2017
|
|
//
|
|
// Copyright Hamish Carr, University of Leeds
|
|
//
|
|
// HierarchicalHyperSweeper.h
|
|
//
|
|
//=======================================================================================
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This class encapsulates a hypersweep over the hierarchical contour tree. It is a
|
|
// separate class primarily to keep the post-processing separate from the main tree
|
|
// construction, but it should also make it easier to generalise to arbitrary computations
|
|
//
|
|
// Basically, the way that this operates is:
|
|
// 1. First, we do a local (standard) hypersweep over the hierarchical tree
|
|
// 2. We then fan-in one round at a time. In each round,
|
|
// a. We trade the prefix of the array with our logical partner, then
|
|
// b. Combine the array prefix with our own
|
|
//
|
|
// Tactically, when we do MPI, we can either embed it in this unit, or leave it in the
|
|
// calling unit. For ease of porting, we will leave all MPI in the calling unit, so
|
|
// this unit only needs to do the combination.
|
|
//
|
|
// Note that we could define an operator to be passed in, and probably want to template it
|
|
// that way in the future, but for now, we'll do the first version directly with addition
|
|
//
|
|
// By assumption, we need a commutative property, since we do not guarantee that we have
|
|
// strict ordering along superarcs (which would require sharing a supernode sort with our
|
|
// partner, if not globally)
|
|
//
|
|
//=======================================================================================
|
|
|
|
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_hyper_sweeper_h
|
|
#define vtk_m_worklet_contourtree_distributed_hierarchical_hyper_sweeper_h
|
|
|
|
#include <iomanip>
|
|
#include <string>
|
|
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
|
|
#include <vtkm/worklet/contourtree_augmented/Types.h>
|
|
#include <vtkm/worklet/contourtree_augmented/data_set_mesh/IdRelabeler.h>
|
|
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
|
|
#include <vtkm/worklet/contourtree_distributed/PrintGraph.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/ComputeSuperarcDependentWeightsWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/ComputeSuperarcTransferWeightsWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/InitializeIntrinsicVertexCountComputeSuperparentIdsWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/InitializeIntrinsicVertexCountInitalizeCountsWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/InitializeIntrinsicVertexCountSubtractLowEndWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferTargetComperator.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorklet.h>
|
|
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace worklet
|
|
{
|
|
namespace contourtree_distributed
|
|
{
|
|
|
|
// the class itself
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
class HierarchicalHyperSweeper
|
|
{ // class HierarchicalHyperSweeper
|
|
public:
|
|
// the tree that it hypersweeps over
|
|
const HierarchicalContourTree<ContourTreeFieldType>& HierarchicalTree;
|
|
|
|
// the Id of the base block (used for debug output)
|
|
vtkm::Id BlockId;
|
|
|
|
// array of values being operated over (same size as supernode set)
|
|
// keep both intrinsic & dependent values
|
|
// the intrinsic values are just stored but not modifid here
|
|
const vtkm::cont::ArrayHandle<SweepValueType>& IntrinsicValues;
|
|
// the dependent values are what is being sweeped and are updated here
|
|
const vtkm::cont::ArrayHandle<SweepValueType>& DependentValues;
|
|
// and to avoid an extra log summation, store the number of logical nodes for the underlying block
|
|
// (computed when initializing the regular vertex list)
|
|
vtkm::Id NumOwnedRegularVertices;
|
|
|
|
|
|
// these are working arrays, lifted up here for ease of debug code
|
|
// Subranges of these arrays will be reused in the rounds / iterations rather than being reallocated
|
|
// an array for temporary storage of the prefix sums
|
|
vtkm::cont::ArrayHandle<SweepValueType> ValuePrefixSum;
|
|
// two arrays for collecting targets of transfers
|
|
vtkm::worklet::contourtree_augmented::IdArrayType TransferTarget;
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SortedTransferTarget;
|
|
// an array for indirect sorting of sets of superarcs
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SuperSortPermute;
|
|
|
|
/// Constructor
|
|
/// @param[in] blockId The Id of the base block (used for debug output)
|
|
/// @param[in] hierarchicalTree the tree that to hypersweeps over
|
|
/// @param[in] baseBlock the underlying mesh base block type
|
|
/// @param[in] intrinsicValues array of values of intrinisic nodes are just being stored here but not modified
|
|
/// @param[in] dependentValues array of values being operated over (same size as supernode set)
|
|
HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>(
|
|
vtkm::Id blockId,
|
|
const HierarchicalContourTree<ContourTreeFieldType>& hierarchicalTree,
|
|
const vtkm::cont::ArrayHandle<SweepValueType>& intrinsicValues,
|
|
const vtkm::cont::ArrayHandle<SweepValueType>& dependentValues);
|
|
|
|
/// Our routines to initialize the sweep need to be static (or externa)l if we are going to use the constructor
|
|
/// to run the actual hypersweep
|
|
/// @param[in] hierarchicalTree the tree that to hypersweeps over
|
|
/// @param[in] baseBlock the underlying mesh base block to initialize from
|
|
/// @param[in] localToGlobalIdRelabeler Id relabeler used to compute global indices from local mesh indices
|
|
/// @param[out] superarcRegularCounts arrray for the output superarc regular counts
|
|
template <typename MeshType>
|
|
void InitializeIntrinsicVertexCount(
|
|
const HierarchicalContourTree<ContourTreeFieldType>& hierarchicalTree,
|
|
const MeshType& baseBlock,
|
|
const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler& localToGlobalIdRelabeler,
|
|
vtkm::worklet::contourtree_augmented::IdArrayType& superarcRegularCounts);
|
|
|
|
/// routine to do the local hypersweep using addition / subtraction
|
|
/// The funtion use the ComputeSuperarcDependentWeights, ComputeSuperarcTransferWeights,
|
|
/// and TransferWeights functions to carry out the local hyper sweep
|
|
void LocalHyperSweep();
|
|
|
|
/// Debug routine to print contents of the HiearchicalHyperSweep
|
|
/// @param[in] message Message to print along the debug output
|
|
/// @param[in] fileName Name of the file the message is printed from. Usually set to __FILE__
|
|
/// @param[in] lineNum Line number in the file where the message is printed from. Usually set to __LINE__
|
|
std::string DebugPrint(std::string message, const char* fileName, long lineNum) const;
|
|
|
|
/// Routine to save the HierarchicalContourTree of this HierarchicalHyperSweeper to a Dot file
|
|
/// @param[in] message Message included in the file
|
|
/// @param[in] outFileName The name of the file to write the
|
|
void SaveHierarchicalContourTreeDot(std::string message, const char* outFileName) const;
|
|
|
|
protected:
|
|
// Functions used internally be LocalHyperSweep to compute the local hyper sweep
|
|
|
|
/// Routine to compute the correct weights dependent on each superarc in a subrange (defined by the round & iteration)
|
|
void ComputeSuperarcDependentWeights(vtkm::Id round,
|
|
vtkm::Id iteration,
|
|
vtkm::Id firstSupernode,
|
|
vtkm::Id lastSupernode);
|
|
|
|
/// routine to compute the weights to transfer to superarcs (defined by the round & iteration)
|
|
void ComputeSuperarcTransferWeights(vtkm::Id round,
|
|
vtkm::Id iteration,
|
|
vtkm::Id firstSupernode,
|
|
vtkm::Id lastSupernode);
|
|
|
|
/// routine to transfer the weights
|
|
void TransferWeights(vtkm::Id round,
|
|
vtkm::Id iteration,
|
|
vtkm::Id firstSupernode,
|
|
vtkm::Id lastSupernode);
|
|
|
|
private:
|
|
/// Used internally to Invoke worklets
|
|
vtkm::cont::Invoker Invoke;
|
|
|
|
}; // class HierarchicalHyperSweeper
|
|
|
|
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::HierarchicalHyperSweeper(
|
|
vtkm::Id blockId,
|
|
const HierarchicalContourTree<ContourTreeFieldType>& hierarchicalTree,
|
|
const vtkm::cont::ArrayHandle<SweepValueType>& intrinsicValues,
|
|
const vtkm::cont::ArrayHandle<SweepValueType>& dependentValues)
|
|
: HierarchicalTree(hierarchicalTree)
|
|
, BlockId(blockId)
|
|
, IntrinsicValues(intrinsicValues)
|
|
, DependentValues(dependentValues)
|
|
, NumOwnedRegularVertices(vtkm::Id{ 0 })
|
|
{ // constructor
|
|
// Initalize arrays with 0s
|
|
vtkm::cont::Algorithm::Fill(
|
|
this->ValuePrefixSum, vtkm::Id{ 0 }, this->HierarchicalTree.Supernodes.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::Fill(
|
|
this->TransferTarget, vtkm::Id{ 0 }, this->HierarchicalTree.Supernodes.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::Fill(this->SortedTransferTarget,
|
|
vtkm::Id{ 0 },
|
|
this->HierarchicalTree.Supernodes.GetNumberOfValues());
|
|
// Initialize the supersortPermute to the identity
|
|
vtkm::cont::ArrayHandleIndex tempIndexArray(
|
|
this->HierarchicalTree.Supernodes.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::Copy(tempIndexArray, this->SuperSortPermute);
|
|
} // constructor
|
|
|
|
|
|
// static function used to compute the initial superarc regular counts
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
template <typename MeshType>
|
|
void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::InitializeIntrinsicVertexCount(
|
|
const HierarchicalContourTree<ContourTreeFieldType>& hierarchicalTree,
|
|
const MeshType& baseBlock,
|
|
const vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler& localToGlobalIdRelabeler,
|
|
vtkm::worklet::contourtree_augmented::IdArrayType& superarcRegularCounts)
|
|
{ // InitializeIntrinsicVertexCount()
|
|
// I. Call the mesh to get a list of all regular vertices belonging to the block by global Id
|
|
vtkm::worklet::contourtree_augmented::IdArrayType globalIds;
|
|
// TODO/FIXME: Even though the virtual function on DataSetMesh was removed in commit
|
|
// 93730495813f7b85e59d4a5dae2076977787fd78, this should call the correct function
|
|
// since MeshType is templated and should have the appropriate type. Verify that
|
|
// this is indeed correct.
|
|
baseBlock.GetOwnedVerticesByGlobalId(localToGlobalIdRelabeler, globalIds);
|
|
// and store the size for later reference
|
|
//hierarchicalTree.NumOwnedRegularVertices = globalIds.GetNumberOfValues();
|
|
this->NumOwnedRegularVertices = globalIds.GetNumberOfValues();
|
|
|
|
#ifdef DEBUG_PRINT
|
|
{
|
|
std::stringstream debugStream;
|
|
debugStream << std::endl << "Owned Regular Vertex List" << std::endl;
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(globalIds.GetNumberOfValues(), debugStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices("GlobalId", globalIds, -1, debugStream);
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str());
|
|
}
|
|
#endif
|
|
|
|
// II. Look up the global Ids in the hierarchical tree & convert to superparent Ids
|
|
vtkm::worklet::contourtree_augmented::IdArrayType superparents;
|
|
{ // scope to make sure temporary variables are deleted
|
|
auto findRegularByGlobal = hierarchicalTree.GetFindRegularByGlobal();
|
|
auto computeSuperparentIdsWorklet = vtkm::worklet::contourtree_distributed::
|
|
hierarchical_hyper_sweeper::InitializeIntrinsicVertexCountComputeSuperparentIdsWorklet();
|
|
std::cout << "Invoking computeSuperparentIdsWorklet" << std::endl;
|
|
Invoke(computeSuperparentIdsWorklet, // worklet to run
|
|
globalIds, // input
|
|
findRegularByGlobal, // input
|
|
hierarchicalTree.Regular2Supernode, // input
|
|
hierarchicalTree.Superparents, // input
|
|
superparents // output
|
|
);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
{
|
|
std::stringstream debugStream;
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Superparents", superparents, -1, debugStream);
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str());
|
|
}
|
|
#endif
|
|
|
|
// III. Sort the superparent Ids & count the copies of each
|
|
vtkm::cont::Algorithm ::Sort(superparents);
|
|
|
|
#ifdef DEBUG_PRINT
|
|
{
|
|
std::stringstream debugStream;
|
|
vtkm::worklet::contourtree_augmented::PrintIndices("Sorted SP", superparents, -1, debugStream);
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str());
|
|
}
|
|
#endif
|
|
|
|
// initialize the counts to zero.
|
|
vtkm::cont::Algorithm::Fill(
|
|
superarcRegularCounts, vtkm::Id{ 0 }, this->HierarchicalTree.Supernodes.GetNumberOfValues());
|
|
|
|
// set the count to the Id one off the high end of each range
|
|
Invoke(vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::
|
|
InitializeIntrinsicVertexCountInitalizeCountsWorklet{},
|
|
superparents, // input domain
|
|
superarcRegularCounts // output
|
|
);
|
|
|
|
// now repeat to subtract out the low end
|
|
Invoke(vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::
|
|
InitializeIntrinsicVertexCountSubtractLowEndWorklet{},
|
|
superparents, // input domain
|
|
superarcRegularCounts // output
|
|
);
|
|
// and that is that
|
|
#ifdef DEBUG_PRINT
|
|
{
|
|
std::stringstream debugStream;
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"SuperarcRegularCounts", superarcRegularCounts, -1, debugStream);
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str());
|
|
}
|
|
#endif
|
|
} // InitializeIntrinsicVertexCount()
|
|
|
|
|
|
// routine to do the local hypersweep using addition / subtraction
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::LocalHyperSweep()
|
|
{ // LocalHyperSweep()
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Starting Local HyperSweep"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// I. Iterate over all rounds of the hyperstructure
|
|
for (vtkm::Id round = 0; round <= this->HierarchicalTree.NumRounds; round++)
|
|
{ // per round
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Round ") + std::to_string(round) +
|
|
std::string(" Step 0 Starting Round"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
// A. Iterate over all iterations of the round
|
|
auto numIterationsPortal =
|
|
this->HierarchicalTree.NumIterations
|
|
.ReadPortal(); // TODO/FIXME: Use portal? Or something more efficient?
|
|
for (vtkm::Id iteration = 0; iteration < numIterationsPortal.Get(round); iteration++)
|
|
{ // per iteration
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Round ") + std::to_string(round) +
|
|
std::string(" Step 1 Iteration ") + std::to_string(iteration) +
|
|
std::string(" Step A Starting Iteration"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
// 1. Establish the range of supernode Ids that we want to process
|
|
// TODO/FIXME: Use portal? Or is there a more efficient way?
|
|
auto firstSupernodePerIterationPortal =
|
|
this->HierarchicalTree.FirstSupernodePerIteration[round].ReadPortal();
|
|
vtkm::Id firstSupernode = firstSupernodePerIterationPortal.Get(iteration);
|
|
vtkm::Id lastSupernode = firstSupernodePerIterationPortal.Get(iteration + 1);
|
|
|
|
// call the routine that computes the dependent weights for each superarc in that range
|
|
this->ComputeSuperarcDependentWeights(round, iteration, firstSupernode, lastSupernode);
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Round ") + std::to_string(round) +
|
|
std::string(" Step 1 Iteration ") + std::to_string(iteration) +
|
|
std::string(" Step B Dependent Weights Computed"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
// now call the routine that computes the weights to be transferred and the superarcs to which they transfer
|
|
this->ComputeSuperarcTransferWeights(round, iteration, firstSupernode, lastSupernode);
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Round ") + std::to_string(round) +
|
|
std::string(" Step 1 Iteration ") + std::to_string(iteration) +
|
|
std::string(" Step C Transfer Weights Computed"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// transfer the weights
|
|
this->TransferWeights(round, iteration, firstSupernode, lastSupernode);
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Round ") + std::to_string(round) +
|
|
std::string(" Step 1 Iteration ") + std::to_string(iteration) +
|
|
std::string(" Step D Weights Transferred"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
} // per iteration
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) +
|
|
std::string(" Round ") + std::to_string(round) +
|
|
std::string(" Step 2 Ending Round"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
} // per round
|
|
} // LocalHyperSweep()
|
|
|
|
|
|
// routine to compute the correct weights dependent on each superarc in a subrange (defined by the round & iteration)
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::
|
|
ComputeSuperarcDependentWeights(
|
|
vtkm::Id round,
|
|
vtkm::Id, // iteration, // Kept parameter in case we need it for debugging.
|
|
vtkm::Id firstSupernode,
|
|
vtkm::Id lastSupernode)
|
|
{ // ComputeSuperarcDependentWeights()
|
|
vtkm::Id numSupernodesToProcess = lastSupernode - firstSupernode;
|
|
|
|
// 2. Use sorted prefix sum to compute the total weight to contribute to the super/hypertarget
|
|
// Same as std::partial_sum(sweepValues.begin() + firstSupernode, sweepValues.begin() + lastSupernode, valuePrefixSum.begin() + firstSupernode);
|
|
{
|
|
// DependentValues[firstSuperNode, lastSupernode)
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
dependentValuesView(this->DependentValues, // subset DependentValues
|
|
firstSupernode, // start at firstSupernode
|
|
numSupernodesToProcess); // until lastSuperNode (not inclued)
|
|
// Target array
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
valuePrefixSumView(this->ValuePrefixSum, // subset ValuePrefixSum
|
|
firstSupernode, // start at firstSupernode
|
|
numSupernodesToProcess); // until lastSuperNode (not inclued)
|
|
// Compute the partial sum for DependentValues[firstSuperNode, lastSupernode) and write to ValuePrefixSum[firstSuperNode, lastSupernode)
|
|
vtkm::cont::Algorithm::ScanInclusive(dependentValuesView, // input
|
|
valuePrefixSumView); // result of partial sum
|
|
}
|
|
|
|
// Since the prefix sum is over *all* supernodes in the iteration, we need to break it into segments
|
|
// There are two cases we have to worry about:
|
|
// a. Hyperarcs made up of multiple supernodes
|
|
// b. Attachment points (which don't have a corresponding hyperarc)
|
|
// and they can be mixed in any given iteration
|
|
|
|
// Since we have the prefix sum in a separate array, we avoid read/write conflicts
|
|
|
|
// 3. Compute the segmented weights from the prefix sum array
|
|
{
|
|
// Create views of the subranges of the arrays we need to update
|
|
vtkm::cont::ArrayHandleCounting<vtkm::Id> supernodeIndex(
|
|
firstSupernode, vtkm::Id{ 1 }, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
hierarchicalTreeSuperarcsView(
|
|
this->HierarchicalTree.Superarcs, firstSupernode, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
hierarchicalTreeHyperparentsView(
|
|
this->HierarchicalTree.Hyperparents, firstSupernode, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
hierarchicalTreeHypernodesView(
|
|
this->HierarchicalTree.Hypernodes, firstSupernode, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
dependentValuesView(this->DependentValues, firstSupernode, numSupernodesToProcess);
|
|
// create the worklet
|
|
vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::
|
|
ComputeSuperarcDependentWeightsWorklet<SweepValueType>
|
|
computeSuperarcDependentWeightsWorklet(
|
|
firstSupernode, round, this->HierarchicalTree.NumRounds);
|
|
// Execute the worklet
|
|
this->Invoke(
|
|
computeSuperarcDependentWeightsWorklet, // the worklet
|
|
supernodeIndex, // input counting index [firstSupernode, lastSupernode)
|
|
hierarchicalTreeSuperarcsView, // input view of hierarchicalTree.Superarcs[firstSupernode, lastSupernode)
|
|
hierarchicalTreeHyperparentsView, // input view of hierarchicalTree.Hyperparents[firstSupernode, lastSupernode)
|
|
this->HierarchicalTree.Hypernodes, // input full hierarchicalTree.Hypernodes array
|
|
this->ValuePrefixSum, // input full ValuePrefixSum array
|
|
dependentValuesView // output view of sweepValues[firstSu
|
|
);
|
|
}
|
|
} // ComputeSuperarcDependentWeights()
|
|
|
|
|
|
// routine to compute the weights to transfer to superarcs (defined by the round & iteration)
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::ComputeSuperarcTransferWeights(
|
|
vtkm::Id round,
|
|
vtkm::Id, // iteration, // Kept parameter in case we need it for debugging.
|
|
vtkm::Id firstSupernode,
|
|
vtkm::Id lastSupernode)
|
|
{ // ComputeSuperarcTransferWeights()
|
|
// At this stage, we would otherwise transfer weights by hyperarc, but attachment points don't *have* hyperarcs
|
|
// so we will do a transfer by superarc instead, making sure that we only transfer from the last superarc in each
|
|
// hyperarc, plus for any attachment point
|
|
vtkm::Id numSupernodesToProcess = lastSupernode - firstSupernode;
|
|
|
|
// 4. Set the amount each superarc wants to transfer, reusing the valuePrefixSum array for the purpose
|
|
// and the transfer target
|
|
{ // scope ComputeSuperarcTransferWeightsWorklet to make sure temp variables are cleared
|
|
// Create ArrayHandleViews of the subrange of values that we need to update
|
|
vtkm::cont::ArrayHandleCounting<vtkm::Id> supernodeIndex(
|
|
firstSupernode, vtkm::Id{ 1 }, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
hierarchicalTreeSupernodesView(
|
|
this->HierarchicalTree.Supernodes, firstSupernode, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
hierarchicalTreeSuperarcsView(
|
|
this->HierarchicalTree.Superarcs, firstSupernode, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
transferTargetView(this->TransferTarget, firstSupernode, numSupernodesToProcess);
|
|
// instantiate the worklet
|
|
vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::
|
|
ComputeSuperarcTransferWeightsWorklet computeSuperarcTransferWeightsWorklet(
|
|
round, this->HierarchicalTree.NumRounds, lastSupernode);
|
|
// call the worklet
|
|
this->Invoke(
|
|
computeSuperarcTransferWeightsWorklet, // worklet
|
|
supernodeIndex, // input counting array [firstSupernode, lastSupernode)
|
|
hierarchicalTreeSupernodesView, // input view of hierarchicalTree.supernodes[firstSupernode, lastSupernode)
|
|
this->HierarchicalTree.Superparents, // input whole array of hierarchicalTree.superparents
|
|
this->HierarchicalTree.Hyperparents, // input whole array of hierarchicalTree.hyperparents
|
|
hierarchicalTreeSuperarcsView, // input/output view of hierarchicalTree.superarcs[firstSupernode, lastSupernode)
|
|
transferTargetView // input view of transferTarget[firstSupernode, lastSupernode)
|
|
);
|
|
} // scope ComputeSuperarcTransferWeightsWorklet
|
|
|
|
// 5. Now we need to sort the transfer targets into contiguous segments
|
|
{
|
|
// create view of superSortPermute[firstSupernode, lastSupernode) for sorting
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
superSortPermuteView(this->SuperSortPermute, firstSupernode, numSupernodesToProcess);
|
|
// create comperator for the sort
|
|
vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::TransferTargetComperator
|
|
transferTargetComperator(this->TransferTarget);
|
|
// sort the subrange of our array
|
|
vtkm::cont::Algorithm::Sort(superSortPermuteView, transferTargetComperator);
|
|
}
|
|
|
|
// 6. The [first,last] subrange is now permuted, so we can copy the transfer targets and weights into arrays
|
|
// The following code block implements the following for loop using fancy array handles and copy
|
|
// for (vtkm::Id supernode = firstSupernode; supernode < lastSupernode; supernode++)
|
|
// {
|
|
// sortedTransferTarget[supernode] = transferTarget[superSortPermute[supernode]];
|
|
// valuePrefixSum[supernode] = sweepValues[superSortPermute[supernode]];
|
|
// }
|
|
{
|
|
// copy transfer target in the sorted order
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
sortedTransferTargetView(this->SortedTransferTarget, firstSupernode, numSupernodesToProcess);
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
superSortPermuteView(this->SuperSortPermute, firstSupernode, numSupernodesToProcess);
|
|
auto permutedTransferTarget =
|
|
vtkm::cont::make_ArrayHandlePermutation(superSortPermuteView, // idArray
|
|
this->TransferTarget); // valueArray
|
|
vtkm::cont::Algorithm::Copy(permutedTransferTarget, sortedTransferTargetView);
|
|
// Note that any values associated with NO_SUCH_ELEMENT will be ignored
|
|
// copy transfer weight in the sorted order
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
valuePrefixSumView(this->ValuePrefixSum, firstSupernode, numSupernodesToProcess);
|
|
auto permutedDependentValues =
|
|
vtkm::cont::make_ArrayHandlePermutation(superSortPermuteView, // idArray
|
|
this->DependentValues); // valueArray
|
|
vtkm::cont::Algorithm::Copy(permutedDependentValues, valuePrefixSumView);
|
|
}
|
|
} // ComputeSuperarcTransferWeights()
|
|
|
|
|
|
// routine to transfer the weights
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::TransferWeights(
|
|
vtkm::Id, // round, // Kept parameters in case we need it for debugging.
|
|
vtkm::Id, // iteration, // Kept parameters in case we need it for debugging.
|
|
vtkm::Id firstSupernode,
|
|
vtkm::Id lastSupernode)
|
|
{ // TransferWeights()
|
|
// 7. Now perform a segmented prefix sum
|
|
vtkm::Id numSupernodesToProcess = lastSupernode - firstSupernode;
|
|
// Same as std::partial_sum(valuePrefixSum.begin() + firstSupernode, valuePrefixSum.begin() + lastSupernode, valuePrefixSum.begin() + firstSupernode);
|
|
{
|
|
// ValuePrefixSum[firstSuperNode, lastSupernode)
|
|
vtkm::cont::ArrayHandleView<vtkm::worklet::contourtree_augmented::IdArrayType>
|
|
valuePrefixSumView(this->ValuePrefixSum, // subset ValuePrefixSum
|
|
firstSupernode, // start at firstSupernode
|
|
numSupernodesToProcess); // until lastSuperNode (not inclued)
|
|
// TODO: If it is safe to use the same array as input and output for ScanInclusive then this code should be updated to avoid the extra copy
|
|
// In this case our traget array is the same as our source array. For safety we
|
|
// store the values of our prefix sum in a temporary arrya and then copy the values
|
|
// back into our valuePrefixSumView at the end
|
|
vtkm::worklet::contourtree_augmented::IdArrayType tempScanInclusiveTarget;
|
|
tempScanInclusiveTarget.Allocate(numSupernodesToProcess);
|
|
// Compute the partial sum for DependentValues[firstSuperNode, lastSupernode) and write to ValuePrefixSum[firstSuperNode, lastSupernode)
|
|
vtkm::cont::Algorithm::ScanInclusive(valuePrefixSumView, // input
|
|
tempScanInclusiveTarget); // result of partial sum
|
|
// Now copy the values from our prefix sum back
|
|
vtkm::cont::Algorithm::Copy(tempScanInclusiveTarget, valuePrefixSumView);
|
|
}
|
|
|
|
// 7a. and 7b.
|
|
{
|
|
// 7a. Find the RHE of each group and transfer the prefix sum weight
|
|
// Note that we do not compute the transfer weight separately, we add it in place instead
|
|
// Instantiate the worklet
|
|
auto supernodeIndex =
|
|
vtkm::cont::make_ArrayHandleCounting(firstSupernode, vtkm::Id{ 1 }, numSupernodesToProcess);
|
|
VTKM_ASSERT(firstSupernode + numSupernodesToProcess <=
|
|
this->ValuePrefixSum.GetNumberOfValues());
|
|
auto valuePrefixSumView = vtkm::cont::make_ArrayHandleView(
|
|
this->ValuePrefixSum, firstSupernode, numSupernodesToProcess);
|
|
|
|
vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::
|
|
TransferWeightsUpdateRHEWorklet transferWeightsUpdateRHEWorklet(lastSupernode);
|
|
// Invoke the worklet
|
|
this->Invoke(transferWeightsUpdateRHEWorklet, // worklet
|
|
supernodeIndex, // input counting array [firstSupernode, lastSupernode)
|
|
this->SortedTransferTarget,
|
|
valuePrefixSumView, // input view of valuePrefixSum[firstSupernode, lastSupernode)
|
|
this->DependentValues);
|
|
}
|
|
|
|
{
|
|
VTKM_ASSERT(firstSupernode + 1 + numSupernodesToProcess - 1 <=
|
|
this->SortedTransferTarget.GetNumberOfValues());
|
|
auto sortedTransferTargetView = vtkm::cont::make_ArrayHandleView(
|
|
this->SortedTransferTarget, firstSupernode + 1, numSupernodesToProcess - 1);
|
|
VTKM_ASSERT(firstSupernode + 1 + numSupernodesToProcess - 1 <=
|
|
this->SortedTransferTarget.GetNumberOfValues());
|
|
auto sortedTransferTargetShiftedView = vtkm::cont::make_ArrayHandleView(
|
|
this->SortedTransferTarget, firstSupernode, numSupernodesToProcess - 1);
|
|
auto valuePrefixSumPreviousValueView = vtkm::cont::make_ArrayHandleView(
|
|
this->ValuePrefixSum, firstSupernode, numSupernodesToProcess - 1);
|
|
|
|
// 7b. Now find the LHE of each group and subtract out the prior weight
|
|
vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper::
|
|
TransferWeightsUpdateLHEWorklet transferWeightsUpdateLHEWorklet;
|
|
this->Invoke(transferWeightsUpdateLHEWorklet,
|
|
sortedTransferTargetView,
|
|
sortedTransferTargetShiftedView,
|
|
valuePrefixSumPreviousValueView,
|
|
this->DependentValues);
|
|
}
|
|
} // TransferWeights()
|
|
|
|
|
|
// debug routine
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
std::string HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::DebugPrint(
|
|
std::string message,
|
|
const char* fileName,
|
|
long lineNum) const
|
|
{ // DebugPrint()
|
|
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 << message << std::endl;
|
|
resultStream << "Hypersweep Value Array Contains: " << std::endl;
|
|
resultStream << "----------------------------------------" << std::endl;
|
|
resultStream << std::endl;
|
|
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->DependentValues.GetNumberOfValues(),
|
|
resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Intrinsic", this->IntrinsicValues, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Dependent", this->DependentValues, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Prefix Sum", this->ValuePrefixSum, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Transfer To", this->TransferTarget, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Sorted Transfer", this->SortedTransferTarget, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Sort Permute", this->SuperSortPermute, -1, resultStream);
|
|
return resultStream.str();
|
|
} // DebugPrint()
|
|
|
|
|
|
// Routine to save the hierarchical tree to file
|
|
template <typename SweepValueType, typename ContourTreeFieldType>
|
|
void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::SaveHierarchicalContourTreeDot(
|
|
std::string message,
|
|
const char* outFileName) const
|
|
{ // SaveHierarchicalContourTreeDot()
|
|
std::string hierarchicalTreeDotString =
|
|
HierarchicalContourTreeDotGraphPrint<vtkm::worklet::contourtree_augmented::IdArrayType>(
|
|
message,
|
|
this->HierarchicalTree,
|
|
SHOW_SUPER_STRUCTURE | SHOW_HYPER_STRUCTURE | SHOW_ALL_IDS | SHOW_ALL_SUPERIDS |
|
|
SHOW_ALL_HYPERIDS | SHOW_EXTRA_DATA, //|GV_NODE_NAME_USES_GLOBAL_ID
|
|
this->BlockId,
|
|
this->DependentValues);
|
|
std::ofstream hierarchicalTreeFile(outFileName);
|
|
hierarchicalTreeFile << hierarchicalTreeDotString;
|
|
} // SaveHierarchicalContourTreeDot
|
|
|
|
|
|
} // namespace contourtree_distributed
|
|
} // namespace worklet
|
|
} // namespace vtkm
|
|
|
|
#endif
|