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 Computes the composition of all phases of a N-phase, | ||
11 | * N-component fluid system assuming that all N phases are | ||
12 | * present | ||
13 | */ | ||
14 | #ifndef DUMUX_MISCIBLE_MULTIPHASE_COMPOSITION_HH | ||
15 | #define DUMUX_MISCIBLE_MULTIPHASE_COMPOSITION_HH | ||
16 | |||
17 | #include <dune/common/fvector.hh> | ||
18 | #include <dune/common/fmatrix.hh> | ||
19 | |||
20 | #include <dumux/common/exceptions.hh> | ||
21 | |||
22 | namespace Dumux { | ||
23 | /*! | ||
24 | * \ingroup ConstraintSolvers | ||
25 | * \brief Computes the composition of all phases of a N-phase, | ||
26 | * N-component fluid system assuming that all N phases are | ||
27 | * present | ||
28 | * | ||
29 | * The constraint solver assumes the following quantities to be set: | ||
30 | * | ||
31 | * - temperatures of *all* phases | ||
32 | * - saturations of *all* phases | ||
33 | * - pressures of *all* phases | ||
34 | * | ||
35 | * It also assumes that the mole/mass fractions of all phases sum up | ||
36 | * to 1. After calling the solve() method the following quantities | ||
37 | * are calculated in addition: | ||
38 | * | ||
39 | * - temperature of *all* phases | ||
40 | * - density, molar density, molar volume of *all* phases | ||
41 | * - composition in mole and mass fractions and molarities of *all* phases | ||
42 | * - mean molar masses of *all* phases | ||
43 | * - fugacity coefficients of *all* components in *all* phases | ||
44 | */ | ||
45 | template <class Scalar, class FluidSystem> | ||
46 | class MiscibleMultiPhaseComposition | ||
47 | { | ||
48 | static constexpr int numPhases = FluidSystem::numPhases; | ||
49 | static constexpr int numComponents = FluidSystem::numComponents; | ||
50 | static const int numMajorComponents = FluidSystem::numPhases; | ||
51 | |||
52 | public: | ||
53 | /*! | ||
54 | * \brief @copybrief Dumux::MiscibleMultiPhaseComposition | ||
55 | * | ||
56 | * This function additionally considers a lowering of the saturation vapor pressure | ||
57 | * of the wetting phase by the Kelvin equation: | ||
58 | * \f[ | ||
59 | * p^\textrm{w}_\textrm{sat,Kelvin} | ||
60 | * = p^\textrm{w}_\textrm{sat} | ||
61 | * \exp \left( -\frac{p_\textrm{c}}{\varrho_\textrm{w} R_\textrm{w} T} \right) | ||
62 | * \f] | ||
63 | * | ||
64 | * \param fluidState A container with the current (physical) state of the fluid | ||
65 | * \param paramCache A container for iterative calculation of fluid composition | ||
66 | * \param knownPhaseIdx The index of the phase with known properties | ||
67 | */ | ||
68 | template <class FluidState, class ParameterCache> | ||
69 | 13908138 | static void solve(FluidState &fluidState, | |
70 | ParameterCache ¶mCache, | ||
71 | int knownPhaseIdx = 0) | ||
72 | { | ||
73 | #ifndef NDEBUG | ||
74 | // currently this solver can only handle fluid systems which | ||
75 | // assume ideal mixtures of all fluids. TODO: relax this | ||
76 | // (requires solving a non-linear system of equations, i.e. using | ||
77 | // Newton method.) | ||
78 | 13908138 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { | |
79 | assert(FluidSystem::isIdealMixture(phaseIdx)); | ||
80 | |||
81 | } | ||
82 | #endif | ||
83 | |||
84 | //get the known mole fractions from the fluidState | ||
85 | //in a 2pnc system the n>2 mole fractions are primary variables and are already set in the fluidstate | ||
86 | ✗ | Dune::FieldVector<Scalar, numComponents-numMajorComponents> xKnown(0.0); | |
87 |
2/2✓ Branch 0 taken 11222029 times.
✓ Branch 1 taken 11222029 times.
|
22444058 | for (int knownCompIdx = 0; knownCompIdx < numComponents-numMajorComponents; ++knownCompIdx) |
88 | { | ||
89 | 22444058 | xKnown[knownCompIdx] = fluidState.moleFraction(knownPhaseIdx, knownCompIdx + numMajorComponents); | |
90 | } | ||
91 | |||
92 | // compute all fugacity coefficients | ||
93 |
2/2✓ Branch 0 taken 27816276 times.
✓ Branch 1 taken 13908138 times.
|
41724414 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
94 | paramCache.updatePhase(fluidState, phaseIdx); | ||
95 | |||
96 | // since we assume ideal mixtures, the fugacity | ||
97 | // coefficients of the components cannot depend on | ||
98 | // composition, i.e. the parameters in the cache are valid | ||
99 |
2/2✓ Branch 0 taken 78076610 times.
✓ Branch 1 taken 27816276 times.
|
105892886 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
100 |
2/2✓ Branch 0 taken 27207726 times.
✓ Branch 1 taken 27207726 times.
|
78076610 | Scalar fugCoeff = FluidSystem::fugacityCoefficient(fluidState, paramCache, phaseIdx, compIdx); |
101 | 156153220 | fluidState.setFugacityCoefficient(phaseIdx, compIdx, fugCoeff); | |
102 | } | ||
103 | } | ||
104 | |||
105 | |||
106 | // create the linear system of equations which defines the | ||
107 | // mole fractions | ||
108 | 13908138 | Dune::FieldMatrix<Scalar, numComponents*numPhases, numComponents*numPhases> M(0.0); | |
109 | 13908138 | Dune::FieldVector<Scalar, numComponents*numPhases> x(0.0); | |
110 | 13908138 | Dune::FieldVector<Scalar, numComponents*numPhases> b(0.0); | |
111 | |||
112 | // assemble the equations expressing the assumption that the | ||
113 | // sum of all mole fractions in each phase must be 1 | ||
114 |
2/2✓ Branch 0 taken 27816276 times.
✓ Branch 1 taken 13908138 times.
|
41724414 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
115 | 27816276 | int rowIdx = numComponents*(numPhases - 1) + phaseIdx; | |
116 | 27816276 | b[rowIdx] = 1.0; | |
117 | |||
118 |
2/2✓ Branch 0 taken 78076610 times.
✓ Branch 1 taken 27816276 times.
|
105892886 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
119 | 78076610 | int colIdx = phaseIdx*numComponents + compIdx; | |
120 | |||
121 | 234229830 | M[rowIdx][colIdx] = 1.0; | |
122 | } | ||
123 | } | ||
124 | |||
125 | // set the additional equations for the numComponents-numMajorComponents | ||
126 | // this is only relevant if numphases != numcomponents e.g. in a 2pnc system | ||
127 | // Components, of which the mole fractions are known, set to molefraction(knownCompIdx)=xKnown | ||
128 |
2/2✓ Branch 0 taken 11222029 times.
✓ Branch 1 taken 11222029 times.
|
22444058 | for(int knownCompIdx = 0; knownCompIdx < numComponents-numMajorComponents; ++knownCompIdx) |
129 | { | ||
130 | 11222029 | int rowIdx = numComponents + numPhases + knownCompIdx; | |
131 | 11222029 | int colIdx = knownPhaseIdx*numComponents + knownCompIdx + numMajorComponents; | |
132 | 22444058 | M[rowIdx][colIdx] = 1.0; | |
133 | 22444058 | b[rowIdx] = xKnown[knownCompIdx]; | |
134 | } | ||
135 | |||
136 | // assemble the equations expressing the fact that the | ||
137 | // fugacities of each component are equal in all phases | ||
138 |
2/2✓ Branch 0 taken 39038305 times.
✓ Branch 1 taken 13908138 times.
|
52946443 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) |
139 | { | ||
140 | 39038305 | int col1Idx = compIdx; | |
141 | 78076610 | const auto entryPhase0 = fluidState.fugacityCoefficient(0, compIdx)*fluidState.pressure(0); | |
142 | |||
143 |
2/2✓ Branch 0 taken 39038305 times.
✓ Branch 1 taken 39038305 times.
|
78076610 | for (unsigned int phaseIdx = 1; phaseIdx < numPhases; ++phaseIdx) |
144 | { | ||
145 | 39038305 | int rowIdx = (phaseIdx - 1)*numComponents + compIdx; | |
146 | 39038305 | int col2Idx = phaseIdx*numComponents + compIdx; | |
147 | 78076610 | M[rowIdx][col1Idx] = entryPhase0; | |
148 | 195191525 | M[rowIdx][col2Idx] = -fluidState.fugacityCoefficient(phaseIdx, compIdx)*fluidState.pressure(phaseIdx); | |
149 | } | ||
150 | } | ||
151 | |||
152 | // preconditioning of M to reduce condition number | ||
153 |
2/2✓ Branch 0 taken 39038305 times.
✓ Branch 1 taken 13908138 times.
|
52946443 | for (int compIdx = 0; compIdx < numComponents; compIdx++) |
154 | { | ||
155 | // Multiply row of main component (Raoult's Law) with 10e-5 (order of magn. of pressure) | ||
156 |
2/2✓ Branch 0 taken 22444058 times.
✓ Branch 1 taken 11222029 times.
|
33666087 | if (compIdx < numMajorComponents) |
157 | 55632552 | M[compIdx] *= 10e-5; | |
158 | |||
159 | // Multiply row of sec. components (Henry's Law) with 10e-9 (order of magn. of Henry constant) | ||
160 | else | ||
161 | ✗ | M[compIdx] *= 10e-9; | |
162 | } | ||
163 | |||
164 | // solve for all mole fractions | ||
165 |
1/2✓ Branch 1 taken 13908138 times.
✗ Branch 2 not taken.
|
13908138 | try { M.solve(x, b); } |
166 | ✗ | catch (const Dune::FMatrixError& e) { | |
167 | ✗ | DUNE_THROW(NumericalProblem, | |
168 | "MiscibleMultiPhaseComposition: Failed to solve the linear equation system for the phase composition."); | ||
169 | } | ||
170 | |||
171 | // set all mole fractions and the the additional quantities in | ||
172 | // the fluid state | ||
173 |
2/2✓ Branch 0 taken 27816276 times.
✓ Branch 1 taken 13908138 times.
|
41724414 | for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { |
174 |
2/2✓ Branch 0 taken 78076610 times.
✓ Branch 1 taken 27816276 times.
|
105892886 | for (int compIdx = 0; compIdx < numComponents; ++compIdx) { |
175 | 78076610 | int rowIdx = phaseIdx*numComponents + compIdx; | |
176 | 215137642 | fluidState.setMoleFraction(phaseIdx, compIdx, x[rowIdx]); | |
177 | } | ||
178 | 27816276 | paramCache.updateComposition(fluidState, phaseIdx); | |
179 | |||
180 | 27816276 | Scalar value = FluidSystem::density(fluidState, paramCache, phaseIdx); | |
181 | 27816276 | fluidState.setDensity(phaseIdx, value); | |
182 | |||
183 | 27816276 | value = FluidSystem::molarDensity(fluidState, paramCache, phaseIdx); | |
184 | 55632552 | fluidState.setMolarDensity(phaseIdx, value); | |
185 | } | ||
186 | 13908138 | } | |
187 | }; | ||
188 | |||
189 | } // end namespace Dumux | ||
190 | |||
191 | #endif | ||
192 |