ENH: read support for EnSight single-file transient (#3154)

- the ensightReadFile init() now automatically sets up binary/ascii
  (for geometry files) and checks for the transient "BEGIN TIME STEP"
  marker. If found, will also populate the file offsets for each of
  the timesteps.  If no corresponding footer is found (which would be
  very inefficient), it simply pretends that there is only a single
  time step instead of performing a costly file scan.

- parsing of the ensight case file now also supports the use of

      filename numbers:

  as an alternative to

      filename start number:
      filename increment:

- improved parsing robustness of "time values:" entry.
  Can now also have contents on the same line as the introducer.

ENH: base-level adjustments for writing transient single-file

- beginGeometry() is now separated out from file creation.

- in append mode, ensightFile and ensightGeoFile will attempt to
  parse existing time-step information.
This commit is contained in:
Mark Olesen
2024-05-13 18:57:38 +02:00
committed by Kutalmış Berçin
parent ee895577ae
commit dfc9a8923a
43 changed files with 2135 additions and 875 deletions

View File

@ -1,3 +0,0 @@
Test-ensightFile.C
EXE = $(FOAM_USER_APPBIN)/Test-ensightFile

View File

@ -1,8 +0,0 @@
EXE_INC = \
-I$(LIB_SRC)/fileFormats/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/conversion/lnInclude
EXE_LIBS = \
-lmeshTools \
-lconversion

View File

@ -0,0 +1,3 @@
Test-ensightFile1.cxx
EXE = $(FOAM_USER_APPBIN)/Test-ensightFile1

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/fileFormats/lnInclude
EXE_LIBS = \
-lfileFormats

View File

@ -0,0 +1,137 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-ensightFile
Description
check cleanup of ensight file and variable names
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "ensightFile.H"
#include "ensightGeoFile.H"
#include "Switch.H"
#include "IOstreams.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
argList::noBanner();
argList::noParallel();
argList::addBoolOption("ascii", "open as ascii instead of binary");
argList::addBoolOption("binary", "(default)");
argList::addBoolOption("clear", "force clear of time-steps");
argList::addBoolOption("no-end", "skip use of endTimeStep");
argList::addBoolOption("append", "open in append mode");
argList::addOption("geom", "geometry file");
argList::addOption("field", "field file");
#include "setRootCase.H"
const bool with_ascii = args.found("ascii") && !args.found("binary");
// const bool with_binary = args.found("binary");
const bool with_append = args.found("append");
const bool with_clear = args.found("clear");
const bool without_end = args.found("no-end");
const IOstreamOption::streamFormat fmt =
(
with_ascii
? IOstreamOption::ASCII
: IOstreamOption::BINARY
);
const IOstreamOption::appendType append =
(
with_append
? IOstreamOption::APPEND_ATE
: IOstreamOption::NO_APPEND
);
fileName file;
if (args.readIfPresent("geom", file))
{
Info<< "Open " << file << " as geometry "
<< " format:" << (with_ascii ? "ASCII" : "BINARY")
<< " append:" << Switch::name(with_append) << nl;
ensightGeoFile ensFile(append, file, fmt);
if (append)
{
ensFile.beginTimeStep();
// At the moment need to pair begin/end time-step calls
if (!without_end)
{
ensFile.endTimeStep();
}
}
if (with_clear)
{
ensFile.clearTimeSteps();
}
}
if (args.readIfPresent("field", file))
{
Info<< "Open " << file << " as field"
<< " format:" << (with_ascii ? "ASCII" : "BINARY")
<< " append:" << Switch::name(with_append) << nl;
ensightFile ensFile(append, file, fmt);
if (append)
{
ensFile.beginTimeStep();
// At the moment need to pair begin/end time-step calls
if (!without_end)
{
ensFile.endTimeStep();
}
}
if (with_clear)
{
ensFile.clearTimeSteps();
}
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
Test-ensightFileName.cxx
EXE = $(FOAM_USER_APPBIN)/Test-ensightFileName

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/fileFormats/lnInclude
EXE_LIBS = \
-lfileFormats

View File

@ -24,10 +24,10 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application Application
Test-ensightFile Test-ensightFileName
Description Description
check cleanup of ensight file and variable names Check cleanup of ensight file and variable names
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2022-2023 OpenCFD Ltd. Copyright (C) 2022-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -93,29 +93,34 @@ void Foam::fileFormats::ensightMeshReader::readIDs
( (
ensightReadFile& is, ensightReadFile& is,
const bool doRead, const bool doRead,
const label nShapes, const label elemCount,
labelList& foamToElem, labelList& foamToElem,
Map<label>& elemToFoam Map<label>& elemToFoam
) const ) const
{ {
const label sz = foamToElem.size(); const label begElem = foamToElem.size();
foamToElem.resize(sz+nShapes); const label endElem = begElem + elemCount;
foamToElem.resize(foamToElem.size()+elemCount);
if (doRead) if (doRead)
{ {
elemToFoam.reserve(elemToFoam.size()+nShapes); elemToFoam.reserve(elemToFoam.size()+elemCount);
for (label shapei = 0; shapei < nShapes; shapei++)
for (label elemi = begElem; elemi < endElem; ++elemi)
{ {
label elemi; label id;
is.read(elemi); is.read(id);
foamToElem[sz+shapei] = elemi; foamToElem[elemi] = id;
elemToFoam.insert(elemi, sz+shapei); elemToFoam.insert(id, elemi);
} }
} }
else else
{ {
for (label shapei = 0; shapei < nShapes; shapei++) // identity
for (label elemi = begElem; elemi < endElem; ++elemi)
{ {
foamToElem[sz+shapei] = sz+shapei; foamToElem[elemi] = elemi;
} }
} }
} }
@ -209,37 +214,54 @@ bool Foam::fileFormats::ensightMeshReader::readGoldPart
// Work // Work
DynamicList<label> verts; DynamicList<label> verts;
string line; string buffer;
while (is.good()) while (is.good())
{ {
do do
{ {
is.readKeyword(line); // Get entire line/string
is.read(buffer);
} }
while (line.empty() && is.good()); while (buffer.empty() && is.good());
const auto split = stringOps::splitSpace(line); if (!is.good())
{
break;
}
else if (buffer.contains("BEGIN TIME STEP"))
{
// Graciously handle a miscued start
continue;
}
else if (buffer.contains("END TIME STEP"))
{
// END TIME STEP is a valid means to terminate input
break;
}
const auto split = stringOps::splitSpace(buffer);
if (split.size() == 0) if (split.empty())
{ {
continue; continue;
} }
if (split[0] == "part") const auto keyword(split[0].str());
if (keyword == "part")
{ {
return false; return false;
} }
else if (split[0] == "node_ids") else if (keyword == "node_ids")
{ {
const label nPoints = points.size(); const label nPoints = points.size();
// Ignore for now // Ignore point ids
for (label i = 0; i < nPoints; i++) for (label pointi = 0; pointi < nPoints; ++pointi)
{ {
label index; label id;
is.read(index); is.read(id);
} }
} }
else if (split[0] == "coordinates") else if (keyword == "coordinates")
{ {
label nPoints; label nPoints;
is.read(nPoints); is.read(nPoints);
@ -257,207 +279,205 @@ bool Foam::fileFormats::ensightMeshReader::readGoldPart
nodeIdToPoints nodeIdToPoints
); );
points.setSize(nPoints);
for (label pointi = 0; pointi < nPoints; pointi++) is.readPoints(nPoints, points);
{
is.read(points[pointi].x());
}
for (label pointi = 0; pointi < nPoints; pointi++)
{
is.read(points[pointi].y());
}
for (label pointi = 0; pointi < nPoints; pointi++)
{
is.read(points[pointi].z());
}
} }
else if (split[0] == "tetra4") else if (keyword == "tetra4")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent<< "tetra4 " << nShapes Pout<< indent<< "tetra4 " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label celli = cells.size();
cells.resize(celli+nShapes);
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
cellToElemIds, cellToElemIds,
elemIdToCells elemIdToCells
); );
// Extend and fill the new trailing portion
const label startElemi = cells.size();
cells.resize(startElemi+elemCount);
faceListList::subList myElements = cells.slice(startElemi);
const auto& model = cellModel::ref(cellModel::TET); const auto& model = cellModel::ref(cellModel::TET);
for (label shapei = 0; shapei < nShapes; shapei++) for (auto& cellFaces : myElements)
{ {
readVerts(is, 4, nodeIdToPoints, verts); readVerts(is, 4, nodeIdToPoints, verts);
if (setHandedness_) if (setHandedness_)
{ {
setHandedness(model, verts, points); setHandedness(model, verts, points);
} }
const cellShape cellVerts(model, verts); cellFaces = cellShape(model, verts).faces();
cells[celli+shapei] = cellVerts.faces();
} }
} }
else if (split[0] == "pyramid5") else if (keyword == "pyramid5")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent<< "pyramid5 " << nShapes Pout<< indent<< "pyramid5 " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label celli = cells.size();
cells.resize(celli+nShapes);
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
cellToElemIds, cellToElemIds,
elemIdToCells elemIdToCells
); );
// Extend and fill the new trailing portion
const label startElemi = cells.size();
cells.resize(startElemi+elemCount);
faceListList::subList myElements = cells.slice(startElemi);
const auto& model = cellModel::ref(cellModel::PYR); const auto& model = cellModel::ref(cellModel::PYR);
for (label shapei = 0; shapei < nShapes; shapei++) for (auto& cellFaces : myElements)
{ {
readVerts(is, 5, nodeIdToPoints, verts); readVerts(is, 5, nodeIdToPoints, verts);
if (setHandedness_) if (setHandedness_)
{ {
setHandedness(model, verts, points); setHandedness(model, verts, points);
} }
const cellShape cellVerts(model, verts); cellFaces = cellShape(model, verts).faces();
cells[celli+shapei] = cellVerts.faces();
} }
} }
else if (split[0] == "penta6") else if (keyword == "penta6")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent<< "penta6 " << nShapes Pout<< indent<< "penta6 " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label celli = cells.size();
cells.resize(celli+nShapes);
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
cellToElemIds, cellToElemIds,
elemIdToCells elemIdToCells
); );
// Extend and fill the new trailing portion
const label startElemi = cells.size();
cells.resize(startElemi+elemCount);
faceListList::subList myElements = cells.slice(startElemi);
const auto& model = cellModel::ref(cellModel::PRISM); const auto& model = cellModel::ref(cellModel::PRISM);
for (label shapei = 0; shapei < nShapes; shapei++) for (auto& cellFaces : myElements)
{ {
readVerts(is, 6, nodeIdToPoints, verts); readVerts(is, 6, nodeIdToPoints, verts);
if (setHandedness_) if (setHandedness_)
{ {
setHandedness(model, verts, points); setHandedness(model, verts, points);
} }
const cellShape cellVerts(model, verts); cellFaces = cellShape(model, verts).faces();
cells[celli+shapei] = cellVerts.faces();
} }
} }
else if (split[0] == "hexa8") else if (keyword == "hexa8")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent<< "hexa8 " << nShapes Pout<< indent<< "hexa8 " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label celli = cells.size();
cells.resize(celli+nShapes);
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
cellToElemIds, cellToElemIds,
elemIdToCells elemIdToCells
); );
// Extend and fill the new trailing portion
const label startElemi = cells.size();
cells.resize(startElemi+elemCount);
faceListList::subList myElements = cells.slice(startElemi);
const auto& model = cellModel::ref(cellModel::HEX); const auto& model = cellModel::ref(cellModel::HEX);
for (label shapei = 0; shapei < nShapes; shapei++) for (auto& cellFaces : myElements)
{ {
readVerts(is, 8, nodeIdToPoints, verts); readVerts(is, 8, nodeIdToPoints, verts);
if (setHandedness_) if (setHandedness_)
{ {
setHandedness(model, verts, points); setHandedness(model, verts, points);
} }
const cellShape cellVerts(model, verts); cellFaces = cellShape(model, verts).faces();
cells[celli+shapei] = cellVerts.faces();
} }
} }
else if (split[0] == "nfaced") else if (keyword == "nfaced")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent<< "nfaced " << nShapes Pout<< indent<< "nfaced " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label celli = cells.size();
cells.resize(celli+nShapes);
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
cellToElemIds, cellToElemIds,
elemIdToCells elemIdToCells
); );
for (label shapei = 0; shapei < nShapes; shapei++) // Extend and fill the new trailing portion
const label startElemi = cells.size();
cells.resize(startElemi+elemCount);
faceListList::subList myElements = cells.slice(startElemi);
for (auto& cellFaces : myElements)
{ {
label nFaces; label nFaces;
is.read(nFaces); is.read(nFaces);
faceList& cellFaces = cells[celli+shapei]; cellFaces.resize(nFaces);
cellFaces.setSize(nFaces);
} }
for (label shapei = 0; shapei < nShapes; shapei++) for (auto& cellFaces : myElements)
{ {
faceList& cellFaces = cells[celli+shapei]; for (face& f : cellFaces)
forAll(cellFaces, cellFacei)
{ {
label nVerts; label nVerts;
is.read(nVerts); is.read(nVerts);
cellFaces[cellFacei].setSize(nVerts); f.resize(nVerts);
} }
} }
for (label shapei = 0; shapei < nShapes; shapei++) for (faceList& cellFaces : myElements)
{ {
faceList& cellFaces = cells[celli+shapei]; for (face& f : cellFaces)
forAll(cellFaces, cellFacei)
{ {
face& f = cellFaces[cellFacei];
readVerts(is, f.size(), nodeIdToPoints, verts); readVerts(is, f.size(), nodeIdToPoints, verts);
f.labelList::operator=(verts); f.labelList::operator=(verts);
}
}
forAll(f, fp) // Full check
forAll(myElements, elemi)
{
for (const face& f : myElements[elemi])
{
for (label pointi : f)
{ {
if (f[fp] < 0 || f[fp] >= points.size()) if (pointi < 0 || pointi >= points.size())
{ {
FatalErrorInFunction<< "Face:" << shapei FatalErrorInFunction
<< "Face:" << elemi
<< " verts:" << f << " verts:" << f
<< " indexes outside points:" << points.size() << " indexes outside points:" << points.size()
<< exit(FatalError); << exit(FatalError);
@ -466,107 +486,104 @@ bool Foam::fileFormats::ensightMeshReader::readGoldPart
} }
} }
} }
else if (split[0] == "tria3") else if (keyword == "tria3")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent << "tria3 " << nShapes Pout<< indent << "tria3 " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label facei = faces.size();
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
faceToElemIDs, faceToElemIDs,
elemIdToFaces elemIdToFaces
); );
faces.setSize(facei+nShapes); // Extend and fill the new trailing portion
const label startElemi = cells.size();
faces.resize(startElemi+elemCount, face(3)); // <- tria3
faceList::subList myElements = faces.slice(startElemi);
for (label shapei = 0; shapei < nShapes; shapei++) for (face& f : myElements)
{ {
auto& f = faces[facei+shapei];
f.setSize(3);
readVerts(is, f.size(), nodeIdToPoints, verts); readVerts(is, f.size(), nodeIdToPoints, verts);
f.labelList::operator=(verts); f.labelList::operator=(verts);
} }
} }
else if (split[0] == "quad4") else if (keyword == "quad4")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent << "quad4 " << nShapes Pout<< indent << "quad4 " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label facei = faces.size();
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
faceToElemIDs, faceToElemIDs,
elemIdToFaces elemIdToFaces
); );
faces.setSize(facei+nShapes); // Extend and fill the new trailing portion
const label startElemi = cells.size();
faces.resize(startElemi+elemCount, face(4)); // <- quad4
faceList::subList myElements = faces.slice(startElemi);
for (label shapei = 0; shapei < nShapes; shapei++) for (face& f : myElements)
{ {
auto& f = faces[facei+shapei];
f.setSize(4);
readVerts(is, f.size(), nodeIdToPoints, verts); readVerts(is, f.size(), nodeIdToPoints, verts);
f.labelList::operator=(verts); f.labelList::operator=(verts);
} }
} }
else if (split[0] == "nsided") else if (keyword == "nsided")
{ {
label nShapes; label elemCount;
is.read(nShapes); is.read(elemCount);
Pout<< indent << "nsided " << nShapes Pout<< indent << "nsided " << elemCount
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
const label facei = faces.size();
readIDs readIDs
( (
is, is,
read_elem_ids, read_elem_ids,
nShapes, elemCount,
faceToElemIDs, faceToElemIDs,
elemIdToFaces elemIdToFaces
); );
faces.setSize(facei+nShapes); // Extend and fill the new trailing portion
const label startElemi = cells.size();
faces.resize(startElemi+elemCount);
faceList::subList myElements = faces.slice(startElemi);
for (label shapei = 0; shapei < nShapes; shapei++) for (face& f : myElements)
{ {
auto& f = faces[facei+shapei];
label nVerts; label nVerts;
is.read(nVerts); is.read(nVerts);
f.setSize(nVerts); f.resize(nVerts);
} }
for (label shapei = 0; shapei < nShapes; shapei++) for (face& f : myElements)
{ {
auto& f = faces[facei+shapei];
readVerts(is, f.size(), nodeIdToPoints, verts); readVerts(is, f.size(), nodeIdToPoints, verts);
f.labelList::operator=(verts); f.labelList::operator=(verts);
} }
} }
else else
{ {
WarningInFunction << "Unhandled key " << string(split[0]) WarningInFunction << "Unhandled key " << keyword
<< " from line " << line << " from line " << buffer
<< " starting at line " << is.lineNumber() << " starting at line " << is.lineNumber()
<< " position " << is.stdStream().tellg() << endl; << " position " << is.stdStream().tellg() << endl;
} }
@ -584,16 +601,21 @@ bool Foam::fileFormats::ensightMeshReader::readGeometry
const scalar scaleFactor const scalar scaleFactor
) )
{ {
// Auto-detect ascii/binary format,
// skips any initial "BEGIN TIME STEP"
ensightReadFile is(geometryFile_); ensightReadFile is(geometryFile_);
// Skip 'binary' tag
is.readBinaryHeader();
string header; string buffer;
is.read(header);
Info<< "Ensight : " << header << endl; // Ensight Geometry File
is.read(header); is.read(buffer);
Info<< "Ensight : " << header << endl; Info<< "Ensight : " << buffer << nl;
// Description - 1
is.read(buffer);
Info<< "Ensight : " << buffer << nl;
bool read_node_ids = false; bool read_node_ids = false;
@ -623,61 +645,72 @@ bool Foam::fileFormats::ensightMeshReader::readGeometry
// Parse all // Parse all
string line; SubStrings<string> split;
while (is.good()) while (is.good())
{ {
do do
{ {
is.readKeyword(line); // Get entire line/string
is.read(buffer);
} }
while (line.empty() && is.good()); while (buffer.empty() && is.good());
const auto split = stringOps::splitSpace(line); if (buffer.contains("END TIME STEP"))
{
// END TIME STEP is a valid means to terminate input
break;
}
split = stringOps::splitSpace(buffer);
if (split[0] == "extents") if (split.empty())
{ {
point min; continue;
point max;
is.read(min.x());
is.read(max.x());
is.read(min.y());
is.read(max.y());
is.read(min.z());
is.read(max.z());
Pout<< indent
<< "Read extents " << boundBox(min, max)
<< endl;
} }
else if (split[0] == "node")
const auto keyword(split[0].str());
if (keyword == "extents")
{ {
word id(split[1]); // Optional extents (xmin, xmax, ymin, ymax, zmin, zmax)
word op(split[2]);
boundBox bb;
point& min = bb.min();
point& max = bb.max();
is.read(min.x()); is.read(max.x());
is.read(min.y()); is.read(max.y());
is.read(min.z()); is.read(max.z());
Pout<< indent << "Read extents " << bb << endl;
}
else if (keyword == "node")
{
// "node id (off|assign|given|ignore)"
std::string op(split[2]);
if (op == "given" || op == "ignore") if (op == "given" || op == "ignore")
{ {
Pout<< indent << "Reading node ids" << endl; Pout<< indent << "Reading node ids" << endl;
read_node_ids = true; read_node_ids = true;
} }
} }
else if (split[0] == "element") else if (keyword == "element")
{ {
word id(split[1]); // "element id (off|assign|given|ignore)"
word op(split[2]); std::string op(split[2]);
if (op == "given" || op == "ignore") if (op == "given" || op == "ignore")
{ {
Pout<< indent << "Reading element ids" << endl; Pout<< indent << "Reading element ids" << endl;
read_elem_ids = true; read_elem_ids = true;
} }
} }
else if (split[0] == "part") else if (keyword == "part")
{ {
bool finished = false; bool finished = false;
do do
{ {
// Make space // Read part id and name
partIDs.emplace_back(); is.read(partIDs.emplace_back());
is.read(partIDs.back()); is.read(partNames.emplace_back());
partNames.emplace_back();
is.read(partNames.back());
Pout<< indent Pout<< indent
<< "Reading part " << partIDs.back() << "Reading part " << partIDs.back()
@ -954,7 +987,7 @@ bool Foam::fileFormats::ensightMeshReader::readGeometry
const face& f = rotateFace(cFaces[cFacei], rotatedFace); const face& f = rotateFace(cFaces[cFacei], rotatedFace);
const auto fFnd = vertsToCell.find(f); const auto fFnd = vertsToCell.find(f);
if (fFnd) if (fFnd.good())
{ {
// Already inserted. Internal face. // Already inserted. Internal face.
vertsToCell.erase(fFnd); vertsToCell.erase(fFnd);
@ -1026,7 +1059,12 @@ bool Foam::fileFormats::ensightMeshReader::readGeometry
) )
); );
if (!cAndF) if (cAndF.good())
{
partCellAndFace[patchFacei++] = cAndF.val();
vertsToCell.erase(cAndF);
}
else
{ {
//WarningInFunction //WarningInFunction
// << "Did not find face " << facei // << "Did not find face " << facei
@ -1036,11 +1074,6 @@ bool Foam::fileFormats::ensightMeshReader::readGeometry
// << " in part " << parti // << " in part " << parti
// << endl; // << endl;
} }
else
{
partCellAndFace[patchFacei++] = cAndF();
vertsToCell.erase(cAndF);
}
} }
partCellAndFace.setSize(patchFacei); partCellAndFace.setSize(patchFacei);
} }

View File

@ -554,7 +554,8 @@ int main(int argc, char *argv[])
{ {
autoPtr<ensightGeoFile> os = autoPtr<ensightGeoFile> os =
ensCase.newGeometry(hasMovingMesh); ensCase.newGeometry(hasMovingMesh);
ensMesh.write(os);
ensMesh.write(os.ref());
} }
// finite-area // finite-area
@ -562,7 +563,8 @@ int main(int argc, char *argv[])
{ {
autoPtr<ensightGeoFile> os = autoPtr<ensightGeoFile> os =
ensFaCasePtr->newGeometry(hasMovingMesh); ensFaCasePtr->newGeometry(hasMovingMesh);
ensFaMeshPtr->write(os);
ensFaMeshPtr->write(os.ref());
} }
} }

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2023 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -26,7 +26,6 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "ensightCase.H" #include "ensightCase.H"
#include "ensightGeoFile.H"
#include "Time.H" #include "Time.H"
#include "cloud.H" #include "cloud.H"
#include "IOmanip.H" #include "IOmanip.H"
@ -42,15 +41,26 @@ const char* Foam::ensightCase::geometryName = "geometry";
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
Foam::word Foam::ensightCase::padded(const int nwidth, const label value) Foam::word Foam::ensightCase::mask(const int nwidth)
{ {
if (nwidth < 1) if (nwidth < 1)
{ {
return Foam::name(value); return word();
}
return word(std::string(nwidth, '*'), false); // stripping=false
}
Foam::word Foam::ensightCase::padded(const int nwidth, const label index)
{
if (nwidth < 1)
{
return Foam::name(index);
} }
std::ostringstream oss; std::ostringstream oss;
oss << std::setfill('0') << std::setw(nwidth) << value; oss << std::setfill('0') << std::setw(nwidth) << index;
return word(oss.str(), false); // stripping=false return word(oss.str(), false); // stripping=false
} }
@ -230,7 +240,7 @@ void Foam::ensightCase::initialize()
// eg, convert new results or a particular time interval // eg, convert new results or a particular time interval
// OR remove everything // OR remove everything
if (isDir(ensightDir_)) if (Foam::isDir(ensightDir_))
{ {
if (options_->overwrite()) if (options_->overwrite())
{ {
@ -245,7 +255,7 @@ void Foam::ensightCase::initialize()
} }
// Create ensight and data directories // Create ensight and data directories
mkDir(dataDir()); Foam::mkDir(dataDir());
// The case file is always ASCII // The case file is always ASCII
os_.reset(new OFstream(ensightDir_/caseName_, IOstreamOption::ASCII)); os_.reset(new OFstream(ensightDir_/caseName_, IOstreamOption::ASCII));
@ -508,7 +518,7 @@ Foam::ensightCase::createDataFile
// Note that data/ITER is indeed a valid ensight::FileName // Note that data/ITER is indeed a valid ensight::FileName
const fileName outdir = dataDir()/padded(timeIndex_); const fileName outdir = dataDir()/padded(timeIndex_);
mkDir(outdir); Foam::mkDir(outdir);
return autoPtr<ensightFile>::New(outdir, name, format()); return autoPtr<ensightFile>::New(outdir, name, format());
} }
@ -537,7 +547,7 @@ Foam::ensightCase::createCloudFile
: (dataDir() / padded(timeIndex_) / cloud::prefix / cloudName) : (dataDir() / padded(timeIndex_) / cloud::prefix / cloudName)
); );
mkDir(outdir); // should be unnecessary after newCloud() Foam::mkDir(outdir); // should be unnecessary after newCloud()
return autoPtr<ensightFile>::New(outdir, name, format()); return autoPtr<ensightFile>::New(outdir, name, format());
} }
@ -561,13 +571,7 @@ Foam::ensightCase::ensightCase
caseName_(caseName + ".case"), caseName_(caseName + ".case"),
changed_(false), changed_(false),
timeIndex_(0), timeIndex_(0),
timeValue_(0), timeValue_(0)
timesUsed_(),
geomTimes_(),
cloudTimes_(),
variables_(),
nodeVariables_(),
cloudVars_()
{ {
initialize(); initialize();
} }
@ -586,13 +590,7 @@ Foam::ensightCase::ensightCase
caseName_(caseName + ".case"), caseName_(caseName + ".case"),
changed_(false), changed_(false),
timeIndex_(0), timeIndex_(0),
timeValue_(0), timeValue_(0)
timesUsed_(),
geomTimes_(),
cloudTimes_(),
variables_(),
nodeVariables_(),
cloudVars_()
{ {
initialize(); initialize();
} }
@ -624,7 +622,7 @@ void Foam::ensightCase::setTime(const scalar value, const label index)
// Note that data/ITER is indeed a valid ensight::FileName // Note that data/ITER is indeed a valid ensight::FileName
const fileName outdir = dataDir()/padded(timeIndex_); const fileName outdir = dataDir()/padded(timeIndex_);
mkDir(outdir); Foam::mkDir(outdir);
// place a timestamp in the directory for future reference // place a timestamp in the directory for future reference
OFstream timeStamp(outdir/"time"); OFstream timeStamp(outdir/"time");
@ -842,7 +840,7 @@ Foam::ensightCase::newGeometry
bool moving bool moving
) const ) const
{ {
autoPtr<Foam::ensightGeoFile> output; autoPtr<ensightGeoFile> filePtr;
if (UPstream::master()) if (UPstream::master())
{ {
@ -859,14 +857,16 @@ Foam::ensightCase::newGeometry
// Static mesh: write as "data/constant/geometry" // Static mesh: write as "data/constant/geometry"
path = dataDir()/word("constant"); path = dataDir()/word("constant");
} }
mkDir(path); Foam::mkDir(path);
noteGeometry(moving); // note for later use noteGeometry(moving); // note for later use
return autoPtr<ensightGeoFile>::New(path, geometryName, format()); filePtr.reset(new ensightGeoFile(path, geometryName, format()));
// Before 2024-05 also implicitly called beginGeometry()
} }
return nullptr; return filePtr;
} }
@ -876,23 +876,24 @@ Foam::ensightCase::newCloud
const word& cloudName const word& cloudName
) const ) const
{ {
autoPtr<Foam::ensightFile> output; autoPtr<ensightFile> filePtr;
if (UPstream::master()) if (UPstream::master())
{ {
output = createCloudFile(cloudName, "positions"); filePtr = createCloudFile(cloudName, "positions");
auto& os = filePtr();
// Tag binary format (just like geometry files) // Tag binary format (just like geometry files)
output().writeBinaryHeader(); os.writeBinaryHeader();
// Description // Description
output().write(cloud::prefix/cloudName); os.write(cloud::prefix/cloudName);
output().newline(); os.newline();
noteCloud(cloudName); // note for later use noteCloud(cloudName); // note for later use
} }
return output; return filePtr;
} }

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2023 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -42,13 +42,11 @@ SourceFiles
#define Foam_ensightCase_H #define Foam_ensightCase_H
#include "autoPtr.H" #include "autoPtr.H"
#include "HashSet.H"
#include "InfoProxy.H" #include "InfoProxy.H"
#include "Map.H"
#include "HashSet.H" #include "HashSet.H"
#include "Map.H"
#include "Pstream.H" #include "Pstream.H"
#include "ensightGeoFile.H" #include "ensightGeoFile.H"
#include <memory>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -195,6 +193,10 @@ private:
) const; ) const;
public:
// Generated Methods
//- No copy construct //- No copy construct
ensightCase(const ensightCase&) = delete; ensightCase(const ensightCase&) = delete;
@ -202,8 +204,6 @@ private:
void operator=(const ensightCase&) = delete; void operator=(const ensightCase&) = delete;
public:
// Constructors // Constructors
//- Construct from components //- Construct from components
@ -229,8 +229,19 @@ public:
// Static Functions // Static Functions
//- Stringified zero-padded integer value //- A '*' mask of specified width
static word padded(const int nwidth, const label value); static word mask(const int nwidth);
//- Stringified zero-padded integer value of specified width
static word padded(const int nwidth, const label index);
//- Replace the '*' mask chars with zero-padded integer value
template<class StringType>
static StringType expand_mask
(
const StringType& input,
const label index
);
// Member Functions // Member Functions
@ -285,6 +296,7 @@ public:
// Addition of entries to case file // Addition of entries to case file
//- Open stream for new geometry file (on master). //- Open stream for new geometry file (on master).
//- Does not include beginGeometry() marker.
autoPtr<ensightGeoFile> newGeometry(bool moving = false) const; autoPtr<ensightGeoFile> newGeometry(bool moving = false) const;
//- Open stream for new cloud positions (on master). //- Open stream for new cloud positions (on master).

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -73,7 +73,7 @@ inline bool Foam::ensightCase::separateCloud() const
inline Foam::Ostream& Foam::ensightCase::operator()() const inline Foam::Ostream& Foam::ensightCase::operator()() const
{ {
return *os_; return (os_ ? *os_ : Foam::Snull);
} }

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2023 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -28,6 +28,35 @@ License
#include "cloud.H" #include "cloud.H"
#include "ensightPTraits.H" #include "ensightPTraits.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
template<class StringType>
StringType Foam::ensightCase::expand_mask
(
const StringType& input,
const label timeIndex
)
{
StringType result(input);
const auto nMask = std::count(input.begin(), input.end(), '*');
// If there are any '*' chars, they are assumed to be contiguous
// Eg, data/******/geometry
if (nMask)
{
result.replace
(
ensightCase::mask(nMask),
ensightCase::padded(nMask, timeIndex)
);
}
return result;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type> template<class Type>

View File

@ -27,6 +27,7 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "ensightFile.H" #include "ensightFile.H"
#include "ensightReadFile.H"
#include "error.H" #include "error.H"
#include "List.H" #include "List.H"
#include <cstring> #include <cstring>
@ -41,6 +42,37 @@ float Foam::ensightFile::undefValue_ = Foam::floatScalarVGREAT;
const char* const Foam::ensightFile::coordinates = "coordinates"; const char* const Foam::ensightFile::coordinates = "coordinates";
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Put integers, floats etc in binary or ascii.
template<class Type>
static inline void putPrimitive
(
const Type& value,
OFstream& os,
const int fieldWidth
)
{
auto& oss = os.stdStream();
if (os.format() == IOstreamOption::BINARY)
{
oss.write(reinterpret_cast<const char*>(&value), sizeof(Type));
}
else
{
oss.width(fieldWidth);
oss << value;
}
os.syncState();
}
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
bool Foam::ensightFile::hasUndef(const UList<float>& field) bool Foam::ensightFile::hasUndef(const UList<float>& field)
@ -82,6 +114,51 @@ void Foam::ensightFile::init()
std::ios_base::floatfield std::ios_base::floatfield
); );
precision(5); precision(5);
// Handle transient single-file timestep information
auto& oss = OFstream::stdStream();
if (OFstream::is_appending())
{
// Already positioned at the EOF (in append mode), but be certain
oss.seekp(0, std::ios_base::end);
origFileSize_ = oss.tellp();
}
else
{
origFileSize_ = 0;
}
int64_t begin_footer(-1);
List<int64_t> offsets;
if (OFstream::is_appending())
{
// Temporarily open for reading as well.
// No race condition since no writing is done concurrently with the
// reading
IFstream is(OFstream::name(), OFstream::format());
begin_footer =
ensightReadFile::getTimeStepFooter
(
is,
offsets
);
}
timeStepOffsets_ = std::move(offsets);
if (OFstream::is_appending() && begin_footer > 0)
{
oss.seekp(begin_footer);
OFstream::syncState();
}
// InfoErr << "output at: " << label(begin_footer) << nl;
// InfoErr
// << "footer: " << label(begin_footer)
// << " time-steps: " << offsets.size() << nl;
} }
@ -89,11 +166,30 @@ void Foam::ensightFile::init()
Foam::ensightFile::ensightFile Foam::ensightFile::ensightFile
( (
std::nullptr_t, // dispatch tag
IOstreamOption::appendType append,
const fileName& pathname, const fileName& pathname,
IOstreamOption::streamFormat fmt IOstreamOption::streamFormat fmt
) )
: :
OFstream(IOstreamOption::ATOMIC, ensight::FileName(pathname), fmt) OFstream
(
(
// Only use atomic when not appending
(append == IOstreamOption::NO_APPEND)
? IOstreamOption::ATOMIC
: IOstreamOption::NON_ATOMIC
),
pathname,
fmt,
(
// Change APPEND_APP -> APPEND_ATE (file rewriting)
(append == IOstreamOption::APPEND_APP)
? IOstreamOption::APPEND_ATE
: append
)
),
origFileSize_(0)
{ {
init(); init();
} }
@ -101,14 +197,44 @@ Foam::ensightFile::ensightFile
Foam::ensightFile::ensightFile Foam::ensightFile::ensightFile
( (
IOstreamOption::appendType append,
const fileName& pathname,
IOstreamOption::streamFormat fmt
)
:
ensightFile
(
nullptr,
append,
ensight::FileName(pathname),
fmt
)
{}
Foam::ensightFile::ensightFile
(
IOstreamOption::appendType append,
const fileName& path, const fileName& path,
const fileName& name, const fileName& name,
IOstreamOption::streamFormat fmt IOstreamOption::streamFormat fmt
) )
: :
OFstream(IOstreamOption::ATOMIC, path/ensight::FileName(name), fmt) ensightFile
(
nullptr,
append,
path/ensight::FileName(name),
fmt
)
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::ensightFile::~ensightFile()
{ {
init(); (void) writeTimeStepFooter();
} }
@ -158,9 +284,11 @@ void Foam::ensightFile::writeString(const char* str, size_t len)
std::copy_n(str, len, buf); std::copy_n(str, len, buf);
std::fill_n(buf + len, (80 - len), '\0'); // Pad trailing with nul std::fill_n(buf + len, (80 - len), '\0'); // Pad trailing with nul
auto& oss = stdStream();
if (format() == IOstreamOption::BINARY) if (format() == IOstreamOption::BINARY)
{ {
write(buf, 80); oss.write(buf, 80);
} }
else else
{ {
@ -170,9 +298,10 @@ void Foam::ensightFile::writeString(const char* str, size_t len)
// char* p = ::strchr(buf, '\n'); // char* p = ::strchr(buf, '\n');
// if (p) *p = 0; // if (p) *p = 0;
stdStream() << buf; oss << buf;
syncState();
} }
syncState();
} }
@ -209,6 +338,7 @@ Foam::Ostream& Foam::ensightFile::write(const std::string& str)
} }
// Same as OFstream::writeRaw(buf, count)
Foam::Ostream& Foam::ensightFile::write Foam::Ostream& Foam::ensightFile::write
( (
const char* buf, const char* buf,
@ -223,82 +353,56 @@ Foam::Ostream& Foam::ensightFile::write
void Foam::ensightFile::writeInt(const int32_t val, const int fieldWidth) void Foam::ensightFile::writeInt(const int32_t val, const int fieldWidth)
{ {
if (format() == IOstreamOption::BINARY) putPrimitive<int32_t>(val, *this, fieldWidth);
{
write
(
reinterpret_cast<const char *>(&val),
sizeof(int32_t)
);
}
else
{
stdStream().width(fieldWidth);
stdStream() << val;
syncState();
}
} }
void Foam::ensightFile::writeInt(const int64_t val, const int fieldWidth) void Foam::ensightFile::writeInt(const int64_t val, const int fieldWidth)
{ {
int32_t work(narrowInt32(val)); putPrimitive<int32_t>(narrowInt32(val), *this, fieldWidth);
writeInt(work, fieldWidth);
} }
void Foam::ensightFile::writeFloat(const float val, const int fieldWidth) void Foam::ensightFile::writeFloat(const float val, const int fieldWidth)
{ {
if (format() == IOstreamOption::BINARY) putPrimitive<float>(val, *this, fieldWidth);
{
write
(
reinterpret_cast<const char *>(&val),
sizeof(float)
);
}
else
{
stdStream().width(fieldWidth);
stdStream() << val;
syncState();
}
} }
void Foam::ensightFile::writeFloat(const double val, const int fieldWidth) void Foam::ensightFile::writeFloat(const double val, const int fieldWidth)
{ {
float work(narrowFloat(val)); putPrimitive<float>(narrowFloat(val), *this, fieldWidth);
writeFloat(work, fieldWidth);
} }
Foam::Ostream& Foam::ensightFile::write(const int32_t val) Foam::Ostream& Foam::ensightFile::write(const int32_t val)
{ {
writeInt(val, 10); putPrimitive<int32_t>(val, *this, 10);
return *this; return *this;
} }
Foam::Ostream& Foam::ensightFile::write(const int64_t val) Foam::Ostream& Foam::ensightFile::write(const int64_t val)
{ {
writeInt(val, 10); putPrimitive<int32_t>(narrowInt32(val), *this, 10);
return *this; return *this;
} }
Foam::Ostream& Foam::ensightFile::write(const float val) Foam::Ostream& Foam::ensightFile::write(const float val)
{ {
writeFloat(val, 12); putPrimitive<float>(val, *this, 12);
return *this; return *this;
} }
Foam::Ostream& Foam::ensightFile::write(const double val) Foam::Ostream& Foam::ensightFile::write(const double val)
{ {
writeFloat(val, 12); putPrimitive<float>(narrowFloat(val), *this, 12);
return *this; return *this;
} }
@ -307,8 +411,7 @@ void Foam::ensightFile::newline()
{ {
if (format() == IOstreamOption::ASCII) if (format() == IOstreamOption::ASCII)
{ {
stdStream() << nl; OFstream::write('\n');
syncState();
} }
} }
@ -343,22 +446,69 @@ void Foam::ensightFile::writeBinaryHeader()
if (format() == IOstreamOption::BINARY) if (format() == IOstreamOption::BINARY)
{ {
writeString("C Binary"); writeString("C Binary");
// Is binary: newline() is a no-op // newline(); // A no-op in binary
} }
} }
void Foam::ensightFile::beginTimeStep() // Footer information looks like this
{ //
writeString("BEGIN TIME STEP"); /* |---------------|---------------|-----------------------|
newline(); * | ASCII | BINARY | element |
} * |---------------|---------------|-----------------------|
* | "%20lld\n" | int32 | nSteps |
* | "%20lld\n" | int64 | offset step 1 |
* | "%20lld\n" | int64 | offset step 2 |
* | "%20lld\n" | .. | |
* | "%20lld\n" | int64 | offset step n |
* | "%20lld\n" | int32 | flag (unused) |
* | "%20lld\n" | int64 | offset to nSteps |
* | "%s\n" | char[80] | 'FILE_INDEX' |
* |---------------|---------------|-----------------------|
*/
int64_t Foam::ensightFile::writeTimeStepFooter()
void Foam::ensightFile::endTimeStep()
{ {
writeString("END TIME STEP"); if (timeStepOffsets_.empty())
{
return -1;
}
auto& oss = OFstream::stdStream();
// The footer begin, which is also the current position
const int64_t footer_begin(oss.tellp());
// nSteps
putPrimitive<int32_t>(int32_t(timeStepOffsets_.size()), *this, 20);
newline(); newline();
// offset step 1, 2, ... N
for (int64_t off : timeStepOffsets_)
{
putPrimitive<int64_t>(off, *this, 20);
newline();
}
// flag (unused)
putPrimitive<int32_t>(0, *this, 20);
newline();
// The footer begin == position of nSteps
putPrimitive<int64_t>(footer_begin, *this, 20);
newline();
// FILE_INDEX is "%s\n", not "%79s\n"
// but our ASCII strings are truncated (nul-padded) anyhow
writeString("FILE_INDEX");
newline();
// Reposition to begin of footer so that any subsequent output
// will overwrite the footer too
oss.seekp(footer_begin);
return footer_begin;
} }
@ -366,6 +516,55 @@ void Foam::ensightFile::endTimeStep()
// Convenience Output Methods // Convenience Output Methods
// //
int64_t Foam::ensightFile::beginTimeStep()
{
writeString("BEGIN TIME STEP");
newline();
auto& oss = OFstream::stdStream();
const int64_t curr_pos(oss.tellp());
timeStepOffsets_.push_back(curr_pos);
// To avoid partly incomplete/incorrect footer information,
// overwrite original footer if needed.
if (curr_pos >= 0 && curr_pos < origFileSize_)
{
const char fill[] = "deadbeef";
for
(
int64_t pos = curr_pos;
pos < origFileSize_ && bool(oss);
pos += 8
)
{
// Overwrite with specified "junk" to avoid/detect corrupt
// files etc. Don't worry about slightly increasing the
// file size (ie, max 7 bytes) - it's unimportant
oss.write(fill, 8);
}
// Maintain the original output position
oss.seekp(curr_pos);
OFstream::syncState();
}
return curr_pos;
}
int64_t Foam::ensightFile::endTimeStep()
{
writeString("END TIME STEP");
newline();
return int64_t(stdStream().tellp());
}
void Foam::ensightFile::beginPart(const label index) void Foam::ensightFile::beginPart(const label index)
{ {
writeString("part"); writeString("part");
@ -375,6 +574,28 @@ void Foam::ensightFile::beginPart(const label index)
} }
void Foam::ensightFile::beginPart
(
const label index,
const std::string& description
)
{
beginPart(index);
writeString(description);
newline();
}
void Foam::ensightFile::beginCoordinates(const label npoints)
{
writeString("coordinates");
newline();
write(npoints);
newline();
}
void Foam::ensightFile::beginParticleCoordinates(const label nparticles) void Foam::ensightFile::beginParticleCoordinates(const label nparticles)
{ {
writeString("particle coordinates"); writeString("particle coordinates");

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -28,8 +28,8 @@ Class
Foam::ensightFile Foam::ensightFile
Description Description
Ensight output with specialized write() for strings, integers and floats. A variant of OFstream with specialised handling for Ensight writing
Correctly handles binary write as well. of strings, integers and floats (ASCII and BINARY).
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -39,6 +39,7 @@ Description
#include "OFstream.H" #include "OFstream.H"
#include "ensightFileName.H" #include "ensightFileName.H"
#include "ensightVarName.H" #include "ensightVarName.H"
#include "DynamicList.H"
#include "IndirectListBase.H" #include "IndirectListBase.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -62,17 +63,36 @@ class ensightFile
//- Value to represent undef in results (default: 1e+37, floatVGREAT) //- Value to represent undef in results (default: 1e+37, floatVGREAT)
static float undefValue_; static float undefValue_;
//- Transient single-file:
//- the original file size when opened in append mode, zero otherwise
int64_t origFileSize_;
//- Transient single-file:
//- the time-step file-offsets (position after "BEGIN TIME STEP").
// Set on initial reading and added to by beginTimeStep().
DynamicList<int64_t> timeStepOffsets_;
// Private Member Functions // Private Member Functions
//- Initialize sets the ASCII output formatting //- Set the ASCII output formatting etc,
//- and handle transient single-file timestep information
void init(); void init();
//- No copy construct
ensightFile(const ensightFile&) = delete;
//- No copy assignment // Constructors
void operator=(const ensightFile&) = delete;
//- Construct with file name, no ensight file naming adjustment.
// Created as an atomic or in append-mode (single-file format).
// In append-mode, attempts to parse existing time-step information.
// Changes APPEND_APP to APPEND_ATE (file rewriting).
ensightFile
(
std::nullptr_t, // dispatch tag
IOstreamOption::appendType append, // (NO_APPEND or APPEND_ATE)
const fileName& pathname,
IOstreamOption::streamFormat fmt
);
public: public:
@ -98,30 +118,70 @@ public:
} }
// Generated Methods
//- No copy construct
ensightFile(const ensightFile&) = delete;
//- No copy assignment
void operator=(const ensightFile&) = delete;
// Constructors // Constructors
//- Construct from path-name. //- Construct from path-name.
// The path-name is adjusted for valid ensight file naming. // The path-name is adjusted for valid ensight file naming.
// Always created as an atomic // Created as an atomic or in append mode (single-file format).
explicit ensightFile // In append mode, attempts to parse existing time-step information.
ensightFile
( (
//! Append mode: NO_APPEND or APPEND_ATE
IOstreamOption::appendType append,
const fileName& pathname, const fileName& pathname,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
); );
//- Construct from path and name. //- Construct from path and name.
// Only the name portion is adjusted for valid ensight file naming. // Only the name portion is adjusted for valid ensight file naming.
// Always created as an atomic // Created as an atomic or in append mode (single-file format).
// In append mode, attempts to parse existing time-step information.
ensightFile ensightFile
( (
//! Append mode: NO_APPEND or APPEND_ATE
IOstreamOption::appendType append,
const fileName& path, const fileName& path,
const fileName& name, const fileName& name,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
); );
//- Construct from path-name.
// The path-name is adjusted for valid ensight file naming.
// Created as an atomic, non-append mode.
explicit ensightFile
(
const fileName& pathname,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
)
:
ensightFile(IOstreamOption::NO_APPEND, pathname, fmt)
{}
//- Destructor //- Construct from path and name.
~ensightFile() = default; // Only the name portion is adjusted for valid ensight file naming.
// Created as an atomic, non-append mode.
ensightFile
(
const fileName& path,
const fileName& name,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
)
:
ensightFile(IOstreamOption::NO_APPEND, path, name, fmt)
{}
//- Destructor. Commits the time-step footer information (if any)
~ensightFile();
// Member Functions // Member Functions
@ -148,12 +208,6 @@ public:
//- Write "C Binary" string for binary files (eg, geometry/measured) //- Write "C Binary" string for binary files (eg, geometry/measured)
void writeBinaryHeader(); void writeBinaryHeader();
//- Write "BEGIN TIME STEP" string and newline
void beginTimeStep();
//- Write "END TIME STEP" string and newline
void endTimeStep();
//- Write character/string content as "%79s" or as binary (max 80 chars) //- Write character/string content as "%79s" or as binary (max 80 chars)
void writeString(const char* str, size_t len); void writeString(const char* str, size_t len);
@ -225,11 +279,59 @@ public:
void newline(); void newline();
// Transient single-file format
//- Write "BEGIN TIME STEP" string and newline
//- (for transient single-file format).
// \returns file position after the write
int64_t beginTimeStep();
//- Write "END TIME STEP" string and newline
//- (for transient single-file format)
// \returns file position after the write
int64_t endTimeStep();
//- Transient single-file:
//- write the time-step file-offsets as footer information.
// Maintains the current file position to allow manual use
// and seamless overwriting.
// \return the output file position at the start of the footer
int64_t writeTimeStepFooter();
//- Transient single-file:
//- forget time-step file positions (advanced use)
void clearTimeSteps() noexcept
{
timeStepOffsets_.clear();
}
//- Transient single-file:
//- the current number of time steps
label nTimes() const noexcept
{
return timeStepOffsets_.size();
}
//- Transient single-file:
//- the current file-offsets for time steps within the file
const UList<int64_t>& timeStepOffets() const noexcept
{
return timeStepOffsets_;
}
// Convenience Output Methods // Convenience Output Methods
//- Begin a part (0-based index internally). //- Begin a part (0-based index internally).
void beginPart(const label index); void beginPart(const label index);
//- Begin a part (0-based index internally), with a description.
//- Only used for geometry files
void beginPart(const label index, const std::string& description);
//- Begin a "coordinates" block. Only used for geometry files.
void beginCoordinates(const label nparticles);
//- Begin a "particle coordinates" block (measured data) //- Begin a "particle coordinates" block (measured data)
void beginParticleCoordinates(const label nparticles); void beginParticleCoordinates(const label nparticles);

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011 OpenFOAM Foundation Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -29,15 +29,56 @@ License
#include "ensightGeoFile.H" #include "ensightGeoFile.H"
#include "foamVersion.H" #include "foamVersion.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
void Foam::ensightGeoFile::init() Foam::ensightGeoFile::ensightGeoFile
(
IOstreamOption::appendType append,
const fileName& pathname,
IOstreamOption::streamFormat fmt
)
:
ensightFile(append, pathname, fmt)
{ {
writeBinaryHeader(); if (!OFstream::is_appending())
beginGeometry(); {
writeBinaryHeader(); // Mandatory for geometry files
}
} }
Foam::ensightGeoFile::ensightGeoFile
(
IOstreamOption::appendType append,
const fileName& path,
const fileName& name,
IOstreamOption::streamFormat fmt
)
:
ensightFile(append, path, name, fmt)
{
if (!OFstream::is_appending())
{
writeBinaryHeader(); // Mandatory for geometry files
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::Ostream& Foam::ensightGeoFile::writeKeyword(const keyType& key)
{
writeString(key);
newline();
return *this;
}
//
// Convenience Output Methods
//
void Foam::ensightGeoFile::beginGeometry() void Foam::ensightGeoFile::beginGeometry()
{ {
// Description line 1 // Description line 1
@ -56,72 +97,4 @@ void Foam::ensightGeoFile::beginGeometry()
} }
void Foam::ensightGeoFile::endGeometry()
{}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::ensightGeoFile::ensightGeoFile
(
const fileName& pathname,
IOstreamOption::streamFormat fmt
)
:
ensightFile(pathname, fmt)
{
init();
}
Foam::ensightGeoFile::ensightGeoFile
(
const fileName& path,
const fileName& name,
IOstreamOption::streamFormat fmt
)
:
ensightFile(path, name, fmt)
{
init();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::Ostream& Foam::ensightGeoFile::writeKeyword(const keyType& key)
{
writeString(key);
newline();
return *this;
}
//
// Convenience Output Methods
//
void Foam::ensightGeoFile::beginPart
(
const label index,
const std::string& description
)
{
beginPart(index);
writeString(description);
newline();
}
void Foam::ensightGeoFile::beginCoordinates(const label npoints)
{
writeString(ensightFile::coordinates);
newline();
write(npoints);
newline();
}
// ************************************************************************* // // ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -28,7 +28,8 @@ Class
Foam::ensightGeoFile Foam::ensightGeoFile
Description Description
Specialized Ensight output with extra geometry file header A variant of ensightFile (Ensight writing) that includes
the extra geometry file header information.
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -50,23 +51,6 @@ class ensightGeoFile
: :
public ensightFile public ensightFile
{ {
// Private Member Functions
//- Initialize outputs the header information and beginGeometry
void init();
//- Start of geometry information
void beginGeometry();
//- End of geometry information
void endGeometry();
//- No copy construct
ensightGeoFile(const ensightGeoFile&) = delete;
//- No copy assignment
void operator=(const ensightGeoFile&) = delete;
public: public:
// Static Functions // Static Functions
@ -78,24 +62,66 @@ public:
} }
// Generated Methods
//- No copy construct
ensightGeoFile(const ensightGeoFile&) = delete;
//- No copy assignment
void operator=(const ensightGeoFile&) = delete;
// Constructors // Constructors
//- Construct from path-name. //- Construct from path-name and initialise the binary header.
// The path-name is adjusted for valid ensight file naming. // The path-name is adjusted for valid ensight file naming.
explicit ensightGeoFile // Created as an atomic or in append mode (single-file format).
ensightGeoFile
( (
//! Append mode: NO_APPEND or APPEND_ATE
IOstreamOption::appendType append,
const fileName& pathname, const fileName& pathname,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
); );
//- Construct from path and name. //- Construct from path / name and initialise the binary header.
// Only the name portion is adjusted for valid ensight file naming. // Only the name portion is adjusted for valid ensight file naming.
// Created as an atomic or in append mode (single-file format).
ensightGeoFile
(
//! Append mode: NO_APPEND or APPEND_ATE
IOstreamOption::appendType append,
const fileName& path,
const fileName& name,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
);
//- Construct from path-name and initialise the binary header.
// The path-name is adjusted for valid ensight file naming.
// Created as an atomic, non-append mode.
// \note Since 2024-05 does \em not call beginGeometry() !!
explicit ensightGeoFile
(
const fileName& pathname,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
)
:
ensightGeoFile(IOstreamOption::NO_APPEND, pathname, fmt)
{}
//- Construct from path / name and initialise the binary header.
// Only the name portion is adjusted for valid ensight file naming.
// Created as an atomic, non-append mode.
// \note Since 2024-05 does \em not call beginGeometry() !!
ensightGeoFile ensightGeoFile
( (
const fileName& path, const fileName& path,
const fileName& name, const fileName& name,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
); )
:
ensightGeoFile(IOstreamOption::NO_APPEND, path, name, fmt)
{}
//- Destructor //- Destructor
@ -107,19 +133,13 @@ public:
// Output // Output
//- Write keyword with trailing newline //- Write keyword with trailing newline
virtual Ostream& writeKeyword(const keyType& key); virtual Ostream& writeKeyword(const keyType& key) override;
// Convenience Output Methods // Convenience Output Methods
//- Begin a part (0-based index). //- Start of geometry information
using ensightFile::beginPart; void beginGeometry();
//- Begin a "part" (0-based index), with a description.
void beginPart(const label index, const std::string& description);
//- Begin a "coordinates" block
void beginCoordinates(const label npoints);
}; };

View File

@ -433,6 +433,11 @@ void Foam::ensightMesh::write
bool parallel bool parallel
) const ) const
{ {
if (UPstream::master())
{
os.beginGeometry();
}
// The internalMesh, cellZones // The internalMesh, cellZones
for (const label id : cellZoneParts_.sortedToc()) for (const label id : cellZoneParts_.sortedToc())
{ {

View File

@ -217,6 +217,7 @@ public:
// Output // Output
//- Write geometry to file (normally in parallel). //- Write geometry to file (normally in parallel).
//- Adds beginGeometry() marker.
// If all geometry is disabled, it will simply writes the mesh // If all geometry is disabled, it will simply writes the mesh
// bounding box (to ensure that the geometry file is non-empty) // bounding box (to ensure that the geometry file is non-empty)
void write void write
@ -226,8 +227,8 @@ public:
) const; ) const;
//- Write geometry to file (normally in parallel). //- Write geometry to file (normally in parallel).
// If all geometry is disabled, it will simply writes the mesh //- Adds beginGeometry() marker.
// bounding box (to ensure that the geometry file is non-empty) FOAM_DEPRECATED_FOR(2024-05, "write(ensightGeoFile&, ...")
inline void write inline void write
( (
autoPtr<ensightGeoFile>& os, autoPtr<ensightGeoFile>& os,

View File

@ -41,8 +41,8 @@ Note
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef ensightFileName_H #ifndef Foam_ensightFileName_H
#define ensightFileName_H #define Foam_ensightFileName_H
#include "fileName.H" #include "fileName.H"
#include "word.H" #include "word.H"
@ -81,6 +81,9 @@ public:
//- Copy construct from std::string //- Copy construct from std::string
inline explicit FileName(const std::string& s); inline explicit FileName(const std::string& s);
//- Move construct from std::string
inline explicit FileName(std::string&& s);
// Member Functions // Member Functions

View File

@ -46,6 +46,14 @@ inline Foam::ensight::FileName::FileName(const std::string& s)
} }
inline Foam::ensight::FileName::FileName(std::string&& s)
:
fileName(std::move(s), false)
{
stripInvalid();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline bool Foam::ensight::FileName::valid(char c) inline bool Foam::ensight::FileName::valid(char c)
@ -65,11 +73,11 @@ inline void Foam::ensight::FileName::stripInvalid()
// Avoid characters that upset Windows or others // Avoid characters that upset Windows or others
string::replaceAny(":<>[]", '_'); string::replaceAny(":<>[]", '_');
removeRepeated('_'); string::removeRepeated('_');
// Minor cleanup of fileName // Minor cleanup of fileName
removeRepeated('/'); string::removeRepeated('/');
removeEnd('/'); string::removeEnd('/');
if (empty()) if (empty())
{ {

View File

@ -41,8 +41,8 @@ Description
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef ensightVarName_H #ifndef Foam_ensightVarName_H
#define ensightVarName_H #define Foam_ensightVarName_H
#include "word.H" #include "word.H"
@ -80,6 +80,9 @@ public:
//- Copy construct from std::string //- Copy construct from std::string
inline explicit VarName(const std::string& s); inline explicit VarName(const std::string& s);
//- Move construct from std::string
inline explicit VarName(std::string&& s);
// Member Functions // Member Functions

View File

@ -46,6 +46,14 @@ inline Foam::ensight::VarName::VarName(const std::string& s)
} }
inline Foam::ensight::VarName::VarName(std::string&& s)
:
word(std::move(s), false)
{
stripInvalid();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline bool Foam::ensight::VarName::valid(char c) inline bool Foam::ensight::VarName::valid(char c)
@ -73,7 +81,7 @@ inline bool Foam::ensight::VarName::valid(char c)
inline void Foam::ensight::VarName::stripInvalid() inline void Foam::ensight::VarName::stripInvalid()
{ {
string::stripInvalid<VarName>(*this); string::stripInvalid<ensight::VarName>(*this);
if (empty()) if (empty())
{ {
@ -83,10 +91,9 @@ inline void Foam::ensight::VarName::stripInvalid()
} }
// Prefix with '_' to avoid starting with leading digits // Prefix with '_' to avoid starting with leading digits
std::string::iterator iter = begin(); if (std::isdigit(front()))
if (isdigit(*iter))
{ {
insert(iter, '_'); insert(0, 1, '_');
} }
} }

View File

@ -274,6 +274,7 @@ public:
virtual void writeDict(Ostream& os, const bool full=false) const; virtual void writeDict(Ostream& os, const bool full=false) const;
//- Write geometry, using a mesh reference (serial only) //- Write geometry, using a mesh reference (serial only)
//- No beginGeometry() marker.
virtual void write virtual void write
( (
ensightGeoFile& os, ensightGeoFile& os,
@ -283,6 +284,7 @@ public:
//- Write bounding box geometry. //- Write bounding box geometry.
//- All parameters are only relevant on master //- All parameters are only relevant on master
//- No beginGeometry() marker.
static void writeBox static void writeBox
( (
ensightGeoFile& os, ensightGeoFile& os,

View File

@ -54,7 +54,7 @@ namespace
{ {
// Trivial shape classifier // Trivial shape classifier
inline Foam::ensightFaces::elemType whatType(const Foam::face& f) inline Foam::ensightFaces::elemType whatType(const Foam::face& f) noexcept
{ {
return return
( (

View File

@ -266,6 +266,7 @@ public:
virtual void writeDict(Ostream& os, const bool full=false) const; virtual void writeDict(Ostream& os, const bool full=false) const;
//- Write geometry, using a mesh reference //- Write geometry, using a mesh reference
//- No beginGeometry() marker.
virtual void write virtual void write
( (
ensightGeoFile& os, ensightGeoFile& os,

View File

@ -61,9 +61,7 @@ void Foam::ensightPart::decrAddressing(const label off)
Foam::ensightPart::ensightPart() Foam::ensightPart::ensightPart()
: :
index_(0), index_(0),
identifier_(-1), identifier_(-1)
name_(),
address_()
{} {}

View File

@ -35,8 +35,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef ensightPart_H #ifndef Foam_ensightPart_H
#define ensightPart_H #define Foam_ensightPart_H
#include "ensightGeoFile.H" #include "ensightGeoFile.H"
#include "labelList.H" #include "labelList.H"

View File

@ -42,8 +42,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef ensightOutputSurface_H #ifndef Foam_ensightOutputSurface_H
#define ensightOutputSurface_H #define Foam_ensightOutputSurface_H
#include "ensightFaces.H" #include "ensightFaces.H"
@ -69,7 +69,9 @@ class ensightOutputSurface
const faceList& faces_; const faceList& faces_;
// Private Member Functions public:
// Generated Methods
//- No copy construct //- No copy construct
ensightOutputSurface(const ensightOutputSurface&) = delete; ensightOutputSurface(const ensightOutputSurface&) = delete;
@ -78,8 +80,6 @@ class ensightOutputSurface
void operator=(const ensightOutputSurface&) = delete; void operator=(const ensightOutputSurface&) = delete;
public:
// Constructors // Constructors
//- Construct from points and faces. //- Construct from points and faces.
@ -98,7 +98,8 @@ public:
// Member Functions // Member Functions
//- Write processor-local geometry (serial-only) //- Write processor-local geometry (serial-only).
//- No beginGeometry() marker.
void write(ensightGeoFile& os) const; void write(ensightGeoFile& os) const;
//- Write a field of face or point values (serial-only) //- Write a field of face or point values (serial-only)
@ -121,7 +122,8 @@ public:
// Housekeeping // Housekeeping
//- Cannot write geometry with a mesh reference //- Cannot write geometry with a mesh reference.
//- No beginGeometry() marker.
virtual void write(ensightGeoFile&, const polyMesh&, bool) const virtual void write(ensightGeoFile&, const polyMesh&, bool) const
{} {}
}; };

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -26,50 +26,300 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "ensightReadFile.H" #include "ensightReadFile.H"
#include "stringOps.H"
#include "defineDebugSwitch.H"
#include "registerSwitch.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
Foam::IOstreamOption::streamFormat defineDebugSwitchWithName(Foam::ensightReadFile, "ensightReadFile", 0);
Foam::ensightReadFile::detectBinaryHeader(const fileName& pathname)
registerDebugSwitchWithName(Foam::ensightReadFile, ensight, "ensightReadFile");
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{ {
IOstreamOption::streamFormat fmt(IOstreamOption::BINARY);
// Detect BINARY vs ASCII by testing for initial "(C|Fortran) Binary" // Get integers, floats etc in binary or ascii.
template<class Type>
static inline Type getPrimitive(IFstream& is)
{
Type value(0);
auto& iss = is.stdStream();
if (is.format() == IOstreamOption::BINARY)
{ {
IFstream ifs(pathname, IOstreamOption::BINARY); iss.read(reinterpret_cast<char*>(&value), sizeof(Type));
}
else
{
iss >> value;
}
is.syncState();
if (!ifs.good()) return value;
{ }
FatalErrorInFunction
<< "Cannot read file " << ifs.name() << nl
<< exit(FatalError);
}
istream& iss = ifs.stdStream();
// Get an Ensight string value (binary or ascii).
static inline void readEnsightString(IFstream& is, std::string& value)
{
if (is.format() == IOstreamOption::BINARY)
{
auto& iss = is.stdStream();
// Binary string is *exactly* 80 characters // Binary string is *exactly* 80 characters
string buf(size_t(80), '\0'); value.resize(80, '\0');
iss.read(&buf[0], 80); iss.read(&value[0], 80);
const std::streamsize gcount = iss.gcount(); const std::streamsize gcount = iss.gcount();
buf.erase(gcount <= 0 ? 0 : gcount); // Truncated? value.erase(gcount <= 0 ? 0 : gcount); // Truncated?
// Could exit on truncated input, but no real advantage // Could exit on truncated input, but no real advantage
// Truncate at the first embedded '\0' // Truncate at the first embedded '\0'
const auto endp = buf.find('\0'); const auto endp = value.find('\0');
if (endp != std::string::npos) if (endp != std::string::npos)
{ {
buf.erase(endp); value.erase(endp);
} }
// ASCII if it does not contain "C Binary" // May have been padded with trailing spaces - remove those
if (!buf.contains("Binary") && !buf.contains("binary")) stringOps::inplaceTrimRight(value);
is.syncState();
}
else
{
value.clear();
while (value.empty() && !is.eof())
{ {
fmt = IOstreamOption::ASCII; is.getLine(value);
} }
} }
}
return fmt;
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
// Footer information looks like this
//
/* |---------------|---------------|-----------------------|
* | ASCII | BINARY | element |
* |---------------|---------------|-----------------------|
* | "%20lld\n" | int32 | nSteps |
* | "%20lld\n" | int64 | offset step 1 |
* | "%20lld\n" | int64 | offset step 2 |
* | "%20lld\n" | .. | |
* | "%20lld\n" | int64 | offset step n |
* | "%20lld\n" | int32 | flag (unused) |
* | "%20lld\n" | int64 | offset to nSteps |
* | "%s\n" | char[80] | 'FILE_INDEX' |
* |---------------|---------------|-----------------------|
*/
int64_t Foam::ensightReadFile::getTimeStepFooter
(
IFstream& is,
// File offsets for each time step (if any)
List<int64_t>& offsets
)
{
std::string buffer;
auto& iss = is.stdStream();
const auto lineNum = is.lineNumber();
const auto curr_pos = iss.tellg();
if (curr_pos < 0)
{
// Impossible positioning - exit
is.lineNumber(lineNum); // Restore line number
offsets.clear();
return -1;
}
iss.seekg(0, std::ios_base::end);
const auto end_pos = iss.tellg();
// As a minimum, expect at least 1 time step, so have four integers
// (nSteps, offset step 1, flag, file offset) and the string (10 chars).
// Thus always at least 80+ chars.
if (end_pos <= 80)
{
// Looks quite impossible - exit
is.lineNumber(lineNum); // Restore line number
iss.seekg(curr_pos); // Restore file position
offsets.clear();
return -1;
}
// Get the last 80 chars as a character string
iss.seekg(-80, std::ios_base::end);
const auto fmt = is.format(IOstreamOption::BINARY);
readEnsightString(is, buffer);
is.format(fmt);
int64_t footer_begin(0);
const auto endp = buffer.find("FILE_INDEX");
if (endp == std::string::npos)
{
// Not found
is.lineNumber(lineNum); // Restore line number
iss.seekg(curr_pos); // Restore file position
offsets.clear();
return -1;
}
else if (fmt == IOstreamOption::ASCII)
{
// In ASCII, the last 80 chars will also include a few integers
buffer.erase(endp); // Remove FILE_INDEX ...
auto split = stringOps::splitSpace(buffer);
if (!split.empty())
{
footer_begin = Foam::readInt64(split.back().str());
}
}
else
{
// Position before string (80 bytes) and int64 value (8 bytes)
iss.seekg(-88, std::ios_base::end);
footer_begin = getPrimitive<int64_t>(is);
}
// The number of steps is stored as int32 at the beginning of the footer
int32_t nSteps(0);
if (footer_begin)
{
iss.seekg(footer_begin);
nSteps = getPrimitive<int32_t>(is);
}
offsets.resize_nocopy(nSteps);
// Next footer entries are the offsets per time-step
for (int32_t step = 0; step < nSteps; ++step)
{
offsets[step] = getPrimitive<int64_t>(is);
}
is.lineNumber(lineNum); // Restore line number
iss.seekg(curr_pos); // Restore file position
return footer_begin;
}
} // End namespace Foam
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::ensightReadFile::readString(std::string& value)
{
readEnsightString(*this, value);
}
void Foam::ensightReadFile::init(bool detectFormat)
{
if (!IFstream::good())
{
FatalErrorInFunction
<< "Cannot read file " << IFstream::name() << nl
<< exit(FatalError);
}
auto& iss = stdStream();
auto lineNum = lineNumber();
auto curr_pos = iss.tellg(); // The starting position (should be 0)
string buffer;
if (detectFormat)
{
// Read initial string as BINARY
IFstream::format(IOstreamOption::BINARY);
readEnsightString(*this, buffer);
// Detect BINARY vs ASCII by testing for initial "(C|Fortran) Binary"
if (buffer.contains("Binary") || buffer.contains("binary"))
{
// Format is BINARY
IFstream::format(IOstreamOption::BINARY);
// New backtracking point is after the initial "C Binary" string
curr_pos = iss.tellg();
// Get the next (optional) line after the "C Binary" (if any)
// and before the description.
readEnsightString(*this, buffer);
}
else
{
// Not binary => ASCII
IFstream::format(IOstreamOption::ASCII);
// Rewind to the beginning again
iss.seekg(curr_pos);
}
}
else
{
// Get the next line.
// It is either the description line or "BEGIN TIME STEP".
readEnsightString(*this, buffer);
}
// The buffer string now either contains the description line
// or "BEGIN TIME STEP"
if (buffer.starts_with("BEGIN TIME STEP"))
{
// Transient single file.
// File position is now after the "BEGIN TIME STEP" string
curr_pos = iss.tellg(); // Fallback value
timeStepFooterBegin_ = getTimeStepFooter(*this, timeStepOffsets_);
if (timeStepOffsets_.empty())
{
// Treat like a single time step
timeStepOffsets_.resize(1, int64_t(curr_pos));
}
}
else
{
// A description line and not "BEGIN TIME STEP"
// so backtrack to before it was read
lineNumber(lineNum); // Restore line number
iss.seekg(curr_pos); // Restore file position
timeStepFooterBegin_ = -1; // safety
timeStepOffsets_.clear(); // safety
}
DebugInfo<< "Time-steps: " << timeStepOffsets_.size() << endl;
syncState();
} }
@ -80,8 +330,11 @@ Foam::ensightReadFile::ensightReadFile
const fileName& pathname const fileName& pathname
) )
: :
IFstream(pathname, ensightReadFile::detectBinaryHeader(pathname)) IFstream(pathname, IOstreamOption::BINARY), // Start as BINARY
{} timeStepFooterBegin_(-1)
{
init(true); // detectFormat = true
}
Foam::ensightReadFile::ensightReadFile Foam::ensightReadFile::ensightReadFile
@ -90,12 +343,16 @@ Foam::ensightReadFile::ensightReadFile
IOstreamOption::streamFormat fmt IOstreamOption::streamFormat fmt
) )
: :
IFstream(pathname, fmt) IFstream(pathname, fmt),
{} timeStepFooterBegin_(-1)
{
init(false); // detectFormat = false
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
// Same as IFstream::readRaw(buf, count)
Foam::Istream& Foam::ensightReadFile::read Foam::Istream& Foam::ensightReadFile::read
( (
char* buf, char* buf,
@ -108,113 +365,39 @@ Foam::Istream& Foam::ensightReadFile::read
} }
// TBD
// Foam::Istream& Foam::ensightReadFile::read(word& value)
// {
// readString(value);
// string::stripInvalid<word>(value);
// return *this;
// }
Foam::Istream& Foam::ensightReadFile::read(string& value) Foam::Istream& Foam::ensightReadFile::read(string& value)
{ {
if (format() == IOstreamOption::BINARY) readString(value);
{
auto& iss = stdStream();
// Binary string is *exactly* 80 characters
value.resize(80, '\0');
iss.read(&value[0], 80);
const std::streamsize gcount = iss.gcount();
value.erase(gcount <= 0 ? 0 : gcount); // Truncated?
// Could exit on truncated input, but no real advantage
syncState();
// Truncate at the first embedded '\0'
auto endp = value.find('\0');
if (endp != std::string::npos)
{
value.erase(endp);
}
// May have been padded with trailing spaces - remove those
endp = value.find_last_not_of(" \t\f\v\n\r");
if (endp != std::string::npos)
{
value.erase(endp + 1);
}
}
else
{
value.clear();
while (value.empty() && !eof())
{
getLine(value);
}
}
return *this; return *this;
} }
Foam::Istream& Foam::ensightReadFile::read(label& value) Foam::Istream& Foam::ensightReadFile::read(label& value)
{ {
int ivalue; value = getPrimitive<int>(*this);
if (format() == IOstreamOption::BINARY)
{
read
(
reinterpret_cast<char*>(&ivalue),
sizeof(ivalue)
);
}
else
{
stdStream() >> ivalue;
syncState();
}
value = ivalue;
return *this; return *this;
} }
Foam::Istream& Foam::ensightReadFile::read(float& value) Foam::Istream& Foam::ensightReadFile::read(float& value)
{ {
if (format() == IOstreamOption::BINARY) value = getPrimitive<float>(*this);
{
read
(
reinterpret_cast<char*>(&value),
sizeof(value)
);
}
else
{
stdStream() >> value;
syncState();
}
return *this; return *this;
} }
Foam::Istream& Foam::ensightReadFile::read(double& value) Foam::Istream& Foam::ensightReadFile::read(double& value)
{ {
float fvalue; value = getPrimitive<float>(*this);
if (format() == IOstreamOption::BINARY)
{
read
(
reinterpret_cast<char*>(&fvalue),
sizeof(fvalue)
);
}
else
{
stdStream() >> fvalue;
syncState();
}
value = fvalue;
return *this; return *this;
} }
@ -226,15 +409,79 @@ Foam::Istream& Foam::ensightReadFile::readKeyword(string& key)
} }
Foam::Istream& Foam::ensightReadFile::readBinaryHeader() void Foam::ensightReadFile::readPoints
(
const label nPoints,
List<floatVector>& points
)
{ {
if (format() == IOstreamOption::BINARY) points.resize_nocopy(nPoints);
for (auto& p : points)
{ {
string buffer; read(p.x());
read(buffer); }
for (auto& p : points)
{
read(p.y());
}
for (auto& p : points)
{
read(p.z());
}
}
void Foam::ensightReadFile::readPoints
(
const label nPoints,
List<doubleVector>& points
)
{
points.resize_nocopy(nPoints);
for (auto& p : points)
{
read(p.x());
}
for (auto& p : points)
{
read(p.y());
}
for (auto& p : points)
{
read(p.z());
}
}
bool Foam::ensightReadFile::seekTime(const label timeIndex)
{
if (timeIndex >= 0 && timeIndex < timeStepOffsets_.size())
{
auto& iss = stdStream();
iss.seekg(timeStepOffsets_[timeIndex]);
syncState();
if (debug)
{
Info<< "seek time "
<< timeIndex << '/' << nTimes()
<< " offset:" << label(timeStepOffsets_[timeIndex]) << nl;
}
return true;
} }
return *this; if (debug)
{
Info<< "seek time "
<< timeIndex << '/' << nTimes()
<< " ignored" << nl;
}
return false;
} }

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2023 OpenCFD Ltd. Copyright (C) 2016-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -27,9 +27,8 @@ Class
Foam::ensightReadFile Foam::ensightReadFile
Description Description
A variant of IFstream with specialised read() for A variant of IFstream with specialised handling for Ensight reading
strings, integers and floats. of strings, integers and floats (ASCII and BINARY).
Correctly handles binary read as well.
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -38,6 +37,7 @@ Description
#include "IFstream.H" #include "IFstream.H"
#include "IOstream.H" #include "IOstream.H"
#include "vector.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -52,9 +52,28 @@ class ensightReadFile
: :
public IFstream public IFstream
{ {
public: // Private Data
// Generated Methods //- Transient single-file:
//- beginning of time-step footer
//- as read from the file
int64_t timeStepFooterBegin_;
//- Transient single-file:
//- the time-step file-offsets (position after "BEGIN TIME STEP")
//- as read from the file
List<int64_t> timeStepOffsets_;
// Private Member Functions
//- Read string as "%80s" or as binary
void readString(std::string& value);
//- Initializes read information
//- (optional detection of "C Binary" header)
//- and scan for transient single-file format.
void init(bool detectFormat);
//- No copy construct //- No copy construct
ensightReadFile(const ensightReadFile&) = delete; ensightReadFile(const ensightReadFile&) = delete;
@ -63,10 +82,20 @@ public:
void operator=(const ensightReadFile&) = delete; void operator=(const ensightReadFile&) = delete;
public:
//- Debug switch
static int debug;
// Constructors // Constructors
//- Construct from pathname, auto-detect the format //- Construct a geometry reader, auto-detecting the "C Binary" header
explicit ensightReadFile(const fileName& pathname); //- for binary files and skipping past it.
explicit ensightReadFile
(
const fileName& pathname
);
//- Construct from pathname, use the specified (ascii/binary) format //- Construct from pathname, use the specified (ascii/binary) format
ensightReadFile ensightReadFile
@ -82,11 +111,13 @@ public:
// Static Functions // Static Functions
//- Detect if the file is \em binary by testing for initial //- Extract time step footer information (if any).
//- "(C|Fortran) Binary" // \return the begin of footer position.
static IOstreamOption::streamFormat detectBinaryHeader static int64_t getTimeStepFooter
( (
const fileName& pathname IFstream& is,
//! [out] File offsets for each time step (if any)
List<int64_t>& offsets
); );
@ -101,7 +132,7 @@ public:
//- Read string as "%80s" or as binary //- Read string as "%80s" or as binary
virtual Istream& read(string& value) override; virtual Istream& read(string& value) override;
//- Read integer as "%10d" or as binary //- Read integer as "%10d" or as binary (narrowed) int
virtual Istream& read(label& value) override; virtual Istream& read(label& value) override;
//- Read floating-point as "%12.5e" or as binary //- Read floating-point as "%12.5e" or as binary
@ -113,8 +144,68 @@ public:
//- Read element keyword. Currently the same as read(string) //- Read element keyword. Currently the same as read(string)
Istream& readKeyword(string& key); Istream& readKeyword(string& key);
//- Read "C Binary" for binary files (eg, geometry/measured)
Istream& readBinaryHeader(); // Special Read Functions
//- Component-wise reading of points/coordinates.
//- Read all x components, y components and z components.
void readPoints(const label nPoints, List<floatVector>& points);
//- Component-wise reading of points/coordinates.
//- Reads x components, y components and z components.
void readPoints(const label nPoints, List<doubleVector>& points);
//- Read and discard specified number of elements
template<class Type>
void skip(label n = 1)
{
Type dummy;
for (; n > 0; --n)
{
this->read(dummy);
}
}
// Transient single-file format
//- Transient single-file:
//- the position of the FILE_INDEX footer
int64_t timeStepFooterBegin() const noexcept
{
return timeStepFooterBegin_;
}
//- Transient single-file:
//- the number of time steps within the file
label nTimes() const noexcept
{
return timeStepOffsets_.size();
}
//- Transient single-file:
//- the file-offsets for time steps within the file
const UList<int64_t>& timeStepOffets() const noexcept
{
return timeStepOffsets_;
}
//- Transient single-file:
//- seek to the file position corresponding to the given time index.
bool seekTime(const label timeIndex);
// Housekeeping
//- Detect if the file is \em binary by testing for initial
//- "(C|Fortran) Binary"
FOAM_DEPRECATED_FOR(2024-05, "detected on construct")
static IOstreamOption::streamFormat
detectBinaryHeader(const fileName& pathname)
{
ensightReadFile reader(pathname);
return reader.format();
}
}; };

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2021-2022 OpenCFD Ltd. Copyright (C) 2021-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -125,6 +125,11 @@ void Foam::ensightFaMesh::write
bool parallel bool parallel
) const ) const
{ {
if (UPstream::master())
{
os.beginGeometry();
}
// Area meshes (currently only one) // Area meshes (currently only one)
// const label areaId = 0; // const label areaId = 0;
areaPart_.write(os, mesh_.mesh(), parallel); areaPart_.write(os, mesh_.mesh(), parallel);

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2021-2022 OpenCFD Ltd. Copyright (C) 2021-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -147,14 +147,17 @@ public:
// Output // Output
//- Write geometry to file. Normally in parallel //- Write geometry to file (normally in parallel).
//- Adds beginGeometry() marker.
void write void write
( (
ensightGeoFile& os, ensightGeoFile& os,
bool parallel = UPstream::parRun() bool parallel = UPstream::parRun()
) const; ) const;
//- Write geometry to file. Normally in parallel //- Write geometry to file (normally in parallel).
//- Adds beginGeometry() marker.
FOAM_DEPRECATED_FOR(2024-05, "write(ensightGeoFile&, ...")
inline void write inline void write
( (
autoPtr<ensightGeoFile>& os, autoPtr<ensightGeoFile>& os,

View File

@ -391,6 +391,11 @@ bool Foam::functionObjects::ensightCloudWriteObject::write()
// Generate a (non-moving) dummy geometry // Generate a (non-moving) dummy geometry
// - ParaView ensight-reader needs this, and usually ensight does too // - ParaView ensight-reader needs this, and usually ensight does too
autoPtr<ensightGeoFile> os = ensCase().newGeometry(false); autoPtr<ensightGeoFile> os = ensCase().newGeometry(false);
if (os)
{
os->beginGeometry();
}
ensightCells::writeBox(os.ref(), mesh_.bounds()); ensightCells::writeBox(os.ref(), mesh_.bounds());
} }

View File

@ -237,7 +237,7 @@ bool Foam::functionObjects::ensightWrite::write()
// Treat all geometry as moving, since we do not know a priori // Treat all geometry as moving, since we do not know a priori
// if the simulation has mesh motion later on. // if the simulation has mesh motion later on.
autoPtr<ensightGeoFile> os = ensCase_().newGeometry(true); autoPtr<ensightGeoFile> os = ensCase_().newGeometry(true);
ensMesh_().write(os); ensMesh_().write(os.ref());
} }

View File

@ -92,9 +92,9 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
merge(); merge();
{ {
if (!isDir(outputFile.path())) if (!Foam::isDir(outputFile.path()))
{ {
mkDir(outputFile.path()); Foam::mkDir(outputFile.path());
} }
const bool stateChanged = const bool stateChanged =
@ -131,12 +131,12 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
); );
// As per mkdir -p "data/00000000" // As per mkdir -p "data/00000000"
mkDir(dataDir); Foam::mkDir(dataDir);
const fileName geomFile(baseDir/geometryName); const fileName geomFile(baseDir/geometryName);
if (!exists(geomFile)) if (!Foam::exists(geomFile))
{ {
if (verbose_) if (verbose_)
{ {
@ -151,6 +151,7 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
caseOpts_.format() caseOpts_.format()
); );
osGeom.beginGeometry();
writeGeometry(osGeom, elemOutput); writeGeometry(osGeom, elemOutput);
} }
@ -176,7 +177,12 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
// Update case file // Update case file
if (stateChanged) if (stateChanged)
{ {
OFstream osCase(outputFile, IOstreamOption::ASCII); OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
if (verbose_) if (verbose_)

View File

@ -91,9 +91,9 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
merge(); merge();
{ {
if (!isDir(outputFile.path())) if (!Foam::isDir(outputFile.path()))
{ {
mkDir(outputFile.path()); Foam::mkDir(outputFile.path());
} }
// Two-argument form for path-name to avoid validating base-dir // Two-argument form for path-name to avoid validating base-dir
@ -110,6 +110,7 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
caseOpts_.format() caseOpts_.format()
); );
osGeom.beginGeometry();
writeGeometry(osGeom, elemOutput); writeGeometry(osGeom, elemOutput);
// Write field (serial only) // Write field (serial only)
@ -118,7 +119,12 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
// Update case file // Update case file
{ {
OFstream osCase(outputFile, IOstreamOption::ASCII); OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
osCase osCase

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -44,54 +44,57 @@ namespace Foam
namespace Foam namespace Foam
{ {
// Read and discard specified number of elements // Extract timeset and fileset from split line information
template<class Type> // when the minElements has been satisfied.
static inline void discard(label n, ensightReadFile& is) // For example,
// ----
// model: 1 some-geometry
// model: 1 1 some-geometry
// ----
// would be split *after* the 'model:' resulting in these sub-strings:
//
// ("1" "some-geometry")
// ("1" "1" some-geometry")
//
// thus call extractTimeset with minElements == 2
//
template<class StringType>
static inline labelPair extractTimeset
(
const SubStrings<StringType>& split,
const std::size_t minElements
)
{ {
Type val; ISpanStream is;
while (n > 0) labelPair result(-1, -1);
if (split.size() >= minElements)
{ {
is.read(val); is.reset(split[0]);
--n; is >> result.first();
if (split.size() > minElements)
{
is.reset(split[1]);
is >> result.second();
}
} }
return result;
} }
} // End namespace Foam } // End namespace Foam
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // // * * * * * * * * * * Protected Static Member Functions * * * * * * * * * * //
void Foam::ensightSurfaceReader::skip(const label n, Istream& is) const bool Foam::ensightSurfaceReader::readLine(ISstream& is, std::string& line)
{
label i = 0;
token tok;
while (is.good() && (i < n))
{
is >> tok;
++i;
DebugInfo
<< "Skipping token " << tok << nl;
}
if (i != n)
{
WarningInFunction
<< "Requested to skip " << n << " tokens, but stream exited after "
<< i << " tokens. Last token read: " << tok
<< nl;
}
}
void Foam::ensightSurfaceReader::readLine(ISstream& is, string& line) const
{ {
do do
{ {
is.getLine(line); is.getLine(line);
// Trim out any '#' comments // Trim out any '#' comments (trailing or otherwise)
const auto pos = line.find('#'); const auto pos = line.find('#');
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
@ -100,6 +103,32 @@ void Foam::ensightSurfaceReader::readLine(ISstream& is, string& line) const
stringOps::inplaceTrimRight(line); stringOps::inplaceTrimRight(line);
} }
while (line.empty() && is.good()); while (line.empty() && is.good());
return !line.empty();
}
void Foam::ensightSurfaceReader::checkSection
(
const word& expected,
const string& buffer,
const ISstream& is
)
{
// Be more generous with our expectations.
// Eg, ensight specifies the "VARIABLE" entry,
// but accepts "VARIABLES" as well.
if (!expected.empty() && !buffer.starts_with(expected))
{
FatalIOErrorInFunction(is)
<< "Expected section header '" << expected
<< "' but read " << buffer << nl
<< exit(FatalIOError);
}
DebugInfo
<< "Read section header: " << buffer.c_str() << nl;
} }
@ -107,54 +136,23 @@ void Foam::ensightSurfaceReader::debugSection
( (
const word& expected, const word& expected,
ISstream& is ISstream& is
) const
{
string actual;
readLine(is, actual);
if (expected != actual)
{
FatalIOErrorInFunction(is)
<< "Expected section header '" << expected
<< "' but read " << actual << nl
<< exit(FatalIOError);
}
DebugInfo
<< "Read section header: " << expected << nl;
}
Foam::fileName Foam::ensightSurfaceReader::replaceMask
(
const fileName& fName,
const label timeIndex
) )
{ {
fileName result(fName); string buffer;
readLine(is, buffer);
const auto nMask = stringOps::count(fName, '*'); checkSection(expected, buffer, is);
// If there are any '*' chars, they are assumed to be contiguous
// Eg, data/******/geometry
if (nMask)
{
const std::string maskStr(nMask, '*');
const Foam::word indexStr(ensightCase::padded(nMask, timeIndex));
result.replace(maskStr, indexStr);
}
return result;
} }
Foam::Pair<Foam::ensightSurfaceReader::idTypes> // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
{
// Binary flag string if applicable
is.readBinaryHeader();
Foam::Pair<Foam::ensightSurfaceReader::idTypes>
Foam::ensightSurfaceReader::readGeometryHeader
(
ensightReadFile& is
) const
{
string buffer; string buffer;
Pair<idTypes> idHandling(idTypes::NONE, idTypes::NONE); Pair<idTypes> idHandling(idTypes::NONE, idTypes::NONE);
@ -203,17 +201,17 @@ Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
// Optional extents - read and discard 6 floats // Optional extents - read and discard 6 floats
// (xmin, xmax, ymin, ymax, zmin, zmax) // (xmin, xmax, ymin, ymax, zmin, zmax)
discard<scalar>(6, is); is.skip<scalar>(6);
// Part // "part"
is.read(buffer); is.read(buffer);
DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl; DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
} }
// The part number // The part number
label ivalue; label intValue;
is.read(ivalue); is.read(intValue);
DebugInfo<< "ivalue: " << ivalue << nl; DebugInfo<< "part number: " << intValue << nl;
// Part description / name // Part description / name
is.read(buffer); is.read(buffer);
@ -231,6 +229,8 @@ void Foam::ensightSurfaceReader::readCase(ISstream& is)
{ {
DebugInFunction << endl; DebugInFunction << endl;
enum ParseSection { UNKNOWN, FORMAT, GEOMETRY, VARIABLE, TIME, FILE };
if (!is.good()) if (!is.good())
{ {
FatalErrorInFunction FatalErrorInFunction
@ -239,99 +239,366 @@ void Foam::ensightSurfaceReader::readCase(ISstream& is)
} }
string buffer; string buffer;
SubStrings<string> split;
// Read the file ParseSection parseState = ParseSection::UNKNOWN;
// FORMAT
// ~~~~~~
debugSection("FORMAT", is); debugSection("FORMAT", is);
readLine(is, buffer); // type: ensight gold readLine(is, buffer); // type: ensight gold
parseState = ParseSection::FORMAT;
// GEOMETRY
// ~~~~~~~~
debugSection("GEOMETRY", is); debugSection("GEOMETRY", is);
readLine(is, buffer); parseState = ParseSection::GEOMETRY;
// GEOMETRY with any of these do
// model: 1 xxx.0000.mesh {
// model: xxx.0000.mesh readLine(is, buffer);
// model: data/directory/geometry
//
// - use the last entry
meshFileName_ = stringOps::splitSpace(buffer).back().str();
DebugInfo << "mesh file:" << meshFileName_ << endl; if (buffer.starts_with("VARIABLE"))
{
parseState = ParseSection::VARIABLE;
break;
}
debugSection("VARIABLE", is); if (buffer.contains("change_coords_only"))
{
FatalIOErrorInFunction(is)
<< "No support for moving points, only topology change" << nl
<< exit(FatalIOError);
}
// Extract filename from GEOMETRY section
// ====
// model: [ts] [fs] filename [change_coords_only [cstep]]
//
// ====
// TBD:
// check for "model:" vs "measured:" ?
const auto pos_colon = buffer.find(':');
if
(
(pos_colon == std::string::npos)
)
{
FatalIOErrorInFunction(is)
<< "Error reading geometry 'model:'" << nl
<< exit(FatalIOError);
}
split = stringOps::splitSpace(buffer, pos_colon+1);
if (split.empty())
{
FatalIOErrorInFunction(is)
<< "Error reading geometry 'model:'" << nl
<< exit(FatalIOError);
}
// With timeset? - need at least 2 entries
meshTimeset_ = extractTimeset(split, 2);
meshFileName_ = split[split.size()-1].str();
DebugInfo << "mesh file:" << meshFileName_ << endl;
}
while (false);
if (parseState != ParseSection::VARIABLE)
{
debugSection("VARIABLE", is);
parseState = ParseSection::VARIABLE;
}
// Read the field description // Read the field description
DynamicList<word> fieldNames(16); DynamicList<labelPair> dynFieldTimesets(16);
DynamicList<string> fieldFileNames(16); DynamicList<word> dynFieldNames(16);
DynamicList<string> dynFieldFileNames(16);
// VARIABLE
// ~~~~~~~~
while (is.good()) while (is.good())
{ {
readLine(is, buffer); readLine(is, buffer);
if (buffer == "TIME") if (buffer.starts_with("TIME"))
{ {
parseState = ParseSection::TIME;
break; break;
} }
// Read the field name and associated file name. Eg, // Read the field name and associated file name.
// scalar per element: 1 p data/********/p // Eg,
// scalar per element: [ts] [fs] p data/********/p
// but ignore
// scalar per node: [ts] [fs] other data/********/other
const auto parsed = stringOps::splitSpace(buffer); const auto pos_colon = buffer.find(':');
if (!buffer.contains(':') || parsed.size() < 4) if (pos_colon == std::string::npos)
{
DebugInfo<< "ignore variable line: " << buffer << nl;
continue;
}
// TODO? handle variable descriptions with spaces (they are quoted)
split = stringOps::splitSpace(buffer, pos_colon+1);
if (split.size() < 2)
{ {
WarningInFunction WarningInFunction
<< "Error reading field file name. Current buffer: " << "Error reading field file name, variable line: "
<< buffer << endl; << buffer << endl;
continue; continue;
} }
else if (debug)
auto pos_key = buffer.find("element");
if ((pos_key == std::string::npos) || (pos_colon < pos_key))
{ {
Info<< "variable line: " << parsed.size(); DebugInfo<< "ignore variable line: " << buffer << nl;
for (const auto& s : parsed) continue;
{
Info<< " " << s.str();
}
Info<< nl;
} }
fieldNames.push_back(parsed[parsed.size()-2].str()); DebugInfo<< "variable line: " << buffer << nl;
fieldFileNames.push_back(parsed.back().str());
// With timeset? - need at least 3 entries
dynFieldTimesets.push_back(extractTimeset(split, 3));
dynFieldNames.push_back(split[split.size()-2].str());
dynFieldFileNames.push_back(split[split.size()-1].str());
} }
fieldNames_.transfer(fieldNames); fieldTimesets_.transfer(dynFieldTimesets);
fieldFileNames_.transfer(fieldFileNames); fieldNames_.transfer(dynFieldNames);
fieldFileNames_.transfer(dynFieldFileNames);
DebugInfo DebugInfo
<< "fieldNames: " << fieldNames_ << nl << "fieldNames: " << fieldNames_ << nl
<< "fieldFileNames: " << fieldFileNames_ << nl; << "fieldFileNames: " << fieldFileNames_ << nl;
// Start reading time information
readLine(is, buffer); // time set: <int>
readLine(is, buffer); if (parseState != ParseSection::TIME)
readFromLine(3, buffer, nTimeSteps_); // number of steps: <int> {
readLine(is, buffer); FatalIOErrorInFunction(is)
readFromLine(3, buffer, timeStartIndex_); // filename start number: <int> << "Did not find section header 'TIME'" << nl
readLine(is, buffer); << exit(FatalIOError);
readFromLine(2, buffer, timeIncrement_); // filename increment: <int> }
// Determine which unique timeset or fileset to expect
labelHashSet expectTimeset;
labelHashSet expectFileset;
expectTimeset.insert(meshTimeset_.first());
expectFileset.insert(meshTimeset_.second());
for (const auto& tup : fieldTimesets_)
{
expectTimeset.insert(tup.first());
expectFileset.insert(tup.second());
}
// Remove placeholders
expectTimeset.erase(-1);
expectFileset.erase(-1);
DebugInfo DebugInfo
<< "nTimeSteps: " << nTimeSteps_ << nl << "expect timesets: " << flatOutput(expectTimeset) << nl
<< "timeStartIndex: " << timeStartIndex_ << nl << "expect filesets: " << flatOutput(expectFileset) << nl;
<< "timeIncrement: " << timeIncrement_ << nl;
// Read the time values
readLine(is, buffer); // time values: // TIME
timeValues_.resize_nocopy(nTimeSteps_); // ~~~~
for (label i = 0; i < nTimeSteps_; ++i) // style 1:
// ====
// time set: <int> [description]
// number of steps: <int>
// filename start number: <int>
// filename increment: <int>
// time values: time_1 .. time_N
// ====
//
// style 2:
// ====
// time set: <int> [description]
// number of steps: <int>
// filename numbers: int_1 .. int_N
// time values: time_1 .. time_N
// ====
//
// style 3:
// ====
// time set: <int> [description]
// number of steps: <int>
// filename numbers file: <filename>
// time values file: <filename>
// ====
// Currently only handling style 1, style 2
// and only a single time set
// time set = 1
{ {
scalar t(readScalar(is)); // time set: <int>
{
readLine(is, buffer);
}
timeValues_[i].value() = t; // number of steps: <int>
// TODO: use character representation of t directly instead of label nTimes = 0;
// regenerating from scalar value {
timeValues_[i].name() = Foam::name(t); readLine(is, buffer);
split = stringOps::splitSpace(buffer);
readFrom(split.back(), nTimes);
}
// filename start number: <int>
// filename increment: <int>
//
// OR:
// filename numbers: ...
readLine(is, buffer);
auto pos_colon = buffer.find(':');
if (buffer.contains("numbers:"))
{
// Split out trailing values...
split = stringOps::splitSpace(buffer, pos_colon+1);
fileNumbers_.resize_nocopy(nTimes);
label numRead = 0;
while (numRead < nTimes)
{
for (const auto& chunk : split)
{
std::string str(chunk.str());
if (!Foam::readLabel(str, fileNumbers_[numRead]))
{
FatalIOErrorInFunction(is)
<< "Could not parse label: " << str << nl
<< exit(FatalIOError);
}
++numRead;
if (numRead == nTimes)
{
break;
}
}
// Get more input
if (numRead < nTimes)
{
readLine(is, buffer);
split = stringOps::splitSpace(buffer);
}
}
timeStartIndex_ = 0;
timeIncrement_ = 0;
fileNumbers_.resize(numRead);
}
else
{
// filename start number: <int>
split = stringOps::splitSpace(buffer);
readFrom(split.back(), timeStartIndex_);
// filename increment: <int>
readLine(is, buffer);
split = stringOps::splitSpace(buffer);
readFrom(split.back(), timeIncrement_);
fileNumbers_.clear();
}
DebugInfo
<< "nTimes: " << nTimes
<< " start-index: " << timeStartIndex_
<< " increment: " << timeIncrement_
<< " file numbers: " << flatOutput(fileNumbers_) << nl;
// time values: time_1 .. time_N
readLine(is, buffer);
// Split out trailing values...
{
const auto pos_colon = buffer.find(':');
const auto pos_key = buffer.find("values");
if
(
(pos_colon == std::string::npos)
|| (pos_key == std::string::npos) || (pos_colon < pos_key)
)
{
split.clear();
}
else
{
split = stringOps::splitSpace(buffer, pos_colon+1);
}
}
timeValues_.resize_nocopy(nTimes);
label numRead = 0;
while (numRead < nTimes)
{
for (const auto& chunk : split)
{
auto& inst = timeValues_[numRead];
// Retain character representation
inst.name() = word(chunk.str());
if (!Foam::readScalar(inst.name(), inst.value()))
{
FatalIOErrorInFunction(is)
<< "Could not parse scalar: " << inst.name() << nl
<< exit(FatalIOError);
}
++numRead;
if (numRead == nTimes)
{
break;
}
}
// Get more input
if (numRead < nTimes)
{
readLine(is, buffer);
split = stringOps::splitSpace(buffer);
}
}
timeValues_.resize(numRead);
} }
// Not yet:
// FILE
// ~~~~
// file set: <int>
// filename index: <int> - file index number in the file name
// number of steps: <int>
} }
@ -351,14 +618,9 @@ Foam::ensightSurfaceReader::ensightSurfaceReader
), ),
readFormat_(IOstreamOption::ASCII), // Placeholder value readFormat_(IOstreamOption::ASCII), // Placeholder value
baseDir_(fName.path()), baseDir_(fName.path()),
meshFileName_(), meshTimeset_(-1,-1),
fieldNames_(),
fieldFileNames_(),
nTimeSteps_(0),
timeStartIndex_(0), timeStartIndex_(0),
timeIncrement_(1), timeIncrement_(1)
timeValues_(),
surfPtr_(nullptr)
{ {
if (options.getOrDefault("debug", false)) if (options.getOrDefault("debug", false))
{ {
@ -376,12 +638,14 @@ Foam::ensightSurfaceReader::ensightSurfaceReader
Pstream::broadcasts Pstream::broadcasts
( (
UPstream::worldComm, UPstream::worldComm,
meshTimeset_,
meshFileName_, meshFileName_,
fieldTimesets_,
fieldNames_, fieldNames_,
fieldFileNames_, fieldFileNames_,
nTimeSteps_,
timeStartIndex_, timeStartIndex_,
timeIncrement_, timeIncrement_,
fileNumbers_,
timeValues_ timeValues_
); );
} }
@ -392,7 +656,8 @@ Foam::ensightSurfaceReader::ensightSurfaceReader
Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
( (
const fileName& geometryFile const fileName& geometryFile,
const label timeIndex
) )
{ {
DebugInFunction << endl; DebugInFunction << endl;
@ -404,6 +669,9 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
// Format detected from the geometry // Format detected from the geometry
readFormat_ = is.format(); readFormat_ = is.format();
// For transient single-file
is.seekTime(timeIndex);
DebugInfo DebugInfo
<< "File: " << is.name() << "File: " << is.name()
<< " format: " << " format: "
@ -433,52 +701,59 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
<< "Ignore " << nPoints << " node ids" << nl; << "Ignore " << nPoints << " node ids" << nl;
// Read and discard labels // Read and discard labels
discard<label>(nPoints, is); is.skip<label>(nPoints);
} }
pointField points(nPoints); pointField points;
for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) is.readPoints(nPoints, points);
{
for (point& p : points)
{
is.read(p[cmpt]);
}
}
// Read faces - may be a mix of tria3, quad4, nsided // Read faces - may be a mix of tria3, quad4, nsided
DynamicList<face> dynFaces(nPoints/3); DynamicList<face> dynFaces(nPoints/3);
DynamicList<faceInfoTuple> faceTypeInfo(16); DynamicList<faceInfoTuple> faceTypeInfo(16);
string faceType; string buffer;
label faceCount = 0;
while (is.good()) // (is.peek() != EOF) while (is.good())
{ {
// The element type // The element type
is.read(faceType); is.read(buffer);
if (!is.good()) if (!is.good())
{ {
break; break;
} }
else if (buffer.contains("BEGIN TIME STEP"))
{
// Graciously handle a miscued start
continue;
}
else if (buffer.contains("END TIME STEP"))
{
// END TIME STEP is a valid means to terminate input
break;
}
if if
( (
faceType buffer
== ensightFaces::elemNames[ensightFaces::elemType::TRIA3] == ensightFaces::elemNames[ensightFaces::elemType::TRIA3]
) )
{ {
is.read(faceCount); label elemCount;
is.read(elemCount);
faceTypeInfo.push_back faceTypeInfo.emplace_back
( (
faceInfoTuple(ensightFaces::elemType::TRIA3, faceCount) ensightFaces::elemType::TRIA3,
elemCount
); );
DebugInfo DebugInfo
<< "faceType <" << faceType.c_str() << "> count: " << "faceType <"
<< faceCount << nl; << ensightFaces::elemNames[ensightFaces::elemType::TRIA3]
<< "> count: "
<< elemCount << nl;
if if
( (
@ -487,39 +762,45 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
) )
{ {
DebugInfo DebugInfo
<< "Ignore " << faceCount << " element ids" << nl; << "Ignore " << elemCount << " element ids" << nl;
// Read and discard labels // Read and discard labels
discard<label>(faceCount, is); is.skip<label>(elemCount);
} }
for (label facei = 0; facei < faceCount; ++facei) // Extend and fill the new trailing portion
const label startElemi = dynFaces.size();
dynFaces.resize(startElemi+elemCount, face(3)); // tria3
faceList::subList myElements = dynFaces.slice(startElemi);
for (auto& f : myElements)
{ {
face f(3);
for (label& fp : f) for (label& fp : f)
{ {
is.read(fp); is.read(fp);
} }
dynFaces.push_back(std::move(f));
} }
} }
else if else if
( (
faceType buffer
== ensightFaces::elemNames[ensightFaces::elemType::QUAD4] == ensightFaces::elemNames[ensightFaces::elemType::QUAD4]
) )
{ {
is.read(faceCount); label elemCount;
is.read(elemCount);
faceTypeInfo.push_back faceTypeInfo.emplace_back
( (
faceInfoTuple(ensightFaces::elemType::QUAD4, faceCount) ensightFaces::elemType::QUAD4,
elemCount
); );
DebugInfo DebugInfo
<< "faceType <" << faceType.c_str() << "> count: " << "faceType <"
<< faceCount << nl; << ensightFaces::elemNames[ensightFaces::elemType::QUAD4]
<< "> count: "
<< elemCount << nl;
if if
( (
@ -528,39 +809,44 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
) )
{ {
DebugInfo DebugInfo
<< "Ignore " << faceCount << " element ids" << nl; << "Ignore " << elemCount << " element ids" << nl;
// Read and discard labels // Read and discard labels
discard<label>(faceCount, is); is.skip<label>(elemCount);
} }
for (label facei = 0; facei < faceCount; ++facei) // Extend and fill the new trailing portion
const label startElemi = dynFaces.size();
dynFaces.resize(startElemi + elemCount, face(4)); // quad4
faceList::subList myElements = dynFaces.slice(startElemi);
for (auto& f : myElements)
{ {
face f(4);
for (label& fp : f) for (label& fp : f)
{ {
is.read(fp); is.read(fp);
} }
dynFaces.push_back(std::move(f));
} }
} }
else if else if
( (
faceType buffer
== ensightFaces::elemNames[ensightFaces::elemType::NSIDED] == ensightFaces::elemNames[ensightFaces::elemType::NSIDED]
) )
{ {
is.read(faceCount); label elemCount;
is.read(elemCount);
faceTypeInfo.push_back faceTypeInfo.emplace_back
( (
faceInfoTuple(ensightFaces::elemType::NSIDED, faceCount) ensightFaces::elemType::NSIDED,
elemCount
); );
DebugInfo DebugInfo
<< "faceType <" << faceType.c_str() << "> count: " << "faceType <"
<< faceCount << nl; << ensightFaces::elemNames[ensightFaces::elemType::NSIDED]
<< "> count: " << elemCount << nl;
if if
( (
@ -569,26 +855,31 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
) )
{ {
DebugInfo DebugInfo
<< "Ignore " << faceCount << " element ids" << nl; << "Ignore " << elemCount << " element ids" << nl;
// Read and discard labels // Read and discard labels
discard<label>(faceCount, is); is.skip<label>(elemCount);
} }
labelList np(faceCount); // Extend and fill the new trailing portion
for (label facei = 0; facei < faceCount; ++facei) const label startElemi = dynFaces.size();
dynFaces.resize(startElemi + elemCount);
faceList::subList myElements = dynFaces.slice(startElemi);
for (auto& f : myElements)
{ {
is.read(np[facei]); label nVerts;
is.read(nVerts);
f.resize(nVerts);
} }
for (label facei = 0; facei < faceCount; ++facei)
for (auto& f : myElements)
{ {
face f(np[facei]);
for (label& fp : f) for (label& fp : f)
{ {
is.read(fp); is.read(fp);
} }
dynFaces.push_back(std::move(f));
} }
} }
else else
@ -596,7 +887,7 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
if (debug) if (debug)
{ {
WarningInFunction WarningInFunction
<< "Unknown face type: <" << faceType.c_str() << "Unknown face type: <" << buffer.c_str()
<< ">. Stopping read and continuing with current " << ">. Stopping read and continuing with current "
<< "elements only" << endl; << "elements only" << endl;
} }
@ -605,7 +896,7 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
} }
// From 1-based Ensight addressing to 0-based OF addressing // From 1-based Ensight addressing to 0-based OF addressing
for (face& f : dynFaces) for (auto& f : dynFaces)
{ {
for (label& fp : f) for (label& fp : f)
{ {
@ -637,11 +928,15 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry
surfPtr_.reset(new meshedSurface); surfPtr_.reset(new meshedSurface);
auto& surf = *surfPtr_; auto& surf = *surfPtr_;
fileName geomFile(baseDir_/replaceMask(meshFileName_, timeIndex)); fileName geomFile
(
baseDir_
/ ensightCase::expand_mask(meshFileName_, timeIndex)
);
if (!masterOnly_ || UPstream::master(UPstream::worldComm)) if (!masterOnly_ || UPstream::master(UPstream::worldComm))
{ {
surf = readGeometry(geomFile); surf = readGeometry(geomFile, timeIndex);
} }
if (masterOnly_ && UPstream::parRun()) if (masterOnly_ && UPstream::parRun())

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -57,6 +57,7 @@ SourceFiles
#define Foam_ensightSurfaceReader_H #define Foam_ensightSurfaceReader_H
#include "surfaceReader.H" #include "surfaceReader.H"
#include "ensightCase.H"
#include "ensightFaces.H" #include "ensightFaces.H"
#include "ensightReadFile.H" #include "ensightReadFile.H"
#include "Pair.H" #include "Pair.H"
@ -103,24 +104,30 @@ protected:
//- Base directory //- Base directory
fileName baseDir_; fileName baseDir_;
//- The timeset/fileset (if any) associated with the mesh
labelPair meshTimeset_;
//- Name of mesh file, including any subdirectory //- Name of mesh file, including any subdirectory
fileName meshFileName_; fileName meshFileName_;
//- The timeset/fileset (if any) associated with fields
List<labelPair> fieldTimesets_;
//- Field names //- Field names
List<word> fieldNames_; List<word> fieldNames_;
//- Field file names //- Field file names
List<string> fieldFileNames_; List<string> fieldFileNames_;
//- Number of time steps
label nTimeSteps_;
//- Start time index //- Start time index
label timeStartIndex_; label timeStartIndex_;
//- Time increment //- Time increment
label timeIncrement_; label timeIncrement_;
//- Numbers for files
labelList fileNumbers_;
//- Times //- Times
instantList timeValues_; instantList timeValues_;
@ -131,23 +138,30 @@ protected:
List<faceInfoTuple> faceTypeInfo_; List<faceInfoTuple> faceTypeInfo_;
// Private Member Functions // Static Member Functions
//- Helper function to skip forward n steps in stream //- Helper function to read an ascii line from file,
void skip(const label n, Istream& is) const; //- skipping blank lines and comments.
// \return True if reading was successful
static bool readLine(ISstream& is, std::string& line);
//- Helper function to read an ascii line from file //- Check a section header
void readLine(ISstream& is, string& buffer) const; static void checkSection
(
const word& expected,
const string& buffer,
const ISstream& is // For errors
);
//- Read and check a section header //- Read and check a section header
void debugSection(const word& expected, ISstream& is) const; static void debugSection(const word& expected, ISstream& is);
//- Replace the '*' mask chars with a 0 padded string. //- Helper function to return Type from string
static fileName replaceMask template<class Type>
( static void readFrom(const std::string& buffer, Type& value);
const fileName& fName,
const label timeIndex
); // Protected Member Functions
//- Read (and discard) geometry file header. //- Read (and discard) geometry file header.
// \return information about node/element id handling // \return information about node/element id handling
@ -157,27 +171,20 @@ protected:
void readCase(ISstream& is); void readCase(ISstream& is);
//- Read and return surface geometry. Updates faceTypeInfo_ //- Read and return surface geometry. Updates faceTypeInfo_
meshedSurface readGeometry(const fileName& geometryFile); meshedSurface readGeometry
//- Helper function to return Type after skipping n tokens
template<class Type>
void readFromLine(const label nSkip, Istream& is, Type& value) const;
//- Helper function to return Type after skipping n tokens
template<class Type>
void readFromLine
( (
const label nSkip, const fileName& geometryFile,
const string& buffer, //! Optional index for transient single-file format
Type& value const label timeIndex = 0
) const; );
//- Helper function to return a field //- Helper function to return a field
template<class Type> template<class Type>
tmp<Field<Type>> readField tmp<Field<Type>> readField
( (
const fileName& dataFile, const fileName& dataFile,
const word& fieldName const word& fieldName,
const label timeIndex = 0
) const; ) const;
//- Helper function to return a field //- Helper function to return a field
@ -191,6 +198,15 @@ protected:
public: public:
// Generated Methods
//- No copy construct
ensightSurfaceReader(const ensightSurfaceReader&) = delete;
//- No copy assignment
void operator=(const ensightSurfaceReader&) = delete;
//- Runtime type information //- Runtime type information
TypeName("ensight"); TypeName("ensight");

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -28,41 +28,28 @@ License
#include "SpanStream.H" #include "SpanStream.H"
#include "ensightPTraits.H" #include "ensightPTraits.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * Protected Static Member Functions * * * * * * * * * * //
template<class Type> template<class Type>
void Foam::ensightSurfaceReader::readFromLine void Foam::ensightSurfaceReader::readFrom
( (
const label nSkip, const std::string& buffer,
Istream& is,
Type& value Type& value
) const )
{ {
skip(nSkip, is); ISpanStream is(buffer.data(), buffer.size());
is >> value; is >> value;
} }
template<class Type> // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::ensightSurfaceReader::readFromLine
(
const label nSkip,
const string& buffer,
Type& value
) const
{
ISpanStream is(buffer.data(), buffer.length());
readFromLine(nSkip, is, value);
}
template<class Type> template<class Type>
Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
( (
const fileName& dataFile, const fileName& dataFile,
const word& fieldName const word& fieldName,
const label timeIndex
) const ) const
{ {
auto tfield = tmp<Field<Type>>::New(surfPtr_->nFaces(), Zero); auto tfield = tmp<Field<Type>>::New(surfPtr_->nFaces(), Zero);
@ -81,6 +68,10 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
<< exit(FatalError); << exit(FatalError);
} }
// If transient single-file
is.seekTime(timeIndex);
// Check that data type is as expected // Check that data type is as expected
// (assuming OpenFOAM generated the data set) // (assuming OpenFOAM generated the data set)
string primitiveType; string primitiveType;
@ -104,11 +95,11 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
} }
string strValue; string strValue;
label iValue; label intValue;
// Read header info: part index, e.g. part 1 // Read header info: part index, e.g. part 1
is.read(strValue); is.read(strValue);
is.read(iValue); is.read(intValue);
label begFace = 0; label begFace = 0;
@ -183,11 +174,18 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
} }
const word& fieldName = fieldNames_[fieldIndex]; const word& fieldName = fieldNames_[fieldIndex];
const label fileIndex = timeStartIndex_ + timeIndex*timeIncrement_;
const label fileIndex =
(
(timeIndex >= 0 && timeIndex < fileNumbers_.size())
? fileNumbers_[timeIndex]
: (timeStartIndex_ + timeIndex*timeIncrement_)
);
const fileName dataFile const fileName dataFile
( (
baseDir_/replaceMask(fieldFileNames_[fieldIndex], fileIndex) baseDir_
/ ensightCase::expand_mask(fieldFileNames_[fieldIndex], fileIndex)
); );
if (debug) if (debug)
@ -196,7 +194,7 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
<< dataFile << endl; << dataFile << endl;
} }
return readField<Type>(dataFile, fieldName); return readField<Type>(dataFile, fieldName, timeIndex);
} }

View File

@ -99,9 +99,9 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
if (UPstream::master() || !parallel_) if (UPstream::master() || !parallel_)
{ {
if (!isDir(outputFile.path())) if (!Foam::isDir(outputFile.path()))
{ {
mkDir(outputFile.path()); Foam::mkDir(outputFile.path());
} }
const bool stateChanged = const bool stateChanged =
@ -138,7 +138,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
); );
// As per mkdir -p "data/00000000" // As per mkdir -p "data/00000000"
mkDir(dataDir); Foam::mkDir(dataDir);
const fileName geomFile(baseDir/geometryName); const fileName geomFile(baseDir/geometryName);
@ -151,7 +151,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
geomFile.name() geomFile.name()
); );
if (!exists(geomFile)) if (!Foam::exists(geomFile))
{ {
if (verbose_) if (verbose_)
{ {
@ -165,7 +165,9 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
geomFile.name(), geomFile.name(),
caseOpts_.format() caseOpts_.format()
); );
part.write(osGeom); // serial
osGeom.beginGeometry();
part.write(osGeom); // serial
} }
// Write field // Write field
@ -190,7 +192,12 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
// Update case file // Update case file
if (stateChanged) if (stateChanged)
{ {
OFstream osCase(outputFile, IOstreamOption::ASCII); OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
if (verbose_) if (verbose_)

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2014 OpenFOAM Foundation Copyright (C) 2011-2014 OpenFOAM Foundation
Copyright (C) 2015-2023 OpenCFD Ltd. Copyright (C) 2015-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -64,11 +64,20 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
if (UPstream::master() || !parallel_) if (UPstream::master() || !parallel_)
{ {
if (!isDir(outputDir)) if (!Foam::isDir(outputDir))
{ {
mkDir(outputDir); Foam::mkDir(outputDir);
} }
// The geometry
ensightOutputSurface part
(
surf.points(),
surf.faces(),
baseName
);
// Two-argument form for path-name to avoid validating outputDir
ensightGeoFile osGeom ensightGeoFile osGeom
( (
outputDir, outputDir,
@ -76,16 +85,16 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
caseOpts_.format() caseOpts_.format()
); );
ensightOutputSurface part osGeom.beginGeometry();
( part.write(osGeom); // serial
surf.points(),
surf.faces(),
osGeom.name().name()
);
part.write(osGeom); // serial
// Update case file // Update case file
OFstream osCase(outputFile, IOstreamOption::ASCII); OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
osCase osCase
@ -168,11 +177,19 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
if (UPstream::master() || !parallel_) if (UPstream::master() || !parallel_)
{ {
if (!isDir(outputFile.path())) if (!Foam::isDir(outputFile.path()))
{ {
mkDir(outputFile.path()); Foam::mkDir(outputFile.path());
} }
// The geometry
ensightOutputSurface part
(
surf.points(),
surf.faces(),
baseName
);
// Two-argument form for path-name to avoid validating base-dir // Two-argument form for path-name to avoid validating base-dir
ensightGeoFile osGeom ensightGeoFile osGeom
( (
@ -187,14 +204,8 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
caseOpts_.format() caseOpts_.format()
); );
// Ensight Geometry osGeom.beginGeometry();
ensightOutputSurface part part.write(osGeom); // serial
(
surf.points(),
surf.faces(),
osGeom.name().name()
);
part.write(osGeom); // serial
// Write field (serial) // Write field (serial)
osField.write(ensightPTraits<Type>::typeName); osField.write(ensightPTraits<Type>::typeName);
@ -204,7 +215,12 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
// Update case file // Update case file
{ {
OFstream osCase(outputFile, IOstreamOption::ASCII); OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
osCase osCase