/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2015-2023 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "MomentumTransferPhaseSystem.H"
#include "dragModel.H"
#include "virtualMassModel.H"
#include "liftModel.H"
#include "wallLubricationModel.H"
#include "turbulentDispersionModel.H"
#include "fvmDdt.H"
#include "fvmDiv.H"
#include "fvmSup.H"
#include "fvcDdt.H"
#include "fvcDiv.H"
#include "fvcFlux.H"
#include "fvcSnGrad.H"
#include "fvcMeshPhi.H"
#include "fvcReconstruct.H"
#include "pimpleNoLoopControl.H"
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template
void Foam::MomentumTransferPhaseSystem::addDmdtUfs
(
const phaseSystem::dmdtfTable& dmdtfs,
phaseSystem::momentumTransferTable& eqns
)
{
forAllConstIter(phaseSystem::dmdtfTable, dmdtfs, dmdtfIter)
{
const phaseInterface interface(*this, dmdtfIter.key());
const volScalarField& dmdtf = *dmdtfIter();
const volScalarField dmdtf21(posPart(dmdtf));
const volScalarField dmdtf12(negPart(dmdtf));
phaseModel& phase1 = this->phases()[interface.phase1().name()];
phaseModel& phase2 = this->phases()[interface.phase2().name()];
if (!phase1.stationary())
{
*eqns[phase1.name()] +=
dmdtf21*phase2.U() + fvm::Sp(dmdtf12, phase1.URef());
}
if (!phase2.stationary())
{
*eqns[phase2.name()] -=
dmdtf12*phase1.U() + fvm::Sp(dmdtf21, phase2.URef());
}
}
}
template
void Foam::MomentumTransferPhaseSystem::addTmpField
(
tmp& result,
const tmp& field
) const
{
if (result.valid())
{
result.ref() += field;
}
else
{
if (field.isTmp())
{
result = field;
}
else
{
result = field().clone();
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template
Foam::MomentumTransferPhaseSystem::
MomentumTransferPhaseSystem
(
const fvMesh& mesh
)
:
BasePhaseSystem(mesh)
{
this->generateInterfacialModels(dragModels_);
this->generateInterfacialModels(virtualMassModels_);
this->generateInterfacialModels(liftModels_);
this->generateInterfacialModels(wallLubricationModels_);
this->generateInterfacialModels(turbulentDispersionModels_);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template
Foam::MomentumTransferPhaseSystem::
~MomentumTransferPhaseSystem()
{}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template
Foam::autoPtr
Foam::MomentumTransferPhaseSystem::momentumTransfer()
{
// Create a momentum transfer matrix for each phase
autoPtr eqnsPtr
(
new phaseSystem::momentumTransferTable()
);
phaseSystem::momentumTransferTable& eqns = eqnsPtr();
forAll(this->movingPhases(), movingPhasei)
{
const phaseModel& phase = this->movingPhases()[movingPhasei];
eqns.insert
(
phase.name(),
new fvVectorMatrix(phase.U(), dimMass*dimVelocity/dimTime)
);
}
// Initialise Kds table if required
if (!Kds_.size())
{
forAllConstIter
(
dragModelTable,
dragModels_,
dragModelIter
)
{
const phaseInterface& interface = dragModelIter()->interface();
Kds_.insert
(
dragModelIter.key(),
new volScalarField
(
IOobject
(
IOobject::groupName("Kd", interface.name()),
this->mesh().time().name(),
this->mesh()
),
this->mesh(),
dimensionedScalar(dragModel::dimK, 0)
)
);
}
}
// Update the drag coefficients
forAllConstIter
(
dragModelTable,
dragModels_,
dragModelIter
)
{
*Kds_[dragModelIter.key()] = dragModelIter()->K();
// Zero-gradient the drag coefficient to boundaries with fixed velocity
const phaseInterface& interface = dragModelIter()->interface();
volScalarField& K = *Kds_[dragModelIter.key()];
forAll(K.boundaryField(), patchi)
{
if
(
(
!interface.phase1().stationary()
&& interface.phase1().U()()
.boundaryField()[patchi].fixesValue()
)
&& (
!interface.phase2().stationary()
&& interface.phase2().U()()
.boundaryField()[patchi].fixesValue()
)
)
{
K.boundaryFieldRef()[patchi] =
K.boundaryField()[patchi].patchInternalField();
}
}
}
// Initialise Vms table if required
if (!Vms_.size())
{
forAllConstIter
(
virtualMassModelTable,
virtualMassModels_,
virtualMassModelIter
)
{
const phaseInterface& interface =
virtualMassModelIter()->interface();
Vms_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName("Vm", interface.name()),
this->mesh().time().name(),
this->mesh()
),
this->mesh(),
dimensionedScalar(virtualMassModel::dimK, 0)
)
);
}
}
// Update the virtual mass coefficients
forAllConstIter
(
virtualMassModelTable,
virtualMassModels_,
virtualMassModelIter
)
{
*Vms_[virtualMassModelIter.key()] = virtualMassModelIter()->K();
}
// Add the virtual mass force
forAllConstIter(VmTable, Vms_, VmIter)
{
const volScalarField& Vm(*VmIter());
const phaseInterface interface(*this, VmIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
if (!phase.stationary())
{
fvVectorMatrix& eqn = *eqns[phase.name()];
const volVectorField& U = eqn.psi();
const surfaceScalarField& phi = phase.phiRef();
const tmp taphi(fvc::absolute(phi, U));
const surfaceScalarField& aphi(taphi());
const volScalarField VmPhase
(
(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*Vm
);
eqn -=
VmPhase
*(
fvm::ddt(U)
+ fvm::div(aphi, U) - fvm::Sp(fvc::div(aphi), U)
- otherPhase.DUDt()
)
+ this->MRF_.DDt(VmPhase, U - otherPhase.U());
}
}
}
return eqnsPtr;
}
template
Foam::autoPtr
Foam::MomentumTransferPhaseSystem::momentumTransferf()
{
// Create a momentum transfer matrix for each phase
autoPtr eqnsPtr
(
new phaseSystem::momentumTransferTable()
);
phaseSystem::momentumTransferTable& eqns = eqnsPtr();
forAll(this->movingPhases(), movingPhasei)
{
const phaseModel& phase = this->movingPhases()[movingPhasei];
eqns.insert
(
phase.name(),
new fvVectorMatrix(phase.U(), dimMass*dimVelocity/dimTime)
);
}
// Initialise Kdfs table if required
if (!Kdfs_.size())
{
forAllConstIter
(
dragModelTable,
dragModels_,
dragModelIter
)
{
const phaseInterface& interface = dragModelIter()->interface();
Kdfs_.insert
(
dragModelIter.key(),
new surfaceScalarField
(
IOobject
(
IOobject::groupName("Kdf", interface.name()),
this->mesh().time().name(),
this->mesh()
),
this->mesh(),
dimensionedScalar(dragModel::dimK, 0)
)
);
}
}
// Update the drag coefficients
forAllConstIter
(
dragModelTable,
dragModels_,
dragModelIter
)
{
*Kdfs_[dragModelIter.key()] = dragModelIter()->Kf();
}
// Initialise Vms table if required
if (!Vms_.size())
{
forAllConstIter
(
virtualMassModelTable,
virtualMassModels_,
virtualMassModelIter
)
{
const phaseInterface& interface =
virtualMassModelIter()->interface();
Vms_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName("Vm", interface.name()),
this->mesh().time().name(),
this->mesh()
),
this->mesh(),
dimensionedScalar(virtualMassModel::dimK, 0)
)
);
}
}
// Update the virtual mass coefficients
forAllConstIter
(
virtualMassModelTable,
virtualMassModels_,
virtualMassModelIter
)
{
*Vms_[virtualMassModelIter.key()] = virtualMassModelIter()->K();
}
// Create U & grad(U) fields
PtrList UgradUs(this->phaseModels_.size());
forAll(this->phaseModels_, phasei)
{
const phaseModel& phase = this->phaseModels_[phasei];
if (!phase.stationary())
{
const volVectorField& U = phase.URef();
const surfaceScalarField& phi = phase.phiRef();
const tmp taphi(fvc::absolute(phi, U));
const surfaceScalarField& aphi(taphi());
UgradUs.set
(
phasei,
new fvVectorMatrix
(
fvm::div(aphi, U) - fvm::Sp(fvc::div(aphi), U)
+ this->MRF().DDt(U)
)
);
}
}
// Add the virtual mass force
forAllConstIter(VmTable, Vms_, VmIter)
{
const volScalarField& Vm(*VmIter());
const phaseInterface interface(*this, VmIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
if (!phase.stationary())
{
const volScalarField VmPhase
(
(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*Vm
);
*eqns[phase.name()] -=
VmPhase
*(
UgradUs[phase.index()]
- (UgradUs[otherPhase.index()] & otherPhase.U())
);
}
}
}
return eqnsPtr;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::KdVmfs() const
{
PtrList KdVmfs(this->phaseModels_.size());
// Add the implicit part of the drag force
forAllConstIter(KdfTable, Kdfs_, KdfIter)
{
const surfaceScalarField& Kf(*KdfIter());
const phaseInterface interface(*this, KdfIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
const surfaceScalarField alphaf
(
fvc::interpolate(otherPhase)
);
addField
(
phase,
"KdVmf",
(alphaf/max(alphaf, otherPhase.residualAlpha()))
*Kf,
KdVmfs
);
}
}
// Add the implicit part of the virtual mass force
forAllConstIter(VmTable, Vms_, VmIter)
{
const volScalarField& Vm(*VmIter());
const phaseInterface interface(*this, VmIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
const volScalarField VmPhase
(
(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*Vm
);
addField(phase, "KdVmf", byDt(fvc::interpolate(VmPhase)), KdVmfs);
}
}
this->fillFields("KdVmf", dimDensity/dimTime, KdVmfs);
return KdVmfs;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::Fs() const
{
PtrList Fs(this->phaseModels_.size());
// Add the lift force
forAllConstIter
(
liftModelTable,
liftModels_,
liftModelIter
)
{
const phaseInterface& interface = liftModelIter()->interface();
const volVectorField F(liftModelIter()->F());
addField
(
interface.phase1(),
"F",
fvc::flux(F),
Fs
);
addField
(
interface.phase2(),
"F",
-fvc::flux(F),
Fs
);
}
// Add the wall lubrication force
forAllConstIter
(
wallLubricationModelTable,
wallLubricationModels_,
wallLubricationModelIter
)
{
const phaseInterface& interface =
wallLubricationModelIter()->interface();
const volVectorField F(wallLubricationModelIter()->F());
addField
(
interface.phase1(),
"F",
fvc::flux(F),
Fs
);
addField
(
interface.phase2(),
"F",
-fvc::flux(F),
Fs
);
}
// Add the phase pressure
forAll(this->phaseModels_, phasei)
{
const phaseModel& phase = this->phaseModels_[phasei];
const tmp pPrime(phase.pPrime());
addField
(
phase,
"F",
fvc::interpolate(pPrime(), pPrime().name())
*fvc::snGrad(phase)*this->mesh_.magSf(),
Fs
);
}
// Add the turbulent dispersion force
forAllConstIter
(
turbulentDispersionModelTable,
turbulentDispersionModels_,
turbulentDispersionModelIter
)
{
const phaseInterface& interface =
turbulentDispersionModelIter()->interface();
const surfaceScalarField Df
(
fvc::interpolate(turbulentDispersionModelIter()->D())
);
const volScalarField alpha12(interface.phase1() + interface.phase2());
const surfaceScalarField snGradAlpha1By12
(
fvc::snGrad
(
interface.phase1()
/max(alpha12, interface.phase1().residualAlpha())
)*this->mesh_.magSf()
);
const surfaceScalarField snGradAlpha2By12
(
fvc::snGrad
(
interface.phase2()
/max(alpha12, interface.phase2().residualAlpha())
)*this->mesh_.magSf()
);
addField(interface.phase1(), "F", Df*snGradAlpha1By12, Fs);
addField(interface.phase2(), "F", Df*snGradAlpha2By12, Fs);
}
if (this->fillFields_)
{
this->fillFields("F", dimArea*dimDensity*dimAcceleration, Fs);
}
return Fs;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::Ffs() const
{
PtrList Ffs(this->phaseModels_.size());
// Add the explicit part of the virtual mass force
forAllConstIter(VmTable, Vms_, VmIter)
{
const volScalarField& Vm(*VmIter());
const phaseInterface interface(*this, VmIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
const volScalarField VmPhase
(
(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*Vm
);
addField
(
phase,
"Ff",
-fvc::interpolate(VmPhase)
*(
byDt
(
fvc::absolute
(
this->MRF().absolute(iter().phi()().oldTime()),
iter().U()
)
)
+ otherPhase.DUDtf()
),
Ffs
);
}
}
// Add the lift force
forAllConstIter
(
liftModelTable,
liftModels_,
liftModelIter
)
{
const phaseInterface& interface = liftModelIter()->interface();
const surfaceScalarField Ff(liftModelIter()->Ff());
addField
(
interface.phase1(),
"Ff",
Ff,
Ffs
);
addField
(
interface.phase2(),
"Ff",
-Ff,
Ffs
);
}
// Add the wall lubrication force
forAllConstIter
(
wallLubricationModelTable,
wallLubricationModels_,
wallLubricationModelIter
)
{
const phaseInterface& interface =
wallLubricationModelIter()->interface();
const surfaceScalarField Ff(wallLubricationModelIter()->Ff());
addField
(
interface.phase1(),
"Ff",
Ff,
Ffs
);
addField
(
interface.phase2(),
"Ff",
-Ff,
Ffs
);
}
// Add the phase pressure
forAll(this->phaseModels_, phasei)
{
const phaseModel& phase = this->phaseModels_[phasei];
addField
(
phase,
"Ff",
fvc::interpolate(phase.pPrime())
*fvc::snGrad(phase)*this->mesh_.magSf(),
Ffs
);
}
// Add the turbulent dispersion force
forAllConstIter
(
turbulentDispersionModelTable,
turbulentDispersionModels_,
turbulentDispersionModelIter
)
{
const phaseInterface& interface =
turbulentDispersionModelIter()->interface();
const surfaceScalarField Df
(
fvc::interpolate(turbulentDispersionModelIter()->D())
);
const volScalarField alpha12(interface.phase1() + interface.phase2());
const surfaceScalarField snGradAlpha1By12
(
fvc::snGrad
(
interface.phase1()
/max(alpha12, interface.phase1().residualAlpha())
)*this->mesh_.magSf()
);
const surfaceScalarField snGradAlpha2By12
(
fvc::snGrad
(
interface.phase2()
/max(alpha12, interface.phase2().residualAlpha())
)*this->mesh_.magSf()
);
addField(interface.phase1(), "F", Df*snGradAlpha1By12, Ffs);
addField(interface.phase2(), "F", Df*snGradAlpha2By12, Ffs);
}
if (this->fillFields_)
{
this->fillFields("Ff", dimArea*dimDensity*dimAcceleration, Ffs);
}
return Ffs;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::KdPhis() const
{
PtrList KdPhis(this->phaseModels_.size());
// Add the explicit part of the drag force
forAllConstIter(KdTable, Kds_, KdIter)
{
const volScalarField& K(*KdIter());
const phaseInterface interface(*this, KdIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
addField
(
phase,
"KdPhi",
fvc::interpolate
(
-(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*K
)
*fvc::absolute
(
this->MRF().absolute(otherPhase.phi()),
otherPhase.U()
),
KdPhis
);
}
}
if (this->fillFields_)
{
this->fillFields
(
"KdPhi",
dimArea*dimDensity*dimAcceleration,
KdPhis
);
}
return KdPhis;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::KdPhifs() const
{
PtrList KdPhifs(this->phaseModels_.size());
// Add the explicit part of the drag force
forAllConstIter(KdfTable, Kdfs_, KdfIter)
{
const surfaceScalarField& Kf(*KdfIter());
const phaseInterface interface(*this, KdfIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
const surfaceScalarField alphaf
(
fvc::interpolate(otherPhase)
);
addField
(
phase,
"KdPhif",
-(alphaf/max(alphaf, otherPhase.residualAlpha()))
*Kf
*fvc::absolute
(
this->MRF().absolute(otherPhase.phi()),
otherPhase.U()
),
KdPhifs
);
}
}
if (this->fillFields_)
{
this->fillFields
(
"KdPhif",
dimArea*dimDensity*dimAcceleration,
KdPhifs
);
}
return KdPhifs;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::Kds() const
{
PtrList Kds(this->phaseModels_.size());
forAllConstIter(KdTable, Kds_, KdIter)
{
const volScalarField& K(*KdIter());
const phaseInterface interface(*this, KdIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
addField
(
phase,
"Kd",
(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*K,
Kds
);
}
}
if (this->fillFields_)
{
this->fillFields("Kd", dimDensity/dimTime, Kds);
}
return Kds;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::KdUs() const
{
PtrList KdUs(this->phaseModels_.size());
// Add the explicit part of the drag force
forAllConstIter(KdTable, Kds_, KdIter)
{
const volScalarField& K(*KdIter());
const phaseInterface interface(*this, KdIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
addField
(
phase,
"KdU",
-(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*K*otherPhase.U(),
KdUs
);
}
}
if (this->fillFields_)
{
this->fillFields("KdU", dimDensity*dimAcceleration, KdUs);
}
return KdUs;
}
template
bool Foam::MomentumTransferPhaseSystem::
implicitPhasePressure(const phaseModel& phase) const
{
return
this->mesh_.solution().solverDict(phase.volScalarField::name()).
template lookupOrDefault
(
"implicitPhasePressure",
false
);
}
template
bool Foam::MomentumTransferPhaseSystem::
implicitPhasePressure() const
{
bool implicitPressure = false;
forAll(this->phaseModels_, phasei)
{
const phaseModel& phase = this->phaseModels_[phasei];
implicitPressure = implicitPressure || implicitPhasePressure(phase);
}
return implicitPressure;
}
template
Foam::tmp
Foam::MomentumTransferPhaseSystem::alphaDByAf
(
const PtrList& rAUs,
const PtrList& rAUfs
) const
{
tmp alphaDByAf;
// Add the phase pressure
forAll(this->phaseModels_, phasei)
{
const phaseModel& phase = this->phaseModels_[phasei];
const tmp pPrime(phase.pPrime());
addTmpField
(
alphaDByAf,
fvc::interpolate(max(phase, scalar(0)))
*(
rAUfs.size()
// Face-momentum form
? rAUfs[phasei]*fvc::interpolate(pPrime())
// Cell-momentum form
: fvc::interpolate(rAUs[phasei]*pPrime(), pPrime().name())
)
);
}
// Add the turbulent dispersion
forAllConstIter
(
turbulentDispersionModelTable,
turbulentDispersionModels_,
turbulentDispersionModelIter
)
{
const phaseInterface& interface =
turbulentDispersionModelIter()->interface();
const surfaceScalarField alpha1f
(
fvc::interpolate(max(interface.phase1(), scalar(0)))
);
const surfaceScalarField alpha2f
(
fvc::interpolate(max(interface.phase2(), scalar(0)))
);
addTmpField
(
alphaDByAf,
alpha1f*alpha2f
/max(alpha1f + alpha2f, interface.phase1().residualAlpha())
*(
rAUfs.size()
// Face-momentum form
? max
(
rAUfs[interface.phase1().index()],
rAUfs[interface.phase2().index()]
)
*fvc::interpolate(turbulentDispersionModelIter()->D())
// Cell-momentum form
: fvc::interpolate
(
max
(
rAUs[interface.phase1().index()],
rAUs[interface.phase2().index()]
)
*turbulentDispersionModelIter()->D()
)
)
);
}
return alphaDByAf;
}
template
Foam::PtrList
Foam::MomentumTransferPhaseSystem::ddtCorrs() const
{
PtrList ddtCorrs(this->phaseModels_.size());
// Add correction
forAll(this->movingPhases(), movingPhasei)
{
const phaseModel& phase = this->movingPhases()[movingPhasei];
addField
(
phase,
"ddtCorr",
fvc::ddtCorr
(
phase,
phase.rho(),
phase.U()(),
phase.phi()(),
phase.Uf()
),
ddtCorrs
);
}
const pimpleNoLoopControl& pimple = this->pimple();
const Switch VmDdtCorr
(
pimple.dict().lookupOrDefault("VmDdtCorrection", false)
);
// Optionally ddd virtual mass correction
if (VmDdtCorr)
{
PtrList VmDdtCoeffs(this->phaseModels_.size());
PtrList VmDdtCorrs(this->phaseModels_.size());
forAll(this->movingPhases(), movingPhasei)
{
const phaseModel& phase = this->movingPhases()[movingPhasei];
const label phasei = phase.index();
VmDdtCoeffs.set
(
phasei,
fvm::ddt(phase.U()())().A()
);
VmDdtCorrs.set
(
phasei,
fvc::ddtCorr
(
phase.U()(),
phase.phi()(),
phase.Uf()
)
);
}
forAllConstIter(VmTable, Vms_, VmIter)
{
const volScalarField& Vm(*VmIter());
const phaseInterface interface(*this, VmIter.key());
forAllConstIter(phaseInterface, interface, iter)
{
const phaseModel& phase = iter();
const label phasei = phase.index();
const phaseModel& otherPhase = iter.otherPhase();
const label otherPhasei = otherPhase.index();
const volScalarField VmPhase
(
(otherPhase/max(otherPhase, otherPhase.residualAlpha()))
*Vm
);
addField
(
phase,
"ddtCorr",
fvc::interpolate(VmPhase)
*(
VmDdtCorrs[phasei]
+ (
fvc::interpolate(VmDdtCoeffs[otherPhasei])
*(
otherPhase.Uf().valid()
? (this->mesh_.Sf() & otherPhase.Uf()())()
: otherPhase.phi()()
)
- fvc::flux(VmDdtCoeffs[otherPhasei]*otherPhase.U())
)
- VmDdtCorrs[otherPhase.index()]
),
ddtCorrs
);
}
}
}
return ddtCorrs;
}
template
void Foam::MomentumTransferPhaseSystem::dragCorrs
(
PtrList& dragCorrs,
PtrList& dragCorrfs
) const
{
const phaseSystem::phaseModelList& phases = this->phaseModels_;
PtrList Uphis(phases.size());
forAll(phases, i)
{
if (!phases[i].stationary())
{
Uphis.set
(
i,
fvc::reconstruct(phases[i].phi())
);
}
}
forAllConstIter(KdTable, Kds_, KdIter)
{
const volScalarField& K(*KdIter());
const phaseInterface interface(*this, KdIter.key());
const phaseModel& phase1 = interface.phase1();
const phaseModel& phase2 = interface.phase2();
const label phase1i = phase1.index();
const label phase2i = phase2.index();
if (!phase1.stationary())
{
const volScalarField K1
(
(phase2/max(phase2, phase2.residualAlpha()))*K
);
addField
(
phase1,
"dragCorr",
K1
*(
phase2.stationary()
? -Uphis[phase1i]
: (Uphis[phase2i] - Uphis[phase1i])
),
dragCorrs
);
addField
(
phase1,
"dragCorrf",
fvc::interpolate(K1)
*(
phase2.stationary()
? -phase1.phi()
: (phase2.phi() - phase1.phi())
),
dragCorrfs
);
}
if (!phase2.stationary())
{
const volScalarField K2
(
(phase1/max(phase1, phase1.residualAlpha()))*K
);
addField
(
phase2,
"dragCorr",
K2
*(
phase1.stationary()
? -Uphis[phase2i]
: (Uphis[phase1i] - Uphis[phase2i])
),
dragCorrs
);
addField
(
phase2,
"dragCorrf",
fvc::interpolate(K2)
*(
phase1.stationary()
? -phase2.phi()
: (phase1.phi() - phase2.phi())
),
dragCorrfs
);
}
}
}
template
void Foam::MomentumTransferPhaseSystem::partialElimination
(
const PtrList& rAUs,
const PtrList& KdUs,
const PtrList& alphafs,
const PtrList& rAUfs,
const PtrList& KdPhis
)
{
Info<< "Inverting drag systems: ";
phaseSystem::phaseModelList& phases = this->phaseModels_;
// Calculate the mean velocity from the current velocity
// of the moving phases
volVectorField Um(this->movingPhases()[0]*this->movingPhases()[0].U());
for
(
label movingPhasei=1;
movingPhaseimovingPhases().size();
movingPhasei++
)
{
Um +=
this->movingPhases()[movingPhasei]
*this->movingPhases()[movingPhasei].U();
}
// Remove the drag contributions from the velocity and flux of the phases
// in preparation for the partial elimination of these terms
forAll(phases, i)
{
if (!phases[i].stationary())
{
phases[i].URef() += rAUs[i]*KdUs[i];
phases[i].phiRef() += rAUfs[i]*KdPhis[i];
}
}
{
// Create drag coefficient matrices
PtrList> KdByAs(phases.size());
PtrList> KdByAfs(phases.size());
forAll(phases, i)
{
KdByAs.set
(
i,
new PtrList(phases.size())
);
KdByAfs.set
(
i,
new PtrList(phases.size())
);
}
forAllConstIter(KdTable, Kds_, KdIter)
{
const volScalarField& K(*KdIter());
const phaseInterface interface(*this, KdIter.key());
const label phase1i = interface.phase1().index();
const label phase2i = interface.phase2().index();
const volScalarField K1
(
interface.phase2()
/max(interface.phase2(), interface.phase2().residualAlpha())
*K
);
const volScalarField K2
(
interface.phase1()
/max(interface.phase1(), interface.phase1().residualAlpha())
*K
);
addField
(
interface.phase2(),
"KdByA",
-rAUs[phase1i]*K1,
KdByAs[phase1i]
);
addField
(
interface.phase1(),
"KdByA",
-rAUs[phase2i]*K2,
KdByAs[phase2i]
);
addField
(
interface.phase2(),
"KdByAf",
-rAUfs[phase1i]*fvc::interpolate(K1),
KdByAfs[phase1i]
);
addField
(
interface.phase1(),
"KdByAf",
-rAUfs[phase2i]*fvc::interpolate(K2),
KdByAfs[phase2i]
);
}
forAll(phases, i)
{
this->fillFields("KdByAs", dimless, KdByAs[i]);
this->fillFields("KdByAfs", dimless, KdByAfs[i]);
KdByAs[i][i] = 1;
KdByAfs[i][i] = 1;
}
// Decompose
for (label i = 0; i < phases.size(); i++)
{
for (label j = i + 1; j < phases.size(); j++)
{
KdByAs[i][j] /= KdByAs[i][i];
KdByAfs[i][j] /= KdByAfs[i][i];
for (label k = i + 1; k < phases.size(); ++ k)
{
KdByAs[j][k] -= KdByAs[j][i]*KdByAs[i][k];
KdByAfs[j][k] -= KdByAfs[j][i]*KdByAfs[i][k];
}
}
}
{
volScalarField detKdByAs(KdByAs[0][0]);
surfaceScalarField detPhiKdfs(KdByAfs[0][0]);
for (label i = 1; i < phases.size(); i++)
{
detKdByAs *= KdByAs[i][i];
detPhiKdfs *= KdByAfs[i][i];
}
Info<< "Min cell/face det = " << gMin(detKdByAs.primitiveField())
<< "/" << gMin(detPhiKdfs.primitiveField()) << endl;
}
// Solve for the velocities and fluxes
for (label i = 1; i < phases.size(); i++)
{
if (!phases[i].stationary())
{
for (label j = 0; j < i; j ++)
{
phases[i].URef() -= KdByAs[i][j]*phases[j].U();
phases[i].phiRef() -= KdByAfs[i][j]*phases[j].phi();
}
}
}
for (label i = phases.size() - 1; i >= 0; i--)
{
if (!phases[i].stationary())
{
for (label j = phases.size() - 1; j > i; j--)
{
phases[i].URef() -= KdByAs[i][j]*phases[j].U();
phases[i].phiRef() -= KdByAfs[i][j]*phases[j].phi();
}
phases[i].URef() /= KdByAs[i][i];
phases[i].phiRef() /= KdByAfs[i][i];
}
}
}
this->setMixtureU(Um);
this->setMixturePhi(alphafs, this->phi());
}
template
void Foam::MomentumTransferPhaseSystem::partialEliminationf
(
const PtrList& rAUfs,
const PtrList& alphafs,
const PtrList& KdPhifs
)
{
Info<< "Inverting drag system: ";
phaseSystem::phaseModelList& phases = this->phaseModels_;
// Remove the drag contributions from the flux of the phases
// in preparation for the partial elimination of these terms
forAll(phases, i)
{
if (!phases[i].stationary())
{
phases[i].phiRef() += rAUfs[i]*KdPhifs[i];
}
}
{
// Create drag coefficient matrix
PtrList> phiKdfs(phases.size());
forAll(phases, phasei)
{
phiKdfs.set
(
phasei,
new PtrList(phases.size())
);
}
forAllConstIter(KdfTable, Kdfs_, KdfIter)
{
const surfaceScalarField& Kf(*KdfIter());
const phaseInterface interface(*this, KdfIter.key());
const label phase1i = interface.phase1().index();
const label phase2i = interface.phase2().index();
const surfaceScalarField alpha1f
(
fvc::interpolate(interface.phase1())
);
const surfaceScalarField alpha2f
(
fvc::interpolate(interface.phase2())
);
addField
(
interface.phase2(),
"phiKdf",
-rAUfs[phase1i]
*alpha2f/max(alpha2f, interface.phase2().residualAlpha())
*Kf,
phiKdfs[phase1i]
);
addField
(
interface.phase1(),
"phiKdf",
-rAUfs[phase2i]
*alpha1f/max(alpha1f, interface.phase1().residualAlpha())
*Kf,
phiKdfs[phase2i]
);
}
forAll(phases, phasei)
{
this->fillFields("phiKdf", dimless, phiKdfs[phasei]);
phiKdfs[phasei][phasei] = 1;
}
// Decompose
for (label i = 0; i < phases.size(); i++)
{
for (label j = i + 1; j < phases.size(); j++)
{
phiKdfs[i][j] /= phiKdfs[i][i];
for (label k = i + 1; k < phases.size(); ++ k)
{
phiKdfs[j][k] -= phiKdfs[j][i]*phiKdfs[i][k];
}
}
}
{
surfaceScalarField detPhiKdfs(phiKdfs[0][0]);
for (label i = 1; i < phases.size(); i++)
{
detPhiKdfs *= phiKdfs[i][i];
}
Info<< "Min face det = "
<< gMin(detPhiKdfs.primitiveField()) << endl;
}
// Solve for the fluxes
for (label i = 1; i < phases.size(); i++)
{
if (!phases[i].stationary())
{
for (label j = 0; j < i; j ++)
{
phases[i].phiRef() -= phiKdfs[i][j]*phases[j].phi();
}
}
}
for (label i = phases.size() - 1; i >= 0; i--)
{
if (!phases[i].stationary())
{
for (label j = phases.size() - 1; j > i; j--)
{
phases[i].phiRef() -= phiKdfs[i][j]*phases[j].phi();
}
phases[i].phiRef() /= phiKdfs[i][i];
}
}
}
this->setMixturePhi(alphafs, this->phi());
}
template
bool Foam::MomentumTransferPhaseSystem::read()
{
if (BasePhaseSystem::read())
{
bool readOK = true;
// Read models ...
return readOK;
}
else
{
return false;
}
}
// ************************************************************************* //