teqp 0.19.1
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Concepts
polar_terms.hpp
Go to the documentation of this file.
1#pragma once
2
10#include "teqp/types.hpp"
11#include "teqp/constants.hpp"
12#include "teqp/exceptions.hpp"
14#include <optional>
15#include <Eigen/Dense>
17#include <variant>
18
19namespace teqp{
20
21namespace SAFTpolar{
22
24template <typename Eta, typename MType, typename TType>
25auto get_JDD_2ij(const Eta& eta, const MType& mij, const TType& Tstarij) {
26 static Eigen::ArrayXd a_0 = (Eigen::ArrayXd(5) << 0.3043504, -0.1358588, 1.4493329, 0.3556977, -2.0653308).finished();
27 static Eigen::ArrayXd a_1 = (Eigen::ArrayXd(5) << 0.9534641, -1.8396383, 2.0131180, -7.3724958, 8.2374135).finished();
28 static Eigen::ArrayXd a_2 = (Eigen::ArrayXd(5) << -1.1610080, 4.5258607, 0.9751222, -12.281038, 5.9397575).finished();
29
30 static Eigen::ArrayXd b_0 = (Eigen::ArrayXd(5) << 0.2187939, -1.1896431, 1.1626889, 0, 0).finished();
31 static Eigen::ArrayXd b_1 = (Eigen::ArrayXd(5) << -0.5873164, 1.2489132, -0.5085280, 0, 0).finished();
32 static Eigen::ArrayXd b_2 = (Eigen::ArrayXd(5) << 3.4869576, -14.915974, 15.372022, 0, 0).finished();
33
34 std::common_type_t<Eta, MType, TType> summer = 0.0;
35 for (auto n = 0; n < 5; ++n){
36 auto anij = a_0[n] + (mij-1)/mij*a_1[n] + (mij-1)/mij*(mij-2)/mij*a_2[n]; // Eq. 12
37 auto bnij = b_0[n] + (mij-1)/mij*b_1[n] + (mij-1)/mij*(mij-2)/mij*b_2[n]; // Eq. 13
38 summer += (anij + bnij/Tstarij)*pow(eta, n);
39 }
40 return forceeval(summer);
41}
42
44template <typename Eta, typename MType>
45auto get_JDD_3ijk(const Eta& eta, const MType& mijk) {
46 static Eigen::ArrayXd c_0 = (Eigen::ArrayXd(5) << -0.0646774, 0.1975882, -0.8087562, 0.6902849, 0.0).finished();
47 static Eigen::ArrayXd c_1 = (Eigen::ArrayXd(5) << -0.9520876, 2.9924258, -2.3802636, -0.2701261, 0.0).finished();
48 static Eigen::ArrayXd c_2 = (Eigen::ArrayXd(5) << -0.6260979, 1.2924686, 1.6542783, -3.4396744, 0.0).finished();
49 std::common_type_t<Eta, MType> summer = 0.0;
50 for (auto n = 0; n < 5; ++n){
51 auto cnijk = c_0[n] + (mijk-1)/mijk*c_1[n] + (mijk-1)/mijk*(mijk-2)/mijk*c_2[n]; // Eq. 14
52 summer += cnijk*pow(eta, n);
53 }
54 return forceeval(summer);
55}
56
58template <typename Eta, typename MType, typename TType>
59auto get_JQQ_2ij(const Eta& eta, const MType& mij, const TType& Tstarij) {
60 static Eigen::ArrayXd a_0 = (Eigen::ArrayXd(5) << 1.2378308, 2.4355031, 1.6330905, -1.6118152, 6.9771185).finished();
61 static Eigen::ArrayXd a_1 = (Eigen::ArrayXd(5) << 1.2854109, -11.465615, 22.086893, 7.4691383, -17.197772).finished();
62 static Eigen::ArrayXd a_2 = (Eigen::ArrayXd(5) << 1.7942954, 0.7695103, 7.2647923, 94.486699, -77.148458).finished();
63
64 static Eigen::ArrayXd b_0 = (Eigen::ArrayXd(5) << 0.4542718, -4.5016264, 3.5858868, 0.0, 0.0).finished();
65 static Eigen::ArrayXd b_1 = (Eigen::ArrayXd(5) << -0.8137340, 10.064030, -10.876631, 0.0, 0.0).finished();
66 static Eigen::ArrayXd b_2 = (Eigen::ArrayXd(5) << 6.8682675, -5.1732238, -17.240207, 0.0, 0.0).finished();
67
68 std::common_type_t<Eta, MType, TType> summer = 0.0;
69 for (auto n = 0; n < 5; ++n){
70 auto anij = a_0[n] + (mij-1)/mij*a_1[n] + (mij-1)/mij*(mij-2)/mij*a_2[n]; // Eq. 12
71 auto bnij = b_0[n] + (mij-1)/mij*b_1[n] + (mij-1)/mij*(mij-2)/mij*b_2[n]; // Eq. 13
72 summer += (anij + bnij/Tstarij)*pow(eta, n);
73 }
74 return forceeval(summer);
75}
76
78template <typename Eta, typename MType>
79auto get_JQQ_3ijk(const Eta& eta, const MType& mijk) {
80 static Eigen::ArrayXd c_0 = (Eigen::ArrayXd(5) << 0.5000437, 6.5318692, -16.014780, 14.425970, 0.0).finished();
81 static Eigen::ArrayXd c_1 = (Eigen::ArrayXd(5) << 2.0002094, -6.7838658, 20.383246, -10.895984, 0.0).finished();
82 static Eigen::ArrayXd c_2 = (Eigen::ArrayXd(5) << 3.1358271, 7.2475888, 3.0759478, 0.0, 0.0).finished();
83 std::common_type_t<Eta, MType> summer = 0.0;
84 for (auto n = 0; n < 5; ++n){
85 auto cnijk = c_0[n] + (mijk-1)/mijk*c_1[n] + (mijk-1)/mijk*(mijk-2)/mijk*c_2[n]; // Eq. 14
86 summer += cnijk*pow(eta, n);
87 }
88 return forceeval(summer);
89}
90
91
93template <typename Eta, typename MType, typename TType>
94auto get_JDQ_2ij(const Eta& eta, const MType& mij, const TType& Tstarij) {
95 static Eigen::ArrayXd a_0 = (Eigen::ArrayXd(4) << 0.6970950, -0.6335541, 2.9455090, -1.4670273).finished();
96 static Eigen::ArrayXd a_1 = (Eigen::ArrayXd(4) << -0.6734593, -1.4258991, 4.1944139, 1.0266216).finished();
97 static Eigen::ArrayXd a_2 = (Eigen::ArrayXd(4) << 0.6703408, -4.3384718, 7.2341684, 0).finished();
98
99 static Eigen::ArrayXd b_0 = (Eigen::ArrayXd(4) << -0.4840383, 1.9704055, -2.1185727, 0).finished();
100 static Eigen::ArrayXd b_1 = (Eigen::ArrayXd(4) << 0.6765101, -3.0138675, 0.4674266, 0).finished();
101 static Eigen::ArrayXd b_2 = (Eigen::ArrayXd(4) << -1.1675601, 2.1348843, 0, 0).finished();
102
103 std::common_type_t<Eta, MType, TType> summer = 0.0;
104 for (auto n = 0; n < 4; ++n){
105 auto anij = a_0[n] + (mij-1)/mij*a_1[n] + (mij-1)/mij*(mij-2)/mij*a_2[n]; // Eq. 18
106 auto bnij = b_0[n] + (mij-1)/mij*b_1[n] + (mij-1)/mij*(mij-2)/mij*b_2[n]; // Eq. 19
107 summer += (anij + bnij/Tstarij)*pow(eta, n);
108 }
109 return forceeval(summer);
110}
111
112
114template <typename Eta, typename MType>
115auto get_JDQ_3ijk(const Eta& eta, const MType& mijk) {
116 static Eigen::ArrayXd c_0 = (Eigen::ArrayXd(4) << 7.846431, 33.42700, 4.689111, 0).finished();
117 static Eigen::ArrayXd c_1 = (Eigen::ArrayXd(4) << -20.72202, -58.63904, -1.764887, 0).finished();
118 std::common_type_t<Eta, MType> summer = 0.0;
119 for (auto n = 0; n < 4; ++n){
120 auto cnijk = c_0[n] + (mijk-1)/mijk*c_1[n]; // Eq. 20
121 summer += cnijk*pow(eta, n);
122 }
123 return forceeval(summer);
124}
125
126/***
127 * \brief The dipolar contribution given in Gross and Vrabec
128 */
130private:
131 const Eigen::ArrayXd m, sigma_Angstrom, epsilon_over_k, mustar2, nmu;
132public:
133 const bool has_a_polar;
134 DipolarContributionGrossVrabec(const Eigen::ArrayX<double> &m, const Eigen::ArrayX<double> &sigma_Angstrom, const Eigen::ArrayX<double> &epsilon_over_k, const Eigen::ArrayX<double> &mustar2, const Eigen::ArrayX<double> &nmu) : m(m), sigma_Angstrom(sigma_Angstrom), epsilon_over_k(epsilon_over_k), mustar2(mustar2), nmu(nmu), has_a_polar(mustar2.cwiseAbs().sum() > 0) {
135 // Check lengths match
136 if (m.size() != mustar2.size()){
137 throw teqp::InvalidArgument("bad size of mustar2");
138 }
139 if (m.size() != nmu.size()){
140 throw teqp::InvalidArgument("bad size of n");
141 }
142 }
143
145 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
146 auto get_alpha2DD(const TTYPE& T, const RhoType& rhoN_A3, const EtaType& eta, const VecType& mole_fractions) const{
147 const auto& x = mole_fractions; // concision
148 const auto& sigma = sigma_Angstrom; // concision
149 const auto N = mole_fractions.size();
150 std::common_type_t<TTYPE, RhoType, decltype(mole_fractions[0])> summer = 0.0;
151 for (auto i = 0; i < N; ++i){
152 for (auto j = 0; j < N; ++j){
153 auto ninj = nmu[i]*nmu[j];
154 if (ninj > 0){
155 // Lorentz-Berthelot mixing rules
156 auto epskij = sqrt(epsilon_over_k[i]*epsilon_over_k[j]);
157 auto sigmaij = (sigma[i]+sigma[j])/2;
158
159 auto Tstarij = forceeval(T/epskij);
160 auto mij = std::min(sqrt(m[i]*m[j]), 2.0);
161 summer += x[i]*x[j]*epsilon_over_k[i]/T*epsilon_over_k[j]/T*POW3(sigma[i]*sigma[j]/sigmaij)*ninj*mustar2[i]*mustar2[j]*get_JDD_2ij(eta, mij, Tstarij);
162 }
163 }
164 }
165 return forceeval(-static_cast<double>(EIGEN_PI)*rhoN_A3*summer);
166 }
167
169 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
170 auto get_alpha3DD(const TTYPE& T, const RhoType& rhoN_A3, const EtaType& eta, const VecType& mole_fractions) const{
171 const auto& x = mole_fractions; // concision
172 const auto& sigma = sigma_Angstrom; // concision
173 const auto N = mole_fractions.size();
174 std::common_type_t<TTYPE, RhoType, decltype(mole_fractions[0])> summer = 0.0;
175 for (auto i = 0; i < N; ++i){
176 for (auto j = 0; j < N; ++j){
177 for (auto k = 0; k < N; ++k){
178 auto ninjnk = nmu[i]*nmu[j]*nmu[k];
179 if (ninjnk > 0){
180 // Lorentz-Berthelot mixing rules for sigma
181 auto sigmaij = (sigma[i]+sigma[j])/2;
182 auto sigmaik = (sigma[i]+sigma[k])/2;
183 auto sigmajk = (sigma[j]+sigma[k])/2;
184
185 auto mijk = std::min(pow(m[i]*m[j]*m[k], 1.0/3.0), 2.0);
186 summer += x[i]*x[j]*x[k]*epsilon_over_k[i]/T*epsilon_over_k[j]/T*epsilon_over_k[k]/T*POW3(sigma[i]*sigma[j]*sigma[k])/(sigmaij*sigmaik*sigmajk)*ninjnk*mustar2[i]*mustar2[j]*mustar2[k]*get_JDD_3ijk(eta, mijk);
187 }
188 }
189 }
190 }
191 return forceeval(-4.0*POW2(static_cast<double>(EIGEN_PI))/3.0*POW2(rhoN_A3)*summer);
192 }
193
194 /***
195 * \brief Get the dipolar contribution to \f$ \alpha = A/(NkT) \f$
196 */
197 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
198 auto eval(const TTYPE& T, const RhoType& rho_A3, const EtaType& eta, const VecType& mole_fractions) const {
199 auto alpha2 = get_alpha2DD(T, rho_A3, eta, mole_fractions);
200 auto alpha3 = get_alpha3DD(T, rho_A3, eta, mole_fractions);
201 auto alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
202
203 using alpha2_t = decltype(alpha2);
204 using alpha3_t = decltype(alpha3);
205 using alpha_t = decltype(alpha);
206 struct DipolarContributionTerms{
207 alpha2_t alpha2;
208 alpha3_t alpha3;
209 alpha_t alpha;
210 };
211 return DipolarContributionTerms{alpha2, alpha3, alpha};
212 }
213};
214
215/***
216 * \brief The quadrupolar contribution from Gross, AICHEJ, doi: 10.1002/aic.10502
217 *
218 */
220private:
221 const Eigen::ArrayXd m, sigma_Angstrom, epsilon_over_k, Qstar2, nQ;
222
223public:
224 const bool has_a_polar;
225 QuadrupolarContributionGross(const Eigen::ArrayX<double> &m, const Eigen::ArrayX<double> &sigma_Angstrom, const Eigen::ArrayX<double> &epsilon_over_k, const Eigen::ArrayX<double> &Qstar2, const Eigen::ArrayX<double> &nQ) : m(m), sigma_Angstrom(sigma_Angstrom), epsilon_over_k(epsilon_over_k), Qstar2(Qstar2), nQ(nQ), has_a_polar(Qstar2.cwiseAbs().sum() > 0) {
226 // Check lengths match
227 if (m.size() != Qstar2.size()){
228 throw teqp::InvalidArgument("bad size of mustar2");
229 }
230 if (m.size() != nQ.size()){
231 throw teqp::InvalidArgument("bad size of n");
232 }
233 }
235
237 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
238 auto get_alpha2QQ(const TTYPE& T, const RhoType& rhoN_A3, const EtaType& eta, const VecType& mole_fractions) const{
239 const auto& x = mole_fractions; // concision
240 const auto& sigma = sigma_Angstrom; // concision
241 const auto N = mole_fractions.size();
242 std::common_type_t<TTYPE, RhoType, decltype(mole_fractions[0])> summer = 0.0;
243 for (auto i = 0; i < N; ++i){
244 for (auto j = 0; j < N; ++j){
245 auto ninj = nQ[i]*nQ[j];
246 if (ninj > 0){
247 // Lorentz-Berthelot mixing rules
248 auto epskij = sqrt(epsilon_over_k[i]*epsilon_over_k[j]);
249 auto sigmaij = (sigma[i]+sigma[j])/2;
250
251 auto Tstarij = forceeval(T/epskij);
252 auto mij = std::min(sqrt(m[i]*m[j]), 2.0);
253 summer += x[i]*x[j]*epsilon_over_k[i]/T*epsilon_over_k[j]/T*POW5(sigma[i]*sigma[j])/POW7(sigmaij)*ninj*Qstar2[i]*Qstar2[j]*get_JQQ_2ij(eta, mij, Tstarij);
254 }
255 }
256 }
257 return forceeval(-static_cast<double>(EIGEN_PI)*POW2(3.0/4.0)*rhoN_A3*summer);
258 }
259
261 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
262 auto get_alpha3QQ(const TTYPE& T, const RhoType& rhoN_A3, const EtaType& eta, const VecType& mole_fractions) const{
263 const auto& x = mole_fractions; // concision
264 const auto& sigma = sigma_Angstrom; // concision
265 const std::size_t N = mole_fractions.size();
266 std::common_type_t<TTYPE, RhoType, decltype(mole_fractions[0])> summer = 0.0;
267 for (std::size_t i = 0; i < N; ++i){
268 for (std::size_t j = 0; j < N; ++j){
269 for (std::size_t k = 0; k < N; ++k){
270 auto ninjnk = nQ[i]*nQ[j]*nQ[k];
271 if (ninjnk > 0){
272 // Lorentz-Berthelot mixing rules for sigma
273 auto sigmaij = (sigma[i]+sigma[j])/2;
274 auto sigmaik = (sigma[i]+sigma[k])/2;
275 auto sigmajk = (sigma[j]+sigma[k])/2;
276
277 auto mijk = std::min(pow(m[i]*m[j]*m[k], 1.0/3.0), 2.0);
278 summer += x[i]*x[j]*x[k]*epsilon_over_k[i]/T*epsilon_over_k[j]/T*epsilon_over_k[k]/T*POW5(sigma[i]*sigma[j]*sigma[k])/POW3(sigmaij*sigmaik*sigmajk)*ninjnk*Qstar2[i]*Qstar2[j]*Qstar2[k]*get_JDD_3ijk(eta, mijk);
279 }
280 }
281 }
282 }
283 return forceeval(-4.0*POW2(static_cast<double>(EIGEN_PI))/3.0*POW3(3.0/4.0)*POW2(rhoN_A3)*summer);
284 }
285
286 /***
287 * \brief Get the quadrupolar contribution to \f$ \alpha = A/(NkT) \f$
288 */
289 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
290 auto eval(const TTYPE& T, const RhoType& rho_A3, const EtaType& eta, const VecType& mole_fractions) const {
291 auto alpha2 = get_alpha2QQ(T, rho_A3, eta, mole_fractions);
292 auto alpha3 = get_alpha3QQ(T, rho_A3, eta, mole_fractions);
293 auto alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
294
295 using alpha2_t = decltype(alpha2);
296 using alpha3_t = decltype(alpha3);
297 using alpha_t = decltype(alpha);
298 struct QuadrupolarContributionTerms{
299 alpha2_t alpha2;
300 alpha3_t alpha3;
301 alpha_t alpha;
302 };
303 return QuadrupolarContributionTerms{alpha2, alpha3, alpha};
304 }
305};
306
307
308/***
309 * \brief The cross dipolar-quadrupolar contribution from Vrabec and Gross
310 * doi: https://doi.org/10.1021/jp072619u
311 *
312 */
314private:
315 const Eigen::ArrayXd m, sigma_Angstrom, epsilon_over_k, mustar2, nmu, Qstar2, nQ;
316
317public:
319 const Eigen::ArrayX<double> &m,
320 const Eigen::ArrayX<double> &sigma_Angstrom,
321 const Eigen::ArrayX<double> &epsilon_over_k,
322 const Eigen::ArrayX<double> &mustar2,
323 const Eigen::ArrayX<double> &nmu,
324 const Eigen::ArrayX<double> &Qstar2,
325 const Eigen::ArrayX<double> &nQ
326 ) : m(m), sigma_Angstrom(sigma_Angstrom), epsilon_over_k(epsilon_over_k), mustar2(mustar2), nmu(nmu), Qstar2(Qstar2), nQ(nQ) {
327 // Check lengths match
328 if (m.size() != Qstar2.size()){
329 throw teqp::InvalidArgument("bad size of Qstar2");
330 }
331 if (m.size() != nQ.size()){
332 throw teqp::InvalidArgument("bad size of nQ");
333 }
334 if (m.size() != mustar2.size()){
335 throw teqp::InvalidArgument("bad size of mustar2");
336 }
337 if (m.size() != nmu.size()){
338 throw teqp::InvalidArgument("bad size of n");
339 }
340 if (Qstar2.cwiseAbs().sum() == 0 || mustar2.cwiseAbs().sum() == 0){
341 throw teqp::InvalidArgument("Invalid to have either missing polar or quadrupolar term in cross-polar term");
342 }
343 }
345
347 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
348 auto get_alpha2DQ(const TTYPE& T, const RhoType& rhoN_A3, const EtaType& eta, const VecType& mole_fractions) const{
349 const auto& x = mole_fractions; // concision
350 const auto& sigma = sigma_Angstrom; // concision
351 const auto N = mole_fractions.size();
352 std::common_type_t<TTYPE, RhoType, decltype(mole_fractions[0])> summer = 0.0;
353 for (auto i = 0; i < N; ++i){
354 for (auto j = 0; j < N; ++j){
355 auto ninj = nmu[i]*nQ[j];
356 if (ninj > 0){
357 // Lorentz-Berthelot mixing rules
358 auto epskij = sqrt(epsilon_over_k[i]*epsilon_over_k[j]);
359 auto sigmaij = (sigma[i]+sigma[j])/2;
360
361 auto Tstarij = forceeval(T/epskij);
362 auto mij = std::min(sqrt(m[i]*m[j]), 2.0);
363 summer += x[i]*x[j]*epsilon_over_k[i]/T*epsilon_over_k[j]/T*POW3(sigma[i])*POW5(sigma[j])/POW5(sigmaij)*ninj*mustar2[i]*Qstar2[j]*get_JDQ_2ij(eta, mij, Tstarij);
364 }
365 }
366 }
367 return forceeval(-static_cast<double>(EIGEN_PI)*9.0/4.0*rhoN_A3*summer);
368 }
369
371 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
372 auto get_alpha3DQ(const TTYPE& T, const RhoType& rhoN_A3, const EtaType& eta, const VecType& mole_fractions) const{
373 const auto& x = mole_fractions; // concision
374 const auto& sigma = sigma_Angstrom; // concision
375 const std::size_t N = mole_fractions.size();
376 std::common_type_t<TTYPE, RhoType, decltype(mole_fractions[0])> summer = 0.0;
377 for (std::size_t i = 0; i < N; ++i){
378 for (std::size_t j = 0; j < N; ++j){
379 for (std::size_t k = 0; k < N; ++k){
380 auto ninjnk1 = nmu[i]*nmu[j]*nQ[k];
381 auto ninjnk2 = nmu[i]*nQ[j]*nQ[k];
382 if (ninjnk1 > 0 || ninjnk2 > 0){
383 // Lorentz-Berthelot mixing rules for sigma
384 auto sigmaij = (sigma[i]+sigma[j])/2;
385 auto sigmaik = (sigma[i]+sigma[k])/2;
386 auto sigmajk = (sigma[j]+sigma[k])/2;
387
388 auto mijk = std::min(pow(m[i]*m[j]*m[k], 1.0/3.0), 2.0);
389 double alpha_GV = 1.19374; // Table 3
390 auto polars = ninjnk1*mustar2[i]*mustar2[j]*Qstar2[k] + ninjnk2*alpha_GV*mustar2[i]*Qstar2[j]*Qstar2[k];
391 summer += x[i]*x[j]*x[k]*epsilon_over_k[i]/T*epsilon_over_k[j]/T*epsilon_over_k[k]/T*POW4(sigma[i]*sigma[j]*sigma[k])/POW2(sigmaij*sigmaik*sigmajk)*polars*get_JDQ_3ijk(eta, mijk);
392 }
393 }
394 }
395 }
396 return forceeval(-POW2(rhoN_A3)*summer);
397 }
398
399 /***
400 * \brief Get the cross-polar contribution to \f$ \alpha = A/(NkT) \f$
401 */
402 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
403 auto eval(const TTYPE& T, const RhoType& rho_A3, const EtaType& eta, const VecType& mole_fractions) const {
404 auto alpha2 = get_alpha2DQ(T, rho_A3, eta, mole_fractions);
405 auto alpha3 = get_alpha3DQ(T, rho_A3, eta, mole_fractions);
406 auto alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
407
408 using alpha2_t = decltype(alpha2);
409 using alpha3_t = decltype(alpha3);
410 using alpha_t = decltype(alpha);
411 struct DipolarQuadrupolarContributionTerms{
412 alpha2_t alpha2;
413 alpha3_t alpha3;
414 alpha_t alpha;
415 };
416 return DipolarQuadrupolarContributionTerms{alpha2, alpha3, alpha};
417 }
418};
419
424
430
431// map multipolar_rhostar_approach values to JSON as strings
435 {multipolar_rhostar_approach::calculate_Gubbins_rhostar, "calculate_Gubbins_rhostar"},
436})
437
438template<typename type>
439struct MultipolarContributionGrossVrabecTerms{
440 type alpha2DD;
441 type alpha3DD;
442 type alphaDD;
443 type alpha2QQ;
444 type alpha3QQ;
445 type alphaQQ;
446 type alpha2DQ;
447 type alpha3DQ;
448 type alphaDQ;
449 type alpha;
450};
451
453public:
455
456 const std::optional<DipolarContributionGrossVrabec> di;
457 const std::optional<QuadrupolarContributionGross> quad;
458 const std::optional<DipolarQuadrupolarContributionVrabecGross> diquad;
459
461 const Eigen::ArrayX<double> &m,
462 const Eigen::ArrayX<double> &sigma_Angstrom,
463 const Eigen::ArrayX<double> &epsilon_over_k,
464 const Eigen::ArrayX<double> &mustar2,
465 const Eigen::ArrayX<double> &nmu,
466 const Eigen::ArrayX<double> &Qstar2,
467 const Eigen::ArrayX<double> &nQ)
468 : di((((nmu*mustar2 > 0).cast<int>().sum() > 0) ? decltype(di)(DipolarContributionGrossVrabec(m, sigma_Angstrom, epsilon_over_k, mustar2, nmu)) : std::nullopt)),
469 quad((((nQ*Qstar2 > 0).cast<int>().sum() > 0) ? decltype(quad)(QuadrupolarContributionGross(m, sigma_Angstrom, epsilon_over_k, Qstar2, nQ)) : std::nullopt)),
470 diquad((di && quad) ? decltype(diquad)(DipolarQuadrupolarContributionVrabecGross(m, sigma_Angstrom, epsilon_over_k, mustar2, nmu, Qstar2, nQ)) : std::nullopt)
471 {};
472
473 template<typename TTYPE, typename RhoType, typename EtaType, typename VecType>
474 auto eval(const TTYPE& T, const RhoType& rho_A3, const EtaType& eta, const VecType& mole_fractions) const {
475
476 using type = std::common_type_t<TTYPE, RhoType, EtaType, decltype(mole_fractions[0])>;
477 type alpha2DD = 0.0, alpha3DD = 0.0, alphaDD = 0.0;
478 if (di && di.value().has_a_polar){
479 alpha2DD = di.value().get_alpha2DD(T, rho_A3, eta, mole_fractions);
480 alpha3DD = di.value().get_alpha3DD(T, rho_A3, eta, mole_fractions);
481 alphaDD = forceeval(alpha2DD/(1.0-alpha3DD/alpha2DD));
482 }
483
484 type alpha2QQ = 0.0, alpha3QQ = 0.0, alphaQQ = 0.0;
485 if (quad && quad.value().has_a_polar){
486 alpha2QQ = quad.value().get_alpha2QQ(T, rho_A3, eta, mole_fractions);
487 alpha3QQ = quad.value().get_alpha3QQ(T, rho_A3, eta, mole_fractions);
488 alphaQQ = forceeval(alpha2QQ/(1.0-alpha3QQ/alpha2QQ));
489 }
490
491 type alpha2DQ = 0.0, alpha3DQ = 0.0, alphaDQ = 0.0;
492 if (diquad){
493 alpha2DQ = diquad.value().get_alpha2DQ(T, rho_A3, eta, mole_fractions);
494 alpha3DQ = diquad.value().get_alpha3DQ(T, rho_A3, eta, mole_fractions);
495 alphaDQ = forceeval(alpha2DQ/(1.0-alpha3DQ/alpha2DQ));
496 }
497
498 auto alpha = forceeval(alphaDD + alphaQQ + alphaDQ);
499
500 return MultipolarContributionGrossVrabecTerms<type>{alpha2DD, alpha3DD, alphaDD, alpha2QQ, alpha3QQ, alphaQQ, alpha2DQ, alpha3DQ, alphaDQ, alpha};
501 }
502
503};
504
505template<typename type>
511
512template<typename KType, typename RhoType, typename Txy>
513auto get_Kijk(const KType& Kint, const RhoType& rhostar, const Txy &Tstarij, const Txy &Tstarik, const Txy &Tstarjk){
514 return forceeval(pow(forceeval(Kint.get_K(Tstarij, rhostar)*Kint.get_K(Tstarik, rhostar)*Kint.get_K(Tstarjk, rhostar)), 1.0/3.0));
515};
516
517// Special treatment needed here because the 334,445 term is negative, so negative*negative*negative is negative, and negative^{1/3} is undefined
518// First flip the sign on the triple product, do the evaluation, the flip it back. Not documented in Gubbins&Twu, but this seem reasonable,
519// in the spirit of the others.
520template<typename KType, typename RhoType, typename Txy>
521auto get_Kijk_334445(const KType& Kint, const RhoType& rhostar, const Txy &Tstarij, const Txy &Tstarik, const Txy &Tstarjk){
522 return forceeval(-pow(-forceeval(Kint.get_K(Tstarij, rhostar)*Kint.get_K(Tstarik, rhostar)*Kint.get_K(Tstarjk, rhostar)), 1.0/3.0));
523};
524
525
532template<class JIntegral, class KIntegral>
534public:
536private:
537 const Eigen::ArrayXd sigma_m, epsilon_over_k, mubar2, Qbar2;
538 const bool has_a_polar;
539 const Eigen::ArrayXd sigma_m3, sigma_m5;
540
541 const JIntegral J6{6};
542 const JIntegral J8{8};
543 const JIntegral J10{10};
544 const JIntegral J11{11};
545 const JIntegral J13{13};
546 const JIntegral J15{15};
547 const KIntegral K222_333{222, 333};
548 const KIntegral K233_344{233, 344};
549 const KIntegral K334_445{334, 445};
550 const KIntegral K444_555{444, 555};
551 const double epsilon_0 = 8.8541878128e-12; // https://en.wikipedia.org/wiki/Vacuum_permittivity, in F/m, or C^2⋅N^−1⋅m^−2
552 const double PI_ = static_cast<double>(EIGEN_PI);
553 Eigen::MatrixXd SIGMAIJ, EPSKIJ;
555
556public:
557 MultipolarContributionGubbinsTwu(const Eigen::ArrayX<double> &sigma_m, const Eigen::ArrayX<double> &epsilon_over_k, const Eigen::ArrayX<double> &mubar2, const Eigen::ArrayX<double> &Qbar2, multipolar_rhostar_approach approach) : sigma_m(sigma_m), epsilon_over_k(epsilon_over_k), mubar2(mubar2), Qbar2(Qbar2), has_a_polar(mubar2.cwiseAbs().sum() > 0 || Qbar2.cwiseAbs().sum() > 0), sigma_m3(sigma_m.pow(3)), sigma_m5(sigma_m.pow(5)), approach(approach) {
558 // Check lengths match
559 if (sigma_m.size() != mubar2.size()){
560 throw teqp::InvalidArgument("bad size of mubar2");
561 }
562 if (sigma_m.size() != Qbar2.size()){
563 throw teqp::InvalidArgument("bad size of Qbar2");
564 }
565 // Pre-calculate the mixing terms;
566 const std::size_t N = sigma_m.size();
567 SIGMAIJ.resize(N, N); EPSKIJ.resize(N, N);
568 for (std::size_t i = 0; i < N; ++i){
569 for (std::size_t j = 0; j < N; ++j){
570 // Lorentz-Berthelot mixing rules
571 double epskij = sqrt(epsilon_over_k[i]*epsilon_over_k[j]);
572 double sigmaij = (sigma_m[i]+sigma_m[j])/2;
573 SIGMAIJ(i, j) = sigmaij;
574 EPSKIJ(i, j) = epskij;
575 }
576 }
577 }
579
580 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType>
581 auto get_alpha2(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions) const{
582 const auto& x = mole_fractions; // concision
583
584 const std::size_t N = mole_fractions.size();
585 using XTtype = std::common_type_t<TTYPE, decltype(mole_fractions[0])>;
586 std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0])> alpha2_112 = 0.0, alpha2_123 = 0.0, alpha2_224 = 0.0;
587
588 const RhoType factor_112 = -2.0*PI_*rhoN/3.0;
589 const RhoType factor_123 = -PI_*rhoN;
590 const RhoType factor_224 = -14.0*PI_*rhoN/5.0;
591
592 for (std::size_t i = 0; i < N; ++i){
593 for (std::size_t j = 0; j < N; ++j){
594
595 TTYPE Tstari = forceeval(T/EPSKIJ(i, i)), Tstarj = forceeval(T/EPSKIJ(j, j));
596 XTtype leading = forceeval(x[i]*x[j]/(Tstari*Tstarj)); // common for all alpha_2 terms
597 TTYPE Tstarij = forceeval(T/EPSKIJ(i, j));
598 double sigmaij = SIGMAIJ(i,j);
599 {
600 double dbl = sigma_m3[i]*sigma_m3[j]/powi(sigmaij,3)*mubar2[i]*mubar2[j];
601 alpha2_112 += leading*dbl*J6.get_J(Tstarij, rhostar);
602 }
603 {
604 double dbl = sigma_m3[i]*sigma_m5[j]/powi(sigmaij,5)*mubar2[i]*Qbar2[j];
605 alpha2_123 += leading*dbl*J8.get_J(Tstarij, rhostar);
606 }
607 {
608 double dbl = sigma_m5[i]*sigma_m5[j]/powi(sigmaij,7)*Qbar2[i]*Qbar2[j];
609 alpha2_224 += leading*dbl*J10.get_J(Tstarij, rhostar);
610 }
611 }
612 }
613 return forceeval(factor_112*alpha2_112 + 2.0*factor_123*alpha2_123 + factor_224*alpha2_224);
614 }
615
616
617 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType>
618 auto get_alpha3(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions) const{
619 const VecType& x = mole_fractions; // concision
620 const std::size_t N = mole_fractions.size();
621 using type = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0])>;
622 using XTtype = std::common_type_t<TTYPE, decltype(mole_fractions[0])>;
623 type summerA_112_112_224 = 0.0, summerA_112_123_213 = 0.0, summerA_123_123_224 = 0.0, summerA_224_224_224 = 0.0;
624 type summerB_112_112_112 = 0.0, summerB_112_123_123 = 0.0, summerB_123_123_224 = 0.0, summerB_224_224_224 = 0.0;
625
626 for (std::size_t i = 0; i < N; ++i){
627 for (std::size_t j = 0; j < N; ++j){
628
629 TTYPE Tstari = forceeval(T/EPSKIJ(i,i)), Tstarj = forceeval(T/EPSKIJ(j,j));
630 TTYPE Tstarij = forceeval(T/EPSKIJ(i,j));
631
632 XTtype leading = forceeval(x[i]*x[j]/pow(forceeval(Tstari*Tstarj), 3.0/2.0)); // common for all alpha_3A terms
633 double sigmaij = SIGMAIJ(i,j);
634 double POW4sigmaij = powi(sigmaij, 4);
635 double POW8sigmaij = POW4sigmaij*POW4sigmaij;
636 double POW10sigmaij = powi(sigmaij, 10);
637 double POW12sigmaij = POW4sigmaij*POW8sigmaij;
638
639 {
640 double dbl = pow(sigma_m[i]*sigma_m[j], 11.0/2.0)/POW8sigmaij*mubar2[i]*mubar2[j]*sqrt(Qbar2[i]*Qbar2[j]);
641 summerA_112_112_224 += leading*dbl*J11.get_J(Tstarij, rhostar);
642 }
643 {
644 double dbl = pow(sigma_m[i]*sigma_m[j], 11.0/2.0)/POW8sigmaij*mubar2[i]*mubar2[j]*sqrt(Qbar2[i]*Qbar2[j]);
645 summerA_112_123_213 += leading*dbl*J11.get_J(Tstarij, rhostar);
646 }
647 {
648 double dbl = pow(sigma_m[i], 11.0/2.0)*pow(sigma_m[j], 15.0/2.0)/POW10sigmaij*mubar2[i]*sqrt(Qbar2[i])*pow(Qbar2[j], 3.0/2.0);
649 summerA_123_123_224 += leading*dbl*J13.get_J(Tstarij, rhostar);
650 }
651 {
652 double dbl = pow(sigma_m[i]*sigma_m[j], 15.0/2.0)/POW12sigmaij*pow(Qbar2[i], 3.0/2.0)*pow(Qbar2[j], 3.0/2.0);
653 summerA_224_224_224 += leading*dbl*J15.get_J(Tstarij, rhostar);
654 }
655
656 for (std::size_t k = 0; k < N; ++k){
657 TTYPE Tstark = forceeval(T/EPSKIJ(k,k));
658 TTYPE Tstarik = forceeval(T/EPSKIJ(i,k));
659 TTYPE Tstarjk = forceeval(T/EPSKIJ(j,k));
660 double sigmaik = SIGMAIJ(i,k), sigmajk = SIGMAIJ(j,k);
661
662 // Lorentz-Berthelot mixing rules for sigma
663 XTtype leadingijk = forceeval(x[i]*x[j]*x[k]/(Tstari*Tstarj*Tstark));
664
665 if (std::abs(mubar2[i]*mubar2[j]*mubar2[k]) > 0){
666 auto K222333 = get_Kijk(K222_333, rhostar, Tstarij, Tstarik, Tstarjk);
667 double dbl = sigma_m3[i]*sigma_m3[j]*sigma_m3[k]/(sigmaij*sigmaik*sigmajk)*mubar2[i]*mubar2[j]*mubar2[k];
668 summerB_112_112_112 += forceeval(leadingijk*dbl*K222333);
669 }
670 if (std::abs(mubar2[i]*mubar2[j]*Qbar2[k]) > 0){
671 auto K233344 = get_Kijk(K233_344, rhostar, Tstarij, Tstarik, Tstarjk);
672 double dbl = sigma_m3[i]*sigma_m3[j]*sigma_m5[k]/(sigmaij*POW2(sigmaik*sigmajk))*mubar2[i]*mubar2[j]*Qbar2[k];
673 summerB_112_123_123 += leadingijk*dbl*K233344;
674 }
675 if (std::abs(mubar2[i]*Qbar2[j]*Qbar2[k]) > 0){
676 auto K334445 = get_Kijk_334445(K334_445, rhostar, Tstarij, Tstarik, Tstarjk);
677 double dbl = sigma_m3[i]*sigma_m5[j]*sigma_m5[k]/(POW2(sigmaij*sigmaik)*POW3(sigmajk))*mubar2[i]*Qbar2[j]*Qbar2[k];
678 summerB_123_123_224 += leadingijk*dbl*K334445;
679 }
680 if (std::abs(Qbar2[i]*Qbar2[j]*Qbar2[k]) > 0){
681 auto K444555 = get_Kijk(K444_555, rhostar, Tstarij, Tstarik, Tstarjk);
682 double dbl = POW5(sigma_m[i]*sigma_m[j]*sigma_m[k])/(POW3(sigmaij*sigmaik*sigmajk))*Qbar2[i]*Qbar2[j]*Qbar2[k];
683 summerB_224_224_224 += leadingijk*dbl*K444555;
684 }
685 }
686 }
687 }
688
689 type alpha3A_112_112_224 = 8.0*PI_*rhoN/25.0*summerA_112_112_224;
690 type alpha3A_112_123_213 = 8.0*PI_*rhoN/75.0*summerA_112_123_213;
691 type alpha3A_123_123_224 = 8.0*PI_*rhoN/35.0*summerA_123_123_224;
692 type alpha3A_224_224_224 = 144.0*PI_*rhoN/245.0*summerA_224_224_224;
693
694 type alpha3A = 3.0*alpha3A_112_112_224 + 6.0*alpha3A_112_123_213 + 6.0*alpha3A_123_123_224 + alpha3A_224_224_224;
695
696 RhoType rhoN2 = rhoN*rhoN;
697
698 type alpha3B_112_112_112 = 32.0*POW3(PI_)*rhoN2/135.0*sqrt(14*PI_/5.0)*summerB_112_112_112;
699 type alpha3B_112_123_123 = 64.0*POW3(PI_)*rhoN2/315.0*sqrt(3.0*PI_)*summerB_112_123_123;
700 type alpha3B_123_123_224 = -32.0*POW3(PI_)*rhoN2/45.0*sqrt(22.0*PI_/63.0)*summerB_123_123_224;
701 type alpha3B_224_224_224 = 32.0*POW3(PI_)*rhoN2/2025.0*sqrt(2002.0*PI_)*summerB_224_224_224;
702
703 type alpha3B = alpha3B_112_112_112 + 3.0*alpha3B_112_123_123 + 3.0*alpha3B_123_123_224 + alpha3B_224_224_224;
704
705 return forceeval(alpha3A + alpha3B);
706 }
707
708 template<typename RhoType, typename PFType, typename MoleFractions>
709 auto get_rhostar(const RhoType rhoN, const PFType& packing_fraction, const MoleFractions& mole_fractions) const{
710 using type = std::common_type_t<RhoType, PFType, decltype(mole_fractions[0])>;
711 type rhostar;
713 // Calculate the effective reduced diameter (cubed) to be used for evaluation
714 // Eq. 24 from Gubbins
715 type sigma_x3 = 0.0;
716 error_if_expr(sigma_m3);
717 auto N = mole_fractions.size();
718 for (auto i = 0; i < N; ++i){
719 for (auto j = 0; j < N; ++j){
720 auto sigmaij = (sigma_m[i] + sigma_m[j])/2;
721 sigma_x3 += mole_fractions[i]*mole_fractions[j]*POW3(sigmaij);
722 }
723 }
724 rhostar = forceeval(rhoN*sigma_x3);
725 }
727 // The packing fraction is defined by eta = pi/6*rho^*, so use the (temperature-dependent) eta to obtain rho^*
728 rhostar = forceeval(packing_fraction/(static_cast<double>(EIGEN_PI)/6.0));
729 }
730 else{
731 throw teqp::InvalidArgument("The method used to determine rho^* is invalid");
732 }
733 return rhostar;
734 }
735
736 /***
737 * \brief Get the contribution to \f$ \alpha = A/(NkT) \f$
738 */
739 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType>
740 auto eval(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions) const {
741 error_if_expr(T); error_if_expr(rhoN); error_if_expr(mole_fractions[0]);
742 using type = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0])>;
743
744 type alpha2 = 0.0, alpha3 = 0.0, alpha = 0.0;
745 if (has_a_polar){
746 alpha2 = get_alpha2(T, rhoN, rhostar, mole_fractions);
747 alpha3 = get_alpha3(T, rhoN, rhostar, mole_fractions);
748 alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
749 }
750 return MultipolarContributionGubbinsTwuTermsGT<type>{alpha2, alpha3, alpha};
751 }
752};
753
757
764template<class JIntegral, class KIntegral>
766public:
768private:
769 const Eigen::ArrayXd sigma_m, epsilon_over_k;
770 Eigen::MatrixXd SIGMAIJ, EPSKIJ;
771 const Eigen::ArrayXd mu, Q, mu2, Q2, Q3;
772 const bool has_a_polar;
773 const Eigen::ArrayXd sigma_m3, sigma_m5;
774
775 const JIntegral J6{6};
776 const JIntegral J8{8};
777 const JIntegral J10{10};
778 const JIntegral J11{11};
779 const JIntegral J13{13};
780 const JIntegral J15{15};
781 const KIntegral K222_333{222, 333};
782 const KIntegral K233_344{233, 344};
783 const KIntegral K334_445{334, 445};
784 const KIntegral K444_555{444, 555};
785
786 const double PI_ = static_cast<double>(EIGEN_PI);
787 const double PI3 = PI_*PI_*PI_;
788 const double epsilon_0 = 8.8541878128e-12; // https://en.wikipedia.org/wiki/Vacuum_permittivity, in F/m, or C^2⋅N^−1⋅m^−2
789 const double k_e = teqp::constants::k_e; // coulomb constant, with units of N m^2 / C^2
790 const double k_B = teqp::constants::k_B; // Boltzmann constant, with units of J/K
791
793
794 // These values were adjusted in the model of Paricaud, JPCB, 2023
796 const double C3, C3b;
797 std::optional<PolarizableArrays> polarizable;
798
799 double get_C3(const std::optional<nlohmann::json>& flags, double default_value=1.0){
800 if (flags){ return flags.value().value("C3", default_value); }
801 return default_value;
802 }
803 double get_C3b(const std::optional<nlohmann::json>& flags, double default_value=1.0){
804 if (flags){ return flags.value().value("C3b", default_value); }
805 return default_value;
806 }
807 multipolar_rhostar_approach get_approach(const std::optional<nlohmann::json>& flags){
808 if (flags){ return flags.value().value("approach", multipolar_rhostar_approach::use_packing_fraction); }
810 }
811 std::optional<PolarizableArrays> get_polarizable(const std::optional<nlohmann::json>& flags){
812 if (flags && flags.value().contains("polarizable")){
813 PolarizableArrays arrays;
814 auto toeig = [](const std::valarray<double>& x) -> Eigen::ArrayXd{ return Eigen::Map<const Eigen::ArrayXd>(&(x[0]), x.size());};
815 auto alpha_symm_m3 = toeig(flags.value()["polarizable"].at("alpha_symm / m^3"));
816 auto alpha_asymm_m3 = toeig(flags.value()["polarizable"].at("alpha_asymm / m^3"));
817 arrays.alpha_symm_C2m2J = alpha_symm_m3/k_e;
818 arrays.alpha_asymm_C2m2J = alpha_asymm_m3/k_e;
819 arrays.alpha_isotropic_C2m2J = 1.0/3.0*(arrays.alpha_symm_C2m2J + 2.0*arrays.alpha_asymm_C2m2J);
820 arrays.alpha_anisotropic_C2m2J = arrays.alpha_symm_C2m2J - arrays.alpha_asymm_C2m2J;
821 return arrays;
822 }
823 return std::nullopt;
824 }
825
826public:
827 MultipolarContributionGrayGubbins(const Eigen::ArrayX<double> &sigma_m, const Eigen::ArrayX<double> &epsilon_over_k, const Eigen::MatrixXd& SIGMAIJ, const Eigen::MatrixXd& EPSKIJ, const Eigen::ArrayX<double> &mu, const Eigen::ArrayX<double> &Q, const std::optional<nlohmann::json>& flags)
828
829 : sigma_m(sigma_m), epsilon_over_k(epsilon_over_k), SIGMAIJ(SIGMAIJ), EPSKIJ(EPSKIJ), mu(mu), Q(Q), mu2(mu.pow(2)), Q2(Q.pow(2)), Q3(Q.pow(3)), has_a_polar(Q.cwiseAbs().sum() > 0 || mu.cwiseAbs().sum() > 0), sigma_m3(sigma_m.pow(3)), sigma_m5(sigma_m.pow(5)), approach(get_approach(flags)), C3(get_C3(flags)), C3b(get_C3b(flags)), polarizable(get_polarizable(flags)) {
830 // Check lengths match
831 if (sigma_m.size() != mu.size()){
832 throw teqp::InvalidArgument("bad size of mu");
833 }
834 if (sigma_m.size() != Q.size()){
835 throw teqp::InvalidArgument("bad size of Q");
836 }
837 if (polarizable){
838 if (polarizable.value().alpha_symm_C2m2J.size() != sigma_m.size() || polarizable.value().alpha_asymm_C2m2J.size() != sigma_m.size()){
839 throw teqp::InvalidArgument("bad size of alpha arrays");
840 }
841 }
842 }
844
846 template<typename Jintegral, typename TTYPE, typename RhoStarType>
847 auto get_In(const Jintegral& J, int n, double sigmaij, const TTYPE& Tstar, const RhoStarType& rhostar) const{
848 return 4.0*PI_/pow(sigmaij, n-3)*J.get_J(Tstar, rhostar);
849 }
850
852 template<typename TTYPE, typename RhoStarType>
853 auto Immm(std::size_t i, std::size_t j, std::size_t k, const TTYPE& T, const RhoStarType& rhostar) const {
854 auto Tstarij = T/EPSKIJ(i,j), Tstarik = T/EPSKIJ(i,k), Tstarjk = T/EPSKIJ(j, k);
855 const double coeff = 64.0*PI3/5.0*sqrt(14*PI_/5.0)/SIGMAIJ(i,j)/SIGMAIJ(i,k)/SIGMAIJ(j,k);
856 return coeff*get_Kijk(K222_333, rhostar, Tstarij, Tstarik, Tstarjk);
857 };
858 template<typename TTYPE, typename RhoStarType>
859 auto ImmQ(std::size_t i, std::size_t j, std::size_t k, const TTYPE& T, const RhoStarType& rhostar) const {
860 auto Tstarij = T/EPSKIJ(i,j), Tstarik = T/EPSKIJ(i,k), Tstarjk = T/EPSKIJ(j, k);
861 const double coeff = 2048.0*PI3/7.0*sqrt(3.0*PI_)/SIGMAIJ(i,j)/POW2(SIGMAIJ(i,k)*SIGMAIJ(j,k));
862 return coeff*get_Kijk(K233_344, rhostar, Tstarij, Tstarik, Tstarjk);
863 };
864 template<typename TTYPE, typename RhoStarType>
865 auto ImQQ(std::size_t i, std::size_t j, std::size_t k, const TTYPE& T, const RhoStarType& rhostar) const {
866 auto Tstarij = T/EPSKIJ(i,j), Tstarik = T/EPSKIJ(i,k), Tstarjk = T/EPSKIJ(j, k);
867 const double coeff = -4096.0*PI3/9.0*sqrt(22.0*PI_/7.0)/POW2(SIGMAIJ(i,j)*SIGMAIJ(i,k))/POW3(SIGMAIJ(j,k));
868 return coeff*get_Kijk_334445(K334_445, rhostar, Tstarij, Tstarik, Tstarjk);
869 };
870 template<typename TTYPE, typename RhoStarType>
871 auto IQQQ(std::size_t i, std::size_t j, std::size_t k, const TTYPE& T, const RhoStarType& rhostar) const {
872 auto Tstarij = T/EPSKIJ(i,j), Tstarik = T/EPSKIJ(i,k), Tstarjk = T/EPSKIJ(j, k);
873 const double coeff = 8192.0*PI3/81.0*sqrt(2002.0*PI_)/POW3(SIGMAIJ(i,j)*SIGMAIJ(i,k)*SIGMAIJ(j,k));
874 return coeff*get_Kijk(K444_555, rhostar, Tstarij, Tstarik, Tstarjk);
875 };
876
878 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType, typename MuPrimeType>
879 auto get_alpha2(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions, const MuPrimeType& muprime) const{
880 const auto& x = mole_fractions; // concision
881
882 const std::size_t N = mole_fractions.size();
883
884 std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0]), decltype(muprime[0])> summer = 0.0;
885
886 const TTYPE beta = forceeval(1.0/(k_B*T));
887 const auto muprime2 = POW2(muprime).eval();
888
889 using ztype = std::common_type_t<TTYPE, decltype(muprime[0])>;
890 // We have to do this type promotion to the ztype to allow for multiplication with
891 // Eigen array types, as some type promotion does not happen automatically
892 //
893 // alpha has units of m^3, divide by k_e (has units of J m / C^2) to get C^2m^2/J, beta has units of 1/J, muprime^2 has units of C m
894 Eigen::ArrayX<ztype> z1 = 1.0/3.0*(muprime2.template cast<ztype>()*static_cast<ztype>(beta));
895 Eigen::ArrayX<ztype> z2 = 0.0*z1;
896 if (polarizable){
897 z1 += polarizable.value().alpha_isotropic_C2m2J.template cast<ztype>();
898 z2 += polarizable.value().alpha_isotropic_C2m2J.template cast<ztype>();
899 }
900
901 for (std::size_t i = 0; i < N; ++i){
902 for (std::size_t j = 0; j < N; ++j){
903 TTYPE Tstarij = forceeval(T/EPSKIJ(i, j));
904 double sigmaij = SIGMAIJ(i,j);
905 summer += x[i]*x[j]*(
906 3.0/2.0*(z1[i]*z1[j] - z2[i]*z2[j])*get_In(J6, 6, sigmaij, Tstarij, rhostar)
907 + 3.0/2.0*z1[i]*beta*Q2[j]*get_In(J8, 8, sigmaij, Tstarij, rhostar)
908 +7.0/10.0*beta*beta*Q2[i]*Q2[j]*get_In(J10, 10, sigmaij, Tstarij, rhostar)
909 );
910 }
911 }
912 return forceeval(-rhoN*k_e*k_e*summer); // The factor of k_e^2 takes us from CGS to SI units
913 }
914
916 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType, typename MuPrimeType>
917 auto get_alpha2_muprime_gradient(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions, const MuPrimeType& muprime) const{
918 const auto& x = mole_fractions; // concision
919 const std::size_t N = mole_fractions.size();
920
921 using type_ = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0]), decltype(muprime[0])>;
922
923 const TTYPE beta = 1.0/(k_B*T);
924 using ztype = std::common_type_t<TTYPE, decltype(muprime[0])>;
925 // We have to do this type promotion to the ztype to allow for multiplication with
926 // Eigen array types, as some type promotion does not happen automatically
927 Eigen::ArrayX<ztype> z1 = 1.0/3.0*(POW2(muprime).template cast<ztype>()*static_cast<ztype>(beta));
928 if (polarizable){
929 z1 += polarizable.value().alpha_isotropic_C2m2J.template cast<ztype>();
930 }
931
932 // Exactly the term as defined in Gray et al., Eq. 2.11
933 Eigen::ArrayX<type_> Eprime2(N);
934 for (std::size_t i = 0; i < N; ++i){
935 type_ summer = 0;
936 for (std::size_t j = 0; j < N; ++j){
937 TTYPE Tstarij = T/EPSKIJ(i, j);
938 double sigmaij = SIGMAIJ(i, j);
939 auto rhoj = rhoN*x[j];
940 summer += rhoj*(2.0*z1[i]*get_In(J6, 6, sigmaij, Tstarij, rhostar) + beta*Q2[j]*get_In(J8, 8, sigmaij, Tstarij, rhostar) );
941 }
942 Eprime2[i] = muprime[i]*summer;
943 }
944 // And now to get dalpha2/dmu', multiply by -beta*x
945 return (-k_e*k_e*Eprime2*mole_fractions.template cast<type_>()*beta).eval(); // The factor of k_e^2 takes us from CGS to SI units
946 }
947
949 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType, typename MuPrimeType>
950 auto get_alpha3(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions, const MuPrimeType& muprime) const{
951 const VecType& x = mole_fractions; // concision
952 const std::size_t N = mole_fractions.size();
953 using type = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0]), decltype(muprime[0])>;
954 type summer_a = 0.0, summer_b = 0.0;
955
956 const TTYPE beta = forceeval(1.0/(k_B*T));
957 const auto muprime2 = POW2(muprime).eval();
958 // We have to do this type promotion to the ztype to allow for multiplication with
959 // Eigen array types, as some type promotion does not happen automatically
960 using ztype = std::common_type_t<TTYPE, decltype(muprime[0])>;
961 Eigen::ArrayX<ztype> z1 = 1.0/3.0*(muprime2.template cast<ztype>()*static_cast<ztype>(beta));
962 Eigen::ArrayX<ztype> z2 = 0.0*z1;
963 Eigen::ArrayX<ztype> gamma = 0.0*z1;
964 if (polarizable){
965 z1 += polarizable.value().alpha_isotropic_C2m2J.template cast<ztype>(); // alpha has units of m^3, divide by k_e (has units of J m / C^2) to get C^2m^2/J, beta has units of 1/J, muprime^2 has units of C m
966 z2 += polarizable.value().alpha_isotropic_C2m2J.template cast<ztype>();
967 gamma += polarizable.value().alpha_symm_C2m2J.template cast<ztype>() - polarizable.value().alpha_asymm_C2m2J.template cast<ztype>();
968 }
969
970 for (std::size_t i = 0; i < N; ++i){
971 for (std::size_t j = 0; j < N; ++j){
972
973 TTYPE Tstarij = forceeval(T/EPSKIJ(i,j));
974 double sigmaij = SIGMAIJ(i,j);
975
976 auto a_ij = ((2.0/5.0*beta*beta*muprime2[i]*muprime2[j] + 4.0/5.0*gamma[i]*beta*muprime2[j] + 4.0/25.0*gamma[i]*gamma[j])*beta*Q[i]*Q[j]*get_In(J11, 11, sigmaij, Tstarij, rhostar)
977 +12.0/35.0*(beta*muprime2[i] + gamma[i])*beta*beta*Q[i]*POW3(Q[j])*get_In(J13, 13, sigmaij, Tstarij, rhostar)
978 + 36.0/245.0*POW3(beta)*Q3[i]*Q3[j]*get_In(J15, 15, sigmaij, Tstarij, rhostar)
979 );
980 summer_a += x[i]*x[j]*a_ij;
981
982 for (std::size_t k = 0; k < N; ++k){
983 auto b_ijk = (
984 1.0/2.0*(z1[i]*z1[j]*z1[k] - z2[i]*z2[j]*z2[k])*Immm(i, j, k, T, rhostar)
985 +C3b*(3.0/160.0*z1[i]*z1[j]*beta*Q2[k]*ImmQ(i, j, k, T, rhostar) + 3.0/640.0*z1[i]*POW2(beta)*Q2[j]*Q2[k]*ImQQ(i,j,k,T, rhostar))
986 +1.0/6400.0*POW3(beta)*Q2[i]*Q2[j]*Q2[k]*IQQQ(i, j, k, T, rhostar)
987 );
988 summer_b += x[i]*x[j]*x[k]*b_ijk;
989 }
990 }
991 }
992
993 return forceeval(C3*(rhoN*summer_a + rhoN*rhoN*summer_b)*k_e*k_e*k_e); // The factor of k_e^3 takes us from CGS to SI units
994 }
995
997 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType, typename MuPrimeType>
998 auto get_alpha3_muprime_gradient(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions, const MuPrimeType& muprime) const{
999 const VecType& x = mole_fractions; // concision
1000 const std::size_t N = mole_fractions.size();
1001 using type_ = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0]), decltype(muprime[0])>;
1002
1003 const TTYPE beta = forceeval(1.0/(k_B*T));
1004 const auto muprime2 = POW2(muprime).eval();
1005 // We have to do this type promotion to the ztype to allow for multiplication with
1006 // Eigen array types, as some type promotion does not happen automatically
1007 using ztype = std::common_type_t<TTYPE, decltype(muprime[0])>;
1008 Eigen::ArrayX<ztype> z1 = 1.0/3.0*(muprime2.template cast<ztype>()*static_cast<ztype>(beta));
1009 Eigen::ArrayX<ztype> gamma = 0.0*z1;
1010 if (polarizable){
1011 z1 += polarizable.value().alpha_isotropic_C2m2J.template cast<ztype>(); // alpha has units of m^3, divide by k_e (has units of J m / C^2) to get C^2m^2/J, beta has units of 1/J, muprime^2 has units of C m
1012 gamma += polarizable.value().alpha_symm_C2m2J.template cast<ztype>() - polarizable.value().alpha_asymm_C2m2J.template cast<ztype>();
1013 }
1014
1015 // Exactly as in Eq. 2.12 of Gray et al. (aside from the scaling parameters of Paricaud)
1016 Eigen::ArrayX<type_> Eprime3(N);
1017 type_ summer_ij = 0, summer_ijk = 0;
1018 for (std::size_t i = 0; i < N; ++i){
1019 for (std::size_t j = 0; j < N; ++j){
1020
1021 TTYPE Tstarij = forceeval(T/EPSKIJ(i,j));
1022 double sigmaij = SIGMAIJ(i,j);
1023 auto p_ij = 8.0/5.0*(beta*muprime2[j] + gamma[j])*beta*Q[i]*Q[j]*get_In(J11, 11, sigmaij, Tstarij, rhostar) + 24.0/35.0*beta*beta*Q[i]*Q3[j]*get_In(J13, 13, sigmaij, Tstarij, rhostar);
1024 summer_ij += (rhoN*x[j]*p_ij);
1025
1026 for (std::size_t k = 0; k < N; ++k){
1027 auto q_ijk = (
1028 z1[j]*z1[k]*Immm(i, j, k, T, rhostar)
1029 +C3b*1.0/40.0*z1[j]*beta*Q2[k]*ImmQ(i, j, k, T, rhostar)
1030 + 1.0/320.0*POW2(beta)*Q2[j]*Q2[k]*ImQQ(i,j,k,T, rhostar)
1031 );
1032 summer_ijk += POW2(rhoN)*x[j]*x[k]*q_ijk;
1033 }
1034 }
1035 Eprime3[i] = -muprime[i]*(summer_ij + summer_ijk);
1036 }
1037
1038 return (-C3*Eprime3*k_e*k_e*k_e*mole_fractions.template cast<type_>()*beta).eval(); // The factor of k_e^3 takes us from CGS to SI units
1039 }
1040
1041 template<typename RhoType, typename PFType, typename MoleFractions>
1042 auto get_rhostar(const RhoType rhoN, const PFType& packing_fraction, const MoleFractions& mole_fractions) const{
1043 using type = std::common_type_t<RhoType, PFType, decltype(mole_fractions[0])>;
1044 type rhostar;
1046 // Calculate the effective reduced diameter (cubed) to be used for evaluation
1047 // Eq. 24 from Gubbins
1048 type sigma_x3 = 0.0;
1049 error_if_expr(sigma_m3);
1050 auto N = mole_fractions.size();
1051 for (auto i = 0; i < N; ++i){
1052 for (auto j = 0; j < N; ++j){
1053 auto sigmaij = (sigma_m[i] + sigma_m[j])/2;
1054 sigma_x3 += mole_fractions[i]*mole_fractions[j]*POW3(sigmaij);
1055 }
1056 }
1057 rhostar = forceeval(rhoN*sigma_x3);
1058 }
1060 // The packing fraction is defined by eta = pi/6*rho^*, so use the (temperature-dependent) eta to obtain rho^*
1061 rhostar = forceeval(packing_fraction/(static_cast<double>(EIGEN_PI)/6.0));
1062 }
1063 else{
1064 throw teqp::InvalidArgument("The method used to determine rho^* is invalid");
1065 }
1066 return rhostar;
1067 }
1068
1070 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType, typename MuPrimeType>
1071 auto get_Eprime(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions, const MuPrimeType& muprime) const{
1072 if (!polarizable){
1073 throw teqp::InvalidArgument("Can only use polarizable code if polarizability is enabled");
1074 }
1075// {
1076// // The lambda function to evaluate the gradient of the perturbational term
1077// // w.r.t. the effective dipole moment
1078// auto f = [&](const auto& muprime){
1079// auto alpha2 = get_alpha2(T, rhoN, rhostar, mole_fractions, muprime);
1080// auto alpha3 = get_alpha3(T, rhoN, rhostar, mole_fractions, muprime);
1081// auto alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
1082// return forceeval(alpha);
1083// };
1084// Eigen::ArrayX<autodiff::real> muprimead = muprime.template cast<autodiff::real>();
1085// Eigen::ArrayXd dalphaperturb_dmuprimead = autodiff::gradient(f, wrt(muprimead), at(muprimead)); // units of 1/(C m)
1086//
1087// auto f2 = [&](const auto& muprime){
1088// return get_alpha2(T, rhoN, rhostar, mole_fractions, muprime);
1089// };
1090// Eigen::ArrayXd dalphaperturb2_dmuprimead = autodiff::gradient(f2, wrt(muprimead), at(muprimead)); // units of 1/(C m)
1091// Eigen::ArrayXd dalphaperturb2_dmuprime = get_alpha2_muprime_gradient(T, rhoN, rhostar, mole_fractions, muprime);
1092//
1093// auto f3 = [&](const auto& muprime){
1094// return get_alpha3(T, rhoN, rhostar, mole_fractions, muprime);
1095// };
1096// Eigen::ArrayXd dalphaperturb3_dmuprimead = autodiff::gradient(f3, wrt(muprimead), at(muprimead)); // units of 1/(C m)
1097// Eigen::ArrayXd dalphaperturb3_dmuprime = get_alpha3_muprime_gradient(T, rhoN, rhostar, mole_fractions, muprime);
1098//
1099// std::cout << dalphaperturb3_dmuprimead << " || " << dalphaperturb3_dmuprime << std::endl;
1100//
1101// auto dmu = 1e-6*muprime[0];
1102// Eigen::ArrayXd muprimep = muprime; muprimep[0] += dmu;
1103// Eigen::ArrayXd muprimem = muprime; muprimem[0] -= dmu;
1104// auto dalphaperturb2_dmuprime_centered = (f2(muprimep)-f2(muprimem))/(2*dmu);
1105// }
1106
1107 auto alpha2 = get_alpha2(T, rhoN, rhostar, mole_fractions, muprime);
1108 auto alpha3 = get_alpha3(T, rhoN, rhostar, mole_fractions, muprime);
1109 auto dalphaperturb2_dmuprime = get_alpha2_muprime_gradient(T, rhoN, rhostar, mole_fractions, muprime);
1110 auto dalphaperturb3_dmuprime = get_alpha3_muprime_gradient(T, rhoN, rhostar, mole_fractions, muprime);
1111 auto dalphaperturb_dmuprime = (((1.0-2.0*alpha3/alpha2)*dalphaperturb2_dmuprime + dalphaperturb3_dmuprime)/POW2(1.0-alpha3/alpha2)).eval();
1112
1113 return (-k_B*T*dalphaperturb_dmuprime).eval(); // Eprime has units of J /(C m) because alpha is dimensionless
1114 }
1115
1117 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType, typename MuPrimeType>
1118 auto iterate_muprime_SS(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions, const MuPrimeType& mu, const int max_steps) const{
1119 if (!polarizable){
1120 throw teqp::InvalidArgument("Can only use polarizable code if polarizability is enabled");
1121 }
1122 using otype = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0]), decltype(mu[0])>;
1123 Eigen::ArrayX<otype> muprime = mu.template cast<otype>();
1124 for (auto counter = 0; counter < max_steps; ++counter){
1125 auto Eprime = get_Eprime(T, rhoN, rhostar, mole_fractions, muprime); // units of J /(C m)
1126 // alpha*Eprime has units of J m^3/(C m), divide by k_e (has units of J m / C^2) to get C m
1127 muprime = mu.template cast<otype>() + polarizable.value().alpha_symm_C2m2J.template cast<otype>()*Eprime.template cast<otype>(); // Units of C m
1128 }
1129 return muprime;
1130 }
1131
1132 /***
1133 * \brief Get the contribution to \f$ \alpha = A/(NkT) \f$
1134 */
1135 template<typename TTYPE, typename RhoType, typename RhoStarType, typename VecType>
1136 auto eval(const TTYPE& T, const RhoType& rhoN, const RhoStarType& rhostar, const VecType& mole_fractions) const {
1137 error_if_expr(T); error_if_expr(rhoN); error_if_expr(mole_fractions[0]);
1138 using type = std::common_type_t<TTYPE, RhoType, RhoStarType, decltype(mole_fractions[0])>;
1139
1140 if (!polarizable){
1141 type alpha2 = 0.0, alpha3 = 0.0, alpha = 0.0;
1142 if (has_a_polar){
1143 alpha2 = get_alpha2(T, rhoN, rhostar, mole_fractions, mu);
1144 alpha3 = get_alpha3(T, rhoN, rhostar, mole_fractions, mu);
1145 alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
1146 // Handle the case where the polar term is present but the contribution is zero
1147 if (getbaseval(alpha2) == 0){
1148 alpha2 = 0;
1149 alpha = 0;
1150 }
1151 }
1152 return MultipolarContributionGubbinsTwuTermsGT<type>{alpha2, alpha3, alpha};
1153 }
1154 else{
1155 // First solve for the effective dipole moments
1156 auto muprime = iterate_muprime_SS(T, rhoN, rhostar, mole_fractions, mu, 10); // C m, array
1157 // And the polarization energy derivative, units of J /(C m)
1158 auto Eprime = get_Eprime(T, rhoN, rhostar, mole_fractions, muprime); // array
1159 using Eprime_t = std::decay_t<decltype(Eprime[0])>;
1160 // And finally the polarization contribution to total polar term
1161 auto U_p_over_rhoN = 0.5*(mole_fractions.template cast<Eprime_t>()*polarizable.value().alpha_symm_C2m2J.template cast<Eprime_t>()*Eprime*Eprime).eval().sum(); // U_p divided by rhoN, has units of J
1162 auto alpha_polarization = U_p_over_rhoN/(k_B*T); // nondimensional, scalar
1163
1164 auto alpha2 = get_alpha2(T, rhoN, rhostar, mole_fractions, muprime);
1165 auto alpha3 = get_alpha3(T, rhoN, rhostar, mole_fractions, muprime);
1166 auto alpha = forceeval(alpha2/(1.0-alpha3/alpha2));
1167 return MultipolarContributionGubbinsTwuTermsGT<type>{alpha2, alpha3, alpha + alpha_polarization};
1168 }
1169 }
1170};
1171
1181>;
1182
1183}
1184}
auto eval(const TTYPE &T, const RhoType &rho_A3, const EtaType &eta, const VecType &mole_fractions) const
auto get_alpha3DD(const TTYPE &T, const RhoType &rhoN_A3, const EtaType &eta, const VecType &mole_fractions) const
Eq. 9 from Gross and Vrabec.
DipolarContributionGrossVrabec(const Eigen::ArrayX< double > &m, const Eigen::ArrayX< double > &sigma_Angstrom, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::ArrayX< double > &mustar2, const Eigen::ArrayX< double > &nmu)
auto get_alpha2DD(const TTYPE &T, const RhoType &rhoN_A3, const EtaType &eta, const VecType &mole_fractions) const
Eq. 8 from Gross and Vrabec.
DipolarQuadrupolarContributionVrabecGross & operator=(const DipolarQuadrupolarContributionVrabecGross &)=delete
auto get_alpha3DQ(const TTYPE &T, const RhoType &rhoN_A3, const EtaType &eta, const VecType &mole_fractions) const
Eq. 15 from Vrabec and Gross.
auto get_alpha2DQ(const TTYPE &T, const RhoType &rhoN_A3, const EtaType &eta, const VecType &mole_fractions) const
Eq. 14 from Vrabec and Gross.
auto eval(const TTYPE &T, const RhoType &rho_A3, const EtaType &eta, const VecType &mole_fractions) const
DipolarQuadrupolarContributionVrabecGross(const Eigen::ArrayX< double > &m, const Eigen::ArrayX< double > &sigma_Angstrom, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::ArrayX< double > &mustar2, const Eigen::ArrayX< double > &nmu, const Eigen::ArrayX< double > &Qstar2, const Eigen::ArrayX< double > &nQ)
auto get_rhostar(const RhoType rhoN, const PFType &packing_fraction, const MoleFractions &mole_fractions) const
auto IQQQ(std::size_t i, std::size_t j, std::size_t k, const TTYPE &T, const RhoStarType &rhostar) const
auto get_alpha2(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions, const MuPrimeType &muprime) const
Return , thus this is a nondimensional term. This is equivalent to from Gray et al.
auto get_alpha2_muprime_gradient(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions, const MuPrimeType &muprime) const
Return , thus this is a nondimensional term. This is equivalent to from Gray et al.
auto iterate_muprime_SS(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions, const MuPrimeType &mu, const int max_steps) const
Use successive substitution to obtain the effective dipole moment based solely on the perturbation te...
auto get_alpha3_muprime_gradient(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions, const MuPrimeType &muprime) const
Return , thus this is a nondimensional term. This is equivalent to from Gray et al.
MultipolarContributionGrayGubbins & operator=(const MultipolarContributionGrayGubbins &)=delete
static constexpr multipolar_argument_spec arg_spec
auto ImmQ(std::size_t i, std::size_t j, std::size_t k, const TTYPE &T, const RhoStarType &rhostar) const
auto get_alpha3(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions, const MuPrimeType &muprime) const
Return , thus this is a nondimensional term. This is equivalent to from Gray et al.
MultipolarContributionGrayGubbins(const Eigen::ArrayX< double > &sigma_m, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::MatrixXd &SIGMAIJ, const Eigen::MatrixXd &EPSKIJ, const Eigen::ArrayX< double > &mu, const Eigen::ArrayX< double > &Q, const std::optional< nlohmann::json > &flags)
auto get_Eprime(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions, const MuPrimeType &muprime) const
Get the polarization term .
auto get_In(const Jintegral &J, int n, double sigmaij, const TTYPE &Tstar, const RhoStarType &rhostar) const
Appendix B of Gray et al.
auto Immm(std::size_t i, std::size_t j, std::size_t k, const TTYPE &T, const RhoStarType &rhostar) const
Appendix B of Gray et al.
auto eval(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions) const
auto ImQQ(std::size_t i, std::size_t j, std::size_t k, const TTYPE &T, const RhoStarType &rhostar) const
static constexpr multipolar_argument_spec arg_spec
const std::optional< DipolarQuadrupolarContributionVrabecGross > diquad
const std::optional< DipolarContributionGrossVrabec > di
auto eval(const TTYPE &T, const RhoType &rho_A3, const EtaType &eta, const VecType &mole_fractions) const
MultipolarContributionGrossVrabec(const Eigen::ArrayX< double > &m, const Eigen::ArrayX< double > &sigma_Angstrom, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::ArrayX< double > &mustar2, const Eigen::ArrayX< double > &nmu, const Eigen::ArrayX< double > &Qstar2, const Eigen::ArrayX< double > &nQ)
const std::optional< QuadrupolarContributionGross > quad
static constexpr multipolar_argument_spec arg_spec
auto get_alpha2(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions) const
auto get_alpha3(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions) const
auto eval(const TTYPE &T, const RhoType &rhoN, const RhoStarType &rhostar, const VecType &mole_fractions) const
MultipolarContributionGubbinsTwu(const Eigen::ArrayX< double > &sigma_m, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::ArrayX< double > &mubar2, const Eigen::ArrayX< double > &Qbar2, multipolar_rhostar_approach approach)
auto get_rhostar(const RhoType rhoN, const PFType &packing_fraction, const MoleFractions &mole_fractions) const
MultipolarContributionGubbinsTwu & operator=(const MultipolarContributionGubbinsTwu &)=delete
auto get_alpha3QQ(const TTYPE &T, const RhoType &rhoN_A3, const EtaType &eta, const VecType &mole_fractions) const
Eq. 10 from Gross, AICHEJ, doi: 10.1002/aic.10502.
auto eval(const TTYPE &T, const RhoType &rho_A3, const EtaType &eta, const VecType &mole_fractions) const
QuadrupolarContributionGross(const Eigen::ArrayX< double > &m, const Eigen::ArrayX< double > &sigma_Angstrom, const Eigen::ArrayX< double > &epsilon_over_k, const Eigen::ArrayX< double > &Qstar2, const Eigen::ArrayX< double > &nQ)
QuadrupolarContributionGross & operator=(const QuadrupolarContributionGross &)=delete
auto get_alpha2QQ(const TTYPE &T, const RhoType &rhoN_A3, const EtaType &eta, const VecType &mole_fractions) const
Eq. 9 from Gross, AICHEJ, doi: 10.1002/aic.10502.
NLOHMANN_JSON_SERIALIZE_ENUM(multipolar_rhostar_approach, { {multipolar_rhostar_approach::kInvalid, nullptr}, {multipolar_rhostar_approach::use_packing_fraction, "use_packing_fraction"}, {multipolar_rhostar_approach::calculate_Gubbins_rhostar, "calculate_Gubbins_rhostar"}, }) template< typename type > struct MultipolarContributionGrossVrabecTerms
auto get_JQQ_3ijk(const Eta &eta, const MType &mijk)
Eq. 13 from Gross and Vrabec, AICHEJ.
auto get_JQQ_2ij(const Eta &eta, const MType &mij, const TType &Tstarij)
Eq. 12 from Gross and Vrabec, AICHEJ.
auto get_JDQ_2ij(const Eta &eta, const MType &mij, const TType &Tstarij)
Eq. 16 from Vrabec and Gross, JPCB, 2008. doi: 10.1021/jp072619u.
auto get_JDQ_3ijk(const Eta &eta, const MType &mijk)
Eq. 17 from Vrabec and Gross, JPCB, 2008. doi: 10.1021/jp072619u.
auto get_Kijk_334445(const KType &Kint, const RhoType &rhostar, const Txy &Tstarij, const Txy &Tstarik, const Txy &Tstarjk)
auto get_Kijk(const KType &Kint, const RhoType &rhostar, const Txy &Tstarij, const Txy &Tstarik, const Txy &Tstarjk)
std::variant< MultipolarContributionGrossVrabec, MultipolarContributionGrayGubbins< GubbinsTwuJIntegral, GubbinsTwuKIntegral >, MultipolarContributionGrayGubbins< GottschalkJIntegral, GottschalkKIntegral >, MultipolarContributionGrayGubbins< LuckasJIntegral, LuckasKIntegral >, MultipolarContributionGubbinsTwu< LuckasJIntegral, LuckasKIntegral >, MultipolarContributionGubbinsTwu< GubbinsTwuJIntegral, GubbinsTwuKIntegral >, MultipolarContributionGubbinsTwu< GottschalkJIntegral, GottschalkKIntegral > > multipolar_contributions_variant
The variant containing the multipolar types that can be provided.
auto get_JDD_2ij(const Eta &eta, const MType &mij, const TType &Tstarij)
Eq. 10 from Gross and Vrabec.
auto get_JDD_3ijk(const Eta &eta, const MType &mijk)
Eq. 11 from Gross and Vrabec.
const double k_e
Coulomb constant, with units of N m^2 / C^2.
Definition constants.hpp:16
const double k_B
Boltzmann constant.
Definition constants.hpp:13
auto POW2(const A &x)
auto POW7(const A &x)
auto POW5(const A &x)
auto toeig(const std::vector< double > &v) -> Eigen::ArrayXd
Definition types.hpp:10
auto POW3(const A &x)
auto getbaseval(const T &expr)
Definition types.hpp:84
void error_if_expr(T &&)
Definition types.hpp:63
T powi(const T &x, int n)
From Ulrich Deiters.
Definition types.hpp:133
auto pow(const double &x, const double &e)
Definition types.hpp:189
auto forceeval(T &&expr)
Definition types.hpp:46
auto POW4(const A &x)