phaseSystemSolve.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) 2013-2025 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 "phaseSystem.H"
27 #include "momentumTransferSystem.H"
28 
29 #include "subCycle.H"
30 
31 #include "fvcDdt.H"
32 #include "fvcDiv.H"
33 #include "fvcSnGrad.H"
34 #include "fvcFlux.H"
35 #include "fvcMeshPhi.H"
36 #include "fvcSup.H"
37 
38 #include "fvmDdt.H"
39 #include "fvmLaplacian.H"
40 #include "fvmSup.H"
41 
42 #include "upwind.H"
43 
44 #include "CMULES.H"
45 
46 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
47 
49 {
50  forAll(phases(), phasei)
51  {
52  if
53  (
54  mesh()
55  .solution()
56  .solverDict(phases()[phasei].volScalarField::name())
57  .lookupOrDefault<Switch>("implicitPhasePressure", false)
58  )
59  {
60  return true;
61  }
62  }
63 
64  return false;
65 }
66 
67 
69 (
70  const alphaControl& alphaControls,
71  const PtrList<volScalarField>& rAs,
72  const momentumTransferSystem& mts
73 )
74 {
75  const bool boundedPredictor =
76  !alphaControls.MULES.globalBounds
77  && alphaControls.MULES.extremaCoeff == 0;
78 
79  MULES::control MULESBD(alphaControls.MULES);
80  MULESBD.globalBounds = true;
81 
82  const label nAlphaSubCycles = alphaControls.nAlphaSubCycles;
83  const bool LTS = fv::localEulerDdt::enabled(mesh_);
84 
85  // Optional reference phase which is not solved for
86  // but obtained from the sum of the other phases
87  phaseModel* referencePhasePtr = nullptr;
88 
89  // The phases which are solved
90  // i.e. the moving phases less the optional reference phase
91  phaseModelPartialList solvePhases;
92  labelList solveMovingPhaseIndices;
93  label referenceMovingPhaseIndex = -1;
94 
95  if (referencePhaseName_ != word::null)
96  {
97  referencePhasePtr = &phases()[referencePhaseName_];
98 
99  solvePhases.setSize(movingPhases().size() - 1);
100  solveMovingPhaseIndices.setSize(solvePhases.size());
101 
102  label solvePhasesi = 0;
103  forAll(movingPhases(), movingPhasei)
104  {
105  if (&movingPhases()[movingPhasei] != referencePhasePtr)
106  {
107  solveMovingPhaseIndices[solvePhasesi] = movingPhasei;
108  solvePhases.set(solvePhasesi++, &movingPhases()[movingPhasei]);
109  }
110  else
111  {
112  referenceMovingPhaseIndex = movingPhasei;
113  }
114  }
115  }
116  else
117  {
118  solvePhases = movingPhases();
119  solveMovingPhaseIndices = identityMap(solvePhases.size());
120  }
121 
122  forAll(phases(), phasei)
123  {
124  phases()[phasei].correctBoundaryConditions();
125  }
126 
127  // Create sub-list of alphas and phis for the moving phases
128  UPtrList<const volScalarField> alphasMoving(movingPhases().size());
129  UPtrList<const surfaceScalarField> phisMoving(movingPhases().size());
130  forAll(movingPhases(), movingPhasei)
131  {
132  alphasMoving.set(movingPhasei, &movingPhases()[movingPhasei]);
133  phisMoving.set(movingPhasei, &movingPhases()[movingPhasei].phi()());
134  }
135 
136  // Calculate the void fraction
137  volScalarField alphaVoid
138  (
139  IOobject
140  (
141  "alphaVoid",
142  mesh_.time().name(),
143  mesh_
144  ),
145  mesh_,
147  );
148  forAll(stationaryPhases(), stationaryPhasei)
149  {
150  alphaVoid -= stationaryPhases()[stationaryPhasei];
151  }
152 
153  // Calculate the effective flux of the moving phases
154  tmp<surfaceScalarField> tphiMoving(phi_);
155  if (stationaryPhases().size())
156  {
157  tphiMoving = phi_/upwind<scalar>(mesh_, phi_).interpolate(alphaVoid);
158  }
159  const surfaceScalarField& phiMoving = tphiMoving();
160 
161  bool dilatation = false;
162  forAll(movingPhases(), movingPhasei)
163  {
164  if (movingPhases()[movingPhasei].divU().valid())
165  {
166  dilatation = true;
167  break;
168  }
169  }
170 
171  PtrList<volScalarField::Internal> Sps(phases().size());
172  PtrList<volScalarField::Internal> Sus(phases().size());
173 
174  forAll(movingPhases(), movingPhasei)
175  {
176  const phaseModel& phase = movingPhases()[movingPhasei];
177  const volScalarField& alpha = phase;
178  const label phasei = phase.index();
179 
180  Sps.set
181  (
182  phasei,
184  (
185  IOobject
186  (
187  IOobject::groupName("Sp", phase.name()),
188  mesh_.time().name(),
189  mesh_
190  ),
191  mesh_,
193  )
194  );
195 
196  Sus.set
197  (
198  phasei,
200  (
201  IOobject::groupName("Su", phase.name()),
202  min(alpha.v(), scalar(1))
203  *fvc::div(fvc::absolute(phi_, phase.U()))->v()
204  )
205  );
206 
207  if (dilatation)
208  {
209  // Construct the dilatation rate source term
211  (
213  (
214  "vDot",
215  mesh_,
217  )
218  );
219 
220  forAll(phases(), phasej)
221  {
222  const phaseModel& phase2 = phases()[phasej];
223  const volScalarField& alpha2 = phase2;
224 
225  if (&phase2 != &phase)
226  {
227  if (!phase.stationary() && phase.divU().valid())
228  {
229  vDot += alpha2()*phase.divU()()();
230  }
231 
232  if (!phase2.stationary() && phase2.divU().valid())
233  {
234  vDot -= alpha()*phase2.divU()()();
235  }
236  }
237  }
238 
239  volScalarField::Internal& Sp = Sps[phasei];
240  volScalarField::Internal& Su = Sus[phasei];
241 
242  forAll(vDot, celli)
243  {
244  if (vDot[celli] > 0)
245  {
246  Sp[celli] -=
247  vDot[celli]
248  /max
249  (
250  1 - alpha[celli],
251  alphaControls.vDotResidualAlpha
252  );
253  Su[celli] +=
254  vDot[celli]
255  /max
256  (
257  1 - alpha[celli],
258  alphaControls.vDotResidualAlpha
259  );
260  }
261  else if (vDot[celli] < 0)
262  {
263  Sp[celli] +=
264  vDot[celli]
265  /max
266  (
267  alpha[celli],
268  alphaControls.vDotResidualAlpha
269  );
270  }
271  }
272  }
273  }
274 
275  tmp<volScalarField> trSubDeltaT;
276 
277  if (LTS && nAlphaSubCycles > 1)
278  {
279  trSubDeltaT =
280  fv::localEulerDdt::localRSubDeltaT(mesh_, nAlphaSubCycles);
281  }
282 
283  // Cache a list of phase-fractions
284  UPtrList<volScalarField> alphas(phases().size());
285  forAll(phases(), phasei)
286  {
287  alphas.set(phasei, &phases()[phasei]);
288  }
289 
290  // Optional current phase-pressure diffusion fluxes
291  PtrList<surfaceScalarField> alphaPhiDByA(movingPhases().size());
292 
293  if (implicitPhasePressure() && rAs.size())
294  {
295  // Cache the phase-pressure diffusion coefficient
296  const surfaceScalarField alphaDByAf(mts.alphaDByAf(rAs));
297 
298  forAll(movingPhases(), movingPhasei)
299  {
300  const phaseModel& phase = movingPhases()[movingPhasei];
301  const volScalarField& alpha = phase;
302 
303  alphaPhiDByA.set
304  (
305  movingPhasei,
306  alphaDByAf*fvc::snGrad(alpha, "bounded")*mesh_.magSf()
307  );
308  }
309  }
310 
311  for
312  (
314  (
315  alphas,
316  nAlphaSubCycles
317  );
318  !(++alphaSubCycle).end();
319  )
320  {
321  // Bounded implicit predictor phase-fractions
322  PtrList<volScalarField> alphaPreds(movingPhases().size());
323 
324  // Bounded implicit predictor phase-fluxes
325  PtrList<surfaceScalarField> alphaPhiPreds(movingPhases().size());
326 
327  if (alphaControls.MULESCorr)
328  {
329  forAll(solvePhases, solvePhasei)
330  {
331  phaseModel& phase = solvePhases[solvePhasei];
332  volScalarField& alpha = phase;
333 
334  // Bounded implicit predictor matrix using the mean-flux only
335  // to ensure boundedness and phase-consistency
336  fvScalarMatrix alphaEqn
337  (
338  (
339  LTS
340  ? fv::localEulerDdtScheme<scalar>(mesh_).fvmDdt(alpha)
341  : fv::EulerDdtScheme<scalar>(mesh_).fvmDdt(alpha)
342  )
344  (
345  mesh_,
346  phiMoving,
347  upwind<scalar>(mesh_, phiMoving)
348  ).fvmDiv(phiMoving, alpha)
349  ==
350  Sus[phase.index()]
351  + fvm::Sp(Sps[phase.index()], alpha)
352  );
353 
354  alphaEqn.solve();
355 
356  alphaPreds.set
357  (
358  solveMovingPhaseIndices[solvePhasei],
359  alpha
360  );
361 
362  alphaPhiPreds.set
363  (
364  solveMovingPhaseIndices[solvePhasei],
365  alphaEqn.flux()
366  );
367 
368  Info<< phase.name() << " predicted fraction, min, max = "
369  << phase.weightedAverage(mesh_.Vsc()).value()
370  << ' ' << min(phase).value()
371  << ' ' << max(phase).value()
372  << endl;
373  }
374 
375  // Calculate the optional reference phase fraction and flux
376  // from the sum of the other phases
377  if (referencePhasePtr)
378  {
379  volScalarField& referenceAlpha = *referencePhasePtr;
380  referenceAlpha = alphaVoid;
381 
382  forAll(solvePhases, solvePhasei)
383  {
384  referenceAlpha -= solvePhases[solvePhasei];
385  }
386 
387  alphaPreds.set
388  (
389  referenceMovingPhaseIndex,
390  referenceAlpha
391  );
392 
393  alphaPhiPreds.set(referenceMovingPhaseIndex, phi_);
394 
395  forAll(solvePhases, solvePhasei)
396  {
397  alphaPhiPreds[referenceMovingPhaseIndex]
398  -= alphaPhiPreds[solveMovingPhaseIndices[solvePhasei]];
399  }
400  }
401 
402  // Cache the initial predicted phase-fluxes in the phase
403  forAll(solvePhases, solvePhasei)
404  {
405  phaseModel& phase = solvePhases[solvePhasei];
406  const label movingPhasei = solveMovingPhaseIndices[solvePhasei];
407 
408  if (alphaSubCycle.index() == 1)
409  {
410  phase.alphaPhiRef() = alphaPhiPreds[movingPhasei];
411  }
412  else
413  {
414  phase.alphaPhiRef() += alphaPhiPreds[movingPhasei];
415  }
416  }
417  }
418 
419  // Corrector loop
420  for (int acorr=0; acorr<alphaControls.nAlphaCorr; acorr++)
421  {
422  // High-order transport and optional compression phase-fluxes
423  PtrList<surfaceScalarField> alphaPhis(movingPhases().size());
424 
425  // Bounded phase-fluxes
426  PtrList<surfaceScalarField> alphaPhiBDs(movingPhases().size());
427 
428  forAll(movingPhases(), movingPhasei)
429  {
430  const phaseModel& phase = movingPhases()[movingPhasei];
431  const volScalarField& alpha = phase;
432 
433  alphaPhis.set
434  (
435  movingPhasei,
437  (
438  IOobject::groupName("alphaPhi", phase.name()),
439  fvc::flux
440  (
441  phase.phi()(),
442  alpha,
443  "div(phi," + alpha.name() + ')'
444  ),
445  phase.alphaPhi()().boundaryField().types()
446  )
447  );
448 
449  surfaceScalarField& alphaPhi = alphaPhis[movingPhasei];
450 
451  if (!cAlphas_.empty())
452  {
453  forAll(phases(), phasei)
454  {
455  const phaseModel& phase2 = phases()[phasei];
456  const volScalarField& alpha2 = phase2;
457 
458  if (&phase2 == &phase) continue;
459 
460  cAlphaTable::const_iterator cAlpha
461  (
462  cAlphas_.find(phaseInterface(phase, phase2))
463  );
464 
465  if (cAlpha != cAlphas_.end())
466  {
467  const surfaceScalarField phir
468  (
469  phase.phi() - phase2.phi()
470  );
471 
472  const surfaceScalarField phic
473  (
474  (mag(phi_) + mag(phir))/mesh_.magSf()
475  );
476 
477  const surfaceScalarField phirc
478  (
479  min(cAlpha()*phic, max(phic))
480  *nHatf(alpha, alpha2)
481  );
482 
483  const word phirScheme
484  (
485  "div(phir,"
486  + alpha2.name() + ',' + alpha.name()
487  + ')'
488  );
489 
490  alphaPhi += fvc::flux
491  (
492  -fvc::flux(-phirc, alpha2, phirScheme),
493  alpha,
494  phirScheme
495  );
496  }
497  }
498  }
499 
500  // Add the optional phase-pressure diffusion flux
501  if (alphaPhiDByA.set(movingPhasei))
502  {
503  alphaPhi += alphaPhiDByA[movingPhasei];
504  }
505 
506  // Correct the inflow and outflow boundary conditions
507  // of the phase flux
508  phase.correctInflowOutflow(alphaPhi);
509 
510  // Construct the optional bounded phase-flux
511  if (boundedPredictor)
512  {
513  alphaPhiBDs.set
514  (
515  movingPhasei,
517  (
518  IOobject::groupName("alphaPhiBD", phase.name()),
519  upwind<scalar>(mesh_, phase.phi()()).flux(alpha),
520  phase.alphaPhi()().boundaryField().types()
521  )
522  );
523 
524  const surfaceScalarField::Boundary& alphaPhiBf =
525  alphaPhi.boundaryField();
526 
527  surfaceScalarField::Boundary& alphaPhiBDBf =
528  alphaPhiBDs[movingPhasei].boundaryFieldRef();
529 
530  // For non-coupled boundaries
531  // set the bounded flux to the high-order flux
532  forAll(alphaPhiBDBf, patchi)
533  {
534  fvsPatchScalarField& alphaPhiBDPf =
535  alphaPhiBDBf[patchi];
536 
537  if (!alphaPhiBDPf.coupled())
538  {
539  alphaPhiBDPf = alphaPhiBf[patchi];
540  }
541  }
542  }
543  }
544 
545  if (alphaControls.MULESCorr)
546  {
547  // Set local reference to the bounded or high-order phase-fluxes
548  PtrList<surfaceScalarField>& alphaPhiCorrs =
549  boundedPredictor ? alphaPhiBDs : alphaPhis;
550 
551  forAll(movingPhases(), movingPhasei)
552  {
553  const phaseModel& phase = movingPhases()[movingPhasei];
554  const volScalarField& alpha = phase;
555 
556  // Calculate the phase-flux correction
557  // with respect to the bounded implicit prediction
558  alphaPhiCorrs[movingPhasei] ==
559  alphaPhiCorrs[movingPhasei]
560  - alphaPhiPreds[movingPhasei];
561 
562  // Limit the phase-flux correction
564  (
565  boundedPredictor
566  ? MULESBD
567  : alphaControls.MULES,
569  alpha,
570  alphaPhiPreds[movingPhasei],
571  alphaPhiCorrs[movingPhasei],
572  Sps[phase.index()],
573  min(alphaVoid.primitiveField(), phase.alphaMax())(),
574  zeroField()
575  );
576  }
577 
578  // Limit the flux corrections of the phases
579  // to ensure the phase fractions sum to 1
580  MULES::limitSumCorr(alphasMoving, alphaPhiCorrs, phiMoving);
581 
582  // Correct the phase-fractions from the phase-flux corrections
583  forAll(solvePhases, solvePhasei)
584  {
585  phaseModel& phase = solvePhases[solvePhasei];
586  volScalarField& alpha = phase;
587  const label movingPhasei =
588  solveMovingPhaseIndices[solvePhasei];
589 
590  const surfaceScalarField& alphaPhiCorr =
591  alphaPhiCorrs[movingPhasei];
592 
594  (
596  alpha,
597  alphaPhiCorr,
598  Sps[phase.index()]
599  );
600  }
601  }
602  else
603  {
604  forAll(movingPhases(), movingPhasei)
605  {
606  const phaseModel& phase = movingPhases()[movingPhasei];
607  const volScalarField& alpha = phase;
608 
610  (
611  boundedPredictor
612  ? MULESBD
613  : alphaControls.MULES,
615  alpha,
616  phiMoving,
617  boundedPredictor
618  ? alphaPhiBDs[movingPhasei]
619  : alphaPhis[movingPhasei],
620  Sps[phase.index()],
621  Sus[phase.index()],
622  min(alphaVoid.primitiveField(), phase.alphaMax())(),
623  zeroField(),
624  false
625  );
626  }
627 
628  // Limit the flux of the phases
629  // to ensure the phase fractions sum to 1
631  (
632  alphasMoving,
633  boundedPredictor ? alphaPhiBDs : alphaPhis,
634  phiMoving
635  );
636 
637  forAll(solvePhases, solvePhasei)
638  {
639  phaseModel& phase = solvePhases[solvePhasei];
640  volScalarField& alpha = phase;
641 
642  const surfaceScalarField& alphaPhi =
643  (boundedPredictor ? alphaPhiBDs : alphaPhis)
644  [solveMovingPhaseIndices[solvePhasei]];
645 
647  (
649  alpha,
650  alphaPhi,
651  Sps[phase.index()],
652  Sus[phase.index()]
653  );
654  }
655  }
656 
657  // Update the phase-fraction of the reference phase
658  if (referencePhasePtr)
659  {
660  volScalarField& referenceAlpha = *referencePhasePtr;
661  referenceAlpha = alphaVoid;
662 
663  forAll(solvePhases, solvePhasei)
664  {
665  referenceAlpha -= solvePhases[solvePhasei];
666  }
667  }
668 
669  if (boundedPredictor)
670  {
671  // Limit the high-order phase-fluxes
672  // to ensure the phase fractions sum to 1
673  MULES::limitSum(alphasMoving, alphaPhis, phiMoving);
674 
675  // Calculate the phase-flux corrections
676  // with respect to the current prediction
677  if (alphaControls.MULESCorr)
678  {
679  forAll(movingPhases(), movingPhasei)
680  {
681  alphaPhis[movingPhasei] -=
682  (
683  alphaPhiPreds[movingPhasei]
684  + alphaPhiBDs[movingPhasei]
685  );
686  }
687  }
688  else
689  {
690  forAll(movingPhases(), movingPhasei)
691  {
692  alphaPhis[movingPhasei] -= alphaPhiBDs[movingPhasei];
693  }
694  }
695 
696  // Limit the phase-flux corrections
697  forAll(movingPhases(), movingPhasei)
698  {
699  const phaseModel& phase = movingPhases()[movingPhasei];
700  const volScalarField& alpha = phase;
701 
703  (
704  alphaControls.MULES,
706  alpha,
707  alphaControls.MULESCorr
708  ? alphaPhiPreds[movingPhasei]
709  : alphaPhiBDs[movingPhasei],
710  alphaPhis[movingPhasei],
711  Sps[phase.index()],
712  min(alphaVoid.primitiveField(), phase.alphaMax())(),
713  zeroField()
714  );
715  }
716 
717  // Limit the flux corrections of the phases
718  // to ensure the phase fractions sum to 1
719  MULES::limitSumCorr(alphasMoving, alphaPhis, phiMoving);
720 
721  // Correct the phase-fractions from the phase-flux corrections
722  forAll(solvePhases, solvePhasei)
723  {
724  phaseModel& phase = solvePhases[solvePhasei];
725  volScalarField& alpha = phase;
726 
727  const surfaceScalarField& alphaPhi =
728  alphaPhis[solveMovingPhaseIndices[solvePhasei]];
729 
731  (
733  alpha,
734  alphaPhi,
735  Sps[phase.index()]
736  );
737  }
738  }
739 
740  // Add the optional implicit phase pressure contribution
741  if (implicitPhasePressure() && rAs.size())
742  {
743  // Cache the phase-pressure diffusion coefficient
744  const surfaceScalarField alphaDByAf(mts.alphaDByAf(rAs));
745 
746  // Add the implicit phase-pressure diffusion contribution
747  // to the phase-fractions and phase-fluxes
748  forAll(solvePhases, solvePhasei)
749  {
750  phaseModel& phase = solvePhases[solvePhasei];
751  volScalarField& alpha = phase;
752 
753  fvScalarMatrix alphaEqn
754  (
756  - fvm::laplacian(alphaDByAf, alpha, "bounded")
757  );
758 
759  alphaEqn.solve
760  (
761  mesh_.solution().solverDict("alpha")
762  .optionalSubDict("phasePressure")
763  );
764 
765  alphaPhis[solveMovingPhaseIndices[solvePhasei]] +=
766  alphaEqn.flux();
767  }
768  }
769 
770  // Report the phase fractions and the phase fraction sum
771  forAll(solvePhases, solvePhasei)
772  {
773  const phaseModel& phase = solvePhases[solvePhasei];
774 
775  Info<< phase.name() << " fraction, min, max = "
776  << phase.weightedAverage(mesh_.Vsc()).value()
777  << ' ' << min(phase).value()
778  << ' ' << max(phase).value()
779  << endl;
780  }
781 
782  // Calculate the reference phase-fraction from the other phases
783  // or re-scale the all the phase fractions to sum to 1 exactly
784  if (referencePhasePtr)
785  {
786  volScalarField& referenceAlpha = *referencePhasePtr;
787  referenceAlpha = alphaVoid;
788 
789  forAll(solvePhases, solvePhasei)
790  {
791  referenceAlpha -= solvePhases[solvePhasei];
792  }
793  }
794  else
795  {
796  volScalarField::Internal sumAlphaMoving
797  (
798  IOobject
799  (
800  "sumAlphaMoving",
801  mesh_.time().name(),
802  mesh_
803  ),
804  mesh_,
806  );
807 
808  forAll(movingPhases(), movingPhasei)
809  {
810  sumAlphaMoving += movingPhases()[movingPhasei];
811  }
812 
813  Info<< "Phase-sum volume fraction, min, max = "
814  << (sumAlphaMoving + 1 - alphaVoid())()
815  .weightedAverage(mesh_.Vsc()).value()
816  << ' ' << min(sumAlphaMoving + 1 - alphaVoid()).value()
817  << ' ' << max(sumAlphaMoving + 1 - alphaVoid()).value()
818  << endl;
819 
820  if (alphaControls.clip)
821  {
822  // Scale moving phase-fractions to sum to alphaVoid
823  forAll(movingPhases(), movingPhasei)
824  {
825  movingPhases()[movingPhasei].internalFieldRef() *=
826  alphaVoid()/sumAlphaMoving;
827  }
828  }
829  }
830 
831  if (alphaControls.MULESCorr)
832  {
833  // For the semi-implicit algorithm relax the phase-fractions
834  // and the phase-flux accumulation
835  if (boundedPredictor)
836  {
837  forAll(movingPhases(), movingPhasei)
838  {
839  phaseModel& phase = movingPhases()[movingPhasei];
840  volScalarField& alpha = phase;
841 
842  alpha +=
843  (1 - alphaControls.MULESCorrRelax)
844  *(alphaPreds[movingPhasei] - alpha);
845 
846  alphaPreds[movingPhasei] = alpha;
847 
848  const surfaceScalarField alphaPhiInc
849  (
850  alphaControls.MULESCorrRelax
851  *(
852  alphaPhiBDs[movingPhasei]
853  + alphaPhis[movingPhasei]
854  )
855  );
856  phase.alphaPhiRef() += alphaPhiInc;
857  alphaPhiPreds[movingPhasei] += alphaPhiInc;
858  }
859  }
860  else
861  {
862  forAll(movingPhases(), movingPhasei)
863  {
864  phaseModel& phase = movingPhases()[movingPhasei];
865  volScalarField& alpha = phase;
866 
867  alpha +=
868  (1 - alphaControls.MULESCorrRelax)
869  *(alphaPreds[movingPhasei] - alpha);
870 
871  alphaPreds[movingPhasei] = alpha;
872 
873  const surfaceScalarField alphaPhiInc
874  (
875  alphaControls.MULESCorrRelax*alphaPhis[movingPhasei]
876  );
877  phase.alphaPhiRef() += alphaPhiInc;
878  alphaPhiPreds[movingPhasei] += alphaPhiInc;
879  }
880  }
881  }
882  else if (acorr == alphaControls.nAlphaCorr-1)
883  {
884  // Accumulate the phase-fluxes
885  if (boundedPredictor)
886  {
887  forAll(movingPhases(), movingPhasei)
888  {
889  phaseModel& phase = movingPhases()[movingPhasei];
890 
891  if (alphaSubCycle.index() == 1)
892  {
893  phase.alphaPhiRef() = alphaPhiBDs[movingPhasei];
894  phase.alphaPhiRef() += alphaPhis[movingPhasei];
895  }
896  else
897  {
898  phase.alphaPhiRef() += alphaPhiBDs[movingPhasei];
899  phase.alphaPhiRef() += alphaPhis[movingPhasei];
900  }
901  }
902  }
903  else
904  {
905  forAll(movingPhases(), movingPhasei)
906  {
907  phaseModel& phase = movingPhases()[movingPhasei];
908 
909  if (alphaSubCycle.index() == 1)
910  {
911  phase.alphaPhiRef() = alphaPhis[movingPhasei];
912  }
913  else
914  {
915  phase.alphaPhiRef() += alphaPhis[movingPhasei];
916  }
917  }
918  }
919  }
920  }
921  }
922 
923  if (nAlphaSubCycles > 1)
924  {
925  forAll(solvePhases, solvePhasei)
926  {
927  solvePhases[solvePhasei].alphaPhiRef() /= nAlphaSubCycles;
928  }
929  }
930 
931  forAll(solvePhases, solvePhasei)
932  {
933  phaseModel& phase = solvePhases[solvePhasei];
934  volScalarField& alpha = phase;
935 
936  phase.alphaRhoPhiRef() =
937  fvc::interpolate(phase.rho())*phase.alphaPhi();
938 
939  if (alphaControls.clip)
940  {
941  // Clip the phase-fractions between 0 and alphaMax
942  alpha.maxMin(0, phase.alphaMax());
943 
944  if (stationaryPhases().size())
945  {
946  alpha = min(alpha, alphaVoid);
947  }
948  }
949  }
950 
951  // Calculate the optional reference phase fraction and flux
952  // from the sum of the other phases
953  if (referencePhasePtr)
954  {
955  phaseModel& referencePhase = *referencePhasePtr;
956 
957  referencePhase.alphaPhiRef() = phi_;
958 
959  forAll(solvePhases, solvePhasei)
960  {
961  referencePhase.alphaPhiRef() -=
962  solvePhases[solvePhasei].alphaPhi();
963  }
964 
965  referencePhase.alphaRhoPhiRef() =
966  fvc::interpolate(referencePhase.rho())
967  *referencePhase.alphaPhi();
968 
969  volScalarField& referenceAlpha = referencePhase;
970  referenceAlpha = alphaVoid;
971 
972  forAll(solvePhases, solvePhasei)
973  {
974  referenceAlpha -= solvePhases[solvePhasei];
975  }
976  }
977 }
978 
979 
980 // ************************************************************************* //
CMULES: Multidimensional universal limiter for explicit corrected implicit solution.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
static tmp< DimensionedField< Type, GeoMesh, PrimitiveField > > New(const word &name, const GeoMesh &mesh, const dimensionSet &, const PrimitiveField< Type > &)
Return a temporary field constructed from name, mesh,.
dimensioned< Type > weightedAverage(const DimensionedField< scalar, GeoMesh, PrimitiveField2 > &) const
Calculate and return weighted average.
Generic GeometricBoundaryField class.
Generic GeometricField class.
void maxMin(const dimensioned< Type > &minDt, const dimensioned< Type > &maxDt)
const Boundary & boundaryField() const
Return const-reference to the boundary field.
const Internal::FieldType & primitiveField() const
Return a const-reference to the primitive field.
const Internal & v() const
Return a const-reference to the dimensioned internal field.
Definition: volFieldsI.H:29
static tmp< GeometricField< Type, GeoMesh, PrimitiveField > > New(const word &name, const Internal &, const PtrList< Patch > &, const HashPtrTable< Source > &=HashPtrTable< Source >())
Return a temporary field constructed from name,.
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
const word & name() const
Return name.
Definition: IOobject.H:307
static word groupName(Name name, const word &group)
void setSize(const label)
Reset size of List.
Definition: List.C:281
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
bool set(const label) const
Is element set.
Definition: PtrListI.H:62
bool set(const label) const
Is element set.
Definition: UPtrListI.H:87
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
void setSize(const label)
Reset size of UPtrList. This can only be used to set the size.
Definition: UPtrList.C:61
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvMatrix.H:118
SolverPerformance< Type > solve(const dictionary &)
Solve segregated or coupled returning the solution statistics.
Definition: fvMatrixSolve.C:58
tmp< SurfaceField< Type > > flux() const
Return the face-flux field from the matrix.
Definition: fvMatrix.C:963
Basic first-order Euler implicit/explicit ddt using only the current and previous time-step values.
Basic second-order convection using face-gradients and Gauss' theorem.
Local time-step first-order Euler implicit/explicit ddt.
static tmp< volScalarField > localRSubDeltaT(const fvMesh &mesh, const label nAlphaSubCycles)
Calculate and return the reciprocal of the local sub-cycling.
Definition: localEulerDdt.C:70
static bool enabled(const fvMesh &mesh)
Return true if LTS is enabled.
Definition: localEulerDdt.C:37
An abstract base class with a fat-interface to all derived classes covering all possible ways in whic...
Definition: fvsPatchField.H:86
virtual bool coupled() const
Return true if this patch field is coupled.
A class representing the concept of a GeometricField of 1 used to avoid unnecessary manipulations for...
virtual tmp< SurfaceField< Type > > flux(const VolField< Type > &) const
Return the interpolation weighting factors.
Class which provides interfacial momentum transfer between a number of phases. Drag,...
virtual tmp< surfaceScalarField > alphaDByAf(const PtrList< volScalarField > &rAs) const
Return the phase diffusivity.
Class to represent an interface between phases. Derivations can further specify the configuration of ...
scalar alphaMax() const
Return the maximum phase-fraction (e.g. packing limit)
Definition: phaseModel.C:122
virtual bool stationary() const =0
Return whether the phase is stationary.
virtual const autoPtr< volScalarField > & divU() const =0
Return the phase dilatation rate (d(alpha)/dt + div(alpha*phi))
virtual tmp< surfaceScalarField > phi() const =0
Return the volumetric flux.
label index() const
Return the index of the phase.
Definition: phaseModel.C:104
virtual surfaceScalarField & alphaRhoPhiRef()=0
Access the mass flux of the phase.
virtual tmp< volVectorField > U() const =0
Return the velocity.
virtual surfaceScalarField & alphaPhiRef()=0
Access the volumetric flux of the phase.
void correctInflowOutflow(surfaceScalarField &alphaPhi) const
Ensure that the flux at inflow/outflow BCs is preserved.
Definition: phaseModel.C:198
const word & name() const
Return the name of this phase.
Definition: phaseModel.C:92
virtual const volScalarField & rho() const =0
Return the density field.
virtual tmp< surfaceScalarField > alphaPhi() const =0
Return the volumetric flux of the phase.
const phaseModelList & phases() const
Return the phase models.
Definition: phaseSystemI.H:104
bool implicitPhasePressure() const
Returns true if the phase pressure is treated implicitly.
const fvMesh & mesh() const
Return the mesh.
Definition: phaseSystemI.H:91
void solve(const alphaControl &alphaControls, const PtrList< volScalarField > &rAs, const momentumTransferSystem &mts)
Solve for the phase fractions.
Selector class for relaxation factors, solver type and solution.
Definition: solution.H:51
Perform a subCycleTime on a field or list of fields.
Definition: subCycle.H:235
static tmp< SurfaceField< Type > > interpolate(const VolField< Type > &, const tmp< surfaceScalarField > &, const tmp< surfaceScalarField > &)
Return the face-interpolate of the given cell field.
A class for managing temporary objects.
Definition: tmp.H:55
A class for handling words, derived from string.
Definition: word.H:63
static const word null
An empty word.
Definition: word.H:78
A class representing the concept of a field of 0 used to avoid unnecessary manipulations for objects ...
Definition: zeroField.H:53
Calculate the first temporal derivative.
Calculate the divergence of the given field.
Calculate the face-flux of the given field.
Calculate the mesh motion flux and convert fluxes from absolute to relative and back.
Calculate the snGrad of the given volField.
Calculate the field for explicit evaluation of implicit and explicit sources.
Calculate the matrix for the first temporal derivative.
Calculate the matrix for the laplacian of the field.
Calculate the matrix for implicit and explicit sources.
label patchi
volScalarField alpha(IOobject("alpha", runTime.name(), mesh, IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE), lambda *max(Ua &U, zeroSensitivity))
void correct(const RdeltaTType &rDeltaT, const RhoType &rho, volScalarField &psi, const surfaceScalarField &phiCorr, const SpType &Sp)
void limitSum(const UPtrList< const volScalarField > &psis, const PtrList< surfaceScalarField > &alphaPhiBDs, UPtrList< surfaceScalarField > &psiPhis, const surfaceScalarField &phi)
Definition: MULES.C:280
void limitSumCorr(UPtrList< scalarField > &psiPhiCorrs)
Definition: MULES.C:78
void limitCorr(const control &controls, const RdeltaTType &rDeltaT, const RhoType &rho, const volScalarField &psi, const surfaceScalarField &phiBD, surfaceScalarField &phiCorr, const SpType &Sp, const PsiMaxType &psiMax, const PsiMinType &psiMin)
void explicitSolve(const RdeltaTType &rDeltaT, const RhoType &rho, volScalarField &psi, const surfaceScalarField &psiPhi, const SpType &Sp, const SuType &Su)
void limit(const control &controls, const RdeltaTType &rDeltaT, const RhoType &rho, const volScalarField &psi, const surfaceScalarField &phi, surfaceScalarField &psiPhi, const SpType &Sp, const SuType &Su, const PsiMaxType &psiMax, const PsiMinType &psiMin, const bool returnCorr)
bool valid(const PtrList< ModelType > &l)
tmp< SurfaceField< typename innerProduct< vector, Type >::type > > flux(const VolField< Type > &vf)
Return the face-flux field obtained from the given volVectorField.
static tmp< SurfaceField< Type > > interpolate(const VolField< Type > &tvf, const surfaceScalarField &faceFlux, Istream &schemeData)
Interpolate field onto faces using scheme given by Istream.
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< surfaceScalarField > absolute(const tmp< surfaceScalarField > &tphi, const volVectorField &U)
Return the given relative flux in absolute form.
Definition: fvcMeshPhi.C:202
tmp< SurfaceField< Type > > snGrad(const VolField< Type > &vf, const word &name)
Definition: fvcSnGrad.C:45
tmp< fvMatrix< Type > > laplacian(const VolField< Type > &vf, const word &name)
Definition: fvmLaplacian.C:47
tmp< fvMatrix< Type > > Sp(const volScalarField::Internal &, const VolField< Type > &)
tmp< fvMatrix< Type > > ddt(const VolField< Type > &vf)
Definition: fvmDdt.C:46
const dimensionSet & dimless
Definition: dimensions.C:138
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
messageStream Info
const dimensionSet & dimTime
Definition: dimensions.C:142
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Switch globalBounds
Optional switch to select global bounds only.
Definition: MULES.H:80
scalar extremaCoeff
Optional coefficient to relax the local boundedness constraint.
Definition: MULES.H:90
alpha solution control structure
Definition: phaseSystem.H:81
Switch clip
Optionally clip the phase-fractions.
Definition: phaseSystem.H:109
label nAlphaCorr
Number of alpha correctors.
Definition: phaseSystem.H:92
scalar vDotResidualAlpha
Compressibility source stabilisation tolerance.
Definition: phaseSystem.H:101
MULES::control MULES
MULES controls.
Definition: phaseSystem.H:104
bool MULESCorr
Semi-implicit MULES switch.
Definition: phaseSystem.H:95
scalar MULESCorrRelax
Explicit correction relaxation factor (defaults to 0.5)
Definition: phaseSystem.H:98
label nAlphaSubCycles
Number of alpha equation sub-cycles.
Definition: phaseSystem.H:87