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 ConstraintSolvers | ||
10 | * \brief Determines the phase compositions, pressures and saturations | ||
11 | * given the total mass of all components. | ||
12 | */ | ||
13 | #ifndef DUMUX_NCP_FLASH_HH | ||
14 | #define DUMUX_NCP_FLASH_HH | ||
15 | |||
16 | #include <dune/common/fvector.hh> | ||
17 | #include <dune/common/fmatrix.hh> | ||
18 | |||
19 | #include <dumux/common/exceptions.hh> | ||
20 | #include <dumux/material/fluidmatrixinteractions/mp/mpadapter.hh> | ||
21 | |||
22 | namespace Dumux { | ||
23 | |||
24 | /*! | ||
25 | * \ingroup ConstraintSolvers | ||
26 | * \brief Determines the phase compositions, pressures and saturations | ||
27 | * given the total mass of all components. | ||
28 | * | ||
29 | * In a M-phase, N-component context, we have the following | ||
30 | * unknowns: | ||
31 | * | ||
32 | * - M pressures | ||
33 | * - M saturations | ||
34 | * - M*N mole fractions | ||
35 | * | ||
36 | * This sums up to M*(N + 2). On the equations side of things, | ||
37 | * we have: | ||
38 | * | ||
39 | * - (M - 1)*N equation stemming from the fact that the | ||
40 | * fugacity of any component is the same in all phases | ||
41 | * - 1 equation from the closure condition of all saturations | ||
42 | * (they sum up to 1) | ||
43 | * - M - 1 constraints from the capillary pressures | ||
44 | * \f$(-> p_\beta = p_\alpha + p_c\alpha,\beta)\f$ | ||
45 | * - N constraints from the fact that the total mass of each | ||
46 | * component is given \f$(-> sum_\alpha rhoMolar_\alpha * | ||
47 | * x_\alpha^\kappa = const)\f$ | ||
48 | * - M model constraints. Here we use the NCP constraints | ||
49 | * (-> 0 = min \f$ {S_\alpha, 1 - \sum_\kappa x_\alpha^\kappa}\f$) | ||
50 | * | ||
51 | * this also sums up to M*(N + 2). | ||
52 | * | ||
53 | * We use the following catches: Capillary pressures are taken | ||
54 | * into account explicitly, so that only the pressure of the first | ||
55 | * phase is solved implicitly, also the closure condition for the | ||
56 | * saturations is taken into account explicitly, which means, that | ||
57 | * we don't need to implicitly solve for the last | ||
58 | * saturation. These two measures reduce the number of unknowns to | ||
59 | * M*(N + 1), namely: | ||
60 | * | ||
61 | * - 1 pressure | ||
62 | * - M - 1 saturations | ||
63 | * - M*N mole fractions | ||
64 | */ | ||
65 | template <class Scalar, class FluidSystem> | ||
66 | class NcpFlash | ||
67 | { | ||
68 | enum { numPhases = FluidSystem::numPhases }; | ||
69 | enum { numComponents = FluidSystem::numComponents }; | ||
70 | |||
71 | using ParameterCache = typename FluidSystem::ParameterCache; | ||
72 | |||
73 | static constexpr int numEq = numPhases*(numComponents + 1); | ||
74 | |||
75 | using Matrix = Dune::FieldMatrix<Scalar, numEq, numEq>; | ||
76 | using Vector = Dune::FieldVector<Scalar, numEq>; | ||
77 | |||
78 | public: | ||
79 | using ComponentVector = Dune::FieldVector<Scalar, numComponents>; | ||
80 | |||
81 | /*! | ||
82 | * \brief Construct a new flash | ||
83 | * \param wettingPhaseIdx the phase index of the wetting phase | ||
84 | */ | ||
85 | 56 | explicit NcpFlash(int wettingPhaseIdx = 0) | |
86 | 56 | : wettingPhaseIdx_(wettingPhaseIdx) | |
87 | {} | ||
88 | |||
89 | /*! | ||
90 | * \brief Guess initial values for all quantities. | ||
91 | * \param fluidState Thermodynamic state of the fluids | ||
92 | * \param paramCache Container for cache parameters | ||
93 | * \param globalMolarities | ||
94 | */ | ||
95 | template <class FluidState> | ||
96 | 5 | void guessInitial(FluidState &fluidState, | |
97 | ParameterCache ¶mCache, | ||
98 | const ComponentVector &globalMolarities) | ||
99 | { | ||
100 | // the sum of all molarities | ||
101 | 5 | Scalar sumMoles = 0; | |
102 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 15 times.
|
20 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) |
103 | 30 | sumMoles += globalMolarities[compIdx]; | |
104 | |||
105 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
|
16 | for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { |
106 | // composition | ||
107 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 11 times.
|
48 | for (int compIdx = 0; compIdx < numComponents; ++ compIdx) |
108 | 37 | fluidState.setMoleFraction(phaseIdx, | |
109 | compIdx, | ||
110 | 74 | globalMolarities[compIdx]/sumMoles); | |
111 | |||
112 | // pressure. use atmospheric pressure as initial guess | ||
113 | 11 | fluidState.setPressure(phaseIdx, 1.0135e5); | |
114 | |||
115 | // saturation. assume all fluids to be equally distributed | ||
116 | 22 | fluidState.setSaturation(phaseIdx, 1.0/numPhases); | |
117 | } | ||
118 | |||
119 | // set the fugacity coefficients of all components in all phases | ||
120 | paramCache.updateAll(fluidState); | ||
121 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
|
16 | for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { |
122 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 11 times.
|
48 | for (int compIdx = 0; compIdx < numComponents; ++ compIdx) { |
123 | 37 | Scalar phi = FluidSystem::fugacityCoefficient(fluidState, paramCache, phaseIdx, compIdx); | |
124 | 74 | fluidState.setFugacityCoefficient(phaseIdx, compIdx, phi); | |
125 | } | ||
126 | } | ||
127 | 5 | } | |
128 | |||
129 | /*! | ||
130 | * \brief Calculates the chemical equilibrium from the component | ||
131 | * fugacities in a phase. | ||
132 | * \param fluidState Thermodynamic state of the fluids | ||
133 | * \param paramCache Container for cache parameters | ||
134 | * \param globalMolarities | ||
135 | * \param material The material law object | ||
136 | * | ||
137 | * The phase's fugacities must already be set. | ||
138 | */ | ||
139 | template <class MaterialLaw, class FluidState> | ||
140 | 664 | void solve(FluidState &fluidState, | |
141 | ParameterCache ¶mCache, | ||
142 | const MaterialLaw& material, | ||
143 | const ComponentVector &globalMolarities) | ||
144 | { | ||
145 | ///////////////////////// | ||
146 | // Newton method | ||
147 | ///////////////////////// | ||
148 | |||
149 | // Jacobian matrix | ||
150 | 664 | Matrix J; | |
151 | // solution, i.e. phase composition | ||
152 | 664 | Vector deltaX; | |
153 | // right hand side | ||
154 | 664 | Vector b; | |
155 | |||
156 | // make the fluid state consistent with the fluid system. | ||
157 | 664 | completeFluidState_(fluidState, paramCache, material); | |
158 | |||
159 | /* | ||
160 | std::cout << "--------------------\n"; | ||
161 | std::cout << "globalMolarities: "; | ||
162 | for (int compIdx = 0; compIdx < numComponents; ++ compIdx) | ||
163 | std::cout << globalMolarities[compIdx] << " "; | ||
164 | std::cout << "\n"; | ||
165 | */ | ||
166 | |||
167 | const int nMax = 50; // <- maximum number of newton iterations | ||
168 |
1/2✓ Branch 0 taken 1744 times.
✗ Branch 1 not taken.
|
1744 | for (int nIdx = 0; nIdx < nMax; ++nIdx) { |
169 | // calculate Jacobian matrix and right hand side | ||
170 | 1744 | linearize_(J, b, fluidState, paramCache, material, globalMolarities); | |
171 | |||
172 | // Solve J*x = b | ||
173 | deltaX = 0; | ||
174 | |||
175 | try { | ||
176 | |||
177 | // simple preconditioning of the linear system to reduce condition number | ||
178 | int eqIdx = 0; | ||
179 |
2/2✓ Branch 0 taken 11798 times.
✓ Branch 1 taken 1744 times.
|
13542 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) |
180 | { | ||
181 |
2/2✓ Branch 0 taken 23432 times.
✓ Branch 1 taken 11798 times.
|
35230 | for (int phaseIdx = 1; phaseIdx < numPhases; ++phaseIdx) |
182 | { | ||
183 | 46864 | J[eqIdx] *= 10e-7; // roughly the magnitude of the fugacities | |
184 | 23432 | b[eqIdx] *= 10e-7; | |
185 | 23432 | ++eqIdx; | |
186 | } | ||
187 | } | ||
188 | |||
189 |
1/2✓ Branch 1 taken 1744 times.
✗ Branch 2 not taken.
|
1744 | J.solve(deltaX, b); |
190 | |||
191 | } | ||
192 | ✗ | catch (Dune::FMatrixError &e) | |
193 | { | ||
194 | /* | ||
195 | printFluidState_(fluidState); | ||
196 | std::cout << "error: " << e << "\n"; | ||
197 | std::cout << "b: " << b << "\n"; | ||
198 | std::cout << "J: " << J << "\n"; | ||
199 | */ | ||
200 | |||
201 | ✗ | throw NumericalProblem(e.what()); | |
202 | } | ||
203 | |||
204 | /* | ||
205 | printFluidState_(fluidState); | ||
206 | std::cout << "J:\n"; | ||
207 | for (int i = 0; i < numEq; ++i) { | ||
208 | for (int j = 0; j < numEq; ++j) { | ||
209 | std::ostringstream os; | ||
210 | os << J[i][j]; | ||
211 | |||
212 | std::string s(os.str()); | ||
213 | do { | ||
214 | s += " "; | ||
215 | } while (s.size() < 20); | ||
216 | std::cout << s; | ||
217 | } | ||
218 | std::cout << "\n"; | ||
219 | } | ||
220 | |||
221 | std::cout << "deltaX: " << deltaX << "\n"; | ||
222 | std::cout << "---------------\n"; | ||
223 | */ | ||
224 | |||
225 | // update the fluid quantities. | ||
226 | 1744 | Scalar relError = update_(fluidState, paramCache, material, deltaX); | |
227 | |||
228 |
2/2✓ Branch 0 taken 664 times.
✓ Branch 1 taken 1080 times.
|
1744 | if (relError < 1e-9) |
229 | 664 | return; | |
230 | } | ||
231 | |||
232 | /* | ||
233 | printFluidState_(fluidState); | ||
234 | std::cout << "globalMolarities: "; | ||
235 | for (int compIdx = 0; compIdx < numComponents; ++ compIdx) | ||
236 | std::cout << globalMolarities[compIdx] << " "; | ||
237 | std::cout << "\n"; | ||
238 | */ | ||
239 | |||
240 | ✗ | DUNE_THROW(NumericalProblem, | |
241 | "Flash calculation failed." | ||
242 | " {c_alpha^kappa} = {" << globalMolarities << "}, T = " | ||
243 | << fluidState.temperature(/*phaseIdx=*/0)); | ||
244 | } | ||
245 | |||
246 | |||
247 | protected: | ||
248 | template <class FluidState> | ||
249 | void printFluidState_(const FluidState &fs) | ||
250 | { | ||
251 | std::cout << "saturations: "; | ||
252 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) | ||
253 | std::cout << fs.saturation(phaseIdx) << " "; | ||
254 | std::cout << "\n"; | ||
255 | |||
256 | std::cout << "pressures: "; | ||
257 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) | ||
258 | std::cout << fs.pressure(phaseIdx) << " "; | ||
259 | std::cout << "\n"; | ||
260 | |||
261 | std::cout << "densities: "; | ||
262 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) | ||
263 | std::cout << fs.density(phaseIdx) << " "; | ||
264 | std::cout << "\n"; | ||
265 | |||
266 | std::cout << "molar densities: "; | ||
267 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) | ||
268 | std::cout << fs.molarDensity(phaseIdx) << " "; | ||
269 | std::cout << "\n"; | ||
270 | |||
271 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { | ||
272 | std::cout << "composition " << FluidSystem::phaseName(phaseIdx) << "Phase: "; | ||
273 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { | ||
274 | std::cout << fs.moleFraction(phaseIdx, compIdx) << " "; | ||
275 | } | ||
276 | std::cout << "\n"; | ||
277 | } | ||
278 | |||
279 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { | ||
280 | std::cout << "fugacities " << FluidSystem::phaseName(phaseIdx) << "Phase: "; | ||
281 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { | ||
282 | std::cout << fs.fugacity(phaseIdx, compIdx) << " "; | ||
283 | } | ||
284 | std::cout << "\n"; | ||
285 | } | ||
286 | |||
287 | std::cout << "global component molarities: "; | ||
288 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { | ||
289 | Scalar sum = 0; | ||
290 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { | ||
291 | sum += fs.saturation(phaseIdx)*fs.molarity(phaseIdx, compIdx); | ||
292 | } | ||
293 | std::cout << sum << " "; | ||
294 | } | ||
295 | std::cout << "\n"; | ||
296 | } | ||
297 | |||
298 | template <class MaterialLaw, class FluidState> | ||
299 | 1744 | void linearize_(Matrix &J, | |
300 | Vector &b, | ||
301 | FluidState &fluidState, | ||
302 | ParameterCache ¶mCache, | ||
303 | const MaterialLaw& material, | ||
304 | const ComponentVector &globalMolarities) | ||
305 | { | ||
306 | 1744 | FluidState origFluidState(fluidState); | |
307 | 1662 | ParameterCache origParamCache(paramCache); | |
308 | |||
309 | 1744 | Vector tmp; | |
310 | |||
311 | // reset jacobian | ||
312 | 1744 | J = 0; | |
313 | |||
314 | 1744 | calculateDefect_(b, fluidState, fluidState, globalMolarities); | |
315 | |||
316 | // assemble jacobian matrix | ||
317 |
2/2✓ Branch 1 taken 40380 times.
✓ Branch 2 taken 1744 times.
|
42124 | for (int pvIdx = 0; pvIdx < numEq; ++ pvIdx) { |
318 | //////// | ||
319 | // approximately calculate partial derivatives of the | ||
320 | // fugacity defect of all components in regard to the mole | ||
321 | // fraction of the i-th component. This is done via | ||
322 | // forward differences | ||
323 | |||
324 | // deviate the mole fraction of the i-th component | ||
325 | 40380 | Scalar x_i = getQuantity_(fluidState, pvIdx); | |
326 | 80760 | const Scalar eps = 1e-8/quantityWeight_(fluidState, pvIdx); | |
327 | 40380 | setQuantity_(fluidState, paramCache, material, pvIdx, x_i + eps); | |
328 | |||
329 | // compute derivative of the defect | ||
330 | 40380 | calculateDefect_(tmp, origFluidState, fluidState, globalMolarities); | |
331 | 40380 | tmp -= b; | |
332 | tmp /= eps; | ||
333 | |||
334 | // store derivative in jacobian matrix | ||
335 |
2/2✓ Branch 0 taken 960264 times.
✓ Branch 1 taken 40380 times.
|
1000644 | for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) |
336 | 3841056 | J[eqIdx][pvIdx] = tmp[eqIdx]; | |
337 | |||
338 | // fluid state and parameter cache to their original values | ||
339 | 40380 | fluidState = origFluidState; | |
340 | 39888 | paramCache = origParamCache; | |
341 | |||
342 | // end forward differences | ||
343 | //////// | ||
344 | } | ||
345 | 1744 | } | |
346 | |||
347 | template <class FluidState> | ||
348 | 42124 | void calculateDefect_(Vector &b, | |
349 | const FluidState &fluidStateEval, | ||
350 | const FluidState &fluidState, | ||
351 | const ComponentVector &globalMolarities) | ||
352 | { | ||
353 | 42124 | int eqIdx = 0; | |
354 | |||
355 | // fugacity of any component must be equal in all phases | ||
356 |
2/2✓ Branch 0 taken 291998 times.
✓ Branch 1 taken 42124 times.
|
334122 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
357 |
2/2✓ Branch 0 taken 582848 times.
✓ Branch 1 taken 291998 times.
|
874846 | for (int phaseIdx = 1; phaseIdx < numPhases; ++phaseIdx) { |
358 | 1165696 | b[eqIdx] = | |
359 | 1748544 | fluidState.fugacity(/*phaseIdx=*/0, compIdx) - | |
360 | fluidState.fugacity(phaseIdx, compIdx); | ||
361 | 582848 | ++eqIdx; | |
362 | } | ||
363 | } | ||
364 | |||
365 |
1/2✓ Branch 0 taken 42124 times.
✗ Branch 1 not taken.
|
42124 | assert(eqIdx == numComponents*(numPhases - 1)); |
366 | |||
367 | // the fact saturations must sum up to 1 is included explicitly! | ||
368 | |||
369 | // capillary pressures are explicitly included! | ||
370 | |||
371 | // global molarities are given | ||
372 |
2/2✓ Branch 0 taken 42124 times.
✓ Branch 1 taken 291998 times.
|
334122 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
373 | 291998 | b[eqIdx] = 0.0; | |
374 |
2/2✓ Branch 0 taken 874846 times.
✓ Branch 1 taken 291998 times.
|
1166844 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
375 | 874846 | b[eqIdx] += | |
376 | fluidState.saturation(phaseIdx) | ||
377 | 2624538 | * fluidState.molarity(phaseIdx, compIdx); | |
378 | } | ||
379 | |||
380 | 583996 | b[eqIdx] -= globalMolarities[compIdx]; | |
381 | 291998 | ++eqIdx; | |
382 | } | ||
383 | |||
384 | // model assumptions (-> non-linear complementarity functions) | ||
385 | // must be adhered | ||
386 |
2/2✓ Branch 0 taken 125798 times.
✓ Branch 1 taken 42124 times.
|
167922 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
387 | Scalar sumMoleFracEval = 0.0; | ||
388 |
2/2✓ Branch 0 taken 874846 times.
✓ Branch 1 taken 125798 times.
|
1000644 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) |
389 | 1749692 | sumMoleFracEval += fluidStateEval.moleFraction(phaseIdx, compIdx); | |
390 | |||
391 |
4/4✓ Branch 0 taken 82760 times.
✓ Branch 1 taken 43038 times.
✓ Branch 2 taken 82760 times.
✓ Branch 3 taken 43038 times.
|
251596 | if (1.0 - sumMoleFracEval > fluidStateEval.saturation(phaseIdx)) { |
392 | 129114 | b[eqIdx] = fluidState.saturation(phaseIdx); | |
393 | } | ||
394 | else { | ||
395 | Scalar sumMoleFrac = 0.0; | ||
396 |
2/2✓ Branch 0 taken 574770 times.
✓ Branch 1 taken 82760 times.
|
657530 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) |
397 | 1149540 | sumMoleFrac += fluidState.moleFraction(phaseIdx, compIdx); | |
398 | 165520 | b[eqIdx] = 1.0 - sumMoleFrac; | |
399 | } | ||
400 | |||
401 | 125798 | ++eqIdx; | |
402 | } | ||
403 | 42124 | } | |
404 | |||
405 | template <class MaterialLaw, class FluidState> | ||
406 | 1744 | Scalar update_(FluidState &fluidState, | |
407 | ParameterCache ¶mCache, | ||
408 | const MaterialLaw& material, | ||
409 | const Vector &deltaX) | ||
410 | { | ||
411 | 1744 | Scalar relError = 0; | |
412 |
2/2✓ Branch 0 taken 40380 times.
✓ Branch 1 taken 1744 times.
|
42124 | for (int pvIdx = 0; pvIdx < numEq; ++ pvIdx) { |
413 | 40380 | Scalar tmp = getQuantity_(fluidState, pvIdx); | |
414 |
2/2✓ Branch 0 taken 38636 times.
✓ Branch 1 taken 1744 times.
|
40380 | Scalar delta = deltaX[pvIdx]; |
415 | using std::max; | ||
416 | using std::abs; | ||
417 | using std::min; | ||
418 |
4/4✓ Branch 0 taken 38636 times.
✓ Branch 1 taken 1744 times.
✓ Branch 2 taken 2418 times.
✓ Branch 3 taken 37962 times.
|
79016 | relError = max(relError, abs(delta)*quantityWeight_(fluidState, pvIdx)); |
419 | |||
420 |
2/2✓ Branch 0 taken 82 times.
✓ Branch 1 taken 410 times.
|
80268 | if (isSaturationIdx_(pvIdx)) { |
421 | // dampen to at most 20% change in saturation per | ||
422 | // iteration | ||
423 |
4/4✓ Branch 0 taken 3394 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3398 times.
✓ Branch 3 taken 8 times.
|
10198 | delta = min(0.2, max(-0.2, delta)); |
424 | } | ||
425 | 73948 | else if (isMoleFracIdx_(pvIdx)) { | |
426 | // dampen to at most 15% change in mole fraction per | ||
427 | // iteration | ||
428 |
4/4✓ Branch 0 taken 35146 times.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 35072 times.
✓ Branch 3 taken 158 times.
|
105448 | delta = min(0.15, max(-0.15, delta)); |
429 | } | ||
430 |
1/2✓ Branch 0 taken 1744 times.
✗ Branch 1 not taken.
|
1744 | else if (isPressureIdx_(pvIdx)) { |
431 | // dampen to at most 15% change in pressure per | ||
432 | // iteration | ||
433 |
8/8✓ Branch 0 taken 1534 times.
✓ Branch 1 taken 210 times.
✓ Branch 2 taken 1534 times.
✓ Branch 3 taken 210 times.
✓ Branch 4 taken 1641 times.
✓ Branch 5 taken 103 times.
✓ Branch 6 taken 1641 times.
✓ Branch 7 taken 103 times.
|
8197 | delta = min(0.15*fluidState.pressure(0), max(-0.15*fluidState.pressure(0), delta)); |
434 | } | ||
435 | |||
436 | 40380 | setQuantityRaw_(fluidState, pvIdx, tmp - delta); | |
437 | } | ||
438 | |||
439 | // make sure all saturations, pressures and mole fractions are non-negative | ||
440 | Scalar sumSat = 0; | ||
441 |
2/2✓ Branch 0 taken 5150 times.
✓ Branch 1 taken 1744 times.
|
6894 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
442 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5148 times.
|
5150 | Scalar value = fluidState.saturation(phaseIdx); |
443 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5148 times.
|
5150 | if (value < -0.05) { |
444 | 2 | value = -0.05; | |
445 | 2 | fluidState.setSaturation(phaseIdx, value); | |
446 | } | ||
447 | 5150 | sumSat += value; | |
448 | |||
449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5150 times.
|
5150 | value = fluidState.pressure(phaseIdx); |
450 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5150 times.
|
5150 | if (value < 0) |
451 | ✗ | fluidState.setPressure(phaseIdx, 0.0); | |
452 | |||
453 | bool allMoleFractionsAreZero = true; | ||
454 |
2/2✓ Branch 0 taken 35230 times.
✓ Branch 1 taken 5150 times.
|
40380 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
455 |
2/2✓ Branch 0 taken 3334 times.
✓ Branch 1 taken 31896 times.
|
35230 | value = fluidState.moleFraction(phaseIdx, compIdx); |
456 |
2/2✓ Branch 0 taken 3334 times.
✓ Branch 1 taken 31896 times.
|
35230 | if (value > 0) |
457 | allMoleFractionsAreZero = false; | ||
458 |
2/2✓ Branch 0 taken 2433 times.
✓ Branch 1 taken 901 times.
|
3334 | else if (value < 0) |
459 | 2433 | fluidState.setMoleFraction(phaseIdx, compIdx, 0.0); | |
460 | } | ||
461 | |||
462 | // set at least one mole fraction to a positive value | ||
463 | // to prevent breakdown of the linear solver in completeFluidState_ | ||
464 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5147 times.
|
5150 | if (allMoleFractionsAreZero) |
465 | 3 | fluidState.setMoleFraction(phaseIdx, /*compIdx=*/0, 1e-10); | |
466 | } | ||
467 | |||
468 | // last saturation | ||
469 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1727 times.
|
1744 | if (sumSat > 1.05) { |
470 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 17 times.
|
62 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
471 | 45 | Scalar value = fluidState.saturation(phaseIdx)/(0.95*sumSat); | |
472 | 90 | fluidState.setSaturation(phaseIdx, value); | |
473 | } | ||
474 | } | ||
475 | |||
476 | 1744 | completeFluidState_(fluidState, paramCache, material); | |
477 | |||
478 | 1744 | return relError; | |
479 | } | ||
480 | |||
481 | template <class MaterialLaw, class FluidState> | ||
482 | 2408 | void completeFluidState_(FluidState &fluidState, | |
483 | ParameterCache ¶mCache, | ||
484 | const MaterialLaw& material) | ||
485 | { | ||
486 | // calculate the saturation of the last phase as a function of | ||
487 | // the other saturations | ||
488 | 2408 | Scalar sumSat = 0.0; | |
489 |
2/2✓ Branch 0 taken 4730 times.
✓ Branch 1 taken 2408 times.
|
7138 | for (int phaseIdx = 0; phaseIdx < numPhases - 1; ++phaseIdx) |
490 | 9460 | sumSat += fluidState.saturation(phaseIdx); | |
491 | 2408 | fluidState.setSaturation(/*phaseIdx=*/numPhases - 1, 1.0 - sumSat); | |
492 | |||
493 | // update the pressures using the material law (saturations | ||
494 | // and first pressure are already set because it is implicitly | ||
495 | // solved for.) | ||
496 | 2408 | const auto pc = material.capillaryPressures(fluidState, wettingPhaseIdx_); | |
497 |
2/2✓ Branch 0 taken 4730 times.
✓ Branch 1 taken 2408 times.
|
7138 | for (int phaseIdx = 1; phaseIdx < numPhases; ++phaseIdx) |
498 | 4730 | fluidState.setPressure(phaseIdx, | |
499 | fluidState.pressure(0) | ||
500 | 18920 | + (pc[phaseIdx] - pc[0])); | |
501 | |||
502 | // update the parameter cache | ||
503 | paramCache.updateAll(fluidState, /*except=*/ParameterCache::Temperature); | ||
504 | |||
505 | // update all densities and fugacity coefficients | ||
506 |
2/2✓ Branch 0 taken 7138 times.
✓ Branch 1 taken 2408 times.
|
9546 | for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { |
507 | |||
508 | 7138 | Scalar rho = FluidSystem::density(fluidState, paramCache, phaseIdx); | |
509 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6966 times.
|
7138 | Scalar rhoMolar = FluidSystem::molarDensity(fluidState, paramCache, phaseIdx); |
510 | 7138 | fluidState.setDensity(phaseIdx, rho); | |
511 | 7138 | fluidState.setMolarDensity(phaseIdx, rhoMolar); | |
512 | |||
513 |
2/2✓ Branch 0 taken 49106 times.
✓ Branch 1 taken 7138 times.
|
56244 | for (int compIdx = 0; compIdx < numComponents; ++ compIdx) { |
514 | 49106 | Scalar phi = FluidSystem::fugacityCoefficient( fluidState, paramCache, phaseIdx, compIdx); | |
515 | 98212 | fluidState.setFugacityCoefficient(phaseIdx, compIdx, phi); | |
516 | } | ||
517 | } | ||
518 | 2408 | } | |
519 | |||
520 | ✗ | bool isPressureIdx_(int pvIdx) | |
521 | ✗ | { return pvIdx == 0; } | |
522 | |||
523 | ✗ | bool isSaturationIdx_(int pvIdx) | |
524 |
2/2✓ Branch 0 taken 3324 times.
✓ Branch 1 taken 36564 times.
|
39888 | { return 1 <= pvIdx && pvIdx < numPhases; } |
525 | |||
526 | ✗ | bool isMoleFracIdx_(int pvIdx) | |
527 |
2/2✓ Branch 0 taken 35230 times.
✓ Branch 1 taken 1744 times.
|
36974 | { return numPhases <= pvIdx && pvIdx < numPhases + numPhases*numComponents; } |
528 | |||
529 | // retrieves a quantity from the fluid state | ||
530 | template <class FluidState> | ||
531 | ✗ | Scalar getQuantity_(const FluidState &fs, int pvIdx) | |
532 | { | ||
533 | ✗ | assert(pvIdx < numEq); | |
534 | |||
535 | // first pressure | ||
536 | ✗ | if (pvIdx < 1) { | |
537 | ✗ | int phaseIdx = 0; | |
538 | ✗ | return fs.pressure(phaseIdx); | |
539 | } | ||
540 | // first M - 1 saturations | ||
541 | ✗ | else if (pvIdx < numPhases) { | |
542 | ✗ | int phaseIdx = pvIdx - 1; | |
543 | ✗ | return fs.saturation(phaseIdx); | |
544 | } | ||
545 | // mole fractions | ||
546 | else // if (pvIdx < numPhases + numPhases*numComponents) | ||
547 | { | ||
548 | ✗ | int phaseIdx = (pvIdx - numPhases)/numComponents; | |
549 | ✗ | int compIdx = (pvIdx - numPhases)%numComponents; | |
550 | ✗ | return fs.moleFraction(phaseIdx, compIdx); | |
551 | } | ||
552 | } | ||
553 | |||
554 | // set a quantity in the fluid state | ||
555 | template <class MaterialLaw, class FluidState> | ||
556 | 40380 | void setQuantity_(FluidState &fs, | |
557 | ParameterCache ¶mCache, | ||
558 | const MaterialLaw& material, | ||
559 | int pvIdx, | ||
560 | Scalar value) | ||
561 | { | ||
562 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40380 times.
|
40380 | assert(0 <= pvIdx && pvIdx < numEq); |
563 | |||
564 |
2/2✓ Branch 0 taken 1744 times.
✓ Branch 1 taken 38636 times.
|
40380 | if (pvIdx < 1) { // <- first pressure |
565 | 1744 | Scalar delta = value - fs.pressure(0); | |
566 | |||
567 | // set all pressures. here we assume that the capillary | ||
568 | // pressure does not depend on absolute pressure. | ||
569 |
2/2✓ Branch 0 taken 5068 times.
✓ Branch 1 taken 1826 times.
|
6894 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) |
570 | 15450 | fs.setPressure(phaseIdx, fs.pressure(phaseIdx) + delta); | |
571 | paramCache.updateAllPressures(fs); | ||
572 | |||
573 | // update all densities and fugacity coefficients | ||
574 |
2/2✓ Branch 0 taken 5150 times.
✓ Branch 1 taken 1744 times.
|
6894 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
575 | 5150 | Scalar rho = FluidSystem::density(fs, paramCache, phaseIdx); | |
576 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4986 times.
|
5150 | Scalar rhoMolar = FluidSystem::molarDensity(fs, paramCache, phaseIdx); |
577 | 5150 | fs.setDensity(phaseIdx, rho); | |
578 | 5150 | fs.setMolarDensity(phaseIdx, rhoMolar); | |
579 | |||
580 |
2/2✓ Branch 0 taken 35230 times.
✓ Branch 1 taken 5150 times.
|
40380 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
581 | 35230 | Scalar phi = FluidSystem::fugacityCoefficient(fs, paramCache, phaseIdx, compIdx); | |
582 | 70460 | fs.setFugacityCoefficient(phaseIdx, compIdx, phi); | |
583 | } | ||
584 | } | ||
585 | } | ||
586 |
2/2✓ Branch 0 taken 3406 times.
✓ Branch 1 taken 35230 times.
|
38636 | else if (pvIdx < numPhases) { // <- first M - 1 saturations |
587 | 3406 | Scalar delta = value - fs.saturation(/*phaseIdx=*/pvIdx - 1); | |
588 | 3406 | fs.setSaturation(/*phaseIdx=*/pvIdx - 1, value); | |
589 | |||
590 | // set last saturation (-> minus the change of the | ||
591 | // satuation of the other phase) | ||
592 | 6812 | fs.setSaturation(/*phaseIdx=*/numPhases - 1, | |
593 | 6812 | fs.saturation(numPhases - 1) - delta); | |
594 | |||
595 | // update all fluid pressures using the capillary pressure | ||
596 | // law | ||
597 | 3406 | const auto pc = material.capillaryPressures(fs, wettingPhaseIdx_); | |
598 |
2/2✓ Branch 0 taken 6730 times.
✓ Branch 1 taken 3406 times.
|
10136 | for (int phaseIdx = 1; phaseIdx < numPhases; ++phaseIdx) |
599 | 6730 | fs.setPressure(phaseIdx, | |
600 | fs.pressure(0) | ||
601 | 26920 | + (pc[phaseIdx] - pc[0])); | |
602 | paramCache.updateAllPressures(fs); | ||
603 | |||
604 | // update all densities and fugacity coefficients | ||
605 |
2/2✓ Branch 0 taken 10136 times.
✓ Branch 1 taken 3406 times.
|
13542 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
606 | 10136 | Scalar rho = FluidSystem::density(fs, paramCache, phaseIdx); | |
607 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9972 times.
|
10136 | Scalar rhoMolar = FluidSystem::molarDensity(fs, paramCache, phaseIdx); |
608 | 10136 | fs.setDensity(phaseIdx, rho); | |
609 | 10136 | fs.setMolarDensity(phaseIdx, rhoMolar); | |
610 | |||
611 |
2/2✓ Branch 0 taken 70132 times.
✓ Branch 1 taken 10136 times.
|
80268 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
612 | 70132 | Scalar phi = FluidSystem::fugacityCoefficient(fs, paramCache, phaseIdx, compIdx); | |
613 | 140264 | fs.setFugacityCoefficient(phaseIdx, compIdx, phi); | |
614 | } | ||
615 | } | ||
616 | } | ||
617 | else if (pvIdx < numPhases + numPhases*numComponents) // <- mole fractions | ||
618 | { | ||
619 | 35230 | int phaseIdx = (pvIdx - numPhases)/numComponents; | |
620 | 35230 | int compIdx = (pvIdx - numPhases)%numComponents; | |
621 | |||
622 | 35230 | fs.setMoleFraction(phaseIdx, compIdx, value); | |
623 | 35230 | paramCache.updateSingleMoleFraction(fs, phaseIdx, compIdx); | |
624 | |||
625 | // update the density of the phase | ||
626 | 35230 | Scalar rho = FluidSystem::density(fs, paramCache, phaseIdx); | |
627 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34902 times.
|
35230 | Scalar rhoMolar = FluidSystem::molarDensity(fs, paramCache, phaseIdx); |
628 |
2/2✓ Branch 0 taken 23268 times.
✓ Branch 1 taken 11634 times.
|
35230 | fs.setDensity(phaseIdx, rho); |
629 |
2/2✓ Branch 0 taken 23268 times.
✓ Branch 1 taken 11634 times.
|
35230 | fs.setMolarDensity(phaseIdx, rhoMolar); |
630 | |||
631 | // if the phase's fugacity coefficients are composition | ||
632 | // dependent, update them as well. | ||
633 |
2/2✓ Branch 0 taken 23268 times.
✓ Branch 1 taken 11634 times.
|
35230 | if (!FluidSystem::isIdealMixture(phaseIdx)) { |
634 |
2/2✓ Branch 0 taken 162876 times.
✓ Branch 1 taken 23268 times.
|
186144 | for (int compIdx2 = 0; compIdx2 < numComponents; ++compIdx2) { |
635 | 162876 | Scalar phi = FluidSystem::fugacityCoefficient(fs, paramCache, phaseIdx, compIdx2); | |
636 | 325752 | fs.setFugacityCoefficient(phaseIdx, compIdx2, phi); | |
637 | } | ||
638 | } | ||
639 | } | ||
640 | else { | ||
641 | assert(false); | ||
642 | } | ||
643 | 40380 | } | |
644 | |||
645 | // set a quantity in the fluid state | ||
646 | template <class FluidState> | ||
647 | 40380 | void setQuantityRaw_(FluidState &fs, int pvIdx, Scalar value) | |
648 | { | ||
649 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40380 times.
|
40380 | assert(pvIdx < numEq); |
650 | |||
651 | // first pressure | ||
652 |
2/2✓ Branch 0 taken 1744 times.
✓ Branch 1 taken 38636 times.
|
40380 | if (pvIdx < 1) { |
653 | 1744 | int phaseIdx = 0; | |
654 | 1744 | fs.setPressure(phaseIdx, value); | |
655 | } | ||
656 | // first M - 1 saturations | ||
657 |
2/2✓ Branch 0 taken 3406 times.
✓ Branch 1 taken 35230 times.
|
38636 | else if (pvIdx < numPhases) { |
658 | 3406 | int phaseIdx = pvIdx - 1; | |
659 | 3406 | fs.setSaturation(phaseIdx, value); | |
660 | } | ||
661 | // mole fractions | ||
662 | else // if (pvIdx < numPhases + numPhases*numComponents) | ||
663 | { | ||
664 | 35230 | int phaseIdx = (pvIdx - numPhases)/numComponents; | |
665 | 35230 | int compIdx = (pvIdx - numPhases)%numComponents; | |
666 | 35230 | fs.setMoleFraction(phaseIdx, compIdx, value); | |
667 | } | ||
668 | 40380 | } | |
669 | |||
670 | template <class FluidState> | ||
671 | ✗ | Scalar quantityWeight_(const FluidState &fs, int pvIdx) | |
672 | { | ||
673 | // first pressure | ||
674 |
4/6✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 38636 times.
✓ Branch 3 taken 1744 times.
✓ Branch 4 taken 38636 times.
✓ Branch 5 taken 1744 times.
|
80760 | if (pvIdx < 1) |
675 | ✗ | return 1.0/1e5; | |
676 | // first M - 1 saturations | ||
677 | ✗ | else if (pvIdx < numPhases) | |
678 | ✗ | return 1.0; | |
679 | // mole fractions | ||
680 | else // if (pvIdx < numPhases + numPhases*numComponents) | ||
681 | ✗ | return 1.0; | |
682 | } | ||
683 | |||
684 | private: | ||
685 | int wettingPhaseIdx_ = 0; //!< the phase index of the wetting phase | ||
686 | }; | ||
687 | |||
688 | } // end namespace Dumux | ||
689 | |||
690 | #endif | ||
691 |