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