The cell-base momentum/pressure algorithm in the multiphaseEuler solver module
has been substantially updated to improve consistency, conservation and reduce
drag generated staggering patterns at sharp interfaces and the boundaries with
stationary phases. For most if not all cases this new algorithm can be used to
provide well resolved and reliable solutions where the faceMomentum algorithm
would have been chosen previously in order to obtain sufficiently smooth
solutions but at the expense of a noticeable loss in accuracy and resolution.
The first significant change in the momentum/pressure algorithm is in the
interpolation practice used to construct the flux predictor equation from the
cell momentum equation: rather than interpolating the H/A ratio to the faces
i.e. (H/A)_f the terms in the momentum equation are interpolated separately so
that H_f/A_f is used. The same approach is used for the drag i.e. (D_f/A_f) and
virtual mass contributions. The advantage of this change is that the phase
forces are now consistent in both the momentum and flux equations, i.e. sum to
zero for each pair of phases.
The second significant change is in the handling of ddtCorr which converts the
old-time time-derivative contributions in H from velocity to flux which is now
consistent due to the change to H/A interpolation and also generalised to use
the fvc::ddtCorr function which has been updated for multiphase. Additionally
ddtCorr may optionally be applied to the time-derivative in the virtual mass
term in a consistent manner so that the contributions to the flux equation sum
to zero for each pair of phases.
The third significant change is the addition of an optional drag correction term
to the momentum corrector to reduce the staggering patters generated in the
velocity field due to sudden changes in drag force between phase, e.g. at sharp
interfaces between phases or at the boundaries with stationary phases. This is
particularly beneficial for fluidised bed simulations. However this correction
is not and cannot be phase consistent, i.e. the correction does not sum to zero
for pairs of phases it is applied to so a small drag error is introduced, but
tests so far have shown that the error is small and outweighed by the benefit in
the reduction in numerical artefacts in the solution.
The final significant change is in the handling of residualAlpha for drag and
virtual mass to provide stable and physical phase velocities in the limit of the
phase-fraction -> 0. The new approach is phase asymmetric such that the
residual drag is applied only to the phase with a phase-fraction less than
residualAlpha and not to the carrier phase. This change ensures that the flow
of a pure phase is unaffected by the residualAlpha and residual drag of the
other phases that are stabilised in pure phase region.
There are now four options in the PIMPLE section of the fvSolutions dictionary
relating to the multiphase momentum/pressure algorithm:
PIMPLE
{
faceMomentum no;
VmDdtCorrection yes;
dragCorrection yes;
partialElimination no;
}
faceMomentum:
Switches between the cell and face momentum equation algorithms.
Provides much smoother and reliable solutions for even the most challenging
multiphase cases at the expense of a noticeable loss in accuracy and resolution.
Defaults to 'no'.
VmDdtCorrection:
Includes the ddtCorr correction term to the time-derivative part of the
virtual-mass term in the flux equation which ensures consistency between the
phase virtual mass force on the faces but generates solutions which are
slightly less smooth and more likely to contain numerical artefacts.
Defaults to 'no'.
Testing so far has shown that the loss in smoothness is small and there is
some noticeable improvement is some cases so in the future the default may
be changed to 'yes'.
dragCorrection:
Includes the momentum corrector drag correction term to reduce the
staggering patters generated in the velocity field due to sudden changes in
drag force at the expense of a small error in drag consistency.
Defaults to 'no'
partialElimination:
Switches the partial-elimination momentum corrector which inverts the drag
matrix for both the momentum equations and/or flux equations to provide a
drag implicit correction to the phase velocity and flux fields. The
algorithm is the same as previously but updated for the new consistent drag
interpolation.
All the tutorials/modules/multiphaseEuler tutorial cases have been updated and
tested with the above developments and the four options set appropriately for
each.
1643 lines
42 KiB
C++
1643 lines
42 KiB
C++
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / 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 <http://www.gnu.org/licenses/>.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#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<class BasePhaseSystem>
|
|
void Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::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<class BasePhaseSystem>
|
|
void Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::addTmpField
|
|
(
|
|
tmp<surfaceScalarField>& result,
|
|
const tmp<surfaceScalarField>& field
|
|
) const
|
|
{
|
|
if (result.valid())
|
|
{
|
|
result.ref() += field;
|
|
}
|
|
else
|
|
{
|
|
if (field.isTmp())
|
|
{
|
|
result = field;
|
|
}
|
|
else
|
|
{
|
|
result = field().clone();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
|
|
template<class BasePhaseSystem>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::
|
|
MomentumTransferPhaseSystem
|
|
(
|
|
const fvMesh& mesh
|
|
)
|
|
:
|
|
BasePhaseSystem(mesh)
|
|
{
|
|
this->generateInterfacialModels(dragModels_);
|
|
this->generateInterfacialModels(virtualMassModels_);
|
|
this->generateInterfacialModels(liftModels_);
|
|
this->generateInterfacialModels(wallLubricationModels_);
|
|
this->generateInterfacialModels(turbulentDispersionModels_);
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
|
|
|
template<class BasePhaseSystem>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::
|
|
~MomentumTransferPhaseSystem()
|
|
{}
|
|
|
|
|
|
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
|
|
|
template<class BasePhaseSystem>
|
|
Foam::autoPtr<Foam::phaseSystem::momentumTransferTable>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::momentumTransfer()
|
|
{
|
|
// Create a momentum transfer matrix for each phase
|
|
autoPtr<phaseSystem::momentumTransferTable> 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();
|
|
}
|
|
|
|
// 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.phi();
|
|
const tmp<surfaceScalarField> 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<class BasePhaseSystem>
|
|
Foam::autoPtr<Foam::phaseSystem::momentumTransferTable>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::momentumTransferf()
|
|
{
|
|
// Create a momentum transfer matrix for each phase
|
|
autoPtr<phaseSystem::momentumTransferTable> 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<fvVectorMatrix> UgradUs(this->phaseModels_.size());
|
|
forAll(this->phaseModels_, phasei)
|
|
{
|
|
const phaseModel& phase = this->phaseModels_[phasei];
|
|
|
|
if (!phase.stationary())
|
|
{
|
|
const volVectorField& U = phase.U();
|
|
const surfaceScalarField& phi = phase.phi();
|
|
const tmp<surfaceScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::KdVmfs() const
|
|
{
|
|
PtrList<surfaceScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::Fs()
|
|
{
|
|
PtrList<surfaceScalarField> 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<volScalarField> 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 volScalarField D(turbulentDispersionModelIter()->D());
|
|
const surfaceScalarField Df(fvc::interpolate(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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::Ffs()
|
|
{
|
|
PtrList<surfaceScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::KdPhis() const
|
|
{
|
|
PtrList<surfaceScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::KdPhifs() const
|
|
{
|
|
PtrList<surfaceScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::volScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::Kds() const
|
|
{
|
|
PtrList<volScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::volVectorField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::KdUs() const
|
|
{
|
|
PtrList<volVectorField> 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<class BasePhaseSystem>
|
|
bool Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::
|
|
implicitPhasePressure(const phaseModel& phase) const
|
|
{
|
|
return
|
|
this->mesh_.solution().solverDict(phase.volScalarField::name()).
|
|
template lookupOrDefault<Switch>
|
|
(
|
|
"implicitPhasePressure",
|
|
false
|
|
);
|
|
}
|
|
|
|
|
|
template<class BasePhaseSystem>
|
|
bool Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::
|
|
implicitPhasePressure() const
|
|
{
|
|
bool implicitPressure = false;
|
|
|
|
forAll(this->phaseModels_, phasei)
|
|
{
|
|
const phaseModel& phase = this->phaseModels_[phasei];
|
|
|
|
implicitPressure = implicitPressure || implicitPhasePressure(phase);
|
|
}
|
|
|
|
return implicitPressure;
|
|
}
|
|
|
|
|
|
template<class BasePhaseSystem>
|
|
Foam::tmp<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::alphaDByAf
|
|
(
|
|
const PtrList<volScalarField>& rAUs,
|
|
const PtrList<surfaceScalarField>& rAUfs
|
|
) const
|
|
{
|
|
tmp<surfaceScalarField> alphaDByAf;
|
|
|
|
// Add the phase pressure
|
|
forAll(this->phaseModels_, phasei)
|
|
{
|
|
const phaseModel& phase = this->phaseModels_[phasei];
|
|
|
|
const tmp<volScalarField> 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<class BasePhaseSystem>
|
|
Foam::PtrList<Foam::surfaceScalarField>
|
|
Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::ddtCorrs
|
|
(
|
|
const bool includeVirtualMass
|
|
) const
|
|
{
|
|
PtrList<surfaceScalarField> 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<Switch>("VmDdtCorrection", false)
|
|
);
|
|
|
|
// Optionally ddd virtual mass correction
|
|
if (VmDdtCorr)
|
|
{
|
|
PtrList<volScalarField> VmDdtCoeffs(this->phaseModels_.size());
|
|
PtrList<surfaceScalarField> 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<class BasePhaseSystem>
|
|
void Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::dragCorrs
|
|
(
|
|
PtrList<volVectorField>& dragCorrs,
|
|
PtrList<surfaceScalarField>& dragCorrfs
|
|
) const
|
|
{
|
|
const phaseSystem::phaseModelList& phases = this->phaseModels_;
|
|
|
|
PtrList<volVectorField> 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<class BasePhaseSystem>
|
|
void Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::partialElimination
|
|
(
|
|
const PtrList<volScalarField>& rAUs,
|
|
const PtrList<volVectorField>& KdUs,
|
|
const PtrList<surfaceScalarField>& alphafs,
|
|
const PtrList<surfaceScalarField>& rAUfs,
|
|
const PtrList<surfaceScalarField>& 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;
|
|
movingPhasei<this->movingPhases().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<PtrList<volScalarField>> KdByAs(phases.size());
|
|
PtrList<PtrList<surfaceScalarField>> KdByAfs(phases.size());
|
|
|
|
forAll(phases, i)
|
|
{
|
|
KdByAs.set
|
|
(
|
|
i,
|
|
new PtrList<volScalarField>(phases.size())
|
|
);
|
|
|
|
KdByAfs.set
|
|
(
|
|
i,
|
|
new PtrList<surfaceScalarField>(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<class BasePhaseSystem>
|
|
void Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::partialEliminationf
|
|
(
|
|
const PtrList<surfaceScalarField>& rAUfs,
|
|
const PtrList<surfaceScalarField>& alphafs,
|
|
const PtrList<surfaceScalarField>& 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<PtrList<surfaceScalarField>> phiKdfs(phases.size());
|
|
|
|
forAll(phases, phasei)
|
|
{
|
|
phiKdfs.set
|
|
(
|
|
phasei,
|
|
new PtrList<surfaceScalarField>(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<class BasePhaseSystem>
|
|
bool Foam::MomentumTransferPhaseSystem<BasePhaseSystem>::read()
|
|
{
|
|
if (BasePhaseSystem::read())
|
|
{
|
|
bool readOK = true;
|
|
|
|
// Read models ...
|
|
|
|
return readOK;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|