diff --git a/IO/CatalystConduit/vtkConduitArrayUtilities.h b/IO/CatalystConduit/vtkConduitArrayUtilities.h index 4a05b04e3723025e4d10a25ed176a94d3c8acaa8..0e7b77b0aab0350a2b8abefe9cf148612bbca6f7 100644 --- a/IO/CatalystConduit/vtkConduitArrayUtilities.h +++ b/IO/CatalystConduit/vtkConduitArrayUtilities.h @@ -17,6 +17,7 @@ #ifndef vtkConduitArrayUtilities_h #define vtkConduitArrayUtilities_h +#include "vtkDeprecation.h" // for VTK_DEPRECATED_IN_9_5_0 #include "vtkIOCatalystConduitModule.h" // for exports #include "vtkObject.h" #include "vtkSmartPointer.h" // for vtkSmartPointer @@ -51,7 +52,13 @@ public: /** * Returns a vtkDataArray from a conduit node in the conduit mcarray protocol * that is a conduit ghost array named ascent_ghosts. + * + * @deprecated: Instead of using this function, use the state/metadata/vtk_fields + * to define the attribute_type, values_to_replace and replacement_values instead for + * the ghost array. */ + VTK_DEPRECATED_IN_9_5_0("This function is deprecated, because in the future " + "state/metadata/vtk_fields will only be used.") static vtkSmartPointer<vtkDataArray> MCGhostArrayToVTKGhostArray( const conduit_node* mcarray, bool is_cell_data); ///@} diff --git a/IO/CatalystConduit/vtkConduitToDataObject.cxx b/IO/CatalystConduit/vtkConduitToDataObject.cxx index 0e8f2b45cd1456a5e7ade1ce188936d52c5ef3c8..30c55483cb65e041514730838865ea8ee34eb785 100644 --- a/IO/CatalystConduit/vtkConduitToDataObject.cxx +++ b/IO/CatalystConduit/vtkConduitToDataObject.cxx @@ -1,5 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause + +// Added due to deprecated vtkConduitArrayUtilities::MCGhostArrayToVTKGhostArray +#define VTK_DEPRECATION_LEVEL 0 + #include "vtkConduitToDataObject.h" #include "vtkAMRBox.h" @@ -18,11 +22,14 @@ #include "vtkParallelAMRUtilities.h" #include "vtkPartitionedDataSet.h" #include "vtkRectilinearGrid.h" +#include "vtkSMPTools.h" #include "vtkStringArray.h" #include "vtkStructuredGrid.h" #include "vtkUniformGrid.h" #include "vtkUnstructuredGrid.h" +#include "vtksys/SystemTools.hxx" + #include <catalyst_conduit.hpp> #include <catalyst_conduit_blueprint.hpp> @@ -33,6 +40,63 @@ namespace vtkConduitToDataObject { VTK_ABI_NAMESPACE_BEGIN +//---------------------------------------------------------------------------- +struct FieldMetadata +{ + vtkSmartPointer<vtkDataArray> ValuesToReplace = nullptr; + vtkSmartPointer<vtkDataArray> ReplacementValues = nullptr; + std::string AttributeType; + + static vtkDataSetAttributes::AttributeTypes GetDataSetAttributeType( + const std::string& otherAttributeTypeName) + { + for (int i = 0; i < vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES; ++i) + { + const std::string attributeTypeName = vtkDataSetAttributes::GetAttributeTypeAsString(i); + if (vtksys::SystemTools::UpperCase(otherAttributeTypeName) == + vtksys::SystemTools::UpperCase(attributeTypeName)) + { + return static_cast<vtkDataSetAttributes::AttributeTypes>(i); + } + } + return vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES; + } + + static bool IsGhostsAttributeType(const std::string& otherAttributeTypeName) + { + return vtksys::SystemTools::UpperCase(otherAttributeTypeName) == "GHOSTS"; + } +}; + +//---------------------------------------------------------------------------- +struct ReplaceValuesWorker +{ + template <typename Array1T, typename Array2T, typename Array3T> + void operator()(Array1T* valuesToReplace, Array2T* replacementValues, Array3T* array) const + { + const vtkIdType numValuesToReplace = valuesToReplace->GetNumberOfTuples(); + auto valuesToReplaceRange = vtk::DataArrayValueRange(valuesToReplace); + auto replacementValuesRange = vtk::DataArrayValueRange(replacementValues); + auto arrayRange = vtk::DataArrayValueRange(array); + + vtkSMPTools::For(0, array->GetNumberOfTuples(), + [&](vtkIdType begin, vtkIdType end) + { + for (vtkIdType inputIdx = begin; inputIdx < end; ++inputIdx) + { + for (vtkIdType repValueId = 0; repValueId < numValuesToReplace; ++repValueId) + { + if (valuesToReplaceRange[repValueId] == arrayRange[inputIdx]) + { + arrayRange[inputIdx] = replacementValuesRange[repValueId]; + break; + } + } + } + }); + } +}; + //---------------------------------------------------------------------------- bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::Node& node) { @@ -51,8 +115,7 @@ bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::No // process "topologies". auto topologies = node["topologies"]; - conduit_index_t nchildren = topologies.number_of_children(); - for (conduit_index_t i = 0; i < nchildren; ++i) + for (conduit_index_t i = 0, nchildren = topologies.number_of_children(); i < nchildren; ++i) { auto child = topologies.child(i); try @@ -88,9 +151,73 @@ bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::No return true; } + // read "state/metadata/vtk_fields" + std::map<std::string, FieldMetadata> fieldMetadata; + if (node.has_path("state/metadata/vtk_fields")) + { + auto fieldsMetadata = node["state/metadata/vtk_fields"]; + for (conduit_index_t i = 0, nchildren = fieldsMetadata.number_of_children(); i < nchildren; ++i) + { + auto fieldMetadataNode = fieldsMetadata.child(i); + const auto& name = fieldMetadataNode.name(); + try + { + // read values_to_replace and replacement_values if they exist + if (fieldMetadataNode.has_path("values_to_replace") && + fieldMetadataNode.has_path("replacement_values")) + { + auto valuesToReplace = fieldMetadataNode["values_to_replace"]; + fieldMetadata[name].ValuesToReplace = + vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&valuesToReplace)); + auto replacementValues = fieldMetadataNode["replacement_values"]; + fieldMetadata[name].ReplacementValues = + vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&replacementValues)); + if (fieldMetadata[name].ValuesToReplace->GetNumberOfTuples() != + fieldMetadata[name].ReplacementValues->GetNumberOfTuples()) + { + vtkLogF(ERROR, + "values_to_replace and replacement_values should have equal size for field '%s'.", + name.c_str()); + return false; + } + if (fieldMetadata[name].ValuesToReplace->GetNumberOfComponents() != 1 || + fieldMetadata[name].ReplacementValues->GetNumberOfComponents() != 1) + { + vtkLogF(ERROR, + "values_to_replace and replacement_values should have 1 component for field '%s'.", + name.c_str()); + return false; + } + } + // read attribute type if it exists + if (fieldMetadataNode.has_path("attribute_type")) + { + const std::string& attributeType = fieldMetadataNode["attribute_type"].as_string(); + // check if the attribute type is valid + if (FieldMetadata::GetDataSetAttributeType(attributeType) != + vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES || + FieldMetadata::IsGhostsAttributeType(attributeType)) + { + fieldMetadata[name].AttributeType = attributeType; + } + else + { + vtkLogF( + ERROR, "invalid attribute type '%s' for '%s'.", attributeType.c_str(), name.c_str()); + return false; + } + } + } + catch (std::exception& e) + { + vtkLogF(ERROR, "failed to process '../state/metadata/vtk_fields/%s'.", name.c_str()); + vtkLogF(ERROR, "ERROR: \n%s\n", e.what()); + return false; + } + } + } auto fields = node["fields"]; - nchildren = fields.number_of_children(); - for (conduit_index_t i = 0; i < nchildren; ++i) + for (conduit_index_t i = 0, nchildren = fields.number_of_children(); i < nchildren; ++i) { auto fieldNode = fields.child(i); const auto& fieldname = fieldNode.name(); @@ -111,25 +238,68 @@ bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::No } if (dataset_size > 0) { - vtkSmartPointer<vtkDataArray> array; + // This code path should be removed once MCGhostArrayToVTKGhostArray is removed. if (fieldname == "ascent_ghosts") { // convert ascent ghost information into VTK ghost information // the VTK array is named vtkDataSetAttributes::GhostArrayName() // and has different values. - array = vtkConduitArrayUtilities::MCGhostArrayToVTKGhostArray( + auto array = vtkConduitArrayUtilities::MCGhostArrayToVTKGhostArray( conduit_cpp::c_node(&values), dsa->IsA("vtkCellData")); + dsa->AddArray(array); + continue; } - else + vtkSmartPointer<vtkDataArray> array = + vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&values), fieldname); + if (array->GetNumberOfTuples() != dataset->GetNumberOfElements(vtk_association)) + { + throw std::runtime_error("mismatched tuple count!"); + } + if (fieldMetadata.find(fieldname) != fieldMetadata.end()) { - array = - vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&values), fieldname); - if (array->GetNumberOfTuples() != dataset->GetNumberOfElements(vtk_association)) + const auto& metadata = fieldMetadata[fieldname]; + // replace values if needed + if (metadata.ValuesToReplace && metadata.ReplacementValues) + { + ReplaceValuesWorker replaceValuesWorker; + if (!vtkArrayDispatch::Dispatch3SameValueType::Execute(metadata.ValuesToReplace.Get(), + metadata.ReplacementValues.Get(), array.Get(), replaceValuesWorker)) + { + replaceValuesWorker( + metadata.ValuesToReplace.Get(), metadata.ReplacementValues.Get(), array.Get()); + } + } + // extract the attribute type, and change the array name if needed + auto dsaAttributeType = vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES; + if (!metadata.AttributeType.empty()) { - throw std::runtime_error("mismatched tuple count!"); + dsaAttributeType = FieldMetadata::GetDataSetAttributeType(metadata.AttributeType); + if (FieldMetadata::IsGhostsAttributeType(metadata.AttributeType)) + { + // convert its name to the VTK ghost array name + array->SetName(vtkDataSetAttributes::GhostArrayName()); + // ensure the array is unsigned char + if (!array->IsA("vtkUnsignedCharArray")) + { + auto ghostArray = vtkSmartPointer<vtkUnsignedCharArray>::New(); + ghostArray->DeepCopy(array); + array = ghostArray; + } + } } + if (dsaAttributeType != vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES) + { + dsa->SetAttribute(array, dsaAttributeType); + } + else + { + dsa->AddArray(array); + } + } + else + { + dsa->AddArray(array); } - dsa->AddArray(array); } } catch (std::exception& e)