Files
OpenFOAM-12/src/OpenFOAM/db/Time/Time.C
Henry Weller fdf1216ef5 objectRegistry: Added support for optionally caching temporary objects
which provides a very convenient mechanism to process and write any temporary
fields created during a time-step, either within models the construction of
equations and matrices or any other intermediate processing step within an
OpenFOAM application.  The cached fields can relate to physical properties in
models, e.g. the generation term or other terms in the turbulence models, or
numerical, e.g. the limiters used on convection schemes.  This mechanism
provides a new very powerful non-intrusive way of analysing the internals of an
OpenFOAM application for diagnosis and general post-processing which cannot be
easily achieved by any other means without adding specific diagnostics code to
the models or interest and recompiling.

For example to cache the kEpsilon:G field in
tutorials/incompressible/simpleFoam/pitzDaily add the dictionary entry

cacheTemporaryObjects
(
    grad(k)
    kEpsilon:G
);

to system/controlDict and to write the field add a writeObjects entry to the
functions list:

functions
{
    writeCachedObjects
    {
        type        writeObjects;
        libs        ("libutilityFunctionObjects.so");

        writeControl writeTime;
        writeOption anyWrite;

        objects
        (
            grad(k)
            kEpsilon:G
        );
    }

    #includeFunc streamlines
}

If a name of a field which in never constructed is added to the
cacheTemporaryObjects list a waning message is generated which includes a useful
list of ALL the temporary fields constructed during the time step, e.g. for the
tutorials/incompressible/simpleFoam/pitzDaily case:

--> FOAM Warning : Could not find temporary object dummy in registry region0
Available temporary objects
81
(
(((0.666667*C1)-C3)*div(phi))
div(phi)
(interpolate(nuEff)*magSf)
surfaceIntegrate(phi)
(interpolate(DepsilonEff)*magSf)
((interpolate(((1|((1|(1|A(U)))-H(1)))-(1|A(U))))*snGrad(p))*magSf)
grad(p)
((interpolate(nuEff)*magSf)*snGradCorr(U))
(interpolate((1|((1|(1|A(U)))-H(1))))*magSf)
((1|((1|(1|A(U)))-H(1)))-(1|A(U)))
((Cmu*sqr(k))|epsilon)
interpolate(HbyA)
interpolate(DkEff)
interpolate(U)
phiHbyA
weights
div(((interpolate((1|((1|(1|A(U)))-H(1))))*magSf)*snGradCorr(p)))
(phiHbyA-flux(p))
MRFZoneList:acceleration
average(interpolate(max(epsilon,epsilonMin)))
div(((interpolate(DepsilonEff)*magSf)*snGradCorr(epsilon)))
nuEff
kEpsilon:G
grad(k)
interpolate((1|((1|(1|A(U)))-H(1))))
(nuEff*dev2(T(grad(U))))
grad(U)
interpolate(epsilon)
(phi*linearUpwind::correction(U))
((interpolate(DepsilonEff)*magSf)*snGradCorr(epsilon))
grad(k)Cached
(HbyA-((1|((1|(1|A(U)))-H(1)))*grad(p)))
pos0(phi)
-div((nuEff*dev2(T(grad(U)))))
H(1)
interpolate(k)
((nut|sigmak)+nu)
snGrad(p)
(0.666667*div(phi))
surfaceIntegrate(((interpolate((1|((1|(1|A(U)))-H(1))))*magSf)*snGradCorr(p)))
DepsilonEff
(1|A(U))
surfaceIntegrate(((interpolate(DepsilonEff)*magSf)*snGradCorr(epsilon)))
limitedLinearLimiter(epsilon)
surfaceIntegrate(((interpolate(DkEff)*magSf)*snGradCorr(k)))
grad(epsilon)
(interpolate(DkEff)*magSf)
div(((interpolate(DkEff)*magSf)*snGradCorr(k)))
surfaceSum(magSf)
((1|A(U))-(1|((1|(1|A(U)))-H(1))))
(1|((1|(1|A(U)))-H(1)))
((interpolate((1|((1|(1|A(U)))-H(1))))*magSf)*snGradCorr(p))
mag(div(phi))
surfaceSum((magSf*interpolate(max(epsilon,epsilonMin))))
interpolate(DepsilonEff)
-grad(p)
snGradCorr(p)
interpolate(p)
interpolate(max(epsilon,epsilonMin))
dev(twoSymm(grad(U)))
surfaceIntegrate((phi*linearUpwind::correction(U)))
(magSf*interpolate(max(epsilon,epsilonMin)))
limitedLinearLimiter(k)
(nut+nu)
HbyA
max(epsilon,epsilonMin)
surfaceIntegrate(((interpolate(nuEff)*magSf)*snGradCorr(U)))
surfaceIntegrate(phiHbyA)
DkEff
(((C1*kEpsilon:G)*epsilon)|k)
(mag(S)+2.22507e-308)
(((1|A(U))-(1|((1|(1|A(U)))-H(1))))*grad(p))
((nut|sigmaEps)+nu)
((interpolate(DkEff)*magSf)*snGradCorr(k))
(nut*(dev(twoSymm(grad(U)))&&grad(U)))
interpolate(nuEff)
((C2*epsilon)|k)
interpolate((nuEff*dev2(T(grad(U)))))
(epsilon|k)
div(phiHbyA)
div(((interpolate(nuEff)*magSf)*snGradCorr(U)))
)

Multiple regions are also supported by specifying individual region names in a
cacheTemporaryObjects dictionary, e.g. in the
tutorials/heatTransfer/chtMultiRegionFoam/heatExchanger case

cacheTemporaryObjects
{
    air
    (
        kEpsilon:G
    );

    porous
    (
        porosityBlockage:UNbr
    );
}

functions
{
    writeAirObjects
    {
        type        writeObjects;
        libs        ("libutilityFunctionObjects.so");

        region      air;
        writeControl writeTime;
        writeOption anyWrite;

        objects     (kEpsilon:G);
    }

    writePorousObjects
    {
        type        writeObjects;
        libs        ("libutilityFunctionObjects.so");

        region      porous;
        writeControl writeTime;
        writeOption anyWrite;

        objects     (porosityBlockage:UNbr);
    }
}
2019-07-22 10:31:36 +01:00

1238 lines
29 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2019 OpenFOAM Foundation
\\/ 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 "Time.H"
#include "PstreamReduceOps.H"
#include "argList.H"
#include "IOdictionary.H"
#include <sstream>
// * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(Time, 0);
template<>
const char* Foam::NamedEnum
<
Foam::Time::stopAtControl,
4
>::names[] =
{
"endTime",
"noWriteNow",
"writeNow",
"nextWrite"
};
template<>
const char* Foam::NamedEnum
<
Foam::Time::writeControl,
5
>::names[] =
{
"timeStep",
"runTime",
"adjustableRunTime",
"clockTime",
"cpuTime"
};
}
const Foam::NamedEnum<Foam::Time::stopAtControl, 4>
Foam::Time::stopAtControlNames_;
const Foam::NamedEnum<Foam::Time::writeControl, 5>
Foam::Time::writeControlNames_;
Foam::Time::format Foam::Time::format_(Foam::Time::format::general);
int Foam::Time::precision_(6);
const int Foam::Time::maxPrecision_(3 - log10(small));
Foam::word Foam::Time::controlDictName("controlDict");
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::Time::adjustDeltaT()
{
const scalar timeToNextWrite = min
(
max
(
0,
(writeTimeIndex_ + 1)*writeInterval_ - (value() - startTime_)
),
functionObjects_.timeToNextWrite()
);
const scalar nSteps = timeToNextWrite/deltaT_;
// Ensure nStepsToNextWrite does not overflow
if (nSteps < labelMax)
{
// Allow the time-step to increase by up to 1%
// to accommodate the next write time before splitting
const label nStepsToNextWrite = label(max(nSteps, 1) + 0.99);
deltaT_ = timeToNextWrite/nStepsToNextWrite;
}
}
void Foam::Time::setControls()
{
// default is to resume calculation from "latestTime"
const word startFrom = controlDict_.lookupOrDefault<word>
(
"startFrom",
"latestTime"
);
if (startFrom == "startTime")
{
controlDict_.lookup("startTime") >> startTime_;
}
else
{
// Search directory for valid time directories
instantList timeDirs = findTimes(path(), constant());
if (startFrom == "firstTime")
{
if (timeDirs.size())
{
if (timeDirs[0].name() == constant() && timeDirs.size() >= 2)
{
startTime_ = timeDirs[1].value();
}
else
{
startTime_ = timeDirs[0].value();
}
}
}
else if (startFrom == "latestTime")
{
if (timeDirs.size())
{
startTime_ = timeDirs.last().value();
}
}
else
{
FatalIOErrorInFunction(controlDict_)
<< "expected startTime, firstTime or latestTime"
<< " found '" << startFrom << "'"
<< exit(FatalIOError);
}
}
setTime(startTime_, 0);
readDict();
deltaTSave_ = deltaT_;
deltaT0_ = deltaT_;
// Check if time directory exists
// If not increase time precision to see if it is formatted differently.
if (!fileHandler().exists(timePath(), false, false))
{
int oldPrecision = precision_;
int requiredPrecision = -1;
bool found = false;
word oldTime(timeName());
for
(
precision_ = maxPrecision_;
precision_ > oldPrecision;
precision_--
)
{
// Update the time formatting
setTime(startTime_, 0);
word newTime(timeName());
if (newTime == oldTime)
{
break;
}
oldTime = newTime;
// Check the existence of the time directory with the new format
found = fileHandler().exists(timePath(), false, false);
if (found)
{
requiredPrecision = precision_;
}
}
if (requiredPrecision > 0)
{
// Update the time precision
precision_ = requiredPrecision;
// Update the time formatting
setTime(startTime_, 0);
WarningInFunction
<< "Increasing the timePrecision from " << oldPrecision
<< " to " << precision_
<< " to support the formatting of the current time directory "
<< timeName() << nl << endl;
}
else
{
// Could not find time directory so assume it is not present
precision_ = oldPrecision;
// Revert the time formatting
setTime(startTime_, 0);
}
}
if (Pstream::parRun())
{
scalar sumStartTime = startTime_;
reduce(sumStartTime, sumOp<scalar>());
if
(
mag(Pstream::nProcs()*startTime_ - sumStartTime)
> Pstream::nProcs()*deltaT_/10.0
)
{
FatalIOErrorInFunction(controlDict_)
<< "Start time is not the same for all processors" << nl
<< "processor " << Pstream::myProcNo() << " has startTime "
<< startTime_ << exit(FatalIOError);
}
}
IOdictionary timeDict
(
IOobject
(
"time",
timeName(),
"uniform",
*this,
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
false
)
);
// Read and set the deltaT only if time-step adjustment is active
// otherwise use the deltaT from the controlDict
if (controlDict_.lookupOrDefault<Switch>("adjustTimeStep", false))
{
if (timeDict.readIfPresent("deltaT", deltaT_))
{
deltaTSave_ = deltaT_;
deltaT0_ = deltaT_;
}
}
timeDict.readIfPresent("deltaT0", deltaT0_);
if (timeDict.readIfPresent("index", startTimeIndex_))
{
timeIndex_ = startTimeIndex_;
}
// Check if values stored in time dictionary are consistent
// 1. Based on time name
bool checkValue = true;
string storedTimeName;
if (timeDict.readIfPresent("name", storedTimeName))
{
if (storedTimeName == timeName())
{
// Same time. No need to check stored value
checkValue = false;
}
}
// 2. Based on time value
// (consistent up to the current time writing precision so it won't
// trigger if we just change the write precision)
if (checkValue)
{
scalar storedTimeValue;
if (timeDict.readIfPresent("value", storedTimeValue))
{
word storedTimeName(timeName(storedTimeValue));
if (storedTimeName != timeName())
{
IOWarningInFunction(timeDict)
<< "Time read from time dictionary " << storedTimeName
<< " differs from actual time " << timeName() << '.' << nl
<< " This may cause unexpected database behaviour."
<< " If you are not interested" << nl
<< " in preserving time state delete"
<< " the time dictionary."
<< endl;
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::Time::Time
(
const word& controlDictName,
const fileName& rootPath,
const fileName& caseName,
const word& systemName,
const word& constantName,
const bool enableFunctionObjects
)
:
TimePaths
(
rootPath,
caseName,
systemName,
constantName
),
objectRegistry(*this),
libs_(),
controlDict_
(
IOobject
(
controlDictName,
system(),
*this,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE,
false
)
),
startTimeIndex_(0),
startTime_(0),
endTime_(0),
stopAt_(stopAtControl::endTime),
writeControl_(writeControl::timeStep),
writeInterval_(great),
purgeWrite_(0),
writeOnce_(false),
subCycling_(false),
sigWriteNow_(true, *this),
sigStopAtWriteNow_(true, *this),
writeFormat_(IOstream::ASCII),
writeVersion_(IOstream::currentVersion),
writeCompression_(IOstream::UNCOMPRESSED),
graphFormat_("raw"),
runTimeModifiable_(false),
cacheTemporaryObjects_(true),
functionObjects_(*this, enableFunctionObjects)
{
libs_.open(controlDict_, "libs");
// Explicitly set read flags on objectRegistry so anything constructed
// from it reads as well (e.g. fvSolution).
readOpt() = IOobject::MUST_READ_IF_MODIFIED;
setControls();
// Time objects not registered so do like objectRegistry::checkIn ourselves.
if (runTimeModifiable_)
{
// Monitor all files that controlDict depends on
fileHandler().addWatches(controlDict_, controlDict_.files());
}
// Clear dependent files
controlDict_.files().clear();
}
Foam::Time::Time
(
const word& controlDictName,
const argList& args,
const word& systemName,
const word& constantName
)
:
TimePaths
(
args.parRunControl().parRun(),
args.rootPath(),
args.globalCaseName(),
args.caseName(),
systemName,
constantName
),
objectRegistry(*this),
libs_(),
controlDict_
(
IOobject
(
controlDictName,
system(),
*this,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE,
false
)
),
startTimeIndex_(0),
startTime_(0),
endTime_(0),
stopAt_(stopAtControl::endTime),
writeControl_(writeControl::timeStep),
writeInterval_(great),
purgeWrite_(0),
writeOnce_(false),
subCycling_(false),
sigWriteNow_(true, *this),
sigStopAtWriteNow_(true, *this),
writeFormat_(IOstream::ASCII),
writeVersion_(IOstream::currentVersion),
writeCompression_(IOstream::UNCOMPRESSED),
graphFormat_("raw"),
runTimeModifiable_(false),
cacheTemporaryObjects_(true),
functionObjects_
(
*this,
argList::validOptions.found("withFunctionObjects")
? args.optionFound("withFunctionObjects")
: !args.optionFound("noFunctionObjects")
)
{
libs_.open(controlDict_, "libs");
// Explicitly set read flags on objectRegistry so anything constructed
// from it reads as well (e.g. fvSolution).
readOpt() = IOobject::MUST_READ_IF_MODIFIED;
setControls();
// Time objects not registered so do like objectRegistry::checkIn ourselves.
if (runTimeModifiable_)
{
// Monitor all files that controlDict depends on
fileHandler().addWatches(controlDict_, controlDict_.files());
}
// Clear dependent files since not needed
controlDict_.files().clear();
}
Foam::Time::Time
(
const dictionary& dict,
const fileName& rootPath,
const fileName& caseName,
const word& systemName,
const word& constantName,
const bool enableFunctionObjects
)
:
TimePaths
(
rootPath,
caseName,
systemName,
constantName
),
objectRegistry(*this),
libs_(),
controlDict_
(
IOobject
(
controlDictName,
system(),
*this,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
dict
),
startTimeIndex_(0),
startTime_(0),
endTime_(0),
stopAt_(stopAtControl::endTime),
writeControl_(writeControl::timeStep),
writeInterval_(great),
purgeWrite_(0),
writeOnce_(false),
subCycling_(false),
sigWriteNow_(true, *this),
sigStopAtWriteNow_(true, *this),
writeFormat_(IOstream::ASCII),
writeVersion_(IOstream::currentVersion),
writeCompression_(IOstream::UNCOMPRESSED),
graphFormat_("raw"),
runTimeModifiable_(false),
cacheTemporaryObjects_(true),
functionObjects_(*this, enableFunctionObjects)
{
libs_.open(controlDict_, "libs");
// Explicitly set read flags on objectRegistry so anything constructed
// from it reads as well (e.g. fvSolution).
readOpt() = IOobject::MUST_READ_IF_MODIFIED;
// Since could not construct regIOobject with setting:
controlDict_.readOpt() = IOobject::MUST_READ_IF_MODIFIED;
setControls();
// Time objects not registered so do like objectRegistry::checkIn ourselves.
if (runTimeModifiable_)
{
// Monitor all files that controlDict depends on
fileHandler().addWatches(controlDict_, controlDict_.files());
}
// Clear dependent files since not needed
controlDict_.files().clear();
}
Foam::Time::Time
(
const fileName& rootPath,
const fileName& caseName,
const word& systemName,
const word& constantName,
const bool enableFunctionObjects
)
:
TimePaths
(
rootPath,
caseName,
systemName,
constantName
),
objectRegistry(*this),
libs_(),
controlDict_
(
IOobject
(
controlDictName,
system(),
*this,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
)
),
startTimeIndex_(0),
startTime_(0),
endTime_(0),
stopAt_(stopAtControl::endTime),
writeControl_(writeControl::timeStep),
writeInterval_(great),
purgeWrite_(0),
writeOnce_(false),
subCycling_(false),
writeFormat_(IOstream::ASCII),
writeVersion_(IOstream::currentVersion),
writeCompression_(IOstream::UNCOMPRESSED),
graphFormat_("raw"),
runTimeModifiable_(false),
cacheTemporaryObjects_(true),
functionObjects_(*this, enableFunctionObjects)
{
libs_.open(controlDict_, "libs");
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::Time::~Time()
{
forAllReverse(controlDict_.watchIndices(), i)
{
fileHandler().removeWatch(controlDict_.watchIndices()[i]);
}
// Destroy function objects first
functionObjects_.clear();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::word Foam::Time::timeName(const scalar t, const int precision)
{
std::ostringstream buf;
buf.setf(ios_base::fmtflags(format_), ios_base::floatfield);
buf.precision(precision);
buf << t;
return buf.str();
}
Foam::word Foam::Time::timeName() const
{
return dimensionedScalar::name();
}
Foam::instantList Foam::Time::times() const
{
return findTimes(path(), constant());
}
Foam::word Foam::Time::findInstance
(
const fileName& dir,
const word& name,
const IOobject::readOption rOpt,
const word& stopInstance
) const
{
IOobject startIO
(
name, // name might be empty!
timeName(),
dir,
*this,
rOpt
);
IOobject io
(
fileHandler().findInstance
(
startIO,
timeOutputValue(),
stopInstance
)
);
return io.instance();
}
Foam::word Foam::Time::findInstancePath
(
const fileName& directory,
const instant& t
) const
{
// Simplified version: use findTimes (readDir + sort). The expensive
// bit is the readDir, not the sorting. Tbd: avoid calling findInstancePath
// from filePath.
instantList timeDirs = findTimes(path(), constant());
// Note:
// - times will include constant (with value 0) as first element.
// For backwards compatibility make sure to find 0 in preference
// to constant.
// - list is sorted so could use binary search
forAllReverse(timeDirs, i)
{
if (t.equal(timeDirs[i].value()))
{
return timeDirs[i].name();
}
}
return word::null;
}
Foam::word Foam::Time::findInstancePath(const instant& t) const
{
return findInstancePath(path(), t);
}
Foam::instant Foam::Time::findClosestTime(const scalar t) const
{
instantList timeDirs = findTimes(path(), constant());
// There is only one time (likely "constant") so return it
if (timeDirs.size() == 1)
{
return timeDirs[0];
}
if (t < timeDirs[1].value())
{
return timeDirs[1];
}
else if (t > timeDirs.last().value())
{
return timeDirs.last();
}
label nearestIndex = -1;
scalar deltaT = great;
for (label timei=1; timei < timeDirs.size(); ++timei)
{
scalar diff = mag(timeDirs[timei].value() - t);
if (diff < deltaT)
{
deltaT = diff;
nearestIndex = timei;
}
}
return timeDirs[nearestIndex];
}
Foam::label Foam::Time::findClosestTimeIndex
(
const instantList& timeDirs,
const scalar t,
const word& constantName
)
{
label nearestIndex = -1;
scalar deltaT = great;
forAll(timeDirs, timei)
{
if (timeDirs[timei].name() == constantName) continue;
scalar diff = mag(timeDirs[timei].value() - t);
if (diff < deltaT)
{
deltaT = diff;
nearestIndex = timei;
}
}
return nearestIndex;
}
Foam::label Foam::Time::startTimeIndex() const
{
return startTimeIndex_;
}
Foam::dimensionedScalar Foam::Time::startTime() const
{
return dimensionedScalar("startTime", dimTime, startTime_);
}
Foam::dimensionedScalar Foam::Time::endTime() const
{
return dimensionedScalar("endTime", dimTime, endTime_);
}
bool Foam::Time::running() const
{
return value() < (endTime_ - 0.5*deltaT_);
}
bool Foam::Time::run() const
{
bool running = this->running();
if (!subCycling_)
{
if (!running && timeIndex_ != startTimeIndex_)
{
if (cacheTemporaryObjects_)
{
cacheTemporaryObjects_ = checkCacheTemporaryObjects();
}
functionObjects_.execute();
functionObjects_.end();
}
}
if (running)
{
if (!subCycling_)
{
const_cast<Time&>(*this).readModifiedObjects();
if (timeIndex_ == startTimeIndex_)
{
functionObjects_.start();
}
else
{
if (cacheTemporaryObjects_)
{
cacheTemporaryObjects_ = checkCacheTemporaryObjects();
}
functionObjects_.execute();
}
}
// Re-evaluate if running in case a function object has changed things
running = this->running();
}
return running;
}
bool Foam::Time::loop()
{
bool running = run();
if (running)
{
operator++();
}
return running;
}
bool Foam::Time::end() const
{
return value() > (endTime_ + 0.5*deltaT_);
}
bool Foam::Time::stopAt(const stopAtControl sa) const
{
const bool changed = (stopAt_ != sa);
stopAt_ = sa;
// adjust endTime
if (sa == stopAtControl::endTime)
{
controlDict_.lookup("endTime") >> endTime_;
}
else
{
endTime_ = great;
}
return changed;
}
void Foam::Time::setTime(const Time& t)
{
value() = t.value();
dimensionedScalar::name() = t.dimensionedScalar::name();
timeIndex_ = t.timeIndex_;
fileHandler().setTime(*this);
}
void Foam::Time::setTime(const instant& inst, const label newIndex)
{
value() = inst.value();
dimensionedScalar::name() = inst.name();
timeIndex_ = newIndex;
IOdictionary timeDict
(
IOobject
(
"time",
timeName(),
"uniform",
*this,
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
false
)
);
timeDict.readIfPresent("deltaT", deltaT_);
timeDict.readIfPresent("deltaT0", deltaT0_);
timeDict.readIfPresent("index", timeIndex_);
fileHandler().setTime(*this);
}
void Foam::Time::setTime(const dimensionedScalar& newTime, const label newIndex)
{
setTime(newTime.value(), newIndex);
}
void Foam::Time::setTime(const scalar newTime, const label newIndex)
{
value() = newTime;
dimensionedScalar::name() = timeName(timeToUserTime(newTime));
timeIndex_ = newIndex;
fileHandler().setTime(*this);
}
void Foam::Time::setEndTime(const dimensionedScalar& endTime)
{
setEndTime(endTime.value());
}
void Foam::Time::setEndTime(const scalar endTime)
{
endTime_ = endTime;
}
void Foam::Time::setDeltaT(const dimensionedScalar& deltaT)
{
setDeltaT(deltaT.value());
}
void Foam::Time::setDeltaT(const scalar deltaT)
{
setDeltaTNoAdjust(deltaT);
functionObjects_.setTimeStep();
if (writeControl_ == writeControl::adjustableRunTime)
{
adjustDeltaT();
}
}
void Foam::Time::setDeltaTNoAdjust(const scalar deltaT)
{
deltaT_ = deltaT;
deltaTchanged_ = true;
}
Foam::TimeState Foam::Time::subCycle(const label nSubCycles)
{
subCycling_ = true;
prevTimeState_.set(new TimeState(*this));
setTime(*this - deltaT(), (timeIndex() - 1)*nSubCycles);
deltaT_ /= nSubCycles;
deltaT0_ /= nSubCycles;
deltaTSave_ = deltaT0_;
return prevTimeState();
}
void Foam::Time::endSubCycle()
{
if (subCycling_)
{
subCycling_ = false;
TimeState::operator=(prevTimeState());
prevTimeState_.clear();
}
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
Foam::Time& Foam::Time::operator+=(const dimensionedScalar& deltaT)
{
return operator+=(deltaT.value());
}
Foam::Time& Foam::Time::operator+=(const scalar deltaT)
{
setDeltaT(deltaT);
return operator++();
}
Foam::Time& Foam::Time::operator++()
{
deltaT0_ = deltaTSave_;
deltaTSave_ = deltaT_;
// Save old time value and name
const scalar oldTimeValue = timeToUserTime(value());
const word oldTimeName = dimensionedScalar::name();
// Increment time
setTime(value() + deltaT_, timeIndex_ + 1);
if (!subCycling_)
{
// If the time is very close to zero reset to zero
if (mag(value()) < 10*small*deltaT_)
{
setTime(0, timeIndex_);
}
if (sigStopAtWriteNow_.active() || sigWriteNow_.active())
{
// A signal might have been sent on one processor only
// Reduce so all decide the same.
label flag = 0;
if
(
sigStopAtWriteNow_.active()
&& stopAt_ == stopAtControl::writeNow
)
{
flag += 1;
}
if (sigWriteNow_.active() && writeOnce_)
{
flag += 2;
}
reduce(flag, maxOp<label>());
if (flag & 1)
{
stopAt_ = stopAtControl::writeNow;
}
if (flag & 2)
{
writeOnce_ = true;
}
}
writeTime_ = false;
switch (writeControl_)
{
case writeControl::timeStep:
writeTime_ = !(timeIndex_ % label(writeInterval_));
break;
case writeControl::runTime:
case writeControl::adjustableRunTime:
{
label writeIndex = label
(
((value() - startTime_) + 0.5*deltaT_)
/ writeInterval_
);
if (writeIndex > writeTimeIndex_)
{
writeTime_ = true;
writeTimeIndex_ = writeIndex;
}
}
break;
case writeControl::cpuTime:
{
label writeIndex = label
(
returnReduce(elapsedCpuTime(), maxOp<double>())
/ writeInterval_
);
if (writeIndex > writeTimeIndex_)
{
writeTime_ = true;
writeTimeIndex_ = writeIndex;
}
}
break;
case writeControl::clockTime:
{
label writeIndex = label
(
returnReduce(label(elapsedClockTime()), maxOp<label>())
/ writeInterval_
);
if (writeIndex > writeTimeIndex_)
{
writeTime_ = true;
writeTimeIndex_ = writeIndex;
}
}
break;
}
// Check if endTime needs adjustment to stop at the next run()/end()
if (!end())
{
if (stopAt_ == stopAtControl::noWriteNow)
{
endTime_ = value();
}
else if (stopAt_ == stopAtControl::writeNow)
{
endTime_ = value();
writeTime_ = true;
}
else if (stopAt_ == stopAtControl::nextWrite && writeTime_ == true)
{
endTime_ = value();
}
}
// Override writeTime if one-shot writing
if (writeOnce_)
{
writeTime_ = true;
writeOnce_ = false;
}
// Adjust the precision of the time directory name if necessary
if (writeTime_)
{
// Tolerance used when testing time equivalence
const scalar timeTol =
max(min(pow(10.0, -precision_), 0.1*deltaT_), small);
// User-time equivalent of deltaT
const scalar userDeltaT = timeToUserTime(deltaT_);
// Time value obtained by reading timeName
scalar timeNameValue = -vGreat;
// Check that new time representation differs from old one
// reinterpretation of the word
if
(
readScalar(dimensionedScalar::name().c_str(), timeNameValue)
&& (mag(timeNameValue - oldTimeValue - userDeltaT) > timeTol)
)
{
int oldPrecision = precision_;
while
(
precision_ < maxPrecision_
&& readScalar(dimensionedScalar::name().c_str(), timeNameValue)
&& (mag(timeNameValue - oldTimeValue - userDeltaT) > timeTol)
)
{
precision_++;
setTime(value(), timeIndex());
}
if (precision_ != oldPrecision)
{
WarningInFunction
<< "Increased the timePrecision from " << oldPrecision
<< " to " << precision_
<< " to distinguish between timeNames at time "
<< dimensionedScalar::name()
<< endl;
if (precision_ == maxPrecision_)
{
// Reached maxPrecision limit
WarningInFunction
<< "Current time name " << dimensionedScalar::name()
<< nl
<< " The maximum time precision has been reached"
" which might result in overwriting previous"
" results."
<< endl;
}
// Check if round-off error caused time-reversal
scalar oldTimeNameValue = -vGreat;
if
(
readScalar(oldTimeName.c_str(), oldTimeNameValue)
&& (
sign(timeNameValue - oldTimeNameValue)
!= sign(deltaT_)
)
)
{
WarningInFunction
<< "Current time name " << dimensionedScalar::name()
<< " is set to an instance prior to the "
"previous one "
<< oldTimeName << nl
<< " This might result in temporal "
"discontinuities."
<< endl;
}
}
}
}
}
return *this;
}
Foam::Time& Foam::Time::operator++(int)
{
return operator++();
}
// ************************************************************************* //