diff --git a/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions.hh b/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions.hh index c3b3278d1eb8ba25e566ad0bc1d1d24f2ee5d633..2b48bbb80e88fdcb21d90dfac0f6a0a0184b744c 100644 --- a/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions.hh +++ b/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions.hh @@ -7,448 +7,38 @@ /*! * \file * \ingroup FreeFlowPorousMediumCoupling - * \copydoc Dumux::FreeFlowPorousMediumCouplingConditions + * \brief Coupling conditions specialized for different discretization schemes */ #ifndef DUMUX_MD_FREEFLOW_POROUSMEDIUM_COUPLINGCONDITIONS_HH #define DUMUX_MD_FREEFLOW_POROUSMEDIUM_COUPLINGCONDITIONS_HH -#include <numeric> +#include <dumux/discretization/method.hh> -#include <dune/common/exceptions.hh> - -#include <dumux/common/properties.hh> -#include <dumux/common/math.hh> - -#include <dumux/discretization/cellcentered/tpfa/computetransmissibility.hh> - -#include <dumux/flux/darcyslaw_fwd.hh> -#include <dumux/flux/fickslaw_fwd.hh> -#include <dumux/flux/forchheimerslaw_fwd.hh> - -#include <dumux/multidomain/boundary/freeflowporousmedium/traits.hh> -#include <dumux/multidomain/boundary/freeflowporousmedium/indexhelper.hh> +#include "couplingconditions_staggered_cctpfa.hh" namespace Dumux { -/*! - * \ingroup FreeFlowPorousMediumCoupling - * \brief This structs holds a set of options which allow to modify the Stokes-Darcy - * coupling mechanism during runtime. - */ -struct FreeFlowPorousMediumCouplingOptions -{ - /*! - * \brief Defines which kind of averanging of diffusion coefficients - * (moleculat diffusion or thermal conductance) at the interface - * between free flow and porous medium shall be used. - */ - enum class DiffusionCoefficientAveragingType - { - harmonic, arithmetic, ffOnly, pmOnly - }; - - /*! - * \brief Convenience function to convert user input given as std::string to the corresponding enum class used for choosing the type - * of averaging of the diffusion/conduction parameter at the interface between the two domains. - */ - static DiffusionCoefficientAveragingType stringToEnum(DiffusionCoefficientAveragingType, const std::string& diffusionCoefficientAveragingType) - { - if (diffusionCoefficientAveragingType == "Harmonic") - return DiffusionCoefficientAveragingType::harmonic; - else if (diffusionCoefficientAveragingType == "Arithmetic") - return DiffusionCoefficientAveragingType::arithmetic; - else if (diffusionCoefficientAveragingType == "FreeFlowOnly") - return DiffusionCoefficientAveragingType::ffOnly; - else if (diffusionCoefficientAveragingType == "PorousMediumOnly") - return DiffusionCoefficientAveragingType::pmOnly; - else - DUNE_THROW(Dune::IOError, "Unknown DiffusionCoefficientAveragingType"); - } - -}; +#ifndef DOXYGEN +namespace FreeFlowPorousMediumDetail { -template<class MDTraits, class CouplingManager, bool enableEnergyBalance, bool isCompositional> -class FreeFlowPorousMediumCouplingConditionsImplementation; +// declaration (specialize for different discretization types) +template<class MDTraits, class CouplingManager, + class DiscFFMomentum = typename MDTraits::template SubDomain<CouplingManager::freeFlowMomentumIndex>::GridGeometry::DiscretizationMethod, + class DiscFFMass = typename MDTraits::template SubDomain<CouplingManager::freeFlowMassIndex>::GridGeometry::DiscretizationMethod, + class DiscPM = typename MDTraits::template SubDomain<CouplingManager::porousMediumIndex>::GridGeometry::DiscretizationMethod + > +struct FreeFlowPorousMediumCouplingConditionsSelector; -/*! -* \ingroup FreeFlowPorousMediumCoupling -* \brief Data for the coupling of a Darcy model (cell-centered finite volume) -* with a (Navier-)Stokes model (staggerd grid). -*/ template<class MDTraits, class CouplingManager> -using FreeFlowPorousMediumCouplingConditions - = FreeFlowPorousMediumCouplingConditionsImplementation< - MDTraits, CouplingManager, - GetPropType<typename MDTraits::template SubDomain<0>::TypeTag, Properties::ModelTraits>::enableEnergyBalance(), - (GetPropType<typename MDTraits::template SubDomain<0>::TypeTag, Properties::ModelTraits>::numFluidComponents() > 1) - >; - -/*! - * \ingroup FreeFlowPorousMediumCoupling - * \brief A base class which provides some common methods used for Stokes-Darcy coupling. - */ -template<class MDTraits, class CouplingManager> -class FreeFlowPorousMediumCouplingConditionsImplementationBase -{ - using Scalar = typename MDTraits::Scalar; - - 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 Element = typename GridGeometry<id>::GridView::template Codim<0>::Entity; - template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView; - template<std::size_t id> using SubControlVolumeFace = typename GridGeometry<id>::LocalView::SubControlVolumeFace; - template<std::size_t id> using SubControlVolume = typename GridGeometry<id>::LocalView::SubControlVolume; - template<std::size_t id> using Indices = typename GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>::Indices; - template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; - template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables; - template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>; - template<std::size_t id> using FluidSystem = GetPropType<SubDomainTypeTag<id>, Properties::FluidSystem>; - template<std::size_t id> using ModelTraits = GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>; - template<std::size_t id> using GlobalPosition = typename Element<id>::Geometry::GlobalCoordinate; - template<std::size_t id> using NumEqVector = typename Problem<id>::Traits::NumEqVector; - -public: - static constexpr auto freeFlowMomentumIndex = CouplingManager::freeFlowMomentumIndex; - static constexpr auto freeFlowMassIndex = CouplingManager::freeFlowMassIndex; - static constexpr auto porousMediumIndex = CouplingManager::porousMediumIndex; -private: - - using AdvectionType = GetPropType<SubDomainTypeTag<porousMediumIndex>, Properties::AdvectionType>; - using DarcysLaw = Dumux::DarcysLaw<SubDomainTypeTag<porousMediumIndex>>; - using ForchheimersLaw = Dumux::ForchheimersLaw<SubDomainTypeTag<porousMediumIndex>>; - - static constexpr bool adapterUsed = ModelTraits<porousMediumIndex>::numFluidPhases() > 1; - using IndexHelper = FreeFlowPorousMediumCoupling::IndexHelper<freeFlowMassIndex, porousMediumIndex, FluidSystem<freeFlowMassIndex>, adapterUsed>; - - static constexpr int enableEnergyBalance = GetPropType<SubDomainTypeTag<freeFlowMassIndex>, Properties::ModelTraits>::enableEnergyBalance(); - static_assert(GetPropType<SubDomainTypeTag<porousMediumIndex>, Properties::ModelTraits>::enableEnergyBalance() == enableEnergyBalance, - "All submodels must both be either isothermal or non-isothermal"); - - static_assert(FreeFlowPorousMediumCoupling::IsSameFluidSystem<FluidSystem<freeFlowMassIndex>, - FluidSystem<porousMediumIndex>>::value, - "All submodels must use the same fluid system"); - - using DiffusionCoefficientAveragingType = typename FreeFlowPorousMediumCouplingOptions::DiffusionCoefficientAveragingType; - -public: - /*! - * \brief Returns the corresponding phase index needed for coupling. - */ - template<std::size_t i> - static constexpr auto couplingPhaseIdx(Dune::index_constant<i> id, int coupledPhaseIdx = 0) - { return IndexHelper::couplingPhaseIdx(id, coupledPhaseIdx); } - - /*! - * \brief Returns the corresponding component index needed for coupling. - */ - template<std::size_t i> - static constexpr auto couplingCompIdx(Dune::index_constant<i> id, int coupledCompIdx) - { return IndexHelper::couplingCompIdx(id, coupledCompIdx); } - - /*! - * \brief Returns the intrinsic permeability of the coupled Darcy element. - */ - template<class Context> - static auto darcyPermeability(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, - const Context& context) - { return context.volVars.permeability(); } - - /*! - * \brief Returns the momentum flux across the coupling boundary. - * - * For the normal momentum coupling, the porous medium side of the coupling condition - * is evaluated, i.e. -[p n]^pm. - * - */ - template<class Context> - static NumEqVector<freeFlowMomentumIndex> momentumCouplingCondition(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, - const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars, - const Context& context) - { - static constexpr auto numPhasesDarcy = GetPropType<SubDomainTypeTag<porousMediumIndex>, Properties::ModelTraits>::numFluidPhases(); - NumEqVector<freeFlowMomentumIndex> momentumFlux(0.0); - - if (!scvf.isFrontal()) - return momentumFlux; - - const auto pmPhaseIdx = couplingPhaseIdx(porousMediumIndex); - - if(numPhasesDarcy > 1) - momentumFlux[scvf.normalAxis()] = context.volVars.pressure(pmPhaseIdx); - else // use pressure reconstruction for single phase models - momentumFlux[scvf.normalAxis()] = pressureAtInterface_(fvGeometry, scvf, elemVolVars, context); - - // TODO: generalize for permeability tensors - - // normalize pressure - momentumFlux[scvf.normalAxis()] -= elemVolVars.gridVolVars().problem().referencePressure(fvGeometry.element(), fvGeometry, scvf); - - momentumFlux[scvf.normalAxis()] *= scvf.directionSign(); - - return momentumFlux; - } - - /*! - * \brief Evaluate an advective flux across the interface and consider upwinding. - */ - static Scalar advectiveFlux(const Scalar insideQuantity, const Scalar outsideQuantity, const Scalar volumeFlow, bool insideIsUpstream) - { - const Scalar upwindWeight = 1.0; //TODO use Flux.UpwindWeight or something like Coupling.UpwindWeight? - - if(insideIsUpstream) - return (upwindWeight * insideQuantity + (1.0 - upwindWeight) * outsideQuantity) * volumeFlow; - else - return (upwindWeight * outsideQuantity + (1.0 - upwindWeight) * insideQuantity) * volumeFlow; - } +struct FreeFlowPorousMediumCouplingConditionsSelector<MDTraits, CouplingManager, DiscretizationMethods::FCStaggered, DiscretizationMethods::CCTpfa, DiscretizationMethods::CCTpfa> +{ using type = FFPMCouplingConditionsStaggeredCCTpfa<MDTraits, CouplingManager>; }; -protected: +} // end namespace FreeFlowPorousMediumDetail +#endif // DOXYGEN - /*! - * \brief Returns the transmissibility used for either molecular diffusion or thermal conductivity. - */ - template<std::size_t i, std::size_t j> - Scalar transmissibility_(Dune::index_constant<i> domainI, - Dune::index_constant<j> domainJ, - const Scalar insideDistance, - const Scalar outsideDistance, - const Scalar avgQuantityI, - const Scalar avgQuantityJ, - const DiffusionCoefficientAveragingType diffCoeffAvgType) const - { - const Scalar totalDistance = insideDistance + outsideDistance; - if(diffCoeffAvgType == DiffusionCoefficientAveragingType::harmonic) - { - return harmonicMean(avgQuantityI, avgQuantityJ, insideDistance, outsideDistance) - / totalDistance; - } - else if(diffCoeffAvgType == DiffusionCoefficientAveragingType::arithmetic) - { - return arithmeticMean(avgQuantityI, avgQuantityJ, insideDistance, outsideDistance) - / totalDistance; - } - else if(diffCoeffAvgType == DiffusionCoefficientAveragingType::ffOnly) - return domainI == freeFlowMassIndex - ? avgQuantityI / totalDistance - : avgQuantityJ / totalDistance; - - else // diffCoeffAvgType == DiffusionCoefficientAveragingType::pmOnly) - return domainI == porousMediumIndex - ? avgQuantityI / totalDistance - : avgQuantityJ / totalDistance; - } - - /*! - * \brief Returns the distance between an scvf and the corresponding scv center. - */ - template<class Scv, class Scvf> - Scalar getDistance_(const Scv& scv, const Scvf& scvf) const - { - return (scv.dofPosition() - scvf.ipGlobal()).two_norm(); - } - - /*! - * \brief Returns the conductive energy flux across the interface. - */ - template<std::size_t i, std::size_t j, bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> - Scalar conductiveEnergyFlux_(Dune::index_constant<i> domainI, - Dune::index_constant<j> domainJ, - const FVElementGeometry<i>& fvGeometryI, - const FVElementGeometry<j>& fvGeometryJ, - const SubControlVolumeFace<i>& scvfI, - const SubControlVolume<i>& scvI, - const SubControlVolume<j>& scvJ, - const VolumeVariables<i>& volVarsI, - const VolumeVariables<j>& volVarsJ, - const DiffusionCoefficientAveragingType diffCoeffAvgType) const - { - const Scalar insideDistance = getDistance_(scvI, scvfI); - const Scalar outsideDistance = getDistance_(scvJ, scvfI); - - const Scalar deltaT = volVarsJ.temperature() - volVarsI.temperature(); - const Scalar tij = transmissibility_( - domainI, domainJ, - insideDistance, outsideDistance, - volVarsI.effectiveThermalConductivity(), volVarsJ.effectiveThermalConductivity(), - diffCoeffAvgType - ); - - return -tij * deltaT; - } - - /*! - * \brief Returns the pressure at the interface - */ - template<class CouplingContext> - static Scalar pressureAtInterface_(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, - const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars, - const CouplingContext& context) - { - GlobalPosition<freeFlowMomentumIndex> velocity(0.0); - const auto& scv = fvGeometry.scv(scvf.insideScvIdx()); - velocity[scv.dofAxis()] = elemVolVars[scv].velocity(); - const auto& pmScvf = context.fvGeometry.scvf(context.porousMediumScvfIdx); - return computeCouplingPhasePressureAtInterface_(context.fvGeometry, pmScvf, context.volVars, context.gravity, velocity, AdvectionType()); - } - - /*! - * \brief Returns the pressure at the interface using Forchheimers's law for reconstruction - */ - static Scalar computeCouplingPhasePressureAtInterface_(const FVElementGeometry<porousMediumIndex>& fvGeometry, - const SubControlVolumeFace<porousMediumIndex>& scvf, - const VolumeVariables<porousMediumIndex>& volVars, - const typename Element<freeFlowMomentumIndex>::Geometry::GlobalCoordinate& couplingPhaseVelocity, - ForchheimersLaw) - { - DUNE_THROW(Dune::NotImplemented, "Forchheimer's law pressure reconstruction"); - } - - /*! - * \brief Returns the pressure at the interface using Darcy's law for reconstruction - */ - static Scalar computeCouplingPhasePressureAtInterface_(const FVElementGeometry<porousMediumIndex>& fvGeometry, - const SubControlVolumeFace<porousMediumIndex>& scvf, - const VolumeVariables<porousMediumIndex>& volVars, - const typename Element<freeFlowMomentumIndex>::Geometry::GlobalCoordinate& gravity, - const typename Element<freeFlowMomentumIndex>::Geometry::GlobalCoordinate& couplingPhaseVelocity, - DarcysLaw) - { - const auto pmPhaseIdx = couplingPhaseIdx(porousMediumIndex); - const Scalar couplingPhaseCellCenterPressure = volVars.pressure(pmPhaseIdx); - const Scalar couplingPhaseMobility = volVars.mobility(pmPhaseIdx); - const Scalar couplingPhaseDensity = volVars.density(pmPhaseIdx); - const auto K = volVars.permeability(); - - // A tpfa approximation yields (works if mobility != 0) - // v*n = -kr/mu*K * (gradP - rho*g)*n = mobility*(ti*(p_center - p_interface) + rho*n^TKg) - // -> p_interface = (1/mobility * (-v*n) + rho*n^TKg)/ti + p_center - // where v is the free-flow velocity (couplingPhaseVelocity) - const auto alpha = vtmv(scvf.unitOuterNormal(), K, gravity); - - const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); - const auto ti = computeTpfaTransmissibility(fvGeometry, scvf, insideScv, K, 1.0); - - return (-1/couplingPhaseMobility * (scvf.unitOuterNormal() * couplingPhaseVelocity) + couplingPhaseDensity * alpha)/ti - + couplingPhaseCellCenterPressure; - } -}; - -/*! - * \ingroup FreeFlowPorousMediumCoupling - * \brief Coupling data specialization for non-compositional models. - */ -template<class MDTraits, class CouplingManager, bool enableEnergyBalance> -class FreeFlowPorousMediumCouplingConditionsImplementation<MDTraits, CouplingManager, enableEnergyBalance, false> -: public FreeFlowPorousMediumCouplingConditionsImplementationBase<MDTraits, CouplingManager> -{ - using ParentType = FreeFlowPorousMediumCouplingConditionsImplementationBase<MDTraits, CouplingManager>; - using Scalar = typename MDTraits::Scalar; - - // 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 Element = typename GridGeometry<id>::GridView::template Codim<0>::Entity; - template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView; - template<std::size_t id> using SubControlVolumeFace = typename GridGeometry<id>::LocalView::SubControlVolumeFace; - template<std::size_t id> using SubControlVolume = typename GridGeometry<id>::LocalView::SubControlVolume; - template<std::size_t id> using Indices = typename GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>::Indices; - template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; - template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables; - - static_assert(GetPropType<SubDomainTypeTag<ParentType::porousMediumIndex>, Properties::ModelTraits>::numFluidComponents() == GetPropType<SubDomainTypeTag<ParentType::porousMediumIndex>, Properties::ModelTraits>::numFluidPhases(), - "Darcy Model must not be compositional"); - - using DiffusionCoefficientAveragingType = typename FreeFlowPorousMediumCouplingOptions::DiffusionCoefficientAveragingType; - -public: - using ParentType::ParentType; - using ParentType::couplingPhaseIdx; - - /*! - * \brief Returns the mass flux across the coupling boundary as seen from the Darcy domain. - */ - template<std::size_t i, std::size_t j, class CouplingContext> - static Scalar massCouplingCondition(Dune::index_constant<i> domainI, - Dune::index_constant<j> domainJ, - const FVElementGeometry<i>& fvGeometry, - const SubControlVolumeFace<i>& scvf, - const ElementVolumeVariables<i>& insideVolVars, - const CouplingContext& context) - { - const Scalar normalVelocity = context.velocity * scvf.unitOuterNormal(); - const Scalar darcyDensity = insideVolVars[scvf.insideScvIdx()].density(couplingPhaseIdx(ParentType::porousMediumIndex)); - const Scalar stokesDensity = context.volVars.density(); - const bool insideIsUpstream = normalVelocity > 0.0; - return ParentType::advectiveFlux(darcyDensity, stokesDensity, normalVelocity, insideIsUpstream); - } - - /*! - * \brief Returns the energy flux across the coupling boundary as seen from the Darcy domain. - */ - template<bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> - Scalar energyCouplingCondition(const Element<ParentType::porousMediumIndex>& element, - const FVElementGeometry<ParentType::porousMediumIndex>& fvGeometry, - const ElementVolumeVariables<ParentType::porousMediumIndex>& darcyElemVolVars, - const SubControlVolumeFace<ParentType::porousMediumIndex>& scvf, - const DiffusionCoefficientAveragingType diffCoeffAvgType = DiffusionCoefficientAveragingType::ffOnly) const - { - DUNE_THROW(Dune::NotImplemented, "Energy coupling condition"); - } - - /*! - * \brief Returns the energy flux across the coupling boundary as seen from the free-flow domain. - */ - template<bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> - Scalar energyCouplingCondition(const Element<ParentType::freeFlowMassIndex>& element, - const FVElementGeometry<ParentType::freeFlowMassIndex>& fvGeometry, - const ElementVolumeVariables<ParentType::freeFlowMassIndex>& stokesElemVolVars, - const SubControlVolumeFace<ParentType::freeFlowMassIndex>& scvf, - const DiffusionCoefficientAveragingType diffCoeffAvgType = DiffusionCoefficientAveragingType::ffOnly) const - { - DUNE_THROW(Dune::NotImplemented, "Energy coupling condition"); - } - -private: - - - - /*! - * \brief Evaluate the energy flux across the interface. - */ - template<std::size_t i, std::size_t j, bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> - Scalar energyFlux_(Dune::index_constant<i> domainI, - Dune::index_constant<j> domainJ, - const FVElementGeometry<i>& insideFvGeometry, - const FVElementGeometry<j>& outsideFvGeometry, - const SubControlVolumeFace<i>& scvf, - const VolumeVariables<i>& insideVolVars, - const VolumeVariables<j>& outsideVolVars, - const Scalar velocity, - const bool insideIsUpstream, - const DiffusionCoefficientAveragingType diffCoeffAvgType) const - { - Scalar flux(0.0); - - const auto& insideScv = (*scvs(insideFvGeometry).begin()); - const auto& outsideScv = (*scvs(outsideFvGeometry).begin()); - - // convective fluxes - const Scalar insideTerm = insideVolVars.density(couplingPhaseIdx(domainI)) * insideVolVars.enthalpy(couplingPhaseIdx(domainI)); - const Scalar outsideTerm = outsideVolVars.density(couplingPhaseIdx(domainJ)) * outsideVolVars.enthalpy(couplingPhaseIdx(domainJ)); - - flux += this->advectiveFlux(insideTerm, outsideTerm, velocity, insideIsUpstream); - - flux += this->conductiveEnergyFlux_(domainI, domainJ, insideFvGeometry, outsideFvGeometry, scvf, insideScv, outsideScv, insideVolVars, outsideVolVars, diffCoeffAvgType); - - return flux; - } - -}; +template<class MDTraits, class CouplingManager> +using FreeFlowPorousMediumCouplingConditions = typename FreeFlowPorousMediumDetail::FreeFlowPorousMediumCouplingConditionsSelector<MDTraits,CouplingManager>::type; } // end namespace Dumux diff --git a/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions_staggered_cctpfa.hh b/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions_staggered_cctpfa.hh new file mode 100644 index 0000000000000000000000000000000000000000..91627504b6cbe3ab4489269ae3ea8c8c3af539fa --- /dev/null +++ b/dumux/multidomain/boundary/freeflowporousmedium/couplingconditions_staggered_cctpfa.hh @@ -0,0 +1,455 @@ +// -*- 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::FFPMCouplingConditionsStaggeredCCTpfa + */ + +#ifndef DUMUX_MD_FREEFLOW_POROUSMEDIUM_COUPLINGCONDITIONS_STAGGERED_TPFA_HH +#define DUMUX_MD_FREEFLOW_POROUSMEDIUM_COUPLINGCONDITIONS_STAGGERED_TPFA_HH + +#include <numeric> + +#include <dune/common/exceptions.hh> + +#include <dumux/common/properties.hh> +#include <dumux/common/math.hh> + +#include <dumux/discretization/cellcentered/tpfa/computetransmissibility.hh> + +#include <dumux/flux/darcyslaw_fwd.hh> +#include <dumux/flux/fickslaw_fwd.hh> +#include <dumux/flux/forchheimerslaw_fwd.hh> + +#include <dumux/multidomain/boundary/freeflowporousmedium/traits.hh> +#include <dumux/multidomain/boundary/freeflowporousmedium/indexhelper.hh> + +namespace Dumux { + +/*! + * \ingroup FreeFlowPorousMediumCoupling + * \brief This structs holds a set of options which allow to modify the Stokes-Darcy + * coupling mechanism during runtime. + */ +struct FreeFlowPorousMediumCouplingOptions +{ + /*! + * \brief Defines which kind of averanging of diffusion coefficients + * (moleculat diffusion or thermal conductance) at the interface + * between free flow and porous medium shall be used. + */ + enum class DiffusionCoefficientAveragingType + { + harmonic, arithmetic, ffOnly, pmOnly + }; + + /*! + * \brief Convenience function to convert user input given as std::string to the corresponding enum class used for choosing the type + * of averaging of the diffusion/conduction parameter at the interface between the two domains. + */ + static DiffusionCoefficientAveragingType stringToEnum(DiffusionCoefficientAveragingType, const std::string& diffusionCoefficientAveragingType) + { + if (diffusionCoefficientAveragingType == "Harmonic") + return DiffusionCoefficientAveragingType::harmonic; + else if (diffusionCoefficientAveragingType == "Arithmetic") + return DiffusionCoefficientAveragingType::arithmetic; + else if (diffusionCoefficientAveragingType == "FreeFlowOnly") + return DiffusionCoefficientAveragingType::ffOnly; + else if (diffusionCoefficientAveragingType == "PorousMediumOnly") + return DiffusionCoefficientAveragingType::pmOnly; + else + DUNE_THROW(Dune::IOError, "Unknown DiffusionCoefficientAveragingType"); + } + +}; + +template<class MDTraits, class CouplingManager, bool enableEnergyBalance, bool isCompositional> +class FFPMCouplingConditionsStaggeredCCTpfaImpl; + +/*! +* \ingroup FreeFlowPorousMediumCoupling +* \brief Data for the coupling of a Darcy model (cell-centered finite volume) +* with a (Navier-)Stokes model (staggerd grid). +*/ +template<class MDTraits, class CouplingManager> +using FFPMCouplingConditionsStaggeredCCTpfa + = FFPMCouplingConditionsStaggeredCCTpfaImpl< + MDTraits, CouplingManager, + GetPropType<typename MDTraits::template SubDomain<0>::TypeTag, Properties::ModelTraits>::enableEnergyBalance(), + (GetPropType<typename MDTraits::template SubDomain<0>::TypeTag, Properties::ModelTraits>::numFluidComponents() > 1) + >; + +/*! + * \ingroup FreeFlowPorousMediumCoupling + * \brief A base class which provides some common methods used for Stokes-Darcy coupling. + */ +template<class MDTraits, class CouplingManager> +class FFPMCouplingConditionsStaggeredCCTpfaImplBase +{ + using Scalar = typename MDTraits::Scalar; + + 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 Element = typename GridGeometry<id>::GridView::template Codim<0>::Entity; + template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView; + template<std::size_t id> using SubControlVolumeFace = typename GridGeometry<id>::LocalView::SubControlVolumeFace; + template<std::size_t id> using SubControlVolume = typename GridGeometry<id>::LocalView::SubControlVolume; + template<std::size_t id> using Indices = typename GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>::Indices; + template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; + template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables; + template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>; + template<std::size_t id> using FluidSystem = GetPropType<SubDomainTypeTag<id>, Properties::FluidSystem>; + template<std::size_t id> using ModelTraits = GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>; + template<std::size_t id> using GlobalPosition = typename Element<id>::Geometry::GlobalCoordinate; + template<std::size_t id> using NumEqVector = typename Problem<id>::Traits::NumEqVector; + +public: + static constexpr auto freeFlowMomentumIndex = CouplingManager::freeFlowMomentumIndex; + static constexpr auto freeFlowMassIndex = CouplingManager::freeFlowMassIndex; + static constexpr auto porousMediumIndex = CouplingManager::porousMediumIndex; +private: + + using AdvectionType = GetPropType<SubDomainTypeTag<porousMediumIndex>, Properties::AdvectionType>; + using DarcysLaw = Dumux::DarcysLaw<SubDomainTypeTag<porousMediumIndex>>; + using ForchheimersLaw = Dumux::ForchheimersLaw<SubDomainTypeTag<porousMediumIndex>>; + + static constexpr bool adapterUsed = ModelTraits<porousMediumIndex>::numFluidPhases() > 1; + using IndexHelper = FreeFlowPorousMediumCoupling::IndexHelper<freeFlowMassIndex, porousMediumIndex, FluidSystem<freeFlowMassIndex>, adapterUsed>; + + static constexpr int enableEnergyBalance = GetPropType<SubDomainTypeTag<freeFlowMassIndex>, Properties::ModelTraits>::enableEnergyBalance(); + static_assert(GetPropType<SubDomainTypeTag<porousMediumIndex>, Properties::ModelTraits>::enableEnergyBalance() == enableEnergyBalance, + "All submodels must both be either isothermal or non-isothermal"); + + static_assert(FreeFlowPorousMediumCoupling::IsSameFluidSystem<FluidSystem<freeFlowMassIndex>, + FluidSystem<porousMediumIndex>>::value, + "All submodels must use the same fluid system"); + + using DiffusionCoefficientAveragingType = typename FreeFlowPorousMediumCouplingOptions::DiffusionCoefficientAveragingType; + +public: + /*! + * \brief Returns the corresponding phase index needed for coupling. + */ + template<std::size_t i> + static constexpr auto couplingPhaseIdx(Dune::index_constant<i> id, int coupledPhaseIdx = 0) + { return IndexHelper::couplingPhaseIdx(id, coupledPhaseIdx); } + + /*! + * \brief Returns the corresponding component index needed for coupling. + */ + template<std::size_t i> + static constexpr auto couplingCompIdx(Dune::index_constant<i> id, int coupledCompIdx) + { return IndexHelper::couplingCompIdx(id, coupledCompIdx); } + + /*! + * \brief Returns the intrinsic permeability of the coupled Darcy element. + */ + template<class Context> + static auto darcyPermeability(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, + const Context& context) + { return context.volVars.permeability(); } + + /*! + * \brief Returns the momentum flux across the coupling boundary. + * + * For the normal momentum coupling, the porous medium side of the coupling condition + * is evaluated, i.e. -[p n]^pm. + * + */ + template<class Context> + static NumEqVector<freeFlowMomentumIndex> momentumCouplingCondition(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, + const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars, + const Context& context) + { + static constexpr auto numPhasesDarcy = GetPropType<SubDomainTypeTag<porousMediumIndex>, Properties::ModelTraits>::numFluidPhases(); + NumEqVector<freeFlowMomentumIndex> momentumFlux(0.0); + + if (!scvf.isFrontal()) + return momentumFlux; + + const auto pmPhaseIdx = couplingPhaseIdx(porousMediumIndex); + + if(numPhasesDarcy > 1) + momentumFlux[scvf.normalAxis()] = context.volVars.pressure(pmPhaseIdx); + else // use pressure reconstruction for single phase models + momentumFlux[scvf.normalAxis()] = pressureAtInterface_(fvGeometry, scvf, elemVolVars, context); + + // TODO: generalize for permeability tensors + + // normalize pressure + momentumFlux[scvf.normalAxis()] -= elemVolVars.gridVolVars().problem().referencePressure(fvGeometry.element(), fvGeometry, scvf); + + momentumFlux[scvf.normalAxis()] *= scvf.directionSign(); + + return momentumFlux; + } + + /*! + * \brief Evaluate an advective flux across the interface and consider upwinding. + */ + static Scalar advectiveFlux(const Scalar insideQuantity, const Scalar outsideQuantity, const Scalar volumeFlow, bool insideIsUpstream) + { + const Scalar upwindWeight = 1.0; //TODO use Flux.UpwindWeight or something like Coupling.UpwindWeight? + + if(insideIsUpstream) + return (upwindWeight * insideQuantity + (1.0 - upwindWeight) * outsideQuantity) * volumeFlow; + else + return (upwindWeight * outsideQuantity + (1.0 - upwindWeight) * insideQuantity) * volumeFlow; + } + +protected: + + /*! + * \brief Returns the transmissibility used for either molecular diffusion or thermal conductivity. + */ + template<std::size_t i, std::size_t j> + Scalar transmissibility_(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ, + const Scalar insideDistance, + const Scalar outsideDistance, + const Scalar avgQuantityI, + const Scalar avgQuantityJ, + const DiffusionCoefficientAveragingType diffCoeffAvgType) const + { + const Scalar totalDistance = insideDistance + outsideDistance; + if(diffCoeffAvgType == DiffusionCoefficientAveragingType::harmonic) + { + return harmonicMean(avgQuantityI, avgQuantityJ, insideDistance, outsideDistance) + / totalDistance; + } + else if(diffCoeffAvgType == DiffusionCoefficientAveragingType::arithmetic) + { + return arithmeticMean(avgQuantityI, avgQuantityJ, insideDistance, outsideDistance) + / totalDistance; + } + else if(diffCoeffAvgType == DiffusionCoefficientAveragingType::ffOnly) + return domainI == freeFlowMassIndex + ? avgQuantityI / totalDistance + : avgQuantityJ / totalDistance; + + else // diffCoeffAvgType == DiffusionCoefficientAveragingType::pmOnly) + return domainI == porousMediumIndex + ? avgQuantityI / totalDistance + : avgQuantityJ / totalDistance; + } + + /*! + * \brief Returns the distance between an scvf and the corresponding scv center. + */ + template<class Scv, class Scvf> + Scalar getDistance_(const Scv& scv, const Scvf& scvf) const + { + return (scv.dofPosition() - scvf.ipGlobal()).two_norm(); + } + + /*! + * \brief Returns the conductive energy flux across the interface. + */ + template<std::size_t i, std::size_t j, bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> + Scalar conductiveEnergyFlux_(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ, + const FVElementGeometry<i>& fvGeometryI, + const FVElementGeometry<j>& fvGeometryJ, + const SubControlVolumeFace<i>& scvfI, + const SubControlVolume<i>& scvI, + const SubControlVolume<j>& scvJ, + const VolumeVariables<i>& volVarsI, + const VolumeVariables<j>& volVarsJ, + const DiffusionCoefficientAveragingType diffCoeffAvgType) const + { + const Scalar insideDistance = getDistance_(scvI, scvfI); + const Scalar outsideDistance = getDistance_(scvJ, scvfI); + + const Scalar deltaT = volVarsJ.temperature() - volVarsI.temperature(); + const Scalar tij = transmissibility_( + domainI, domainJ, + insideDistance, outsideDistance, + volVarsI.effectiveThermalConductivity(), volVarsJ.effectiveThermalConductivity(), + diffCoeffAvgType + ); + + return -tij * deltaT; + } + + /*! + * \brief Returns the pressure at the interface + */ + template<class CouplingContext> + static Scalar pressureAtInterface_(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, + const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars, + const CouplingContext& context) + { + GlobalPosition<freeFlowMomentumIndex> velocity(0.0); + const auto& scv = fvGeometry.scv(scvf.insideScvIdx()); + velocity[scv.dofAxis()] = elemVolVars[scv].velocity(); + const auto& pmScvf = context.fvGeometry.scvf(context.porousMediumScvfIdx); + return computeCouplingPhasePressureAtInterface_(context.fvGeometry, pmScvf, context.volVars, context.gravity, velocity, AdvectionType()); + } + + /*! + * \brief Returns the pressure at the interface using Forchheimers's law for reconstruction + */ + static Scalar computeCouplingPhasePressureAtInterface_(const FVElementGeometry<porousMediumIndex>& fvGeometry, + const SubControlVolumeFace<porousMediumIndex>& scvf, + const VolumeVariables<porousMediumIndex>& volVars, + const typename Element<freeFlowMomentumIndex>::Geometry::GlobalCoordinate& couplingPhaseVelocity, + ForchheimersLaw) + { + DUNE_THROW(Dune::NotImplemented, "Forchheimer's law pressure reconstruction"); + } + + /*! + * \brief Returns the pressure at the interface using Darcy's law for reconstruction + */ + static Scalar computeCouplingPhasePressureAtInterface_(const FVElementGeometry<porousMediumIndex>& fvGeometry, + const SubControlVolumeFace<porousMediumIndex>& scvf, + const VolumeVariables<porousMediumIndex>& volVars, + const typename Element<freeFlowMomentumIndex>::Geometry::GlobalCoordinate& gravity, + const typename Element<freeFlowMomentumIndex>::Geometry::GlobalCoordinate& couplingPhaseVelocity, + DarcysLaw) + { + const auto pmPhaseIdx = couplingPhaseIdx(porousMediumIndex); + const Scalar couplingPhaseCellCenterPressure = volVars.pressure(pmPhaseIdx); + const Scalar couplingPhaseMobility = volVars.mobility(pmPhaseIdx); + const Scalar couplingPhaseDensity = volVars.density(pmPhaseIdx); + const auto K = volVars.permeability(); + + // A tpfa approximation yields (works if mobility != 0) + // v*n = -kr/mu*K * (gradP - rho*g)*n = mobility*(ti*(p_center - p_interface) + rho*n^TKg) + // -> p_interface = (1/mobility * (-v*n) + rho*n^TKg)/ti + p_center + // where v is the free-flow velocity (couplingPhaseVelocity) + const auto alpha = vtmv(scvf.unitOuterNormal(), K, gravity); + + const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); + const auto ti = computeTpfaTransmissibility(fvGeometry, scvf, insideScv, K, 1.0); + + return (-1/couplingPhaseMobility * (scvf.unitOuterNormal() * couplingPhaseVelocity) + couplingPhaseDensity * alpha)/ti + + couplingPhaseCellCenterPressure; + } +}; + +/*! + * \ingroup FreeFlowPorousMediumCoupling + * \brief Coupling data specialization for non-compositional models. + */ +template<class MDTraits, class CouplingManager, bool enableEnergyBalance> +class FFPMCouplingConditionsStaggeredCCTpfaImpl<MDTraits, CouplingManager, enableEnergyBalance, false> +: public FFPMCouplingConditionsStaggeredCCTpfaImplBase<MDTraits, CouplingManager> +{ + using ParentType = FFPMCouplingConditionsStaggeredCCTpfaImplBase<MDTraits, CouplingManager>; + using Scalar = typename MDTraits::Scalar; + + // 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 Element = typename GridGeometry<id>::GridView::template Codim<0>::Entity; + template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView; + template<std::size_t id> using SubControlVolumeFace = typename GridGeometry<id>::LocalView::SubControlVolumeFace; + template<std::size_t id> using SubControlVolume = typename GridGeometry<id>::LocalView::SubControlVolume; + template<std::size_t id> using Indices = typename GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>::Indices; + template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; + template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables; + + static_assert(GetPropType<SubDomainTypeTag<ParentType::porousMediumIndex>, Properties::ModelTraits>::numFluidComponents() == GetPropType<SubDomainTypeTag<ParentType::porousMediumIndex>, Properties::ModelTraits>::numFluidPhases(), + "Darcy Model must not be compositional"); + + using DiffusionCoefficientAveragingType = typename FreeFlowPorousMediumCouplingOptions::DiffusionCoefficientAveragingType; + +public: + using ParentType::ParentType; + using ParentType::couplingPhaseIdx; + + /*! + * \brief Returns the mass flux across the coupling boundary as seen from the Darcy domain. + */ + template<std::size_t i, std::size_t j, class CouplingContext> + static Scalar massCouplingCondition(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ, + const FVElementGeometry<i>& fvGeometry, + const SubControlVolumeFace<i>& scvf, + const ElementVolumeVariables<i>& insideVolVars, + const CouplingContext& context) + { + const Scalar normalVelocity = context.velocity * scvf.unitOuterNormal(); + const Scalar darcyDensity = insideVolVars[scvf.insideScvIdx()].density(couplingPhaseIdx(ParentType::porousMediumIndex)); + const Scalar stokesDensity = context.volVars.density(); + const bool insideIsUpstream = normalVelocity > 0.0; + return ParentType::advectiveFlux(darcyDensity, stokesDensity, normalVelocity, insideIsUpstream); + } + + /*! + * \brief Returns the energy flux across the coupling boundary as seen from the Darcy domain. + */ + template<bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> + Scalar energyCouplingCondition(const Element<ParentType::porousMediumIndex>& element, + const FVElementGeometry<ParentType::porousMediumIndex>& fvGeometry, + const ElementVolumeVariables<ParentType::porousMediumIndex>& darcyElemVolVars, + const SubControlVolumeFace<ParentType::porousMediumIndex>& scvf, + const DiffusionCoefficientAveragingType diffCoeffAvgType = DiffusionCoefficientAveragingType::ffOnly) const + { + DUNE_THROW(Dune::NotImplemented, "Energy coupling condition"); + } + + /*! + * \brief Returns the energy flux across the coupling boundary as seen from the free-flow domain. + */ + template<bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> + Scalar energyCouplingCondition(const Element<ParentType::freeFlowMassIndex>& element, + const FVElementGeometry<ParentType::freeFlowMassIndex>& fvGeometry, + const ElementVolumeVariables<ParentType::freeFlowMassIndex>& stokesElemVolVars, + const SubControlVolumeFace<ParentType::freeFlowMassIndex>& scvf, + const DiffusionCoefficientAveragingType diffCoeffAvgType = DiffusionCoefficientAveragingType::ffOnly) const + { + DUNE_THROW(Dune::NotImplemented, "Energy coupling condition"); + } + +private: + + + + /*! + * \brief Evaluate the energy flux across the interface. + */ + template<std::size_t i, std::size_t j, bool isNI = enableEnergyBalance, typename std::enable_if_t<isNI, int> = 0> + Scalar energyFlux_(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ, + const FVElementGeometry<i>& insideFvGeometry, + const FVElementGeometry<j>& outsideFvGeometry, + const SubControlVolumeFace<i>& scvf, + const VolumeVariables<i>& insideVolVars, + const VolumeVariables<j>& outsideVolVars, + const Scalar velocity, + const bool insideIsUpstream, + const DiffusionCoefficientAveragingType diffCoeffAvgType) const + { + Scalar flux(0.0); + + const auto& insideScv = (*scvs(insideFvGeometry).begin()); + const auto& outsideScv = (*scvs(outsideFvGeometry).begin()); + + // convective fluxes + const Scalar insideTerm = insideVolVars.density(couplingPhaseIdx(domainI)) * insideVolVars.enthalpy(couplingPhaseIdx(domainI)); + const Scalar outsideTerm = outsideVolVars.density(couplingPhaseIdx(domainJ)) * outsideVolVars.enthalpy(couplingPhaseIdx(domainJ)); + + flux += this->advectiveFlux(insideTerm, outsideTerm, velocity, insideIsUpstream); + + flux += this->conductiveEnergyFlux_(domainI, domainJ, insideFvGeometry, outsideFvGeometry, scvf, insideScv, outsideScv, insideVolVars, outsideVolVars, diffCoeffAvgType); + + return flux; + } + +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager.hh b/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager.hh index 0ad552da909e7d4d3722e3f90e7899bee9625915..b4d6b62d68bd1dff2c41cd95b80329c54ed28658 100644 --- a/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager.hh +++ b/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager.hh @@ -7,459 +7,39 @@ /*! * \file * \ingroup FreeFlowPorousMediumCoupling - * \copydoc Dumux::FreeFlowPorousMediumCouplingManager + * \brief Coupling managers for free and porous medium flow, specialized for different discretization schemes. */ #ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FREEFLOW_POROUSMEDIUM_COUPLINGMANAGER_HH #define DUMUX_MULTIDOMAIN_BOUNDARY_FREEFLOW_POROUSMEDIUM_COUPLINGMANAGER_HH -#include <utility> -#include <memory> +#include <dumux/discretization/method.hh> -#include <dune/common/indices.hh> -#include <dumux/common/properties.hh> -#include <dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh> -#include <dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager.hh> -#include <dumux/multidomain/freeflow/couplingmanager.hh> -#include <dumux/multidomain/multibinarycouplingmanager.hh> - -#include "couplingconditions.hh" +#include "couplingmanager_base.hh" +#include "couplingmanager_staggered_cctpfa.hh" namespace Dumux { +#ifndef DOXYGEN namespace FreeFlowPorousMediumDetail { -// global subdomain indices -static constexpr auto freeFlowMomentumIndex = Dune::index_constant<0>(); -static constexpr auto freeFlowMassIndex = Dune::index_constant<1>(); -static constexpr auto porousMediumIndex = Dune::index_constant<2>(); - -// coupling indices -static constexpr auto freeFlowMassToFreeFlowMomentumIndex = Dune::index_constant<0>(); -static constexpr auto freeFlowMomentumToPorousMediumIndex = Dune::index_constant<1>(); -static constexpr auto freeFlowMassToPorousMediumIndex = Dune::index_constant<2>(); -static constexpr auto noCouplingIdx = Dune::index_constant<99>(); - -constexpr auto makeCouplingManagerMap() -{ - auto map = std::array<std::array<std::size_t, 3>, 3>{}; - - // free flow (momentum-mass) - map[freeFlowMomentumIndex][freeFlowMassIndex] = freeFlowMassToFreeFlowMomentumIndex; - map[freeFlowMassIndex][freeFlowMomentumIndex] = freeFlowMassToFreeFlowMomentumIndex; - - // free flow momentum - porous medium - map[freeFlowMomentumIndex][porousMediumIndex] = freeFlowMomentumToPorousMediumIndex; - map[porousMediumIndex][freeFlowMomentumIndex] = freeFlowMomentumToPorousMediumIndex; - - // free flow mass - porous medium - map[freeFlowMassIndex][porousMediumIndex] = freeFlowMassToPorousMediumIndex; - map[porousMediumIndex][freeFlowMassIndex] = freeFlowMassToPorousMediumIndex; - - return map; -} - -template<std::size_t i> -constexpr auto coupledDomains(Dune::index_constant<i> domainI) -{ - if constexpr (i == freeFlowMomentumIndex) - return std::make_tuple(freeFlowMassIndex, porousMediumIndex); - else if constexpr (i == freeFlowMassIndex) - return std::make_tuple(freeFlowMomentumIndex, porousMediumIndex); - else // i == porousMediumIndex - return std::make_tuple(freeFlowMomentumIndex, freeFlowMassIndex); -} - -template<std::size_t i, std::size_t j> -constexpr auto globalToLocalDomainIndices(Dune::index_constant<i>, Dune::index_constant<j>) -{ - static_assert(i <= 2 && j <= 2); - static_assert(i != j); - - if constexpr (i < j) - return std::pair<Dune::index_constant<0>, Dune::index_constant<1>>{}; - else - return std::pair<Dune::index_constant<1>, Dune::index_constant<0>>{}; -} - -struct CouplingMaps -{ - static constexpr auto managerMap() - { - return FreeFlowPorousMediumDetail::makeCouplingManagerMap(); - } - - template<std::size_t i, std::size_t j> - static constexpr auto globalToLocal(Dune::index_constant<i> domainI, Dune::index_constant<j> domainJ) - { - return FreeFlowPorousMediumDetail::globalToLocalDomainIndices(domainI, domainJ); - } - - template<std::size_t i> - static constexpr auto coupledDomains(Dune::index_constant<i> domainI) - { - return FreeFlowPorousMediumDetail::coupledDomains(domainI); - } -}; +// declaration (specialize for different discretization types) +template<class MDTraits, + class DiscFFMomentum = typename MDTraits::template SubDomain<FreeFlowPorousMediumDetail::freeFlowMomentumIndex>::GridGeometry::DiscretizationMethod, + class DiscFFMass = typename MDTraits::template SubDomain<FreeFlowPorousMediumDetail::freeFlowMassIndex>::GridGeometry::DiscretizationMethod, + class DiscPM = typename MDTraits::template SubDomain<FreeFlowPorousMediumDetail::porousMediumIndex>::GridGeometry::DiscretizationMethod + > +struct FreeFlowPorousMediumCouplingManagerSelector; template<class MDTraits> -struct CouplingManagers -{ - template<std::size_t id> - using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag; - - using FreeFlowTraits = MultiDomainTraits< - SubDomainTypeTag<freeFlowMomentumIndex>, SubDomainTypeTag<freeFlowMassIndex> - >; - - using FreeFlowMomentumPorousMediumTraits = MultiDomainTraits< - SubDomainTypeTag<freeFlowMomentumIndex>, SubDomainTypeTag<porousMediumIndex> - >; - - using FreeFlowMassPorousMediumTraits = MultiDomainTraits< - SubDomainTypeTag<freeFlowMassIndex>, SubDomainTypeTag<porousMediumIndex> - >; +struct FreeFlowPorousMediumCouplingManagerSelector<MDTraits, DiscretizationMethods::FCStaggered, DiscretizationMethods::CCTpfa, DiscretizationMethods::CCTpfa> +{ using type = FreeFlowPorousMediumCouplingManagerStaggeredCCTpfa<MDTraits>; }; - using FreeFlowCouplingManager - = Dumux::FreeFlowCouplingManager<FreeFlowTraits>; - using FreeFlowMomentumPorousMediumCouplingManager - = Dumux::FreeFlowMomentumPorousMediumCouplingManager<FreeFlowMomentumPorousMediumTraits>; - using FreeFlowMassPorousMediumCouplingManager - = Dumux::FreeFlowMassPorousMediumCouplingManager<FreeFlowMassPorousMediumTraits>; -}; +} // end namespace FreeFlowPorousMediumDetail +#endif // DOXYGEN -} // end namespace Detail - - -/*! - * \ingroup FreeFlowPorousMediumCoupling - * \brief Coupling manager for coupling freeflow and porous medium flow models - */ template<class MDTraits> -class FreeFlowPorousMediumCouplingManager -: public MultiBinaryCouplingManager< - MDTraits, - FreeFlowPorousMediumDetail::CouplingMaps, - typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowCouplingManager, - typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMomentumPorousMediumCouplingManager, - typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMassPorousMediumCouplingManager -> -{ - using ParentType = MultiBinaryCouplingManager< - MDTraits, - FreeFlowPorousMediumDetail::CouplingMaps, - typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowCouplingManager, - typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMomentumPorousMediumCouplingManager, - typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMassPorousMediumCouplingManager - >; - - using Scalar = typename MDTraits::Scalar; - - // the sub domain type tags - template<std::size_t id> - using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag; - - template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>; - 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 SubControlVolumeFace = typename FVElementGeometry<id>::SubControlVolumeFace; - template<std::size_t id> using SubControlVolume = typename FVElementGeometry<id>::SubControlVolume; - template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; - template<std::size_t id> using NumEqVector = typename Problem<id>::Traits::NumEqVector; - - 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; - using SolutionVector = typename MDTraits::SolutionVector; - - using CouplingConditions = FreeFlowPorousMediumCouplingConditions<MDTraits, FreeFlowPorousMediumCouplingManager<MDTraits>>; - -public: - - template<std::size_t i, std::size_t j> - using SubCouplingManager = typename ParentType::template SubCouplingManager<i, j>; - - static constexpr auto freeFlowMomentumIndex = FreeFlowPorousMediumDetail::freeFlowMomentumIndex; - static constexpr auto freeFlowMassIndex = FreeFlowPorousMediumDetail::freeFlowMassIndex; - static constexpr auto porousMediumIndex = FreeFlowPorousMediumDetail::porousMediumIndex; - -public: - using ParentType::ParentType; - - template<class GridVarsTuple> - void init(std::shared_ptr<Problem<freeFlowMomentumIndex>> freeFlowMomentumProblem, - std::shared_ptr<Problem<freeFlowMassIndex>> freeFlowMassProblem, - std::shared_ptr<Problem<porousMediumIndex>> porousMediumProblem, - GridVarsTuple&& gridVarsTuple, - const SolutionVector& curSol) - { - this->updateSolution(curSol); // generic coupling manager stores tuple of shared_ptr - - // initialize the binary sub coupling managers - typename SubCouplingManager<freeFlowMomentumIndex, freeFlowMassIndex>::SolutionVectorStorage ffSolVecTuple; - std::get<0>(ffSolVecTuple) = std::get<freeFlowMomentumIndex>(this->curSol()); - std::get<1>(ffSolVecTuple) = std::get<freeFlowMassIndex>(this->curSol()); - this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).init( - freeFlowMomentumProblem, freeFlowMassProblem, - std::make_tuple(std::get<freeFlowMomentumIndex>(gridVarsTuple), std::get<freeFlowMassIndex>(gridVarsTuple)), - ffSolVecTuple - ); - - typename SubCouplingManager<freeFlowMassIndex, porousMediumIndex>::SolutionVectorStorage ffMassPmSolVecTuple; - std::get<0>(ffMassPmSolVecTuple) = std::get<freeFlowMassIndex>(this->curSol()); - std::get<1>(ffMassPmSolVecTuple) = std::get<porousMediumIndex>(this->curSol()); - this->subCouplingManager(freeFlowMassIndex, porousMediumIndex).init( - freeFlowMassProblem, porousMediumProblem, ffMassPmSolVecTuple - ); - - typename SubCouplingManager<freeFlowMomentumIndex, porousMediumIndex>::SolutionVectorStorage ffMomentumPmSolVecTuple; - std::get<0>(ffMomentumPmSolVecTuple) = std::get<freeFlowMomentumIndex>(this->curSol()); - std::get<1>(ffMomentumPmSolVecTuple) = std::get<porousMediumIndex>(this->curSol()); - this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).init( - freeFlowMomentumProblem, porousMediumProblem, ffMomentumPmSolVecTuple - ); - } - - /*! - * \brief Returns the mass flux across the coupling boundary. - */ - template<std::size_t i, std::size_t j> - auto massCouplingCondition(Dune::index_constant<i> domainI, Dune::index_constant<j> domainJ, - const FVElementGeometry<i>& fvGeometry, - const typename FVElementGeometry<i>::SubControlVolumeFace& scvf, - const ElementVolumeVariables<i>& elemVolVars) const - { - static_assert(domainI != freeFlowMomentumIndex && domainJ != freeFlowMomentumIndex); - - const auto& couplingContext = this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj) -> const auto& { - return cm.couplingContext(ii, fvGeometry, scvf); - }); - - const auto& freeFlowElement = [&] - { - if constexpr (domainI == freeFlowMassIndex) - return fvGeometry.element(); - else - return couplingContext.fvGeometry.element(); - }(); - - const auto& freeFlowScvf = [&] - { - if constexpr (domainI == freeFlowMassIndex) - return scvf; - else - return couplingContext.fvGeometry.scvf(couplingContext.freeFlowMassScvfIdx); - - }(); - - // todo revise velocity (see ff mom pm mgr) - - couplingContext.velocity = this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).faceVelocity(freeFlowElement, freeFlowScvf); - return CouplingConditions::massCouplingCondition(domainI, domainJ, fvGeometry, scvf, elemVolVars, couplingContext); - } - - - //////////////////////// Conditions for FreeFlowMomentum - PorousMedium coupling ////////// - /////////////////////////////////////////////////////////////////////////////////////////// - - NumEqVector<freeFlowMomentumIndex> momentumCouplingCondition(Dune::index_constant<freeFlowMomentumIndex> domainI, - Dune::index_constant<porousMediumIndex> domainJ, - const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const typename FVElementGeometry<freeFlowMomentumIndex>::SubControlVolumeFace& scvf, - const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars) const - { - if (scvf.isLateral()) - return NumEqVector<freeFlowMomentumIndex>(0.0); - - const auto& context = this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).couplingContext( - domainI, fvGeometry, scvf - ); - - return CouplingConditions::momentumCouplingCondition(fvGeometry, scvf, elemVolVars, context); - } - - /*! - * \brief Returns the intrinsic permeability of the coupled Darcy element. - */ - auto darcyPermeability(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const - { - if (scvf.isFrontal()) - { - const auto& context = this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).couplingContext( - Dune::index_constant<freeFlowMomentumIndex>(), fvGeometry, scvf - ); - - return CouplingConditions::darcyPermeability(fvGeometry, scvf, context); - } - else - { - const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); - const auto& orthogonalScv = fvGeometry.scv(orthogonalScvf.insideScvIdx()); - const auto& frontalScvfOnBoundary = fvGeometry.frontalScvfOnBoundary(orthogonalScv); - const auto& context = this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).couplingContext( - Dune::index_constant<freeFlowMomentumIndex>(), fvGeometry, frontalScvfOnBoundary - ); - - return CouplingConditions::darcyPermeability(fvGeometry, frontalScvfOnBoundary, context); - } - } - - //////////////////////// Conditions for FreeFlowMomentum - FreeFlowMass coupling ////////// - /////////////////////////////////////////////////////////////////////////////////////////// - - /*! - * \brief Returns the pressure at a given sub control volume face. - */ - Scalar pressure(const Element<freeFlowMomentumIndex>& element, - const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).pressure( - element, fvGeometry, scvf - ); - } - - /*! - * \brief Returns the pressure at the center of a sub control volume corresponding to a given sub control volume face. - * This is used for setting a Dirichlet pressure for the mass model when a fixed pressure for the momentum balance is set at another - * boundary. Since the the pressure at the given scvf is solution-dependent and thus unknown a priori, we just use the value - * of the interior cell here. - */ - Scalar cellPressure(const Element<freeFlowMomentumIndex>& element, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).cellPressure( - element, scvf - ); - } - - /*! - * \brief Returns the density at a given sub control volume face. - */ - Scalar density(const Element<freeFlowMomentumIndex>& element, - const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, - const bool considerPreviousTimeStep = false) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).density( - element, fvGeometry, scvf, considerPreviousTimeStep - ); - } - - auto insideAndOutsideDensity(const Element<freeFlowMomentumIndex>& element, - const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, - const bool considerPreviousTimeStep = false) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).insideAndOutsideDensity( - element, fvGeometry, scvf, considerPreviousTimeStep - ); - } - - /*! - * \brief Returns the density at a given sub control volume. - */ - Scalar density(const Element<freeFlowMomentumIndex>& element, - const SubControlVolume<freeFlowMomentumIndex>& scv, - const bool considerPreviousTimeStep = false) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).density( - element, scv, considerPreviousTimeStep - ); - } - - /*! - * \brief Returns the pressure at a given sub control volume face. - */ - Scalar effectiveViscosity(const Element<freeFlowMomentumIndex>& element, - const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).effectiveViscosity( - element, fvGeometry, scvf - ); - } - - /*! - * \brief Returns the velocity at a given sub control volume face. - */ - auto faceVelocity(const Element<freeFlowMassIndex>& element, - const SubControlVolumeFace<freeFlowMassIndex>& scvf) const - { - return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).faceVelocity( - element, scvf - ); - } - - template<std::size_t i> - const Problem<i>& problem(Dune::index_constant<i> domainI) const - { - return this->subApply(domainI, [&](const auto& cm, auto&& ii) -> const auto& { - return cm.problem(ii); - }); - } - - template<std::size_t i, std::size_t j> - bool isCoupled(Dune::index_constant<i> domainI, - Dune::index_constant<j> domainJ, - const SubControlVolumeFace<i>& scvf) const - { - return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj){ - return cm.isCoupled(ii, scvf); - }); - } - - /*! - * \brief If the boundary entity is on a coupling boundary - * \param domainI the domain index of domain i for which to compute the flux - * \param domainJ the domain index of domain j for which to compute the flux - * \param scv the sub control volume - */ - template<std::size_t i, std::size_t j> - bool isCoupled(Dune::index_constant<i> domainI, - Dune::index_constant<j> domainJ, - const SubControlVolume<i>& scv) const - { - return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj){ - return cm.isCoupled(ii, scv); - }); - } - - /*! - * \brief Returns whether a given scvf is coupled to the other domain - */ - bool isCoupledLateralScvf(Dune::index_constant<freeFlowMomentumIndex> domainI, - Dune::index_constant<porousMediumIndex> domainJ, - const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const - { - return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj){ - return cm.isCoupledLateralScvf(ii, scvf); - }); - } - - - using ParentType::couplingStencil; - /*! - * \brief returns an iterable container of all indices of degrees of freedom of domain j - * that couple with / influence the residual of the given sub-control volume of domain i - * - * \param domainI the domain index of domain i - * \param elementI the coupled element of domain à - * \param scvI the sub-control volume of domain i - * \param domainJ the domain index of domain j - */ - template<std::size_t j> - const auto& couplingStencil(Dune::index_constant<freeFlowMomentumIndex> domainI, - const Element<freeFlowMomentumIndex>& elementI, - const SubControlVolume<freeFlowMomentumIndex>& scvI, - Dune::index_constant<j> domainJ) const - { - static_assert(freeFlowMomentumIndex != j); - return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj) -> const auto& { - return cm.couplingStencil(ii, elementI, scvI, jj); - }); - } -}; +using FreeFlowPorousMediumCouplingManager = typename FreeFlowPorousMediumDetail::FreeFlowPorousMediumCouplingManagerSelector<MDTraits>::type; } // end namespace Dumux diff --git a/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager_base.hh b/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager_base.hh new file mode 100644 index 0000000000000000000000000000000000000000..664d051fcc93102d795b7406fa5d5b9b1e9e98de --- /dev/null +++ b/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager_base.hh @@ -0,0 +1,278 @@ +// -*- 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 + * \brief Base class for coupling freeflow and porous medium flow models. + */ + +#ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FREEFLOW_POROUSMEDIUM_COUPLINGMANAGER_BASE_HH +#define DUMUX_MULTIDOMAIN_BOUNDARY_FREEFLOW_POROUSMEDIUM_COUPLINGMANAGER_BASE_HH + +#include <utility> +#include <memory> + +#include <dune/common/indices.hh> +#include <dumux/common/properties.hh> +#include <dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh> +#include <dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager.hh> +#include <dumux/multidomain/freeflow/couplingmanager.hh> +#include <dumux/multidomain/multibinarycouplingmanager.hh> + +namespace Dumux { + +#ifndef DOXYGEN +namespace FreeFlowPorousMediumDetail { + +// global subdomain indices +static constexpr auto freeFlowMomentumIndex = Dune::index_constant<0>(); +static constexpr auto freeFlowMassIndex = Dune::index_constant<1>(); +static constexpr auto porousMediumIndex = Dune::index_constant<2>(); + +// coupling indices +static constexpr auto freeFlowMassToFreeFlowMomentumIndex = Dune::index_constant<0>(); +static constexpr auto freeFlowMomentumToPorousMediumIndex = Dune::index_constant<1>(); +static constexpr auto freeFlowMassToPorousMediumIndex = Dune::index_constant<2>(); +static constexpr auto noCouplingIdx = Dune::index_constant<99>(); + +constexpr auto makeCouplingManagerMap() +{ + auto map = std::array<std::array<std::size_t, 3>, 3>{}; + + // free flow (momentum-mass) + map[freeFlowMomentumIndex][freeFlowMassIndex] = freeFlowMassToFreeFlowMomentumIndex; + map[freeFlowMassIndex][freeFlowMomentumIndex] = freeFlowMassToFreeFlowMomentumIndex; + + // free flow momentum - porous medium + map[freeFlowMomentumIndex][porousMediumIndex] = freeFlowMomentumToPorousMediumIndex; + map[porousMediumIndex][freeFlowMomentumIndex] = freeFlowMomentumToPorousMediumIndex; + + // free flow mass - porous medium + map[freeFlowMassIndex][porousMediumIndex] = freeFlowMassToPorousMediumIndex; + map[porousMediumIndex][freeFlowMassIndex] = freeFlowMassToPorousMediumIndex; + + return map; +} + +template<std::size_t i> +constexpr auto coupledDomains(Dune::index_constant<i> domainI) +{ + if constexpr (i == freeFlowMomentumIndex) + return std::make_tuple(freeFlowMassIndex, porousMediumIndex); + else if constexpr (i == freeFlowMassIndex) + return std::make_tuple(freeFlowMomentumIndex, porousMediumIndex); + else // i == porousMediumIndex + return std::make_tuple(freeFlowMomentumIndex, freeFlowMassIndex); +} + +template<std::size_t i, std::size_t j> +constexpr auto globalToLocalDomainIndices(Dune::index_constant<i>, Dune::index_constant<j>) +{ + static_assert(i <= 2 && j <= 2); + static_assert(i != j); + + if constexpr (i < j) + return std::pair<Dune::index_constant<0>, Dune::index_constant<1>>{}; + else + return std::pair<Dune::index_constant<1>, Dune::index_constant<0>>{}; +} + +struct CouplingMaps +{ + static constexpr auto managerMap() + { + return FreeFlowPorousMediumDetail::makeCouplingManagerMap(); + } + + template<std::size_t i, std::size_t j> + static constexpr auto globalToLocal(Dune::index_constant<i> domainI, Dune::index_constant<j> domainJ) + { + return FreeFlowPorousMediumDetail::globalToLocalDomainIndices(domainI, domainJ); + } + + template<std::size_t i> + static constexpr auto coupledDomains(Dune::index_constant<i> domainI) + { + return FreeFlowPorousMediumDetail::coupledDomains(domainI); + } +}; + +template<class MDTraits> +struct CouplingManagers +{ + template<std::size_t id> + using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag; + + using FreeFlowTraits = MultiDomainTraits< + SubDomainTypeTag<freeFlowMomentumIndex>, SubDomainTypeTag<freeFlowMassIndex> + >; + + using FreeFlowMomentumPorousMediumTraits = MultiDomainTraits< + SubDomainTypeTag<freeFlowMomentumIndex>, SubDomainTypeTag<porousMediumIndex> + >; + + using FreeFlowMassPorousMediumTraits = MultiDomainTraits< + SubDomainTypeTag<freeFlowMassIndex>, SubDomainTypeTag<porousMediumIndex> + >; + + using FreeFlowCouplingManager + = Dumux::FreeFlowCouplingManager<FreeFlowTraits>; + using FreeFlowMomentumPorousMediumCouplingManager + = Dumux::FreeFlowMomentumPorousMediumCouplingManager<FreeFlowMomentumPorousMediumTraits>; + using FreeFlowMassPorousMediumCouplingManager + = Dumux::FreeFlowMassPorousMediumCouplingManager<FreeFlowMassPorousMediumTraits>; +}; + +} // end namespace FreeFlowPorousMediumDetail +#endif // DOXYGEN + +/*! + * \ingroup FreeFlowPorousMediumCoupling + * \brief Base coupling manager for coupling freeflow and porous medium flow models + */ +template<class MDTraits> +class FreeFlowPorousMediumCouplingManagerBase +: public MultiBinaryCouplingManager< + MDTraits, + FreeFlowPorousMediumDetail::CouplingMaps, + typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowCouplingManager, + typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMomentumPorousMediumCouplingManager, + typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMassPorousMediumCouplingManager +> +{ + using ParentType = MultiBinaryCouplingManager< + MDTraits, + FreeFlowPorousMediumDetail::CouplingMaps, + typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowCouplingManager, + typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMomentumPorousMediumCouplingManager, + typename FreeFlowPorousMediumDetail::CouplingManagers<MDTraits>::FreeFlowMassPorousMediumCouplingManager + >; + + using Scalar = typename MDTraits::Scalar; + + // the sub domain type tags + template<std::size_t id> + using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag; + + template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>; + 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 SubControlVolumeFace = typename FVElementGeometry<id>::SubControlVolumeFace; + template<std::size_t id> using SubControlVolume = typename FVElementGeometry<id>::SubControlVolume; + template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; + template<std::size_t id> using NumEqVector = typename Problem<id>::Traits::NumEqVector; + + 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; + using SolutionVector = typename MDTraits::SolutionVector; + +public: + + template<std::size_t i, std::size_t j> + using SubCouplingManager = typename ParentType::template SubCouplingManager<i, j>; + + static constexpr auto freeFlowMomentumIndex = FreeFlowPorousMediumDetail::freeFlowMomentumIndex; + static constexpr auto freeFlowMassIndex = FreeFlowPorousMediumDetail::freeFlowMassIndex; + static constexpr auto porousMediumIndex = FreeFlowPorousMediumDetail::porousMediumIndex; + +public: + using ParentType::ParentType; + + template<class GridVarsTuple> + void init(std::shared_ptr<Problem<freeFlowMomentumIndex>> freeFlowMomentumProblem, + std::shared_ptr<Problem<freeFlowMassIndex>> freeFlowMassProblem, + std::shared_ptr<Problem<porousMediumIndex>> porousMediumProblem, + GridVarsTuple&& gridVarsTuple, + const SolutionVector& curSol) + { + this->updateSolution(curSol); // generic coupling manager stores tuple of shared_ptr + + // initialize the binary sub coupling managers + typename SubCouplingManager<freeFlowMomentumIndex, freeFlowMassIndex>::SolutionVectorStorage ffSolVecTuple; + std::get<0>(ffSolVecTuple) = std::get<freeFlowMomentumIndex>(this->curSol()); + std::get<1>(ffSolVecTuple) = std::get<freeFlowMassIndex>(this->curSol()); + this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).init( + freeFlowMomentumProblem, freeFlowMassProblem, + std::make_tuple(std::get<freeFlowMomentumIndex>(gridVarsTuple), std::get<freeFlowMassIndex>(gridVarsTuple)), + ffSolVecTuple + ); + + typename SubCouplingManager<freeFlowMassIndex, porousMediumIndex>::SolutionVectorStorage ffMassPmSolVecTuple; + std::get<0>(ffMassPmSolVecTuple) = std::get<freeFlowMassIndex>(this->curSol()); + std::get<1>(ffMassPmSolVecTuple) = std::get<porousMediumIndex>(this->curSol()); + this->subCouplingManager(freeFlowMassIndex, porousMediumIndex).init( + freeFlowMassProblem, porousMediumProblem, ffMassPmSolVecTuple + ); + + typename SubCouplingManager<freeFlowMomentumIndex, porousMediumIndex>::SolutionVectorStorage ffMomentumPmSolVecTuple; + std::get<0>(ffMomentumPmSolVecTuple) = std::get<freeFlowMomentumIndex>(this->curSol()); + std::get<1>(ffMomentumPmSolVecTuple) = std::get<porousMediumIndex>(this->curSol()); + this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).init( + freeFlowMomentumProblem, porousMediumProblem, ffMomentumPmSolVecTuple + ); + } + + template<std::size_t i> + const Problem<i>& problem(Dune::index_constant<i> domainI) const + { + return this->subApply(domainI, [&](const auto& cm, auto&& ii) -> const auto& { + return cm.problem(ii); + }); + } + + template<std::size_t i, std::size_t j> + bool isCoupled(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ, + const SubControlVolumeFace<i>& scvf) const + { + return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj){ + return cm.isCoupled(ii, scvf); + }); + } + + /*! + * \brief If the boundary entity is on a coupling boundary + * \param domainI the domain index of domain i for which to compute the flux + * \param domainJ the domain index of domain j for which to compute the flux + * \param scv the sub control volume + */ + template<std::size_t i, std::size_t j> + bool isCoupled(Dune::index_constant<i> domainI, + Dune::index_constant<j> domainJ, + const SubControlVolume<i>& scv) const + { + return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj){ + return cm.isCoupled(ii, scv); + }); + } + + using ParentType::couplingStencil; + /*! + * \brief returns an iterable container of all indices of degrees of freedom of domain j + * that couple with / influence the residual of the given sub-control volume of domain i + * + * \param domainI the domain index of domain i + * \param elementI the coupled element of domain à + * \param scvI the sub-control volume of domain i + * \param domainJ the domain index of domain j + */ + template<std::size_t j> + const auto& couplingStencil(Dune::index_constant<freeFlowMomentumIndex> domainI, + const Element<freeFlowMomentumIndex>& elementI, + const SubControlVolume<freeFlowMomentumIndex>& scvI, + Dune::index_constant<j> domainJ) const + { + static_assert(freeFlowMomentumIndex != j); + return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj) -> const auto& { + return cm.couplingStencil(ii, elementI, scvI, jj); + }); + } +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager_staggered_cctpfa.hh b/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager_staggered_cctpfa.hh new file mode 100644 index 0000000000000000000000000000000000000000..5ca8cc1f15ab1b8eb8184b71ce89052f7ebdca1e --- /dev/null +++ b/dumux/multidomain/boundary/freeflowporousmedium/couplingmanager_staggered_cctpfa.hh @@ -0,0 +1,245 @@ +// -*- 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::FreeFlowPorousMediumCouplingManagerStaggeredCCTpfa + */ + +#ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FREEFLOW_POROUSMEDIUM_COUPLINGMANAGER_STAGGERED_CCTPFA_HH +#define DUMUX_MULTIDOMAIN_BOUNDARY_FREEFLOW_POROUSMEDIUM_COUPLINGMANAGER_STAGGERED_CCTPFA_HH + +#include "couplingmanager_base.hh" +#include "couplingconditions_staggered_cctpfa.hh" + +namespace Dumux { + +/*! + * \ingroup FreeFlowPorousMediumCoupling + * \brief Coupling manager for coupling freeflow and porous medium flow models + * specialization for staggered-cctpfa coupling. + */ +template<class MDTraits> +class FreeFlowPorousMediumCouplingManagerStaggeredCCTpfa +: public FreeFlowPorousMediumCouplingManagerBase<MDTraits> +{ + using ParentType = FreeFlowPorousMediumCouplingManagerBase<MDTraits>; + + using Scalar = typename MDTraits::Scalar; + + // the sub domain type tags + template<std::size_t id> + using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag; + + template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>; + 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 SubControlVolumeFace = typename FVElementGeometry<id>::SubControlVolumeFace; + template<std::size_t id> using SubControlVolume = typename FVElementGeometry<id>::SubControlVolume; + template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView; + template<std::size_t id> using NumEqVector = typename Problem<id>::Traits::NumEqVector; + + 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; + using SolutionVector = typename MDTraits::SolutionVector; + + using CouplingConditions = FFPMCouplingConditionsStaggeredCCTpfa<MDTraits, FreeFlowPorousMediumCouplingManagerStaggeredCCTpfa<MDTraits>>; + +public: + static constexpr auto freeFlowMomentumIndex = ParentType::freeFlowMomentumIndex; + static constexpr auto freeFlowMassIndex = ParentType::freeFlowMassIndex; + static constexpr auto porousMediumIndex = ParentType::porousMediumIndex; + +public: + /*! + * \brief Returns the mass flux across the coupling boundary. + */ + template<std::size_t i, std::size_t j> + auto massCouplingCondition(Dune::index_constant<i> domainI, Dune::index_constant<j> domainJ, + const FVElementGeometry<i>& fvGeometry, + const typename FVElementGeometry<i>::SubControlVolumeFace& scvf, + const ElementVolumeVariables<i>& elemVolVars) const + { + static_assert(domainI != freeFlowMomentumIndex && domainJ != freeFlowMomentumIndex); + + const auto& couplingContext = this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj) -> const auto& { + return cm.couplingContext(ii, fvGeometry, scvf); + }); + + const auto& freeFlowElement = [&] + { + if constexpr (domainI == freeFlowMassIndex) + return fvGeometry.element(); + else + return couplingContext.fvGeometry.element(); + }(); + + const auto& freeFlowScvf = [&] + { + if constexpr (domainI == freeFlowMassIndex) + return scvf; + else + return couplingContext.fvGeometry.scvf(couplingContext.freeFlowMassScvfIdx); + + }(); + + // todo revise velocity (see ff mom pm mgr) + + couplingContext.velocity = this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).faceVelocity(freeFlowElement, freeFlowScvf); + return CouplingConditions::massCouplingCondition(domainI, domainJ, fvGeometry, scvf, elemVolVars, couplingContext); + } + + + //////////////////////// Conditions for FreeFlowMomentum - PorousMedium coupling ////////// + /////////////////////////////////////////////////////////////////////////////////////////// + + NumEqVector<freeFlowMomentumIndex> momentumCouplingCondition(Dune::index_constant<freeFlowMomentumIndex> domainI, + Dune::index_constant<porousMediumIndex> domainJ, + const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const typename FVElementGeometry<freeFlowMomentumIndex>::SubControlVolumeFace& scvf, + const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars) const + { + if (scvf.isLateral()) + return NumEqVector<freeFlowMomentumIndex>(0.0); + + const auto& context = this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).couplingContext( + domainI, fvGeometry, scvf + ); + + return CouplingConditions::momentumCouplingCondition(fvGeometry, scvf, elemVolVars, context); + } + + /*! + * \brief Returns the intrinsic permeability of the coupled Darcy element. + */ + auto darcyPermeability(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const + { + if (scvf.isFrontal()) + { + const auto& context = this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).couplingContext( + Dune::index_constant<freeFlowMomentumIndex>(), fvGeometry, scvf + ); + + return CouplingConditions::darcyPermeability(fvGeometry, scvf, context); + } + else + { + const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); + const auto& orthogonalScv = fvGeometry.scv(orthogonalScvf.insideScvIdx()); + const auto& frontalScvfOnBoundary = fvGeometry.frontalScvfOnBoundary(orthogonalScv); + const auto& context = this->subCouplingManager(freeFlowMomentumIndex, porousMediumIndex).couplingContext( + Dune::index_constant<freeFlowMomentumIndex>(), fvGeometry, frontalScvfOnBoundary + ); + + return CouplingConditions::darcyPermeability(fvGeometry, frontalScvfOnBoundary, context); + } + } + + //////////////////////// Conditions for FreeFlowMomentum - FreeFlowMass coupling ////////// + /////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * \brief Returns the pressure at a given sub control volume face. + */ + Scalar pressure(const Element<freeFlowMomentumIndex>& element, + const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).pressure( + element, fvGeometry, scvf + ); + } + + /*! + * \brief Returns the pressure at the center of a sub control volume corresponding to a given sub control volume face. + * This is used for setting a Dirichlet pressure for the mass model when a fixed pressure for the momentum balance is set at another + * boundary. Since the the pressure at the given scvf is solution-dependent and thus unknown a priori, we just use the value + * of the interior cell here. + */ + Scalar cellPressure(const Element<freeFlowMomentumIndex>& element, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).cellPressure( + element, scvf + ); + } + + /*! + * \brief Returns the density at a given sub control volume face. + */ + Scalar density(const Element<freeFlowMomentumIndex>& element, + const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, + const bool considerPreviousTimeStep = false) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).density( + element, fvGeometry, scvf, considerPreviousTimeStep + ); + } + + auto insideAndOutsideDensity(const Element<freeFlowMomentumIndex>& element, + const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf, + const bool considerPreviousTimeStep = false) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).insideAndOutsideDensity( + element, fvGeometry, scvf, considerPreviousTimeStep + ); + } + + /*! + * \brief Returns the density at a given sub control volume. + */ + Scalar density(const Element<freeFlowMomentumIndex>& element, + const SubControlVolume<freeFlowMomentumIndex>& scv, + const bool considerPreviousTimeStep = false) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).density( + element, scv, considerPreviousTimeStep + ); + } + + /*! + * \brief Returns the pressure at a given sub control volume face. + */ + Scalar effectiveViscosity(const Element<freeFlowMomentumIndex>& element, + const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).effectiveViscosity( + element, fvGeometry, scvf + ); + } + + /*! + * \brief Returns the velocity at a given sub control volume face. + */ + auto faceVelocity(const Element<freeFlowMassIndex>& element, + const SubControlVolumeFace<freeFlowMassIndex>& scvf) const + { + return this->subCouplingManager(freeFlowMomentumIndex, freeFlowMassIndex).faceVelocity( + element, scvf + ); + } + + /*! + * \brief Returns whether a given scvf is coupled to the other domain + */ + bool isCoupledLateralScvf(Dune::index_constant<freeFlowMomentumIndex> domainI, + Dune::index_constant<porousMediumIndex> domainJ, + const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const + { + return this->subApply(domainI, domainJ, [&](const auto& cm, auto&& ii, auto&& jj){ + return cm.isCoupledLateralScvf(ii, scvf); + }); + } +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh index 9f8c8493a5b976b6ff687e2dc39e530409834a41..c59221cc89224cbc0c5b73e5b64f6a9c7fca5ccb 100644 --- a/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh +++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager.hh @@ -7,285 +7,37 @@ /*! * \file * \ingroup FreeFlowPorousMediumCoupling - * \copydoc Dumux::FreeFlowMassPorousMediumCouplingManager + * \brief Coupling managers specialized for different discretization schemes for mass coupling */ #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); - } +#ifndef DOXYGEN +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 FreeFlowMassPorousMediumDetail +#endif // DOXYGEN - 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 0000000000000000000000000000000000000000..16fb3d0751f34a5db40540d09809883cd89b6f6f --- /dev/null +++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmasspm/couplingmanager_staggered_cctpfa.hh @@ -0,0 +1,293 @@ +// -*- 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::FFMassPMCouplingManagerStaggeredCCTpfa + */ + +#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. + * Specialization for staggered-cctpfa coupling. + */ +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 diff --git a/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager.hh b/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager.hh index d07569c68a0f4558847397a43abc1d269210f4cd..52156ab9c40688e449392c68bc708627669ddfd9 100644 --- a/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager.hh +++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager.hh @@ -7,403 +7,37 @@ /*! * \file * \ingroup FreeFlowPorousMediumCoupling - * \copydoc Dumux::FreeFlowMomentumPorousMediumCouplingManager + * \brief Coupling managers specialized for different discretization schemes for momentum coupling */ #ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMMOMENTUMPM_COUPLINGMANAGER_HH #define DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMMOMENTUMPM_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 "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 FreeFlowMomentumPorousMediumCouplingManager -: public CouplingManager<MDTraits> -{ - using Scalar = typename MDTraits::Scalar; - using ParentType = CouplingManager<MDTraits>; - -public: - static constexpr auto freeFlowMomentumIndex = 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 FreeFlowMomentumTypeTag = typename MDTraits::template SubDomain<freeFlowMomentumIndex>::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<freeFlowMomentumIndex>::Geometry::GlobalCoordinate; - - struct FreeFlowMomentumCouplingContext - { - Element<porousMediumIndex> element; - VolumeVariables<porousMediumIndex> volVars; - FVElementGeometry<porousMediumIndex> fvGeometry; - std::size_t freeFlowMomentumScvfIdx; - std::size_t porousMediumScvfIdx; - std::size_t porousMediumDofIdx; - VelocityVector gravity; - }; - - struct PorousMediumCouplingContext - { - Element<freeFlowMomentumIndex> element; - FVElementGeometry<freeFlowMomentumIndex> fvGeometry; - std::size_t porousMediumScvfIdx; - std::size_t freeFlowMomentumScvfIdx; - std::size_t freeFlowMomentumDofIdx; - VelocityVector faceVelocity; - }; - - using CouplingMapper = FreeFlowMomentumPorousMediumCouplingMapper<MDTraits, FreeFlowMomentumPorousMediumCouplingManager<MDTraits>>; - -public: - - /*! - * \brief Methods to be accessed by main - */ - // \{ - - //! Initialize the coupling manager - void init(std::shared_ptr<Problem<freeFlowMomentumIndex>> freeFlowMomentumProblem, - std::shared_ptr<Problem<porousMediumIndex>> porousMediumProblem, - SolutionVectorStorage& curSol) - { - this->setSubProblems(std::make_tuple(freeFlowMomentumProblem, porousMediumProblem)); - 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 - - // update the faceVelocity in the PorousMediumCouplingContext - if constexpr (domainJ == freeFlowMomentumIndex) - { - // we only need to update if we are assembling the porous medium domain - // since the freeflow domain will not use the velocity from the context - if constexpr (domainI == porousMediumIndex) - { - auto& context = std::get<porousMediumIndex>(couplingContext_); - for (auto& c : context) - { - if (c.freeFlowMomentumDofIdx == dofIdxGlobalJ) - { - const auto& scvf = localAssemblerI.fvGeometry().scvf(c.porousMediumScvfIdx); - c.faceVelocity = faceVelocity(localAssemblerI.element(), scvf); - } - } - } - } - - // update volVars in the FreeFlowMomentumCouplingContext - else if (domainJ == porousMediumIndex) - { - auto& context = std::get<freeFlowMomentumIndex>(couplingContext_); - for (auto& c : context) - { - if (c.porousMediumDofIdx == dofIdxGlobalJ) - { - const auto& ggJ = c.fvGeometry.gridGeometry(); - const auto& scv = *scvs(c.fvGeometry).begin(); - const auto elemSol = elementSolution(c.element, this->curSol(domainJ), ggJ); - 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_); +#ifndef DOXYGEN +namespace FreeFlowMomentumPorousMediumDetail { - if (contexts.empty() || couplingContextBoundForElement_[i] != scvf.insideScvIdx()) - bindCouplingContext_(domainI, fvGeometry); +// declaration (specialize for different discretization types) +template<class MDTraits, + class DiscFFMomentum = typename MDTraits::template SubDomain<0>::GridGeometry::DiscretizationMethod, + class DiscPM = typename MDTraits::template SubDomain<1>::GridGeometry::DiscretizationMethod + > +struct FreeFlowMomentumPorousMediumCouplingManagerSelector; - for (const auto& context : contexts) - { - const auto expectedScvfIdx = domainI == freeFlowMomentumIndex ? context.freeFlowMomentumScvfIdx : 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 - */ - const CouplingStencil& couplingStencil(Dune::index_constant<porousMediumIndex> domainI, - const Element<porousMediumIndex>& element, - Dune::index_constant<freeFlowMomentumIndex> domainJ) const - { - const auto eIdx = this->problem(domainI).gridGeometry().elementMapper().index(element); - return couplingMapper_.couplingStencil(domainI, eIdx, domainJ); - } - - /*! - * \brief returns an iterable container of all indices of degrees of freedom of domain j - * that couple with / influence the residual of the given sub-control volume of domain i - * - * \param domainI the domain index of domain i - * \param elementI the coupled element of domain à - * \param scvI the sub-control volume of domain i - * \param domainJ the domain index of domain j - */ - const CouplingStencil& couplingStencil(Dune::index_constant<freeFlowMomentumIndex> domainI, - const Element<freeFlowMomentumIndex>& elementI, - const SubControlVolume<freeFlowMomentumIndex>& scvI, - Dune::index_constant<porousMediumIndex> domainJ) const - { - return couplingMapper_.couplingStencil(domainI, elementI, scvI, domainJ); - } - - using ParentType::evalCouplingResidual; - - /*! - * \brief evaluate the coupling residual - * special interface for fcstaggered methods - */ - template<class LocalAssemblerI, std::size_t j> - decltype(auto) evalCouplingResidual(Dune::index_constant<freeFlowMomentumIndex> domainI, - const LocalAssemblerI& localAssemblerI, - const SubControlVolume<freeFlowMomentumIndex>& scvI, - Dune::index_constant<j> domainJ, - std::size_t dofIdxGlobalJ) const - { - return localAssemblerI.evalLocalResidual(); - } - - // \} - - /*! - * \brief Returns whether a given scvf is coupled to the other domain - */ - bool isCoupledLateralScvf(Dune::index_constant<freeFlowMomentumIndex> domainI, const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const - { return couplingMapper_.isCoupledLateralScvf(domainI, scvf); } - - /*! - * \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 - { - if constexpr (i == freeFlowMomentumIndex) - return couplingMapper_.isCoupled(domainI, scvf) || couplingMapper_.isCoupledLateralScvf(domainI, scvf); - else - return couplingMapper_.isCoupled(domainI, scvf); - } - - /*! - * \brief If the boundary entity is on a coupling boundary - * \param domainI the domain index for which to compute the flux - * \param scv the sub control volume - */ - bool isCoupled(Dune::index_constant<freeFlowMomentumIndex> domainI, - const SubControlVolume<freeFlowMomentumIndex>& scv) const - { return couplingMapper_.isCoupled(domainI, scv); } - - /*! - * \brief Returns the velocity at a given sub control volume face. - */ - auto faceVelocity(const Element<porousMediumIndex>& element, - const SubControlVolumeFace<porousMediumIndex>& scvf) const - { - // create a unit normal vector oriented in positive coordinate direction - auto velocity = scvf.unitOuterNormal(); - using std::abs; - std::for_each(velocity.begin(), velocity.end(), [](auto& v){ v = abs(v); }); - - // create the actual velocity vector - velocity *= this->curSol(freeFlowMomentumIndex)[ - couplingMapper_.outsideDofIndex(Dune::index_constant<porousMediumIndex>(), scvf) - ]; - - return velocity; - } - -private: - //! 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 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); } - - /*! - * \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 (couplingMapper_.isCoupled(domainI, scvf)) - { - if constexpr (domainI == freeFlowMomentumIndex) - { - const auto otherElementIdx = couplingMapper_.outsideElementIndex(domainI, scvf); - constexpr auto domainJ = Dune::index_constant<1-i>(); - 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), - scvf.index(), - couplingMapper_.flipScvfIndex(domainI, scvf), - otherElementIdx, - this->problem(domainJ).spatialParams().gravity(scvf.center()) - }); - } - - else if constexpr (domainI == porousMediumIndex) - { - const auto otherElementIdx = couplingMapper_.outsideElementIndex(domainI, scvf); - constexpr auto domainJ = Dune::index_constant<1-i>(); - const auto& otherGridGeometry = this->problem(domainJ).gridGeometry(); - const auto& otherElement = otherGridGeometry.element(otherElementIdx); - auto otherFvGeometry = localView(otherGridGeometry).bindElement(otherElement); - - const auto otherScvfIdx = couplingMapper_.flipScvfIndex(domainI, scvf); - const auto& otherScvf = otherFvGeometry.scvf(otherScvfIdx); - const auto& otherScv = otherFvGeometry.scv(otherScvf.insideScvIdx()); - - context.push_back({ - otherElement, - std::move(otherFvGeometry), - scvf.index(), - otherScvfIdx, - otherScv.dofIndex(), - faceVelocity(fvGeometry.element(), scvf) - }); - } - } - } - } +template<class MDTraits> +struct FreeFlowMomentumPorousMediumCouplingManagerSelector<MDTraits, DiscretizationMethods::FCStaggered, DiscretizationMethods::CCTpfa> +{ using type = FFMomentumPMCouplingManagerStaggeredCCTpfa<MDTraits>; }; - mutable std::tuple<std::vector<FreeFlowMomentumCouplingContext>, std::vector<PorousMediumCouplingContext>> couplingContext_; - mutable std::array<std::size_t, 2> couplingContextBoundForElement_; +} // end namespace FreeFlowMomentumPorousMediumDetail +#endif // DOXYGEN - CouplingMapper couplingMapper_; -}; +template<class MDTraits> +using FreeFlowMomentumPorousMediumCouplingManager = typename FreeFlowMomentumPorousMediumDetail::FreeFlowMomentumPorousMediumCouplingManagerSelector<MDTraits>::type; } // end namespace Dumux diff --git a/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager_staggered_cctpfa.hh b/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager_staggered_cctpfa.hh new file mode 100644 index 0000000000000000000000000000000000000000..6e0c3c0cbdc136e0d77377261dc2123d395e9f5a --- /dev/null +++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmanager_staggered_cctpfa.hh @@ -0,0 +1,411 @@ +// -*- 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::FFMomentumPMCouplingManagerStaggeredCCTpfa + */ + +#ifndef DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMMOMENTUMPM_COUPLINGMANAGER_STAGGERED_TPFA_HH +#define DUMUX_MULTIDOMAIN_BOUNDARY_FFPM_FFMMOMENTUMPM_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 "couplingmapper_staggered_cctpfa.hh" + +namespace Dumux { + +/*! + * \ingroup FreeFlowPorousMediumCoupling + * \brief Coupling manager for Stokes and Darcy domains with equal dimension + * specialization for staggered-cctpfa coupling. + */ +template<class MDTraits> +class FFMomentumPMCouplingManagerStaggeredCCTpfa +: public CouplingManager<MDTraits> +{ + using Scalar = typename MDTraits::Scalar; + using ParentType = CouplingManager<MDTraits>; + +public: + static constexpr auto freeFlowMomentumIndex = 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 FreeFlowMomentumTypeTag = typename MDTraits::template SubDomain<freeFlowMomentumIndex>::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<freeFlowMomentumIndex>::Geometry::GlobalCoordinate; + + struct FreeFlowMomentumCouplingContext + { + Element<porousMediumIndex> element; + VolumeVariables<porousMediumIndex> volVars; + FVElementGeometry<porousMediumIndex> fvGeometry; + std::size_t freeFlowMomentumScvfIdx; + std::size_t porousMediumScvfIdx; + std::size_t porousMediumDofIdx; + VelocityVector gravity; + }; + + struct PorousMediumCouplingContext + { + Element<freeFlowMomentumIndex> element; + FVElementGeometry<freeFlowMomentumIndex> fvGeometry; + std::size_t porousMediumScvfIdx; + std::size_t freeFlowMomentumScvfIdx; + std::size_t freeFlowMomentumDofIdx; + VelocityVector faceVelocity; + }; + + using CouplingMapper = FFMomentumPMCouplingMapperStaggeredCCTpfa<MDTraits, FFMomentumPMCouplingManagerStaggeredCCTpfa<MDTraits>>; + +public: + + /*! + * \brief Methods to be accessed by main + */ + // \{ + + //! Initialize the coupling manager + void init(std::shared_ptr<Problem<freeFlowMomentumIndex>> freeFlowMomentumProblem, + std::shared_ptr<Problem<porousMediumIndex>> porousMediumProblem, + SolutionVectorStorage& curSol) + { + this->setSubProblems(std::make_tuple(freeFlowMomentumProblem, porousMediumProblem)); + 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 + + // update the faceVelocity in the PorousMediumCouplingContext + if constexpr (domainJ == freeFlowMomentumIndex) + { + // we only need to update if we are assembling the porous medium domain + // since the freeflow domain will not use the velocity from the context + if constexpr (domainI == porousMediumIndex) + { + auto& context = std::get<porousMediumIndex>(couplingContext_); + for (auto& c : context) + { + if (c.freeFlowMomentumDofIdx == dofIdxGlobalJ) + { + const auto& scvf = localAssemblerI.fvGeometry().scvf(c.porousMediumScvfIdx); + c.faceVelocity = faceVelocity(localAssemblerI.element(), scvf); + } + } + } + } + + // update volVars in the FreeFlowMomentumCouplingContext + else if (domainJ == porousMediumIndex) + { + auto& context = std::get<freeFlowMomentumIndex>(couplingContext_); + for (auto& c : context) + { + if (c.porousMediumDofIdx == dofIdxGlobalJ) + { + const auto& ggJ = c.fvGeometry.gridGeometry(); + const auto& scv = *scvs(c.fvGeometry).begin(); + const auto elemSol = elementSolution(c.element, this->curSol(domainJ), ggJ); + 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 == freeFlowMomentumIndex ? context.freeFlowMomentumScvfIdx : 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 + */ + const CouplingStencil& couplingStencil(Dune::index_constant<porousMediumIndex> domainI, + const Element<porousMediumIndex>& element, + Dune::index_constant<freeFlowMomentumIndex> domainJ) const + { + const auto eIdx = this->problem(domainI).gridGeometry().elementMapper().index(element); + return couplingMapper_.couplingStencil(domainI, eIdx, domainJ); + } + + /*! + * \brief returns an iterable container of all indices of degrees of freedom of domain j + * that couple with / influence the residual of the given sub-control volume of domain i + * + * \param domainI the domain index of domain i + * \param elementI the coupled element of domain à + * \param scvI the sub-control volume of domain i + * \param domainJ the domain index of domain j + */ + const CouplingStencil& couplingStencil(Dune::index_constant<freeFlowMomentumIndex> domainI, + const Element<freeFlowMomentumIndex>& elementI, + const SubControlVolume<freeFlowMomentumIndex>& scvI, + Dune::index_constant<porousMediumIndex> domainJ) const + { + return couplingMapper_.couplingStencil(domainI, elementI, scvI, domainJ); + } + + using ParentType::evalCouplingResidual; + + /*! + * \brief evaluate the coupling residual + * special interface for fcstaggered methods + */ + template<class LocalAssemblerI, std::size_t j> + decltype(auto) evalCouplingResidual(Dune::index_constant<freeFlowMomentumIndex> domainI, + const LocalAssemblerI& localAssemblerI, + const SubControlVolume<freeFlowMomentumIndex>& scvI, + Dune::index_constant<j> domainJ, + std::size_t dofIdxGlobalJ) const + { + return localAssemblerI.evalLocalResidual(); + } + + // \} + + /*! + * \brief Returns whether a given scvf is coupled to the other domain + */ + bool isCoupledLateralScvf(Dune::index_constant<freeFlowMomentumIndex> domainI, const SubControlVolumeFace<freeFlowMomentumIndex>& scvf) const + { return couplingMapper_.isCoupledLateralScvf(domainI, scvf); } + + /*! + * \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 + { + if constexpr (i == freeFlowMomentumIndex) + return couplingMapper_.isCoupled(domainI, scvf) || couplingMapper_.isCoupledLateralScvf(domainI, scvf); + else + return couplingMapper_.isCoupled(domainI, scvf); + } + + /*! + * \brief If the boundary entity is on a coupling boundary + * \param domainI the domain index for which to compute the flux + * \param scv the sub control volume + */ + bool isCoupled(Dune::index_constant<freeFlowMomentumIndex> domainI, + const SubControlVolume<freeFlowMomentumIndex>& scv) const + { return couplingMapper_.isCoupled(domainI, scv); } + + /*! + * \brief Returns the velocity at a given sub control volume face. + */ + auto faceVelocity(const Element<porousMediumIndex>& element, + const SubControlVolumeFace<porousMediumIndex>& scvf) const + { + // create a unit normal vector oriented in positive coordinate direction + auto velocity = scvf.unitOuterNormal(); + using std::abs; + std::for_each(velocity.begin(), velocity.end(), [](auto& v){ v = abs(v); }); + + // create the actual velocity vector + velocity *= this->curSol(freeFlowMomentumIndex)[ + couplingMapper_.outsideDofIndex(Dune::index_constant<porousMediumIndex>(), scvf) + ]; + + return velocity; + } + +private: + //! 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 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); } + + /*! + * \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 (couplingMapper_.isCoupled(domainI, scvf)) + { + if constexpr (domainI == freeFlowMomentumIndex) + { + const auto otherElementIdx = couplingMapper_.outsideElementIndex(domainI, scvf); + constexpr auto domainJ = Dune::index_constant<1-i>(); + 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), + scvf.index(), + couplingMapper_.flipScvfIndex(domainI, scvf), + otherElementIdx, + this->problem(domainJ).spatialParams().gravity(scvf.center()) + }); + } + + else if constexpr (domainI == porousMediumIndex) + { + const auto otherElementIdx = couplingMapper_.outsideElementIndex(domainI, scvf); + constexpr auto domainJ = Dune::index_constant<1-i>(); + const auto& otherGridGeometry = this->problem(domainJ).gridGeometry(); + const auto& otherElement = otherGridGeometry.element(otherElementIdx); + auto otherFvGeometry = localView(otherGridGeometry).bindElement(otherElement); + + const auto otherScvfIdx = couplingMapper_.flipScvfIndex(domainI, scvf); + const auto& otherScvf = otherFvGeometry.scvf(otherScvfIdx); + const auto& otherScv = otherFvGeometry.scv(otherScvf.insideScvIdx()); + + context.push_back({ + otherElement, + std::move(otherFvGeometry), + scvf.index(), + otherScvfIdx, + otherScv.dofIndex(), + faceVelocity(fvGeometry.element(), scvf) + }); + } + } + } + } + + mutable std::tuple<std::vector<FreeFlowMomentumCouplingContext>, std::vector<PorousMediumCouplingContext>> couplingContext_; + mutable std::array<std::size_t, 2> couplingContextBoundForElement_; + + CouplingMapper couplingMapper_; +}; + +} // end namespace Dumux + +#endif diff --git a/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmapper.hh b/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmapper_staggered_cctpfa.hh similarity index 98% rename from dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmapper.hh rename to dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmapper_staggered_cctpfa.hh index 6e02da1a72cc6ca5c1369166fb391284c085230a..56ecf1a5cec28b86d0822a2306af6d1dbc2e002c 100644 --- a/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmapper.hh +++ b/dumux/multidomain/boundary/freeflowporousmedium/ffmomentumpm/couplingmapper_staggered_cctpfa.hh @@ -7,11 +7,11 @@ /*! * \file * \ingroup FreeFlowPorousMediumCoupling - * \copydoc Dumux::FreeFlowMomentumPorousMediumCouplingMapper + * \copydoc Dumux::FFMomentumPMCouplingMapperStaggeredCCTpfa */ -#ifndef DUMUX_MULTIDOMAIN_FREEFLOWMOMENTUM_POROUSMEDIUM_COUPLINGMAPPER_HH -#define DUMUX_MULTIDOMAIN_FREEFLOWMOMENTUM_POROUSMEDIUM_COUPLINGMAPPER_HH +#ifndef DUMUX_MULTIDOMAIN_FREEFLOWMOMENTUM_POROUSMEDIUM_COUPLINGMAPPER_STAGGERED_TPFA_HH +#define DUMUX_MULTIDOMAIN_FREEFLOWMOMENTUM_POROUSMEDIUM_COUPLINGMAPPER_STAGGERED_TPFA_HH #include <iostream> #include <unordered_map> @@ -33,7 +33,7 @@ namespace Dumux { * \todo how to extend to arbitrary number of domains? */ template<class MDTraits, class CouplingManager> -class FreeFlowMomentumPorousMediumCouplingMapper +class FFMomentumPMCouplingMapperStaggeredCCTpfa { using Scalar = typename MDTraits::Scalar; @@ -80,7 +80,6 @@ public: */ void update(const CouplingManager& couplingManager) { - // TODO: Box and multiple domains static_assert(numSD == 2, "More than two subdomains not implemented!"); static_assert(isFcStaggered<0>() && isCCTpfa<1>(), "Only coupling between fcstaggered and cctpfa implemented!");