OMCompiler/SimulationRuntime/c/simulation/solver/ida_solver.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 ida_solver.c | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include <float.h> | ||
| 32 | #include <math.h> | ||
| 33 | #include <string.h> | ||
| 34 | #include <setjmp.h> | ||
| 35 | |||
| 36 | #include "omc_config.h" | ||
| 37 | #include "openmodelica.h" | ||
| 38 | #include "openmodelica_func.h" | ||
| 39 | #include "simulation_data.h" | ||
| 40 | |||
| 41 | #include "gc/omc_gc.h" | ||
| 42 | #include "util/context.h" | ||
| 43 | #include "util/omc_error.h" | ||
| 44 | |||
| 45 | #include "ida_solver.h" | ||
| 46 | |||
| 47 | #include "sundials_error.h" | ||
| 48 | #include "sundials_util.h" | ||
| 49 | |||
| 50 | #include "../arrayIndex.h" | ||
| 51 | #include "dae_mode.h" | ||
| 52 | #include "dassl.h" | ||
| 53 | #include "epsilon.h" | ||
| 54 | #include "external_input.h" | ||
| 55 | #include "simulation/jacobian_util.h" | ||
| 56 | #include "model_help.h" | ||
| 57 | #include "omc_math.h" | ||
| 58 | #include "simulation/options.h" | ||
| 59 | #include "simulation/results/simulation_result.h" | ||
| 60 | #include "simulation/simulation_runtime.h" | ||
| 61 | #include "solver_main.h" | ||
| 62 | |||
| 63 | #ifdef WITH_SUNDIALS | ||
| 64 | |||
| 65 | |||
| 66 | /* Private function prototypes */ | ||
| 67 | static int callDenseJacobian(sunrealtype tt, sunrealtype cj, N_Vector yy, | ||
| 68 | N_Vector yp, N_Vector rr, SUNMatrix Jac, | ||
| 69 | void *user_data, N_Vector tmp1, N_Vector tmp2, | ||
| 70 | N_Vector tmp3); | ||
| 71 | |||
| 72 | static int callSparseJacobian(sunrealtype currentTime, sunrealtype cj, | ||
| 73 | N_Vector yy, N_Vector yp, N_Vector rr, SUNMatrix Jac, void *user_data, | ||
| 74 | N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); | ||
| 75 | |||
| 76 | static int residualFunctionIDA(double time, N_Vector yy, N_Vector yp, N_Vector res, void* user_data); | ||
| 77 | static int rootsFunctionIDA(double time, N_Vector yy, N_Vector yp, double *gout, void* userData); | ||
| 78 | |||
| 79 | static int getScalingFactors(DATA* data, IDA_SOLVER *idaData, SUNMatrix scaleMatrix); | ||
| 80 | |||
| 81 | static void idaScaleData(IDA_SOLVER *idaData); | ||
| 82 | static void idaReScaleData(IDA_SOLVER *idaData); | ||
| 83 | static void idaScaleVector(N_Vector vec, double* factors, unsigned int size); | ||
| 84 | static void idaReScaleVector(N_Vector vec, double* factors, unsigned int size); | ||
| 85 | |||
| 86 | int ida_event_update(DATA* data, threadData_t *threadData); | ||
| 87 | |||
| 88 | /* Static variables */ | ||
| 89 | /* TODO: Don't use global variables */ | ||
| 90 | static IDA_SOLVER *idaDataGlobal; | ||
| 91 | |||
| 92 | |||
| 93 | /** | ||
| 94 | * @brief Return true if flag signals success. | ||
| 95 | * | ||
| 96 | * If flag is IDA_SUCCESS or IDA_TSTOP_RETURN return true. | ||
| 97 | * Warnings (IDA_WARNING) or encountering roots (IDA_ROOT_RETURN) doesn't count as success. | ||
| 98 | * | ||
| 99 | * @param flag Value of IDA/IDAS flag. | ||
| 100 | * @return modelica_boolean Return true if flag signals success, otherwise return false. | ||
| 101 | */ | ||
| 102 | ✗ | modelica_boolean IDAflagIsSuccess(int flag) { | |
| 103 | ✗ | switch (flag) | |
| 104 | { | ||
| 105 | case IDA_SUCCESS: | ||
| 106 | case IDA_TSTOP_RETURN: | ||
| 107 | case IDA_ROOT_RETURN: | ||
| 108 | return TRUE; | ||
| 109 | ✗ | default: | |
| 110 | ✗ | return FALSE; | |
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | /** | ||
| 116 | * @brief Read the unknowns' nominal values into the tolerances and the scaling. | ||
| 117 | * | ||
| 118 | * Re-read by updateSolverNominals once initialization has computed the nominals | ||
| 119 | * that are parameter expressions. | ||
| 120 | * | ||
| 121 | * @param data Runtime data struct. | ||
| 122 | * @param threadData Thread data for error handling | ||
| 123 | * @param idaData IDA solver data with its arrays already allocated. | ||
| 124 | * @return int Returns 0 on success. | ||
| 125 | */ | ||
| 126 | ✗ | int ida_solver_setNominals(DATA* data, threadData_t *threadData, IDA_SOLVER* idaData) | |
| 127 | { | ||
| 128 | int flag; | ||
| 129 | long int i; | ||
| 130 | ✗ | double* abstol = N_VGetArrayPointer_Serial(idaData->absoluteTolerance); | |
| 131 | |||
| 132 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "The relative tolerance is %g. Following absolute tolerances are used for the states: ", data->simulationInfo->tolerance); | |
| 133 | ✗ | for(i=0; i < data->modelData->nStates; ++i) { | |
| 134 | ✗ | const modelica_real nominal = getNominalFromScalarIdx(data->simulationInfo, data->modelData, VAR_KIND_STATE, i); | |
| 135 | ✗ | idaData->nominal[i] = fmax(fabs(nominal), 1e-32); | |
| 136 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, "%ld. %s -> %g", i+1, data->modelData->realVarsData[data->simulationInfo->realVarsReverseIndex[i].array_idx].info.name, idaData->nominal[i]); | |
| 137 | } | ||
| 138 | |||
| 139 | /* daeMode: set nominal values for algebraic variables */ | ||
| 140 | ✗ | if (idaData->daeMode) { | |
| 141 | ✗ | getAlgebraicDAEVarNominals(data, idaData->nominal + data->modelData->nStates); | |
| 142 | ✗ | for(i=data->modelData->nStates; i < idaData->N; ++i) { | |
| 143 | ✗ | idaData->nominal[i] = fmax(fabs(idaData->nominal[i]), 1e-32); | |
| 144 | } | ||
| 145 | } | ||
| 146 | /* multiply by tolerance to obtain a relative tolerace */ | ||
| 147 | ✗ | for(i=0; i < idaData->N; ++i) { | |
| 148 | ✗ | abstol[i] = idaData->nominal[i] * data->simulationInfo->tolerance; | |
| 149 | } | ||
| 150 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 151 | ✗ | flag = IDASVtolerances(idaData->ida_mem, data->simulationInfo->tolerance, idaData->absoluteTolerance); | |
| 152 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASVtolerances"); | |
| 153 | |||
| 154 | ✗ | if (idaData->yScale != NULL) { | |
| 155 | ✗ | for(i=0; i < idaData->N; ++i) { | |
| 156 | ✗ | idaData->yScale[i] = idaData->nominal[i]; | |
| 157 | } | ||
| 158 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "The scale factors for all ida states: "); | |
| 159 | ✗ | for (i=0; i < idaData->N; ++i) { | |
| 160 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, "%ld. scaleFactor: %g", i+1, idaData->yScale[i]); | |
| 161 | } | ||
| 162 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 163 | } | ||
| 164 | |||
| 165 | ✗ | return 0; | |
| 166 | } | ||
| 167 | |||
| 168 | /** | ||
| 169 | * @brief Initialize main IDA data. | ||
| 170 | * | ||
| 171 | * Allocate memory for IDA_SOLVER struct and initialize IDA solver. | ||
| 172 | * | ||
| 173 | * @param data Runtime data struct. | ||
| 174 | * @param threadData Thread data for error handling | ||
| 175 | * @param solverInfo Information about main solver. Unused at the moment. | ||
| 176 | * @param idaData IDA solver data. | ||
| 177 | * @return int Returns 0 on success, aborts with throw on failure. | ||
| 178 | */ | ||
| 179 | ✗ | int ida_solver_initial(DATA* data, threadData_t *threadData, | |
| 180 | SOLVER_INFO* solverInfo, IDA_SOLVER* idaData) | ||
| 181 | { | ||
| 182 | /* Variables */ | ||
| 183 | int flag; | ||
| 184 | long int i; | ||
| 185 | int maxOrder; | ||
| 186 | |||
| 187 | /* Initialize constants */ | ||
| 188 | ✗ | idaData->setInitialSolution = FALSE; | |
| 189 | ✗ | idaData->homotopyRampActive = 0; | |
| 190 | ✗ | idaData->homotopyTramp = -1.0; | |
| 191 | |||
| 192 | /* Instantiate IDA solver object */ | ||
| 193 | /* Create the SUNDIALS context every other SUNDIALS object is created with */ | ||
| 194 | ✗ | flag = SUNContext_Create(SUN_COMM_NULL, &idaData->sunctx); | |
| 195 | ✗ | assertStreamPrint(threadData, flag == SUN_SUCCESS, "SUNDIALS_ERROR: SUNContext_Create failed."); | |
| 196 | ✗ | sundialsSilenceLogger(idaData->sunctx); | |
| 197 | |||
| 198 | /* Set error handler */ | ||
| 199 | ✗ | flag = SUNContext_PushErrHandler(idaData->sunctx, sundialsErrorHandlerFunction, idaData); | |
| 200 | ✗ | assertStreamPrint(threadData, flag == SUN_SUCCESS, "SUNDIALS_ERROR: SUNContext_PushErrHandler failed."); | |
| 201 | |||
| 202 | ✗ | idaData->ida_mem = IDACreate(idaData->sunctx); | |
| 203 | ✗ | if (idaData->ida_mem == NULL) { | |
| 204 | ✗ | throwStreamPrint(threadData, "##IDA## Initialization of IDA solver failed!"); | |
| 205 | } | ||
| 206 | |||
| 207 | ✗ | idaData->residualFunction = residualFunctionIDA; | |
| 208 | |||
| 209 | /* Start measuring time */ /* TODO: Why start here? */ | ||
| 210 | ✗ | if (measure_time_flag) { | |
| 211 | ✗ | rt_tick(SIM_TIMER_SOLVER); | |
| 212 | } | ||
| 213 | |||
| 214 | /* change parameter for DAE mode */ | ||
| 215 | ✗ | if (compiledInDAEMode) { | |
| 216 | ✗ | idaData->daeMode = TRUE; | |
| 217 | ✗ | idaData->N = (long int) data->modelData->nStates + data->simulationInfo->daeModeData->nAlgebraicDAEVars; | |
| 218 | } | ||
| 219 | else { | ||
| 220 | ✗ | idaData->daeMode = FALSE; | |
| 221 | ✗ | idaData->N = (long int) data->modelData->nStates; | |
| 222 | } | ||
| 223 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "## IDA ## Initializing solver of size %ld %s.", idaData->N, idaData->daeMode?"in DAE mode":""); | |
| 224 | ✗ | idaData->NNZ = -1; | |
| 225 | |||
| 226 | /* initialize states and der(states) */ | ||
| 227 | ✗ | if (idaData->daeMode) | |
| 228 | { | ||
| 229 | ✗ | idaData->states = (double*) malloc(idaData->N*sizeof(double)); | |
| 230 | ✗ | idaData->statesDer = (double*) calloc(idaData->N,sizeof(double)); | |
| 231 | |||
| 232 | ✗ | memcpy(idaData->states, data->localData[0]->realVars, sizeof(double)*data->modelData->nStates); | |
| 233 | // and also algebraic vars | ||
| 234 | ✗ | getAlgebraicDAEVars(data, idaData->states + data->modelData->nStates); | |
| 235 | ✗ | memcpy(idaData->statesDer, data->localData[0]->realVars + data->modelData->nStates, sizeof(double)*data->modelData->nStates); | |
| 236 | |||
| 237 | ✗ | idaData->y = N_VMake_Serial(idaData->N, idaData->states, idaData->sunctx); | |
| 238 | ✗ | idaData->yp = N_VMake_Serial(idaData->N, idaData->statesDer, idaData->sunctx); | |
| 239 | } | ||
| 240 | else { | ||
| 241 | ✗ | idaData->states = NULL; | |
| 242 | ✗ | idaData->statesDer = NULL; | |
| 243 | ✗ | idaData->y = N_VMake_Serial(idaData->N, data->localData[0]->realVars, idaData->sunctx); | |
| 244 | ✗ | idaData->yp = N_VMake_Serial(idaData->N, data->localData[0]->realVars + data->modelData->nStates, idaData->sunctx); | |
| 245 | } | ||
| 246 | |||
| 247 | ✗ | flag = IDAInit(idaData->ida_mem, idaData->residualFunction, | |
| 248 | ✗ | data->simulationInfo->startTime, idaData->y, idaData->yp); | |
| 249 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAInit"); | |
| 250 | |||
| 251 | /* Allocate memory for jacobians calculation */ | ||
| 252 | ✗ | idaData->ysave = (double*) malloc(idaData->N*sizeof(double)); | |
| 253 | ✗ | idaData->ypsave = (double*) malloc(idaData->N*sizeof(double)); | |
| 254 | ✗ | idaData->delta_hh = (double*) malloc(idaData->N*sizeof(double)); | |
| 255 | ✗ | idaData->nominal = (double*) malloc(idaData->N*sizeof(double)); | |
| 256 | ✗ | idaData->newdelta = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 257 | |||
| 258 | /* Allocate memory for linear solver */ | ||
| 259 | ✗ | idaData->y_linSol = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 260 | |||
| 261 | /* Set user data */ | ||
| 262 | ✗ | idaData->userData = (IDA_USERDATA*) malloc(sizeof(IDA_USERDATA)); | |
| 263 | ✗ | idaData->userData->data = data; | |
| 264 | ✗ | idaData->userData->threadData = threadData; | |
| 265 | ✗ | flag = IDASetUserData(idaData->ida_mem, idaData); | |
| 266 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetUserData"); | |
| 267 | |||
| 268 | ✗ | idaData->jacNominalFactor = omc_flag[FLAG_JACOBIAN_NOMINAL_FACTOR] | |
| 269 | ✗ | ? atof(omc_flagValue[FLAG_JACOBIAN_NOMINAL_FACTOR]) : 1.0; | |
| 270 | |||
| 271 | ✗ | idaData->absoluteTolerance = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 272 | ✗ | idaData->id = NULL; | |
| 273 | |||
| 274 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) { /* idaNoScaling */ | |
| 275 | /* allocate memory for scaling */ | ||
| 276 | ✗ | idaData->yScale = (double*) malloc(idaData->N*sizeof(double)); | |
| 277 | ✗ | idaData->ypScale = (double*) malloc(idaData->N*sizeof(double)); | |
| 278 | ✗ | idaData->resScale = (double*) malloc(idaData->N*sizeof(double)); | |
| 279 | |||
| 280 | ✗ | for(i=0; i < idaData->N; ++i) { | |
| 281 | ✗ | idaData->ypScale[i] = 1.0; // TODO: 1 is not a good scaling value. Use something like nominal value / number of intervals | |
| 282 | } | ||
| 283 | } else { | ||
| 284 | ✗ | idaData->yScale = NULL; | |
| 285 | ✗ | idaData->ypScale = NULL; | |
| 286 | ✗ | idaData->resScale = NULL; | |
| 287 | } | ||
| 288 | |||
| 289 | ✗ | ida_solver_setNominals(data, threadData, idaData); | |
| 290 | /* initialize */ | ||
| 291 | ✗ | idaData->useScaling = TRUE; | |
| 292 | |||
| 293 | /* Set root functions unless flag FLAG_NO_ROOTFINDING is set */ | ||
| 294 | ✗ | if (!omc_flag[FLAG_NO_ROOTFINDING]) { | |
| 295 | ✗ | solverInfo->solverRootFinding = 1; | |
| 296 | ✗ | flag = IDARootInit(idaData->ida_mem, data->modelData->nZeroCrossings, rootsFunctionIDA); | |
| 297 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDARootInit"); | |
| 298 | } | ||
| 299 | else { | ||
| 300 | ✗ | solverInfo->solverRootFinding = 0; | |
| 301 | } | ||
| 302 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "IDA uses internal root finding method %s", solverInfo->solverRootFinding?"YES":"NO"); | |
| 303 | |||
| 304 | /* Define maximum integration order of IDA */ | ||
| 305 | ✗ | if (omc_flag[FLAG_MAX_ORDER]) { | |
| 306 | ✗ | maxOrder = atoi(omc_flagValue[FLAG_MAX_ORDER]); | |
| 307 | |||
| 308 | ✗ | flag = IDASetMaxOrd(idaData->ida_mem, maxOrder); | |
| 309 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetMaxOrd"); | |
| 310 | } else { | ||
| 311 | maxOrder = 5; /* Default max order for IDA */ | ||
| 312 | } | ||
| 313 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Maximum integration order %d", maxOrder); | |
| 314 | |||
| 315 | /* if FLAG_NOEQUIDISTANT_GRID is set, choose ida step method */ | ||
| 316 | ✗ | if (omc_flag[FLAG_NOEQUIDISTANT_GRID]) { | |
| 317 | ✗ | idaData->internalSteps = 1; /* TRUE */ | |
| 318 | ✗ | solverInfo->solverNoEquidistantGrid = TRUE; | |
| 319 | } else { | ||
| 320 | ✗ | idaData->internalSteps = 0; /* FALSE */ | |
| 321 | } | ||
| 322 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "use equidistant time grid %s", idaData->internalSteps?"NO":"YES"); | |
| 323 | |||
| 324 | /* check if Flags FLAG_NOEQUIDISTANT_OUT_FREQ or FLAG_NOEQUIDISTANT_OUT_TIME are set */ | ||
| 325 | ✗ | if (idaData->internalSteps) { | |
| 326 | ✗ | if (omc_flag[FLAG_NOEQUIDISTANT_OUT_FREQ]) { | |
| 327 | ✗ | idaData->stepsFreq = atoi(omc_flagValue[FLAG_NOEQUIDISTANT_OUT_FREQ]); | |
| 328 | ✗ | } else if (omc_flag[FLAG_NOEQUIDISTANT_OUT_TIME]) { | |
| 329 | ✗ | idaData->stepsTime = atof(omc_flagValue[FLAG_NOEQUIDISTANT_OUT_TIME]); | |
| 330 | ✗ | flag = IDASetMaxStep(idaData->ida_mem, idaData->stepsTime); | |
| 331 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetMaxStep"); | |
| 332 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "maximum step size %g", idaData->stepsTime); | |
| 333 | } else { | ||
| 334 | ✗ | idaData->stepsFreq = 1; | |
| 335 | ✗ | idaData->stepsTime = 0.0; | |
| 336 | } | ||
| 337 | |||
| 338 | ✗ | if (omc_flag[FLAG_NOEQUIDISTANT_OUT_FREQ] && omc_flag[FLAG_NOEQUIDISTANT_OUT_TIME]) { | |
| 339 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "The flags are \"noEquidistantOutputFrequency\" " | |
| 340 | "and \"noEquidistantOutputTime\" are in opposition " | ||
| 341 | "to each other. The flag \"noEquidistantOutputFrequency\" superiors."); | ||
| 342 | } | ||
| 343 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "as the output frequency control is used: %d", idaData->stepsFreq); | |
| 344 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "as the output frequency time step control is used: %f", idaData->stepsTime); | |
| 345 | } | ||
| 346 | |||
| 347 | /* if FLAG_IDA_LS is set, choose ida linear solver method */ | ||
| 348 | ✗ | if (omc_flag[FLAG_IDA_LS]) { | |
| 349 | ✗ | for (i=1; i< IDA_LS_MAX; i++) { | |
| 350 | ✗ | if (!strcmp((const char*)omc_flagValue[FLAG_IDA_LS], IDA_LS_METHOD_NAME[i])) { | |
| 351 | ✗ | idaData->linearSolverMethod = (enum IDA_LS)i; | |
| 352 | ✗ | break; | |
| 353 | } | ||
| 354 | } | ||
| 355 | ✗ | if (idaData->linearSolverMethod == IDA_LS_UNKNOWN) { | |
| 356 | ✗ | if (OMC_ACTIVE_WARNING_STREAM(OMC_LOG_SOLVER)) { | |
| 357 | ✗ | warningStreamPrint(OMC_LOG_SOLVER, 1, "unrecognized ida linear solver method %s, current options are:", (const char*)omc_flagValue[FLAG_IDA_LS]); | |
| 358 | ✗ | for(i=1; i < IDA_LS_MAX; ++i) { | |
| 359 | ✗ | warningStreamPrint(OMC_LOG_SOLVER, 0, "%-15s [%s]", IDA_LS_METHOD_NAME[i], IDA_LS_METHOD_DESC[i]); | |
| 360 | } | ||
| 361 | ✗ | messageCloseWarning(OMC_LOG_SOLVER); | |
| 362 | } | ||
| 363 | ✗ | throwStreamPrint(threadData,"unrecognized ida linear solver method %s", (const char*)omc_flagValue[FLAG_IDA_LS]); | |
| 364 | } | ||
| 365 | } else { | ||
| 366 | ✗ | idaData->linearSolverMethod = IDA_LS_KLU; | |
| 367 | } | ||
| 368 | |||
| 369 | /* Choose and initialize the ODE Jacobian. The mapping from the `-jacobian` flag to | ||
| 370 | * the forward / adjoint / bidirectional Jacobian is shared with DASSL and GBODE. */ | ||
| 371 | ✗ | idaData->jacobianMethod = getRequestedJacobianMethod(threadData); | |
| 372 | ✗ | JACOBIAN* jacobian = initSymbolicOdeJacobian(data, threadData, &idaData->jacobianMethod, FALSE); | |
| 373 | /* IDA always needs a column oriented pattern. */ | ||
| 374 | ✗ | const SPARSE_PATTERN* cscPattern = getJacobianCscPattern(jacobian); | |
| 375 | |||
| 376 | // change IDA specific jacobian method | ||
| 377 | ✗ | if(idaData->jacobianMethod == SYMJAC) { | |
| 378 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Symbolic Jacobians without coloring are currently not supported by IDA." | |
| 379 | " Colored symbolical Jacobian will be used."); | ||
| 380 | ✗ | idaData->jacobianMethod = COLOREDSYMJAC; | |
| 381 | ✗ | }else if(idaData->jacobianMethod == NUMJAC) { | |
| 382 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Numerical Jacobians without coloring are currently not supported by IDA." | |
| 383 | " Colored numerical Jacobian will be used."); | ||
| 384 | ✗ | idaData->jacobianMethod = COLOREDNUMJAC; | |
| 385 | ✗ | }else if(idaData->jacobianMethod == INTERNALNUMJAC && idaData->linearSolverMethod == IDA_LS_KLU) { | |
| 386 | ✗ | if ((!idaData->daeMode && jacobian->sparsePattern == NULL) || (idaData->daeMode && data->simulationInfo->daeModeData->sparsePattern == NULL)) { | |
| 387 | ✗ | throwStreamPrint(threadData, "##IDA## Internal Numerical Jacobians require a sparse pattern for the jacobian but no sparse pattern is generated."); | |
| 388 | } else { | ||
| 389 | ✗ | warningStreamPrint(OMC_LOG_STDOUT, 0, "Internal Numerical Jacobians without coloring are currently not supported by IDA with KLU." | |
| 390 | " Colored numerical Jacobian will be used."); | ||
| 391 | ✗ | idaData->jacobianMethod = COLOREDNUMJAC; | |
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | /* Set NNZ */ | ||
| 396 | ✗ | if (idaData->daeMode) { | |
| 397 | ✗ | idaData->NNZ = data->simulationInfo->daeModeData->sparsePattern->nnz; | |
| 398 | } else { | ||
| 399 | // could also be jacobian->sparsePattern->nnz but the cscPattern is the one that is actually used. | ||
| 400 | ✗ | idaData->NNZ = cscPattern->nnz; | |
| 401 | } | ||
| 402 | |||
| 403 | ✗ | switch (idaData->linearSolverMethod){ | |
| 404 | ✗ | case IDA_LS_SPGMR: | |
| 405 | ✗ | idaData->J = NULL; | |
| 406 | ✗ | idaData->linSol = SUNLinSol_SPGMR(idaData->y_linSol, SUN_PREC_NONE, idaData->N, idaData->sunctx); | |
| 407 | ✗ | if (idaData->linSol == NULL) { | |
| 408 | ✗ | throwStreamPrint(threadData, "##IDA## In function SUNLinSol_SPGMR: Input incompatible."); | |
| 409 | } | ||
| 410 | ✗ | idaData->jacobianMethod = INTERNALNUMJAC; | |
| 411 | ✗ | break; | |
| 412 | ✗ | case IDA_LS_SPBCG: | |
| 413 | ✗ | idaData->J = NULL; | |
| 414 | ✗ | idaData->linSol = SUNLinSol_SPBCGS(idaData->y_linSol, SUN_PREC_NONE, idaData->N, idaData->sunctx); | |
| 415 | ✗ | if (idaData->linSol == NULL) { | |
| 416 | ✗ | throwStreamPrint(threadData, "##IDA## In function SUNLinSol_SPBCGS: Input incompatible."); | |
| 417 | } | ||
| 418 | ✗ | idaData->jacobianMethod = INTERNALNUMJAC; | |
| 419 | ✗ | break; | |
| 420 | ✗ | case IDA_LS_SPTFQMR: | |
| 421 | ✗ | idaData->J = NULL; | |
| 422 | ✗ | idaData->linSol = SUNLinSol_SPTFQMR(idaData->y_linSol, SUN_PREC_NONE, idaData->N, idaData->sunctx); | |
| 423 | ✗ | if (idaData->linSol == NULL) { | |
| 424 | ✗ | throwStreamPrint(threadData, "##IDA## In function SUNLinSol_SPTFQMR: Input incompatible."); | |
| 425 | } | ||
| 426 | ✗ | idaData->jacobianMethod = INTERNALNUMJAC; | |
| 427 | ✗ | break; | |
| 428 | ✗ | case IDA_LS_DENSE: | |
| 429 | ✗ | idaData->J = SUNDenseMatrix(idaData->N, idaData->N, idaData->sunctx); | |
| 430 | ✗ | idaData->linSol = SUNLinSol_Dense(idaData->y_linSol, idaData->J, idaData->sunctx); | |
| 431 | ✗ | if (idaData->linSol == NULL) { | |
| 432 | ✗ | throwStreamPrint(threadData, "##IDA## In function SUNLinSol_Dense: Input incompatible."); | |
| 433 | } | ||
| 434 | break; | ||
| 435 | ✗ | case IDA_LS_KLU: | |
| 436 | /* Set KLU after initialized sparse pattern of the jacobian for nnz */ | ||
| 437 | ✗ | if (idaData->NNZ < 0) { | |
| 438 | ✗ | throwStreamPrint(threadData, "##IDA## idaData->NNZ not set."); | |
| 439 | } | ||
| 440 | ✗ | idaData->J = SUNSparseMatrix(idaData->N, idaData->N, idaData->NNZ + idaData->N, SUN_CSC_MAT, idaData->sunctx); | |
| 441 | ✗ | idaData->linSol = SUNLinSol_KLU(idaData->y_linSol, idaData->J, idaData->sunctx); | |
| 442 | ✗ | if (idaData->linSol == NULL) { | |
| 443 | ✗ | throwStreamPrint(threadData, "##IDA## In function SUNLinSol_KLU: Input incompatible."); | |
| 444 | } | ||
| 445 | break; | ||
| 446 | ✗ | default: | |
| 447 | ✗ | throwStreamPrint(threadData,"unrecognized linear solver method %s", (const char*)omc_flagValue[FLAG_IDA_LS]); | |
| 448 | break; | ||
| 449 | } | ||
| 450 | |||
| 451 | ✗ | flag = IDASetLinearSolver(idaData->ida_mem, idaData->linSol, idaData->J); | |
| 452 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDALS_FLAG, "IDASetLinearSolver"); | |
| 453 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "IDA linear solver method selected %s", IDA_LS_METHOD_DESC[idaData->linearSolverMethod]); | |
| 454 | |||
| 455 | /* Set Jacobian function */ | ||
| 456 | ✗ | idaData->scaleMatrix = NULL; /* allocated on demand, see getScalingFactors */ | |
| 457 | |||
| 458 | /* Use sparse jacobian evaluation */ | ||
| 459 | ✗ | if (idaData->linearSolverMethod == IDA_LS_KLU) { | |
| 460 | /* Set Jacobian function for matrix based linear solvers */ | ||
| 461 | ✗ | switch (idaData->jacobianMethod){ | |
| 462 | ✗ | case SYMJAC: | |
| 463 | case NUMJAC: | ||
| 464 | case COLOREDSYMJAC: | ||
| 465 | case COLOREDSYMJACADJ: | ||
| 466 | case BICOLOREDSYMJAC: | ||
| 467 | case COLOREDNUMJAC: | ||
| 468 | ✗ | flag = IDASetJacFn(idaData->ida_mem, callSparseJacobian); | |
| 469 | |||
| 470 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDALS_FLAG, "IDASetJacFn"); | |
| 471 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) { | |
| 472 | ✗ | idaData->scaleMatrix = SUNSparseMatrix(idaData->N, idaData->N, idaData->NNZ + idaData->N, SUN_CSC_MAT, idaData->sunctx); | |
| 473 | } | ||
| 474 | break; | ||
| 475 | ✗ | default: | |
| 476 | ✗ | throwStreamPrint(threadData,"For the klu solver jacobian calculation method has to be one of %s, %s, %s or %s", | |
| 477 | JACOBIAN_METHOD_NAME[COLOREDSYMJAC], JACOBIAN_METHOD_NAME[COLOREDSYMJACADJ], | ||
| 478 | JACOBIAN_METHOD_NAME[BICOLOREDSYMJAC], JACOBIAN_METHOD_NAME[COLOREDNUMJAC]); | ||
| 479 | break; | ||
| 480 | } | ||
| 481 | /* Use dense jacobian evaluation */ | ||
| 482 | } else { | ||
| 483 | ✗ | switch (idaData->jacobianMethod){ | |
| 484 | ✗ | case SYMJAC: | |
| 485 | case NUMJAC: | ||
| 486 | case COLOREDSYMJAC: | ||
| 487 | case COLOREDSYMJACADJ: | ||
| 488 | case BICOLOREDSYMJAC: | ||
| 489 | case COLOREDNUMJAC: | ||
| 490 | ✗ | flag = IDASetJacFn(idaData->ida_mem, callDenseJacobian); | |
| 491 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDALS_FLAG, "IDASetJacFn"); | |
| 492 | ✗ | break; | |
| 493 | case INTERNALNUMJAC: | ||
| 494 | /* TODO: Set a preconditioner if possible */ | ||
| 495 | break; | ||
| 496 | ✗ | default: | |
| 497 | ✗ | throwStreamPrint(threadData,"unrecognized jacobian calculation method %s", (const char*)omc_flagValue[FLAG_JACOBIAN]); | |
| 498 | break; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Jacobian is calculated by \"%s\"", JACOBIAN_METHOD_DESC[idaData->jacobianMethod]); | |
| 502 | |||
| 503 | /* Set max error test fails */ | ||
| 504 | ✗ | if (omc_flag[FLAG_IDA_MAXERRORTESTFAIL]) | |
| 505 | { | ||
| 506 | ✗ | int maxErrorTestFails = atoi(omc_flagValue[FLAG_IDA_MAXERRORTESTFAIL]); | |
| 507 | ✗ | flag = IDASetMaxErrTestFails(idaData->ida_mem, maxErrorTestFails); | |
| 508 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetMaxErrTestFails"); | |
| 509 | } | ||
| 510 | |||
| 511 | /* set maximum number of nonlinear solver iterations at one step */ | ||
| 512 | ✗ | if (omc_flag[FLAG_IDA_MAXNONLINITERS]) | |
| 513 | { | ||
| 514 | ✗ | int maxNonlinIters = atoi(omc_flagValue[FLAG_IDA_MAXNONLINITERS]); | |
| 515 | ✗ | flag = IDASetMaxNonlinIters(idaData->ida_mem, maxNonlinIters); | |
| 516 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetMaxNonlinIters"); | |
| 517 | } | ||
| 518 | |||
| 519 | /* maximum number of nonlinear solver convergence failures at one step */ | ||
| 520 | ✗ | if (omc_flag[FLAG_IDA_MAXCONVFAILS]) | |
| 521 | { | ||
| 522 | ✗ | int maxConvFails = atoi(omc_flagValue[FLAG_IDA_MAXCONVFAILS]); | |
| 523 | ✗ | flag = IDASetMaxConvFails(idaData->ida_mem, maxConvFails); | |
| 524 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetMaxConvFails"); | |
| 525 | } | ||
| 526 | |||
| 527 | /* safety factor in the nonlinear convergence test */ | ||
| 528 | ✗ | if (omc_flag[FLAG_IDA_NONLINCONVCOEF]) | |
| 529 | { | ||
| 530 | ✗ | double nonlinConvCoef = atof(omc_flagValue[FLAG_IDA_NONLINCONVCOEF]); | |
| 531 | ✗ | flag = IDASetNonlinConvCoef(idaData->ida_mem, nonlinConvCoef); | |
| 532 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetNonlinConvCoef"); | |
| 533 | } | ||
| 534 | |||
| 535 | /* configure algebraic variables as such */ | ||
| 536 | ✗ | if (idaData->daeMode) { | |
| 537 | ✗ | if (omc_flag[FLAG_NO_SUPPRESS_ALG]) { | |
| 538 | ✗ | flag = IDASetSuppressAlg(idaData->ida_mem, 1 /* TRUE */); | |
| 539 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetSuppressAlg"); | |
| 540 | } | ||
| 541 | ✗ | idaData->id = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 542 | ✗ | for (i=0; i<idaData->N; ++i) { | |
| 543 | ✗ | NV_Ith_S(idaData->id, i) = (i<data->modelData->nStates)? 1.0: 0.0; | |
| 544 | } | ||
| 545 | |||
| 546 | ✗ | flag = IDASetId(idaData->ida_mem, idaData->id); | |
| 547 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetId"); | |
| 548 | } | ||
| 549 | |||
| 550 | /* define initial step size */ | ||
| 551 | ✗ | if (omc_flag[FLAG_INITIAL_STEP_SIZE]) { | |
| 552 | ✗ | double initialStepSize = atof(omc_flagValue[FLAG_INITIAL_STEP_SIZE]); | |
| 553 | |||
| 554 | ✗ | assertStreamPrint(threadData, initialStepSize >= DASSL_STEP_EPS, "Selected initial step size %e is too small.", initialStepSize); | |
| 555 | |||
| 556 | ✗ | flag = IDASetInitStep(idaData->ida_mem, initialStepSize); | |
| 557 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetInitStep"); | |
| 558 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "initial step size: %g", initialStepSize); | |
| 559 | } else { | ||
| 560 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "initial step size is set automatically."); | |
| 561 | } | ||
| 562 | |||
| 563 | /* Initialize sensitivities analysis */ | ||
| 564 | ✗ | idaData->idaSmode = omc_flag[FLAG_IDAS] ? 1 : 0; | |
| 565 | |||
| 566 | ✗ | if (idaData->idaSmode) { | |
| 567 | ✗ | idaData->Ns = data->modelData->nSensitivityParamVars; | |
| 568 | ✗ | idaData->yS = N_VCloneVectorArray(idaData->Ns, idaData->y); | |
| 569 | ✗ | idaData->ySp = N_VCloneVectorArray(idaData->Ns, idaData->yp); | |
| 570 | |||
| 571 | ✗ | for (i=0; i<idaData->Ns; ++i) { | |
| 572 | ✗ | N_VConst_Serial(0.0, idaData->yS[i]); | |
| 573 | ✗ | N_VConst_Serial(0.0, idaData->ySp[i]); | |
| 574 | } | ||
| 575 | |||
| 576 | ✗ | flag = IDASensInit(idaData->ida_mem, idaData->Ns, IDA_SIMULTANEOUS, NULL, idaData->yS, idaData->ySp); | |
| 577 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASensInit"); | |
| 578 | |||
| 579 | ✗ | flag = IDASetSensParams(idaData->ida_mem, data->simulationInfo->realParameter, NULL, data->simulationInfo->sensitivityParList); | |
| 580 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetSensParams"); | |
| 581 | ✗ | flag = IDASetSensDQMethod(idaData->ida_mem, IDA_FORWARD, 0); | |
| 582 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetSensDQMethod"); | |
| 583 | |||
| 584 | ✗ | flag = IDASensEEtolerances(idaData->ida_mem); | |
| 585 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASensEEtolerances"); | |
| 586 | /* | ||
| 587 | flag = IDASetSensErrCon(idaData->ida_mem, TRUE); | ||
| 588 | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetSensErrCon"); | ||
| 589 | */ | ||
| 590 | /* allocate result workspace */ | ||
| 591 | ✗ | idaData->ySResult = N_VNewVectorArray(idaData->Ns, idaData->sunctx); | |
| 592 | ✗ | for(i = 0; i < idaData->Ns; ++i) | |
| 593 | { | ||
| 594 | ✗ | idaData->ySResult[i] = N_VMake_Serial(idaData->N, | |
| 595 | ✗ | data->simulationInfo->sensitivityMatrix + i*idaData->N, | |
| 596 | idaData->sunctx); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | ✗ | if (compiledInDAEMode){ | |
| 600 | ✗ | idaDataGlobal = idaData; | |
| 601 | ✗ | data->callback->functionDAE = ida_event_update; | |
| 602 | } | ||
| 603 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 604 | |||
| 605 | ✗ | if (measure_time_flag) rt_clear(SIM_TIMER_SOLVER); /* TODO Initialization should not add to this timer... */ | |
| 606 | |||
| 607 | ✗ | return 0; | |
| 608 | } | ||
| 609 | |||
| 610 | /** | ||
| 611 | * @brief Deinitialize IDA data. | ||
| 612 | * | ||
| 613 | * @param idaData Pointer to IDA solver data struct. | ||
| 614 | */ | ||
| 615 | ✗ | void ida_solver_deinitial(IDA_SOLVER *idaData) | |
| 616 | { | ||
| 617 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) { | |
| 618 | /* free scaling data */ | ||
| 619 | ✗ | free(idaData->yScale); | |
| 620 | ✗ | free(idaData->ypScale); | |
| 621 | ✗ | free(idaData->resScale); | |
| 622 | ✗ | SUNMatDestroy(idaData->scaleMatrix); | |
| 623 | } | ||
| 624 | |||
| 625 | /* free work arrays */ | ||
| 626 | ✗ | free(idaData->userData); | |
| 627 | ✗ | free(idaData->ysave); | |
| 628 | ✗ | free(idaData->ypsave); | |
| 629 | ✗ | free(idaData->delta_hh); | |
| 630 | |||
| 631 | /* Free linear solver data */ | ||
| 632 | ✗ | N_VDestroy_Serial(idaData->y_linSol); | |
| 633 | ✗ | SUNMatDestroy(idaData->J); | |
| 634 | ✗ | SUNLinSolFree(idaData->linSol); | |
| 635 | |||
| 636 | /* Free dae-mode data */ | ||
| 637 | ✗ | if (idaData->daeMode) { | |
| 638 | ✗ | free(idaData->states); | |
| 639 | ✗ | free(idaData->statesDer); | |
| 640 | } | ||
| 641 | |||
| 642 | /* Free sensitivity-mode data */ | ||
| 643 | ✗ | if (idaData->idaSmode) { | |
| 644 | ✗ | N_VDestroyVectorArray(idaData->yS, idaData->Ns); | |
| 645 | ✗ | N_VDestroyVectorArray(idaData->ySp, idaData->Ns); | |
| 646 | ✗ | N_VDestroyVectorArray(idaData->ySResult, idaData->Ns); | |
| 647 | } | ||
| 648 | |||
| 649 | ✗ | free(idaData->nominal); | |
| 650 | ✗ | N_VDestroy_Serial(idaData->absoluteTolerance); | |
| 651 | ✗ | if (idaData->id != NULL) { | |
| 652 | ✗ | N_VDestroy_Serial(idaData->id); | |
| 653 | } | ||
| 654 | ✗ | N_VDestroy_Serial(idaData->newdelta); | |
| 655 | |||
| 656 | ✗ | IDAFree(&idaData->ida_mem); | |
| 657 | |||
| 658 | ✗ | SUNContext_Free(&idaData->sunctx); | |
| 659 | ✗ | } | |
| 660 | |||
| 661 | |||
| 662 | /** | ||
| 663 | * @brief EventHandle for DAE mode. | ||
| 664 | * | ||
| 665 | * Handles events by reinitialize main IDA solver, initializing next step and | ||
| 666 | * evaluate DAE residual equations. | ||
| 667 | * | ||
| 668 | * @param data Runtime data struct. | ||
| 669 | * @param threadData Thread data for error handling. | ||
| 670 | * @return int Return 0. | ||
| 671 | */ | ||
| 672 | ✗ | int ida_event_update(DATA* data, threadData_t *threadData) | |
| 673 | { | ||
| 674 | ✗ | IDA_SOLVER *idaData = idaDataGlobal; | |
| 675 | int flag; | ||
| 676 | long nonLinIters; | ||
| 677 | double init_h; | ||
| 678 | |||
| 679 | ✗ | if (!compiledInDAEMode){ | |
| 680 | ✗ | throwStreamPrint(threadData, "Function ida_event_update only callable in DAE mode"); | |
| 681 | } | ||
| 682 | |||
| 683 | ✗ | data->simulationInfo->needToIterate = 0 /* FALSE */; | |
| 684 | |||
| 685 | ✗ | memcpy(idaData->states, data->localData[0]->realVars, sizeof(double)*data->modelData->nStates); | |
| 686 | ✗ | getAlgebraicDAEVars(data, idaData->states + data->modelData->nStates); | |
| 687 | ✗ | memcpy(idaData->statesDer, data->localData[0]->realVars + data->modelData->nStates, sizeof(double)*data->modelData->nStates); | |
| 688 | |||
| 689 | /* update inner algebraic get new values from data */ | ||
| 690 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 691 | ✗ | evaluateDAEResiduals_wrapperEventUpdate(data, threadData); | |
| 692 | ✗ | getAlgebraicDAEVars(data, idaData->states + data->modelData->nStates); | |
| 693 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 694 | |||
| 695 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## do event update at %.15g", data->localData[0]->timeValue); | |
| 696 | ✗ | memcpy(idaData->states, data->localData[0]->realVars, sizeof(double)*data->modelData->nStates); | |
| 697 | ✗ | memcpy(idaData->statesDer, data->localData[0]->realVars + data->modelData->nStates, sizeof(double)*data->modelData->nStates); | |
| 698 | ✗ | memcpy(NV_DATA_S(idaData->y), idaData->states, idaData->N); | |
| 699 | ✗ | memcpy(NV_DATA_S(idaData->yp), idaData->statesDer, idaData->N); | |
| 700 | ✗ | flag = IDAReInit(idaData->ida_mem, | |
| 701 | ✗ | data->localData[0]->timeValue, | |
| 702 | idaData->y, | ||
| 703 | idaData->yp); | ||
| 704 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAReInit"); | |
| 705 | |||
| 706 | /* get initial step to provide a direction of the solution */ | ||
| 707 | ✗ | flag = IDAGetActualInitStep(idaData->ida_mem, &init_h); | |
| 708 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetActualInitStep"); | |
| 709 | /* provide a feasible step-size if it's too small */ | ||
| 710 | ✗ | if (init_h < DBL_EPSILON){ | |
| 711 | ✗ | init_h = DBL_EPSILON; | |
| 712 | ✗ | flag = IDASetInitStep(idaData->ida_mem, init_h); | |
| 713 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetInitStep"); | |
| 714 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## corrected step-size at %.15g", init_h); | |
| 715 | } | ||
| 716 | |||
| 717 | /* increase limits of the non-linear solver */ | ||
| 718 | ✗ | IDASetMaxNumStepsIC(idaData->ida_mem, 2*idaData->N*10); | |
| 719 | ✗ | IDASetMaxNumJacsIC(idaData->ida_mem, 2*idaData->N*10); | |
| 720 | ✗ | IDASetMaxNumItersIC(idaData->ida_mem, 2*idaData->N*10); | |
| 721 | /* Calc Consistent y_algebraic and y_prime with current y */ | ||
| 722 | ✗ | flag = IDACalcIC(idaData->ida_mem, IDA_YA_YDP_INIT, data->localData[0]->timeValue+init_h); | |
| 723 | |||
| 724 | /* debug */ | ||
| 725 | ✗ | IDAGetNumNonlinSolvIters(idaData->ida_mem, &nonLinIters); | |
| 726 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## IDACalcIC run status %d.\nIterations : %ld\n", flag, nonLinIters); | |
| 727 | |||
| 728 | /* try again without line search if first try fails */ | ||
| 729 | ✗ | if (!IDAflagIsSuccess(flag)){ | |
| 730 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## first event iteration failed. Start next try without line search!"); | |
| 731 | ✗ | IDASetLineSearchOffIC(idaData->ida_mem, 1 /* TRUE */); | |
| 732 | ✗ | flag = IDACalcIC(idaData->ida_mem, IDA_YA_YDP_INIT, data->localData[0]->timeValue+data->simulationInfo->tolerance); | |
| 733 | ✗ | IDAGetNumNonlinSolvIters(idaData->ida_mem, &nonLinIters); | |
| 734 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## IDACalcIC run status %d.\nIterations : %ld\n", flag, nonLinIters); | |
| 735 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetNumNonlinSolvIters"); | |
| 736 | } | ||
| 737 | /* obtain consistent values of y_algebraic and y_prime */ | ||
| 738 | ✗ | IDAGetConsistentIC(idaData->ida_mem, idaData->y, idaData->yp); | |
| 739 | |||
| 740 | /* update inner algebraic variables */ | ||
| 741 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 742 | ✗ | evaluateDAEResiduals_wrapperEventUpdate(data, threadData); | |
| 743 | |||
| 744 | ✗ | memcpy(data->localData[0]->realVars, idaData->states, sizeof(double)*data->modelData->nStates); | |
| 745 | // and also algebraic vars | ||
| 746 | ✗ | setAlgebraicDAEVars(data, idaData->states + data->modelData->nStates); | |
| 747 | ✗ | memcpy(data->localData[0]->realVars + data->modelData->nStates, idaData->statesDer, sizeof(double)*data->modelData->nStates); | |
| 748 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 749 | |||
| 750 | /* reset initial step size again to default */ | ||
| 751 | ✗ | IDASetInitStep(idaData->ida_mem, 0.0); | |
| 752 | |||
| 753 | ✗ | return 0; | |
| 754 | } | ||
| 755 | |||
| 756 | /** | ||
| 757 | * @brief Main IDA solver step. | ||
| 758 | * | ||
| 759 | * Call IDASolve to make solver steps. | ||
| 760 | * | ||
| 761 | * @param data Runtime data struct. | ||
| 762 | * @param threadData Thread data for error handling. | ||
| 763 | * @param solverInfo Main ODE/DAE solver info. | ||
| 764 | * @return int Return 0 on success or IDA flag on failure. | ||
| 765 | */ | ||
| 766 | /* Activate the daeMode homotopy ramp after a singular initial DAE Jacobian was | ||
| 767 | detected (IDA_LSETUP_FAIL). Sets the ramp window and caps the integrator step | ||
| 768 | so the lambda 0->1 transition is resolved gradually regardless of the | ||
| 769 | requested output interval count; the cap is lifted again in ida_solver_step | ||
| 770 | once the ramp is complete. */ | ||
| 771 | ✗ | static void idaActivateHomotopyRamp(IDA_SOLVER *idaData, DATA *data) | |
| 772 | { | ||
| 773 | ✗ | const char *e = getenv("OMC_DAE_HOMOTOPY_TRAMP"); | |
| 774 | ✗ | idaData->homotopyTramp = (e != NULL) ? atof(e) | |
| 775 | ✗ | : 0.1 * (data->simulationInfo->stopTime - data->simulationInfo->startTime); | |
| 776 | ✗ | idaData->homotopyRampActive = 1; | |
| 777 | ✗ | if (idaData->homotopyTramp > 0.0) | |
| 778 | ✗ | IDASetMaxStep(idaData->ida_mem, idaData->homotopyTramp / 50.0); | |
| 779 | ✗ | } | |
| 780 | |||
| 781 | /* Whether a failed IDASolve is the degenerate initial operating point the ramp | ||
| 782 | above recovers from. The Jacobian there is singular, but a numerical one is | ||
| 783 | only singular to the accuracy of its finite difference: a step that reaches | ||
| 784 | past the regularized region gives a matrix that factorizes, and the corrector | ||
| 785 | fails to converge instead. lambda is ramped over [startTime, startTime + | ||
| 786 | t_ramp], so this only applies while still at startTime. */ | ||
| 787 | static modelica_boolean idaHomotopyRampRecovers(IDA_SOLVER *idaData, DATA *data, | ||
| 788 | SOLVER_INFO *solverInfo, int flag) | ||
| 789 | { | ||
| 790 | ✗ | if (!idaData->daeMode || idaData->homotopyRampActive || | |
| 791 | ✗ | data->simulationInfo->homotopySteps <= 0) | |
| 792 | return FALSE; | ||
| 793 | ✗ | if (flag != IDA_LSETUP_FAIL && flag != IDA_CONV_FAIL && flag != IDA_ERR_FAIL) | |
| 794 | return FALSE; | ||
| 795 | ✗ | return solverInfo->currentTime <= data->simulationInfo->startTime; | |
| 796 | } | ||
| 797 | |||
| 798 | ✗ | int ida_solver_step(DATA* data, threadData_t *threadData, SOLVER_INFO* solverInfo) | |
| 799 | { | ||
| 800 | double tout = 0; | ||
| 801 | int i = 0, flag; | ||
| 802 | ✗ | int retVal = 0, finished = 0 /* FALSE */; | |
| 803 | int saveJumpState; | ||
| 804 | long int tmp; | ||
| 805 | static unsigned int stepsOutputCounter = 1; | ||
| 806 | int stepsMode; /* Has to be IDA_NORMAL (1) or IDA_ONE_STEP (2) */ | ||
| 807 | ✗ | int restartAfterLSFail = 0; | |
| 808 | modelica_boolean rampRecovery; | ||
| 809 | |||
| 810 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 811 | |||
| 812 | ✗ | IDA_SOLVER *idaData = (IDA_SOLVER*) solverInfo->solverData; | |
| 813 | |||
| 814 | ✗ | SIMULATION_DATA *sData = data->localData[0]; | |
| 815 | SIMULATION_DATA *sDataOld = data->localData[1]; | ||
| 816 | MODEL_DATA *mData = (MODEL_DATA*) data->modelData; | ||
| 817 | |||
| 818 | /* DAE-mode homotopy ramp: lambda is set smoothly as a function of time in the | ||
| 819 | residual callback (residualFunctionIDA). Once the ramp window has elapsed, | ||
| 820 | lift the step-size cap that was applied while ramping and pin lambda to 1 | ||
| 821 | (the actual model) for the rest of the integration. */ | ||
| 822 | ✗ | if (idaData->homotopyRampActive && idaData->homotopyTramp > 0.0 && | |
| 823 | ✗ | solverInfo->currentTime >= data->simulationInfo->startTime + idaData->homotopyTramp) | |
| 824 | { | ||
| 825 | ✗ | IDASetMaxStep(idaData->ida_mem, 0.0); /* 0 = no limit */ | |
| 826 | ✗ | data->simulationInfo->lambda = 1.0; | |
| 827 | ✗ | idaData->homotopyRampActive = 0; | |
| 828 | } | ||
| 829 | |||
| 830 | |||
| 831 | /* alloc all work arrays */ | ||
| 832 | ✗ | if (!idaData->daeMode) | |
| 833 | { | ||
| 834 | ✗ | N_VSetArrayPointer_Serial(data->localData[0]->realVars, idaData->y); | |
| 835 | ✗ | N_VSetArrayPointer_Serial(data->localData[1]->realVars + data->modelData->nStates, idaData->yp); | |
| 836 | } | ||
| 837 | |||
| 838 | ✗ | if (solverInfo->didEventStep) | |
| 839 | { | ||
| 840 | ✗ | idaData->setInitialSolution = 0; | |
| 841 | } | ||
| 842 | |||
| 843 | /* reinit solver */ | ||
| 844 | ✗ | if (!idaData->setInitialSolution) | |
| 845 | { | ||
| 846 | /* initialize states and der(states) */ | ||
| 847 | ✗ | if (idaData->daeMode) | |
| 848 | { | ||
| 849 | ✗ | memcpy(idaData->states, data->localData[0]->realVars, sizeof(double)*data->modelData->nStates); | |
| 850 | /* and also algebraic vars */ | ||
| 851 | ✗ | getAlgebraicDAEVars(data, idaData->states + data->modelData->nStates); | |
| 852 | ✗ | memcpy(idaData->statesDer, data->localData[0]->realVars + data->modelData->nStates, sizeof(double)*data->modelData->nStates); | |
| 853 | } | ||
| 854 | |||
| 855 | /* calculate matrix for residual scaling */ | ||
| 856 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) | |
| 857 | { | ||
| 858 | ✗ | getScalingFactors(data, idaData, NULL); | |
| 859 | |||
| 860 | /* scale idaData->y and idaData->yp */ | ||
| 861 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "Scale y and yp"); | |
| 862 | ✗ | idaScaleData(idaData); | |
| 863 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 864 | } | ||
| 865 | |||
| 866 | ✗ | flag = IDAReInit(idaData->ida_mem, | |
| 867 | solverInfo->currentTime, | ||
| 868 | idaData->y, | ||
| 869 | idaData->yp); | ||
| 870 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAReInit"); | |
| 871 | |||
| 872 | /* calculate matrix for residual scaling */ | ||
| 873 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) | |
| 874 | { | ||
| 875 | /* scale idaData->y and idaData->yp */ | ||
| 876 | ✗ | idaReScaleData(idaData); | |
| 877 | } | ||
| 878 | |||
| 879 | ✗ | if (idaData->idaSmode) | |
| 880 | { | ||
| 881 | ✗ | for(i=0; i<idaData->Ns; ++i) | |
| 882 | { | ||
| 883 | int j; | ||
| 884 | ✗ | for(j=0; j<idaData->N; ++j) | |
| 885 | { | ||
| 886 | ✗ | NV_Ith_S(idaData->yS[i],j) = 0; | |
| 887 | ✗ | NV_Ith_S(idaData->ySp[i],j) = 0; | |
| 888 | } | ||
| 889 | } | ||
| 890 | ✗ | flag = IDASensReInit(idaData->ida_mem, IDA_SIMULTANEOUS, idaData->yS, idaData->ySp); | |
| 891 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASensReInit"); | |
| 892 | } | ||
| 893 | ✗ | idaData->setInitialSolution = 1; | |
| 894 | } | ||
| 895 | |||
| 896 | ✗ | saveJumpState = threadData->currentErrorStage; | |
| 897 | ✗ | threadData->currentErrorStage = ERROR_INTEGRATOR; | |
| 898 | |||
| 899 | /* try */ | ||
| 900 | #if !defined(OMC_EMCC) | ||
| 901 | ✗ | OMC_TRY_INTERNAL(simulationJumpBuffer) | |
| 902 | #endif | ||
| 903 | |||
| 904 | |||
| 905 | /* Check that tout is not less than timeValue otherwise the solver | ||
| 906 | * will come in trouble. | ||
| 907 | * If that is the case we skip the current step. */ | ||
| 908 | ✗ | if (solverInfo->currentStepSize < DASSL_STEP_EPS) | |
| 909 | { | ||
| 910 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Desired step to small try next one"); | |
| 911 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "Interpolate linear"); | |
| 912 | |||
| 913 | /* linear extrapolation */ | ||
| 914 | ✗ | for(i = 0; i < idaData->N; i++) | |
| 915 | { | ||
| 916 | ✗ | NV_Ith_S(idaData->y, i) = NV_Ith_S(idaData->y, i) + NV_Ith_S(idaData->yp, i) * solverInfo->currentStepSize; | |
| 917 | } | ||
| 918 | ✗ | sData->timeValue = solverInfo->currentTime + solverInfo->currentStepSize; | |
| 919 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 920 | ✗ | data->callback->functionODE(data, threadData); | |
| 921 | ✗ | solverInfo->currentTime = sData->timeValue; | |
| 922 | |||
| 923 | ✗ | return 0; | |
| 924 | } | ||
| 925 | |||
| 926 | |||
| 927 | /* Calculate steps until TOUT is reached */ | ||
| 928 | ✗ | if (idaData->internalSteps) | |
| 929 | { | ||
| 930 | /* If internalSteps are selected, let IDA run to stopTime or next sample event */ | ||
| 931 | ✗ | if (data->simulationInfo->nextSampleEvent < data->simulationInfo->stopTime) | |
| 932 | { | ||
| 933 | tout = data->simulationInfo->nextSampleEvent; | ||
| 934 | } | ||
| 935 | else | ||
| 936 | { | ||
| 937 | tout = data->simulationInfo->stopTime; | ||
| 938 | } | ||
| 939 | stepsMode = IDA_ONE_STEP; | ||
| 940 | ✗ | flag = IDASetStopTime(idaData->ida_mem, tout); | |
| 941 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDASetStopTime"); | |
| 942 | } | ||
| 943 | else | ||
| 944 | { | ||
| 945 | ✗ | tout = solverInfo->currentTime + solverInfo->currentStepSize; | |
| 946 | stepsMode = IDA_NORMAL; | ||
| 947 | /* Never step past the next time event */ | ||
| 948 | ✗ | if (data->simulationInfo->nextSampleEvent < DBL_MAX) | |
| 949 | { | ||
| 950 | ✗ | IDASetStopTime(idaData->ida_mem, fmax(data->simulationInfo->nextSampleEvent, tout)); | |
| 951 | } | ||
| 952 | else | ||
| 953 | { | ||
| 954 | ✗ | IDAClearStopTime(idaData->ida_mem); | |
| 955 | } | ||
| 956 | } | ||
| 957 | |||
| 958 | |||
| 959 | do | ||
| 960 | { | ||
| 961 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 1, "##IDA## new step from %.15g to %.15g", solverInfo->currentTime, tout); | |
| 962 | |||
| 963 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 964 | /* read input vars */ | ||
| 965 | ✗ | externalInputUpdate(data); | |
| 966 | ✗ | data->callback->input_function(data, threadData); | |
| 967 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 968 | |||
| 969 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) | |
| 970 | { | ||
| 971 | /* scale idaData->y and idaData->yp */ | ||
| 972 | ✗ | idaScaleData(idaData); | |
| 973 | } | ||
| 974 | |||
| 975 | ✗ | flag = IDASolve(idaData->ida_mem, tout, &solverInfo->currentTime, idaData->y, idaData->yp, stepsMode); | |
| 976 | |||
| 977 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) | |
| 978 | { | ||
| 979 | /* rescale idaData->y and idaData->yp */ | ||
| 980 | ✗ | idaReScaleData(idaData); | |
| 981 | } | ||
| 982 | |||
| 983 | /* set time to current time */ | ||
| 984 | ✗ | sData->timeValue = solverInfo->currentTime; | |
| 985 | |||
| 986 | rampRecovery = idaHomotopyRampRecovers(idaData, data, solverInfo, flag); | ||
| 987 | |||
| 988 | /* error handling */ | ||
| 989 | ✗ | if (IDAflagIsSuccess(flag) && solverInfo->currentTime >= tout) | |
| 990 | { | ||
| 991 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## step done to time = %.15g", solverInfo->currentTime); | |
| 992 | finished = 1 /* TRUE */; | ||
| 993 | } | ||
| 994 | ✗ | else if (flag == IDA_ROOT_RETURN) | |
| 995 | { | ||
| 996 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## root found at time = %.15g", solverInfo->currentTime); | |
| 997 | finished = 1 /* TRUE */; | ||
| 998 | } | ||
| 999 | ✗ | else if (flag == IDA_SUCCESS || flag == IDA_TSTOP_RETURN) | |
| 1000 | { | ||
| 1001 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## continue integration time = %.15g", solverInfo->currentTime); | |
| 1002 | } | ||
| 1003 | ✗ | else if (flag == IDA_TOO_MUCH_WORK) | |
| 1004 | { | ||
| 1005 | ✗ | warningStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## has done too much work with small steps at time = %.15g", solverInfo->currentTime); | |
| 1006 | } | ||
| 1007 | ✗ | else if ((flag == IDA_LSETUP_FAIL || rampRecovery) && !restartAfterLSFail) | |
| 1008 | { | ||
| 1009 | ✗ | if (rampRecovery) | |
| 1010 | { | ||
| 1011 | ✗ | idaActivateHomotopyRamp(idaData, data); | |
| 1012 | ✗ | warningStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## degenerate DAE operating point at t = %.15g (flag %d); activating homotopy ramp", solverInfo->currentTime, flag); | |
| 1013 | } | ||
| 1014 | ✗ | flag = IDAReInit(idaData->ida_mem, | |
| 1015 | solverInfo->currentTime, | ||
| 1016 | idaData->y, | ||
| 1017 | idaData->yp); | ||
| 1018 | ✗ | restartAfterLSFail = 1; | |
| 1019 | ✗ | warningStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## solver failed, try once again at time = %.15g", solverInfo->currentTime); | |
| 1020 | } | ||
| 1021 | else | ||
| 1022 | { | ||
| 1023 | ✗ | infoStreamPrint(OMC_LOG_STDOUT, 0, "##IDA## %d error occurred at time = %.15g", flag, solverInfo->currentTime); | |
| 1024 | finished = 1 /* TRUE */; | ||
| 1025 | retVal = flag; | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | /* closing new step message */ | ||
| 1029 | ✗ | messageClose(OMC_LOG_SOLVER); | |
| 1030 | |||
| 1031 | /* emit step, if step mode is selected */ | ||
| 1032 | ✗ | if (idaData->internalSteps) | |
| 1033 | { | ||
| 1034 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## noEquadistant stepsOutputCounter %d by freq %d at time = %.15g", stepsOutputCounter, idaData->stepsFreq, solverInfo->currentTime); | |
| 1035 | ✗ | if (omc_flag[FLAG_NOEQUIDISTANT_OUT_FREQ]){ | |
| 1036 | /* output every n-th time step */ | ||
| 1037 | ✗ | if (stepsOutputCounter >= idaData->stepsFreq){ | |
| 1038 | ✗ | stepsOutputCounter = 1; /* next line set it to one */ | |
| 1039 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## noEquadistant output %d by freq at time = %.15g", stepsOutputCounter, solverInfo->currentTime); | |
| 1040 | break; | ||
| 1041 | } | ||
| 1042 | ✗ | stepsOutputCounter++; | |
| 1043 | ✗ | } else if (omc_flag[FLAG_NOEQUIDISTANT_OUT_TIME]){ | |
| 1044 | /* output when time>=k*timeValue */ | ||
| 1045 | ✗ | if (solverInfo->currentTime > stepsOutputCounter * idaData->stepsTime){ | |
| 1046 | ✗ | stepsOutputCounter++; | |
| 1047 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## noEquadistant output %d by time freq at time = %.15g", stepsOutputCounter, solverInfo->currentTime); | |
| 1048 | break; | ||
| 1049 | } | ||
| 1050 | } else { | ||
| 1051 | break; | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | ✗ | } while(!finished && !OMC_ERROR_RAISED()); | |
| 1056 | ✗ | if (OMC_ERROR_RAISED()) { OMC_ERROR_CLEAR(); } | |
| 1057 | |||
| 1058 | #if !defined(OMC_EMCC) | ||
| 1059 | ✗ | OMC_CATCH_INTERNAL(simulationJumpBuffer) | |
| 1060 | #endif | ||
| 1061 | ✗ | threadData->currentErrorStage = saveJumpState; | |
| 1062 | |||
| 1063 | /* if a state event occurs than no sample event does need to be activated */ | ||
| 1064 | ✗ | if (data->simulationInfo->sampleActivated && solverInfo->currentTime < data->simulationInfo->nextSampleEvent) | |
| 1065 | { | ||
| 1066 | ✗ | data->simulationInfo->sampleActivated = 0; | |
| 1067 | } | ||
| 1068 | |||
| 1069 | /* initialize states and der(states) */ | ||
| 1070 | ✗ | if (idaData->daeMode) | |
| 1071 | { | ||
| 1072 | ✗ | memcpy(data->localData[0]->realVars, idaData->states, sizeof(double)*data->modelData->nStates); | |
| 1073 | // and also algebraic vars | ||
| 1074 | ✗ | setAlgebraicDAEVars(data, idaData->states + data->modelData->nStates); | |
| 1075 | ✗ | memcpy(data->localData[0]->realVars + data->modelData->nStates, idaData->statesDer, sizeof(double)*data->modelData->nStates); | |
| 1076 | ✗ | sData->timeValue = solverInfo->currentTime; | |
| 1077 | } | ||
| 1078 | |||
| 1079 | /* sensitivity mode */ | ||
| 1080 | ✗ | if (idaData->idaSmode) | |
| 1081 | { | ||
| 1082 | ✗ | flag = IDAGetSens(idaData->ida_mem, &solverInfo->currentTime, idaData->ySResult); | |
| 1083 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetSens"); | |
| 1084 | } | ||
| 1085 | |||
| 1086 | /* save stats */ | ||
| 1087 | /* steps */ | ||
| 1088 | ✗ | tmp = 0; | |
| 1089 | ✗ | flag = IDAGetNumSteps(idaData->ida_mem, &tmp); | |
| 1090 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetNumSteps"); | |
| 1091 | ✗ | solverInfo->solverStatsTmp.nStepsTaken = tmp; | |
| 1092 | |||
| 1093 | /* functionODE evaluations */ | ||
| 1094 | ✗ | tmp = 0; | |
| 1095 | ✗ | flag = IDAGetNumResEvals(idaData->ida_mem, &tmp); | |
| 1096 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetNumResEvals"); | |
| 1097 | ✗ | solverInfo->solverStatsTmp.nCallsODE = tmp; | |
| 1098 | |||
| 1099 | /* Jacobians evaluations */ | ||
| 1100 | ✗ | tmp = 0; | |
| 1101 | ✗ | flag = IDAGetNumJacEvals(idaData->ida_mem, &tmp); | |
| 1102 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetNumJacEvals"); | |
| 1103 | ✗ | solverInfo->solverStatsTmp.nCallsJacobian = tmp; | |
| 1104 | |||
| 1105 | /* local error test failures */ | ||
| 1106 | ✗ | tmp = 0; | |
| 1107 | ✗ | flag = IDAGetNumErrTestFails(idaData->ida_mem, &tmp); | |
| 1108 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetNumErrTestFails"); | |
| 1109 | ✗ | solverInfo->solverStatsTmp.nErrorTestFailures = tmp; | |
| 1110 | |||
| 1111 | /* local error test failures */ | ||
| 1112 | ✗ | tmp = 0; | |
| 1113 | ✗ | flag = IDAGetNumNonlinSolvConvFails(idaData->ida_mem, &tmp); | |
| 1114 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_IDA_FLAG, "IDAGetNumNonlinSolvConvFails"); | |
| 1115 | ✗ | solverInfo->solverStatsTmp.nConvergenceTestFailures = tmp; | |
| 1116 | |||
| 1117 | /* get more statistics */ | ||
| 1118 | ✗ | if (omc_useStream[OMC_LOG_SOLVER_V]) | |
| 1119 | { | ||
| 1120 | long int tmp1,tmp2; | ||
| 1121 | double dtmp; | ||
| 1122 | |||
| 1123 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "### IDAStats ###"); | |
| 1124 | /* nonlinear stats */ | ||
| 1125 | ✗ | tmp1 = tmp2 = 0; | |
| 1126 | ✗ | flag = IDAGetNonlinSolvStats(idaData->ida_mem, &tmp1, &tmp2); | |
| 1127 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, " ## Cumulative number of nonlinear iterations performed: %ld", tmp1); | |
| 1128 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, " ## Cumulative number of nonlinear convergence failures that have occurred: %ld", tmp2); | |
| 1129 | |||
| 1130 | /* others */ | ||
| 1131 | ✗ | flag = IDAGetTolScaleFactor(idaData->ida_mem, &dtmp); | |
| 1132 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, " ## Suggested scaling factor for user tolerances: %g", dtmp); | |
| 1133 | |||
| 1134 | ✗ | flag = IDAGetNumLinSolvSetups(idaData->ida_mem, &tmp1); | |
| 1135 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, " ## Number of calls made to the linear solver setup function: %ld", tmp1); | |
| 1136 | |||
| 1137 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1138 | } | ||
| 1139 | |||
| 1140 | ✗ | infoStreamPrint(OMC_LOG_SOLVER, 0, "##IDA## Finished Integrator step."); | |
| 1141 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1142 | |||
| 1143 | ✗ | return retVal; | |
| 1144 | } | ||
| 1145 | |||
| 1146 | /** | ||
| 1147 | * @brief Compute residual F(t, y, y'). | ||
| 1148 | * | ||
| 1149 | * This function has to be of type IDAResFn. | ||
| 1150 | * See section 4.6.1 Residual function of SUNDIALS v5.4.0 IDA documentation. | ||
| 1151 | * | ||
| 1152 | * @param time Value of the independent variable (time). | ||
| 1153 | * @param yy Vector of state variables y(t). | ||
| 1154 | * @param yp Vector of derivatives y'(t). | ||
| 1155 | * @param rr Output residual vector F(t, y, y'). | ||
| 1156 | * @param user_data Pointer to user data of type IDA_SOLVER*, set with IDASetUserDat. | ||
| 1157 | * @return int Return 0 on success, positive value on recoverable error and negative value otherwise. | ||
| 1158 | */ | ||
| 1159 | ✗ | static int residualFunctionIDA(double time, N_Vector yy, N_Vector yp, N_Vector rr, void* user_data) | |
| 1160 | { | ||
| 1161 | IDA_SOLVER* idaData = (IDA_SOLVER*) user_data; | ||
| 1162 | ✗ | DATA* data = idaData->userData->data; | |
| 1163 | ✗ | threadData_t* threadData = idaData->userData->threadData; | |
| 1164 | |||
| 1165 | double timeBackup; | ||
| 1166 | long int i; | ||
| 1167 | int saveJumpState; | ||
| 1168 | ✗ | int success = 0, retVal = 0; | |
| 1169 | ✗ | double *states = N_VGetArrayPointer_Serial(yy); | |
| 1170 | ✗ | double *statesDer = N_VGetArrayPointer_Serial(yp); | |
| 1171 | ✗ | double *delta = N_VGetArrayPointer_Serial(rr); | |
| 1172 | |||
| 1173 | /* DAE-mode homotopy ramp for a degenerate initial operating point: a | ||
| 1174 | homotopy()-regularized characteristic (e.g. a pump at zero flow/speed) makes | ||
| 1175 | the actual (lambda=1) DAE Jacobian singular near t=0, so IDA's LU | ||
| 1176 | factorization fails. The simplified (lambda<1) branch is non-singular, so | ||
| 1177 | once such a failure is detected (homotopyRampActive set in ida_solver_step) | ||
| 1178 | we ramp lambda 0->1 smoothly over [startTime, startTime+t_ramp]. This is | ||
| 1179 | activated only on failure, so models that integrate normally are unaffected. | ||
| 1180 | t_ramp defaults to 10% of the simulation interval (tunable via env var). */ | ||
| 1181 | ✗ | if (idaData->daeMode && idaData->homotopyRampActive && data->simulationInfo->homotopySteps > 0) { | |
| 1182 | ✗ | double tRamp = idaData->homotopyTramp; | |
| 1183 | ✗ | data->simulationInfo->lambda = (tRamp > 0.0 && time < data->simulationInfo->startTime + tRamp) | |
| 1184 | ✗ | ? ((time - data->simulationInfo->startTime) / tRamp) : 1.0; | |
| 1185 | } | ||
| 1186 | |||
| 1187 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "### eval residualFunctionIDA ###"); | |
| 1188 | /* rescale idaData->y and idaData->yp */ | ||
| 1189 | ✗ | if ((omc_flag[FLAG_IDA_SCALING] && idaData->useScaling)) | |
| 1190 | { | ||
| 1191 | ✗ | idaReScaleData(idaData); | |
| 1192 | } | ||
| 1193 | |||
| 1194 | ✗ | if (data->simulationInfo->currentContext == CONTEXT_ALGEBRAIC) | |
| 1195 | { | ||
| 1196 | ✗ | setContext(data, time, CONTEXT_ODE); | |
| 1197 | } | ||
| 1198 | ✗ | data->localData[0]->timeValue = time; | |
| 1199 | |||
| 1200 | ✗ | saveJumpState = threadData->currentErrorStage; | |
| 1201 | ✗ | threadData->currentErrorStage = ERROR_INTEGRATOR; | |
| 1202 | |||
| 1203 | /* try */ | ||
| 1204 | #if !defined(OMC_EMCC) | ||
| 1205 | ✗ | OMC_TRY_INTERNAL(simulationJumpBuffer) | |
| 1206 | #endif | ||
| 1207 | |||
| 1208 | /* if sensitivity mode update also bound parameters*/ | ||
| 1209 | ✗ | if (idaData->idaSmode) | |
| 1210 | { | ||
| 1211 | ✗ | data->callback->updateBoundParameters(data, threadData); | |
| 1212 | } | ||
| 1213 | /* if daeMode update also all dynamic algebraic equations */ | ||
| 1214 | ✗ | if (idaData->daeMode) | |
| 1215 | { | ||
| 1216 | /* set state, state derivative and dynamic algebraic | ||
| 1217 | * variables for evaluateDAEResiduals evaluation | ||
| 1218 | */ | ||
| 1219 | ✗ | memcpy(data->localData[0]->realVars, states, sizeof(double)*data->modelData->nStates); | |
| 1220 | ✗ | memcpy(data->localData[0]->realVars + data->modelData->nStates, statesDer, sizeof(double)*data->modelData->nStates); | |
| 1221 | ✗ | setAlgebraicDAEVars(data, states + data->modelData->nStates); | |
| 1222 | } | ||
| 1223 | |||
| 1224 | /* debug */ | ||
| 1225 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_SOLVER_V)){ | |
| 1226 | ✗ | printCurrentStatesVector(OMC_LOG_SOLVER_V, data->localData[0]->realVars, data, time); | |
| 1227 | ✗ | printVector(OMC_LOG_SOLVER_V, "yprime", data->localData[0]->realVars + data->modelData->nStates, data->modelData->nStates, time); | |
| 1228 | ✗ | if (idaData->daeMode) | |
| 1229 | { | ||
| 1230 | ✗ | printVector(OMC_LOG_SOLVER_V, "yalg", states + data->modelData->nStates, data->simulationInfo->daeModeData->nAlgebraicDAEVars, time); | |
| 1231 | } | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | /* read input vars */ | ||
| 1235 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1236 | ✗ | externalInputUpdate(data); | |
| 1237 | ✗ | data->callback->input_function(data, threadData); | |
| 1238 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 1239 | |||
| 1240 | ✗ | if (idaData->daeMode) | |
| 1241 | { | ||
| 1242 | /* eval residual vars */ | ||
| 1243 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1244 | ✗ | data->simulationInfo->daeModeData->evaluateDAEResiduals(data, threadData, EVAL_DYNAMIC); | |
| 1245 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 1246 | /* get residual variables */ | ||
| 1247 | ✗ | for(i=0; i < idaData->N; i++) | |
| 1248 | { | ||
| 1249 | ✗ | NV_Ith_S(rr, i) = data->simulationInfo->daeModeData->residualVars[i]; | |
| 1250 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, "%ld. residual = %e", i, NV_Ith_S(rr, i)); | |
| 1251 | } | ||
| 1252 | } | ||
| 1253 | else | ||
| 1254 | { | ||
| 1255 | /* In sensitivity mode IDAS evaluates the residual on perturbed copies of the | ||
| 1256 | * state vector (yy != idaData->y, which otherwise shares storage with realVars). | ||
| 1257 | * Copy the perturbed states into realVars so functionODE sees them; otherwise the | ||
| 1258 | * dF/dy term is lost from the sensitivity difference quotient. The base states are | ||
| 1259 | * backed up and restored afterwards so idaData->y is not corrupted for the | ||
| 1260 | * remaining sensitivity equations of the same difference-quotient sweep. */ | ||
| 1261 | ✗ | const int perturbedStates = (idaData->idaSmode && data->localData[0]->realVars != states); | |
| 1262 | if (perturbedStates) | ||
| 1263 | { | ||
| 1264 | ✗ | memcpy(idaData->ysave, data->localData[0]->realVars, sizeof(double)*data->modelData->nStates); | |
| 1265 | ✗ | memcpy(data->localData[0]->realVars, states, sizeof(double)*data->modelData->nStates); | |
| 1266 | } | ||
| 1267 | /* eval function ODE */ | ||
| 1268 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1269 | ✗ | data->callback->functionODE(data, threadData); | |
| 1270 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 1271 | ✗ | for(i=0; i < idaData->N; i++) | |
| 1272 | { | ||
| 1273 | ✗ | NV_Ith_S(rr, i) = data->localData[0]->realVars[data->modelData->nStates + i] - NV_Ith_S(yp, i); | |
| 1274 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 0, "%ld. residual = %e", i, NV_Ith_S(rr, i)); | |
| 1275 | } | ||
| 1276 | ✗ | if (perturbedStates) | |
| 1277 | ✗ | memcpy(data->localData[0]->realVars, idaData->ysave, sizeof(double)*data->modelData->nStates); | |
| 1278 | } | ||
| 1279 | |||
| 1280 | /* scale ressidual rr */ | ||
| 1281 | ✗ | if ((omc_flag[FLAG_IDA_SCALING] && idaData->useScaling)) | |
| 1282 | { | ||
| 1283 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "scale residuals"); | |
| 1284 | ✗ | idaScaleVector(rr, idaData->resScale, idaData->N); | |
| 1285 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1286 | ✗ | idaScaleData(idaData); | |
| 1287 | } | ||
| 1288 | |||
| 1289 | ✗ | printVector(OMC_LOG_DASSL_STATES, "delta", delta, idaData->N, time); | |
| 1290 | ✗ | if (OMC_ERROR_RAISED()) { OMC_ERROR_CLEAR(); } else { success = 1; } | |
| 1291 | #if !defined(OMC_EMCC) | ||
| 1292 | ✗ | OMC_CATCH_INTERNAL(simulationJumpBuffer) | |
| 1293 | #endif | ||
| 1294 | |||
| 1295 | ✗ | if (!success) { | |
| 1296 | retVal = 1; /* Recoverable error, reduce step size and retry */ | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | ✗ | threadData->currentErrorStage = saveJumpState; | |
| 1300 | |||
| 1301 | ✗ | if (data->simulationInfo->currentContext == CONTEXT_ODE){ | |
| 1302 | ✗ | unsetContext(data); | |
| 1303 | } | ||
| 1304 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1305 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1306 | |||
| 1307 | ✗ | return retVal; | |
| 1308 | } | ||
| 1309 | |||
| 1310 | /** | ||
| 1311 | * @brief Evaluate zero crossings for IDA root finding. | ||
| 1312 | * | ||
| 1313 | * Set by IDARootInit, has be of type IDARootFn. | ||
| 1314 | * See section 4.5.6 Rootfinding initialization function of SUNDIALS v5.4.0 IDA documentation. | ||
| 1315 | * | ||
| 1316 | * @param time Independent variable (time). | ||
| 1317 | * @param yy Vector of state variables y. | ||
| 1318 | * @param yp Vector of state derivatives y'. | ||
| 1319 | * @param gout Output array: ZeroCrossings g(t, y, y'). | ||
| 1320 | * @param user_data Pointer to user data of type `IDA_SOLVER*`. | ||
| 1321 | * @return int Return 0 on success and otherwise error. | ||
| 1322 | */ | ||
| 1323 | ✗ | static int rootsFunctionIDA(double time, N_Vector yy, N_Vector yp, double *gout, void* user_data) | |
| 1324 | { | ||
| 1325 | IDA_SOLVER* idaData = (IDA_SOLVER*) user_data; | ||
| 1326 | ✗ | DATA* data = idaData->userData->data; | |
| 1327 | ✗ | threadData_t* threadData = idaData->userData->threadData; | |
| 1328 | ✗ | double *states = N_VGetArrayPointer_Serial(yy); | |
| 1329 | ✗ | double *statesDer = N_VGetArrayPointer_Serial(yp); | |
| 1330 | |||
| 1331 | int saveJumpState; | ||
| 1332 | |||
| 1333 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "### eval rootsFunctionIDA ###"); | |
| 1334 | |||
| 1335 | ✗ | if (data->simulationInfo->currentContext == CONTEXT_ALGEBRAIC) | |
| 1336 | { | ||
| 1337 | ✗ | setContext(data, time, CONTEXT_EVENTS); | |
| 1338 | } | ||
| 1339 | |||
| 1340 | /* re-scale idaData->y and idaData->yp to evaluate the equations */ | ||
| 1341 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) | |
| 1342 | { | ||
| 1343 | ✗ | idaReScaleData(idaData); | |
| 1344 | } | ||
| 1345 | |||
| 1346 | ✗ | saveJumpState = threadData->currentErrorStage; | |
| 1347 | ✗ | threadData->currentErrorStage = ERROR_EVENTSEARCH; | |
| 1348 | |||
| 1349 | ✗ | if (idaData->daeMode) | |
| 1350 | { | ||
| 1351 | ✗ | memcpy(data->localData[0]->realVars, states, sizeof(double)*data->modelData->nStates); | |
| 1352 | ✗ | setAlgebraicDAEVars(data, states + data->modelData->nStates); | |
| 1353 | ✗ | memcpy(data->localData[0]->realVars + data->modelData->nStates, statesDer, sizeof(double)*data->modelData->nStates); | |
| 1354 | } | ||
| 1355 | |||
| 1356 | ✗ | data->localData[0]->timeValue = time; | |
| 1357 | |||
| 1358 | /* Exlude zero-crossings eval from sim timer */ | ||
| 1359 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1360 | |||
| 1361 | /* Update inputs and evaluate zero crossings */ | ||
| 1362 | ✗ | externalInputUpdate(data); | |
| 1363 | ✗ | data->callback->input_function(data, threadData); | |
| 1364 | ✗ | if (idaData->daeMode){ | |
| 1365 | ✗ | data->simulationInfo->daeModeData->evaluateDAEResiduals(data, threadData, EVAL_ZEROCROSS); | |
| 1366 | } | ||
| 1367 | else | ||
| 1368 | { | ||
| 1369 | ✗ | data->callback->function_ZeroCrossingsEquations(data, threadData); | |
| 1370 | } | ||
| 1371 | ✗ | data->callback->function_ZeroCrossings(data, threadData, gout); | |
| 1372 | |||
| 1373 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 1374 | ✗ | threadData->currentErrorStage = saveJumpState; | |
| 1375 | |||
| 1376 | /* scale data again */ | ||
| 1377 | ✗ | if (omc_flag[FLAG_IDA_SCALING]) | |
| 1378 | { | ||
| 1379 | ✗ | idaScaleData(idaData); | |
| 1380 | } | ||
| 1381 | |||
| 1382 | ✗ | if (data->simulationInfo->currentContext == CONTEXT_EVENTS){ | |
| 1383 | ✗ | unsetContext(data); | |
| 1384 | } | ||
| 1385 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1386 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); // TODO: Why do we have two rt_tick calls? Keep only this one? | |
| 1387 | |||
| 1388 | ✗ | return 0; | |
| 1389 | } | ||
| 1390 | |||
| 1391 | /** | ||
| 1392 | * @brief Compute colored numeric Jacobian. | ||
| 1393 | * | ||
| 1394 | * Calculate Jacobian matrix using finite differences method | ||
| 1395 | * with coloring from sparsity pattern. | ||
| 1396 | * | ||
| 1397 | * @param currentTime Independent variable (time). | ||
| 1398 | * @param cj Scalar in the system Jacobian, proportional to the inverse of the step size. | ||
| 1399 | * @param yy Vector of state variables y. | ||
| 1400 | * @param yp Vector of derivatives y'. | ||
| 1401 | * @param rr Vector of residual vector F(y,y'). | ||
| 1402 | * @param Jac Output Jacobian: J = (∂F)/(∂y). | ||
| 1403 | * @param idaData Pointer to IDA user data. | ||
| 1404 | * @return int Return 0 on success, positive value on recoverable error and negative value otherwise. | ||
| 1405 | */ | ||
| 1406 | ✗ | static int jacColoredNumericalDense(double currentTime, double cj, N_Vector yy, N_Vector yp, | |
| 1407 | N_Vector rr, SUNMatrix Jac, IDA_SOLVER *idaData) | ||
| 1408 | { | ||
| 1409 | ✗ | DATA* data = idaData->userData->data; | |
| 1410 | ✗ | void* ida_mem = idaData->ida_mem; | |
| 1411 | ✗ | const int index = data->callback->INDEX_JAC_A; | |
| 1412 | |||
| 1413 | /* prepare variables */ | ||
| 1414 | ✗ | double *states = N_VGetArrayPointer_Serial(yy); | |
| 1415 | ✗ | double *yprime = N_VGetArrayPointer_Serial(yp); | |
| 1416 | ✗ | double *delta = N_VGetArrayPointer_Serial(rr); | |
| 1417 | ✗ | double *newdelta = N_VGetArrayPointer_Serial(idaData->newdelta); | |
| 1418 | |||
| 1419 | ✗ | double *delta_hh = idaData->delta_hh; | |
| 1420 | ✗ | double *ysave = idaData->ysave; | |
| 1421 | ✗ | double *ypsave = idaData->ypsave; | |
| 1422 | |||
| 1423 | double delta_hhh; | ||
| 1424 | ✗ | double *abstol = N_VGetArrayPointer_Serial(idaData->absoluteTolerance); | |
| 1425 | ✗ | double rtol = data->simulationInfo->tolerance; | |
| 1426 | long int i,j,l,ii; | ||
| 1427 | |||
| 1428 | double currentStep; | ||
| 1429 | |||
| 1430 | /* set values */ | ||
| 1431 | ✗ | IDAGetCurrentStep(ida_mem, ¤tStep); | |
| 1432 | |||
| 1433 | SPARSE_PATTERN* sparsePattern; | ||
| 1434 | |||
| 1435 | /* set sparse pattern */ | ||
| 1436 | ✗ | if (idaData->daeMode) | |
| 1437 | { | ||
| 1438 | ✗ | sparsePattern = data->simulationInfo->daeModeData->sparsePattern; | |
| 1439 | } | ||
| 1440 | else | ||
| 1441 | { | ||
| 1442 | ✗ | sparsePattern = data->simulationInfo->analyticJacobians[index].sparsePattern; | |
| 1443 | } | ||
| 1444 | |||
| 1445 | ✗ | setContext(data, currentTime, CONTEXT_JACOBIAN); | |
| 1446 | |||
| 1447 | ✗ | for(i = 0; i < sparsePattern->maxColors; i++) | |
| 1448 | { | ||
| 1449 | ✗ | for(ii=0; ii < idaData->N; ii++) | |
| 1450 | { | ||
| 1451 | ✗ | if(sparsePattern->colorCols[ii]-1 == i) | |
| 1452 | { | ||
| 1453 | ✗ | delta_hhh = currentStep * yprime[ii]; | |
| 1454 | ✗ | delta_hh[ii] = numericalJacobianStep(states[ii], delta_hhh, rtol*fabs(states[ii]) + abstol[ii], | |
| 1455 | ✗ | idaData->jacNominalFactor * idaData->nominal[ii]); | |
| 1456 | ✗ | delta_hh[ii] = (delta_hhh >= 0 ? delta_hh[ii] : -delta_hh[ii]); | |
| 1457 | ✗ | delta_hh[ii] = (states[ii] + delta_hh[ii]) - states[ii]; // Due to floating-point arithmetic rounding errors can result in: delta_hh[ii] != (states[ii] + delta_hh[ii]) - states[ii] | |
| 1458 | ✗ | ysave[ii] = states[ii]; | |
| 1459 | ✗ | states[ii] += delta_hh[ii]; | |
| 1460 | |||
| 1461 | ✗ | if (idaData->daeMode){ | |
| 1462 | ✗ | ypsave[ii] = yprime[ii]; | |
| 1463 | ✗ | yprime[ii] += cj * delta_hh[ii]; | |
| 1464 | } | ||
| 1465 | |||
| 1466 | ✗ | delta_hh[ii] = 1. / delta_hh[ii]; | |
| 1467 | } | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | ✗ | idaData->residualFunction(currentTime, yy, yp, idaData->newdelta, (void*) idaData); /* Points to residualFunctionIDA */ | |
| 1471 | |||
| 1472 | ✗ | increaseJacContext(data); | |
| 1473 | |||
| 1474 | ✗ | for(ii = 0; ii < idaData->N; ii++) | |
| 1475 | { | ||
| 1476 | ✗ | if(sparsePattern->colorCols[ii]-1 == i) | |
| 1477 | { | ||
| 1478 | ✗ | j = sparsePattern->leadindex[ii]; | |
| 1479 | ✗ | while(j < sparsePattern->leadindex[ii+1]) | |
| 1480 | { | ||
| 1481 | ✗ | l = sparsePattern->index[j]; | |
| 1482 | ✗ | SM_ELEMENT_D(Jac, l, ii) = (newdelta[l] - delta[l]) * delta_hh[ii]; | |
| 1483 | ✗ | j++; | |
| 1484 | }; | ||
| 1485 | ✗ | states[ii] = ysave[ii]; | |
| 1486 | ✗ | if (idaData->daeMode) | |
| 1487 | { | ||
| 1488 | ✗ | yprime[ii] = ypsave[ii]; | |
| 1489 | } | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | } | ||
| 1493 | ✗ | unsetContext(data); | |
| 1494 | |||
| 1495 | ✗ | return 0; | |
| 1496 | } | ||
| 1497 | |||
| 1498 | /** | ||
| 1499 | * @brief Compute colored symbolic Jacobian. | ||
| 1500 | * | ||
| 1501 | * Calculate the Jacobian matrix with the shared symbolic Jacobian evaluation, which | ||
| 1502 | * transparently handles forward (coloredSymbolical), adjoint (coloredSymbolicalAdjoint) | ||
| 1503 | * and bidirectional (bicoloredSymbolical) evaluation. | ||
| 1504 | * | ||
| 1505 | * @param currentTime Independent variable (time). | ||
| 1506 | * @param cj Scalar in the system Jacobian, proportional to the inverse of the step size. | ||
| 1507 | * @param yy Vector of state variables y. | ||
| 1508 | * @param yp Vector of derivatives y'. | ||
| 1509 | * @param rr Vector of residual vector F(y,y'). | ||
| 1510 | * @param Jac Output Jacobian: J = (∂F)/(∂y) + cj * (∂F)/(∂y'). | ||
| 1511 | * @param idaData Pointer to IDA user data. | ||
| 1512 | * @return int Return 0 on success, positive value on recoverable error and negative value otherwise. | ||
| 1513 | */ | ||
| 1514 | ✗ | static int jacColoredSymbolicalDense(double currentTime, double cj, N_Vector yy, | |
| 1515 | N_Vector yp, N_Vector rr, SUNMatrix Jac, | ||
| 1516 | IDA_SOLVER *idaData) | ||
| 1517 | { | ||
| 1518 | ✗ | DATA* data = idaData->userData->data; | |
| 1519 | ✗ | threadData_t* threadData = idaData->userData->threadData; | |
| 1520 | ✗ | JACOBIAN* jac = getSymbolicOdeJacobian(data); | |
| 1521 | ✗ | jac->dae_cj = cj; | |
| 1522 | |||
| 1523 | ✗ | setContext(data, currentTime, CONTEXT_SYM_JACOBIAN); /* Reuse jacobian matrix in KLU solver */ | |
| 1524 | ✗ | evalJacobian(data, threadData, jac, NULL, SM_DATA_D(Jac), TRUE); | |
| 1525 | |||
| 1526 | ✗ | unsetContext(data); | |
| 1527 | |||
| 1528 | ✗ | return 0; | |
| 1529 | } | ||
| 1530 | |||
| 1531 | /** | ||
| 1532 | * @brief Compute colored Jacobian matrix of ODE/DAE system. | ||
| 1533 | * | ||
| 1534 | * Available methods: | ||
| 1535 | * - Colored Numeric Jacobian --> jacColoredNumericalDense | ||
| 1536 | * - Colored Symbolic Jacobian --> jacColoredSymbolicalDense | ||
| 1537 | * | ||
| 1538 | * See Section 4.6.5 in IDA documentation of SUNDIALS v5.4.0 for more details. | ||
| 1539 | * | ||
| 1540 | * @param tt Independent variable (time). | ||
| 1541 | * @param cj Scalar in the system Jacobian, proportional to the inverse of the step size. | ||
| 1542 | * @param yy Vector of state variables y. | ||
| 1543 | * @param yp Vector of state derivatives y'. | ||
| 1544 | * @param rr Vector of residual vector F(y,y'). | ||
| 1545 | * @param Jac Output Jacobian: J = (∂F)/(∂y) + cj * (∂F)/(∂y'). | ||
| 1546 | * @param user_data Pointer to user data of type `IDA_SOLVER*`. | ||
| 1547 | * @param tmp1 Work array that can be used by, currently unused. | ||
| 1548 | * @param tmp2 Work array that can be used by, currently unused. | ||
| 1549 | * @param tmp3 Work array that can be used by, currently unused. | ||
| 1550 | * @return int Return 0 on success, positive value on recoverable error and negative value otherwise. | ||
| 1551 | */ | ||
| 1552 | ✗ | static int callDenseJacobian(sunrealtype tt, sunrealtype cj, N_Vector yy, | |
| 1553 | N_Vector yp, N_Vector rr, SUNMatrix Jac, | ||
| 1554 | void *user_data, N_Vector tmp1, N_Vector tmp2, | ||
| 1555 | N_Vector tmp3) { | ||
| 1556 | IDA_SOLVER* idaData = (IDA_SOLVER*) user_data; | ||
| 1557 | ✗ | threadData_t* threadData = idaData->userData->threadData; | |
| 1558 | int retVal; | ||
| 1559 | |||
| 1560 | /* profiling */ | ||
| 1561 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1562 | ✗ | rt_tick(SIM_TIMER_JACOBIAN); | |
| 1563 | |||
| 1564 | ✗ | if (idaData->jacobianMethod == COLOREDNUMJAC || idaData->jacobianMethod == NUMJAC) | |
| 1565 | { | ||
| 1566 | ✗ | retVal = jacColoredNumericalDense(tt, cj, yy, yp, rr, Jac, idaData); | |
| 1567 | } | ||
| 1568 | ✗ | else if (idaData->jacobianMethod == COLOREDSYMJAC || idaData->jacobianMethod == SYMJAC | |
| 1569 | ✗ | || idaData->jacobianMethod == COLOREDSYMJACADJ || idaData->jacobianMethod == BICOLOREDSYMJAC) | |
| 1570 | { | ||
| 1571 | ✗ | retVal = jacColoredSymbolicalDense(tt, cj, yy, yp, rr, Jac, idaData); | |
| 1572 | } | ||
| 1573 | else | ||
| 1574 | { | ||
| 1575 | ✗ | throwStreamPrint(threadData, "##IDA## Something went wrong while obtain Jacobian matrix."); | |
| 1576 | } | ||
| 1577 | |||
| 1578 | /* debug */ | ||
| 1579 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_JAC)){ | |
| 1580 | ✗ | _omc_matrix* dumpJac = _omc_createMatrix(idaData->N, idaData->N, SM_DATA_D(Jac)); | |
| 1581 | ✗ | _omc_printMatrix(dumpJac, "IDA-Solver: Matrix A", OMC_LOG_JAC); | |
| 1582 | ✗ | _omc_destroyMatrix(dumpJac); | |
| 1583 | } | ||
| 1584 | |||
| 1585 | /* add cj to diagonal elements and store in Jac */ | ||
| 1586 | ✗ | if (!idaData->daeMode) | |
| 1587 | { | ||
| 1588 | ✗ | for(int i = 0; i < SM_COLUMNS_D(Jac); i++) | |
| 1589 | { | ||
| 1590 | ✗ | SM_ELEMENT_D(Jac, i, i) -= (double) cj; | |
| 1591 | } | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | /* profiling */ | ||
| 1595 | ✗ | rt_accumulate(SIM_TIMER_JACOBIAN); | |
| 1596 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 1597 | |||
| 1598 | ✗ | return retVal; | |
| 1599 | } | ||
| 1600 | |||
| 1601 | /* | ||
| 1602 | * function calculates a jacobian matrix by | ||
| 1603 | * numerical method finite differences with coloring | ||
| 1604 | * into a sparse SlsMat matrix | ||
| 1605 | */ | ||
| 1606 | ✗ | static int jacoColoredNumericalSparse(double currentTime, N_Vector yy, | |
| 1607 | N_Vector yp, N_Vector rr, SUNMatrix Jac, | ||
| 1608 | double cj, void *userData) { | ||
| 1609 | IDA_SOLVER* idaData = (IDA_SOLVER*)userData; | ||
| 1610 | ✗ | DATA* data = (DATA*)(((IDA_USERDATA*)idaData->userData)->data); | |
| 1611 | ✗ | void* ida_mem = idaData->ida_mem; | |
| 1612 | ✗ | const int index = data->callback->INDEX_JAC_A; | |
| 1613 | |||
| 1614 | /* prepare variables */ | ||
| 1615 | ✗ | double *states = N_VGetArrayPointer_Serial(yy); | |
| 1616 | ✗ | double *yprime = N_VGetArrayPointer_Serial(yp); | |
| 1617 | ✗ | double *delta = N_VGetArrayPointer_Serial(rr); | |
| 1618 | ✗ | double *newdelta = N_VGetArrayPointer_Serial(idaData->newdelta); | |
| 1619 | |||
| 1620 | SPARSE_PATTERN* sparsePattern; | ||
| 1621 | |||
| 1622 | ✗ | double *ysave = idaData->ysave; | |
| 1623 | ✗ | double *ypsave = idaData->ypsave; | |
| 1624 | |||
| 1625 | ✗ | double *delta_hh = idaData->delta_hh; | |
| 1626 | double delta_hhh; | ||
| 1627 | ✗ | double *abstol = N_VGetArrayPointer_Serial(idaData->absoluteTolerance); | |
| 1628 | ✗ | double rtol = data->simulationInfo->tolerance; | |
| 1629 | |||
| 1630 | long int i,j,ii; | ||
| 1631 | int nth = 0; | ||
| 1632 | ✗ | int disBackup = idaData->useScaling; | |
| 1633 | |||
| 1634 | double currentStep; | ||
| 1635 | |||
| 1636 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "### eval jacobianSparseNumIDA ###"); | |
| 1637 | /* set values */ | ||
| 1638 | ✗ | IDAGetCurrentStep(ida_mem, ¤tStep); | |
| 1639 | |||
| 1640 | /* set sparse pattern */ | ||
| 1641 | ✗ | if (idaData->daeMode) | |
| 1642 | { | ||
| 1643 | ✗ | sparsePattern = data->simulationInfo->daeModeData->sparsePattern; | |
| 1644 | } | ||
| 1645 | else | ||
| 1646 | { | ||
| 1647 | ✗ | sparsePattern = data->simulationInfo->analyticJacobians[index].sparsePattern; | |
| 1648 | } | ||
| 1649 | |||
| 1650 | /* Reset Jacobian matrix */ | ||
| 1651 | ✗ | SUNMatZero(Jac); | |
| 1652 | |||
| 1653 | ✗ | setContext(data, currentTime, CONTEXT_JACOBIAN); | |
| 1654 | |||
| 1655 | /* rescale idaData->y and idaData->yp | ||
| 1656 | * the evaluation of the residual function | ||
| 1657 | * needs to be performed on unscaled values | ||
| 1658 | */ | ||
| 1659 | ✗ | if (omc_flag[FLAG_IDA_SCALING] && idaData->useScaling) | |
| 1660 | { | ||
| 1661 | ✗ | idaReScaleVector(rr, idaData->resScale, idaData->N); | |
| 1662 | ✗ | idaReScaleData(idaData); | |
| 1663 | } | ||
| 1664 | |||
| 1665 | ✗ | for(i = 0; i < sparsePattern->maxColors; i++) | |
| 1666 | { | ||
| 1667 | ✗ | for(ii=0; ii < idaData->N; ii++) | |
| 1668 | { | ||
| 1669 | ✗ | if(sparsePattern->colorCols[ii]-1 == i) | |
| 1670 | { | ||
| 1671 | ✗ | delta_hhh = currentStep * yprime[ii]; | |
| 1672 | ✗ | delta_hh[ii] = numericalJacobianStep(states[ii], delta_hhh, rtol*fabs(states[ii]) + abstol[ii], | |
| 1673 | ✗ | idaData->jacNominalFactor * idaData->nominal[ii]); | |
| 1674 | ✗ | delta_hh[ii] = (delta_hhh >= 0 ? delta_hh[ii] : -delta_hh[ii]); | |
| 1675 | ✗ | delta_hh[ii] = (states[ii] + delta_hh[ii]) - states[ii]; // Due to floating-point arithmetic rounding errors can result in: delta_hh[ii] != (states[ii] + delta_hh[ii]) - states[ii] | |
| 1676 | ✗ | ysave[ii] = states[ii]; | |
| 1677 | ✗ | states[ii] += delta_hh[ii]; | |
| 1678 | |||
| 1679 | ✗ | if (idaData->daeMode){ | |
| 1680 | ✗ | ypsave[ii] = yprime[ii]; | |
| 1681 | ✗ | yprime[ii] += cj * delta_hh[ii]; | |
| 1682 | } | ||
| 1683 | |||
| 1684 | ✗ | delta_hh[ii] = 1. / delta_hh[ii]; | |
| 1685 | } | ||
| 1686 | } | ||
| 1687 | ✗ | idaData->useScaling = FALSE; | |
| 1688 | ✗ | idaData->residualFunction(currentTime, yy, yp, idaData->newdelta, userData); /* Points to residualFunctionIDA */ | |
| 1689 | ✗ | idaData->useScaling = disBackup; | |
| 1690 | |||
| 1691 | ✗ | increaseJacContext(data); | |
| 1692 | |||
| 1693 | ✗ | for(ii = 0; ii < idaData->N; ii++) | |
| 1694 | { | ||
| 1695 | ✗ | if(sparsePattern->colorCols[ii]-1 == i) | |
| 1696 | { | ||
| 1697 | ✗ | nth = sparsePattern->leadindex[ii]; | |
| 1698 | ✗ | while(nth < sparsePattern->leadindex[ii+1]) | |
| 1699 | { | ||
| 1700 | ✗ | j = sparsePattern->index[nth]; | |
| 1701 | /* use row scaling for jacobian elements */ | ||
| 1702 | ✗ | if (!idaData->useScaling || !omc_flag[FLAG_IDA_SCALING]){ | |
| 1703 | ✗ | setJacElementSundialsSparse(j, ii, nth, (newdelta[j] - delta[j]) * delta_hh[ii], Jac, SM_CONTENT_S(Jac)->M); | |
| 1704 | } else { | ||
| 1705 | ✗ | setJacElementSundialsSparse(j, ii, nth, ((newdelta[j] - delta[j]) * delta_hh[ii]) / idaData->resScale[j] * idaData->yScale[ii], Jac, SM_CONTENT_S(Jac)->M); | |
| 1706 | } | ||
| 1707 | ✗ | nth++; | |
| 1708 | }; | ||
| 1709 | ✗ | states[ii] = ysave[ii]; | |
| 1710 | ✗ | if (idaData->daeMode) | |
| 1711 | { | ||
| 1712 | ✗ | yprime[ii] = ypsave[ii]; | |
| 1713 | } | ||
| 1714 | } | ||
| 1715 | } | ||
| 1716 | } | ||
| 1717 | ✗ | setSundialsSparseColPtrs(sparsePattern, Jac); | |
| 1718 | |||
| 1719 | /* scale idaData->y and idaData->yp again */ | ||
| 1720 | ✗ | if ((omc_flag[FLAG_IDA_SCALING] && idaData->useScaling)) | |
| 1721 | { | ||
| 1722 | ✗ | idaScaleVector(rr, idaData->resScale, idaData->N); | |
| 1723 | ✗ | idaScaleData(idaData); | |
| 1724 | } | ||
| 1725 | |||
| 1726 | ✗ | unsetContext(data); | |
| 1727 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1728 | |||
| 1729 | ✗ | return 0; | |
| 1730 | } | ||
| 1731 | |||
| 1732 | /* | ||
| 1733 | * This function calculates the jacobian matrix symbolically while exploiting coloring. | ||
| 1734 | * ToDo: backend: generate seeds for der(x) | ||
| 1735 | here: always set der(x) seeds to cj when setting seed for x | ||
| 1736 | */ | ||
| 1737 | ✗ | int jacColoredSymbolicalSparse(double currentTime, N_Vector yy, N_Vector yp, | |
| 1738 | N_Vector rr, SUNMatrix Jac, double cj, | ||
| 1739 | void *userData) | ||
| 1740 | { | ||
| 1741 | IDA_SOLVER* idaData = (IDA_SOLVER*)userData; | ||
| 1742 | ✗ | DATA* data = (DATA*)(((IDA_USERDATA*)idaData->userData)->data); | |
| 1743 | ✗ | threadData_t* threadData = (threadData_t*)(((IDA_USERDATA*)idaData->userData)->threadData); | |
| 1744 | ✗ | JACOBIAN* jac = getSymbolicOdeJacobian(data); | |
| 1745 | ✗ | jac->dae_cj = cj; | |
| 1746 | |||
| 1747 | /* Reset Jacobian matrix */ | ||
| 1748 | ✗ | SUNMatZero(Jac); | |
| 1749 | |||
| 1750 | ✗ | setContext(data, currentTime, CONTEXT_SYM_JACOBIAN); /* Reuse jacobian matrix in KLU solver */ | |
| 1751 | |||
| 1752 | ✗ | setSundialsSparsePattern(jac, Jac); | |
| 1753 | ✗ | evalJacobian(data, threadData, jac, NULL, SM_DATA_S(Jac), FALSE); | |
| 1754 | |||
| 1755 | ✗ | unsetContext(data); | |
| 1756 | |||
| 1757 | ✗ | return 0; | |
| 1758 | } | ||
| 1759 | |||
| 1760 | /* | ||
| 1761 | * Wrapper function to call numerical or symbolical jacobian matrix | ||
| 1762 | */ | ||
| 1763 | ✗ | static int callSparseJacobian(double currentTime, double cj, | |
| 1764 | N_Vector yy, N_Vector yp, N_Vector rr, | ||
| 1765 | SUNMatrix Jac, void *user_data, | ||
| 1766 | N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) | ||
| 1767 | { | ||
| 1768 | IDA_SOLVER* idaData = (IDA_SOLVER*)user_data; | ||
| 1769 | DATA* data = (DATA*)(((IDA_USERDATA*)idaData->userData)->data); | ||
| 1770 | threadData_t* threadData = (threadData_t*)(((IDA_USERDATA*)((IDA_SOLVER*)user_data)->userData)->threadData); | ||
| 1771 | int i; | ||
| 1772 | SUNErrCode flag; | ||
| 1773 | |||
| 1774 | /* profiling */ | ||
| 1775 | ✗ | if (measure_time_flag) rt_accumulate(SIM_TIMER_SOLVER); | |
| 1776 | ✗ | rt_tick(SIM_TIMER_JACOBIAN); | |
| 1777 | |||
| 1778 | ✗ | if (idaData->jacobianMethod == COLOREDSYMJAC || idaData->jacobianMethod == SYMJAC | |
| 1779 | ✗ | || idaData->jacobianMethod == COLOREDSYMJACADJ || idaData->jacobianMethod == BICOLOREDSYMJAC) | |
| 1780 | { | ||
| 1781 | ✗ | jacColoredSymbolicalSparse(currentTime, yy, yp, rr, Jac, cj, user_data); | |
| 1782 | } | ||
| 1783 | ✗ | else if (idaData->jacobianMethod == COLOREDNUMJAC || idaData->jacobianMethod == NUMJAC) | |
| 1784 | { | ||
| 1785 | ✗ | jacoColoredNumericalSparse(currentTime, yy, yp, rr, Jac, cj, user_data); | |
| 1786 | } | ||
| 1787 | |||
| 1788 | /* debug */ | ||
| 1789 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_JAC)) { | |
| 1790 | ✗ | infoStreamPrint(OMC_LOG_JAC, 0, "##IDA## Sparse Matrix A."); | |
| 1791 | ✗ | SUNSparseMatrix_Print(Jac, stdout); | |
| 1792 | } | ||
| 1793 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_DEBUG)) { | |
| 1794 | ✗ | sundialsPrintSparseMatrix(Jac, "A", OMC_LOG_JAC); | |
| 1795 | } | ||
| 1796 | |||
| 1797 | /* add cj to diagonal elements and store in Jac */ | ||
| 1798 | ✗ | if (!idaData->daeMode) { | |
| 1799 | ✗ | flag = _omc_SUNMatScaleIAdd_Sparse(-cj, Jac); | |
| 1800 | ✗ | checkReturnFlag_SUNDIALS(flag, SUNDIALS_MATRIX_FLAG, "_omc_SUNMatScaleIAdd_Sparse"); | |
| 1801 | } | ||
| 1802 | |||
| 1803 | /* profiling */ | ||
| 1804 | ✗ | rt_accumulate(SIM_TIMER_JACOBIAN); | |
| 1805 | ✗ | if (measure_time_flag) rt_tick(SIM_TIMER_SOLVER); | |
| 1806 | |||
| 1807 | ✗ | return 0; | |
| 1808 | } | ||
| 1809 | |||
| 1810 | |||
| 1811 | /* TODO: Unify with nlsKinsolFScaling from kinsolSolver.c? */ | ||
| 1812 | ✗ | static int getScalingFactors(DATA* data, IDA_SOLVER* idaData, SUNMatrix inScaleMatrix) | |
| 1813 | { | ||
| 1814 | int i; | ||
| 1815 | |||
| 1816 | ✗ | N_Vector tmp1 = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 1817 | ✗ | N_Vector tmp2 = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 1818 | ✗ | N_Vector tmp3 = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 1819 | |||
| 1820 | ✗ | N_Vector rres = N_VNew_Serial(idaData->N, idaData->sunctx); | |
| 1821 | |||
| 1822 | SUNMatrix denseMatrix; | ||
| 1823 | |||
| 1824 | ✗ | if (inScaleMatrix == NULL) | |
| 1825 | { | ||
| 1826 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "##IDA## get new scaling matrix."); | |
| 1827 | |||
| 1828 | /* use y scale to scale jacobian, but y and yp are not scaled */ | ||
| 1829 | ✗ | idaData->useScaling = FALSE; | |
| 1830 | |||
| 1831 | /* eval residual function first */ | ||
| 1832 | ✗ | idaData->residualFunction(data->localData[0]->timeValue, idaData->y, idaData->yp, rres, (void*) idaData); /* Points to residualFunctionIDA */ | |
| 1833 | |||
| 1834 | /* choose the jacobian sparse vs. dense */ | ||
| 1835 | ✗ | if (idaData->linearSolverMethod == IDA_LS_KLU) | |
| 1836 | { | ||
| 1837 | ✗ | if (idaData->NNZ < 0) | |
| 1838 | { | ||
| 1839 | ✗ | throwStreamPrint(NULL, "##IDA## idaData->NNZ not set."); | |
| 1840 | } | ||
| 1841 | ✗ | if (idaData->scaleMatrix == NULL) { | |
| 1842 | ✗ | idaData->scaleMatrix = SUNSparseMatrix(idaData->N, idaData->N, idaData->NNZ + idaData->N, SUN_CSC_MAT, idaData->sunctx); | |
| 1843 | } | ||
| 1844 | ✗ | callSparseJacobian(data->localData[0]->timeValue, 1.0, idaData->y, idaData->yp, rres, | |
| 1845 | idaData->scaleMatrix, idaData, tmp1, tmp2, tmp3); | ||
| 1846 | } | ||
| 1847 | else | ||
| 1848 | { | ||
| 1849 | ✗ | denseMatrix = SUNDenseMatrix(idaData->N, idaData->N, idaData->sunctx); | |
| 1850 | ✗ | callDenseJacobian(data->localData[0]->timeValue, 1.0, idaData->y, | |
| 1851 | idaData->yp, rres, denseMatrix, idaData, tmp1, tmp2, | ||
| 1852 | tmp3); | ||
| 1853 | ✗ | SUNMatDestroy(idaData->scaleMatrix); | |
| 1854 | ✗ | idaData->scaleMatrix = SUNSparseFromDenseMatrix(denseMatrix, DBL_MIN, SUN_CSC_MAT); | |
| 1855 | ✗ | if (idaData->scaleMatrix == NULL) { | |
| 1856 | ✗ | errorStreamPrint( | |
| 1857 | OMC_LOG_STDOUT, 0, | ||
| 1858 | "##IDA## In function SUNSparseFromDenseMatrix: Requirements are " | ||
| 1859 | "violated, or matrix storage request cannot be satisfied."); | ||
| 1860 | } | ||
| 1861 | ✗ | SUNMatDestroy(denseMatrix); | |
| 1862 | } | ||
| 1863 | /* enable scaled jacobian again */ | ||
| 1864 | ✗ | idaData->useScaling = TRUE; | |
| 1865 | } | ||
| 1866 | else | ||
| 1867 | { | ||
| 1868 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "##IDA## use given scaling matrix."); | |
| 1869 | ✗ | idaData->scaleMatrix = inScaleMatrix; | |
| 1870 | } | ||
| 1871 | |||
| 1872 | /* set resScale factors */ | ||
| 1873 | ✗ | _omc_fillVector(_omc_createVector(idaData->N,idaData->resScale), MINIMAL_SCALE_FACTOR); | |
| 1874 | ✗ | for (i=0; i<SM_INDEXPTRS_S(idaData->scaleMatrix)[idaData->N]; ++i) { | |
| 1875 | ✗ | if (idaData->resScale[SM_INDEXVALS_S(idaData->scaleMatrix)[i]] < fabs(SM_DATA_S(idaData->scaleMatrix)[i])) { | |
| 1876 | ✗ | idaData->resScale[SM_INDEXVALS_S(idaData->scaleMatrix)[i]] = fabs(SM_DATA_S(idaData->scaleMatrix)[i]); | |
| 1877 | } | ||
| 1878 | } | ||
| 1879 | |||
| 1880 | ✗ | printVector(OMC_LOG_SOLVER_V, "Prime scale factors", idaData->ypScale, idaData->N, 0.0); | |
| 1881 | ✗ | printVector(OMC_LOG_SOLVER_V, "Residual scale factors", idaData->resScale, idaData->N, 0.0); | |
| 1882 | |||
| 1883 | /* Free memory */ | ||
| 1884 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1885 | ✗ | N_VDestroy_Serial(tmp1); | |
| 1886 | ✗ | N_VDestroy_Serial(tmp2); | |
| 1887 | ✗ | N_VDestroy_Serial(tmp3); | |
| 1888 | ✗ | N_VDestroy_Serial(rres); | |
| 1889 | |||
| 1890 | ✗ | return 0; | |
| 1891 | } | ||
| 1892 | |||
| 1893 | /** | ||
| 1894 | * @brief Scale NVector by factors. | ||
| 1895 | * | ||
| 1896 | * @param vec Vector to scale. | ||
| 1897 | * @param factors Array with scaling factors. | ||
| 1898 | * @param size Length of array factors and vector vec. | ||
| 1899 | */ | ||
| 1900 | ✗ | static void idaScaleVector(N_Vector vec, double* factors, unsigned int size) | |
| 1901 | { | ||
| 1902 | int i; | ||
| 1903 | ✗ | double *data = N_VGetArrayPointer_Serial(vec); | |
| 1904 | ✗ | printVector(OMC_LOG_SOLVER_V, "un-scaled", data, size, 0.0); | |
| 1905 | ✗ | for(i=0; i < size; ++i) | |
| 1906 | { | ||
| 1907 | ✗ | data[i] = data[i] / factors[i]; | |
| 1908 | } | ||
| 1909 | ✗ | printVector(OMC_LOG_SOLVER_V, "scaled", data, size, 0.0); | |
| 1910 | ✗ | } | |
| 1911 | |||
| 1912 | /** | ||
| 1913 | * @brief Scale state and state derivate vector. | ||
| 1914 | * | ||
| 1915 | * @param idaData Containing state vector y(t) and state derivate vector y'(t) | ||
| 1916 | * as well as scaling arrays yScale and ypScale. | ||
| 1917 | */ | ||
| 1918 | ✗ | static void idaScaleData(IDA_SOLVER *idaData) | |
| 1919 | { | ||
| 1920 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "Scale y"); | |
| 1921 | ✗ | idaScaleVector(idaData->y, idaData->yScale, idaData->N); | |
| 1922 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1923 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "Scale yp"); | |
| 1924 | ✗ | idaScaleVector(idaData->yp, idaData->ypScale, idaData->N); | |
| 1925 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1926 | ✗ | } | |
| 1927 | |||
| 1928 | /** | ||
| 1929 | * @brief Rescale NVector by factors. | ||
| 1930 | * | ||
| 1931 | * @param vec Vector to rescale. | ||
| 1932 | * @param factors Array with scaling factors. | ||
| 1933 | * @param size Length of array factors and vector vec. | ||
| 1934 | */ | ||
| 1935 | ✗ | static void idaReScaleVector(N_Vector vec, double* factors, unsigned int size) | |
| 1936 | { | ||
| 1937 | int i; | ||
| 1938 | ✗ | double *data = N_VGetArrayPointer_Serial(vec); | |
| 1939 | |||
| 1940 | ✗ | printVector(OMC_LOG_SOLVER_V, "scaled", data, size, 0.0); | |
| 1941 | ✗ | for(i=0; i < size; ++i) | |
| 1942 | { | ||
| 1943 | ✗ | data[i] = data[i] * factors[i]; | |
| 1944 | } | ||
| 1945 | ✗ | printVector(OMC_LOG_SOLVER_V, "un-scaled", data, size, 0.0); | |
| 1946 | ✗ | } | |
| 1947 | |||
| 1948 | /** | ||
| 1949 | * @brief Rescale state and state derivate vector. | ||
| 1950 | * Undo scaling of function idaScaleData. | ||
| 1951 | * | ||
| 1952 | * @param idaData Containing state vector y(t) and state derivate vector y'(t) | ||
| 1953 | * as well as scaling arrays yScale and ypScale. | ||
| 1954 | */ | ||
| 1955 | ✗ | static void idaReScaleData(IDA_SOLVER *idaData) | |
| 1956 | { | ||
| 1957 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "Re-Scale y"); | |
| 1958 | ✗ | idaReScaleVector(idaData->y, idaData->yScale, idaData->N); | |
| 1959 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1960 | ✗ | infoStreamPrint(OMC_LOG_SOLVER_V, 1, "Re-Scale yp"); | |
| 1961 | ✗ | idaReScaleVector(idaData->yp, idaData->ypScale, idaData->N); | |
| 1962 | ✗ | messageClose(OMC_LOG_SOLVER_V); | |
| 1963 | ✗ | } | |
| 1964 | |||
| 1965 | #endif /* #ifdef WITH_SUNDIALS */ | ||
| 1966 |