From 619f50b31315711e3487c649cabbb2f9ff7eeabd Mon Sep 17 00:00:00 2001 From: Kilian Weishaupt <kilian.weishaupt@iws.uni-stuttgart.de> Date: Thu, 9 Sep 2021 15:52:10 +0200 Subject: [PATCH] [md] Add meta-coupling manager combining multiple binary coupling managers --- .../multidomain/multibinarycouplingmanager.hh | 312 ++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 dumux/multidomain/multibinarycouplingmanager.hh diff --git a/dumux/multidomain/multibinarycouplingmanager.hh b/dumux/multidomain/multibinarycouplingmanager.hh new file mode 100644 index 0000000000..9979e8255e --- /dev/null +++ b/dumux/multidomain/multibinarycouplingmanager.hh @@ -0,0 +1,312 @@ +// -*- 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 3 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 MultiDomain + * \copydoc Dumux::MultiBinaryCouplingManager + */ + +#ifndef DUMUX_MULTIDOMAIN_MULTIBINARY_COUPLINGMANAGER_HH +#define DUMUX_MULTIDOMAIN_MULTIBINARY_COUPLINGMANAGER_HH + +#include <utility> +#include <memory> +#include <dune/common/hybridutilities.hh> +#include <dumux/common/properties.hh> +#include <dumux/multidomain/traits.hh> + +namespace Dumux { + +/*! + * \ingroup MultiDomain + * \brief Coupling manager that combines an arbitrary number of binary coupling manager (coupling two domains each) + */ +template<class MDTraits, class CouplingManagerMaps, class ...CouplingMgrs> +class MultiBinaryCouplingManager +{ + // the sub domain type tags + template<std::size_t id> + using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag; + + template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>; + template<std::size_t id> using GridView = typename GridGeometry<id>::GridView; + template<std::size_t id> using Element = typename GridView<id>::template Codim<0>::Entity; + template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView; + template<std::size_t id> using SubControlVolumeFace = typename FVElementGeometry<id>::SubControlVolumeFace; + template<std::size_t id> using SubControlVolume = typename FVElementGeometry<id>::SubControlVolume; + using CouplingStencil = std::vector<std::size_t>; + + template<std::size_t id> + using SubCouplingManagerT = typename std::tuple_element_t<id, std::tuple<CouplingMgrs...>>; + + using Indices = std::make_index_sequence<sizeof...(CouplingMgrs)>; + using CouplingManagers = typename Detail::MultiDomainTupleSharedPtr<SubCouplingManagerT, Indices>::type; + + template<std::size_t id> + using SolutionVectorT = std::decay_t<decltype(std::declval<typename MDTraits::SolutionVector>()[Dune::index_constant<id>()])>; + using SolutionVectors = typename Detail::MultiDomainTupleSharedPtr<SolutionVectorT, Indices>::type; + +public: + + static constexpr auto couplingManagerMap = CouplingManagerMaps::makeCouplingManagerMap(); + + template<std::size_t i, std::size_t j> + using SubCouplingManager = SubCouplingManagerT<couplingManagerMap[i][j]>; + + MultiBinaryCouplingManager() + { + using namespace Dune::Hybrid; + forEach(couplingManagers_, [&](auto&& couplingManager) + { + couplingManager = std::make_shared<typename std::decay_t<decltype(couplingManager)>::element_type>(); + }); + + forEach(solutionVectors_, [&](auto&& solutionVector) + { + solutionVector = std::make_shared<typename std::decay_t<decltype(solutionVector)>::element_type>(); + }); + } + + //! Returns the local domain indices used within the binary sub coupling managers. + template<std::size_t i, std::size_t j> + static constexpr auto globalToLocalDomainIndices(Dune::index_constant<i> domainI, Dune::index_constant<j> domainJ) + { + static_assert(i <= MDTraits::numSubDomains && j <= MDTraits::numSubDomains); + return CouplingManagerMaps::globalToLocalDomainIndices(domainI, domainJ); + } + + //! Returns the coupling manager index for a given domain combination + template<std::size_t i, std::size_t j> + static constexpr auto getCouplingManagerIndex(Dune::index_constant<i>, Dune::index_constant<j>) + { + static_assert(i != j); + return couplingManagerMap[i][j]; + } + + //! return the binary sub-coupling manager + template<std::size_t i, std::size_t j> + auto& subCouplingManager(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ) + { + constexpr auto idx = getCouplingManagerIndex(domainI, domainJ); + return *std::get<idx>(couplingManagers_); + } + + //! return the binary sub-coupling manager + template<std::size_t i, std::size_t j> + const auto& subCouplingManager(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ) const + { + constexpr auto idx = getCouplingManagerIndex(domainI, domainJ); + return *std::get<idx>(couplingManagers_); + } + + //! Update the solution vector before assembly + void updateSolution(const typename MDTraits::SolutionVector& curSol) + { + using namespace Dune::Hybrid; + forEach(integralRange(Dune::Hybrid::size(solutionVectors_)), [&](const auto id) + { + *std::get<id>(solutionVectors_) = curSol[id]; + }); + } + + /*! + * \brief extend the jacobian pattern of the diagonal block of domain i + * by those entries that are not already in the uncoupled pattern + * \note per default we do not add such additional dependencies + * \note Such additional dependencies can arise from the coupling, e.g. if a coupling source + * term depends on a non-local average of a quantity of the same domain + * \warning if you overload this also implement evalAdditionalDomainDerivatives + */ + template<std::size_t id, class JacobianPattern> + void extendJacobianPattern(Dune::index_constant<id> domainI, JacobianPattern& pattern) const + {} + + /*! + * \brief Return the coupling element stencil for a given bulk domain element + */ + template<std::size_t i, class Entity, std::size_t j> + const auto& couplingStencil(Dune::index_constant<i> domainI, + const Entity& entity, + Dune::index_constant<j> domainJ) const + { + return subCouplingManager(domainI, domainJ).couplingStencil(globalToLocalDomainIndices(domainI, domainJ).domainI, + entity, + globalToLocalDomainIndices(domainI, domainJ).domainJ); + } + + // ! evaluate coupling residual for the derivative low dim DOF with respect to bulk DOF + // ! we only need to evaluate the part of the residual that will be influence by the bulk DOF + template<std::size_t i, class LocalAssemblerI, std::size_t j> + decltype(auto) evalCouplingResidual(Dune::index_constant<i> domainI, + const SubControlVolumeFace<i>& scvfI, + const LocalAssemblerI& localAssemblerI, + Dune::index_constant<j> domainJ, + std::size_t dofIdxGlobalJ) const + { + return subCouplingManager(domainI, domainJ).evalCouplingResidual(globalToLocalDomainIndices(domainI, domainJ).domainI, + scvfI, localAssemblerI, + globalToLocalDomainIndices(domainI, domainJ).domainJ, + dofIdxGlobalJ); + } + + //! evaluate coupling residual for the derivative low dim DOF with respect to bulk DOF + //! we only need to evaluate the part of the residual that will be influence by the bulk DOF + template<std::size_t i, class LocalAssemblerI, std::size_t j> + decltype(auto) evalCouplingResidual(Dune::index_constant<i> domainI, + const LocalAssemblerI& localAssemblerI, + Dune::index_constant<j> domainJ, + std::size_t dofIdxGlobalJ) const + { + return subCouplingManager(domainI, domainJ).evalCouplingResidual(globalToLocalDomainIndices(domainI, domainJ).domainI, + localAssemblerI, + globalToLocalDomainIndices(domainI, domainJ).domainJ, + dofIdxGlobalJ); + } + + //! evaluate coupling residual for the derivative low dim DOF with respect to bulk DOF + //! we only need to evaluate the part of the residual that will be influence by the bulk DOF + template<std::size_t i, class LocalAssemblerI, std::size_t j> + decltype(auto) evalCouplingResidual(Dune::index_constant<i> domainI, + const LocalAssemblerI& localAssemblerI, + const SubControlVolume<i>& scvI, + Dune::index_constant<j> domainJ, + std::size_t dofIdxGlobalJ) const + { + return subCouplingManager(domainI, domainJ).evalCouplingResidual(globalToLocalDomainIndices(domainI, domainJ).domainI, + localAssemblerI, + scvI, + globalToLocalDomainIndices(domainI, domainJ).domainJ, + dofIdxGlobalJ); + } + + /*! + * \brief Update the coupling context for the bulk face residual w.r.t to the lowDim dofs + */ + template<std::size_t i, class LocalAssemblerI, std::size_t j, class PrimaryVariables> + void updateCouplingContext(Dune::index_constant<i> domainI, + const LocalAssemblerI& localAssemblerI, + Dune::index_constant<j> domainJ, + const std::size_t dofIdxGlobalJ, + const PrimaryVariables& priVars, + int pvIdxJ) + { + // only one other manager needs to take action if i != j + if constexpr (i != j) + { + subCouplingManager(domainI, domainJ).updateCouplingContext(globalToLocalDomainIndices(domainI, domainJ).domainI, + localAssemblerI, + globalToLocalDomainIndices(domainI, domainJ).domainJ, + dofIdxGlobalJ, + priVars, + pvIdxJ); + } + else + { + // for i == j, we need to call all relevant managers where domainI is involved and make sure that the + // context is updated with respect to its own domain + using namespace Dune::Hybrid; + forEach(CouplingManagerMaps::coupledDomains(domainI), [&](const auto domainJ) + { + static_assert(domainI != domainJ); + subCouplingManager(domainI, domainJ).updateCouplingContext(globalToLocalDomainIndices(domainI, domainJ).domainI, + localAssemblerI, + globalToLocalDomainIndices(domainI, domainJ).domainI, + dofIdxGlobalJ, + priVars, + pvIdxJ); + }); + } + } + + //! Bind the coupling context for a low dim element TODO remove Assembler + template<std::size_t i, class Assembler = int> + void bindCouplingContext(Dune::index_constant<i> domainI, const Element<i>& element, const Assembler& assembler = 0) + { + // for the coupling blocks + using namespace Dune::Hybrid; + forEach(integralRange(Dune::index_constant<MDTraits::numSubDomains>{}), [&](const auto domainJ) + { + if constexpr (domainI != domainJ) + this->subCouplingManager(domainI, domainJ).bindCouplingContext(globalToLocalDomainIndices(domainI, domainJ).domainI, + element, + assembler); + }); + } + + /*! + * \brief return the numeric epsilon used for deflecting primary variables of coupled domain i. + * \note specialization for free-flow schemes + */ + template<std::size_t i> + decltype(auto) numericEpsilon(Dune::index_constant<i> domainI, + const std::string& paramGroup) const + { + static constexpr auto someOtherDomain = std::get<0>(CouplingManagerMaps::coupledDomains(domainI)); + return subCouplingManager(domainI, someOtherDomain).numericEpsilon(globalToLocalDomainIndices(domainI, someOtherDomain).domainI, paramGroup); + } + + /*! + * \brief evaluate additional derivatives of the element residual of a domain with respect + * to dofs in the same domain that are not in the regular stencil (see CouplingManager::extendJacobianPattern) + * \note Such additional dependencies can arise from the coupling, e.g. if a coupling source + * term depends on a non-local average of a quantity of the same domain + */ + template<std::size_t i, class LocalAssemblerI, class JacobianMatrixDiagBlock, class GridVariables> + void evalAdditionalDomainDerivatives(Dune::index_constant<i> domainI, + const LocalAssemblerI& localAssemblerI, + const typename LocalAssemblerI::LocalResidual::ElementResidualVector& origResiduals, + JacobianMatrixDiagBlock& A, + GridVariables& gridVariables) + {} + + template<std::size_t i, class LocalAssemblerI, class UpdatableElementVolVars, class UpdatableFluxVarCache> + void updateCoupledVariables(Dune::index_constant<i> domainI, + const LocalAssemblerI& localAssemblerI, + UpdatableElementVolVars& elemVolVars, + UpdatableFluxVarCache& elemFluxVarsCache) + { + // for the coupling blocks + using namespace Dune::Hybrid; + forEach(integralRange(Dune::index_constant<MDTraits::numSubDomains>{}), [&](const auto domainJ) + { + if constexpr (domainI != domainJ) // TODO is this correct? + subCouplingManager(domainI, domainJ).updateCoupledVariables(globalToLocalDomainIndices(domainI, domainJ).domainI, + localAssemblerI, + elemVolVars, + elemFluxVarsCache); + }); + } + +protected: + SolutionVectors& curSol() + { return solutionVectors_; } + + const SolutionVectors& curSol() const + { return solutionVectors_; } + +private: + CouplingManagers couplingManagers_; + SolutionVectors solutionVectors_; +}; + +} // end namespace Dumux + +#endif -- GitLab