opm-common
Loading...
Searching...
No Matches
Brine_CO2.hpp
Go to the documentation of this file.
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 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18
19 Consult the COPYING file in the top-level source directory of this
20 module for the precise wording of the license and the list of
21 copyright holders.
22*/
28#ifndef OPM_BINARY_COEFF_BRINE_CO2_HPP
29#define OPM_BINARY_COEFF_BRINE_CO2_HPP
30
33#include <opm/common/ErrorMacros.hpp>
34#include <opm/common/TimingMacros.hpp>
35#include <opm/common/utility/gpuDecorators.hpp>
36
37#include <array>
38#include <numbers>
39
40namespace Opm {
41namespace BinaryCoeff {
42
47template<class Scalar, class H2O, class CO2, bool verbose = true>
48class Brine_CO2 {
49 typedef ::Opm::IdealGas<Scalar> IdealGas;
50 static const int liquidPhaseIdx = 0; // index of the liquid phase
51 static const int gasPhaseIdx = 1; // index of the gas phase
52
53public:
64 template <class Evaluation, class CO2Params>
65 OPM_HOST_DEVICE static Evaluation gasDiffCoeff(const CO2Params& params,
66 const Evaluation& temperature,
67 const Evaluation& pressure,
68 bool extrapolate = false)
69 {
70 //Diffusion coefficient of water in the CO2 phase
71 Scalar k = 1.3806504e-23; // Boltzmann constant
72 Scalar c = 4; // slip parameter, can vary between 4 (slip condition) and 6 (stick condition)
73 Scalar R_h = 1.72e-10; // hydrodynamic radius of the solute
74 const Evaluation& mu = CO2::gasViscosity(params, temperature, pressure, extrapolate); // CO2 viscosity
75 return k / (c * std::numbers::pi * R_h) * (temperature / mu);
76 }
77
84 template <class Evaluation>
85 OPM_HOST_DEVICE static Evaluation liquidDiffCoeff(const Evaluation& /*temperature*/, const Evaluation& /*pressure*/)
86 {
87 //Diffusion coefficient of CO2 in the brine phase
88 return 2e-9;
89 }
90
111 template <class Evaluation, class CO2Params>
112 OPM_HOST_DEVICE static void
113 calculateMoleFractions(const CO2Params& params,
114 const Evaluation& temperature,
115 const Evaluation& pg,
116 const Evaluation& salinity,
117 const int knownPhaseIdx,
118 Evaluation& xlCO2,
119 Evaluation& ygH2O,
120 const int& activityModel,
121 bool extrapolate = false)
122 {
123 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
124
125 // Iterate or not?
126 bool iterate = false;
127 if ((activityModel == 1 && salinity > 0.0) || (activityModel == 2 && temperature > 372.15)) {
128 iterate = true;
129 }
130
131 // If both phases are present the mole fractions in each phase can be calculate with the mutual solubility
132 // function
133 if (knownPhaseIdx < 0) {
134 Evaluation molalityNaCl = massFracToMolality_(salinity); // mass fraction to molality of NaCl
135
136 // Duan-Sun model as given in Spycher & Pruess (2005) have a different fugacity coefficient formula and
137 // activity coefficient definition (not a true activity coefficient but a ratio).
138 // Technically only valid below T = 100 C, but we use low-temp. parameters and formulas even above 100 C as
139 // an approximation.
140 if (activityModel == 3) {
141 auto [xCO2, yH2O] = mutualSolubilitySpycherPruess2005_(params, temperature, pg, molalityNaCl, extrapolate);
142 xlCO2 = xCO2;
143 ygH2O = yH2O;
144
145 }
146 else {
147 // Fixed-point iterations to calculate solubility
148 if (iterate) {
149 auto [xCO2, yH2O] = fixPointIterSolubility_(params, temperature, pg, molalityNaCl, activityModel, extrapolate);
150 xlCO2 = xCO2;
151 ygH2O = yH2O;
152 }
153
154 // Solve mutual solubility equation with back substitution (no need for iterations)
155 else {
156 auto [xCO2, yH2O] = nonIterSolubility_(params, temperature, pg, molalityNaCl, activityModel, extrapolate);
157 xlCO2 = xCO2;
158 ygH2O = yH2O;
159 }
160 }
161 }
162
163 // if only liquid phase is present the mole fraction of CO2 in brine is given and
164 // and the virtual equilibrium mole fraction of water in the non-existing gas phase can be estimated
165 // with the mutual solubility function
166 else if (knownPhaseIdx == liquidPhaseIdx && activityModel == 3) {
167 Evaluation x_NaCl = salinityToMolFrac_(salinity);
168 const Evaluation& A = computeA_(params, temperature, pg, Evaluation(0.0), Evaluation(0.0), false, extrapolate, true);
169 ygH2O = A * (1 - xlCO2 - x_NaCl);
170 }
171
172 // if only gas phase is present the mole fraction of water in the gas phase is given and
173 // and the virtual equilibrium mole fraction of CO2 in the non-existing liquid phase can be estimated
174 // with the mutual solubility function
175 else if (knownPhaseIdx == gasPhaseIdx && activityModel == 3) {
176 //y_H2o = fluidstate.
177 Evaluation x_NaCl = salinityToMolFrac_(salinity);
178 const Evaluation& A = computeA_(params, temperature, pg, Evaluation(0.0), Evaluation(0.0), false, extrapolate, true);
179 xlCO2 = 1 - x_NaCl - ygH2O / A;
180 }
181 }
182
186 template <class Evaluation>
187 OPM_HOST_DEVICE static Evaluation henry(const Evaluation& temperature, bool extrapolate = false)
188 { return fugacityCoefficientCO2(temperature, /*pressure=*/1e5, extrapolate)*1e5; }
189
203 template <class Evaluation, class CO2Params>
204 OPM_HOST_DEVICE static Evaluation fugacityCoefficientCO2(const CO2Params& params,
205 const Evaluation& temperature,
206 const Evaluation& pg,
207 const Evaluation& yH2O,
208 const bool highTemp,
209 bool extrapolate = false,
210 bool spycherPruess2005 = false)
211 {
212 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
213 Valgrind::CheckDefined(temperature);
215
216 const Evaluation V = 1 / (CO2::gasDensity(params, temperature, pg, extrapolate) / CO2::molarMass()) * 1.e6; // molar volume in cm^3/mol
217 const Evaluation pg_bar = pg / 1.e5; // gas phase pressure in bar
218 const Scalar R = IdealGas::R * 10.; // ideal gas constant with unit bar cm^3 /(K mol)
219
220 // Parameters in Redlich-Kwong equation
221 const Evaluation a_CO2 = aCO2_(temperature, highTemp);
222 const Evaluation a_CO2_H2O = aCO2_H2O_(temperature, yH2O, highTemp);
223 const Evaluation a_mix = aMix_(temperature, yH2O, highTemp);
224 const Scalar b_CO2 = bCO2_(highTemp);
225 const Evaluation b_mix = bMix_(yH2O, highTemp);
226 const Evaluation Rt15 = R * pow(temperature, 1.5);
227
228 Evaluation lnPhiCO2;
229 if (spycherPruess2005) {
230 const Evaluation logVpb_V = log((V + b_CO2) / V);
231 lnPhiCO2 = log(V / (V - b_CO2));
232 lnPhiCO2 += b_CO2 / (V - b_CO2);
233 lnPhiCO2 -= 2 * a_CO2 / (Rt15 * b_CO2) * logVpb_V;
234 lnPhiCO2 +=
235 a_CO2 * b_CO2
236 / (Rt15
237 * b_CO2
238 * b_CO2)
239 * (logVpb_V
240 - b_CO2 / (V + b_CO2));
241 lnPhiCO2 -= log(pg_bar * V / (R * temperature));
242 }
243 else {
244 lnPhiCO2 = (b_CO2 / b_mix) * (pg_bar * V / (R * temperature) - 1);
245 lnPhiCO2 -= log(pg_bar * (V - b_mix) / (R * temperature));
246 lnPhiCO2 += (2 * (yH2O * a_CO2_H2O + (1 - yH2O) * a_CO2) / a_mix - (b_CO2 / b_mix)) *
247 a_mix / (b_mix * Rt15) * log(V / (V + b_mix));
248 }
249 return exp(lnPhiCO2); // fugacity coefficient of CO2
250 }
251
265 template <class Evaluation, class CO2Params>
266 OPM_HOST_DEVICE static Evaluation fugacityCoefficientH2O(const CO2Params& params,
267 const Evaluation& temperature,
268 const Evaluation& pg,
269 const Evaluation& yH2O,
270 const bool highTemp,
271 bool extrapolate = false,
272 bool spycherPruess2005 = false)
273 {
274 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
275 Valgrind::CheckDefined(temperature);
277
278 const Evaluation& V = 1 / (CO2::gasDensity(params, temperature, pg, extrapolate) / CO2::molarMass()) * 1.e6; // molar volume in cm^3/mol
279 const Evaluation& pg_bar = pg / 1.e5; // gas phase pressure in bar
280 const Scalar R = IdealGas::R * 10.; // ideal gas constant with unit bar cm^3 /(K mol)
281
282 // Mixture parameter of Redlich-Kwong equation
283 const Evaluation a_H2O = aH2O_(temperature, highTemp);
284 const Evaluation a_CO2_H2O = aCO2_H2O_(temperature, yH2O, highTemp);
285 const Evaluation a_mix = aMix_(temperature, yH2O, highTemp);
286 const Scalar b_H2O = bH2O_(highTemp);
287 const Evaluation b_mix = bMix_(yH2O, highTemp);
288 const Evaluation Rt15 = R * pow(temperature, 1.5);
289
290 Evaluation lnPhiH2O;
291 if (spycherPruess2005) {
292 const Evaluation logVpb_V = log((V + b_mix) / V);
293 lnPhiH2O =
294 log(V/(V - b_mix))
295 + b_H2O/(V - b_mix) - 2*a_CO2_H2O
296 / (Rt15*b_mix)*logVpb_V
297 + a_mix*b_H2O/(Rt15*b_mix*b_mix)
298 *(logVpb_V - b_mix/(V + b_mix))
299 - log(pg_bar*V/(R*temperature));
300 }
301 else {
302 lnPhiH2O = (b_H2O / b_mix) * (pg_bar * V / (R * temperature) - 1);
303 lnPhiH2O -= log(pg_bar * (V - b_mix) / (R * temperature));
304 lnPhiH2O += (2 * (yH2O * a_H2O + (1 - yH2O) * a_CO2_H2O) / a_mix - (b_H2O / b_mix)) *
305 a_mix / (b_mix * Rt15) * log(V / (V + b_mix));
306 }
307 return exp(lnPhiH2O); // fugacity coefficient of H2O
308 }
309
310private:
314 template <class Evaluation>
315 OPM_HOST_DEVICE static Evaluation aCO2_(const Evaluation& temperature, const bool& highTemp)
316 {
317 if (highTemp) {
318 return 8.008e7 - 4.984e4 * temperature;
319 }
320 else {
321 return 7.54e7 - 4.13e4 * temperature;
322 }
323 }
324
328 template <class Evaluation>
329 OPM_HOST_DEVICE static Evaluation aH2O_(const Evaluation& temperature, const bool& highTemp)
330 {
331 if (highTemp) {
332 return 1.337e8 - 1.4e4 * temperature;
333 }
334 else {
335 return 0.0;
336 }
337 }
338
342 template <class Evaluation>
343 OPM_HOST_DEVICE static Evaluation aCO2_H2O_(const Evaluation& temperature, const Evaluation& yH2O, const bool& highTemp)
344 {
345 if (highTemp) {
346 // Pure parameters
347 Evaluation aCO2 = aCO2_(temperature, highTemp);
348 Evaluation aH2O = aH2O_(temperature, highTemp);
349
350 // Mixture Eq. (A-6)
351 Evaluation K_CO2_H2O = 0.4228 - 7.422e-4 * temperature;
352 Evaluation K_H2O_CO2 = 1.427e-2 - 4.037e-4 * temperature;
353 Evaluation k_CO2_H2O = yH2O * K_H2O_CO2 + (1 - yH2O) * K_CO2_H2O;
354
355 // Eq. (A-5)
356 return sqrt(aCO2 * aH2O) * (1 - k_CO2_H2O);
357 }
358 else {
359 return 7.89e7;
360 }
361 }
362
366 template <class Evaluation>
367 OPM_HOST_DEVICE static Evaluation aMix_(const Evaluation& temperature, const Evaluation& yH2O, const bool& highTemp)
368 {
369 if (highTemp) {
370 // Parameters
371 Evaluation aCO2 = aCO2_(temperature, highTemp);
372 Evaluation aH2O = aH2O_(temperature, highTemp);
373 Evaluation a_CO2_H2O = aCO2_H2O_(temperature, yH2O, highTemp);
374
375 return yH2O * yH2O * aH2O + 2 * yH2O * (1 - yH2O) * a_CO2_H2O + (1 - yH2O) * (1 - yH2O) * aCO2;
376 }
377 else {
378 return aCO2_(temperature, highTemp);
379 }
380 }
381
385 OPM_HOST_DEVICE static Scalar bCO2_(const bool& highTemp)
386 {
387 if (highTemp) {
388 return 28.25;
389 }
390 else {
391 return 27.8;
392 }
393 }
394
398 OPM_HOST_DEVICE static Scalar bH2O_(const bool& highTemp)
399 {
400 if (highTemp) {
401 return 15.7;
402 }
403 else {
404 return 18.18;
405 }
406 }
407
411 template <class Evaluation>
412 OPM_HOST_DEVICE static Evaluation bMix_(const Evaluation& yH2O, const bool& highTemp)
413 {
414 if (highTemp) {
415 // Parameters
416 Scalar bCO2 = bCO2_(highTemp);
417 Scalar bH2O = bH2O_(highTemp);
418
419 return yH2O * bH2O + (1 - yH2O) * bCO2;
420 }
421 else {
422 return bCO2_(highTemp);
423 }
424 }
425
429 template <class Evaluation>
430 OPM_HOST_DEVICE static Evaluation V_avg_CO2_(const Evaluation& temperature, const bool& highTemp)
431 {
432 if (highTemp && (temperature > 373.15)) {
433 return 32.6 + 3.413e-2 * (temperature - 373.15);
434 }
435 else {
436 return 32.6;
437 }
438 }
439
443 template <class Evaluation>
444 OPM_HOST_DEVICE static Evaluation V_avg_H2O_(const Evaluation& temperature, const bool& highTemp)
445 {
446 if (highTemp && (temperature > 373.15)) {
447 return 18.1 + 3.137e-2 * (temperature - 373.15);
448 }
449 else {
450 return 18.1;
451 }
452 }
453
457 template <class Evaluation>
458 OPM_HOST_DEVICE static Evaluation AM_(const Evaluation& temperature, const bool& highTemp)
459 {
460 if (highTemp && temperature > 373.15) {
461 Evaluation deltaTk = temperature - 373.15;
462 return deltaTk * (-3.084e-2 + 1.927e-5 * deltaTk);
463 }
464 else {
465 return 0.0;
466 }
467 }
468
472 template <class Evaluation>
473 OPM_HOST_DEVICE static Evaluation Pref_(const Evaluation& temperature, const bool& highTemp)
474 {
475 if (highTemp && temperature > 373.15) {
476 const Evaluation& temperatureCelcius = temperature - 273.15;
477 static const Scalar c[5] = { -1.9906e-1, 2.0471e-3, 1.0152e-4, -1.4234e-6, 1.4168e-8 };
478 return c[0] + temperatureCelcius * (c[1] + temperatureCelcius * (c[2] +
479 temperatureCelcius * (c[3] + temperatureCelcius * c[4])));
480 }
481 else {
482 return 1.0;
483 }
484 }
485
489 template <class Evaluation>
490 OPM_HOST_DEVICE static Evaluation activityCoefficientCO2_(const Evaluation& temperature,
491 const Evaluation& xCO2,
492 const bool& highTemp)
493 {
494 if (highTemp) {
495 // Eq. (13)
496 Evaluation AM = AM_(temperature, highTemp);
497 Evaluation lnGammaCO2 = 2 * AM * xCO2 * (1 - xCO2) * (1 - xCO2);
498 return exp(lnGammaCO2);
499 }
500 else {
501 return 1.0;
502 }
503 }
504
508 template <class Evaluation>
509 OPM_HOST_DEVICE static Evaluation activityCoefficientH2O_(const Evaluation& temperature,
510 const Evaluation& xCO2,
511 const bool& highTemp)
512 {
513 if (highTemp) {
514 // Eq. (12)
515 Evaluation AM = AM_(temperature, highTemp);
516 Evaluation lnGammaH2O = (1 - 2 * (1 - xCO2)) * AM * xCO2 * xCO2;
517 return exp(lnGammaH2O);
518 }
519 else {
520 return 1.0;
521 }
522 }
523
529 template <class Evaluation>
530 OPM_HOST_DEVICE static Evaluation salinityToMolFrac_(const Evaluation& salinity) {
531 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
532 const Scalar Mw = H2O::molarMass(); /* molecular weight of water [kg/mol] */
533 const Scalar Ms = 58.44e-3; /* molecular weight of NaCl [kg/mol] */
534
535 const Evaluation X_NaCl = salinity;
536 /* salinity: conversion from mass fraction to mol fraction */
537 const Evaluation x_NaCl = -Mw * X_NaCl / ((Ms - Mw) * X_NaCl - Ms);
538 return x_NaCl;
539 }
540
546#if 0
547 template <class Evaluation>
548 OPM_HOST_DEVICE static Evaluation moleFracToMolality_(const Evaluation& x_NaCl)
549 {
550 // conversion from mol fraction to molality (dissolved CO2 neglected)
551 return 55.508 * x_NaCl / (1 - x_NaCl);
552 }
553#endif
554
555 template <class Evaluation>
556 OPM_HOST_DEVICE static Evaluation massFracToMolality_(const Evaluation& X_NaCl)
557 {
558 const Scalar MmNaCl = 58.44e-3;
559 return X_NaCl / (MmNaCl * (1 - X_NaCl));
560 }
561
567 template <class Evaluation>
568 OPM_HOST_DEVICE static Evaluation molalityToMoleFrac_(const Evaluation& m_NaCl)
569 {
570 // conversion from molality to mole fractio (dissolved CO2 neglected)
571 return m_NaCl / (55.508 + m_NaCl);
572 }
573
577 template <class Evaluation, class CO2Parameters>
578 OPM_HOST_DEVICE static std::pair<Evaluation, Evaluation> fixPointIterSolubility_(const CO2Parameters& params,
579 const Evaluation& temperature,
580 const Evaluation& pg,
581 const Evaluation& m_NaCl,
582 const int& activityModel,
583 bool extrapolate = false)
584 {
585 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
586 // Start point for fixed-point iterations as recommended below in section 2.2
587 Evaluation yH2O = H2O::vaporPressure(temperature) / pg; // ideal mixing
588 Evaluation xCO2 = 0.009; // same as ~0.5 mol/kg
589 Evaluation gammaNaCl = 1.0; // default salt activity coeff = 1.0
590
591 // We can pre-calculate Duan-Sun, Spycher & Pruess (2009) salt activity coeff.
592 if (m_NaCl > 0.0 && activityModel == 2) {
593 gammaNaCl = activityCoefficientSalt_(temperature, pg, m_NaCl, Evaluation(0.0), activityModel);
594 }
595
596 // Options
597 int max_iter = 100;
598 Scalar tol = 1e-8;
599 bool highTemp = true;
600 if (activityModel == 1) {
601 highTemp = false;
602 }
603 const bool iterate = true;
604
605 // Fixed-point loop x_i+1 = F(x_i)
606 for (int i = 0; i < max_iter; ++i) {
607 // Calculate activity coefficient for Rumpf et al (1994) model
608 if (m_NaCl > 0.0 && activityModel == 1) {
609 gammaNaCl = activityCoefficientSalt_(temperature, pg, m_NaCl, xCO2, activityModel);
610 }
611
612 // F(x_i) is the mutual solubilities
613 auto [xCO2_new, yH2O_new] = mutualSolubility_(params, temperature, pg, xCO2, yH2O, m_NaCl, gammaNaCl, highTemp,
614 iterate, extrapolate);
615
616 // Check for convergence
617 if (abs(xCO2_new - xCO2) < tol && abs(yH2O_new - yH2O) < tol) {
618 xCO2 = xCO2_new;
619 yH2O = yH2O_new;
620 break;
621 }
622
623 // Else update mole fractions for next iteration
624 else {
625 xCO2 = xCO2_new;
626 yH2O = yH2O_new;
627 }
628 }
629
630 return {xCO2, yH2O};
631 }
632
636 template <class Evaluation, class CO2Parameters>
637 OPM_HOST_DEVICE static std::pair<Evaluation, Evaluation> nonIterSolubility_(const CO2Parameters& params,
638 const Evaluation& temperature,
639 const Evaluation& pg,
640 const Evaluation& m_NaCl,
641 const int& activityModel,
642 bool extrapolate = false)
643 {
644 // Calculate activity coefficient for salt
645 Evaluation gammaNaCl = 1.0;
646 if (m_NaCl > 0.0 && activityModel > 0 && activityModel < 3) {
647 gammaNaCl = activityCoefficientSalt_(temperature, pg, m_NaCl, Evaluation(0.0), activityModel);
648 }
649
650 // Calculate mutual solubility.
651 // Note that we don't use xCO2 and yH2O input in low-temperature case, so we set them to 0.0
652 const bool highTemp = false;
653 const bool iterate = false;
654 auto [xCO2, yH2O] = mutualSolubility_(params, temperature, pg, Evaluation(0.0), Evaluation(0.0), m_NaCl, gammaNaCl,
655 highTemp, iterate, extrapolate);
656
657 return {xCO2, yH2O};
658 }
659
663 template <class Evaluation, class CO2Parameters>
664 OPM_HOST_DEVICE static std::pair<Evaluation, Evaluation> mutualSolubility_(const CO2Parameters& params,
665 const Evaluation& temperature,
666 const Evaluation& pg,
667 const Evaluation& xCO2,
668 const Evaluation& yH2O,
669 const Evaluation& m_NaCl,
670 const Evaluation& gammaNaCl,
671 const bool& highTemp,
672 const bool& iterate,
673 bool extrapolate = false)
674 {
675 // Calculate A and B (without salt effect); Eqs. (8) and (9)
676 const Evaluation& A = computeA_(params, temperature, pg, yH2O, xCO2, highTemp, extrapolate);
677 Evaluation B = computeB_(params, temperature, pg, yH2O, xCO2, highTemp, extrapolate);
678
679 // Add salt effect to B, Eq. (17)
680 B /= gammaNaCl;
681
682 // Compute yH2O and xCO2, Eqs. (B-7) and (B-2)
683 Evaluation yH2O_new = (1. - B) * 55.508 / ((1. / A - B) * (2 * m_NaCl + 55.508) + 2 * m_NaCl * B);
684 Evaluation xCO2_new;
685 if (iterate) {
686 xCO2_new = B * (1 - yH2O);
687 }
688 else {
689 xCO2_new = B * (1 - yH2O_new);
690 }
691
692 return {xCO2_new, yH2O_new};
693 }
694
698 template <class Evaluation, class CO2Parameters>
699 OPM_HOST_DEVICE static std::pair<Evaluation, Evaluation> mutualSolubilitySpycherPruess2005_(const CO2Parameters& params,
700 const Evaluation& temperature,
701 const Evaluation& pg,
702 const Evaluation& m_NaCl,
703 bool extrapolate = false)
704 {
705 // Calculate A and B (without salt effect); Eqs. (8) and (9)
706 const Evaluation& A = computeA_(params, temperature, pg, Evaluation(0.0), Evaluation(0.0), false, extrapolate, true);
707 const Evaluation& B = computeB_(params, temperature, pg, Evaluation(0.0), Evaluation(0.0), false, extrapolate, true);
708
709 // Mole fractions and molality in pure water
710 Evaluation yH2O = (1 - B) / (1. / A - B);
711 Evaluation xCO2 = B * (1 - yH2O);
712
713 // Modifiy mole fractions with Duan-Sun "activity coefficient" if salt is involved
714 if (m_NaCl > 0.0) {
715 const Evaluation& gammaNaCl = activityCoefficientSalt_(temperature, pg, m_NaCl, Evaluation(0.0), 3);
716
717 // Molality with salt
718 Evaluation mCO2 = (xCO2 * 55.508) / (1 - xCO2); // pure water
719 mCO2 /= gammaNaCl;
720 xCO2 = mCO2 / (m_NaCl + 55.508 + mCO2);
721
722 // new yH2O with salt
723 const Evaluation& xNaCl = molalityToMoleFrac_(m_NaCl);
724 yH2O = A * (1 - xCO2 - xNaCl);
725 }
726
727 return {xCO2, yH2O};
728 }
729
738 template <class Evaluation, class CO2Params>
739 OPM_HOST_DEVICE static Evaluation computeA_(const CO2Params& params,
740 const Evaluation& temperature,
741 const Evaluation& pg,
742 const Evaluation& yH2O,
743 const Evaluation& xCO2,
744 const bool& highTemp,
745 bool extrapolate = false,
746 bool spycherPruess2005 = false)
747 {
748 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
749 // Intermediate calculations
750 const Evaluation& deltaP = pg / 1e5 - Pref_(temperature, highTemp); // pressure range [bar] from pref to pg[bar]
751 Evaluation v_av_H2O = V_avg_H2O_(temperature, highTemp); // average partial molar volume of H2O [cm^3/mol]
752 Evaluation k0_H2O = equilibriumConstantH2O_(temperature, highTemp); // equilibrium constant for H2O at 1 bar
753 Evaluation phi_H2O = fugacityCoefficientH2O(params, temperature, pg, yH2O, highTemp, extrapolate, spycherPruess2005); // fugacity coefficient of H2O for the water-CO2 system
754 Evaluation gammaH2O = activityCoefficientH2O_(temperature, xCO2, highTemp);
755
756 // In the intermediate temperature range 99-109 C, equilibrium constants and fugacity coeff. are linearly
757 // weighted
758 if ( temperature > 372.15 && temperature < 382.15 && !spycherPruess2005) {
759 const Evaluation weight = (382.15 - temperature) / 10.;
760 const Evaluation& k0_H2O_low = equilibriumConstantH2O_(temperature, false);
761 const Evaluation& phi_H2O_low = fugacityCoefficientH2O(params, temperature, pg, Evaluation(0.0), false, extrapolate);
762 k0_H2O = k0_H2O * (1 - weight) + k0_H2O_low * weight;
763 phi_H2O = phi_H2O * (1 - weight) + phi_H2O_low * weight;
764 }
765
766 // Eq. (10)
767 const Evaluation& pg_bar = pg / 1.e5;
768 Scalar R = IdealGas::R * 10;
769 return k0_H2O * gammaH2O / (phi_H2O * pg_bar) * exp(deltaP * v_av_H2O / (R * temperature));
770 }
771
780 template <class Evaluation, class CO2Parameters>
781 OPM_HOST_DEVICE static Evaluation computeB_(const CO2Parameters& params,
782 const Evaluation& temperature,
783 const Evaluation& pg,
784 const Evaluation& yH2O,
785 const Evaluation& xCO2,
786 const bool& highTemp,
787 bool extrapolate = false,
788 bool spycherPruess2005 = false)
789 {
790 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
791 // Intermediate calculations
792 const Evaluation& deltaP = pg / 1e5 - Pref_(temperature, highTemp); // pressure range [bar] from pref to pg[bar]
793 Evaluation v_av_CO2 = V_avg_CO2_(temperature, highTemp); // average partial molar volume of CO2 [cm^3/mol]
794 Evaluation k0_CO2 = equilibriumConstantCO2_(temperature, pg, highTemp, spycherPruess2005); // equilibrium constant for CO2 at 1 bar
795 Evaluation phi_CO2 = fugacityCoefficientCO2(params, temperature, pg, yH2O, highTemp, extrapolate, spycherPruess2005); // fugacity coefficient of CO2 for the water-CO2 system
796 Evaluation gammaCO2 = activityCoefficientCO2_(temperature, xCO2, highTemp);
797
798 // In the intermediate temperature range 99-109 C, equilibrium constants and fugacity coeff. are linearly
799 // weighted
800 if ( temperature > 372.15 && temperature < 382.15 && !spycherPruess2005) {
801 const Evaluation weight = (382.15 - temperature) / 10.;
802 const Evaluation& k0_CO2_low = equilibriumConstantCO2_(temperature, pg, false, spycherPruess2005);
803 const Evaluation& phi_CO2_low = fugacityCoefficientCO2(params, temperature, pg, Evaluation(0.0), false, extrapolate);
804 k0_CO2 = k0_CO2 * (1 - weight) + k0_CO2_low * weight;
805 phi_CO2 = phi_CO2 * (1 - weight) + phi_CO2_low * weight;
806 }
807
808 // Eq. (11)
809 const Evaluation& pg_bar = pg / 1.e5;
810 const Scalar R = IdealGas::R * 10;
811 return phi_CO2 * pg_bar / (55.508 * k0_CO2 * gammaCO2) * exp(-deltaP * v_av_CO2 / (R * temperature));
812 }
813
817 template <class Evaluation>
818 OPM_HOST_DEVICE static Evaluation activityCoefficientSalt_(const Evaluation& temperature,
819 const Evaluation& pg,
820 const Evaluation& m_NaCl,
821 const Evaluation& xCO2,
822 const int& activityModel)
823 {
824 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
825 // Lambda and xi parameter for either Rumpf et al (1994) (activityModel = 1) or Duan-Sun as modified by Spycher
826 // & Pruess (2009) (activityModel = 2) or Duan & Sun (2003) as given in Spycher & Pruess (2005) (activityModel =
827 // 3)
828 Evaluation lambda;
829 Evaluation xi;
830 Evaluation convTerm;
831 if (activityModel == 1) {
832 lambda = computeLambdaRumpfetal_(temperature);
833 xi = -0.0028 * 3.0;
834 Evaluation m_CO2 = xCO2 * (2 * m_NaCl + 55.508) / (1 - xCO2);
835 convTerm = (1 + (m_CO2 + 2 * m_NaCl) / 55.508) / (1 + m_CO2 / 55.508);
836 }
837 else if (activityModel == 2) {
838 lambda = computeLambdaSpycherPruess2009_(temperature);
839 xi = computeXiSpycherPruess2009_(temperature);
840 convTerm = 1 + 2 * m_NaCl / 55.508;
841 }
842 else if (activityModel == 3) {
843 lambda = computeLambdaDuanSun_(temperature, pg);
844 xi = computeXiDuanSun_(temperature, pg);
845 convTerm = 1.0;
846 }
847 else {
848 OPM_THROW(std::invalid_argument, "Activity model for salt-out effect has not been implemented!.");
849 }
850
851 // Eq. (18)
852 const Evaluation& lnGamma = 2 * lambda * m_NaCl + xi * m_NaCl * m_NaCl;
853
854 // Eq. (18), return activity coeff. on mole-fraction scale
855 return convTerm * exp(lnGamma);
856 }
857
861 template <class Evaluation>
862 OPM_HOST_DEVICE static Evaluation computeLambdaSpycherPruess2009_(const Evaluation& temperature)
863 {
864 // Table 1
865 static const Scalar c[3] = { 2.217e-4, 1.074, 2648. };
866
867 // Eq. (19)
868 return c[0] * temperature + c[1] / temperature + c[2] / (temperature * temperature);
869 }
870
874 template <class Evaluation>
875 OPM_HOST_DEVICE static Evaluation computeXiSpycherPruess2009_(const Evaluation& temperature)
876 {
877 // Table 1
878 static const Scalar c[3] = { 1.3e-5, -20.12, 5259. };
879
880 // Eq. (19)
881 return c[0] * temperature + c[1] / temperature + c[2] / (temperature * temperature);
882 }
883
887 template <class Evaluation>
888 OPM_HOST_DEVICE static Evaluation computeLambdaRumpfetal_(const Evaluation& temperature)
889 {
890 // B^(0) below Eq. (A-6)
891 static const Scalar c[4] = { 0.254, -76.82, -10656, 6312e3 };
892
893 return c[0] + c[1] / temperature + c[2] / (temperature * temperature) +
894 c[3] / (temperature * temperature * temperature);
895 }
896
904 template <class Evaluation>
905 OPM_HOST_DEVICE static Evaluation computeLambdaDuanSun_(const Evaluation& temperature, const Evaluation& pg)
906 {
907 static const Scalar c[6] =
908 { -0.411370585, 6.07632013E-4, 97.5347708, -0.0237622469, 0.0170656236, 1.41335834E-5 };
909
910 Evaluation pg_bar = pg / 1.0E5; /* conversion from Pa to bar */
911 return c[0] + c[1]*temperature + c[2]/temperature + c[3]*pg_bar/temperature + c[4]*pg_bar/(630.0 - temperature)
912 + c[5]*temperature*log(pg_bar);
913 }
914
922 template <class Evaluation>
923 OPM_HOST_DEVICE static Evaluation computeXiDuanSun_(const Evaluation& temperature, const Evaluation& pg)
924 {
925 static const Scalar c[4] =
926 { 3.36389723E-4, -1.98298980E-5, 2.12220830E-3, -5.24873303E-3 };
927
928 Evaluation pg_bar = pg / 1.0E5; /* conversion from Pa to bar */
929 return c[0] + c[1]*temperature + c[2]*pg_bar/temperature + c[3]*pg_bar/(630.0 - temperature);
930 }
931
938 template <class Evaluation>
939 OPM_HOST_DEVICE static Evaluation equilibriumConstantCO2_(const Evaluation& temperature,
940 const Evaluation& pg,
941 const bool& highTemp,
942 bool spycherPruess2005 = false)
943 {
944 OPM_TIMEFUNCTION_LOCAL(Subsystem::PvtProps);
945 Evaluation temperatureCelcius = temperature - 273.15;
946 std::array<Scalar, 4> c;
947 if (highTemp) {
948 c = { 1.668, 3.992e-3, -1.156e-5, 1.593e-9 };
949 }
950 else {
951 // For temperature below critical temperature and pressures above saturation pressure, separate parameters are needed
952 bool model1 = temperature < CO2::criticalTemperature() && !spycherPruess2005;
953 if (model1) {
954 // Computing the vapor pressure is not trivial and is also not defined for T > criticalTemperature
955 Evaluation psat = CO2::vaporPressure(temperature);
956 model1 = pg > psat;
957 }
958 if (model1) {
959 c = { 1.169, 1.368e-2, -5.38e-5, 0.0 };
960 }
961 else {
962 c = { 1.189, 1.304e-2, -5.446e-5, 0.0 };
963 }
964 }
965 Evaluation logk0_CO2 = c[0] + temperatureCelcius * (c[1] + temperatureCelcius *
966 (c[2] + temperatureCelcius * c[3]));
967 Evaluation k0_CO2 = pow(10.0, logk0_CO2);
968 return k0_CO2;
969 }
970
977 template <class Evaluation>
978 OPM_HOST_DEVICE static Evaluation equilibriumConstantH2O_(const Evaluation& temperature, const bool& highTemp)
979 {
980 Evaluation temperatureCelcius = temperature - 273.15;
981 std::array<Scalar, 5> c;
982 if (highTemp){
983 c = { -2.1077, 2.8127e-2, -8.4298e-5, 1.4969e-7, -1.1812e-10 };
984 }
985 else {
986 c = { -2.209, 3.097e-2, -1.098e-4, 2.048e-7, 0.0 };
987 }
988 Evaluation logk0_H2O = c[0] + temperatureCelcius * (c[1] + temperatureCelcius * (c[2] +
989 temperatureCelcius * (c[3] + temperatureCelcius * c[4])));
990 return pow(10.0, logk0_H2O);
991 }
992
993};
994
995} // namespace BinaryCoeff
996} // namespace Opm
997
998#endif
Relations valid for an ideal gas.
Some templates to wrap the valgrind client request macros.
OPM_HOST_DEVICE bool CheckDefined(const T &value)
Make valgrind complain if any of the memory occupied by an object is undefined.
Definition Valgrind.hpp:76
Binary coefficients for brine and CO2.
Definition Brine_CO2.hpp:48
static OPM_HOST_DEVICE Evaluation fugacityCoefficientCO2(const CO2Params &params, const Evaluation &temperature, const Evaluation &pg, const Evaluation &yH2O, const bool highTemp, bool extrapolate=false, bool spycherPruess2005=false)
Returns the fugacity coefficient of the CO2 component in a water-CO2 mixture.
Definition Brine_CO2.hpp:204
static OPM_HOST_DEVICE Evaluation liquidDiffCoeff(const Evaluation &, const Evaluation &)
Binary diffusion coefficent [m^2/s] of CO2 in the brine phase.
Definition Brine_CO2.hpp:85
static OPM_HOST_DEVICE void calculateMoleFractions(const CO2Params &params, const Evaluation &temperature, const Evaluation &pg, const Evaluation &salinity, const int knownPhaseIdx, Evaluation &xlCO2, Evaluation &ygH2O, const int &activityModel, bool extrapolate=false)
Returns the mol (!) fraction of CO2 in the liquid phase and the mol_ (!) fraction of H2O in the gas p...
Definition Brine_CO2.hpp:113
static OPM_HOST_DEVICE Evaluation henry(const Evaluation &temperature, bool extrapolate=false)
Henry coefficent for CO2 in brine.
Definition Brine_CO2.hpp:187
static OPM_HOST_DEVICE Evaluation fugacityCoefficientH2O(const CO2Params &params, const Evaluation &temperature, const Evaluation &pg, const Evaluation &yH2O, const bool highTemp, bool extrapolate=false, bool spycherPruess2005=false)
Returns the fugacity coefficient of the H2O component in a water-CO2 mixture.
Definition Brine_CO2.hpp:266
static OPM_HOST_DEVICE Evaluation gasDiffCoeff(const CO2Params &params, const Evaluation &temperature, const Evaluation &pressure, bool extrapolate=false)
Binary diffusion coefficent [m^2/s] of water in the CO2 phase.
Definition Brine_CO2.hpp:65
static OPM_HOST_DEVICE Evaluation gasDensity(const Params &params, const Evaluation &temperature, const Evaluation &pressure, bool extrapolate=false)
The density of CO2 at a given pressure and temperature [kg/m^3].
Definition CO2.hpp:222
static OPM_HOST_DEVICE Scalar molarMass()
The mass in [kg] of one mole of CO2.
Definition CO2.hpp:73
static OPM_HOST_DEVICE Scalar criticalTemperature()
Returns the critical temperature [K] of CO2.
Definition CO2.hpp:79
static OPM_HOST_DEVICE Evaluation vaporPressure(const Evaluation &T)
Returns the pressure [Pa] at CO2's triple point.
Definition CO2.hpp:137
static OPM_HOST_DEVICE Evaluation gasViscosity(const Params &params, Evaluation temperature, const Evaluation &pressure, bool extrapolate=false)
The dynamic viscosity [Pa s] of CO2.
Definition CO2.hpp:249
static Evaluation vaporPressure(Evaluation temperature)
The vapor pressure in of pure water at a given temperature.
Definition H2O.hpp:143
static const Scalar molarMass()
The molar mass in of water.
Definition H2O.hpp:85
static constexpr Scalar R
The ideal gas constant .
Definition IdealGas.hpp:42
This class implements a small container which holds the transmissibility mulitpliers for all the face...
Definition Exceptions.hpp:30
Scalar Brine< Scalar, H2O >::salinity
Default value for the salinity of the brine (dimensionless).
Definition Brine.hpp:391