teqp 0.22.0
Loading...
Searching...
No Matches
pcsaft.hpp
Go to the documentation of this file.
1/***
2
3 \brief This file contains the contributions that can be composed together to form SAFT models
4
5*/
6
7#pragma once
8
9#include "nlohmann/json.hpp"
10#include "teqp/types.hpp"
11#include "teqp/exceptions.hpp"
12#include "teqp/constants.hpp"
13#include "teqp/json_tools.hpp"
16#include <optional>
17
18// Definitions for the matrices of global constants for the PCSAFT model
20namespace GrossSadowski2001{
21 extern const Eigen::Array<double, 3, 7> a, b;
22}
23namespace LiangIECR2012{
24 extern const Eigen::Array<double, 3, 7> a, b;
25}
26namespace LiangIECR2014{
27 extern const Eigen::Array<double, 3, 7> a, b;
28}
29}
30
32
33//#define PCSAFTDEBUG
34
36struct SAFTCoeffs {
37 std::string name;
38 double m = -1,
41 std::string BibTeXKey;
42 double mustar2 = 0,
43 nmu = 0,
44 Qstar2 = 0,
45 nQ = 0;
46};
47
50 std::map<std::string, SAFTCoeffs> coeffs;
51public:
53 insert_normal_fluid("Methane", 1.0000, 3.7039, 150.03, "Gross-IECR-2001");
54 insert_normal_fluid("Ethane", 1.6069, 3.5206, 191.42, "Gross-IECR-2001");
55 insert_normal_fluid("Propane", 2.0020, 3.6184, 208.11, "Gross-IECR-2001");
56 }
57 void insert_normal_fluid(const std::string& name, double m, const double sigma_Angstrom, const double epsilon_over_k, const std::string& BibTeXKey) {
58 SAFTCoeffs coeff;
59 coeff.name = name;
60 coeff.m = m;
61 coeff.sigma_Angstrom = sigma_Angstrom;
62 coeff.epsilon_over_k = epsilon_over_k;
63 coeff.BibTeXKey = BibTeXKey;
64 coeffs.insert(std::pair<std::string, SAFTCoeffs>(name, coeff));
65 }
66 const auto& get_normal_fluid(const std::string& name) {
67 auto it = coeffs.find(name);
68 if (it != coeffs.end()) {
69 return it->second;
70 }
71 else {
72 throw std::invalid_argument("Bad name:" + name);
73 }
74 }
75 auto get_coeffs(const std::vector<std::string>& names){
76 std::vector<SAFTCoeffs> c;
77 for (auto n : names){
78 c.push_back(get_normal_fluid(n));
79 }
80 return c;
81 }
82};
83
87template <typename Eta, typename Mbar>
88auto C1(const Eta& eta, const Mbar& mbar) {
89 auto oneeta = 1.0 - eta;
90 return forceeval(1.0 / (1.0
91 + mbar * (8.0 * eta - 2.0 * eta * eta) / (oneeta*oneeta*oneeta*oneeta)
92 + (1.0 - mbar) * (20.0 * eta - 27.0 * eta * eta + 12.0 * eta*eta*eta - 2.0 * eta*eta*eta*eta) / ((1.0 - eta) * (2.0 - eta)*(1.0 - eta) * (2.0 - eta))));
93}
95template <typename Eta, typename Mbar>
96auto C2(const Eta& eta, const Mbar& mbar) {
97 return forceeval(-pow(C1(eta, mbar), 2) * (
98 mbar * (-4.0 * eta * eta + 20.0 * eta + 8.0) / pow(1.0 - eta, 5)
99 + (1.0 - mbar) * (2.0 * eta * eta * eta + 12.0 * eta * eta - 48.0 * eta + 40.0) / pow((1.0 - eta) * (2.0 - eta), 3)
100 ));
101}
102
104template<typename VecType, typename VecType2>
105auto get_alphar_hs(const VecType& zeta, const VecType2& D) {
106 /*
107 The limit of alphar_hs in the case of density going to zero is zero,
108 but its derivatives must still match so that the automatic differentiation tooling
109 will work properly, so a Taylor series around rho=0 is constructed. The first term is
110 needed for calculations of virial coefficient temperature derivatives.
111 The term zeta_0 in the denominator is zero, but *ratios* of zeta values are ok because
112 they cancel the rho (in the limit at least) so we can write that zeta_x/zeta_y = D_x/D_x where
113 D_i = sum_i x_im_id_{ii}. This allows for the substitution into the series expansion terms.
114
115 <sympy>
116 from sympy import *
117 zeta_0, zeta_1, zeta_2, zeta_3, rho = symbols('zeta_0, zeta_1, zeta_2, zeta_3, rho')
118 D_0, D_1, D_2, D_3 = symbols('D_0, D_1, D_2, D_3')
119 POW2 = lambda x: x**2
120 POW3 = lambda x: x**3
121 alpha = 1/zeta_0*(3*zeta_1*zeta_2/(1-zeta_3) + zeta_2**3/(zeta_3*POW2(1-zeta_3)) + (POW3(zeta_2)/POW2(zeta_3)-zeta_0)*log(1-zeta_3))
122 alpha = alpha.subs(zeta_0, rho*D_0).subs(zeta_1, rho*D_1).subs(zeta_2, rho*D_2).subs(zeta_3, rho*D_3)
123 for Nderiv in [1, 2, 3, 4, 5]:
124 display(simplify((simplify(diff(alpha, rho, Nderiv)).subs(rho,0)*rho**Nderiv/factorial(Nderiv)).subs(D_0, zeta_0/rho).subs(D_1, zeta_1/rho).subs(D_2, zeta_2/rho).subs(D_3, zeta_3/rho)))
125 </sympy>
126
127 The updated approach is simpler; start off with the expression for alphar_hs, and simplify
128 ratios where 0/0 for which the l'hopital limit would be ok until you remove all the terms
129 in the denominator, allowing it to be evaluated. All terms have a zeta_x/zeta_0 and a few
130 have zeta_3*zeta_0 in the denominator
131
132 */
133 auto Upsilon = 1.0 - zeta[3];
134 if (getbaseval(zeta[3]) == 0){
135 return forceeval(
136 3.0*D[1]/D[0]*zeta[2]/Upsilon
137 + D[2]*D[2]*zeta[2]/(D[3]*D[0]*Upsilon*Upsilon)
138 - log(Upsilon)
139 + (D[2]*D[2]*D[2])/(D[3]*D[3]*D[0])*log(Upsilon)
140 );
141 }
142
143 return forceeval(1.0 / zeta[0] * (3.0 * zeta[1] * zeta[2] / Upsilon
144 + zeta[2] * zeta[2] * zeta[2] / zeta[3] / Upsilon / Upsilon
145 + (zeta[2] * zeta[2] * zeta[2] / (zeta[3] * zeta[3]) - zeta[0]) * log(1.0 - zeta[3])
146 ));
147}
148
150template<typename zVecType, typename dVecType>
151auto gij_HS(const zVecType& zeta, const dVecType& d,
152 std::size_t i, std::size_t j) {
153 auto Upsilon = 1.0 - zeta[3];
154#if defined(PCSAFTDEBUG)
155 auto term1 = forceeval(1.0 / (Upsilon));
156 auto term2 = forceeval(d[i] * d[j] / (d[i] + d[j]) * 3.0 * zeta[2] / pow(Upsilon, 2));
157 auto term3 = forceeval(pow(d[i] * d[j] / (d[i] + d[j]), 2) * 2.0 * zeta[2]*zeta[2] / pow(Upsilon, 3));
158#endif
159 return forceeval(1.0 / (Upsilon)+d[i] * d[j] / (d[i] + d[j]) * 3.0 * zeta[2] / pow(Upsilon, 2)
160 + pow(d[i] * d[j] / (d[i] + d[j]), 2) * 2.0 * zeta[2]*zeta[2] / pow(Upsilon, 3));
161}
162
166template<typename VecType1, typename NType>
167auto powvec(const VecType1& v1, NType n) {
168 auto o = v1;
169 for (auto i = 0; i < v1.size(); ++i) {
170 o[i] = pow(v1[i], n);
171 }
172 return o;
173}
174
178template<typename VecType1, typename VecType2, typename VecType3>
179auto sumproduct(const VecType1& v1, const VecType2& v2, const VecType3& v3) {
180 using ResultType = typename std::common_type_t<decltype(v1[0]), decltype(v2[0]), decltype(v3[0])>;
181 return forceeval((v1.template cast<ResultType>().array() * v2.template cast<ResultType>().array() * v3.template cast<ResultType>().array()).sum());
182}
183
184/***
185 * \brief This class provides the evaluation of the hard chain contribution from classic PC-SAFT
186 */
188
189protected:
190 const Eigen::ArrayX<double> m,
194 const Eigen::ArrayXXd kmat;
195 Eigen::Array<double, 3, 7> a,
197
198public:
199 PCSAFTHardChainContribution(const Eigen::ArrayX<double> &m, const Eigen::ArrayX<double> &mminus1, const Eigen::ArrayX<double> &sigma_Angstrom, const Eigen::ArrayX<double> &epsilon_over_k, const Eigen::ArrayXXd &kmat, const Eigen::Array<double, 3, 7>&a, const Eigen::Array<double, 3,7>&b)
201
203
204 template<typename TTYPE, typename RhoType, typename VecType>
205 auto eval(const TTYPE& T, const RhoType& rhomolar, const VecType& mole_fractions) const {
206
207 Eigen::Index N = m.size();
208
209 if (mole_fractions.size() != N) {
210 throw std::invalid_argument("Length of mole_fractions (" + std::to_string(mole_fractions.size()) + ") is not the length of components (" + std::to_string(N) + ")");
211 }
212
213 using TRHOType = std::common_type_t<std::decay_t<TTYPE>, std::decay_t<RhoType>, std::decay_t<decltype(mole_fractions[0])>, std::decay_t<decltype(m[0])>>;
214
215 Eigen::ArrayX<TTYPE> d(N);
216 TRHOType m2_epsilon_sigma3_bar = 0.0;
217 TRHOType m2_epsilon2_sigma3_bar = 0.0;
218 for (auto i = 0L; i < N; ++i) {
219 d[i] = sigma_Angstrom[i]*(1.0 - 0.12 * exp(-3.0*epsilon_over_k[i]/T)); // [A]
220 for (auto j = 0; j < N; ++j) {
221 // Eq. A.5
222 auto sigma_ij = 0.5 * sigma_Angstrom[i] + 0.5 * sigma_Angstrom[j];
223 auto eij_over_k = sqrt(epsilon_over_k[i] * epsilon_over_k[j]) * (1.0 - kmat(i,j));
224 auto sigmaij3 = sigma_ij*sigma_ij*sigma_ij;
225 auto ekT = eij_over_k/T;
226 m2_epsilon_sigma3_bar += mole_fractions[i] * mole_fractions[j] * m[i] * m[j] * ekT * sigmaij3;
227 m2_epsilon2_sigma3_bar += mole_fractions[i] * mole_fractions[j] * m[i] * m[j] * (ekT*ekT) * sigmaij3;
228 }
229 }
230 auto mbar = (mole_fractions.template cast<TRHOType>().array()*m.template cast<TRHOType>().array()).sum();
231
233 RhoType rho_A3 = rhomolar * N_A * 1e-30; //[molecules (not moles)/A^3]
234
235 constexpr double MY_PI = EIGEN_PI;
236 double pi6 = (MY_PI / 6.0);
237
239 using ta = std::common_type_t<decltype(m[0]), decltype(d[0]), decltype(rho_A3)>;
240 std::vector<ta> zeta(4), D(4);
241 for (std::size_t n = 0; n < 4; ++n) {
242 // Eqn A.8
243 auto dn = pow(d, static_cast<int>(n));
244 TRHOType xmdn = forceeval((mole_fractions.template cast<TRHOType>().array()*m.template cast<TRHOType>().array()*dn.template cast<TRHOType>().array()).sum());
245 D[n] = forceeval(pi6*xmdn);
246 zeta[n] = forceeval(D[n]*rho_A3);
247 }
248
250 auto eta = zeta[3];
251
252 Eigen::Array<decltype(eta), 7, 1> etapowers; etapowers(0) = 1.0; for (auto i = 1U; i <= 6; ++i){ etapowers(i) = eta*etapowers(i-1); }
253 using TYPE = TRHOType;
254 Eigen::Array<TYPE, 7, 1> abar = (a.row(0).cast<TYPE>().array() + ((mbar - 1.0) / mbar) * a.row(1).cast<TYPE>().array() + ((mbar - 1.0) / mbar) * ((mbar - 2.0) / mbar) * a.row(2).cast<TYPE>().array()).eval();
255 Eigen::Array<TYPE, 7, 1> bbar = (b.row(0).cast<TYPE>().array() + ((mbar - 1.0) / mbar) * b.row(1).cast<TYPE>().array() + ((mbar - 1.0) / mbar) * ((mbar - 2.0) / mbar) * b.row(2).cast<TYPE>().array()).eval();
256 auto I1 = (abar.array().template cast<decltype(eta)>()*etapowers).sum();
257 auto I2 = (bbar.array().template cast<decltype(eta)>()*etapowers).sum();
258
259 // Hard chain contribution from G&S
260 using tt = std::common_type_t<decltype(zeta[0]), decltype(d[0])>;
261 Eigen::ArrayX<tt> lngii_hs(mole_fractions.size());
262 for (auto i = 0; i < lngii_hs.size(); ++i) {
263 lngii_hs[i] = log(gij_HS(zeta, d, i, i));
264 }
265 auto alphar_hc = forceeval(mbar * get_alphar_hs(zeta, D) - sumproduct(mole_fractions, mminus1, lngii_hs)); // Eq. A.4
266
267 // Dispersive contribution
268 auto C1_ = C1(eta, mbar);
269 auto alphar_disp = forceeval(-2 * MY_PI * rho_A3 * I1 * m2_epsilon_sigma3_bar - MY_PI * rho_A3 * mbar * C1_ * I2 * m2_epsilon2_sigma3_bar);
270
271#if defined(PCSAFTDEBUG)
272 if (!std::isfinite(getbaseval(alphar_hc))){
273 throw teqp::InvalidValue("An invalid value was obtained for alphar_hc; please investigate");
274 }
275 if (!std::isfinite(getbaseval(I1))){
276 throw teqp::InvalidValue("An invalid value was obtained for I1; please investigate");
277 }
278 if (!std::isfinite(getbaseval(I2))){
279 throw teqp::InvalidValue("An invalid value was obtained for I2; please investigate");
280 }
281 if (!std::isfinite(getbaseval(C1_))){
282 throw teqp::InvalidValue("An invalid value was obtained for C1; please investigate");
283 }
284 if (!std::isfinite(getbaseval(alphar_disp))){
285 throw teqp::InvalidValue("An invalid value was obtained for alphar_disp; please investigate");
286 }
287#endif
288 struct PCSAFTHardChainContributionTerms{
289 TRHOType eta;
290 TRHOType alphar_hc;
291 TRHOType alphar_disp;
292 };
293 return PCSAFTHardChainContributionTerms{eta, alphar_hc, alphar_disp};
294 }
295};
296
304public:
307protected:
308 Eigen::ArrayX<double> m,
312 std::vector<std::string> names, bibtex;
313 Eigen::ArrayXXd kmat;
314
316 std::optional<PCSAFTDipolarContribution> dipolar; // Can be present or not
317 std::optional<PCSAFTQuadrupolarContribution> quadrupolar; // Can be present or not
318
319 void check_kmat(Eigen::Index N) {
320 if (kmat.cols() != kmat.rows()) {
321 throw teqp::InvalidArgument("kmat rows and columns are not identical");
322 }
323 if (kmat.cols() == 0) {
324 kmat.resize(N, N); kmat.setZero();
325 }
326 else if (kmat.cols() != N) {
327 throw teqp::InvalidArgument("kmat needs to be a square matrix the same size as the number of components");
328 }
329 };
330 auto get_coeffs_from_names(const std::vector<std::string> &the_names){
331 PCSAFTLibrary library;
332 return library.get_coeffs(the_names);
333 }
334 auto build_hardchain(const std::vector<SAFTCoeffs> &coeffs, const Eigen::Array<double, 3, 7>& a, const Eigen::Array<double, 3, 7>& b){
335 check_kmat(coeffs.size());
336
337 m.resize(coeffs.size());
338 mminus1.resize(coeffs.size());
339 sigma_Angstrom.resize(coeffs.size());
340 epsilon_over_k.resize(coeffs.size());
341 names.resize(coeffs.size());
342 bibtex.resize(coeffs.size());
343 auto i = 0;
344 for (const auto &coeff : coeffs) {
345 m[i] = coeff.m;
346 mminus1[i] = m[i] - 1;
347 sigma_Angstrom[i] = coeff.sigma_Angstrom;
348 epsilon_over_k[i] = coeff.epsilon_over_k;
349 names[i] = coeff.name;
350 bibtex[i] = coeff.BibTeXKey;
351 i++;
352 }
354 }
355 auto extract_names(const std::vector<SAFTCoeffs> &coeffs){
356 std::vector<std::string> names_;
357 for (const auto& c: coeffs){
358 names_.push_back(c.name);
359 }
360 return names_;
361 }
362 auto build_dipolar(const std::vector<SAFTCoeffs> &coeffs) -> std::optional<PCSAFTDipolarContribution>{
363 Eigen::ArrayXd mustar2(coeffs.size()), nmu(coeffs.size());
364 auto i = 0;
365 for (const auto &coeff : coeffs) {
366 mustar2[i] = coeff.mustar2;
367 nmu[i] = coeff.nmu;
368 i++;
369 }
370 if ((mustar2*nmu).cwiseAbs().sum() == 0){
371 return std::nullopt; // No dipolar contribution is present
372 }
373 // The dispersive and hard chain initialization has already happened at this point
375 }
376 auto build_quadrupolar(const std::vector<SAFTCoeffs> &coeffs) -> std::optional<PCSAFTQuadrupolarContribution>{
377 // The dispersive and hard chain initialization has already happened at this point
378 Eigen::ArrayXd Qstar2(coeffs.size()), nQ(coeffs.size());
379 auto i = 0;
380 for (const auto &coeff : coeffs) {
381 Qstar2[i] = coeff.Qstar2;
382 nQ[i] = coeff.nQ;
383 i++;
384 }
385 if ((Qstar2*nQ).cwiseAbs().sum() == 0){
386 return std::nullopt; // No quadrupolar contribution is present
387 }
389 }
390public:
391 PCSAFTMixture(const std::vector<std::string> &names,
392 const Eigen::Array<double, 3, 7>& a = teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::a,
393 const Eigen::Array<double, 3, 7>& b = teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::b,
394 const Eigen::ArrayXXd& kmat = {}) : PCSAFTMixture(get_coeffs_from_names(names), a, b, kmat){};
395 PCSAFTMixture(const std::vector<SAFTCoeffs> &coeffs,
396 const Eigen::Array<double, 3, 7>& a = teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::a,
397 const Eigen::Array<double, 3, 7>& b = teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::b,
398 const Eigen::ArrayXXd &kmat = {}) : names(extract_names(coeffs)), kmat(kmat), hardchain(build_hardchain(coeffs, a, b)), dipolar(build_dipolar(coeffs)), quadrupolar(build_quadrupolar(coeffs)) {};
399
400// PCSAFTMixture( const PCSAFTMixture& ) = delete; // non construction-copyable
401 PCSAFTMixture& operator=( const PCSAFTMixture& ) = delete; // non copyable
402
403 auto get_m() const { return m; }
404 auto get_sigma_Angstrom() const { return sigma_Angstrom; }
405 auto get_epsilon_over_k_K() const { return epsilon_over_k; }
406 auto get_kmat() const { return kmat; }
407 auto get_names() const { return names;}
408 auto get_BibTeXKeys() const { return bibtex;}
409
410 auto print_info() {
411 std::string s = std::string("i m sigma / A e/kB / K \n ++++++++++++++") + "\n";
412 for (auto i = 0; i < m.size(); ++i) {
413 s += std::to_string(i) + " " + std::to_string(m[i]) + " " + std::to_string(sigma_Angstrom[i]) + " " + std::to_string(epsilon_over_k[i]) + "\n";
414 }
415 return s;
416 }
417
418 template<typename VecType>
419 double max_rhoN(const double T, const VecType& mole_fractions) const {
420 auto N = mole_fractions.size();
421 Eigen::ArrayX<double> d(N);
422 for (auto i = 0; i < N; ++i) {
423 d[i] = sigma_Angstrom[i] * (1.0 - 0.12 * exp(-3.0 * epsilon_over_k[i] / T));
424 }
425 return 6 * 0.74 / EIGEN_PI / (mole_fractions*m*powvec(d, 3)).sum()*1e30; // particles/m^3
426 }
427
428 template<class VecType>
429 auto R(const VecType& molefrac) const {
431 }
432
433 template<typename TTYPE, typename RhoType, typename VecType>
434 auto alphar(const TTYPE& T, const RhoType& rhomolar, const VecType& mole_fractions) const {
435 // First values for the chain with dispersion (always included)
436 auto vals = hardchain.eval(T, rhomolar, mole_fractions);
437 auto alphar = forceeval(vals.alphar_hc + vals.alphar_disp);
438
439 auto rho_A3 = forceeval(rhomolar*N_A*1e-30);
440 // If dipole is present, add its contribution
441 if (dipolar){
442 auto valsdip = dipolar.value().eval(T, rho_A3, vals.eta, mole_fractions);
443 alphar += valsdip.alpha;
444 }
445 // If quadrupole is present, add its contribution
446 if (quadrupolar){
447 auto valsquad = quadrupolar.value().eval(T, rho_A3, vals.eta, mole_fractions);
448 alphar += valsquad.alpha;
449 }
450 return forceeval(alphar);
451 }
452};
453
455inline auto PCSAFTfactory(const nlohmann::json& spec) {
456 std::optional<Eigen::ArrayXXd> kmat;
457 if (spec.contains("kmat") && spec.at("kmat").is_array() && spec.at("kmat").size() > 0){
458 kmat = build_square_matrix(spec["kmat"]);
459 }
460 // By default use the a & b matrices of Gross&Sadowski, IECR, 2001
463 // Unless overwritten by user selection via the "ab" field
464 if (spec.contains("ab")){
465 std::string source = spec.at("ab");
466 if (source == "Liang-IECR-2012"){
469 }
470 else if (source == "Liang-IECR-2014"){
473 }
474 else{
475 throw teqp::InvalidArgument("Don't know what to do with this source for a&b: " + source);
476 }
477 }
478
479 if (spec.contains("names")){
480 std::vector<std::string> names = spec["names"];
481 if (kmat && static_cast<std::size_t>(kmat.value().rows()) != names.size()){
482 throw teqp::InvalidArgument("Provided length of names of " + std::to_string(names.size()) + " does not match the dimension of the kmat of " + std::to_string(kmat.value().rows()));
483 }
484 return PCSAFTMixture(names, a, b, kmat.value_or(Eigen::ArrayXXd{}));
485 }
486 else if (spec.contains("coeffs")){
487 std::vector<SAFTCoeffs> coeffs;
488 for (auto j : spec["coeffs"]) {
489 SAFTCoeffs c;
490 c.name = j.at("name");
491 c.m = j.at("m");
492 c.sigma_Angstrom = j.at("sigma_Angstrom");
493 c.epsilon_over_k = j.at("epsilon_over_k");
494 c.BibTeXKey = j.at("BibTeXKey");
495 if (j.contains("(mu^*)^2") && j.contains("nmu")){
496 c.mustar2 = j.at("(mu^*)^2");
497 c.nmu = j.at("nmu");
498 }
499 if (j.contains("(Q^*)^2") && j.contains("nQ")){
500 c.Qstar2 = j.at("(Q^*)^2");
501 c.nQ = j.at("nQ");
502 }
503 coeffs.push_back(c);
504 }
505 if (kmat && static_cast<std::size_t>(kmat.value().rows()) != coeffs.size()){
506 throw teqp::InvalidArgument("Provided length of coeffs of " + std::to_string(coeffs.size()) + " does not match the dimension of the kmat of " + std::to_string(kmat.value().rows()));
507 }
508 return PCSAFTMixture(coeffs, a, b, kmat.value_or(Eigen::ArrayXXd{}));
509 }
510 else{
511 throw std::invalid_argument("you must provide names or coeffs, but not both");
512 }
513}
515
516}; // namespace teqp::saft
517
518namespace teqp::PCSAFT{
519using namespace teqp::saft::pcsaft;
520}
const Eigen::ArrayX< double > sigma_Angstrom
Definition pcsaft.hpp:192
auto eval(const TTYPE &T, const RhoType &rhomolar, const VecType &mole_fractions) const
Definition pcsaft.hpp:205
const Eigen::ArrayXXd kmat
binary interaction parameter matrix
Definition pcsaft.hpp:194
const Eigen::ArrayX< double > epsilon_over_k
depth of pair potential divided by Boltzman constant
Definition pcsaft.hpp:193
PCSAFTHardChainContribution & operator=(const PCSAFTHardChainContribution &)=delete
PCSAFTHardChainContribution(const Eigen::ArrayX< double > &m, const Eigen::ArrayX< double > &mminus1, const Eigen::ArrayX< double > &sigma_Angstrom, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::ArrayXXd &kmat, const Eigen::Array< double, 3, 7 > &a, const Eigen::Array< double, 3, 7 > &b)
Definition pcsaft.hpp:199
Eigen::Array< double, 3, 7 > b
The universal constants used in Eqn. A.19 of G&S.
Definition pcsaft.hpp:196
Eigen::Array< double, 3, 7 > a
The universal constants used in Eqn. A.18 of G&S.
Definition pcsaft.hpp:195
const Eigen::ArrayX< double > m
number of segments
Definition pcsaft.hpp:190
const Eigen::ArrayX< double > mminus1
m-1
Definition pcsaft.hpp:191
Manager class for PCSAFT coefficients.
Definition pcsaft.hpp:49
auto get_coeffs(const std::vector< std::string > &names)
Definition pcsaft.hpp:75
void insert_normal_fluid(const std::string &name, double m, const double sigma_Angstrom, const double epsilon_over_k, const std::string &BibTeXKey)
Definition pcsaft.hpp:57
const auto & get_normal_fluid(const std::string &name)
Definition pcsaft.hpp:66
auto build_hardchain(const std::vector< SAFTCoeffs > &coeffs, const Eigen::Array< double, 3, 7 > &a, const Eigen::Array< double, 3, 7 > &b)
Definition pcsaft.hpp:334
std::vector< std::string > names
Definition pcsaft.hpp:312
auto get_coeffs_from_names(const std::vector< std::string > &the_names)
Definition pcsaft.hpp:330
std::optional< PCSAFTQuadrupolarContribution > quadrupolar
Definition pcsaft.hpp:317
PCSAFTMixture(const std::vector< std::string > &names, const Eigen::Array< double, 3, 7 > &a=teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::a, const Eigen::Array< double, 3, 7 > &b=teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::b, const Eigen::ArrayXXd &kmat={})
Definition pcsaft.hpp:391
Eigen::ArrayX< double > epsilon_over_k
depth of pair potential divided by Boltzman constant
Definition pcsaft.hpp:311
std::optional< PCSAFTDipolarContribution > dipolar
Definition pcsaft.hpp:316
Eigen::ArrayX< double > m
number of segments
Definition pcsaft.hpp:308
Eigen::ArrayXXd kmat
binary interaction parameter matrix
Definition pcsaft.hpp:313
auto build_quadrupolar(const std::vector< SAFTCoeffs > &coeffs) -> std::optional< PCSAFTQuadrupolarContribution >
Definition pcsaft.hpp:376
Eigen::ArrayX< double > sigma_Angstrom
Definition pcsaft.hpp:310
auto alphar(const TTYPE &T, const RhoType &rhomolar, const VecType &mole_fractions) const
Definition pcsaft.hpp:434
PCSAFTMixture(const std::vector< SAFTCoeffs > &coeffs, const Eigen::Array< double, 3, 7 > &a=teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::a, const Eigen::Array< double, 3, 7 > &b=teqp::saft::PCSAFT::PCSAFTMatrices::GrossSadowski2001::b, const Eigen::ArrayXXd &kmat={})
Definition pcsaft.hpp:395
double max_rhoN(const double T, const VecType &mole_fractions) const
Definition pcsaft.hpp:419
Eigen::ArrayX< double > mminus1
m-1
Definition pcsaft.hpp:309
auto build_dipolar(const std::vector< SAFTCoeffs > &coeffs) -> std::optional< PCSAFTDipolarContribution >
Definition pcsaft.hpp:362
PCSAFTHardChainContribution hardchain
Definition pcsaft.hpp:315
teqp::saft::polar_terms::GrossVrabec::DipolarContributionGrossVrabec PCSAFTDipolarContribution
Definition pcsaft.hpp:305
std::vector< std::string > bibtex
Definition pcsaft.hpp:312
auto R(const VecType &molefrac) const
Definition pcsaft.hpp:429
teqp::saft::polar_terms::GrossVrabec::QuadrupolarContributionGross PCSAFTQuadrupolarContribution
Definition pcsaft.hpp:306
PCSAFTMixture & operator=(const PCSAFTMixture &)=delete
auto extract_names(const std::vector< SAFTCoeffs > &coeffs)
Definition pcsaft.hpp:355
void check_kmat(Eigen::Index N)
Definition pcsaft.hpp:319
const Eigen::Array< double, 3, 7 > b
Definition pcsaft.hpp:21
const Eigen::Array< double, 3, 7 > a
const Eigen::Array< double, 3, 7 > b
Definition pcsaft.hpp:24
const Eigen::Array< double, 3, 7 > a
const Eigen::Array< double, 3, 7 > b
Definition pcsaft.hpp:27
auto PCSAFTfactory(const nlohmann::json &spec)
A JSON-based factory function for the PC-SAFT model.
Definition pcsaft.hpp:455
auto sumproduct(const VecType1 &v1, const VecType2 &v2, const VecType3 &v3)
Definition pcsaft.hpp:179
auto powvec(const VecType1 &v1, NType n)
Definition pcsaft.hpp:167
auto get_alphar_hs(const VecType &zeta, const VecType2 &D)
Residual contribution to alphar from hard-sphere (Eqn. A.6)
Definition pcsaft.hpp:105
auto C1(const Eta &eta, const Mbar &mbar)
Definition pcsaft.hpp:88
auto gij_HS(const zVecType &zeta, const dVecType &d, std::size_t i, std::size_t j)
Term from Eqn. A.7.
Definition pcsaft.hpp:151
auto C2(const Eta &eta, const Mbar &mbar)
Eqn. A.31.
Definition pcsaft.hpp:96
auto build_square_matrix
auto getbaseval(const T &expr)
Definition types.hpp:90
auto pow(const double &x, const double &e)
Definition types.hpp:195
auto forceeval(T &&expr)
Definition types.hpp:52
auto get_R_gas()
< Gas constant, according to CODATA 2019, in the given number type
Definition constants.hpp:22
Coefficients for one fluid.
Definition pcsaft.hpp:36
double nQ
number of quadrupolar segments
Definition pcsaft.hpp:45
double sigma_Angstrom
[A] segment diameter
Definition pcsaft.hpp:39
double Qstar2
nondimensional, the reduced quadrupole squared
Definition pcsaft.hpp:44
double nmu
number of dipolar segments
Definition pcsaft.hpp:43
double mustar2
nondimensional, the reduced dipole moment squared
Definition pcsaft.hpp:42
double m
number of segments
Definition pcsaft.hpp:38
std::string BibTeXKey
The BibTeXKey for the reference for these coefficients.
Definition pcsaft.hpp:41
double epsilon_over_k
[K] depth of pair potential divided by Boltzman constant
Definition pcsaft.hpp:40
std::string name
Name of fluid.
Definition pcsaft.hpp:37