OMCompiler/SimulationRuntime/c/simulation/solver/nonlinearValuesList.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 nonlinearValuesList.h | ||
| 29 | * Description: This is a C implementation of a value database | ||
| 30 | * based on a list. It's purpose is to be used by a | ||
| 31 | * a non-linear solver in OpenModelica in order to | ||
| 32 | * guess next value by extrapolation or interpolation. | ||
| 33 | * Assuming time passes forward. | ||
| 34 | * | ||
| 35 | */ | ||
| 36 | |||
| 37 | #include "epsilon.h" | ||
| 38 | #include "nonlinearValuesList.h" | ||
| 39 | |||
| 40 | #include "../../util/list.h" | ||
| 41 | #include "../../util/omc_error.h" | ||
| 42 | |||
| 43 | #include <stdlib.h> | ||
| 44 | #include <string.h> | ||
| 45 | |||
| 46 | #define UNUSED(x) (void)(x) | ||
| 47 | |||
| 48 | /* Forward extrapolate function definition */ | ||
| 49 | double extrapolateValues(const double, const double, const double, const double, const double); | ||
| 50 | void* valueListAlloc(const void* data); | ||
| 51 | void valueListFree(void* data); | ||
| 52 | void valueListCopy(void* dest, const void* src); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * @brief Allocate value lists. | ||
| 56 | * | ||
| 57 | * @param numberOfList Number of lists to allocate. | ||
| 58 | * @param valueSize Length of array double* values | ||
| 59 | * @return VALUES_LIST* Array of value lists. | ||
| 60 | */ | ||
| 61 | ✗ | VALUES_LIST* allocValueList(unsigned int numberOfList, unsigned int valueSize) | |
| 62 | { | ||
| 63 | unsigned int i = 0; | ||
| 64 | ✗ | VALUES_LIST* valueList = (VALUES_LIST*) malloc(numberOfList*sizeof(VALUES_LIST)); | |
| 65 | |||
| 66 | ✗ | for(i=0; i<numberOfList; i++) { | |
| 67 | ✗ | valueList[i].valueList = allocList(valueListAlloc, valueListFree, valueListCopy); | |
| 68 | } | ||
| 69 | |||
| 70 | ✗ | return valueList; | |
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * @brief Free array of value lists. | ||
| 75 | * | ||
| 76 | * @param valueList Array of value lists. | ||
| 77 | * @param numberOfList Length of array valueList. | ||
| 78 | */ | ||
| 79 | ✗ | void freeValueList(VALUES_LIST* valueList, unsigned int numberOfList) | |
| 80 | { | ||
| 81 | unsigned int i = 0; | ||
| 82 | |||
| 83 | ✗ | for(i=0; i<numberOfList; i++) { | |
| 84 | ✗ | freeList(valueList[i].valueList); | |
| 85 | } | ||
| 86 | ✗ | free(valueList); | |
| 87 | ✗ | } | |
| 88 | |||
| 89 | /** | ||
| 90 | * @brief Removes all nodes after startNode from valueList. | ||
| 91 | * If startNode = NULL then clear the whole list. | ||
| 92 | * | ||
| 93 | * @param valueList Pointer to value list | ||
| 94 | * @param startNode Pointer to list node, following nodes will be deleted | ||
| 95 | */ | ||
| 96 | ✗ | void cleanValueList(LIST* valueList, LIST_NODE *startNode) | |
| 97 | { | ||
| 98 | int len; | ||
| 99 | ✗ | if(startNode) | |
| 100 | { | ||
| 101 | ✗ | listClearAfterNode(valueList, startNode); | |
| 102 | } | ||
| 103 | ✗ | else listClear(valueList); | |
| 104 | ✗ | } | |
| 105 | |||
| 106 | /** | ||
| 107 | * @brief Removes all nodes except the one just before or at time. | ||
| 108 | * | ||
| 109 | * @param valueList Pointer to value list | ||
| 110 | * @param time time | ||
| 111 | */ | ||
| 112 | ✗ | void cleanValueListbyTime(LIST *valueList, double time) | |
| 113 | { | ||
| 114 | LIST_NODE *next, *it; | ||
| 115 | VALUE* elem; | ||
| 116 | |||
| 117 | ✗ | printValuesListTimes(valueList); | |
| 118 | // need to get first node at each iteration since head is removed | ||
| 119 | ✗ | for(it = listFirstNode(valueList); it; it = listFirstNode(valueList)) | |
| 120 | { | ||
| 121 | assert(it != NULL); | ||
| 122 | ✗ | elem = (VALUE*)listNodeData(it); | |
| 123 | ✗ | if (elem->time <= time) | |
| 124 | { | ||
| 125 | ✗ | cleanValueList(valueList, it); | |
| 126 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "New list length %d: ", listLen(valueList)); | |
| 127 | ✗ | printValuesListTimes(valueList); | |
| 128 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Done!"); | |
| 129 | ✗ | break; | |
| 130 | } | ||
| 131 | /* debug output */ | ||
| 132 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "cleanValueListbyTime %g check element: ", time); | |
| 133 | ✗ | printValueElement(elem); | |
| 134 | |||
| 135 | ✗ | listRemoveFront(valueList); | |
| 136 | } | ||
| 137 | ✗ | } | |
| 138 | |||
| 139 | /** | ||
| 140 | * @brief Create new value element for value list. | ||
| 141 | * | ||
| 142 | * @param size Length of values array. | ||
| 143 | * @param time Time | ||
| 144 | * @param values Array of values | ||
| 145 | */ | ||
| 146 | ✗ | VALUE* createValueElement(unsigned int size, double time, double* values) | |
| 147 | { | ||
| 148 | ✗ | VALUE* elem = calloc(1, sizeof(VALUE)); | |
| 149 | ✗ | elem->values = calloc(size, sizeof(double)); | |
| 150 | ✗ | elem->time = time; | |
| 151 | ✗ | elem->size = size; | |
| 152 | |||
| 153 | ✗ | memcpy(elem->values, values, size*sizeof(double)); | |
| 154 | |||
| 155 | /* debug output */ | ||
| 156 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Create Element"); | |
| 157 | |||
| 158 | ✗ | return elem; | |
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * @brief Free value element allocated with createValueElement | ||
| 163 | * | ||
| 164 | * @param elem Value element to free. | ||
| 165 | */ | ||
| 166 | ✗ | void freeValue(VALUE* elem) | |
| 167 | { | ||
| 168 | ✗ | free(elem->values); | |
| 169 | ✗ | free(elem); | |
| 170 | ✗ | } | |
| 171 | |||
| 172 | /** | ||
| 173 | * @brief Adds copy of new element to list. | ||
| 174 | * | ||
| 175 | * @param valuesList List | ||
| 176 | * @param newElem New element to add to list. | ||
| 177 | */ | ||
| 178 | ✗ | void addListElement(LIST* valuesList, VALUE* newElem) | |
| 179 | { | ||
| 180 | LIST_NODE *node, *next; | ||
| 181 | VALUE* elem; | ||
| 182 | int replace = 0, i = 0; | ||
| 183 | |||
| 184 | /* debug output */ | ||
| 185 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 1, "Adding element in a list of size %d", listLen(valuesList)); | |
| 186 | ✗ | printValueElement(newElem); | |
| 187 | |||
| 188 | /* if it's empty, just push it in */ | ||
| 189 | ✗ | if (listLen(valuesList) == 0) | |
| 190 | { | ||
| 191 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "List is empty add new element."); | |
| 192 | ✗ | listPushFront(valuesList, (void*) newElem); | |
| 193 | |||
| 194 | ✗ | messageClose(OMC_LOG_NLS_EXTRAPOLATE); | |
| 195 | ✗ | return; | |
| 196 | } | ||
| 197 | |||
| 198 | /* if the element at begin is earlier than current | ||
| 199 | * push the element just in front and if the end element | ||
| 200 | * is later than current push it just back.*/ | ||
| 201 | ✗ | node = listFirstNode(valuesList); | |
| 202 | ✗ | if ( fabs( ((VALUE*)listNodeData(node))->time - newElem->time ) > MINIMAL_STEP_SIZE ) | |
| 203 | { | ||
| 204 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "First Value list element is:"); | |
| 205 | ✗ | printValueElement(((VALUE*)listNodeData(node))); | |
| 206 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "so new element is added before."); | |
| 207 | ✗ | listPushFront(valuesList, (void*) newElem); | |
| 208 | |||
| 209 | ✗ | messageClose(OMC_LOG_NLS_EXTRAPOLATE); | |
| 210 | ✗ | return; | |
| 211 | } | ||
| 212 | |||
| 213 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Search position of new element"); | |
| 214 | /* search correct position */ | ||
| 215 | next = node; | ||
| 216 | do | ||
| 217 | { | ||
| 218 | /* if next node is empty */ | ||
| 219 | ✗ | if (!next) | |
| 220 | { | ||
| 221 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Search finished last element reached"); | |
| 222 | break; | ||
| 223 | } | ||
| 224 | |||
| 225 | ✗ | elem = ((VALUE*)listNodeData(next)); | |
| 226 | |||
| 227 | /* debug output */ | ||
| 228 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Next node of list is element:"); | |
| 229 | ✗ | printValueElement(elem); | |
| 230 | |||
| 231 | |||
| 232 | ✗ | if (fabs(elem->time - newElem->time)<=MINIMAL_STEP_SIZE) | |
| 233 | { | ||
| 234 | replace = 1; | ||
| 235 | break; | ||
| 236 | } | ||
| 237 | ✗ | else if (elem->time < newElem->time) | |
| 238 | { | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | node = next; | ||
| 242 | ✗ | next = listNextNode(node); | |
| 243 | ✗ | i++; /* count insert or replace place */ | |
| 244 | }while(1); | ||
| 245 | |||
| 246 | /* add element before currect node */ | ||
| 247 | ✗ | if (!replace){ | |
| 248 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Insert element before last output element."); | |
| 249 | ✗ | listInsert(valuesList, node, (void*) newElem); | |
| 250 | } | ||
| 251 | else | ||
| 252 | { | ||
| 253 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "replace element."); | |
| 254 | ✗ | updateNodeData(valuesList, next, (void*) newElem); | |
| 255 | } | ||
| 256 | /* clean list if too full */ | ||
| 257 | ✗ | if (i < 3 && listLen(valuesList)>10) | |
| 258 | { | ||
| 259 | ✗ | while(i < 4) | |
| 260 | { | ||
| 261 | ✗ | next = listNextNode(next); | |
| 262 | ✗ | i++; | |
| 263 | } | ||
| 264 | ✗ | cleanValueList(valuesList, next); | |
| 265 | } | ||
| 266 | |||
| 267 | ✗ | messageClose(OMC_LOG_NLS_EXTRAPOLATE); | |
| 268 | ✗ | return; | |
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * @brief Gets extrapolated values for time from value list. | ||
| 273 | * | ||
| 274 | * @param valuesList Pointer to value list | ||
| 275 | * @param time time | ||
| 276 | * @param extrapolatedValues values extrapolated (overwritten) | ||
| 277 | * @param oldOutput old values just before time | ||
| 278 | */ | ||
| 279 | ✗ | void getValues(LIST* valuesList, double time, double* extrapolatedValues, double* oldOutput) | |
| 280 | { | ||
| 281 | LIST_NODE *it; | ||
| 282 | LIST_NODE *old = NULL; | ||
| 283 | LIST_NODE *old2 = NULL; | ||
| 284 | VALUE *oldValues, *old2Values, *elem; | ||
| 285 | |||
| 286 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 1, "Get values for time %g in a list of size %d", time, listLen(valuesList)); | |
| 287 | |||
| 288 | /* find corresponding values */ | ||
| 289 | ✗ | for(it = listFirstNode(valuesList); it; it = listNextNode(it)) | |
| 290 | { | ||
| 291 | ✗ | elem = (VALUE*)listNodeData(it); | |
| 292 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Searching current element:"); | |
| 293 | ✗ | printValueElement(elem); | |
| 294 | |||
| 295 | old = it; | ||
| 296 | ✗ | if(fabs(elem->time - time) <= MINIMAL_STEP_SIZE) | |
| 297 | { | ||
| 298 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "take element with the same time."); | |
| 299 | ✗ | break; | |
| 300 | } | ||
| 301 | ✗ | else if(elem->time < time) | |
| 302 | { | ||
| 303 | ✗ | old2 = listNextNode(old); | |
| 304 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "found element to use for extrapolation."); | |
| 305 | ✗ | break; | |
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | /* if the list is empty old never gets set */ | ||
| 310 | ✗ | assertStreamPrint(NULL, NULL != old, "getValues failed, no elements!"); | |
| 311 | |||
| 312 | ✗ | if(it == NULL) | |
| 313 | { | ||
| 314 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "reached end of list."); | |
| 315 | } | ||
| 316 | |||
| 317 | /* get next values */ | ||
| 318 | ✗ | if (old2 == NULL) | |
| 319 | { | ||
| 320 | ✗ | oldValues = (VALUE*) listNodeData(old); | |
| 321 | ✗ | memcpy(extrapolatedValues, oldValues->values, oldValues->size*sizeof(double)); | |
| 322 | ✗ | memcpy(oldOutput, oldValues->values, oldValues->size*sizeof(double)); | |
| 323 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "take just old values."); | |
| 324 | } | ||
| 325 | else | ||
| 326 | { | ||
| 327 | int i; | ||
| 328 | ✗ | oldValues = (VALUE*) listNodeData(old); | |
| 329 | ✗ | old2Values = (VALUE*) listNodeData(old2); | |
| 330 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, "Use following elements for calculation:"); | |
| 331 | ✗ | printValueElement(oldValues); | |
| 332 | ✗ | printValueElement(old2Values); | |
| 333 | ✗ | for(i = 0; i < oldValues->size; ++i) | |
| 334 | { | ||
| 335 | ✗ | extrapolatedValues[i] = extrapolateValues(time, oldValues->values[i], oldValues->time, old2Values->values[i], old2Values->time); | |
| 336 | } | ||
| 337 | ✗ | memcpy(oldOutput, oldValues->values, oldValues->size*sizeof(double)); | |
| 338 | } | ||
| 339 | ✗ | messageClose(OMC_LOG_NLS_EXTRAPOLATE); | |
| 340 | ✗ | return; | |
| 341 | } | ||
| 342 | |||
| 343 | ✗ | void printValueElement(VALUE* elem) | |
| 344 | { | ||
| 345 | /* debug output */ | ||
| 346 | ✗ | if(OMC_ACTIVE_STREAM(OMC_LOG_NLS_EXTRAPOLATE)) | |
| 347 | { | ||
| 348 | int i; | ||
| 349 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 1, "Element(size %d) at time %g ", elem->size, elem->time); | |
| 350 | ✗ | for(i = 0; i < elem->size; i++) { | |
| 351 | ✗ | infoStreamPrint(OMC_LOG_NLS_EXTRAPOLATE, 0, " oldValues[%d] = %g",i, elem->values[i]); | |
| 352 | } | ||
| 353 | ✗ | messageClose(OMC_LOG_NLS_EXTRAPOLATE); | |
| 354 | } | ||
| 355 | ✗ | } | |
| 356 | |||
| 357 | /** | ||
| 358 | * @brief Print function for printValuesListTimes | ||
| 359 | * | ||
| 360 | * @param data Data of node of type VALUE* | ||
| 361 | * @param stream Stream to output to. | ||
| 362 | * @param unused Unused | ||
| 363 | */ | ||
| 364 | ✗ | static void printElemTimes(void* data, int stream, void* unused) { | |
| 365 | UNUSED(unused); | ||
| 366 | VALUE* elem = (VALUE*) data; | ||
| 367 | |||
| 368 | unsigned int i; | ||
| 369 | ✗ | for(i=0; i<elem->size; i++) { | |
| 370 | ✗ | infoStreamPrint(stream, 0, "Element %d at time %g", i, elem->time); | |
| 371 | } | ||
| 372 | ✗ | } | |
| 373 | |||
| 374 | /** | ||
| 375 | * @brief Print value times of value list. | ||
| 376 | * | ||
| 377 | * @param list Value list. | ||
| 378 | */ | ||
| 379 | ✗ | void printValuesListTimes(LIST* list) { | |
| 380 | ✗ | printList(list, OMC_LOG_NLS_EXTRAPOLATE, printElemTimes); | |
| 381 | ✗ | } | |
| 382 | |||
| 383 | /*! \fn extraPolateValues | ||
| 384 | * This function extrapolates linear values based on old values. | ||
| 385 | * | ||
| 386 | * \param [in] [time] desired time for extrapolation | ||
| 387 | * \param [in] [old1] old value at time1 | ||
| 388 | * \param [in] [time1] time for the first value | ||
| 389 | * \param [in] [old2] old value at time2 | ||
| 390 | * \param [in] [time2] time for the second value | ||
| 391 | */ | ||
| 392 | ✗ | double extrapolateValues(const double time, const double old1, const double time1, const double old2, const double time2) | |
| 393 | { | ||
| 394 | double retValue; | ||
| 395 | |||
| 396 | ✗ | if (time1 == time2 || old1 == old2) | |
| 397 | { | ||
| 398 | retValue = old1; | ||
| 399 | } | ||
| 400 | else | ||
| 401 | { | ||
| 402 | ✗ | retValue = old2 + ((time - time2)/(time1 - time2)) * (old1-old2); | |
| 403 | } | ||
| 404 | |||
| 405 | ✗ | return retValue; | |
| 406 | } | ||
| 407 | |||
| 408 | /** | ||
| 409 | * @brief Allocate memory for valueList elements. | ||
| 410 | * | ||
| 411 | * @param data value list element, containing size of array values | ||
| 412 | * Has to be of type VALUE*; | ||
| 413 | * @return void* Allocated memory for LIST_NODE data. | ||
| 414 | */ | ||
| 415 | ✗ | void* valueListAlloc(const void* data) { | |
| 416 | const VALUE* valueElem = (VALUE*) data; | ||
| 417 | ✗ | VALUE* newElem = malloc(sizeof(VALUE)); | |
| 418 | ✗ | assertStreamPrint(NULL, newElem != NULL, "valueListAlloc: Out of memory"); | |
| 419 | ✗ | newElem->values = malloc(valueElem->size*sizeof(double)); | |
| 420 | ✗ | assertStreamPrint(NULL, newElem->values != NULL, "valueListAlloc: Out of memory"); | |
| 421 | ✗ | return (void*) newElem; | |
| 422 | } | ||
| 423 | |||
| 424 | /** | ||
| 425 | * @brief Free memory allocated with valueListAlloc. | ||
| 426 | * | ||
| 427 | * @param data Void pointer, representing index for new list element. | ||
| 428 | */ | ||
| 429 | ✗ | void valueListFree(void* data) { | |
| 430 | VALUE* valueElem = (VALUE*) data; | ||
| 431 | ✗ | free(valueElem->values); | |
| 432 | ✗ | free(valueElem); | |
| 433 | ✗ | } | |
| 434 | |||
| 435 | /** | ||
| 436 | * @brief Copy data of valueList elements. | ||
| 437 | * | ||
| 438 | * @param dest Void pointer of destination data, representing VALUE. | ||
| 439 | * @param src Void pointer of source data, representing VALUE. | ||
| 440 | */ | ||
| 441 | ✗ | void valueListCopy(void* dest, const void* src) { | |
| 442 | VALUE* destValue = (VALUE*) dest; | ||
| 443 | VALUE* srcValue = (VALUE*) src; | ||
| 444 | ✗ | destValue->size = srcValue->size; | |
| 445 | ✗ | destValue->time = srcValue->time; | |
| 446 | ✗ | memcpy(destValue->values, srcValue->values, srcValue->size*sizeof(double)); | |
| 447 | ✗ | } | |
| 448 |