#include "CoolProp.h" #include "AbstractState.h" #if defined(_MSC_VER) #define _CRTDBG_MAP_ALLOC #define _CRT_SECURE_NO_WARNINGS #include #endif #if defined(__ISWINDOWS__) #include #else #ifndef DBL_EPSILON #include #define DBL_EPSILON std::numeric_limits::epsilon() #endif #endif #include #include #include #include #include #include #include #include "CoolPropTools.h" #include "Solvers.h" #include "MatrixMath.h" #include "Backends/Helmholtz/Fluids/FluidLibrary.h" #include "Backends/Incompressible/IncompressibleLibrary.h" #include "Backends/Helmholtz/HelmholtzEOSBackend.h" namespace CoolProp { static int debug_level = 0; static std::string error_string; static std::string warning_string; void set_debug_level(int level){debug_level = level;} int get_debug_level(void){return debug_level;} //// This is very hacky, but pull the git revision from the file #include "gitrevision.h" // Contents are like "std::string gitrevision = "aa121435436ggregrea4t43t433";" #include "cpversion.h" // Contents are like "char version [] = "2.5";" //int global_Phase = -1; void set_warning_string(std::string warning){ warning_string = warning; } void set_error_string(std::string error){ error_string = error; } // //static int IsCoolPropFluid(std::string FluidName) //{ // // Try to get the fluid from Fluids by name // try // { // pFluid = Fluids.get_fluid(FluidName); // } // catch (NotImplementedError &) // { // return false; // } // // If NULL, didn't find it (or its alias) // if (pFluid!=NULL) // { // return true; // } // else // return false; //} // //static int IsBrine(const char* Ref) //{ // // First check whether it is one of the Brines that does // // not have a pure-fluid equivalent in CoolProp // if ( // strcmp(Ref,"HC-10")==0 || // strncmp(Ref,"PG-",3)==0 || // strncmp(Ref,"EG-",3)==0 || // strncmp(Ref,"EA-",3)==0 || // strncmp(Ref,"MA-",3)==0 || // strncmp(Ref,"Glycerol-",9)==0 || // strncmp(Ref,"K2CO3-",6)==0 || // strncmp(Ref,"CaCl2-",6)==0 || // strncmp(Ref,"MgCl2-",6)==0 || // strncmp(Ref,"NaCl-",5)==0 || // strncmp(Ref,"KAC-",4)==0 || // strncmp(Ref,"KFO-",4)==0 || // strncmp(Ref,"LiCl-",4)==0 || // strncmp(Ref,"NH3/H2O-",8)==0 // ) // { // return 1; // } // // Then check for diluants that are also pure fluids in CoolProp // else if ( (strncmp(Ref,"Methanol-",9)==0 && Ref[8] == '-') || // (strncmp(Ref,"Ethanol-",8)==0 && Ref[7] == '-') || // (strncmp(Ref,"NH3-",4)==0 && Ref[3] == '-') // ) // { // return 1; // } // else // { // return 0; // } //} // //long getFluidType(std::string FluidName){ // if (IsREFPROP(FluidName)) { return FLUID_TYPE_REFPROP;} // else if(IsIncompressibleLiquid(FluidName)){ return FLUID_TYPE_INCOMPRESSIBLE_LIQUID;} // // TODO SOLUTION: Check if working // else if(IsIncompressibleSolution(FluidName)){ return FLUID_TYPE_INCOMPRESSIBLE_SOLUTION; } // else { // // Try to get the index of the fluid // long iFluid = get_Fluid_index(FluidName); // // If iFluid is greater than -1, it is a CoolProp Fluid, otherwise not // if (iFluid > -1) { // // Get a pointer to the fluid object // pFluid = get_fluid(iFluid); // if (pFluid->pure()) { return FLUID_TYPE_PURE;} // else { return FLUID_TYPE_PSEUDOPURE; } // } else { // throw ValueError(format("Bad Fluid name [%s] - not a CoolProp fluid",FluidName.c_str())); // } // } // return -1; //} // //EXPORT_CODE int CONVENTION IsFluidType(const char *Ref, const char *Type) //{ // pFluid = Fluids.get_fluid(Ref); // // if (IsBrine(Ref)){ // TODO Solution: Remove this part // if (!strcmp(Type,"Brine")){ // return 1; // } // else{ // return 0; // } // } // else if (IsIncompressibleSolution(Ref)){ // if (!strcmp(Type,"Solution")){ // return 1; // } // else{ // return 0; // } // } // else if (IsIncompressibleLiquid(Ref)){ // if (!strcmp(Type,"Liquid")){ // return 1; // } // else{ // return 0; // } // } // else if (IsREFPROP(Ref)){ // if (!strcmp(Type,"PureFluid")){ // return 1; // } // else{ // return 0; // } // } // else if (!pFluid->pure()){ // if (!strcmp(Type,"PseudoPure") || !strcmp(Type,"PseudoPureFluid")){ // return 1; // } // else{ // return 0; // } // } // else if (pFluid->pure()){ // if (!strcmp(Type,"PureFluid")){ // return 1; // } // else{ // return 0; // } // } // else // { // return 0; // } //} // // //std::string Phase_Trho(std::string Fluid, double T, double rho) //{ // try{ // // Try to load the CoolProp Fluid // pFluid = Fluids.get_fluid(Fluid); // double pL,pV,rhoL,rhoV; // return pFluid->phase_Trho(T,rho, pL, pV, rhoL, rhoV); // } // catch(NotImplementedError &){ // return std::string(""); // } // return std::string(""); //} // //std::string Phase(std::string Fluid, double T, double p) //{ // try{ // // Try to load the CoolProp Fluid // pFluid = Fluids.get_fluid(Fluid); // double pL,pV,rhoL,rhoV; // return pFluid->phase_Tp(T, p, pL, pV, rhoL, rhoV); // } // catch(NotImplementedError &){ // return std::string(""); // } // return std::string(""); //} // //std::string Phase_Tp(std::string Fluid, double T, double p) //{ // return Phase(Fluid,T,p); //} // // Return true if the string has "BACKEND::*" format where * signifies a wildcard bool has_backend_in_string(const std::string &fluid_string, std::size_t &i) { i = fluid_string.find("::"); return i != std::string::npos; } void extract_backend(const std::string &fluid_string, std::string &backend, std::string &fluid) { std::size_t i; if (has_backend_in_string(fluid_string, i)) { // Part without the :: backend = fluid_string.substr(0, i); // Fluid name after the :: fluid = fluid_string.substr(i+2); } else { backend = "?"; fluid = fluid_string; } if (get_debug_level()>10) std::cout << format("%s:%d: backend extracted. backend: %s. fluid: %s\n",__FILE__,__LINE__, backend.c_str(), fluid.c_str()); } bool has_fractions_in_string(const std::string &fluid_string) { // If can find both "[" and "]", it must have mole fractions encoded as string return (fluid_string.find("[")!=std::string::npos && fluid_string.find("]")!=std::string::npos); } bool has_solution_concentration(const std::string &fluid_string) { // If can find "-", expect mass fractions encoded as string return (fluid_string.find('-') != std::string::npos); } std::string extract_fractions(const std::string &fluid_string, std::vector &fractions) { if (has_fractions_in_string(fluid_string)) { fractions.clear(); std::vector names; // Break up into pairs - like "Ethane[0.5]&Methane[0.5]" -> ("Ethane[0.5]","Methane[0.5]") std::vector pairs = strsplit(fluid_string, '&'); for (std::size_t i = 0; i < pairs.size(); ++i) { std::string fluid = pairs[i]; // Must end with ']' if (fluid[fluid.size()-1] != ']') throw ValueError(format("Fluid entry [%s] must end with ']' character",pairs[i].c_str())); // Split at '[', but first remove the ']' from the end by taking a substring std::vector name_fraction = strsplit(fluid.substr(0, fluid.size()-1), '['); if (name_fraction.size() != 2){throw ValueError(format("Could not break [%s] into name/fraction", fluid.substr(0, fluid.size()-1).c_str()));} // Convert fraction to a double char *pEnd; std::string &name = name_fraction[0], &fraction = name_fraction[1]; double f = strtod(fraction.c_str(), &pEnd); // If pEnd points to the last character in the string, it wasn't able to do the conversion if (pEnd == &(fraction[fraction.size()-1])){throw ValueError(format("Could not convert [%s] into number", fraction.c_str()));} // And add to vector fractions.push_back(f); // Add name names.push_back(name); } if (get_debug_level()>10) std::cout << format("%s:%d: Detected fractions of %s for %s.",__FILE__,__LINE__,vec_to_string(fractions).c_str(), (strjoin(names, "&")).c_str()); // Join fluids back together return strjoin(names, "&"); } else if (has_solution_concentration(fluid_string)) { fractions.clear(); double x; std::vector fluid_parts = strsplit(fluid_string,'-'); // Check it worked if (fluid_parts.size() != 2){ throw ValueError(format("Format of incompressible solution string [%s] is invalid, should be like \"EG-20%\" or \"EG-0.2\" ", fluid_string.c_str()) ); } // Convert the concentration into a string char* pEnd; x = strtod(fluid_parts[1].c_str(), &pEnd); // Check if per cent or fraction syntax is used if (!strcmp(pEnd,"%")){ x *= 0.01;} fractions.push_back(x); if (get_debug_level()>10) std::cout << format("%s:%d: Detected incompressible concentration of %s for %s.",__FILE__,__LINE__,vec_to_string(fractions).c_str(), fluid_parts[0].c_str()); return fluid_parts[0]; } else { return fluid_string; } } // 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 &z) { double x1, x2; 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(); } // Convert all the input and output parameters to integers long iOutput = get_parameter_index(Output); // The state we are going to use shared_ptr State; // If the fractions of the components have been encoded in the string, extract them // If they have not, this function does nothing std::vector fractions; if (z.empty()) { // Make a one-element vector fractions = std::vector(1, 1); } else{ // Make a copy fractions = z; } std::string fluid_string = extract_fractions(Ref, fractions); // We are going to let the factory function load the state State.reset(AbstractState::factory(backend, fluid_string)); // First check if it is a trivial input (critical/max parameters for instance) if (is_trivial_parameter(iOutput)) { double val = State->trivial_keyed_output(iOutput); return val; }; long iName1 = get_parameter_index(Name1); long iName2 = get_parameter_index(Name2); 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(); } // Obtain the input pair long pair = generate_update_pair(iName1, Prop1, iName2, Prop2, x1, x2); // Update the state State->update(pair, x1, x2); // Return the desired output double val = State->keyed_output(iOutput); // Return the value return val; } double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &Ref, const std::vector &z) { std::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);) } double PropsSI(const char *Output, const char *Name1, double Prop1, const char *Name2, double Prop2, const char *FluidName, const std::vector &x) { std::string _Output = Output, _Name1 = Name1, _Name2 = Name2, _FluidName = FluidName; return PropsSI(_Output,_Name1,Prop1,_Name2,Prop2,_FluidName, 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 backend, fluid; #if !defined(PROPSSI_NO_ERROR_CATCH) // In this function the error catching happens; try{ #else std::cout << "macro is on; error checking disabled in PropsSI" << std::endl; #endif // Here is the real code extract_backend(Ref, backend, fluid); return _PropsSI(Output, Name1, Prop1, Name2, Prop2, backend, fluid, std::vector()); #if !defined(PROPSSI_NO_ERROR_CATCH) } catch(const std::exception& e){ set_error_string(e.what()); #if defined (PROPSSI_ERROR_STDOUT) std::cout << e.what() << std::endl; #endif return _HUGE; } catch(...){ return _HUGE; } #endif } std::vector PropsSI(const std::string &Output, const std::string &Name1, const std::vector &Prop1, const std::string &Name2, const std::vector Prop2, const std::string &Ref, const std::vector &z) { std::vector out(Prop1.size(), _HUGE); if (Prop1.size() != Prop2.size()) { std::cout << format("Sizes of Prop1 [%d] and Prop2 [%d] to PropsSI are not the same", Prop1.size(), Prop2.size()) << std::endl; return out; } 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); } //EXPORT_CODE double CONVENTION IProps(long iOutput, long iName1, double Prop1, long iName2, double Prop2, long iFluid) //{ // Prop1 = convert_from_unit_system_to_SI(iName1, Prop1, get_standard_unit_system()); // Prop2 = convert_from_unit_system_to_SI(iName2, Prop2, get_standard_unit_system()); // double out = IPropsSI(iOutput,iName1,Prop1,iName2,Prop2,iFluid); // return convert_from_SI_to_unit_system(iOutput,out,get_standard_unit_system()); //} //double _CoolProp_Fluid_PropsSI(long iOutput, long iName1, double Prop1, long iName2, double Prop2, Fluid *pFluid) //{ // double val = _HUGE, T = _HUGE; // // This private method uses the indices directly for speed // // if (get_debug_level()>3){ // std::cout << format("%s:%d: _CoolProp_Fluid_PropsSI(%d,%d,%g,%d,%g,%s)\n",__FILE__,__LINE__,iOutput,iName1, Prop1, iName2, Prop2, pFluid->get_name().c_str()).c_str(); // } // if (iName1 == iT){ // T = Prop1;} // else if (iName2 == iT){ // T = Prop2;} // // // Generate a State instance wrapped around the Fluid instance // CoolPropStateClassSI CPS(pFluid); // // // Check if it is an output that doesn't require a state input // // Deal with it and return // switch (iOutput) // { // case iI: // { // if (!ValidNumber(T)){throw ValueError(format("T must be provided as an input to use this output").c_str());} // return CPS.pFluid->surface_tension_T(T); // } // case iRhosatLanc: // { // if (!ValidNumber(T)){throw ValueError(format("T must be provided as an input to use this output").c_str());} // return CPS.pFluid->rhosatL(T); // } // case iRhosatVanc: // { // if (!ValidNumber(T)){throw ValueError(format("T must be provided as an input to use this output").c_str());} // return CPS.pFluid->rhosatV(T); // } // case iPsatLanc: // { // if (!ValidNumber(T)){throw ValueError(format("T must be provided as an input to use this output").c_str());} // if (CPS.pFluid->pure()){ // return CPS.pFluid->psat(T); // } // else{ // return CPS.pFluid->psatL(T); // } // } // case iPsatVanc: // { // if (!ValidNumber(T)){throw ValueError(format("T must be provided as an input to use this output").c_str());} // if (CPS.pFluid->pure()){ // return CPS.pFluid->psat(T); // } // else{ // return CPS.pFluid->psatV(T); // } // } // case iMM: // case iPcrit: // case iTcrit: // case iTtriple: // case iPtriple: // case iPmax: // case iTmax: // case iRhocrit: // case iTmin: // case iAccentric: // case iPHASE_LIQUID: // case iPHASE_GAS: // case iPHASE_SUPERCRITICAL: // case iPHASE_TWOPHASE: // case iGWP20: // case iGWP100: // case iGWP500: // case iODP: // case iCritSplineT: // case iScrit: // case iHcrit: // case iTreduce: // case iRhoreduce: // return CPS.keyed_output(iOutput); // } // // // Update the class // CPS.update(iName1,Prop1,iName2,Prop2); // // // Debug // if (get_debug_level()>9){std::cout << format("%s:%d: State update successful\n",__FILE__,__LINE__).c_str();} // // // Get the output // val = CPS.keyed_output(iOutput); // // // Debug // if (get_debug_level()>5){std::cout << format("%s:%d: _CoolProp_Fluid_PropsSI returns: %g\n",__FILE__,__LINE__,val).c_str();} // // // Return the value // return val; //} //EXPORT_CODE double CONVENTION IPropsSI(long iOutput, long iName1, double Prop1, long iName2, double Prop2, long iFluid) //{ // pFluid = Fluids.get_fluid(iFluid); // // Didn't work // if (pFluid == NULL){ // err_string=std::string("CoolProp error: ").append(format("%d is an invalid fluid index to IProps",iFluid)); // return _HUGE; // } // else{ // // In this function the error catching happens; // try{ // // This is already converted to the right units since we take in SI units // return _CoolProp_Fluid_PropsSI(iOutput,iName1,Prop1,iName2,Prop2,pFluid); // } // catch(std::exception &e){ // err_string=std::string("CoolProp error: ").append(e.what()); // return _HUGE; // } // catch(...){ // err_string=std::string("CoolProp error: Indeterminate error"); // return _HUGE; // } // } //} ///// Calculate some interesting derivatives //double _CoolProp_Deriv_Terms(long iTerm, double T, double rho, Fluid * pFluid) //{ // double val = _HUGE; // // This private method uses the indices directly for speed // // if (get_debug_level()>3){ // std::cout<<__FILE__<<" _CoolProp_Deriv_Terms return: "<5){ // std::cout<<__FILE__<<" _CoolProp_Deriv_Terms return: "<5){ // std::cout<<__FILE__<<": "< HEOS; std::vector _comps(1, Ref); HEOS.reset(new CoolProp::HelmholtzEOSMixtureBackend(_comps)); if (!reference_state.compare("IIR")) { HEOS->update(QT_INPUTS, 0, 273.15); // Get current values for the enthalpy and entropy double deltah = HEOS->hmass() - 200000; // offset from 200000 J/kg enthalpy double deltas = HEOS->smass() - 1000; // offset from 1000 J/kg/K entropy double delta_a1 = deltas/(8.314472/HEOS->molar_mass()); double delta_a2 = -deltah/(8.314472/HEOS->molar_mass()*HEOS->get_reducing().T); HEOS->get_components()[0]->pEOS->alpha0.EnthalpyEntropyOffset.set(delta_a1, delta_a2, "IIR"); HEOS->update_states(); } else if (!reference_state.compare("ASHRAE")) { HEOS->update(QT_INPUTS, 0, 243.15); // Get current values for the enthalpy and entropy double deltah = HEOS->hmass() - 0; // offset from 0 J/kg enthalpy double deltas = HEOS->smass() - 0; // offset from 0 J/kg/K entropy double delta_a1 = deltas/(8.314472/HEOS->molar_mass()); double delta_a2 = -deltah/(8.314472/HEOS->molar_mass()*HEOS->get_reducing().T); HEOS->get_components()[0]->pEOS->alpha0.EnthalpyEntropyOffset.set(delta_a1, delta_a2, "ASHRAE"); HEOS->update_states(); } else if (!reference_state.compare("NBP")) { // Saturated liquid boiling point at 1 atmosphere HEOS->update(PQ_INPUTS, 101325, 0); double deltah = HEOS->hmass() - 0; // offset from 0 kJ/kg enthalpy double deltas = HEOS->smass() - 0; // offset from 0 kJ/kg/K entropy double delta_a1 = deltas/(8.314472/HEOS->molar_mass()); double delta_a2 = -deltah/(8.314472/HEOS->molar_mass()*HEOS->get_reducing().T); HEOS->get_components()[0]->pEOS->alpha0.EnthalpyEntropyOffset.set(delta_a1, delta_a2, "NBP"); HEOS->update_states(); } else if (!reference_state.compare("DEF")) { //HEOS->get_components()[0]->pEOS->alpha0.EnthalpyEntropyOffset.set(0,0); throw NotImplementedError("Default reference state has not been implemented yet"); } else if (!reference_state.compare("RESET")) { HEOS->get_components()[0]->pEOS->alpha0.EnthalpyEntropyOffset.set(0, 0, ""); } else { throw ValueError(format("reference state string is invalid: [%s]",reference_state.c_str())); } } //int set_reference_stateD(std::string Ref, double T, double rho, double h0, double s0) //{ // pFluid=Fluids.get_fluid(Ref); // if (pFluid!=NULL) // { // CoolPropStateClassSI CPS(pFluid); // CPS.update(iT,T,iD,rho); // // Get current values for the enthalpy and entropy // double h1 = CPS.h(); // double s1 = CPS.s(); // double deltah = h1-h0; // offset from given enthalpy in SI units // double deltas = s1-s0; // offset from given enthalpy in SI units // double delta_a1 = deltas/((8314.472)); // double delta_a2 = -deltah/((8314.472)*pFluid->reduce.T); // pFluid->phi0list.push_back(new phi0_enthalpy_entropy_offset(delta_a1, delta_a2)); // return 0; // } // else{ // return -1; // } //} std::string get_BibTeXKey(std::string Ref, std::string key) { std::vector names(1, Ref); HelmholtzEOSMixtureBackend HEOS(names); if (!key.compare("EOS")){ return HEOS.get_components()[0]->pEOS->BibTeX_EOS; } else if (!key.compare("CP0")){ return HEOS.get_components()[0]->pEOS->BibTeX_CP0; } else if (!key.compare("VISCOSITY")){ return HEOS.get_components()[0]->transport.BibTeX_viscosity; } else if (!key.compare("CONDUCTIVITY")){ return HEOS.get_components()[0]->transport.BibTeX_conductivity; } else if (!key.compare("ECS_LENNARD_JONES")){ throw NotImplementedError(); } else if (!key.compare("ECS_VISCOSITY_FITS")){ throw NotImplementedError(); } else if (!key.compare("ECS_CONDUCTIVITY_FITS")){ throw NotImplementedError(); } else if (!key.compare("SURFACE_TENSION")){ return HEOS.get_components()[0]->ancillaries.surface_tension.BibTeX;} else{ return "Bad key";} } std::string get_global_param_string(std::string ParamName) { if (!ParamName.compare("version")){ return version; } else if (!ParamName.compare("gitrevision")){ return gitrevision; } else if (!ParamName.compare("errstring")){ std::string temp = error_string; error_string = ""; return temp; } else if (!ParamName.compare("warnstring")){ std::string temp = warning_string; warning_string = ""; return temp; } else if (!ParamName.compare("FluidsList") || !ParamName.compare("fluids_list") || !ParamName.compare("fluidslist")){ return get_fluid_list(); } else if (!ParamName.compare("incompressible_list_pure")){ return get_incompressible_list_pure(); } else if (!ParamName.compare("incompressible_list_solution")){ return get_incompressible_list_solution(); } else if (!ParamName.compare("parameter_list") ) { return get_csv_parameter_list(); } else{ return format("Input value [%s] is invalid",ParamName.c_str()).c_str(); } }; std::string get_fluid_param_string(std::string FluidName, std::string ParamName) { try{ std::vector comps(1,FluidName); shared_ptr HEOS(new CoolProp::HelmholtzEOSMixtureBackend(comps)); CoolProp::CoolPropFluid *fluid = HEOS->get_components()[0]; if (!ParamName.compare("aliases")) { return strjoin(fluid->aliases, ", "); } else if (!ParamName.compare("CAS") || !ParamName.compare("CAS_number")) { return fluid->CAS; } else if (!ParamName.compare("ASHRAE34")) { return fluid->environment.ASHRAE34; } else if (!ParamName.compare("REFPROPName") || !ParamName.compare("REFPROP_name") || !ParamName.compare("REFPROPname")) { return fluid->REFPROPname; } else { return format("Input value [%s] is invalid for Fluid [%s]",ParamName.c_str(),FluidName.c_str()).c_str(); } } catch(std::exception &e) { return(std::string("CoolProp error: ").append(e.what())); } catch(...){ return(std::string("CoolProp error: Indeterminate error")); } } } /* namespace CoolProp */