Allow CompositeVectors filter to build any size vector

Using the new `ArrayHandleRuntimeVec` feature, we can construct an array
with any vec sized value.
This commit is contained in:
Kenneth Moreland 2023-05-16 11:56:24 -06:00
parent a8b4e5a629
commit b59580bb82
4 changed files with 129 additions and 104 deletions

@ -1,3 +1,3 @@
# New Composite Vector filter
The composite vector filter combines multiple scalar fields into a single vector field. Scalar fields are selected as the active input fields, and the combined vector field is set at the output. The current composite vector filter only supports 2d and 3d scalar field composition. Users may use `vtkm::cont::make_ArrayHandleCompositeVector` to execute a more flexible scalar field composition.
The composite vector filter combines multiple scalar fields into a single vector field. Scalar fields are selected as the active input fields, and the combined vector field is set at the output.

@ -7,94 +7,119 @@
// 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/ArrayHandleCompositeVector.h>
#include <vtkm/cont/ErrorFilterExecution.h>
#include <vtkm/filter/field_transform/CompositeVectors.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandleRuntimeVec.h>
#include <vtkm/cont/ErrorFilterExecution.h>
#include <vtkm/cont/UnknownArrayHandle.h>
#include <vtkm/List.h>
#include <vtkm/TypeList.h>
namespace
{
// Extracts a component from an UnknownArrayHandle and returns the extracted component
// as an UnknownArrayHandle. Perhaps this functionality should be part of UnknownArrayHandle
// proper, but its use is probably rare. Note that this implementation makes some assumptions
// on its use in the CompositeVectors filter.
VTKM_CONT vtkm::cont::UnknownArrayHandle ExtractComponent(
const vtkm::cont::UnknownArrayHandle& array,
vtkm::IdComponent componentIndex)
{
vtkm::cont::UnknownArrayHandle extractedComponentArray;
auto resolveType = [&](auto componentExample) {
using ComponentType = decltype(componentExample);
if (array.IsBaseComponentType<ComponentType>())
{
extractedComponentArray =
array.ExtractComponent<ComponentType>(componentIndex, vtkm::CopyFlag::Off);
}
};
vtkm::ListForEach(resolveType, vtkm::TypeListBaseC{});
return extractedComponentArray;
}
} // anonymous namespace
namespace vtkm
{
namespace filter
{
namespace field_transform
{
VTKM_CONT void CompositeVectors::SetFieldNameList(const std::vector<std::string>& fieldNameList,
vtkm::cont::Field::Association association)
{
vtkm::IdComponent index = 0;
for (auto& fieldName : fieldNameList)
{
this->SetActiveField(index, fieldName, association);
++index;
}
}
VTKM_CONT vtkm::IdComponent CompositeVectors::GetNumberOfFields() const
{
return this->GetNumberOfActiveFields();
}
VTKM_CONT vtkm::cont::DataSet CompositeVectors::DoExecute(const vtkm::cont::DataSet& inDataSet)
{
vtkm::IdComponent numComponents = this->GetNumberOfFields();
if (numComponents < 1)
{
throw vtkm::cont::ErrorBadValue(
"No input fields to combine into a vector for CompositeVectors.");
}
vtkm::cont::UnknownArrayHandle outArray;
if (this->NumberOfFields < 2)
{
throw vtkm::cont::ErrorFilterExecution("FieldNameList is supposed to be larger than 2.");
}
else if (this->NumberOfFields == 2)
{
auto fieldAssociation = this->GetFieldFromDataSet(0, inDataSet).GetAssociation();
if (fieldAssociation != this->GetFieldFromDataSet(1, inDataSet).GetAssociation())
// Allocate output array to the correct type.
vtkm::cont::Field firstField = this->GetFieldFromDataSet(0, inDataSet);
vtkm::Id numValues = firstField.GetNumberOfValues();
vtkm::cont::Field::Association association = firstField.GetAssociation();
auto allocateOutput = [&](auto exampleComponent) {
using ComponentType = decltype(exampleComponent);
if (firstField.GetData().IsBaseComponentType<ComponentType>())
{
throw vtkm::cont::ErrorFilterExecution("Field 0 and Field 2 have different associations.");
outArray = vtkm::cont::ArrayHandleRuntimeVec<ComponentType>{ numComponents };
}
auto resolveType2d = [&](const auto& field0) {
using T = typename std::decay_t<decltype(field0)>::ValueType;
vtkm::cont::ArrayHandle<T> field1;
vtkm::cont::ArrayCopyShallowIfPossible(this->GetFieldFromDataSet(1, inDataSet).GetData(),
field1);
auto compositedArray = vtkm::cont::make_ArrayHandleCompositeVector(field0, field1);
using VecType = vtkm::Vec<T, 2>;
using ArrayHandleType = vtkm::cont::ArrayHandle<VecType>;
ArrayHandleType result;
vtkm::cont::ArrayCopy(compositedArray, result);
outArray = result;
};
const auto& inField0 = this->GetFieldFromDataSet(0, inDataSet);
inField0.GetData().CastAndCallForTypes<vtkm::TypeListScalarAll, VTKM_DEFAULT_STORAGE_LIST>(
resolveType2d);
}
else if (this->NumberOfFields == 3)
};
vtkm::ListForEach(allocateOutput, vtkm::TypeListBaseC{});
if (!outArray.IsValid() || (outArray.GetNumberOfComponentsFlat() != numComponents))
{
auto fieldAssociation0 = this->GetFieldFromDataSet(0, inDataSet).GetAssociation();
auto fieldAssociation1 = this->GetFieldFromDataSet(1, inDataSet).GetAssociation();
auto fieldAssociation2 = this->GetFieldFromDataSet(2, inDataSet).GetAssociation();
throw vtkm::cont::ErrorBadType("Unable to allocate output array due to unexpected type.");
}
outArray.Allocate(numValues);
if (fieldAssociation0 != fieldAssociation1 || fieldAssociation1 != fieldAssociation2 ||
fieldAssociation0 != fieldAssociation2)
// Iterate over all component fields and copy them into the output array.
for (vtkm::IdComponent componentIndex = 0; componentIndex < numComponents; ++componentIndex)
{
vtkm::cont::Field inScalarField = this->GetFieldFromDataSet(componentIndex, inDataSet);
if (inScalarField.GetData().GetNumberOfComponentsFlat() != 1)
{
throw vtkm::cont::ErrorFilterExecution(
"Field 0, Field 1 and Field 2 have different associations.");
throw vtkm::cont::ErrorBadValue("All input fields to CompositeVectors must be scalars.");
}
if (inScalarField.GetAssociation() != association)
{
throw vtkm::cont::ErrorBadValue(
"All scalar fields must have the same association (point, cell, etc.).");
}
if (inScalarField.GetNumberOfValues() != numValues)
{
throw vtkm::cont::ErrorBadValue("Inconsistent number of field values.");
}
auto resolveType3d = [&](const auto& field0) {
using T = typename std::decay_t<decltype(field0)>::ValueType;
vtkm::cont::ArrayHandle<T> field1;
vtkm::cont::ArrayCopyShallowIfPossible(this->GetFieldFromDataSet(1, inDataSet).GetData(),
field1);
vtkm::cont::ArrayHandle<T> field2;
vtkm::cont::ArrayCopyShallowIfPossible(this->GetFieldFromDataSet(2, inDataSet).GetData(),
field2);
auto compositedArray = vtkm::cont::make_ArrayHandleCompositeVector(field0, field1, field2);
using VecType = vtkm::Vec<T, 3>;
using ArrayHandleType = vtkm::cont::ArrayHandle<VecType>;
ArrayHandleType result;
// ArrayHandleCompositeVector currently does not implement the ability to
// get to values on the control side, so copy to an array that is accessible.
vtkm::cont::ArrayCopy(compositedArray, result);
outArray = result;
};
const auto& inField0 = this->GetFieldFromDataSet(0, inDataSet);
inField0.GetData().CastAndCallForTypes<vtkm::TypeListScalarAll, VTKM_DEFAULT_STORAGE_LIST>(
resolveType3d);
}
else
{
throw vtkm::cont::ErrorFilterExecution(
"Using make_ArrayHandleCompositeVector to composite vectors more than 3.");
ExtractComponent(outArray, componentIndex).DeepCopyFrom(inScalarField.GetData());
}
return this->CreateResultField(
inDataSet, this->GetOutputFieldName(), this->GetActiveFieldAssociation(), outArray);
return this->CreateResultField(inDataSet, this->GetOutputFieldName(), association, outArray);
}
} // namespace field_transform
} // namespace vtkm::filter
} // namespace vtkm

@ -20,8 +20,15 @@ namespace filter
namespace field_transform
{
/// \brief The composite vector filter combines multiple scalar fields into a single vector field.
/// Scalar fields are selected as the active input fields, and the combined vector field is set at the output.
/// \brief Combine multiple scalar fields into a single vector field.
///
/// Scalar fields are selected as the active input fields, and the combined vector
/// field is set at the output. The `SetFieldNameList` method takes a `std::vector`
/// of field names to use as the component fields. Alternately, the superclass'
/// set active field methods can be used to select the fields independently.
///
/// All of the input fields must be scalar values. The type of the first field
/// determines the type of the output vector field.
///
class VTKM_FILTER_FIELD_TRANSFORM_EXPORT CompositeVectors : public vtkm::filter::FilterField
{
@ -33,24 +40,12 @@ public:
VTKM_CONT
void SetFieldNameList(
const std::vector<std::string>& fieldNameList,
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::Any)
{
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::Any);
vtkm::IdComponent index = 0;
for (auto& fieldName : fieldNameList)
{
this->SetActiveField(index, fieldName, association);
++index;
}
this->NumberOfFields = static_cast<vtkm::IdComponent>(fieldNameList.size());
}
VTKM_CONT
vtkm::IdComponent GetNumberOfFields() { return this->NumberOfFields; }
VTKM_CONT vtkm::IdComponent GetNumberOfFields() const;
private:
vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input) override;
vtkm::IdComponent NumberOfFields;
};
} // namespace field_transform
} // namespace vtkm::filter

@ -20,19 +20,19 @@ vtkm::cont::DataSet MakeDataSet(vtkm::IdComponent numArrays)
vtkm::IdComponent arrayLen = 100;
for (vtkm::IdComponent i = 0; i < numArrays; ++i)
for (vtkm::IdComponent fieldIndex = 0; fieldIndex < numArrays; ++fieldIndex)
{
std::vector<ScalarType> pointarray;
std::vector<ScalarType> cellarray;
for (vtkm::IdComponent j = 0; j < arrayLen; ++j)
for (vtkm::Id valueIndex = 0; valueIndex < arrayLen; ++valueIndex)
{
pointarray.push_back(static_cast<ScalarType>(i * 1.1 + j * 1.1));
cellarray.push_back(static_cast<ScalarType>(i * 2.1 + j * 2.1));
pointarray.push_back(static_cast<ScalarType>(fieldIndex * 1.1 + valueIndex * 1.1));
cellarray.push_back(static_cast<ScalarType>(fieldIndex * 2.1 + valueIndex * 2.1));
}
dataSet.AddPointField("pointArray" + std::to_string(i), pointarray);
dataSet.AddCellField("cellArray" + std::to_string(i), cellarray);
dataSet.AddPointField("pointArray" + std::to_string(fieldIndex), pointarray);
dataSet.AddCellField("cellArray" + std::to_string(fieldIndex), cellarray);
}
return dataSet;
@ -44,7 +44,7 @@ void CheckResults(vtkm::cont::DataSet inDataSet,
const std::string compositedName)
{
//there are only three fields for this testing , it is ok to use vtkm::IdComponent
vtkm::IdComponent dims = static_cast<vtkm::IdComponent>(FieldNames.size());
vtkm::IdComponent numComponents = static_cast<vtkm::IdComponent>(FieldNames.size());
auto compositedField = inDataSet.GetField(compositedName);
vtkm::IdComponent compositedFieldLen =
static_cast<vtkm::IdComponent>(compositedField.GetNumberOfValues());
@ -55,9 +55,9 @@ void CheckResults(vtkm::cont::DataSet inDataSet,
auto compFieldReadPortal = compFieldArrayCopy.ReadPortal();
for (vtkm::IdComponent i = 0; i < dims; i++)
for (vtkm::IdComponent componentIndex = 0; componentIndex < numComponents; componentIndex++)
{
auto field = inDataSet.GetField(FieldNames[i]);
auto field = inDataSet.GetField(FieldNames[componentIndex]);
VTKM_TEST_ASSERT(compositedField.GetAssociation() == field.GetAssociation(),
"Got bad association value.");
@ -68,11 +68,11 @@ void CheckResults(vtkm::cont::DataSet inDataSet,
field.GetData().AsArrayHandle(fieldArrayHandle);
auto fieldReadPortal = fieldArrayHandle.ReadPortal();
for (vtkm::IdComponent j = 0; j < fieldLen; j++)
for (vtkm::Id valueIndex = 0; valueIndex < fieldLen; valueIndex++)
{
auto compFieldVec = compFieldReadPortal.Get(j);
auto comFieldValue = compFieldVec[static_cast<vtkm::UInt64>(i)];
auto fieldValue = fieldReadPortal.Get(j);
auto compFieldVec = compFieldReadPortal.Get(valueIndex);
auto comFieldValue = compFieldVec[static_cast<vtkm::UInt64>(componentIndex)];
auto fieldValue = fieldReadPortal.Get(valueIndex);
VTKM_TEST_ASSERT(comFieldValue == fieldValue, "Got bad field value.");
}
}
@ -80,27 +80,30 @@ void CheckResults(vtkm::cont::DataSet inDataSet,
template <typename ScalarType, typename VecType>
void TestCompositeVectors(vtkm::IdComponent dim)
void TestCompositeVectors(vtkm::IdComponent numComponents)
{
vtkm::cont::DataSet inDataSet = MakeDataSet<ScalarType>(dim);
vtkm::cont::DataSet inDataSet = MakeDataSet<ScalarType>(numComponents);
vtkm::filter::field_transform::CompositeVectors filter;
// For the first pass (point field), we are going to use the generic `SetActiveField` method
// and let the filter figure out how many fields.
std::vector<std::string> pointFieldNames;
for (vtkm::IdComponent i = 0; i < dim; i++)
for (vtkm::IdComponent componentIndex = 0; componentIndex < numComponents; componentIndex++)
{
pointFieldNames.push_back("pointArray" + std::to_string(i));
pointFieldNames.push_back("pointArray" + std::to_string(componentIndex));
filter.SetActiveField(componentIndex, "pointArray" + std::to_string(componentIndex));
}
filter.SetFieldNameList(pointFieldNames, vtkm::cont::Field::Association::Points);
filter.SetOutputFieldName("CompositedFieldPoint");
vtkm::cont::DataSet outDataSetPointAssoci = filter.Execute(inDataSet);
CheckResults<ScalarType, VecType>(
outDataSetPointAssoci, pointFieldNames, filter.GetOutputFieldName());
// For the second pass (cell field), we will use the `SetFieldNameList` method.
std::vector<std::string> cellFieldNames;
for (vtkm::IdComponent i = 0; i < dim; i++)
for (vtkm::IdComponent componentIndex = 0; componentIndex < numComponents; componentIndex++)
{
cellFieldNames.push_back("cellArray" + std::to_string(i));
cellFieldNames.push_back("cellArray" + std::to_string(componentIndex));
}
filter.SetFieldNameList(cellFieldNames, vtkm::cont::Field::Association::Cells);
filter.SetOutputFieldName("CompositedFieldCell");
@ -114,8 +117,10 @@ void CompositeVectors()
{
TestCompositeVectors<vtkm::FloatDefault, vtkm::Vec2f>(2);
TestCompositeVectors<vtkm::FloatDefault, vtkm::Vec3f>(3);
TestCompositeVectors<vtkm::FloatDefault, vtkm::Vec<vtkm::FloatDefault, 5>>(5);
TestCompositeVectors<vtkm::Id, vtkm::Vec2i>(2);
TestCompositeVectors<vtkm::Id, vtkm::Vec3i>(3);
TestCompositeVectors<vtkm::Id, vtkm::Vec<vtkm::Id, 5>>(5);
}
} // anonymous namespace