diff --git a/include/AbstractState.h b/include/AbstractState.h index dc9d860d..6cb260a8 100644 --- a/include/AbstractState.h +++ b/include/AbstractState.h @@ -296,29 +296,45 @@ protected: public: - AbstractState(){}; + AbstractState(){clear();}; virtual ~AbstractState(){}; /// A factory function to return a pointer to a new-allocated instance of one of the backends. /** - Very Important!! : Use a smart pointer to manage the pointer returned. In older versions of C++, you can use std::tr1::smart_ptr. In C++2011 you can use std::shared_ptr - - Several backends are possible: - - 1. "?" : The backend is unknown, we will parse the fluid string to determine the backend to be used. Probably will use HEOS backend (see below) - 2. "HEOS" : The Helmholtz Equation of State backend for use with pure and pseudo-pure fluids, and mixtures, all of which are based on multi-parameter Helmholtz Energy equations of state. The fluid part of the string should then either be - 1. A pure or pseudo-pure fluid name (eg. "PROPANE" or "R410A"), yielding a HelmholtzEOSBackend instance. - 2. A string that encodes the components of the mixture with a "&" between them (e.g. "R32&R125"), yielding a HelmholtzEOSMixtureBackend instance. - - 3. "REFPROP" : The REFPROP backend will be used. The fluid part of the string should then either be - 1. A pure or pseudo-pure fluid name (eg. "PROPANE" or "R410A"), yielding a REFPROPBackend instance. - 2. A string that encodes the components of the mixture with a "&" between them (e.g. "R32&R125"), yielding a REFPROPMixtureBackend instance. - - 4. "TTSE&XXXX": The TTSE backend will be used, and the tables will be generated using the XXXX backend where XXXX is one of the base backends("HEOS", "REFPROP", etc. ) - 5. "BICUBIC&XXXX": The Bicubic backend will be used, and the tables will be generated using the XXXX backend where XXXX is one of the base backends("HEOS", "REFPROP", etc. ) - 6. "INCOMP": The incompressible backend will be used - */ - static AbstractState * factory(const std::string &backend, const std::string &fluid_string); + * @brief This is a convenience function to allow for the use of '&' delimited fluid names. Slightly less computationally efficient than the + * @param backend The backend in use, one of "HEOS", "REFPROP", etc. + * @param fluid_names Fluid names as a '&' delimited string + * @return + */ + static AbstractState * factory(const std::string &backend, const std::string &fluid_names) + { + return factory(backend, strsplit(fluid_names, '&')); + }; + + /** + * @brief A factory function to return a pointer to a new-allocated instance of one of the backends. + * @param backend The backend in use, "HEOS", "REFPROP", etc. + * @param fluid_names A vector of strings of the fluid names + * @return A pointer to the instance generated + * + * Several backends are possible: + * + * 1. "?" : The backend is unknown, we will parse the fluid string to determine the backend to be used. Probably will use HEOS backend (see below) + * 2. "HEOS" : The Helmholtz Equation of State backend for use with pure and pseudo-pure fluids, and mixtures, all of which are based on multi-parameter Helmholtz Energy equations of state. The fluid part of the string should then either be + * 1. A pure or pseudo-pure fluid name (eg. "PROPANE" or "R410A"), yielding a HelmholtzEOSBackend instance. + * 2. A string that encodes the components of the mixture with a "&" between them (e.g. "R32&R125"), yielding a HelmholtzEOSMixtureBackend instance. + * + * 3. "REFPROP" : The REFPROP backend will be used. The fluid part of the string should then either be + * 1. A pure or pseudo-pure fluid name (eg. "PROPANE" or "R410A"), yielding a REFPROPBackend instance. + * 2. A string that encodes the components of the mixture with a "&" between them (e.g. "R32&R125"), yielding a REFPROPMixtureBackend instance. + * + * 4. "INCOMP": The incompressible backend will be used + * 5. "TTSE&XXXX": The TTSE backend will be used, and the tables will be generated using the XXXX backend where XXXX is one of the base backends("HEOS", "REFPROP", etc. ) + * 6. "BICUBIC&XXXX": The Bicubic backend will be used, and the tables will be generated using the XXXX backend where XXXX is one of the base backends("HEOS", "REFPROP", etc. ) + * + * Very Important!! : Use a smart pointer to manage the pointer returned. In older versions of C++, you can use std::tr1::smart_ptr. In C++2011 you can use std::shared_ptr + */ + static AbstractState * factory(const std::string &backend, const std::vector &fluid_names); // The derived classes must implement this function to define whether they use mole fractions (true) or mass fractions (false) virtual bool using_mole_fractions(void) = 0; diff --git a/include/CoolProp.h b/include/CoolProp.h index 9bc4509f..bcc4ddae 100644 --- a/include/CoolProp.h +++ b/include/CoolProp.h @@ -33,38 +33,26 @@ You might want to start by looking at CoolProp.h /// @param Prop2 The second state variable value /// @param FluidName The fluid name double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName); - /// Return a value that depends on the thermodynamic state - /// @param Output The output parameter, one of "T","D","H",etc. - /// @param Name1 The first state variable name, one of "T","D","H",etc. - /// @param Prop1 The first state variable value - /// @param Name2 The second state variable name, one of "T","D","H",etc. - /// @param Prop2 The second state variable value - /// @param FluidName The fluid name - /// @param z The mole or mass fractions depending on the requirements of the backend - double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector &z); - /// Return a value that depends on the thermodynamic state - /// @param Output The output parameter, one of "T","D","H",etc. - /// @param Name1 The first state variable name, one of "T","D","H",etc. - /// @param Prop1 The first state variable value - /// @param Name2 The second state variable name, one of "T","D","H",etc. - /// @param Prop2 The second state variable value - /// @param FluidName The fluid name, or names seperated by '&' if a mixture - /// @param z The mole or mass fractions depending on the requirements of the backend - std::vector PropsSI(const std::string &Output, const std::string &Name1, const std::vector &Prop1, const std::string &Name2, const std::vector Prop2, const std::string &FluidName, const std::vector &z); - /// Return a value that depends on the thermodynamic state - /// @param Output The output parameter, one of "T","D","H",etc. - /// @param Name1 The first state variable name, one of "T","D","H",etc. - /// @param Prop1 The first state variable value - /// @param Name2 The second state variable name, one of "T","D","H",etc. - /// @param Prop2 The second state variable value - /// @param FluidName The fluid name - std::vector PropsSI(const std::string &Output, const std::string &Name1, const std::vector &Prop1, const std::string &Name2, const std::vector Prop2, const std::string &FluidName); /** - \overload - \sa PropsSI(std::string &Output, std::string &Name1, double Prop1, std::string &Name2, double Prop2, std::string &FluidName, const std::vector &x); - */ - double PropsSI(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const std::vector &x); + * @brief Get a matrix of outputs for a given input. Can handle both vector inputs as well as a vector of output strings + * @param Outputs A vector of strings for the output parameters + * @param Name1 The name of the first input variable + * @param Prop1 A vector of the first input values + * @param Name2 The name of the second input variable + * @param Prop2 A vector of the second input values + * @param backend The string representation of the backend (HEOS, REFPROP, INCOMP, etc.) + * @param fluids The fluid name(s) + * @param fractions The fractions (molar, mass, volume, etc.) of the components + */ + std::vector > PropsSImulti(const std::vector &Outputs, + const std::string &Name1, + const std::vector &Prop1, + const std::string &Name2, + const std::vector &Prop2, + const std::string &backend, + const std::vector &fluids, + const std::vector &fractions); /// Get the debug level /// @returns level The level of the verbosity for the debugging output (0-10) 0: no debgging output @@ -178,7 +166,23 @@ You might want to start by looking at CoolProp.h /// @param FluidName The fluid name /// @param z The mole or mass fractions depending on the requirements of the backend /// \note Returns empty string if there was an error; use get_global_param_string("errstring") to retrieve the error - std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector &z); + //std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector &z); + + /** + * @brief Extract the backend from a string - something like "HEOS::Water" would split to "HEOS" and "Water". If no backend is specified, the backend will be set to "?" + * @param fluid_string The input string + * @param backend The output backend, if none found, "?" + * @param fluid The output fluid string (minus the backend string) + */ + void extract_backend(const std::string &fluid_string, std::string &backend, std::string &fluid); + + /** + * @brief Extract fractions (molar, mass, etc.) encoded in the string if any + * @param fluid_string The input string + * @param fractions The fractions + * @return The fluids, as a '&' delimited string + */ + std::string extract_fractions(const std::string &fluid_string, std::vector &fractions); } /* namespace CoolProp */ #endif diff --git a/include/CoolPropLib.h b/include/CoolPropLib.h index 0a058d26..8000bf60 100644 --- a/include/CoolPropLib.h +++ b/include/CoolPropLib.h @@ -138,7 +138,7 @@ * * \note If there is an error, a huge value will be returned, you can get the error message by doing something like get_global_param_string("errstring",output) */ - EXPORT_CODE double CONVENTION PropsSIZ(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const double *z, int n); + //EXPORT_CODE double CONVENTION PropsSIZ(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const double *z, int n); /// Convert from degrees Fahrenheit to Kelvin (useful primarily for testing) EXPORT_CODE double CONVENTION F2K(double T_F); diff --git a/include/DataStructures.h b/include/DataStructures.h index 1aad7110..a84c32ea 100644 --- a/include/DataStructures.h +++ b/include/DataStructures.h @@ -39,7 +39,8 @@ struct SsatSimpleState : public SimpleState // // !! If you add a parameter, update the map in the corresponding CPP file !! enum parameters{ - + INVALID_PARAMETER = 0, + // General parameters igas_constant, imolar_mass, @@ -183,6 +184,7 @@ enum fluid_types{FLUID_TYPE_PURE, FLUID_TYPE_PSEUDOPURE, FLUID_TYPE_REFPROP, FLU // !! If you add a parameter, update the map in the corresponding CPP file !! /// These are input pairs that can be used (in each pair, input keys are sorted alphabetically) enum input_pairs{ + INPUT_PAIR_INVALID = 0, // Default (invalid) value QT_INPUTS, ///< Molar quality, Temperature in K PQ_INPUTS, ///< Pressure in Pa, Molar quality QSmolar_INPUTS, ///< Molar quality, Entropy in J/mol/K @@ -229,7 +231,20 @@ inline bool match_pair(parameters key1, parameters key2, parameters x1, paramete swap = !(key1 == x1); return ((key1 == x1 && key2 == x2) || (key2 == x1 && key1 == x2)); }; -template CoolProp::input_pairs generate_update_pair(parameters key1, T value1, parameters key2, T value2, T &out1, T &out2) +/** + * @brief Generate an update pair from key, value pairs + * + * If the input pair is valid, v1 and v2 will correspond to the returned output pair + * + * @param key1 The first input key + * @param value1 The first input value + * @param key2 The second input key + * @param value2 The second input value + * @param out1 The first output value + * @param out2 The second output value + * @return pair, or INPUT_PAIR_INVALID if not valid + */ +template CoolProp::input_pairs generate_update_pair(parameters key1, T value1, parameters key2, T value2, T &out1, T &out2) throw() { CoolProp::input_pairs pair; bool swap; @@ -321,8 +336,9 @@ template CoolProp::input_pairs generate_update_pair(parameters key1, T else if (match_pair(key1, key2, iSmolar, iUmolar, swap)){ pair = SmolarUmolar_INPUTS; ///< Entropy in J/mol/K, Internal energy in J/mol } - else - throw ValueError("Invalid set of inputs to generate_update_pair"); + else{ + pair = INPUT_PAIR_INVALID; return pair; + } if (!swap){ out1 = value1; out2 = value2; diff --git a/src/AbstractState.cpp b/src/AbstractState.cpp index 4ca7845e..09ec08fc 100644 --- a/src/AbstractState.cpp +++ b/src/AbstractState.cpp @@ -16,41 +16,37 @@ namespace CoolProp { -AbstractState * AbstractState::factory(const std::string &backend, const std::string &fluid_string) +AbstractState * AbstractState::factory(const std::string &backend, const std::vector &fluid_names) { static std::string HEOS_string = "HEOS"; if (!backend.compare("HEOS")) { - if (fluid_string.find('&') == std::string::npos){ - return new HelmholtzEOSBackend(fluid_string); + if (fluid_names.size() == 1){ + return new HelmholtzEOSBackend(fluid_names[0]); } else{ - // Split at the '&' - std::vector components = strsplit(fluid_string,'&'); - - return new HelmholtzEOSMixtureBackend(components); + return new HelmholtzEOSMixtureBackend(fluid_names); } } else if (!backend.compare("REFPROP")) { - if (fluid_string.find('&') == std::string::npos){ - return new REFPROPBackend(fluid_string); + if (fluid_names.size() == 1){ + return new REFPROPBackend(fluid_names[0]); } else{ - // Split at the '&' - std::vector components = strsplit(fluid_string,'&'); - - return new REFPROPMixtureBackend(components); + return new REFPROPMixtureBackend(fluid_names); } } else if (!backend.compare("INCOMP")) { - return new IncompressibleBackend(fluid_string); + if (fluid_names.size() != 1){throw ValueError(format("For INCOMP backend, name vector must be one element long"));} + return new IncompressibleBackend(fluid_names[0]); } else if (backend.find("TTSE&") == 0) { + if (fluid_names.size() != 1){throw ValueError(format("For backend [%s], name vector must be one element long", backend.c_str()));} // Will throw if there is a problem with this backend - shared_ptr AS(factory(backend.substr(5), fluid_string)); + shared_ptr AS(factory(backend.substr(5), fluid_names[0])); return new TTSEBackend(*AS.get()); } else if (!backend.compare("TREND")) @@ -59,19 +55,18 @@ AbstractState * AbstractState::factory(const std::string &backend, const std::st } else if (!backend.compare("?")) { - std::size_t idel = fluid_string.find("::"); + std::size_t idel = fluid_names[0].find("::"); // Backend has not been specified, and we have to figure out what the backend is by parsing the string if (idel == std::string::npos) // No '::' found, no backend specified, try HEOS, otherwise a failure { // Figure out what backend to use - return factory(HEOS_string, fluid_string); + return factory(HEOS_string, fluid_names); } else { // Split string at the '::' into two std::string, call again - return factory(std::string(fluid_string.begin(), fluid_string.begin() + idel), std::string(fluid_string.begin()+idel+2, fluid_string.end())); + return factory(std::string(fluid_names[0].begin(), fluid_names[0].begin() + idel), std::string(fluid_names[0].begin()+idel+2, fluid_names[0].end())); } - } else { diff --git a/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.cpp b/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.cpp index 6ee3689b..042fd421 100644 --- a/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.cpp +++ b/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.cpp @@ -35,7 +35,7 @@ static int deriv_counter = 0; namespace CoolProp { -HelmholtzEOSMixtureBackend::HelmholtzEOSMixtureBackend(std::vector &component_names, bool generate_SatL_and_SatV) { +HelmholtzEOSMixtureBackend::HelmholtzEOSMixtureBackend(const std::vector &component_names, bool generate_SatL_and_SatV) { std::vector components; components.resize(component_names.size()); diff --git a/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.h b/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.h index df4f9562..71c15ba7 100644 --- a/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.h +++ b/src/Backends/Helmholtz/HelmholtzEOSMixtureBackend.h @@ -36,7 +36,7 @@ public: HelmholtzEOSMixtureBackend(){ imposed_phase_index = iphase_not_imposed; _phase = iphase_unknown;}; HelmholtzEOSMixtureBackend(std::vector components, bool generate_SatL_and_SatV = true); - HelmholtzEOSMixtureBackend(std::vector &component_names, bool generate_SatL_and_SatV = true); + HelmholtzEOSMixtureBackend(const std::vector &component_names, bool generate_SatL_and_SatV = true); virtual ~HelmholtzEOSMixtureBackend(){}; shared_ptr Reducing; ExcessTerm Excess; diff --git a/src/Backends/Incompressible/IncompressibleBackend.cpp b/src/Backends/Incompressible/IncompressibleBackend.cpp index 4a44d3cd..338526c0 100644 --- a/src/Backends/Incompressible/IncompressibleBackend.cpp +++ b/src/Backends/Incompressible/IncompressibleBackend.cpp @@ -470,14 +470,12 @@ TEST_CASE("Internal consistency checks and example use cases for the incompressi CAPTURE(res); CHECK( check_abs(val,res,acc) ); } - - } SECTION("Tests for the full implementation using PropsSI") { // Prepare the results and compare them to the calculated values - std::string fluid("INCOMP::ExampleMelinder"); + std::string fluid("ExampleMelinder"); double acc = 0.0001; double T = -5 + 273.15; double p = 10e5; @@ -488,7 +486,10 @@ TEST_CASE("Internal consistency checks and example use cases for the incompressi // Compare different inputs // ... as vector expected = 9.6212e+02; - actual = CoolProp::PropsSI("D","T",T,"P",p,fluid,std::vector(1,x)); + std::vector fluid_Melinder(1,fluid); + std::vector > IO = CoolProp::PropsSImulti(std::vector(1,"D"),"T",std::vector(1,T),"P",std::vector(1,p),"INCOMP",fluid_Melinder,std::vector(1,x)); + REQUIRE(!IO.empty()); + actual = IO[0][0]; { CAPTURE(T); CAPTURE(p); @@ -498,7 +499,7 @@ TEST_CASE("Internal consistency checks and example use cases for the incompressi CHECK( check_abs(expected,actual,acc) ); } // ... as % - actual = CoolProp::PropsSI("D","T",T,"P",p,fluid+format("-%f%s",x*100.0,"%")); + actual = CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::"+fluid+format("-%f%%", x*100.0)); { CAPTURE(T); CAPTURE(p); @@ -510,98 +511,95 @@ TEST_CASE("Internal consistency checks and example use cases for the incompressi CHECK( check_abs(expected,actual,acc) ); } // ... as mass fraction - actual = CoolProp::PropsSI("D","T",T,"P",p,fluid+format("[%f]",x)); + actual = CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::"+fluid+format("[%f]",x)); { CAPTURE(T); CAPTURE(p); CAPTURE(x); CAPTURE(expected); CAPTURE(actual); - std::string name = fluid+format("[%f]",x); + std::string name = "INCOMP::"+fluid+format("[%f]",x); CAPTURE(name); std::string errmsg = CoolProp::get_global_param_string("errstring"); CAPTURE(errmsg); CHECK( check_abs(expected,actual,acc) ); } - - - fluid = std::string("INCOMP::ExampleSecCool"); - T = -5 + 273.15; - p = 10e5; - x = 0.4; + } + SECTION("SecCool example") + { + double acc = 0.0001; + std::string backend = "INCOMP"; + std::vector fluids(1,"ExampleSecCool"); + double T = -5 + 273.15; + double p = 10e5; + double x = 0.4; std::vector x_vec = std::vector(1,x); + std::vector T_vec = std::vector(1,T); + std::vector p_vec = std::vector(1,p); // Compare d - expected = 9.4844e+02; - actual = CoolProp::PropsSI("D","T",T,"P",p,fluid,x_vec); + double dexpected = 9.4844e+02; + std::vector > IO = CoolProp::PropsSImulti(std::vector(1,"D"),"T",T_vec,"P",p_vec,backend,fluids,x_vec); + REQUIRE(!IO.empty()); + double dactual = IO[0][0]; { CAPTURE(T); CAPTURE(p); CAPTURE(x); - CAPTURE(expected); - CAPTURE(actual); + CAPTURE(dexpected); + CAPTURE(dactual); std::string errmsg = CoolProp::get_global_param_string("errstring"); CAPTURE(errmsg); - CHECK( check_abs(expected,actual,acc) ); + CHECK( check_abs(dexpected,dactual,acc) ); } // Compare cp - expected = 3.6304e+03; - actual = CoolProp::PropsSI("C","T",T,"P",p,fluid,x_vec); + double cpexpected = 3.6304e+03; + double cpactual = CoolProp::PropsSImulti(std::vector(1,"C"),"T",T_vec,"P",p_vec,backend,fluids,x_vec)[0][0]; { CAPTURE(T); CAPTURE(p); CAPTURE(x); - CAPTURE(expected); - CAPTURE(actual); + CAPTURE(cpexpected); + CAPTURE(cpactual); std::string errmsg = CoolProp::get_global_param_string("errstring"); CAPTURE(errmsg); - CHECK( check_abs(expected,actual,acc) ); - } - - fluid = std::string("INCOMP::ExamplePure"); - T = +55 + 273.15; - p = 10e5; - - // Compare d - expected = 7.3646e+02; - actual = CoolProp::PropsSI("D","T",T,"P",p,fluid); - { - CAPTURE(T); - CAPTURE(p); - CAPTURE(x); - CAPTURE(expected); - CAPTURE(actual); - std::string errmsg = CoolProp::get_global_param_string("errstring"); - CAPTURE(errmsg); - CHECK( check_abs(expected,actual,acc) ); - } - - // Compare cp - expected = 2.2580e+03; - actual = CoolProp::PropsSI("C","T",T,"P",p,fluid); - { - CAPTURE(T); - CAPTURE(p); - CAPTURE(x); - CAPTURE(expected); - CAPTURE(actual); - std::string errmsg = CoolProp::get_global_param_string("errstring"); - CAPTURE(errmsg); - CHECK( check_abs(expected,actual,acc) ); + CHECK( check_abs(cpexpected,cpactual,acc) ); } } + SECTION("INCOMP::ExamplePure") + { + double acc = 0.0001; + std::string fluid = std::string("INCOMP::ExamplePure"); + double T = +55 + 273.15; + double p = 10e5; - //std::string name("INCOMP::TCO"); - double T = 50 + 273.15; - double p = 10e5; - double x = 0.3; - -// std::cout << CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::TCO",std::vector(1,x)) << std::endl; -// std::cout << CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::LiBr",std::vector(1,x)) << std::endl; -// std::cout << CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::NaK",std::vector(1,x)) << std::endl; - + // Compare d + double dexpected = 7.3646e+02; + double dactual = CoolProp::PropsSI("D","T",T,"P",p,fluid); + { + CAPTURE(T); + CAPTURE(p); + CAPTURE(dexpected); + CAPTURE(dactual); + std::string errmsg = CoolProp::get_global_param_string("errstring"); + CAPTURE(errmsg); + CHECK( check_abs(dexpected,dactual,acc) ); + } + // Compare cp + double cpexpected = 2.2580e+03; + double cpactual = CoolProp::PropsSI("C","T",T,"P",p,fluid); + { + CAPTURE(T); + CAPTURE(p); + CAPTURE(cpexpected); + CAPTURE(cpactual); + std::string errmsg = CoolProp::get_global_param_string("errstring"); + CAPTURE(errmsg); + CHECK( check_abs(cpexpected,cpactual,acc) ); + } + } // SECTION("Tests for the hardcoded fluids") { // diff --git a/src/CoolProp.cpp b/src/CoolProp.cpp index 539dc8a2..507572bd 100644 --- a/src/CoolProp.cpp +++ b/src/CoolProp.cpp @@ -5,10 +5,17 @@ #define _CRTDBG_MAP_ALLOC #define _CRT_SECURE_NO_WARNINGS #include + #endif #if defined(__ISWINDOWS__) #include +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif #else #ifndef DBL_EPSILON #include @@ -175,107 +182,397 @@ std::string extract_fractions(const std::string &fluid_string, std::vector &z) -{ - parameters iOutput = iundefined_parameter; - parameters iOf = iundefined_parameter, iWrt = iundefined_parameter, iConstant = iundefined_parameter, - iOf1 = iundefined_parameter, iWrt1 = iundefined_parameter, iConstant1 = iundefined_parameter, - iWrt2 = iundefined_parameter, iConstant2 = iundefined_parameter; - double x1, x2; +void _PropsSI_initialize(const std::string &backend, + const std::vector &fluid_names, + const std::vector &z, + shared_ptr &State){ + + if (fluid_names.empty()){throw ValueError("fluid_names cannot be empty");} + + Dictionary dict; - if (get_debug_level()>5){ - std::cout << format("%s:%d: _PropsSI(%s,%s,%g,%s,%g,%s,%s)\n",__FILE__,__LINE__,Output.c_str(),Name1.c_str(),Prop1,Name2.c_str(),Prop2,backend.c_str(),Ref.c_str(), vec_to_string(z).c_str()).c_str(); - } - - // The state we are going to use - shared_ptr State; - - // If the fractions of the components have been encoded in the string, extract them - // If they have not, this function does nothing - std::vector fractions; - if (z.empty()) - { - // Make a one-element vector - fractions = std::vector(1, 1); - } - else{ - // Make a copy - fractions = z; - } - - std::string fluid_string = extract_fractions(Ref, fractions); - - // We are going to let the factory function load the state - State.reset(AbstractState::factory(backend, fluid_string)); - - // Set the fraction for the state - if (State->using_mole_fractions()){ - State->set_mole_fractions(fractions); - } else if (State->using_mass_fractions()){ - State->set_mass_fractions(fractions); - } else if (State->using_volu_fractions()){ - State->set_volu_fractions(fractions); - } else { - if (get_debug_level()>50) std::cout << format("%s:%d: _PropsSI, could not set composition to %s, defaulting to mole fraction.\n",__FILE__,__LINE__, vec_to_string(z).c_str()).c_str(); - } - - // First check if it is a trivial input (critical/max parameters for instance) - if (is_valid_parameter(Output, iOutput)) - { - if (is_trivial_parameter(iOutput)){ - double val = State->trivial_keyed_output(iOutput); - return val; + // If a predefined mixture, set it up. Fractions and names are given + if (is_predefined_mixture(fluid_names[0], dict)){ + + // Retrieve the information on the predefined mixture + std::vector fractions = dict.get_double_vector("mole_fractions"); + std::vector fluid_names = dict.get_string_vector("fluids"); + + // Reset the state + State.reset(AbstractState::factory(backend, fluid_names)); + + // Set the fraction for the state + if (State->using_mole_fractions()){ + State->set_mole_fractions(fractions); + } else if (State->using_mass_fractions()){ + State->set_mass_fractions(fractions); + } else if (State->using_volu_fractions()){ + State->set_volu_fractions(fractions); + } else { + if (get_debug_level()>50) std::cout << format("%s:%d: _PropsSI, could not set composition to %s, defaulting to mole fraction.\n",__FILE__,__LINE__, vec_to_string(z).c_str()).c_str(); } + } + else{ + std::vector fractions(1, 1.0); // Default to one component, unity fraction + const std::vector *fractions_ptr = NULL; // Pointer to the array to be used; + + if (fluid_names.size() > 1){ + // Set the pointer - we are going to use the supplied fractions; they must be provided + fractions_ptr = &z; + // Reset the state + State.reset(AbstractState::factory(backend, fluid_names)); + } + else if (fluid_names.size() == 1){ + if (has_fractions_in_string(fluid_names[0]) || has_solution_concentration(fluid_names[0])){ + // Extract fractions from the string + std::string fluid_string = extract_fractions(fluid_names[0], fractions); + // Set the pointer - we are going to use the extracted fractions + fractions_ptr = &fractions; + // Reset the state + State.reset(AbstractState::factory(backend, fluid_string)); + } + else{ + if (z.empty()){ + // Set the pointer - we are going to use the default fractions + fractions_ptr = &fractions; + } + else{ + // Set the pointer - we are going to use the provided fractions + fractions_ptr = &z; + } + // Reset the state + State.reset(AbstractState::factory(backend, fluid_names)); + } + } + + // Set the fraction for the state + if (State->using_mole_fractions()){ + State->set_mole_fractions(*fractions_ptr); + } else if (State->using_mass_fractions()){ + State->set_mass_fractions(*fractions_ptr); + } else if (State->using_volu_fractions()){ + State->set_volu_fractions(*fractions_ptr); + } else { + if (get_debug_level()>50) std::cout << format("%s:%d: _PropsSI, could not set composition to %s, defaulting to mole fraction.\n",__FILE__,__LINE__, vec_to_string(z).c_str()).c_str(); + } } - - parameters iName1 = get_parameter_index(Name1); - parameters iName2 = get_parameter_index(Name2); +} - // Obtain the input pair - CoolProp::input_pairs pair = generate_update_pair(iName1, Prop1, iName2, Prop2, x1, x2); +struct output_parameter{ + enum OutputParametersType {OUTPUT_TYPE_UNSET = 0, OUTPUT_TYPE_TRIVIAL, OUTPUT_TYPE_NORMAL, OUTPUT_TYPE_FIRST_DERIVATIVE, OUTPUT_TYPE_SECOND_DERIVATIVE}; + CoolProp::parameters Of1, Wrt1, Constant1, Wrt2, Constant2; + OutputParametersType type; + /// Parse a '&' separated string into a data structure with one entry per output + /// Covers both normal and derivative outputs + static std::vector get_output_parameters(const std::vector &Outputs){ + std::vector outputs; + for (std::vector::const_iterator str = Outputs.begin(); str != Outputs.end(); ++str){ + output_parameter out; + CoolProp::parameters iOutput; + if (is_valid_parameter(*str, iOutput)){ + out.Of1 = iOutput; + if (is_trivial_parameter(iOutput)){ out.type = OUTPUT_TYPE_TRIVIAL; } + else{ out.type = OUTPUT_TYPE_NORMAL; } + } + else if (is_valid_first_derivative(*str, out.Of1, out.Wrt1, out.Constant1)){ + out.type = OUTPUT_TYPE_FIRST_DERIVATIVE; + } + else if (is_valid_second_derivative(*str, out.Of1, out.Wrt1, out.Constant1, out.Wrt2, out.Constant2)){ + out.type = OUTPUT_TYPE_SECOND_DERIVATIVE; + } + else{ + throw ValueError(format("Output string is invalid [%s]", str->c_str())); + } + outputs.push_back(out); + } + return outputs; + }; +}; - // Update the state - State->update(pair, x1, x2); +void _PropsSI_outputs(shared_ptr &State, + std::vector output_parameters, + CoolProp::input_pairs input_pair, + const std::vector &in1, + const std::vector &in2, + std::vector > &IO){ + + // Check the inputs + if (in1.size() != in2.size()){ throw ValueError(format("lengths of in1 [%d] and in2 [%d] are not the same", in1.size(), in2.size()));} + bool one_input_one_output = (in1.size() == 1 && in2.size() == 1 && output_parameters.size() == 1); + + // Resize the output matrix + std::size_t N1 = std::max(static_cast(1), in1.size()); + std::size_t N2 = std::max(static_cast(1), output_parameters.size()); + IO.resize(N1, std::vector(N2, _HUGE)); - if (iOutput != iundefined_parameter){ - // Get the desired output - double val = State->keyed_output(iOutput); + // Throw an error if at the end, there were no successes + bool success = false; + + // Iterate over the state variable inputs + for (std::size_t i = 0; i < IO.size(); ++i){ + try{ + if (input_pair != INPUT_PAIR_INVALID){ + // Update the state since it is a valid set of inputs + State->update(input_pair, in1[i], in2[i]); + } + } + catch(std::exception &){ + // All the outputs are filled with _HUGE; go to next input + for (std::size_t j = 0; j < IO[i].size(); ++j){ IO[i][j] = _HUGE; } + continue; + } - // Return the value - return val; - } - else if (is_valid_first_derivative(Output, iOf, iWrt, iConstant)){ - // Return the desired output - double val = State->first_partial_deriv(iOf, iWrt, iConstant); - - // Return the value - return val; - } - else if (is_valid_second_derivative(Output, iOf1, iWrt1, iConstant1, iWrt2, iConstant2)){ - // Return the desired output - double val = State->second_partial_deriv(iOf1, iWrt1, iConstant1, iWrt2, iConstant2); - - // Return the value - return val; - } - else{ - throw ValueError(format("Output [%s] is not a parameter or a string representation of a derivative",Output.c_str()).c_str()); - } + for (std::size_t j = 0; j < IO[i].size(); ++j){ + try{ + output_parameter &output = output_parameters[j]; + switch (output.type){ + case output_parameter::OUTPUT_TYPE_TRIVIAL: + case output_parameter::OUTPUT_TYPE_NORMAL: + IO[i][j] = State->keyed_output(output.Of1); break; + case output_parameter::OUTPUT_TYPE_FIRST_DERIVATIVE: + IO[i][j] = State->first_partial_deriv(output.Of1, output.Wrt1, output.Constant1); break; + case output_parameter::OUTPUT_TYPE_SECOND_DERIVATIVE: + IO[i][j] = State->second_partial_deriv(output.Of1, output.Wrt1, output.Constant1, output.Wrt2, output.Constant2); break; + default: + throw ValueError(format("")); break; + } + // At least one has succeeded + success = true; + } + catch(std::exception &){ + IO[i][j] = _HUGE; + } + } + } + if (success == false) {IO.empty(); throw ValueError(format("No outputs were able to be calculated")); } } -double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &Ref, const std::vector &z) +std::vector > PropsSImulti(const std::vector &Outputs, + const std::string &Name1, + const std::vector &Prop1, + const std::string &Name2, + const std::vector &Prop2, + const std::string &backend, + const std::vector &fluids, + const std::vector &fractions) { - std::string backend, fluid; - // Fractions are already provided, we just need to parse the Ref string - extract_backend(Ref, backend, fluid); - CATCH_ALL_ERRORS_RETURN_HUGE(return _PropsSI(Output,Name1,Prop1,Name2,Prop2,backend, fluid,z);) + shared_ptr State; + std::vector > IO; + CoolProp::input_pairs input_pair; + std::vector output_parameters; + std::vector v1, v2; + + #if !defined(NO_ERROR_CATCHING) + try{ + #endif + + try{ + // Initialize the State class + _PropsSI_initialize(backend, fluids, fractions, State); + } + catch(std::exception &e){ + // Initialization failed. Stop. + throw ValueError(format("Initialize failed for backend: \"%s\", fluid: \"%s\" fractions \"%s\"; error: %s",backend.c_str(), strjoin(fluids,"&").c_str(), vec_to_string(fractions, "%0.10f").c_str(), e.what()) ); + } + + try{ + // Get update pair + CoolProp::parameters key1; + is_valid_parameter(Name1, key1); + CoolProp::parameters key2; + is_valid_parameter(Name2, key2); + input_pair = generate_update_pair(key1, Prop1, key2, Prop2, v1, v2); + } + catch (std::exception &e){ + // Input parameter parsing failed. Stop + throw ValueError(format("Input pair parsing failed for Name1: \"%s\", Name2: \"%s\"; err: %s", Name1.c_str(), Name2.c_str(), e.what())); + } + + try{ + output_parameters = output_parameter::get_output_parameters(Outputs); + } + catch (std::exception &e){ + // Output parameter parsing failed. Stop. + throw ValueError(format("Output parameter parsing failed; error: %s", e.what())); + } + + // Calculate the output(s). In the case of a failure, all values will be filled with _HUGE + _PropsSI_outputs(State, output_parameters, input_pair, v1, v2, IO); + + // Return the value(s) + return IO; + + #if !defined(NO_ERROR_CATCHING) + } + catch(const std::exception& e){ + set_error_string(e.what()); + #if defined (PROPSSI_ERROR_STDOUT) + std::cout << e.what() << std::endl; + #endif + if (get_debug_level() > 1){std::cout << e.what() << std::endl;} + } + catch(...){ + } + #endif + return std::vector >(); } -double PropsSI(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const std::vector &x) +double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &Ref) { - std::string _Output = Output, _Name1 = Name1, _Name2 = Name2, _FluidName = FluidName; - return PropsSI(_Output,_Name1,Prop1,_Name2,Prop2,_FluidName, x); + std::string backend, fluid; std::vector fractions(1,1.0); + #if !defined(NO_ERROR_CATCHING) + try{ + #endif + + // BEGIN OF TRY + // Here is the real code that is inside the try block + + shared_ptr State; + std::vector > IO; + + extract_backend(Ref, backend, fluid); + if (has_fractions_in_string(fluid)){ + extract_fractions(fluid, fractions); + } + + IO = PropsSImulti(strsplit(Output,'&'), Name1, std::vector(1, Prop1), Name2, std::vector(1, Prop2), backend, std::vector(1, fluid), fractions); + if (IO.size()!= 1 || IO[0].size() != 1){ throw ValueError(format("output should be 1x1; error was %s", get_global_param_string("errstring").c_str())); } + + double val = IO[0][0]; + + if (get_debug_level() > 1){ std::cout << format("_PropsSI will return %g",val) << std::endl; } + return val; + // END OF TRY + #if !defined(NO_ERROR_CATCHING) + } + catch(const std::exception& e){ + set_error_string(e.what() + format(" : PropsSI(\"%s\",\"%s\",%0.10g,\"%s\",%0.10g,\"%s\")",Output.c_str(),Name1.c_str(), Prop1, Name2.c_str(), Prop2, Ref.c_str())); + #if defined (PROPSSI_ERROR_STDOUT) + std::cout << e.what() << std::endl; + #endif + if (get_debug_level() > 1){std::cout << e.what() << std::endl;} + return _HUGE; + } + catch(...){ + return _HUGE; + } + #endif } +#if defined(ENABLE_CATCH) +TEST_CASE("Check inputs to PropsSI","[PropsSI]") +{ + SECTION("Single state, single output"){ + CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"Water"))); + }; + SECTION("Single state, single output, pure incompressible"){ + CHECK(ValidNumber(CoolProp::PropsSI("D","P",101325,"T",300,"INCOMP::DowQ"))); + }; + SECTION("Bad input pair"){ + CHECK(!ValidNumber(CoolProp::PropsSI("D","Q",0,"Q",0,"Water"))); + }; + SECTION("Single state, single output, 40% incompressible"){ + CHECK(ValidNumber(CoolProp::PropsSI("D","P",101325,"T",300,"INCOMP::MEG[0.40]"))); + }; + SECTION("Single state, single output, predefined CoolProp mixture"){ + CHECK(ValidNumber(CoolProp::PropsSI("T","Q",1,"P",3e6,"HEOS::R125[0.7]&R32[0.3]"))); + }; + SECTION("Single state, single output"){ + CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"HEOS::Water"))); + }; + SECTION("Single state, single output, predefined mixture"){ + CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"R410A.mix"))); + }; + SECTION("Predefined mixture"){ + std::vector p(1, 101325), Q(1, 1.0), z; + std::vector outputs(1,"T"); outputs.push_back("Dmolar"); + std::vector > IO; + std::vector fluids(1, "R410A.mix"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + }; + SECTION("Single state, two outputs"){ + std::vector p(1, 101325), Q(1, 1.0), z(1, 1.0); + std::vector outputs(1,"T"); outputs.push_back("Dmolar"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(std::vector > IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + }; + SECTION("Single state, two bad outputs"){ + std::vector p(1, 101325), Q(1, 1.0), z(1, 1.0); + std::vector > IO; + std::vector outputs(1,"???????"); outputs.push_back("?????????"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + CHECK(IO.size() == 0); + }; + SECTION("Two states, one output"){ + std::vector p(2, 101325), Q(2, 1.0), z(1, 1.0); + std::vector outputs(1,"T"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(std::vector > IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + }; + SECTION("Two states, two outputs"){ + std::vector p(2, 101325), Q(2, 1.0), z(1, 1.0); + std::vector outputs(1,"T"); outputs.push_back("Dmolar"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(std::vector > IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + }; + SECTION("cp and its derivative representation"){ + std::vector p(1, 101325), Q(1, 1.0), z(1, 1.0); + std::vector > IO; + std::vector outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + std::string errstring = get_global_param_string("errstring"); + CAPTURE(errstring); + REQUIRE(!IO.empty()); + CAPTURE(IO[0][0]); + CAPTURE(IO[0][1]); + CHECK(std::abs(IO[0][0] - IO[0][1]) < 1e-5); + }; + SECTION("bad fluid"){ + std::vector p(1, 101325), Q(1, 1.0), z(1, 1.0); + std::vector > IO; + std::vector outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P"); + std::vector fluids(1, "????????"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + std::string errstring = get_global_param_string("errstring"); + CAPTURE(errstring); + REQUIRE(IO.empty()); + }; + SECTION("bad mole fraction length"){ + std::vector p(1, 101325), Q(1, 1.0), z(1, 1.0); + std::vector > IO; + std::vector outputs(1,"T"); + std::vector fluids(1, "Water&Ethanol"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + std::string errstring = get_global_param_string("errstring"); + CAPTURE(errstring); + REQUIRE(IO.empty()); + }; + SECTION("bad input lengths"){ + std::vector p(1, 101325), Q(2, 1.0), z(100, 1.0); + std::vector > IO; + std::vector outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z);); + std::string errstring = get_global_param_string("errstring"); + CAPTURE(errstring); + REQUIRE(IO.empty()); + }; + SECTION("bad input pair"){ + std::vector Q(2, 1.0), z(1, 1.0); + std::vector > IO; + std::vector outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P"); + std::vector fluids(1, "Water"); + CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"Q",Q,"Q",Q,"HEOS",fluids,z);); + std::string errstring = get_global_param_string("errstring"); + CAPTURE(errstring); + REQUIRE(IO.empty()); + }; +}; +#endif + +/**************************************************** + * Props1SI * + ****************************************************/ + double Props1SI(const std::string &FluidName, const std::string &Output) { std::string _FluidName = FluidName, empty_string = "", _Output = Output; @@ -304,62 +601,27 @@ double Props1SI(const std::string &FluidName, const std::string &Output) return val1; } } -double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &Ref) +#if defined(ENABLE_CATCH) +TEST_CASE("Check inputs to Props1SI","[Props1SI],[PropsSI]") { - std::string backend, fluid; - std::vector fractions; - #if !defined(NO_ERROR_CATCHING) - // In this function the error catching happens; - try{ - #endif - // BEGIN OF TRY - // Here is the real code that is inside the try block - extract_backend(Ref, backend, fluid); - Dictionary dict; - if (is_predefined_mixture(fluid, dict)){ - fractions = dict.get_double_vector("mole_fractions"); - fluid = strjoin(dict.get_string_vector("fluids"),"&"); - } - double val = _PropsSI(Output, Name1, Prop1, Name2, Prop2, backend, fluid, fractions); - if (get_debug_level() > 1){ std::cout << format("_PropsSI will return %g",val) << std::endl; } - return val; - // END OF TRY - #if !defined(NO_ERROR_CATCHING) - } - catch(const std::exception& e){ - set_error_string(e.what() + format(" : PropsSI(\"%s\",\"%s\",%0.10g,\"%s\",%0.10g,\"%s\")",Output.c_str(),Name1.c_str(), Prop1, Name2.c_str(), Prop2, Ref.c_str())); - #if defined (PROPSSI_ERROR_STDOUT) - std::cout << e.what() << std::endl; - #endif - if (get_debug_level() > 1){std::cout << e.what() << std::endl;} - return _HUGE; - } - catch(...){ - return _HUGE; - } - #endif -} -std::vector PropsSI(const std::string &Output, const std::string &Name1, const std::vector &Prop1, const std::string &Name2, const std::vector Prop2, const std::string &FluidName) -{ - return PropsSI(Output, Name1, Prop1, Name2, Prop2, FluidName, std::vector(1,1)); -} -std::vector PropsSI(const std::string &Output, const std::string &Name1, const std::vector &Prop1, const std::string &Name2, const std::vector Prop2, const std::string &Ref, const std::vector &z) -{ - std::vector out(Prop1.size(), _HUGE); - if (Prop1.size() != Prop2.size()) - { - throw ValueError(format("Sizes of Prop1 [%d] and Prop2 [%d] to PropsSI are not the same", Prop1.size(), Prop2.size())); - } - for (std::size_t i = 0; i < Prop1.size(); ++i) - { - out[i] = PropsSI(Output,Name1,Prop1[i],Name2,Prop2[i],Ref,z); - } - return out; -} -double Props1SI(std::string FluidName,std::string Output) -{ - return PropsSI(Output,"",0,"",0,FluidName); -} + SECTION("Good fluid, good parameter"){ + CHECK(ValidNumber(CoolProp::Props1SI("Tcrit","Water"))); + }; + SECTION("Good fluid, good parameter"){ + CHECK(ValidNumber(CoolProp::PropsSI("Tcrit","",0,"",0,"Water"))); + }; + SECTION("Good fluid, good parameter, inverted"){ + CHECK(ValidNumber(CoolProp::Props1SI("Water","Tcrit"))); + }; + SECTION("Good fluid, bad parameter"){ + CHECK(!ValidNumber(CoolProp::Props1SI("Water","????????????"))); + }; + SECTION("Bad fluid, good parameter"){ + CHECK(!ValidNumber(CoolProp::Props1SI("?????","Tcrit"))); + }; +}; +#endif + bool is_valid_fluid_string(std::string &input_fluid_string) { @@ -628,6 +890,7 @@ std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &N return phase_lookup_string(static_cast(Phase_int)); } +/* std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector &z) { double Phase_double = PropsSI("Phase",Name1,Prop1,Name2,Prop2,FluidName,z); @@ -635,5 +898,5 @@ std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &N std::size_t Phase_int = static_cast(Phase_double); return phase_lookup_string(static_cast(Phase_int)); } - +*/ } /* namespace CoolProp */ \ No newline at end of file diff --git a/src/CoolPropLib.cpp b/src/CoolPropLib.cpp index 4d18345a..06d6439f 100644 --- a/src/CoolPropLib.cpp +++ b/src/CoolPropLib.cpp @@ -199,13 +199,15 @@ EXPORT_CODE long CONVENTION PhaseSI(const char *Name1, double Prop1, const char return 0; } } -EXPORT_CODE double CONVENTION PropsSIZ(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char * FluidName, const double *z, int n) +/* + * EXPORT_CODE double CONVENTION PropsSIZ(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char * FluidName, const double *z, int n) { std::string _Output = Output, _Name1 = Name1, _Name2 = Name2, _FluidName = FluidName; double val = CoolProp::PropsSI(_Output, _Name1, Prop1, _Name2, Prop2, _FluidName, std::vector(z, z+n)); reset_fpu(); return val; } + * */ EXPORT_CODE void CONVENTION propssi_(const char *Output, const char *Name1, double *Prop1, const char *Name2, double *Prop2, const char * FluidName, double *output) { std::string _Output = Output, _Name1 = Name1, _Name2 = Name2, _FluidName = FluidName; diff --git a/src/DataStructures.cpp b/src/DataStructures.cpp index 09f59007..6899424b 100644 --- a/src/DataStructures.cpp +++ b/src/DataStructures.cpp @@ -463,13 +463,13 @@ std::string get_input_pair_long_desc(int pair) TEST_CASE("Check that all parameters are descibed","") { - for (int i = 0; i < CoolProp::iundefined_parameter; ++i){ + for (int i = 1; i < CoolProp::iundefined_parameter; ++i){ std::ostringstream ss; ss << "Parameter index," << i << "last index:" << CoolProp::iundefined_parameter; SECTION(ss.str(), "") { std::string prior; - if (i > 0){ + if (i > 1){ CHECK_NOTHROW(prior = CoolProp::get_parameter_information(i-1,"short")); CAPTURE(prior); } diff --git a/wrappers/EES/main.cpp b/wrappers/EES/main.cpp index 95d29f49..fe443d2c 100644 --- a/wrappers/EES/main.cpp +++ b/wrappers/EES/main.cpp @@ -152,8 +152,19 @@ extern "C" { if (!Units.compare("SI")){ if (z.size() > 0){ - // Mole fractions are given - out = PropsSI(Outstr, In1str, In1, In2str, In2, Fluidstr, z); + std::string backend, fluid; + extract_backend(Fluidstr, backend, fluid); + // Vectorize the inputs + std::vector fluids = strsplit(fluid,'&'); + std::vector outputs(1,Outstr); + std::vector val1(1,In1); + std::vector val2(1,In2); + // Mole fractions are given, we use the advanced PropsSImulti function + std::vector > IO = PropsSImulti(outputs, In1str, val1, In2str, val2, backend, fluids, z); + if (IO.size() != 1 || IO[0].size() != 1){out = _HUGE;} + else{ + out = IO[0][0]; + } } else{ // Mole fractions are not given diff --git a/wrappers/Python/CoolProp/CoolProp.pxd b/wrappers/Python/CoolProp/CoolProp.pxd index 3bf1c10c..11289575 100644 --- a/wrappers/Python/CoolProp/CoolProp.pxd +++ b/wrappers/Python/CoolProp/CoolProp.pxd @@ -33,12 +33,13 @@ cdef extern from "CoolProp.h" namespace "CoolProp": double _Props1SI "CoolProp::Props1SI"(string Ref, string Output) double _PropsSI "CoolProp::PropsSI"(string Output, string Name1, double Prop1, string Name2, double Prop2, string FluidName) string _PhaseSI "CoolProp::PhaseSI"(string Name1, double Prop1, string Name2, double Prop2, string FluidName) - vector[double] _PropsSI "CoolProp::PropsSI"(string Output, string Name1, vector[double] Prop1, string Name2, vector[double] Prop2, string FluidName, vector[double] fractions) - vector[double] _PropsSII "CoolProp::PropsSI"(string Output, string Name1, vector[double] Prop1, string Name2, vector[double] Prop2, string FluidName) + vector[vector[double]] _PropsSImulti "CoolProp::PropsSImulti"(vector[string] Outputs, string Name1, vector[double] Prop1, string Name2, vector[double] Prop2, string backend, vector[string] FluidName, vector[double] fractions) string _get_global_param_string "CoolProp::get_global_param_string"(string ParamName) except + int _get_debug_level "CoolProp::get_debug_level"() except + void _set_debug_level "CoolProp::set_debug_level"(int level) except + string _get_fluid_param_string "CoolProp::get_fluid_param_string"(string ParamName, string FluidName) except + + void _extract_backend "CoolProp::extract_backend"(string input, string backend, string fluids) except + + string _extract_fractions "CoolProp::extract_fractions"(string input, vector[double] fractions) except + void _set_reference_stateS "CoolProp::set_reference_stateS"(string, string) except + void _set_reference_stateD "CoolProp::set_reference_stateD"(string, double, double, double, double) except + diff --git a/wrappers/Python/CoolProp/CoolProp.pyx b/wrappers/Python/CoolProp/CoolProp.pyx index 4a815cb0..4360ba40 100644 --- a/wrappers/Python/CoolProp/CoolProp.pyx +++ b/wrappers/Python/CoolProp/CoolProp.pyx @@ -205,7 +205,11 @@ cpdef PropsSI(in1, in2, in3 = None, in4 = None, in5 = None, in6 = None, in7 = No """ A Python wrapper of C++ function :cpapi:`CoolProp::PropsSI` . """ + cdef vector[string] vin1 + cdef vector[double] fractions cdef double val + cdef string backend, fluid, delimitedfluids + cdef bool is_iterable1, is_iterable3, is_iterable5 # Two parameter inputs if in3 is None and in4 is None and in5 is None and in6 is None and in7 is None: @@ -216,19 +220,47 @@ cpdef PropsSI(in1, in2, in3 = None, in4 = None, in5 = None, in6 = None, in7 = No return val # Six parameter inputs elif in7 is None: - if iterable(in3) and iterable(in5): - if len(in3) != len(in5): - raise TypeError("Sizes of Prop1 {n1:d} and Prop2 {n2:d} to PropsSI are not the same".format(n1 = len(in3), n2 = len(in5))) - # This version takes iterables - return ndarray_or_iterable(_PropsSII(in1, in2, in3, in4, in5, in6)) - elif iterable(in3) and not(iterable(in5)): - i5 = [in5]*len(in3) - # This version takes iterables - return ndarray_or_iterable(_PropsSII(in1, in2, in3, in4, i5, in6)) - elif iterable(in5) and not(iterable(in3)): - i3 = [in3]*len(in5) - # This version takes iterables - return ndarray_or_iterable(_PropsSII(in1, in2, i3, in4, in5, in6)) + is_iterable1 = iterable(in1) + is_iterable3 = iterable(in3) + is_iterable5 = iterable(in5) + if is_iterable1 or is_iterable3 or is_iterable5: + # Prepare the output datatype + if not is_iterable1: + vin1.push_back(in1) + else: + vin1 = in1 + # Resize state variable inputs + if is_iterable3 and is_iterable5: + if len(in3) != len(in5): + raise TypeError("Sizes of Prop1 {n1:d} and Prop2 {n2:d} to PropsSI are not the same".format(n1 = len(in3), n2 = len(in5))) + else: + vval1 = in3 + vval2 = in5 + elif is_iterable3 and not is_iterable5: + vval1 = in3 + vval2 = [in5]*len(in3) + elif is_iterable5 and not is_iterable3: + vval1 = [in3]*len(in5) + vval2 = in5 + + # Extract the backend and the fluid from the input string + _extract_backend(in6, backend, fluid) + + # Extract the fractions + fractions.push_back(1.0) + delimitedfluids = _extract_fractions(fluid, fractions) + + # Extract the fluids + fluids = delimitedfluids.split('&') + + # Call the function - this version takes iterables + outmat = _PropsSImulti(in1, in2, vval1, in4, vval2, backend, fluids, fractions) + + # Check that we got some output + if outmat.empty(): + raise ValueError(_get_global_param_string('errstring')) + + return outmat else: # This version takes doubles val = _PropsSI(in1, in2, in3, in4, in5, in6) @@ -236,8 +268,6 @@ cpdef PropsSI(in1, in2, in3 = None, in4 = None, in5 = None, in6 = None, in7 = No __Props_err2("PropsSI", in1, in2, in3, in4, in5, in6) else: return val - else: - return _PropsSI(in1, in2, in3, in4, in5, in6, in7) cpdef list FluidsList(): """ diff --git a/wrappers/Python/generate_constants_module.py b/wrappers/Python/generate_constants_module.py index ee26f1d0..6cc0f322 100644 --- a/wrappers/Python/generate_constants_module.py +++ b/wrappers/Python/generate_constants_module.py @@ -25,6 +25,8 @@ def params_constants(enum_key): for i,line in enumerate(lines): if line.find('/'): lines[i] = line.split('/')[0] + if '=' in lines[i]: + lines[i] = lines[i].split('=')[0].strip() + ',' # Chomp all the whitespace, split at commas keys = ''.join(lines).replace(' ','').split(',') @@ -68,4 +70,7 @@ def generate_cython(data): def generate(): data = [(enum,params_constants(enum)) for enum in ['parameters', 'input_pairs', 'fluid_types', 'phases']] - generate_cython(data) \ No newline at end of file + generate_cython(data) + +if __name__=='__main__': + generate() \ No newline at end of file