19#include "nlohmann/json.hpp"
28template<
typename NumType>
36 template<
typename TType>
48template<
typename NumType>
56 throw teqp::InvalidArgument(
"coefficients c for Twu alpha function must have length 3");
59 template<
typename TType>
61 return forceeval(
pow(T/Tci,c[2]*(c[1]-1))*exp(c[0]*(1.0-
pow(T/Tci, c[1]*c[2]))));
76template<
typename NumType>
84 throw teqp::InvalidArgument(
"coefficients c for Mathias-Copeman alpha function must have length 3");
87 template<
typename TType>
89 auto x = 1.0 - sqrt(T/Tci);
90 auto paren = 1.0 + c[0]*x + c[1]*x*x + c[2]*x*x*x;
99 std::vector<AlphaFunctionOptions> alphas;
101 if (jalphas.size() != Tc_K.size()){
102 throw teqp::InvalidArgument(
"alpha [size "+std::to_string(jalphas.size())+
"] must be the same size as components [size "+std::to_string(Tc_K.size())+
"]");
104 for (
auto alpha : jalphas){
105 std::string type = alpha.at(
"type");
107 std::valarray<double> c_ = alpha.at(
"c");
108 Eigen::Array3d c = Eigen::Map<Eigen::Array3d>(&(c_[0]), c_.size());
111 else if (type ==
"Mathias-Copeman"){
112 std::valarray<double> c_ = alpha.at(
"c");
113 Eigen::Array3d c = Eigen::Map<Eigen::Array3d>(&(c_[0]), c_.size());
116 else if (type ==
"PR78"){
117 double acentric = alpha.at(
"acentric");
119 if (acentric < 0.491) {
120 mi = 0.37464 + 1.54226*acentric - 0.26992*
pow2(acentric);
123 mi = 0.379642 + 1.48503*acentric -0.164423*
pow2(acentric) + 0.016666*
pow3(acentric);
135template <
typename NumType,
typename AlphaFunctions>
147 template<
typename TType,
typename IndexType>
148 auto get_ai(TType , IndexType i)
const {
return ai[i]; }
150 template<
typename TType,
typename IndexType>
151 auto get_bi(TType , IndexType i)
const {
return bi[i]; }
153 template<
typename IndexType>
156 throw teqp::InvalidArgument(
"kmat rows [" + std::to_string(
kmat.rows()) +
"] and columns [" + std::to_string(
kmat.cols()) +
"] are not identical");
158 if (
kmat.cols() == 0) {
161 else if (
static_cast<std::size_t
>(
kmat.cols()) != N) {
162 throw teqp::InvalidArgument(
"kmat needs to be a square matrix the same size as the number of components [" + std::to_string(N) +
"]");
170 ai.resize(Tc_K.size());
171 bi.resize(Tc_K.size());
172 for (
auto i = 0U; i < Tc_K.size(); ++i) {
187 auto superanc_rhoLV(
double T, std::optional<std::size_t> ifluid = std::nullopt)
const {
189 std::valarray<double> molefracs(
ai.size()); molefracs = 1.0;
196 if (ifluid.value() >
ai.size()-1){
200 molefracs[ifluid.value()] = 1.0;
203 auto b =
get_b(T, molefracs);
204 auto a =
get_a(T, molefracs);
205 auto Ttilde =
R(molefracs)*T*b/a;
206 return std::make_tuple(
212 template<
class VecType>
213 auto R(
const VecType& )
const {
217 template<
typename TType,
typename CompType>
218 auto get_a(TType T,
const CompType& molefracs)
const {
219 std::common_type_t<TType,
decltype(molefracs[0])> a_ = 0.0;
220 for (
auto i = 0U; i < molefracs.size(); ++i) {
221 auto alphai =
forceeval(std::visit([&](
auto& t) {
return t(T); },
alphas[i]));
222 auto ai_ =
forceeval(this->ai[i] * alphai);
223 for (
auto j = 0U; j < molefracs.size(); ++j) {
224 auto alphaj =
forceeval(std::visit([&](
auto& t) {
return t(T); },
alphas[j]));
225 auto aj_ = this->ai[j] * alphaj;
227 a_ = a_ + molefracs[i] * molefracs[j] * aij;
233 template<
typename TType,
typename CompType>
234 auto get_b(TType ,
const CompType& molefracs)
const {
235 std::common_type_t<TType,
decltype(molefracs[0])> b_ = 0.0;
236 for (
auto i = 0U; i < molefracs.size(); ++i) {
237 b_ = b_ + molefracs[i] *
bi[i];
242 template<
typename TType,
typename RhoType,
typename MoleFracType>
245 const MoleFracType& molefrac)
const
247 if (
static_cast<std::size_t
>(molefrac.size()) !=
alphas.size()) {
248 throw std::invalid_argument(
"Sizes do not match");
250 auto b =
get_b(T, molefrac);
251 auto Psiminus = -log(1.0 - b * rho);
253 auto val = Psiminus -
get_a(T, molefrac) / (
m_R_JmolK * T) * Psiplus;
258template <
typename TCType,
typename PCType,
typename AcentricType>
259auto canonical_SRK(TCType Tc_K, PCType pc_Pa, AcentricType acentric,
const std::optional<Eigen::ArrayXXd>& kmat = std::nullopt,
const std::optional<double> R_JmolK = std::nullopt) {
262 AcentricType m = 0.48 + 1.574 * acentric - 0.176 * acentric * acentric;
264 std::vector<AlphaFunctionOptions> alphas;
265 for (
auto i = 0U; i < Tc_K.size(); ++i) {
270 double OmegaA = 1.0 / (9.0 * (cbrt(2) - 1));
271 double OmegaB = (cbrt(2) - 1) / 3;
273 nlohmann::json meta = {
278 {
"kind",
"Soave-Redlich-Kwong"},
281 const std::size_t N = m.size();
282 auto cub =
GenericCubic(Delta1, Delta2, OmegaA, OmegaB,
CubicSuperAncillary::SRK_CODE, Tc_K, pc_Pa, std::move(alphas), kmat.value_or(Eigen::ArrayXXd::Zero(N,N)), R_JmolK.value_or(
constants::R_CODATA2017));
289 std::valarray<double> Tc_K = spec.at(
"Tcrit / K"), pc_Pa = spec.at(
"pcrit / Pa"), acentric = spec.at(
"acentric");
290 Eigen::ArrayXXd kmat(0, 0);
291 if (spec.contains(
"kmat")){
297template <
typename TCType,
typename PCType,
typename AcentricType>
298auto canonical_PR(TCType Tc_K, PCType pc_Pa, AcentricType acentric,
const std::optional<Eigen::ArrayXXd>& kmat = std::nullopt,
const std::optional<double> R_JmolK = std::nullopt) {
299 double Delta1 = 1+sqrt(2.0);
300 double Delta2 = 1-sqrt(2.0);
301 AcentricType m = acentric*0.0;
302 std::vector<AlphaFunctionOptions> alphas;
303 for (
auto i = 0U; i < Tc_K.size(); ++i) {
304 if (acentric[i] < 0.491) {
305 m[i] = 0.37464 + 1.54226*acentric[i] - 0.26992*
pow2(acentric[i]);
308 m[i] = 0.379642 + 1.48503*acentric[i] -0.164423*
pow2(acentric[i]) + 0.016666*
pow3(acentric[i]);
314 double OmegaA = 0.45723552892138218938;
315 double OmegaB = 0.077796073903888455972;
317 nlohmann::json meta = {
322 {
"kind",
"Peng-Robinson"},
326 const std::size_t N = m.size();
327 auto cub =
GenericCubic(Delta1, Delta2, OmegaA, OmegaB,
CubicSuperAncillary::PR_CODE, Tc_K, pc_Pa, std::move(alphas), kmat.value_or(Eigen::ArrayXXd::Zero(N,N)), R_JmolK.value_or(
constants::R_CODATA2017));
334 std::valarray<double> Tc_K = spec.at(
"Tcrit / K"), pc_Pa = spec.at(
"pcrit / Pa"), acentric = spec.at(
"acentric");
335 Eigen::ArrayXXd kmat(0, 0);
336 if (spec.contains(
"kmat")){
345 std::valarray<double> Tc_K = spec.at(
"Tcrit / K"),
346 pc_Pa = spec.at(
"pcrit / Pa"),
347 acentric = spec.at(
"acentric");
350 std::optional<Eigen::ArrayXXd> kmat;
351 if (spec.contains(
"kmat")){
354 std::optional<double> R_JmolK;
355 if (spec.contains(
"R / J/mol/K")){
356 R_JmolK = spec.at(
"R / J/mol/K");
362 std::vector<AlphaFunctionOptions> alphas;
364 double Delta1, Delta2, OmegaA, OmegaB;
365 std::string kind =
"custom";
367 if (spec.at(
"type") ==
"PR" ){
368 Delta1 = 1+sqrt(2.0);
369 Delta2 = 1-sqrt(2.0);
371 OmegaA = 0.45723552892138218938;
372 OmegaB = 0.077796073903888455972;
374 kind =
"Peng-Robinson";
376 if (!spec.contains(
"alpha")){
377 for (
auto i = 0U; i < Tc_K.size(); ++i) {
379 if (acentric[i] < 0.491) {
380 mi = 0.37464 + 1.54226*acentric[i] - 0.26992*
pow2(acentric[i]);
383 mi = 0.379642 + 1.48503*acentric[i] -0.164423*
pow2(acentric[i]) + 0.016666*
pow3(acentric[i]);
389 if (!spec[
"alpha"].is_array()){
395 else if (spec.at(
"type") ==
"SRK"){
398 if (!spec.contains(
"alpha")){
399 for (
auto i = 0U; i < Tc_K.size(); ++i) {
400 double mi = 0.48 + 1.574 * acentric[i] - 0.176 * acentric[i] * acentric[i];
405 if (!spec[
"alpha"].is_array()){
411 OmegaA = 1.0 / (9.0 * (cbrt(2) - 1));
412 OmegaB = (cbrt(2) - 1) / 3;
414 kind =
"Soave-Redlich-Kwong";
418 throw teqp::InvalidArgument(
"Generic cubic EOS are not yet supported (open an issue on github if you want this)");
421 const std::size_t N = Tc_K.size();
422 nlohmann::json meta = {
430 if (spec.contains(
"alpha")){
431 meta[
"alpha"] = spec.at(
"alpha");
434 auto cub =
GenericCubic(Delta1, Delta2, OmegaA, OmegaB, superanc_code, Tc_K, pc_Pa, std::move(alphas), kmat.value_or(Eigen::ArrayXXd::Zero(N,N)), R_JmolK.value_or(
constants::R_CODATA2017));
450 const std::vector<double> Tc_K, pc_Pa;
451 const std::vector<AlphaFunctionOptions> alphas;
452 const std::vector<double> As, Bs, cs_m3mol;
453 const Eigen::ArrayXXd kmat, lmat;
456 auto build_alphas(
const nlohmann::json& j){
457 std::vector<AlphaFunctionOptions> alphas_;
458 std::vector<double> L = j.at(
"Ls"), M = j.at(
"Ms"), N = j.at(
"Ns");
459 if (L.size() != M.size() || M.size() != N.size()){
462 for (
auto i = 0U; i < L.size(); ++i){
463 auto coeffs = (Eigen::Array3d() << L[i], M[i], N[i]).finished();
469 std::vector<double> get_(
const nlohmann::json &j,
const std::string& k)
const {
return j.at(k).get<std::vector<double>>(); }
472 QuantumCorrectedPR(
const nlohmann::json &j) : Tc_K(get_(j,
"Tcrit / K")), pc_Pa(get_(j,
"pcrit / Pa")), alphas(build_alphas(j)), As(get_(j,
"As")), Bs(get_(j,
"Bs")), cs_m3mol(get_(j,
"cs / m^3/mol")), kmat(
build_square_matrix(j.at(
"kmat"))), lmat(
build_square_matrix(j.at(
"lmat"))), Ru(j.value(
"R / J/mol/K",
constants::R_CODATA2017)) {}
476 template<
class VecType>
477 auto R(
const VecType& )
const {
485 template<
typename TType>
486 auto get_bi(std::size_t i,
const TType& T)
const {
487 auto beta =
POW3(1.0 + As[i]/(T+Bs[i]))/
POW3(1.0+As[i]/(Tc_K[i]+Bs[i]));
489 auto b = 0.07780*Tc_K[i]*Ru/pc_Pa[i];
493 template<
typename TType>
494 auto get_ai(std::size_t i,
const TType& T)
const {
495 auto alphai =
forceeval(std::visit([&](
auto& t) {
return t(T); }, alphas[i]));
497 auto OmegaA = 0.45723552892138218938;
498 auto a = OmegaA*
POW2(Tc_K[i]*Ru)/pc_Pa[i];
502 template<
typename TType,
typename FractionsType>
503 auto get_ab(
const TType& T,
const FractionsType& z)
const{
504 using numtype = std::common_type_t<TType,
decltype(z[0])>;
507 std::size_t N = alphas.size();
508 for (
auto i = 0U; i < N; ++i){
511 for (
auto j = 0U; j < N; ++j){
514 b += z[i]*z[j]*(bi + bj)/2.0*(1.0 - lmat(i,j));
515 a += z[i]*z[j]*sqrt(ai*aj)*(1.0 - kmat(i,j));
518 return std::make_tuple(a, b);
520 template<
typename TType,
typename FractionsType>
521 auto get_c(
const TType& ,
const FractionsType& z)
const{
522 using numtype = std::common_type_t<TType,
decltype(z[0])>;
524 std::size_t N = alphas.size();
525 for (
auto i = 0U; i < N; ++i){
526 c += z[i]*cs_m3mol[i];
531 template<
typename TType,
typename RhoType,
typename FractionsType>
532 auto alphar(
const TType& T,
const RhoType& rhoinit,
const FractionsType& molefrac)
const {
533 if (
static_cast<std::size_t
>(molefrac.size()) != alphas.size()) {
534 throw std::invalid_argument(
"Sizes do not match");
537 auto c =
get_c(T, molefrac);
538 auto rho = 1.0/(1.0/rhoinit + c);
539 auto Delta1 = 1.0 + sqrt(2.0);
540 auto Delta2 = 1.0 - sqrt(2.0);
541 auto [a, b] =
get_ab(T, molefrac);
542 auto Psiminus = -log(1.0 - b * rho);
543 auto Psiplus = log((Delta1 * b * rho + 1.0) / (Delta2 * b * rho + 1.0)) / (b * (Delta1 - Delta2));
544 auto val = Psiminus - a / (Ru * T) * Psiplus;
553 auto superanc_rhoLV(
double T,
const std::optional<std::size_t> ifluid = std::nullopt)
const {
554 if (Tc_K.size() != 1) {
555 throw std::invalid_argument(
"function only available for pure species");
558 std::valarray<double> molefracs(Tc_K.size()); molefracs = 1.0;
564 if (ifluid.value() > Tc_K.size()-1){
568 molefracs[ifluid.value()] = 1.0;
571 auto [a, b] =
get_ab(T, molefracs);
572 auto Ttilde =
R(molefracs)*T*b/a;
574 return std::make_tuple(
595 const std::vector<double> delta_1, Tc_K, pc_Pa, k;
596 const Eigen::ArrayXXd kmat, lmat;
598 const std::vector<double> a_c, b_c;
601 std::vector<double> get_(
const nlohmann::json &j,
const std::string& key)
const {
return j.at(key).get<std::vector<double>>(); }
604 auto get_yd1(
double delta_1_){
605 return std::make_tuple(1 + cbrt(2*(1+delta_1_)) + cbrt(4/(1+delta_1_)), (1+delta_1_*delta_1_)/(1+delta_1_));
609 std::vector<double> a_c_(delta_1.size());
610 for (
auto i = 0U; i < delta_1.size(); ++i){
611 auto [y, d_1] = get_yd1(delta_1[i]);
612 auto Omega_a = (3*y*y + 3*y*d_1 + d_1*d_1 + d_1 - 1.0)/
pow(3.0*y + d_1 - 1.0, 2);
613 a_c_[i] = Omega_a*
pow(Ru*Tc_K[i], 2)/pc_Pa[i];
618 std::vector<double> b_c_(delta_1.size());
619 for (
auto i = 0U; i < delta_1.size(); ++i){
620 auto [y, d_1] = get_yd1(delta_1[i]);
621 auto Omega_b = 1.0/(3.0*y + d_1 - 1.0);
622 b_c_[i] = Omega_b*Ru*Tc_K[i]/pc_Pa[i];
628 RKPRCismondi2005(
const nlohmann::json &j) : delta_1(get_(j,
"delta_1")), Tc_K(get_(j,
"Tcrit / K")), pc_Pa(get_(j,
"pcrit / Pa")), k(get_(j,
"k")), kmat(
build_square_matrix(j.at(
"kmat"))), lmat(
build_square_matrix(j.at(
"lmat"))), Ru(j.value(
"R / J/mol/K",
constants::R_CODATA2017)), a_c(build_ac()), b_c(build_bc()) {}
630 template<
class VecType>
631 auto R(
const VecType& )
const {
641 template<
typename TType>
642 auto get_bi(std::size_t i,
const TType& )
const {
646 template<
typename TType>
647 auto get_ai(std::size_t i,
const TType& T)
const {
648 return forceeval(a_c[i]*
pow(3.0/(2.0+T/Tc_K[i]), k[i]));
651 template<
typename TType,
typename FractionsType>
652 auto get_ab(
const TType& T,
const FractionsType& z)
const{
653 using numtype = std::common_type_t<TType,
decltype(z[0])>;
656 std::size_t N = delta_1.size();
657 for (
auto i = 0U; i < N; ++i){
660 for (
auto j = 0U; j < N; ++j){
664 a += z[i]*z[j]*sqrt(ai*aj)*(1.0 - kmat(i,j));
665 b += z[i]*z[j]*(bi + bj)/2.0*(1.0 - lmat(i,j));
669 return std::make_tuple(a, b);
672 template<
typename TType,
typename RhoType,
typename FractionsType>
673 auto alphar(
const TType& T,
const RhoType& rho,
const FractionsType& molefrac)
const {
674 if (
static_cast<std::size_t
>(molefrac.size()) != delta_1.size()) {
675 throw std::invalid_argument(
"Sizes do not match");
679 const auto delta_1_view = Eigen::Map<const Eigen::ArrayXd>(&(delta_1[0]), delta_1.size());
680 const decltype(molefrac[0]) Delta1 = (molefrac*delta_1_view).eval().sum();
682 auto Delta2 = (1.0-Delta1)/(1.0+Delta1);
683 auto [a, b] =
get_ab(T, molefrac);
685 auto Psiminus = -log(1.0 - b*rho);
686 auto Psiplus = log((Delta1 * b * rho + 1.0) / (Delta2 * b * rho + 1.0)) / (b * (Delta1 - Delta2));
687 auto val = Psiminus - a/(this->Ru*T)*Psiplus;
The standard alpha function used by Peng-Robinson and SRK.
auto operator()(const TType &T) const
BasicAlphaFunction(NumType Tci, NumType mi)
auto get_ai(TType, IndexType i) const
GenericCubic(NumType Delta1, NumType Delta2, NumType OmegaA, NumType OmegaB, int superanc_index, const std::valarray< NumType > &Tc_K, const std::valarray< NumType > &pc_Pa, const AlphaFunctions &alphas, const Eigen::ArrayXXd &kmat, const double R_JmolK)
auto get_b(TType, const CompType &molefracs) const
void set_meta(const nlohmann::json &j)
void check_kmat(IndexType N)
auto alphar(const TType &T, const RhoType &rho, const MoleFracType &molefrac) const
auto get_a(TType T, const CompType &molefracs) const
std::valarray< NumType > ai
auto superanc_rhoLV(double T, std::optional< std::size_t > ifluid=std::nullopt) const
auto get_bi(TType, IndexType i) const
const AlphaFunctions alphas
std::valarray< NumType > bi
auto R(const VecType &) const
The Mathias-Copeman alpha function used by Peng-Robinson and SRK.
auto operator()(const TType &T) const
MathiasCopemanAlphaFunction(NumType Tci, const Eigen::Array3d &c)
auto get_ai(std::size_t i, const TType &T) const
auto get_ab(const TType &T, const FractionsType &z) const
auto alphar(const TType &T, const RhoType &rhoinit, const FractionsType &molefrac) const
QuantumCorrectedPR(const nlohmann::json &j)
auto get_bi(std::size_t i, const TType &T) const
auto R(const VecType &) const
auto superanc_rhoLV(double T, const std::optional< std::size_t > ifluid=std::nullopt) const
auto get_c(const TType &, const FractionsType &z) const
RKPRCismondi2005(const nlohmann::json &j)
auto get_bi(std::size_t i, const TType &) const
auto R(const VecType &) const
auto alphar(const TType &T, const RhoType &rho, const FractionsType &molefrac) const
auto get_ab(const TType &T, const FractionsType &z) const
auto get_ai(std::size_t i, const TType &T) const
The Twu alpha function used by Peng-Robinson and SRK.
auto operator()(const TType &T) const
TwuAlphaFunction(NumType Tci, const Eigen::Array3d &c)
const double R_CODATA2017
molar gas constant from CODATA 2017: https://doi.org/10.1103/RevModPhys.84.1527
auto make_generalizedcubic(const nlohmann::json &spec)
A JSON-based factory function for the generalized cubic + alpha.
auto canonical_PR(TCType Tc_K, PCType pc_Pa, AcentricType acentric, const std::optional< Eigen::ArrayXXd > &kmat=std::nullopt, const std::optional< double > R_JmolK=std::nullopt)
auto make_canonicalPR(const nlohmann::json &spec)
A JSON-based factory function for the canonical SRK model.
decltype(RKPRCismondi2005({})) RKPRCismondi2005_t
std::variant< BasicAlphaFunction< double >, TwuAlphaFunction< double >, MathiasCopemanAlphaFunction< double > > AlphaFunctionOptions
auto make_canonicalSRK(const nlohmann::json &spec)
A JSON-based factory function for the canonical SRK model.
auto pow(const double &x, const double &e)
auto build_alpha_functions(const TC &Tc_K, const nlohmann::json &jalphas)
auto canonical_SRK(TCType Tc_K, PCType pc_Pa, AcentricType acentric, const std::optional< Eigen::ArrayXXd > &kmat=std::nullopt, const std::optional< double > R_JmolK=std::nullopt)