Add implementation of ArrayRangeCompute for UnknownArrayHandle

This allows you to easily compute the range of any ArrayHandle, even if
you don't know the type.

A unit test for ArrayRangeCompute was also added.
This commit is contained in:
Kenneth Moreland 2021-02-16 14:22:55 -07:00
parent 3aa2c7521a
commit 2a41428fe4
6 changed files with 395 additions and 22 deletions

@ -0,0 +1,18 @@
# `ArrayRangeCompute` works on any array type without compiling device code
Originally, `ArrayRangeCompute` required you to know specifically the
`ArrayHandle` type (value type and storage type) and to compile using any
device compiler. The method is changed to include only overloads that have
precompiled versions of `ArrayRangeCompute`.
Additionally, an `ArrayRangeCompute` overload that takes an
`UnknownArrayHandle` has been added. In addition to allowing you to compute
the range of arrays of unknown types, this implementation of
`ArrayRangeCompute` serves as a fallback for `ArrayHandle` types that are
not otherwise explicitly supported.
If you really want to make sure that you compute the range directly on an
`ArrayHandle` of a particular type, you can include
`ArrayRangeComputeTemplate.h`, which contains a templated overload of
`ArrayRangeCompute` that directly computes the range of an `ArrayHandle`.
Including this header requires compiling for device code.

@ -110,6 +110,7 @@ struct TypeTraits<const T> : TypeTraits<T>
VTKM_BASIC_REAL_TYPE(float)
VTKM_BASIC_REAL_TYPE(double)
VTKM_BASIC_INTEGER_TYPE(bool)
VTKM_BASIC_INTEGER_TYPE(char)
VTKM_BASIC_INTEGER_TYPE(signed char)
VTKM_BASIC_INTEGER_TYPE(unsigned char)

@ -10,6 +10,8 @@
#include <vtkm/cont/ArrayRangeComputeTemplate.h>
#include <vtkm/TypeList.h>
namespace vtkm
{
namespace cont
@ -78,6 +80,8 @@ VTKM_ARRAY_RANGE_COMPUTE_IMPL_ALL_VEC(2, vtkm::cont::StorageTagSOA);
VTKM_ARRAY_RANGE_COMPUTE_IMPL_ALL_VEC(3, vtkm::cont::StorageTagSOA);
VTKM_ARRAY_RANGE_COMPUTE_IMPL_ALL_VEC(4, vtkm::cont::StorageTagSOA);
VTKM_ARRAY_RANGE_COMPUTE_IMPL_ALL_SCALAR_T(vtkm::cont::StorageTagStride);
VTKM_ARRAY_RANGE_COMPUTE_IMPL_VEC(vtkm::Float32, 3, vtkm::cont::StorageTagXGCCoordinates);
VTKM_ARRAY_RANGE_COMPUTE_IMPL_VEC(vtkm::Float64, 3, vtkm::cont::StorageTagXGCCoordinates);
@ -111,7 +115,7 @@ vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
return rangeArray;
}
VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
const vtkm::cont::ArrayHandle<vtkm::Id, vtkm::cont::StorageTagIndex>& input,
vtkm::cont::DeviceAdapterId)
{
@ -120,5 +124,129 @@ VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
result.WritePortal().Set(0, vtkm::Range(0, input.GetNumberOfValues() - 1));
return result;
}
namespace
{
using AllScalars = vtkm::TypeListBaseC;
template <typename vtkm::IdComponent N>
struct VecTransform
{
template <typename T>
using type = vtkm::Vec<T, N>;
};
template <vtkm::IdComponent N>
using AllVecOfSize = vtkm::ListTransform<AllScalars, VecTransform<N>::template type>;
using AllVec = vtkm::ListAppend<AllVecOfSize<2>, AllVecOfSize<3>, AllVecOfSize<4>>;
using AllTypes = vtkm::ListAppend<AllScalars, AllVec>;
struct ComputeRangeFunctor
{
// Used with UnknownArrayHandle::CastAndCallForTypes
template <typename T, typename S>
void operator()(const vtkm::cont::ArrayHandle<T, S>& array,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::ArrayHandle<vtkm::Range>& ranges) const
{
ranges = vtkm::cont::ArrayRangeCompute(array, device);
}
// Used with vtkm::ListForEach to get components
template <typename T>
void operator()(T,
const vtkm::cont::UnknownArrayHandle& array,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::ArrayHandle<vtkm::Range>& ranges,
bool& success) const
{
if (!success && array.IsBaseComponentType<T>())
{
vtkm::IdComponent numComponents = array.GetNumberOfComponentsFlat();
ranges.Allocate(numComponents);
auto rangePortal = ranges.WritePortal();
for (vtkm::IdComponent componentI = 0; componentI < numComponents; ++componentI)
{
vtkm::cont::ArrayHandleStride<T> componentArray = array.ExtractComponent<T>(componentI);
vtkm::cont::ArrayHandle<vtkm::Range> componentRange =
vtkm::cont::ArrayRangeCompute(componentArray, device);
rangePortal.Set(componentI, componentRange.ReadPortal().Get(0));
}
success = true;
}
}
};
template <typename TList, typename Storage>
vtkm::cont::ArrayHandle<vtkm::Range> ComputeForStorage(const vtkm::cont::UnknownArrayHandle& array,
vtkm::cont::DeviceAdapterId device)
{
vtkm::cont::ArrayHandle<vtkm::Range> ranges;
array.CastAndCallForTypes<TList, vtkm::List<Storage>>(ComputeRangeFunctor{}, device, ranges);
return ranges;
}
} // anonymous namespace
vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(const vtkm::cont::UnknownArrayHandle& array,
vtkm::cont::DeviceAdapterId device)
{
// First, try fast-paths of precompiled array types common(ish) in fields.
try
{
if (array.IsStorageType<vtkm::cont::StorageTagBasic>())
{
return ComputeForStorage<AllTypes, vtkm::cont::StorageTagBasic>(array, device);
}
if (array.IsStorageType<vtkm::cont::StorageTagSOA>())
{
return ComputeForStorage<AllVec, vtkm::cont::StorageTagSOA>(array, device);
}
if (array.IsStorageType<vtkm::cont::StorageTagXGCCoordinates>())
{
return ComputeForStorage<vtkm::TypeListFieldScalar, vtkm::cont::StorageTagXGCCoordinates>(
array, device);
}
if (array.IsStorageType<vtkm::cont::ArrayHandleUniformPointCoordinates::StorageTag>())
{
vtkm::cont::ArrayHandleUniformPointCoordinates uniformPoints;
array.AsArrayHandle(uniformPoints);
return vtkm::cont::ArrayRangeCompute(uniformPoints, device);
}
using CartesianProductStorage =
vtkm::cont::StorageTagCartesianProduct<vtkm::cont::StorageTagBasic,
vtkm::cont::StorageTagBasic,
vtkm::cont::StorageTagBasic>;
if (array.IsStorageType<CartesianProductStorage>())
{
return ComputeForStorage<vtkm::TypeListFieldScalar, CartesianProductStorage>(array, device);
}
if (array.IsStorageType<vtkm::cont::StorageTagConstant>())
{
return ComputeForStorage<AllTypes, vtkm::cont::StorageTagConstant>(array, device);
}
if (array.IsStorageType<vtkm::cont::StorageTagCounting>())
{
return ComputeForStorage<AllTypes, vtkm::cont::StorageTagCounting>(array, device);
}
if (array.IsStorageType<vtkm::cont::StorageTagIndex>())
{
return ArrayRangeCompute(array.AsArrayHandle<vtkm::cont::ArrayHandleIndex>(), device);
}
}
catch (vtkm::cont::ErrorBadValue&)
{
// If a cast/call failed, try falling back to a more general implementation.
}
vtkm::cont::ArrayHandle<vtkm::Range> ranges;
bool success = false;
vtkm::ListForEach(ComputeRangeFunctor{}, AllScalars{}, array, device, ranges, success);
return ranges;
}
}
} // namespace vtkm::cont

@ -20,9 +20,11 @@
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandleSOA.h>
#include <vtkm/cont/ArrayHandleStride.h>
#include <vtkm/cont/ArrayHandleUniformPointCoordinates.h>
#include <vtkm/cont/ArrayHandleXGCCoordinates.h>
#include <vtkm/cont/DeviceAdapterTag.h>
#include <vtkm/cont/UnknownArrayHandle.h>
namespace vtkm
{
@ -51,6 +53,10 @@ namespace cont
/// that will compile for any `ArrayHandle` type not already handled.
///
VTKM_CONT_EXPORT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
const vtkm::cont::UnknownArrayHandle& array,
vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny{});
#define VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_T(T, Storage) \
VTKM_CONT_EXPORT \
VTKM_CONT \
@ -104,6 +110,8 @@ VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_ALL_VEC(2, vtkm::cont::StorageTagSOA);
VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_ALL_VEC(3, vtkm::cont::StorageTagSOA);
VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_ALL_VEC(4, vtkm::cont::StorageTagSOA);
VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_ALL_SCALAR_T(vtkm::cont::StorageTagStride);
VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_VEC(vtkm::Float32, 3, vtkm::cont::StorageTagXGCCoordinates);
VTK_M_ARRAY_RANGE_COMPUTE_EXPORT_VEC(vtkm::Float64, 3, vtkm::cont::StorageTagXGCCoordinates);
@ -117,25 +125,6 @@ VTKM_CONT_EXPORT VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeComput
vtkm::cont::ArrayHandleUniformPointCoordinates::StorageTag>& array,
vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny());
// Implementation of composite vectors
VTKM_CONT_EXPORT
VTKM_CONT
vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
const vtkm::cont::ArrayHandle<vtkm::Vec3f_32,
typename vtkm::cont::ArrayHandleCompositeVector<
vtkm::cont::ArrayHandle<vtkm::Float32>,
vtkm::cont::ArrayHandle<vtkm::Float32>,
vtkm::cont::ArrayHandle<vtkm::Float32>>::StorageTag>& input,
vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny());
VTKM_CONT_EXPORT VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
const vtkm::cont::ArrayHandle<vtkm::Vec3f_64,
typename vtkm::cont::ArrayHandleCompositeVector<
vtkm::cont::ArrayHandle<vtkm::Float64>,
vtkm::cont::ArrayHandle<vtkm::Float64>,
vtkm::cont::ArrayHandle<vtkm::Float64>>::StorageTag>& input,
vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny());
// Implementation of cartesian products
template <typename T, typename ST1, typename ST2, typename ST3>
VTKM_CONT inline vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
@ -204,7 +193,7 @@ VTKM_CONT inline vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
if (portal.GetNumberOfValues() > 0)
{
T first = input.ReadPortal().Get(0);
T last = input.ReadPortal().Get(portal.GetNumberOfValues() - 1);
T last = input.ReadPortal().Get(input.GetNumberOfValues() - 1);
for (vtkm::IdComponent cIndex = 0; cIndex < Traits::NUM_COMPONENTS; ++cIndex)
{
auto firstComponent = Traits::GetComponent(first, cIndex);
@ -226,7 +215,7 @@ VTKM_CONT inline vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
}
// Implementation of index arrays
VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
VTKM_CONT_EXPORT vtkm::cont::ArrayHandle<vtkm::Range> ArrayRangeCompute(
const vtkm::cont::ArrayHandle<vtkm::Id, vtkm::cont::StorageTagIndex>& input,
vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny{});
///@}

@ -55,6 +55,7 @@ set(unit_tests
UnitTestArrayHandleVirtual.cxx
UnitTestArrayHandleXGCCoordinates.cxx
UnitTestArrayPortalToIterators.cxx
UnitTestArrayRangeCompute.cxx
UnitTestCellLocatorChooser.cxx
UnitTestCellLocatorGeneral.cxx
UnitTestCellSet.cxx

@ -0,0 +1,236 @@
//============================================================================
// 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/ArrayCopy.h>
#include <vtkm/cont/ArrayHandleBasic.h>
#include <vtkm/cont/ArrayHandleCartesianProduct.h>
#include <vtkm/cont/ArrayHandleCompositeVector.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleExtractComponent.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandleRandomUniformReal.h>
#include <vtkm/cont/ArrayHandleSOA.h>
#include <vtkm/cont/ArrayHandleStride.h>
#include <vtkm/cont/ArrayHandleUniformPointCoordinates.h>
#include <vtkm/cont/ArrayRangeCompute.h>
#include <vtkm/Math.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
constexpr vtkm::Id ARRAY_SIZE = 20;
template <typename T, typename S>
void CheckRange(const vtkm::cont::ArrayHandle<T, S>& array, bool checkUnknown = true)
{
using Traits = vtkm::VecTraits<T>;
vtkm::IdComponent numComponents = Traits::NUM_COMPONENTS;
vtkm::cont::ArrayHandle<vtkm::Range> computedRangeArray = vtkm::cont::ArrayRangeCompute(array);
VTKM_TEST_ASSERT(computedRangeArray.GetNumberOfValues() == numComponents);
auto computedRangePortal = computedRangeArray.ReadPortal();
auto portal = array.ReadPortal();
for (vtkm::IdComponent component = 0; component < numComponents; ++component)
{
vtkm::Range computedRange = computedRangePortal.Get(component);
vtkm::Range expectedRange;
for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); ++index)
{
T value = portal.Get(index);
expectedRange.Include(Traits::GetComponent(value, component));
}
VTKM_TEST_ASSERT(!vtkm::IsNan(computedRange.Min));
VTKM_TEST_ASSERT(!vtkm::IsNan(computedRange.Max));
VTKM_TEST_ASSERT(test_equal(expectedRange, computedRange));
}
if (checkUnknown)
{
computedRangeArray = vtkm::cont::ArrayRangeCompute(vtkm::cont::UnknownArrayHandle{ array });
VTKM_TEST_ASSERT(computedRangeArray.GetNumberOfValues() == numComponents);
computedRangePortal = computedRangeArray.ReadPortal();
portal = array.ReadPortal();
for (vtkm::IdComponent component = 0; component < numComponents; ++component)
{
vtkm::Range computedRange = computedRangePortal.Get(component);
vtkm::Range expectedRange;
for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); ++index)
{
T value = portal.Get(index);
expectedRange.Include(Traits::GetComponent(value, component));
}
VTKM_TEST_ASSERT(!vtkm::IsNan(computedRange.Min));
VTKM_TEST_ASSERT(!vtkm::IsNan(computedRange.Max));
VTKM_TEST_ASSERT(test_equal(expectedRange, computedRange));
}
}
}
template <typename T, typename S>
void FillArray(vtkm::cont::ArrayHandle<T, S>& array)
{
using Traits = vtkm::VecTraits<T>;
vtkm::IdComponent numComponents = Traits::NUM_COMPONENTS;
vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(T{}, ARRAY_SIZE), array);
for (vtkm::IdComponent component = 0; component < numComponents; ++component)
{
vtkm::cont::ArrayHandleRandomUniformReal<vtkm::Float64> randomArray(ARRAY_SIZE);
auto dest = vtkm::cont::make_ArrayHandleExtractComponent(array, component);
vtkm::cont::ArrayCopy(randomArray, dest);
}
}
template <typename T>
void TestBasicArray()
{
std::cout << "Checking basic array" << std::endl;
vtkm::cont::ArrayHandleBasic<T> array;
FillArray(array);
CheckRange(array);
}
template <typename T>
void TestSOAArray(vtkm::TypeTraitsVectorTag)
{
std::cout << "Checking SOA array" << std::endl;
vtkm::cont::ArrayHandleSOA<T> array;
FillArray(array);
CheckRange(array);
}
template <typename T>
void TestSOAArray(vtkm::TypeTraitsScalarTag)
{
// Skip test.
}
template <typename T>
void TestStrideArray(vtkm::TypeTraitsScalarTag)
{
std::cout << "Checking stride array" << std::endl;
vtkm::cont::ArrayHandleBasic<T> array;
FillArray(array);
CheckRange(vtkm::cont::ArrayHandleStride<T>(array, ARRAY_SIZE / 2, 2, 1));
}
template <typename T>
void TestCartesianProduct(vtkm::TypeTraitsScalarTag)
{
std::cout << "Checking Cartesian product" << std::endl;
vtkm::cont::ArrayHandleBasic<T> array0;
FillArray(array0);
vtkm::cont::ArrayHandleBasic<T> array1;
FillArray(array1);
vtkm::cont::ArrayHandleBasic<T> array2;
FillArray(array2);
CheckRange(vtkm::cont::make_ArrayHandleCartesianProduct(array0, array1, array2));
}
template <typename T>
void TestCartesianProduct(vtkm::TypeTraitsVectorTag)
{
// Skip test.
}
template <typename T>
void TestComposite(vtkm::TypeTraitsScalarTag)
{
std::cout << "Checking composite vector" << std::endl;
vtkm::cont::ArrayHandleBasic<T> array0;
FillArray(array0);
vtkm::cont::ArrayHandleBasic<T> array1;
FillArray(array1);
vtkm::cont::ArrayHandleBasic<T> array2;
FillArray(array2);
CheckRange(vtkm::cont::make_ArrayHandleCompositeVector(array0, array1, array2));
}
template <typename T>
void TestComposite(vtkm::TypeTraitsVectorTag)
{
// Skip test.
}
template <typename T>
void TestConstant()
{
std::cout << "Checking constant array" << std::endl;
CheckRange(vtkm::cont::make_ArrayHandleConstant(TestValue(10, T{}), ARRAY_SIZE));
}
template <typename T>
void TestCounting(std::true_type vtkmNotUsed(is_signed))
{
std::cout << "Checking counting array" << std::endl;
CheckRange(vtkm::cont::make_ArrayHandleCounting(TestValue(10, T{}), T{ 1 }, ARRAY_SIZE));
std::cout << "Checking counting backward array" << std::endl;
CheckRange(vtkm::cont::make_ArrayHandleCounting(TestValue(10, T{}), T{ -1 }, ARRAY_SIZE));
}
template <typename T>
void TestCounting(std::false_type vtkmNotUsed(is_signed))
{
// Skip test
}
void TestIndex()
{
std::cout << "Checking index array" << std::endl;
CheckRange(vtkm::cont::make_ArrayHandleIndex(ARRAY_SIZE));
}
void TestUniformPointCoords()
{
std::cout << "Checking uniform point coordinates" << std::endl;
CheckRange(
vtkm::cont::ArrayHandleUniformPointCoordinates(vtkm::Id3(ARRAY_SIZE, ARRAY_SIZE, ARRAY_SIZE)));
}
struct DoTestFunctor
{
template <typename T>
void operator()(T) const
{
TestBasicArray<T>();
TestSOAArray<T>(typename vtkm::TypeTraits<T>::DimensionalityTag{});
TestCartesianProduct<T>(typename vtkm::TypeTraits<T>::DimensionalityTag{});
TestComposite<T>(typename vtkm::TypeTraits<T>::DimensionalityTag{});
TestConstant<T>();
TestCounting<T>(typename std::is_signed<typename vtkm::VecTraits<T>::ComponentType>::type{});
}
};
void DoTest()
{
vtkm::testing::Testing::TryTypes(DoTestFunctor{});
std::cout << "*** Specific arrays *****************" << std::endl;
TestIndex();
TestUniformPointCoords();
}
} // anonymous namespace
int UnitTestArrayRangeCompute(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(DoTest, argc, argv);
}