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