proteus 1.9.0
C/C++/Fortran libraries
Loading...
Searching...
No Matches
co2_brine_flash.h
Go to the documentation of this file.
1#ifndef CO2_BRINE_FLASH_H
2#define CO2_BRINE_FLASH_H
3// Analytic (p,z) flash for the 2-component CO2-brine model -- C++ port of
4// co2_brine_flash.py. Header-only; the kernel includes this.
5//
6// flashPZ(p_Pa, z, T_C, m_NaCl, eps) -> FlashState
7//
8// All first AND second derivatives are exact, computed by 2nd-order forward-mode
9// AD (Jet2) -- NO finite differencing. The composition clamps are hyperbolically
10// smoothed so S_g is C1 across phase appearance/disappearance and the capillary
11// value-block Jacobian (which needs d2S_g) is fully consistent.
12#include "co2_brine_eos.h"
13#include "jet2.h"
14#include <cmath>
15#include <algorithm>
16
17namespace m_comp_co2 {
18namespace flash {
19
20constexpr double PA_PER_BAR = 1.0e5;
21// composition-clamp smoothing band [mole fraction]. See co2_brine_flash.py for
22// the full rationale: smooth_min undershoots X below z, and the lever rule
23// amplifies that by rho_a/rho_g (~1400x) into S_g. At SI scale (Xeq~6e-4) the old
24// 5e-4 leaked S_g~0.10 at z=1e-5; 1e-5 keeps spurious S_g ~5e-5 while staying C1.
25constexpr double EPS_Z = 1.0e-5; // composition-clamp smoothing band
26
27// Immiscible / incompressible limit (verification only) -- must match
28// co2_brine_flash.py. When enabled (flashPZ immiscible=true, set from the
29// Coefficients 'immiscible' option) mutual solubility is suppressed (Xeq=0,
30// Yeq=1) and both phase densities are pinned to these constants, so the species
31// decouple into the classical immiscible two-phase saturation equations. The
32// McWhorter-Sunada S_n similarity profile is invariant to the density values
33// (they divide out of each saturation equation); any IC/BC converting S_n <-> z
34// MUST use the SAME two constants.
35constexpr double RHO_A_IMM = 5.55e4; // constant aqueous molar density [mol/m^3]
36constexpr double RHO_G_IMM = 2.00e4; // constant gas molar density [mol/m^3]
37
38struct FlashState {
39 double S_g, X, Y, rho_a, rho_g, f_g;
42 double d2S_g_dp2, d2S_g_dpdz, d2S_g_dz2; // flash Hessian (capillary Jac)
43 int phase; // 0 aqueous, 1 two-phase, 2 gas
44};
45
46// CO2-rich molar volume as a 2-jet: value from the float cubic solver, then
47// Newton-refined in jet space against the RK residual so (p,z) derivs are exact.
48inline Jet2 _V_jet(double T_C, const Jet2& P_jet, bool liquid) {
49 using namespace m_comp_co2::eos;
50 double T_K = T_C + 273.15;
51 double a = A_CO2_0 - A_CO2_1*T_K, b = B_CO2;
52 double RTK = R_CM3_BAR*T_K, T05 = std::sqrt(T_K);
53 Jet2 V = Jet2::cst(rk_molar_volume(T_C, P_jet.v, liquid));
54 for (int it = 0; it < 4; ++it) {
55 Jet2 VVb = V*(V + b);
56 Jet2 F = P_jet - RTK*recip(V - b) + (a/T05)*recip(VVb);
57 Jet2 dFdV = RTK*recip((V - b)*(V - b))
58 - (a/T05)*(2.0*V + b)*recip(VVb*VVb);
59 V = V - F*recip(dFdV);
60 }
61 return V;
62}
63
64inline Jet2 _ln_phi_jet(double T_C, const Jet2& P_jet, const Jet2& V,
65 double b_k, double sum_aik) {
66 using namespace m_comp_co2::eos;
67 double T_K = T_C + 273.15;
68 double a = A_CO2_0 - A_CO2_1*T_K, b = B_CO2;
69 double RTK = R_CM3_BAR*T_K, RT15 = R_CM3_BAR*std::pow(T_K, 1.5);
70 Jet2 lnr = jlog((V + b)/V);
71 return jlog(V/(V - b))
72 + b_k*recip(V - b)
73 - (2.0*sum_aik/(RT15*b))*lnr
74 + (a*b_k/(RT15*b*b))*(lnr - b*recip(V + b))
75 - jlog(P_jet*V/RTK);
76}
77
78inline void _solubility_jet(double T_C, const Jet2& P_jet, bool liquid,
79 Jet2& x_CO2, Jet2& y_H2O, Jet2& V) {
80 using namespace m_comp_co2::eos;
81 double T_K = T_C + 273.15, RTK = R_CM3_BAR*T_K;
82 V = _V_jet(T_C, P_jet, liquid);
83 Jet2 phi_CO2 = jexp(_ln_phi_jet(T_C, P_jet, V, B_CO2, A_CO2_0 - A_CO2_1*T_K));
84 Jet2 phi_H2O = jexp(_ln_phi_jet(T_C, P_jet, V, B_H2O, A_H2O_CO2));
85 double K0H = std::pow(10.0, poly_logK0(K0_H2O, T_C));
86 double K0C = std::pow(10.0, poly_logK0(liquid ? K0_CO2_L : K0_CO2_G, T_C));
87 Jet2 dP = P_jet - P0_BAR;
88 Jet2 A = (K0H*recip(phi_H2O*P_jet))*jexp(dP*(V_H2O/RTK));
89 Jet2 B = (phi_CO2*P_jet*(1.0/(M_PER_KG_H2O*K0C)))*jexp(dP*(-V_CO2/RTK));
90 y_H2O = (1.0 - B)*recip(recip(A) - B);
91 x_CO2 = B*(1.0 - y_H2O);
92}
93
94inline FlashState flashPZ(double p_Pa, double z, double T_C,
95 double /*m_NaCl*/ = 0.0, double eps = EPS_Z,
96 bool immiscible = false) {
97 using namespace m_comp_co2::eos;
98 // Immiscible/incompressible verification limit (Xeq=0, Yeq=1, const
99 // densities); driven by the Coefficients 'immiscible' option via argsDict.
100 Jet2 p_jet = Jet2::var_p(p_Pa);
101 Jet2 z_jet = Jet2::var_z(z);
102
103 Jet2 X, Y, rho_a, rho_g;
104 if (immiscible) {
105 // No mutual solubility (Xeq=0, Yeq=1), constant densities: f_g = z and
106 // the lever rule maps z -> S_g with all p-derivatives exactly zero, so
107 // the species balances reduce to the two saturation equations.
108 X = Jet2::cst(0.0);
109 Y = Jet2::cst(1.0);
110 rho_a = Jet2::cst(RHO_A_IMM);
111 rho_g = Jet2::cst(RHO_G_IMM);
112 } else {
113 Jet2 pb_jet = p_jet*(1.0/PA_PER_BAR);
114 bool liquid = co2_is_liquid(T_C, pb_jet.v);
115
116 Jet2 Xeq, yH2O, V_jet;
117 _solubility_jet(T_C, pb_jet, liquid, Xeq, yH2O, V_jet);
118 Jet2 Yeq = 1.0 - yH2O;
119 rho_g = 1.0e6*recip(V_jet);
120
121 Jet2 sx = jsqrt((z_jet - Xeq)*(z_jet - Xeq) + eps*eps);
122 X = 0.5*(z_jet + Xeq - sx);
123 Jet2 sy = jsqrt((z_jet - Yeq)*(z_jet - Yeq) + eps*eps);
124 Y = 0.5*(z_jet + Yeq + sy);
125
126 double rho_w = pure_water_density_gcc(T_C);
127 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;
128 double c0 = 18.015/rho_w;
129 rho_a = (1.0e6*recip(c0 + (Vphi - c0)*X))
130 * jexp((p_jet - P_REF_BRINE)*C_F_BRINE); // brine compressibility
131 }
132
133 Jet2 f_g = (z_jet - X)*recip(Y - X);
134 Jet2 G = f_g*recip(rho_g);
135 Jet2 L = (1.0 - f_g)*recip(rho_a);
136 Jet2 S_g = G*recip(G + L);
137
139 s.S_g = S_g.v; s.X = X.v; s.Y = Y.v; s.rho_a = rho_a.v; s.rho_g = rho_g.v;
140 s.f_g = f_g.v;
141 s.dS_g_dp = S_g.dp; s.dS_g_dz = S_g.dz;
142 s.dX_dp = X.dp; s.dX_dz = X.dz; s.dY_dp = Y.dp; s.dY_dz = Y.dz;
143 s.drho_a_dp = rho_a.dp; s.drho_a_dz = rho_a.dz;
144 s.drho_g_dp = rho_g.dp; s.drho_g_dz = rho_g.dz;
145 s.d2S_g_dp2 = S_g.dpp; s.d2S_g_dpdz = S_g.dpz; s.d2S_g_dz2 = S_g.dzz;
146 s.phase = (f_g.v < 1.0e-6) ? 0 : (f_g.v > 1.0 - 1.0e-6 ? 2 : 1);
147 return s;
148}
149
150} // namespace flash
151} // namespace m_comp_co2
152#endif
Double L
Definition Headers.h:72
Double * B
Definition Headers.h:41
Double s
Definition Headers.h:84
Double * z
Definition Headers.h:49
Double * Y
Definition Headers.h:48
constexpr double A_CO2_1
constexpr double R_CM3_BAR
double poly_logK0(const double c[5], double T_C)
constexpr double C_F_BRINE
double rk_molar_volume(double T_C, double P_bar, bool liquid)
constexpr double B_H2O
constexpr double V_H2O
bool co2_is_liquid(double T_C, double P_bar)
constexpr double P0_BAR
constexpr double V_CO2
constexpr double K0_CO2_L[5]
constexpr double B_CO2
constexpr double M_PER_KG_H2O
double pure_water_density_gcc(double T_C)
constexpr double A_CO2_0
constexpr double K0_H2O[5]
constexpr double P_REF_BRINE
constexpr double A_H2O_CO2
constexpr double K0_CO2_G[5]
void _solubility_jet(double T_C, const Jet2 &P_jet, bool liquid, Jet2 &x_CO2, Jet2 &y_H2O, Jet2 &V)
constexpr double RHO_A_IMM
constexpr double PA_PER_BAR
constexpr double RHO_G_IMM
FlashState flashPZ(double p_Pa, double z, double T_C, double=0.0, double eps=EPS_Z, bool immiscible=false)
Jet2 _ln_phi_jet(double T_C, const Jet2 &P_jet, const Jet2 &V, double b_k, double sum_aik)
constexpr double EPS_Z
Jet2 _V_jet(double T_C, const Jet2 &P_jet, bool liquid)
Jet2 jsqrt(const Jet2 &s)
Definition jet2.h:63
Jet2 recip(const Jet2 &s)
Definition jet2.h:52
Jet2 jexp(const Jet2 &s)
Definition jet2.h:70
Jet2 jlog(const Jet2 &s)
Definition jet2.h:77
double dp
Definition jet2.h:22
double dpp
Definition jet2.h:22
static Jet2 var_z(double z)
Definition jet2.h:28
double dz
Definition jet2.h:22
double dpz
Definition jet2.h:22
double dzz
Definition jet2.h:22
static Jet2 var_p(double p)
Definition jet2.h:27
static Jet2 cst(double c)
Definition jet2.h:26
double v
Definition jet2.h:22