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",
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.good() && 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 
401  tmp<DimensionedField<scalar, volMesh>> tsp;
402 
403  if (iter1.good())
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_,
466  dimensioned<scalar>(SpDims, Zero)
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.good() && 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_,
505  dimensioned<scalar>(SpDims, Zero)
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 
523 template<class Type>
524 bool Foam::fv::SemiImplicitSource<Type>::read(const dictionary& dict)
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 // ************************************************************************* //
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:598
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:81
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)
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 const GeometricField< scalar, fvPatchField, volMesh > & null()
Return a null geometric field.
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:627
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.
bool notNull(const T *ptr)
True if ptr is not a pointer (of type T) to the nullObject.
Definition: nullObject.H:246
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