From fc800aea5f27d591e7120399c3a5a22a3af6c616 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Wed, 5 Aug 2020 17:16:54 +0200
Subject: [PATCH] ENH: finer granularity for handling functionObject failure
 (#1779)

- additional "errors" entry with enumerated values
  (default|warn|ignore|strict) for defining warning or error at
  construct or runtime stage

- default : construct = warn, runtime = fatal
- warn    : construct = warn, runtime = warn
- ignore  : construct = silent, runtime = silent
- strict  : construct = fatal, runtime = fatal

The errors control can be added at the top-level and/or for individual
function objects.
---
 .../functionObject/functionObject.H           |  18 +-
 .../functionObjectList/functionObjectList.C   | 479 ++++++++++++++----
 .../functionObjectList/functionObjectList.H   |  84 ++-
 3 files changed, 480 insertions(+), 101 deletions(-)

diff --git a/src/OpenFOAM/db/functionObjects/functionObject/functionObject.H b/src/OpenFOAM/db/functionObjects/functionObject/functionObject.H
index dd43486501a..50b90f75d3b 100644
--- a/src/OpenFOAM/db/functionObjects/functionObject/functionObject.H
+++ b/src/OpenFOAM/db/functionObjects/functionObject/functionObject.H
@@ -56,13 +56,15 @@ Description
     sub-dictionary, typically as in the following example:
 
     \verbatim
-    functions    // sub-dictionary name under the system/controlDict file
+    functions   // sub-dictionary name under the system/controlDict file
     {
-        <userDefinedSubDictName1>
+        ..optional entries..
+
+        <dictName1>
         {
             // Mandatory entries
-            type                <functionObjectTypeName>;
-            libs                (<libType>FunctionObjects);
+            type    <functionObjectTypeName>;
+            libs    (<libType>FunctionObjects);
 
             // Mandatory entries defined in <functionObjectType>
             ...
@@ -82,14 +84,14 @@ Description
             writeInterval       1;
         }
 
-        <userDefinedSubDictName2>
+        <dictName2>
         {
             ...
         }
 
         ...
 
-        <userDefinedSubDictNameN>
+        <dictNameN>
         {
             ...
         }
@@ -101,6 +103,7 @@ Description
         Property | Description                            | Type | Reqd | Deflt
         type     | Type name of function object           | word |  yes  | -
         libs     | Library name(s) for implementation     | words | no  | -
+        errors   | Error handling (default/warn/ignore/strict) | word | no | inherits
         region   | Name of region for multi-region cases  | word | no | region0
         enabled  | Switch to turn function object on/off  | bool | no | true
         log     | Switch to write log info to standard output | bool | no | true
@@ -112,6 +115,9 @@ Description
         writeInterval   | Steps/time between write phases    | label | no  | 1
     \endtable
 
+    If the \c errors entry is missing, it uses the value (if any)
+    specified within the top-level functionObjectList.
+
     Time controls:
     \table
         Option            | Description
diff --git a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C
index fc54c45ff1e..f7f465065c5 100644
--- a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C
+++ b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C
@@ -37,15 +37,64 @@ License
 #include "Tuple2.H"
 #include "etcFiles.H"
 #include "IOdictionary.H"
+#include "Pstream.H"
+#include "OSspecific.H"
 
 /* * * * * * * * * * * * * * * Static Member Data  * * * * * * * * * * * * * */
 
+//- Max number of warnings (per functionObject)
+static constexpr const uint32_t maxWarnings = 10u;
+
 Foam::fileName Foam::functionObjectList::functionObjectDictPath
 (
     "caseDicts/postProcessing"
 );
 
 
+const Foam::Enum
+<
+    Foam::functionObjectList::errorHandlingType
+>
+Foam::functionObjectList::errorHandlingNames_
+({
+    { errorHandlingType::DEFAULT, "default" },
+    { errorHandlingType::WARN, "warn" },
+    { errorHandlingType::IGNORE, "ignore" },
+    { errorHandlingType::STRICT, "strict" },
+});
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    //- Mimic exit handling of the error class
+    static void exitNow(const error& err)
+    {
+        if (hasEnv("FOAM_ABORT"))
+        {
+            Perr<< nl << err << nl
+                << "\nFOAM aborting (FOAM_ABORT set)\n" << endl;
+            error::printStack(Perr);
+            std::abort();
+        }
+        else if (Pstream::parRun())
+        {
+            Perr<< nl << err << nl
+                << "\nFOAM parallel run exiting\n" << endl;
+            Pstream::exit(1);
+        }
+        else
+        {
+            Perr<< nl << err << nl
+                << "\nFOAM exiting\n" << endl;
+            std::exit(1);
+        }
+    }
+
+} // End namespace Foam
+
+
 // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
 
 void Foam::functionObjectList::createStateDict() const
@@ -282,7 +331,7 @@ bool Foam::functionObjectList::readFunctionObject
     // Search for the functionObject dictionary
     fileName path = functionObjectList::findDict(funcName);
 
-    if (path == fileName::null)
+    if (path.empty())
     {
         WarningInFunction
             << "Cannot find functionObject file " << funcName << endl;
@@ -291,7 +340,7 @@ bool Foam::functionObjectList::readFunctionObject
 
     // Read the functionObject dictionary
     autoPtr<ISstream> fileStreamPtr(fileHandler().NewIFstream(path));
-    ISstream& fileStream = fileStreamPtr();
+    ISstream& fileStream = *fileStreamPtr;
 
     dictionary funcsDict(fileStream);
     dictionary* funcDictPtr = funcsDict.findDict(funcName);
@@ -347,6 +396,45 @@ bool Foam::functionObjectList::readFunctionObject
 }
 
 
+Foam::functionObjectList::errorHandlingType
+Foam::functionObjectList::getOrDefaultErrorHandling
+(
+    const word& key,
+    const dictionary& dict,
+    const errorHandlingType deflt
+) 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 (!errorHandlingNames_.found(enumName))
+            {
+                // Failed the name lookup
+                FatalIOErrorInFunction(dict)
+                    << enumName << " is not in enumeration: "
+                    << errorHandlingNames_ << nl
+                    << exit(FatalIOError);
+            }
+
+            return errorHandlingNames_.get(enumName);
+        }
+    }
+
+    return deflt;
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::functionObjectList::functionObjectList
@@ -355,15 +443,7 @@ Foam::functionObjectList::functionObjectList
     const bool execution
 )
 :
-    PtrList<functionObject>(),
-    digests_(),
-    indices_(),
-    time_(runTime),
-    parentDict_(runTime.controlDict()),
-    stateDictPtr_(),
-    objectsRegistryPtr_(),
-    execution_(execution),
-    updated_(false)
+    functionObjectList(runTime, runTime.controlDict(), execution)
 {}
 
 
@@ -375,12 +455,14 @@ Foam::functionObjectList::functionObjectList
 )
 :
     PtrList<functionObject>(),
+    errorHandling_(),
     digests_(),
     indices_(),
+    warnings_(),
     time_(runTime),
     parentDict_(parentDict),
-    stateDictPtr_(),
-    objectsRegistryPtr_(),
+    stateDictPtr_(nullptr),
+    objectsRegistryPtr_(nullptr),
     execution_(execution),
     updated_(false)
 {}
@@ -442,9 +524,7 @@ Foam::autoPtr<Foam::functionObjectList> Foam::functionObjectList::New
     {
         modifiedControlDict = true;
 
-        wordList funcNames = args.getList<word>("funcs");
-
-        for (const word& funcName : funcNames)
+        for (const word& funcName : args.getList<word>("funcs"))
         {
             readFunctionObject
             (
@@ -478,17 +558,14 @@ Foam::autoPtr<Foam::functionObjectList> Foam::functionObjectList::New
 
 Foam::label Foam::functionObjectList::triggerIndex() const
 {
-    label triggeri = labelMin;
-    stateDict().readIfPresent("triggerIndex", triggeri);
-
-    return triggeri;
+    return stateDict().getOrDefault<label>("triggerIndex", labelMin);
 }
 
 
 void Foam::functionObjectList::resetState()
 {
     // Reset (re-read) the state dictionary
-    stateDictPtr_.clear();
+    stateDictPtr_.reset(nullptr);
     createStateDict();
 }
 
@@ -540,19 +617,21 @@ const Foam::objectRegistry& Foam::functionObjectList::storedObjects() const
 void Foam::functionObjectList::clear()
 {
     PtrList<functionObject>::clear();
+    errorHandling_.clear();
     digests_.clear();
     indices_.clear();
+    warnings_.clear();
     updated_ = false;
 }
 
 
-Foam::label Foam::functionObjectList::findObjectID(const word& name) const
+Foam::label Foam::functionObjectList::findObjectID(const word& objName) const
 {
     label id = 0;
 
     for (const functionObject& funcObj : functions())
     {
-        if (funcObj.name() == name)
+        if (funcObj.name() == objName)
         {
             return id;
         }
@@ -600,19 +679,140 @@ bool Foam::functionObjectList::execute()
             read();
         }
 
+        auto errIter = errorHandling_.cbegin();
+
         for (functionObject& funcObj : functions())
         {
+            const errorHandlingType errorHandling = *errIter;
+            ++errIter;
+
             const word& objName = funcObj.name();
+
+            if
+            (
+                errorHandling == errorHandlingType::WARN
+             || errorHandling == errorHandlingType::IGNORE
+            )
             {
-                addProfiling(fo, "functionObject::" + objName + "::execute");
+                // Throw FatalError, FatalIOError as exceptions
 
-                ok = funcObj.execute() && ok;
-            }
+                const bool throwingError = FatalError.throwExceptions();
+                const bool throwingIOerr = FatalIOError.throwExceptions();
+
+                bool hadError = false;
+
+                // execute()
+                try
+                {
+                    addProfiling
+                    (
+                        fo,
+                        "functionObject::" + objName + "::execute"
+                    );
+
+                    ok = funcObj.execute() && ok;
+                }
+                catch (const Foam::error& err)
+                {
+                    // Treat IOerror and error identically
+                    uint32_t nWarnings;
+                    hadError = true;
+
+                    if
+                    (
+                        errorHandling != errorHandlingType::IGNORE
+                     && (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.throwExceptions(throwingError);
+                    FatalIOError.throwExceptions(throwingIOerr);
+                    continue;
+                }
+
+                // write()
+                try
+                {
+                    addProfiling
+                    (
+                        fo,
+                        "functionObject::" + objName + ":write"
+                    );
+
+                    ok = funcObj.write() && ok;
+                }
+                catch (const Foam::error& err)
+                {
+                    // Treat IOerror and error identically
+                    uint32_t nWarnings;
+
+                    if
+                    (
+                        errorHandling != errorHandlingType::IGNORE
+                     && (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;
+                    }
+                }
 
+                // Restore previous state
+                FatalError.throwExceptions(throwingError);
+                FatalIOError.throwExceptions(throwingIOerr);
+            }
+            else
             {
-                addProfiling(fo, "functionObject::" + objName + "::write");
+                // No special trapping of errors
+
+                // execute()
+                {
+                    addProfiling
+                    (
+                        fo,
+                        "functionObject::" + objName + "::execute"
+                    );
 
-                ok = funcObj.write() && ok;
+                    ok = funcObj.execute() && ok;
+                }
+
+                // write()
+                {
+                    addProfiling
+                    (
+                        fo,
+                        "functionObject::" + objName + ":write"
+                    );
+
+                    ok = funcObj.write() && ok;
+                }
             }
         }
     }
@@ -620,7 +820,7 @@ bool Foam::functionObjectList::execute()
     // Force writing of state dictionary after function object execution
     if (time_.writeTime())
     {
-        label oldPrecision = IOstream::precision_;
+        const auto oldPrecision = IOstream::precision_;
         IOstream::precision_ = 16;
 
         stateDictPtr_->writeObject
@@ -644,6 +844,8 @@ bool Foam::functionObjectList::execute(const label subIndex)
     {
         for (functionObject& funcObj : functions())
         {
+            // Probably do not need try/catch...
+
             ok = funcObj.execute(subIndex) && ok;
         }
     }
@@ -666,6 +868,8 @@ bool Foam::functionObjectList::execute
         {
             if (stringOps::match(functionNames, funcObj.name()))
             {
+                // Probably do not need try/catch...
+
                 ok = funcObj.execute(subIndex) && ok;
             }
         }
@@ -686,13 +890,55 @@ bool Foam::functionObjectList::end()
             read();
         }
 
+        auto errIter = errorHandling_.cbegin();
+
         for (functionObject& funcObj : functions())
         {
+            const errorHandlingType errorHandling = *errIter;
+            ++errIter;
+
             const word& objName = funcObj.name();
 
-            addProfiling(fo, "functionObject::" + objName + "::end");
+            // Ignore failure on end() - not much we can do anyhow
+
+            // Throw FatalError, FatalIOError as exceptions
+            const bool throwingError = FatalError.throwExceptions();
+            const bool throwingIOerr = FatalIOError.throwExceptions();
+
+            try
+            {
+                addProfiling(fo, "functionObject::" + objName + "::end");
+                ok = funcObj.end() && ok;
+            }
+            catch (const Foam::error& err)
+            {
+                // Treat IOerror and error identically
+                uint32_t nWarnings;
+
+                if
+                (
+                    errorHandling != errorHandlingType::IGNORE
+                 && (nWarnings = ++warnings_(objName)) <= maxWarnings
+                )
+                {
+                    // Trickery to get original message
+                    err.write(Warning, false);
+                    Info<< nl
+                        << "--> end() function object '"
+                        << objName << "'";
+
+                    if (nWarnings == maxWarnings)
+                    {
+                        Info<< nl << "... silencing further warnings";
+                    }
+
+                    Info<< nl << endl;
+                }
+            }
 
-            ok = funcObj.end() && ok;
+            // Restore previous state
+            FatalError.throwExceptions(throwingError);
+            FatalIOError.throwExceptions(throwingIOerr);
         }
     }
 
@@ -715,7 +961,13 @@ bool Foam::functionObjectList::adjustTimeStep()
         {
             const word& objName = funcObj.name();
 
-            addProfiling(fo, "functionObject::" + objName + "::adjustTimeStep");
+            // Probably do not need try/catch...
+
+            addProfiling
+            (
+                fo,
+                "functionObject::" + objName + "::adjustTimeStep"
+            );
 
             ok = funcObj.adjustTimeStep() && ok;
         }
@@ -750,8 +1002,10 @@ bool Foam::functionObjectList::read()
     {
         // No functions
         PtrList<functionObject>::clear();
+        errorHandling_.clear();
         digests_.clear();
         indices_.clear();
+        warnings_.clear();
     }
     else if (!entryPtr->isDict())
     {
@@ -767,10 +1021,18 @@ bool Foam::functionObjectList::read()
 
         PtrList<functionObject> newPtrs(functionsDict.size());
         List<SHA1Digest> newDigs(functionsDict.size());
+
+        errorHandling_.resize
+        (
+            functionsDict.size(),
+            errorHandlingType::DEFAULT
+        );
+
         HashTable<label> newIndices;
 
         addProfiling(fo, "functionObjects::read");
 
+        // Top-level "libs" specification (optional)
         time_.libs().open
         (
             functionsDict,
@@ -778,6 +1040,15 @@ bool Foam::functionObjectList::read()
             functionObject::dictionaryConstructorTablePtr_
         );
 
+        // Top-level "errors" specification (optional)
+        const errorHandlingType errorHandlingFallback =
+            getOrDefaultErrorHandling
+            (
+                "errors",
+                functionsDict,
+                errorHandlingType::DEFAULT
+            );
+
         label nFunc = 0;
 
         for (const entry& dEntry : functionsDict)
@@ -786,7 +1057,7 @@ bool Foam::functionObjectList::read()
 
             if (!dEntry.isDict())
             {
-                if (key != "libs")
+                if (key != "errors" && key != "libs")
                 {
                     IOWarningInFunction(parentDict_)
                         << "Entry " << key << " is not a dictionary" << endl;
@@ -799,66 +1070,67 @@ bool Foam::functionObjectList::read()
 
             bool enabled = dict.getOrDefault("enabled", true);
 
+            // Per-function "errors" specification
+            const errorHandlingType errorHandling =
+                getOrDefaultErrorHandling
+                (
+                    "errors",
+                    dict,
+                    errorHandlingFallback
+                );
+
+            errorHandling_[nFunc] = errorHandling;
+
             newDigs[nFunc] = dict.digest();
 
             label oldIndex = -1;
             autoPtr<functionObject> objPtr = remove(key, oldIndex);
 
+            const bool needsTimeControl =
+                functionObjects::timeControl::entriesPresent(dict);
+
             if (objPtr)
             {
-                // Re-read if dictionary content changed for
-                // existing functionObject
+                // Existing functionObject:
+                // Re-read if dictionary content changed and did not
+                // change timeControl <-> regular
+
                 if (enabled && newDigs[nFunc] != digests_[oldIndex])
                 {
-                    addProfiling
-                    (
-                        fo2,
-                        "functionObject::" + objPtr->name() + "::read"
-                    );
+                    const bool wasTimeControl =
+                        isA<functionObjects::timeControl>(*objPtr);
 
-                    if (functionObjects::timeControl::entriesPresent(dict))
+                    if (needsTimeControl != wasTimeControl)
                     {
-                        if (isA<functionObjects::timeControl>(objPtr()))
-                        {
-                            // Already a time control - normal read
-                            enabled = objPtr->read(dict);
-                        }
-                        else
-                        {
-                            // Was not a time control - need to re-create
-                            objPtr.reset
-                            (
-                                new functionObjects::timeControl
-                                (
-                                    key,
-                                    time_,
-                                    dict
-                                )
-                            );
-
-                            enabled = true;
-                        }
+                        // Changed from timeControl <-> regular
+
+                        // Fallthrough to 'new'
+                        objPtr.reset(nullptr);
                     }
                     else
                     {
-                        // Plain function object - normal read
+                        // Normal read. Assume no errors to trap
+
+                        addProfiling
+                        (
+                            fo,
+                            "functionObject::" + objPtr->name() + "::read"
+                        );
+
                         enabled = objPtr->read(dict);
                     }
-
-                    ok = enabled && ok;
                 }
 
                 if (!enabled)
                 {
                     // Delete disabled or an invalid(read) functionObject
-                    objPtr.clear();
+                    objPtr.reset(nullptr);
                     continue;
                 }
             }
-            else if (enabled)
-            {
-                autoPtr<functionObject> foPtr;
 
+            if (enabled && !objPtr)
+            {
                 // Throw FatalError, FatalIOError as exceptions
                 const bool throwingError = FatalError.throwExceptions();
                 const bool throwingIOerr = FatalIOError.throwExceptions();
@@ -868,47 +1140,72 @@ bool Foam::functionObjectList::read()
                     // New functionObject
                     addProfiling
                     (
-                        fo2,
+                        fo,
                         "functionObject::" + key + "::new"
                     );
-                    if (functionObjects::timeControl::entriesPresent(dict))
+                    if (needsTimeControl)
                     {
-                        foPtr.reset
+                        objPtr.reset
                         (
                             new functionObjects::timeControl(key, time_, dict)
                         );
                     }
                     else
                     {
-                        foPtr = functionObject::New(key, time_, dict);
+                        objPtr = functionObject::New(key, time_, dict);
                     }
                 }
-                catch (const Foam::IOerror& ioErr)
-                {
-                    Info<< ioErr << nl << endl;
-                    std::exit(1);
-                }
                 catch (const Foam::error& err)
                 {
-                    // Bit of trickery to get the original message
-                    err.write(Warning, false);
-                    InfoInFunction
-                        << nl
-                        << "--> while loading function object '" << key << "'"
-                        << nl << endl;
+                    objPtr.reset(nullptr);  // extra safety
+
+                    switch (errorHandling)
+                    {
+                        case errorHandlingType::IGNORE:
+                            break;
+
+                        case errorHandlingType::STRICT:
+                        {
+                            exitNow(err);
+                            break;
+                        }
+
+                        case errorHandlingType::DEFAULT:
+                        {
+                            if (isA<Foam::IOerror>(err))
+                            {
+                                // Fatal on Foam::IOerror
+                                exitNow(err);
+                                break;
+                            }
+
+                            // Warn on Foam::error
+                            #if (__cplusplus >= 201703L)
+                            [[fallthrough]];
+                            #endif
+                        }
+
+                        case errorHandlingType::WARN:
+                        {
+                            // Trickery to get original message
+                            err.write(Warning, false);
+                            Info<< nl
+                                << "--> loading function object '"
+                                << key << "'"
+                                << nl << endl;
+                            break;
+                        }
+                    }
                 }
 
-                // Restore previous exception throwing state
+                // Restore previous state
                 FatalError.throwExceptions(throwingError);
                 FatalIOError.throwExceptions(throwingIOerr);
 
-                // Required functionObject to be valid on all processors
-                if (returnReduce(bool(foPtr), andOp<bool>()))
-                {
-                    objPtr.reset(foPtr.release());
-                }
-                else
+                // Require functionObject to be valid on all processors
+                if (!returnReduce(bool(objPtr), andOp<bool>()))
                 {
+                    objPtr.reset(nullptr);
                     ok = false;
                 }
             }
@@ -924,12 +1221,14 @@ bool Foam::functionObjectList::read()
 
         newPtrs.resize(nFunc);
         newDigs.resize(nFunc);
+        errorHandling_.resize(nFunc);
 
         // Updating PtrList of functionObjects deletes any
         // existing unused functionObjects
         PtrList<functionObject>::transfer(newPtrs);
         digests_.transfer(newDigs);
         indices_.transfer(newIndices);
+        warnings_.clear();
     }
 
     return ok;
diff --git a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.H b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.H
index bc1801e18e9..b96c665693e 100644
--- a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.H
+++ b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2019 OpenCFD Ltd.
+    Copyright (C) 2015-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -31,6 +31,43 @@ Description
     List of function objects with start(), execute() and end() functions
     that is called for each object.
 
+    \verbatim
+    functions   // sub-dictionary name under the system/controlDict file
+    {
+        ..optional entries..
+
+        <userDict1>
+        {
+            // Mandatory entries
+            type    <typeName>;
+            libs    (<libName> .. <libName>);
+            ...
+        }
+
+        <userDict2>
+        {
+            ...
+        }
+
+        ...
+    }
+    \endverbatim
+
+    with optional entries:
+    \table
+        Property | Description                            | Type | Reqd | Deflt
+        libs     | Preloaded library names                | words | no  | -
+        errors   | Error handling (default/warn/ignore/strict) | word | no | inherits
+    \endtable
+
+    The optional \c errors entry controls how FatalError is caught
+    during construction and execute/write. FatalIOError is unaffected.
+    - \c default : warn on construction errors, fatal on runtime errors
+    - \c warn : warn on construction and runtime errors
+    - \c ignore : ignore construction and runtime errors
+    - \c strict : fatal on construction and runtime errors
+    .
+
 See also
     Foam::functionObject
     Foam::functionObjects::timeControl
@@ -44,6 +81,7 @@ SourceFiles
 #define functionObjectList_H
 
 #include "PtrList.H"
+#include "Enum.H"
 #include "functionObject.H"
 #include "SHA1Digest.H"
 #include "HashTable.H"
@@ -55,7 +93,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 class argList;
 class mapPolyMesh;
 class wordRe;
@@ -68,14 +106,37 @@ class functionObjectList
 :
     private PtrList<functionObject>
 {
-    // Private data
+    // Data Types
+
+        //- Handling of construction or execution errors
+        enum class errorHandlingType : uint8_t
+        {
+            DEFAULT = 0,  //!< Warn on construct, Fatal on runtime
+            WARN,         //!< Warn on construct, Warn on runtime
+            IGNORE,       //!< Ignore on construct, Ignore on runtime
+            STRICT,       //!< Fatal on construct, Fatal on runtime
+        };
+
+        //- Names for error handling types
+        static const Enum<errorHandlingType> errorHandlingNames_;
+
+
+    // Private Data
+
+        //- A list of error/warning handling
+        List<errorHandlingType> errorHandling_;
 
         //- A list of SHA1 digests for the function object dictionaries
         List<SHA1Digest> digests_;
 
-        //- Quick lookup of the index into functions/digests
+        //- Quick lookup of the index into functions/digests/errorHandling
         HashTable<label> indices_;
 
+        //- Track the number of warnings per function object and limit
+        //  to a predefined number to avoid flooding the display.
+        //  Clear on re-read of functions.
+        HashTable<uint32_t> warnings_;
+
         //- Reference to Time
         const Time& time_;
 
@@ -119,6 +180,19 @@ class functionObjectList
         //- configuration files, add to the given map and recurse
         static void listDir(const fileName& dir, wordHashSet& available);
 
+        //- Like Enum::getOrDefault, but with additional code to warn if
+        //- the 'key' is not a primitive entry.
+        //
+        //  This additional treatment is to ensure that potentially existing
+        //  code with an "errors" functionObject will continue to run.
+        errorHandlingType getOrDefaultErrorHandling
+        (
+            const word& key,
+            const dictionary& dict,
+            const errorHandlingType deflt
+        ) const;
+
+
         //- No copy construct
         functionObjectList(const functionObjectList&) = delete;
 
@@ -214,7 +288,7 @@ public:
         void clear();
 
         //- Find the ID of a given function object by name, -1 if not found.
-        label findObjectID(const word& name) const;
+        label findObjectID(const word& objName) const;
 
         //- Print a list of functionObject configuration files in the
         //- directories located using
-- 
GitLab