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