vtk-m/vtkm/worklet/spatialstructure/BoundingIntervalHierarchy.h
Allison Vacanti 5db762ee71 Refactor topology mappings to clarify meaning.
The `From` and `To` nomenclature for topology mapping has been confusing for
both users and developers, especially at lower levels where the intention of
mapping attributes from one element to another is easily conflated with the
concept of mapping indices (which maps in the exact opposite direction).

These identifiers have been renamed to `VisitTopology` and `IncidentTopology`
to clarify the direction of the mapping. The order in which these template
parameters are specified for `WorkletMapTopology` have also been reversed,
since eventually there may be more than one `IncidentTopology`, and having
`IncidentTopology` at the end will allow us to replace it with a variadic
template parameter pack in the future.

Other implementation details supporting these worklets, include `Fetch` tags,
`Connectivity` classes, and methods on the various `CellSet` classes (such as
`PrepareForInput` have also reversed their template arguments. These will need
to be cautiously updated.

The convenience implementations of `WorkletMapTopology` have been renamed for
clarity as follows:

```
WorkletMapPointToCell --> WorkletVisitCellsWithPoints
WorkletMapCellToPoint --> WorkletVisitPointsWithCells
```

The `ControlSignature` tags have been renamed as follows:

```
FieldInTo --> FieldInVisit
FieldInFrom --> FieldInMap
FromCount --> IncidentElementCount
FromIndices --> IncidentElementIndices
```
2019-08-06 11:27:26 -04:00

607 lines
19 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.
//============================================================================
#ifndef vtk_m_worklet_spatialstructure_BoundingIntervalHierarchy_h
#define vtk_m_worklet_spatialstructure_BoundingIntervalHierarchy_h
#include <type_traits>
#include <vtkm/Bounds.h>
#include <vtkm/Types.h>
#include <vtkm/VecFromPortalPermute.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandlePermutation.h>
#include <vtkm/cont/ArrayHandleReverse.h>
#include <vtkm/cont/ArrayHandleTransform.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/cuda/DeviceAdapterCuda.h>
#include <vtkm/exec/CellLocatorBoundingIntervalHierarchyExec.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
namespace vtkm
{
namespace worklet
{
namespace spatialstructure
{
struct TreeNode
{
vtkm::FloatDefault LMax;
vtkm::FloatDefault RMin;
vtkm::IdComponent Dimension;
VTKM_EXEC
TreeNode()
: LMax()
, RMin()
, Dimension()
{
}
}; // struct TreeNode
struct SplitProperties
{
vtkm::FloatDefault Plane;
vtkm::Id NumLeftPoints;
vtkm::Id NumRightPoints;
vtkm::FloatDefault LMax;
vtkm::FloatDefault RMin;
vtkm::FloatDefault Cost;
VTKM_EXEC
SplitProperties()
: Plane()
, NumLeftPoints()
, NumRightPoints()
, LMax()
, RMin()
, Cost()
{
}
}; // struct SplitProperties
struct CellRangesExtracter : public vtkm::worklet::WorkletVisitCellsWithPoints
{
typedef void ControlSignature(CellSetIn,
WholeArrayIn,
FieldOutCell,
FieldOutCell,
FieldOutCell,
FieldOutCell,
FieldOutCell,
FieldOutCell);
typedef void ExecutionSignature(_1, PointIndices, _2, _3, _4, _5, _6, _7, _8);
template <typename CellShape, typename PointIndicesVec, typename PointsPortal>
VTKM_EXEC void operator()(CellShape vtkmNotUsed(shape),
const PointIndicesVec& pointIndices,
const PointsPortal& points,
vtkm::Range& rangeX,
vtkm::Range& rangeY,
vtkm::Range& rangeZ,
vtkm::FloatDefault& centerX,
vtkm::FloatDefault& centerY,
vtkm::FloatDefault& centerZ) const
{
vtkm::Bounds bounds;
vtkm::VecFromPortalPermute<PointIndicesVec, PointsPortal> cellPoints(&pointIndices, points);
vtkm::IdComponent numPoints = cellPoints.GetNumberOfComponents();
for (vtkm::IdComponent i = 0; i < numPoints; ++i)
{
bounds.Include(cellPoints[i]);
}
rangeX = bounds.X;
rangeY = bounds.Y;
rangeZ = bounds.Z;
vtkm::Vec3f center = bounds.Center();
centerX = center[0];
centerY = center[1];
centerZ = center[2];
}
}; // struct CellRangesExtracter
struct LEQWorklet : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldIn, FieldOut, FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4);
using InputDomain = _1;
VTKM_EXEC
void operator()(const vtkm::FloatDefault& value,
const vtkm::FloatDefault& planeValue,
vtkm::Id& leq,
vtkm::Id& r) const
{
leq = value <= planeValue;
r = !leq;
}
}; // struct LEQWorklet
template <bool LEQ>
struct FilterRanges;
template <>
struct FilterRanges<true> : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldIn, FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4);
using InputDomain = _1;
VTKM_EXEC
void operator()(const vtkm::FloatDefault& value,
const vtkm::FloatDefault& planeValue,
const vtkm::Range& cellBounds,
vtkm::Range& outBounds) const
{
outBounds = (value <= planeValue) ? cellBounds : vtkm::Range();
}
}; // struct FilterRanges
template <>
struct FilterRanges<false> : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldIn, FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4);
using InputDomain = _1;
VTKM_EXEC
void operator()(const vtkm::FloatDefault& value,
const vtkm::FloatDefault& planeValue,
const vtkm::Range& cellBounds,
vtkm::Range& outBounds) const
{
outBounds = (value > planeValue) ? cellBounds : vtkm::Range();
}
}; // struct FilterRanges
struct SplitPlaneCalculatorWorklet : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2);
using InputDomain = _1;
VTKM_CONT
SplitPlaneCalculatorWorklet(vtkm::IdComponent planeIdx, vtkm::IdComponent numPlanes)
: Scale(static_cast<vtkm::FloatDefault>(planeIdx + 1) /
static_cast<vtkm::FloatDefault>(numPlanes + 1))
{
}
VTKM_EXEC
void operator()(const vtkm::Range& range, vtkm::FloatDefault& splitPlane) const
{
splitPlane = static_cast<vtkm::FloatDefault>(range.Min + Scale * (range.Max - range.Min));
}
vtkm::FloatDefault Scale;
};
struct SplitPropertiesCalculator : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldIn, FieldIn, FieldIn, FieldIn, WholeArrayInOut);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, InputIndex);
using InputDomain = _1;
VTKM_CONT
SplitPropertiesCalculator(vtkm::IdComponent index, vtkm::Id stride)
: Index(index)
, Stride(stride)
{
}
template <typename SplitPropertiesPortal>
VTKM_EXEC void operator()(const vtkm::Id& pointsToLeft,
const vtkm::Id& pointsToRight,
const vtkm::Range& lMaxRanges,
const vtkm::Range& rMinRanges,
const vtkm::FloatDefault& planeValue,
SplitPropertiesPortal& splits,
vtkm::Id inputIndex) const
{
SplitProperties split;
split.Plane = planeValue;
split.NumLeftPoints = pointsToLeft;
split.NumRightPoints = pointsToRight;
split.LMax = static_cast<vtkm::FloatDefault>(lMaxRanges.Max);
split.RMin = static_cast<vtkm::FloatDefault>(rMinRanges.Min);
split.Cost = vtkm::Abs(split.LMax * static_cast<vtkm::FloatDefault>(pointsToLeft) -
split.RMin * static_cast<vtkm::FloatDefault>(pointsToRight));
if (vtkm::IsNan(split.Cost))
{
split.Cost = vtkm::Infinity<vtkm::FloatDefault>();
}
splits.Set(inputIndex * Stride + Index, split);
//printf("Plane = %lf, NL = %lld, NR = %lld, LM = %lf, RM = %lf, C = %lf\n", split.Plane, split.NumLeftPoints, split.NumRightPoints, split.LMax, split.RMin, split.Cost);
}
vtkm::IdComponent Index;
vtkm::Id Stride;
};
struct SplitSelector : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn,
WholeArrayIn,
WholeArrayIn,
WholeArrayIn,
FieldIn,
FieldOut,
FieldOut,
FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8);
using InputDomain = _1;
VTKM_CONT
SplitSelector(vtkm::IdComponent numPlanes,
vtkm::IdComponent maxLeafSize,
vtkm::IdComponent stride)
: NumPlanes(numPlanes)
, MaxLeafSize(maxLeafSize)
, Stride(stride)
{
}
template <typename SplitPropertiesPortal>
VTKM_EXEC void operator()(vtkm::Id index,
const SplitPropertiesPortal& xSplits,
const SplitPropertiesPortal& ySplits,
const SplitPropertiesPortal& zSplits,
const vtkm::Id& segmentSize,
TreeNode& node,
vtkm::FloatDefault& plane,
vtkm::Id& choice) const
{
if (segmentSize <= MaxLeafSize)
{
node.Dimension = -1;
choice = 0;
return;
}
choice = 1;
using Split = SplitProperties;
vtkm::FloatDefault minCost = vtkm::Infinity<vtkm::FloatDefault>();
const Split& xSplit = xSplits[ArgMin(xSplits, index * Stride, Stride)];
bool found = false;
if (xSplit.Cost < minCost && xSplit.NumLeftPoints != 0 && xSplit.NumRightPoints != 0)
{
minCost = xSplit.Cost;
node.Dimension = 0;
node.LMax = xSplit.LMax;
node.RMin = xSplit.RMin;
plane = xSplit.Plane;
found = true;
}
const Split& ySplit = ySplits[ArgMin(ySplits, index * Stride, Stride)];
if (ySplit.Cost < minCost && ySplit.NumLeftPoints != 0 && ySplit.NumRightPoints != 0)
{
minCost = ySplit.Cost;
node.Dimension = 1;
node.LMax = ySplit.LMax;
node.RMin = ySplit.RMin;
plane = ySplit.Plane;
found = true;
}
const Split& zSplit = zSplits[ArgMin(zSplits, index * Stride, Stride)];
if (zSplit.Cost < minCost && zSplit.NumLeftPoints != 0 && zSplit.NumRightPoints != 0)
{
minCost = zSplit.Cost;
node.Dimension = 2;
node.LMax = zSplit.LMax;
node.RMin = zSplit.RMin;
plane = zSplit.Plane;
found = true;
}
if (!found)
{
const Split& xMSplit = xSplits[NumPlanes];
minCost = xMSplit.Cost;
node.Dimension = 0;
node.LMax = xMSplit.LMax;
node.RMin = xMSplit.RMin;
plane = xMSplit.Plane;
const Split& yMSplit = ySplits[NumPlanes];
if (yMSplit.Cost < minCost && yMSplit.NumLeftPoints != 0 && yMSplit.NumRightPoints != 0)
{
minCost = yMSplit.Cost;
node.Dimension = 1;
node.LMax = yMSplit.LMax;
node.RMin = yMSplit.RMin;
plane = yMSplit.Plane;
}
const Split& zMSplit = zSplits[NumPlanes];
if (zMSplit.Cost < minCost && zMSplit.NumLeftPoints != 0 && zMSplit.NumRightPoints != 0)
{
minCost = zMSplit.Cost;
node.Dimension = 2;
node.LMax = zMSplit.LMax;
node.RMin = zMSplit.RMin;
plane = zMSplit.Plane;
}
}
//printf("Selected plane %lf, with cost %lf [%d, %lf, %lf]\n", plane, minCost, node.Dimension, node.LMax, node.RMin);
}
template <typename ArrayPortal>
VTKM_EXEC vtkm::Id ArgMin(const ArrayPortal& values, vtkm::Id start, vtkm::Id length) const
{
vtkm::Id minIdx = start;
for (vtkm::Id i = start; i < (start + length); ++i)
{
if (values[i].Cost < values[minIdx].Cost)
{
minIdx = i;
}
}
return minIdx;
}
vtkm::IdComponent NumPlanes;
vtkm::IdComponent MaxLeafSize;
vtkm::Id Stride;
};
struct CalculateSplitDirectionFlag : public vtkm::worklet::WorkletMapField
{
typedef void ControlSignature(FieldIn, FieldIn, FieldIn, FieldIn, FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6);
using InputDomain = _1;
VTKM_EXEC
void operator()(const vtkm::FloatDefault& x,
const vtkm::FloatDefault& y,
const vtkm::FloatDefault& z,
const TreeNode& split,
const vtkm::FloatDefault& plane,
vtkm::Id& flag) const
{
if (split.Dimension >= 0)
{
const vtkm::Vec3f point(x, y, z);
const vtkm::FloatDefault& c = point[split.Dimension];
// We use 0 to signify left child, 1 for right child
flag = 1 - static_cast<vtkm::Id>(c <= plane);
}
else
{
flag = 0;
}
}
}; // struct CalculateSplitDirectionFlag
struct SegmentSplitter : public vtkm::worklet::WorkletMapField
{
typedef void ControlSignature(FieldIn, FieldIn, FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4);
using InputDomain = _1;
VTKM_CONT
SegmentSplitter(vtkm::IdComponent maxLeafSize)
: MaxLeafSize(maxLeafSize)
{
}
VTKM_EXEC
void operator()(const vtkm::Id& segmentId,
const vtkm::Id& leqFlag,
const vtkm::Id& segmentSize,
vtkm::Id& newSegmentId) const
{
if (segmentSize <= MaxLeafSize)
{
// We do not split the segments which have cells fewer than MaxLeafSize, moving them to left
newSegmentId = 2 * segmentId;
}
else
{
newSegmentId = 2 * segmentId + leqFlag;
}
}
vtkm::IdComponent MaxLeafSize;
}; // struct SegmentSplitter
struct SplitIndicesCalculator : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldIn, FieldIn, FieldIn, FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6);
using InputDomain = _1;
VTKM_EXEC
void operator()(const vtkm::Id& leqFlag,
const vtkm::Id& trueFlagCount,
const vtkm::Id& countPreviousSegment,
const vtkm::Id& runningFalseFlagCount,
const vtkm::Id& totalFalseFlagCount,
vtkm::Id& scatterIndex) const
{
if (leqFlag)
{
scatterIndex = countPreviousSegment + totalFalseFlagCount + trueFlagCount;
}
else
{
scatterIndex = countPreviousSegment + runningFalseFlagCount - 1;
}
}
}; // struct SplitIndicesCalculator
struct Scatter : public vtkm::worklet::WorkletMapField
{
typedef void ControlSignature(FieldIn, FieldIn, WholeArrayOut);
typedef void ExecutionSignature(_1, _2, _3);
using InputDomain = _1;
template <typename InputType, typename OutputPortalType>
VTKM_EXEC void operator()(const InputType& in, const vtkm::Id& idx, OutputPortalType& out) const
{
out.Set(idx, in);
}
}; // struct Scatter
template <typename ValueArrayHandle, typename IndexArrayHandle>
ValueArrayHandle ScatterArray(const ValueArrayHandle& input, const IndexArrayHandle& indices)
{
ValueArrayHandle output;
output.Allocate(input.GetNumberOfValues());
vtkm::worklet::DispatcherMapField<Scatter>().Invoke(input, indices, output);
return output;
}
struct NonSplitIndexCalculator : public vtkm::worklet::WorkletMapField
{
typedef void ControlSignature(FieldIn, FieldOut);
typedef void ExecutionSignature(_1, _2);
using InputDomain = _1;
VTKM_CONT
NonSplitIndexCalculator(vtkm::IdComponent maxLeafSize)
: MaxLeafSize(maxLeafSize)
{
}
VTKM_EXEC void operator()(const vtkm::Id& inSegmentSize, vtkm::Id& outSegmentSize) const
{
if (inSegmentSize <= MaxLeafSize)
{
outSegmentSize = inSegmentSize;
}
else
{
outSegmentSize = 0;
}
}
vtkm::Id MaxLeafSize;
}; // struct NonSplitIndexCalculator
struct TreeLevelAdder : public vtkm::worklet::WorkletMapField
{
typedef void ControlSignature(FieldIn nodeIndices,
FieldIn segmentSplits,
FieldIn nonSplitSegmentIndices,
FieldIn segmentSizes,
FieldIn runningSplitSegmentCounts,
FieldIn parentIndices,
WholeArrayInOut newTree,
WholeArrayOut nextParentIndices);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8);
using InputDomain = _1;
VTKM_CONT
TreeLevelAdder(vtkm::Id cellIdsOffset, vtkm::Id treeOffset, vtkm::IdComponent maxLeafSize)
: CellIdsOffset(cellIdsOffset)
, TreeOffset(treeOffset)
, MaxLeafSize(maxLeafSize)
{
}
template <typename BoundingIntervalHierarchyPortal, typename NextParentPortal>
VTKM_EXEC void operator()(vtkm::Id index,
const TreeNode& split,
vtkm::Id start,
vtkm::Id count,
vtkm::Id numPreviousSplits,
vtkm::Id parentIndex,
BoundingIntervalHierarchyPortal& treePortal,
NextParentPortal& nextParentPortal) const
{
vtkm::exec::CellLocatorBoundingIntervalHierarchyNode node;
node.ParentIndex = parentIndex;
if (count > this->MaxLeafSize)
{
node.Dimension = split.Dimension;
node.ChildIndex = this->TreeOffset + 2 * numPreviousSplits;
node.Node.LMax = split.LMax;
node.Node.RMin = split.RMin;
nextParentPortal.Set(2 * numPreviousSplits, index);
nextParentPortal.Set(2 * numPreviousSplits + 1, index);
}
else
{
node.ChildIndex = -1;
node.Leaf.Start = this->CellIdsOffset + start;
node.Leaf.Size = count;
}
treePortal.Set(index, node);
}
vtkm::Id CellIdsOffset;
vtkm::Id TreeOffset;
vtkm::IdComponent MaxLeafSize;
}; // struct TreeLevelAdder
template <typename T, class BinaryFunctor>
vtkm::cont::ArrayHandle<T> ReverseScanInclusiveByKey(const vtkm::cont::ArrayHandle<T>& keys,
const vtkm::cont::ArrayHandle<T>& values,
BinaryFunctor binaryFunctor)
{
vtkm::cont::ArrayHandle<T> result;
auto reversedResult = vtkm::cont::make_ArrayHandleReverse(result);
vtkm::cont::Algorithm::ScanInclusiveByKey(vtkm::cont::make_ArrayHandleReverse(keys),
vtkm::cont::make_ArrayHandleReverse(values),
reversedResult,
binaryFunctor);
return result;
}
template <typename T, typename U>
vtkm::cont::ArrayHandle<T> CopyIfArray(const vtkm::cont::ArrayHandle<T>& input,
const vtkm::cont::ArrayHandle<U>& stencil)
{
vtkm::cont::ArrayHandle<T> result;
vtkm::cont::Algorithm::CopyIf(input, stencil, result);
return result;
}
VTKM_CONT
struct Invert
{
VTKM_EXEC
vtkm::Id operator()(const vtkm::Id& value) const { return 1 - value; }
}; // struct Invert
VTKM_CONT
struct RangeAdd
{
VTKM_EXEC
vtkm::Range operator()(const vtkm::Range& accumulator, const vtkm::Range& value) const
{
if (value.IsNonEmpty())
{
return accumulator.Union(value);
}
else
{
return accumulator;
}
}
}; // struct RangeAdd
} // namespace spatialstructure
} // namespace worklet
} // namespace vtkm
#endif //vtk_m_worklet_spatialstructure_BoundingIntervalHierarchy_h