OMCompiler/SimulationRuntime/c/simulation/solver/newtonIteration.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 newtonIteration.c | ||
| 29 | */ | ||
| 30 | |||
| 31 | #ifdef __cplusplus | ||
| 32 | extern "C" { | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #include <math.h> | ||
| 36 | #include <stdlib.h> | ||
| 37 | #include <string.h> /* memcpy */ | ||
| 38 | |||
| 39 | #include "simulation/simulation_info_json.h" | ||
| 40 | #include "model_help.h" | ||
| 41 | #include "omc_math.h" | ||
| 42 | #include "util/omc_error.h" | ||
| 43 | #include "util/varinfo.h" | ||
| 44 | |||
| 45 | #include "nonlinearSystem.h" | ||
| 46 | #include "newtonIteration.h" | ||
| 47 | |||
| 48 | #include "external_input.h" | ||
| 49 | |||
| 50 | /* Private function prototypes */ | ||
| 51 | |||
| 52 | int solveLinearSystem(int n, int* iwork, double* fvec, double *fjac, DATA_NEWTON* solverData); | ||
| 53 | void calculatingErrors(DATA_NEWTON* solverData, double* delta_x, double* delta_x_scaled, double* delta_f, double* error_f, | ||
| 54 | double* scaledError_f, int n, double* x, double* fvec); | ||
| 55 | void scaling_residual_vector(DATA_NEWTON* solverData); | ||
| 56 | void damping_heuristic(double* x, genericResidualFunc f, | ||
| 57 | double current_fvec_enorm, int n, double* fvec, double* lambda, int* k, | ||
| 58 | DATA_NEWTON* solverData, NLS_USERDATA* userData); | ||
| 59 | void damping_heuristic2(double damping_parameter, double* x, genericResidualFunc f, | ||
| 60 | double current_fvec_enorm, int n, double* fvec, int* k, | ||
| 61 | DATA_NEWTON* solverData, NLS_USERDATA* userdata); | ||
| 62 | void LineSearch(double* x, genericResidualFunc f, | ||
| 63 | double current_fvec_enorm, int n, double* fvec, int* k, | ||
| 64 | DATA_NEWTON* solverData, NLS_USERDATA* userdata); | ||
| 65 | void Backtracking(double* x, genericResidualFunc f, double current_fvec_enorm, | ||
| 66 | int n, double* fvec, DATA_NEWTON* solverData, | ||
| 67 | NLS_USERDATA* userdata); | ||
| 68 | void printErrors(double delta_x, double delta_x_scaled, double delta_f, double error_f, double scaledError_f, double* eps); | ||
| 69 | |||
| 70 | /* Extern function prototypes */ | ||
| 71 | |||
| 72 | extern double enorm_(int *n, double *x); | ||
| 73 | extern int dgesv_(int *n, int *nrhs, doublereal *a, int *lda, int *ipiv, doublereal *b, int *ldb, int *info); | ||
| 74 | extern void dgetrf_(int *m, int *n, doublereal *fjac, int *lda, int* iwork, int *info); | ||
| 75 | extern void dgetrs_(char *trans, int *n, int *nrhs, doublereal *a, int *lda, int *ipiv, doublereal *b, int *ldb, int *info); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * @brief Allocate NLS Newton data. | ||
| 79 | * | ||
| 80 | * @param size Size of non-linear system. | ||
| 81 | * @param userData Pointer to set NLS user data. | ||
| 82 | * @return DATA_NEWTON* Allocated memory. | ||
| 83 | */ | ||
| 84 | ✗ | DATA_NEWTON* allocateNewtonData(int size, NLS_USERDATA* userData) | |
| 85 | { | ||
| 86 | ✗ | DATA_NEWTON* newtonData = (DATA_NEWTON*) malloc(sizeof(DATA_NEWTON)); | |
| 87 | ✗ | assertStreamPrint(NULL, NULL != newtonData, "allocationNewtonData() failed. Out of memory."); | |
| 88 | |||
| 89 | ✗ | newtonData->resScaling = (double*) malloc(size*sizeof(double)); | |
| 90 | ✗ | newtonData->fvecScaled = (double*) malloc(size*sizeof(double)); | |
| 91 | |||
| 92 | ✗ | newtonData->n = size; | |
| 93 | ✗ | newtonData->x = (double*) malloc((size+1)*sizeof(double)); | |
| 94 | ✗ | newtonData->fvec = (double*) calloc(size,sizeof(double)); | |
| 95 | ✗ | newtonData->xtol = 1e-6; | |
| 96 | ✗ | newtonData->ftol = 1e-6; | |
| 97 | ✗ | newtonData->maxfev = size*100; | |
| 98 | ✗ | newtonData->epsfcn = DBL_EPSILON; | |
| 99 | ✗ | newtonData->fjac = (double*) malloc((size*(size+1))*sizeof(double)); | |
| 100 | |||
| 101 | ✗ | newtonData->rwork = (double*) malloc((size)*sizeof(double)); | |
| 102 | ✗ | newtonData->iwork = (int*) malloc(size*sizeof(int)); | |
| 103 | |||
| 104 | /* damped newton */ | ||
| 105 | ✗ | newtonData->x_new = (double*) malloc((size+1)*sizeof(double)); | |
| 106 | ✗ | newtonData->x_increment = (double*) malloc(size*sizeof(double)); | |
| 107 | ✗ | newtonData->f_old = (double*) calloc(size,sizeof(double)); | |
| 108 | ✗ | newtonData->fvec_minimum = (double*) calloc(size,sizeof(double)); | |
| 109 | ✗ | newtonData->delta_f = (double*) calloc(size,sizeof(double)); | |
| 110 | ✗ | newtonData->delta_x_vec = (double*) calloc(size,sizeof(double)); | |
| 111 | |||
| 112 | ✗ | newtonData->factorization = 0; | |
| 113 | ✗ | newtonData->calculate_jacobian = 1; | |
| 114 | ✗ | newtonData->numberOfIterations = 0; | |
| 115 | ✗ | newtonData->numberOfFunctionEvaluations = 0; | |
| 116 | |||
| 117 | ✗ | newtonData->userData = userData; | |
| 118 | |||
| 119 | ✗ | return newtonData; | |
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * @brief Free NLS Newton data. | ||
| 124 | * | ||
| 125 | * @param newtonData Pointer to Newton data. | ||
| 126 | */ | ||
| 127 | ✗ | void freeNewtonData(DATA_NEWTON* newtonData) | |
| 128 | { | ||
| 129 | ✗ | free(newtonData->resScaling); | |
| 130 | ✗ | free(newtonData->fvecScaled); | |
| 131 | ✗ | free(newtonData->x); | |
| 132 | ✗ | free(newtonData->fvec); | |
| 133 | ✗ | free(newtonData->fjac); | |
| 134 | ✗ | free(newtonData->rwork); | |
| 135 | ✗ | free(newtonData->iwork); | |
| 136 | |||
| 137 | /* damped newton */ | ||
| 138 | ✗ | free(newtonData->x_new); | |
| 139 | ✗ | free(newtonData->x_increment); | |
| 140 | ✗ | free(newtonData->f_old); | |
| 141 | ✗ | free(newtonData->fvec_minimum); | |
| 142 | ✗ | free(newtonData->delta_f); | |
| 143 | ✗ | free(newtonData->delta_x_vec); | |
| 144 | |||
| 145 | ✗ | freeNlsUserData(newtonData->userData); | |
| 146 | ✗ | free(newtonData); | |
| 147 | ✗ | } | |
| 148 | |||
| 149 | /** | ||
| 150 | * @brief Solve system with Newton-Raphson. | ||
| 151 | * | ||
| 152 | * @param f Residual function. | ||
| 153 | * @param solverData Solver data for containing information for Newton solver. | ||
| 154 | * @param userData Void pointer containing user data for supplied function f and damping heuristics. | ||
| 155 | * @return int Returns 0. | ||
| 156 | */ | ||
| 157 | |||
| 158 | ✗ | int _omc_newton(genericResidualFunc f, DATA_NEWTON* solverData, void* userData) | |
| 159 | { | ||
| 160 | ✗ | int i, j, k = 0, l = 0, nrsh = 1; | |
| 161 | ✗ | int n = solverData->n; /* size of equation */ | |
| 162 | ✗ | double *x = solverData->x; | |
| 163 | ✗ | double *fvec = solverData->fvec; | |
| 164 | ✗ | double *eps = &(solverData->ftol); /* tolerance for x */ | |
| 165 | double *fdeps = &(solverData->epsfcn); | ||
| 166 | int * maxfev = &(solverData->maxfev); | ||
| 167 | ✗ | double *fjac = solverData->fjac; | |
| 168 | double *work = solverData->rwork; | ||
| 169 | ✗ | int *iwork = solverData->iwork; | |
| 170 | int *info = &(solverData->info); | ||
| 171 | int calc_jac = 1; | ||
| 172 | |||
| 173 | ✗ | double error_f = 1.0 + *eps, scaledError_f = 1.0 + *eps, delta_x = 1.0 + *eps, delta_f = 1.0 + *eps, delta_x_scaled = 1.0 + *eps, lambda = 1.0; | |
| 174 | double current_fvec_enorm, enorm_new; | ||
| 175 | |||
| 176 | ✗ | if(OMC_ACTIVE_STREAM(OMC_LOG_NLS_V)) | |
| 177 | { | ||
| 178 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "######### Start Newton maxfev: %d #########", (int)*maxfev); | |
| 179 | |||
| 180 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "x vector"); | |
| 181 | ✗ | for(i=0; i<n; i++) | |
| 182 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "x[%d]: %e ", i, x[i]); | |
| 183 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 184 | |||
| 185 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 186 | } | ||
| 187 | |||
| 188 | ✗ | *info = 1; | |
| 189 | |||
| 190 | /* calculate the function values */ | ||
| 191 | ✗ | (*f)(n, x, fvec, userData, 1); | |
| 192 | |||
| 193 | ✗ | solverData->nfev++; | |
| 194 | |||
| 195 | /* save current fvec in f_old*/ | ||
| 196 | ✗ | memcpy(solverData->f_old, fvec, n*sizeof(double)); | |
| 197 | |||
| 198 | ✗ | error_f = current_fvec_enorm = enorm_(&n, fvec); | |
| 199 | |||
| 200 | ✗ | memcpy(solverData->fvecScaled, solverData->fvec, n*sizeof(double)); | |
| 201 | |||
| 202 | ✗ | while(error_f > *eps && scaledError_f > *eps && delta_x > *eps && delta_f > *eps && delta_x_scaled > *eps) | |
| 203 | { | ||
| 204 | ✗ | if(OMC_ACTIVE_STREAM(OMC_LOG_NLS_V)) | |
| 205 | { | ||
| 206 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "\n**** start Iteration: %d *****", (int) l); | |
| 207 | |||
| 208 | /* Debug output */ | ||
| 209 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "function values"); | |
| 210 | ✗ | for(i=0; i<n; i++) | |
| 211 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "fvec[%d]: %e ", i, fvec[i]); | |
| 212 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 213 | } | ||
| 214 | |||
| 215 | /* calculate jacobian if no matrix is given */ | ||
| 216 | ✗ | if (calc_jac == 1 && solverData->calculate_jacobian >= 0) | |
| 217 | { | ||
| 218 | ✗ | (*f)(n, x, fvec, userData, 0); | |
| 219 | ✗ | solverData->factorization = 0; | |
| 220 | ✗ | calc_jac = solverData->calculate_jacobian; | |
| 221 | } | ||
| 222 | else | ||
| 223 | { | ||
| 224 | ✗ | solverData->factorization = 1; | |
| 225 | ✗ | calc_jac--; | |
| 226 | } | ||
| 227 | |||
| 228 | |||
| 229 | /* debug output */ | ||
| 230 | ✗ | if(OMC_ACTIVE_STREAM(OMC_LOG_NLS_JAC)) | |
| 231 | { | ||
| 232 | ✗ | char *buffer = (char*)malloc(sizeof(char)*solverData->n*15); | |
| 233 | |||
| 234 | ✗ | infoStreamPrint(OMC_LOG_NLS_JAC, 1, "jacobian matrix [%dx%d]", n, n); | |
| 235 | ✗ | for(i=0; i<solverData->n;i++) | |
| 236 | { | ||
| 237 | char *p = buffer; | ||
| 238 | ✗ | for(j=0; j<solverData->n; j++) | |
| 239 | ✗ | p += sprintf(p, "%10g ", fjac[i*n+j]); | |
| 240 | ✗ | infoStreamPrint(OMC_LOG_NLS_JAC, 0, "%s", buffer); | |
| 241 | } | ||
| 242 | ✗ | messageClose(OMC_LOG_NLS_JAC); | |
| 243 | ✗ | free(buffer); | |
| 244 | } | ||
| 245 | |||
| 246 | ✗ | if (solveLinearSystem(n, iwork, fvec, fjac, solverData) != 0) | |
| 247 | { | ||
| 248 | ✗ | *info=-1; | |
| 249 | ✗ | break; | |
| 250 | } | ||
| 251 | else | ||
| 252 | { | ||
| 253 | ✗ | for (i = 0; i < n; i++) | |
| 254 | ✗ | solverData->x_new[i] = x[i]-solverData->x_increment[i]; | |
| 255 | |||
| 256 | ✗ | if(OMC_ACTIVE_STREAM(OMC_LOG_NLS_V)) { | |
| 257 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "x_increment"); | |
| 258 | ✗ | for(i = 0; i < n; i++) { | |
| 259 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "x_increment[%d] = %e ", i, solverData->x_increment[i]); | |
| 260 | } | ||
| 261 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 262 | } | ||
| 263 | |||
| 264 | ✗ | if (solverData->newtonStrategy == NEWTON_DAMPED) | |
| 265 | { | ||
| 266 | ✗ | damping_heuristic(x, f, current_fvec_enorm, n, fvec, &lambda, &k, solverData, userData); | |
| 267 | } | ||
| 268 | ✗ | else if (solverData->newtonStrategy == NEWTON_DAMPED2) | |
| 269 | { | ||
| 270 | ✗ | damping_heuristic2(0.75, x, f, current_fvec_enorm, n, fvec, &k, solverData, userData); | |
| 271 | } | ||
| 272 | ✗ | else if (solverData->newtonStrategy == NEWTON_DAMPED_LS) | |
| 273 | { | ||
| 274 | ✗ | LineSearch(x, f, current_fvec_enorm, n, fvec, &k, solverData, userData); | |
| 275 | } | ||
| 276 | ✗ | else if (solverData->newtonStrategy == NEWTON_DAMPED_BT) | |
| 277 | { | ||
| 278 | ✗ | Backtracking(x, f, current_fvec_enorm, n, fvec, solverData, userData); | |
| 279 | } | ||
| 280 | else | ||
| 281 | { | ||
| 282 | /* calculate the function values */ | ||
| 283 | ✗ | (*f)(n, solverData->x_new, fvec, userData, 1); | |
| 284 | ✗ | solverData->nfev++; | |
| 285 | } | ||
| 286 | |||
| 287 | ✗ | calculatingErrors(solverData, &delta_x, &delta_x_scaled, &delta_f, &error_f, &scaledError_f, n, x, fvec); | |
| 288 | |||
| 289 | /* updating x */ | ||
| 290 | ✗ | memcpy(x, solverData->x_new, n*sizeof(double)); | |
| 291 | |||
| 292 | /* updating f_old */ | ||
| 293 | ✗ | memcpy(solverData->f_old, fvec, n*sizeof(double)); | |
| 294 | |||
| 295 | ✗ | current_fvec_enorm = error_f; | |
| 296 | |||
| 297 | /* check if maximum iteration is reached */ | ||
| 298 | ✗ | if (++l > *maxfev) | |
| 299 | { | ||
| 300 | ✗ | *info = -1; | |
| 301 | ✗ | if (solverData->initial) { | |
| 302 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Newton iteration: Maximal number of iteration reached at initialization, but no root found."); | |
| 303 | } else { | ||
| 304 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Newton iteration: Maximal number of iteration reached at time %f, but no root found.", solverData->time); | |
| 305 | } | ||
| 306 | break; | ||
| 307 | } | ||
| 308 | /* check if maximum iteration is reached */ | ||
| 309 | ✗ | if (k > 5) | |
| 310 | { | ||
| 311 | ✗ | *info = -1; | |
| 312 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Newton iteration: Maximal number of iterations reached."); | |
| 313 | ✗ | break; | |
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | ✗ | if(OMC_ACTIVE_STREAM(OMC_LOG_NLS_V)) | |
| 318 | { | ||
| 319 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "x vector"); | |
| 320 | ✗ | for(i = 0; i < n; i++) | |
| 321 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "x[%d] = %e ", i, x[i]); | |
| 322 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 323 | ✗ | printErrors(delta_x, delta_x_scaled, delta_f, error_f, scaledError_f, eps); | |
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | ✗ | solverData->numberOfIterations += l; | |
| 328 | ✗ | solverData->numberOfFunctionEvaluations += solverData->nfev; | |
| 329 | |||
| 330 | ✗ | return 0; | |
| 331 | } | ||
| 332 | |||
| 333 | /** | ||
| 334 | * @brief Print errors. | ||
| 335 | * | ||
| 336 | * Print if tolerance is reached. | ||
| 337 | * Errors computed by calculatingErrors. | ||
| 338 | * | ||
| 339 | * @param delta_x delta_x := ||x_new - x_old|| | ||
| 340 | * @param delta_x_scaled delta_x_scaled := delta_x / scaling_factor | ||
| 341 | * @param delta_f delta_f := || f_old - f_new || | ||
| 342 | * @param error_f enorm_(n,fvec) | ||
| 343 | * @param scaledError_f | ||
| 344 | * @param eps | ||
| 345 | */ | ||
| 346 | ✗ | void printErrors(double delta_x, double delta_x_scaled, double delta_f, double error_f, double scaledError_f, double* eps) | |
| 347 | { | ||
| 348 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "errors "); | |
| 349 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "delta_x = %e \ndelta_x_scaled = %e \ndelta_f = %e \nerror_f = %e \nscaledError_f = %e", delta_x, delta_x_scaled, delta_f, error_f, scaledError_f); | |
| 350 | |||
| 351 | ✗ | if (delta_x < *eps) | |
| 352 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "delta_x reached eps"); | |
| 353 | ✗ | if (delta_x_scaled < *eps) | |
| 354 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "delta_x_scaled reached eps"); | |
| 355 | ✗ | if (delta_f < *eps) | |
| 356 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "delta_f reached eps"); | |
| 357 | ✗ | if (error_f < *eps) | |
| 358 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "error_f reached eps"); | |
| 359 | ✗ | if (scaledError_f < *eps) | |
| 360 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "scaledError_f reached eps"); | |
| 361 | |||
| 362 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 363 | ✗ | } | |
| 364 | |||
| 365 | /*! \fn solveLinearSystem | ||
| 366 | * | ||
| 367 | * function solves linear system J*(x_{n+1} - x_n) = f using lapack | ||
| 368 | */ | ||
| 369 | ✗ | int solveLinearSystem(int n, int* iwork, double* fvec, double *fjac, DATA_NEWTON* solverData) | |
| 370 | { | ||
| 371 | ✗ | int i, nrsh=1, lapackinfo; | |
| 372 | ✗ | char trans = 'N'; | |
| 373 | |||
| 374 | /* if no factorization is given, calculate it */ | ||
| 375 | ✗ | if (solverData->factorization == 0) | |
| 376 | { | ||
| 377 | /* solve J*(x_{n+1} - x_n)=f */ | ||
| 378 | ✗ | dgetrf_(&n, &n, fjac, &n, iwork, &lapackinfo); | |
| 379 | ✗ | solverData->factorization = 1; | |
| 380 | ✗ | dgetrs_(&trans, &n, &nrsh, fjac, &n, iwork, fvec, &n, &lapackinfo); | |
| 381 | } | ||
| 382 | else | ||
| 383 | { | ||
| 384 | ✗ | dgetrs_(&trans, &n, &nrsh, fjac, &n, iwork, fvec, &n, &lapackinfo); | |
| 385 | } | ||
| 386 | |||
| 387 | ✗ | if(lapackinfo > 0) | |
| 388 | { | ||
| 389 | ✗ | warningStreamPrint(OMC_LOG_NLS, 0, "Newton iteration linear solver: Jacobian matrix singular."); | |
| 390 | ✗ | return -1; | |
| 391 | } | ||
| 392 | ✗ | else if(lapackinfo < 0) | |
| 393 | { | ||
| 394 | ✗ | warningStreamPrint(OMC_LOG_NLS, 0, "illegal input in argument %d", (int)lapackinfo); | |
| 395 | ✗ | return -1; | |
| 396 | } | ||
| 397 | else | ||
| 398 | { | ||
| 399 | /* save solution of J*(x_{n+1} - x_n)=f */ | ||
| 400 | ✗ | memcpy(solverData->x_increment, fvec, n*sizeof(double)); | |
| 401 | } | ||
| 402 | |||
| 403 | ✗ | return 0; | |
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * @brief Calculate delta and error. | ||
| 408 | * | ||
| 409 | * Current value of x from input `x`, old value from `solverData->x_new`. | ||
| 410 | * Current value of f(x) from input `fvec`, old value from `solverData->fvecScaled` | ||
| 411 | * | ||
| 412 | * @param solverData Newton solver data. | ||
| 413 | * @param delta_x delta_x := ||x_new - x_old|| | ||
| 414 | * @param delta_x_scaled delta_x_scaled := delta_x / scaling_factor, where | ||
| 415 | * scaling_factor := ||x|| | ||
| 416 | * @param delta_f delta_f := ||f_old - f_new|| | ||
| 417 | * @param error_f error_f := ||fvec|| | ||
| 418 | * @param scaledError_f scaledError_f := || fvec ./ resScaling||, where | ||
| 419 | * resScaling is from solverData. | ||
| 420 | * @param n Length of arrays x and fvec. | ||
| 421 | * @param x New vector x. | ||
| 422 | * @param fvec New vector f(x). | ||
| 423 | */ | ||
| 424 | ✗ | void calculatingErrors(DATA_NEWTON* solverData, double* delta_x, double* delta_x_scaled, double* delta_f, double* error_f, | |
| 425 | double* scaledError_f, int n, double* x, double* fvec) | ||
| 426 | { | ||
| 427 | int i=0; | ||
| 428 | double scaling_factor; | ||
| 429 | |||
| 430 | /* delta_x = || x_new-x_old || */ | ||
| 431 | ✗ | for (i=0; i<n; i++) | |
| 432 | ✗ | solverData->delta_x_vec[i] = x[i]-solverData->x_new[i]; | |
| 433 | |||
| 434 | ✗ | *delta_x = enorm_(&n,solverData->delta_x_vec); | |
| 435 | |||
| 436 | ✗ | scaling_factor = enorm_(&n,x); | |
| 437 | ✗ | if (scaling_factor > 1) { | |
| 438 | ✗ | *delta_x_scaled = *delta_x * 1./ scaling_factor; | |
| 439 | } else { | ||
| 440 | ✗ | *delta_x_scaled = *delta_x; | |
| 441 | } | ||
| 442 | |||
| 443 | /* delta_f = || f_old - f_new || */ | ||
| 444 | ✗ | for (i=0; i<n; i++) | |
| 445 | ✗ | solverData->delta_f[i] = solverData->f_old[i]-fvec[i]; | |
| 446 | |||
| 447 | ✗ | *delta_f=enorm_(&n, solverData->delta_f); | |
| 448 | |||
| 449 | ✗ | *error_f = enorm_(&n,fvec); | |
| 450 | |||
| 451 | /* scaling residual vector */ | ||
| 452 | ✗ | scaling_residual_vector(solverData); | |
| 453 | |||
| 454 | ✗ | for (i=0; i<n; i++) { | |
| 455 | ✗ | solverData->fvecScaled[i]=fvec[i]/solverData->resScaling[i]; | |
| 456 | } | ||
| 457 | ✗ | *scaledError_f = enorm_(&n,solverData->fvecScaled); | |
| 458 | ✗ | } | |
| 459 | |||
| 460 | /** | ||
| 461 | * @brief Compute residual scaling vector. | ||
| 462 | * | ||
| 463 | * scalingVector[i] = 1 / ||Jac(i,:)|| | ||
| 464 | * Warn if Jacobian row is all zeros i.e. the Jacobian is singular. | ||
| 465 | * | ||
| 466 | * @param solverData Newton solver data. | ||
| 467 | * @param scalingVector Residual scaling vector. | ||
| 468 | */ | ||
| 469 | ✗ | void compute_scaling_vector(DATA_NEWTON* solverData, double* scalingVector) { | |
| 470 | int i; | ||
| 471 | int jac_row_start; | ||
| 472 | |||
| 473 | ✗ | for(i=0; i<solverData->n; i++) | |
| 474 | { | ||
| 475 | ✗ | jac_row_start = i*solverData->n; | |
| 476 | ✗ | scalingVector[i] = _omc_gen_maximumVectorNorm(&(solverData->fjac[jac_row_start]), solverData->n); | |
| 477 | ✗ | if(scalingVector[i] <= 0.0) { | |
| 478 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Jacobian matrix is singular. Scaling of residual entry is set to 1e-16."); | |
| 479 | ✗ | scalingVector[i] = 1e-16; | |
| 480 | } | ||
| 481 | ✗ | else if (!isfinite(scalingVector[i])) | |
| 482 | { | ||
| 483 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Jacobian entry is inf or nan. Scaling of residual entry will be set to 1.0."); | |
| 484 | ✗ | scalingVector[i] = 1.0; | |
| 485 | } | ||
| 486 | } | ||
| 487 | ✗ | } | |
| 488 | |||
| 489 | /** | ||
| 490 | * @brief Scale residual vector. | ||
| 491 | * | ||
| 492 | * Save result in solverData->fvecScaled. | ||
| 493 | * | ||
| 494 | * @param solverData Newton solver data. | ||
| 495 | */ | ||
| 496 | ✗ | void scaling_residual_vector(DATA_NEWTON* solverData) | |
| 497 | { | ||
| 498 | int i; | ||
| 499 | |||
| 500 | ✗ | compute_scaling_vector(solverData, solverData->resScaling); | |
| 501 | ✗ | for(i=0; i<solverData->n; i++) | |
| 502 | { | ||
| 503 | ✗ | solverData->fvecScaled[i] = solverData->fvec[i] / solverData->resScaling[i]; | |
| 504 | } | ||
| 505 | ✗ | } | |
| 506 | |||
| 507 | /*! \fn damping_heuristic | ||
| 508 | * | ||
| 509 | * first damping heuristic: | ||
| 510 | * x_increment will be halved until the Euclidean norm of the residual function | ||
| 511 | * is smaller than the Euclidean norm of the current point | ||
| 512 | * | ||
| 513 | * treshold for damping = 0.01 | ||
| 514 | * compiler flag: -newton = damped | ||
| 515 | */ | ||
| 516 | ✗ | void damping_heuristic(double* x, genericResidualFunc f, | |
| 517 | double current_fvec_enorm, int n, double* fvec, double* lambda, int* k, | ||
| 518 | DATA_NEWTON* solverData, NLS_USERDATA* userData) | ||
| 519 | { | ||
| 520 | int i; | ||
| 521 | double enorm_new, treshold = 1e-2; | ||
| 522 | modelica_boolean startDamping = FALSE; /* remember to close log message */ | ||
| 523 | |||
| 524 | /* calculate new function values */ | ||
| 525 | ✗ | (*f)(n, solverData->x_new, fvec, userData, 1); | |
| 526 | ✗ | solverData->nfev++; | |
| 527 | |||
| 528 | ✗ | enorm_new=enorm_(&n,fvec); | |
| 529 | |||
| 530 | ✗ | if (enorm_new >= current_fvec_enorm) { | |
| 531 | startDamping = TRUE; | ||
| 532 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "Start Damping: enorm_new : %e; current_fvec_enorm: %e ", enorm_new, current_fvec_enorm); | |
| 533 | } | ||
| 534 | |||
| 535 | ✗ | while (enorm_new >= current_fvec_enorm) | |
| 536 | { | ||
| 537 | ✗ | *lambda*=0.5; | |
| 538 | |||
| 539 | |||
| 540 | ✗ | for (i=0; i<n; i++) | |
| 541 | ✗ | solverData->x_new[i]=x[i]-*lambda*solverData->x_increment[i]; | |
| 542 | |||
| 543 | |||
| 544 | /* calculate new function values */ | ||
| 545 | ✗ | (*f)(n, solverData->x_new, fvec, userData, 1); | |
| 546 | ✗ | solverData->nfev++; | |
| 547 | |||
| 548 | ✗ | enorm_new=enorm_(&n,fvec); | |
| 549 | |||
| 550 | ✗ | if (*lambda <= treshold) | |
| 551 | { | ||
| 552 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Warning: lambda reached a threshold."); | |
| 553 | |||
| 554 | /* if damping is without success, trying full newton step; | ||
| 555 | after 5 full newton steps try a very little step */ | ||
| 556 | ✗ | if (*k >= 5) | |
| 557 | ✗ | for (i=0; i<n; i++) | |
| 558 | ✗ | solverData->x_new[i]=x[i]-*lambda*solverData->x_increment[i]; | |
| 559 | else | ||
| 560 | ✗ | for (i=0; i<n; i++) | |
| 561 | ✗ | solverData->x_new[i]=x[i]-solverData->x_increment[i]; | |
| 562 | |||
| 563 | /* calculate new function values */ | ||
| 564 | ✗ | (*f)(n, solverData->x_new, fvec, userData, 1); | |
| 565 | ✗ | solverData->nfev++; | |
| 566 | |||
| 567 | ✗ | (*k)++; | |
| 568 | |||
| 569 | ✗ | break; | |
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 573 | ✗ | *lambda = 1; | |
| 574 | |||
| 575 | ✗ | if (startDamping) | |
| 576 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 577 | ✗ | } | |
| 578 | |||
| 579 | /*! \fn damping_heuristic2 | ||
| 580 | * | ||
| 581 | * second (default) damping heuristic: | ||
| 582 | * x_increment will be multiplied by 3/4 until the Euclidean norm of the | ||
| 583 | * residual function is smaller than the Euclidean norm of the current point | ||
| 584 | * | ||
| 585 | * treshold for damping = 0.0001 | ||
| 586 | * compiler flag: -newton = damped2 | ||
| 587 | */ | ||
| 588 | ✗ | void damping_heuristic2(double damping_parameter, double* x, genericResidualFunc f, | |
| 589 | double current_fvec_enorm, int n, double* fvec, int* k, | ||
| 590 | DATA_NEWTON* solverData, NLS_USERDATA* userdata) | ||
| 591 | { | ||
| 592 | int i; | ||
| 593 | double enorm_new, treshold = 1e-4, lambda=1; | ||
| 594 | modelica_boolean startDamping = FALSE; /* remember to close log message */ | ||
| 595 | |||
| 596 | /* calculate new function values */ | ||
| 597 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 598 | ✗ | solverData->nfev++; | |
| 599 | |||
| 600 | ✗ | enorm_new=enorm_(&n,fvec); | |
| 601 | |||
| 602 | ✗ | if (enorm_new >= current_fvec_enorm) { | |
| 603 | startDamping = TRUE; | ||
| 604 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 1, "StartDamping:"); | |
| 605 | } | ||
| 606 | |||
| 607 | ✗ | while (enorm_new >= current_fvec_enorm) | |
| 608 | { | ||
| 609 | ✗ | lambda*=damping_parameter; | |
| 610 | |||
| 611 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "lambda = %e, k = %d", lambda, *k); | |
| 612 | |||
| 613 | ✗ | for (i=0; i<n; i++) | |
| 614 | ✗ | solverData->x_new[i]=x[i]-lambda*solverData->x_increment[i]; | |
| 615 | |||
| 616 | |||
| 617 | /* calculate new function values */ | ||
| 618 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 619 | ✗ | solverData->nfev++; | |
| 620 | |||
| 621 | ✗ | enorm_new=enorm_(&n,fvec); | |
| 622 | |||
| 623 | ✗ | if (lambda <= treshold) | |
| 624 | { | ||
| 625 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Warning: lambda reached a threshold."); | |
| 626 | |||
| 627 | /* if damping is without success, trying full newton step; | ||
| 628 | after 5 full newton steps try a very little step */ | ||
| 629 | ✗ | if (*k >= 5) | |
| 630 | ✗ | for (i=0; i<n; i++) | |
| 631 | ✗ | solverData->x_new[i]=x[i]-lambda*solverData->x_increment[i]; | |
| 632 | else | ||
| 633 | ✗ | for (i=0; i<n; i++) | |
| 634 | ✗ | solverData->x_new[i]=x[i]-solverData->x_increment[i]; | |
| 635 | |||
| 636 | /* calculate new function values */ | ||
| 637 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 638 | ✗ | solverData->nfev++; | |
| 639 | |||
| 640 | ✗ | (*k)++; | |
| 641 | |||
| 642 | ✗ | break; | |
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | ✗ | if (startDamping) | |
| 647 | ✗ | messageClose(OMC_LOG_NLS_V); | |
| 648 | ✗ | } | |
| 649 | |||
| 650 | /*! \fn LineSearch | ||
| 651 | * | ||
| 652 | * third damping heuristic: | ||
| 653 | * Along the tangent 5 five points are selected. For every point the Euclidean | ||
| 654 | * norm of the residual function will be calculated and the minimum is chosen | ||
| 655 | * for the further iteration. | ||
| 656 | * | ||
| 657 | * compiler flag: -newton = damped_ls | ||
| 658 | */ | ||
| 659 | ✗ | void LineSearch(double* x, genericResidualFunc f, | |
| 660 | double current_fvec_enorm, int n, double* fvec, int* k, | ||
| 661 | DATA_NEWTON* solverData, NLS_USERDATA* userdata) | ||
| 662 | { | ||
| 663 | int i,j; | ||
| 664 | double enorm_new, enorm_minimum=current_fvec_enorm, lambda_minimum=0; | ||
| 665 | ✗ | double lambda[5]={1.25,1,0.75,0.5,0.25}; | |
| 666 | |||
| 667 | |||
| 668 | ✗ | for (j=0; j<5; j++) | |
| 669 | { | ||
| 670 | ✗ | for (i=0; i<n; i++) | |
| 671 | ✗ | solverData->x_new[i]=x[i]-lambda[j]*solverData->x_increment[i]; | |
| 672 | |||
| 673 | /* calculate new function values */ | ||
| 674 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 675 | ✗ | solverData->nfev++; | |
| 676 | |||
| 677 | ✗ | enorm_new=enorm_(&n,fvec); | |
| 678 | |||
| 679 | /* searching minimal enorm */ | ||
| 680 | ✗ | if (enorm_new < enorm_minimum) | |
| 681 | { | ||
| 682 | enorm_minimum = enorm_new; | ||
| 683 | ✗ | lambda_minimum = lambda[j]; | |
| 684 | ✗ | memcpy(solverData->fvec_minimum, fvec,n*sizeof(double)); | |
| 685 | } | ||
| 686 | } | ||
| 687 | |||
| 688 | ✗ | infoStreamPrint(OMC_LOG_NLS_V,0,"lambda_minimum = %e", lambda_minimum); | |
| 689 | |||
| 690 | ✗ | if (lambda_minimum == 0) | |
| 691 | { | ||
| 692 | ✗ | warningStreamPrint(OMC_LOG_NLS_V, 0, "Warning: lambda_minimum = 0 "); | |
| 693 | |||
| 694 | /* if damping is without success, trying full newton step; | ||
| 695 | after 5 full newton steps try a very little step */ | ||
| 696 | ✗ | if (*k >= 5) | |
| 697 | { | ||
| 698 | lambda_minimum = 0.125; | ||
| 699 | |||
| 700 | /* calculate new function values */ | ||
| 701 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 702 | ✗ | solverData->nfev++; | |
| 703 | } | ||
| 704 | else | ||
| 705 | { | ||
| 706 | lambda_minimum = 1; | ||
| 707 | |||
| 708 | /* calculate new function values */ | ||
| 709 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 710 | ✗ | solverData->nfev++; | |
| 711 | } | ||
| 712 | |||
| 713 | ✗ | (*k)++; | |
| 714 | } | ||
| 715 | else | ||
| 716 | { | ||
| 717 | /* save new function values */ | ||
| 718 | ✗ | memcpy(fvec, solverData->fvec_minimum, n*sizeof(double)); | |
| 719 | } | ||
| 720 | |||
| 721 | ✗ | for (i=0; i<n; i++) | |
| 722 | ✗ | solverData->x_new[i]=x[i]-lambda_minimum*solverData->x_increment[i]; | |
| 723 | ✗ | } | |
| 724 | |||
| 725 | /*! \fn Backtracking | ||
| 726 | * | ||
| 727 | * forth damping heuristic: | ||
| 728 | * Calculate new function h:R^n->R ; h(x) = 1/2 * ||f(x)|| ^2 | ||
| 729 | * g(lambda) = h(x_old + lambda * x_increment) | ||
| 730 | * find minimum of g with golden ratio method | ||
| 731 | * tau = golden ratio | ||
| 732 | * | ||
| 733 | * compiler flag: -newton = damped_bt | ||
| 734 | */ | ||
| 735 | ✗ | void Backtracking(double* x, | |
| 736 | genericResidualFunc f, | ||
| 737 | double current_fvec_enorm, | ||
| 738 | int n, | ||
| 739 | double* fvec, | ||
| 740 | DATA_NEWTON* solverData, | ||
| 741 | NLS_USERDATA* userdata) | ||
| 742 | { | ||
| 743 | int i,j; | ||
| 744 | double enorm_new, enorm_f, lambda, a1, b1, a, b, tau, g1, g2; | ||
| 745 | double tolerance = 1e-3; | ||
| 746 | |||
| 747 | /* saving current function values in f_old */ | ||
| 748 | ✗ | memcpy(solverData->f_old, fvec, n*sizeof(double)); | |
| 749 | |||
| 750 | ✗ | for (i=0; i<n; i++) | |
| 751 | ✗ | solverData->x_new[i]=x[i]-solverData->x_increment[i]; | |
| 752 | |||
| 753 | /* calculate new function values */ | ||
| 754 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 755 | ✗ | solverData->nfev++; | |
| 756 | |||
| 757 | |||
| 758 | /* calculate new enorm */ | ||
| 759 | ✗ | enorm_new = enorm_(&n,fvec); | |
| 760 | |||
| 761 | /* Backtracking only if full newton step is useless */ | ||
| 762 | ✗ | if (enorm_new >= current_fvec_enorm) | |
| 763 | { | ||
| 764 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "Start Backtracking\n enorm_new= %f \t current_fvec_enorm=%f", enorm_new, current_fvec_enorm); | |
| 765 | |||
| 766 | /* h(x) = 1/2 * ||f(x)|| ^2 | ||
| 767 | * g(lambda) = h(x_old + lambda * x_increment) | ||
| 768 | * find minimum of g with golden ratio method | ||
| 769 | * tau = golden ratio | ||
| 770 | * */ | ||
| 771 | |||
| 772 | a = 0; | ||
| 773 | b = 1; | ||
| 774 | tau = 0.618033988749894848; | ||
| 775 | |||
| 776 | a1 = a + (1-tau)*(b-a); | ||
| 777 | /* g1 = g(a1) = h(x_old - a1 * x_increment) = 1/2 * ||f(x_old- a1 * x_increment)||^2 */ | ||
| 778 | ✗ | solverData->x_new[i] = x[i]- a1 * solverData->x_increment[i]; | |
| 779 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 780 | ✗ | solverData->nfev++; | |
| 781 | ✗ | enorm_f= enorm_(&n,fvec); | |
| 782 | ✗ | g1 = 0.5 * enorm_f * enorm_f; | |
| 783 | |||
| 784 | |||
| 785 | b1 = a + tau * (b-a); | ||
| 786 | /* g2 = g(b1) = h(x_old - b1 * x_increment) = 1/2 * ||f(x_old- b1 * x_increment)||^2 */ | ||
| 787 | ✗ | solverData->x_new[i] = x[i]- b1 * solverData->x_increment[i]; | |
| 788 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 789 | ✗ | solverData->nfev++; | |
| 790 | ✗ | enorm_f= enorm_(&n,fvec); | |
| 791 | ✗ | g2 = 0.5 * enorm_f * enorm_f; | |
| 792 | |||
| 793 | ✗ | while ( (b - a) > tolerance) | |
| 794 | { | ||
| 795 | ✗ | if (g1<g2) | |
| 796 | { | ||
| 797 | b = b1; | ||
| 798 | b1 = a1; | ||
| 799 | ✗ | a1 = a + (1-tau)*(b-a); | |
| 800 | g2 = g1; | ||
| 801 | |||
| 802 | /* g1 = g(a1) = h(x_old - a1 * x_increment) = 1/2 * ||f(x_old- a1 * x_increment)||^2 */ | ||
| 803 | ✗ | solverData->x_new[i] = x[i]- a1 * solverData->x_increment[i]; | |
| 804 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 805 | ✗ | solverData->nfev++; | |
| 806 | ✗ | enorm_f= enorm_(&n,fvec); | |
| 807 | ✗ | g1 = 0.5 * enorm_f * enorm_f; | |
| 808 | } | ||
| 809 | else | ||
| 810 | { | ||
| 811 | a = a1; | ||
| 812 | a1 = b1; | ||
| 813 | ✗ | b1 = a + tau * (b-a); | |
| 814 | g1 = g2; | ||
| 815 | |||
| 816 | /* g2 = g(b1) = h(x_old - b1 * x_increment) = 1/2 * ||f(x_old- b1 * x_increment)||^2 */ | ||
| 817 | ✗ | solverData->x_new[i] = x[i]- b1 * solverData->x_increment[i]; | |
| 818 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 819 | ✗ | solverData->nfev++; | |
| 820 | ✗ | enorm_f= enorm_(&n,fvec); | |
| 821 | ✗ | g2 = 0.5 * enorm_f * enorm_f; | |
| 822 | } | ||
| 823 | } | ||
| 824 | |||
| 825 | ✗ | lambda = (a+b)/2; | |
| 826 | |||
| 827 | /* print lambda */ | ||
| 828 | ✗ | infoStreamPrint(OMC_LOG_NLS_V, 0, "Backtracking - lambda = %e", lambda); | |
| 829 | |||
| 830 | ✗ | for (i=0; i<n; i++) | |
| 831 | ✗ | solverData->x_new[i]=x[i]-lambda*solverData->x_increment[i]; | |
| 832 | |||
| 833 | /* calculate new function values */ | ||
| 834 | ✗ | (*f)(n, solverData->x_new, fvec, userdata, 1); | |
| 835 | ✗ | solverData->nfev++; | |
| 836 | } | ||
| 837 | ✗ | } | |
| 838 | |||
| 839 | #ifdef __cplusplus | ||
| 840 | } | ||
| 841 | #endif | ||
| 842 |