40 if (j.contains(
"alpha")){ j.at(
"alpha").get_to(o.
alpha); }
41 if (j.contains(
"rtol")){ j.at(
"rtol").get_to(o.
rtol); }
42 if (j.contains(
"atol")){ j.at(
"atol").get_to(o.
atol); }
43 if (j.contains(
"max_iters")){ j.at(
"max_iters").get_to(o.
max_iters); }
47namespace DufalMatrices{
48 extern const std::unordered_map<int, Eigen::MatrixXd>
bcoeffs;
62 using CompSite = std::tuple<std::size_t, std::string>;
79 ind.
N_sites.resize(molecule_sites.size());
84 std::size_t icomp = 0;
85 std::size_t siteid = 0;
86 for (
auto& molecule: molecule_sites){
88 std::map<std::string, int> site_counts;
89 for (
auto& site: molecule){
92 auto unique_sites_on_molecule = std::set(molecule.begin(), molecule.end());
97 std::size_t Cindex = 0;
98 for (
auto& site: unique_sites_on_molecule){
103 ind.
counts[siteid] = site_counts[site];
104 ind.
comp_index[siteid] =
static_cast<int>(icomp);
108 ind.
N_sites[icomp] =
static_cast<int>(molecule.size());
109 ind.
N_unique_sites[icomp] =
static_cast<int>(unique_sites_on_molecule.size());
113 ind.
counts.conservativeResize(siteid);
121 auto make_D(
const IndexMapper& ind,
const AssociationOptions&
options )
const{
123 auto get_DIJ = [&ind, &
options](std::size_t I, std::size_t J) ->
int {
129 auto [ph2, typej] = ind.to_CompSite.at(J);
135 auto contains = [](
auto& container,
const auto& val){
return std::find(container.begin(), container.end(), val) != container.end(); };
138 return ind.counts[J];
145 int Ngroups =
static_cast<int>(ind.to_siteid.size());
146 Eigen::ArrayXXi Dmat(Ngroups, Ngroups);
148 for (
int I = 0; I < Ngroups; ++I){
149 for (
int J = 0; J < Ngroups; ++J){
150 Dmat(I, J) = get_DIJ(I, J);
155 static auto toEig(
const nlohmann::json& j,
const std::string& k) -> Eigen::ArrayXd{
156 std::vector<double> vec = j.at(k);
157 return Eigen::Map<Eigen::ArrayXd>(&vec[0], vec.size());
159 static auto get_association_options(
const nlohmann::json&j){
160 AssociationOptions opt;
161 if (j.contains(
"options")){
162 opt = j.at(
"options").get<AssociationOptions>();
164 if (j.contains(
"/options/self_association_mask"_json_pointer)){
165 opt.self_association_mask = j.at(
"/options/self_association_mask"_json_pointer).template get<std::vector<bool>>();
173 const Eigen::ArrayXXi
D;
177 Association(
const Eigen::ArrayXd& b_m3mol,
const Eigen::ArrayXd& beta,
const Eigen::ArrayXd& epsilon_Jmol,
const std::vector<std::vector<std::string>>& molecule_sites,
const AssociationOptions&
options) :
options(
options),
mapper(make_mapper(molecule_sites,
options)),
D(make_D(
mapper,
options)),
m_Delta_rule(
options.Delta_rule),
datasidecar(
CanonicalData{b_m3mol, beta, epsilon_Jmol,
options.radial_dist}){
179 throw std::invalid_argument(
"Delta rule is invalid; options are: {CR1}");
187 std::set<std::string> unique_site_types;
188 for (
auto molsite : j.at(
"molecule_sites")) {
189 for (
auto & s : molsite){
190 unique_site_types.insert(s);
194 auto get_interaction_partners = [&](
const nlohmann::json& j){
195 std::map<std::string, std::vector<std::string>> interaction_partners;
197 if (j.contains(
"options") && j.at(
"options").contains(
"interaction_partners")){
198 interaction_partners = j.at(
"options").at(
"interaction_partners");
199 for (
auto [k,partners] : interaction_partners){
200 if (unique_site_types.count(k) == 0){
203 for (
auto& partner : partners){
204 if (unique_site_types.count(partner) == 0){
212 for (
auto& site1 : unique_site_types){
213 std::vector<std::string> partners;
214 for (
auto& site2: unique_site_types){
216 partners.push_back(site2);
219 interaction_partners[site1] = partners;
222 return interaction_partners;
225 if (j.contains(
"Delta_rule")){
226 std::string Delta_rule = j.at(
"Delta_rule");
227 if (Delta_rule ==
"CR1"){
229 data.
b_m3mol = toEig(j,
"b / m^3/mol");
230 data.
beta = toEig(j,
"beta");
232 auto options = get_association_options(j);
236 return {data, j.at(
"molecule_sites"),
options};
238 else if(Delta_rule ==
"Dufal"){
242 data.
sigma_m = toEig(j,
"sigma / m");
243 if (j.contains(
"epsilon / J/mol")){
246 else if (j.contains(
"epsilon/kB / K")){
252 data.
lambda_r = toEig(j,
"lambda_r");
256 data.
K_HB_m3 = toEig(j,
"K_HB / m^3");
259 auto options = get_association_options(j);
262 return {data, j.at(
"molecule_sites"),
options};
265 throw std::invalid_argument(
"The Delta_rule has not been specified");
274 template<
typename TType,
typename RhoType,
typename MoleFracsType>
275 auto get_Delta(
const TType& T,
const RhoType& rhomolar,
const MoleFracsType& molefracs)
const {
277 using resulttype = std::common_type_t<
decltype(T),
decltype(rhomolar),
decltype(molefracs[0])>;
278 using Mat = Eigen::Array<resulttype, Eigen::Dynamic, Eigen::Dynamic>;
282 using eta_t = std::common_type_t<
decltype(rhomolar),
decltype(molefracs[0])>;
283 std::optional<eta_t> g;
286 auto bmix = (molefracs*d.
b_m3mol).sum();
287 auto eta = bmix*rhomolar/4.0;
290 g = (2.0-eta)/(2.0*(1.0-eta)*(1.0-eta)*(1.0-eta));
break;
292 g = 1.0 / (1.0 - 1.9*eta);
break;
294 throw std::invalid_argument(
"Bad radial distribution");
299 auto get_I_Dufal = [](
const auto& Tstar,
const auto& rhostar,
const auto& lambda_r){
301 using result_t = std::decay_t<std::common_type_t<
decltype(Tstar),
decltype(rhostar),
decltype(lambda_r)>>;
302 result_t summer = 0.0;
304 std::decay_t<
decltype(rhostar)> rhostar_to_i = 1.0;
305 for (
auto i = 0U; i <= 10; ++i){
306 std::decay_t<
decltype(Tstar)> Tstar_to_j = 1.0;
307 for (
auto j = 0U; i + j <= 10; ++j){
308 double aij = 0.0, lambdar_to_k = 1.0;
309 for (
auto k = 0; k <= 6; ++k){
311 aij += bijk*lambdar_to_k;
312 lambdar_to_k *= lambda_r;
314 summer += aij*rhostar_to_i*Tstar_to_j;
317 rhostar_to_i *= rhostar;
322 using rhostar_t = std::common_type_t<
decltype(rhomolar),
decltype(molefracs[0])>;
323 std::optional<rhostar_t> rhostar;
327 std::decay_t<
decltype(molefracs[0])> sigma3_vdW1 = 0.0;
328 auto N = molefracs.size();
329 for (
auto i = 0U; i < N; ++i){
330 for (
auto j = 0U; j < N; ++j){
332 sigma3_vdW1 += molefracs[i]*molefracs[j]*sigma3_ij_m3;
335 rhostar = rhomolar*N_A*sigma3_vdW1;
338 Mat Delta = Mat::Zero(Ngroups, Ngroups);
339 for (
auto I = 0U; I < Ngroups; ++I){
341 for (
auto J = 0U; J < Ngroups; ++J){
351 auto beta_ij = sqrt(d.
beta[i]*d.
beta[j]);
352 Delta(I, J) = g.value()*b_ij*beta_ij*(exp(epsilon_ij_Jmol/(R_CODATA2017*T))-1.0)/N_A;
357 auto _I = get_I_Dufal(Tstar, rhostar.value(), d.
LAMBDA_Rij(i, j));
359 Delta(I, J) = F_Meyer*d.
KHBij_m3(i,j)*_I;
362 throw std::invalid_argument(
"Don't know what to do with this Delta rule");
369 template<
typename TType,
typename RhoType,
typename MoleFracsType,
typename XType>
370 auto successive_substitution(
const TType& T,
const RhoType& rhomolar,
const MoleFracsType& molefracs,
const XType& X_init)
const {
376 using resulttype = std::common_type_t<
decltype(T),
decltype(rhomolar),
decltype(molefracs[0])>;
377 using Mat = Eigen::Array<resulttype, Eigen::Dynamic, Eigen::Dynamic>;
379 const Mat Delta =
get_Delta(T, rhomolar, molefracs);
383 Eigen::RowVectorX<typename MoleFracsType::Scalar> xj(Ngroups);
384 for (
auto I = 0U; I< Ngroups; ++I){
388 using rDDXtype = std::decay_t<std::common_type_t<
typename decltype(Delta)::Scalar,
decltype(rhomolar),
decltype(molefracs[0])>>;
389 Eigen::ArrayX<std::decay_t<rDDXtype>>
X = X_init.template cast<rDDXtype>(), Xnew;
391 Eigen::MatrixX<rDDXtype> rDDX = rhomolar*N_A*(Delta.array()*
D.cast<resulttype>().array()).matrix();
392 for (
auto j = 0; j < rDDX.rows(); ++j){
393 rDDX.row(j).array() = rDDX.row(j).array()*xj.array().template cast<rDDXtype>();
401 auto Delta_ = Delta(0, 1);
402 auto kappa_A = rhomolar*N_A*
static_cast<double>(
mapper.
counts[0])*Delta_;
403 auto kappa_B = rhomolar*N_A*
static_cast<double>(
mapper.
counts[1])*Delta_;
405 auto X_A1 = (kappa_A-kappa_B-sqrt(kappa_A*kappa_A-2.0*kappa_A*kappa_B + 2.0*kappa_A + kappa_B*kappa_B + 2.0*kappa_B+1.0)-1.0)/(2.0*kappa_A);
406 auto X_A2 = (kappa_A-kappa_B+sqrt(kappa_A*kappa_A-2.0*kappa_A*kappa_B + 2.0*kappa_A + kappa_B*kappa_B + 2.0*kappa_B+1.0)-1.0)/(2.0*kappa_A);
414 auto X_B = 1.0/(1.0+kappa_A*
X(0));
424 auto diff = (Xnew-
X).eval().cwiseAbs().unaryExpr([](
const auto&x){
return getbaseval(x); }).eval();
426 if ((diff < tol).all()){
437 template<
typename TType,
typename RhoType,
typename MoleFracsType>
438 auto alphar(
const TType& T,
const RhoType& rhomolar,
const MoleFracsType& molefracs)
const {
448 using resulttype = std::common_type_t<
decltype(T),
decltype(rhomolar),
decltype(molefracs[0])>;
449 resulttype alpha_r_asso = 0.0;
450 for (
auto icomp = 0; icomp < molefracs.size(); ++icomp){
451 resulttype summer = 0.0;
457 summer += (log(X_A(I))- X_A(I)/2.0 + 0.5)*
static_cast<double>(
mapper.
counts(I));
459 alpha_r_asso += molefracs[icomp]*summer;
473 nlohmann::json
get_assoc_calcs(
double T,
double rhomolar,
const Eigen::ArrayXd& mole_fractions)
const{
475 using Mat = Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic>;
476 const Mat Delta =
get_Delta(T, rhomolar, mole_fractions);
481 auto fromArrayXd = [](
const Eigen::ArrayXd &x){std::valarray<double>n(x.size());
for (
auto i = 0U; i < n.size(); ++i){ n[i] = x[i];}
return n;};
482 auto fromArrayXXd = [](
const Eigen::ArrayXXd &x){
483 std::size_t N = x.rows();
484 std::vector<std::vector<double>> n; n.resize(N);
485 for (
auto i = 0U; i < N; ++i){
487 for (
auto j = 0U; j < N; ++j){
493 auto fromArrayXXi = [](
const Eigen::ArrayXXi &x){
494 std::size_t N = x.rows();
495 std::vector<std::vector<int>> n; n.resize(N);
496 for (
auto i = 0U; i < N; ++i){
498 for (
auto j = 0U; j < N; ++j){
508 {
"D", fromArrayXXi(
D.array())},
509 {
"Delta", fromArrayXXd(Delta.array())},
510 {
"X_A", fromArrayXd(XA.array())},
512 {
"note",
"X_A is the fraction of non-bonded sites for each siteid"}
nlohmann::json get_assoc_calcs(double T, double rhomolar, const Eigen::ArrayXd &mole_fractions) const
Get things from the association calculations for debug purposes.
const Delta_rules m_Delta_rule
std::tuple< std::size_t, std::size_t > CompCIndex
auto get_Delta(const TType &T, const RhoType &rhomolar, const MoleFracsType &molefracs) const
const std::variant< CanonicalData, DufalData > datasidecar
static Association factory(const nlohmann::json &j)
Association(const decltype(datasidecar)&data, const std::vector< std::vector< std::string > > &molecule_sites, const AssociationOptions &options)
std::tuple< std::size_t, std::string > CompSite
Association(const Eigen::ArrayXd &b_m3mol, const Eigen::ArrayXd &beta, const Eigen::ArrayXd &epsilon_Jmol, const std::vector< std::vector< std::string > > &molecule_sites, const AssociationOptions &options)
auto successive_substitution(const TType &T, const RhoType &rhomolar, const MoleFracsType &molefracs, const XType &X_init) const
const AssociationOptions options
auto alphar(const TType &T, const RhoType &rhomolar, const MoleFracsType &molefracs) const
Calculate the contribution , where the Helmholtz energy is on a molar basis, making dimensionless.
const std::unordered_map< int, Eigen::MatrixXd > bcoeffs
void from_json(const nlohmann::json &j, AssociationOptions &o)
const double R_CODATA2017
molar gas constant from CODATA 2017: https://doi.org/10.1103/RevModPhys.84.1527
auto getbaseval(const T &expr)
association::radial_dists radial_dist
std::map< std::string, std::vector< std::string > > interaction_partners
bool allow_explicit_fractions
std::vector< std::string > site_order
std::vector< bool > self_association_mask
association::Delta_rules Delta_rule
Eigen::ArrayXi N_unique_sites
How many unique types of sites are on each molecule.
Eigen::ArrayXi comp_index
The pure component indices associated with each siteid.
std::map< CompSite, std::size_t > to_siteid
Eigen::ArrayXi N_sites
How many total sites are on each molecule.
std::map< CompCIndex, std::size_t > CompCIndex_to_siteid
std::map< std::size_t, CompSite > to_CompSite
std::vector< std::vector< std::string > > molecule_sites
Eigen::ArrayXi counts
An array of counts of each siteid for the entire mixture.
Eigen::ArrayXd b_m3mol
The covolume b, in m^3/mol, one per component.
Eigen::ArrayXd epsilon_Jmol
The association energy of each molecule, in J/mol, one per component.
Eigen::ArrayXd beta
The volume factor, dimensionless, one per component.
Eigen::ArrayXd epsilon_Jmol
Eigen::ArrayXd EPSILONOVERK_HBij_K
Eigen::ArrayXd LAMBDA_Rij
Eigen::ArrayXd epsilon_HB_Jmol
void apply_mixing_rules()
Eigen::ArrayXd EPSILONOVERKij_K
Eigen::ArrayXXd kmat
Matrix of k_{ij} values.