CoolProp 8.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
6
9#include <memory>
10using std::shared_ptr;
11
12#include <map>
13#include <algorithm>
18
19namespace CoolProp {
20
21// Forward declaration of the necessary debug function to avoid including the whole header
22extern int get_debug_level();
23
25
31{
33 std::map<std::size_t, CoolPropFluid> fluid_map;
35 std::map<std::size_t, std::string> JSONstring_map;
36 std::vector<std::string> name_vector;
37 std::map<std::string, std::size_t> string_to_index_map;
38 bool _is_empty;
39
40 public:
42 static ResidualHelmholtzContainer parse_alphar(const nlohmann::json& jsonalphar) {
44
45 for (const auto& contribution : jsonalphar) {
46 // Get the type (required!)
47 std::string type = cpjson::get_string(contribution, "type");
48
49 if (!type.compare("ResidualHelmholtzPower")) {
50 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
51 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
52 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
53 std::vector<CoolPropDbl> l = cpjson::get_long_double_array(contribution.at("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.at("n"));
61 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
62 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
63 std::vector<CoolPropDbl> eta = cpjson::get_long_double_array(contribution.at("eta"));
64 std::vector<CoolPropDbl> epsilon = cpjson::get_long_double_array(contribution.at("epsilon"));
65 std::vector<CoolPropDbl> beta = cpjson::get_long_double_array(contribution.at("beta"));
66 std::vector<CoolPropDbl> gamma = cpjson::get_long_double_array(contribution.at("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.at("n"));
76 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
77 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
78 std::vector<CoolPropDbl> eta = cpjson::get_long_double_array(contribution.at("eta"));
79 std::vector<CoolPropDbl> beta = cpjson::get_long_double_array(contribution.at("beta"));
80 std::vector<CoolPropDbl> gamma = cpjson::get_long_double_array(contribution.at("gamma"));
81 std::vector<CoolPropDbl> epsilon = cpjson::get_long_double_array(contribution.at("epsilon"));
82 std::vector<CoolPropDbl> b = cpjson::get_long_double_array(contribution.at("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.at("n"));
96 std::vector<CoolPropDbl> a = cpjson::get_long_double_array(contribution.at("a"));
97 std::vector<CoolPropDbl> b = cpjson::get_long_double_array(contribution.at("b"));
98 std::vector<CoolPropDbl> beta = cpjson::get_long_double_array(contribution.at("beta"));
99 std::vector<CoolPropDbl> A = cpjson::get_long_double_array(contribution.at("A"));
100 std::vector<CoolPropDbl> B = cpjson::get_long_double_array(contribution.at("B"));
101 std::vector<CoolPropDbl> C = cpjson::get_long_double_array(contribution.at("C"));
102 std::vector<CoolPropDbl> D = cpjson::get_long_double_array(contribution.at("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.at("n"));
113 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
114 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
115 std::vector<CoolPropDbl> l = cpjson::get_long_double_array(contribution.at("l"));
116 std::vector<CoolPropDbl> m = cpjson::get_long_double_array(contribution.at("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.at("n"));
124 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
125 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
126 std::vector<CoolPropDbl> gt = cpjson::get_long_double_array(contribution.at("gt"));
127 std::vector<CoolPropDbl> lt = cpjson::get_long_double_array(contribution.at("lt"));
128 std::vector<CoolPropDbl> gd = cpjson::get_long_double_array(contribution.at("gd"));
129 std::vector<CoolPropDbl> ld = cpjson::get_long_double_array(contribution.at("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.at("n"));
140 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
141 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
142 std::vector<CoolPropDbl> g = cpjson::get_long_double_array(contribution.at("g"));
143 std::vector<CoolPropDbl> l = cpjson::get_long_double_array(contribution.at("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(const nlohmann::json& jsonalpha0) {
172 if (!jsonalpha0.is_array()) {
173 throw ValueError();
174 }
175
177
178 for (const auto& contribution : jsonalpha0) {
179 // Get the type (required!)
180 std::string type = cpjson::get_string(contribution, "type");
181
182 if (!type.compare("IdealGasHelmholtzLead")) {
183 if (alpha0.Lead.is_enabled() == true) {
184 throw ValueError("Cannot add ");
185 }
186 CoolPropDbl a1 = cpjson::get_double(contribution, "a1");
187 CoolPropDbl a2 = cpjson::get_double(contribution, "a2");
188
189 alpha0.Lead = IdealHelmholtzLead(a1, a2);
190 } else if (!type.compare("IdealGasHelmholtzPower")) {
191 if (alpha0.Power.is_enabled() == true) {
192 throw ValueError("Cannot add ");
193 }
194 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
195 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
196
197 alpha0.Power = IdealHelmholtzPower(n, t);
198 } else if (!type.compare("IdealGasHelmholtzLogTau")) {
199 if (alpha0.LogTau.is_enabled() == true) {
200 throw ValueError("Cannot add ");
201 }
202 CoolPropDbl a = cpjson::get_double(contribution, "a");
203
204 alpha0.LogTau = IdealHelmholtzLogTau(a);
205 } else if (!type.compare("IdealGasHelmholtzPlanckEinsteinGeneralized")) {
206 // Retrieve the values
207 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
208 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
209
210 std::vector<CoolPropDbl> c = cpjson::get_long_double_array(contribution.at("c"));
211 std::vector<CoolPropDbl> d = cpjson::get_long_double_array(contribution.at("d"));
212
213 if (alpha0.PlanckEinstein.is_enabled() == true) {
214 alpha0.PlanckEinstein.extend(n, t, c, d);
215 } else {
217 }
218 } else if (!type.compare("IdealGasHelmholtzPlanckEinstein")) {
219 // Retrieve the values
220 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
221 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
222 // Flip the sign of theta
223 for (auto& i : t) {
224 i *= -1;
225 }
226 std::vector<CoolPropDbl> c(n.size(), 1);
227 std::vector<CoolPropDbl> d(c.size(), -1);
228
229 if (alpha0.PlanckEinstein.is_enabled() == true) {
230 alpha0.PlanckEinstein.extend(n, t, c, d);
231 } else {
233 }
234 } else if (!type.compare("IdealGasHelmholtzPlanckEinsteinFunctionT")) {
235 // Retrieve the values
236 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
237 std::vector<CoolPropDbl> v = cpjson::get_long_double_array(contribution.at("v")), theta(n.size(), 0.0);
238 // Calculate theta
239 double Tc = cpjson::get_double(contribution, "Tcrit");
240 for (std::size_t i = 0; i < v.size(); ++i) {
241 theta[i] = -v[i] / Tc;
242 }
243 std::vector<CoolPropDbl> c(n.size(), 1);
244 std::vector<CoolPropDbl> d(c.size(), -1);
245
246 if (alpha0.PlanckEinstein.is_enabled() == true) {
247 alpha0.PlanckEinstein.extend(n, theta, c, d);
248 } else {
250 }
251 } else if (!type.compare("IdealGasHelmholtzGERG2004Cosh")) {
252 // Retrieve the values
253 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
254 std::vector<CoolPropDbl> theta = cpjson::get_long_double_array(contribution.at("theta"));
255 double Tc = cpjson::get_double(contribution, "Tcrit");
256 if (alpha0.GERG2004Cosh.is_enabled() == true) {
257 alpha0.GERG2004Cosh.extend(n, theta);
258 } else {
259 alpha0.GERG2004Cosh = IdealHelmholtzGERG2004Cosh(n, theta, Tc);
260 }
261 } else if (!type.compare("IdealGasHelmholtzGERG2004Sinh")) {
262 // Retrieve the values
263 std::vector<CoolPropDbl> n = cpjson::get_long_double_array(contribution.at("n"));
264 std::vector<CoolPropDbl> theta = cpjson::get_long_double_array(contribution.at("theta"));
265 double Tc = cpjson::get_double(contribution, "Tcrit");
266 if (alpha0.GERG2004Sinh.is_enabled() == true) {
267 alpha0.GERG2004Sinh.extend(n, theta);
268 } else {
269 alpha0.GERG2004Sinh = IdealHelmholtzGERG2004Sinh(n, theta, Tc);
270 }
271 } else if (!type.compare("IdealGasHelmholtzCP0Constant")) {
272 if (alpha0.CP0Constant.is_enabled() == true) {
273 throw ValueError("Cannot add another IdealGasHelmholtzCP0Constant term; join them together");
274 }
275 CoolPropDbl cp_over_R = cpjson::get_double(contribution, "cp_over_R");
276 CoolPropDbl Tc = cpjson::get_double(contribution, "Tc");
277 CoolPropDbl T0 = cpjson::get_double(contribution, "T0");
278 alpha0.CP0Constant = IdealHelmholtzCP0Constant(cp_over_R, Tc, T0);
279 } else if (!type.compare("IdealGasHelmholtzCP0PolyT")) {
280 if (alpha0.CP0PolyT.is_enabled() == true) {
281 throw ValueError("Cannot add another CP0PolyT term; join them together");
282 }
283 std::vector<CoolPropDbl> c = cpjson::get_long_double_array(contribution.at("c"));
284 std::vector<CoolPropDbl> t = cpjson::get_long_double_array(contribution.at("t"));
285 CoolPropDbl Tc = cpjson::get_double(contribution, "Tc");
286 CoolPropDbl T0 = cpjson::get_double(contribution, "T0");
287 alpha0.CP0PolyT = IdealHelmholtzCP0PolyT(c, t, Tc, T0);
288 } else if (!type.compare("IdealGasHelmholtzCP0AlyLee")) {
289
290 std::vector<CoolPropDbl> constants = cpjson::get_long_double_array(contribution.at("c"));
291 CoolPropDbl Tc = cpjson::get_double(contribution, "Tc");
292 CoolPropDbl T0 = cpjson::get_double(contribution, "T0");
293
294 // Take the constant term if nonzero and set it as a polyT term
295 if (std::abs(constants[0]) > 1e-14) {
296 std::vector<CoolPropDbl> c(1, constants[0]), t(1, 0);
297 if (alpha0.CP0PolyT.is_enabled() == true) {
298 alpha0.CP0PolyT.extend(c, t);
299 } else {
300 alpha0.CP0PolyT = IdealHelmholtzCP0PolyT(c, t, Tc, T0);
301 }
302 }
303 std::vector<CoolPropDbl> n, c, d, t;
304 if (std::abs(constants[1]) > 1e-14) {
305 // sinh term can be converted by setting a_k = C, b_k = 2*D, c_k = -1, d_k = 1
306 n.push_back(constants[1]);
307 t.push_back(-2 * constants[2] / Tc);
308 c.push_back(1);
309 d.push_back(-1);
310 }
311 if (std::abs(constants[3]) > 1e-14) {
312 // cosh term can be converted by setting a_k = C, b_k = 2*D, c_k = 1, d_k = 1
313 n.push_back(-constants[3]);
314 t.push_back(-2 * constants[4] / Tc);
315 c.push_back(1);
316 d.push_back(1);
317 }
318 if (alpha0.PlanckEinstein.is_enabled() == true) {
319 alpha0.PlanckEinstein.extend(n, t, c, d);
320 } else {
322 }
323 } else if (!type.compare("IdealGasHelmholtzEnthalpyEntropyOffset")) {
324 CoolPropDbl a1 = cpjson::get_double(contribution, "a1");
325 CoolPropDbl a2 = cpjson::get_double(contribution, "a2");
326 std::string reference = cpjson::get_string(contribution, "reference");
327 alpha0.EnthalpyEntropyOffsetCore.set(a1, a2, reference);
328 } else {
329 std::cout << format("Unsupported ideal-gas Helmholtz type: %s\n", type.c_str());
330 //throw ValueError(format("Unsupported ideal-gas Helmholtz type: %s",type.c_str()));
331 }
332 }
333 return alpha0;
334 };
335
336 protected:
338 void parse_environmental(const nlohmann::json& json, CoolPropFluid& fluid) {
339 fluid.environment.ASHRAE34 = cpjson::get_string(json, "ASHRAE34");
340 fluid.environment.GWP20 = cpjson::get_double(json, "GWP20");
341 fluid.environment.GWP100 = cpjson::get_double(json, "GWP100");
342 fluid.environment.GWP500 = cpjson::get_double(json, "GWP500");
343 fluid.environment.HH = cpjson::get_double(json, "HH");
344 fluid.environment.FH = cpjson::get_double(json, "FH");
345 fluid.environment.PH = cpjson::get_double(json, "PH");
346 fluid.environment.ODP = cpjson::get_double(json, "ODP");
347 }
348
350 void parse_EOS(const nlohmann::json& EOS_json, CoolPropFluid& fluid) {
352 fluid.EOSVector.push_back(E);
353
354 EquationOfState& EOS = fluid.EOSVector.at(fluid.EOSVector.size() - 1);
355
356 // Universal gas constant [J/mol/K]
357 EOS.R_u = cpjson::get_double(EOS_json, "gas_constant");
358 EOS.molar_mass = cpjson::get_double(EOS_json, "molar_mass");
359 EOS.acentric = cpjson::get_double(EOS_json, "acentric");
360
361 EOS.pseudo_pure = cpjson::get_bool(EOS_json, "pseudo_pure");
362 EOS.limits.Tmax = cpjson::get_double(EOS_json, "T_max");
363 EOS.limits.pmax = cpjson::get_double(EOS_json, "p_max");
364
365 const nlohmann::json& reducing_state = EOS_json.at("STATES").at("reducing");
366 const nlohmann::json& satminL_state = EOS_json.at("STATES").at("sat_min_liquid");
367 const nlohmann::json& satminV_state = EOS_json.at("STATES").at("sat_min_vapor");
368
369 // Reducing state
370 EOS.reduce.T = cpjson::get_double(reducing_state, "T");
371 EOS.reduce.rhomolar = cpjson::get_double(reducing_state, "rhomolar");
372 EOS.reduce.p = cpjson::get_double(reducing_state, "p");
373 EOS.reduce.hmolar = cpjson::get_double(reducing_state, "hmolar");
374 EOS.reduce.smolar = cpjson::get_double(reducing_state, "smolar");
375
376 EOS.sat_min_liquid.T = cpjson::get_double(satminL_state, "T");
377 EOS.sat_min_liquid.p = cpjson::get_double(satminL_state, "p");
378 EOS.sat_min_liquid.rhomolar = cpjson::get_double(satminL_state, "rhomolar");
379 EOS.sat_min_vapor.T = cpjson::get_double(satminV_state, "T");
380 EOS.sat_min_vapor.p = cpjson::get_double(satminV_state, "p");
381 EOS.sat_min_vapor.rhomolar = cpjson::get_double(satminV_state, "rhomolar");
382
384 EOS.limits.Tmin = cpjson::get_double(satminL_state, "T");
385 EOS.ptriple = cpjson::get_double(satminL_state, "p");
386 EOS.Ttriple = EOS.limits.Tmin;
387
388 // BibTex keys
389 EOS.BibTeX_EOS = cpjson::get_string(EOS_json, "BibTeX_EOS");
390 EOS.BibTeX_CP0 = cpjson::get_string(EOS_json, "BibTeX_CP0");
391
392 if (EOS_json.contains("SUPERANCILLARY")) {
393 if (getenv("COOLPROP_DISABLE_SUPERANCILLARIES_ENTIRELY")) {
394 } else {
395 EOS.set_superancillaries_str(EOS_json.at("SUPERANCILLARY").dump());
396 }
397 }
398
399 EOS.alphar = parse_alphar(EOS_json.at("alphar"));
400 EOS.alpha0 = parse_alpha0(EOS_json.at("alpha0"));
401
402 // Store the prefactor multipliying alpha0 if present
403 if (EOS_json.contains("alpha0_prefactor")) {
404 EOS.alpha0.set_prefactor(cpjson::get_double(EOS_json, "alpha0_prefactor"));
405 }
406 if (EOS_json.at("STATES").contains("hs_anchor")) {
407 const nlohmann::json& hs_anchor = EOS_json.at("STATES").at("hs_anchor");
408 EOS.hs_anchor.T = cpjson::get_double(hs_anchor, "T");
409 EOS.hs_anchor.p = cpjson::get_double(hs_anchor, "p");
410 EOS.hs_anchor.rhomolar = cpjson::get_double(hs_anchor, "rhomolar");
411 EOS.hs_anchor.hmolar = cpjson::get_double(hs_anchor, "hmolar");
412 EOS.hs_anchor.smolar = cpjson::get_double(hs_anchor, "smolar");
413 }
414
415 if (EOS_json.at("STATES").contains("pressure_max_sat")) {
416 const nlohmann::json& s = EOS_json.at("STATES").at("pressure_max_sat");
417 EOS.max_sat_p.T = cpjson::get_double(s, "T");
418 EOS.max_sat_p.p = cpjson::get_double(s, "p");
419 EOS.max_sat_p.rhomolar = cpjson::get_double(s, "rhomolar");
420 if (s.contains("hmolar")) {
421 EOS.max_sat_p.hmolar = cpjson::get_double(s, "hmolar");
422 EOS.max_sat_p.smolar = cpjson::get_double(s, "smolar");
423 }
424 }
425
426 if (EOS_json.at("STATES").contains("temperature_max_sat")) {
427 const nlohmann::json& s = EOS_json.at("STATES").at("temperature_max_sat");
428 EOS.max_sat_T.T = cpjson::get_double(s, "T");
429 EOS.max_sat_T.p = cpjson::get_double(s, "p");
430 EOS.max_sat_T.rhomolar = cpjson::get_double(s, "rhomolar");
431 if (s.contains("hmolar")) {
432 EOS.max_sat_T.hmolar = cpjson::get_double(s, "hmolar");
433 EOS.max_sat_T.smolar = cpjson::get_double(s, "smolar");
434 }
435 }
436
437 if (EOS_json.contains("critical_region_splines")) {
438 const nlohmann::json& spline = EOS_json.at("critical_region_splines");
439 EOS.critical_region_splines.T_min = cpjson::get_double(spline, "T_min");
440 EOS.critical_region_splines.T_max = cpjson::get_double(spline, "T_max");
441 EOS.critical_region_splines.rhomolar_min = cpjson::get_double(spline, "rhomolar_min");
442 EOS.critical_region_splines.rhomolar_max = cpjson::get_double(spline, "rhomolar_max");
446 }
447
448 // Validate the equation of state that was just created
449 EOS.validate();
450 }
451
453 void parse_EOS_listing(const nlohmann::json& EOS_array, CoolPropFluid& fluid) {
454 for (const auto& EOS_json : EOS_array) {
455 parse_EOS(EOS_json, fluid);
456 }
457 };
458
460 void parse_dilute_viscosity(const nlohmann::json& dilute, CoolPropFluid& fluid) {
461 if (dilute.contains("hardcoded")) {
462 std::string target = cpjson::get_string(dilute, "hardcoded");
463 if (!target.compare("Ethane")) {
465 return;
466 } else if (!target.compare("Cyclohexane")) {
468 return;
469 } else if (!target.compare("CarbonDioxideLaeseckeJPCRD2017")) {
471 return;
472 } else {
473 throw ValueError(format("hardcoded dilute viscosity [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
474 }
475 }
476 std::string type = cpjson::get_string(dilute, "type");
477 if (!type.compare("collision_integral")) {
478 // Get a reference to the entry in the fluid instance
480
481 // Set the type flag
483
484 // Load up the values
485 CI.a = cpjson::get_long_double_array(dilute.at("a"));
486 CI.t = cpjson::get_long_double_array(dilute.at("t"));
487 CI.molar_mass = cpjson::get_double(dilute, "molar_mass");
488 CI.C = cpjson::get_double(dilute, "C");
489 } else if (!type.compare("kinetic_theory")) {
491 } else if (!type.compare("powers_of_T")) {
492 // Get a reference to the entry in the fluid instance
494
495 // Load up the values
496 CI.a = cpjson::get_long_double_array(dilute.at("a"));
497 CI.t = cpjson::get_long_double_array(dilute.at("t"));
498
500 } else if (!type.compare("powers_of_Tr")) {
501 // Get a reference to the entry in the fluid instance
503 // Load up the values
504 CI.a = cpjson::get_long_double_array(dilute.at("a"));
505 CI.t = cpjson::get_long_double_array(dilute.at("t"));
506 CI.T_reducing = cpjson::get_double(dilute, "T_reducing");
508 } else if (!type.compare("collision_integral_powers_of_Tstar")) {
509 // Get a reference to the entry in the fluid instance
511
512 // Load up the values
513 CI.a = cpjson::get_long_double_array(dilute.at("a"));
514 CI.t = cpjson::get_long_double_array(dilute.at("t"));
515 CI.T_reducing = cpjson::get_double(dilute, "T_reducing");
516 CI.C = cpjson::get_double(dilute, "C");
517
519 } else {
520 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
521 }
522 };
523
525 void parse_initial_density_viscosity(const nlohmann::json& initial_density, CoolPropFluid& fluid) {
526 std::string type = cpjson::get_string(initial_density, "type");
527 if (!type.compare("Rainwater-Friend")) {
528 // Get a reference to the entry in the fluid instance
530
531 // Load up the values
532 RF.b = cpjson::get_long_double_array(initial_density.at("b"));
533 RF.t = cpjson::get_long_double_array(initial_density.at("t"));
534
535 // Set the type flag
537 } else if (!type.compare("empirical")) {
538 // Get a reference to the entry in the fluid instance
540
541 // Load up the values
542 EM.n = cpjson::get_long_double_array(initial_density.at("n"));
543 EM.d = cpjson::get_long_double_array(initial_density.at("d"));
544 EM.t = cpjson::get_long_double_array(initial_density.at("t"));
545 EM.T_reducing = cpjson::get_double(initial_density, "T_reducing");
546 EM.rhomolar_reducing = cpjson::get_double(initial_density, "rhomolar_reducing");
547
548 // Set the type flag
550 } else {
551 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
552 }
553 };
554
556 void parse_higher_order_viscosity(const nlohmann::json& higher, CoolPropFluid& fluid) {
557 // First check for hardcoded higher-order term
558 if (higher.contains("hardcoded")) {
559 std::string target = cpjson::get_string(higher, "hardcoded");
560 if (!target.compare("Hydrogen")) {
562 return;
563 } else if (!target.compare("n-Hexane")) {
565 return;
566 } else if (!target.compare("n-Heptane")) {
568 return;
569 } else if (!target.compare("Toluene")) {
571 return;
572 } else if (!target.compare("Ethane")) {
574 return;
575 } else if (!target.compare("Benzene")) {
577 return;
578 } else if (!target.compare("CarbonDioxideLaeseckeJPCRD2017")) {
580 return;
581 } else {
582 throw ValueError(
583 format("hardcoded higher order viscosity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
584 }
585 }
586
587 std::string type = cpjson::get_string(higher, "type");
588 if (!type.compare("modified_Batschinski_Hildebrand")) {
589 // Get a reference to the entry in the fluid instance to simplify the code that follows
591
592 // Set the flag for the type of this model
594
595 BH.T_reduce = cpjson::get_double(higher, "T_reduce");
596 BH.rhomolar_reduce = cpjson::get_double(higher, "rhomolar_reduce");
597 // Load up the values
598 BH.a = cpjson::get_long_double_array(higher.at("a"));
599 BH.t1 = cpjson::get_long_double_array(higher.at("t1"));
600 BH.d1 = cpjson::get_long_double_array(higher.at("d1"));
601 BH.gamma = cpjson::get_long_double_array(higher.at("gamma"));
602 BH.l = cpjson::get_long_double_array(higher.at("l"));
603 assert(BH.a.size() == BH.t1.size());
604 assert(BH.a.size() == BH.d1.size());
605 assert(BH.a.size() == BH.gamma.size());
606 assert(BH.a.size() == BH.l.size());
607 BH.f = cpjson::get_long_double_array(higher.at("f"));
608 BH.t2 = cpjson::get_long_double_array(higher.at("t2"));
609 BH.d2 = cpjson::get_long_double_array(higher.at("d2"));
610 assert(BH.f.size() == BH.t2.size());
611 assert(BH.f.size() == BH.d2.size());
612 BH.g = cpjson::get_long_double_array(higher.at("g"));
613 BH.h = cpjson::get_long_double_array(higher.at("h"));
614 assert(BH.g.size() == BH.h.size());
615 BH.p = cpjson::get_long_double_array(higher.at("p"));
616 BH.q = cpjson::get_long_double_array(higher.at("q"));
617 assert(BH.p.size() == BH.q.size());
618 } else if (!type.compare("friction_theory")) {
619 // Get a reference to the entry in the fluid instance to simplify the code that follows
621
622 // Set the flag for the type of this model
624
625 // Always need these terms
626 F.Ai = cpjson::get_long_double_array(higher.at("Ai"));
627 F.Aa = cpjson::get_long_double_array(higher.at("Aa"));
628 F.Aaa = cpjson::get_long_double_array(higher.at("Aaa"));
629 F.Ar = cpjson::get_long_double_array(higher.at("Ar"));
630
631 F.Na = cpjson::get_integer(higher, "Na");
632 F.Naa = cpjson::get_integer(higher, "Naa");
633 F.Nr = cpjson::get_integer(higher, "Nr");
634 F.Nrr = cpjson::get_integer(higher, "Nrr");
635 F.c1 = cpjson::get_double(higher, "c1");
636 F.c2 = cpjson::get_double(higher, "c2");
637 assert(F.Aa.size() == 3);
638 assert(F.Aaa.size() == 3);
639 assert(F.Ar.size() == 3);
640
641 F.T_reduce = cpjson::get_double(higher, "T_reduce");
642
643 if (higher.contains("Arr") && !higher.contains("Adrdr")) {
644 F.Arr = cpjson::get_long_double_array(higher.at("Arr"));
645 assert(F.Arr.size() == 3);
646 } else if (higher.contains("Adrdr") && !higher.contains("Arr")) {
647 F.Adrdr = cpjson::get_long_double_array(higher.at("Adrdr"));
648 assert(F.Adrdr.size() == 3);
649 } else {
650 throw ValueError(format("can only provide one of Arr or Adrdr for fluid %s", fluid.name.c_str()));
651 }
652 if (higher.contains("Aii")) {
653 F.Aii = cpjson::get_long_double_array(higher.at("Aii"));
654 F.Nii = cpjson::get_integer(higher, "Nii");
655 }
656 if (higher.contains("Aaaa") && higher.contains("Arrr")) {
657 F.Aaaa = cpjson::get_long_double_array(higher.at("Aaaa"));
658 F.Arrr = cpjson::get_long_double_array(higher.at("Arrr"));
659 F.Naaa = cpjson::get_integer(higher, "Naaa");
660 F.Nrrr = cpjson::get_integer(higher, "Nrrr");
661 }
662
663 } else {
664 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
665 }
666 };
667
668 void parse_ECS_conductivity(const nlohmann::json& conductivity, CoolPropFluid& fluid) {
669 fluid.transport.conductivity_ecs.reference_fluid = cpjson::get_string(conductivity, "reference_fluid");
670
671 // Parameters for correction polynomials
672 fluid.transport.conductivity_ecs.psi_a = cpjson::get_long_double_array(conductivity.at("psi").at("a"));
673 fluid.transport.conductivity_ecs.psi_t = cpjson::get_long_double_array(conductivity.at("psi").at("t"));
674 fluid.transport.conductivity_ecs.psi_rhomolar_reducing = cpjson::get_double(conductivity.at("psi"), "rhomolar_reducing");
675 fluid.transport.conductivity_ecs.f_int_a = cpjson::get_long_double_array(conductivity.at("f_int").at("a"));
676 fluid.transport.conductivity_ecs.f_int_t = cpjson::get_long_double_array(conductivity.at("f_int").at("t"));
677 fluid.transport.conductivity_ecs.f_int_T_reducing = cpjson::get_double(conductivity.at("f_int"), "T_reducing");
678
680 }
681
682 void parse_ECS_viscosity(const nlohmann::json& viscosity, CoolPropFluid& fluid) {
683 fluid.transport.viscosity_ecs.reference_fluid = cpjson::get_string(viscosity, "reference_fluid");
684
685 // Parameters for correction polynomial
686 fluid.transport.viscosity_ecs.psi_a = cpjson::get_long_double_array(viscosity.at("psi").at("a"));
687 fluid.transport.viscosity_ecs.psi_t = cpjson::get_long_double_array(viscosity.at("psi").at("t"));
688 fluid.transport.viscosity_ecs.psi_rhomolar_reducing = cpjson::get_double(viscosity.at("psi"), "rhomolar_reducing");
689
690 fluid.transport.viscosity_using_ECS = true;
691 }
692
693 void parse_Chung_viscosity(const nlohmann::json& viscosity, CoolPropFluid& fluid) {
694 // These in base SI units
695 fluid.transport.viscosity_Chung.rhomolar_critical = cpjson::get_double(viscosity, "rhomolar_critical");
696 fluid.transport.viscosity_Chung.T_critical = cpjson::get_double(viscosity, "T_critical");
697 fluid.transport.viscosity_Chung.molar_mass = cpjson::get_double(viscosity, "molar_mass");
698 fluid.transport.viscosity_Chung.dipole_moment_D = cpjson::get_double(viscosity, "dipole_moment_D");
699 fluid.transport.viscosity_Chung.acentric = cpjson::get_double(viscosity, "acentric");
700 fluid.transport.viscosity_using_Chung = true;
701 }
702
703 void parse_rhosr_viscosity(const nlohmann::json& viscosity, CoolPropFluid& fluid) {
704 fluid.transport.viscosity_rhosr.C = cpjson::get_double(viscosity, "C");
705 fluid.transport.viscosity_rhosr.c_liq = cpjson::get_double_array(viscosity, "c_liq");
706 fluid.transport.viscosity_rhosr.c_vap = cpjson::get_double_array(viscosity, "c_vap");
707 fluid.transport.viscosity_rhosr.rhosr_critical = cpjson::get_double(viscosity, "rhosr_critical");
708 fluid.transport.viscosity_rhosr.x_crossover = cpjson::get_double(viscosity, "x_crossover");
709 fluid.transport.viscosity_using_rhosr = true;
710 }
711
713 void parse_viscosity(const nlohmann::json& viscosity, CoolPropFluid& fluid) {
714 // If an array, use the first one, and then stop;
715 if (viscosity.is_array()) {
716 parse_viscosity(viscosity.front(), fluid);
717 return;
718 }
719
720 // Load the BibTeX key
721 fluid.transport.BibTeX_viscosity = cpjson::get_string(viscosity, "BibTeX");
722
723 // Set the Lennard-Jones 12-6 potential variables, or approximate them from method of Chung
724 if (!viscosity.contains("sigma_eta") || !viscosity.contains("epsilon_over_k")) {
725 default_transport(fluid);
726 } else {
727 fluid.transport.sigma_eta = cpjson::get_double(viscosity, "sigma_eta");
728 fluid.transport.epsilon_over_k = cpjson::get_double(viscosity, "epsilon_over_k");
729 }
730
731 // If it is using ECS, set ECS parameters and quit
732 if (viscosity.contains("type") && !cpjson::get_string(viscosity, "type").compare("ECS")) {
733 parse_ECS_viscosity(viscosity, fluid);
734 return;
735 }
736
737 // If it is using rho*sr CS, set parameters and quit
738 if (viscosity.contains("type") && !cpjson::get_string(viscosity, "type").compare("rhosr-CS")) {
739 parse_rhosr_viscosity(viscosity, fluid);
740 return;
741 }
742
743 // Use the method of Chung
744 // If it is using ECS, set ECS parameters and quit
745 if (viscosity.contains("type") && !cpjson::get_string(viscosity, "type").compare("Chung")) {
746 parse_Chung_viscosity(viscosity, fluid);
747 return;
748 }
749
750 if (viscosity.contains("hardcoded")) {
751 std::string target = cpjson::get_string(viscosity, "hardcoded");
752 if (!target.compare("Water")) {
754 return;
755 } else if (!target.compare("HeavyWater")) {
757 return;
758 } else if (!target.compare("Helium")) {
760 return;
761 } else if (!target.compare("R23")) {
763 return;
764 } else if (!target.compare("Methanol")) {
766 return;
767 } else if (!target.compare("m-Xylene")) {
769 return;
770 } else if (!target.compare("o-Xylene")) {
772 return;
773 } else if (!target.compare("p-Xylene")) {
775 return;
776 } else {
777 throw ValueError(format("hardcoded viscosity [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
778 }
779 }
780
781 // Load dilute viscosity term
782 if (viscosity.contains("dilute")) {
783 parse_dilute_viscosity(viscosity.at("dilute"), fluid);
784 }
785 // Load initial density term
786 if (viscosity.contains("initial_density")) {
787 parse_initial_density_viscosity(viscosity.at("initial_density"), fluid);
788 }
789 // Load higher_order term
790 if (viscosity.contains("higher_order")) {
791 parse_higher_order_viscosity(viscosity.at("higher_order"), fluid);
792 }
793 };
794
796 void parse_dilute_conductivity(const nlohmann::json& dilute, CoolPropFluid& fluid) {
797 if (dilute.contains("hardcoded")) {
798 std::string target = cpjson::get_string(dilute, "hardcoded");
799 if (!target.compare("CO2")) {
801 return;
802 } else if (!target.compare("CarbonDioxideHuberJPCRD2016")) {
804 return;
805 } else if (!target.compare("Ethane")) {
807 return;
808 } else if (!target.compare("none")) {
810 return;
811 } else {
812 throw ValueError(
813 format("hardcoded dilute conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
814 }
815 }
816 std::string type = cpjson::get_string(dilute, "type");
817 if (!type.compare("ratio_of_polynomials")) {
818 // Get a reference to the entry in the fluid instance
820
821 // Set the type flag
823
824 // Load up the values
825 data.A = cpjson::get_long_double_array(dilute.at("A"));
826 data.B = cpjson::get_long_double_array(dilute.at("B"));
827 data.n = cpjson::get_long_double_array(dilute.at("n"));
828 data.m = cpjson::get_long_double_array(dilute.at("m"));
829 data.T_reducing = cpjson::get_double(dilute, "T_reducing");
830 } else if (!type.compare("eta0_and_poly")) {
831 // Get a reference to the entry in the fluid instance
833
834 // Set the type flag
836
837 // Load up the values
838 data.A = cpjson::get_long_double_array(dilute.at("A"));
839 data.t = cpjson::get_long_double_array(dilute.at("t"));
840 } else {
841 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
842 }
843 };
844
846 void parse_residual_conductivity(const nlohmann::json& dilute, CoolPropFluid& fluid) {
847 if (dilute.contains("hardcoded")) {
848 std::string target = cpjson::get_string(dilute, "hardcoded");
849 if (!target.compare("CO2")) {
851 return;
852 } else {
853 throw ValueError(
854 format("hardcoded residual conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
855 }
856 }
857 std::string type = cpjson::get_string(dilute, "type");
858 if (!type.compare("polynomial")) {
859 // Get a reference to the entry in the fluid instance
861
862 // Set the type flag
864
865 // Load up the values
866 data.B = cpjson::get_long_double_array(dilute.at("B"));
867 data.d = cpjson::get_long_double_array(dilute.at("d"));
868 data.t = cpjson::get_long_double_array(dilute.at("t"));
869 data.T_reducing = cpjson::get_double(dilute, "T_reducing");
870 data.rhomass_reducing = cpjson::get_double(dilute, "rhomass_reducing");
871 } else if (!type.compare("polynomial_and_exponential")) {
872 // Get a reference to the entry in the fluid instance
874
875 // Set the type flag
877
878 // Load up the values
879 data.A = cpjson::get_long_double_array(dilute.at("A"));
880 data.d = cpjson::get_long_double_array(dilute.at("d"));
881 data.t = cpjson::get_long_double_array(dilute.at("t"));
882 data.gamma = cpjson::get_long_double_array(dilute.at("gamma"));
883 data.l = cpjson::get_long_double_array(dilute.at("l"));
884 } else {
885 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
886 }
887 };
888
889 void parse_critical_conductivity(const nlohmann::json& critical, CoolPropFluid& fluid) {
890 if (critical.contains("hardcoded")) {
891 std::string target = cpjson::get_string(critical, "hardcoded");
892 if (!target.compare("R123")) {
894 return;
895 } else if (!target.compare("Ammonia")) {
897 return;
898 } else if (!target.compare("CarbonDioxideScalabrinJPCRD2006")) {
901 return;
902 } else if (!target.compare("None")) {
904 return;
905 } else {
906 throw ValueError(format("critical conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
907 }
908 }
909 std::string type = cpjson::get_string(critical, "type");
910 if (!type.compare("simplified_Olchowy_Sengers")) {
913
914 // Set the type flag
916
917 // Set values if they are found - otherwise fall back to default values
918 if (critical.contains("qD")) {
919 data.qD = cpjson::get_double(critical, "qD");
920 }
921 if (critical.contains("zeta0")) {
922 data.zeta0 = cpjson::get_double(critical, "zeta0");
923 }
924 if (critical.contains("GAMMA")) {
925 data.GAMMA = cpjson::get_double(critical, "GAMMA");
926 }
927 if (critical.contains("gamma")) {
928 data.gamma = cpjson::get_double(critical, "gamma");
929 }
930 if (critical.contains("R0")) {
931 data.R0 = cpjson::get_double(critical, "R0");
932 }
933 if (critical.contains("T_ref")) {
934 data.T_ref = cpjson::get_double(critical, "T_ref");
935 }
936 } else {
937 throw ValueError(format("type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
938 }
939 };
940
942 void parse_thermal_conductivity(const nlohmann::json& conductivity, CoolPropFluid& fluid) {
943 // Load the BibTeX key
944 fluid.transport.BibTeX_conductivity = cpjson::get_string(conductivity, "BibTeX");
945
946 // If it is using ECS, set ECS parameters and quit
947 if (conductivity.contains("type") && !cpjson::get_string(conductivity, "type").compare("ECS")) {
948 parse_ECS_conductivity(conductivity, fluid);
949 return;
950 }
951
952 if (conductivity.contains("hardcoded")) {
953 std::string target = cpjson::get_string(conductivity, "hardcoded");
954 if (!target.compare("Water")) {
956 return;
957 } else if (!target.compare("HeavyWater")) {
959 return;
960 } else if (!target.compare("Methane")) {
962 return;
963 } else if (!target.compare("R23")) {
965 return;
966 } else if (!target.compare("Helium")) {
968 return;
969 } else {
970 throw ValueError(
971 format("hardcoded residual conductivity term [%s] is not understood for fluid %s", target.c_str(), fluid.name.c_str()));
972 }
973 }
974
975 // Load dilute conductivity term
976 if (conductivity.contains("dilute")) {
977 parse_dilute_conductivity(conductivity.at("dilute"), fluid);
978 }
979 // Load residual conductivity term
980 if (conductivity.contains("residual")) {
981 parse_residual_conductivity(conductivity.at("residual"), fluid);
982 }
983 // Load critical conductivity term
984 if (conductivity.contains("critical")) {
985 parse_critical_conductivity(conductivity.at("critical"), fluid);
986 }
987 };
988
990 void parse_transport(const nlohmann::json& transport, CoolPropFluid& fluid) {
991
992 // Parse viscosity
993 if (transport.contains("viscosity")) {
994 parse_viscosity(transport.at("viscosity"), fluid);
996 }
997
998 // Parse thermal conductivity
999 if (transport.contains("conductivity")) {
1000 parse_thermal_conductivity(transport.at("conductivity"), fluid);
1002 }
1003 };
1004
1006 // Use the method of Chung to approximate the values for epsilon_over_k and sigma_eta
1007 // 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.
1008 // rhoc needs to be in mol/L to yield a sigma in nm,
1009 CoolPropDbl rho_crit_molar = fluid.EOS().reduce.rhomolar / 1000.0; // [mol/m3 to mol/L]
1010 CoolPropDbl Tc = fluid.EOS().reduce.T;
1011 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
1012 fluid.transport.epsilon_over_k = Tc / 1.2593; // [K]
1013 }
1014
1015 void parse_melting_line(const nlohmann::json& melting_line, CoolPropFluid& fluid) {
1016 fluid.ancillaries.melting_line.T_m = cpjson::get_double(melting_line, "T_m");
1017 fluid.ancillaries.melting_line.BibTeX = cpjson::get_string(melting_line, "BibTeX");
1018
1019 if (melting_line.contains("type")) {
1020 std::string type = cpjson::get_string(melting_line, "type");
1021 if (!type.compare("Simon")) {
1022 const nlohmann::json& parts = melting_line.at("parts");
1024 for (const auto& part : parts) {
1026 data.a = cpjson::get_double(part, "a");
1027 data.c = cpjson::get_double(part, "c");
1028 data.T_min = cpjson::get_double(part, "T_min");
1029 data.T_max = cpjson::get_double(part, "T_max");
1030 data.T_0 = cpjson::get_double(part, "T_0");
1031 data.p_0 = cpjson::get_double(part, "p_0");
1032 fluid.ancillaries.melting_line.simon.parts.push_back(data);
1033 }
1034 } else if (!type.compare("polynomial_in_Tr")) {
1035 const nlohmann::json& parts = melting_line.at("parts");
1037 for (const auto& part : parts) {
1039 data.a = cpjson::get_long_double_array(part, "a");
1040 data.t = cpjson::get_long_double_array(part, "t");
1041 data.T_min = cpjson::get_double(part, "T_min");
1042 data.T_max = cpjson::get_double(part, "T_max");
1043 data.T_0 = cpjson::get_double(part, "T_0");
1044 data.p_0 = cpjson::get_double(part, "p_0");
1045 fluid.ancillaries.melting_line.polynomial_in_Tr.parts.push_back(data);
1046 }
1047 } else if (!type.compare("polynomial_in_Theta")) {
1048 const nlohmann::json& parts = melting_line.at("parts");
1050 for (const auto& part : parts) {
1052 data.a = cpjson::get_long_double_array(part, "a");
1053 data.t = cpjson::get_long_double_array(part, "t");
1054 data.T_min = cpjson::get_double(part, "T_min");
1055 data.T_max = cpjson::get_double(part, "T_max");
1056 data.T_0 = cpjson::get_double(part, "T_0");
1057 data.p_0 = cpjson::get_double(part, "p_0");
1058 fluid.ancillaries.melting_line.polynomial_in_Theta.parts.push_back(data);
1059 }
1060 } else {
1061 throw ValueError(format("melting line type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
1062 }
1063 // Set the limits for the melting line curve
1065 } else {
1066 throw ValueError(format("melting line does not have \"type\" for fluid %s", fluid.name.c_str()));
1067 }
1068 };
1069
1071 void parse_states(const nlohmann::json& states, CoolPropFluid& fluid) {
1072 if (!states.contains("critical")) {
1073 throw ValueError(format(R"(fluid["STATES"] [%s] does not have "critical" member)", fluid.name.c_str()));
1074 }
1075 const nlohmann::json& crit = states.at("critical");
1076 fluid.crit.T = cpjson::get_double(crit, "T");
1077 fluid.crit.p = cpjson::get_double(crit, "p");
1078 fluid.crit.rhomolar = cpjson::get_double(crit, "rhomolar");
1079 fluid.crit.hmolar = cpjson::get_double(crit, "hmolar");
1080 fluid.crit.smolar = cpjson::get_double(crit, "smolar");
1081
1082 if (!states.contains("triple_liquid")) {
1083 throw ValueError(format(R"(fluid["STATES"] [%s] does not have "triple_liquid" member)", fluid.name.c_str()));
1084 }
1085 const nlohmann::json& triple_liquid = states.at("triple_liquid");
1086 if (triple_liquid.empty()) {
1087 // State is empty - probably because the triple point temperature is below the minimum saturation temperature
1088 fluid.triple_liquid.T = -1;
1089 fluid.triple_liquid.p = -1;
1090 fluid.triple_liquid.rhomolar = -1;
1091 fluid.triple_liquid.hmolar = _HUGE;
1092 fluid.triple_liquid.smolar = _HUGE;
1093 } else {
1094 fluid.triple_liquid.T = cpjson::get_double(triple_liquid, "T");
1095 fluid.triple_liquid.p = cpjson::get_double(triple_liquid, "p");
1096 fluid.triple_liquid.rhomolar = cpjson::get_double(triple_liquid, "rhomolar");
1097 fluid.triple_liquid.hmolar = cpjson::get_double(triple_liquid, "hmolar");
1098 fluid.triple_liquid.smolar = cpjson::get_double(triple_liquid, "smolar");
1099 }
1100
1101 if (!states.contains("triple_vapor")) {
1102 throw ValueError(format(R"(fluid["STATES"] [%s] does not have "triple_vapor" member)", fluid.name.c_str()));
1103 }
1104 const nlohmann::json& triple_vapor = states.at("triple_vapor");
1105 if (triple_vapor.empty()) {
1106 // State is empty - probably because the triple point temperature is below the minimum saturation temperature
1107 fluid.triple_vapor.T = -1;
1108 fluid.triple_vapor.p = -1;
1109 fluid.triple_vapor.rhomolar = -1;
1110 fluid.triple_vapor.hmolar = _HUGE;
1111 fluid.triple_vapor.smolar = _HUGE;
1112 } else {
1113 fluid.triple_vapor.T = cpjson::get_double(triple_vapor, "T");
1114 fluid.triple_vapor.p = cpjson::get_double(triple_vapor, "p");
1115 fluid.triple_vapor.rhomolar = cpjson::get_double(triple_vapor, "rhomolar");
1116 fluid.triple_vapor.hmolar = cpjson::get_double(triple_vapor, "hmolar");
1117 fluid.triple_vapor.smolar = cpjson::get_double(triple_vapor, "smolar");
1118 }
1119 };
1120
1122 void parse_ancillaries(const nlohmann::json& ancillaries, CoolPropFluid& fluid) {
1123 if (!ancillaries.contains("rhoL") || !ancillaries.contains("rhoV")) {
1124 throw ValueError("Ancillary curves for either rhoL or rhoV are missing");
1125 }
1126 fluid.ancillaries.rhoL = cpjson::make_saturation_ancillary(ancillaries.at("rhoL"));
1127 fluid.ancillaries.rhoV = cpjson::make_saturation_ancillary(ancillaries.at("rhoV"));
1128
1129 // If a pseudo-pure fluid, has pL and pV curves
1130 if (ancillaries.contains("pL") && ancillaries.contains("pV")) {
1131 fluid.ancillaries.pL = cpjson::make_saturation_ancillary(ancillaries.at("pL"));
1132 fluid.ancillaries.pV = cpjson::make_saturation_ancillary(ancillaries.at("pV"));
1133 }
1134 // Otherwise has a single pS curve and not pL and not pV
1135 else if (!ancillaries.contains("pL") && !ancillaries.contains("pV") && ancillaries.contains("pS")) {
1136 fluid.ancillaries.pL = cpjson::make_saturation_ancillary(ancillaries.at("pS"));
1137 fluid.ancillaries.pV = cpjson::make_saturation_ancillary(ancillaries.at("pS"));
1138 } else {
1139 throw ValueError("Pressure ancillary curves are missing or invalid");
1140 }
1141
1142 if (ancillaries.contains("hL")) {
1143 fluid.ancillaries.hL = cpjson::make_saturation_ancillary(ancillaries.at("hL"));
1144 } else {
1145 if (get_debug_level() > 0) {
1146 std::cout << "Missing hL ancillary for fluid " << fluid.name;
1147 }
1148 }
1149 if (ancillaries.contains("hLV")) {
1150 fluid.ancillaries.hLV = cpjson::make_saturation_ancillary(ancillaries.at("hLV"));
1151 } else {
1152 if (get_debug_level() > 0) {
1153 std::cout << "Missing hLV ancillary for fluid " << fluid.name;
1154 }
1155 }
1156
1157 if (ancillaries.contains("sL")) {
1158 fluid.ancillaries.sL = cpjson::make_saturation_ancillary(ancillaries.at("sL"));
1159 } else {
1160 if (get_debug_level() > 0) {
1161 std::cout << "Missing sL ancillary for fluid " << fluid.name;
1162 }
1163 }
1164 if (ancillaries.contains("sLV")) {
1165 fluid.ancillaries.sLV = cpjson::make_saturation_ancillary(ancillaries.at("sLV"));
1166 } else {
1167 if (get_debug_level() > 0) {
1168 std::cout << "Missing sLV ancillary for fluid " << fluid.name;
1169 }
1170 }
1171 if (!ValidNumber(fluid.ancillaries.sL.get_Tmin()) && get_debug_level() > 0) {
1172 std::cout << "Tmin invalid for sL for " << fluid.name << '\n';
1173 }
1174 };
1175
1177 void parse_surface_tension(const nlohmann::json& surface_tension, CoolPropFluid& fluid) {
1179 };
1180
1183 assert(fluid.EOSVector.size() > 0);
1184 assert(fluid.CAS.length() > 0);
1185 assert(fluid.name.length() > 0);
1186 }
1187
1188 public:
1189 // Default constructor;
1191 : _is_empty(true) {
1192
1193 };
1194 bool is_empty() {
1195 return _is_empty;
1196 };
1197
1199 static void add_many(const std::string& JSON_string);
1200
1202 void add_many(const nlohmann::json& listing);
1203
1204 void add_one(const nlohmann::json& fluid_json);
1205
1206 std::string get_JSONstring(const std::string& key) {
1207 // Try to find it
1208 auto it = string_to_index_map.find(key);
1209 if (it != string_to_index_map.end()) {
1210
1211 auto it2 = JSONstring_map.find(it->second);
1212 if (it2 != JSONstring_map.end()) {
1213 // Wrap the stored single-fluid JSON object in a one-element array
1214 nlohmann::json doc2 = nlohmann::json::array();
1215 doc2.push_back(cpjson::parse(it2->second));
1216 return doc2.dump();
1217 } else {
1218 throw ValueError(format("Unable to obtain JSON string for this identifier [%d]", it->second));
1219 }
1220 } else {
1221 throw ValueError(format("Unable to obtain index for this identifier [%s]", key.c_str()));
1222 }
1223 }
1224
1226
1229 CoolPropFluid get(const std::string& key) {
1230 // Try to find it
1231 auto it = string_to_index_map.find(key);
1232 // If it is found
1233 if (it != string_to_index_map.end()) {
1234 return get(it->second);
1235 } else {
1236 // Here we check for the use of a cubic Helmholtz energy transformation for a multi-fluid model
1237 std::vector<std::string> endings;
1238 endings.emplace_back("-SRK");
1239 endings.emplace_back("-PengRobinson");
1240 for (const auto& ending : endings) {
1241 if (endswith(key, ending)) {
1242 std::string used_name = key.substr(0, key.size() - ending.size());
1243 it = string_to_index_map.find(used_name);
1244 if (it != string_to_index_map.end()) {
1245 // We found the name of the fluid within the library of multiparameter
1246 // Helmholtz-explicit models. We will load its parameters from the
1247 // multiparameter EOS
1248 //
1249 CoolPropFluid fluid = get(it->second);
1250 // Remove all the residual contributions to the Helmholtz energy
1251 fluid.EOSVector[0].alphar.empty_the_EOS();
1252 // Get the parameters for the cubic EOS
1253 CoolPropDbl Tc = fluid.EOSVector[0].reduce.T;
1254 CoolPropDbl pc = fluid.EOSVector[0].reduce.p;
1255 CoolPropDbl rhomolarc = fluid.EOSVector[0].reduce.rhomolar;
1256 CoolPropDbl acentric = fluid.EOSVector[0].acentric;
1257 CoolPropDbl R = 8.3144598; // fluid.EOSVector[0].R_u;
1258 // Set the cubic contribution to the residual Helmholtz energy
1259 shared_ptr<AbstractCubic> ac;
1260 if (ending == "-SRK") {
1261 ac = std::make_shared<SRK>(Tc, pc, acentric, R);
1262 } else if (ending == "-PengRobinson") {
1263 ac = std::make_shared<PengRobinson>(Tc, pc, acentric, R);
1264 } else {
1265 throw CoolProp::ValueError(format("Unable to match this ending [%s]", ending.c_str()));
1266 }
1267 ac->set_Tr(Tc);
1268 ac->set_rhor(rhomolarc);
1269 fluid.EOSVector[0].alphar.cubic = ResidualHelmholtzGeneralizedCubic(ac);
1270 return fluid;
1271 } else {
1272 // Let's look in the library of cubic EOS
1274 // Set the cubic contribution to the residual Helmholtz energy
1275 shared_ptr<AbstractCubic> ac;
1276 if (ending == "-SRK") {
1277 ac = std::make_shared<SRK>(vals.Tc, vals.pc, vals.acentric, get_config_double(R_U_CODATA));
1278 } else if (ending == "-PengRobinson") {
1279 ac = std::make_shared<PengRobinson>(vals.Tc, vals.pc, vals.acentric, get_config_double(R_U_CODATA));
1280 } else {
1281 throw CoolProp::ValueError(format("Unable to match this ending [%s]", ending.c_str()));
1282 }
1283 ac->set_Tr(vals.Tc);
1284 if (vals.rhomolarc > 0) {
1285 ac->set_rhor(vals.rhomolarc);
1286 } else {
1287 // Curve fit from all the pure fluids in CoolProp (thanks to recommendation of A. Kazakov)
1288 double v_c_Lmol = 2.14107171795 * (vals.Tc / vals.pc * 1000) + 0.00773144012514; // [L/mol]
1289 ac->set_rhor(1 / (v_c_Lmol / 1000.0));
1290 }
1291 if (vals.alpha_type == "Twu") {
1292 std::vector<double>& c = vals.alpha_coeffs;
1293 ac->set_C_Twu(0, c[0], c[1], c[2]);
1294 }
1295 CoolPropFluid fluid;
1296 fluid.CAS = vals.CAS;
1298 E.acentric = vals.acentric;
1299 E.sat_min_liquid.T = _HUGE;
1300 E.sat_min_liquid.p = _HUGE;
1301 E.reduce.T = vals.Tc;
1302 E.reduce.p = vals.pc;
1303 E.reduce.rhomolar = ac->get_rhor();
1304 fluid.EOSVector.push_back(E);
1306 fluid.EOS().alpha0 = vals.alpha0;
1307 fluid.crit.T = vals.Tc;
1308 fluid.crit.p = vals.pc;
1309 fluid.crit.rhomolar = ac->get_rhor();
1310
1311 return fluid;
1312 }
1313 }
1314 }
1315 throw ValueError(format("key [%s] was not found in string_to_index_map in JSONFluidLibrary", key.c_str()));
1316 }
1317 };
1318
1320
1323 CoolPropFluid get(std::size_t key) {
1324 // Try to find it
1325 auto it = fluid_map.find(key);
1326 // If it is found
1327 if (it != fluid_map.end()) {
1328 return it->second;
1329 } else {
1330 throw ValueError(format("key [%d] was not found in JSONFluidLibrary", key));
1331 }
1332 };
1333 void set_fluid_enthalpy_entropy_offset(const std::string& fluid, double delta_a1, double delta_a2, const std::string& ref);
1335 std::string get_fluid_list() {
1336 return strjoin(name_vector, get_config_string(LIST_STRING_DELIMITER));
1337 };
1338};
1339
1341JSONFluidLibrary& get_library();
1342
1344std::string get_fluid_list();
1345
1347CoolPropFluid get_fluid(const std::string& fluid_string);
1348
1350std::string get_fluid_as_JSONstring(const std::string& indentifier);
1351
1353void set_fluid_enthalpy_entropy_offset(const std::string& fluid, double delta_a1, double delta_a2, const std::string& ref);
1354
1355} /* namespace CoolProp */
1356#endif