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