ENH: delay evaluation of surfaces for surfaceFieldValue (issue #1202)

- complete any pending initialisation on write().
  Allows lazier evaluation until when the surfaces are actually needed.
This commit is contained in:
Mark Olesen
2019-02-12 12:39:49 +01:00
committed by Andrew Heather
parent 52101db781
commit bfb0693bbe
4 changed files with 207 additions and 181 deletions

View File

@ -61,7 +61,7 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::regionTypeNames_
{ regionTypes::stFaceZone, "faceZone" }, { regionTypes::stFaceZone, "faceZone" },
{ regionTypes::stPatch, "patch" }, { regionTypes::stPatch, "patch" },
{ regionTypes::stSurface, "surface" }, { regionTypes::stSurface, "surface" },
{ regionTypes::stSampledSurface, "sampledSurface" }, { regionTypes::stSampled, "sampledSurface" },
}); });
@ -118,7 +118,7 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::postOperationTypeNames_
const Foam::objectRegistry& const Foam::objectRegistry&
Foam::functionObjects::fieldValues::surfaceFieldValue::obr() const Foam::functionObjects::fieldValues::surfaceFieldValue::obr() const
{ {
if (regionType_ == stSurface) if (stSurface == regionType_)
{ {
return mesh_.lookupObject<objectRegistry>(regionName_); return mesh_.lookupObject<objectRegistry>(regionName_);
} }
@ -135,7 +135,7 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces()
{ {
FatalErrorInFunction FatalErrorInFunction
<< type() << " " << name() << ": " << type() << " " << name() << ": "
<< regionTypeNames_[regionType_] << "(" << regionName_ << "):" << nl << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
<< " Unknown face zone name: " << regionName_ << " Unknown face zone name: " << regionName_
<< ". Valid face zones are: " << mesh_.faceZones().names() << ". Valid face zones are: " << mesh_.faceZones().names()
<< nl << exit(FatalError); << nl << exit(FatalError);
@ -213,7 +213,7 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces()
{ {
FatalErrorInFunction FatalErrorInFunction
<< type() << " " << name() << ": " << type() << " " << name() << ": "
<< regionTypeNames_[regionType_] << "(" << regionName_ << "):" << nl << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
<< " Unknown patch name: " << regionName_ << " Unknown patch name: " << regionName_
<< ". Valid patch names are: " << ". Valid patch names are: "
<< mesh_.boundaryMesh().names() << nl << mesh_.boundaryMesh().names() << nl
@ -368,7 +368,7 @@ combineSurfaceGeometry
pointField& points pointField& points
) const ) const
{ {
if (regionType_ == stSurface) if (stSurface == regionType_)
{ {
const surfMesh& s = dynamicCast<const surfMesh>(obr()); const surfMesh& s = dynamicCast<const surfMesh>(obr());
@ -398,9 +398,9 @@ combineSurfaceGeometry
points = s.points(); points = s.points();
} }
} }
else if (surfacePtr_.valid()) else if (sampledPtr_.valid())
{ {
const sampledSurface& s = surfacePtr_(); const sampledSurface& s = sampledPtr_();
if (Pstream::parRun()) if (Pstream::parRun())
{ {
@ -436,15 +436,15 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::totalArea() const
{ {
scalar totalArea = 0; scalar totalArea = 0;
if (regionType_ == stSurface) if (stSurface == regionType_)
{ {
const surfMesh& s = dynamicCast<const surfMesh>(obr()); const surfMesh& s = dynamicCast<const surfMesh>(obr());
totalArea = gSum(s.magSf()); totalArea = gSum(s.magSf());
} }
else if (surfacePtr_.valid()) else if (sampledPtr_.valid())
{ {
totalArea = gSum(surfacePtr_().magSf()); totalArea = gSum(sampledPtr_().magSf());
} }
else else
{ {
@ -481,20 +481,17 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::usesSf() const
} }
void Foam::functionObjects::fieldValues::surfaceFieldValue::initialise bool Foam::functionObjects::fieldValues::surfaceFieldValue::update()
(
const dictionary& dict
)
{ {
dict.readEntry("name", regionName_); if (sampledPtr_.valid())
{
sampledPtr_->update();
}
totalArea_ = 0; if (!needsUpdate_)
nFaces_ = 0; {
faceId_.clear(); return false;
facePatchId_.clear(); }
faceFlip_.clear();
surfacePtr_.clear();
surfaceWriterPtr_.clear();
switch (regionType_) switch (regionType_)
{ {
@ -514,124 +511,33 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::initialise
nFaces_ = returnReduce(s.size(), sumOp<label>()); nFaces_ = returnReduce(s.size(), sumOp<label>());
break; break;
} }
case stSampledSurface: case stSampled:
{ {
surfacePtr_ = sampledSurface::New nFaces_ = returnReduce(sampledPtr_->faces().size(), sumOp<label>());
(
name(),
mesh_,
dict.subDict("sampledSurfaceDict")
);
surfacePtr_().update();
nFaces_ =
returnReduce(surfacePtr_().faces().size(), sumOp<label>());
break; break;
} }
default:
{ // Compiler warning if we forgot an enumeration
FatalErrorInFunction
<< type() << " " << name() << ": "
<< int(regionType_) << "(" << regionName_ << "):"
<< nl << " Unknown region type. Valid region types are:"
<< regionTypeNames_ << nl
<< exit(FatalError);
}
} }
if (nFaces_ == 0) if (nFaces_ == 0)
{ {
FatalErrorInFunction FatalErrorInFunction
<< type() << " " << name() << ": " << type() << " " << name() << ": "
<< regionTypeNames_[regionType_] << "(" << regionName_ << "):" << nl << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
<< " Region has no faces" << exit(FatalError); << " Region has no faces" << exit(FatalError);
} }
if (surfacePtr_.valid())
{
surfacePtr_().update();
}
totalArea_ = totalArea(); totalArea_ = totalArea();
Info<< type() << " " << name() << ":" << nl Log
<< " operation = "; << " total faces = " << nFaces_ << nl
if (postOperation_ != postOpNone)
{
Info<< postOperationTypeNames_[postOperation_] << '('
<< operationTypeNames_[operation_] << ')' << nl;
}
else
{
Info<< operationTypeNames_[operation_] << nl;
}
Info<< " total faces = " << nFaces_ << nl
<< " total area = " << totalArea_ << nl; << " total area = " << totalArea_ << nl;
writeFileHeader(file());
weightFieldName_ = "none"; needsUpdate_ = false;
if (usesWeight()) return true;
{
if (regionType_ == stSampledSurface)
{
FatalIOErrorInFunction(dict)
<< "Cannot use weighted operation '"
<< operationTypeNames_[operation_]
<< "' for sampledSurface"
<< exit(FatalIOError);
}
if (dict.readIfPresent("weightField", weightFieldName_))
{
Info<< " weight field = " << weightFieldName_ << nl;
}
else
{
// Suggest possible alternative unweighted operation?
FatalIOErrorInFunction(dict)
<< "The '" << operationTypeNames_[operation_]
<< "' operation is missing a weightField." << nl
<< "Either provide the weightField, "
<< "use weightField 'none' to suppress weighting," << nl
<< "or use a different operation."
<< exit(FatalIOError);
}
}
// Backwards compatibility for v1612 and older
List<word> orientedFields;
if (dict.readIfPresent("orientedFields", orientedFields))
{
WarningInFunction
<< "The 'orientedFields' option is deprecated. These fields can "
<< "and have been added to the standard 'fields' list."
<< endl;
fields_.append(orientedFields);
}
if (writeFields_)
{
const word surfaceFormat(dict.get<word>("surfaceFormat"));
if (surfaceFormat != "none")
{
surfaceWriterPtr_.reset
(
surfaceWriter::New
(
surfaceFormat,
dict.subOrEmptyDict("formatOptions").
subOrEmptyDict(surfaceFormat)
).ptr()
);
Info<< " surfaceFormat = " << surfaceFormat << nl;
}
}
Info<< nl << endl;
} }
@ -911,15 +817,15 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
) )
), ),
weightFieldName_("none"), weightFieldName_("none"),
needsUpdate_(true),
writeArea_(false),
totalArea_(0), totalArea_(0),
writeArea_(dict.lookupOrDefault("writeArea", false)),
nFaces_(0), nFaces_(0),
faceId_(), faceId_(),
facePatchId_(), facePatchId_(),
faceFlip_() faceFlip_()
{ {
read(dict); read(dict);
writeFileHeader(file());
} }
@ -944,15 +850,15 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
) )
), ),
weightFieldName_("none"), weightFieldName_("none"),
needsUpdate_(true),
writeArea_(false),
totalArea_(0), totalArea_(0),
writeArea_(dict.lookupOrDefault("writeArea", false)),
nFaces_(0), nFaces_(0),
faceId_(), faceId_(),
facePatchId_(), facePatchId_(),
faceFlip_() faceFlip_()
{ {
read(dict); read(dict);
writeFileHeader(file());
} }
@ -964,7 +870,106 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read
) )
{ {
fieldValue::read(dict); fieldValue::read(dict);
initialise(dict);
weightFieldName_ = "none";
needsUpdate_ = true;
writeArea_ = dict.lookupOrDefault("writeArea", false);
totalArea_ = 0;
nFaces_ = 0;
faceId_.clear();
facePatchId_.clear();
faceFlip_.clear();
sampledPtr_.clear();
surfaceWriterPtr_.clear();
dict.readEntry("name", regionName_);
// Create sampled surface, but leave 'expired' (ie, no update) since it
// may depend on fields or data that do not yet exist
if (stSampled == regionType_)
{
sampledPtr_ = sampledSurface::New
(
name(),
mesh_,
dict.subDict("sampledSurfaceDict")
);
}
Info<< type() << " " << name() << ":" << nl
<< " operation = ";
if (postOperation_ != postOpNone)
{
Info<< postOperationTypeNames_[postOperation_] << '('
<< operationTypeNames_[operation_] << ')' << nl;
}
else
{
Info<< operationTypeNames_[operation_] << nl;
}
if (usesWeight())
{
if (stSampled == regionType_)
{
FatalIOErrorInFunction(dict)
<< "Cannot use weighted operation '"
<< operationTypeNames_[operation_]
<< "' for sampledSurface"
<< exit(FatalIOError);
}
if (dict.readIfPresent("weightField", weightFieldName_))
{
Info<< " weight field = " << weightFieldName_ << nl;
}
else
{
// Suggest possible alternative unweighted operation?
FatalIOErrorInFunction(dict)
<< "The '" << operationTypeNames_[operation_]
<< "' operation is missing a weightField." << nl
<< "Either provide the weightField, "
<< "use weightField 'none' to suppress weighting," << nl
<< "or use a different operation."
<< exit(FatalIOError);
}
}
// Backwards compatibility for v1612 and older
List<word> orientedFields;
if (dict.readIfPresent("orientedFields", orientedFields))
{
fields_.append(orientedFields);
WarningInFunction
<< "The 'orientedFields' option is deprecated. These fields can "
<< "and have been added to the standard 'fields' list."
<< endl;
}
if (writeFields_)
{
const word formatName(dict.get<word>("surfaceFormat"));
if (formatName != "none")
{
surfaceWriterPtr_.reset
(
surfaceWriter::New
(
formatName,
dict.subOrEmptyDict("formatOptions")
.subOrEmptyDict(formatName)
)
);
Info<< " surfaceFormat = " << formatName << nl;
}
}
Info<< nl << endl;
return true; return true;
} }
@ -972,10 +977,7 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read
bool Foam::functionObjects::fieldValues::surfaceFieldValue::write() bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
{ {
if (surfacePtr_.valid()) update();
{
surfacePtr_().update();
}
if (operation_ != opNone) if (operation_ != opNone)
{ {
@ -1002,14 +1004,14 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
vectorField Sf; vectorField Sf;
if (usesSf()) if (usesSf())
{ {
if (regionType_ == stSurface) if (stSurface == regionType_)
{ {
const surfMesh& s = dynamicCast<const surfMesh>(obr()); const surfMesh& s = dynamicCast<const surfMesh>(obr());
Sf = s.Sf(); Sf = s.Sf();
} }
else if (surfacePtr_.valid()) else if (sampledPtr_.valid())
{ {
Sf = surfacePtr_().Sf(); Sf = sampledPtr_().Sf();
} }
else else
{ {
@ -1023,13 +1025,13 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
if (surfaceWriterPtr_.valid()) if (surfaceWriterPtr_.valid())
{ {
if (regionType_ == stSurface || surfacePtr_.valid()) if (withTopologicalMerge())
{ {
combineSurfaceGeometry(faces, points); combineMeshGeometry(faces, points);
} }
else else
{ {
combineMeshGeometry(faces, points); combineSurfaceGeometry(faces, points);
} }
} }

View File

@ -80,26 +80,26 @@ Usage
Where the entries comprise: Where the entries comprise:
\table \table
Property | Description | Required | Default Property | Description | Required | Default
type | type name: surfaceFieldValue | yes | type | Type name: surfaceFieldValue | yes |
log | write data to standard output | no | no log | Write data to standard output | no | no
writeFields | Write the region field values | yes |
writeArea | Write the area of the surfaceFieldValue | no |
surfaceFormat | Output value format | no |
regionType | Face regionType: see below | yes | regionType | Face regionType: see below | yes |
name | Name of face regionType if required | no | name | Name for regionType | yes |
operation | Operation to perform | yes | operation | Operation to perform | yes |
postOperation | Post-operation to perform | no | none postOperation | Post-operation to perform | no | none
weightField | Name of field to apply weighting | no |
scaleFactor | Scale factor | no | 1
fields | List of fields to operate on | yes | fields | List of fields to operate on | yes |
weightField | Name of field to apply weighting | no |
scaleFactor | Output value scaling factor | no | 1
writeArea | Write the surface area | no |
writeFields | Write the region field values | yes |
surfaceFormat | Output value format | no | none
\endtable \endtable
Where \c regionType is defined by Where \c regionType is defined by
\plaintable \plaintable
faceZone | Requires a \b name entry to specify the faceZone faceZone | The \b name entry to specify the faceZone
patch | Requires a \b name entry to specify the patch patch | The \b name entry to specify the patch
surface | Requires a \b name entry to specify the surfMesh surface | The \b name entry to specify the surfMesh
sampledSurface | Requires a \b sampledSurfaceDict sub-dictionary sampledSurface | A \b sampledSurfaceDict sub-dictionary and \b name
\endplaintable \endplaintable
The \c operation is one of: The \c operation is one of:
@ -191,6 +191,7 @@ SourceFiles
namespace Foam namespace Foam
{ {
// Forward declarations
class sampledSurface; class sampledSurface;
class surfaceWriter; class surfaceWriter;
@ -214,10 +215,10 @@ public:
//- Region type enumeration //- Region type enumeration
enum regionTypes enum regionTypes
{ {
stFaceZone, //!< Calculate on a faceZone stFaceZone = 0x01, //!< Calculate on a faceZone
stPatch, //!< Calculate on a patch stPatch = 0x02, //!< Calculate on a patch
stSurface, //!< Calculate with fields on a surfMesh stSurface = 0x11, //!< Calculate with fields on a surfMesh
stSampledSurface //!< Sample onto surface and calculate stSampled = 0x12 //!< Sample onto surface and calculate
}; };
//- Region type names //- Region type names
@ -336,7 +337,7 @@ private:
protected: protected:
// Protected data // Protected Data
//- Region type //- Region type
regionTypes regionType_; regionTypes regionType_;
@ -350,12 +351,15 @@ protected:
//- Weight field name - optional //- Weight field name - optional
word weightFieldName_; word weightFieldName_;
//- Total area of the surfaceFieldValue //- Track if the surface needs an update
scalar totalArea_; bool needsUpdate_;
//- Optionally write the area of the surfaceFieldValue //- Optionally write the area of the surfaceFieldValue
bool writeArea_; bool writeArea_;
//- Total area of the surfaceFieldValue
scalar totalArea_;
//- Global number of faces //- Global number of faces
label nFaces_; label nFaces_;
@ -372,9 +376,8 @@ protected:
// (false: use as-is, true: negate) // (false: use as-is, true: negate)
boolList faceFlip_; boolList faceFlip_;
//- The sampledSurface (when operating on sampledSurface)
//- The sampledSurface (if operating on sampledSurface) autoPtr<sampledSurface> sampledPtr_;
autoPtr<sampledSurface> surfacePtr_;
//- Surface writer //- Surface writer
autoPtr<surfaceWriter> surfaceWriterPtr_; autoPtr<surfaceWriter> surfaceWriterPtr_;
@ -385,6 +388,12 @@ protected:
//- The volume mesh or surface registry being used //- The volume mesh or surface registry being used
const objectRegistry& obr() const; const objectRegistry& obr() const;
//- Can the surface definition sample surface-fields?
inline bool withSurfaceFields() const;
//- Can use mesh topological merge?
inline bool withTopologicalMerge() const;
//- Return the local list of face IDs //- Return the local list of face IDs
inline const labelList& faceId() const; inline const labelList& faceId() const;
@ -408,8 +417,9 @@ protected:
template<class WeightType> template<class WeightType>
inline bool canWeight(const Field<WeightType>& weightField) const; inline bool canWeight(const Field<WeightType>& weightField) const;
//- Initialise, e.g. face addressing //- Update the surface and surface information as required.
void initialise(const dictionary& dict); // Do nothing (and return false) if no update was required
bool update();
//- Return true if the field name is known and a valid type //- Return true if the field name is known and a valid type
template<class Type> template<class Type>
@ -420,7 +430,7 @@ protected:
tmp<Field<Type>> getFieldValues tmp<Field<Type>> getFieldValues
( (
const word& fieldName, const word& fieldName,
const bool mustGet = false const bool mandatory = false
) const; ) const;
//- Apply the 'operation' to the values. Operation must preserve Type. //- Apply the 'operation' to the values. Operation must preserve Type.
@ -533,7 +543,7 @@ public:
// Public Member Functions // Public Member Functions
//- Return the region type //- Return the region type
inline const regionTypes& regionType() const; inline regionTypes regionType() const;
//- Return the output directory //- Return the output directory
inline fileName outputDir() const; inline fileName outputDir() const;

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd. \\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
| Copyright (C) 2011-2016 OpenFOAM Foundation | Copyright (C) 2011-2016 OpenFOAM Foundation
@ -29,6 +29,20 @@ License
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
inline bool Foam::functionObjects::fieldValues::surfaceFieldValue::
withSurfaceFields() const
{
return (stFaceZone == regionType_ || stPatch == regionType_);
}
inline bool Foam::functionObjects::fieldValues::surfaceFieldValue::
withTopologicalMerge() const
{
return (stFaceZone == regionType_ || stPatch == regionType_);
}
inline const Foam::labelList& inline const Foam::labelList&
Foam::functionObjects::fieldValues::surfaceFieldValue::faceId() const Foam::functionObjects::fieldValues::surfaceFieldValue::faceId() const
{ {
@ -68,7 +82,7 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::usesWeight() const
} }
inline const Foam::functionObjects::fieldValues::surfaceFieldValue::regionTypes& inline Foam::functionObjects::fieldValues::surfaceFieldValue::regionTypes
Foam::functionObjects::fieldValues::surfaceFieldValue::regionType() const Foam::functionObjects::fieldValues::surfaceFieldValue::regionType() const
{ {
return regionType_; return regionType_;

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2015-2018 OpenCFD Ltd. \\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
| Copyright (C) 2011-2017 OpenFOAM Foundation | Copyright (C) 2011-2017 OpenFOAM Foundation
@ -64,7 +64,7 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::validField
( (
foundObject<smt>(fieldName) foundObject<smt>(fieldName)
|| foundObject<vf>(fieldName) || foundObject<vf>(fieldName)
|| (regionType_ != stSampledSurface && foundObject<sf>(fieldName)) || (withSurfaceFields() && foundObject<sf>(fieldName))
); );
} }
@ -74,7 +74,7 @@ Foam::tmp<Foam::Field<Type>>
Foam::functionObjects::fieldValues::surfaceFieldValue::getFieldValues Foam::functionObjects::fieldValues::surfaceFieldValue::getFieldValues
( (
const word& fieldName, const word& fieldName,
const bool mustGet const bool mandatory
) const ) const
{ {
typedef GeometricField<Type, fvsPatchField, surfaceMesh> sf; typedef GeometricField<Type, fvsPatchField, surfaceMesh> sf;
@ -85,7 +85,7 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::getFieldValues
{ {
return lookupObject<smt>(fieldName); return lookupObject<smt>(fieldName);
} }
else if (regionType_ != stSampledSurface && foundObject<sf>(fieldName)) else if (withSurfaceFields() && foundObject<sf>(fieldName))
{ {
return filterField(lookupObject<sf>(fieldName)); return filterField(lookupObject<sf>(fieldName));
} }
@ -93,16 +93,16 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::getFieldValues
{ {
const vf& fld = lookupObject<vf>(fieldName); const vf& fld = lookupObject<vf>(fieldName);
if (surfacePtr_.valid()) if (sampledPtr_.valid())
{ {
if (surfacePtr_().interpolate()) if (sampledPtr_().interpolate())
{ {
const interpolationCellPoint<Type> interp(fld); const interpolationCellPoint<Type> interp(fld);
tmp<Field<Type>> tintFld(surfacePtr_().interpolate(interp)); tmp<Field<Type>> tintFld(sampledPtr_().interpolate(interp));
const Field<Type>& intFld = tintFld(); const Field<Type>& intFld = tintFld();
// Average // Average
const faceList& faces = surfacePtr_().faces(); const faceList& faces = sampledPtr_().faces();
auto tavg = tmp<Field<Type>>::New(faces.size(), Zero); auto tavg = tmp<Field<Type>>::New(faces.size(), Zero);
auto& avg = tavg.ref(); auto& avg = tavg.ref();
@ -122,7 +122,7 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::getFieldValues
{ {
const interpolationCell<Type> interp(fld); const interpolationCell<Type> interp(fld);
return surfacePtr_().sample(interp); return sampledPtr_().sample(interp);
} }
} }
else else
@ -131,7 +131,7 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::getFieldValues
} }
} }
if (mustGet) if (mandatory)
{ {
FatalErrorInFunction FatalErrorInFunction
<< "Field " << fieldName << " not found in database" << "Field " << fieldName << " not found in database"