vtk-m/vtkm/cont/CellLocatorUniformBins.cxx
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

522 lines
17 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.
//============================================================================
#include <vtkm/cont/CellLocatorUniformBins.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleTransform.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
using namespace vtkm::internal::cl_uniform_bins;
namespace
{
struct BinsBBox
{
DimVec3 Min;
DimVec3 Max;
VTKM_EXEC
bool Empty() const
{
return (this->Max[0] < this->Min[0]) || (this->Max[1] < this->Min[1]) ||
(this->Max[2] < this->Min[2]);
}
};
VTKM_EXEC_CONT static DimVec3 ComputeGridDimension(vtkm::Id numberOfCells,
const FloatVec3& size,
vtkm::FloatDefault density)
{
vtkm::FloatDefault nsides = 0.0f;
vtkm::FloatDefault volume = 1.0f;
vtkm::FloatDefault maxside = vtkm::Max(size[0], vtkm::Max(size[1], size[2]));
for (int i = 0; i < 3; ++i)
{
if (size[i] / maxside >= 1e-4f)
{
nsides += 1.0f;
volume *= size[i];
}
}
auto r =
vtkm::Pow((static_cast<vtkm::FloatDefault>(numberOfCells) / (volume * density)), 1.0f / nsides);
return vtkm::Max(DimVec3(1),
DimVec3(static_cast<DimensionType>(size[0] * r),
static_cast<DimensionType>(size[1] * r),
static_cast<DimensionType>(size[2] * r)));
}
VTKM_EXEC static BinsBBox ComputeIntersectingBins(const Bounds cellBounds, const Grid& grid)
{
auto minb = static_cast<DimVec3>((cellBounds.Min - grid.Origin) / grid.BinSize);
auto maxb = static_cast<DimVec3>((cellBounds.Max - grid.Origin) / grid.BinSize);
return { vtkm::Max(DimVec3(0), minb), vtkm::Min(grid.Dimensions - DimVec3(1), maxb) };
}
VTKM_EXEC static vtkm::Id GetNumberOfBins(const BinsBBox& binsBBox)
{
return binsBBox.Empty() ? 0 : ((binsBBox.Max[0] - binsBBox.Min[0] + 1) *
(binsBBox.Max[1] - binsBBox.Min[1] + 1) *
(binsBBox.Max[2] - binsBBox.Min[2] + 1));
}
class BBoxIterator
{
public:
VTKM_EXEC_CONT BBoxIterator(const BinsBBox& bbox, const DimVec3& dim)
: BBox(bbox)
, Dim(dim)
, StepY(dim[0] - (bbox.Max[0] - bbox.Min[0] + 1))
, StepZ((dim[0] * dim[1]) - ((bbox.Max[1] - bbox.Min[1] + 1) * dim[0]))
{
this->Init();
}
VTKM_EXEC_CONT void Init()
{
this->Idx = this->BBox.Min;
this->FlatIdx = ComputeFlatIndex(this->Idx, this->Dim);
this->DoneFlag = this->BBox.Empty();
}
VTKM_EXEC_CONT bool Done() const { return this->DoneFlag; }
VTKM_EXEC_CONT void Next()
{
if (!this->DoneFlag)
{
++this->Idx[0];
this->FlatIdx += 1;
if (this->Idx[0] > this->BBox.Max[0])
{
this->Idx[0] = this->BBox.Min[0];
++this->Idx[1];
this->FlatIdx += this->StepY;
if (this->Idx[1] > this->BBox.Max[1])
{
this->Idx[1] = this->BBox.Min[1];
++this->Idx[2];
this->FlatIdx += this->StepZ;
if (this->Idx[2] > this->BBox.Max[2])
{
this->DoneFlag = true;
}
}
}
}
}
VTKM_EXEC_CONT const DimVec3& GetIdx() const { return this->Idx; }
VTKM_EXEC_CONT vtkm::Id GetFlatIdx() const { return this->FlatIdx; }
private:
const BinsBBox BBox;
const DimVec3 Dim;
const vtkm::Id StepY, StepZ;
DimVec3 Idx;
vtkm::Id FlatIdx;
bool DoneFlag;
};
class CountBinsL1 : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellset, FieldInPoint coords, FieldOutCell bincount);
using ExecutionSignature = void(_2, _3);
CountBinsL1(const Grid& grid)
: L1Grid(grid)
{
}
template <typename PointsVecType>
VTKM_EXEC void operator()(const PointsVecType& points, vtkm::Id& numBins) const
{
auto cellBounds = ComputeCellBounds(points);
auto binsBBox = ComputeIntersectingBins(cellBounds, this->L1Grid);
numBins = GetNumberOfBins(binsBBox);
}
private:
Grid L1Grid;
};
class FindBinsL1 : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellset,
FieldInPoint coords,
FieldInCell offsets,
WholeArrayOut binIds);
using ExecutionSignature = void(_2, _3, _4);
FindBinsL1(const Grid& grid)
: L1Grid(grid)
{
}
template <typename PointsVecType, typename BinIdsPortalType>
VTKM_EXEC void operator()(const PointsVecType& points,
vtkm::Id offset,
BinIdsPortalType& binIds) const
{
auto cellBounds = ComputeCellBounds(points);
auto binsBBox = ComputeIntersectingBins(cellBounds, this->L1Grid);
for (BBoxIterator i(binsBBox, this->L1Grid.Dimensions); !i.Done(); i.Next())
{
binIds.Set(offset, i.GetFlatIdx());
++offset;
}
}
private:
Grid L1Grid;
};
class GenerateBinsL1 : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn binIds, FieldIn cellCounts, WholeArrayOut dimensions);
using ExecutionSignature = void(_1, _2, _3);
using InputDomain = _1;
GenerateBinsL1(FloatVec3 size, vtkm::FloatDefault density)
: Size(size)
, Density(density)
{
}
template <typename OutputDimensionsPortal>
VTKM_EXEC void operator()(vtkm::Id binId,
vtkm::Id numCells,
OutputDimensionsPortal& dimensions) const
{
dimensions.Set(binId, ComputeGridDimension(numCells, this->Size, this->Density));
}
private:
FloatVec3 Size;
vtkm::FloatDefault Density;
};
class CountBinsL2 : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellset,
FieldInPoint coords,
WholeArrayIn binDimensions,
FieldOutCell bincount);
using ExecutionSignature = void(_2, _3, _4);
CountBinsL2(const Grid& grid)
: L1Grid(grid)
{
}
template <typename PointsVecType, typename BinDimensionsPortalType>
VTKM_EXEC void operator()(const PointsVecType& points,
const BinDimensionsPortalType& binDimensions,
vtkm::Id& numBins) const
{
auto cellBounds = ComputeCellBounds(points);
auto binsBBox = ComputeIntersectingBins(cellBounds, this->L1Grid);
numBins = 0;
for (BBoxIterator i(binsBBox, this->L1Grid.Dimensions); !i.Done(); i.Next())
{
Grid leaf = ComputeLeafGrid(i.GetIdx(), binDimensions.Get(i.GetFlatIdx()), this->L1Grid);
auto binsBBoxL2 = ComputeIntersectingBins(cellBounds, leaf);
numBins += GetNumberOfBins(binsBBoxL2);
}
}
private:
Grid L1Grid;
};
class FindBinsL2 : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellset,
FieldInPoint coords,
WholeArrayIn binDimensions,
WholeArrayIn binStarts,
FieldInCell offsets,
WholeArrayOut binIds,
WholeArrayOut cellIds);
using ExecutionSignature = void(InputIndex, _2, _3, _4, _5, _6, _7);
FindBinsL2(const Grid& grid)
: L1Grid(grid)
{
}
template <typename PointsVecType,
typename BinDimensionsPortalType,
typename BinStartsPortalType,
typename BinIdsPortalType,
typename CellIdsPortalType>
VTKM_EXEC void operator()(vtkm::Id cellId,
const PointsVecType& points,
const BinDimensionsPortalType& binDimensions,
const BinStartsPortalType& binStarts,
vtkm::Id offset,
BinIdsPortalType binIds,
CellIdsPortalType cellIds) const
{
auto cellBounds = ComputeCellBounds(points);
auto binsBBox = ComputeIntersectingBins(cellBounds, this->L1Grid);
for (BBoxIterator i(binsBBox, this->L1Grid.Dimensions); !i.Done(); i.Next())
{
Grid leaf = ComputeLeafGrid(i.GetIdx(), binDimensions.Get(i.GetFlatIdx()), this->L1Grid);
auto binsBBoxL2 = ComputeIntersectingBins(cellBounds, leaf);
vtkm::Id leafStart = binStarts.Get(i.GetFlatIdx());
for (BBoxIterator j(binsBBoxL2, leaf.Dimensions); !j.Done(); j.Next())
{
binIds.Set(offset, leafStart + j.GetFlatIdx());
cellIds.Set(offset, cellId);
++offset;
}
}
}
private:
Grid L1Grid;
};
class GenerateBinsL2 : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn binIds,
FieldIn startsIn,
FieldIn countsIn,
WholeArrayOut startsOut,
WholeArrayOut countsOut);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
template <typename CellStartsPortalType, typename CellCountsPortalType>
VTKM_EXEC void operator()(vtkm::Id binIndex,
vtkm::Id start,
vtkm::Id count,
CellStartsPortalType& cellStarts,
CellCountsPortalType& cellCounts) const
{
cellStarts.Set(binIndex, start);
cellCounts.Set(binIndex, count);
}
};
struct DimensionsToCount
{
VTKM_EXEC vtkm::Id operator()(const DimVec3& dim) const { return dim[0] * dim[1] * dim[2]; }
};
} // anonymous namespace
namespace vtkm
{
namespace cont
{
//----------------------------------------------------------------------------
/// Builds the cell locator lookup structure
///
VTKM_CONT void CellLocatorUniformBins::Build()
{
vtkm::cont::Invoker invoke;
auto cellset = this->GetCellSet();
const auto& coords = this->GetCoordinates();
// 1: Compute the top level grid
auto bounds = coords.GetBounds();
FloatVec3 bmin(static_cast<vtkm::FloatDefault>(bounds.X.Min),
static_cast<vtkm::FloatDefault>(bounds.Y.Min),
static_cast<vtkm::FloatDefault>(bounds.Z.Min));
FloatVec3 bmax(static_cast<vtkm::FloatDefault>(bounds.X.Max),
static_cast<vtkm::FloatDefault>(bounds.Y.Max),
static_cast<vtkm::FloatDefault>(bounds.Z.Max));
auto size = bmax - bmin;
auto fudge = vtkm::Max(FloatVec3(1e-6f), size * 1e-4f);
size += 2.0f * fudge;
this->TopLevel.Dimensions =
ComputeGridDimension(cellset.GetNumberOfCells(), size, this->DensityL1);
this->TopLevel.Origin = bmin - fudge;
this->TopLevel.BinSize = size / static_cast<FloatVec3>(this->TopLevel.Dimensions);
// 2: For each cell, find the number of top level bins they intersect
vtkm::cont::ArrayHandle<vtkm::Id> binCounts;
CountBinsL1 countL1(this->TopLevel);
invoke(countL1, cellset, coords, binCounts);
// 3: Total number of unique (cell, bin) pairs (for pre-allocating arrays)
vtkm::Id numPairsL1 = vtkm::cont::Algorithm::ScanExclusive(binCounts, binCounts);
// 4: For each cell find the top level bins that intersect it
vtkm::cont::ArrayHandle<vtkm::Id> binIds;
binIds.Allocate(numPairsL1);
FindBinsL1 findL1(this->TopLevel);
invoke(findL1, cellset, coords, binCounts, binIds);
binCounts.ReleaseResources();
// 5: From above, find the number of cells that intersect each top level bin
vtkm::cont::Algorithm::Sort(binIds);
vtkm::cont::ArrayHandle<vtkm::Id> bins;
vtkm::cont::ArrayHandle<vtkm::Id> cellsPerBin;
vtkm::cont::Algorithm::ReduceByKey(binIds,
vtkm::cont::make_ArrayHandleConstant(vtkm::Id(1), numPairsL1),
bins,
cellsPerBin,
vtkm::Sum());
binIds.ReleaseResources();
// 6: Compute level-2 dimensions
vtkm::Id numberOfBins =
this->TopLevel.Dimensions[0] * this->TopLevel.Dimensions[1] * this->TopLevel.Dimensions[2];
vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(DimVec3(0), numberOfBins),
this->LeafDimensions);
GenerateBinsL1 generateL1(this->TopLevel.BinSize, this->DensityL2);
invoke(generateL1, bins, cellsPerBin, this->LeafDimensions);
bins.ReleaseResources();
cellsPerBin.ReleaseResources();
// 7: Compute number of level-2 bins
vtkm::Id numberOfLeaves = vtkm::cont::Algorithm::ScanExclusive(
vtkm::cont::make_ArrayHandleTransform(this->LeafDimensions, DimensionsToCount()),
this->LeafStartIndex);
// 8: For each cell, find the number of l2 bins they intersect
CountBinsL2 countL2(this->TopLevel);
invoke(countL2, cellset, coords, this->LeafDimensions, binCounts);
// 9: Total number of unique (cell, bin) pairs (for pre-allocating arrays)
vtkm::Id numPairsL2 = vtkm::cont::Algorithm::ScanExclusive(binCounts, binCounts);
// 10: For each cell, find the l2 bins they intersect
binIds.Allocate(numPairsL2);
this->CellIds.Allocate(numPairsL2);
FindBinsL2 findL2(this->TopLevel);
invoke(findL2,
cellset,
coords,
this->LeafDimensions,
this->LeafStartIndex,
binCounts,
binIds,
this->CellIds);
binCounts.ReleaseResources();
// 11: From above, find the cells that each l2 bin intersects
vtkm::cont::Algorithm::SortByKey(binIds, this->CellIds);
vtkm::cont::Algorithm::ReduceByKey(binIds,
vtkm::cont::make_ArrayHandleConstant(vtkm::Id(1), numPairsL2),
bins,
cellsPerBin,
vtkm::Sum());
binIds.ReleaseResources();
// 12: Generate the leaf bin arrays
vtkm::cont::ArrayHandle<vtkm::Id> cellsStart;
vtkm::cont::Algorithm::ScanExclusive(cellsPerBin, cellsStart);
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleConstant<vtkm::Id>(0, numberOfLeaves),
this->CellStartIndex);
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleConstant<vtkm::Id>(0, numberOfLeaves),
this->CellCount);
invoke(GenerateBinsL2{}, bins, cellsStart, cellsPerBin, this->CellStartIndex, this->CellCount);
}
//----------------------------------------------------------------------------
struct CellLocatorUniformBins::MakeExecObject
{
template <typename CellSetType, typename DeviceAdapter>
VTKM_CONT void operator()(const CellSetType& cellSet,
DeviceAdapter,
const CellLocatorUniformBins& self) const
{
auto execObject =
new vtkm::exec::CellLocatorUniformBins<CellSetType, DeviceAdapter>(self.TopLevel,
self.LeafDimensions,
self.LeafStartIndex,
self.CellStartIndex,
self.CellCount,
self.CellIds,
cellSet,
self.GetCoordinates());
self.ExecutionObjectHandle.Reset(execObject);
}
};
struct CellLocatorUniformBins::PrepareForExecutionFunctor
{
template <typename DeviceAdapter>
VTKM_CONT bool operator()(DeviceAdapter, const CellLocatorUniformBins& self) const
{
self.GetCellSet().CastAndCall(MakeExecObject{}, DeviceAdapter{}, self);
return true;
}
};
VTKM_CONT const vtkm::exec::CellLocator* CellLocatorUniformBins::PrepareForExecution(
vtkm::cont::DeviceAdapterId device) const
{
if (!vtkm::cont::TryExecuteOnDevice(device, PrepareForExecutionFunctor(), *this))
{
throwFailedRuntimeDeviceTransfer("CellLocatorUniformBins", device);
}
return this->ExecutionObjectHandle.PrepareForExecution(device);
}
//----------------------------------------------------------------------------
void CellLocatorUniformBins::PrintSummary(std::ostream& out) const
{
out << "DensityL1: " << this->DensityL1 << "\n";
out << "DensityL2: " << this->DensityL2 << "\n";
out << "Input CellSet: \n";
this->GetCellSet().PrintSummary(out);
out << "Input Coordinates: \n";
this->GetCoordinates().PrintSummary(out);
out << "LookupStructure:\n";
out << " TopLevelGrid\n";
out << " Dimensions: " << this->TopLevel.Dimensions << "\n";
out << " Origin: " << this->TopLevel.Origin << "\n";
out << " BinSize: " << this->TopLevel.BinSize << "\n";
out << " LeafDimensions:\n";
vtkm::cont::printSummary_ArrayHandle(this->LeafDimensions, out);
out << " LeafStartIndex:\n";
vtkm::cont::printSummary_ArrayHandle(this->LeafStartIndex, out);
out << " CellStartIndex:\n";
vtkm::cont::printSummary_ArrayHandle(this->CellStartIndex, out);
out << " CellCount:\n";
vtkm::cont::printSummary_ArrayHandle(this->CellCount, out);
out << " CellIds:\n";
vtkm::cont::printSummary_ArrayHandle(this->CellIds, out);
}
}
} // vtkm::cont