populationBalanceModel.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-2026 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 "populationBalance.H"
27 #include "populationBalanceModel.H"
28 #include "phaseSystem.H"
30 #include "shapeModel.H"
31 #include "coalescenceModel.H"
33 #include "binary.H"
34 #include "fvmDdt.H"
35 #include "fvmDiv.H"
36 #include "fvmSup.H"
37 #include "fvcDdt.H"
38 #include "fvcDiv.H"
39 #include "distribution.H"
40 #include "fvSpecificSource.H"
43 
44 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
45 
46 namespace Foam
47 {
49 }
50 
51 
52 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
53 
55 (
56  const word& name,
57  const label i,
58  const phaseModel& phase,
59  const IOobject::readOption r,
60  const IOobject::writeOption w,
61  const bool registerObject
62 )
63 {
64  return
65  IOobject
66  (
68  (
69  name + (i == -1 ? "Default" : Foam::name(i)),
70  phase.name()
71  ),
72  phase.mesh().time().name(),
73  phase.mesh(),
74  r,
75  w,
77  );
78 }
79 
80 
82 (
83  const word& name,
84  const label i,
85  const phaseModel& phase
86 )
87 {
89  (
90  groupFieldIo
91  (
92  name,
93  i,
94  phase,
97  false
98  )
99  );
100 
101  return
103  (
104  new volScalarField
105  (
106  io.headerOk()
107  ? io
108  : groupFieldIo
109  (
110  name,
111  -1,
112  phase,
115  false
116  ),
117  phase.mesh()
118  )
119  );
120 }
121 
122 
123 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
124 
125 const Foam::dictionary& Foam::populationBalanceModel::typeDict() const
126 {
127  return fluid_.optionalTypeDict("populationBalance").subDict(name_);
128 }
129 
130 
131 void Foam::populationBalanceModel::precomputeCoalescenceAndBreakup()
132 {
133  coalescenceModel_->precompute();
134 
135  breakupModel_->precompute();
136 }
137 
138 
139 void Foam::populationBalanceModel::birthByCoalescence
140 (
141  const label j,
142  const label k,
144 )
145 {
146  const dimensionedScalar vjk = vs_[j] + vs_[k];
147 
148  const volScalarField::Internal alphaFjk
149  (
150  phases_[j]()*fs_[j]()*phases_[k]()*fs_[k]()
151  );
152 
153  for (label i = j; i < nGroups(); i++)
154  {
155  const dimensionedScalar Eta = eta(i, vjk);
156 
157  if (Eta.value() == 0) continue;
158 
160  (
161  (j == k ? 0.5 : 1)
162  *vs_[i]/(vs_[j]*vs_[k])*Eta*rate*alphaFjk
163  );
164 
165  Su_[i] += Sui;
166 
167  const phaseInterface interfaceij(phases_[i], phases_[j]);
168 
169  if (dmdtfs_.found(interfaceij))
170  {
171  dmdtfs_[interfaceij] +=
172  (interfaceij.index(phases_[i]) == 0 ? +1 : -1)
173  *vs_[j]/vjk*Sui*phases_[j].rho();
174  }
175 
176  const phaseInterface interfaceik(phases_[i], phases_[k]);
177 
178  if (dmdtfs_.found(interfaceik))
179  {
180  dmdtfs_[interfaceik] +=
181  (interfaceik.index(phases_[i]) == 0 ? +1 : -1)
182  *vs_[k]/vjk*Sui*phases_[k].rho();
183  }
184 
185  shapeModel_->addCoalescence(Sui, i, j, k);
186  }
187 }
188 
189 
190 void Foam::populationBalanceModel::deathByCoalescence
191 (
192  const label i,
193  const label j,
195 )
196 {
197  Sp_[i] -= rate*phases_[i]*fs_[j]*phases_[j]/vs_[j];
198 
199  if (i == j) return;
200 
201  Sp_[j] -= rate*phases_[j]*phases_[i]*fs_[i]/vs_[i];
202 }
203 
204 
205 void Foam::populationBalanceModel::birthByDaughterSizeDistributionBreakup
206 (
207  const label k,
209 )
210 {
211  for (label i = 0; i <= k; i++)
212  {
213  const volScalarField::Internal Sui
214  (
215  rate*phases_[k]*fs_[k]
216  *vs_[i]/vs_[k]
217  *daughterSizeDistributionBreakupModel_->dsd().nik(i, k)
218  );
219 
220  Su_[i] += Sui;
221 
222  const phaseInterface interface(phases_[i], phases_[k]);
223 
224  if (dmdtfs_.found(interface))
225  {
226  dmdtfs_[interface] +=
227  (interface.index(phases_[i]) == 0 ? +1 : -1)
228  *Sui*phases_[k].rho();
229  }
230 
231  shapeModel_->addBreakup(Sui, i, k);
232  }
233 }
234 
235 
236 void Foam::populationBalanceModel::deathByDaughterSizeDistributionBreakup
237 (
238  const label i,
240 )
241 {
242  Sp_[i] -= rate*phases_[i];
243 }
244 
245 
246 void Foam::populationBalanceModel::birthByBinaryBreakup
247 (
248  const label i,
249  const label j,
251 )
252 {
253  const volScalarField::Internal Su(rate*phases_[j]*fs_[j]);
254 
255  {
256  const volScalarField::Internal Sui
257  (
258  vs_[i]*binaryBreakupDeltas_[i][j]/vs_[j]*Su
259  );
260 
261  Su_[i] += Sui;
262 
263  const phaseInterface interfaceij(phases_[i], phases_[j]);
264 
265  if (dmdtfs_.found(interfaceij))
266  {
267  dmdtfs_[interfaceij] +=
268  (interfaceij.index(phases_[i]) == 0 ? +1 : -1)
269  *Sui*phases_[j].rho();
270  }
271 
272  shapeModel_->addBreakup(Sui, i, j);
273  }
274 
275  const dimensionedScalar v = vs_[j] - vs_[i];
276 
277  for (label k = 0; k <= j; k ++)
278  {
279  const dimensionedScalar Eta = eta(k, v);
280 
281  if (Eta.value() == 0) continue;
282 
283  const volScalarField::Internal Suk
284  (
285  vs_[k]*binaryBreakupDeltas_[i][j]*Eta/vs_[j]*Su
286  );
287 
288  Su_[k] += Suk;
289 
290  const phaseInterface interfacekj(phases_[k], phases_[j]);
291 
292  if (dmdtfs_.found(interfacekj))
293  {
294  dmdtfs_[interfacekj] +=
295  (interfacekj.index(phases_[k]) == 0 ? +1 : -1)
296  *Suk*phases_[j].rho();
297  }
298 
299  shapeModel_->addBreakup(Suk, k, j);
300  }
301 }
302 
303 
304 void Foam::populationBalanceModel::deathByBinaryBreakup
305 (
306  const label j,
307  const label i,
309 )
310 {
311  Sp_[i] -= rate*phases_[i]*binaryBreakupDeltas_[j][i];
312 }
313 
314 
315 void Foam::populationBalanceModel::computeCoalescenceAndBreakup()
316 {
317  shapeModel_->reset();
318 
319  forAll(fs_, i)
320  {
321  Su_[i] = Zero;
322  Sp_[i] = Zero;
323  }
324 
325  forAllIter(dmdtfTable, dmdtfs_, dmdtfIter)
326  {
327  *dmdtfIter() = Zero;
328  }
329 
330  if (coalescenceModel_->coalesces())
331  {
332  forAll(coalescencePairs_, coalescencePairi)
333  {
334  const label i = coalescencePairs_[coalescencePairi].first();
335  const label j = coalescencePairs_[coalescencePairi].second();
336 
337  tmp<volScalarField::Internal> trate = coalescenceModel_->rate(i, j);
338 
339  birthByCoalescence(i, j, trate());
340 
341  deathByCoalescence(i, j, trate());
342  }
343  }
344 
345  if (daughterSizeDistributionBreakupModel_)
346  {
347  forAll(fs_, i)
348  {
349  tmp<volScalarField::Internal> trate =
350  daughterSizeDistributionBreakupModel_->rate(i);
351 
352  birthByDaughterSizeDistributionBreakup(i, trate());
353 
354  deathByDaughterSizeDistributionBreakup(i, trate());
355  }
356  }
357 
358  if (binaryBreakupModel_)
359  {
360  forAll(binaryBreakupPairs_, binaryBreakupPairi)
361  {
362  const label i = binaryBreakupPairs_[binaryBreakupPairi].first();
363  const label j = binaryBreakupPairs_[binaryBreakupPairi].second();
364 
365  tmp<volScalarField::Internal> trate =
366  binaryBreakupModel_->rate(j, i);
367 
368  birthByBinaryBreakup(j, i, trate());
369 
370  deathByBinaryBreakup(j, i, trate());
371  }
372  }
373 }
374 
375 
376 void Foam::populationBalanceModel::precomputeExpansion()
377 {
378  forAll(uniquePhases_, uniquePhasei)
379  {
380  const phaseModel& phase = uniquePhases_[uniquePhasei];
381  const volScalarField& alpha = phase;
382  const volScalarField& rho = phase.rho();
383 
384  expansionRates_.set
385  (
386  phase.index(),
387  - (fvc::ddt(alpha, rho)()() + fvc::div(phase.alphaRhoPhi())()())/rho()
388  + fvc::ddt(alpha)()() + fvc::div(phase.alphaPhi())()()
389  );
390  }
391 }
392 
393 
395 Foam::populationBalanceModel::expansionSus
396 (
397  const label i,
398  const UPtrList<volScalarField>& flds
399 ) const
400 {
401  auto fiFld = [&](const label deltai)
402  {
403  return
404  flds.empty()
405  ? tmp<volScalarField::Internal>(fs_[i + deltai])
406  : fs_[i + deltai]()*flds[i + deltai]();
407  };
408 
409  Pair<tmp<volScalarField::Internal>> tSus;
410 
411  if (i != 0)
412  {
413  tSus.first() =
414  posPart(expansionRates_[phases_[i - 1].index()])
415  *vs_[i]/(vs_[i] - vs_[i - 1])
416  *fiFld(-1);
417  }
418 
419  if (i != nGroups() - 1)
420  {
421  tSus.second() =
422  - negPart(expansionRates_[phases_[i + 1].index()])
423  *vs_[i]/(vs_[i + 1] - vs_[i])
424  *fiFld(+1);
425  }
426 
427  return tSus;
428 }
429 
430 
431 void Foam::populationBalanceModel::computeExpansion()
432 {
433  forAllIter(dmdtfTable, expansionDmdtfs_, dmdtfIter)
434  {
435  *dmdtfIter() = Zero;
436  }
437 
438  for
439  (
440  label uniquePhasei = 0;
441  uniquePhasei < uniquePhases_.size() - 1;
442  ++ uniquePhasei
443  )
444  {
445  const phaseModel& phase0 = uniquePhases_[uniquePhasei];
446  const phaseModel& phase1 = uniquePhases_[uniquePhasei + 1];
447 
448  const label i0 = uniqueDiameters_[uniquePhasei].iLast();
449  const label i1 = uniqueDiameters_[uniquePhasei + 1].iFirst();
450 
451  Pair<tmp<volScalarField::Internal>> tSus0 = expansionSus(i0);
452  Pair<tmp<volScalarField::Internal>> tSus1 = expansionSus(i1);
453 
454  const phaseInterface interface01(phase0, phase1);
455 
456  expansionDmdtfs_[interface01] +=
457  (interface01.index(phase0) == 0 ? -1 : +1)
458  *(- tSus0.second()*phase0.rho() + tSus1.first()*phase1.rho());
459  }
460 }
461 
462 
463 void Foam::populationBalanceModel::precomputeModelSources()
464 {
465  // nothing to do
466 }
467 
468 
470 Foam::populationBalanceModel::modelSourceRhoSus
471 (
472  const label i,
473  const UPtrList<volScalarField>& flds
474 ) const
475 {
476  const volScalarField& fldi = flds.empty() ? fs_[i] : flds[i];
477 
478  Pair<tmp<volScalarField::Internal>> tRhoSus;
479 
480  forAll(tRhoSus, Sui)
481  {
482  const label iPopBalEnd = Sui == 0 ? 0 : nGroups() - 1;
483 
484  if (i == iPopBalEnd) continue;
485 
486  const label iVelGrpEnd =
487  Sui == 0 ? diameters_[i].iFirst() : diameters_[i].iLast();
488 
489  if (i != iVelGrpEnd) continue;
490 
491  const label iOther = i + (Sui == 0 ? -1 : +1);
492 
493  forAll(fluid_.fvModels(), modeli)
494  {
495  if (!isA<fvSpecificSource>(fluid_.fvModels()[modeli])) continue;
496 
497  const fvSpecificSource& source =
498  refCast<const fvSpecificSource>(fluid_.fvModels()[modeli]);
499 
500  if
501  (
502  source.addsSupToField(phases_[iOther].volScalarField::name())
503  && isA<growthFvScalarFieldSource>(fldi.sources()[source.name()])
504  )
505  {
506  const growthFvScalarFieldSource& growthSource =
507  refCast<const growthFvScalarFieldSource>
508  (
509  fldi.sources()[source.name()]
510  );
511 
512  const volScalarField::Internal S(source.S(fs_[iOther].name()));
513 
514  Pair<tmp<volScalarField::Internal>> sourceCoeffs =
515  growthSource.sourceCoeffs(source);
516 
517  tRhoSus[Sui] =
518  Sui == 0
519  ? posPart(S)*sourceCoeffs.first()
520  : negPart(S)*sourceCoeffs.second();
521  }
522  }
523  }
524 
525  return tRhoSus;
526 }
527 
528 
529 void Foam::populationBalanceModel::computeModelSources()
530 {
531  forAllIter(dmdtfTable, modelSourceDmdtfs_, dmdtfIter)
532  {
533  *dmdtfIter() = Zero;
534  }
535 
536  forAll(uniquePhases_, uniquePhasei)
537  {
538  const label i0 = uniqueDiameters_[uniquePhasei].iLast();
539 
540  if (i0 == nGroups() - 1) continue;
541 
542  const label i1 = i0 + 1;
543 
544  Pair<tmp<volScalarField::Internal>> tRhoSus0 = modelSourceRhoSus(i0);
545  Pair<tmp<volScalarField::Internal>> tRhoSus1 = modelSourceRhoSus(i1);
546 
547  const phaseInterface interface01(phases_[i0], phases_[i1]);
548  const scalar sign = interface01.index(phases_[i0]) == 0 ? -1 : +1;
549 
550  if (tRhoSus0.second().valid())
551  {
552  modelSourceDmdtfs_[interface01] -= sign*tRhoSus0.second();
553  }
554 
555  if (tRhoSus1.first().valid())
556  {
557  modelSourceDmdtfs_[interface01] -= sign*tRhoSus1.first();
558  }
559  }
560 }
561 
562 
563 void Foam::populationBalanceModel::computeDilatationErrors()
564 {
565  PtrList<volScalarField::Internal> modelSourceDmdts(fluid_.phases().size());
566  forAllConstIter(dmdtfTable, modelSourceDmdtfs_, dmdtfIter)
567  {
568  const phaseInterface interface(fluid_, dmdtfIter.key());
569 
570  addField(interface.phase1(), "dmdt", *dmdtfIter(), modelSourceDmdts);
571  addField(interface.phase2(), "dmdt", - *dmdtfIter(), modelSourceDmdts);
572  }
573 
574  forAll(uniquePhases_, uniquePhasei)
575  {
576  const phaseModel& phase = uniquePhases_[uniquePhasei];
577  const diameterModels::populationBalance& diameter =
578  uniqueDiameters_[uniquePhasei];
579  const volScalarField& alpha = phase;
580  const volScalarField& rho = phase.rho();
581 
582  dilatationErrors_.set
583  (
584  phase.index(),
585  fvc::ddt(alpha)()() + fvc::div(phase.alphaPhi())()()
586  - (fluid_.fvModels().source(alpha, rho) & rho)()()/rho()
587  );
588 
589  for (label i = diameter.iFirst(); i <= diameter.iLast(); ++ i)
590  {
591  dilatationErrors_[phase.index()] -=
592  Su_[i] + expansionSu(i) + (Sp_[i] + expansionSp(i))*fs_[i];
593  }
594 
595  if (modelSourceDmdts.set(phase.index()))
596  {
597  dilatationErrors_[phase.index()] -=
598  modelSourceDmdts[phase.index()]/rho;
599  }
600  }
601 }
602 
603 
604 bool Foam::populationBalanceModel::updateSources()
605 {
606  const bool result = sourceUpdateCounter_ % sourceUpdateInterval() == 0;
607 
608  ++ sourceUpdateCounter_;
609 
610  return result;
611 }
612 
613 
614 Foam::Pair<Foam::dimensionedScalar> Foam::populationBalanceModel::etaCoeffs0
615 (
616  const label i
617 ) const
618 {
619  return
620  i == 0
621  ? Pair<dimensionedScalar>
622  (
623  dimensionedScalar(dimless, scalar(0)),
624  1/vs_[i]
625  )
626  : Pair<dimensionedScalar>
627  (
628  -vs_[i-1]/(vs_[i] - vs_[i-1]),
629  1/(vs_[i] - vs_[i-1])
630  );
631 }
632 
633 
634 Foam::Pair<Foam::dimensionedScalar> Foam::populationBalanceModel::etaCoeffs1
635 (
636  const label i
637 ) const
638 {
639  return
640  i == vs_.size() - 1
641  ? Pair<dimensionedScalar>
642  (
643  dimensionedScalar(dimless, scalar(0)),
644  1/vs_[i]
645  )
646  : Pair<dimensionedScalar>
647  (
648  vs_[i+1]/(vs_[i+1] - vs_[i]),
649  -1/(vs_[i+1] - vs_[i])
650  );
651 }
652 
653 
654 Foam::Pair<Foam::dimensionedScalar> Foam::populationBalanceModel::etaVCoeffs0
655 (
656  const label i
657 ) const
658 {
659  return
660  i == 0
661  ? Pair<dimensionedScalar>
662  (
663  dimensionedScalar(dimless, scalar(1)),
664  dimensionedScalar(dimVolume, scalar(0))
665  )
666  : Pair<dimensionedScalar>
667  (
668  -1/vs_[i-1]/(1/vs_[i] - 1/vs_[i-1]),
669  1/(1/vs_[i] - 1/vs_[i-1])
670  );
671 }
672 
673 
674 Foam::Pair<Foam::dimensionedScalar> Foam::populationBalanceModel::etaVCoeffs1
675 (
676  const label i
677 ) const
678 {
679  return
680  i == vs_.size() - 1
681  ? Pair<dimensionedScalar>
682  (
683  dimensionedScalar(dimless, scalar(1)),
684  dimensionedScalar(dimVolume, scalar(0))
685  )
686  : Pair<dimensionedScalar>
687  (
688  1/vs_[i+1]/(1/vs_[i+1] - 1/vs_[i]),
689  -1/(1/vs_[i+1] - 1/vs_[i])
690  );
691 }
692 
693 
694 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
695 
697 (
698  const phaseSystem& fluid,
699  const word& name
700 )
701 :
703  (
704  IOobject
705  (
706  name,
707  fluid.time().constant(),
708  fluid.mesh()
709  )
710  ),
711  fluid_(fluid),
712  mesh_(fluid_.mesh()),
713  name_(name),
714  continuousPhase_
715  (
716  mesh_.lookupObject<phaseModel>
717  (
718  IOobject::groupName("alpha", typeDict().lookup("continuousPhase"))
719  )
720  ),
721  phases_(),
722  uniquePhases_(),
723  diameters_(),
724  uniqueDiameters_(),
725  fs_(),
726  dSphs_(),
727  vs_(),
728  Su_(),
729  Sp_(),
730  dmdtfs_(),
731  expansionDmdtfs_(),
732  modelSourceDmdtfs_(),
733  expansionRates_(fluid_.phases().size()),
734  dilatationErrors_(fluid_.phases().size()),
735  shapeModel_(nullptr),
736  coalescenceModel_(),
737  coalescencePairs_(),
738  breakupModel_(),
739  daughterSizeDistributionBreakupModel_(nullptr),
740  binaryBreakupModel_(nullptr),
741  binaryBreakupDeltas_(),
742  binaryBreakupPairs_(),
743  alphas_(),
744  dsm_(),
745  U_(),
746  sourceUpdateCounter_(0)
747 {
748  Info<< indentOrNl << "Constructing " << typeName << ' ' << name << endl;
749 
750  printDictionary print(typeDict());
751 
752  // Build the phase-reference lists
753  for
754  (
755  label fluidPhasei = 0, nGroups = 0;
756  fluidPhasei < fluid.phases().size();
757  ++ fluidPhasei
758  )
759  {
760  const phaseModel& phase = fluid.phases()[fluidPhasei];
761 
762  if (!isA<diameterModels::populationBalance>(phase.diameter())) continue;
763 
764  const diameterModels::populationBalance& diameter =
765  refCast<const diameterModels::populationBalance>(phase.diameter());
766 
767  if (diameter.popBalName() != name_) continue;
768 
769  uniquePhases_.append(&phase);
770  uniqueDiameters_.append(&diameter);
771 
772  phases_.resize(nGroups + diameter.nGroups());
773  diameters_.resize(nGroups + diameter.nGroups());
774  for (label i = 0; i < diameter.nGroups(); ++ i)
775  {
776  phases_.set(nGroups + i, &phase);
777  diameters_.set(nGroups + i, &diameter);
778  }
779 
780  nGroups += diameter.nGroups();
781  }
782 
783  // Check that there are sufficiently many groups
784  if (nGroups() < 3)
785  {
787  << "A population balance model requires a minimum of 3 groups but "
788  << name_ << " only has " << nGroups() << exit(FatalError);
789  }
790 
791  // Read/initialise the group fraction fields
792  fs_.resize(nGroups());
793  forAll(fs_, i)
794  {
795  fs_.set
796  (
797  i,
798  new volScalarField
799  (
801  (
802  "f",
803  i,
804  phases_[i],
807  ),
808  groupField("f", i, phases_[i])
809  )
810  );
811  }
812 
813  // Create a discretisation in representative spherical diameter space
814  dSphs_ =
816  (
817  "dSph",
818  dimLength,
819  nGroups(),
820  typeDict().subDict("sphericalDiameters")
821  )->dimensionedCoordinates();
822 
823  // Build the groups' representative volumes
824  vs_.resize(nGroups());
825  forAll(fs_, i)
826  {
827  vs_.set
828  (
829  i,
831  (
832  "v" + Foam::name(i),
833  constant::mathematical::pi/6*pow3(dSphs_[i])
834  )
835  );
836  }
837 
838  // Print some confirmatory information about the setup
839  forAll(uniquePhases_, uniquePhasei)
840  {
841  const phaseModel& phase = uniquePhases_[uniquePhasei];
842  const diameterModels::populationBalance& diameter =
843  uniqueDiameters_[uniquePhasei];
844 
845  Info<< indent << "Phase: " << phase.name() << incrIndent << endl;
846 
847  for (label i = diameter.iFirst(); i <= diameter.iLast(); ++ i)
848  {
849  Info<< indent << "Group #" << i
850  << ": dSph = " << dSphs_[i].value()
851  << ", min/average/max fraction = "
852  << min(fs_[i]()).value() << '/'
853  << average(fs_[i]()).value() << '/'
854  << max(fs_[i]()).value() << endl;
855  }
856 
857  Info<< decrIndent;
858  }
859 
860  // Create source terms
861  Su_.setSize(nGroups());
862  Sp_.setSize(nGroups());
863  forAll(Su_, i)
864  {
865  Su_.set
866  (
867  i,
869  (
870  IOobject
871  (
872  IOobject::groupName("Su" + Foam::name(i), this->name()),
873  fluid_.time().name(),
874  mesh_
875  ),
876  mesh_,
878  )
879  );
880 
881  Sp_.set
882  (
883  i,
885  (
886  IOobject
887  (
888  IOobject::groupName("Sp" + Foam::name(i), this->name()),
889  fluid_.time().name(),
890  mesh_
891  ),
892  mesh_,
894  )
895  );
896  }
897 
898  // Create interfacial mass transfer rates
899  forAll(uniquePhases_, uniquePhasei)
900  {
901  for
902  (
903  label uniquePhasej = 0;
904  uniquePhasej < uniquePhasei;
905  ++ uniquePhasej
906  )
907  {
908  const phaseInterface interface
909  (
910  uniquePhases_[uniquePhasei],
911  uniquePhases_[uniquePhasej]
912  );
913 
914  dmdtfs_.insert
915  (
916  interface,
918  (
919  IOobject
920  (
922  (
923  typedName("dmdtf"),
924  interface.name()
925  ),
926  mesh().time().name(),
927  mesh()
928  ),
929  mesh(),
931  )
932  );
933 
934  expansionDmdtfs_.insert
935  (
936  interface,
938  (
939  IOobject
940  (
942  (
943  typedName("expansionDmdtf"),
944  interface.name()
945  ),
946  mesh().time().name(),
947  mesh()
948  ),
949  mesh(),
951  )
952  );
953 
954  modelSourceDmdtfs_.insert
955  (
956  interface,
958  (
959  IOobject
960  (
962  (
963  typedName("modelSourceDmdtf"),
964  interface.name()
965  ),
966  mesh().time().name(),
967  mesh()
968  ),
969  mesh(),
971  )
972  );
973  }
974  }
975 
976  using namespace populationBalance;
977 
978  // Select the shape model
979  shapeModel_.set(shapeModel::New(typeDict(), *this).ptr());
980 
981  // Select coalescence model
982  coalescenceModel_.set(coalescenceModel::New(*this, typeDict()).ptr());
983  if (coalescenceModel_->coalesces())
984  {
985  forAll(fs_, i)
986  {
987  for (label j = 0; j <= i; j++)
988  {
989  coalescencePairs_.append(labelPair(i, j));
990  }
991  }
992  }
993 
994  // Select breakup model
995  breakupModel_.set(breakupModel::New(*this, typeDict()).ptr());
996  if (isA<breakupModels::daughterSizeDistribution>(breakupModel_()))
997  {
998  daughterSizeDistributionBreakupModel_ =
999  &refCast<breakupModels::daughterSizeDistribution>(breakupModel_());
1000  }
1001  if (isA<breakupModels::binary>(breakupModel_()))
1002  {
1003  binaryBreakupModel_ =
1004  &refCast<breakupModels::binary>(breakupModel_());
1005  }
1006  if (binaryBreakupModel_)
1007  {
1008  binaryBreakupDeltas_.setSize(nGroups());
1009 
1010  forAll(fs_, i)
1011  {
1012  binaryBreakupDeltas_.set
1013  (
1014  i,
1016  );
1017 
1018  const dimensionedScalar vMid0
1019  (
1020  i == 0 ? vs_.first() : (vs_[i - 1] + vs_[i])/2
1021  );
1022  const dimensionedScalar vMid1
1023  (
1024  i == nGroups() - 1 ? vs_.last() : (vs_[i] + vs_[i + 1])/2
1025  );
1026 
1027  forAll(fs_, j)
1028  {
1029  binaryBreakupDeltas_[i].set
1030  (
1031  j,
1032  new dimensionedScalar
1033  (
1034  "binaryBreakupDelta_"
1035  + Foam::name(i)
1036  + "_"
1037  + Foam::name(j),
1038  vMid0 < 0.5*vs_[j] && 0.5*vs_[j] < vMid1
1039  ? mag(0.5*vs_[j] - vMid0)
1040  : 0.5*vs_[j] < vMid0
1041  ? dimensionedScalar(dimVolume, scalar(0))
1042  : vMid1 - vMid0
1043  )
1044  );
1045  }
1046  }
1047 
1048  forAll(fs_, i)
1049  {
1050  label j = 0;
1051 
1052  while (binaryBreakupDeltas_[j][i].value() != 0)
1053  {
1054  binaryBreakupPairs_.append(labelPair(i, j));
1055  j++;
1056  }
1057  }
1058  }
1059 
1060  // Everything is now available so correct the diameter models and the
1061  // population balance model so that all publicly available data is valid
1062  forAll(uniquePhases_, uniquePhasei)
1063  {
1065  (
1066  uniqueDiameters_[uniquePhasei]
1067  ).correct();
1068  }
1069 
1070  correct();
1071 }
1072 
1073 
1074 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1075 
1077 {}
1078 
1079 
1080 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1081 
1084 {
1085  return shapeModel_();
1086 }
1087 
1088 
1090 (
1091  const label i
1092 ) const
1093 {
1094  return shapeModel_->a(i);
1095 }
1096 
1097 
1099 (
1100  const label i
1101 ) const
1102 {
1103  return shapeModel_->d(i);
1104 }
1105 
1106 
1108 (
1109  const label i,
1110  const dimensionedScalar& v
1111 ) const
1112 {
1113  const Pair<dimensionedScalar> coeffs0 = etaCoeffs0(i);
1114  const Pair<dimensionedScalar> coeffs1 = etaCoeffs1(i);
1115 
1116  return
1117  max
1118  (
1119  min
1120  (
1121  coeffs0.first() + coeffs0.second()*v,
1122  coeffs1.first() + coeffs1.second()*v
1123  ),
1124  dimensionedScalar(dimless, scalar(0))
1125  );
1126 }
1127 
1128 
1130 (
1131  const label i,
1133 ) const
1134 {
1135  const Pair<dimensionedScalar> coeffs0 = etaCoeffs0(i);
1136  const Pair<dimensionedScalar> coeffs1 = etaCoeffs1(i);
1137 
1138  return
1139  max
1140  (
1141  min
1142  (
1143  coeffs0.first() + coeffs0.second()*v,
1144  coeffs1.first() + coeffs1.second()*v
1145  ),
1146  dimensionedScalar(dimless, scalar(0))
1147  );
1148 }
1149 
1150 
1152 (
1153  const label i,
1154  const dimensionedScalar& v
1155 ) const
1156 {
1157  const Pair<dimensionedScalar> coeffs0 = etaVCoeffs0(i);
1158  const Pair<dimensionedScalar> coeffs1 = etaVCoeffs1(i);
1159 
1160  return
1161  max
1162  (
1163  min
1164  (
1165  coeffs0.first() + coeffs0.second()/v,
1166  coeffs1.first() + coeffs1.second()/v
1167  ),
1168  dimensionedScalar(dimless, scalar(0))
1169  );
1170 }
1171 
1172 
1175 (
1176  const label i,
1178 ) const
1179 {
1180  const Pair<dimensionedScalar> coeffs0 = etaVCoeffs0(i);
1181  const Pair<dimensionedScalar> coeffs1 = etaVCoeffs1(i);
1182 
1183  return
1184  max
1185  (
1186  min
1187  (
1188  coeffs0.first() + coeffs0.second()/v,
1189  coeffs1.first() + coeffs1.second()/v
1190  ),
1191  dimensionedScalar(dimless, scalar(0))
1192  );
1193 }
1194 
1195 
1197 (
1198  const labelPair is,
1199  const distribution& d
1200 ) const
1201 {
1202  // Check that the distribution is generating volumes
1203  if (d.sampleQ() != 3)
1204  {
1206  << "The volumetric allocation coefficient should be evaluated "
1207  << "with a volumetrically sampled distribution (i.e., sampleQ "
1208  << "should equal 3)"
1209  << exit(FatalError);
1210  }
1211 
1212  // Get the four diameters that bound the sections of the group range's
1213  // basis function. Diameters #1 and #2 are the representative diameters of
1214  // the first and last groups in the range. Diameters #0 and #3 are the
1215  // diameters of the adjacent groups, or if at an end (or ends), the upper
1216  // or lower bounding diameter (or diameters) of the distribution.
1217  //
1218  // ^ .
1219  // | o - o - - - - o .
1220  // F_first | . . .\ .
1221  // | . . . \ .
1222  // +----+---+---------+--o------------------------------+---->
1223  // 0 1 2 3 .
1224  // ^ . .
1225  // | . o - - - - o .
1226  // F_middle | . /. .\ .
1227  // | . / . . \ .
1228  // +----+-------------o--+---------+--o-----------------+---->
1229  // . 0 1 2 3 .
1230  // ^ . .
1231  // | . o - - - - o - - - o
1232  // F_last | . /. . .
1233  // | . / . . .
1234  // +----+--------------------------o--+---------+-------+---->
1235  // . 0 1 2 3
1236  // . .
1237  // dMin dMax
1238  //
1239  const bool isFirst = is.first() == 0;
1240  const bool isLast = is.second() == nGroups() - 1;
1241  const scalarField dSphs
1242  (
1243  scalarList
1244  ({
1245  isFirst
1246  ? d.min()*(1 - small)
1247  : dSphs_[is.first() - 1].value(),
1248  dSphs_[is.first()].value(),
1249  dSphs_[is.second()].value(),
1250  isLast
1251  ? d.max()*(1 + small)
1252  : dSphs_[is.second() + 1].value()
1253  })
1254  );
1255 
1256  // Integrate the distribution, and the distribution divided by volume,
1257  // across the group range's basis function
1258  const scalarField integralPDFs
1259  (
1260  d.integralPDFxPow(dSphs, 0, true)
1261  );
1262  const scalarField integralPDFByVs
1263  (
1264  d.integralPDFxPow(dSphs, -3, true)
1266  );
1267 
1268  // Compute the integral of the group range's basis function multiplied by
1269  // the PDF.
1270  //
1271  // Between diameters #1 and #2 the basis function is a constant value of
1272  // one, so the integral is just the same as that of the distribution.
1273  //
1274  // Between diameters #0 and #1, and between #2 and #3, the basis function
1275  // is given by 'C0 + C1/v', where C0 and C1 are thecoefficients given by
1276  // etaVCoeffs0 and etaVCoeffs1. So, the integral is 'Integral(C0*PDF +
1277  // C1/v*PDF)'. C0 and C1 are constants, so this can be rearranged to
1278  // 'C0*Integral(PDF) + C1*Integral(PDF/v)'. The integrals in this
1279  // expression are those calculated above.
1280  const Pair<dimensionedScalar> etaVCoeffs0 = this->etaVCoeffs0(is.first());
1281  const Pair<dimensionedScalar> etaVCoeffs1 = this->etaVCoeffs1(is.second());
1282  return
1283  etaVCoeffs0.first().value()
1284  *(integralPDFs[1] - integralPDFs[0])
1285  + etaVCoeffs0.second().value()
1286  *(integralPDFByVs[1] - integralPDFByVs[0])
1287  + integralPDFs[2] - integralPDFs[1]
1288  + etaVCoeffs1.first().value()
1289  *(integralPDFs[3] - integralPDFs[2])
1290  + etaVCoeffs1.second().value()
1291  *(integralPDFByVs[3] - integralPDFByVs[2]);
1292 }
1293 
1294 
1296 (
1297  const label i,
1298  const distribution& d
1299 ) const
1300 {
1301  const label i0 = diameters_[i].iFirst();
1302  const label iNm1 = diameters_[i].iLast();
1303 
1304  // Compute the integral for the group's basis function and for the phase's
1305  // basis function. The allocation coefficient for this group is then the
1306  // ratio of these two integrals. This means the group integral is "scaled
1307  // up" to be a proportion for the phase, rather than for the population
1308  // balance as a whole.
1309 
1310  const dimensionedScalar groupIntegralPDFetaV = etaV(labelPair(i, i), d);
1311  const dimensionedScalar phaseIntegralPDFetaV = etaV(labelPair(i0, iNm1), d);
1312 
1313  static const dimensionedScalar dimlessRootVSmall(dimless, rootVSmall);
1314 
1315  return
1316  max(groupIntegralPDFetaV, dimlessRootVSmall/diameters_[i].nGroups())
1317  /max(phaseIntegralPDFetaV, dimlessRootVSmall);
1318 }
1319 
1320 
1323 (
1324  const phaseModel& dispersedPhase
1325 ) const
1326 {
1327  return phaseInterface(dispersedPhase, continuousPhase_).sigma();
1328 }
1329 
1330 
1333 (
1334  const label i
1335 ) const
1336 {
1337  return phaseInterface(phases_[i], continuousPhase_).sigma();
1338 }
1339 
1340 
1343 {
1344  return
1346  (
1347  continuousPhase_.name()
1348  );
1349 }
1350 
1351 
1353 (
1354  const label i
1355 ) const
1356 {
1357  return Sp_[i];
1358 }
1359 
1360 
1363 (
1364  const label i,
1365  const UPtrList<volScalarField>& flds
1366 ) const
1367 {
1368  Pair<tmp<volScalarField::Internal>> tSus = expansionSus(i, flds);
1369 
1370  return
1371  !tSus.first().valid() ? tSus.second()
1372  : !tSus.second().valid() ? tSus.first()
1373  : tSus.first() + tSus.second();
1374 }
1375 
1376 
1379 (
1380  const label i
1381 ) const
1382 {
1383  const volScalarField::Internal& expansionRate =
1384  expansionRates_[phases_[i].index()];
1385 
1387 
1388  if (i == 0)
1389  {
1390  tSp = negPart(expansionRate);
1391  }
1392  else
1393  {
1394  tSp = negPart(expansionRate)*vs_[i]/(vs_[i] - vs_[i - 1]);
1395  }
1396 
1397  if (i != nGroups() - 1)
1398  {
1399  tSp.ref() -= posPart(expansionRate)*vs_[i]/(vs_[i + 1] - vs_[i]);
1400  }
1401  else
1402  {
1403  tSp.ref() += posPart(expansionRate);
1404  }
1405 
1406  return tSp;
1407 }
1408 
1409 
1412 (
1413  const label i,
1414  const UPtrList<volScalarField>& flds
1415 ) const
1416 {
1417  Pair<tmp<volScalarField::Internal>> tRhoSus = modelSourceRhoSus(i, flds);
1418 
1419  const dimensionedScalar zeroSu
1420  (
1421  (flds.empty() ? dimless : flds[i].dimensions())/dimTime,
1422  scalar(0)
1423  );
1424 
1425  return
1426  tRhoSus.first().valid() && tRhoSus.second().valid()
1427  ? (tRhoSus.first() + tRhoSus.second())/phases_[i].rho()
1428  : tRhoSus.first().valid() ? tRhoSus.first()/phases_[i].rho()
1429  : tRhoSus.second().valid() ? tRhoSus.second()/phases_[i].rho()
1430  : volScalarField::Internal::New(zeroSu.name(), mesh(), zeroSu);
1431 }
1432 
1433 
1435 {
1436  if (solveOnFinalIterOnly() && !fluid_.pimple().finalIter())
1437  {
1438  return;
1439  }
1440 
1441  const label nCorr =
1442  solverDict().lookupBackwardsCompatible<label>({"nCorrectors", "nCorr"});
1443 
1444  const scalar tolerance = solverDict().lookup<scalar>("tolerance");
1445 
1446  const bool updateSrc = updateSources();
1447 
1448  if (nCorr > 0 && updateSrc)
1449  {
1450  precomputeCoalescenceAndBreakup();
1451  }
1452  precomputeExpansion();
1453  precomputeModelSources();
1454 
1455  int iCorr = 0;
1456  scalar maxInitialResidual = 1;
1457  while (++iCorr <= nCorr && maxInitialResidual > tolerance)
1458  {
1459  Info<< "populationBalance " << this->name()
1460  << ": Iteration " << iCorr << endl;
1461 
1462  if (updateSrc)
1463  {
1464  computeCoalescenceAndBreakup();
1465  }
1466  computeExpansion();
1467  computeModelSources();
1468 
1469  computeDilatationErrors();
1470 
1471  maxInitialResidual = 0;
1472 
1473  forAll(fs_, i)
1474  {
1475  volScalarField& fi = fs_[i];
1476  const phaseModel& phase = phases_[i];
1477  const volScalarField& alpha = phase;
1478  const volScalarField& rho = phase.rho();
1479 
1480  fvScalarMatrix fiEqn
1481  (
1482  fvm::ddt(alpha, fi)
1483  + fvm::div(phase.alphaPhi(), fi)
1484  + fvm::Sp(-(1 - small)*dilatationErrors_[phase.index()], fi)
1485  + fvm::SuSp(-small*dilatationErrors_[phase.index()], fi)
1486  ==
1487  Su_[i] + fvm::Sp(Sp_[i], fi)
1488  + expansionSu(i) + fvm::Sp(expansionSp(i), fi)
1489  + modelSourceSu(i)
1490  + fluid_.fvModels().source(alpha, rho, fi)/rho
1491  - correction
1492  (
1493  fvm::Sp
1494  (
1495  max(phase.residualAlpha() - alpha, scalar(0))
1496  /mesh().time().deltaT(),
1497  fi
1498  )
1499  )
1500  );
1501 
1502  fiEqn.relax();
1503 
1504  fluid_.fvConstraints().constrain(fiEqn);
1505 
1506  maxInitialResidual = max
1507  (
1508  fiEqn.solve().initialResidual(),
1509  maxInitialResidual
1510  );
1511 
1512  fluid_.fvConstraints().constrain(fi);
1513  }
1514  }
1515 
1516  const volScalarField alphaF0(phases_.first()*fs_.first());
1517  const volScalarField alphaFNm1(phases_.last()*fs_.last());
1518 
1519  Info<< "populationBalance " << this->name() << ": Group fraction "
1520  << "first/last = " << alphaF0.weightedAverage(mesh().V()).value()
1521  << '/' << alphaFNm1.weightedAverage(mesh().V()).value() << endl;
1522 
1523  if (solverDict().lookupOrDefault<Switch>("scale", true))
1524  {
1525  Info<< "populationBalance " << this->name()
1526  << ": Scaling group fractions " << endl;
1527 
1528  forAll(fs_, i)
1529  {
1530  fs_[i].max(0);
1531  }
1532 
1533  forAll(uniquePhases_, uniquePhasei)
1534  {
1535  const diameterModels::populationBalance& diameter =
1536  uniqueDiameters_[uniquePhasei];
1537 
1538  const volScalarField::Internal fSum(diameter.fSum());
1539 
1540  for (label i = diameter.iFirst(); i <= diameter.iLast(); ++ i)
1541  {
1542  fs_[i].internalFieldRef() /= fSum;
1543 
1544  fs_[i].correctBoundaryConditions();
1545  }
1546  }
1547  }
1548  else
1549  {
1550  forAll(uniquePhases_, uniquePhasei)
1551  {
1552  const diameterModels::populationBalance& diameter =
1553  uniqueDiameters_[uniquePhasei];
1554 
1555  const volScalarField::Internal fSum(diameter.fSum());
1556 
1557  Info<< diameter.phase().name()
1558  << ": Group fraction sum min/average/max = "
1559  << min(fSum).value() << '/'
1560  << fSum.weightedAverage(mesh().V()).value() << '/'
1561  << max(fSum).value() << endl;
1562  }
1563  }
1564 
1565  shapeModel_->solve();
1566 }
1567 
1568 
1570 {
1571  shapeModel_->correct();
1572 
1573  if (uniquePhases_.size() <= 1) return;
1574 
1575  // Calculate the total void fraction
1576  if (alphas_.empty())
1577  {
1578  alphas_.set
1579  (
1580  new volScalarField
1581  (
1582  IOobject
1583  (
1584  IOobject::groupName("alpha", this->name()),
1585  fluid_.time().name(),
1586  mesh_,
1589  ),
1590  mesh_,
1592  )
1593  );
1594  }
1595  else
1596  {
1597  alphas_() = Zero;
1598  }
1599 
1600  forAll(uniquePhases_, uniquePhasei)
1601  {
1602  alphas_() +=
1603  max
1604  (
1605  uniquePhases_[uniquePhasei],
1606  uniquePhases_[uniquePhasei].residualAlpha()
1607  );
1608  }
1609 
1610  // Calculate the Sauter mean diameter
1611  tmp<volScalarField> tInvDsm
1612  (
1614  (
1615  "invDsm",
1616  mesh_,
1618  )
1619  );
1620  volScalarField& invDsm = tInvDsm.ref();
1621 
1622  forAll(uniquePhases_, uniquePhasei)
1623  {
1624  invDsm +=
1625  max
1626  (
1627  uniquePhases_[uniquePhasei],
1628  uniquePhases_[uniquePhasei].residualAlpha()
1629  )
1630  /alphas_()
1631  /uniquePhases_[uniquePhasei].d();
1632  }
1633 
1634  if (dsm_.empty())
1635  {
1636  dsm_.set
1637  (
1638  new volScalarField
1639  (
1640  IOobject
1641  (
1642  IOobject::groupName("d", this->name()),
1643  fluid_.time().name(),
1644  mesh_,
1647  ),
1648  1/tInvDsm
1649  )
1650  );
1651  }
1652  else
1653  {
1654  dsm_() = 1/tInvDsm;
1655  }
1656 
1657  // Calculate the average velocity
1658  if (U_.empty())
1659  {
1660  U_.set
1661  (
1662  new volVectorField
1663  (
1664  IOobject
1665  (
1666  IOobject::groupName("U", this->name()),
1667  fluid_.time().name(),
1668  mesh_,
1671  ),
1672  mesh_,
1674  )
1675  );
1676  }
1677  else
1678  {
1679  U_() = Zero;
1680  }
1681 
1682  forAll(uniquePhases_, uniquePhasei)
1683  {
1684  U_() +=
1685  max
1686  (
1687  uniquePhases_[uniquePhasei],
1688  uniquePhases_[uniquePhasei].residualAlpha()
1689  )
1690  /alphas_()
1691  *uniquePhases_[uniquePhasei].U();
1692  }
1693 }
1694 
1695 
1697 {
1698  return os.good();
1699 }
1700 
1701 
1702 // ************************************************************************* //
label k
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:474
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:492
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
static tmp< DimensionedField< Type, GeoMesh, PrimitiveField > > New(const word &name, const GeoMesh &mesh, const dimensionSet &, const PrimitiveField< Type > &)
Return a temporary field constructed from name, mesh,.
dimensioned< Type > weightedAverage(const DimensionedField< scalar, GeoMesh, PrimitiveField2 > &) const
Calculate and return weighted average.
const GeoMesh & mesh() const
Return mesh.
Generic GeometricField class.
DimensionedField< Type, GeoMesh, PrimitiveField > Internal
Type of the internal field from which this GeometricField is derived.
static tmp< GeometricField< Type, GeoMesh, PrimitiveField > > New(const word &name, const Internal &, const PtrList< Patch > &, const HashPtrTable< Source > &=HashPtrTable< Source >())
Return a temporary field constructed from name,.
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
const Time & time() const
Return time.
Definition: IOobject.C:315
readOption
Enumeration defining the read options.
Definition: IOobject.H:117
bool & registerObject()
Register object created from this IOobject with registry if true.
Definition: IOobject.H:343
IOobject(const word &name, const fileName &instance, const objectRegistry &registry, readOption r=NO_READ, writeOption w=NO_WRITE, bool registerObject=true)
Construct from name, instance, registry, io options.
Definition: IOobject.C:164
const word & name() const
Return name.
Definition: IOobject.H:307
static word groupName(Name name, const word &group)
writeOption
Enumeration defining the write options.
Definition: IOobject.H:126
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:333
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
An ordered pair of two objects of type <Type> with first() and second() elements.
Definition: Pair.H:67
const Type & second() const
Return second.
Definition: PairI.H:121
const Type & first() const
Return first.
Definition: PairI.H:107
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
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
bool empty() const
Return true if the UPtrList is empty (ie, size() is zero)
Definition: UPtrListI.H:36
const phaseModel & phase() const
Return the phase.
This diameter model computes the diameter from multiple size group diameters and fractions provided b...
label iLast() const
Return the index of the last group of this phase.
label iFirst() const
Return the index of the first group of this phase.
label nGroups() const
Return the number of groups in this phase.
const word & popBalName() const
Return name of populationBalance this populationBalance belongs to.
tmp< volScalarField::Internal > fSum() const
...
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:669
const dictionary & subDict(const word &) const
Find and return a sub-dictionary.
Definition: dictionary.C:778
ITstream & lookupBackwardsCompatible(const wordList &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream, trying a list of keywords.
Definition: dictionary.C:680
const dictionary & optionalTypeDict(const word &typeName) const
Find and return an optional type sub-dictionary.
Definition: dictionary.C:921
const word & name() const
Return const reference to name.
Base class for statistical distributions.
Definition: distribution.H:76
bool constrain(fvMatrix< Type > &eqn) const
Apply constraints to an equation.
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvMatrix.H:118
void relax(const scalar alpha)
Relax matrix (for steady-state solution).
Definition: fvMatrix.C:602
SolverPerformance< Type > solve(const dictionary &)
Solve segregated or coupled returning the solution statistics.
Definition: fvMatrixSolve.C:58
tmp< fvMatrix< Type > > source(const VolField< Type > &field) const
Return source for an equation.
const Type & lookupType(const word &group=word::null) const
Lookup and return the object of the given Type.
static autoPtr< oneDimensionalDiscretisation > New(const word &name, const dimensionSet &dims, const label n, const dictionary &dict)
Selector.
Templated abstract base class for multiphase compressible momentum transport models.
Class to represent an interface between phases. Derivations can further specify the configuration of ...
virtual word name() const
Name.
tmp< volScalarField > sigma() const
Surface tension coefficient.
const dimensionedScalar & residualAlpha() const
Return the residual phase-fraction for given phase.
Definition: phaseModel.C:116
const diameterModel & diameter() const
Return a reference to the diameterModel of the phase.
Definition: phaseModel.C:134
label index() const
Return the index of the phase.
Definition: phaseModel.C:104
const word & name() const
Return the name of this phase.
Definition: phaseModel.C:92
virtual const volScalarField & rho() const =0
Return the density field.
virtual tmp< surfaceScalarField > alphaPhi() const =0
Return the volumetric flux of the phase.
Class to represent a system of phases.
Definition: phaseSystem.H:74
Foam::fvModels & fvModels(fvMesh &mesh)
Access the fvModels.
Definition: phaseSystemI.H:226
Foam::fvConstraints & fvConstraints(fvMesh &mesh)
Access the fvConstraints.
Definition: phaseSystemI.H:238
const phaseModelList & phases() const
Return the phase models.
Definition: phaseSystemI.H:104
const pimpleNoLoopControl & pimple() const
Return pimpleNoLoopControl.
Definition: phaseSystemI.H:97
bool finalIter() const
Flag to indicate the final iteration.
Model for tracking the evolution of a dispersed phase size distribution due to coalescence (synonymou...
tmp< volScalarField::Internal > Sp(const label i) const
Return the implicit coalescence and breakup source term.
const dimensionedScalar & v(const label i) const
Access the representative volumes diameters of a group.
const PtrList< dimensionedScalar > & dSphs() const
Access the representative spherical diameters of the groups.
const phaseSystem & fluid() const
Return reference to the phaseSystem.
bool writeData(Ostream &) const
Dummy write for regIOobject.
void correct()
Correct derived quantities.
bool solveOnFinalIterOnly() const
Solve on final pimple iteration only.
populationBalanceModel(const phaseSystem &fluid, const word &name)
Construct for a fluid and with a given name.
tmp< volScalarField::Internal > expansionSp(const label i) const
Return the implicit expansion source term.
tmp< volScalarField::Internal > expansionSu(const label i, const UPtrList< volScalarField > &flds=UPtrList< volScalarField >()) const
Return the explicit expansion source term.
dimensionedScalar eta(const label i, const dimensionedScalar &v) const
Return the number allocation coefficient for a single volume.
label nGroups() const
Return the number of groups in the population balance.
tmp< volScalarField::Internal > modelSourceSu(const label i, const UPtrList< volScalarField > &flds=UPtrList< volScalarField >()) const
Return the explicit model source source term.
static tmp< volScalarField > groupField(const word &name, const label i, const phaseModel &phase)
Read and return a group-associated field.
dimensionedScalar etaV(const label i, const dimensionedScalar &v) const
Return the volume allocation coefficient for a single volume.
const populationBalance::shapeModel & shape() const
Access the shape model.
static IOobject groupFieldIo(const word &name, const label i, const phaseModel &phase, const IOobject::readOption r=IOobject::NO_READ, const IOobject::writeOption w=IOobject::NO_WRITE, const bool registerObject=true)
Return IO for a group-associated field.
const tmp< volScalarField > sigmaWithContinuousPhase(const phaseModel &dispersedPhase) const
Return the surface tension coefficient between a given dispersed.
const fvMesh & mesh() const
Return reference to the mesh.
tmp< volScalarField > d(const label i) const
Return the representative diameter of the group.
const phaseCompressibleMomentumTransportModel & continuousTurbulence() const
Return reference to momentumTransport model of the continuous phase.
tmp< volScalarField > a(const label i) const
Return the representative surface area of the group.
virtual ~populationBalanceModel()
Destructor.
void solve()
Solve the population balance equation.
const dictionary & solverDict() const
Return solution settings dictionary for this populationBalance.
Base class for modelling the shape of the particles belonging to a size class through alternative dia...
Definition: shapeModel.H:59
Enables the printing of a dictionary and subsequently looked-up defaulted entries.
regIOobject is an abstract class derived from IOobject to handle automatic object registration with t...
Definition: regIOobject.H:55
A class for managing temporary objects.
Definition: tmp.H:55
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:197
Templated form of IOobject providing type information for file reading and header type checking.
Definition: IOobject.H:545
bool headerOk()
Read header (uses typeGlobalFile to find file) and check.
Template function which returns the un-mangled name of a given type. Useful for types which do not ha...
A class for handling words, derived from string.
Definition: word.H:63
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
Calculate the first temporal derivative.
Calculate the divergence of the given field.
Calculate the matrix for the first temporal derivative.
Calculate the matrix for the divergence of the given field and flux.
Calculate the matrix for implicit and explicit sources.
rho
Definition: pEqn.H:1
volScalarField alpha(IOobject("alpha", runTime.name(), mesh, IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE), lambda *max(Ua &U, zeroSensitivity))
const dimensionSet time
const dimensionSet rate
tmp< VolField< Type > > ddt(const dimensioned< Type > dt, const fvMesh &mesh)
Definition: fvcDdt.C:45
tmp< VolField< Type > > Su(const VolField< Type > &su, const VolField< Type > &vf)
Definition: fvcSup.C:44
tmp< VolField< Type > > div(const SurfaceField< Type > &ssf)
Definition: fvcDiv.C:47
tmp< fvMatrix< Type > > S(const Pair< tmp< volScalarField::Internal >> &, const VolField< Type > &)
tmp< fvMatrix< Type > > div(const surfaceScalarField &flux, const VolField< Type > &vf, const word &name)
Definition: fvmDiv.C:48
tmp< fvMatrix< Type > > Sp(const volScalarField::Internal &, const VolField< Type > &)
tmp< fvMatrix< Type > > SuSp(const volScalarField::Internal &, const VolField< Type > &)
tmp< fvMatrix< Type > > ddt(const VolField< Type > &vf)
Definition: fvmDdt.C:46
const unitSet & lookup(const word &unitName)
Lookup and return the named unit from the table.
Definition: units.C:346
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static const zero Zero
Definition: zero.H:97
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
const dimensionSet & dimless
Definition: dimensions.C:138
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:272
dimensionedScalar sign(const dimensionedScalar &ds)
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
void addField(const label phasei, const word &name, tmp< GeoField > field, PtrList< GeoField > &fieldList)
Definition: phaseSystemI.H:265
const dimensionSet & dimLength
Definition: dimensions.C:141
dimensioned< Type > average(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
messageStream Info
const dimensionSet & dimVolume
Definition: dimensions.C:150
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:265
dimensionedScalar negPart(const dimensionedScalar &ds)
const dimensionSet & dimVelocity
Definition: dimensions.C:154
void inv(pointPatchField< tensor > &, const pointPatchField< tensor > &)
const dimensionSet & dimTime
Definition: dimensions.C:142
VolField< scalar > volScalarField
Definition: volFieldsFwd.H:62
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
const dimensionSet & dimDensity
Definition: dimensions.C:158
word typedName(Name name)
Return the name of the object within the given type.
Definition: typeInfo.H:188
tmp< fvMatrix< Type > > correction(const fvMatrix< Type > &)
Return the correction form of the given matrix.
void pow3(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
Ostream & indentOrNl(Ostream &os)
Indent stream or add newline if indent level == 0.
Definition: Ostream.H:250
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
error FatalError
defineTypeNameAndDebug(atmosphericBoundaryLayer, 0)
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:243
tmp< DimensionedField< TypeR, GeoMesh, Field > > New(const tmp< DimensionedField< TypeR, GeoMesh, Field >> &tdf1, const word &name, const dimensionSet &dimensions)
dimensionedScalar posPart(const dimensionedScalar &ds)
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.