From 13fc6c7cf0fbc2c91487226c2f2398696f1ae5f0 Mon Sep 17 00:00:00 2001
From: Ian Bell <ian.bell@nist.gov>
Date: Thu, 4 Nov 2021 10:07:08 -0400
Subject: [PATCH] Add support for looking up names by alternative identifiers
 (aliases, CAS, and REFPROP name)

Add tests for all binary pairs
Add conversion script for HFC mixture xi and zeta
---
 include/teqp/models/multifluid.hpp  | 80 ++++++++++++++++++++++++-----
 src/tests/catch_test_multifluid.cxx | 17 ++++++
 2 files changed, 85 insertions(+), 12 deletions(-)

diff --git a/include/teqp/models/multifluid.hpp b/include/teqp/models/multifluid.hpp
index 169efd8..434325f 100644
--- a/include/teqp/models/multifluid.hpp
+++ b/include/teqp/models/multifluid.hpp
@@ -13,6 +13,7 @@
 #include "teqp/derivs.hpp"
 #include "teqp/types.hpp"
 #include "teqp/constants.hpp"
+#include "teqp/filesystem.hpp"
 
 #include "MultiComplex/MultiComplex.hpp"
 
@@ -226,18 +227,32 @@ public:
 
         throw std::invalid_argument("Can't match the binary pair for: " + identifiers[0] + "/" + identifiers[1]);
     }
-    static auto get_binary_interaction_double(const nlohmann::json& collection, const std::vector<std::string>& components, const nlohmann::json& flags) {
-        auto [el, swap_needed] = get_BIPdep(collection, components, flags);
-
-        double betaT = el["betaT"], gammaT = el["gammaT"], betaV = el["betaV"], gammaV = el["gammaV"];
-        // Backwards order of components, flip beta values
-        if (swap_needed) {
-            betaT = 1.0 / betaT;
-            betaV = 1.0 / betaV;
+
+    static auto get_binary_interaction_double(const nlohmann::json& collection, const std::vector<std::string>& identifiers, const nlohmann::json& flags, const std::vector<double>&Tc, const std::vector<double>&vc) {
+        auto [el, swap_needed] = get_BIPdep(collection, identifiers, flags);
+
+        double betaT, gammaT, betaV, gammaV;
+        if (el.contains("betaT") && el.contains("gammaT") && el.contains("betaV") & el.contains("gammaV")){
+            betaT = el["betaT"]; gammaT = el["gammaT"]; betaV = el["betaV"]; gammaV = el["gammaV"];
+            // Backwards order of components, flip beta values
+            if (swap_needed) {
+                betaT = 1.0 / betaT;
+                betaV = 1.0 / betaV;
+            }
+        }
+        else if (el.contains("xi") && el.contains("zeta")) {
+            gammaT = 0.5 * (Tc[0] + Tc[1] + el["xi"]) / (2 * sqrt(Tc[0] * Tc[1]));
+            gammaV =  4.0 * (vc[0] + vc[1] + el["zeta"]) / (0.25*pow(1 / pow(1 / vc[0], 1.0 / 3.0) + 1 / pow(1 / vc[1], 1.0 / 3.0), 3));
+            betaT = 1.0;
+            betaV = 1.0;
+        }
+        else {
+            std::invalid_argument("Could not understaind what to do with this binary model specification: " + el.dump());
         }
         return std::make_tuple(betaT, gammaT, betaV, gammaV);
     }
-    static auto get_BIP_matrices(const nlohmann::json& collection, const std::vector<std::string>& components, const nlohmann::json& flags) {
+    template <typename Tcvec, typename vcvec>
+    static auto get_BIP_matrices(const nlohmann::json& collection, const std::vector<std::string>& components, const nlohmann::json& flags, const Tcvec& Tc, const vcvec& vc) {
         Eigen::MatrixXd betaT, gammaT, betaV, gammaV, YT, Yv;
         auto N = components.size();
         betaT.resize(N, N); betaT.setZero();
@@ -246,7 +261,7 @@ public:
         gammaV.resize(N, N); gammaV.setZero();
         for (auto i = 0; i < N; ++i) {
             for (auto j = i + 1; j < N; ++j) {
-                auto [betaT_, gammaT_, betaV_, gammaV_] = get_binary_interaction_double(collection, { components[i], components[j] }, flags);
+                auto [betaT_, gammaT_, betaV_, gammaV_] = get_binary_interaction_double(collection, { components[i], components[j] }, flags, { Tc[i], Tc[j] }, { vc[i], vc[j] });
                 betaT(i, j) = betaT_;         betaT(j, i) = 1.0 / betaT(i, j);
                 gammaT(i, j) = gammaT_;       gammaT(j, i) = gammaT(i, j);
                 betaV(i, j) = betaV_;         betaV(j, i) = 1.0 / betaV(i, j);
@@ -870,6 +885,33 @@ inline nlohmann::json load_a_JSON_file(const std::string &path){
     return nlohmann::json::parse(stream);
 }
 
+/// Build a reverse-lookup map for finding a fluid JSON structure given a backup identifier
+inline auto build_alias_map(const std::string& root) {
+    std::map<std::string, std::string> aliasmap;
+    for (auto path : get_files_in_folder(root + "/dev/fluids", ".json")) {
+        auto j = load_a_JSON_file(path.string());
+        for (std::string k : {"NAME", "CAS", "REFPROP_NAME"}) {
+            std::string val = j.at("INFO").at(k); 
+            if (aliasmap.count(val) > 0) {
+                std::invalid_argument("Duplicated reverse lookup identifier ["+k+"] found in file:" + path.string());
+            }
+            else {
+                aliasmap[val] = std::filesystem::absolute(path).string();
+            }
+        }
+        std::vector<std::string> aliases = j.at("INFO").at("ALIASES");
+        for (std::string alias : aliases) {
+            if (aliasmap.count(alias) > 0) {
+                std::invalid_argument("Duplicated alias [" + alias + "] found in file:" + path.string());
+            }
+            else {
+                aliasmap[alias] = std::filesystem::absolute(path).string();
+            }
+        }
+    }
+    return aliasmap;
+}
+
 inline auto build_multifluid_model(const std::vector<std::string>& components, const std::string& coolprop_root, const std::string& BIPcollectionpath = {}, const nlohmann::json& flags = {}, const std::string& departurepath = {}) {
 
     std::string BIPpath = (BIPcollectionpath.empty()) ? coolprop_root + "/dev/mixtures/mixture_binary_pairs.json" : BIPcollectionpath;
@@ -879,7 +921,21 @@ inline auto build_multifluid_model(const std::vector<std::string>& components, c
     const auto depcollection = load_a_JSON_file(deppath);
 
     // Pure fluids
-    auto pureJSON = collect_component_json(components, coolprop_root);
+    std::vector<nlohmann::json> pureJSON;
+    try {
+        // Try the normal lookup, matching component name to a file in dev/fluids (case sensitive match on linux!)
+        pureJSON = collect_component_json(components, coolprop_root);
+    }
+    catch(...){
+        // Lookup the absolute paths for each component
+        auto aliasmap = build_alias_map(coolprop_root);
+        std::vector<std::string> abspaths;
+        for (auto c : components) {
+            abspaths.push_back(aliasmap[c]);
+        }
+        // Backup lookup with absolute paths resolved for each component
+        pureJSON = collect_component_json(abspaths, coolprop_root);
+    }
     auto [Tc, vc] = MultiFluidReducingFunction::get_Tcvc(pureJSON);
     auto EOSs = get_EOSs(pureJSON); 
 
@@ -891,7 +947,7 @@ inline auto build_multifluid_model(const std::vector<std::string>& components, c
     // Things related to the mixture
     auto F = MultiFluidReducingFunction::get_F_matrix(BIPcollection, identifiers, flags);
     auto funcs = get_departure_function_matrix(depcollection, BIPcollection, identifiers, flags);
-    auto [betaT, gammaT, betaV, gammaV] = MultiFluidReducingFunction::get_BIP_matrices(BIPcollection, identifiers, flags);
+    auto [betaT, gammaT, betaV, gammaV] = MultiFluidReducingFunction::get_BIP_matrices(BIPcollection, identifiers, flags, Tc, vc);
 
     auto redfunc = MultiFluidReducingFunction(betaT, gammaT, betaV, gammaV, Tc, vc);
 
diff --git a/src/tests/catch_test_multifluid.cxx b/src/tests/catch_test_multifluid.cxx
index bd3bbea..d86c9a9 100644
--- a/src/tests/catch_test_multifluid.cxx
+++ b/src/tests/catch_test_multifluid.cxx
@@ -80,4 +80,21 @@ TEST_CASE("Check that mixtures can also do absolute paths", "[multifluid],[abspa
         auto model = build_multifluid_model(abspaths, root, root + "/dev/mixtures/mixture_binary_pairs.json");
         auto model2 = build_multifluid_model(abspaths, root); // default path for BIP
     }
+}
+
+TEST_CASE("Check that all binary pairs specified in the binary pair file can be instantiated", "[multifluid],[binaries]") {
+    std::string root = "../mycp";
+    REQUIRE_NOTHROW(build_alias_map(root));
+    auto amap = build_alias_map(root);
+    for (auto el : load_a_JSON_file(root + "/dev/mixtures/mixture_binary_pairs.json")) {
+        auto is_unsupported = [](const auto& s) {
+            return (s == "METHANOL" || s == "R1216" || s == "C14" || s == "IOCTANE" || s == "C4F10" || s == "C5F12" || s == "C1CC6" || s == "C3CC6" || s == "CHLORINE" || s == "RE347MCC");
+        };
+        if (is_unsupported(el["Name1"]) || is_unsupported(el["Name2"])) {
+            continue;
+        }
+        CAPTURE(el["Name1"]);
+        CAPTURE(el["Name2"]);
+        CHECK_NOTHROW(build_multifluid_model({ amap[el["Name1"]], amap[el["Name2"]] }, root)); // default path for BIP
+    }
 }
\ No newline at end of file
-- 
GitLab