9#include "nlohmann/json.hpp"
35 std::map<std::string, SAFTCoeffs> coeffs;
42 void insert_normal_fluid(
const std::string& name,
double m,
const double sigma_Angstrom,
const double epsilon_over_k,
const std::string& BibTeXKey) {
49 coeffs.insert(std::pair<std::string, SAFTCoeffs>(name, coeff));
52 auto it = coeffs.find(name);
53 if (it != coeffs.end()) {
57 throw std::invalid_argument(
"Bad name:" + name);
61 std::vector<SAFTCoeffs> c;
72template <
typename Eta,
typename Mbar>
73auto C1(
const Eta& eta, Mbar mbar) {
75 + mbar * (8.0 * eta - 2.0 * eta * eta) /
pow(1.0 - eta, 4)
76 + (1.0 - mbar) * (20.0 * eta - 27.0 * eta * eta + 12.0 *
pow(eta, 3) - 2.0 *
pow(eta, 4)) /
pow((1.0 - eta) * (2.0 - eta), 2)));
79template <
typename Eta,
typename Mbar>
80auto C2(
const Eta& eta, Mbar mbar) {
82 mbar * (-4.0 * eta * eta + 20.0 * eta + 8.0) /
pow(1.0 - eta, 5)
83 + (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)
87template<
typename TYPE>
89 static Eigen::ArrayXd a_0 = (Eigen::ArrayXd(7) << 0.9105631445, 0.6361281449, 2.6861347891, -26.547362491, 97.759208784, -159.59154087, 91.297774084).finished();
90 static Eigen::ArrayXd a_1 = (Eigen::ArrayXd(7) << -0.3084016918, 0.1860531159, -2.5030047259, 21.419793629, -65.255885330, 83.318680481, -33.746922930).finished();
91 static Eigen::ArrayXd a_2 = (Eigen::ArrayXd(7) << -0.0906148351, 0.4527842806, 0.5962700728, -1.7241829131, -4.1302112531, 13.776631870, -8.6728470368).finished();
92 return forceeval(a_0.cast<TYPE>().array() + ((mbar - 1.0) / mbar) * a_1.cast<TYPE>().array() + ((mbar - 1.0) / mbar * (mbar - 2.0) / mbar) * a_2.cast<TYPE>().array()).eval();
95template<
typename TYPE>
98 static Eigen::ArrayXd b_0 = (Eigen::ArrayXd(7) << 0.7240946941, 2.2382791861, -4.0025849485, -21.003576815, 26.855641363, 206.55133841, -355.60235612).finished();
99 static Eigen::ArrayXd b_1 = (Eigen::ArrayXd(7) << -0.5755498075, 0.6995095521, 3.8925673390, -17.215471648, 192.67226447, -161.82646165, -165.20769346).finished();
100 static Eigen::ArrayXd b_2 = (Eigen::ArrayXd(7) << 0.0976883116, -0.2557574982, -9.1558561530, 20.642075974, -38.804430052, 93.626774077, -29.666905585).finished();
101 return forceeval(b_0.cast<TYPE>().array() + (mbar - 1.0) / mbar * b_1.cast<TYPE>().array() + (mbar - 1.0) / mbar * (mbar - 2.0) / mbar * b_2.cast<TYPE>().array()).eval();
104template<
typename VecType>
111 auto Upsilon = 1.0 - zeta[3];
112 return forceeval(1.0 / zeta[0] * (3.0 * zeta[1] * zeta[2] / Upsilon
113 + zeta[2] * zeta[2] * zeta[2] / zeta[3] / Upsilon / Upsilon
114 + (zeta[2] * zeta[2] * zeta[2] / (zeta[3] * zeta[3]) - zeta[0]) * log(1.0 - zeta[3])
119template<
typename zVecType,
typename dVecType>
120auto gij_HS(
const zVecType& zeta,
const dVecType& d,
121 std::size_t i, std::size_t j) {
122 auto Upsilon = 1.0 - zeta[3];
123 return forceeval(1.0 / (Upsilon)+d[i] * d[j] / (d[i] + d[j]) * 3.0 * zeta[2] /
pow(Upsilon, 2)
124 +
pow(d[i] * d[j] / (d[i] + d[j]), 2) * 2.0 *
pow(zeta[2], 2) /
pow(Upsilon, 3));
127template <
typename Eta,
typename MbarType>
128auto get_I1(
const Eta& eta, MbarType mbar) {
129 auto avec =
get_a(mbar);
130 Eta summer_I1 = 0.0, summer_etadI1deta = 0.0;
131 for (std::size_t i = 0; i < 7; ++i) {
132 auto increment = avec(i) *
powi(eta,
static_cast<int>(i));
133 summer_I1 = summer_I1 + increment;
134 summer_etadI1deta = summer_etadI1deta + increment * (i + 1.0);
139template <
typename Eta,
typename MbarType>
140auto get_I2(
const Eta& eta, MbarType mbar) {
141 auto bvec =
get_b(mbar);
142 Eta summer_I2 = 0.0 * eta, summer_etadI2deta = 0.0 * eta;
143 for (std::size_t i = 0; i < 7; ++i) {
144 auto increment = bvec(i) *
powi(eta,
static_cast<int>(i));
145 summer_I2 = summer_I2 + increment;
146 summer_etadI2deta = summer_etadI2deta + increment * (i + 1.0);
154template<
typename VecType1,
typename NType>
155auto powvec(
const VecType1& v1, NType n) {
157 for (
auto i = 0; i < v1.size(); ++i) {
158 o[i] =
pow(v1[i], n);
166template<
typename VecType1,
typename VecType2,
typename VecType3>
167auto sumproduct(
const VecType1& v1,
const VecType2& v2,
const VecType3& v3) {
168 using ResultType =
typename std::common_type_t<
decltype(v1[0]),
decltype(v2[0]),
decltype(v3[0])>;
169 return forceeval((v1.template cast<ResultType>().array() * v2.template cast<ResultType>().array() * v3.template cast<ResultType>().array()).sum());
173template<
typename NumType,
typename ProductType>
177 Eigen::ArrayX<NumType>
d;
190 const Eigen::ArrayX<double>
m,
202 template<
typename TTYPE,
typename RhoType,
typename VecType>
203 auto eval(
const TTYPE& T,
const RhoType& rhomolar,
const VecType& mole_fractions)
const {
205 Eigen::Index N =
m.size();
207 if (mole_fractions.size() != N) {
208 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 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])>>;
217 for (
auto i = 0L; i < N; ++i) {
219 for (
auto j = 0; j < N; ++j) {
227 auto mbar = (mole_fractions.template cast<TRHOType>().array()*
m.template cast<TRHOType>().array()).sum();
230 RhoType rho_A3 = rhomolar * N_A * 1e-30;
232 constexpr double MY_PI = EIGEN_PI;
233 double pi6 = (MY_PI / 6.0);
236 using ta = std::common_type_t<
decltype(pi6),
decltype(
m[0]),
decltype(c.
d[0]),
decltype(rho_A3)>;
237 std::vector<ta> zeta(4);
238 for (std::size_t n = 0; n < 4; ++n) {
240 auto dn =
pow(c.
d,
static_cast<int>(n));
241 TRHOType xmdn =
forceeval((mole_fractions.template cast<TRHOType>().array()*
m.template cast<TRHOType>().array()*dn.template cast<TRHOType>().array()).sum());
248 auto [I1, etadI1deta] =
get_I1(eta, mbar);
249 auto [I2, etadI2deta] =
get_I2(eta, mbar);
252 using tt = std::common_type_t<
decltype(zeta[0]),
decltype(c.
d[0])>;
253 Eigen::ArrayX<tt> lngii_hs(mole_fractions.size());
254 for (
auto i = 0; i < lngii_hs.size(); ++i) {
255 lngii_hs[i] = log(
gij_HS(zeta, c.
d, i, i));
262 using eta_t =
decltype(eta);
263 using hc_t =
decltype(alphar_hc);
264 using disp_t =
decltype(alphar_disp);
265 struct PCSAFTHardChainContributionTerms{
285 Eigen::ArrayX<double>
m,
293 std::optional<PCSAFTDipolarContribution>
dipolar;
300 if (
kmat.cols() == 0) {
303 else if (
kmat.cols() != N) {
304 throw teqp::InvalidArgument(
"kmat needs to be a square matrix the same size as the number of components");
314 m.resize(coeffs.size());
318 names.resize(coeffs.size());
319 bibtex.resize(coeffs.size());
321 for (
const auto &coeff : coeffs) {
326 names[i] = coeff.name;
327 bibtex[i] = coeff.BibTeXKey;
333 std::vector<std::string> names_;
334 for (
const auto& c: coeffs){
335 names_.push_back(c.name);
339 auto build_dipolar(
const std::vector<SAFTCoeffs> &coeffs) -> std::optional<PCSAFTDipolarContribution>{
340 Eigen::ArrayXd mustar2(coeffs.size()), nmu(coeffs.size());
342 for (
const auto &coeff : coeffs) {
343 mustar2[i] = coeff.mustar2;
347 if ((mustar2*nmu).cwiseAbs().sum() == 0){
353 auto build_quadrupolar(
const std::vector<SAFTCoeffs> &coeffs) -> std::optional<PCSAFTQuadrupolarContribution>{
355 Eigen::ArrayXd Qstar2(coeffs.size()), nQ(coeffs.size());
357 for (
const auto &coeff : coeffs) {
358 Qstar2[i] = coeff.Qstar2;
362 if ((Qstar2*nQ).cwiseAbs().sum() == 0){
382 std::string s = std::string(
"i m sigma / A e/kB / K \n ++++++++++++++") +
"\n";
383 for (
auto i = 0; i <
m.size(); ++i) {
384 s += std::to_string(i) +
" " + std::to_string(
m[i]) +
" " + std::to_string(
sigma_Angstrom[i]) +
" " + std::to_string(
epsilon_over_k[i]) +
"\n";
389 template<
typename VecType>
390 double max_rhoN(
const double T,
const VecType& mole_fractions)
const {
391 auto N = mole_fractions.size();
392 Eigen::ArrayX<double> d(N);
393 for (
auto i = 0; i < N; ++i) {
396 return 6 * 0.74 / EIGEN_PI / (mole_fractions*
m*
powvec(d, 3)).sum()*1e30;
399 template<
class VecType>
400 auto R(
const VecType& molefrac)
const {
401 return get_R_gas<decltype(molefrac[0])>();
404 template<
typename TTYPE,
typename RhoType,
typename VecType>
405 auto alphar(
const TTYPE& T,
const RhoType& rhomolar,
const VecType& mole_fractions)
const {
410 auto rho_A3 =
forceeval(rhomolar*N_A*1e-30);
413 auto valsdip =
dipolar.value().eval(T, rho_A3, vals.eta, mole_fractions);
418 auto valsquad =
quadrupolar.value().eval(T, rho_A3, vals.eta, mole_fractions);
427 std::optional<Eigen::ArrayXXd> kmat;
428 if (spec.contains(
"kmat") && spec.at(
"kmat").is_array() && spec.at(
"kmat").size() > 0){
432 if (spec.contains(
"names")){
433 std::vector<std::string> names = spec[
"names"];
434 if (kmat &&
static_cast<std::size_t
>(kmat.value().rows()) != names.size()){
435 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()));
437 return PCSAFTMixture(names, kmat.value_or(Eigen::ArrayXXd{}));
439 else if (spec.contains(
"coeffs")){
440 std::vector<SAFTCoeffs> coeffs;
441 for (
auto j : spec[
"coeffs"]) {
443 c.
name = j.at(
"name");
448 if (j.contains(
"(mu^*)^2") && j.contains(
"nmu")){
452 if (j.contains(
"(Q^*)^2") && j.contains(
"nQ")){
453 c.
Qstar2 = j.at(
"(Q^*)^2");
458 if (kmat &&
static_cast<std::size_t
>(kmat.value().rows()) != coeffs.size()){
459 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()));
461 return PCSAFTMixture(coeffs, kmat.value_or(Eigen::ArrayXXd{}));
464 throw std::invalid_argument(
"you must provide names or coeffs, but not both");
PCSAFTHardChainContribution & operator=(const PCSAFTHardChainContribution &)=delete
const Eigen::ArrayX< double > sigma_Angstrom
auto eval(const TTYPE &T, const RhoType &rhomolar, const VecType &mole_fractions) const
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::ArrayX< double > epsilon_over_k
depth of pair potential divided by Boltzman constant
const Eigen::ArrayXXd kmat
binary interaction parameter matrix
const Eigen::ArrayX< double > mminus1
m-1
const Eigen::ArrayX< double > m
number of segments
Manager class for PCSAFT coefficients.
void insert_normal_fluid(const std::string &name, double m, const double sigma_Angstrom, const double epsilon_over_k, const std::string &BibTeXKey)
const auto & get_normal_fluid(const std::string &name)
auto get_coeffs(const std::vector< std::string > &names)
PCSAFTMixture(const std::vector< std::string > &names, const Eigen::ArrayXXd &kmat={})
std::vector< std::string > names
auto R(const VecType &molefrac) const
auto build_hardchain(const std::vector< SAFTCoeffs > &coeffs)
Eigen::ArrayXXd kmat
binary interaction parameter matrix
auto build_dipolar(const std::vector< SAFTCoeffs > &coeffs) -> std::optional< PCSAFTDipolarContribution >
auto alphar(const TTYPE &T, const RhoType &rhomolar, const VecType &mole_fractions) const
Eigen::ArrayX< double > epsilon_over_k
depth of pair potential divided by Boltzman constant
auto get_epsilon_over_k_K() const
Eigen::ArrayX< double > sigma_Angstrom
SAFTpolar::QuadrupolarContributionGross PCSAFTQuadrupolarContribution
Eigen::ArrayX< double > m
number of segments
SAFTpolar::DipolarContributionGrossVrabec PCSAFTDipolarContribution
std::optional< PCSAFTDipolarContribution > dipolar
auto build_quadrupolar(const std::vector< SAFTCoeffs > &coeffs) -> std::optional< PCSAFTQuadrupolarContribution >
auto get_BibTeXKeys() const
auto get_coeffs_from_names(const std::vector< std::string > &the_names)
std::vector< std::string > bibtex
PCSAFTMixture(const std::vector< SAFTCoeffs > &coeffs, const Eigen::ArrayXXd &kmat={})
std::optional< PCSAFTQuadrupolarContribution > quadrupolar
void check_kmat(Eigen::Index N)
Eigen::ArrayX< double > mminus1
m-1
PCSAFTHardChainContribution hardchain
auto get_sigma_Angstrom() const
auto extract_names(const std::vector< SAFTCoeffs > &coeffs)
double max_rhoN(const double T, const VecType &mole_fractions) const
PCSAFTMixture & operator=(const PCSAFTMixture &)=delete
Parameters for model evaluation.
ProductType m2_epsilon2_sigma3_bar
Eq. A. 13.
Eigen::ArrayX< NumType > d
ProductType m2_epsilon_sigma3_bar
Eq. A. 12.
auto powvec(const VecType1 &v1, NType n)
auto get_I2(const Eta &eta, MbarType mbar)
Eqn. A.17, Eqn. A.30.
auto get_I1(const Eta &eta, MbarType mbar)
Eqn. A.16, Eqn. A.29.
auto gij_HS(const zVecType &zeta, const dVecType &d, std::size_t i, std::size_t j)
Term from Eqn. A.7.
auto get_a(TYPE mbar)
Eqn. A.18.
auto C1(const Eta &eta, Mbar mbar)
auto C2(const Eta &eta, Mbar mbar)
Eqn. A.31.
auto PCSAFTfactory(const nlohmann::json &spec)
A JSON-based factory function for the PC-SAFT model.
auto sumproduct(const VecType1 &v1, const VecType2 &v2, const VecType3 &v3)
auto get_b(TYPE mbar)
Eqn. A.19.
auto get_alphar_hs(const VecType &zeta)
Residual contribution to alphar from hard-sphere (Eqn. A.6)
auto getbaseval(const T &expr)
T powi(const T &x, int n)
From Ulrich Deiters.
auto pow(const double &x, const double &e)
Coefficients for one fluid.
double epsilon_over_k
[K] depth of pair potential divided by Boltzman constant
double Qstar2
nondimensional, the reduced quadrupole squared
std::string BibTeXKey
The BibTeXKey for the reference for these coefficients.
std::string name
Name of fluid.
double mustar2
nondimensional, the reduced dipole moment squared
double m
number of segments
double nQ
number of quadrupolar segments
double nmu
number of dipolar segments
double sigma_Angstrom
[A] segment diameter