From 01a313d8890ddc3568cdd52f8f745edb76cbb392 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Mon, 7 May 2018 16:59:15 +0200
Subject: [PATCH] BUG: collated ensight not working with isosurfaces (closes
 #318)

- the problem arises since the various surface writers are stateless.
  The collated output format hacks around this limitation by adding in
  its own fieldDict caching (to disk).

  Now include an updateMesh() method to hook into geometry changes.
  This is considered a stop-gap measure until the surface output
  handling is improved.
---
 src/fileFormats/ensight/file/ensightCase.C    |   2 +-
 src/fileFormats/ensight/file/ensightCase.H    |   2 +-
 .../sampledSurfaces/sampledSurfaces.C         |  23 +++-
 .../sampledSurfaces/sampledSurfaces.H         |   3 +
 .../sampledSurfacesTemplates.C                |   7 ++
 .../writers/ensight/ensightSurfaceWriter.C    | 111 ++++++++++++++++--
 .../writers/ensight/ensightSurfaceWriter.H    |  26 +++-
 .../ensight/ensightSurfaceWriterTemplates.C   | 110 +++++++++--------
 .../sampledSurface/writers/surfaceWriter.H    |  10 ++
 .../squareBend/system/controlDict             |   1 +
 .../squareBend/system/samplingDebug           |   1 +
 11 files changed, 224 insertions(+), 72 deletions(-)

diff --git a/src/fileFormats/ensight/file/ensightCase.C b/src/fileFormats/ensight/file/ensightCase.C
index 1d7ff6fc31e..2338cfbf0de 100644
--- a/src/fileFormats/ensight/file/ensightCase.C
+++ b/src/fileFormats/ensight/file/ensightCase.C
@@ -138,7 +138,7 @@ Foam::scalar Foam::ensightCase::writeTimeset() const
 {
     const label ts = 1;
 
-    const labelList indices = timesUsed_.sortedToc();
+    const labelList indices(timesUsed_.sortedToc());
     label count = indices.size();
 
     // correct for negative starting values
diff --git a/src/fileFormats/ensight/file/ensightCase.H b/src/fileFormats/ensight/file/ensightCase.H
index 67835f2e7d6..1b516eed0ac 100644
--- a/src/fileFormats/ensight/file/ensightCase.H
+++ b/src/fileFormats/ensight/file/ensightCase.H
@@ -106,7 +106,7 @@ private:
         //- Record time indices when geometry is written.
         //  These values will be used to decide if timeset 1
         //  or a separate timeset are used.
-        //  The special index '-1' is used static geometry.
+        //  The special index '-1' is used for static geometry.
         mutable labelHashSet geomTimes_;
 
         //- Record time indices when clouds are written.
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C
index c1fcbe4adf1..721b684b6ce 100644
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C
+++ b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C
@@ -135,6 +135,7 @@ Foam::sampledSurfaces::sampledSurfaces
     sampleFaceScheme_(word::null),
     sampleNodeScheme_(word::null),
     mergedList_(),
+    changedGeom_(),
     formatter_(nullptr)
 {
     if (Pstream::parRun())
@@ -168,6 +169,7 @@ Foam::sampledSurfaces::sampledSurfaces
     sampleFaceScheme_(word::null),
     sampleNodeScheme_(word::null),
     mergedList_(),
+    changedGeom_(),
     formatter_(nullptr)
 {
     read(dict);
@@ -219,11 +221,12 @@ bool Foam::sampledSurfaces::write()
 
     const label nFields = classifyFields();
 
-    // write geometry first if required,
+    // Write geometry first if required,
     // or when no fields would otherwise be written
-    if (nFields == 0 || formatter_->separateGeometry())
+    if (formatter_->separateGeometry() || !nFields)
     {
         writeGeometry();
+        changedGeom_ = false;
     }
 
     const IOobjectList objects(obr_, obr_.time().timeName());
@@ -246,9 +249,7 @@ bool Foam::sampledSurfaces::write()
 
 bool Foam::sampledSurfaces::read(const dictionary& dict)
 {
-    bool surfacesFound = dict.found("surfaces");
-
-    if (surfacesFound)
+    if (dict.found("surfaces"))
     {
         sampleFaceScheme_ = dict.lookupOrDefault<word>("sampleScheme", "cell");
 
@@ -304,6 +305,10 @@ bool Foam::sampledSurfaces::read(const dictionary& dict)
         Pout<< ")" << endl;
     }
 
+    // New geometry
+    changedGeom_.resize(size());
+    changedGeom_ = true;
+
     return true;
 }
 
@@ -369,6 +374,8 @@ bool Foam::sampledSurfaces::expire()
         }
     }
 
+    changedGeom_ = true;
+
     // true if any surfaces just expired
     return justExpired;
 }
@@ -388,9 +395,12 @@ bool Foam::sampledSurfaces::update()
     {
         forAll(*this, surfi)
         {
-            if (operator[](surfi).update())
+            sampledSurface& s = operator[](surfi);
+
+            if (s.update())
             {
                 updated = true;
+                changedGeom_[surfi] = true;
             }
         }
 
@@ -414,6 +424,7 @@ bool Foam::sampledSurfaces::update()
         if (s.update())
         {
             updated = true;
+            changedGeom_[surfi] = true;
             mergedList_[surfi].merge(s, mergeDim);
         }
     }
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H
index 590da3ed4b9..16432d71653 100644
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H
+++ b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H
@@ -151,6 +151,9 @@ class sampledSurfaces
         //- Merged meshed surfaces (parallel only)
         List<mergedSurf> mergedList_;
 
+        //- Track which surfaces have changed
+        List<bool> changedGeom_;
+
 
     // Calculated
 
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C
index f314d938187..19ccc258a66 100644
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C
+++ b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C
@@ -42,6 +42,13 @@ void Foam::sampledSurfaces::writeSurface
 {
     const sampledSurface& s = operator[](surfi);
 
+    if (changedGeom_[surfi])
+    {
+        // Trigger any changes
+        formatter_->updateMesh(outputDir, s.name());
+        changedGeom_[surfi] = false;
+    }
+
     if (Pstream::parRun())
     {
         // Collect values from all processors
diff --git a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C
index 77cdd19687e..ef6575a29d6 100644
--- a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C
+++ b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2013 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2015-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,6 +36,61 @@ namespace Foam
 }
 
 
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+void Foam::ensightSurfaceWriter::printTimeset
+(
+    OSstream& os,
+    const label ts,
+    const scalar& timeValue
+)
+{
+    os
+        << "time set:               " << ts << nl
+        << "number of steps:        " << 1 << nl;
+
+    // Assume to be contiguous numbering
+    os  << "filename start number:  0" << nl
+        << "filename increment:     1" << nl
+        << "time values:" << nl;
+
+    os  << "    " << timeValue
+        << nl << nl;
+}
+
+
+void Foam::ensightSurfaceWriter::printTimeset
+(
+    OSstream& os,
+    const label ts,
+    const UList<scalar>& values
+)
+{
+    label count = values.size();
+    os
+        << "time set:               " << ts << nl
+        << "number of steps:        " << count << nl;
+
+    // Assume to be contiguous numbering
+    os  << "filename start number:  0" << nl
+        << "filename increment:     1" << nl
+        << "time values:" << nl;
+
+    count = 0;
+    for (const scalar& t : values)
+    {
+        os << ' ' << setw(12) << t;
+
+        if (++count % 6 == 0)
+        {
+            os << nl;
+        }
+    }
+    os  << nl << nl;
+}
+
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::ensightSurfaceWriter::ensightSurfaceWriter()
@@ -75,6 +130,47 @@ bool Foam::ensightSurfaceWriter::separateGeometry() const
 }
 
 
+void Foam::ensightSurfaceWriter::updateMesh
+(
+    const fileName& outputDir,
+    const fileName& surfaceName
+) const
+{
+    if (collateTimes_ && Pstream::master())
+    {
+        const ensight::FileName surfName(surfaceName);
+
+        const fileName baseDir = outputDir.path()/surfName;
+        const fileName timeDir = outputDir.name();
+        const scalar timeValue = readScalar(timeDir);
+
+        if (!isDir(baseDir))
+        {
+            mkDir(baseDir);
+        }
+
+        dictionary dict;
+
+        if (isFile(baseDir/"fieldsDict"))
+        {
+            IFstream is(baseDir/"fieldsDict");
+            if (is.good() && dict.read(is))
+            {
+                dict.read(is);
+            }
+        }
+
+        dict.set("updateMesh", timeValue);
+
+        {
+            OFstream os(baseDir/"fieldsDict");
+            os << "// Summary of Ensight fields, times" << nl << nl;
+            dict.write(os, false);
+        }
+    }
+}
+
+
 Foam::fileName Foam::ensightSurfaceWriter::write
 (
     const fileName& outputDir,
@@ -92,8 +188,6 @@ Foam::fileName Foam::ensightSurfaceWriter::write
         mkDir(outputDir);
     }
 
-    const scalar timeValue = 0.0;
-
     OFstream osCase(outputDir/surfName + ".case");
     ensightGeoFile osGeom
     (
@@ -114,14 +208,9 @@ Foam::fileName Foam::ensightSurfaceWriter::write
         << "GEOMETRY" << nl
         << "model:        1     " << osGeom.name().name() << nl
         << nl
-        << "TIME" << nl
-        << "time set:                      1" << nl
-        << "number of steps:               1" << nl
-        << "filename start number:         0" << nl
-        << "filename increment:            1" << nl
-        << "time values:" << nl
-        << "    " << timeValue << nl
-        << nl;
+        << "TIME" << nl;
+
+    printTimeset(osCase, 1, 0.0);
 
     ensightPartFaces ensPart(0, osGeom.name().name(), points, faces, true);
     osGeom << ensPart;
diff --git a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H
index 4086a1875d0..db5ef7adb7a 100644
--- a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H
+++ b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2015-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -79,6 +79,23 @@ class ensightSurfaceWriter
 
     // Private Member Functions
 
+        //- Print time-set for ensight case file
+        static void printTimeset
+        (
+            OSstream& os,
+            const label ts,
+            const scalar& timeValue
+        );
+
+        //- Print time-set for ensight case file
+        static void printTimeset
+        (
+            OSstream& os,
+            const label ts,
+            const UList<scalar>& times
+        );
+
+
         //- Templated write operation - one file per timestep
         template<class Type>
         fileName writeCollated
@@ -144,6 +161,13 @@ public:
         //  False if geometry and field must be in a single file
         virtual bool separateGeometry() const;
 
+        //- Trigger for geometry changes.
+        //  \note this is a stop-gap solution
+        virtual void updateMesh
+        (
+            const fileName& outputDir,
+            const fileName& surfaceName
+        ) const; // override
 
         //- Write single surface geometry to file.
         virtual fileName write
diff --git a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterTemplates.C b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterTemplates.C
index c1fe9ae7fad..f04011fe01d 100644
--- a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterTemplates.C
+++ b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterTemplates.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2014 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2015-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,7 +29,6 @@ License
 #include "ensightPartFaces.H"
 #include "ensightSerialOutput.H"
 #include "ensightPTraits.H"
-#include "StringStream.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
@@ -63,15 +62,13 @@ Foam::fileName Foam::ensightSurfaceWriter::writeUncollated
 
     const fileName baseDir = outputDir/varName;
     const fileName timeDir = outputDir.name();
+    const scalar timeValue = readScalar(timeDir);
 
     if (!isDir(baseDir))
     {
         mkDir(baseDir);
     }
 
-    // const scalar timeValue = Foam::name(this->mesh().time().timeValue());
-    const scalar timeValue = readScalar(timeDir);
-
     OFstream osCase(baseDir/surfName + ".case");
     ensightGeoFile osGeom
     (
@@ -109,14 +106,10 @@ Foam::fileName Foam::ensightSurfaceWriter::writeUncollated
         << setw(15) << varName
         << "   " << surfName.c_str() << ".********." << varName << nl
         << nl
-        << "TIME" << nl
-        << "time set:               1" << nl
-        << "number of steps:        1" << nl
-        << "filename start number:  0" << nl
-        << "filename increment:     1" << nl
-        << "time values:" << nl
-        << "    " << timeValue
-        << nl << nl << "# end" << nl;
+        << "TIME" << nl;
+
+    printTimeset(osCase, 1, timeValue);
+    osCase << "# end" << nl;
 
     ensightPartFaces ensPart
     (
@@ -171,20 +164,22 @@ Foam::fileName Foam::ensightSurfaceWriter::writeCollated
 
     const fileName baseDir = outputDir.path()/surfName;
     const fileName timeDir = outputDir.name();
+    const scalar timeValue = readScalar(timeDir);
+    scalar meshValue = 0;
 
     if (!isDir(baseDir))
     {
         mkDir(baseDir);
     }
 
-    // surfName already validated
-    const fileName meshFile(baseDir/surfName + ".000000.mesh");
-    const scalar timeValue = readScalar(timeDir);
-    label timeIndex = 0;
+    label meshIndex = 0, timeIndex = 0;
+
+    fileName meshFile;
 
     // Do case file
     {
         dictionary dict;
+        scalarList meshes;
         scalarList times;
         bool stateChanged = false;
 
@@ -193,26 +188,56 @@ Foam::fileName Foam::ensightSurfaceWriter::writeCollated
             IFstream is(baseDir/"fieldsDict");
             if (is.good() && dict.read(is))
             {
-                dict.lookup("times") >> times;
-                const scalar timeValue = readScalar(timeDir);
-                label index = findLower(times, timeValue);
-                timeIndex = index+1;
+                dict.readIfPresent("meshes", meshes);
+                dict.readIfPresent("times", times);
+
+                timeIndex = 1+findLower(times, timeValue);
+
+                if (dict.readIfPresent("updateMesh", meshValue))
+                {
+                    meshIndex = 1+findLower(meshes, meshValue);
+
+                    dict.remove("updateMesh");
+                    stateChanged = true;
+                }
+                else if (meshes.size())
+                {
+                    meshIndex = meshes.size()-1;
+                    meshValue = meshes.last();
+                }
+                else
+                {
+                    meshIndex = 0;
+                }
             }
         }
 
-
         // Update stored times list
-        times.setSize(timeIndex+1, -1);
+        meshes.resize(meshIndex+1, -1);
+        times.resize(timeIndex+1, -1);
 
+        if (meshes[meshIndex] != meshValue)
+        {
+            stateChanged = true;
+        }
         if (times[timeIndex] != timeValue)
         {
             stateChanged = true;
         }
+
+        meshes[meshIndex] = meshValue;
         times[timeIndex] = timeValue;
 
+        // surfName already validated
+        meshFile =
+        (
+            baseDir/"data"/word::printf("%06d", meshIndex)/"geometry"
+        );
+
 
         // Add my information to dictionary
         {
+            dict.set("meshes", meshes);
             dict.set("times", times);
             if (dict.found("fields"))
             {
@@ -252,7 +277,7 @@ Foam::fileName Foam::ensightSurfaceWriter::writeCollated
             }
             {
                 OFstream os(baseDir/"fieldsDict");
-                os << "// summary of ensight times/fields" << nl << nl;
+                os << "// Summary of Ensight fields, times" << nl << nl;
                 dict.write(os, false);
             }
 
@@ -268,7 +293,8 @@ Foam::fileName Foam::ensightSurfaceWriter::writeCollated
                 << "type: ensight gold" << nl
                 << nl
                 << "GEOMETRY" << nl
-                << "model:  1   " << meshFile.name() << nl
+                << "model:  2  " // time-set 2
+                << " data/******/geometry" << nl
                 << nl
                 << "VARIABLE" << nl;
 
@@ -298,24 +324,12 @@ Foam::fileName Foam::ensightSurfaceWriter::writeCollated
             osCase << nl;
 
             osCase
-                << "TIME" << nl
-                << "time set:               1" << nl
-                << "number of steps:        " << timeIndex+1 << nl
-                << "filename start number:  0" << nl
-                << "filename increment:     1" << nl
-                << "time values:" << nl;
-
-            label count = 0;
-            forAll(times, timei)
-            {
-                osCase << ' ' << setw(12) << times[timei];
+                << "TIME" << nl;
 
-                if (!(++count % 6))
-                {
-                    osCase << nl;
-                }
-            }
-            osCase << nl << nl << "# end" << nl;
+            printTimeset(osCase, 1, times);
+            printTimeset(osCase, 2, meshes);
+
+            osCase << "# end" << nl;
         }
     }
 
@@ -341,16 +355,8 @@ Foam::fileName Foam::ensightSurfaceWriter::writeCollated
     }
 
 
-    // Get string representation
-    string timeString;
-    {
-        OStringStream os;
-        os.stdStream().fill('0');
-        os << setw(6) << timeIndex;
-        timeString = os.str();
-    }
-
-    fileName dataDir = baseDir/"data"/timeString;
+    // Location for data
+    fileName dataDir = baseDir/"data"/word::printf("%06d", timeIndex);
 
     // As per mkdir -p "data/000000"
     mkDir(dataDir);
diff --git a/src/sampling/sampledSurface/writers/surfaceWriter.H b/src/sampling/sampledSurface/writers/surfaceWriter.H
index 2b9b9315238..e22fca29773 100644
--- a/src/sampling/sampledSurface/writers/surfaceWriter.H
+++ b/src/sampling/sampledSurface/writers/surfaceWriter.H
@@ -113,6 +113,16 @@ public:
             return false;
         }
 
+        //- Trigger for geometry changes.
+        //  \note this is a stop-gap solution until the writing infrastructure
+        //  is improved.
+        virtual void updateMesh
+        (
+            const fileName& outputDir,
+            const fileName& surfaceName
+        ) const
+        {}
+
         //- Write single surface geometry to file.
         virtual fileName write
         (
diff --git a/tutorials/compressible/rhoSimpleFoam/squareBend/system/controlDict b/tutorials/compressible/rhoSimpleFoam/squareBend/system/controlDict
index b95b2cb2d28..74ccb50c5a6 100644
--- a/tutorials/compressible/rhoSimpleFoam/squareBend/system/controlDict
+++ b/tutorials/compressible/rhoSimpleFoam/squareBend/system/controlDict
@@ -52,6 +52,7 @@ runTimeModifiable true;
 functions
 {
     #include "sampling"
+    // #include "samplingDebug"
 }
 
 
diff --git a/tutorials/compressible/rhoSimpleFoam/squareBend/system/samplingDebug b/tutorials/compressible/rhoSimpleFoam/squareBend/system/samplingDebug
index 796b038f1ae..19c0f2d5c6d 100644
--- a/tutorials/compressible/rhoSimpleFoam/squareBend/system/samplingDebug
+++ b/tutorials/compressible/rhoSimpleFoam/squareBend/system/samplingDebug
@@ -21,6 +21,7 @@ debug
         ensight
         {
             collateTimes  true;
+            // collateTimes  false;
         }
     }
 
-- 
GitLab