/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | Copyright (C) 2015-2017 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 "PstreamReduceOps.H" #include "argList.H" #include "HashSet.H" #include "profiling.H" #include // * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(Time, 0); template<> const char* Foam::NamedEnum < Foam::Time::stopAtControls, 4 >::names[] = { "endTime", "noWriteNow", "writeNow", "nextWrite" }; template<> const char* Foam::NamedEnum < Foam::Time::writeControls, 5 >::names[] = { "timeStep", "runTime", "adjustableRunTime", "clockTime", "cpuTime" }; } const Foam::NamedEnum Foam::Time::stopAtControlNames_; const Foam::NamedEnum Foam::Time::writeControlNames_; Foam::Time::fmtflags Foam::Time::format_(Foam::Time::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() { bool adjustTime = false; scalar timeToNextWrite = VGREAT; if (writeControl_ == wcAdjustableRunTime) { adjustTime = true; timeToNextWrite = max ( 0.0, (writeTimeIndex_ + 1)*writeInterval_ - (value() - startTime_) ); } if (adjustTime) { scalar nSteps = timeToNextWrite/deltaT_; // For tiny deltaT the label can overflow! if (nSteps < labelMax) { // nSteps can be < 1 so make sure at least 1 label nStepsToNextWrite = max(1, round(nSteps)); scalar newDeltaT = timeToNextWrite/nStepsToNextWrite; // Control the increase of the time step to within a factor of 2 // and the decrease within a factor of 5. if (newDeltaT >= deltaT_) { deltaT_ = min(newDeltaT, 2.0*deltaT_); } else { deltaT_ = max(newDeltaT, 0.2*deltaT_); } } } functionObjects_.adjustTimeStep(); } void Foam::Time::setControls() { // default is to resume calculation from "latestTime" const word startFrom = controlDict_.lookupOrDefault ( "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 (!exists(timePath(), false)) { int oldPrecision = precision_; int requiredPrecision = -1; bool found = false; for ( precision_ = maxPrecision_; precision_ > oldPrecision; precision_-- ) { // Update the time formatting setTime(startTime_, 0); // Check the existence of the time directory with the new format found = exists(timePath(), 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()); 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("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; } } } } void Foam::Time::setMonitoring(const bool forceProfiling) { const dictionary* profilingDict = controlDict_.subDictPtr("profiling"); if (!profilingDict) { // ... or from etc/controlDict profilingDict = debug::controlDict().subDictPtr("profiling"); } // initialize profiling on request // otherwise rely on profiling entry within controlDict // and skip if 'active' keyword is explicitly set to false if (forceProfiling) { profiling::initialize ( IOobject ( "profiling", timeName(), "uniform", *this, IOobject::NO_READ, IOobject::AUTO_WRITE ), *this ); } else if ( profilingDict && profilingDict->lookupOrDefault("active", true) ) { profiling::initialize ( *profilingDict, IOobject ( "profiling", timeName(), "uniform", *this, IOobject::NO_READ, IOobject::AUTO_WRITE ), *this ); } // Time objects not registered so do like objectRegistry::checkIn ourselves. if (runTimeModifiable_) { monitorPtr_.reset ( new fileMonitor ( regIOobject::fileModificationChecking == inotify || regIOobject::fileModificationChecking == inotifyMaster ) ); // Monitor all files that controlDict depends on addWatches(controlDict_, controlDict_.files()); } // Clear dependent files - not needed now controlDict_.files().clear(); } // * * * * * * * * * * * * * * * * 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_(saEndTime), writeControl_(wcTimeStep), 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), 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(); setMonitoring(); } Foam::Time::Time ( const word& controlDictName, const argList& args, const word& systemName, const word& constantName ) : TimePaths ( args.parRunControl().parRun(), args.rootPath(), args.distributed(), 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_(saEndTime), writeControl_(wcTimeStep), 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), functionObjects_ ( *this, argList::validOptions.found("withFunctionObjects") ? args.optionFound("withFunctionObjects") : argList::validOptions.found("noFunctionObjects") ? !args.optionFound("noFunctionObjects") : false ) { 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(); // '-profiling' = force profiling, ignore controlDict entry setMonitoring(args.optionFound("profiling")); } 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_(saEndTime), writeControl_(wcTimeStep), 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), 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(); setMonitoring(); } 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_(saEndTime), writeControl_(wcTimeStep), writeInterval_(GREAT), purgeWrite_(0), writeOnce_(false), subCycling_(false), writeFormat_(IOstream::ASCII), writeVersion_(IOstream::currentVersion), writeCompression_(IOstream::UNCOMPRESSED), graphFormat_("raw"), runTimeModifiable_(false), functionObjects_(*this, enableFunctionObjects) { libs_.open(controlDict_, "libs"); setMonitoring(); // for profiling etc } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::Time::~Time() { forAllReverse(controlDict_.watchIndices(), i) { removeWatch(controlDict_.watchIndices()[i]); } // Destroy function objects first functionObjects_.clear(); // Clean up profiling profiling::stop(*this); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::Time::addWatches(regIOobject& rio, const fileNameList& files) const { const labelList& watchIndices = rio.watchIndices(); DynamicList