From bb9e7a0d6f8ea2706f7e2e38c17cfee21467fdbe Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 17 Apr 2023 11:41:18 -0600 Subject: [PATCH] Handle any Vec size in VTKDataSetReader The legacy VTK file reader previously only supported a specific set of Vec lengths (i.e., 1, 2, 3, 4, 6, and 9). This is because a basic array handle has to have the vec length compiled in. However, the new `ArrayHandleRuntimeVec` feature is capable of reading in any vec-length and can be leveraged to read in arbitrarily sized vectors in field arrays. --- docs/changelog/read-any-vec-size.md | 7 + vtkm/cont/ArrayHandleRuntimeVec.h | 67 ++++++- vtkm/io/VTKDataSetReaderBase.cxx | 200 ++----------------- vtkm/io/internal/VTKDataSetTypes.h | 52 ++--- vtkm/io/testing/UnitTestVTKDataSetWriter.cxx | 22 +- 5 files changed, 122 insertions(+), 226 deletions(-) create mode 100644 docs/changelog/read-any-vec-size.md diff --git a/docs/changelog/read-any-vec-size.md b/docs/changelog/read-any-vec-size.md new file mode 100644 index 000000000..df6ff8204 --- /dev/null +++ b/docs/changelog/read-any-vec-size.md @@ -0,0 +1,7 @@ +# VTKDataSetReader handles any Vec size. + +The legacy VTK file reader previously only supported a specific set of Vec +lengths (i.e., 1, 2, 3, 4, 6, and 9). This is because a basic array handle +has to have the vec length compiled in. However, the new +`ArrayHandleRuntimeVec` feature is capable of reading in any vec-length and +can be leveraged to read in arbitrarily sized vectors in field arrays. diff --git a/vtkm/cont/ArrayHandleRuntimeVec.h b/vtkm/cont/ArrayHandleRuntimeVec.h index c7ef442fc..eab0c3f18 100644 --- a/vtkm/cont/ArrayHandleRuntimeVec.h +++ b/vtkm/cont/ArrayHandleRuntimeVec.h @@ -372,10 +372,13 @@ public: ///@} }; -/// \c make_ArrayHandleRuntimeVec is convenience function to generate an -/// ArrayHandleRuntimeVec. 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. +/// `make_ArrayHandleRuntimeVec` is convenience function to generate an +/// `ArrayHandleRuntimeVec`. It takes the number of components stored in +/// each value's `Vec`, which must be specified on the construction of +/// the `ArrayHandleRuntimeVec`. If not specified, the number of components +/// is set to 1. `make_ArrayHandleRuntimeVec` can also optionally take an +/// existing array of components, which will be grouped into `Vec` values +/// based on the specified number of components. /// template VTKM_CONT auto make_ArrayHandleRuntimeVec( @@ -402,6 +405,62 @@ VTKM_CONT auto make_ArrayHandleRuntimeVec( return make_ArrayHandleRuntimeVec(1, componentsArray); } +/// A convenience function for creating an `ArrayHandleRuntimeVec` from a standard C array. +/// +template +VTKM_CONT auto make_ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, + const T* array, + vtkm::Id numberOfValues, + vtkm::CopyFlag copy) +{ + return make_ArrayHandleRuntimeVec(numComponents, + vtkm::cont::make_ArrayHandle(array, numberOfValues, copy)); +} + +/// A convenience function to move a user-allocated array into an `ArrayHandleRuntimeVec`. +/// The provided array pointer will be reset to `nullptr`. +/// If the array was not allocated with the `new[]` operator, then deleter and reallocater +/// functions must be provided. +/// +template +VTKM_CONT auto make_ArrayHandleRuntimeVecMove( + vtkm::IdComponent numComponents, + T*& array, + vtkm::Id numberOfValues, + vtkm::cont::internal::BufferInfo::Deleter deleter = internal::SimpleArrayDeleter, + vtkm::cont::internal::BufferInfo::Reallocater reallocater = internal::SimpleArrayReallocater) +{ + return make_ArrayHandleRuntimeVec( + numComponents, vtkm::cont::make_ArrayHandleMove(array, numberOfValues, deleter, reallocater)); +} + +/// A convenience function for creating an `ArrayHandleRuntimeVec` from an `std::vector`. +/// +template +VTKM_CONT auto make_ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, + const std::vector& array, + vtkm::CopyFlag copy) +{ + return make_ArrayHandleRuntimeVec(numComponents, vtkm::cont::make_ArrayHandle(array, copy)); +} + +/// Move an `std::vector` into an `ArrayHandleRuntimeVec`. +/// +template +VTKM_CONT auto make_ArrayHandleRuntimeVecMove(vtkm::IdComponent numComponents, + std::vector&& array) +{ + return make_ArrayHandleRuntimeVec(numComponents, make_ArrayHandleMove(std::move(array))); +} + +template +VTKM_CONT auto make_ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, + std::vector&& array, + vtkm::CopyFlag vtkmNotUsed(copy)) +{ + return make_ArrayHandleRuntimeVecMove(numComponents, std::move(array)); +} + namespace internal { diff --git a/vtkm/io/VTKDataSetReaderBase.cxx b/vtkm/io/VTKDataSetReaderBase.cxx index 03f28c7bd..82605cadd 100644 --- a/vtkm/io/VTKDataSetReaderBase.cxx +++ b/vtkm/io/VTKDataSetReaderBase.cxx @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -35,162 +36,6 @@ inline void PrintVTKDataFileSummary(const vtkm::io::internal::VTKDataSetFile& df << std::endl; } -// Since Fields and DataSets store data in the default UnknownArrayHandle, convert -// the data to the closest type supported by default. The following will -// need to be updated if UnknownArrayHandle or TypeListCommon changes. -template -struct ClosestCommonType -{ - using Type = T; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; - -template -struct ClosestFloat -{ - using Type = T; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; - -template -vtkm::cont::UnknownArrayHandle CreateUnknownArrayHandle(const std::vector& vec) -{ - switch (vtkm::VecTraits::NUM_COMPONENTS) - { - case 1: - { - using CommonType = typename ClosestCommonType::Type; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() - << " is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() << "."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - portal.Set(i, static_cast(vec[static_cast(i)])); - } - - return vtkm::cont::UnknownArrayHandle(output); - } - case 2: - case 3: - case 9: - { - constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; - - using InComponentType = typename vtkm::VecTraits::ComponentType; - using OutComponentType = typename ClosestFloat::Type; - using CommonType = vtkm::Vec; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() << "[" - << vtkm::VecTraits::GetNumberOfComponents(T()) << "] " - << "is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() << "[" - << numComps << "]."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - CommonType outval = CommonType(); - for (vtkm::IdComponent j = 0; j < numComps; ++j) - { - outval[j] = static_cast( - vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); - } - portal.Set(i, outval); - } - - return vtkm::cont::UnknownArrayHandle(output); - } - default: - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); - return vtkm::cont::UnknownArrayHandle(vtkm::cont::ArrayHandle()); - } - } -} - } // anonymous namespace namespace vtkm @@ -676,27 +521,23 @@ void VTKDataSetReaderBase::ReadGlobalOrPedigreeIds(vtkm::cont::Field::Associatio class VTKDataSetReaderBase::SkipArrayVariant { public: - SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) + SkipArrayVariant(VTKDataSetReaderBase* reader, + std::size_t numElements, + vtkm::IdComponent numComponents) : Reader(reader) - , NumElements(numElements) + , TotalSize(numElements * static_cast(numComponents)) { } template void operator()(T) const { - this->Reader->SkipArray(this->NumElements, T()); - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); + this->Reader->SkipArray(this->TotalSize, T()); } protected: VTKDataSetReaderBase* Reader; - std::size_t NumElements; + std::size_t TotalSize; }; class VTKDataSetReaderBase::ReadArrayVariant : public SkipArrayVariant @@ -705,9 +546,11 @@ public: ReadArrayVariant(VTKDataSetReaderBase* reader, vtkm::cont::Field::Association association, std::size_t numElements, + vtkm::IdComponent numComponents, vtkm::cont::UnknownArrayHandle& data) - : SkipArrayVariant(reader, numElements) + : SkipArrayVariant(reader, numElements, numComponents) , Association(association) + , NumComponents(numComponents) , Data(&data) { } @@ -715,12 +558,13 @@ public: template void operator()(T) const { - std::vector buffer(this->NumElements); + std::vector buffer(this->TotalSize); this->Reader->ReadArray(buffer); if ((this->Association != vtkm::cont::Field::Association::Cells) || (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) { - *this->Data = CreateUnknownArrayHandle(buffer); + *this->Data = + vtkm::cont::make_ArrayHandleRuntimeVecMove(this->NumComponents, std::move(buffer)); } else { @@ -734,20 +578,14 @@ public: std::size_t inIndex = static_cast(permutation.Get(outIndex)); permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; } - *this->Data = CreateUnknownArrayHandle(permutedBuffer); + *this->Data = + vtkm::cont::make_ArrayHandleRuntimeVecMove(this->NumComponents, std::move(permutedBuffer)); } } - template - void operator()(vtkm::IdComponent numComponents, T) const - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for " << numComponents << " components not implemented. Skipping."); - SkipArrayVariant::operator()(numComponents, T()); - } - private: vtkm::cont::Field::Association Association; + vtkm::IdComponent NumComponents; vtkm::cont::UnknownArrayHandle* Data; }; @@ -764,8 +602,8 @@ void VTKDataSetReaderBase::DoSkipArrayVariant(std::string dataType, else { vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, SkipArrayVariant(this, numElements)); + vtkm::io::internal::SelectTypeAndCall(typeId, + SkipArrayVariant(this, numElements, numComponents)); } } @@ -791,7 +629,7 @@ vtkm::cont::UnknownArrayHandle VTKDataSetReaderBase::DoReadArrayVariant( { vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); + typeId, ReadArrayVariant(this, association, numElements, numComponents, data)); } return data; diff --git a/vtkm/io/internal/VTKDataSetTypes.h b/vtkm/io/internal/VTKDataSetTypes.h index 483573a6f..df7ad903c 100644 --- a/vtkm/io/internal/VTKDataSetTypes.h +++ b/vtkm/io/internal/VTKDataSetTypes.h @@ -170,73 +170,45 @@ struct DataTypeName static const char* Name() { return "double"; } }; -template -inline void SelectVecTypeAndCall(T, vtkm::IdComponent numComponents, const Functor& functor) -{ - switch (numComponents) - { - case 1: - functor(T()); - break; - case 2: - functor(vtkm::Vec()); - break; - case 3: - functor(vtkm::Vec()); - break; - case 4: - functor(vtkm::Vec()); - break; - case 9: - functor(vtkm::Vec()); - break; - default: - functor(numComponents, T()); - break; - } -} - template -inline void SelectTypeAndCall(DataType dtype, - vtkm::IdComponent numComponents, - const Functor& functor) +inline void SelectTypeAndCall(DataType dtype, Functor&& functor) { switch (dtype) { case DTYPE_BIT: - SelectVecTypeAndCall(DummyBitType(), numComponents, functor); + functor(DummyBitType()); break; case DTYPE_UNSIGNED_CHAR: - SelectVecTypeAndCall(vtkm::UInt8(), numComponents, functor); + functor(vtkm::UInt8()); break; case DTYPE_CHAR: - SelectVecTypeAndCall(vtkm::Int8(), numComponents, functor); + functor(vtkm::Int8()); break; case DTYPE_UNSIGNED_SHORT: - SelectVecTypeAndCall(vtkm::UInt16(), numComponents, functor); + functor(vtkm::UInt16()); break; case DTYPE_SHORT: - SelectVecTypeAndCall(vtkm::Int16(), numComponents, functor); + functor(vtkm::Int16()); break; case DTYPE_UNSIGNED_INT: - SelectVecTypeAndCall(vtkm::UInt32(), numComponents, functor); + functor(vtkm::UInt32()); break; case DTYPE_INT: - SelectVecTypeAndCall(vtkm::Int32(), numComponents, functor); + functor(vtkm::Int32()); break; case DTYPE_UNSIGNED_LONG: case DTYPE_UNSIGNED_LONG_LONG: - SelectVecTypeAndCall(vtkm::UInt64(), numComponents, functor); + functor(vtkm::UInt64()); break; case DTYPE_LONG: case DTYPE_LONG_LONG: - SelectVecTypeAndCall(vtkm::Int64(), numComponents, functor); + functor(vtkm::Int64()); break; case DTYPE_FLOAT: - SelectVecTypeAndCall(vtkm::Float32(), numComponents, functor); + functor(vtkm::Float32()); break; case DTYPE_DOUBLE: - SelectVecTypeAndCall(vtkm::Float64(), numComponents, functor); + functor(vtkm::Float64()); break; default: assert(false); diff --git a/vtkm/io/testing/UnitTestVTKDataSetWriter.cxx b/vtkm/io/testing/UnitTestVTKDataSetWriter.cxx index 06caecd69..c7498c3cd 100644 --- a/vtkm/io/testing/UnitTestVTKDataSetWriter.cxx +++ b/vtkm/io/testing/UnitTestVTKDataSetWriter.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -122,7 +123,7 @@ void CheckWrittenReadData(const vtkm::cont::DataSet& originalData, VTKM_TEST_ASSERT(fileData.HasField(originalField.GetName(), originalField.GetAssociation())); vtkm::cont::Field fileField = fileData.GetField(originalField.GetName(), originalField.GetAssociation()); - vtkm::cont::CastAndCall(originalField, CheckSameField{}, fileField); + VTKM_TEST_ASSERT(test_equal_ArrayHandles(originalField.GetData(), fileField.GetData())); } VTKM_TEST_ASSERT(fileData.GetNumberOfCoordinateSystems() > 0); @@ -261,12 +262,31 @@ void TestVTKCompoundWrite() std::remove("chirp.vtk"); } +void TestVTKOddVecSizes() +{ + vtkm::cont::DataSetBuilderUniform dsb; + vtkm::cont::DataSet dataSet = dsb.Create({ 2, 2, 2 }); + + vtkm::cont::ArrayHandle> vec5Array; + vec5Array.Allocate(dataSet.GetNumberOfPoints()); + SetPortal(vec5Array.WritePortal()); + dataSet.AddPointField("vec5", vec5Array); + + vtkm::cont::ArrayHandleSOA> vec13Array; + vec13Array.Allocate(dataSet.GetNumberOfPoints()); + SetPortal(vec13Array.WritePortal()); + dataSet.AddPointField("vec13", vec13Array); + + TestVTKWriteTestData("OddVecSizes", dataSet); +} + void TestVTKWrite() { TestVTKExplicitWrite(); TestVTKUniformWrite(); TestVTKRectilinearWrite(); TestVTKCompoundWrite(); + TestVTKOddVecSizes(); } } //Anonymous namespace