/*---------------------------------------------------------------------------*\ ========= | \\ / 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) 2015-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 . \*---------------------------------------------------------------------------*/ #include "functionObjectList.H" #include "Time.H" #include "mapPolyMesh.H" #include "profiling.H" #include "argList.H" #include "timeControlFunctionObject.H" #include "dictionaryEntry.H" #include "stringOps.H" #include "Tuple2.H" #include "etcFiles.H" #include "IOdictionary.H" /* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */ Foam::fileName Foam::functionObjectList::functionObjectDictPath ( "caseDicts/postProcessing" ); // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * // void Foam::functionObjectList::createStateDict() const { // Cannot set the state dictionary on construction since Time has not // been fully initialised stateDictPtr_.reset ( new IOdictionary ( IOobject ( "functionObjectProperties", time_.timeName(), "uniform"/word("functionObjects"), time_, IOobject::READ_IF_PRESENT, IOobject::NO_WRITE ) ) ); } void Foam::functionObjectList::createOutputRegistry() const { objectsRegistryPtr_.reset ( new objectRegistry ( IOobject ( "functionObjectObjects", time_.timeName(), time_, IOobject::NO_READ, IOobject::NO_WRITE ) ) ); } Foam::autoPtr Foam::functionObjectList::remove ( const word& key, label& oldIndex ) { autoPtr oldptr; auto iter = indices_.find(key); // Index of existing functionObject if (iter.found()) { oldIndex = *iter; // Remove pointer from the old list oldptr = this->release(oldIndex); indices_.erase(iter); } else { oldIndex = -1; } return oldptr; } void Foam::functionObjectList::listDir ( const fileName& dir, wordHashSet& available ) { // Search specified directory for functionObject configuration files for (const fileName& f : fileHandler().readDir(dir)) { if (f.ext().empty()) { available.insert(f); } } // Recurse into sub-directories for (const fileName& d : fileHandler().readDir(dir, fileName::DIRECTORY)) { listDir(dir/d, available); } } void Foam::functionObjectList::list() { wordHashSet available; for (const fileName& d : findEtcDirs(functionObjectDictPath)) { listDir(d, available); } Info<< nl << "Available configured functionObjects:" << available.sortedToc() << nl; } Foam::fileName Foam::functionObjectList::findDict(const word& funcName) { // First check for functionObject dictionary file in globalCase system/ fileName dictFile = stringOps::expand("")/funcName; if (isFile(dictFile)) { return dictFile; } for (const fileName& d : findEtcDirs(functionObjectDictPath)) { dictFile = search(funcName, d); if (!dictFile.empty()) { return dictFile; } } return fileName::null; } bool Foam::functionObjectList::readFunctionObject ( const string& funcNameArgs, dictionary& functionsDict, HashSet& requiredFields, const word& region ) { // Parse the optional functionObject arguments: // 'Q(U)' -> funcName = Q; args = (U); field = U // // Supports named arguments: // 'patchAverage(patch=inlet, p)' -> funcName = patchAverage; // args = (patch=inlet, p); field = p word funcName(funcNameArgs); int argLevel = 0; wordRes args; List> namedArgs; bool hasNamedArg = false; word argName; word::size_type start = 0; word::size_type i = 0; for ( word::const_iterator iter = funcNameArgs.begin(); iter != funcNameArgs.end(); ++iter ) { char c = *iter; if (c == '(') { if (argLevel == 0) { funcName = funcNameArgs.substr(start, i - start); start = i+1; } ++argLevel; } else if (c == ',' || c == ')') { if (argLevel == 1) { if (hasNamedArg) { namedArgs.append ( Tuple2 ( argName, funcNameArgs.substr(start, i - start) ) ); hasNamedArg = false; } else { args.append ( wordRe ( word::validate ( funcNameArgs.substr(start, i - start) ) ) ); } start = i+1; } if (c == ')') { if (argLevel == 1) { break; } --argLevel; } } else if (c == '=') { argName = word::validate ( funcNameArgs.substr(start, i - start) ); start = i+1; hasNamedArg = true; } ++i; } // Search for the functionObject dictionary fileName path = functionObjectList::findDict(funcName); if (path == fileName::null) { WarningInFunction << "Cannot find functionObject file " << funcName << endl; return false; } // Read the functionObject dictionary autoPtr fileStreamPtr(fileHandler().NewIFstream(path)); ISstream& fileStream = fileStreamPtr(); dictionary funcsDict(fileStream); dictionary* funcDictPtr = funcsDict.findDict(funcName); dictionary& funcDict = (funcDictPtr ? *funcDictPtr : funcsDict); // Insert the 'field' and/or 'fields' entry corresponding to the optional // arguments or read the 'field' or 'fields' entry and add the required // fields to requiredFields if (args.size() == 1) { funcDict.set("field", args[0]); funcDict.set("fields", args); requiredFields.insert(args[0]); } else if (args.size() > 1) { funcDict.set("fields", args); requiredFields.insert(args); } else if (funcDict.found("field")) { requiredFields.insert(funcDict.get("field")); } else if (funcDict.found("fields")) { requiredFields.insert(funcDict.get("fields")); } // Insert named arguments for (const Tuple2& namedArg : namedArgs) { IStringStream entryStream ( namedArg.first() + ' ' + namedArg.second() + ';' ); funcDict.set(entry::New(entryStream).ptr()); } // Insert the region name if specified if (!region.empty()) { funcDict.set("region", region); } // Merge this functionObject dictionary into functionsDict dictionary funcArgsDict; funcArgsDict.add(word::validate(funcNameArgs), funcDict); functionsDict.merge(funcArgsDict); return true; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::functionObjectList::functionObjectList ( const Time& runTime, const bool execution ) : PtrList(), digests_(), indices_(), time_(runTime), parentDict_(runTime.controlDict()), stateDictPtr_(), objectsRegistryPtr_(), execution_(execution), updated_(false) {} Foam::functionObjectList::functionObjectList ( const Time& runTime, const dictionary& parentDict, const bool execution ) : PtrList(), digests_(), indices_(), time_(runTime), parentDict_(parentDict), stateDictPtr_(), objectsRegistryPtr_(), execution_(execution), updated_(false) {} Foam::autoPtr Foam::functionObjectList::New ( const argList& args, const Time& runTime, dictionary& controlDict, HashSet& requiredFields ) { // Merge any functions from the provided controlDict controlDict.add ( dictionaryEntry("functions", controlDict, dictionary::null), true ); dictionary& functionsDict = controlDict.subDict("functions"); const word regionName = args.getOrDefault("region", ""); bool modifiedControlDict = false; if (args.found("dict")) { modifiedControlDict = true; controlDict.merge ( IOdictionary ( IOobject ( args["dict"], runTime, IOobject::MUST_READ_IF_MODIFIED ) ) ); } if (args.found("func")) { modifiedControlDict = true; readFunctionObject ( args["func"], functionsDict, requiredFields, regionName ); } if (args.found("funcs")) { modifiedControlDict = true; wordList funcNames = args.getList("funcs"); for (const word& funcName : funcNames) { readFunctionObject ( funcName, functionsDict, requiredFields, regionName ); } } autoPtr functionsPtr; if (modifiedControlDict) { functionsPtr.reset(new functionObjectList(runTime, controlDict)); } else { functionsPtr.reset(new functionObjectList(runTime)); } functionsPtr->start(); return functionsPtr; } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::label Foam::functionObjectList::triggerIndex() const { label triggeri = labelMin; stateDict().readIfPresent("triggerIndex", triggeri); return triggeri; } void Foam::functionObjectList::resetState() { // Reset (re-read) the state dictionary stateDictPtr_.clear(); createStateDict(); } Foam::IOdictionary& Foam::functionObjectList::stateDict() { if (!stateDictPtr_.valid()) { createStateDict(); } return *stateDictPtr_; } const Foam::IOdictionary& Foam::functionObjectList::stateDict() const { if (!stateDictPtr_.valid()) { createStateDict(); } return *stateDictPtr_; } Foam::objectRegistry& Foam::functionObjectList::storedObjects() { if (!objectsRegistryPtr_.valid()) { createOutputRegistry(); } return *objectsRegistryPtr_; } const Foam::objectRegistry& Foam::functionObjectList::storedObjects() const { if (!objectsRegistryPtr_.valid()) { createOutputRegistry(); } return *objectsRegistryPtr_; } void Foam::functionObjectList::clear() { PtrList::clear(); digests_.clear(); indices_.clear(); updated_ = false; } Foam::label Foam::functionObjectList::findObjectID(const word& name) const { label id = 0; for (const functionObject& funcObj : functions()) { if (funcObj.name() == name) { return id; } ++id; } return -1; } void Foam::functionObjectList::on() { execution_ = true; } void Foam::functionObjectList::off() { // For safety, also force a read() when execution is resumed updated_ = execution_ = false; } bool Foam::functionObjectList::status() const { return execution_; } bool Foam::functionObjectList::start() { return read(); } bool Foam::functionObjectList::execute() { bool ok = true; if (execution_) { if (!updated_) { read(); } for (functionObject& funcObj : functions()) { const word& objName = funcObj.name(); { addProfiling(fo, "functionObject::" + objName + "::execute"); ok = funcObj.execute() && ok; } { addProfiling(fo, "functionObject::" + objName + "::write"); ok = funcObj.write() && ok; } } } // Force writing of state dictionary after function object execution if (time_.writeTime()) { label oldPrecision = IOstream::precision_; IOstream::precision_ = 16; stateDictPtr_->writeObject ( IOstreamOption(IOstream::ASCII, time_.writeCompression()), true ); IOstream::precision_ = oldPrecision; } return ok; } bool Foam::functionObjectList::execute(const label subIndex) { bool ok = execution_; if (ok) { for (functionObject& funcObj : functions()) { ok = funcObj.execute(subIndex) && ok; } } return ok; } bool Foam::functionObjectList::execute ( const UList& functionNames, const label subIndex ) { bool ok = execution_; if (ok && functionNames.size()) { for (functionObject& funcObj : functions()) { if (stringOps::match(functionNames, funcObj.name())) { ok = funcObj.execute(subIndex) && ok; } } } return ok; } bool Foam::functionObjectList::end() { bool ok = true; if (execution_) { if (!updated_) { read(); } for (functionObject& funcObj : functions()) { const word& objName = funcObj.name(); addProfiling(fo, "functionObject::" + objName + "::end"); ok = funcObj.end() && ok; } } return ok; } bool Foam::functionObjectList::adjustTimeStep() { bool ok = true; if (execution_) { if (!updated_) { read(); } for (functionObject& funcObj : functions()) { const word& objName = funcObj.name(); addProfiling(fo, "functionObject::" + objName + "::adjustTimeStep"); ok = funcObj.adjustTimeStep() && ok; } } return ok; } bool Foam::functionObjectList::read() { if (!stateDictPtr_.valid()) { createStateDict(); } updated_ = execution_; // Avoid reading/initializing if execution is off if (!execution_) { return true; } // Update existing and add new functionObjects const entry* entryPtr = parentDict_.findEntry("functions", keyType::LITERAL); bool ok = true; if (!entryPtr) { // No functions PtrList::clear(); digests_.clear(); indices_.clear(); } else if (!entryPtr->isDict()) { // Bad entry type ok = false; FatalIOErrorInFunction(parentDict_) << "'functions' entry is not a dictionary" << exit(FatalIOError); } else { const dictionary& functionsDict = entryPtr->dict(); PtrList newPtrs(functionsDict.size()); List newDigs(functionsDict.size()); HashTable