From 043fc17aa28447a1968285028e0c0db8fe949ec6 Mon Sep 17 00:00:00 2001
From: Ian Bell <ian.bell@nist.gov>
Date: Mon, 10 May 2021 15:19:37 -0400
Subject: [PATCH] Add a model container and a testing file

---
 include/teqp/containers.hpp | 48 +++++++++++++++++++
 src/teqp_profile.cpp        | 96 +++++++++++++++++++++++++++++++++++++
 src/test_variant.cpp        | 80 +++++++++++++++++++++++++++++++
 3 files changed, 224 insertions(+)
 create mode 100644 include/teqp/containers.hpp
 create mode 100644 src/teqp_profile.cpp
 create mode 100644 src/test_variant.cpp

diff --git a/include/teqp/containers.hpp b/include/teqp/containers.hpp
new file mode 100644
index 0000000..2f750fb
--- /dev/null
+++ b/include/teqp/containers.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+//template <typename T>
+//concept has_meta = requires(T const& m) {
+//    { m.get_meta() };
+//};
+
+template<typename... Args>
+class ModelContainer {
+public:
+    using mid = std::size_t;
+    using varModels = std::variant<Args...>;
+private:
+    mid last_id = 0;
+    std::map<mid, varModels> modcoll;
+public:
+    template <typename T> const auto& get_ref(mid id) { return std::get<T>(get_model(id)); }
+
+    auto size() const { return modcoll.size(); }
+    auto new_id() {
+        last_id++;
+        return last_id;
+    }
+    template<typename Instance>
+    auto add_model(Instance&& instance) {
+        auto uid = new_id();
+        modcoll.emplace(uid, std::move(instance));
+        return uid;
+    }
+
+    const varModels& get_model(mid id) const {
+        return modcoll.at(id);
+    }
+
+    template <typename Function>
+    auto caller(const mid& mid, const Function &f) const {
+        return std::visit([&](auto& model) { return f(model); }, get_model(mid));
+    }
+
+    //nlohmann::json get_meta(const mid& mid) const {
+    //    const auto& modvar = get_model(mid);
+    //    nlohmann::json result;
+    //    std::visit([&](auto&& model) {
+    //        if constexpr (has_meta<decltype(model)>) { result = model.get_meta(); }
+    //    }, modvar);
+    //    return result;
+    //}
+};
\ No newline at end of file
diff --git a/src/teqp_profile.cpp b/src/teqp_profile.cpp
new file mode 100644
index 0000000..9a0af08
--- /dev/null
+++ b/src/teqp_profile.cpp
@@ -0,0 +1,96 @@
+#include <vector>
+#include <iostream>
+#include <complex>
+#include <variant>
+#include <chrono>
+
+#include "teqp/types.hpp"
+#include "nlohmann/json.hpp"
+#include "teqp/models/eos.hpp"
+//#include "teqp/models/CPA.hpp"
+#include "teqp/models/multifluid.hpp"
+#include "teqp/models/pcsaft.hpp"
+#include "teqp/containers.hpp"
+#include <Eigen/Dense>
+#include "teqp/derivs.hpp"
+
+class Timer {
+private:
+    int N;
+    decltype(std::chrono::steady_clock::now()) tic;
+public:
+    Timer(int N) : N(N), tic(std::chrono::steady_clock::now()) {}
+    ~Timer() {
+        auto elap = std::chrono::duration<double>(std::chrono::steady_clock::now() - tic).count();
+        std::cout << elap / N * 1e6 << " us/call" << std::endl;
+    }
+};
+
+template <typename ModelContainer, typename T1, typename T2, typename T3>
+auto get_f(const ModelContainer& modcon, const T1& x1, const T2& x2, const T3& x3) {
+    // Call the function with T, rho, molefrac arguments
+    return std::visit([&](auto&& model) { return model.alphar(x1, x2, x3); }, modcon);
+}
+
+int main() {
+    // Here, all models that can be stored in this container are defined. Types may
+    // be obtained from factory functions without explicit definition
+    using MFType = decltype(build_multifluid_model(std::vector<std::string>{"", ""}, "", ""));
+    ModelContainer<vdWEOS1, vdWEOS<double>, MFType, PCSAFTMixture> mc;
+    std::vector<int> indices;
+
+    auto adder = [&](auto&&m) { indices.push_back(mc.add_model(m)); };
+
+    adder(vdWEOS1(1, 2));
+    adder(vdWEOS<double>({ 150.687 }, { 4863000.0 })); 
+    adder(PCSAFTMixture({ "Methane" }));
+    adder(build_multifluid_model({ "Methane" }, "../mycp", "../mycp/dev/mixtures/mixture_binary_pairs.json"));
+
+    double x1 = 3.0;
+    double x2 = 2.0; 
+    auto c = (Eigen::ArrayXd(1) << 1.0).finished();
+    int N = 100000;
+
+    auto f_alphar = [&x1, &x2, &c](auto& model) { return model.alphar(x1, x2, c); };
+    auto alphar = mc.caller(1, f_alphar);
+    //auto meta = mc.caller(1, [&x1, &x2, &c](auto& model) { return model.get_meta(); });
+
+    auto m = mc.get_ref<MFType>(4);
+    m.alphar(x2, x2, c);
+    {
+        Timer t(N);
+        for (auto j = 0; j < N; ++j) {
+            auto v = m.alphar(x1, x2, c);
+            //std::cout << v << std::endl;
+        }
+    }
+    for (auto counter = 0; counter < 10; ++counter){
+        Timer t(N);
+        for (auto j = 0; j < N; ++j) {
+            x1 += j * 1e-10;
+            //auto f_Ar01 = [&x1, &x2, &c](auto& model) { using tdx = TDXDerivatives<decltype(model), decltype(x1), decltype(c)>;  return tdx::get_Ar02(model, x1, x2, c); };
+            auto v = mc.caller(4, [&x1, &x2, &c](auto& model) { using tdx = TDXDerivatives<decltype(model), decltype(x1), decltype(c)>;  return tdx::get_Ar00(model, x1, x2, c); });
+            //std::cout << v << std::endl;
+        }
+    }
+
+    ////std::complex<double> x2(3.0, 1.0);
+    //std::cout << "----" << std::endl;
+    //for (auto i : indices) {
+    //    const auto& m = mc.get_model(i);
+    //    Timer t(N);
+    //    for (auto j = 0; j < N; ++j) {
+    //        auto v = get_f(m, x1, x2, c);
+    //        //std::cout << v << std::endl;
+    //    }
+    //}
+    //std::cout << "----" << std::endl;
+    //for (auto i : indices) {
+    //    Timer t(N);
+    //    for (auto j = 0; j < N; ++j) {
+    //        auto v = mc.caller(i, f_alphar);
+    //        //std::cout << v << std::endl;
+    //    }
+    //    auto f_alphar = [&x1, &x2, &c](auto& model) { return model(x1, x2, c); };
+    //}
+}
\ No newline at end of file
diff --git a/src/test_variant.cpp b/src/test_variant.cpp
new file mode 100644
index 0000000..bb55749
--- /dev/null
+++ b/src/test_variant.cpp
@@ -0,0 +1,80 @@
+#include <vector>
+#include <iostream>
+#include <complex>
+#include <variant>
+#include <chrono>
+#include <map>
+#include <valarray>
+
+#include "teqp/types.hpp"
+#include "nlohmann/json.hpp"
+#include "teqp/models/eos.hpp"
+//#include "teqp/models/CPA.hpp"
+#include "teqp/models/multifluid.hpp"
+#include "teqp/models/pcsaft.hpp"
+#include "teqp/containers.hpp"
+#include <Eigen/Dense>
+
+class Timer {
+private:
+    int N;
+    decltype(std::chrono::steady_clock::now()) tic;
+public:
+    Timer(int N) : N(N), tic(std::chrono::steady_clock::now()){}
+    ~Timer() {
+        auto elap = std::chrono::duration<double>(std::chrono::steady_clock::now()-tic).count();
+        std::cout << elap/N*1e6 << " us/call" << std::endl;
+    }
+};
+
+template <typename ModelContainer, typename T1, typename T2, typename T3> 
+auto get_f(const ModelContainer &modcon, const T1& x1, const T2& x2, const T3& x3){
+    // The output type is the type promotion of T, rho, and molefrac
+    std::common_type_t<T1, T2, decltype(x3[0])> result = -1;
+    // Call the function with T, rho, molefrac arguments
+    std::visit([&](auto&& model) { result = model.alphar(x1, x2, x3); }, modcon);
+    return result;
+}
+
+int main(){
+    // Here, all models that can be stored in this container are defined. Types may
+    // be obtained from factory functions without explicit definition
+    using MFType = decltype(build_multifluid_model(std::vector<std::string>{"", ""}, "", ""));
+    ModelContainer<MFType, vdWEOS<double>, vdWEOS1> mc;
+    nlohmann::json vdWargs = { { "a",3 }, { "b", 4 } };
+    std::string vdWs = vdWargs.dump();
+    for (auto i = 0; i < 10; ++i) {
+        mc.add_model(vdWEOS1(1, 2));
+    }
+    const auto& v = mc.get_ref<vdWEOS1>(1);
+
+    auto c = (Eigen::ArrayXd(2) << 3.0, 3.0).finished();
+    int N = 1000;
+    volatile double x1 = 3.0;
+    std::complex<double> x2(3.0, 1.0);
+    vdWEOS1 b1(3, 4);
+    for (auto i = 0; i < mc.size(); ++i) {
+        Timer t(N);
+        for (auto j = 0; j < N; ++j) {
+            volatile auto v = b1.alphar(x1, x2, c);
+            //std::cout << v << std::endl;
+        }
+    }
+    std::cout << "----" << std::endl;
+    for (auto i = 0; i < mc.size(); ++i) {
+        const auto& m = mc.get_model(1);
+        Timer t(N); 
+        for (auto j = 0; j < N; ++j) {
+            auto v = get_f(m, x1, x2, c);
+            //std::cout << v << std::endl;
+        }
+    }
+    std::cout << "----" << std::endl;
+    for (auto i = 1; i <= mc.size(); ++i) {
+        Timer t(N);
+        for (auto j = 0; j < N; ++j) {
+            auto v = mc.get_alphar(i, x1, x2, c);
+            //std::cout << v << std::endl;
+        }
+    }
+}
\ No newline at end of file
-- 
GitLab