#ifndef HELMHOLTZ_H #define HELMHOLTZ_H #include #include #include #include "rapidjson_include.h" //#include "Eigen/Core" #include "time.h" #include "CachedElement.h" #include "Backends/Cubics/GeneralizedCubic.h" #include using std::shared_ptr; #include "CPnumerics.h" #if ENABLE_CATCH # include "MultiComplex/MultiComplex.hpp" #endif namespace CoolProp { // ############################################################################# // ############################################################################# // ############################################################################# // RESIDUAL TERMS // ############################################################################# // ############################################################################# // ############################################################################# #define LIST_OF_DERIVATIVE_VARIABLES \ X(alphar) \ X(dalphar_ddelta) \ X(dalphar_dtau) \ X(d2alphar_ddelta2) \ X(d2alphar_dtau2) \ X(d2alphar_ddelta_dtau) \ X(d3alphar_ddelta3) \ X(d3alphar_ddelta_dtau2) \ X(d3alphar_ddelta2_dtau) \ X(d3alphar_dtau3) \ X(d4alphar_ddelta4) \ X(d4alphar_ddelta3_dtau) \ X(d4alphar_ddelta2_dtau2) \ X(d4alphar_ddelta_dtau3) \ X(d4alphar_dtau4) \ X(delta_x_dalphar_ddelta) \ X(tau_x_dalphar_dtau) \ X(delta2_x_d2alphar_ddelta2) \ X(deltatau_x_d2alphar_ddelta_dtau) \ X(tau2_x_d2alphar_dtau2) struct HelmholtzDerivatives { #define X(name) CoolPropDbl name; LIST_OF_DERIVATIVE_VARIABLES #undef X CoolPropDbl tau, delta, T_red, rhomolar_red; void reset(CoolPropDbl v){ #define X(name) name = v; LIST_OF_DERIVATIVE_VARIABLES #undef X } HelmholtzDerivatives operator+(const HelmholtzDerivatives& other) const { HelmholtzDerivatives _new; #define X(name) _new.name = name + other.name; LIST_OF_DERIVATIVE_VARIABLES #undef X return _new; } HelmholtzDerivatives operator*(const CoolPropDbl& other) const { HelmholtzDerivatives _new; #define X(name) _new.name = name * other; LIST_OF_DERIVATIVE_VARIABLES #undef X return _new; } HelmholtzDerivatives() : tau(0.0), delta(0.0), T_red(_HUGE), rhomolar_red(_HUGE) { reset(0.0); }; /// Retrieve a single value based on the number of derivatives with respect to tau and delta double get(std::size_t itau, std::size_t idelta) { if (itau == 0) { if (idelta == 0) { return alphar; } else if (idelta == 1) { return dalphar_ddelta; } else if (idelta == 2) { return d2alphar_ddelta2; } else if (idelta == 3) { return d3alphar_ddelta3; } else if (idelta == 4) { return d4alphar_ddelta4; } else { throw ValueError(); } } else if (itau == 1) { if (idelta == 0) { return dalphar_dtau; } else if (idelta == 1) { return d2alphar_ddelta_dtau; } else if (idelta == 2) { return d3alphar_ddelta2_dtau; } else if (idelta == 3) { return d4alphar_ddelta3_dtau; } else { throw ValueError(); } } else if (itau == 2) { if (idelta == 0) { return d2alphar_dtau2; } else if (idelta == 1) { return d3alphar_ddelta_dtau2; } else if (idelta == 2) { return d4alphar_ddelta2_dtau2; } else { throw ValueError(); } } else if (itau == 3) { if (idelta == 0) { return d3alphar_dtau3; } else if (idelta == 1) { return d4alphar_ddelta_dtau3; } else { throw ValueError(); } } else if (itau == 4) { if (idelta == 0) { return d4alphar_dtau4; } else { throw ValueError(); } } else { throw ValueError(); } } }; #undef LIST_OF_DERIVATIVE_VARIABLES /// The base class class for the Helmholtz energy terms /** Residual Helmholtz Energy Terms: Term | Helmholtz Energy Contribution ---------- | ------------------------------ ResidualHelmholtzPower | \f$ \alpha^r=\left\lbrace\begin{array}{cc}\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} & l_i=0\\ \displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\delta^{l_i}) & l_i\neq 0\end{array}\right.\f$ ResidualHelmholtzExponential | \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\gamma_i\delta^{l_i}) \f$ ResidualHelmholtzLemmon2005 | \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\delta^{l_i}-\tau^{m_i})\f$ ResidualHelmholtzGaussian | \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\eta_i(\delta-\epsilon_i)^2-\beta_i(\tau-\gamma_i)^2)\f$ ResidualHelmholtzGERG2008Gaussian | \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\eta_i(\delta-\epsilon_i)^2-\beta_i(\delta-\gamma_i))\f$ ResidualHelmholtzNonAnalytic | \f$ \begin{array}{c}\alpha^r&=&\displaystyle\sum_i n_i \Delta^{b_i}\delta\psi \\ \Delta & = & \theta^2+B_i[(\delta-1)^2]^{a_i}\\ \theta & = & (1-\tau)+A_i[(\delta-1)^2]^{1/(2\beta_i)}\\ \psi & = & \exp(-C_i(\delta-1)^2-D_i(\tau-1)^2) \end{array}\f$ ResidualHelmholtzSAFTAssociating | \f$ \alpha^r = am\left(\ln X-\frac{X}{2}+\frac{1}{2}\right); \f$ Ideal-Gas Helmholtz Energy Terms: Term | Helmholtz Energy Contribution ---------- | ------------------------------ IdealHelmholtzLead | \f$ \alpha^0 = n_1 + n_2\tau + \ln\delta \f$ IdealHelmholtzEnthalpyEntropyOffset | \f$ \alpha^0 = \displaystyle\frac{\Delta s}{R_u/M}+\frac{\Delta h}{(R_u/M)T}\tau \f$ IdealHelmholtzLogTau | \f$ \alpha^0 = n_1\log\tau \f$ IdealHelmholtzPower | \f$ \alpha^0 = \displaystyle\sum_i n_i\tau^{t_i} \f$ IdealHelmholtzPlanckEinsteinGeneralized | \f$ \alpha^0 = \displaystyle\sum_i n_i\log[c_i+d_i\exp(\theta_i\tau)] \f$ */ class BaseHelmholtzTerm { public: BaseHelmholtzTerm() {}; virtual ~BaseHelmholtzTerm() {}; /// Returns the base, non-dimensional, Helmholtz energy term (no derivatives) [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl base(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.alphar; }; /// Returns the first partial derivative of Helmholtz energy term with respect to tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dTau(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.dalphar_dtau; }; /// Returns the second partial derivative of Helmholtz energy term with respect to tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dTau2(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d2alphar_dtau2; }; /// Returns the second mixed partial derivative (delta1,dtau1) of Helmholtz energy term with respect to delta and tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dDelta_dTau(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d2alphar_ddelta_dtau; }; /// Returns the first partial derivative of Helmholtz energy term with respect to delta [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dDelta(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.dalphar_ddelta; }; /// Returns the second partial derivative of Helmholtz energy term with respect to delta [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dDelta2(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d2alphar_ddelta2; }; /// Returns the third mixed partial derivative (delta2,dtau1) of Helmholtz energy term with respect to delta and tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dDelta2_dTau(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d3alphar_ddelta2_dtau; }; /// Returns the third mixed partial derivative (delta1,dtau2) of Helmholtz energy term with respect to delta and tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dDelta_dTau2(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d3alphar_ddelta_dtau2; }; /// Returns the third partial derivative of Helmholtz energy term with respect to tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dTau3(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d3alphar_dtau3; }; /// Returns the third partial derivative of Helmholtz energy term with respect to delta [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dDelta3(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d3alphar_ddelta3; }; /// Returns the fourth partial derivative of Helmholtz energy term with respect to tau [-] /** @param tau Reciprocal reduced temperature where \f$\tau=T_c / T\f$ * @param delta Reduced density where \f$\delta = \rho / \rho_c \f$ */ virtual CoolPropDbl dTau4(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d4alphar_dtau4; }; virtual CoolPropDbl dDelta_dTau3(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d4alphar_ddelta_dtau3; }; virtual CoolPropDbl dDelta2_dTau2(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d4alphar_ddelta2_dtau2; }; virtual CoolPropDbl dDelta3_dTau(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d4alphar_ddelta3_dtau; }; virtual CoolPropDbl dDelta4(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { HelmholtzDerivatives deriv; all(tau, delta, deriv); return deriv.d4alphar_ddelta4; }; virtual void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) = 0; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const { throw CoolProp::NotImplementedError("The mcx derivative function was not implemented"); } #endif }; struct ResidualHelmholtzGeneralizedExponentialElement { /// These variables are for the n*delta^d_i*tau^t_i part CoolPropDbl n, d, t; /// These variables are for the exp(u) part /// u is given by -c*delta^l_i-omega*tau^m_i-eta1*(delta-epsilon1)-eta2*(delta-epsilon2)^2-beta1*(tau-gamma1)-beta2*(tau-gamma2)^2 CoolPropDbl c, l_double, omega, m_double, eta1, epsilon1, eta2, epsilon2, beta1, gamma1, beta2, gamma2; /// If l_i or m_i are integers, we will store them as integers in order to call pow(double, int) rather than pow(double, double) int l_int, m_int; /// If l is an integer, store a boolean flag so we can evaluate the correct pow() function bool l_is_int, m_is_int; ResidualHelmholtzGeneralizedExponentialElement() { n = 0; d = 0; t = 0; c = 0; l_double = 0; omega = 0; m_double = 0; eta1 = 0; epsilon1 = 0; eta2 = 0; epsilon2 = 0; beta1 = 0; gamma1 = 0; beta2 = 0; gamma2 = 0; l_int = 0; m_int = 0; l_is_int = false; m_is_int = true; }; }; /** \brief A generalized residual helmholtz energy container that can deal with a wide range of terms which can be converted to this general form * * \f$ \alpha^r=\sum_i n_i \delta^{d_i} \tau^{t_i}\exp(u_i) \f$ * * where \f$ u_i \f$ is given by * * \f$ u_i = -c_i\delta^{l_i}-\omega_i\tau^{m_i}-\eta_{1,i}(\delta-\epsilon_{1,i})-\eta_{2,i}(\delta-\epsilon_{2,i})^2-\beta_{1,i}(\tau-\gamma_{1,i})-\beta_{2,i}(\tau-\gamma_{2,i})^2 \f$ */ class ResidualHelmholtzGeneralizedExponential : public BaseHelmholtzTerm { public: bool delta_li_in_u, tau_mi_in_u, eta1_in_u, eta2_in_u, beta1_in_u, beta2_in_u, finished; std::vector s; std::size_t N; // These variables are for the exp(u) part // u is given by -c*delta^l_i-omega*tau^m_i-eta1*(delta-epsilon1)-eta2*(delta-epsilon2)^2-beta1*(tau-gamma1)-beta2*(tau-gamma2)^2 std::vector n, d, t, c, l_double, omega, m_double, eta1, epsilon1, eta2, epsilon2, beta1, gamma1, beta2, gamma2; // If l_i or m_i are integers, we will store them as integers in order to call pow(double, int) rather than pow(double, double) std::vector l_int, m_int; //Eigen::ArrayXd uE, du_ddeltaE, du_dtauE, d2u_ddelta2E, d2u_dtau2E, d3u_ddelta3E, d3u_dtau3E; std::vector elements; // Default Constructor ResidualHelmholtzGeneralizedExponential() : delta_li_in_u(false), tau_mi_in_u(false), eta1_in_u(false), eta2_in_u(false), beta1_in_u(false), beta2_in_u(false), finished(false), N(0) {}; /** \brief Add and convert an old-style power (polynomial) term to generalized form * * Term of the format * \f$ \alpha^r=\left\lbrace\begin{array}{cc}\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} & l_i=0\\ \displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\delta^{l_i}) & l_i\neq 0\end{array}\right.\f$ */ void add_Power(const std::vector& n, const std::vector& d, const std::vector& t, const std::vector& l) { for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzGeneralizedExponentialElement el; el.n = n[i]; el.d = d[i]; el.t = t[i]; el.l_double = l[i]; el.l_int = (int)el.l_double; if (el.l_double > 0) el.c = 1.0; else el.c = 0.0; elements.push_back(el); } delta_li_in_u = true; }; /** \brief Add and convert an old-style exponential term to generalized form * * Term of the format * \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-g_i\delta^{l_i}) \f$ */ void add_Exponential(const std::vector& n, const std::vector& d, const std::vector& t, const std::vector& g, const std::vector& l) { for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzGeneralizedExponentialElement el; el.n = n[i]; el.d = d[i]; el.t = t[i]; el.c = g[i]; el.l_double = l[i]; el.l_int = (int)el.l_double; elements.push_back(el); } delta_li_in_u = true; } /** \brief Add and convert an old-style Gaussian term to generalized form * * Term of the format * \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\eta_i(\delta-\epsilon_i)^2-\beta_i(\tau-\gamma_i)^2)\f$ */ void add_Gaussian(const std::vector& n, const std::vector& d, const std::vector& t, const std::vector& eta, const std::vector& epsilon, const std::vector& beta, const std::vector& gamma) { for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzGeneralizedExponentialElement el; el.n = n[i]; el.d = d[i]; el.t = t[i]; el.eta2 = eta[i]; el.epsilon2 = epsilon[i]; el.beta2 = beta[i]; el.gamma2 = gamma[i]; elements.push_back(el); } eta2_in_u = true; beta2_in_u = true; }; /** \brief Add and convert an old-style Gaussian term from GERG 2008 natural gas model to generalized form * * Term of the format * \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\eta_i(\delta-\epsilon_i)^2-\beta_i(\delta-\gamma_i))\f$ */ void add_GERG2008Gaussian(const std::vector& n, const std::vector& d, const std::vector& t, const std::vector& eta, const std::vector& epsilon, const std::vector& beta, const std::vector& gamma) { for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzGeneralizedExponentialElement el; el.n = n[i]; el.d = d[i]; el.t = t[i]; el.eta2 = eta[i]; el.epsilon2 = epsilon[i]; el.eta1 = beta[i]; el.epsilon1 = gamma[i]; elements.push_back(el); } eta2_in_u = true; eta1_in_u = true; }; /** \brief Add and convert a term from Lemmon and Jacobsen (2005) used for R125 * * Term of the format * \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-\delta^{l_i}-\tau^{m_i})\f$ */ void add_Lemmon2005(const std::vector& n, const std::vector& d, const std::vector& t, const std::vector& l, const std::vector& m) { for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzGeneralizedExponentialElement el; el.n = n[i]; el.d = d[i]; el.t = t[i]; el.c = 1.0; el.omega = 1.0; el.l_double = l[i]; el.m_double = m[i]; el.l_int = (int)el.l_double; el.m_int = (int)el.m_double; elements.push_back(el); } delta_li_in_u = true; tau_mi_in_u = true; }; /** \brief Add and convert a double-exponential term * * Term of the format * \f$ \alpha^r=\displaystyle\sum_i n_i \delta^{d_i} \tau^{t_i} \exp(-gd_j\delta^{ld_j}-gt_j\tau^{lt_i})\f$ */ void add_DoubleExponential(const std::vector& n, const std::vector& d, const std::vector& t, const std::vector& gd, const std::vector& ld, const std::vector& gt, const std::vector& lt) { for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzGeneralizedExponentialElement el; el.n = n[i]; el.d = d[i]; el.t = t[i]; el.c = gd[i]; el.l_double = ld[i]; el.l_int = (int)el.l_double; el.omega = gt[i]; el.m_double = lt[i]; elements.push_back(el); } delta_li_in_u = true; tau_mi_in_u = true; }; void finish() { n.resize(elements.size()); d.resize(elements.size()); t.resize(elements.size()); c.resize(elements.size()); omega.resize(elements.size()); l_double.resize(elements.size()); l_int.resize(elements.size()); m_double.resize(elements.size()); m_int.resize(elements.size()); epsilon2.resize(elements.size()); eta2.resize(elements.size()); gamma2.resize(elements.size()); beta2.resize(elements.size()); for (std::size_t i = 0; i < elements.size(); ++i) { n[i] = elements[i].n; d[i] = elements[i].d; t[i] = elements[i].t; c[i] = elements[i].c; omega[i] = elements[i].omega; l_double[i] = elements[i].l_double; l_int[i] = elements[i].l_int; m_double[i] = elements[i].m_double; m_int[i] = elements[i].m_int; epsilon2[i] = elements[i].epsilon2; eta2[i] = elements[i].eta2; gamma2[i] = elements[i].gamma2; beta2[i] = elements[i].beta2; // See if l is an integer, and store a flag if it is elements[i].l_is_int = (std::abs(static_cast(elements[i].l_double) - elements[i].l_double) < 1e-14); } // uE.resize(elements.size()); // du_ddeltaE.resize(elements.size()); // du_dtauE.resize(elements.size()); // d2u_ddelta2E.resize(elements.size()); // d2u_dtau2E.resize(elements.size()); // d3u_ddelta3E.resize(elements.size()); // d3u_dtau3E.resize(elements.size()); finished = true; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc); void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; //void allEigen(const CoolPropDbl &tau, const CoolPropDbl &delta, HelmholtzDerivatives &derivs) throw(); #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; struct ResidualHelmholtzNonAnalyticElement { CoolPropDbl n, a, b, beta, A, B, C, D; }; class ResidualHelmholtzNonAnalytic : public BaseHelmholtzTerm { public: std::size_t N; std::vector s; std::vector elements; /// Default Constructor ResidualHelmholtzNonAnalytic() { N = 0; }; /// Destructor. No implementation ~ResidualHelmholtzNonAnalytic() {}; /// Constructor ResidualHelmholtzNonAnalytic(const std::vector& n, const std::vector& a, const std::vector& b, const std::vector& beta, const std::vector& A, const std::vector& B, const std::vector& C, const std::vector& D) { N = n.size(); s.resize(N); for (std::size_t i = 0; i < n.size(); ++i) { ResidualHelmholtzNonAnalyticElement el; el.n = n[i]; el.a = a[i]; el.b = b[i]; el.beta = beta[i]; el.A = A[i]; el.B = B[i]; el.C = C[i]; el.D = D[i]; elements.push_back(el); } }; void to_json(rapidjson::Value& el, rapidjson::Document& doc); void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; class ResidualHelmholtzGeneralizedCubic : public BaseHelmholtzTerm { protected: shared_ptr m_abstractcubic; std::vector z; /// Vector of mole fractions, will be initialized to [1.0] since this is a pure fluid public: bool enabled; /// Default Constructor ResidualHelmholtzGeneralizedCubic() { enabled = false; }; /// Constructor given an abstract cubic instance ResidualHelmholtzGeneralizedCubic(shared_ptr& ac) : m_abstractcubic(ac) { enabled = true; z = std::vector(1, 1); // Init the vector to [1.0] }; void to_json(rapidjson::Value& el, rapidjson::Document& doc); void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; class ResidualHelmholtzGaoB : public BaseHelmholtzTerm { protected: std::vector n, t, d, eta, beta, gamma, epsilon, b; public: bool enabled; /// Default Constructor ResidualHelmholtzGaoB() { enabled = false; }; /// Constructor given coefficients ResidualHelmholtzGaoB(const std::vector& n, const std::vector& t, const std::vector& d, const std::vector& eta, const std::vector& beta, const std::vector& gamma, const std::vector& epsilon, const std::vector& b) : n(n), t(t), d(d), eta(eta), beta(beta), gamma(gamma), epsilon(epsilon), b(b) { enabled = true; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc); void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; /// The generalized Lee-Kesler formulation of Xiang & Deiters: doi:10.1016/j.ces.2007.11.029 class ResidualHelmholtzXiangDeiters : public BaseHelmholtzTerm { public: bool enabled; ResidualHelmholtzGeneralizedExponential phi0, phi1, phi2; CoolPropDbl Tc, pc, rhomolarc, acentric, R, theta; /// Default Constructor ResidualHelmholtzXiangDeiters() : Tc(_HUGE), pc(_HUGE), rhomolarc(_HUGE), acentric(_HUGE), R(_HUGE), theta(_HUGE) { enabled = false; }; /// Constructor ResidualHelmholtzXiangDeiters(const CoolPropDbl Tc, const CoolPropDbl pc, const CoolPropDbl rhomolarc, const CoolPropDbl acentric, const CoolPropDbl R); void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; class ResidualHelmholtzSAFTAssociating : public BaseHelmholtzTerm { protected: double a, m, epsilonbar, vbarn, kappabar; CoolPropDbl Deltabar(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl dDeltabar_ddelta__consttau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d2Deltabar_ddelta2__consttau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl dDeltabar_dtau__constdelta(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d2Deltabar_dtau2__constdelta(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d2Deltabar_ddelta_dtau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3Deltabar_dtau3__constdelta(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3Deltabar_ddelta_dtau2(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3Deltabar_ddelta3__consttau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3Deltabar_ddelta2_dtau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl X(const CoolPropDbl& delta, const CoolPropDbl& Deltabar) const; CoolPropDbl dX_dDeltabar__constdelta(const CoolPropDbl& delta, const CoolPropDbl& Deltabar) const; CoolPropDbl dX_ddelta__constDeltabar(const CoolPropDbl& delta, const CoolPropDbl& Deltabar) const; CoolPropDbl dX_dtau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl dX_ddelta(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d2X_dtau2(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d2X_ddeltadtau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d2X_ddelta2(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3X_dtau3(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3X_ddelta3(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3X_ddeltadtau2(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl d3X_ddelta2dtau(const CoolPropDbl& tau, const CoolPropDbl& delta) const; CoolPropDbl g(const CoolPropDbl& eta) const; CoolPropDbl dg_deta(const CoolPropDbl& eta) const; CoolPropDbl d2g_deta2(const CoolPropDbl& eta) const; CoolPropDbl d3g_deta3(const CoolPropDbl& eta) const; CoolPropDbl eta(const CoolPropDbl& delta) const; public: /// Default constructor ResidualHelmholtzSAFTAssociating() : a(_HUGE), m(_HUGE), epsilonbar(_HUGE), vbarn(_HUGE), kappabar(_HUGE) { disabled = true; }; // Constructor ResidualHelmholtzSAFTAssociating(double a, double m, double epsilonbar, double vbarn, double kappabar) : a(a), m(m), epsilonbar(epsilonbar), vbarn(vbarn), kappabar(kappabar) { disabled = false; }; bool disabled; //Destructor. No Implementation ~ResidualHelmholtzSAFTAssociating() {}; void to_json(rapidjson::Value& el, rapidjson::Document& doc); CoolPropDbl dTau4(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { return 1e99; }; CoolPropDbl dDelta_dTau3(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { return 1e99; }; CoolPropDbl dDelta2_dTau2(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { return 1e99; }; CoolPropDbl dDelta3_dTau(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { return 1e99; }; CoolPropDbl dDelta4(const CoolPropDbl& tau, const CoolPropDbl& delta) throw() { return 1e99; }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& deriv) throw(); }; class BaseHelmholtzContainer { protected: std::array cache = create_filled_array(_HUGE); std::array is_cached = create_filled_array(false); constexpr static std::size_t i00 = 0, i01 = 1, i02 = 2, i03 = 3, i04 = 4, i10 = 5, i11 = 6, i12 = 7, i13 = 8, i20 = 9, i21 = 10, i22 = 11, i30 = 12, i31 = 13, i40 = 14; bool cache_valid(std::size_t i) const { return is_cached[i]; } public: void clear() { memset(cache.data(), 0, sizeof(cache)); memset(is_cached.data(), false, sizeof(is_cached)); }; virtual void empty_the_EOS() = 0; virtual HelmholtzDerivatives all(const CoolPropDbl tau, const CoolPropDbl delta, bool cache_values) = 0; CoolPropDbl base(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i00) || dont_use_cache) return all(tau, delta, false).alphar; else return cache[i00]; }; CoolPropDbl dDelta(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i10) || dont_use_cache) return all(tau, delta, false).dalphar_ddelta; else return cache[i10]; }; CoolPropDbl dTau(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i01) || dont_use_cache) return all(tau, delta, false).dalphar_dtau; else return cache[i01]; }; CoolPropDbl dDelta2(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i20) || dont_use_cache) return all(tau, delta, false).d2alphar_ddelta2; else return cache[i20]; }; CoolPropDbl dDelta_dTau(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i11) || dont_use_cache) return all(tau, delta, false).d2alphar_ddelta_dtau; else return cache[i11]; }; CoolPropDbl dTau2(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i02) || dont_use_cache) return all(tau, delta, false).d2alphar_dtau2; else return cache[i02]; }; CoolPropDbl dDelta3(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i30) || dont_use_cache) return all(tau, delta, false).d3alphar_ddelta3; else return cache[i30]; }; CoolPropDbl dDelta2_dTau(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i21) || dont_use_cache) return all(tau, delta, false).d3alphar_ddelta2_dtau; else return cache[i21]; }; CoolPropDbl dDelta_dTau2(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i12) || dont_use_cache) return all(tau, delta, false).d3alphar_ddelta_dtau2; else return cache[i12]; }; CoolPropDbl dTau3(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { if (!cache_valid(i03) || dont_use_cache) return all(tau, delta, false).d3alphar_dtau3; else return cache[i03]; }; CoolPropDbl dDelta4(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { return all(tau, delta, false).d4alphar_ddelta4; }; CoolPropDbl dDelta3_dTau(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { return all(tau, delta, false).d4alphar_ddelta3_dtau; }; CoolPropDbl dDelta2_dTau2(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { return all(tau, delta, false).d4alphar_ddelta2_dtau2; }; CoolPropDbl dDelta_dTau3(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { return all(tau, delta, false).d4alphar_ddelta_dtau3; }; CoolPropDbl dTau4(CoolPropDbl tau, CoolPropDbl delta, const bool dont_use_cache = false) { return all(tau, delta, false).d4alphar_dtau4; }; }; class ResidualHelmholtzContainer : public BaseHelmholtzContainer { public: ResidualHelmholtzNonAnalytic NonAnalytic; ResidualHelmholtzSAFTAssociating SAFT; ResidualHelmholtzGeneralizedExponential GenExp; ResidualHelmholtzGeneralizedCubic cubic; ResidualHelmholtzXiangDeiters XiangDeiters; ResidualHelmholtzGaoB GaoB; void empty_the_EOS() { NonAnalytic = ResidualHelmholtzNonAnalytic(); SAFT = ResidualHelmholtzSAFTAssociating(); GenExp = ResidualHelmholtzGeneralizedExponential(); cubic = ResidualHelmholtzGeneralizedCubic(); XiangDeiters = ResidualHelmholtzXiangDeiters(); GaoB = ResidualHelmholtzGaoB(); }; HelmholtzDerivatives all(const CoolPropDbl tau, const CoolPropDbl delta, bool cache_values = false) { HelmholtzDerivatives derivs; // zeros out the elements GenExp.all(tau, delta, derivs); NonAnalytic.all(tau, delta, derivs); SAFT.all(tau, delta, derivs); cubic.all(tau, delta, derivs); XiangDeiters.all(tau, delta, derivs); GaoB.all(tau, delta, derivs); if (cache_values) { cache[i00] = derivs.alphar; cache[i10] = derivs.dalphar_ddelta; cache[i01] = derivs.dalphar_dtau; cache[i20] = derivs.d2alphar_ddelta2; cache[i02] = derivs.d2alphar_dtau2; cache[i11] = derivs.d2alphar_ddelta_dtau; cache[i30] = derivs.d3alphar_ddelta3; cache[i03] = derivs.d3alphar_dtau3; cache[i21] = derivs.d3alphar_ddelta2_dtau; cache[i12] = derivs.d3alphar_ddelta_dtau2; memset(is_cached.data(), true, sizeof(is_cached)); } return derivs; }; }; // ############################################################################# // ############################################################################# // ############################################################################# // IDEAL GAS TERMS // ############################################################################# // ############################################################################# // ############################################################################# /// The leading term in the EOS used to set the desired reference state /** \f[ \alpha^0 = \log(\delta)+a_1+a_2\tau \f] */ class IdealHelmholtzLead : public BaseHelmholtzTerm { private: CoolPropDbl a1, a2; bool enabled; public: // Default constructor IdealHelmholtzLead() : a1(_HUGE), a2(_HUGE), enabled(false) {} // Constructor IdealHelmholtzLead(CoolPropDbl a1, CoolPropDbl a2) : a1(a1), a2(a2), enabled(true) {} bool is_enabled() const { return enabled; } void to_json(rapidjson::Value& el, rapidjson::Document& doc) { el.AddMember("type", "IdealHelmholtzLead", doc.GetAllocator()); el.AddMember("a1", static_cast(a1), doc.GetAllocator()); el.AddMember("a2", static_cast(a2), doc.GetAllocator()); }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; /// The term in the EOS used to shift the reference state of the fluid /** \f[ \alpha^0 = a_1+a_2\tau \f] */ class IdealHelmholtzEnthalpyEntropyOffset : public BaseHelmholtzTerm { private: CoolPropDbl a1, a2; // Use these variables internally std::string reference; bool enabled; public: IdealHelmholtzEnthalpyEntropyOffset() : a1(_HUGE), a2(_HUGE), enabled(false) {} // Constructor IdealHelmholtzEnthalpyEntropyOffset(CoolPropDbl a1, CoolPropDbl a2, const std::string& ref) : a1(a1), a2(a2), reference(ref), enabled(true) {} // Set the values in the class void set(CoolPropDbl a1, CoolPropDbl a2, const std::string& ref) { // If it doesn't already exist, just set the values if (enabled == false) { this->a1 = a1; this->a2 = a2; enabled = true; } else if (ref == "DEF") { this->a1 = 0.0; this->a2 = 0.0; enabled = false; } else { // Otherwise, increment the values this->a1 += a1; this->a2 += a2; enabled = true; } this->reference = ref; } bool is_enabled() const { return enabled; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc) { el.AddMember("type", "IdealHelmholtzEnthalpyEntropyOffset", doc.GetAllocator()); el.AddMember("a1", static_cast(a1), doc.GetAllocator()); el.AddMember("a2", static_cast(a2), doc.GetAllocator()); }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; /** \f[ \alpha^0 = a_1\ln\tau \f] */ class IdealHelmholtzLogTau : public BaseHelmholtzTerm { private: CoolPropDbl a1; bool enabled; public: /// Default constructor IdealHelmholtzLogTau() : a1(_HUGE), enabled(false) {} // Constructor IdealHelmholtzLogTau(CoolPropDbl a1) : a1(a1), enabled(true) {} bool is_enabled() const { return enabled; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc) { el.AddMember("type", "IdealHelmholtzLogTau", doc.GetAllocator()); el.AddMember("a1", static_cast(a1), doc.GetAllocator()); }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; /** \f[ \alpha^0 = \displaystyle\sum_i n_i\tau^{t_i} \f] */ class IdealHelmholtzPower : public BaseHelmholtzTerm { private: std::vector n, t; // Use these variables internally std::size_t N; bool enabled; public: IdealHelmholtzPower() : N(0), enabled(false) {}; // Constructor IdealHelmholtzPower(const std::vector& n, const std::vector& t) : n(n), t(t), N(n.size()), enabled(true) {}; bool is_enabled() const { return enabled; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc) { el.AddMember("type", "IdealHelmholtzPower", doc.GetAllocator()); cpjson::set_long_double_array("n", n, el, doc); cpjson::set_long_double_array("t", t, el, doc); }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; /** \f[ \alpha^0 = \displaystyle\sum_i n_i\log[c_i+d_i\exp(\theta_i\tau)] \f] To convert conventional Plank-Einstein forms, given by \f$ \frac{c_p^0}{R} = a_k\displaystyle\frac{\left( b_k/T \right)^2\exp \left( b_k/T \right)}{\left(\exp \left(b_k/T\right) - 1 \right)^2} \f$ and \f$ \alpha^0 = a_k\ln \left[1 - \exp \left( \frac{-b_k\tau}{T_c} \right) \right] \f$ use \f$c = 1\f$, \f$d = -1\f$, \f$n = a\f$, \f$\theta = -\displaystyle\frac{b_k}{T_c}\f$ To convert the second form of Plank-Einstein terms, given by \f$ \frac{c_p^0}{R} = a_k\displaystyle\frac{\left( -b_k/T \right)^2\exp \left( b_k/T \right)}{c\left(\exp \left(-b_k/T\right) + 1 \right)^2} \f$ and \f$ \alpha^0 = a_k\ln \left[c + \exp \left( \frac{b_k\tau}{T_c} \right) \right] \f$ use \f$c = 1\f$, \f$d = 1\f$, \f$n = -a\f$, \f$\theta = \displaystyle\frac{b_k}{T_c}\f$ Converting Aly-Lee tems is a bit more complex Aly-Lee starts as \f[\frac{c_p^0}{R_u} = A + B\left(\frac{C/T}{\sinh(C/T)}\right)^2 + D\left(\frac{E/T}{\cosh(E/T)}\right)^2\f] Constant is separated out, and handled separately. sinh part can be expanded as \f[B\left(\frac{C/T}{\sinh(C/T)}\right)^2 = \frac{B(-2C/T)^2\exp(-2C/T)}{(1-\exp(-2C/T))^2}\f] where \f[n_k = B\f] \f[\theta_k = -\frac{2C}{T_c}\f] \f[c_k = 1\f] \f[d_k = -1\f] cosh part can be expanded as \f[D\left(\frac{E/T}{\cosh(E/T)}\right)^2 = \frac{D(-2E/T)^2\exp(-2E/T)}{(1+\exp(-2E/T))^2}\f] where \f[n_k = -D\f] \f[\theta_k = -\frac{2E}{T_c}\f] \f[c_k = 1\f] \f[d_k = 1\f] */ class IdealHelmholtzPlanckEinsteinGeneralized : public BaseHelmholtzTerm { private: std::vector n, theta, c, d; // Use these variables internally std::size_t N; bool enabled; public: IdealHelmholtzPlanckEinsteinGeneralized() : N(0), enabled(false) {} // Constructor with std::vector instances IdealHelmholtzPlanckEinsteinGeneralized(const std::vector& n, const std::vector& theta, const std::vector& c, const std::vector& d) : n(n), theta(theta), c(c), d(d), N(n.size()), enabled(true) {} // Extend the vectors to allow for multiple instances feeding values to this function void extend(const std::vector& n, const std::vector& theta, const std::vector& c, const std::vector& d) { this->n.insert(this->n.end(), n.begin(), n.end()); this->theta.insert(this->theta.end(), theta.begin(), theta.end()); this->c.insert(this->c.end(), c.begin(), c.end()); this->d.insert(this->d.end(), d.begin(), d.end()); N += n.size(); } bool is_enabled() const { return enabled; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc) { el.AddMember("type", "IdealHelmholtzPlanckEinsteinGeneralized", doc.GetAllocator()); cpjson::set_long_double_array("n", n, el, doc); cpjson::set_long_double_array("theta", theta, el, doc); }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; class IdealHelmholtzCP0Constant : public BaseHelmholtzTerm { private: double cp_over_R, Tc, T0, tau0; // Use these variables internally bool enabled; public: /// Default constructor IdealHelmholtzCP0Constant() : cp_over_R(_HUGE), Tc(_HUGE), T0(_HUGE), tau0(_HUGE) { enabled = false; }; /// Constructor with just a single double value IdealHelmholtzCP0Constant(CoolPropDbl cp_over_R, CoolPropDbl Tc, CoolPropDbl T0) : cp_over_R(cp_over_R), Tc(Tc), T0(T0) { enabled = true; tau0 = Tc / T0; }; /// Destructor ~IdealHelmholtzCP0Constant() {}; bool is_enabled() const { return enabled; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc) { el.AddMember("type", "IdealGasHelmholtzCP0Constant", doc.GetAllocator()); el.AddMember("cp_over_R", cp_over_R, doc.GetAllocator()); el.AddMember("Tc", Tc, doc.GetAllocator()); el.AddMember("T0", T0, doc.GetAllocator()); }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) throw(); }; class IdealHelmholtzCP0PolyT : public BaseHelmholtzTerm { private: std::vector c, t; CoolPropDbl Tc, T0, tau0; // Use these variables internally std::size_t N; bool enabled; public: IdealHelmholtzCP0PolyT() : Tc(_HUGE), T0(_HUGE), tau0(_HUGE), N(0), enabled(false) {} /// Constructor with std::vectors IdealHelmholtzCP0PolyT(const std::vector& c, const std::vector& t, double Tc, double T0) : c(c), t(t), Tc(Tc), T0(T0), tau0(Tc / T0), N(c.size()), enabled(true) { assert(c.size() == t.size()); } void extend(const std::vector& c, const std::vector& t) { this->c.insert(this->c.end(), c.begin(), c.end()); this->t.insert(this->t.end(), t.begin(), t.end()); N += c.size(); } bool is_enabled() const { return enabled; }; void to_json(rapidjson::Value& el, rapidjson::Document& doc); void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; /** */ class IdealHelmholtzGERG2004Sinh : public BaseHelmholtzTerm { private: std::vector n, theta; CoolPropDbl Tc, _Tr; std::size_t N; bool enabled; public: IdealHelmholtzGERG2004Sinh() : Tc(_HUGE), _Tr(_HUGE), N(0), enabled(false) {} /// Constructor with std::vectors IdealHelmholtzGERG2004Sinh(const std::vector& n, const std::vector& theta, double Tc) : n(n), theta(theta), Tc(Tc), _Tr(_HUGE), N(n.size()), enabled(true) { assert(n.size() == theta.size()); } void extend(const std::vector& c, const std::vector& t) { this->n.insert(this->n.end(), n.begin(), n.end()); this->theta.insert(this->theta.end(), theta.begin(), theta.end()); N += c.size(); } void set_Tred(CoolPropDbl Tr) { this->_Tr = Tr; } bool is_enabled() const { return enabled; }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; class IdealHelmholtzGERG2004Cosh : public BaseHelmholtzTerm { private: std::vector n, theta; CoolPropDbl Tc, _Tr; std::size_t N; bool enabled; public: IdealHelmholtzGERG2004Cosh() : Tc(_HUGE), _Tr(_HUGE), N(0), enabled(false) {} /// Constructor with std::vectors IdealHelmholtzGERG2004Cosh(const std::vector& n, const std::vector& theta, double Tc) : n(n), theta(theta), Tc(Tc), _Tr(_HUGE), N(n.size()), enabled(true) { assert(n.size() == theta.size()); } void extend(const std::vector& n, const std::vector& theta) { this->n.insert(this->n.end(), n.begin(), n.end()); this->theta.insert(this->theta.end(), theta.begin(), theta.end()); N += n.size(); } void set_Tred(CoolPropDbl Tr) { this->_Tr = Tr; } bool is_enabled() const { return enabled; }; void all(const CoolPropDbl& tau, const CoolPropDbl& delta, HelmholtzDerivatives& derivs) override; #if ENABLE_CATCH virtual mcx::MultiComplex one_mcx(const mcx::MultiComplex& tau, const mcx::MultiComplex& delta) const override; #endif }; ///// Term in the ideal-gas specific heat equation that is based on Aly-Lee formulation ///** Specific heat is of the form: //\f[ //\frac{c_p^0}{R_u} = A + B\left(\frac{C/T}{\sinh(C/T)}\right)^2 + D\left(\frac{E/T}{\cosh(E/T)}\right)^2 //\f] //Second partial of ideal-gas Helmholtz energy given directly by specific heat (\f$\displaystyle\alpha_{\tau\tau}^0=-\frac{1}{\tau^2}\frac{c_p^0}{R_u} \f$) - this is obtained by real gas \f$c_p\f$ relationship, and killing off residual Helmholtz terms //\f[ //\alpha^0_{\tau\tau} = -\frac{A}{\tau^2} - \frac{B}{\tau^2}\left(\frac{C/T}{\sinh(C/T)}\right)^2 - \frac{D}{\tau^2}\left(\frac{E/T}{\cosh(E/T)}\right)^2 //\f] //or in terms of \f$ \tau \f$: //\f[ //\alpha^0_{\tau\tau} = -\frac{A}{\tau^2} - \frac{BC^2}{T_c^2}\left(\frac{1}{\sinh(C\tau/T_c)}\right)^2 - \frac{DE^2}{T_c^2}\left(\frac{1}{\cosh(E\tau/T_c)}\right)^2 //\f] //Third partial: //\f[ //\alpha^0_{\tau\tau\tau} = 2\frac{A}{\tau^3} + 2\frac{BC^3}{T_c^3}\frac{\cosh(C\tau/T_c)}{\sinh^3(C\tau/T_c)} +2 \frac{DE^3}{T_c^3}\frac{\sinh(E\tau/T_c)}{\cosh^3(E\tau/T_c)} //\f] //Now coming back to the ideal gas Helmholtz energy definition: //\f[ //\alpha^0 = -\tau\displaystyle\int_{\tau_0}^{\tau} \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau+\displaystyle\int_{\tau_0}^{\tau} \frac{1}{\tau}\frac{c_p^0}{R_u}d\tau //\f] //Applying derivative //\f[ //\alpha^0_{\tau} = -\displaystyle\int_{\tau_0}^{\tau} \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau-\tau\frac{\partial}{\partial \tau}\left[\displaystyle\int_{\tau_0}^{\tau} \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau \right]+\frac{\partial}{\partial \tau}\left[\displaystyle\int_{\tau_0}^{\tau} \frac{1}{\tau}\frac{c_p^0}{R_u}d\tau \right] //\f] //Fundamental theorem of calculus //\f[ //\alpha^0_{\tau} = -\int_{\tau_0}^{\tau} \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau-\tau \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau+\frac{1}{\tau}\frac{c_p^0}{R_u} //\f] //Last two terms cancel, leaving //\f[ //\alpha^0_{\tau} = -\int_{\tau_0}^{\tau} \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau //\f] //Another derivative yields (from fundamental theorem of calculus) //\f[ //\alpha^0_{\tau\tau} = - \frac{1}{\tau^2}\frac{c_p^0}{R_u} //\f] // //see also Jaeschke and Schley, 1995, (http://link.springer.com/article/10.1007%2FBF02083547#page-1) //*/ ///* //class IdealHelmholtzCP0AlyLee : public BaseHelmholtzTerm{ //private: // std::vector c; // CoolPropDbl Tc, tau0, T0; // Use these variables internally // bool enabled; //public: // IdealHelmholtzCP0AlyLee(){enabled = false;}; // // /// Constructor with std::vectors // IdealHelmholtzCP0AlyLee(const std::vector &c, double Tc, double T0) // :c(c), Tc(Tc), T0(T0) // { // tau0=Tc/T0; // enabled = true; // }; // // /// Destructor // ~IdealHelmholtzCP0AlyLee(){}; // // bool is_enabled() const {return enabled;}; // // void to_json(rapidjson::Value &el, rapidjson::Document &doc); // // // /// The antiderivative given by \f$ \displaystyle\int \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau \f$ // /** // sympy code for this derivative: // // from sympy import * // a1,a2,a3,a4,a5,Tc,tau = symbols('a1,a2,a3,a4,a5,Tc,tau', real = True) // integrand = a1 + a2*(a3/Tc/sinh(a3*tau/Tc))**2 + a4*(a5/Tc/cosh(a5*tau/Tc))**2 // integrand = integrand.rewrite(exp) // antideriv = trigsimp(integrate(integrand,tau)) // display(antideriv) // print latex(antideriv) // print ccode(antideriv) // // \f[ // \displaystyle\int \frac{1}{\tau^2}\frac{c_p^0}{R_u}d\tau = -\frac{a_0}{\tau}+\frac{2a_1a_2}{T_c\left[\exp\left(-\frac{2a_2\tau}{T_c}\right)-1\right]}+\frac{2a_3a_4}{T_c\left[\exp\left(-\frac{2a_4\tau}{T_c}\right)+1\right]} // \f] // */ // CoolPropDbl anti_deriv_cp0_tau2(const CoolPropDbl &tau); // // /// The antiderivative given by \f$ \displaystyle\int \frac{1}{\tau}\frac{c_p^0}{R_u}d\tau \f$ // /** // sympy code for this derivative: // // a_0,a_1,a_2,a_3,a_4,Tc,tau = symbols('a_0,a_1,a_2,a_3,a_4,Tc,tau', real = True) // integrand = a_0/tau + a_1/tau*(a_2*tau/Tc/sinh(a_2*tau/Tc))**2 + a_3/tau*(a_4*tau/Tc/cosh(a_4*tau/Tc))**2 // // term2 = a_1/tau*(a_2*tau/Tc/sinh(a_2*tau/Tc))**2 // term2 = term2.rewrite(exp) # Unpack the sinh to exp functions // antideriv2 = trigsimp(integrate(term2,tau)) // display(antideriv2) // print latex(antideriv2) // print ccode(antideriv2) // // term3 = a_3/tau*(a_4*tau/Tc/cosh(a_4*tau/Tc))**2 // term3 = term3.rewrite(exp) # Unpack the cosh to exp functions // antideriv3 = factor(trigsimp(integrate(term3,tau).rewrite(exp))) // display(antideriv3) // print latex(antideriv3) // print ccode(antideriv3) // // Can be broken into three parts (trick is to express \f$sinh\f$ and \f$cosh\f$ in terms of \f$exp\f$ function) // // Term 2: // \f[ // \displaystyle\int \frac{a_1a_2^2}{T_c^2}\frac{\tau}{\sinh\left(\displaystyle\frac{a_2\tau}{T_c}\right)^2} d\tau = \frac{2 a_{1} a_{2} \tau}{- Tc + Tc e^{- \frac{2 a_{2}}{Tc} \tau}} + a_{1} \log{\left (-1 + e^{- \frac{2 a_{2}}{Tc} \tau} \right )} + \frac{2 a_{1}}{Tc} a_{2} \tau // \f] // // Term 3: // \f[ // \displaystyle\int \frac{a_1a_2^2}{T_c^2}\frac{\tau}{\cosh\left(\displaystyle\frac{a_2\tau}{T_c}\right)^2} d\tau = - \frac{a_{3}}{Tc \left(e^{\frac{2 a_{4}}{Tc} \tau} + 1\right)} \left(Tc e^{\frac{2 a_{4}}{Tc} \tau} \log{\left (e^{\frac{2 a_{4}}{Tc} \tau} + 1 \right )} + Tc \log{\left (e^{\frac{2 a_{4}}{Tc} \tau} + 1 \right )} - 2 a_{4} \tau e^{\frac{2 a_{4}}{Tc} \tau}\right) // \f] // */ // CoolPropDbl anti_deriv_cp0_tau(const CoolPropDbl &tau); // // CoolPropDbl base(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(); // CoolPropDbl dDelta(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(){return 0.0;}; // CoolPropDbl dTau(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(); // CoolPropDbl dDelta2(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(){return 0.0;}; // CoolPropDbl dDelta_dTau(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(){return 0.0;}; // CoolPropDbl dTau2(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(); // CoolPropDbl dDelta3(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(){return 0.0;}; // CoolPropDbl dDelta2_dTau(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(){return 0.0;}; // CoolPropDbl dDelta_dTau2(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(){return 0.0;}; // CoolPropDbl dTau3(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(); // CoolPropDbl dTau4(const CoolPropDbl &tau, const CoolPropDbl &delta) throw(); // //}; class IdealHelmholtzContainer : public BaseHelmholtzContainer { private: double _prefactor; public: IdealHelmholtzLead Lead; IdealHelmholtzEnthalpyEntropyOffset EnthalpyEntropyOffsetCore, EnthalpyEntropyOffset; IdealHelmholtzLogTau LogTau; IdealHelmholtzPower Power; IdealHelmholtzPlanckEinsteinGeneralized PlanckEinstein; IdealHelmholtzCP0Constant CP0Constant; IdealHelmholtzCP0PolyT CP0PolyT; IdealHelmholtzGERG2004Cosh GERG2004Cosh; IdealHelmholtzGERG2004Sinh GERG2004Sinh; IdealHelmholtzContainer() : _prefactor(1.0) {}; void set_prefactor(double prefactor) { _prefactor = prefactor; } void set_Tred(double T_red) { GERG2004Cosh.set_Tred(T_red); GERG2004Sinh.set_Tred(T_red); } void empty_the_EOS() { Lead = IdealHelmholtzLead(); EnthalpyEntropyOffsetCore = IdealHelmholtzEnthalpyEntropyOffset(); EnthalpyEntropyOffset = IdealHelmholtzEnthalpyEntropyOffset(); LogTau = IdealHelmholtzLogTau(); Power = IdealHelmholtzPower(); PlanckEinstein = IdealHelmholtzPlanckEinsteinGeneralized(); CP0Constant = IdealHelmholtzCP0Constant(); CP0PolyT = IdealHelmholtzCP0PolyT(); GERG2004Cosh = IdealHelmholtzGERG2004Cosh(); GERG2004Sinh = IdealHelmholtzGERG2004Sinh(); }; HelmholtzDerivatives all(const CoolPropDbl tau, const CoolPropDbl delta, bool cache_values = false) { HelmholtzDerivatives derivs; // zeros out the elements Lead.all(tau, delta, derivs); EnthalpyEntropyOffsetCore.all(tau, delta, derivs); EnthalpyEntropyOffset.all(tau, delta, derivs); LogTau.all(tau, delta, derivs); Power.all(tau, delta, derivs); PlanckEinstein.all(tau, delta, derivs); CP0Constant.all(tau, delta, derivs); CP0PolyT.all(tau, delta, derivs); GERG2004Cosh.all(tau, delta, derivs); GERG2004Sinh.all(tau, delta, derivs); if (cache_values) { cache[i00] = derivs.alphar * _prefactor; cache[i10] = derivs.dalphar_ddelta * _prefactor; cache[i01] = derivs.dalphar_dtau * _prefactor; cache[i20] = derivs.d2alphar_ddelta2 * _prefactor; cache[i02] = derivs.d2alphar_dtau2 * _prefactor; cache[i11] = derivs.d2alphar_ddelta_dtau * _prefactor; cache[i30] = derivs.d3alphar_ddelta3 * _prefactor; cache[i03] = derivs.d3alphar_dtau3 * _prefactor; cache[i21] = derivs.d3alphar_ddelta2_dtau * _prefactor; cache[i12] = derivs.d3alphar_ddelta_dtau2 * _prefactor; memset(is_cached.data(), true, sizeof(is_cached)); } return derivs * _prefactor; }; }; }; /* namespace CoolProp */ #endif