Merge topic 'composite-vectors-any-size'

b59580bb8 Allow CompositeVectors filter to build any size vector
a8b4e5a62 Add GetNumberOfActiveFields method to FilterField

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3063
This commit is contained in:
Kenneth Moreland 2023-05-18 20:28:20 +00:00 committed by Kitware Robot
commit 2cc2205218
5 changed files with 144 additions and 106 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.

@ -46,7 +46,7 @@ public:
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::Any)
{
auto index_st = static_cast<std::size_t>(index);
ResizeIfNeeded(index_st);
this->ResizeIfNeeded(index_st);
this->ActiveFieldNames[index_st] = name;
this->ActiveFieldAssociation[index_st] = association;
}
@ -79,7 +79,7 @@ public:
void SetActiveCoordinateSystem(vtkm::IdComponent index, vtkm::Id coord_idx)
{
auto index_st = static_cast<std::size_t>(index);
ResizeIfNeeded(index_st);
this->ResizeIfNeeded(index_st);
this->ActiveCoordinateSystemIndices[index_st] = coord_idx;
}
@ -120,6 +120,19 @@ public:
}
///@}
/// \brief Return the number of active fields currently set.
///
/// The general interface to `FilterField` allows a user to set an arbitrary number
/// of active fields (indexed 0 and on). This method returns the number of active
/// fields that are set. Note that the filter implementation is free to ignore
/// any active fields it does not support. Also note that an active field can be
/// set to be either a named field or a coordinate system.
vtkm::IdComponent GetNumberOfActiveFields() const
{
VTKM_ASSERT(this->ActiveFieldNames.size() == this->UseCoordinateSystemAsField.size());
return static_cast<vtkm::IdComponent>(this->UseCoordinateSystemAsField.size());
}
protected:
VTKM_CONT
const vtkm::cont::Field& GetFieldFromDataSet(const vtkm::cont::DataSet& input) const

@ -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