openfoam/applications/utilities/preProcessing/setExprFields/setExprFields.C
Mark Olesen 5a121119e6 ENH: add -verbose support into argList
- similar to -dry-run handling, can be interrogated from argList,
  which makes it simpler to add into utilities.

- support multiple uses of -dry-run and -verbose to increase the
  level. For example, could have

    someApplication -verbose -verbose

 and inside of the application:

    if (args.verbose() > 2) ...

BUG: error with empty distributed roots specification (fixes #2196)

- previously used the size of distributed roots to transmit if the
  case was running in distributed mode, but this behaves rather poorly
  with bad input. Specifically, the following questionable setup:

      distributed true;
      roots ( /*none*/ );

  Now transmit the ParRunControl distributed() value instead,
  and also emit a gentle warning for the user:

      WARNING: running distributed but did not specify roots!
2021-11-09 15:44:54 +01:00

968 lines
27 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019-2021 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/>.
Application
setExprFields
Group
grpPreProcessingUtilities
Description
Set values on a selected set of cells/patch-faces via a dictionary.
Note
Based on funkySetFields from
Bernhard Gschaider <bgschaid@hfd-research.com>
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "fvMesh.H"
#include "pointMesh.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "pointFields.H"
#include "exprOps.H"
#include "volumeExprDriver.H"
#include "timeSelector.H"
#include "readFields.H"
using namespace Foam;
using FieldAssociation = expressions::volumeExpr::FieldAssociation;
word fieldGeoType(const FieldAssociation geoType)
{
switch (geoType)
{
case FieldAssociation::VOLUME_DATA : return "cells"; break;
case FieldAssociation::SURFACE_DATA : return "faces"; break;
case FieldAssociation::POINT_DATA : return "points"; break;
default: break;
}
return "unknown";
}
//- Simple control structure to with collected switches to simplify passing
struct setExprFieldsControl
{
bool dryRun;
bool debugParsing;
bool cacheVariables;
bool useDimensions;
bool createNew;
bool keepPatches;
bool correctPatches;
bool correctBCs;
IOstreamOption streamOpt;
};
template<class Type>
void doCorrectBoundaryConditions
(
bool correctBCs,
GeometricField<Type, fvPatchField, volMesh>& field
)
{
if (correctBCs)
{
Info<< "Correcting boundary conditions: " << field.name() << nl;
field.correctBoundaryConditions();
}
}
template<class Type>
void doCorrectBoundaryConditions
(
bool correctBCs,
GeometricField<Type, pointPatchField, pointMesh>& field
)
{
if (correctBCs)
{
Info<< "Correcting boundary conditions: " << field.name() << nl;
field.correctBoundaryConditions();
}
}
template<class Type>
void doCorrectBoundaryConditions
(
bool correctBCs,
GeometricField<Type, fvsPatchField, surfaceMesh>& field
)
{}
template<class GeoField, class Mesh>
void setField
(
const word& fieldName,
const Mesh& mesh,
const GeoField& result,
const scalarField& cond,
const dimensionSet& dims,
const wordList& valuePatches,
const setExprFieldsControl& ctrl
)
{
Info<< "setField(" << fieldName << "): "
<< pTraits<GeoField>::typeName << endl;
tmp<GeoField> toutput;
if (ctrl.createNew)
{
// Create with zero
toutput = GeoField::New
(
fieldName,
mesh,
dimensioned<typename GeoField::value_type>(dims)
);
}
else
{
// Read
toutput = tmp<GeoField>::New
(
IOobject
(
fieldName,
mesh.thisDb().time().timeName(),
mesh.thisDb(),
IOobject::MUST_READ,
IOobject::NO_WRITE,
false // No register
),
mesh
);
}
auto& output = toutput.ref();
label setCells = 0;
if (cond.empty())
{
// No condition - set all
output = result;
setCells = output.size();
}
else
{
forAll(output, celli)
{
if (expressions::boolOp<scalar>()(cond[celli]))
{
output[celli] = result[celli];
++setCells;
}
}
}
const label totalCells = returnReduce(output.size(), plusOp<label>());
reduce(setCells, plusOp<label>());
forAll(result.boundaryField(), patchi)
{
auto& pf = output.boundaryFieldRef()[patchi];
if (pf.patch().coupled())
{
pf == result.boundaryField()[patchi];
}
}
if (setCells == totalCells)
{
Info<< "Set all ";
}
else
{
Info<< "Set " << setCells << " of ";
}
Info<< totalCells << " cells" << endl;
doCorrectBoundaryConditions(ctrl.correctBCs, output);
if (ctrl.useDimensions)
{
Info<< "Setting dimensions to " << dims << endl;
output.dimensions().reset(dims);
}
if (ctrl.dryRun)
{
Info<< "(dry-run): Writing to " << output.name() << nl;
}
else
{
Info<< "Writing to " << output.name() << nl;
output.writeObject(ctrl.streamOpt, true);
}
}
void evaluate
(
const fvMesh& mesh,
const word& fieldName,
const expressions::exprString& expression,
const expressions::exprString& condition,
const dictionary& dict,
const dimensionSet& dims,
const wordList& valuePatches,
const setExprFieldsControl& ctrl
)
{
word oldFieldType;
if (ctrl.createNew)
{
Info<< "Set new field: " << fieldName;
}
else
{
IOobject io
(
fieldName,
mesh.thisDb().time().timeName(),
mesh.thisDb(),
IOobject::MUST_READ,
IOobject::NO_WRITE
);
io.typeHeaderOk<IOobject>(false);
oldFieldType = io.headerClassName();
if (oldFieldType == IOobject::typeName)
{
FatalErrorInFunction
<< "Field " << fieldName << " is "
<< oldFieldType
<< ". Seems that it does not exist. Use 'create'"
<< nl
<< exit(FatalError);
}
Info<< "Modify field: " << fieldName
<< " (type " << oldFieldType << ')';
}
Info<< " time=" << mesh.thisDb().time().timeName() << nl
<< "Expression:" << nl
<< ">>>>" << nl
<< expression.c_str() << nl
<< "<<<<" << nl;
if (condition.size() && condition != "true")
{
Info<< "Condition:" << nl
<< ">>>>" << nl
<< condition.c_str() << nl
<< "<<<<" << nl;
}
if (ctrl.keepPatches)
{
Info<< "Keeping patches unaltered" << endl;
}
else if (!valuePatches.empty())
{
Info<< "Setting patches " << flatOutput(valuePatches)
<< " to fixed value" << endl;
}
Info<< endl;
expressions::volumeExprDriver driver(mesh);
driver.setCaching(ctrl.cacheVariables);
driver.readDict(dict);
if (ctrl.debugParsing)
{
Info<< "Parsing expression: " << expression << "\nand condition "
<< condition << nl << endl;
driver.setDebugging(true, true);
}
driver.clearVariables();
scalarField conditionField;
bool evaluatedCondition = false;
FieldAssociation conditionDataType(FieldAssociation::VOLUME_DATA);
if (condition.size() && condition != "true")
{
if (ctrl.debugParsing)
{
Info<< "Parsing condition:" << condition << endl;
}
driver.parse(condition);
if (ctrl.debugParsing)
{
Info<< "Parsed condition" << endl;
}
// Process any/all scalar fields. May help with diagnosis
bool goodCond = true;
while (goodCond)
{
// volScalarField
{
const auto* ptr = driver.isResultType<volScalarField>();
if (ptr)
{
conditionField = ptr->internalField();
break;
}
}
// surfaceScalarField
{
const auto* ptr = driver.isResultType<surfaceScalarField>();
if (ptr)
{
conditionField = ptr->internalField();
conditionDataType = FieldAssociation::SURFACE_DATA;
break;
}
}
// pointScalarField
{
const auto* ptr = driver.isResultType<pointScalarField>();
if (ptr)
{
conditionField = ptr->internalField();
conditionDataType = FieldAssociation::POINT_DATA;
break;
}
}
// No matching field types
goodCond = false;
}
// Verify that it also logical
goodCond = goodCond && driver.isLogical();
if (!goodCond)
{
FatalErrorInFunction
<< " condition: " << condition
<< " does not evaluate to a logical expression: "
<< driver.resultType() << nl
#ifdef FULLDEBUG
<< "contents: " << conditionField
#endif
<< exit(FatalError);
}
if (ctrl.debugParsing)
{
Info<< "Condition evaluates to "
<< conditionField << nl;
}
evaluatedCondition = true;
}
if (ctrl.debugParsing)
{
Info<< "Parsing expression:" << expression << endl;
}
driver.parse(expression);
if (ctrl.debugParsing)
{
Info<< "Parsed expression" << endl;
}
if (evaluatedCondition)
{
if (conditionDataType != driver.fieldAssociation())
{
FatalErrorInFunction
<< "Mismatch between condition geometric type ("
<< fieldGeoType(conditionDataType) << ") and" << nl
<< "expression geometric type ("
<< fieldGeoType(driver.fieldAssociation()) << ')' << nl
<< nl
<< "Expression: " << expression << nl
<< "Condition: " << condition << nl
<< nl
<< exit(FatalError);
}
}
if (!ctrl.createNew && driver.resultType() != oldFieldType)
{
FatalErrorInFunction
<< "Inconsistent types: " << fieldName << " is "
<< oldFieldType
<< " but the expression evaluates to "
<< driver.resultType()
<< exit(FatalError);
}
Info<< "Dispatch ... " << driver.resultType() << nl;
#undef setFieldDispatch
#define setFieldDispatch(FieldType) \
{ \
/* FieldType */ \
const auto* ptr = driver.isResultType<FieldType>(); \
if (ptr) \
{ \
/* driver.getResult<FieldType>(correctPatches), */ \
\
setField \
( \
fieldName, \
mesh, \
*ptr, \
conditionField, \
dims, \
valuePatches, \
ctrl \
); \
return; \
} \
} \
setFieldDispatch(volScalarField);
setFieldDispatch(volVectorField);
setFieldDispatch(volTensorField);
setFieldDispatch(volSymmTensorField);
setFieldDispatch(volSphericalTensorField);
setFieldDispatch(surfaceScalarField);
setFieldDispatch(surfaceVectorField);
setFieldDispatch(surfaceTensorField);
setFieldDispatch(surfaceSymmTensorField);
setFieldDispatch(surfaceSphericalTensorField);
#undef setFieldDispatch
#define setFieldDispatch(FieldType) \
{ \
/* FieldType */ \
const auto* ptr = driver.isResultType<FieldType>(); \
\
if (ptr) \
{ \
/* driver.getResult<FieldType>(correctPatches), */ \
\
setField \
( \
fieldName, \
pointMesh::New(mesh), \
*ptr, \
conditionField, \
dims, \
valuePatches, \
ctrl \
); \
return; \
} \
} \
setFieldDispatch(pointScalarField);
setFieldDispatch(pointVectorField);
setFieldDispatch(pointTensorField);
setFieldDispatch(pointSymmTensorField);
setFieldDispatch(pointSphericalTensorField);
#undef setFieldDispatch
// Nothing dispatched?
FatalErrorInFunction
<< "Expression evaluates to an unsupported type: "
<< driver.resultType() << nl << nl
<< "Expression " << expression << nl << endl
<< exit(FatalError);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noFunctionObjects(true);
// No -constant, no special treatment for 0/
timeSelector::addOptions(false);
argList::addBoolOption
(
"ascii",
"Write in ASCII format instead of the controlDict setting"
);
argList::addOption
(
"dict",
"file",
"Alternative dictionary for setExprFieldsDict"
);
argList::addDryRunOption
(
"Evaluate but do not write"
);
argList::addVerboseOption
(
"Additional verbosity",
true // Advanced option
);
argList::addOption
(
"load-fields",
"wordList",
"Specify field or fields to preload. Eg, 'T' or '(p T U)'",
true // Advanced option
);
argList::addOption
(
"field",
"name",
"The field to create/overwrite"
" (command-line operation)",
true // Advanced option
);
argList::addOption
(
"expression",
"expr",
"The expression to evaluate"
" (command-line operation)",
true // Advanced option
);
argList::addOption
(
"condition",
"logic",
"The logical condition when to apply the expression"
" (command-line operation)",
true // Advanced option
);
argList::addOption
(
"dimensions",
"dims",
"The dimensions for created fields"
" (command-line operation)",
true // Advanced option
);
argList::addOptionCompat("dimensions", {"dimension", 2012});
argList::addBoolOption
(
"debug-parser",
"Additional debugging information",
true // Advanced option
);
argList::addBoolOption
(
"no-variable-cache",
"Disable caching of expression variables",
true // Advanced option
);
argList::addBoolOption
(
"create",
"Create a new field"
" (command-line operation)",
true // Advanced option
);
argList::addBoolOption
(
"keepPatches",
"Leave patches unaltered"
" (command-line operation)",
true // Advanced option
);
argList::addOption
(
"value-patches",
"(patches)",
"A list of patches that receive a fixed value"
" (command-line operation)",
true // Advanced option
);
argList::addBoolOption
(
"dummy-phi",
"Provide a zero phi field"
" (command-line operation)",
true // Advanced option
);
// Future?
#if 0
argList::addBoolOption
(
"noCorrectPatches",
""
);
argList::addBoolOption
(
"correctResultBoundaryFields",
"",
true
);
#endif
#include "addRegionOption.H"
#include "setRootCase.H"
#include "createTime.H"
const word dictName("setExprFieldsDict");
instantList times = timeSelector::select0(runTime, args);
if (times.empty())
{
FatalErrorInFunction
<< "No times selected." << nl
<< exit(FatalError);
}
// Disable dimension checking during operations
dimensionSet::checking(false);
#include "createNamedMesh.H"
autoPtr<surfaceScalarField> dummyPhi;
autoPtr<IOdictionary> exprDictPtr;
// Sort out conflicts
const bool useCommandArgs = args.found("field");
if (useCommandArgs)
{
bool fatalCombination = false;
if (args.found("dict"))
{
fatalCombination = true;
FatalErrorInFunction
<< "Cannot specify both dictionary and command-line arguments"
<< nl << endl;
}
if (args.found("create") && args.found("keepPatches"))
{
fatalCombination = true;
FatalErrorInFunction
<< "Cannot specify both 'create' and 'keepPatches'" << nl
<< endl;
}
if (!args.found("expression"))
{
fatalCombination = true;
FatalErrorInFunction
<< "Missing mandatory 'expression' option'" << nl
<< endl;
}
if (fatalCombination)
{
FatalError
<< exit(FatalError);
}
}
else
{
// Carp about inapplicable options (non-fatal)
wordHashSet badOptions
({
"create", "keepPatches", "value-patches",
"condition", "expression", "dimensions"
});
badOptions.retain(args.options());
if (!badOptions.empty())
{
// Non-fatal (warning)
FatalErrorInFunction
<< "Using a dictionary. Cannot specify these options:" << nl
<< flatOutput(badOptions.sortedToc()) << nl
<< endl;
}
#include "setSystemMeshDictionaryIO.H"
exprDictPtr.reset(new IOdictionary(dictIO));
}
forAll(times, timei)
{
runTime.setTime(times[timei], timei);
Info<< "\nTime = " << runTime.timeName() << endl;
mesh.readUpdate();
// preload fields specified on command-line
if (timei == 0)
{
wordList preloadFields;
args.readListIfPresent("load-fields", preloadFields);
readFieldsHandler(mesh).execute(preloadFields);
}
if (args.found("dummy-phi") && !dummyPhi)
{
Info<< "Adding a dummy phi" << endl;
dummyPhi.reset
(
new surfaceScalarField
(
IOobject
(
"phi",
mesh.thisDb().time().constant(),
mesh.thisDb(),
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh,
dimensionedScalar(Zero)
)
);
}
if (args.found("withFunctionObjects"))
{
runTime.functionObjects().start();
}
if (useCommandArgs)
{
const word fieldName(args.get<word>("field"));
Info<< "Using command-line options for "
<< fieldName << nl << endl;
setExprFieldsControl ctrl;
ctrl.dryRun = args.dryRun();
ctrl.debugParsing = args.found("debug-parser");
ctrl.cacheVariables = !args.found("no-variable-caching");
ctrl.createNew = args.found("create");
ctrl.keepPatches = args.found("keepPatches");
ctrl.correctPatches = !args.found("noCorrectPatches");
ctrl.correctBCs = args.found("correctResultBoundaryFields");
ctrl.useDimensions = args.found("dimensions");
ctrl.streamOpt.format(runTime.writeFormat());
if (args.found("ascii"))
{
ctrl.streamOpt.format(IOstream::ASCII);
}
expressions::exprString
expression
(
args["expression"],
dictionary::null
);
expressions::exprString condition;
args.readIfPresent("condition", condition);
dimensionSet dims;
if (ctrl.useDimensions)
{
ITstream is(args.lookup("dimensions"));
is >> dims;
}
evaluate
(
mesh,
fieldName,
expression,
condition,
dictionary::null,
dims,
args.getList<word>("value-patches", false),
ctrl
);
}
else if (exprDictPtr)
{
const dictionary& exprDict = *exprDictPtr;
// preload fields specified in dictionary
{
wordList preloadFields;
exprDict.readIfPresent("readFields", preloadFields);
readFieldsHandler(mesh).execute(preloadFields);
}
// Read set construct info from dictionary
PtrList<entry> actions(exprDict.lookup("expressions"));
for (const entry& dEntry : actions)
{
if (!dEntry.isDict())
{
Info<< "Ignore non-dictionary entry: "
<< dEntry.keyword() << nl;
continue;
}
const dictionary& dict = dEntry.dict();
setExprFieldsControl ctrl;
ctrl.dryRun = args.dryRun();
ctrl.debugParsing = args.found("debug-parser");
ctrl.cacheVariables = !args.found("no-variable-caching");
ctrl.createNew = dict.getOrDefault("create", false);
ctrl.keepPatches = dict.getOrDefault("keepPatches", false);
ctrl.correctPatches = !args.found("noCorrectPatches");
ctrl.correctBCs = args.found("correctResultBoundaryFields");
ctrl.streamOpt.format(runTime.writeFormat());
if (args.found("ascii"))
{
ctrl.streamOpt.format(IOstream::ASCII);
}
if (ctrl.createNew && ctrl.keepPatches)
{
FatalIOErrorInFunction(dict)
<< "Cannot specify both 'create' and 'keepPatches'"
<< nl << endl
<< exit(FatalIOError);
}
// Local override
dict.readIfPresent
(
"correctResultBoundaryFields",
ctrl.correctBCs
);
const word fieldName(dict.get<word>("field"));
expressions::exprString expression
(
dict.get<string>("expression"),
dict
);
expressions::exprString condition;
if (dict.found("condition"))
{
condition =
expressions::exprString
(
dict.get<string>("condition"),
dict
);
}
dimensionSet dims;
{
const entry* dimPtr = dict.findCompat
(
"dimensions", {{"dimension", 2012}},
keyType::LITERAL
);
if (dimPtr)
{
dimPtr->stream() >> dims;
}
ctrl.useDimensions = bool(dimPtr);
}
if (args.verbose() && !timei)
{
// Report once
Info<< "Processing" << dict << nl;
}
evaluate
(
mesh,
fieldName,
expression,
condition,
dict,
dims,
dict.getOrDefault<wordList>("valuePatches", wordList()),
ctrl
);
}
}
else
{
FatalErrorInFunction
<< "No command-line or dictionary??" << nl << endl
<< exit(FatalError);
}
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //