teqp 0.22.0
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 return std::abs(rhoL-rhoL_exp.value())/rhoL_exp.value()*weight_rho + std::abs(p-p_exp.value())/p_exp.value()*weight_p;
74 }
75};
76
77#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)
79
80 #define X(field) std::optional<double> field;
82 #undef X
83 double weight_rho=1.0, weight_p=1.0, weight_w=1.0;
84 Eigen::ArrayXd z = (Eigen::ArrayXd(1) << 1.0).finished();
85
86 auto check_fields() const{
87 #define X(field) if (!field){ throw teqp::InvalidArgument("A field [" + stdstringify(field) + "] has not been initialized"); }
89 #undef X
90 }
91
92 template<typename Model>
93 auto calculate_contribution(const Model& model) const{
94
95 auto rhoLrhoV = model->pure_VLE_T(T.value(), rhoL_guess.value(), rhoV_guess.value(), 10);
96
97 // First part, density
98 auto rhoL = rhoLrhoV[0];
99
100 auto Ar0n = model->get_Ar02n(T.value(), rhoL, z);
101 double Ar01 = Ar0n[1], Ar02 = Ar0n[2];
102 auto Ar11 = model->get_Ar11(T.value(), rhoL, z);
103 auto Ar20 = model->get_Ar20(T.value(), rhoL, z);
104
105 // Second part, presure
106 auto p = rhoL*R.value()*T.value()*(1+Ar01);
107
108 // Third part, speed of sound
109 //
110 // M*w^2/(R*T) where w is the speed of sound
111 // from the definition w = sqrt(dp/drho|s)
112 double Mw2RT = 1 + 2*Ar01 + Ar02 - POW2(1.0 + Ar01 - Ar11)/(Ao20.value() + Ar20);
113 double w = sqrt(Mw2RT*R.value()*T.value()/M.value());
114
115// std::cout << p << "," << p_exp << "," << (p_exp-p)/p_exp << std::endl;
116 double cost_rhoL = std::abs(rhoL-rhoL_exp.value())/rhoL_exp.value()*weight_rho;
117 double cost_p = std::abs(p-p_exp.value())/p_exp.value()*weight_p;
118 double cost_w = std::abs(w-w_exp.value())/w_exp.value()*weight_w;
119 return ((weight_rho != 0) ? cost_rhoL : 0) + ((weight_p != 0) ? cost_p : 0) + ((weight_w != 0) ? cost_w : 0);
120 }
121};
122
123#define SOSPoint_fields X(T) X(p_exp) X(rho_guess) X(w_exp) X(Ao20) X(M) X(R)
124struct SOSPoint{
125
126 #define X(field) std::optional<double> field;
128 #undef X
129
130 double weight_w=1.0;
131 Eigen::ArrayXd z = (Eigen::ArrayXd(1) << 1.0).finished();
132
133 auto check_fields() const{
134 #define X(field) if (!field){ throw teqp::InvalidArgument("A field [" + stdstringify(field) + "] has not been initialized"); }
136 #undef X
137 }
138
139 template<typename Model>
140 auto calculate_contribution(const Model& model) const{
141
142 double rho = rho_guess.value();
143 double R_ = R.value();
144 double T_K_ = T.value();
145
146 // First part, iterate for density
147 // ...
148 for (auto step = 0; step < 10; ++step){
149 auto Ar0n = model->get_Ar02n(T_K_, rho, z);
150 double Ar01 = Ar0n[1], Ar02 = Ar0n[2];
151 double pEOS = rho*R_*T_K_*(1+Ar01);
152 double dpdrho = R_*T_K_*(1 + 2*Ar0n[1] + Ar0n[2]);
153 double res = (pEOS-p_exp.value())/p_exp.value();
154 double dresdrho = dpdrho/p_exp.value();
155 double change = -res/dresdrho;
156 if (std::abs(change/rho-1) < 1e-10 || abs(res) < 1e-12){
157 break;
158 }
159 rho += change;
160 }
161
162 // Second part, speed of sound
163 //
164 auto Ar0n = model->get_Ar02n(T_K_, rho, z);
165 double Ar01 = Ar0n[1], Ar02 = Ar0n[2];
166 auto Ar11 = model->get_Ar11(T_K_, rho, z);
167 auto Ar20 = model->get_Ar20(T_K_, rho, z);
168
169 // M*w^2/(R*T) where w is the speed of sound
170 // from the definition w = sqrt(dp/drho|s)
171 double Mw2RT = 1.0 + 2.0*Ar01 + Ar02 - POW2(1.0 + Ar01 - Ar11)/(Ao20.value() + Ar20);
172 double w = sqrt(Mw2RT*R_*T_K_/M.value());
173 double cost_w = std::abs(w-w_exp.value())/w_exp.value()*weight_w;
174 return cost_w;
175 }
176};
177
178using PureOptimizationContribution = std::variant<SatRhoLPoint, SatRhoLPPoint, SatRhoLPWPoint, SOSPoint, PVTNoniterativePoint>;
179
181private:
182 auto make_pointers(const std::vector<std::variant<std::string, std::vector<std::string>>>& pointerstrs){
183 std::vector<std::vector<nlohmann::json::json_pointer>> pointers_;
184 for (auto & pointer: pointerstrs){
185 if (std::holds_alternative<std::string>(pointer)){
186 const std::string& s= std::get<std::string>(pointer);
187 pointers_.emplace_back(1, nlohmann::json::json_pointer(s));
188 }
189 else{
190 std::vector<nlohmann::json::json_pointer> buffer;
191 for (const auto& s : std::get<std::vector<std::string>>(pointer)){
192 buffer.emplace_back(s);
193 }
194 pointers_.push_back(buffer);
195 }
196 }
197 return pointers_;
198 }
199public:
200 const nlohmann::json jbase;
201 std::vector<std::vector<nlohmann::json::json_pointer>> pointers;
202 std::vector<PureOptimizationContribution> contributions;
203
204 PureParameterOptimizer(const nlohmann::json jbase, const std::vector<std::variant<std::string, std::vector<std::string>>>& pointerstrs) : jbase(jbase), pointers(make_pointers(pointerstrs)){}
205
207 std::visit([](const auto&c){c.check_fields();}, cont);
208 contributions.push_back(cont);
209 }
210
211 auto prepare_helper_models(const auto& model) const {
212 return std::vector<double>{1, 3};
213 }
214
215 template<typename T>
216 nlohmann::json build_JSON(const T& x) const{
217
218 if (x.size() != pointers.size()){
219 throw teqp::InvalidArgument("sizes don't match");
220 };
221 nlohmann::json j = jbase;
222 for (auto i = 0; i < x.size(); ++i){
223 for (const auto& ptr : pointers[i]){
224 j.at(ptr) = x[i];
225 }
226 }
227 return j;
228 }
229
230 template<typename T>
231 auto prepare(const T& x) const {
232 auto j = build_JSON(x);
233 auto model = teqp::cppinterface::make_model(j);
234 auto helpers = prepare_helper_models(model);
235 return std::make_tuple(std::move(model), helpers);
236 }
237
238 template<typename T>
239 auto cost_function(const T& x) const{
240 const auto [_model, _helpers] = prepare(x);
241 const auto& model = _model;
242 const auto& helpers = _helpers;
243 double cost = 0.0;
244 for (const auto& contrib : contributions){
245 cost += std::visit([&model](const auto& c){ return c.calculate_contribution(model); }, contrib);
246 }
247 if (!std::isfinite(cost)){
248 return 1e30;
249 }
250 return cost;
251 }
252
253 template<typename T>
254 auto cost_function_threaded(const T& x, std::size_t Nthreads) {
255 boost::asio::thread_pool pool{Nthreads}; // Nthreads in the pool
256 const auto [_model, _helpers] = prepare(x);
257 const auto& model = _model;
258 const auto& helpers = _helpers;
259 std::valarray<double> buffer(contributions.size());
260 std::size_t i = 0;
261 for (const auto& contrib : contributions){
262 auto& dest = buffer[i];
263 auto payload = [&model, &dest, contrib] (){
264 dest = std::visit([&model](const auto& c){ return c.calculate_contribution(model); }, contrib);
265 if (!std::isfinite(dest)){ dest = 1e30; }
266 };
267 boost::asio::post(pool, payload);
268 i++;
269 }
270 pool.join();
271 double summer = 0.0;
272 for (auto i = 0; i < contributions.size(); ++i){
273// std::cout << buffer[i] << std::endl;
274 summer += buffer[i];
275 }
276// std::cout << summer << std::endl;
277 return summer;
278 }
279
280};
281
282}
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