diff --git a/externals/mcx b/externals/mcx index 5e48688a952d1e24e876541a68609e43a7083aa3..fa339e9963f58fb4998e56271173c9afa3e5dc28 160000 --- a/externals/mcx +++ b/externals/mcx @@ -1 +1 @@ -Subproject commit 5e48688a952d1e24e876541a68609e43a7083aa3 +Subproject commit fa339e9963f58fb4998e56271173c9afa3e5dc28 diff --git a/include/teqp/algorithms/VLE.hpp b/include/teqp/algorithms/VLE.hpp index 70050a45717ae8bfc99504dcb7271101fb2e3292..f9ef0a9d0bbf82c856fc6d2bfa8a2aea4e3ce783 100644 --- a/include/teqp/algorithms/VLE.hpp +++ b/include/teqp/algorithms/VLE.hpp @@ -22,11 +22,12 @@ auto linsolve(const A& a, const B& b) { return a.matrix().colPivHouseholderQr().solve(b.matrix()).array().eval(); } -template<typename Model, typename TYPE = double> +template<typename Model, typename TYPE = double, ADBackends backend = ADBackends::autodiff> class IsothermPureVLEResiduals { - typedef Eigen::Array<TYPE, 2, 1> EigenArray; - typedef Eigen::Array<TYPE, 1, 1> EigenArray1; - typedef Eigen::Array<TYPE, 2, 2> EigenMatrix; +public: + using EigenArray = Eigen::Array<TYPE, 2, 1>; + using EigenArray1 = Eigen::Array<TYPE, 1, 1>; + using EigenMatrix = Eigen::Array<TYPE, 2, 2>; private: const Model& m_model; TYPE m_T; @@ -59,13 +60,13 @@ public: const TYPE R = m_model.R(molefracs); double R0_over_Rr = R0 / Rr; - auto derL = tdx::template get_Ar0n<2>(m_model, T, rhomolarL, molefracs); + auto derL = tdx::template get_Ar0n<2, backend>(m_model, T, rhomolarL, molefracs); auto pRTL = rhomolarL*(R0_over_Rr + derL[1]); // p/(R*T) auto dpRTLdrhoL = R0_over_Rr + 2*derL[1] + derL[2]; auto hatmurL = derL[1] + derL[0] + R0_over_Rr*log(rhomolarL); auto dhatmurLdrho = (2*derL[1] + derL[2])/rhomolarL + R0_over_Rr/rhomolarL; - auto derV = tdx::template get_Ar0n<2>(m_model, T, rhomolarV, molefracs); + auto derV = tdx::template get_Ar0n<2, backend>(m_model, T, rhomolarV, molefracs); auto pRTV = rhomolarV*(R0_over_Rr + derV[1]); // p/(R*T) auto dpRTVdrhoV = R0_over_Rr + 2*derV[1] + derV[2]; auto hatmurV = derV[1] + derV[0] + R0_over_Rr *log(rhomolarV); @@ -97,8 +98,9 @@ public: }; template<typename Residual, typename Scalar> -Eigen::ArrayXd do_pure_VLE_T(Residual &resid, Scalar rhoL, Scalar rhoV, int maxiter) { - auto rhovec = (Eigen::ArrayXd(2) << rhoL, rhoV).finished(); +auto do_pure_VLE_T(Residual &resid, Scalar rhoL, Scalar rhoV, int maxiter) { + using EArray = Eigen::Array<Scalar, 2, 1>; + auto rhovec = (EArray() << rhoL, rhoV).finished(); auto r0 = resid.call(rhovec); auto J = resid.Jacobian(rhovec); for (int iter = 0; iter < maxiter; ++iter){ @@ -108,21 +110,28 @@ Eigen::ArrayXd do_pure_VLE_T(Residual &resid, Scalar rhoL, Scalar rhoV, int maxi } auto v = J.matrix().colPivHouseholderQr().solve(-r0.matrix()).array().eval(); auto rhovecnew = (rhovec + v).eval(); + double r00 = static_cast<double>(r0[0]); + double r01 = static_cast<double>(r0[1]); // If the solution has stopped improving, stop. The change in rhovec is equal to v in infinite precision, but // not when finite precision is involved, use the minimum non-denormal float as the determination of whether // the values are done changing - if (((rhovecnew - rhovec).cwiseAbs() < std::numeric_limits<Scalar>::min()).all()) { + auto minval = std::numeric_limits<Scalar>::epsilon(); + double minvaldbl = static_cast<double>(minval); + if (((rhovecnew - rhovec).cwiseAbs() < minval).all()) { + break; + } + if ((r0.cwiseAbs() < minval).all()) { break; } rhovec = rhovecnew; } - return (Eigen::ArrayXd(2) << rhovec[0], rhovec[1]).finished(); + return rhovec; } -template<typename Model, typename Scalar> -Eigen::ArrayXd pure_VLE_T(const Model& model, Scalar T, Scalar rhoL, Scalar rhoV, int maxiter) { - auto res = IsothermPureVLEResiduals(model, T); +template<typename Model, typename Scalar, ADBackends backend = ADBackends::autodiff> +auto pure_VLE_T(const Model& model, Scalar T, Scalar rhoL, Scalar rhoV, int maxiter) { + auto res = IsothermPureVLEResiduals<Model, Scalar, backend>(model, T); return do_pure_VLE_T(res, rhoL, rhoV, maxiter); } diff --git a/include/teqp/derivs.hpp b/include/teqp/derivs.hpp index e6196337e520aa612a977a8307eb6d26768d5f9e..64b3f6693befa1acd2c029b222c515477c469cbd 100644 --- a/include/teqp/derivs.hpp +++ b/include/teqp/derivs.hpp @@ -205,7 +205,7 @@ struct TDXDerivatives { template<int Nderiv, ADBackends be = ADBackends::autodiff> static auto get_Ar0n(const Model& model, const Scalar& T, const Scalar& rho, const VectorType& molefrac) { - std::valarray<double> o(Nderiv+1); + std::valarray<Scalar> o(Nderiv+1); if constexpr (be == ADBackends::autodiff) { autodiff::HigherOrderDual<Nderiv, double> rhodual = rho; auto f = [&model, &T, &molefrac](const auto& rho_) { return eval(model.alphar(T, rho_, molefrac)); }; diff --git a/include/teqp/models/cubics.hpp b/include/teqp/models/cubics.hpp index 76782c0f5fe1328885bfb13f3b599459326d627a..a631d6faf739f6111a10b3f73d5bae82ab2f60ac 100644 --- a/include/teqp/models/cubics.hpp +++ b/include/teqp/models/cubics.hpp @@ -141,7 +141,7 @@ public: throw std::invalid_argument("Sizes do not match"); } auto b = get_b(T, molefrac); - auto Psiminus = -log(1.0 - b * rho); + auto Psiminus = -1.0*log(1.0 - b * rho); auto Psiplus = log((Delta1 * b * rho + 1.0) / (Delta2 * b * rho + 1.0)) / (b * (Delta1 - Delta2)); auto val = Psiminus - get_a(T, molefrac) / (Ru * T) * Psiplus; return forceeval(val); @@ -178,8 +178,8 @@ auto canonical_SRK(TCType Tc_K, PCType pc_K, AcentricType acentric, const Eigen: 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); - double Delta2 = 1-sqrt(2); + double Delta1 = 1+sqrt(2.0); + double Delta2 = 1-sqrt(2.0); AcentricType m = acentric*0.0; std::vector<AlphaFunctionOptions> alphas; for (auto i = 0; i < Tc_K.size(); ++i) { diff --git a/src/test_mpderivs.cpp b/src/test_mpderivs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80a19a2b0f95fa59cfcb6dc54de13375c344c050 --- /dev/null +++ b/src/test_mpderivs.cpp @@ -0,0 +1,27 @@ +// Imports from boost +#include <boost/multiprecision/cpp_bin_float.hpp> +using namespace boost::multiprecision; + +#include "teqp/models/cubics.hpp" +#include "teqp/algorithms/VLE.hpp" +#include "teqp/derivs.hpp" + +int main() { + + // Values taken from http://dx.doi.org/10.6028/jres.121.011 + std::valarray<double> Tc_K = { 154.581}, pc_Pa = { 5042800}, acentric = { 0.022}; + auto modelPR = teqp::canonical_PR(Tc_K, pc_Pa, acentric); + + using my_float = boost::multiprecision::number<boost::multiprecision::cpp_bin_float<200>>; + auto [rhoLdbl, rhoVdbl] = modelPR.superanc_rhoLV(125); + my_float T = 125; + auto soln = teqp::pure_VLE_T<decltype(modelPR), my_float, teqp::ADBackends::multicomplex>(modelPR, T, static_cast<my_float>(rhoLdbl), static_cast<my_float>(rhoVdbl), 20).cast<double>(); + std::cout << soln << std::endl; + + my_float x = 10567.6789; + std::function<mcx::MultiComplex<my_float>(const mcx::MultiComplex<my_float>&)> ff = [](const auto & x) {return 1.0 - x;}; + auto o = mcx::diff_mcx1<my_float>(ff, x, 3, true); + std::cout << o[1] << std::endl; + + std::cout << std::is_arithmetic_v<my_float> << std::endl; +} \ No newline at end of file