Commit 2a41428f authored by Kenneth Moreland's avatar Kenneth Moreland
Browse files

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.
parent 3aa2c752
# `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
......
//============================================================================
// 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);
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment