CoolProp 8.0.0
An open-source fluid property and humid air property database
UNIFACLibrary.cpp
Go to the documentation of this file.
1#include "UNIFACLibrary.h"
4
5namespace UNIFACLibrary {
6
7void UNIFACParameterLibrary::jsonize(std::string& s, nlohmann::json& d) {
8 d = cpjson::parse(s);
9}
10void UNIFACParameterLibrary::populate(const nlohmann::json& group_data, const nlohmann::json& interaction_data, const nlohmann::json& comp_data) {
11 if (CoolProp::get_config_bool(VTPR_ALWAYS_RELOAD_LIBRARY)) {
12 groups.clear();
13 interaction_parameters.clear();
14 components.clear();
15 }
16 // Callers are expected to validate against the UNIFAC schema before calling populate; the cpjson::get_* helpers below still throw CoolProp::ValueError on missing/mistyped fields as a safety net.
17 for (const auto& el : group_data) {
18 Group g;
19 g.sgi = cpjson::get_integer(el, "sgi");
20 g.mgi = cpjson::get_integer(el, "mgi");
21 g.R_k = cpjson::get_double(el, "R_k");
22 g.Q_k = cpjson::get_double(el, "Q_k");
23 groups.push_back(g);
24 }
25 for (const auto& el : interaction_data) {
27 ip.mgi1 = cpjson::get_integer(el, "mgi1");
28 ip.mgi2 = cpjson::get_integer(el, "mgi2");
29 ip.a_ij = cpjson::get_double(el, "a_ij");
30 ip.a_ji = cpjson::get_double(el, "a_ji");
31 ip.b_ij = cpjson::get_double(el, "b_ij");
32 ip.b_ji = cpjson::get_double(el, "b_ji");
33 ip.c_ij = cpjson::get_double(el, "c_ij");
34 ip.c_ji = cpjson::get_double(el, "c_ji");
35 interaction_parameters.push_back(ip);
36 }
37 for (const auto& el : comp_data) {
38 Component c;
39 c.inchikey = cpjson::get_string(el, "inchikey");
40 c.registry_number = cpjson::get_string(el, "registry_number");
41 c.name = cpjson::get_string(el, "name");
42 c.Tc = cpjson::get_double(el, "Tc");
43 c.pc = cpjson::get_double(el, "pc");
44 c.acentric = cpjson::get_double(el, "acentric");
45 c.molemass = cpjson::get_double(el, "molemass");
46 // userid is an optional user identifier
47 if (el.contains("userid")) {
48 c.userid = cpjson::get_string(el, "userid");
49 }
50 // If provided, store information about the alpha function in use
51 if (el.contains("alpha") && el.at("alpha").is_object()) {
52 const nlohmann::json& alpha = el.at("alpha");
53 c.alpha_type = cpjson::get_string(alpha, "type");
55 } else {
56 c.alpha_type = "default";
57 }
58 if (el.contains("alpha0") && el.at("alpha0").is_array()) {
60 }
61 const nlohmann::json& comp_groups = el.at("groups");
62 for (const auto& g : comp_groups) {
63 int count = cpjson::get_integer(g, "count");
64 int sgi = cpjson::get_integer(g, "sgi");
65 if (has_group(sgi)) {
66 ComponentGroup cg(count, get_group(sgi));
67 c.groups.push_back(cg);
68 }
69 }
70 components.push_back(c);
71 }
72}
73void UNIFACParameterLibrary::populate(std::string& group_data, std::string& interaction_data, std::string& decomp_data) {
74 nlohmann::json group_JSON, interaction_JSON, decomp_JSON;
75 jsonize(group_data, group_JSON);
76 jsonize(interaction_data, interaction_JSON);
77 jsonize(decomp_data, decomp_JSON);
78 populate(group_JSON, interaction_JSON, decomp_JSON);
79 m_populated = true;
80}
82 for (const auto& group : groups) {
83 if (group.sgi == sgi) {
84 return group;
85 }
86 }
87 throw CoolProp::ValueError("Could not find group");
88}
90 for (const auto& group : groups) {
91 if (group.sgi == sgi) {
92 return true;
93 }
94 }
95 return false;
96}
97
99
100 // If both mgi are the same, yield all zeros for the interaction parameters
101 if (mgi1 == mgi2) {
103 ip.mgi1 = mgi1;
104 ip.mgi2 = mgi2;
105 ip.zero_out();
106 return ip;
107 }
108 for (const auto& interaction_parameter : interaction_parameters) {
109 if (interaction_parameter.mgi1 == mgi1 && interaction_parameter.mgi2 == mgi2) {
110 // Correct order, return it
111 return interaction_parameter;
112 }
113 if (interaction_parameter.mgi2 == mgi1 && interaction_parameter.mgi1 == mgi2) {
114 // Backwards, swap the parameters
115 InteractionParameters ip = interaction_parameter;
116 ip.swap();
117 return ip;
118 }
119 }
120 throw CoolProp::ValueError(format("Could not find interaction between pair mgi[%d]-mgi[%d]", static_cast<int>(mgi1), static_cast<int>(mgi2)));
121}
122
123Component UNIFACParameterLibrary::get_component(const std::string& identifier, const std::string& value) const {
124 if (identifier == "name") {
125 for (const auto& component : components) {
126 if (component.name == value) {
127 return component;
128 }
129 }
130 }
131 throw CoolProp::ValueError(format("Could not find component: %s with identifier: %s", value.c_str(), identifier.c_str()));
132}
133
134}; /* namespace UNIFACLibrary */
135
136#if defined(ENABLE_CATCH)
137# include <catch2/catch_all.hpp>
138
139# include "UNIFAC.h"
140
141TEST_CASE("Check Poling example for UNIFAC", "[UNIFAC]") {
142 std::string acetone_pentane_groups =
143 "[{ \"Tc\": 508.1, \"acentric\": 0.3071, \"groups\": [ { \"count\": 1, \"sgi\": 1 }, {\"count\": 1, \"sgi\": 18 } ], \"molemass\": 0.44, "
144 "\"inchikey\": \"?????????????\", \"name\": \"Acetone\", \"pc\": 4700000.0, \"registry_number\": \"67-64-1\", \"userid\": \"\" }, { \"Tc\": "
145 "469.7000000000001, \"acentric\": 0.251, \"molemass\": 0.44, \"groups\": [ { \"count\": 2, \"sgi\": 1 }, { \"count\": 3, \"sgi\": 2 } ], "
146 "\"inchikey\": \"?????????????\", \"name\": \"n-Pentane\", \"pc\": 3370000.0, \"registry_number\": \"109-66-0\", \"userid\": \"\" } ]";
147 std::string groups = "[{\"Q_k\": 0.848, \"R_k\": 0.9011, \"maingroup_name\": \"CH2\", \"mgi\": 1, \"sgi\": 1, \"subgroup_name\": \"CH3\"},"
148 "{\"Q_k\": 0.540, \"R_k\": 0.6744, \"maingroup_name\": \"CH2\", \"mgi\": 1, \"sgi\": 2, \"subgroup_name\": \"CH2\"},"
149 "{\"Q_k\": 1.488, \"R_k\": 1.6724, \"maingroup_name\": \"CH2CO\", \"mgi\": 9, \"sgi\": 18, \"subgroup_name\": \"CH3CO\"}]";
150 std::string interactions = R"([{"a_ij": 476.4, "a_ji": 26.76, "b_ij": 0.0, "b_ji": 0.0, "c_ij": 0.0, "c_ji": 0.0, "mgi1": 1, "mgi2": 9}])";
151
152 SECTION("Validate AC for acetone + n-pentane") {
154 CHECK_NOTHROW(lib.populate(groups, interactions, acetone_pentane_groups));
155 UNIFAC::UNIFACMixture mix(lib, 1.0);
156 std::vector<std::string> names;
157 names.emplace_back("Acetone");
158 names.emplace_back("n-Pentane");
159 mix.set_components("name", names);
160 mix.set_interaction_parameters();
161
162 std::vector<double> z(2, 0.047);
163 z[1] = 1 - z[0];
164 mix.set_mole_fractions(z);
165 CHECK_NOTHROW(mix.set_temperature(307));
166
167 double lngammaR0 = mix.ln_gamma_R(1.0 / 307, 0, 0);
168 double lngammaR1 = mix.ln_gamma_R(1.0 / 307, 1, 0);
169 CAPTURE(lngammaR0);
170 CAPTURE(lngammaR1);
171 CHECK(std::abs(lngammaR0 - 1.66) < 1e-2);
172 CHECK(std::abs(lngammaR1 - 5.68e-3) < 1e-3);
173
174 std::vector<double> gamma(2);
175 mix.activity_coefficients(1.0 / 307, z, gamma);
176 CAPTURE(gamma[0]);
177 CAPTURE(gamma[1]);
178 CHECK(std::abs(gamma[0] - 4.99) < 1e-2);
179 CHECK(std::abs(gamma[1] - 1.005) < 1e-3);
180 };
181};
182
183#endif