Consistency update in solution control class:

The rewrite enables multiple pressure/velocity systems to be treated in a time
and under-relaxation consistent way (useful for multiphase flows).
This commit is contained in:
Vuko Vukcevic 2017-04-20 08:15:50 +02:00
parent b2d3172e52
commit 16ea621887
12 changed files with 264 additions and 119 deletions

View file

@ -105,7 +105,7 @@ int main(int argc, char *argv[])
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/piso.aCoeff(), fvc::interpolate(rAU)/piso.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -116,7 +116,7 @@ int main(int argc, char *argv[])
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/piso.aCoeff(), fvc::interpolate(rAU)/piso.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -122,7 +122,7 @@ int main(int argc, char *argv[])
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/piso.aCoeff(), fvc::interpolate(rAU)/piso.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -96,7 +96,7 @@ int main(int argc, char *argv[])
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/piso.aCoeff(), fvc::interpolate(rAU)/piso.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -100,7 +100,7 @@ int main(int argc, char *argv[])
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/piso.aCoeff(), fvc::interpolate(rAU)/piso.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -21,7 +21,7 @@
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/pimple.aCoeff(), fvc::interpolate(rAU)/pimple.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -21,7 +21,7 @@
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/pimple.aCoeff(), fvc::interpolate(rAU)/pimple.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -107,7 +107,7 @@ int main(int argc, char *argv[])
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/piso.aCoeff(), fvc::interpolate(rAU)/piso.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -15,7 +15,7 @@
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAU)/simple.aCoeff(), fvc::interpolate(rAU)/simple.aCoeff(U.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -15,7 +15,7 @@
( (
fvm::laplacian fvm::laplacian
( (
fvc::interpolate(rAUrel)/simple.aCoeff(), fvc::interpolate(rAUrel)/simple.aCoeff(Urel.name()),
p, p,
"laplacian(rAU," + p.name() + ')' "laplacian(rAU," + p.name() + ')'
) )

View file

@ -25,7 +25,6 @@ License
#include "solutionControl.H" #include "solutionControl.H"
#include "lduMatrix.H" #include "lduMatrix.H"
#include "demandDrivenData.H"
#include "fvc.H" #include "fvc.H"
#include "slipFvPatchFields.H" #include "slipFvPatchFields.H"
#include "symmetryFvPatchFields.H" #include "symmetryFvPatchFields.H"
@ -267,6 +266,12 @@ void Foam::solutionControl::addUnderRelaxationFluxContribution
// Get under-relaxation factor used in this iteration // Get under-relaxation factor used in this iteration
const dimensionedScalar alphaU(this->relaxFactor(U)); const dimensionedScalar alphaU(this->relaxFactor(U));
// Return if the under-relaxation factor is >= 1 and =< 0
if ((alphaU.value() > 1.0 - SMALL) || (alphaU.value() < SMALL))
{
return;
}
const dimensionedScalar urfCoeff((1.0 - alphaU)/alphaU); const dimensionedScalar urfCoeff((1.0 - alphaU)/alphaU);
// Add under-relaxation dependent flux contribution // Add under-relaxation dependent flux contribution
@ -274,14 +279,6 @@ void Foam::solutionControl::addUnderRelaxationFluxContribution
// Add under-relaxation dependent contribution to diffusion scale coeff // Add under-relaxation dependent contribution to diffusion scale coeff
aCoeff += urfCoeff*aCoeff; aCoeff += urfCoeff*aCoeff;
// Note: this is not 100% entirely consistent with the way FOAM handles
// under-relaxation. In fvMatrix::relax() member function, the diagonal
// dominance is first assured by limiting the diagonal and the matrix is
// then relaxed. However, this is a very minor inconsistency, especially
// since this term vanishes when non-linear iterations converge to a tight
// tolerance (achieving steady state in SIMPLE or converging PISO/PIMPLE in
// each time step). VV, 23/Dec/2016.
} }
@ -344,18 +341,16 @@ Foam::solutionControl::solutionControl(fvMesh& mesh, const word& algorithmName)
consistent_(false), consistent_(false),
corr_(0), corr_(0),
corrNonOrtho_(0), corrNonOrtho_(0),
aCoeffPtr_(NULL), aCoeffPtrs_(),
faceUPtr_(NULL) faceUPtrs_(),
indices_()
{} {}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::solutionControl::~solutionControl() Foam::solutionControl::~solutionControl()
{ {}
deleteDemandDrivenData(aCoeffPtr_);
deleteDemandDrivenData(faceUPtr_);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
@ -368,55 +363,118 @@ void Foam::solutionControl::calcTransientConsistentFlux
const fvVectorMatrix& ddtUEqn const fvVectorMatrix& ddtUEqn
) const ) const
{ {
// Store necessary fields for time consistency if they are not present // Store necessary data for this velocity field
if (!aCoeffPtr_ && !faceUPtr_) const word& UName = U.name();
{
aCoeffPtr_ = new surfaceScalarField
(
IOobject
(
"aCoeff",
mesh_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensionedScalar("zero", dimless, 0.0)
);
faceUPtr_ = new surfaceVectorField // Check whether the fields are present in the list
( if (!indices_.found(UName))
IOobject
(
"faceU",
mesh_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensionedVector("zero", dimVelocity, vector::zero)
);
}
else if (!aCoeffPtr_ || !faceUPtr_)
{ {
FatalErrorIn // Get current index as size of the indices list (before insertion)
( const label i = indices_.size();
"void solutionControl::calcTransientConsistentFlux"
"\n(" // Insert the index into the hash table
"\n surfaceScalarField& phi," indices_.insert(UName, i);
"\n const volVectorField& U,"
"\n const volScalarField& rAU," // Extend lists
"\n const fvVectorMatrix& ddtUEqn" aCoeffPtrs_.resize(indices_.size());
"\n)" faceUPtrs_.resize(indices_.size());
) << "Either aCoeffPtr_ or faceUPtr_ is allocated while the"
<< " other is not." // Double check whether the fields have been already set
<< nl if (!aCoeffPtrs_.set(i) && !faceUPtrs_.set(i))
<< " This must not happen in transient simulation. Make sure" {
<< " that functions aiding consistency are called in the right" aCoeffPtrs_.set
<< " order (first flux and then velocity reconstruction)." (
<< exit(FatalError); i,
new surfaceScalarField
(
IOobject
(
"aCoeff." + UName,
mesh_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensionedScalar("zero", dimless, 0.0)
)
);
faceUPtrs_.set
(
i,
new surfaceVectorField
(
IOobject
(
"faceU." + UName,
mesh_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensionedVector("zero", dimVelocity, vector::zero)
)
);
}
else if (!aCoeffPtrs_.set(i) || !faceUPtrs_.set(i))
{
FatalErrorIn
(
"void solutionControl::calcTransientConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU,"
"\n const fvVectorMatrix& ddtUEqn"
"\n)"
) << "Either aCoeff or faceU is allocated for field " << UName
<< " while the other is not." << nl
<< " This must not happen in transient simulation. Make sure"
<< " that functions aiding consistency are called in the right"
<< " order (first flux and then velocity reconstruction)."
<< exit(FatalError);
}
}
else
{
// Index has been set for this field, so the fields must be there as
// well. Check and report an error if they are not allocated
if (!aCoeffPtrs_.set(indices_[UName]))
{
FatalErrorIn
(
"void solutionControl::calcTransientConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU,"
"\n const fvVectorMatrix& ddtUEqn"
"\n)"
) << "Index is set, but the aCoeff field is not allocated for "
<< UName << "." << nl
<< "This should not happen for transient simulation." << nl
<< "Something went wrong."
<< exit(FatalError);
}
else if (!faceUPtrs_.set(indices_[UName]))
{
FatalErrorIn
(
"void solutionControl::calcTransientConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU,"
"\n const fvVectorMatrix& ddtUEqn"
"\n)"
) << "Index is set, but the faceU field is not allocated for "
<< UName << "." << nl
<< "This should not happen for transient simulation." << nl
<< "Something went wrong."
<< exit(FatalError);
}
} }
// Algorithm: // Algorithm:
@ -425,9 +483,12 @@ void Foam::solutionControl::calcTransientConsistentFlux
// 3. Scale the flux with aCoeff, making sure that flux at fixed boundaries // 3. Scale the flux with aCoeff, making sure that flux at fixed boundaries
// remains consistent // remains consistent
// Get index from the hash table
const label i = indices_[UName];
// Get fields that will be updated // Get fields that will be updated
surfaceScalarField& aCoeff = *aCoeffPtr_; surfaceScalarField& aCoeff = aCoeffPtrs_[i];
surfaceVectorField& faceU = *faceUPtr_; surfaceVectorField& faceU = faceUPtrs_[i];
// Update face interpolated velocity field. Note: handling of oldTime faceU // Update face interpolated velocity field. Note: handling of oldTime faceU
// fields happens in ddt scheme when calling ddtConsistentPhiCorr inside // fields happens in ddt scheme when calling ddtConsistentPhiCorr inside
@ -464,40 +525,99 @@ void Foam::solutionControl::calcSteadyConsistentFlux
const volVectorField& U const volVectorField& U
) const ) const
{ {
// Store aCoeff field for time consistency if it is not present. Note: faceU // Store necessary data for this velocity field
// does not need to be stored const word& UName = U.name();
if (!aCoeffPtr_ && !faceUPtr_)
// Check whether the fields are present in the list
if (!indices_.found(UName))
{ {
aCoeffPtr_ = new surfaceScalarField // Get current index as size of the indices list (before insertion)
( const label i = indices_.size();
IOobject
// Insert the index into the hash table
indices_.insert(UName, i);
// Extend list (only aCoeff list because faceU does not need to be
// stored for steady state simulations)
aCoeffPtrs_.resize(indices_.size());
// Double check whether the aCoeff field has been already set. Note:
// faceU does not need to be stored for steady state
if (!aCoeffPtrs_.set(i) && faceUPtrs_.empty())
{
aCoeffPtrs_.set
( (
"aCoeff", i,
mesh_.time().timeName(), new surfaceScalarField
mesh_, (
IOobject::NO_READ, IOobject
IOobject::NO_WRITE (
), "aCoeff." + UName,
mesh_, mesh_.time().timeName(),
dimensionedScalar("zero", dimless, 0.0) mesh_,
); IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensionedScalar("zero", dimless, 0.0)
)
);
}
else if (!aCoeffPtrs_.set(i) || !faceUPtrs_.empty())
{
FatalErrorIn
(
"void solutionControl::calcSteadyConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU"
"\n)"
) << "Either aCoeff or faceU is allocated for field " << UName
<< " while the other is not."
<< "This must not happen in transient simulation. Make sure"
<< " that functions aiding consistency are called in the right"
<< " order (first flux and then velocity reconstruction)."
<< exit(FatalError);
}
} }
else if (faceUPtr_) else
{ {
FatalErrorIn // Index has been set for this field. Check the state of fields
( if (!aCoeffPtrs_.set(indices_[UName]))
"void solutionControl::calcSteadyConsistentFlux" {
"\n(" // aCoeff field should be allocated
"\n surfaceScalarField& phi," FatalErrorIn
"\n const volVectorField& U," (
"\n const volScalarField& rAU" "void solutionControl::calcSteadyConsistentFlux"
"\n)" "\n("
) << "faceUPtr_ is allocated." "\n surfaceScalarField& phi,"
<< nl "\n const volVectorField& U,"
<< " This must not happen in steady simulation. Make sure" "\n const volScalarField& rAU"
<< " that the right functions aiding consistency are called in the" "\n)"
<< " right order (first flux and then velocity reconstruction)." ) << "Index is set, but the aCoeff field is not allocated for "
<< exit(FatalError); << UName << "." << nl
<< "This should not happen for steady state simulation." << nl
<< "Something went wrong."
<< exit(FatalError);
}
else if (!faceUPtrs_.empty())
{
// faceU field shouldn't be allocated
FatalErrorIn
(
"void solutionControl::calcSteadyConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU"
"\n)"
) << "Index is set, but the faceU field is allocated for "
<< UName << "." << nl
<< "This should not happen for steady state simulation." << nl
<< "Something went wrong."
<< exit(FatalError);
}
} }
// Algorithm: // Algorithm:
@ -505,8 +625,12 @@ void Foam::solutionControl::calcSteadyConsistentFlux
// 2. Scale the flux with aCoeff, making sure that flux at fixed boundaries // 2. Scale the flux with aCoeff, making sure that flux at fixed boundaries
// remains consistent // remains consistent
// Get aCoeff field // Get index from the hash table
surfaceScalarField& aCoeff = *aCoeffPtr_; const label i = indices_[UName];
// Get aCoeff field. Note: no need to check whether the entry has been found
// since we have just inserted it above
surfaceScalarField& aCoeff = aCoeffPtrs_[i];
// Store previous iteration for the correct handling of under-relaxation // Store previous iteration for the correct handling of under-relaxation
phi.storePrevIter(); phi.storePrevIter();
@ -541,9 +665,13 @@ void Foam::solutionControl::reconstructTransientVelocity
U/rAU + ddtUEqn.H() - fvc::grad(p) U/rAU + ddtUEqn.H() - fvc::grad(p)
); );
// Get name and the corresponding index
const word& UName = U.name();
const label i = indices_[UName];
// Update divergence free face velocity field, whose value will be used in // Update divergence free face velocity field, whose value will be used in
// the next time step. Note that we need to have absolute flux here. // the next time step. Note that we need to have absolute flux here.
if (!faceUPtr_) if (!faceUPtrs_.set(i))
{ {
FatalErrorIn FatalErrorIn
( (
@ -555,11 +683,15 @@ void Foam::solutionControl::reconstructTransientVelocity
"\n const volScalarField& p" "\n const volScalarField& p"
"\n const surfaceScalarField& phi" "\n const surfaceScalarField& phi"
"\n) const" "\n) const"
) << "faceUPtr_ not calculated. Make sure you have called" ) << "faceU not calculated for field " << UName
<< ". Make sure you have called"
<< " calcTransientConsistentFlux(...) before calling this function." << " calcTransientConsistentFlux(...) before calling this function."
<< exit(FatalError); << exit(FatalError);
} }
surfaceVectorField& faceU = *faceUPtr_;
// Get faceU field. Note: no need to check whether the entry has been found
// since we have just checked
surfaceVectorField& faceU = faceUPtrs_[i];
// First interpolate the reconstructed velocity on the faces // First interpolate the reconstructed velocity on the faces
faceU = fvc::interpolate(U); faceU = fvc::interpolate(U);
@ -600,20 +732,27 @@ void Foam::solutionControl::reconstructSteadyVelocity
} }
const Foam::surfaceScalarField& Foam::solutionControl::aCoeff() const const Foam::surfaceScalarField& Foam::solutionControl::aCoeff
(
const word& UName
) const
{ {
if (!aCoeffPtr_) // Get corresponding index
const label i = indices_[UName];
if (!aCoeffPtrs_.set(i))
{ {
FatalErrorIn FatalErrorIn
( (
"const surfaceScalarField& solutionControl::aCoeff() const" "const surfaceScalarField& solutionControl::aCoeff() const"
) << "aCoeffPtr_ not calculated. Make sure you have called" ) << "aCoeff not calculated for field " << UName
<< ". Make sure you have called"
<< " calcTransientConsistentFlux(...) or " << " calcTransientConsistentFlux(...) or "
<< " calcSteadyConsistentFlux(...) before calling aCoeff()." << " calcSteadyConsistentFlux(...) before calling aCoeff()."
<< exit(FatalError); << exit(FatalError);
} }
return *aCoeffPtr_; return aCoeffPtrs_[i];
} }

View file

@ -190,11 +190,16 @@ private:
// Fields necessary for time and under-relaxation consistency // Fields necessary for time and under-relaxation consistency
//- A coeff (A^~) arising from consistency formulation //- A coeff (A^~) arising from consistency formulation. Note: we can
mutable surfaceScalarField* aCoeffPtr_; // have multiple pressure/velocity systems, hence the PtrList
mutable PtrList<surfaceScalarField> aCoeffPtrs_;
//- Face velocity needed for consistent formulation //- Face velocity needed for consistent formulation. Note: we can
mutable surfaceVectorField* faceUPtr_; // have multiple pressure/velocity systems, hence the PtrList
mutable PtrList<surfaceVectorField> faceUPtrs_;
//- Hash Table containing indices of PtrLists for given names
mutable HashTable<label> indices_;
// Private member functions // Private member functions
@ -301,8 +306,9 @@ public:
const volScalarField& p const volScalarField& p
) const; ) const;
//- Const access to aCoeff (needed for pressure equation) //- Const access to aCoeff (needed for pressure equation) given the
const surfaceScalarField& aCoeff() const; // name of the velocity field
const surfaceScalarField& aCoeff(const word& UName) const;
// Evolution // Evolution