From c802adcbebfb795c8e434da5d584adfedfa9f4a9 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 22 May 2023 09:26:06 -0600 Subject: [PATCH] Add support for CastAndCallVariableVecField in FilterField The `FilterField` class provides convenience functions for subclasses to determine the `ArrayHandle` type for scalar and vector fields. However, you needed to know the specific size of vectors. For filters that support an input field of any type, a new form, `CastAndCallVariableVecField` has been added. This calls the underlying functor with an `ArrayHandleRecombineVec` of the appropriate component type. The `CastAndaCallVariableVecField` method also reduces the number of instances created by having a float fallback for any component type that does not satisfy the field types. --- docs/changelog/cast-and-call-variable-vec.md | 12 ++++ vtkm/filter/FilterField.h | 71 ++++++++++++++++++++ vtkm/filter/vector_analysis/DotProduct.cxx | 24 ++----- 3 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 docs/changelog/cast-and-call-variable-vec.md diff --git a/docs/changelog/cast-and-call-variable-vec.md b/docs/changelog/cast-and-call-variable-vec.md new file mode 100644 index 000000000..d089dad77 --- /dev/null +++ b/docs/changelog/cast-and-call-variable-vec.md @@ -0,0 +1,12 @@ +# Added support for `CastAndCallVariableVecField` in `FilterField` + +The `FilterField` class provides convenience functions for subclasses to +determine the `ArrayHandle` type for scalar and vector fields. However, you +needed to know the specific size of vectors. For filters that support an +input field of any type, a new form, `CastAndCallVariableVecField` has been +added. This calls the underlying functor with an `ArrayHandleRecombineVec` +of the appropriate component type. + +The `CastAndaCallVariableVecField` method also reduces the number of +instances created by having a float fallback for any component type that +does not satisfy the field types. diff --git a/vtkm/filter/FilterField.h b/vtkm/filter/FilterField.h index 0005fd4ac..aa89f5943 100644 --- a/vtkm/filter/FilterField.h +++ b/vtkm/filter/FilterField.h @@ -13,6 +13,8 @@ #include +#include + namespace vtkm { namespace filter @@ -160,6 +162,16 @@ protected: } } + ///@{ + /// \brief Convenience method to get the array from a filter's input scalar field. + /// + /// A field filter typically gets its input fields using the internal `GetFieldFromDataSet`. + /// To use this field in a worklet, it eventually needs to be converted to an + /// `ArrayHandle`. If the input field is limited to be a scalar field, then this method + /// provides a convenient way to determine the correct array type. Like other `CastAndCall` + /// methods, it takes as input a `Field` (or `UnknownArrayHandle`) and a function/functor + /// to call with the appropriate `ArrayHandle` type. + /// template VTKM_CONT void CastAndCallScalarField(const vtkm::cont::UnknownArrayHandle& fieldArray, Functor&& functor, @@ -178,6 +190,7 @@ protected: this->CastAndCallScalarField( field.GetData(), std::forward(functor), std::forward(args)...); } + ///@} private: @@ -189,6 +202,18 @@ private: }; protected: + ///@{ + /// \brief Convenience method to get the array from a filter's input vector field. + /// + /// A field filter typically gets its input fields using the internal `GetFieldFromDataSet`. + /// To use this field in a worklet, it eventually needs to be converted to an + /// `ArrayHandle`. If the input field is limited to be a vector field with vectors of a + /// specific size, then this method provides a convenient way to determine the correct array + /// type. Like other `CastAndCall` methods, it takes as input a `Field` (or + /// `UnknownArrayHandle`) and a function/functor to call with the appropriate `ArrayHandle` + /// type. You also have to provide the vector size as the first template argument. + /// For example `CastAndCallVecField<3>(field, functor);`. + /// template VTKM_CONT void CastAndCallVecField(const vtkm::cont::UnknownArrayHandle& fieldArray, Functor&& functor, @@ -208,6 +233,52 @@ protected: this->CastAndCallVecField( field.GetData(), std::forward(functor), std::forward(args)...); } + ///@} + + ///@{ + /// This method is like `CastAndCallVecField` except that it can be used for a + /// field of unknown vector size (or scalars). This method will call the given + /// functor with an `ArrayHandleRecombineVec`. + /// + /// Note that there are limitations with using `ArrayHandleRecombineVec` within a + /// worklet. Because the size of the vectors are not known at compile time, you + /// cannot just create an intermediate `Vec` of the correct size. Typically, you + /// must allocate the output array (for example, with `ArrayHandleRuntimeVec`), and + /// the worklet must iterate over the components and store them in the prealocated + /// output. + /// + template + VTKM_CONT void CastAndCallVariableVecField(const vtkm::cont::UnknownArrayHandle& fieldArray, + Functor&& functor, + Args&&... args) const + { + if (fieldArray.IsBaseComponentType()) + { + functor(fieldArray.ExtractArrayFromComponents(), std::forward(args)...); + } + else if (fieldArray.IsBaseComponentType()) + { + functor(fieldArray.ExtractArrayFromComponents(), std::forward(args)...); + } + else + { + // Field component type is not directly supported. Copy to floating point array. + vtkm::cont::UnknownArrayHandle floatArray = fieldArray.NewInstanceFloatBasic(); + vtkm::cont::ArrayCopy(fieldArray, floatArray); + functor(floatArray.ExtractArrayFromComponents(), + std::forward(args)...); + } + } + + template + VTKM_CONT void CastAndCallVariableVecField(const vtkm::cont::Field& field, + Functor&& functor, + Args&&... args) const + { + this->CastAndCallVariableVecField( + field.GetData(), std::forward(functor), std::forward(args)...); + } + ///@} /// \brief Create the output data set for `DoExecute` /// diff --git a/vtkm/filter/vector_analysis/DotProduct.cxx b/vtkm/filter/vector_analysis/DotProduct.cxx index 14eacd949..c96202871 100644 --- a/vtkm/filter/vector_analysis/DotProduct.cxx +++ b/vtkm/filter/vector_analysis/DotProduct.cxx @@ -78,11 +78,9 @@ VTKM_CONT DotProduct::DotProduct() VTKM_CONT vtkm::cont::DataSet DotProduct::DoExecute(const vtkm::cont::DataSet& inDataSet) { vtkm::cont::Field primaryField = this->GetFieldFromDataSet(0, inDataSet); - vtkm::cont::UnknownArrayHandle primaryArray = primaryField.GetData(); - vtkm::cont::Field secondaryField = this->GetFieldFromDataSet(1, inDataSet); - if (primaryArray.GetNumberOfComponentsFlat() != + if (primaryField.GetData().GetNumberOfComponentsFlat() != secondaryField.GetData().GetNumberOfComponentsFlat()) { throw vtkm::cont::ErrorFilterExecution( @@ -91,22 +89,10 @@ VTKM_CONT vtkm::cont::DataSet DotProduct::DoExecute(const vtkm::cont::DataSet& i vtkm::cont::UnknownArrayHandle outArray; - if (primaryArray.IsBaseComponentType()) - { - outArray = - DoDotProduct(primaryArray.ExtractArrayFromComponents(), secondaryField); - } - else if (primaryArray.IsBaseComponentType()) - { - outArray = - DoDotProduct(primaryArray.ExtractArrayFromComponents(), secondaryField); - } - else - { - primaryArray = primaryField.GetDataAsDefaultFloat(); - outArray = - DoDotProduct(primaryArray.ExtractArrayFromComponents(), secondaryField); - } + auto resolveArray = [&](const auto& concretePrimaryField) { + outArray = DoDotProduct(concretePrimaryField, secondaryField); + }; + this->CastAndCallVariableVecField(primaryField, resolveArray); return this->CreateResultField(inDataSet, this->GetOutputFieldName(),