populationBalanceSizeDistribution.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2017-2025 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
27 #include "polyTopoChangeMap.H"
28 #include "polyMeshMap.H"
29 #include "polyDistributionMap.H"
31 
32 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 
34 namespace Foam
35 {
36 namespace functionObjects
37 {
40  (
44  );
45 }
46 }
47 
48 const Foam::NamedEnum
49 <
51  6
52 >
54 {
55  "numberConcentration",
56  "numberDensity",
57  "volumeConcentration",
58  "volumeDensity",
59  "areaConcentration",
60  "areaDensity"
61 };
62 
63 const Foam::NamedEnum
64 <
66  4
67 >
69 {
70  "volume",
71  "area",
72  "diameter",
73  "projectedAreaDiameter"
74 };
75 
76 const Foam::NamedEnum
77 <
79  4
80 >
82 {
83  "numberConcentration",
84  "volumeConcentration",
85  "areaConcentration",
86  "cellVolume"
87 };
88 
90 
91 
92 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
93 
95 Foam::functionObjects::populationBalanceSizeDistribution::
96 functionTypeSymbolicName()
97 {
98  word functionTypeSymbolicName(word::null);
99 
100  switch (functionType_)
101  {
102  case functionType::numberConcentration:
103  {
104  functionTypeSymbolicName = "N";
105 
106  break;
107  }
108  case functionType::numberDensity:
109  {
110  functionTypeSymbolicName = "n";
111 
112  break;
113  }
114  case functionType::volumeConcentration:
115  {
116  functionTypeSymbolicName = "V";
117 
118  break;
119  }
120  case functionType::volumeDensity:
121  {
122  functionTypeSymbolicName = "v";
123 
124  break;
125  }
126  case functionType::areaConcentration:
127  {
128  functionTypeSymbolicName = "A";
129 
130  break;
131  }
132  case functionType::areaDensity:
133  {
134  functionTypeSymbolicName = "a";
135 
136  break;
137  }
138  }
139 
140  return functionTypeSymbolicName;
141 }
142 
143 
145 Foam::functionObjects::populationBalanceSizeDistribution::
146 coordinateTypeSymbolicName
147 (
148  const coordinateType& cType
149 )
150 {
151  word coordinateTypeSymbolicName(word::null);
152 
153  switch (cType)
154  {
155  case coordinateType::volume:
156  {
157  coordinateTypeSymbolicName = "v";
158 
159  break;
160  }
161  case coordinateType::area:
162  {
163  coordinateTypeSymbolicName = "a";
164 
165  break;
166  }
167  case coordinateType::diameter:
168  {
169  coordinateTypeSymbolicName = "d";
170 
171  break;
172  }
173  case coordinateType::projectedAreaDiameter:
174  {
175  coordinateTypeSymbolicName = "dPa";
176 
177  break;
178  }
179  }
180 
181  return coordinateTypeSymbolicName;
182 }
183 
184 
186 Foam::functionObjects::populationBalanceSizeDistribution::filterField
187 (
188  const scalarField& field
189 ) const
190 {
191  if (zone_.all())
192  {
193  return field;
194  }
195  else
196  {
197  return tmp<scalarField>(new scalarField(field, zone_.zone()));
198  }
199 }
200 
201 
202 Foam::scalar
203 Foam::functionObjects::populationBalanceSizeDistribution::averageCoordinateValue
204 (
206  const coordinateType& cType
207 )
208 {
209  scalar averageCoordinateValue(Zero);
210 
211  switch (cType)
212  {
213  case coordinateType::volume:
214  {
215  averageCoordinateValue = fi.x().value();
216 
217  break;
218  }
219  case coordinateType::area:
220  {
221  averageCoordinateValue =
222  weightedAverage(fi.a(), fi);
223 
224  break;
225  }
226  case coordinateType::diameter:
227  {
228  averageCoordinateValue =
229  weightedAverage(fi.d(), fi);
230 
231  break;
232  }
233  case coordinateType::projectedAreaDiameter:
234  {
235  averageCoordinateValue =
236  weightedAverage(sqrt(fi.a()/pi), fi);
237 
238  break;
239  }
240  }
241 
242  return averageCoordinateValue;
243 }
244 
245 
246 Foam::scalar
247 Foam::functionObjects::populationBalanceSizeDistribution::weightedAverage
248 (
249  const Foam::scalarField& fld,
251 )
252 {
253  scalar weightedAverage(Zero);
254 
255  switch (weightType_)
256  {
257  case weightType::numberConcentration:
258  {
259  scalarField Ni(filterField(fi*fi.phase()/fi.x().value()));
260 
261  if (gSum(Ni) == 0)
262  {
263  weightedAverage =
264  gSum(filterField(mesh_.V()*fld))/zone_.V();
265  }
266  else
267  {
268  weightedAverage =
269  gSum(Ni*filterField(fld))/gSum(Ni);
270  }
271 
272  break;
273  }
274  case weightType::volumeConcentration:
275  {
276  scalarField Vi(filterField(fi*fi.phase()));
277 
278  if (gSum(Vi) == 0)
279  {
280  weightedAverage =
281  gSum(filterField(mesh_.V()*fld))/zone_.V();
282  }
283  else
284  {
285  weightedAverage =
286  gSum(Vi*filterField(fld))/gSum(Vi);
287  }
288 
289  break;
290  }
291  case weightType::areaConcentration:
292  {
293  scalarField Ai(filterField(fi.a().ref()*fi.phase()));
294 
295  if (gSum(Ai) == 0)
296  {
297  weightedAverage =
298  gSum(filterField(mesh_.V()*fld))/zone_.V();
299  }
300  else
301  {
302  weightedAverage =
303  gSum(Ai*filterField(fld))/gSum(Ai);
304  }
305 
306  break;
307  }
309  {
310  weightedAverage =
311  gSum(filterField(mesh_.V()*fld))/zone_.V();
312 
313  break;
314  }
315  }
316 
317  return weightedAverage;
318 }
319 
320 
321 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
322 
325 (
326  const word& name,
327  const Time& runTime,
328  const dictionary& dict
329 )
330 :
331  fvMeshFunctionObject(name, runTime, dict),
332  file_(obr_, name),
333  mesh_(fvMeshFunctionObject::mesh_),
334  zone_(fvMeshFunctionObject::mesh_, dict),
335  popBalName_(dict.lookup("populationBalance")),
336  functionType_(functionTypeNames_.read(dict.lookup("functionType"))),
337  coordinateType_(coordinateTypeNames_.read(dict.lookup("coordinateType"))),
338  allCoordinates_
339  (
340  dict.lookupOrDefault<Switch>("allCoordinates", false)
341  ),
342  normalise_(dict.lookupOrDefault<Switch>("normalise", false)),
343  logTransform_
344  (
345  dict.lookupOrDefaultBackwardsCompatible<Switch>
346  (
347  {"logTransform", "geometric"},
348  false
349  )
350  ),
351  weightType_
352  (
353  dict.found("weightType")
354  ? weightTypeNames_.read(dict.lookup("weightType"))
355  : weightType::numberConcentration
356  ),
357  formatterPtr_(nullptr)
358 {
359  read(dict);
360 }
361 
362 
363 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
364 
367 {}
368 
369 
370 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
371 
373 (
374  const dictionary& dict
375 )
376 {
377  Log << type() << " " << name() << ":" << nl;
378 
380 
381  formatterPtr_ = setWriter::New(dict.lookup("setFormat"), dict);
382 
383  return false;
384 }
385 
386 
388 {
389  return true;
390 }
391 
392 
394 {
395  Log << type() << " " << name() << " write:" << nl;
396 
398  obr_.lookupObject<diameterModels::populationBalanceModel>
399  (
400  popBalName_
401  );
402 
403  const UPtrList<diameterModels::sizeGroup>& sizeGroups =
404  popBal.sizeGroups();
405 
406  scalarField coordinateValues(sizeGroups.size());
407  scalarField boundaryValues(sizeGroups.size() + 1);
408  scalarField resultValues(sizeGroups.size());
409 
410  forAll(sizeGroups, i)
411  {
412  const diameterModels::sizeGroup& fi = sizeGroups[i];
413 
414  coordinateValues[i] = averageCoordinateValue(fi, coordinateType_);
415  }
416 
417  if
418  (
419  functionType_ == functionType::numberDensity
420  || functionType_ == functionType::volumeDensity
421  || functionType_ == functionType::areaDensity
422  )
423  {
424  boundaryValues.first() = coordinateValues.first();
425  boundaryValues.last() = coordinateValues.last();
426 
427  for (label i = 1; i < boundaryValues.size() - 1; i++)
428  {
429  boundaryValues[i] =
430  0.5*(coordinateValues[i] + coordinateValues[i-1]);
431  }
432 
433  if (logTransform_)
434  {
435  boundaryValues = Foam::log(boundaryValues);
436  }
437  }
438 
439  switch (functionType_)
440  {
441  case functionType::numberConcentration:
442  {
443  forAll(sizeGroups, i)
444  {
445  const diameterModels::sizeGroup& fi = sizeGroups[i];
446 
447  resultValues[i] =
448  gSum(filterField(mesh_.V()*fi*fi.phase()/fi.x()))/zone_.V();
449  }
450 
451  if (normalise_ && sum(resultValues) != 0)
452  {
453  resultValues /= sum(resultValues);
454  }
455 
456  break;
457  }
458  case functionType::numberDensity:
459  {
460  forAll(sizeGroups, i)
461  {
462  const diameterModels::sizeGroup& fi = sizeGroups[i];
463 
464  resultValues[i] =
465  gSum(filterField(mesh_.V()*fi*fi.phase()/fi.x()))/zone_.V();
466  }
467 
468  if (normalise_ && sum(resultValues) != 0)
469  {
470  resultValues /= sum(resultValues);
471  }
472 
473  forAll(resultValues, i)
474  {
475  resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
476  }
477 
478  break;
479  }
480  case functionType::volumeConcentration:
481  {
482  forAll(sizeGroups, i)
483  {
484  const diameterModels::sizeGroup& fi = sizeGroups[i];
485 
486  resultValues[i] =
487  gSum(filterField(mesh_.V()*fi*fi.phase()))/zone_.V();
488  }
489 
490  if (normalise_ && sum(resultValues) != 0)
491  {
492  resultValues /= sum(resultValues);
493  }
494 
495  break;
496  }
497  case functionType::volumeDensity:
498  {
499  forAll(sizeGroups, i)
500  {
501  const diameterModels::sizeGroup& fi = sizeGroups[i];
502 
503  resultValues[i] =
504  gSum(filterField(mesh_.V()*fi*fi.phase()))/zone_.V();
505  }
506 
507  if (normalise_ && sum(resultValues) != 0)
508  {
509  resultValues /= sum(resultValues);
510  }
511 
512  forAll(resultValues, i)
513  {
514  resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
515  }
516 
517  break;
518  }
519  case functionType::areaConcentration:
520  {
521  forAll(sizeGroups, i)
522  {
523  const diameterModels::sizeGroup& fi = sizeGroups[i];
524 
525  resultValues[i] =
526  gSum
527  (
528  filterField(mesh_.V()*fi.a().ref()*fi*fi.phase()/fi.x())
529  )
530  /zone_.V();
531  }
532 
533  if (normalise_ && sum(resultValues) != 0)
534  {
535  resultValues /= sum(resultValues);
536  }
537 
538  break;
539  }
540  case functionType::areaDensity:
541  {
542  forAll(sizeGroups, i)
543  {
544  const diameterModels::sizeGroup& fi = sizeGroups[i];
545 
546  resultValues[i] =
547  gSum
548  (
549  filterField(mesh_.V()*fi.a().ref()*fi*fi.phase()/fi.x())
550  )
551  /zone_.V();
552  }
553 
554  if (normalise_ && sum(resultValues) != 0)
555  {
556  resultValues /= sum(resultValues);
557  }
558 
559  forAll(resultValues, i)
560  {
561  resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
562  }
563 
564  break;
565  }
566  }
567 
568 
569  if (allCoordinates_)
570  {
571  wordList otherCoordinateSymbolicNames(coordinateTypeNames_.size());
572  PtrList<scalarField> otherCoordinateValues(coordinateTypeNames_.size());
573 
574  forAll(coordinateTypeNames_, i)
575  {
576  const coordinateType cType = coordinateType(i);
577 
578  otherCoordinateSymbolicNames[cType] =
579  coordinateTypeSymbolicName(cType);
580 
581  otherCoordinateValues.set
582  (
583  cType,
584  new scalarField(popBal.sizeGroups().size())
585  );
586 
587  forAll(sizeGroups, i)
588  {
589  const diameterModels::sizeGroup& fi = sizeGroups[i];
590 
591  otherCoordinateValues[cType][i] =
592  averageCoordinateValue(fi, cType);
593  }
594  }
595 
596  if (Pstream::master())
597  {
598  formatterPtr_->write
599  (
600  file_.baseTimeDir(),
601  name(),
602  coordSet
603  (
604  true,
605  coordinateTypeSymbolicName(coordinateType_),
606  coordinateValues
607  ),
608  functionTypeSymbolicName(),
609  resultValues,
610  otherCoordinateSymbolicNames,
611  otherCoordinateValues
612  );
613  }
614  }
615  else
616  {
617  if (Pstream::master())
618  {
619  formatterPtr_->write
620  (
621  file_.baseTimeDir(),
622  name(),
623  coordSet
624  (
625  true,
626  coordinateTypeSymbolicName(coordinateType_),
627  coordinateValues
628  ),
629  functionTypeSymbolicName(),
630  resultValues
631  );
632  }
633  }
634 
635  return true;
636 }
637 
638 
640 (
641  const polyMesh& mesh
642 )
643 {
644  if (&mesh == &this->mesh())
645  {
646  zone_.movePoints();
647  }
648 }
649 
650 
652 (
653  const polyTopoChangeMap& map
654 )
655 {
656  if (&map.mesh() == &mesh())
657  {
658  zone_.topoChange(map);
659  }
660 }
661 
662 
664 (
665  const polyMeshMap& map
666 )
667 {
668  if (&map.mesh() == &mesh())
669  {
670  zone_.mapMesh(map);
671  }
672 }
673 
674 
676 (
677  const polyDistributionMap& map
678 )
679 {
680  if (&map.mesh() == &mesh())
681  {
682  zone_.distribute(map);
683  }
684 }
685 
686 
687 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
Macros for easy insertion into run-time selection tables.
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
Initialise the NamedEnum HashTable from the static list of names.
Definition: NamedEnum.H:55
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
bool set(const label) const
Is element set.
Definition: PtrListI.H:62
A simple wrapper around bool so that it can be read as a word: true/false, on/off,...
Definition: Switch.H:61
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
T & first()
Return the first element of the list.
Definition: UListI.H:114
T & last()
Return the last element of the list.
Definition: UListI.H:128
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: UPtrList.H:66
Holds list of sampling positions.
Definition: coordSet.H:51
Model for tracking the evolution of a dispersed phase size distribution due to coalescence (synonymou...
const UPtrList< sizeGroup > & sizeGroups() const
Return the size groups belonging to this populationBalance.
Single size class fraction field representing a fixed particle volume as defined by the user through ...
Definition: sizeGroup.H:96
const dimensionedScalar & x() const
Return representative volume of the sizeGroup.
Definition: sizeGroupI.H:58
const tmp< volScalarField > a() const
Return representative surface area of the sizeGroup.
Definition: sizeGroup.C:140
const phaseModel & phase() const
Return const-reference to the phase.
Definition: sizeGroupI.H:37
const tmp< volScalarField > d() const
Return representative diameter of the sizeGroup.
Definition: sizeGroup.C:147
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
const Type & value() const
Return const reference to value.
Abstract base-class for Time/database functionObjects.
Specialisation of Foam::functionObject for an Foam::fvMesh, providing a reference to the Foam::fvMesh...
virtual bool read(const dictionary &)
Read optional controls.
Writes out the size distribution determined by a population balance model, either for the entire doma...
virtual void topoChange(const polyTopoChangeMap &)
Update topology using the given map.
virtual void distribute(const polyDistributionMap &)
Redistribute or update using the given distribution map.
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
populationBalanceSizeDistribution(const word &name, const Time &runTime, const dictionary &dict)
Construct from Time and dictionary.
virtual void movePoints(const polyMesh &)
Update for mesh motion.
static const NamedEnum< weightType, 4 > weightTypeNames_
Names of the weight types.
static const NamedEnum< functionType, 6 > functionTypeNames_
Function type names.
static const NamedEnum< coordinateType, 4 > coordinateTypeNames_
Coordinate type names.
virtual bool write()
Calculate and write the size distribution.
virtual bool read(const dictionary &)
Read the populationBalanceSizeDistribution data.
Class containing mesh-to-mesh mapping information after a mesh distribution where we send parts of me...
const polyMesh & mesh() const
Return polyMesh.
Class containing mesh-to-mesh mapping information.
Definition: polyMeshMap.H:51
const polyMesh & mesh() const
Return polyMesh.
Definition: polyMeshMap.H:75
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const polyMesh & mesh() const
Return polyMesh.
static autoPtr< setWriter > New(const word &writeType, const IOstream::streamFormat writeFormat=IOstream::ASCII, const IOstream::compressionType writeCompression=IOstream::UNCOMPRESSED)
Select given write options.
Definition: setWriter.C:286
A class for managing temporary objects.
Definition: tmp.H:55
A class for handling words, derived from string.
Definition: word.H:62
static const word null
An empty word.
Definition: word.H:77
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
gmvFile<< "tracers "<< particles.size()<< nl;{ pointField positions(particles.size());label particlei=0;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter) { positions[particlei++]=iter().position(mesh);} for(i=0;i< pTraits< point >::nComponents;i++) { forAll(positions, particlei) { gmvFile<< component(positions[particlei], i)<< ' ';} gmvFile<< nl;}}forAll(lagrangianScalarNames, i){ const word &name=lagrangianScalarNames[i];IOField< scalar > fld(IOobject(name, runTime.name(), lagrangian::cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
#define Log
Report write to Foam::Info if the local log switch is true.
scalar cellVolume(const cell &c, const cellEdgeAddressing &cAddr, const point &cPAvg, const vectorField &fAreas, const pointField &fCentres)
Compute the cell-volume.
defineTypeNameAndDebug(adjustTimeStepToCombustion, 0)
addToRunTimeSelectionTable(functionObject, adjustTimeStepToCombustion, dictionary)
Namespace for OpenFOAM.
static const zero Zero
Definition: zero.H:97
Type gSum(const FieldField< Field, Type > &f)
bool read(const char *, int32_t &)
Definition: int32IO.C:85
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
dimensionedScalar log(const dimensionedScalar &ds)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
static const char nl
Definition: Ostream.H:267
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
dictionary dict