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