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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -73,7 +73,7 @@ inline bool Foam::ensightCase::separateCloud() 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
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,6 +28,35 @@ License
#include "cloud.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 * * * * * * * * * * * * * //
template<class Type>

View File

@ -27,6 +27,7 @@ License
\*---------------------------------------------------------------------------*/
#include "ensightFile.H"
#include "ensightReadFile.H"
#include "error.H"
#include "List.H"
#include <cstring>
@ -41,6 +42,37 @@ float Foam::ensightFile::undefValue_ = Foam::floatScalarVGREAT;
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 * * * * * * * * * * * * //
bool Foam::ensightFile::hasUndef(const UList<float>& field)
@ -82,6 +114,51 @@ void Foam::ensightFile::init()
std::ios_base::floatfield
);
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
(
std::nullptr_t, // dispatch tag
IOstreamOption::appendType append,
const fileName& pathname,
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();
}
@ -101,14 +197,44 @@ 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& name,
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::fill_n(buf + len, (80 - len), '\0'); // Pad trailing with nul
auto& oss = stdStream();
if (format() == IOstreamOption::BINARY)
{
write(buf, 80);
oss.write(buf, 80);
}
else
{
@ -170,9 +298,10 @@ void Foam::ensightFile::writeString(const char* str, size_t len)
// char* p = ::strchr(buf, '\n');
// if (p) *p = 0;
stdStream() << buf;
syncState();
oss << buf;
}
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
(
const char* buf,
@ -223,82 +353,56 @@ Foam::Ostream& Foam::ensightFile::write
void Foam::ensightFile::writeInt(const int32_t val, const int fieldWidth)
{
if (format() == IOstreamOption::BINARY)
{
write
(
reinterpret_cast<const char *>(&val),
sizeof(int32_t)
);
}
else
{
stdStream().width(fieldWidth);
stdStream() << val;
syncState();
}
putPrimitive<int32_t>(val, *this, fieldWidth);
}
void Foam::ensightFile::writeInt(const int64_t val, const int fieldWidth)
{
int32_t work(narrowInt32(val));
writeInt(work, fieldWidth);
putPrimitive<int32_t>(narrowInt32(val), *this, fieldWidth);
}
void Foam::ensightFile::writeFloat(const float val, const int fieldWidth)
{
if (format() == IOstreamOption::BINARY)
{
write
(
reinterpret_cast<const char *>(&val),
sizeof(float)
);
}
else
{
stdStream().width(fieldWidth);
stdStream() << val;
syncState();
}
putPrimitive<float>(val, *this, fieldWidth);
}
void Foam::ensightFile::writeFloat(const double val, const int fieldWidth)
{
float work(narrowFloat(val));
writeFloat(work, fieldWidth);
putPrimitive<float>(narrowFloat(val), *this, fieldWidth);
}
Foam::Ostream& Foam::ensightFile::write(const int32_t val)
{
writeInt(val, 10);
putPrimitive<int32_t>(val, *this, 10);
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const int64_t val)
{
writeInt(val, 10);
putPrimitive<int32_t>(narrowInt32(val), *this, 10);
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const float val)
{
writeFloat(val, 12);
putPrimitive<float>(val, *this, 12);
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const double val)
{
writeFloat(val, 12);
putPrimitive<float>(narrowFloat(val), *this, 12);
return *this;
}
@ -307,8 +411,7 @@ void Foam::ensightFile::newline()
{
if (format() == IOstreamOption::ASCII)
{
stdStream() << nl;
syncState();
OFstream::write('\n');
}
}
@ -343,22 +446,69 @@ void Foam::ensightFile::writeBinaryHeader()
if (format() == IOstreamOption::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
//
/* |---------------|---------------|-----------------------|
* | 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()
{
writeString("BEGIN 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();
// offset step 1, 2, ... N
for (int64_t off : timeStepOffsets_)
{
putPrimitive<int64_t>(off, *this, 20);
newline();
}
void Foam::ensightFile::endTimeStep()
{
writeString("END TIME STEP");
// 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
//
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)
{
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)
{
writeString("particle coordinates");

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,8 +28,8 @@ Class
Foam::ensightFile
Description
Ensight output with specialized write() for strings, integers and floats.
Correctly handles binary write as well.
A variant of OFstream with specialised handling for Ensight writing
of strings, integers and floats (ASCII and BINARY).
\*---------------------------------------------------------------------------*/
@ -39,6 +39,7 @@ Description
#include "OFstream.H"
#include "ensightFileName.H"
#include "ensightVarName.H"
#include "DynamicList.H"
#include "IndirectListBase.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -62,17 +63,36 @@ class ensightFile
//- Value to represent undef in results (default: 1e+37, floatVGREAT)
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
//- Initialize sets the ASCII output formatting
//- Set the ASCII output formatting etc,
//- and handle transient single-file timestep information
void init();
//- No copy construct
ensightFile(const ensightFile&) = delete;
//- No copy assignment
void operator=(const ensightFile&) = delete;
// Constructors
//- 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:
@ -98,30 +118,70 @@ public:
}
// Generated Methods
//- No copy construct
ensightFile(const ensightFile&) = delete;
//- No copy assignment
void operator=(const ensightFile&) = delete;
// Constructors
//- Construct from path-name.
// The path-name is adjusted for valid ensight file naming.
// Always created as an atomic
explicit ensightFile
// Created as an atomic or in append mode (single-file format).
// In append mode, attempts to parse existing time-step information.
ensightFile
(
//! Append mode: NO_APPEND or APPEND_ATE
IOstreamOption::appendType append,
const fileName& pathname,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
);
//- Construct from path and name.
// 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
(
//! 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.
// 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
~ensightFile() = default;
//- Construct from path and name.
// 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
@ -148,12 +208,6 @@ public:
//- Write "C Binary" string for binary files (eg, geometry/measured)
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)
void writeString(const char* str, size_t len);
@ -225,11 +279,59 @@ public:
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
//- Begin a part (0-based index internally).
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)
void beginParticleCoordinates(const label nparticles);

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -29,15 +29,56 @@ License
#include "ensightGeoFile.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();
beginGeometry();
if (!OFstream::is_appending())
{
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()
{
// 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 |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,7 +28,8 @@ Class
Foam::ensightGeoFile
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
{
// 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:
// 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
//- Construct from path-name.
//- Construct from path-name and initialise the binary header.
// 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,
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.
// 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
(
const fileName& path,
const fileName& name,
IOstreamOption::streamFormat fmt = IOstreamOption::BINARY
);
)
:
ensightGeoFile(IOstreamOption::NO_APPEND, path, name, fmt)
{}
//- Destructor
@ -107,19 +133,13 @@ public:
// Output
//- Write keyword with trailing newline
virtual Ostream& writeKeyword(const keyType& key);
virtual Ostream& writeKeyword(const keyType& key) override;
// Convenience Output Methods
//- Begin a part (0-based index).
using ensightFile::beginPart;
//- 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);
//- Start of geometry information
void beginGeometry();
};

View File

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

View File

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

View File

@ -41,8 +41,8 @@ Note
\*---------------------------------------------------------------------------*/
#ifndef ensightFileName_H
#define ensightFileName_H
#ifndef Foam_ensightFileName_H
#define Foam_ensightFileName_H
#include "fileName.H"
#include "word.H"
@ -81,6 +81,9 @@ public:
//- Copy construct from std::string
inline explicit FileName(const std::string& s);
//- Move construct from std::string
inline explicit FileName(std::string&& s);
// 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 * * * * * * * * * * * * * //
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
string::replaceAny(":<>[]", '_');
removeRepeated('_');
string::removeRepeated('_');
// Minor cleanup of fileName
removeRepeated('/');
removeEnd('/');
string::removeRepeated('/');
string::removeEnd('/');
if (empty())
{

View File

@ -41,8 +41,8 @@ Description
\*---------------------------------------------------------------------------*/
#ifndef ensightVarName_H
#define ensightVarName_H
#ifndef Foam_ensightVarName_H
#define Foam_ensightVarName_H
#include "word.H"
@ -80,6 +80,9 @@ public:
//- Copy construct from std::string
inline explicit VarName(const std::string& s);
//- Move construct from std::string
inline explicit VarName(std::string&& s);
// 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 * * * * * * * * * * * * * //
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()
{
string::stripInvalid<VarName>(*this);
string::stripInvalid<ensight::VarName>(*this);
if (empty())
{
@ -83,10 +91,9 @@ inline void Foam::ensight::VarName::stripInvalid()
}
// Prefix with '_' to avoid starting with leading digits
std::string::iterator iter = begin();
if (isdigit(*iter))
if (std::isdigit(front()))
{
insert(iter, '_');
insert(0, 1, '_');
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -26,50 +26,300 @@ License
\*---------------------------------------------------------------------------*/
#include "ensightReadFile.H"
#include "stringOps.H"
#include "defineDebugSwitch.H"
#include "registerSwitch.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
Foam::IOstreamOption::streamFormat
Foam::ensightReadFile::detectBinaryHeader(const fileName& pathname)
defineDebugSwitchWithName(Foam::ensightReadFile, "ensightReadFile", 0);
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)
{
IFstream ifs(pathname, IOstreamOption::BINARY);
Type value(0);
if (!ifs.good())
auto& iss = is.stdStream();
if (is.format() == IOstreamOption::BINARY)
{
FatalErrorInFunction
<< "Cannot read file " << ifs.name() << nl
<< exit(FatalError);
iss.read(reinterpret_cast<char*>(&value), sizeof(Type));
}
else
{
iss >> value;
}
is.syncState();
return value;
}
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
string buf(size_t(80), '\0');
iss.read(&buf[0], 80);
value.resize(80, '\0');
iss.read(&value[0], 80);
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
// Truncate at the first embedded '\0'
const auto endp = buf.find('\0');
const auto endp = value.find('\0');
if (endp != std::string::npos)
{
buf.erase(endp);
value.erase(endp);
}
// ASCII if it does not contain "C Binary"
if (!buf.contains("Binary") && !buf.contains("binary"))
// May have been padded with trailing spaces - remove those
stringOps::inplaceTrimRight(value);
is.syncState();
}
else
{
fmt = IOstreamOption::ASCII;
value.clear();
while (value.empty() && !is.eof())
{
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
)
:
IFstream(pathname, ensightReadFile::detectBinaryHeader(pathname))
{}
IFstream(pathname, IOstreamOption::BINARY), // Start as BINARY
timeStepFooterBegin_(-1)
{
init(true); // detectFormat = true
}
Foam::ensightReadFile::ensightReadFile
@ -90,12 +343,16 @@ Foam::ensightReadFile::ensightReadFile
IOstreamOption::streamFormat fmt
)
:
IFstream(pathname, fmt)
{}
IFstream(pathname, fmt),
timeStepFooterBegin_(-1)
{
init(false); // detectFormat = false
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
// Same as IFstream::readRaw(buf, count)
Foam::Istream& Foam::ensightReadFile::read
(
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)
{
if (format() == IOstreamOption::BINARY)
{
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);
}
}
readString(value);
return *this;
}
Foam::Istream& Foam::ensightReadFile::read(label& value)
{
int ivalue;
if (format() == IOstreamOption::BINARY)
{
read
(
reinterpret_cast<char*>(&ivalue),
sizeof(ivalue)
);
}
else
{
stdStream() >> ivalue;
syncState();
}
value = ivalue;
value = getPrimitive<int>(*this);
return *this;
}
Foam::Istream& Foam::ensightReadFile::read(float& value)
{
if (format() == IOstreamOption::BINARY)
{
read
(
reinterpret_cast<char*>(&value),
sizeof(value)
);
}
else
{
stdStream() >> value;
syncState();
}
value = getPrimitive<float>(*this);
return *this;
}
Foam::Istream& Foam::ensightReadFile::read(double& value)
{
float fvalue;
if (format() == IOstreamOption::BINARY)
{
read
(
reinterpret_cast<char*>(&fvalue),
sizeof(fvalue)
);
}
else
{
stdStream() >> fvalue;
syncState();
}
value = fvalue;
value = getPrimitive<float>(*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(buffer);
read(p.x());
}
for (auto& p : points)
{
read(p.y());
}
for (auto& p : points)
{
read(p.z());
}
}
return *this;
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;
}
if (debug)
{
Info<< "seek time "
<< timeIndex << '/' << nTimes()
<< " ignored" << nl;
}
return false;
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -27,9 +27,8 @@ Class
Foam::ensightReadFile
Description
A variant of IFstream with specialised read() for
strings, integers and floats.
Correctly handles binary read as well.
A variant of IFstream with specialised handling for Ensight reading
of strings, integers and floats (ASCII and BINARY).
\*---------------------------------------------------------------------------*/
@ -38,6 +37,7 @@ Description
#include "IFstream.H"
#include "IOstream.H"
#include "vector.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -52,9 +52,28 @@ class ensightReadFile
:
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
ensightReadFile(const ensightReadFile&) = delete;
@ -63,10 +82,20 @@ public:
void operator=(const ensightReadFile&) = delete;
public:
//- Debug switch
static int debug;
// Constructors
//- Construct from pathname, auto-detect the format
explicit ensightReadFile(const fileName& pathname);
//- Construct a geometry reader, auto-detecting the "C Binary" header
//- for binary files and skipping past it.
explicit ensightReadFile
(
const fileName& pathname
);
//- Construct from pathname, use the specified (ascii/binary) format
ensightReadFile
@ -82,11 +111,13 @@ public:
// Static Functions
//- Detect if the file is \em binary by testing for initial
//- "(C|Fortran) Binary"
static IOstreamOption::streamFormat detectBinaryHeader
//- Extract time step footer information (if any).
// \return the begin of footer position.
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
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;
//- Read floating-point as "%12.5e" or as binary
@ -113,8 +144,68 @@ public:
//- Read element keyword. Currently the same as read(string)
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
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2022 OpenCFD Ltd.
Copyright (C) 2021-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -125,6 +125,11 @@ void Foam::ensightFaMesh::write
bool parallel
) const
{
if (UPstream::master())
{
os.beginGeometry();
}
// Area meshes (currently only one)
// const label areaId = 0;
areaPart_.write(os, mesh_.mesh(), parallel);

View File

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

View File

@ -391,6 +391,11 @@ bool Foam::functionObjects::ensightCloudWriteObject::write()
// Generate a (non-moving) dummy geometry
// - ParaView ensight-reader needs this, and usually ensight does too
autoPtr<ensightGeoFile> os = ensCase().newGeometry(false);
if (os)
{
os->beginGeometry();
}
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
// if the simulation has mesh motion later on.
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();
{
if (!isDir(outputFile.path()))
if (!Foam::isDir(outputFile.path()))
{
mkDir(outputFile.path());
Foam::mkDir(outputFile.path());
}
const bool stateChanged =
@ -131,12 +131,12 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
);
// As per mkdir -p "data/00000000"
mkDir(dataDir);
Foam::mkDir(dataDir);
const fileName geomFile(baseDir/geometryName);
if (!exists(geomFile))
if (!Foam::exists(geomFile))
{
if (verbose_)
{
@ -151,6 +151,7 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
caseOpts_.format()
);
osGeom.beginGeometry();
writeGeometry(osGeom, elemOutput);
}
@ -176,7 +177,12 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
// Update case file
if (stateChanged)
{
OFstream osCase(outputFile, IOstreamOption::ASCII);
OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
if (verbose_)

View File

@ -91,9 +91,9 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
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
@ -110,6 +110,7 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
caseOpts_.format()
);
osGeom.beginGeometry();
writeGeometry(osGeom, elemOutput);
// Write field (serial only)
@ -118,7 +119,12 @@ Foam::fileName Foam::coordSetWriters::ensightWriter::writeUncollated
// Update case file
{
OFstream osCase(outputFile, IOstreamOption::ASCII);
OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
osCase

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2015-2022 OpenCFD Ltd.
Copyright (C) 2015-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -44,54 +44,57 @@ namespace Foam
namespace Foam
{
// Read and discard specified number of elements
template<class Type>
static inline void discard(label n, ensightReadFile& is)
// Extract timeset and fileset from split line information
// when the minElements has been satisfied.
// 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);
--n;
is.reset(split[0]);
is >> result.first();
if (split.size() > minElements)
{
is.reset(split[1]);
is >> result.second();
}
}
return result;
}
} // End namespace Foam
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
// * * * * * * * * * * Protected Static Member Functions * * * * * * * * * * //
void Foam::ensightSurfaceReader::skip(const label n, Istream& is) const
{
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
bool Foam::ensightSurfaceReader::readLine(ISstream& is, std::string& line)
{
do
{
is.getLine(line);
// Trim out any '#' comments
// Trim out any '#' comments (trailing or otherwise)
const auto pos = line.find('#');
if (pos != std::string::npos)
{
@ -100,6 +103,32 @@ void Foam::ensightSurfaceReader::readLine(ISstream& is, string& line) const
stringOps::inplaceTrimRight(line);
}
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,
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, '*');
// 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);
checkSection(expected, buffer, is);
}
return result;
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
Foam::Pair<Foam::ensightSurfaceReader::idTypes>
Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
Foam::ensightSurfaceReader::readGeometryHeader
(
ensightReadFile& is
) const
{
// Binary flag string if applicable
is.readBinaryHeader();
string buffer;
Pair<idTypes> idHandling(idTypes::NONE, idTypes::NONE);
@ -203,17 +201,17 @@ Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
// Optional extents - read and discard 6 floats
// (xmin, xmax, ymin, ymax, zmin, zmax)
discard<scalar>(6, is);
is.skip<scalar>(6);
// Part
// "part"
is.read(buffer);
DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
}
// The part number
label ivalue;
is.read(ivalue);
DebugInfo<< "ivalue: " << ivalue << nl;
label intValue;
is.read(intValue);
DebugInfo<< "part number: " << intValue << nl;
// Part description / name
is.read(buffer);
@ -231,6 +229,8 @@ void Foam::ensightSurfaceReader::readCase(ISstream& is)
{
DebugInFunction << endl;
enum ParseSection { UNKNOWN, FORMAT, GEOMETRY, VARIABLE, TIME, FILE };
if (!is.good())
{
FatalErrorInFunction
@ -239,99 +239,366 @@ void Foam::ensightSurfaceReader::readCase(ISstream& is)
}
string buffer;
SubStrings<string> split;
// Read the file
ParseSection parseState = ParseSection::UNKNOWN;
// FORMAT
// ~~~~~~
debugSection("FORMAT", is);
readLine(is, buffer); // type: ensight gold
parseState = ParseSection::FORMAT;
// GEOMETRY
// ~~~~~~~~
debugSection("GEOMETRY", is);
parseState = ParseSection::GEOMETRY;
do
{
readLine(is, buffer);
// GEOMETRY with any of these
// model: 1 xxx.0000.mesh
// model: xxx.0000.mesh
// model: data/directory/geometry
if (buffer.starts_with("VARIABLE"))
{
parseState = ParseSection::VARIABLE;
break;
}
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]]
//
// - use the last entry
meshFileName_ = stringOps::splitSpace(buffer).back().str();
// ====
// 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
DynamicList<word> fieldNames(16);
DynamicList<string> fieldFileNames(16);
DynamicList<labelPair> dynFieldTimesets(16);
DynamicList<word> dynFieldNames(16);
DynamicList<string> dynFieldFileNames(16);
// VARIABLE
// ~~~~~~~~
while (is.good())
{
readLine(is, buffer);
if (buffer == "TIME")
if (buffer.starts_with("TIME"))
{
parseState = ParseSection::TIME;
break;
}
// Read the field name and associated file name. Eg,
// scalar per element: 1 p data/********/p
// Read the field name and associated file name.
// 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
<< "Error reading field file name. Current buffer: "
<< "Error reading field file name, variable line: "
<< buffer << endl;
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();
for (const auto& s : parsed)
{
Info<< " " << s.str();
}
Info<< nl;
DebugInfo<< "ignore variable line: " << buffer << nl;
continue;
}
fieldNames.push_back(parsed[parsed.size()-2].str());
fieldFileNames.push_back(parsed.back().str());
DebugInfo<< "variable line: " << buffer << nl;
// 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);
fieldFileNames_.transfer(fieldFileNames);
fieldTimesets_.transfer(dynFieldTimesets);
fieldNames_.transfer(dynFieldNames);
fieldFileNames_.transfer(dynFieldFileNames);
DebugInfo
<< "fieldNames: " << fieldNames_ << nl
<< "fieldFileNames: " << fieldFileNames_ << nl;
// Start reading time information
readLine(is, buffer); // time set: <int>
readLine(is, buffer);
readFromLine(3, buffer, nTimeSteps_); // number of steps: <int>
readLine(is, buffer);
readFromLine(3, buffer, timeStartIndex_); // filename start number: <int>
readLine(is, buffer);
readFromLine(2, buffer, timeIncrement_); // filename increment: <int>
if (parseState != ParseSection::TIME)
{
FatalIOErrorInFunction(is)
<< "Did not find section header 'TIME'" << nl
<< exit(FatalIOError);
}
// 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
<< "nTimeSteps: " << nTimeSteps_ << nl
<< "timeStartIndex: " << timeStartIndex_ << nl
<< "timeIncrement: " << timeIncrement_ << nl;
<< "expect timesets: " << flatOutput(expectTimeset) << nl
<< "expect filesets: " << flatOutput(expectFileset) << nl;
// Read the time values
readLine(is, buffer); // time values:
timeValues_.resize_nocopy(nTimeSteps_);
for (label i = 0; i < nTimeSteps_; ++i)
// TIME
// ~~~~
// 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));
timeValues_[i].value() = t;
// TODO: use character representation of t directly instead of
// regenerating from scalar value
timeValues_[i].name() = Foam::name(t);
// time set: <int>
{
readLine(is, buffer);
}
// number of steps: <int>
label nTimes = 0;
{
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
baseDir_(fName.path()),
meshFileName_(),
fieldNames_(),
fieldFileNames_(),
nTimeSteps_(0),
meshTimeset_(-1,-1),
timeStartIndex_(0),
timeIncrement_(1),
timeValues_(),
surfPtr_(nullptr)
timeIncrement_(1)
{
if (options.getOrDefault("debug", false))
{
@ -376,12 +638,14 @@ Foam::ensightSurfaceReader::ensightSurfaceReader
Pstream::broadcasts
(
UPstream::worldComm,
meshTimeset_,
meshFileName_,
fieldTimesets_,
fieldNames_,
fieldFileNames_,
nTimeSteps_,
timeStartIndex_,
timeIncrement_,
fileNumbers_,
timeValues_
);
}
@ -392,7 +656,8 @@ Foam::ensightSurfaceReader::ensightSurfaceReader
Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
(
const fileName& geometryFile
const fileName& geometryFile,
const label timeIndex
)
{
DebugInFunction << endl;
@ -404,6 +669,9 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
// Format detected from the geometry
readFormat_ = is.format();
// For transient single-file
is.seekTime(timeIndex);
DebugInfo
<< "File: " << is.name()
<< " format: "
@ -433,52 +701,59 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
<< "Ignore " << nPoints << " node ids" << nl;
// Read and discard labels
discard<label>(nPoints, is);
is.skip<label>(nPoints);
}
pointField points(nPoints);
for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt)
{
for (point& p : points)
{
is.read(p[cmpt]);
}
}
pointField points;
is.readPoints(nPoints, points);
// Read faces - may be a mix of tria3, quad4, nsided
DynamicList<face> dynFaces(nPoints/3);
DynamicList<faceInfoTuple> faceTypeInfo(16);
string faceType;
label faceCount = 0;
string buffer;
while (is.good()) // (is.peek() != EOF)
while (is.good())
{
// The element type
is.read(faceType);
is.read(buffer);
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;
}
if
(
faceType
buffer
== 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
<< "faceType <" << faceType.c_str() << "> count: "
<< faceCount << nl;
<< "faceType <"
<< ensightFaces::elemNames[ensightFaces::elemType::TRIA3]
<< "> count: "
<< elemCount << nl;
if
(
@ -487,39 +762,45 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
)
{
DebugInfo
<< "Ignore " << faceCount << " element ids" << nl;
<< "Ignore " << elemCount << " element ids" << nl;
// 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)
{
is.read(fp);
}
dynFaces.push_back(std::move(f));
}
}
else if
(
faceType
buffer
== 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
<< "faceType <" << faceType.c_str() << "> count: "
<< faceCount << nl;
<< "faceType <"
<< ensightFaces::elemNames[ensightFaces::elemType::QUAD4]
<< "> count: "
<< elemCount << nl;
if
(
@ -528,39 +809,44 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
)
{
DebugInfo
<< "Ignore " << faceCount << " element ids" << nl;
<< "Ignore " << elemCount << " element ids" << nl;
// 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)
{
is.read(fp);
}
dynFaces.push_back(std::move(f));
}
}
else if
(
faceType
buffer
== 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
<< "faceType <" << faceType.c_str() << "> count: "
<< faceCount << nl;
<< "faceType <"
<< ensightFaces::elemNames[ensightFaces::elemType::NSIDED]
<< "> count: " << elemCount << nl;
if
(
@ -569,26 +855,31 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
)
{
DebugInfo
<< "Ignore " << faceCount << " element ids" << nl;
<< "Ignore " << elemCount << " element ids" << nl;
// Read and discard labels
discard<label>(faceCount, is);
is.skip<label>(elemCount);
}
labelList np(faceCount);
for (label facei = 0; facei < faceCount; ++facei)
// Extend and fill the new trailing portion
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)
{
is.read(fp);
}
dynFaces.push_back(std::move(f));
}
}
else
@ -596,7 +887,7 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
if (debug)
{
WarningInFunction
<< "Unknown face type: <" << faceType.c_str()
<< "Unknown face type: <" << buffer.c_str()
<< ">. Stopping read and continuing with current "
<< "elements only" << endl;
}
@ -605,7 +896,7 @@ Foam::meshedSurface Foam::ensightSurfaceReader::readGeometry
}
// From 1-based Ensight addressing to 0-based OF addressing
for (face& f : dynFaces)
for (auto& f : dynFaces)
{
for (label& fp : f)
{
@ -637,11 +928,15 @@ const Foam::meshedSurface& Foam::ensightSurfaceReader::geometry
surfPtr_.reset(new meshedSurface);
auto& surf = *surfPtr_;
fileName geomFile(baseDir_/replaceMask(meshFileName_, timeIndex));
fileName geomFile
(
baseDir_
/ ensightCase::expand_mask(meshFileName_, timeIndex)
);
if (!masterOnly_ || UPstream::master(UPstream::worldComm))
{
surf = readGeometry(geomFile);
surf = readGeometry(geomFile, timeIndex);
}
if (masterOnly_ && UPstream::parRun())

View File

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

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2015-2022 OpenCFD Ltd.
Copyright (C) 2015-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,41 +28,28 @@ License
#include "SpanStream.H"
#include "ensightPTraits.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * Protected Static Member Functions * * * * * * * * * * //
template<class Type>
void Foam::ensightSurfaceReader::readFromLine
void Foam::ensightSurfaceReader::readFrom
(
const label nSkip,
Istream& is,
const std::string& buffer,
Type& value
) const
)
{
skip(nSkip, is);
ISpanStream is(buffer.data(), buffer.size());
is >> value;
}
template<class Type>
void Foam::ensightSurfaceReader::readFromLine
(
const label nSkip,
const string& buffer,
Type& value
) const
{
ISpanStream is(buffer.data(), buffer.length());
readFromLine(nSkip, is, value);
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template<class Type>
Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
(
const fileName& dataFile,
const word& fieldName
const word& fieldName,
const label timeIndex
) const
{
auto tfield = tmp<Field<Type>>::New(surfPtr_->nFaces(), Zero);
@ -81,6 +68,10 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
<< exit(FatalError);
}
// If transient single-file
is.seekTime(timeIndex);
// Check that data type is as expected
// (assuming OpenFOAM generated the data set)
string primitiveType;
@ -104,11 +95,11 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
}
string strValue;
label iValue;
label intValue;
// Read header info: part index, e.g. part 1
is.read(strValue);
is.read(iValue);
is.read(intValue);
label begFace = 0;
@ -183,11 +174,18 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
}
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
(
baseDir_/replaceMask(fieldFileNames_[fieldIndex], fileIndex)
baseDir_
/ ensightCase::expand_mask(fieldFileNames_[fieldIndex], fileIndex)
);
if (debug)
@ -196,7 +194,7 @@ Foam::tmp<Foam::Field<Type>> Foam::ensightSurfaceReader::readField
<< 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 (!isDir(outputFile.path()))
if (!Foam::isDir(outputFile.path()))
{
mkDir(outputFile.path());
Foam::mkDir(outputFile.path());
}
const bool stateChanged =
@ -138,7 +138,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
);
// As per mkdir -p "data/00000000"
mkDir(dataDir);
Foam::mkDir(dataDir);
const fileName geomFile(baseDir/geometryName);
@ -151,7 +151,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
geomFile.name()
);
if (!exists(geomFile))
if (!Foam::exists(geomFile))
{
if (verbose_)
{
@ -165,6 +165,8 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
geomFile.name(),
caseOpts_.format()
);
osGeom.beginGeometry();
part.write(osGeom); // serial
}
@ -190,7 +192,12 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
// Update case file
if (stateChanged)
{
OFstream osCase(outputFile, IOstreamOption::ASCII);
OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
if (verbose_)

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2014 OpenFOAM Foundation
Copyright (C) 2015-2023 OpenCFD Ltd.
Copyright (C) 2015-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -64,11 +64,20 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
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
(
outputDir,
@ -76,16 +85,16 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
caseOpts_.format()
);
ensightOutputSurface part
(
surf.points(),
surf.faces(),
osGeom.name().name()
);
osGeom.beginGeometry();
part.write(osGeom); // serial
// Update case file
OFstream osCase(outputFile, IOstreamOption::ASCII);
OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
osCase
@ -168,11 +177,19 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
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
ensightGeoFile osGeom
(
@ -187,13 +204,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
caseOpts_.format()
);
// Ensight Geometry
ensightOutputSurface part
(
surf.points(),
surf.faces(),
osGeom.name().name()
);
osGeom.beginGeometry();
part.write(osGeom); // serial
// Write field (serial)
@ -204,7 +215,12 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
// Update case file
{
OFstream osCase(outputFile, IOstreamOption::ASCII);
OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
osCase