populationBalanceModel.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) 2017-2019 OpenFOAM Foundation
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "populationBalanceModel.H"
29 #include "coalescenceModel.H"
30 #include "breakupModel.H"
31 #include "binaryBreakupModel.H"
32 #include "driftModel.H"
33 #include "nucleationModel.H"
34 #include "phaseSystem.H"
35 #include "surfaceTensionModel.H"
36 #include "fvmDdt.H"
37 #include "fvcDdt.H"
38 #include "fvmSup.H"
39 #include "fvcSup.H"
40 #include "fvcDiv.H"
41 #include "phaseCompressibleTurbulenceModel.H"
42 
43 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
44 
45 void Foam::diameterModels::populationBalanceModel::registerVelocityGroups()
46 {
47  forAll(fluid_.phases(), phasei)
48  {
49  if (isA<velocityGroup>(fluid_.phases()[phasei].dPtr()()))
50  {
51  const velocityGroup& velGroup =
52  refCast<const velocityGroup>(fluid_.phases()[phasei].dPtr()());
53 
54  if (velGroup.popBalName() == this->name())
55  {
56  velocityGroups_.resize(velocityGroups_.size() + 1);
57 
58  velocityGroups_.set
59  (
60  velocityGroups_.size() - 1,
61  &const_cast<velocityGroup&>(velGroup)
62  );
63 
64  forAll(velGroup.sizeGroups(), i)
65  {
66  this->registerSizeGroups
67  (
68  const_cast<sizeGroup&>(velGroup.sizeGroups()[i])
69  );
70  }
71  }
72  }
73  }
74 }
75 
76 
77 void Foam::diameterModels::populationBalanceModel::registerSizeGroups
78 (
79  sizeGroup& group
80 )
81 {
82  if
83  (
84  sizeGroups_.size() != 0
85  &&
86  group.x().value() <= sizeGroups_.last().x().value()
87  )
88  {
90  << "Size groups must be entered according to their representative"
91  << " size"
92  << exit(FatalError);
93  }
94 
95  sizeGroups_.resize(sizeGroups_.size() + 1);
96  sizeGroups_.set(sizeGroups_.size() - 1, &group);
97 
98  // Grid generation over property space
99  if (sizeGroups_.size() == 1)
100  {
101  // Set the first sizeGroup boundary to the representative value of
102  // the first sizeGroup
103  v_.append
104  (
106  (
107  "v",
108  sizeGroups_.last().x()
109  )
110  );
111 
112  // Set the last sizeGroup boundary to the representative size of the
113  // last sizeGroup
114  v_.append
115  (
117  (
118  "v",
119  sizeGroups_.last().x()
120  )
121  );
122  }
123  else
124  {
125  // Overwrite the next-to-last sizeGroup boundary to lie halfway between
126  // the last two sizeGroups
127  v_.last() =
128  0.5
129  *(
130  sizeGroups_[sizeGroups_.size()-2].x()
131  + sizeGroups_.last().x()
132  );
133 
134  // Set the last sizeGroup boundary to the representative size of the
135  // last sizeGroup
136  v_.append
137  (
139  (
140  "v",
141  sizeGroups_.last().x()
142  )
143  );
144  }
145 
146  delta_.append(new PtrList<dimensionedScalar>());
147 
148  Su_.append
149  (
150  new volScalarField
151  (
152  IOobject
153  (
154  "Su",
155  fluid_.time().timeName(),
156  mesh_
157  ),
158  mesh_,
159  dimensionedScalar("zero", inv(dimTime), 0)
160  )
161  );
162 
163  SuSp_.append
164  (
165  new volScalarField
166  (
167  IOobject
168  (
169  "SuSp",
170  fluid_.time().timeName(),
171  mesh_
172  ),
173  mesh_,
174  dimensionedScalar("zero", inv(dimTime), 0)
175  )
176  );
177 }
178 
179 
180 void Foam::diameterModels::populationBalanceModel::createPhasePairs()
181 {
182  forAll(velocityGroups_, i)
183  {
184  const phaseModel& phasei = velocityGroups_[i].phase();
185 
186  forAll(velocityGroups_, j)
187  {
188  const phaseModel& phasej = velocityGroups_[j].phase();
189 
190  if (&phasei != &phasej)
191  {
192  const phasePairKey key
193  (
194  phasei.name(),
195  phasej.name(),
196  false
197  );
198 
199  if (!phasePairs_.found(key))
200  {
201  phasePairs_.insert
202  (
203  key,
204  autoPtr<phasePair>
205  (
206  new phasePair
207  (
208  phasei,
209  phasej
210  )
211  )
212  );
213  }
214  }
215  }
216  }
217 }
218 
219 
220 void Foam::diameterModels::populationBalanceModel::correct()
221 {
222  calcDeltas();
223 
224  forAll(velocityGroups_, v)
225  {
226  velocityGroups_[v].preSolve();
227  }
228 
229  forAll(coalescence_, model)
230  {
231  coalescence_[model].correct();
232  }
233 
234  forAll(breakup_, model)
235  {
236  breakup_[model].correct();
237 
238  breakup_[model].dsdPtr()().correct();
239  }
240 
241  forAll(binaryBreakup_, model)
242  {
243  binaryBreakup_[model].correct();
244  }
245 
246  forAll(drift_, model)
247  {
248  drift_[model].correct();
249  }
250 
251  forAll(nucleation_, model)
252  {
253  nucleation_[model].correct();
254  }
255 }
256 
257 
258 void Foam::diameterModels::populationBalanceModel::
259 birthByCoalescence
260 (
261  const label j,
262  const label k
263 )
264 {
265  const sizeGroup& fj = sizeGroups_[j];
266  const sizeGroup& fk = sizeGroups_[k];
267 
268  dimensionedScalar Gamma;
269  dimensionedScalar v = fj.x() + fk.x();
270 
271  for (label i = j; i < sizeGroups_.size(); i++)
272  {
273  // Calculate fraction for intra-interval events
274  Gamma = gamma(i, v);
275 
276  if (Gamma.value() == 0) continue;
277 
278  const sizeGroup& fi = sizeGroups_[i];
279 
280  // Avoid double counting of events
281  if (j == k)
282  {
283  Sui_ =
284  0.5*fi.x()*coalescenceRate_()*Gamma
285  *fj*fj.phase()/fj.x()
286  *fk*fk.phase()/fk.x();
287  }
288  else
289  {
290  Sui_ =
291  fi.x()*coalescenceRate_()*Gamma
292  *fj*fj.phase()/fj.x()
293  *fk*fk.phase()/fk.x();
294  }
295 
296  Su_[i] += Sui_;
297 
298  const phasePairKey pairij
299  (
300  fi.phase().name(),
301  fj.phase().name()
302  );
303 
304  if (pDmdt_.found(pairij))
305  {
306  const scalar dmdtSign
307  (
308  Pair<word>::compare(pDmdt_.find(pairij).key(), pairij)
309  );
310 
311  pDmdt_[pairij]->ref() += dmdtSign*fj.x()/v*Sui_*fi.phase().rho();
312  }
313 
314  const phasePairKey pairik
315  (
316  fi.phase().name(),
317  fk.phase().name()
318  );
319 
320  if (pDmdt_.found(pairik))
321  {
322  const scalar dmdtSign
323  (
324  Pair<word>::compare(pDmdt_.find(pairik).key(), pairik)
325  );
326 
327  pDmdt_[pairik]->ref() += dmdtSign*fk.x()/v*Sui_*fi.phase().rho();
328  }
329  }
330 }
331 
332 
333 void Foam::diameterModels::populationBalanceModel::
334 deathByCoalescence
335 (
336  const label i,
337  const label j
338 )
339 {
340  const sizeGroup& fi = sizeGroups_[i];
341  const sizeGroup& fj = sizeGroups_[j];
342 
343  SuSp_[i] += coalescenceRate_()*fi.phase()*fj*fj.phase()/fj.x();
344 
345  if (i != j)
346  {
347  SuSp_[j] += coalescenceRate_()*fj.phase()*fi*fi.phase()/fi.x();
348  }
349 }
350 
351 
352 void Foam::diameterModels::populationBalanceModel::
353 birthByBreakup
354 (
355  const label k,
356  const label model
357 )
358 {
359  const sizeGroup& fk = sizeGroups_[k];
360 
361  for (label i = 0; i <= k; i++)
362  {
363  const sizeGroup& fi = sizeGroups_[i];
364 
365  Sui_ =
366  fi.x()*breakupRate_()*breakup_[model].dsdPtr()().nik(i, k)
367  *fk*fk.phase()/fk.x();
368 
369  Su_[i] += Sui_;
370 
371  const phasePairKey pair
372  (
373  fi.phase().name(),
374  fk.phase().name()
375  );
376 
377  if (pDmdt_.found(pair))
378  {
379  const scalar dmdtSign
380  (
381  Pair<word>::compare(pDmdt_.find(pair).key(), pair)
382  );
383 
384  pDmdt_[pair]->ref() += dmdtSign*Sui_*fi.phase().rho();
385  }
386  }
387 }
388 
389 
390 void Foam::diameterModels::populationBalanceModel::deathByBreakup(const label i)
391 {
392  const sizeGroup& fi = sizeGroups_[i];
393 
394  SuSp_[i] += breakupRate_()*fi.phase();
395 }
396 
397 
398 void Foam::diameterModels::populationBalanceModel::calcDeltas()
399 {
400  forAll(sizeGroups_, i)
401  {
402  if (delta_[i].empty())
403  {
404  for (label j = 0; j <= sizeGroups_.size() - 1; j++)
405  {
406  delta_[i].append
407  (
409  (
410  "delta",
411  dimVolume,
412  v_[i+1].value() - v_[i].value()
413  )
414  );
415 
416  if
417  (
418  v_[i].value() < 0.5*sizeGroups_[j].x().value()
419  &&
420  0.5*sizeGroups_[j].x().value() < v_[i+1].value()
421  )
422  {
423  delta_[i][j] = mag(0.5*sizeGroups_[j].x() - v_[i]);
424  }
425  else if (0.5*sizeGroups_[j].x().value() <= v_[i].value())
426  {
427  delta_[i][j].value() = 0;
428  }
429  }
430  }
431  }
432 }
433 
434 
435 void Foam::diameterModels::populationBalanceModel::
436 birthByBinaryBreakup
437 (
438  const label i,
439  const label j
440 )
441 {
442  const sizeGroup& fj = sizeGroups_[j];
443  const sizeGroup& fi = sizeGroups_[i];
444 
445  Sui_ = fi.x()*binaryBreakupRate_()*delta_[i][j]*fj*fj.phase()/fj.x();
446 
447  Su_[i] += Sui_;
448 
449  const phasePairKey pairij
450  (
451  fi.phase().name(),
452  fj.phase().name()
453  );
454 
455  if (pDmdt_.found(pairij))
456  {
457  const scalar dmdtSign
458  (
459  Pair<word>::compare(pDmdt_.find(pairij).key(), pairij)
460  );
461 
462  pDmdt_[pairij]->ref() += dmdtSign*Sui_*fi.phase().rho();
463  }
464 
465  dimensionedScalar Gamma;
466  dimensionedScalar v = fj.x() - fi.x();
467 
468  for (label k = 0; k <= j; k++)
469  {
470  // Calculate fraction for intra-interval events
471  Gamma = gamma(k, v);
472 
473  if (Gamma.value() == 0) continue;
474 
475  const sizeGroup& fk = sizeGroups_[k];
476 
477  volScalarField& Suk = Sui_;
478 
479  Suk =
480  sizeGroups_[k].x()*binaryBreakupRate_()*delta_[i][j]*Gamma
481  *fj*fj.phase()/fj.x();
482 
483  Su_[k] += Suk;
484 
485  const phasePairKey pairkj
486  (
487  fk.phase().name(),
488  fj.phase().name()
489  );
490 
491  if (pDmdt_.found(pairkj))
492  {
493  const scalar dmdtSign
494  (
496  (
497  pDmdt_.find(pairkj).key(),
498  pairkj
499  )
500  );
501 
502  pDmdt_[pairkj]->ref() += dmdtSign*Suk*fi.phase().rho();
503  }
504  }
505 }
506 
507 
508 void Foam::diameterModels::populationBalanceModel::
509 deathByBinaryBreakup
510 (
511  const label j,
512  const label i
513 )
514 {
515  const volScalarField& alphai = sizeGroups_[i].phase();
516 
517  SuSp_[i] += alphai*binaryBreakupRate_()*delta_[j][i];
518 }
519 
520 
521 void Foam::diameterModels::populationBalanceModel::drift(const label i)
522 {
523  const sizeGroup& fp = sizeGroups_[i];
524 
525  if (i == 0)
526  {
527  rx_() = pos(driftRate_())*sizeGroups_[i+1].x()/sizeGroups_[i].x();
528  }
529  else if (i == sizeGroups_.size() - 1)
530  {
531  rx_() = neg(driftRate_())*sizeGroups_[i-1].x()/sizeGroups_[i].x();
532  }
533  else
534  {
535  rx_() =
536  pos(driftRate_())*sizeGroups_[i+1].x()/sizeGroups_[i].x()
537  + neg(driftRate_())*sizeGroups_[i-1].x()/sizeGroups_[i].x();
538  }
539 
540  SuSp_[i] +=
541  (neg(1 - rx_()) + neg(rx_() - rx_()/(1 - rx_())))*driftRate_()
542  *fp.phase()/((rx_() - 1)*fp.x());
543 
544  rx_() *= 0.0;
545  rdx_() *= 0.0;
546 
547  if (i == sizeGroups_.size() - 2)
548  {
549  rx_() = pos(driftRate_())*sizeGroups_[i+1].x()/sizeGroups_[i].x();
550 
551  rdx_() =
552  pos(driftRate_())
553  *(sizeGroups_[i+1].x() - sizeGroups_[i].x())
554  /(sizeGroups_[i].x() - sizeGroups_[i-1].x());
555  }
556  else if (i < sizeGroups_.size() - 2)
557  {
558  rx_() = pos(driftRate_())*sizeGroups_[i+2].x()/sizeGroups_[i+1].x();
559 
560  rdx_() =
561  pos(driftRate_())
562  *(sizeGroups_[i+2].x() - sizeGroups_[i+1].x())
563  /(sizeGroups_[i+1].x() - sizeGroups_[i].x());
564  }
565 
566  if (i == 1)
567  {
568  rx_() += neg(driftRate_())*sizeGroups_[i-1].x()/sizeGroups_[i].x();
569 
570  rdx_() +=
571  neg(driftRate_())
572  *(sizeGroups_[i].x() - sizeGroups_[i-1].x())
573  /(sizeGroups_[i+1].x() - sizeGroups_[i].x());
574  }
575  else if (i > 1)
576  {
577  rx_() += neg(driftRate_())*sizeGroups_[i-2].x()/sizeGroups_[i-1].x();
578 
579  rdx_() +=
580  neg(driftRate_())
581  *(sizeGroups_[i-1].x() - sizeGroups_[i-2].x())
582  /(sizeGroups_[i].x() - sizeGroups_[i-1].x());
583  }
584 
585  if (i != sizeGroups_.size() - 1)
586  {
587  const sizeGroup& fe = sizeGroups_[i+1];
588  volScalarField& Sue = Sui_;
589 
590  Sue =
591  pos(driftRate_())*driftRate_()*rdx_()
592  *fp*fp.phase()/fp.x()
593  /(rx_() - 1);
594 
595  Su_[i+1] += Sue;
596 
597  const phasePairKey pairij
598  (
599  fp.phase().name(),
600  fe.phase().name()
601  );
602 
603  if (pDmdt_.found(pairij))
604  {
605  const scalar dmdtSign
606  (
607  Pair<word>::compare(pDmdt_.find(pairij).key(), pairij)
608  );
609 
610  pDmdt_[pairij]->ref() -= dmdtSign*Sue*fp.phase().rho();
611  }
612  }
613 
614  if (i != 0)
615  {
616  const sizeGroup& fw = sizeGroups_[i-1];
617  volScalarField& Suw = Sui_;
618 
619  Suw =
620  neg(driftRate_())*driftRate_()*rdx_()
621  *fp*fp.phase()/fp.x()
622  /(rx_() - 1);
623 
624  Su_[i-1] += Suw;
625 
626  const phasePairKey pairih
627  (
628  fp.phase().name(),
629  fw.phase().name()
630  );
631 
632  if (pDmdt_.found(pairih))
633  {
634  const scalar dmdtSign
635  (
636  Pair<word>::compare(pDmdt_.find(pairih).key(), pairih)
637  );
638 
639  pDmdt_[pairih]->ref() -= dmdtSign*Suw*fp.phase().rho();
640  }
641  }
642 }
643 
644 
645 void Foam::diameterModels::populationBalanceModel::nucleation(const label i)
646 {
647  Su_[i] += sizeGroups_[i].x()*nucleationRate_();
648 }
649 
650 
651 void Foam::diameterModels::populationBalanceModel::sources()
652 {
653  forAll(sizeGroups_, i)
654  {
655  Su_[i] *= 0.0;
656  SuSp_[i] *= 0.0;
657  }
658 
660  (
661  phasePairTable,
662  phasePairs(),
663  phasePairIter
664  )
665  {
666  pDmdt_(phasePairIter())->ref() *= 0.0;
667  }
668 
669  // Since the calculation of the rates is computationally expensive,
670  // they are calculated once for each sizeGroup pair and inserted into source
671  // terms as required
672  forAll(sizeGroups_, i)
673  {
674  const sizeGroup& fi = sizeGroups_[i];
675 
676  if (coalescence_.size() != 0)
677  {
678  for (label j = 0; j <= i; j++)
679  {
680  const sizeGroup& fj = sizeGroups_[j];
681 
682  if (fi.x() + fj.x() > sizeGroups_.last().x()) break;
683 
684  coalescenceRate_() *= 0.0;
685 
686  forAll(coalescence_, model)
687  {
688  coalescence_[model].addToCoalescenceRate
689  (
690  coalescenceRate_(),
691  i,
692  j
693  );
694  }
695 
696  birthByCoalescence(i, j);
697 
698  deathByCoalescence(i, j);
699  }
700  }
701 
702  if (breakup_.size() != 0)
703  {
704  forAll(breakup_, model)
705  {
706  breakup_[model].setBreakupRate(breakupRate_(), i);
707 
708  birthByBreakup(i, model);
709 
710  deathByBreakup(i);
711  }
712  }
713 
714  if (binaryBreakup_.size() != 0)
715  {
716  label j = 0;
717 
718  while (delta_[j][i].value() != 0)
719  {
720  binaryBreakupRate_() *= 0.0;
721 
722  forAll(binaryBreakup_, model)
723  {
724  binaryBreakup_[model].addToBinaryBreakupRate
725  (
726  binaryBreakupRate_(),
727  j,
728  i
729  );
730  }
731 
732  birthByBinaryBreakup(j, i);
733 
734  deathByBinaryBreakup(j, i);
735 
736  j++;
737  }
738  }
739 
740  if (drift_.size() != 0)
741  {
742  driftRate_() *= 0.0;
743 
744  forAll(drift_, model)
745  {
746  drift_[model].addToDriftRate(driftRate_(), i);
747  }
748 
749  drift(i);
750  }
751 
752  if (nucleation_.size() != 0)
753  {
754  nucleationRate_() *= 0.0;
755 
756  forAll(nucleation_, model)
757  {
758  nucleation_[model].addToNucleationRate(nucleationRate_(), i);
759  }
760 
761  nucleation(i);
762  }
763  }
764 }
765 
766 
767 void Foam::diameterModels::populationBalanceModel::dmdt()
768 {
769  forAll(velocityGroups_, v)
770  {
771  velocityGroup& velGroup = velocityGroups_[v];
772 
773  velGroup.dmdtRef() *= 0.0;
774 
775  forAll(sizeGroups_, i)
776  {
777  if (&sizeGroups_[i].phase() == &velGroup.phase())
778  {
779  sizeGroup& fi = sizeGroups_[i];
780 
781  velGroup.dmdtRef() += fi.phase().rho()*(Su_[i] - SuSp_[i]*fi);
782  }
783  }
784  }
785 }
786 
787 
788 void Foam::diameterModels::populationBalanceModel::calcAlphas()
789 {
790  alphas_() *= 0.0;
791 
792  forAll(velocityGroups_, v)
793  {
794  const phaseModel& phase = velocityGroups_[v].phase();
795 
796  alphas_() += max(phase, phase.residualAlpha());
797  }
798 }
799 
800 
802 Foam::diameterModels::populationBalanceModel::calcDsm()
803 {
804  tmp<volScalarField> tInvDsm
805  (
807  (
808  "invDsm",
809  mesh_,
811  )
812  );
813 
814  volScalarField& invDsm = tInvDsm.ref();
815 
816  forAll(velocityGroups_, v)
817  {
818  const phaseModel& phase = velocityGroups_[v].phase();
819 
820  invDsm += max(phase, phase.residualAlpha())/(phase.d()*alphas_());
821  }
822 
823  return 1.0/tInvDsm;
824 }
825 
826 
827 void Foam::diameterModels::populationBalanceModel::calcVelocity()
828 {
829  U_() *= 0.0;
830 
831  forAll(velocityGroups_, v)
832  {
833  const phaseModel& phase = velocityGroups_[v].phase();
834 
835  U_() += max(phase, phase.residualAlpha())*phase.U()/alphas_();
836  }
837 }
838 
839 bool Foam::diameterModels::populationBalanceModel::updateSources()
840 {
841  const bool result = sourceUpdateCounter_ % sourceUpdateInterval() == 0;
842 
843  ++ sourceUpdateCounter_;
844 
845  return result;
846 }
847 
848 
849 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
850 
852 (
853  const phaseSystem& fluid,
854  const word& name,
856 )
857 :
859  (
860  IOobject
861  (
862  name,
863  fluid.time().constant(),
864  fluid.mesh()
865  )
866  ),
867  fluid_(fluid),
868  pDmdt_(pDmdt),
869  mesh_(fluid_.mesh()),
870  name_(name),
871  dict_
872  (
873  fluid_.subDict("populationBalanceCoeffs").subDict(name_)
874  ),
875  pimple_(mesh_.lookupObject<pimpleControl>("solutionControl")),
876  continuousPhase_
877  (
878  mesh_.lookupObject<phaseModel>
879  (
880  IOobject::groupName("alpha", dict_.get<word>("continuousPhase"))
881  )
882  ),
883  velocityGroups_(),
884  sizeGroups_(),
885  v_(),
886  delta_(),
887  Su_(),
888  SuSp_(),
889  Sui_
890  (
891  IOobject
892  (
893  "Sui",
894  mesh_.time().timeName(),
895  mesh_
896  ),
897  mesh_,
898  dimensionedScalar("zero", inv(dimTime), Zero)
899  ),
900  coalescence_
901  (
902  dict_.lookup("coalescenceModels"),
904  ),
905  coalescenceRate_(),
906  breakup_
907  (
908  dict_.lookup("breakupModels"),
909  breakupModel::iNew(*this)
910  ),
911  breakupRate_(),
912  binaryBreakup_
913  (
914  dict_.lookup("binaryBreakupModels"),
916  ),
917  binaryBreakupRate_(),
918  drift_
919  (
920  dict_.lookup("driftModels"),
921  driftModel::iNew(*this)
922  ),
923  driftRate_(),
924  rx_(),
925  rdx_(),
926  nucleation_
927  (
928  dict_.lookup("nucleationModels"),
929  nucleationModel::iNew(*this)
930  ),
931  nucleationRate_(),
932  alphas_(),
933  dsm_(),
934  U_(),
935  sourceUpdateCounter_
936  (
937  (mesh_.time().timeIndex()*nCorr())%sourceUpdateInterval()
938  )
939 {
940  this->registerVelocityGroups();
941 
942  this->createPhasePairs();
943 
944  if (sizeGroups_.size() < 3)
945  {
947  << "The populationBalance " << name_
948  << " requires a minimum number of three sizeGroups to be specified."
949  << exit(FatalError);
950  }
951 
952 
953  if (coalescence_.size() != 0)
954  {
955  coalescenceRate_.set
956  (
957  new volScalarField
958  (
959  IOobject
960  (
961  "coalescenceRate",
962  mesh_.time().timeName(),
963  mesh_
964  ),
965  mesh_,
967  )
968  );
969  }
970 
971  if (breakup_.size() != 0)
972  {
973  breakupRate_.set
974  (
975  new volScalarField
976  (
977  IOobject
978  (
979  "breakupRate",
980  fluid_.time().timeName(),
981  mesh_
982  ),
983  mesh_,
984  dimensionedScalar("zero", inv(dimTime), Zero)
985  )
986  );
987  }
988 
989  if (binaryBreakup_.size() != 0)
990  {
991  binaryBreakupRate_.set
992  (
993  new volScalarField
994  (
995  IOobject
996  (
997  "binaryBreakupRate",
998  fluid_.time().timeName(),
999  mesh_
1000  ),
1001  mesh_,
1003  (
1004  "binaryBreakupRate",
1006  Zero
1007  )
1008  )
1009  );
1010  }
1011 
1012  if (drift_.size() != 0)
1013  {
1014  driftRate_.set
1015  (
1016  new volScalarField
1017  (
1018  IOobject
1019  (
1020  "driftRate",
1021  fluid_.time().timeName(),
1022  mesh_
1023  ),
1024  mesh_,
1026  )
1027  );
1028 
1029  rx_.set
1030  (
1031  new volScalarField
1032  (
1033  IOobject
1034  (
1035  "r",
1036  fluid_.time().timeName(),
1037  mesh_
1038  ),
1039  mesh_,
1040  dimensionedScalar("zero", dimless, Zero)
1041  )
1042  );
1043 
1044  rdx_.set
1045  (
1046  new volScalarField
1047  (
1048  IOobject
1049  (
1050  "r",
1051  fluid_.time().timeName(),
1052  mesh_
1053  ),
1054  mesh_,
1055  dimensionedScalar("zero", dimless, Zero)
1056  )
1057  );
1058  }
1059 
1060  if (nucleation_.size() != 0)
1061  {
1062  nucleationRate_.set
1063  (
1064  new volScalarField
1065  (
1066  IOobject
1067  (
1068  "nucleationRate",
1069  fluid_.time().timeName(),
1070  mesh_
1071  ),
1072  mesh_,
1074  (
1075  "nucleationRate",
1077  Zero
1078  )
1079  )
1080  );
1081  }
1082 
1083  if (velocityGroups_.size() > 1)
1084  {
1085  alphas_.set
1086  (
1087  new volScalarField
1088  (
1089  IOobject
1090  (
1091  IOobject::groupName("alpha", this->name()),
1092  fluid_.time().timeName(),
1093  mesh_,
1096  ),
1097  mesh_,
1098  dimensionedScalar("zero", dimless, Zero)
1099  )
1100  );
1101 
1102  dsm_.set
1103  (
1104  new volScalarField
1105  (
1106  IOobject
1107  (
1108  IOobject::groupName("d", this->name()),
1109  fluid_.time().timeName(),
1110  mesh_,
1113  ),
1114  mesh_,
1115  dimensionedScalar("zero", dimLength, Zero)
1116  )
1117  );
1118 
1119  U_.set
1120  (
1121  new volVectorField
1122  (
1123  IOobject
1124  (
1125  IOobject::groupName("U", this->name()),
1126  fluid_.time().timeName(),
1127  mesh_,
1130  ),
1131  mesh_,
1133  )
1134  );
1135  }
1136 }
1137 
1138 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1139 
1141 {}
1142 
1143 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1144 
1147 {
1149  return nullptr;
1150 }
1151 
1152 
1154 {
1155  return os.good();
1156 }
1157 
1158 
1162  const label i,
1163  const dimensionedScalar& v
1164 ) const
1165 {
1166  dimensionedScalar lowerBoundary(v);
1167  dimensionedScalar upperBoundary(v);
1168  const dimensionedScalar& xi = sizeGroups_[i].x();
1169 
1170  if (i == 0)
1171  {
1172  lowerBoundary = xi;
1173  }
1174  else
1175  {
1176  lowerBoundary = sizeGroups_[i-1].x();
1177  }
1178 
1179  if (i == sizeGroups_.size() - 1)
1180  {
1181  upperBoundary = xi;
1182  }
1183  else
1184  {
1185  upperBoundary = sizeGroups_[i+1].x();
1186  }
1187 
1188  if (v < lowerBoundary || v > upperBoundary)
1189  {
1190  return 0;
1191  }
1192  else if (v.value() <= xi.value())
1193  {
1194  return (v - lowerBoundary)/(xi - lowerBoundary);
1195  }
1196  else
1197  {
1198  return (upperBoundary - v)/(upperBoundary - xi);
1199  }
1200 }
1201 
1202 
1206  const phaseModel& dispersedPhase
1207 ) const
1208 {
1209  return
1210  fluid_.lookupSubModel<surfaceTensionModel>
1211  (
1212  phasePair(dispersedPhase, continuousPhase_)
1213  ).sigma();
1214 }
1215 
1216 
1219 {
1220  return
1221  mesh_.lookupObject<phaseCompressibleTurbulenceModel>
1222  (
1224  (
1226  continuousPhase_.name()
1227  )
1228  );
1229 }
1230 
1231 
1233 {
1234  const dictionary& solutionControls = mesh_.solverDict(name_);
1235  bool solveOnFinalIterOnly =
1236  solutionControls.lookupOrDefault<bool>("solveOnFinalIterOnly", false);
1237 
1238  if (!solveOnFinalIterOnly || pimple_.finalIter())
1239  {
1240  const label nCorr = this->nCorr();
1241  const scalar tolerance =
1242  readScalar(solutionControls.lookup("tolerance"));
1243 
1244  if (nCorr > 0)
1245  {
1246  correct();
1247  }
1248 
1249  int iCorr = 0;
1250  scalar maxInitialResidual = 1;
1251 
1252  while (++iCorr <= nCorr && maxInitialResidual > tolerance)
1253  {
1254  Info<< "populationBalance "
1255  << this->name()
1256  << ": Iteration "
1257  << iCorr
1258  << endl;
1259 
1260  if (updateSources())
1261  {
1262  sources();
1263  }
1264 
1265  dmdt();
1266 
1267  maxInitialResidual = 0;
1268 
1269  forAll(sizeGroups_, i)
1270  {
1271  sizeGroup& fi = sizeGroups_[i];
1272  const phaseModel& phase = fi.phase();
1273  const volScalarField& alpha = phase;
1274  const dimensionedScalar& residualAlpha = phase.residualAlpha();
1275  const volScalarField& rho = phase.thermo().rho();
1276 
1277  fvScalarMatrix sizeGroupEqn
1278  (
1279  fvm::ddt(alpha, rho, fi)
1280  + fi.VelocityGroup().mvConvection()->fvmDiv
1281  (
1282  phase.alphaRhoPhi(),
1283  fi
1284  )
1285  - fvm::Sp
1286  (
1287  fvc::ddt(alpha, rho) + fvc::div(phase.alphaRhoPhi())
1288  - fi.VelocityGroup().dmdt(),
1289  fi
1290  )
1291  ==
1292  fvc::Su(Su_[i]*rho, fi)
1293  - fvm::SuSp(SuSp_[i]*rho, fi)
1294  + fvc::ddt(residualAlpha*rho, fi)
1295  - fvm::ddt(residualAlpha*rho, fi)
1296  );
1297 
1298  sizeGroupEqn.relax();
1299 
1300  maxInitialResidual = max
1301  (
1302  sizeGroupEqn.solve().initialResidual(),
1303  maxInitialResidual
1304  );
1305  }
1306  }
1307 
1308  if (nCorr > 0)
1309  {
1310  forAll(velocityGroups_, i)
1311  {
1312  velocityGroups_[i].postSolve();
1313  }
1314  }
1315 
1316  if (velocityGroups_.size() > 1)
1317  {
1318  calcAlphas();
1319  dsm_() = calcDsm();
1320  calcVelocity();
1321  }
1322 
1323  volScalarField fAlpha0
1324  (
1325  sizeGroups_.first()*sizeGroups_.first().phase()
1326  );
1327 
1328  volScalarField fAlphaN
1329  (
1330  sizeGroups_.last()*sizeGroups_.last().phase()
1331  );
1332 
1333  Info<< this->name() << " sizeGroup phase fraction first, last = "
1334  << fAlpha0.weightedAverage(this->mesh().V()).value()
1335  << ' ' << fAlphaN.weightedAverage(this->mesh().V()).value()
1336  << endl;
1337  }
1338 }
1339 
1340 // ************************************************************************* //
populationBalanceModel.H
Foam::phaseModel
Single incompressible phase derived from the phase-fraction. Used as part of the multiPhaseMixture fo...
Definition: phaseModel.H:57
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:104
Foam::pimpleControl
PIMPLE control class to supply convergence information/checks for the PIMPLE loop.
Definition: pimpleControl.H:55
Foam::phasePair
Description for mass transfer between a pair of phases. The direction of the mass transfer is from th...
Definition: phasePair.H:51
Foam::IOobject::AUTO_WRITE
Definition: IOobject.H:129
nCorr
const int nCorr
Definition: readFluidMultiRegionPIMPLEControls.H:3
Foam::dimless
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Dimensionless.
Definition: dimensionSets.H:50
Foam::diameterModels::sizeGroup::phase
const phaseModel & phase() const
Return const-reference to the phase.
Definition: sizeGroupI.H:38
Foam::IOobject::name
const word & name() const
Return name.
Definition: IOobjectI.H:46
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::dimLength
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:53
Foam::phase
Single incompressible phase derived from the phase-fraction. Used as part of the multiPhaseMixture fo...
Definition: phase.H:54
Foam::diameterModels::driftModel::iNew
Class used for the read-construction of.
Definition: driftModel.H:85
Foam::tmp
A class for managing temporary objects.
Definition: PtrList.H:59
Foam::diameterModels::sizeGroup::VelocityGroup
const velocityGroup & VelocityGroup() const
Return const-reference to the velocityGroup.
Definition: sizeGroupI.H:45
Foam::Zero
static constexpr const zero Zero
Global zero.
Definition: zero.H:128
Foam::dimVelocity
const dimensionSet dimVelocity
Foam::diameterModels::binaryBreakupModel::iNew
Class used for the read-construction of.
Definition: binaryBreakupModel.H:87
Foam::surfaceTensionModel
Abstract base-class for surface tension models which return the surface tension coefficient field.
Definition: surfaceTensionModel.H:54
Foam::diameterModels::populationBalanceModel::velocityGroup
friend class velocityGroup
Definition: populationBalanceModel.H:336
Foam::constant::atomic::alpha
const dimensionedScalar alpha
Fine-structure constant: default SI units: [].
Definition: readThermalProperties.H:212
Foam::turbulenceModel::propertiesName
static const word propertiesName
Default name of the turbulence properties dictionary.
Definition: turbulenceModel.H:100
fvcDiv.H
Calculate the divergence of the given field.
driftModel.H
Foam::fvc::div
tmp< GeometricField< Type, fvPatchField, volMesh > > div(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcDiv.C:49
phasei
label phasei
Definition: pEqn.H:27
Foam::constant::atomic::group
const char *const group
Group name for atomic constants.
Definition: atomicConstants.C:41
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:337
Foam::dimensioned::value
const Type & value() const
Return const reference to value.
Definition: dimensionedType.C:404
fvcSup.H
Calculate the field for explicit evaluation of implicit and explicit sources.
Foam::dictionary::lookupOrDefault
T lookupOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionary.H:1241
fluid
twoPhaseSystem & fluid
Definition: setRegionFluidFields.H:3
Foam::diameterModels::velocityGroup::mvConvection
const tmp< fv::convectionScheme< scalar > > & mvConvection() const
Return const-reference to multivariate convectionScheme.
Definition: velocityGroupI.H:59
rho
rho
Definition: readInitialConditions.H:96
forAllConstIter
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object.
Definition: stdFoam.H:338
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:290
nucleationModel.H
Foam::dimensionedVector
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
Definition: dimensionedVector.H:50
Foam::GeometricField< scalar, fvPatchField, volMesh >::New
static tmp< GeometricField< scalar, fvPatchField, volMesh > > New(const word &name, const Mesh &mesh, const dimensionSet &ds, const word &patchFieldType=fvPatchField< scalar >::calculatedType())
Return tmp field from name, mesh, dimensions and patch type.
Definition: GeometricFieldNew.C:34
NotImplemented
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:419
Foam::dimTime
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:54
Foam::fvm::SuSp
tmp< fvMatrix< Type > > SuSp(const volScalarField::Internal &, const GeometricField< Type, fvPatchField, volMesh > &)
Foam::Pair< word >::compare
static int compare(const Pair< word > &a, const Pair< word > &b)
Compare Pairs.
Definition: PairI.H:33
Foam::label
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:62
Foam::fvm::Sp
tmp< fvMatrix< Type > > Sp(const volScalarField::Internal &, const GeometricField< Type, fvPatchField, volMesh > &)
Foam::fvc::Su
tmp< GeometricField< Type, fvPatchField, volMesh > > Su(const GeometricField< Type, fvPatchField, volMesh > &su, const GeometricField< Type, fvPatchField, volMesh > &vf)
Definition: fvcSup.C:46
coalescenceModel.H
Foam::inv
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
Definition: dimensionedSphericalTensor.C:73
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
Foam::diameterModels::velocityGroup::dmdt
const volScalarField & dmdt() const
Return const-reference to the mass transfer rate.
Definition: velocityGroupI.H:66
Foam::fvMatrix::relax
void relax(const scalar alpha)
Relax matrix (for steady-state solution).
Definition: fvMatrix.C:574
correct
fvOptions correct(rho)
binaryBreakupModel.H
Foam::dimensionedScalar
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Definition: dimensionedScalarFwd.H:43
Foam::volScalarField
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:57
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Foam::diameterModels::sizeGroup
This class represents a single sizeGroup belonging to a velocityGroup. The main property of a sizeGro...
Definition: sizeGroup.H:96
Foam::dictionary::lookup
ITstream & lookup(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionary.C:419
Foam::FatalError
error FatalError
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:121
Foam::diameterModels::populationBalanceModel::~populationBalanceModel
virtual ~populationBalanceModel()
Destructor.
Definition: populationBalanceModel.C:1140
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
Foam::dimensioned< scalar >
Foam::diameterModels::populationBalanceModel::sizeGroup
friend class sizeGroup
Definition: populationBalanceModel.H:335
breakupModel.H
fvmSup.H
Calculate the matrix for implicit and explicit sources.
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::diameterModels::coalescenceModel::iNew
Class used for the read-construction of.
Definition: coalescenceModel.H:85
Foam::diameterModels::populationBalanceModel::clone
autoPtr< populationBalanceModel > clone() const
Return clone.
Definition: populationBalanceModel.C:1146
Foam::autoPtr
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: HashPtrTable.H:53
Foam::regIOobject
regIOobject is an abstract class derived from IOobject to handle automatic object registration with t...
Definition: regIOobject.H:67
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:355
Foam::fvm::ddt
tmp< fvMatrix< Type > > ddt(const GeometricField< Type, fvPatchField, volMesh > &vf)
Definition: fvmDdt.C:48
Foam::diameterModels::populationBalanceModel::populationBalanceModel
populationBalanceModel(const phaseSystem &fluid, const word &name, HashPtrTable< volScalarField, phasePairKey, phasePairKey::hash > &pDmdt)
Definition: populationBalanceModel.C:852
Foam::diameterModels::populationBalanceModel::solve
void solve()
Solve the population balance equation.
Definition: populationBalanceModel.C:1232
Foam::HashPtrTable< volScalarField, phasePairKey, phasePairKey::hash >
Foam::GeometricField::ref
Internal & ref(const bool updateAccessTime=true)
Return a reference to the dimensioned internal field.
Definition: GeometricField.C:718
Foam::ThermalDiffusivity
Templated wrapper class to provide compressible turbulence models thermal diffusivity based thermal t...
Definition: phaseCompressibleTurbulenceModelFwd.H:47
Foam::IOobject::groupName
static word groupName(StringType name, const word &group)
Create dot-delimited name.group.
Foam::diameterModels::populationBalanceModel::continuousTurbulence
const phaseCompressibleTurbulenceModel & continuousTurbulence() const
Return reference to turbulence model of the continuous phase.
Definition: populationBalanceModel.C:1218
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
gamma
const scalar gamma
Definition: EEqn.H:9
k
label k
Boltzmann constant.
Definition: LISASMDCalcMethod2.H:41
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::fvMatrix
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvPatchField.H:76
fvcDdt.H
Calculate the first temporal derivative.
Foam::fvc::ddt
tmp< GeometricField< Type, fvPatchField, volMesh > > ddt(const dimensioned< Type > dt, const fvMesh &mesh)
Definition: fvcDdt.C:47
Foam::phaseSystem::phases
const phaseModelList & phases() const
Return the phase models.
Definition: phaseSystemI.H:37
Foam::phaseSystem
Class to represent a system of phases and model interfacial transfers between them.
Definition: phaseSystem.H:69
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
sigma
dimensionedScalar sigma("sigma", dimMass/sqr(dimTime), transportProperties)
Foam::IOstream::good
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:216
Foam::dimVolume
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:61
Foam::GeometricField< scalar, fvPatchField, volMesh >
Foam::diameterModels::breakupModel::iNew
Class used for the read-construction of.
Definition: breakupModel.H:94
Foam::IOobject::NO_READ
Definition: IOobject.H:123
Foam::diameterModels::nucleationModel::iNew
Class used for the read-construction of.
Definition: nucleationModel.H:85
Foam::neg
dimensionedScalar neg(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:199
Foam::diameterModels::populationBalanceModel::gamma
const dimensionedScalar gamma(const label i, const dimensionedScalar &v) const
Return allocation coefficient.
Definition: populationBalanceModel.C:1161
fvmDdt.H
Calulate the matrix for the first temporal derivative.
Foam::diameterModels::populationBalanceModel::writeData
bool writeData(Ostream &) const
Dummy write for regIOobject.
Definition: populationBalanceModel.C:1153
Foam::phase::rho
const dimensionedScalar & rho() const
Return const-access to phase1 density.
Definition: phase.H:140
Foam::diameterModels::populationBalanceModel::sigmaWithContinuousPhase
const tmp< volScalarField > sigmaWithContinuousPhase(const phaseModel &dispersedPhase) const
Return the surface tension coefficient between a given dispersed.
Definition: populationBalanceModel.C:1205
Foam::pos
dimensionedScalar pos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:177