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