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 
26 #include "populationBalanceModel.H"
28 #include "polyTopoChangeMap.H"
29 #include "polyMeshMap.H"
30 #include "polyDistributionMap.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37 namespace functionObjects
38 {
41  (
45  );
46 }
47 }
48 
49 const Foam::NamedEnum
50 <
52  6
53 >
55 {
56  "numberConcentration",
57  "numberDensity",
58  "volumeConcentration",
59  "volumeDensity",
60  "areaConcentration",
61  "areaDensity"
62 };
63 
64 const Foam::NamedEnum
65 <
67  4
68 >
70 {
71  "volume",
72  "area",
73  "diameter",
74  "projectedAreaDiameter"
75 };
76 
77 const Foam::NamedEnum
78 <
80  4
81 >
83 {
84  "numberConcentration",
85  "volumeConcentration",
86  "areaConcentration",
87  "cellVolume"
88 };
89 
91 
92 
93 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
94 
96 Foam::functionObjects::populationBalanceSizeDistribution::
97 functionTypeSymbolicName()
98 {
99  word functionTypeSymbolicName(word::null);
100 
101  switch (functionType_)
102  {
103  case functionType::numberConcentration:
104  {
105  functionTypeSymbolicName = "N";
106 
107  break;
108  }
109  case functionType::numberDensity:
110  {
111  functionTypeSymbolicName = "n";
112 
113  break;
114  }
115  case functionType::volumeConcentration:
116  {
117  functionTypeSymbolicName = "V";
118 
119  break;
120  }
121  case functionType::volumeDensity:
122  {
123  functionTypeSymbolicName = "v";
124 
125  break;
126  }
127  case functionType::areaConcentration:
128  {
129  functionTypeSymbolicName = "A";
130 
131  break;
132  }
133  case functionType::areaDensity:
134  {
135  functionTypeSymbolicName = "a";
136 
137  break;
138  }
139  }
140 
141  return functionTypeSymbolicName;
142 }
143 
144 
146 Foam::functionObjects::populationBalanceSizeDistribution::
147 coordinateTypeSymbolicName
148 (
149  const coordinateType& cType
150 )
151 {
152  word coordinateTypeSymbolicName(word::null);
153 
154  switch (cType)
155  {
157  {
158  coordinateTypeSymbolicName = "v";
159 
160  break;
161  }
163  {
164  coordinateTypeSymbolicName = "a";
165 
166  break;
167  }
168  case coordinateType::diameter:
169  {
170  coordinateTypeSymbolicName = "d";
171 
172  break;
173  }
174  case coordinateType::projectedAreaDiameter:
175  {
176  coordinateTypeSymbolicName = "dPa";
177 
178  break;
179  }
180  }
181 
182  return coordinateTypeSymbolicName;
183 }
184 
185 
187 Foam::functionObjects::populationBalanceSizeDistribution::filterField
188 (
189  const scalarField& field
190 ) const
191 {
192  if (zone_.all())
193  {
194  return field;
195  }
196  else
197  {
198  return tmp<scalarField>(new scalarField(field, zone_.zone()));
199  }
200 }
201 
202 
203 Foam::scalar
204 Foam::functionObjects::populationBalanceSizeDistribution::averageCoordinateValue
205 (
206  const populationBalanceModel& popBal,
207  const label i,
208  const coordinateType& cType
209 )
210 {
211  scalar averageCoordinateValue(Zero);
212 
213  switch (cType)
214  {
216  {
217  averageCoordinateValue = popBal.v(i).value();
218 
219  break;
220  }
222  {
223  averageCoordinateValue = weightedAverage(popBal, i, popBal.a(i));
224 
225  break;
226  }
227  case coordinateType::diameter:
228  {
229  averageCoordinateValue = weightedAverage(popBal, i, popBal.d(i));
230 
231  break;
232  }
233  case coordinateType::projectedAreaDiameter:
234  {
235  averageCoordinateValue =
236  weightedAverage(popBal, i, sqrt(popBal.a(i)/pi));
237 
238  break;
239  }
240  }
241 
242  return averageCoordinateValue;
243 }
244 
245 
246 Foam::scalar
247 Foam::functionObjects::populationBalanceSizeDistribution::weightedAverage
248 (
249  const populationBalanceModel& popBal,
250  const label i,
251  const Foam::scalarField& field
252 )
253 {
254  const volScalarField& alpha = popBal.phases()[i];
255  const volScalarField& fi = popBal.f(i);
256 
257  scalar weightedAverage(Zero);
258 
259  switch (weightType_)
260  {
261  case weightType::numberConcentration:
262  {
263  scalarField Ni(filterField(fi*alpha/popBal.v(i).value()));
264 
265  if (gSum(Ni) == 0)
266  {
267  weightedAverage =
268  gSum(filterField(mesh_.V()*field))/zone_.V();
269  }
270  else
271  {
272  weightedAverage =
273  gSum(Ni*filterField(field))/gSum(Ni);
274  }
275 
276  break;
277  }
278  case weightType::volumeConcentration:
279  {
280  scalarField Vi(filterField(fi*alpha));
281 
282  if (gSum(Vi) == 0)
283  {
284  weightedAverage =
285  gSum(filterField(mesh_.V()*field))/zone_.V();
286  }
287  else
288  {
289  weightedAverage =
290  gSum(Vi*filterField(field))/gSum(Vi);
291  }
292 
293  break;
294  }
295  case weightType::areaConcentration:
296  {
297  scalarField Ai(filterField(popBal.a(i)()*alpha));
298 
299  if (gSum(Ai) == 0)
300  {
301  weightedAverage =
302  gSum(filterField(mesh_.V()*field))/zone_.V();
303  }
304  else
305  {
306  weightedAverage =
307  gSum(Ai*filterField(field))/gSum(Ai);
308  }
309 
310  break;
311  }
313  {
314  weightedAverage =
315  gSum(filterField(mesh_.V()*field))/zone_.V();
316 
317  break;
318  }
319  }
320 
321  return weightedAverage;
322 }
323 
324 
325 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
326 
329 (
330  const word& name,
331  const Time& runTime,
332  const dictionary& dict
333 )
334 :
335  fvMeshFunctionObject(name, runTime, dict),
336  file_(obr_, name),
337  mesh_(fvMeshFunctionObject::mesh_),
338  zone_(fvMeshFunctionObject::mesh_, dict),
339  popBalName_(dict.lookup("populationBalance")),
340  functionType_(functionTypeNames_.read(dict.lookup("functionType"))),
341  coordinateType_(coordinateTypeNames_.read(dict.lookup("coordinateType"))),
342  allCoordinates_
343  (
344  dict.lookupOrDefault<Switch>("allCoordinates", false)
345  ),
346  normalise_(dict.lookupOrDefault<Switch>("normalise", false)),
347  logTransform_
348  (
349  dict.lookupOrDefaultBackwardsCompatible<Switch>
350  (
351  {"logTransform", "geometric"},
352  false
353  )
354  ),
355  weightType_
356  (
357  dict.found("weightType")
358  ? weightTypeNames_.read(dict.lookup("weightType"))
359  : weightType::numberConcentration
360  ),
361  formatterPtr_(nullptr)
362 {
363  read(dict);
364 }
365 
366 
367 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
368 
371 {}
372 
373 
374 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
375 
377 (
378  const dictionary& dict
379 )
380 {
381  Log << type() << " " << name() << ":" << nl;
382 
384 
385  formatterPtr_ = setWriter::New(dict.lookup("setFormat"), dict);
386 
387  return false;
388 }
389 
390 
392 {
393  return true;
394 }
395 
396 
398 {
399  Log << type() << " " << name() << " write:" << nl;
400 
401  const populationBalanceModel& popBal =
402  obr_.lookupObject<populationBalanceModel>(popBalName_);
403 
404  scalarField coordinateValues(popBal.nGroups());
405  scalarField boundaryValues(popBal.nGroups() + 1);
406  scalarField resultValues(popBal.nGroups());
407 
408  forAll(popBal.fs(), i)
409  {
410  coordinateValues[i] =
411  averageCoordinateValue(popBal, i, coordinateType_);
412  }
413 
414  if
415  (
416  functionType_ == functionType::numberDensity
417  || functionType_ == functionType::volumeDensity
418  || functionType_ == functionType::areaDensity
419  )
420  {
421  boundaryValues.first() = coordinateValues.first();
422  boundaryValues.last() = coordinateValues.last();
423 
424  for (label i = 1; i < boundaryValues.size() - 1; i++)
425  {
426  boundaryValues[i] =
427  0.5*(coordinateValues[i] + coordinateValues[i-1]);
428  }
429 
430  if (logTransform_)
431  {
432  boundaryValues = Foam::log(boundaryValues);
433  }
434  }
435 
436  switch (functionType_)
437  {
438  case functionType::numberConcentration:
439  {
440  forAll(popBal.fs(), i)
441  {
442  const volScalarField& alpha = popBal.phases()[i];
443  const volScalarField& fi = popBal.f(i);
444  const dimensionedScalar& vi = popBal.v(i);
445 
446  resultValues[i] =
447  gSum(filterField(mesh_.V()*fi*alpha/vi))/zone_.V();
448  }
449 
450  if (normalise_ && sum(resultValues) != 0)
451  {
452  resultValues /= sum(resultValues);
453  }
454 
455  break;
456  }
457  case functionType::numberDensity:
458  {
459  forAll(popBal.fs(), i)
460  {
461  const volScalarField& alpha = popBal.phases()[i];
462  const volScalarField& fi = popBal.f(i);
463  const dimensionedScalar& vi = popBal.v(i);
464 
465  resultValues[i] =
466  gSum(filterField(mesh_.V()*fi*alpha/vi))/zone_.V();
467  }
468 
469  if (normalise_ && sum(resultValues) != 0)
470  {
471  resultValues /= sum(resultValues);
472  }
473 
474  forAll(resultValues, i)
475  {
476  resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
477  }
478 
479  break;
480  }
481  case functionType::volumeConcentration:
482  {
483  forAll(popBal.fs(), i)
484  {
485  const volScalarField& alpha = popBal.phases()[i];
486  const volScalarField& fi = popBal.f(i);
487 
488  resultValues[i] =
489  gSum(filterField(mesh_.V()*fi*alpha))/zone_.V();
490  }
491 
492  if (normalise_ && sum(resultValues) != 0)
493  {
494  resultValues /= sum(resultValues);
495  }
496 
497  break;
498  }
499  case functionType::volumeDensity:
500  {
501  forAll(popBal.fs(), i)
502  {
503  const volScalarField& alpha = popBal.phases()[i];
504  const volScalarField& fi = popBal.f(i);
505 
506  resultValues[i] =
507  gSum(filterField(mesh_.V()*fi*alpha))/zone_.V();
508  }
509 
510  if (normalise_ && sum(resultValues) != 0)
511  {
512  resultValues /= sum(resultValues);
513  }
514 
515  forAll(resultValues, i)
516  {
517  resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
518  }
519 
520  break;
521  }
522  case functionType::areaConcentration:
523  {
524  forAll(popBal.fs(), i)
525  {
526  const volScalarField& alpha = popBal.phases()[i];
527  const volScalarField& fi = popBal.f(i);
528  const dimensionedScalar& vi = popBal.v(i);
529 
530  resultValues[i] =
531  gSum
532  (
533  filterField(mesh_.V()*popBal.a(i)()*fi*alpha/vi)
534  )
535  /zone_.V();
536  }
537 
538  if (normalise_ && sum(resultValues) != 0)
539  {
540  resultValues /= sum(resultValues);
541  }
542 
543  break;
544  }
545  case functionType::areaDensity:
546  {
547  forAll(popBal.fs(), i)
548  {
549  const volScalarField& alpha = popBal.phases()[i];
550  const volScalarField& fi = popBal.f(i);
551  const dimensionedScalar& vi = popBal.v(i);
552 
553  resultValues[i] =
554  gSum
555  (
556  filterField(mesh_.V()*popBal.a(i)()*fi*alpha/vi)
557  )
558  /zone_.V();
559  }
560 
561  if (normalise_ && sum(resultValues) != 0)
562  {
563  resultValues /= sum(resultValues);
564  }
565 
566  forAll(resultValues, i)
567  {
568  resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
569  }
570 
571  break;
572  }
573  }
574 
575 
576  if (allCoordinates_)
577  {
578  wordList otherCoordinateSymbolicNames(coordinateTypeNames_.size());
579  PtrList<scalarField> otherCoordinateValues(coordinateTypeNames_.size());
580 
581  forAll(coordinateTypeNames_, i)
582  {
583  const coordinateType cType = coordinateType(i);
584 
585  otherCoordinateSymbolicNames[cType] =
586  coordinateTypeSymbolicName(cType);
587 
588  otherCoordinateValues.set(cType, new scalarField(popBal.nGroups()));
589 
590  forAll(popBal.fs(), i)
591  {
592  otherCoordinateValues[cType][i] =
593  averageCoordinateValue(popBal, i, cType);
594  }
595  }
596 
597  if (Pstream::master())
598  {
599  formatterPtr_->write
600  (
601  file_.baseTimeDir(),
602  name(),
603  coordSet
604  (
605  true,
606  coordinateTypeSymbolicName(coordinateType_),
607  coordinateValues
608  ),
609  functionTypeSymbolicName(),
610  resultValues,
611  otherCoordinateSymbolicNames,
612  otherCoordinateValues
613  );
614  }
615  }
616  else
617  {
618  if (Pstream::master())
619  {
620  formatterPtr_->write
621  (
622  file_.baseTimeDir(),
623  name(),
624  coordSet
625  (
626  true,
627  coordinateTypeSymbolicName(coordinateType_),
628  coordinateValues
629  ),
630  functionTypeSymbolicName(),
631  resultValues
632  );
633  }
634  }
635 
636  return true;
637 }
638 
639 
641 (
642  const polyMesh& mesh
643 )
644 {
645  if (&mesh == &this->mesh())
646  {
647  zone_.movePoints();
648  }
649 }
650 
651 
653 (
654  const polyTopoChangeMap& map
655 )
656 {
657  if (&map.mesh() == &mesh())
658  {
659  zone_.topoChange(map);
660  }
661 }
662 
663 
665 (
666  const polyMeshMap& map
667 )
668 {
669  if (&map.mesh() == &mesh())
670  {
671  zone_.mapMesh(map);
672  }
673 }
674 
675 
677 (
678  const polyDistributionMap& map
679 )
680 {
681  if (&map.mesh() == &mesh())
682  {
683  zone_.distribute(map);
684  }
685 }
686 
687 
688 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
Macros for easy insertion into run-time selection tables.
Generic GeometricField class.
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
Holds list of sampling positions.
Definition: coordSet.H:51
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
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:78
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const polyMesh & mesh() const
Return polyMesh.
Model for tracking the evolution of a dispersed phase size distribution due to coalescence (synonymou...
const volScalarField & f(const label i) const
Access a group fraction.
const dimensionedScalar & v(const label i) const
Access the representative volumes diameters of a group.
label nGroups() const
Return the number of groups in the population balance.
const UPtrList< const phaseModel > & phases() const
Access the list of phases associated with each group.
const PtrList< volScalarField > & fs() const
Access the group fractions.
tmp< volScalarField > a(const label i) const
Return the representative surface area of the group.
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:63
static const word null
An empty word.
Definition: word.H:78
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
volScalarField alpha(IOobject("alpha", runTime.name(), mesh, IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE), lambda *max(Ua &U, zeroSensitivity))
#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.
const dimensionSet area
const dimensionSet volume
Definition: annulus.H:177
defineTypeNameAndDebug(fvMeshFunctionObject, 0)
addToRunTimeSelectionTable(functionObject, fvModel, dictionary)
const unitSet & lookup(const word &unitName)
Lookup and return the named unit from the table.
Definition: units.C:346
Namespace for OpenFOAM.
static const zero Zero
Definition: zero.H:97
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
Type gSum(const UList< Type > &f, const label comm)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
dimensionedScalar log(const dimensionedScalar &ds)
VolField< scalar > volScalarField
Definition: volFieldsFwd.H:62
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:297
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