From 3779b027c6f2aac6e94ebd19ddc48f4fb9a6364e Mon Sep 17 00:00:00 2001 From: Ian Bell <ian.bell@nist.gov> Date: Wed, 31 Mar 2021 19:55:46 -0400 Subject: [PATCH] Everything compiles again minus most of the valarray Isochoric derivatives moved into their own namespace --- externals/mcx | 2 +- include/teqp/algorithms/critical_tracing.hpp | 71 +++++----- include/teqp/derivs.hpp | 138 ++++++++++++------- src/multifluid.cpp | 61 +++++--- src/tests/catch_tests.cxx | 33 ++++- 5 files changed, 200 insertions(+), 105 deletions(-) diff --git a/externals/mcx b/externals/mcx index ffc9c34..1f97ce8 160000 --- a/externals/mcx +++ b/externals/mcx @@ -1 +1 @@ -Subproject commit ffc9c34e8ddfcebec9d417096735b4516e673c98 +Subproject commit 1f97ce856ea22bf90c483253fcbc8da2ebc2b9db diff --git a/include/teqp/algorithms/critical_tracing.hpp b/include/teqp/algorithms/critical_tracing.hpp index 44baf51..fca4493 100644 --- a/include/teqp/algorithms/critical_tracing.hpp +++ b/include/teqp/algorithms/critical_tracing.hpp @@ -32,13 +32,15 @@ auto eigen_problem(const Model& model, const TType T, const RhoType& rhovec) { EigenData ed; auto N = rhovec.size(); - std::valarray<bool> mask = (rhovec != 0); + Eigen::ArrayX<bool> mask = (rhovec != 0).eval(); + + using id = IsochoricDerivatives<decltype(model)>; // Build the Hessian for the residual part; #if defined(USE_AUTODIFF) - auto H = build_Psir_Hessian_autodiff(model, T, rhovec); + auto H = id::build_Psir_Hessian_autodiff(model, T, rhovec); #else - auto H = build_Psir_Hessian_mcx(model, T, rhovec); + auto H = id::build_Psir_Hessian_mcx(model, T, rhovec); #endif // ... and add ideal-gas terms to H for (auto i = 0; i < N; ++i) { @@ -100,7 +102,7 @@ auto eigen_problem(const Model& model, const TType T, const RhoType& rhovec) { } struct psi1derivs { - std::valarray<double> psir, psi0, tot; + Eigen::ArrayXd psir, psi0, tot; EigenData ei; }; @@ -112,7 +114,7 @@ auto get_derivs(const Model& model, const TType T, const RhoType& rhovec) { auto ei = eigen_problem(model, T, rhovec); // Ideal-gas contributions of psi0 w.r.t. sigma_1, in the same form as the residual part - std::valarray<double> psi0_derivs(0.0, 5); + Eigen::ArrayXd psi0_derivs(5); psi0_derivs.setZero(); psi0_derivs[0] = -1; // Placeholder, not needed psi0_derivs[1] = -1; // Placeholder, not needed for (auto i = 0; i < rhovec.size(); ++i) { @@ -129,23 +131,28 @@ auto get_derivs(const Model& model, const TType T, const RhoType& rhovec) { VectorXdual4th rhovecad(rhovec.size()); for (auto i = 0; i < rhovec.size(); ++i) { rhovecad[i] = rhovec[i]; } dual4th varsigma{0.0}; auto wrapper = [&rhovecad, &v0, &T, &model](const auto &sigma_1) { - auto rhovecused = rhovecad + sigma_1*v0; - return get_Psir(model, T, rhovecused); + auto rhovecused = (rhovecad + sigma_1*v0).eval(); + auto rhotot = rhovecused.sum(); + auto molefrac = (rhovecused / rhotot).eval(); + return eval(model.alphar(T, rhotot, molefrac) * model.R * T * rhotot); }; - auto derivs = derivatives(wrapper, wrt(varsigma, varsigma, varsigma, varsigma), at(varsigma)); - auto psir_derivs = std::valarray<double>(&derivs[0], derivs.size()); + auto psir_derivs_ = derivatives(wrapper, wrt(varsigma), at(varsigma)); + RhoType psir_derivs; psir_derivs.resize(5); + for (auto i = 0; i < 5; ++i){ psir_derivs[i] = psir_derivs_[i]; } + #else using namespace mcx; // Calculate the first through fourth derivative of Psi^r w.r.t. sigma_1 - std::valarray<MultiComplex<double>> v0(ei.v0.size()); for (auto i = 0; i < ei.v0.size(); ++i) { v0[i] = ei.v0[i]; } - std::valarray<MultiComplex<double>> rhovecmcx(rhovec.size()); for (auto i = 0; i < rhovec.size(); ++i) { rhovecmcx[i] = rhovec[i]; } + Eigen::Vector<MultiComplex<double>,Eigen::Dynamic> v0(ei.v0.size()); for (auto i = 0; i < ei.v0.size(); ++i) { v0[i] = ei.v0[i]; } + Eigen::Vector<MultiComplex<double>,Eigen::Dynamic> rhovecmcx(rhovec.size()); for (auto i = 0; i < rhovec.size(); ++i) { rhovecmcx[i] = rhovec[i]; } using fcn_t = std::function<MultiComplex<double>(const MultiComplex<double>&)>; fcn_t wrapper = [&rhovecmcx, &v0, &T, &model](const MultiComplex<double>& sigma_1) { - std::valarray<MultiComplex<double>> rhovecused = rhovecmcx + sigma_1 * v0; - return get_Psir(model, T, rhovecused); + Eigen::Vector<MultiComplex<double>, Eigen::Dynamic> rhovecused = rhovecmcx + sigma_1 * v0; + auto rhotot = rhovecused.sum(); + auto molefrac = rhovecused/rhotot; + return model.alphar(T, rhotot, molefrac)*model.R*T*rhotot; }; - auto psir_derivs_ = diff_mcx1(wrapper, 0.0, 4, true); - auto psir_derivs = std::valarray<double>(&psir_derivs_[0], psir_derivs_.size()); + RhoType psir_derivs = diff_mcx1(wrapper, 0.0, 4, true); #endif // As a sanity check, the minimum eigenvalue of the Hessian constructed based on the molar concentrations @@ -170,7 +177,7 @@ bool all(const Iterable& foo) { template <typename Iterable> bool any(const Iterable& foo) { return std::any_of(std::begin(foo), std::end(foo), [](const auto x) { return x; }); -} +} template<typename Model, typename TType, typename RhoType> auto get_drhovec_dT_crit(const Model& model, const TType T, const RhoType& rhovec) { @@ -191,12 +198,11 @@ auto get_drhovec_dT_crit(const Model& model, const TType T, const RhoType& rhove auto sigma2 = 2e-5 * rhovec.sum(); // This is the perturbation along the second eigenvector - auto v1 = std::valarray<double>(&ei.v1[0], ei.v1.size()); - decltype(v1) rhovec_plus = rhovec + v1 * sigma2; - decltype(v1) rhovec_minus = rhovec - v1 * sigma2; + auto rhovec_plus = (rhovec + ei.v1 * sigma2).eval(); + auto rhovec_minus = (rhovec - ei.v1 * sigma2).eval(); std::string stepping_desc = ""; auto deriv_sigma2 = all_derivs.tot; - auto eval = [](const auto &ex){ return std::valarray<bool>(ex); }; + auto eval = [](const auto &ex){ return ex.eval(); }; if (all(eval(rhovec_minus > 0)) && all(eval(rhovec_plus > 0))) { // Conventional centered derivative auto plus_sigma2 = get_derivs(model, T, rhovec_plus); @@ -207,7 +213,7 @@ auto get_drhovec_dT_crit(const Model& model, const TType T, const RhoType& rhove else if (any(eval(rhovec_minus < 0))) { // Forward derivative in the direction of v1 auto plus_sigma2 = get_derivs(model, T, rhovec_plus); - auto rhovec_2plus = rhovec + 2 * v1 * sigma2; + auto rhovec_2plus = (rhovec + 2 * ei.v1 * sigma2).eval(); auto plus2_sigma2 = get_derivs(model, T, rhovec_2plus); deriv_sigma2 = (-3 * derivs + 4 * plus_sigma2.tot - plus2_sigma2.tot) / (2.0 * sigma2); stepping_desc = "forward"; @@ -215,7 +221,7 @@ auto get_drhovec_dT_crit(const Model& model, const TType T, const RhoType& rhove else if (any(eval(rhovec_minus > 0))) { // Negative derivative in the direction of v1 auto minus_sigma2 = get_derivs(model, T, rhovec_minus); - auto rhovec_2minus = rhovec - 2 * v1 * sigma2; + auto rhovec_2minus = (rhovec - 2 * ei.v1 * sigma2).eval(); auto minus2_sigma2 = get_derivs(model, T, rhovec_2minus); deriv_sigma2 = (-3 * derivs + 4 * minus_sigma2.tot - minus2_sigma2.tot) / (-2.0 * sigma2); stepping_desc = "backwards"; @@ -245,14 +251,14 @@ auto get_drhovec_dT_crit(const Model& model, const TType T, const RhoType& rhove // print("RHS", RHS) // print("drhovec_dT", drhovec_dT) - return std::valarray<double>(&drhovec_dT(0), drhovec_dT.size()); + return drhovec_dT; } template<typename ModelType, typename VecType> auto critical_polish_molefrac(const ModelType &model, const double T, const VecType &rhovec, const double z0) { auto polish_x_resid = [&model, &z0](const auto& x) { auto T = x[0]; - std::valarray<double> rhovec = { x[1], x[2] }; + Eigen::ArrayXd rhovec(2); rhovec << x[1], x[2] ; auto z0new = rhovec[0] / rhovec.sum(); auto derivs = get_derivs(model, T, rhovec); // First two are residuals on critical point, third is residual on composition @@ -266,14 +272,15 @@ auto critical_polish_molefrac(const ModelType &model, const double T, const VecT if (!std::isfinite(T) || !std::isfinite(x[1]) || !std::isfinite(x[2])) { throw std::invalid_argument("Something not finite; aborting polishing"); } - return std::make_tuple(x[0], x.tail(x.size()-1).eval()); + Eigen::ArrayXd rho = x.tail(x.size() - 1); + return std::make_tuple(x[0], rho); } template<typename ModelType, typename VecType> void trace_critical_arclength_binary(const ModelType& model, double T0, const VecType &rhovec0, const std::string &filename) { double t = 0.0, dt = 100; - std::valarray<double> last_drhodt; + VecType last_drhodt; VecType rhovec = rhovec0; double T = T0; @@ -299,17 +306,17 @@ void trace_critical_arclength_binary(const ModelType& model, double T0, const Ve } } - auto drhodT = get_drhovec_dT_crit(model, T, rhovec); + auto drhodT = get_drhovec_dT_crit(model, T, rhovec).array().eval(); auto dTdt = 1.0 / norm(drhodT); auto drhodt = drhodT * dTdt; - auto eval = [](const auto& ex) { return std::valarray<bool>(ex); }; - // Flip the sign if the tracing wants to go backwards, or if the first step would take you to negative concentrations - if (iter == 0 && any(eval((rhovec + c * drhodt * dt) < 0))) { + Eigen::ArrayXd step = rhovec + c*drhodt*dt; + auto negativestepvals = (step < 0).eval(); + if (iter == 0 && negativestepvals.all()) { c *= -1; } - else if (iter > 0 && dot(std::valarray<double>(c * drhodt), last_drhodt) < 0) { + else if (iter > 0 && dot((c * drhodt).eval(), last_drhodt) < 0) { c *= -1; } @@ -323,7 +330,7 @@ void trace_critical_arclength_binary(const ModelType& model, double T0, const Ve try { auto [Tnew, rhovecnew] = critical_polish_molefrac(model, T, rhovec, z0); - T = Tnew; rhovec = VecType(&(rhovecnew[0]), rhovecnew.size()); + T = Tnew; rhovec = rhovecnew; } catch (std::exception& e) { std::cout << e.what() << std::endl; diff --git a/include/teqp/derivs.hpp b/include/teqp/derivs.hpp index ef9ed41..aad41a8 100644 --- a/include/teqp/derivs.hpp +++ b/include/teqp/derivs.hpp @@ -179,7 +179,7 @@ auto get_Bnvir(const Model& model, const TType &T, const ContainerType& molefrac } template <typename Model, typename TType, typename ContainerType> -typename ContainerType::value_type get_B12vir(const Model& model, const TType T, const ContainerType& molefrac) { +auto get_B12vir(const Model& model, const TType T, const ContainerType& molefrac) { auto B2 = get_B2vir(model, T, molefrac); // Overall B2 for mixture auto B20 = get_B2vir(model, T, std::valarray<double>({ 1,0 })); // Pure first component with index 0 @@ -199,15 +199,6 @@ typename ContainerType::value_type get_splus(const Model& model, const TType T, return model.alphar(T, rhotot, molefrac) - get_Ar10(model, T, rhovec); } -/*** -* \brief Calculate Psir=ar*rho -*/ -template <typename TType, typename ContainerType, typename Model> -typename ContainerType::value_type get_Psir(const Model& model, const TType T, const ContainerType& rhovec) { - auto rhotot_ = std::accumulate(std::begin(rhovec), std::end(rhovec), (decltype(rhovec[0]))0.0); - return model.alphar(T, rhotot_, rhovec / rhotot_) * model.R * T * rhotot_; -} - /*** * \brief Calculate the residual pressure from derivatives of alphar */ @@ -218,46 +209,97 @@ typename ContainerType::value_type get_pr(const Model& model, const TType T, con return get_Ar01(model, T, rhotot_, rhovec / rhotot_) * rhotot_ * model.R * T; } +template<typename Model, typename Scalar=double, typename VectorType = Eigen::ArrayXd> +struct IsochoricDerivatives{ + /*** + * \brief Calculate Psir=ar*rho + */ + static auto get_Psir(const Model& model, const Scalar &T, const VectorType& rhovec) { + auto rhotot_ = std::accumulate(std::begin(rhovec), std::end(rhovec), (decltype(rhovec[0]))0.0); + return model.alphar(T, rhotot_, rhovec / rhotot_) * model.R * T * rhotot_; + } -/*** -* \brief Calculate the Hessian of Psir = ar*rho w.r.t. the molar concentrations -* -* Requires the use of autodiff derivatives to calculate second partial derivatives -*/ -template<typename Model, typename TType, typename RhoType> -auto build_Psir_Hessian_autodiff(const Model& model, const TType &T, const RhoType& rho) { - // Double derivatives in each component's concentration - // N^N matrix (symmetric) + /*** + * \brief Calculate the Hessian of Psir = ar*rho w.r.t. the molar concentrations + * + * Requires the use of autodiff derivatives to calculate second partial derivatives + */ + static auto build_Psir_Hessian_autodiff(const Model& model, const Scalar& T, const VectorType& rho) { + // Double derivatives in each component's concentration + // N^N matrix (symmetric) - dual2nd u; // the output scalar u = f(x), evaluated together with Hessian below - VectorXdual2nd g; - VectorXdual2nd rhovecc(rho.size()); for (auto i = 0; i < rho.size(); ++i) { rhovecc[i] = rho[i]; } - auto hfunc = [&model, &T](const VectorXdual2nd& rho_) { - auto rhotot_ = rho_.sum(); - auto molefrac = (rho_ / rhotot_).eval(); - return eval(model.alphar(T, rhotot_, molefrac) * model.R * T * rhotot_); - }; - return autodiff::hessian(hfunc, wrt(rhovecc), at(rhovecc), u, g).eval(); // evaluate the function value u, its gradient, and its Hessian matrix H -} + dual2nd u; // the output scalar u = f(x), evaluated together with Hessian below + VectorXdual2nd g; + VectorXdual2nd rhovecc(rho.size()); for (auto i = 0; i < rho.size(); ++i) { rhovecc[i] = rho[i]; } + auto hfunc = [&model, &T](const VectorXdual2nd& rho_) { + auto rhotot_ = rho_.sum(); + auto molefrac = (rho_ / rhotot_).eval(); + return eval(model.alphar(T, rhotot_, molefrac) * model.R * T * rhotot_); + }; + return autodiff::hessian(hfunc, wrt(rhovecc), at(rhovecc), u, g).eval(); // evaluate the function value u, its gradient, and its Hessian matrix H + } -/*** -* \brief Calculate the Hessian of Psir = ar*rho w.r.t. the molar concentrations -* -* Requires the use of multicomplex derivatives to calculate second partial derivatives -*/ -template<typename Model, typename TType, typename RhoType> -auto build_Psir_Hessian_mcx(const Model& model, const TType &T, const RhoType& rho) { - // Double derivatives in each component's concentration - // N^N matrix (symmetric) - using namespace mcx; + /*** + * \brief Calculate the Hessian of Psi = a*rho w.r.t. the molar concentrations + * + * Uses autodiff derivatives to calculate second partial derivatives + */ + static auto build_Psi_Hessian_autodiff(const Model& model, const Scalar& T, const VectorType& rho) { + auto H = build_Psir_Hessian_autodiff(model, T, rho); + for (auto i = 0; i < 2; ++i) { + H(i, i) += model.R * T / rho[i]; + } + return H; + } + + /*** + * \brief Calculate the Hessian of Psir = ar*rho w.r.t. the molar concentrations (residual contribution only) + * + * Requires the use of multicomplex derivatives to calculate second partial derivatives + */ + static auto build_Psir_Hessian_mcx(const Model& model, const Scalar& T, const VectorType& rho) { + // Double derivatives in each component's concentration + // N^N matrix (symmetric) + using namespace mcx; - // Lambda function for getting Psir with multicomplex concentrations - using fcn_t = std::function< MultiComplex<double>(const std::valarray<MultiComplex<double>>&)>; - fcn_t func = [&model, &T](const auto& rhovec) { - return get_Psir(model, T, rhovec); - }; - using mattype = Eigen::ArrayXXd; - auto H = get_Hessian<mattype, fcn_t, std::valarray<double>, HessianMethods::Multiple>(func, rho); - return H; -} \ No newline at end of file + // Lambda function for getting Psir with multicomplex concentrations + using fcn_t = std::function< MultiComplex<double>(const Eigen::ArrayX<MultiComplex<double>>&)>; + fcn_t func = [&model, &T](const auto& rhovec) { + auto rhotot_ = rhovec.sum(); + auto molefrac = (rhovec / rhotot_).eval(); + return model.alphar(T, rhotot_, molefrac) * model.R * T * rhotot_; + }; + using mattype = Eigen::ArrayXXd; + auto H = get_Hessian<mattype, fcn_t, VectorType, HessianMethods::Multiple>(func, rho); + return H; + } + + /*** + * \brief Gradient of Psir = ar*rho w.r.t. the molar concentrations + * + * Uses autodiff to calculate second partial derivatives + */ + static auto build_Psir_gradient_autodiff(const Model& model, const Scalar& T, const VectorType& rho) { + VectorXdual2nd rhovecc(rho.size()); for (auto i = 0; i < rho.size(); ++i) { rhovecc[i] = rho[i]; } + auto psirfunc = [&model, &T](const VectorXdual2nd& rho_) { + auto rhotot_ = rho_.sum(); + auto molefrac = (rho_ / rhotot_).eval(); + return eval(model.alphar(T, rhotot_, molefrac) * model.R * T * rhotot_); + }; + auto val = autodiff::gradient(psirfunc, wrt(rhovecc), at(rhovecc)).eval(); // evaluate the gradient + return val; + } + + /*** + * \brief Calculate the chemical potential of each component + * + * Uses autodiff derivatives to calculate second partial derivatives + * See Eq. 9 of https://doi.org/10.1002/aic.16730 + * \note: Some contributions to the ideal gas part are missing (reference state and cp0), but are not relevant to phase equilibria + */ + static auto get_chempot_autodiff(const Model& model, const Scalar& T, const VectorType& rho) { + typename VectorType::value_type rhotot = rho.sum(); + return (build_Psir_gradient_autodiff(model, T, rho).array() + model.R*T*(1.0 + log(rho / rhotot))).eval(); + } +}; \ No newline at end of file diff --git a/src/multifluid.cpp b/src/multifluid.cpp index 6b90279..5554926 100644 --- a/src/multifluid.cpp +++ b/src/multifluid.cpp @@ -38,7 +38,7 @@ void trace_critical_loci(const std::string &coolprop_root, const nlohmann::json const auto &model = optmodel.value(); auto rhoc0 = 1.0 / model.redfunc.vc[i]; auto T0 = model.redfunc.Tc[i]; - std::valarray<double> rhovec(2); rhovec[i] = { rhoc0 }; rhovec[1L - i] = 0.0; + Eigen::ArrayXd rhovec(2); rhovec[i] = { rhoc0 }; rhovec[1L - i] = 0.0; // Non-analytic terms make it impossible to initialize AT the pure components if (pp[0] == "CarbonDioxide" || pp[1] == "CarbonDioxide") { if (i == 0) { @@ -70,18 +70,24 @@ int main(){ std::ifstream(coolprop_root + "/dev/mixtures/mixture_binary_pairs.json") ); - // Critical curves - { - Timer t(1); - trace_critical_loci(coolprop_root, BIPcollection); - } + //// Critical curves + //{ + // Timer t(1); + // trace_critical_loci(coolprop_root, BIPcollection); + //} { auto model = build_multifluid_model({ "methane", "ethane" }, coolprop_root, BIPcollection); - std::valarray<double> rhovec = { 1.0, 2.0 }; + Eigen::ArrayXd rhovec(2); rhovec << 1.0, 2.0 ; double T = 300; { - const std::valarray<double> molefrac = { rhovec[0]/rhovec.sum(), rhovec[1]/rhovec.sum() }; + const auto molefrac = (Eigen::ArrayXd(2) << rhovec[0]/rhovec.sum(), rhovec[1]/rhovec.sum()).finished(); + + auto B12 = get_B12vir(model, T, molefrac); + + using id = IsochoricDerivatives<decltype(model)>; + auto mu = id::get_chempot_autodiff(model, T, rhovec); + const double rho = rhovec.sum(); volatile double T = 300.0; constexpr int N = 10000; @@ -93,7 +99,7 @@ int main(){ for (auto i = 0; i < N; ++i){ alphar = model.alphar(T, rho, molefrac); } - std::cout << alphar << std::endl; + std::cout << alphar << " function call" << std::endl; } { Timer t(N); @@ -121,25 +127,40 @@ int main(){ for (auto i = 0; i < N; ++i) { alphar = get_Ar02(model, T, rho, molefrac); } - std::cout << alphar << std::endl; + std::cout << alphar << "; 2nd autodiff" << std::endl; + } + { + Timer t(N); + for (auto i = 0; i < N; ++i) { + auto o = get_Bnvir<3, ADBackends::autodiff>(model, T, molefrac)[3]; + } + std::cout << alphar << "; 3 derivs" << std::endl; + } + { + Timer t(N); + for (auto i = 0; i < N; ++i) { + auto o = get_Bnvir<4, ADBackends::autodiff>(model, T, molefrac)[4]; + } + std::cout << alphar << "; 4 derivs" << std::endl; + } + { + Timer t(N); + for (auto i = 0; i < N; ++i) { + auto o = get_Bnvir<5, ADBackends::autodiff>(model, T, molefrac)[5]; + } + std::cout << alphar << "; 5 derivs" << std::endl; } } + const auto molefrac = (Eigen::ArrayXd(2) << 1.0 / 3.0, 2.0 / 3.0 ).finished(); + auto alphar = model.alphar(T, rhovec); auto Ar01 = get_Ar01(model, T, rhovec); auto Ar10 = get_Ar10(model, T, rhovec); + auto Ar02 = get_Ar02(model, T, rhovec.sum(), molefrac); auto splus = get_splus(model, T, rhovec); - - std::valarray<double> molefrac = { 1.0/3.0, 2.0/3.0 }; - auto B2 = get_B2vir(model, T, molefrac); - - std::valarray<double> dilrho = 0.00000000001*molefrac; - auto B2other = get_Ar01(model, T, dilrho)/dilrho.sum(); - - std::valarray<double> zerorho = 0.0*rhovec; - auto Ar01dil = get_Ar01(model, T, zerorho); - int ttt =0 ; + int ttt = 0; } return EXIT_SUCCESS; } diff --git a/src/tests/catch_tests.cxx b/src/tests/catch_tests.cxx index 32377d5..0cdccf4 100644 --- a/src/tests/catch_tests.cxx +++ b/src/tests/catch_tests.cxx @@ -84,12 +84,14 @@ TEST_CASE("Check virial coefficients for vdW", "[virial]") TEST_CASE("Check Hessian of Psir", "[virial]") { + double T = 298.15; double rho = 3.0; - const std::valarray<double> rhovec = { rho / 2, rho / 2 }; + const Eigen::Array2d rhovec = { rho / 2, rho / 2 }; auto get_worst_error = [&T, &rhovec](const auto &model){ - auto H1 = build_Psir_Hessian_autodiff(model, T, rhovec); - auto H2 = build_Psir_Hessian_mcx(model, T, rhovec); + using id = IsochoricDerivatives <decltype(model)>; + auto H1 = id::build_Psir_Hessian_autodiff(model, T, rhovec); + auto H2 = id::build_Psir_Hessian_mcx(model, T, rhovec); auto err = (H1.array() - H2).abs().maxCoeff(); CAPTURE(err); CHECK(err < 1e-15); @@ -139,7 +141,7 @@ TEST_CASE("Trace critical locus for vdW", "[vdW][crit]") auto Zc = 3.0/8.0; auto rhoc0 = pc_Pa[0] / (vdW.R * Tc_K[0]) / Zc; double T0 = Tc_K[0]; - std::valarray<double> rhovec0 = { rhoc0, 0.0 }; + Eigen::ArrayXd rhovec0(2); rhovec0 << rhoc0, 0.0 ; auto tic0 = std::chrono::steady_clock::now(); std::string filename = ""; @@ -152,4 +154,27 @@ TEST_CASE("TEST B12", "") { const double T = 298.15; const std::valarray<double> molefrac = { 1/3, 2/3 }; auto B12 = get_B12vir(model, T, molefrac); +} + +TEST_CASE("Test psir gradient", "") { + const auto model = build_vdW(); + const double T = 298.15; + + using id = IsochoricDerivatives<decltype(model)>; + const Eigen::Array2d rhovec = { 1, 2 }; + const Eigen::Array2d molefrac = { 1 / 3, 2 / 3 }; + auto psirfunc2 = [&model](const auto& T, const auto& rho_) { + auto rhotot_ = rho_.sum(); + auto molefrac = (rho_ / rhotot_); + return model.alphar(T, rhotot_, molefrac) * model.R * T * rhotot_; + }; + auto chk0 = derivrhoi(psirfunc2, T, rhovec, 0); + auto chk1 = derivrhoi(psirfunc2, T, rhovec, 1); + auto grad = id::build_Psir_gradient_autodiff(model, T, rhovec); + auto err0 = std::abs((chk0 - grad[0])/chk0); + auto err1 = std::abs((chk1 - grad[1])/chk1); + CAPTURE(err0); + CAPTURE(err1); + CHECK(err0 < 1e-12); + CHECK(err1 < 1e-12); } \ No newline at end of file -- GitLab