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)