GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/flux/porenetwork/advection.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 74 85 87.1%
Functions: 21 36 58.3%
Branches: 89 114 78.1%

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 PoreNetworkFlux
10 * \brief This file contains the data which is required to calculate
11 * the fluxes of the pore network model over a face of a finite volume.
12 */
13 #ifndef DUMUX_FLUX_PNM_ADVECTION_HH
14 #define DUMUX_FLUX_PNM_ADVECTION_HH
15
16 #include <array>
17 #include <dumux/common/parameters.hh>
18
19 namespace Dumux::PoreNetwork::Detail {
20
21 template<class... TransmissibilityLawTypes>
22 struct Transmissibility : public TransmissibilityLawTypes... {};
23
24 } // end namespace Dumux::PoreNetwork::Detail
25
26 namespace Dumux::PoreNetwork {
27
28 /*!
29 * \ingroup PoreNetworkFlux
30 * \brief Hagen–Poiseuille-type flux law to describe the advective flux for pore-network models.
31 */
32 template<class ScalarT, class... TransmissibilityLawTypes>
33 class CreepingFlow
34 {
35
36 public:
37 //! Export the Scalar type
38 using Scalar = ScalarT;
39
40 //! Export the transmissibility law
41 using Transmissibility = Detail::Transmissibility<TransmissibilityLawTypes...>;
42
43 /*!
44 * \brief Returns the advective flux of a fluid phase
45 * across the given sub-control volume face (corresponding to a pore throat).
46 * \note The flux is given in N*m, and can be converted
47 * into a volume flux (m^3/s) or mass flux (kg/s) by applying an upwind scheme
48 * for the mobility (1/viscosity) or the product of density and mobility, respectively.
49 */
50 template<class Problem, class Element, class FVElementGeometry,
51 class ElementVolumeVariables, class SubControlVolumeFace, class ElemFluxVarsCache>
52 35151003 static Scalar flux(const Problem& problem,
53 const Element& element,
54 const FVElementGeometry& fvGeometry,
55 const ElementVolumeVariables& elemVolVars,
56 const SubControlVolumeFace& scvf,
57 const int phaseIdx,
58 const ElemFluxVarsCache& elemFluxVarsCache)
59 {
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
35151003 const auto& fluxVarsCache = elemFluxVarsCache[scvf];
61
62 // Get the inside and outside volume variables
63
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35151003 times.
70302006 const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx());
64
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35151003 times.
70302006 const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx());
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
35151003 const auto& insideVolVars = elemVolVars[insideScv];
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
35151003 const auto& outsideVolVars = elemVolVars[outsideScv];
67
68 // calculate the pressure difference
69
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35151003 times.
70302006 const Scalar deltaP = insideVolVars.pressure(phaseIdx) - outsideVolVars.pressure(phaseIdx);
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1641413 times.
35151003 const Scalar transmissibility = fluxVarsCache.transmissibility(phaseIdx);
71 using std::isfinite;
72
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 35151003 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35151003 times.
70302006 assert(isfinite(transmissibility));
73
74 35151003 Scalar volumeFlow = transmissibility*deltaP;
75
76 // add gravity term
77
5/8
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 35150989 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 14 times.
✗ Branch 10 not taken.
35151003 static const bool enableGravity = getParamFromGroup<bool>(problem.paramGroup(), "Problem.EnableGravity");
78
2/2
✓ Branch 0 taken 1872 times.
✓ Branch 1 taken 35149131 times.
35151003 if (enableGravity)
79 {
80 3744 const Scalar rho = 0.5*insideVolVars.density(phaseIdx) + 0.5*outsideVolVars.density(phaseIdx);
81 9360 const Scalar g = problem.spatialParams().gravity(scvf.center()) * scvf.unitOuterNormal();
82
83 // The transmissibility is with respect to the effective throat length (potentially dropping the pore body radii).
84 // For gravity, we need to consider the total throat length (i.e., the cell-center to cell-center distance).
85 // This might cause some inconsistencies TODO: is there a better way?
86 1872 volumeFlow += transmissibility * fluxVarsCache.poreToPoreDistance() * rho * g;
87 }
88
89 35151003 return volumeFlow;
90 }
91
92 /*!
93 * \brief Returns the throat conductivity
94 */
95 template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables, class FluxVariablesCache>
96 1919640 static Scalar calculateTransmissibility(const Problem& problem,
97 const Element& element,
98 const FVElementGeometry& fvGeometry,
99 const typename FVElementGeometry::SubControlVolumeFace& scvf,
100 const ElementVolumeVariables& elemVolVars,
101 const FluxVariablesCache& fluxVarsCache,
102 const int phaseIdx)
103 {
104 if constexpr (ElementVolumeVariables::VolumeVariables::numFluidPhases() == 1)
105 37204427 return Transmissibility::singlePhaseTransmissibility(problem, element, fvGeometry, scvf, elemVolVars, fluxVarsCache, phaseIdx);
106 else
107 {
108 static_assert(ElementVolumeVariables::VolumeVariables::numFluidPhases() == 2);
109
110 1919640 const auto& spatialParams = problem.spatialParams();
111 using FluidSystem = typename ElementVolumeVariables::VolumeVariables::FluidSystem;
112 1919640 const int wPhaseIdx = spatialParams.template wettingPhase<FluidSystem>(element, elemVolVars);
113 1919640 const bool invaded = fluxVarsCache.invaded();
114
115
2/2
✓ Branch 0 taken 959820 times.
✓ Branch 1 taken 959820 times.
1919640 if (phaseIdx == wPhaseIdx)
116 {
117
2/2
✓ Branch 0 taken 92457 times.
✓ Branch 1 taken 867363 times.
959820 return invaded ? Transmissibility::wettingLayerTransmissibility(element, fvGeometry, scvf, fluxVarsCache)
118 867363 : Transmissibility::singlePhaseTransmissibility(problem, element, fvGeometry, scvf, elemVolVars, fluxVarsCache, phaseIdx);
119 }
120 else // non-wetting phase
121 {
122
2/2
✓ Branch 0 taken 92457 times.
✓ Branch 1 taken 867363 times.
959820 return invaded ? Transmissibility::nonWettingPhaseTransmissibility(element, fvGeometry, scvf, fluxVarsCache)
123 : 0.0;
124 }
125 }
126 }
127
128 template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables, class FluxVariablesCache>
129 static std::array<Scalar, 2> calculateTransmissibilities(const Problem& problem,
130 const Element& element,
131 const FVElementGeometry& fvGeometry,
132 const ElementVolumeVariables& elemVolVars,
133 const typename FVElementGeometry::SubControlVolumeFace& scvf,
134 const FluxVariablesCache& fluxVarsCache)
135 {
136 static_assert(ElementVolumeVariables::VolumeVariables::numFluidPhases() == 1);
137 605 const Scalar t = calculateTransmissibility(problem, element, fvGeometry, scvf, elemVolVars, fluxVarsCache, 0);
138
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 603 times.
605 return {t, -t};
139 }
140 };
141
142 /*!
143 * \ingroup PoreNetworkFlux
144 * \brief Non-creeping flow flux law to describe the advective flux for pore-network models based on
145 * El-Zehairy et al.(2019).
146 * https://doi.org/10.1016/j.advwatres.2019.103378
147 */
148 template <class ScalarT, class... TransmissibilityLawTypes>
149 class NonCreepingFlow
150 {
151 public:
152 //! Export the Scalar type
153 using Scalar = ScalarT;
154
155 //! Export the creeping flow transmissibility law
156 using TransmissibilityCreepingFlow = Detail::Transmissibility<TransmissibilityLawTypes...>;
157
158 //! Inherit transmissibility from creeping flow transmissibility law to cache non-creeping flow-related parameters
159 class Transmissibility: public TransmissibilityCreepingFlow
160 {
161 public:
162 class SinglePhaseCache : public TransmissibilityCreepingFlow::SinglePhaseCache
163 {
164 using ParentType = typename TransmissibilityCreepingFlow::SinglePhaseCache;
165 public:
166 using Scalar = ScalarT;
167
168 template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables, class FluxVariablesCache>
169 2554623 void fill(const Problem& problem,
170 const Element& element,
171 const FVElementGeometry& fvGeometry,
172 const typename FVElementGeometry::SubControlVolumeFace& scvf,
173 const ElementVolumeVariables& elemVolVars,
174 const FluxVariablesCache& fluxVarsCache,
175 const int phaseIdx)
176 {
177 2554623 ParentType::fill(problem, element, fvGeometry,scvf, elemVolVars, fluxVarsCache, phaseIdx);
178 2554623 const auto elemSol = elementSolution(element, elemVolVars, fvGeometry);
179
180
2/2
✓ Branch 0 taken 5109246 times.
✓ Branch 1 taken 2554623 times.
7663869 for (const auto& scv : scvs(fvGeometry))
181 {
182 5109246 const auto localIdx = scv.indexInElement();
183 10218492 const Scalar throatToPoreAreaRatio = fluxVarsCache.throatCrossSectionalArea() / problem.spatialParams().poreCrossSectionalArea(element, scv, elemSol);
184
185 // dimensionless momentum coefficient
186 5109246 const Scalar momentumCoefficient = problem.spatialParams().momentumCoefficient(element, scv, elemSol);
187
188 // dimensionless kinetik-energy coefficient
189 5109246 const Scalar kineticEnergyCoefficient = problem.spatialParams().kineticEnergyCoefficient(element, scv, elemSol);
190
191 15327738 expansionCoefficient_[localIdx] = getExpansionCoefficient_(throatToPoreAreaRatio, momentumCoefficient, kineticEnergyCoefficient);
192 5109246 contractionCoefficient_[localIdx] = getContractionCoefficient_(throatToPoreAreaRatio, momentumCoefficient, kineticEnergyCoefficient);
193 }
194 2554623 }
195
196 Scalar expansionCoefficient(int downstreamIdx) const
197 5109246 { return expansionCoefficient_[downstreamIdx]; }
198
199 Scalar contractionCoefficient(int upstreamIdx) const
200 5109246 { return contractionCoefficient_[upstreamIdx]; }
201
202 private:
203
204 Scalar getExpansionCoefficient_(const Scalar throatToPoreAreaRatio, const Scalar momentumCoefficient, const Scalar kineticEnergyCoefficient) const
205 {
206 10218492 Scalar expansionCoefficient = throatToPoreAreaRatio * throatToPoreAreaRatio * (2 * momentumCoefficient - kineticEnergyCoefficient)
207 5109246 + kineticEnergyCoefficient - 2 * momentumCoefficient * throatToPoreAreaRatio
208 5109246 - (1 - throatToPoreAreaRatio * throatToPoreAreaRatio);
209
210 return expansionCoefficient;
211 }
212
213 Scalar getContractionCoefficient_(const Scalar throatToPoreAreaRatio, const Scalar momentumCoefficient, const Scalar kineticEnergyCoefficient) const
214 {
215 10218492 const Scalar contractionAreaRatio = getContractionAreaRatio_(throatToPoreAreaRatio);
216 15327738 Scalar contractionCoefficient = (1 - (throatToPoreAreaRatio * throatToPoreAreaRatio * kineticEnergyCoefficient - 2 * momentumCoefficient /*+1-throatToPoreAreaRatio*throatToPoreAreaRatio*/)
217 10218492 * contractionAreaRatio * contractionAreaRatio - 2 * contractionAreaRatio) / (contractionAreaRatio * contractionAreaRatio);
218
219 return contractionCoefficient;
220 }
221
222 Scalar getContractionAreaRatio_(const Scalar throatToPoreAreaRatio) const
223 {
224 5109246 return 1.0-(1.0-throatToPoreAreaRatio)/(2.08*(1.0-throatToPoreAreaRatio)+0.5371);
225 }
226
227 std::array<Scalar, 2> expansionCoefficient_;
228 std::array<Scalar, 2> contractionCoefficient_;
229 };
230 };
231
232 /*!
233 * \brief Returns the advective flux of a fluid phase
234 * across the given sub-control volume face (corresponding to a pore throat).
235 * \note The flux is given in N*m, and can be converted
236 * into a volume flux (m^3/s) or mass flux (kg/s) by applying an upwind scheme
237 * for the mobility (1/viscosity) or the product of density and mobility, respectively.
238 */
239 template<class Problem, class Element, class FVElementGeometry,
240 class ElementVolumeVariables, class SubControlVolumeFace, class ElemFluxVarsCache>
241 2554623 static Scalar flux(const Problem& problem,
242 const Element& element,
243 const FVElementGeometry& fvGeometry,
244 const ElementVolumeVariables& elemVolVars,
245 const SubControlVolumeFace& scvf,
246 const int phaseIdx,
247 const ElemFluxVarsCache& elemFluxVarsCache)
248 {
249
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
2554623 const auto& fluxVarsCache = elemFluxVarsCache[scvf];
250
251 // Get the inside and outside volume variables
252
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2554620 times.
5109246 const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx());
253
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2554620 times.
5109246 const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx());
254
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
2554623 const auto& insideVolVars = elemVolVars[insideScv];
255
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
2554623 const auto& outsideVolVars = elemVolVars[outsideScv];
256
257 // calculate the pressure difference
258
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2554620 times.
5109246 Scalar deltaP = insideVolVars.pressure(phaseIdx) - outsideVolVars.pressure(phaseIdx);
259
260 // add gravity term
261
5/8
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2554620 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
2554623 static const bool enableGravity = getParamFromGroup<bool>(problem.paramGroup(), "Problem.EnableGravity");
262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2554623 times.
2554623 if (enableGravity)
263 {
264 const Scalar rho = 0.5*insideVolVars.density(phaseIdx) + 0.5*outsideVolVars.density(phaseIdx);
265
266 // projected distance between pores in gravity field direction
267 const auto poreToPoreVector = fluxVarsCache.poreToPoreDistance() * scvf.unitOuterNormal();
268 const auto& gravityVector = problem.spatialParams().gravity(scvf.center());
269
270 deltaP += rho * (gravityVector * poreToPoreVector);
271 }
272 2554623 const Scalar creepingFlowTransmissibility = fluxVarsCache.transmissibility(phaseIdx);
273 2554623 const Scalar throatCrossSectionalArea = fluxVarsCache.throatCrossSectionalArea();
274 using std::isfinite;
275
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2554623 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2554623 times.
5109246 assert(isfinite(creepingFlowTransmissibility));
276
277
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2554623 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2554623 times.
5109246 assert(scvf.insideScvIdx() == 0);
278
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2554623 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2554623 times.
5109246 assert(scvf.outsideScvIdx() == 1);
279
280 // determine the flow direction to predict contraction and expansion
281
6/6
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
✓ Branch 2 taken 955632 times.
✓ Branch 3 taken 1598991 times.
✓ Branch 4 taken 955632 times.
✓ Branch 5 taken 1598991 times.
2554623 const auto [upstreamIdx, downstreamIdx] = deltaP > 0 ? std::pair(0, 1) : std::pair(1, 0);
282
283
4/4
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
✓ Branch 2 taken 955632 times.
✓ Branch 3 taken 1598991 times.
5109246 const Scalar contractionCoefficient = fluxVarsCache.singlePhaseFlowVariables().contractionCoefficient(upstreamIdx);
284
4/4
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
✓ Branch 2 taken 955632 times.
✓ Branch 3 taken 1598991 times.
5109246 const Scalar expansionCoefficient = fluxVarsCache.singlePhaseFlowVariables().expansionCoefficient(downstreamIdx);
285
4/4
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
✓ Branch 2 taken 955632 times.
✓ Branch 3 taken 1598991 times.
5109246 const Scalar mu = elemVolVars[upstreamIdx].viscosity();
286
287 //! El-Zehairy et al.(2019): Eq.(14):
288 //! A0Q^2 + B0Q + C0 = 0 where Q is volumetric flowrate, A0 = [Kc + Ke] * rho /[2aij^2], Kc and Ke are contraction and expansion coefficient respectively,
289 //! aij is cross sectional area of the throat, B0 = 1/K0 (K0 is trnasmissibility used in paper) and C0 = -deltaP
290 //! In our implementation viscosity of fluid will be included using upwinding later. Thus, creepingFlowTransmissibility = mu * K0 and
291 //! the volumetric flowrate calculated here q = mu * Q. Substitution of new variables into El-Zehairy et al.(2019), Eq.(14) gives:
292 //! Aq^2 + Bq + C = 0 where
293 //! A = A0 / mu^2, B = B0/mu = 1/creepingFlowTransmissibility and C = C0
294 //! attention: the q, volumetric flowrate, calculated here is always positive and its sign needs to be determined based on flow direction
295 //! this approach is taken to prevent the term under the square root (discriminant) becoming negative
296
8/8
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
✓ Branch 2 taken 955632 times.
✓ Branch 3 taken 1598991 times.
✓ Branch 4 taken 955632 times.
✓ Branch 5 taken 1598991 times.
✓ Branch 6 taken 955632 times.
✓ Branch 7 taken 1598991 times.
10218492 const Scalar A = (contractionCoefficient * elemVolVars[upstreamIdx].density() + expansionCoefficient * elemVolVars[downstreamIdx].density())
297 2554623 / (2.0 * mu * mu * throatCrossSectionalArea * throatCrossSectionalArea);
298 2554623 const Scalar B = 1.0/creepingFlowTransmissibility;
299 using std::abs;
300
2/2
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
2554623 const Scalar C = -abs(deltaP);
301
302 using std::sqrt;
303 2554623 const auto discriminant = B*B - 4*A*C;
304 2554623 const auto q = (-B + sqrt(discriminant)) / (2*A);
305
306 //! give the volume flowrate proper sign based on flow direction.
307
2/2
✓ Branch 0 taken 955632 times.
✓ Branch 1 taken 1598991 times.
2554623 if (upstreamIdx == 0)
308 return q;
309 else
310 955632 return -q;
311
312 }
313
314 template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables, class FluxVariablesCache>
315 static Scalar calculateTransmissibility(const Problem& problem,
316 const Element& element,
317 const FVElementGeometry& fvGeometry,
318 const typename FVElementGeometry::SubControlVolumeFace& scvf,
319 const ElementVolumeVariables& elemVolVars,
320 const FluxVariablesCache& fluxVarsCache,
321 const int phaseIdx)
322 {
323 static_assert(ElementVolumeVariables::VolumeVariables::numFluidPhases() == 1);
324 2554623 return Transmissibility::singlePhaseTransmissibility(problem, element, fvGeometry, scvf, elemVolVars, fluxVarsCache, phaseIdx);
325 }
326
327 template<class Problem, class Element, class FVElementGeometry, class ElementVolumeVariables, class FluxVariablesCache>
328 static std::array<Scalar, 2> calculateTransmissibilities(const Problem& problem,
329 const Element& element,
330 const FVElementGeometry& fvGeometry,
331 const ElementVolumeVariables& elemVolVars,
332 const typename FVElementGeometry::SubControlVolumeFace& scvf,
333 const FluxVariablesCache& fluxVarsCache)
334 {
335 static_assert(ElementVolumeVariables::VolumeVariables::numFluidPhases() == 1);
336 const Scalar t = calculateTransmissibility(problem, element, fvGeometry, scvf, elemVolVars, fluxVarsCache, 0);
337 return {t, -t};
338 }
339
340 };
341
342 } // end namespace Dumux
343
344 #endif
345