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-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
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
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
46void 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
78void 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 (
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 (
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
181void 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
221void 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
259void Foam::diameterModels::populationBalanceModel::
260birthByCoalescence
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
334void Foam::diameterModels::populationBalanceModel::
335deathByCoalescence
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
353void Foam::diameterModels::populationBalanceModel::
354birthByBreakup
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
391void Foam::diameterModels::populationBalanceModel::deathByBreakup(const label i)
392{
393 const sizeGroup& fi = sizeGroups_[i];
394
395 SuSp_[i] += breakupRate_()*fi.phase();
396}
397
398
399void 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
436void Foam::diameterModels::populationBalanceModel::
437birthByBinaryBreakup
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 (
496 Pair<word>::compare
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
509void Foam::diameterModels::populationBalanceModel::
510deathByBinaryBreakup
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
522void 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
646void Foam::diameterModels::populationBalanceModel::nucleation(const label i)
647{
648 Su_[i] += sizeGroups_[i].x()*nucleationRate_();
649}
650
651
652void 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
768void 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
789void 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
803Foam::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
828void 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
840bool 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 (
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 (
893 (
894 "Sui",
895 mesh_.time().timeName(),
896 mesh_
897 ),
898 mesh_,
900 ),
901 coalescence_
902 (
903 dict_.lookup("coalescenceModels"),
904 coalescenceModel::iNew(*this)
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 (
959 (
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 (
977 (
979 (
980 "breakupRate",
981 fluid_.time().timeName(),
982 mesh_
983 ),
984 mesh_,
986 )
987 );
988 }
989
990 if (binaryBreakup_.size() != 0)
991 {
992 binaryBreakupRate_.reset
993 (
995 (
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_,
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_,
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_,
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_,
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
1163(
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
1207(
1208 const phaseModel& dispersedPhase
1209) const
1210{
1211 return
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// ************************************************************************* //
label k
twoPhaseSystem & fluid
dimensioned< Type > weightedAverage(const DimensionedField< scalar, GeoMesh > &weightField) const
Calculate and return weighted average.
Internal & ref(const bool updateAccessTime=true)
Return a reference to the dimensioned internal field.
A HashTable of pointers to objects of type <T>, with deallocation management of the pointers.
Definition: HashPtrTable.H:68
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:170
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:65
const Time & time() const
Return Time associated with the objectRegistry.
Definition: IOobject.C:506
static word groupName(StringType base, const word &group)
Create dot-delimited name.group string.
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:62
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
static word timeName(const scalar t, const int precision=precision_)
Definition: Time.C:780
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: autoPtr.H:66
Base class for binary breakup models which give the breakup rate between a sizeGroup pair directly,...
Base class for breakup models which give a total breakup rate and a separate daughter size distributi...
Definition: breakupModel.H:56
Base class for coalescence models.
Constant dispersed-phase particle diameter model.
Base class for drift models.
Definition: driftModel.H:53
Base class for nucleation models.
Return a pointer to a new populationBalanceModel object created on.
Class that solves the univariate population balance equation by means of a class method (also called ...
bool writeData(Ostream &) const
Dummy write for regIOobject.
const dimensionedScalar gamma(const label i, const dimensionedScalar &v) const
Return allocation coefficient.
autoPtr< populationBalanceModel > clone() const
Return clone.
const phaseCompressibleTurbulenceModel & continuousTurbulence() const
Return reference to turbulence model of the continuous phase.
const tmp< volScalarField > sigmaWithContinuousPhase(const phaseModel &dispersedPhase) const
Return the surface tension coefficient between a given dispersed.
void solve()
Solve the population balance equation.
This class represents a single sizeGroup belonging to a velocityGroup. The main property of a sizeGro...
Definition: sizeGroup.H:99
const velocityGroup & VelocityGroup() const
Return const-reference to the velocityGroup.
Definition: sizeGroupI.H:45
const phaseModel & phase() const
Return const-reference to the phase.
Definition: sizeGroupI.H:38
const volScalarField & dmdt() const
Return const-reference to the mass transfer rate.
const tmp< fv::convectionScheme< scalar > > & mvConvection() const
Return const-reference to multivariate convectionScheme.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
T get(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
const Type & value() const
Return const reference to value.
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvMatrix.H:121
void relax(const scalar alpha)
Relax matrix (for steady-state solution).
Definition: fvMatrix.C:1092
SolverPerformance< Type > solve(const dictionary &)
Solve returning the solution statistics.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:290
Single incompressible phase derived from the phase-fraction. Used as part of the multiPhaseMixture fo...
Definition: phaseModel.H:61
Description for mass transfer between a pair of phases. The direction of the mass transfer is from th...
Definition: phasePair.H:56
Class to represent a system of phases and model interfacial transfers between them.
Definition: phaseSystem.H:76
const phaseModelList & phases() const
Return the phase models.
Definition: phaseSystemI.H:37
Single incompressible phase derived from the phase-fraction. Used as part of the multiPhaseMixture fo...
Definition: phase.H:57
const dimensionedScalar & rho() const
Return const-access to phase1 density.
Definition: phase.H:140
PIMPLE control class to supply convergence information/checks for the PIMPLE loop.
Definition: pimpleControl.H:58
Lookup type of boundary radiation properties.
Definition: lookup.H:66
regIOobject is an abstract class derived from IOobject to handle automatic object registration with t...
Definition: regIOobject.H:76
A class for managing temporary objects.
Definition: tmp.H:65
static const word propertiesName
Default name of the turbulence properties dictionary.
A class for handling words, derived from Foam::string.
Definition: word.H:68
thermo correct()
const scalar gamma
Definition: EEqn.H:9
dynamicFvMesh & mesh
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:517
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
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 finiteVolume matrix for implicit and explicit sources.
word timeName
Definition: getTimeIndex.H:3
label phasei
Definition: pEqn.H:27
constexpr const char *const group
Group name for atomic constants.
tmp< GeometricField< Type, fvPatchField, volMesh > > div(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcDiv.C:49
tmp< GeometricField< Type, fvPatchField, volMesh > > ddt(const dimensioned< Type > dt, const fvMesh &mesh)
Definition: fvcDdt.C:47
tmp< GeometricField< Type, fvPatchField, volMesh > > Su(const GeometricField< Type, fvPatchField, volMesh > &su, const GeometricField< Type, fvPatchField, volMesh > &vf)
Definition: fvcSup.C:46
tmp< fvMatrix< Type > > ddt(const GeometricField< Type, fvPatchField, volMesh > &vf)
Definition: fvmDdt.C:48
zeroField SuSp(const Foam::zero, const GeometricField< Type, fvPatchField, volMesh > &)
A no-op source.
zeroField Sp(const Foam::zero, const GeometricField< Type, fvPatchField, volMesh > &)
A no-op source.
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
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
dimensionedScalar pos(const dimensionedScalar &ds)
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
const dimensionSet dimless
Dimensionless.
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:52
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:53
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:82
const dimensionSet dimVelocity
messageStream Info
Information stream (stdout output on master, null elsewhere)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
dimensionedScalar neg(const dimensionedScalar &ds)
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
error FatalError
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:60
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
label timeIndex
Definition: getTimeIndex.H:30
volScalarField & alpha
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object.
Definition: stdFoam.H:381