teqp 0.23.1
Loading...
Searching...
No Matches
pure_param_optimization.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <tuple>
4#include <variant>
5
6#include "nlohmann/json.hpp"
8#include "teqp/exceptions.hpp"
10
11#include <boost/asio/thread_pool.hpp>
12#include <boost/asio/post.hpp>
13
15
18 auto check_fields() const{}
19 template<typename Model>
20 auto calculate_contribution(const Model& model) const{
21 auto rhoLrhoV = model->pure_VLE_T(T, rhoL_guess, rhoV_guess, 10);
22 return std::abs(rhoLrhoV[0]-rhoL_exp)*weight;
23 }
24};
25
26#define stdstringify(s) std::string(#s)
27
28#define PVTNoniterativePoint_optionalfields X(T) X(rho_exp) X(p_exp)
30 #define X(field) std::optional<double> field;
32 #undef X
33 double weight=1.0, R=8.31446261815324;
34 Eigen::ArrayXd z = (Eigen::ArrayXd(1) << 1.0).finished();
35
36 auto check_fields() const{
37 #define X(field) if (!field){ throw teqp::InvalidArgument("A field [" + stdstringify(field) + "] has not been initialized"); }
39 #undef X
40 }
41
42 template<typename Model>
43 auto calculate_contribution(const Model& model) const{
44 // See for instance Eq. 17 in https://doi.org/10.1063/5.0086060
45 double T_ = T.value(), rho_ = rho_exp.value();
46 auto Ar0n = model->get_Ar02n(T_, rho_, z);
47 auto p = rho_*R*T_*(1+Ar0n[1]);
48 auto dpdrho = R*T_*(1 + 2*Ar0n[1] + Ar0n[2]);
49 return std::abs((p-p_exp.value())/rho_exp.value()/dpdrho)*weight;
50 }
51};
52
53#define SatRhoLPPoint_optionalfields X(T) X(p_exp) X(rhoL_exp) X(rhoL_guess) X(rhoV_guess)
55 #define X(field) std::optional<double> field;
57 #undef X
58 double weight_rho=1.0, weight_p=1.0, R=8.31446261815324;
59 Eigen::ArrayXd z = (Eigen::ArrayXd(1) << 1.0).finished();
60
61 auto check_fields() const{
62 #define X(field) if (!field){ throw teqp::InvalidArgument("A field [" + stdstringify(field) + "] has not been initialized"); }
64 #undef X
65 }
66
67 template<typename Model>
68 auto calculate_contribution(const Model& model) const{
69 auto rhoLrhoV = model->pure_VLE_T(T.value(), rhoL_guess.value(), rhoV_guess.value(), 10);
70 auto rhoL = rhoLrhoV[0];
71 auto p = rhoL*R*T.value()*(1+model->get_Ar01(T.value(), rhoL, z));
72// std::cout << p << "," << p_exp << "," << (p_exp-p)/p_exp << std::endl;
73
74 double cost_rhoL = std::abs(rhoL-rhoL_exp.value())/rhoL_exp.value()*weight_rho;
75 double cost_p = std::abs(p-p_exp.value())/p_exp.value()*weight_p;
76 return ((weight_rho != 0) ? cost_rhoL : 0) + ((weight_p != 0) ? cost_p : 0);
77 }
78};
79
80#define SatRhoLPWPoint_optionalfields X(T) X(p_exp) X(rhoL_exp) X(w_exp) X(rhoL_guess) X(rhoV_guess) X(Ao20) X(M) X(R)
82
83 #define X(field) std::optional<double> field;
85 #undef X
86 double weight_rho=1.0, weight_p=1.0, weight_w=1.0;
87 Eigen::ArrayXd z = (Eigen::ArrayXd(1) << 1.0).finished();
88
89 auto check_fields() const{
90 #define X(field) if (!field){ throw teqp::InvalidArgument("A field [" + stdstringify(field) + "] has not been initialized"); }
92 #undef X
93 }
94
95 template<typename Model>
96 auto calculate_contribution(const Model& model) const{
97
98 auto rhoLrhoV = model->pure_VLE_T(T.value(), rhoL_guess.value(), rhoV_guess.value(), 10);
99
100 // First part, density
101 auto rhoL = rhoLrhoV[0];
102
103 auto Ar0n = model->get_Ar02n(T.value(), rhoL, z);
104 double Ar01 = Ar0n[1], Ar02 = Ar0n[2];
105 auto Ar11 = model->get_Ar11(T.value(), rhoL, z);
106 auto Ar20 = model->get_Ar20(T.value(), rhoL, z);
107
108 // Second part, presure
109 auto p = rhoL*R.value()*T.value()*(1+Ar01);
110
111 // Third part, speed of sound
112 //
113 // M*w^2/(R*T) where w is the speed of sound
114 // from the definition w = sqrt(dp/drho|s)
115 double Mw2RT = 1 + 2*Ar01 + Ar02 - POW2(1.0 + Ar01 - Ar11)/(Ao20.value() + Ar20);
116 double w = sqrt(Mw2RT*R.value()*T.value()/M.value());
117
118// std::cout << p << "," << p_exp << "," << (p_exp-p)/p_exp << std::endl;
119 double cost_rhoL = std::abs(rhoL-rhoL_exp.value())/rhoL_exp.value()*weight_rho;
120 double cost_p = std::abs(p-p_exp.value())/p_exp.value()*weight_p;
121 double cost_w = std::abs(w-w_exp.value())/w_exp.value()*weight_w;
122 return ((weight_rho != 0) ? cost_rhoL : 0) + ((weight_p != 0) ? cost_p : 0) + ((weight_w != 0) ? cost_w : 0);
123 }
124};
125
126#define SOSPoint_fields X(T) X(p_exp) X(rho_guess) X(w_exp) X(Ao20) X(M) X(R)
127struct SOSPoint{
128
129 #define X(field) std::optional<double> field;
131 #undef X
132
133 double weight_w=1.0;
134 Eigen::ArrayXd z = (Eigen::ArrayXd(1) << 1.0).finished();
135
136 auto check_fields() const{
137 #define X(field) if (!field){ throw teqp::InvalidArgument("A field [" + stdstringify(field) + "] has not been initialized"); }
139 #undef X
140 }
141
142 template<typename Model>
143 auto calculate_contribution(const Model& model) const{
144
145 double rho = rho_guess.value();
146 double R_ = R.value();
147 double T_K_ = T.value();
148
149 // First part, iterate for density
150 // ...
151 for (auto step = 0; step < 10; ++step){
152 auto Ar0n = model->get_Ar02n(T_K_, rho, z);
153 double Ar01 = Ar0n[1], Ar02 = Ar0n[2];
154 double pEOS = rho*R_*T_K_*(1+Ar01);
155 double dpdrho = R_*T_K_*(1 + 2*Ar0n[1] + Ar0n[2]);
156 double res = (pEOS-p_exp.value())/p_exp.value();
157 double dresdrho = dpdrho/p_exp.value();
158 double change = -res/dresdrho;
159 if (std::abs(change/rho-1) < 1e-10 || abs(res) < 1e-12){
160 break;
161 }
162 rho += change;
163 }
164
165 // Second part, speed of sound
166 //
167 auto Ar0n = model->get_Ar02n(T_K_, rho, z);
168 double Ar01 = Ar0n[1], Ar02 = Ar0n[2];
169 auto Ar11 = model->get_Ar11(T_K_, rho, z);
170 auto Ar20 = model->get_Ar20(T_K_, rho, z);
171
172 // M*w^2/(R*T) where w is the speed of sound
173 // from the definition w = sqrt(dp/drho|s)
174 double Mw2RT = 1.0 + 2.0*Ar01 + Ar02 - POW2(1.0 + Ar01 - Ar11)/(Ao20.value() + Ar20);
175 double w = sqrt(Mw2RT*R_*T_K_/M.value());
176 double cost_w = std::abs(w-w_exp.value())/w_exp.value()*weight_w;
177 return cost_w;
178 }
179};
180
181using PureOptimizationContribution = std::variant<SatRhoLPoint, SatRhoLPPoint, SatRhoLPWPoint, SOSPoint, PVTNoniterativePoint>;
182
184private:
185 auto make_pointers(const std::vector<std::variant<std::string, std::vector<std::string>>>& pointerstrs){
186 std::vector<std::vector<nlohmann::json::json_pointer>> pointers_;
187 for (auto & pointer: pointerstrs){
188 if (std::holds_alternative<std::string>(pointer)){
189 const std::string& s= std::get<std::string>(pointer);
190 pointers_.emplace_back(1, nlohmann::json::json_pointer(s));
191 }
192 else{
193 std::vector<nlohmann::json::json_pointer> buffer;
194 for (const auto& s : std::get<std::vector<std::string>>(pointer)){
195 buffer.emplace_back(s);
196 }
197 pointers_.push_back(buffer);
198 }
199 }
200 return pointers_;
201 }
202public:
203 const nlohmann::json jbase;
204 std::vector<std::vector<nlohmann::json::json_pointer>> pointers;
205 std::vector<PureOptimizationContribution> contributions;
206
207 PureParameterOptimizer(const nlohmann::json jbase, const std::vector<std::variant<std::string, std::vector<std::string>>>& pointerstrs) : jbase(jbase), pointers(make_pointers(pointerstrs)){}
208
210 std::visit([](const auto&c){c.check_fields();}, cont);
211 contributions.push_back(cont);
212 }
213
214 auto prepare_helper_models(const auto& model) const {
215 return std::vector<double>{1, 3};
216 }
217
218 template<typename T>
219 nlohmann::json build_JSON(const T& x) const{
220
221 if (x.size() != pointers.size()){
222 throw teqp::InvalidArgument("sizes don't match");
223 };
224 nlohmann::json j = jbase;
225 for (auto i = 0; i < x.size(); ++i){
226 for (const auto& ptr : pointers[i]){
227 j.at(ptr) = x[i];
228 }
229 }
230 return j;
231 }
232
233 template<typename T>
234 auto prepare(const T& x) const {
235 auto j = build_JSON(x);
236 auto model = teqp::cppinterface::make_model(j);
237 auto helpers = prepare_helper_models(model);
238 return std::make_tuple(std::move(model), helpers);
239 }
240
241 template<typename T>
242 auto cost_function(const T& x) const{
243 const auto [_model, _helpers] = prepare(x);
244 const auto& model = _model;
245 const auto& helpers = _helpers;
246 double cost = 0.0;
247 for (const auto& contrib : contributions){
248 cost += std::visit([&model](const auto& c){ return c.calculate_contribution(model); }, contrib);
249 }
250 if (!std::isfinite(cost)){
251 return 1e30;
252 }
253 return cost;
254 }
255
256 template<typename T>
257 auto cost_function_threaded(const T& x, std::size_t Nthreads) {
258 boost::asio::thread_pool pool{Nthreads}; // Nthreads in the pool
259 const auto [_model, _helpers] = prepare(x);
260 const auto& model = _model;
261 const auto& helpers = _helpers;
262 std::valarray<double> buffer(contributions.size());
263 std::size_t i = 0;
264 for (const auto& contrib : contributions){
265 auto& dest = buffer[i];
266 auto payload = [&model, &dest, contrib] (){
267 dest = std::visit([&model](const auto& c){ return c.calculate_contribution(model); }, contrib);
268 if (!std::isfinite(dest)){ dest = 1e30; }
269 };
270 boost::asio::post(pool, payload);
271 i++;
272 }
273 pool.join();
274 double summer = 0.0;
275 for (auto i = 0; i < contributions.size(); ++i){
276// std::cout << buffer[i] << std::endl;
277 summer += buffer[i];
278 }
279// std::cout << summer << std::endl;
280 return summer;
281 }
282
283};
284
285}
PureParameterOptimizer(const nlohmann::json jbase, const std::vector< std::variant< std::string, std::vector< std::string > > > &pointerstrs)
std::vector< std::vector< nlohmann::json::json_pointer > > pointers
std::variant< SatRhoLPoint, SatRhoLPPoint, SatRhoLPWPoint, SOSPoint, PVTNoniterativePoint > PureOptimizationContribution
std::unique_ptr< AbstractModel > make_model(const nlohmann::json &, bool validate=true)
auto POW2(const A &x)
#define PVTNoniterativePoint_optionalfields
#define SatRhoLPWPoint_optionalfields
#define SOSPoint_fields
#define SatRhoLPPoint_optionalfields