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