From d37621b7132f00b89acd7d90a3cac31840cab2eb Mon Sep 17 00:00:00 2001
From: Martin Schneider <martin.schneider@iws.uni-stuttgart.de>
Date: Wed, 26 Jul 2023 12:29:25 +0200
Subject: [PATCH] [md][ffpm] Generalize ff-mass pm coupling managers

---
 .../ffmasspm/couplingmanager.hh               | 280 +----------------
 .../couplingmanager_staggered_cctpfa.hh       | 292 ++++++++++++++++++
 2 files changed, 307 insertions(+), 265 deletions(-)
 create mode 100644 dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager_staggered_cctpfa.hh

diff --git a/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh
index 9f8c8493a5..44fee689e3 100644
--- a/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh
+++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh
@@ -13,279 +13,29 @@
 #ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMASSPM_COUPLINGMANAGER_HH
 #define DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMASSPM_COUPLINGMANAGER_HH
 
-#include <utility>
-#include <memory>
+#include <dumux/discretization/method.hh>
 
-#include <dune/common/float_cmp.hh>
-#include <dune/common/exceptions.hh>
-#include <dumux/common/properties.hh>
-#include <dumux/discretization/staggered/elementsolution.hh>
-#include <dumux/multidomain/couplingmanager.hh>
-#include <dumux/multidomain/boundary/darcydarcy/couplingmapper.hh>
+#include "couplingmanager_staggered_cctpfa.hh"
 
 namespace Dumux {
 
-/*!
- * \ingroup FreeFlowPorousMediumCoupling
- * \brief Coupling manager for Stokes and Darcy domains with equal dimension.
- */
-template<class MDTraits>
-class FreeFlowMassPorousMediumCouplingManager
-: public CouplingManager<MDTraits>
-{
-    using Scalar = typename MDTraits::Scalar;
-    using ParentType = CouplingManager<MDTraits>;
-
-public:
-    static constexpr auto freeFlowMassIndex = typename MDTraits::template SubDomain<0>::Index();
-    static constexpr auto porousMediumIndex = typename MDTraits::template SubDomain<1>::Index();
-
-    using SolutionVectorStorage = typename ParentType::SolutionVectorStorage;
-private:
-
-    // obtain the type tags of the sub problems
-    using FreeFlowMassTypeTag = typename MDTraits::template SubDomain<freeFlowMassIndex>::TypeTag;
-    using PorousMediumTypeTag = typename MDTraits::template SubDomain<porousMediumIndex>::TypeTag;
-
-    using CouplingStencils = std::unordered_map<std::size_t, std::vector<std::size_t> >;
-    using CouplingStencil = CouplingStencils::mapped_type;
-
-    // the sub domain type tags
-    template<std::size_t id>
-    using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
-
-    template<std::size_t id> using GridView = typename GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>::GridView;
-    template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>;
-    template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView;
-    template<std::size_t id> using GridVolumeVariables = GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>;
-    template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables;
-    template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
-    template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView;
-    template<std::size_t id> using GridVariables = GetPropType<SubDomainTypeTag<id>, Properties::GridVariables>;
-    template<std::size_t id> using Element = typename GridView<id>::template Codim<0>::Entity;
-    template<std::size_t id> using PrimaryVariables = GetPropType<SubDomainTypeTag<id>, Properties::PrimaryVariables>;
-    template<std::size_t id> using SubControlVolumeFace = typename FVElementGeometry<id>::SubControlVolumeFace;
-    template<std::size_t id> using SubControlVolume = typename FVElementGeometry<id>::SubControlVolume;
-
-    using VelocityVector = typename Element<freeFlowMassIndex>::Geometry::GlobalCoordinate;
-
-    struct FreeFlowMassCouplingContext
-    {
-        Element<porousMediumIndex> element;
-        VolumeVariables<porousMediumIndex> volVars;
-        FVElementGeometry<porousMediumIndex> fvGeometry;
-        std::size_t dofIdx;
-        std::size_t freeFlowMassScvfIdx;
-        std::size_t porousMediumScvfIdx;
-        mutable VelocityVector velocity; // velocity needs to be set externally, not available in this class
-    };
-
-    struct PorousMediumCouplingContext
-    {
-        Element<freeFlowMassIndex> element;
-        VolumeVariables<freeFlowMassIndex> volVars;
-        FVElementGeometry<freeFlowMassIndex> fvGeometry;
-        std::size_t dofIdx;
-        std::size_t porousMediumScvfIdx;
-        std::size_t freeFlowMassScvfIdx;
-        mutable VelocityVector velocity; // velocity needs to be set externally, not available in this class
-    };
-
-    using CouplingMapper = DarcyDarcyBoundaryCouplingMapper<MDTraits>; // TODO rename/generalize class
-
-public:
-
-    /*!
-     * \brief Methods to be accessed by main
-     */
-    // \{
-
-    //! Initialize the coupling manager
-    void init(std::shared_ptr<Problem<freeFlowMassIndex>> freeFlowMassProblem,
-              std::shared_ptr<Problem<porousMediumIndex>> darcyProblem,
-              SolutionVectorStorage& curSol)
-    {
-        this->setSubProblems(std::make_tuple(freeFlowMassProblem, darcyProblem));
-        this->attachSolution(curSol);
-
-        couplingMapper_.update(*this);
-    }
-
-    // \}
-
-    /*!
-     * \brief Methods to be accessed by the assembly
-     */
-    // \{
-
-    /*!
-     * \brief prepares all data and variables that are necessary to evaluate the residual (called from the local assembler)
-     */
-    template<std::size_t i, class Assembler>
-    void bindCouplingContext(Dune::index_constant<i> domainI, const Element<i>& element, const Assembler& assembler) const
-    {
-        bindCouplingContext_(domainI, element);
-    }
+namespace FreeFlowMassPorousMediumDetail {
 
-    /*!
-     * \brief Update the coupling context
-     */
-    template<std::size_t i, std::size_t j, class LocalAssemblerI>
-    void updateCouplingContext(Dune::index_constant<i> domainI,
-                               const LocalAssemblerI& localAssemblerI,
-                               Dune::index_constant<j> domainJ,
-                               std::size_t dofIdxGlobalJ,
-                               const PrimaryVariables<j>& priVarsJ,
-                               int pvIdxJ)
-    {
-        this->curSol(domainJ)[dofIdxGlobalJ][pvIdxJ] = priVarsJ[pvIdxJ];
+// declaration (specialize for different discretization types)
+template<class MDTraits,
+         class DiscFFMass = typename MDTraits::template SubDomain<0>::GridGeometry::DiscretizationMethod,
+         class DiscPM = typename MDTraits::template SubDomain<1>::GridGeometry::DiscretizationMethod
+         >
+struct FreeFlowMassPorousMediumCouplingManagerSelector;
 
-        // we need to update all solution-depenent components of the coupling context
-        // the dof of domain J has been deflected
-        // if domainJ == freeFlowMassIndex: update volvars in the PorousMediumCouplingContext
-        // if domainJ == porousMediumIndex: update volvars in the FreeFlowMassCouplingContext
-        // as the update is symmetric we only need to write this once
-        auto& context = std::get<1-j>(couplingContext_);
-        for (auto& c : context)
-        {
-            if (c.dofIdx == dofIdxGlobalJ)
-            {
-                const auto elemSol = elementSolution(c.element, this->curSol(domainJ), this->problem(domainJ).gridGeometry());
-                const auto& scv = *scvs(c.fvGeometry).begin();
-                c.volVars.update(elemSol, this->problem(domainJ), c.element, scv);
-            }
-        }
-    }
-
-    // \}
-
-    /*!
-     * \brief Access the coupling context needed for the Stokes domain
-     */
-    template<std::size_t i>
-    const auto& couplingContext(Dune::index_constant<i> domainI,
-                                const FVElementGeometry<i>& fvGeometry,
-                                const SubControlVolumeFace<i> scvf) const
-    {
-        auto& contexts = std::get<i>(couplingContext_);
-
-        if (contexts.empty() || couplingContextBoundForElement_[i] != scvf.insideScvIdx())
-            bindCouplingContext_(domainI, fvGeometry);
-
-        for (const auto& context : contexts)
-        {
-            const auto expectedScvfIdx = domainI == freeFlowMassIndex ? context.freeFlowMassScvfIdx : context.porousMediumScvfIdx;
-            if (scvf.index() == expectedScvfIdx)
-                return context;
-        }
-
-        DUNE_THROW(Dune::InvalidStateException, "No coupling context found at scvf " << scvf.center());
-    }
-
-    /*!
-     * \brief The coupling stencils
-     */
-    // \{
-
-    /*!
-     * \brief The Stokes cell center coupling stencil w.r.t. Darcy DOFs
-     */
-    template<std::size_t i, std::size_t j>
-    const CouplingStencil& couplingStencil(Dune::index_constant<i> domainI,
-                                           const Element<i>& element,
-                                           Dune::index_constant<j> domainJ) const
-    {
-        const auto eIdx = this->problem(domainI).gridGeometry().elementMapper().index(element);
-        return couplingMapper_.couplingStencil(domainI, eIdx, domainJ);
-    }
-
-    // \}
-
-    /*!
-     * \brief Returns whether a given scvf is coupled to the other domain
-     */
-    template<std::size_t i>
-    bool isCoupled(Dune::index_constant<i> domainI, const SubControlVolumeFace<i>& scvf) const
-    { return couplingMapper_.isCoupled(domainI, scvf); }
-
-private:
-    /*!
-     * \brief Returns whether a given scvf is coupled to the other domain
-     */
-    template<std::size_t i>
-    bool isCoupledElement_(Dune::index_constant<i> domainI, std::size_t eIdx) const
-    { return couplingMapper_.isCoupledElement(domainI, eIdx); }
-
-    //! Return the volume variables of domain i for a given element and scv
-    template<std::size_t i>
-    VolumeVariables<i> volVars_(Dune::index_constant<i> domainI,
-                                const Element<i>& element,
-                                const SubControlVolume<i>& scv) const
-    {
-        VolumeVariables<i> volVars;
-        const auto elemSol = elementSolution(
-            element, this->curSol(domainI), this->problem(domainI).gridGeometry()
-        );
-        volVars.update(elemSol, this->problem(domainI), element, scv);
-        return volVars;
-    }
-
-    /*!
-     * \brief prepares all data and variables that are necessary to evaluate the residual of an Darcy element (i.e. Stokes information)
-     */
-    template<std::size_t i>
-    void bindCouplingContext_(Dune::index_constant<i> domainI, const Element<i>& element) const
-    {
-        const auto fvGeometry = localView(this->problem(domainI).gridGeometry()).bindElement(element);
-        bindCouplingContext_(domainI, fvGeometry);
-    }
-
-    /*!
-     * \brief prepares all data and variables that are necessary to evaluate the residual of an Darcy element (i.e. Stokes information)
-     */
-    template<std::size_t i>
-    void bindCouplingContext_(Dune::index_constant<i> domainI, const FVElementGeometry<i>& fvGeometry) const
-    {
-        auto& context = std::get<domainI>(couplingContext_);
-        context.clear();
-
-        const auto eIdx = this->problem(domainI).gridGeometry().elementMapper().index(fvGeometry.element());
-
-        // do nothing if the element is not coupled to the other domain
-        if (!isCoupledElement_(domainI, eIdx))
-            return;
-
-        couplingContextBoundForElement_[domainI] = eIdx;
-
-        for (const auto& scvf : scvfs(fvGeometry))
-        {
-            if (isCoupled(domainI, scvf))
-            {
-                const auto otherElementIdx = couplingMapper_.outsideElementIndex(domainI, scvf);
-                constexpr auto domainJ = Dune::index_constant<1-domainI>();
-                const auto& otherGridGeometry = this->problem(domainJ).gridGeometry();
-                const auto& otherElement = otherGridGeometry.element(otherElementIdx);
-                auto otherFvGeometry = localView(otherGridGeometry).bindElement(otherElement);
-
-                // there is only one scv for TPFA
-                context.push_back({
-                    otherElement,
-                    volVars_(domainJ, otherElement, *std::begin(scvs(otherFvGeometry))),
-                    std::move(otherFvGeometry),
-                    otherElementIdx,
-                    scvf.index(),
-                    couplingMapper_.flipScvfIndex(domainI, scvf),
-                    VelocityVector{}
-                });
-            }
-        }
-    }
+template<class MDTraits>
+struct FreeFlowMassPorousMediumCouplingManagerSelector<MDTraits, DiscretizationMethods::CCTpfa, DiscretizationMethods::CCTpfa>
+{ using type = FFMassPMCouplingManagerStaggeredCCTpfa<MDTraits>; };
 
-    mutable std::tuple<std::vector<FreeFlowMassCouplingContext>, std::vector<PorousMediumCouplingContext>> couplingContext_;
-    mutable std::array<std::size_t, 2> couplingContextBoundForElement_;
+} // end namespace Detail
 
-    CouplingMapper couplingMapper_;
-};
+template<class MDTraits>
+using FreeFlowMassPorousMediumCouplingManager = typename FreeFlowMassPorousMediumDetail::FreeFlowMassPorousMediumCouplingManagerSelector<MDTraits>::type;
 
 } // end namespace Dumux
 
diff --git a/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager_staggered_cctpfa.hh b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager_staggered_cctpfa.hh
new file mode 100644
index 0000000000..948a977698
--- /dev/null
+++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager_staggered_cctpfa.hh
@@ -0,0 +1,292 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+//
+// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+/*!
+ * \file
+ * \ingroup FreeFlowPorousMediumCoupling
+ * \copydoc Dumux::FreeFlowMassPorousMediumCouplingManager
+ */
+
+#ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMASSPM_COUPLINGMANAGER_STAGGERED_TPFA_HH
+#define DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMASSPM_COUPLINGMANAGER_STAGGERED_TPFA_HH
+
+#include <utility>
+#include <memory>
+
+#include <dune/common/float_cmp.hh>
+#include <dune/common/exceptions.hh>
+#include <dumux/common/properties.hh>
+#include <dumux/discretization/staggered/elementsolution.hh>
+#include <dumux/multidomain/couplingmanager.hh>
+#include <dumux/multidomain/boundary/darcydarcy/couplingmapper.hh>
+
+namespace Dumux {
+
+/*!
+ * \ingroup FreeFlowPorousMediumCoupling
+ * \brief Coupling manager for Stokes and Darcy domains with equal dimension.
+ */
+template<class MDTraits>
+class FFMassPMCouplingManagerStaggeredCCTpfa
+: public CouplingManager<MDTraits>
+{
+    using Scalar = typename MDTraits::Scalar;
+    using ParentType = CouplingManager<MDTraits>;
+
+public:
+    static constexpr auto freeFlowMassIndex = typename MDTraits::template SubDomain<0>::Index();
+    static constexpr auto porousMediumIndex = typename MDTraits::template SubDomain<1>::Index();
+
+    using SolutionVectorStorage = typename ParentType::SolutionVectorStorage;
+private:
+
+    // obtain the type tags of the sub problems
+    using FreeFlowMassTypeTag = typename MDTraits::template SubDomain<freeFlowMassIndex>::TypeTag;
+    using PorousMediumTypeTag = typename MDTraits::template SubDomain<porousMediumIndex>::TypeTag;
+
+    using CouplingStencils = std::unordered_map<std::size_t, std::vector<std::size_t> >;
+    using CouplingStencil = CouplingStencils::mapped_type;
+
+    // the sub domain type tags
+    template<std::size_t id>
+    using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
+
+    template<std::size_t id> using GridView = typename GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>::GridView;
+    template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>;
+    template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView;
+    template<std::size_t id> using GridVolumeVariables = GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>;
+    template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables;
+    template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
+    template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView;
+    template<std::size_t id> using GridVariables = GetPropType<SubDomainTypeTag<id>, Properties::GridVariables>;
+    template<std::size_t id> using Element = typename GridView<id>::template Codim<0>::Entity;
+    template<std::size_t id> using PrimaryVariables = GetPropType<SubDomainTypeTag<id>, Properties::PrimaryVariables>;
+    template<std::size_t id> using SubControlVolumeFace = typename FVElementGeometry<id>::SubControlVolumeFace;
+    template<std::size_t id> using SubControlVolume = typename FVElementGeometry<id>::SubControlVolume;
+
+    using VelocityVector = typename Element<freeFlowMassIndex>::Geometry::GlobalCoordinate;
+
+    struct FreeFlowMassCouplingContext
+    {
+        Element<porousMediumIndex> element;
+        VolumeVariables<porousMediumIndex> volVars;
+        FVElementGeometry<porousMediumIndex> fvGeometry;
+        std::size_t dofIdx;
+        std::size_t freeFlowMassScvfIdx;
+        std::size_t porousMediumScvfIdx;
+        mutable VelocityVector velocity; // velocity needs to be set externally, not available in this class
+    };
+
+    struct PorousMediumCouplingContext
+    {
+        Element<freeFlowMassIndex> element;
+        VolumeVariables<freeFlowMassIndex> volVars;
+        FVElementGeometry<freeFlowMassIndex> fvGeometry;
+        std::size_t dofIdx;
+        std::size_t porousMediumScvfIdx;
+        std::size_t freeFlowMassScvfIdx;
+        mutable VelocityVector velocity; // velocity needs to be set externally, not available in this class
+    };
+
+    using CouplingMapper = DarcyDarcyBoundaryCouplingMapper<MDTraits>; // TODO rename/generalize class
+
+public:
+
+    /*!
+     * \brief Methods to be accessed by main
+     */
+    // \{
+
+    //! Initialize the coupling manager
+    void init(std::shared_ptr<Problem<freeFlowMassIndex>> freeFlowMassProblem,
+              std::shared_ptr<Problem<porousMediumIndex>> darcyProblem,
+              SolutionVectorStorage& curSol)
+    {
+        this->setSubProblems(std::make_tuple(freeFlowMassProblem, darcyProblem));
+        this->attachSolution(curSol);
+
+        couplingMapper_.update(*this);
+    }
+
+    // \}
+
+    /*!
+     * \brief Methods to be accessed by the assembly
+     */
+    // \{
+
+    /*!
+     * \brief prepares all data and variables that are necessary to evaluate the residual (called from the local assembler)
+     */
+    template<std::size_t i, class Assembler>
+    void bindCouplingContext(Dune::index_constant<i> domainI, const Element<i>& element, const Assembler& assembler) const
+    {
+        bindCouplingContext_(domainI, element);
+    }
+
+    /*!
+     * \brief Update the coupling context
+     */
+    template<std::size_t i, std::size_t j, class LocalAssemblerI>
+    void updateCouplingContext(Dune::index_constant<i> domainI,
+                               const LocalAssemblerI& localAssemblerI,
+                               Dune::index_constant<j> domainJ,
+                               std::size_t dofIdxGlobalJ,
+                               const PrimaryVariables<j>& priVarsJ,
+                               int pvIdxJ)
+    {
+        this->curSol(domainJ)[dofIdxGlobalJ][pvIdxJ] = priVarsJ[pvIdxJ];
+
+        // we need to update all solution-depenent components of the coupling context
+        // the dof of domain J has been deflected
+        // if domainJ == freeFlowMassIndex: update volvars in the PorousMediumCouplingContext
+        // if domainJ == porousMediumIndex: update volvars in the FreeFlowMassCouplingContext
+        // as the update is symmetric we only need to write this once
+        auto& context = std::get<1-j>(couplingContext_);
+        for (auto& c : context)
+        {
+            if (c.dofIdx == dofIdxGlobalJ)
+            {
+                const auto elemSol = elementSolution(c.element, this->curSol(domainJ), this->problem(domainJ).gridGeometry());
+                const auto& scv = *scvs(c.fvGeometry).begin();
+                c.volVars.update(elemSol, this->problem(domainJ), c.element, scv);
+            }
+        }
+    }
+
+    // \}
+
+    /*!
+     * \brief Access the coupling context needed for the Stokes domain
+     */
+    template<std::size_t i>
+    const auto& couplingContext(Dune::index_constant<i> domainI,
+                                const FVElementGeometry<i>& fvGeometry,
+                                const SubControlVolumeFace<i> scvf) const
+    {
+        auto& contexts = std::get<i>(couplingContext_);
+
+        if (contexts.empty() || couplingContextBoundForElement_[i] != scvf.insideScvIdx())
+            bindCouplingContext_(domainI, fvGeometry);
+
+        for (const auto& context : contexts)
+        {
+            const auto expectedScvfIdx = domainI == freeFlowMassIndex ? context.freeFlowMassScvfIdx : context.porousMediumScvfIdx;
+            if (scvf.index() == expectedScvfIdx)
+                return context;
+        }
+
+        DUNE_THROW(Dune::InvalidStateException, "No coupling context found at scvf " << scvf.center());
+    }
+
+    /*!
+     * \brief The coupling stencils
+     */
+    // \{
+
+    /*!
+     * \brief The Stokes cell center coupling stencil w.r.t. Darcy DOFs
+     */
+    template<std::size_t i, std::size_t j>
+    const CouplingStencil& couplingStencil(Dune::index_constant<i> domainI,
+                                           const Element<i>& element,
+                                           Dune::index_constant<j> domainJ) const
+    {
+        const auto eIdx = this->problem(domainI).gridGeometry().elementMapper().index(element);
+        return couplingMapper_.couplingStencil(domainI, eIdx, domainJ);
+    }
+
+    // \}
+
+    /*!
+     * \brief Returns whether a given scvf is coupled to the other domain
+     */
+    template<std::size_t i>
+    bool isCoupled(Dune::index_constant<i> domainI, const SubControlVolumeFace<i>& scvf) const
+    { return couplingMapper_.isCoupled(domainI, scvf); }
+
+private:
+    /*!
+     * \brief Returns whether a given scvf is coupled to the other domain
+     */
+    template<std::size_t i>
+    bool isCoupledElement_(Dune::index_constant<i> domainI, std::size_t eIdx) const
+    { return couplingMapper_.isCoupledElement(domainI, eIdx); }
+
+    //! Return the volume variables of domain i for a given element and scv
+    template<std::size_t i>
+    VolumeVariables<i> volVars_(Dune::index_constant<i> domainI,
+                                const Element<i>& element,
+                                const SubControlVolume<i>& scv) const
+    {
+        VolumeVariables<i> volVars;
+        const auto elemSol = elementSolution(
+            element, this->curSol(domainI), this->problem(domainI).gridGeometry()
+        );
+        volVars.update(elemSol, this->problem(domainI), element, scv);
+        return volVars;
+    }
+
+    /*!
+     * \brief prepares all data and variables that are necessary to evaluate the residual of an Darcy element (i.e. Stokes information)
+     */
+    template<std::size_t i>
+    void bindCouplingContext_(Dune::index_constant<i> domainI, const Element<i>& element) const
+    {
+        const auto fvGeometry = localView(this->problem(domainI).gridGeometry()).bindElement(element);
+        bindCouplingContext_(domainI, fvGeometry);
+    }
+
+    /*!
+     * \brief prepares all data and variables that are necessary to evaluate the residual of an Darcy element (i.e. Stokes information)
+     */
+    template<std::size_t i>
+    void bindCouplingContext_(Dune::index_constant<i> domainI, const FVElementGeometry<i>& fvGeometry) const
+    {
+        auto& context = std::get<domainI>(couplingContext_);
+        context.clear();
+
+        const auto eIdx = this->problem(domainI).gridGeometry().elementMapper().index(fvGeometry.element());
+
+        // do nothing if the element is not coupled to the other domain
+        if (!isCoupledElement_(domainI, eIdx))
+            return;
+
+        couplingContextBoundForElement_[domainI] = eIdx;
+
+        for (const auto& scvf : scvfs(fvGeometry))
+        {
+            if (isCoupled(domainI, scvf))
+            {
+                const auto otherElementIdx = couplingMapper_.outsideElementIndex(domainI, scvf);
+                constexpr auto domainJ = Dune::index_constant<1-domainI>();
+                const auto& otherGridGeometry = this->problem(domainJ).gridGeometry();
+                const auto& otherElement = otherGridGeometry.element(otherElementIdx);
+                auto otherFvGeometry = localView(otherGridGeometry).bindElement(otherElement);
+
+                // there is only one scv for TPFA
+                context.push_back({
+                    otherElement,
+                    volVars_(domainJ, otherElement, *std::begin(scvs(otherFvGeometry))),
+                    std::move(otherFvGeometry),
+                    otherElementIdx,
+                    scvf.index(),
+                    couplingMapper_.flipScvfIndex(domainI, scvf),
+                    VelocityVector{}
+                });
+            }
+        }
+    }
+
+    mutable std::tuple<std::vector<FreeFlowMassCouplingContext>, std::vector<PorousMediumCouplingContext>> couplingContext_;
+    mutable std::array<std::size_t, 2> couplingContextBoundForElement_;
+
+    CouplingMapper couplingMapper_;
+};
+
+} // end namespace Dumux
+
+#endif
-- 
GitLab