Skip to content
Snippets Groups Projects
functionObjectList.C 31.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
    OpenFOAM bot's avatar
    OpenFOAM bot committed
        \\  /    A nd           | www.openfoam.com
    
         \\/     M anipulation  |
    -------------------------------------------------------------------------------
    
    OpenFOAM bot's avatar
    OpenFOAM bot committed
        Copyright (C) 2011-2017 OpenFOAM Foundation
    
        Copyright (C) 2015-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 <http://www.gnu.org/licenses/>.
    
    
    \*---------------------------------------------------------------------------*/
    
    #include "functionObjectList.H"
    #include "Time.H"
    
    #include "mapPolyMesh.H"
    
    #include "profiling.H"
    
    #include "timeControlFunctionObject.H"
    
    #include "dictionaryEntry.H"
    #include "stringOps.H"
    
    #include "Tuple2.H"
    
    #include "Pstream.H"
    #include "OSspecific.H"
    
    
    /* * * * * * * * * * * * * * * Static Member Data  * * * * * * * * * * * * * */
    
    
    //- Max number of warnings (per functionObject)
    
    static constexpr const unsigned maxWarnings = 10u;
    
    Foam::fileName Foam::functionObjectList::functionObjectDictPath
    (
        "caseDicts/postProcessing"
    );
    
    
    // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
    
    namespace Foam
    {
        //- Mimic exit handling of the error class
        static void exitNow(const error& err)
        {
    
            {
                Perr<< nl << err << nl
                    << "\nFOAM aborting (FOAM_ABORT set)\n" << endl;
                error::printStack(Perr);
                std::abort();
            }
    
            {
                Perr<< nl << err << nl
                    << "\nFOAM parallel run exiting\n" << endl;
    
            }
            else
            {
                Perr<< nl << err << nl
                    << "\nFOAM exiting\n" << endl;
                std::exit(1);
            }
        }
    
    } // End namespace Foam
    
    
    
    // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
    
    
    void Foam::functionObjectList::createPropertiesDict() const
    
        // Cannot set the properties dictionary on construction since Time has not
    
        // been fully initialised
    
        propsDictPtr_.reset
    
            new functionObjects::properties
    
            (
                IOobject
                (
                    "functionObjectProperties",
                    time_.timeName(),
                    "uniform"/word("functionObjects"),
                    time_,
                    IOobject::READ_IF_PRESENT,
    
                    IOobject::NO_WRITE,
                    IOobject::REGISTER
    
    void Foam::functionObjectList::createOutputRegistry() const
    {
        objectsRegistryPtr_.reset
        (
            new objectRegistry
            (
                IOobject
                (
                    "functionObjectObjects",
                    time_.timeName(),
                    time_,
                    IOobject::NO_READ,
    
                    IOobject::NO_WRITE,
                    IOobject::REGISTER
    
    Foam::autoPtr<Foam::functionObject> Foam::functionObjectList::remove
    
    (
        const word& key,
        label& oldIndex
    )
    
        autoPtr<functionObject> oldptr;
    
        auto iter = indices_.find(key);  // Index of existing functionObject
    
            oldIndex = *iter;
    
            // Remove pointer from the old list
    
            oldptr = this->release(oldIndex);
    
            indices_.erase(iter);
    
        return oldptr;
    
    void Foam::functionObjectList::listDir
    (
        const fileName& dir,
    
    )
    {
        // Search specified directory for functionObject configuration files
    
        for (const fileName& f : fileHandler().readDir(dir))
    
        for (const fileName& d : fileHandler().readDir(dir, fileName::DIRECTORY))
    
        for (const fileName& d : findEtcDirs(functionObjectDictPath))
    
    Foam::fileName Foam::functionObjectList::findDict(const word& funcName)
    {
    
        // First check for functionObject dictionary file in globalCase system/
    
        fileName dictFile = stringOps::expand("<system>")/funcName;
    
        for (const fileName& d : findEtcDirs(functionObjectDictPath))
    
        {
            dictFile = search(funcName, d);
            if (!dictFile.empty())
    
                return dictFile;
    
    bool Foam::functionObjectList::readFunctionObject
    
        const string& funcNameArgs,
    
        HashSet<wordRe>& requiredFields,
    
        // 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
    
        List<Tuple2<word, string>> namedArgs;
    
    
            const auto argsBeg = funcNameArgs.find('(');
            if (argsBeg == std::string::npos)
    
                // Function name only, no args
                funcName = word::validate(funcNameArgs);
    
                // Leading function name
                funcName = word::validate(funcNameArgs.substr(0, argsBeg));
    
                const auto argsEnd = funcNameArgs.rfind(')');
    
                stringOps::splitFunctionArgs
    
                    funcNameArgs.substr
                    (
                        (argsBeg + 1),
                        (
                            (argsEnd != std::string::npos && argsBeg < argsEnd)
                          ? (argsEnd - argsBeg - 1)
                          : std::string::npos
                        )
                    ),
                    args,
                    namedArgs
    
        // Search for the functionObject dictionary
    
        fileName path = functionObjectList::findDict(funcName);
    
        {
            WarningInFunction
                << "Cannot find functionObject file " << funcName << endl;
    
        }
    
        // Read the functionObject dictionary
    
        autoPtr<ISstream> 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
    
        if (args.size() == 1)
        {
            funcDict.set("field", args[0]);
    
            funcDict.set("fields", args);
    
        }
        else if (args.size() > 1)
        {
            funcDict.set("fields", args);
    
            requiredFields.insert(funcDict.get<wordRe>("field"));
    
        }
        else if (funcDict.found("fields"))
        {
    
            requiredFields.insert(funcDict.get<wordRes>("fields"));
    
        // Insert named arguments
    
        for (const Tuple2<word, string>& namedArg : namedArgs)
    
        {
            IStringStream entryStream
            (
    
                namedArg.first() + ' ' + namedArg.second() + ';'
    
            funcDict.set(entry::New(entryStream).ptr());
        }
    
    
        // Merge this functionObject dictionary into functionsDict
        dictionary funcArgsDict;
    
        funcArgsDict.add(word::validate(funcNameArgs), funcDict);
    
        functionsDict.merge(funcArgsDict);
    
        return true;
    
    Foam::functionObjectList::getOrDefaultErrorHandling
    (
        const word& key,
        const dictionary& dict,
    
    ) const
    {
        const entry* eptr = dict.findEntry(key, keyType::LITERAL);
    
        if (eptr)
        {
            if (eptr->isDict())
            {
                Warning
                    << "The sub-dictionary '" << key
                    << "' masks error handling for functions" << endl;
            }
            else
            {
                const word enumName(eptr->get<word>());
    
    
                if (!error::handlerNames.found(enumName))
    
                {
                    // Failed the name lookup
                    FatalIOErrorInFunction(dict)
                        << enumName << " is not in enumeration: "
    
                return error::handlerNames.get(enumName);
    
    // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
    
    Foam::functionObjectList::functionObjectList
    (
    
        const Time& runTime,
    
        functionObjectList(runTime, runTime.controlDict(), execution)
    
    {}
    
    
    Foam::functionObjectList::functionObjectList
    (
    
        const Time& runTime,
    
        const dictionary& parentDict,
    
        PtrList<functionObject>(),
    
        parentDict_(parentDict),
    
        propsDictPtr_(nullptr),
    
        execution_(execution),
        updated_(false)
    
    Foam::autoPtr<Foam::functionObjectList> Foam::functionObjectList::New
    (
        const argList& args,
        const Time& runTime,
    
        HashSet<wordRe>& requiredFields
    
        // Merge any functions from the provided controlDict
    
            dictionaryEntry("functions", controlDict, dictionary::null),
            true
    
        dictionary& functionsDict = controlDict.subDict("functions");
    
    
        const word regionName = args.getOrDefault<word>("region", "");
    
        bool modifiedControlDict = false;
    
            modifiedControlDict = true;
    
            controlDict.merge
            (
                IOdictionary
    
                        args["dict"],
                        runTime,
    
                        IOobject::MUST_READ_IF_MODIFIED,
                        IOobject::NO_WRITE,
                        IOobject::REGISTER
    
        {
            modifiedControlDict = true;
    
            readFunctionObject
            (
                args["func"],
                functionsDict,
                requiredFields,
    
        {
            modifiedControlDict = true;
    
            for (const word& funcName : args.getList<word>("funcs"))
    
        autoPtr<functionObjectList> functionsPtr;
    
        if (modifiedControlDict)
        {
    
            functionsPtr.reset(new functionObjectList(runTime, controlDict));
    
            functionsPtr.reset(new functionObjectList(runTime));
    
        functionsPtr->start();
    
        return functionsPtr;
    
    // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
    
    
    Foam::label Foam::functionObjectList::triggerIndex() const
    {
    
        return propsDict().getTrigger();
    
    void Foam::functionObjectList::resetPropertiesDict()
    
        // Reset (re-read) the properties dictionary
        propsDictPtr_.reset(nullptr);
        createPropertiesDict();
    
    Foam::functionObjects::properties& Foam::functionObjectList::propsDict()
    
        if (!propsDictPtr_)
    
            createPropertiesDict();
    
        return *propsDictPtr_;
    
    const Foam::functionObjects::properties&
    Foam::functionObjectList::propsDict() const
    
        if (!propsDictPtr_)
    
            createPropertiesDict();
    
        return *propsDictPtr_;
    
    Foam::objectRegistry& Foam::functionObjectList::storedObjects()
    {
    
        if (!objectsRegistryPtr_)
    
        {
            createOutputRegistry();
        }
    
        return *objectsRegistryPtr_;
    }
    
    
    const Foam::objectRegistry& Foam::functionObjectList::storedObjects() const
    {
    
        if (!objectsRegistryPtr_)
    
    void Foam::functionObjectList::clear()
    {
        PtrList<functionObject>::clear();
    
        digests_.clear();
        indices_.clear();
    
    Foam::label Foam::functionObjectList::findObjectID(const word& objName) const
    
        label id = 0;
    
        for (const functionObject& funcObj : functions())
    
            if (funcObj.name() == objName)
    
    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 writeProperties)
    
            if (!updated_)
    
            auto errIter = errorHandling_.cbegin();
    
    
            for (functionObject& funcObj : functions())
    
                const auto errorHandling = *errIter;
    
                const word& objName = funcObj.name();
    
                    errorHandling == error::handlerTypes::WARN
                 || errorHandling == error::handlerTypes::IGNORE
    
                    // Throw FatalError, FatalIOError as exceptions
    
                    const bool oldThrowingError = FatalError.throwing(true);
                    const bool oldThrowingIOerr = FatalIOError.throwing(true);
    
                            "functionObject::", objName, "::execute"
    
                        );
    
                        ok = funcObj.execute() && ok;
                    }
                    catch (const Foam::error& err)
                    {
                        // Treat IOerror and error identically
    
                            (errorHandling == error::handlerTypes::WARN)
    
                         && (nWarnings = ++warnings_(objName)) <= maxWarnings
                        )
                        {
                            // Trickery to get original message
                            err.write(Warning, false);
                            Info<< nl
                                << "--> execute() function object '"
                                << objName << "'";
    
                            if (nWarnings == maxWarnings)
                            {
                                Info<< nl << "... silencing further warnings";
                            }
    
                            Info<< nl << endl;
                        }
                    }
    
                    if (hadError)
                    {
                        // Restore previous state
    
                        FatalError.throwing(oldThrowingError);
                        FatalIOError.throwing(oldThrowingIOerr);
    
                            "functionObject::", objName, ":write"
    
                        );
    
                        ok = funcObj.write() && ok;
                    }
                    catch (const Foam::error& err)
                    {
                        // Treat IOerror and error identically
    
                            (errorHandling == error::handlerTypes::WARN)
    
                         && (nWarnings = ++warnings_(objName)) <= maxWarnings
                        )
                        {
                            // Trickery to get original message
                            err.write(Warning, false);
                            Info<< nl
                                << "--> write() function object '"
                                << objName << "'";
    
                            if (nWarnings == maxWarnings)
                            {
                                Info<< nl << "... silencing further warnings";
                            }
    
                            Info<< nl << endl;
                        }
                    }
    
                    FatalError.throwing(oldThrowingError);
                    FatalIOError.throwing(oldThrowingIOerr);
    
    
                    // Reset the warning counter (if any)
                    // if no errors were encountered
                    if
                    (
                        (errorHandling == error::handlerTypes::WARN)
                     && !hadError && !warnings_.empty()
                    )
                    {
                        warnings_.erase(objName);
                    }
    
                    // No special trapping of errors
    
                    // execute()
                    {
                        addProfiling
                        (
                            fo,
    
                            "functionObject::", objName, "::execute"
    
                        ok = funcObj.execute() && ok;
                    }
    
                    // write()
                    {
                        addProfiling
                        (
                            fo,
    
                            "functionObject::", objName, ":write"
    
        // Force writing of properties dictionary after function object execution
    
        if (time_.writeTime() && writeProperties)
    
            const auto oldPrecision = IOstream::precision_;
    
            IOstream::precision_ = 16;
    
    
            propsDictPtr_->writeObject
    
                IOstreamOption(IOstreamOption::ASCII, time_.writeCompression()),
    
            );
    
            IOstream::precision_ = oldPrecision;
        }
    
    
    bool Foam::functionObjectList::execute(const label subIndex)
    {
        bool ok = execution_;
    
        if (ok)
        {
    
            for (functionObject& funcObj : functions())
    
                // Probably do not need try/catch...
    
    
                ok = funcObj.execute(subIndex) && ok;
            }
        }
    
        return ok;
    }
    
    
    bool Foam::functionObjectList::execute
    (
    
        const label subIndex
    )
    {
        bool ok = execution_;
    
        if (ok && functionNames.size())
        {
    
            for (functionObject& funcObj : functions())
    
                if (wordRes::match(functionNames, funcObj.name()))
    
                    // Probably do not need try/catch...
    
    
                    ok = funcObj.execute(subIndex) && ok;
                }
            }
        }
    
        return ok;
    }
    
    
    
    bool Foam::functionObjectList::end()
    {
        bool ok = true;
    
        if (execution_)
        {
            if (!updated_)
            {
                read();
            }
    
    
            auto errIter = errorHandling_.cbegin();
    
    
            for (functionObject& funcObj : functions())
    
                const auto errorHandling = *errIter;
    
                const word& objName = funcObj.name();
    
                // Ignore failure on end() - not much we can do anyhow
    
                // Throw FatalError, FatalIOError as exceptions
    
                const bool oldThrowingError = FatalError.throwing(true);
                const bool oldThrowingIOerr = FatalIOError.throwing(true);
    
                    addProfiling(fo, "functionObject::", objName, "::end");
    
                    ok = funcObj.end() && ok;
                }
                catch (const Foam::error& err)
                {
                    // Treat IOerror and error identically
    
                    // If it somehow failed, emit a warning (unless IGNORE).
                    // Unlike execute(), do not suppress further warning messages
                    // (we want to know about rare issues)
                    // but do reset the warnings counter for the next cycle.
    
                    if (errorHandling != error::handlerTypes::IGNORE)
    
                    {
                        // Trickery to get original message
                        err.write(Warning, false);
                        Info<< nl
                            << "--> end() function object '"
    
                FatalError.throwing(oldThrowingError);
                FatalIOError.throwing(oldThrowingIOerr);
    
    
                // Reset the corresponding warning counter (if any)
                if
                (
                    (errorHandling == error::handlerTypes::WARN)
                 && !warnings_.empty()
                )
                {
                    warnings_.erase(objName);
                }
    
    bool Foam::functionObjectList::adjustTimeStep()
    {
        bool ok = true;
    
        if (execution_)
        {
            if (!updated_)
            {
                read();
            }
    
    
            for (functionObject& funcObj : functions())
    
                const word& objName = funcObj.name();
    
                // Probably do not need try/catch...
    
                addProfiling
                (
                    fo,
    
                    "functionObject::", objName, "::adjustTimeStep"
    
                ok = funcObj.adjustTimeStep() && ok;
    
    bool Foam::functionObjectList::read()
    {
    
        if (!propsDictPtr_)
    
            createPropertiesDict();
    
        updated_ = execution_;
    
    
        if (!execution_)
        {
    
        // Update existing and add new functionObjects
    
        const entry* entryPtr =
            parentDict_.findEntry("functions", keyType::LITERAL);
    
        bool ok = true;
    
        if (!entryPtr)
        {
            // No functions
            PtrList<functionObject>::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<functionObject> newPtrs(functionsDict.size());
            List<SHA1Digest> newDigs(functionsDict.size());
    
    
            errorHandling_.resize
            (
                functionsDict.size(),
    
            HashTable<label> newIndices;
    
            addProfiling(fo, "functionObjects::read");
    
            // Top-level "libs" specification (optional)
    
            (
                functionsDict,
                "libs",
                functionObject::dictionaryConstructorTablePtr_
            );