47class SAFTVRMieLibrary {
48 std::map<std::string, SAFTVRMieCoeffs> coeffs;
51 insert_normal_fluid(
"Methane", 1.0000, 3.7412e-10, 153.36, 12.650, 6,
"Lafitte-JCP-2001");
52 insert_normal_fluid(
"Ethane", 1.4373, 3.7257e-10, 206.12, 12.400, 6,
"Lafitte-JCP-2001");
53 insert_normal_fluid(
"Propane", 1.6845, 3.9056e-10, 239.89, 13.006, 6,
"Lafitte-JCP-2001");
55 void insert_normal_fluid(
const std::string& name,
double m,
const double sigma_m,
const double epsilon_over_k,
const double lambda_r,
const double lambda_a,
const std::string& BibTeXKey) {
56 SAFTVRMieCoeffs coeff;
59 coeff.sigma_m = sigma_m;
60 coeff.epsilon_over_k = epsilon_over_k;
61 coeff.lambda_r = lambda_r;
62 coeff.lambda_a = lambda_a;
63 coeff.BibTeXKey = BibTeXKey;
64 coeffs.insert(std::pair<std::string, SAFTVRMieCoeffs>(name, coeff));
66 const auto& get_normal_fluid(
const std::string& name) {
67 auto it = coeffs.find(name);
68 if (it != coeffs.end()) {
72 throw std::invalid_argument(
"Bad name:" + name);
75 auto get_coeffs(
const std::vector<std::string>& names){
76 std::vector<SAFTVRMieCoeffs> c;
78 c.push_back(get_normal_fluid(n));
89 const Eigen::Matrix<double, 7, 7> phi{(Eigen::Matrix<double, 7, 7>() <<
90 7.5365557, -359.44, 1550.9, -1.19932, -1911.28, 9236.9, 10,
91 -37.60463, 1825.6, -5070.1, 9.063632, 21390.175, -129430, 10,
92 71.745953, -3168.0, 6534.6, -17.9482, -51320.7, 357230, 0.57,
93 -46.83552, 1884.2, -3288.7, 11.34027, 37064.54, -315530, -6.7,
94 -2.467982, -0.82376, -2.7171, 20.52142, 1103.742, 1390.2, -8,
95 -0.50272, -3.1935, 2.0883, -56.6377, -3264.61, -4518.2, 0,
96 8.0956883, 3.7090, 0, 40.53683, 2556.181, 4241.6, 0 ).finished()};
99 const Eigen::Matrix<double, 4, 4> A{(Eigen::Matrix<double, 4, 4>() <<
100 0.81096, 1.7888, -37.578, 92.284,
101 1.0205, -19.341, 151.26, -463.50,
102 -1.9057, 22.845, -228.14, 973.92,
103 1.0885, -6.1962, 106.98, -677.64).finished()};
106 auto get_lambda_k_ij(
const Eigen::ArrayXd& lambda_k)
const{
107 Eigen::ArrayXXd mat(
N,
N);
108 for (
auto i = 0; i < lambda_k.size(); ++i){
109 for (
auto j = i; j < lambda_k.size(); ++j){
110 mat(i,j) = 3 + sqrt((lambda_k(i)-3)*(lambda_k(j)-3));
118 auto get_C_ij()
const{
119 Eigen::ArrayXXd C(
N,
N);
120 for (
auto i = 0U; i <
N; ++i){
121 for (
auto j = i; j <
N; ++j){
122 C(i,j) =
lambda_r_ij(i,j)/(
lambda_r_ij(i,j)-
lambda_a_ij(i,j))*
pow(
lambda_r_ij(i,j)/
lambda_a_ij(i,j),
lambda_a_ij(i,j)/(
lambda_r_ij(i,j)-
lambda_a_ij(i,j)));
130 auto get_fkij()
const{
131 std::vector<Eigen::ArrayXXd> f_(8);
132 for (
auto k = 1; k < 8; ++k){
135 for (
auto k = 1; k < 8; ++k){
136 auto phik = phi.col(k-1);
137 Eigen::ArrayXXd num(
N,
N), den(
N,
N); num.setZero(), den.setZero();
138 for (
auto n = 0; n < 4; ++n){
141 for (
auto n = 4; n < 7; ++n){
144 f_[k] = num/(1 + den);
150 auto get_sigma_ij()
const{
151 Eigen::ArrayXXd sigma(
N,
N);
152 for (
auto i = 0U; i <
N; ++i){
153 for (
auto j = i; j <
N; ++j){
155 sigma(j,i) = sigma(i,j);
164 auto get_epsilon_ij()
const{
167 Eigen::ArrayXXd eps_(
N,
N);
168 for (
auto i = 0U; i <
N; ++i){
169 for (
auto j = i; j <
N; ++j){
171 eps_(j,i) = eps_(i,j);
177 Eigen::ArrayXXd eps_(
N,
N);
178 for (
auto i = 0U; i <
N; ++i){
179 for (
auto j = i; j <
N; ++j){
181 eps_(j,i) = eps_(i,j);
187 throw std::invalid_argument(
"epsilon_ij_flag is invalid");
192 if (sizes.maxCoeff() != sizes.minCoeff()){
199 auto get_cij(
const Eigen::ArrayXXd& lambdaij)
const{
200 std::vector<Eigen::ArrayXXd> cij(4);
201 for (
auto n = 0; n < 4; ++n){
204 for (
auto i = 0U; i <
N; ++i){
205 for (
auto j = i; j <
N; ++j){
206 using CV = Eigen::Vector<double, 4>;
207 const CV b{(CV() << 1, 1.0/lambdaij(i,j), 1.0/
pow(lambdaij(i,j),2), 1.0/
pow(lambdaij(i,j),3)).finished()};
208 auto c1234 = (A*b).eval();
209 cij[0](i,j) = c1234(0);
210 cij[1](i,j) = c1234(1);
211 cij[2](i,j) = c1234(2);
212 cij[3](i,j) = c1234(3);
219 auto get_canij()
const{
223 auto get_c2anij()
const{
227 auto get_crnij()
const{
231 auto get_c2rnij()
const{
235 auto get_carnij()
const{
239 EpsilonijFlags get_epsilon_ij(
const std::optional<nlohmann::json>& flags){
241 const nlohmann::json& j = flags.value();
242 if (j.contains(
"epsilon_ij")){
255 const Eigen::Index
N;
262 const std::vector<Eigen::ArrayXXd>
fkij;
265 const Eigen::ArrayXd&
m,
267 const Eigen::ArrayXd& sigma_m,
270 const Eigen::ArrayXXd&
kmat,
271 const std::optional<nlohmann::json> & flags = std::nullopt)
289 template<
typename RType>
296 template <
typename TType>
299 auto EPS = std::numeric_limits<
decltype(
getbaseval(T))>::epsilon();
305 auto fgh = [&kappa, &lambda_r_, &lambda_a_, &T, &EPS](
auto j){
306 auto jlr =
pow(j, lambda_r_), jla =
pow(j, lambda_a_);
307 auto u = kappa*(jlr - jla);
308 auto uprime = kappa*(lambda_r_*jlr - lambda_a_*jla)/j;
309 auto uprime2 = kappa*(lambda_r_*(lambda_r_-1.0)*jlr - lambda_a_*(lambda_a_-1.0)*jla)/(j*j);
313 for (
auto counter = 0; counter <= 3; ++counter){
315 auto [R, Rprime, Rprime2] = fgh(j);
316 auto denominator = 2.0*Rprime*Rprime-R*Rprime2;
320 j -= 2.0*R*Rprime/denominator;
361 template <
typename TType>
362 TType
get_dii(std::size_t i,
const TType &T)
const{
363 std::function<TType(TType)> integrand = [
this, i, &T](
const TType& r){
369 auto integral_contribution = quad<10, TType, TType>(integrand, rcut,
sigma_A[i]);
370 auto d =
forceeval(rcut + integral_contribution);
378 template <
typename TType>
380 Eigen::Array<TType, Eigen::Dynamic, Eigen::Dynamic> d(
N,
N);
382 for (
auto i = 0U; i <
N; ++i){
386 for (
auto i = 0U; i <
N; ++i){
387 for (
auto j = i+1; j <
N; ++j){
388 d(i,j) = (d(i,i) + d(j,j))/2.0;
395 template <
typename TType,
typename RhoType,
typename VecType>
396 auto get_core_calcs(
const TType& T,
const RhoType& rhomolar,
const VecType& molefracs)
const{
398 if (molefracs.size() !=
N){
399 throw teqp::InvalidArgument(
"Length of molefracs of "+std::to_string(molefracs.size()) +
" does not match the model size of"+std::to_string(
N));
402 using FracType = std::decay_t<
decltype(molefracs[0])>;
403 using NumType = std::common_type_t<TType, RhoType, FracType>;
412 auto xs =
forceeval((
m*molefracs/mbar).eval());
414 constexpr double MY_PI =
static_cast<double>(EIGEN_PI);
417 using TRHOType = std::common_type_t<std::decay_t<TType>, std::decay_t<RhoType>, std::decay_t<
decltype(molefracs[0])>, std::decay_t<
decltype(
m[0])>>;
418 Eigen::Array<TRHOType, 4, 1> zeta;
419 for (
auto l = 0; l < 4; ++l){
420 TRHOType summer = 0.0;
421 for (
auto i = 0U; i <
N; ++i){
422 summer += xs(i)*
powi(dmat(i,i), l);
427 NumType summer_zeta_x = 0.0;
428 TRHOType summer_zeta_x_bar = 0.0;
429 for (
auto i = 0U; i <
N; ++i){
430 for (
auto j = 0U; j <
N; ++j){
431 summer_zeta_x += xs(i)*xs(j)*
powi(dmat(i,j), 3)*rhos;
432 summer_zeta_x_bar += xs(i)*xs(j)*
powi(
sigma_ij(i,j), 3);
436 auto zeta_x =
forceeval(pi6*summer_zeta_x);
437 auto zeta_x_bar =
forceeval(pi6*rhos*summer_zeta_x_bar);
445 auto k0 =
forceeval(-log(1.0-zeta_x) + (42.0*zeta_x - 39.0*
POW2(zeta_x) + 9.0*
POW3(zeta_x) - 2.0*
POW4(zeta_x))/(6.0*X3));
451 auto dmat3 = dmat.array().cube().eval();
456 NumType alphar_chain = 0.0;
458 NumType K_HS =
get_KHS(zeta_x);
461 for (
auto i = 0U; i <
N; ++i){
462 for (
auto j = i; j <
N; ++j){
463 NumType x_0_ij =
sigma_ij(i,j)/dmat(i, j);
469 auto I = [&x_0_ij](
double lambda_ij){
470 return forceeval(-(
pow(x_0_ij, 3-lambda_ij)-1.0)/(lambda_ij-3.0));
472 auto J = [&x_0_ij](
double lambda_ij){
473 return forceeval(-(
pow(x_0_ij, 4-lambda_ij)*(lambda_ij-3.0)-
pow(x_0_ij, 3.0-lambda_ij)*(lambda_ij-4.0)-1.0)/((lambda_ij-3.0)*(lambda_ij-4.0)));
481 auto one_term = [
this, &x_0_ij, &I, &J, &zeta_x, &
X](
double lambda_ij,
const NumType& zeta_x_eff){
483 pow(x_0_ij, lambda_ij)*(
484 this->
get_Bhatij(zeta_x,
X, I(lambda_ij), J(lambda_ij))
494 NumType a1ij = 2.0*MY_PI*rhos*dmat3(i,j)*
epsilon_ij(i,j)*
C_ij(i,j)*(
498 NumType contribution = xs(i)*xs(j)*a1ij;
499 double factor = (i == j) ? 1.0 : 2.0;
500 a1kB += contribution*factor;
513 NumType chi_ij =
fkij[1](i,j)*zeta_x_bar +
fkij[2](i,j)*zeta_x_bar5 +
fkij[3](i,j)*zeta_x_bar8;
520 NumType contributiona2 = xs(i)*xs(j)*a2ij;
521 a2kB2 += contributiona2*factor;
529 NumType contributiona3 = xs(i)*xs(j)*a3ij;
530 a3kB3 += contributiona3*factor;
538 auto gdHSii = exp(k0 + k1*x_0_ij + k2*
POW2(x_0_ij) + k3*
POW3(x_0_ij));
545 auto g1_term = [&one_term](
double lambda_ij,
const NumType& zeta_x_eff){
546 return forceeval(lambda_ij*one_term(lambda_ij, zeta_x_eff));
553 auto rhosda1iidrhos_term = [
this, &x_0_ij, &I, &J, &zeta_x, &
X](
double lambda_ij,
const NumType& zeta_x_eff,
const NumType& dzetaxeff_dzetax,
const NumType& Bhatij){
554 auto I_ = I(lambda_ij);
555 auto J_ = J(lambda_ij);
556 auto rhosda1Sdrhos = this->
get_rhoda1Shatijdrho(zeta_x, zeta_x_eff, dzetaxeff_dzetax, lambda_ij);
558 return forceeval(
pow(x_0_ij, lambda_ij)*(rhosda1Sdrhos + rhosdBdrhos));
561 auto da1iidrhos_term =
C_ij(i,j)*(
562 rhosda1iidrhos_term(
lambda_a_ij(i,i), zeta_x_eff_a, dzeta_x_eff_dzetax_a, Bhatij_a)
563 -rhosda1iidrhos_term(
lambda_r_ij(i,i), zeta_x_eff_r, dzeta_x_eff_dzetax_r, Bhatij_r)
565 auto g1ii = 3.0*da1iidrhos_term + g1_noderivterm;
572 auto g2_noderivterm = -
POW2(
C_ij(i,i))*K_HS*(
578 auto da2iidrhos_term = 0.5*
POW2(
C_ij(i,j))*(
584 rhosda1iidrhos_term(2.0*
lambda_a_ij(i,i), zeta_x_eff_2a, dzeta_x_eff_dzetax_2a, Bhatij_2a)
585 -2.0*rhosda1iidrhos_term(
lambda_a_ij(i,i)+
lambda_r_ij(i,i), zeta_x_eff_ar, dzeta_x_eff_dzetax_ar, Bhatij_ar)
586 +rhosda1iidrhos_term(2.0*
lambda_r_ij(i,i), zeta_x_eff_2r, dzeta_x_eff_dzetax_2r, Bhatij_2r)
589 auto g2MCAij = 3.0*da2iidrhos_term + g2_noderivterm;
592 auto theta = exp(betaepsilon)-1.0;
593 auto phi7 = phi.col(6);
594 auto gamma_cij = phi7(0)*(-tanh(phi7(1)*(phi7(2)-
alpha_ij(i,j)))+1.0)*zeta_x_bar*theta*exp(phi7(3)*zeta_x_bar + phi7(4)*
POW2(zeta_x_bar));
595 auto g2ii = (1.0+gamma_cij)*g2MCAij;
597 NumType giiMie = gdHSii*exp((betaepsilon*g1ii +
POW2(betaepsilon)*g2ii)/gdHSii);
598 alphar_chain -= molefracs[i]*(
m[i]-1.0)*log(giiMie);
605 auto alphar_mono =
forceeval(mbar*(ahs + a1kB/T + a2kB2/(T*T) + a3kB3/(T*T*T)));
607 using dmat_t =
decltype(dmat);
608 using rhos_t =
decltype(rhos);
609 using rhoN_t =
decltype(rhoN);
610 using mbar_t =
decltype(mbar);
611 using xs_t =
decltype(xs);
612 using zeta_t =
decltype(zeta);
613 using zeta_x_t =
decltype(zeta_x);
614 using zeta_x_bar_t =
decltype(zeta_x_bar);
615 using alphar_mono_t =
decltype(alphar_mono);
616 using a1kB_t =
decltype(a1kB);
617 using a2kB2_t =
decltype(a2kB2);
618 using a3kB3_t =
decltype(a3kB3);
619 using alphar_chain_t =
decltype(alphar_chain);
628 zeta_x_bar_t zeta_x_bar;
629 alphar_mono_t alphar_mono;
633 alphar_chain_t alphar_chain;
635 return vals{dmat, rhos, rhoN, mbar, xs, zeta, zeta_x, zeta_x_bar, alphar_mono, a1kB, a2kB2, a3kB3, alphar_chain};
639 template<
typename RhoType>
641 return forceeval(
pow(1.0-pf,4)/(1.0 + 4.0*pf + 4.0*pf*pf - 4.0*pf*pf*pf + pf*pf*pf*pf));
649 template<
typename RhoType>
651 auto num = -4.0*
POW3(zeta_x - 1.0)*(
POW2(zeta_x) - 5.0*zeta_x - 2.0);
652 auto den =
POW2(
POW4(zeta_x) - 4.0*
POW3(zeta_x) + 4.0*
POW2(zeta_x) + 4.0*zeta_x + 1.0);
657 template<
typename RhoType,
typename ZetaType>
658 auto get_a_HS(
const RhoType& rhos,
const Eigen::Array<ZetaType, 4, 1>& zeta)
const{
659 constexpr double MY_PI =
static_cast<double>(EIGEN_PI);
665 return forceeval(6.0/(MY_PI*rhos)*(3.0*zeta[1]*zeta[2]/(1.0-zeta[3]) +
POW3(zeta[2])/(zeta[3]*
POW2(1.0-zeta[3])) + (
POW3(zeta[2])/
POW2(zeta[3])-zeta[0])*log(1.0-zeta[3])));
677 template<
typename ZetaType,
typename IJ>
678 auto get_Bhatij(
const ZetaType& zeta_x,
const ZetaType& one_minus_zeta_x3,
const IJ& I,
const IJ& J)
const{
680 (1.0-zeta_x/2.0)/one_minus_zeta_x3*I - 9.0*zeta_x*(1.0+zeta_x)/(2.0*one_minus_zeta_x3)*J
696 template<
typename ZetaType,
typename IJ>
697 auto get_rhodBijdrho(
const ZetaType& zeta_x,
const ZetaType& ,
const IJ& I,
const IJ& J,
const ZetaType& Bhatij)
const{
698 auto dBhatdzetax = (-3.0*I*(zeta_x - 2.0) - 27.0*J*zeta_x*(zeta_x + 1.0) + (zeta_x - 1.0)*(I + 9.0*J*zeta_x + 9.0*J*(zeta_x + 1.0)))/(2.0*
POW4(1.0-zeta_x));
699 return forceeval(Bhatij + dBhatdzetax*zeta_x);
713 template<
typename ZetaType>
716 -1.0/(lambda_ij-3.0)*(1.0-zeta_x_eff/2.0)/
POW3(
forceeval(1.0-zeta_x_eff))
731 template<
typename ZetaType>
732 auto get_rhoda1Shatijdrho(
const ZetaType& zeta_x,
const ZetaType& zeta_x_eff,
const ZetaType& dzetaxeffdzetax,
double lambda_ij)
const{
733 auto zetaxda1Shatdzetax = ((2.0*zeta_x_eff - 5.0)*dzetaxeffdzetax)/(2.0*(lambda_ij-3)*
POW4(zeta_x_eff-1.0))*zeta_x;
744 std::vector<std::string> names, bibtex;
746 const std::optional<SAFTpolar::multipolar_contributions_variant> polar;
748 static void check_kmat(
const Eigen::ArrayXXd& kmat, Eigen::Index N) {
749 if (kmat.size() == 0){
752 if (kmat.cols() != kmat.rows()) {
755 if (kmat.cols() != N) {
756 throw teqp::InvalidArgument(
"kmat needs to be a square matrix the same size as the number of components");
759 static auto get_coeffs_from_names(
const std::vector<std::string> &names){
760 SAFTVRMieLibrary library;
761 return library.get_coeffs(names);
763 auto get_names(
const std::vector<SAFTVRMieCoeffs> &coeffs){
764 std::vector<std::string> names_;
765 for (
auto c : coeffs){
766 names_.push_back(c.name);
770 auto get_bibtex(
const std::vector<SAFTVRMieCoeffs> &coeffs){
771 std::vector<std::string> keys_;
772 for (
auto c : coeffs){
773 keys_.push_back(c.BibTeXKey);
778 static auto build_chain(
const std::vector<SAFTVRMieCoeffs> &coeffs,
const std::optional<Eigen::ArrayXXd>& kmat,
const std::optional<nlohmann::json>& flags = std::nullopt){
780 check_kmat(kmat.value(), coeffs.size());
782 const std::size_t N = coeffs.size();
783 Eigen::ArrayXd m(N), epsilon_over_k(N), sigma_m(N), lambda_r(N), lambda_a(N);
785 for (
const auto &coeff : coeffs) {
787 epsilon_over_k[i] = coeff.epsilon_over_k;
788 sigma_m[i] = coeff.sigma_m;
789 lambda_r[i] = coeff.lambda_r;
790 lambda_a[i] = coeff.lambda_a;
797 auto mat = Eigen::ArrayXXd::Zero(N,N);
801 auto build_polar(
const std::vector<SAFTVRMieCoeffs> &coeffs) ->
decltype(this->polar){
802 Eigen::ArrayXd mustar2(coeffs.size()), nmu(coeffs.size()), Qstar2(coeffs.size()), nQ(coeffs.size());
804 for (
const auto &coeff : coeffs) {
805 mustar2[i] = coeff.mustar2;
807 Qstar2[i] = coeff.Qstar2;
811 bool has_dipolar = ((mustar2*nmu).cwiseAbs().sum() == 0);
812 bool has_quadrupolar = ((Qstar2*nQ).cwiseAbs().sum() == 0);
813 if (!has_dipolar && !has_quadrupolar){
823 SAFTVRMieMixture(
const std::vector<std::string> &names,
const std::optional<Eigen::ArrayXXd>& kmat = std::nullopt,
const std::optional<nlohmann::json>&flags = std::nullopt) :
SAFTVRMieMixture(get_coeffs_from_names(names), kmat, flags){};
824 SAFTVRMieMixture(
const std::vector<SAFTVRMieCoeffs> &coeffs,
const std::optional<Eigen::ArrayXXd> &kmat = std::nullopt,
const std::optional<nlohmann::json>&flags = std::nullopt) : names(
get_names(coeffs)), bibtex(get_bibtex(coeffs)), terms(
build_chain(coeffs, kmat, flags)), polar(
build_polar(coeffs)) {};
825 SAFTVRMieMixture(
SAFTVRMieChainContributionTerms&& terms,
const std::vector<SAFTVRMieCoeffs> &coeffs, std::optional<SAFTpolar::multipolar_contributions_variant> &&polar = std::nullopt) : names(
get_names(coeffs)), bibtex(get_bibtex(coeffs)), terms(std::move(terms)), polar(std::move(polar)) {};
831 auto chain_factory(
const std::vector<SAFTVRMieCoeffs> &coeffs,
const std::optional<Eigen::ArrayXXd>& kmat){
841 auto get_core_calcs(
double T,
double rhomolar,
const Eigen::ArrayXd& mole_fractions)
const {
844 auto fromArrayX = [](
const Eigen::ArrayXd &x){std::valarray<double>n(x.size());
for (
auto i = 0U; i < n.size(); ++i){ n[i] = x[i];}
return n;};
845 auto fromArrayXX = [](
const Eigen::ArrayXXd &x){
846 std::size_t N = x.rows();
847 std::vector<std::vector<double>> n; n.resize(N);
848 for (
auto i = 0U; i < N; ++i){
850 for (
auto j = 0U; j < N; ++j){
856 return nlohmann::json{
857 {
"dmat", fromArrayXX(val.dmat)},
861 {
"xs", fromArrayX(val.xs)},
862 {
"zeta", fromArrayX(val.zeta)},
863 {
"zeta_x", val.zeta_x},
864 {
"zeta_x_bar", val.zeta_x_bar},
865 {
"alphar_mono", val.alphar_mono},
867 {
"a2kB2", val.a2kB2},
868 {
"a3kB3", val.a3kB3},
869 {
"alphar_chain", val.alphar_chain}
894 template<
class VecType>
895 auto R(
const VecType& molefrac)
const {
896 return get_R_gas<decltype(molefrac[0])>();
899 template<
typename TTYPE,
typename RhoType,
typename VecType>
900 auto alphar(
const TTYPE& T,
const RhoType& rhomolar,
const VecType& mole_fractions)
const {
904 using type = std::common_type_t<TTYPE, RhoType,
decltype(mole_fractions[0])>;
905 type
alphar = vals.alphar_mono + vals.alphar_chain;
906 type packing_fraction = vals.zeta[3];
910 auto visitor = [&T, &rhomolar, &mole_fractions, &packing_fraction](
const auto& contrib) -> type {
912 constexpr mas arg_spec = std::decay_t<
decltype(contrib)>::arg_spec;
913 if constexpr(arg_spec == mas::TK_rhoNA3_packingfraction_molefractions){
914 RhoType rho_A3 = rhomolar*N_A*1e-30;
915 type alpha = contrib.eval(T, rho_A3, packing_fraction, mole_fractions).alpha;
918 else if constexpr(arg_spec == mas::TK_rhoNm3_rhostar_molefractions){
919 RhoType rhoN_m3 = rhomolar*N_A;
920 auto rhostar = contrib.get_rhostar(rhoN_m3, packing_fraction, mole_fractions);
921 return contrib.eval(T, rhoN_m3, rhostar, mole_fractions).alpha;
927 alphar += std::visit(visitor, polar.value());
936 std::optional<Eigen::ArrayXXd> kmat;
937 if (spec.contains(
"kmat") && spec.at(
"kmat").is_array() && spec.at(
"kmat").size() > 0){
941 if (spec.contains(
"names")){
942 std::vector<std::string> names = spec[
"names"];
943 if (kmat &&
static_cast<std::size_t
>(kmat.value().rows()) != names.size()){
944 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()));
948 else if (spec.contains(
"coeffs")){
949 bool something_polar =
false;
950 std::vector<SAFTVRMieCoeffs> coeffs;
951 for (
auto j : spec[
"coeffs"]) {
953 c.
name = j.at(
"name");
955 c.
sigma_m = (j.contains(
"sigma_m")) ? j.at(
"sigma_m").get<
double>() : j.at(
"sigma_Angstrom").get<
double>()/1e10;
962 if (j.contains(
"(mu^*)^2") && j.contains(
"nmu")){
965 something_polar =
true;
967 if (j.contains(
"(Q^*)^2") && j.contains(
"nQ")){
968 c.
Qstar2 = j.at(
"(Q^*)^2");
970 something_polar =
true;
972 if (j.contains(
"Q_Cm2") || j.contains(
"Q_DA") || j.contains(
"mu_Cm") || j.contains(
"mu_D")){
973 something_polar =
true;
977 if (kmat &&
static_cast<std::size_t
>(kmat.value().rows()) != coeffs.size()){
978 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()));
981 if (!something_polar){
987 std::string polar_model =
"GrossVrabec";
988 if (spec.contains(
"polar_model")){
989 polar_model = spec[
"polar_model"];
991 std::optional<nlohmann::json> SAFTVRMie_flags = std::nullopt;
992 if (spec.contains(
"SAFTVRMie_flags")){
993 SAFTVRMie_flags = spec[
"SAFTVRMie_flags"];
995 std::optional<nlohmann::json> polar_flags = std::nullopt;
996 if (spec.contains(
"polar_flags")){
997 polar_flags = spec[
"polar_flags"];
1001 polar_flags = nlohmann::json{{
"approach",
"use_packing_fraction"}};
1006 const double D_to_Cm = 3.33564e-30;
1007 const double mustar2factor = 1.0/(4*
static_cast<double>(EIGEN_PI)*8.8541878128e-12*1.380649e-23);
1008 const double Qstar2factor = 1.0/(4*
static_cast<double>(EIGEN_PI)*8.8541878128e-12*1.380649e-23);
1009 auto N = coeffs.size();
1010 Eigen::ArrayXd ms(N), epsks(N), sigma_ms(N), mu_Cm(N), Q_Cm2(N), nQ(N), nmu(N);
1012 for (
auto j : spec[
"coeffs"]) {
1013 double m = j.at(
"m");
1014 double sigma_m = (j.contains(
"sigma_m")) ? j.at(
"sigma_m").get<
double>() : j.at(
"sigma_Angstrom").get<
double>()/1e10;
1015 double epsilon_over_k = j.at(
"epsilon_over_k");
1016 auto get_dipole_Cm = [&]() ->
double {
1017 if (j.contains(
"(mu^*)^2") && j.contains(
"nmu")){
1019 double mustar2 = j.at(
"(mu^*)^2");
1020 return sqrt(mustar2*(m*epsilon_over_k*
pow(sigma_m, 3))/mustar2factor);
1022 else if (j.contains(
"mu_Cm")){
1023 return j.at(
"mu_Cm");
1025 else if (j.contains(
"mu_D")){
1026 return j.at(
"mu_D").get<
double>()*D_to_Cm;
1032 auto get_quadrupole_Cm2 = [&]() ->
double{
1033 if (j.contains(
"(Q^*)^2") && j.contains(
"nQ")){
1035 double Qstar2 = j.at(
"(Q^*)^2");
1036 return sqrt(Qstar2*(m*epsilon_over_k*
pow(sigma_m, 5))/Qstar2factor);
1038 else if (j.contains(
"Q_Cm2")){
1039 return j.at(
"Q_Cm2");
1041 else if (j.contains(
"Q_DA")){
1042 return j.at(
"Q_DA").get<
double>()*D_to_Cm/1e10;
1048 ms(i) = m; sigma_ms(i) = sigma_m; epsks(i) = epsilon_over_k; mu_Cm(i) = get_dipole_Cm(); Q_Cm2(i) = get_quadrupole_Cm2();
1049 nmu(i) = (j.contains(
"nmu") ? j[
"nmu"].get<
double>() : 0.0);
1050 nQ(i) = (j.contains(
"nQ") ? j[
"nQ"].get<
double>() : 0.0);
1053 nlohmann::json SAFTVRMieFlags;
1056 auto EPSKIJ = chain.get_EPSKIJ_K_matrix();
1057 auto SIGMAIJ = chain.get_SIGMAIJ_m_matrix();
1059 using namespace SAFTpolar;
1060 if (polar_model ==
"GrossVrabec"){
1061 auto mustar2 = (mustar2factor*mu_Cm.pow(2)/(ms*epsks*sigma_ms.pow(3))).eval();
1062 auto Qstar2 = (Qstar2factor*Q_Cm2.pow(2)/(ms*epsks*sigma_ms.pow(5))).eval();
1063 auto polar = MultipolarContributionGrossVrabec(ms, sigma_ms*1e10, epsks, mustar2, nmu, Qstar2, nQ);
1066 if (polar_model ==
"GubbinsTwu+Luckas"){
1067 using MCGTL = MultipolarContributionGubbinsTwu<LuckasJIntegral, LuckasKIntegral>;
1068 auto mubar2 = (mustar2factor*mu_Cm.pow(2)/(epsks*sigma_ms.pow(3))).eval();
1069 auto Qbar2 = (Qstar2factor*Q_Cm2.pow(2)/(epsks*sigma_ms.pow(5))).eval();
1070 auto polar = MCGTL(sigma_ms, epsks, mubar2, Qbar2, multipolar_rhostar_approach::use_packing_fraction);
1073 if (polar_model ==
"GubbinsTwu+GubbinsTwu"){
1074 using MCGG = MultipolarContributionGubbinsTwu<GubbinsTwuJIntegral, GubbinsTwuKIntegral>;
1075 auto mubar2 = (mustar2factor*mu_Cm.pow(2)/(epsks*sigma_ms.pow(3))).eval();
1076 auto Qbar2 = (Qstar2factor*Q_Cm2.pow(2)/(epsks*sigma_ms.pow(5))).eval();
1077 auto polar = MCGG(sigma_ms, epsks, mubar2, Qbar2, multipolar_rhostar_approach::use_packing_fraction);
1080 if (polar_model ==
"GubbinsTwu+Gottschalk"){
1081 using MCGG = MultipolarContributionGubbinsTwu<GottschalkJIntegral, GottschalkKIntegral>;
1082 auto mubar2 = (mustar2factor*mu_Cm.pow(2)/(epsks*sigma_ms.pow(3))).eval();
1083 auto Qbar2 = (Qstar2factor*Q_Cm2.pow(2)/(epsks*sigma_ms.pow(5))).eval();
1084 auto polar = MCGG(sigma_ms, epsks, mubar2, Qbar2, multipolar_rhostar_approach::use_packing_fraction);
1088 if (polar_model ==
"GrayGubbins+GubbinsTwu"){
1089 using MCGG = MultipolarContributionGrayGubbins<GubbinsTwuJIntegral, GubbinsTwuKIntegral>;
1090 auto polar = MCGG(sigma_ms, epsks, SIGMAIJ, EPSKIJ, mu_Cm, Q_Cm2, polar_flags);
1098 if (polar_model ==
"GrayGubbins+Luckas"){
1099 using MCGG = MultipolarContributionGrayGubbins<LuckasJIntegral, LuckasKIntegral>;
1100 auto polar = MCGG(sigma_ms, epsks, SIGMAIJ, EPSKIJ, mu_Cm, Q_Cm2, polar_flags);
1105 if (polar_model ==
"GubbinsTwu+Luckas+GubbinsTwuRhostar"){
1106 using MCGTL = MultipolarContributionGubbinsTwu<LuckasJIntegral, LuckasKIntegral>;
1107 auto mubar2 = (mustar2factor*mu_Cm.pow(2)/(epsks*sigma_ms.pow(3))).eval();
1108 auto Qbar2 = (Qstar2factor*Q_Cm2.pow(2)/(epsks*sigma_ms.pow(5))).eval();
1109 auto polar = MCGTL(sigma_ms, epsks, mubar2, Qbar2, multipolar_rhostar_approach::calculate_Gubbins_rhostar);
1112 if (polar_model ==
"GubbinsTwu+GubbinsTwu+GubbinsTwuRhostar"){
1113 using MCGG = MultipolarContributionGubbinsTwu<GubbinsTwuJIntegral, GubbinsTwuKIntegral>;
1114 auto mubar2 = (mustar2factor*mu_Cm.pow(2)/(epsks*sigma_ms.pow(3))).eval();
1115 auto Qbar2 = (Qstar2factor*Q_Cm2.pow(2)/(epsks*sigma_ms.pow(5))).eval();
1116 auto polar = MCGG(sigma_ms, epsks, mubar2, Qbar2, multipolar_rhostar_approach::calculate_Gubbins_rhostar);
1123 throw std::invalid_argument(
"you must provide names or coeffs, but not both");