vtk-m/vtkm/cont/ArrayHandleGroupVecVariable.h
Kenneth Moreland 9b992dcdde Add GetNumberOfComponentsFlat method to ArrayHandle
Getting the number of components (or the number of flattened components)
from an `ArrayHandle` is usually trivial. However, if the `ArrayHandle` is
special in that the number of components is specified at runtime, then it
becomes much more difficult to determine.

Getting the number of components is most important when extracting
component arrays (or reconstructions using component arrays) with
`UnknownArrayHandle`. Previously, `UnknownArrayHandle` used a hack to get
the number of components, which mostly worked but broke down when wrapping
a runtime array inside another array such as `ArrayHandleView`.

To prevent this issue, the ability to get the number of components has been
added to `ArrayHandle` proper. All `Storage` objects for `ArrayHandle`s now
need a method named `GetNumberOfComponentsFlat`. The implementation of this
method is usually trivial. The `ArrayHandle` template now also provides a
`GetNumberOfComponentsFlat` method that gets this information from the
`Storage`. This provides an easy access point for the `UnknownArrayHandle`
to pull this information.
2023-10-03 10:31:38 -04:00

401 lines
15 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.
//============================================================================
#ifndef vtk_m_cont_ArrayHandleGroupVecVariable_h
#define vtk_m_cont_ArrayHandleGroupVecVariable_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayPortal.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/Assert.h>
#include <vtkm/VecFromPortal.h>
namespace vtkm
{
namespace internal
{
template <typename ComponentsPortalType, typename OffsetsPortalType>
class VTKM_ALWAYS_EXPORT ArrayPortalGroupVecVariable
{
public:
using ComponentType = typename std::remove_const<typename ComponentsPortalType::ValueType>::type;
using ValueType = vtkm::VecFromPortal<ComponentsPortalType>;
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
ArrayPortalGroupVecVariable()
: ComponentsPortal()
, OffsetsPortal()
{
}
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
ArrayPortalGroupVecVariable(const ComponentsPortalType& componentsPortal,
const OffsetsPortalType& offsetsPortal)
: ComponentsPortal(componentsPortal)
, OffsetsPortal(offsetsPortal)
{
}
/// Copy constructor for any other ArrayPortalGroupVecVariable with a portal type
/// that can be copied to this portal type. This allows us to do any type
/// casting that the portals do (like the non-const to const cast).
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename OtherComponentsPortalType, typename OtherOffsetsPortalType>
VTKM_EXEC_CONT ArrayPortalGroupVecVariable(
const ArrayPortalGroupVecVariable<OtherComponentsPortalType, OtherOffsetsPortalType>& src)
: ComponentsPortal(src.GetComponentsPortal())
, OffsetsPortal(src.GetOffsetsPortal())
{
}
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
vtkm::Id GetNumberOfValues() const { return this->OffsetsPortal.GetNumberOfValues() - 1; }
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
ValueType Get(vtkm::Id index) const
{
vtkm::Id offsetIndex = this->OffsetsPortal.Get(index);
vtkm::Id nextOffsetIndex = this->OffsetsPortal.Get(index + 1);
return ValueType(this->ComponentsPortal,
static_cast<vtkm::IdComponent>(nextOffsetIndex - offsetIndex),
offsetIndex);
}
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
void Set(vtkm::Id index, const ValueType& value) const
{
if ((&value.GetPortal() == &this->ComponentsPortal) &&
(value.GetOffset() == this->OffsetsPortal.Get(index)))
{
// The ValueType (VecFromPortal) operates on demand. Thus, if you set
// something in the value, it has already been passed to the array.
}
else
{
// The value comes from somewhere else. Copy data in.
this->Get(index) = value;
}
}
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
const ComponentsPortalType& GetComponentsPortal() const { return this->ComponentsPortal; }
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
const OffsetsPortalType& GetOffsetsPortal() const { return this->OffsetsPortal; }
private:
ComponentsPortalType ComponentsPortal;
OffsetsPortalType OffsetsPortal;
};
}
} // namespace vtkm::internal
namespace vtkm
{
namespace cont
{
template <typename ComponentsStorageTag, typename OffsetsStorageTag>
struct VTKM_ALWAYS_EXPORT StorageTagGroupVecVariable
{
};
namespace internal
{
template <typename ComponentsPortal, typename ComponentsStorageTag, typename OffsetsStorageTag>
class Storage<vtkm::VecFromPortal<ComponentsPortal>,
vtkm::cont::StorageTagGroupVecVariable<ComponentsStorageTag, OffsetsStorageTag>>
{
using ComponentType = typename ComponentsPortal::ValueType;
using ComponentsStorage = vtkm::cont::internal::Storage<ComponentType, ComponentsStorageTag>;
using OffsetsStorage = vtkm::cont::internal::Storage<vtkm::Id, OffsetsStorageTag>;
using ComponentsArray = vtkm::cont::ArrayHandle<ComponentType, ComponentsStorageTag>;
using OffsetsArray = vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorageTag>;
VTKM_STATIC_ASSERT_MSG(
(std::is_same<ComponentsPortal, typename ComponentsStorage::WritePortalType>::value),
"Used invalid ComponentsPortal type with expected ComponentsStorageTag.");
struct Info
{
std::size_t OffsetsBuffersOffset;
};
VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> ComponentsBuffers(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
Info info = buffers[0].GetMetaData<Info>();
return std::vector<vtkm::cont::internal::Buffer>(buffers.begin() + 1,
buffers.begin() + info.OffsetsBuffersOffset);
}
VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> OffsetsBuffers(
const std::vector<vtkm::cont::internal::Buffer> buffers)
{
Info info = buffers[0].GetMetaData<Info>();
return std::vector<vtkm::cont::internal::Buffer>(buffers.begin() + info.OffsetsBuffersOffset,
buffers.end());
}
public:
VTKM_STORAGE_NO_RESIZE;
using ReadPortalType =
vtkm::internal::ArrayPortalGroupVecVariable<typename ComponentsStorage::ReadPortalType,
typename OffsetsStorage::ReadPortalType>;
using WritePortalType =
vtkm::internal::ArrayPortalGroupVecVariable<typename ComponentsStorage::WritePortalType,
typename OffsetsStorage::ReadPortalType>;
VTKM_CONT static vtkm::IdComponent GetNumberOfComponentsFlat(
const std::vector<vtkm::cont::internal::Buffer>&)
{
// Number of components can be variable.
return 0;
}
VTKM_CONT static vtkm::Id GetNumberOfValues(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return OffsetsStorage::GetNumberOfValues(OffsetsBuffers(buffers)) - 1;
}
VTKM_CONT static void Fill(const std::vector<vtkm::cont::internal::Buffer>&,
const vtkm::VecFromPortal<ComponentsPortal>&,
vtkm::Id,
vtkm::Id,
vtkm::cont::Token&)
{
throw vtkm::cont::ErrorBadType("Fill not supported for ArrayHandleGroupVecVariable.");
}
VTKM_CONT static ReadPortalType CreateReadPortal(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
{
return ReadPortalType(
ComponentsStorage::CreateReadPortal(ComponentsBuffers(buffers), device, token),
OffsetsStorage::CreateReadPortal(OffsetsBuffers(buffers), device, token));
}
VTKM_CONT static WritePortalType CreateWritePortal(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
{
return WritePortalType(
ComponentsStorage::CreateWritePortal(ComponentsBuffers(buffers), device, token),
OffsetsStorage::CreateReadPortal(OffsetsBuffers(buffers), device, token));
}
VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> CreateBuffers(
const ComponentsArray& componentsArray = ComponentsArray{},
const OffsetsArray& offsetsArray = OffsetsArray{})
{
Info info;
info.OffsetsBuffersOffset = 1 + componentsArray.GetBuffers().size();
return vtkm::cont::internal::CreateBuffers(info, componentsArray, offsetsArray);
}
VTKM_CONT static ComponentsArray GetComponentsArray(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return ComponentsArray(ComponentsBuffers(buffers));
}
VTKM_CONT static OffsetsArray GetOffsetsArray(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return OffsetsArray(OffsetsBuffers(buffers));
}
};
} // namespace internal
/// \brief Fancy array handle that groups values into vectors of different sizes.
///
/// It is sometimes the case that you need to run a worklet with an input or
/// output that has a different number of values per instance. For example, the
/// cells of a CellCetExplicit can have different numbers of points in each
/// cell. If inputting or outputting cells of this type, each instance of the
/// worklet might need a \c Vec of a different length. This fance array handle
/// takes an array of values and an array of offsets and groups the consecutive
/// values in Vec-like objects. The values are treated as tightly packed, so
/// that each Vec contains the values from one offset to the next. The last
/// value contains values from the last offset to the end of the array.
///
/// For example, if you have an array handle with the 9 values
/// 0,1,2,3,4,5,6,7,8 an offsets array handle with the 4 values 0,4,6,9 and give
/// them to an \c ArrayHandleGroupVecVariable, you get an array that looks like
/// it contains three values of Vec-like objects with the data [0,1,2,3],
/// [4,5], and [6,7,8].
///
/// Note that caution should be used with `ArrayHandleRuntimeVec` because the
/// size of the `Vec` values is not known at compile time. Thus, the value
/// type of this array is forced to a special `VecFromPortal` class that can cause
/// surprises if treated as a `Vec`. In particular, the static `NUM_COMPONENTS`
/// expression does not exist. Furthermore, new variables of type `VecFromPortal`
/// cannot be created. This means that simple operators like `+` will not work
/// because they require an intermediate object to be created. (Equal operators
/// like `+=` do work because they are given an existing variable to place the
/// output.)
///
/// The offsets array is often derived from a list of sizes for each of the
/// entries. You can use the convenience function \c
/// ConvertNumComponentsToOffsets to take an array of sizes (i.e. the number of
/// components for each entry) and get an array of offsets needed for \c
/// ArrayHandleGroupVecVariable.
///
template <typename ComponentsArrayHandleType, typename OffsetsArrayHandleType>
class ArrayHandleGroupVecVariable
: public vtkm::cont::ArrayHandle<
vtkm::VecFromPortal<typename ComponentsArrayHandleType::WritePortalType>,
vtkm::cont::StorageTagGroupVecVariable<typename ComponentsArrayHandleType::StorageTag,
typename OffsetsArrayHandleType::StorageTag>>
{
VTKM_IS_ARRAY_HANDLE(ComponentsArrayHandleType);
VTKM_IS_ARRAY_HANDLE(OffsetsArrayHandleType);
VTKM_STATIC_ASSERT_MSG(
(std::is_same<vtkm::Id, typename OffsetsArrayHandleType::ValueType>::value),
"ArrayHandleGroupVecVariable's offsets array must contain vtkm::Id values.");
public:
VTKM_ARRAY_HANDLE_SUBCLASS(
ArrayHandleGroupVecVariable,
(ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType>),
(vtkm::cont::ArrayHandle<
vtkm::VecFromPortal<typename ComponentsArrayHandleType::WritePortalType>,
vtkm::cont::StorageTagGroupVecVariable<typename ComponentsArrayHandleType::StorageTag,
typename OffsetsArrayHandleType::StorageTag>>));
using ComponentType = typename ComponentsArrayHandleType::ValueType;
VTKM_CONT
ArrayHandleGroupVecVariable(const ComponentsArrayHandleType& componentsArray,
const OffsetsArrayHandleType& offsetsArray)
: Superclass(StorageType::CreateBuffers(componentsArray, offsetsArray))
{
}
VTKM_CONT ComponentsArrayHandleType GetComponentsArray() const
{
return StorageType::GetComponentsArray(this->GetBuffers());
}
VTKM_CONT OffsetsArrayHandleType GetOffsetsArray() const
{
return StorageType::GetOffsetsArray(this->GetBuffers());
}
};
/// \c make_ArrayHandleGroupVecVariable is convenience function to generate an
/// ArrayHandleGroupVecVariable. It takes in an ArrayHandle of values and an
/// array handle of offsets and returns an array handle with consecutive
/// entries grouped in a Vec.
///
template <typename ComponentsArrayHandleType, typename OffsetsArrayHandleType>
VTKM_CONT vtkm::cont::ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType>
make_ArrayHandleGroupVecVariable(const ComponentsArrayHandleType& componentsArray,
const OffsetsArrayHandleType& offsetsArray)
{
return vtkm::cont::ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType>(
componentsArray, offsetsArray);
}
}
} // namespace vtkm::cont
//=============================================================================
// Specializations of serialization related classes
/// @cond SERIALIZATION
namespace vtkm
{
namespace cont
{
template <typename SAH, typename OAH>
struct SerializableTypeString<vtkm::cont::ArrayHandleGroupVecVariable<SAH, OAH>>
{
static VTKM_CONT const std::string& Get()
{
static std::string name = "AH_GroupVecVariable<" + SerializableTypeString<SAH>::Get() + "," +
SerializableTypeString<OAH>::Get() + ">";
return name;
}
};
template <typename SP, typename SST, typename OST>
struct SerializableTypeString<
vtkm::cont::ArrayHandle<vtkm::VecFromPortal<SP>,
vtkm::cont::StorageTagGroupVecVariable<SST, OST>>>
: SerializableTypeString<
vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<typename SP::ValueType, SST>,
vtkm::cont::ArrayHandle<vtkm::Id, OST>>>
{
};
}
} // vtkm::cont
namespace mangled_diy_namespace
{
template <typename SAH, typename OAH>
struct Serialization<vtkm::cont::ArrayHandleGroupVecVariable<SAH, OAH>>
{
private:
using Type = vtkm::cont::ArrayHandleGroupVecVariable<SAH, OAH>;
using BaseType = vtkm::cont::ArrayHandle<typename Type::ValueType, typename Type::StorageTag>;
public:
static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj)
{
vtkmdiy::save(bb, Type(obj).GetComponentsArray());
vtkmdiy::save(bb, Type(obj).GetOffsetsArray());
}
static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj)
{
SAH src;
OAH off;
vtkmdiy::load(bb, src);
vtkmdiy::load(bb, off);
obj = vtkm::cont::make_ArrayHandleGroupVecVariable(src, off);
}
};
template <typename SP, typename SST, typename OST>
struct Serialization<vtkm::cont::ArrayHandle<vtkm::VecFromPortal<SP>,
vtkm::cont::StorageTagGroupVecVariable<SST, OST>>>
: Serialization<
vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<typename SP::ValueType, SST>,
vtkm::cont::ArrayHandle<vtkm::Id, OST>>>
{
};
} // diy
/// @endcond SERIALIZATION
#endif //vtk_m_cont_ArrayHandleGroupVecVariable_h