CoolProp 6.8.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
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 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
51void 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
109void 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
122void 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
132void 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
360CoolPropFluid get_fluid(const std::string& fluid_string) {
361 if (library.is_empty()) {
362 load();
363 }
364 return library.get(fluid_string);
365}
366
367std::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
374std::string get_fluid_list(void) {
375 if (library.is_empty()) {
376 load();
377 }
378 return library.get_fluid_list();
379};
380
381void 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 */