OMCompiler/SimulationRuntime/c/simulation/solver/gbode_main.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 | /*! \file gbode_main.c | ||
| 29 | * Implementation of a generic (implicit and explicit) Runge Kutta solver, which works for any | ||
| 30 | * order and stage based on a provided Butcher tableau. Utilizes the sparsity pattern of the ODE | ||
| 31 | * together with the KINSOL / KLU solver | ||
| 32 | * | ||
| 33 | * \author bbachmann | ||
| 34 | */ | ||
| 35 | |||
| 36 | #include <time.h> | ||
| 37 | |||
| 38 | #include "gbode_main.h" | ||
| 39 | #include "gbode_util.h" | ||
| 40 | |||
| 41 | #include "gbode_conf.h" | ||
| 42 | #include "gbode_ctrl.h" | ||
| 43 | #include "gbode_err.h" | ||
| 44 | #include "gbode_events.h" | ||
| 45 | #include "gbode_nls.h" | ||
| 46 | #include "gbode_internal_nls.h" | ||
| 47 | #include "gbode_sparse.h" | ||
| 48 | #include "gbode_step.h" | ||
| 49 | #include "gbode_util.h" | ||
| 50 | |||
| 51 | #include <float.h> | ||
| 52 | #include <math.h> | ||
| 53 | #include <string.h> | ||
| 54 | |||
| 55 | #include "../arrayIndex.h" | ||
| 56 | #include "external_input.h" | ||
| 57 | #include "kinsolSolver.h" | ||
| 58 | #include "kinsol_b.h" | ||
| 59 | #include "newtonIteration.h" | ||
| 60 | #include "nonlinearSystem.h" | ||
| 61 | #include "omc_math.h" | ||
| 62 | #include "../options.h" | ||
| 63 | #include "../results/simulation_result.h" | ||
| 64 | #include "../jacobian_util.h" | ||
| 65 | #include "../../util/omc_error.h" | ||
| 66 | #include "../../util/omc_file.h" | ||
| 67 | #include "../../util/simulation_options.h" | ||
| 68 | #include "epsilon.h" | ||
| 69 | |||
| 70 | extern void communicateStatus(const char *phase, double completionPercent, double currentTime, double currentStepSize); | ||
| 71 | |||
| 72 | // TODO: we should add proper return handling of callbacks: ODE, Jacobian, Zero-Crossings, etc. | ||
| 73 | // TODO: make the interface between fast steps and slow steps more clear: It would be best to have one central function, which | ||
| 74 | // copies the required fields from fast -> slow | ||
| 75 | |||
| 76 | /** | ||
| 77 | * @brief Calculate function values of function ODE f(t,y). | ||
| 78 | * | ||
| 79 | * Assuming the correct values for time value and states are set. | ||
| 80 | * | ||
| 81 | * @param data Runtime data struct. | ||
| 82 | * @param threadData Thread data for error handling. | ||
| 83 | * @param counter Counter for function calls. Incremented by 1. | ||
| 84 | * @param selection Equations to evaluate. | ||
| 85 | */ | ||
| 86 | ✗ | int gbode_fODE(DATA *data, threadData_t *threadData, unsigned int* counter, EVAL_SELECTION* selection) | |
| 87 | { | ||
| 88 | int ret = -1; | ||
| 89 | /* try */ | ||
| 90 | #if !defined(OMC_EMCC) | ||
| 91 | ✗ | OMC_TRY_INTERNAL(simulationJumpBuffer) | |
| 92 | #endif | ||
| 93 | |||
| 94 | ✗ | if (counter) | |
| 95 | { | ||
| 96 | ✗ | (*counter)++; | |
| 97 | } | ||
| 98 | |||
| 99 | ✗ | externalInputUpdate(data); | |
| 100 | ✗ | data->callback->input_function(data, threadData); | |
| 101 | |||
| 102 | ✗ | data->simulationInfo->evalSelection = selection; | |
| 103 | ✗ | data->callback->functionODE(data, threadData); | |
| 104 | |||
| 105 | ✗ | if (OMC_ERROR_RAISED()) { OMC_ERROR_CLEAR(); } else { ret = 0; } | |
| 106 | |||
| 107 | #if !defined(OMC_EMCC) | ||
| 108 | ✗ | OMC_CATCH_INTERNAL(simulationJumpBuffer) | |
| 109 | #endif | ||
| 110 | |||
| 111 | ✗ | return ret; | |
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * @brief Get the Jacobian method GBODE can use for the given non-linear solver. | ||
| 116 | * | ||
| 117 | * The adjoint and bidirectional evaluation modes produce a full Jacobian matrix in one | ||
| 118 | * go. GBODE's KINSOL and Newton non-linear solvers instead request single columns of the | ||
| 119 | * ODE Jacobian (see jacobian_SR_column() and friends in gbode_nls.c, | ||
| 120 | * These could be adapted of course aswell to allow adjoints), which is a genuine | ||
| 121 | * solver API restriction. Only the internal non-linear solver evaluates the whole ODE | ||
| 122 | * Jacobian at once and can therefore use all evaluation directions. | ||
| 123 | * | ||
| 124 | * @param threadData Used for error handling. | ||
| 125 | * @param nlsSolverMethod Non-linear solver method used by GBODE. | ||
| 126 | * @return JACOBIAN_METHOD Requested method, downgraded to the default if unusable. | ||
| 127 | */ | ||
| 128 | ✗ | static JACOBIAN_METHOD getGbodeJacobianMethod(threadData_t* threadData, enum GB_NLS_METHOD nlsSolverMethod) | |
| 129 | { | ||
| 130 | ✗ | JACOBIAN_METHOD jacobianMethod = getRequestedJacobianMethod(threadData); | |
| 131 | |||
| 132 | /* non-internal non-linear solvers cannot use the adjoint or bidirectional Jacobian evaluation methods, | ||
| 133 | * because they only request single columns of the ODE Jacobian and not the whole matrix at once. */ | ||
| 134 | ✗ | if ((jacobianMethod == COLOREDSYMJACADJ || jacobianMethod == BICOLOREDSYMJAC) | |
| 135 | ✗ | && nlsSolverMethod != GB_NLS_INTERNAL) { | |
| 136 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Jacobian method %s requires the internal non-linear solver of GBODE. " | |
| 137 | "Use `-gbnls=internal` / `-gbfnls=internal`. " | ||
| 138 | "Switching to the forward symbolic Jacobian.", | ||
| 139 | JACOBIAN_METHOD_NAME[jacobianMethod]); | ||
| 140 | jacobianMethod = JAC_UNKNOWN; | ||
| 141 | } | ||
| 142 | |||
| 143 | ✗ | return jacobianMethod; | |
| 144 | } | ||
| 145 | |||
| 146 | /** | ||
| 147 | * @brief Function allocates memory needed for chosen gbodef method. | ||
| 148 | * | ||
| 149 | * @param data Runtime data struct. | ||
| 150 | * @param threadData Thread data for error handling. | ||
| 151 | * @param solverInfo Information about main solver. | ||
| 152 | * @return int Return 0 on success, -1 on failure. | ||
| 153 | */ | ||
| 154 | ✗ | int gbodef_allocateData(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, DATA_GBODE *gbData) | |
| 155 | { | ||
| 156 | ✗ | DATA_GBODEF *gbfData = (DATA_GBODEF *)calloc(1, sizeof(DATA_GBODEF)); | |
| 157 | ✗ | gbData->gbfData = gbfData; | |
| 158 | |||
| 159 | JACOBIAN *jacobian = NULL; | ||
| 160 | int i; | ||
| 161 | |||
| 162 | ✗ | gbfData->nStates = gbData->nStates; | |
| 163 | |||
| 164 | ✗ | gbfData->GM_method = getGB_method(FLAG_MR); | |
| 165 | ✗ | gbfData->tableau = initButcherTableau(gbfData->GM_method, FLAG_MR_ERR); | |
| 166 | ✗ | if (gbfData->tableau == NULL) { | |
| 167 | // ERROR | ||
| 168 | ✗ | messageClose(OMC_LOG_STDOUT); // FIXME what does this belong to? | |
| 169 | ✗ | omc_throw_function(threadData); | |
| 170 | } | ||
| 171 | |||
| 172 | // Get size of non-linear system | ||
| 173 | ✗ | analyseButcherTableau(gbfData->tableau, gbData->nStates, &gbfData->nlSystemSize, &gbfData->type); | |
| 174 | |||
| 175 | ✗ | if (gbfData->GM_method == MS_ADAMS_MOULTON) { | |
| 176 | ✗ | gbfData->nlSystemSize = gbData->nStates; | |
| 177 | ✗ | gbfData->step_fun = &(full_implicit_MS_MR); | |
| 178 | ✗ | gbfData->type = MS_TYPE_IMPLICIT; | |
| 179 | ✗ | gbfData->isExplicit = FALSE; | |
| 180 | } | ||
| 181 | |||
| 182 | ✗ | switch (gbfData->type) | |
| 183 | { | ||
| 184 | ✗ | case GM_TYPE_EXPLICIT: | |
| 185 | ✗ | gbfData->isExplicit = TRUE; | |
| 186 | ✗ | gbfData->step_fun = &(expl_diag_impl_RK_MR); | |
| 187 | ✗ | break; | |
| 188 | ✗ | case GM_TYPE_DIRK: | |
| 189 | ✗ | gbfData->isExplicit = FALSE; | |
| 190 | ✗ | gbfData->step_fun = &(expl_diag_impl_RK_MR); | |
| 191 | ✗ | break; | |
| 192 | ✗ | case MS_TYPE_IMPLICIT: | |
| 193 | ✗ | gbfData->isExplicit = FALSE; | |
| 194 | ✗ | gbfData->step_fun = &(full_implicit_MS_MR); | |
| 195 | ✗ | break; | |
| 196 | ✗ | case GM_TYPE_IMPLICIT: | |
| 197 | ✗ | if (getGB_NLS_method(FLAG_MR_NLS) != GB_NLS_INTERNAL) | |
| 198 | { | ||
| 199 | ✗ | throwStreamPrint(NULL, "Unsupported configuration: fully implicit Runge-Kutta multirate integration is only available with -gbnls=internal."); | |
| 200 | } | ||
| 201 | ✗ | gbfData->isExplicit = FALSE; | |
| 202 | ✗ | gbfData->step_fun = &(full_implicit_RK_MR); | |
| 203 | ✗ | break; | |
| 204 | ✗ | default: | |
| 205 | ✗ | throwStreamPrint(NULL, "Not handled case for Runge-Kutta method %i", gbfData->type); | |
| 206 | } | ||
| 207 | |||
| 208 | ✗ | gbfData->nlsSolverMethod = gbfData->isExplicit ? GB_NLS_UNKNOWN : getGB_NLS_method(FLAG_MR_NLS); | |
| 209 | ✗ | finalizeButcherTableauError(gbfData->tableau, gbfData->nlsSolverMethod); | |
| 210 | |||
| 211 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Step control factor is set to %g", gbfData->tableau->fac); | |
| 212 | |||
| 213 | ✗ | gbfData->ctrl_method = getControllerMethod(FLAG_MR_CTRL); | |
| 214 | ✗ | if (gbfData->ctrl_method == GB_CTRL_CNST) { | |
| 215 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Constant step size not supported for inner integration. Using IController."); | |
| 216 | ✗ | gbfData->ctrl_method = GB_CTRL_I; | |
| 217 | } | ||
| 218 | ✗ | gbfData->currentErrorOrder = gbfData->tableau->error_order; | |
| 219 | |||
| 220 | // allocate memory for the generic RK method | ||
| 221 | ✗ | gbfData->y = malloc(gbData->nStates*sizeof(double)); | |
| 222 | ✗ | gbfData->yOld = malloc(gbData->nStates*sizeof(double)); | |
| 223 | ✗ | gbfData->yt = malloc(gbData->nStates*sizeof(double)); | |
| 224 | ✗ | gbfData->y1 = malloc(gbData->nStates*sizeof(double)); | |
| 225 | ✗ | gbfData->f = malloc(gbData->nStates*sizeof(double)); | |
| 226 | ✗ | gbfData->yLast = malloc(gbData->nStates*sizeof(double)); | |
| 227 | ✗ | gbfData->yOldPacked = malloc(gbData->nStates*sizeof(double)); | |
| 228 | ✗ | gbfData->kLast = malloc(gbData->nStates*gbfData->tableau->nStages*sizeof(double)); | |
| 229 | ✗ | gbfData->kCurrPacked = malloc(gbData->nStates*gbfData->tableau->nStages*sizeof(double)); | |
| 230 | ✗ | gbfData->k = malloc(gbData->nStates*gbfData->tableau->nStages*sizeof(double)); | |
| 231 | ✗ | gbfData->x = malloc(gbData->nStates*gbfData->tableau->nStages*sizeof(double)); | |
| 232 | ✗ | gbfData->yLeft = malloc(gbData->nStates*sizeof(double)); | |
| 233 | ✗ | gbfData->kLeft = malloc(gbData->nStates*sizeof(double)); | |
| 234 | ✗ | gbfData->yRight = malloc(gbData->nStates*sizeof(double)); | |
| 235 | ✗ | gbfData->kRight = malloc(gbData->nStates*sizeof(double)); | |
| 236 | ✗ | gbfData->res_const = malloc(gbData->nStates*sizeof(double)); | |
| 237 | ✗ | gbfData->errest = malloc(gbData->nStates*sizeof(double)); | |
| 238 | ✗ | gbfData->errtol = malloc(gbData->nStates*sizeof(double)); | |
| 239 | ✗ | gbfData->err = malloc(gbData->nStates*sizeof(double)); | |
| 240 | ✗ | gbfData->ringBufferSize = 4; | |
| 241 | ✗ | gbfData->errValues = calloc(gbfData->ringBufferSize, sizeof(double)); | |
| 242 | ✗ | gbfData->stepSizeValues = malloc(gbfData->ringBufferSize*sizeof(double)); | |
| 243 | ✗ | gbfData->tv = malloc(gbfData->ringBufferSize*sizeof(double)); | |
| 244 | ✗ | gbfData->yv = malloc(gbData->nStates*gbfData->ringBufferSize*sizeof(double)); | |
| 245 | ✗ | gbfData->kv = malloc(gbData->nStates*gbfData->ringBufferSize*sizeof(double)); | |
| 246 | ✗ | gbfData->slowStateCache = slowStateCache_alloc(gbfData->tableau->nStages, gbData->nStates, gbfData->tableau->c); | |
| 247 | |||
| 248 | ✗ | gbfData->extrapolationBaseTime = INFINITY; | |
| 249 | ✗ | gbfData->extrapolationStepSize = 0.0; | |
| 250 | ✗ | gbfData->extrapolationValid = FALSE; | |
| 251 | |||
| 252 | ✗ | gbData->nFastStates = 0; | |
| 253 | ✗ | gbData->nSlowStates = gbData->nFastStates; | |
| 254 | ✗ | gbfData->fastStates_old = malloc(gbData->nStates*sizeof(int)); | |
| 255 | ✗ | gbfData->nFastStates_old = gbData->nFastStates; | |
| 256 | ✗ | for (int i = 0; i < gbData->nStates; i++) { | |
| 257 | ✗ | gbfData->fastStates_old[i] = i; | |
| 258 | } | ||
| 259 | |||
| 260 | ✗ | printButcherTableau(gbfData->tableau); | |
| 261 | |||
| 262 | /* get DAG for functionODE */ | ||
| 263 | ✗ | data->callback->getDAG_ODE(data, threadData); | |
| 264 | /* allocate selective RHS evaluation */ | ||
| 265 | ✗ | gbfData->evalSelectionFast = allocEvalSelection(data->modelData->dag); | |
| 266 | |||
| 267 | /* initialize analytic Jacobian, if available and needed */ | ||
| 268 | ✗ | if (!gbfData->isExplicit) { | |
| 269 | // Allocate Jacobian, if !gbfData->isExplcit and gbData->isExplicit | ||
| 270 | // Free is done in gbode_freeData | ||
| 271 | jacobian = &(data->simulationInfo->analyticJacobians[data->callback->INDEX_JAC_A]); | ||
| 272 | ✗ | if (gbData->isExplicit) { | |
| 273 | ✗ | JACOBIAN_METHOD jacobianMethod = getGbodeJacobianMethod(threadData, gbfData->nlsSolverMethod); | |
| 274 | /* GBODE always needs the forward Jacobian A for its evaluation DAG and the | ||
| 275 | * multi-rate path so set requireForwardJacobian=TRUE. */ | ||
| 276 | ✗ | jacobian = initSymbolicOdeJacobian(data, threadData, &jacobianMethod, TRUE); | |
| 277 | ✗ | if (jacobian->availability != JACOBIAN_AVAILABLE && jacobian->availability != JACOBIAN_ONLY_SPARSITY) { | |
| 278 | ✗ | throwStreamPrint(threadData, "##GBODE## Implicit method requires a sparse pattern for the jacobian but no sparse pattern is generated."); | |
| 279 | } | ||
| 280 | |||
| 281 | ✗ | gbfData->symJacAvailable = jacobian->availability == JACOBIAN_AVAILABLE; | |
| 282 | // change GBODE specific jacobian method | ||
| 283 | ✗ | if (jacobianMethod == SYMJAC) { | |
| 284 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Symbolic Jacobians without coloring are currently not supported by GBODE." | |
| 285 | " Colored symbolical Jacobian will be used."); | ||
| 286 | ✗ | } else if(jacobianMethod == NUMJAC || jacobianMethod == COLOREDNUMJAC || jacobianMethod == INTERNALNUMJAC) { | |
| 287 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Numerical Jacobians without coloring are currently not supported by GBODE." | |
| 288 | " Colored numerical Jacobian will be used."); | ||
| 289 | ✗ | gbfData->symJacAvailable = FALSE; | |
| 290 | } | ||
| 291 | } else { | ||
| 292 | ✗ | gbfData->symJacAvailable = gbData->symJacAvailable; | |
| 293 | ✗ | jacobian = getSymbolicOdeJacobian(data); | |
| 294 | } | ||
| 295 | /* The evaluation DAG is generated and consumed for the forward Jacobian A, | ||
| 296 | * even when the selected Jacobian evaluates adjoint directions. */ | ||
| 297 | ✗ | JACOBIAN* forwardJacobian = &(data->simulationInfo->analyticJacobians[data->callback->INDEX_JAC_A]); | |
| 298 | ✗ | if (forwardJacobian->availability == JACOBIAN_AVAILABLE) { | |
| 299 | ✗ | data->callback->getDAG_JacA(data, threadData, forwardJacobian); | |
| 300 | } | ||
| 301 | ✗ | if (!forwardJacobian->dag) { | |
| 302 | ✗ | throwStreamPrint(threadData, | |
| 303 | "Cannot create multirate data structures without a valid Jacobian DAG. Use a symbolic Jacobian " | ||
| 304 | "(--generateDynamicJacobian=symbolic), an explicit integrator, or switch to single-rate integration."); | ||
| 305 | } | ||
| 306 | |||
| 307 | ✗ | initializeSparsePattern_GBODEF(data, gbfData); | |
| 308 | |||
| 309 | /* Initialize data for the nonlinear solver */ | ||
| 310 | ✗ | gbfData->nlsData = initRK_NLS_DATA_MR(data, threadData, gbfData); | |
| 311 | ✗ | if (!gbfData->nlsData) { | |
| 312 | return -1; | ||
| 313 | } | ||
| 314 | } else { | ||
| 315 | ✗ | gbfData->symJacAvailable = FALSE; | |
| 316 | ✗ | gbfData->nlsSolverMethod = GB_NLS_UNKNOWN; | |
| 317 | ✗ | gbfData->nlsData = NULL; | |
| 318 | ✗ | gbfData->jacobian = NULL; | |
| 319 | } | ||
| 320 | |||
| 321 | ✗ | gbfData->interpolation = getInterpolationMethod(FLAG_MR_INT); | |
| 322 | ✗ | if (!gbfData->tableau->withDenseOutput) { | |
| 323 | ✗ | if (gbfData->interpolation == GB_DENSE_OUTPUT) gbfData->interpolation = GB_INTERPOL_HERMITE; | |
| 324 | } | ||
| 325 | ✗ | switch (gbfData->interpolation) | |
| 326 | { | ||
| 327 | ✗ | case GB_INTERPOL_LIN: | |
| 328 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Linear interpolation is used for emitting results"); | |
| 329 | ✗ | break; | |
| 330 | ✗ | case GB_INTERPOL_HERMITE: | |
| 331 | case GB_INTERPOL_HERMITE_a: | ||
| 332 | case GB_INTERPOL_HERMITE_b: | ||
| 333 | case GB_INTERPOL_HERMITE_ERRCTRL: | ||
| 334 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Hermite interpolation is used for the slow states"); | |
| 335 | ✗ | break; | |
| 336 | ✗ | case GB_DENSE_OUTPUT: | |
| 337 | case GB_DENSE_OUTPUT_ERRCTRL: | ||
| 338 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Dense output is used for emitting results"); | |
| 339 | ✗ | break; | |
| 340 | ✗ | default: | |
| 341 | ✗ | throwStreamPrint(NULL, "Unhandled interpolation case."); | |
| 342 | } | ||
| 343 | |||
| 344 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 345 | enum { bufSize = 4096 }; | ||
| 346 | char filename[bufSize]; | ||
| 347 | ✗ | snprintf(filename, bufSize, "%s_ActiveStates.txt", data->modelData->modelFilePrefix); | |
| 348 | ✗ | gbfData->fastStatesDebugFile = omc_fopen(filename, "w"); | |
| 349 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "LOG_GBODE_STATES sets -noEquidistantTimeGrid for emitting results!"); | |
| 350 | ✗ | solverInfo->solverNoEquidistantGrid = TRUE; | |
| 351 | } else { | ||
| 352 | ✗ | gbfData->fastStatesDebugFile = NULL; | |
| 353 | } | ||
| 354 | ✗ | i = (int) fmin(fmax(round(gbData->nStates * gbData->percentage), 1), gbData->nStates - 1); | |
| 355 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Number of states %d (%d slow states, %d fast states)", gbData->nStates, gbData->nStates-i, i); | |
| 356 | |||
| 357 | /* reset statistics because it is accumulated in solver_main.c */ | ||
| 358 | ✗ | resetSolverStats(&gbfData->stats); | |
| 359 | ✗ | gbfData->fastStateUpdateCount = 0; | |
| 360 | ✗ | gbfData->additionalFullODEEvaluations = 0; | |
| 361 | |||
| 362 | ✗ | return 0; | |
| 363 | } | ||
| 364 | |||
| 365 | /** | ||
| 366 | * @brief Read the states' nominal, min and max attributes into the solver data. | ||
| 367 | * | ||
| 368 | * Expensive scalar queries, so cached. Re-read by updateSolverNominals once | ||
| 369 | * initialization has computed the ones that are parameter expressions. | ||
| 370 | * | ||
| 371 | * @param data Runtime data struct. | ||
| 372 | * @param gbData Runge-Kutta solver data struct. | ||
| 373 | */ | ||
| 374 | ✗ | void gbode_setVarAttributes(DATA* data, DATA_GBODE* gbData) | |
| 375 | { | ||
| 376 | ✗ | for (int i = 0; i < gbData->nStates; i++) { | |
| 377 | ✗ | gbData->nominals[i] = fmax(fabs(getNominalFromScalarIdx(data->simulationInfo, data->modelData, VAR_KIND_STATE, i)), 1e-32); | |
| 378 | ✗ | gbData->mins[i] = getMinFromScalarIdx(data->simulationInfo, data->modelData, VAR_TYPE_REAL, VAR_KIND_STATE, i); | |
| 379 | ✗ | gbData->maxs[i] = getMaxFromScalarIdx(data->simulationInfo, data->modelData, VAR_TYPE_REAL, VAR_KIND_STATE, i); | |
| 380 | } | ||
| 381 | ✗ | } | |
| 382 | |||
| 383 | /** | ||
| 384 | * @brief Function allocates memory needed for generic RK method. | ||
| 385 | * | ||
| 386 | * @param data Runtime data struct. | ||
| 387 | * @param threadData Thread data for error handling. | ||
| 388 | * @param solverInfo Information about main solver. | ||
| 389 | * @return int Return 0 on success, -1 on failure. | ||
| 390 | */ | ||
| 391 | ✗ | int gbode_allocateData(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo) | |
| 392 | { | ||
| 393 | ✗ | DATA_GBODE *gbData = (DATA_GBODE *)calloc(1, sizeof(DATA_GBODE)); | |
| 394 | |||
| 395 | // Set backup in simulationInfo | ||
| 396 | ✗ | data->simulationInfo->backupSolverData = (void *)gbData; | |
| 397 | |||
| 398 | ✗ | solverInfo->solverData = (void *)gbData; | |
| 399 | |||
| 400 | ✗ | gbData->nStates = data->modelData->nStates; | |
| 401 | |||
| 402 | JACOBIAN* jacobian = NULL; | ||
| 403 | |||
| 404 | ✗ | gbData->GM_method = getGB_method(FLAG_SR); | |
| 405 | ✗ | gbData->tableau = initButcherTableau(gbData->GM_method, FLAG_SR_ERR); | |
| 406 | ✗ | if (gbData->tableau == NULL) { | |
| 407 | ✗ | errorStreamPrint(OMC_LOG_STDOUT, 0, "allocateDataGm: Failed to initialize gbode tableau for method %s", GB_METHOD_NAME[gbData->GM_method]); | |
| 408 | ✗ | return -1; | |
| 409 | } | ||
| 410 | |||
| 411 | // Get size of non-linear system | ||
| 412 | ✗ | analyseButcherTableau(gbData->tableau, gbData->nStates, &gbData->nlSystemSize, &gbData->type); | |
| 413 | |||
| 414 | ✗ | switch (gbData->type) { | |
| 415 | ✗ | case GM_TYPE_EXPLICIT: | |
| 416 | ✗ | gbData->isExplicit = TRUE; | |
| 417 | ✗ | gbData->step_fun = &(expl_diag_impl_RK); | |
| 418 | ✗ | break; | |
| 419 | ✗ | case GM_TYPE_DIRK: | |
| 420 | ✗ | gbData->isExplicit = FALSE; | |
| 421 | ✗ | gbData->step_fun = &(expl_diag_impl_RK); | |
| 422 | ✗ | break; | |
| 423 | ✗ | case GM_TYPE_IMPLICIT: | |
| 424 | ✗ | gbData->isExplicit = FALSE; | |
| 425 | ✗ | gbData->step_fun = &(full_implicit_RK); | |
| 426 | ✗ | break; | |
| 427 | ✗ | case MS_TYPE_IMPLICIT: | |
| 428 | ✗ | gbData->isExplicit = FALSE; | |
| 429 | ✗ | gbData->step_fun = &(full_implicit_MS); | |
| 430 | ✗ | break; | |
| 431 | ✗ | default: | |
| 432 | ✗ | throwStreamPrint(NULL, "gbode_allocateData: Unknown type %i", gbData->type); | |
| 433 | } | ||
| 434 | ✗ | if (gbData->GM_method == MS_ADAMS_MOULTON) { | |
| 435 | ✗ | gbData->nlSystemSize = gbData->nStates; | |
| 436 | ✗ | gbData->step_fun = &(full_implicit_MS); | |
| 437 | ✗ | gbData->type = MS_TYPE_IMPLICIT; | |
| 438 | ✗ | gbData->isExplicit = FALSE; | |
| 439 | } | ||
| 440 | |||
| 441 | ✗ | gbData->nlsSolverMethod = gbData->isExplicit ? GB_NLS_UNKNOWN : getGB_NLS_method(FLAG_SR_NLS); | |
| 442 | ✗ | finalizeButcherTableauError(gbData->tableau, gbData->nlsSolverMethod); | |
| 443 | |||
| 444 | // detect controller method | ||
| 445 | ✗ | gbData->ctrl_method = getControllerMethod(FLAG_SR_CTRL); | |
| 446 | ✗ | gbData->currentErrorOrder = gbData->tableau->error_order; | |
| 447 | ✗ | use_fhr = (modelica_boolean) omc_flag[FLAG_SR_CTRL_FHR]; | |
| 448 | ✗ | use_filter = getGBCtrlFilterValue(); | |
| 449 | |||
| 450 | /* define maximum step size gbode is allowed to go */ | ||
| 451 | ✗ | if (omc_flag[FLAG_MAX_STEP_SIZE]) { | |
| 452 | ✗ | gbData->maxStepSize = atof(omc_flagValue[FLAG_MAX_STEP_SIZE]); | |
| 453 | ✗ | if (gbData->maxStepSize < 0 || gbData->maxStepSize > DBL_MAX/2) { | |
| 454 | ✗ | throwStreamPrint(NULL, "maximum step size %g is not allowed", gbData->maxStepSize); | |
| 455 | } else { | ||
| 456 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "maximum step size %g", gbData->maxStepSize); | |
| 457 | } | ||
| 458 | } else { | ||
| 459 | ✗ | gbData->maxStepSize = -1; | |
| 460 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "maximum step size not set"); | |
| 461 | } | ||
| 462 | /* Initial step size */ | ||
| 463 | ✗ | if (omc_flag[FLAG_INITIAL_STEP_SIZE]) { | |
| 464 | ✗ | gbData->initialStepSize = atof(omc_flagValue[FLAG_INITIAL_STEP_SIZE]); | |
| 465 | ✗ | if (gbData->initialStepSize < GB_MINIMAL_STEP_SIZE || gbData->initialStepSize > DBL_MAX/2) { | |
| 466 | ✗ | throwStreamPrint(NULL, "initial step size %g is not allowed, minimal step size is %g", gbData->initialStepSize, GB_MINIMAL_STEP_SIZE); | |
| 467 | } else { | ||
| 468 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "initial step size %g", gbData->initialStepSize); | |
| 469 | } | ||
| 470 | } else { | ||
| 471 | ✗ | gbData->initialStepSize = -1; /* use default */ | |
| 472 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "initial step size not set"); | |
| 473 | } | ||
| 474 | |||
| 475 | /* if FLAG_NO_RESTART is set, configure gbode */ | ||
| 476 | ✗ | gbData->noRestart = omc_flag[FLAG_NO_RESTART]; | |
| 477 | |||
| 478 | ✗ | gbData->eventTime = DBL_MAX; | |
| 479 | |||
| 480 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "gbode performs a restart after an event occurs %s", gbData->noRestart?"NO":"YES"); | |
| 481 | |||
| 482 | ✗ | gbData->isFirstStep = TRUE; | |
| 483 | ✗ | gbData->didFastStep = FALSE; | |
| 484 | ✗ | gbData->eventHappened = FALSE; | |
| 485 | |||
| 486 | /* mark initial extrapolation data as invalid () */ | ||
| 487 | ✗ | gbData->extrapolationBaseTime = INFINITY; | |
| 488 | ✗ | gbData->extrapolationStepSize = 0.0; | |
| 489 | |||
| 490 | /* Allocate internal memory */ | ||
| 491 | ✗ | gbData->y = malloc(sizeof(double) * gbData->nStates); | |
| 492 | ✗ | gbData->yOld = malloc(sizeof(double) * gbData->nStates); | |
| 493 | ✗ | gbData->yLeft = malloc(sizeof(double) * gbData->nStates); | |
| 494 | ✗ | gbData->kLeft = malloc(sizeof(double) * gbData->nStates); | |
| 495 | ✗ | gbData->yRight = malloc(sizeof(double) * gbData->nStates); | |
| 496 | ✗ | gbData->kRight = malloc(sizeof(double) * gbData->nStates); | |
| 497 | ✗ | gbData->kLast = malloc(sizeof(double) * gbData->nStates * gbData->tableau->nStages); | |
| 498 | ✗ | gbData->yLast = malloc(sizeof(double) * gbData->nStates); | |
| 499 | ✗ | gbData->yt = malloc(sizeof(double) * gbData->nStates); | |
| 500 | ✗ | gbData->y1 = malloc(sizeof(double) * gbData->nStates); | |
| 501 | ✗ | gbData->y2 = malloc(sizeof(double) * gbData->nStates); | |
| 502 | ✗ | gbData->f = malloc(sizeof(double) * gbData->nStates); | |
| 503 | ✗ | gbData->k = malloc(sizeof(double) * gbData->nStates * gbData->tableau->nStages); | |
| 504 | ✗ | gbData->x = malloc(sizeof(double) * gbData->nStates * gbData->tableau->nStages); | |
| 505 | ✗ | gbData->res_const = malloc(sizeof(double) * gbData->nStates); | |
| 506 | ✗ | gbData->errest = malloc(sizeof(double) * gbData->nStates); | |
| 507 | ✗ | gbData->errtol = malloc(sizeof(double) * gbData->nStates); | |
| 508 | ✗ | gbData->err = malloc(sizeof(double) * gbData->nStates); | |
| 509 | ✗ | gbData->nominals = malloc(sizeof(double) * gbData->nStates); | |
| 510 | ✗ | gbData->mins = malloc(sizeof(double) * gbData->nStates); | |
| 511 | ✗ | gbData->maxs = malloc(sizeof(double) * gbData->nStates); | |
| 512 | // ring buffer for different purposes (extrapolation, etc.) | ||
| 513 | ✗ | gbData->ringBufferSize = 4; | |
| 514 | ✗ | gbData->errValues = malloc(sizeof(double) * gbData->ringBufferSize); | |
| 515 | ✗ | gbData->stepSizeValues = malloc(sizeof(double) * gbData->ringBufferSize); | |
| 516 | ✗ | gbData->tv = malloc(sizeof(double) * gbData->ringBufferSize); | |
| 517 | ✗ | gbData->yv = malloc(gbData->nStates*sizeof(double) * gbData->ringBufferSize); | |
| 518 | ✗ | gbData->kv = malloc(gbData->nStates*sizeof(double) * gbData->ringBufferSize); | |
| 519 | ✗ | gbData->tr = malloc(sizeof(double) * 2); | |
| 520 | ✗ | gbData->yr = malloc(gbData->nStates*sizeof(double) * 2); | |
| 521 | ✗ | gbData->kr = malloc(gbData->nStates*sizeof(double) * 2); | |
| 522 | |||
| 523 | ✗ | printButcherTableau(gbData->tableau); | |
| 524 | |||
| 525 | ✗ | gbode_setVarAttributes(data, gbData); | |
| 526 | |||
| 527 | /* initialize analytic Jacobian, if available and needed */ | ||
| 528 | ✗ | if (!gbData->isExplicit) { | |
| 529 | ✗ | JACOBIAN_METHOD jacobianMethod = getGbodeJacobianMethod(threadData, gbData->nlsSolverMethod); | |
| 530 | /* GBODE always needs the forward Jacobian A for its evaluation DAG and the | ||
| 531 | * multi-rate path, see gbInternal_evalJacobian() and initRK_NLS_DATA_MR(). */ | ||
| 532 | ✗ | jacobian = initSymbolicOdeJacobian(data, threadData, &jacobianMethod, TRUE); | |
| 533 | ✗ | if (jacobian->availability != JACOBIAN_AVAILABLE && jacobian->availability != JACOBIAN_ONLY_SPARSITY) { | |
| 534 | ✗ | throwStreamPrint(threadData, "##GBODE## Implicit method requires a sparse pattern for the jacobian but no sparse pattern is generated."); | |
| 535 | } | ||
| 536 | |||
| 537 | ✗ | gbData->symJacAvailable = jacobian->availability == JACOBIAN_AVAILABLE; | |
| 538 | // change GBODE specific jacobian method | ||
| 539 | ✗ | if (jacobianMethod == SYMJAC) { | |
| 540 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Symbolic Jacobians without coloring are currently not supported by GBODE." | |
| 541 | " Colored symbolical Jacobian will be used."); | ||
| 542 | ✗ | } else if (jacobianMethod == NUMJAC || jacobianMethod == COLOREDNUMJAC || jacobianMethod == INTERNALNUMJAC) { | |
| 543 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Numerical Jacobians without coloring are currently not supported by GBODE." | |
| 544 | " Colored numerical Jacobian will be used."); | ||
| 545 | ✗ | gbData->symJacAvailable = FALSE; | |
| 546 | } | ||
| 547 | |||
| 548 | ✗ | initializeSparsePattern_GBODE(data, gbData); | |
| 549 | |||
| 550 | /* Initialize data for the nonlinear solver */ | ||
| 551 | ✗ | gbData->nlsData = initRK_NLS_DATA(data, threadData, gbData); | |
| 552 | ✗ | if (!gbData->nlsData) { | |
| 553 | ✗ | return -1; | |
| 554 | } else { | ||
| 555 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "Nominal values of the states:"); | |
| 556 | ✗ | for (int i = 0; i < gbData->nStates; i++) { | |
| 557 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "%s = %g", data->modelData->realVarsData[i].info.name, gbData->nlsData->nominal[i]); | |
| 558 | } | ||
| 559 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 560 | } | ||
| 561 | } else { | ||
| 562 | ✗ | gbData->symJacAvailable = FALSE; | |
| 563 | ✗ | gbData->nlsSolverMethod = GB_NLS_UNKNOWN; | |
| 564 | ✗ | gbData->nlsData = NULL; | |
| 565 | ✗ | gbData->jacobian = NULL; | |
| 566 | } | ||
| 567 | |||
| 568 | ✗ | gbData->percentage = getGBRatio(); | |
| 569 | ✗ | gbData->multi_rate = gbData->percentage > 0 && gbData->percentage < 1; | |
| 570 | |||
| 571 | ✗ | gbData->fastStatesIdx = malloc(sizeof(int) * gbData->nStates); | |
| 572 | ✗ | gbData->slowStatesIdx = malloc(sizeof(int) * gbData->nStates); | |
| 573 | ✗ | gbData->sortedStatesIdx = malloc(sizeof(int) * gbData->nStates); | |
| 574 | |||
| 575 | ✗ | gbData->nFastStates = 0; | |
| 576 | ✗ | gbData->nSlowStates = gbData->nStates; | |
| 577 | ✗ | for (int i = 0; i < gbData->nStates; i++) { | |
| 578 | // TODO memcpy() faster? | ||
| 579 | ✗ | gbData->fastStatesIdx[i] = i; | |
| 580 | ✗ | gbData->slowStatesIdx[i] = i; | |
| 581 | ✗ | gbData->sortedStatesIdx[i] = i; | |
| 582 | } | ||
| 583 | |||
| 584 | ✗ | if (gbData->multi_rate && omc_flagValue[FLAG_SR_INT]==NULL) { | |
| 585 | ✗ | gbData->interpolation = GB_DENSE_OUTPUT; | |
| 586 | } else { | ||
| 587 | ✗ | gbData->interpolation = getInterpolationMethod(FLAG_SR_INT); | |
| 588 | } | ||
| 589 | |||
| 590 | ✗ | if (!gbData->tableau->withDenseOutput) { | |
| 591 | ✗ | switch (gbData->interpolation) { | |
| 592 | ✗ | case GB_DENSE_OUTPUT: gbData->interpolation = GB_INTERPOL_HERMITE; break; | |
| 593 | ✗ | case GB_DENSE_OUTPUT_ERRCTRL: gbData->interpolation = GB_INTERPOL_HERMITE_ERRCTRL; break; | |
| 594 | default: break; | ||
| 595 | } | ||
| 596 | } | ||
| 597 | |||
| 598 | enum { bufSize = 1024 }; | ||
| 599 | char buffer[bufSize]; | ||
| 600 | ✗ | if (gbData->multi_rate) { | |
| 601 | snprintf(buffer, bufSize, "%s", " and slow states interpolation"); | ||
| 602 | } else { | ||
| 603 | snprintf(buffer, bufSize, "%s"," "); | ||
| 604 | } | ||
| 605 | ✗ | switch (gbData->interpolation) | |
| 606 | { | ||
| 607 | ✗ | case GB_INTERPOL_LIN: | |
| 608 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Linear interpolation is used for emitting results%s", buffer); | |
| 609 | ✗ | break; | |
| 610 | ✗ | case GB_INTERPOL_HERMITE_ERRCTRL: | |
| 611 | case GB_INTERPOL_HERMITE_a: | ||
| 612 | case GB_INTERPOL_HERMITE_b: | ||
| 613 | case GB_INTERPOL_HERMITE: | ||
| 614 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Hermite interpolation is used for emitting results%s", buffer); | |
| 615 | ✗ | break; | |
| 616 | ✗ | case GB_DENSE_OUTPUT: | |
| 617 | case GB_DENSE_OUTPUT_ERRCTRL: | ||
| 618 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Dense output is used for emitting results%s", buffer); | |
| 619 | ✗ | break; | |
| 620 | ✗ | default: | |
| 621 | ✗ | throwStreamPrint(NULL, "Unhandled interpolation case."); | |
| 622 | } | ||
| 623 | ✗ | gbData->err_int = 0; // needed, if GB_INTERPOL_HERMITE_ERRCTRL or GB_DENSE_OUTPUT_ERRCTRL is used | |
| 624 | |||
| 625 | ✗ | if (gbData->multi_rate) { | |
| 626 | ✗ | gbodef_allocateData(data, threadData, solverInfo, gbData); | |
| 627 | ✗ | gbData->tableau->isKRightAvailable = FALSE; | |
| 628 | } else { | ||
| 629 | ✗ | gbData->gbfData = NULL; | |
| 630 | } | ||
| 631 | |||
| 632 | // Value will be handled in the initial step size determination (-1 and 0 means no failure) | ||
| 633 | ✗ | gbData->initialFailures = -1; | |
| 634 | |||
| 635 | ✗ | return 0; | |
| 636 | } | ||
| 637 | |||
| 638 | /** | ||
| 639 | * @brief Free generic RK data. | ||
| 640 | * | ||
| 641 | * @param data Pointer to generik Runge-Kutta data struct. | ||
| 642 | */ | ||
| 643 | ✗ | void gbodef_freeData(DATA_GBODEF *gbfData) | |
| 644 | { | ||
| 645 | ✗ | freeEvalSelection(gbfData->evalSelectionFast); | |
| 646 | |||
| 647 | /* Free non-linear system data */ | ||
| 648 | ✗ | freeRK_NLS_DATA(gbfData->nlsSolverMethod, gbfData->nlsData); | |
| 649 | |||
| 650 | /* Free Jacobian */ | ||
| 651 | ✗ | freeJacobianCopy(gbfData->jacobian); | |
| 652 | |||
| 653 | /* Free sparsity data. */ | ||
| 654 | ✗ | freeSparsePattern(gbfData->sparsePattern_ODE); | |
| 655 | ✗ | freeSparsePattern(gbfData->sparsePattern_NLS); | |
| 656 | ✗ | free(gbfData->sparseWork); | |
| 657 | |||
| 658 | /* Free Butcher tableau */ | ||
| 659 | ✗ | freeButcherTableau(gbfData->tableau); | |
| 660 | |||
| 661 | ✗ | free(gbfData->y); | |
| 662 | ✗ | free(gbfData->yOld); | |
| 663 | ✗ | free(gbfData->yLeft); | |
| 664 | ✗ | free(gbfData->kLeft); | |
| 665 | ✗ | free(gbfData->yRight); | |
| 666 | ✗ | free(gbfData->kRight); | |
| 667 | ✗ | free(gbfData->yLast); | |
| 668 | ✗ | free(gbfData->yOldPacked); | |
| 669 | ✗ | free(gbfData->kLast); | |
| 670 | ✗ | free(gbfData->kCurrPacked); | |
| 671 | ✗ | free(gbfData->yt); | |
| 672 | ✗ | free(gbfData->y1); | |
| 673 | ✗ | free(gbfData->f); | |
| 674 | ✗ | free(gbfData->k); | |
| 675 | ✗ | free(gbfData->x); | |
| 676 | ✗ | free(gbfData->res_const); | |
| 677 | ✗ | free(gbfData->errest); | |
| 678 | ✗ | free(gbfData->errtol); | |
| 679 | ✗ | free(gbfData->err); | |
| 680 | ✗ | free(gbfData->errValues); | |
| 681 | ✗ | free(gbfData->stepSizeValues); | |
| 682 | ✗ | free(gbfData->tv); | |
| 683 | ✗ | free(gbfData->yv); | |
| 684 | ✗ | free(gbfData->kv); | |
| 685 | ✗ | free(gbfData->fastStates_old); | |
| 686 | |||
| 687 | ✗ | slowStateCache_free(gbfData->slowStateCache); | |
| 688 | |||
| 689 | ✗ | if (gbfData->fastStatesDebugFile) | |
| 690 | ✗ | fclose(gbfData->fastStatesDebugFile); | |
| 691 | |||
| 692 | ✗ | free(gbfData); | |
| 693 | ✗ | } | |
| 694 | |||
| 695 | /** | ||
| 696 | * @brief Free generic RK data. | ||
| 697 | * | ||
| 698 | * @param gbData Pointer to generik Runge-Kutta data struct. | ||
| 699 | */ | ||
| 700 | ✗ | void gbode_freeData(DATA* data, DATA_GBODE *gbData) | |
| 701 | { | ||
| 702 | ✗ | freeSymbolicOdeJacobian(data); | |
| 703 | |||
| 704 | /* Free non-linear system data */ | ||
| 705 | ✗ | freeRK_NLS_DATA(gbData->nlsSolverMethod, gbData->nlsData); | |
| 706 | |||
| 707 | /* Free Jacobian */ | ||
| 708 | ✗ | freeJacobianCopy(gbData->jacobian); | |
| 709 | |||
| 710 | /* Free sparsity data. */ | ||
| 711 | ✗ | freeSparsePattern(gbData->sparsePattern_NLS); | |
| 712 | |||
| 713 | /* Free Butcher tableau */ | ||
| 714 | ✗ | freeButcherTableau(gbData->tableau); | |
| 715 | |||
| 716 | ✗ | if (gbData->multi_rate) | |
| 717 | { | ||
| 718 | ✗ | gbodef_freeData(gbData->gbfData); | |
| 719 | gbData->gbfData = NULL; | ||
| 720 | } | ||
| 721 | /* Free multi-rate data */ | ||
| 722 | ✗ | free(gbData->err); | |
| 723 | ✗ | free(gbData->errValues); | |
| 724 | ✗ | free(gbData->stepSizeValues); | |
| 725 | ✗ | free(gbData->tv); | |
| 726 | ✗ | free(gbData->yv); | |
| 727 | ✗ | free(gbData->kv); | |
| 728 | ✗ | free(gbData->tr); | |
| 729 | ✗ | free(gbData->yr); | |
| 730 | ✗ | free(gbData->kr); | |
| 731 | ✗ | free(gbData->fastStatesIdx); | |
| 732 | ✗ | free(gbData->slowStatesIdx); | |
| 733 | ✗ | free(gbData->sortedStatesIdx); | |
| 734 | |||
| 735 | /* Free remaining arrays */ | ||
| 736 | ✗ | free(gbData->y); | |
| 737 | ✗ | free(gbData->yOld); | |
| 738 | ✗ | free(gbData->yLast); | |
| 739 | ✗ | free(gbData->yLeft); | |
| 740 | ✗ | free(gbData->kLeft); | |
| 741 | ✗ | free(gbData->kLast); | |
| 742 | ✗ | free(gbData->yRight); | |
| 743 | ✗ | free(gbData->kRight); | |
| 744 | ✗ | free(gbData->yt); | |
| 745 | ✗ | free(gbData->y1); | |
| 746 | ✗ | free(gbData->y2); | |
| 747 | ✗ | free(gbData->f); | |
| 748 | ✗ | free(gbData->k); | |
| 749 | ✗ | free(gbData->x); | |
| 750 | ✗ | free(gbData->res_const); | |
| 751 | ✗ | free(gbData->errest); | |
| 752 | ✗ | free(gbData->errtol); | |
| 753 | ✗ | free(gbData->nominals); | |
| 754 | ✗ | free(gbData->mins); | |
| 755 | ✗ | free(gbData->maxs); | |
| 756 | |||
| 757 | ✗ | free(gbData); | |
| 758 | |||
| 759 | ✗ | return; | |
| 760 | } | ||
| 761 | |||
| 762 | /** | ||
| 763 | * @brief Calculate initial step size. | ||
| 764 | * | ||
| 765 | * Called at the beginning of simulation or after an event occurred. | ||
| 766 | * | ||
| 767 | * @param data Runtime data struct. | ||
| 768 | * @param threadData Thread data for error handling. | ||
| 769 | * @param solverInfo Storing Runge-Kutta solver data. | ||
| 770 | */ | ||
| 771 | ✗ | void gbodef_init(DATA* data, threadData_t* threadData, SOLVER_INFO* solverInfo) | |
| 772 | { | ||
| 773 | SIMULATION_DATA *sData = (SIMULATION_DATA*)data->localData[0]; | ||
| 774 | SIMULATION_DATA *sDataOld = (SIMULATION_DATA*)data->localData[1]; | ||
| 775 | ✗ | DATA_GBODE* gbData = (DATA_GBODE*)solverInfo->solverData; | |
| 776 | ✗ | DATA_GBODEF* gbfData = gbData->gbfData; | |
| 777 | ✗ | int nStates = gbfData->nStates; | |
| 778 | int nStages = gbfData->tableau->nStages; | ||
| 779 | |||
| 780 | int i; | ||
| 781 | |||
| 782 | ✗ | gbfData->didEventStep = FALSE; | |
| 783 | ✗ | gbfData->extrapolationBaseTime = INFINITY; | |
| 784 | ✗ | gbfData->extrapolationValid = FALSE; | |
| 785 | ✗ | slowStateCache_invalidate(gbfData->slowStateCache); | |
| 786 | |||
| 787 | ✗ | gbfData->time = gbData->time; | |
| 788 | ✗ | gbfData->stepSize = 0.1*gbData->stepSize*GenericController(&(gbData->err_fast), &(gbData->stepSize), 1, GB_CTRL_I); | |
| 789 | |||
| 790 | ✗ | memcpy(gbfData->yOld, gbData->yOld, sizeof(double) * nStates); | |
| 791 | ✗ | memcpy(gbfData->y, gbData->y, sizeof(double) * nStates); | |
| 792 | |||
| 793 | ✗ | gbfData->timeRight = gbData->timeLeft; | |
| 794 | ✗ | memcpy(gbfData->yRight, gbData->yLeft, sizeof(double) * nStates); | |
| 795 | ✗ | memcpy(gbfData->kRight, gbData->kLeft, sizeof(double) * nStates); | |
| 796 | |||
| 797 | // set solution ring buffer (extrapolation in case of NLS) | ||
| 798 | ✗ | for (i = 0; i < gbfData->ringBufferSize; i++) { | |
| 799 | ✗ | gbfData->tv[i] = gbData->tv[i]; | |
| 800 | ✗ | memcpy(gbfData->yv + i * nStates, gbData->yv + i * nStates, nStates * sizeof(double)); | |
| 801 | ✗ | memcpy(gbfData->kv + i * nStates, gbData->kv + i * nStates, nStates * sizeof(double)); | |
| 802 | } | ||
| 803 | ✗ | } | |
| 804 | |||
| 805 | /** | ||
| 806 | * @brief Initialize ring buffer and interpolation arrays. | ||
| 807 | * | ||
| 808 | * Called at the beginning of simulation or after an event occurred. | ||
| 809 | * | ||
| 810 | * @param data Runtime data struct. | ||
| 811 | * @param threadData Thread data for error handling. | ||
| 812 | * @param solverInfo Storing Runge-Kutta solver data. | ||
| 813 | */ | ||
| 814 | ✗ | void gbode_init(DATA* data, threadData_t* threadData, SOLVER_INFO* solverInfo) | |
| 815 | { | ||
| 816 | ✗ | DATA_GBODE* gbData = (DATA_GBODE*)solverInfo->solverData; | |
| 817 | ✗ | SIMULATION_DATA *sData = (SIMULATION_DATA*)data->localData[0]; | |
| 818 | ✗ | modelica_real* fODE = &sData->realVars[gbData->nStates]; | |
| 819 | int nStates = gbData->nStates; | ||
| 820 | int i; | ||
| 821 | |||
| 822 | // initialize ring buffer for error and step size control | ||
| 823 | // TODO memset() faster? | ||
| 824 | ✗ | for (i = 0; i < gbData->ringBufferSize; i++) { | |
| 825 | ✗ | gbData->errValues[i] = 0; | |
| 826 | ✗ | gbData->stepSizeValues[i] = 0; | |
| 827 | } | ||
| 828 | |||
| 829 | /* reset statistics, because it is accumulated in solver_main.c */ | ||
| 830 | ✗ | if (!gbData->isExplicit) | |
| 831 | ✗ | gbData->nlsData->numberOfJEval = 0; | |
| 832 | ✗ | resetSolverStats(&gbData->stats); | |
| 833 | |||
| 834 | // initialize vector used for interpolation (equidistant time grid) | ||
| 835 | // and for the birate inner integration | ||
| 836 | ✗ | gbData->timeRight = gbData->time; | |
| 837 | ✗ | memcpy(gbData->yRight, gbData->yOld, nStates*sizeof(double)); | |
| 838 | ✗ | memcpy(gbData->kRight, fODE, nStates*sizeof(double)); | |
| 839 | |||
| 840 | // set solution ring buffer (extrapolation in case of NLS) | ||
| 841 | ✗ | for (i = 0; i < gbData->ringBufferSize; i++) { | |
| 842 | ✗ | gbData->tv[i] = gbData->timeRight; | |
| 843 | ✗ | memcpy(gbData->yv + i * nStates, gbData->yRight, nStates * sizeof(double)); | |
| 844 | ✗ | memcpy(gbData->kv + i * nStates, gbData->kRight, nStates * sizeof(double)); | |
| 845 | } | ||
| 846 | ✗ | gbData->eventTime = DBL_MAX; // reset event time | |
| 847 | ✗ | } | |
| 848 | |||
| 849 | /*! \fn updateEvalSelection | ||
| 850 | * | ||
| 851 | * updates evalSelectionFast for evaluating gbode_fODE | ||
| 852 | */ | ||
| 853 | ✗ | static void updateEvalSelection(DATA* data, DATA_GBODE* gbData) | |
| 854 | { | ||
| 855 | size_t k; | ||
| 856 | ✗ | EVAL_SELECTION* selection = gbData->gbfData->evalSelectionFast; | |
| 857 | |||
| 858 | ✗ | clearEvalSelection(selection); | |
| 859 | |||
| 860 | /* set equations for fast derivatives */ | ||
| 861 | ✗ | for (k = 0; k < gbData->nFastStates; k++) { | |
| 862 | ✗ | size_t derIdx = gbData->fastStatesIdx[k] + gbData->nStates; | |
| 863 | ✗ | size_t eqnIdx = selection->dag->mapVarToEqNode[derIdx]; | |
| 864 | ✗ | if (eqnIdx == (size_t)(-1)) continue; | |
| 865 | ✗ | selection->dag->select[eqnIdx] = TRUE; | |
| 866 | } | ||
| 867 | |||
| 868 | ✗ | activateEvalDependencies(selection); | |
| 869 | |||
| 870 | /* debug print */ | ||
| 871 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 872 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "updateEvalSelection"); | |
| 873 | enum { bufSize = 40960 }; | ||
| 874 | char row_to_print[bufSize]; | ||
| 875 | unsigned int ct; | ||
| 876 | ✗ | ct = snprintf(row_to_print, bufSize, "%s (time=%g): =", "eqFunctions", data->localData[0]->timeValue); | |
| 877 | ✗ | for (k = 0; k < selection->n; k++) { | |
| 878 | ✗ | ct += snprintf(row_to_print+ct, bufSize-ct, " %zu", selection->idx[k]); | |
| 879 | } | ||
| 880 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 0, "%s", row_to_print); | |
| 881 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 882 | } | ||
| 883 | ✗ | } | |
| 884 | |||
| 885 | /*! \fn updateEvalSelectionJacobian | ||
| 886 | * | ||
| 887 | * updates evalSelection for evaluating Jacobian_ODE | ||
| 888 | */ | ||
| 889 | ✗ | static void updateEvalSelectionJacobian(DATA* data, DATA_GBODE* gbData) | |
| 890 | { | ||
| 891 | size_t k; | ||
| 892 | ✗ | EVAL_SELECTION* selection = gbData->gbfData->jacobian->evalSelection; | |
| 893 | |||
| 894 | ✗ | clearEvalSelection(selection); | |
| 895 | |||
| 896 | /* set equations for fast derivatives */ | ||
| 897 | ✗ | for (k = 0; k < gbData->nFastStates; k++) { | |
| 898 | ✗ | size_t derIdx = gbData->fastStatesIdx[k]; | |
| 899 | ✗ | size_t eqnIdx = selection->dag->mapVarToEqNode[derIdx]; | |
| 900 | ✗ | if (eqnIdx == (size_t)(-1)) continue; | |
| 901 | ✗ | selection->dag->select[eqnIdx] = TRUE; | |
| 902 | } | ||
| 903 | |||
| 904 | ✗ | activateEvalDependencies(selection); | |
| 905 | |||
| 906 | /* debug print */ | ||
| 907 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 908 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "updateEvalSelectionJacobian"); | |
| 909 | enum { bufSize = 40960 }; | ||
| 910 | char row_to_print[bufSize]; | ||
| 911 | unsigned int ct; | ||
| 912 | ✗ | ct = snprintf(row_to_print, bufSize, "%s (time=%g): =", "Jacobian eqFunctions", data->localData[0]->timeValue); | |
| 913 | ✗ | for (k = 0; k < selection->n; k++) { | |
| 914 | ✗ | ct += snprintf(row_to_print+ct, bufSize-ct, " %zu", selection->idx[k]); | |
| 915 | } | ||
| 916 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 0, "%s", row_to_print); | |
| 917 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 918 | } | ||
| 919 | ✗ | } | |
| 920 | |||
| 921 | /*! \fn gbodef_main | ||
| 922 | * | ||
| 923 | * function does one integration step and calculates | ||
| 924 | * next step size by the implicit midpoint rule | ||
| 925 | * | ||
| 926 | * used for solver 'gm' | ||
| 927 | */ | ||
| 928 | ✗ | int gbodef_main(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, double targetTime) | |
| 929 | { | ||
| 930 | ✗ | SIMULATION_DATA *sData = (SIMULATION_DATA *)data->localData[0]; | |
| 931 | ✗ | modelica_real *fODE = sData->realVars + data->modelData->nStates; | |
| 932 | ✗ | DATA_GBODE *gbData = (DATA_GBODE *)solverInfo->solverData; | |
| 933 | ✗ | DATA_GBODEF *gbfData = gbData->gbfData; | |
| 934 | |||
| 935 | ✗ | double stopTime = data->simulationInfo->stopTime; | |
| 936 | |||
| 937 | double err, eventTime; | ||
| 938 | double tol = data->simulationInfo->tolerance; | ||
| 939 | |||
| 940 | int i, ii, j, jj, l, ll, r, rr; | ||
| 941 | int integrator_step_info; | ||
| 942 | |||
| 943 | ✗ | int nStates = gbData->nStates; | |
| 944 | ✗ | int nFastStates = gbData->nFastStates; | |
| 945 | ✗ | int nStages = gbfData->tableau->nStages; | |
| 946 | |||
| 947 | modelica_boolean fastStatesChange = FALSE; | ||
| 948 | modelica_boolean foundEvent; | ||
| 949 | |||
| 950 | // This is the target time of the main integrator | ||
| 951 | ✗ | const double innerTargetTime = fmin(targetTime, gbData->timeRight); | |
| 952 | |||
| 953 | /* The inner integrator needs to be initialzed, at start time, when an event occured, | ||
| 954 | * and if outer integrations have been done with all states involved | ||
| 955 | * (gbfData->timeRight < gbData->timeLeft) | ||
| 956 | */ | ||
| 957 | ✗ | if (gbfData->didEventStep || gbfData->timeRight < gbData->timeLeft) { | |
| 958 | ✗ | gbodef_init(data, threadData, solverInfo); | |
| 959 | } | ||
| 960 | |||
| 961 | ✗ | fastStatesChange = checkFastStatesChange(gbData); | |
| 962 | |||
| 963 | ✗ | if (fastStatesChange) { | |
| 964 | ✗ | updateEvalSelection(data, gbData); | |
| 965 | ✗ | gbfData->extrapolationValid = FALSE; | |
| 966 | ✗ | gbfData->fastStateUpdateCount++; | |
| 967 | } | ||
| 968 | |||
| 969 | ✗ | if (fastStatesChange && !gbfData->isExplicit) { | |
| 970 | ✗ | struct dataSolver *solverData = gbfData->nlsData->solverData; | |
| 971 | // set number of non-linear variables and corresponding nominal values (changes dynamically during simulation) | ||
| 972 | ✗ | gbfData->nlsData->size = gbData->nFastStates; | |
| 973 | ✗ | slowStateCache_invalidate(gbfData->slowStateCache); | |
| 974 | |||
| 975 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Fast states and corresponding nominal values:"); | |
| 976 | ✗ | for (ii = 0; ii < nFastStates; ii++) { | |
| 977 | ✗ | i = gbData->fastStatesIdx[ii]; | |
| 978 | ✗ | gbfData->nlsData->nominal[ii] = gbData->nominals[i]; | |
| 979 | ✗ | gbfData->nlsData->min[ii] = gbData->mins[i]; | |
| 980 | ✗ | gbfData->nlsData->max[ii] = gbData->maxs[i]; | |
| 981 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 0, "%s = %g", data->modelData->realVarsData[i].info.name, gbfData->nlsData->nominal[ii]); | |
| 982 | } | ||
| 983 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 984 | |||
| 985 | ✗ | if (gbfData->sparsePattern_NLS) { | |
| 986 | ✗ | updateSparsePattern_GBODEF(data, gbData); | |
| 987 | ✗ | if (gbfData->nlsSolverMethod != GB_NLS_INTERNAL) | |
| 988 | { | ||
| 989 | ✗ | gbfData->jacobian->sizeCols = nFastStates; | |
| 990 | ✗ | gbfData->jacobian->sizeRows = nFastStates; | |
| 991 | } | ||
| 992 | |||
| 993 | /* TODO don't free and realloc, instead overwrite large enough buffer */ | ||
| 994 | ✗ | switch (gbfData->nlsSolverMethod) | |
| 995 | { | ||
| 996 | ✗ | case GB_NLS_NEWTON: | |
| 997 | ✗ | ((DATA_NEWTON *)solverData->ordinaryData)->n = gbData->nFastStates; | |
| 998 | ✗ | break; | |
| 999 | ✗ | case GB_NLS_KINSOL: | |
| 1000 | ✗ | nlsKinsolFree(solverData->ordinaryData); | |
| 1001 | /* Set NLS user data */ | ||
| 1002 | ✗ | NLS_USERDATA* nlsUserData = initNlsUserData(data, threadData, -1, gbfData->nlsData, gbfData->jacobian); | |
| 1003 | ✗ | nlsUserData->solverData = (void*) gbfData; | |
| 1004 | ✗ | solverData->ordinaryData = (void*) nlsKinsolAllocate(gbfData->nlsData->size, nlsUserData, FALSE, !!gbfData->nlsData->sparsePattern); | |
| 1005 | ✗ | break; | |
| 1006 | ✗ | case GB_NLS_KINSOL_B: | |
| 1007 | ✗ | B_nlsKinsolFree(solverData->ordinaryData); | |
| 1008 | /* Set NLS user data */ | ||
| 1009 | ✗ | NLS_USERDATA* B_nlsUserData = initNlsUserData(data, threadData, -1, gbfData->nlsData, gbfData->jacobian); | |
| 1010 | ✗ | B_nlsUserData->solverData = (void*) gbfData; | |
| 1011 | ✗ | solverData->ordinaryData = (void*) B_nlsKinsolAllocate(gbfData->nlsData->size, B_nlsUserData, FALSE, !!gbfData->nlsData->sparsePattern); | |
| 1012 | ✗ | break; | |
| 1013 | ✗ | case GB_NLS_INTERNAL: | |
| 1014 | // notify internal to update the sparsity + symbolic factorization in the next iteration | ||
| 1015 | ✗ | gbInternalScheduleFastStatesUpdate(solverData->ordinaryData); | |
| 1016 | ✗ | break; | |
| 1017 | ✗ | default: | |
| 1018 | ✗ | throwStreamPrint(NULL, "NLS method %s not yet implemented.", GB_NLS_METHOD_NAME[gbfData->nlsSolverMethod]); | |
| 1019 | } | ||
| 1020 | } | ||
| 1021 | // TODO: -gbnls=internal currently does not use the Jacobian eval selection | ||
| 1022 | ✗ | if (gbfData->nlsSolverMethod != GB_NLS_INTERNAL && gbfData->symJacAvailable) { | |
| 1023 | ✗ | updateEvalSelectionJacobian(data, gbData); | |
| 1024 | } | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | // print informations on the calling details | ||
| 1028 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "gbodef solver started (fast states/states): %d/%d", gbData->nFastStates,gbData->nStates); | |
| 1029 | ✗ | printIntVector_gb(OMC_LOG_SOLVER, "fast States:", gbData->fastStatesIdx, gbData->nFastStates, gbfData->time); | |
| 1030 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "interpolation is done between %10g to %10g (SR-stepsize: %10g)", | |
| 1031 | gbData->timeLeft, gbData->timeRight, gbData->lastStepSize); | ||
| 1032 | |||
| 1033 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 1034 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "Interpolation values from outer integration:"); | |
| 1035 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "yL", gbData->yLeft, gbData->nStates, gbData->timeLeft); | |
| 1036 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "kL", gbData->kLeft, gbData->nStates, gbData->timeLeft); | |
| 1037 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "yR", gbData->yRight, gbData->nStates, gbData->timeRight); | |
| 1038 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "kR", gbData->kRight, gbData->nStates, gbData->timeRight); | |
| 1039 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 1040 | } | ||
| 1041 | |||
| 1042 | ✗ | while (gbfData->time < innerTargetTime) { | |
| 1043 | |||
| 1044 | // Don't exceed simulation stop time | ||
| 1045 | ✗ | if (gbfData->time + gbfData->stepSize > stopTime) { | |
| 1046 | ✗ | gbfData->stepSize = stopTime - gbfData->time; | |
| 1047 | } | ||
| 1048 | |||
| 1049 | // Synchronize inner integration with outer integration | ||
| 1050 | // Strategy: either set outer step to the inner integration | ||
| 1051 | // or the other way around (depending on, if more or less | ||
| 1052 | // than 2 inner steps required) | ||
| 1053 | ✗ | if (gbfData->time + gbfData->stepSize > gbData->timeRight) { | |
| 1054 | // if (gbfData->time - gbfData->stepSize > gbData->timeLeft) { | ||
| 1055 | // gbData->timeRight = gbfData->timeRight; | ||
| 1056 | // gbData->lastStepSize = gbData->timeRight - gbData->timeLeft; | ||
| 1057 | // messageClose(OMC_LOG_SOLVER); // FIXME what does this belong to? | ||
| 1058 | // return 0; | ||
| 1059 | // } else { | ||
| 1060 | ✗ | gbfData->stepSize = gbData->timeRight - gbfData->time; | |
| 1061 | // } | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | // store left hand data for later interpolation | ||
| 1065 | ✗ | gbfData->timeLeft = gbfData->timeRight; | |
| 1066 | ✗ | memcpy(gbfData->yLeft, gbfData->yRight, nStates * sizeof(double)); | |
| 1067 | ✗ | memcpy(gbfData->kLeft, gbfData->kRight, nStates * sizeof(double)); | |
| 1068 | |||
| 1069 | // debug the changes of the states and derivatives during integration | ||
| 1070 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1071 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "states and derivatives at left hand side (inner integration):"); | |
| 1072 | ✗ | printVector_gbf(OMC_LOG_GBODE, "yL", gbfData->yLeft, nStates, gbfData->timeLeft, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1073 | ✗ | printVector_gbf(OMC_LOG_GBODE, "kL", gbfData->kLeft, nStates, gbfData->timeLeft, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1074 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1075 | } | ||
| 1076 | |||
| 1077 | do { | ||
| 1078 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER_V)) { | |
| 1079 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "States and derivatives of the ring buffer:"); | |
| 1080 | ✗ | for (int i = 0; i < gbfData->ringBufferSize; i++) { | |
| 1081 | ✗ | printVector_gbf(OMC_LOG_SOLVER_V, "y", gbfData->yv + i * nStates, nStates, gbfData->tv[i], gbData->nFastStates, gbData->fastStatesIdx); | |
| 1082 | ✗ | printVector_gbf(OMC_LOG_SOLVER_V, "k", gbfData->kv + i * nStates, nStates, gbfData->tv[i], gbData->nFastStates, gbData->fastStatesIdx); | |
| 1083 | } | ||
| 1084 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1085 | } | ||
| 1086 | |||
| 1087 | // do one integration step resulting in two different approximations | ||
| 1088 | // results are stored in gbData->y and gbData->yt | ||
| 1089 | ✗ | if (gbfData->tableau->richardson) { | |
| 1090 | ✗ | integrator_step_info = gbodef_richardson(data, threadData, solverInfo); | |
| 1091 | } else { | ||
| 1092 | ✗ | integrator_step_info = gbfData->step_fun(data, threadData, solverInfo); | |
| 1093 | } | ||
| 1094 | |||
| 1095 | // error handling: try half of the step size! | ||
| 1096 | ✗ | if (integrator_step_info != 0) { | |
| 1097 | ✗ | (gbfData->stats).nConvergenceTestFailures++; | |
| 1098 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER)) infoStreamPrint(OMC_LOG_SOLVER, 0, "gbodef_main: Failed to calculate step at time = %5g with step size h = %5g.", gbData->time, gbData->stepSize); | |
| 1099 | ✗ | gbfData->stepSize *= 0.5; | |
| 1100 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER)) infoStreamPrint(OMC_LOG_SOLVER, 0, "Try half of the step size = %g", gbfData->stepSize); | |
| 1101 | ✗ | if (gbfData->stepSize < GB_MINIMAL_STEP_SIZE) { | |
| 1102 | ✗ | errorStreamPrint(OMC_LOG_STDOUT, 0, "Simulation aborted! Minimum step size %g reached, but error still to large.", GB_MINIMAL_STEP_SIZE); | |
| 1103 | ✗ | messageClose(OMC_LOG_SOLVER); // FIXME what does this belong to? | |
| 1104 | ✗ | return -1; | |
| 1105 | } | ||
| 1106 | ✗ | slowStateCache_invalidate_keep_left(gbfData->slowStateCache); | |
| 1107 | err = INFINITY; | ||
| 1108 | ✗ | continue; | |
| 1109 | } | ||
| 1110 | |||
| 1111 | ✗ | tol = gbScaledErrorTolerance(data->simulationInfo->tolerance, gbfData->tableau->order_b, | |
| 1112 | ✗ | gbfData->currentErrorOrder, gbfData->tableau->richardson); | |
| 1113 | |||
| 1114 | /* use same error estimate (scaled 2-norm) as for the SR case */ | ||
| 1115 | ✗ | for (i = 0, err=0; i < nFastStates; i++) { | |
| 1116 | ✗ | ii = gbData->fastStatesIdx[i]; | |
| 1117 | // calculate corresponding values for the error estimator and step size control | ||
| 1118 | ✗ | gbfData->errtol[ii] = tol * gbData->nominals[ii] + fmax(fabs(gbfData->yOld[ii]), fabs(gbfData->y[ii])) * tol; | |
| 1119 | ✗ | if (gbfData->tableau->richardson || gbfData->type == MS_TYPE_IMPLICIT) { | |
| 1120 | ✗ | gbfData->errest[ii] = fabs(gbfData->yt[ii]); | |
| 1121 | } | ||
| 1122 | ✗ | gbfData->err[ii] = gbfData->tableau->fac * gbfData->errest[ii] / gbfData->errtol[ii]; | |
| 1123 | ✗ | err += gbfData->err[ii] * gbfData->err[ii]; | |
| 1124 | } | ||
| 1125 | ✗ | err = sqrt(err / (double) nFastStates); | |
| 1126 | |||
| 1127 | // debug ring buffer for the states and derviatives of the states | ||
| 1128 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 1129 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "ring buffer during steps of inner integration"); | |
| 1130 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 0, "old value:"); | |
| 1131 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "y", gbfData->yOld, nStates, gbfData->time, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1132 | ✗ | debugRingBuffer_gbf(OMC_LOG_GBODE_V, gbfData->x, gbfData->k, nStates, gbfData->tableau, gbfData->time, gbfData->lastStepSize, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1133 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 0, "new value:"); | |
| 1134 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "y", gbfData->y, nStates, gbfData->time + gbfData->lastStepSize, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1135 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 1136 | } | ||
| 1137 | |||
| 1138 | // Re-do step, if error is larger than requested | ||
| 1139 | ✗ | if (err > 1) { | |
| 1140 | ✗ | gbfData->stats.nErrorTestFailures++; | |
| 1141 | ✗ | gbfData->stepSize *= 0.5; | |
| 1142 | ✗ | slowStateCache_invalidate_keep_left(gbfData->slowStateCache); | |
| 1143 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Reject step from %10g to %10g, error %10g, new stepsize %10g", | |
| 1144 | ✗ | gbfData->time, gbfData->time + gbfData->lastStepSize, err, gbfData->stepSize); | |
| 1145 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1146 | ✗ | dumpFastStates_gbf(gbData, gbfData->time + gbfData->lastStepSize, 1); | |
| 1147 | } | ||
| 1148 | } | ||
| 1149 | ✗ | } while (err > 1); | |
| 1150 | |||
| 1151 | /* remember last time values for dense output extrapolation with yLast, kLast */ | ||
| 1152 | ✗ | gbfData->extrapolationBaseTime = gbfData->time; | |
| 1153 | ✗ | gbfData->extrapolationStepSize = gbfData->stepSize; | |
| 1154 | ✗ | gbfData->extrapolationValid = TRUE; | |
| 1155 | ✗ | gbData->didFastStep = TRUE; | |
| 1156 | |||
| 1157 | /* remember kLast and yLast for dense output extrapolation, we have to pack them properly though | ||
| 1158 | TODO: gbfData->k and gbfData->y / yOld should always contain only the fast states packed from | ||
| 1159 | fast index 0, ..., nFastIndex - 1 (so no slow states) - we never need them, except for some interpolations | ||
| 1160 | but that is purely possible with data from gbData itself!! */ | ||
| 1161 | ✗ | for (int fast_idx = 0; fast_idx < nFastStates; fast_idx++) | |
| 1162 | { | ||
| 1163 | ✗ | int slow_idx = gbData->fastStatesIdx[fast_idx]; | |
| 1164 | ✗ | gbfData->yLast[fast_idx] = gbfData->yOld[slow_idx]; | |
| 1165 | } | ||
| 1166 | |||
| 1167 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 1168 | { | ||
| 1169 | ✗ | double *kLast_strided = &gbfData->kLast[stage * nFastStates]; | |
| 1170 | ✗ | double *k_strided = &gbfData->k[stage * nStates]; | |
| 1171 | ✗ | for (int fast_idx = 0; fast_idx < nFastStates; fast_idx++) | |
| 1172 | { | ||
| 1173 | ✗ | int slow_idx = gbData->fastStatesIdx[fast_idx]; | |
| 1174 | ✗ | kLast_strided[fast_idx] = k_strided[slow_idx]; | |
| 1175 | } | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | // Count successful integration steps | ||
| 1179 | ✗ | gbfData->stats.nStepsTaken += 1; | |
| 1180 | |||
| 1181 | // pretty sure this is redundant (at least for ESDIRK) -> inspect this further | ||
| 1182 | ✗ | slowStateCache_merge_left(gbData, gbfData->slowStateCache, gbfData->yOld); | |
| 1183 | |||
| 1184 | // interpolation to right boundary | ||
| 1185 | ✗ | slowStateCache_merge_right(gbData, gbfData->slowStateCache, gbfData->y); | |
| 1186 | ✗ | slowStateCache_rotate(gbfData->slowStateCache); | |
| 1187 | |||
| 1188 | // store right hand values for latter interpolation | ||
| 1189 | ✗ | gbfData->timeRight = gbfData->time + gbfData->stepSize; | |
| 1190 | ✗ | memcpy(gbfData->yRight, gbfData->y, nStates * sizeof(double)); | |
| 1191 | // update kRight | ||
| 1192 | ✗ | if (!gbfData->tableau->isKRightAvailable) { | |
| 1193 | ✗ | sData->timeValue = gbfData->timeRight; | |
| 1194 | ✗ | memcpy(sData->realVars, gbfData->yRight, data->modelData->nStates * sizeof(double)); | |
| 1195 | ✗ | gbode_fODE(data, threadData, &(gbData->gbfData->stats.nCallsODE), gbfData->evalSelectionFast); | |
| 1196 | ✗ | memcpy(gbfData->kRight, fODE, nStates * sizeof(double)); | |
| 1197 | } | ||
| 1198 | else | ||
| 1199 | { | ||
| 1200 | // last stage of method already provides the vector | ||
| 1201 | ✗ | memcpy(gbfData->kRight, &gbfData->k[nStates * (nStages - 1)], nStates * sizeof(double)); | |
| 1202 | } | ||
| 1203 | |||
| 1204 | ✗ | foundEvent = checkForEvents(data, threadData, solverInfo, gbfData->time, gbfData->yOld, gbfData->time + gbfData->stepSize, gbfData->y, TRUE, &eventTime); | |
| 1205 | ✗ | if (foundEvent) { | |
| 1206 | ✗ | solverInfo->currentTime = eventTime; | |
| 1207 | ✗ | sData->timeValue = solverInfo->currentTime; | |
| 1208 | ✗ | gbData->eventHappened = TRUE; | |
| 1209 | |||
| 1210 | // sData->realVars are the "numerical" values on the right hand side of the event | ||
| 1211 | ✗ | gbData->time = eventTime; | |
| 1212 | ✗ | memcpy(gbData->yOld, sData->realVars, gbData->nStates * sizeof(double)); | |
| 1213 | |||
| 1214 | ✗ | gbfData->time = eventTime; | |
| 1215 | ✗ | memcpy(gbfData->yOld, sData->realVars, gbData->nStates * sizeof(double)); | |
| 1216 | |||
| 1217 | /* write statistics to the solverInfo data structure */ | ||
| 1218 | ✗ | memcpy(&solverInfo->solverStatsTmp, &gbfData->stats, sizeof(SOLVERSTATS)); | |
| 1219 | |||
| 1220 | // log the emitted result | ||
| 1221 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)){ | |
| 1222 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Emit result (inner integration):"); | |
| 1223 | ✗ | printVector_gbf(OMC_LOG_GBODE, " y", sData->realVars, nStates, sData->timeValue, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1224 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1225 | } | ||
| 1226 | |||
| 1227 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1228 | ✗ | dumpFastStates_gb(gbData, TRUE, eventTime, 0); | |
| 1229 | } | ||
| 1230 | |||
| 1231 | // Get out of the integration routine for event handling | ||
| 1232 | ✗ | messageClose(OMC_LOG_SOLVER); // FIXME what does this belong to? | |
| 1233 | ✗ | return 1; | |
| 1234 | } | ||
| 1235 | |||
| 1236 | // set previously computed error (is accepted at this point) + predict new step size | ||
| 1237 | ✗ | gbData->err_fast = err; | |
| 1238 | |||
| 1239 | // Rotate and update buffer | ||
| 1240 | ✗ | for (i = (gbfData->ringBufferSize - 1); i > 0 ; i--) { | |
| 1241 | ✗ | gbfData->errValues[i] = gbfData->errValues[i - 1]; | |
| 1242 | ✗ | gbfData->stepSizeValues[i] = gbfData->stepSizeValues[i - 1]; | |
| 1243 | } | ||
| 1244 | |||
| 1245 | ✗ | gbfData->errValues[0] = err; | |
| 1246 | ✗ | gbfData->stepSizeValues[0] = gbfData->stepSize; | |
| 1247 | |||
| 1248 | /* update time with performed stepSize */ | ||
| 1249 | ✗ | gbfData->time += gbfData->stepSize; | |
| 1250 | |||
| 1251 | // Store performed stepSize for adjusting the time in case of latter interpolation | ||
| 1252 | // Call the step size control | ||
| 1253 | ✗ | gbfData->lastStepSize = gbfData->stepSize; | |
| 1254 | ✗ | gbfData->stepSize *= GenericController(gbfData->errValues, gbfData->stepSizeValues, gbfData->currentErrorOrder, gbfData->ctrl_method); | |
| 1255 | |||
| 1256 | // debug the changes of the states and derivatives during integration | ||
| 1257 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1258 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "States and derivatives at right hand side (inner integration):"); | |
| 1259 | ✗ | printVector_gbf(OMC_LOG_GBODE, "yR", gbfData->yRight, nStates, gbfData->timeRight, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1260 | ✗ | printVector_gbf(OMC_LOG_GBODE, "kR", gbfData->kRight, nStates, gbfData->timeRight, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1261 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1262 | } | ||
| 1263 | |||
| 1264 | // Rotate ring buffer | ||
| 1265 | ✗ | for (i = (gbfData->ringBufferSize - 1); i > 0 ; i--) { | |
| 1266 | ✗ | gbfData->tv[i] = gbfData->tv[i - 1]; | |
| 1267 | ✗ | memcpy(gbfData->yv + i * nStates, gbfData->yv + (i - 1) * nStates, nStates * sizeof(double)); | |
| 1268 | ✗ | memcpy(gbfData->kv + i * nStates, gbfData->kv + (i - 1) * nStates, nStates * sizeof(double)); | |
| 1269 | } | ||
| 1270 | |||
| 1271 | ✗ | gbfData->tv[0] = gbfData->timeRight; | |
| 1272 | ✗ | memcpy(gbfData->yv, gbfData->yRight, nStates * sizeof(double)); | |
| 1273 | ✗ | memcpy(gbfData->kv, gbfData->kRight, nStates * sizeof(double)); | |
| 1274 | |||
| 1275 | ✗ | debugRingBufferSteps_gbf(OMC_LOG_GBODE, gbfData->yv, gbfData->kv, gbfData->tv, nStates, gbfData->ringBufferSize, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1276 | |||
| 1277 | /* step is accepted and yOld needs to be updated */ | ||
| 1278 | // copyVector_gbf(gbfData->yOld, gbfData->y, nFastStates, gbData->fastStates); | ||
| 1279 | ✗ | memcpy(gbfData->yOld, gbfData->y, nStates * sizeof(double)); | |
| 1280 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Accept step from %10g to %10g, error %10g, new stepsize %10g", | |
| 1281 | ✗ | gbfData->time - gbfData->lastStepSize, gbfData->time, err, gbfData->stepSize); | |
| 1282 | |||
| 1283 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1284 | ✗ | dumpFastStates_gbf(gbData, gbfData->time, 0); | |
| 1285 | } | ||
| 1286 | |||
| 1287 | /* emit step, if solverNoEquidistantGrid is selected */ | ||
| 1288 | ✗ | if (solverInfo->solverNoEquidistantGrid) { | |
| 1289 | ✗ | sData->timeValue = gbfData->time; | |
| 1290 | ✗ | solverInfo->currentTime = sData->timeValue; | |
| 1291 | ✗ | memcpy(sData->realVars, gbfData->y, nStates * sizeof(double)); | |
| 1292 | /* | ||
| 1293 | * to emit consistent value we need to update the whole | ||
| 1294 | * continuous system with algebraic variables. | ||
| 1295 | */ | ||
| 1296 | ✗ | data->callback->updateContinuousSystem(data, threadData); | |
| 1297 | ✗ | sim_result.emit(&sim_result, data, threadData); | |
| 1298 | // log the emitted result | ||
| 1299 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)){ | |
| 1300 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Emit result (inner integration):"); | |
| 1301 | ✗ | printVector_gbf(OMC_LOG_GBODE, " y", sData->realVars, nStates, sData->timeValue, gbData->nFastStates, gbData->fastStatesIdx); | |
| 1302 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1303 | } | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | ✗ | if ((gbData->timeRight - gbfData->time) < GB_MINIMAL_STEP_SIZE || gbData->stepSize < GB_MINIMAL_STEP_SIZE) { | |
| 1307 | ✗ | gbfData->time = gbData->timeRight; | |
| 1308 | ✗ | break; | |
| 1309 | } | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | // TODO: these 2 full ODE evaluations are a significant performance issue | ||
| 1313 | // I would advocate for removing the kv/yv/tv ring buffer and these 2 ODE evaluations. | ||
| 1314 | // For NLS initial guess extrapolation, yLast + kLast is sufficient as we already | ||
| 1315 | // have the stage derivatives of the last accepted step and a dense output function. If Hermite interpolation to a | ||
| 1316 | // specific point is needed, evaluate kLeft / kRight on demand or with a cache structure similar to slowStateCache. | ||
| 1317 | // For result file output, only state variables need to be set. The fODE is called | ||
| 1318 | // internally by the emit function anyway, so computing it explicitly here just to fill kv is redundant. (only to maintain the ring buffer) | ||
| 1319 | |||
| 1320 | /* update last two entries of ringbuffer with missing values of new fast derivatives */ | ||
| 1321 | ✗ | for (i = 0; i < 2; i++) { | |
| 1322 | // TODO actually we only need fast derivatives, but at this point we don't | ||
| 1323 | // yet know which states will become fast so we compute everything. | ||
| 1324 | ✗ | sData->timeValue = gbfData->tv[i]; | |
| 1325 | ✗ | memcpy(sData->realVars, gbfData->yv + i * nStates, data->modelData->nStates * sizeof(double)); | |
| 1326 | ✗ | gbode_fODE(data, threadData, &gbfData->additionalFullODEEvaluations, NULL); | |
| 1327 | |||
| 1328 | ✗ | for (j = 0; j < gbData->nSlowStates; j++) | |
| 1329 | ✗ | (gbfData->kv + i * nStates)[gbData->slowStatesIdx[j]] = fODE[gbData->slowStatesIdx[j]]; | |
| 1330 | } | ||
| 1331 | |||
| 1332 | // copy error and values of the fast states to the outer integrator routine if outer integration time is reached | ||
| 1333 | //gbData->err_fast = gbfData->errValues[0]; | ||
| 1334 | |||
| 1335 | ✗ | if (!solverInfo->solverNoEquidistantGrid && gbfData->time >= targetTime) { | |
| 1336 | /* Integrator does large steps and needs to interpolate results with respect to the output grid */ | ||
| 1337 | /* Here, only the fast states get updated */ | ||
| 1338 | ✗ | sData->timeValue = solverInfo->currentTime + solverInfo->currentStepSize; | |
| 1339 | // solverInfo->currentTime = sData->timeValue; | ||
| 1340 | |||
| 1341 | ✗ | gb_interpolation(gbfData->interpolation, | |
| 1342 | gbfData->timeLeft, gbfData->yLeft, gbfData->kLeft, | ||
| 1343 | gbfData->timeRight, gbfData->yRight, gbfData->kRight, | ||
| 1344 | ✗ | sData->timeValue, sData->realVars, | |
| 1345 | nFastStates, gbData->fastStatesIdx, nStates, gbfData->tableau, gbfData->x, gbfData->k); | ||
| 1346 | } | ||
| 1347 | /* Solver statistics */ | ||
| 1348 | ✗ | if (!gbfData->isExplicit) | |
| 1349 | { | ||
| 1350 | ✗ | gbfData->stats.nCallsJacobian += gbfData->nlsData->numberOfJEval; | |
| 1351 | ✗ | gbfData->nlsData->numberOfJEval = 0; | |
| 1352 | } | ||
| 1353 | |||
| 1354 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "gbodef finished (inner steps)."); | |
| 1355 | ✗ | messageClose(OMC_LOG_SOLVER); // FIXME what does this belong to? | |
| 1356 | |||
| 1357 | ✗ | return 0; | |
| 1358 | } | ||
| 1359 | |||
| 1360 | /** | ||
| 1361 | * @brief Generic Runge-Kutta step. | ||
| 1362 | * | ||
| 1363 | * Do one Runge-Kutta integration step. | ||
| 1364 | * Has step-size control and event handling. | ||
| 1365 | * | ||
| 1366 | * @param data Runtime data struct. | ||
| 1367 | * @param threadData Thread data for error handling. | ||
| 1368 | * @param solverInfo Storing Runge-Kutta solver data. | ||
| 1369 | * @return int Return 0 on success, -1 on failure. | ||
| 1370 | */ | ||
| 1371 | ✗ | int gbode_main(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo) | |
| 1372 | { | ||
| 1373 | ✗ | SIMULATION_DATA *sData = (SIMULATION_DATA *)data->localData[0]; | |
| 1374 | ✗ | modelica_real *fODE = sData->realVars + data->modelData->nStates; | |
| 1375 | ✗ | DATA_GBODE *gbData = (DATA_GBODE *)solverInfo->solverData; | |
| 1376 | |||
| 1377 | ✗ | double stopTime = data->simulationInfo->stopTime; | |
| 1378 | double tol = data->simulationInfo->tolerance; | ||
| 1379 | |||
| 1380 | ✗ | int nStates = gbData->nStates; | |
| 1381 | ✗ | int nStages = gbData->tableau->nStages; | |
| 1382 | |||
| 1383 | double targetTime, err; | ||
| 1384 | |||
| 1385 | ✗ | const modelica_boolean noConst_intWithErrctrl = gbData->ctrl_method != GB_CTRL_CNST && (gbData->interpolation == GB_INTERPOL_HERMITE_ERRCTRL || gbData->interpolation == GB_DENSE_OUTPUT_ERRCTRL); | |
| 1386 | |||
| 1387 | int gb_step_info; | ||
| 1388 | int i, retries = 0; | ||
| 1389 | modelica_boolean foundEvent; | ||
| 1390 | |||
| 1391 | double err_states; // error of the (slow, if multirate) states | ||
| 1392 | |||
| 1393 | // root finding will be done in gbode after each accepted step | ||
| 1394 | ✗ | solverInfo->solverRootFinding = 1; | |
| 1395 | |||
| 1396 | /* | ||
| 1397 | * Determine the next target simulation time step. | ||
| 1398 | * | ||
| 1399 | * If the solver is using a non-equidistant grid: | ||
| 1400 | * → The target time is the minimum of the next sample event time | ||
| 1401 | * and the overall stop time. | ||
| 1402 | * Otherwise (equidistant grid): | ||
| 1403 | * → Start from the current time plus the step size, | ||
| 1404 | * but cap it by the stop time and the next scheduled event time. | ||
| 1405 | */ | ||
| 1406 | ✗ | if (solverInfo->solverNoEquidistantGrid) { | |
| 1407 | // Non-equidistant grid: next step is driven by the nearest sample event. | ||
| 1408 | ✗ | targetTime = fmin(data->simulationInfo->nextSampleEvent, stopTime); | |
| 1409 | } else { | ||
| 1410 | // Equidistant output grid: targetTime set to the next output time. | ||
| 1411 | ✗ | targetTime = solverInfo->currentTime + solverInfo->currentStepSize; | |
| 1412 | |||
| 1413 | // Ensure we don't run past the stop time. | ||
| 1414 | ✗ | targetTime = fmin(targetTime, stopTime); | |
| 1415 | |||
| 1416 | // Also ensure we don't skip over an event time. | ||
| 1417 | ✗ | targetTime = fmin(gbData->eventTime, targetTime); | |
| 1418 | } | ||
| 1419 | |||
| 1420 | ✗ | if (gbData->multi_rate) { | |
| 1421 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "Start gbode (birate integration) from %g to %g", | |
| 1422 | solverInfo->currentTime, targetTime); | ||
| 1423 | } else { | ||
| 1424 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "Start gbode (single-rate integration) from %g to %g", | |
| 1425 | solverInfo->currentTime, targetTime); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | ✗ | gbData->eventHappened = solverInfo->didEventStep || gbData->isFirstStep; | |
| 1429 | |||
| 1430 | /* | ||
| 1431 | * Handle step initialization after an event step or at the very first solver step. | ||
| 1432 | * | ||
| 1433 | * This section ensures that the solver’s time, step size, and related buffers | ||
| 1434 | * are correctly initialized before proceeding with integration. | ||
| 1435 | */ | ||
| 1436 | ✗ | if (solverInfo->didEventStep || gbData->isFirstStep) { | |
| 1437 | ✗ | if (gbData->noRestart && !gbData->isFirstStep) { | |
| 1438 | /* | ||
| 1439 | * Case: No restart requested after event (-noRestart flag set) | ||
| 1440 | * and we are not at the very first step. | ||
| 1441 | * → Continue from the right boundary of the last interval | ||
| 1442 | * using the optimal step size determined earlier. | ||
| 1443 | */ | ||
| 1444 | ✗ | gbData->time = gbData->timeRight; | |
| 1445 | ✗ | gbData->stepSize = gbData->optStepSize; | |
| 1446 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, | |
| 1447 | "Initial step size = %e at time %g", | ||
| 1448 | gbData->stepSize, gbData->time); | ||
| 1449 | } else { | ||
| 1450 | /* | ||
| 1451 | * Case: Either restart is allowed OR this is the very first solver step. | ||
| 1452 | * → Recalculate the initial step size. | ||
| 1453 | * → Reset the ring buffer and solver statistics. | ||
| 1454 | * → Initialize gbData->timeRight, gbData->yRight, and gbData->kRight. | ||
| 1455 | */ | ||
| 1456 | ✗ | getInitStepSize(data, threadData, gbData, solverInfo); | |
| 1457 | ✗ | gbode_init(data, threadData, solverInfo); | |
| 1458 | } | ||
| 1459 | |||
| 1460 | // Mark initialization as complete for this step | ||
| 1461 | ✗ | gbData->isFirstStep = FALSE; | |
| 1462 | ✗ | solverInfo->didEventStep = FALSE; | |
| 1463 | |||
| 1464 | // For multi-rate solvers, propagate event-step flag to the fine-level solver | ||
| 1465 | ✗ | if (gbData->multi_rate) { | |
| 1466 | ✗ | gbData->gbfData->didEventStep = TRUE; | |
| 1467 | } | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | ✗ | debugRingBufferSteps_gb(OMC_LOG_GBODE, gbData->yv, gbData->kv, gbData->tv, nStates, gbData->ringBufferSize); | |
| 1471 | |||
| 1472 | /* | ||
| 1473 | * Case: Constant step size control method. | ||
| 1474 | * Use the solver's current step size directly without adjustment. | ||
| 1475 | */ | ||
| 1476 | ✗ | if (gbData->ctrl_method == GB_CTRL_CNST) { | |
| 1477 | ✗ | gbData->stepSize = solverInfo->currentStepSize; | |
| 1478 | } | ||
| 1479 | |||
| 1480 | |||
| 1481 | ✗ | if (gbData->multi_rate) { | |
| 1482 | // Check if multirate step is necessary, otherwise the correct values are already stored in sData | ||
| 1483 | ✗ | if (gbData->nFastStates > 0 && gbData->gbfData->time < gbData->timeRight && !gbData->gbfData->didEventStep) { | |
| 1484 | // run multirate step | ||
| 1485 | ✗ | gb_step_info = gbodef_main(data, threadData, solverInfo, targetTime); | |
| 1486 | // synchronize y, yRight , kRight and buffer | ||
| 1487 | ✗ | if (fabs(gbData->timeRight - gbData->gbfData->timeRight) < GB_MINIMAL_STEP_SIZE) { | |
| 1488 | ✗ | gbData->time = gbData->timeRight; | |
| 1489 | ✗ | memcpy(gbData->y, gbData->gbfData->y, nStates * sizeof(double)); | |
| 1490 | ✗ | memcpy(gbData->yOld, gbData->y, nStates * sizeof(double)); | |
| 1491 | ✗ | memcpy(gbData->yRight, gbData->gbfData->yRight, nStates * sizeof(double)); | |
| 1492 | ✗ | memcpy(gbData->kRight, gbData->gbfData->kRight, nStates * sizeof(double)); | |
| 1493 | ✗ | memcpy(gbData->err, gbData->gbfData->err, nStates * sizeof(double)); | |
| 1494 | |||
| 1495 | // update buffer, rest has already been rotated | ||
| 1496 | ✗ | gbData->tv[0] = gbData->timeRight; | |
| 1497 | ✗ | memcpy(gbData->yv, gbData->yRight, nStates * sizeof(double)); | |
| 1498 | ✗ | memcpy(gbData->kv, gbData->kRight, nStates * sizeof(double)); | |
| 1499 | |||
| 1500 | /* step is accepted and yOld needs to be updated */ | ||
| 1501 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Accept step from %10g to %10g, error slow states %10g, new stepsize %10g", | |
| 1502 | ✗ | gbData->time - gbData->lastStepSize, gbData->time, gbData->errValues[0], gbData->stepSize); | |
| 1503 | |||
| 1504 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1505 | // dump fast states in file | ||
| 1506 | ✗ | dumpFastStates_gb(gbData, FALSE, gbData->time, 0); | |
| 1507 | } | ||
| 1508 | } | ||
| 1509 | ✗ | if (gb_step_info !=0) { | |
| 1510 | // get out of here, if an event has happend! | ||
| 1511 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 1512 | ✗ | if (gb_step_info > 0) | |
| 1513 | return 0; | ||
| 1514 | else | ||
| 1515 | ✗ | return gb_step_info; | |
| 1516 | } | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | |||
| 1521 | /* Main integration loop, if gbData->time already greater than targetTime, only the | ||
| 1522 | interpolation is necessary for emitting the output variables (see below) */ | ||
| 1523 | ✗ | while (gbData->time < targetTime) { | |
| 1524 | /* | ||
| 1525 | * Limit the step size so we do not overshoot: | ||
| 1526 | * 1. The next sample event time | ||
| 1527 | * 2. The overall simulation stop time | ||
| 1528 | */ | ||
| 1529 | ✗ | gbData->stepSize = fmin(gbData->stepSize, data->simulationInfo->nextSampleEvent - gbData->time); | |
| 1530 | ✗ | gbData->stepSize = fmin(gbData->stepSize, stopTime - gbData->time); | |
| 1531 | // TODO maybe easier to use targetTime | ||
| 1532 | //gbData->stepSize = fmin(gbData->stepSize, targetTime - gbData->time); | ||
| 1533 | |||
| 1534 | // Store the “left-hand side” data from the current step | ||
| 1535 | // for later use during interpolation. | ||
| 1536 | // Copies time, states, and derivatives from the “right” (current step) | ||
| 1537 | // to the “left” (previous step). | ||
| 1538 | // FIXME is this comment correct? | ||
| 1539 | ✗ | gbData->timeLeft = gbData->timeRight; | |
| 1540 | ✗ | memcpy(gbData->yLeft, gbData->yRight, nStates * sizeof(double)); | |
| 1541 | ✗ | memcpy(gbData->kLeft, gbData->kRight, nStates * sizeof(double)); | |
| 1542 | |||
| 1543 | // debug the ring buffer changes of the states and derivatives during integration | ||
| 1544 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1545 | // debug the changes of the states and derivatives during integration | ||
| 1546 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "States and derivatives at left hand side:"); | |
| 1547 | ✗ | printVector_gb(OMC_LOG_GBODE, "yL", gbData->yLeft, nStates, gbData->timeLeft); | |
| 1548 | ✗ | printVector_gb(OMC_LOG_GBODE, "kL", gbData->kLeft, nStates, gbData->timeLeft); | |
| 1549 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1550 | } | ||
| 1551 | |||
| 1552 | // Loop will be performed until the error estimate for all states fullfills the | ||
| 1553 | // given tolerance | ||
| 1554 | do { | ||
| 1555 | // set error to INFINITY, in case we break / continue early | ||
| 1556 | err = INFINITY; | ||
| 1557 | |||
| 1558 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER_V)) { | |
| 1559 | // debug ring buffer of the states and derivatives during integration | ||
| 1560 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "States and derivatives of the ring buffer:"); | |
| 1561 | ✗ | for (int i=0; i<gbData->ringBufferSize; i++) { | |
| 1562 | ✗ | printVector_gb(OMC_LOG_SOLVER_V, "y", gbData->yv + i * nStates, nStates, gbData->tv[i]); | |
| 1563 | } | ||
| 1564 | ✗ | for (int i=0; i<gbData->ringBufferSize; i++) { | |
| 1565 | ✗ | printVector_gb(OMC_LOG_SOLVER_V, "k", gbData->kv + i * nStates, nStates, gbData->tv[i]); | |
| 1566 | } | ||
| 1567 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1568 | } | ||
| 1569 | |||
| 1570 | // Perform one integration step. New error estimators write |error| directly to errest; | ||
| 1571 | // Richardson and MS methods still write a signed error estimate to yt. | ||
| 1572 | // Choose the integration method based on the tableau: | ||
| 1573 | // - If Richardson extrapolation is enabled, use gbode_richardson. | ||
| 1574 | // - Otherwise, use the default step function stored in gbData->step_fun. | ||
| 1575 | ✗ | if (gbData->tableau->richardson) { | |
| 1576 | ✗ | gb_step_info = gbode_richardson(data, threadData, solverInfo); | |
| 1577 | } else { | ||
| 1578 | ✗ | gb_step_info = gbData->step_fun(data, threadData, solverInfo); | |
| 1579 | } | ||
| 1580 | |||
| 1581 | // debug the approximations after performed step | ||
| 1582 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1583 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Approximations after step calculation:"); | |
| 1584 | ✗ | printVector_gb(OMC_LOG_GBODE, " y", gbData->y, nStates, gbData->time + gbData->stepSize); | |
| 1585 | ✗ | if (gbData->tableau->richardson || gbData->type == MS_TYPE_IMPLICIT) { | |
| 1586 | ✗ | printVector_gb(OMC_LOG_GBODE, "yt", gbData->yt, nStates, gbData->time + gbData->stepSize); | |
| 1587 | } else { | ||
| 1588 | ✗ | printVector_gb(OMC_LOG_GBODE, "errest", gbData->errest, nStates, gbData->time + gbData->stepSize); | |
| 1589 | } | ||
| 1590 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1591 | } | ||
| 1592 | |||
| 1593 | // Error handling for failed integration step: | ||
| 1594 | // If the step calculation failed (gb_step_info != 0), try reducing the step size by half and retry. | ||
| 1595 | // | ||
| 1596 | // Actions taken on failure: | ||
| 1597 | // - Increment convergence failure statistics counter. | ||
| 1598 | // - Print an informational message about the failure and the current simulation time. | ||
| 1599 | // | ||
| 1600 | // If the solver is using a constant step size control method: | ||
| 1601 | // - Abort the simulation and print an error message since no step size adjustment is possible. | ||
| 1602 | // | ||
| 1603 | // Otherwise (adaptive step size control): | ||
| 1604 | // - Halve the current step size. | ||
| 1605 | // - If multi-rate integration is active and detailed logging is enabled: | ||
| 1606 | // - Reset error metrics for slow, fast, and internal components. | ||
| 1607 | // - Dump the fast states to a file for diagnostics. | ||
| 1608 | // - Print the new reduced step size being tried. | ||
| 1609 | // | ||
| 1610 | // If the step size becomes smaller than the minimal allowed threshold: | ||
| 1611 | // - Abort the simulation with an error indicating minimum step size reached without acceptable error. | ||
| 1612 | // | ||
| 1613 | // If none of the abort conditions occur, the loop continues to retry with the reduced step size. | ||
| 1614 | ✗ | if (gb_step_info != 0) { | |
| 1615 | ✗ | gbData->stats.nConvergenceTestFailures++; | |
| 1616 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER)) infoStreamPrint(OMC_LOG_SOLVER, 0, "gbode_main: Failed to calculate step at time = %5g with step size h = %5g.", gbData->time, gbData->stepSize); | |
| 1617 | ✗ | if (gbData->ctrl_method == GB_CTRL_CNST) { | |
| 1618 | ✗ | errorStreamPrint(OMC_LOG_STDOUT, 0, "Simulation aborted since gbode is running with fixed step size and step calculation has failed at time = %5g with step size h = %5g.", gbData->time, gbData->stepSize); | |
| 1619 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 1620 | ✗ | return -1; | |
| 1621 | } else { | ||
| 1622 | ✗ | if (gbData->eventHappened) { | |
| 1623 | ✗ | gbData->stepSize *= 0.1; // event or initial step rejection: reduce step size to 10% of previous step size | |
| 1624 | } else { | ||
| 1625 | ✗ | gbData->stepSize *= 0.5; // standard rejection: reduce the step size by half to attempt a more accurate integration in the next iteration | |
| 1626 | } | ||
| 1627 | |||
| 1628 | ✗ | if (gbData->multi_rate && OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1629 | ✗ | gbData->err_slow = 0; | |
| 1630 | ✗ | gbData->err_fast = 0; | |
| 1631 | ✗ | gbData->err_int = 0; | |
| 1632 | // dump fast states in file | ||
| 1633 | ✗ | dumpFastStates_gb(gbData, FALSE, gbData->time + gbData->stepSize, 3); | |
| 1634 | } | ||
| 1635 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER)) infoStreamPrint(OMC_LOG_SOLVER, 0, "Try half of the step size = %g", gbData->stepSize); | |
| 1636 | ✗ | if (gbData->stepSize < GB_MINIMAL_STEP_SIZE) { | |
| 1637 | ✗ | errorStreamPrint(OMC_LOG_STDOUT, 0, "Simulation aborted! Minimum step size %g reached, but error still to large.", GB_MINIMAL_STEP_SIZE); | |
| 1638 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 1639 | ✗ | return -1; | |
| 1640 | } | ||
| 1641 | ✗ | continue; | |
| 1642 | } | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | // Calculate error estimators and tolerance scaling for each state variable | ||
| 1646 | // Compute error tolerance for the i-th state based on relative and absolute tolerances: | ||
| 1647 | // errtol = Rtol * max(|old state|, |current state|) + Atol * |nominal(state)| | ||
| 1648 | ✗ | tol = gbScaledErrorTolerance(data->simulationInfo->tolerance, gbData->tableau->order_b, | |
| 1649 | ✗ | gbData->currentErrorOrder, gbData->tableau->richardson); | |
| 1650 | |||
| 1651 | ✗ | for (i = 0, err=0; i < nStates; i++) { | |
| 1652 | // calculate corresponding values for the error estimator and step size control | ||
| 1653 | ✗ | gbData->errtol[i] = tol * gbData->nominals[i] + fmax(fabs(gbData->yOld[i]), fabs(gbData->y[i])) * tol; | |
| 1654 | ✗ | if (gbData->tableau->richardson || gbData->type == MS_TYPE_IMPLICIT) { | |
| 1655 | ✗ | gbData->errest[i] = fabs(gbData->yt[i]); | |
| 1656 | } | ||
| 1657 | ✗ | gbData->err[i] = gbData->tableau->fac * gbData->errest[i] / gbData->errtol[i]; | |
| 1658 | ✗ | err += gbData->err[i] * gbData->err[i]; | |
| 1659 | } | ||
| 1660 | |||
| 1661 | ✗ | err = sqrt(err / (double) nStates); | |
| 1662 | |||
| 1663 | ✗ | if (gbData->multi_rate) { | |
| 1664 | // Multi-rate integration enabled: | ||
| 1665 | |||
| 1666 | // Calculate the error threshold for slow states (used to separate slow and fast states). | ||
| 1667 | ✗ | err_states = getErrorThreshold(gbData); | |
| 1668 | err = err_states; | ||
| 1669 | |||
| 1670 | // Classify states into fast and slow based on the scaled error: | ||
| 1671 | // - States with error >= 1 are considered fast. | ||
| 1672 | // - States with error < 1 are considered slow. | ||
| 1673 | // | ||
| 1674 | // Keep track of the count of fast and slow states, | ||
| 1675 | // and record the maximum error encountered for each group. | ||
| 1676 | ✗ | gbData->nFastStates = 0; | |
| 1677 | ✗ | gbData->nSlowStates = 0; | |
| 1678 | ✗ | gbData->err_slow = 0; | |
| 1679 | ✗ | gbData->err_fast = 0; | |
| 1680 | ✗ | gbData->err_int = 0; | |
| 1681 | |||
| 1682 | ✗ | for (i = 0; i < gbData->nStates; i++) { | |
| 1683 | ✗ | if (gbData->err[i] >= 1) { | |
| 1684 | ✗ | gbData->fastStatesIdx[gbData->nFastStates] = i; | |
| 1685 | ✗ | gbData->nFastStates++; | |
| 1686 | ✗ | gbData->err_fast = fmax(gbData->err_fast, gbData->err[i]); | |
| 1687 | } else { | ||
| 1688 | ✗ | gbData->slowStatesIdx[gbData->nSlowStates] = i; | |
| 1689 | ✗ | gbData->nSlowStates++; | |
| 1690 | ✗ | gbData->err_slow = fmax(gbData->err_slow, gbData->err[i]); | |
| 1691 | } | ||
| 1692 | } | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | // Reject the current integration step if the estimated error exceeds the tolerance, | ||
| 1696 | // and if the solver is not running with a fixed (constant) step size. | ||
| 1697 | ✗ | if (err > 1 && gbData->ctrl_method != GB_CTRL_CNST) { | |
| 1698 | |||
| 1699 | // Logging | ||
| 1700 | ✗ | if (gbData->multi_rate) { | |
| 1701 | // For multi-rate integration, print detailed info about the rejected step, | ||
| 1702 | // including the slow states' error and the reduced step size. | ||
| 1703 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, | |
| 1704 | "Reject step from %.16g to %.16g, error slow states %.16g, new stepsize %.16g", | ||
| 1705 | ✗ | gbData->time, gbData->time + gbData->stepSize, err, gbData->stepSize * 0.5); | |
| 1706 | |||
| 1707 | // If verbose solver logging is enabled, print detailed error information for debugging. | ||
| 1708 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER_V)) { | |
| 1709 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "Error of the states: threshold = %15.10g", err_states); | |
| 1710 | ✗ | printVector_gb(OMC_LOG_SOLVER_V, "y", gbData->y, nStates, gbData->time + gbData->stepSize); | |
| 1711 | ✗ | printVector_gb(OMC_LOG_SOLVER_V, "er", gbData->err, nStates, gbData->time + gbData->stepSize); | |
| 1712 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1713 | } | ||
| 1714 | |||
| 1715 | // If GBODE state logging is active, dump fast state data to file for further analysis. | ||
| 1716 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1717 | ✗ | gbData->err_slow = err; // FIXME should this really only happen when logging is active? | |
| 1718 | ✗ | dumpFastStates_gb(gbData, FALSE, gbData->time + gbData->stepSize, 1); | |
| 1719 | } | ||
| 1720 | } else { | ||
| 1721 | // For single-rate integration, print basic rejection info with the error and new step size. | ||
| 1722 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, | |
| 1723 | "Reject step from %.16g to %.16g, error %.16g, new stepsize %.16g", | ||
| 1724 | ✗ | gbData->time, gbData->time + gbData->stepSize, err, gbData->stepSize * 0.5); | |
| 1725 | } | ||
| 1726 | |||
| 1727 | // Increment the counter for error test failures. | ||
| 1728 | ✗ | gbData->stats.nErrorTestFailures++; | |
| 1729 | |||
| 1730 | ✗ | if (gbData->eventHappened) | |
| 1731 | { | ||
| 1732 | // event or initial step rejection: reduce step size to 10% of previous step size | ||
| 1733 | ✗ | gbData->stepSize *= 0.1; | |
| 1734 | } | ||
| 1735 | else | ||
| 1736 | { | ||
| 1737 | // standard rejection: reduce the step size by half to attempt a more accurate integration in the next iteration. | ||
| 1738 | ✗ | gbData->stepSize *= 0.5; | |
| 1739 | } | ||
| 1740 | |||
| 1741 | // Restart the integration loop with the smaller step size. | ||
| 1742 | ✗ | continue; | |
| 1743 | } | ||
| 1744 | |||
| 1745 | // Store right-hand side values for later interpolation, including event handling: | ||
| 1746 | // - Update gbData->timeRight to the time at the end of the current step. | ||
| 1747 | // - Copy current state values (gbData->y) to gbData->yRight. | ||
| 1748 | // | ||
| 1749 | // Update the derivative estimates gbData->kRight: | ||
| 1750 | // - If the tableau does not provide kRight values directly, | ||
| 1751 | // compute them by evaluating the ODE function at timeRight and current states. | ||
| 1752 | // | ||
| 1753 | // Compute interpolation error estimate (gbData->err_int) if either: | ||
| 1754 | // - Solver logging is enabled, or | ||
| 1755 | // - The control method is not constant step size and | ||
| 1756 | // the interpolation method is one of the error-controlled Hermite or dense output. | ||
| 1757 | // | ||
| 1758 | // For multi-rate integration with fast states, compute the interpolation error only | ||
| 1759 | // for the slow states subset; otherwise, consider all states. | ||
| 1760 | ✗ | gbData->timeRight = gbData->time + gbData->stepSize; | |
| 1761 | ✗ | memcpy(gbData->yRight, gbData->y, nStates * sizeof(double)); | |
| 1762 | // update kRight | ||
| 1763 | ✗ | if (!gbData->tableau->isKRightAvailable) { | |
| 1764 | ✗ | sData->timeValue = gbData->timeRight; | |
| 1765 | ✗ | memcpy(sData->realVars, gbData->y, data->modelData->nStates * sizeof(double)); | |
| 1766 | ✗ | gbode_fODE(data, threadData, &(gbData->stats.nCallsODE), NULL); | |
| 1767 | ✗ | memcpy(gbData->kRight, fODE, nStates * sizeof(double)); | |
| 1768 | } | ||
| 1769 | else | ||
| 1770 | { | ||
| 1771 | // last stage of method already provides the vector (we have some tiny error of the Newton iteration though) | ||
| 1772 | ✗ | memcpy(gbData->kRight, &gbData->k[nStates * (nStages - 1)], nStates * sizeof(double)); | |
| 1773 | } | ||
| 1774 | |||
| 1775 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER) || noConst_intWithErrctrl) { | |
| 1776 | ✗ | if (gbData->multi_rate && gbData->nFastStates>0) { | |
| 1777 | ✗ | gbData->err_int = error_interpolation_gb(gbData, gbData->nSlowStates, gbData->slowStatesIdx, tol); | |
| 1778 | } else { | ||
| 1779 | ✗ | gbData->err_int = error_interpolation_gb(gbData, nStates, NULL, tol); | |
| 1780 | } | ||
| 1781 | } | ||
| 1782 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 1783 | // debug the changes of the state values during integration | ||
| 1784 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "Interpolation error of slow states at midpoint:"); | |
| 1785 | ✗ | if (gbData->multi_rate) { | |
| 1786 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "yL", gbData->yLeft, nStates, gbData->timeLeft, gbData->nSlowStates, gbData->slowStatesIdx); | |
| 1787 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "kL", gbData->kLeft, nStates, gbData->timeLeft, gbData->nSlowStates, gbData->slowStatesIdx); | |
| 1788 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "yR", gbData->yRight, nStates, gbData->timeRight, gbData->nSlowStates, gbData->slowStatesIdx); | |
| 1789 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "kR", gbData->kRight, nStates, gbData->timeRight, gbData->nSlowStates, gbData->slowStatesIdx); | |
| 1790 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "e", gbData->errest, nStates, (gbData->timeLeft + gbData->timeRight)/2, gbData->nSlowStates, gbData->slowStatesIdx); | |
| 1791 | } else { | ||
| 1792 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "yL", gbData->yLeft, nStates, gbData->timeLeft); | |
| 1793 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "yR", gbData->yRight, nStates, gbData->timeRight); | |
| 1794 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "kL", gbData->kLeft, nStates, gbData->timeLeft); | |
| 1795 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "kR", gbData->kRight, nStates, gbData->timeRight); | |
| 1796 | ✗ | printVector_gbf(OMC_LOG_GBODE_V, "e", gbData->errest, nStates, (gbData->timeLeft + gbData->timeRight)/2, gbData->nSlowStates, gbData->slowStatesIdx); | |
| 1797 | } | ||
| 1798 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 1799 | } | ||
| 1800 | |||
| 1801 | /* TODO: do we even need this condition anymore? */ | ||
| 1802 | |||
| 1803 | // Adjust the error estimate for step size control by incorporating interpolation error. | ||
| 1804 | // This is done only if: | ||
| 1805 | // - The current error estimate is greater than 0.01, | ||
| 1806 | // - The number of retries is less than 4, | ||
| 1807 | // - The solver is not using a constant step size, | ||
| 1808 | // - And the interpolation method supports error control (Hermite or dense output). | ||
| 1809 | // | ||
| 1810 | // The error used for step size control is set to the maximum of the interpolation error and the current error. | ||
| 1811 | // if ((err > 1e-2) && (retries < 4) && noConst_intWithErrctrl && gbData->multi_rate) { | ||
| 1812 | // err = fmax(gbData->err_int, err); | ||
| 1813 | // } | ||
| 1814 | |||
| 1815 | // Reject the current integration step if the interpolation error exceeds the tolerance, | ||
| 1816 | // provided that the solver is not running with a fixed step size and interpolation error control is enabled. | ||
| 1817 | // | ||
| 1818 | // On rejection: | ||
| 1819 | // - Increment the retry counter and error test failure statistics. | ||
| 1820 | // - Reduce the step size by half to attempt a more accurate integration. | ||
| 1821 | // - Abort the simulation if the step size falls below the minimal allowed threshold. | ||
| 1822 | // | ||
| 1823 | // Logging differs for multi-rate and single-rate integration: | ||
| 1824 | // - For multi-rate, log errors of slow states and interpolation error. | ||
| 1825 | // - For single-rate, log the overall error and interpolation error. | ||
| 1826 | // | ||
| 1827 | // If multi-rate integration and GBODE state logging is active, dump fast states for diagnostics. | ||
| 1828 | // | ||
| 1829 | // If the step is accepted, reset the retry counter. | ||
| 1830 | ✗ | if (err > 1 && noConst_intWithErrctrl) { | |
| 1831 | |||
| 1832 | retries++; | ||
| 1833 | ✗ | gbData->stats.nErrorTestFailures++; | |
| 1834 | ✗ | gbData->stepSize *= 0.5; | |
| 1835 | |||
| 1836 | ✗ | if (gbData->stepSize < GB_MINIMAL_STEP_SIZE) { | |
| 1837 | ✗ | errorStreamPrint(OMC_LOG_STDOUT, 0, | |
| 1838 | "Simulation aborted! Minimum step size %g reached, but interpolation error still too large.", | ||
| 1839 | GB_MINIMAL_STEP_SIZE); | ||
| 1840 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 1841 | ✗ | return -1; | |
| 1842 | } | ||
| 1843 | |||
| 1844 | ✗ | if (gbData->multi_rate) { | |
| 1845 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, | |
| 1846 | "Reject step from %.16g to %.16g, error slow states %.16g, error interpolation %.16g, new stepsize %.16g", | ||
| 1847 | ✗ | gbData->time, gbData->time + gbData->stepSize, gbData->err_slow, gbData->err_int, gbData->stepSize); | |
| 1848 | } else { | ||
| 1849 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, | |
| 1850 | "Reject step from %.16g to %.16g, error %.16g, interpolation error %.16g, new stepsize %.16g", | ||
| 1851 | ✗ | gbData->time, gbData->time + gbData->stepSize, err_states, gbData->err_int, gbData->stepSize); | |
| 1852 | } | ||
| 1853 | |||
| 1854 | ✗ | if (gbData->multi_rate && OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1855 | // Dump fast states to file for further analysis after step rejection. | ||
| 1856 | ✗ | dumpFastStates_gb(gbData, FALSE, gbData->time + gbData->stepSize, 2); | |
| 1857 | } | ||
| 1858 | |||
| 1859 | ✗ | continue; | |
| 1860 | } else { | ||
| 1861 | // Reset retries counter if the step was accepted. | ||
| 1862 | retries = 0; | ||
| 1863 | } | ||
| 1864 | |||
| 1865 | /* Step is accepted from here on, as err <= 1 */ | ||
| 1866 | |||
| 1867 | /* remember last time values for dense output extrapolation with yLast, kLast */ | ||
| 1868 | ✗ | gbData->extrapolationBaseTime = gbData->time; | |
| 1869 | ✗ | gbData->extrapolationStepSize = gbData->stepSize; | |
| 1870 | ✗ | gbData->eventHappened = FALSE; | |
| 1871 | ✗ | gbData->didFastStep = FALSE; | |
| 1872 | |||
| 1873 | /* remember kLast and yLast for dense output extrapolation */ | ||
| 1874 | ✗ | memcpy(gbData->kLast, gbData->k, nStates * nStages * sizeof(double)); | |
| 1875 | ✗ | memcpy(gbData->yLast, gbData->yOld, nStates * sizeof(double)); | |
| 1876 | |||
| 1877 | // Rotate the error and step size ring buffers to make room for the latest values. | ||
| 1878 | // The oldest entries are shifted one position towards the end, | ||
| 1879 | // and the newest error and step size values are stored at the front (index 0). | ||
| 1880 | // FIXME use actual ring buffer instead of moving data around! | ||
| 1881 | ✗ | for (i = (gbData->ringBufferSize - 1); i > 0; i--) { | |
| 1882 | ✗ | gbData->errValues[i] = gbData->errValues[i - 1]; | |
| 1883 | ✗ | gbData->stepSizeValues[i] = gbData->stepSizeValues[i - 1]; | |
| 1884 | } | ||
| 1885 | // Store the current error and step size at the beginning of the buffers. | ||
| 1886 | ✗ | gbData->errValues[0] = err; | |
| 1887 | ✗ | gbData->stepSizeValues[0] = gbData->stepSize; | |
| 1888 | |||
| 1889 | // Update the step size using the step size controller | ||
| 1890 | ✗ | gbData->lastStepSize = gbData->stepSize; // Save the current step size before updating | |
| 1891 | // Calculate a new step size based on recent error and step size history, | ||
| 1892 | // the method’s error order, and the control method in use | ||
| 1893 | ✗ | gbData->stepSize *= GenericController(gbData->errValues, gbData->stepSizeValues, gbData->currentErrorOrder, gbData->ctrl_method); | |
| 1894 | |||
| 1895 | // Ensure the new step size does not exceed the user-defined maximum step size (if set) | ||
| 1896 | ✗ | if (gbData->maxStepSize > 0 && gbData->maxStepSize < gbData->stepSize) | |
| 1897 | ✗ | gbData->stepSize = gbData->maxStepSize; | |
| 1898 | |||
| 1899 | // Store the optimized step size for further use | ||
| 1900 | ✗ | gbData->optStepSize = gbData->stepSize; | |
| 1901 | |||
| 1902 | ✗ | if (gbData->multi_rate) { | |
| 1903 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1904 | // debug the changes of the state values during integration | ||
| 1905 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "States and derivatives at right hand side:"); | |
| 1906 | ✗ | printVector_gb(OMC_LOG_GBODE, "yR", gbData->yRight, nStates, gbData->timeRight); | |
| 1907 | ✗ | printVector_gb(OMC_LOG_GBODE, "kR", gbData->kRight, nStates, gbData->timeRight); | |
| 1908 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1909 | } | ||
| 1910 | |||
| 1911 | ✗ | if (gbData->nFastStates > 0) { | |
| 1912 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1913 | // debug the error of the states and derivatives after outer integration | ||
| 1914 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Error of the states before inner integration: threshold = %15.10g", err_states); | |
| 1915 | ✗ | printVector_gb(OMC_LOG_GBODE, "er", gbData->err, nStates, gbData->timeRight); | |
| 1916 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1917 | } | ||
| 1918 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 1919 | // dump fast states in file | ||
| 1920 | ✗ | dumpFastStates_gb(gbData, FALSE, gbData->time + gbData->lastStepSize, -1); | |
| 1921 | } | ||
| 1922 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Refine step from %10g to %10g, error fast states %10g, error interpolation %10g, new stepsize %10g", | |
| 1923 | ✗ | gbData->time, gbData->time + gbData->lastStepSize, gbData->err_fast, error_interpolation_gb(gbData, nStates, NULL, tol), gbData->stepSize); | |
| 1924 | // run multirate step | ||
| 1925 | ✗ | gb_step_info = gbodef_main(data, threadData, solverInfo, targetTime); | |
| 1926 | // synchronize relevant information | ||
| 1927 | ✗ | if (fabs(gbData->timeRight - gbData->gbfData->timeRight) < GB_MINIMAL_STEP_SIZE) { | |
| 1928 | ✗ | memcpy(gbData->y, gbData->gbfData->y, nStates * sizeof(double)); | |
| 1929 | ✗ | memcpy(gbData->yRight, gbData->gbfData->yRight, nStates * sizeof(double)); | |
| 1930 | ✗ | memcpy(gbData->err, gbData->gbfData->err, nStates * sizeof(double)); | |
| 1931 | ✗ | sData->timeValue = gbData->timeRight; | |
| 1932 | ✗ | memcpy(sData->realVars, gbData->yRight, data->modelData->nStates * sizeof(double)); | |
| 1933 | ✗ | gbode_fODE(data, threadData, &(gbData->stats.nCallsODE), NULL); | |
| 1934 | ✗ | memcpy(gbData->kRight, fODE, nStates * sizeof(double)); | |
| 1935 | } | ||
| 1936 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Refined step from %10g to %10g, error fast states %10g, error interpolation %10g, new stepsize %10g", | |
| 1937 | ✗ | gbData->time, gbData->time + gbData->lastStepSize, gbData->err_fast, error_interpolation_gb(gbData, nStates, NULL, tol), gbData->stepSize); | |
| 1938 | ✗ | if (gb_step_info !=0) { | |
| 1939 | // get out of here, if an event has happend! | ||
| 1940 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 1941 | ✗ | if (gb_step_info>0) | |
| 1942 | return 0; | ||
| 1943 | else | ||
| 1944 | ✗ | return gb_step_info; | |
| 1945 | } | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 1949 | // debug the error of the states and derivatives after outer integration | ||
| 1950 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "Error of the states: threshold = %15.10g", err_states); | |
| 1951 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "er", gbData->err, nStates, gbData->timeRight); | |
| 1952 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 1953 | } | ||
| 1954 | } | ||
| 1955 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_V)) { | |
| 1956 | // debug ring buffer for the states and derviatives of the states | ||
| 1957 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 1, "Ring buffer during steps of integration"); | |
| 1958 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 0, "Old value:"); | |
| 1959 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "y", gbData->yOld, nStates, gbData->time); | |
| 1960 | ✗ | debugRingBuffer_gb(OMC_LOG_GBODE_V, gbData->x, gbData->k, nStates, gbData->tableau, gbData->time, gbData->lastStepSize); | |
| 1961 | ✗ | infoStreamPrint(OMC_LOG_GBODE_V, 0, "New value:"); | |
| 1962 | ✗ | printVector_gb(OMC_LOG_GBODE_V, "y", gbData->y, nStates, gbData->time + gbData->lastStepSize); | |
| 1963 | ✗ | messageClose(OMC_LOG_GBODE_V); | |
| 1964 | } | ||
| 1965 | ✗ | } while (!isfinite(err) || (err > 1 && gbData->ctrl_method != GB_CTRL_CNST)); | |
| 1966 | |||
| 1967 | // count processed steps | ||
| 1968 | ✗ | gbData->stats.nStepsTaken++; | |
| 1969 | |||
| 1970 | // debug the changes of the state values during integration | ||
| 1971 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) { | |
| 1972 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "States and derivatives at right hand side:"); | |
| 1973 | ✗ | printVector_gb(OMC_LOG_GBODE, "yR", gbData->yRight, nStates, gbData->timeRight); | |
| 1974 | ✗ | printVector_gb(OMC_LOG_GBODE, "kR", gbData->kRight, nStates, gbData->timeRight); | |
| 1975 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 1976 | } | ||
| 1977 | |||
| 1978 | // If not using multi-rate integration, or if multi-rate is active but the fast integration time | ||
| 1979 | // is behind the main integrator time, then check for events. | ||
| 1980 | ✗ | if (!gbData->multi_rate || (gbData->multi_rate && gbData->gbfData->time < gbData->time)) { | |
| 1981 | |||
| 1982 | // Check for any events occurring between the previous accepted time (timeLeft) and current time (timeRight). | ||
| 1983 | // The function returns the event time if an event is detected, and sets foundEvent accordingly. | ||
| 1984 | ✗ | foundEvent = checkForEvents(data, threadData, solverInfo, gbData->timeLeft, gbData->yLeft, gbData->timeRight, gbData->yRight, FALSE, &(gbData->eventTime)); | |
| 1985 | |||
| 1986 | ✗ | if (foundEvent) { | |
| 1987 | // Clear any pending events in the solver's event list before handling the new event. | ||
| 1988 | ✗ | listClear(solverInfo->eventLst); | |
| 1989 | |||
| 1990 | // Update the current integration time to the event time. | ||
| 1991 | ✗ | gbData->time = gbData->eventTime; | |
| 1992 | ✗ | gbData->eventHappened = TRUE; | |
| 1993 | |||
| 1994 | // Perform interpolation at the event time to estimate states and derivatives accurately. | ||
| 1995 | ✗ | gb_interpolation(gbData->interpolation, | |
| 1996 | gbData->timeLeft, gbData->yLeft, gbData->kLeft, | ||
| 1997 | gbData->timeRight, gbData->yRight, gbData->kRight, | ||
| 1998 | gbData->time, gbData->yOld, | ||
| 1999 | nStates, NULL, nStates, gbData->tableau, | ||
| 2000 | gbData->x, gbData->k); | ||
| 2001 | |||
| 2002 | // Adjust targetTime to not exceed the detected event time, | ||
| 2003 | // ensuring the integrator stops exactly at the event. | ||
| 2004 | ✗ | targetTime = fmin(targetTime, gbData->eventTime); | |
| 2005 | |||
| 2006 | // Exit the integration loop early since an event was detected. | ||
| 2007 | ✗ | break; | |
| 2008 | } | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | ✗ | if (gbData->multi_rate) { | |
| 2012 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Accept step from %.16g to %.16g, error slow states %.16g, error interpolation %.16g, new stepsize %.16g", | |
| 2013 | gbData->timeLeft, gbData->timeRight, err_states, gbData->err_int, gbData->stepSize); | ||
| 2014 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE_STATES)) { | |
| 2015 | // dump fast states in file | ||
| 2016 | ✗ | dumpFastStates_gb(gbData, FALSE, gbData->time, 0); | |
| 2017 | } | ||
| 2018 | } else { | ||
| 2019 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Accept step from %.16g to %.16g, error %.16g interpolation error %.16g, new stepsize %16g", | |
| 2020 | gbData->timeLeft, gbData->timeRight, err_states, gbData->err_int, gbData->stepSize); | ||
| 2021 | |||
| 2022 | } | ||
| 2023 | |||
| 2024 | /* update time with performed stepSize */ | ||
| 2025 | ✗ | gbData->time = gbData->timeRight; | |
| 2026 | |||
| 2027 | /* step is accepted and yOld needs to be updated */ | ||
| 2028 | ✗ | memcpy(gbData->yOld, gbData->yRight, nStates * sizeof(double)); | |
| 2029 | |||
| 2030 | // Rotate ring buffer | ||
| 2031 | ✗ | for (i = (gbData->ringBufferSize - 1); i > 0 ; i--) { | |
| 2032 | ✗ | gbData->tv[i] = gbData->tv[i - 1]; | |
| 2033 | ✗ | memcpy(gbData->yv + i * nStates, gbData->yv + (i - 1) * nStates, nStates * sizeof(double)); | |
| 2034 | ✗ | memcpy(gbData->kv + i * nStates, gbData->kv + (i - 1) * nStates, nStates * sizeof(double)); | |
| 2035 | } | ||
| 2036 | |||
| 2037 | // update new values | ||
| 2038 | ✗ | gbData->tv[0] = gbData->timeRight; | |
| 2039 | ✗ | memcpy(gbData->yv, gbData->yRight, nStates * sizeof(double)); | |
| 2040 | ✗ | memcpy(gbData->kv, gbData->kRight, nStates * sizeof(double)); | |
| 2041 | |||
| 2042 | ✗ | debugRingBufferSteps_gb(OMC_LOG_GBODE_V, gbData->yv, gbData->kv, gbData->tv, nStates, gbData->ringBufferSize); | |
| 2043 | |||
| 2044 | /* emit step, if solverNoEquidistantGrid is selected */ | ||
| 2045 | ✗ | if (solverInfo->solverNoEquidistantGrid && (!gbData->multi_rate || (gbData->multi_rate && gbData->gbfData->time<gbData->time))) { | |
| 2046 | ✗ | sData->timeValue = gbData->time; | |
| 2047 | ✗ | solverInfo->currentTime = sData->timeValue; | |
| 2048 | ✗ | memcpy(sData->realVars, gbData->y, nStates * sizeof(double)); | |
| 2049 | // log the emitted result | ||
| 2050 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)){ | |
| 2051 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Emit result:"); | |
| 2052 | ✗ | printVector_gb(OMC_LOG_GBODE, " y", sData->realVars, nStates, sData->timeValue); | |
| 2053 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 2054 | } | ||
| 2055 | break; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | // stop, if simulation nearly reached stopTime | ||
| 2059 | ✗ | if (stopTime - gbData->time < GB_MINIMAL_STEP_SIZE) { | |
| 2060 | ✗ | gbData->time = stopTime; | |
| 2061 | ✗ | break; | |
| 2062 | } | ||
| 2063 | } // end of while-loop (gbData->time < targetTime) | ||
| 2064 | |||
| 2065 | ✗ | if (gbData->eventTime == targetTime) { | |
| 2066 | |||
| 2067 | ✗ | if (!solverInfo->solverNoEquidistantGrid) { | |
| 2068 | ✗ | foundEvent = checkForEvents(data, threadData, solverInfo, gbData->eventTime, gbData->yOld, gbData->eventTime, gbData->yOld, FALSE, &(gbData->eventTime)); | |
| 2069 | } | ||
| 2070 | |||
| 2071 | ✗ | solverInfo->currentTime = gbData->time; | |
| 2072 | ✗ | sData->timeValue = gbData->time; | |
| 2073 | ✗ | memcpy(sData->realVars, gbData->yOld, nStates * sizeof(double)); | |
| 2074 | |||
| 2075 | // if noRestart is set, the right hand side values are stored | ||
| 2076 | ✗ | if (gbData->noRestart) { | |
| 2077 | ✗ | gbData->timeRight = gbData->time; | |
| 2078 | ✗ | memcpy(gbData->yRight, gbData->yOld, nStates * sizeof(double)); | |
| 2079 | ✗ | gbode_fODE(data, threadData, &(gbData->stats.nCallsODE), NULL); | |
| 2080 | ✗ | memcpy(gbData->kRight, fODE, nStates * sizeof(double)); | |
| 2081 | } | ||
| 2082 | |||
| 2083 | /* write statistics to the solverInfo data structure */ | ||
| 2084 | ✗ | memcpy(&solverInfo->solverStatsTmp, &gbData->stats, sizeof(SOLVERSTATS)); | |
| 2085 | |||
| 2086 | // log the emitted result | ||
| 2087 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)){ | |
| 2088 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Emit result (single-rate integration):"); | |
| 2089 | ✗ | printVector_gb(OMC_LOG_GBODE, " y", sData->realVars, nStates, sData->timeValue); | |
| 2090 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 2091 | } | ||
| 2092 | |||
| 2093 | ✗ | listClear(solverInfo->eventLst); | |
| 2094 | ✗ | gbData->eventTime = DBL_MAX; // reset event time, if eventTime is reached | |
| 2095 | |||
| 2096 | // return to solver main routine for proper event handling (iteration) | ||
| 2097 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 2098 | |||
| 2099 | ✗ | return 0; | |
| 2100 | } | ||
| 2101 | |||
| 2102 | ✗ | if (!solverInfo->solverNoEquidistantGrid) { | |
| 2103 | /* Integrator does large steps and needs to interpolate results with respect to the output grid */ | ||
| 2104 | ✗ | sData->timeValue = fmin(solverInfo->currentTime + solverInfo->currentStepSize, targetTime); | |
| 2105 | ✗ | sData->timeValue = fmin(sData->timeValue, stopTime); | |
| 2106 | ✗ | solverInfo->currentTime = sData->timeValue; | |
| 2107 | |||
| 2108 | ✗ | if (gbData->multi_rate) { | |
| 2109 | // if the inner integration has not been started, the outer values need to be emitted | ||
| 2110 | ✗ | if (gbData->gbfData->time >= sData->timeValue) { | |
| 2111 | ✗ | gb_interpolation(gbData->interpolation, | |
| 2112 | gbData->timeLeft, gbData->yLeft, gbData->kLeft, | ||
| 2113 | gbData->timeRight, gbData->yRight, gbData->kRight, | ||
| 2114 | ✗ | sData->timeValue, sData->realVars, | |
| 2115 | gbData->nSlowStates, gbData->slowStatesIdx, nStates, gbData->tableau, gbData->x, gbData->k); | ||
| 2116 | |||
| 2117 | ✗ | DATA_GBODEF *gbfData = gbData->gbfData; | |
| 2118 | ✗ | gb_interpolation(gbData->gbfData->interpolation, | |
| 2119 | gbfData->timeLeft, gbfData->yLeft, gbfData->kLeft, | ||
| 2120 | gbfData->timeRight, gbfData->yRight, gbfData->kRight, | ||
| 2121 | ✗ | sData->timeValue, sData->realVars, | |
| 2122 | gbfData->nFastStates, gbData->fastStatesIdx, nStates, gbfData->tableau, gbfData->x, gbfData->k); | ||
| 2123 | } else { | ||
| 2124 | ✗ | gb_interpolation(gbData->interpolation, | |
| 2125 | gbData->timeLeft, gbData->yLeft, gbData->kLeft, | ||
| 2126 | gbData->timeRight, gbData->yRight, gbData->kRight, | ||
| 2127 | ✗ | sData->timeValue, sData->realVars, | |
| 2128 | nStates, NULL, nStates, gbData->tableau, gbData->x, gbData->k); | ||
| 2129 | } | ||
| 2130 | } else { | ||
| 2131 | // use chosen interpolation for emitting equidistant output (default hermite) | ||
| 2132 | ✗ | if (solverInfo->currentStepSize>0) | |
| 2133 | ✗ | gb_interpolation(gbData->interpolation, | |
| 2134 | gbData->timeLeft, gbData->yLeft, gbData->kLeft, | ||
| 2135 | gbData->timeRight, gbData->yRight, gbData->kRight, | ||
| 2136 | ✗ | sData->timeValue, sData->realVars, | |
| 2137 | nStates, NULL, nStates, gbData->tableau, gbData->x, gbData->k); | ||
| 2138 | } | ||
| 2139 | // log the emitted result | ||
| 2140 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)){ | |
| 2141 | ✗ | infoStreamPrint(OMC_LOG_GBODE, 1, "Emit result:"); | |
| 2142 | ✗ | printVector_gb(OMC_LOG_GBODE, " y", sData->realVars, nStates, sData->timeValue); | |
| 2143 | ✗ | messageClose(OMC_LOG_GBODE); | |
| 2144 | } | ||
| 2145 | } else { | ||
| 2146 | // Integrator emits result on the simulation grid (see above) | ||
| 2147 | ✗ | sData->timeValue = gbData->time; | |
| 2148 | ✗ | solverInfo->currentTime = gbData->time; | |
| 2149 | ✗ | solverInfo->currentStepSize = gbData->stepSize; | |
| 2150 | } | ||
| 2151 | |||
| 2152 | /* if a state event occurs than no sample event does need to be activated */ | ||
| 2153 | ✗ | data->simulationInfo->sampleActivated = data->simulationInfo->sampleActivated | |
| 2154 | ✗ | && solverInfo->currentTime >= data->simulationInfo->nextSampleEvent; | |
| 2155 | |||
| 2156 | /* Solver statistics */ | ||
| 2157 | ✗ | if (!gbData->isExplicit && gbData->nlsSolverMethod != GB_NLS_INTERNAL) | |
| 2158 | ✗ | gbData->stats.nCallsJacobian = gbData->nlsData->numberOfJEval; | |
| 2159 | ✗ | if (!solverInfo->solverNoEquidistantGrid && fabs(targetTime - stopTime) < GB_MINIMAL_STEP_SIZE && OMC_ACTIVE_STREAM(OMC_LOG_STATS)) { | |
| 2160 | ✗ | if (gbData->multi_rate) { | |
| 2161 | ✗ | infoStreamPrint(OMC_LOG_STATS, 0, "gbode (birate integration): slow: %s / fast: %s", | |
| 2162 | ✗ | GB_METHOD_NAME[gbData->GM_method], GB_METHOD_NAME[gbData->gbfData->GM_method]); | |
| 2163 | ✗ | logSolverStats(OMC_LOG_STATS, "inner integration", stopTime, stopTime, 0, &gbData->gbfData->stats, &gbData->gbfData->fastStateUpdateCount, &gbData->gbfData->additionalFullODEEvaluations); | |
| 2164 | ✗ | logSolverStats(OMC_LOG_STATS, "outer integration", stopTime, stopTime, 0, &gbData->stats, NULL, NULL); | |
| 2165 | } else { | ||
| 2166 | ✗ | infoStreamPrint(OMC_LOG_STATS, 0, "gbode (single-rate integration): %s", GB_METHOD_NAME[gbData->GM_method]); | |
| 2167 | } | ||
| 2168 | } | ||
| 2169 | /* Write statistics to the solverInfo data structure */ | ||
| 2170 | ✗ | logSolverStats(OMC_LOG_SOLVER_V, "gb_singlerate", solverInfo->currentTime, gbData->time, gbData->stepSize, &gbData->stats, NULL, NULL); | |
| 2171 | ✗ | memcpy(&solverInfo->solverStatsTmp, &gbData->stats, sizeof(SOLVERSTATS)); | |
| 2172 | |||
| 2173 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 2174 | |||
| 2175 | ✗ | return 0; | |
| 2176 | } | ||
| 2177 |