sorptionWallFunctionFvPatchScalarField.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) 2022 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
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 
30 #include "fvPatchFieldMapper.H"
31 #include "volFields.H"
32 #include "wallDist.H"
33 
34 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38 
39 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
40 
41 //- Estimate the y* at the intersection of the two sublayers
42 static scalar calcYStarLam
43 (
44  const scalar kappa,
45  const scalar E,
46  const scalar Sc,
47  const scalar Sct,
48  const scalar Pc
49 )
50 {
51  scalar ypl = 11;
52 
53  for (int iter = 0; iter < 10; ++iter)
54  {
55  // (F:Eq. 5.5)
56  ypl = (log(max(E*ypl, scalar(1)))/kappa + Pc)*Sct/Sc;
57  }
58 
59  return ypl;
60 }
61 
62 
63 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
64 
65 tmp<scalarField> sorptionWallFunctionFvPatchScalarField::yPlus() const
66 {
67  // Calculate fields of interest
68  const label patchi = patch().index();
69 
70  const auto& k = db().lookupObject<volScalarField>(kName_);
71  tmp<scalarField> tkwc = k.boundaryField()[patchi].patchInternalField();
72  const scalarField& kwc = tkwc.cref();
73 
74  const auto& nu = db().lookupObject<volScalarField>(nuName_);
75  tmp<scalarField> tnuwc = nu.boundaryField()[patchi].patchInternalField();
76  const scalarField& nuwc = tnuwc.cref();
77 
78  const volScalarField& y = wallDist::New(internalField().mesh()).y();
79  tmp<scalarField> tywc = y.boundaryField()[patchi].patchInternalField();
80  const scalarField& ywc = tywc.cref();
81 
82 
83  // Calculate the empirical constant given by (Jayatilleke, 1966) (FDC:Eq. 6)
84  const scalar Pc =
85  9.24*(pow(Sc_/Sct_, 0.75) - 1)*(1 + 0.28*exp(-0.007*Sc_/Sct_));
86  const scalar Cmu25 = pow025(wallCoeffs_.Cmu());
87  const scalar kappa = wallCoeffs_.kappa();
88  const scalar E = wallCoeffs_.E();
89 
90  auto tyPlus = tmp<scalarField>::New(patch().size(), Zero);
91  auto& yPlus = tyPlus.ref();
92 
93  forAll(yPlus, facei)
94  {
95  // (FDC:Eq. 3)
96  const scalar yStar = Cmu25*sqrt(kwc[facei])*ywc[facei]/nuwc[facei];
97 
98  // (FDC:Eq. 4)
99  const scalar yPlusVis = Sc_*yStar;
100 
101  // (FDC:Eq. 5)
102  const scalar yPlusLog = Sct_*(log(max(E*yStar, 1 + 1e-4))/kappa + Pc);
103 
104  switch (blender_)
105  {
106  case blenderType::EXPONENTIAL:
107  {
108  // (FDC:Eq. 2)
109  const scalar Gamma =
110  0.01*pow4(Sc_*yStar)/(1 + 5*pow3(Sc_)*yStar);
111  const scalar invGamma = scalar(1)/max(Gamma, ROOTVSMALL);
112 
113  // (FDC:Eq. 1)
114  yPlus[facei] = yPlusVis*exp(-Gamma) + yPlusLog*exp(-invGamma);
115  break;
116  }
117 
118  case blenderType::STEPWISE:
119  {
120  static const scalar yStarLam =
121  calcYStarLam(kappa, E, Sc_, Sct_, Pc);
122 
123  // (F:Eq. 5.3)
124  if (yStar < yStarLam)
125  {
126  yPlus[facei] = yPlusVis;
127  }
128  else
129  {
130  yPlus[facei] = yPlusLog;
131  }
132  break;
133  }
134 
135  case blenderType::BINOMIAL:
136  {
137  yPlus[facei] =
138  pow
139  (
140  pow(yPlusVis, n_) + pow(yPlusLog, n_),
141  scalar(1)/n_
142  );
143  break;
144  }
145 
146  case blenderType::MAX:
147  {
148  yPlus[facei] = max(yPlusVis, yPlusLog);
149  break;
150  }
151 
152  case blenderType::TANH:
153  {
154  const scalar phiTanh = tanh(pow4(0.1*yStar));
155  const scalar b1 = yPlusVis + yPlusLog;
156  const scalar b2 =
157  pow(pow(yPlusVis, 1.2) + pow(yPlusLog, 1.2), 1.0/1.2);
158 
159  yPlus[facei] = phiTanh*b1 + (1 - phiTanh)*b2;
160  break;
161  }
162  }
163  }
164 
165  return tyPlus;
166 }
167 
168 
169 tmp<scalarField> sorptionWallFunctionFvPatchScalarField::flux() const
170 {
171  // Calculate fields of interest
172  const label patchi = patch().index();
173 
174  const auto& k = db().lookupObject<volScalarField>(kName_);
175  tmp<scalarField> tkwc = k.boundaryField()[patchi].patchInternalField();
176 
177  const volScalarField& y = wallDist::New(internalField().mesh()).y();
178  tmp<scalarField> tywc = y.boundaryField()[patchi].patchInternalField();
179 
180 
181  // Calculate mass-transfer coefficient field (FDC:Eqs. 8-9)
182  tmp<scalarField> ta =
183  laminar_
184  ? D_/tywc
185  : pow025(wallCoeffs_.Cmu())*sqrt(tkwc)/yPlus();
186 
187 
188  // Calculate wall-surface concentration
189  const auto& Csurf = *this;
190 
191  // Calculate wall-adjacent concentration
192  const scalar t = db().time().timeOutputValue();
193  tmp<scalarField> tkAbs = kAbsPtr_->value(t);
194  tmp<scalarField> tCstar = Csurf/tkAbs;
195 
196  // Calculate near-wall cell concentration
197  const word& fldName = internalField().name();
198  const auto& C = db().lookupObject<volScalarField>(fldName);
199  tmp<scalarField> tCwc = C.boundaryField()[patchi].patchInternalField();
200 
201  // Return adsorption or absorption/permeation flux
202  // normalised by the mass-transfer coefficient (FDC:Fig. 1)
203  return (tCstar - tCwc)/ta;
204 }
205 
206 
207 void sorptionWallFunctionFvPatchScalarField::writeLocalEntries
208 (
209  Ostream& os
210 ) const
211 {
213  os.writeEntryIfDifferent<bool>("laminar", false, laminar_);
214  os.writeEntry("Sc", Sc_);
215  os.writeEntry("Sct", Sct_);
216  os.writeEntryIfDifferent<scalar>("D", -1, D_);
217  wallCoeffs_.writeEntries(os);
218  os.writeEntryIfDifferent<word>("k", "k", kName_);
219  os.writeEntryIfDifferent<word>("nu", "nu", nuName_);
220 
221  if (kAbsPtr_)
222  {
223  kAbsPtr_->writeData(os);
224  }
225 }
227 
228 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
229 
231 (
232  const fvPatch& p,
234 )
235 :
236  fixedGradientFvPatchScalarField(p, iF),
238  laminar_(false),
239  kAbsPtr_(nullptr),
240  Sc_(1),
241  Sct_(1),
242  D_(-1),
243  kName_("k"),
244  nuName_("nu"),
245  wallCoeffs_()
246 {}
247 
248 
250 (
252  const fvPatch& p,
254  const fvPatchFieldMapper& mapper
255 )
256 :
257  fixedGradientFvPatchScalarField(ptf, p, iF, mapper),
259  laminar_(ptf.laminar_),
260  kAbsPtr_(ptf.kAbsPtr_.clone(patch().patch())),
261  Sc_(ptf.Sc_),
262  Sct_(ptf.Sct_),
263  D_(ptf.D_),
264  kName_(ptf.kName_),
265  nuName_(ptf.nuName_),
266  wallCoeffs_(ptf.wallCoeffs_)
267 {}
268 
269 
271 (
272  const fvPatch& p,
274  const dictionary& dict
275 )
276 :
277  fixedGradientFvPatchScalarField(p, iF), // Bypass dictionary constructor
278  wallFunctionBlenders(dict, blenderType::STEPWISE, scalar(2)),
279  laminar_(dict.getOrDefault<bool>("laminar", false)),
280  kAbsPtr_(PatchFunction1<scalar>::New(p.patch(), "kAbs", dict)),
281  Sc_(dict.getCheck<scalar>("Sc", scalarMinMax::ge(0))),
282  Sct_(dict.getCheck<scalar>("Sct", scalarMinMax::ge(0))),
283  D_(dict.getOrDefault<scalar>("D", -1)),
284  kName_(dict.getOrDefault<word>("k", "k")),
285  nuName_(dict.getOrDefault<word>("nu", "nu")),
286  wallCoeffs_(dict)
287 {
288  if (laminar_)
289  {
290  if (D_ < 0)
291  {
293  << "Molecular diffusion coefficient cannot be non-positive. "
294  << "D = " << D_
295  << exit(FatalIOError);
296  }
297  }
298 
299  if (!kAbsPtr_)
300  {
302  << "Adsorption or absorption coefficient is not set."
303  << exit(FatalIOError);
304  }
305 
306  if (!this->readGradientEntry(dict) || !this->readValueEntry(dict))
307  {
308  extrapolateInternal();
309  gradient() = Zero;
310  }
311 }
312 
313 
315 (
316  const sorptionWallFunctionFvPatchScalarField& swfpsf
317 )
318 :
319  fixedGradientFvPatchScalarField(swfpsf),
320  wallFunctionBlenders(swfpsf),
321  laminar_(swfpsf.laminar_),
322  kAbsPtr_(swfpsf.kAbsPtr_.clone(patch().patch())),
323  Sc_(swfpsf.Sc_),
324  Sct_(swfpsf.Sct_),
325  D_(swfpsf.D_),
326  kName_(swfpsf.kName_),
327  nuName_(swfpsf.nuName_),
328  wallCoeffs_(swfpsf.wallCoeffs_)
329 {}
330 
331 
333 (
336 )
337 :
338  fixedGradientFvPatchScalarField(swfpsf, iF),
339  wallFunctionBlenders(swfpsf),
340  laminar_(swfpsf.laminar_),
341  kAbsPtr_(swfpsf.kAbsPtr_.clone(patch().patch())),
342  Sc_(swfpsf.Sc_),
343  Sct_(swfpsf.Sct_),
344  D_(swfpsf.D_),
345  kName_(swfpsf.kName_),
346  nuName_(swfpsf.nuName_),
347  wallCoeffs_(swfpsf.wallCoeffs_)
348 {}
350 
351 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
352 
354 (
355  const fvPatchFieldMapper& mapper
356 )
357 {
358  fixedGradientFvPatchScalarField::autoMap(mapper);
359 
360  if (kAbsPtr_)
361  {
362  kAbsPtr_->autoMap(mapper);
363  }
364 }
365 
366 
368 (
369  const fvPatchScalarField& ptf,
370  const labelList& addr
371 )
372 {
373  fixedGradientFvPatchScalarField::rmap(ptf, addr);
374 
375  const auto& swfptf =
376  refCast<const sorptionWallFunctionFvPatchScalarField>(ptf);
377 
378  if (kAbsPtr_)
379  {
380  kAbsPtr_->rmap(swfptf.kAbsPtr_(), addr);
381  }
382 }
383 
384 
386 {
387  if (updated())
388  {
389  return;
390  }
391 
392  gradient() = flux()/patch().deltaCoeffs();
394  fixedGradientFvPatchScalarField::updateCoeffs();
395 }
396 
397 
399 {
401 
402  writeLocalEntries(os);
403 
405 }
406 
407 
408 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
409 
411 (
413  sorptionWallFunctionFvPatchScalarField
414 );
415 
416 
417 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
418 
419 } // End namespace Foam
420 
421 // ************************************************************************* //
scalar n_
Binomial blending exponent being used when blenderType is blenderType::BINOMIAL.
dimensionedScalar tanh(const dimensionedScalar &ds)
dictionary dict
dimensionedScalar log(const dimensionedScalar &ds)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
static const wallDist & New(const fvMesh &mesh, Args &&... args)
Get existing or create a new MeshObject. Registered with typeName.
Definition: MeshObject.C:53
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
sorptionWallFunctionFvPatchScalarField(const fvPatch &, const DimensionedField< scalar, volMesh > &)
Construct from patch and internal field.
dimensionedScalar sqrt(const dimensionedScalar &ds)
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:70
dimensionedScalar pow025(const dimensionedScalar &ds)
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.
void writeValueEntry(Ostream &os) const
Write *this field as a "value" entry.
Definition: fvPatchField.H:372
label k
Boltzmann constant.
const dimensionedScalar kappa
Coulomb constant: default SI units: [N.m2/C2].
The sorptionWallFunction is a wall boundary condition to specify scalar/concentration gradient for tu...
The class wallFunctionBlenders is a base class that hosts common entries for various derived wall-fun...
Macros for easy insertion into run-time selection tables.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:414
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:81
virtual void write(Ostream &) const
Write.
scalar y
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
dynamicFvMesh & mesh
dimensionedScalar exp(const dimensionedScalar &ds)
virtual void rmap(const fvPatchScalarField &, const labelList &)
Reverse map the given fvPatchField onto this fvPatchField.
fvPatchField< scalar > fvPatchScalarField
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
A FieldMapper for finite-volume patch fields.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:212
virtual void autoMap(const fvPatchFieldMapper &)
Map (and resize as needed) from self given a mapping object.
const volScalarField & y() const noexcept
Return reference to cached distance-to-wall field.
Definition: wallDist.H:201
static scalar calcYStarLam(const scalar kappa, const scalar E, const scalar Sc, const scalar Sct, const scalar Pc)
Estimate the y* at the intersection of the two sublayers.
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:55
Top level data entry class for use in dictionaries. Provides a mechanism to specify a variable as a c...
void writeEntries(Ostream &) const
Write wall-function coefficients as dictionary entries.
OBJstream os(runTime.globalPath()/outputName)
volScalarField & C
scalar Cmu() const noexcept
Return the object: Cmu.
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
dimensionedScalar pow3(const dimensionedScalar &ds)
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:607
void writeEntries(Ostream &) const
Write wall-function blending data as dictionary entries.
virtual void updateCoeffs()
Update the coefficients associated with the patch field.
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Definition: areaFieldsFwd.H:42
dimensionedScalar pow4(const dimensionedScalar &ds)
const std::string patch
OpenFOAM patch number as a std::string.
scalar E() const noexcept
Return the object: E.
enum blenderType blender_
Blending treatment.
List< label > labelList
A List of labels.
Definition: List.H:62
scalar kappa() const noexcept
Return the object: kappa.
volScalarField & p
makePatchTypeField(fvPatchScalarField, atmBoundaryLayerInletEpsilonFvPatchScalarField)
volScalarField & nu
Namespace for OpenFOAM.
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:133