opm-common
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 
40 namespace Opm {
41 namespace BinaryCoeff {
42 
47 template<class Scalar, class H2O, class CO2, bool verbose = true>
48 class 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 
53 public:
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);
214  Valgrind::CheckDefined(pg);
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);
276  Valgrind::CheckDefined(pg);
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 
310 private:
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
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 Scalar molarMass()
The mass in [kg] of one mole of CO2.
Definition: CO2.hpp:73
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
Relations valid for an ideal gas.
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 Evaluation henry(const Evaluation &temperature, bool extrapolate=false)
Henry coefficent for CO2 in brine.
Definition: Brine_CO2.hpp:187
static constexpr Scalar R
The ideal gas constant .
Definition: IdealGas.hpp:42
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
This class implements a small container which holds the transmissibility mulitpliers for all the face...
Definition: Exceptions.hpp:30
Binary coefficients for brine and CO2.
Definition: Brine_CO2.hpp:48
Relations valid for an ideal gas.
Definition: IdealGas.hpp:38
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 Evaluation vaporPressure(Evaluation temperature)
The vapor pressure in of pure water at a given temperature.
Definition: H2O.hpp:143
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&#39;s triple point.
Definition: CO2.hpp:137
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 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 const Scalar molarMass()
The molar mass in of water.
Definition: H2O.hpp:85
Some templates to wrap the valgrind client request macros.