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 -------------------------------------------------------------------------------
11 License
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 
38 template<class Type>
39 const Foam::Enum
40 <
42 >
44 ({
45  { volumeModeType::vmAbsolute, "absolute" },
46  { volumeModeType::vmSpecific, "specific" },
47 });
48 
49 
50 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
51 
52 template<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_.reserve(count);
67  Sp_.reserve(count);
68 
69  driverSu_.clear();
70  driverSp_.clear();
71  driverSu_.reserve(count);
72  driverSp_.reserve(count);
73 
74  valueExprSu_.clear();
75  valueExprSp_.clear();
76  valueExprSu_.reserve(count);
77  valueExprSp_.reserve(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 
214 template<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 
233 template<class Type>
235 (
236  fvMatrix<Type>& eqn,
237  const label fieldi
238 )
239 {
240  return this->addSup(volScalarField::null(), eqn, fieldi);
241 }
242 
243 
244 template<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.good())
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",
340  mesh_,
341  dimensioned<Type>(SuDims, Zero)
342  );
343 
344  if (this->useSubMesh())
345  {
346  for (const label celli : cells_)
347  {
348  tsu.ref()[celli] = exprFld[celli]/VDash_;
349  }
350  }
351  else
352  {
353  tsu.ref().field() = exprFld;
354 
355  if (!equal(VDash_, 1))
356  {
357  tsu.ref().field() /= VDash_;
358  }
359  }
360  }
361  else if (iter2.good() && iter2.val()->good())
362  {
363  const dimensioned<Type> SuValue
364  (
365  "Su",
366  SuDims,
367  iter2.val()->value(tmVal)/VDash_
368  );
369 
370  if (mag(SuValue.value()) <= ROOTVSMALL)
371  {
372  // No-op
373  }
374  else if (this->useSubMesh())
375  {
377  (
378  name_ + fieldName + "Su",
380  mesh_,
381  dimensioned<Type>(SuDims, Zero)
382  );
383  UIndirectList<Type>(tsu.ref(), cells_) = SuValue.value();
384  }
385  else
386  {
387  eqn += SuValue;
388  }
389  }
390 
391  if (tsu.valid())
392  {
393  eqn += tsu;
394  }
395  }
396 
397 
398  // Implicit source
399  {
400  const auto iter1 = valueExprSp_.cfind(fieldName);
401  const auto iter2 = Sp_.cfind(fieldName);
402 
403  tmp<DimensionedField<scalar, volMesh>> tsp;
404 
405  if (iter1.good())
406  {
407  const auto& valueExpr = iter1.val();
408 
409  typedef volScalarField ExprResultType;
410 
411  if (debug)
412  {
413  Info<< "Implicit expression source:" << nl
414  << ">>>>" << nl
415  << valueExpr.c_str() << nl
416  << "<<<<" << nl;
417  }
418 
419  auto& driver = *(driverSp_[fieldName]);
420 
421  driver.clearVariables();
422 
423  if (notNull(rho))
424  {
425  driver.addContextObject("rho", &rho);
426  }
427 
428  // Switch dimension checking off
429  const bool oldDimChecking = dimensionSet::checking(false);
430 
431  driver.parse(valueExpr);
432 
433  // Restore dimension checking
434  dimensionSet::checking(oldDimChecking);
435 
436  const ExprResultType* ptr =
437  driver.template isResultType<ExprResultType>();
438 
439  if (!ptr)
440  {
442  << "Expression for Sp " << fieldName
443  << " evaluated to <" << driver.resultType()
444  << "> but expected <" << ExprResultType::typeName
445  << ">" << endl
446  << exit(FatalError);
447  }
448  else if (ptr->size() != mesh_.nCells())
449  {
451  << "Expression for Sp " << fieldName
452  << " evaluated to " << ptr->size()
453  << " instead of " << mesh_.nCells() << " values" << endl
454  << exit(FatalError);
455  }
456 
457  if (notNull(rho))
458  {
459  driver.removeContextObject(&rho);
460  }
461 
462  const Field<scalar>& exprFld = ptr->primitiveField();
463 
465  (
466  name_ + fieldName + "Sp",
468  mesh_,
469  dimensioned<scalar>(SpDims, Zero)
470  );
471 
472  if (this->useSubMesh())
473  {
474  for (const label celli : cells_)
475  {
476  tsp.ref()[celli] = exprFld[celli]/VDash_;
477  }
478  }
479  else
480  {
481  tsp.ref().field() = exprFld;
482 
483  if (!equal(VDash_, 1))
484  {
485  tsp.ref().field() /= VDash_;
486  }
487  }
488  }
489  else if (iter2.good() && iter2.val()->good())
490  {
491  const dimensioned<scalar> SpValue
492  (
493  "Sp",
494  SpDims,
495  iter2.val()->value(tmVal)/VDash_
496  );
497 
498  if (mag(SpValue.value()) <= ROOTVSMALL)
499  {
500  // No-op
501  }
502  else if (this->useSubMesh())
503  {
505  (
506  name_ + fieldName + "Sp",
508  mesh_,
509  dimensioned<scalar>(SpDims, Zero)
510  );
511  UIndirectList<scalar>(tsp.ref(), cells_) = SpValue.value();
512  }
513  else
514  {
515  eqn += fvm::SuSp(SpValue, psi);
516  }
517  }
518 
519  if (tsp.valid())
520  {
521  eqn += fvm::SuSp(tsp, psi);
522  }
523  }
524 }
525 
526 
527 template<class Type>
528 bool Foam::fv::SemiImplicitSource<Type>::read(const dictionary& dict)
529 {
530  VDash_ = 1;
531 
533  {
534  volumeMode_ = volumeModeTypeNames_.get("volumeMode", coeffs_);
535 
536  // Set volume normalisation
537  if (volumeMode_ == vmAbsolute)
538  {
539  VDash_ = V_;
540  }
541 
542  // Compatibility (2112 and earlier)
543  const dictionary* injectDict =
544  coeffs_.findDict("injectionRateSuSp", keyType::LITERAL);
545 
546  if (injectDict)
547  {
548  setFieldCoeffs
549  (
550  *injectDict,
551  "Su", // Su = explicit
552  "Sp" // Sp = implicit
553  );
554  }
555  else
556  {
557  setFieldCoeffs
558  (
559  coeffs_.subDict("sources", keyType::LITERAL),
560  "explicit",
561  "implicit"
562  );
563  }
564 
565  return true;
566  }
567 
568  return false;
569 }
570 
571 
572 // ************************************************************************* //
bool valid() const noexcept
Identical to good(), or bool operator.
Definition: tmp.H:472
dictionary dict
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
bool equal(const T &a, const T &b)
Compare two values for equality.
Definition: label.H:164
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:608
T & ref() const
Return non-const reference to the contents of a non-null managed pointer.
Definition: tmpI.H:235
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
A traits class, which is primarily used for primitives and vector-space.
Definition: pTraits.H:75
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tf1, const word &name, const dimensionSet &dimensions, const bool initCopy=false)
Global function forwards to reuseTmpDimensionedField::New.
virtual bool read(const dictionary &dict)
Read source dictionary.
virtual bool read(const dictionary &dict)
Read source dictionary.
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:72
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:58
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
Dimension set for the base types, which can be used to implement rigorous dimension checking for alge...
Definition: dimensionSet.H:105
const GeometricField< Type, fvPatchField, volMesh > & psi(const label i=0) const
Return psi.
Definition: fvMatrix.H:485
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
volumeExpr::parseDriver volumeExprDriver
Typedef for volumeExpr parseDriver.
Definition: volumeExprFwd.H:59
static const Enum< volumeModeType > volumeModeTypeNames_
Names for volumeModeType.
A class for handling words, derived from Foam::string.
Definition: word.H:63
labelList fv(nPoints)
static const GeometricField< scalar, fvPatchField, volMesh > & null() noexcept
Return a null GeometricField (reference to a nullObject).
A special matrix type and solver, designed for finite volume solutions of scalar equations. Face addressing is used to make all matrix assembly and solution loops vectorise.
Definition: fvPatchField.H:64
zeroField SuSp(const Foam::zero, const GeometricField< Type, fvPatchField, volMesh > &)
A no-op source.
String literal.
Definition: keyType.H:82
int debug
Static debugging option.
static bool checking() noexcept
True if dimension checking is enabled (the usual default)
Definition: dimensionSet.H:241
const dimensionSet & dimensions() const noexcept
Definition: fvMatrix.H:528
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: error.H:64
volumeModeType
Options for the volume mode type.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:637
bool notNull(const T *ptr) noexcept
True if ptr is not a pointer (of type T) to the nullObject.
Definition: nullObject.H:267
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
virtual void addSup(fvMatrix< Type > &eqn, const label fieldi)
Add explicit contribution to incompressible equation.
static tmp< DimensionedField< Type, GeoMesh > > New(const word &name, IOobjectOption::registerOption regOpt, const Mesh &mesh, const dimensionSet &dims, const Field< Type > &iField)
Return tmp field (NO_READ, NO_WRITE) from name, mesh, dimensions, copy of internal field...
A special matrix type and solver, designed for finite volume solutions of scalar equations.
messageStream Info
Information stream (stdout output on master, null elsewhere)
Intermediate abstract class for handling cell-set options for the derived fvOptions.
const volScalarField & psi
Applies semi-implicit source within a specified region for Type, where <Type>=Scalar/Vector/Spherical...
A class for managing temporary objects.
Definition: HashPtrTable.H:50
SemiImplicitSource(const word &name, const word &modelType, const dictionary &dict, const fvMesh &mesh)
Construct from components.
Do not request registration (bool: false)
Calculate the finiteVolume matrix for implicit and explicit sources.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127