From 4d3f8fd2c8f1239eea1331f3269e21145e3f70f8 Mon Sep 17 00:00:00 2001 From: Ian Bell <ian.bell@nist.gov> Date: Tue, 8 Nov 2022 15:35:20 -0700 Subject: [PATCH] Add Newton-Raphson iterator for homogeneous iterative calculations --- include/teqp/algorithms/iteration.hpp | 72 +++++++++++++++++++++++++++ interface/pybind11_wrapper.cpp | 13 +++++ src/bench_derivbox.cpp | 36 ++++++++++++-- 3 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 include/teqp/algorithms/iteration.hpp diff --git a/include/teqp/algorithms/iteration.hpp b/include/teqp/algorithms/iteration.hpp new file mode 100644 index 0000000..9d05044 --- /dev/null +++ b/include/teqp/algorithms/iteration.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "teqp/cpp/teqpcpp.hpp" +#include "teqp/cpp/derivs.hpp" + +namespace teqp { +namespace iteration { + +using namespace cppinterface; +/** + A class for doing Newton-Raphson steps to solve for two unknown thermodynamic variables + + */ +class NRIterator{ +private: + const std::shared_ptr<AbstractModel> ar, aig; + const std::vector<char> vars; + const Eigen::Ref<const Eigen::ArrayXd> vals; + double T, rho; + const Eigen::Ref<const Eigen::ArrayXd> z; + +public: + NRIterator(const std::shared_ptr<AbstractModel> &ar, const std::shared_ptr<AbstractModel> &aig, const std::vector<char>& vars, const Eigen::Ref<const Eigen::ArrayXd>& vals, double T, double rho, const Eigen::Ref<const Eigen::ArrayXd>& z) : ar(ar), aig(aig), vars(vars), vals(vals), T(T), rho(rho), z(z){} + + /// Return the variables that are being used in the iteration + std::vector<char> get_vars() const { return vars; } + /// Return the target values to be obtained + Eigen::ArrayXd get_vals() const { return vals; } + /// Return the current temperature + auto get_T() const { return T; } + /// Return the current molar density + auto get_rho() const { return rho; } + /// Return the current mole fractions + Eigen::ArrayXd get_molefrac() const { return z; } + + /** Do the calculations needed for the step and return the step and the other data + * In C++, the copy will be elided (the return value will be moved) + * \param T Temperature + * \param rhomolar Molar density + */ + auto calc_step(double T, double rho){ + auto Ar = ar->get_deriv_mat2(T, rho, z); + auto Aig = aig->get_deriv_mat2(T, rho, z); + auto R = ar->get_R(z); + auto im = build_iteration_Jv(vars, Ar, Aig, R, T, rho, z); + return std::make_tuple((im.J.matrix().colPivHouseholderQr().solve((-(im.v-vals)).matrix())).eval(), im); + } + + /// Take one step, return the residuals + auto take_step(){ + auto [dx, im] = calc_step(T, rho); + T += dx(0); + rho += dx(1); + return (im.v-vals).eval(); + } + + /** Take a given number of steps + * \param N The number of steps to take + */ + auto take_steps(int N){ + if (N <= 0){ + throw teqp::InvalidArgument("N must be greater than 0"); + } + for (auto i = 0; i < N; ++i){ + take_step(); + } + } +}; + + +} +} diff --git a/interface/pybind11_wrapper.cpp b/interface/pybind11_wrapper.cpp index 06588c6..6e6d571 100644 --- a/interface/pybind11_wrapper.cpp +++ b/interface/pybind11_wrapper.cpp @@ -11,6 +11,7 @@ #include "teqp/derivs.hpp" #include "teqp/cpp/teqpcpp.hpp" #include "teqp/models/multifluid_ancillaries.hpp" +#include "teqp/algorithms/iteration.hpp" namespace py = pybind11; using namespace py::literals; @@ -353,6 +354,18 @@ void init_teqp(py::module& m) { m.def("_make_model", &teqp::cppinterface::make_model); m.def("attach_model_specific_methods", &attach_model_specific_methods); + using namespace teqp::iteration; + py::class_<NRIterator>(m, "NRIterator") + .def(py::init<const std::shared_ptr<AbstractModel> &, const std::shared_ptr<AbstractModel> &, const std::vector<char>&, const Eigen::Ref<const Eigen::ArrayXd>&, double, double, const Eigen::Ref<const Eigen::ArrayXd>&>()) + .def("take_step", &NRIterator::take_step) + .def("take_steps", &NRIterator::take_steps) + .def("get_vars", &NRIterator::get_vars) + .def("get_vals", &NRIterator::get_vals) + .def("get_molefrac", &NRIterator::get_molefrac) + .def("get_T", &NRIterator::get_T) + .def("get_rho", &NRIterator::get_rho) + ; + // // Some functions for timing overhead of interface // m.def("___mysummer", [](const double &c, const Eigen::ArrayXd &x) { return c*x.sum(); }); // using RAX = Eigen::Ref<const Eigen::ArrayXd>; diff --git a/src/bench_derivbox.cpp b/src/bench_derivbox.cpp index e79ffc7..7223d7e 100644 --- a/src/bench_derivbox.cpp +++ b/src/bench_derivbox.cpp @@ -6,11 +6,14 @@ #include "teqp/derivs.hpp" #include "teqp/ideal_eosterms.hpp" +#include "teqp/cpp/teqpcpp.hpp" +#include "teqp/algorithms/iteration.hpp" + using namespace teqp; TEST_CASE("multifluid derivatives", "[mf]") { - std::vector<std::string> names = { "Propane" }; + std::vector<std::string> names = { "Ethane" }; auto model = build_multifluid_model(names, "../mycp"); double T = 300, rho = 2; @@ -21,6 +24,15 @@ TEST_CASE("multifluid derivatives", "[mf]") nlohmann::json jigs = nlohmann::json::array(); jigs.push_back(jig); auto aig = teqp::IdealHelmholtz(jigs); + auto amm = teqp::cppinterface::make_multifluid_model(names, "../mycp"); + auto aigg = teqp::cppinterface::make_model({{"kind","IdealHelmholtz"}, {"model", jigs}}); + + std::vector<char> vars = {'P', 'S'}; + const auto vals = (Eigen::ArrayXd(2) << 300.0, 400.0).finished(); + + Eigen::Ref<const Eigen::ArrayXd> rvals = vals, rz = z; + teqp::iteration::NRIterator NR(amm, aigg, vars, rvals, T, rho, rz); + BENCHMARK("alphar") { return model.alphar(T, rho, z); }; @@ -33,8 +45,24 @@ TEST_CASE("multifluid derivatives", "[mf]") return DerivativeHolderSquare<2,AlphaWrapperOption::idealgas>(aig, T, rho, z).derivs; }; - BENCHMARK("IterationStepper matrix") { - std::vector<char> vars = {'P','S','T','D'}; - return build_iteration_Jv(vars, model, aig, T, rho, z); + BENCHMARK("All residual derivatives (via AbstractModel) needed for first derivatives of h,s,u,p w.r.t. T&rho") { + return amm->get_deriv_mat2(T, rho, z); + }; + BENCHMARK("All residual derivatives (via AbstractModel) needed for first derivatives of h,s,u,p w.r.t. T&rho") { + return aigg->get_deriv_mat2(T, rho, z); }; + + BENCHMARK("Newton-Raphson construction") { + return teqp::iteration::NRIterator(amm, aigg, vars, rvals, T, rho, rz); + }; + BENCHMARK("Newton-Raphson calc_step") { + return NR.calc_step(T, rho); + }; + BENCHMARK("Newton-Raphson take_step") { + return NR.take_step(); + }; + BENCHMARK("Newton-Raphson take_steps") { + return NR.take_steps(5); + }; + } -- GitLab