GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/multidomain/boundary/freeflowporenetwork/couplingconditions.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 46 54 85.2%
Functions: 4 6 66.7%
Branches: 25 56 44.6%

Line Branch Exec Source
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 //
4 // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 /*!
8 * \file
9 * \ingroup FreeFlowPoreNetworkCoupling
10 * \copydoc Dumux::FreeFlowPoreNetworkCouplingConditions
11 */
12
13 #ifndef DUMUX_MD_FREEFLOW_PORENETWORK_COUPLINGCONDITIONS_HH
14 #define DUMUX_MD_FREEFLOW_PORENETWORK_COUPLINGCONDITIONS_HH
15
16 #include <numeric>
17
18 #include <dune/common/exceptions.hh>
19
20 #include <dumux/common/properties.hh>
21 #include <dumux/common/math.hh>
22 #include <dumux/discretization/cellcentered/tpfa/computetransmissibility.hh>
23 #include <dumux/flux/fickslaw_fwd.hh>
24 #include <dumux/freeflow/navierstokes/momentum/velocitygradients.hh>
25 #include <dumux/multidomain/boundary/freeflowporousmedium/traits.hh>
26 #include <dumux/multidomain/boundary/freeflowporousmedium/indexhelper.hh>
27
28 namespace Dumux {
29
30 template<class MDTraits, class CouplingManager, bool enableEnergyBalance, bool isCompositional>
31 class FreeFlowPoreNetworkCouplingConditionsImplementation;
32
33 /*!
34 * \ingroup FreeFlowPoreNetworkCoupling
35 * \brief Coupling conditions for the coupling of a pore-network model
36 * with a (Navier-)Stokes model (staggerd grid).
37 */
38 template<class MDTraits, class CouplingManager>
39 using FreeFlowPoreNetworkCouplingConditions
40 = FreeFlowPoreNetworkCouplingConditionsImplementation<
41 MDTraits, CouplingManager,
42 GetPropType<typename MDTraits::template SubDomain<0>::TypeTag, Properties::ModelTraits>::enableEnergyBalance(),
43 (GetPropType<typename MDTraits::template SubDomain<0>::TypeTag, Properties::ModelTraits>::numFluidComponents() > 1)
44 >;
45
46 /*!
47 * \ingroup FreeFlowPoreNetworkCoupling
48 * \brief A base class which provides some common methods used for free-flow/pore-network coupling.
49 */
50 template<class MDTraits, class CouplingManager>
51 class FreeFlowPoreNetworkCouplingConditionsImplementationBase
52 {
53 using Scalar = typename MDTraits::Scalar;
54
55 template<std::size_t id> using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
56 template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
57 template<std::size_t id> using Element = typename GridGeometry<id>::GridView::template Codim<0>::Entity;
58 template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView;
59 template<std::size_t id> using SubControlVolumeFace = typename GridGeometry<id>::LocalView::SubControlVolumeFace;
60 template<std::size_t id> using SubControlVolume = typename GridGeometry<id>::LocalView::SubControlVolume;
61 template<std::size_t id> using Indices = typename GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>::Indices;
62 template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView;
63 template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables;
64 template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>;
65 template<std::size_t id> using FluidSystem = GetPropType<SubDomainTypeTag<id>, Properties::FluidSystem>;
66 template<std::size_t id> using ModelTraits = GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>;
67 template<std::size_t id> using GlobalPosition = typename Element<id>::Geometry::GlobalCoordinate;
68 template<std::size_t id> using NumEqVector = typename Problem<id>::Traits::NumEqVector;
69
70 using VelocityGradients = StaggeredVelocityGradients;
71
72 public:
73 static constexpr auto freeFlowMomentumIndex = CouplingManager::freeFlowMomentumIndex;
74 static constexpr auto freeFlowMassIndex = CouplingManager::freeFlowMassIndex;
75 static constexpr auto poreNetworkIndex = CouplingManager::poreNetworkIndex;
76 private:
77
78 using AdvectionType = GetPropType<SubDomainTypeTag<poreNetworkIndex>, Properties::AdvectionType>;
79
80 static constexpr bool adapterUsed = ModelTraits<poreNetworkIndex>::numFluidPhases() > 1;
81 using IndexHelper = FreeFlowPorousMediumCoupling::IndexHelper<freeFlowMassIndex, poreNetworkIndex, FluidSystem<freeFlowMassIndex>, adapterUsed>;
82
83 static constexpr int enableEnergyBalance = GetPropType<SubDomainTypeTag<freeFlowMassIndex>, Properties::ModelTraits>::enableEnergyBalance();
84 static_assert(GetPropType<SubDomainTypeTag<poreNetworkIndex>, Properties::ModelTraits>::enableEnergyBalance() == enableEnergyBalance,
85 "All submodels must both be either isothermal or non-isothermal");
86
87 static_assert(FreeFlowPorousMediumCoupling::IsSameFluidSystem<FluidSystem<freeFlowMassIndex>,
88 FluidSystem<poreNetworkIndex>>::value,
89 "All submodels must use the same fluid system");
90
91 using VelocityVector = GlobalPosition<freeFlowMomentumIndex>;
92
93 public:
94 /*!
95 * \brief Returns the corresponding phase index needed for coupling.
96 */
97 template<std::size_t i>
98 static constexpr auto couplingPhaseIdx(Dune::index_constant<i> id, int coupledPhaseIdx = 0)
99 { return IndexHelper::couplingPhaseIdx(id, coupledPhaseIdx); }
100
101 /*!
102 * \brief Returns the corresponding component index needed for coupling.
103 */
104 template<std::size_t i>
105 static constexpr auto couplingCompIdx(Dune::index_constant<i> id, int coupledCompIdx)
106 { return IndexHelper::couplingCompIdx(id, coupledCompIdx); }
107
108
109 /*!
110 * \brief Returns the momentum flux across the coupling boundary.
111 *
112 * For the normal momentum coupling, the porous medium side of the coupling condition
113 * is evaluated, i.e. -[p n]^pm.
114 *
115 */
116 template<class Context>
117 885 static NumEqVector<freeFlowMomentumIndex> momentumCouplingCondition(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry,
118 const SubControlVolumeFace<freeFlowMomentumIndex>& scvf,
119 const ElementVolumeVariables<freeFlowMomentumIndex>& elemVolVars,
120 const Context& context)
121 {
122 885 NumEqVector<freeFlowMomentumIndex> momentumFlux(0.0);
123 885 const auto pnmPhaseIdx = couplingPhaseIdx(poreNetworkIndex);
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 885 times.
885 const auto [pnmPressure, pnmViscosity] = [&]
125 {
126
1/2
✓ Branch 0 taken 1770 times.
✗ Branch 1 not taken.
2655 for (const auto& scv : scvs(context.fvGeometry))
127 {
128
2/2
✓ Branch 0 taken 885 times.
✓ Branch 1 taken 885 times.
1770 if (scv.dofIndex() == context.poreNetworkDofIdx)
129 5310 return std::make_pair(context.elemVolVars[scv].pressure(pnmPhaseIdx), context.elemVolVars[scv].viscosity(pnmPhaseIdx));
130 }
131 DUNE_THROW(Dune::InvalidStateException, "No coupled scv found");
132
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 885 times.
885 }();
133
134 // set p_freeflow = p_PNM
135 885 momentumFlux[scvf.normalAxis()] = pnmPressure;
136
137 // normalize pressure
138 885 momentumFlux[scvf.normalAxis()] -= elemVolVars.gridVolVars().problem().referencePressure(fvGeometry.element(), fvGeometry, scvf);
139
140 // Explicitly account for dv_i/dx_i, which is NOT part of the actual coupling condition. We do it here for convenience so
141 // we do not forget to set it in the problem. We assume that the velocity gradient at the boundary towards the interface is the same
142 // as the one in the center of the element. TODO check sign
143
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 885 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 885 times.
1770 const auto& scv = fvGeometry.scv(scvf.insideScvIdx());
144 885 const auto& frontalInternalScvf = (*scvfs(fvGeometry, scv).begin());
145 885 momentumFlux[scvf.normalAxis()] -= 2*VelocityGradients::velocityGradII(fvGeometry, frontalInternalScvf, elemVolVars) * pnmViscosity;
146
147 // We do NOT consider the inertia term here. If included, Newton convergence decreases drastically and the solution even does not converge to a reference solution.
148 // We furthermore assume creeping flow within the boundary layer thus neglecting this term is physically justified.
149
150 885 momentumFlux[scvf.normalAxis()] *= scvf.directionSign();
151
152 885 return momentumFlux;
153 }
154
155 template<class Context>
156 3606 static VelocityVector interfaceThroatVelocity(const FVElementGeometry<freeFlowMomentumIndex>& fvGeometry,
157 const SubControlVolumeFace<freeFlowMomentumIndex>& scvf,
158 const Context& context)
159 {
160
1/2
✓ Branch 0 taken 3606 times.
✗ Branch 1 not taken.
3606 const auto& pnmElement = context.fvGeometry.element();
161 3606 const auto& pnmFVGeometry = context.fvGeometry;
162
1/2
✓ Branch 0 taken 3606 times.
✗ Branch 1 not taken.
3606 const auto& pnmScvf = pnmFVGeometry.scvf(0);
163 3606 const auto& pnmElemVolVars = context.elemVolVars;
164 3606 const auto& pnmElemFluxVarsCache = context.elemFluxVarsCache;
165 3606 const auto& pnmProblem = pnmElemVolVars.gridVolVars().problem();
166
167 3606 const auto pnmPhaseIdx = couplingPhaseIdx(poreNetworkIndex);
168
1/2
✓ Branch 0 taken 3606 times.
✗ Branch 1 not taken.
3606 const Scalar area = pnmElemFluxVarsCache[pnmScvf].throatCrossSectionalArea(pnmPhaseIdx);
169
170 // only proceed if area > 0 in order to prevent division by zero (e.g., when the throat was not invaded yet)
171
1/2
✓ Branch 0 taken 3606 times.
✗ Branch 1 not taken.
3606 if (area > 0.0)
172 {
173 using PNMFluxVariables = GetPropType<SubDomainTypeTag<poreNetworkIndex>, Properties::FluxVariables>;
174 3606 PNMFluxVariables fluxVars;
175 3606 fluxVars.init(pnmProblem, pnmElement, pnmFVGeometry, pnmElemVolVars, pnmScvf, pnmElemFluxVarsCache);
176
177 18030 const Scalar flux = fluxVars.advectiveFlux(pnmPhaseIdx, [pnmPhaseIdx](const auto& volVars){ return volVars.mobility(pnmPhaseIdx);});
178
179 // account for the orientation of the bulk face.
180 3606 VelocityVector velocity = (pnmElement.geometry().corner(1) - pnmElement.geometry().corner(0));
181 7212 velocity /= velocity.two_norm();
182 3606 velocity *= flux / area;
183
184 // TODO: Multiple throats connected to the same pore?
185 3606 return velocity;
186 }
187 else
188 return VelocityVector(0.0);
189 }
190
191 /*!
192 * \brief Evaluate an advective flux across the interface and consider upwinding.
193 */
194 static Scalar advectiveFlux(const Scalar insideQuantity, const Scalar outsideQuantity, const Scalar volumeFlow, bool insideIsUpstream)
195 {
196 648 const Scalar upwindWeight = 1.0; //TODO use Flux.UpwindWeight or something like Coupling.UpwindWeight?
197
198 648 if(insideIsUpstream)
199 344 return (upwindWeight * insideQuantity + (1.0 - upwindWeight) * outsideQuantity) * volumeFlow;
200 else
201 304 return (upwindWeight * outsideQuantity + (1.0 - upwindWeight) * insideQuantity) * volumeFlow;
202 }
203
204 protected:
205
206 /*!
207 * \brief Returns the distance between an scvf and the corresponding scv center.
208 */
209 template<class Scv, class Scvf>
210 Scalar getDistance_(const Scv& scv, const Scvf& scvf) const
211 {
212 return (scv.dofPosition() - scvf.ipGlobal()).two_norm();
213 }
214 };
215
216 /*!
217 * \ingroup FreeFlowPoreNetworkCoupling
218 * \brief Coupling conditions specialization for non-compositional models.
219 */
220 template<class MDTraits, class CouplingManager, bool enableEnergyBalance>
221 class FreeFlowPoreNetworkCouplingConditionsImplementation<MDTraits, CouplingManager, enableEnergyBalance, false>
222 : public FreeFlowPoreNetworkCouplingConditionsImplementationBase<MDTraits, CouplingManager>
223 {
224 using ParentType = FreeFlowPoreNetworkCouplingConditionsImplementationBase<MDTraits, CouplingManager>;
225 using Scalar = typename MDTraits::Scalar;
226
227 // the sub domain type tags
228 template<std::size_t id>
229 using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
230
231 template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
232 template<std::size_t id> using Element = typename GridGeometry<id>::GridView::template Codim<0>::Entity;
233 template<std::size_t id> using FVElementGeometry = typename GridGeometry<id>::LocalView;
234 template<std::size_t id> using SubControlVolumeFace = typename GridGeometry<id>::LocalView::SubControlVolumeFace;
235 template<std::size_t id> using SubControlVolume = typename GridGeometry<id>::LocalView::SubControlVolume;
236 template<std::size_t id> using Indices = typename GetPropType<SubDomainTypeTag<id>, Properties::ModelTraits>::Indices;
237 template<std::size_t id> using ElementVolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::LocalView;
238 template<std::size_t id> using VolumeVariables = typename GetPropType<SubDomainTypeTag<id>, Properties::GridVolumeVariables>::VolumeVariables;
239
240 static_assert(GetPropType<SubDomainTypeTag<ParentType::poreNetworkIndex>, Properties::ModelTraits>::numFluidComponents() == GetPropType<SubDomainTypeTag<ParentType::poreNetworkIndex>, Properties::ModelTraits>::numFluidPhases(),
241 "Pore-network model must not be compositional");
242
243 public:
244 using ParentType::ParentType;
245 using ParentType::couplingPhaseIdx;
246
247 /*!
248 * \brief Returns the mass flux across the coupling boundary as seen from the pore-network domain.
249 */
250 template<class CouplingContext>
251 168 static Scalar massCouplingCondition(Dune::index_constant<ParentType::poreNetworkIndex> domainI,
252 Dune::index_constant<ParentType::freeFlowMassIndex> domainJ,
253 const FVElementGeometry<ParentType::poreNetworkIndex>& fvGeometry,
254 const SubControlVolume<ParentType::poreNetworkIndex>& scv,
255 const ElementVolumeVariables<ParentType::poreNetworkIndex>& insideVolVars,
256 const CouplingContext& context)
257 {
258 168 Scalar massFlux(0.0);
259
260
4/4
✓ Branch 0 taken 648 times.
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 648 times.
✓ Branch 3 taken 168 times.
1800 for (const auto& c : context)
261 {
262 // positive values indicate flux into pore-network region
263 648 const Scalar normalFFVelocity = c.velocity * c.scvf.unitOuterNormal();
264
2/2
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 304 times.
648 const bool pnmIsUpstream = std::signbit(normalFFVelocity);
265
266
4/4
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 304 times.
✓ Branch 2 taken 344 times.
✓ Branch 3 taken 304 times.
1296 const Scalar pnmDensity = insideVolVars[scv].density(couplingPhaseIdx(ParentType::poreNetworkIndex));
267
2/2
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 304 times.
648 const Scalar ffDensity = c.volVars.density(couplingPhaseIdx(ParentType::freeFlowMassIndex));
268 648 const Scalar area = c.scvf.area() * c.volVars.extrusionFactor();
269
270 // flux is used as source term: positive values mean influx
271
2/2
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 304 times.
1296 massFlux += ParentType::advectiveFlux(pnmDensity, ffDensity, normalFFVelocity*area, pnmIsUpstream);
272 }
273
274 168 return massFlux;
275 }
276
277 /*!
278 * \brief Returns the mass flux across the coupling boundary as seen from the free-flow domain.
279 */
280 template<class CouplingContext>
281 static Scalar massCouplingCondition(Dune::index_constant<ParentType::freeFlowMassIndex> domainI,
282 Dune::index_constant<ParentType::poreNetworkIndex> domainJ,
283 const FVElementGeometry<ParentType::freeFlowMassIndex>& fvGeometry,
284 const SubControlVolumeFace<ParentType::freeFlowMassIndex>& scvf,
285 const ElementVolumeVariables<ParentType::freeFlowMassIndex>& insideVolVars,
286 const CouplingContext& context)
287 {
288 // positive values indicate flux into pore-network region
289 const Scalar normalFFVelocity = context.velocity * scvf.unitOuterNormal();
290 const bool ffIsUpstream = !std::signbit(normalFFVelocity);
291
292 const Scalar ffDensity = insideVolVars[scvf.insideScvIdx()].density(couplingPhaseIdx(ParentType::freeFlowMassIndex));
293 const Scalar pnmDensity = context.volVars.density(couplingPhaseIdx(ParentType::poreNetworkIndex));
294
295 // flux is used in Neumann condition: positive values mean flux out of free-flow domain
296 return ParentType::advectiveFlux(ffDensity, pnmDensity, normalFFVelocity, ffIsUpstream);
297 }
298
299 private:
300
301 };
302
303 } // end namespace Dumux
304
305 #endif
306