teqp 0.23.1
Loading...
Searching...
No Matches
multifluid.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "nlohmann/json.hpp"
4
5#include <Eigen/Dense>
6#include <string>
7#include <cmath>
8#include <optional>
9#include <variant>
10
11#include "teqp/types.hpp"
12#include "teqp/constants.hpp"
13#include "teqp/filesystem.hpp"
14#include "teqp/json_tools.hpp"
15#include "teqp/exceptions.hpp"
16
17#include "RPinterop/interop.hpp"
18
19#if defined(TEQP_MULTICOMPLEX_ENABLED)
20#include "MultiComplex/MultiComplex.hpp"
21#endif
22
26
27#include <boost/algorithm/string/join.hpp>
28
29#if defined(TEQP_MULTICOMPLEX_ENABLED)
30// See https://eigen.tuxfamily.org/dox/TopicCustomizing_CustomScalar.html
31namespace Eigen {
32 template<typename TN> struct NumTraits<mcx::MultiComplex<TN>> : NumTraits<double> // permits to get the epsilon, dummy_precision, lowest, highest functions
33 {
34 enum {
35 IsComplex = 1,
36 IsInteger = 0,
37 IsSigned = 1,
38 RequireInitialization = 1,
39 ReadCost = 1,
40 AddCost = 3,
41 MulCost = 3
42 };
43 };
44}
45#endif
46
47namespace teqp{
48
49template<typename EOSCollection>
51
52private:
53 const EOSCollection EOSs;
54public:
55 CorrespondingStatesContribution(EOSCollection&& EOSs) : EOSs(EOSs) {};
56
57 auto size() const { return EOSs.size(); }
58
59 template<typename TauType, typename DeltaType, typename MoleFractions>
60 auto alphar(const TauType& tau, const DeltaType& delta, const MoleFractions& molefracs) const {
61 using resulttype = std::common_type_t<decltype(tau), decltype(molefracs[0]), decltype(delta)>; // Type promotion, without the const-ness
62 resulttype alphar = 0.0;
63 auto N = molefracs.size();
64 for (auto i = 0U; i < N; ++i) {
65 alphar += molefracs[i] * EOSs[i].alphar(tau, delta);
66 }
67 return alphar;
68 }
69
70 template<typename TauType, typename DeltaType>
71 auto alphari(const TauType& tau, const DeltaType& delta, std::size_t i) const {
72 return EOSs[i].alphar(tau, delta);
73 }
74
75 auto get_EOS(std::size_t i) const{
76 return EOSs[i];
77 }
78};
79
80template<typename FCollection, typename DepartureFunctionCollection>
82
83private:
84 const FCollection F;
85 const DepartureFunctionCollection funcs;
86public:
87 DepartureContribution(FCollection&& F, DepartureFunctionCollection&& funcs) : F(F), funcs(funcs) {};
88
89 const auto& get_F() const { return F; }
90
91 template<typename TauType, typename DeltaType, typename MoleFractions>
92 auto alphar(const TauType& tau, const DeltaType& delta, const MoleFractions& molefracs) const {
93 using resulttype = std::decay_t<std::common_type_t<decltype(tau), decltype(molefracs[0]), decltype(delta)>>; // Type promotion, without the const-ness
94 resulttype alphar = 0.0;
95 std::size_t N = molefracs.size();
96 for (auto i = 0U; i < N; ++i) {
97 for (auto j = i+1; j < N; ++j) {
98 alphar += molefracs[i] * molefracs[j] * F(i, j) * funcs[i][j].alphar(tau, delta);
99 }
100 }
101 return alphar;
102 }
103
105 template<typename TauType, typename DeltaType>
106 auto get_alpharij(const std::size_t i, const std::size_t j, const TauType& tau, const DeltaType& delta) const {
107 std::size_t N = funcs.size();
108 if (i < 0 || j < 0){
109 throw teqp::InvalidArgument("i or j is negative");
110 }
111 if (i >= N || j >= N){
112 throw teqp::InvalidArgument("i or j is invalid; size is " + std::to_string(N));
113 }
114 return forceeval(funcs[i][j].alphar(tau, delta));
115 }
116};
117
118template<typename CorrespondingTerm, typename DepartureTerm>
120
121private:
122 std::string meta = "";
123public:
125 const CorrespondingTerm corr;
126 const DepartureTerm dep;
129
130 template<class VecType>
131 auto R(const VecType& molefracs) const {
132 return std::visit([&molefracs](const auto& el){ return el.get_R(molefracs); }, Rcalc);
133 }
134
136 void set_meta(const std::string& m) { meta = m; }
138 auto get_meta() const { return meta; }
140 const std::variant<double, std::string> get_BIP(const std::size_t &i, const std::size_t &j, const std::string& key) const{
141 if (key == "F" || key == "Fij"){
142 auto F = dep.get_F();
143 if (0 <= i && i < F.rows() && 0 <= j && j < F.cols()){
144 return F(i,j);
145 }
146 }
147 return redfunc.get_BIP(i, j, key);
148 }
149
151
152 template<typename TType, typename RhoType>
153 auto alphar(TType T,
154 const RhoType& rhovec,
155 const std::optional<typename RhoType::value_type> rhotot = std::nullopt) const
156 {
157 typename RhoType::value_type rhotot_ = (rhotot.has_value()) ? rhotot.value() : std::accumulate(std::begin(rhovec), std::end(rhovec), (decltype(rhovec[0]))0.0);
158 auto molefrac = rhovec / rhotot_;
159 return alphar(T, rhotot_, molefrac);
160 }
161
162 template<typename TType, typename RhoType, typename MoleFracType>
163 auto alphar(const TType &T,
164 const RhoType &rho,
165 const MoleFracType& molefrac) const
166 {
167 if (static_cast<std::size_t>(molefrac.size()) != corr.size()){
168 throw teqp::InvalidArgument("Wrong size of mole fractions; "+std::to_string(corr.size()) + " are loaded but "+std::to_string(molefrac.size()) + " were provided");
169 }
170 auto delta = forceeval(rho / redfunc.get_rhor(molefrac));
171 auto tau = forceeval(redfunc.get_Tr(molefrac) / T);
172 if (molefrac.size() == 1){
173 // Short circuit for pure fluids and avoid mole fractions and departure terms
174 return corr.alphari(tau, delta, 0);
175 }
176 return forceeval(corr.alphar(tau, delta, molefrac) + dep.alphar(tau, delta, molefrac));
177 }
178
179 template<typename TType, typename RhoType, typename MoleFracType>
180 auto alphar_taudelta(const TType &tau,
181 const RhoType &delta,
182 const MoleFracType& molefrac) const
183 {
184 if (static_cast<std::size_t>(molefrac.size()) != corr.size()){
185 throw teqp::InvalidArgument("Wrong size of mole fractions; "+std::to_string(corr.size()) + " are loaded but "+std::to_string(molefrac.size()) + " were provided");
186 }
187 if (molefrac.size() == 1){
188 return corr.alphari(tau, delta, 0U);
189 }
190 return forceeval(corr.alphar(tau, delta, molefrac) + dep.alphar(tau, delta, molefrac));
191 }
192
193 template<typename TType, typename RhoType>
194 inline auto alphar_taudeltai(const TType &tau, const RhoType &delta, const std::size_t i) const
195 {
196 return corr.alphari(tau, delta, i);
197 }
198
199 template<typename MoleFracType>
200 auto get_reducing_temperature(const MoleFracType& molefrac) const {
201 return forceeval(redfunc.get_Tr(molefrac));
202 }
203
204 template<typename MoleFracType>
205 auto get_reducing_density(const MoleFracType& molefrac) const {
206 return forceeval(redfunc.get_rhor(molefrac));
207 }
208};
209
210
211/***
212* \brief Get the JSON data structure for a given departure function
213* \param name The name (or alias) of the departure function to be looked up
214* \parm path The root path to the fluid data, or alternatively, the path to the json file directly
215*/
216inline auto get_departure_json(const std::string& name, const std::string& path) {
217 std::string filepath = std::filesystem::is_regular_file(path) ? path : path + "/dev/mixtures/mixture_departure_functions.json";
218 nlohmann::json j = load_a_JSON_file(filepath);
219 std::string js = j.dump(2);
220 // First pass, direct name lookup
221 for (auto& el : j) {
222 if (el.at("Name") == name) {
223 return el;
224 }
225 }
226 // Second pass, iterate over aliases
227 for (auto& el : j) {
228 for (auto &alias : el.at("aliases")) {
229 if (alias == name) {
230 return el;
231 }
232 }
233 }
234 throw std::invalid_argument("Could not match the name: " + name + "when looking up departure function");
235}
236
237inline auto build_departure_function(const nlohmann::json& j) {
238 auto build_power = [&](auto term, auto& dep) {
239 std::size_t N = term["n"].size();
240
241 // Don't add a departure function if there are no coefficients provided
242 if (N == 0) {
243 return;
244 }
245
247
248 auto eigorzero = [&term, &N](const std::string& name) -> Eigen::ArrayXd {
249 if (!term[name].empty()) {
250 return toeig(term[name]);
251 }
252 else {
253 return Eigen::ArrayXd::Zero(N);
254 }
255 };
256
257
258 eos.n = eigorzero("n");
259 eos.t = eigorzero("t");
260 eos.d = eigorzero("d");
261
262 Eigen::ArrayXd c(N), l(N); c.setZero();
263
264 int Nlzero = 0, Nlnonzero = 0;
265 bool contiguous_lzero = false;
266
267 if (term["l"].empty()) {
268 // exponential part not included
269 l.setZero();
270 if (!all_same_length(term, { "n","t","d" })) {
271 throw std::invalid_argument("Lengths are not all identical in polynomial-like term");
272 }
273 }
274 else {
275 if (!all_same_length(term, { "n","t","d","l"})) {
276 throw std::invalid_argument("Lengths are not all identical in exponential term");
277 }
278 l = toeig(term["l"]);
279 if (term.contains("c")){
280 c = eigorzero("c");
281 if (!all_same_length(term, { "n","t","d","l","c"})) {
282 throw std::invalid_argument("Lengths are not all identical in exponential term");
283 }
284 }
285 else{
286 // l is included, use it to build c; c_i = 1 if l_i > 0, zero otherwise
287 c = (l > 0).cast<double>();
288 }
289
290 // See how many of the first entries have zero values for l_i
291 contiguous_lzero = (l[0] == 0);
292 for (auto i = 0; i < c.size(); ++i) {
293 if (l[i] == 0) {
294 Nlzero++;
295 }
296 }
297 }
298 Nlnonzero = static_cast<int>(l.size()) - Nlzero;
299
300 if (contiguous_lzero && (l.tail(Nlnonzero) == 0).any()) {
301 throw std::invalid_argument("If l_i has zero and non-zero values, the zero values need to come first");
302 }
303
304 eos.c = c;
305 eos.l = l;
306
307 eos.l_i = eos.l.cast<int>();
308
309 if (Nlzero + Nlnonzero != l.size()) {
310 throw std::invalid_argument("Somehow the l lengths don't add up");
311 }
312
313
314 if (((eos.l_i.cast<double>() - eos.l).cwiseAbs() > 0.0).any()) {
315 throw std::invalid_argument("Non-integer entry in l found");
316 }
317
318 // If a contiguous portion of the terms have values of l_i that are zero
319 // it is computationally advantageous to break up the evaluation into
320 // part that has just the n_i*tau^t_i*delta^d_i and the part with the
321 // exponential term exp(-delta^l_i)
322 if (l.sum() == 0) {
323 // No l term at all, just polynomial
324 JustPowerEOSTerm poly;
325 poly.n = eos.n;
326 poly.t = eos.t;
327 poly.d = eos.d;
328 dep.add_term(poly);
329 }
330 else if (l.sum() > 0 && contiguous_lzero){
331 JustPowerEOSTerm poly;
332 poly.n = eos.n.head(Nlzero);
333 poly.t = eos.t.head(Nlzero);
334 poly.d = eos.d.head(Nlzero);
335 dep.add_term(poly);
336
338 e.n = eos.n.tail(Nlnonzero);
339 e.t = eos.t.tail(Nlnonzero);
340 e.d = eos.d.tail(Nlnonzero);
341 e.c = eos.c.tail(Nlnonzero);
342 e.l = eos.l.tail(Nlnonzero);
343 e.l_i = eos.l_i.tail(Nlnonzero);
344 dep.add_term(PowerEOSTerm(e));
345 }
346 else {
347 // Don't try to get too clever, just add the departure term
348 dep.add_term(eos);
349 }
350 };
351
352 auto build_doubleexponential = [&](auto& term, auto& dep) {
353 if (!all_same_length(term, { "n","t","d","ld","gd","lt","gt" })) {
354 throw std::invalid_argument("Lengths are not all identical in double exponential term");
355 }
357 eos.n = toeig(term.at("n"));
358 eos.t = toeig(term.at("t"));
359 eos.d = toeig(term.at("d"));
360 eos.ld = toeig(term.at("ld"));
361 eos.gd = toeig(term.at("gd"));
362 eos.lt = toeig(term.at("lt"));
363 eos.gt = toeig(term.at("gt"));
364 eos.ld_i = eos.ld.cast<int>();
365 dep.add_term(eos);
366 };
367 auto build_Chebyshev2D = [&](auto& term, auto& dep) {
369 int Ntau = term.at("Ntau"); // Degree in tau (there will be Ntau+1 coefficients in the tau direction)
370 int Ndelta = term.at("Ndelta"); // Degree in delta (there will be Ndelta+1 coefficients in the delta direction)
371 Eigen::ArrayXd c = toeig(term.at("a"));
372 if ((Ntau + 1)*(Ndelta + 1) != c.size()){
373 throw std::invalid_argument("Provided length [" + std::to_string(c.size()) + "] is not equal to (Ntau+1)*(Ndelta+1)");
374 }
375 eos.a = c.reshaped(Ntau+1, Ndelta+1).eval(); // All in one long array, then reshaped
376 eos.taumin = term.at("taumin");
377 eos.taumax = term.at("taumax");
378 eos.deltamin = term.at("deltamin");
379 eos.deltamax = term.at("deltamax");
380 dep.add_term(eos);
381 };
382 //auto build_gaussian = [&](auto& term) {
383 // GaussianEOSTerm eos;
384 // eos.n = toeig(term["n"]);
385 // eos.t = toeig(term["t"]);
386 // eos.d = toeig(term["d"]);
387 // eos.eta = toeig(term["eta"]);
388 // eos.beta = toeig(term["beta"]);
389 // eos.gamma = toeig(term["gamma"]);
390 // eos.epsilon = toeig(term["epsilon"]);
391 // if (!all_same_length(term, { "n","t","d","eta","beta","gamma","epsilon" })) {
392 // throw std::invalid_argument("Lengths are not all identical in Gaussian term");
393 // }
394 // return eos;
395 //};
396 auto build_GERG2004 = [&](const auto& term, auto& dep) {
397 if (!all_same_length(term, { "n","t","d","eta","beta","gamma","epsilon" })) {
398 throw std::invalid_argument("Lengths are not all identical in GERG term");
399 }
400 int Npower = term["Npower"];
401 auto NGERG = static_cast<int>(term["n"].size()) - Npower;
402
404 eos.n = toeig(term["n"]).head(Npower);
405 eos.t = toeig(term["t"]).head(Npower);
406 eos.d = toeig(term["d"]).head(Npower);
407 if (term.contains("l")) {
408 eos.l = toeig(term["l"]).head(Npower);
409 }
410 else {
411 eos.l = 0.0 * eos.n;
412 }
413 eos.c = (eos.l > 0).cast<int>().cast<double>();
414 eos.l_i = eos.l.cast<int>();
415 dep.add_term(PowerEOSTerm(eos));
416
418 e.n = toeig(term["n"]).tail(NGERG);
419 e.t = toeig(term["t"]).tail(NGERG);
420 e.d = toeig(term["d"]).tail(NGERG);
421 e.eta = toeig(term["eta"]).tail(NGERG);
422 e.beta = toeig(term["beta"]).tail(NGERG);
423 e.gamma = toeig(term["gamma"]).tail(NGERG);
424 e.epsilon = toeig(term["epsilon"]).tail(NGERG);
425 dep.add_term(e);
426 };
427 auto build_GaussianExponential = [&](const auto& term, auto& dep) {
428 if (!all_same_length(term, { "n","t","d","eta","beta","gamma","epsilon" })) {
429 throw std::invalid_argument("Lengths are not all identical in Gaussian+Exponential term");
430 }
431 int Npower = term["Npower"];
432 auto NGauss = static_cast<int>(term["n"].size()) - Npower;
433
435 eos.n = toeig(term["n"]).head(Npower);
436 eos.t = toeig(term["t"]).head(Npower);
437 eos.d = toeig(term["d"]).head(Npower);
438 if (term.contains("l")) {
439 eos.l = toeig(term["l"]).head(Npower);
440 }
441 else {
442 eos.l = 0.0 * eos.n;
443 }
444 eos.c = (eos.l > 0).cast<int>().cast<double>();
445 eos.l_i = eos.l.cast<int>();
446 dep.add_term(PowerEOSTerm(eos));
447
449 e.n = toeig(term["n"]).tail(NGauss);
450 e.t = toeig(term["t"]).tail(NGauss);
451 e.d = toeig(term["d"]).tail(NGauss);
452 e.eta = toeig(term["eta"]).tail(NGauss);
453 e.beta = toeig(term["beta"]).tail(NGauss);
454 e.gamma = toeig(term["gamma"]).tail(NGauss);
455 e.epsilon = toeig(term["epsilon"]).tail(NGauss);
456 dep.add_term(e);
457 };
458
459 std::string type = j.at("type");
460 DepartureTerms dep;
461 if (type == "Exponential") {
462 build_power(j, dep);
463 }
464 else if (type == "DoubleExponential") {
465 build_doubleexponential(j, dep);
466 }
467 else if (type == "GERG-2004" || type == "GERG-2008") {
468 build_GERG2004(j, dep);
469 }
470 else if (type == "Gaussian+Exponential") {
471 build_GaussianExponential(j, dep);
472 }
473 else if (type == "Chebyshev2D") {
474 build_Chebyshev2D(j, dep);
475 }
476 else if (type == "none") {
477 dep.add_term(NullEOSTerm());
478 }
479 else {
480
481 std::vector<std::string> options = { "Exponential","GERG-2004","GERG-2008","Gaussian+Exponential", "none", "DoubleExponential","Chebyshev2D"};
482 throw std::invalid_argument("Bad departure term type: " + type + ". Options are {" + boost::algorithm::join(options, ",") + "}");
483 }
484 return dep;
485}
486
487inline auto get_departure_function_matrix(const nlohmann::json& depcollection, const nlohmann::json& BIPcollection, const std::vector<std::string>& components, const nlohmann::json& flags) {
488
489 // Allocate the matrix with default models
490 std::vector<std::vector<DepartureTerms>> funcs(components.size()); for (auto i = 0U; i < funcs.size(); ++i) { funcs[i].resize(funcs.size()); }
491
492 // Load the collection of data on departure functions
493
494 auto get_departure_json = [&depcollection](const std::string& Name) {
495 for (auto& el : depcollection) {
496 if (el["Name"] == Name) { return el; }
497 }
498 throw std::invalid_argument("Bad departure function name: "+Name);
499 };
500
501 auto funcsmeta = nlohmann::json::object();
502
503 for (auto i = 0U; i < funcs.size(); ++i) {
504 std::string istr = std::to_string(i);
505 if (funcsmeta.contains(istr)) { funcsmeta[istr] = {}; }
506 for (auto j = i + 1; j < funcs.size(); ++j) {
507 std::string jstr = std::to_string(j);
508 auto [BIP, swap_needed] = reducing::get_BIPdep(BIPcollection, { components[i], components[j] }, flags);
509 std::string funcname = BIP.contains("function") ? BIP["function"] : "";
510 nlohmann::json jj;
511 if (!funcname.empty()) {
512 if (depcollection.empty()){
513 throw teqp::InvalidArgument("No departure functions were loaded, unable to select requested function: " + funcname);
514 }
515 jj = get_departure_json(funcname);
516 funcs[i][j] = build_departure_function(jj);
517 funcs[j][i] = build_departure_function(jj);
518 }
519 else {
520 funcs[i][j].add_term(NullEOSTerm());
521 funcs[j][i].add_term(NullEOSTerm());
522 }
523 funcsmeta[istr][jstr] = { {"departure", jj}, {"BIP", BIP} };
524 funcsmeta[istr][jstr]["BIP"]["swap_needed"] = swap_needed;
525 }
526 }
527 return std::make_tuple(funcs, funcsmeta);
528}
529
530inline auto get_EOS_terms(const nlohmann::json& j)
531{
532 auto alphar = j["EOS"][0]["alphar"];
533
534 if (alphar.empty()){
535 throw teqp::InvalidArgument("alphar array cannot be empty");
536 }
537
538 // First check whether term type is allowed
539 const std::vector<std::string> allowed_types = { "ResidualHelmholtzPower", "ResidualHelmholtzGaussian", "ResidualHelmholtzNonAnalytic","ResidualHelmholtzGaoB", "ResidualHelmholtzLemmon2005", "ResidualHelmholtzExponential", "ResidualHelmholtzDoubleExponential","ResidualHelmholtzGenericCubic","ResidualHelmholtzPCSAFTGrossSadowski2001" };
540
541 auto isallowed = [&](const auto& conventional_types, const std::string& name) {
542 for (auto& a : conventional_types) { if (name == a) { return true; }; } return false;
543 };
544
545 for (auto& term : alphar) {
546 std::string type = term["type"];
547 if (!isallowed(allowed_types, type)) {
548 std::string a = allowed_types[0]; for (auto i = 1U; i < allowed_types.size(); ++i) { a += "," + allowed_types[i]; }
549 throw std::invalid_argument("Bad type:" + type + "; allowed types are: {" + a + "}");
550 }
551 }
552
553 EOSTerms container;
554
555 auto build_power = [&](auto term, auto & container) {
556 std::size_t N = term["n"].size();
557
559
560 auto eigorzero = [&term, &N](const std::string& name) -> Eigen::ArrayXd {
561 if (!term[name].empty()) {
562 return toeig(term[name]);
563 }
564 else {
565 return Eigen::ArrayXd::Zero(N);
566 }
567 };
568
569
570 eos.n = eigorzero("n");
571 eos.t = eigorzero("t");
572 eos.d = eigorzero("d");
573
574 Eigen::ArrayXd c(N), l(N); c.setZero();
575 int Nlzero = 0, Nlnonzero = 0;
576 bool contiguous_lzero;
577 if (term["l"].empty()) {
578 // exponential part not included
579 l.setZero();
580 }
581 else {
582 l = toeig(term["l"]);
583 // l is included, use it to build c; c_i = 1 if l_i > 0, zero otherwise
584 for (auto i = 0; i < c.size(); ++i) {
585 if (l[i] > 0) {
586 c[i] = 1.0;
587 }
588 }
589
590 // See how many of the first entries have zero values for l_i
591 contiguous_lzero = (l[0] == 0);
592 for (auto i = 0; i < c.size(); ++i) {
593 if (l[i] == 0) {
594 Nlzero++;
595 }
596 }
597 }
598 Nlnonzero = static_cast<int>(l.size()) - Nlzero;
599
600 eos.c = c;
601 eos.l = l;
602
603 eos.l_i = eos.l.cast<int>();
604
605 if (Nlzero + Nlnonzero != l.size()) {
606 throw std::invalid_argument("Somehow the l lengths don't add up");
607 }
608
609 if (((eos.l_i.cast<double>() - eos.l).cwiseAbs() > 0.0).any()) {
610 throw std::invalid_argument("Non-integer entry in l found");
611 }
612
613 // If a contiguous portion of the terms have values of l_i that are zero
614 // it is computationally advantageous to break up the evaluation into
615 // part that has just the n_i*tau^t_i*delta^d_i and the part with the
616 // exponential term exp(-delta^l_i)
617 if (l.sum() == 0) {
618 // No l term at all, just polynomial
619 JustPowerEOSTerm poly;
620 poly.n = eos.n;
621 poly.t = eos.t;
622 poly.d = eos.d;
623 container.add_term(poly);
624 }
625 else if (l.sum() > 0 && contiguous_lzero) {
626 JustPowerEOSTerm poly;
627 poly.n = eos.n.head(Nlzero);
628 poly.t = eos.t.head(Nlzero);
629 poly.d = eos.d.head(Nlzero);
630 container.add_term(poly);
631
633 e.n = eos.n.tail(Nlnonzero);
634 e.t = eos.t.tail(Nlnonzero);
635 e.d = eos.d.tail(Nlnonzero);
636 e.c = eos.c.tail(Nlnonzero);
637 e.l = eos.l.tail(Nlnonzero);
638 e.l_i = eos.l_i.tail(Nlnonzero);
639 container.add_term(PowerEOSTerm(e));
640 }
641 else {
642 // Don't try to get too clever, just add the term
643 container.add_term(eos);
644 }
645 };
646
647 auto build_Lemmon2005 = [&](auto term, auto & container) {
648 if (!all_same_length(term, { "n","t","d","m","l" })) {
649 throw std::invalid_argument("Lengths are not all identical in Lemmon2005 term");
650 }
651
653 eos.n = toeig(term["n"]);
654 eos.t = toeig(term["t"]);
655 eos.d = toeig(term["d"]);
656 eos.m = toeig(term["m"]);
657 eos.l = toeig(term["l"]);
658 eos.l_i = eos.l.cast<int>();
659 if (((eos.l_i.cast<double>() - eos.l).cwiseAbs() > 0.0).any()) {
660 throw std::invalid_argument("Non-integer entry in l found");
661 }
662
663 auto getindices = [](const auto& mask){
664 std::vector<int> indices;
665 for (auto i = 0; i < mask.size(); ++i){
666 if (mask[i]){ indices.push_back(i); }
667 }
668 return indices;
669 };
670 auto pow_indices = getindices((eos.m == 0 ) && (eos.l == 0));
671 auto mzerolpos_indices = getindices((eos.m == 0) && (eos.l > 0));
672 auto mzero_indices = getindices((eos.m > 0) && (eos.l > 0));
673
674 if (pow_indices.size() + mzerolpos_indices.size() + mzero_indices.size() != static_cast<std::size_t>(eos.m.size())) {
675 throw std::invalid_argument("Term subdivision failed in Lemmon2005 term");
676 }
677
678 if (!pow_indices.empty()){
679 JustPowerEOSTerm poly;
680 poly.n = eos.n(pow_indices);
681 poly.t = eos.t(pow_indices);
682 poly.d = eos.d(pow_indices);
683 container.add_term(poly);
684 }
685 if (!mzerolpos_indices.empty()){
687 e.n = eos.n(mzerolpos_indices);
688 e.t = eos.t(mzerolpos_indices);
689 e.d = eos.d(mzerolpos_indices);
690 e.l = eos.l(mzerolpos_indices);
691 e.c = (1 + 0*eos.l).cast<double>();
692 e.l_i = eos.l_i(mzerolpos_indices);
693 container.add_term(PowerEOSTerm(e));
694 }
695 if (!mzero_indices.empty()){
697 e.n = eos.n(mzero_indices);
698 e.t = eos.t(mzero_indices);
699 e.d = eos.d(mzero_indices);
700 e.m = eos.m(mzero_indices);
701 e.l = eos.l(mzero_indices);
702 e.l_i = e.l.cast<int>();
703 container.add_term(e);
704 }
705 };
706
707 auto build_gaussian = [&](auto term) {
708 GaussianEOSTerm eos;
709 eos.n = toeig(term["n"]);
710 eos.t = toeig(term["t"]);
711 eos.d = toeig(term["d"]);
712 eos.eta = toeig(term["eta"]);
713 eos.beta = toeig(term["beta"]);
714 eos.gamma = toeig(term["gamma"]);
715 eos.epsilon = toeig(term["epsilon"]);
716 if (!all_same_length(term, { "n","t","d","eta","beta","gamma","epsilon" })) {
717 throw std::invalid_argument("Lengths are not all identical in Gaussian term");
718 }
719 return eos;
720 };
721
722 auto build_exponential = [&](auto term) {
724 eos.n = toeig(term["n"]);
725 eos.t = toeig(term["t"]);
726 eos.d = toeig(term["d"]);
727 eos.g = toeig(term["g"]);
728 eos.l = toeig(term["l"]);
729 eos.l_i = eos.l.cast<int>();
730 if (!all_same_length(term, { "n","t","d","g","l" })) {
731 throw std::invalid_argument("Lengths are not all identical in exponential term");
732 }
733 return eos;
734 };
735
736 auto build_doubleexponential = [&](auto& term) {
737 if (!all_same_length(term, { "n","t","d","ld","gd","lt","gt" })) {
738 throw std::invalid_argument("Lengths are not all identical in double exponential term");
739 }
741 eos.n = toeig(term.at("n"));
742 eos.t = toeig(term.at("t"));
743 eos.d = toeig(term.at("d"));
744 eos.ld = toeig(term.at("ld"));
745 eos.gd = toeig(term.at("gd"));
746 eos.lt = toeig(term.at("lt"));
747 eos.gt = toeig(term.at("gt"));
748 eos.ld_i = eos.ld.cast<int>();
749 return eos;
750 };
751
752 auto build_GaoB = [&](auto term) {
753 GaoBEOSTerm eos;
754 eos.n = toeig(term["n"]);
755 eos.t = toeig(term["t"]);
756 eos.d = toeig(term["d"]);
757 eos.eta = -toeig(term["eta"]); // Watch out for this sign flip!!
758 eos.beta = toeig(term["beta"]);
759 eos.gamma = toeig(term["gamma"]);
760 eos.epsilon = toeig(term["epsilon"]);
761 eos.b = toeig(term["b"]);
762 if (!all_same_length(term, { "n","t","d","eta","beta","gamma","epsilon","b" })) {
763 throw std::invalid_argument("Lengths are not all identical in GaoB term");
764 }
765 return eos;
766 };
767
769 auto build_na = [&](auto& term) {
771 eos.n = toeig(term["n"]);
772 eos.A = toeig(term["A"]);
773 eos.B = toeig(term["B"]);
774 eos.C = toeig(term["C"]);
775 eos.D = toeig(term["D"]);
776 eos.a = toeig(term["a"]);
777 eos.b = toeig(term["b"]);
778 eos.beta = toeig(term["beta"]);
779 if (!all_same_length(term, { "n","A","B","C","D","a","b","beta" })) {
780 throw std::invalid_argument("Lengths are not all identical in nonanalytic term");
781 }
782 return eos;
783 };
784
785 for (auto& term : alphar) {
786 std::string type = term.at("type");
787 if (type == "ResidualHelmholtzPower") {
788 build_power(term, container);
789 }
790 else if (type == "ResidualHelmholtzGaussian") {
791 container.add_term(build_gaussian(term));
792 }
793 else if (type == "ResidualHelmholtzNonAnalytic") {
794 container.add_term(build_na(term));
795 }
796 else if (type == "ResidualHelmholtzLemmon2005") {
797 build_Lemmon2005(term, container);
798 }
799 else if (type == "ResidualHelmholtzGaoB") {
800 container.add_term(build_GaoB(term));
801 }
802 else if (type == "ResidualHelmholtzExponential") {
803 container.add_term(build_exponential(term));
804 }
805 else if (type == "ResidualHelmholtzDoubleExponential") {
806 container.add_term(build_doubleexponential(term));
807 }
808 else if (type == "ResidualHelmholtzGenericCubic") {
809 container.add_term(GenericCubicTerm(term));
810 }
811 else if (type == "ResidualHelmholtzPCSAFTGrossSadowski2001") {
812 container.add_term(PCSAFTGrossSadowski2001Term(term));
813 }
814 else {
815 throw std::invalid_argument("Bad term type: "+type);
816 }
817 }
818 return container;
819}
820
821inline auto get_EOSs(const std::vector<nlohmann::json>& pureJSON) {
822 std::vector<EOSTerms> EOSs;
823 for (auto& j : pureJSON) {
824 auto term = get_EOS_terms(j);
825 EOSs.emplace_back(term);
826 }
827 return EOSs;
828}
829
830inline auto collect_component_json(const std::vector<std::string>& components, const std::string& root)
831{
832 std::vector<nlohmann::json> out;
833 for (auto c : components) {
834 // First we try to lookup the name as a path, which can be on the filesystem, or relative to the root for default name lookup
835 std::vector<std::filesystem::path> candidates = { c, root + "/dev/fluids/" + c + ".json" };
836 std::filesystem::path selected_path = "";
837 for (auto candidate : candidates) {
838 if (std::filesystem::is_regular_file(candidate)) {
839 selected_path = candidate;
840 break;
841 }
842 }
843 if (selected_path != "") {
844 out.push_back(load_a_JSON_file(selected_path.string()));
845 }
846 else {
847 throw std::invalid_argument("Could not load any of the candidates:" + c);
848 }
849 }
850 return out;
851}
852
853inline auto collect_identifiers(const std::vector<nlohmann::json>& pureJSON)
854{
855 std::vector<std::string> CAS, Name, REFPROP, hash;
856 for (auto j : pureJSON) {
857 auto INFO = j.at("INFO");
858 Name.push_back(INFO.at("NAME"));
859 CAS.push_back(INFO.at("CAS"));
860 REFPROP.push_back(INFO.at("REFPROP_NAME"));
861 if (INFO.contains("HASH")){
862 hash.push_back(INFO.at("HASH"));
863 }
864 }
865 std::map<std::string, std::vector<std::string>> result{
866 {"CAS", CAS},
867 {"Name", Name},
868 {"REFPROP", REFPROP}
869 };
870 if (hash.size() == result["CAS"].size()){
871 result["hash"] = hash;
872 }
873 return result;
874}
875
877template<typename mapvecstring>
878inline auto select_identifier(const nlohmann::json& BIPcollection, const mapvecstring& identifierset, const nlohmann::json& flags){
879 for (const auto &ident: identifierset){
880 std::string key; std::vector<std::string> identifiers;
881 std::tie(key, identifiers) = ident;
882 try{
883 for (auto i = 0U; i < identifiers.size(); ++i){
884 for (auto j = i+1; j < identifiers.size(); ++j){
885 const std::vector<std::string> pair = {identifiers[i], identifiers[j]};
886 reducing::get_BIPdep(BIPcollection, pair, flags);
887 }
888 }
889 return key;
890 }
891 catch(...){
892
893 }
894 }
895 std::string errmsg;
896 for (const auto& [k,v] : identifierset){
897 if (errmsg.empty()){
898 errmsg += k;
899 }else {
900 errmsg += "," + k;
901 }
902 }
903 throw std::invalid_argument("Unable to match any of the identifier options: " + errmsg);
904}
905
907inline auto build_alias_map(const std::string& root) {
908 std::map<std::string, std::string> aliasmap;
909 for (auto path : get_files_in_folder(root + "/dev/fluids", ".json")) {
910 auto j = load_a_JSON_file(path.string());
911 std::string REFPROP_name = j.at("INFO").at("REFPROP_NAME");
912 std::string name = j.at("INFO").at("NAME");
913 for (std::string k : {"NAME", "CAS", "REFPROP_NAME"}) {
914 std::string val = j.at("INFO").at(k);
915 // Skip REFPROP names that match the fluid itself
916 if (k == "REFPROP_NAME" && val == name) {
917 continue;
918 }
919 // Skip invalid REFPROP names
920 if (k == "REFPROP_NAME" && val == "N/A") {
921 continue;
922 }
923 if (aliasmap.count(val) > 0) {
924 throw std::invalid_argument("Duplicated reverse lookup identifier ["+k+"] found in file:" + path.string());
925 }
926 else {
927 aliasmap[val] = std::filesystem::absolute(path).string();
928 }
929 }
930 std::vector<std::string> aliases = j.at("INFO").at("ALIASES");
931
932 for (std::string alias : aliases) {
933 if (alias != REFPROP_name && alias != name) { // Don't add REFPROP name or base name, were already above to list of aliases
934 if (aliasmap.count(alias) > 0) {
935 throw std::invalid_argument("Duplicated alias [" + alias + "] found in file:" + path.string());
936 }
937 else {
938 aliasmap[alias] = std::filesystem::absolute(path).string();
939 }
940 }
941 }
942 }
943 return aliasmap;
944}
945
946
948inline auto _build_multifluid_model(const std::vector<nlohmann::json> &pureJSON, const nlohmann::json& BIPcollection, const nlohmann::json& depcollection, const nlohmann::json& flags = {}) {
949
950 auto get_Rvals = [](const std::vector<nlohmann::json> &pureJSON) -> std::vector<double>{
951 std::vector<double> o;
952 for (auto pure : pureJSON){
953 o.push_back(pure.at("EOS")[0].at("gas_constant"));
954 }
955 return o;
956 };
957
958 auto [Tc, vc] = reducing::get_Tcvc(pureJSON);
959 auto EOSs = get_EOSs(pureJSON);
960 // Array of gas constants for each fluid
961 auto Rvals = get_Rvals(pureJSON);
962
963 // Extract the set of possible identifiers to be used to match parameters
964 auto identifierset = collect_identifiers(pureJSON);
965 // Decide which identifier is to be used (Name, CAS, REFPROP name)
966 auto identifiers = identifierset[select_identifier(BIPcollection, identifierset, flags)];
967
968 // Things related to the mixture
969 auto F = reducing::get_F_matrix(BIPcollection, identifiers, flags);
970 auto [funcs, funcsmeta] = get_departure_function_matrix(depcollection, BIPcollection, identifiers, flags);
971 auto [betaT, gammaT, betaV, gammaV] = reducing::get_BIP_matrices(BIPcollection, identifiers, flags, Tc, vc);
972
974
975 if (flags.contains("Rmodel") && flags.at("Rmodel") == "CODATA"){
977 }
978
979
980 nlohmann::json meta = {
981 {"pures", pureJSON},
982 {"mix", funcsmeta},
983 };
984
985 auto redfunc = ReducingFunctions(std::move(MultiFluidReducingFunction(betaT, gammaT, betaV, gammaV, Tc, vc)));
986
987 auto model = MultiFluid(
988 std::move(redfunc),
989 CorrespondingStatesContribution(std::move(EOSs)),
990 DepartureContribution(std::move(F), std::move(funcs)),
991 std::move(Rcalc)
992 );
993 model.set_meta(meta.dump(1));
994 return model;
995}
996
998inline auto build_multifluid_JSONstr(const std::vector<std::string>& componentJSON, const std::string& BIPJSON, const std::string& departureJSON, const nlohmann::json& flags = {}) {
999
1000 // Mixture things
1001 const auto BIPcollection = nlohmann::json::parse(BIPJSON);
1002 const auto depcollection = nlohmann::json::parse(departureJSON);
1003
1004 // Pure fluids
1005 std::vector<nlohmann::json> pureJSON;
1006 for (auto& c : componentJSON) {
1007 pureJSON.emplace_back(nlohmann::json::parse(c));
1008 }
1009 return _build_multifluid_model(pureJSON, BIPcollection, depcollection, flags);
1010}
1011
1020inline auto make_pure_components_JSON(const nlohmann::json& components, const std::optional<std::string>& root = std::nullopt){
1021
1022 std::vector<nlohmann::json> pureJSON;
1023 if (!components.is_array()){
1024 throw std::invalid_argument("Must be an array");
1025 }
1026 std::optional<decltype(build_alias_map(""))> optaliasmap;
1027 for (const nlohmann::json& comp : components){
1028 auto get_or_aliasmap = [&](){
1029 try{
1030 return multilevel_JSON_load(comp, root);
1031 }
1032 catch(...){
1033 // Build the alias map if not already constructed
1034 if (!optaliasmap && root){
1035 optaliasmap = build_alias_map(root.value());
1036 if (optaliasmap.value().count(comp) != 1){
1037 std::string scomp = comp.get<std::string>();
1038 std::string errname = (scomp.size() > 200) ? scomp.substr(0, 200)+"..." : scomp;
1039 throw teqp::InvalidArgument("Alias map constructed, but component name is not found in alias map: " + errname);
1040 }
1041 }
1042 else{
1043 std::string scomp = comp.get<std::string>();
1044 std::string errname = (scomp.size() > 200) ? scomp.substr(0, 200)+"..." : scomp;
1045 teqp::InvalidArgument("It was not possible to load the alias map because no path was provided. Failure to load: " + errname);
1046 }
1047 return multilevel_JSON_load(optaliasmap.value()[comp], root);
1048 }
1049 };
1050 if (comp.is_string()){
1051 std::string contents = comp;
1052 // Note: first arg to substr is first index to *keep*, no second arg so keep to the end
1053 if (contents.find("PATH::") == 0){
1054 pureJSON.push_back(load_a_JSON_file(contents.substr(6)));
1055 }
1056 else if (contents.find("FLDPATH::") == 0){
1057 pureJSON.push_back(RPinterop::FLDfile(contents.substr(9)).make_json(""));
1058 }
1059 else if (contents.find("FLD::") == 0){
1060 pureJSON.push_back(RPinterop::FLDfile(contents.substr(5)).make_json(""));
1061 }
1062 else{
1063 pureJSON.push_back(get_or_aliasmap());
1064 }
1065 }
1066 else{
1067 pureJSON.push_back(get_or_aliasmap());
1068 }
1069 }
1070 return pureJSON;
1071}
1072
1073inline auto build_multifluid_model(const std::vector<std::string>& components, const std::string& root, const std::string& BIPcollectionpath = {}, const nlohmann::json& flags = {}, const std::string& departurepath = {}) {
1074
1075 // Convert the string representations to JSON using the existing routines (a bit slower, but more convenient, more DRY)
1076 nlohmann::json BIPcollection = nlohmann::json::array();
1077 nlohmann::json depcollection = nlohmann::json::array();
1078 if (components.size() > 1){
1079 nlohmann::json B = BIPcollectionpath, D = departurepath;
1080 BIPcollection = multilevel_JSON_load(B, root + "/dev/mixtures/mixture_binary_pairs.json");
1081 depcollection = multilevel_JSON_load(D, root + "/dev/mixtures/mixture_departure_functions.json");
1082 }
1083
1084 return _build_multifluid_model(make_pure_components_JSON(components, root), BIPcollection, depcollection, flags);
1085}
1086
1095inline auto multifluidfactory(const nlohmann::json& spec) {
1096
1097 nlohmann::json flags = (spec.contains("flags")) ? spec.at("flags") : nlohmann::json();
1098
1099 // We are in the interop logical branch in which we will be invoking the REFPROP-interop code
1100 if (spec.contains("HMX.BNC")){
1101 std::vector<nlohmann::json> componentJSON;
1102 for (auto comp : spec.at("components")){
1103 componentJSON.push_back(RPinterop::FLDfile(comp).make_json(""));
1104 }
1105 auto [BIPcollection, depcollection] = RPinterop::HMXBNCfile(spec.at("HMX.BNC")).make_jsons();
1106 return _build_multifluid_model(componentJSON, BIPcollection, depcollection, flags);
1107 }
1108 else{
1109
1110 std::string root = (spec.contains("root")) ? spec.at("root") : "";
1111
1112 auto components = spec.at("components");
1113
1114 nlohmann::json BIPcollection = nlohmann::json::array();
1115 nlohmann::json depcollection = nlohmann::json::array();
1116 if (components.size() > 1){
1117 BIPcollection = multilevel_JSON_load(spec.at("BIP"), root + "/dev/mixtures/mixture_binary_pairs.json");
1118
1119 if (spec.contains("departure")){
1120 std::string msg = "departure was provided but is invalid; options are non-empty array, path to file as string, or JSON data encoded as string";
1121 auto load_departure = [&msg](const nlohmann::json& j){
1122 if (j.is_array() && j.size() > 0){
1123 return j;
1124 }
1125 else if (j.is_string()){
1126 const std::string& s = j;
1127 if (s.find("PATH::") == 0){
1128 return load_a_JSON_file(s.substr(6));
1129 }
1130 else{
1131 try{
1132 try{
1133 return multilevel_JSON_load(s);
1134 }
1135 catch(...){
1136 return nlohmann::json::parse(s);
1137 }
1138 }
1139 catch(...){
1140 throw teqp::InvalidArgument(msg);
1141 }
1142 }
1143 }
1144 else{
1145 throw teqp::InvalidArgument(msg);
1146 }
1147 };
1148
1149 if (root.empty()){
1150 depcollection = load_departure(spec.at("departure"));
1151 }
1152 else{
1153 depcollection = multilevel_JSON_load(spec.at("departure"), root + "/dev/mixtures/mixture_departure_functions.json");
1154 }
1155 }
1156 }
1157
1158 return _build_multifluid_model(make_pure_components_JSON(components, root), BIPcollection, depcollection, flags);
1159 }
1160}
1161
1162inline auto multifluidfactory(const std::string& specstring) {
1163 return multifluidfactory(nlohmann::json::parse(specstring));
1164}
1165
1166
1167
1168//class DummyEOS {
1169//public:
1170// template<typename TType, typename RhoType> auto alphar(TType tau, const RhoType& delta) const { return tau * delta; }
1171//};
1172//class DummyReducingFunction {
1173//public:
1174// template<typename MoleFractions> auto get_Tr(const MoleFractions& molefracs) const { return molefracs[0]; }
1175// template<typename MoleFractions> auto get_rhor(const MoleFractions& molefracs) const { return molefracs[0]; }
1176//};
1177//inline auto build_dummy_multifluid_model(const std::vector<std::string>& components) {
1178// std::vector<DummyEOS> EOSs(2);
1179// std::vector<std::vector<DummyEOS>> funcs(2); for (auto i = 0; i < funcs.size(); ++i) { funcs[i].resize(funcs.size()); }
1180// std::vector<std::vector<double>> F(2); for (auto i = 0; i < F.size(); ++i) { F[i].resize(F.size()); }
1181//
1182// struct Fwrapper {
1183// private:
1184// const std::vector<std::vector<double>> F_;
1185// public:
1186// Fwrapper(const std::vector<std::vector<double>> &F) : F_(F){};
1187// auto operator ()(std::size_t i, std::size_t j) const{ return F_[i][j]; }
1188// };
1189// auto ff = Fwrapper(F);
1190// auto redfunc = DummyReducingFunction();
1191// return MultiFluid(std::move(redfunc), std::move(CorrespondingStatesContribution(std::move(EOSs))), std::move(DepartureContribution(std::move(ff), std::move(funcs))));
1192//}
1193//inline void test_dummy() {
1194// auto model = build_dummy_multifluid_model({ "A", "B" });
1195// std::valarray<double> rhovec = { 1.0, 2.0 };
1196// auto alphar = model.alphar(300.0, rhovec);
1197//}
1198
1199}; // namespace teqp
CorrespondingStatesContribution(EOSCollection &&EOSs)
auto alphari(const TauType &tau, const DeltaType &delta, std::size_t i) const
auto alphar(const TauType &tau, const DeltaType &delta, const MoleFractions &molefracs) const
auto get_EOS(std::size_t i) const
DepartureContribution(FCollection &&F, DepartureFunctionCollection &&funcs)
auto alphar(const TauType &tau, const DeltaType &delta, const MoleFractions &molefracs) const
auto get_alpharij(const std::size_t i, const std::size_t j, const TauType &tau, const DeltaType &delta) const
Call a single departure term at i,j.
const auto & get_F() const
auto add_term(Instance &&instance)
auto alphar(const TType &T, const RhoType &rho, const MoleFracType &molefrac) const
auto get_reducing_density(const MoleFracType &molefrac) const
const std::variant< double, std::string > get_BIP(const std::size_t &i, const std::size_t &j, const std::string &key) const
Return a binary interaction parameter.
const CorrespondingTerm corr
auto get_meta() const
Get the metadata stored in string form.
const DepartureTerm dep
void set_meta(const std::string &m)
Store some sort of metadata in string form (perhaps a JSON representation of the model?...
MultiFluid(ReducingFunctions &&redfunc, CorrespondingTerm &&corr, DepartureTerm &&dep, GasConstantCalculator &&Rcalc)
multifluid::gasconstant::GasConstantCalculator GasConstantCalculator
const ReducingFunctions redfunc
const GasConstantCalculator Rcalc
auto get_reducing_temperature(const MoleFracType &molefrac) const
auto alphar(TType T, const RhoType &rhovec, const std::optional< typename RhoType::value_type > rhotot=std::nullopt) const
auto alphar_taudelta(const TType &tau, const RhoType &delta, const MoleFracType &molefrac) const
auto alphar_taudeltai(const TType &tau, const RhoType &delta, const std::size_t i) const
auto R(const VecType &molefracs) const
std::variant< MoleFractionWeighted, CODATA > GasConstantCalculator
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.
auto _build_multifluid_model(const std::vector< nlohmann::json > &pureJSON, const nlohmann::json &BIPcollection, const nlohmann::json &depcollection, const nlohmann::json &flags={})
Internal method for actually constructing the model with the provided JSON data structures.
auto get_EOS_terms(const nlohmann::json &j)
auto get_EOSs(const std::vector< nlohmann::json > &pureJSON)
auto build_alias_map(const std::string &root)
Build a reverse-lookup map for finding a fluid JSON structure given a backup identifier.
auto toeig(const std::vector< double > &v) -> Eigen::ArrayXd
Definition types.hpp:10
nlohmann::json load_a_JSON_file(const std::string &path)
Load a JSON file from a specified file.
auto collect_identifiers(const std::vector< nlohmann::json > &pureJSON)
EOSTermContainer< JustPowerEOSTerm, PowerEOSTerm, GaussianEOSTerm, NonAnalyticEOSTerm, Lemmon2005EOSTerm, GaoBEOSTerm, ExponentialEOSTerm, DoubleExponentialEOSTerm, GenericCubicTerm, PCSAFTGrossSadowski2001Term > EOSTerms
auto build_multifluid_JSONstr(const std::vector< std::string > &componentJSON, const std::string &BIPJSON, const std::string &departureJSON, const nlohmann::json &flags={})
A builder function where the JSON-formatted strings are provided explicitly rather than file paths.
auto get_departure_json(const std::string &name, const std::string &path)
EOSTermContainer< JustPowerEOSTerm, PowerEOSTerm, GaussianEOSTerm, GERG2004EOSTerm, NullEOSTerm, DoubleExponentialEOSTerm, Chebyshev2DEOSTerm > DepartureTerms
auto get_departure_function_matrix(const nlohmann::json &depcollection, const nlohmann::json &BIPcollection, const std::vector< std::string > &components, const nlohmann::json &flags)
auto build_departure_function(const nlohmann::json &j)
auto all_same_length(const nlohmann::json &j, const std::vector< std::string > &ks)
auto select_identifier(const nlohmann::json &BIPcollection, const mapvecstring &identifierset, const nlohmann::json &flags)
Iterate over the possible options for identifiers to determine which one will satisfy all the binary ...
auto multifluidfactory(const nlohmann::json &spec)
Load a model from a JSON data structure.
auto make_pure_components_JSON(const nlohmann::json &components, const std::optional< std::string > &root=std::nullopt)
auto build_multifluid_model(const std::vector< std::string > &components, const std::string &root, const std::string &BIPcollectionpath={}, const nlohmann::json &flags={}, const std::string &departurepath={})
ReducingTermContainer< MultiFluidReducingFunction, MultiFluidInvariantReducingFunction > ReducingFunctions
auto collect_component_json(const std::vector< std::string > &components, const std::string &root)
auto forceeval(T &&expr)
Definition types.hpp:52
auto multilevel_JSON_load(const nlohmann::json &j, const std::optional< std::string > &default_path=std::nullopt)