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 | * \brief This file contains the data which is required to calculate | ||
10 | * diffusive heat fluxes with Fourier's law. | ||
11 | */ | ||
12 | #ifndef DUMUX_TEST_MULTIDOMAIN_DUALNETWORK_FLUID_FOURIERS_LAW_HH | ||
13 | #define DUMUX_TEST_MULTIDOMAIN_DUALNETWORK_FLUID_FOURIERS_LAW_HH | ||
14 | |||
15 | #include <dumux/common/math.hh> | ||
16 | #include <dumux/common/parameters.hh> | ||
17 | |||
18 | namespace Dumux::PoreNetwork { | ||
19 | |||
20 | /*! | ||
21 | * \ingroup DualNetworkCoupling | ||
22 | * \brief Implements a Fourier's law assuming pyramid frustum shapes for the geometry from pore bodies to pore throats. | ||
23 | */ | ||
24 | template<bool isFluid> | ||
25 | struct FluidOrGrainPyramidFouriersLaw | ||
26 | { | ||
27 | template<class Problem, class Element, class FVElementGeometry, | ||
28 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
29 | static auto flux(const Problem& problem, | ||
30 | const Element& element, | ||
31 | const FVElementGeometry& fvGeometry, | ||
32 | const ElementVolumeVariables& elemVolVars, | ||
33 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
34 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
35 | { | ||
36 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | ||
37 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | ||
38 | const auto& insideVolVars = elemVolVars[insideScv]; | ||
39 | const auto& outsideVolVars = elemVolVars[outsideScv]; | ||
40 | const auto deltaT = insideVolVars.temperature() - outsideVolVars.temperature(); | ||
41 | return transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache) * deltaT; | ||
42 | } | ||
43 | |||
44 | template<class Problem, class Element, class FVElementGeometry, | ||
45 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
46 | ✗ | static auto transmissibility(const Problem& problem, | |
47 | const Element& element, | ||
48 | const FVElementGeometry& fvGeometry, | ||
49 | const ElementVolumeVariables& elemVolVars, | ||
50 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
51 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
52 | { | ||
53 | using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type; | ||
54 | ✗ | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | |
55 | ✗ | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | |
56 | ✗ | const auto& insideVolVars = elemVolVars[insideScv]; | |
57 | ✗ | const auto& outsideVolVars = elemVolVars[outsideScv]; | |
58 | ✗ | const auto& fluxVarsCache = elemFluxVarsCache[scvf]; | |
59 | |||
60 | ✗ | const auto getConductivity = [&](const auto& volVars) | |
61 | { | ||
62 | if constexpr (isFluid) | ||
63 | ✗ | return volVars.fluidThermalConductivity(0); | |
64 | else | ||
65 | ✗ | return volVars.solidThermalConductivity(); | |
66 | }; | ||
67 | |||
68 | ✗ | const auto insideThermalConducitivity = getConductivity(insideVolVars); | |
69 | ✗ | const auto outsideThermalConducitivity = getConductivity(outsideVolVars); | |
70 | |||
71 | ✗ | const auto eIdx = fvGeometry.gridGeometry().elementMapper().index(element); | |
72 | ✗ | const auto throatCenter = problem.spatialParams().throatCenter(eIdx); | |
73 | ✗ | const auto distanceInside = (insideScv.dofPosition() - throatCenter).two_norm(); | |
74 | ✗ | const auto distanceOutside = (outsideScv.dofPosition() - throatCenter).two_norm(); | |
75 | |||
76 | ✗ | const auto throatArea = [&]() | |
77 | { | ||
78 | ✗ | static const Scalar givenArea = getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.ThroatArea", 0.0); | |
79 | ✗ | if (givenArea > 0.0) | |
80 | return givenArea; | ||
81 | |||
82 | if constexpr (isFluid) | ||
83 | ✗ | return fluxVarsCache.throatCrossSectionalArea(0); | |
84 | else | ||
85 | ✗ | return fluxVarsCache.grainContactArea(); | |
86 | ✗ | }(); | |
87 | |||
88 | |||
89 | ✗ | const Scalar pyramidFrustumTopArea = [&]() | |
90 | { | ||
91 | ✗ | static const bool realArea = getParamFromGroup<bool>(problem.paramGroup(), "FouriersLaw.UseRealThroatAreaInPyramid", true); | |
92 | ✗ | if (realArea) | |
93 | ✗ | return throatArea; | |
94 | else | ||
95 | { | ||
96 | // use inscribed throat diameter as square side length | ||
97 | ✗ | const Scalar baseLength = fluxVarsCache.throatInscribedRadius() * 2.0; | |
98 | ✗ | return baseLength*baseLength; | |
99 | } | ||
100 | ✗ | }(); | |
101 | |||
102 | ✗ | const auto getPyramidFrustumBaseArea = [&](const auto& volVars, const Scalar distance) | |
103 | { | ||
104 | ✗ | static const Scalar givenArea = getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.PoreArea", 0.0); | |
105 | ✗ | if (givenArea > 0.0) | |
106 | return givenArea; | ||
107 | |||
108 | ✗ | static const bool realVolume = getParamFromGroup<bool>(problem.paramGroup(), "FouriersLaw.UseVolumeEqualPyramid", true); | |
109 | ✗ | if (realVolume) | |
110 | { | ||
111 | // Using the formula for the volume of a pyramid frustum to calculate its base length and base area: | ||
112 | // v = 1/3 h * (a^2 + a*b + b^2), where a is the base side length, b the top side length, | ||
113 | // h the height and v the volume of the frustum . | ||
114 | using std::sqrt; | ||
115 | ✗ | const Scalar vol = 0.5 * volVars.poreVolume(); | |
116 | ✗ | const Scalar baseLenTop = sqrt(pyramidFrustumTopArea); | |
117 | ✗ | const Scalar height = distance; | |
118 | // see https://en.wikipedia.org/wiki/Moscow_Mathematical_Papyrus | ||
119 | ✗ | const Scalar baseLenBot = 0.5*sqrt(3.0) * sqrt(-(baseLenTop*baseLenTop*height-4.0*vol)/height) -0.5*baseLenTop; | |
120 | ✗ | return baseLenBot*baseLenBot; | |
121 | } | ||
122 | else | ||
123 | ✗ | return volVars.poreVolume() / (2.0*distance); | |
124 | }; | ||
125 | |||
126 | using std::sqrt; | ||
127 | ✗ | const auto baseAreaInside = getPyramidFrustumBaseArea(insideVolVars, distanceInside); | |
128 | ✗ | const auto baseAreaOutside = getPyramidFrustumBaseArea(outsideVolVars, distanceOutside); | |
129 | ✗ | const auto topArea = pyramidFrustumTopArea; | |
130 | ✗ | const auto insideTransmissibility = insideThermalConducitivity * sqrt(baseAreaInside*topArea) / distanceInside; | |
131 | ✗ | const auto outsideTransmissibility = outsideThermalConducitivity * sqrt(baseAreaOutside*topArea) / distanceOutside; | |
132 | |||
133 | ✗ | return 1.0 / (1.0/insideTransmissibility + 1.0/outsideTransmissibility); | |
134 | } | ||
135 | }; | ||
136 | |||
137 | /*! | ||
138 | * \ingroup DualNetworkCoupling | ||
139 | * \brief Implements a Fourier's law and scales the transmissibillity with a fixed, user-defined factor. | ||
140 | */ | ||
141 | template<bool isFluid> | ||
142 | struct FixedFactorFouriersLaw | ||
143 | { | ||
144 | template<class Problem, class Element, class FVElementGeometry, | ||
145 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
146 | static auto flux(const Problem& problem, | ||
147 | const Element& element, | ||
148 | const FVElementGeometry& fvGeometry, | ||
149 | const ElementVolumeVariables& elemVolVars, | ||
150 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
151 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
152 | { | ||
153 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | ||
154 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | ||
155 | const auto& insideVolVars = elemVolVars[insideScv]; | ||
156 | const auto& outsideVolVars = elemVolVars[outsideScv]; | ||
157 | const auto deltaT = insideVolVars.temperature() - outsideVolVars.temperature(); | ||
158 | return transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache) * deltaT; | ||
159 | } | ||
160 | |||
161 | template<class Problem, class Element, class FVElementGeometry, | ||
162 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
163 | ✗ | static auto transmissibility(const Problem& problem, | |
164 | const Element& element, | ||
165 | const FVElementGeometry& fvGeometry, | ||
166 | const ElementVolumeVariables& elemVolVars, | ||
167 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
168 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
169 | { | ||
170 | using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type; | ||
171 | |||
172 | ✗ | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | |
173 | ✗ | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | |
174 | ✗ | const auto& insideVolVars = elemVolVars[insideScv]; | |
175 | ✗ | const auto& outsideVolVars = elemVolVars[outsideScv]; | |
176 | |||
177 | ✗ | const auto getConductivity = [&](const auto& volVars) | |
178 | { | ||
179 | if constexpr (isFluid) | ||
180 | ✗ | return volVars.fluidThermalConductivity(0); | |
181 | else | ||
182 | ✗ | return volVars.solidThermalConductivity(); | |
183 | }; | ||
184 | |||
185 | ✗ | const auto insideThermalConducitivity = getConductivity(insideVolVars); | |
186 | ✗ | const auto outsideThermalConducitivity = getConductivity(outsideVolVars); | |
187 | |||
188 | ✗ | const auto eIdx = fvGeometry.gridGeometry().elementMapper().index(element); | |
189 | ✗ | const auto throatCenter = problem.spatialParams().throatCenter(eIdx); | |
190 | ✗ | const auto distanceInside = (insideScv.dofPosition() - throatCenter).two_norm(); | |
191 | ✗ | const auto distanceOutside = (outsideScv.dofPosition() - throatCenter).two_norm(); | |
192 | |||
193 | ✗ | static const Scalar fixedFactor = getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.FixedFourierFactor"); | |
194 | ✗ | const auto insideTransmissibility = insideThermalConducitivity*4*distanceInside*fixedFactor; | |
195 | ✗ | const auto outsideTransmissibility = outsideThermalConducitivity*4*distanceOutside*fixedFactor; | |
196 | |||
197 | ✗ | return 1.0 / (1.0/insideTransmissibility + 1.0/outsideTransmissibility); | |
198 | } | ||
199 | }; | ||
200 | |||
201 | /*! | ||
202 | * \ingroup DualNetworkCoupling | ||
203 | * \brief Implements a Fourier's law taking effective areas available for conduction into account. | ||
204 | * | ||
205 | * This Fourier's law is based on the work of Koch et al (2021) https://doi.org/10.1007/s11242-021-01602-5. | ||
206 | * It assumes pyramid frustum shapes for the geometry from pore bodies to pore throats, but not taking the full area | ||
207 | * of pore bodies into account, but the effective areas available for conduction. | ||
208 | * | ||
209 | * Depending on the conductivity ratio between the fluid and the solid \f$ \kappa = \frac{\łambda_s}{\lambda_s}\f$, | ||
210 | * the area available for conduction of heat through each phase might be restricted due to the other phase. | ||
211 | * The effective areas available for heat conduction in the solid are shown in the picture below: | ||
212 | * \image html effectiveareasforconduction_dnm.png | ||
213 | * This picture (figure 3) was taken from Koch et al (2021) https://doi.org/10.1007/s11242-021-01602-5. | ||
214 | * \f$A_{A}\f$ denotes here the contact area between two grains (solid bodies) and | ||
215 | * \f$\tilde{A}\f$ denotes the effective area of the solid body. | ||
216 | */ | ||
217 | template<bool isFluid> | ||
218 | struct FluidSolidEffectiveAreasFouriersLaw | ||
219 | { | ||
220 | template<class Problem, class Element, class FVElementGeometry, | ||
221 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
222 | static auto flux(const Problem& problem, | ||
223 | const Element& element, | ||
224 | const FVElementGeometry& fvGeometry, | ||
225 | const ElementVolumeVariables& elemVolVars, | ||
226 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
227 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
228 | { | ||
229 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | ||
230 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | ||
231 | const auto& insideVolVars = elemVolVars[insideScv]; | ||
232 | const auto& outsideVolVars = elemVolVars[outsideScv]; | ||
233 | const auto deltaT = insideVolVars.temperature() - outsideVolVars.temperature(); | ||
234 | return transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache) * deltaT; | ||
235 | } | ||
236 | |||
237 | template<class Problem, class Element, class FVElementGeometry, | ||
238 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
239 | 111842 | static auto transmissibility(const Problem& problem, | |
240 | const Element& element, | ||
241 | const FVElementGeometry& fvGeometry, | ||
242 | const ElementVolumeVariables& elemVolVars, | ||
243 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
244 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
245 | { | ||
246 | //For transmissibillity calculation see section 5.2 in Koch et al (2021) https://doi.org/10.1007/s11242-021-01602-5 | ||
247 | using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type; | ||
248 | |||
249 | 223684 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | |
250 | 223684 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | |
251 | 111842 | const auto& insideVolVars = elemVolVars[insideScv]; | |
252 | 111842 | const auto& outsideVolVars = elemVolVars[outsideScv]; | |
253 | |||
254 | ✗ | const auto getConductivity = [&](const auto& volVars) | |
255 | { | ||
256 | if constexpr (isFluid) | ||
257 | 185288 | return volVars.fluidThermalConductivity(0); | |
258 | else | ||
259 | 262080 | return volVars.solidThermalConductivity(); | |
260 | }; | ||
261 | |||
262 | 111842 | const auto insideThermalConducitivity = getConductivity(insideVolVars); | |
263 | 111842 | const auto outsideThermalConducitivity = getConductivity(outsideVolVars); | |
264 | |||
265 | 223684 | const auto eIdx = fvGeometry.gridGeometry().elementMapper().index(element); | |
266 | |||
267 | 223684 | const auto distance = [&](const auto& scv) | |
268 | { | ||
269 |
10/16✓ Branch 0 taken 1 times.
✓ Branch 1 taken 46321 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 65519 times.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
|
111842 | static const bool useThroatCenter = getParamFromGroup<bool>(problem.paramGroup(), "FouriersLaw.UseThroatCenter", true); |
270 |
2/4✓ Branch 0 taken 46322 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65520 times.
✗ Branch 3 not taken.
|
111842 | if (useThroatCenter) |
271 | { | ||
272 | 223684 | const auto throatCenter = problem.spatialParams().throatCenter(eIdx); | |
273 | 335526 | return (scv.dofPosition() - throatCenter).two_norm(); | |
274 | } | ||
275 | else | ||
276 | { | ||
277 | ✗ | static const Scalar R = getParam<Scalar>("DualNetwork.SphereRadius", 50e-6); | |
278 | ✗ | static const Scalar overlapFactor = getParam<Scalar>("DualNetwork.OverlapFactor"); | |
279 | ✗ | static const auto dx = overlapFactor*R; | |
280 | ✗ | return dx; | |
281 | } | ||
282 | }; | ||
283 | |||
284 | 111842 | const Scalar distanceInside = distance(insideScv); | |
285 | 111842 | const Scalar distanceOutside = distance(outsideScv); | |
286 | |||
287 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
|
111842 | assert(distanceInside > 0.0); |
288 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
|
111842 | assert(distanceOutside > 0.0); |
289 | |||
290 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
111842 | static const Scalar liquidThermalConductivity = getParam<Scalar>("2.Component.LiquidThermalConductivity"); |
291 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
111842 | static const Scalar solidThermalConductivity = getParam<Scalar>("1.Component.SolidThermalConductivity"); |
292 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
111842 | static const Scalar kappa = liquidThermalConductivity / solidThermalConductivity; |
293 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
111842 | static const Scalar kappaFactor = isFluid ? kappa : 1.0/kappa; |
294 | |||
295 | 111842 | const Scalar ApInside = insideVolVars.poreVolume()/(2.0*distanceInside); | |
296 | 111842 | const Scalar ApOutside = outsideVolVars.poreVolume()/(2.0*distanceOutside); | |
297 | |||
298 | //For effective areas see figure 3 in Koch et al (2021) https://doi.org/10.1007/s11242-021-01602-5 | ||
299 | 111842 | Scalar effectiveAreaInside = 0.0; | |
300 | 111842 | Scalar effectiveAreaOutside = 0.0; | |
301 | 111842 | Scalar At = 0.0; | |
302 | |||
303 | 111842 | const auto effectiveArea = [kappaFactor = kappaFactor](const Scalar At, const Scalar Cinf, const Scalar C0) | |
304 | { | ||
305 | 223684 | return At*(Cinf + ((C0 - Cinf)*(Cinf - 1.0))/((Cinf - 1.0) + kappaFactor*(1.0 - C0))); | |
306 | }; | ||
307 | |||
308 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
111842 | static const Scalar useExactThroatAreaSphere = getParam<bool>("DualNetwork.UseExactThroatAreaSphere", false); |
309 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
|
111842 | if (useExactThroatAreaSphere) |
310 | { | ||
311 | ✗ | static const Scalar R = getParamFromGroup<Scalar>(problem.paramGroup(), "DualNetwork.SphereRadius", 50e-6); | |
312 | ✗ | const auto As = [](Scalar x, Scalar dx, Scalar R) { return M_PI*(R - x)*(R + x); }; | |
313 | ✗ | const auto Asq = [](Scalar x, Scalar dx, Scalar R) { return 4*dx*dx; }; | |
314 | ✗ | const auto Asemicirc = [](Scalar x, Scalar dx, Scalar R) { | |
315 | ✗ | const auto r = std::sqrt((R - x)*(R + x)); | |
316 | ✗ | return (r*r)*std::acos(dx/r) - dx*std::sqrt(r*r - dx*dx); | |
317 | }; | ||
318 | ✗ | const auto A1s = [&](Scalar x, Scalar dx, Scalar R) { return As(x, dx, R) - 4*Asemicirc(x, dx, R); }; | |
319 | ✗ | const auto A2s = [&](Scalar x, Scalar dx, Scalar R) { return As(x, dx, R); }; | |
320 | ✗ | const auto A1f = [&](Scalar x, Scalar dx, Scalar R) { return Asq(x, dx, R) - A1s(x, dx, R); }; | |
321 | // const auto A2f = [&](Scalar x, Scalar dx, Scalar R) { return Asq(x, dx, R) - A2s(x, dx, R); }; // TODO unused? | ||
322 | |||
323 | ✗ | At = isFluid ? A1f(0, distanceInside, R) : A2s(distanceInside, distanceInside, R); | |
324 | ✗ | const Scalar C0 = isFluid ? 0.1 : 0.45; | |
325 | ✗ | const Scalar Cinf = isFluid ? ApInside/At : ApInside/At*1.45; | |
326 | ✗ | effectiveAreaInside = effectiveArea(At, Cinf, C0); | |
327 | ✗ | effectiveAreaOutside = effectiveAreaInside; | |
328 | ✗ | assert(std::isnormal(effectiveAreaInside)); | |
329 | } | ||
330 | else | ||
331 | { | ||
332 | ✗ | At = [&] | |
333 | { | ||
334 | if constexpr (isFluid) | ||
335 | 92644 | return elemFluxVarsCache[scvf].throatCrossSectionalArea(0); | |
336 | else | ||
337 | 131040 | return elemFluxVarsCache[scvf].grainContactArea(); | |
338 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
|
111842 | }(); |
339 | |||
340 |
5/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
111844 | static const Scalar C0 = isFluid ? getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.C0Fluid") |
341 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
4 | : getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.C0Solid"); |
342 |
5/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55919 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
111844 | static const Scalar CinfFactor = isFluid ? getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.CInfFactorFluid") |
343 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
4 | : getParamFromGroup<Scalar>(problem.paramGroup(), "FouriersLaw.CInfFactorSolid"); |
344 | using std::max; | ||
345 |
2/2✓ Branch 0 taken 13048 times.
✓ Branch 1 taken 42873 times.
|
111842 | const Scalar CinfInside = max(ApInside/At*CinfFactor, 1.0); |
346 |
2/2✓ Branch 0 taken 12403 times.
✓ Branch 1 taken 43518 times.
|
111842 | const Scalar CinfOutside = max(ApOutside/At*CinfFactor, 1.0); |
347 | 223684 | effectiveAreaInside = effectiveArea(At, CinfInside, C0); | |
348 | 111842 | effectiveAreaOutside = effectiveArea(At, CinfOutside, C0); | |
349 | } | ||
350 | |||
351 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 55921 times.
|
223684 | assert(std::isnormal(effectiveAreaInside)); |
352 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
|
111842 | assert(effectiveAreaInside > 0.0); |
353 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 55921 times.
|
223684 | assert(std::isnormal(effectiveAreaOutside)); |
354 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
|
111842 | assert(effectiveAreaOutside > 0.0); |
355 | |||
356 | 111842 | const Scalar tInside = std::sqrt(effectiveAreaInside*At)/distanceInside; | |
357 | 111842 | const Scalar tOutside = std::sqrt(effectiveAreaOutside*At)/distanceOutside; | |
358 | |||
359 | 111842 | const auto insideTransmissibility = insideThermalConducitivity*tInside; | |
360 | 111842 | const auto outsideTransmissibility = outsideThermalConducitivity*tOutside; | |
361 | 111842 | const auto result = 1.0 / (1.0/insideTransmissibility + 1.0/outsideTransmissibility); | |
362 | |||
363 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 55921 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 55921 times.
|
223684 | if (!std::isnormal(result)) |
364 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Error in heat conductivity. Check your grid and your factors."); | |
365 | |||
366 | 111842 | return result; | |
367 | } | ||
368 | }; | ||
369 | |||
370 | /*! | ||
371 | * \ingroup DualNetworkCoupling | ||
372 | * \brief Implements a Fourier's law and scales a fixed transmissibillity with a factor | ||
373 | * depending on the position of the throat. | ||
374 | */ | ||
375 | template<class BaseLaw> | ||
376 | struct ScalingFouriersLaw | ||
377 | { | ||
378 | template<class Problem, class Element, class FVElementGeometry, | ||
379 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
380 | static auto flux(const Problem& problem, | ||
381 | const Element& element, | ||
382 | const FVElementGeometry& fvGeometry, | ||
383 | const ElementVolumeVariables& elemVolVars, | ||
384 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
385 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
386 | { | ||
387 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | ||
388 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | ||
389 | const auto& insideVolVars = elemVolVars[insideScv]; | ||
390 | const auto& outsideVolVars = elemVolVars[outsideScv]; | ||
391 | using Scalar = typename ElementVolumeVariables::VolumeVariables::PrimaryVariables::value_type; | ||
392 | const auto deltaT = insideVolVars.temperature() - outsideVolVars.temperature(); | ||
393 | |||
394 | static const bool useScaling = getParamFromGroup<bool>(problem.paramGroup(), "FouriersLaw.UseFourierScaling", true); | ||
395 | if (!useScaling) | ||
396 | return transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache) * deltaT; | ||
397 | |||
398 | |||
399 | Scalar factor = 1.0; | ||
400 | const auto& bBoxMin = problem.gridGeometry().bBoxMin(); | ||
401 | const auto& bBoxMax = problem.gridGeometry().bBoxMax(); | ||
402 | const auto throatCenter = element.geometry().center(); | ||
403 | |||
404 | for (int i = 0; i < throatCenter.size(); ++i) | ||
405 | { | ||
406 | constexpr Scalar eps = 1e-8; // TODO | ||
407 | if (throatCenter[i] < bBoxMin[i] + eps || throatCenter[i] > bBoxMax[i] - eps) | ||
408 | factor *= 0.5; | ||
409 | } | ||
410 | |||
411 | static const Scalar baseTransmissibility = problem.getInternalReferenceHeatTransmissibility(); | ||
412 | return baseTransmissibility * factor * deltaT; | ||
413 | } | ||
414 | |||
415 | template<class Problem, class Element, class FVElementGeometry, | ||
416 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
417 | static auto transmissibility(const Problem& problem, | ||
418 | const Element& element, | ||
419 | const FVElementGeometry& fvGeometry, | ||
420 | const ElementVolumeVariables& elemVolVars, | ||
421 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
422 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
423 | { | ||
424 | return BaseLaw::transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache); | ||
425 | } | ||
426 | }; | ||
427 | |||
428 | template<bool isFluid> | ||
429 | struct FlexibleFouriersLaw | ||
430 | { | ||
431 | enum class Mode {pyramid, fixedFactor, fluidSolidEffectiveAreas, tpfa}; | ||
432 | |||
433 | template<class Problem, class Element, class FVElementGeometry, | ||
434 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
435 | 111842 | static auto flux(const Problem& problem, | |
436 | const Element& element, | ||
437 | const FVElementGeometry& fvGeometry, | ||
438 | const ElementVolumeVariables& elemVolVars, | ||
439 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
440 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
441 | { | ||
442 | 223684 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); | |
443 | 223684 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | |
444 | 111842 | const auto& insideVolVars = elemVolVars[insideScv]; | |
445 | 111842 | const auto& outsideVolVars = elemVolVars[outsideScv]; | |
446 | 223684 | const auto deltaT = insideVolVars.temperature() - outsideVolVars.temperature(); | |
447 | 111842 | return transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache) * deltaT; | |
448 | } | ||
449 | |||
450 | template<class Problem, class Element, class FVElementGeometry, | ||
451 | class ElementVolumeVariables, class ElementFluxVariablesCache> | ||
452 | 55921 | static auto transmissibility(const Problem& problem, | |
453 | const Element& element, | ||
454 | const FVElementGeometry& fvGeometry, | ||
455 | const ElementVolumeVariables& elemVolVars, | ||
456 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
457 | const ElementFluxVariablesCache& elemFluxVarsCache) | ||
458 | { | ||
459 | 55925 | static const Mode mode = [&]() | |
460 | { | ||
461 | 6 | const auto m = getParamFromGroup<std::string>(problem.paramGroup(), "FouriersLaw.ThroatConductionType"); | |
462 | 2 | if (m == "Pyramid") | |
463 | return Mode::pyramid; | ||
464 | 2 | else if (m == "FluidSolidEffectiveAreas") | |
465 | return Mode::fluidSolidEffectiveAreas; | ||
466 | ✗ | else if (m == "FixedFactor") | |
467 | return Mode::fixedFactor; | ||
468 | else | ||
469 | ✗ | return Mode::tpfa; | |
470 | 4 | }(); | |
471 | |||
472 | 55921 | if (mode == Mode::pyramid) | |
473 | ✗ | return FluidOrGrainPyramidFouriersLaw<isFluid>::transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache); | |
474 | 55921 | else if (mode == Mode::fixedFactor) | |
475 | ✗ | return FixedFactorFouriersLaw<isFluid>::transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache); | |
476 | 55921 | else if (mode == Mode::fluidSolidEffectiveAreas) | |
477 | 55921 | return FluidSolidEffectiveAreasFouriersLaw<isFluid>::transmissibility(problem, element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache); | |
478 | else return 0.0; // TODO implement TPFA | ||
479 | } | ||
480 | }; | ||
481 | |||
482 | |||
483 | } // end namespace Dumux::PoreNetwork | ||
484 | |||
485 | #endif | ||
486 |