Merge pull request #374 from CoolProp/vectorize_PropsSI

Vectorize PropsSI
This commit is contained in:
Ian Bell
2014-12-31 09:57:57 -05:00
15 changed files with 649 additions and 308 deletions

View File

@@ -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<std::string> &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;

View File

@@ -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<double> &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<double> PropsSI(const std::string &Output, const std::string &Name1, const std::vector<double> &Prop1, const std::string &Name2, const std::vector<double> Prop2, const std::string &FluidName, const std::vector<double> &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<double> PropsSI(const std::string &Output, const std::string &Name1, const std::vector<double> &Prop1, const std::string &Name2, const std::vector<double> 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<double> &x);
*/
double PropsSI(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const std::vector<double> &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<std::vector<double> > PropsSImulti(const std::vector<std::string> &Outputs,
const std::string &Name1,
const std::vector<double> &Prop1,
const std::string &Name2,
const std::vector<double> &Prop2,
const std::string &backend,
const std::vector<std::string> &fluids,
const std::vector<double> &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<double> &z);
//std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector<double> &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<double> &fractions);
} /* namespace CoolProp */
#endif

View File

@@ -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);

View File

@@ -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<class T> 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<class T> 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<class T> 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;

View File

@@ -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<std::string> &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<std::string> 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<std::string> 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<AbstractState> AS(factory(backend.substr(5), fluid_string));
shared_ptr<AbstractState> 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
{

View File

@@ -35,7 +35,7 @@ static int deriv_counter = 0;
namespace CoolProp {
HelmholtzEOSMixtureBackend::HelmholtzEOSMixtureBackend(std::vector<std::string> &component_names, bool generate_SatL_and_SatV) {
HelmholtzEOSMixtureBackend::HelmholtzEOSMixtureBackend(const std::vector<std::string> &component_names, bool generate_SatL_and_SatV) {
std::vector<CoolPropFluid*> components;
components.resize(component_names.size());

View File

@@ -36,7 +36,7 @@ public:
HelmholtzEOSMixtureBackend(){
imposed_phase_index = iphase_not_imposed; _phase = iphase_unknown;};
HelmholtzEOSMixtureBackend(std::vector<CoolPropFluid*> components, bool generate_SatL_and_SatV = true);
HelmholtzEOSMixtureBackend(std::vector<std::string> &component_names, bool generate_SatL_and_SatV = true);
HelmholtzEOSMixtureBackend(const std::vector<std::string> &component_names, bool generate_SatL_and_SatV = true);
virtual ~HelmholtzEOSMixtureBackend(){};
shared_ptr<ReducingFunction> Reducing;
ExcessTerm Excess;

View File

@@ -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<double>(1,x));
std::vector<std::string> fluid_Melinder(1,fluid);
std::vector<std::vector<double> > IO = CoolProp::PropsSImulti(std::vector<std::string>(1,"D"),"T",std::vector<double>(1,T),"P",std::vector<double>(1,p),"INCOMP",fluid_Melinder,std::vector<double>(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<std::string> fluids(1,"ExampleSecCool");
double T = -5 + 273.15;
double p = 10e5;
double x = 0.4;
std::vector<double> x_vec = std::vector<double>(1,x);
std::vector<double> T_vec = std::vector<double>(1,T);
std::vector<double> p_vec = std::vector<double>(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<std::vector<double> > IO = CoolProp::PropsSImulti(std::vector<std::string>(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<std::string>(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<double>(1,x)) << std::endl;
// std::cout << CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::LiBr",std::vector<double>(1,x)) << std::endl;
// std::cout << CoolProp::PropsSI("D","T",T,"P",p,"INCOMP::NaK",std::vector<double>(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") {
//

View File

@@ -5,10 +5,17 @@
#define _CRTDBG_MAP_ALLOC
#define _CRT_SECURE_NO_WARNINGS
#include <crtdbg.h>
#endif
#if defined(__ISWINDOWS__)
#include <windows.h>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#else
#ifndef DBL_EPSILON
#include <limits>
@@ -175,107 +182,397 @@ std::string extract_fractions(const std::string &fluid_string, std::vector<doubl
}
}
// Internal function to do the actual calculations, make this a wrapped function so
// that error bubbling can be done properly
double _PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &backend, const std::string &Ref, const std::vector<double> &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<std::string> &fluid_names,
const std::vector<double> &z,
shared_ptr<AbstractState> &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<AbstractState> 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<double> fractions;
if (z.empty())
{
// Make a one-element vector
fractions = std::vector<double>(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<double> fractions = dict.get_double_vector("mole_fractions");
std::vector<std::string> 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<double> fractions(1, 1.0); // Default to one component, unity fraction
const std::vector<double> *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<output_parameter> get_output_parameters(const std::vector<std::string> &Outputs){
std::vector<output_parameter> outputs;
for (std::vector<std::string>::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<AbstractState> &State,
std::vector<output_parameter> output_parameters,
CoolProp::input_pairs input_pair,
const std::vector<double> &in1,
const std::vector<double> &in2,
std::vector<std::vector<double> > &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<std::size_t>(1), in1.size());
std::size_t N2 = std::max(static_cast<std::size_t>(1), output_parameters.size());
IO.resize(N1, std::vector<double>(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<double> &z)
std::vector<std::vector<double> > PropsSImulti(const std::vector<std::string> &Outputs,
const std::string &Name1,
const std::vector<double> &Prop1,
const std::string &Name2,
const std::vector<double> &Prop2,
const std::string &backend,
const std::vector<std::string> &fluids,
const std::vector<double> &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<AbstractState> State;
std::vector<std::vector<double> > IO;
CoolProp::input_pairs input_pair;
std::vector<output_parameter> output_parameters;
std::vector<double> 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<std::vector<double> >();
}
double PropsSI(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const std::vector<double> &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<double> 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<AbstractState> State;
std::vector<std::vector<double> > IO;
extract_backend(Ref, backend, fluid);
if (has_fractions_in_string(fluid)){
extract_fractions(fluid, fractions);
}
IO = PropsSImulti(strsplit(Output,'&'), Name1, std::vector<double>(1, Prop1), Name2, std::vector<double>(1, Prop2), backend, std::vector<std::string>(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<double> p(1, 101325), Q(1, 1.0), z;
std::vector<std::string> outputs(1,"T"); outputs.push_back("Dmolar");
std::vector<std::vector<double> > IO;
std::vector<std::string> fluids(1, "R410A.mix");
CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z););
};
SECTION("Single state, two outputs"){
std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
std::vector<std::string> outputs(1,"T"); outputs.push_back("Dmolar");
std::vector<std::string> fluids(1, "Water");
CHECK_NOTHROW(std::vector<std::vector<double> > IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z););
};
SECTION("Single state, two bad outputs"){
std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
std::vector<std::vector<double> > IO;
std::vector<std::string> outputs(1,"???????"); outputs.push_back("?????????");
std::vector<std::string> 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<double> p(2, 101325), Q(2, 1.0), z(1, 1.0);
std::vector<std::string> outputs(1,"T");
std::vector<std::string> fluids(1, "Water");
CHECK_NOTHROW(std::vector<std::vector<double> > IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z););
};
SECTION("Two states, two outputs"){
std::vector<double> p(2, 101325), Q(2, 1.0), z(1, 1.0);
std::vector<std::string> outputs(1,"T"); outputs.push_back("Dmolar");
std::vector<std::string> fluids(1, "Water");
CHECK_NOTHROW(std::vector<std::vector<double> > IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z););
};
SECTION("cp and its derivative representation"){
std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
std::vector<std::vector<double> > IO;
std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
std::vector<std::string> 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<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
std::vector<std::vector<double> > IO;
std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
std::vector<std::string> 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<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
std::vector<std::vector<double> > IO;
std::vector<std::string> outputs(1,"T");
std::vector<std::string> 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<double> p(1, 101325), Q(2, 1.0), z(100, 1.0);
std::vector<std::vector<double> > IO;
std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
std::vector<std::string> 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<double> Q(2, 1.0), z(1, 1.0);
std::vector<std::vector<double> > IO;
std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
std::vector<std::string> 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<double> 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<double> PropsSI(const std::string &Output, const std::string &Name1, const std::vector<double> &Prop1, const std::string &Name2, const std::vector<double> Prop2, const std::string &FluidName)
{
return PropsSI(Output, Name1, Prop1, Name2, Prop2, FluidName, std::vector<double>(1,1));
}
std::vector<double> PropsSI(const std::string &Output, const std::string &Name1, const std::vector<double> &Prop1, const std::string &Name2, const std::vector<double> Prop2, const std::string &Ref, const std::vector<double> &z)
{
std::vector<double> 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<phases>(Phase_int));
}
/*
std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector<double> &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<std::size_t>(Phase_double);
return phase_lookup_string(static_cast<phases>(Phase_int));
}
*/
} /* namespace CoolProp */

View File

@@ -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<double>(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;

View File

@@ -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);
}

View File

@@ -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<std::string> fluids = strsplit(fluid,'&');
std::vector<std::string> outputs(1,Outstr);
std::vector<double> val1(1,In1);
std::vector<double> val2(1,In2);
// Mole fractions are given, we use the advanced PropsSImulti function
std::vector<std::vector<double> > 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

View File

@@ -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 +

View File

@@ -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():
"""

View File

@@ -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)
generate_cython(data)
if __name__=='__main__':
generate()