mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: vtk::seriesWriter to encapsulate writing file series (issue #926)
This commit is contained in:
3
applications/test/vtkSeriesWriter/Make/files
Normal file
3
applications/test/vtkSeriesWriter/Make/files
Normal file
@ -0,0 +1,3 @@
|
||||
Test-vtkSeriesWriter.C
|
||||
|
||||
EXE = $(FOAM_APPBIN)/Test-vtkSeriesWriter
|
||||
5
applications/test/vtkSeriesWriter/Make/options
Normal file
5
applications/test/vtkSeriesWriter/Make/options
Normal file
@ -0,0 +1,5 @@
|
||||
EXE_INC = \
|
||||
-I$(LIB_SRC)/fileFormats/lnInclude
|
||||
|
||||
EXE_LIBS = \
|
||||
-lfileFormats
|
||||
118
applications/test/vtkSeriesWriter/Test-vtkSeriesWriter.C
Normal file
118
applications/test/vtkSeriesWriter/Test-vtkSeriesWriter.C
Normal file
@ -0,0 +1,118 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
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-vtkSeriesWriter
|
||||
|
||||
Description
|
||||
Basic functionality tests for vtk::seriesWriter
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "foamVtkSeriesWriter.H"
|
||||
#include "argList.H"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
argList::addBoolOption("sort", "Sort value / name");
|
||||
argList::addBoolOption("check", "Check for existence of files");
|
||||
argList::addOption("time", "value", "Filter based on given time");
|
||||
argList::addOption("scan", "series", "Scan directory to create series");
|
||||
|
||||
argList args(argc, argv, false, true);
|
||||
|
||||
const scalar currTime = args.lookupOrDefault<scalar>("time", GREAT);
|
||||
|
||||
Info<< "Using currTime = " << currTime << nl
|
||||
<< "when loading " << (args.size()-1) << " files" << nl << nl;
|
||||
|
||||
for (label argi=1; argi < args.size(); ++argi)
|
||||
{
|
||||
const auto& input = args[argi];
|
||||
|
||||
Info << "load from " << input << nl;
|
||||
|
||||
vtk::seriesWriter writer;
|
||||
writer.load(input);
|
||||
|
||||
writer.print(Info);
|
||||
Info<< nl << nl;
|
||||
|
||||
if (writer.removeNewer(currTime))
|
||||
{
|
||||
Info<< "removed entries with time >= " << currTime << nl;
|
||||
writer.print(Info);
|
||||
Info<< nl << nl;
|
||||
}
|
||||
|
||||
if (args.found("sort"))
|
||||
{
|
||||
writer.sort();
|
||||
|
||||
Info<< "sorted" << nl;
|
||||
writer.print(Info);
|
||||
Info<< nl << nl;
|
||||
}
|
||||
|
||||
if (args.found("check"))
|
||||
{
|
||||
writer.load(input, true);
|
||||
|
||||
Info<< "reload, checking the existance of files" << nl;
|
||||
writer.print(Info);
|
||||
Info<< nl << nl;
|
||||
}
|
||||
|
||||
if (writer.empty())
|
||||
{
|
||||
Info<< "No entries" << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< writer.size() << " entries" << nl;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.found("scan"))
|
||||
{
|
||||
vtk::seriesWriter writer;
|
||||
|
||||
writer.scan(args.opt<fileName>("scan"));
|
||||
|
||||
Info<< "scanned for files" << nl;
|
||||
writer.print(Info);
|
||||
Info<< nl << nl;
|
||||
}
|
||||
|
||||
|
||||
Info<< "\nEnd\n" << nl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
4
applications/test/vtkSeriesWriter/file.vtm
Normal file
4
applications/test/vtkSeriesWriter/file.vtm
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version='1.0'?>
|
||||
<!-- case='abc' time='0' index='0' -->
|
||||
<VTKFile type='vtkMultiBlockDataSet' version='1.0' byte_order='LittleEndian' header_type='UInt64'>
|
||||
<vtkMultiBlockDataSet>
|
||||
4
applications/test/vtkSeriesWriter/file_00000679.vtm
Normal file
4
applications/test/vtkSeriesWriter/file_00000679.vtm
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version='1.0'?>
|
||||
<!-- case='abc' time='0.00125' index='679' -->
|
||||
<VTKFile type='vtkMultiBlockDataSet' version='1.0' byte_order='LittleEndian' header_type='UInt64'>
|
||||
<vtkMultiBlockDataSet>
|
||||
4
applications/test/vtkSeriesWriter/file_00001025.vtm
Normal file
4
applications/test/vtkSeriesWriter/file_00001025.vtm
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version='1.0'?>
|
||||
<!-- case='abc' time="0.5250" index='1025' -->
|
||||
<VTKFile type='vtkMultiBlockDataSet' version='1.0' byte_order='LittleEndian' header_type='UInt64'>
|
||||
<vtkMultiBlockDataSet>
|
||||
4
applications/test/vtkSeriesWriter/file_00001234.vtm
Normal file
4
applications/test/vtkSeriesWriter/file_00001234.vtm
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version='1.0'?>
|
||||
<!-- note="no time entry" -->
|
||||
<VTKFile type='vtkMultiBlockDataSet' version='1.0' byte_order='LittleEndian' header_type='UInt64'>
|
||||
<vtkMultiBlockDataSet>
|
||||
4
applications/test/vtkSeriesWriter/file_00005680.vtm
Normal file
4
applications/test/vtkSeriesWriter/file_00005680.vtm
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version='1.0'?>
|
||||
<!-- case='abc' time= index='1234' note="degenerate time entry" -->
|
||||
<VTKFile type='vtkMultiBlockDataSet' version='1.0' byte_order='LittleEndian' header_type='UInt64'>
|
||||
<vtkMultiBlockDataSet>
|
||||
4
applications/test/vtkSeriesWriter/file_23.vtm
Normal file
4
applications/test/vtkSeriesWriter/file_23.vtm
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version='1.0'?>
|
||||
<!-- case='abc' time=0.0145 index='1234' note="unquoted time entry" -->
|
||||
<VTKFile type='vtkMultiBlockDataSet' version='1.0' byte_order='LittleEndian' header_type='UInt64'>
|
||||
<vtkMultiBlockDataSet>
|
||||
20
applications/test/vtkSeriesWriter/test1.vtm.series
Normal file
20
applications/test/vtkSeriesWriter/test1.vtm.series
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"file-series-version" : "1.0",
|
||||
"files": [
|
||||
{ "name" : "file_00001742.vtm", "time" : 50 },
|
||||
{ "name" : "file_00001025.vtm", "time" : 30 },
|
||||
{
|
||||
"time" : 40,
|
||||
"name" : "file_00001380.vtm", ,,,,
|
||||
},
|
||||
{ "name" : "file_00000679.vtm", "time" : 20 },
|
||||
{ "name" : "file_00002110.vtm", "time" : 60 },
|
||||
{ "name" : "file_00001234.vtm", "time" : 60 },
|
||||
{ "name" : "file_00001742.vtm", "time" : 150 },
|
||||
{ "name" : "file_00001742.vtm", "time" : 5 },
|
||||
|
||||
{ "name" : "file_1000.vtm", "badTime" : 60 },
|
||||
{ "" : "", "" : 60, "" : false },
|
||||
{ "name" : "", "time" : 10 },
|
||||
]
|
||||
}
|
||||
@ -17,6 +17,7 @@ stl/STLAsciiParseManual.C
|
||||
stl/STLAsciiParseRagel.C
|
||||
|
||||
vtk/file/foamVtkFileWriter.C
|
||||
vtk/file/foamVtkSeriesWriter.C
|
||||
vtk/file/foamVtmWriter.C
|
||||
vtk/core/foamVtkCore.C
|
||||
vtk/core/foamVtkPTraits.C
|
||||
|
||||
753
src/fileFormats/vtk/file/foamVtkSeriesWriter.C
Normal file
753
src/fileFormats/vtk/file/foamVtkSeriesWriter.C
Normal file
@ -0,0 +1,753 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
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/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "foamVtkSeriesWriter.H"
|
||||
#include "Fstream.H"
|
||||
#include "ListOps.H"
|
||||
#include "stringOpsSort.H"
|
||||
#include "OSspecific.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
// Get any single token.
|
||||
static inline bool getToken(ISstream& is, token& tok)
|
||||
{
|
||||
return (!is.read(tok).bad() && tok.good());
|
||||
}
|
||||
|
||||
// Get two tokens.
|
||||
// The first one must be a ':' token, the second one is any value
|
||||
//
|
||||
// This corrsponds to the JSON "key" : value syntax,
|
||||
// we trigger after reading the "key".
|
||||
static inline bool getValueToken(ISstream& is, token& tok)
|
||||
{
|
||||
return
|
||||
(
|
||||
// Token 1 = ':' separator
|
||||
(
|
||||
getToken(is, tok)
|
||||
&& tok.isPunctuation() && tok.pToken() == token::COLON
|
||||
)
|
||||
|
||||
// Token 2 is the value
|
||||
&& getToken(is, tok)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Sorting for fileNameInstant
|
||||
// 1. sort by value (time)
|
||||
// 2. natural sort (name)
|
||||
struct seriesLess
|
||||
{
|
||||
bool operator()(const fileNameInstant a, const fileNameInstant b) const
|
||||
{
|
||||
scalar val = compareOp<scalar>()(a.value(), b.value());
|
||||
if (val == 0)
|
||||
{
|
||||
return
|
||||
stringOps::natural_sort::compare(a.name(), b.name()) < 0;
|
||||
}
|
||||
return val < 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Check if value is less than upper, with some tolerance.
|
||||
static inline bool lessThan(const scalar& val, const scalar& upper)
|
||||
{
|
||||
return (val < upper && Foam::mag(val - upper) > ROOTVSMALL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
|
||||
|
||||
Foam::fileName Foam::vtk::seriesWriter::base
|
||||
(
|
||||
const fileName& outputName,
|
||||
char sep
|
||||
)
|
||||
{
|
||||
const auto dash = outputName.rfind(sep);
|
||||
|
||||
// No separator? Or separator in path() instead of name()?
|
||||
if
|
||||
(
|
||||
std::string::npos == dash
|
||||
|| std::string::npos != outputName.find('/', dash)
|
||||
)
|
||||
{
|
||||
// Warn?
|
||||
return outputName;
|
||||
}
|
||||
|
||||
const auto dot = outputName.find('.', dash);
|
||||
|
||||
if (std::string::npos == dot)
|
||||
{
|
||||
return outputName.substr(0, dash);
|
||||
}
|
||||
|
||||
return outputName.substr(0, dash) + outputName.substr(dot);
|
||||
}
|
||||
|
||||
|
||||
Foam::word Foam::vtk::seriesWriter::suffix
|
||||
(
|
||||
const fileName& file,
|
||||
char sep
|
||||
)
|
||||
{
|
||||
const auto dash = file.rfind(sep);
|
||||
|
||||
// No separator? Or separator in path() instead of name()?
|
||||
if
|
||||
(
|
||||
std::string::npos == dash
|
||||
|| std::string::npos != file.find('/', dash)
|
||||
)
|
||||
{
|
||||
// Warn?
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto dot = file.find('.', dash);
|
||||
|
||||
if (std::string::npos == dot)
|
||||
{
|
||||
return file.substr(dash);
|
||||
}
|
||||
|
||||
return file.substr(dash, (dot-dash));
|
||||
}
|
||||
|
||||
|
||||
Foam::Ostream& Foam::vtk::seriesWriter::print
|
||||
(
|
||||
Ostream& os,
|
||||
const fileName& base,
|
||||
const UList<instant>& series,
|
||||
const char sep
|
||||
)
|
||||
{
|
||||
// Split the base into (stem, ext) components
|
||||
//
|
||||
// base = "path/file.vtm"
|
||||
//
|
||||
// stem = "file"
|
||||
// ext = ".vtm"
|
||||
|
||||
const word stem = base.nameLessExt();
|
||||
const word ext = "." + base.ext();
|
||||
|
||||
// Begin file-series (JSON)
|
||||
os << "{\n \"file-series-version\" : \"1.0\",\n \"files\" : [\n";
|
||||
|
||||
// Track how many entries are remaining
|
||||
// - trailing commas on all but the final entry (JSON requirement)
|
||||
label nremain = series.size();
|
||||
|
||||
// Each entry
|
||||
// { "name" : "<stem><sep>name<ext>", "time" : value }
|
||||
|
||||
for (const instant& inst : series)
|
||||
{
|
||||
os << " { \"name\" : \""
|
||||
<< stem << sep << inst.name() << ext
|
||||
<< "\", \"time\" : " << inst.value() << " }";
|
||||
|
||||
if (--nremain)
|
||||
{
|
||||
os << ',';
|
||||
}
|
||||
os << nl;
|
||||
}
|
||||
|
||||
os << " ]\n}\n";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
Foam::Ostream& Foam::vtk::seriesWriter::print
|
||||
(
|
||||
Ostream& os,
|
||||
const UList<fileNameInstant>& series
|
||||
)
|
||||
{
|
||||
// Begin file-series (JSON)
|
||||
os << "{\n \"file-series-version\" : \"1.0\",\n \"files\" : [\n";
|
||||
|
||||
// Track how many entries are remaining
|
||||
// - trailing commas on all but the final entry (JSON requirement)
|
||||
label nremain = series.size();
|
||||
|
||||
// Each entry
|
||||
// { "name" : "<file>", "time" : <value> }
|
||||
|
||||
for (const fileNameInstant& inst : series)
|
||||
{
|
||||
os << " { \"name\" : \""
|
||||
<< inst.name().name()
|
||||
<< "\", \"time\" : " << inst.value() << " }";
|
||||
|
||||
if (--nremain)
|
||||
{
|
||||
os << ',';
|
||||
}
|
||||
os << nl;
|
||||
}
|
||||
|
||||
os << " ]\n}\n";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
void Foam::vtk::seriesWriter::write
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const UList<instant>& series,
|
||||
const char sep
|
||||
)
|
||||
{
|
||||
mkDir(seriesName.path());
|
||||
|
||||
autoPtr<OFstream> osPtr =
|
||||
(
|
||||
seriesName.hasExt("series")
|
||||
? autoPtr<OFstream>::New(seriesName)
|
||||
: autoPtr<OFstream>::New(seriesName + ".series")
|
||||
);
|
||||
|
||||
print(*osPtr, seriesName, series, sep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Foam::vtk::seriesWriter::write
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const UList<fileNameInstant>& series
|
||||
)
|
||||
{
|
||||
mkDir(seriesName.path());
|
||||
|
||||
autoPtr<OFstream> osPtr =
|
||||
(
|
||||
seriesName.hasExt("series")
|
||||
? autoPtr<OFstream>::New(seriesName)
|
||||
: autoPtr<OFstream>::New(seriesName + ".series")
|
||||
);
|
||||
|
||||
print(*osPtr, series);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
bool Foam::vtk::seriesWriter::appendCheck(fileNameInstant inst)
|
||||
{
|
||||
if (inst.name().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto iter = existing_.find(inst.name());
|
||||
|
||||
if (iter.found())
|
||||
{
|
||||
for (fileNameInstant& dst : entries_)
|
||||
{
|
||||
if (dst.name() == inst.name())
|
||||
{
|
||||
// Replace value
|
||||
dst.value() = inst.value();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries_.append(inst);
|
||||
existing_.insert(inst.name());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::vtk::seriesWriter::removeDuplicates()
|
||||
{
|
||||
const label nElem = entries_.size();
|
||||
|
||||
HashTable<label, fileName> filesSeen(2*nElem);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
for (label elemi=0; elemi < nElem; ++elemi)
|
||||
{
|
||||
fileNameInstant& inst = entries_[elemi];
|
||||
|
||||
if (inst.name().empty())
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iter = filesSeen.find(inst.name());
|
||||
|
||||
if (iter.found())
|
||||
{
|
||||
// Mark previous location as being superseded
|
||||
entries_[*iter].name().clear();
|
||||
changed = true;
|
||||
|
||||
*iter = elemi; // The latest with this name
|
||||
}
|
||||
else
|
||||
{
|
||||
filesSeen.insert(inst.name(), elemi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (changed)
|
||||
{
|
||||
label dsti = 0;
|
||||
for (label elemi=0; elemi < nElem; ++elemi)
|
||||
{
|
||||
fileNameInstant& src = entries_[elemi];
|
||||
|
||||
if (!src.name().empty())
|
||||
{
|
||||
if (dsti != elemi)
|
||||
{
|
||||
entries_[dsti] = std::move(src);
|
||||
}
|
||||
++dsti;
|
||||
}
|
||||
}
|
||||
|
||||
entries_.resize(dsti);
|
||||
}
|
||||
|
||||
return (nElem != entries_.size());
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
Foam::label Foam::vtk::seriesWriter::load
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const bool checkFiles,
|
||||
const scalar restartTime
|
||||
)
|
||||
{
|
||||
clear();
|
||||
|
||||
fileName seriesFile(seriesName);
|
||||
if (!seriesFile.hasExt("series"))
|
||||
{
|
||||
seriesFile.ext("series");
|
||||
}
|
||||
|
||||
if (!isFile(seriesFile))
|
||||
{
|
||||
return size();
|
||||
}
|
||||
|
||||
HashSet<fileName> filesOnDisk;
|
||||
|
||||
if (checkFiles)
|
||||
{
|
||||
filesOnDisk.insert(Foam::readDir(seriesFile.path()));
|
||||
}
|
||||
|
||||
|
||||
// Parse JSON content:
|
||||
//
|
||||
// {
|
||||
// "file-series-version" : "1.0",
|
||||
// "files" : [
|
||||
// { "name" : "abc", "time" : 123 },
|
||||
// { "name" : "def", "time" : 345 }
|
||||
// ]
|
||||
// }
|
||||
|
||||
// Parsing states
|
||||
enum parse
|
||||
{
|
||||
NONE, // Looking for "files"
|
||||
FILES_ARRAY, // Saw "file" : '['
|
||||
ENTRY, // Parsing in { "name" : ... }
|
||||
DONE, // Saw a ']' while in FILES_ARRAY
|
||||
FAIL // Something bad happened
|
||||
};
|
||||
|
||||
// Track if "file" and "time" keys have been located
|
||||
unsigned instStatus = 0;
|
||||
fileNameInstant inst;
|
||||
|
||||
token tok;
|
||||
|
||||
IFstream is(seriesFile);
|
||||
|
||||
for
|
||||
(
|
||||
parse state = parse::NONE;
|
||||
(state != parse::DONE && state != parse::FAIL)
|
||||
&& getToken(is, tok);
|
||||
/*nil*/
|
||||
)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
// Still scanning for initial "files" entry
|
||||
case parse::NONE :
|
||||
{
|
||||
if (tok.isString() && tok.stringToken() == "files")
|
||||
{
|
||||
// Expect "files" : [ ...
|
||||
|
||||
if
|
||||
(
|
||||
getValueToken(is, tok)
|
||||
&& tok.isPunctuation()
|
||||
&& tok.pToken() == token::BEGIN_SQR
|
||||
)
|
||||
{
|
||||
state = parse::FILES_ARRAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = parse::FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Parsing entries within "files" array
|
||||
case parse::FILES_ARRAY :
|
||||
{
|
||||
if (tok.isPunctuation())
|
||||
{
|
||||
switch (tok.pToken())
|
||||
{
|
||||
// ',' - keep going (another entry)
|
||||
case token::COMMA :
|
||||
break;
|
||||
|
||||
// '{' - begin entry
|
||||
case token::BEGIN_BLOCK :
|
||||
state = parse::ENTRY;
|
||||
instStatus = 0;
|
||||
break;
|
||||
|
||||
// ']' - done array
|
||||
case token::END_SQR :
|
||||
state = parse::DONE;
|
||||
break;
|
||||
|
||||
default:
|
||||
state = parse::FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = parse::FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Parsing an individual entry within "files"
|
||||
case parse::ENTRY :
|
||||
{
|
||||
if (tok.isPunctuation())
|
||||
{
|
||||
switch (tok.pToken())
|
||||
{
|
||||
// ',' - keep going (another key/value pair)
|
||||
case token::COMMA :
|
||||
break;
|
||||
|
||||
// '}'
|
||||
case token::END_BLOCK :
|
||||
{
|
||||
// Verify instant was properly parsed and
|
||||
// is also valid
|
||||
if
|
||||
(
|
||||
instStatus == 0x03
|
||||
&& lessThan(inst.value(), restartTime)
|
||||
&&
|
||||
(
|
||||
checkFiles
|
||||
? filesOnDisk.found(inst.name())
|
||||
: true
|
||||
)
|
||||
)
|
||||
{
|
||||
appendCheck(inst);
|
||||
}
|
||||
|
||||
state = parse::FILES_ARRAY;
|
||||
instStatus = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
state = parse::FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (tok.isString())
|
||||
{
|
||||
// Expect "key" : value
|
||||
|
||||
const string key(tok.stringToken());
|
||||
|
||||
if (getValueToken(is, tok))
|
||||
{
|
||||
if ("name" == key)
|
||||
{
|
||||
if (tok.isString())
|
||||
{
|
||||
inst.name() = tok.stringToken();
|
||||
instStatus |= 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = parse::FAIL;
|
||||
}
|
||||
}
|
||||
else if ("time" == key)
|
||||
{
|
||||
if (tok.isNumber())
|
||||
{
|
||||
inst.value() = tok.number();
|
||||
instStatus |= 0x02;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = parse::FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = parse::FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = parse::FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::vtk::seriesWriter::scan
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const scalar restartTime
|
||||
)
|
||||
{
|
||||
clear();
|
||||
|
||||
const fileName path = seriesName.path();
|
||||
|
||||
if (!isDir(path))
|
||||
{
|
||||
return size();
|
||||
}
|
||||
|
||||
fileName seriesFile(seriesName);
|
||||
|
||||
if (seriesName.hasExt("series"))
|
||||
{
|
||||
seriesFile.removeExt();
|
||||
}
|
||||
|
||||
const word stem = seriesFile.nameLessExt();
|
||||
const word ext = seriesFile.ext();
|
||||
|
||||
// Accept "fileN.ext", "fileNN.ext", but reject "file.ext"
|
||||
const auto minLen = stem.length() + ext.length() + 1;
|
||||
|
||||
const auto acceptName =
|
||||
[=](const fileName& file) -> bool
|
||||
{
|
||||
return
|
||||
(
|
||||
minLen < file.length()
|
||||
&& file.hasExt(ext) && file.startsWith(stem)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
fileNameList files = subsetList(Foam::readDir(path), acceptName);
|
||||
|
||||
// Names sorted so warnings appear less random
|
||||
Foam::sort(files, stringOps::natural_sort());
|
||||
|
||||
// Scratch space for reading some of the file
|
||||
std::string header;
|
||||
|
||||
scalar timeValue;
|
||||
|
||||
bool warnings = false;
|
||||
|
||||
for (const fileName& file : files)
|
||||
{
|
||||
std::ifstream is(path/file);
|
||||
|
||||
if (!is)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read directly into the string
|
||||
// 1024 (12 lines of 80 chars) is plenty for all comments
|
||||
|
||||
header.resize(1024);
|
||||
is.read(&(header.front()), header.size());
|
||||
header.resize(is.gcount());
|
||||
|
||||
// DebugInfo
|
||||
// << "got header:\n=====\n" << header << "\n=====\n" << nl;
|
||||
|
||||
|
||||
// Look for time="...", time='...', or even time=... attribute
|
||||
|
||||
auto begAttr = header.find("time=");
|
||||
|
||||
if (string::npos == begAttr)
|
||||
{
|
||||
if (!warnings)
|
||||
{
|
||||
Info<< "No 'time=' comment attribute found:\n(" << nl;
|
||||
warnings = true;
|
||||
}
|
||||
Info<< " " << file << nl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip past the 'time='
|
||||
begAttr += 5;
|
||||
const char quote = header[begAttr];
|
||||
|
||||
// Info<< "have time=" << int(begAttr) << nl;
|
||||
|
||||
auto endAttr =
|
||||
(
|
||||
(quote == '"' || quote == '\'')
|
||||
?
|
||||
// Quoted
|
||||
header.find(quote, ++begAttr)
|
||||
:
|
||||
// Unquoted
|
||||
header.find_first_of("\t\n\v\f\r ", begAttr)
|
||||
);
|
||||
|
||||
|
||||
if
|
||||
(
|
||||
string::npos != endAttr && begAttr < endAttr
|
||||
&& readScalar
|
||||
(
|
||||
header.substr(begAttr, endAttr-begAttr),
|
||||
timeValue
|
||||
)
|
||||
&& lessThan(timeValue, restartTime)
|
||||
)
|
||||
{
|
||||
// Success
|
||||
append(timeValue, file);
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings)
|
||||
{
|
||||
Info<< ")" << nl << nl;
|
||||
}
|
||||
|
||||
// Don't trust the order. Sort by time and name instead.
|
||||
this->sort();
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
|
||||
bool Foam::vtk::seriesWriter::removeNewer(const scalar timeValue)
|
||||
{
|
||||
// Rebuild hash as side-effect
|
||||
existing_.clear();
|
||||
|
||||
label dsti = 0;
|
||||
|
||||
const label nElem = entries_.size();
|
||||
|
||||
for (label elemi=0; elemi < nElem; ++elemi)
|
||||
{
|
||||
fileNameInstant& src = entries_[elemi];
|
||||
|
||||
if (!src.name().empty() && lessThan(src.value(), timeValue))
|
||||
{
|
||||
if (dsti != elemi)
|
||||
{
|
||||
entries_[dsti] = std::move(src);
|
||||
existing_.insert(entries_[dsti].name());
|
||||
}
|
||||
++dsti;
|
||||
}
|
||||
}
|
||||
|
||||
entries_.resize(dsti);
|
||||
|
||||
return (nElem != entries_.size());
|
||||
}
|
||||
|
||||
|
||||
void Foam::vtk::seriesWriter::sort()
|
||||
{
|
||||
Foam::sort(entries_, seriesLess());
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
276
src/fileFormats/vtk/file/foamVtkSeriesWriter.H
Normal file
276
src/fileFormats/vtk/file/foamVtkSeriesWriter.H
Normal file
@ -0,0 +1,276 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
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/>.
|
||||
|
||||
Class
|
||||
Foam::vtk::seriesWriter
|
||||
|
||||
Description
|
||||
Provides a means of accumulating and generating VTK file series.
|
||||
|
||||
The VTK file series format is a simple JSON format with the following
|
||||
type of content:
|
||||
\verbatim
|
||||
{
|
||||
"file-series-version" : "1.0",
|
||||
"files": [
|
||||
{ "name" : "file1.vtk", "time" : 10 },
|
||||
{ "name" : "file2.vtk", "time" : 20 },
|
||||
{ "name" : "file3.vtk", "time" : 30 },
|
||||
]
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
The append() operations include various sanity checks.
|
||||
Entries with an empty name are ignored.
|
||||
If an entry with an identical name already exists, its place
|
||||
will be overwritten with the new time value.
|
||||
|
||||
SourceFiles
|
||||
foamVtkSeriesWriter.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef foamVtkSeriesWriter_H
|
||||
#define foamVtkSeriesWriter_H
|
||||
|
||||
#include <fstream>
|
||||
#include "foamVtkOutputOptions.H"
|
||||
#include "instant.H"
|
||||
#include "fileNameInstant.H"
|
||||
#include "DynamicList.H"
|
||||
#include "HashSet.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace vtk
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class vtk::seriesWriter Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class seriesWriter
|
||||
{
|
||||
// Private Member Data
|
||||
|
||||
//- A list of file/time entries
|
||||
DynamicList<fileNameInstant> entries_;
|
||||
|
||||
//- Hash of existing (known) file names
|
||||
HashSet<fileName> existing_;
|
||||
|
||||
//- Append the specified file/time instant.
|
||||
// Overwrites existing entry that has the same name,
|
||||
// does not append empty names.
|
||||
bool appendCheck(fileNameInstant inst);
|
||||
|
||||
//- Remove duplicate filename entries. Keeping the last one seen.
|
||||
bool removeDuplicates();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct an empty series
|
||||
seriesWriter() = default;
|
||||
|
||||
//- Copy construct
|
||||
seriesWriter(const seriesWriter&) = default;
|
||||
|
||||
//- Move construct
|
||||
seriesWriter(seriesWriter&&) = default;
|
||||
|
||||
//- Copy assignment
|
||||
seriesWriter& operator=(const seriesWriter&) = default;
|
||||
|
||||
//- Move assignment
|
||||
seriesWriter& operator=(seriesWriter&&) = default;
|
||||
|
||||
|
||||
//- Destructor
|
||||
~seriesWriter() = default;
|
||||
|
||||
|
||||
// Static Member Functions
|
||||
|
||||
//- Extract the base name for a file series
|
||||
//
|
||||
// \param outputName The name of the data output file
|
||||
// Eg, "somefile_0001.vtk" would extract to "somefile.vtk"
|
||||
// \param sep The separator used between file stem and suffix.
|
||||
static fileName base(const fileName& outputName, char sep = '_');
|
||||
|
||||
//- Extract the time-varying ending of files
|
||||
//
|
||||
// \param file The name of the file
|
||||
// Eg, "somefile_0001.vtk" would extract to "0001"
|
||||
// \param sep The separator used between file stem and suffix.
|
||||
static word suffix(const fileName& file, char sep = '_');
|
||||
|
||||
//- Print file series (JSON format) for specified time instances
|
||||
//
|
||||
// \param os The output stream
|
||||
// \param base The name for the series (eg, "path/file.vtk")
|
||||
// \param series The list of suffix/value entries
|
||||
// \param sep The separator used between file stem and suffix.
|
||||
static Ostream& print
|
||||
(
|
||||
Ostream& os,
|
||||
const fileName& seriesName,
|
||||
const UList<instant>& series,
|
||||
const char sep = '_'
|
||||
);
|
||||
|
||||
//- Write file series (JSON format) to disk, for specified instances
|
||||
//
|
||||
// \param base The name for the series (eg, "path/file.vtk")
|
||||
// \param series The list of suffix/value entries
|
||||
// \param sep The separator used between file stem and suffix.
|
||||
static void write
|
||||
(
|
||||
const fileName& base,
|
||||
const UList<instant>& series,
|
||||
const char sep = '_'
|
||||
);
|
||||
|
||||
//- Print file series (JSON format) for specified time instances.
|
||||
// Since the VTK file series does not currently (OCT-2018) support
|
||||
// sub-directories, these will be stripped on output.
|
||||
//
|
||||
// \param os The output stream
|
||||
// \param series The list of filename/value entries
|
||||
static Ostream& print
|
||||
(
|
||||
Ostream& os,
|
||||
const UList<fileNameInstant>& series
|
||||
);
|
||||
|
||||
//- Write file series (JSON format) to disk, for specified instances
|
||||
//
|
||||
// \param seriesName The name for the series (eg, "path/file.vtk")
|
||||
// \param series The list of filename/value entries
|
||||
static void write
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const UList<fileNameInstant>& series
|
||||
);
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- True if there are no data sets
|
||||
inline bool empty() const;
|
||||
|
||||
//- The number of data sets
|
||||
inline label size() const;
|
||||
|
||||
|
||||
// Content Management
|
||||
|
||||
//- Clear entries
|
||||
inline void clear();
|
||||
|
||||
//- Append the specified file instant
|
||||
inline bool append(const fileNameInstant& inst);
|
||||
|
||||
//- Append the specified file instant
|
||||
inline bool append(fileNameInstant&& inst);
|
||||
|
||||
//- Append the specified file instant.
|
||||
inline bool append(scalar timeValue, const fileName& file);
|
||||
|
||||
//- Append the specified file instant.
|
||||
inline bool append(scalar timeValue, fileName&& file);
|
||||
|
||||
//- Clear contents and reload by parsing the specified file.
|
||||
//
|
||||
// \param seriesName the base name of the series to scan, without
|
||||
// the ".series" ending.
|
||||
// \param checkFiles verify that the files also exist
|
||||
// \param restartTime ignore entries with a time greater/equal
|
||||
// to the specified restart time.
|
||||
//
|
||||
// \return the number of entries
|
||||
label load
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const bool checkFiles = false,
|
||||
const scalar restartTime = ROOTVGREAT
|
||||
);
|
||||
|
||||
//- Clear contents and scan directory for files.
|
||||
//
|
||||
// The expected xml header content is a comment with the following:
|
||||
// \verbatim
|
||||
// <!-- ... time='3.14159' ... -->
|
||||
// \endverbatim
|
||||
//
|
||||
// \param seriesName the base name of the series to scan, without
|
||||
// the ".series" ending.
|
||||
// \param restartTime ignore entries with a time greater/equal
|
||||
// to the specified restart time.
|
||||
//
|
||||
// \return the number of entries
|
||||
label scan
|
||||
(
|
||||
const fileName& seriesName,
|
||||
const scalar restartTime = ROOTVGREAT
|
||||
);
|
||||
|
||||
//- Remove entries that are greater_equal the time value.
|
||||
//
|
||||
// \return True if the contents changed
|
||||
bool removeNewer(const scalar timeValue);
|
||||
|
||||
//- Sort by time value and by file name
|
||||
void sort();
|
||||
|
||||
|
||||
// Writing
|
||||
|
||||
//- Print file series as (JSON format)
|
||||
inline void print(Ostream& os) const;
|
||||
|
||||
//- Write file series as (JSON format) to disk
|
||||
inline void write(const fileName& seriesName) const;
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace vtk
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#include "foamVtkSeriesWriterI.H"
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
99
src/fileFormats/vtk/file/foamVtkSeriesWriterI.H
Normal file
99
src/fileFormats/vtk/file/foamVtkSeriesWriterI.H
Normal file
@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
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/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
inline bool Foam::vtk::seriesWriter::empty() const
|
||||
{
|
||||
return entries_.empty();
|
||||
}
|
||||
|
||||
|
||||
inline Foam::label Foam::vtk::seriesWriter::size() const
|
||||
{
|
||||
return entries_.size();
|
||||
}
|
||||
|
||||
|
||||
inline void Foam::vtk::seriesWriter::clear()
|
||||
{
|
||||
entries_.clear();
|
||||
existing_.clear();
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::vtk::seriesWriter::append(const fileNameInstant& inst)
|
||||
{
|
||||
// Strip out path before saving
|
||||
return appendCheck(fileNameInstant(inst.value(), inst.name().name()));
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::vtk::seriesWriter::append(fileNameInstant&& inst)
|
||||
{
|
||||
// Strip out path before saving
|
||||
inst.name().removePath();
|
||||
|
||||
return appendCheck(inst);
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::vtk::seriesWriter::append
|
||||
(
|
||||
scalar timeValue,
|
||||
const fileName& file
|
||||
)
|
||||
{
|
||||
// Strip out path before saving
|
||||
return appendCheck(fileNameInstant(timeValue, file.name()));
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::vtk::seriesWriter::append
|
||||
(
|
||||
scalar timeValue,
|
||||
fileName&& file
|
||||
)
|
||||
{
|
||||
// Strip out path before saving
|
||||
file.removePath();
|
||||
|
||||
return appendCheck(fileNameInstant(timeValue, std::move(file)));
|
||||
}
|
||||
|
||||
|
||||
inline void Foam::vtk::seriesWriter::print(Ostream& os) const
|
||||
{
|
||||
seriesWriter::print(os, entries_);
|
||||
}
|
||||
|
||||
|
||||
inline void Foam::vtk::seriesWriter::write(const fileName& seriesName) const
|
||||
{
|
||||
seriesWriter::write(seriesName, entries_);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
Reference in New Issue
Block a user