/*---------------------------------------------------------------------------*\
========= |
\\ / 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 "ensightWriterCaching.H"
#include "ListOps.H"
#include "OTstream.H"
#include "Fstream.H"
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Compare time values with tolerance
static const equalOp 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& 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::ensightOutput::writerCaching::writerCaching(const word& cacheFileName)
:
dictName_(cacheFileName)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
const Foam::dictionary& Foam::ensightOutput::writerCaching::fieldsDict() const
{
const dictionary* dictptr = cache_.findDict("fields", keyType::LITERAL);
if (!dictptr)
{
dictptr = &dictionary::null;
}
return *dictptr;
}
Foam::dictionary& Foam::ensightOutput::writerCaching::fieldDict
(
const word& fieldName
)
{
return
cache_
.subDictOrAdd("fields", keyType::LITERAL)
.subDictOrAdd(fieldName, keyType::LITERAL);
}
bool Foam::ensightOutput::writerCaching::remove(const word& fieldName)
{
dictionary* dictptr = cache_.findDict("fields", keyType::LITERAL);
if (dictptr)
{
return dictptr->remove(fieldName);
}
return false;
}
void Foam::ensightOutput::writerCaching::clear()
{
times_.clear();
geoms_.clear();
cache_.clear();
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::label Foam::ensightOutput::writerCaching::readPreviousTimes
(
const fileName& dictFile,
const scalar timeValue
)
{
// Track the used geometry intervals as a bitSet
// Note: 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);
}
// 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;
}
Foam::label Foam::ensightOutput::writerCaching::latestTimeIndex() const
{
return max(0, times_.size()-1);
}
Foam::label Foam::ensightOutput::writerCaching::latestGeomIndex() const
{
return max(0, geoms_.find_last());
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
int Foam::ensightOutput::writerCaching::geometryTimeset() const
{
if (geoms_.count() <= 1)
{
// Static
return 0;
}
if (geoms_.size() == times_.size() && geoms_.all())
{
// Geometry changing is identical to fields changing
return 1;
}
// Geometry changing differently from fields
return 2;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::ensightOutput::writerCaching::update
(
const fileName& baseDir,
const scalar timeValue,
const bool geomChanged,
const word& fieldName,
const string& fieldType, // May contain spaces (eg, "tensor symm")
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
// Note: to avoid inadvertent loss of precision,
// generate output tokens for the list of times directly
{
// Same as: cache_.set("times", times_);
OTstream os;
os << times_;
tokenList toks(std::move(os.tokens()));
cache_.set(new primitiveEntry("times", std::move(toks)));
}
cache_.set("geometry", 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.precision(16); // increased precision to avoid rounding
os << "// State file for writer output" << nl << nl;
cache_.write(os, false);
os << nl << "// End" << nl;
}
return stateChanged;
}
// ************************************************************************* //