diff --git a/include/teqp/cpp/teqpcpp.hpp b/include/teqp/cpp/teqpcpp.hpp index 2478c5cc5ec64ab590a1bff229fda48c9384b095..d5f1bce70fa14e5fb99699ababf8918a37ac1f5a 100644 --- a/include/teqp/cpp/teqpcpp.hpp +++ b/include/teqp/cpp/teqpcpp.hpp @@ -176,8 +176,6 @@ namespace teqp { const std::string& departurepath = {} ); - std::unique_ptr<AbstractModel> emplace_model(AllowedModels&& model); - std::unique_ptr<AbstractModel> build_model_ptr(const nlohmann::json& json); } } diff --git a/include/teqp/json_builder.hpp b/include/teqp/json_builder.hpp index 23502cced6fc7dd635918389abf398b5bb5a977c..0b2d73f3cda6509d94a26f846d33719d738980fa 100644 --- a/include/teqp/json_builder.hpp +++ b/include/teqp/json_builder.hpp @@ -44,46 +44,7 @@ namespace teqp { return CPA::CPAfactory(spec); } else if (kind == "PCSAFT") { - using namespace PCSAFT; - std::optional<Eigen::ArrayXXd> kmat; - if (spec.contains("kmat") && spec.at("kmat").is_array() && spec.at("kmat").size() > 0){ - kmat = build_square_matrix(spec["kmat"]); - } - - if (spec.contains("names")){ - std::vector<std::string> names = spec["names"]; - if (kmat && kmat.value().rows() != names.size()){ - throw teqp::InvalidArgument("Provided length of names of " + std::to_string(names.size()) + " does not match the dimension of the kmat of " + std::to_string(kmat.value().rows())); - } - return PCSAFTMixture(names, kmat.value_or(Eigen::ArrayXXd{})); - } - else if (spec.contains("coeffs")){ - std::vector<SAFTCoeffs> coeffs; - for (auto j : spec["coeffs"]) { - SAFTCoeffs c; - c.name = j.at("name"); - c.m = j.at("m"); - c.sigma_Angstrom = j.at("sigma_Angstrom"); - c.epsilon_over_k = j.at("epsilon_over_k"); - c.BibTeXKey = j.at("BibTeXKey"); - if (j.contains("(mu^*)^2") && j.contains("nmu")){ - c.mustar2 = j.at("(mu^*)^2"); - c.nmu = j.at("nmu"); - } - if (j.contains("(Q^*)^2") && j.contains("nQ")){ - c.Qstar2 = j.at("(Q^*)^2"); - c.nQ = j.at("nQ"); - } - coeffs.push_back(c); - } - if (kmat && kmat.value().rows() != coeffs.size()){ - throw teqp::InvalidArgument("Provided length of coeffs of " + std::to_string(coeffs.size()) + " does not match the dimension of the kmat of " + std::to_string(kmat.value().rows())); - } - return PCSAFTMixture(coeffs, kmat.value_or(Eigen::ArrayXXd{})); - } - else{ - throw std::invalid_argument("you must provide names or coeffs, but not both"); - } + return PCSAFT::PCSAFTfactory(spec); } else if (kind == "SAFT-VR-Mie") { return SAFTVRMie::SAFTVRMiefactory(spec); diff --git a/include/teqp/models/cubics.hpp b/include/teqp/models/cubics.hpp index 6d8dd256b940fb823097e4c681e4fc2b1724f56a..cd1e16d398af4ed5c3b3214e5cc8c743aac7c91c 100644 --- a/include/teqp/models/cubics.hpp +++ b/include/teqp/models/cubics.hpp @@ -12,6 +12,7 @@ Implementations of the canonical cubic equations of state #include "teqp/constants.hpp" #include "teqp/exceptions.hpp" #include "cubicsuperancillary.hpp" +#include "teqp/json_tools.hpp" #include "nlohmann/json.hpp" @@ -20,16 +21,16 @@ Implementations of the canonical cubic equations of state namespace teqp { /** -* \brief The standard alpha function used by Peng-Robinson and SRK -*/ + * \brief The standard alpha function used by Peng-Robinson and SRK + */ template<typename NumType> class BasicAlphaFunction { private: NumType Tci, ///< The critical temperature - mi; ///< The "m" parameter + mi; ///< The "m" parameter public: BasicAlphaFunction(NumType Tci, NumType mi) : Tci(Tci), mi(mi) {}; - + template<typename TType> auto operator () (const TType& T) const { return forceeval(pow2(forceeval(1.0 + mi * (1.0 - sqrt(T / Tci))))); @@ -44,18 +45,18 @@ class GenericCubic { protected: std::valarray<NumType> ai, bi; const NumType Delta1, Delta2, OmegaA, OmegaB; - int superanc_index; + int superanc_index; const AlphaFunctions alphas; Eigen::ArrayXXd kmat; - + nlohmann::json meta; - + template<typename TType, typename IndexType> auto get_ai(TType T, IndexType i) const { return ai[i]; } - + template<typename TType, typename IndexType> auto get_bi(TType T, IndexType i) const { return bi[i]; } - + template<typename IndexType> void check_kmat(IndexType N) { if (kmat.cols() != kmat.rows()) { @@ -68,10 +69,10 @@ protected: throw teqp::InvalidArgument("kmat needs to be a square matrix the same size as the number of components [" + std::to_string(N) + "]"); } }; - + public: 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) - : Delta1(Delta1), Delta2(Delta2), OmegaA(OmegaA), OmegaB(OmegaB), superanc_index(superanc_index), alphas(alphas), kmat(kmat) + : Delta1(Delta1), Delta2(Delta2), OmegaA(OmegaA), OmegaB(OmegaB), superanc_index(superanc_index), alphas(alphas), kmat(kmat) { ai.resize(Tc_K.size()); bi.resize(Tc_K.size()); @@ -81,13 +82,13 @@ public: } check_kmat(ai.size()); }; - + void set_meta(const nlohmann::json& j) { meta = j; } auto get_meta() const { return meta; } auto get_kmat() const { return kmat; } - + /// Return a tuple of saturated liquid and vapor densities for the EOS given the temperature - /// Uses the superancillary equations from Bell and Deiters: + /// Uses the superancillary equations from Bell and Deiters: auto superanc_rhoLV(double T) const { if (ai.size() != 1) { throw std::invalid_argument("function only available for pure species"); @@ -96,18 +97,18 @@ public: auto b = get_b(T, z); auto Ttilde = R(z)*T*b/get_a(T,z); return std::make_tuple( - CubicSuperAncillary::supercubic(superanc_index, CubicSuperAncillary::RHOL_CODE, Ttilde)/b, - CubicSuperAncillary::supercubic(superanc_index, CubicSuperAncillary::RHOV_CODE, Ttilde)/b - ); + CubicSuperAncillary::supercubic(superanc_index, CubicSuperAncillary::RHOL_CODE, Ttilde)/b, + CubicSuperAncillary::supercubic(superanc_index, CubicSuperAncillary::RHOV_CODE, Ttilde)/b + ); } - + const NumType Ru = get_R_gas<double>(); /// Universal gas constant, exact number - + template<class VecType> auto R(const VecType& molefrac) const { return Ru; } - + template<typename TType, typename CompType> auto get_a(TType T, const CompType& molefracs) const { std::common_type_t<TType, decltype(molefracs[0])> a_ = 0.0; @@ -124,7 +125,7 @@ public: } return forceeval(a_); } - + template<typename TType, typename CompType> auto get_b(TType /*T*/, const CompType& molefracs) const { std::common_type_t<TType, decltype(molefracs[0])> b_ = 0.0; @@ -133,11 +134,11 @@ public: } return forceeval(b_); } - + template<typename TType, typename RhoType, typename MoleFracType> auto alphar(const TType& T, - const RhoType& rho, - const MoleFracType& molefrac) const + const RhoType& rho, + const MoleFracType& molefrac) const { if (molefrac.size() != alphas.size()) { throw std::invalid_argument("Sizes do not match"); @@ -155,16 +156,16 @@ auto canonical_SRK(TCType Tc_K, PCType pc_K, AcentricType acentric, const Eigen: double Delta1 = 1; double Delta2 = 0; AcentricType m = 0.48 + 1.574 * acentric - 0.176 * acentric * acentric; - + std::vector<AlphaFunctionOptions> alphas; for (auto i = 0; i < Tc_K.size(); ++i) { alphas.emplace_back(BasicAlphaFunction(Tc_K[i], m[i])); } - + // See https://doi.org/10.1021/acs.iecr.1c00847 double OmegaA = 1.0 / (9.0 * (cbrt(2) - 1)); double OmegaB = (cbrt(2) - 1) / 3; - + nlohmann::json meta = { {"Delta1", Delta1}, {"Delta2", Delta2}, @@ -172,12 +173,22 @@ auto canonical_SRK(TCType Tc_K, PCType pc_K, AcentricType acentric, const Eigen: {"OmegaB", OmegaB}, {"kind", "Soave-Redlich-Kwong"} }; - + auto cub = GenericCubic(Delta1, Delta2, OmegaA, OmegaB, CubicSuperAncillary::SRK_CODE, Tc_K, pc_K, std::move(alphas), kmat); cub.set_meta(meta); return cub; } +/// A JSON-based factory function for the canonical SRK model +inline auto make_canonicalSRK(const nlohmann::json& spec){ + std::valarray<double> Tc_K = spec.at("Tcrit / K"), pc_Pa = spec.at("pcrit / Pa"), acentric = spec.at("acentric"); + Eigen::ArrayXXd kmat(0, 0); + if (spec.contains("kmat")){ + kmat = build_square_matrix(spec.at("kmat")); + } + return canonical_SRK(Tc_K, pc_Pa, acentric, kmat); +} + template <typename TCType, typename PCType, typename AcentricType> auto canonical_PR(TCType Tc_K, PCType pc_K, AcentricType acentric, const Eigen::ArrayXXd& kmat = {}) { double Delta1 = 1+sqrt(2.0); @@ -211,4 +222,14 @@ auto canonical_PR(TCType Tc_K, PCType pc_K, AcentricType acentric, const Eigen:: return cub; } +/// A JSON-based factory function for the canonical SRK model +inline auto make_canonicalPR(const nlohmann::json& spec){ + std::valarray<double> Tc_K = spec.at("Tcrit / K"), pc_Pa = spec.at("pcrit / Pa"), acentric = spec.at("acentric"); + Eigen::ArrayXXd kmat(0, 0); + if (spec.contains("kmat")){ + kmat = build_square_matrix(spec.at("kmat")); + } + return canonical_PR(Tc_K, pc_Pa, acentric, kmat); +} + }; // namespace teqp diff --git a/include/teqp/models/pcsaft.hpp b/include/teqp/models/pcsaft.hpp index 324c134cea6ef2ba8e7a866bcb7f52674f50a789..04b534b0476045b4273655086b2e64f28b4cc49a 100644 --- a/include/teqp/models/pcsaft.hpp +++ b/include/teqp/models/pcsaft.hpp @@ -410,19 +410,48 @@ public: } }; -inline auto PCSAFTfactory(const nlohmann::json& json) { - std::vector<SAFTCoeffs> coeffs; - for (auto j : json) { - SAFTCoeffs c; - c.name = j.at("name"); - c.m = j.at("m"); - c.sigma_Angstrom = j.at("sigma_Angstrom"); - c.epsilon_over_k = j.at("epsilon_over_k"); - c.BibTeXKey = j.at("BibTeXKey"); - coeffs.push_back(c); +/// A JSON-based factory function for the PC-SAFT model +inline auto PCSAFTfactory(const nlohmann::json& spec) { + std::optional<Eigen::ArrayXXd> kmat; + if (spec.contains("kmat") && spec.at("kmat").is_array() && spec.at("kmat").size() > 0){ + kmat = build_square_matrix(spec["kmat"]); } - return PCSAFTMixture(coeffs); -}; + + if (spec.contains("names")){ + std::vector<std::string> names = spec["names"]; + if (kmat && kmat.value().rows() != names.size()){ + throw teqp::InvalidArgument("Provided length of names of " + std::to_string(names.size()) + " does not match the dimension of the kmat of " + std::to_string(kmat.value().rows())); + } + return PCSAFTMixture(names, kmat.value_or(Eigen::ArrayXXd{})); + } + else if (spec.contains("coeffs")){ + std::vector<SAFTCoeffs> coeffs; + for (auto j : spec["coeffs"]) { + SAFTCoeffs c; + c.name = j.at("name"); + c.m = j.at("m"); + c.sigma_Angstrom = j.at("sigma_Angstrom"); + c.epsilon_over_k = j.at("epsilon_over_k"); + c.BibTeXKey = j.at("BibTeXKey"); + if (j.contains("(mu^*)^2") && j.contains("nmu")){ + c.mustar2 = j.at("(mu^*)^2"); + c.nmu = j.at("nmu"); + } + if (j.contains("(Q^*)^2") && j.contains("nQ")){ + c.Qstar2 = j.at("(Q^*)^2"); + c.nQ = j.at("nQ"); + } + coeffs.push_back(c); + } + if (kmat && kmat.value().rows() != coeffs.size()){ + throw teqp::InvalidArgument("Provided length of coeffs of " + std::to_string(coeffs.size()) + " does not match the dimension of the kmat of " + std::to_string(kmat.value().rows())); + } + return PCSAFTMixture(coeffs, kmat.value_or(Eigen::ArrayXXd{})); + } + else{ + throw std::invalid_argument("you must provide names or coeffs, but not both"); + } +} } /* namespace PCSAFT */ }; // namespace teqp diff --git a/interface/CPP/teqp_impl_factory.cpp b/interface/CPP/teqp_impl_factory.cpp index 1f46fb0123123f2d1bd4552605379c3e99f65b5b..bdecf6f3d38958af8c2786b5f16225af7b4a1c7d 100644 --- a/interface/CPP/teqp_impl_factory.cpp +++ b/interface/CPP/teqp_impl_factory.cpp @@ -4,15 +4,6 @@ namespace teqp { namespace cppinterface { - - // std::unique_ptr<AbstractModel> emplace_model(AllowedModels&& model){ - // return make(model); - // } - -#warning PR is not yet implemented -#warning SRK is not yet implemented -#warning CPA is not yet implemented -#warning PCSAFT is not yet implemented std::unique_ptr<teqp::cppinterface::AbstractModel> make_SAFTVRMie(const nlohmann::json &j); @@ -21,7 +12,12 @@ namespace teqp { static std::unordered_map<std::string, makefunc> pointer_factory = { {"vdW1", [](const nlohmann::json& spec){ return make_owned(vdWEOS1(spec.at("a"), spec.at("b"))); }}, - {"SAFT-VR-Mie", [](const nlohmann::json& spec){ return make_SAFTVRMie(spec); }}, + + {"PR", [](const nlohmann::json& spec){ return make_owned(make_canonicalPR(spec));}}, + {"SRK", [](const nlohmann::json& spec){ return make_owned(make_canonicalSRK(spec));}}, + {"CPA", [](const nlohmann::json& spec){ return make_owned(CPA::CPAfactory(spec));}}, + {"PCSAFT", [](const nlohmann::json& spec){ return make_owned(PCSAFT::PCSAFTfactory(spec));}}, + {"multifluid", [](const nlohmann::json& spec){ return make_owned(multifluidfactory(spec));}}, {"SW_EspindolaHeredia2009", [](const nlohmann::json& spec){ return make_owned(squarewell::EspindolaHeredia2009(spec.at("lambda")));}}, {"EXP6_Kataoka1992", [](const nlohmann::json& spec){ return make_owned(exp6::Kataoka1992(spec.at("alpha")));}}, @@ -32,7 +28,10 @@ namespace teqp { {"Mie_Pohl2023", [](const nlohmann::json& spec){ return make_owned(Mie::Mie6Pohl2023(spec.at("lambda_a")));}}, {"2CLJF-Dipole", [](const nlohmann::json& spec){ return make_owned(twocenterljf::build_two_center_model_dipole(spec.at("author"), spec.at("L^*"), spec.at("(mu^*)^2")));}}, {"2CLJF-Quadrupole", [](const nlohmann::json& spec){ return make_owned(twocenterljf::build_two_center_model_quadrupole(spec.at("author"), spec.at("L^*"), spec.at("(mu^*)^2")));}}, - {"IdealHelmholtz", [](const nlohmann::json& spec){ return make_owned(IdealHelmholtz(spec));}} + {"IdealHelmholtz", [](const nlohmann::json& spec){ return make_owned(IdealHelmholtz(spec));}}, + + // Implemented in its own compilation unit to help with compilation time + {"SAFT-VR-Mie", [](const nlohmann::json& spec){ return make_SAFTVRMie(spec); }} }; std::unique_ptr<teqp::cppinterface::AbstractModel> build_model_ptr(const nlohmann::json& json) {