From cfa3b3a3b7ed670e4ce7421f7aeb18185f2202c3 Mon Sep 17 00:00:00 2001 From: Ian Bell <ian.bell@nist.gov> Date: Tue, 24 Jan 2023 20:25:29 -0500 Subject: [PATCH] Add L-J EOS from Johnson And some tests --- include/teqp/json_builder.hpp | 3 + include/teqp/models/fwd.hpp | 4 +- include/teqp/models/mie/lennardjones.hpp | 147 +++++++++++++++++++++++ src/tests/catch_test_LJmodels.cxx | 61 ++++++++++ 4 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/tests/catch_test_LJmodels.cxx diff --git a/include/teqp/json_builder.hpp b/include/teqp/json_builder.hpp index 4e4487f..68b2f11 100644 --- a/include/teqp/json_builder.hpp +++ b/include/teqp/json_builder.hpp @@ -104,6 +104,9 @@ namespace teqp { else if (kind == "LJ126_KolafaNezbeda1994"){ return LJ126KolafaNezbeda1994(); } + else if (kind == "LJ126_Johnson1993"){ + return LJ126Johnson1993(); + } else if (kind == "2CLJF-Dipole"){ return twocenterljf::build_two_center_model_dipole(spec.at("author"), spec.at("L^*"), spec.at("(mu^*)^2")); } diff --git a/include/teqp/models/fwd.hpp b/include/teqp/models/fwd.hpp index 4937934..a0e4297 100644 --- a/include/teqp/models/fwd.hpp +++ b/include/teqp/models/fwd.hpp @@ -41,6 +41,7 @@ namespace teqp { using vdWEOS_t = vdWEOS<double>; using twocenterLJF_t = decltype(twocenterljf::build_two_center_model_dipole(std::string{}, double{}, double{})); using LJ126KolafaNezbeda1994_t = LJ126KolafaNezbeda1994; + using LJ126Johnson1993_t = LJ126Johnson1993; using idealgas_t = IdealHelmholtz; @@ -58,6 +59,7 @@ namespace teqp { SW_EspindolaHeredia2009_t, EXP6_Kataoka1992_t, twocenterLJF_t, - LJ126KolafaNezbeda1994_t + LJ126KolafaNezbeda1994_t, + LJ126Johnson1993_t >; } diff --git a/include/teqp/models/mie/lennardjones.hpp b/include/teqp/models/mie/lennardjones.hpp index 53d6434..7df7055 100644 --- a/include/teqp/models/mie/lennardjones.hpp +++ b/include/teqp/models/mie/lennardjones.hpp @@ -1,6 +1,7 @@ #pragma once #include "teqp/models/multifluid.hpp" +#include "teqp/types.hpp" namespace teqp { @@ -203,4 +204,150 @@ namespace teqp { } }; + /** + J. KARL JOHNSON, JOHN A. ZOLLWEG and KEITH E. GUBBINS + The Lennard-Jones equation of state revisited + MOLECULAR PHYSICS,1993, VOL. 78, No. 3, 591-618 + doi: 10.1080/00268979300100411 + */ + class LJ126Johnson1993 { + + private: + template<typename T> + auto POW2(const T& x) const -> T{ + return x*x; + } + + template<typename T> + auto POW3(const T& x) const -> T{ + return POW2(x)*x; + } + + template<typename T> + auto POW4(const T& x) const -> T{ + return POW2(x)*POW2(x); + } + + const double gamma = 3.0; + const std::vector<double> x { + 0.0, // placeholder for i=0 term since C++ uses 0-based indexing + 0.8623085097507421, + 2.976218765822098, + -8.402230115796038, + 0.1054136629203555, + -0.8564583828174598, + 1.582759470107601, + 0.7639421948305453, + 1.753173414312048, + 2.798291772190376e03, + -4.8394220260857657e-2, + 0.9963265197721935, + -3.698000291272493e01, + 2.084012299434647e01, + 8.305402124717285e01, + -9.574799715203068e02, + -1.477746229234994e02, + + 6.398607852471505e01, + 1.603993673294834e01, + 6.805916615864377e01, + -2.791293578795945e03, + -6.245128304568454, + -8.116836104958410e03, + 1.488735559561229e01, + -1.059346754655084e04, + -1.131607632802822e02, + -8.867771540418822e03, + -3.986982844450543e01, + -4.689270299917261e03, + 2.593535277438717e02, + -2.694523589434903e03, + -7.218487631550215e02, + 1.721802063863269e02 + }; + + // Table 5 + template<typename TTYPE> + auto get_ai(const int i, const TTYPE& Tstar) const -> TTYPE{ + switch(i){ + case 1: + return x[1]*Tstar + x[2]*sqrt(Tstar) + x[3] + x[4]/Tstar + x[5]/POW2(Tstar); + case 2: + return x[6]*Tstar + x[7] + x[8]/Tstar + x[9]/POW2(Tstar); + case 3: + return x[10]*Tstar + x[11] + x[12]/Tstar; + case 4: + return x[13]; + case 5: + return x[14]/Tstar + x[15]/POW2(Tstar); + case 6: + return x[16]/Tstar; + case 7: + return x[17]/Tstar + x[18]/POW2(Tstar); + case 8: + return x[19]/POW2(Tstar); + default: + throw teqp::InvalidArgument("i is not possible in get_ai"); + } + } + + // Table 6 + template<typename TTYPE> + auto get_bi(const int i, const TTYPE& Tstar) const -> TTYPE{ + switch(i){ + case 1: + return x[20]/POW2(Tstar) + x[21]/POW3(Tstar); + case 2: + return x[22]/POW2(Tstar) + x[23]/POW4(Tstar); + case 3: + return x[24]/POW2(Tstar) + x[25]/POW3(Tstar); + case 4: + return x[26]/POW2(Tstar) + x[27]/POW4(Tstar); + case 5: + return x[28]/POW2(Tstar) + x[29]/POW3(Tstar); + case 6: + return x[30]/POW2(Tstar) + x[31]/POW3(Tstar) + x[32]/POW4(Tstar); + default: + throw teqp::InvalidArgument("i is not possible in get_bi"); + } + } + + // Table 7 + template<typename FTYPE, typename RHOTYPE> + auto get_Gi(const int i, const FTYPE& F, const RHOTYPE& rhostar) const -> RHOTYPE{ + if (i == 1){ + return forceeval((1.0-F)/(2.0*gamma)); + } + else{ + // Recursive definition of the other terms; + return forceeval(-(F*powi(rhostar, 2*(i-1)) - 2.0*(i-1)*get_Gi(i-1, F, rhostar))/(2.0*gamma)); + } + } + + template<typename TTYPE, typename RHOTYPE> + auto get_alphar(const TTYPE& Tstar, const RHOTYPE& rhostar) const{ + std::common_type_t<TTYPE, RHOTYPE> summer = 0.0; + auto F = exp(-gamma*POW2(rhostar)); + for (int i = 1; i <= 8; ++i){ + summer += get_ai(i, Tstar)*powi(rhostar, i)/static_cast<double>(i); + } + for (int i = 1; i <= 6; ++i){ + summer += get_bi(i, Tstar)*get_Gi(i, F, rhostar); + } + return forceeval(summer); + } + + public: + // We are in "simulation units", so R is 1.0, and T and rho that + // go into alphar are actually T^* and rho^* + template<typename MoleFracType> + double R(const MoleFracType &) const { return 1.0; } + + template<typename TTYPE, typename RHOTYPE, typename MoleFracType> + auto alphar(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& /*molefrac*/) const { + return forceeval(get_alphar(Tstar, rhostar)/Tstar); + } + + }; + }; diff --git a/src/tests/catch_test_LJmodels.cxx b/src/tests/catch_test_LJmodels.cxx new file mode 100644 index 0000000..30f4caa --- /dev/null +++ b/src/tests/catch_test_LJmodels.cxx @@ -0,0 +1,61 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/catch_approx.hpp> +using Catch::Approx; + +#include "teqp/models/mie/lennardjones.hpp" +#include "teqp/algorithms/critical_pure.hpp" +#include "teqp/derivs.hpp" + +using namespace teqp; + +TEST_CASE("Test for critical point of Kolafa-Nezbeda", "[LJ126]") +{ + auto m = LJ126KolafaNezbeda1994(); + auto soln = solve_pure_critical(m, 1.3, 0.3); + auto Tc = 1.3396, rhoc = 0.3108; + CHECK(std::get<0>(soln) == Approx(Tc).margin(0.001)); + CHECK(std::get<1>(soln) == Approx(rhoc).margin(0.001)); +} +TEST_CASE("Test for critical point of Thol", "[LJ126-Thol]") +{ + auto m = build_LJ126_TholJPCRD2016(); + auto soln = solve_pure_critical(m, 1.3, 0.3); + auto Tc = 1.32, rhoc = 0.31; + CHECK(std::get<0>(soln) == Approx(Tc).margin(0.01)); + CHECK(std::get<1>(soln) == Approx(rhoc).margin(0.01)); +} +TEST_CASE("Test for critical point of Johnson", "[LJ126]") +{ + auto m = LJ126Johnson1993(); + auto soln = solve_pure_critical(m, 1.3, 0.3); + auto Tc = 1.313, rhoc = 0.310; + CHECK(std::get<0>(soln) == Approx(Tc).margin(0.001)); + CHECK(std::get<1>(soln) == Approx(rhoc).margin(0.001)); +} +TEST_CASE("Test single point values for Johnson against calculated values from S. Stephan", "[LJ126]") +{ + auto m = LJ126Johnson1993(); + auto z = (Eigen::ArrayXd(1) << 1.0).finished(); + + auto Bn = VirialDerivatives<decltype(m)>::get_B2vir(m, 0.8, z); + CHECK(Bn == Approx(-7.821026827)); + auto Bnmcx = VirialDerivatives<decltype(m)>::get_Bnvir<2,ADBackends::multicomplex>(m, 0.8, z)[2]; + CHECK(Bnmcx == Approx(-7.821026827)); + auto Bnad = VirialDerivatives<decltype(m)>::get_Bnvir<2,ADBackends::autodiff>(m, 0.8, z)[2]; + CHECK(Bnad == Approx(-7.821026827)); + + auto ar = m.alphar(1.350, 0.600, z); + CHECK(ar == Approx(-1.237403479)); +} +TEST_CASE("Test single point values for K-N against calculated values from S. Stephan", "[LJ126]") +{ + auto m = LJ126KolafaNezbeda1994(); + auto z = (Eigen::ArrayXd(1) << 1.0).finished(); + + auto Bn = VirialDerivatives<decltype(m)>::get_B2vir(m, 0.8, z); + CHECK(Bn == Approx(-7.821026827).margin(0.0005)); + auto Bnmcx = VirialDerivatives<decltype(m)>::get_Bnvir<2,ADBackends::multicomplex>(m, 0.8, z)[2]; + CHECK(Bnmcx == Approx(-7.821026827).margin(0.0005)); + auto Bnad = VirialDerivatives<decltype(m)>::get_Bnvir<2,ADBackends::autodiff>(m, 0.8, z)[2]; + CHECK(Bnad == Approx(-7.821026827).margin(0.0005)); +} -- GitLab