Loading [MathJax]/extensions/TeX/AMSsymbols.js
CoolProp  6.7.1dev
An open-source fluid property and humid air property database
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FluidLibrary.cpp
Go to the documentation of this file.
1 
2 #include "FluidLibrary.h"
3 
5 #include "miniz.h"
6 
7 #if defined(COOLPROP_NO_INCBIN)
8 # define INCBIN_CONST
9 # define INCBIN_ALIGN
10 # include "all_fluids_JSON_z.h"
11 # undef INCBIN_CONST
12 # undef INCBIN_ALIGN
13 #else
14 #include "incbin.h"
15 // Use the magic of the incbin library to include binary data in compressed form
16 #if defined(_MSC_VER)
17 #include "all_fluids_JSON_z.h"
18 #else
19 
20 INCBIN(all_fluids_JSON_z, "all_fluids.json.z");
21 #endif
22 #endif
23 
24 namespace CoolProp {
25 
26 static JSONFluidLibrary library;
27 
28 void load() {
29  std::vector<unsigned char> outbuffer(gall_fluids_JSON_zSize * 7);
30  uLong outlen = static_cast<uLong>(outbuffer.size());
31  auto code = uncompress(&outbuffer[0], &outlen, gall_fluids_JSON_zData, gall_fluids_JSON_zSize);
32  std::string buf(outbuffer.begin(), outbuffer.begin() + outlen);
33  if (code != 0) {
34  throw ValueError("Unable to uncompress the fluid data from z compressed form");
35  }
36 
37  rapidjson::Document dd;
38  // This json formatted string comes from the all_fluids_JSON.h header which is a C++-escaped version of the JSON file
39  dd.Parse<0>(buf.c_str());
40  if (dd.HasParseError()) {
41  throw ValueError("Unable to load all_fluids.json");
42  } else {
43  try {
44  library.add_many(dd);
45  } catch (std::exception& e) {
46  std::cout << e.what() << std::endl;
47  }
48  }
49 }
50 
51 void JSONFluidLibrary::set_fluid_enthalpy_entropy_offset(const std::string& fluid, double delta_a1, double delta_a2, const std::string& ref) {
52  // Try to find it
53  std::map<std::string, std::size_t>::const_iterator it = string_to_index_map.find(fluid);
54  if (it != string_to_index_map.end()) {
55  std::map<std::size_t, CoolPropFluid>::iterator it2 = fluid_map.find(it->second);
56  // If it is found
57  if (it2 != fluid_map.end()) {
58  if (!ValidNumber(delta_a1) || !ValidNumber(delta_a2)) {
59  throw ValueError(format("Not possible to set reference state for fluid %s because offset values are NAN", fluid.c_str()));
60  }
61  it2->second.EOS().alpha0.EnthalpyEntropyOffset.set(delta_a1, delta_a2, ref);
62 
63  shared_ptr<CoolProp::HelmholtzEOSBackend> HEOS(new CoolProp::HelmholtzEOSBackend(it2->second));
64  HEOS->specify_phase(iphase_gas); // Something homogeneous;
65  // Calculate the new enthalpy and entropy values
66  HEOS->update(DmolarT_INPUTS, it2->second.EOS().hs_anchor.rhomolar, it2->second.EOS().hs_anchor.T);
67  it2->second.EOS().hs_anchor.hmolar = HEOS->hmolar();
68  it2->second.EOS().hs_anchor.smolar = HEOS->smolar();
69 
70  double f = (HEOS->name() == "Water" || HEOS->name() == "CarbonDioxide") ? 1.00001 : 1.0;
71 
72  // Calculate the new enthalpy and entropy values at the reducing state
73  HEOS->update(DmolarT_INPUTS, it2->second.EOS().reduce.rhomolar * f, it2->second.EOS().reduce.T * f);
74  it2->second.EOS().reduce.hmolar = HEOS->hmolar();
75  it2->second.EOS().reduce.smolar = HEOS->smolar();
76 
77  // Calculate the new enthalpy and entropy values at the critical state
78  HEOS->update(DmolarT_INPUTS, it2->second.crit.rhomolar * f, it2->second.crit.T * f);
79  it2->second.crit.hmolar = HEOS->hmolar();
80  it2->second.crit.smolar = HEOS->smolar();
81 
82  // Calculate the new enthalpy and entropy values
83  HEOS->update(DmolarT_INPUTS, it2->second.triple_liquid.rhomolar, it2->second.triple_liquid.T);
84  it2->second.triple_liquid.hmolar = HEOS->hmolar();
85  it2->second.triple_liquid.smolar = HEOS->smolar();
86 
87  // Calculate the new enthalpy and entropy values
88  HEOS->update(DmolarT_INPUTS, it2->second.triple_vapor.rhomolar, it2->second.triple_vapor.T);
89  it2->second.triple_vapor.hmolar = HEOS->hmolar();
90  it2->second.triple_vapor.smolar = HEOS->smolar();
91 
92  if (!HEOS->is_pure()) {
93  // Calculate the new enthalpy and entropy values
94  HEOS->update(DmolarT_INPUTS, it2->second.EOS().max_sat_T.rhomolar, it2->second.EOS().max_sat_T.T);
95  it2->second.EOS().max_sat_T.hmolar = HEOS->hmolar();
96  it2->second.EOS().max_sat_T.smolar = HEOS->smolar();
97  // Calculate the new enthalpy and entropy values
98  HEOS->update(DmolarT_INPUTS, it2->second.EOS().max_sat_p.rhomolar, it2->second.EOS().max_sat_p.T);
99  it2->second.EOS().max_sat_p.hmolar = HEOS->hmolar();
100  it2->second.EOS().max_sat_p.smolar = HEOS->smolar();
101  }
102  } else {
103  throw ValueError(format("fluid [%s] was not found in JSONFluidLibrary", fluid.c_str()));
104  }
105  }
106 }
107 
109 void JSONFluidLibrary::add_many(const std::string& JSON_string) {
110 
111  // First load all the baseline fluids
112  if (library.is_empty()) {
113  load();
114  }
115 
116  // Then, load the fluids we would like to add
117  rapidjson::Document doc;
118  cpjson::JSON_string_to_rapidjson(JSON_string, doc);
119  library.add_many(doc);
120 };
121 
122 void JSONFluidLibrary::add_many(rapidjson::Value& listing) {
123  if (!listing.IsArray()) {
124  add_one(listing);
125  return;
126  }
127  for (rapidjson::Value::ValueIterator itr = listing.Begin(); itr != listing.End(); ++itr) {
128  add_one(*itr);
129  }
130 };
131 
132 void JSONFluidLibrary::add_one(rapidjson::Value& fluid_json) {
133  _is_empty = false;
134 
135  // The variable index is initialized to the size of the fluid_map.
136  // Since the first fluid_map key equals zero (0), index is initialized to the key
137  // value for the next fluid to be added. (e.g. fluid_map[0..140]; index = 141 )
138  std::size_t index = fluid_map.size();
139 
140  CoolPropFluid fluid; // create a new CoolPropFluid object
141 
142  // Assign the fluid properties based on the passed in fluid_json
143  // =============================================================
144  // Parse out Fluid name
145  fluid.name = fluid_json["INFO"]["NAME"].GetString();
146 
147  // Push the fluid name onto the name_vector used for returning the full list of library fluids
148  // If it is found that this fluid already exists in the library, it will be popped back off below.
149  name_vector.push_back(fluid.name);
150 
151  try {
152  // CAS number
153  if (!fluid_json["INFO"].HasMember("CAS")) {
154  throw ValueError(format("fluid [%s] does not have \"CAS\" member", fluid.name.c_str()));
155  }
156  fluid.CAS = fluid_json["INFO"]["CAS"].GetString();
157 
158  // REFPROP alias
159  if (!fluid_json["INFO"].HasMember("REFPROP_NAME")) {
160  throw ValueError(format("fluid [%s] does not have \"REFPROP_NAME\" member", fluid.name.c_str()));
161  }
162  fluid.REFPROPname = fluid_json["INFO"]["REFPROP_NAME"].GetString();
163 
164  // FORMULA
165  if (fluid_json["INFO"].HasMember("FORMULA")) {
166  fluid.formula = cpjson::get_string(fluid_json["INFO"], "FORMULA");
167  } else {
168  fluid.formula = "N/A";
169  }
170 
171  // Abstract references
172  if (fluid_json["INFO"].HasMember("INCHI_STRING")) {
173  fluid.InChI = cpjson::get_string(fluid_json["INFO"], "INCHI_STRING");
174  } else {
175  fluid.InChI = "N/A";
176  }
177 
178  if (fluid_json["INFO"].HasMember("INCHI_KEY")) {
179  fluid.InChIKey = cpjson::get_string(fluid_json["INFO"], "INCHI_KEY");
180  } else {
181  fluid.InChIKey = "N/A";
182  }
183 
184  if (fluid_json["INFO"].HasMember("SMILES")) {
185  fluid.smiles = cpjson::get_string(fluid_json["INFO"], "SMILES");
186  } else {
187  fluid.smiles = "N/A";
188  }
189 
190  if (fluid_json["INFO"].HasMember("CHEMSPIDER_ID")) {
191  fluid.ChemSpider_id = cpjson::get_integer(fluid_json["INFO"], "CHEMSPIDER_ID");
192  } else {
193  fluid.ChemSpider_id = -1;
194  }
195 
196  if (fluid_json["INFO"].HasMember("2DPNG_URL")) {
197  fluid.TwoDPNG_URL = cpjson::get_string(fluid_json["INFO"], "2DPNG_URL");
198  } else {
199  fluid.TwoDPNG_URL = "N/A";
200  }
201 
202  // Parse the environmental parameters
203  if (!(fluid_json["INFO"].HasMember("ENVIRONMENTAL"))) {
204  if (get_debug_level() > 0) {
205  std::cout << format("Environmental data are missing for fluid [%s]\n", fluid.name.c_str());
206  }
207  } else {
208  parse_environmental(fluid_json["INFO"]["ENVIRONMENTAL"], fluid);
209  }
210 
211  // Aliases
212  fluid.aliases = cpjson::get_string_array(fluid_json["INFO"]["ALIASES"]);
213 
214  // Critical state
215  if (!fluid_json.HasMember("STATES")) {
216  throw ValueError(format("fluid [%s] does not have \"STATES\" member", fluid.name.c_str()));
217  }
218  parse_states(fluid_json["STATES"], fluid);
219 
220  if (get_debug_level() > 5) {
221  std::cout << format("Loading fluid %s with CAS %s; %d fluids loaded\n", fluid.name.c_str(), fluid.CAS.c_str(), index);
222  }
223 
224  // EOS
225  parse_EOS_listing(fluid_json["EOS"], fluid);
226 
227  // Validate the fluid
228  validate(fluid);
229 
230  // Ancillaries for saturation
231  if (!fluid_json.HasMember("ANCILLARIES")) {
232  throw ValueError(format("Ancillary curves are missing for fluid [%s]", fluid.name.c_str()));
233  };
234  parse_ancillaries(fluid_json["ANCILLARIES"], fluid);
235 
236  // Surface tension
237  if (!(fluid_json["ANCILLARIES"].HasMember("surface_tension"))) {
238  if (get_debug_level() > 0) {
239  std::cout << format("Surface tension curves are missing for fluid [%s]\n", fluid.name.c_str());
240  }
241  } else {
242  parse_surface_tension(fluid_json["ANCILLARIES"]["surface_tension"], fluid);
243  }
244 
245  // Melting line
246  if (!(fluid_json["ANCILLARIES"].HasMember("melting_line"))) {
247  if (get_debug_level() > 0) {
248  std::cout << format("Melting line curves are missing for fluid [%s]\n", fluid.name.c_str());
249  }
250  } else {
251  parse_melting_line(fluid_json["ANCILLARIES"]["melting_line"], fluid);
252  }
253 
254  // Parse the transport property (viscosity and/or thermal conductivity) parameters
255  if (!(fluid_json.HasMember("TRANSPORT"))) {
256  default_transport(fluid);
257  } else {
258  parse_transport(fluid_json["TRANSPORT"], fluid);
259  }
260 
261  // If the fluid is ok...
262 
263  // First check that none of the identifiers are already present
264  // ===============================================================
265  // Remember that index is already initialized to fluid_map.size() = max index + 1.
266  // If the new fluid name, CAS, or aliases are found in the string_to_index_map, then
267  // the fluid is already in the fluid_map, so reset index to it's key.
268 
269  if (string_to_index_map.find(fluid.CAS) != string_to_index_map.end()) {
270  index = string_to_index_map.find(fluid.CAS)->second; //if CAS found, grab index
271  } else if (string_to_index_map.find(fluid.name) != string_to_index_map.end()) {
272  index = string_to_index_map.find(fluid.name)->second; // if name found, grab index
273  } else if (string_to_index_map.find(upper(fluid.name)) != string_to_index_map.end()) {
274  index = string_to_index_map.find(upper(fluid.name))->second; // if uppercase name found, grab index
275  } else {
276  // Check the aliases
277  for (std::size_t i = 0; i < fluid.aliases.size(); ++i) {
278  if (string_to_index_map.find(fluid.aliases[i]) != string_to_index_map.end()) {
279  index = string_to_index_map.find(fluid.aliases[i])->second; // if alias found, grab index
280  break;
281  }
282  if (string_to_index_map.find(upper(fluid.aliases[i])) != string_to_index_map.end()) { // if ALIAS found, grab index
283  index = string_to_index_map.find(upper(fluid.aliases[i]))->second;
284  break;
285  }
286  }
287  }
288 
289  bool fluid_exists = false; // Initialize flag for doing replace instead of add
290 
291  if (index != fluid_map.size()) { // Fluid already in list if index was reset to something < fluid_map.size()
292  fluid_exists = true; // Set the flag for replace
293  name_vector.pop_back(); // Pop duplicate name off the back of the name vector; otherwise it keeps growing!
294  if (!get_config_bool(OVERWRITE_FLUIDS)) { // Throw exception if replacing fluids is not allowed
295  throw ValueError(format("Cannot load fluid [%s:%s] because it is already in library; index = [%i] of [%i]; Consider enabling the "
296  "config boolean variable OVERWRITE_FLUIDS",
297  fluid.name.c_str(), fluid.CAS.c_str(), index, fluid_map.size()));
298  }
299  }
300 
301  // index now holds either
302  // 1. the index of a fluid that's already present, in which case it will be overwritten, or
303  // 2. the fluid_map.size(), in which case a new entry will be added to the list
304 
305  // Add/Replace index->fluid mapping
306  // If the fluid index exists, the [] operator replaces the existing entry with the new fluid;
307  // However, since fluid is a custom type, the old entry must be erased first to properly
308  // release the memory before adding in the new fluid object at the same location (index)
309  if (fluid_exists) fluid_map.erase(fluid_map.find(index));
310  // if not, it will add the (index,fluid) pair to the map using the new index value (fluid_map.size())
311  fluid_map[index] = fluid;
312 
313  // Add/Replace index->JSONstring mapping to easily pull out if the user wants it
314  // Convert fuid_json to a string and store it in the map at index.
315  // if the fluid index exists, the [] operator replaces the existing entry with the new JSONstring;
316  // However, since fluid_json is a custom type, the old entry must be erased first to properly
317  // release the memory before adding in the new fluid object at the same location (index)
318  if (fluid_exists) JSONstring_map.erase(JSONstring_map.find(index));
319  // if not, it will add the new (index,JSONstring) pair to the map.
320  JSONstring_map[index] = cpjson::json2string(fluid_json);
321 
322  // Add/Replace CAS->index mapping
323  // This map helps find the index of a fluid in the fluid_map given a CAS string
324  // If the CAS string exists, the [] operator will replace index with an updated index number;
325  // if not, it will add a new (CAS,index) pair to the map.
326  string_to_index_map[fluid.CAS] = index;
327 
328  // Add/Replace name->index mapping
329  // This map quickly finds the index of a fluid in the fluid_map given its name string
330  // Again, the map [] operator replaces if the alias is found, adds the new (name,index) pair if not
331  string_to_index_map[fluid.name] = index;
332 
333  // Add/Replace the aliases->index mapping
334  // This map quickly finds the index of a fluid in the fluid_map given an alias string
335  // Again, the map [] operator replaces if the alias is found, adds the new (alias,index) pair if not
336  for (std::size_t i = 0; i < fluid.aliases.size(); ++i) {
337  string_to_index_map[fluid.aliases[i]] = index;
338 
339  // Add uppercase alias for EES compatibility
340  string_to_index_map[upper(fluid.aliases[i])] = index;
341  }
342 
343  //If Debug level set >5 print fluid name and total size of fluid_map
344  if (get_debug_level() > 5) {
345  std::cout << format("Loaded fluid: %s - Number of fluids = %d\n", fluid.name, fluid_map.size());
346  }
347 
348  } catch (const std::exception& e) {
349  throw ValueError(format("Unable to load fluid [%s] due to error: %s", fluid.name.c_str(), e.what()));
350  }
351 };
352 
354  if (library.is_empty()) {
355  load();
356  }
357  return library;
358 }
359 
360 CoolPropFluid get_fluid(const std::string& fluid_string) {
361  if (library.is_empty()) {
362  load();
363  }
364  return library.get(fluid_string);
365 }
366 
367 std::string get_fluid_as_JSONstring(const std::string& identifier) {
368  if (library.is_empty()) {
369  load();
370  }
371  return library.get_JSONstring(identifier);
372 }
373 
374 std::string get_fluid_list(void) {
375  if (library.is_empty()) {
376  load();
377  }
378  return library.get_fluid_list();
379 };
380 
381 void set_fluid_enthalpy_entropy_offset(const std::string& fluid, double delta_a1, double delta_a2, const std::string& ref) {
382  if (library.is_empty()) {
383  load();
384  }
385  library.set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, ref);
386 }
387 
388 } /* namespace CoolProp */