OMCompiler/SimulationRuntime/c/simulation/solver/gbode_err.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file belongs to the OpenModelica Run-Time System | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings | ||
| 5 | * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights | ||
| 6 | * reserved. | ||
| 7 | * | ||
| 8 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE | ||
| 9 | * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY | ||
| 10 | * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S | ||
| 11 | * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL | ||
| 12 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 13 | * | ||
| 14 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License | ||
| 15 | * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs: | ||
| 16 | * http://www.openmodelica.org or https://github.com/OpenModelica/ or | ||
| 17 | * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU | ||
| 18 | * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW | ||
| 19 | * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause. | ||
| 20 | * | ||
| 21 | * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY | ||
| 23 | * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF | ||
| 24 | * OSMC-PL. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include "gbode_err.h" | ||
| 29 | #include "gbode_internal_nls.h" | ||
| 30 | |||
| 31 | #include <float.h> | ||
| 32 | #include <math.h> | ||
| 33 | |||
| 34 | /* some constants for less verbose BLAS calls */ | ||
| 35 | static const double DBL_ZERO = 0.0; | ||
| 36 | static const double DBL_ONE = 1.0; | ||
| 37 | static const double DBL_MINUS_ONE = -1.0; | ||
| 38 | static const int INT_ONE = 1; | ||
| 39 | static const char CHAR_NO_TRANS = 'N'; | ||
| 40 | |||
| 41 | /* y := a * x + y */ | ||
| 42 | extern void daxpy_(const int *n, | ||
| 43 | const double *alpha, | ||
| 44 | const double *x, const int *incX, | ||
| 45 | double *y, const int *incY); | ||
| 46 | |||
| 47 | /* y := alpha * A * x + beta * y */ | ||
| 48 | extern void dgemv_(const char *trans, | ||
| 49 | const int *m, | ||
| 50 | const int *n, | ||
| 51 | const double *alpha, const double *A, const int *ldA, | ||
| 52 | const double *x, const int *incX, | ||
| 53 | const double *beta, double *y, const int *incY | ||
| 54 | ); | ||
| 55 | |||
| 56 | /* x := alpha * x */ | ||
| 57 | extern void dscal_(const int *n, | ||
| 58 | const double *alpha, | ||
| 59 | double *x, const int *incX); | ||
| 60 | |||
| 61 | static inline void setErrorEstimatorOrder(GB_ERROR_CONTEXT *context, int order) | ||
| 62 | { | ||
| 63 | ✗ | if (context->isFast) | |
| 64 | { | ||
| 65 | ✗ | context->gbfData->currentErrorOrder = order; | |
| 66 | } | ||
| 67 | else | ||
| 68 | { | ||
| 69 | ✗ | context->gbData->currentErrorOrder = order; | |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | static inline int evaluateError(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | ||
| 74 | { | ||
| 75 | ✗ | if (estimator == NULL || estimator->type == GB_ERROR_UNKNOWN || estimator->evaluate == NULL) | |
| 76 | { | ||
| 77 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 78 | } | ||
| 79 | |||
| 80 | ✗ | return estimator->evaluate(context, estimator); | |
| 81 | } | ||
| 82 | |||
| 83 | ✗ | int gbEstimateError(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | |
| 84 | { | ||
| 85 | int order = evaluateError(context, estimator); | ||
| 86 | ✗ | if (order < 0) | |
| 87 | { | ||
| 88 | ✗ | return order; | |
| 89 | } | ||
| 90 | |||
| 91 | setErrorEstimatorOrder(context, order); | ||
| 92 | return order; | ||
| 93 | } | ||
| 94 | |||
| 95 | ✗ | double gbScaledErrorTolerance(double tol, int methodOrder, int estimatorOrder, modelica_boolean richardson) | |
| 96 | { | ||
| 97 | ✗ | if (richardson || estimatorOrder >= methodOrder) | |
| 98 | { | ||
| 99 | return tol; | ||
| 100 | } | ||
| 101 | |||
| 102 | ✗ | const double order_quot = ((double) estimatorOrder + 1.0) / ((double) methodOrder + 1.0); | |
| 103 | ✗ | const double rtol_pred = GB_TOLERANCE_SCALING_SAFETY * pow(tol, order_quot); | |
| 104 | |||
| 105 | ✗ | return fmax(tol, rtol_pred); | |
| 106 | } | ||
| 107 | |||
| 108 | ✗ | static void embeddedErrorEstimate_gb(BUTCHER_TABLEAU *tableau, const double *weights, const double *K, double stepSize, int nStates, double *err) | |
| 109 | { | ||
| 110 | double factors[MAX_GBODE_STAGES]; | ||
| 111 | ✗ | int nStages = tableau->nStages; | |
| 112 | |||
| 113 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 114 | { | ||
| 115 | ✗ | factors[stage] = stepSize * (tableau->b[stage] - weights[stage]); | |
| 116 | } | ||
| 117 | |||
| 118 | /* err := stepSize * (K otimes I) * (b - weights) */ | ||
| 119 | ✗ | dgemv_(&CHAR_NO_TRANS, | |
| 120 | &nStates, | ||
| 121 | &nStages, | ||
| 122 | &DBL_ONE, K, &nStates, | ||
| 123 | factors, &INT_ONE, | ||
| 124 | &DBL_ZERO, err, &INT_ONE); | ||
| 125 | ✗ | } | |
| 126 | |||
| 127 | static inline void absErrorEstimate_gb(int nStates, double *errest) | ||
| 128 | { | ||
| 129 | ✗ | for (int i = 0; i < nStates; i++) | |
| 130 | { | ||
| 131 | ✗ | errest[i] = fabs(errest[i]); | |
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | ✗ | static void embeddedErrorEstimate_gbf(BUTCHER_TABLEAU *tableau, const double *weights, DATA_GBODE *gbData, DATA_GBODEF *gbfData, const double *K, double *err) | |
| 136 | { | ||
| 137 | ✗ | int nStates = gbData->nStates; | |
| 138 | ✗ | int nFastStates = gbData->nFastStates; | |
| 139 | ✗ | int nStages = tableau->nStages; | |
| 140 | double factors[MAX_GBODE_STAGES]; | ||
| 141 | |||
| 142 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 143 | { | ||
| 144 | ✗ | factors[stage] = gbfData->stepSize * (tableau->b[stage] - weights[stage]); | |
| 145 | } | ||
| 146 | |||
| 147 | ✗ | for (int fast_idx = 0; fast_idx < nFastStates; fast_idx++) | |
| 148 | { | ||
| 149 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 150 | ✗ | err[full_idx] = 0.0; | |
| 151 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 152 | { | ||
| 153 | ✗ | err[full_idx] += factors[stage] * K[stage * nStates + full_idx]; | |
| 154 | } | ||
| 155 | } | ||
| 156 | ✗ | } | |
| 157 | |||
| 158 | static void absErrorEstimate_gbf(DATA_GBODE *gbData, DATA_GBODEF *gbfData) | ||
| 159 | { | ||
| 160 | ✗ | for (int fast_idx = 0; fast_idx < gbData->nFastStates; fast_idx++) | |
| 161 | { | ||
| 162 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 163 | ✗ | gbfData->errest[full_idx] = fabs(gbfData->errest[full_idx]); | |
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | ✗ | int gbEmbeddedErrorEstimator(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | |
| 168 | { | ||
| 169 | ✗ | const double *weights = (const double *) estimator->data; | |
| 170 | |||
| 171 | ✗ | if (weights == NULL) | |
| 172 | { | ||
| 173 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 174 | } | ||
| 175 | |||
| 176 | ✗ | if (context->isFast) | |
| 177 | { | ||
| 178 | ✗ | embeddedErrorEstimate_gbf(context->gbfData->tableau, weights, context->gbData, context->gbfData, context->gbfData->k, context->gbfData->errest); | |
| 179 | absErrorEstimate_gbf(context->gbData, context->gbfData); | ||
| 180 | } | ||
| 181 | else | ||
| 182 | { | ||
| 183 | ✗ | DATA_GBODE *gbData = context->gbData; | |
| 184 | ✗ | embeddedErrorEstimate_gb(gbData->tableau, weights, gbData->k, gbData->stepSize, gbData->nStates, gbData->errest); | |
| 185 | ✗ | absErrorEstimate_gb(gbData->nStates, gbData->errest); | |
| 186 | } | ||
| 187 | |||
| 188 | ✗ | return estimator->order; | |
| 189 | } | ||
| 190 | |||
| 191 | ✗ | int gbContractiveDefectErrorEstimator(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | |
| 192 | { | ||
| 193 | ✗ | CONTRACTIVE_DEFECT *contractive = (CONTRACTIVE_DEFECT *) estimator->data; | |
| 194 | |||
| 195 | ✗ | if (contractive == NULL) | |
| 196 | { | ||
| 197 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 198 | } | ||
| 199 | |||
| 200 | ✗ | if (context->isFast) | |
| 201 | { | ||
| 202 | ✗ | DATA_GBODE *gbData = context->gbData; | |
| 203 | ✗ | DATA_GBODEF *gbfData = context->gbfData; | |
| 204 | |||
| 205 | ✗ | if (gbfData->tableau->t_transform == NULL || gbfData->nlsSolverMethod != GB_NLS_INTERNAL) | |
| 206 | { | ||
| 207 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 208 | } | ||
| 209 | |||
| 210 | ✗ | double *work = gbInternalGetWorkPointer(((struct dataSolver *)gbfData->nlsData->solverData)->ordinaryData); | |
| 211 | ✗ | gbInternalContractiveDefect(context->data, context->threadData, gbfData->nlsData, gbData, contractive, work); | |
| 212 | ✗ | for (int fast_idx = 0; fast_idx < gbData->nFastStates; fast_idx++) | |
| 213 | { | ||
| 214 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 215 | ✗ | gbfData->errest[full_idx] = fabs(work[fast_idx]); | |
| 216 | } | ||
| 217 | } | ||
| 218 | else | ||
| 219 | { | ||
| 220 | ✗ | DATA_GBODE *gbData = context->gbData; | |
| 221 | |||
| 222 | ✗ | if (gbData->tableau->t_transform == NULL || gbData->nlsSolverMethod != GB_NLS_INTERNAL) | |
| 223 | { | ||
| 224 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 225 | } | ||
| 226 | |||
| 227 | ✗ | gbInternalContractiveDefect(context->data, context->threadData, gbData->nlsData, gbData, contractive, gbData->errest); | |
| 228 | ✗ | absErrorEstimate_gb(gbData->nStates, gbData->errest); | |
| 229 | } | ||
| 230 | |||
| 231 | ✗ | return estimator->order; | |
| 232 | } | ||
| 233 | |||
| 234 | ✗ | int gbContractiveFilterErrorEstimator(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | |
| 235 | { | ||
| 236 | ✗ | if (context->isFast) | |
| 237 | { | ||
| 238 | ✗ | const double *weights = (const double *) context->gbfData->tableau->error.embedded.data; | |
| 239 | ✗ | if (weights == NULL || context->gbfData->nlsSolverMethod != GB_NLS_INTERNAL) | |
| 240 | { | ||
| 241 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 242 | } | ||
| 243 | ✗ | embeddedErrorEstimate_gbf(context->gbfData->tableau, weights, context->gbData, context->gbfData, context->gbfData->k, context->gbfData->errest); | |
| 244 | ✗ | gbInternalContractiveFilterError(context->gbfData->nlsData, context->gbData, context->gbfData->errest); | |
| 245 | ✗ | absErrorEstimate_gbf(context->gbData, context->gbfData); | |
| 246 | } | ||
| 247 | else | ||
| 248 | { | ||
| 249 | ✗ | DATA_GBODE *gbData = context->gbData; | |
| 250 | ✗ | const double *weights = (const double *) gbData->tableau->error.embedded.data; | |
| 251 | ✗ | if (weights == NULL || gbData->nlsSolverMethod != GB_NLS_INTERNAL) | |
| 252 | { | ||
| 253 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 254 | } | ||
| 255 | ✗ | embeddedErrorEstimate_gb(gbData->tableau, weights, gbData->k, gbData->stepSize, gbData->nStates, gbData->errest); | |
| 256 | ✗ | gbInternalContractiveFilterError(gbData->nlsData, gbData, gbData->errest); | |
| 257 | ✗ | absErrorEstimate_gb(gbData->nStates, gbData->errest); | |
| 258 | } | ||
| 259 | |||
| 260 | ✗ | return estimator->order; | |
| 261 | } | ||
| 262 | |||
| 263 | ✗ | static inline modelica_boolean twoStepScaleMu(double tol, | |
| 264 | int methodOrder, | ||
| 265 | int estimatorOrder, | ||
| 266 | modelica_boolean richardson, | ||
| 267 | double *mu) | ||
| 268 | { | ||
| 269 | ✗ | const double scaled_tol = gbScaledErrorTolerance(tol, methodOrder, estimatorOrder, richardson); | |
| 270 | ✗ | const double order_quot = ((double) estimatorOrder + 1.0) / ((double) methodOrder + 1.0); | |
| 271 | |||
| 272 | ✗ | *mu *= scaled_tol / pow(tol, order_quot); | |
| 273 | |||
| 274 | // These cases should not occur for a well-conditioned two-step estimator: | ||
| 275 | // - num(r) -> 0, then mu(r) -> inf: the raw estimator loses its leading term and becomes locally one order higher than designed | ||
| 276 | // - den(r) -> 0, then mu(r) -> 0: the coefficient part already has a pole | ||
| 277 | // in both cases the estimator is invalid / inadequate | ||
| 278 | |||
| 279 | ✗ | if (!isfinite(*mu)) | |
| 280 | { | ||
| 281 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) warningStreamPrint(OMC_LOG_GBODE, 0, "Two-step estimator mu(r) is not finite - falling back."); | |
| 282 | ✗ | return FALSE; | |
| 283 | } | ||
| 284 | ✗ | else if (fabs(*mu) < 1e-6) | |
| 285 | { | ||
| 286 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) warningStreamPrint(OMC_LOG_GBODE, 0, "Two-step estimator mu(r) is below 1e-6 - falling back."); | |
| 287 | ✗ | return FALSE; | |
| 288 | } | ||
| 289 | ✗ | else if (fabs(*mu) > 1e6) | |
| 290 | { | ||
| 291 | ✗ | if (OMC_ACTIVE_STREAM(OMC_LOG_GBODE)) warningStreamPrint(OMC_LOG_GBODE, 0, "Two-step estimator mu(r) is above 1e6 - falling back."); | |
| 292 | ✗ | return FALSE; | |
| 293 | } | ||
| 294 | |||
| 295 | return TRUE; | ||
| 296 | } | ||
| 297 | |||
| 298 | ✗ | static int twoStepEstimate_gb(TWO_STEP_ESTIMATOR *two_step, DATA_GBODE *gbData, double tol, int estimatorOrder) | |
| 299 | { | ||
| 300 | ✗ | BUTCHER_TABLEAU *tableau = gbData->tableau; | |
| 301 | double d_old[MAX_GBODE_FIRK_STAGES]; | ||
| 302 | double g_new[MAX_GBODE_FIRK_STAGES]; | ||
| 303 | double mu; | ||
| 304 | double minus_mu; | ||
| 305 | ✗ | int nStages = tableau->nStages; | |
| 306 | ✗ | int nStates = gbData->nStates; | |
| 307 | |||
| 308 | ✗ | if (tableau->nStages > MAX_GBODE_FIRK_STAGES || gbData->lastStepSize <= 0.0 || gbData->extrapolationBaseTime == INFINITY || gbData->eventHappened || gbData->didFastStep) | |
| 309 | { | ||
| 310 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 311 | } | ||
| 312 | |||
| 313 | ✗ | double r = gbData->stepSize / gbData->lastStepSize; | |
| 314 | ✗ | two_step->weights(r, d_old, g_new, &mu); | |
| 315 | ✗ | if (!twoStepScaleMu(tol, tableau->order_b, estimatorOrder, tableau->richardson, &mu)) | |
| 316 | { | ||
| 317 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 318 | } | ||
| 319 | |||
| 320 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 321 | { | ||
| 322 | ✗ | d_old[stage] *= gbData->lastStepSize; | |
| 323 | ✗ | g_new[stage] *= gbData->stepSize; | |
| 324 | } | ||
| 325 | |||
| 326 | ✗ | dgemv_(&CHAR_NO_TRANS, | |
| 327 | &nStates, | ||
| 328 | &nStages, | ||
| 329 | ✗ | &DBL_ONE, gbData->kLast, &nStates, | |
| 330 | d_old, &INT_ONE, | ||
| 331 | &DBL_ZERO, gbData->errest, &INT_ONE); | ||
| 332 | |||
| 333 | ✗ | dgemv_(&CHAR_NO_TRANS, | |
| 334 | &nStates, | ||
| 335 | &nStages, | ||
| 336 | ✗ | &DBL_ONE, gbData->k, &nStates, | |
| 337 | g_new, &INT_ONE, | ||
| 338 | &DBL_ONE, gbData->errest, &INT_ONE); | ||
| 339 | |||
| 340 | ✗ | daxpy_(&nStates, &DBL_ONE, gbData->yOld, &INT_ONE, gbData->errest, &INT_ONE); | |
| 341 | |||
| 342 | /* errest := abs(mu * (y - y_emb)) */ | ||
| 343 | ✗ | minus_mu = DBL_MINUS_ONE * mu; | |
| 344 | ✗ | dscal_(&nStates, &minus_mu, gbData->errest, &INT_ONE); | |
| 345 | ✗ | daxpy_(&nStates, &mu, gbData->y, &INT_ONE, gbData->errest, &INT_ONE); | |
| 346 | ✗ | absErrorEstimate_gb(nStates, gbData->errest); | |
| 347 | |||
| 348 | return 0; | ||
| 349 | } | ||
| 350 | |||
| 351 | ✗ | static int twoStepEstimate_gbf(TWO_STEP_ESTIMATOR *two_step, DATA_GBODE *gbData, DATA_GBODEF *gbfData, double tol, int estimatorOrder) | |
| 352 | { | ||
| 353 | ✗ | BUTCHER_TABLEAU *tableau = gbfData->tableau; | |
| 354 | double d_old[MAX_GBODE_FIRK_STAGES]; | ||
| 355 | double g_new[MAX_GBODE_FIRK_STAGES]; | ||
| 356 | double mu; | ||
| 357 | ✗ | int nStates = gbData->nStates; | |
| 358 | ✗ | int nFastStates = gbData->nFastStates; | |
| 359 | ✗ | int nStages = tableau->nStages; | |
| 360 | |||
| 361 | ✗ | if (tableau->nStages > MAX_GBODE_FIRK_STAGES || gbfData->lastStepSize <= 0.0 || !gbfData->extrapolationValid) | |
| 362 | { | ||
| 363 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 364 | } | ||
| 365 | |||
| 366 | ✗ | double r = gbfData->stepSize / gbfData->lastStepSize; | |
| 367 | ✗ | two_step->weights(r, d_old, g_new, &mu); | |
| 368 | ✗ | if (!twoStepScaleMu(tol, tableau->order_b, estimatorOrder, tableau->richardson, &mu)) | |
| 369 | { | ||
| 370 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 371 | } | ||
| 372 | |||
| 373 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 374 | { | ||
| 375 | ✗ | d_old[stage] *= gbfData->lastStepSize; | |
| 376 | ✗ | g_new[stage] *= gbfData->stepSize; | |
| 377 | } | ||
| 378 | |||
| 379 | ✗ | for (int fast_idx = 0; fast_idx < nFastStates; fast_idx++) | |
| 380 | { | ||
| 381 | ✗ | int full_idx = gbData->fastStatesIdx[fast_idx]; | |
| 382 | ✗ | double y_emb = gbfData->yOld[full_idx]; | |
| 383 | ✗ | for (int stage = 0; stage < nStages; stage++) | |
| 384 | { | ||
| 385 | ✗ | double k_new = gbfData->nlsSolverMethod == GB_NLS_INTERNAL | |
| 386 | ✗ | ? gbfData->kCurrPacked[stage * nFastStates + fast_idx] | |
| 387 | ✗ | : gbfData->k[stage * nStates + full_idx]; | |
| 388 | ✗ | y_emb += d_old[stage] * gbfData->kLast[stage * nFastStates + fast_idx] + g_new[stage] * k_new; | |
| 389 | } | ||
| 390 | ✗ | gbfData->errest[full_idx] = fabs(mu * (gbfData->y[full_idx] - y_emb)); | |
| 391 | } | ||
| 392 | |||
| 393 | return 0; | ||
| 394 | } | ||
| 395 | |||
| 396 | ✗ | int gbTwoStepErrorEstimator(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | |
| 397 | { | ||
| 398 | ✗ | TWO_STEP_ESTIMATOR *two_step = (TWO_STEP_ESTIMATOR *) estimator->data; | |
| 399 | |||
| 400 | ✗ | if (two_step == NULL) | |
| 401 | { | ||
| 402 | return GB_ERROR_ESTIMATOR_FAILED; | ||
| 403 | } | ||
| 404 | |||
| 405 | ✗ | const double tol = context->data->simulationInfo->tolerance; | |
| 406 | ✗ | int order = context->isFast ? twoStepEstimate_gbf(two_step, context->gbData, context->gbfData, tol, estimator->order) | |
| 407 | ✗ | : twoStepEstimate_gb(two_step, context->gbData, tol, estimator->order); | |
| 408 | ✗ | if (order >= 0) | |
| 409 | { | ||
| 410 | ✗ | return estimator->order; | |
| 411 | } | ||
| 412 | |||
| 413 | ✗ | return evaluateError(context, two_step->fallback); | |
| 414 | } | ||
| 415 | |||
| 416 | ✗ | int gbRichardsonErrorEstimator(GB_ERROR_CONTEXT *context, const GB_ERROR_ESTIMATOR *estimator) | |
| 417 | { | ||
| 418 | (void) context; // suppress it. | ||
| 419 | ✗ | return estimator->order; | |
| 420 | } | ||
| 421 |