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-2023 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "populationBalanceModel.H"
27 #include "phaseSystem.H"
28 #include "coalescenceModel.H"
29 #include "breakupModel.H"
30 #include "binaryBreakupModel.H"
31 #include "driftModel.H"
32 #include "nucleationModel.H"
34 #include "fvmDdt.H"
35 #include "fvmDiv.H"
36 #include "fvmSup.H"
37 #include "fvcDdt.H"
38 #include "fvcDiv.H"
39 #include "fvcSup.H"
40 #include "shapeModel.H"
41 
42 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46 namespace diameterModels
47 {
49 }
50 }
51 
52 
53 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
54 
55 void Foam::diameterModels::populationBalanceModel::registerVelocityGroups()
56 {
57  forAll(fluid_.phases(), phasei)
58  {
59  if (isA<velocityGroup>(fluid_.phases()[phasei].dPtr()()))
60  {
61  const velocityGroup& velGroup =
62  refCast<const velocityGroup>(fluid_.phases()[phasei].dPtr()());
63 
64  if (velGroup.popBalName() == this->name())
65  {
66  velocityGroupPtrs_.insert(velGroup.phase().name(), &velGroup);
67 
68  dilatationErrors_.insert
69  (
70  velGroup.phase().name(),
72  (
73  IOobject
74  (
76  (
77  "dilatationError",
78  velGroup.phase().name()
79  ),
80  fluid_.time().name(),
81  mesh_
82  ),
83  mesh_,
85  )
86  );
87 
88  forAll(velGroup.sizeGroups(), i)
89  {
90  this->registerSizeGroups
91  (
92  const_cast<sizeGroup&>(velGroup.sizeGroups()[i])
93  );
94  }
95  }
96  }
97  }
98 }
99 
100 
101 void Foam::diameterModels::populationBalanceModel::registerSizeGroups
102 (
103  sizeGroup& group
104 )
105 {
106  if
107  (
108  sizeGroups().size() != 0
109  &&
110  group.x().value() <= sizeGroups().last().x().value()
111  )
112  {
114  << "Size groups must be entered according to their representative"
115  << " size"
116  << exit(FatalError);
117  }
118 
119  sizeGroups_.resize(sizeGroups().size() + 1);
120  sizeGroups_.set(sizeGroups().size() - 1, &group);
121 
122  if (sizeGroups().size() == 1)
123  {
124  v_.append
125  (
127  (
128  "v",
129  sizeGroups().last().x()
130  )
131  );
132 
133  v_.append
134  (
136  (
137  "v",
138  sizeGroups().last().x()
139  )
140  );
141  }
142  else
143  {
144  v_.last() =
145  0.5
146  *(
147  sizeGroups()[sizeGroups().size()-2].x()
148  + sizeGroups().last().x()
149  );
150 
151  v_.append
152  (
154  (
155  "v",
156  sizeGroups().last().x()
157  )
158  );
159  }
160 
161  delta_.append(new PtrList<dimensionedScalar>());
162 
163  Su_.append
164  (
165  new volScalarField
166  (
167  IOobject
168  (
169  "Su",
170  fluid_.time().name(),
171  mesh_
172  ),
173  mesh_,
175  )
176  );
177 
178  Sp_.append
179  (
180  new volScalarField
181  (
182  IOobject
183  (
184  "Sp",
185  fluid_.time().name(),
186  mesh_
187  ),
188  mesh_,
190  )
191  );
192 }
193 
194 
195 void Foam::diameterModels::populationBalanceModel::initialiseDmdtfs()
196 {
198  (
199  HashTable<const diameterModels::velocityGroup*>,
200  velocityGroupPtrs(),
201  iter1
202  )
203  {
204  const diameterModels::velocityGroup& velGrp1 = *iter1();
205 
207  (
208  HashTable<const diameterModels::velocityGroup*>,
209  velocityGroupPtrs(),
210  iter2
211  )
212  {
213  const diameterModels::velocityGroup& velGrp2 = *iter2();
214 
215  const phaseInterface interface(velGrp1.phase(), velGrp2.phase());
216 
217  if
218  (
219  &velGrp1 != &velGrp2
220  &&
221  !dmdtfs_.found(interface)
222  )
223  {
224  fluid_.validateMassTransfer
225  <diameterModels::populationBalanceModel>(interface);
226 
227  dmdtfs_.insert
228  (
229  interface,
230  new volScalarField
231  (
232  IOobject
233  (
235  (
236  typedName("dmdtf"),
237  interface.name()
238  ),
239  mesh().time().name(),
240  mesh()
241  ),
242  mesh(),
244  )
245  );
246  }
247  }
248  }
249 }
250 
251 
252 void Foam::diameterModels::populationBalanceModel::precompute()
253 {
254  forAll(coalescenceModels_, model)
255  {
256  coalescenceModels_[model].precompute();
257  }
258 
259  forAll(breakupModels_, model)
260  {
261  breakupModels_[model].precompute();
262 
263  breakupModels_[model].dsdPtr()->precompute();
264  }
265 
266  forAll(binaryBreakupModels_, model)
267  {
268  binaryBreakupModels_[model].precompute();
269  }
270 
271  forAll(drift_, model)
272  {
273  drift_[model].precompute();
274  }
275 
276  forAll(nucleation_, model)
277  {
278  nucleation_[model].precompute();
279  }
280 }
281 
282 
283 void Foam::diameterModels::populationBalanceModel::birthByCoalescence
284 (
285  const label j,
286  const label k
287 )
288 {
289  const sizeGroup& fj = sizeGroups()[j];
290  const sizeGroup& fk = sizeGroups()[k];
291 
292  dimensionedScalar Eta;
293  dimensionedScalar v = fj.x() + fk.x();
294 
295  for (label i = j; i < sizeGroups().size(); i++)
296  {
297  Eta = eta(i, v);
298 
299  if (Eta.value() == 0) continue;
300 
301  const sizeGroup& fi = sizeGroups()[i];
302 
303  if (j == k)
304  {
305  Sui_ =
306  0.5*fi.x()/(fj.x()*fk.x())*Eta
307  *coalescenceRate_()*fj*fj.phase()*fk*fk.phase();
308  }
309  else
310  {
311  Sui_ =
312  fi.x()/(fj.x()*fk.x())*Eta
313  *coalescenceRate_()*fj*fj.phase()*fk*fk.phase();
314  }
315 
316  Su_[i] += Sui_;
317 
318  const phaseInterface interfaceij(fi.phase(), fj.phase());
319 
320  if (dmdtfs_.found(interfaceij))
321  {
322  const scalar dmdtSign =
323  interfaceij.index(fi.phase()) == 0 ? +1 : -1;
324 
325  *dmdtfs_[interfaceij] += dmdtSign*fj.x()/v*Sui_*fj.phase().rho();
326  }
327 
328  const phaseInterface interfaceik(fi.phase(), fk.phase());
329 
330  if (dmdtfs_.found(interfaceik))
331  {
332  const scalar dmdtSign =
333  interfaceik.index(fi.phase()) == 0 ? +1 : -1;
334 
335  *dmdtfs_[interfaceik] += dmdtSign*fk.x()/v*Sui_*fk.phase().rho();
336  }
337 
338  sizeGroups_[i].shapeModelPtr()->addCoalescence(Sui_, fj, fk);
339  }
340 }
341 
342 
343 void Foam::diameterModels::populationBalanceModel::deathByCoalescence
344 (
345  const label i,
346  const label j
347 )
348 {
349  const sizeGroup& fi = sizeGroups()[i];
350  const sizeGroup& fj = sizeGroups()[j];
351 
352  Sp_[i] += coalescenceRate_()*fi.phase()*fj*fj.phase()/fj.x();
353 
354  if (i != j)
355  {
356  Sp_[j] += coalescenceRate_()*fj.phase()*fi*fi.phase()/fi.x();
357  }
358 }
359 
360 
361 void Foam::diameterModels::populationBalanceModel::birthByBreakup
362 (
363  const label k,
364  const label model
365 )
366 {
367  const sizeGroup& fk = sizeGroups()[k];
368 
369  for (label i = 0; i <= k; i++)
370  {
371  const sizeGroup& fi = sizeGroups()[i];
372 
373  Sui_ =
374  fi.x()*breakupModels_[model].dsdPtr()().nik(i, k)/fk.x()
375  *breakupRate_()*fk*fk.phase();
376 
377  Su_[i] += Sui_;
378 
379  const phaseInterface interface(fi.phase(), fk.phase());
380 
381  if (dmdtfs_.found(interface))
382  {
383  const scalar dmdtSign =
384  interface.index(fi.phase()) == 0 ? +1 : -1;
385 
386  *dmdtfs_[interface] += dmdtSign*Sui_*fk.phase().rho();
387  }
388 
389  sizeGroups_[i].shapeModelPtr()->addBreakup(Sui_, fk);
390  }
391 }
392 
393 
394 void Foam::diameterModels::populationBalanceModel::deathByBreakup(const label i)
395 {
396  Sp_[i] += breakupRate_()*sizeGroups()[i].phase();
397 }
398 
399 
400 void Foam::diameterModels::populationBalanceModel::calcDeltas()
401 {
402  forAll(sizeGroups(), i)
403  {
404  if (delta_[i].empty())
405  {
406  for (label j = 0; j <= sizeGroups().size() - 1; j++)
407  {
408  const sizeGroup& fj = sizeGroups()[j];
409 
410  delta_[i].append
411  (
413  (
414  "delta",
415  dimVolume,
416  v_[i+1].value() - v_[i].value()
417  )
418  );
419 
420  if
421  (
422  v_[i].value() < 0.5*fj.x().value()
423  &&
424  0.5*fj.x().value() < v_[i+1].value()
425  )
426  {
427  delta_[i][j] = mag(0.5*fj.x() - v_[i]);
428  }
429  else if (0.5*fj.x().value() <= v_[i].value())
430  {
431  delta_[i][j].value() = 0;
432  }
433  }
434  }
435  }
436 }
437 
438 
439 void Foam::diameterModels::populationBalanceModel::birthByBinaryBreakup
440 (
441  const label i,
442  const label j
443 )
444 {
445  const sizeGroup& fi = sizeGroups()[i];
446  const sizeGroup& fj = sizeGroups()[j];
447 
448  const volScalarField Su(binaryBreakupRate_()*fj*fj.phase());
449 
450  Sui_ = fi.x()*delta_[i][j]/fj.x()*Su;
451 
452  Su_[i] += Sui_;
453 
454  sizeGroups_[i].shapeModelPtr()->addBreakup(Sui_, fj);
455 
456  const phaseInterface interfaceij(fi.phase(), fj.phase());
457 
458  if (dmdtfs_.found(interfaceij))
459  {
460  const scalar dmdtSign =
461  interfaceij.index(fi.phase()) == 0 ? +1 : -1;
462 
463  *dmdtfs_[interfaceij] += dmdtSign*Sui_*fj.phase().rho();
464  }
465 
466  dimensionedScalar Eta;
467  dimensionedScalar v = fj.x() - fi.x();
468 
469  for (label k = 0; k <= j; k++)
470  {
471  Eta = eta(k, v);
472 
473  if (Eta.value() == 0) continue;
474 
475  const sizeGroup& fk = sizeGroups()[k];
476 
477  volScalarField& Suk = Sui_;
478 
479  Suk = fk.x()*delta_[i][j]*Eta/fj.x()*Su;
480 
481  Su_[k] += Suk;
482 
483  const phaseInterface interfacekj(fk.phase(), fj.phase());
484 
485  if (dmdtfs_.found(interfacekj))
486  {
487  const scalar dmdtSign =
488  interfacekj.index(fk.phase()) == 0 ? +1 : -1;
489 
490  *dmdtfs_[interfacekj] += dmdtSign*Suk*fj.phase().rho();
491  }
492 
493  sizeGroups_[k].shapeModelPtr()->addBreakup(Suk, fj);
494  }
495 }
496 
497 
498 void Foam::diameterModels::populationBalanceModel::deathByBinaryBreakup
499 (
500  const label j,
501  const label i
502 )
503 {
504  Sp_[i] += sizeGroups()[i].phase()*binaryBreakupRate_()*delta_[j][i];
505 }
506 
507 
508 void Foam::diameterModels::populationBalanceModel::drift
509 (
510  const label i,
511  driftModel& model
512 )
513 {
514  model.addToDriftRate(driftRate_(), i);
515 
516  const sizeGroup& fp = sizeGroups()[i];
517 
518  if (i < sizeGroups().size() - 1)
519  {
520  const sizeGroup& fe = sizeGroups()[i+1];
521  volScalarField& Sue = Sui_;
522 
523  Sp_[i] += 1/(fe.x() - fp.x())*pos(driftRate_())*driftRate_();
524 
525  Sue =
526  fe.x()/(fp.x()*(fe.x() - fp.x()))*pos(driftRate_())*driftRate_()*fp;
527 
528  Su_[i+1] += Sue;
529 
530  const phaseInterface interfacepe(fp.phase(), fe.phase());
531 
532  if (dmdtfs_.found(interfacepe))
533  {
534  const scalar dmdtSign =
535  interfacepe.index(fp.phase()) == 0 ? +1 : -1;
536 
537  *dmdtfs_[interfacepe] -= dmdtSign*Sue*fp.phase().rho();
538  }
539 
540  sizeGroups_[i+1].shapeModelPtr()->addDrift(Sue, fp, model);
541  }
542 
543  if (i == sizeGroups().size() - 1)
544  {
545  Sp_[i] -= pos(driftRate_())*driftRate_()/fp.x();
546  }
547 
548  if (i > 0)
549  {
550  const sizeGroup& fw = sizeGroups()[i-1];
551  volScalarField& Suw = Sui_;
552 
553  Sp_[i] += 1/(fw.x() - fp.x())*neg(driftRate_())*driftRate_();
554 
555  Suw =
556  fw.x()/(fp.x()*(fw.x() - fp.x()))*neg(driftRate_())*driftRate_()*fp;
557 
558  Su_[i-1] += Suw;
559 
560  const phaseInterface interfacepw(fp.phase(), fw.phase());
561 
562  if (dmdtfs_.found(interfacepw))
563  {
564  const scalar dmdtSign =
565  interfacepw.index(fp.phase()) == 0 ? +1 : -1;
566 
567  *dmdtfs_[interfacepw] -= dmdtSign*Suw*fp.phase().rho();
568  }
569 
570  sizeGroups_[i-1].shapeModelPtr()->addDrift(Suw, fp, model);
571  }
572 
573  if (i == 0)
574  {
575  Sp_[i] -= neg(driftRate_())*driftRate_()/fp.x();
576  }
577 }
578 
579 
580 void Foam::diameterModels::populationBalanceModel::nucleation
581 (
582  const label i,
583  nucleationModel& model
584 )
585 {
586  const sizeGroup& fi = sizeGroups()[i];
587 
588  model.addToNucleationRate(nucleationRate_(), i);
589 
590  Sui_ = fi.x()*nucleationRate_();
591 
592  Su_[i] += Sui_;
593 
594  sizeGroups_[i].shapeModelPtr()->addNucleation(Sui_, fi, model);
595 }
596 
597 
598 void Foam::diameterModels::populationBalanceModel::sources()
599 {
600  forAll(sizeGroups(), i)
601  {
602  sizeGroups_[i].shapeModelPtr()->reset();
603  Su_[i] = Zero;
604  Sp_[i] = Zero;
605  }
606 
607  forAllIter(phaseSystem::dmdtfTable, dmdtfs_, pDmdtIter)
608  {
609  *pDmdtIter() = Zero;
610  }
611 
612  forAll(coalescencePairs_, coalescencePairi)
613  {
614  label i = coalescencePairs_[coalescencePairi].first();
615  label j = coalescencePairs_[coalescencePairi].second();
616 
617  coalescenceRate_() = Zero;
618 
619  forAll(coalescenceModels_, model)
620  {
621  coalescenceModels_[model].addToCoalescenceRate
622  (
623  coalescenceRate_(),
624  i,
625  j
626  );
627  }
628 
629  birthByCoalescence(i, j);
630 
631  deathByCoalescence(i, j);
632  }
633 
634  forAll(binaryBreakupPairs_, binaryBreakupPairi)
635  {
636  label i = binaryBreakupPairs_[binaryBreakupPairi].first();
637  label j = binaryBreakupPairs_[binaryBreakupPairi].second();
638 
639  binaryBreakupRate_() = Zero;
640 
641  forAll(binaryBreakupModels_, model)
642  {
643  binaryBreakupModels_[model].addToBinaryBreakupRate
644  (
645  binaryBreakupRate_(),
646  j,
647  i
648  );
649  }
650 
651  birthByBinaryBreakup(j, i);
652 
653  deathByBinaryBreakup(j, i);
654  }
655 
656  forAll(sizeGroups(), i)
657  {
658  forAll(breakupModels_, model)
659  {
660  breakupModels_[model].setBreakupRate(breakupRate_(), i);
661 
662  birthByBreakup(i, model);
663 
664  deathByBreakup(i);
665  }
666 
667  forAll(drift_, model)
668  {
669  driftRate_() = Zero;
670 
671  drift(i, drift_[model]);
672  }
673 
674  forAll(nucleation_, model)
675  {
676  nucleationRate_() = Zero;
677 
678  nucleation(i, nucleation_[model]);
679  }
680  }
681 }
682 
683 
684 void Foam::diameterModels::populationBalanceModel::correctDilatationError()
685 {
686  forAllIter
687  (
688  HashTable<volScalarField>,
689  dilatationErrors_,
690  iter
691  )
692  {
693  volScalarField& dilatationError = iter();
694  const word& phaseName = iter.key();
695  const phaseModel& phase = fluid_.phases()[phaseName];
696  const velocityGroup& velGroup = *velocityGroupPtrs_[phaseName];
697  const volScalarField& alpha = phase;
698  const volScalarField& rho = phase.rho();
699 
700  dilatationError =
701  fvc::ddt(alpha) + fvc::div(phase.alphaPhi())
702  - (fluid_.fvModels().source(alpha, rho) & rho)/rho;
703 
704  forAll(velGroup.sizeGroups(), i)
705  {
706  const sizeGroup& fi = velGroup.sizeGroups()[i];
707 
708  dilatationError -= Su_[fi.i()] - fvc::Sp(Sp_[fi.i()], fi);
709  }
710  }
711 }
712 
713 
714 void Foam::diameterModels::populationBalanceModel::calcAlphas()
715 {
716  alphas_() = Zero;
717 
718  forAllConstIter(HashTable<const velocityGroup*>, velocityGroupPtrs_, iter)
719  {
720  const phaseModel& phase = iter()->phase();
721 
722  alphas_() += max(phase, phase.residualAlpha());
723  }
724 }
725 
726 
728 Foam::diameterModels::populationBalanceModel::calcDsm()
729 {
730  tmp<volScalarField> tInvDsm
731  (
733  (
734  "invDsm",
735  mesh_,
737  )
738  );
739 
740  volScalarField& invDsm = tInvDsm.ref();
741 
742  forAllConstIter(HashTable<const velocityGroup*>, velocityGroupPtrs_, iter)
743  {
744  const phaseModel& phase = iter()->phase();
745 
746  invDsm += max(phase, phase.residualAlpha())/(phase.d()*alphas_());
747  }
748 
749  return 1/tInvDsm;
750 }
751 
752 
753 void Foam::diameterModels::populationBalanceModel::calcVelocity()
754 {
755  U_() = Zero;
756 
757  forAllConstIter(HashTable<const velocityGroup*>, velocityGroupPtrs_, iter)
758  {
759  const phaseModel& phase = iter()->phase();
760 
761  U_() += max(phase, phase.residualAlpha())*phase.U()/alphas_();
762  }
763 }
764 
765 bool Foam::diameterModels::populationBalanceModel::updateSources()
766 {
767  const bool result = sourceUpdateCounter_ % sourceUpdateInterval() == 0;
768 
769  ++ sourceUpdateCounter_;
770 
771  return result;
772 }
773 
774 
775 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
776 
778 (
779  const phaseSystem& fluid,
780  const word& name
781 )
782 :
784  (
785  IOobject
786  (
787  name,
788  fluid.time().constant(),
789  fluid.mesh()
790  )
791  ),
792  fluid_(fluid),
793  dmdtfs_(),
794  mesh_(fluid_.mesh()),
795  name_(name),
796  dict_
797  (
798  fluid_.subDict("populationBalanceCoeffs").subDict(name_)
799  ),
800  continuousPhase_
801  (
802  mesh_.lookupObject<phaseModel>
803  (
804  IOobject::groupName("alpha", dict_.lookup("continuousPhase"))
805  )
806  ),
807  sizeGroups_(),
808  v_(),
809  delta_(),
810  Su_(),
811  Sp_(),
812  Sui_
813  (
814  IOobject
815  (
816  "Sui",
817  mesh_.time().name(),
818  mesh_
819  ),
820  mesh_,
822  ),
823  coalescenceModels_
824  (
825  dict_.lookup("coalescenceModels"),
826  coalescenceModel::iNew(*this)
827  ),
828  coalescenceRate_(),
829  coalescencePairs_(),
830  breakupModels_
831  (
832  dict_.lookup("breakupModels"),
833  breakupModel::iNew(*this)
834  ),
835  breakupRate_(),
836  binaryBreakupModels_
837  (
838  dict_.lookup("binaryBreakupModels"),
839  binaryBreakupModel::iNew(*this)
840  ),
841  binaryBreakupRate_(),
842  binaryBreakupPairs_(),
843  drift_
844  (
845  dict_.lookup("driftModels"),
846  driftModel::iNew(*this)
847  ),
848  driftRate_(),
849  nucleation_
850  (
851  dict_.lookup("nucleationModels"),
852  nucleationModel::iNew(*this)
853  ),
854  nucleationRate_(),
855  alphas_(),
856  dsm_(),
857  U_(),
858  sourceUpdateCounter_(0)
859 {
860  this->registerVelocityGroups();
861 
862  if (sizeGroups().size() < 3)
863  {
865  << "The populationBalance " << name_
866  << " requires a minimum number of three sizeGroups to be specified."
867  << exit(FatalError);
868  }
869 
870  this->initialiseDmdtfs();
871 
872  if (coalescenceModels_.size() != 0)
873  {
874  coalescenceRate_.set
875  (
876  new volScalarField
877  (
878  IOobject
879  (
880  "coalescenceRate",
881  mesh_.time().name(),
882  mesh_
883  ),
884  mesh_,
886  )
887  );
888 
889  forAll(sizeGroups(), i)
890  {
891  for (label j = 0; j <= i; j++)
892  {
893  coalescencePairs_.append(labelPair(i, j));
894  }
895  }
896  }
897 
898  if (breakupModels_.size() != 0)
899  {
900  breakupRate_.set
901  (
902  new volScalarField
903  (
904  IOobject
905  (
906  "breakupRate",
907  fluid_.time().name(),
908  mesh_
909  ),
910  mesh_,
912  )
913  );
914  }
915 
916  if (binaryBreakupModels_.size() != 0)
917  {
918  binaryBreakupRate_.set
919  (
920  new volScalarField
921  (
922  IOobject
923  (
924  "binaryBreakupRate",
925  fluid_.time().name(),
926  mesh_
927  ),
928  mesh_,
930  (
931  "binaryBreakupRate",
933  Zero
934  )
935  )
936  );
937 
938  calcDeltas();
939 
940  forAll(sizeGroups(), i)
941  {
942  label j = 0;
943 
944  while (delta_[j][i].value() != 0)
945  {
946  binaryBreakupPairs_.append(labelPair(i, j));
947  j++;
948  }
949  }
950  }
951 
952  if (drift_.size() != 0)
953  {
954  driftRate_.set
955  (
956  new volScalarField
957  (
958  IOobject
959  (
960  "driftRate",
961  fluid_.time().name(),
962  mesh_
963  ),
964  mesh_,
966  )
967  );
968  }
969 
970  if (nucleation_.size() != 0)
971  {
972  nucleationRate_.set
973  (
974  new volScalarField
975  (
976  IOobject
977  (
978  "nucleationRate",
979  fluid_.time().name(),
980  mesh_
981  ),
982  mesh_,
984  (
985  "nucleationRate",
987  Zero
988  )
989  )
990  );
991  }
992 
993  if (velocityGroupPtrs_.size() > 1)
994  {
995  alphas_.set
996  (
997  new volScalarField
998  (
999  IOobject
1000  (
1001  IOobject::groupName("alpha", this->name()),
1002  fluid_.time().name(),
1003  mesh_,
1006  ),
1007  mesh_,
1009  )
1010  );
1011 
1012  dsm_.set
1013  (
1014  new volScalarField
1015  (
1016  IOobject
1017  (
1018  IOobject::groupName("d", this->name()),
1019  fluid_.time().name(),
1020  mesh_,
1023  ),
1024  mesh_,
1026  )
1027  );
1028 
1029  U_.set
1030  (
1031  new volVectorField
1032  (
1033  IOobject
1034  (
1035  IOobject::groupName("U", this->name()),
1036  fluid_.time().name(),
1037  mesh_,
1040  ),
1041  mesh_,
1043  )
1044  );
1045  }
1046 }
1047 
1048 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1049 
1051 {}
1052 
1053 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1054 
1057 {
1058  notImplemented("populationBalance::clone() const");
1059  return autoPtr<populationBalanceModel>(nullptr);
1060 }
1061 
1062 
1064 {
1065  return os.good();
1066 }
1067 
1068 
1071 (
1072  const label i,
1073  const dimensionedScalar& v
1074 ) const
1075 {
1076  const dimensionedScalar& x0 = sizeGroups()[0].x();
1077  const dimensionedScalar& xi = sizeGroups()[i].x();
1078  const dimensionedScalar& xm = sizeGroups().last().x();
1079  dimensionedScalar lowerBoundary(x0);
1080  dimensionedScalar upperBoundary(xm);
1081 
1082  if (i != 0) lowerBoundary = sizeGroups()[i-1].x();
1083 
1084  if (i != sizeGroups().size() - 1) upperBoundary = sizeGroups()[i+1].x();
1085 
1086  if ((i == 0 && v < x0) || (i == sizeGroups().size() - 1 && v > xm))
1087  {
1088  return v/xi;
1089  }
1090  else if (v < lowerBoundary || v > upperBoundary)
1091  {
1092  return 0;
1093  }
1094  else if (v.value() == xi.value())
1095  {
1096  return 1;
1097  }
1098  else if (v > xi)
1099  {
1100  return (upperBoundary - v)/(upperBoundary - xi);
1101  }
1102  else
1103  {
1104  return (v - lowerBoundary)/(xi - lowerBoundary);
1105  }
1106 }
1107 
1108 
1111 (
1112  const phaseModel& dispersedPhase
1113 ) const
1114 {
1115  return phaseInterface(dispersedPhase, continuousPhase_).sigma();
1116 }
1117 
1118 
1121 {
1122  return
1124  (
1125  continuousPhase_.name()
1126  );
1127 }
1128 
1129 
1131 {
1132  if (!solveOnFinalIterOnly() || fluid_.pimple().finalIter())
1133  {
1134  const label nCorr = this->nCorr();
1135  const scalar tolerance =
1136  mesh_.solution().solverDict(name_).lookup<scalar>("tolerance");
1137 
1138  const bool updateSrc = updateSources();
1139 
1140  if (nCorr > 0 && updateSrc)
1141  {
1142  precompute();
1143  }
1144 
1145  int iCorr = 0;
1146  scalar maxInitialResidual = 1;
1147 
1148  while (++iCorr <= nCorr && maxInitialResidual > tolerance)
1149  {
1150  Info<< "populationBalance "
1151  << this->name()
1152  << ": Iteration "
1153  << iCorr
1154  << endl;
1155 
1156  if (updateSrc)
1157  {
1158  sources();
1159  }
1160 
1161  correctDilatationError();
1162 
1163  maxInitialResidual = 0;
1164 
1165  forAll(sizeGroups(), i)
1166  {
1167  sizeGroup& fi = sizeGroups_[i];
1168  const phaseModel& phase = fi.phase();
1169  const volScalarField& alpha = phase;
1170  const volScalarField& rho = phase.rho();
1171  const volScalarField& dilatationError =
1172  dilatationErrors_[phase.name()];
1173 
1174  fvScalarMatrix sizeGroupEqn
1175  (
1176  fvm::ddt(alpha, fi)
1177  + fvm::div(phase.alphaPhi(), fi)
1178  + fvm::Sp(-(1 - small)*dilatationError, fi)
1179  + fvm::SuSp(-small*dilatationError, fi)
1180  ==
1181  fvc::Su(Su_[i], fi)
1182  - fvm::Sp(Sp_[i], fi)
1183  + fluid_.fvModels().source(alpha, rho, fi)/rho
1184  - correction
1185  (
1186  fvm::Sp
1187  (
1188  max(phase.residualAlpha() - alpha, scalar(0))
1189  /this->mesh().time().deltaT(),
1190  fi
1191  )
1192  )
1193  );
1194 
1195  sizeGroupEqn.relax();
1196 
1197  fluid_.fvConstraints().constrain(sizeGroupEqn);
1198 
1199  maxInitialResidual = max
1200  (
1201  sizeGroupEqn.solve().initialResidual(),
1202  maxInitialResidual
1203  );
1204 
1205  fluid_.fvConstraints().constrain(fi);
1206  }
1207  }
1208 
1209  volScalarField fAlpha0
1210  (
1211  sizeGroups().first()*sizeGroups().first().phase()
1212  );
1213 
1214  volScalarField fAlphaN
1215  (
1216  sizeGroups().last()*sizeGroups().last().phase()
1217  );
1218 
1219  Info<< this->name() << " sizeGroup phase fraction first, last = "
1220  << fAlpha0.weightedAverage(this->mesh().V()).value()
1221  << ' ' << fAlphaN.weightedAverage(this->mesh().V()).value()
1222  << endl;
1223  }
1224 }
1225 
1226 
1228 {
1229  if (velocityGroupPtrs_.size() > 1)
1230  {
1231  calcAlphas();
1232  dsm_() = calcDsm();
1233  calcVelocity();
1234  }
1235 }
1236 
1237 
1238 // ************************************************************************* //
label k
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:459
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
dimensioned< Type > weightedAverage(const DimensionedField< scalar, GeoMesh > &) const
Calculate and return weighted average.
Generic GeometricField class.
static tmp< GeometricField< Type, PatchField, GeoMesh > > New(const word &name, const Internal &, const PtrList< PatchField< Type >> &)
Return a temporary field constructed from name,.
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:318
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:167
const word & name() const
Return name.
Definition: IOobject.H:310
static word groupName(Name name, const word &group)
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:330
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: autoPtr.H:51
Base class for binary breakup models that provide a breakup rate between a size class pair directly,...
Base class for breakup models which provide a total breakup rate and a separate daughter size distrib...
Definition: breakupModel.H:56
Base class for coalescence models.
Constant dispersed-phase particle diameter model.
Base class for drift models.
Definition: driftModel.H:54
Base class for nucleation models.
Return a pointer to a new populationBalanceModel object created on.
Model for tracking the evolution of a dispersed phase size distribution due to coalescence (synonymou...
const dimensionedScalar eta(const label i, const dimensionedScalar &v) const
Return allocation coefficient.
bool writeData(Ostream &) const
Dummy write for regIOobject.
autoPtr< populationBalanceModel > clone() const
Return clone.
populationBalanceModel(const phaseSystem &fluid, const word &name)
Construct for a fluid.
const UPtrList< sizeGroup > & sizeGroups() const
Return the size groups belonging to this populationBalance.
const tmp< volScalarField > sigmaWithContinuousPhase(const phaseModel &dispersedPhase) const
Return the surface tension coefficient between a given dispersed.
const phaseCompressible::momentumTransportModel & continuousTurbulence() const
Return reference to momentumTransport model of the continuous phase.
void solve()
Solve the population balance equation.
Single size class fraction field representing a fixed particle volume as defined by the user through ...
Definition: sizeGroup.H:102
const phaseModel & phase() const
Return const-reference to the phase.
Definition: sizeGroupI.H:36
const Type & value() const
Return const reference to value.
const word & name() const
Return const reference to name.
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:604
SolverPerformance< Type > solve(const dictionary &)
Solve segregated or coupled returning the solution statistics.
Definition: fvMatrixSolve.C:58
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:406
Templated abstract base class for multiphase compressible turbulence models.
Class to represent an interface between phases. Derivations can further specify the configuration of ...
tmp< volScalarField > sigma() const
Surface tension coefficient.
const dimensionedScalar & residualAlpha() const
Return the residual phase-fraction for given phase.
Definition: phaseModel.C:133
const word & name() const
Return the name of this phase.
Definition: phaseModel.C:109
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 and model interfacial transfers between them.
Definition: phaseSystem.H:73
const phaseModelList & phases() const
Return the phase models.
Definition: phaseSystemI.H:41
HashPtrTable< volScalarField, phaseInterfaceKey, phaseInterfaceKey::hash > dmdtfTable
Definition: phaseSystem.H:95
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
A class for handling words, derived from string.
Definition: word.H:62
#define notImplemented(functionName)
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:343
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
Calculate the first temporal derivative.
Calculate the divergence of the given field.
Calculate the field for explicit evaluation of implicit and explicit sources.
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.
volScalarField alpha(IOobject("alpha", runTime.name(), mesh, IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE), lambda *max(Ua &U, zeroSensitivity))
const char *const group
Group name for atomic constants.
defineTypeNameAndDebug(constant, 0)
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 > > Sp(const volScalarField &sp, const VolField< Type > &vf)
Definition: fvcSup.C:67
tmp< VolField< Type > > div(const SurfaceField< Type > &ssf)
Definition: fvcDiv.C:47
tmp< fvMatrix< Type > > Sp(const volScalarField::Internal &, const VolField< Type > &)
tmp< fvMatrix< Type > > SuSp(const 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 > > ddt(const VolField< Type > &vf)
Definition: fvmDdt.C:46
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
static const zero Zero
Definition: zero.H:97
dimensionedScalar pos(const dimensionedScalar &ds)
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
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:251
const dimensionSet dimless
messageStream Info
const dimensionSet dimLength
labelList first(const UList< labelPair > &p)
Definition: patchToPatch.C:39
const dimensionSet dimTime
const dimensionSet dimDensity
const dimensionSet dimVolume
VolField< scalar > volScalarField
Definition: volFieldsFwd.H:61
dimensioned< scalar > mag(const dimensioned< Type > &)
word typedName(Name name)
Return the name of the object within the given type.
Definition: typeInfo.H:149
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
tmp< fvMatrix< Type > > correction(const fvMatrix< Type > &)
Return the correction form of the given matrix.
dimensionedScalar neg(const dimensionedScalar &ds)
const dimensionSet dimVelocity
error FatalError
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47