1 #include "Substances/Solute/SoluteHKFreaktoro.h"
2 #include "Common/Exception.h"
3 #include "Substance.h"
4 #include "ThermoParameters.h"
5 #include "ThermoProperties.h"
6
7 namespace ThermoFun {
8
9 /// The reference temperature assumed in the HKF equations of state (in units of K)
10 const double referenceTemperature = 298.15;
11
12 /// The reference temperature assumed in the HKF equations of state (in units of bar)
13 const double referencePressure = 1.0;
14
15 /// The reference dielectric constant of water \epsilon
16 const double referenceDielectricConstant = 78.24385513;
17
18 /// The reference Born function Z (dimensionless)
19 const double referenceBornZ = -1.278055636e-02;
20
21 /// The reference Born function Y (dimensionless)
22 const double referenceBornY = -5.795424563e-05;
23
24 /// The reference Born function Q (dimensionless)
25 const double referenceBornQ = 6.638388994e-12;
26
27 /// The reference Born function N (dimensionless)
28 const double referenceBornN = -2.321814455e-20;
29
30 /// The reference Born function U (dimensionless)
31 const double referenceBornU = 4.872982291e-14;
32
33 /// The reference Born function X (dimensionless)
34 const double referenceBornX = -3.060388224e-07;
35
36 /// The \eta constant in the HKF model (in units of (A*cal)/mol)
37 const double eta = 1.66027e+05;
38
39 /// The constant characteristics \Theta of the solvent (in units of K)
40 const double theta = 228;
41
42 /// The constant characteristics \Psi of the solvent (in units of bar)
43 const double psi = 2600;
44
thermoPropertiesAqSoluteHKFreaktoro(Reaktoro_::Temperature TK,Reaktoro_::Pressure Pbar,Substance subst,const ElectroPropertiesSubstance & aes,const ElectroPropertiesSolvent & wes,const PropertiesSolvent & wp)45 auto thermoPropertiesAqSoluteHKFreaktoro(Reaktoro_::Temperature TK, Reaktoro_::Pressure Pbar, Substance subst, const ElectroPropertiesSubstance& aes, const ElectroPropertiesSolvent& wes, const PropertiesSolvent& wp) -> ThermoPropertiesSubstance
46 {
47 // Get the HKF thermodynamic data of the species
48 auto hkf = subst.thermoParameters().HKF_parameters;
49 auto refProp = subst.thermoReferenceProperties();
50
51 if (hkf.size() == 0)
52 {
53 Exception exception;
54 exception.error << "Error in HKFreaktoro EOS";
55 exception.reason << "The HKF paramteres for "<< subst.symbol() << " are not defined or are not correclty initialized.";
56 exception.line = __LINE__;
57 RaiseError(exception)
58 }
59 // if (hkf[0] == 0.0 || hkf[1] == 0.0 || hkf[2] == 0.0)
60 // {
61 // Exception exception;
62 // exception.error << "Error in HKFreaktoro EOS";
63 // exception.reason << "The HKF paramteres for "<< subst.symbol() << " are not defined or are not correclty initialized.";
64 // exception.line = __LINE__;
65 // RaiseError(exception);
66 // }
67
68 // Auxiliary variables
69 const auto Tr = referenceTemperature;
70 const auto Pr = referencePressure;
71 const auto Zr = referenceBornZ;
72 const auto Yr = referenceBornY;
73 const auto Gf = refProp.gibbs_energy / cal_to_J;
74 const auto Hf = refProp.enthalpy / cal_to_J;
75 const auto Sr = refProp.entropy / cal_to_J;
76 const auto a1 = hkf[0];
77 const auto a2 = hkf[1];
78 const auto a3 = hkf[2];
79 const auto a4 = hkf[3];
80 const auto c1 = hkf[4];
81 const auto c2 = hkf[5];
82 const auto wr = hkf[6];
83 const auto w = aes.w;
84 const auto wT = aes.wT;
85 const auto wP = aes.wP*1e05; // from 1/Pa to 1/bar
86 const auto wTT = aes.wTT;
87 const auto Z = wes.bornZ;
88 const auto Y = wes.bornY;
89 const auto Q = wes.bornQ*1e05; // from 1/Pa to 1/bar
90 const auto X = wes.bornX;
91
92 // Calculate the standard molal thermodynamic properties of the aqueous species
93 auto V = 0.4184004e2 * (a1 + a2/(psi + Pbar) +
94 (a3 + a4/(psi + Pbar))/(TK - theta) - w*Q - (Z + 1)*wP);
95
96 auto G = Gf - Sr*(TK - Tr) - c1*(TK*log(TK/Tr) - TK + Tr)
97 + a1*(Pbar - Pr) + a2*log((psi + Pbar)/(psi + Pr))
98 - c2*((1.0/(TK - theta) - 1.0/(Tr - theta))*(theta - TK)/theta
99 - TK/(theta*theta)*log(Tr/TK * (TK - theta)/(Tr - theta)))
100 + 1.0/(TK - theta)*(a3*(Pbar - Pr) + a4*log((psi + Pbar)/(psi + Pr)))
101 - w*(Z + 1) + wr*(Zr + 1) + wr*Yr*(TK - Tr);
102
103 auto H = Hf + c1*(TK - Tr) - c2*(1.0/(TK - theta) - 1.0/(Tr - theta))
104 + a1*(Pbar - Pr) + a2*log((psi + Pbar)/(psi + Pr))
105 + (2*TK - theta)/pow(TK - theta, 2)*(a3*(Pbar - Pr)
106 + a4*log((psi + Pbar)/(psi + Pr)))
107 - w*(Z + 1) + w*TK*Y + TK*(Z + 1)*wT + wr*(Zr + 1) - wr*Tr*Yr;
108
109 auto S = Sr + c1*log(TK/Tr) - c2/theta*(1.0/(TK - theta)
110 - 1.0/(Tr - theta) + log(Tr/TK * (TK - theta)/(Tr - theta))/theta)
111 + 1.0/pow(TK - theta, 2)*(a3*(Pbar - Pr) + a4*log((psi + Pbar)/(psi + Pr)))
112 + w*Y + (Z + 1)*wT - wr*Yr;
113
114 auto Cp = c1 + c2/pow(TK - theta, 2) - (2*TK/pow(TK - theta, 3))*(a3*(Pbar - Pr)
115 + a4*log((psi + Pbar)/(psi + Pr))) + w*TK*X + 2*TK*Y*wT + TK*(Z + 1)*wTT;
116
117 auto U = H - Pbar*V;
118
119 auto A = U - TK*S;
120
121 // Convert the thermodynamic properties of the gas to the standard units
122 V *= 1e-01/**cal_to_J/bar_to_Pa*/;
123 G *= cal_to_J;
124 H *= cal_to_J;
125 S *= cal_to_J;
126 U *= cal_to_J;
127 A *= cal_to_J;
128 Cp *= cal_to_J;
129
130 ThermoPropertiesSubstance tps;
131 tps.volume = V;
132 tps.gibbs_energy = G;
133 tps.enthalpy = H;
134 tps.entropy = S;
135 tps.internal_energy = U;
136 tps.helmholtz_energy = A;
137 tps.heat_capacity_cp = Cp;
138 tps.heat_capacity_cv = tps.heat_capacity_cp; // approximate Cp = Cv for an aqueous solution
139
140 subst.checkCalcMethodBounds("HKF model", TK.val-C_to_K, Pbar.val, tps);
141 if (wp.density >= 1400 || wp.density<=600)
142 {
143 setMessage(Reaktoro_::Status::calculated, "HKF model: outside of 600-1400 kg/m3 density of pure H2O interval", tps);
144 }
145
146 return tps;
147 }
148
149
speciesElectroStateHKF(const FunctionG & g,Substance species)150 auto speciesElectroStateHKF(const FunctionG& g, Substance species) -> ElectroPropertiesSubstance
151 {
152 // Get the HKF thermodynamic parameters of the aqueous species
153 auto hkf = species.thermoParameters().HKF_parameters;
154
155 // The species electro instance to be calculated
156 ElectroPropertiesSubstance se;
157
158 // Check if the aqueous species is neutral or H+, and set its electrostatic data accordingly
159 if(species.charge() == 0.0 || (species.symbol() == "H+"))
160 {
161 se.w = hkf[6] /*hkf.wref*/;
162 se.wT = 0.0;
163 se.wP = 0.0;
164 se.wTT = 0.0;
165 se.wTP = 0.0;
166 se.wPP = 0.0;
167 }
168 else
169 {
170 const auto z = species.charge();
171 const auto wref = hkf[6] /*hkf.wref*/;
172
173 const auto reref = z*z/(wref/eta + z/3.082);
174 const auto re = reref + std::abs(z) * g.g;
175
176 const auto X1 = -eta * (std::abs(z*z*z)/(re*re) - z/pow(3.082 + g.g, 2));
177 const auto X2 = 2*eta * (z*z*z*z/(re*re*re) - z/pow(3.082 + g.g, 3));
178
179 se.re = re;
180 se.reref = reref;
181 se.w = eta * (z*z/re - z/(3.082 + g.g));
182 se.wT = X1 * g.gT;
183 se.wP = X1 * g.gP;
184 se.wTT = X1 * g.gTT + X2 * g.gT * g.gT;
185 se.wTP = X1 * g.gTP + X2 * g.gT * g.gP;
186 se.wPP = X1 * g.gPP + X2 * g.gP * g.gP;
187 }
188
189 return se;
190 }
191
192
functionG(Reaktoro_::Temperature T,Reaktoro_::Pressure P,const PropertiesSolvent & ps)193 auto functionG(Reaktoro_::Temperature T, Reaktoro_::Pressure P, const PropertiesSolvent& ps) -> FunctionG
194 {
195 // The function G
196 FunctionG funcG;
197
198 // The temperature in units of celsius and pressure in units of bar
199 const auto TdegC = T - 273.15;
200 const auto Pbar = P;
201
202 if (ps.density >= 1400)
203 {
204 std::cout << "water density higher than 1.4 g*cm-3, Dw = "
205 << ps.density / 1000 << "g*cm-3." << std::endl << __FILE__ << std::endl << __LINE__;
206 // Exception exception;
207 // exception.error << "Error in functionG";
208 // exception.reason << "water density higher than 1.4 g*cm-3, Dw = "
209 // << ps.density / 1000 << "g*cm-3.";
210 // exception.line = __LINE__;
211 // RaiseError(exception)
212 }
213
214 // Check if the point (T,P) is inside region III or the shaded region in Fig. 6 of
215 // Shock and others (1992), on page 809. In this case, we assume the g function to be zero.
216 //if(ps.density > 1000.0 || ps.density < 350.0)
217 if(ps.density > 1000.0)
218 return funcG;
219
220 // Auxiliary references
221 auto& g = funcG.g;
222 auto& gT = funcG.gT;
223 auto& gP = funcG.gP;
224 auto& gTT = funcG.gTT;
225 auto& gTP = funcG.gTP;
226 auto& gPP = funcG.gPP;
227
228 // Use equations (24)-(31) of Shock and others (1992) to compute `g` and its derivatives on region I
229 const auto ag1 = -2.037662;
230 const auto ag2 = 5.747000e-03;
231 const auto ag3 = -6.557892e-06;
232
233 const auto bg1 = 6.107361;
234 const auto bg2 = -1.074377e-02;
235 const auto bg3 = 1.268348e-05;
236
237 const auto ag = ag1 + ag2*TdegC + ag3*TdegC*TdegC;
238 const auto bg = bg1 + bg2*TdegC + bg3*TdegC*TdegC;
239
240 const auto agT = ag2 + 2*ag3*TdegC;
241 const auto bgT = bg2 + 2*bg3*TdegC;
242
243 const auto agTT = 2*ag3;
244 const auto bgTT = 2*bg3;
245
246 const auto r = ps.density/1000.0;
247
248 const auto alpha = -ps.densityT/ps.density;
249 const auto beta = ps.densityP/ps.density;
250 const auto alphaT = -ps.densityTT/ps.density + alpha*alpha;
251 const auto alphaP = -ps.densityTP/ps.density - alpha*beta;
252 const auto betaP = ps.densityPP/ps.density - beta*beta;
253
254 g = ag * pow(1 - r, bg);
255 gT = g * (agT/ag + bgT*log(1 - r) + r*alpha*bg/(1 - r));
256 gP = -g * r*beta*bg/(1 - r);
257 gTT = g * (agTT/ag - pow(agT/ag, 2) + bgTT*log(1 - r) + r*alpha*bg/(1 - r) * (2*bgT/bg + alphaT/alpha - alpha - r*alpha/(1 - r))) + gT*gT/g;
258 gTP = gP * (bgT/bg - alpha - alphaP/beta - r*alpha/(1 - r)) + gP*gT/g;
259 gPP = gP * (gP/g + beta + betaP/beta + r*beta/(1 - r));
260
261 // Check if the point (T,P) is inside region II, as depicted in Fig. 6 of Shock and others (1992), on page 809
262 if ((TdegC > 155.0 && TdegC < 355.0 && Pbar < 1000.0) || (TdegC>=355 && Pbar >=500 && Pbar < 1000) || (TdegC>=355 && Pbar<500))
263 {
264 // Use equations (32)-(44) of Shock and others (1992) to compute the function g and its partial derivatives on region II
265 const auto af1 = 3.666660e+01; // unit: K
266 const auto af2 = -1.504956e-10; // unit: A:bar^{-3}
267 const auto af3 = 5.017990e-14; // unit: A:bar^{-4}
268
269 const auto auxT = (TdegC - 155)/300;
270 const auto auxT1 = pow(auxT, 4.8);
271 const auto auxT2 = pow(auxT, 16);
272
273 const auto auxP = 1000 - Pbar;
274 const auto auxP1 = pow(auxP, 3);
275 const auto auxP2 = pow(auxP, 4);
276
277 const auto ft = auxT1 + af1*auxT2;
278 const auto ftT = ( 4.80 * auxT1 + 16.0 * af1*auxT2)/(300 * auxT);
279 const auto ftTT = (18.24 * auxT1 + 240.0 * af1*auxT2)/pow(300 * auxT, 2);
280
281 const auto fp = af2*auxP1 + af3*auxP2;
282 const auto fpP = -(3.0 * af2*auxP1 + 4.0 * af3*auxP2)/(auxP*1.e+5); // convert derivative from bar to Pa
283 const auto fpPP = (6.0 * af2*auxP1 + 12.0 * af3*auxP2)/pow(auxP*1.e+5, 2); // convert derivative from bar^2 to Pa^2
284
285 g -= ft * fp;
286 gT -= fp * ftT;
287 gP -= ft * fpP;
288 gTT -= fp * ftTT;
289 gTP -= ftT * fpP;
290 gPP -= ft * fpPP;
291 }
292
293 return funcG;
294 }
295
296 }
297