From 99fdc88848b6443b1322b58d3c3d99dab4c1e90f Mon Sep 17 00:00:00 2001
From: David Thompson <david.thompson@kitware.com>
Date: Thu, 26 Sep 2024 10:15:05 -0400
Subject: [PATCH] Add support for node-sets in the vtkCellGrid IOSS reader.

---
 IO/IOSS/vtkIOSSCellGridReaderInternal.cxx | 83 +++++++++++++++++++++--
 IO/IOSS/vtkIOSSCellGridUtilities.cxx      |  4 +-
 2 files changed, 80 insertions(+), 7 deletions(-)

diff --git a/IO/IOSS/vtkIOSSCellGridReaderInternal.cxx b/IO/IOSS/vtkIOSSCellGridReaderInternal.cxx
index ab8ddada523..39b8c81b12d 100644
--- a/IO/IOSS/vtkIOSSCellGridReaderInternal.cxx
+++ b/IO/IOSS/vtkIOSSCellGridReaderInternal.cxx
@@ -37,6 +37,7 @@
 
 // clang-format off
 #include VTK_IOSS(Ioss_Field.h)
+#include VTK_IOSS(Ioss_TransformFactory.h)
 // clang-format on
 
 VTK_ABI_NAMESPACE_BEGIN
@@ -326,18 +327,83 @@ std::vector<vtkSmartPointer<vtkCellGrid>> vtkIOSSCellGridReaderInternal::GetNode
   const DatabaseHandle& handle, int timestep, vtkIOSSCellGridReader* self)
 {
   (void)self;
-  (void)timestep;
   (void)vtk_entity_type;
-  std::vector<vtkSmartPointer<vtkCellGrid>> data;
   auto region = this->GetRegion(handle);
   auto group_entity = region->get_entity(blockName, Ioss::EntityType::NODESET);
   if (!group_entity)
   {
-    throw std::runtime_error("No group entity for node set.");
+    vtkErrorWithObjectMacro(self, "No group entity for node-set \"" << blockName << "\".");
+    return {};
   }
+  auto grid = vtkSmartPointer<vtkCellGrid>::New();
+  auto meta = vtkCellMetadata::NewInstance("vtkDGVert"_token, grid);
+  auto* dg = vtkDGCell::SafeDownCast(meta);
+  if (!meta || !dg)
+  {
+    vtkErrorWithObjectMacro(
+      self, "Could not create metadata for node-set \"" << blockName << "\".");
+    return {};
+  }
+  if (!grid->AddCellMetadata(meta))
+  {
+    vtkErrorWithObjectMacro(
+      self, "Could not add metadata for node-set \"" << blockName << "\" to grid.");
+    return {};
+  }
+  // Fetch the IDs of the file-global points included in the node-set,
+  // offseting by -1 so they are 0-indexed:
+  auto transform = std::unique_ptr<Ioss::Transform>(Ioss::TransformFactory::create("offset"));
+  transform->set_property("offset", -1);
+  auto ids_raw = vtkIOSSUtilities::GetData(group_entity, "ids_raw", transform.get());
+  ids_raw->SetNumberOfComponents(1);
+
+  // Add the ID array to a vtkDataSetAttributes instance corresponding to
+  // the number of cells of the node-set. Since a separate cell-grid holds
+  // each node-set, we use the name of the cell type ("vtkDGVert") for the
+  // array group:
+  auto* cellGroup = grid->GetAttributes(dg->GetClassName());
+  cellGroup->AddArray(ids_raw);
+  dg->GetCellSpec().Connectivity = ids_raw;
+  dg->GetCellSpec().SourceShape = vtkDGCell::Shape::Vertex;
+  dg->GetCellSpec().Blanked = false;
 
-  std::cerr << "Node-sets (" << blockName << ") are currently unsupported.\n";
-  return data;
+  // From the shape of cells in the block, the connectivity size, and the order,
+  // we need to infer vtkDGCell::CellTypeInfo data (FunctionSpace, Basis, Order).
+  vtkCellAttribute::CellTypeInfo cellShapeInfo;
+  cellShapeInfo.FunctionSpace = "constant"_token;
+  cellShapeInfo.Basis = "C"_token;
+  cellShapeInfo.Order = 0;
+
+  // Read node coordinates as the shape attribute.
+  // This must always be a "CG" (continuous) attribute.
+  vtkIOSSCellGridUtilities::GetShape(
+    region, group_entity, cellShapeInfo, timestep, dg, grid, &this->Cache);
+  // Apply displacements before reading other cell-attributes as
+  // computing the range of HDIV/HCURL attributes **must** use
+  // the actual (deformed) cell shape. Also, note that using a
+  // displacement scale factor other than 1.0 will introduce errors.
+  if (self->GetApplyDisplacements())
+  {
+    this->ApplyDisplacements(grid, region, group_entity, handle, timestep);
+  }
+
+  // TODO: Add per-block attributes.
+  // Note that this is very difficult since nodes may be attached to
+  // cells in any number (0 or more) of element blocks. We could add
+  // all cell-attributes across all element blocks (assuming there are
+  // no cell-attributes with different spaces/components in different
+  // blocks), but then we must have NaN entries for nodes that do not
+  // attach to cells in those blocks. Also, it is possible for a node
+  // to take on multiple values if multiple element blocks provide the
+  // same field. In that case, it is unclear what to do.
+
+  // Add cell-attributes for nodal-data.
+  auto nodeFieldSelection = self->GetNodeBlockFieldSelection();
+  auto nodeblock = region->get_entity("nodeblock_1", Ioss::EntityType::NODEBLOCK);
+  this->GetNodalAttributes(nodeFieldSelection, grid->GetAttributes("point-data"_token), grid, dg,
+    nodeblock, region, handle, timestep, self->GetReadIds(), "");
+
+  return { grid };
 }
 
 vtkCellAttribute::CellTypeInfo vtkIOSSCellGridReaderInternal::GetCellGridInfoForBlock(
@@ -618,7 +684,12 @@ void vtkIOSSCellGridReaderInternal::GetNodalAttributes(vtkDataArraySelection* fi
       attribute->Initialize(array->GetName(), "ℝ³", array->GetNumberOfComponents());
       vtkCellAttribute::CellTypeInfo cellTypeInfo;
       cellTypeInfo.DOFSharing = "point-data"_token;
-      cellTypeInfo.FunctionSpace = "HGRAD"; // All point-data arrays are HGRAD
+      // Point-data arrays must match the shape-attribute since they are
+      // continuous and must thus use the connectivity array provided for
+      // the shape attribute. Note that even nodesets and blocks of vertex
+      // cells are "continuous," though in that case they live in the
+      // "constant" function space, not "HGRAD."
+      cellTypeInfo.FunctionSpace = shapeInfo.FunctionSpace;
       cellTypeInfo.Basis = shapeInfo.Basis;
       cellTypeInfo.Order = shapeInfo.Order;
       cellTypeInfo.ArraysByRole["connectivity"] = meta->GetCellSpec().Connectivity;
diff --git a/IO/IOSS/vtkIOSSCellGridUtilities.cxx b/IO/IOSS/vtkIOSSCellGridUtilities.cxx
index 82db04de503..90bc4e03305 100644
--- a/IO/IOSS/vtkIOSSCellGridUtilities.cxx
+++ b/IO/IOSS/vtkIOSSCellGridUtilities.cxx
@@ -467,7 +467,9 @@ bool GetShape(Ioss::Region* region, const Ioss::GroupingEntity* group_entity,
   vtkNew<vtkCellAttribute> attribute;
   attribute->Initialize("shape", "ℝ³", 3);
   cellShapeInfo.DOFSharing = "coordinates"_token; // Required for the shape attribute.
-  cellShapeInfo.FunctionSpace = "HGRAD"_token;    // Required for the shape attribute.
+  cellShapeInfo.FunctionSpace = meta->GetCellSpec().SourceShape == vtkDGCell::Shape::Vertex
+    ? "constant"_token
+    : "HGRAD"_token; // Required for the shape attribute.
   cellShapeInfo.ArraysByRole["connectivity"] = meta->GetCellSpec().Connectivity;
   cellShapeInfo.ArraysByRole["values"] = cached;
   attribute->SetCellTypeInfo(meta->GetClassName(), cellShapeInfo);
-- 
GitLab