ENH: Added new caseInfo function object

Collects and writes case information to file in OpenFOAM dictionary or JSON
format.  Data includes:

- meta: case name, path, regions, parallel etc.
- dictionaries: entries retrieved from dictionaries - registered or from file
- per region: mesh metrics, boundary and boundary field types
- function object results

Example of function object specification:

    caseInfo
    {
        type            caseInfo;
        libs            (utilityFunctionObjects);

        // Warn when entries are not found
        lookupMode  warn; // none | warn | error;

        // Write format
        writeFormat json; //  dictionary | json;

        dictionaries
        {
            USolver // User-specified names
            {
                // Look up using registered name
                name        "fvSolution";

                // Optionally limit to specific entries
                include
                (
                    "solvers/U/solver"
                );
            }
            fvSchemes
            {
                name        "fvSchemes";

                // include all entries by default
            }
            timeScheme
            {
                name        "fvSchemes";

                include
                (
                    "/ddtSchemes/default"
                );
            }

            turbulence
            {
                name        "turbulenceProperties";

                // include all entries by default
            }
            controlDict
            {
                // Look up using file path
                path        "<case>/system/controlDict";

                include
                (
                    "application"
                    "deltaT"
                    "startTime"
                    "endTime"
                );
            }
        }

        functionObjects (minMax1);
    }
This commit is contained in:
Andrew Heather
2023-11-28 13:47:28 +00:00
parent d77db0edb9
commit 6242e70d09
3 changed files with 818 additions and 0 deletions

View File

@ -1,5 +1,7 @@
abort/abort.C
caseInfo/caseInfo.C
codedFunctionObject/codedFunctionObject.C
areaWrite/areaWrite.C

View File

@ -0,0 +1,529 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "caseInfo.H"
#include "OFstream.H"
#include "fvMesh.H"
#include "cloud.H"
#include "globalMeshData.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "processorFvPatch.H"
#include "JSONformatter.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
defineTypeNameAndDebug(caseInfo, 0);
addToRunTimeSelectionTable
(
functionObject,
caseInfo,
dictionary
);
}
const Enum<functionObjects::caseInfo::writeFormat>
Foam::functionObjects::caseInfo::writeFormatNames_
{
{ writeFormat::dict, "dictionary" },
{ writeFormat::json, "json" },
};
const Enum<functionObjects::caseInfo::lookupMode>
Foam::functionObjects::caseInfo::lookupModeNames_
{
{ lookupMode::none, "none" },
{ lookupMode::warn, "warn" },
{ lookupMode::error, "error" },
};
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::functionObjects::caseInfo::report(const string& str) const
{
switch (lookupMode_)
{
case lookupMode::warn:
{
Warning << str.c_str() << endl;
break;
}
case lookupMode::error:
{
FatalError << str.c_str() << exit(FatalError);
break;
}
case lookupMode::none:
{
break;
}
}
}
void Foam::functionObjects::caseInfo::processDict
(
dictionary& dict,
const dictionary& targetDict,
const entry* includePtr,
const entry* excludePtr
) const
{
auto sanitise = [](const wordRe& w){
string str = w;
str.replaceAll("/", "_").replaceAll(".", "_");
// Strip any leading "_"
while (str.starts_with('_'))
{
str = str.substr(1);
}
return str;
};
if (includePtr)
{
const wordRes includeEntryNames(includePtr->stream());
for (const auto& nameRegex : includeEntryNames)
{
const auto* e = targetDict.findScoped(nameRegex, keyType::REGEX);
if (e)
{
if (nameRegex.contains('/') || nameRegex.contains('.'))
{
auto copyPtr = e->clone();
copyPtr->keyword() = sanitise(nameRegex);
dict.add(copyPtr.ptr());
}
else
{
dict.add(*e);
}
}
else
{
report
(
"Unable to find entry "
+ nameRegex
+ " in dictionary "
+ targetDict.name()
);
}
}
}
else
{
if (excludePtr)
{
dictionary allData(targetDict);
const wordRes excludeEntryNames(excludePtr->stream());
for (const auto& nameRegex : excludeEntryNames)
{
const auto* e = allData.findScoped(nameRegex, keyType::REGEX);
if (e)
{
allData.remove(e->keyword());
}
}
dict += allData;
}
else
{
// Add complete dictionary
dict += targetDict;
}
}
}
// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * //
void Foam::functionObjects::caseInfo::writeMeta(dictionary& out) const
{
out.add("case", time_.globalCaseName());
out.add("path", time_.globalPath());
out.add("regions", time_.sortedNames<fvMesh>());
out.add("nTimes", time_.times().size());
out.add("nProc", Pstream::nProcs());
}
void Foam::functionObjects::caseInfo::writeRegisteredDicts
(
const objectRegistry& obr,
dictionary& out,
dictionary& dictionaries
) const
{
for (const auto& e : dictionaries)
{
const auto& keyword = e.keyword();
if (!e.isDict())
{
FatalIOErrorInFunction(dictionaries)
<< "Entries must be specified in dictionary format. Please "
<< "correct entry " << keyword
<< exit(FatalIOError);
}
const dictionary& inputDict = e.dict();
auto* includePtr = inputDict.findEntry("include");
auto* excludePtr = inputDict.findEntry("exclude");
const auto* ePtr = inputDict.findEntry("name");
if (ePtr)
{
const word name(ePtr->stream());
auto* dictPtr = obr.cfindObject<IOdictionary>(name);
if (dictPtr)
{
processDict
(
out.subDictOrAdd(keyword),
*dictPtr,
includePtr,
excludePtr
);
dictionaries.remove(keyword);
}
}
}
}
void Foam::functionObjects::caseInfo::writeFileDicts
(
dictionary& out,
dictionary& dictionaries
) const
{
for (auto& e : dictionaries)
{
const auto& keyword = e.keyword();
if (!e.isDict())
{
FatalIOErrorInFunction(dictionaries)
<< "Entries must be specified in dictionary format. Please "
<< "correct entry " << keyword
<< exit(FatalIOError);
}
const dictionary& inputDict = e.dict();
auto* includePtr = inputDict.findEntry("include");
auto* excludePtr = inputDict.findEntry("exclude");
const auto* ePtr = inputDict.findEntry("path");
if (ePtr)
{
fileName path(ePtr->stream());
path.expand();
IOobject io(path, time(), IOobject::MUST_READ);
if (!io.typeHeaderOk<dictionary>(false))
{
continue;
}
const word oldTypeName = IOdictionary::typeName;
const_cast<word&>(IOdictionary::typeName) = word::null;
processDict
(
out.subDictOrAdd(keyword),
IOdictionary(io),
includePtr,
excludePtr
);
const_cast<word&>(IOdictionary::typeName) = oldTypeName;
dictionaries.remove(keyword);
}
}
}
void Foam::functionObjects::caseInfo::writeFunctionObjects
(
dictionary& out
) const
{
for (const auto& fo : functionObjectNames_)
{
dictionary dict;
if (getObjectResultDict(fo, dict))
{
out.add(fo, dict);
}
else
{
report("No result entries found for function object " + fo);
}
}
}
void Foam::functionObjects::caseInfo::writeMeshStats
(
const polyMesh& mesh,
dictionary& dict
) const
{
dict.add("nGeometricD", mesh.nGeometricD());
dict.add("nSolutionD", mesh.nSolutionD());
const auto& globalData = mesh.globalData();
dict.add("nPoints", globalData.nTotalPoints());
dict.add("nFaces", globalData.nTotalFaces());
dict.add("nCells", globalData.nTotalCells());
dict.add("nPatches", mesh.boundaryMesh().nNonProcessor());
dict.add("pointZones", mesh.pointZones().names());
dict.add("faceZones", mesh.faceZones().names());
dict.add("cellZones", mesh.cellZones().names());
dict.add("boundsMin", mesh.bounds().min());
dict.add("boundsMax", mesh.bounds().max());
dict.add("clouds", mesh.sortedNames<cloud>());
}
namespace Foam
{
template<class Type>
void addPatchTypeDetails(const fvMesh& mesh, dictionary& dict)
{
auto objects = mesh.lookupClass<Type>();
for (const auto* objPtr : objects)
{
if (objPtr->readOpt() == IOobject::MUST_READ)
{
const auto& bf = objPtr->boundaryField();
dictionary& objDict = dict.subDictOrAdd(objPtr->name());
for (const auto& pf : bf)
{
if (!isA<processorFvPatch>(pf.patch()))
{
objDict.add(pf.patch().name(), pf.type());
}
}
}
}
}
template<template<typename> class FieldType>
void addPatchDetails(const fvMesh& mesh, dictionary& dict)
{
addPatchTypeDetails<FieldType<scalar>>(mesh, dict);
addPatchTypeDetails<FieldType<vector>>(mesh, dict);
addPatchTypeDetails<FieldType<sphericalTensor>>(mesh, dict);
addPatchTypeDetails<FieldType<symmTensor>>(mesh, dict);
addPatchTypeDetails<FieldType<tensor>>(mesh, dict);
}
}
void Foam::functionObjects::caseInfo::writePatches
(
const fvMesh& mesh,
dictionary& dict
) const
{
// Geometry
dictionary& bnd = dict.subDictOrAdd("types");
const auto& pbm = mesh.boundaryMesh();
for (const auto& pp : pbm)
{
if (!isA<processorPolyPatch>(pp))
{
bnd.add(pp.name(), pp.type());
}
}
// Fields
dictionary& fld = dict.subDictOrAdd("fields");
addPatchDetails<VolumeField>(mesh, fld);
addPatchDetails<SurfaceField>(mesh, fld);
//addPatchDetails<AreaField>(mesh, fld);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::caseInfo::caseInfo
(
const word& name,
const Time& runTime,
const dictionary& dict
)
:
stateFunctionObject(name, runTime),
writeFile(runTime, name, typeName, dict),
writeFormat_(writeFormat::dict),
lookupMode_(lookupMode::warn)
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::caseInfo::read(const dictionary& dict)
{
if (stateFunctionObject::read(dict) && writeFile::read(dict))
{
writeFormat_ = writeFormatNames_.get("writeFormat", dict);
lookupModeNames_.readIfPresent("lookupMode", dict, lookupMode_);
dictionaries_ = dict.subOrEmptyDict("dictionaries");
functionObjectNames_ =
dict.getOrDefault<wordList>("functionObjects", wordList());
return true;
}
return false;
}
bool Foam::functionObjects::caseInfo::execute()
{
return true;
}
bool Foam::functionObjects::caseInfo::write()
{
// Output dictionary
dictionary data;
// Case meta data
writeMeta(data.subDictOrAdd("meta"));
// Note: copying dictionaries
// - these are consumed/removed when found to enable checks that all
// dictionaries are processed
dictionary dicts(dictionaries_);
dictionary& dataDicts = data.subDictOrAdd("dictionaries");
// Time-registered dictionaries
writeRegisteredDicts(time_, dataDicts, dicts);
// File-based dictionaries
writeFileDicts(dataDicts, dicts);
// Per-region information
const auto meshes = time_.lookupClass<fvMesh>();
dictionary& regionDict = data.subDictOrAdd("regions");
for (const auto& iter : meshes.csorted())
{
dictionary meshDicts(dicts);
const fvMesh& mesh = *iter.val();
const word& name = mesh.name();
dictionary& out = regionDict.subDictOrAdd(name);
writeMeshStats(mesh, out.subDictOrAdd("mesh"));
writePatches(mesh, out.subDictOrAdd("boundary"));
// Mesh-registered dictionaries
writeRegisteredDicts(mesh, out.subDictOrAdd("dictionaries"), meshDicts);
for (const word& keyword : meshDicts.sortedToc())
{
report
(
"Mesh '"
+ keyword
+ "' : Unable to process dictionary entry '"
+ keyword
+ "'"
);
}
}
writeFunctionObjects(data.subDictOrAdd("functions"));
if (Pstream::master())
{
auto filePtr = newFileAtTime(name(), time_.value());
auto& os = filePtr();
// Reset stream width - was set in writeFile
os.width(0);
switch (writeFormat_)
{
case writeFormat::dict:
{
os << data << endl;
break;
}
case writeFormat::json:
{
JSONformatter json(os);
json.writeDict(data);
break;
}
}
Info<< "Written " << writeFormatNames_[writeFormat_]
<< " file: " << os.name() << endl;
}
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,287 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::functionObjects::caseInfo
Description
Collects and writes case information to file.
Example of function object specification:
\verbatim
caseInfo
{
type caseInfo;
libs (utilityFunctionObjects);
// Warn when entries are not found
lookupMode warn; // none | warn | error;
// Write format
writeFormat json; // dictionary | json;
dictionaries
{
USolver // User-specified names
{
// Look up using registered name
name "fvSolution";
// Optionally limit to specific entries
include
(
"solvers/U/solver"
);
}
fvSchemes
{
name "fvSchemes";
// include all entries by default
}
timeScheme
{
name "fvSchemes";
include
(
"/ddtSchemes/default"
);
}
turbulence
{
name "turbulenceProperties";
// include all entries by default
}
controlDict
{
// Look up using file path
path "<case>/system/controlDict";
include
(
"application"
"deltaT"
"startTime"
"endTime"
);
}
}
functionObjects (minMax1); // v2306 only
}
\endverbatim
Where the entries comprise:
\table
Property | Description | Required | Default
type | Type name: caseInfo | yes |
lookupMode | Lookup mode | no | warn
writeFormat | Write format | yes |
dictionaries | Dictionaries to process | no | \<none\>
functionObjects | Function objects to process | no | \<none\>
\endtable
See also
Foam::functionObject
Foam::timeFunctionObject
SourceFiles
caseInfo.C
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_caseInfo_H
#define functionObjects_caseInfo_H
#include "writeFile.H"
#include "stateFunctionObject.H"
#include "Enum.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declarations
class fvMesh;
namespace functionObjects
{
/*---------------------------------------------------------------------------*\
Class caseInfo Declaration
\*---------------------------------------------------------------------------*/
class caseInfo
:
public stateFunctionObject,
public writeFile
{
public:
// Public enumerations
//- Write format enumeration
enum class writeFormat
{
dict,
json
};
//- Lookup mode enumeration
enum class lookupMode
{
none,
warn,
error
};
private:
// Private Member Data
//- Write format names
static const Enum<writeFormat> writeFormatNames_;
//- Lookup mode names
static const Enum<lookupMode> lookupModeNames_;
//- Write/output format, e.g. dictionary, JSON
writeFormat writeFormat_;
//- Lookup mode when reading entries
lookupMode lookupMode_;
//- Dictionaries
dictionary dictionaries_;
//- List of function objects to process
wordList functionObjectNames_;
// Private Member Functions
//- Report
void report(const string& str) const;
//- Process dictionary
void processDict
(
dictionary& dict,
const dictionary& targetDict,
const entry* includePtr,
const entry* excludePtr
) const;
protected:
// Protected Member Functions
//- No copy construct
caseInfo(const caseInfo&) = delete;
//- No copy assignment
void operator=(const caseInfo&) = delete;
// Write data
//- Write case meta data
void writeMeta(dictionary& dict) const;
//- Write registered dictionaries
void writeRegisteredDicts
(
const objectRegistry& obr,
dictionary& dict,
dictionary& dictionaries
) const;
//- Write file-based dictionaries
void writeFileDicts
(
dictionary& dict,
dictionary& dictionaries
) const;
//- Write function object results
void writeFunctionObjects(dictionary& dict) const;
//- Write mesh statistics
void writeMeshStats(const polyMesh& mesh, dictionary& dict) const;
//- Write mesh patches
void writePatches(const fvMesh& mesh, dictionary& dict) const;
public:
//- Runtime type information
TypeName("caseInfo");
// Constructors
//- Construct from Time and dictionary
caseInfo
(
const word& name,
const Time& runTime,
const dictionary& dict
);
//- Destructor
virtual ~caseInfo() = default;
// Member Functions
//- Read the controls
virtual bool read(const dictionary& dict);
//- Execute, does nothing
virtual bool execute();
//- Write the caseInfo
virtual bool write();
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //