teqp 0.23.1
Loading...
Searching...
No Matches
multifluid_reducing.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "teqp/types.hpp"
4
5namespace teqp {
6
7 namespace reducing {
8
9 inline auto get_BIPdep(const nlohmann::json& collection, const std::vector<std::string>& identifiers, const nlohmann::json& flags) {
10 if (!collection.is_array()){
11 throw teqp::InvalidArgument("collection provided to get_BIPdep must be an array");
12 }
13 if (collection.size() > 0 && !collection[0].is_object()){
14 throw teqp::InvalidArgument("entries in collection provided to get_BIPdep must be objects");
15 }
16
17 // If force-estimate is provided in flags, the estimation will over-ride the provided model(s)
18 if (flags.contains("force-estimate")) {
19 std::string scheme = flags.at("estimate");
20 if (scheme == "Lorentz-Berthelot") {
21 return std::make_tuple(nlohmann::json({
22 {"betaT", 1.0}, {"gammaT", 1.0}, {"betaV", 1.0}, {"gammaV", 1.0}, {"F", 0.0}
23 }), false);
24 }
25 else {
26 throw std::invalid_argument("estimation scheme is not understood:" + scheme);
27 }
28 }
29
30 // convert string to upper case
31 auto toupper = [](const std::string s) { auto data = s; std::for_each(data.begin(), data.end(), [](char& c) { c = ::toupper(c); }); return data; };
32
33 std::string comp0 = toupper(identifiers[0]);
34 std::string comp1 = toupper(identifiers[1]);
35 // O-th pass, check the hashes
36 for (auto& el : collection) {
37 if (!el.contains("hash1")){ continue; }
38 std::string name1 = toupper(el.at("hash1"));
39 std::string name2 = toupper(el.at("hash2"));
40 if (comp0 == name1 && comp1 == name2) {
41 return std::make_tuple(el, false);
42 }
43 if (comp0 == name2 && comp1 == name1) {
44 return std::make_tuple(el, true);
45 }
46 }
47 // First pass, check names
48 for (auto& el : collection) {
49 std::string name1 = toupper(el.at("Name1"));
50 std::string name2 = toupper(el.at("Name2"));
51 if (comp0 == name1 && comp1 == name2) {
52 return std::make_tuple(el, false);
53 }
54 if (comp0 == name2 && comp1 == name1) {
55 return std::make_tuple(el, true);
56 }
57 }
58 // Second pass, check CAS#
59 for (auto& el : collection) {
60 std::string CAS1 = el.at("CAS1");
61 std::string CAS2 = el.at("CAS2");
62 if (identifiers[0] == CAS1 && identifiers[1] == CAS2) {
63 return std::make_tuple(el, false);
64 }
65 if (identifiers[0] == CAS2 && identifiers[1] == CAS1) {
66 return std::make_tuple(el, true);
67 }
68 }
69
70 // If estimate is provided in flags, it will be the fallback solution for filling in interaction parameters
71 if (flags.contains("estimate")) {
72 std::string scheme = flags.at("estimate");
73 if (scheme == "Lorentz-Berthelot") {
74 return std::make_tuple(nlohmann::json({
75 {"betaT", 1.0}, {"gammaT", 1.0}, {"betaV", 1.0}, {"gammaV", 1.0}, {"F", 0.0}
76 }), false);
77 }
78 else {
79 throw std::invalid_argument("estimation scheme is not understood:" + scheme);
80 }
81 }
82 else {
83 throw std::invalid_argument("Can't match the binary pair for: " + identifiers[0] + "/" + identifiers[1]);
84 }
85 }
86
88 inline 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) {
89 auto [el, swap_needed] = get_BIPdep(collection, identifiers, flags);
90
91 double betaT, gammaT, betaV, gammaV;
92 if (el.contains("betaT") && el.contains("gammaT") && el.contains("betaV") && el.contains("gammaV")) {
93 betaT = el["betaT"]; gammaT = el["gammaT"]; betaV = el["betaV"]; gammaV = el["gammaV"];
94 // Backwards order of components, flip beta values
95 if (swap_needed) {
96 betaT = 1.0 / betaT;
97 betaV = 1.0 / betaV;
98 }
99 }
100 else if (el.contains("xi") && el.contains("zeta")) {
101 double xi = el["xi"], zeta = el["zeta"];
102 gammaT = 0.5 * (Tc[0] + Tc[1] + xi) / (2 * sqrt(Tc[0] * Tc[1]));
103 gammaV = 4.0 * (vc[0] + vc[1] + zeta) / (0.25 * pow(1 / pow(1 / vc[0], 1.0 / 3.0) + 1 / pow(1 / vc[1], 1.0 / 3.0), 3));
104 betaT = 1.0;
105 betaV = 1.0;
106 }
107 else {
108 throw std::invalid_argument("Could not understand what to do with this binary model specification: " + el.dump());
109 }
110 return std::make_tuple(betaT, gammaT, betaV, gammaV);
111 }
112
114 template <typename Tcvec, typename vcvec>
115 inline auto get_BIP_matrices(const nlohmann::json& collection, const std::vector<std::string>& components, const nlohmann::json& flags, const Tcvec& Tc, const vcvec& vc) {
116 Eigen::MatrixXd betaT, gammaT, betaV, gammaV, YT, Yv;
117 auto N = components.size();
118 betaT.resize(N, N); betaT.setZero();
119 gammaT.resize(N, N); gammaT.setZero();
120 betaV.resize(N, N); betaV.setZero();
121 gammaV.resize(N, N); gammaV.setZero();
122 for (auto i = 0U; i < N; ++i) {
123 for (auto j = i + 1; j < N; ++j) {
124 auto [betaT_, gammaT_, betaV_, gammaV_] = get_binary_interaction_double(collection, { components[i], components[j] }, flags, { Tc[i], Tc[j] }, { vc[i], vc[j] });
125 betaT(i, j) = betaT_; betaT(j, i) = 1.0 / betaT(i, j);
126 gammaT(i, j) = gammaT_; gammaT(j, i) = gammaT(i, j);
127 betaV(i, j) = betaV_; betaV(j, i) = 1.0 / betaV(i, j);
128 gammaV(i, j) = gammaV_; gammaV(j, i) = gammaV(i, j);
129 }
130 }
131 return std::make_tuple(betaT, gammaT, betaV, gammaV);
132 }
133
138 inline auto get_Tcvc(const std::vector<nlohmann::json>& pureJSON) {
139 Eigen::ArrayXd Tc(pureJSON.size()), vc(pureJSON.size());
140 auto i = 0;
141 for (auto& j : pureJSON) {
142 auto red = j.at("EOS")[0].at("STATES").at("reducing");
143 double Tc_ = red.at("T");
144 double rhoc_ = red.at("rhomolar");
145 Tc[i] = Tc_;
146 vc[i] = 1.0 / rhoc_;
147 i++;
148 }
149 return std::make_tuple(Tc, vc);
150 }
151
153 inline auto get_F_matrix(const nlohmann::json& collection, const std::vector<std::string>& identifiers, const nlohmann::json& flags) {
154 auto N = identifiers.size();
155 Eigen::MatrixXd F(N, N);
156 for (auto i = 0U; i < N; ++i) {
157 F(i, i) = 0.0;
158 for (auto j = i + 1; j < N; ++j) {
159 auto [el, swap_needed] = get_BIPdep(collection, { identifiers[i], identifiers[j] }, flags);
160 if (el.empty()) {
161 F(i, j) = 0.0;
162 F(j, i) = 0.0;
163 }
164 else {
165 F(i, j) = el.at("F");
166 F(j, i) = el.at("F");
167 }
168 }
169 }
170 return F;
171 }
172 }
173
175 private:
176 Eigen::MatrixXd YT, Yv;
177
178 public:
179 const Eigen::MatrixXd betaT, gammaT, betaV, gammaV;
180 const Eigen::ArrayXd Tc, vc;
181
182 template<typename ArrayLike>
184 const Eigen::MatrixXd& betaT, const Eigen::MatrixXd& gammaT,
185 const Eigen::MatrixXd& betaV, const Eigen::MatrixXd& gammaV,
186 const ArrayLike& Tc, const ArrayLike& vc)
188
189 auto N = Tc.size();
190
191 YT.resize(N, N); YT.setZero();
192 Yv.resize(N, N); Yv.setZero();
193 for (auto i = 0; i < N; ++i) {
194 for (auto j = i + 1; j < N; ++j) {
195 YT(i, j) = 2.0 * betaT(i, j) * gammaT(i, j) * sqrt(Tc[i] * Tc[j]);
196 YT(j, i) = 2.0 * betaT(j, i) * gammaT(j, i) * sqrt(Tc[i] * Tc[j]);
197 Yv(i, j) = 2.0 * 1.0 / 8.0 * betaV(i, j) * gammaV(i, j) * pow3(cbrt(vc[i]) + cbrt(vc[j]));
198 Yv(j, i) = 2.0 * 1.0 / 8.0 * betaV(j, i) * gammaV(j, i) * pow3(cbrt(vc[i]) + cbrt(vc[j]));
199 }
200 }
201 }
202
203 template <typename MoleFractions>
204 auto Y(const MoleFractions& z, const Eigen::ArrayXd& Yc, const Eigen::MatrixXd& beta, const Eigen::MatrixXd& Yij) const {
205
206 auto N = z.size();
207 if (N != Yc.size()){
208 throw teqp::InvalidArgument("Length of fractions of " + std::to_string(N) + " does not equal # of components of " + std::to_string(Yc.size()));
209 }
210 typename MoleFractions::value_type sum1 = 0.0;
211 for (auto i = 0U; i < N; ++i) {
212 sum1 = sum1 + pow2(z[i]) * Yc[i];
213 }
214
215 typename MoleFractions::value_type sum2 = 0.0;
216 for (auto i = 0U; i < N - 1; ++i) {
217 for (auto j = i + 1; j < N; ++j) {
218 auto den = beta(i, j)*beta(i, j) * z[i] + z[j];
219 if (getbaseval(den) != 0){
220 sum2 = sum2 + z[i] * z[j] * (z[i] + z[j]) / den * Yij(i, j);
221 }
222 else{
223 // constexpr check to abort if trying to do second derivatives
224 // and at least two compositions are zero. This should incur
225 // zero runtime overhead. First derivatives are ok.
227 using namespace autodiff::detail;
228 constexpr auto isDual_ = isDual<typename MoleFractions::Scalar>;
229 constexpr auto order = NumberTraits<typename MoleFractions::Scalar>::Order;
230 if constexpr (isDual_ && order > 1){
231 throw teqp::InvalidArgument("The multifluid reducing term of GERG does not permit more than one zero composition when taking second composition derivatives with autodiff");
232 }
233 }
234 double beta2 = beta(i,j)*beta(i,j);
235 sum2 = sum2 + Yij(i, j)*(
236 z[i]*z[j] + z[i]*z[i]*(1.0-beta2)
237 );
238 }
239 }
240 }
241
242 return forceeval(sum1 + sum2);
243 }
244
245 template<typename MoleFractions> auto get_Tr(const MoleFractions& molefracs) const { return Y(molefracs, Tc, betaT, YT); }
246 template<typename MoleFractions> auto get_rhor(const MoleFractions& molefracs) const { return 1.0 / Y(molefracs, vc, betaV, Yv); }
247
248 const auto& get_mat(const std::string& key) const {
249 if (key == "betaT"){ return betaT; }
250 if (key == "gammaT"){ return gammaT; }
251 if (key == "betaV"){ return betaV; }
252 if (key == "gammaV"){ return gammaV; }
253 throw std::invalid_argument("variable is not understood: " + key);
254 }
255 auto get_BIP(const std::size_t& i, const std::size_t& j, const std::string& key) const {
256 const auto& mat = get_mat(key);
257 if (i < static_cast<std::size_t>(mat.rows()) && j < static_cast<std::size_t>(mat.cols())){
258 return mat(i,j);
259 }
260 else{
261 throw std::invalid_argument("Indices are out of bounds");
262 }
263 }
264
265 };
266
268 private:
269 Eigen::MatrixXd YT, Yv;
270
271 public:
272 const Eigen::MatrixXd phiT, lambdaT, phiV, lambdaV;
273 const Eigen::ArrayXd Tc, vc;
274
275 template<typename ArrayLike>
277 const Eigen::MatrixXd& phiT, const Eigen::MatrixXd& lambdaT,
278 const Eigen::MatrixXd& phiV, const Eigen::MatrixXd& lambdaV,
279 const ArrayLike& Tc, const ArrayLike& vc)
281
282 auto N = Tc.size();
283
284 YT.resize(N, N); YT.setZero();
285 Yv.resize(N, N); Yv.setZero();
286 for (auto i = 0; i < N; ++i) {
287 for (auto j = 0; j < N; ++j) {
288 YT(i, j) = sqrt(Tc[i] * Tc[j]);
289 YT(j, i) = sqrt(Tc[i] * Tc[j]);
290 Yv(i, j) = 1.0 / 8.0 * pow3(cbrt(vc[i]) + cbrt(vc[j]));
291 Yv(j, i) = 1.0 / 8.0 * pow3(cbrt(vc[i]) + cbrt(vc[j]));
292 }
293 }
294 }
295
296 template <typename MoleFractions>
297 auto Y(const MoleFractions& z, const Eigen::MatrixXd& phi, const Eigen::MatrixXd& lambda, const Eigen::MatrixXd& Yij) const {
298 auto N = z.size();
299 typename MoleFractions::value_type sum = 0.0;
300 for (auto i = 0U; i < N; ++i) {
301 typename MoleFractions::value_type sumj1 = 0.0, sumj2 = 0.0;
302 for (auto j = 0U; j < N; ++j) {
303 sumj1 += z[j] * phi(i, j) * Yij(i, j);
304 sumj2 += z[j] * cbrt(lambda(i,j)) * cbrt(Yij(i,j));
305 }
306 sum += z[i]*(sumj1 + sumj2*sumj2*sumj2);
307 }
308 return sum;
309 }
310 template<typename MoleFractions> auto get_Tr(const MoleFractions& molefracs) const { return Y(molefracs, phiT, lambdaT, YT); }
311 template<typename MoleFractions> auto get_rhor(const MoleFractions& molefracs) const { return 1.0 / Y(molefracs, phiV, lambdaV, Yv); }
312
313 const auto& get_mat(const std::string& key) const {
314 if (key == "phiT"){ return phiT; }
315 if (key == "lambdaT"){ return lambdaT; }
316 if (key == "phiV"){ return phiV; }
317 if (key == "lambdaV"){ return lambdaV; }
318 throw std::invalid_argument("variable is not understood: " + key);
319 }
320 auto get_BIP(const std::size_t& i, const std::size_t& j, const std::string& key) const {
321 const auto& mat = get_mat(key);
322 if (i < static_cast<std::size_t>(mat.rows()) && j < static_cast<std::size_t>(mat.cols())){
323 return mat(i,j);
324 }
325 else{
326 throw std::invalid_argument("Indices are out of bounds");
327 }
328 }
329 };
330
331
332 template<typename... Args>
334 private:
335 const std::variant<Args...> term;
336 auto get_Tc() const { return std::visit([](const auto& t) { return std::cref(t.Tc); }, term); }
337 auto get_vc() const { return std::visit([](const auto& t) { return std::cref(t.vc); }, term); }
338 public:
339 const Eigen::ArrayXd Tc, vc;
340
341 template<typename Instance>
342 ReducingTermContainer(const Instance& instance) : term(instance), Tc(get_Tc()), vc(get_vc()) {}
343
344 template <typename MoleFractions>
345 auto get_Tr(const MoleFractions& molefracs) const {
346 return std::visit([&](auto& t) { return t.get_Tr(molefracs); }, term);
347 }
348
349 template <typename MoleFractions>
350 auto get_rhor(const MoleFractions& molefracs) const {
351 return std::visit([&](auto& t) { return t.get_rhor(molefracs); }, term);
352 }
353
354 auto get_BIP(const std::size_t& i, const std::size_t& j, const std::string& key) const {
355 return std::visit([&](auto& t) { return t.get_BIP(i, j, key); }, term);
356 }
357 };
358
360
361}; // namespace teqp
MultiFluidInvariantReducingFunction(const Eigen::MatrixXd &phiT, const Eigen::MatrixXd &lambdaT, const Eigen::MatrixXd &phiV, const Eigen::MatrixXd &lambdaV, const ArrayLike &Tc, const ArrayLike &vc)
auto get_rhor(const MoleFractions &molefracs) const
auto get_Tr(const MoleFractions &molefracs) const
auto get_BIP(const std::size_t &i, const std::size_t &j, const std::string &key) const
const auto & get_mat(const std::string &key) const
auto Y(const MoleFractions &z, const Eigen::MatrixXd &phi, const Eigen::MatrixXd &lambda, const Eigen::MatrixXd &Yij) const
As implemented in Table 7.18 from GERG-2004.
auto get_Tr(const MoleFractions &molefracs) const
auto get_BIP(const std::size_t &i, const std::size_t &j, const std::string &key) const
auto Y(const MoleFractions &z, const Eigen::ArrayXd &Yc, const Eigen::MatrixXd &beta, const Eigen::MatrixXd &Yij) const
auto get_rhor(const MoleFractions &molefracs) const
MultiFluidReducingFunction(const Eigen::MatrixXd &betaT, const Eigen::MatrixXd &gammaT, const Eigen::MatrixXd &betaV, const Eigen::MatrixXd &gammaV, const ArrayLike &Tc, const ArrayLike &vc)
const auto & get_mat(const std::string &key) const
auto get_rhor(const MoleFractions &molefracs) const
ReducingTermContainer(const Instance &instance)
auto get_Tr(const MoleFractions &molefracs) const
auto get_BIP(const std::size_t &i, const std::size_t &j, const std::string &key) const
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)
Get the binary interaction parameters for a given binary pair.
auto get_Tcvc(const std::vector< nlohmann::json > &pureJSON)
auto get_F_matrix(const nlohmann::json &collection, const std::vector< std::string > &identifiers, const nlohmann::json &flags)
Get the matrix F of Fij factors multiplying the departure functions.
auto get_BIPdep(const nlohmann::json &collection, const std::vector< std::string > &identifiers, const nlohmann::json &flags)
auto get_BIP_matrices(const nlohmann::json &collection, const std::vector< std::string > &components, const nlohmann::json &flags, const Tcvec &Tc, const vcvec &vc)
Build the matrices of betaT, gammaT, betaV, gammaV for the multi-fluid model.
T pow3(const T &x)
Definition types.hpp:8
auto getbaseval(const T &expr)
Definition types.hpp:90
T pow2(const T &x)
Definition types.hpp:7
ReducingTermContainer< MultiFluidReducingFunction, MultiFluidInvariantReducingFunction > ReducingFunctions
auto pow(const double &x, const double &e)
Definition types.hpp:195
auto forceeval(T &&expr)
Definition types.hpp:52