Compare commits

...

4 Commits

Author SHA1 Message Date
d9f3dd00ff update 2023-08-24 14:35:02 +02:00
81475e3be1 STYLE: report zero faces/area (#2966) 2023-08-24 13:12:26 +02:00
cc6bdb7ce5 ENH: error handling for empty surfaces in surfaceFieldValue (#2966)
- for workflows with appearing/disappearing patches (for example)
  can specify that empty surfaces should be ignored or warned about
  instead of rasing a FatalError.

  Note that this handling is additional to the regular top-level
  "errors" specification. So specifying 'strict' will only actually
  result in a FatalError if the "errors" does not trap errors.
2023-08-22 14:03:49 +02:00
77cb50ae97 ENH: relocate functionObjectList errorHandling types to error (#2966)
- was previously private within functionObjectList, now exposed from
  error as 'handlerTypes' and 'handlerNames' enumeration.
2023-08-22 13:58:47 +02:00
6 changed files with 328 additions and 131 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2014 OpenFOAM Foundation
Copyright (C) 2015-2020 OpenCFD Ltd.
Copyright (C) 2015-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -34,8 +34,24 @@ License
#include "Pstream.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 * * * * * * * * * * * * //
void Foam::error::warnAboutAge(const char* what, const int version)

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2015-2020 OpenCFD Ltd.
Copyright (C) 2015-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -53,8 +53,8 @@ SeeAlso
\*---------------------------------------------------------------------------*/
#ifndef error_H
#define error_H
#ifndef Foam_error_H
#define Foam_error_H
#include "messageStream.H"
#include <memory>
@ -64,6 +64,10 @@ SeeAlso
namespace Foam
{
// Forward Declarations
class OStringStream;
template<class EnumType> class Enum;
/*---------------------------------------------------------------------------*\
Class error Declaration
\*---------------------------------------------------------------------------*/
@ -92,6 +96,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

View File

@ -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.
@ -43,7 +43,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
(
@ -51,19 +51,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
@ -396,12 +383,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);
@ -418,16 +405,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);
}
}
@ -683,15 +670,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
@ -715,12 +702,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
)
{
@ -761,11 +748,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
)
{
@ -894,7 +881,7 @@ bool Foam::functionObjectList::end()
for (functionObject& funcObj : functions())
{
const errorHandlingType errorHandling = *errIter;
const auto errorHandling = *errIter;
++errIter;
const word& objName = funcObj.name();
@ -913,11 +900,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
)
{
@ -1025,7 +1012,7 @@ bool Foam::functionObjectList::read()
errorHandling_.resize
(
functionsDict.size(),
errorHandlingType::DEFAULT
error::handlerTypes::DEFAULT
);
HashTable<label> newIndices;
@ -1041,12 +1028,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;
@ -1071,7 +1058,7 @@ bool Foam::functionObjectList::read()
bool enabled = dict.getOrDefault("enabled", true);
// Per-function "errors" specification
const errorHandlingType errorHandling =
const error::handlerTypes errorHandling =
getOrDefaultErrorHandling
(
"errors",
@ -1161,16 +1148,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))
{
@ -1183,7 +1170,7 @@ bool Foam::functionObjectList::read()
[[fallthrough]];
}
case errorHandlingType::WARN:
case error::handlerTypes::WARN:
{
// Trickery to get original message
err.write(Warning, false);

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2020 OpenCFD Ltd.
Copyright (C) 2015-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -77,11 +77,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"
@ -106,25 +105,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_;
@ -135,7 +119,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_;
@ -185,11 +169,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;

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2017-2021 OpenCFD Ltd.
Copyright (C) 2017-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -38,6 +38,9 @@ License
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
// Max number of warnings
static constexpr const unsigned maxWarnings = 10u;
namespace Foam
{
namespace functionObjects
@ -136,39 +139,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 (!returnReduce(bool(numFaces), orOp<bool>()))
{
WarningInFunction
<< type() << ' ' << name() << ": "
<< regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
<< " The faceZone specification: "
<< flatOutput(selectionNames_) << nl
<< " resulted in 0 faces" << nl
<< exit(FatalError);
}
#endif
faceId_.resize(numFaces);
facePatchId_.resize(numFaces);
faceFlip_.resize(numFaces);
@ -231,7 +208,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;
}
}
}
}
@ -307,36 +352,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 (!returnReduce(bool(numFaces), orOp<bool>()))
{
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>());
nFaces_ = returnReduce(numFaces, sumOp<label>());
numFaces = 0;
for (const label patchi : patchIds)
@ -350,6 +369,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;
}
}
}
}
@ -583,20 +670,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 polySurface& s = dynamicCast<const polySurface>(obr());
nFaces_ = returnReduce(s.size(), sumOp<label>());
break;
@ -610,23 +707,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;
}
@ -919,10 +1069,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_()
@ -953,10 +1105,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_()
@ -976,10 +1130,19 @@ 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();
totalArea_ = 0;
nFaces_ = 0;
nWarnings_ = 0;
faceId_.clear();
facePatchId_.clear();
faceFlip_.clear();
@ -1161,7 +1324,15 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
if (writeArea_)
{
totalArea_ = totalArea();
// Update the area
if (!nFaces_)
{
totalArea_ = 0;
}
else
{
totalArea_ = totalArea();
}
Log << " total area = " << totalArea_ << endl;
if (operation_ != opNone && Pstream::master())
@ -1170,6 +1341,18 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
}
}
if (!nFaces_)
{
// Early exit on error
if (operation_ != opNone)
{
file() << endl;
Log << endl;
}
return true;
}
// Many operations use the Sf field
vectorField Sf;
if (usesSf())

View File

@ -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:
@ -401,6 +403,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_;
@ -413,6 +418,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)