Commit 7d203443 authored by Mark Olesen's avatar Mark Olesen

ENH: refactor surface writer collated time management (#1600)

- abstracted out from ensight surface writer for potential reuse by
  other surface writers.
parent f8ffee81
......@@ -62,6 +62,7 @@ triSurface/patches/surfacePatch.C
writers = writers
$(writers)/surfaceWriter.C
$(writers)/caching/surfaceWriterCaching.C
$(writers)/boundaryData/boundaryDataSurfaceWriter.C
$(writers)/ensight/ensightSurfaceWriter.C
$(writers)/foam/foamSurfaceWriter.C
......
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2020 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/>.
\*---------------------------------------------------------------------------*/
#include "surfaceWriterCaching.H"
#include "ListOps.H"
#include "Fstream.H"
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Compare time values with tolerance
static const equalOp<scalar> equalTimes(ROOTSMALL);
// Use ListOps findLower (with tolerance), to find the location of the next
// time-related index.
// The returned index is always 0 or larger (no negative values).
static label findTimeIndex(const UList<scalar>& list, const scalar val)
{
label idx =
findLower
(
list,
val,
0,
[](const scalar a, const scalar b)
{
return (a < b) && (Foam::mag(b - a) > ROOTSMALL);
}
);
if (idx < 0 || !equalTimes(list[idx], val))
{
++idx;
}
return idx;
}
} // End namespace Foam
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::surfaceWriters::writerCaching::writerCaching(const word& cacheFileName)
:
dictName_(cacheFileName)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
const Foam::dictionary& Foam::surfaceWriters::writerCaching::fieldsDict() const
{
const dictionary* dictptr = cache_.findDict("fields", keyType::LITERAL);
if (!dictptr)
{
dictptr = &dictionary::null;
}
return *dictptr;
}
Foam::dictionary& Foam::surfaceWriters::writerCaching::fieldDict
(
const word& fieldName
)
{
return
cache_
.subDictOrAdd("fields", keyType::LITERAL)
.subDictOrAdd(fieldName, keyType::LITERAL);
}
bool Foam::surfaceWriters::writerCaching::remove(const word& fieldName)
{
dictionary* dictptr = cache_.findDict("fields", keyType::LITERAL);
if (dictptr)
{
return dictptr->remove(fieldName);
}
return false;
}
void Foam::surfaceWriters::writerCaching::clear()
{
times_.clear();
geoms_.clear();
cache_.clear();
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::label Foam::surfaceWriters::writerCaching::readPreviousTimes
(
const fileName& dictFile,
const scalar timeValue
)
{
// In 1906 and earlier, the fieldsDict contained "meshes" and "times"
// entries, each with their own time values.
// This makes it more difficult to define the exact correspondence
// between geometry intervals and times.
//
// Now track the used geometry intervals as a bitSet.
// Only called from master
label timeIndex = 0;
cache_.clear();
IFstream is(dictFile);
if (is.good() && cache_.read(is))
{
geoms_.clear();
cache_.readIfPresent("times", times_);
timeIndex = findTimeIndex(times_, timeValue);
labelList geomIndices;
scalarList meshTimes;
if (cache_.readIfPresent("geometry", geomIndices))
{
// Convert indices to bitSet entries
geoms_.set(geomIndices);
}
else if (cache_.readIfPresent("meshes", meshTimes))
{
WarningInFunction
<< nl
<< "Setting geometry timeset information from time values"
<< " (cache from an older OpenFOAM version)." << nl
<< "This may not be fully reliable." << nl
<< nl;
for (const scalar meshTime : meshTimes)
{
const label geomIndex = findTimeIndex(times_, meshTime);
geoms_.set(geomIndex);
}
}
// Make length consistent with time information.
// We read/write the indices instead of simply dumping the bitSet.
// This makes the contents more human readable.
geoms_.resize(times_.size());
}
return timeIndex;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::surfaceWriters::writerCaching::update
(
const fileName& baseDir,
const scalar timeValue,
const bool geomChanged,
const word& fieldName,
const word& fieldType,
const word& varName
)
{
const fileName dictFile(baseDir/dictName_);
bool stateChanged = false;
const label timeIndex =
(
times_.empty()
? readPreviousTimes(dictFile, timeValue)
: findTimeIndex(times_, timeValue)
);
// Update stored times list and geometry index
if (timeIndex < geoms_.size()-1)
{
// Clear old content when shrinking
geoms_.unset(timeIndex);
}
// Extend or truncate list
geoms_.resize(timeIndex+1);
times_.resize(timeIndex+1, VGREAT);
if (!equalTimes(times_[timeIndex], timeValue))
{
stateChanged = true;
times_[timeIndex] = timeValue;
}
if (geomChanged)
{
stateChanged = true;
geoms_.set(timeIndex);
}
// Update time/geometry information in dictionary
cache_.set("times", times_);
cache_.set("geometry", geoms_.sortedToc());
// Debugging, or if needed for older versions:
//// cache_.set
//// (
//// "meshes",
//// IndirectList<scalar>(times_, geoms_.sortedToc())
//// );
// Add field information to dictionary
dictionary& dict = fieldDict(fieldName);
if (dict.empty())
{
stateChanged = true;
dict.set("type", fieldType);
if (!varName.empty() && varName != fieldName)
{
// Use variable name, if it differs from fieldName
dict.set("name", varName);
}
}
if (stateChanged)
{
OFstream os(dictFile);
os << "// State file for surface writer output" << nl << nl;
cache_.write(os, false);
os << nl << "// End" << nl;
}
return stateChanged;
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2020 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/>.
Class
Foam::surfaceWriters::writerCaching
Description
Information for surface writers with collated times.
The class maintains an internal list of the known times
as well as a file-cached version with the field information.
The information is used for restarts.
SourceFiles
surfaceWriterCaching.C
\*---------------------------------------------------------------------------*/
#ifndef surfaceWriters_writerCaching_H
#define surfaceWriters_writerCaching_H
#include "bitSet.H"
#include "dictionary.H"
#include "scalarList.H"
#include "DynamicList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace surfaceWriters
{
/*---------------------------------------------------------------------------*\
Class writerCaching Declaration
\*---------------------------------------------------------------------------*/
class writerCaching
{
// Private Data
//- Cache dictionary file name
word dictName_;
//- The output times
DynamicList<scalar> times_;
//- Indices in times_ when geometry (mesh) has been written
bitSet geoms_;
//- Cached information for geometry, times, fields
dictionary cache_;
// Private Member Functions
//- Read time information from dictFileName.
// Returns timeIndex corresponding to timeValue
label readPreviousTimes
(
const fileName& dictFile,
const scalar timeValue
);
//- Get or create a sub-dictionary for named field
dictionary& fieldDict(const word& fieldName);
//- Remove named field
bool remove(const word& fieldName);
public:
// Constructors
//- Construct with specified cache name
explicit writerCaching(const word& cacheFileName);
//- Destructor
virtual ~writerCaching() = default;
// Member Functions
//- The output times for fields
const scalarList& times() const
{
return times_;
}
//- Indices in times() when geometry (mesh) has been written
const bitSet& geometries() const
{
return geoms_;
}
//- The most current time index
label latestTimeIndex() const
{
return max(0, times_.size()-1);
}
//- The most current geometry index
label latestGeomIndex() const
{
return max(0, geoms_.find_last());
}
//- Get or create the 'fields' information dictionary.
const dictionary& fieldsDict() const;
//- Clear all values
void clear();
//- Update time/geometry information and file cache.
//- This routine should only be called from the master process
// \return True if there is a state change, which is either a
// geometry change or a new time interval
bool update
(
const fileName& baseDir, //!< Directory containing the cache file
const scalar timeValue, //!< The current time value
const bool geomChanged, //!< Monitored geometry changed
const word& fieldName, //!< Name of field
const word& fieldType, //!< Type of field
const word& varName = word::null //!< Alternative field name
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace surfaceWriters
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
......@@ -56,7 +56,7 @@ void Foam::surfaceWriters::ensightWriter::printTimeset
(
OSstream& os,
const label ts,
const scalar& timeValue
const scalar timeValue
)
{
os
......@@ -176,7 +176,8 @@ Foam::surfaceWriters::ensightWriter::ensightWriter()
:
surfaceWriter(),
writeFormat_(IOstream::ASCII),
collateTimes_(true)
collateTimes_(true),
caching_("fieldsDict") // Historic name
{}
......@@ -190,7 +191,8 @@ Foam::surfaceWriters::ensightWriter::ensightWriter
(
IOstreamOption::formatEnum("format", options, IOstream::ASCII)
),
collateTimes_(options.getOrDefault("collateTimes", true))
collateTimes_(options.getOrDefault("collateTimes", true)),
caching_("fieldsDict") // Historic name
{}
......@@ -227,15 +229,13 @@ Foam::surfaceWriters::ensightWriter::ensightWriter
void Foam::surfaceWriters::ensightWriter::close()
{
times_.clear();
meshes_.clear();
cache_.clear();
caching_.clear();
surfaceWriter::close();
}
// Note that ensight does supports geometry in a separate file,
// but setting this true leaves mesh files in the wrong places
// but setting this true leaves geometry files in the wrong places
// (when there are fields).
//
// Make this false to let the field writers take back control
......
......@@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2015-2019 OpenCFD Ltd.
Copyright (C) 2015-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -61,8 +61,7 @@ SourceFiles
#define ensightSurfaceWriter_H
#include "surfaceWriter.H"
#include "bitSet.H"
#include "DynamicList.H"
#include "surfaceWriterCaching.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......@@ -87,27 +86,12 @@ class ensightWriter
//- Collate times (default: true)
bool collateTimes_;
//- The collated output times
DynamicList<scalar> times_;
//- Indices in times_ when geometry (mesh) has been written (collated)
bitSet meshes_;
//- Cached information for times, geometry, fields (collated)
dictionary cache_;
writerCaching caching_;
// Private Member Functions
//- Read time information from baseDir / dictName.
// Returns timeIndex corresponding to timeValue
label readPreviousTimes
(
const fileName& baseDir,
const word& dictName,
const scalar& timeValue
);
//- The geometry can be any of the following:
//
// 0: constant/static
......@@ -120,7 +104,7 @@ class ensightWriter
(
OSstream& os,
const label ts,
const scalar& timeValue
const scalar timeValue
);
//- Print time-set for ensight case file, with N times and 0-based
......
......@@ -37,7 +37,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
// Uncollated
// ==========
// Geometry: rootdir/<TIME>/surfaceName.case
// CaseFile: rootdir/<TIME>/surfaceName.case
// Geometry: rootdir/<TIME>/surfaceName.00000000.mesh
fileName outputDir;
......@@ -85,7 +85,7 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
<< nl
<< "TIME" << nl;
printTimeset(osCase, 1, 0.0);
printTimeset(osCase, 1, scalar(0));
ensightOutputSurface part
(
......@@ -116,9 +116,9 @@ Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
// Uncollated
// ==========
// geometry: rootdir/time/<field>/surfaceName.case
// geometry: rootdir/time/<field>/surfaceName.<index>.mesh
// field: rootdir/time/<field>/surfaceName.<index>.<field>
// CaseFile: rootdir/time/<field>/surfaceName.case
// Geometry: rootdir/time/<field>/surfaceName.<index>.mesh
// Field: rootdir/time/<field>/surfaceName.<index>.<field>
// Variable name as sub-directory for results. Eg,
// - VAR1/SURF1.case
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment