teqp 0.22.0
Loading...
Searching...
No Matches
critical_pure.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "nlohmann/json.hpp"
4
5#include <Eigen/Dense>
6#include "teqp/derivs.hpp"
7#include "teqp/exceptions.hpp"
10
11#include <optional>
12
13using namespace teqp::cppinterface;
14
15namespace teqp {
16
22 inline auto get_pure_critical_conditions_Jacobian(const AbstractModel& model, const double T, const double rho,
23 const std::optional<std::size_t>& alternative_pure_index = std::nullopt, const std::optional<std::size_t>& alternative_length = std::nullopt) {
24
25 Eigen::ArrayXd z;
26 if (!alternative_pure_index) {
27 z = (Eigen::ArrayXd(1) << 1.0).finished();
28 }
29 else {
30 z = Eigen::ArrayXd(alternative_length.value()); z.setZero();
31 auto index = alternative_pure_index.value();
32 if (index >= 0 && index < static_cast<std::size_t>(z.size())){
33 z(index) = 1.0;
34 }
35 else{
36 throw teqp::InvalidArgument("The provided alternative index of " + std::to_string(index) + " is out of range");
37 }
38 }
39 auto R = model.get_R(z);
40
41 auto ders = model.get_Ar04n(T, rho, z);
42
43 auto dpdrho = R * T * (1 + 2 * ders[1] + ders[2]); // Should be zero at critical point
44 auto d2pdrho2 = R * T / rho * (2 * ders[1] + 4 * ders[2] + ders[3]); // Should be zero at critical point
45
46 auto resids = (Eigen::ArrayXd(2) << dpdrho, d2pdrho2).finished();
47
48 /* Sympy code for derivatives:
49 import sympy as sy
50 rho, R, Trecip,T = sy.symbols('rho,R,(1/T),T')
51 alphar = sy.symbols('alphar', cls=sy.Function)(Trecip, rho)
52 p = rho*R/Trecip*(1 + rho*sy.diff(alphar,rho))
53 dTrecip_dT = -1/T**2
54
55 sy.simplify(sy.diff(p,rho,3).replace(Trecip,1/T))
56
57 sy.simplify(sy.diff(sy.diff(p,rho,1),Trecip)*dTrecip_dT)
58
59 sy.simplify(sy.diff(sy.diff(p,rho,2),Trecip)*dTrecip_dT)
60 */
61
62 // Note: these derivatives are expressed in terms of 1/T and rho as independent variables
63 auto Ar11 = model.get_Ar11(T, rho, z);
64 auto Ar12 = model.get_Ar12(T, rho, z);
65 auto Ar13 = model.get_Ar13(T, rho, z);
66
67 auto d3pdrho3 = R * T / (rho * rho) * (6 * ders[2] + 6 * ders[3] + ders[4]);
68 auto d_dpdrho_dT = R * (-(Ar12 + 2 * Ar11) + ders[2] + 2 * ders[1] + 1);
69 auto d_d2pdrho2_dT = R / rho * (-(Ar13 + 4 * Ar12 + 2 * Ar11) + ders[3] + 4 * ders[2] + 2 * ders[1]);
70
71 // Jacobian of resid terms w.r.t. T and rho
72 Eigen::MatrixXd J(2, 2);
73 J(0, 0) = d_dpdrho_dT; // d(dpdrho)/dT
74 J(0, 1) = d2pdrho2; // d2pdrho2
75 J(1, 0) = d_d2pdrho2_dT; // d(d2pdrho2)/dT
76 J(1, 1) = d3pdrho3; // d3pdrho3
77
78 return std::make_tuple(resids, J);
79 }
80
81 template <typename Model, typename Scalar, ADBackends backend = ADBackends::autodiff,
82 typename = typename std::enable_if<not std::is_base_of<teqp::cppinterface::AbstractModel, Model>::value>::type>
83 auto get_pure_critical_conditions_Jacobian(const Model& model, const Scalar T, const Scalar rho,
84 const std::optional<std::size_t>& alternative_pure_index = std::nullopt, const std::optional<std::size_t>& alternative_length = std::nullopt) {
85 using namespace teqp::cppinterface::adapter;
86 auto view_ = std::unique_ptr<AbstractModel>(view(model));
87// static_assert(std::is_base_of<teqp::cppinterface::AbstractModel, std::decay_t<decltype(*view_)> >::value);
88 return get_pure_critical_conditions_Jacobian(*(view_), T, rho, alternative_pure_index, alternative_length);
89 }
90
91 template<typename Model, typename Scalar, ADBackends backend = ADBackends::autodiff, typename = typename std::enable_if<not std::is_base_of<teqp::cppinterface::AbstractModel, Model>::value>::type>
92 auto solve_pure_critical(const Model& model, const Scalar T0, const Scalar rho0, const std::optional<nlohmann::json>& flags = std::nullopt) {
93 auto x = (Eigen::ArrayXd(2) << T0, rho0).finished();
94 int maxsteps = 10;
95 std::optional<std::size_t> alternative_pure_index;
96 std::optional<std::size_t> alternative_length;
97 if (flags){
98 if (flags.value().contains("maxsteps")){
99 maxsteps = flags.value().at("maxsteps");
100 }
101 if (flags.value().contains("alternative_pure_index")){
102 auto i = flags.value().at("alternative_pure_index").get<int>();
103 if (i < 0){ throw teqp::InvalidArgument("alternative_pure_index cannot be less than 0"); }
104 alternative_pure_index = i;
105 }
106 if (flags.value().contains("alternative_length")){
107 auto i = flags.value().at("alternative_length").get<int>();
108 if (i < 2){ throw teqp::InvalidArgument("alternative_length cannot be less than 2"); }
109 alternative_length = i;
110 }
111 }
112 // A convenience method to make linear system solving more concise with Eigen datatypes
113 // All arguments are converted to matrices, the solve is done, and an array is returned
114 auto linsolve = [](const auto& a, const auto& b) {
115 return a.matrix().colPivHouseholderQr().solve(b.matrix()).array().eval();
116 };
117 for (auto counter = 0; counter < maxsteps; ++counter) {
118 auto [resids, Jacobian] = get_pure_critical_conditions_Jacobian(model, x[0], x[1], alternative_pure_index, alternative_length);
119 auto v = linsolve(Jacobian, -resids);
120 x += v;
121 }
122 return std::make_tuple(x[0], x[1]);
123 }
124
125 inline auto solve_pure_critical(const AbstractModel& model, const double T0, const double rho0, const std::optional<nlohmann::json>& flags = std::nullopt) {
126 auto x = (Eigen::ArrayXd(2) << T0, rho0).finished();
127 int maxsteps = 10;
128 std::optional<std::size_t> alternative_pure_index;
129 std::optional<std::size_t> alternative_length;
130 if (flags){
131 if (flags.value().contains("maxsteps")){
132 maxsteps = flags.value().at("maxsteps");
133 }
134 if (flags.value().contains("alternative_pure_index")){
135 auto i = flags.value().at("alternative_pure_index").get<int>();
136 if (i < 0){ throw teqp::InvalidArgument("alternative_pure_index cannot be less than 0"); }
137 alternative_pure_index = i;
138 }
139 if (flags.value().contains("alternative_length")){
140 auto i = flags.value().at("alternative_length").get<int>();
141 if (i < 2){ throw teqp::InvalidArgument("alternative_length cannot be less than 2"); }
142 alternative_length = i;
143 }
144 }
145 // A convenience method to make linear system solving more concise with Eigen datatypes
146 // All arguments are converted to matrices, the solve is done, and an array is returned
147 auto linsolve = [](const auto& a, const auto& b) {
148 return a.matrix().colPivHouseholderQr().solve(b.matrix()).array().eval();
149 };
150 for (auto counter = 0; counter < maxsteps; ++counter) {
151 auto [resids, Jacobian] = get_pure_critical_conditions_Jacobian(model, x[0], x[1], alternative_pure_index, alternative_length);
152 auto v = linsolve(Jacobian, -resids);
153 x += v;
154 }
155 return std::make_tuple(x[0], x[1]);
156 }
157
158 template <typename Model, typename Scalar, typename = typename std::enable_if<not std::is_base_of<teqp::cppinterface::AbstractModel, Model>::value>::type>
159 Scalar get_Brho_critical_extrap(const Model& model, const Scalar& Tc, const Scalar& rhoc, const std::optional<Eigen::ArrayXd>& z = std::nullopt) {
160
162 auto z_ = (Eigen::ArrayXd(1) << 1.0).finished();
163 if (z){
164 z_ = z.value();
165 }
166 auto R = model.R(z_);
167 auto ders = tdx::template get_Ar0n<4>(model, Tc, rhoc, z_);
168 //auto dpdrho = R*Tc*(1 + 2 * ders[1] + ders[2]); // Should be zero
169 //auto d2pdrho2 = R*Tc/rhoc*(2 * ders[1] + 4 * ders[2] + ders[3]); // Should be zero
170 auto d3pdrho3 = R * Tc / (rhoc * rhoc) * (6 * ders[2] + 6 * ders[3] + ders[4]);
171 auto Ar11 = tdx::template get_Ar11(model, Tc, rhoc, z_);
172 auto Ar12 = tdx::template get_Ar12(model, Tc, rhoc, z_);
173 auto d2pdrhodT = R * (1 + 2 * ders[1] + ders[2] - 2 * Ar11 - Ar12);
174 auto Brho = sqrt(6 * d2pdrhodT * Tc / d3pdrho3);
175 return Brho;
176 }
177
178
179 template <typename Model, typename Scalar, typename = typename std::enable_if<not std::is_base_of<teqp::cppinterface::AbstractModel, Model>::value>::type>
180 Eigen::Array<double, 2, 1> extrapolate_from_critical(const Model& model, const Scalar& Tc, const Scalar& rhoc, const Scalar& T, const std::optional<Eigen::ArrayXd>& z = std::nullopt) {
181 auto Brho = get_Brho_critical_extrap(model, Tc, rhoc, z);
182
183 auto drhohat_dT = Brho / Tc;
184 auto dT = T - Tc;
185
186 auto drhohat = dT * drhohat_dT;
187 auto rholiq = -drhohat / sqrt(1 - T / Tc) + rhoc;
188 auto rhovap = drhohat / sqrt(1 - T / Tc) + rhoc;
189 return (Eigen::ArrayXd(2) << rholiq, rhovap).finished();
190 }
191
192
193
194 inline double get_Brho_critical_extrap(const AbstractModel& model, const double& Tc, const double& rhoc, const std::optional<Eigen::ArrayXd>& z = std::nullopt) {
195
196 auto z_ = (Eigen::ArrayXd(1) << 1.0).finished();
197 if (z){
198 z_ = z.value();
199 }
200 auto R = model.get_R(z_);
201 auto ders = model.get_Ar04n(Tc, rhoc, z_);
202 //auto dpdrho = R*Tc*(1 + 2 * ders[1] + ders[2]); // Should be zero
203 //auto d2pdrho2 = R*Tc/rhoc*(2 * ders[1] + 4 * ders[2] + ders[3]); // Should be zero
204 auto d3pdrho3 = R * Tc / (rhoc * rhoc) * (6 * ders[2] + 6 * ders[3] + ders[4]);
205 auto Ar11 = model.get_Ar11(Tc, rhoc, z_);
206 auto Ar12 = model.get_Ar12(Tc, rhoc, z_);
207 auto d2pdrhodT = R * (1 + 2 * ders[1] + ders[2] - 2 * Ar11 - Ar12);
208 auto Brho = sqrt(6 * d2pdrhodT * Tc / d3pdrho3);
209 return Brho;
210 }
211
212 inline auto extrapolate_from_critical(const AbstractModel& model, const double& Tc, const double& rhoc, const double& T, const std::optional<Eigen::ArrayXd>& z = std::nullopt) {
213
214 auto Brho = get_Brho_critical_extrap(model, Tc, rhoc, z);
215
216 auto drhohat_dT = Brho / Tc;
217 auto dT = T - Tc;
218
219 auto drhohat = dT * drhohat_dT;
220 auto rholiq = -drhohat / sqrt(1 - T / Tc) + rhoc;
221 auto rhovap = drhohat / sqrt(1 - T / Tc) + rhoc;
222 return (Eigen::ArrayXd(2) << rholiq, rhovap).finished();
223 }
224};
virtual double get_R(const EArrayd &) const =0
virtual double get_Ar12(const double T, const double rho, const REArrayd &molefrac) const =0
virtual double get_Ar11(const double T, const double rho, const REArrayd &molefrac) const =0
virtual EArrayd get_Ar04n(const double T, const double rho, const REArrayd &molefrac) const =0
virtual double get_Ar13(const double T, const double rho, const REArrayd &molefrac) const =0
auto view(const TemplatedModel &tp)
auto linsolve(const A &a, const B &b)
Definition VLE.hpp:55
auto solve_pure_critical(const Model &model, const Scalar T0, const Scalar rho0, const std::optional< nlohmann::json > &flags=std::nullopt)
Eigen::Array< double, 2, 1 > extrapolate_from_critical(const Model &model, const Scalar &Tc, const Scalar &rhoc, const Scalar &T, const std::optional< Eigen::ArrayXd > &z=std::nullopt)
auto get_pure_critical_conditions_Jacobian(const AbstractModel &model, const double T, const double rho, const std::optional< std::size_t > &alternative_pure_index=std::nullopt, const std::optional< std::size_t > &alternative_length=std::nullopt)
Scalar get_Brho_critical_extrap(const Model &model, const Scalar &Tc, const Scalar &rhoc, const std::optional< Eigen::ArrayXd > &z=std::nullopt)
ADBackends
Definition derivs.hpp:92