mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-19 18:45:43 +00:00
45a3570270
Fix bad array resize in HierarchicalAugmenter to do true resize to preserve values and initalize arrays, not just allocate Fix bad array resize in HierarchicalAugmenter::RetrieveInAttachmentPoints to do a true resize to preserve values and initalize arrays, not just allocate
1488 lines
80 KiB
C++
1488 lines
80 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
|
|
//
|
|
// HierarchicalAugmenter.h
|
|
//
|
|
//=======================================================================================
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// In order to compute geometric measures properly, we probably need to have all supernodes
|
|
// inserted rather than the lazy insertion implicit in the existing computation. After discussion,
|
|
// we have decided to make this a post-processing step in order to keep our options open.
|
|
//
|
|
// Fortunately, the HierarchicalContourTree structure will hold a tree augmented with lower level
|
|
// supernodes, so we just want a factory class that takes a HCT as input and produces another one
|
|
// as output. Note that the output will no longer have insertions to be made, as all subtrees will
|
|
// be rooted at a supernode in the parent level.
|
|
//
|
|
// Since this is blockwise, we will have the main loop external (as with the HierarchicalHyperSweeper)
|
|
// and have it invoke subroutines here
|
|
//
|
|
// The processing will be based on a fanin with partners as usual
|
|
// I. Each block swaps all attachment points for the level with its partner
|
|
// II. Fanning-in builds sets of all attachment points to insert into each superarc except the base level
|
|
// III.At the end of the fanin, we know the complete set of all supernodes to be inserted in all superarcs,
|
|
// so we insert them all at once & renumber. We should not need to do so in a fan-out
|
|
//
|
|
// After some prototyping in Excel, the test we will need to apply is the following:
|
|
//
|
|
// In round N, we transfer all attachment points whose round is < N+1 and whose superparent round is >= N+1
|
|
// (NB: In excel, I started with Round 1, when I should have started with round 0 to keep the swap-partner correct)
|
|
//
|
|
// The superparent round is the round at which the attachment point will be inserted at the end, so the
|
|
// attachment point needs to be shared at all levels up to and including that round, hence the second branch
|
|
// of the test.
|
|
//
|
|
// The first branch is because the attachment points at round N are already represented in the partner
|
|
// due to the construction of the hierarchical contour tree. Therefore transferring them is redundant and
|
|
// complicates processing, so we omit them. For higher levels, they will need to be inserted.
|
|
//
|
|
// This test is independent of things such as sort order, so we can keep our arrays in unsorted order.
|
|
// We may wish to revisit this later to enforce a canonical order for validation / verification
|
|
// but as long as we are consistent, we should in fact have a canonical ordering on each block.
|
|
//
|
|
//=======================================================================================
|
|
|
|
#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_h
|
|
#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_h
|
|
|
|
#include <iomanip>
|
|
#include <string>
|
|
#include <vtkm/worklet/contourtree_augmented/PrintVectors.h>
|
|
#include <vtkm/worklet/contourtree_augmented/Types.h>
|
|
#include <vtkm/worklet/contourtree_distributed/PrintGraph.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/AttachmentAndSupernodeComparator.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/AttachmentSuperparentAndIndexComparator.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/CopyBaseRegularStructureWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/HierarchicalAugmenterInOutData.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/IsAscendingDecorator.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/IsAttachementPointNeededPredicate.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/IsAttachementPointPredicate.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/NotNoSuchElementPredicate.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/ResizeArraysBuildNewSupernodeIdsWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/SetFirstAttachmentPointInRoundWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/SetSuperparentSetDecorator.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/UpdateHyperstructureSetHyperarcsAndNodesWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_augmenter/UpdateHyperstructureSetSuperchildrenWorklet.h>
|
|
#include <vtkm/worklet/contourtree_distributed/hierarchical_contour_tree/PermuteComparator.h>
|
|
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace worklet
|
|
{
|
|
namespace contourtree_distributed
|
|
{
|
|
|
|
/// Facture class for augmenting the hierarchical contour tree to enable computations of measures, e.g., volumne
|
|
template <typename FieldType>
|
|
class HierarchicalAugmenter
|
|
{ // class HierarchicalAugmenter
|
|
public:
|
|
/// the tree that it hypersweeps over
|
|
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* BaseTree;
|
|
/// the tree that it is building
|
|
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* AugmentedTree;
|
|
|
|
/// the ID of the base block (used for debug output)
|
|
vtkm::Id BlockId;
|
|
|
|
/// arrays storing the details for the attachment points & old supernodes the Id in the global data set
|
|
vtkm::worklet::contourtree_augmented::IdArrayType GlobalRegularIds;
|
|
|
|
/// the data value
|
|
vtkm::cont::ArrayHandle<FieldType> DataValues;
|
|
|
|
/// the supernode index
|
|
/// when we swap attachment points, we will set this to NO_SUCH_ELEMENT because the supernodes
|
|
/// added are on a different block, so their original supernodeId becomes invalid
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SupernodeIds;
|
|
|
|
/// the superarc will *ALWAYS* be -1 for a true attachment point, so we don't store it
|
|
/// instead, the superparent stores the Id for the arc it inserts into
|
|
/// WARNING: in order for sorting to work, we will need to carry forward the ascending / descending flag
|
|
/// This flag is normally stored on the superarc, but will be stored in this class on the superparent
|
|
vtkm::worklet::contourtree_augmented::IdArrayType Superparents;
|
|
|
|
/// we want to track the round on which the superparent is transferred (we could look it up, but it's
|
|
/// more convenient to have it here). Also, we don't need the iteration.
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SuperparentRounds;
|
|
|
|
/// we also want to track the round on which the attachment point was originally transferred
|
|
vtkm::worklet::contourtree_augmented::IdArrayType WhichRounds;
|
|
|
|
/// if we're not careful, we'll have read-write conflicts when swapping with the partner
|
|
/// there are other solutions, but the simpler solution is to have a transfer buffer for
|
|
/// the set we want to send - which means another set of parallel arrays
|
|
/// Output arrays used in DIY exchange
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::HierarchicalAugmenterInOutData<
|
|
FieldType>
|
|
OutData;
|
|
/// Output arrays used in DIY exchange
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::HierarchicalAugmenterInOutData<
|
|
FieldType>
|
|
InData;
|
|
|
|
/// these are essentially temporary local variables, but are placed here to make the DebugPrint()
|
|
/// more comprehensive. They will be allocated where used
|
|
/// this one makes a list of attachment Ids and is used in sevral different places, so resize it when done
|
|
vtkm::worklet::contourtree_augmented::IdArrayType AttachmentIds;
|
|
/// tracks segments of attachment points by round
|
|
vtkm::worklet::contourtree_augmented::IdArrayType FirstAttachmentPointInRound;
|
|
/// maps from old supernode Id to new supernode Id
|
|
vtkm::worklet::contourtree_augmented::IdArrayType NewSupernodeIds;
|
|
/// tracks which supernodes are kept in a given round
|
|
vtkm::worklet::contourtree_augmented::IdArrayType KeptSupernodes;
|
|
/// sorting array & arrays for data details
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SupernodeSorter;
|
|
vtkm::worklet::contourtree_augmented::IdArrayType GlobalRegularIdSet;
|
|
vtkm::cont::ArrayHandle<FieldType> DataValueSet;
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SuperparentSet;
|
|
vtkm::worklet::contourtree_augmented::IdArrayType SupernodeIdSet;
|
|
/// data for transferring regular nodes
|
|
vtkm::worklet::contourtree_augmented::IdArrayType RegularSuperparents;
|
|
vtkm::worklet::contourtree_augmented::IdArrayType RegularNodesNeeded;
|
|
|
|
/// empty constructor
|
|
HierarchicalAugmenter() {}
|
|
|
|
/// initializer (called explicitly after constructor)
|
|
void Initialize(
|
|
vtkm::Id blockId,
|
|
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inBaseTree,
|
|
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inAugmentedTree);
|
|
|
|
/// routine to prepare the set of attachment points to transfer
|
|
void PrepareOutAttachmentPoints(vtkm::Id round);
|
|
|
|
/// routine to retrieve partner's current list of attachment points
|
|
void RetrieveInAttachmentPoints();
|
|
|
|
/// routine to release memory used for swap arrays
|
|
void ReleaseSwapArrays();
|
|
|
|
/// routine to reconstruct a hierarchical tree using the augmenting supernodes
|
|
void BuildAugmentedTree();
|
|
|
|
// subroutines for BuildAugmentedTree
|
|
/// initial preparation
|
|
void PrepareAugmentedTree();
|
|
/// transfer of hyperstructure but not superchildren count
|
|
void CopyHyperstructure();
|
|
/// transfer level of superstructure with insertions
|
|
void CopySuperstructure();
|
|
/// reset the super Ids in the hyperstructure to the new values
|
|
void UpdateHyperstructure();
|
|
/// copy the remaining base level regular nodes
|
|
void CopyBaseRegularStructure();
|
|
|
|
// subroutines for CopySuperstructure
|
|
/// gets a list of all the old supernodes to transfer at this level (ie except attachment points
|
|
void RetrieveOldSupernodes(vtkm::Id roundNumber);
|
|
/// resizes the arrays for the level
|
|
void ResizeArrays(vtkm::Id roundNumber);
|
|
/// adds a round full of superarcs (and regular nodes) to the tree
|
|
void CreateSuperarcs(vtkm::Id roundNumber);
|
|
|
|
/// debug routine
|
|
std::string DebugPrint(std::string message, const char* fileName, long lineNum);
|
|
|
|
private:
|
|
/// Used internally to Invoke worklets
|
|
vtkm::cont::Invoker Invoke;
|
|
|
|
}; // class HierarchicalAugmenter
|
|
|
|
|
|
|
|
// initalizating function
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::Initialize(
|
|
vtkm::Id blockId,
|
|
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* baseTree,
|
|
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* augmentedTree)
|
|
{ // Initialize()
|
|
// copy the parameters for use
|
|
this->BlockId = blockId;
|
|
this->BaseTree = baseTree;
|
|
this->AugmentedTree = augmentedTree;
|
|
|
|
// now construct a list of all attachment points on the block
|
|
// to do this, we construct an index array with all supernode ID's that satisfy:
|
|
// 1. superparent == NO_SUCH_ELEMENT (i.e. root of interior tree)
|
|
// 2. round < nRounds (except the top level, where 1. indicates the tree root)
|
|
// initalize AttachementIds
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::IsAttachementPointPredicate
|
|
isAttachementPointPredicate(
|
|
this->BaseTree->Superarcs, this->BaseTree->WhichRound, this->BaseTree->NumRounds);
|
|
auto tempSupernodeIndex =
|
|
vtkm::cont::ArrayHandleIndex(this->BaseTree->Supernodes.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::CopyIf(
|
|
// first a list of all of the supernodes
|
|
tempSupernodeIndex,
|
|
// then our stencil
|
|
tempSupernodeIndex,
|
|
// And the CopyIf compress the supernodes array to eliminate the non-attachement points and
|
|
// save to this->AttachmentIds
|
|
this->AttachmentIds,
|
|
// then our predicate identifies all attachment points
|
|
// i.e., an attachment point is defined by having no superarc (NO_SUCH_ELEMENT) and not
|
|
// being in the final round (where this indicates the global root) defined by the condition
|
|
// if (noSuchElement(baseTree->superarcs[supernode]) && (baseTree->whichRound[supernode] < baseTree->nRounds))
|
|
isAttachementPointPredicate);
|
|
}
|
|
|
|
// we now resize the working arrays
|
|
this->GlobalRegularIds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->DataValues.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->SupernodeIds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->Superparents.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->SuperparentRounds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->WhichRounds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
|
|
// and do an indexed copy (permutation) to copy in the attachment point information
|
|
{
|
|
auto hierarchicalRegularIds =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->BaseTree->Supernodes);
|
|
auto superparents =
|
|
vtkm::cont::make_ArrayHandlePermutation(hierarchicalRegularIds, this->BaseTree->Superparents);
|
|
// globalRegularIDs[attachmentPoint] = baseTree->regularNodeGlobalIDs[hierarchicalRegularID];
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::make_ArrayHandlePermutation(
|
|
hierarchicalRegularIds, BaseTree->RegularNodeGlobalIds),
|
|
this->GlobalRegularIds);
|
|
//dataValues[attachmentPoint] = baseTree->dataValues [hierarchicalRegularID];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(hierarchicalRegularIds, BaseTree->DataValues),
|
|
this->DataValues);
|
|
//supernodeIDs[attachmentPoint] = supernodeID;
|
|
vtkm::cont::Algorithm::Copy(this->AttachmentIds, // these are our supernodeIds
|
|
this->SupernodeIds);
|
|
//superparentRounds[attachmentPoint] = baseTree->whichRound [superparent];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(superparents, this->BaseTree->WhichRound),
|
|
this->SuperparentRounds);
|
|
//whichRounds[attachmentPoint] = baseTree->whichRound[supernodeID];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->BaseTree->WhichRound),
|
|
this->WhichRounds);
|
|
|
|
// get the ascending flag from the superparent's superarc and transfer to the superparent
|
|
// Array decorator to add the IS_ASCENDING flag to our superparent, i.e.,
|
|
// if (isAscending(baseTree->superarcs[superparent])){ superparent |= IS_ASCENDING; }
|
|
// TODO: When using the superparents ArrayHandlePermutation in the ArrayHandleDecorator the compiler
|
|
// has issues discovering the StorageType when calling Copy(isAscendingSuperparentArr, superparents)
|
|
// by copying superparents to an actual array in tempArrSuperparents we can avoid this issue,
|
|
// at the cost of an extra copy.
|
|
vtkm::worklet::contourtree_augmented::IdArrayType tempArrSuperparents;
|
|
vtkm::cont::Algorithm::Copy(superparents, tempArrSuperparents);
|
|
auto isAscendingSuperparentArr = vtkm::cont::make_ArrayHandleDecorator(
|
|
tempArrSuperparents.GetNumberOfValues(), //superparents.GetNumberOfValues(),
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::IsAscendingDecorator{},
|
|
tempArrSuperparents, //superparents,
|
|
this->BaseTree->Superarcs);
|
|
vtkm::cont::Algorithm::Copy(isAscendingSuperparentArr, this->Superparents);
|
|
}
|
|
|
|
// clean up memory
|
|
this->AttachmentIds.ReleaseResources();
|
|
} // Initialize()
|
|
|
|
|
|
|
|
// routine to prepare the set of attachment points to transfer
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::PrepareOutAttachmentPoints(vtkm::Id round)
|
|
{ // PrepareOutAttachmentPoints()
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
IsAttachementPointNeededPredicate isAttachementPointNeededPredicate(
|
|
this->SuperparentRounds, this->WhichRounds, round);
|
|
auto tempAttachmentPointsIndex =
|
|
vtkm::cont::ArrayHandleIndex(this->GlobalRegularIds.GetNumberOfValues());
|
|
vtkm::cont::Algorithm::CopyIf(
|
|
// 1. fancy array of all of the attachment points of which parts are copied to this->AttachmentIds
|
|
tempAttachmentPointsIndex, // input
|
|
// 2. stencil used with the predicate to decide which AttachementIds to keep
|
|
tempAttachmentPointsIndex, // stencil
|
|
// 3. CopyIf compress the supernodes array to eliminate the non-attachement
|
|
// points and save to this->AttachmentIds
|
|
this->AttachmentIds,
|
|
// 4. the unary predicate uses the stencil to identify all attachment points needed
|
|
isAttachementPointNeededPredicate);
|
|
}
|
|
|
|
// 4. resize the out array
|
|
this->OutData.GlobalRegularIds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->OutData.DataValues.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->OutData.SupernodeIds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->OutData.Superparents.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->OutData.SuperparentRounds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
this->OutData.WhichRounds.Allocate(this->AttachmentIds.GetNumberOfValues());
|
|
|
|
// 5. copy the points we want
|
|
{
|
|
// outGlobalRegularIDs[outAttachmentPoint] = globalRegularIDs[attachmentPoint];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->GlobalRegularIds),
|
|
this->OutData.GlobalRegularIds);
|
|
// outDataValues[outAttachmentPoint] = dataValues[attachmentPoint];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->DataValues),
|
|
this->OutData.DataValues);
|
|
// outSupernodeIDs[outAttachmentPoint] = supernodeIDs[attachmentPoint];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->SupernodeIds),
|
|
this->OutData.SupernodeIds);
|
|
// outSuperparents[outAttachmentPoint] = superparents[attachmentPoint];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->Superparents),
|
|
this->OutData.Superparents);
|
|
// outSuperparentRounds[outAttachmentPoint] = superparentRounds[attachmentPoint];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->SuperparentRounds),
|
|
this->OutData.SuperparentRounds);
|
|
// outWhichRounds[outAttachmentPoint] = whichRounds[attachmentPoint];
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::make_ArrayHandlePermutation(this->AttachmentIds, this->WhichRounds),
|
|
this->OutData.WhichRounds);
|
|
}
|
|
|
|
// clean up memory
|
|
this->AttachmentIds.ReleaseResources();
|
|
} // PrepareOutAttachmentPoints()
|
|
|
|
|
|
// routine to add partner's current list of attachment points
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::RetrieveInAttachmentPoints()
|
|
{ // RetrieveInAttachmentPoints()
|
|
// what we want to do here is to copy all of the partner's attachments for the round into our own buffer
|
|
// this code will be replaced in the MPI with a suitable transmit / receive
|
|
// store the current size
|
|
vtkm::Id numAttachmentsCurrently = this->GlobalRegularIds.GetNumberOfValues();
|
|
vtkm::Id numIncomingAttachments = InData.GlobalRegularIds.GetNumberOfValues();
|
|
vtkm::Id numTotalAttachments = numAttachmentsCurrently + numIncomingAttachments;
|
|
|
|
// I. resize the existing arrays
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->GlobalRegularIds, numTotalAttachments, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<FieldType>(
|
|
this->DataValues, numTotalAttachments, static_cast<FieldType>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->SupernodeIds, numTotalAttachments, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->Superparents, numTotalAttachments, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->SuperparentRounds, numTotalAttachments, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->WhichRounds, numTotalAttachments, static_cast<vtkm::Id>(0));
|
|
|
|
/*this->GlobalRegularIds.Allocate(numTotalAttachments);
|
|
this->DataValues.Allocate(numTotalAttachments);
|
|
this->SupernodeIds.Allocate(numTotalAttachments);
|
|
this->Superparents.Allocate(numTotalAttachments);
|
|
this->SuperparentRounds.Allocate(numTotalAttachments);
|
|
this->WhichRounds.Allocate(numTotalAttachments);*/
|
|
|
|
// II. copy the additional points into them
|
|
{
|
|
// The following sequence of copy operations implements the following for from the orginal code
|
|
// for (vtkm::Id inAttachmentPoint = 0; inAttachmentPoint < inGlobalRegularIDs.size(); inAttachmentPoint++)
|
|
// globalRegularIDs[attachmentPoint] = inGlobalRegularIDs[outAttachmentPoint];
|
|
auto tempGlobalRegularIdsView = vtkm::cont::make_ArrayHandleView(
|
|
this->GlobalRegularIds, numAttachmentsCurrently, numIncomingAttachments);
|
|
vtkm::cont::Algorithm::Copy(InData.GlobalRegularIds, tempGlobalRegularIdsView);
|
|
// dataValues[attachmentPoint] = inDataValues[outAttachmentPoint];
|
|
auto tempDataValuesView = vtkm::cont::make_ArrayHandleView(
|
|
this->DataValues, numAttachmentsCurrently, numIncomingAttachments);
|
|
vtkm::cont::Algorithm::Copy(InData.DataValues, tempDataValuesView);
|
|
// supernodeIDs[attachmentPoint] = NO_SUCH_ELEMENT;
|
|
auto tempNoSuchElementArr = vtkm::cont::make_ArrayHandleConstant(
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT, numIncomingAttachments);
|
|
auto tempSupernodeIdsView = vtkm::cont::make_ArrayHandleView(
|
|
this->SupernodeIds, numAttachmentsCurrently, numIncomingAttachments);
|
|
vtkm::cont::Algorithm::Copy(tempNoSuchElementArr, tempSupernodeIdsView);
|
|
// superparents[attachmentPoint] = inSuperparents[outAttachmentPoint];
|
|
auto tempSuperparentsView = vtkm::cont::make_ArrayHandleView(
|
|
this->Superparents, numAttachmentsCurrently, numIncomingAttachments);
|
|
vtkm::cont::Algorithm::Copy(InData.Superparents, tempSuperparentsView);
|
|
// superparentRounds[attachmentPoint] = inSuperparentRounds[outAttachmentPoint];
|
|
auto tempSuperparentRoundsView = vtkm::cont::make_ArrayHandleView(
|
|
this->SuperparentRounds, numAttachmentsCurrently, numIncomingAttachments);
|
|
vtkm::cont::Algorithm::Copy(InData.SuperparentRounds, tempSuperparentRoundsView);
|
|
// whichRounds[attachmentPoint] = inWhichRounds[outAttachmentPoint];
|
|
auto tempWhichRoundsView = vtkm::cont::make_ArrayHandleView(
|
|
this->WhichRounds, numAttachmentsCurrently, numIncomingAttachments);
|
|
vtkm::cont::Algorithm::Copy(InData.WhichRounds, tempWhichRoundsView);
|
|
}
|
|
} // RetrieveInAttachmentPoints()
|
|
|
|
|
|
// routine to release memory used for out arrays
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::ReleaseSwapArrays()
|
|
{ // ReleaseSwapArrays()
|
|
this->OutData.ReleaseResources();
|
|
this->InData.ReleaseResources();
|
|
} // ReleaseSwapArrays()
|
|
|
|
|
|
// routine to reconstruct a hierarchical tree using the augmenting supernodes
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::BuildAugmentedTree()
|
|
{ // BuildAugmentedTree()
|
|
// 1. Prepare the data structures for filling in, copying in basic information & organising the attachment points
|
|
this->PrepareAugmentedTree();
|
|
// 2. Copy the hyperstructure, using the old super IDs for now
|
|
this->CopyHyperstructure();
|
|
// 3. Copy the superstructure, inserting additional points as we do
|
|
this->CopySuperstructure();
|
|
// 4. Update the hyperstructure to use the new super IDs
|
|
this->UpdateHyperstructure();
|
|
// 5. Copy the remaining regular structure at the bottom level, setting up the regular sort order in the process
|
|
this->CopyBaseRegularStructure();
|
|
} // BuildAugmentedTree()
|
|
|
|
|
|
// initial preparation
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::PrepareAugmentedTree()
|
|
{ // PrepareAugmentedTree()
|
|
// 1. Sort attachment points on superparent round, with secondary sort on global index so duplicates appear next to each other
|
|
// This can (and does) happen when a vertex on the boundary is an attachment point separately for multiple blocks
|
|
// We add a tertiary sort on supernode ID so that on each block, it gets the correct "home" supernode ID for reconciliation
|
|
//: note that we use a standard comparator that tie breaks with index. This separates into
|
|
// segments with identical superparent round, which is all we need for now
|
|
std::cout << this->DebugPrint("PrepareAugmentedTree 1", __FILE__, __LINE__) << std::endl;
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleIndex(this->GlobalRegularIds.GetNumberOfValues()), this->AttachmentIds);
|
|
// 1a. We now need to suppress duplicates,
|
|
{
|
|
// Sort the attachement Ids
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
AttachmentSuperparentAndIndexComparator attachmentSuperparentAndIndexComparator(
|
|
this->SuperparentRounds, this->GlobalRegularIds, this->SupernodeIds);
|
|
vtkm::cont::Algorithm::Sort(this->AttachmentIds, attachmentSuperparentAndIndexComparator);
|
|
// Remove the duplicate values
|
|
vtkm::cont::Algorithm::Unique(this->AttachmentIds);
|
|
}
|
|
|
|
std::cout << this->DebugPrint("PrepareAugmentedTree 2", __FILE__, __LINE__) << std::endl;
|
|
|
|
// 2. Set up array with bounds for subsegments
|
|
// We do +2 because the top level is extra, and we need an extra sentinel value at the end
|
|
// We initialise to NO_SUCH_ELEMENT because we may have rounds with none and we'll need to clean up serially (over the number of rounds, i.e. lg n)
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id>(vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT,
|
|
this->BaseTree->NumRounds + 2),
|
|
this->FirstAttachmentPointInRound);
|
|
|
|
// Now do a parallel set operation
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
SetFirstAttachmentPointInRoundWorklet setFirstAttachmentPointInRoundWorklet;
|
|
this->Invoke(setFirstAttachmentPointInRoundWorklet,
|
|
this->AttachmentIds, // input
|
|
this->SuperparentRounds, // input
|
|
this->FirstAttachmentPointInRound);
|
|
}
|
|
// The last element in the array is always set to the size as a sentinel value
|
|
// We need to pull the firstAttachmentPointInRound array to the control environment
|
|
// anyways for the loop afterwards so can do this set here without using Copy
|
|
// Use regular WritePortal here since we need to update a number of values and the array should be small
|
|
auto firstAttachmentPointInRoundPortal = this->FirstAttachmentPointInRound.WritePortal();
|
|
firstAttachmentPointInRoundPortal.Set(this->BaseTree->NumRounds + 1,
|
|
this->AttachmentIds.GetNumberOfValues());
|
|
|
|
std::cout << this->DebugPrint("PrepareAugmentedTree 3", __FILE__, __LINE__) << std::endl;
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
this->DebugPrint("First Attachment Point Set Where Possible", __FILE__, __LINE__));
|
|
#endif
|
|
// Now clean up by looping through the rounds (serially - this is logarithmic at worst)
|
|
// We loop backwards so that the next up propagates downwards
|
|
// WARNING: DO NOT PARALLELISE THIS LOOP
|
|
for (vtkm::Id roundNumber = this->BaseTree->NumRounds; roundNumber >= 0; roundNumber--)
|
|
{ // per round
|
|
// if it still holds NSE, there are none in this round, so use the next one up
|
|
if (vtkm::worklet::contourtree_augmented::NoSuchElement(
|
|
firstAttachmentPointInRoundPortal.Get(roundNumber)))
|
|
{
|
|
firstAttachmentPointInRoundPortal.Set(roundNumber,
|
|
firstAttachmentPointInRoundPortal.Get(roundNumber + 1));
|
|
}
|
|
} // per round
|
|
|
|
std::cout << this->DebugPrint("PrepareAugmentedTree 4", __FILE__, __LINE__) << std::endl;
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Subsegments Identified", __FILE__, __LINE__));
|
|
#endif
|
|
// 3. Initialise an array to keep track of the mapping from old supernode ID to new supernode ID
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id>(vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT,
|
|
this->BaseTree->Supernodes.GetNumberOfValues()),
|
|
this->NewSupernodeIds);
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
this->DebugPrint("Augmented Tree Prepared", __FILE__, __LINE__));
|
|
|
|
#endif
|
|
|
|
std::cout << this->DebugPrint("PrepareAugmentedTree 5", __FILE__, __LINE__) << std::endl;
|
|
|
|
} // PrepareAugmentedTree()
|
|
|
|
|
|
// transfer of hyperstructure but not superchildren count
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::CopyHyperstructure()
|
|
{ // CopyHyperstructure()
|
|
// we can also resize some of the additional information
|
|
this->AugmentedTree->NumRounds = this->BaseTree->NumRounds;
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id>(
|
|
static_cast<vtkm::Id>(0), this->BaseTree->NumRegularNodesInRound.GetNumberOfValues()),
|
|
this->AugmentedTree->NumRegularNodesInRound);
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id>(
|
|
static_cast<vtkm::Id>(0), this->BaseTree->NumSupernodesInRound.GetNumberOfValues()),
|
|
this->AugmentedTree->NumSupernodesInRound);
|
|
|
|
// this chunk needs to be here to prevent the HierarchicalContourTree::DebugPrint() routine from crashing
|
|
this->AugmentedTree->FirstSupernodePerIteration.resize(
|
|
this->BaseTree->FirstSupernodePerIteration.size());
|
|
// this loop doesn't need to be parallelised, as it is a small size: we will fill in values later
|
|
for (vtkm::Id roundNumber = 0;
|
|
roundNumber < static_cast<vtkm::Id>(this->AugmentedTree->FirstSupernodePerIteration.size());
|
|
roundNumber++)
|
|
{
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id>(
|
|
static_cast<vtkm::Id>(0),
|
|
this->BaseTree->FirstSupernodePerIteration[roundNumber].GetNumberOfValues()),
|
|
this->AugmentedTree->FirstSupernodePerIteration[roundNumber]);
|
|
}
|
|
|
|
// hyperstructure is unchanged, so we can copy it
|
|
vtkm::cont::Algorithm::Copy(this->BaseTree->NumHypernodesInRound,
|
|
this->AugmentedTree->NumHypernodesInRound);
|
|
vtkm::cont::Algorithm::Copy(this->BaseTree->NumIterations, this->AugmentedTree->NumIterations);
|
|
this->AugmentedTree->FirstHypernodePerIteration.resize(
|
|
this->BaseTree->FirstHypernodePerIteration.size());
|
|
// this loop doesn't need to be parallelised, as it is a small size
|
|
for (vtkm::Id roundNumber = 0;
|
|
roundNumber < static_cast<vtkm::Id>(this->AugmentedTree->FirstHypernodePerIteration.size());
|
|
roundNumber++)
|
|
{ // per round
|
|
// duplicate the existing array
|
|
vtkm::cont::Algorithm::Copy(this->BaseTree->FirstHypernodePerIteration[roundNumber],
|
|
this->AugmentedTree->FirstHypernodePerIteration[roundNumber]);
|
|
} // per round
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Hyperstructure Copied", __FILE__, __LINE__));
|
|
#endif
|
|
} // CopyHyperstructure()
|
|
|
|
|
|
// transfer level of superstructure with insertions
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::CopySuperstructure()
|
|
{ // CopySuperstructure()
|
|
|
|
// Loop from the top down:
|
|
for (vtkm::Id roundNumber = this->BaseTree->NumRounds; roundNumber >= 0; roundNumber--)
|
|
{ // per round
|
|
// start by retrieving list of old supernodes from the tree (except for attachment points)
|
|
this->RetrieveOldSupernodes(roundNumber);
|
|
// since we know the number of attachment points, we can now allocate space for the level
|
|
// and set up arrays for sorting the supernodes
|
|
this->ResizeArrays(roundNumber);
|
|
// now we create the superarcs for the round in the new tree
|
|
this->CreateSuperarcs(roundNumber);
|
|
} // per round
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
this->DebugPrint("Superstructure Copied", __FILE__, __LINE__));
|
|
#endif
|
|
} // CopySuperstructure()
|
|
|
|
|
|
// reset the super IDs in the hyperstructure to the new values
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::UpdateHyperstructure()
|
|
{ // UpdateHyperstructure()
|
|
|
|
// 5. Reset hypernodes, hyperarcs and superchildren using supernode IDs
|
|
// The hyperstructure is unchanged, but uses old supernode IDs
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
UpdateHyperstructureSetHyperarcsAndNodesWorklet
|
|
updateHyperstructureSetHyperarcsAndNodesWorklet;
|
|
this->Invoke(
|
|
updateHyperstructureSetHyperarcsAndNodesWorklet,
|
|
this->BaseTree->Hypernodes, // input
|
|
this->BaseTree->Hyperarcs, // input
|
|
this->NewSupernodeIds, // input
|
|
this->AugmentedTree->Hypernodes, // output (the array is automatically resized here)
|
|
this->AugmentedTree->Hyperarcs // output (the array is automatically resized here)
|
|
);
|
|
}
|
|
|
|
// finally, find the number of superchildren as the delta between the
|
|
// super ID and the next hypernode's super ID
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
UpdateHyperstructureSetSuperchildrenWorklet updateHyperstructureSetSuperchildrenWorklet(
|
|
this->AugmentedTree->Supernodes.GetNumberOfValues());
|
|
this->Invoke(
|
|
updateHyperstructureSetSuperchildrenWorklet,
|
|
this->AugmentedTree->Hypernodes, // input
|
|
this->AugmentedTree->Superchildren // output (the array is automatically resized here)
|
|
);
|
|
}
|
|
} // UpdateHyperstructure()
|
|
|
|
|
|
// copy the remaining base level regular nodes
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::CopyBaseRegularStructure()
|
|
{ // CopyBaseRegularStructure()
|
|
// 6. Set up the regular node sorter for the final phase
|
|
vtkm::cont::Algorithm::Copy(
|
|
vtkm::cont::ArrayHandleIndex(this->AugmentedTree->RegularNodeGlobalIds.GetNumberOfValues()),
|
|
this->AugmentedTree->RegularNodeSortOrder);
|
|
{
|
|
vtkm::worklet::contourtree_distributed::PermuteComparator // hierarchical_contour_tree::
|
|
permuteComparator(this->AugmentedTree->RegularNodeGlobalIds);
|
|
vtkm::cont::Algorithm::Sort(this->AugmentedTree->RegularNodeSortOrder, permuteComparator);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint("Regular Node Sorter Sorted", __FILE__, __LINE__));
|
|
#endif
|
|
|
|
// 7. Cleanup at level = 0. The principal task here is to insert all of the regular
|
|
// nodes in the original block into the regular arrays. The problem is that we
|
|
// haven't got a canonical list of them since in the original hierarchical tree,
|
|
// they might have been passed upwards as part of the boundary resolution. We
|
|
// now have a choice: we can take all "unfiled" regular nodes in the original hierarchical
|
|
// tree, or we can return to the block of data. The difference here is that the
|
|
// "unfiled" regular nodes can include nodes from other blocks which were passed up
|
|
// and retained in both partners. On the other hand, we already have the list of
|
|
// unfiled regular nodes, so the overhead for using them is not huge. And if we
|
|
// return to the block, we will need to pass it in as a parameter and template
|
|
// on mesh type. So, for the purposes of tidy coding, I shall use the first
|
|
// option, which means that not all of the Level 0 regular nodes belong to the block.
|
|
{
|
|
// For each regular node, if it hasn't been transferred to the new tree, search for the superarc to which it belongs
|
|
// default the superparent to NO_SUCH_ELEMENT to use as a flag for "we can ignore it"
|
|
// now loop, finding the superparent for each node needed and set the approbriate value or set to
|
|
// NO_SUCH_ELEMENT if not needed. The worklet also automatically sized our arrays
|
|
// temporary array so we can stream compact (aka CopyIf) afterwards
|
|
vtkm::worklet::contourtree_augmented::IdArrayType tempRegularNodesNeeded;
|
|
// create the worklet
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet;
|
|
// Get a FindRegularByGlobal and FindSuperArcForUnknownNode execution object for our worklet
|
|
auto findRegularByGlobal = this->AugmentedTree->GetFindRegularByGlobal();
|
|
auto findSuperArcForUnknownNode = this->AugmentedTree->GetFindSuperArcForUnknownNode();
|
|
|
|
// excute the worklet
|
|
this->Invoke(findSuperparentForNecessaryNodesWorklet, // the worklet to call
|
|
// inputs
|
|
this->BaseTree->RegularNodeGlobalIds, // input domain
|
|
this->BaseTree->Superparents, // input
|
|
this->BaseTree->DataValues, // input
|
|
this->BaseTree->Superarcs, // input
|
|
this->NewSupernodeIds, // input
|
|
// Execution objects from the AugmentedTree
|
|
findRegularByGlobal,
|
|
findSuperArcForUnknownNode,
|
|
// Output arrays to populate
|
|
this->RegularSuperparents, // output
|
|
tempRegularNodesNeeded // output. will be CopyIf'd to this->RegularNodesNeeded
|
|
);
|
|
// We now compress to get the set of nodes to transfer. I.e., remove all
|
|
// NO_SUCH_ELEMENT entires and copy the values to keep to our proper arrays
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::NotNoSuchElementPredicate
|
|
notNoSuchElementPredicate;
|
|
vtkm::cont::Algorithm::CopyIf(
|
|
tempRegularNodesNeeded, // input data
|
|
tempRegularNodesNeeded, // stencil (same as input)
|
|
this->RegularNodesNeeded, // target array (will be resized)
|
|
notNoSuchElementPredicate // predicate returning true of element is NOT NO_SUCH_ELEMENT
|
|
);
|
|
}
|
|
|
|
// resize the regular arrays to fit
|
|
vtkm::Id numRegNeeded = this->RegularNodesNeeded.GetNumberOfValues();
|
|
vtkm::Id numExistingRegular = this->AugmentedTree->RegularNodeGlobalIds.GetNumberOfValues();
|
|
vtkm::Id numTotalRegular = numExistingRegular + numRegNeeded;
|
|
{
|
|
// Resize the array, while preserving the orginial values and initalizing new values
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->AugmentedTree->RegularNodeGlobalIds, numTotalRegular, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<FieldType>(
|
|
this->AugmentedTree->DataValues, numTotalRegular, static_cast<FieldType>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->AugmentedTree->RegularNodeSortOrder, numTotalRegular, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->AugmentedTree->Regular2Supernode,
|
|
numTotalRegular,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->AugmentedTree->Superparents, numTotalRegular, static_cast<vtkm::Id>(0));
|
|
}
|
|
|
|
// OK: we have a complete list of the nodes to transfer. Since we make no guarantees (yet) about sorting, they just copy across
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::CopyBaseRegularStructureWorklet
|
|
copyBaseRegularStructureWorklet(numExistingRegular);
|
|
// NOTE: We require the input arrays (aside form the input domain) to be permutted by the
|
|
// regularNodesNeeded input domain so that we can use FieldIn instead of WholeArrayIn
|
|
// NOTE: We require ArrayHandleView for the output arrays of the range [numExistingRegular:end] so
|
|
// that we can use FieldOut instead of requiring WholeArrayInOut
|
|
// input domain for the worklet of [0, regularNodesNeeded.GetNumberOfValues()]
|
|
auto regularNodesNeededRange =
|
|
vtkm::cont::ArrayHandleIndex(this->RegularNodesNeeded.GetNumberOfValues());
|
|
// input baseTree->regularNodeGlobalIds permuted by regularNodesNeeded
|
|
auto baseTreeRegularNodeGlobalIdsPermuted = vtkm::cont::make_ArrayHandlePermutation(
|
|
this->RegularNodesNeeded, this->BaseTree->RegularNodeGlobalIds);
|
|
// input baseTree->dataValues permuted by regularNodesNeeded
|
|
auto baseTreeDataValuesPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->RegularNodesNeeded, this->BaseTree->DataValues);
|
|
// input regularSuperparents permuted by regularNodesNeeded
|
|
auto regularSuperparentsPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->RegularNodesNeeded, this->RegularSuperparents);
|
|
// input view of augmentedTree->regularNodeGlobalIds[numExistingRegular:]
|
|
auto augmentedTreeRegularNodeGlobalIdsView =
|
|
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->RegularNodeGlobalIds,
|
|
numExistingRegular, // start writing at numExistingRegular
|
|
numRegNeeded); // fill until the end
|
|
// input view of augmentedTree->dataValues[numExistingRegular:]
|
|
auto augmentedTreeDataValuesView =
|
|
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->DataValues,
|
|
numExistingRegular, // start writing at numExistingRegular
|
|
numRegNeeded); // fill until the end
|
|
// input view of augmentedTree->superparents[numExistingRegular:]
|
|
auto augmentedTreeSuperparentsView =
|
|
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Superparents,
|
|
numExistingRegular, // start writing at numExistingRegular
|
|
numRegNeeded); // fill until the end
|
|
// input view of augmentedTree->regularNodeSortOrder[numExistingRegular:]
|
|
auto augmentedTreeRegularNodeSortOrderView =
|
|
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->RegularNodeSortOrder,
|
|
numExistingRegular, // start writing at numExistingRegular
|
|
numRegNeeded); // fill until the end
|
|
this->Invoke(copyBaseRegularStructureWorklet, // worklet to call
|
|
regularNodesNeededRange, // input domain
|
|
baseTreeRegularNodeGlobalIdsPermuted, // input
|
|
baseTreeDataValuesPermuted, // input
|
|
regularSuperparentsPermuted, // input
|
|
augmentedTreeRegularNodeGlobalIdsView, // output
|
|
augmentedTreeDataValuesView, // output
|
|
augmentedTreeSuperparentsView, // output
|
|
augmentedTreeRegularNodeSortOrderView // output
|
|
);
|
|
}
|
|
|
|
// Finally, we resort the regular node sort order
|
|
{
|
|
vtkm::worklet::contourtree_distributed::PermuteComparator // hierarchical_contour_tree::
|
|
permuteComparator(this->AugmentedTree->RegularNodeGlobalIds);
|
|
vtkm::cont::Algorithm::Sort(this->AugmentedTree->RegularNodeSortOrder, permuteComparator);
|
|
}
|
|
} // CopyBaseRegularStructure()
|
|
|
|
|
|
// subroutines for CopySuperstructure
|
|
// gets a list of all the old supernodes to transfer at this level (ie except attachment points
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::RetrieveOldSupernodes(vtkm::Id roundNumber)
|
|
{ // RetrieveOldSupernodes()
|
|
// a. Transfer supernodes from same level of old tree minus attachment points, storing by global regular ID not regular ID
|
|
// Use compression to get the set of supernode IDs that we want to keep
|
|
// TODO PERFORMANCE STATISTICS:
|
|
// the # of supernodes at each level minus the # of kept supernodes gives us the # of attachment points we lose at this level
|
|
// in addition to this, the firstAttachmentPointInRound array gives us the # of attachment points we gain at this level
|
|
vtkm::Id supernodeIndexBase =
|
|
vtkm::cont::ArrayGetValue(0, this->BaseTree->FirstSupernodePerIteration[roundNumber]);
|
|
vtkm::cont::ArrayHandleCounting<vtkm::Id> supernodeIdVals(
|
|
supernodeIndexBase, // start
|
|
1, // step
|
|
this->BaseTree->NumSupernodesInRound.ReadPortal().Get(roundNumber));
|
|
// the test for whether to keep it is:
|
|
// a1. at the top level, keep everything
|
|
if (!(roundNumber < this->BaseTree->NumRounds))
|
|
{
|
|
vtkm::cont::Algorithm::Copy(supernodeIdVals, this->KeptSupernodes);
|
|
}
|
|
// a2. at lower levels, keep them if the superarc is NO_SUCH_ELEMENT
|
|
else
|
|
{
|
|
// Reset this-KeptSupernodes to the right size and initalize with NO_SUCH_ELEMENT.
|
|
// TODO: Check if a simple free and allocate without initalizing the array is sufficient
|
|
vtkm::cont::Algorithm::Copy(
|
|
// Create const array to copy
|
|
vtkm::cont::ArrayHandleConstant<vtkm::Id>(
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT,
|
|
this->BaseTree->NumSupernodesInRound.ReadPortal().Get(roundNumber)),
|
|
// target array
|
|
this->KeptSupernodes);
|
|
// Create the predicate for the CopyIf
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::NotNoSuchElementPredicate
|
|
notNoSuchElementPredicate;
|
|
// Copy supernodeId to this->KeptSupernodes
|
|
vtkm::cont::Algorithm::CopyIf(
|
|
// first we generate a list of supernodeIds
|
|
supernodeIdVals,
|
|
// Stencil with baseTree->superarcs[supernodeID]
|
|
vtkm::cont::make_ArrayHandleView(
|
|
this->BaseTree->Superarcs, supernodeIndexBase, this->KeptSupernodes.GetNumberOfValues()),
|
|
// And the CopyIf compresses the array to eliminate unnecssary elements
|
|
// save to this->KeptSupernodes
|
|
this->KeptSupernodes,
|
|
// then our predicate identifies all necessary points. These
|
|
// are all points that suffice the condition
|
|
// vtkm::Id supernodeID = keptSupernode + supernodeIndexBase;
|
|
// !noSuchElement(baseTree->superarcs[supernodeID]);
|
|
notNoSuchElementPredicate);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) +
|
|
std::string(" Old Supernodes Retrieved"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
} // RetrieveOldSupernodes()
|
|
|
|
|
|
// resizes the arrays for the level
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::ResizeArrays(vtkm::Id roundNumber)
|
|
{ // ResizeArrays()
|
|
// at this point, we know how many supernodes are kept from the same level of the old tree
|
|
// we can also find out how many supernodes are being inserted, which gives us the correct amount to expand by, saving a double resize() call
|
|
// note that some of these arrays could probably be resized later, but it's cleaner this way
|
|
// also note that if it becomes a problem, we could resize all of the arrays to baseTree->supernodes.size() + # of attachmentPoints as an over-estimate
|
|
// then cut them down at the end. The code would however be even messier if we did so
|
|
vtkm::Id numSupernodesAlready = this->AugmentedTree->Supernodes.GetNumberOfValues();
|
|
vtkm::Id numInsertedSupernodes =
|
|
vtkm::cont::ArrayGetValue(roundNumber + 1, this->FirstAttachmentPointInRound) -
|
|
vtkm::cont::ArrayGetValue(roundNumber, this->FirstAttachmentPointInRound);
|
|
vtkm::Id numSupernodesThisLevel =
|
|
numInsertedSupernodes + this->KeptSupernodes.GetNumberOfValues();
|
|
vtkm::Id newSupernodeCount = numSupernodesAlready + numSupernodesThisLevel;
|
|
|
|
// conveniently, the value numSupernodesThisLevel is the number of supernodes *!AND!* regular nodes to store for the round
|
|
vtkm::worklet::contourtree_augmented::IdArraySetValue(
|
|
roundNumber, numSupernodesThisLevel, this->AugmentedTree->NumRegularNodesInRound);
|
|
vtkm::worklet::contourtree_augmented::IdArraySetValue(
|
|
roundNumber, numSupernodesThisLevel, this->AugmentedTree->NumSupernodesInRound);
|
|
vtkm::worklet::contourtree_augmented::IdArraySetValue(
|
|
0, numSupernodesAlready, this->AugmentedTree->FirstSupernodePerIteration[roundNumber]);
|
|
|
|
// resize the arrays accordingly.
|
|
// NOTE: We have to resize arrays (not just allocate them) as we need to
|
|
// expand the arrays while preserving the original values
|
|
{
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Supernodes,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Supernodes,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Superarcs,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Hyperparents,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Super2Hypernode,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->WhichRound,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->WhichIteration,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
|
|
// we also know that only supernodes are needed as regular nodes at each level, so we resize those here as well
|
|
// we note that it might be possible to update all regular IDs at the end, but leave that optimisation out for now
|
|
// therefore we resize the regular-sized arrays as well
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->RegularNodeGlobalIds,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<FieldType>(
|
|
this->AugmentedTree->DataValues, newSupernodeCount, static_cast<FieldType>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Regular2Supernode,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
vtkm::worklet::contourtree_augmented::ResizeVector(
|
|
this->AugmentedTree->Superparents,
|
|
newSupernodeCount,
|
|
vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(
|
|
vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Arrays Resized"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// The next task is to assemble a sorting array which we will use to construct the new superarcs, containing both the
|
|
// kept supernodes and the attachment points. The attachment points are easier since we already have them, so we start
|
|
// by allocating space and copying them in: this means another set of arrays for the individual elements. However, we do not
|
|
// need all of the data elements, since superparentRound is fixed (and equal to roundNumber inside this loop), and whichRound will be reset
|
|
vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(numSupernodesThisLevel),
|
|
this->SupernodeSorter);
|
|
{
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->GlobalRegularIdSet, numSupernodesThisLevel, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<FieldType>(
|
|
this->DataValueSet, numSupernodesThisLevel, static_cast<FieldType>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->SuperparentSet, numSupernodesThisLevel, static_cast<vtkm::Id>(0));
|
|
vtkm::worklet::contourtree_augmented::ResizeVector<vtkm::Id>(
|
|
this->SupernodeIdSet, numSupernodesThisLevel, static_cast<vtkm::Id>(0));
|
|
}
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) +
|
|
std::string(" Sorter Set Resized"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// b. Transfer attachment points for level into new supernode array
|
|
// to copy them in, we use the existing array of attachment point IDs by round
|
|
{
|
|
vtkm::Id firstAttachmentPointInRoundCurrent =
|
|
vtkm::cont::ArrayGetValue(roundNumber, this->FirstAttachmentPointInRound);
|
|
vtkm::Id firstAttachmentPointInRoundNext =
|
|
vtkm::cont::ArrayGetValue(roundNumber + 1, this->FirstAttachmentPointInRound);
|
|
vtkm::Id currRange = firstAttachmentPointInRoundNext - firstAttachmentPointInRoundCurrent;
|
|
auto attachmentPointIdView =
|
|
vtkm::cont::make_ArrayHandleView(this->AttachmentIds, // array to subset
|
|
firstAttachmentPointInRoundCurrent, // start index
|
|
currRange); // count
|
|
// Permute the source arrays for the copy
|
|
auto globalRegularIdsPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(attachmentPointIdView, // index array
|
|
this->GlobalRegularIds // value array
|
|
);
|
|
auto dataValuesPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(attachmentPointIdView, this->DataValues);
|
|
auto superparentsPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(attachmentPointIdView, this->Superparents);
|
|
auto supernodeIdsPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(attachmentPointIdView, this->SupernodeIds);
|
|
// Now use CopySubRange to copy the values into the right places. This allows
|
|
// us to place them in the right place and avoid shrinking the array on Copy
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
// copy all values of our permutted array
|
|
globalRegularIdsPermuted,
|
|
0,
|
|
globalRegularIdsPermuted.GetNumberOfValues(),
|
|
// copy target
|
|
this->GlobalRegularIdSet,
|
|
0);
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
dataValuesPermuted, 0, dataValuesPermuted.GetNumberOfValues(), this->DataValueSet, 0);
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
superparentsPermuted, 0, superparentsPermuted.GetNumberOfValues(), this->SuperparentSet, 0);
|
|
vtkm::cont::Algorithm::CopySubRange(
|
|
supernodeIdsPermuted, 0, supernodeIdsPermuted.GetNumberOfValues(), this->SupernodeIdSet, 0);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) +
|
|
std::string(" Attachment Points Transferred"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// Now we copy in the kept supernodes
|
|
{
|
|
auto oldRegularIdArr =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->KeptSupernodes, // index
|
|
this->BaseTree->Supernodes); // values
|
|
// Permute the source arrays for the copy
|
|
auto baseTreeregularNodeGlobalIdsPermuted = vtkm::cont::make_ArrayHandlePermutation(
|
|
oldRegularIdArr, this->BaseTree->RegularNodeGlobalIds);
|
|
auto baseTreeDataValuesPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(oldRegularIdArr, this->BaseTree->DataValues);
|
|
|
|
// Now use CopySubRange to copy the values into the right places. This allows
|
|
// us to place them in the right place and avoids shrinking the array on Copy
|
|
vtkm::cont::Algorithm::CopySubRange(baseTreeregularNodeGlobalIdsPermuted,
|
|
0,
|
|
baseTreeregularNodeGlobalIdsPermuted.GetNumberOfValues(),
|
|
this->GlobalRegularIdSet,
|
|
numInsertedSupernodes);
|
|
vtkm::cont::Algorithm::CopySubRange(baseTreeDataValuesPermuted,
|
|
0,
|
|
baseTreeDataValuesPermuted.GetNumberOfValues(),
|
|
this->DataValueSet,
|
|
numInsertedSupernodes);
|
|
vtkm::cont::Algorithm::CopySubRange(this->KeptSupernodes,
|
|
0,
|
|
this->KeptSupernodes.GetNumberOfValues(),
|
|
this->SupernodeIdSet,
|
|
numInsertedSupernodes);
|
|
// For this->SuperparentSet we need to set values to
|
|
// superparentSet[supernodeSetID] = oldSupernodeID | (isAscending(baseTree->superarcs[oldSupernodeID]) ? IS_ASCENDING: 0x00);
|
|
// so we use an ArrayHanldeDecorator instead to compute the values and copy them in place
|
|
auto setSuperparentSetArrayDecorator = vtkm::cont::make_ArrayHandleDecorator(
|
|
this->KeptSupernodes.GetNumberOfValues(),
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::SetSuperparentSetDecorator{},
|
|
this->KeptSupernodes,
|
|
this->BaseTree->Superarcs);
|
|
vtkm::cont::Algorithm::CopySubRange(setSuperparentSetArrayDecorator,
|
|
0,
|
|
this->KeptSupernodes.GetNumberOfValues(),
|
|
this->SuperparentSet,
|
|
numInsertedSupernodes);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) +
|
|
std::string(" Kept Supernodes Transferred"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// c. Create a permutation array and sort supernode segment by a. superparent, b. value, d. global index to establish segments (reversing as needed)
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
AttachmentAndSupernodeComparator<FieldType>
|
|
attachmentAndSupernodeComparator(
|
|
this->SuperparentSet, this->DataValueSet, this->GlobalRegularIdSet);
|
|
vtkm::cont::Algorithm::Sort(this->SupernodeSorter, attachmentAndSupernodeComparator);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) +
|
|
std::string(" Sorter Set Sorted"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
|
|
// d. Build the inverse permutation array for lookup purposes:
|
|
{
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
|
|
ResizeArraysBuildNewSupernodeIdsWorklet resizeArraysBuildNewSupernodeIdsWorklet(
|
|
numSupernodesAlready);
|
|
auto supernodeIndex = vtkm::cont::ArrayHandleIndex(this->SupernodeSorter.GetNumberOfValues());
|
|
auto supernodeIdSetPermuted =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SupernodeIdSet);
|
|
this->Invoke(
|
|
resizeArraysBuildNewSupernodeIdsWorklet,
|
|
supernodeIndex, // input domain. We only need the index because supernodeIdSetPermuted already does the permute
|
|
supernodeIdSetPermuted, // input input supernodeIDSet permuted by supernodeSorter to allow for FieldIn
|
|
this
|
|
->NewSupernodeIds // output/input (both are necessary since not all values will be overwritten)
|
|
);
|
|
}
|
|
|
|
#ifdef DEBUG_PRINT
|
|
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
|
|
DebugPrint(std::string("Round ") + std::to_string(roundNumber) +
|
|
std::string(" Sorting Arrays Built"),
|
|
__FILE__,
|
|
__LINE__));
|
|
#endif
|
|
} // ResizeArrays()
|
|
|
|
|
|
// adds a round full of superarcs (and regular nodes) to the tree
|
|
template <typename FieldType>
|
|
void HierarchicalAugmenter<FieldType>::CreateSuperarcs(vtkm::Id roundNumber)
|
|
{ // CreateSuperarcs()
|
|
// retrieve the ID number of the first supernode at this level
|
|
vtkm::Id numSupernodesAlready =
|
|
vtkm::cont::ArrayGetValue(0, this->AugmentedTree->FirstSupernodePerIteration[roundNumber]);
|
|
|
|
// e. Connect superarcs for the level & set hyperparents & superchildren count, whichRound, whichIteration, super2hypernode
|
|
{ // START scope for e. to delete temporary variables
|
|
// Note: The original PPP algorithm performed all operations listed in this block
|
|
// in a single parralel for loop. Many of those operations were smart array
|
|
// copies. So to simplfy the worklet and to make more effective use of
|
|
// VTKm algorithm, a large number of copy operations have been extracted from
|
|
// the loop and are performed here via combinations of fancy array handles and
|
|
// vtkm::cont::Algorithm::Copy operations.
|
|
|
|
// Define the new supernode and regular Id. Both are actually the same here since we are
|
|
// augmenting the tree here, but for clarity we define them as separate variables.
|
|
// At all levels above 0, we used to keep regular vertices in case
|
|
// they are attachment points. After augmentation, we don't need to.
|
|
// Instead, at all levels above 0, the regular nodes in each round
|
|
// are identical to the supernodes. In order to avoid confusion,
|
|
// we will copy the Id into a separate variable
|
|
vtkm::cont::ArrayHandleCounting<vtkm::Id> newSupernodeId(
|
|
numSupernodesAlready, // start
|
|
static_cast<vtkm::Id>(1), // step
|
|
this->SupernodeSorter.GetNumberOfValues() // number of values
|
|
);
|
|
auto newRegularId = newSupernodeId;
|
|
// define the superparentOldSuperId
|
|
auto permutedSuperparentSet =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SuperparentSet);
|
|
auto superparentOldSuperId = vtkm::cont::make_ArrayHandleTransform(
|
|
permutedSuperparentSet, vtkm::worklet::contourtree_augmented::MaskedIndexFunctor<vtkm::Id>());
|
|
|
|
// set the supernode's regular Id. Set: augmentedTree->supernodes[newSupernodeID] = newRegularID;
|
|
auto permutedAugmentedTreeSupernodes =
|
|
vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->Supernodes);
|
|
vtkm::cont::Algorithm::Copy(newRegularId, permutedAugmentedTreeSupernodes);
|
|
|
|
// Run the worklet for more complex operations
|
|
{ // START block for CreateSuperarcsWorklet
|
|
// create the worklet
|
|
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::CreateSuperarcsWorklet
|
|
createSuperarcsWorklet(
|
|
numSupernodesAlready,
|
|
this->BaseTree->NumRounds,
|
|
vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations),
|
|
roundNumber,
|
|
this->AugmentedTree->Supernodes.GetNumberOfValues());
|
|
// create fancy arrays needed to allow use of FieldIn for worklet parameters
|
|
auto permutedGlobalRegularIdSet =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->GlobalRegularIdSet);
|
|
auto augmentedTreeSuperarcsView =
|
|
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Superarcs,
|
|
numSupernodesAlready,
|
|
this->SupernodeSorter.GetNumberOfValues());
|
|
auto augmentedTreeSuper2HypernodeView =
|
|
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Super2Hypernode,
|
|
numSupernodesAlready,
|
|
this->SupernodeSorter.GetNumberOfValues());
|
|
// invoke the worklet
|
|
this->Invoke(createSuperarcsWorklet, // the worklet
|
|
this->SupernodeSorter, // input domain
|
|
this->SuperparentSet, // input
|
|
this->BaseTree->Superarcs, // input
|
|
this->NewSupernodeIds, // input
|
|
this->BaseTree->Supernodes, // input
|
|
this->BaseTree->RegularNodeGlobalIds, // input
|
|
permutedGlobalRegularIdSet, // input
|
|
this->BaseTree->Super2Hypernode, // input
|
|
this->BaseTree->WhichIteration, // input
|
|
augmentedTreeSuperarcsView, // output
|
|
this->AugmentedTree->FirstSupernodePerIteration[roundNumber], // input/output
|
|
augmentedTreeSuper2HypernodeView // output
|
|
);
|
|
} // END block for CreateSuperarcsWorklet
|
|
|
|
// setting the hyperparent is straightforward since the hyperstructure is preserved
|
|
// we take the superparent (which is guaranteed to be in the baseTree), find it's hyperparent and use that
|
|
// Set: augmentedTree->hyperparents[newSupernodeID] = baseTree->hyperparents[superparentOldSuperID];
|
|
auto permutedAugmentedTreeHyperparents =
|
|
vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->Hyperparents);
|
|
auto permutedBaseTreeHyperparents =
|
|
vtkm::cont::make_ArrayHandlePermutation(superparentOldSuperId, this->BaseTree->Hyperparents);
|
|
vtkm::cont::Algorithm::Copy(permutedBaseTreeHyperparents, permutedAugmentedTreeHyperparents);
|
|
|
|
// which round and iteration carry over
|
|
// Set: augmentedTree->whichRound[newSupernodeID] = baseTree->whichRound[superparentOldSuperID];
|
|
auto permutedAugmentedTreeWhichRound =
|
|
vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->WhichRound);
|
|
auto permutedBaseTreeWhichRound =
|
|
vtkm::cont::make_ArrayHandlePermutation(superparentOldSuperId, this->BaseTree->WhichRound);
|
|
vtkm::cont::Algorithm::Copy(permutedBaseTreeWhichRound, permutedAugmentedTreeWhichRound);
|
|
|
|
// Set: augmentedTree->whichIteration[newSupernodeID] = baseTree->whichIteration[superparentOldSuperID];
|
|
auto permutedAugmentedTreeWhichIteration =
|
|
vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->WhichIteration);
|
|
auto permutedBaseTreeWhichIterationPortal = vtkm::cont::make_ArrayHandlePermutation(
|
|
superparentOldSuperId, this->BaseTree->WhichIteration);
|
|
vtkm::cont::Algorithm::Copy(permutedBaseTreeWhichIterationPortal,
|
|
permutedAugmentedTreeWhichIteration);
|
|
|
|
// now we deal with the regular-sized arrays. In the following supernodeSetIndex is simply supernodeSorterPortal.Get(supernode);
|
|
// copy the global regular Id and data value
|
|
// Set: augmentedTree->regularNodeGlobalIDs[newRegularID] = globalRegularIDSet[supernodeSetIndex];
|
|
auto permutedAugmentedTreeRegularNodeGlobalIds = vtkm::cont::make_ArrayHandlePermutation(
|
|
newRegularId, this->AugmentedTree->RegularNodeGlobalIds);
|
|
auto permutedGlobalRegularIdSet =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->GlobalRegularIdSet);
|
|
vtkm::cont::Algorithm::Copy(permutedGlobalRegularIdSet,
|
|
permutedAugmentedTreeRegularNodeGlobalIds);
|
|
// SetL augmentedTree->dataValues[newRegularID] = dataValueSet[supernodeSetIndex];
|
|
auto permutedAugmentedTreeDataValues =
|
|
vtkm::cont::make_ArrayHandlePermutation(newRegularId, this->AugmentedTree->DataValues);
|
|
auto permutedDataValueSet =
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->DataValueSet);
|
|
vtkm::cont::Algorithm::Copy(permutedDataValueSet, permutedAugmentedTreeDataValues);
|
|
|
|
// the sort order will be dealt with later
|
|
// since all of these nodes are supernodes, they will be their own superparent, which means that:
|
|
// a. the regular2node can be set immediately
|
|
// Set: augmentedTree->regular2supernode[newRegularID] = newSupernodeID;
|
|
auto permutedAugmentedTreeRegular2Supernode =
|
|
vtkm::cont::make_ArrayHandlePermutation(newRegularId, this->AugmentedTree->Regular2Supernode);
|
|
vtkm::cont::Algorithm::Copy(newSupernodeId, permutedAugmentedTreeRegular2Supernode);
|
|
|
|
// b. as can the superparent
|
|
// Set: augmentedTree->superparents[newRegularID] = newSupernodeID;
|
|
auto permutedAugmentedTreeSuperparents =
|
|
vtkm::cont::make_ArrayHandlePermutation(newRegularId, this->AugmentedTree->Superparents);
|
|
vtkm::cont::Algorithm::Copy(newSupernodeId, permutedAugmentedTreeSuperparents);
|
|
} // END scope for e. to delete temporary variables
|
|
|
|
// We have one last bit of cleanup to do. If there were attachment points, then the round in which they transfer has been removed
|
|
// While it is possible to turn this into a null round, it is better to reduce the iteration count by one and resize the arrays
|
|
// To do this, we access the *LAST* element written and check to see whether it is in the final iteration (according to the base tree)
|
|
// But there might be *NO* supernodes in the round, so we check first
|
|
vtkm::Id iterationArraySize =
|
|
vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations);
|
|
if (iterationArraySize > 0)
|
|
{ // at least one iteration
|
|
vtkm::Id lastSupernodeThisLevel = this->AugmentedTree->Supernodes.GetNumberOfValues() - 1;
|
|
vtkm::Id lastIterationThisLevel = vtkm::worklet::contourtree_augmented::MaskedIndex(
|
|
vtkm::cont::ArrayGetValue(lastSupernodeThisLevel, this->AugmentedTree->WhichIteration));
|
|
// if there were no attachment points, it will be in the last iteration: if there were attachment points, it will be in the previous one
|
|
if (lastIterationThisLevel < iterationArraySize - 1)
|
|
{ // attachment point round was removed
|
|
// decrement the iteration count (still with an extra element as sentinel)
|
|
vtkm::worklet::contourtree_augmented::IdArraySetValue(
|
|
roundNumber, iterationArraySize - 1, this->AugmentedTree->NumIterations);
|
|
// shrink the supernode array
|
|
this->AugmentedTree->FirstSupernodePerIteration[roundNumber].Allocate(
|
|
iterationArraySize, vtkm::CopyFlag::On); // shrink array but keep values
|
|
vtkm::worklet::contourtree_augmented::IdArraySetValue(
|
|
iterationArraySize - 1,
|
|
this->AugmentedTree->Supernodes.GetNumberOfValues(),
|
|
this->AugmentedTree->FirstSupernodePerIteration[roundNumber]);
|
|
// for the hypernode array, the last iteration is guaranteed not to have hyperarcs by construction
|
|
// so the last iteration will already have the correct sentinel value, and we just need to shrink the array
|
|
this->AugmentedTree->FirstHypernodePerIteration[roundNumber].Allocate(
|
|
iterationArraySize, vtkm::CopyFlag::On); // shrink array but keep values
|
|
} // attachment point round was removed
|
|
} // at least one iteration
|
|
|
|
// in the interests of debug, we resize the sorting array to zero here,
|
|
// even though we will re-resize them in the next function
|
|
this->SupernodeSorter.ReleaseResources();
|
|
this->GlobalRegularIdSet.ReleaseResources();
|
|
this->DataValueSet.ReleaseResources();
|
|
this->SuperparentSet.ReleaseResources();
|
|
this->SupernodeIdSet.ReleaseResources();
|
|
} // CreateSuperarcs()
|
|
|
|
|
|
// debug routine
|
|
template <typename FieldType>
|
|
std::string HierarchicalAugmenter<FieldType>::DebugPrint(std::string message,
|
|
const char* fileName,
|
|
long lineNum)
|
|
{ // 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 << "Block " << std::setw(4) << this->BlockId << ": " << std::left << message
|
|
<< std::endl;
|
|
resultStream << "----------------------------------------" << std::endl;
|
|
|
|
resultStream << this->BaseTree->DebugPrint(
|
|
(message + std::string(" Base Tree")).c_str(), fileName, lineNum);
|
|
resultStream << this->AugmentedTree->DebugPrint(
|
|
(message + std::string(" Augmented Tree")).c_str(), fileName, lineNum);
|
|
resultStream << "========================================" << std::endl;
|
|
resultStream << "Local List of Attachment Points" << std::endl;
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->GlobalRegularIds.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Global Regular Ids", this->GlobalRegularIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintValues(
|
|
"Data Values", this->DataValues, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Supernode Ids", this->SupernodeIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Superparents", this->Superparents, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Superparent Rounds", this->SuperparentRounds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"WhichRounds", this->WhichRounds, -1, resultStream);
|
|
resultStream << std::endl;
|
|
resultStream << "Outgoing Attachment Points" << std::endl;
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(
|
|
this->OutData.GlobalRegularIds.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Out Global Regular Ids", this->OutData.GlobalRegularIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintValues(
|
|
"Out Data Values", this->OutData.DataValues, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Out Supernode Ids", this->OutData.SupernodeIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Out Superparents", this->OutData.Superparents, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Out Superparent Rounds", this->OutData.SuperparentRounds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Out WhichRounds", this->OutData.WhichRounds, -1, resultStream);
|
|
resultStream << std::endl;
|
|
resultStream << "Incoming Attachment Points" << std::endl;
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(
|
|
this->InData.GlobalRegularIds.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"In Global Regular Ids", this->InData.GlobalRegularIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintValues(
|
|
"In Data Values", this->InData.DataValues, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"In Supernode Ids", this->InData.SupernodeIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"In Superparents", this->InData.Superparents, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"In Superparent Rounds", this->InData.SuperparentRounds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"In WhichRounds", this->InData.WhichRounds, -1, resultStream);
|
|
resultStream << std::endl;
|
|
resultStream << "Holding Arrays" << std::endl;
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(
|
|
this->FirstAttachmentPointInRound.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"First Attach / Rd", this->FirstAttachmentPointInRound, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"AttachmentIds", this->AttachmentIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->NewSupernodeIds.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"New Supernode Ids", this->NewSupernodeIds, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->KeptSupernodes.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Kept Supernodes", this->KeptSupernodes, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->SupernodeSorter.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Supernode Sorter", this->SupernodeSorter, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Global Regular Id", this->GlobalRegularIdSet, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintValues(
|
|
"Data Values", this->DataValueSet, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Superparents", this->SuperparentSet, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"SupernodeIds", this->SupernodeIdSet, -1, resultStream);
|
|
resultStream << std::endl;
|
|
resultStream << std::endl;
|
|
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->SupernodeSorter.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"Supernode Id", this->SupernodeSorter, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintArrayHandle(
|
|
"Permuted Superparent",
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SuperparentSet),
|
|
-1,
|
|
resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintArrayHandle(
|
|
"Permuted Value",
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->DataValueSet),
|
|
-1,
|
|
resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintArrayHandle(
|
|
"Permuted Global Id",
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->GlobalRegularIdSet),
|
|
-1,
|
|
resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintArrayHandle(
|
|
"Permuted Supernode Id",
|
|
vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SupernodeIdSet),
|
|
-1,
|
|
resultStream);
|
|
resultStream << std::endl;
|
|
resultStream << std::endl;
|
|
|
|
vtkm::worklet::contourtree_augmented::PrintHeader(this->RegularSuperparents.GetNumberOfValues());
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"RegularNodesNeeded", this->RegularNodesNeeded, -1, resultStream);
|
|
vtkm::worklet::contourtree_augmented::PrintIndices(
|
|
"RegularSuperparents", this->RegularSuperparents, -1, resultStream);
|
|
resultStream << std::endl;
|
|
// for now, nothing here at all
|
|
//std::string hierarchicalTreeDotString = HierarchicalContourTreeDotGraphPrint(message,
|
|
// this->BaseTree,
|
|
// SHOW_SUPER_STRUCTURE|SHOW_HYPER_STRUCTURE|GV_NODE_NAME_USES_GLOBAL_ID|SHOW_ALL_IDS|SHOW_ALL_SUPERIDS|SHOW_ALL_HYPERIDS|SHOW_EXTRA_DATA,
|
|
// this->BlockId,
|
|
// this->SweepValues
|
|
// );
|
|
return resultStream.str();
|
|
} // DebugPrint()
|
|
|
|
|
|
} // namespace contourtree_distributed
|
|
} // namespace worklet
|
|
} // namespace vtkm
|
|
|
|
|
|
#endif
|