From 799d5a620c39abb3bdf44bb3e6b9c91b1c824ada Mon Sep 17 00:00:00 2001
From: Ian Bell <ian.bell@nist.gov>
Date: Fri, 15 Oct 2021 09:36:17 -0400
Subject: [PATCH] Add catch tests to C interface and simple, fast uid approach

---
 CMakeLists.txt          |   2 +
 include/teqp/derivs.hpp |  14 +++---
 interface/C/teqpc.cpp   | 108 +++++++++++++++++++++++++++++-----------
 3 files changed, 87 insertions(+), 37 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 647c9bd..75e2bce 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,8 @@ if (MSVC)
     target_sources(teqpc PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/externals/Eigen/debug/msvc/eigen.natvis")
 endif()
 target_link_libraries(teqpc autodiff)
+target_include_definitions(teqpc PUBLIC -DTEQPC_CATCH)
+add_test(teqpc_catch teqpc)
 
 ### TARGETS from src folder
 if (TEQP_SNIPPETS)
diff --git a/include/teqp/derivs.hpp b/include/teqp/derivs.hpp
index 5864e2c..3bf97db 100644
--- a/include/teqp/derivs.hpp
+++ b/include/teqp/derivs.hpp
@@ -226,12 +226,12 @@ struct TDXDerivatives {
             if (idelta == 0) {
                 return get_Ar00(model, T, rho, molefrac);
             }
-            //else if (idelta == 1) {
-            //    return get_Ar01(model, T, rho, molefrac);
-            //}
-            //else if (idelta == 2) {
-            //    return get_Ar02(model, T, rho, molefrac);
-            //}
+            else if (idelta == 1) {
+                return get_Ar01(model, T, rho, molefrac);
+            }
+            else if (idelta == 2) {
+                return get_Ar02(model, T, rho, molefrac);
+            }
             else {
                 throw std::invalid_argument("Invalid value for idelta");
             }
@@ -259,7 +259,7 @@ struct TDXDerivatives {
             }
         }
         else {
-            throw std::invalid_argument("Invalid value for idelta");
+            throw std::invalid_argument("Invalid value for itau");
         }
     }
 
diff --git a/interface/C/teqpc.cpp b/interface/C/teqpc.cpp
index 3ae632f..4c9907c 100644
--- a/interface/C/teqpc.cpp
+++ b/interface/C/teqpc.cpp
@@ -5,20 +5,29 @@
 
 #include <unordered_map>
 #include <variant>
+#include <atomic>
 
 #include "teqp/models/eos.hpp"
 #include "teqp/models/cubics.hpp"
 #include "teqp/derivs.hpp"
 
-// TODO: actually make new UUID for each model instance
-// TODO: catch tests
-
 using vad = std::valarray<double>;
 using cub = decltype(canonical_PR(vad{}, vad{}, vad{}));
 
 using AllowedModels = std::variant<vdWEOS1, cub>;
 std::unordered_map<std::string, AllowedModels> library;
 
+// An atomic is used here for thread safety
+// The max possible index is 18,446,744,073,709,551,615
+std::atomic<unsigned long long int> next_index{ 0 };
+
+/// A function for returning a sequential index of the next 
+std::string get_uid(int N) {
+    auto s = std::to_string(next_index);
+    next_index++;
+    return std::string(N - s.size(), '0') + s;
+}
+
 class teqpcException : public std::exception {
 public:
     const int code;
@@ -26,7 +35,7 @@ public:
     teqpcException(int code, const std::string& msg) : code(code), msg(msg) {}
 };
 
-void HandleException(int& errcode, char* message_buffer, const int buffer_length)
+void exception_handler(int& errcode, char* message_buffer, const int buffer_length)
 {
     try{
         throw; // Rethrow the error so that we can handle it here
@@ -44,13 +53,14 @@ void HandleException(int& errcode, char* message_buffer, const int buffer_length
 int build_model(const char* j, char* uuid, char* errmsg, int errmsg_length){
     int errcode = 0;
     try{
-        std::string uid = "100"; // TODO: this needs to be unique, not hardcoded
         nlohmann::json json = nlohmann::json::parse(j);
         
-        // Extract the name of the model and the parameters
+        // Extract the name of the model and the model parameters
         std::string kind = json.at("kind");
         auto spec = json.at("model");
 
+        std::string uid = get_uid(32);
+
         if (kind == "vdW1") {
             library.emplace(std::make_pair(uid, vdWEOS1(spec.at("a"), spec.at("b"))));
         }
@@ -68,7 +78,7 @@ int build_model(const char* j, char* uuid, char* errmsg, int errmsg_length){
         strcpy(uuid, uid.c_str());
     }
     catch (...) {
-        HandleException(errcode, errmsg, errmsg_length);
+        exception_handler(errcode, errmsg, errmsg_length);
     }
     return errcode;
 }
@@ -79,7 +89,7 @@ int free_model(char* uuid, char* errmsg, int errmsg_length) {
         library.erase(std::string(uuid));
     }
     catch (...) {
-        HandleException(errcode, errmsg, errmsg_length);
+        exception_handler(errcode, errmsg, errmsg_length);
     }
     return errcode;
 }
@@ -100,24 +110,47 @@ int get_Arxy(char* uuid, const int NT, const int ND, const double T, const doubl
         *val = std::visit(f, library.at(std::string(uuid)));
     }
     catch (...) {
-        HandleException(errcode, errmsg, errmsg_length);
+        exception_handler(errcode, errmsg, errmsg_length);
     }
     return errcode;
 }
 
-int main() {
+#if defined(TEQPC_CATCH)
+
+#define CATCH_CONFIG_ENABLE_BENCHMARKING
+#define CATCH_CONFIG_MAIN
+#include "catch/catch.hpp"
+
+TEST_CASE("Use of C interface with simple models") {
+
     constexpr int errmsg_length = 300;
-    char uuid[300] = "", errmsg[errmsg_length] = "";
+    char uuid[33] = "", uuidPR[33] = "", errmsg[errmsg_length] = "";
     double val = -1;
     std::valarray<double> molefrac = { 1.0 };
-    {
+
+    std::string j = R"(
+            {
+                "kind": "PR", 
+                "model": {
+                    "Tcrit / K": [190], 
+                    "pcrit / Pa": [3.5e6], 
+                    "acentric": [0.11]
+                }
+            }
+        )";
+    build_model(j.c_str(), uuidPR, errmsg, errmsg_length);
+    
+    BENCHMARK("vdW1") {
         std::string j = R"({"kind":"vdW1", "model":{"a":1.0, "b":2.0}})";
-        int errcode1 = build_model(j.c_str(), uuid, errmsg, errmsg_length);
-        int errcode11 = get_Arxy(uuid, 0, 0, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
-        int errcode2 = free_model(uuid, errmsg, errmsg_length);
-        int rrr = 0;
-    }
-    {
+        int e1 = build_model(j.c_str(), uuid, errmsg, errmsg_length);
+        int e2 = get_Arxy(uuid, 0, 0, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
+        int e3 = free_model(uuid, errmsg, errmsg_length);
+        REQUIRE(e1 == 0);
+        REQUIRE(e2 == 0);
+        REQUIRE(e3 == 0);
+        return val;
+    };
+    BENCHMARK("PR") {
         std::string j = R"(
             {
                 "kind": "PR", 
@@ -128,12 +161,20 @@ int main() {
                 }
             }
         )";
-        int errcode1 = build_model(j.c_str(), uuid, errmsg, errmsg_length);
-        int errcode11 = get_Arxy(uuid, 0, 0, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
-        int errcode2 = free_model(uuid, errmsg, errmsg_length);
-        int rrr = 0;
-    }
-    {
+        int e1 = build_model(j.c_str(), uuid, errmsg, errmsg_length);
+        int e2 = get_Arxy(uuid, 0, 0, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
+        int e3 = free_model(uuid, errmsg, errmsg_length);
+        REQUIRE(e1 == 0);
+        REQUIRE(e2 == 0);
+        REQUIRE(e3 == 0);
+        return val;
+    };
+    BENCHMARK("PR call") {
+        int e = get_Arxy(uuidPR, 0, 0, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
+        REQUIRE(e == 0);
+        return val;
+    };
+    BENCHMARK("SRK") {
         std::string j = R"(
             {
                 "kind": "SRK", 
@@ -144,9 +185,16 @@ int main() {
                 }
             }
         )";
-        int errcode1 = build_model(j.c_str(), uuid, errmsg, errmsg_length);
-        int errcode11 = get_Arxy(uuid, 0, 1, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
-        int errcode2 = free_model(uuid, errmsg, errmsg_length);
-        int rrr = 0;
-    }
-}
\ No newline at end of file
+        int e1 = build_model(j.c_str(), uuid, errmsg, errmsg_length);
+        int e2 = get_Arxy(uuid, 0, 1, 300, 3.0e-6, &(molefrac[0]), molefrac.size(), &val, errmsg, errmsg_length);
+        int e3 = free_model(uuid, errmsg, errmsg_length);
+        REQUIRE(e1 == 0);
+        REQUIRE(e2 == 0);
+        REQUIRE(e3 == 0);
+        return val;
+    };
+}
+#else 
+int main() {
+}
+#endif
\ No newline at end of file
-- 
GitLab