From 01b61ad4e286fd33725dcd174d7299ba70910c3e Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Wed, 21 Apr 2021 17:00:58 +0200
Subject: [PATCH] openfoam: preserve uncollated lagrangian information (fixes
 #18179)

- old version assumed lagrangian data are available on all processor
  sub-directories and used a central naming mechanism accordingly.

  This meant that missing clouds on higher processors would
  effectively block out clouds. Likely didn't work properly with
  clouds in multiple regions.
---
 IO/Geometry/vtkOpenFOAMReader.cxx | 145 ++++++++++++++++++++----------
 1 file changed, 100 insertions(+), 45 deletions(-)

diff --git a/IO/Geometry/vtkOpenFOAMReader.cxx b/IO/Geometry/vtkOpenFOAMReader.cxx
index a78e1be0e1b..978c6b17255 100644
--- a/IO/Geometry/vtkOpenFOAMReader.cxx
+++ b/IO/Geometry/vtkOpenFOAMReader.cxx
@@ -316,6 +316,19 @@ void AppendLabelValue(vtkDataArray* array, vtkTypeInt64 val, bool use64BitLabels
   }
 }
 
+// Append unique string to list
+static void appendUniq(vtkStringArray* list, vtkStringArray* items)
+{
+  for (int i = 0; i < items->GetNumberOfTuples(); ++i)
+  {
+    vtkStdString& str = items->GetValue(i);
+    if (list->LookupValue(str) == -1)
+    {
+      list->InsertNextValue(str);
+    }
+  }
+}
+
 // Tuple remapping for symmTensor ordering
 // OpenFOAM [XX XY XZ YY YZ ZZ]
 // VTK uses [XX YY ZZ XY YZ XZ]
@@ -1002,6 +1015,8 @@ public:
 
   const std::string& GetRegionName() const noexcept { return this->RegionName; }
 
+  vtkStringArray* GetLagrangianPaths() { return this->LagrangianPaths; }
+
   // Read mesh/fields and create dataset
   int RequestData(vtkMultiBlockDataSet* output);
   int MakeMetaDataAtTimeStep(vtkStringArray*, vtkStringArray*, vtkStringArray*, bool);
@@ -1046,6 +1061,9 @@ private:
   vtkStringArray* PointFieldFiles;
   vtkStringArray* LagrangianFieldFiles;
 
+  // The cloud paths (region-local)
+  vtkNew<vtkStringArray> LagrangianPaths;
+
   // Mesh dimensions and construction information
   vtkIdType NumPoints;
   vtkIdType NumInternalFaces;
@@ -5805,15 +5823,16 @@ void vtkOpenFOAMReaderPrivate::LocateLagrangianClouds(const std::string& timePat
       if (io.OpenOrGzip(cloudPath + "/positions") && io.GetObjectName() == "positions" &&
         io.GetClassName().find("Cloud") != std::string::npos)
       {
-        if (this->Parent->LagrangianPaths->LookupValue(displayName) == -1)
+        // Append unique
+        if (this->LagrangianPaths->LookupValue(displayName) == -1)
         {
-          this->Parent->LagrangianPaths->InsertNextValue(displayName);
+          this->LagrangianPaths->InsertNextValue(displayName);
         }
         this->GetFieldNames(cloudPath, true);
         this->Parent->PatchDataArraySelection->AddArray(displayName.c_str());
       }
     }
-    this->Parent->LagrangianPaths->Squeeze();
+    this->LagrangianPaths->Squeeze();
   }
 }
 
@@ -6161,7 +6180,7 @@ int vtkOpenFOAMReaderPrivate::MakeMetaDataAtTimeStep(vtkStringArray* cellSelecti
   this->LagrangianFieldFiles->Initialize();
   if (listNextTimeStep)
   {
-    this->Parent->LagrangianPaths->Initialize();
+    this->LagrangianPaths->Initialize();
   }
   this->LocateLagrangianClouds(timePath);
 
@@ -6174,7 +6193,7 @@ int vtkOpenFOAMReaderPrivate::MakeMetaDataAtTimeStep(vtkStringArray* cellSelecti
     const std::string timePath2(this->TimePath(1));
     this->GetFieldNames(timePath2 + this->RegionPath());
     // if lagrangian clouds were not found at timestep 0
-    if (this->Parent->LagrangianPaths->GetNumberOfTuples() == 0)
+    if (!this->LagrangianPaths->GetNumberOfTuples())
     {
       this->LocateLagrangianClouds(timePath2);
     }
@@ -9594,18 +9613,26 @@ vtkMultiBlockDataSet* vtkOpenFOAMReaderPrivate::MakeLagrangianMesh()
 
   auto* lagrangianMesh = vtkMultiBlockDataSet::New();
 
-  const vtkIdType nClouds = this->Parent->LagrangianPaths->GetNumberOfTuples();
+  const std::string regionCloudPrefix(this->RegionPrefix() + "lagrangian/");
+
+  vtkDataArraySelection* selection = this->Parent->PatchDataArraySelection;
 
-  for (vtkIdType cloudi = 0; cloudi < nClouds; ++cloudi)
+  const vtkIdType nItems = selection->GetNumberOfArrays();
+  for (vtkIdType itemi = 0; itemi < nItems; ++itemi)
   {
-    const std::string& displayName = this->Parent->LagrangianPaths->GetValue(cloudi);
+    if (!selection->GetArraySetting(itemi))
+    {
+      continue;
+    }
+    const std::string displayName(selection->GetArrayName(itemi));
 
-    if (!this->Parent->PatchDataArraySelection->ArrayExists(displayName.c_str()) ||
-      !this->Parent->GetPatchArrayStatus(displayName.c_str()) ||
-      !this->IsDisplayRegion(displayName))
+    auto slash = displayName.rfind('/');
+    if (slash == std::string::npos || displayName.compare(0, ++slash, regionCloudPrefix) != 0)
     {
       continue;
     }
+    // The cloud name is the final component of the displayName
+    const std::string cloudName(displayName.substr(slash));
 
     // Equivalent to CurrentTimeRegionPath() + "lagrangian/{cloudName}"
     std::string cloudPath(this->CurrentTimePath());
@@ -9615,64 +9642,74 @@ vtkMultiBlockDataSet* vtkOpenFOAMReaderPrivate::MakeLagrangianMesh()
     }
     cloudPath += displayName;
 
-    // create an empty mesh to keep node/leaf structure of the
-    // multi-block consistent even if mesh doesn't exist
+    // Keep node/leaf structure consistent even if mesh doesn't exist
     vtkNew<vtkPolyData> cloudMesh;
-
-    // The cloud name is the final component of the displayName
-    ::AppendBlock(lagrangianMesh, cloudMesh, displayName.substr(displayName.rfind('/') + 1));
+    ::AppendBlock(lagrangianMesh, cloudMesh, cloudName);
 
     // Get the parcel positions as vtkPoints
     vtkNew<vtkPoints> points;
     {
+      bool missingCloud = true;
       const std::string positionsPath(cloudPath + "/positions");
 
       vtkFoamIOobject io(this->CasePath, this->Parent);
-      if (!io.OpenOrGzip(positionsPath))
+      if (io.OpenOrGzip(positionsPath))
       {
-        continue;
-      }
+        vtkFoamEntryValue dict(nullptr);
 
-      vtkFoamEntryValue dict(nullptr);
-      try
-      {
-        if (io.IsFloat64())
+        try
         {
-          dict.ReadNonUniformList<vtkFoamToken::VECTORLIST, //
-            vtkFoamRead::vectorListTraits<vtkFloatArray, double, 3, true>>(io);
+          if (io.IsFloat64())
+          {
+            dict.ReadNonUniformList<vtkFoamToken::VECTORLIST, //
+              vtkFoamRead::vectorListTraits<vtkFloatArray, double, 3, true>>(io);
+          }
+          else
+          {
+            dict.ReadNonUniformList<vtkFoamToken::VECTORLIST, //
+              vtkFoamRead::vectorListTraits<vtkFloatArray, float, 3, true>>(io);
+          }
+
+          // Transfer float tuples to points
+          auto* pointArray = dict.ReleasePtr<vtkFloatArray>();
+
+          points->SetData(pointArray);
+          pointArray->Delete();
+          missingCloud = false;
         }
-        else
+        catch (const vtkFoamError& err)
         {
-          dict.ReadNonUniformList<vtkFoamToken::VECTORLIST, //
-            vtkFoamRead::vectorListTraits<vtkFloatArray, float, 3, true>>(io);
+          vtkErrorMacro(<< "Error reading line " << io.GetLineNumber() << " of " << io.GetFileName()
+                        << ": " << err);
         }
+      }
 
-        // Transfer float tuples to points
-        auto* pointArray = dict.ReleasePtr<vtkFloatArray>();
+      if (missingCloud)
+      {
+        vtkNew<vtkFloatArray> pointArray;
+        pointArray->SetNumberOfComponents(3);
         points->SetData(pointArray);
-        pointArray->Delete();
       }
-      catch (const vtkFoamError& err)
+      const vtkIdType nParticles = points->GetNumberOfPoints();
+
+      cloudMesh->SetPoints(points);
+
+      // Cells (verts) for lagrangian mesh
+      vtkNew<vtkCellArray> verts;
+      verts->AllocateExact(nParticles, nParticles);
+      for (vtkIdType i = 0; i < nParticles; ++i)
       {
-        vtkErrorMacro(<< "Error reading line " << io.GetLineNumber() << " of " << io.GetFileName()
-                      << ": " << err);
-        continue;
+        verts->InsertNextCell(1, &i);
       }
+      cloudMesh->SetVerts(verts);
     }
 
-    const vtkIdType nParticles = points->GetNumberOfPoints();
-
-    // Create lagrangian mesh
-    cloudMesh->AllocateEstimate(nParticles, 1);
-    for (vtkIdType i = 0; i < nParticles; i++)
-    {
-      cloudMesh->InsertNextCell(VTK_VERTEX, 1, &i);
-    }
-    cloudMesh->SetPoints(points);
+    const vtkIdType nParticles = cloudMesh->GetPoints()->GetNumberOfPoints();
 
+    // Can be empty or missing for a particular region or processor
     if (!nParticles)
     {
-      continue; // Could be empty or missing for a particular region or processor
+      continue;
     }
 
     // Read lagrangian fields
@@ -11291,6 +11328,16 @@ int vtkOpenFOAMReader::MakeMetaDataAtTimeStep(const bool listNextTimeStep)
   vtkNew<vtkStringArray> cellDataNames;
   vtkNew<vtkStringArray> pointDataNames;
   vtkNew<vtkStringArray> lagrangianDataNames;
+  vtkNew<vtkStringArray> lagrangianPaths;
+
+  if (listNextTimeStep)
+  {
+    this->LagrangianPaths->Initialize();
+  }
+  else
+  {
+    lagrangianPaths->DeepCopy(this->LagrangianPaths);
+  }
 
   int ret = 1;
   vtkOpenFOAMReaderPrivate* reader;
@@ -11300,11 +11347,19 @@ int vtkOpenFOAMReader::MakeMetaDataAtTimeStep(const bool listNextTimeStep)
   {
     ret *= reader->MakeMetaDataAtTimeStep(
       cellDataNames, pointDataNames, lagrangianDataNames, listNextTimeStep);
+
+    appendUniq(lagrangianPaths, reader->GetLagrangianPaths());
   }
   this->AddSelectionNames(this->Parent->CellDataArraySelection, cellDataNames);
   this->AddSelectionNames(this->Parent->PointDataArraySelection, pointDataNames);
   this->AddSelectionNames(this->Parent->LagrangianDataArraySelection, lagrangianDataNames);
 
+  lagrangianPaths->Squeeze();
+  vtkSortDataArray::Sort(lagrangianPaths);
+
+  // Combine for all regions
+  this->LagrangianPaths->DeepCopy(lagrangianPaths);
+
   return ret;
 }
 
-- 
GitLab