teqp 0.23.1
Loading...
Searching...
No Matches
advancedmixing_cubics.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <vector>
4#include <variant>
5#include <valarray>
6#include <optional>
7
8#include "teqp/types.hpp"
9#include "teqp/constants.hpp"
10#include "teqp/exceptions.hpp"
12#include "teqp/json_tools.hpp"
14
15#include "nlohmann/json.hpp"
16
17#include <Eigen/Dense>
18
22
23namespace teqp {
24
26
31})
32
33struct AdvancedPRaEOptions{
35 double s = 2.0;
36 double CEoS = -sqrt(2.0)/2.0*log(1.0 + sqrt(2.0));
37 double R_JmolK = constants::R_CODATA2017;
38};
39
40inline void from_json(const json& j, AdvancedPRaEOptions& o) {
41 j.at("brule").get_to(o.brule);
42 j.at("s").get_to(o.s);
43 j.at("CEoS").get_to(o.CEoS);
44 if (j.contains("R / J/mol/K")){
45 o.R_JmolK = j.at("R / J/mol/K");
46 }
47}
48
53template <typename NumType, typename AlphaFunctions = std::vector<AlphaFunctionOptions>>
55public:
56 // Hard-coded values for Peng-Robinson
57 const NumType Delta1 = 1+sqrt(2.0);
58 const NumType Delta2 = 1-sqrt(2.0);
59 // See https://doi.org/10.1021/acs.iecr.1c00847
60 const NumType OmegaA = 0.45723552892138218938;
61 const NumType OmegaB = 0.077796073903888455972;
63
64
65protected:
66
67 std::valarray<NumType> Tc_K, pc_Pa;
68
69 std::valarray<NumType> ai, bi;
70
71 const AlphaFunctions alphas;
73 Eigen::ArrayXXd lmat;
74
76 const double s;
77 const double CEoS;
78 const double R_JmolK;
79
80 nlohmann::json meta;
81
82 template<typename TType, typename IndexType>
83 auto get_ai(TType& T, IndexType i) const {
84 auto alphai = std::visit([&](auto& t) { return t(T); }, alphas[i]);
85 return forceeval(ai[i]*alphai);
86 }
87
88 template<typename TType, typename IndexType>
89 auto get_bi(TType& /*T*/, IndexType i) const { return bi[i]; }
90
91 template<typename IndexType>
92 void check_lmat(IndexType N) {
93 if (lmat.cols() != lmat.rows()) {
94 throw teqp::InvalidArgument("lmat rows [" + std::to_string(lmat.rows()) + "] and columns [" + std::to_string(lmat.cols()) + "] are not identical");
95 }
96 if (lmat.cols() == 0) {
97 lmat.resize(N, N); lmat.setZero();
98 }
99 else if (lmat.cols() != static_cast<Eigen::Index>(N)) {
100 throw teqp::InvalidArgument("lmat needs to be a square matrix the same size as the number of components [" + std::to_string(N) + "]");
101 }
102 }
103
104public:
105 AdvancedPRaEres(const std::valarray<NumType>& Tc_K, const std::valarray<NumType>& pc_Pa, const AlphaFunctions& alphas, const ResidualHelmholtzOverRTVariant& ares, const Eigen::ArrayXXd& lmat, const AdvancedPRaEOptions& options = {})
106 : Tc_K(Tc_K), pc_Pa(pc_Pa), alphas(alphas), ares(ares), lmat(lmat), brule(options.brule), s(options.s), CEoS(options.CEoS), R_JmolK(options.R_JmolK)
107 {
108 ai.resize(Tc_K.size());
109 bi.resize(Tc_K.size());
110 for (auto i = 0U; i < Tc_K.size(); ++i) {
111 ai[i] = OmegaA * pow2(R_JmolK * Tc_K[i]) / pc_Pa[i];
112 bi[i] = OmegaB * R_JmolK * Tc_K[i] / pc_Pa[i];
113 }
114 check_lmat(ai.size());
115 };
116
117 void set_meta(const nlohmann::json& j) { meta = j; }
118 auto get_meta() const { return meta; }
119 auto get_lmat() const { return lmat; }
120 auto get_Tc_K() const { return Tc_K; }
121 auto get_pc_Pa() const { return pc_Pa; }
122
123 static double get_bi(double Tc_K, double pc_Pa){
124 const NumType OmegaB = 0.077796073903888455972;
125 const NumType R = 8.31446261815324;
126 return OmegaB*R*Tc_K/pc_Pa;
127 }
128
133 auto superanc_rhoLV(double T, std::optional<std::size_t> ifluid = std::nullopt) const {
134
135 std::valarray<double> molefracs(ai.size()); molefracs = 1.0;
136
137 // If more than one component, must provide the ifluid argument
138 if(ai.size() > 1){
139 if (!ifluid){
140 throw teqp::InvalidArgument("For mixtures, the argument ifluid must be provided");
141 }
142 if (ifluid.value() > ai.size()-1){
143 throw teqp::InvalidArgument("ifluid must be less than "+std::to_string(ai.size()));
144 }
145 molefracs = 0.0;
146 molefracs[ifluid.value()] = 1.0;
147 }
148
149 auto b = get_b(T, molefracs);
150 auto a = get_am_over_bm(T, molefracs)*b;
151 auto Ttilde = R(molefracs)*T*b/a;
152 return std::make_tuple(
153 CubicSuperAncillary::supercubic(superanc_code, CubicSuperAncillary::RHOL_CODE, Ttilde)/b,
154 CubicSuperAncillary::supercubic(superanc_code, CubicSuperAncillary::RHOV_CODE, Ttilde)/b
155 );
156 }
157
158 template<class VecType>
159 auto R(const VecType& /*molefrac*/) const {
160 return R_JmolK;
161 }
162
163 template<typename TType, typename CompType>
164 auto get_a(TType T, const CompType& molefracs) const {
165 return forceeval(get_am_over_bm(T, molefracs)*get_b(T, molefracs));
166 }
167
168 template<typename TType, typename CompType>
169 auto get_am_over_bm(TType T, const CompType& molefracs) const {
170 auto aEresRT = std::visit([&](auto& aresRTfunc) { return aresRTfunc(T, molefracs); }, ares); // aEres/RT, so a non-dimensional quantity
171 std::common_type_t<TType, decltype(molefracs[0])> summer = aEresRT*R_JmolK*T/CEoS;
172 for (auto i = 0U; i < molefracs.size(); ++i) {
173 summer += molefracs[i]*get_ai(T,i)/get_bi(T,i);
174 }
175 return forceeval(summer);
176 }
177
178 template<typename TType, typename CompType>
179 auto get_b(TType T, const CompType& molefracs) const {
180 std::common_type_t<TType, decltype(molefracs[0])> b_ = 0.0;
181
182 switch (brule){
184 for (auto i = 0U; i < molefracs.size(); ++i) {
185 auto bi_ = get_bi(T, i);
186 for (auto j = 0U; j < molefracs.size(); ++j) {
187 auto bj_ = get_bi(T, j);
188
189 auto bij = (1 - lmat(i,j)) * pow((pow(bi_, 1.0/s) + pow(bj_, 1.0/s))/2.0, s);
190 b_ += molefracs[i] * molefracs[j] * bij;
191 }
192 }
193 break;
195 for (auto i = 0U; i < molefracs.size(); ++i) {
196 b_ += molefracs[i] * get_bi(T, i);
197 }
198 break;
199 default:
200 throw teqp::InvalidArgument("Mixing rule for b is invalid");
201 }
202 return forceeval(b_);
203 }
204
205 template<typename TType, typename RhoType, typename MoleFracType>
206 auto alphar(const TType& T,
207 const RhoType& rho,
208 const MoleFracType& molefrac) const
209 {
210 if (static_cast<std::size_t>(molefrac.size()) != alphas.size()) {
211 throw std::invalid_argument("Sizes do not match");
212 }
213 auto b = get_b(T, molefrac);
214 auto a = get_am_over_bm(T, molefrac)*b;
215 auto Psiminus = -log(1.0 - b * rho);
216 auto Psiplus = log((Delta1 * b * rho + 1.0) / (Delta2 * b * rho + 1.0)) / (b * (Delta1 - Delta2));
217 auto val = Psiminus - a / (R_JmolK * T) * Psiplus;
218 return forceeval(val);
219 }
220};
221
222inline auto make_AdvancedPRaEres(const nlohmann::json& j){
223
224 std::valarray<double> Tc_K = j.at("Tcrit / K");
225 std::valarray<double> pc_Pa = j.at("pcrit / Pa");
226
227 std::vector<AlphaFunctionOptions> alphas = build_alpha_functions(Tc_K, j.at("alphas"));
228
229 auto get_ares_model = [&](const nlohmann::json& armodel) -> ResidualHelmholtzOverRTVariant {
230
231 std::string type = armodel.at("type");
232 if (type == "Wilson"){
233 std::vector<double> b;
234 for (auto i = 0U; i < Tc_K.size(); ++i){
235 b.push_back(teqp::AdvancedPRaEres<double>::get_bi(Tc_K[i], pc_Pa[i]));
236 }
237 auto mWilson = build_square_matrix(armodel.at("m"));
238 auto nWilson = build_square_matrix(armodel.at("n"));
239 return WilsonResidualHelmholtzOverRT<double>(b, mWilson, nWilson);
240 }
241 else{
242 throw teqp::InvalidArgument("bad type of ares model: " + type);
243 }
244 };
245 auto aresmodel = get_ares_model(j.at("aresmodel"));
246
247 AdvancedPRaEOptions options = j.at("options");
248 auto model = teqp::AdvancedPRaEres<double>(Tc_K, pc_Pa, alphas, aresmodel, Eigen::ArrayXXd::Zero(2, 2), options);
249 return model;
250}
251
253
254
255}; // namespace teqp
auto get_a(TType T, const CompType &molefracs) const
auto get_b(TType T, const CompType &molefracs) const
AdvancedPRaEres(const std::valarray< NumType > &Tc_K, const std::valarray< NumType > &pc_Pa, const AlphaFunctions &alphas, const ResidualHelmholtzOverRTVariant &ares, const Eigen::ArrayXXd &lmat, const AdvancedPRaEOptions &options={})
std::valarray< NumType > Tc_K
static double get_bi(double Tc_K, double pc_Pa)
const ResidualHelmholtzOverRTVariant ares
std::valarray< NumType > ai
auto get_ai(TType &T, IndexType i) const
const AlphaFunctions alphas
const AdvancedPRaEMixingRules brule
void set_meta(const nlohmann::json &j)
std::valarray< NumType > pc_Pa
auto get_bi(TType &, IndexType i) const
std::valarray< NumType > bi
auto R(const VecType &) const
auto superanc_rhoLV(double T, std::optional< std::size_t > ifluid=std::nullopt) const
auto get_am_over_bm(TType T, const CompType &molefracs) const
auto alphar(const TType &T, const RhoType &rho, const MoleFracType &molefrac) const
std::variant< NullResidualHelmholtzOverRT< double >, WilsonResidualHelmholtzOverRT< double >, BinaryInvariantResidualHelmholtzOverRT< double >, COSMOSAC::COSMO3 > ResidualHelmholtzOverRTVariant
const double R_CODATA2017
molar gas constant from CODATA 2017: https://doi.org/10.1103/RevModPhys.84.1527
Definition constants.hpp:10
auto build_square_matrix
auto make_AdvancedPRaEres(const nlohmann::json &j)
void from_json(const json &j, AdvancedPRaEOptions &o)
decltype(make_AdvancedPRaEres({})) advancedPRaEres_t
T pow2(const T &x)
Definition types.hpp:7
NLOHMANN_JSON_SERIALIZE_ENUM(AdvancedPRaEMixingRules, { {AdvancedPRaEMixingRules::knotspecified, nullptr}, {AdvancedPRaEMixingRules::kLinear, "Linear"}, {AdvancedPRaEMixingRules::kQuadratic, "Quadratic"}, }) struct AdvancedPRaEOptions
auto pow(const double &x, const double &e)
Definition types.hpp:195
auto forceeval(T &&expr)
Definition types.hpp:52
auto build_alpha_functions(const TC &Tc_K, const nlohmann::json &jalphas)