Modify VariantAH::AsVirtual to cast to new ValueType if needed.

E.g:

```
    ArrayHandle<Float64> doubleArray;
    VariantArrayHandle varHandle{doubleArray};
    ArrayHandleVirtual<Float32> = varHandle.AsVirtual<Float32>();
```

If there is a loss in range and/or precision, a warning is logged. If
the ValueTypes are Vecs with mismatched widths, an ErrorBadType is thrown.
Internally, an ArrayHandleCast is used between the VariantArrayHandle's
stored array and the ArrayHandleVirtual.
This commit is contained in:
Allison Vacanti 2019-03-15 15:15:47 -04:00
parent b44d1b2d6b
commit e1f5c4dd9b
5 changed files with 230 additions and 22 deletions

@ -0,0 +1,6 @@
# VariantArrayHandle::AsVirtual<T>() performs casting
The AsVirtual<T> method of VariantArrayHandle now works for any arithmetic type,
not just the actual type of the underlying array. This works by inserting an
ArrayHandleCast between the underlying concrete array and the new
ArrayHandleVirtual when needed.

@ -24,6 +24,13 @@
#include <vtkm/cont/ArrayHandleTransform.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/BaseComponent.h>
#include <vtkm/Range.h>
#include <limits>
namespace vtkm
{
namespace cont
@ -62,6 +69,60 @@ public:
ArrayHandleCast(const ArrayHandleType& handle)
: Superclass(handle)
{
this->ValidateTypeCast<typename ArrayHandleType::ValueType>();
}
private:
// Log warnings if type cast is valid but lossy:
template <typename SrcValueType>
VTKM_CONT static typename std::enable_if<!std::is_same<T, SrcValueType>::value>::type
ValidateTypeCast()
{
#ifdef VTKM_ENABLE_LOGGING
using DstValueType = T;
using SrcComp = typename vtkm::BaseComponent<SrcValueType>::Type;
using DstComp = typename vtkm::BaseComponent<DstValueType>::Type;
using SrcLimits = std::numeric_limits<SrcComp>;
using DstLimits = std::numeric_limits<DstComp>;
const vtkm::Range SrcRange{ SrcLimits::min(), SrcLimits::max() };
const vtkm::Range DstRange{ DstLimits::min(), DstLimits::max() };
const bool RangeLoss = (SrcRange.Max > DstRange.Max || SrcRange.Min < DstRange.Min);
const bool PrecLoss = SrcLimits::digits > DstLimits::digits;
if (RangeLoss && PrecLoss)
{
VTKM_LOG_F(vtkm::cont::LogLevel::Warn,
"VariantArrayHandle::AsVirtual: Casting ComponentType of "
"%s to %s reduces range and precision.",
vtkm::cont::TypeToString<SrcValueType>().c_str(),
vtkm::cont::TypeToString<DstValueType>().c_str());
}
else if (RangeLoss)
{
VTKM_LOG_F(vtkm::cont::LogLevel::Warn,
"VariantArrayHandle::AsVirtual: Casting ComponentType of "
"%s to %s reduces range.",
vtkm::cont::TypeToString<SrcValueType>().c_str(),
vtkm::cont::TypeToString<DstValueType>().c_str());
}
else if (PrecLoss)
{
VTKM_LOG_F(vtkm::cont::LogLevel::Warn,
"VariantArrayHandle::AsVirtual: Casting ComponentType of "
"%s to %s reduces precision.",
vtkm::cont::TypeToString<SrcValueType>().c_str(),
vtkm::cont::TypeToString<DstValueType>().c_str());
}
#endif // Logging
}
template <typename SrcValueType>
VTKM_CONT static typename std::enable_if<std::is_same<T, SrcValueType>::value>::type
ValidateTypeCast()
{
//no-op if types match
}
};

@ -136,13 +136,22 @@ public:
return internal::variant::Cast<ArrayHandleType>(this->ArrayContainer.get());
}
/// Returns this array cast to a \c ArrayHandleVirtual of the given type. Throws \c
/// ErrorBadType if the cast does not work.
/// Returns this array cast to a \c ArrayHandleVirtual of the given type.
/// This will perform type conversions as necessary, and will log warnings
/// if the conversion is lossy.
///
template <typename T>
/// This method internally uses CastAndCall. A custom storage tag list may
/// be specified in the second template parameter, which will be passed to
/// the CastAndCall.
///
template <typename T, typename StorageTagList = VTKM_DEFAULT_STORAGE_LIST_TAG>
VTKM_CONT vtkm::cont::ArrayHandleVirtual<T> AsVirtual() const
{
return internal::variant::Cast<vtkm::cont::ArrayHandleVirtual<T>>(this->ArrayContainer.get());
VTKM_IS_LIST_TAG(StorageTagList);
vtkm::cont::internal::variant::ForceCastToVirtual caster;
vtkm::cont::ArrayHandleVirtual<T> output;
this->CastAndCall(StorageTagList{}, caster, output);
return output;
}
/// Given a references to an ArrayHandle object, casts this array to the

@ -22,15 +22,17 @@
#include <vtkm/cont/vtkm_cont_export.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleVirtual.h>
#include <vtkm/cont/ArrayHandleVirtual.hxx>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/VecTraits.h>
#include <memory>
#include <sstream>
#include <typeindex>
namespace vtkm
{
namespace cont
@ -221,7 +223,6 @@ struct VTKM_ALWAYS_EXPORT Caster<T, vtkm::cont::StorageTagVirtual>
}
};
template <typename ArrayHandleType>
VTKM_CONT ArrayHandleType Cast(const VariantArrayHandleContainerBase* container)
{ //container could be nullptr
@ -231,6 +232,54 @@ VTKM_CONT ArrayHandleType Cast(const VariantArrayHandleContainerBase* container)
auto ret = Caster<Type, Storage>{}(container);
return ArrayHandleType(std::move(ret));
}
struct ForceCastToVirtual
{
template <typename SrcValueType, typename Storage, typename DstValueType>
VTKM_CONT typename std::enable_if<std::is_same<SrcValueType, DstValueType>::value>::type
operator()(const vtkm::cont::ArrayHandle<SrcValueType, Storage>& input,
vtkm::cont::ArrayHandleVirtual<DstValueType>& output) const
{ // ValueTypes match
output = vtkm::cont::make_ArrayHandleVirtual<DstValueType>(input);
}
template <typename SrcValueType, typename Storage, typename DstValueType>
VTKM_CONT typename std::enable_if<!std::is_same<SrcValueType, DstValueType>::value>::type
operator()(const vtkm::cont::ArrayHandle<SrcValueType, Storage>& input,
vtkm::cont::ArrayHandleVirtual<DstValueType>& output) const
{ // ValueTypes do not match
this->ValidateWidthAndCast<SrcValueType, DstValueType>(input, output);
}
private:
template <typename S,
typename D,
typename InputType,
vtkm::IdComponent SSize = vtkm::VecTraits<S>::NUM_COMPONENTS,
vtkm::IdComponent DSize = vtkm::VecTraits<D>::NUM_COMPONENTS>
VTKM_CONT typename std::enable_if<SSize == DSize>::type ValidateWidthAndCast(
const InputType& input,
vtkm::cont::ArrayHandleVirtual<D>& output) const
{ // number of components match
auto casted = vtkm::cont::make_ArrayHandleCast<D>(input);
output = vtkm::cont::make_ArrayHandleVirtual<D>(casted);
}
template <typename S,
typename D,
vtkm::IdComponent SSize = vtkm::VecTraits<S>::NUM_COMPONENTS,
vtkm::IdComponent DSize = vtkm::VecTraits<D>::NUM_COMPONENTS>
VTKM_CONT typename std::enable_if<SSize != DSize>::type ValidateWidthAndCast(
const ArrayHandleBase&,
ArrayHandleBase&) const
{ // number of components do not match
std::ostringstream str;
str << "VariantArrayHandle::AsVirtual: Cannot cast from " << vtkm::cont::TypeToString<S>()
<< " to " << vtkm::cont::TypeToString<D>() << "; "
"number of components must match exactly.";
throw vtkm::cont::ErrorBadType(str.str());
}
};
}
}
}

@ -41,6 +41,7 @@
#include <sstream>
#include <string>
#include <type_traits>
#include <typeinfo>
namespace vtkm
@ -266,11 +267,85 @@ void CheckCastToArrayHandle(const ArrayHandleType& array)
ArrayHandleType castArray2 = arrayVariant.Cast<ArrayHandleType>();
VTKM_TEST_ASSERT(array == castArray2, "Did not get back same array.");
}
using T = typename ArrayHandleType::ValueType;
vtkm::cont::ArrayHandleVirtual<T> castArray3 = arrayVariant.AsVirtual<T>();
VTKM_TEST_ASSERT(castArray3.GetNumberOfValues() == castArray2.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
// A vtkm::Vec if NumComps > 1, otherwise a scalar
template <typename T, vtkm::IdComponent NumComps>
using VecOrScalar = typename std::conditional<(NumComps > 1), vtkm::Vec<T, NumComps>, T>::type;
template <typename ArrayType>
void CheckCastToVirtualArrayHandle(const ArrayType& array)
{
VTKM_IS_ARRAY_HANDLE(ArrayType);
using ValueType = typename ArrayType::ValueType;
using VTraits = vtkm::VecTraits<ValueType>;
using ComponentType = typename VTraits::ComponentType;
static constexpr vtkm::IdComponent NumComps = VTraits::NUM_COMPONENTS;
using Storage = typename ArrayType::StorageTag;
using StorageList = vtkm::ListTagAppendUnique<VTKM_DEFAULT_STORAGE_LIST_TAG, Storage>;
using TypeList = vtkm::ListTagAppendUnique<VTKM_DEFAULT_TYPE_LIST_TAG, ValueType>;
using VariantArrayType = vtkm::cont::VariantArrayHandleBase<TypeList>;
VariantArrayType arrayVariant = array;
{
auto testArray = arrayVariant.template AsVirtual<ValueType, StorageList>();
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == array.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
}
{
auto testArray =
arrayVariant.template AsVirtual<VecOrScalar<vtkm::Int8, NumComps>, StorageList>();
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == array.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
}
{
auto testArray =
arrayVariant.template AsVirtual<VecOrScalar<vtkm::Int64, NumComps>, StorageList>();
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == array.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
}
{
auto testArray =
arrayVariant.template AsVirtual<VecOrScalar<vtkm::UInt64, NumComps>, StorageList>();
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == array.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
}
{
auto testArray =
arrayVariant.template AsVirtual<VecOrScalar<vtkm::Float32, NumComps>, StorageList>();
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == array.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
}
{
auto testArray =
arrayVariant.template AsVirtual<VecOrScalar<vtkm::Float64, NumComps>, StorageList>();
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == array.GetNumberOfValues(),
"Did not get back virtual array handle representation.");
}
bool threw = false;
try
{
arrayVariant.template AsVirtual<vtkm::Vec<ComponentType, NumComps + 1>, StorageList>();
}
catch (vtkm::cont::ErrorBadType&)
{
// caught expected exception
threw = true;
}
VTKM_TEST_ASSERT(threw,
"Casting to different vector width did not throw expected "
"ErrorBadType exception.");
}
template <typename T, typename ArrayVariantType>
@ -385,6 +460,13 @@ void TryUnusualTypeAndStorage()
}
}
template <typename ArrayHandleType>
void TryCastToArrayHandle(const ArrayHandleType& array)
{
CheckCastToArrayHandle(array);
CheckCastToVirtualArrayHandle(array);
}
void TryCastToArrayHandle()
{
std::cout << " Normal array handle." << std::endl;
@ -393,42 +475,43 @@ void TryCastToArrayHandle()
{
buffer[index] = TestValue(index, vtkm::Id());
}
vtkm::cont::ArrayHandle<vtkm::Id> array = vtkm::cont::make_ArrayHandle(buffer, ARRAY_SIZE);
CheckCastToArrayHandle(array);
TryCastToArrayHandle(array);
std::cout << " Cast array handle." << std::endl;
CheckCastToArrayHandle(vtkm::cont::make_ArrayHandleCast(array, vtkm::FloatDefault()));
TryCastToArrayHandle(vtkm::cont::make_ArrayHandleCast(array, vtkm::FloatDefault()));
std::cout << " Composite vector array handle." << std::endl;
CheckCastToArrayHandle(vtkm::cont::make_ArrayHandleCompositeVector(array, array));
TryCastToArrayHandle(vtkm::cont::make_ArrayHandleCompositeVector(array, array));
std::cout << " Constant array handle." << std::endl;
CheckCastToArrayHandle(vtkm::cont::make_ArrayHandleConstant(5, ARRAY_SIZE));
TryCastToArrayHandle(vtkm::cont::make_ArrayHandleConstant(5, ARRAY_SIZE));
std::cout << " Counting array handle." << std::endl;
vtkm::cont::ArrayHandleCounting<vtkm::Id> countingArray(ARRAY_SIZE - 1, -1, ARRAY_SIZE);
CheckCastToArrayHandle(countingArray);
TryCastToArrayHandle(countingArray);
std::cout << " Group vec array handle" << std::endl;
vtkm::cont::ArrayHandleGroupVec<vtkm::cont::ArrayHandle<vtkm::Id>, 2> groupVecArray(array);
CheckCastToArrayHandle(groupVecArray);
TryCastToArrayHandle(groupVecArray);
std::cout << " Implicit array handle." << std::endl;
CheckCastToArrayHandle(
TryCastToArrayHandle(
vtkm::cont::make_ArrayHandleImplicit(TestValueFunctor<vtkm::FloatDefault>(), ARRAY_SIZE));
std::cout << " Index array handle." << std::endl;
CheckCastToArrayHandle(vtkm::cont::ArrayHandleIndex(ARRAY_SIZE));
TryCastToArrayHandle(vtkm::cont::ArrayHandleIndex(ARRAY_SIZE));
std::cout << " Permutation array handle." << std::endl;
CheckCastToArrayHandle(vtkm::cont::make_ArrayHandlePermutation(countingArray, array));
TryCastToArrayHandle(vtkm::cont::make_ArrayHandlePermutation(countingArray, array));
std::cout << " Transform array handle." << std::endl;
CheckCastToArrayHandle(
TryCastToArrayHandle(
vtkm::cont::make_ArrayHandleTransform(countingArray, TestValueFunctor<vtkm::FloatDefault>()));
std::cout << " Uniform point coordinates array handle." << std::endl;
CheckCastToArrayHandle(vtkm::cont::ArrayHandleUniformPointCoordinates(vtkm::Id3(ARRAY_SIZE)));
TryCastToArrayHandle(vtkm::cont::ArrayHandleUniformPointCoordinates(vtkm::Id3(ARRAY_SIZE)));
// std::cout << " Zip array handle." << std::endl;
// CheckCastToArrayHandle(vtkm::cont::make_ArrayHandleZip(countingArray, array));