Merge topic 'add/ct_iter_check'

ca86402f9 Provide additional debug info in case contour tree hangs
48d91b99f Throw exception if merge tree gets stuck in a loop
c7ea03ee2 Throw exception if contour tree gets stuck in a loop

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !2420
This commit is contained in:
Gunther Weber 2021-07-14 01:28:44 +00:00 committed by Kitware Robot
commit fa0ce37b96
3 changed files with 60 additions and 13 deletions

@ -377,6 +377,7 @@ inline void ActiveGraph::MakeMergeTree(MergeTree& tree, MeshExtrema& meshExtrema
DebugPrint("Active Graph Computation Starting", __FILE__, __LINE__);
// loop until we run out of active edges
vtkm::Id maxNumIterations = this->EdgeSorter.GetNumberOfValues();
this->NumIterations = 0;
while (true)
{ // main loop
@ -385,7 +386,16 @@ inline void ActiveGraph::MakeMergeTree(MergeTree& tree, MeshExtrema& meshExtrema
// test whether there are any left (if not, we're on the trunk)
if (this->EdgeSorter.GetNumberOfValues() <= 0)
{
break;
}
// test whether we are in a bad infinite loop due to bad input data. Usually
// this is not an issue for the merge tree (only for the contour tree), but
// we check just to make absolutely sure we won't get stuck in an infinite loop
if (this->NumIterations >= maxNumIterations)
{
throw new std::domain_error("Bad iteration. Merge tree unable to process all edges.");
}
// find & label the extrema with their governing saddles
FindGoverningSaddles();

@ -201,6 +201,7 @@ inline void ContourTreeMaker::ComputeHyperAndSuperStructure()
// tree can end with either 0 or 1 vertices unprocessed
// 0 means the last edge was pruned from both ends
// 1 means that there were two final edges meeting at a vertex
vtkm::Id maxNumIterations = this->ActiveSupernodes.GetNumberOfValues();
while (this->ActiveSupernodes.GetNumberOfValues() > 1)
{ // loop until no active vertices remaining
// recompute the vertex degrees
@ -218,6 +219,15 @@ inline void ContourTreeMaker::ComputeHyperAndSuperStructure()
CompressActiveSupernodes();
this->ContourTreeResult.NumIterations++;
// Check to make sure we are not iterating too long
// this can happen if we are given a bad mesh that defines
// a forest of contour trees, rather than a single tree.
// Raise error if we have done more itertions than there are active nodes to remove
if (this->ContourTreeResult.NumIterations >= maxNumIterations)
{
throw new std::domain_error("Bad iteration. This can happen if the input mesh "
"defines a contour forest rather than a simple tree.");
}
} // loop until no active vertices remaining
// test for final edges meeting

@ -95,12 +95,14 @@ public:
{
}
/// Operator used by DIY to compute a step in the fan in
/// @param[in] block The local data block to be processed in this step. Instance of DistributedContourTreeBlockData.
/// @param[in] rp DIY communication proxy
/// @param[in] unused partners of the current block (unused)
void operator()(
vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<FieldType>*
block, // local Block.
const vtkmdiy::ReduceProxy& rp, // communication proxy
const vtkmdiy::RegularSwapPartners& // partners of the current block (unused)
) const
vtkm::worklet::contourtree_distributed::DistributedContourTreeBlockData<FieldType>* block,
const vtkmdiy::ReduceProxy& rp,
const vtkmdiy::RegularSwapPartners&) const
{
// Track timing of main steps
vtkm::cont::Timer totalTimer; // Total time for each call
@ -218,14 +220,39 @@ public:
currBlockOrigin[2] + currBlockSize[2] - 1 };
auto meshBoundaryExecObj = block->ContourTreeMeshes.back().GetMeshBoundaryExecutionObject(
this->GlobalSize, currBlockOrigin, maxIdx);
worklet.Run(block->ContourTreeMeshes.back()
.SortedValues, // Unused param. Provide something to keep the API happy
block->ContourTreeMeshes.back(),
block->ContourTrees.back(),
currSortOrder,
currNumIterations,
1, // Fully augmented
meshBoundaryExecObj);
try
{
worklet.Run(block->ContourTreeMeshes.back()
.SortedValues, // Unused param. Provide something to keep the API happy
block->ContourTreeMeshes.back(),
block->ContourTrees.back(),
currSortOrder,
currNumIterations,
1, // Fully augmented
meshBoundaryExecObj);
}
// In case the contour tree got stuck, expand the debug information from
// the message to check whether we combined bad blocks
catch (const std::domain_error& ex)
{
std::stringstream ex_message;
ex_message << ex.what();
ex_message << " Self/In DIY Id=(" << selfid << ", " << ingid << ")";
ex_message << " Rank=" << rank << " Round=" << rp.round();
ex_message << " Origin Self=(" << block->BlockOrigin[0] << ", " << block->BlockOrigin[1]
<< ", " << block->BlockOrigin[2] << ")";
ex_message << " Origin In=(" << otherBlockOrigin[0] << ", " << otherBlockOrigin[1] << ", "
<< otherBlockOrigin[2] << ")";
ex_message << " Origin Comb=(" << currBlockOrigin[0] << ", " << currBlockOrigin[1] << ", "
<< currBlockOrigin[2] << ")";
ex_message << " Size Self=(" << block->BlockSize[0] << ", " << block->BlockSize[1] << ", "
<< block->BlockSize[2] << ")";
ex_message << " Size In=(" << otherBlockSize[0] << ", " << otherBlockSize[1] << ", "
<< otherBlockSize[2] << ")";
ex_message << " Size Comb=(" << currBlockSize[0] << ", " << currBlockSize[1] << ", "
<< currBlockSize[2] << ")";
std::throw_with_nested(std::domain_error(ex_message.str()));
}
// Update block extents
block->BlockOrigin = currBlockOrigin;