Loading...
Searching...
No Matches
fvExpressionField.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2021-2024 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
11 This file is part of OpenFOAM.
12
13 OpenFOAM is free software: you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25
26\*---------------------------------------------------------------------------*/
27
28#include "fvExpressionField.H"
29#include "volFields.H"
30#include "surfaceFields.H"
31#include "pointMesh.H"
32#include "pointFields.H"
33#include "volumeExprDriver.H"
37// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
38
39namespace Foam
40{
41namespace functionObjects
42{
45}
46}
47
48
49const Foam::Enum
50<
52>
54({
55 { actionType::opNone, "none" },
56 { actionType::opNew, "new" },
57 { actionType::opModify, "modify" },
58});
60
61// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
62
63namespace Foam
64{
65
67{
68 switch (geoType)
69 {
70 case expressions::FieldAssociation::POINT_DATA : return "points"; break;
71 case expressions::FieldAssociation::FACE_DATA : return "faces"; break;
72 case expressions::FieldAssociation::VOLUME_DATA : return "cells"; break;
73 default: break;
74 }
75 return "unknown";
76}
77
78
79template<class Type>
81(
82 bool correctBCs,
84)
85{
86 if (correctBCs)
87 {
88 // Info<< "Correcting boundary conditions: " << field.name() << nl;
89 field.correctBoundaryConditions();
90
91 // Ensure that calculated patches are updated
92 for (auto& pf : field.boundaryFieldRef())
93 {
95 {
96 pf = pf.patchInternalField();
97 }
98 }
99 }
100}
101
102
103template<class Type>
105(
106 bool correctBCs,
108)
109{
110 if (correctBCs)
111 {
112 // Info<< "Correcting boundary conditions: " << field.name() << nl;
113 field.correctBoundaryConditions();
114 }
115}
116
117
118template<class Type>
120(
121 bool correctBCs,
123)
124{}
126} // End namespace Foam
127
128
129// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
130
131template<class FieldType>
133{
134 if (io.isHeaderClass<FieldType>())
135 {
136 // Store field on mesh database
137 Log << " Reading " << io.name()
138 << " (" << FieldType::typeName << ')' << endl;
139
140 regIOobject::store(new FieldType(io, mesh_));
141 return true;
143
144 return false;
145}
146
147
148template<class Type>
150{
151 return
152 (
156 );
157}
158
159
161(
162 const UList<word>& fieldSet_
163)
164{
165 label nLoaded = 0;
166
167 for (const word& fieldName : fieldSet_)
168 {
169 // Already loaded?
170 const auto* ptr = mesh_.cfindObject<regIOobject>(fieldName);
171
172 if (ptr)
173 {
174 ++nLoaded;
176 << "readFields : "
177 << ptr->name() << " (" << ptr->type()
178 << ") already in database" << endl;
179 continue;
180 }
181
182 // Load field as necessary
184 (
185 fieldName,
186 mesh_.time().timeName(),
187 mesh_,
190 );
191
192 const bool ok =
193 (
194 io.typeHeaderOk<regIOobject>(false)
195 &&
196 (
197 loadField<scalar>(io)
198 || loadField<vector>(io)
199 || loadField<sphericalTensor>(io)
200 || loadField<symmTensor>(io)
201 || loadField<tensor>(io)
202 )
203 );
204
205 if (ok)
206 {
207 ++nLoaded;
208 }
209 else
210 {
212 << "readFields : failed to load " << fieldName << endl;
213 }
215
216 return nLoaded;
217}
218
219
220template<class GeoField>
222(
223 GeoField& output,
224 const GeoField& evaluated,
225 const boolField& fieldMask
226)
227{
228 label numValuesChanged = 0;
229
230 // Internal field
231 if (fieldMask.empty())
232 {
233 // No field-mask - set all
234 numValuesChanged = output.size();
235
236 output.primitiveFieldRef() = evaluated;
237 }
238 else
239 {
240 auto& internal = output.primitiveFieldRef();
241
242 forAll(internal, idx)
243 {
244 if (fieldMask[idx])
245 {
246 internal[idx] = evaluated[idx];
247 ++numValuesChanged;
248 }
249 }
250 }
251
252 // Boundary fields
253 forAll(evaluated.boundaryField(), patchi)
254 {
255 auto& pf = output.boundaryFieldRef()[patchi];
256
257 if (pf.patch().coupled())
258 {
259 pf == evaluated.boundaryField()[patchi];
260 }
261 }
262
263 doCorrectBoundaryConditions(true, output);
264
265 if (action_ == actionType::opModify && log)
266 {
267 const label numTotal = returnReduce(output.size(), sumOp<label>());
268 reduce(numValuesChanged, sumOp<label>());
269
270 Info<< this->name() << ": set ";
271 if (numValuesChanged == numTotal)
272 {
273 Info<< "all ";
274 }
275 else
276 {
277 Info<< numValuesChanged << " of ";
278 }
279 Info<< numTotal << " values (field: "
280 << output.name() << ')' << nl << endl;
281 }
282
283 if (hasDimensions_)
284 {
285 // Log<< "Setting dimensions to " << dims << endl;
286 output.dimensions().reset(dimensions_);
287 }
289 return true;
290}
291
292
293// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
294
296(
297 const word& name,
298 const Time& runTime,
299 const dictionary& dict,
300 const bool loadFromFiles
301)
302:
304 dict_(dict), // Deep copy
305 fieldName_(),
306 preloadFields_(),
307 maskExpr_(),
308 valueExpr_(),
309 dimensions_(),
310 action_(actionType::opNew),
311 autowrite_(false),
312 store_(true),
313 hasDimensions_(false),
314 loadFromFiles_(loadFromFiles)
316 read(dict);
317}
318
319
320// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
323{}
324
325
326// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
327
329{
330 switch (action_)
331 {
332 case actionType::opNone:
333 {
334 break; // No-op
335 }
336 case actionType::opNew:
337 {
338 return scopedName(fieldName_);
339 }
340 case actionType::opModify:
341 {
342 return fieldName_;
344 }
345
346 return word::null;
347}
348
349
351{
353
354 action_ = actionNames_.getOrDefault("action", dict, actionType::opNew);
355
356 fieldName_ = dict.get<word>("field");
357 const word fldName = fieldName();
358
359 Log << type() << ' ' << this->name() << ':' << nl
360 << " action = " << actionNames_[action_] << nl
361 << " field = " << fldName << nl;
362
363 maskExpr_.clear();
364 valueExpr_.clear();
365
366 preloadFields_.clear();
367 dict.readIfPresent("readFields", preloadFields_);
368
369 switch (action_)
370 {
371 case actionType::opNone:
372 {
373 // No-op
374 break;
375 }
376 case actionType::opModify:
377 {
378 // Optional <fieldMask> for modify
379 maskExpr_.readIfPresent("fieldMask", dict);
380 [[fallthrough]];
381 }
382 case actionType::opNew:
383 {
384 // Mandatory <expression> for new and modify
385 valueExpr_.readEntry("expression", dict);
386 break;
387 }
388 }
389
390 autowrite_ = dict.getOrDefault("autowrite", false);
391 store_ = dict.getOrDefault("store", true);
392
393 // "dimensions" is optional
394 dimensions_.clear();
395 hasDimensions_ = dimensions_.readIfPresent("dimensions", dict);
396
397 if (action_ == actionType::opNew)
398 {
399 if (!hasDimensions_)
400 {
401 Log << " no 'dimensions' : treat '" << fldName
402 << "' as dimensionless" << endl;
403 }
404 }
405 else
406 {
407 // Ignore for none/modify
408 hasDimensions_ = false;
409 }
410
411
412 if (action_ == actionType::opNone)
413 {
414 driver_.reset(nullptr);
415 return true; // Done
416 }
417
418 driver_.reset
419 (
420 new expressions::volumeExprDriver(mesh_, dict_)
421 );
422
423 driver_->setSearchBehaviour
424 (
426 (
428 | (
429 loadFromFiles_
431 : int(0)
432 )
433 ),
434 false // No caching
435 );
437 driver_->readDict(dict_);
438
439 return true;
440}
441
442
444{
446
447 if (!driver_ || action_ == actionType::opNone)
448 {
449 // No-op
450 return true;
451 }
452
453 const word fldName = fieldName();
454
455 if (loadFromFiles_)
456 {
457 loadFields(preloadFields_);
458 }
459
460 if (action_ == actionType::opModify && loadFromFiles_)
461 {
462 loadFields(wordList({fldName}));
463 }
464
465 auto& driver = *driver_;
466
467
468 // Current availability
469 auto* regIOobjectPtr = mesh_.getObjectPtr<regIOobject>(fldName);
470
471 if (action_ == actionType::opModify && !regIOobjectPtr)
472 {
473 // Cannot continue
475 << type() << ' ' << this->name() << ':' << nl
476 << " missing-field: " << fldName << nl
477 << exit(FatalError);
478
479 return false;
480 }
481
482
483 // Handle "field-mask" evaluation
484 bool evalFieldMask
485 (
486 (action_ == actionType::opModify)
487 && maskExpr_.size() && maskExpr_ != "true" && maskExpr_ != "1"
488 );
489
490 boolField fieldMask;
491 FieldAssociation maskFieldAssoc(FieldAssociation::NO_DATA);
492
493 if (evalFieldMask)
494 {
495 driver.parse(maskExpr_);
496
497 if (driver.isLogical())
498 {
499 auto& result = driver.result();
500 if (result.is_bool())
501 {
502 fieldMask = result.getResult<bool>();
503 maskFieldAssoc = driver.fieldAssociation();
504 }
505 }
506
507 // Slightly pedantic...
508 driver.clearField();
509 driver.clearResult();
510
511 evalFieldMask = (maskFieldAssoc != FieldAssociation::NO_DATA);
512
513 if (!evalFieldMask)
514 {
516 << "field-mask: " << maskExpr_
517 << " does not evaluate to a logical expression: "
518 << driver.resultType() << nl
519 #ifdef FULLDEBUG
520 << "contents: " << fieldMask
521 #endif
522 << exit(FatalError);
523 }
524 }
525
526
527 // Start "expression" evaluation
528
529 bool applied = false;
530 autoPtr<regIOobject> toutputField;
531
532 {
533 driver.clearVariables();
534
535 driver.parse(valueExpr_);
536
537 if (evalFieldMask && maskFieldAssoc != driver.fieldAssociation())
538 {
540 << "Mismatch between field-mask geometric type ("
541 << fieldGeoType(maskFieldAssoc) << ") and" << nl
542 << "expression geometric type ("
543 << fieldGeoType(driver.fieldAssociation()) << ')' << nl
544 << nl
545 << "Expression: " << valueExpr_ << nl
546 << "Field-mask: " << maskExpr_ << nl
547 << nl
548 << exit(FatalError);
549 }
550
551 // The output field does not appear to exist
552 // - create a new 'blank slate'
553 if (!regIOobjectPtr)
554 {
555 toutputField.reset(driver.dupZeroField());
556
557 if (toutputField)
558 {
559 toutputField->rename(fldName);
560
561 if (autowrite_)
562 {
563 toutputField->writeOpt(IOobject::AUTO_WRITE);
564 }
565 }
566
567 if (!store_)
568 {
569 // Local (non-registered) field only
570 regIOobjectPtr = toutputField.get();
571 }
572 else
573 {
574 if (toutputField->checkIn() && toutputField->store())
575 {
576 // Register and transfer ownership to registry
577 toutputField.release();
578 }
579
580 regIOobjectPtr = mesh_.getObjectPtr<regIOobject>(fldName);
581 }
582 }
583
584
585 // Additional checks (TBD):
586
587 if (!regIOobjectPtr)
588 {
589 // Cannot continue
591 << type() << ' ' << this->name() << ':' << nl
592 << " missing-field: " << fldName << nl
593 << exit(FatalError);
594 }
595
596 // const word oldFieldType = regIOobjectPtr->type();
597
598 // if (driver.resultType() != oldFieldType)
599 // {
600 // FatalErrorInFunction
601 // << "Inconsistent types: " << fldName << " is "
602 // << oldFieldType
603 // << " but the expression evaluates to "
604 // << driver.resultType()
605 // << exit(FatalError);
606 // }
607
608 switch (driver.fieldAssociation())
609 {
610 #undef doLocalCode
611 #define doLocalCode(GeoField) \
612 { \
613 /* FieldType */ \
614 auto* outPtr = dynamic_cast<GeoField*>(regIOobjectPtr); \
615 const auto* ptr = driver.isResultType<GeoField>(); \
616 \
617 if (outPtr && ptr) \
618 { \
619 applied = setField(*outPtr, *ptr, fieldMask); \
620 if (doWrite) \
621 { \
622 outPtr->write(); \
623 } \
624 break; \
625 } \
626 }
627
628 case FieldAssociation::VOLUME_DATA:
629 {
635 break;
636 }
637 case FieldAssociation::FACE_DATA:
638 {
644 break;
645 }
646 case FieldAssociation::POINT_DATA:
647 {
653 break;
654 }
655
656 default: break;
657 #undef doLocalCode
658 }
659 }
660
661
662 // Clear out heavier data
663 driver.clearResult();
664 driver.clearField();
665
666 if (!applied)
667 {
668 // Or error?
670 << type() << ' ' << this->name() << ": Failed to apply "
671 << actionNames_[action_] << " for " << fldName
672 << nl;
673 }
674
675 return true;
676}
677
682}
683
684
686{
687 return performAction(true);
688}
689
690
691// ************************************************************************* //
#define Log
Definition PDRblock.C:28
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition Enum.H:57
@ MUST_READ
Reading required.
@ NO_WRITE
Ignore writing from objectRegistry::writeObject().
@ AUTO_WRITE
Automatically write from objectRegistry::writeObject().
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition IOobject.H:191
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition Time.H:75
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition UList.H:89
bool empty() const noexcept
True if List is empty (ie, size() is zero).
Definition UList.H:701
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition autoPtr.H:65
T * get() noexcept
Return pointer to managed object without nullptr checking.
Definition autoPtr.H:216
T * release() noexcept
Release ownership and return the pointer.
Definition autoPtrI.H:28
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition autoPtrI.H:37
This boundary condition is not designed to be evaluated; it is assumed that the value is assigned via...
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
The field association for mesh (patch/volume) values.
searchControls
Search/caching controls.
Definition exprDriver.H:146
@ SEARCH_REGISTRY
Search registry before disk.
Definition exprDriver.H:148
@ SEARCH_FILES
Search disk (eg, standalone app).
Definition exprDriver.H:149
Abstract base-class for Time/database function objects.
virtual bool read(const dictionary &dict)
Read and set the function object if its data have changed.
word scopedName(const word &name) const
Return a scoped (prefixed) name.
Function object that generates or modifies a field based on expressions.
expressions::exprString maskExpr_
The field-mask expression (modify mode).
bool setField(GeoField &output, const GeoField &evaluated, const boolField &cond)
Sets the values of the output field based on a condition.
autoPtr< expressions::volumeExprDriver > driver_
bool performAction(bool doWrite)
Execute the requested action.
dimensionSet dimensions_
Dimensions for new field.
dictionary dict_
The context dictionary.
bool loadFromFiles_
Load fields from files (not from objectRegistry).
bool autowrite_
Set AUTO_WRITE for new field.
virtual bool read(const dictionary &dict)
Read the function-object dictionary.
bool hasDimensions_
True if dimensions_ should be used (creation).
bool loadAndStore(const IOobject &io)
Attempt load from io, store on database if successful.
static const Enum< actionType > actionNames_
Action type names.
@ opNew
Create/overwrite field (default).
label loadFields(const UList< word > &fieldSet_)
Attempt to load specified fields.
fvExpressionField(const word &name, const Time &runTime, const dictionary &dict, const bool loadFromFiles=false)
Construct from name, Time and dictionary.
virtual bool execute()
Execute the function-object operations.
virtual word fieldName() const
Qualified/unqualified field name (depends on action).
expressions::exprString valueExpr_
Expression to evaluate.
wordList preloadFields_
Names fields to preload.
virtual bool write()
Write the function-object results.
bool loadField(const IOobject &io)
Forward to loadAndStore for supported types.
Specialization of Foam::functionObject for an Foam::fvMesh, providing a reference to the Foam::fvMesh...
const fvMesh & mesh_
Reference to the fvMesh.
fvMeshFunctionObject(const fvMeshFunctionObject &)=delete
No copy construct.
Computes the natural logarithm of an input volScalarField.
Definition log.H:212
regIOobject is an abstract class derived from IOobject to handle automatic object registration with t...
Definition regIOobject.H:71
bool store()
Register object with its registry and transfer ownership to the registry.
A class for handling words, derived from Foam::string.
Definition word.H:66
static const word null
An empty word.
Definition word.H:84
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
rDeltaTY field()
engineTime & runTime
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
const auto & io
#define doLocalCode(FieldType, Variable)
auto & name
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
volumeExpr::parseDriver volumeExprDriver
Typedef for volumeExpr parseDriver.
Function objects are OpenFOAM utilities to ease workflow configurations and enhance workflows.
Namespace for OpenFOAM.
GeometricField< vector, fvsPatchField, surfaceMesh > surfaceVectorField
List< word > wordList
List of word.
Definition fileName.H:60
GeometricField< vector, fvPatchField, volMesh > volVectorField
GeometricField< Type, fvPatchField, volMesh > VolumeField
A volume field for a given type.
GeometricField< tensor, pointPatchField, pointMesh > pointTensorField
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition int32.H:127
GeometricField< scalar, pointPatchField, pointMesh > pointScalarField
GeometricField< sphericalTensor, pointPatchField, pointMesh > pointSphericalTensorField
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Field< bool > boolField
Specialisation of Field<T> for bool.
Definition boolField.H:47
messageStream Info
Information stream (stdout output on master, null elsewhere).
GeometricField< vector, pointPatchField, pointMesh > pointVectorField
GeometricField< scalar, fvsPatchField, surfaceMesh > surfaceScalarField
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition POSIX.C:801
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
GeometricField< symmTensor, pointPatchField, pointMesh > pointSymmTensorField
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
GeometricField< tensor, fvPatchField, volMesh > volTensorField
const Type * isA(const U &obj)
Attempt dynamic_cast to Type.
Definition typeInfo.H:87
GeometricField< tensor, fvsPatchField, surfaceMesh > surfaceTensorField
GeometricField< sphericalTensor, fvPatchField, volMesh > volSphericalTensorField
void reduce(T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce).
GeometricField< sphericalTensor, fvsPatchField, surfaceMesh > surfaceSphericalTensorField
GeometricField< symmTensor, fvPatchField, volMesh > volSymmTensorField
static void doCorrectBoundaryConditions(bool correctBCs, VolumeField< Type > &field)
GeometricField< Type, pointPatchField, pointMesh > PointField
A point field for a given type.
word fieldGeoType(const expressions::FieldAssociation geoType)
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
GeometricField< symmTensor, fvsPatchField, surfaceMesh > surfaceSymmTensorField
GeometricField< Type, fvsPatchField, surfaceMesh > SurfaceField
A (volume) surface field for a given type.
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
Foam::surfaceFields.