vtk-m/vtkm/cont/PointLocatorUniformGrid.cxx
Kenneth Moreland e473cb4bbc Fix PointLocatorUniformGrid for points on boundary
When creating the search structures in PointLocatorUniformGrid, a point
outside the boundary would be given an invalid bin id. These points
could never be found. Generally, this is not a big deal for points
outside of the boundary, but it could be a problem for points on the
boundary. A point on the boundary could be taken as outside the
boundary. Since the boundary is chosen from limits of the points, some
will almost always be on the boundary.

Fix this problem by clamping all points to the nearest valid bin. This
could cause a problem if the user has selected a boundary excluding a
lot of points. All those points could be grouped to the same edge bins,
but that is probably not a great idea anyway.
2019-07-10 08:35:45 -06:00

141 lines
5.2 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 2017 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2017 Los Alamos National Security.
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#include <vtkm/cont/PointLocatorUniformGrid.h>
namespace vtkm
{
namespace cont
{
namespace internal
{
class BinPointsWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn coord, FieldOut label);
using ExecutionSignature = void(_1, _2);
VTKM_CONT
BinPointsWorklet(vtkm::Vec<vtkm::FloatDefault, 3> min,
vtkm::Vec<vtkm::FloatDefault, 3> max,
vtkm::Vec<vtkm::Id, 3> dims)
: Min(min)
, Dims(dims)
, Dxdydz((max - Min) / Dims)
{
}
template <typename CoordVecType, typename IdType>
VTKM_EXEC void operator()(const CoordVecType& coord, IdType& label) const
{
vtkm::Vec<vtkm::Id, 3> ijk = (coord - Min) / Dxdydz;
ijk = vtkm::Max(ijk, vtkm::Id3(0));
ijk = vtkm::Min(ijk, this->Dims - vtkm::Id3(1));
label = ijk[0] + ijk[1] * Dims[0] + ijk[2] * Dims[0] * Dims[1];
}
private:
vtkm::Vec<vtkm::FloatDefault, 3> Min;
vtkm::Vec<vtkm::Id, 3> Dims;
vtkm::Vec<vtkm::FloatDefault, 3> Dxdydz;
};
} // internal
void PointLocatorUniformGrid::Build()
{
if (this->IsRangeInvalid())
{
this->Range = this->GetCoordinates().GetRange();
}
auto rmin = vtkm::make_Vec(static_cast<vtkm::FloatDefault>(this->Range[0].Min),
static_cast<vtkm::FloatDefault>(this->Range[1].Min),
static_cast<vtkm::FloatDefault>(this->Range[2].Min));
auto rmax = vtkm::make_Vec(static_cast<vtkm::FloatDefault>(this->Range[0].Max),
static_cast<vtkm::FloatDefault>(this->Range[1].Max),
static_cast<vtkm::FloatDefault>(this->Range[2].Max));
// generate unique id for each input point
vtkm::cont::ArrayHandleCounting<vtkm::Id> pointCounting(
0, 1, this->GetCoordinates().GetNumberOfValues());
vtkm::cont::ArrayCopy(pointCounting, this->PointIds);
using internal::BinPointsWorklet;
// bin points into cells and give each of them the cell id.
vtkm::cont::ArrayHandle<vtkm::Id> cellIds;
BinPointsWorklet cellIdWorklet(rmin, rmax, this->Dims);
vtkm::worklet::DispatcherMapField<BinPointsWorklet> dispatchCellId(cellIdWorklet);
dispatchCellId.Invoke(this->GetCoordinates(), cellIds);
// Group points of the same cell together by sorting them according to the cell ids
vtkm::cont::Algorithm::SortByKey(cellIds, this->PointIds);
// for each cell, find the lower and upper bound of indices to the sorted point ids.
vtkm::cont::ArrayHandleCounting<vtkm::Id> cell_ids_counting(
0, 1, this->Dims[0] * this->Dims[1] * this->Dims[2]);
vtkm::cont::Algorithm::UpperBounds(cellIds, cell_ids_counting, this->CellUpper);
vtkm::cont::Algorithm::LowerBounds(cellIds, cell_ids_counting, this->CellLower);
}
struct PointLocatorUniformGrid::PrepareExecutionObjectFunctor
{
template <typename DeviceAdapter>
VTKM_CONT bool operator()(DeviceAdapter,
const vtkm::cont::PointLocatorUniformGrid& self,
ExecutionObjectHandleType& handle) const
{
auto rmin = vtkm::make_Vec(static_cast<vtkm::FloatDefault>(self.Range[0].Min),
static_cast<vtkm::FloatDefault>(self.Range[1].Min),
static_cast<vtkm::FloatDefault>(self.Range[2].Min));
auto rmax = vtkm::make_Vec(static_cast<vtkm::FloatDefault>(self.Range[0].Max),
static_cast<vtkm::FloatDefault>(self.Range[1].Max),
static_cast<vtkm::FloatDefault>(self.Range[2].Max));
vtkm::exec::PointLocatorUniformGrid<DeviceAdapter>* h =
new vtkm::exec::PointLocatorUniformGrid<DeviceAdapter>(
rmin,
rmax,
self.Dims,
self.GetCoordinates().GetData().PrepareForInput(DeviceAdapter()),
self.PointIds.PrepareForInput(DeviceAdapter()),
self.CellLower.PrepareForInput(DeviceAdapter()),
self.CellUpper.PrepareForInput(DeviceAdapter()));
handle.Reset(h);
return true;
}
};
VTKM_CONT void PointLocatorUniformGrid::PrepareExecutionObject(
ExecutionObjectHandleType& execObjHandle,
vtkm::cont::DeviceAdapterId deviceId) const
{
const bool success =
vtkm::cont::TryExecuteOnDevice(deviceId, PrepareExecutionObjectFunctor(), *this, execObjHandle);
if (!success)
{
throwFailedRuntimeDeviceTransfer("PointLocatorUniformGrid", deviceId);
}
}
}
} // vtkm::cont