diff --git a/src/OpenFOAM/db/error/error.C b/src/OpenFOAM/db/error/error.C index 5dce9fd6cdf5978e9caeb30a76fdfd1b70e34780..120ac2427a2eee52158f14bdcd40928c6367343e 100644 --- a/src/OpenFOAM/db/error/error.C +++ b/src/OpenFOAM/db/error/error.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2014 OpenFOAM Foundation - Copyright (C) 2015-2021 OpenCFD Ltd. + Copyright (C) 2015-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -37,8 +37,24 @@ Note #include "StringStream.H" #include "foamVersion.H" #include "OSspecific.H" +#include "Enum.H" #include "Switch.H" +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +const Foam::Enum +< + Foam::error::handlerTypes +> +Foam::error::handlerNames +({ + { handlerTypes::DEFAULT, "default" }, + { handlerTypes::IGNORE, "ignore" }, + { handlerTypes::WARN, "warn" }, + { handlerTypes::STRICT, "strict" }, +}); + + // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // bool Foam::error::master(const label communicator) diff --git a/src/OpenFOAM/db/error/error.H b/src/OpenFOAM/db/error/error.H index 36b5a200bc952203ae7049fbbd63cb2487c2be62..21dfea45f1f6b5a882e28013b97d8bf75ebdde1d 100644 --- a/src/OpenFOAM/db/error/error.H +++ b/src/OpenFOAM/db/error/error.H @@ -53,8 +53,8 @@ SeeAlso \*---------------------------------------------------------------------------*/ -#ifndef error_H -#define error_H +#ifndef Foam_error_H +#define Foam_error_H #include "messageStream.H" #include <memory> @@ -66,6 +66,7 @@ namespace Foam // Forward Declarations class OStringStream; +template<class EnumType> class Enum; /*---------------------------------------------------------------------------*\ Class error Declaration @@ -101,6 +102,21 @@ protected: public: + // Data Types + + //- Handling of errors. The exact handling depends on the local context. + enum class handlerTypes : char + { + DEFAULT = 0, //!< Default behaviour (local meaning) + IGNORE, //!< Ignore on errors/problems + WARN, //!< Warn on errors/problems + STRICT //!< Fatal on errors/problems + }; + + //- Names of the error handler types + static const Enum<handlerTypes> handlerNames; + + // Constructors //- Construct from title string diff --git a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C index d1e4105d0a9facbf0aa1df9b35474e3dc989aafb..3c0beb533a32a79994da1a5f60239838b56c9575 100644 --- a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C +++ b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2015-2022 OpenCFD Ltd. + Copyright (C) 2015-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -44,7 +44,7 @@ License /* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */ //- Max number of warnings (per functionObject) -static constexpr const uint32_t maxWarnings = 10u; +static constexpr const unsigned maxWarnings = 10u; Foam::fileName Foam::functionObjectList::functionObjectDictPath ( @@ -52,19 +52,6 @@ Foam::fileName Foam::functionObjectList::functionObjectDictPath ); -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 @@ -350,12 +337,12 @@ bool Foam::functionObjectList::readFunctionObject } -Foam::functionObjectList::errorHandlingType +Foam::error::handlerTypes Foam::functionObjectList::getOrDefaultErrorHandling ( const word& key, const dictionary& dict, - const errorHandlingType deflt + const error::handlerTypes deflt ) const { const entry* eptr = dict.findEntry(key, keyType::LITERAL); @@ -372,16 +359,16 @@ Foam::functionObjectList::getOrDefaultErrorHandling { const word enumName(eptr->get<word>()); - if (!errorHandlingNames_.found(enumName)) + if (!error::handlerNames.found(enumName)) { // Failed the name lookup FatalIOErrorInFunction(dict) << enumName << " is not in enumeration: " - << errorHandlingNames_ << nl + << error::handlerNames << nl << exit(FatalIOError); } - return errorHandlingNames_.get(enumName); + return error::handlerNames.get(enumName); } } @@ -640,15 +627,15 @@ bool Foam::functionObjectList::execute() for (functionObject& funcObj : functions()) { - const errorHandlingType errorHandling = *errIter; + const auto errorHandling = *errIter; ++errIter; const word& objName = funcObj.name(); if ( - errorHandling == errorHandlingType::WARN - || errorHandling == errorHandlingType::IGNORE + errorHandling == error::handlerTypes::WARN + || errorHandling == error::handlerTypes::IGNORE ) { // Throw FatalError, FatalIOError as exceptions @@ -672,12 +659,12 @@ bool Foam::functionObjectList::execute() catch (const Foam::error& err) { // Treat IOerror and error identically - uint32_t nWarnings; + unsigned nWarnings; hadError = true; if ( - errorHandling != errorHandlingType::IGNORE + (errorHandling != error::handlerTypes::IGNORE) && (nWarnings = ++warnings_(objName)) <= maxWarnings ) { @@ -718,11 +705,11 @@ bool Foam::functionObjectList::execute() catch (const Foam::error& err) { // Treat IOerror and error identically - uint32_t nWarnings; + unsigned nWarnings; if ( - errorHandling != errorHandlingType::IGNORE + (errorHandling != error::handlerTypes::IGNORE) && (nWarnings = ++warnings_(objName)) <= maxWarnings ) { @@ -851,7 +838,7 @@ bool Foam::functionObjectList::end() for (functionObject& funcObj : functions()) { - const errorHandlingType errorHandling = *errIter; + const auto errorHandling = *errIter; ++errIter; const word& objName = funcObj.name(); @@ -870,11 +857,11 @@ bool Foam::functionObjectList::end() catch (const Foam::error& err) { // Treat IOerror and error identically - uint32_t nWarnings; + unsigned nWarnings; if ( - errorHandling != errorHandlingType::IGNORE + (errorHandling != error::handlerTypes::IGNORE) && (nWarnings = ++warnings_(objName)) <= maxWarnings ) { @@ -982,7 +969,7 @@ bool Foam::functionObjectList::read() errorHandling_.resize ( functionsDict.size(), - errorHandlingType::DEFAULT + error::handlerTypes::DEFAULT ); HashTable<label> newIndices; @@ -998,12 +985,12 @@ bool Foam::functionObjectList::read() ); // Top-level "errors" specification (optional) - const errorHandlingType errorHandlingFallback = + const error::handlerTypes errorHandlingFallback = getOrDefaultErrorHandling ( "errors", functionsDict, - errorHandlingType::DEFAULT + error::handlerTypes::DEFAULT ); label nFunc = 0; @@ -1045,7 +1032,7 @@ bool Foam::functionObjectList::read() bool enabled = dict.getOrDefault("enabled", true); // Per-function "errors" specification - const errorHandlingType errorHandling = + const error::handlerTypes errorHandling = getOrDefaultErrorHandling ( "errors", @@ -1135,16 +1122,16 @@ bool Foam::functionObjectList::read() switch (errorHandling) { - case errorHandlingType::IGNORE: + case error::handlerTypes::IGNORE: break; - case errorHandlingType::STRICT: + case error::handlerTypes::STRICT: { exitNow(err); break; } - case errorHandlingType::DEFAULT: + case error::handlerTypes::DEFAULT: { if (isA<Foam::IOerror>(err)) { @@ -1157,7 +1144,7 @@ bool Foam::functionObjectList::read() [[fallthrough]]; } - case errorHandlingType::WARN: + case error::handlerTypes::WARN: { // Trickery to get original message err.write(Warning, false); diff --git a/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.H b/src/OpenFOAM/db/functionObjects/functionObjectList/functionObjectList.H index fa57a3c78f21272f8c3a74b7e0f637a0f0f9fc6c..da9df075273c3219f1edccccbcbc210378a473c6 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-2021 OpenCFD Ltd. + Copyright (C) 2015-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -78,11 +78,10 @@ SourceFiles \*---------------------------------------------------------------------------*/ -#ifndef functionObjectList_H -#define functionObjectList_H +#ifndef Foam_functionObjectList_H +#define Foam_functionObjectList_H #include "PtrList.H" -#include "Enum.H" #include "functionObject.H" #include "SHA1Digest.H" #include "HashTable.H" @@ -108,25 +107,10 @@ class functionObjectList : private PtrList<functionObject> { - // 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_; + List<error::handlerTypes> errorHandling_; //- A list of SHA1 digests for the function object dictionaries List<SHA1Digest> digests_; @@ -137,7 +121,7 @@ class functionObjectList //- 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_; + HashTable<unsigned> warnings_; //- Reference to Time const Time& time_; @@ -187,11 +171,11 @@ class functionObjectList // // This additional treatment is to ensure that potentially existing // code with an "errors" functionObject will continue to run. - errorHandlingType getOrDefaultErrorHandling + error::handlerTypes getOrDefaultErrorHandling ( const word& key, const dictionary& dict, - const errorHandlingType deflt + const error::handlerTypes deflt ) const; diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C index 0b8517c8a7e7e50afdbcc892301983445fce3953..98bf75f3ef7f914c4398bbd15c7314b3e3f79c7c 100644 --- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C +++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C @@ -37,6 +37,9 @@ License // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // +// Max number of warnings +static constexpr const unsigned maxWarnings = 10u; + namespace Foam { namespace functionObjects @@ -135,39 +138,13 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces() mesh_.faceZones().indices(selectionNames_) ); - // Total number of faces selected + // Total number of faces that could be selected (before patch filtering) label numFaces = 0; for (const label zoneId : zoneIds) { numFaces += mesh_.faceZones()[zoneId].size(); } - if (zoneIds.empty()) - { - FatalErrorInFunction - << type() << ' ' << name() << ": " - << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl - << " No matching face zone(s): " - << flatOutput(selectionNames_) << nl - << " Known face zones: " - << flatOutput(mesh_.faceZones().names()) << nl - << exit(FatalError); - } - - // Could also check this - #if 0 - if (!returnReduceOr(numFaces)) - { - WarningInFunction - << type() << ' ' << name() << ": " - << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl - << " The faceZone specification: " - << flatOutput(selectionNames_) << nl - << " resulted in 0 faces" << nl - << exit(FatalError); - } - #endif - faceId_.resize_nocopy(numFaces); facePatchId_.resize_nocopy(numFaces); faceFlip_.resize_nocopy(numFaces); @@ -223,7 +200,75 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces() faceId_.resize(numFaces); facePatchId_.resize(numFaces); faceFlip_.resize(numFaces); - nFaces_ = returnReduce(faceId_.size(), sumOp<label>()); + nFaces_ = returnReduce(numFaces, sumOp<label>()); + + if (!nFaces_) + { + // Raise warning or error + refPtr<OSstream> os; + bool fatal = false; + + ++nWarnings_; // Always increment (even if ignore etc) + + switch (emptySurfaceError_) + { + case error::handlerTypes::IGNORE: + { + break; + } + + case error::handlerTypes::WARN: + { + if (nWarnings_ <= maxWarnings) + { + os.ref(WarningInFunction); + } + break; + } + + // STRICT / DEFAULT + default: + { + os.ref(FatalErrorInFunction); + fatal = true; + break; + } + } + + if (os) + { + os.ref() + << type() << ' ' << name() << ": " + << regionTypeNames_[regionType_] + << '(' << regionName_ << "):" << nl; + + if (zoneIds.empty()) + { + os.ref() + << " No matching face zones: " + << flatOutput(selectionNames_) << nl + << " Known face zones: " + << flatOutput(mesh_.faceZones().names()) << nl; + } + else + { + os.ref() + << " The face zones: " + << flatOutput(selectionNames_) << nl + << " resulted in 0 faces" << nl; + } + + if (fatal) + { + FatalError<< exit(FatalError); + } + else if (nWarnings_ == maxWarnings) + { + os.ref() + << "... suppressing further warnings." << nl; + } + } + } } @@ -283,7 +328,7 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces() nGood = 0; for (const label patchi : selected) { - if (!bad.found(patchi)) + if (!bad.contains(patchi)) { patchIds[nGood] = patchi; ++nGood; @@ -295,36 +340,10 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces() patchIds = std::move(selected); } - if (patchIds.empty()) - { - FatalErrorInFunction - << type() << ' ' << name() << ": " - << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl - << " No matching patch name(s): " - << flatOutput(selectionNames_) << nl - << " Known patch names:" << nl - << mesh_.boundaryMesh().names() << nl - << exit(FatalError); - } - - // Could also check this - #if 0 - if (!returnReduceOr(numFaces)) - { - WarningInFunction - << type() << ' ' << name() << ": " - << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl - << " The patch specification: " - << flatOutput(selectionNames_) << nl - << " resulted in 0 faces" << nl - << exit(FatalError); - } - #endif - - faceId_.resize(numFaces); - facePatchId_.resize(numFaces); - faceFlip_.resize(numFaces); - nFaces_ = returnReduce(faceId_.size(), sumOp<label>()); + faceId_.resize_nocopy(numFaces); + facePatchId_.resize_nocopy(numFaces); + faceFlip_.resize_nocopy(numFaces); + nFaces_ = returnReduce(numFaces, sumOp<label>()); numFaces = 0; for (const label patchi : patchIds) @@ -338,6 +357,74 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces() numFaces += len; } + + if (!nFaces_) + { + // Raise warning or error + refPtr<OSstream> os; + bool fatal = false; + + ++nWarnings_; // Always increment (even if ignore etc) + + switch (emptySurfaceError_) + { + case error::handlerTypes::IGNORE: + { + break; + } + + case error::handlerTypes::WARN: + { + if (nWarnings_ <= maxWarnings) + { + os.ref(WarningInFunction); + } + break; + } + + // STRICT / DEFAULT + default: + { + os.ref(FatalErrorInFunction); + fatal = true; + break; + } + } + + if (os) + { + os.ref() + << type() << ' ' << name() << ": " + << regionTypeNames_[regionType_] + << '(' << regionName_ << "):" << nl; + + if (patchIds.empty()) + { + os.ref() + << " No matching patches: " + << flatOutput(selectionNames_) << nl + << " Known patch names:" << nl + << mesh_.boundaryMesh().names() << nl; + } + else + { + os.ref() + << " The patches: " + << flatOutput(selectionNames_) << nl + << " resulted in 0 faces" << nl; + } + + if (fatal) + { + FatalError<< exit(FatalError); + } + else if (nWarnings_ == maxWarnings) + { + os.ref() + << "... suppressing further warnings." << nl; + } + } + } } @@ -511,20 +598,30 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::update() return false; } + // Reset some values + totalArea_ = 0; + nFaces_ = 0; + bool checkEmptyFaces = true; + switch (regionType_) { case stFaceZone: { + // Raises warning or error internally, don't check again setFaceZoneFaces(); + checkEmptyFaces = false; break; } case stPatch: { + // Raises warning or error internally, don't check again setPatchFaces(); + checkEmptyFaces = false; break; } case stObject: { + // TBD: special handling of cast errors? const auto& s = refCast<const polySurface>(obr()); nFaces_ = returnReduce(s.size(), sumOp<label>()); break; @@ -538,23 +635,76 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::update() // Compiler warning if we forgot an enumeration } - if (nFaces_ == 0) + if (nFaces_) { - FatalErrorInFunction - << type() << ' ' << name() << ": " - << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl - << " Region has no faces" << exit(FatalError); + // Appears to be successful + needsUpdate_ = false; + totalArea_ = totalArea(); // Update the area + nWarnings_ = 0u; // Reset the warnings counter } + else if (checkEmptyFaces) + { + // Raise warning or error + refPtr<OSstream> os; + bool fatal = false; - totalArea_ = totalArea(); + ++nWarnings_; // Always increment (even if ignore etc) + + switch (emptySurfaceError_) + { + case error::handlerTypes::IGNORE: + { + break; + } + + case error::handlerTypes::WARN: + { + if (nWarnings_ <= maxWarnings) + { + os.ref(WarningInFunction); + } + break; + } + + // STRICT / DEFAULT + default: + { + os.ref(FatalErrorInFunction); + fatal = true; + break; + } + } + + if (os) + { + os.ref() + << type() << ' ' << name() << ": " + << regionTypeNames_[regionType_] + << '(' << regionName_ << "):" << nl + << " Region has no faces" << endl; + + if (fatal) + { + FatalError<< exit(FatalError); + } + else if (nWarnings_ == maxWarnings) + { + os.ref() + << "... suppressing further warnings." << nl; + } + } + } Log << " total faces = " << nFaces_ << nl << " total area = " << totalArea_ << nl << endl; - writeFileHeader(file()); + // Emit file header on success or change of state + if (nWarnings_ <= 1) + { + writeFileHeader(file()); + } - needsUpdate_ = false; return true; } @@ -931,10 +1081,12 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue ), needsUpdate_(true), writeArea_(false), + emptySurfaceError_(error::handlerTypes::DEFAULT), selectionNames_(), weightFieldNames_(), totalArea_(0), nFaces_(0), + nWarnings_(0), faceId_(), facePatchId_(), faceFlip_() @@ -965,10 +1117,12 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue ), needsUpdate_(true), writeArea_(false), + emptySurfaceError_(error::handlerTypes::DEFAULT), selectionNames_(), weightFieldNames_(), totalArea_(0), nFaces_(0), + nWarnings_(0), faceId_(), facePatchId_(), faceFlip_() @@ -995,12 +1149,21 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read needsUpdate_ = true; writeArea_ = dict.getOrDefault("writeArea", false); + emptySurfaceError_ = error::handlerNames.getOrDefault + ( + "empty-surface", + dict, + error::handlerTypes::DEFAULT, + true // Failsafe behaviour + ); + weightFieldNames_.clear(); // future? // sampleFaceScheme_ = dict.getOrDefault<word>("sampleScheme", "cell"); totalArea_ = 0; nFaces_ = 0; + nWarnings_ = 0; faceId_.clear(); facePatchId_.clear(); faceFlip_.clear(); @@ -1182,12 +1345,39 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write() writeCurrentTime(file()); } + // Handle ignore/warn about empty-surface + if (!nFaces_) + { + totalArea_ = 0; // Update the area (safety) + + if (operation_ != opNone) + { + if (emptySurfaceError_ == error::handlerTypes::WARN) + { + if (writeArea_) + { + Log << " total area = " << totalArea_ << endl; + file() << tab << totalArea_; + } + + file() << tab << "NaN"; + Log << endl; + } + + file() << endl; + } + + // Early exit on error + return true; + } + if (writeArea_) { + // Update the area totalArea_ = totalArea(); Log << " total area = " << totalArea_ << endl; - if (operation_ != opNone && Pstream::master()) + if (operation_ != opNone && UPstream::master()) { file() << tab << totalArea_; } diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H index 3782e878a7674aa947c4bf55a383a871599f1a98..efda52462a84898070619a45aa83509515b46446 100644 --- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H +++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2015-2020 OpenCFD Ltd. + Copyright (C) 2015-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -88,6 +88,7 @@ Usage scaleFactor 1.0; writeArea false; surfaceFormat none; + empty-surface warn; // default | warn | ignore | strict // Optional (inherited) entries ... @@ -111,6 +112,7 @@ Usage writeArea | Write the surface area | bool | no | false surfaceFormat | Output value format | word <!-- --> | conditional on writeFields | none + empty-surface | Error handling for empty surfaces | enum | no | default \endtable The inherited entries are elaborated in: @@ -400,6 +402,9 @@ protected: //- Optionally write the area of the surfaceFieldValue bool writeArea_; + //- Handling of empty surfaces (nFaces = 0). Default is Fatal. + error::handlerTypes emptySurfaceError_; + //- Extended selections wordRes selectionNames_; @@ -412,6 +417,9 @@ protected: //- Global number of faces label nFaces_; + //- Number of warnings emitted since the last valid update + unsigned nWarnings_; + // If operating on mesh faces (faceZone, patch)