CoolProp 7.0.0
An open-source fluid property and humid air property database
FluidLibrary.h
Go to the documentation of this file.
1
2#ifndef FLUIDLIBRARY_H
3#define FLUIDLIBRARY_H
4
5#include "CoolPropFluid.h"
6
7#include "rapidjson_include.h"
9
10#include <map>
11#include <algorithm>
12#include "Configuration.h"
14#include "Helmholtz.h"
15
16namespace CoolProp {
17
18// Forward declaration of the necessary debug function to avoid including the whole header
19extern int get_debug_level();
20
22
28{
30 std::map<std::size_t, CoolPropFluid> fluid_map;
32 std::map<std::size_t, std::string> JSONstring_map;
33 std::vector<std::string> name_vector;
34 std::map<std::string, std::size_t> string_to_index_map;
35 bool _is_empty;
36
37 public:
39 static ResidualHelmholtzContainer parse_alphar(rapidjson::Value& jsonalphar) {
41
42 for (rapidjson::Value::ValueIterator itr = jsonalphar.Begin(); itr != jsonalphar.End(); ++itr) {
43 // A reference for code cleanness
44 rapidjson::Value& contribution = *itr;
45
46 // Get the type (required!)
47 std::string type = contribution["type"].GetString();
48
49 if (!type.compare("ResidualHelmholtzPower")) {
50 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
51 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
52 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
53 std::vector<CoolPropDbl> l = cpjson::get_long_double_array(contribution["l"]);
54 assert(n.size() == d.size());
55 assert(n.size() == t.size());
56 assert(n.size() == l.size());
57
58 alphar.GenExp.add_Power(n, d, t, l);
59 } else if (!type.compare("ResidualHelmholtzGaussian")) {
60 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
61 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
62 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
63 std::vector<CoolPropDbl> eta = cpjson::get_long_double_array(contribution["eta"]);
64 std::vector<CoolPropDbl> epsilon = cpjson::get_long_double_array(contribution["epsilon"]);
65 std::vector<CoolPropDbl> beta = cpjson::get_long_double_array(contribution["beta"]);
66 std::vector<CoolPropDbl> gamma = cpjson::get_long_double_array(contribution["gamma"]);
67 assert(n.size() == d.size());
68 assert(n.size() == t.size());
69 assert(n.size() == eta.size());
70 assert(n.size() == epsilon.size());
71 assert(n.size() == beta.size());
72 assert(n.size() == gamma.size());
73 alphar.GenExp.add_Gaussian(n, d, t, eta, epsilon, beta, gamma);
74 } else if (!type.compare("ResidualHelmholtzGaoB")) {
75 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
76 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
77 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
78 std::vector<CoolPropDbl> eta = cpjson::get_long_double_array(contribution["eta"]);
79 std::vector<CoolPropDbl> beta = cpjson::get_long_double_array(contribution["beta"]);
80 std::vector<CoolPropDbl> gamma = cpjson::get_long_double_array(contribution["gamma"]);
81 std::vector<CoolPropDbl> epsilon = cpjson::get_long_double_array(contribution["epsilon"]);
82 std::vector<CoolPropDbl> b = cpjson::get_long_double_array(contribution["b"]);
83 assert(n.size() == t.size());
84 assert(n.size() == d.size());
85 assert(n.size() == eta.size());
86 assert(n.size() == epsilon.size());
87 assert(n.size() == beta.size());
88 assert(n.size() == gamma.size());
89 assert(n.size() == b.size());
90 alphar.GaoB = ResidualHelmholtzGaoB(n, t, d, eta, beta, gamma, epsilon, b);
91 } else if (!type.compare("ResidualHelmholtzNonAnalytic")) {
92 if (alphar.NonAnalytic.N > 0) {
93 throw ValueError("Cannot add ");
94 }
95 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
96 std::vector<CoolPropDbl> a = cpjson::get_long_double_array(contribution["a"]);
97 std::vector<CoolPropDbl> b = cpjson::get_long_double_array(contribution["b"]);
98 std::vector<CoolPropDbl> beta = cpjson::get_long_double_array(contribution["beta"]);
99 std::vector<CoolPropDbl> A = cpjson::get_long_double_array(contribution["A"]);
100 std::vector<CoolPropDbl> B = cpjson::get_long_double_array(contribution["B"]);
101 std::vector<CoolPropDbl> C = cpjson::get_long_double_array(contribution["C"]);
102 std::vector<CoolPropDbl> D = cpjson::get_long_double_array(contribution["D"]);
103 assert(n.size() == a.size());
104 assert(n.size() == b.size());
105 assert(n.size() == beta.size());
106 assert(n.size() == A.size());
107 assert(n.size() == B.size());
108 assert(n.size() == C.size());
109 assert(n.size() == D.size());
110 alphar.NonAnalytic = ResidualHelmholtzNonAnalytic(n, a, b, beta, A, B, C, D);
111 } else if (!type.compare("ResidualHelmholtzLemmon2005")) {
112 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
113 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
114 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
115 std::vector<CoolPropDbl> l = cpjson::get_long_double_array(contribution["l"]);
116 std::vector<CoolPropDbl> m = cpjson::get_long_double_array(contribution["m"]);
117 assert(n.size() == d.size());
118 assert(n.size() == t.size());
119 assert(n.size() == l.size());
120 assert(n.size() == m.size());
121 alphar.GenExp.add_Lemmon2005(n, d, t, l, m);
122 } else if (!type.compare("ResidualHelmholtzDoubleExponential")) {
123 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
124 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
125 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
126 std::vector<CoolPropDbl> gt = cpjson::get_long_double_array(contribution["gt"]);
127 std::vector<CoolPropDbl> lt = cpjson::get_long_double_array(contribution["lt"]);
128 std::vector<CoolPropDbl> gd = cpjson::get_long_double_array(contribution["gd"]);
129 std::vector<CoolPropDbl> ld = cpjson::get_long_double_array(contribution["ld"]);
130
131 assert(n.size() == d.size());
132 assert(n.size() == t.size());
133 assert(n.size() == gt.size());
134 assert(n.size() == lt.size());
135 assert(n.size() == gd.size());
136 assert(n.size() == ld.size());
137 alphar.GenExp.add_DoubleExponential(n,d,t,gd,ld,gt,lt);
138 } else if (!type.compare("ResidualHelmholtzExponential")) {
139 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
140 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
141 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
142 std::vector<CoolPropDbl> g = cpjson::get_long_double_array(contribution["g"]);
143 std::vector<CoolPropDbl> l = cpjson::get_long_double_array(contribution["l"]);
144 assert(n.size() == d.size());
145 assert(n.size() == t.size());
146 assert(n.size() == g.size());
147 assert(n.size() == l.size());
148 alphar.GenExp.add_Exponential(n, d, t, g, l);
149 } else if (!type.compare("ResidualHelmholtzAssociating")) {
150 if (alphar.SAFT.disabled == false) {
151 throw ValueError("Cannot add ");
152 }
153 CoolPropDbl a = cpjson::get_double(contribution, "a");
154 CoolPropDbl m = cpjson::get_double(contribution, "m");
155 CoolPropDbl epsilonbar = cpjson::get_double(contribution, "epsilonbar");
156 CoolPropDbl vbarn = cpjson::get_double(contribution, "vbarn");
157 CoolPropDbl kappabar = cpjson::get_double(contribution, "kappabar");
158 alphar.SAFT = ResidualHelmholtzSAFTAssociating(a, m, epsilonbar, vbarn, kappabar);
159 } else {
160 throw ValueError(format("Unsupported Residual helmholtz type: %s", type.c_str()));
161 }
162 }
163
164 // Finish adding parts to the Generalized Exponential term, build other vectors
165 alphar.GenExp.finish();
166
167 return alphar;
168 };
169
171 static IdealHelmholtzContainer parse_alpha0(rapidjson::Value& jsonalpha0) {
172 if (!jsonalpha0.IsArray()) {
173 throw ValueError();
174 }
175
177
178 for (rapidjson::Value::ConstValueIterator itr = jsonalpha0.Begin(); itr != jsonalpha0.End(); ++itr) {
179 // A reference for code cleanness
180 const rapidjson::Value& contribution = *itr;
181
182 // Get the type (required!)
183 std::string type = contribution["type"].GetString();
184
185 if (!type.compare("IdealGasHelmholtzLead")) {
186 if (alpha0.Lead.is_enabled() == true) {
187 throw ValueError("Cannot add ");
188 }
189 CoolPropDbl a1 = cpjson::get_double(contribution, "a1");
190 CoolPropDbl a2 = cpjson::get_double(contribution, "a2");
191
192 alpha0.Lead = IdealHelmholtzLead(a1, a2);
193 } else if (!type.compare("IdealGasHelmholtzPower")) {
194 if (alpha0.Power.is_enabled() == true) {
195 throw ValueError("Cannot add ");
196 }
197 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
198 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
199
200 alpha0.Power = IdealHelmholtzPower(n, t);
201 } else if (!type.compare("IdealGasHelmholtzLogTau")) {
202 if (alpha0.LogTau.is_enabled() == true) {
203 throw ValueError("Cannot add ");
204 }
205 CoolPropDbl a = cpjson::get_double(contribution, "a");
206
207 alpha0.LogTau = IdealHelmholtzLogTau(a);
208 } else if (!type.compare("IdealGasHelmholtzPlanckEinsteinGeneralized")) {
209 // Retrieve the values
210 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
211 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
212
213 std::vector<CoolPropDbl> c = cpjson::get_long_double_array(contribution["c"]);
214 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution["d"]);
215
216 if (alpha0.PlanckEinstein.is_enabled() == true) {
217 alpha0.PlanckEinstein.extend(n, t, c, d);
218 } else {
220 }
221 } else if (!type.compare("IdealGasHelmholtzPlanckEinstein")) {
222 // Retrieve the values
223 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
224 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
225 // Flip the sign of theta
226 for (std::size_t i = 0; i < t.size(); ++i) {
227 t[i] *= -1;
228 }
229 std::vector<CoolPropDbl> c(n.size(), 1);
230 std::vector<CoolPropDbl> d(c.size(), -1);
231
232 if (alpha0.PlanckEinstein.is_enabled() == true) {
233 alpha0.PlanckEinstein.extend(n, t, c, d);
234 } else {
236 }
237 } else if (!type.compare("IdealGasHelmholtzPlanckEinsteinFunctionT")) {
238 // Retrieve the values
239 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
240 std::vector<CoolPropDbl> v = cpjson::get_long_double_array(contribution["v"]), theta(n.size(), 0.0);
241 // Calculate theta
242 double Tc = cpjson::get_double(contribution, "Tcrit");
243 for (std::size_t i = 0; i < v.size(); ++i) {
244 theta[i] = -v[i] / Tc;
245 }
246 std::vector<CoolPropDbl> c(n.size(), 1);
247 std::vector<CoolPropDbl> d(c.size(), -1);
248
249 if (alpha0.PlanckEinstein.is_enabled() == true) {
250 alpha0.PlanckEinstein.extend(n, theta, c, d);
251 } else {
253 }
254 } else if (!type.compare("IdealGasHelmholtzGERG2004Cosh")) {
255 // Retrieve the values
256 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
257 std::vector<CoolPropDbl> theta = cpjson::get_long_double_array(contribution["theta"]);
258 double Tc = cpjson::get_double(contribution, "Tcrit");
259 if (alpha0.GERG2004Cosh.is_enabled() == true) {
260 alpha0.GERG2004Cosh.extend(n, theta);
261 } else {
262 alpha0.GERG2004Cosh = IdealHelmholtzGERG2004Cosh(n, theta, Tc);
263 }
264 } else if (!type.compare("IdealGasHelmholtzGERG2004Sinh")) {
265 // Retrieve the values
266 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution["n"]);
267 std::vector<CoolPropDbl> theta = cpjson::get_long_double_array(contribution["theta"]);
268 double Tc = cpjson::get_double(contribution, "Tcrit");
269 if (alpha0.GERG2004Sinh.is_enabled() == true) {
270 alpha0.GERG2004Sinh.extend(n, theta);
271 } else {
272 alpha0.GERG2004Sinh = IdealHelmholtzGERG2004Sinh(n, theta, Tc);
273 }
274 } else if (!type.compare("IdealGasHelmholtzCP0Constant")) {
275 if (alpha0.CP0Constant.is_enabled() == true) {
276 throw ValueError("Cannot add another IdealGasHelmholtzCP0Constant term; join them together");
277 }
278 CoolPropDbl cp_over_R = cpjson::get_double(contribution, "cp_over_R");
279 CoolPropDbl Tc = cpjson::get_double(contribution, "Tc");
280 CoolPropDbl T0 = cpjson::get_double(contribution, "T0");
281 alpha0.CP0Constant = IdealHelmholtzCP0Constant(cp_over_R, Tc, T0);
282 } else if (!type.compare("IdealGasHelmholtzCP0PolyT")) {
283 if (alpha0.CP0PolyT.is_enabled() == true) {
284 throw ValueError("Cannot add another CP0PolyT term; join them together");
285 }
286 std::vector<CoolPropDbl> c = cpjson::get_long_double_array(contribution["c"]);
287 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution["t"]);
288 CoolPropDbl Tc = cpjson::get_double(contribution, "Tc");
289 CoolPropDbl T0 = cpjson::get_double(contribution, "T0");
290 alpha0.CP0PolyT = IdealHelmholtzCP0PolyT(c, t, Tc, T0);
291 } else if (!type.compare("IdealGasHelmholtzCP0AlyLee")) {
292
293 std::vector<CoolPropDbl> constants = cpjson::get_long_double_array(contribution["c"]);
294 CoolPropDbl Tc = cpjson::get_double(contribution, "Tc");
295 CoolPropDbl T0 = cpjson::get_double(contribution, "T0");
296
297 // Take the constant term if nonzero and set it as a polyT term
298 if (std::abs(constants[0]) > 1e-14) {
299 std::vector<CoolPropDbl> c(1, constants[0]), t(1, 0);
300 if (alpha0.CP0PolyT.is_enabled() == true) {
301 alpha0.CP0PolyT.extend(c, t);
302 } else {
303 alpha0.CP0PolyT = IdealHelmholtzCP0PolyT(c, t, Tc, T0);
304 }
305 }
306 std::vector<CoolPropDbl> n, c, d, t;
307 if (std::abs(constants[1]) > 1e-14) {
308 // sinh term can be converted by setting a_k = C, b_k = 2*D, c_k = -1, d_k = 1
309 n.push_back(constants[1]);
310 t.push_back(-2 * constants[2] / Tc);
311 c.push_back(1);
312 d.push_back(-1);
313 }
314 if (std::abs(constants[3]) > 1e-14) {
315 // cosh term can be converted by setting a_k = C, b_k = 2*D, c_k = 1, d_k = 1
316 n.push_back(-constants[3]);
317 t.push_back(-2 * constants[4] / Tc);
318 c.push_back(1);
319 d.push_back(1);
320 }
321 if (alpha0.PlanckEinstein.is_enabled() == true) {
322 alpha0.PlanckEinstein.extend(n, t, c, d);
323 } else {
325 }
326 } else if (!type.compare("IdealGasHelmholtzEnthalpyEntropyOffset")) {
327 CoolPropDbl a1 = cpjson::get_double(contribution, "a1");
328 CoolPropDbl a2 = cpjson::get_double(contribution, "a2");
329 std::string reference = cpjson::get_string(contribution, "reference");
330 alpha0.EnthalpyEntropyOffsetCore.set(a1, a2, reference);
331 } else {
332 std::cout << format("Unsupported ideal-gas Helmholtz type: %s\n", type.c_str());
333 //throw ValueError(format("Unsupported ideal-gas Helmholtz type: %s",type.c_str()));
334 }
335 }
336 return alpha0;
337 };
338
339 protected:
341 void parse_environmental(rapidjson::Value& json, CoolPropFluid& fluid) {
342 fluid.environment.ASHRAE34 = cpjson::get_string(json, "ASHRAE34");
343 fluid.environment.GWP20 = cpjson::get_double(json, "GWP20");
344 fluid.environment.GWP100 = cpjson::get_double(json, "GWP100");
345 fluid.environment.GWP500 = cpjson::get_double(json, "GWP500");
346 fluid.environment.HH = cpjson::get_double(json, "HH");
347 fluid.environment.FH = cpjson::get_double(json, "FH");
348 fluid.environment.PH = cpjson::get_double(json, "PH");
349 fluid.environment.ODP = cpjson::get_double(json, "ODP");
350 }
351
353 void parse_EOS(rapidjson::Value& EOS_json, CoolPropFluid& fluid) {
355 fluid.EOSVector.push_back(E);
356
357 EquationOfState& EOS = fluid.EOSVector.at(fluid.EOSVector.size() - 1);
358
359 // Universal gas constant [J/mol/K]
360 EOS.R_u = cpjson::get_double(EOS_json, "gas_constant");
361 EOS.molar_mass = cpjson::get_double(EOS_json, "molar_mass");
362 EOS.acentric = cpjson::get_double(EOS_json, "acentric");
363
364 EOS.pseudo_pure = cpjson::get_bool(EOS_json, "pseudo_pure");
365 EOS.limits.Tmax = cpjson::get_double(EOS_json, "T_max");
366 EOS.limits.pmax = cpjson::get_double(EOS_json, "p_max");
367
368 rapidjson::Value& reducing_state = EOS_json["STATES"]["reducing"];
369 rapidjson::Value& satminL_state = EOS_json["STATES"]["sat_min_liquid"];
370 rapidjson::Value& satminV_state = EOS_json["STATES"]["sat_min_vapor"];
371
372 // Reducing state
373 EOS.reduce.T = cpjson::get_double(reducing_state, "T");
374 EOS.reduce.rhomolar = cpjson::get_double(reducing_state, "rhomolar");
375 EOS.reduce.p = cpjson::get_double(reducing_state, "p");
376 EOS.reduce.hmolar = cpjson::get_double(reducing_state, "hmolar");
377 EOS.reduce.smolar = cpjson::get_double(reducing_state, "smolar");
378
379 EOS.sat_min_liquid.T = cpjson::get_double(satminL_state, "T");
380 EOS.sat_min_liquid.p = cpjson::get_double(satminL_state, "p");
381 EOS.sat_min_liquid.rhomolar = cpjson::get_double(satminL_state, "rhomolar");
382 EOS.sat_min_vapor.T = cpjson::get_double(satminV_state, "T");
383 EOS.sat_min_vapor.p = cpjson::get_double(satminV_state, "p");
384 EOS.sat_min_vapor.rhomolar = cpjson::get_double(satminV_state, "rhomolar");
385
387 EOS.limits.Tmin = cpjson::get_double(satminL_state, "T");
388 EOS.ptriple = cpjson::get_double(satminL_state, "p");
389 EOS.Ttriple = EOS.limits.Tmin;
390
391 // BibTex keys
392 EOS.BibTeX_EOS = cpjson::get_string(EOS_json, "BibTeX_EOS");
393 EOS.BibTeX_CP0 = cpjson::get_string(EOS_json, "BibTeX_CP0");
394
395 if (EOS_json.HasMember("SUPERANCILLARY")){
396 if (getenv("COOLPROP_DISABLE_SUPERANCILLARIES_ENTIRELY")){
397 }
398 else{
399 // This is inefficient as we do JSON(rapidjson) -> string -> JSON(nlohmann)
400 // which implies two large parsing passes
401 EOS.set_superancillaries_str(cpjson::json2string(EOS_json["SUPERANCILLARY"]));
402 }
403 }
404
405 EOS.alphar = parse_alphar(EOS_json["alphar"]);
406 EOS.alpha0 = parse_alpha0(EOS_json["alpha0"]);
407
408 // Store the prefactor multipliying alpha0 if present
409 if (EOS_json.HasMember("alpha0_prefactor")) {
410 EOS.alpha0.set_prefactor(cpjson::get_double(EOS_json, "alpha0_prefactor"));
411 }
412 if (EOS_json["STATES"].HasMember("hs_anchor")) {
413 rapidjson::Value& hs_anchor = EOS_json["STATES"]["hs_anchor"];
414 EOS.hs_anchor.T = cpjson::get_double(hs_anchor, "T");
415 EOS.hs_anchor.p = cpjson::get_double(hs_anchor, "p");
416 EOS.hs_anchor.rhomolar = cpjson::get_double(hs_anchor, "rhomolar");
417 EOS.hs_anchor.hmolar = cpjson::get_double(hs_anchor, "hmolar");
418 EOS.hs_anchor.smolar = cpjson::get_double(hs_anchor, "smolar");
419 }
420
421 if (EOS_json["STATES"].HasMember("pressure_max_sat")) {
422 rapidjson::Value& s = EOS_json["STATES"]["pressure_max_sat"];
423 EOS.max_sat_p.T = cpjson::get_double(s, "T");
424 EOS.max_sat_p.p = cpjson::get_double(s, "p");
425 EOS.max_sat_p.rhomolar = cpjson::get_double(s, "rhomolar");
426 if (s.HasMember("hmolar")) {
427 EOS.max_sat_p.hmolar = cpjson::get_double(s, "hmolar");
428 EOS.max_sat_p.smolar = cpjson::get_double(s, "smolar");
429 }
430 }
431
432 if (EOS_json["STATES"].HasMember("temperature_max_sat")) {
433 rapidjson::Value& s = EOS_json["STATES"]["temperature_max_sat"];
434 EOS.max_sat_T.T = cpjson::get_double(s, "T");
435 EOS.max_sat_T.p = cpjson::get_double(s, "p");
436 EOS.max_sat_T.rhomolar = cpjson::get_double(s, "rhomolar");
437 if (s.HasMember("hmolar")) {
438 EOS.max_sat_T.hmolar = cpjson::get_double(s, "hmolar");
439 EOS.max_sat_T.smolar = cpjson::get_double(s, "smolar");
440 }
441 }
442
443 if (EOS_json.HasMember("critical_region_splines")) {
444 rapidjson::Value& spline = EOS_json["critical_region_splines"];
445 EOS.critical_region_splines.T_min = cpjson::get_double(spline, "T_min");
446 EOS.critical_region_splines.T_max = cpjson::get_double(spline, "T_max");
447 EOS.critical_region_splines.rhomolar_min = cpjson::get_double(spline, "rhomolar_min");
448 EOS.critical_region_splines.rhomolar_max = cpjson::get_double(spline, "rhomolar_max");
452 }
453
454 // Validate the equation of state that was just created
455 EOS.validate();
456 }
457
459 void parse_EOS_listing(rapidjson::Value& EOS_array, CoolPropFluid& fluid) {
460 for (rapidjson::Value::ValueIterator itr = EOS_array.Begin(); itr != EOS_array.End(); ++itr) {
461 parse_EOS(*itr, fluid);
462 }
463 };
464
466 void parse_dilute_viscosity(rapidjson::Value& dilute, CoolPropFluid& fluid) {
467 if (dilute.HasMember("hardcoded")) {
468 std::string target = cpjson::get_string(dilute, "hardcoded");
469 if (!target.compare("Ethane")) {
471 return;
472 } else if (!target.compare("Cyclohexane")) {
474 return;
475 } else if (!target.compare("CarbonDioxideLaeseckeJPCRD2017")) {
477 return;
478 } else {
479 throw ValueError(format("hardcoded dilute viscosity [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
480 }
481 }
482 std::string type = cpjson::get_string(dilute, "type");
483 if (!type.compare("collision_integral")) {
484 // Get a reference to the entry in the fluid instance
486
487 // Set the type flag
489
490 // Load up the values
491 CI.a = cpjson::get_long_double_array(dilute["a"]);
492 CI.t = cpjson::get_long_double_array(dilute["t"]);
493 CI.molar_mass = cpjson::get_double(dilute, "molar_mass");
494 CI.C = cpjson::get_double(dilute, "C");
495 } else if (!type.compare("kinetic_theory")) {
497 } else if (!type.compare("powers_of_T")) {
498 // Get a reference to the entry in the fluid instance
500
501 // Load up the values
502 CI.a = cpjson::get_long_double_array(dilute["a"]);
503 CI.t = cpjson::get_long_double_array(dilute["t"]);
504
506 } else if (!type.compare("powers_of_Tr")) {
507 // Get a reference to the entry in the fluid instance
509 // Load up the values
510 CI.a = cpjson::get_long_double_array(dilute["a"]);
511 CI.t = cpjson::get_long_double_array(dilute["t"]);
512 CI.T_reducing = cpjson::get_double(dilute, "T_reducing");
514 } else if (!type.compare("collision_integral_powers_of_Tstar")) {
515 // Get a reference to the entry in the fluid instance
517
518 // Load up the values
519 CI.a = cpjson::get_long_double_array(dilute["a"]);
520 CI.t = cpjson::get_long_double_array(dilute["t"]);
521 CI.T_reducing = cpjson::get_double(dilute, "T_reducing");
522 CI.C = cpjson::get_double(dilute, "C");
523
525 } else {
526 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
527 }
528 };
529
531 void parse_initial_density_viscosity(rapidjson::Value& initial_density, CoolPropFluid& fluid) {
532 std::string type = cpjson::get_string(initial_density, "type");
533 if (!type.compare("Rainwater-Friend")) {
534 // Get a reference to the entry in the fluid instance
536
537 // Load up the values
538 RF.b = cpjson::get_long_double_array(initial_density["b"]);
539 RF.t = cpjson::get_long_double_array(initial_density["t"]);
540
541 // Set the type flag
543 } else if (!type.compare("empirical")) {
544 // Get a reference to the entry in the fluid instance
546
547 // Load up the values
548 EM.n = cpjson::get_long_double_array(initial_density["n"]);
549 EM.d = cpjson::get_long_double_array(initial_density["d"]);
550 EM.t = cpjson::get_long_double_array(initial_density["t"]);
551 EM.T_reducing = cpjson::get_double(initial_density, "T_reducing");
552 EM.rhomolar_reducing = cpjson::get_double(initial_density, "rhomolar_reducing");
553
554 // Set the type flag
556 } else {
557 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
558 }
559 };
560
562 void parse_higher_order_viscosity(rapidjson::Value& higher, CoolPropFluid& fluid) {
563 // First check for hardcoded higher-order term
564 if (higher.HasMember("hardcoded")) {
565 std::string target = cpjson::get_string(higher, "hardcoded");
566 if (!target.compare("Hydrogen")) {
568 return;
569 } else if (!target.compare("n-Hexane")) {
571 return;
572 } else if (!target.compare("n-Heptane")) {
574 return;
575 } else if (!target.compare("Toluene")) {
577 return;
578 } else if (!target.compare("Ethane")) {
580 return;
581 } else if (!target.compare("Benzene")) {
583 return;
584 } else if (!target.compare("CarbonDioxideLaeseckeJPCRD2017")) {
586 return;
587 } else {
588 throw ValueError(
589 format("hardcoded higher order viscosity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
590 }
591 }
592
593 std::string type = cpjson::get_string(higher, "type");
594 if (!type.compare("modified_Batschinski_Hildebrand")) {
595 // Get a reference to the entry in the fluid instance to simplify the code that follows
597
598 // Set the flag for the type of this model
600
601 BH.T_reduce = cpjson::get_double(higher, "T_reduce");
602 BH.rhomolar_reduce = cpjson::get_double(higher, "rhomolar_reduce");
603 // Load up the values
604 BH.a = cpjson::get_long_double_array(higher["a"]);
605 BH.t1 = cpjson::get_long_double_array(higher["t1"]);
606 BH.d1 = cpjson::get_long_double_array(higher["d1"]);
607 BH.gamma = cpjson::get_long_double_array(higher["gamma"]);
608 BH.l = cpjson::get_long_double_array(higher["l"]);
609 assert(BH.a.size() == BH.t1.size());
610 assert(BH.a.size() == BH.d1.size());
611 assert(BH.a.size() == BH.gamma.size());
612 assert(BH.a.size() == BH.l.size());
613 BH.f = cpjson::get_long_double_array(higher["f"]);
614 BH.t2 = cpjson::get_long_double_array(higher["t2"]);
615 BH.d2 = cpjson::get_long_double_array(higher["d2"]);
616 assert(BH.f.size() == BH.t2.size());
617 assert(BH.f.size() == BH.d2.size());
618 BH.g = cpjson::get_long_double_array(higher["g"]);
619 BH.h = cpjson::get_long_double_array(higher["h"]);
620 assert(BH.g.size() == BH.h.size());
621 BH.p = cpjson::get_long_double_array(higher["p"]);
622 BH.q = cpjson::get_long_double_array(higher["q"]);
623 assert(BH.p.size() == BH.q.size());
624 } else if (!type.compare("friction_theory")) {
625 // Get a reference to the entry in the fluid instance to simplify the code that follows
627
628 // Set the flag for the type of this model
630
631 // Always need these terms
632 F.Ai = cpjson::get_long_double_array(higher["Ai"]);
633 F.Aa = cpjson::get_long_double_array(higher["Aa"]);
634 F.Aaa = cpjson::get_long_double_array(higher["Aaa"]);
635 F.Ar = cpjson::get_long_double_array(higher["Ar"]);
636
637 F.Na = cpjson::get_integer(higher, "Na");
638 F.Naa = cpjson::get_integer(higher, "Naa");
639 F.Nr = cpjson::get_integer(higher, "Nr");
640 F.Nrr = cpjson::get_integer(higher, "Nrr");
641 F.c1 = cpjson::get_double(higher, "c1");
642 F.c2 = cpjson::get_double(higher, "c2");
643 assert(F.Aa.size() == 3);
644 assert(F.Aaa.size() == 3);
645 assert(F.Ar.size() == 3);
646
647 F.T_reduce = cpjson::get_double(higher, "T_reduce");
648
649 if (higher.HasMember("Arr") && !higher.HasMember("Adrdr")) {
650 F.Arr = cpjson::get_long_double_array(higher["Arr"]);
651 assert(F.Arr.size() == 3);
652 } else if (higher.HasMember("Adrdr") && !higher.HasMember("Arr")) {
653 F.Adrdr = cpjson::get_long_double_array(higher["Adrdr"]);
654 assert(F.Adrdr.size() == 3);
655 } else {
656 throw ValueError(format("can only provide one of Arr or Adrdr for fluid %s", fluid.name.c_str()));
657 }
658 if (higher.HasMember("Aii")) {
659 F.Aii = cpjson::get_long_double_array(higher["Aii"]);
660 F.Nii = cpjson::get_integer(higher, "Nii");
661 }
662 if (higher.HasMember("Aaaa") && higher.HasMember("Arrr")) {
663 F.Aaaa = cpjson::get_long_double_array(higher["Aaaa"]);
664 F.Arrr = cpjson::get_long_double_array(higher["Arrr"]);
665 F.Naaa = cpjson::get_integer(higher, "Naaa");
666 F.Nrrr = cpjson::get_integer(higher, "Nrrr");
667 }
668
669 } else {
670 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
671 }
672 };
673
674 void parse_ECS_conductivity(rapidjson::Value& conductivity, CoolPropFluid& fluid) {
675 fluid.transport.conductivity_ecs.reference_fluid = cpjson::get_string(conductivity, "reference_fluid");
676
677 // Parameters for correction polynomials
678 fluid.transport.conductivity_ecs.psi_a = cpjson::get_long_double_array(conductivity["psi"]["a"]);
679 fluid.transport.conductivity_ecs.psi_t = cpjson::get_long_double_array(conductivity["psi"]["t"]);
680 fluid.transport.conductivity_ecs.psi_rhomolar_reducing = cpjson::get_double(conductivity["psi"], "rhomolar_reducing");
681 fluid.transport.conductivity_ecs.f_int_a = cpjson::get_long_double_array(conductivity["f_int"]["a"]);
682 fluid.transport.conductivity_ecs.f_int_t = cpjson::get_long_double_array(conductivity["f_int"]["t"]);
683 fluid.transport.conductivity_ecs.f_int_T_reducing = cpjson::get_double(conductivity["f_int"], "T_reducing");
684
686 }
687
688 void parse_ECS_viscosity(rapidjson::Value& viscosity, CoolPropFluid& fluid) {
689 fluid.transport.viscosity_ecs.reference_fluid = cpjson::get_string(viscosity, "reference_fluid");
690
691 // Parameters for correction polynomial
692 fluid.transport.viscosity_ecs.psi_a = cpjson::get_long_double_array(viscosity["psi"]["a"]);
693 fluid.transport.viscosity_ecs.psi_t = cpjson::get_long_double_array(viscosity["psi"]["t"]);
694 fluid.transport.viscosity_ecs.psi_rhomolar_reducing = cpjson::get_double(viscosity["psi"], "rhomolar_reducing");
695
696 fluid.transport.viscosity_using_ECS = true;
697 }
698
699 void parse_Chung_viscosity(rapidjson::Value& viscosity, CoolPropFluid& fluid) {
700 // These in base SI units
701 fluid.transport.viscosity_Chung.rhomolar_critical = cpjson::get_double(viscosity, "rhomolar_critical");
702 fluid.transport.viscosity_Chung.T_critical = cpjson::get_double(viscosity, "T_critical");
703 fluid.transport.viscosity_Chung.molar_mass = cpjson::get_double(viscosity, "molar_mass");
704 fluid.transport.viscosity_Chung.dipole_moment_D = cpjson::get_double(viscosity, "dipole_moment_D");
705 fluid.transport.viscosity_Chung.acentric = cpjson::get_double(viscosity, "acentric");
706 fluid.transport.viscosity_using_Chung = true;
707 }
708
709 void parse_rhosr_viscosity(rapidjson::Value& viscosity, CoolPropFluid& fluid) {
710 fluid.transport.viscosity_rhosr.C = cpjson::get_double(viscosity, "C");
711 fluid.transport.viscosity_rhosr.c_liq = cpjson::get_double_array(viscosity, "c_liq");
712 fluid.transport.viscosity_rhosr.c_vap = cpjson::get_double_array(viscosity, "c_vap");
713 fluid.transport.viscosity_rhosr.rhosr_critical = cpjson::get_double(viscosity, "rhosr_critical");
714 fluid.transport.viscosity_rhosr.x_crossover = cpjson::get_double(viscosity, "x_crossover");
715 fluid.transport.viscosity_using_rhosr = true;
716 }
717
719 void parse_viscosity(rapidjson::Value& viscosity, CoolPropFluid& fluid) {
720 // If an array, use the first one, and then stop;
721 if (viscosity.IsArray()) {
722 rapidjson::Value::ValueIterator itr = viscosity.Begin();
723 parse_viscosity(*itr, fluid);
724 return;
725 }
726
727 // Load the BibTeX key
728 fluid.transport.BibTeX_viscosity = cpjson::get_string(viscosity, "BibTeX");
729
730 // Set the Lennard-Jones 12-6 potential variables, or approximate them from method of Chung
731 if (!viscosity.HasMember("sigma_eta") || !viscosity.HasMember("epsilon_over_k")) {
732 default_transport(fluid);
733 } else {
734 fluid.transport.sigma_eta = cpjson::get_double(viscosity, "sigma_eta");
735 fluid.transport.epsilon_over_k = cpjson::get_double(viscosity, "epsilon_over_k");
736 }
737
738 // If it is using ECS, set ECS parameters and quit
739 if (viscosity.HasMember("type") && !cpjson::get_string(viscosity, "type").compare("ECS")) {
740 parse_ECS_viscosity(viscosity, fluid);
741 return;
742 }
743
744 // If it is using rho*sr CS, set parameters and quit
745 if (viscosity.HasMember("type") && !cpjson::get_string(viscosity, "type").compare("rhosr-CS")) {
746 parse_rhosr_viscosity(viscosity, fluid);
747 return;
748 }
749
750 // Use the method of Chung
751 // If it is using ECS, set ECS parameters and quit
752 if (viscosity.HasMember("type") && !cpjson::get_string(viscosity, "type").compare("Chung")) {
753 parse_Chung_viscosity(viscosity, fluid);
754 return;
755 }
756
757 if (viscosity.HasMember("hardcoded")) {
758 std::string target = cpjson::get_string(viscosity, "hardcoded");
759 if (!target.compare("Water")) {
761 return;
762 } else if (!target.compare("HeavyWater")) {
764 return;
765 } else if (!target.compare("Helium")) {
767 return;
768 } else if (!target.compare("R23")) {
770 return;
771 } else if (!target.compare("Methanol")) {
773 return;
774 } else if (!target.compare("m-Xylene")) {
776 return;
777 } else if (!target.compare("o-Xylene")) {
779 return;
780 } else if (!target.compare("p-Xylene")) {
782 return;
783 } else {
784 throw ValueError(format("hardcoded viscosity [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
785 }
786 }
787
788 // Load dilute viscosity term
789 if (viscosity.HasMember("dilute")) {
790 parse_dilute_viscosity(viscosity["dilute"], fluid);
791 }
792 // Load initial density term
793 if (viscosity.HasMember("initial_density")) {
794 parse_initial_density_viscosity(viscosity["initial_density"], fluid);
795 }
796 // Load higher_order term
797 if (viscosity.HasMember("higher_order")) {
798 parse_higher_order_viscosity(viscosity["higher_order"], fluid);
799 }
800 };
801
803 void parse_dilute_conductivity(rapidjson::Value& dilute, CoolPropFluid& fluid) {
804 if (dilute.HasMember("hardcoded")) {
805 std::string target = cpjson::get_string(dilute, "hardcoded");
806 if (!target.compare("CO2")) {
808 return;
809 }
810 else if (!target.compare("CarbonDioxideHuberJPCRD2016")) {
812 return;
813 } else if (!target.compare("Ethane")) {
815 return;
816 } else if (!target.compare("none")) {
818 return;
819 } else {
820 throw ValueError(
821 format("hardcoded dilute conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
822 }
823 }
824 std::string type = cpjson::get_string(dilute, "type");
825 if (!type.compare("ratio_of_polynomials")) {
826 // Get a reference to the entry in the fluid instance
828
829 // Set the type flag
831
832 // Load up the values
833 data.A = cpjson::get_long_double_array(dilute["A"]);
834 data.B = cpjson::get_long_double_array(dilute["B"]);
835 data.n = cpjson::get_long_double_array(dilute["n"]);
836 data.m = cpjson::get_long_double_array(dilute["m"]);
837 data.T_reducing = cpjson::get_double(dilute, "T_reducing");
838 } else if (!type.compare("eta0_and_poly")) {
839 // Get a reference to the entry in the fluid instance
841
842 // Set the type flag
844
845 // Load up the values
846 data.A = cpjson::get_long_double_array(dilute["A"]);
847 data.t = cpjson::get_long_double_array(dilute["t"]);
848 } else {
849 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
850 }
851 };
852
854 void parse_residual_conductivity(rapidjson::Value& dilute, CoolPropFluid& fluid) {
855 if (dilute.HasMember("hardcoded")) {
856 std::string target = cpjson::get_string(dilute, "hardcoded");
857 if (!target.compare("CO2")) {
859 return;
860 } else {
861 throw ValueError(
862 format("hardcoded residual conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
863 }
864 }
865 std::string type = cpjson::get_string(dilute, "type");
866 if (!type.compare("polynomial")) {
867 // Get a reference to the entry in the fluid instance
869
870 // Set the type flag
872
873 // Load up the values
874 data.B = cpjson::get_long_double_array(dilute["B"]);
875 data.d = cpjson::get_long_double_array(dilute["d"]);
876 data.t = cpjson::get_long_double_array(dilute["t"]);
877 data.T_reducing = cpjson::get_double(dilute, "T_reducing");
878 data.rhomass_reducing = cpjson::get_double(dilute, "rhomass_reducing");
879 } else if (!type.compare("polynomial_and_exponential")) {
880 // Get a reference to the entry in the fluid instance
882
883 // Set the type flag
885
886 // Load up the values
887 data.A = cpjson::get_long_double_array(dilute["A"]);
888 data.d = cpjson::get_long_double_array(dilute["d"]);
889 data.t = cpjson::get_long_double_array(dilute["t"]);
890 data.gamma = cpjson::get_long_double_array(dilute["gamma"]);
891 data.l = cpjson::get_long_double_array(dilute["l"]);
892 } else {
893 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
894 }
895 };
896
897 void parse_critical_conductivity(rapidjson::Value& critical, CoolPropFluid& fluid) {
898 if (critical.HasMember("hardcoded")) {
899 std::string target = cpjson::get_string(critical, "hardcoded");
900 if (!target.compare("R123")) {
902 return;
903 } else if (!target.compare("Ammonia")) {
905 return;
906 } else if (!target.compare("CarbonDioxideScalabrinJPCRD2006")) {
909 return;
910 } else if (!target.compare("None")) {
912 return;
913 } else {
914 throw ValueError(format("critical conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
915 }
916 }
917 std::string type = cpjson::get_string(critical, "type");
918 if (!type.compare("simplified_Olchowy_Sengers")) {
921
922 // Set the type flag
924
925 // Set values if they are found - otherwise fall back to default values
926 if (critical.HasMember("qD")) {
927 data.qD = cpjson::get_double(critical, "qD");
928 }
929 if (critical.HasMember("zeta0")) {
930 data.zeta0 = cpjson::get_double(critical, "zeta0");
931 }
932 if (critical.HasMember("GAMMA")) {
933 data.GAMMA = cpjson::get_double(critical, "GAMMA");
934 }
935 if (critical.HasMember("gamma")) {
936 data.gamma = cpjson::get_double(critical, "gamma");
937 }
938 if (critical.HasMember("R0")) {
939 data.R0 = cpjson::get_double(critical, "R0");
940 }
941 if (critical.HasMember("T_ref")) {
942 data.T_ref = cpjson::get_double(critical, "T_ref");
943 }
944 } else {
945 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
946 }
947 };
948
950 void parse_thermal_conductivity(rapidjson::Value& conductivity, CoolPropFluid& fluid) {
951 // Load the BibTeX key
952 fluid.transport.BibTeX_conductivity = cpjson::get_string(conductivity, "BibTeX");
953
954 // If it is using ECS, set ECS parameters and quit
955 if (conductivity.HasMember("type") && !cpjson::get_string(conductivity, "type").compare("ECS")) {
956 parse_ECS_conductivity(conductivity, fluid);
957 return;
958 }
959
960 if (conductivity.HasMember("hardcoded")) {
961 std::string target = cpjson::get_string(conductivity, "hardcoded");
962 if (!target.compare("Water")) {
964 return;
965 } else if (!target.compare("HeavyWater")) {
967 return;
968 } else if (!target.compare("Methane")) {
970 return;
971 } else if (!target.compare("R23")) {
973 return;
974 } else if (!target.compare("Helium")) {
976 return;
977 } else {
978 throw ValueError(
979 format("hardcoded residual conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
980 }
981 }
982
983 // Load dilute conductivity term
984 if (conductivity.HasMember("dilute")) {
985 parse_dilute_conductivity(conductivity["dilute"], fluid);
986 }
987 // Load residual conductivity term
988 if (conductivity.HasMember("residual")) {
989 parse_residual_conductivity(conductivity["residual"], fluid);
990 }
991 // Load critical conductivity term
992 if (conductivity.HasMember("critical")) {
993 parse_critical_conductivity(conductivity["critical"], fluid);
994 }
995 };
996
998 void parse_transport(rapidjson::Value& transport, CoolPropFluid& fluid) {
999
1000 // Parse viscosity
1001 if (transport.HasMember("viscosity")) {
1002 parse_viscosity(transport["viscosity"], fluid);
1004 }
1005
1006 // Parse thermal conductivity
1007 if (transport.HasMember("conductivity")) {
1008 parse_thermal_conductivity(transport["conductivity"], fluid);
1010 }
1011 };
1012
1014 // Use the method of Chung to approximate the values for epsilon_over_k and sigma_eta
1015 // Chung, T.-H.; Ajlan, M.; Lee, L. L.; Starling, K. E. Generalized Multiparameter Correlation for Nonpolar and Polar Fluid Transport Properties. Ind. Eng. Chem. Res. 1988, 27, 671-679.
1016 // rhoc needs to be in mol/L to yield a sigma in nm,
1017 CoolPropDbl rho_crit_molar = fluid.EOS().reduce.rhomolar / 1000.0; // [mol/m3 to mol/L]
1018 CoolPropDbl Tc = fluid.EOS().reduce.T;
1019 fluid.transport.sigma_eta = 0.809 / pow(rho_crit_molar, static_cast<CoolPropDbl>(1.0 / 3.0)) / 1e9; // 1e9 is to convert from nm to m
1020 fluid.transport.epsilon_over_k = Tc / 1.2593; // [K]
1021 }
1022
1023 void parse_melting_line(rapidjson::Value& melting_line, CoolPropFluid& fluid) {
1024 fluid.ancillaries.melting_line.T_m = cpjson::get_double(melting_line, "T_m");
1025 fluid.ancillaries.melting_line.BibTeX = cpjson::get_string(melting_line, "BibTeX");
1026
1027 if (melting_line.HasMember("type")) {
1028 std::string type = cpjson::get_string(melting_line, "type");
1029 if (!type.compare("Simon")) {
1030 rapidjson::Value& parts = melting_line["parts"];
1032 for (rapidjson::Value::ValueIterator itr = parts.Begin(); itr != parts.End(); ++itr) {
1034 data.a = cpjson::get_double((*itr), "a");
1035 data.c = cpjson::get_double((*itr), "c");
1036 data.T_min = cpjson::get_double((*itr), "T_min");
1037 data.T_max = cpjson::get_double((*itr), "T_max");
1038 data.T_0 = cpjson::get_double((*itr), "T_0");
1039 data.p_0 = cpjson::get_double((*itr), "p_0");
1040 fluid.ancillaries.melting_line.simon.parts.push_back(data);
1041 }
1042 } else if (!type.compare("polynomial_in_Tr")) {
1043 rapidjson::Value& parts = melting_line["parts"];
1045 for (rapidjson::Value::ValueIterator itr = parts.Begin(); itr != parts.End(); ++itr) {
1047 data.a = cpjson::get_long_double_array((*itr), "a");
1048 data.t = cpjson::get_long_double_array((*itr), "t");
1049 data.T_min = cpjson::get_double((*itr), "T_min");
1050 data.T_max = cpjson::get_double((*itr), "T_max");
1051 data.T_0 = cpjson::get_double((*itr), "T_0");
1052 data.p_0 = cpjson::get_double((*itr), "p_0");
1053 fluid.ancillaries.melting_line.polynomial_in_Tr.parts.push_back(data);
1054 }
1055 } else if (!type.compare("polynomial_in_Theta")) {
1056 rapidjson::Value& parts = melting_line["parts"];
1058 for (rapidjson::Value::ValueIterator itr = parts.Begin(); itr != parts.End(); ++itr) {
1060 data.a = cpjson::get_long_double_array((*itr), "a");
1061 data.t = cpjson::get_long_double_array((*itr), "t");
1062 data.T_min = cpjson::get_double((*itr), "T_min");
1063 data.T_max = cpjson::get_double((*itr), "T_max");
1064 data.T_0 = cpjson::get_double((*itr), "T_0");
1065 data.p_0 = cpjson::get_double((*itr), "p_0");
1066 fluid.ancillaries.melting_line.polynomial_in_Theta.parts.push_back(data);
1067 }
1068 } else {
1069 throw ValueError(format("melting line type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
1070 }
1071 // Set the limits for the melting line curve
1073 } else {
1074 throw ValueError(format("melting line does not have \"type\" for fluid %s", fluid.name.c_str()));
1075 }
1076 };
1077
1079 void parse_states(rapidjson::Value& states, CoolPropFluid& fluid) {
1080 if (!states.HasMember("critical")) {
1081 throw ValueError(format("fluid[\"STATES\"] [%s] does not have \"critical\" member", fluid.name.c_str()));
1082 }
1083 rapidjson::Value& crit = states["critical"];
1084 fluid.crit.T = cpjson::get_double(crit, "T");
1085 fluid.crit.p = cpjson::get_double(crit, "p");
1086 fluid.crit.rhomolar = cpjson::get_double(crit, "rhomolar");
1087 fluid.crit.hmolar = cpjson::get_double(crit, "hmolar");
1088 fluid.crit.smolar = cpjson::get_double(crit, "smolar");
1089
1090 if (!states.HasMember("triple_liquid")) {
1091 throw ValueError(format("fluid[\"STATES\"] [%s] does not have \"triple_liquid\" member", fluid.name.c_str()));
1092 }
1093 rapidjson::Value& triple_liquid = states["triple_liquid"];
1094 if (triple_liquid.ObjectEmpty()) {
1095 // State is empty - probably because the triple point temperature is below the minimum saturation temperature
1096 fluid.triple_liquid.T = -1;
1097 fluid.triple_liquid.p = -1;
1098 fluid.triple_liquid.rhomolar = -1;
1099 fluid.triple_liquid.hmolar = _HUGE;
1100 fluid.triple_liquid.smolar = _HUGE;
1101 } else {
1102 fluid.triple_liquid.T = cpjson::get_double(triple_liquid, "T");
1103 fluid.triple_liquid.p = cpjson::get_double(triple_liquid, "p");
1104 fluid.triple_liquid.rhomolar = cpjson::get_double(triple_liquid, "rhomolar");
1105 fluid.triple_liquid.hmolar = cpjson::get_double(triple_liquid, "hmolar");
1106 fluid.triple_liquid.smolar = cpjson::get_double(triple_liquid, "smolar");
1107 }
1108
1109 if (!states.HasMember("triple_vapor")) {
1110 throw ValueError(format("fluid[\"STATES\"] [%s] does not have \"triple_vapor\" member", fluid.name.c_str()));
1111 }
1112 rapidjson::Value& triple_vapor = states["triple_vapor"];
1113 if (triple_vapor.ObjectEmpty()) {
1114 // State is empty - probably because the triple point temperature is below the minimum saturation temperature
1115 fluid.triple_vapor.T = -1;
1116 fluid.triple_vapor.p = -1;
1117 fluid.triple_vapor.rhomolar = -1;
1118 fluid.triple_vapor.hmolar = _HUGE;
1119 fluid.triple_vapor.smolar = _HUGE;
1120 } else {
1121 fluid.triple_vapor.T = cpjson::get_double(triple_vapor, "T");
1122 fluid.triple_vapor.p = cpjson::get_double(triple_vapor, "p");
1123 fluid.triple_vapor.rhomolar = cpjson::get_double(triple_vapor, "rhomolar");
1124 fluid.triple_vapor.hmolar = cpjson::get_double(triple_vapor, "hmolar");
1125 fluid.triple_vapor.smolar = cpjson::get_double(triple_vapor, "smolar");
1126 }
1127 };
1128
1130 void parse_ancillaries(rapidjson::Value& ancillaries, CoolPropFluid& fluid) {
1131 if (!ancillaries.HasMember("rhoL") || !ancillaries.HasMember("rhoV")) {
1132 throw ValueError("Ancillary curves for either rhoL or rhoV are missing");
1133 }
1134 fluid.ancillaries.rhoL = SaturationAncillaryFunction(ancillaries["rhoL"]);
1135 fluid.ancillaries.rhoV = SaturationAncillaryFunction(ancillaries["rhoV"]);
1136
1137 // If a pseudo-pure fluid, has pL and pV curves
1138 if (ancillaries.HasMember("pL") && ancillaries.HasMember("pV")) {
1139 fluid.ancillaries.pL = SaturationAncillaryFunction(ancillaries["pL"]);
1140 fluid.ancillaries.pV = SaturationAncillaryFunction(ancillaries["pV"]);
1141 }
1142 // Otherwise has a single pS curve and not pL and not pV
1143 else if (!ancillaries.HasMember("pL") && !ancillaries.HasMember("pV") && ancillaries.HasMember("pS")) {
1144 fluid.ancillaries.pL = SaturationAncillaryFunction(ancillaries["pS"]);
1145 fluid.ancillaries.pV = SaturationAncillaryFunction(ancillaries["pS"]);
1146 } else {
1147 throw ValueError("Pressure ancillary curves are missing or invalid");
1148 }
1149
1150 if (ancillaries.HasMember("hL")) {
1151 fluid.ancillaries.hL = SaturationAncillaryFunction(ancillaries["hL"]);
1152 } else {
1153 if (get_debug_level() > 0) {
1154 std::cout << "Missing hL ancillary for fluid " << fluid.name;
1155 }
1156 }
1157 if (ancillaries.HasMember("hLV")) {
1158 fluid.ancillaries.hLV = SaturationAncillaryFunction(ancillaries["hLV"]);
1159 } else {
1160 if (get_debug_level() > 0) {
1161 std::cout << "Missing hLV ancillary for fluid " << fluid.name;
1162 }
1163 }
1164
1165 if (ancillaries.HasMember("sL")) {
1166 fluid.ancillaries.sL = SaturationAncillaryFunction(ancillaries["sL"]);
1167 } else {
1168 if (get_debug_level() > 0) {
1169 std::cout << "Missing sL ancillary for fluid " << fluid.name;
1170 }
1171 }
1172 if (ancillaries.HasMember("sLV")) {
1173 fluid.ancillaries.sLV = SaturationAncillaryFunction(ancillaries["sLV"]);
1174 } else {
1175 if (get_debug_level() > 0) {
1176 std::cout << "Missing sLV ancillary for fluid " << fluid.name;
1177 }
1178 }
1179 if (!ValidNumber(fluid.ancillaries.sL.get_Tmin()) && get_debug_level() > 0) {
1180 std::cout << "Tmin invalid for sL for " << fluid.name << std::endl;
1181 }
1182 };
1183
1185 void parse_surface_tension(rapidjson::Value& surface_tension, CoolPropFluid& fluid) {
1186 fluid.ancillaries.surface_tension = SurfaceTensionCorrelation(surface_tension);
1187 };
1188
1191 assert(fluid.EOSVector.size() > 0);
1192 assert(fluid.CAS.length() > 0);
1193 assert(fluid.name.length() > 0);
1194 }
1195
1196 public:
1197 // Default constructor;
1199 _is_empty = true;
1200 };
1201 bool is_empty(void) {
1202 return _is_empty;
1203 };
1204
1206 static void add_many(const std::string& JSON_string);
1207
1209 void add_many(rapidjson::Value& listing);
1210
1211 void add_one(rapidjson::Value& fluid_json);
1212
1213 std::string get_JSONstring(const std::string& key) {
1214 // Try to find it
1215 std::map<std::string, std::size_t>::const_iterator it = string_to_index_map.find(key);
1216 if (it != string_to_index_map.end()) {
1217
1218 std::map<std::size_t, std::string>::const_iterator it2 = JSONstring_map.find(it->second);
1219 if (it2 != JSONstring_map.end()) {
1220 // Then, load the fluids we would like to add
1221 rapidjson::Document doc;
1222 cpjson::JSON_string_to_rapidjson(it2->second, doc);
1223 rapidjson::Document doc2;
1224 doc2.SetArray();
1225 doc2.PushBack(doc, doc.GetAllocator());
1226 return cpjson::json2string(doc2);
1227 } else {
1228 throw ValueError(format("Unable to obtain JSON string for this identifier [%d]", it->second));
1229 }
1230 } else {
1231 throw ValueError(format("Unable to obtain index for this identifier [%s]", key.c_str()));
1232 }
1233 }
1234
1236
1239 CoolPropFluid get(const std::string& key) {
1240 // Try to find it
1241 std::map<std::string, std::size_t>::const_iterator it = string_to_index_map.find(key);
1242 // If it is found
1243 if (it != string_to_index_map.end()) {
1244 return get(it->second);
1245 } else {
1246 // Here we check for the use of a cubic Helmholtz energy transformation for a multi-fluid model
1247 std::vector<std::string> endings;
1248 endings.push_back("-SRK");
1249 endings.push_back("-PengRobinson");
1250 for (std::vector<std::string>::const_iterator end = endings.begin(); end != endings.end(); ++end) {
1251 if (endswith(key, *end)) {
1252 std::string used_name = key.substr(0, key.size() - (*end).size());
1253 it = string_to_index_map.find(used_name);
1254 if (it != string_to_index_map.end()) {
1255 // We found the name of the fluid within the library of multiparameter
1256 // Helmholtz-explicit models. We will load its parameters from the
1257 // multiparameter EOS
1258 //
1259 CoolPropFluid fluid = get(it->second);
1260 // Remove all the residual contributions to the Helmholtz energy
1261 fluid.EOSVector[0].alphar.empty_the_EOS();
1262 // Get the parameters for the cubic EOS
1263 CoolPropDbl Tc = fluid.EOSVector[0].reduce.T;
1264 CoolPropDbl pc = fluid.EOSVector[0].reduce.p;
1265 CoolPropDbl rhomolarc = fluid.EOSVector[0].reduce.rhomolar;
1266 CoolPropDbl acentric = fluid.EOSVector[0].acentric;
1267 CoolPropDbl R = 8.3144598; // fluid.EOSVector[0].R_u;
1268 // Set the cubic contribution to the residual Helmholtz energy
1269 shared_ptr<AbstractCubic> ac;
1270 if (*end == "-SRK") {
1271 ac.reset(new SRK(Tc, pc, acentric, R));
1272 } else if (*end == "-PengRobinson") {
1273 ac.reset(new PengRobinson(Tc, pc, acentric, R));
1274 } else {
1275 throw CoolProp::ValueError(format("Unable to match this ending [%s]", (*end).c_str()));
1276 }
1277 ac->set_Tr(Tc);
1278 ac->set_rhor(rhomolarc);
1279 fluid.EOSVector[0].alphar.cubic = ResidualHelmholtzGeneralizedCubic(ac);
1280 return fluid;
1281 } else {
1282 // Let's look in the library of cubic EOS
1284 // Set the cubic contribution to the residual Helmholtz energy
1285 shared_ptr<AbstractCubic> ac;
1286 if (*end == "-SRK") {
1287 ac.reset(new SRK(vals.Tc, vals.pc, vals.acentric, get_config_double(R_U_CODATA)));
1288 } else if (*end == "-PengRobinson") {
1289 ac.reset(new PengRobinson(vals.Tc, vals.pc, vals.acentric, get_config_double(R_U_CODATA)));
1290 } else {
1291 throw CoolProp::ValueError(format("Unable to match this ending [%s]", (*end).c_str()));
1292 }
1293 ac->set_Tr(vals.Tc);
1294 if (vals.rhomolarc > 0) {
1295 ac->set_rhor(vals.rhomolarc);
1296 } else {
1297 // Curve fit from all the pure fluids in CoolProp (thanks to recommendation of A. Kazakov)
1298 double v_c_Lmol = 2.14107171795 * (vals.Tc / vals.pc * 1000) + 0.00773144012514; // [L/mol]
1299 ac->set_rhor(1 / (v_c_Lmol / 1000.0));
1300 }
1301 if (vals.alpha_type == "Twu") {
1302 std::vector<double>& c = vals.alpha_coeffs;
1303 ac->set_C_Twu(0, c[0], c[1], c[2]);
1304 }
1305 CoolPropFluid fluid;
1306 fluid.CAS = vals.CAS;
1308 E.acentric = vals.acentric;
1309 E.sat_min_liquid.T = _HUGE;
1310 E.sat_min_liquid.p = _HUGE;
1311 E.reduce.T = vals.Tc;
1312 E.reduce.p = vals.pc;
1313 E.reduce.rhomolar = ac->get_rhor();
1314 fluid.EOSVector.push_back(E);
1316 fluid.EOS().alpha0 = vals.alpha0;
1317 fluid.crit.T = vals.Tc;
1318 fluid.crit.p = vals.pc;
1319 fluid.crit.rhomolar = ac->get_rhor();
1320
1321 return fluid;
1322 }
1323 }
1324 }
1325 throw ValueError(format("key [%s] was not found in string_to_index_map in JSONFluidLibrary", key.c_str()));
1326 }
1327 };
1328
1330
1333 CoolPropFluid get(std::size_t key) {
1334 // Try to find it
1335 std::map<std::size_t, CoolPropFluid>::iterator it = fluid_map.find(key);
1336 // If it is found
1337 if (it != fluid_map.end()) {
1338 return it->second;
1339 } else {
1340 throw ValueError(format("key [%d] was not found in JSONFluidLibrary", key));
1341 }
1342 };
1343 void set_fluid_enthalpy_entropy_offset(const std::string& fluid, double delta_a1, double delta_a2, const std::string& ref);
1345 std::string get_fluid_list(void) {
1346 return strjoin(name_vector, get_config_string(LIST_STRING_DELIMITER));
1347 };
1348};
1349
1351JSONFluidLibrary& get_library(void);
1352
1354std::string get_fluid_list(void);
1355
1357CoolPropFluid get_fluid(const std::string& fluid_string);
1358
1360std::string get_fluid_as_JSONstring(const std::string& indentifier);
1361
1363void set_fluid_enthalpy_entropy_offset(const std::string& fluid, double delta_a1, double delta_a2, const std::string& ref);
1364
1365} /* namespace CoolProp */
1366#endif