/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "volFields.H"
#include "surfaceFields.H"
#include "calculatedFvPatchFields.H"
#include "extrapolatedCalculatedFvPatchFields.H"
#include "coupledFvPatchFields.H"
#include "UIndirectList.H"
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template
template
void Foam::fvMatrix::addToInternalField
(
const labelUList& addr,
const Field& pf,
Field& intf
) const
{
if (addr.size() != pf.size())
{
FatalErrorInFunction
<< "sizes of addressing and field are different"
<< abort(FatalError);
}
forAll(addr, facei)
{
intf[addr[facei]] += pf[facei];
}
}
template
template
void Foam::fvMatrix::addToInternalField
(
const labelUList& addr,
const tmp>& tpf,
Field& intf
) const
{
addToInternalField(addr, tpf(), intf);
tpf.clear();
}
template
template
void Foam::fvMatrix::subtractFromInternalField
(
const labelUList& addr,
const Field& pf,
Field& intf
) const
{
if (addr.size() != pf.size())
{
FatalErrorInFunction
<< "sizes of addressing and field are different"
<< abort(FatalError);
}
forAll(addr, facei)
{
intf[addr[facei]] -= pf[facei];
}
}
template
template
void Foam::fvMatrix::subtractFromInternalField
(
const labelUList& addr,
const tmp>& tpf,
Field& intf
) const
{
subtractFromInternalField(addr, tpf(), intf);
tpf.clear();
}
template
void Foam::fvMatrix::addBoundaryDiag
(
scalarField& diag,
const direction solveCmpt
) const
{
forAll(internalCoeffs_, patchi)
{
addToInternalField
(
lduAddr().patchAddr(patchi),
internalCoeffs_[patchi].component(solveCmpt),
diag
);
}
}
template
void Foam::fvMatrix::addCmptAvBoundaryDiag(scalarField& diag) const
{
forAll(internalCoeffs_, patchi)
{
addToInternalField
(
lduAddr().patchAddr(patchi),
cmptAv(internalCoeffs_[patchi]),
diag
);
}
}
template
void Foam::fvMatrix::addBoundarySource
(
Field& source,
const bool couples
) const
{
forAll(psi_.boundaryField(), patchi)
{
const fvPatchField& ptf = psi_.boundaryField()[patchi];
const Field& pbc = boundaryCoeffs_[patchi];
if (!ptf.coupled())
{
addToInternalField(lduAddr().patchAddr(patchi), pbc, source);
}
else if (couples)
{
const tmp> tpnf = ptf.patchNeighbourField();
const Field& pnf = tpnf();
const labelUList& addr = lduAddr().patchAddr(patchi);
forAll(addr, facei)
{
source[addr[facei]] += cmptMultiply(pbc[facei], pnf[facei]);
}
}
}
}
template
template class ListType>
void Foam::fvMatrix::setValuesFromList
(
const labelUList& cellLabels,
const ListType& values
)
{
const fvMesh& mesh = psi_.mesh();
const cellList& cells = mesh.cells();
const labelUList& own = mesh.owner();
const labelUList& nei = mesh.neighbour();
scalarField& Diag = diag();
Field& psi =
const_cast
<
GeometricField&
>(psi_).primitiveFieldRef();
forAll(cellLabels, i)
{
const label celli = cellLabels[i];
const Type& value = values[i];
psi[celli] = value;
source_[celli] = value*Diag[celli];
if (symmetric() || asymmetric())
{
const cell& c = cells[celli];
forAll(c, j)
{
const label facei = c[j];
if (mesh.isInternalFace(facei))
{
if (symmetric())
{
if (celli == own[facei])
{
source_[nei[facei]] -= upper()[facei]*value;
}
else
{
source_[own[facei]] -= upper()[facei]*value;
}
upper()[facei] = 0.0;
}
else
{
if (celli == own[facei])
{
source_[nei[facei]] -= lower()[facei]*value;
}
else
{
source_[own[facei]] -= upper()[facei]*value;
}
upper()[facei] = 0.0;
lower()[facei] = 0.0;
}
}
else
{
label patchi = mesh.boundaryMesh().whichPatch(facei);
if (internalCoeffs_[patchi].size())
{
label patchFacei =
mesh.boundaryMesh()[patchi].whichFace(facei);
internalCoeffs_[patchi][patchFacei] =
Zero;
boundaryCoeffs_[patchi][patchFacei] =
Zero;
}
}
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template
Foam::fvMatrix::fvMatrix
(
const GeometricField& psi,
const dimensionSet& ds
)
:
lduMatrix(psi.mesh()),
psi_(psi),
dimensions_(ds),
source_(psi.size(), Zero),
internalCoeffs_(psi.mesh().boundary().size()),
boundaryCoeffs_(psi.mesh().boundary().size()),
faceFluxCorrectionPtr_(nullptr)
{
if (debug)
{
InfoInFunction
<< "Constructing fvMatrix for field " << psi_.name() << endl;
}
// Initialise coupling coefficients
forAll(psi.mesh().boundary(), patchi)
{
internalCoeffs_.set
(
patchi,
new Field
(
psi.mesh().boundary()[patchi].size(),
Zero
)
);
boundaryCoeffs_.set
(
patchi,
new Field
(
psi.mesh().boundary()[patchi].size(),
Zero
)
);
}
// Update the boundary coefficients of psi without changing its event No.
GeometricField& psiRef =
const_cast&>(psi_);
label currentStatePsi = psiRef.eventNo();
psiRef.boundaryFieldRef().updateCoeffs();
psiRef.eventNo() = currentStatePsi;
}
template
Foam::fvMatrix::fvMatrix(const fvMatrix& fvm)
:
refCount(),
lduMatrix(fvm),
psi_(fvm.psi_),
dimensions_(fvm.dimensions_),
source_(fvm.source_),
internalCoeffs_(fvm.internalCoeffs_),
boundaryCoeffs_(fvm.boundaryCoeffs_),
faceFluxCorrectionPtr_(nullptr)
{
if (debug)
{
InfoInFunction
<< "Copying fvMatrix for field " << psi_.name() << endl;
}
if (fvm.faceFluxCorrectionPtr_)
{
faceFluxCorrectionPtr_ = new
GeometricField
(
*(fvm.faceFluxCorrectionPtr_)
);
}
}
#ifndef NoConstructFromTmp
template
Foam::fvMatrix::fvMatrix(const tmp>& tfvm)
:
lduMatrix
(
const_cast&>(tfvm()),
tfvm.isTmp()
),
psi_(tfvm().psi_),
dimensions_(tfvm().dimensions_),
source_
(
const_cast&>(tfvm()).source_,
tfvm.isTmp()
),
internalCoeffs_
(
const_cast&>(tfvm()).internalCoeffs_,
tfvm.isTmp()
),
boundaryCoeffs_
(
const_cast&>(tfvm()).boundaryCoeffs_,
tfvm.isTmp()
),
faceFluxCorrectionPtr_(nullptr)
{
if (debug)
{
InfoInFunction
<< "Copying fvMatrix for field " << psi_.name() << endl;
}
if (tfvm().faceFluxCorrectionPtr_)
{
if (tfvm.isTmp())
{
faceFluxCorrectionPtr_ = tfvm().faceFluxCorrectionPtr_;
tfvm().faceFluxCorrectionPtr_ = nullptr;
}
else
{
faceFluxCorrectionPtr_ = new
GeometricField
(
*(tfvm().faceFluxCorrectionPtr_)
);
}
}
tfvm.clear();
}
#endif
template
Foam::fvMatrix::fvMatrix
(
const GeometricField& psi,
Istream& is
)
:
lduMatrix(psi.mesh()),
psi_(psi),
dimensions_(is),
source_(is),
internalCoeffs_(psi.mesh().boundary().size()),
boundaryCoeffs_(psi.mesh().boundary().size()),
faceFluxCorrectionPtr_(nullptr)
{
if (debug)
{
InfoInFunction
<< "Constructing fvMatrix for field " << psi_.name() << endl;
}
// Initialise coupling coefficients
forAll(psi.mesh().boundary(), patchi)
{
internalCoeffs_.set
(
patchi,
new Field
(
psi.mesh().boundary()[patchi].size(),
Zero
)
);
boundaryCoeffs_.set
(
patchi,
new Field
(
psi.mesh().boundary()[patchi].size(),
Zero
)
);
}
}
template
Foam::tmp> Foam::fvMatrix::clone() const
{
return tmp>
(
new fvMatrix(*this)
);
}
// * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * * * //
template
Foam::fvMatrix::~fvMatrix()
{
if (debug)
{
InfoInFunction
<< "Destroying fvMatrix for field " << psi_.name() << endl;
}
if (faceFluxCorrectionPtr_)
{
delete faceFluxCorrectionPtr_;
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template
void Foam::fvMatrix::setValues
(
const labelUList& cellLabels,
const UList& values
)
{
this->setValuesFromList(cellLabels, values);
}
template
void Foam::fvMatrix::setValues
(
const labelUList& cellLabels,
const UIndirectList& values
)
{
this->setValuesFromList(cellLabels, values);
}
template
void Foam::fvMatrix::setReference
(
const label celli,
const Type& value,
const bool forceReference
)
{
if ((forceReference || psi_.needReference()) && celli >= 0)
{
source()[celli] += diag()[celli]*value;
diag()[celli] += diag()[celli];
}
}
template
void Foam::fvMatrix::setReferences
(
const labelUList& cellLabels,
const Type& value,
const bool forceReference
)
{
const bool needRef = (forceReference || psi_.needReference());
if (needRef)
{
forAll(cellLabels, celli)
{
const label cellId = cellLabels[celli];
if (cellId >= 0)
{
source()[cellId] += diag()[cellId]*value;
diag()[cellId] += diag()[cellId];
}
}
}
}
template
void Foam::fvMatrix::setReferences
(
const labelUList& cellLabels,
const UList& values,
const bool forceReference
)
{
const bool needRef = (forceReference || psi_.needReference());
if (needRef)
{
forAll(cellLabels, celli)
{
const label cellId = cellLabels[celli];
if (cellId >= 0)
{
source()[cellId] += diag()[cellId]*values[celli];
diag()[cellId] += diag()[cellId];
}
}
}
}
template
void Foam::fvMatrix::relax(const scalar alpha)
{
if (alpha <= 0)
{
return;
}
if (debug)
{
InfoInFunction
<< "Relaxing " << psi_.name() << " by " << alpha << endl;
}
Field& S = source();
scalarField& D = diag();
// Store the current unrelaxed diagonal for use in updating the source
scalarField D0(D);
// Calculate the sum-mag off-diagonal from the interior faces
scalarField sumOff(D.size(), 0.0);
sumMagOffDiag(sumOff);
// Handle the boundary contributions to the diagonal
forAll(psi_.boundaryField(), patchi)
{
const fvPatchField& ptf = psi_.boundaryField()[patchi];
if (ptf.size())
{
const labelUList& pa = lduAddr().patchAddr(patchi);
Field& iCoeffs = internalCoeffs_[patchi];
if (ptf.coupled())
{
const Field& pCoeffs = boundaryCoeffs_[patchi];
// For coupled boundaries add the diagonal and
// off-diagonal contributions
forAll(pa, face)
{
D[pa[face]] += component(iCoeffs[face], 0);
sumOff[pa[face]] += mag(component(pCoeffs[face], 0));
}
}
else
{
// For non-coupled boundaries add the maximum magnitude diagonal
// contribution to ensure stability
forAll(pa, face)
{
D[pa[face]] += cmptMax(cmptMag(iCoeffs[face]));
}
}
}
}
if (debug)
{
// Calculate amount of non-dominance.
label nNon = 0;
scalar maxNon = 0.0;
scalar sumNon = 0.0;
forAll(D, celli)
{
scalar d = (sumOff[celli] - D[celli])/mag(D[celli]);
if (d > 0)
{
nNon++;
maxNon = max(maxNon, d);
sumNon += d;
}
}
reduce(nNon, sumOp