SurfaceFilmModel.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-2017 OpenFOAM Foundation
9  Copyright (C) 2020-2025 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 "SurfaceFilmModel.H"
30 #include "mathematicalConstants.H"
31 #include "surfaceFilmRegionModel.H"
32 #include "liquidFilmBase.H"
33 
34 using namespace Foam::constant;
35 
36 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
37 
38 // The area film models.
39 // - 2506 and earlier: registered on Time.
40 // - 2512 and later : registered below faMeshesRegistry
41 
42 template<class CloudType>
45 {
46  if (const objectRegistry* parent = faMesh::registry(mesh); parent)
47  {
48  return
49  (
50  parent->thisDb().csorted
51  <
53  >()
54  );
55  }
56  else
57  {
58  return {};
59  }
60 }
61 
62 template<class CloudType>
65 {
66  if (const objectRegistry* parent = faMesh::registry(mesh); parent)
67  {
68  return
69  (
70  const_cast<objectRegistry&>(parent->thisDb()).sorted
71  <
73  >()
74  );
75  }
76  else
77  {
78  return {};
79  }
80 }
81 
82 
83 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
84 
85 template<class CloudType>
87 :
89  g_(owner.g()),
90  ejectedParcelType_(0),
91  injectionOffset_(1.1),
92  minDiameter_(0),
93  massParcelPatch_(),
94  diameterParcelPatch_(),
95  UFilmPatch_(),
96  rhoFilmPatch_(),
97  deltaFilmPatch_(),
98  nParcelsTransferred_(0),
99  nParcelsInjected_(0),
100  totalMassTransferred_(0)
101 {}
102 
103 
104 template<class CloudType>
106 (
107  const dictionary& dict,
108  CloudType& owner,
109  const word& type
110 )
111 :
112  CloudSubModelBase<CloudType>(owner, dict, typeName, type),
113  g_(owner.g()),
114  ejectedParcelType_
115  (
116  this->coeffDict().template getOrDefault<label>("ejectedParcelType", -1)
117  ),
118  injectionOffset_
119  (
120  this->coeffDict().template getOrDefault<scalar>("injectionOffset", 1.1)
121  ),
122  minDiameter_
123  (
124  this->coeffDict().template getOrDefault<scalar>("minDiameter", -1)
125  ),
126  massParcelPatch_(),
127  diameterParcelPatch_(),
128  UFilmPatch_(),
129  rhoFilmPatch_(),
130  deltaFilmPatch_(owner.mesh().boundary().size()),
131  nParcelsTransferred_(0),
132  nParcelsInjected_(0),
133  totalMassTransferred_()
134 {}
135 
136 
137 template<class CloudType>
139 (
140  const SurfaceFilmModel<CloudType>& sfm
141 )
142 :
144  g_(sfm.g_),
145  ejectedParcelType_(sfm.ejectedParcelType_),
146  injectionOffset_(sfm.injectionOffset_),
147  minDiameter_(sfm.minDiameter_),
148  massParcelPatch_(sfm.massParcelPatch_),
149  diameterParcelPatch_(sfm.diameterParcelPatch_),
150  UFilmPatch_(sfm.UFilmPatch_),
151  rhoFilmPatch_(sfm.rhoFilmPatch_),
152  deltaFilmPatch_(sfm.deltaFilmPatch_),
153  nParcelsTransferred_(sfm.nParcelsTransferred_),
154  nParcelsInjected_(sfm.nParcelsInjected_),
155  totalMassTransferred_(sfm.totalMassTransferred_)
156 {}
158 
159 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
160 
161 template<class CloudType>
162 template<class CloudTrackType>
164 (
165  const label primaryPatchi,
166  const labelUList& injectorCells,
167  CloudTrackType& cloud
168 )
169 {
170  const fvMesh& mesh = this->owner().mesh();
171 
172  const vectorField& Cf = mesh.C().boundaryField()[primaryPatchi];
173  const vectorField& Sf = mesh.Sf().boundaryField()[primaryPatchi];
174 
175  // Cached sizes are identical to the patch size
176  forAll(injectorCells, facei)
177  {
178  const label celli = injectorCells[facei];
179 
180  if (diameterParcelPatch_[facei] > 0)
181  {
182  const scalar offset =
183  (
184  injectionOffset_
185  * max
186  (
187  diameterParcelPatch_[facei],
188  deltaFilmPatch_[primaryPatchi][facei]
189  )
190  );
191 
192  const point pos = Cf[facei] - offset*normalised(Sf[facei]);
193 
194  // Create a new parcel
195  parcelType* pPtr =
196  new parcelType(this->owner().pMesh(), pos, celli);
197 
198  // Check/set new parcel thermo properties
199  cloud.setParcelThermoProperties(*pPtr, 0.0);
200 
201  setParcelProperties(*pPtr, facei);
202 
203  if (pPtr->nParticle() > 0.001)
204  {
205  // Check new parcel properties
206  cloud.checkParcelProperties(*pPtr, 0.0, false);
207 
208  // Add the new parcel to the cloud
209  cloud.addParticle(pPtr);
210 
211  ++nParcelsInjected_;
212  }
213  else
214  {
215  // TODO: cache mass and re-distribute?
216  delete pPtr;
217  }
218  }
219  }
220 }
221 
222 
223 template<class CloudType>
224 template<class CloudTrackType>
226 (
227  const UList<labelPair>& patchFaces,
228  CloudTrackType& cloud
229 )
230 {
231  const fvMesh& mesh = this->owner().mesh();
233 
234  const auto& Cf = mesh.C().boundaryField();
235  const auto& Sf = mesh.Sf().boundaryField();
236 
237  forAll(patchFaces, filmFacei)
238  {
239  const labelPair& patchAndFace = patchFaces[filmFacei];
240  const label patchi = patchAndFace.first();
241  const label facei = patchAndFace.second();
242 
243  if (patchi < 0) continue; // extra safety
244 
245  const label celli = pbm[patchi].faceCells()[facei];
246 
247  if (diameterParcelPatch_[filmFacei] > 0)
248  {
249  const scalar offset =
250  injectionOffset_ * max
251  (
252  diameterParcelPatch_[filmFacei],
253  deltaFilmPatch_[patchi][facei]
254  );
255 
256  const point pos =
257  (
258  Cf[patchAndFace]
259  - offset * normalised(Sf[patchAndFace])
260  );
261 
262  // Create a new parcel
263  parcelType* pPtr =
264  new parcelType(this->owner().pMesh(), pos, celli);
265 
266  // Check/set new parcel thermo properties
267  cloud.setParcelThermoProperties(*pPtr, 0.0);
268 
269  setParcelProperties(*pPtr, filmFacei);
270 
271  if (pPtr->nParticle() > 0.001)
272  {
273  // Check new parcel properties
274  cloud.checkParcelProperties(*pPtr, 0.0, false);
275 
276  // Add the new parcel to the cloud
277  cloud.addParticle(pPtr);
278 
279  ++nParcelsInjected_;
280  }
281  else
282  {
283  // TODO: cache mass and re-distribute?
284  delete pPtr;
285  }
286  }
287  }
288 }
289 
290 
291 template<class CloudType>
292 template<class TrackCloudType>
294 {
295  if (!this->active())
296  {
297  return;
298  }
299 
300  const fvMesh& mesh = this->owner().mesh();
302 
303  // Check the singleLayer type of films
304  {
305  const auto* filmPtr =
306  mesh.time().objectRegistry::template findObject<regionFilm>
307  (
308  "surfaceFilmProperties"
309  );
310 
311  if (filmPtr && filmPtr->active())
312  {
313  const auto& film = *filmPtr;
314  const labelList& filmPatches = film.intCoupledPatchIDs();
315  const labelList& primaryPatches = film.primaryPatchIDs();
316 
317  forAll(filmPatches, i)
318  {
319  const label filmPatchi = filmPatches[i];
320  const label primaryPatchi = primaryPatches[i];
321 
322  cacheFilmFields(filmPatchi, primaryPatchi, film);
323 
324  injectParticles
325  (
326  primaryPatchi,
327  pbm[primaryPatchi].faceCells(), // injector cells
328  cloud
329  );
330  }
331  }
332  }
333 
334  // Check finite-area films
335  // - allow non-const access to the area films
337  {
338  if (film.active())
339  {
340  const List<labelPair>& patchFaces =
341  film.regionMesh().whichPatchFaces();
342 
343  cacheFilmFields(film);
344 
345  injectParticles(patchFaces, cloud);
346 
347  forAll(patchFaces, filmFacei)
348  {
349  const label patchi = patchFaces[filmFacei].first();
350  const label facei = patchFaces[filmFacei].second();
351 
352  if (diameterParcelPatch_[filmFacei] > 0)
353  {
354  film.addSources
355  (
356  patchi,
357  facei,
358  -massParcelPatch_[filmFacei],// mass
359  Zero, // tangential momentum
360  Zero, // impingement
361  Zero // energy
362  );
363  }
364  }
365  }
366  }
367 }
368 
369 
370 template<class CloudType>
372 (
373  const label filmPatchi,
374  const label primaryPatchi,
376 )
377 {
378  massParcelPatch_ = filmModel.cloudMassTrans().boundaryField()[filmPatchi];
379  filmModel.toPrimary(filmPatchi, massParcelPatch_);
380 
381  diameterParcelPatch_ =
382  filmModel.cloudDiameterTrans().boundaryField()[filmPatchi];
383  filmModel.toPrimary(filmPatchi, diameterParcelPatch_, maxEqOp<scalar>());
384 
385  UFilmPatch_ = filmModel.Us().boundaryField()[filmPatchi];
386  filmModel.toPrimary(filmPatchi, UFilmPatch_);
387 
388  rhoFilmPatch_ = filmModel.rho().boundaryField()[filmPatchi];
389  filmModel.toPrimary(filmPatchi, rhoFilmPatch_);
390 
391  deltaFilmPatch_[primaryPatchi] =
392  filmModel.delta().boundaryField()[filmPatchi];
393  filmModel.toPrimary(filmPatchi, deltaFilmPatch_[primaryPatchi]);
394 }
395 
396 
397 template<class CloudType>
399 (
401 )
402 {
403  const polyBoundaryMesh& pbm = this->owner().mesh().boundaryMesh();
404  const volSurfaceMapping& map = film.region().vsm();
405 
406  // The polyPatch/local-face for each of the faceLabels
407  const List<labelPair>& patchFaces =
408  film.regionMesh().whichPatchFaces();
409 
410  const label nFaces = film.Uf().size(); // or regionMesh().nFaces()
411 
412 
413  // Flat fields
414 
415  massParcelPatch_.resize(nFaces, Zero);
416  map.mapToSurface(film.cloudMassTrans(), massParcelPatch_);
417 
418  diameterParcelPatch_.resize(nFaces, Zero);
419  map.mapToSurface(film.cloudDiameterTrans(), diameterParcelPatch_);
420 
421  // Direct copy (one-to-one mapping)
422  UFilmPatch_ = film.Uf().primitiveField();
423 
424  // Direct copy (one-to-one mapping)
425  rhoFilmPatch_ = film.rho().primitiveField();
426 
427 
428  // Per-patch fields
429 
430  // Same as film.region().primaryPatchIDs()
431  for (const label patchi : film.regionMesh().whichPolyPatches())
432  {
433  deltaFilmPatch_[patchi].resize(pbm[patchi].size(), Zero);
434  }
435 
436  forAll(patchFaces, i)
437  {
438  const label patchi = patchFaces[i].first();
439  const label facei = patchFaces[i].second();
440 
441  if (patchi >= 0)
442  {
443  deltaFilmPatch_[patchi][facei] = film.h()[i];
444  }
445  }
446 }
447 
448 
449 template<class CloudType>
451 (
452  parcelType& p,
453  const label filmFacei
454 ) const
455 {
456  // Set parcel properties
457  scalar vol = mathematical::pi/6.0*pow3(diameterParcelPatch_[filmFacei]);
458  p.d() = diameterParcelPatch_[filmFacei];
459  p.U() = UFilmPatch_[filmFacei];
460  p.rho() = rhoFilmPatch_[filmFacei];
461 
462  p.nParticle() = massParcelPatch_[filmFacei]/p.rho()/vol;
463 
464  if (minDiameter_ != -1)
465  {
466  if (p.d() < minDiameter_)
467  {
468  p.nParticle() = 0;
469  }
470  }
471 
472  if (ejectedParcelType_ >= 0)
473  {
474  p.typeId() = ejectedParcelType_;
475  }
476 }
477 
478 
479 template<class CloudType>
481 {
483 
484  label nTrans0 =
485  this->template getModelProperty<label>("nParcelsTransferred");
486 
487  label nInject0 =
488  this->template getModelProperty<label>("nParcelsInjected");
489 
490  scalar massTransferred0 =
491  this->template getModelProperty<scalar>("massTransferred");
492 
493  label nTransTotal =
494  nTrans0 + returnReduce(nParcelsTransferred_, sumOp<label>());
495 
496  label nInjectTotal =
497  nInject0 + returnReduce(nParcelsInjected_, sumOp<label>());
498 
499  scalar massTransferredTotal =
500  massTransferred0 + returnReduce(totalMassTransferred_, sumOp<scalar>());
501 
502 
503  Log_<< " Surface film:" << nl
504  << " - parcels absorbed = " << nTransTotal << nl
505  << " - mass absorbed = " << massTransferredTotal << nl
506  << " - parcels ejected = " << nInjectTotal << endl;
507 
508  if (this->writeTime())
509  {
510  this->setModelProperty("nParcelsTransferred", nTransTotal);
511  this->setModelProperty("nParcelsInjected", nInjectTotal);
512  this->setModelProperty("massTransferred", massTransferredTotal);
513 
514  nParcelsTransferred_ = 0;
515  nParcelsInjected_ = 0;
516  totalMassTransferred_ = 0;
517  }
518 }
519 
520 
521 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
522 
523 #include "SurfaceFilmModelNew.C"
524 
525 // ************************************************************************* //
Different types of constants.
faceListList boundary
const polyBoundaryMesh & pbm
void mapToSurface(const GeometricBoundaryField< Type, fvPatchField, volMesh > &, Field< Type > &result) const
Map volume boundary fields as area field.
const T & first() const noexcept
Access the first element.
Definition: Pair.H:167
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:119
const surfaceVectorField & Sf() const
Return cell face area vectors.
void injectParticles(const label primaryPatchi, const labelUList &injectorCells, TrackCloudType &cloud)
Inject particles in cloud.
const Internal::FieldType & primitiveField() const noexcept
Return a const-reference to the internal field values.
virtual const volVectorField & Us() const =0
Return the film surface velocity [m/s].
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:130
const volSurfaceMapping & vsm() const
Return mapping between surface and volume fields.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
T & first()
Access first element of the list, position [0].
Definition: UList.H:958
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:518
const labelList & whichPolyPatches() const
The polyPatches related to the areaMesh, in sorted order.
Definition: faMeshI.H:128
Base class for cloud sub-models.
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
Definition: quaternionI.H:674
virtual const volScalarField & cloudMassTrans() const =0
Return mass transfer source - Eulerian phase only.
Smooth ATC in cells next to a set of patches supplied by type.
Definition: faceCells.H:52
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
Volume to surface and surface to volume mapping.
virtual const volScalarField & rho() const =0
Return the film density [kg/m3].
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:400
dimensionedScalar pos(const dimensionedScalar &ds)
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:805
void toPrimary(const label regionPatchi, List< Type > &regionField) const
Convert a local region field to the primary region.
void resize(const label len)
Alter addressable list size, allocating new space if required while recovering old content...
UPtrList< const labelUList > faceCells() const
Return a list of faceCells for each patch.
const regionFaModel & region() const noexcept
Access to this region.
dynamicFvMesh & mesh
#define Log_
Report write to Foam::Info if the class log switch is true.
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:611
A class for handling words, derived from Foam::string.
Definition: word.H:63
A cloud is a registry collection of lagrangian particles.
Definition: cloud.H:53
const areaScalarField & h() const noexcept
Access const reference h.
virtual const volScalarField & delta() const =0
Return the film thickness [m].
const faMesh & regionMesh() const
Return the region mesh database.
constexpr scalar pi(M_PI)
void inject(TrackCloudType &cloud)
Inject parcels into the cloud.
A list of pointers to objects of type <T>, without allocation/deallocation management of the pointers...
Definition: HashTable.H:106
Switch active() const noexcept
Return the active flag.
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh...
const uniformDimensionedVectorField & g
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
virtual void info()
Write surface film info.
Templated wall surface film model class.
virtual void setParcelProperties(parcelType &p, const label filmFacei) const
Set the individual parcel properties.
static UPtrList< const areaFilm > csorted_areaFilms(const polyMesh &)
Registry.
const labelList & primaryPatchIDs() const
List of patch IDs on the primary region coupled to this region.
static UPtrList< areaFilm > sorted_areaFilms(const polyMesh &)
Return a sorted list of area-film objects that are registered on the faMeshesRegistry.
dimensionedScalar pow3(const dimensionedScalar &ds)
virtual void cacheFilmFields(const label filmPatchi, const label primaryPatchi, const regionFilm &)
Cache the film fields in preparation for injection.
Foam::KinematicCloud< Cloud< basicKinematicCollidingParcel > > ::parcelType parcelType
Convenience typedef to the cloud&#39;s parcel type.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
virtual const areaScalarField & rho() const =0
Access const reference rho.
return returnReduce(nRefine-oldNRefine, sumOp< label >())
const areaVectorField & Uf() const noexcept
Access const reference Uf.
static const objectRegistry * registry(const polyMesh &pMesh)
Find the singleton parent registry (on the polyMesh) that contains all objects related to finite-area...
Definition: faMesh.C:145
SurfaceFilmModel(CloudType &owner)
Construct null from owner.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:76
const List< labelPair > & whichPatchFaces() const
The polyPatch/local-face for each faceLabels()
Definition: faMeshI.H:138
const volVectorField & C() const
Return cell centres as volVectorField.
volScalarField & p
Registry of regIOobjects.
const T & second() const noexcept
Access the second element.
Definition: Pair.H:177
Templated base class for dsmc cloud.
Definition: DSMCCloud.H:67
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
virtual const volScalarField & cloudDiameterTrans() const =0
Return the parcel diameters originating from film to cloud.
virtual const volScalarField & cloudDiameterTrans() const =0
Return the parcel diameters originating from film.
virtual const volScalarField & cloudMassTrans() const =0
Return the film mass available for transfer.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127
virtual void addSources(const label patchi, const label facei, const scalar massSource, const vector &momentumSource, const scalar pressureSource, const scalar energySource=0)
Add sources.