Merge branch 'master' into eigenPolynomials

Conflicts:
	CMakeLists.txt
This commit is contained in:
jowr
2014-06-10 00:43:05 +02:00
144 changed files with 7818 additions and 3132 deletions

View File

@@ -19,7 +19,7 @@ AbstractState * AbstractState::factory(const std::string &backend, const std::st
static std::string HEOS_string = "HEOS";
if (!backend.compare("HEOS"))
{
if (fluid_string.find('&') == -1){
if (fluid_string.find('&') == std::string::npos){
return new HelmholtzEOSBackend(&get_fluid(fluid_string));
}
else{
@@ -31,7 +31,7 @@ AbstractState * AbstractState::factory(const std::string &backend, const std::st
}
else if (!backend.compare("REFPROP"))
{
if (fluid_string.find('&') == -1){
if (fluid_string.find('&') == std::string::npos){
return new REFPROPBackend(fluid_string);
}
else{
@@ -57,7 +57,7 @@ AbstractState * AbstractState::factory(const std::string &backend, const std::st
{
std::size_t idel = fluid_string.find("::");
// Backend has not been specified, and we have to figure out what the backend is by parsing the string
if (idel == -1) // No '::' found, no backend specified, try HEOS, otherwise a failure
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);
@@ -196,6 +196,8 @@ double AbstractState::keyed_output(int key)
return get_reducing().T;
case irhomolar_reducing:
return get_reducing().rhomolar;
case ispeed_sound:
return speed_sound();
//case iT_critical:
// return get_critical().T;
//case irhomolar_critical:

View File

@@ -52,7 +52,7 @@ void FlashRoutines::QT_flash(HelmholtzEOSMixtureBackend &HEOS)
rhoLsat = HEOS.solver_rho_Tp(HEOS._T, psatLanc, rhoLanc);
rhoVsat = HEOS.solver_rho_Tp(HEOS._T, psatVanc, rhoLanc);
if (!ValidNumber(rhoLsat) || !ValidNumber(rhoVsat) ||
if (!ValidNumber(rhoLsat) || !ValidNumber(rhoVsat) ||
fabs(rhoLsat/rhoLanc-1) > 0.1 || fabs(rhoVanc/rhoVsat-1) > 0.1)
{
throw ValueError("pseudo-pure failed");
@@ -79,7 +79,7 @@ void FlashRoutines::QT_flash(HelmholtzEOSMixtureBackend &HEOS)
// Use Wilson iteration to obtain updated guess for pressure
pguess = SaturationSolvers::saturation_Wilson(&HEOS, HEOS._Q, HEOS._T, SaturationSolvers::imposed_T, HEOS.mole_fractions, pguess);
// Actually call the successive substitution solver
SaturationSolvers::successive_substitution(&HEOS, HEOS._Q, HEOS._T, pguess, HEOS.mole_fractions, HEOS.K, options);
}
@@ -109,7 +109,7 @@ void FlashRoutines::PQ_flash(HelmholtzEOSMixtureBackend &HEOS)
options.use_logdelta = false;
// Actually call the solver
SaturationSolvers::saturation_PHSU_pure(&HEOS, HEOS._p, options);
// Load the outputs
HEOS._p = HEOS._Q*HEOS.SatV->p() + (1 - HEOS._Q)*HEOS.SatL->p();
HEOS._rhomolar = 1/(HEOS._Q/HEOS.SatV->rhomolar() + (1 - HEOS._Q)/HEOS.SatL->rhomolar());
@@ -128,10 +128,10 @@ void FlashRoutines::PQ_flash(HelmholtzEOSMixtureBackend &HEOS)
// Use Wilson iteration to obtain updated guess for temperature
Tguess = SaturationSolvers::saturation_Wilson(&HEOS, HEOS._Q, HEOS._p, SaturationSolvers::imposed_p, HEOS.mole_fractions, Tguess);
// Actually call the successive substitution solver
SaturationSolvers::successive_substitution(&HEOS, HEOS._Q, Tguess, HEOS._p, HEOS.mole_fractions, HEOS.K, io);
PhaseEnvelope::PhaseEnvelope_GV ENV_GV;
ENV_GV.build(&HEOS, HEOS.mole_fractions, HEOS.K, io);
}
@@ -143,7 +143,7 @@ void FlashRoutines::PHSU_D_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
class solver_resid : public FuncWrapper1D
{
public:
HelmholtzEOSMixtureBackend *HEOS;
long double r, eos, rhomolar, value, T;
int other;
@@ -164,15 +164,15 @@ void FlashRoutines::PHSU_D_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
default:
throw ValueError(format("Input not supported"));
}
r = eos - value;
return r;
};
};
std::string errstring;
if (HEOS.imposed_phase_index > -1)
if (HEOS.imposed_phase_index > -1)
{
// Use the phase defined by the imposed phase
HEOS._phase = HEOS.imposed_phase_index;
@@ -182,17 +182,17 @@ void FlashRoutines::PHSU_D_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
if (HEOS.is_pure_or_pseudopure)
{
CoolPropFluid * component = HEOS.components[0];
shared_ptr<HelmholtzEOSMixtureBackend> Sat;
long double rhoLtriple = component->pEOS->rhoLtriple;
long double rhoVtriple = component->pEOS->rhoVtriple;
long double rhoLtriple = component->triple_liquid.rhomolar;
long double rhoVtriple = component->triple_vapor.rhomolar;
// Check if in the "normal" region
if (HEOS._rhomolar >= rhoVtriple && HEOS._rhomolar <= rhoLtriple)
{
long double yL, yV, value, y_solid;
long double TLtriple = component->pEOS->Ttriple; //TODO: separate TL and TV for ppure
long double TVtriple = component->pEOS->Ttriple; //TODO: separate TL and TV for ppure
long double TLtriple = component->triple_liquid.T; ///TODO: separate TL and TV for ppure
long double TVtriple = component->triple_vapor.T;
// First check if solid (below the line connecting the triple point values) - this is an error for now
switch (other)
{
@@ -246,10 +246,10 @@ void FlashRoutines::PHSU_D_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
}
// Check if vapor/solid region below triple point vapor density
else if (HEOS._rhomolar < component->pEOS->rhoVtriple)
else if (HEOS._rhomolar < component->triple_vapor.rhomolar)
{
long double y, value;
long double TVtriple = component->pEOS->Ttriple; //TODO: separate TL and TV for ppure
long double TVtriple = component->triple_vapor.T; //TODO: separate TL and TV for ppure
// If value is above the value calculated from X(Ttriple, _rhomolar), it is vapor
switch (other)
@@ -275,15 +275,15 @@ void FlashRoutines::PHSU_D_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
}
else
{
throw ValueError(format("D < DLtriple"));
throw ValueError(format("D < DLtriple %g %g", value, y));
}
}
// Check in the liquid/solid region above the triple point density
else
else
{
long double y, value;
long double TLtriple = component->pEOS->Ttriple; //TODO: separate TL and TV for ppure
long double TLtriple = component->pEOS->Ttriple;
// If value is above the value calculated from X(Ttriple, _rhomolar), it is vapor
switch (other)
@@ -309,11 +309,11 @@ void FlashRoutines::PHSU_D_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
}
else
{
throw ValueError(format("D < DLtriple"));
throw ValueError(format("D < DLtriple %g %g", value, y));
}
}
}
else
else
throw NotImplementedError("PHSU_D_flash not ready for mixtures");
}
}
@@ -323,7 +323,7 @@ void FlashRoutines::HSU_P_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
}
void FlashRoutines::DHSU_T_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
{
if (HEOS.imposed_phase_index > -1)
if (HEOS.imposed_phase_index > -1)
{
// Use the phase defined by the imposed phase
HEOS._phase = HEOS.imposed_phase_index;
@@ -370,9 +370,9 @@ void FlashRoutines::DHSU_T_flash(HelmholtzEOSMixtureBackend &HEOS, int other)
default:
break;
}
HEOS.calc_pressure();
HEOS.calc_pressure();
HEOS._Q = -1;
}
}
} /* namespace CoolProp */
} /* namespace CoolProp */

View File

@@ -16,8 +16,8 @@ extern int get_debug_level();
/// A container for the fluid parameters for the CoolProp fluids
/**
This container holds copies of all of the fluid instances for the fluids that are loaded in CoolProp.
New fluids can be added by passing in a rapidjson::Value instance to the add_one function, or
This container holds copies of all of the fluid instances for the fluids that are loaded in CoolProp.
New fluids can be added by passing in a rapidjson::Value instance to the add_one function, or
a rapidjson array of fluids to the add_many function.
*/
class JSONFluidLibrary
@@ -31,9 +31,9 @@ protected:
/// Parse the contributions to the residual Helmholtz energy
void parse_alphar(rapidjson::Value &alphar, EquationOfState &EOS)
{
{
for (rapidjson::Value::ValueIterator itr = alphar.Begin(); itr != alphar.End(); ++itr)
{
{
// A reference for code cleanness
rapidjson::Value &contribution = *itr;
@@ -140,7 +140,7 @@ protected:
{
if (!alpha0.IsArray()){throw ValueError();}
for (rapidjson::Value::ValueIterator itr = alpha0.Begin(); itr != alpha0.End(); ++itr)
{
{
// A reference for code cleanness
rapidjson::Value &contribution = *itr;
@@ -172,7 +172,7 @@ protected:
// Retrieve the values
std::vector<long double> n = cpjson::get_long_double_array(contribution["n"]);
std::vector<long double> t = cpjson::get_long_double_array(contribution["t"]);
std::vector<long double> c = cpjson::get_long_double_array(contribution["c"]);
std::vector<long double> d = cpjson::get_long_double_array(contribution["d"]);
if (EOS.alpha0.PlanckEinstein.is_enabled() == true){
@@ -217,7 +217,7 @@ protected:
}
else if (!type.compare("IdealGasHelmholtzCP0AlyLee"))
{
std::vector<long double> constants = cpjson::get_long_double_array(contribution["c"]);
long double Tc = cpjson::get_double(contribution, "Tc");
long double T0 = cpjson::get_double(contribution, "T0");
@@ -249,7 +249,7 @@ protected:
c.push_back(1);
d.push_back(1);
}
if (EOS.alpha0.PlanckEinstein.is_enabled() == true){
EOS.alpha0.PlanckEinstein.extend(n, t, c, d);
}
@@ -297,38 +297,41 @@ protected:
EOS.R_u = cpjson::get_double(EOS_json,"gas_constant");
EOS.molar_mass = cpjson::get_double(EOS_json,"molar_mass");
EOS.accentric = cpjson::get_double(EOS_json,"accentric");
EOS.Ttriple = cpjson::get_double(EOS_json, "Ttriple");
EOS.ptriple = cpjson::get_double(EOS_json, "ptriple");
EOS.rhoLtriple = cpjson::get_double(EOS_json, "rhoLtriple");
EOS.rhoVtriple = cpjson::get_double(EOS_json, "rhoVtriple");
EOS.pseudo_pure = cpjson::get_bool(EOS_json, "pseudo_pure");
EOS.limits.Tmax = cpjson::get_double(EOS_json, "T_max");
EOS.limits.pmax = cpjson::get_double(EOS_json, "p_max");
rapidjson::Value &reducing_state = EOS_json["reducing_state"];
rapidjson::Value &reducing_state = EOS_json["STATES"]["reducing"];
rapidjson::Value &satminL_state = EOS_json["STATES"]["sat_min_liquid"];
rapidjson::Value &satminV_state = EOS_json["STATES"]["sat_min_vapor"];
// Reducing state
EOS.reduce.T = cpjson::get_double(reducing_state,"T");
EOS.reduce.rhomolar = cpjson::get_double(reducing_state,"rhomolar");
EOS.reduce.p = cpjson::get_double(reducing_state,"p");
/// \todo: define limits of EOS better
EOS.limits.Tmin = cpjson::get_double(satminL_state, "T");
EOS.Ttriple = EOS.limits.Tmin;
// BibTex keys
EOS.BibTeX_EOS = cpjson::get_string(EOS_json,"BibTeX_EOS");
EOS.BibTeX_CP0 = cpjson::get_string(EOS_json,"BibTeX_CP0");
parse_alphar(EOS_json["alphar"], EOS);
parse_alpha0(EOS_json["alpha0"], EOS);
// Validate the equation of state that was just created
EOS.validate();
}
/// Parse the list of possible equations of state
void parse_EOS_listing(rapidjson::Value &EOS_array, CoolPropFluid & fluid)
{
for (rapidjson::Value::ValueIterator itr = EOS_array.Begin(); itr != EOS_array.End(); ++itr)
{
{
parse_EOS(*itr,fluid);
}
@@ -436,7 +439,7 @@ protected:
if (!type.compare("modified_Batschinski_Hildebrand")){
// Get a reference to the entry in the fluid instance to simplify the code that follows
CoolProp::ViscosityModifiedBatschinskiHildebrandData &BH = fluid.transport.viscosity_higher_order.modified_Batschinski_Hildebrand;
// Set the flag for the type of this model
fluid.transport.viscosity_higher_order.type = CoolProp::ViscosityHigherOrderVariables::VISCOSITY_HIGHER_ORDER_BATSCHINKI_HILDEBRAND;
@@ -467,7 +470,7 @@ protected:
else if (!type.compare("friction_theory")){
// Get a reference to the entry in the fluid instance to simplify the code that follows
CoolProp::ViscosityFrictionTheoryData &F = fluid.transport.viscosity_higher_order.friction_theory;
// Set the flag for the type of this model
fluid.transport.viscosity_higher_order.type = CoolProp::ViscosityHigherOrderVariables::VISCOSITY_HIGHER_ORDER_FRICTION_THEORY;
@@ -476,7 +479,7 @@ protected:
F.Aa = cpjson::get_long_double_array(higher["Aa"]);
F.Aaa = cpjson::get_long_double_array(higher["Aaa"]);
F.Ar = cpjson::get_long_double_array(higher["Ar"]);
F.Na = cpjson::get_integer(higher,"Na");
F.Naa = cpjson::get_integer(higher,"Naa");
@@ -487,7 +490,7 @@ protected:
assert(F.Aa.size() == 3);
assert(F.Aaa.size() == 3);
assert(F.Ar.size() == 3);
F.T_reduce = cpjson::get_double(higher,"T_reduce");
if (higher.HasMember("Arr") && !higher.HasMember("Adrdr")){
@@ -581,7 +584,7 @@ protected:
}
}
// Load dilute viscosity term
if (viscosity.HasMember("dilute")){
@@ -596,7 +599,7 @@ protected:
parse_higher_order_viscosity(viscosity["higher_order"], fluid);
}
};
/// Parse the transport properties
void parse_dilute_conductivity(rapidjson::Value &dilute, CoolPropFluid & fluid)
{
@@ -639,7 +642,7 @@ protected:
// Load up the values
data.A = cpjson::get_long_double_array(dilute["A"]);
data.t = cpjson::get_long_double_array(dilute["t"]);
data.t = cpjson::get_long_double_array(dilute["t"]);
}
else{
throw ValueError(format("type [%s] is not understood for fluid %s",type.c_str(),fluid.name.c_str()));
@@ -732,7 +735,7 @@ protected:
throw ValueError(format("type [%s] is not understood for fluid %s",type.c_str(),fluid.name.c_str()));
}
};
/// Parse the thermal conductivity data
void parse_thermal_conductivity(rapidjson::Value &conductivity, CoolPropFluid & fluid)
{
@@ -784,7 +787,7 @@ protected:
parse_viscosity(transport["viscosity"],fluid);
}
// Parse thermal conductivity
// Parse thermal conductivity
if (transport.HasMember("conductivity")){
parse_thermal_conductivity(transport["conductivity"],fluid);
}
@@ -794,19 +797,115 @@ protected:
{
// Use the method of Chung to approximate the values for epsilon_over_k and sigma_eta
// Chung, T.-H.; Ajlan, M.; Lee, L. L.; Starling, K. E. Generalized Multiparameter Correlation for Nonpolar and Polar Fluid Transport Properties. Ind. Eng. Chem. Res. 1988, 27, 671-679.
// rhoc needs to be in mol/L to yield a sigma in nm,
// rhoc needs to be in mol/L to yield a sigma in nm,
long double rho_crit_molar = fluid.pEOS->reduce.rhomolar/1000.0;// [mol/m3 to mol/L]
long double Tc = fluid.pEOS->reduce.T;
fluid.transport.sigma_eta = 0.809/pow(rho_crit_molar, static_cast<long double>(1.0/3.0))/1e9; // 1e9 is to convert from nm to m
fluid.transport.epsilon_over_k = Tc/1.3593; // [K]
}
/// Parse the critical state for the given EOS
void parse_crit_state(rapidjson::Value &crit, CoolPropFluid & fluid)
void parse_melting_line(rapidjson::Value &melting_line, CoolPropFluid & fluid)
{
fluid.crit.T = cpjson::get_double(crit,"T");
fluid.crit.p = cpjson::get_double(crit,"p");
fluid.crit.rhomolar = cpjson::get_double(crit,"rhomolar");
fluid.ancillaries.melting_line.T_m = cpjson::get_double(melting_line, "T_m");
fluid.ancillaries.melting_line.BibTeX = cpjson::get_string(melting_line, "BibTeX");
if (melting_line.HasMember("type"))
{
std::string type = cpjson::get_string(melting_line, "type");
if (!type.compare("Simon"))
{
rapidjson::Value &parts = melting_line["parts"];
for (rapidjson::Value::ValueIterator itr = parts.Begin(); itr != parts.End(); ++itr)
{
MeltingLinePiecewiseSimonSegment data;
data.a = cpjson::get_double((*itr),"a");
data.c = cpjson::get_double((*itr),"c");
data.T_min = cpjson::get_double((*itr),"T_min");
data.T_max = cpjson::get_double((*itr),"T_max");
data.T_0 = cpjson::get_double((*itr),"T_0");
data.p_0 = cpjson::get_double((*itr),"p_0");
fluid.ancillaries.melting_line.simon.parts.push_back(data);
}
fluid.ancillaries.melting_line.type = MeltingLineVariables::MELTING_LINE_SIMON_TYPE;
}
else if (!type.compare("polynomial_in_Tr"))
{
rapidjson::Value &parts = melting_line["parts"];
for (rapidjson::Value::ValueIterator itr = parts.Begin(); itr != parts.End(); ++itr)
{
MeltingLinePiecewisePolynomialInTrSegment data;
data.a = cpjson::get_long_double_array((*itr),"a");
data.t = cpjson::get_long_double_array((*itr),"t");
data.T_min = cpjson::get_double((*itr),"T_min");
data.T_max = cpjson::get_double((*itr),"T_max");
data.T_0 = cpjson::get_double((*itr),"T_0");
data.p_0 = cpjson::get_double((*itr),"p_0");
fluid.ancillaries.melting_line.polynomial_in_Tr.parts.push_back(data);
}
fluid.ancillaries.melting_line.type = MeltingLineVariables::MELTING_LINE_POLYNOMIAL_IN_TR_TYPE;
}
else if (!type.compare("polynomial_in_Theta"))
{
rapidjson::Value &parts = melting_line["parts"];
for (rapidjson::Value::ValueIterator itr = parts.Begin(); itr != parts.End(); ++itr)
{
MeltingLinePiecewisePolynomialInThetaSegment data;
data.a = cpjson::get_long_double_array((*itr),"a");
data.t = cpjson::get_long_double_array((*itr),"t");
data.T_min = cpjson::get_double((*itr),"T_min");
data.T_max = cpjson::get_double((*itr),"T_max");
data.T_0 = cpjson::get_double((*itr),"T_0");
data.p_0 = cpjson::get_double((*itr),"p_0");
fluid.ancillaries.melting_line.polynomial_in_Theta.parts.push_back(data);
}
fluid.ancillaries.melting_line.type = MeltingLineVariables::MELTING_LINE_POLYNOMIAL_IN_THETA_TYPE;
}
else{
throw ValueError(format("melting line type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
}
}
else{
throw ValueError(format("melting line does not have \"type\" for fluid %s", fluid.name.c_str()));
}
};
/// Parse the critical state for the given EOS
void parse_states(rapidjson::Value &states, CoolPropFluid & fluid)
{
if (!states.HasMember("critical")){ throw ValueError(format("fluid[\"STATES\"] [%s] does not have \"critical\" member",fluid.name.c_str())); }
rapidjson::Value &crit = states["critical"];
fluid.crit.T = cpjson::get_double(crit, "T");
fluid.crit.p = cpjson::get_double(crit, "p");
fluid.crit.rhomolar = cpjson::get_double(crit, "rhomolar");
if (!states.HasMember("triple_liquid")){ throw ValueError(format("fluid[\"STATES\"] [%s] does not have \"triple_liquid\" member",fluid.name.c_str())); }
rapidjson::Value &triple_liquid = states["triple_liquid"];
if (triple_liquid.ObjectEmpty()){
// State is empty - probably because the triple point temperature is below the minimum saturation temperature
fluid.triple_liquid.T = -1;
fluid.triple_liquid.p = -1;
fluid.triple_liquid.rhomolar = -1;
}
else{
fluid.triple_liquid.T = cpjson::get_double(triple_liquid, "T");
fluid.triple_liquid.p = cpjson::get_double(triple_liquid, "p");
fluid.triple_liquid.rhomolar = cpjson::get_double(triple_liquid, "rhomolar");
}
if (!states.HasMember("triple_vapor")){ throw ValueError(format("fluid[\"STATES\"] [%s] does not have \"triple_vapor\" member",fluid.name.c_str())); }
rapidjson::Value &triple_vapor = states["triple_vapor"];
if (triple_vapor.ObjectEmpty()){
// State is empty - probably because the triple point temperature is below the minimum saturation temperature
fluid.triple_vapor.T = -1;
fluid.triple_vapor.p = -1;
fluid.triple_vapor.rhomolar = -1;
}
else{
fluid.triple_vapor.T = cpjson::get_double(triple_vapor, "T");
fluid.triple_vapor.p = cpjson::get_double(triple_vapor, "p");
fluid.triple_vapor.rhomolar = cpjson::get_double(triple_vapor, "rhomolar");
}
};
/// Parse the critical state for the given EOS
@@ -833,7 +932,7 @@ protected:
assert(fluid.name.length() > 0);
}
public:
// Default constructor;
JSONFluidLibrary(){
_is_empty = true;
@@ -844,14 +943,14 @@ public:
void add_many(rapidjson::Value &listing)
{
for (rapidjson::Value::ValueIterator itr = listing.Begin(); itr != listing.End(); ++itr)
{
{
add_one(*itr);
}
};
void add_one(rapidjson::Value &fluid_json)
{
_is_empty = false;
// Get the next index for this fluid
std::size_t index = fluid_map.size();
@@ -863,73 +962,93 @@ public:
// Fluid name
fluid.name = fluid_json["NAME"].GetString(); name_vector.push_back(fluid.name);
// CAS number
fluid.CAS = fluid_json["CAS"].GetString();
// REFPROP alias
fluid.REFPROPname = fluid_json["REFPROP_NAME"].GetString();
// Critical state
parse_crit_state(fluid_json["CRITICAL"], fluid);
if (get_debug_level() > 5){
std::cout << format("Loading fluid %s with CAS %s; %d fluids loaded\n", fluid.name.c_str(), fluid.CAS.c_str(), index);
}
try{
// CAS number
if (!fluid_json.HasMember("CAS")){ throw ValueError(format("fluid [%s] does not have \"CAS\" member",fluid.name.c_str())); }
fluid.CAS = fluid_json["CAS"].GetString();
// REFPROP alias
if (!fluid_json.HasMember("REFPROP_NAME")){ throw ValueError(format("fluid [%s] does not have \"REFPROP_NAME\" member",fluid.name.c_str())); }
fluid.REFPROPname = fluid_json["REFPROP_NAME"].GetString();
// Critical state
if (!fluid_json.HasMember("STATES")){ throw ValueError(format("fluid [%s] does not have \"STATES\" member",fluid.name.c_str())); }
parse_states(fluid_json["STATES"], fluid);
// Aliases
fluid.aliases = cpjson::get_string_array(fluid_json["ALIASES"]);
// EOS
parse_EOS_listing(fluid_json["EOS"], fluid);
// Validate the fluid
validate(fluid);
// Ancillaries for saturation
if (!fluid_json.HasMember("ANCILLARIES")){throw ValueError(format("Ancillary curves are missing for fluid [%s]",fluid.name.c_str()));};
parse_ancillaries(fluid_json["ANCILLARIES"],fluid);
// Surface tension
if (!(fluid_json["ANCILLARIES"].HasMember("surface_tension"))){
if (get_debug_level() > 0){
std::cout << format("Surface tension curves are missing for fluid [%s]\n", fluid.name.c_str()) ;
if (get_debug_level() > 5){
std::cout << format("Loading fluid %s with CAS %s; %d fluids loaded\n", fluid.name.c_str(), fluid.CAS.c_str(), index);
}
}
else{
parse_surface_tension(fluid_json["ANCILLARIES"]["surface_tension"], fluid);
}
// Parse the environmental parameters
if (!(fluid_json.HasMember("ENVIRONMENTAL"))){
if (get_debug_level() > 0){
std::cout << format("Environmental data are missing for fluid [%s]\n", fluid.name.c_str()) ;
// Aliases
fluid.aliases = cpjson::get_string_array(fluid_json["ALIASES"]);
// EOS
parse_EOS_listing(fluid_json["EOS"], fluid);
// Validate the fluid
validate(fluid);
// Ancillaries for saturation
if (!fluid_json.HasMember("ANCILLARIES")){throw ValueError(format("Ancillary curves are missing for fluid [%s]",fluid.name.c_str()));};
parse_ancillaries(fluid_json["ANCILLARIES"],fluid);
// Surface tension
if (!(fluid_json["ANCILLARIES"].HasMember("surface_tension"))){
if (get_debug_level() > 0){
std::cout << format("Surface tension curves are missing for fluid [%s]\n", fluid.name.c_str()) ;
}
}
else{
parse_surface_tension(fluid_json["ANCILLARIES"]["surface_tension"], fluid);
}
}
else{
parse_environmental(fluid_json["ENVIRONMENTAL"], fluid);
}
// Parse the environmental parameters
if (!(fluid_json.HasMember("TRANSPORT"))){
default_transport(fluid);
}
else{
parse_transport(fluid_json["TRANSPORT"], fluid);
}
// If the fluid is ok...
// Melting line
if (!(fluid_json["ANCILLARIES"].HasMember("melting_line"))){
if (get_debug_level() > 0){
std::cout << format("Melting line curves are missing for fluid [%s]\n", fluid.name.c_str()) ;
}
}
else{
parse_melting_line(fluid_json["ANCILLARIES"]["melting_line"], fluid);
}
// Add CAS->index mapping
string_to_index_map[fluid.CAS] = index;
// Parse the environmental parameters
if (!(fluid_json.HasMember("ENVIRONMENTAL"))){
if (get_debug_level() > 0){
std::cout << format("Environmental data are missing for fluid [%s]\n", fluid.name.c_str()) ;
}
}
else{
parse_environmental(fluid_json["ENVIRONMENTAL"], fluid);
}
// Parse the environmental parameters
if (!(fluid_json.HasMember("TRANSPORT"))){
default_transport(fluid);
}
else{
parse_transport(fluid_json["TRANSPORT"], fluid);
}
// If the fluid is ok...
// Add CAS->index mapping
string_to_index_map[fluid.CAS] = index;
// Add name->index mapping
string_to_index_map[fluid.name] = index;
// Add the aliases
for (std::size_t i = 0; i < fluid.aliases.size(); ++i)
{
string_to_index_map[fluid.aliases[i]] = index;
}
if (get_debug_level() > 5){ std::cout << format("Loaded.\n"); }
// Add name->index mapping
string_to_index_map[fluid.name] = index;
// Add the aliases
for (std::size_t i = 0; i < fluid.aliases.size(); ++i)
{
string_to_index_map[fluid.aliases[i]] = index;
}
if (get_debug_level() > 5){ std::cout << format("Loaded.\n"); }
catch (const std::exception &e){
throw ValueError(format("Unable to load fluid [%s] due to error: %s",fluid.name.c_str(),e.what()));
}
};
/// Get a CoolPropFluid instance stored in this library
/**
@@ -982,4 +1101,4 @@ std::string get_fluid_list(void);
CoolPropFluid& get_fluid(std::string fluid_string);
} /* namespace CoolProp */
#endif
#endif

View File

@@ -115,6 +115,17 @@ long double HelmholtzEOSMixtureBackend::calc_molar_mass(void)
}
return summer;
}
long double HelmholtzEOSMixtureBackend::calc_melt_p_T(long double T)
{
if (is_pure_or_pseudopure)
{
return components[0]->ancillaries.melting_line.evaluate(iP, iT, T);
}
else
{
throw NotImplementedError(format("calc_melt_p_T not implemented for mixtures"));
}
}
long double HelmholtzEOSMixtureBackend::calc_surface_tension(void)
{
if (is_pure_or_pseudopure)
@@ -1174,8 +1185,8 @@ long double HelmholtzEOSMixtureBackend::solver_for_rho_given_T_oneof_HSU(long do
// Supercritical temperature
if (_T > _crit.T)
{
long double rhomelt = components[0]->pEOS->rhoLtriple;
long double rhoc = components[0]->pEOS->reduce.rhomolar;
long double rhomelt = components[0]->triple_liquid.rhomolar;
long double rhoc = components[0]->crit.rhomolar;
long double rhomin = 1e-10;
switch(other)
@@ -1228,7 +1239,7 @@ long double HelmholtzEOSMixtureBackend::solver_for_rho_given_T_oneof_HSU(long do
else if (_phase == iphase_liquid)
{
long double ymelt, yL, y;
long double rhomelt = components[0]->pEOS->rhoLtriple;
long double rhomelt = components[0]->triple_liquid.rhomolar;
long double rhoL = static_cast<double>(_rhoLanc);
switch(other)
@@ -1591,8 +1602,8 @@ long double HelmholtzEOSMixtureBackend::calc_speed_sound(void)
long double d2ar_dDelta2 = d2alphar_dDelta2();
long double d2ar_dDelta_dTau = d2alphar_dDelta_dTau();
long double d2ar_dTau2 = d2alphar_dTau2();
long double R_u = static_cast<long double>(_gas_constant);
long double mm = static_cast<long double>(_molar_mass);
long double R_u = gas_constant();
long double mm = molar_mass();
// Get speed of sound
_speed_sound = sqrt(R_u*_T/mm*(1+2*_delta.pt()*dar_dDelta+pow(_delta.pt(),2)*d2ar_dDelta2 - pow(1+_delta.pt()*dar_dDelta-_delta.pt()*_tau.pt()*d2ar_dDelta_dTau,2)/(pow(_tau.pt(),2)*(d2ar_dTau2 + d2a0_dTau2))));

View File

@@ -17,10 +17,10 @@ class FlashRoutines;
class HelmholtzEOSMixtureBackend : public AbstractState {
protected:
std::vector<CoolPropFluid*> components; ///< The components that are in use
bool is_pure_or_pseudopure; ///< A flag for whether the substance is a pure or pseudo-pure fluid (true) or a mixture (false)
std::vector<long double> mole_fractions; ///< The mole fractions of the components
std::vector<long double> mole_fractions_liq, ///< The mole fractions of the saturated liquid
std::vector<long double> mole_fractions_liq, ///< The mole fractions of the saturated liquid
mole_fractions_vap, ///< The mole fractions of the saturated vapor
K, ///< The K factors for the components
lnK; ///< The natural logarithms of the K factors of the components
@@ -37,7 +37,7 @@ public:
friend class FlashRoutines; // Allows the static methods in the FlashRoutines class to have access to all the protected members and methods of this class
friend class TransportRoutines; // Allows the static methods in the TransportRoutines class to have access to all the protected members and methods of this class
//friend class MixtureDerivatives; //
//friend class MixtureDerivatives; //
// Helmholtz EOS backend uses mole fractions
bool using_mole_fractions(){return true;}
@@ -46,7 +46,7 @@ public:
std::vector<long double> &get_K(){return K;};
std::vector<long double> &get_lnK(){return lnK;};
shared_ptr<HelmholtzEOSMixtureBackend> SatL, SatV; ///<
shared_ptr<HelmholtzEOSMixtureBackend> SatL, SatV; ///<
void update(long input_pair, double value1, double value2);
@@ -69,19 +69,19 @@ public:
void set_excess_term();
/// Set the mole fractions
/**
/**
@param mole_fractions The vector of mole fractions of the components
*/
void set_mole_fractions(const std::vector<long double> &mole_fractions);
const std::vector<long double> &get_mole_fractions(){return mole_fractions;};
/// Set the mass fractions
/**
/**
@param mass_fractions The vector of mass fractions of the components
*/
void set_mass_fractions(const std::vector<long double> &mass_fractions){throw std::exception();};
long double calc_molar_mass(void);
long double calc_gas_constant(void);
@@ -118,6 +118,7 @@ public:
long double calc_d2alpha0_dDelta_dTau(void);
long double calc_d2alpha0_dTau2(void);
long double calc_melt_p_T(long double T);
long double calc_surface_tension(void);
long double calc_viscosity(void);
long double calc_viscosity_dilute(void);
@@ -137,7 +138,7 @@ public:
std::string calc_name(void);
long double calc_alphar_deriv_nocache(const int nTau, const int nDelta, const std::vector<long double> & mole_fractions, const long double &tau, const long double &delta);
/**
\brief Take derivatives of the ideal-gas part of the Helmholtz energy, don't use any cached values, or store any cached values
@@ -163,7 +164,7 @@ public:
\sa Table B5, GERG 2008 from Kunz Wagner, JCED, 2012
*/
long double calc_alpha0_deriv_nocache(const int nTau, const int nDelta, const std::vector<long double> & mole_fractions, const long double &tau, const long double &delta, const long double &Tr, const long double &rhor);
void calc_reducing_state(void);
SimpleState calc_reducing_state_nocache(const std::vector<long double> & mole_fractions);
@@ -183,11 +184,11 @@ public:
void mass_to_molar_inputs(long &input_pair, double &value1, double &value2);
// ***************************************************************
// ***************************************************************
// ***************************************************************
// ************* PHASE DETERMINATION ROUTINES ******************
// ***************************************************************
// ***************************************************************
// ***************************************************************
void T_phase_determination_pure_or_pseudopure(int other, long double value);
void p_phase_determination_pure_or_pseudopure(int other, long double value);
void DmolarP_phase_determination();
@@ -197,8 +198,8 @@ public:
// ***************************************************************
// ******************* SOLVER ROUTINES *************************
// ***************************************************************
// ***************************************************************
// ***************************************************************
long double solver_rho_Tp(long double T, long double p, long double rho_guess = -1);
long double solver_rho_Tp_SRK(long double T, long double p, int phase);
long double solver_for_rho_given_T_oneof_HSU(long double T, long double value, int other);
@@ -310,7 +311,7 @@ public:
\f[
n\left(\frac{\partial \ln \phi_i}{\partial n_j}\right)_{T,p} = n\left(\frac{\partial^2n\alpha^r}{\partial n_j \partial n_i} \right)_{T,V}+1+\frac{n}{RT}\frac{\left(\frac{\partial p}{\partial n_j}\right)_{T,V,n_i}\left(\frac{\partial p}{\partial n_i}\right)_{T,V,n_j}}{\left(\frac{\partial p}{\partial V}\right)_{T,\bar n}}
\f]
which is also equal to
which is also equal to
\f[
n\left(\frac{\partial \ln \phi_i}{\partial n_j}\right)_{T,p} = n\left(\frac{\partial^2n\alpha^r}{\partial n_j \partial n_i} \right)_{T,V}+1-\frac{\hat v_i}{RT}\left[n\left(\frac{\partial p}{\partial n_j}\right)_{T,V,n_i}\right]
\f]
@@ -320,7 +321,7 @@ public:
/// Gernert Equation 3.115
/// Catch test provided
long double mixderiv_dln_fugacity_coefficient_dxj__constT_p_xi(int i, int j);
/// Gernert Equation 3.130
/// Catch test provided
long double mixderiv_dpdxj__constT_V_xi(int j);
@@ -390,7 +391,7 @@ public:
\f]
GERG 2004 Monograph equation 7.47:
\f{eqnarray*}{
n\left( \frac{\partial}{\partial n_j}\left(n\left(\frac{\partial \alpha^r}{\partial n_i}\right)_{T,V,n_j} \right) \right)_{T,V,n_i} &=& \left( \frac{\partial}{\partial \delta}\left(n\left(\frac{\partial\alpha^r}{\partial n_i}\right)_{T,V,n_j}\right)\right)_{\tau,\bar x}\cdot n\left(\frac{\partial\delta}{\partial n_j}\right)_{T,V,n_i}\\
n\left( \frac{\partial}{\partial n_j}\left(n\left(\frac{\partial \alpha^r}{\partial n_i}\right)_{T,V,n_j} \right) \right)_{T,V,n_i} &=& \left( \frac{\partial}{\partial \delta}\left(n\left(\frac{\partial\alpha^r}{\partial n_i}\right)_{T,V,n_j}\right)\right)_{\tau,\bar x}\cdot n\left(\frac{\partial\delta}{\partial n_j}\right)_{T,V,n_i}\\
&+& \left( \frac{\partial}{\partial \tau}\left(n\left(\frac{\partial\alpha^r}{\partial n_i}\right)_{T,V,n_j}\right)\right)_{\tau,\bar x}\cdot n\left(\frac{\partial\tau}{\partial n_j}\right)_{T,V,n_i}\\
&+& \left( \frac{\partial}{\partial x_j}\left(n\left(\frac{\partial\alpha^r}{\partial n_i}\right)_{T,V,n_j}\right)\right)_{\delta,\tau,x_i}-\sum_{k=1}^{N}x_k \left( \frac{\partial}{\partial x_k}\left(n\left(\frac{\partial\alpha^r}{\partial n_i}\right)_{T,V,n_j}\right)\right)_{\delta,\tau,x_i}\\
\f}
@@ -404,7 +405,7 @@ public:
\f]
*/
long double mixderiv_nddeltadni__constT_V_nj(int i);
/// GERG 2004 Monograph equation 7.49
/** The derivative term
\f[

View File

@@ -11,13 +11,13 @@ long double TransportRoutines::viscosity_dilute_kinetic_theory(HelmholtzEOSMixtu
long double Tstar = HEOS.T()/HEOS.components[0]->transport.epsilon_over_k;
long double sigma_nm = HEOS.components[0]->transport.sigma_eta*1e9; // 1e9 to convert from m to nm
long double molar_mass_kgkmol = HEOS.molar_mass()*1000; // 1000 to convert from kg/mol to kg/kmol
// The nondimensional empirical collision integral from Neufeld
// Neufeld, P. D.; Janzen, A. R.; Aziz, R. A. Empirical Equations to Calculate 16 of the Transport Collision Integrals (l,s)*
// The nondimensional empirical collision integral from Neufeld
// Neufeld, P. D.; Janzen, A. R.; Aziz, R. A. Empirical Equations to Calculate 16 of the Transport Collision Integrals (l,s)*
// for the Lennard-Jones (12-6) Potential. J. Chem. Phys. 1972, 57, 1100-1102
long double OMEGA22 = 1.16145*pow(Tstar, static_cast<long double>(-0.14874))+0.52487*exp(-0.77320*Tstar)+2.16178*exp(-2.43787*Tstar);
// The dilute gas component -
// The dilute gas component -
return 26.692e-9*sqrt(molar_mass_kgkmol*HEOS.T())/(pow(sigma_nm, 2)*OMEGA22); // Pa-s
}
else{
@@ -40,8 +40,8 @@ long double TransportRoutines::viscosity_dilute_collision_integral(HelmholtzEOSM
const long double sigma_nm = HEOS.components[0]->transport.sigma_eta*1e9; // 1e9 to convert from m to nm
const long double molar_mass_kgkmol = molar_mass*1000; // 1000 to convert from kg/mol to kg/kmol
/// Both the collision integral \f$\mathfrak{S}^*\f$ and effective cross section \f$\Omega^{(2,2)}\f$ have the same form,
/// in general we don't care which is used. The are related through \f$\Omega^{(2,2)} = (5/4)\mathfrak{S}^*\f$
/// Both the collision integral \f$\mathfrak{S}^*\f$ and effective cross section \f$\Omega^{(2,2)}\f$ have the same form,
/// in general we don't care which is used. The are related through \f$\Omega^{(2,2)} = (5/4)\mathfrak{S}^*\f$
/// see Vesovic(JPCRD, 1990) for CO\f$_2\f$ for further information
long double summer = 0, lnTstar = log(Tstar);
for (std::size_t i = 0; i < a.size(); ++i)
@@ -49,7 +49,7 @@ long double TransportRoutines::viscosity_dilute_collision_integral(HelmholtzEOSM
summer += a[i]*pow(lnTstar,t[i]);
}
S = exp(summer);
// The dilute gas component
return C*sqrt(molar_mass_kgkmol*HEOS.T())/(pow(sigma_nm, 2)*S); // Pa-s
}
@@ -148,7 +148,7 @@ long double TransportRoutines::viscosity_initial_density_dependence_Rainwater_Fr
long double B_eta, B_eta_star;
long double Tstar = HEOS.T()/HEOS.components[0]->transport.epsilon_over_k; // [no units]
long double sigma = HEOS.components[0]->transport.sigma_eta; // [m]
long double summer = 0;
for (unsigned int i = 0; i < b.size(); ++i){
summer += b[i]*pow(Tstar, t[i]);
@@ -225,7 +225,7 @@ long double TransportRoutines::viscosity_water_hardcoded(HelmholtzEOSMixtureBack
double x_mu=0.068,qc=1/1.9,qd=1/1.1,nu=0.630,gamma=1.239,zeta_0=0.13,LAMBDA_0=0.06,Tbar_R=1.5, pstar, Tstar, rhostar;
double delta,tau,mubar_0,mubar_1,mubar_2,drhodp,drhodp_R,DeltaChibar,zeta,w,L,Y,psi_D,Tbar,rhobar;
double drhobar_dpbar,drhobar_dpbar_R,R_Water;
pstar = 22.064e6; // [Pa]
Tstar = 647.096; // [K]
rhostar = 322; // [kg/m^3]
@@ -246,7 +246,7 @@ long double TransportRoutines::viscosity_water_hardcoded(HelmholtzEOSMixtureBack
tau=1/Tbar_R;
drhodp_R=1/(R_Water*Tbar_R*Tstar*(1+2*rhobar*HEOS.calc_alphar_deriv_nocache(0,1,HEOS.mole_fractions,tau,delta)+delta*delta*HEOS.calc_alphar_deriv_nocache(0,2,HEOS.mole_fractions,tau, delta)));
drhobar_dpbar_R = pstar/rhostar*drhodp_R;
DeltaChibar=rhobar*(drhobar_dpbar-drhobar_dpbar_R*Tbar_R/Tbar);
if (DeltaChibar<0)
DeltaChibar=0;
@@ -330,7 +330,7 @@ long double TransportRoutines::viscosity_higher_order_friction_theory(HelmholtzE
double deltapr = pr - pid;
double eta_f = ka*pa + kr*deltapr + ki*pid + kaa*pa*pa + kdrdr*deltapr*deltapr + krr*pr*pr + kii*pid*pid + krrr*pr*pr*pr + kaaa*pa*pa*pa;
return eta_f; //[Pa-s]
}
else{
@@ -345,10 +345,10 @@ long double TransportRoutines::viscosity_helium_hardcoded(HelmholtzEOSMixtureBac
double eta_0,eta_0_slash, eta_E_slash, B,C,D,ln_eta,x;
//
// Arp, V.D., McCarty, R.D., and Friend, D.G.,
// "Thermophysical Properties of Helium-4 from 0.8 to 1500 K with Pressures to 2000 MPa",
// "Thermophysical Properties of Helium-4 from 0.8 to 1500 K with Pressures to 2000 MPa",
// NIST Technical Note 1334 (revised), 1998.
//
// Using Arp NIST report
//
// Using Arp NIST report
// Report is not clear on viscosity, referring to REFPROP source code for clarity
// Correlation wants density in g/cm^3; kg/m^3 --> g/cm^3, divide by 1000
@@ -391,9 +391,6 @@ long double TransportRoutines::viscosity_R23_hardcoded(HelmholtzEOSMixtureBacken
rhocbar = 7.5114,
Tc = 299.2793,
DELTAeta_max = 3.967,
k = 1.380658e-23,
N_A = 6.022137e23, // 1/mol
pi = 3.141592654, //
Ru = 8.31451,
molar_mass = 70.014;
@@ -418,7 +415,7 @@ long double TransportRoutines::viscosity_R23_hardcoded(HelmholtzEOSMixtureBacken
long double TransportRoutines::viscosity_dilute_ethane(HelmholtzEOSMixtureBackend &HEOS)
{
double C[] = {0, -3.0328138281, 16.918880086, -37.189364917, 41.288861858, -24.615921140, 8.9488430959, -1.8739245042, 0.20966101390, -9.6570437074e-3};
double OMEGA_2_2 = 0, e_k = 245, sigma = 0.43682, Tstar;
double OMEGA_2_2 = 0, e_k = 245, Tstar;
Tstar = HEOS.T()/e_k;
for (int i = 1; i<= 9; i++)
@@ -572,7 +569,7 @@ long double TransportRoutines::conductivity_critical_hardcoded_R123(HelmholtzEOS
long double TransportRoutines::conductivity_critical_hardcoded_CO2_ScalabrinJPCRD2006(HelmholtzEOSMixtureBackend &HEOS){
long double nc = 0.775547504e-3*4.81384, Tr = HEOS.T()/304.1282, alpha, rhor = HEOS.keyed_output(iDmass)/467.6;
static long double a[] = {0.0, 3.0, 6.70697, 0.94604, 0.30, 0.30, 0.39751, 0.33791, 0.77963, 0.79857, 0.90, 0.02, 0.20};
// Equation 6 from Scalabrin
alpha = 1-a[10]*acosh(1+a[11]*pow(pow(1-Tr,2),a[12]));
@@ -598,8 +595,8 @@ long double TransportRoutines::conductivity_dilute_hardcoded_CO2(HelmholtzEOSMix
//Vesovic Eq. 12 [no units]
double r = sqrt(2.0/5.0*cint_k);
// According to REFPROP, 1+r^2 = cp-2.5R. This is unclear to me but seems to suggest that cint/k is the difference
// between the ideal gas specific heat and a monatomic specific heat of 5/2*R. Using the form of cint/k from Vesovic
// According to REFPROP, 1+r^2 = cp-2.5R. This is unclear to me but seems to suggest that cint/k is the difference
// between the ideal gas specific heat and a monatomic specific heat of 5/2*R. Using the form of cint/k from Vesovic
// does not yield exactly the correct values
Tstar = HEOS.T()/e_k;
@@ -670,15 +667,14 @@ long double TransportRoutines::conductivity_hardcoded_water(HelmholtzEOSMixtureB
// Finite density contribution
lambdabar_1=exp(rhobar*sum);
double nu=0.630,GAMMA =177.8514,gamma=1.239,xi_0=0.13,Lambda_0=0.06,Tr_bar=1.5,tau_ref = Tr_bar*Tstar/HEOS.T(),
double nu=0.630,GAMMA =177.8514,gamma=1.239,xi_0=0.13,Lambda_0=0.06,Tr_bar=1.5,
qd_bar=1/0.4,pi=3.141592654, delta = HEOS.delta(), R=461.51805;//J/kg/K
tau=1/Tbar;
double drhodp = 1/(R*HEOS.T()*(1+2*rhobar*HEOS.dalphar_dDelta()+rhobar*rhobar*HEOS.d2alphar_dDelta2()));
double drhobar_dpbar = pstar/rhostar*drhodp;
double drhodp_Trbar = 1/(R*Tr_bar*Tstar*(1+2*rhobar*HEOS.calc_alphar_deriv_nocache(0,1,HEOS.mole_fractions,1/Tr_bar,delta)+delta*delta*HEOS.calc_alphar_deriv_nocache(0,2,HEOS.mole_fractions,1/Tr_bar,delta)));
double drhobar_dpbar_Trbar = pstar/rhostar*drhodp_Trbar;
double cpmol = HEOS.cpmolar(); // [J/mol/K]
double cp = HEOS.cpmass(); // [J/kg/K]
double cv = HEOS.cvmass(); // [J/kg/K]
double cpbar = cp/R; //[-]
@@ -731,17 +727,17 @@ long double TransportRoutines::conductivity_hardcoded_R23(HelmholtzEOSMixtureBac
long double TransportRoutines::conductivity_critical_hardcoded_ammonia(HelmholtzEOSMixtureBackend &HEOS){
/*
From "Thermal Conductivity of Ammonia in a Large
/*
From "Thermal Conductivity of Ammonia in a Large
Temperature and Pressure Range Including the Critical Region"
by R. Tufeu, D.Y. Ivanov, Y. Garrabos, B. Le Neindre,
by R. Tufeu, D.Y. Ivanov, Y. Garrabos, B. Le Neindre,
Bereicht der Bunsengesellschaft Phys. Chem. 88 (1984) 422-427
*/
double T = HEOS.T(), Tc = 405.4, rhoc = 235, rho;
double LAMBDA=1.2, nu=0.63, gamma =1.24, DELTA=0.50,t,zeta_0_plus=1.34e-10,a_zeta=1,GAMMA_0_plus=0.423e-8;
double pi=3.141592654,a_chi,k_B=1.3806504e-23,X_T,DELTA_lambda,dPdT,eta_B,DELTA_lambda_id,DELTA_lambda_i;
rho = HEOS.keyed_output(CoolProp::iDmass);
t = fabs((T-Tc)/Tc);
a_chi = a_zeta/0.7;
@@ -785,14 +781,14 @@ long double TransportRoutines::conductivity_hardcoded_helium(HelmholtzEOSMixture
lambda_e = (c[0]+c[1]*T+c[2]*pow(T,1/3.0)+c[3]*pow(T,2.0/3.0))*rho
+(c[4]+c[5]*pow(T,1.0/3.0)+c[6]*pow(T,2.0/3.0))*rho*rho*rho
+(c[7]+c[8]*pow(T,1.0/3.0)+c[9]*pow(T,2.0/3.0)+c[10]/T)*rho*rho*log(rho/rhoc);
// Critical component
lambda_c = 0.0;
if (3.5 < T && T < 12)
{
double x0 = 0.392, E1 = 2.8461, E2 = 0.27156, beta = 0.3554, gamma = 1.1743, delta = 4.304, rhoc_crit = 69.158,
Tc = 5.18992, pc = 2.2746e5, R = 4.633e-10, m = 6.6455255e-27, k = 1.38066e-23, pi = M_PI;
double x0 = 0.392, E1 = 2.8461, E2 = 0.27156, beta = 0.3554, gamma = 1.1743, delta = 4.304, rhoc_crit = 69.158,
Tc = 5.18992, pc = 2.2746e5;
double DeltaT = fabs(1-T/Tc), DeltaRho = fabs(1-rho/rhoc_crit);
double eta = HEOS.viscosity(); // [Pa-s]
@@ -810,7 +806,7 @@ long double TransportRoutines::conductivity_hardcoded_helium(HelmholtzEOSMixture
double x = pow(DeltaT/DeltaRho,1/beta);
double h = E1*(1 + x/x0)*pow(1 + E2*pow(1 + x/x0, 2/beta), (gamma-1)/(2*beta));
/**
/**
dh/dx derived using sympy:
E1,x,x0,E2,beta,gamma = symbols('E1,x,x0,E2,beta,gamma')
@@ -824,7 +820,7 @@ long double TransportRoutines::conductivity_hardcoded_helium(HelmholtzEOSMixture
K_Tbar = W*K_T + (1-W)*K_Tprime;
}
// 3.4685233d-17 and 3.726229668d0 are "magical" coefficients that are present in the REFPROP source to yield the right values. Not clear why these values are needed.
// 3.4685233d-17 and 3.726229668d0 are "magical" coefficients that are present in the REFPROP source to yield the right values. Not clear why these values are needed.
// Also, the form of the critical term in REFPROP does not agree with Hands paper. EL and MH from NIST are not sure where these coefficients come from.
lambda_c = 3.4685233e-17*3.726229668*sqrt(K_Tbar)*pow(T,2)/rho/eta*pow(dpdT,2)*exp(-18.66*pow(DeltaT,2)-4.25*pow(DeltaRho,4));
}
@@ -843,7 +839,7 @@ long double TransportRoutines::viscosity_ECS(HelmholtzEOSMixtureBackend &HEOS, H
// Get a reference to the ECS data
CoolProp::ViscosityECSVariables &ECS = HEOS.components[0]->transport.viscosity_ecs;
// The correction polynomial psi_eta
double psi = 0;
for (std::size_t i=0; i < ECS.psi_a.size(); i++){
@@ -897,7 +893,7 @@ long double TransportRoutines::conductivity_ECS(HelmholtzEOSMixtureBackend &HEOS
// Get a reference to the ECS data
CoolProp::ConductivityECSVariables &ECS = HEOS.components[0]->transport.conductivity_ecs;
// The correction polynomial psi_eta in rho/rho_red
double psi = 0;
for (std::size_t i=0; i < ECS.psi_a.size(); ++i){
@@ -954,4 +950,4 @@ long double TransportRoutines::conductivity_ECS(HelmholtzEOSMixtureBackend &HEOS
}
}; /* namespace CoolProp */
}; /* namespace CoolProp */

View File

@@ -11,23 +11,21 @@
//#include "CoolProp.h"
#include "IncompressibleBackend.h"
#include "../Helmholtz/Fluids/FluidLibrary.h"
#include "IncompressibleFluid.h"
#include "IncompressibleLibrary.h"
#include "Solvers.h"
#include "MatrixMath.h"
namespace CoolProp {
IncompressibleBackend::IncompressibleBackend(const std::string &fluid_name) {
throw CoolProp::NotImplementedError("Mixture-style constructor is not implemented yet for incompressible fluids");
fluid = &get_incompressible_fluid(fluid_name);
}
IncompressibleBackend::IncompressibleBackend(const std::vector<std::string> &component_names) {
throw CoolProp::NotImplementedError("Mixture-style constructor is not implemented yet for incompressible fluids");
throw NotImplementedError("Mixture-style constructor is not implemented yet for incompressible fluids");
}
bool IncompressibleBackend::using_mole_fractions(){return false;}
void IncompressibleBackend::update(long input_pair, double value1, double value2) {
switch (input_pair) {
case PT_INPUTS: {

View File

@@ -2,6 +2,7 @@
#ifndef INCOMPRESSIBLEBACKEND_H_
#define INCOMPRESSIBLEBACKEND_H_
#include "IncompressibleFluid.h"
#include "AbstractState.h"
#include "Exceptions.h"
@@ -15,6 +16,7 @@ protected:
bool _mole_fractions_set;
static bool _REFPROP_supported;
std::vector<long double> mass_fractions;
IncompressibleFluid *fluid;
public:
IncompressibleBackend(){};
virtual ~IncompressibleBackend(){};
@@ -27,10 +29,10 @@ public:
IncompressibleBackend(const std::vector<std::string> &component_names);
// Incompressible backend uses mole fractions
bool using_mole_fractions();
bool using_mole_fractions(){return false;};
/// Updating function for incompressible fluid
/**
/**
In this function we take a pair of thermodynamic states, those defined in the input_pairs
enumeration and update all the internal variables that we can.
@@ -41,13 +43,13 @@ public:
void update(long input_pair, double value1, double value2);
/// Set the mole fractions
/**
/**
@param mole_fractions The vector of mole fractions of the components
*/
void set_mole_fractions(const std::vector<long double> &mole_fractions);
/// Set the mass fractions
/**
/**
@param mass_fractions The vector of mass fractions of the components
*/
void set_mass_fractions(const std::vector<long double> &mass_fractions);

View File

@@ -0,0 +1,35 @@
#include "IncompressibleLibrary.h"
#include "all_incompressibles_JSON.h" // Makes a std::string variable called all_fluids_JSON
namespace CoolProp{
static JSONIncompressibleLibrary library;
void load_incompressible_library()
{
rapidjson::Document dd;
// This json formatted string comes from the all_fluids_JSON.h header which is a C++-escaped version of the JSON file
dd.Parse<0>(all_incompressibles_JSON.c_str());
if (dd.HasParseError()){
throw ValueError("Unable to load all_fluids.json");
} else{
try{library.add_many(dd);}catch(std::exception &e){std::cout << e.what() << std::endl;}
}
}
JSONIncompressibleLibrary & get_incompressible_library(void){
if (library.is_empty()){ load_incompressible_library(); }
return library;
}
IncompressibleFluid& get_incompressible_fluid(std::string fluid_string){
if (library.is_empty()){ load_incompressible_library(); }
return library.get(fluid_string);
}
std::string get_incompressible_list(void){
if (library.is_empty()){ load_incompressible_library(); }
return library.get_fluid_list();
};
} /* namespace CoolProp */

View File

@@ -0,0 +1,203 @@
#ifndef INCOMPRESSIBLELIBRARY_H
#define INCOMPRESSIBLELIBRARY_H
#include "IncompressibleFluid.h"
#include "rapidjson/rapidjson_include.h"
#include <map>
#include <algorithm>
namespace CoolProp{
// Forward declaration of the necessary debug function to avoid including the whole header
extern int get_debug_level();
/// A container for the fluid parameters for the incompressible fluids
/**
This container holds copies of all of the fluid instances for the fluids that are loaded in incompressible.
New fluids can be added by passing in a rapidjson::Value instance to the add_one function, or
a rapidjson array of fluids to the add_many function.
*/
class JSONIncompressibleLibrary
{
/// Map from CAS code to JSON instance. For pseudo-pure fluids, use name in place of CAS code since no CASE number is defined for mixtures
std::map<std::size_t, IncompressibleFluid> fluid_map;
std::vector<std::string> name_vector;
std::map<std::string, std::size_t> string_to_index_map;
bool _is_empty;
protected:
/// Parse the viscosity
void parse_viscosity(rapidjson::Value &viscosity, IncompressibleFluid & fluid)
{
if (viscosity.HasMember("type")){
std::string type = cpjson::get_string(viscosity, "type");
if (!type.compare("polynomial")){
fluid.viscosity.type = CoolProp::IncompressibleViscosityVariables::INCOMPRESSIBLE_VISCOSITY_POLYNOMIAL;
fluid.viscosity.poly.coeffs = cpjson::get_double_array(viscosity["coeffs"]);
return;
}
else{
throw ValueError(format("viscosity type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
}
}
else{
throw ValueError(format("viscosity does not have \"type\" for fluid %s", fluid.name.c_str()));
}
};
/// Parse the conductivity
void parse_conductivity(rapidjson::Value &conductivity, IncompressibleFluid & fluid)
{
if (conductivity.HasMember("type")){
std::string type = cpjson::get_string(conductivity, "type");
if (!type.compare("polynomial")){
fluid.conductivity.type = CoolProp::IncompressibleConductivityVariables::INCOMPRESSIBLE_CONDUCTIVITY_POLYNOMIAL;
fluid.conductivity.poly.coeffs = cpjson::get_double_array(conductivity["coeffs"]);
return;
}
else{
throw ValueError(format("conductivity type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
}
}
else{
throw ValueError(format("conductivity does not have \"type\" for fluid %s", fluid.name.c_str()));
}
};
/// Parse the specific_heat
void parse_specific_heat(rapidjson::Value &specific_heat, IncompressibleFluid & fluid)
{
if (specific_heat.HasMember("type")){
std::string type = cpjson::get_string(specific_heat, "type");
if (!type.compare("polynomial")){
fluid.specific_heat.type = CoolProp::IncompressibleSpecificHeatVariables::INCOMPRESSIBLE_SPECIFIC_HEAT_POLYNOMIAL; return;
fluid.specific_heat.poly.coeffs = cpjson::get_double_array(specific_heat["coeffs"]);
}
else{
throw ValueError(format("specific_heat type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
}
}
else{
throw ValueError(format("specific_heat does not have \"type\" for fluid %s", fluid.name.c_str()));
}
};
/// Parse the density
void parse_density(rapidjson::Value &density, IncompressibleFluid & fluid)
{
if (density.HasMember("type")){
std::string type = cpjson::get_string(density, "type");
if (!type.compare("polynomial")){
fluid.density.type = CoolProp::IncompressibleDensityVariables::INCOMPRESSIBLE_DENSITY_POLYNOMIAL; return;
fluid.density.poly.coeffs = cpjson::get_double_array(density["coeffs"]);
}
else{
throw ValueError(format("density type [%s] is not understood for fluid %s", type.c_str(), fluid.name.c_str()));
}
}
else{
throw ValueError(format("density does not have \"type\" for fluid %s", fluid.name.c_str()));
}
};
/// Validate the fluid file that was just constructed
void validate(IncompressibleFluid & fluid)
{
}
public:
// Default constructor;
JSONIncompressibleLibrary(){
_is_empty = true;
};
bool is_empty(void){ return _is_empty;};
/// Add all the fluid entries in the rapidjson::Value instance passed in
void add_many(rapidjson::Value &listing)
{
for (rapidjson::Value::ValueIterator itr = listing.Begin(); itr != listing.End(); ++itr)
{
add_one(*itr);
}
};
void add_one(rapidjson::Value &fluid_json)
{
_is_empty = false;
// Get the next index for this fluid
std::size_t index = fluid_map.size();
// Add index->fluid mapping
fluid_map[index] = IncompressibleFluid();
// Create an instance of the fluid
IncompressibleFluid &fluid = fluid_map[index];
fluid.name = cpjson::get_string(fluid_json, "name");
fluid.Tmin = cpjson::get_double(fluid_json, "Tmin");
fluid.Tmax = cpjson::get_double(fluid_json, "Tmax");
parse_conductivity(fluid_json["conductivity"], fluid);
parse_density(fluid_json["density"], fluid);
parse_viscosity(fluid_json["viscosity"], fluid);
parse_specific_heat(fluid_json["specific_heat"], fluid);
// Add name->index mapping
string_to_index_map[fluid.name] = index;
};
/// Get an IncompressibleFluid instance stored in this library
/**
@param name Name of the fluid
*/
IncompressibleFluid& get(std::string key)
{
std::map<std::string, std::size_t>::iterator it;
// Try to find it
it = string_to_index_map.find(key);
// If it is found
if (it != string_to_index_map.end()){
return get(it->second);
}
else{
throw ValueError(format("key [%s] was not found in string_to_index_map in JSONIncompressibleLibrary",key.c_str()));
}
};
/// Get a CoolPropFluid instance stored in this library
/**
@param key The index of the fluid in the map
*/
IncompressibleFluid& get(std::size_t key)
{
std::map<std::size_t, IncompressibleFluid>::iterator it;
// Try to find it
it = fluid_map.find(key);
// If it is found
if (it != fluid_map.end()){
return it->second;
}
else{
throw ValueError(format("key [%d] was not found in JSONIncompressibleLibrary",key));
}
};
/// Return a comma-separated list of fluid names
std::string get_fluid_list(void)
{
return strjoin(name_vector, ",");
};
};
/// Get a reference to the library instance
JSONIncompressibleLibrary & get_incompressible_library(void);
/// Get a comma-separated-list of incompressible fluids that are included
std::string get_incompressible_list(void);
/// Get the fluid structure returned as a reference
IncompressibleFluid& get_incompressible_fluid(std::string fluid_string);
} /* namespace CoolProp */
#endif

View File

@@ -50,7 +50,8 @@ parameter_info parameter_info_list[] = {
parameter_info(irhomolar_critical, "rhomolar_critical","O","mol/m^3","Molar density at critical point"),
parameter_info(iT_reducing, "T_reducing","O","K","Temperature at the reducing point"),
parameter_info(iT_critical, "T_critical","O","K","Temperature at the critical point"),
parameter_info(iisothermal_compressibility, "isothermal_compressibility","O","1/Pa","Isothermal compressibility")
parameter_info(iisothermal_compressibility, "isothermal_compressibility","O","1/Pa","Isothermal compressibility"),
parameter_info(ispeed_sound, "speed_of_sound","O","m/s","Speed of sound")
};
class ParameterInformation
@@ -157,7 +158,7 @@ input_pair_info input_pair_list[] = {
input_pair_info(SmolarT_INPUTS, "SmolarT_INPUTS", "Entropy in J/mol/K, Temperature in K"),
input_pair_info(TUmass_INPUTS, "TUmass_INPUTS", "Temperature in K, Internal energy in J/kg"),
input_pair_info(TUmolar_INPUTS, "TUmolar_INPUTS", "Temperature in K, Internal energy in J/mol"),
input_pair_info(DmassP_INPUTS, "DmassP_INPUTS", "Mass density in kg/m^3, Pressure in Pa"),
input_pair_info(DmolarP_INPUTS, "DmolarP_INPUTS", "Molar density in mol/m^3, Pressure in Pa"),
input_pair_info(HmassP_INPUTS, "HmassP_INPUTS", "Enthalpy in J/kg, Pressure in Pa"),
@@ -206,4 +207,4 @@ std::string get_input_pair_long_desc(int pair)
return input_pair_information.long_desc_map[pair];
}
} /* namespace CoolProp */
} /* namespace CoolProp */

View File

@@ -468,8 +468,8 @@ long double ResidualHelmholtzGaussian::dDelta3(const long double &tau, const lon
for (std::size_t i=0; i<N; ++i)
{
ResidualHelmholtzGaussianElement &el = elements[i];
long double psi=exp(-el.eta*pow(delta-el.epsilon,2)-el.beta*pow(tau-el.gamma,2));
long double bracket = pow(el.t/tau-2.0*el.beta*(tau-el.gamma),2)-el.t/pow(tau,2)-2.0*el.beta;
long double psi = exp(-el.eta*pow(delta-el.epsilon,2)-el.beta*pow(tau-el.gamma,2));
long double bracket = (pow(el.d-2*el.eta*delta*(delta-el.epsilon),3)-3*el.d*el.d+2*el.d-6*el.d*el.eta*delta*delta+6*el.eta*delta*(delta-el.epsilon)*(el.d+2*el.eta*delta*delta));
s[i] = el.n*pow(tau,el.t)*pow(delta,el.d-3)*psi*bracket;
}
return std::accumulate(s.begin(), s.end(), 0.0);
@@ -725,7 +725,7 @@ long double ResidualHelmholtzLemmon2005::dTau(const long double &tau, const long
int li = el.l, mi = el.m;
if (li != 0 && mi != 0){
long double pow_tau_mi = pow(tau, md);
s[i] = ni*(ti-mi*pow_tau_mi)*exp((ti-1)*log_tau+di*log_delta-pow(delta,li)-pow_tau_mi);
s[i] = ni*(ti-md*pow_tau_mi)*exp((ti-1)*log_tau+di*log_delta-pow(delta,li)-pow_tau_mi);
}
else if (li != 0 && mi == 0)
s[i] = ni*ti*exp((ti-1)*log_tau+di*log_delta-pow(delta,li));
@@ -769,7 +769,7 @@ long double ResidualHelmholtzLemmon2005::dDelta_dTau(const long double &tau, con
if (li != 0 && mi != 0){
long double pow_delta_li = pow(delta, li);
long double pow_tau_mi = pow(tau, md);
s[i] = ni*(di-li*pow_delta_li)*(ti-mi*pow_tau_mi)*exp((ti-1)*log_tau+(di-1)*log_delta-pow_delta_li-pow_tau_mi);
s[i] = ni*(di-li*pow_delta_li)*(ti-md*pow_tau_mi)*exp((ti-1)*log_tau+(di-1)*log_delta-pow_delta_li-pow_tau_mi);
}
else if (li != 0 && mi == 0){
long double pow_delta_li = pow(delta, li);
@@ -791,7 +791,7 @@ long double ResidualHelmholtzLemmon2005::dTau2(const long double &tau, const lon
int li = el.l, mi = el.m;
if (li != 0 && mi != 0){
long double pow_tau_mi = pow(tau, md);
long double bracket = (ti-mi*pow_tau_mi)*(ti-1-mi*pow_tau_mi)-mi*mi*pow_tau_mi;
long double bracket = (ti-md*pow_tau_mi)*(ti-1.0-md*pow_tau_mi)-md*md*pow_tau_mi;
s[i] = ni*bracket*exp((ti-2)*log_tau+di*log_delta-pow(delta,li)-pow_tau_mi);
}
else if (li != 0 && mi == 0)
@@ -839,7 +839,7 @@ long double ResidualHelmholtzLemmon2005::dDelta2_dTau(const long double &tau, co
if (li != 0 && mi != 0){
long double pow_delta_li = pow(delta,li);
long double pow_tau_mi = pow(tau,md);
long double bracket = (ti-mi*pow_tau_mi)*(((di-li*pow_delta_li))*(di-1-li*pow_delta_li)-li*li*pow_delta_li);
long double bracket = (ti-md*pow_tau_mi)*(((di-li*pow_delta_li))*(di-1-li*pow_delta_li)-li*li*pow_delta_li);
s[i] = ni*bracket*exp((ti-1)*log_tau+(di-2)*log_delta-pow_delta_li-pow_tau_mi);
}
else if (li != 0 && mi == 0){
@@ -865,7 +865,7 @@ long double ResidualHelmholtzLemmon2005::dDelta_dTau2(const long double &tau, co
long double pow_delta_li = pow(delta,li);
long double pow_tau_mi = pow(tau,md);
// delta derivative of second tau derivative
long double bracket = ((ti-mi*pow_tau_mi)*(ti-1-mi*pow_tau_mi)-mi*mi*pow_tau_mi)*(di-li*pow_delta_li);
long double bracket = ((ti-md*pow_tau_mi)*(ti-1-md*pow_tau_mi)-md*md*pow_tau_mi)*(di-li*pow_delta_li);
s[i] = ni*bracket*exp((ti-2)*log_tau+(di-1)*log_delta-pow_delta_li-pow_tau_mi);
}
else if (li != 0 && mi == 0){
@@ -879,31 +879,14 @@ long double ResidualHelmholtzLemmon2005::dDelta_dTau2(const long double &tau, co
}
/**
\f[
\frac{{{\partial ^2}{\alpha ^r}}}{{\partial {\tau ^2}}} = {N_k}{\delta ^{{d_k}}}{\tau ^{{t_k} - 2}}\exp \left( { - {\delta ^{{l_k}}}} \right)\exp \left( { - {\tau ^{{m_k}}}} \right)\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right]\\
\f]
\f[
\frac{{{\partial ^2}{\alpha ^r}}}{{\partial {\tau ^2}}} = {N_k}{\delta ^{{d_k}}}\exp \left( { - {\delta ^{{l_k}}}} \right){\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right]\\
\f]
Group all the terms that don't depend on \f$ \tau \f$
\f[
\frac{{{\partial ^2}{\alpha ^r}}}{{\partial {\tau ^2}}} = A{\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right]\\
\f]
\f[
\frac{1}{A}\frac{{{\partial ^3}{\alpha ^r}}}{{\partial {\tau ^3}}} = {\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)\frac{\partial }{{\partial \tau }}\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right] + \frac{\partial }{{\partial \tau }}\left[ {{\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)} \right]\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right]\\
\f]
\f[
\frac{\partial }{{\partial \tau }}\left[ {{\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)} \right] = ({t_k} - 2){\tau ^{{t_k} - 3}}\exp \left( { - {\tau ^{{m_k}}}} \right) + {\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)( - {m_k}{\tau ^{{m_k} - 1}}) = \exp \left( { - {\tau ^{{m_k}}}} \right)\left( {({t_k} - 2){\tau ^{{t_k} - 3}} - {\tau ^{{t_k} - 2}}{m_k}{\tau ^{{m_k} - 1}}} \right)\\
\f]
\f[
\frac{\partial }{{\partial \tau }}\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right] = \left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( { - m_k^2{\tau ^{{m_k} - 1}}} \right) + \left( { - m_k^2{\tau ^{{m_k} - 1}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^3{\tau ^{{m_k} - 1}} = - m_k^2{\tau ^{{m_k} - 1}}\left[ {{t_k} - {m_k}{\tau ^{{m_k}}} + {t_k} - 1 - {m_k}{\tau ^{{m_k}}} + {m_k}} \right] = - m_k^2{\tau ^{{m_k} - 1}}\left[ {2{t_k} - 2{m_k}{\tau ^{{m_k}}} - 1 + {m_k}} \right]\\
\f]
\f[
\frac{1}{A}\frac{{{\partial ^3}{\alpha ^r}}}{{\partial {\tau ^3}}} = {\tau ^{{t_k} - 2}}\exp \left( { - {\tau ^{{m_k}}}} \right)\left( { - m_k^2{\tau ^{{m_k} - 1}}\left[ {2{t_k} - 2{m_k}{\tau ^{{m_k}}} - 1 + {m_k}} \right]} \right) + \exp \left( { - {\tau ^{{m_k}}}} \right)\left( {({t_k} - 2){\tau ^{{t_k} - 3}} - {\tau ^{{t_k} - 2}}{m_k}{\tau ^{{m_k} - 1}}} \right)\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right]\\
\f]
\f[
\frac{1}{A}\frac{{{\partial ^3}{\alpha ^r}}}{{\partial {\tau ^3}}} = \exp \left( { - {\tau ^{{m_k}}}} \right)\left[ { - {\tau ^{{t_k} - 2}}m_k^2{\tau ^{{m_k} - 1}}\left[ {2{t_k} - 2{m_k}{\tau ^{{m_k}}} - 1 + {m_k}} \right] + \left( {({t_k} - 2){\tau ^{{t_k} - 3}} - {\tau ^{{t_k} - 2}}{m_k}{\tau ^{{m_k} - 1}}} \right)\left[ {\left( {{t_k} - {m_k}{\tau ^{{m_k}}}} \right)\left( {{t_k} - 1 - {m_k}{\tau ^{{m_k}}}} \right) - m_k^2{\tau ^{{m_k}}}} \right]} \right]
\f]
Derived using sympy:
from sympy import *
init_printing()
n_i, d_i, t_i, m_i, l_i, tau, delta, eta, gamma, beta, epsilon = symbols('n_i, d_i, t_i, m_i, l_i, tau, delta, eta, gamma, beta, epsilon')
I = n_i*delta**d_i*tau**t_i*exp(-delta**l_i)*exp(-tau**m_i)
ccode(simplify(diff(I,tau,3)))
*/
long double ResidualHelmholtzLemmon2005::dTau3(const long double &tau, const long double &delta) throw()
{
@@ -912,13 +895,13 @@ long double ResidualHelmholtzLemmon2005::dTau3(const long double &tau, const lon
for (std::size_t i=0; i<N; ++i)
{
ResidualHelmholtzLemmon2005Element &el = elements[i];
long double ni = el.n, ti = el.t, di = el.d;
long double ni = el.n, ti = el.t, di = el.d, md = el.md;
int li = el.l, mi = el.m;
if (li != 0 && mi != 0){
long double pow_delta_li = pow(delta,li);
long double pow_tau_mi = pow(tau,mi);
//long double bracket = -pow(tau,ti+mi-3)*mi*mi*(2*ti-2*mi*pow_tau_mi-1-mi)+((ti-2)*pow(tau,ti-3)-pow(tau,ti-2)*mi*pow(tau,mi-1))*((ti-mi*pow_tau_mi)*(ti-1-mi*pow_tau_mi)-mi*mi*pow_tau_mi);
s[i] = ni*ti*(ti-1)*(ti-2)*exp((ti-3)*log_tau+di*log_delta-pow_delta_li-pow_tau_mi);
long double pow_delta_li = pow(delta, li);
long double pow_tau_mi = pow(tau, md);
long double bracket = (pow(md, 3)*pow_tau_mi*pow_tau_mi*pow_tau_mi - 3*pow(md, 3)*pow_tau_mi*pow_tau_mi + pow(md, 3)*pow_tau_mi - 3*pow(md, 2)*ti*pow_tau_mi*pow_tau_mi + 3*pow(md, 2)*ti*pow_tau_mi + 3*pow(md, 2)*pow_tau_mi*pow_tau_mi - 3*pow(md, 2)*pow_tau_mi + 3*md*pow(ti, 2)*pow_tau_mi - 6*md*ti*pow_tau_mi + 2*md*pow_tau_mi - pow(ti, 3) + 3*pow(ti, 2) - 2*ti);
s[i] = -ni*bracket*exp((ti-3)*log_tau+di*log_delta-pow_delta_li-pow_tau_mi);
}
else if (li != 0 && mi == 0){
s[i] = ni*ti*(ti-1)*(ti-2)*exp((ti-3)*log_tau+di*log_delta-pow(delta,li));
@@ -1058,7 +1041,7 @@ long double ResidualHelmholtzNonAnalytic::dDelta2(const long double &tau, const
dDELTA2_dDelta2 = dDELTA_dDelta_over_delta_minus_1+pow(delta-1.0,2)*(4.0*Bi*ai*(ai-1.0)*pow(pow(delta-1,2),ai-2.0)+2.0*pow(Ai/betai,2)*pow(pow(pow(delta-1,2),1.0/(2.0*betai)-1.0),2)+Ai*theta*4.0/betai*(1.0/(2.0*betai)-1.0)*pow(pow(delta-1.0,2),1.0/(2.0*betai)-2.0));
dDELTAbi2_dDelta2 = bi*(pow(DELTA,bi-1.0)*dDELTA2_dDelta2+(bi-1.0)*pow(DELTA,bi-2.0)*pow(dDELTA_dDelta,2));
}
// At critical point, DELTA is 0, and 1/0^n is undefined
if (fabs(DELTA) < 10*DBL_EPSILON)
{
@@ -1966,7 +1949,7 @@ TEST_CASE_METHOD(HelmholtzConsistencyFixture, "Helmholtz energy derivatives", "[
term = get(terms[i]);
for (std::size_t j = 0; j < sizeof(derivs)/sizeof(derivs[0]); ++j)
{
call(derivs[j], term, 1.3, 0.7, 1e-8);
call(derivs[j], term, 1.3, 0.7, 1e-7);
CAPTURE(derivs[j]);
CAPTURE(numerical);
CAPTURE(analytic);