From cee6524c34c3f22932bc92bf8e5fe84025956418 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Tue, 5 Jul 2022 14:14:21 +0200
Subject: [PATCH] ENH: delay writing of ensight case until after
 geometry/fields (#2512)

- in situations where the simulation diverges, the ensight writing can
  be incomplete. If the case file is updated prior to writing geometry
  or fields, the generated case may refer to incomplete entries (which
  make loading problematic).

  NOTE: if multiple fields are sampled and written, this change cannot
  entirely prevent case files addressing corrupt fields. For example,

  1a. write U field, update case file with new times/fields
  1b. write p field, update case file with new times/fields
  2a. write U field, update case file with new times
  2b. write p field, but fails

  Since 2a already updates the case file with a new time-step entry
  (for the U field), the case glob patterns will automatically include
  the not-yet-written 'p' field. If this write fails with an
  incomplete/corrupt field, the case file will still be addressing it!
---
 .../ensight/ensightCoordSetWriterCollated.C   |  95 ++++++++--------
 .../ensight/ensightCoordSetWriterUncollated.C |  69 ++++++------
 .../ensight/ensightSurfaceWriterCollated.C    | 106 +++++++++---------
 .../ensight/ensightSurfaceWriterUncollated.C  |  88 ++++++++-------
 4 files changed, 182 insertions(+), 176 deletions(-)

diff --git a/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterCollated.C b/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterCollated.C
index 3a0cafb27f7..759544e15a6 100644
--- a/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterCollated.C
+++ b/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterCollated.C
@@ -122,7 +122,53 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
         );
 
 
-        // Do case file
+        // Location for data (and possibly the geometry as well)
+        fileName dataDir = baseDir/"data"/word::printf(fmt, timeIndex);
+
+        // As per mkdir -p "data/00000000"
+        mkDir(dataDir);
+
+
+        const fileName geomFile(baseDir/geometryName);
+
+        if (!exists(geomFile))
+        {
+            if (verbose_)
+            {
+                Info<< "Writing geometry to " << geomFile.name() << endl;
+            }
+
+            // Two-argument form for path-name to avoid validating base-dir
+            ensightGeoFile osGeom
+            (
+                geomFile.path(),
+                geomFile.name(),
+                writeFormat_
+            );
+
+            writeGeometry(osGeom, elemOutput);
+        }
+
+
+        // Write field
+        ensightFile osField
+        (
+            dataDir,
+            varName,
+            writeFormat_
+        );
+
+        if (verbose_)
+        {
+            Info<< "Writing field file to " << osField.name() << endl;
+        }
+
+
+        // Write field (serial only)
+        writeTrackField<Type>(osField, fieldPtrs);
+
+
+        // Update case file
         if (stateChanged)
         {
             OFstream osCase(outputFile, IOstream::ASCII);
@@ -216,53 +262,6 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
             osCase << "# end" << nl;
         }
 
-
-        // Location for data (and possibly the geometry as well)
-        fileName dataDir = baseDir/"data"/word::printf(fmt, timeIndex);
-
-        // As per mkdir -p "data/00000000"
-        mkDir(dataDir);
-
-
-        const fileName geomFile(baseDir/geometryName);
-
-        if (!exists(geomFile))
-        {
-            if (verbose_)
-            {
-                Info<< "Writing geometry to " << geomFile.name() << endl;
-            }
-
-            // Two-argument form for path-name to avoid validating base-dir
-            ensightGeoFile osGeom
-            (
-                geomFile.path(),
-                geomFile.name(),
-                writeFormat_
-            );
-
-            writeGeometry(osGeom, elemOutput);
-        }
-
-
-        // Write field
-        ensightFile osField
-        (
-            dataDir,
-            varName,
-            writeFormat_
-        );
-
-        if (verbose_)
-        {
-            Info<< "Writing field file to " << osField.name() << endl;
-        }
-
-
-        // Write field (serial only)
-        writeTrackField<Type>(osField, fieldPtrs);
-
-
         // Timestamp in the directory for future reference
         {
             OFstream timeStamp(dataDir/"time");
diff --git a/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterUncollated.C b/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterUncollated.C
index f0f89997ede..4849dc1ff9f 100644
--- a/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterUncollated.C
+++ b/src/meshTools/coordSet/writers/ensight/ensightCoordSetWriterUncollated.C
@@ -97,13 +97,6 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
             mkDir(outputFile.path());
         }
 
-        OFstream osCase(outputFile, IOstream::ASCII);
-
-        // Format options
-        osCase.setf(ios_base::left);
-        osCase.setf(ios_base::scientific, ios_base::floatfield);
-        osCase.precision(5);
-
         // Two-argument form for path-name to avoid validating base-dir
         ensightGeoFile osGeom
         (
@@ -118,36 +111,46 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
             writeFormat_
         );
 
-        osCase
-            << "FORMAT" << nl
-            << "type: ensight gold" << nl
-            << nl
-            << "GEOMETRY" << nl
-            << "model:  1   " << osGeom.name().name() << nl
-            << nl
-            << "VARIABLE" << nl
-            << ensightPTraits<Type>::typeName
-            <<
-            (
-                true  // this->isPointData()
-              ? " per node:    1  "  // time-set 1
-              : " per element: 1  "  // time-set 1
-            )
-            << setw(15) << varName << ' '
-            << baseName.c_str() << ".********." << varName << nl;
-
-        osCase
-            << nl
-            << "TIME" << nl;
-
-        ensightCase::printTimeset(osCase, 1, timeValue);
-        osCase << "# end" << nl;
-
-
         writeGeometry(osGeom, elemOutput);
 
         // Write field (serial only)
         writeTrackField<Type>(osField, fieldPtrs);
+
+
+        // Update case file
+        {
+            OFstream osCase(outputFile, IOstream::ASCII);
+
+            // Format options
+            osCase.setf(ios_base::left);
+            osCase.setf(ios_base::scientific, ios_base::floatfield);
+            osCase.precision(5);
+
+            osCase
+                << "FORMAT" << nl
+                << "type: ensight gold" << nl
+                << nl
+                << "GEOMETRY" << nl
+                << "model:  1   " << osGeom.name().name() << nl
+                << nl
+                << "VARIABLE" << nl
+                << ensightPTraits<Type>::typeName
+                <<
+                (
+                    true  // this->isPointData()
+                  ? " per node:    1  "  // time-set 1
+                  : " per element: 1  "  // time-set 1
+                )
+                << setw(15) << varName << ' '
+                << baseName.c_str() << ".********." << varName << nl;
+
+            osCase
+                << nl
+                << "TIME" << nl;
+
+            ensightCase::printTimeset(osCase, 1, timeValue);
+            osCase << "# end" << nl;
+        }
     }
 
     wroteGeom_ = true;
diff --git a/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C b/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C
index 511df9159a8..18da1870965 100644
--- a/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C
+++ b/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C
@@ -129,7 +129,59 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
         );
 
 
-        // Do case file
+        // Location for data (and possibly the geometry as well)
+        fileName dataDir = baseDir/"data"/word::printf(fmt, timeIndex);
+
+        // As per mkdir -p "data/00000000"
+        mkDir(dataDir);
+
+
+        const fileName geomFile(baseDir/geometryName);
+
+        // Ensight Geometry
+        ensightOutputSurface part
+        (
+            surf.points(),
+            surf.faces(),
+            geomFile.name()
+        );
+
+        if (!exists(geomFile))
+        {
+            if (verbose_)
+            {
+                Info<< "Writing geometry to " << geomFile.name() << endl;
+            }
+
+            // Two-argument form for path-name to avoid validating base-dir
+            ensightGeoFile osGeom
+            (
+                geomFile.path(),
+                geomFile.name(),
+                writeFormat_
+            );
+            part.write(osGeom); // serial
+        }
+
+        // Write field
+        ensightFile osField
+        (
+            dataDir,
+            varName,
+            writeFormat_
+        );
+
+        if (verbose_)
+        {
+            Info<< "Writing field file to " << osField.name() << endl;
+        }
+
+        // Write field (serial only)
+        osField.writeKeyword(ensightPTraits<Type>::typeName);
+        part.writeData(osField, tfield(), this->isPointData());
+
+
+        // Update case file
         if (stateChanged)
         {
             OFstream osCase(outputFile, IOstream::ASCII);
@@ -223,58 +275,6 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
             osCase << "# end" << nl;
         }
 
-
-        // Location for data (and possibly the geometry as well)
-        fileName dataDir = baseDir/"data"/word::printf(fmt, timeIndex);
-
-        // As per mkdir -p "data/00000000"
-        mkDir(dataDir);
-
-
-        const fileName geomFile(baseDir/geometryName);
-
-        // Ensight Geometry
-        ensightOutputSurface part
-        (
-            surf.points(),
-            surf.faces(),
-            geomFile.name()
-        );
-
-        if (!exists(geomFile))
-        {
-            if (verbose_)
-            {
-                Info<< "Writing geometry to " << geomFile.name() << endl;
-            }
-
-            // Two-argument form for path-name to avoid validating base-dir
-            ensightGeoFile osGeom
-            (
-                geomFile.path(),
-                geomFile.name(),
-                writeFormat_
-            );
-            part.write(osGeom); // serial
-        }
-
-        // Write field
-        ensightFile osField
-        (
-            dataDir,
-            varName,
-            writeFormat_
-        );
-
-        if (verbose_)
-        {
-            Info<< "Writing field file to " << osField.name() << endl;
-        }
-
-        // Write field (serial only)
-        osField.writeKeyword(ensightPTraits<Type>::typeName);
-        part.writeData(osField, tfield(), this->isPointData());
-
         // Timestamp in the directory for future reference
         {
             OFstream timeStamp(dataDir/"time");
diff --git a/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C b/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C
index fde360edc0c..9d73cc06f79 100644
--- a/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C
+++ b/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C
@@ -69,7 +69,6 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
             mkDir(outputDir);
         }
 
-        OFstream osCase(outputFile);
         ensightGeoFile osGeom
         (
             outputDir,
@@ -77,6 +76,16 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
             writeFormat_
         );
 
+        ensightOutputSurface part
+        (
+            surf.points(),
+            surf.faces(),
+            osGeom.name().name()
+        );
+        part.write(osGeom); // serial
+
+        // Update case file
+        OFstream osCase(outputFile);
         osCase
             << "FORMAT" << nl
             << "type: ensight gold" << nl
@@ -87,14 +96,6 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
             << "TIME" << nl;
 
         ensightCase::printTimeset(osCase, 1, scalar(0));
-
-        ensightOutputSurface part
-        (
-            surf.points(),
-            surf.faces(),
-            osGeom.name().name()
-        );
-        part.write(osGeom); // serial
     }
 
     wroteGeom_ = true;
@@ -170,13 +171,6 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
             mkDir(outputFile.path());
         }
 
-        OFstream osCase(outputFile, IOstream::ASCII);
-
-        // Format options
-        osCase.setf(ios_base::left);
-        osCase.setf(ios_base::scientific, ios_base::floatfield);
-        osCase.precision(5);
-
         // Two-argument form for path-name to avoid validating base-dir
         ensightGeoFile osGeom
         (
@@ -191,32 +185,6 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
             writeFormat_
         );
 
-        osCase
-            << "FORMAT" << nl
-            << "type: ensight gold" << nl
-            << nl
-            << "GEOMETRY" << nl
-            << "model:  1   " << osGeom.name().name() << nl
-            << nl
-            << "VARIABLE" << nl
-            << ensightPTraits<Type>::typeName
-            <<
-            (
-                this->isPointData()
-              ? " per node:    1  "  // time-set 1
-              : " per element: 1  "  // time-set 1
-            )
-            << setw(15) << varName << ' '
-            << baseName.c_str() << ".********." << varName << nl;
-
-        osCase
-            << nl
-            << "TIME" << nl;
-
-        ensightCase::printTimeset(osCase, 1, timeValue);
-        osCase << "# end" << nl;
-
-
         // Ensight Geometry
         ensightOutputSurface part
         (
@@ -229,6 +197,42 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
         // Write field (serial)
         osField.writeKeyword(ensightPTraits<Type>::typeName);
         part.writeData(osField, tfield(), this->isPointData());
+
+
+        // Update case file
+        {
+            OFstream osCase(outputFile, IOstream::ASCII);
+
+            // Format options
+            osCase.setf(ios_base::left);
+            osCase.setf(ios_base::scientific, ios_base::floatfield);
+            osCase.precision(5);
+
+            osCase
+                << "FORMAT" << nl
+                << "type: ensight gold" << nl
+                << nl
+                << "GEOMETRY" << nl
+                << "model:  1   " << osGeom.name().name() << nl
+                << nl
+                << "VARIABLE" << nl
+                << ensightPTraits<Type>::typeName
+                <<
+                (
+                    this->isPointData()
+                  ? " per node:    1  "  // time-set 1
+                  : " per element: 1  "  // time-set 1
+                )
+                << setw(15) << varName << ' '
+                << baseName.c_str() << ".********." << varName << nl;
+
+            osCase
+                << nl
+                << "TIME" << nl;
+
+                ensightCase::printTimeset(osCase, 1, timeValue);
+                osCase << "# end" << nl;
+        }
     }
 
     wroteGeom_ = true;
-- 
GitLab