OMCompiler/SimulationRuntime/c/moo/evaluations.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file belongs to the OpenModelica Run-Time System | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings | ||
| 5 | * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights | ||
| 6 | * reserved. | ||
| 7 | * | ||
| 8 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE | ||
| 9 | * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY | ||
| 10 | * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S | ||
| 11 | * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL | ||
| 12 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 13 | * | ||
| 14 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License | ||
| 15 | * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs: | ||
| 16 | * http://www.openmodelica.org or https://github.com/OpenModelica/ or | ||
| 17 | * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU | ||
| 18 | * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW | ||
| 19 | * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause. | ||
| 20 | * | ||
| 21 | * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY | ||
| 23 | * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF | ||
| 24 | * OSMC-PL. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <base/block_sparsity.h> | ||
| 29 | |||
| 30 | #include "hessian_finite_diff.h" | ||
| 31 | |||
| 32 | #include "evaluations.h" | ||
| 33 | |||
| 34 | namespace OpenModelica { | ||
| 35 | |||
| 36 | ✗ | void init_eval(InfoGDOP& info, GDOP::FullSweepLayout& layout_lfg, GDOP::BoundarySweepLayout& layout_mr) { | |
| 37 | ✗ | init_eval_lfg(info, layout_lfg); | |
| 38 | ✗ | init_eval_mr(info, layout_mr); | |
| 39 | ✗ | } | |
| 40 | |||
| 41 | /* just enumerate them in order: L -> f -> g. The correct placement will be handled in eval */ | ||
| 42 | ✗ | void init_eval_lfg(InfoGDOP& info, GDOP::FullSweepLayout& layout_lfg) { | |
| 43 | int buf_index = 0; | ||
| 44 | |||
| 45 | ✗ | if (layout_lfg.L) { | |
| 46 | ✗ | layout_lfg.L->buf_index = buf_index++; | |
| 47 | } | ||
| 48 | |||
| 49 | ✗ | for (auto& f : layout_lfg.f) { | |
| 50 | ✗ | f.buf_index = buf_index++; | |
| 51 | } | ||
| 52 | |||
| 53 | ✗ | for (auto& g : layout_lfg.g) { | |
| 54 | ✗ | g.buf_index = buf_index++; | |
| 55 | } | ||
| 56 | ✗ | } | |
| 57 | |||
| 58 | /* just enumerate them in order: M -> r. The correct placement will be handled in eval */ | ||
| 59 | ✗ | void init_eval_mr(InfoGDOP& info, GDOP::BoundarySweepLayout& layout_mr) { | |
| 60 | int buf_index = 0; | ||
| 61 | |||
| 62 | ✗ | if (layout_mr.M) { | |
| 63 | ✗ | layout_mr.M->buf_index = buf_index++; | |
| 64 | } | ||
| 65 | |||
| 66 | ✗ | for (auto& r : layout_mr.r) { | |
| 67 | ✗ | r.buf_index = buf_index++; | |
| 68 | } | ||
| 69 | ✗ | } | |
| 70 | |||
| 71 | ✗ | void init_jac(InfoGDOP& info, GDOP::FullSweepLayout& layout_lfg, GDOP::BoundarySweepLayout& layout_mr) { | |
| 72 | ✗ | init_jac_lfg(info, layout_lfg); | |
| 73 | ✗ | init_jac_mr(info, layout_mr); | |
| 74 | ✗ | } | |
| 75 | |||
| 76 | // rows are sorted as fLg (as in OpenModelica) | ||
| 77 | ✗ | void init_jac_lfg(InfoGDOP& info, GDOP::FullSweepLayout& layout_lfg) { | |
| 78 | /* full B Jacobian */ | ||
| 79 | ✗ | for (int nz = 0; nz < info.exc_jac->B.sparsity.nnz; nz++) { | |
| 80 | ✗ | int row = info.exc_jac->B.sparsity.row[nz]; // OpenModelica B matrix row | |
| 81 | ✗ | int col = info.exc_jac->B.sparsity.col[nz]; // OpenModelica B matrix col | |
| 82 | int csc_buffer_entry_B = info.exc_jac->B.sparsity.coo_to_csc(nz); // OpenModelica B matrix CSC buffer index | ||
| 83 | ✗ | FunctionLFG& fn = access_fLg_from_row(layout_lfg, row); // get function corresponding to the OM row | |
| 84 | ✗ | if (col < info.x_size) { | |
| 85 | ✗ | fn.jac.dx.push_back(JacobianSparsity{col, csc_buffer_entry_B}); | |
| 86 | } | ||
| 87 | ✗ | else if (col < info.xu_size) { | |
| 88 | ✗ | fn.jac.du.push_back(JacobianSparsity{col - info.x_size, csc_buffer_entry_B}); | |
| 89 | } | ||
| 90 | else { | ||
| 91 | ✗ | fn.jac.dp.push_back(JacobianSparsity{col - info.xu_size, csc_buffer_entry_B}); | |
| 92 | } | ||
| 93 | } | ||
| 94 | ✗ | } | |
| 95 | |||
| 96 | ✗ | void init_jac_mr(InfoGDOP& info, GDOP::BoundarySweepLayout& layout_mr) { | |
| 97 | /* M (first row) in C(COO) Jacobian */ | ||
| 98 | int nz_C = 0; | ||
| 99 | ✗ | if (info.mayer_exists) { | |
| 100 | ✗ | assert(layout_mr.M); | |
| 101 | |||
| 102 | ✗ | while (info.exc_jac->C.sparsity.row[nz_C] == 0) { | |
| 103 | ✗ | int col = info.exc_jac->C.sparsity.col[nz_C]; | |
| 104 | |||
| 105 | /* for now only final states: xf! no parameters, no dx0 */ | ||
| 106 | ✗ | if (col < info.x_size) { | |
| 107 | /* just point to nz_C, since for 1 row, CSC == COO */ | ||
| 108 | ✗ | layout_mr.M->jac.dxf.push_back(JacobianSparsity{col, nz_C}); | |
| 109 | } | ||
| 110 | ✗ | else if (col < info.xu_size) { | |
| 111 | /* just point to nz_C, since for 1 row, CSC == COO */ | ||
| 112 | ✗ | layout_mr.M->jac.duf.push_back(JacobianSparsity{col - info.x_size, nz_C}); | |
| 113 | } | ||
| 114 | |||
| 115 | ✗ | nz_C++; | |
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /* r in D Jacobian */ | ||
| 120 | ✗ | for (int nz_D = 0; nz_D < info.exc_jac->D.sparsity.nnz; nz_D++) { | |
| 121 | ✗ | int row = info.exc_jac->D.sparsity.row[nz_D]; | |
| 122 | ✗ | int col = info.exc_jac->D.sparsity.col[nz_D]; | |
| 123 | int csc_buffer_entry_D = info.exc_jac->D.sparsity.coo_to_csc(nz_D); // jac_buffer == OpenModelica D CSC buffer! | ||
| 124 | ✗ | auto& fn = layout_mr.r[row]; | |
| 125 | ✗ | if (col < info.x_size) { | |
| 126 | /* add the Mayer offset, since the values f64* is [M, r] */ | ||
| 127 | // Attention: this offset only works if D contains just r!! | ||
| 128 | ✗ | fn.jac.dxf.push_back(JacobianSparsity{col, info.exc_jac->D.sparsity.nnz_offset + csc_buffer_entry_D}); | |
| 129 | } | ||
| 130 | ✗ | else if (col < info.xu_size) { | |
| 131 | ✗ | fn.jac.duf.push_back(JacobianSparsity{col - info.x_size, info.exc_jac->D.sparsity.nnz_offset + csc_buffer_entry_D}); | |
| 132 | } | ||
| 133 | } | ||
| 134 | ✗ | } | |
| 135 | |||
| 136 | ✗ | void init_hes(InfoGDOP& info, GDOP::FullSweepLayout& layout_lfg, GDOP::BoundarySweepLayout& layout_mr) { | |
| 137 | ✗ | init_hes_lfg(info, layout_lfg); | |
| 138 | ✗ | init_hes_mr(info, layout_mr); | |
| 139 | ✗ | } | |
| 140 | |||
| 141 | ✗ | void init_hes_lfg(InfoGDOP& info, GDOP::FullSweepLayout& layout_lfg) { | |
| 142 | /* TODO: PARAMETERS include hes_lfg_pp and make it threaded (~numberThreads buffers with fancy pooling) */ | ||
| 143 | auto& hes = layout_lfg.hes; | ||
| 144 | |||
| 145 | ✗ | HESSIAN_PATTERN* hes_b = info.exc_hes->B.hessian; | |
| 146 | |||
| 147 | ✗ | for (int lnz = 0; lnz < hes_b->lnnz; lnz++) { | |
| 148 | ✗ | int row = hes_b->row[lnz]; | |
| 149 | ✗ | int col = hes_b->col[lnz]; | |
| 150 | |||
| 151 | ✗ | if (row < info.x_size && col < info.x_size) { | |
| 152 | ✗ | hes.dx_dx.push_back({row, col, lnz}); | |
| 153 | } | ||
| 154 | ✗ | else if (row >= info.x_size && col < info.x_size) { | |
| 155 | ✗ | hes.du_dx.push_back({row - info.x_size, col, lnz}); | |
| 156 | } | ||
| 157 | else { | ||
| 158 | ✗ | hes.du_du.push_back({row - info.x_size, col - info.x_size, lnz}); | |
| 159 | } | ||
| 160 | } | ||
| 161 | ✗ | } | |
| 162 | |||
| 163 | ✗ | void init_hes_mr(InfoGDOP& info, GDOP::BoundarySweepLayout& layout_mr) { | |
| 164 | auto& hes_mr = layout_mr.hes; | ||
| 165 | |||
| 166 | ✗ | HESSIAN_PATTERN* hes_c = info.exc_hes->C.hessian; | |
| 167 | ✗ | HESSIAN_PATTERN* hes_d = info.exc_hes->D.hessian; | |
| 168 | |||
| 169 | OrderedIndexSet hessian_mr; | ||
| 170 | std::vector<HessianSparsity> M_sparsities; | ||
| 171 | std::vector<int> M_cols; | ||
| 172 | ✗ | if (info.mayer_exists) { | |
| 173 | ✗ | assert(layout_mr.M); | |
| 174 | |||
| 175 | auto& mayer_term = *layout_mr.M; | ||
| 176 | |||
| 177 | // ignore x0 and p for now | ||
| 178 | ✗ | for (auto& M_dxf : mayer_term.jac.dxf) M_cols.push_back(M_dxf.col); | |
| 179 | ✗ | for (auto& M_duf : mayer_term.jac.duf) M_cols.push_back(info.x_size + M_duf.col); | |
| 180 | |||
| 181 | // estimate lower triangle sparsity pattern | ||
| 182 | ✗ | for (size_t i = 0; i < M_cols.size(); i++) { | |
| 183 | ✗ | for (size_t j = 0; j <= i; j++) { | |
| 184 | ✗ | M_sparsities.push_back({M_cols[i], M_cols[j], 0}); | |
| 185 | } | ||
| 186 | } | ||
| 187 | ✗ | hessian_mr.insert_sparsity(M_sparsities, 0, 0); | |
| 188 | } | ||
| 189 | |||
| 190 | ✗ | if (hes_d != nullptr) { | |
| 191 | ✗ | for (int nz = 0; nz < hes_d->lnnz; nz++) { | |
| 192 | ✗ | hessian_mr.set.insert({hes_d->row[nz], hes_d->col[nz]}); | |
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | // create sparsity pattern struct(H(M) + H(r)) | ||
| 197 | int lnz = 0; | ||
| 198 | std::map<std::pair<int, int>, int> sparsity_to_lnz; | ||
| 199 | ✗ | for (auto pair : hessian_mr.set) { | |
| 200 | ✗ | auto [row, col] = pair; | |
| 201 | ✗ | sparsity_to_lnz[pair] = lnz; | |
| 202 | |||
| 203 | ✗ | if (row < info.x_size && col < info.x_size) { | |
| 204 | ✗ | hes_mr.dxf_dxf.push_back({row, col, lnz}); | |
| 205 | } | ||
| 206 | ✗ | else if (row >= info.x_size && col < info.x_size) { | |
| 207 | ✗ | hes_mr.duf_dxf.push_back({row - info.x_size, col, lnz}); | |
| 208 | } | ||
| 209 | else { | ||
| 210 | ✗ | hes_mr.duf_duf.push_back({row - info.x_size, col - info.x_size, lnz}); | |
| 211 | } | ||
| 212 | ✗ | lnz++; | |
| 213 | } | ||
| 214 | |||
| 215 | int c_index = 0; | ||
| 216 | ✗ | info.exc_hes->C_to_Mr_buffer = FixedVector<std::pair<int, int>>(info.mayer_exists ? M_sparsities.size() : 0); | |
| 217 | |||
| 218 | ✗ | if (hes_c) { | |
| 219 | int hes_c_index = 0; | ||
| 220 | ✗ | for (const auto& mayer_hess : M_sparsities) { | |
| 221 | ✗ | while (hes_c_index < hes_c->lnnz && | |
| 222 | ✗ | (hes_c->row[hes_c_index] < mayer_hess.row || | |
| 223 | ✗ | (hes_c->row[hes_c_index] == mayer_hess.row && hes_c->col[hes_c_index] < mayer_hess.col))) { | |
| 224 | ✗ | hes_c_index++; | |
| 225 | } | ||
| 226 | ✗ | auto it = sparsity_to_lnz.find({mayer_hess.row, mayer_hess.col}); | |
| 227 | ✗ | if (it != sparsity_to_lnz.end()) { | |
| 228 | ✗ | info.exc_hes->C_to_Mr_buffer[c_index++] = {hes_c_index, it->second}; | |
| 229 | ✗ | hes_c_index++; | |
| 230 | } | ||
| 231 | else { | ||
| 232 | ✗ | Log::error("Hessian entry row = {}, col = {} from hes_d not found in pattern!", mayer_hess.row, mayer_hess.col); | |
| 233 | ✗ | abort(); | |
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | ✗ | info.exc_hes->D_to_Mr_buffer = FixedVector<std::pair<int, int>>(hes_d ? hes_d->lnnz : 0); | |
| 239 | ✗ | if (hes_d != nullptr) { | |
| 240 | ✗ | for (int i = 0; i < hes_d->lnnz; i++) { | |
| 241 | ✗ | int row = hes_d->row[i]; | |
| 242 | ✗ | int col = hes_d->col[i]; | |
| 243 | ✗ | auto it = sparsity_to_lnz.find({row, col}); | |
| 244 | ✗ | if (it != sparsity_to_lnz.end()) { | |
| 245 | info.exc_hes->D_to_Mr_buffer[i] = {i, it->second}; | ||
| 246 | } else { | ||
| 247 | ✗ | Log::error("Hessian entry row = {}, col = {} from hes_d not found in pattern!", row, col); | |
| 248 | ✗ | abort(); | |
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | ✗ | } | |
| 253 | |||
| 254 | /* TODO: PARAMETERS add me */ | ||
| 255 | ✗ | void set_parameters(InfoGDOP& info, const f64* p) { | |
| 256 | ✗ | return; | |
| 257 | } | ||
| 258 | |||
| 259 | ✗ | void set_states(InfoGDOP& info, const f64* x_ij) { | |
| 260 | ✗ | std::memcpy( | |
| 261 | ✗ | info.data->localData[0]->realVars + info.index_x_real_vars, | |
| 262 | x_ij, | ||
| 263 | ✗ | info.x_size * sizeof(f64) | |
| 264 | ); | ||
| 265 | ✗ | } | |
| 266 | |||
| 267 | ✗ | void set_inputs(InfoGDOP& info, const f64* u_ij) { | |
| 268 | ✗ | for (int u = 0; u < info.u_size; u++) { | |
| 269 | ✗ | info.data->localData[0]->realVars[info.u_indices_real_vars[u]] = u_ij[u]; | |
| 270 | } | ||
| 271 | ✗ | } | |
| 272 | |||
| 273 | ✗ | void set_states_inputs(InfoGDOP& info, const f64* xu_ij) { | |
| 274 | ✗ | set_states(info, xu_ij); | |
| 275 | ✗ | set_inputs(info, xu_ij + info.x_size); | |
| 276 | ✗ | } | |
| 277 | |||
| 278 | ✗ | void set_time(InfoGDOP& info, const f64 t_ij) { | |
| 279 | /* move time horizon to Modelica model time */ | ||
| 280 | ✗ | info.data->localData[0]->timeValue = t_ij; | |
| 281 | ✗ | } | |
| 282 | |||
| 283 | ✗ | void eval_ode_write(InfoGDOP& info, f64* eval_ode_buffer) { | |
| 284 | /* f */ | ||
| 285 | ✗ | for (int der_x = 0; der_x < info.f_size; der_x++) { | |
| 286 | ✗ | eval_ode_buffer[der_x] = info.data->localData[0]->realVars[info.index_der_x_real_vars + der_x]; | |
| 287 | } | ||
| 288 | ✗ | } | |
| 289 | |||
| 290 | ✗ | void eval_lfg_write(InfoGDOP& info, f64* eval_lfg_buffer) { | |
| 291 | int nz = 0; | ||
| 292 | /* L */ | ||
| 293 | ✗ | if (info.lagrange_exists) { | |
| 294 | ✗ | eval_lfg_buffer[nz++] = info.data->localData[0]->realVars[info.index_lagrange_real_vars]; | |
| 295 | } | ||
| 296 | /* f */ | ||
| 297 | ✗ | for (int der_x = 0; der_x < info.f_size; der_x++) { | |
| 298 | ✗ | eval_lfg_buffer[nz++] = info.data->localData[0]->realVars[info.index_der_x_real_vars + der_x]; | |
| 299 | } | ||
| 300 | /* g */ | ||
| 301 | ✗ | for (int g = 0; g < info.g_size; g++) { | |
| 302 | ✗ | eval_lfg_buffer[nz++] = info.data->localData[0]->realVars[info.index_g_real_vars + g]; | |
| 303 | } | ||
| 304 | ✗ | } | |
| 305 | |||
| 306 | ✗ | void eval_mr_write(InfoGDOP& info, f64* eval_mr_buffer) { | |
| 307 | int nz = 0; | ||
| 308 | /* M */ | ||
| 309 | ✗ | if (info.mayer_exists) { | |
| 310 | ✗ | eval_mr_buffer[nz++] = info.data->localData[0]->realVars[info.index_mayer_real_vars]; | |
| 311 | } | ||
| 312 | /* r */ | ||
| 313 | ✗ | for (int r = 0; r < info.r_size; r++) { | |
| 314 | ✗ | eval_mr_buffer[nz++] = info.data->localData[0]->realVars[info.index_r_real_vars + r]; | |
| 315 | } | ||
| 316 | ✗ | } | |
| 317 | |||
| 318 | ✗ | void jac_eval_write_first_row_as_csc(InfoGDOP& info, JACOBIAN* jacobian, f64* full_buffer, | |
| 319 | f64* eval_jac_buffer, CscToCoo& exc) { | ||
| 320 | ✗ | assert(jacobian && jacobian->sparsePattern); | |
| 321 | ✗ | evalJacobian(info.data, info.threadData, jacobian, NULL, full_buffer, FALSE); | |
| 322 | |||
| 323 | ✗ | for (int nz = 0; nz < exc.nnz_moved_row; nz++) { | |
| 324 | ✗ | eval_jac_buffer[nz] = full_buffer[exc.coo_to_csc(nz)]; | |
| 325 | } | ||
| 326 | ✗ | } | |
| 327 | |||
| 328 | } // namespace OpenModelica | ||
| 329 |