SemiImplicitSource.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) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2020-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "SemiImplicitSource.H"
30#include "fvMesh.H"
31#include "fvMatrices.H"
32#include "fvmSup.H"
33#include "Constant.H"
34#include "Tuple2.H"
35
36// * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * //
37
38template<class Type>
39const Foam::Enum
40<
42>
44({
45 { volumeModeType::vmAbsolute, "absolute" },
46 { volumeModeType::vmSpecific, "specific" },
47});
48
49
50// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
51
52template<class Type>
54(
55 const dictionary& dict,
56 const word& keyExplicit,
57 const word& keyImplicit
58)
59{
60 label count = dict.size();
61
62 fieldNames_.resize_nocopy(count);
63
64 Su_.clear();
65 Sp_.clear();
66 Su_.resize(2*count);
67 Sp_.resize(2*count);
68
69 driverSu_.clear();
70 driverSp_.clear();
71 driverSu_.resize(2*count);
72 driverSp_.resize(2*count);
73
74 valueExprSu_.clear();
75 valueExprSp_.clear();
76 valueExprSu_.resize(2*count);
77 valueExprSp_.resize(2*count);
78
79 fv::option::resetApplied();
80
81 word modelType;
82 Tuple2<Type, scalar> sourceRates;
83
84 count = 0;
85
86 for (const entry& dEntry : dict)
87 {
88 const word& fieldName = dEntry.keyword();
89 bool ok = false;
90
91 if (dEntry.isDict())
92 {
93 const dictionary& subDict = dEntry.dict();
94
95 const entry* eptr;
96
97 if
98 (
99 (eptr = subDict.findEntry(keyExplicit, keyType::LITERAL))
100 != nullptr
101 )
102 {
103 ok = true;
104
105 if
106 (
107 eptr->isDict()
108 && eptr->dict().readEntry("type", modelType, keyType::LITERAL)
109 && (modelType == "exprField")
110 )
111 {
112 const dictionary& exprDict = eptr->dict();
113
114 valueExprSu_.emplace_set(fieldName);
115 valueExprSu_[fieldName].readEntry("expression", exprDict);
116
117 driverSu_.set
118 (
119 fieldName,
120 new expressions::volumeExprDriver(mesh_, exprDict)
121 );
122 }
123 else
124 {
125 Su_.set
126 (
127 fieldName,
128 Function1<Type>::New(keyExplicit, subDict, &mesh_)
129 );
130 }
131 }
132
133 if
134 (
135 (eptr = subDict.findEntry(keyImplicit, keyType::LITERAL))
136 != nullptr
137 )
138 {
139 ok = true;
140
141 if
142 (
143 eptr->isDict()
144 && eptr->dict().readEntry("type", modelType, keyType::LITERAL)
145 && (modelType == "exprField")
146 )
147 {
148 const dictionary& exprDict = eptr->dict();
149
150 valueExprSp_.emplace_set(fieldName);
151 valueExprSp_[fieldName].readEntry("expression", exprDict);
152
153 driverSp_.set
154 (
155 fieldName,
156 new expressions::volumeExprDriver(mesh_, exprDict)
157 );
158 }
159 else
160 {
161 Sp_.set
162 (
163 fieldName,
164 Function1<scalar>::New(keyImplicit, subDict, &mesh_)
165 );
166 }
167 }
168 }
169 else
170 {
171 // Non-dictionary form
172
173 dEntry.readEntry(sourceRates);
174
175 ok = true;
176
177 Su_.set
178 (
179 fieldName,
180 new Function1Types::Constant<Type>
181 (
182 keyExplicit,
183 sourceRates.first()
184 )
185 );
186 Sp_.set
187 (
188 fieldName,
189 new Function1Types::Constant<scalar>
190 (
191 keyImplicit,
192 sourceRates.second()
193 )
194 );
195 }
196
197 if (!ok)
198 {
200 << "Require at least one of "
201 << keyExplicit << '/' << keyImplicit << " entries for "
202 << "field: " << fieldName << endl
203 << exit(FatalIOError);
204 }
205
206 fieldNames_[count] = fieldName;
207 ++count;
208 }
209}
210
211
212// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
213
214template<class Type>
216(
217 const word& name,
218 const word& modelType,
219 const dictionary& dict,
220 const fvMesh& mesh
221)
222:
223 fv::cellSetOption(name, modelType, dict, mesh),
224 volumeMode_(vmAbsolute),
225 VDash_(1)
226{
227 read(dict);
228}
229
230
231// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
232
233template<class Type>
235(
236 fvMatrix<Type>& eqn,
237 const label fieldi
238)
239{
240 return this->addSup(volScalarField::null(), eqn, fieldi);
241}
242
243
244template<class Type>
246(
247 const volScalarField& rho,
248 fvMatrix<Type>& eqn,
249 const label fieldi
250)
251{
252 if (debug)
253 {
254 Info<< "SemiImplicitSource<" << pTraits<Type>::typeName
255 << ">::addSup for source " << name_ << endl;
256 }
257
259
260 // Note: field name may deviate from psi name
261 const word& fieldName = fieldNames_[fieldi];
262
263 const scalar tmVal = mesh_.time().timeOutputValue();
264
265 const dimensionSet SuDims(eqn.dimensions()/dimVolume);
266 const dimensionSet SpDims(SuDims/psi.dimensions());
267
268 // Explicit source
269 {
270 const auto iter1 = valueExprSu_.cfind(fieldName);
271 const auto iter2 = Su_.cfind(fieldName);
272
274
275 if (iter1.found())
276 {
277 const auto& valueExpr = iter1.val();
278
279 typedef
281 ExprResultType;
282
283 if (debug)
284 {
285 Info<< "Explicit expression source:" << nl
286 << ">>>>" << nl
287 << valueExpr.c_str() << nl
288 << "<<<<" << nl;
289 }
290
291 auto& driver = *(driverSu_[fieldName]);
292
293 driver.clearVariables();
294
295 if (notNull(rho))
296 {
297 driver.addContextObject("rho", &rho);
298 }
299
300 // Switch dimension checking off
301 const bool oldDimChecking = dimensionSet::checking(false);
302
303 driver.parse(valueExpr);
304
305 // Restore dimension checking
306 dimensionSet::checking(oldDimChecking);
307
308 const ExprResultType* ptr =
309 driver.template isResultType<ExprResultType>();
310
311 if (!ptr)
312 {
314 << "Expression for Su " << fieldName
315 << " evaluated to <" << driver.resultType()
316 << "> but expected <" << ExprResultType::typeName
317 << ">" << endl
318 << exit(FatalError);
319 }
320 else if (ptr->size() != mesh_.nCells())
321 {
323 << "Expression for Su " << fieldName
324 << " evaluated to " << ptr->size()
325 << " instead of " << mesh_.nCells() << " values" << endl
326 << exit(FatalError);
327 }
328
329 if (notNull(rho))
330 {
331 driver.removeContextObject(&rho);
332 }
333
334 const Field<Type>& exprFld = ptr->primitiveField();
335
337 (
338 name_ + fieldName + "Su",
339 mesh_,
340 dimensioned<Type>(SuDims, Zero)
341 );
342
343 if (this->useSubMesh())
344 {
345 for (const label celli : cells_)
346 {
347 tsu.ref()[celli] = exprFld[celli]/VDash_;
348 }
349 }
350 else
351 {
352 tsu.ref().field() = exprFld;
353
354 if (!equal(VDash_, 1))
355 {
356 tsu.ref().field() /= VDash_;
357 }
358 }
359 }
360 else if (iter2.found() && iter2.val()->good())
361 {
362 const dimensioned<Type> SuValue
363 (
364 "Su",
365 SuDims,
366 iter2.val()->value(tmVal)/VDash_
367 );
368
369 if (mag(SuValue.value()) <= ROOTVSMALL)
370 {
371 // No-op
372 }
373 else if (this->useSubMesh())
374 {
376 (
377 name_ + fieldName + "Su",
378 mesh_,
379 dimensioned<Type>(SuDims, Zero)
380 );
381 UIndirectList<Type>(tsu.ref(), cells_) = SuValue.value();
382 }
383 else
384 {
385 eqn += SuValue;
386 }
387 }
388
389 if (tsu.valid())
390 {
391 eqn += tsu;
392 }
393 }
394
395
396 // Implicit source
397 {
398 const auto iter1 = valueExprSp_.cfind(fieldName);
399 const auto iter2 = Sp_.cfind(fieldName);
400
402
403 if (iter1.found())
404 {
405 const auto& valueExpr = iter1.val();
406
407 typedef volScalarField ExprResultType;
408
409 if (debug)
410 {
411 Info<< "Implicit expression source:" << nl
412 << ">>>>" << nl
413 << valueExpr.c_str() << nl
414 << "<<<<" << nl;
415 }
416
417 auto& driver = *(driverSp_[fieldName]);
418
419 driver.clearVariables();
420
421 if (notNull(rho))
422 {
423 driver.addContextObject("rho", &rho);
424 }
425
426 // Switch dimension checking off
427 const bool oldDimChecking = dimensionSet::checking(false);
428
429 driver.parse(valueExpr);
430
431 // Restore dimension checking
432 dimensionSet::checking(oldDimChecking);
433
434 const ExprResultType* ptr =
435 driver.template isResultType<ExprResultType>();
436
437 if (!ptr)
438 {
440 << "Expression for Sp " << fieldName
441 << " evaluated to <" << driver.resultType()
442 << "> but expected <" << ExprResultType::typeName
443 << ">" << endl
444 << exit(FatalError);
445 }
446 else if (ptr->size() != mesh_.nCells())
447 {
449 << "Expression for Sp " << fieldName
450 << " evaluated to " << ptr->size()
451 << " instead of " << mesh_.nCells() << " values" << endl
452 << exit(FatalError);
453 }
454
455 if (notNull(rho))
456 {
457 driver.removeContextObject(&rho);
458 }
459
460 const Field<scalar>& exprFld = ptr->primitiveField();
461
463 (
464 name_ + fieldName + "Sp",
465 mesh_,
467 );
468
469 if (this->useSubMesh())
470 {
471 for (const label celli : cells_)
472 {
473 tsp.ref()[celli] = exprFld[celli]/VDash_;
474 }
475 }
476 else
477 {
478 tsp.ref().field() = exprFld;
479
480 if (!equal(VDash_, 1))
481 {
482 tsp.ref().field() /= VDash_;
483 }
484 }
485 }
486 else if (iter2.found() && iter2.val()->good())
487 {
488 const dimensioned<scalar> SpValue
489 (
490 "Sp",
491 SpDims,
492 iter2.val()->value(tmVal)/VDash_
493 );
494
495 if (mag(SpValue.value()) <= ROOTVSMALL)
496 {
497 // No-op
498 }
499 else if (this->useSubMesh())
500 {
502 (
503 name_ + fieldName + "Sp",
504 mesh_,
506 );
507 UIndirectList<scalar>(tsp.ref(), cells_) = SpValue.value();
508 }
509 else
510 {
511 eqn += fvm::SuSp(SpValue, psi);
512 }
513 }
514
515 if (tsp.valid())
516 {
517 eqn += fvm::SuSp(tsp, psi);
518 }
519 }
520}
521
522
523template<class Type>
525{
526 VDash_ = 1;
527
529 {
530 volumeMode_ = volumeModeTypeNames_.get("volumeMode", coeffs_);
531
532 // Set volume normalisation
533 if (volumeMode_ == vmAbsolute)
534 {
535 VDash_ = V_;
536 }
537
538 // Compatibility (2112 and earlier)
539 const dictionary* injectDict =
540 coeffs_.findDict("injectionRateSuSp", keyType::LITERAL);
541
542 if (injectDict)
543 {
544 setFieldCoeffs
545 (
546 *injectDict,
547 "Su", // Su = explicit
548 "Sp" // Sp = implicit
549 );
550 }
551 else
552 {
553 setFieldCoeffs
554 (
555 coeffs_.subDict("sources", keyType::LITERAL),
556 "explicit",
557 "implicit"
558 );
559 }
560
561 return true;
562 }
563
564 return false;
565}
566
567
568// ************************************************************************* //
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: Enum.H:61
Generic templated field type.
Definition: Field.H:82
virtual bool read()
Re-read model coefficients if they have changed.
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
A List with indirect addressing. Like IndirectList but does not store addressing.
Definition: IndirectList.H:79
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
dictionary * findDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX)
Find and return a sub-dictionary pointer if present.
Definition: dictionaryI.H:127
Dimension set for the base types, which can be used to implement rigorous dimension checking for alge...
Definition: dimensionSet.H:109
static bool checking() noexcept
True if dimension checking is enabled (the usual default)
Definition: dimensionSet.H:229
Generic dimensioned Type class.
const Type & value() const
Return const reference to value.
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvMatrix.H:121
const dimensionSet & dimensions() const noexcept
Definition: fvMatrix.H:453
const GeometricField< Type, fvPatchField, volMesh > & psi(const label i=0) const
Return psi.
Definition: fvMatrix.H:412
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:91
Applies semi-implicit source within a specified region for Type, where <Type>=Scalar/Vector/Spherical...
virtual void addSup(fvMatrix< Type > &eqn, const label fieldi)
Add explicit contribution to incompressible equation.
static const Enum< volumeModeType > volumeModeTypeNames_
Names for volumeModeType.
virtual bool read(const dictionary &dict)
Read source dictionary.
volumeModeType
Options for the volume mode type.
Intermediate abstract class for handling cell-set options for the derived fvOptions.
@ LITERAL
String literal.
Definition: keyType.H:81
A traits class, which is primarily used for primitives.
Definition: pTraits.H:59
A class for managing temporary objects.
Definition: tmp.H:65
bool valid() const noexcept
Identical to good(), or bool operator.
Definition: tmp.H:292
T & ref() const
Definition: tmpI.H:227
A class for handling words, derived from Foam::string.
Definition: word.H:68
const volScalarField & psi
dynamicFvMesh & mesh
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
A special matrix type and solver, designed for finite volume solutions of scalar equations.
Calculate the finiteVolume matrix for implicit and explicit sources.
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of 'true' entries.
Definition: BitOps.H:78
zeroField SuSp(const Foam::zero, const GeometricField< Type, fvPatchField, volMesh > &)
A no-op source.
messageStream Info
Information stream (stdout output on master, null elsewhere)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
bool equal(const T &s1, const T &s2)
Compare two values for equality.
Definition: doubleFloat.H:46
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
bool notNull(const T *ptr)
True if ptr is not a pointer (of type T) to the nullObject.
Definition: nullObject.H:207
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
error FatalError
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:60
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
labelList fv(nPoints)
dictionary dict