CoolProp 8.0.0
An open-source fluid property and humid air property database
PCSAFTLibrary.cpp
Go to the documentation of this file.
1#include <string>
2#include <map>
3#include "PCSAFTLibrary.h"
4#include "all_pcsaft_JSON.h" // Makes a std::string variable called all_pcsaft_JSON
5#include "pcsaft_fluids_schema_JSON.h" // Makes a std::string variable called pcsaft_fluids_schema_JSON
6#include "mixture_binary_pairs_pcsaft_JSON.h" // Makes a std::string variable called mixture_binary_pairs_pcsaft_JSON
10#include "CoolProp/CoolProp.h"
14
15namespace CoolProp {
16
17std::string get_mixture_binary_pair_pcsaft(const std::string& CAS1, const std::string& CAS2, const std::string& key) {
19}
20
21void set_mixture_binary_pair_pcsaft(const std::string& CAS1, const std::string& CAS2, const std::string& key, const double value) {
23}
24
25namespace PCSAFTLibrary {
26
27namespace {
31void add_fluids_from_JSON_string(PCSAFTLibraryClass& dest, const std::string_view& JSON) {
32 std::string errstr;
33 cpjson::schema_validation_code val_code = cpjson::validate_schema(pcsaft_fluids_schema_JSON, JSON, errstr);
34 if (val_code == cpjson::SCHEMA_VALIDATION_OK) {
35 nlohmann::json dd = cpjson::parse(JSON);
36 try {
37 dest.add_many(dd);
38 } catch (std::exception& e) {
39 std::cout << e.what() << '\n';
40 }
41 } else {
42 if (get_debug_level() > 0) {
43 throw ValueError(format("Unable to load PC-SAFT library with error: %s", errstr.c_str()));
44 }
45 }
46}
47} // anonymous namespace
48
50 // Function-local static (Meyers singleton): C++11 guarantees the
51 // constructor runs exactly once even under concurrent first calls. The
52 // previous namespace-scope `static PCSAFTLibraryClass library;` relied on
53 // single-threaded static init, which is fragile across translation-unit
54 // init order and dynamic-library load contexts (gh-2787).
55 static PCSAFTLibraryClass library;
56 return library;
57}
58
60 // Populate via the file-private helper so the constructor does NOT recurse
61 // through get_library() while the singleton is still being built.
62 add_fluids_from_JSON_string(*this, all_pcsaft_JSON);
63
64 // Then we add the library of binary interaction parameters
65 if (m_binary_pair_map.size() == 0) {
66 PCSAFTLibraryClass::load_from_string(mixture_binary_pairs_pcsaft_JSON);
67 }
68}
69
70// Get a PCSAFTFluid instance stored in this library
71PCSAFTFluid& PCSAFTLibraryClass::get(const std::string& key) {
72 // Try to find it
73 auto it = string_to_index_map.find(key);
74 // If it is found
75 if (it != string_to_index_map.end()) {
76 return get(it->second);
77 } else {
78 throw ValueError(format("key [%s] was not found in string_to_index_map in PCSAFTLibraryClass", key.c_str()));
79 }
80}
81
83
87 // Try to find it
88 auto it = fluid_map.find(key);
89 // If it is found
90 if (it != fluid_map.end()) {
91 return it->second;
92 } else {
93 throw ValueError(format("key [%d] was not found in PCSAFTLibraryClass", key));
94 }
95};
96
97void add_fluids_as_JSON(const std::string_view& JSON) {
98 add_fluids_from_JSON_string(get_library(), JSON);
99}
100
101int PCSAFTLibraryClass::add_many(const nlohmann::json& listing) {
102 int counter = 0;
103 std::string fluid_name;
104 for (const auto& fluid_json : listing) {
105 try {
106 PCSAFTFluid fluid = cpjson::make_pcsaft_fluid(fluid_json);
107 fluid_name = fluid.getName();
108
109 // If the fluid is ok...
110
111 // First check that none of the identifiers are already present
112 bool already_present = false;
113
114 if (string_to_index_map.find(fluid.getCAS()) != string_to_index_map.end()
115 || string_to_index_map.find(fluid_name) != string_to_index_map.end()
116 || string_to_index_map.find(upper(fluid_name)) != string_to_index_map.end()) {
117 already_present = true;
118 } else {
119 // Check the aliases
120 for (std::size_t i = 0; i < fluid.getAliases().size(); ++i) {
121 if (string_to_index_map.find(fluid.getAliases()[i]) != string_to_index_map.end()) {
122 already_present = true;
123 break;
124 }
125 if (string_to_index_map.find(upper(fluid.getAliases()[i])) != string_to_index_map.end()) {
126 already_present = true;
127 break;
128 }
129 }
130 }
131
132 if (already_present) {
133 if (!get_config_bool(OVERWRITE_FLUIDS)) {
134 throw ValueError(format(
135 "Cannot load fluid [%s:%s] because it is already in library; consider enabling the config boolean variable OVERWRITE_FLUIDS",
136 fluid.getName().c_str(), fluid.getCAS().c_str()));
137 } else {
138 // Remove the one(s) that are already there
139
140 // Remove the actual fluid instance
141 std::size_t index = string_to_index_map.find(fluid_name)->second;
142
143 if (fluid_map.find(index) != fluid_map.end()) {
144 fluid_map.erase(fluid_map.find(index));
145 }
146
147 if (string_to_index_map.find(fluid_name) != string_to_index_map.end()) {
148 fluid_map.erase(fluid_map.find(index));
149 }
150
151 // Remove the identifiers pointing to that instance
152 if (string_to_index_map.find(fluid.getCAS()) != string_to_index_map.end()) {
153 string_to_index_map.erase(string_to_index_map.find(fluid.getCAS()));
154 }
155 if (string_to_index_map.find(fluid_name) != string_to_index_map.end()) {
156 string_to_index_map.erase(string_to_index_map.find(fluid_name));
157 }
158 // Check the aliases
159 for (std::size_t i = 0; i < fluid.getAliases().size(); ++i) {
160 if (string_to_index_map.find(fluid.getAliases()[i]) != string_to_index_map.end()) {
161 string_to_index_map.erase(string_to_index_map.find(fluid.getAliases()[i]));
162 }
163 if (string_to_index_map.find(upper(fluid.getAliases()[i])) != string_to_index_map.end()) {
164 string_to_index_map.erase(string_to_index_map.find(upper(fluid.getAliases()[i])));
165 }
166 }
167 }
168 }
169
170 // By now, the library has been cleared of remnants of this fluid; safe to add the fluid now.
171
172 // Get the next index for this fluid
173 std::size_t index = fluid_map.size();
174
175 // Add index->fluid mapping
176 fluid_map[index] = fluid;
177
178 // fluid_map[index] = cpjson::json2string(fluid_json);
179
180 // Add CAS->index mapping
181 string_to_index_map[fluid.getCAS()] = index;
182
183 // Add name->index mapping
184 string_to_index_map[fluid_name] = index;
185
186 // Add the aliases
187 for (std::size_t i = 0; i < fluid.getAliases().size(); ++i) {
188 string_to_index_map[fluid.getAliases()[i]] = index;
189
190 // Add uppercase alias for EES compatibility
191 string_to_index_map[upper(fluid.getAliases()[i])] = index;
192 }
193
194 counter++;
195 if (get_debug_level() > 5) {
196 std::cout << format("Loaded.\n");
197 }
198 } catch (const std::exception& e) {
199 throw ValueError(format("Unable to load fluid [%s] due to error: %s", fluid_name.c_str(), e.what()));
200 }
201 }
202 return counter;
203};
204
205std::string_view get_pcsaft_fluids_schema() {
206 return pcsaft_fluids_schema_JSON;
207}
208
209std::string PCSAFTLibraryClass::get_binary_interaction_pcsaft(const std::string& CAS1, const std::string& CAS2, const std::string& key) {
210 // Find pair
211 std::vector<std::string> CAS;
212 CAS.push_back(CAS1);
213 CAS.push_back(CAS2);
214
215 std::vector<std::string> CASrev;
216 CASrev.push_back(CAS2);
217 CASrev.push_back(CAS1);
218
219 if (m_binary_pair_map.find(CAS) != m_binary_pair_map.end()) {
220 std::vector<Dictionary>& v = m_binary_pair_map[CAS];
221 try {
222 if (key == "name1") {
223 return v[0].get_string("name1");
224 } else if (key == "name2") {
225 return v[0].get_string("name2");
226 } else if (key == "BibTeX") {
227 return v[0].get_string("BibTeX");
228 } else if (key == "kij") {
229 return format("%0.16g", v[0].get_double("kij"));
230 } else if (key == "kijT") {
231 try {
232 return format("%0.16g", v[0].get_double("kijT"));
233 } catch (const ValueError&) {
234 return format("%0.16g", 0.0);
235 }
236 } else {
237 }
238 } catch (...) { // NOLINT(bugprone-empty-catch)
239 // Dictionary lookup threw; fall through to the uniform
240 // "could not match the parameter" ValueError below.
241 }
242 throw ValueError(format("Could not match the parameter [%s] for the binary pair [%s,%s] - for now this is an error.", key.c_str(),
243 CAS1.c_str(), CAS2.c_str()));
244 } else if (m_binary_pair_map.find(CASrev) != m_binary_pair_map.end()) {
245 std::vector<Dictionary>& v = m_binary_pair_map[CASrev];
246 try {
247 if (key == "name1") {
248 return v[0].get_string("name1");
249 } else if (key == "name2") {
250 return v[0].get_string("name2");
251 } else if (key == "BibTeX") {
252 return v[0].get_string("BibTeX");
253 } else if (key == "kij") {
254 return format("%0.16g", v[0].get_double("kij"));
255 } else if (key == "kijT") {
256 try {
257 return format("%0.16g", v[0].get_double("kijT"));
258 } catch (const ValueError&) {
259 return format("%0.16g", 0.0);
260 }
261 } else {
262 }
263 } catch (...) { // NOLINT(bugprone-empty-catch)
264 // Dictionary lookup threw; fall through to the uniform
265 // "could not match the parameter" ValueError below.
266 }
267 throw ValueError(format("Could not match the parameter [%s] for the binary pair [%s,%s] - for now this is an error.", key.c_str(),
268 CAS1.c_str(), CAS2.c_str()));
269 } else {
270 // Sort, see if other order works properly
271 std::sort(CAS.begin(), CAS.end());
272 if (m_binary_pair_map.find(CAS) != m_binary_pair_map.end()) {
273 throw ValueError(format("Could not match the binary pair [%s,%s] - order of CAS numbers is backwards; found the swapped CAS numbers.",
274 CAS1.c_str(), CAS2.c_str()));
275 } else {
276 throw ValueError(format("Could not match the binary pair [%s,%s] - for now this is an error.", CAS1.c_str(), CAS2.c_str()));
277 }
278 }
279}
280
281void PCSAFTLibraryClass::set_binary_interaction_pcsaft(const std::string& CAS1, const std::string& CAS2, const std::string& key, const double value) {
282 // Find pair
283 std::vector<std::string> CAS;
284 CAS.push_back(CAS1);
285 CAS.push_back(CAS2);
286
287 std::vector<std::string> CASrev;
288 CASrev.push_back(CAS2);
289 CASrev.push_back(CAS1);
290
291 if (m_binary_pair_map.find(CAS) != m_binary_pair_map.end()) {
292 if (get_config_bool(OVERWRITE_BINARY_INTERACTION)) {
293 std::vector<Dictionary>& v = m_binary_pair_map[CAS];
294 if (v[0].has_number(key)) {
295 v[0].add_number(key, value);
296 } else {
297 throw ValueError(format("Could not set the parameter [%s] for the binary pair [%s,%s] - for now this is an error", key.c_str(),
298 CAS1.c_str(), CAS2.c_str()));
299 }
300 } else {
301 throw ValueError(
302 format("CAS pair(%s,%s) already in binary interaction map; considering enabling configuration key OVERWRITE_BINARY_INTERACTION",
303 CAS1.c_str(), CAS2.c_str()));
304 }
305 } else if (m_binary_pair_map.find(CASrev) != m_binary_pair_map.end()) {
306 if (get_config_bool(OVERWRITE_BINARY_INTERACTION)) {
307 std::vector<Dictionary>& v = m_binary_pair_map[CASrev];
308 if (v[0].has_number(key)) {
309 v[0].add_number(key, value);
310 } else {
311 throw ValueError(format("Could not set the parameter [%s] for the binary pair [%s,%s] - for now this is an error", key.c_str(),
312 CAS1.c_str(), CAS2.c_str()));
313 }
314 } else {
315 throw ValueError(
316 format("CAS pair(%s,%s) already in binary interaction map; considering enabling configuration key OVERWRITE_BINARY_INTERACTION",
317 CAS1.c_str(), CAS2.c_str()));
318 }
319 } else {
320 Dictionary dict;
321 std::vector<std::string> CAS;
322 CAS.push_back(CAS1);
323 CAS.push_back(CAS2);
324 dict.add_number(key, value);
325
326 m_binary_pair_map.insert(std::pair<std::vector<std::string>, std::vector<Dictionary>>(CAS, std::vector<Dictionary>(1, dict)));
327 }
328}
329
330void PCSAFTLibraryClass::load_from_JSON(const nlohmann::json& doc) {
331 for (const auto& el : doc) {
332 // Get the empty dictionary to be filled by the appropriate interaction parameter
333 Dictionary dict;
334
335 // Get the vector of CAS numbers
336 std::vector<std::string> CAS;
337 CAS.push_back(cpjson::get_string(el, "CAS1"));
338 CAS.push_back(cpjson::get_string(el, "CAS2"));
339 std::string name1 = cpjson::get_string(el, "Name1");
340 std::string name2 = cpjson::get_string(el, "Name2");
341
342 // Sort the CAS number vector
343 std::sort(CAS.begin(), CAS.end());
344
345 // A sort was carried out, names/CAS were swapped
346 bool swapped = CAS[0].compare(cpjson::get_string(el, "CAS1")) != 0;
347
348 if (swapped) {
349 std::swap(name1, name2);
350 }
351
352 // Populate the dictionary with common terms
353 dict.add_string("name1", name1);
354 dict.add_string("name2", name2);
355 dict.add_string("BibTeX", cpjson::get_string(el, "BibTeX"));
356 if (el.contains("kij")) {
357 dict.add_number("kij", cpjson::get_double(el, "kij"));
358 } else {
359 std::cout << "Loading error: binary pair of " << name1 << " & " << name2 << "does not provide kij" << '\n';
360 }
361 if (el.contains("kijT")) {
362 dict.add_number("kijT", cpjson::get_double(el, "kijT"));
363 }
364
365 auto it = m_binary_pair_map.find(CAS);
366 if (it == m_binary_pair_map.end()) {
367 // Add to binary pair map by creating one-element vector
368 m_binary_pair_map.insert(std::pair<std::vector<std::string>, std::vector<Dictionary>>(CAS, std::vector<Dictionary>(1, dict)));
369 } else {
370 if (get_config_bool(OVERWRITE_BINARY_INTERACTION)) {
371 // Already there, see http://www.cplusplus.com/reference/map/map/insert/, so we are going to pop it and overwrite it
372 m_binary_pair_map.erase(it);
373 std::pair<std::map<std::vector<std::string>, std::vector<Dictionary>>::iterator, bool> ret;
374 ret = m_binary_pair_map.insert(std::pair<std::vector<std::string>, std::vector<Dictionary>>(CAS, std::vector<Dictionary>(1, dict)));
375 assert(ret.second == true);
376 } else {
377 // Error if already in map!
378 throw ValueError(
379 format("CAS pair(%s,%s) already in binary interaction map; considering enabling configuration key OVERWRITE_BINARY_INTERACTION",
380 CAS[0].c_str(), CAS[1].c_str()));
381 }
382 }
383 }
384}
385
386void PCSAFTLibraryClass::load_from_string(const std::string_view& str) {
387 nlohmann::json doc = cpjson::parse(str);
388 load_from_JSON(doc);
389}
390
391} // namespace PCSAFTLibrary
392} // namespace CoolProp