/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2016-2023 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 "Time.H"
#include "argList.H"
#include "Pstream.H"
#include "simpleObjectRegistry.H"
#include "dimensionedConstants.H"
#include "profiling.H"
#include "IOdictionary.H"
#include "fileOperation.H"
#include "fstreamPointer.H"
#include
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Output seconds as day-hh:mm:ss
static std::ostream& printTimeHMS(std::ostream& os, double seconds)
{
const unsigned long ss = seconds;
// days
const auto dd = (ss / 86400);
if (dd) os << dd << '-';
// hours
const int hh = ((ss / 3600) % 24);
if (dd || hh)
{
os << std::setw(2) << std::setfill('0')
<< hh << ':';
}
// minutes
os << std::setw(2) << std::setfill('0')
<< ((ss / 60) % 60) << ':';
// seconds
os << std::setw(2) << std::setfill('0')
<< (ss % 60);
// 1/100th seconds. As none or 2 decimal places
const int hundredths = int(100 * (seconds - ss)) % 100;
if (hundredths)
{
os << '.' << std::setw(2) << std::setfill('0') << hundredths;
}
return os;
}
} // End namespace Foam
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::Time::readDict()
{
word application;
if (controlDict_.readIfPresent("application", application))
{
// Do not override if already set so external application can override
setEnv("FOAM_APPLICATION", application, false);
}
// Check for local switches and settings
const dictionary* localDict = nullptr;
// DebugSwitches
if
(
(localDict = controlDict_.findDict("DebugSwitches")) != nullptr
&& localDict->size()
)
{
DetailInfo
<< "Overriding DebugSwitches according to "
<< controlDict_.name() << nl;
debug::debugObjects().setValues(*localDict, true);
}
// InfoSwitches
if
(
(localDict = controlDict_.findDict("InfoSwitches")) != nullptr
&& localDict->size()
)
{
DetailInfo
<< "Overriding InfoSwitches according to "
<< controlDict_.name() << nl;
debug::infoObjects().setValues(*localDict, true);
}
// OptimisationSwitches
if
(
(localDict = controlDict_.findDict("OptimisationSwitches")) != nullptr
&& localDict->size()
)
{
DetailInfo
<< "Overriding OptimisationSwitches according to "
<< controlDict_.name() << nl;
debug::optimisationObjects().setValues(*localDict, true);
}
// Handle fileHandler explicitly since it affects local dictionary
// monitoring.
word fileHandlerName;
if
(
localDict
&& localDict->readIfPresent("fileHandler", fileHandlerName)
&& fileHandler().type() != fileHandlerName
)
{
DetailInfo << "Overriding fileHandler to " << fileHandlerName << nl;
// Remove old watches since destroying the file
fileNameList oldWatched(controlDict_.watchIndices().size());
forAllReverse(controlDict_.watchIndices(), i)
{
const label watchi = controlDict_.watchIndices()[i];
oldWatched[i] = fileHandler().getFile(watchi);
fileHandler().removeWatch(watchi);
}
controlDict_.watchIndices().clear();
// Reporting verbosity corresponding to detail level
const bool verbose = (::Foam::infoDetailLevel > 0);
// The new handler
refPtr newHandler
(
fileOperation::New(fileHandlerName, verbose)
);
// Install the new handler
(void) fileOperation::fileHandler(newHandler);
if (TimePaths::distributed())
{
newHandler->distributed(true);
}
// Reinstall old watches
fileHandler().addWatches(controlDict_, oldWatched);
}
// DimensionedConstants.
// - special case since it may change both the 'unitSet' and the
// individual values
if
(
(localDict = controlDict_.findDict("DimensionedConstants")) != nullptr
&& localDict->size()
)
{
DetailInfo
<< "Overriding DimensionedConstants according to "
<< controlDict_.name() << nl;
simpleObjectRegistry& objs = debug::dimensionedConstantObjects();
// Change in-memory
dimensionedConstants().merge(*localDict);
IStringStream dummyIs("");
// Reporting verbosity corresponding to detail level
const bool verbose = (::Foam::infoDetailLevel > 0);
forAllConstIters(objs, iter)
{
const List& objects = *iter;
for (simpleRegIOobject* obj : objects)
{
obj->readData(dummyIs);
if (verbose)
{
Info<< " ";
obj->writeData(Info);
Info<< nl;
}
}
}
}
// DimensionSets
if
(
(localDict = controlDict_.findDict("DimensionSets")) != nullptr
&& localDict->size()
)
{
DetailInfo
<< "Overriding DimensionSets according to "
<< controlDict_.name() << nl;
simpleObjectRegistry& objs = debug::dimensionSetObjects();
dictionary dict(Foam::dimensionSystems());
dict.merge(*localDict);
simpleObjectRegistryEntry* objPtr = objs.find("DimensionSets");
if (objPtr)
{
DetailInfo << *localDict << nl;
const List& objects = *objPtr;
for (simpleRegIOobject* obj : objects)
{
OStringStream os;
os << dict;
IStringStream is(os.str());
obj->readData(is);
}
}
}
if (!deltaTchanged_)
{
controlDict_.readEntry("deltaT", deltaT_);
}
writeControlNames.readIfPresent
(
"writeControl",
controlDict_,
writeControl_
);
scalar oldWriteInterval = writeInterval_;
if (controlDict_.readIfPresent("writeInterval", writeInterval_))
{
if (writeControl_ == wcTimeStep && label(writeInterval_) < 1)
{
FatalIOErrorInFunction(controlDict_)
<< "writeInterval < 1 for writeControl timeStep"
<< exit(FatalIOError);
}
}
else
{
controlDict_.readEntry("writeFrequency", writeInterval_);
}
if (oldWriteInterval != writeInterval_)
{
switch (writeControl_)
{
case wcRunTime:
case wcAdjustableRunTime:
// Recalculate writeTimeIndex_ to be in units of current
// writeInterval.
writeTimeIndex_ = label
(
writeTimeIndex_
* oldWriteInterval
/ writeInterval_
);
break;
default:
break;
}
}
if (controlDict_.readIfPresent("purgeWrite", purgeWrite_))
{
if (purgeWrite_ < 0)
{
WarningInFunction
<< "invalid value for purgeWrite " << purgeWrite_
<< ", should be >= 0, setting to 0"
<< endl;
purgeWrite_ = 0;
}
}
if (controlDict_.found("timeFormat"))
{
const word formatName(controlDict_.get("timeFormat"));
if (formatName == "general")
{
format_ = fmtflags::general;
}
else if (formatName == "fixed")
{
format_ = fmtflags::fixed;
}
else if (formatName == "scientific")
{
format_ = fmtflags::scientific;
}
else
{
WarningInFunction
<< "Unsupported time format " << formatName
<< endl;
}
}
controlDict_.readIfPresent("timePrecision", precision_);
// stopAt at 'endTime' or a specified value
// if nothing is specified, the endTime is zero
if (stopAtControlNames.readIfPresent("stopAt", controlDict_, stopAt_))
{
if (stopAt_ == saEndTime)
{
controlDict_.readEntry("endTime", endTime_);
}
else
{
endTime_ = GREAT;
}
}
else if (!controlDict_.readIfPresent("endTime", endTime_))
{
endTime_ = 0;
}
// Adjust the TimeState name
dimensionedScalar::name() = timeName(value());
if (controlDict_.found("writeVersion"))
{
writeStreamOption_.version(controlDict_.get("writeVersion"));
}
if (controlDict_.found("writeFormat"))
{
writeStreamOption_.format(controlDict_.get("writeFormat"));
}
if (controlDict_.found("writePrecision"))
{
IOstream::defaultPrecision
(
controlDict_.get("writePrecision")
);
Sout.precision(IOstream::defaultPrecision());
Serr.precision(IOstream::defaultPrecision());
Pout.precision(IOstream::defaultPrecision());
Perr.precision(IOstream::defaultPrecision());
FatalError.stream().precision(IOstream::defaultPrecision());
FatalIOError.stream().precision(IOstream::defaultPrecision());
}
if (controlDict_.found("writeCompression"))
{
writeStreamOption_.compression
(
controlDict_.get("writeCompression")
);
if (writeStreamOption_.compression() == IOstreamOption::COMPRESSED)
{
if (writeStreamOption_.format() != IOstreamOption::ASCII)
{
IOWarningInFunction(controlDict_)
<< "Disabled output compression for non-ascii format"
<< " (inefficient/ineffective)"
<< endl;
writeStreamOption_.compression(IOstreamOption::UNCOMPRESSED);
}
else if (!ofstreamPointer::supports_gz())
{
IOWarningInFunction(controlDict_)
<< "Disabled output compression"
<< " (missing libz support)"
<< endl;
writeStreamOption_.compression(IOstreamOption::UNCOMPRESSED);
}
}
}
controlDict_.readIfPresent("graphFormat", graphFormat_);
controlDict_.readIfPresent("runTimeModifiable", runTimeModifiable_);
if (!runTimeModifiable_ && controlDict_.watchIndices().size())
{
forAllReverse(controlDict_.watchIndices(), i)
{
fileHandler().removeWatch(controlDict_.watchIndices()[i]);
}
controlDict_.watchIndices().clear();
}
}
bool Foam::Time::read()
{
if (controlDict_.regIOobject::read())
{
// Read contents
readDict();
functionObjects_.read();
if (runTimeModifiable_)
{
// For IOdictionary the call to regIOobject::read() would have
// already updated all the watchIndices via the addWatch but
// controlDict_ is an unwatchedIOdictionary so will only have
// stored the dependencies as files.
fileHandler().addWatches(controlDict_, controlDict_.files());
}
controlDict_.files().clear();
return true;
}
return false;
}
void Foam::Time::readModifiedObjects()
{
if (runTimeModifiable_)
{
// Get state of all monitored objects (=registered objects with a
// valid filePath).
// Note: requires same ordering in objectRegistries on different
// processors!
fileHandler().updateStates
(
(
IOobject::fileModificationChecking == IOobject::inotifyMaster
|| IOobject::fileModificationChecking == IOobject::timeStampMaster
),
Pstream::parRun()
);
// Time handling is special since controlDict_ is the one dictionary
// that is not registered to any database.
if (controlDict_.readIfModified())
{
readDict();
functionObjects_.read();
if (runTimeModifiable_)
{
// For IOdictionary the call to regIOobject::read() would have
// already updated all the watchIndices via the addWatch but
// controlDict_ is an unwatchedIOdictionary so will only have
// stored the dependencies as files.
fileHandler().addWatches(controlDict_, controlDict_.files());
}
controlDict_.files().clear();
}
bool registryModified = objectRegistry::modified();
if (registryModified)
{
objectRegistry::readModifiedObjects();
}
}
}
bool Foam::Time::writeTimeDict() const
{
addProfiling(writing, "objectRegistry::writeObject");
const word tmName(timeName());
IOdictionary timeDict
(
IOobject
(
"time",
tmName,
"uniform",
*this,
IOobjectOption::NO_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER
)
);
timeDict.add("value", timeName(timeToUserTime(value()), maxPrecision_));
timeDict.add("name", string(tmName));
timeDict.add("index", timeIndex_);
timeDict.add("deltaT", timeToUserTime(deltaT_));
timeDict.add("deltaT0", timeToUserTime(deltaT0_));
return timeDict.regIOobject::writeObject
(
IOstreamOption(IOstreamOption::ASCII),
true
);
}
bool Foam::Time::writeObject
(
IOstreamOption streamOpt,
const bool writeOnProc
) const
{
if (writeTime())
{
bool writeOK = writeTimeDict();
if (writeOK)
{
writeOK = objectRegistry::writeObject(streamOpt, writeOnProc);
}
if (writeOK)
{
// Does the writeTime trigger purging?
if (writeTime_ && purgeWrite_)
{
if
(
previousWriteTimes_.empty()
|| previousWriteTimes_.top() != timeName()
)
{
previousWriteTimes_.push(timeName());
}
while (previousWriteTimes_.size() > purgeWrite_)
{
fileHandler().rmDir
(
fileHandler().filePath
(
objectRegistry::path(previousWriteTimes_.pop()),
false // No .gz check (is directory)
)
);
}
}
}
return writeOK;
}
return false;
}
bool Foam::Time::writeNow()
{
writeTime_ = true;
return write();
}
bool Foam::Time::writeAndEnd()
{
stopAt_ = saWriteNow;
endTime_ = value();
return writeNow();
}
void Foam::Time::writeOnce()
{
writeOnce_ = true;
}
Foam::Ostream& Foam::Time::printExecutionTime(OSstream& os) const
{
switch (printExecutionFormat_)
{
case 1:
{
os << "ExecutionTime = ";
printTimeHMS(os.stdStream(), elapsedCpuTime());
os << " ClockTime = ";
printTimeHMS(os.stdStream(), elapsedClockTime());
}
break;
default:
{
os << "ExecutionTime = " << elapsedCpuTime() << " s"
<< " ClockTime = " << elapsedClockTime() << " s";
}
break;
}
os << nl << endl;
return os;
}
// ************************************************************************* //