OMCompiler/SimulationRuntime/c/simulation/solver/gbode_internal_nls.c
| 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 <klu.h> | ||
| 29 | |||
| 30 | #include "gbode_main.h" | ||
| 31 | #include "gbode_util.h" | ||
| 32 | #include "gbode_sparse.h" | ||
| 33 | #include "gbode_internal_nls.h" | ||
| 34 | |||
| 35 | #include "../options.h" | ||
| 36 | #include "../arrayIndex.h" | ||
| 37 | |||
| 38 | // TODO: Calibrate safety factor for internal tolerances | ||
| 39 | |||
| 40 | #define DBL_ABSORPTION (10 * DBL_EPSILON) | ||
| 41 | #define MAX(a,b) (((a)>(b))?(a):(b)) | ||
| 42 | |||
| 43 | /* some constants for less verbose BLAS calls */ | ||
| 44 | static const double DBL_ZERO = 0.0; | ||
| 45 | static const double DBL_ONE = 1.0; | ||
| 46 | static const double DBL_MINUS_ONE = -1.0; | ||
| 47 | static const int INT_ONE = 1; | ||
| 48 | static const int INT_TWO = 2; | ||
| 49 | static const char CHAR_TRANS = 'T'; | ||
| 50 | static const char CHAR_NO_TRANS = 'N'; | ||
| 51 | |||
| 52 | /* y := a * x + y */ | ||
| 53 | extern void daxpy_(const int *n, | ||
| 54 | const double *alpha, | ||
| 55 | const double *x, const int *incX, | ||
| 56 | double *y, const int *incY); | ||
| 57 | |||
| 58 | /* y := alpha * A * x + beta * y */ | ||
| 59 | extern void dgemv_(const char *trans, | ||
| 60 | const int *m, | ||
| 61 | const int *n, | ||
| 62 | const double *alpha, const double *A, const int *ldA, | ||
| 63 | const double *x, const int *incX, | ||
| 64 | const double *beta, double *y, const int *incY | ||
| 65 | ); | ||
| 66 | |||
| 67 | /* C := alpha * A * B + beta * C */ | ||
| 68 | extern void dgemm_(const char *transA, | ||
| 69 | const char *transB, | ||
| 70 | const int *m, | ||
| 71 | const int *n, | ||
| 72 | const int *k, | ||
| 73 | const double *alpha, const double *A, const int *ldA, | ||
| 74 | const double *B, const int *ldB, | ||
| 75 | const double *beta, double *C, const int *ldC | ||
| 76 | ); | ||
| 77 | |||
| 78 | /* x := alpha * x */ | ||
| 79 | extern void dscal_(const int *n, | ||
| 80 | const double *alpha, | ||
| 81 | double *x, const int *incX | ||
| 82 | ); | ||
| 83 | |||
| 84 | /* y[incY * i] := x[incX * i] for i = 0 ... n - 1 */ | ||
| 85 | extern void dcopy_(const int *n, | ||
| 86 | const double *x, const int *incX, | ||
| 87 | double *y, const int *incY | ||
| 88 | ); | ||
| 89 | |||
| 90 | typedef struct KLUInternals | ||
| 91 | { | ||
| 92 | klu_common common; // shared KLU configuration for all transformed systems | ||
| 93 | klu_symbolic *symbolic; // shared symbolic factorization of the NLS sparse pattern | ||
| 94 | klu_numeric **num_real; // real numerical factorizations | ||
| 95 | klu_numeric **num_cmplx; // complex numerical factorizations | ||
| 96 | int n_real; | ||
| 97 | int n_cmplx; | ||
| 98 | } KLUInternals; | ||
| 99 | |||
| 100 | typedef struct GB_INTERNAL_NLS_DATA | ||
| 101 | { | ||
| 102 | NLS_USERDATA *nls_user_data; // pointer to data, gbode data, etc. | ||
| 103 | KLUInternals klu; // KLU data | ||
| 104 | double *jacobian_callback; // buffer for continuous ODE Jacobian (size = nnz(J_f)) | ||
| 105 | int *ode_to_nls; // mapping ODE Jacobian nnz -> NLS Jacobian nnz | ||
| 106 | int *nls_diag_indices; // all diagonal nz indices of NLS Jacobian (size = cols) | ||
| 107 | double *scal; // scaling vector for termination of Newton loop | ||
| 108 | double *etas; // Newton contraction factors for each NLS stage (size == number of stages) | ||
| 109 | double eta_inital_damping; // Initial damping factor eta_new = eta_old^eta_initial_damping | ||
| 110 | double integrator_tol; // Integrator / user provided tolerance | ||
| 111 | double fnewt; // Newton tolerance: if eta * norm(dx) <= fnewt -> convergence | ||
| 112 | double theta_keep; // if norm(dx_k) / norm(dx_{k-1}) = theta_{k} < theta_keep -> keep old jacobian_callback | ||
| 113 | modelica_boolean call_jac; // call jacobian in the next call to NLS solve | ||
| 114 | double theta_divergence; // if norm(dx_k) / norm(dx_{k-1}) = theta_{k} > theta_divergence (<= 1.0) -> divergence of Newton | ||
| 115 | int max_newton_it; // maximum number of Newton iterations | ||
| 116 | int size; // size of the system | ||
| 117 | BUTCHER_TABLEAU *tabl; // butcher tableau of the method | ||
| 118 | modelica_boolean use_t_transform; // use T transform to solve the system (false for (E)SDIRK, true for FIRK) | ||
| 119 | double **real_nls_jacs; // real NLS jacobians | ||
| 120 | double **cmplx_nls_jacs; // complex NLS jacobians (packed as real, imag - memory layout is struct{double real, double imag}[]) | ||
| 121 | double **real_nls_res; // real NLS residuum | ||
| 122 | double **cmplx_nls_res; // complex NLS residuum (packed as real, imag - memory layout is struct{double real, double imag}[]) | ||
| 123 | double *Z; // update variables Z = Y(t_ij) - Y0 for T-transformation (coupled space) | ||
| 124 | double *W; // update variables W = (T^{-1} otimes I) Z for T-transformation (decoupled space) | ||
| 125 | double *work; // some work memory for the T transformation (size: transform->size * x.size) or other stuff, at least 32 * N_STATES bytes | ||
| 126 | |||
| 127 | // stuff for multirate | ||
| 128 | modelica_boolean multirate; // multirate or singlerate system? | ||
| 129 | modelica_boolean new_fast_states; // if the selection changed and we need to update sparse pattern and symbolic factorization - set from NLS routine | ||
| 130 | } GB_INTERNAL_NLS_DATA; | ||
| 131 | |||
| 132 | ✗ | static inline SPARSE_PATTERN *getODEPattern(DATA *data, DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls) | |
| 133 | { | ||
| 134 | ✗ | return nls->multirate ? gbData->gbfData->sparsePattern_ODE : getJacobianCscPattern(getSymbolicOdeJacobian(data)); | |
| 135 | } | ||
| 136 | |||
| 137 | static inline SPARSE_PATTERN *getNLSPattern(DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls) | ||
| 138 | { | ||
| 139 | ✗ | return nls->multirate ? gbData->gbfData->sparsePattern_NLS : gbData->sparsePattern_NLS; | |
| 140 | } | ||
| 141 | |||
| 142 | ✗ | static void gbInternal_evalJacobianMR(DATA* data, | |
| 143 | threadData_t *threadData, | ||
| 144 | DATA_GBODE *gbData, | ||
| 145 | JACOBIAN* fullJac, | ||
| 146 | GB_INTERNAL_NLS_DATA *nls, | ||
| 147 | double* smallJac) | ||
| 148 | { | ||
| 149 | ✗ | const SPARSE_PATTERN* smallSp = gbData->gbfData->sparsePattern_ODE; | |
| 150 | |||
| 151 | ✗ | int* fast_idx = gbData->fastStatesIdx; | |
| 152 | ✗ | unsigned int size_fast = gbData->nFastStates; | |
| 153 | |||
| 154 | ✗ | fullJac->evalSelection = NULL; | |
| 155 | ✗ | memset(fullJac->seedVars, 0, fullJac->sizeCols * sizeof(modelica_real)); | |
| 156 | |||
| 157 | int color, col, nz; | ||
| 158 | |||
| 159 | ✗ | for (color = 0; color < smallSp->maxColors; color++) | |
| 160 | { | ||
| 161 | ✗ | for (col = 0; col < size_fast; col++) | |
| 162 | { | ||
| 163 | ✗ | unsigned int big_col = fast_idx[col]; | |
| 164 | |||
| 165 | ✗ | if (smallSp->colorCols[col] - 1 == color) | |
| 166 | { | ||
| 167 | ✗ | fullJac->seedVars[big_col] = 1.0; | |
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | ✗ | fullJac->evalColumn(data, threadData, fullJac, NULL); | |
| 172 | |||
| 173 | ✗ | for (col = 0; col < size_fast; col++) | |
| 174 | { | ||
| 175 | ✗ | unsigned int big_col = fast_idx[col]; | |
| 176 | |||
| 177 | ✗ | if (smallSp->colorCols[col] - 1 == color) | |
| 178 | { | ||
| 179 | ✗ | for (nz = smallSp->leadindex[col]; nz < smallSp->leadindex[col + 1]; nz++) | |
| 180 | { | ||
| 181 | ✗ | unsigned int small_row = smallSp->index[nz]; | |
| 182 | ✗ | unsigned int full_row = fast_idx[small_row]; | |
| 183 | |||
| 184 | ✗ | smallJac[nz] = fullJac->resultVars[full_row]; | |
| 185 | } | ||
| 186 | |||
| 187 | ✗ | fullJac->seedVars[big_col] = 0.0; | |
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | ✗ | fullJac->evalSelection = NULL; | |
| 193 | ✗ | } | |
| 194 | |||
| 195 | ✗ | static void gbInternal_evalNumericalJacobian(DATA *data, | |
| 196 | threadData_t *threadData, | ||
| 197 | DATA_GBODE *gbData, | ||
| 198 | GB_INTERNAL_NLS_DATA *nls) | ||
| 199 | { | ||
| 200 | ✗ | const double delta_h = numericalDifferentiationDeltaXsolver; | |
| 201 | |||
| 202 | ✗ | const SPARSE_PATTERN *sparsity = getODEPattern(data, gbData, nls); | |
| 203 | EVAL_SELECTION *selection = NULL; | ||
| 204 | int *state_map = NULL; | ||
| 205 | ✗ | int size = gbData->nStates; | |
| 206 | int full_size = gbData->nStates; | ||
| 207 | |||
| 208 | ✗ | if (nls->multirate) | |
| 209 | { | ||
| 210 | ✗ | state_map = gbData->fastStatesIdx; | |
| 211 | ✗ | size = gbData->nFastStates; | |
| 212 | ✗ | selection = gbData->gbfData->evalSelectionFast; | |
| 213 | } | ||
| 214 | |||
| 215 | ✗ | double *x = data->localData[0]->realVars; | |
| 216 | ✗ | double *der_x = &data->localData[0]->realVars[full_size]; | |
| 217 | |||
| 218 | // work 0 ... full_size - 1 is used already by the backup data | ||
| 219 | ✗ | double *der_x_ref = &nls->work[full_size]; | |
| 220 | ✗ | double *x_save = &nls->work[2 * full_size]; | |
| 221 | ✗ | double *delta_hh = &nls->work[3 * full_size]; | |
| 222 | |||
| 223 | ✗ | const double *nominals = gbData->nominals; | |
| 224 | ✗ | const double *maxs = gbData->maxs; | |
| 225 | |||
| 226 | memcpy(der_x_ref, der_x, full_size * sizeof(double)); | ||
| 227 | |||
| 228 | ✗ | for (unsigned int color = 0; color < sparsity->maxColors; color++) | |
| 229 | { | ||
| 230 | // careful perturbation of the variables (a la DASSL interface) | ||
| 231 | ✗ | for (unsigned int col = 0; col < size; col++) | |
| 232 | { | ||
| 233 | ✗ | unsigned int big_col = state_map ? state_map[col] : col; | |
| 234 | |||
| 235 | ✗ | if (sparsity->colorCols[col] - 1 == color) | |
| 236 | { | ||
| 237 | // we follow the procedure of the DASSL interface for the selection of perturbation h_i | ||
| 238 | |||
| 239 | // h * f(x)_i | ||
| 240 | ✗ | double delta_hhh = delta_h * der_x_ref[big_col]; | |
| 241 | |||
| 242 | // scal_raw = ATOL * NOMINAL + RTOL * abs(x_i), we use the real (un-transformed) integrator tolerances though | ||
| 243 | ✗ | double raw_weight = nls->integrator_tol * nominals[big_col] + nls->integrator_tol * fabs(x[big_col]); | |
| 244 | |||
| 245 | // choose h_i := h * max(abs(x_i), h * f(x)_i, ATOL * NOMINAL + RTOL * abs(x_i), 1e-3) | ||
| 246 | ✗ | delta_hh[big_col] = delta_h * fmax(fmax(fmax(fabs(x[big_col]), 1e-3), fabs(delta_hhh)), fabs(raw_weight)); | |
| 247 | ✗ | delta_hh[big_col] = x[big_col] + delta_hh[big_col] - x[big_col]; | |
| 248 | |||
| 249 | ✗ | if (x[big_col] + delta_hh[big_col] >= maxs[big_col]) | |
| 250 | { | ||
| 251 | ✗ | delta_hh[big_col] *= -1; | |
| 252 | } | ||
| 253 | |||
| 254 | ✗ | x_save[big_col] = x[big_col]; | |
| 255 | ✗ | x[big_col] += delta_hh[big_col]; | |
| 256 | ✗ | delta_hh[big_col] = 1.0 / delta_hh[big_col]; | |
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | // eval f(x + h) | ||
| 261 | ✗ | gbode_fODE(data, threadData, NULL, selection); | |
| 262 | |||
| 263 | // do forward finite differencing (f(x + h) - f(x)) / h and reset states | ||
| 264 | ✗ | for (unsigned int col = 0; col < size; col++) | |
| 265 | { | ||
| 266 | ✗ | unsigned int big_col = state_map ? state_map[col] : col; | |
| 267 | |||
| 268 | ✗ | if (sparsity->colorCols[col] - 1 == color) | |
| 269 | { | ||
| 270 | ✗ | for (unsigned int nz = sparsity->leadindex[col]; nz < sparsity->leadindex[col + 1]; nz++) | |
| 271 | { | ||
| 272 | ✗ | unsigned int small_row = sparsity->index[nz]; | |
| 273 | ✗ | unsigned int big_row = state_map ? state_map[small_row] : small_row; | |
| 274 | |||
| 275 | ✗ | nls->jacobian_callback[nz] = (der_x[big_row] - der_x_ref[big_row]) * delta_hh[big_col]; | |
| 276 | } | ||
| 277 | |||
| 278 | ✗ | x[big_col] = x_save[big_col]; | |
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 282 | ✗ | } | |
| 283 | |||
| 284 | /** | ||
| 285 | * @brief Calculate ODE Jacobian (numerical or analytic) depending on availability of | ||
| 286 | * analytic Jacobian. | ||
| 287 | * | ||
| 288 | * Fills the nls->jacobian_callback callback buffer with the ODE Jacobian. | ||
| 289 | * It requires the sparsity pattern and a work array of size 3 * #states in nls. | ||
| 290 | * This is just a wrapper around evalJacobian to also support numerical ODE Jacobians. | ||
| 291 | * | ||
| 292 | * @param data DATA object | ||
| 293 | * @param threadData Thread data | ||
| 294 | * @param gbData GBODE solver data | ||
| 295 | * @param nls Internal strategy data (nls->jacobian_callback will be filled) | ||
| 296 | */ | ||
| 297 | ✗ | static int gbInternal_evalJacobian(DATA *data, threadData_t *threadData, DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls) | |
| 298 | { | ||
| 299 | int ret = -1; | ||
| 300 | |||
| 301 | /* try */ | ||
| 302 | #if !defined(OMC_EMCC) | ||
| 303 | ✗ | OMC_TRY_INTERNAL(simulationJumpBuffer) | |
| 304 | #endif | ||
| 305 | |||
| 306 | ✗ | rt_tick(SIM_TIMER_JACOBIAN); | |
| 307 | /* The multi-rate path drives a sub-set of the columns with its own coloring, so it | ||
| 308 | * always needs the forward Jacobian. All other paths use the Jacobian selected by | ||
| 309 | * the `-jacobian` flag, which may be evaluated forward, adjoint or bidirectionally. | ||
| 310 | * Its currently disabled so its fine but its the safe option */ | ||
| 311 | ✗ | JACOBIAN* jacobian_ODE = nls->multirate | |
| 312 | ✗ | ? &(data->simulationInfo->analyticJacobians[data->callback->INDEX_JAC_A]) | |
| 313 | ✗ | : getSymbolicOdeJacobian(data); | |
| 314 | |||
| 315 | ✗ | if (nls->multirate && jacobian_ODE->availability == JACOBIAN_AVAILABLE) | |
| 316 | { | ||
| 317 | ✗ | gbInternal_evalJacobianMR(data, threadData, gbData, jacobian_ODE, nls, nls->jacobian_callback); | |
| 318 | } | ||
| 319 | ✗ | else if (!nls->multirate && jacobian_ODE->availability == JACOBIAN_AVAILABLE) | |
| 320 | { | ||
| 321 | ✗ | evalJacobian(data, threadData, jacobian_ODE, NULL, nls->jacobian_callback, FALSE); | |
| 322 | } | ||
| 323 | else | ||
| 324 | { | ||
| 325 | ✗ | gbInternal_evalNumericalJacobian(data, threadData, gbData, nls); | |
| 326 | } | ||
| 327 | |||
| 328 | ✗ | if (OMC_ERROR_RAISED()) { OMC_ERROR_CLEAR(); } else { ret = 0; } | |
| 329 | |||
| 330 | #if !defined(OMC_EMCC) | ||
| 331 | ✗ | OMC_CATCH_INTERNAL(simulationJumpBuffer) | |
| 332 | #endif | ||
| 333 | |||
| 334 | ✗ | if (nls->multirate) | |
| 335 | { | ||
| 336 | ✗ | gbData->gbfData->stats.nCallsJacobian++; | |
| 337 | } | ||
| 338 | else | ||
| 339 | { | ||
| 340 | ✗ | gbData->stats.nCallsJacobian++; | |
| 341 | } | ||
| 342 | |||
| 343 | ✗ | rt_accumulate(SIM_TIMER_JACOBIAN); | |
| 344 | |||
| 345 | ✗ | return ret; | |
| 346 | } | ||
| 347 | |||
| 348 | /** | ||
| 349 | * @brief Assemble (E)SDIRK stage Jacobian for the nonlinear system. | ||
| 350 | * | ||
| 351 | * Scales the ODE Jacobian by `h * gamma`, maps it into the NLS Jacobian buffer, | ||
| 352 | * and subtracts the identity on the diagonal: J = -I + h*a_ii * dfdx. | ||
| 353 | * | ||
| 354 | * @param[in] data Runtime data (unused) | ||
| 355 | * @param[in] threadData Thread data | ||
| 356 | * @param[in] gbData GBODE integrator data | ||
| 357 | * @param[in] nls Internal NLS data with index mapping | ||
| 358 | * @param[in] jac_ode ODE Jacobian (structures) | ||
| 359 | * @param[in] jac_buf_ode ODE Jacobian values (already filled) | ||
| 360 | * @param[out] jac_buf_nls Output buffer for NLS Jacobian | ||
| 361 | * @return 0 on success | ||
| 362 | */ | ||
| 363 | ✗ | static int jacobian_DIRK_assemble(DATA *data, | |
| 364 | threadData_t *threadData, | ||
| 365 | DATA_GBODE* gbData, | ||
| 366 | GB_INTERNAL_NLS_DATA *nls, | ||
| 367 | SPARSE_PATTERN *ode_jac_sp, | ||
| 368 | double *jac_buf_ode, | ||
| 369 | double *jac_buf_nls) | ||
| 370 | { | ||
| 371 | ✗ | memset(jac_buf_nls, 0, getNLSPattern(gbData, nls)->nnz * sizeof(double)); | |
| 372 | |||
| 373 | ✗ | DATA_GBODEF *gbfData = gbData->gbfData; | |
| 374 | |||
| 375 | ✗ | const double fac = (nls->multirate ? gbfData->stepSize * gbfData->tableau->A[gbfData->act_stage * gbfData->tableau->nStages + gbfData->act_stage] | |
| 376 | ✗ | : gbData->stepSize * gbData->tableau->A[gbData->act_stage * gbData->tableau->nStages + gbData->act_stage]); | |
| 377 | |||
| 378 | ✗ | for (int nz = 0; nz < ode_jac_sp->nnz; nz++) | |
| 379 | { | ||
| 380 | ✗ | int idx = nls->ode_to_nls[nz]; | |
| 381 | ✗ | jac_buf_nls[idx] = fac * jac_buf_ode[nz]; | |
| 382 | } | ||
| 383 | |||
| 384 | ✗ | for (int d = 0; d < nls->size; d++) | |
| 385 | { | ||
| 386 | ✗ | jac_buf_nls[nls->nls_diag_indices[d]] -= 1.0; | |
| 387 | } | ||
| 388 | |||
| 389 | ✗ | return 0; | |
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * @brief Assemble Jacobian for the nonlinear system. | ||
| 394 | * | ||
| 395 | * Jacobian has form gamma / h * I - J_f | ||
| 396 | * | ||
| 397 | * @param[in] data Runtime data (unused) | ||
| 398 | * @param[in] threadData Thread data | ||
| 399 | * @param[in] gbData GBODE integrator data | ||
| 400 | * @param[in] nls Internal NLS data with index mapping | ||
| 401 | * @param[in] jac_ode ODE Jacobian (structures) | ||
| 402 | * @param[in] jac_buf_ode ODE Jacobian values | ||
| 403 | * @param[out] jac_buf_nls Output buffer for NLS Jacobian | ||
| 404 | * @return 0 on success | ||
| 405 | */ | ||
| 406 | ✗ | static int jacobian_real_assemble(DATA *data, | |
| 407 | threadData_t *threadData, | ||
| 408 | DATA_GBODE* gbData, | ||
| 409 | GB_INTERNAL_NLS_DATA *nls, | ||
| 410 | double gamma, | ||
| 411 | SPARSE_PATTERN *ode_jac_sp, | ||
| 412 | double *jac_buf_ode, | ||
| 413 | double *jac_buf_nls) | ||
| 414 | { | ||
| 415 | ✗ | memset(jac_buf_nls, 0, getNLSPattern(gbData, nls)->nnz * sizeof(double)); | |
| 416 | |||
| 417 | ✗ | const double inv_step = 1.0 / (nls->multirate ? gbData->gbfData->stepSize : gbData->stepSize); | |
| 418 | ✗ | const double weight = inv_step * gamma; | |
| 419 | |||
| 420 | ✗ | for (int nz = 0; nz < ode_jac_sp->nnz; nz++) | |
| 421 | { | ||
| 422 | ✗ | int idx = nls->ode_to_nls[nz]; | |
| 423 | ✗ | jac_buf_nls[idx] = -jac_buf_ode[nz]; | |
| 424 | } | ||
| 425 | |||
| 426 | ✗ | for (int d = 0; d < nls->size; d++) | |
| 427 | { | ||
| 428 | ✗ | jac_buf_nls[nls->nls_diag_indices[d]] += weight; | |
| 429 | } | ||
| 430 | |||
| 431 | ✗ | return 0; | |
| 432 | } | ||
| 433 | |||
| 434 | /** | ||
| 435 | * @brief Assemble complex Jacobian for the nonlinear system. | ||
| 436 | * | ||
| 437 | * Jacobian has form (alpha + i * beta) / h * I - J_f | ||
| 438 | * | ||
| 439 | * @param[in] data Runtime data (unused) | ||
| 440 | * @param[in] threadData Thread data | ||
| 441 | * @param[in] gbData GBODE integrator data (step size, tableau, stage) | ||
| 442 | * @param[in] nls Internal NLS data with index mapping | ||
| 443 | * @param[in] alpha Real part of complex weight | ||
| 444 | * @param[in] beta Imaginary part of complex weight | ||
| 445 | * @param[in] jac_ode ODE Jacobian (structures) | ||
| 446 | * @param[in] jac_buf_ode ODE Jacobian values | ||
| 447 | * @param[out] jac_buf_nls Output buffer for NLS Jacobian | ||
| 448 | * @return 0 on success | ||
| 449 | */ | ||
| 450 | ✗ | static int jacobian_cmplx_assemble(DATA *data, | |
| 451 | threadData_t *threadData, | ||
| 452 | DATA_GBODE* gbData, | ||
| 453 | GB_INTERNAL_NLS_DATA *nls, | ||
| 454 | double alpha, | ||
| 455 | double beta, | ||
| 456 | SPARSE_PATTERN *ode_jac_sp, | ||
| 457 | double *jac_buf_ode, | ||
| 458 | double *jac_buf_nls) | ||
| 459 | { | ||
| 460 | ✗ | memset(jac_buf_nls, 0, 2 * getNLSPattern(gbData, nls)->nnz * sizeof(double)); | |
| 461 | |||
| 462 | ✗ | const double inv_step = 1.0 / (nls->multirate ? gbData->gbfData->stepSize : gbData->stepSize); | |
| 463 | ✗ | const double weight_real = inv_step * alpha; | |
| 464 | ✗ | const double weight_imag = inv_step * beta; | |
| 465 | |||
| 466 | ✗ | for (int nz = 0; nz < ode_jac_sp->nnz; nz++) | |
| 467 | { | ||
| 468 | ✗ | int idx = nls->ode_to_nls[nz]; | |
| 469 | ✗ | jac_buf_nls[2 * idx] = -jac_buf_ode[nz]; | |
| 470 | } | ||
| 471 | |||
| 472 | ✗ | for (int d = 0; d < nls->size; d++) | |
| 473 | { | ||
| 474 | ✗ | jac_buf_nls[2 * nls->nls_diag_indices[d]] += weight_real; | |
| 475 | ✗ | jac_buf_nls[2 * nls->nls_diag_indices[d] + 1] += weight_imag; | |
| 476 | } | ||
| 477 | |||
| 478 | ✗ | return 0; | |
| 479 | } | ||
| 480 | |||
| 481 | ✗ | static void gbInternal_KLU_initialize(KLUInternals *klu, int n_real, int n_cmplx) | |
| 482 | { | ||
| 483 | ✗ | assertStreamPrint(NULL, n_real >= 0 && n_cmplx >= 0, "Invalid number of KLU systems: %d real and %d complex.", n_real, n_cmplx); | |
| 484 | ✗ | klu_defaults(&klu->common); | |
| 485 | ✗ | klu->symbolic = NULL; | |
| 486 | ✗ | klu->n_real = n_real; | |
| 487 | ✗ | klu->n_cmplx = n_cmplx; | |
| 488 | ✗ | klu->num_real = n_real ? (klu_numeric **) calloc(n_real, sizeof(klu_numeric *)) : NULL; | |
| 489 | ✗ | klu->num_cmplx = n_cmplx ? (klu_numeric **) calloc(n_cmplx, sizeof(klu_numeric *)) : NULL; | |
| 490 | ✗ | } | |
| 491 | |||
| 492 | ✗ | static void gbInternal_KLU_freeNumerics(KLUInternals *klu) | |
| 493 | { | ||
| 494 | ✗ | for (int sys = 0; sys < klu->n_real; sys++) | |
| 495 | { | ||
| 496 | ✗ | if (klu->num_real[sys]) | |
| 497 | { | ||
| 498 | ✗ | klu_free_numeric(&klu->num_real[sys], &klu->common); | |
| 499 | } | ||
| 500 | } | ||
| 501 | ✗ | for (int sys = 0; sys < klu->n_cmplx; sys++) | |
| 502 | { | ||
| 503 | ✗ | if (klu->num_cmplx[sys]) | |
| 504 | { | ||
| 505 | ✗ | klu_free_numeric(&klu->num_cmplx[sys], &klu->common); | |
| 506 | } | ||
| 507 | } | ||
| 508 | ✗ | } | |
| 509 | |||
| 510 | /** @brief Run one shared symbolic analysis for all KLU systems. */ | ||
| 511 | ✗ | static int gbInternal_KLU_analyze(KLUInternals *klu, int size, int *Ap, int *Ai) | |
| 512 | { | ||
| 513 | ✗ | klu_defaults(&klu->common); | |
| 514 | ✗ | klu->symbolic = klu_analyze(size, Ap, Ai, &klu->common); | |
| 515 | ✗ | if (klu->common.status < 0) | |
| 516 | { | ||
| 517 | ✗ | throwStreamPrint(NULL, "Error in gbInternal_KLU_analyze. Symbolic analysis with KLU failed."); | |
| 518 | } | ||
| 519 | ✗ | return klu->common.status; | |
| 520 | } | ||
| 521 | |||
| 522 | ✗ | static int gbInternal_KLU_reanalyze(KLUInternals *klu, int size, int *Ap, int *Ai) | |
| 523 | { | ||
| 524 | ✗ | gbInternal_KLU_freeNumerics(klu); | |
| 525 | ✗ | if (klu->symbolic) | |
| 526 | { | ||
| 527 | ✗ | klu_free_symbolic(&klu->symbolic, &klu->common); | |
| 528 | } | ||
| 529 | ✗ | return gbInternal_KLU_analyze(klu, size, Ap, Ai); | |
| 530 | } | ||
| 531 | |||
| 532 | ✗ | static void gbInternal_KLU_free(KLUInternals *klu) | |
| 533 | { | ||
| 534 | ✗ | gbInternal_KLU_freeNumerics(klu); | |
| 535 | ✗ | if (klu->symbolic) | |
| 536 | { | ||
| 537 | ✗ | klu_free_symbolic(&klu->symbolic, &klu->common); | |
| 538 | } | ||
| 539 | ✗ | free(klu->num_real); | |
| 540 | ✗ | free(klu->num_cmplx); | |
| 541 | ✗ | } | |
| 542 | |||
| 543 | /** @brief Perform or update real-valued KLU numeric factorization. */ | ||
| 544 | ✗ | static int gbInternal_dKLU_factorize(KLUInternals *klu, int system, int *Ap, int *Ai, double *values) | |
| 545 | { | ||
| 546 | ✗ | assertStreamPrint(NULL, system >= 0 && system < klu->n_real, "Invalid real KLU system index %d.", system); | |
| 547 | ✗ | klu_numeric **numeric = &klu->num_real[system]; | |
| 548 | ✗ | if (*numeric) | |
| 549 | { | ||
| 550 | ✗ | klu_refactor(Ap, Ai, values, klu->symbolic, *numeric, &klu->common); | |
| 551 | } | ||
| 552 | else | ||
| 553 | { | ||
| 554 | ✗ | *numeric = klu_factor(Ap, Ai, values, klu->symbolic, &klu->common); | |
| 555 | } | ||
| 556 | ✗ | return klu->common.status; | |
| 557 | } | ||
| 558 | |||
| 559 | /** @brief Solve a real linear system using KLU. */ | ||
| 560 | ✗ | static int gbInternal_dKLU_solve(KLUInternals *klu, int system, int size, double *rhs) | |
| 561 | { | ||
| 562 | ✗ | assertStreamPrint(NULL, system >= 0 && system < klu->n_real, "Invalid real KLU system index %d.", system); | |
| 563 | ✗ | return klu_solve(klu->symbolic, klu->num_real[system], size, 1, rhs, &klu->common); | |
| 564 | } | ||
| 565 | |||
| 566 | /** @brief Perform or update complex-valued KLU numeric factorization (values packed as struct {double real, double imag}). */ | ||
| 567 | ✗ | static int gbInternal_zKLU_factorize(KLUInternals *klu, int system, int *Ap, int *Ai, double *values) | |
| 568 | { | ||
| 569 | ✗ | assertStreamPrint(NULL, system >= 0 && system < klu->n_cmplx, "Invalid complex KLU system index %d.", system); | |
| 570 | ✗ | klu_numeric **numeric = &klu->num_cmplx[system]; | |
| 571 | ✗ | if (*numeric) | |
| 572 | { | ||
| 573 | ✗ | klu_z_refactor(Ap, Ai, values, klu->symbolic, *numeric, &klu->common); | |
| 574 | } | ||
| 575 | else | ||
| 576 | { | ||
| 577 | ✗ | *numeric = klu_z_factor(Ap, Ai, values, klu->symbolic, &klu->common); | |
| 578 | } | ||
| 579 | ✗ | return klu->common.status; | |
| 580 | } | ||
| 581 | |||
| 582 | /** @brief Solve a complex linear system using KLU (values packed as struct {double real, double imag}). */ | ||
| 583 | ✗ | static int gbInternal_zKLU_solve(KLUInternals *klu, int system, int size, double *rhs) | |
| 584 | { | ||
| 585 | ✗ | assertStreamPrint(NULL, system >= 0 && system < klu->n_cmplx, "Invalid complex KLU system index %d.", system); | |
| 586 | ✗ | return klu_z_solve(klu->symbolic, klu->num_cmplx[system], size, 1, rhs, &klu->common); | |
| 587 | } | ||
| 588 | |||
| 589 | /** @brief Create scalings for scaled 2-norms: used for Newton convergence and integration acceptance criteria. */ | ||
| 590 | ✗ | static void createGbScales(GB_INTERNAL_NLS_DATA *nls, DATA_GBODE *gbData, double *y1, double *y2) | |
| 591 | { | ||
| 592 | ✗ | const double *nominals = gbData->nominals; | |
| 593 | |||
| 594 | ✗ | if (!nls->multirate) | |
| 595 | { | ||
| 596 | ✗ | for (int i = 0; i < nls->size; i++) | |
| 597 | { | ||
| 598 | ✗ | nls->scal[i] = 1.0 / (nls->integrator_tol * nominals[i] + fmax(fabs(y1[i]), fabs(y2[i])) * nls->integrator_tol); | |
| 599 | } | ||
| 600 | } | ||
| 601 | else | ||
| 602 | { | ||
| 603 | ✗ | for (int i = 0; i < nls->size; i++) | |
| 604 | { | ||
| 605 | ✗ | const size_t fast_idx = (size_t) gbData->fastStatesIdx[i]; | |
| 606 | ✗ | nls->scal[i] = 1.0 / (nls->integrator_tol * nominals[fast_idx] + fmax(fabs(y1[i]), fabs(y2[i])) * nls->integrator_tol); | |
| 607 | } | ||
| 608 | } | ||
| 609 | ✗ | } | |
| 610 | |||
| 611 | /** @brief Compute scaled norm of possible vector stack vec = (v1, v2, ..., v_{stacksize}). */ | ||
| 612 | ✗ | static double gbScalesNorm(GB_INTERNAL_NLS_DATA *nls, double *vec, int stack_size) | |
| 613 | { | ||
| 614 | double sum = 0.0; | ||
| 615 | ✗ | for (int j = 0; j < stack_size; j++) | |
| 616 | { | ||
| 617 | ✗ | double *vec_stride = &vec[j * nls->size]; | |
| 618 | ✗ | for (int i = 0; i < nls->size; i++) | |
| 619 | { | ||
| 620 | ✗ | double tmp = vec_stride[i] * nls->scal[i]; | |
| 621 | ✗ | sum += tmp * tmp; | |
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | ✗ | return sqrt(sum / ((double)nls->size * (double)stack_size)); | |
| 626 | } | ||
| 627 | |||
| 628 | /** @brief Compute scaled norm of possible vector stack vec = (x + v1, x + v2, ..., x + v_{stacksize}). */ | ||
| 629 | ✗ | static double gbScalesNormXPlusZ(GB_INTERNAL_NLS_DATA *nls, double *x, double *z, int stack_size) | |
| 630 | { | ||
| 631 | double sum = 0.0; | ||
| 632 | ✗ | for (int j = 0; j < stack_size; j++) | |
| 633 | { | ||
| 634 | ✗ | double *vec_stride = &z[j * nls->size]; | |
| 635 | ✗ | for (int i = 0; i < nls->size; i++) | |
| 636 | { | ||
| 637 | ✗ | double tmp = (x[i] + vec_stride[i]) * nls->scal[i]; | |
| 638 | ✗ | sum += tmp * tmp; | |
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | ✗ | return sqrt(sum / ((double)nls->size * (double)stack_size)); | |
| 643 | } | ||
| 644 | |||
| 645 | ✗ | static int gbInternalEvaluateSimplifiedJacobian(DATA *data, | |
| 646 | threadData_t *threadData, | ||
| 647 | DATA_GBODE* gbData, | ||
| 648 | GB_INTERNAL_NLS_DATA *nls, | ||
| 649 | modelica_boolean *jac_called, | ||
| 650 | modelica_boolean isDIRK) | ||
| 651 | { | ||
| 652 | int ret; | ||
| 653 | double time_backup; | ||
| 654 | ✗ | int full_size = gbData->nStates; | |
| 655 | ✗ | SOLVERSTATS *stats = (nls->multirate ? &gbData->gbfData->stats : &gbData->stats); | |
| 656 | ✗ | double *y_eval = (nls->multirate ? gbData->gbfData->yOld : gbData->yOld); | |
| 657 | |||
| 658 | // we need to backup potential interpolation data (e.g. in SDIRK realVars already contains Y_full(t0 + h * c1)) | ||
| 659 | // backup the time and all the states and apply them after the simplified Jacobian computation | ||
| 660 | ✗ | if (isDIRK) | |
| 661 | { | ||
| 662 | ✗ | time_backup = data->localData[0]->timeValue; | |
| 663 | ✗ | if (nls->multirate) memcpy(nls->work, data->localData[0]->realVars, full_size * sizeof(double)); | |
| 664 | } | ||
| 665 | |||
| 666 | // set values for known last point y_eval (simplified Newton) | ||
| 667 | ✗ | memcpy(data->localData[0]->realVars, y_eval, full_size * sizeof(double)); | |
| 668 | ✗ | data->localData[0]->timeValue = (nls->multirate ? gbData->gbfData->time : gbData->time); | |
| 669 | |||
| 670 | // callback ODE + callback Jacobian of ODE -> nls_jacobian buffer | ||
| 671 | ✗ | ret = gbode_fODE(data, threadData, &(stats->nCallsODE), nls->multirate ? gbData->gbfData->evalSelectionFast : NULL); // TODO: is this correct? | |
| 672 | ✗ | if (ret < 0) return ret; | |
| 673 | |||
| 674 | ✗ | ret = gbInternal_evalJacobian(data, threadData, gbData, nls); | |
| 675 | ✗ | if (ret < 0) return ret; | |
| 676 | |||
| 677 | ✗ | *jac_called = TRUE; | |
| 678 | |||
| 679 | // reapply the interpolated data | ||
| 680 | ✗ | if (isDIRK) | |
| 681 | { | ||
| 682 | ✗ | data->localData[0]->timeValue = time_backup; | |
| 683 | ✗ | if (nls->multirate) memcpy(data->localData[0]->realVars, nls->work, full_size * sizeof(double)); | |
| 684 | } | ||
| 685 | |||
| 686 | return 0; | ||
| 687 | } | ||
| 688 | |||
| 689 | /** @brief Solve one stage of a DIRK method with the internal solve routine. */ | ||
| 690 | ✗ | static NLS_SOLVER_STATUS gbInternalSolveNls_DIRK(DATA *data, | |
| 691 | threadData_t *threadData, | ||
| 692 | NONLINEAR_SYSTEM_DATA* nonlinsys, | ||
| 693 | DATA_GBODE* gbData, | ||
| 694 | GB_INTERNAL_NLS_DATA *nls) | ||
| 695 | { | ||
| 696 | ✗ | int size = nls->size; | |
| 697 | ✗ | int stage = (nls->multirate ? gbData->gbfData->act_stage : gbData->act_stage); | |
| 698 | ✗ | double *x = nonlinsys->nlsx; | |
| 699 | ✗ | double *x_start = nonlinsys->nlsxOld; // currently the extrapolated (e.g. dense output / hermite guess) | |
| 700 | ✗ | double *res = nonlinsys->resValues; | |
| 701 | ✗ | BUTCHER_TABLEAU *tabl = (nls->multirate ? gbData->gbfData->tableau : gbData->tableau); | |
| 702 | |||
| 703 | ✗ | double stepSize = (nls->multirate ? gbData->gbfData->stepSize : gbData->stepSize); | |
| 704 | ✗ | double lastStepSize = (nls->multirate ? gbData->gbfData->lastStepSize : gbData->lastStepSize); | |
| 705 | |||
| 706 | // return code from KLU or RHS / Jac calls | ||
| 707 | int ret; | ||
| 708 | |||
| 709 | ✗ | createGbScales(nls, gbData, x, x_start); | |
| 710 | double *scal = nls->scal; | ||
| 711 | |||
| 712 | ✗ | RESIDUAL_USERDATA resUserData = {.data=data, .threadData=threadData, .solverData=(nls->multirate ? (void *) gbData->gbfData : (void *) gbData)}; | |
| 713 | ✗ | SPARSE_PATTERN *ode_pattern = getODEPattern(data, gbData, nls); | |
| 714 | SPARSE_PATTERN *nls_pattern = getNLSPattern(gbData, nls); | ||
| 715 | |||
| 716 | ✗ | const int flag = 1; | |
| 717 | ✗ | modelica_boolean jac_called = FALSE; | |
| 718 | |||
| 719 | ✗ | modelica_boolean is_esdirk = (tabl->A[0] == 0.0); | |
| 720 | ✗ | modelica_boolean sdirk_first_stage = (stage == 0 && !is_esdirk); | |
| 721 | ✗ | modelica_boolean esdirk_first_stage = (stage == 1 && is_esdirk); | |
| 722 | |||
| 723 | ✗ | if (sdirk_first_stage || esdirk_first_stage) | |
| 724 | { | ||
| 725 | ✗ | if (nls->call_jac || gbData->eventHappened) | |
| 726 | { | ||
| 727 | ✗ | ret = gbInternalEvaluateSimplifiedJacobian(data, threadData, gbData, nls, &jac_called, TRUE); | |
| 728 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 729 | } | ||
| 730 | |||
| 731 | ✗ | if (jac_called || stepSize != lastStepSize) | |
| 732 | { | ||
| 733 | /* fill NLS Jacobian as h * gamma * J_f - I, where J_f is old or newly computed ODE Jacobian nls->jacobian_callback */ | ||
| 734 | ✗ | jacobian_DIRK_assemble(data, threadData, gbData, nls, ode_pattern, nls->jacobian_callback, nls->real_nls_jacs[0]); | |
| 735 | |||
| 736 | /* perform factorization */ | ||
| 737 | ✗ | ret = gbInternal_dKLU_factorize(&nls->klu, 0, (int *) nls_pattern->leadindex, (int *) nls_pattern->index, nls->real_nls_jacs[0]); | |
| 738 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | /* invalidate eta of stage if an event happened */ | ||
| 743 | ✗ | if (gbData->eventHappened) nls->etas[stage] = DBL_MAX; | |
| 744 | |||
| 745 | ✗ | memcpy(x, x_start, size * sizeof(double)); | |
| 746 | |||
| 747 | // norms, convergence rate | ||
| 748 | double nrm_x = 0; | ||
| 749 | double nrm_delta = 0; | ||
| 750 | double nrm_delta_prev = 0; | ||
| 751 | double theta = 0; | ||
| 752 | |||
| 753 | // Newton iteration count - we start with newt_it = 1, because we need this for the step size selection and conditions below | ||
| 754 | ✗ | for (int newt_it = 1 ;; newt_it++) | |
| 755 | ✗ | { | |
| 756 | ✗ | nonlinsys->residualFunc(&resUserData, x, res, &flag); | |
| 757 | |||
| 758 | ✗ | ret = gbInternal_dKLU_solve(&nls->klu, 0, size, res); | |
| 759 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 760 | ✗ | daxpy_(&size, &DBL_MINUS_ONE, res, &INT_ONE, x, &INT_ONE); | |
| 761 | |||
| 762 | ✗ | nrm_delta_prev = fmax(DBL_EPSILON, nrm_delta); | |
| 763 | ✗ | nrm_delta = gbScalesNorm(nls, res, 1); | |
| 764 | |||
| 765 | // handle absorption effects | ||
| 766 | ✗ | nrm_x = gbScalesNorm(nls, x, 1); | |
| 767 | ✗ | modelica_boolean absorption = (nrm_delta <= DBL_ABSORPTION * nrm_x); | |
| 768 | |||
| 769 | ✗ | if (newt_it > 1) | |
| 770 | { | ||
| 771 | ✗ | theta = nrm_delta / nrm_delta_prev; | |
| 772 | |||
| 773 | // Newton failed -> divergence | ||
| 774 | ✗ | if (theta >= nls->theta_divergence && !absorption) | |
| 775 | { | ||
| 776 | break; | ||
| 777 | } | ||
| 778 | |||
| 779 | ✗ | nls->etas[stage] = theta / (1 - theta); | |
| 780 | } | ||
| 781 | else | ||
| 782 | { | ||
| 783 | ✗ | nls->etas[stage] = pow(fmax(nls->etas[stage], DBL_EPSILON), nls->eta_inital_damping); | |
| 784 | } | ||
| 785 | |||
| 786 | ✗ | if (!isfinite(nls->etas[stage]) || !isfinite(nrm_delta)) | |
| 787 | { | ||
| 788 | // Inf or NaN detected | ||
| 789 | // Either RHS or Jacobian or solution of the system contained a Inf or NaN | ||
| 790 | return NLS_FAILED; | ||
| 791 | } | ||
| 792 | |||
| 793 | // Newton converged | ||
| 794 | ✗ | if (nls->etas[stage] * nrm_delta < nls->fnewt || absorption) | |
| 795 | { | ||
| 796 | ✗ | if (theta < nls->theta_keep) | |
| 797 | { | ||
| 798 | ✗ | nls->call_jac = FALSE; | |
| 799 | } | ||
| 800 | else | ||
| 801 | { | ||
| 802 | ✗ | nls->call_jac = TRUE; | |
| 803 | } | ||
| 804 | |||
| 805 | ✗ | return NLS_SOLVED; | |
| 806 | } | ||
| 807 | |||
| 808 | // Newton failed -> iteration limit exceeded or too slow convergence | ||
| 809 | ✗ | if (newt_it == nls->max_newton_it || (pow(theta, nls->max_newton_it - newt_it) / (1 - theta) * nrm_delta > nls->fnewt)) | |
| 810 | { | ||
| 811 | break; | ||
| 812 | } | ||
| 813 | } | ||
| 814 | |||
| 815 | ✗ | nls->call_jac = TRUE; | |
| 816 | ✗ | return NLS_FAILED; | |
| 817 | } | ||
| 818 | |||
| 819 | /** @brief Compute (T otimes I) * v for block vectors (applies T to block_count blocks of size block_size). */ | ||
| 820 | ✗ | static inline void dense_kron_id_vec(int block_count, | |
| 821 | int block_size, | ||
| 822 | const double *T, | ||
| 823 | const double *v, | ||
| 824 | double *out) | ||
| 825 | { | ||
| 826 | ✗ | dgemm_( | |
| 827 | &CHAR_NO_TRANS, &CHAR_NO_TRANS, | ||
| 828 | &block_size, &block_count, &block_count, | ||
| 829 | &DBL_ONE, | ||
| 830 | v, &block_size, | ||
| 831 | T, &block_count, | ||
| 832 | &DBL_ZERO, | ||
| 833 | out, &block_size | ||
| 834 | ); | ||
| 835 | ✗ | } | |
| 836 | |||
| 837 | /** | ||
| 838 | * @brief Multiply a stacked vector by a scaled block-lower triangular or block-diagonal matrix | ||
| 839 | * @par Runtime: O(m * n) for block diagonal -- O(m^2 * n) for fully dense block-lower-triangular | ||
| 840 | * | ||
| 841 | * out += factor * ((Lambda + L) otimes I_n) * v. | ||
| 842 | * | ||
| 843 | * Vector layout is fixed by the transform: | ||
| 844 | * - first nRealBlocks scalar real rows, | ||
| 845 | * - then nComplexBlocks consecutive 2x2 real blocks. | ||
| 846 | * | ||
| 847 | * Lambda is the block diagonal part. Each diagonal block is either: | ||
| 848 | * - 1x1 real row: out_i += factor * gamma[i] * v_i | ||
| 849 | * | ||
| 850 | * - 2x2 complex block: out_i += factor * alpha[j] * v_i - factor * beta[j] * v_{i+1} | ||
| 851 | * out_i+1 += factor * beta[j] * v_i + factor * alpha[j] * v_{i+1} | ||
| 852 | * | ||
| 853 | * where i and j are given from the eigenvalue indices (realEigenvalueIndex, complexEigenvalueIndex). | ||
| 854 | * | ||
| 855 | * L contains only the strict lower triangular couplings outside these diagonal blocks. | ||
| 856 | * Hence L never stores the lower entry inside a complex 2x2 block; that entry is beta[j]. | ||
| 857 | * Rows with hasL[row] == FALSE are skipped completely. | ||
| 858 | */ | ||
| 859 | ✗ | static void scaled_transform_matvec(T_TRANSFORM *transform, | |
| 860 | int block_size, | ||
| 861 | const double factor, | ||
| 862 | const double *v, | ||
| 863 | double *out) | ||
| 864 | { | ||
| 865 | // Diagonal / Block-Diagonal part: | ||
| 866 | |||
| 867 | // 1x1 real blocks: out_i += factor * gamma_i * v_i | ||
| 868 | ✗ | for (int real_row = 0; real_row < transform->nRealBlocks; real_row++) | |
| 869 | { | ||
| 870 | ✗ | int sys = transform->realEigenvalueIndex[real_row]; | |
| 871 | ✗ | double a = factor * transform->gamma[sys]; | |
| 872 | ✗ | daxpy_(&block_size, &a, &v[real_row * block_size], &INT_ONE, &out[real_row * block_size], &INT_ONE); | |
| 873 | } | ||
| 874 | |||
| 875 | ✗ | int offset = transform->nRealBlocks * block_size; | |
| 876 | |||
| 877 | // 2x2 blocks: out[j] += factor * (alpha_j * v[j] - beta_j * v[j+1]) | ||
| 878 | // out[j+1] += factor * (beta_j * v[j] + alpha_j * v[j+1]) | ||
| 879 | ✗ | for (int cmplx_block = 0; cmplx_block < transform->nComplexBlocks; cmplx_block++) | |
| 880 | { | ||
| 881 | ✗ | int sys = transform->complexEigenpairIndex[cmplx_block]; | |
| 882 | ✗ | double a = factor * transform->alpha[sys]; | |
| 883 | ✗ | double b = factor * transform->beta[sys]; | |
| 884 | ✗ | double mb = -b; | |
| 885 | |||
| 886 | ✗ | const double *v0 = &v[offset]; | |
| 887 | ✗ | const double *v1 = &v[offset + block_size]; | |
| 888 | |||
| 889 | ✗ | double *out0 = &out[offset]; | |
| 890 | ✗ | double *out1 = &out[offset + block_size]; | |
| 891 | |||
| 892 | // out0 = a*v0 - b*v1 | ||
| 893 | ✗ | daxpy_(&block_size, &a, v0, &INT_ONE, out0, &INT_ONE); // out0 += a*v0 | |
| 894 | ✗ | daxpy_(&block_size, &mb, v1, &INT_ONE, out0, &INT_ONE); // out0 -= b*v1 | |
| 895 | |||
| 896 | // out1 = a*v1 + b*v0 | ||
| 897 | ✗ | daxpy_(&block_size, &a, v1, &INT_ONE, out1, &INT_ONE); // out1 += a*v1 | |
| 898 | ✗ | daxpy_(&block_size, &b, v0, &INT_ONE, out1, &INT_ONE); // out1 += b*v0 | |
| 899 | |||
| 900 | ✗ | offset += 2 * block_size; | |
| 901 | } | ||
| 902 | |||
| 903 | // Strictly lower triangular part: | ||
| 904 | |||
| 905 | // L rows for real blocks: out[row] += factor * L[row,col] * v[col], col < row | ||
| 906 | ✗ | for (int row = 1; row < transform->nRealBlocks; row++) | |
| 907 | { | ||
| 908 | ✗ | if (!transform->hasL[row]) continue; | |
| 909 | |||
| 910 | ✗ | double *out_row = &out[row * block_size]; | |
| 911 | |||
| 912 | ✗ | for (int col = 0; col < row; col++) | |
| 913 | { | ||
| 914 | ✗ | double a = factor * transform->L[GBODE_L_INDEX(row, col)]; | |
| 915 | ✗ | if (a != 0.0) daxpy_(&block_size, &a, &v[col * block_size], &INT_ONE, out_row, &INT_ONE); | |
| 916 | } | ||
| 917 | } | ||
| 918 | |||
| 919 | // L rows for complex blocks: out[i] += factor * L[i,col] * v[col], col < i | ||
| 920 | // out[i+1] += factor * L[i+1,col] * v[col], col < i | ||
| 921 | int cmplx_row = transform->nRealBlocks; | ||
| 922 | ✗ | for (int cmplx_block = 0; cmplx_block < transform->nComplexBlocks; cmplx_block++) | |
| 923 | { | ||
| 924 | int row0 = cmplx_row; | ||
| 925 | ✗ | int row1 = cmplx_row + 1; | |
| 926 | |||
| 927 | ✗ | double *out0 = &out[row0 * block_size]; | |
| 928 | ✗ | double *out1 = &out[row1 * block_size]; | |
| 929 | |||
| 930 | ✗ | if (transform->hasL[row0]) | |
| 931 | { | ||
| 932 | ✗ | for (int col = 0; col < cmplx_row; col++) | |
| 933 | { | ||
| 934 | ✗ | double a = factor * transform->L[GBODE_L_INDEX(row0, col)]; | |
| 935 | ✗ | if (a != 0.0) daxpy_(&block_size, &a, &v[col * block_size], &INT_ONE, out0, &INT_ONE); | |
| 936 | } | ||
| 937 | } | ||
| 938 | |||
| 939 | ✗ | if (transform->hasL[row1]) | |
| 940 | { | ||
| 941 | ✗ | for (int col = 0; col < cmplx_row; col++) | |
| 942 | { | ||
| 943 | ✗ | double a = factor * transform->L[GBODE_L_INDEX(row1, col)]; | |
| 944 | ✗ | if (a != 0.0) daxpy_(&block_size, &a, &v[col * block_size], &INT_ONE, out1, &INT_ONE); | |
| 945 | } | ||
| 946 | } | ||
| 947 | |||
| 948 | ✗ | cmplx_row += 2; | |
| 949 | } | ||
| 950 | ✗ | } | |
| 951 | |||
| 952 | #define GB_INTERNAL_LEFT_BOUNDARY -1 | ||
| 953 | |||
| 954 | /** | ||
| 955 | * @brief Set states and time for a T-transform stage evaluation | ||
| 956 | * @par Runtime: O(nStates) | ||
| 957 | * | ||
| 958 | * In multirate mode, slow states are restored from the slow-state cache and | ||
| 959 | * fast states are overwritten from the input vector using the fast state index | ||
| 960 | * mapping. In single-rate mode, the full state vector is copied from the input. | ||
| 961 | * | ||
| 962 | * The simulation time is set to the corresponding stage time using the tableau | ||
| 963 | * coefficient. If stage < 0, the left interval boundary time is used. | ||
| 964 | * | ||
| 965 | * @param[in,out] data Data | ||
| 966 | * @param[in] gbData GBODE data | ||
| 967 | * @param[in] nls Internal nonlinear system data | ||
| 968 | * @param[in] y_fast Input state vector (size nls->size) | ||
| 969 | * @param[in] stage Stage index of the tableau, or negative for left boundary (use macro GB_INTERNAL_LEFT_BOUNDARY) | ||
| 970 | */ | ||
| 971 | ✗ | static inline void gbInternal_T_Transform_set_states(DATA *data, DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls, const double *y_fast, int stage) | |
| 972 | { | ||
| 973 | ✗ | if (nls->multirate) | |
| 974 | { | ||
| 975 | ✗ | DATA_GBODEF *gbfData = gbData->gbfData; | |
| 976 | |||
| 977 | ✗ | if (stage >= 0) | |
| 978 | { | ||
| 979 | ✗ | slowStateCache_overwrite_stage(gbData, gbfData->slowStateCache, stage, data->localData[0]->realVars); | |
| 980 | } | ||
| 981 | else | ||
| 982 | { | ||
| 983 | ✗ | slowStateCache_overwrite_left(gbData, gbfData->slowStateCache, data->localData[0]->realVars); | |
| 984 | } | ||
| 985 | |||
| 986 | ✗ | for (int fast_idx = 0; fast_idx < nls->size; fast_idx++) | |
| 987 | { | ||
| 988 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 989 | ✗ | data->localData[0]->realVars[full_idx] = y_fast[fast_idx]; | |
| 990 | } | ||
| 991 | |||
| 992 | ✗ | data->localData[0]->timeValue = (stage >= 0 ? gbfData->time + gbfData->tableau->c[stage] * gbfData->stepSize : gbfData->time); | |
| 993 | } | ||
| 994 | else | ||
| 995 | { | ||
| 996 | ✗ | memcpy(data->localData[0]->realVars, y_fast, nls->size * sizeof(double)); | |
| 997 | ✗ | data->localData[0]->timeValue = (stage >= 0 ? gbData->time + gbData->tableau->c[stage] * gbData->stepSize : gbData->time); | |
| 998 | } | ||
| 999 | ✗ | } | |
| 1000 | |||
| 1001 | ✗ | static inline void gbInternal_T_Transform_copy_full_to_fast(DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls, const double *src_full, double *dest_fast) | |
| 1002 | { | ||
| 1003 | ✗ | if (nls->multirate) | |
| 1004 | { | ||
| 1005 | ✗ | for (int fast_idx = 0; fast_idx < nls->size; fast_idx++) | |
| 1006 | { | ||
| 1007 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 1008 | ✗ | dest_fast[fast_idx] = src_full[full_idx]; | |
| 1009 | } | ||
| 1010 | } | ||
| 1011 | else | ||
| 1012 | { | ||
| 1013 | ✗ | memcpy(dest_fast, src_full, nls->size * sizeof(double)); | |
| 1014 | } | ||
| 1015 | ✗ | } | |
| 1016 | |||
| 1017 | ✗ | static inline void gbInternal_T_Transform_copy_fast_to_full(DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls, const double *src_fast, double *dest_full) | |
| 1018 | { | ||
| 1019 | ✗ | if (nls->multirate) | |
| 1020 | { | ||
| 1021 | ✗ | for (int fast_idx = 0; fast_idx < nls->size; fast_idx++) | |
| 1022 | { | ||
| 1023 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 1024 | ✗ | dest_full[full_idx] = src_fast[fast_idx]; | |
| 1025 | } | ||
| 1026 | } | ||
| 1027 | else | ||
| 1028 | { | ||
| 1029 | ✗ | memcpy(dest_full, src_fast, nls->size * sizeof(double)); | |
| 1030 | } | ||
| 1031 | ✗ | } | |
| 1032 | |||
| 1033 | ✗ | static inline void gbInternal_T_Transform_fast_to_full_axpy(DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls, const double alpha, const double *x_fast, double *y_full) | |
| 1034 | { | ||
| 1035 | ✗ | if (nls->multirate) | |
| 1036 | { | ||
| 1037 | ✗ | for (int fast_idx = 0; fast_idx < nls->size; fast_idx++) | |
| 1038 | { | ||
| 1039 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 1040 | ✗ | y_full[full_idx] += alpha * x_fast[fast_idx]; | |
| 1041 | } | ||
| 1042 | } | ||
| 1043 | else | ||
| 1044 | { | ||
| 1045 | ✗ | daxpy_(&nls->size, &alpha, x_fast, &INT_ONE, y_full, &INT_ONE); | |
| 1046 | } | ||
| 1047 | ✗ | } | |
| 1048 | |||
| 1049 | ✗ | static inline void gbInternal_T_Transform_full_to_fast_axpy(DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls, const double alpha, const double *x_full, double *y_fast) | |
| 1050 | { | ||
| 1051 | ✗ | if (nls->multirate) | |
| 1052 | { | ||
| 1053 | ✗ | for (int fast_idx = 0; fast_idx < nls->size; fast_idx++) | |
| 1054 | { | ||
| 1055 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 1056 | ✗ | y_fast[fast_idx] += alpha * x_full[full_idx]; | |
| 1057 | } | ||
| 1058 | } | ||
| 1059 | else | ||
| 1060 | { | ||
| 1061 | ✗ | daxpy_(&nls->size, &alpha, x_full, &INT_ONE, y_fast, &INT_ONE); | |
| 1062 | } | ||
| 1063 | ✗ | } | |
| 1064 | |||
| 1065 | static inline void gbInternal_T_Transform_copy_full_to_full_axpy(DATA_GBODE *gbData, GB_INTERNAL_NLS_DATA *nls, const double alpha, const double *x_full, double *y_full) | ||
| 1066 | { | ||
| 1067 | if (nls->multirate) | ||
| 1068 | { | ||
| 1069 | for (int fast_idx = 0; fast_idx < nls->size; fast_idx++) | ||
| 1070 | { | ||
| 1071 | int full_idx = gbData->fastStatesIdx[fast_idx]; | ||
| 1072 | y_full[full_idx] += alpha * x_full[full_idx]; | ||
| 1073 | } | ||
| 1074 | } | ||
| 1075 | else | ||
| 1076 | { | ||
| 1077 | daxpy_(&nls->size, &alpha, x_full, &INT_ONE, y_full, &INT_ONE); | ||
| 1078 | } | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | /** @brief Solve entire NLS of FIRK with possibly singular Runge-Kutta matrix | ||
| 1082 | * via the T-transformation (decoupled space). | ||
| 1083 | * | ||
| 1084 | * After convergence the solutions are written into nonlinsys->x and the stage updates are | ||
| 1085 | * written into gbData->k or gbfData->kCurrPacked. | ||
| 1086 | */ | ||
| 1087 | ✗ | static NLS_SOLVER_STATUS gbInternalSolveNls_T_Transform(DATA *data, | |
| 1088 | threadData_t *threadData, | ||
| 1089 | NONLINEAR_SYSTEM_DATA* nonlinsys, | ||
| 1090 | DATA_GBODE* gbData, | ||
| 1091 | GB_INTERNAL_NLS_DATA *nls) | ||
| 1092 | { | ||
| 1093 | ✗ | int size = nls->size; | |
| 1094 | ✗ | int w_size = nls->size * nls->tabl->t_transform->size; | |
| 1095 | ✗ | double stepSize = (nls->multirate ? gbData->gbfData->stepSize : gbData->stepSize); | |
| 1096 | ✗ | double lastStepSize = (nls->multirate ? gbData->gbfData->lastStepSize : gbData->lastStepSize); | |
| 1097 | ✗ | double invh = 1.0 / stepSize; | |
| 1098 | ✗ | double minvh = -invh; | |
| 1099 | ✗ | double *x = nonlinsys->nlsx; | |
| 1100 | ✗ | double *x_start = nonlinsys->nlsxOld; | |
| 1101 | ✗ | double *flat_res = nonlinsys->resValues; | |
| 1102 | |||
| 1103 | ✗ | double *yOld = (nls->multirate ? gbData->gbfData->yOldPacked : gbData->yOld); | |
| 1104 | ✗ | double *kPacked = (nls->multirate ? gbData->gbfData->kCurrPacked : gbData->k); | |
| 1105 | |||
| 1106 | ✗ | EVAL_SELECTION *selection = (nls->multirate ? gbData->gbfData->evalSelectionFast : NULL); | |
| 1107 | |||
| 1108 | ✗ | SOLVERSTATS *stats = (nls->multirate ? &gbData->gbfData->stats : &gbData->stats); | |
| 1109 | |||
| 1110 | // return code from KLU or RHS / Jac calls | ||
| 1111 | int ret; | ||
| 1112 | |||
| 1113 | ✗ | createGbScales(nls, gbData, x, x_start); | |
| 1114 | double *scal = nls->scal; | ||
| 1115 | |||
| 1116 | ✗ | SPARSE_PATTERN *ode_pattern = getODEPattern(data, gbData, nls); | |
| 1117 | SPARSE_PATTERN *nls_pattern = getNLSPattern(gbData, nls); | ||
| 1118 | ✗ | T_TRANSFORM *transform = nls->tabl->t_transform; | |
| 1119 | |||
| 1120 | modelica_boolean jac_called = FALSE; | ||
| 1121 | |||
| 1122 | ✗ | if (nls->call_jac || transform->firstRowZero || gbData->eventHappened) | |
| 1123 | { | ||
| 1124 | /* set values for known last point (simplified Newton) */ | ||
| 1125 | ✗ | gbInternal_T_Transform_set_states(data, gbData, nls, yOld, GB_INTERNAL_LEFT_BOUNDARY); | |
| 1126 | |||
| 1127 | /* callback ODE + callback Jacobian of ODE -> nls_jacobian buffer | ||
| 1128 | => TODO: if method has property a_{s,:} = b, i.e. FSAL or kLeft is available, we could recycle the k_s from before here | ||
| 1129 | or does fODE set algebraic variables that may be needed for the Jacobian? */ | ||
| 1130 | ✗ | ret = gbode_fODE(data, threadData, &stats->nCallsODE, selection); | |
| 1131 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1132 | |||
| 1133 | ✗ | if (transform->firstRowZero) | |
| 1134 | { | ||
| 1135 | // save explicit stage (e.g. Lobatto IIIA) | ||
| 1136 | ✗ | gbInternal_T_Transform_copy_full_to_fast(gbData, nls, &data->localData[0]->realVars[gbData->nStates], kPacked); | |
| 1137 | } | ||
| 1138 | |||
| 1139 | ✗ | if (nls->call_jac || gbData->eventHappened) | |
| 1140 | { | ||
| 1141 | ✗ | ret = gbInternal_evalJacobian(data, threadData, gbData, nls); | |
| 1142 | ✗ | if (ret < 0) return ret; | |
| 1143 | |||
| 1144 | jac_called = TRUE; | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | ✗ | if (jac_called || stepSize != lastStepSize) | |
| 1149 | { | ||
| 1150 | ✗ | for (int sys_real = 0; sys_real < transform->nRealEigenvalues; sys_real++) | |
| 1151 | { | ||
| 1152 | /* create Jacobian real: gamma/h * I - J_f */ | ||
| 1153 | ✗ | jacobian_real_assemble(data, threadData, gbData, nls, transform->gamma[sys_real], | |
| 1154 | ✗ | ode_pattern, nls->jacobian_callback, nls->real_nls_jacs[sys_real]); | |
| 1155 | ✗ | ret = gbInternal_dKLU_factorize(&nls->klu, sys_real, | |
| 1156 | ✗ | (int *) nls_pattern->leadindex, | |
| 1157 | ✗ | (int *) nls_pattern->index, | |
| 1158 | ✗ | nls->real_nls_jacs[sys_real]); | |
| 1159 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1160 | } | ||
| 1161 | ✗ | for (int sys_cmplx = 0; sys_cmplx < transform->nComplexEigenpairs; sys_cmplx++) | |
| 1162 | { | ||
| 1163 | /* create Jacobian complex: (alpha + i * beta)/h * I - J_f */ | ||
| 1164 | ✗ | jacobian_cmplx_assemble(data, threadData, gbData, nls, transform->alpha[sys_cmplx], transform->beta[sys_cmplx], | |
| 1165 | ✗ | ode_pattern, nls->jacobian_callback, nls->cmplx_nls_jacs[sys_cmplx]); | |
| 1166 | ✗ | ret = gbInternal_zKLU_factorize(&nls->klu, sys_cmplx, | |
| 1167 | ✗ | (int *) nls_pattern->leadindex, | |
| 1168 | ✗ | (int *) nls_pattern->index, | |
| 1169 | ✗ | nls->cmplx_nls_jacs[sys_cmplx]); | |
| 1170 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1171 | } | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | // we solve for Z = X(t_ij) - yOld or W = (T^{-1} otimes I) * Z, then get K back via K = 1/h * A^{-1} * Z | ||
| 1175 | |||
| 1176 | // set guess Z[j] = X_start[j] - yOld | ||
| 1177 | ✗ | for (int j = 0; j < transform->size; j++) | |
| 1178 | { | ||
| 1179 | ✗ | memcpy(&nls->Z[j * size], &x_start[j * size], size * sizeof(double)); | |
| 1180 | ✗ | daxpy_(&size, &DBL_MINUS_ONE, yOld, &INT_ONE, &nls->Z[j * size], &INT_ONE); | |
| 1181 | } | ||
| 1182 | |||
| 1183 | // W = (T^{-1} otimes I) * Z | ||
| 1184 | ✗ | dense_kron_id_vec(transform->size, size, transform->T_inv, nls->Z, nls->W); | |
| 1185 | |||
| 1186 | // norms, convergence rate | ||
| 1187 | double nrm_x = 0; | ||
| 1188 | double nrm_delta = 0; | ||
| 1189 | double nrm_delta_prev = 0; | ||
| 1190 | double theta = 0; | ||
| 1191 | |||
| 1192 | // invalidate eta if an event happened | ||
| 1193 | ✗ | if (gbData->eventHappened) *nls->etas = DBL_MAX; | |
| 1194 | |||
| 1195 | // Newton iteration count - we start with newt_it = 1, because we need this for the step size selection and conditions below | ||
| 1196 | ✗ | for (int newt_it = 1 ;; newt_it++) | |
| 1197 | ✗ | { | |
| 1198 | // compute residuals: rhs := -1 / h (Lambda otimes I) * W + (T^{-1} * I) * F((T otimes I) * W) + Phi (Phi = T^{-1} A_part^{-1} * K_1 if (K_1 explicit else 0)) | ||
| 1199 | |||
| 1200 | // work[j] = F((T otimes I) * W)[j] | ||
| 1201 | ✗ | for (int j = 0; j < transform->size; j++) | |
| 1202 | { | ||
| 1203 | ✗ | gbInternal_T_Transform_set_states(data, gbData, nls, yOld, j + (int)transform->firstRowZero); | |
| 1204 | ✗ | gbInternal_T_Transform_fast_to_full_axpy(gbData, nls, DBL_ONE, &nls->Z[j * size], data->localData[0]->realVars); | |
| 1205 | ✗ | ret = gbode_fODE(data, threadData, &stats->nCallsODE, selection); | |
| 1206 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1207 | |||
| 1208 | ✗ | gbInternal_T_Transform_copy_full_to_fast(gbData, nls, &data->localData[0]->realVars[gbData->nStates], &nls->work[j * size]); | |
| 1209 | } | ||
| 1210 | |||
| 1211 | // rhs[j] = (T^{-1} otimes I) * F((T otimes I) * W) | ||
| 1212 | ✗ | dense_kron_id_vec(transform->size, size, transform->T_inv, nls->work, flat_res); | |
| 1213 | |||
| 1214 | // rhs[j] += -1 / h * ((Lambda + L) otimes I) * W | ||
| 1215 | ✗ | scaled_transform_matvec(transform, size, minvh, nls->W, flat_res); | |
| 1216 | |||
| 1217 | // add Phi = T^{-1} * A_part^{-1} * a{r, 1} * K_1 if first stage is explicit, else skip (we computed nls->phi = T^{-1} * A_part^{-1} * a{r, 1}) | ||
| 1218 | // where r are all rows that belong to A_part | ||
| 1219 | ✗ | if (transform->firstRowZero) | |
| 1220 | { | ||
| 1221 | ✗ | for (int j = 0; j < transform->size; j++) | |
| 1222 | { | ||
| 1223 | ✗ | daxpy_(&size, &transform->phi[j], kPacked, | |
| 1224 | ✗ | &INT_ONE, &flat_res[j * size], &INT_ONE); | |
| 1225 | } | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | ✗ | for (int real_row = 0; real_row < transform->nRealBlocks; real_row++) | |
| 1229 | { | ||
| 1230 | ✗ | double *res_row = &flat_res[real_row * size]; | |
| 1231 | |||
| 1232 | ✗ | if (transform->hasL[real_row]) | |
| 1233 | { | ||
| 1234 | ✗ | for (int col = 0; col < real_row; col++) | |
| 1235 | { | ||
| 1236 | ✗ | double a = -invh * transform->L[GBODE_L_INDEX(real_row, col)]; | |
| 1237 | ✗ | if (a != 0.0) daxpy_(&size, &a, &flat_res[col * size], &INT_ONE, res_row, &INT_ONE); | |
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | ✗ | int sys = transform->realEigenvalueIndex[real_row]; | |
| 1242 | ✗ | ret = gbInternal_dKLU_solve(&nls->klu, sys, size, res_row); | |
| 1243 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1244 | } | ||
| 1245 | |||
| 1246 | int cmplx_row = transform->nRealBlocks; | ||
| 1247 | ✗ | for (int cmplx_block = 0; cmplx_block < transform->nComplexBlocks; cmplx_block++) | |
| 1248 | { | ||
| 1249 | int row0 = cmplx_row; | ||
| 1250 | ✗ | int row1 = cmplx_row + 1; | |
| 1251 | ✗ | double *res0 = &flat_res[row0 * size]; | |
| 1252 | ✗ | double *res1 = &flat_res[row1 * size]; | |
| 1253 | |||
| 1254 | ✗ | if (transform->hasL[row0]) | |
| 1255 | { | ||
| 1256 | ✗ | for (int col = 0; col < cmplx_row; col++) | |
| 1257 | { | ||
| 1258 | ✗ | double a = -invh * transform->L[GBODE_L_INDEX(row0, col)]; | |
| 1259 | ✗ | if (a != 0.0) daxpy_(&size, &a, &flat_res[col * size], &INT_ONE, res0, &INT_ONE); | |
| 1260 | } | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | ✗ | if (transform->hasL[row1]) | |
| 1264 | { | ||
| 1265 | ✗ | for (int col = 0; col < cmplx_row; col++) | |
| 1266 | { | ||
| 1267 | ✗ | double a = -invh * transform->L[GBODE_L_INDEX(row1, col)]; | |
| 1268 | ✗ | if (a != 0.0) daxpy_(&size, &a, &flat_res[col * size], &INT_ONE, res1, &INT_ONE); | |
| 1269 | } | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | ✗ | int sys = transform->complexEigenpairIndex[cmplx_block]; | |
| 1273 | ✗ | dcopy_(&size, res0, &INT_ONE, &nls->cmplx_nls_res[sys][0], &INT_TWO); // .real | |
| 1274 | ✗ | dcopy_(&size, res1, &INT_ONE, &nls->cmplx_nls_res[sys][1], &INT_TWO); // .imag | |
| 1275 | |||
| 1276 | ✗ | ret = gbInternal_zKLU_solve(&nls->klu, sys, size, &nls->cmplx_nls_res[sys][0]); | |
| 1277 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1278 | |||
| 1279 | ✗ | dcopy_(&size, &nls->cmplx_nls_res[sys][0], &INT_TWO, res0, &INT_ONE); // r1 | |
| 1280 | ✗ | dcopy_(&size, &nls->cmplx_nls_res[sys][1], &INT_TWO, res1, &INT_ONE); // r2 | |
| 1281 | |||
| 1282 | ✗ | cmplx_row += 2; | |
| 1283 | } | ||
| 1284 | |||
| 1285 | // Newton step (we must do W += dW) | ||
| 1286 | ✗ | daxpy_(&w_size, &DBL_ONE, flat_res, &INT_ONE, nls->W, &INT_ONE); | |
| 1287 | |||
| 1288 | // Z = (T otimes I) * W | ||
| 1289 | ✗ | dense_kron_id_vec(transform->size, size, transform->T, nls->W, nls->Z); | |
| 1290 | |||
| 1291 | ✗ | nrm_delta_prev = fmax(DBL_EPSILON, nrm_delta); | |
| 1292 | ✗ | nrm_delta = gbScalesNorm(nls, flat_res, nls->tabl->t_transform->size); | |
| 1293 | |||
| 1294 | // handle absorption effects | ||
| 1295 | ✗ | nrm_x = gbScalesNormXPlusZ(nls, yOld, nls->Z, transform->size); | |
| 1296 | ✗ | modelica_boolean absorption = (nrm_delta <= DBL_ABSORPTION * nrm_x); | |
| 1297 | |||
| 1298 | ✗ | if (newt_it > 1) | |
| 1299 | { | ||
| 1300 | ✗ | theta = nrm_delta / nrm_delta_prev; | |
| 1301 | |||
| 1302 | // Newton failed -> divergence | ||
| 1303 | ✗ | if (theta >= nls->theta_divergence && !absorption) | |
| 1304 | { | ||
| 1305 | ✗ | nls->call_jac = TRUE; | |
| 1306 | ✗ | return NLS_FAILED; | |
| 1307 | } | ||
| 1308 | |||
| 1309 | ✗ | *nls->etas = theta / (1 - theta); | |
| 1310 | } | ||
| 1311 | else | ||
| 1312 | { | ||
| 1313 | ✗ | *nls->etas = pow(fmax(*nls->etas, DBL_EPSILON), nls->eta_inital_damping); | |
| 1314 | } | ||
| 1315 | |||
| 1316 | ✗ | if (!isfinite(*nls->etas) || !isfinite(nrm_delta)) | |
| 1317 | { | ||
| 1318 | // Inf or NaN detected | ||
| 1319 | // Either RHS or Jacobian or solution of the system contained a Inf or NaN | ||
| 1320 | return NLS_FAILED; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | // Newton converged | ||
| 1324 | ✗ | if (*nls->etas * nrm_delta < nls->fnewt || absorption) | |
| 1325 | { | ||
| 1326 | ✗ | if (theta < nls->theta_keep) | |
| 1327 | { | ||
| 1328 | ✗ | nls->call_jac = FALSE; | |
| 1329 | } | ||
| 1330 | else | ||
| 1331 | { | ||
| 1332 | ✗ | nls->call_jac = TRUE; | |
| 1333 | } | ||
| 1334 | |||
| 1335 | // set solution X[j] = X_0 + Z[j] | ||
| 1336 | ✗ | int offset = transform->firstRowZero ? size : 0; | |
| 1337 | ✗ | for (int j = 0; j < transform->size; j++) | |
| 1338 | { | ||
| 1339 | ✗ | memcpy(&x[j * size + offset], &nls->Z[j * size], size * sizeof(double)); | |
| 1340 | ✗ | daxpy_(&size, &DBL_ONE, yOld, &INT_ONE, &x[j * size + offset], &INT_ONE); | |
| 1341 | } | ||
| 1342 | |||
| 1343 | // recompute weights K from Z via K = 1 / h * (A_part^{-1} otimes I) * Z + rho * k_1 if k_1 explicit else 0 (rho := -A_part^{-1} * A_{r, 1}) | ||
| 1344 | // where r are all rows that belong to A_part | ||
| 1345 | ✗ | dense_kron_id_vec(transform->size, size, transform->A_part_inv, nls->Z, &kPacked[offset]); | |
| 1346 | ✗ | dscal_(&w_size, &invh, &kPacked[offset], &INT_ONE); | |
| 1347 | |||
| 1348 | ✗ | if (transform->firstRowZero) | |
| 1349 | { | ||
| 1350 | // add k[j] += rho[j] * k_1 or k[j] -= (-A_part^{-1} * A_{r, 1} * k_1)[j] * k_1 | ||
| 1351 | ✗ | for (int j = 0; j < transform->size; j++) | |
| 1352 | { | ||
| 1353 | ✗ | daxpy_(&size, &transform->rho[j], kPacked, | |
| 1354 | ✗ | &INT_ONE, &kPacked[offset + j * size], &INT_ONE); | |
| 1355 | } | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | // for explicit first stage: copy x0 into x (e.g. Lobatto IIIA) | ||
| 1359 | ✗ | if (transform->firstRowZero) | |
| 1360 | { | ||
| 1361 | ✗ | memcpy(x, yOld, size * sizeof(double)); | |
| 1362 | } | ||
| 1363 | |||
| 1364 | // for explicit last stage: compute final K_s and X_s | ||
| 1365 | ✗ | if (transform->lastColumnZero) | |
| 1366 | { | ||
| 1367 | ✗ | int s_minus1 = nls->tabl->nStages - 1; | |
| 1368 | |||
| 1369 | // X_s = x0 + h * sum{j=1}^{s-1} A_{s, j} * k_j as A_{s, s} == 0! | ||
| 1370 | ✗ | memcpy(&x[size * s_minus1], yOld, size * sizeof(double)); | |
| 1371 | ✗ | dgemm_(&CHAR_NO_TRANS, &CHAR_TRANS, | |
| 1372 | &size, &INT_ONE, &s_minus1, | ||
| 1373 | &stepSize, | ||
| 1374 | kPacked, &size, | ||
| 1375 | ✗ | &nls->tabl->A[s_minus1 * nls->tabl->nStages], &INT_ONE, | |
| 1376 | &DBL_ONE, | ||
| 1377 | ✗ | &x[size * s_minus1], &size); | |
| 1378 | |||
| 1379 | ✗ | gbInternal_T_Transform_set_states(data, gbData, nls, &x[size * s_minus1], s_minus1); | |
| 1380 | ✗ | ret = gbode_fODE(data, threadData, &stats->nCallsODE, selection); | |
| 1381 | ✗ | if (ret < 0) return NLS_FAILED; | |
| 1382 | |||
| 1383 | // k_s = f(t + h * c_s, x0 + h * sum{j=1}^{s-1} A_{s, j} * k_j) | ||
| 1384 | ✗ | gbInternal_T_Transform_copy_full_to_fast(gbData, nls, &data->localData[0]->realVars[gbData->nStates], &kPacked[size * s_minus1]); | |
| 1385 | } | ||
| 1386 | |||
| 1387 | ✗ | return NLS_SOLVED; | |
| 1388 | } | ||
| 1389 | |||
| 1390 | // Newton failed -> iteration limit exceeded or too slow convergence | ||
| 1391 | ✗ | if (newt_it == nls->max_newton_it || (pow(theta, nls->max_newton_it - newt_it) / (1 - theta) * nrm_delta > nls->fnewt)) | |
| 1392 | { | ||
| 1393 | ✗ | nls->call_jac = TRUE; | |
| 1394 | ✗ | return NLS_FAILED; | |
| 1395 | } | ||
| 1396 | } | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | /* Allocate the internal memory + do symbolic analysis. */ | ||
| 1400 | ✗ | void *gbInternalNlsAllocate(int size, | |
| 1401 | NLS_USERDATA* userData, | ||
| 1402 | modelica_boolean attemptRetry, | ||
| 1403 | modelica_boolean isFast) | ||
| 1404 | { | ||
| 1405 | ✗ | DATA_GBODE *gbData = isFast ? NULL : (DATA_GBODE *) userData->solverData; | |
| 1406 | ✗ | DATA_GBODEF *gbfData = isFast ? (DATA_GBODEF *) userData->solverData : NULL; | |
| 1407 | ✗ | BUTCHER_TABLEAU *tabl = isFast ? gbfData->tableau : gbData->tableau; | |
| 1408 | ✗ | T_TRANSFORM *transform = tabl->t_transform; | |
| 1409 | ✗ | JACOBIAN* jacobian_ODE = getSymbolicOdeJacobian(userData->data); | |
| 1410 | |||
| 1411 | ✗ | GB_INTERNAL_NLS_DATA *nls = (GB_INTERNAL_NLS_DATA *) malloc(sizeof(GB_INTERNAL_NLS_DATA)); | |
| 1412 | ✗ | gbInternal_KLU_initialize(&nls->klu, transform ? transform->nRealEigenvalues : 1, transform ? transform->nComplexEigenpairs : 0); | |
| 1413 | |||
| 1414 | // multirate stuff | ||
| 1415 | ✗ | nls->multirate = isFast; | |
| 1416 | ✗ | nls->new_fast_states = FALSE; | |
| 1417 | |||
| 1418 | // to have sufficiently large buffers, we need an overestimate for the system of the form struct(I + J) * v = b | ||
| 1419 | unsigned int nls_nnz_estimate = 0; | ||
| 1420 | |||
| 1421 | ✗ | nls->nls_user_data = userData; | |
| 1422 | ✗ | nls->size = jacobian_ODE->sizeRows; | |
| 1423 | ✗ | nls->jacobian_callback = (double *) malloc(jacobian_ODE->sparsePattern->nnz * sizeof(double)); | |
| 1424 | ✗ | nls->ode_to_nls = (int *) malloc(jacobian_ODE->sparsePattern->nnz * sizeof(int)); | |
| 1425 | ✗ | nls->nls_diag_indices = (int *) malloc(jacobian_ODE->sizeRows * sizeof(int)); | |
| 1426 | |||
| 1427 | ✗ | nls->tabl = tabl; | |
| 1428 | ✗ | nls->use_t_transform = (transform != NULL); | |
| 1429 | |||
| 1430 | ✗ | SPARSE_PATTERN *odePattern = isFast ? gbfData->sparsePattern_ODE : getJacobianCscPattern(jacobian_ODE); | |
| 1431 | ✗ | SPARSE_PATTERN *nlsPattern = isFast ? gbfData->sparsePattern_NLS : gbData->sparsePattern_NLS; | |
| 1432 | ✗ | assertStreamPrint(NULL, odePattern != NULL && nlsPattern != NULL, "GBODE internal NLS requires sparse patterns."); | |
| 1433 | ✗ | nls_nnz_estimate = nlsPattern->nnz; | |
| 1434 | |||
| 1435 | ✗ | if (!nls->multirate) | |
| 1436 | { | ||
| 1437 | // create ODE Jac -> NLS Jacobian mapping | ||
| 1438 | ✗ | gbodeMapSparsePattern(odePattern, nlsPattern, jacobian_ODE->sizeRows, nls->ode_to_nls, nls->nls_diag_indices); | |
| 1439 | } | ||
| 1440 | |||
| 1441 | ✗ | nls->scal = (double *) malloc(jacobian_ODE->sizeRows * sizeof(double)); | |
| 1442 | ✗ | nls->etas = (double *) malloc(tabl->nStages * sizeof(double)); | |
| 1443 | |||
| 1444 | ✗ | for (int i = 0; i < tabl->nStages; i++) | |
| 1445 | { | ||
| 1446 | ✗ | nls->etas[i] = DBL_MAX; | |
| 1447 | } | ||
| 1448 | |||
| 1449 | ✗ | nls->integrator_tol = userData->data->simulationInfo->tolerance; | |
| 1450 | |||
| 1451 | /* Internal Newton convergence criteria is written in terms of the raw (unscaled), i.e. | ||
| 1452 | user-provided tolerance: || v ||_scal <= alpha, where || v ||_scal = sqrt(1/N sum_i=1^N (v_i / (ATOL_i + |y_i| * RTOL_i))^2), | ||
| 1453 | ATOL and RTOL are unscaled tolerances. */ | ||
| 1454 | const double alpha_default = 3e-2; | ||
| 1455 | const double alpha_maximal = 5e-2; | ||
| 1456 | const double safety_newt = 0.1; | ||
| 1457 | double target_alpha = alpha_default; | ||
| 1458 | |||
| 1459 | ✗ | if (!tabl->richardson && tabl->error_order < tabl->order_b && tabl->order_b - tabl->error_order != 1) | |
| 1460 | { | ||
| 1461 | ✗ | const double order_quot = ((double)tabl->error_order + 1.0) / ((double)tabl->order_b + 1.0); | |
| 1462 | ✗ | target_alpha = pow(safety_newt, 1.0 / order_quot); | |
| 1463 | } | ||
| 1464 | ✗ | nls->fnewt = fmax(DBL_ABSORPTION / nls->integrator_tol, fmin(alpha_maximal, target_alpha)); | |
| 1465 | |||
| 1466 | // damping power for eta | ||
| 1467 | ✗ | if (omc_flag[FLAG_SR_NLS_INTERNAL_DAMPING_FAC]) | |
| 1468 | { | ||
| 1469 | ✗ | double eta_damping = atof(omc_flagValue[FLAG_SR_NLS_INTERNAL_DAMPING_FAC]); | |
| 1470 | |||
| 1471 | ✗ | if (eta_damping > 1.0 || eta_damping < 0.0) | |
| 1472 | { | ||
| 1473 | ✗ | throwStreamPrint(NULL, "Invalid value %1.6e for flag '-gbnls_internal_damping'. Value must be less or equal to 1 and greater or equal to 0.", eta_damping); | |
| 1474 | } | ||
| 1475 | else | ||
| 1476 | { | ||
| 1477 | ✗ | nls->eta_inital_damping = eta_damping; | |
| 1478 | } | ||
| 1479 | } | ||
| 1480 | else | ||
| 1481 | { | ||
| 1482 | ✗ | nls->eta_inital_damping = 0.8; | |
| 1483 | } | ||
| 1484 | |||
| 1485 | // add a history of thetas_last + #newt iterations to detect nearly linear systems, similar to err controller | ||
| 1486 | ✗ | if (omc_flag[FLAG_SR_NLS_INTERNAL_JACKEEP]) | |
| 1487 | { | ||
| 1488 | ✗ | double keep_flag_value = atof(omc_flagValue[FLAG_SR_NLS_INTERNAL_JACKEEP]); | |
| 1489 | ✗ | if (keep_flag_value >= 1.0) | |
| 1490 | { | ||
| 1491 | ✗ | throwStreamPrint(NULL, "Invalid value %1.6e for flag '-gbnls_internal_jackeep'. Value must be strictly less than 1.", keep_flag_value); | |
| 1492 | } | ||
| 1493 | else | ||
| 1494 | { | ||
| 1495 | ✗ | nls->theta_keep = keep_flag_value; | |
| 1496 | } | ||
| 1497 | } | ||
| 1498 | else | ||
| 1499 | { | ||
| 1500 | // heuristic that takes sparsity into account | ||
| 1501 | ✗ | if (nls->size > 8) | |
| 1502 | { | ||
| 1503 | ✗ | nls->theta_keep = pow(10.0, -3.0 + 1.75 * log(1.0 + (double)odePattern->maxColors) / log(1.0 + (double)nls->size)); | |
| 1504 | } | ||
| 1505 | else | ||
| 1506 | { | ||
| 1507 | // for very small systems we can compute the Jacobian frequently | ||
| 1508 | ✗ | nls->theta_keep = 1e-3; | |
| 1509 | } | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | ✗ | nls->call_jac = TRUE; | |
| 1513 | ✗ | nls->theta_divergence = 0.99; | |
| 1514 | ✗ | nls->max_newton_it = !transform ? 5 : 4 + 2 * transform->size; // = 5 for each (E)SDIRK stage and e.g. 10 for full RadauIIA 3-step | |
| 1515 | |||
| 1516 | ✗ | if (!transform) | |
| 1517 | { | ||
| 1518 | ✗ | nls->real_nls_jacs = (double **) malloc(sizeof(double *)); | |
| 1519 | ✗ | nls->real_nls_jacs[0] = (double *) malloc(nls_nnz_estimate * sizeof(double)); | |
| 1520 | |||
| 1521 | // auxiliary memory | ||
| 1522 | ✗ | nls->work = (double *) malloc(4 * nls->size * sizeof(double)); | |
| 1523 | |||
| 1524 | ✗ | if (!nls->multirate) | |
| 1525 | { | ||
| 1526 | ✗ | gbInternal_KLU_analyze(&nls->klu, nls->size, (int *) nlsPattern->leadindex, (int *) nlsPattern->index); | |
| 1527 | } | ||
| 1528 | } | ||
| 1529 | else | ||
| 1530 | { | ||
| 1531 | ✗ | nls->real_nls_jacs = (double **) malloc(transform->nRealEigenvalues * sizeof(double *)); | |
| 1532 | ✗ | nls->real_nls_res = (double **) malloc(transform->nRealEigenvalues * sizeof(double *)); | |
| 1533 | ✗ | nls->cmplx_nls_jacs = (double **) malloc(transform->nComplexEigenpairs * sizeof(double *)); | |
| 1534 | ✗ | nls->cmplx_nls_res = (double **) malloc(transform->nComplexEigenpairs * sizeof(double *)); | |
| 1535 | |||
| 1536 | ✗ | for (int sys_real = 0; sys_real < transform->nRealEigenvalues; sys_real++) | |
| 1537 | { | ||
| 1538 | ✗ | nls->real_nls_res[sys_real] = (double *) malloc(nls->size * sizeof(double)); | |
| 1539 | ✗ | nls->real_nls_jacs[sys_real] = (double *) malloc(nls_nnz_estimate * sizeof(double)); | |
| 1540 | } | ||
| 1541 | ✗ | for (int sys_cmplx = 0; sys_cmplx < transform->nComplexEigenpairs; sys_cmplx++) | |
| 1542 | { | ||
| 1543 | ✗ | nls->cmplx_nls_res[sys_cmplx] = (double *) malloc(2 * nls->size * sizeof(double)); | |
| 1544 | ✗ | nls->cmplx_nls_jacs[sys_cmplx] = (double *) malloc(2 * nls_nnz_estimate * sizeof(double)); | |
| 1545 | } | ||
| 1546 | |||
| 1547 | ✗ | if (!nls->multirate) | |
| 1548 | { | ||
| 1549 | ✗ | gbInternal_KLU_analyze(&nls->klu, nls->size, (int *) nlsPattern->leadindex, (int *) nlsPattern->index); | |
| 1550 | } | ||
| 1551 | |||
| 1552 | // iterate | ||
| 1553 | ✗ | nls->Z = (double *) malloc(nls->size * transform->size * sizeof(double)); | |
| 1554 | ✗ | nls->W = (double *) malloc(nls->size * transform->size * sizeof(double)); | |
| 1555 | |||
| 1556 | // auxiliary memory | ||
| 1557 | ✗ | nls->work = (double *) malloc(nls->size * MAX(transform->size, 4) * sizeof(double)); | |
| 1558 | } | ||
| 1559 | |||
| 1560 | ✗ | return (void *) nls; | |
| 1561 | } | ||
| 1562 | |||
| 1563 | /* Free the internal memory. */ | ||
| 1564 | ✗ | void gbInternalNlsFree(void *nls_ptr) | |
| 1565 | { | ||
| 1566 | GB_INTERNAL_NLS_DATA *nls = (GB_INTERNAL_NLS_DATA *) nls_ptr; | ||
| 1567 | ✗ | gbInternal_KLU_free(&nls->klu); | |
| 1568 | ✗ | free(nls->jacobian_callback); | |
| 1569 | ✗ | free(nls->ode_to_nls); | |
| 1570 | ✗ | free(nls->nls_diag_indices); | |
| 1571 | ✗ | free(nls->scal); | |
| 1572 | ✗ | free(nls->etas); | |
| 1573 | ✗ | free(nls->work); | |
| 1574 | |||
| 1575 | ✗ | if (!nls->tabl->t_transform) | |
| 1576 | { | ||
| 1577 | ✗ | free(nls->real_nls_jacs[0]); | |
| 1578 | ✗ | free(nls->real_nls_jacs); | |
| 1579 | } | ||
| 1580 | else | ||
| 1581 | { | ||
| 1582 | ✗ | for (int sys_real = 0; sys_real < nls->tabl->t_transform->nRealEigenvalues; sys_real++) | |
| 1583 | { | ||
| 1584 | ✗ | free(nls->real_nls_jacs[sys_real]); | |
| 1585 | ✗ | free(nls->real_nls_res[sys_real]); | |
| 1586 | } | ||
| 1587 | ✗ | for (int sys_cmplx = 0; sys_cmplx < nls->tabl->t_transform->nComplexEigenpairs; sys_cmplx++) | |
| 1588 | { | ||
| 1589 | ✗ | free(nls->cmplx_nls_jacs[sys_cmplx]); | |
| 1590 | ✗ | free(nls->cmplx_nls_res[sys_cmplx]); | |
| 1591 | } | ||
| 1592 | |||
| 1593 | ✗ | free(nls->real_nls_jacs); | |
| 1594 | ✗ | free(nls->real_nls_res); | |
| 1595 | ✗ | free(nls->cmplx_nls_jacs); | |
| 1596 | ✗ | free(nls->cmplx_nls_res); | |
| 1597 | |||
| 1598 | ✗ | free(nls->Z); | |
| 1599 | ✗ | free(nls->W); | |
| 1600 | } | ||
| 1601 | |||
| 1602 | ✗ | freeNlsUserData(nls->nls_user_data); | |
| 1603 | ✗ | free(nls); | |
| 1604 | ✗ | } | |
| 1605 | |||
| 1606 | ✗ | void gbInternalScheduleFastStatesUpdate(void *nls_ptr) | |
| 1607 | { | ||
| 1608 | ✗ | assert(((GB_INTERNAL_NLS_DATA *) nls_ptr)->multirate); | |
| 1609 | ✗ | ((GB_INTERNAL_NLS_DATA *) nls_ptr)->new_fast_states = TRUE; | |
| 1610 | ✗ | } | |
| 1611 | |||
| 1612 | ✗ | modelica_boolean updateFastStates(DATA *data, | |
| 1613 | threadData_t *threadData, | ||
| 1614 | NONLINEAR_SYSTEM_DATA* nonlinsys, | ||
| 1615 | DATA_GBODE* gbData, | ||
| 1616 | GB_INTERNAL_NLS_DATA *nls) | ||
| 1617 | { | ||
| 1618 | ✗ | DATA_GBODEF *gbfData = gbData->gbfData; | |
| 1619 | ✗ | SPARSE_PATTERN *odePattern = gbfData->sparsePattern_ODE; | |
| 1620 | ✗ | SPARSE_PATTERN *nlsPattern = gbfData->sparsePattern_NLS; | |
| 1621 | |||
| 1622 | // update size | ||
| 1623 | ✗ | nls->size = gbData->nFastStates; | |
| 1624 | ✗ | nls->call_jac = TRUE; | |
| 1625 | |||
| 1626 | ✗ | for (int stage = 0; stage < gbfData->tableau->nStages; stage++) | |
| 1627 | { | ||
| 1628 | ✗ | nls->etas[stage] = DBL_MAX; | |
| 1629 | } | ||
| 1630 | |||
| 1631 | // update mappings: ode_to_nls and nls_diag_indices | ||
| 1632 | ✗ | gbodeMapSparsePattern(odePattern, nlsPattern, nls->size, nls->ode_to_nls, nls->nls_diag_indices); | |
| 1633 | |||
| 1634 | // all transformed systems have the same sparsity pattern and share one symbolic analysis | ||
| 1635 | ✗ | gbInternal_KLU_reanalyze(&nls->klu, nls->size, (int *) nlsPattern->leadindex, (int *) nlsPattern->index); | |
| 1636 | |||
| 1637 | ✗ | return TRUE; | |
| 1638 | } | ||
| 1639 | |||
| 1640 | /* Entry point for `internal` solve routine: DIRK or FIRK */ | ||
| 1641 | ✗ | NLS_SOLVER_STATUS gbInternalSolveNls(DATA *data, | |
| 1642 | threadData_t *threadData, | ||
| 1643 | NONLINEAR_SYSTEM_DATA* nonlinsys, | ||
| 1644 | DATA_GBODE* gbData, | ||
| 1645 | void *nls_ptr) | ||
| 1646 | { | ||
| 1647 | GB_INTERNAL_NLS_DATA *nls = (GB_INTERNAL_NLS_DATA *) nls_ptr; | ||
| 1648 | |||
| 1649 | ✗ | if (nls->new_fast_states) | |
| 1650 | { | ||
| 1651 | // update sparse pattern struct(I + J) and create a new symbolic factorization | ||
| 1652 | ✗ | modelica_boolean success = updateFastStates(data, threadData, nonlinsys, gbData, nls); | |
| 1653 | ✗ | if (!success) return NLS_FAILED; | |
| 1654 | ✗ | nls->new_fast_states = FALSE; | |
| 1655 | } | ||
| 1656 | |||
| 1657 | ✗ | if (nls->use_t_transform) | |
| 1658 | { | ||
| 1659 | ✗ | return gbInternalSolveNls_T_Transform(data, threadData, nonlinsys, gbData, nls); | |
| 1660 | } | ||
| 1661 | else | ||
| 1662 | { | ||
| 1663 | ✗ | return gbInternalSolveNls_DIRK(data, threadData, nonlinsys, gbData, nls); | |
| 1664 | } | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | /** | ||
| 1668 | * @brief Contractive error estimate for stiff problems. (stiffness filter) | ||
| 1669 | * | ||
| 1670 | * Construct an embedded method of order `nStages` for a given collocation method | ||
| 1671 | * with at least one real eigenvalue and uncollocated point 0.0. We exclude | ||
| 1672 | * complex eigenvalues as this work would be even more expensive then. | ||
| 1673 | * | ||
| 1674 | * This estimate is A-stable and of one order higher than the naive embedded method, which | ||
| 1675 | * is crucial for stiff problems. | ||
| 1676 | * | ||
| 1677 | * See notes on struct CONTRACTIVE_DEFECT for more context. | ||
| 1678 | */ | ||
| 1679 | ✗ | void gbInternalContractiveDefect(DATA *data, | |
| 1680 | threadData_t *threadData, | ||
| 1681 | NONLINEAR_SYSTEM_DATA *nonlinsys, | ||
| 1682 | DATA_GBODE *gbData, | ||
| 1683 | CONTRACTIVE_DEFECT *contractive, | ||
| 1684 | double *err) | ||
| 1685 | { | ||
| 1686 | ✗ | GB_INTERNAL_NLS_DATA *nls = (GB_INTERNAL_NLS_DATA *) (((struct dataSolver *)nonlinsys->solverData)->ordinaryData); | |
| 1687 | ✗ | BUTCHER_TABLEAU *tabl = nls->tabl; | |
| 1688 | ✗ | SOLVERSTATS *stats = (nls->multirate ? &gbData->gbfData->stats : &gbData->stats); | |
| 1689 | |||
| 1690 | ✗ | int nStates = gbData->nStates; | |
| 1691 | ✗ | int size = nls->size; | |
| 1692 | ✗ | int nStages = tabl->nStages; | |
| 1693 | |||
| 1694 | ✗ | double *yOld = (nls->multirate ? gbData->gbfData->yOldPacked : gbData->yOld); | |
| 1695 | ✗ | double *kPacked = (nls->multirate ? gbData->gbfData->kCurrPacked : gbData->k); | |
| 1696 | |||
| 1697 | // ERR := -d(0)^T * A * k | ||
| 1698 | ✗ | dgemm_(&CHAR_NO_TRANS, &CHAR_NO_TRANS, | |
| 1699 | &size, | ||
| 1700 | &INT_ONE, | ||
| 1701 | &nStages, | ||
| 1702 | &DBL_MINUS_ONE, kPacked, &size, | ||
| 1703 | ✗ | contractive->dT_A, &nStages, | |
| 1704 | &DBL_ZERO, err, &size); | ||
| 1705 | |||
| 1706 | ✗ | modelica_boolean sr_valid = (!nls->multirate && !gbData->didFastStep && gbData->time != data->simulationInfo->startTime && !gbData->eventHappened && gbData->extrapolationBaseTime != INFINITY); | |
| 1707 | ✗ | modelica_boolean mr_valid = (nls->multirate && gbData->didFastStep && gbData->gbfData->extrapolationValid); | |
| 1708 | |||
| 1709 | ✗ | if (tabl->isKRightAvailable && (sr_valid || mr_valid)) | |
| 1710 | ✗ | { | |
| 1711 | // f(t_n, y(t_n)) == k_right == f(t_n-1 + h_n-1, y(t_n-1 + h_n-1)) of the previous step up to NLS precision | ||
| 1712 | // similar to the FSAL property of ESDIRK or ERK, but the method by itself does not have c_0 = 0 as a node! | ||
| 1713 | // Note that the order of this k_right is the order p of the method (e.g. p = 2s-1 for Radau IIA), since kRight is a collocated node | ||
| 1714 | // of the previous interval. Therefore, the computed defect will still be O(h^s) even though f(x0, y0) = k_right + O(h^p) | ||
| 1715 | ✗ | double *k0Packed = (nls->multirate ? &gbData->gbfData->kLast[(tabl->nStages - 1) * gbData->nFastStates] | |
| 1716 | ✗ | : &gbData->kLast[(tabl->nStages - 1) * gbData->nStates]); | |
| 1717 | |||
| 1718 | // ERR := f(t_n, y(t_n)) - d(0)^T * A * k | ||
| 1719 | ✗ | daxpy_(&nls->size, &DBL_ONE, k0Packed, &INT_ONE, err, &INT_ONE); | |
| 1720 | } | ||
| 1721 | else | ||
| 1722 | { | ||
| 1723 | // fresh computation of f(t_n, y(t_n)) as previous step is not valid or method does not collocate node 1 | ||
| 1724 | ✗ | gbInternal_T_Transform_set_states(data, gbData, nls, yOld, GB_INTERNAL_LEFT_BOUNDARY); | |
| 1725 | ✗ | gbode_fODE(data, threadData, &stats->nCallsODE, (nls->multirate ? gbData->gbfData->evalSelectionFast : NULL)); | |
| 1726 | |||
| 1727 | // ERR := f(t_n, y(t_n)) - d(0)^T * A * k | ||
| 1728 | ✗ | gbInternal_T_Transform_full_to_fast_axpy(gbData, nls, DBL_ONE, &data->localData[0]->realVars[nStates], err); | |
| 1729 | } | ||
| 1730 | |||
| 1731 | // ERR := (gamma / h * I - J)^{-1} * yt = (gamma / h * I - J)^{-1} * (f(t_n, y(t_n)) - d(0)^T * A * k) (exact error measure) | ||
| 1732 | ✗ | gbInternal_dKLU_solve(&nls->klu, 0, size, err); | |
| 1733 | ✗ | } | |
| 1734 | |||
| 1735 | ✗ | static void gbInternalContractiveFilterPacked(GB_INTERNAL_NLS_DATA *nls, | |
| 1736 | DATA_GBODE *gbData, | ||
| 1737 | double *err) | ||
| 1738 | { | ||
| 1739 | ✗ | int size = nls->size; | |
| 1740 | ✗ | double filter_scale = 1.0; | |
| 1741 | |||
| 1742 | ✗ | if (nls->use_t_transform) | |
| 1743 | { | ||
| 1744 | ✗ | double stepSize = nls->multirate ? gbData->gbfData->stepSize : gbData->stepSize; | |
| 1745 | ✗ | filter_scale = nls->tabl->t_transform->gamma[0] / stepSize; | |
| 1746 | } | ||
| 1747 | |||
| 1748 | // DIRK systems use h*gamma*J - I, so the solve already applies the filter up to sign. | ||
| 1749 | // FIRK/T systems use gamma/h*I - J = gamma/h * (I - h/gamma*J), so scale by gamma/h after the solve. | ||
| 1750 | ✗ | gbInternal_dKLU_solve(&nls->klu, 0, size, err); | |
| 1751 | ✗ | if (filter_scale != 1.0) dscal_(&size, &filter_scale, err, &INT_ONE); | |
| 1752 | ✗ | } | |
| 1753 | |||
| 1754 | ✗ | void gbInternalContractiveFilterError(NONLINEAR_SYSTEM_DATA *nonlinsys, | |
| 1755 | DATA_GBODE *gbData, | ||
| 1756 | double *err) | ||
| 1757 | { | ||
| 1758 | ✗ | GB_INTERNAL_NLS_DATA *nls = (GB_INTERNAL_NLS_DATA *) (((struct dataSolver *)nonlinsys->solverData)->ordinaryData); | |
| 1759 | |||
| 1760 | ✗ | if (!nls->multirate) | |
| 1761 | { | ||
| 1762 | ✗ | gbInternalContractiveFilterPacked(nls, gbData, err); | |
| 1763 | } | ||
| 1764 | else | ||
| 1765 | { | ||
| 1766 | ✗ | double *work = nls->work; | |
| 1767 | |||
| 1768 | // work := fast(err) | ||
| 1769 | ✗ | gbInternal_T_Transform_copy_full_to_fast(gbData, nls, err, work); | |
| 1770 | ✗ | gbInternalContractiveFilterPacked(nls, gbData, work); | |
| 1771 | |||
| 1772 | // err := full(work) | ||
| 1773 | ✗ | gbInternal_T_Transform_copy_fast_to_full(gbData, nls, work, err); | |
| 1774 | } | ||
| 1775 | ✗ | } | |
| 1776 | |||
| 1777 | // returns a work pointer of at least 32 * N_STATES bytes == 4 * N_STATES * sizeof(double) | ||
| 1778 | ✗ | double *gbInternalGetWorkPointer(void *nls_ptr) | |
| 1779 | { | ||
| 1780 | ✗ | return ((GB_INTERNAL_NLS_DATA *) nls_ptr)->work; | |
| 1781 | } | ||
| 1782 | |||
| 1783 | /** | ||
| 1784 | * @brief Perform intrastep stage-value-prediction (linear combination of known stage values k). | ||
| 1785 | * | ||
| 1786 | * Calculates y_predictor := y0 + h * A_predictor[1] * k[1] + A_predictor[2] * k[2] + ... + A_predictor[s-1] * k[s-1]), | ||
| 1787 | * where values of A_predictor are choosen such that order and stability properties are nice. | ||
| 1788 | * | ||
| 1789 | * @note This function is located in gbode_internal_nls.c as we get symbol conflicts with | ||
| 1790 | * SUNDIALS BLAS symbols that use long int as index type and are included transitively. | ||
| 1791 | */ | ||
| 1792 | ✗ | void gbInternalLinearCombinationSVP(STAGE_VALUE_PREDICTORS *svp, | |
| 1793 | int active_stage, | ||
| 1794 | int nStates, | ||
| 1795 | double stepSize, | ||
| 1796 | const double *K, | ||
| 1797 | const double *y0, | ||
| 1798 | double *ypred) | ||
| 1799 | { | ||
| 1800 | ✗ | memcpy(ypred, y0, nStates * sizeof(double)); | |
| 1801 | |||
| 1802 | ✗ | dgemv_( | |
| 1803 | &CHAR_NO_TRANS, | ||
| 1804 | &nStates, | ||
| 1805 | &active_stage, | ||
| 1806 | &stepSize, K, &nStates, | ||
| 1807 | ✗ | &svp->A_predictor[active_stage * svp->nStages], &INT_ONE, | |
| 1808 | &DBL_ONE, ypred, &INT_ONE | ||
| 1809 | ); | ||
| 1810 | ✗ | } | |
| 1811 |