/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-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 .
\*---------------------------------------------------------------------------*/
#include "ensightReadFile.H"
#include "stringOps.H"
#include "defineDebugSwitch.H"
#include "registerSwitch.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
defineDebugSwitchWithName(Foam::ensightReadFile, "ensightReadFile", 0);
registerDebugSwitchWithName(Foam::ensightReadFile, ensight, "ensightReadFile");
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Get integers, floats etc in binary or ascii.
template
static inline Type getPrimitive(IFstream& is)
{
Type value(0);
auto& iss = is.stdStream();
if (is.format() == IOstreamOption::BINARY)
{
iss.read(reinterpret_cast(&value), sizeof(Type));
}
else
{
iss >> value;
}
is.syncState();
return value;
}
// 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
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
// Truncate at the first embedded '\0'
const auto endp = value.find('\0');
if (endp != std::string::npos)
{
value.erase(endp);
}
// May have been padded with trailing spaces - remove those
stringOps::inplaceTrimRight(value);
is.syncState();
}
else
{
value.clear();
while (value.empty() && !is.eof())
{
is.getLine(value);
}
}
}
// * * * * * * * * * * * * * 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& 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(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(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(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();
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::ensightReadFile::ensightReadFile
(
const fileName& pathname
)
:
IFstream(pathname, IOstreamOption::BINARY), // Start as BINARY
timeStepFooterBegin_(-1)
{
init(true); // detectFormat = true
}
Foam::ensightReadFile::ensightReadFile
(
const fileName& pathname,
IOstreamOption::streamFormat 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,
std::streamsize count
)
{
stdStream().read(buf, count);
syncState();
return *this;
}
// TBD
// Foam::Istream& Foam::ensightReadFile::read(word& value)
// {
// readString(value);
// string::stripInvalid(value);
// return *this;
// }
Foam::Istream& Foam::ensightReadFile::read(string& value)
{
readString(value);
return *this;
}
Foam::Istream& Foam::ensightReadFile::read(label& value)
{
value = getPrimitive(*this);
return *this;
}
Foam::Istream& Foam::ensightReadFile::read(float& value)
{
value = getPrimitive(*this);
return *this;
}
Foam::Istream& Foam::ensightReadFile::read(double& value)
{
value = getPrimitive(*this);
return *this;
}
Foam::Istream& Foam::ensightReadFile::readKeyword(string& key)
{
read(key);
return *this;
}
void Foam::ensightReadFile::readPoints
(
const label nPoints,
List& 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());
}
}
void Foam::ensightReadFile::readPoints
(
const label nPoints,
List& 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;
}
// ************************************************************************* //