diff --git a/src/functionObjects/utilities/Make/files b/src/functionObjects/utilities/Make/files index bc759df937..2e3e9c84f4 100644 --- a/src/functionObjects/utilities/Make/files +++ b/src/functionObjects/utilities/Make/files @@ -1,5 +1,7 @@ abort/abort.C +caseInfo/caseInfo.C + codedFunctionObject/codedFunctionObject.C areaWrite/areaWrite.C diff --git a/src/functionObjects/utilities/caseInfo/caseInfo.C b/src/functionObjects/utilities/caseInfo/caseInfo.C new file mode 100644 index 0000000000..cee60e20b0 --- /dev/null +++ b/src/functionObjects/utilities/caseInfo/caseInfo.C @@ -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 . +\*---------------------------------------------------------------------------*/ + +#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 +Foam::functionObjects::caseInfo::writeFormatNames_ +{ + { writeFormat::dict, "dictionary" }, + { writeFormat::json, "json" }, +}; + +const Enum +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()); + 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(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(false)) + { + continue; + } + + const word oldTypeName = IOdictionary::typeName; + const_cast(IOdictionary::typeName) = word::null; + + processDict + ( + out.subDictOrAdd(keyword), + IOdictionary(io), + includePtr, + excludePtr + ); + + const_cast(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()); +} + + +namespace Foam +{ + template + void addPatchTypeDetails(const fvMesh& mesh, dictionary& dict) + { + auto objects = mesh.lookupClass(); + 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(pf.patch())) + { + objDict.add(pf.patch().name(), pf.type()); + } + } + } + } + } + + template class FieldType> + void addPatchDetails(const fvMesh& mesh, dictionary& dict) + { + addPatchTypeDetails>(mesh, dict); + addPatchTypeDetails>(mesh, dict); + addPatchTypeDetails>(mesh, dict); + addPatchTypeDetails>(mesh, dict); + addPatchTypeDetails>(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(pp)) + { + bnd.add(pp.name(), pp.type()); + } + } + + // Fields + dictionary& fld = dict.subDictOrAdd("fields"); + addPatchDetails(mesh, fld); + addPatchDetails(mesh, fld); + //addPatchDetails(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("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(); + 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; +} + + +// ************************************************************************* // \ No newline at end of file diff --git a/src/functionObjects/utilities/caseInfo/caseInfo.H b/src/functionObjects/utilities/caseInfo/caseInfo.H new file mode 100644 index 0000000000..0be7f29294 --- /dev/null +++ b/src/functionObjects/utilities/caseInfo/caseInfo.H @@ -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 . + +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 "/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 | \ + functionObjects | Function objects to process | no | \ + \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 writeFormatNames_; + + //- Lookup mode names + static const Enum 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 + +// ************************************************************************* // \ No newline at end of file