Added harmonic balance support

This commit is contained in:
Hrvoje Jasak 2018-12-04 10:31:08 +00:00
parent ec17114a75
commit 857db99eb1
2 changed files with 348 additions and 0 deletions

View file

@ -362,6 +362,58 @@ void Foam::solutionControl::correctBoundaryFlux
}
void Foam::solutionControl::correctBoundaryFlux
(
surfaceScalarField& phi,
const volVectorField& U,
const surfaceScalarField& meshPhi
) const
{
// Get necessary data at the boundaries
const volVectorField::GeometricBoundaryField& Ub = U.boundaryField();
const surfaceVectorField::GeometricBoundaryField& Sb =
mesh_.Sf().boundaryField();
surfaceScalarField::GeometricBoundaryField& phib = phi.boundaryField();
const surfaceScalarField::GeometricBoundaryField& meshPhib =
meshPhi.boundaryField();
// Correct flux at boundaries depending on patch type. Note: it is possible
// that we missed some important boundary conditions that need special
// considerations when calculating the flux. Maybe we can reorganise this in
// future by relying on distinction between = and == operators for
// fvsPatchFields. However, that would require serious changes at the
// moment (e.g. using phi = rUA*UEqn.H() as in most solvers would not update
// the boundary values correctly for fixed fvsPatchFields). VV, 20/Apr/2017.
forAll (phib, patchI)
{
// Get patch field
const fvPatchVectorField& Up = Ub[patchI];
if
(
Up.fixesValue()
&& !isA<inletOutletFvPatchVectorField>(Up)
)
{
// This is fixed value patch, flux needs to be recalculated
// with respect to the boundary condition
phib[patchI] == (Up & Sb[patchI]) - meshPhib[patchI];
}
else if
(
isA<slipFvPatchVectorField>(Up)
|| isA<symmetryFvPatchVectorField>(Up)
)
{
// This is slip or symmetry, flux needs to be zero
phib[patchI] == 0.0;
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::solutionControl::solutionControl(fvMesh& mesh, const word& algorithmName)
@ -690,6 +742,280 @@ void Foam::solutionControl::calcSteadyConsistentFlux
}
void Foam::solutionControl::calcSteadyConsistentFlux
(
surfaceScalarField& phi,
const volVectorField& U,
const surfaceScalarField& meshPhi
) const
{
// Store necessary data for this velocity field
const word& UName = U.name();
// Check whether the fields are present in the list
if (!indices_.found(UName))
{
// Get current index as size of the indices list (before insertion)
const label i = indices_.size();
// 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
(
i,
new surfaceScalarField
(
IOobject
(
"aCoeff." + UName,
mesh_.time().timeName(),
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
{
// Index has been set for this field. Check the state of fields
if (!aCoeffPtrs_.set(indices_[UName]))
{
// aCoeff field should be allocated
FatalErrorIn
(
"void solutionControl::calcSteadyConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU"
"\n)"
) << "Index is set, but the aCoeff field is not allocated for "
<< 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:
// 1. Update flux and aCoeff due to under-relaxation
// 2. Scale the flux with aCoeff, making sure that flux at fixed boundaries
// remains consistent
// Get index from the hash table
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
phi.storePrevIter();
// Calculate the ordinary part of the flux (H/A)
phi = (fvc::interpolate(U) & mesh_.Sf()) - meshPhi;
// surfaceScalarField meshPhi("meshPhi", phi);
// mrfZones.relativeFlux(phi);
// meshPhi -= phi;
// Initialize aCoeff to 1
aCoeff = dimensionedScalar("one", dimless, 1.0);
// STAGE 1: consistent under-relaxation handling
addUnderRelaxationFluxContribution(phi, aCoeff, U);
// STAGE 2: scale the flux and correct it at the boundaries
phi /= aCoeff;
correctBoundaryFlux(phi, U, meshPhi);
}
void Foam::solutionControl::calcSteadyMRFConsistentFlux
(
surfaceScalarField& phi,
const volVectorField& U,
const MRFZones& mrfZones
) const
{
// Store necessary data for this velocity field
const word& UName = U.name();
// Check whether the fields are present in the list
if (!indices_.found(UName))
{
// Get current index as size of the indices list (before insertion)
const label i = indices_.size();
// 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
(
i,
new surfaceScalarField
(
IOobject
(
"aCoeff." + UName,
mesh_.time().timeName(),
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
{
// Index has been set for this field. Check the state of fields
if (!aCoeffPtrs_.set(indices_[UName]))
{
// aCoeff field should be allocated
FatalErrorIn
(
"void solutionControl::calcSteadyConsistentFlux"
"\n("
"\n surfaceScalarField& phi,"
"\n const volVectorField& U,"
"\n const volScalarField& rAU"
"\n)"
) << "Index is set, but the aCoeff field is not allocated for "
<< 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:
// 1. Update flux and aCoeff due to under-relaxation
// 2. Scale the flux with aCoeff, making sure that flux at fixed boundaries
// remains consistent
// Get index from the hash table
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
phi.storePrevIter();
// Calculate the ordinary part of the flux (H/A)
phi = (fvc::interpolate(U) & mesh_.Sf());
surfaceScalarField meshPhi("meshPhi", phi);
mrfZones.relativeFlux(phi);
meshPhi -= phi;
// Initialize aCoeff to 1
aCoeff = dimensionedScalar("one", dimless, 1.0);
// STAGE 1: consistent under-relaxation handling
addUnderRelaxationFluxContribution(phi, aCoeff, U);
// STAGE 2: scale the flux and correct it at the boundaries
phi /= aCoeff;
correctBoundaryFlux(phi, U, meshPhi);
}
void Foam::solutionControl::reconstructTransientVelocity
(
volVectorField& U,

View file

@ -39,6 +39,7 @@ Description
#include "fvsPatchField.H"
#include "fvMatrices.H"
#include "wordRe.H"
#include "MRFZones.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -183,6 +184,13 @@ protected:
const volVectorField& U
) const;
void correctBoundaryFlux
(
surfaceScalarField& phi,
const volVectorField& U,
const surfaceScalarField& meshPhi
) const;
private:
@ -286,6 +294,20 @@ public:
const volVectorField& U
) const;
void calcSteadyConsistentFlux
(
surfaceScalarField& phi,
const volVectorField& U,
const surfaceScalarField& meshPhi
) const;
void calcSteadyMRFConsistentFlux
(
surfaceScalarField& phi,
const volVectorField& U,
const MRFZones& mrfZones
) const;
//- Reconstruct velocity for transient solvers (after pressure
// equation and flux reconstruction).
void reconstructTransientVelocity