diff --git a/dumux/multidomain/facet/cellcentered/mpfa/localassembler.hh b/dumux/multidomain/facet/cellcentered/mpfa/localassembler.hh index c9ed42f75ce8c83bbd633567f2766c10f1f1c8bd..54cf1e8e26ee68956628a968e8434128a070dd61 100644 --- a/dumux/multidomain/facet/cellcentered/mpfa/localassembler.hh +++ b/dumux/multidomain/facet/cellcentered/mpfa/localassembler.hh @@ -120,7 +120,161 @@ public: template< class DataHandle, class IV, class GetRho > void assembleGravity(DataHandle& handle, const IV& iv, const GetRho& getRho) { - DUNE_THROW(Dune::NotImplemented, "mpfa facet coupling with gravity"); + using GridView = typename IV::Traits::GridView; + static constexpr int dim = GridView::dimension; + static constexpr int dimWorld = GridView::dimensionworld; + static constexpr bool isSurfaceGrid = dim < dimWorld; + + // resize the gravity vectors + auto& g = handle.g(); + auto& deltaG = handle.deltaG(); + auto& outsideG = handle.gOutside(); + Helper::resizeVector(g, iv.numFaces()); + Helper::resizeVector(deltaG, iv.numUnknowns()); + if (isSurfaceGrid) + Helper::resizeVector(outsideG, iv.numFaces()); + + //! For each face, we... + //! - arithmetically average the phase densities + //! - compute the term \f$ \alpha := \mathbf{A} \rho \ \mathbf{n}^T \mathbf{K} \mathbf{g} \f$ in each neighboring cell + //! - compute \f$ \alpha^* = \sum{\alpha_{outside, i}} - \alpha_{inside} \f$ + using Scalar = typename IV::Traits::MatVecTraits::TMatrix::value_type; + using LocalIndexType = typename IV::Traits::IndexSet::LocalIndexType; + + // xi factor for coupling conditions + static const Scalar xi = getParamFromGroup<Scalar>(this->problem().paramGroup(), "FacetCoupling.Xi", 1.0); + + for (LocalIndexType faceIdx = 0; faceIdx < iv.numFaces(); ++faceIdx) + { + // gravitational acceleration on this face + const auto& curLocalScvf = iv.localScvf(faceIdx); + const auto& curGlobalScvf = this->fvGeometry().scvf(curLocalScvf.gridScvfIndex()); + const auto& gravity = this->problem().gravityAtPos(curGlobalScvf.ipGlobal()); + const auto curIsInteriorBoundary = curLocalScvf.isOnInteriorBoundary(); + const Scalar curXiFactor = curIsInteriorBoundary ? (curGlobalScvf.boundary() ? 1.0 : xi) : 1.0; + + // get permeability tensor in "positive" sub volume + const auto& neighborScvIndices = curLocalScvf.neighboringLocalScvIndices(); + const auto& posGlobalScv = this->fvGeometry().scv(iv.localScv(neighborScvIndices[0]).gridScvIndex()); + const auto& posVolVars = this->elemVolVars()[posGlobalScv]; + const auto alpha_inside = posVolVars.extrusionFactor()*vtmv(curGlobalScvf.unitOuterNormal(), + posVolVars.permeability(), + gravity); + + const auto numOutsideFaces = !curGlobalScvf.boundary() ? curGlobalScvf.numOutsideScvs() : 0; + using OutsideAlphaStorage = std::conditional_t< isSurfaceGrid, + std::vector<Scalar>, + Dune::ReservedVector<Scalar, 1> >; + OutsideAlphaStorage alpha_outside; alpha_outside.resize(numOutsideFaces); + std::fill(alpha_outside.begin(), alpha_outside.end(), 0.0); + Scalar rho; + + if (isSurfaceGrid && !curIsInteriorBoundary) + Helper::resizeVector(outsideG[faceIdx], numOutsideFaces); + + if (!curLocalScvf.isDirichlet()) + { + const auto localDofIdx = curLocalScvf.localDofIndex(); + const Scalar curOneMinusXi = curIsInteriorBoundary ? -(1.0 - xi) : 1.0; + + rho = getRho(posVolVars); + deltaG[localDofIdx] = 0.0; + + if (!curGlobalScvf.boundary()) + { + for (unsigned int idxInOutside = 0; idxInOutside < curGlobalScvf.numOutsideScvs(); ++idxInOutside) + { + // obtain outside tensor + const auto negLocalScvIdx = neighborScvIndices[idxInOutside+1]; + const auto& negGlobalScv = this->fvGeometry().scv(iv.localScv(negLocalScvIdx).gridScvIndex()); + const auto& negVolVars = this->elemVolVars()[negGlobalScv]; + const auto& flipScvf = !isSurfaceGrid ? curGlobalScvf + : this->fvGeometry().flipScvf(curGlobalScvf.index(), idxInOutside); + + alpha_outside[idxInOutside] = negVolVars.extrusionFactor()*vtmv(flipScvf.unitOuterNormal(), + negVolVars.permeability(), + gravity); + if (isSurfaceGrid) + alpha_outside[idxInOutside] *= -1.0; + + if (!curIsInteriorBoundary) + rho += getRho(negVolVars); + + deltaG[localDofIdx] += curOneMinusXi*alpha_outside[idxInOutside]; + } + } + + if (curIsInteriorBoundary) + { + const auto posElement = this->fvGeometry().fvGridGeometry().element(posGlobalScv.elementIndex()); + const auto& facetVolVars = this->problem().couplingManager().getLowDimVolVars(posElement, curGlobalScvf); + rho += getRho(facetVolVars); + rho /= 2.0; + const auto alphaFacet = posVolVars.extrusionFactor()*vtmv(curGlobalScvf.unitOuterNormal(), + facetVolVars.permeability(), + gravity); + deltaG[localDofIdx] += alphaFacet; + } + else + rho /= numOutsideFaces + 1; + + deltaG[localDofIdx] -= curXiFactor*alpha_inside; + deltaG[localDofIdx] *= rho*curGlobalScvf.area(); + } + // use average density between facet and cell density on interior Dirichlet boundaries + else if (curIsInteriorBoundary) + { + const auto posElement = this->fvGeometry().fvGridGeometry().element(posGlobalScv.elementIndex()); + const auto& facetVolVars = this->problem().couplingManager().getLowDimVolVars(posElement, curGlobalScvf); + rho = 0.5*(getRho(facetVolVars) + getRho(posVolVars)); + } + // use density resulting from Dirichlet BCs + else + rho = getRho(this->elemVolVars()[curGlobalScvf.outsideScvIdx()]); + + // add "inside" & "outside" alphas to gravity containers + g[faceIdx] = alpha_inside*rho*curGlobalScvf.area(); + + if (isSurfaceGrid && !curIsInteriorBoundary) + { + unsigned int i = 0; + for (const auto& alpha : alpha_outside) + outsideG[faceIdx][i++] = alpha*rho*curGlobalScvf.area(); + } + } + + // add iv-wide contributions to gravity vectors + handle.CA().umv(deltaG, g); + if (isSurfaceGrid) + { + using FaceVector = typename IV::Traits::MatVecTraits::FaceVector; + FaceVector AG; + Helper::resizeVector(AG, iv.numUnknowns()); + handle.A().mv(deltaG, AG); + + // compute gravitational accelerations + for (const auto& localFaceData : iv.localFaceData()) + { + // continue only for "outside" faces + if (!localFaceData.isOutsideFace()) + continue; + + const auto localScvIdx = localFaceData.ivLocalInsideScvIndex(); + const auto localScvfIdx = localFaceData.ivLocalScvfIndex(); + const auto idxInOutside = localFaceData.scvfLocalOutsideScvfIndex(); + const auto& posLocalScv = iv.localScv(localScvIdx); + const auto& wijk = handle.omegas()[localScvfIdx][idxInOutside+1]; + + // add contributions from all local directions + for (LocalIndexType localDir = 0; localDir < dim; localDir++) + { + // the scvf corresponding to this local direction in the scv + const auto& curLocalScvf = iv.localScvf(posLocalScv.localScvfIndex(localDir)); + if (!curLocalScvf.isDirichlet()) + outsideG[localScvfIdx][idxInOutside] -= wijk[localDir]*AG[curLocalScvf.localDofIndex()]; + } + } + } } private: