solvers::incompressibleVoF: New solver module for incompressible two-phase flow with VoF

executed with foamRun for single region simulations of foamMultiRun for
multi-region simulations.  Replaces interFoam and all the corresponding
tutorials have been updated and moved to tutorials/modules/incompressibleVoF.

Both incompressibleVoF and compressibleVoF solver modules are derived from the
common two-phase VoF base-class solvers::VoFSolver which handles the
complexities of VoF interface-compression, boundedness and conservation with
2nd-order schemes in space and time using the semi-implicit MULES limiter and
solution proceedure.  This maximises code re-use, improves readability and
simplifies maintenance.

Class
    Foam::solvers::incompressibleVoF

Description
    Solver module for for 2 incompressible, isothermal immiscible fluids using a
    VOF (volume of fluid) phase-fraction based interface capturing approach,
    with optional mesh motion and mesh topology changes including adaptive
    re-meshing.

    The momentum and other fluid properties are of the "mixture" and a single
    momentum equation is solved.

    Either mixture or two-phase transport modelling may be selected.  In the
    mixture approach a single laminar, RAS or LES model is selected to model the
    momentum stress.  In the Euler-Euler two-phase approach separate laminar,
    RAS or LES selected models are selected for each of the phases.

    Uses the flexible PIMPLE (PISO-SIMPLE) solution for time-resolved and
    pseudo-transient and steady simulations.

    Optional fvModels and fvConstraints are provided to enhance the simulation
    in many ways including adding various sources, Lagrangian
    particles, surface film etc. and constraining or limiting the solution.

SourceFiles
    incompressibleVoF.C

See also
    Foam::solvers::VoFSolver
    Foam::solvers::compressibleVoF
This commit is contained in:
Henry Weller
2022-12-25 11:38:36 +00:00
parent 1a24f1e30c
commit 851c9391be
782 changed files with 2333 additions and 4501 deletions

View File

@ -0,0 +1,7 @@
setRDeltaT.C
moveMesh.C
alphaPredictor.C
momentumPredictor.C
VoFSolver.C
LIB = $(FOAM_LIBBIN)/libVoFSolver

View File

@ -0,0 +1,22 @@
EXE_INC = \
-I$(FOAM_SOLVERS)/modules/fluidSolver/lnInclude \
-I$(LIB_SRC)/physicalProperties/lnInclude \
-I$(LIB_SRC)/twoPhaseModels/VoF \
-I$(LIB_SRC)/twoPhaseModels/interfaceCompression/lnInclude \
-I$(LIB_SRC)/twoPhaseModels/interfaceProperties/lnInclude \
-I$(LIB_SRC)/twoPhaseModels/twoPhaseMixture/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/sampling/lnInclude
LIB_LIBS = \
-lfluidSolver \
-lphysicalProperties \
-linterfaceCompression \
-linterfaceProperties \
-ltwoPhaseMixture \
-lfiniteVolume \
-lmeshTools \
-lfvModels \
-lfvConstraints \
-lsampling

View File

@ -0,0 +1,314 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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 "VoFSolver.H"
#include "localEulerDdtScheme.H"
#include "CorrectPhi.H"
#include "geometricZeroField.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace solvers
{
defineTypeNameAndDebug(VoFSolver, 0);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::solvers::VoFSolver::correctCoNum()
{
fluidSolver::correctCoNum(phi);
const scalarField sumPhi
(
interface.nearInterface()().primitiveField()
*fvc::surfaceSum(mag(phi))().primitiveField()
);
alphaCoNum = 0.5*gMax(sumPhi/mesh.V().field())*runTime.deltaTValue();
const scalar meanAlphaCoNum =
0.5*(gSum(sumPhi)/gSum(mesh.V().field()))*runTime.deltaTValue();
Info<< "Interface Courant Number mean: " << meanAlphaCoNum
<< " max: " << alphaCoNum << endl;
}
void Foam::solvers::VoFSolver::continuityErrors()
{
fluidSolver::continuityErrors(phi);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::solvers::VoFSolver::VoFSolver
(
fvMesh& mesh,
autoPtr<twoPhaseMixture> mixturePtr
)
:
fluidSolver(mesh),
mixture_(mixturePtr),
mixture(mixture_()),
alpha1(mixture.alpha1()),
alpha2(mixture.alpha2()),
alphaRestart
(
typeIOobject<surfaceScalarField>
(
IOobject::groupName("alphaPhi", alpha1.group()),
runTime.name(),
mesh,
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
).headerOk()
),
divAlphaName("div(phi,alpha)"),
U
(
IOobject
(
"U",
runTime.name(),
mesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh
),
phi
(
IOobject
(
"phi",
runTime.name(),
mesh,
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
linearInterpolate(U) & mesh.Sf()
),
interface(mixture, alpha1, alpha2, U),
buoyancy(mesh),
p_rgh(buoyancy.p_rgh),
rho(mixture.rho()),
rhoPhi
(
IOobject
(
"rhoPhi",
runTime.name(),
mesh,
IOobject::NO_READ,
IOobject::NO_WRITE
),
fvc::interpolate(rho)*phi
),
alphaPhi1
(
IOobject
(
IOobject::groupName("alphaPhi", alpha1.group()),
runTime.name(),
mesh,
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
phi*fvc::interpolate(alpha1)
),
MRF(mesh)
{
// Read the controls
read();
mesh.schemes().setFluxRequired(p_rgh.name());
mesh.schemes().setFluxRequired(alpha1.name());
if (alphaRestart)
{
Info << "Restarting alpha" << endl;
}
if (mesh.dynamic())
{
Info<< "Constructing face momentum Uf" << endl;
Uf = new surfaceVectorField
(
IOobject
(
"Uf",
runTime.name(),
mesh,
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
fvc::interpolate(U)
);
}
if (transient())
{
correctCoNum();
}
else if (LTS)
{
Info<< "Using LTS" << endl;
trDeltaT = tmp<volScalarField>
(
new volScalarField
(
IOobject
(
fv::localEulerDdt::rDeltaTName,
runTime.name(),
mesh,
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
mesh,
dimensionedScalar(dimless/dimTime, 1),
extrapolatedCalculatedFvPatchScalarField::typeName
)
);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::solvers::VoFSolver::~VoFSolver()
{}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
Foam::scalar Foam::solvers::VoFSolver::maxDeltaT() const
{
const scalar maxAlphaCo
(
runTime.controlDict().lookup<scalar>("maxAlphaCo")
);
const scalar deltaT = fluidSolver::maxDeltaT();
if (alphaCoNum > small)
{
return min
(
deltaT,
maxAlphaCo/(alphaCoNum + small)*runTime.deltaTValue()
);
}
else
{
return deltaT;
}
}
void Foam::solvers::VoFSolver::preSolve()
{
// Read the controls
read();
if (transient())
{
correctCoNum();
}
else if (LTS)
{
setRDeltaT();
}
fvModels().preUpdateMesh();
// Store divU from the previous mesh so that it can be mapped
// and used in correctPhi to ensure the corrected phi has the
// same divergence
if
(
correctPhi
&& divergent()
&& mesh.topoChanged())
{
// Construct and register divU for mapping
divU = new volScalarField
(
"divU0",
fvc::div(fvc::absolute(phi, U))
);
}
// Update the mesh for topology change, mesh to mesh mapping
const bool topoChanged = mesh.update();
// Do not apply previous time-step mesh compression flux
// if the mesh topology changed
if (topoChanged)
{
talphaPhi1Corr0.clear();
}
}
void Foam::solvers::VoFSolver::prePredictor()
{
fvModels().correct();
alphaPredictor();
}
void Foam::solvers::VoFSolver::postCorrector()
{}
void Foam::solvers::VoFSolver::postSolve()
{
divU.clear();
}
// ************************************************************************* //

View File

@ -0,0 +1,282 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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/>.
Class
Foam::solvers::VoFSolver
Description
Solver module base-class for for 2 immiscible fluids using a VOF (volume
of fluid) phase-fraction based interface capturing approach, with optional
mesh motion and mesh topology changes including adaptive re-meshing.
The momentum and other fluid properties are of the "mixture" and a single
momentum equation is solved.
Either mixture or two-phase transport modelling may be selected. In the
mixture approach a single laminar, RAS or LES model is selected to model the
momentum stress. In the Euler-Euler two-phase approach separate laminar,
RAS or LES selected models are selected for each of the phases.
Uses the flexible PIMPLE (PISO-SIMPLE) solution for time-resolved and
pseudo-transient and steady simulations.
Optional fvModels and fvConstraints are provided to enhance the simulation
in many ways including adding various sources, Lagrangian
particles, surface film etc. and constraining or limiting the solution.
SourceFiles
VoFSolver.C
See also
Foam::solvers::fluidSolver
\*---------------------------------------------------------------------------*/
#ifndef VoFSolver_H
#define VoFSolver_H
#include "fluidSolver.H"
#include "twoPhaseMixture.H"
#include "interfaceProperties.H"
#include "buoyancy.H"
#include "pressureReference.H"
#include "IOMRFZoneList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace solvers
{
/*---------------------------------------------------------------------------*\
Class VoFSolver Declaration
\*---------------------------------------------------------------------------*/
class VoFSolver
:
public fluidSolver
{
protected:
// Phase properties
//- The compressible two-phase mixture
autoPtr<twoPhaseMixture> mixture_;
twoPhaseMixture& mixture;
//- Reference to the phase1-fraction
volScalarField& alpha1;
//- Reference to the phase2-fraction
volScalarField& alpha2;
//- Switch indicating if this is a restart
bool alphaRestart;
//- Name of the alpha convection scheme
const word divAlphaName;
//- Phase-fraction flux Courant number
scalar alphaCoNum;
// Kinematic properties
//- Velocity field
volVectorField U;
//- Volumetric-flux field
surfaceScalarField phi;
// Interface properties
interfaceProperties interface;
// Thermophysical properties
//- Buoyancy force
solvers::buoyancy buoyancy;
//- Reference to the buoyant pressure for buoyant cases
// otherwise to the pressure
volScalarField& p_rgh;
//- Reference to the mixture continuity density field
const volScalarField& rho;
// Kinematic properties
//- Mass flux field
surfaceScalarField rhoPhi;
// Phase-1 volumetric flux
surfaceScalarField alphaPhi1;
// Optional models
//- MRF zone list
IOMRFZoneList MRF;
// Cached temporary fields
tmp<volScalarField> rAU;
//- MULES Correction
tmp<surfaceScalarField> talphaPhi1Corr0;
//- Pointer to the surface momentum field
// used to recreate the flux after mesh-change
autoPtr<surfaceVectorField> Uf;
//- Pointer to the momentum divergence field
// used in correctPhi to ensure the corrected phi has the
// same divergence
autoPtr<volScalarField> divU;
//- Optional LTS reciprocal time-step field
tmp<volScalarField> trDeltaT;
//- Cached momentum matrix
// shared between the momentum predictor and pressure corrector
tmp<fvVectorMatrix> tUEqn;
private:
// Private Member Functions
//- Set rDeltaT for LTS
virtual void setRDeltaT();
//- Correct the cached Courant numbers
void correctCoNum();
//- Solve for the phase-fractions
void alphaSolve(const dictionary& alphaControls);
//- Solve for the phase-fractions
void alphaPredictor();
protected:
// Protected Member Functions
//- Calculate and print the continuity errors
void continuityErrors();
//- Return the pressure reference
virtual const Foam::pressureReference& pressureReference() const = 0;
//- Is the flow divergent?
// i.e. compressible or include phase-fraction sources
virtual bool divergent() = 0;
//- Calculate the alpha equation sources
virtual void alphaSuSp
(
tmp<volScalarField::Internal>& Su,
tmp<volScalarField::Internal>& Sp
) = 0;
//- Return the momentum equation stress term
virtual tmp<fvVectorMatrix> divDevTau(volVectorField& U) = 0;
public:
//- Runtime type information
TypeName("VoFSolver");
// Constructors
//- Construct from region mesh
VoFSolver(fvMesh& mesh, autoPtr<twoPhaseMixture>);
//- Disallow default bitwise copy construction
VoFSolver(const VoFSolver&) = delete;
//- Destructor
virtual ~VoFSolver();
// Member Functions
//- Return the current maximum time-step for stable solution
virtual scalar maxDeltaT() const;
//- Called at the start of the time-step, before the PIMPLE loop
virtual void preSolve();
//- Called at the start of the PIMPLE loop to move the mesh
virtual bool moveMesh();
//- Called at the start of the PIMPLE loop
virtual void prePredictor() = 0;
//- Construct and optionally solve the momentum equation
virtual void momentumPredictor();
//- Construct and solve the energy equation,
// convert to temperature
// and update thermophysical and transport properties
virtual void thermophysicalPredictor() = 0;
//- Construct and solve the pressure equation in the PISO loop
virtual void pressureCorrector() = 0;
//- Correct the momentum transport modelling
virtual void postCorrector();
//- Called after the PIMPLE loop at the end of the time-step
virtual void postSolve();
// Member Operators
//- Disallow default bitwise assignment
void operator=(const VoFSolver&) = delete;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace solvers
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,398 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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 "VoFSolver.H"
#include "subCycle.H"
#include "interfaceCompression.H"
#include "CMULES.H"
#include "CrankNicolsonDdtScheme.H"
#include "fvcFlux.H"
#include "fvmSup.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
void Foam::solvers::VoFSolver::alphaSolve(const dictionary& alphaControls)
{
const label nAlphaSubCycles(alphaControls.lookup<label>("nAlphaSubCycles"));
const label nAlphaCorr(alphaControls.lookup<label>("nAlphaCorr"));
const bool MULESCorr
(
alphaControls.lookupOrDefault<Switch>("MULESCorr", false)
);
// Apply the compression correction from the previous iteration
// Improves efficiency for steady-simulations but can only be applied
// once the alpha field is reasonably steady, i.e. fully developed
const bool alphaApplyPrevCorr
(
alphaControls.lookupOrDefault<Switch>("alphaApplyPrevCorr", false)
);
const word alphaScheme(mesh.schemes().div(divAlphaName)[1].wordToken());
ITstream compressionScheme
(
compressionSchemes.found(alphaScheme)
? mesh.schemes().div(divAlphaName)
: ITstream
(
divAlphaName,
tokenList
{
word("Gauss"),
word("interfaceCompression"),
alphaScheme,
alphaControls.lookup<scalar>("cAlpha")
}
)
);
// Set the off-centering coefficient according to ddt scheme
scalar ocCoeff = 0;
{
tmp<fv::ddtScheme<scalar>> tddtAlpha
(
fv::ddtScheme<scalar>::New
(
mesh,
mesh.schemes().ddt("ddt(alpha)")
)
);
const fv::ddtScheme<scalar>& ddtAlpha = tddtAlpha();
if
(
isType<fv::EulerDdtScheme<scalar>>(ddtAlpha)
|| isType<fv::localEulerDdtScheme<scalar>>(ddtAlpha)
)
{
ocCoeff = 0;
}
else if (isType<fv::CrankNicolsonDdtScheme<scalar>>(ddtAlpha))
{
if (nAlphaSubCycles > 1)
{
FatalErrorInFunction
<< "Sub-cycling is not supported "
"with the CrankNicolson ddt scheme"
<< exit(FatalError);
}
if
(
alphaRestart
|| mesh.time().timeIndex() > mesh.time().startTimeIndex() + 1
)
{
ocCoeff =
refCast<const fv::CrankNicolsonDdtScheme<scalar>>(ddtAlpha)
.ocCoeff();
}
}
else
{
FatalErrorInFunction
<< "Only Euler and CrankNicolson ddt schemes are supported"
<< exit(FatalError);
}
}
// Set the time blending factor, 1 for Euler
scalar cnCoeff = 1.0/(1.0 + ocCoeff);
tmp<surfaceScalarField> phiCN(phi);
// Calculate the Crank-Nicolson off-centred volumetric flux
if (ocCoeff > 0)
{
phiCN = surfaceScalarField::New
(
"phiCN",
cnCoeff*phi + (1.0 - cnCoeff)*phi.oldTime()
);
}
tmp<volScalarField> divU;
if (divergent())
{
divU =
(
mesh.moving()
? fvc::div(phiCN + mesh.phi())
: fvc::div(phiCN)
);
}
tmp<volScalarField::Internal> Su;
tmp<volScalarField::Internal> Sp;
alphaSuSp(Su, Sp);
if (MULESCorr)
{
fvScalarMatrix alpha1Eqn
(
(
LTS
? fv::localEulerDdtScheme<scalar>(mesh).fvmDdt(alpha1)
: fv::EulerDdtScheme<scalar>(mesh).fvmDdt(alpha1)
)
+ fv::gaussConvectionScheme<scalar>
(
mesh,
phiCN,
upwind<scalar>(mesh, phiCN)
).fvmDiv(phiCN, alpha1)
);
if (divU.valid())
{
alpha1Eqn -= Su() + fvm::Sp(Sp() + divU(), alpha1);
}
alpha1Eqn.solve();
Info<< "Phase-1 volume fraction = "
<< alpha1.weightedAverage(mesh.Vsc()).value()
<< " Min(" << alpha1.name() << ") = " << min(alpha1).value()
<< " Max(" << alpha1.name() << ") = " << max(alpha1).value()
<< endl;
tmp<surfaceScalarField> talphaPhi1UD(alpha1Eqn.flux());
alphaPhi1 = talphaPhi1UD();
if (alphaApplyPrevCorr && talphaPhi1Corr0.valid())
{
Info<< "Applying the previous iteration compression flux" << endl;
MULES::correct
(
geometricOneField(),
alpha1,
alphaPhi1,
talphaPhi1Corr0.ref(),
oneField(),
zeroField()
);
alphaPhi1 += talphaPhi1Corr0();
}
// Cache the upwind-flux
talphaPhi1Corr0 = talphaPhi1UD;
alpha2 = 1.0 - alpha1;
interface.correct();
}
for (int aCorr=0; aCorr<nAlphaCorr; aCorr++)
{
// Split operator
tmp<surfaceScalarField> talphaPhi1Un
(
fvc::flux
(
phiCN(),
(cnCoeff*alpha1 + (1.0 - cnCoeff)*alpha1.oldTime())(),
compressionScheme.rewind()
)
);
if (MULESCorr)
{
tmp<surfaceScalarField> talphaPhi1Corr(talphaPhi1Un() - alphaPhi1);
volScalarField alpha10("alpha10", alpha1);
if (divU.valid())
{
MULES::correct
(
geometricOneField(),
alpha1,
talphaPhi1Un(),
talphaPhi1Corr.ref(),
Sp(),
(-Sp()*alpha1)(),
oneField(),
zeroField()
);
}
else
{
MULES::correct
(
geometricOneField(),
alpha1,
talphaPhi1Un(),
talphaPhi1Corr.ref(),
oneField(),
zeroField()
);
}
// Under-relax the correction for all but the 1st corrector
if (aCorr == 0)
{
alphaPhi1 += talphaPhi1Corr();
}
else
{
alpha1 = 0.5*alpha1 + 0.5*alpha10;
alphaPhi1 += 0.5*talphaPhi1Corr();
}
}
else
{
alphaPhi1 = talphaPhi1Un;
if (divU.valid())
{
MULES::explicitSolve
(
geometricOneField(),
alpha1,
phiCN,
alphaPhi1,
Sp(),
(Su() + divU()*min(alpha1(), scalar(1)))(),
oneField(),
zeroField()
);
}
else
{
MULES::explicitSolve
(
geometricOneField(),
alpha1,
phiCN,
alphaPhi1,
oneField(),
zeroField()
);
}
}
alpha2 = 1.0 - alpha1;
// Correct only the mixture interface for the interface compression flux
interface.correct();
}
if (alphaApplyPrevCorr && MULESCorr)
{
talphaPhi1Corr0 = alphaPhi1 - talphaPhi1Corr0;
talphaPhi1Corr0.ref().rename("alphaPhi1Corr0");
}
else
{
talphaPhi1Corr0.clear();
}
if
(
word(mesh.schemes().ddt("ddt(rho,U)"))
!= fv::EulerDdtScheme<vector>::typeName
&& word(mesh.schemes().ddt("ddt(rho,U)"))
!= fv::localEulerDdtScheme<vector>::typeName
)
{
if (ocCoeff > 0)
{
// Calculate the end-of-time-step alpha flux
alphaPhi1 =
(alphaPhi1 - (1.0 - cnCoeff)*alphaPhi1.oldTime())/cnCoeff;
}
}
Info<< "Phase-1 volume fraction = "
<< alpha1.weightedAverage(mesh.Vsc()).value()
<< " Min(" << alpha1.name() << ") = " << min(alpha1).value()
<< " Max(" << alpha1.name() << ") = " << max(alpha1).value()
<< endl;
}
void Foam::solvers::VoFSolver::alphaPredictor()
{
const dictionary& alphaControls = mesh.solution().solverDict(alpha1.name());
const label nAlphaSubCycles(alphaControls.lookup<label>("nAlphaSubCycles"));
if (nAlphaSubCycles > 1)
{
dimensionedScalar totalDeltaT = runTime.deltaT();
tmp<volScalarField> trSubDeltaT;
if (LTS)
{
trSubDeltaT =
fv::localEulerDdt::localRSubDeltaT(mesh, nAlphaSubCycles);
}
// Create a temporary alphaPhi1 to accumulate the sub-cycled alphaPhi1
tmp<surfaceScalarField> talphaPhi1
(
surfaceScalarField::New
(
"alphaPhi1",
mesh,
dimensionedScalar(alphaPhi1.dimensions(), 0)
)
);
List<volScalarField*> alphaPtrs({&alpha1, &alpha2});
for
(
subCycle<volScalarField, subCycleFields> alphaSubCycle
(
alphaPtrs,
nAlphaSubCycles
);
!(++alphaSubCycle).end();
)
{
alphaSolve(alphaControls);
talphaPhi1.ref() += (runTime.deltaT()/totalDeltaT)*alphaPhi1;
}
alphaPhi1 = talphaPhi1();
}
else
{
alphaSolve(alphaControls);
}
mixture.correct();
}
// ************************************************************************* //

View File

@ -0,0 +1,19 @@
tmp<volScalarField> divU;
tmp<volScalarField::Internal> Su;
tmp<volScalarField::Internal> Sp;
if (divergent())
{
// Phase change alpha1 source
const fvScalarMatrix alphaSup(fvModels().source(alpha1));
Su = alphaSup.Su();
Sp = alphaSup.Sp();
divU =
(
mesh.moving()
? fvc::div(phiCN() + mesh.phi())
: fvc::div(phiCN())
);
}

View File

@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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 "VoFSolver.H"
#include "fvmDiv.H"
#include "fvcSnGrad.H"
#include "fvcReconstruct.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
void Foam::solvers::VoFSolver::momentumPredictor()
{
tUEqn =
(
fvm::ddt(rho, U) + fvm::div(rhoPhi, U)
+ MRF.DDt(rho, U)
+ divDevTau(U)
==
fvModels().source(rho, U)
);
fvVectorMatrix& UEqn = tUEqn.ref();
UEqn.relax();
fvConstraints().constrain(UEqn);
if (pimple.momentumPredictor())
{
solve
(
UEqn
==
fvc::reconstruct
(
(
interface.surfaceTensionForce()
- buoyancy.ghf*fvc::snGrad(rho)
- fvc::snGrad(p_rgh)
) * mesh.magSf()
)
);
fvConstraints().constrain(U);
}
}
// ************************************************************************* //

View File

@ -0,0 +1,118 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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 "VoFSolver.H"
#include "CorrectPhi.H"
#include "geometricZeroField.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
bool Foam::solvers::VoFSolver::moveMesh()
{
if (pimple.firstIter() || pimple.moveMeshOuterCorrectors())
{
if
(
correctPhi
&& divergent()
&& !divU.valid()
)
{
// Construct and register divU for correctPhi
divU = new volScalarField
(
"divU0",
fvc::div(fvc::absolute(phi, U))
);
}
// Move the mesh
mesh.move();
if (mesh.changing())
{
buoyancy.moveMesh();
MRF.update();
if (correctPhi || mesh.topoChanged())
{
// Calculate absolute flux
// from the mapped surface velocity
phi = mesh.Sf() & Uf();
correctUphiBCs(U, phi, true);
if (correctPhi)
{
if (divU.valid())
{
CorrectPhi
(
phi,
U,
p_rgh,
surfaceScalarField("rAUf", fvc::interpolate(rAU())),
divU(),
pressureReference(),
pimple
);
}
else
{
CorrectPhi
(
phi,
U,
p_rgh,
surfaceScalarField("rAUf", fvc::interpolate(rAU())),
geometricZeroField(),
pressureReference(),
pimple
);
}
}
// Make the fluxes relative to the mesh motion
fvc::makeRelative(phi, U);
}
meshCourantNo();
interface.correct();
divU.clear();
return true;
}
divU.clear();
}
return false;
}
// ************************************************************************* //

View File

@ -0,0 +1,183 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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 "VoFSolver.H"
#include "fvcSmooth.H"
#include "fvcSurfaceIntegrate.H"
#include "fvcAverage.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::solvers::VoFSolver::setRDeltaT()
{
volScalarField& rDeltaT = trDeltaT.ref();
const dictionary& pimpleDict = pimple.dict();
const scalar maxCo
(
pimpleDict.lookupOrDefault<scalar>("maxCo", 0.9)
);
const scalar maxAlphaCo
(
pimpleDict.lookupOrDefault<scalar>("maxAlphaCo", 0.2)
);
const scalar rDeltaTSmoothingCoeff
(
pimpleDict.lookupOrDefault<scalar>("rDeltaTSmoothingCoeff", 0.1)
);
const label nAlphaSpreadIter
(
pimpleDict.lookupOrDefault<label>("nAlphaSpreadIter", 1)
);
const scalar alphaSpreadDiff
(
pimpleDict.lookupOrDefault<scalar>("alphaSpreadDiff", 0.2)
);
const scalar alphaSpreadMax
(
pimpleDict.lookupOrDefault<scalar>("alphaSpreadMax", 0.99)
);
const scalar alphaSpreadMin
(
pimpleDict.lookupOrDefault<scalar>("alphaSpreadMin", 0.01)
);
const label nAlphaSweepIter
(
pimpleDict.lookupOrDefault<label>("nAlphaSweepIter", 5)
);
const scalar maxDeltaT
(
pimpleDict.lookupOrDefault<scalar>("maxDeltaT", great)
);
const scalar minDeltaT
(
pimpleDict.lookupOrDefault<scalar>("minDeltaT", small)
);
const volScalarField rDeltaT0("rDeltaT0", rDeltaT);
// Set the reciprocal time-step from the local Courant number
// and maximum and minimum time-steps
rDeltaT.ref() = min
(
1/dimensionedScalar(dimTime, minDeltaT),
max
(
1/dimensionedScalar(dimTime, maxDeltaT),
fvc::surfaceSum(mag(phi))()()
/((2*maxCo)*mesh.V())
)
);
if (maxAlphaCo < maxCo)
{
// Further limit the reciprocal time-step
// in the vicinity of the interface
volScalarField alpha1Bar(fvc::average(alpha1));
rDeltaT.ref() = max
(
rDeltaT(),
pos0(alpha1Bar() - alphaSpreadMin)
*pos0(alphaSpreadMax - alpha1Bar())
*fvc::surfaceSum(mag(phi))()()
/((2*maxAlphaCo)*mesh.V())
);
}
// Update the boundary values of the reciprocal time-step
rDeltaT.correctBoundaryConditions();
Info<< "Flow time scale min/max = "
<< gMin(1/rDeltaT.primitiveField())
<< ", " << gMax(1/rDeltaT.primitiveField()) << endl;
if (rDeltaTSmoothingCoeff < 1.0)
{
fvc::smooth(rDeltaT, rDeltaTSmoothingCoeff);
}
if (nAlphaSpreadIter > 0)
{
fvc::spread
(
rDeltaT,
alpha1,
nAlphaSpreadIter,
alphaSpreadDiff,
alphaSpreadMax,
alphaSpreadMin
);
}
if (nAlphaSweepIter > 0)
{
fvc::sweep(rDeltaT, alpha1, nAlphaSweepIter, alphaSpreadDiff);
}
Info<< "Smoothed flow time scale min/max = "
<< gMin(1/rDeltaT.primitiveField())
<< ", " << gMax(1/rDeltaT.primitiveField()) << endl;
// Limit rate of change of time scale
// - reduce as much as required
// - only increase at a fraction of old time scale
if
(
pimpleDict.found("rDeltaTDampingCoeff")
&& runTime.timeIndex() > runTime.startTimeIndex() + 1
)
{
// Damping coefficient (1-0)
const scalar rDeltaTDampingCoeff
(
pimpleDict.lookup<scalar>("rDeltaTDampingCoeff")
);
rDeltaT = max
(
rDeltaT,
(scalar(1) - rDeltaTDampingCoeff)*rDeltaT0
);
Info<< "Damped flow time scale min/max = "
<< gMin(1/rDeltaT.primitiveField())
<< ", " << gMax(1/rDeltaT.primitiveField()) << endl;
}
}
// ************************************************************************* //