diff --git a/dumux/multidomain/CMakeLists.txt b/dumux/multidomain/CMakeLists.txt index 2a80fbedd0d53319bb6906f8326582c6184b0841..3c791b8787c300e919aa9c17147c94b10a2bbe25 100644 --- a/dumux/multidomain/CMakeLists.txt +++ b/dumux/multidomain/CMakeLists.txt @@ -3,5 +3,6 @@ couplingjacobianpattern.hh couplingmanager.hh newtonsolver.hh subdomainboxlocalassembler.hh +subdomainstaggeredlocalassembler.hh traits.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/multidomain) diff --git a/dumux/multidomain/subdomainstaggeredlocalassembler.hh b/dumux/multidomain/subdomainstaggeredlocalassembler.hh new file mode 100644 index 0000000000000000000000000000000000000000..ec60c2179317005178c4b276cf4d472f4a0f04d1 --- /dev/null +++ b/dumux/multidomain/subdomainstaggeredlocalassembler.hh @@ -0,0 +1,960 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \ingroup Assembly + * \ingroup StaggeredDiscretization + * \brief An assembler for Jacobian and residual contribution per element (staggered method) + * \tparam TypeTag the TypeTag + * \tparam DM the differentiation method to residual compute derivatives + * \tparam implicit if to use an implicit or explicit time discretization + */ +#ifndef DUMUX_MULTIDOMAIN_STAGGERED_LOCAL_ASSEMBLER_HH +#define DUMUX_MULTIDOMAIN_STAGGERED_LOCAL_ASSEMBLER_HH + +#include <dune/common/reservedvector.hh> +#include <dune/grid/common/gridenums.hh> // for GhostEntity +#include <dune/istl/matrixindexset.hh> + +#include <dumux/common/reservedblockvector.hh> +#include <dumux/common/properties.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/numericdifferentiation.hh> +#include <dumux/common/typetraits/utility.hh> +#include <dumux/assembly/diffmethod.hh> +#include <dumux/assembly/fvlocalassemblerbase.hh> + +namespace Dumux { + +/*! + * \ingroup Assembly + * \ingroup StaggeredDiscretization + * \brief A base class for all local assemblers + * \tparam id the id of the sub domain + * \tparam TypeTag the TypeTag + * \tparam Assembler the assembler type + */ +template<std::size_t id, class TypeTag, class Assembler, class Implementation, bool isImplicit = true> +class SubDomainStaggeredLocalAssemblerBase : public FVLocalAssemblerBase<TypeTag, Assembler,Implementation, isImplicit> +{ + using ParentType = FVLocalAssemblerBase<TypeTag, Assembler,Implementation, isImplicit>; + + using Problem = typename GET_PROP_TYPE(TypeTag, Problem); + using LocalResidualValues = typename GET_PROP_TYPE(TypeTag, NumEqVector); + using LocalResidual = typename GET_PROP_TYPE(TypeTag, LocalResidual); + using ElementResidualVector = typename LocalResidual::ElementResidualVector; + using JacobianMatrix = typename GET_PROP_TYPE(TypeTag, JacobianMatrix); + using SolutionVector = typename Assembler::SolutionVector; + using SubSolutionVector = typename GET_PROP_TYPE(TypeTag, SolutionVector); + using ElementBoundaryTypes = typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes); + + using GridVariables = typename GET_PROP_TYPE(TypeTag, GridVariables); + using GridVolumeVariables = typename GridVariables::GridVolumeVariables; + using ElementVolumeVariables = typename GridVolumeVariables::LocalView; + using ElementFluxVariablesCache = typename GridVariables::GridFluxVariablesCache::LocalView; + using Scalar = typename GridVariables::Scalar; + + using ElementFaceVariables = typename GET_PROP_TYPE(TypeTag, GridFaceVariables)::LocalView; + using CellCenterResidualValue = typename LocalResidual::CellCenterResidualValue; + using FaceResidualValue = typename LocalResidual::FaceResidualValue; + + using FVGridGeometry = typename GridVariables::GridGeometry; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVGridGeometry::SubControlVolume; + using SubControlVolumeFace = typename FVGridGeometry::SubControlVolumeFace; + using GridView = typename GET_PROP_TYPE(TypeTag, GridView); + using Element = typename GridView::template Codim<0>::Entity; + + using CouplingManager = typename Assembler::CouplingManager; + + static constexpr auto numEq = GET_PROP_TYPE(TypeTag, ModelTraits)::numEq(); + +public: + static constexpr auto domainId = typename Dune::index_constant<id>(); + static constexpr auto cellCenterId = typename Dune::index_constant<0>(); + static constexpr auto faceId = typename Dune::index_constant<1>(); + + static constexpr auto faceOffset = GET_PROP_VALUE(TypeTag, NumEqCellCenter); + + using ParentType::ParentType; + + explicit SubDomainStaggeredLocalAssemblerBase(const Assembler& assembler, + const Element& element, + const SolutionVector& curSol, + CouplingManager& couplingManager) + : ParentType(assembler, + element, + curSol, + localView(assembler.fvGridGeometry(domainId)), + localView(assembler.gridVariables(domainId).curGridVolVars()), + localView(assembler.gridVariables(domainId).prevGridVolVars()), + localView(assembler.gridVariables(domainId).gridFluxVarsCache()), + assembler.localResidual(domainId), + (element.partitionType() == Dune::GhostEntity)) + , curElemFaceVars_(localView(assembler.gridVariables(domainId).curGridFaceVars())) + , prevElemFaceVars_(localView(assembler.gridVariables(domainId).prevGridFaceVars())) + , couplingManager_(couplingManager) + {} + + /*! + * \brief Computes the derivatives with respect to the given element and adds them + * to the global matrix. The element residual is written into the right hand side. + */ + template<class JacobianMatrixRow, class SubSol, class GridVariablesTuple> + void assembleJacobianAndResidual(JacobianMatrixRow& jacRow, SubSol& res, GridVariablesTuple& gridVariables) + { + this->asImp_().bindLocalViews(); + this->elemBcTypes().update(problem(), this->element(), this->fvGeometry()); + + assembleJacobianAndResidualImpl_(domainId, jacRow, res, gridVariables); + } + + /*! + * \brief Assemble the residual only + */ + template<class SubSol> + void assembleResidual(SubSol& res) + { + this->asImp_().bindLocalViews(); + this->elemBcTypes().update(problem(), this->element(), this->fvGeometry()); + + assembleResidualImpl_(domainId, res); + } + + /*! + * \brief Convenience function to evaluate the complete local residual for the current element. Automatically chooses the the appropriate + * element volume variables. + */ + CellCenterResidualValue evalLocalResidualForCellCenter() const + { + if (!isImplicit) + if (this->assembler().isStationaryProblem()) + DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual"); + + if (this->elementIsGhost()) + { + return CellCenterResidualValue(0.0); + } + + return isImplicit ? evalLocalResidualForCellCenter(this->curElemVolVars(), this->curElemFaceVars()) + : evalLocalResidualForCellCenter(this->prevElemVolVars(), this->prevElemFaceVars()); + } + + /*! + * \brief Evaluates the complete local residual for the current cell center. + * \param elemVolVars The element volume variables + * \param elemFaceVars The element face variables + */ + CellCenterResidualValue evalLocalResidualForCellCenter(const ElementVolumeVariables& elemVolVars, + const ElementFaceVariables& elemFaceVars) const + { + auto residual = evalLocalFluxAndSourceResidualForCellCenter(elemVolVars, elemFaceVars); + + if (!this->assembler().isStationaryProblem()) + residual += evalLocalStorageResidualForCellCenter(); + + this->localResidual().evalBoundaryForCellCenter(residual, this->problem(), this->element(), this->fvGeometry(), elemVolVars, elemFaceVars, this->elemBcTypes(), this->elemFluxVarsCache()); + + return residual; + } + + /*! + * \brief Convenience function to evaluate the flux and source terms (i.e, the terms without a time derivative) + * of the local residual for the current element. Automatically chooses the the appropriate + * element volume and face variables. + */ + CellCenterResidualValue evalLocalFluxAndSourceResidualForCellCenter() const + { + return isImplicit ? evalLocalFluxAndSourceResidualForCellCenter(this->curElemVolVars(), this->curElemFaceVars()) + : evalLocalFluxAndSourceResidualForCellCenter(this->prevElemVolVars(), this->prevElemFaceVars()); + } + + /*! + * \brief Evaluates the flux and source terms (i.e, the terms without a time derivative) + * of the local residual for the current element. + * + * \param elemVolVars The element volume variables + * \param elemVolVars The element face variables + */ + CellCenterResidualValue evalLocalFluxAndSourceResidualForCellCenter(const ElementVolumeVariables& elemVolVars, const ElementFaceVariables& elemFaceVars) const + { + return this->localResidual().evalFluxAndSourceForCellCenter(this->element(), this->fvGeometry(), elemVolVars, elemFaceVars, this->elemBcTypes(), this->elemFluxVarsCache()); + } + + /*! + * \brief Convenience function to evaluate storage term (i.e, the term with a time derivative) + * of the local residual for the current element. Automatically chooses the the appropriate + * element volume and face variables. + */ + CellCenterResidualValue evalLocalStorageResidualForCellCenter() const + { + return this->localResidual().evalStorageForCellCenter(this->element(), this->fvGeometry(), this->prevElemVolVars(), this->curElemVolVars()); + } + + /*! + * \brief Convenience function to evaluate the local residual for the current face. Automatically chooses the the appropriate + * element volume and face variables. + * \param scvf The sub control volume face + */ + FaceResidualValue evalLocalResidualForFace(const SubControlVolumeFace& scvf) const + { + if (!isImplicit) + if (this->assembler().isStationaryProblem()) + DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual"); + + if (this->elementIsGhost()) + { + return FaceResidualValue(0.0); + } + + return isImplicit ? evalLocalResidualForFace(scvf, this->curElemVolVars(), this->curElemFaceVars()) + : evalLocalResidualForFace(scvf, this->prevElemVolVars(), this->prevElemFaceVars()); + } + + /*! + * \brief Evaluates the complete local residual for the current face. + * \param scvf The sub control volume face + * \param elemVolVars The element volume variables + * \param elemFaceVars The element face variables + */ + FaceResidualValue evalLocalResidualForFace(const SubControlVolumeFace& scvf, + const ElementVolumeVariables& elemVolVars, + const ElementFaceVariables& elemFaceVars) const + { + auto residual = evalLocalFluxAndSourceResidualForFace(scvf, elemVolVars, elemFaceVars); + + if (!this->assembler().isStationaryProblem()) + residual += evalLocalStorageResidualForFace(scvf); + + this->localResidual().evalBoundaryForFace(residual, this->problem(), this->element(), this->fvGeometry(), elemVolVars, elemFaceVars, this->elemBcTypes(), this->elemFluxVarsCache(), scvf); + + return residual; + } + + /*! + * \brief Convenience function to evaluate the flux and source terms (i.e, the terms without a time derivative) + * of the local residual for the current element. Automatically chooses the the appropriate + * element volume and face variables. + * \param scvf The sub control volume face + */ + FaceResidualValue evalLocalFluxAndSourceResidualForFace(const SubControlVolumeFace& scvf) const + { + return isImplicit ? evalLocalFluxAndSourceResidualForFace(scvf, this->curElemVolVars(), this->curElemFaceVars()) + : evalLocalFluxAndSourceResidualForFace(scvf, this->prevElemVolVars(), this->prevElemFaceVars()); + } + + /*! + * \brief Evaluates the flux and source terms (i.e, the terms without a time derivative) + * of the local residual for the current face. + * \param scvf The sub control volume face + * \param elemVolVars The element volume variables + * \param elemFaceVars The element face variables + */ + FaceResidualValue evalLocalFluxAndSourceResidualForFace(const SubControlVolumeFace& scvf, + const ElementVolumeVariables& elemVolVars, + const ElementFaceVariables& elemFaceVars) const + { + return this->localResidual().evalFluxAndSourceForFace(this->element(), this->fvGeometry(), elemVolVars, elemFaceVars, this->elemBcTypes(), this->elemFluxVarsCache(), scvf); + } + + /*! + * \brief Convenience function to evaluate storage term (i.e, the term with a time derivative) + * of the local residual for the current face. Automatically chooses the the appropriate + * element volume and face variables. + * \param scvf The sub control volume face + */ + FaceResidualValue evalLocalStorageResidualForFace(const SubControlVolumeFace& scvf) const + { + return this->localResidual().evalStorageForFace(this->element(), this->fvGeometry(), this->prevElemVolVars(), this->curElemVolVars(), this->prevElemFaceVars(), this->curElemFaceVars(), scvf); + } + + const Problem& problem() const + { return this->assembler().problem(domainId); } + + //! The current element volume variables + ElementFaceVariables& curElemFaceVars() + { return curElemFaceVars_; } + + //! The element volume variables of the provious time step + ElementFaceVariables& prevElemFaceVars() + { return prevElemFaceVars_; } + + //! The current element volume variables + const ElementFaceVariables& curElemFaceVars() const + { return curElemFaceVars_; } + + //! The element volume variables of the provious time step + const ElementFaceVariables& prevElemFaceVars() const + { return prevElemFaceVars_; } + + CouplingManager& couplingManager() + { return couplingManager_; } + +private: + + //! Assembles the residuals for the cell center dofs. + template<class SubSol> + void assembleResidualImpl_(Dune::index_constant<0>, SubSol& res) + { + const auto cellCenterGlobalI = problem().fvGridGeometry().elementMapper().index(this->element()); + res[cellCenterGlobalI] = this->asImp_().assembleCellCenterResidualImpl(); + } + + //! Assembles the residuals for the face dofs. + template<class SubSol> + void assembleResidualImpl_(Dune::index_constant<1>, SubSol& res) + { + for(auto&& scvf : scvfs(this->fvGeometry())) + { + res[scvf.dofIndex()] += this->asImp_().assembleFaceResidualImpl(scvf); + + } + } + + //! Assembles the residuals and derivatives for the cell center dofs. + template<class JacobianMatrixRow, class SubSol, class GridVariablesTuple> + auto assembleJacobianAndResidualImpl_(Dune::index_constant<0>, JacobianMatrixRow& jacRow, SubSol& res, GridVariablesTuple& gridVariables) + { + const auto cellCenterGlobalI = problem().fvGridGeometry().elementMapper().index(this->element()); + const auto residual = this->asImp_().assembleCellCenterJacobianAndResidualImpl(jacRow[domainId], *std::get<domainId>(gridVariables)); + res[cellCenterGlobalI] = residual; + + + // for the coupling blocks + using namespace Dune::Hybrid; + static constexpr auto otherDomainIds = makeIncompleteIntegerSequence<Dune::Hybrid::size(jacRow), domainId>{}; + forEach(otherDomainIds, [&, domainId = domainId](auto&& domainJ) + { + this->asImp_().assembleJacobianCellCenterCoupling(domainJ, jacRow[domainJ], residual, *std::get<domainJ>(gridVariables)); + }); + } + + //! Assembles the residuals and derivatives for the face dofs. + template<class JacobianMatrixRow, class SubSol, class GridVariablesTuple> + void assembleJacobianAndResidualImpl_(Dune::index_constant<1>, JacobianMatrixRow& jacRow, SubSol& res, GridVariablesTuple& gridVariables) + { + const auto residual = this->asImp_().assembleFaceJacobianAndResidualImpl(jacRow[domainId], *std::get<domainId>(gridVariables)); + + for(auto&& scvf : scvfs(this->fvGeometry())) + res[scvf.dofIndex()] += residual[scvf.localFaceIdx()]; + + // for the coupling blocks + using namespace Dune::Hybrid; + static constexpr auto otherDomainIds = makeIncompleteIntegerSequence<Dune::Hybrid::size(jacRow), domainId>{}; + forEach(otherDomainIds, [&, domainId = domainId](auto&& domainJ) + { + this->asImp_().assembleJacobianFaceCoupling(domainJ, jacRow[domainJ], residual, *std::get<domainJ>(gridVariables)); + }); + } + + ElementFaceVariables curElemFaceVars_; + ElementFaceVariables prevElemFaceVars_; + CouplingManager& couplingManager_; //!< the coupling manager +}; + +/*! + * \ingroup Assembly + * \ingroup CCDiscretization + * \brief A base class for all implicit local assemblers + * \tparam TypeTag the TypeTag + * \tparam Assembler the assembler type + */ +template<std::size_t id, class TypeTag, class Assembler, class Implementation> +class SubDomainStaggeredLocalAssemblerImplicitBase : public SubDomainStaggeredLocalAssemblerBase<id, TypeTag, Assembler, Implementation> +{ + using ParentType = SubDomainStaggeredLocalAssemblerBase<id, TypeTag, Assembler, Implementation>; + static constexpr auto domainId = Dune::index_constant<id>(); +public: + using ParentType::ParentType; + + void bindLocalViews() + { + // get some references for convenience + auto& couplingManager = this->couplingManager(); + const auto& element = this->element(); + const auto& curSol = this->curSol()[domainId]; + auto&& fvGeometry = this->fvGeometry(); + auto&& curElemVolVars = this->curElemVolVars(); + auto&& curElemFaceVars = this->curElemFaceVars(); + auto&& elemFluxVarsCache = this->elemFluxVarsCache(); + + // bind the caches + couplingManager.bindCouplingContext(domainId, element, this->assembler()); + fvGeometry.bind(element); + curElemVolVars.bind(element, fvGeometry, curSol); + curElemFaceVars.bind(element, fvGeometry, curSol); + elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars); + if (!this->assembler().isStationaryProblem()) + { + this->prevElemVolVars().bindElement(element, fvGeometry, this->assembler().prevSol()[domainId]); + this->prevElemFaceVars().bindElement(element, fvGeometry, this->assembler().prevSol()[domainId]); + } + } + +}; + +/*! + * \ingroup Assembly + * \ingroup CCDiscretization + * \brief An assembler for Jacobian and residual contribution per element (cell-centered methods) + * \tparam TypeTag the TypeTag + * \tparam DM the differentiation method to residual compute derivatives + * \tparam implicit if to use an implicit or explicit time discretization + */ +template<std::size_t id, class TypeTag, class Assembler, DiffMethod DM = DiffMethod::numeric, bool implicit = true> +class SubDomainStaggeredLocalAssembler; + +/*! + * \ingroup Assembly + * \ingroup CCDiscretization + * \brief Cell-centered scheme local assembler using numeric differentiation and implicit time discretization + */ +template<std::size_t id, class TypeTag, class Assembler> +class SubDomainStaggeredLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/true> +: public SubDomainStaggeredLocalAssemblerImplicitBase<id, TypeTag, Assembler, + SubDomainStaggeredLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, true> > +{ + using ThisType = SubDomainStaggeredLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/true>; + using ParentType = SubDomainStaggeredLocalAssemblerImplicitBase<id, TypeTag, Assembler, ThisType>; + using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); + using LocalResidual = typename GET_PROP_TYPE(TypeTag, LocalResidual); + using CellCenterResidualValue = typename LocalResidual::CellCenterResidualValue; + using FaceResidualValue = typename LocalResidual::FaceResidualValue; + using Element = typename GET_PROP_TYPE(TypeTag, GridView)::template Codim<0>::Entity; + using GridFaceVariables = typename GET_PROP_TYPE(TypeTag, GridFaceVariables); + using ElementFaceVariables = typename GET_PROP_TYPE(TypeTag, GridFaceVariables)::LocalView; + using FaceVariables = typename ElementFaceVariables::FaceVariables; + using FVGridGeometry = typename GET_PROP_TYPE(TypeTag, FVGridGeometry); + using FVElementGeometry = typename FVGridGeometry::LocalView; + using SubControlVolume = typename FVGridGeometry::SubControlVolume; + using SubControlVolumeFace = typename FVGridGeometry::SubControlVolumeFace; + using VolumeVariables = typename GET_PROP_TYPE(TypeTag, VolumeVariables); + using CellCenterPrimaryVariables = typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables); + using FacePrimaryVariables = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); + using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits); + + static constexpr bool enableGridFluxVarsCache = GET_PROP_VALUE(TypeTag, EnableGridFluxVariablesCache); + static constexpr int maxNeighbors = 4*(2*ModelTraits::dim()); + static constexpr auto domainI = Dune::index_constant<id>(); + static constexpr auto cellCenterId = typename Dune::index_constant<0>(); + static constexpr auto faceId = typename Dune::index_constant<1>(); + + static constexpr auto numEq = ModelTraits::numEq(); + static constexpr auto numEqCellCenter = CellCenterPrimaryVariables::dimension; + static constexpr auto numEqFace = FacePrimaryVariables::dimension; + +public: + using ParentType::ParentType; + + CellCenterResidualValue assembleCellCenterResidualImpl() + { + return this->evalLocalResidualForCellCenter(); + } + + FaceResidualValue assembleFaceResidualImpl(const SubControlVolumeFace& scvf) + { + return this->evalLocalFluxAndSourceResidualForFace(scvf); + } + + /*! + * \brief Computes the derivatives with respect to the given element and adds them + * to the global matrix. + * + * \return The element residual at the current solution. + */ + template<class JacobianMatrixDiagBlock, class GridVariables> + CellCenterResidualValue assembleCellCenterJacobianAndResidualImpl(JacobianMatrixDiagBlock& A, GridVariables& gridVariables) + { + assert(domainI == cellCenterId); + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the // + // neighboring elements we do so by computing the derivatives of the fluxes which depend on the // + // actual element. In the actual element we evaluate the derivative of the entire residual. // + ////////////////////////////////////////////////////////////////////////////////////////////////// + + // get some aliases for convenience + const auto& element = this->element(); + const auto& fvGeometry = this->fvGeometry(); + auto&& curElemVolVars = this->curElemVolVars(); + const auto& fvGridGeometry = this->problem().fvGridGeometry(); + const auto& curSol = this->curSol()[domainI]; + + // get the vecor of the acutal element residuals + // const auto origResiduals = this->evalLocalResidual(); + + const auto cellCenterGlobalI = fvGridGeometry.elementMapper().index(element); + const auto origResidual = this->evalLocalResidualForCellCenter(); + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the // + // neighboring elements we do so by computing the derivatives of the fluxes which depend on the // + // actual element. In the actual element we evaluate the derivative of the entire residual. // + // // + ////////////////////////////////////////////////////////////////////////////////////////////////// + + // build derivatives with for cell center dofs w.r.t. cell center dofs + + const auto& connectivityMap = fvGridGeometry.connectivityMap(); + + // create the vector storing the partial derivatives + CellCenterResidualValue partialDeriv(0.0); + + for(const auto& globalJ : connectivityMap(cellCenterId, cellCenterId, cellCenterGlobalI)) + { + // get the volVars of the element with respect to which we are going to build the derivative + auto&& scvJ = fvGeometry.scv(globalJ); + const auto elementJ = fvGeometry.fvGridGeometry().element(globalJ); + auto& curVolVars = this->getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scvJ); + const auto origVolVars(curVolVars); + + for(int pvIdx = 0; pvIdx < numEqCellCenter; ++pvIdx) + { + using PrimaryVariables = typename VolumeVariables::PrimaryVariables; + const auto& cellCenterPriVars = curSol[globalJ]; + PrimaryVariables priVars = makePriVarsFromCellCenterPriVars<PrimaryVariables>(cellCenterPriVars); + + constexpr auto offset = numEq - numEqCellCenter; + partialDeriv = 0.0; + + auto evalResidual = [&](Scalar priVar) + { + // update the volume variables and compute element residual + priVars[pvIdx + offset] = priVar; + auto elemSol = elementSolution<FVElementGeometry>(std::move(priVars)); + curVolVars.update(elemSol, this->problem(), elementJ, scvJ); + return this->evalLocalResidualForCellCenter(); + }; + + // derive the residuals numerically + const auto& paramGroup = this->problem().paramGroup(); + static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod"); + static const auto eps = this->couplingManager().numericEpsilon(domainI, paramGroup); + NumericDifferentiation::partialDerivative(evalResidual, priVars[pvIdx + offset], partialDeriv, origResidual, + eps(priVars[pvIdx + offset], pvIdx), numDiffMethod); + + + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(A, cellCenterGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the original volVars + curVolVars = origVolVars; + } + } + + return origResidual; + } + + /*! + * \brief Computes the derivatives with respect to the given element and adds them + * to the global matrix. + * + * \return The element residual at the current solution. + */ + template<class JacobianMatrixDiagBlock, class GridVariables> + auto assembleFaceJacobianAndResidualImpl(JacobianMatrixDiagBlock& A, GridVariables& gridVariables) + { + assert(domainI == faceId); + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the // + // neighboring elements we do so by computing the derivatives of the fluxes which depend on the // + // actual element. In the actual element we evaluate the derivative of the entire residual. // + ////////////////////////////////////////////////////////////////////////////////////////////////// + + // get some aliases for convenience + const auto& problem = this->problem(); + const auto& element = this->element(); + const auto& fvGeometry = this->fvGeometry(); + const auto& fvGridGeometry = this->problem().fvGridGeometry(); + const auto& curSol = this->curSol()[domainI]; + + using FaceSolutionVector = typename GET_PROP_TYPE(TypeTag, FaceSolutionVector); // TODO: use reserved vector + FaceSolutionVector origResiduals; + origResiduals.resize(fvGeometry.numScvf()); + origResiduals = 0.0; + + // get the vecor of the acutal element residuals + // const auto origResiduals = this->evalLocalResidual(); + // treat the local residua of the face dofs: + for(auto&& scvf : scvfs(fvGeometry)) + origResiduals[scvf.localFaceIdx()] = this->evalLocalResidualForFace(scvf); + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the // + // neighboring elements we do so by computing the derivatives of the fluxes which depend on the // + // actual element. In the actual element we evaluate the derivative of the entire residual. // + // // + ////////////////////////////////////////////////////////////////////////////////////////////////// + + // build derivatives with for cell center dofs w.r.t. cell center dofs + + const auto& connectivityMap = fvGridGeometry.connectivityMap(); + + FaceResidualValue partialDeriv(0.0); + + for(auto&& scvf : scvfs(fvGeometry)) + { + // set the actual dof index + const auto faceGlobalI = scvf.dofIndex(); + + using FaceSolution = typename GET_PROP_TYPE(TypeTag, StaggeredFaceSolution); + const auto origFaceSolution = FaceSolution(scvf, curSol, fvGridGeometry); + + // build derivatives with for face dofs w.r.t. cell center dofs + for(const auto& globalJ : connectivityMap(faceId, faceId, scvf.index())) + { + // get the faceVars of the face with respect to which we are going to build the derivative + auto& faceVars = getFaceVarAccess(gridVariables.curGridFaceVars(), this->curElemFaceVars(), scvf); + const auto origFaceVars(faceVars); + + for(int pvIdx = 0; pvIdx < numEqFace; ++pvIdx) + { + auto faceSolution = origFaceSolution; + partialDeriv = 0; + + auto evalResidual = [&](Scalar priVar) + { + // update the volume variables and compute element residual + faceSolution[globalJ][pvIdx] = priVar; + faceVars.update(faceSolution, problem, element, fvGeometry, scvf); + return this->evalLocalResidualForFace(scvf); + }; + + // derive the residuals numerically + const auto& paramGroup = problem.paramGroup(); + static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod"); + static const auto eps = this->couplingManager().numericEpsilon(domainI, paramGroup); + NumericDifferentiation::partialDerivative(evalResidual, faceSolution[globalJ][pvIdx], partialDeriv, origResiduals[scvf.localFaceIdx()], + eps(faceSolution[globalJ][pvIdx], pvIdx), numDiffMethod); + + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(A, faceGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the original faceVars + faceVars = origFaceVars; + } + } + } + + return origResiduals; + } + + /*! + * \brief Computes the derivatives with respect to the given element and adds them + * to the global matrix. + * + * \return The element residual at the current solution. + */ + template<class JacobianBlock, class GridVariables> + void assembleJacobianCellCenterCoupling(Dune::index_constant<1> domainJ, JacobianBlock& A, + const CellCenterResidualValue& origResidual, GridVariables& gridVariables) + { + // //////////////////////////////////////////////////////////////////////////////////////////////////////// + // // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. // + // //////////////////////////////////////////////////////////////////////////////////////////////////////// + + // get some aliases for convenience + const auto& element = this->element(); + const auto& fvGeometry = this->fvGeometry(); + const auto& fvGridGeometry = this->problem().fvGridGeometry(); + // const auto& curSol = this->curSol()[domainI]; + // build derivatives with for cell center dofs w.r.t. cell center dofs + const auto cellCenterGlobalI = fvGridGeometry.elementMapper().index(element); + + // create the vector storing the partial derivatives + CellCenterResidualValue partialDeriv(0.0); + + for(const auto& scvfJ : scvfs(fvGeometry)) + { + const auto globalJ = scvfJ.dofIndex(); + + // get the faceVars of the face with respect to which we are going to build the derivative + auto& faceVars = getFaceVarAccess(gridVariables.curGridFaceVars(), this->curElemFaceVars(), scvfJ); + const auto origFaceVars(faceVars); + + for(int pvIdx = 0; pvIdx < numEqFace; ++pvIdx) + { + auto facePriVars(this->curSol()[faceId][globalJ]); + partialDeriv = 0.0; + + auto evalResidual = [&](Scalar priVar) + { + // update the volume variables and compute element residual + facePriVars[pvIdx] = priVar; + faceVars.updateOwnFaceOnly(facePriVars); + return this->evalLocalResidualForCellCenter(); + }; + + // derive the residuals numerically + const auto& paramGroup = this->assembler().problem(domainJ).paramGroup(); + static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod"); + static const auto epsCoupl = this->couplingManager().numericEpsilon(domainJ, paramGroup); + NumericDifferentiation::partialDerivative(evalResidual, facePriVars[pvIdx], partialDeriv, origResidual, + epsCoupl(facePriVars[pvIdx], pvIdx), numDiffMethod); + + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(A, cellCenterGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the original faceVars + faceVars = origFaceVars; + } + } + + } + + template<std::size_t otherId, class JacobianBlock, class GridVariables> + void assembleJacobianCellCenterCoupling(Dune::index_constant<otherId> domainJ, JacobianBlock& A, + const CellCenterResidualValue& res, GridVariables& gridVariables) + { + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. // + //////////////////////////////////////////////////////////////////////////////////////////////////////// + + // get some aliases for convenience + const auto& element = this->element(); + + // get stencil informations + const auto& stencil = this->couplingManager().couplingStencil(domainI, element, domainJ); + + if(stencil.empty()) + return; + + for (const auto globalJ : stencil) + { + const auto origResidual = this->couplingManager().evalCouplingResidual(domainI, *this, domainJ, globalJ); + const auto& curSol = this->curSol()[domainJ]; + const auto origPriVarsJ = curSol[globalJ]; + + for (int pvIdx = 0; pvIdx < JacobianBlock::block_type::cols; ++pvIdx) + { + auto evalCouplingResidual = [&](Scalar priVar) + { + auto deflectedPriVarsJ = origPriVarsJ; + deflectedPriVarsJ[pvIdx] = priVar; + this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, deflectedPriVarsJ, pvIdx); + return this->couplingManager().evalCouplingResidual(domainI, *this, domainJ, globalJ); + }; + + CellCenterResidualValue partialDeriv(0.0); + + // derive the residuals numerically + const auto& paramGroup = this->assembler().problem(domainJ).paramGroup(); + static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod"); + static const auto epsCoupl = this->couplingManager().numericEpsilon(domainJ, paramGroup); + NumericDifferentiation::partialDerivative(evalCouplingResidual, origPriVarsJ[pvIdx], partialDeriv, origResidual, + epsCoupl(origPriVarsJ[pvIdx], pvIdx), numDiffMethod); + + // update the global stiffness matrix with the current partial derivatives + const auto cellCenterGlobalI = this->problem().fvGridGeometry().elementMapper().index(element); + updateGlobalJacobian_(A, cellCenterGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the undeflected state of the coupling context + this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, origPriVarsJ, pvIdx); + } + } + } + + /*! + * \brief Computes the derivatives with respect to the given element and adds them + * to the global matrix. + * + * \return The element residual at the current solution. + */ + template<class JacobianBlock, class ElementResidualVector, class GridVariables> + void assembleJacobianFaceCoupling(Dune::index_constant<0> domainJ, JacobianBlock& A, + const ElementResidualVector& origResiduals, GridVariables& gridVariables) + { + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. // + //////////////////////////////////////////////////////////////////////////////////////////////////////// + + // get some aliases for convenience + const auto& problem = this->problem(); + const auto& fvGeometry = this->fvGeometry(); + const auto& fvGridGeometry = this->problem().fvGridGeometry(); + const auto& connectivityMap = fvGridGeometry.connectivityMap(); + FaceResidualValue partialDeriv; + + // build derivatives with for cell center dofs w.r.t. cell center dofs + for(auto&& scvf : scvfs(fvGeometry)) + { + + // set the actual dof index + const auto faceGlobalI = scvf.dofIndex(); + + // build derivatives with for face dofs w.r.t. cell center dofs + for(const auto& globalJ : connectivityMap(faceId, cellCenterId, scvf.index())) + { + // get the volVars of the element with respect to which we are going to build the derivative + auto&& scvJ = fvGeometry.scv(globalJ); + const auto elementJ = fvGeometry.fvGridGeometry().element(globalJ); + auto& curVolVars = this->getVolVarAccess(gridVariables.curGridVolVars(), this->curElemVolVars(), scvJ); + const auto origVolVars(curVolVars); + const auto origCellCenterPriVars(this->curSol()[cellCenterId][globalJ]); + + for(int pvIdx = 0; pvIdx < numEqCellCenter; ++pvIdx) + { + + using PrimaryVariables = typename VolumeVariables::PrimaryVariables; + const auto& cellCenterPriVars = this->curSol()[cellCenterId][globalJ]; + PrimaryVariables priVars = makePriVarsFromCellCenterPriVars<PrimaryVariables>(cellCenterPriVars); + + constexpr auto offset = PrimaryVariables::dimension - CellCenterPrimaryVariables::dimension; + + partialDeriv = 0; + + auto evalResidual = [&](Scalar priVar) + { + // update the volume variables and compute element residual + priVars[pvIdx + offset] = priVar; + auto elemSol = elementSolution<FVElementGeometry>(std::move(priVars)); + curVolVars.update(elemSol, problem, elementJ, scvJ); + return this->evalLocalResidualForFace(scvf); + }; + + // derive the residuals numerically + const auto& paramGroup = this->assembler().problem(domainJ).paramGroup(); + static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod"); + static const auto epsCoupl = this->couplingManager().numericEpsilon(domainJ, paramGroup); + NumericDifferentiation::partialDerivative(evalResidual, priVars[pvIdx + offset], partialDeriv, origResiduals[scvf.localFaceIdx()], + epsCoupl(priVars[pvIdx + offset], pvIdx), numDiffMethod); + + // update the global jacobian matrix with the current partial derivatives + updateGlobalJacobian_(A, faceGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the original volVars + curVolVars = origVolVars; + } + } + } + + } + + template<std::size_t otherId, class JacobianBlock, class ElementResidualVector, class GridVariables> + void assembleJacobianFaceCoupling(Dune::index_constant<otherId> domainJ, JacobianBlock& A, + const ElementResidualVector& res, GridVariables& gridVariables) + { + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. // + //////////////////////////////////////////////////////////////////////////////////////////////////////// + + // get some aliases for convenience + const auto& fvGeometry = this->fvGeometry(); + const auto& curSol = this->curSol()[domainJ]; + FaceResidualValue partialDeriv; + + // build derivatives with for cell center dofs w.r.t. cell center dofs + for(auto&& scvf : scvfs(fvGeometry)) + { + + // set the actual dof index + const auto faceGlobalI = scvf.dofIndex(); + + // get stencil informations + const auto& stencil = this->couplingManager().couplingStencil(domainI, scvf, domainJ); + + if(stencil.empty()) + continue; + + // build derivatives with for face dofs w.r.t. cell center dofs + for(const auto& globalJ : stencil) + { + const auto origPriVarsJ = curSol[globalJ]; + const auto origResidual = this->couplingManager().evalCouplingResidual(domainI, scvf, *this, domainJ, globalJ); + + for (int pvIdx = 0; pvIdx < JacobianBlock::block_type::cols; ++pvIdx) + { + auto evalCouplingResidual = [&](Scalar priVar) + { + auto deflectedPriVars = origPriVarsJ; + deflectedPriVars[pvIdx] = priVar; + this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, deflectedPriVars, pvIdx); + return this->couplingManager().evalCouplingResidual(domainI, scvf, *this, domainJ, globalJ); + }; + + // derive the residuals numerically + FaceResidualValue partialDeriv(0.0); + const auto& paramGroup = this->assembler().problem(domainJ).paramGroup(); + static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod"); + static const auto epsCoupl = this->couplingManager().numericEpsilon(domainJ, paramGroup); + NumericDifferentiation::partialDerivative(evalCouplingResidual, origPriVarsJ[pvIdx], partialDeriv, origResidual, + epsCoupl(origPriVarsJ[pvIdx], pvIdx), numDiffMethod); + + // update the global stiffness matrix with the current partial derivatives + updateGlobalJacobian_(A, faceGlobalI, globalJ, pvIdx, partialDeriv); + + // restore the undeflected state of the coupling context + this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, origPriVarsJ, pvIdx); + } + } + } + + + } + + template<class JacobianMatrixDiagBlock, class GridVariables> + void evalAdditionalDerivatives(const std::vector<std::size_t>& additionalDofDependencies, + JacobianMatrixDiagBlock& A, GridVariables& gridVariables) + { + + } + + /*! + * \brief Updates the current global Jacobian matrix with the + * partial derivatives of all equations in regard to the + * primary variable 'pvIdx' at dof 'col'. Specialization for cc methods. + */ + template<class SubMatrix, class CCOrFacePrimaryVariables> + static void updateGlobalJacobian_(SubMatrix& matrix, + const int globalI, + const int globalJ, + const int pvIdx, + const CCOrFacePrimaryVariables &partialDeriv) + { + for (int eqIdx = 0; eqIdx < partialDeriv.size(); eqIdx++) + { + // A[i][col][eqIdx][pvIdx] is the rate of change of + // the residual of equation 'eqIdx' at dof 'i' + // depending on the primary variable 'pvIdx' at dof + // 'col'. + + assert(pvIdx >= 0); + assert(eqIdx < matrix[globalI][globalJ].size()); + assert(pvIdx < matrix[globalI][globalJ][eqIdx].size()); + matrix[globalI][globalJ][eqIdx][pvIdx] += partialDeriv[eqIdx]; + } + } + +private: + + template<class T = TypeTag> + static typename std::enable_if<!GET_PROP_VALUE(T, EnableGridFaceVariablesCache), FaceVariables&>::type + getFaceVarAccess(GridFaceVariables& gridFaceVariables, ElementFaceVariables& elemFaceVars, const SubControlVolumeFace& scvf) + { return elemFaceVars[scvf]; } + + template<class T = TypeTag> + static typename std::enable_if<GET_PROP_VALUE(T, EnableGridFaceVariablesCache), FaceVariables&>::type + getFaceVarAccess(GridFaceVariables& gridFaceVariables, ElementFaceVariables& elemFaceVars, const SubControlVolumeFace& scvf) + { return gridFaceVariables.faceVars(scvf.index()); } +}; + +} // end namespace Dumux + +#endif