diff --git a/include/CPstrings.h b/include/CPstrings.h index 939ce151..2bdc6b4a 100644 --- a/include/CPstrings.h +++ b/include/CPstrings.h @@ -17,6 +17,8 @@ #include #endif + #include "Exceptions.h" + #if !defined(__powerpc__) /// Copy string to wstring /// Dangerous if the string has non-ASCII characters; from http://stackoverflow.com/a/8969776/1360263 @@ -76,4 +78,32 @@ std::string strjoin(const std::vector &strings, const std::string &delim); + /// A convenience function that return true if a string begins with the given other string + inline bool strstartswith(const std::string &s, const std::string &other){ return s.find(other) == 0; }; + + /** + * @brief Convert a number encoded as a string to a double + * @param s The string to be converted + * + * @note + */ + inline double string2double(const std::string &s){ + std::string mys = s; //copy + // replace D with e (FORTRAN style scientific definition) + if (mys.find("D") != std::string::npos){ std::size_t pos = mys.find("D"), len = 1; mys.replace(pos,len,"e"); } + // replace d with e (FORTRAN style scientific definition) + if (mys.find("d") != std::string::npos){ std::size_t pos = mys.find("d"), len = 1; mys.replace(pos,len,"e"); } + + const char * cs = mys.c_str(); + char* pEnd; + double val = strtod(cs, &pEnd); + if ((pEnd - &(cs[0])) != s.size() ){ + // Found a character that is not able to be converted to number + throw CoolProp::ValueError(format("Unable to convert this string to a number:%s",cs)); + } + else{ + return val; + } + } + #endif diff --git a/include/Configuration.h b/include/Configuration.h index 45f1d675..73ad1057 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -36,7 +36,9 @@ X(R_U_CODATA, "R_U_CODATA", 8.3144598, "The value for the ideal gas constant in J/mol/K according to CODATA 2014. This value is used to harmonize all the ideal gas constants. This is especially important in the critical region.") \ X(VTPR_UNIFAQ_PATH, "VTPR_UNIFAQ_PATH", "", "The path to the directory containing the UNIFAQ JSON files. Should be slash terminated") \ X(SPINODAL_MINIMUM_DELTA, "SPINODAL_MINIMUM_DELTA", 0.5, "The minimal delta to be used in tracing out the spinodal; make sure that the EOS has a spinodal at this value of delta=rho/rho_r") \ - X(OVERWRITE_FLUIDS, "OVERWRITE_FLUIDS", false, "If true, and a fluid is added to the fluids library that is already there, rather than not adding the fluid (and probably throwing an exception), overwrite it") + X(OVERWRITE_FLUIDS, "OVERWRITE_FLUIDS", false, "If true, and a fluid is added to the fluids library that is already there, rather than not adding the fluid (and probably throwing an exception), overwrite it") \ + X(OVERWRITE_DEPARTURE_FUNCTION, "OVERWRITE_DEPARTURE_FUNCTION", false, "If true, and a departure function to be added is already there, rather than not adding the departure function (and probably throwing an exception), overwrite it") \ + X(OVERWRITE_BINARY_INTERACTION, "OVERWRITE_BINARY_INTERACTION", false, "If true, and a pair of binary interaction pairs to be added is already there, rather than not adding the binary interaction pair (and probably throwing an exception), overwrite it") // Use preprocessor to create the Enum diff --git a/include/CoolPropLib.h b/include/CoolPropLib.h index ea1361f5..e8e8ea48 100644 --- a/include/CoolPropLib.h +++ b/include/CoolPropLib.h @@ -139,6 +139,15 @@ * \note you can get the error message by doing something like get_global_param_string("errstring",output) */ EXPORT_CODE void CONVENTION set_config_double(const char * key, const double val); + /** + * @brief Set the departure functions in the departure function library from a string format + * @param string_data The departure functions to be set, either provided as a JSON-formatted string + * or as a string of the contents of a HMX.BNC file from REFPROP + * + * @note By default, if a departure function already exists in the library, this is an error, + * unless the configuration variable OVERWRITE_DEPARTURE_FUNCTIONS is set to true + */ + EXPORT_CODE void CONVENTION set_departure_functions(const char * string_data); /** * \overload * \sa \ref CoolProp::set_reference_stateS diff --git a/include/rapidjson_include.h b/include/rapidjson_include.h index 26e9b7ab..2e9e20c5 100644 --- a/include/rapidjson_include.h +++ b/include/rapidjson_include.h @@ -215,7 +215,7 @@ namespace cpjson return out; }; - /// A convenience function to get a double array compactly + /// A convenience function to get a string array compactly inline std::vector get_string_array(rapidjson::Value &v, std::string m) { if (!v.HasMember(m.c_str())){ throw CoolProp::ValueError(format("Does not have member [%s]",m.c_str())); } @@ -248,6 +248,19 @@ namespace cpjson } value.AddMember(rapidjson::Value(key, doc.GetAllocator()).Move(), _i, doc.GetAllocator()); }; + + /// A convenience function to set a string array compactly + inline void set_string_array(const char *key, const std::vector &vec, rapidjson::Value &value, rapidjson::Document &doc) + { + rapidjson::Value _v(rapidjson::kArrayType); + for (unsigned int i = 0; i < vec.size(); ++i) + { + _v.PushBack(rapidjson::Value(vec[i].c_str(), doc.GetAllocator()).Move(), doc.GetAllocator()); + } + value.AddMember(rapidjson::Value(key, doc.GetAllocator()).Move(), + _v, + doc.GetAllocator()); + }; /// A convenience function to set a double array compactly inline void set_double_array(const char *key, const std::vector &vec, rapidjson::Value &value, rapidjson::Document &doc) diff --git a/src/Backends/Helmholtz/MixtureParameters.cpp b/src/Backends/Helmholtz/MixtureParameters.cpp index 513a1a52..fbb63f1c 100644 --- a/src/Backends/Helmholtz/MixtureParameters.cpp +++ b/src/Backends/Helmholtz/MixtureParameters.cpp @@ -1,4 +1,5 @@ #include "MixtureParameters.h" +#include "CPstrings.h" #include "mixture_departure_functions_JSON.h" // Creates the variable mixture_departure_functions_JSON #include "mixture_binary_pairs_JSON.h" // Creates the variable mixture_binary_pairs_JSON #include "predefined_mixtures_JSON.h" // Makes a std::string variable called predefined_mixtures_JSON @@ -60,20 +61,39 @@ bool is_predefined_mixture(const std::string &name, Dictionary &dict){ * Each entry in the binary pair library includes reducing parameters as well as the name of the reducing function to be used and */ class MixtureBinaryPairLibrary{ -public: +private: /// Map from sorted pair of CAS numbers to reducing parameter map. The reducing parameter map is a map from key (string) to value (double) - std::map< std::vector, std::vector > binary_pair_map; - + std::map< std::vector, std::vector > m_binary_pair_map; +public: + std::map< std::vector, std::vector > & binary_pair_map(){ + // Set the default departure functions if none have been provided yet + if(m_binary_pair_map.size() == 0){ load_defaults(); } + return m_binary_pair_map; + }; + + void load_from_string(const std::string &str){ + rapidjson::Document doc; + doc.Parse<0>(str.c_str()); + if (doc.HasParseError()){ + std::cout << str << std::endl ; + throw ValueError("Unable to parse departure function string"); + } + load_from_JSON(doc); + } + + // Load the defaults that come from the JSON-encoded string compiled into library + // as the variable mixture_departure_functions_JSON + void load_defaults() + { + load_from_string(mixture_binary_pairs_JSON); + } + /** \brief Construct the binary pair library including all the binary pairs that are possible * * The data structure also includes space for a string that gives the pointer to the departure function to be used for this binary pair. */ - MixtureBinaryPairLibrary() + void load_from_JSON(rapidjson::Document &doc) { - rapidjson::Document doc; - - doc.Parse<0>(mixture_binary_pairs_JSON.c_str()); - if (doc.HasParseError()){throw ValueError();} // Iterate over the papers in the listing for (rapidjson::Value::ValueIterator itr = doc.Begin(); itr != doc.End(); ++itr) @@ -132,13 +152,13 @@ public: continue; } - if (binary_pair_map.find(CAS) == binary_pair_map.end()){ + if (m_binary_pair_map.find(CAS) == m_binary_pair_map.end()){ // Add to binary pair map by creating one-element vector - binary_pair_map.insert(std::pair, std::vector >(CAS, std::vector(1, dict))); + m_binary_pair_map.insert(std::pair, std::vector >(CAS, std::vector(1, dict))); } else { - binary_pair_map[CAS].push_back(dict); + m_binary_pair_map[CAS].push_back(dict); } } } @@ -196,13 +216,23 @@ public: throw ValueError(format("Your simple mixing rule [%s] was not understood", rule.c_str())); } - if (binary_pair_map.find(CAS) == binary_pair_map.end()){ + std::map, std::vector >::iterator it = m_binary_pair_map.find(CAS); + if (it == m_binary_pair_map.end()){ // Add to binary pair map by creating one-element vector - binary_pair_map.insert(std::pair, std::vector >(CAS, std::vector(1, dict))); + m_binary_pair_map.insert(std::pair, std::vector >(CAS, std::vector(1, dict))); } else { - binary_pair_map[CAS].push_back(dict); + if (!get_config_bool(OVERWRITE_BINARY_INTERACTION)){ + // Already there, see http://www.cplusplus.com/reference/map/map/insert/ + m_binary_pair_map.erase(it); + std::pair, std::vector >::iterator, bool> ret; + ret = m_binary_pair_map.insert(std::pair, std::vector >(CAS, std::vector(1, dict))); + assert(ret.second == true); + } + else{ + m_binary_pair_map[CAS].push_back(dict); + } } } }; @@ -218,8 +248,9 @@ void apply_simple_mixing_rule(const std::string &identifier1, const std::string std::string get_csv_mixture_binary_pairs() { + std::vector out; - for (std::map< std::vector, std::vector >::const_iterator it = mixturebinarypairlibrary.binary_pair_map.begin(); it != mixturebinarypairlibrary.binary_pair_map.end(); ++it) + for (std::map< std::vector, std::vector >::const_iterator it = mixturebinarypairlibrary.binary_pair_map().begin(); it != mixturebinarypairlibrary.binary_pair_map().end(); ++it) { out.push_back(strjoin(it->first, "&")); } @@ -233,8 +264,8 @@ std::string get_mixture_binary_pair_data(const std::string &CAS1, const std::str CAS.push_back(CAS1); CAS.push_back(CAS2); - if (mixturebinarypairlibrary.binary_pair_map.find(CAS) != mixturebinarypairlibrary.binary_pair_map.end()){ - std::vector &v = mixturebinarypairlibrary.binary_pair_map[CAS]; + if (mixturebinarypairlibrary.binary_pair_map().find(CAS) != mixturebinarypairlibrary.binary_pair_map().end()){ + std::vector &v = mixturebinarypairlibrary.binary_pair_map()[CAS]; try{ if (key == "name1"){ return v[0].get_string("name1"); } else if (key == "name2"){ return v[0].get_string("name2"); } @@ -256,7 +287,7 @@ std::string get_mixture_binary_pair_data(const std::string &CAS1, const std::str else{ // Sort, see if other order works properly std::sort(CAS.begin(), CAS.end()); - if (mixturebinarypairlibrary.binary_pair_map.find(CAS) != mixturebinarypairlibrary.binary_pair_map.end()) + if (mixturebinarypairlibrary.binary_pair_map().find(CAS) != mixturebinarypairlibrary.binary_pair_map().end()) { throw ValueError(format("Could not match the binary pair [%s,%s] - order of CAS numbers is backwards; found the swapped CAS numbers.",CAS1.c_str(), CAS2.c_str())); } @@ -267,13 +298,14 @@ std::string get_mixture_binary_pair_data(const std::string &CAS1, const std::str } void set_mixture_binary_pair_data(const std::string &CAS1, const std::string &CAS2, const std::string &key, const double value) { + // Find pair std::vector CAS; CAS.push_back(CAS1); CAS.push_back(CAS2); - if (mixturebinarypairlibrary.binary_pair_map.find(CAS) != mixturebinarypairlibrary.binary_pair_map.end()){ - std::vector &v = mixturebinarypairlibrary.binary_pair_map[CAS]; + if (mixturebinarypairlibrary.binary_pair_map().find(CAS) != mixturebinarypairlibrary.binary_pair_map().end()){ + std::vector &v = mixturebinarypairlibrary.binary_pair_map()[CAS]; if (v[0].has_number(key)){ v[0].add_number(key, value); } @@ -285,7 +317,7 @@ void set_mixture_binary_pair_data(const std::string &CAS1, const std::string &CA else{ // Sort, see if other order works properly std::sort(CAS.begin(), CAS.end()); - if (mixturebinarypairlibrary.binary_pair_map.find(CAS) != mixturebinarypairlibrary.binary_pair_map.end()) + if (mixturebinarypairlibrary.binary_pair_map().find(CAS) != mixturebinarypairlibrary.binary_pair_map().end()) { throw ValueError(format("Could not match the binary pair [%s,%s] - order of CAS numbers is backwards; found the swapped CAS numbers.",CAS1.c_str(), CAS2.c_str())); } @@ -297,6 +329,7 @@ void set_mixture_binary_pair_data(const std::string &CAS1, const std::string &CA std::string get_reducing_function_name(const std::string &CAS1, const std::string &CAS2) { + std::vector CAS; CAS.push_back(CAS1); CAS.push_back(CAS2); @@ -304,8 +337,8 @@ std::string get_reducing_function_name(const std::string &CAS1, const std::strin // Sort the CAS number vector - map is based on sorted CAS codes std::sort(CAS.begin(), CAS.end()); - if (mixturebinarypairlibrary.binary_pair_map.find(CAS) != mixturebinarypairlibrary.binary_pair_map.end()){ - return mixturebinarypairlibrary.binary_pair_map[CAS][0].get_string("function"); + if (mixturebinarypairlibrary.binary_pair_map().find(CAS) != mixturebinarypairlibrary.binary_pair_map().end()){ + return mixturebinarypairlibrary.binary_pair_map()[CAS][0].get_string("function"); } else{ throw ValueError(format("Could not match the binary pair [%s,%s] - for now this is an error.",CAS1.c_str(), CAS2.c_str())); @@ -315,21 +348,28 @@ std::string get_reducing_function_name(const std::string &CAS1, const std::strin /** \brief A container for the departure functions for CoolProp mixtures */ class MixtureDepartureFunctionsLibrary{ -public: +private: /// Map from sorted pair of CAS numbers to departure term dictionary. - std::map departure_function_map; - - MixtureDepartureFunctionsLibrary() - { + std::map m_departure_function_map; +public: + + std::map & departure_function_map(){ + // Set the default departure functions if none have been provided yet + if(m_departure_function_map.size() == 0){ load_defaults(); } + return m_departure_function_map; + }; + + void load_from_string(const std::string &str){ rapidjson::Document doc; - - // Load the JSON data for the departure functions - doc.Parse<0>(mixture_departure_functions_JSON.c_str()); + doc.Parse<0>(str.c_str()); if (doc.HasParseError()){ - std::cout << mixture_departure_functions_JSON << std::endl ; - throw ValueError("Unable to parse mixture_departure_functions_JSON.h"); + std::cout << str << std::endl ; + throw ValueError("Unable to parse departure function string"); } - + load_from_JSON(doc); + } + void load_from_JSON(rapidjson::Document &doc) + { // Iterate over the departure functions in the listing for (rapidjson::Value::ValueIterator itr = doc.Begin(); itr != doc.End(); ++itr) { @@ -367,30 +407,47 @@ public: } // Check if this name is already in use - if (departure_function_map.find(Name) == departure_function_map.end()) + std::map::iterator it = m_departure_function_map.find(Name); + if (it == m_departure_function_map.end()) { // Not in map, add new entry to map with dictionary as value and Name as key - departure_function_map.insert(std::pair(Name, dict)); + m_departure_function_map.insert(std::pair(Name, dict)); } else { - // Error if already in map! - // - // Collect all the current names for departure functions for a nicer error message - std::vector names; - for (std::map::const_iterator it = departure_function_map.begin(); it != departure_function_map.end(); ++it) - { - names.push_back(it->first); + if (!get_config_bool(OVERWRITE_DEPARTURE_FUNCTION)){ + // Already there, see http://www.cplusplus.com/reference/map/map/insert/ + m_departure_function_map.erase(it); + std::pair::iterator, bool> ret; + ret = m_departure_function_map.insert(std::pair(Name, dict)); + assert(ret.second == true); + } + else{ + // Error if already in map! + // + // Collect all the current names for departure functions for a nicer error message + std::vector names; + for (std::map::const_iterator it = m_departure_function_map.begin(); it != m_departure_function_map.end(); ++it) + { + names.push_back(it->first); + } + throw ValueError(format("Name of departure function [%s] is already loaded. Current departure function names are: %s", Name.c_str(), strjoin(names,",").c_str() )); } - throw ValueError(format("Name of departure function [%s] is already loaded. Current departure function names are: %s", Name.c_str(), strjoin(names,",").c_str() )); } } } + // Load the defaults that come from the JSON-encoded string compiled into library + // as the variable mixture_departure_functions_JSON + void load_defaults() + { + load_from_string(mixture_departure_functions_JSON); + } }; static MixtureDepartureFunctionsLibrary mixturedeparturefunctionslibrary; void MixtureParameters::set_mixture_parameters(HelmholtzEOSMixtureBackend &HEOS) { + std::vector components = HEOS.get_components(); std::size_t N = components.size(); @@ -422,13 +479,13 @@ void MixtureParameters::set_mixture_parameters(HelmholtzEOSMixtureBackend &HEOS) // Reducing parameters for binary pair // *************************************************** - if (mixturebinarypairlibrary.binary_pair_map.find(CAS) == mixturebinarypairlibrary.binary_pair_map.end()) + if (mixturebinarypairlibrary.binary_pair_map().find(CAS) == mixturebinarypairlibrary.binary_pair_map().end()) { throw ValueError(format("Could not match the binary pair [%s,%s] - for now this is an error.", CAS[0].c_str(), CAS[1].c_str())); } // Get a reference to the first matching binary pair in the dictionary - Dictionary &dict_red = mixturebinarypairlibrary.binary_pair_map[CAS][0]; + Dictionary &dict_red = mixturebinarypairlibrary.binary_pair_map()[CAS][0]; // Get the name of the type being used, one of GERG-2008, Lemmon-xi-zeta, etc. std::string type_red = dict_red.get_string("type"); @@ -478,7 +535,7 @@ void MixtureParameters::set_mixture_parameters(HelmholtzEOSMixtureBackend &HEOS) std::string Name = CoolProp::get_reducing_function_name(components[i].CAS, components[j].CAS); // Get the dictionary itself - Dictionary &dict_dep = mixturedeparturefunctionslibrary.departure_function_map[Name]; + Dictionary &dict_dep = mixturedeparturefunctionslibrary.departure_function_map()[Name]; if (dict_dep.is_empty()){throw ValueError(format("Departure function name [%s] seems to be invalid",Name.c_str()));} @@ -514,5 +571,211 @@ void MixtureParameters::set_mixture_parameters(HelmholtzEOSMixtureBackend &HEOS) // We have obtained all the parameters needed for the reducing function, now set the reducing function for the mixture HEOS.Reducing = shared_ptr(new GERG2008ReducingFunction(components, beta_v, gamma_v, beta_T, gamma_T)); } + +void parse_HMX_BNC(const std::string &s, std::vector &BIP, std::vector &functions) +{ + // Capture the betas, gammas, Fij, models + bool block_started = false, block_ended = false; + std::size_t i_started = 0, i_ended = 0, i = 0; + std::vector lines = strsplit(s, '\n'); + for(std::vector::iterator it = lines.begin(); it != lines.end(); ++it){ + if (strstartswith(strstrip(*it), "#BNC")){ + block_started = true; + i_started = i+1; + } + if (block_started && strstrip(*it).empty()){ + block_ended = true; + i_ended = i-1; + break; + } + i++; + } + i = 0; + // Find the first line with a ! + for (i = i_started; i < i_ended; ++i){ + if (strstrip(lines[i]) == "!" ){ i_started = i; break; } + } + // Find all the lines that '!' - these are delimiters + std::vector > bounds; // The left and right indices (inclusive) that form a binary pair + std::size_t last_excalamation = i_started; + for (i = i_started; i <= i_ended; ++i){ + if (strstrip(lines[i]) == "!" ){ + bounds.push_back(std::make_pair(last_excalamation+1, i-1)); + last_excalamation = i; + } + } + // Parse each chunk + std::vector chunks; + for (std::vector >::iterator it = bounds.begin(); it != bounds.end(); ++it){ + REFPROP_binary_element bnc; + for (std::size_t i = it->first; i <= it->second; ++i){ + // Store comments + if (strstartswith(lines[i], "?")){ bnc.comments.push_back(lines[i]); continue; } + // Parse the line with the thermo BIP + if (lines[i].find("/") > 0){ + // Split at ' ' + std::vector bits = strsplit(strstrip(lines[i]), ' '); + // Remove empty elements + for (std::size_t j = bits.size()-1; j > 0; --j){ if (bits[j].empty()){ bits.erase(bits.begin() + j); } } + // Get the line that contains the thermo BIP + if (bits[0].find("/") > 0 && bits[1].size()==3){ + std::vector theCAS = strsplit(bits[0], '/'); + bnc.CAS1 = theCAS[0]; bnc.CAS2 = theCAS[1]; + bnc.model = bits[1]; + bnc.betaT = string2double(bits[2]); + bnc.gammaT = string2double(bits[3]); + bnc.betaV = string2double(bits[4]); + bnc.gammaV = string2double(bits[5]); + bnc.Fij = string2double(bits[6]); + break; + } + else if (strstrip(bits[0]) == "CAS#"){ + break; + } + else{ + throw CoolProp::ValueError(format("Unable to parse binary interaction line: %s", lines[i])); + } + } + } + if (!bnc.CAS1.empty()){ + BIP.push_back(bnc); + } + } + + // **************************************** + // Parse the departure functions + // **************************************** + for (std::size_t i = i_ended+1; i < lines.size(); ++i){ + std::size_t j_end = i; + // Find the end of this block + for (j_end = i+1; j_end < lines.size(); ++j_end){ if (strstrip(lines[j_end]).empty()) { j_end -= 1; break; } } + + if (strstartswith(lines[i], "#MXM")){ + REFPROP_departure_function dep; + dep.Npower = -1; + dep.model = std::string(lines[i+1].begin(), lines[i+1].begin()+3); + dep.comments.push_back(lines[i+1]); + for (std::size_t j = i+2; j <= j_end; ++j){ + if (strstartswith(strstrip(lines[j]), "?")){ + dep.comments.push_back(lines[j]); continue; + } + if (strstartswith(strstrip(lines[j]), "!")){ + j += 2; // Skip the BIP here, not used + continue; + } + std::vector bits = strsplit(lines[j], ' '); + // Remove empty elements + for (std::size_t k = bits.size()-1; k > 0; --k){ if (bits[k].empty()){ bits.erase(bits.begin() + k); } } + + if (dep.Npower < 0){ // Not extracted yet, let's do it now + // Extract the number of terms + dep.Npower = strtol(bits[0].c_str(), NULL, 10); + dep.Nterms_power = strtol(bits[1].c_str(), NULL, 10); + dep.Nspecial = strtol(bits[3].c_str(), NULL, 10); + dep.Nterms_special = strtol(bits[4].c_str(), NULL, 10); + } + else{ + dep.a.push_back(string2double(bits[0])); + dep.t.push_back(string2double(bits[1])); + dep.d.push_back(string2double(bits[2])); + // Extracting "polynomial" terms + if (dep.Nterms_power == 4){ + dep.e.push_back(string2double(bits[3])); + } + if (dep.Nspecial > 0){ + if (dep.a.size()-1 < dep.Npower){ + dep.eta.push_back(0); + dep.epsilon.push_back(0); + dep.beta.push_back(0); + dep.gamma.push_back(0); + } + else{ + // Extracting "special" terms + dep.eta.push_back(string2double(bits[3])); + dep.epsilon.push_back(string2double(bits[4])); + dep.beta.push_back(string2double(bits[5])); + dep.gamma.push_back(string2double(bits[6])); + } + } + } + } + functions.push_back(dep); + } + } +} + +void set_departure_functions(const std::string &string_data) +{ + if (string_data.find("#MXM") > 0 ){ + // REFPROP HMX.BNC file was provided + std::vector BIP; + std::vector functions; + parse_HMX_BNC(string_data, BIP, functions); + + { + rapidjson::Document doc; doc.SetArray(); + for(std::vector::const_iterator it = BIP.begin(); it < BIP.end(); ++it){ + rapidjson::Value el; el.SetObject(); + el.AddMember("CAS1", rapidjson::Value(it->CAS1.c_str(), doc.GetAllocator()).Move(), doc.GetAllocator()); + el.AddMember("CAS2", rapidjson::Value(it->CAS2.c_str(), doc.GetAllocator()).Move(), doc.GetAllocator()); + el.AddMember("Name1", "??", doc.GetAllocator()); + el.AddMember("Name2", "??", doc.GetAllocator()); + el.AddMember("betaT", it->betaT, doc.GetAllocator()); + el.AddMember("gammaT", it->gammaT, doc.GetAllocator()); + el.AddMember("betaV", it->betaV, doc.GetAllocator()); + el.AddMember("gammaV", it->gammaV, doc.GetAllocator()); + el.AddMember("F", it->Fij, doc.GetAllocator()); + el.AddMember("function", rapidjson::Value(it->model.c_str(), doc.GetAllocator()).Move() , doc.GetAllocator()); + std::string tex_string = "(from HMX.BNC format)::" + strjoin(it->comments, "\n"); + el.AddMember("BibTeX", rapidjson::Value(tex_string.c_str(), doc.GetAllocator()).Move(), doc.GetAllocator()); + doc.PushBack(el, doc.GetAllocator()); + } + mixturebinarypairlibrary.load_from_JSON(doc); + } + { + rapidjson::Document doc; doc.SetArray(); + for(std::vector::const_iterator it = functions.begin(); it < functions.end(); ++it){ + rapidjson::Value el; el.SetObject(); + el.AddMember("Name", rapidjson::Value(it->model.c_str(), doc.GetAllocator()).Move(), doc.GetAllocator()); + std::vector aliases; + cpjson::set_string_array("aliases", aliases, el, doc); + cpjson::set_double_array("n", it->a, el, doc); + cpjson::set_double_array("d", it->d, el, doc); + cpjson::set_double_array("t", it->t, el, doc); + if (it->Nterms_special > 0 || it->Nterms_power == 3){ + el.AddMember("type", "GERG-2008", doc.GetAllocator()); + el.AddMember("Npower", it->Npower, doc.GetAllocator()); + if (it->Nterms_power == 3 && it->Nspecial == 0){ + std::vector zeros(it->a.size(), 0); + cpjson::set_double_array("eta", zeros, el, doc); + cpjson::set_double_array("epsilon", zeros, el, doc); + cpjson::set_double_array("beta", zeros, el, doc); + cpjson::set_double_array("gamma", zeros, el, doc); + } + else{ + cpjson::set_double_array("eta", it->eta, el, doc); + cpjson::set_double_array("epsilon", it->epsilon, el, doc); + cpjson::set_double_array("beta", it->beta, el, doc); + cpjson::set_double_array("gamma", it->gamma, el, doc); + } + } + else{ + el.AddMember("type", "Exponential", doc.GetAllocator()); + cpjson::set_double_array("l", it->e, el, doc); + } + + std::string tex_string = "(from HMX.BNC format)::" + strjoin(it->comments, "\n"); + el.AddMember("BibTeX", rapidjson::Value(tex_string.c_str(), doc.GetAllocator()).Move(), doc.GetAllocator()); + doc.PushBack(el, doc.GetAllocator()); + } + mixturedeparturefunctionslibrary.load_from_JSON(doc); + } + } + else{ + // JSON-encoded string for departure functions + mixturedeparturefunctionslibrary.load_from_string(string_data); + } +} + } /* namespace CoolProp */ \ No newline at end of file diff --git a/src/Backends/Helmholtz/MixtureParameters.h b/src/Backends/Helmholtz/MixtureParameters.h index 79139b54..4bb261d9 100644 --- a/src/Backends/Helmholtz/MixtureParameters.h +++ b/src/Backends/Helmholtz/MixtureParameters.h @@ -51,5 +51,29 @@ public: static void set_mixture_parameters(HelmholtzEOSMixtureBackend &HEOS); }; +/// A Data structure for holding BIP coming from REFPROP +struct REFPROP_binary_element{ + std::string CAS1, CAS2, model; + double betaT, gammaT, betaV, gammaV, Fij; + std::vector comments; +}; +/// A data structure for holding departure functions coming from REFPROP +struct REFPROP_departure_function{ + short Npower, Nspecial, Nterms_power, Nterms_special; + std::string model; + std::vector a, t, d, e, eta, epsilon, beta, gamma; + std::vector comments; +}; + +/** + * @brief Set the departure functions in the departure function library from a string format + * @param string_data The departure functions to be set, either provided as a JSON-formatted string + * or as a string of the contents of a HMX.BNC file from REFPROP + * + * @note By default, if a departure function already exists in the library, this is an error, + * unless the configuration variable OVERWRITE_DEPARTURE_FUNCTIONS is set to true + */ +void set_departure_functions(const std::string &string_data); + } /* namespace CoolProp */ #endif \ No newline at end of file diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 8711a14d..3cfd9ddc 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -138,24 +138,3 @@ void set_config_as_json_string(const std::string &s){ } } - -#if defined(PYBIND11) - -#include -namespace py = pybind11; - -// Add Configuration variables to pybind11 wrapper -void init_CoolProp_Configuration_constants(py::module &m) -{ - // See http://stackoverflow.com/a/148610 and http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c#202511 - - py::enum_(m, "configuration_keys") - #define X(Enum, String, Default, Desc) \ - .value(String, configuration_keys::Enum) - CONFIGURATION_KEYS_ENUM - #undef X - .export_values(); -} - -#endif - diff --git a/src/CoolPropLib.cpp b/src/CoolPropLib.cpp index eb36417b..153eefd9 100644 --- a/src/CoolPropLib.cpp +++ b/src/CoolPropLib.cpp @@ -16,6 +16,7 @@ #include "AbstractState.h" #include "Exceptions.h" #include "Configuration.h" +#include "Backends/Helmholtz/MixtureParameters.h" #include @@ -333,6 +334,13 @@ EXPORT_CODE void CONVENTION set_config_double(const char * key, const double val catch (std::exception &e) { CoolProp::set_error_string(e.what()); } catch (...) { CoolProp::set_error_string("Undefined error"); } } +EXPORT_CODE void CONVENTION set_departure_functions(const char * string_data) { + try { + CoolProp::set_departure_functions(string_data); + } + catch (std::exception &e) { CoolProp::set_error_string(e.what()); } + catch (...) { CoolProp::set_error_string("Undefined error"); } +} EXPORT_CODE double CONVENTION HAPropsSI(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char * Name3, double Prop3) { fpu_reset_guard guard; diff --git a/src/pybind11_interface.cxx b/src/pybind11_interface.cxx index 8f669c37..d5d3e756 100644 --- a/src/pybind11_interface.cxx +++ b/src/pybind11_interface.cxx @@ -15,8 +15,6 @@ CoolProp::AbstractState * factory(const std::string &backend, const std::string return CoolProp::AbstractState::factory(backend, fluid_names); } -void init_CoolProp_Configuration_constants(py::module &m); - void init_CoolProp(py::module &m){ using namespace CoolProp; @@ -57,6 +55,13 @@ void init_CoolProp(py::module &m){ .def_readwrite("conductivity_vap", &PhaseEnvelopeData::conductivity_vap) .def_readwrite("speed_sound_vap", &PhaseEnvelopeData::speed_sound_vap); + // See http://stackoverflow.com/a/148610 and http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c#202511 + py::enum_(m, "configuration_keys") + #define X(Enum, String, Default, Desc) .value(String, configuration_keys::Enum) + CONFIGURATION_KEYS_ENUM + #undef X + .export_values(); + py::enum_(m, "parameters") .value("igas_constant", parameters::igas_constant) .value("imolar_mass", parameters::imolar_mass) @@ -344,6 +349,7 @@ void init_CoolProp(py::module &m){ m.def("config_key_description", (std::string (*)(const std::string &))&config_key_description); m.def("set_config_string", &set_config_string); m.def("set_config_double", &set_config_double); + m.def("set_departure_functions", &set_departure_functions); m.def("set_config_bool", &set_config_bool); m.def("get_config_string", &get_config_string); m.def("get_config_double", &get_config_double); @@ -374,8 +380,7 @@ void init_CoolProp(py::module &m){ m.def("get_mixture_binary_pair_data", &get_mixture_binary_pair_data); m.def("set_mixture_binary_pair_data", &set_mixture_binary_pair_data); m.def("apply_simple_mixing_rule", &apply_simple_mixing_rule); - - init_CoolProp_Configuration_constants(m); + } #if defined(COOLPROP_PYBIND11_MODULE)