proteus
1.9.0
C/C++/Fortran libraries
Toggle main menu visibility
Loading...
Searching...
No Matches
work
cekees
proteus
proteus
m_comp_co2
co2_brine_eos.h
Go to the documentation of this file.
1
#ifndef CO2_BRINE_EOS_H
2
#define CO2_BRINE_EOS_H
3
// Spycher-Pruess-Ennis-King (2003) CO2-brine equilibrium EOS -- C++ port of
4
// co2_brine_eos.py (P2). Header-only, no proteus/xtensor deps so it is
5
// standalone-testable. All constants/equations match the numpy reference.
6
//
7
// Spycher, Pruess & Ennis-King (2003), GCA 67(16) 3015-3031 (LBNL-50991)
8
//
9
// Salt-free (m_NaCl wired but the SP2005 salting-out is a follow-up).
10
#include <cmath>
11
#include <algorithm>
12
13
namespace
m_comp_co2
{
14
namespace
eos
{
15
16
// --- SP2003 constants (verbatim, == co2_brine_eos.py) ----------------------
17
constexpr
double
R_CM3_BAR
= 83.1446;
// cm^3 bar /(mol K)
18
constexpr
double
P0_BAR
= 1.0;
19
constexpr
double
M_PER_KG_H2O
= 55.508;
20
21
constexpr
double
A_CO2_0
= 7.54e7;
// a_CO2(T) = A_CO2_0 - A_CO2_1*T(K)
22
constexpr
double
A_CO2_1
= 4.02e4;
23
constexpr
double
A_H2O_CO2
= 7.89e7;
24
constexpr
double
B_CO2
= 27.80;
25
constexpr
double
B_H2O
= 18.10;
26
27
constexpr
double
V_H2O
= 18.5,
V_CO2
= 32.1;
// partial molar volumes cm^3/mol
28
29
// Brine compressibility: rho_a(p) = rho_a(T,X)*exp(C_F_BRINE*(p - P_REF_BRINE)),
30
// p in Pa. Gives the aqueous phase a pressure dependence so the comp-0 pressure
31
// equation is non-degenerate in single-phase brine.
32
constexpr
double
C_F_BRINE
= 4.5e-10;
// isothermal compressibility [1/Pa]
33
constexpr
double
P_REF_BRINE
= 1.01325e5;
// reference pressure [Pa]
34
35
constexpr
double
M_CO2_KG
= 0.04401;
// molar mass [kg/mol]
36
constexpr
double
M_H2O_KG
= 0.018015;
// molar mass [kg/mol]
37
38
// log10(K0) = a + bT + cT^2 + dT^3 + eT^4 , T in CELSIUS
39
constexpr
double
K0_H2O
[5] = {-2.215, 3.162e-2, -1.294e-4, 4.187e-7, -7.331e-10};
40
constexpr
double
K0_CO2_G
[5] = { 1.188, 1.307e-2, -5.445e-5, 0.0, 0.0};
41
constexpr
double
K0_CO2_L
[5] = { 1.168, 1.361e-2, -5.135e-5, 0.0, 0.0};
42
43
constexpr
double
TC_CO2_K
= 304.1282,
PC_CO2_BAR
= 73.773;
44
constexpr
double
SW_A
[4] = {-7.0602087, 1.9391218, -1.6463597, -3.2995634};
45
46
inline
double
poly_logK0
(
const
double
c
[5],
double
T_C) {
47
return
c
[0] + T_C*(
c
[1] + T_C*(
c
[2] + T_C*(
c
[3] + T_C*
c
[4])));
48
}
49
50
inline
double
co2_saturation_pressure_bar
(
double
T_C) {
51
double
T_K = T_C + 273.15;
52
if
(T_K >=
TC_CO2_K
)
return
INFINITY;
53
double
tau = 1.0 - T_K/
TC_CO2_K
;
54
double
s
=
SW_A
[0]*tau +
SW_A
[1]*std::pow(tau,1.5)
55
+
SW_A
[2]*tau*tau +
SW_A
[3]*std::pow(tau,4.0);
56
return
PC_CO2_BAR
*std::exp((
TC_CO2_K
/T_K)*
s
);
57
}
58
59
inline
bool
co2_is_liquid
(
double
T_C,
double
P_bar) {
60
if
(T_C + 273.15 >=
TC_CO2_K
)
return
false
;
61
return
P_bar >=
co2_saturation_pressure_bar
(T_C);
62
}
63
64
// real roots of x^3 + b x^2 + c x + d = 0 ; returns count, fills r[3]
65
inline
int
solve_cubic_real
(
double
b,
double
c
,
double
d,
double
r
[3]) {
66
double
p =
c
- b*b/3.0;
67
double
q
= 2.0*b*b*b/27.0 - b*
c
/3.0 + d;
68
double
shift = -b/3.0;
69
double
disc =
q
*
q
/4.0 + p*p*p/27.0;
70
if
(disc > 1e-14) {
// one real root
71
double
s
= std::sqrt(disc);
72
r
[0] = std::cbrt(-
q
/2.0 +
s
) + std::cbrt(-
q
/2.0 -
s
) + shift;
73
return
1;
74
}
else
if
(disc < -1e-14) {
// three real roots
75
double
m = 2.0*std::sqrt(-p/3.0);
76
double
theta = std::acos(std::max(-1.0, std::min(1.0,
77
3.0*
q
/(p*m))))/3.0;
78
for
(
int
k = 0; k < 3; ++k)
79
r
[k] = m*std::cos(theta - 2.0*M_PI*k/3.0) + shift;
80
return
3;
81
}
else
{
// (near) repeated
82
double
u
= std::cbrt(-
q
/2.0);
83
r
[0] = 2.0*
u
+ shift;
84
r
[1] = -
u
+ shift;
85
return
2;
86
}
87
}
88
89
// molar volume [cm^3/mol] of the CO2-rich phase (RK, infinite-dilution a,b=CO2)
90
inline
double
rk_molar_volume
(
double
T_C,
double
P_bar,
bool
liquid) {
91
double
T_K = T_C + 273.15;
92
double
a =
A_CO2_0
-
A_CO2_1
*T_K;
93
double
b =
B_CO2
;
94
double
A = a*P_bar/(
R_CM3_BAR
*
R_CM3_BAR
*std::pow(T_K,2.5));
95
double
B
= b*P_bar/(
R_CM3_BAR
*T_K);
96
double
roots[3];
97
int
n
=
solve_cubic_real
(-1.0, (A -
B
-
B
*
B
), -A*
B
, roots);
98
double
Zsel = liquid ? INFINITY : -INFINITY;
99
bool
found =
false
;
100
for
(
int
i = 0; i <
n
; ++i) {
101
if
(roots[i] <=
B
)
continue
;
// physical: Z > B
102
if
(liquid) {
if
(roots[i] < Zsel) { Zsel = roots[i]; found =
true
; } }
103
else
{
if
(roots[i] > Zsel) { Zsel = roots[i]; found =
true
; } }
104
}
105
if
(!found) {
// fall back: any root
106
for
(
int
i = 0; i <
n
; ++i)
107
if
(liquid ? roots[i] < Zsel : roots[i] > Zsel) Zsel = roots[i];
108
}
109
return
Zsel*
R_CM3_BAR
*T_K/P_bar;
110
}
111
112
inline
double
ln_phi
(
double
T_C,
double
P_bar,
double
V,
113
double
b_k,
double
sum_aik) {
114
double
T_K = T_C + 273.15;
115
double
a =
A_CO2_0
-
A_CO2_1
*T_K;
116
double
b =
B_CO2
;
117
double
RT15 =
R_CM3_BAR
*std::pow(T_K,1.5);
118
double
ln_ratio = std::log((V + b)/V);
119
return
std::log(V/(V - b))
120
+ b_k/(V - b)
121
- (2.0*sum_aik/(RT15*b))*ln_ratio
122
+ (a*b_k/(RT15*b*b))*(ln_ratio - b/(V + b))
123
- std::log(P_bar*V/(
R_CM3_BAR
*T_K));
124
}
125
126
// mutual solubilities at (T_C [C], P_bar [bar]) -> x_CO2 (aqueous), y_H2O (gas)
127
inline
void
spycher_pruess_solubility
(
double
T_C,
double
P_bar,
double
/*m_NaCl*/
,
128
double
& x_CO2,
double
& y_H2O) {
129
bool
liquid =
co2_is_liquid
(T_C, P_bar);
130
double
T_K = T_C + 273.15;
131
double
V =
rk_molar_volume
(T_C, P_bar, liquid);
132
double
phi_CO2 = std::exp(
ln_phi
(T_C, P_bar, V,
B_CO2
,
A_CO2_0
-
A_CO2_1
*T_K));
133
double
phi_H2O = std::exp(
ln_phi
(T_C, P_bar, V,
B_H2O
,
A_H2O_CO2
));
134
double
K0H = std::pow(10.0,
poly_logK0
(
K0_H2O
, T_C));
135
double
K0C = std::pow(10.0,
poly_logK0
(liquid ?
K0_CO2_L
:
K0_CO2_G
, T_C));
136
double
dP = P_bar -
P0_BAR
;
137
double
A = (K0H/(phi_H2O*P_bar))*std::exp(dP*
V_H2O
/(
R_CM3_BAR
*T_K));
138
double
B
= (phi_CO2*P_bar/(
M_PER_KG_H2O
*K0C))*std::exp(-dP*
V_CO2
/(
R_CM3_BAR
*T_K));
139
y_H2O = (1.0 -
B
)/(1.0/A -
B
);
140
x_CO2 =
B
*(1.0 - y_H2O);
141
}
142
143
inline
double
co2_rich_molar_density
(
double
T_C,
double
P_bar) {
144
return
1.0e6/
rk_molar_volume
(T_C, P_bar,
co2_is_liquid
(T_C, P_bar));
145
}
146
147
inline
double
pure_water_density_gcc
(
double
T_C) {
// Kell (1975), g/cm^3
148
double
num
= 999.83952 + 16.945176*T_C - 7.9870401e-3*T_C*T_C
149
- 46.170461e-6*T_C*T_C*T_C + 105.56302e-9*std::pow(T_C,4)
150
- 280.54253e-12*std::pow(T_C,5);
151
return
num
/(1.0 + 16.879850e-3*T_C)/1000.0;
152
}
153
154
inline
double
brine_molar_density
(
double
T_C,
double
P_bar,
double
x_CO2) {
155
double
Vphi = 37.51 - 9.585e-2*T_C + 8.740e-4*T_C*T_C - 5.044e-7*T_C*T_C*T_C;
156
double
rho_w =
pure_water_density_gcc
(T_C);
157
double
n_H2O =
M_PER_KG_H2O
;
158
double
n_CO2 = x_CO2/(1.0 - x_CO2)*n_H2O;
159
double
vol_cm3 = (n_H2O*18.015)/rho_w + n_CO2*Vphi;
160
double
rho0 = ((n_H2O + n_CO2)/vol_cm3)*1.0e6;
161
return
rho0*std::exp(
C_F_BRINE
*(P_bar*1.0e5 -
P_REF_BRINE
));
162
}
163
164
}
// namespace eos
165
}
// namespace m_comp_co2
166
#endif
n
Int n
Definition
Headers.h:28
q
Double q
Definition
Headers.h:81
r
Double r
Definition
Headers.h:83
B
Double * B
Definition
Headers.h:41
s
Double s
Definition
Headers.h:84
u
Double u
Definition
Headers.h:89
num
Int num
Definition
Headers.h:32
c
#define c(i)
Definition
jf.h:21
m_comp_co2::eos
Definition
co2_brine_eos.h:14
m_comp_co2::eos::A_CO2_1
constexpr double A_CO2_1
Definition
co2_brine_eos.h:22
m_comp_co2::eos::ln_phi
double ln_phi(double T_C, double P_bar, double V, double b_k, double sum_aik)
Definition
co2_brine_eos.h:112
m_comp_co2::eos::R_CM3_BAR
constexpr double R_CM3_BAR
Definition
co2_brine_eos.h:17
m_comp_co2::eos::spycher_pruess_solubility
void spycher_pruess_solubility(double T_C, double P_bar, double, double &x_CO2, double &y_H2O)
Definition
co2_brine_eos.h:127
m_comp_co2::eos::poly_logK0
double poly_logK0(const double c[5], double T_C)
Definition
co2_brine_eos.h:46
m_comp_co2::eos::C_F_BRINE
constexpr double C_F_BRINE
Definition
co2_brine_eos.h:32
m_comp_co2::eos::co2_saturation_pressure_bar
double co2_saturation_pressure_bar(double T_C)
Definition
co2_brine_eos.h:50
m_comp_co2::eos::rk_molar_volume
double rk_molar_volume(double T_C, double P_bar, bool liquid)
Definition
co2_brine_eos.h:90
m_comp_co2::eos::B_H2O
constexpr double B_H2O
Definition
co2_brine_eos.h:25
m_comp_co2::eos::TC_CO2_K
constexpr double TC_CO2_K
Definition
co2_brine_eos.h:43
m_comp_co2::eos::co2_rich_molar_density
double co2_rich_molar_density(double T_C, double P_bar)
Definition
co2_brine_eos.h:143
m_comp_co2::eos::solve_cubic_real
int solve_cubic_real(double b, double c, double d, double r[3])
Definition
co2_brine_eos.h:65
m_comp_co2::eos::V_H2O
constexpr double V_H2O
Definition
co2_brine_eos.h:27
m_comp_co2::eos::co2_is_liquid
bool co2_is_liquid(double T_C, double P_bar)
Definition
co2_brine_eos.h:59
m_comp_co2::eos::P0_BAR
constexpr double P0_BAR
Definition
co2_brine_eos.h:18
m_comp_co2::eos::M_H2O_KG
constexpr double M_H2O_KG
Definition
co2_brine_eos.h:36
m_comp_co2::eos::V_CO2
constexpr double V_CO2
Definition
co2_brine_eos.h:27
m_comp_co2::eos::K0_CO2_L
constexpr double K0_CO2_L[5]
Definition
co2_brine_eos.h:41
m_comp_co2::eos::SW_A
constexpr double SW_A[4]
Definition
co2_brine_eos.h:44
m_comp_co2::eos::brine_molar_density
double brine_molar_density(double T_C, double P_bar, double x_CO2)
Definition
co2_brine_eos.h:154
m_comp_co2::eos::M_CO2_KG
constexpr double M_CO2_KG
Definition
co2_brine_eos.h:35
m_comp_co2::eos::B_CO2
constexpr double B_CO2
Definition
co2_brine_eos.h:24
m_comp_co2::eos::M_PER_KG_H2O
constexpr double M_PER_KG_H2O
Definition
co2_brine_eos.h:19
m_comp_co2::eos::pure_water_density_gcc
double pure_water_density_gcc(double T_C)
Definition
co2_brine_eos.h:147
m_comp_co2::eos::A_CO2_0
constexpr double A_CO2_0
Definition
co2_brine_eos.h:21
m_comp_co2::eos::K0_H2O
constexpr double K0_H2O[5]
Definition
co2_brine_eos.h:39
m_comp_co2::eos::P_REF_BRINE
constexpr double P_REF_BRINE
Definition
co2_brine_eos.h:33
m_comp_co2::eos::PC_CO2_BAR
constexpr double PC_CO2_BAR
Definition
co2_brine_eos.h:43
m_comp_co2::eos::A_H2O_CO2
constexpr double A_H2O_CO2
Definition
co2_brine_eos.h:23
m_comp_co2::eos::K0_CO2_G
constexpr double K0_CO2_G[5]
Definition
co2_brine_eos.h:40
m_comp_co2
Definition
co2_brine_eos.h:13
Generated on
for proteus by
1.18.0