Updated TreeGrafter for parallel hypersweep

This commit is contained in:
Oliver Ruebel 2021-03-02 02:15:41 -08:00 committed by Gunther H. Weber
parent 76d88db157
commit 7506068236
3 changed files with 159 additions and 20 deletions

@ -66,6 +66,7 @@
#include <vtkm/worklet/contourtree_augmented/ContourTree.h>
#include <vtkm/worklet/contourtree_distributed/HierarchicalContourTree.h>
#include <vtkm/worklet/contourtree_distributed/InteriorForest.h>
#include <vtkm/worklet/contourtree_distributed/tree_grafter/CalculateAttachementCounterWorklet.h>
#include <vtkm/worklet/contourtree_distributed/tree_grafter/CollapseRegularChainsWorklet.h>
#include <vtkm/worklet/contourtree_distributed/tree_grafter/CopyFirstHypernodePerIterationWorklet.h>
#include <vtkm/worklet/contourtree_distributed/tree_grafter/CopyFirstSupernodePerIterationWorklet.h>
@ -354,7 +355,18 @@ void TreeGrafter<MeshType, FieldType>::GraftInteriorForests(
// count the number of iterations
this->NumTransferIterations = 0;
// Now loop to transfer one iteration at a time
// There are several cases we need to handle properly
// 1. We could have a round with no superarcs to add (in which case we are
// guaranteed not to have attachment points)
// 2. We could have a round with some superarcs but no attachment points
// (because we attach to existing supernodes)
// 3. We could have a round with attachment points to add
// Attachment points are interior, so are never added to the active superarc
// list in the first place. This means that we need to have an extra round
// some of the time to transfer attachment points. So the logic is:
// first we transfer all active superarcs, then we test (somehow) for having
// attachment points to transfer
// Loop to transfer active superarcs with a variation of the PPP transfer phase
// We stop when all that is left are attachment points (which aren't included in the active list)
while (this->ActiveSuperarcs.GetNumberOfValues() > 0)
{ // loop to transfer
@ -381,18 +393,34 @@ void TreeGrafter<MeshType, FieldType>::GraftInteriorForests(
DebugPrint("Finished Transfer Iterations", __FILE__, __LINE__));
#endif
// Now set the transfer iteration for all attachment points
// If there were no supernodes to transfer, their types are all NO_SUCH_ELEMENT
auto setTransferIterationWorklet = vtkm::worklet::contourtree_distributed::tree_grafter::
GraftInteriorForestsSetTransferIterationWorklet(this->NumTransferIterations);
this->Invoke(setTransferIterationWorklet,
this->SupernodeType, // input
this->HierarchicalSuperId, // input
this->WhenTransferred // output
// At this point, we can check to see whether all supernodes in the residue have already been transferred
// length of the attachementCounter will be set to (this->ContourTree->Supernodes.GetNumberOfValue());
// as a result of the worklet
vtkm::worklet::contourtree_augmented::IdArrayType attachmentCounter;
vtkm::worklet::contourtree_distributed::tree_grafter::CalculateAttachementCounterWorklet
calculateAttachementCounterWorklet;
this->Invoke(calculateAttachementCounterWorklet, // worklet
this->SupernodeType, // input
this->HierarchicalSuperId, // input
attachmentCounter // output
);
vtkm::Id numAttachmentPoints = vtkm::cont::Algorithm::Reduce(attachmentCounter, 0, vtkm::Sum());
// and increment the number of iterations
this->NumTransferIterations++;
// if there are any at all, we need an extra iteration
if (numAttachmentPoints > 0)
{ // attachment points needing transfer
// Now set the transfer iteration for all attachment points
// If there were no supernodes to transfer, their types are all NO_SUCH_ELEMENT
auto setTransferIterationWorklet = vtkm::worklet::contourtree_distributed::tree_grafter::
GraftInteriorForestsSetTransferIterationWorklet(this->NumTransferIterations);
this->Invoke(setTransferIterationWorklet,
this->SupernodeType, // input
this->HierarchicalSuperId, // input
this->WhenTransferred // output
);
// and increment the number of iterations
this->NumTransferIterations++;
} // attachment points needing transfer
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
@ -1397,8 +1425,8 @@ void TreeGrafter<MeshType, FieldType>::CopyIterationDetails(
this->NewSupernodes.GetNumberOfValues());
hierarchicalTree.NumHypernodesInRound.WritePortal().Set(theRound,
this->NewHypernodes.GetNumberOfValues());
// the -1 is because the last iteration is just setting attachment points
hierarchicalTree.NumIterations.WritePortal().Set(theRound, this->NumTransferIterations - 1);
// last iteration is just setting attachment points (but we are including this now) (previously added -1)
hierarchicalTree.NumIterations.WritePortal().Set(theRound, this->NumTransferIterations);
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
@ -1424,7 +1452,7 @@ void TreeGrafter<MeshType, FieldType>::CopyIterationDetails(
// and set the per round iteration counts. There may be smarter ways of doing this, but . . .
this->ResizeVector<vtkm::Id>(
hierarchicalTree.FirstSupernodePerIteration[static_cast<std::size_t>(theRound)],
this->NumTransferIterations,
this->NumTransferIterations + 1,
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
{
auto copyFirstSupernodePerIterationWorklet =
@ -1445,11 +1473,16 @@ void TreeGrafter<MeshType, FieldType>::CopyIterationDetails(
hierarchicalTree.DebugPrint("Supernode Iteration Counts Set", __FILE__, __LINE__));
#endif
// Initalize hierarchicalTree.FirstHypernodePerIteration with NO_SUCH_ELEMENT
// we add one so we don't need special cases when establishing subranges
// There's a tricky case to be dealt with due to attachment points - the last (extra) iteration transfers supernodes
// with a "virtual" superarc but no hyperarc. This can only occur in the final iteration, in which case the correct value is
// the "off the end" sentinel. But it is also possible for there to be no attachment points, in which case the final iteration
// will have some other value. Also, we need to set the "off the end" for the extra entry in any event.
// THEREFORE, instead of instantiating to NO_SUCH_ELEMENT for safety, we instantiate to the hypernodes.size()
this->ResizeVector<vtkm::Id>(
hierarchicalTree.FirstHypernodePerIteration[static_cast<std::size_t>(theRound)],
this->NumTransferIterations,
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
this->NumTransferIterations + 1,
hierarchicalTree.Hypernodes.GetNumberOfValues());
// copy the approbriat hierarchicalTree.FirstHypernodePerIteration values
{
auto copyFirstHypernodePerIterationWorklet =
@ -1466,9 +1499,9 @@ void TreeGrafter<MeshType, FieldType>::CopyIterationDetails(
);
}
// force the extra one to be one-off-the end for safety
hierarchicalTree.FirstHypernodePerIteration[static_cast<size_t>(theRound)].WritePortal().Set(
this->NumTransferIterations - 1, hierarchicalTree.Hypernodes.GetNumberOfValues());
// force the extra one to be one-off-the end for safety; REMOVED - SEE ABOVE FOR LOGIC
//hierarchicalTree.FirstHypernodePerIteration[static_cast<size_t>(theRound)].WritePortal().Set(
// this->NumTransferIterations, hierarchicalTree.Hypernodes.GetNumberOfValues());
#ifdef DEBUG_PRINT
VTKM_LOG_S(vtkm::cont::LogLevel::Info,

@ -32,6 +32,7 @@ set(headers
CopyNewNodesSetSuperparentsWorklet.h
CopyFirstSupernodePerIterationWorklet.h
CopyFirstHypernodePerIterationWorklet.h
CalculateAttachementCounterWorklet.h
)
vtkm_declare_headers(${headers})

@ -0,0 +1,105 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
// Copyright (c) 2018, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals
// from the U.S. Dept. of Energy). All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// (3) Neither the name of the University of California, Lawrence Berkeley National
// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//=============================================================================
//
// This code is an extension of the algorithm presented in the paper:
// Parallel Peak Pruning for Scalable SMP Contour Tree Computation.
// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens.
// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization
// (LDAV), October 2016, Baltimore, Maryland.
//
// The PPP2 algorithm and software were jointly developed by
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
#ifndef vtk_m_worklet_contourtree_distributed_tree_grafter_calculate_attachement_counter_worklet_h
#define vtk_m_worklet_contourtree_distributed_tree_grafter_calculate_attachement_counter_worklet_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace tree_grafter
{
// In TreeGrafter.GraftInteriorForests to compute attachmentCounter to
// check whether additional attachement points need to be transferred
class CalculateAttachementCounterWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn supernodeType, // input
FieldIn hierarchicalSuperId, // input
FieldOut attachmentCounter // output
);
using ExecutionSignature = _3(_1, _2);
using InputDomain = _1;
// Default Constructor
VTKM_EXEC_CONT
CalculateAttachementCounterWorklet() {}
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& supernodeType,
const vtkm::Id& hierarchicalSuperId) const
{ // operator ()
return (supernodeType == vtkm::worklet::contourtree_augmented::IS_ATTACHMENT) &&
vtkm::worklet::contourtree_augmented::NoSuchElement(hierarchicalSuperId);
// In serial this worklet implements the following operation
/*
for (indexType supernode = 0; supernode < contourTree->supernodes.size(); supernode++)
attachmentCounter[supernode] = (supernodeType[supernode] == IS_ATTACHMENT) && noSuchElement(hierarchicalSuperID[supernode]);
*/
} // operator ()
}; // CalculateAttachementCounterWorklet
} // namespace tree_grafter
} // namespace contourtree_distributed
} // namespace worklet
} // namespace vtkm
#endif