From 1b017938c30ddc83e69ff5a1efc9d9b69e0db1ae Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Mon, 2 Dec 2019 09:42:52 +0100
Subject: [PATCH] ENH: be more forgiving when reading ensight surface files
 (#1511)

- The case files may contain #... comment lines
- The geometry file may contain an optional "extents" entry
- Properly handle element id specifications (off|assign|ignore|given).

- Partially handle node id specifications (off|assign|ignore|given).
  Treat "given" like "ignore", since results in the lightest amount of
  coding and in many cases the "given" node ids are in fact 1-based
  contiguous values and thus no different than "ignore" for our
  purposes.
---
 .../readers/ensight/ensightSurfaceReader.C    | 176 +++++++++++++++---
 .../readers/ensight/ensightSurfaceReader.H    |  16 +-
 2 files changed, 161 insertions(+), 31 deletions(-)

diff --git a/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.C b/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.C
index ce95050bfaf..eefcf1d2835 100644
--- a/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.C
+++ b/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.C
@@ -37,6 +37,29 @@ namespace Foam
 }
 
 
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Read and discard specified number of elements
+template<class Type>
+static inline void discard(label n, ensightReadFile& is)
+{
+    Type val;
+
+    while (n > 0)
+    {
+        is.read(val);
+        --n;
+    }
+}
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
 void Foam::ensightSurfaceReader::skip(const label n, Istream& is) const
 {
     label i = 0;
@@ -53,20 +76,28 @@ void Foam::ensightSurfaceReader::skip(const label n, Istream& is) const
     if (i != n)
     {
         WarningInFunction
-            << "Requested to skip " << n << "tokens, but stream exited after "
+            << "Requested to skip " << n << " tokens, but stream exited after "
             << i << " tokens. Last token read: " << tok
             << nl;
     }
 }
 
 
-void Foam::ensightSurfaceReader::readLine(IFstream& is, string& buffer) const
+void Foam::ensightSurfaceReader::readLine(IFstream& is, string& line) const
 {
-    buffer.clear();
-    while (is.good() && buffer.empty())
+    do
     {
-        is.getLine(buffer);
+        is.getLine(line);
+
+        // Trim out any '#' comments
+        const auto pos = line.find('#');
+        if (pos != std::string::npos)
+        {
+            line.erase(pos);
+        }
+        stringOps::inplaceTrimRight(line);
     }
+    while (line.empty() && is.good());
 }
 
 
@@ -83,7 +114,7 @@ void Foam::ensightSurfaceReader::debugSection
     {
         FatalIOErrorInFunction(is)
             << "Expected section header '" << expected
-            << "' but read the word '" << actual << "'"
+            << "' but read " << actual << nl
             << exit(FatalIOError);
     }
 
@@ -92,13 +123,16 @@ void Foam::ensightSurfaceReader::debugSection
 }
 
 
-void Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
+Foam::Pair<Foam::ensightSurfaceReader::idTypes>
+Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
 {
     // Binary flag string if applicable
     is.readBinaryHeader();
 
     string buffer;
 
+    Pair<idTypes> idHandling(idTypes::NONE, idTypes::NONE);
+
     // Ensight Geometry File
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
@@ -107,30 +141,63 @@ void Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
 
-    // Node info
+    // "node id (off|assign|given|ignore)" - "given" is not actually supported
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
 
-    // Element info
+    if (buffer.find("ignore") != std::string::npos)
+    {
+        idHandling.first() = idTypes::IGNORE;
+    }
+    else if (buffer.find("given") != std::string::npos)
+    {
+        idHandling.first() = idTypes::GIVEN;
+    }
+
+    // "element id (off|assign|given|ignore)"
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
 
-    // Part
+    if (buffer.find("ignore") != std::string::npos)
+    {
+        idHandling.second() = idTypes::IGNORE;
+    }
+    else if (buffer.find("given") != std::string::npos)
+    {
+        idHandling.second() = idTypes::GIVEN;
+    }
+
+
+    // "part" - but could also be an optional "extents"
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
 
-    // Part number
-    label ibuffer;
-    is.read(ibuffer);
-    DebugInfo<< "ibuffer: " << ibuffer << nl;
+    if (buffer.find("extents") != std::string::npos)
+    {
+        // Optional extents - read and discard 6 floats
+        // (xmin, xmax, ymin, ymax, zmin, zmax)
+
+        discard<scalar>(6, is);
+
+        // Part
+        is.read(buffer);
+        DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
+    }
+
+    // The part number
+    label ivalue;
+    is.read(ivalue);
+    DebugInfo<< "ivalue: " << ivalue << nl;
 
-    // Description - 2
+    // Part description / name
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
 
-    // Coordinates
+    // "coordinates"
     is.read(buffer);
     DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
+
+    return idHandling;
 }
 
 
@@ -166,8 +233,8 @@ void Foam::ensightSurfaceReader::readCase(IFstream& is)
     debugSection("VARIABLE", is);
 
     // Read the field description
-    DynamicList<word> fieldNames(10);
-    DynamicList<string> fieldFileNames(10);
+    DynamicList<word> fieldNames(16);
+    DynamicList<string> fieldFileNames(16);
 
     while (is.good())
     {
@@ -228,7 +295,7 @@ void Foam::ensightSurfaceReader::readCase(IFstream& is)
     // Read the time values
     readLine(is, buffer); // time values:
     timeValues_.setSize(nTimeSteps_);
-    for (label i = 0; i < nTimeSteps_; i++)
+    for (label i = 0; i < nTimeSteps_; ++i)
     {
         scalar t(readScalar(is));
 
@@ -329,7 +396,7 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry()
         DebugInfo
             << "File: " << is.name() << nl;
 
-        readGeometryHeader(is);
+        Pair<idTypes> idHandling = readGeometryHeader(is);
 
         label nPoints;
         is.read(nPoints);
@@ -337,17 +404,31 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry()
         DebugInfo
             << "nPoints: " << nPoints << nl;
 
+        if (idHandling.first() == idTypes::GIVEN)
+        {
+            WarningInFunction
+                << "Treating node id 'given' as being 'ignore'" << nl
+                << "If something fails, this could be the reason" << nl
+                << endl;
+
+            idHandling.first() = idTypes::IGNORE;
+        }
+
+        if (idHandling.first() == idTypes::IGNORE)
+        {
+            DebugInfo
+                << "Ignore " << nPoints << " node ids" << nl;
+
+            // Read and discard labels
+            discard<label>(nPoints, is);
+        }
+
         pointField points(nPoints);
+        for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt)
         {
-            scalarField x(nPoints);
-            for (label dir = 0; dir < 3; dir++)
+            for (point& pt : points)
             {
-                forAll(points, pointI)
-                {
-                    is.read(x[pointI]);
-                }
-
-                points.replace(dir, x);
+                is.read(pt[cmpt]);
             }
         }
 
@@ -375,6 +456,19 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry()
                     << "faceType <" << faceType.c_str() << "> count: "
                     << nFace << nl;
 
+                if
+                (
+                    idHandling.second() == idTypes::IGNORE
+                 || idHandling.second() == idTypes::GIVEN
+                )
+                {
+                    DebugInfo
+                        << "Ignore " << nFace << " element ids" << nl;
+
+                    // Read and discard labels
+                    discard<label>(nFace, is);
+                }
+
                 face f(3);
                 for (label facei = 0; facei < nFace; ++facei)
                 {
@@ -394,6 +488,19 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry()
                     << "faceType <" << faceType.c_str() << "> count: "
                     << nFace << nl;
 
+                if
+                (
+                    idHandling.second() == idTypes::IGNORE
+                 || idHandling.second() == idTypes::GIVEN
+                )
+                {
+                    DebugInfo
+                        << "Ignore " << nFace << " element ids" << nl;
+
+                    // Read and discard labels
+                    discard<label>(nFace, is);
+                }
+
                 face f(4);
                 for (label facei = 0; facei < nFace; ++facei)
                 {
@@ -413,6 +520,19 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry()
                     << "faceType <" << faceType.c_str() << "> count: "
                     << nFace << nl;
 
+                if
+                (
+                    idHandling.second() == idTypes::IGNORE
+                 || idHandling.second() == idTypes::GIVEN
+                )
+                {
+                    DebugInfo
+                        << "Ignore " << nFace << " element ids" << nl;
+
+                    // Read and discard labels
+                    discard<label>(nFace, is);
+                }
+
                 labelList np(nFace);
                 for (label facei = 0; facei < nFace; ++facei)
                 {
diff --git a/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.H b/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.H
index 376fda35141..5b30eceebde 100644
--- a/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.H
+++ b/src/sampling/sampledSurface/readers/ensight/ensightSurfaceReader.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2015-2016 OpenCFD Ltd.
+    Copyright (C) 2015-2019 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -40,6 +40,7 @@ SourceFiles
 #include "surfaceReader.H"
 #include "ensightReadFile.H"
 #include "StringStream.H"
+#include "Pair.H"
 #include "Tuple2.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -59,6 +60,14 @@ protected:
 
     // Protected Data
 
+        //- Handling of node/element id types (off, assign, ignore, given)
+        enum idTypes : unsigned char
+        {
+            NONE = 0,       //!< "off", "assign"
+            IGNORE = 1,     //!< Read but "ignore"
+            GIVEN = 2       //!< Use "given" values (not supported)
+        };
+
         //- Format flag
         IOstream::streamFormat streamFormat_;
 
@@ -103,8 +112,9 @@ protected:
         //- Read and check a section header
         void debugSection(const word& expected, IFstream& is) const;
 
-        //- Read (and throw away) geometry file header
-        void readGeometryHeader(ensightReadFile& is) const;
+        //- Read (and discard) geometry file header.
+        //  \return information about node/element id handling
+        Pair<idTypes> readGeometryHeader(ensightReadFile& is) const;
 
         //- Read the case file
         void readCase(IFstream& is);
-- 
GitLab