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 FluidSystems | ||
10 | * \copybrief Dumux::FluidSystems::BrineAir | ||
11 | */ | ||
12 | #ifndef DUMUX_BRINE_AIR_FLUID_SYSTEM_HH | ||
13 | #define DUMUX_BRINE_AIR_FLUID_SYSTEM_HH | ||
14 | |||
15 | #include <array> | ||
16 | #include <cassert> | ||
17 | #include <iomanip> | ||
18 | |||
19 | #include <dumux/material/idealgas.hh> | ||
20 | #include <dumux/material/fluidstates/adapter.hh> | ||
21 | #include <dumux/material/fluidsystems/base.hh> | ||
22 | #include <dumux/material/fluidsystems/brine.hh> | ||
23 | #include <dumux/material/components/air.hh> | ||
24 | #include <dumux/material/components/h2o.hh> | ||
25 | #include <dumux/material/components/nacl.hh> | ||
26 | #include <dumux/material/binarycoefficients/h2o_air.hh> | ||
27 | #include <dumux/material/components/tabulatedcomponent.hh> | ||
28 | |||
29 | #include <dumux/common/exceptions.hh> | ||
30 | |||
31 | #include <dumux/io/name.hh> | ||
32 | |||
33 | #include "brine.hh" | ||
34 | |||
35 | namespace Dumux { | ||
36 | namespace FluidSystems { | ||
37 | |||
38 | /*! | ||
39 | * \ingroup FluidSystems | ||
40 | * \brief Policy for the brine-air fluid system | ||
41 | */ | ||
42 | template<bool fastButSimplifiedRelations = false> | ||
43 | struct BrineAirDefaultPolicy | ||
44 | { | ||
45 | static constexpr bool useBrineDensityAsLiquidMixtureDensity() { return fastButSimplifiedRelations;} | ||
46 | static constexpr bool useIdealGasDensity() { return fastButSimplifiedRelations; } | ||
47 | static constexpr bool useKelvinVaporPressure() { return false; } | ||
48 | }; | ||
49 | |||
50 | /*! | ||
51 | * \ingroup FluidSystems | ||
52 | * \brief A compositional two-phase fluid system with a liquid and a gaseous phase | ||
53 | * and \f$H_2O\f$, \f$Air\f$ and \f$S\f$ (dissolved minerals) as components. | ||
54 | * | ||
55 | * \note This fluidsystem is applied by default with the tabulated version of | ||
56 | * water of the IAPWS-formulation. | ||
57 | */ | ||
58 | template <class Scalar, | ||
59 | class H2Otype = Components::TabulatedComponent<Components::H2O<Scalar>>, | ||
60 | class Policy = BrineAirDefaultPolicy<>> | ||
61 | class BrineAir | ||
62 | : public Base<Scalar, BrineAir<Scalar, H2Otype, Policy>> | ||
63 | { | ||
64 | using ThisType = BrineAir<Scalar, H2Otype, Policy>; | ||
65 | using IdealGas = Dumux::IdealGas<Scalar>; | ||
66 | |||
67 | public: | ||
68 | //! export the involved components | ||
69 | using H2O = H2Otype; | ||
70 | using Air = Components::Air<Scalar>; | ||
71 | using NaCl = Components::NaCl<Scalar>; | ||
72 | |||
73 | //! export the underlying brine fluid system for the liquid phase | ||
74 | using Brine = Dumux::FluidSystems::Brine<Scalar, H2Otype>; | ||
75 | |||
76 | //! export the binary coefficients between air and water | ||
77 | using H2O_Air = BinaryCoeff::H2O_Air; | ||
78 | |||
79 | //! the type of parameter cache objects | ||
80 | using ParameterCache = NullParameterCache; | ||
81 | |||
82 | /**************************************** | ||
83 | * Fluid phase related static parameters | ||
84 | ****************************************/ | ||
85 | static constexpr int numPhases = 2; // one liquid and one gas phase | ||
86 | static constexpr int numComponents = 3; // H2O, Air, NaCl | ||
87 | |||
88 | static constexpr int liquidPhaseIdx = 0; // index of the liquid phase | ||
89 | static constexpr int gasPhaseIdx = 1; // index of the gas phase | ||
90 | |||
91 | static constexpr int phase0Idx = liquidPhaseIdx; // index of the first phase | ||
92 | static constexpr int phase1Idx = gasPhaseIdx; // index of the second phase | ||
93 | |||
94 | // export component indices to indicate the main component | ||
95 | // of the corresponding phase at atmospheric pressure 1 bar | ||
96 | // and room temperature 20°C: | ||
97 | static constexpr int H2OIdx = 0; | ||
98 | static constexpr int AirIdx = 1; | ||
99 | static constexpr int NaClIdx = 2; | ||
100 | static constexpr int comp0Idx = H2OIdx; | ||
101 | static constexpr int comp1Idx = AirIdx; | ||
102 | static constexpr int comp2Idx = NaClIdx; | ||
103 | |||
104 | private: | ||
105 | struct BrineAdapterPolicy | ||
106 | { | ||
107 | using FluidSystem = Brine; | ||
108 | |||
109 | ✗ | static constexpr int phaseIdx(int brinePhaseIdx) { return liquidPhaseIdx; } | |
110 | static constexpr int compIdx(int brineCompIdx) | ||
111 | { | ||
112 |
10/20✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 6651543 times.
✓ Branch 3 taken 6651543 times.
✓ Branch 4 taken 6651543 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 86316 times.
✓ Branch 7 taken 86316 times.
✓ Branch 8 taken 86316 times.
✓ Branch 9 taken 86316 times.
✓ Branch 10 taken 345264 times.
✓ Branch 11 taken 345264 times.
✓ Branch 12 taken 345264 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
21335685 | switch (brineCompIdx) |
113 | { | ||
114 | case Brine::H2OIdx: return H2OIdx; | ||
115 | 14166246 | case Brine::NaClIdx: return NaClIdx; | |
116 | default: return 0; // this will never be reached, only needed to suppress compiler warning | ||
117 | } | ||
118 | } | ||
119 | }; | ||
120 | |||
121 | template<class FluidState> | ||
122 | using BrineAdapter = FluidStateAdapter<FluidState, BrineAdapterPolicy>; | ||
123 | |||
124 | public: | ||
125 | |||
126 | /**************************************** | ||
127 | * phase related static parameters | ||
128 | ****************************************/ | ||
129 | |||
130 | /*! | ||
131 | * \brief Return the human readable name of a fluid phase | ||
132 | * \param phaseIdx index of the phase | ||
133 | */ | ||
134 | 418 | static std::string phaseName(int phaseIdx) | |
135 | { | ||
136 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 414 times.
|
418 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
137 |
2/3✓ Branch 0 taken 207 times.
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
|
418 | switch (phaseIdx) |
138 | { | ||
139 | 209 | case liquidPhaseIdx: return IOName::liquidPhase(); | |
140 | 209 | case gasPhaseIdx: return IOName::gaseousPhase(); | |
141 | } | ||
142 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | |
143 | } | ||
144 | |||
145 | /*! | ||
146 | * \brief Returns whether the fluids are miscible | ||
147 | */ | ||
148 | static constexpr bool isMiscible() | ||
149 | { return true; } | ||
150 | |||
151 | /*! | ||
152 | * \brief Return whether a phase is gaseous | ||
153 | * \param phaseIdx The index of the fluid phase to consider | ||
154 | */ | ||
155 | static constexpr bool isGas(int phaseIdx) | ||
156 | { | ||
157 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
158 | return phaseIdx == gasPhaseIdx; | ||
159 | } | ||
160 | |||
161 | /*! | ||
162 | * \brief Returns true if and only if a fluid phase is assumed to | ||
163 | * be an ideal mixture. | ||
164 | * | ||
165 | * We define an ideal mixture as a fluid phase where the fugacity | ||
166 | * coefficients of all components times the pressure of the phase | ||
167 | * are independent on the fluid composition. This assumption is true | ||
168 | * if Henry's law and Raoult's law apply. If you are unsure what | ||
169 | * this function should return, it is safe to return false. The | ||
170 | * only damage done will be (slightly) increased computation times | ||
171 | * in some cases. | ||
172 | * | ||
173 | * \param phaseIdx The index of the fluid phase to consider | ||
174 | */ | ||
175 | static bool isIdealMixture(int phaseIdx) | ||
176 | { | ||
177 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 452423 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 452423 times.
|
904846 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
178 | // we assume Henry's and Raoult's laws for the water phase and | ||
179 | // and no interaction between gas molecules of different | ||
180 | // components, so all phases are ideal mixtures! | ||
181 | return true; | ||
182 | } | ||
183 | |||
184 | /*! | ||
185 | * \brief Returns true if and only if a fluid phase is assumed to | ||
186 | * be compressible. | ||
187 | * | ||
188 | * Compressible means that the partial derivative of the density | ||
189 | * to the fluid pressure is always larger than zero. | ||
190 | * | ||
191 | * \param phaseIdx The index of the fluid phase to consider | ||
192 | */ | ||
193 | static constexpr bool isCompressible(int phaseIdx) | ||
194 | { | ||
195 | assert(0 <= phaseIdx && phaseIdx < numPhases); | ||
196 | // ideal gases are always compressible | ||
197 |
4/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
4 | if (phaseIdx == gasPhaseIdx) |
198 | return true; | ||
199 | // let brine decide for the liquid phase... | ||
200 | return Brine::isCompressible(Brine::liquidPhaseIdx); | ||
201 | } | ||
202 | |||
203 | /*! | ||
204 | * \brief Returns true if and only if a fluid phase is assumed to | ||
205 | * be an ideal gas. | ||
206 | * \param phaseIdx The index of the fluid phase to consider | ||
207 | */ | ||
208 | static bool isIdealGas(int phaseIdx) | ||
209 | { | ||
210 | assert(0 <= phaseIdx && phaseIdx < numPhases); | ||
211 | // let the fluids decide | ||
212 | if (phaseIdx == gasPhaseIdx) | ||
213 | return H2O::gasIsIdeal() && Air::gasIsIdeal(); | ||
214 | return false; // not a gas | ||
215 | } | ||
216 | |||
217 | /*! | ||
218 | * \brief Get the main component of a given phase if possible | ||
219 | * \param phaseIdx The index of the fluid phase to consider | ||
220 | */ | ||
221 | static constexpr int getMainComponent(int phaseIdx) | ||
222 | { | ||
223 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 30432872 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 22179600 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 26000256 times.
|
78612728 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
224 |
8/8✓ Branch 0 taken 15216436 times.
✓ Branch 1 taken 15216436 times.
✓ Branch 2 taken 11089800 times.
✓ Branch 3 taken 11089800 times.
✓ Branch 4 taken 13000128 times.
✓ Branch 5 taken 13000128 times.
✓ Branch 6 taken 6002898 times.
✓ Branch 7 taken 6002898 times.
|
90618524 | if (phaseIdx == liquidPhaseIdx) |
225 | return H2OIdx; | ||
226 | else | ||
227 | return AirIdx; | ||
228 | } | ||
229 | |||
230 | /**************************************** | ||
231 | * Component related static parameters | ||
232 | ****************************************/ | ||
233 | /*! | ||
234 | * \brief Return the human readable name of a component | ||
235 | * \param compIdx The index of the component to consider | ||
236 | */ | ||
237 | 36 | static std::string componentName(int compIdx) | |
238 | { | ||
239 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
36 | assert(0 <= compIdx && compIdx < numComponents); |
240 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
36 | switch (compIdx) |
241 | { | ||
242 | 12 | case H2OIdx: return H2O::name(); | |
243 | 12 | case AirIdx: return Air::name(); | |
244 | 12 | case NaClIdx: return NaCl::name(); | |
245 | } | ||
246 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid component index " << compIdx); | |
247 | } | ||
248 | |||
249 | /*! | ||
250 | * \brief Return the molar mass of a component in \f$\mathrm{[kg/mol]}\f$. | ||
251 | * \param compIdx The index of the component to consider | ||
252 | */ | ||
253 | 206124543 | static Scalar molarMass(int compIdx) | |
254 | { | ||
255 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 206124453 times.
|
206124543 | assert(0 <= compIdx && compIdx < numComponents); |
256 | switch (compIdx) | ||
257 | { | ||
258 | case H2OIdx: return H2O::molarMass(); | ||
259 | case AirIdx: return Air::molarMass(); | ||
260 | case NaClIdx: return NaCl::molarMass(); | ||
261 | } | ||
262 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid component index " << compIdx); | |
263 | } | ||
264 | |||
265 | /*! | ||
266 | * \brief Vapor pressure of a component \f$\mathrm{[Pa]}\f$. | ||
267 | * | ||
268 | * \param fluidState The fluid state | ||
269 | * \param compIdx The index of the component to consider | ||
270 | */ | ||
271 | template <class FluidState> | ||
272 | static Scalar vaporPressure(const FluidState& fluidState, int compIdx) | ||
273 | { | ||
274 | // The vapor pressure of the water is affected by the | ||
275 | // salinity, thus, we forward to the interface of Brine here | ||
276 | if (compIdx == H2OIdx) | ||
277 | { | ||
278 | if (!Policy::useKelvinVaporPressure()) | ||
279 | return Brine::vaporPressure(BrineAdapter<FluidState>(fluidState), Brine::H2OIdx); | ||
280 | else | ||
281 | { | ||
282 | //additionally taking the influence of the capillary pressure on the vapor pressure, described by Kelvin's equation, into account. | ||
283 | const auto t = fluidState.temperature(liquidPhaseIdx); | ||
284 | const auto pc = (fluidState.wettingPhase() == (int) liquidPhaseIdx) | ||
285 | ? fluidState.pressure(gasPhaseIdx)-fluidState.pressure(liquidPhaseIdx) | ||
286 | : fluidState.pressure(liquidPhaseIdx)-fluidState.pressure(gasPhaseIdx); | ||
287 | //influence of the capillary pressure on the vapor pressure, the influence of the salt concentration is already taken into account in the brine vapour pressure | ||
288 | return Brine::vaporPressure(BrineAdapter<FluidState>(fluidState), Brine::H2OIdx) | ||
289 | *exp( -pc / (Brine::molarDensity(fluidState, liquidPhaseIdx) * (Dumux::Constants<Scalar>::R*t))); | ||
290 | } | ||
291 | } | ||
292 | else if (compIdx == NaClIdx) | ||
293 | DUNE_THROW(Dune::NotImplemented, "NaCl::vaporPressure(t)"); | ||
294 | else | ||
295 | DUNE_THROW(Dune::NotImplemented, "Invalid component index " << compIdx); | ||
296 | } | ||
297 | |||
298 | /**************************************** | ||
299 | * thermodynamic relations | ||
300 | ****************************************/ | ||
301 | /*! | ||
302 | * \brief Initialize the fluid system's static parameters generically | ||
303 | * | ||
304 | * If a tabulated H2O component is used, we do our best to create | ||
305 | * tables that always work. | ||
306 | */ | ||
307 | static void init() | ||
308 | { | ||
309 | 2 | init(/*tempMin=*/273.15, | |
310 | /*tempMax=*/800.0, | ||
311 | /*numTemptempSteps=*/200, | ||
312 | /*startPressure=*/-10, | ||
313 | /*endPressure=*/20e6, | ||
314 | /*pressureSteps=*/200); | ||
315 | } | ||
316 | |||
317 | /*! | ||
318 | * \brief Initialize the fluid system's static parameters using | ||
319 | * problem specific temperature and pressure ranges | ||
320 | * | ||
321 | * \param tempMin The minimum temperature used for tabulation of water \f$\mathrm{[K]}\f$ | ||
322 | * \param tempMax The maximum temperature used for tabulation of water \f$\mathrm{[K]}\f$ | ||
323 | * \param nTemp The number of ticks on the temperature axis of the table of water | ||
324 | * \param pressMin The minimum pressure used for tabulation of water \f$\mathrm{[Pa]}\f$ | ||
325 | * \param pressMax The maximum pressure used for tabulation of water \f$\mathrm{[Pa]}\f$ | ||
326 | * \param nPress The number of ticks on the pressure axis of the table of water | ||
327 | */ | ||
328 | 8 | static void init(Scalar tempMin, Scalar tempMax, unsigned nTemp, | |
329 | Scalar pressMin, Scalar pressMax, unsigned nPress) | ||
330 | { | ||
331 | 8 | std::cout << "The brine-air fluid system was configured with the following policy:\n"; | |
332 | 16 | std::cout << " - use brine density as liquid mixture density: " << std::boolalpha << Policy::useBrineDensityAsLiquidMixtureDensity() << "\n"; | |
333 | 16 | std::cout << " - use ideal gas density: " << std::boolalpha << Policy::useIdealGasDensity() << std::endl; | |
334 | |||
335 | if (H2O::isTabulated) | ||
336 | H2O::init(tempMin, tempMax, nTemp, pressMin, pressMax, nPress); | ||
337 | 8 | } | |
338 | |||
339 | using Base<Scalar, ThisType>::density; | ||
340 | /*! | ||
341 | * \brief Given a phase's composition, temperature, pressure, and | ||
342 | * the partial pressures of all components, return its | ||
343 | * density \f$\mathrm{[kg/m^3]}\f$. | ||
344 | * | ||
345 | * \param fluidState the fluid state | ||
346 | * \param phaseIdx index of the phase | ||
347 | * | ||
348 | * Equation given in: | ||
349 | * - Batzle & Wang (1992) \cite batzle1992 | ||
350 | * - cited by: Bachu & Adams (2002) | ||
351 | * "Equations of State for basin geofluids" \cite adams2002 | ||
352 | */ | ||
353 | template <class FluidState> | ||
354 | 4607002 | static Scalar density(const FluidState &fluidState, int phaseIdx) | |
355 | { | ||
356 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4606998 times.
|
4607002 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
357 | |||
358 |
2/2✓ Branch 0 taken 2303497 times.
✓ Branch 1 taken 2303497 times.
|
4607002 | const auto T = fluidState.temperature(phaseIdx); |
359 |
2/2✓ Branch 0 taken 2303497 times.
✓ Branch 1 taken 2303497 times.
|
4607002 | const auto p = fluidState.pressure(phaseIdx); |
360 | |||
361 |
2/2✓ Branch 0 taken 2303499 times.
✓ Branch 1 taken 2303499 times.
|
4607002 | if (phaseIdx == liquidPhaseIdx) |
362 | { | ||
363 | // assume pure brine | ||
364 | if (Policy::useBrineDensityAsLiquidMixtureDensity()) | ||
365 | 4 | return Brine::density(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx); | |
366 | |||
367 | // assume one molecule of gas replaces one "brine" molecule | ||
368 | else | ||
369 | 4606998 | return Brine::molarDensity(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx) | |
370 | 2303499 | *(H2O::molarMass()*fluidState.moleFraction(liquidPhaseIdx, H2OIdx) | |
371 | 2303499 | + NaCl::molarMass()*fluidState.moleFraction(liquidPhaseIdx, NaClIdx) | |
372 | 4606996 | + Air::molarMass()*fluidState.moleFraction(liquidPhaseIdx, AirIdx)); | |
373 | } | ||
374 | else if (phaseIdx == phase1Idx) | ||
375 | { | ||
376 | // for the gas phase assume an ideal gas | ||
377 | if (Policy::useIdealGasDensity()) | ||
378 | 2 | return IdealGas::density(fluidState.averageMolarMass(phase1Idx), T, p); | |
379 | |||
380 | // if useComplexRelations = true, compute density. NaCl is assumed | ||
381 | // not to be present in gas phase, NaCl has only solid interfaces implemented | ||
382 | 4606996 | return H2O::gasDensity(T, fluidState.partialPressure(phase1Idx, H2OIdx)) | |
383 | 6910493 | + Air::gasDensity(T, fluidState.partialPressure(phase1Idx, AirIdx)); | |
384 | } | ||
385 | else | ||
386 | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | ||
387 | } | ||
388 | |||
389 | using Base<Scalar, ThisType>::molarDensity; | ||
390 | //! \copydoc Base<Scalar,ThisType>::molarDensity(const FluidState&,int) | ||
391 | template <class FluidState> | ||
392 | 4607002 | static Scalar molarDensity(const FluidState& fluidState, int phaseIdx) | |
393 | { | ||
394 |
2/2✓ Branch 0 taken 2303499 times.
✓ Branch 1 taken 2303499 times.
|
4607002 | if (phaseIdx == liquidPhaseIdx) |
395 | 6910503 | return Brine::molarDensity(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx); | |
396 |
1/2✓ Branch 0 taken 2303499 times.
✗ Branch 1 not taken.
|
2303501 | else if (phaseIdx == phase1Idx) |
397 | { | ||
398 | 2303501 | const Scalar T = fluidState.temperature(phaseIdx); | |
399 | |||
400 | // for the gas phase assume an ideal gas | ||
401 | if (Policy::useIdealGasDensity()) | ||
402 | 2 | return IdealGas::molarDensity(T, fluidState.pressure(phaseIdx)); | |
403 | |||
404 | // if useComplexRelations = true, compute density. NaCl is assumed | ||
405 | // not to be present in gas phase, NaCl has only solid interfaces implemented | ||
406 | 4606996 | return H2O::gasMolarDensity(T, fluidState.partialPressure(phase1Idx, H2OIdx)) | |
407 | 6910493 | + Air::gasMolarDensity(T, fluidState.partialPressure(phase1Idx, AirIdx)); | |
408 | } | ||
409 | else | ||
410 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | |
411 | } | ||
412 | |||
413 | using Base<Scalar, ThisType>::viscosity; | ||
414 | /*! | ||
415 | * \brief Calculate the dynamic viscosity of a fluid phase \f$\mathrm{[Pa*s]}\f$ | ||
416 | * | ||
417 | * \param fluidState An arbitrary fluid state | ||
418 | * \param phaseIdx The index of the fluid phase to consider | ||
419 | * | ||
420 | * \note For the viscosity of the phases the contribution of the minor | ||
421 | * component is neglected. This contribution is probably not big, but somebody | ||
422 | * would have to find out its influence. | ||
423 | */ | ||
424 | template <class FluidState> | ||
425 | 4607002 | static Scalar viscosity(const FluidState& fluidState, int phaseIdx) | |
426 | { | ||
427 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4606998 times.
|
4607002 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
428 | |||
429 |
2/2✓ Branch 0 taken 2303499 times.
✓ Branch 1 taken 2303499 times.
|
4607002 | if (phaseIdx == liquidPhaseIdx) |
430 | 4607002 | return Brine::viscosity(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx); | |
431 | else | ||
432 | 6910495 | return Air::gasViscosity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)); | |
433 | } | ||
434 | |||
435 | using Base<Scalar, ThisType>::fugacityCoefficient; | ||
436 | /*! | ||
437 | * \brief Returns the fugacity coefficient \f$\mathrm{[-]}\f$ of a component in a | ||
438 | * phase. | ||
439 | * \param fluidState The fluid state | ||
440 | * \param phaseIdx Index of the phase | ||
441 | * \param compIdx Index of the component | ||
442 | * | ||
443 | * The fugacity coefficient \f$\mathrm{\phi^\kappa_\alpha}\f$ of | ||
444 | * component \f$\mathrm{\kappa}\f$ in phase \f$\mathrm{\alpha}\f$ is connected to | ||
445 | * the fugacity \f$\mathrm{f^\kappa_\alpha}\f$ and the component's mole | ||
446 | * fraction \f$\mathrm{x^\kappa_\alpha}\f$ by means of the relation | ||
447 | * | ||
448 | * \f[ | ||
449 | f^\kappa_\alpha = \phi^\kappa_\alpha\;x^\kappa_\alpha\;p_\alpha | ||
450 | \f] | ||
451 | * where \f$\mathrm{p_\alpha}\f$ is the pressure of the fluid phase. | ||
452 | * | ||
453 | * For liquids with very low miscibility this boils down to the | ||
454 | * Henry constant for the solutes and the saturated vapor pressure | ||
455 | * both divided by phase pressure. | ||
456 | */ | ||
457 | template <class FluidState> | ||
458 | 13821006 | static Scalar fugacityCoefficient(const FluidState& fluidState, int phaseIdx, int compIdx) | |
459 | { | ||
460 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13820994 times.
|
13821006 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
461 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13820994 times.
|
13821006 | assert(0 <= compIdx && compIdx < numComponents); |
462 | |||
463 |
2/2✓ Branch 0 taken 6910491 times.
✓ Branch 1 taken 6910491 times.
|
13821006 | Scalar T = fluidState.temperature(phaseIdx); |
464 |
2/2✓ Branch 0 taken 6910491 times.
✓ Branch 1 taken 6910491 times.
|
13821006 | Scalar p = fluidState.pressure(phaseIdx); |
465 | |||
466 |
2/2✓ Branch 0 taken 6910497 times.
✓ Branch 1 taken 6910497 times.
|
13821006 | if (phaseIdx == gasPhaseIdx) |
467 | return 1.0; | ||
468 | |||
469 | else if (phaseIdx == liquidPhaseIdx) | ||
470 | { | ||
471 | // TODO: Should we use the vapor pressure of the mixture (brine) here? | ||
472 | // The presence of NaCl lowers the vapor pressure, thus, we would | ||
473 | // expect the fugacity coefficient to be lower as well. However, | ||
474 | // with the fugacity coefficient being dependent on the salinity, | ||
475 | // the equation system for the phase equilibria becomes non-linear | ||
476 | // and our constraint solvers assume linear system of equations. | ||
477 |
2/2✓ Branch 0 taken 2303499 times.
✓ Branch 1 taken 4606998 times.
|
6910503 | if (compIdx == H2OIdx) |
478 | 2303501 | return H2O::vaporPressure(T)/p; | |
479 | |||
480 |
2/2✓ Branch 0 taken 2303499 times.
✓ Branch 1 taken 2303499 times.
|
4607002 | else if (compIdx == AirIdx) |
481 | 4607002 | return BinaryCoeff::H2O_Air::henry(T)/p; | |
482 | |||
483 | // we assume nacl always stays in the liquid phase | ||
484 | else | ||
485 | return 0.0; | ||
486 | } | ||
487 | |||
488 | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | ||
489 | } | ||
490 | |||
491 | using Base<Scalar, ThisType>::diffusionCoefficient; | ||
492 | //! \copydoc Base<Scalar,ThisType>::diffusionCoefficient(const FluidState&,int,int) | ||
493 | template <class FluidState> | ||
494 | 24 | static Scalar diffusionCoefficient(const FluidState &fluidState, | |
495 | int phaseIdx, | ||
496 | int compIdx) | ||
497 | { | ||
498 |
7/16✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 12 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 12 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 12 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 12 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 12 times.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✓ Branch 27 taken 12 times.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
|
264 | DUNE_THROW(Dune::NotImplemented, "Diffusion coefficients"); |
499 | } | ||
500 | |||
501 | using Base<Scalar, ThisType>::binaryDiffusionCoefficient; | ||
502 | //! \copydoc Base<Scalar,ThisType>::binaryDiffusionCoefficient(const FluidState&,int,int,int) | ||
503 | template <class FluidState> | ||
504 | 9231008 | static Scalar binaryDiffusionCoefficient(const FluidState& fluidState, | |
505 | int phaseIdx, | ||
506 | int compIIdx, | ||
507 | int compJIdx) | ||
508 | { | ||
509 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9230972 times.
|
9231008 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
510 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9230972 times.
|
9231008 | assert(0 <= compIIdx && compIIdx < numComponents); |
511 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9230972 times.
|
9231008 | assert(0 <= compJIdx && compJIdx < numComponents); |
512 | |||
513 |
2/2✓ Branch 0 taken 2320445 times.
✓ Branch 1 taken 6910491 times.
|
9231008 | const auto T = fluidState.temperature(phaseIdx); |
514 |
2/2✓ Branch 0 taken 2320445 times.
✓ Branch 1 taken 6910491 times.
|
9231008 | const auto p = fluidState.pressure(phaseIdx); |
515 | |||
516 |
2/2✓ Branch 0 taken 2320457 times.
✓ Branch 1 taken 6910515 times.
|
9231008 | if (compIIdx > compJIdx) |
517 | 2320469 | std::swap(compIIdx, compJIdx); | |
518 | |||
519 |
2/2✓ Branch 0 taken 4607012 times.
✓ Branch 1 taken 4623960 times.
|
9231008 | if (phaseIdx == liquidPhaseIdx) |
520 | { | ||
521 |
4/4✓ Branch 0 taken 4607004 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2303501 times.
✓ Branch 3 taken 2303503 times.
|
4607030 | if(compIIdx == H2OIdx && compJIdx == AirIdx) |
522 | 4607010 | return H2O_Air::liquidDiffCoeff(T, p); | |
523 |
4/4✓ Branch 0 taken 2303503 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2303501 times.
✓ Branch 3 taken 2 times.
|
2303525 | else if (compIIdx == H2OIdx && compJIdx == NaClIdx) |
524 | 4607010 | return Brine::binaryDiffusionCoefficient(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx, Brine::H2OIdx, Brine::NaClIdx); | |
525 | else | ||
526 |
9/20✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 10 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
✓ Branch 20 taken 10 times.
✗ Branch 21 not taken.
✓ Branch 24 taken 10 times.
✗ Branch 25 not taken.
✓ Branch 27 taken 10 times.
✗ Branch 28 not taken.
✓ Branch 30 taken 10 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 10 times.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✓ Branch 36 taken 10 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
280 | DUNE_THROW(Dune::NotImplemented, "Binary diffusion coefficient of components " |
527 | << compIIdx << " and " << compJIdx | ||
528 | << " in phase " << phaseIdx); | ||
529 | } | ||
530 | else if (phaseIdx == gasPhaseIdx) | ||
531 | { | ||
532 |
4/4✓ Branch 0 taken 2320455 times.
✓ Branch 1 taken 2303505 times.
✓ Branch 2 taken 2320449 times.
✓ Branch 3 taken 6 times.
|
4623978 | if (compIIdx == H2OIdx && compJIdx == AirIdx) |
533 | 4640906 | return H2O_Air::gasDiffCoeff(T, p); | |
534 | |||
535 | // NaCl is expected to never be present in the gas phase. we need to | ||
536 | // return a diffusion coefficient that does not case numerical problems. | ||
537 | // We choose a very small value here. | ||
538 |
4/4✓ Branch 0 taken 2303503 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2303501 times.
|
2303525 | else if (compIIdx == AirIdx && compJIdx == NaClIdx) |
539 | return 1e-12; | ||
540 | |||
541 | else | ||
542 |
9/20✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 10 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
✓ Branch 20 taken 10 times.
✗ Branch 21 not taken.
✓ Branch 24 taken 10 times.
✗ Branch 25 not taken.
✓ Branch 27 taken 10 times.
✗ Branch 28 not taken.
✓ Branch 30 taken 10 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 10 times.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✓ Branch 36 taken 10 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
280 | DUNE_THROW(Dune::NotImplemented, "Binary diffusion coefficient of components " |
543 | << compIIdx << " and " << compJIdx | ||
544 | << " in phase " << phaseIdx); | ||
545 | } | ||
546 | |||
547 | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | ||
548 | } | ||
549 | |||
550 | using Base<Scalar, ThisType>::enthalpy; | ||
551 | /*! | ||
552 | * \brief Given a phase's composition, temperature and pressure, | ||
553 | * return its specific enthalpy \f$\mathrm{[J/kg]}\f$. | ||
554 | * \param fluidState The fluid state | ||
555 | * \param phaseIdx The index of the phase | ||
556 | * | ||
557 | * See: | ||
558 | * Class 2000 | ||
559 | * Theorie und numerische Modellierung nichtisothermer Mehrphasenprozesse in NAPL-kontaminierten porösen Medien | ||
560 | * Chapter 2.1.13 Innere Energie, Wäremekapazität, Enthalpie \cite A3:class:2001 | ||
561 | * | ||
562 | * Formula (2.42): | ||
563 | * the specific enthalpy of a gas phase result from the sum of (enthalpies*mass fraction) of the components | ||
564 | * For the calculation of enthalpy of brine we refer to (Michaelides 1981) | ||
565 | * | ||
566 | * \note For the phase enthalpy the contribution of gas-molecules in the liquid phase | ||
567 | * is neglected. This contribution is probably not big. Somebody would have to | ||
568 | * find out the enthalpy of solution for this system. ... | ||
569 | */ | ||
570 | template <class FluidState> | ||
571 | 172640 | static Scalar enthalpy(const FluidState& fluidState, int phaseIdx) | |
572 | { | ||
573 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 172636 times.
|
172640 | assert(0 <= phaseIdx && phaseIdx < numPhases); |
574 | |||
575 |
2/2✓ Branch 0 taken 86316 times.
✓ Branch 1 taken 86316 times.
|
172640 | const Scalar T = fluidState.temperature(phaseIdx); |
576 |
2/2✓ Branch 0 taken 86316 times.
✓ Branch 1 taken 86316 times.
|
172640 | const Scalar p = fluidState.pressure(phaseIdx); |
577 | |||
578 |
2/2✓ Branch 0 taken 86318 times.
✓ Branch 1 taken 86318 times.
|
172640 | if (phaseIdx == liquidPhaseIdx) |
579 | 172640 | return Brine::enthalpy(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx); | |
580 | else if (phaseIdx == gasPhaseIdx) | ||
581 | { | ||
582 | // This assumes NaCl not to be present in the gas phase | ||
583 | 86320 | const Scalar XAir = fluidState.massFraction(gasPhaseIdx, AirIdx); | |
584 | 86320 | const Scalar XH2O = fluidState.massFraction(gasPhaseIdx, H2OIdx); | |
585 | 86320 | return XH2O*H2O::gasEnthalpy(T, p) + XAir*Air::gasEnthalpy(T, p); | |
586 | } | ||
587 | |||
588 | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | ||
589 | } | ||
590 | |||
591 | /*! | ||
592 | * \brief Returns the specific enthalpy \f$\mathrm{[J/kg]}\f$ of a component in a specific phase | ||
593 | * \param fluidState The fluid state | ||
594 | * \param phaseIdx The index of the phase | ||
595 | * \param componentIdx The index of the component | ||
596 | */ | ||
597 | template <class FluidState> | ||
598 | 33896 | static Scalar componentEnthalpy(const FluidState& fluidState, int phaseIdx, int componentIdx) | |
599 | { | ||
600 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33896 times.
|
33896 | const Scalar T = fluidState.temperature(gasPhaseIdx); |
601 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33896 times.
|
33896 | const Scalar p = fluidState.pressure(gasPhaseIdx); |
602 | |||
603 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33896 times.
|
33896 | if (phaseIdx == liquidPhaseIdx) |
604 | ✗ | DUNE_THROW(Dune::NotImplemented, "The component enthalpies in the liquid phase are not implemented."); | |
605 | |||
606 |
1/2✓ Branch 0 taken 33896 times.
✗ Branch 1 not taken.
|
33896 | else if (phaseIdx == gasPhaseIdx) |
607 | { | ||
608 |
2/2✓ Branch 0 taken 16948 times.
✓ Branch 1 taken 16948 times.
|
33896 | if (componentIdx == H2OIdx) |
609 | 16948 | return H2O::gasEnthalpy(T, p); | |
610 |
1/2✓ Branch 0 taken 16948 times.
✗ Branch 1 not taken.
|
16948 | else if (componentIdx == AirIdx) |
611 | 33896 | return Air::gasEnthalpy(T, p); | |
612 | ✗ | else if (componentIdx == NaClIdx) | |
613 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Implementation assumes NaCl not to be present in gas phase"); | |
614 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid component index " << componentIdx); | |
615 | } | ||
616 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | |
617 | } | ||
618 | |||
619 | using Base<Scalar, ThisType>::thermalConductivity; | ||
620 | /*! | ||
621 | * \brief Thermal conductivity of a fluid phase \f$\mathrm{[W/(m K)]}\f$. | ||
622 | * \param fluidState An arbitrary fluid state | ||
623 | * \param phaseIdx The index of the fluid phase to consider | ||
624 | * | ||
625 | * \note We assume an ideal mixture for the gas-phase thermal conductivity, as a first approximation. | ||
626 | * In porous media, gas-phase thermal conductivities are negligible, as they are | ||
627 | * orders of magnitude lower than the thermal conductivity in solid and liquids (gas: 0.0x compared to solid: x.). | ||
628 | * However, moisture can have a significant influce on the thermal conductivity of moist air, see e.g. \cite Beirao2012, | ||
629 | * which could be relevant for free-flow systems. | ||
630 | */ | ||
631 | template <class FluidState> | ||
632 | 189588 | static Scalar thermalConductivity(const FluidState& fluidState, int phaseIdx) | |
633 | { | ||
634 |
2/2✓ Branch 0 taken 86318 times.
✓ Branch 1 taken 103266 times.
|
189588 | if (phaseIdx == liquidPhaseIdx) |
635 | 172640 | return Brine::thermalConductivity(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx); | |
636 | // This assumes NaCl not to be present in the gas phase | ||
637 |
1/2✓ Branch 0 taken 103266 times.
✗ Branch 1 not taken.
|
103268 | else if (phaseIdx == gasPhaseIdx) |
638 | 309796 | return Air::gasThermalConductivity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)) * fluidState.massFraction(gasPhaseIdx, AirIdx) | |
639 | 309796 | + H2O::gasThermalConductivity(fluidState.temperature(phaseIdx), fluidState.pressure(phaseIdx)) * fluidState.massFraction(gasPhaseIdx, H2OIdx); | |
640 | |||
641 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | |
642 | } | ||
643 | |||
644 | using Base<Scalar, ThisType>::heatCapacity; | ||
645 | /*! | ||
646 | * \brief Specific isobaric heat capacity of a fluid phase. | ||
647 | * \f$\mathrm{[J/(kg*K)}\f$. | ||
648 | * \param fluidState An arbitrary fluid state | ||
649 | * \param phaseIdx The index of the fluid phase to consider | ||
650 | * | ||
651 | * \note We assume an ideal mixture for the gas-phase specific isobaric heat capacity, as a first approximation. | ||
652 | * In porous media, gas-phase heat capacities are, due to the usually low densities of gases, negligible anyway. | ||
653 | * A better implementation might be relevant for free-flow systems. | ||
654 | */ | ||
655 | template <class FluidState> | ||
656 | 4 | static Scalar heatCapacity(const FluidState &fluidState, int phaseIdx) | |
657 | { | ||
658 | 4 | const Scalar T = fluidState.temperature(phaseIdx); | |
659 | 4 | const Scalar p = fluidState.pressure(phaseIdx); | |
660 | |||
661 | 4 | if (phaseIdx == liquidPhaseIdx) | |
662 | 4 | return Brine::heatCapacity(BrineAdapter<FluidState>(fluidState), Brine::liquidPhaseIdx); | |
663 | |||
664 | // We assume NaCl not to be present in the gas phase here | ||
665 | 2 | else if (phaseIdx == gasPhaseIdx) | |
666 | 2 | return Air::gasHeatCapacity(T, p)*fluidState.massFraction(gasPhaseIdx, AirIdx) | |
667 | 2 | + H2O::gasHeatCapacity(T, p)*fluidState.massFraction(gasPhaseIdx, H2OIdx); | |
668 | |||
669 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid phase index " << phaseIdx); | |
670 | } | ||
671 | }; | ||
672 | |||
673 | } // end namespace FluidSystems | ||
674 | } // end namespace Dumux | ||
675 | |||
676 | #endif | ||
677 |