/*---------------------------------------------------------------------------*\ ========= | \\ / 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