From 48b11e4717a486c16b184ae930d286cfbdfb708b Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:45:42 +0200 Subject: [PATCH 1/8] Update SUNDIALS to 7.8.0 Bump 3rdParty, which replaces the patched sundials-5.4.0 source copy with a pristine submodule pinned to v7.8.0, and port the solvers to the new API. In the integrated OpenModelica build (OPENMODELICA_NEW_CMAKE_BUILD) this block is skipped and we link the SUNDIALS that OMC configured, so the two option sets have to stay in agreement. Going from 5.4.0 to 7.8.0 crosses two breaking releases: - Every SUNDIALS object is now created against a SUNContext. SystemSC, SystemSC3 and KinsolSolver each own one and pass it to N_VNew_Serial, SUNDenseMatrix, SUNLinSol_Dense, CVodeCreate and KINCreate. It has to be created before the first such object and freed after the last one. - realtype -> sunrealtype. - KINSetErrHandlerFn is gone; the error handler is pushed onto the SUNContext with SUNContext_PushErrHandler and now reports a source location plus a SUNErrCode instead of a module name and message string. Package level codes (KIN_* and friends) are not SUNErrCodes, so SUNGetErrMsg() is only used when SUNDIALS did not supply a message, the same rule its own default handler uses. - KINSetPrintLevel and KINSetInfoHandlerFn are gone with no equivalent, so sundialsInfoHandlerFunction is dropped. KINSOL's progress output only exists via the SUNLogger, which is compiled out at the logging level SUNDIALS is built with here. - SUNDIALS no longer writes to a FILE* of its own. Up to SUNDIALS 5 CVODE printed to cv_errfp, which defaulted to stderr; the same messages now go through the SUNContext error handler chain, whose default handler writes to the SUNLogger's error stream. Either way that bypasses OMSimulator's logger, so the logger's error and warning streams are muted per context and we report through logError as before. While in here, report the allocation failures in KinsolSolver::NewKinsolSolver that were silently ignored: the five N_VNew_Serial calls, SUNDenseMatrix and SUNLinSol_Dense were used without checking for NULL. Also correct two copy-pasted messages that blamed N_VNew_Serial for a failing SUNDenseMatrix. Also follow the oms::3rd::cvode/kinsol -> oms::3rd::sundials::cvode/kinsol alias rename from 3rdParty. Co-Authored-By: Claude Opus 4.8 --- 3rdParty | 2 +- src/OMSimulatorLib/AlgLoop.cpp | 123 +++++++++++++++--------------- src/OMSimulatorLib/AlgLoop.h | 9 ++- src/OMSimulatorLib/CMakeLists.txt | 8 +- src/OMSimulatorLib/SystemSC.cpp | 36 ++++++--- src/OMSimulatorLib/SystemSC.h | 21 ++--- src/OMSimulatorLib/SystemSC3.cpp | 36 ++++++--- src/OMSimulatorLib/SystemSC3.h | 21 ++--- 8 files changed, 148 insertions(+), 108 deletions(-) diff --git a/3rdParty b/3rdParty index 34b05ece4..bb4be742c 160000 --- a/3rdParty +++ b/3rdParty @@ -1 +1 @@ -Subproject commit 34b05ece412f8dbf0376f7969e5404ae0242cf75 +Subproject commit bb4be742c171721c9efe19979b1b7236ac9e0b69 diff --git a/src/OMSimulatorLib/AlgLoop.cpp b/src/OMSimulatorLib/AlgLoop.cpp index ff85d689f..1cf3116ae 100644 --- a/src/OMSimulatorLib/AlgLoop.cpp +++ b/src/OMSimulatorLib/AlgLoop.cpp @@ -70,49 +70,28 @@ inline bool checkFlag(int flag, std::string functionName) * @param msg Error Message. * @param user_data Pointer to user data. Unused. */ -void oms::KinsolSolver::sundialsErrorHandlerFunction(int errorCode, const char *module, - const char *function, char *msg, - void *user_data) +void oms::KinsolSolver::sundialsErrorHandlerFunction(int line, const char *func_name, + const char *file, const char *msg, + SUNErrCode err_code, + void *err_user_data, SUNContext sunctx) { KINSOL_USER_DATA* kinsolUserData; std::string systNum = "unknown"; - std::string mod = module; - std::string func = function; + std::string mod = std::string(file) + ":" + std::to_string(line); + std::string func = func_name; - if (user_data != NULL) + if (err_user_data != NULL) { - kinsolUserData = (KINSOL_USER_DATA *)user_data; + kinsolUserData = (KINSOL_USER_DATA *)err_user_data; systNum = std::to_string(kinsolUserData->algLoopNumber); } - logError("SUNDIALS_ERROR: [system] " + systNum + " [module] " + mod + " | [function] " + func - + " | [error_code] " + std::to_string(errorCode) + "\n" + std::string(msg)); -} - -/** - * @brief Info handler function given to KINSOL. - * - * Will only print information when debug loging is active. - * - * @param module Name of the module reporting the information. - * @param function Name of the function reporting the information. - * @param msg Message. - * @param user_data Pointer to user data. Unused. - */ -void oms::KinsolSolver::sundialsInfoHandlerFunction(const char *module, const char *function, - char *msg, void *user_data) -{ - KINSOL_USER_DATA* kinsolUserData; - std::string systNum = "unknown"; - std::string mod = module; - std::string func = function; - - if (user_data != NULL) { - kinsolUserData = (KINSOL_USER_DATA *)user_data; - systNum = std::to_string(kinsolUserData->algLoopNumber); - } - - logDebug("SUNDIALS_INFO: [system] " + systNum + " [module] " + mod + " | [function] " + func + "\n" + std::string(msg)); + /* Package level codes (KIN_* and friends) are not SUNErrCodes, so SUNGetErrMsg() + only makes sense when SUNDIALS did not supply a message - same rule as + SUNDIALS' own default handler. */ + logError("SUNDIALS_ERROR: [system] " + systNum + " [at] " + mod + " | [function] " + func + + " | [error_code] " + std::to_string(err_code) + + "\n" + std::string(msg ? msg : SUNGetErrMsg(err_code))); } /** @@ -285,6 +264,8 @@ oms::KinsolSolver::~KinsolSolver() SUNMatDestroy(this->J); N_VDestroy_Serial(this->y); + SUNContext_Free(&(this->sunctx)); + delete((KINSOL_USER_DATA*)(this->user_data)); } @@ -299,23 +280,46 @@ oms::KinsolSolver::~KinsolSolver() oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, const unsigned int size, double relativeTolerance, const bool useDirectionalDerivative) { int flag; - int printLevel; KinsolSolver* kinsolSolver = new KinsolSolver(); logDebug("Create new KinsolSolver object for algebraic loop number " + std::to_string(algLoopNum)); kinsolSolver->size = size; + /* Create the SUNDIALS context every other SUNDIALS object is created with */ + if (SUNContext_Create(SUN_COMM_NULL, &kinsolSolver->sunctx) != SUN_SUCCESS) + { + logError("SUNDIALS_ERROR: SUNContext_Create() failed"); + return NULL; + } + + /* Mute SUNDIALS' own logger: since SUNDIALS 7 package level messages go to + stderr/stdout by default, and we report solver failures ourselves. */ + { + SUNLogger logger = NULL; + if (SUNContext_GetLogger(kinsolSolver->sunctx, &logger) == SUN_SUCCESS && logger != NULL) + { + SUNLogger_SetErrorFilename(logger, ""); + SUNLogger_SetWarningFilename(logger, ""); + } + } + /* Allocate memory */ - kinsolSolver->initialGuess = N_VNew_Serial(kinsolSolver->size); - kinsolSolver->uScale = N_VNew_Serial(kinsolSolver->size); - kinsolSolver->fScale = N_VNew_Serial(kinsolSolver->size); - kinsolSolver->fTmp = N_VNew_Serial(kinsolSolver->size); - kinsolSolver->y = N_VNew_Serial(kinsolSolver->size); + kinsolSolver->initialGuess = N_VNew_Serial(kinsolSolver->size, kinsolSolver->sunctx); + kinsolSolver->uScale = N_VNew_Serial(kinsolSolver->size, kinsolSolver->sunctx); + kinsolSolver->fScale = N_VNew_Serial(kinsolSolver->size, kinsolSolver->sunctx); + kinsolSolver->fTmp = N_VNew_Serial(kinsolSolver->size, kinsolSolver->sunctx); + kinsolSolver->y = N_VNew_Serial(kinsolSolver->size, kinsolSolver->sunctx); + if (!kinsolSolver->initialGuess || !kinsolSolver->uScale || !kinsolSolver->fScale + || !kinsolSolver->fTmp || !kinsolSolver->y) + { + logError("SUNDIALS_ERROR: N_VNew_Serial() failed"); + return NULL; + } kinsolSolver->kinsolMemory = NULL; /* Create KINSOL memory block */ - kinsolSolver->kinsolMemory = KINCreate(); + kinsolSolver->kinsolMemory = KINCreate(kinsolSolver->sunctx); if (kinsolSolver->kinsolMemory == NULL) { logError("SUNDIALS_ERROR: KINCreate() failed"); @@ -327,34 +331,29 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons flag = KINSetUserData(kinsolSolver->kinsolMemory, kinsolSolver->user_data); if (!checkFlag(flag, "KINSetUserData")) return NULL; - /* Set error handler and print level */ - if (logDebugEnabled()) - { - logDebug("SUNDIALS KINSOL: Set print level to maximum."); - printLevel = 3; - } - else - { - printLevel = 0; - } - flag = KINSetPrintLevel(kinsolSolver->kinsolMemory, printLevel); - if (!checkFlag(flag, "KINSetPrintLevel")) return NULL; - - flag = KINSetErrHandlerFn(kinsolSolver->kinsolMemory, sundialsErrorHandlerFunction, kinsolSolver->user_data); - if (!checkFlag(flag, "KINSetErrHandlerFn")) return NULL; - - flag = KINSetInfoHandlerFn(kinsolSolver->kinsolMemory, sundialsInfoHandlerFunction, kinsolSolver->user_data); - if (!checkFlag(flag, "KINSetInfoHandlerFn")) return NULL; + /* Set error handler. KINSOL's progress output goes through the SUNLogger */ + flag = SUNContext_PushErrHandler(kinsolSolver->sunctx, sundialsErrorHandlerFunction, kinsolSolver->user_data); + if (!checkFlag(flag, "SUNContext_PushErrHandler")) return NULL; /* Initialize KINSOL object */ flag = KINInit(kinsolSolver->kinsolMemory, nlsKinsolResiduals, kinsolSolver->initialGuess); if (!checkFlag(flag, "KINInit")) return NULL; /* Create matrix object */ - kinsolSolver->J = SUNDenseMatrix(kinsolSolver->size, kinsolSolver->size); + kinsolSolver->J = SUNDenseMatrix(kinsolSolver->size, kinsolSolver->size, kinsolSolver->sunctx); + if (kinsolSolver->J == NULL) + { + logError("SUNDIALS_ERROR: SUNDenseMatrix() failed"); + return NULL; + } /* Create linear solver object */ - kinsolSolver->linSol = SUNLinSol_Dense(kinsolSolver->y, kinsolSolver->J); + kinsolSolver->linSol = SUNLinSol_Dense(kinsolSolver->y, kinsolSolver->J, kinsolSolver->sunctx); + if (kinsolSolver->linSol == NULL) + { + logError("SUNDIALS_ERROR: SUNLinSol_Dense() failed"); + return NULL; + } /* Set linear solver */ flag = KINSetLinearSolver(kinsolSolver->kinsolMemory, kinsolSolver->linSol, kinsolSolver->J); diff --git a/src/OMSimulatorLib/AlgLoop.h b/src/OMSimulatorLib/AlgLoop.h index 872fbf765..0e2a102ae 100644 --- a/src/OMSimulatorLib/AlgLoop.h +++ b/src/OMSimulatorLib/AlgLoop.h @@ -41,9 +41,11 @@ #include "OMSimulator/Types.h" #include "DirectedGraph.h" +#include /* SUNContext */ +#include /* SUNLogger */ #include #include -#include /* Default dense linear solver */ +#include /* Default dense linear solver */ namespace oms { @@ -75,6 +77,7 @@ namespace oms N_Vector fTmp; /* Vector used for tmp computations */ /* kinsol internal data */ + SUNContext sunctx; /* SUNDIALS simulation context */ void* kinsolMemory; void* user_data; int size; @@ -87,8 +90,8 @@ namespace oms /* member function */ static int nlsKinsolJac(N_Vector u, N_Vector fu, SUNMatrix J, void *user_data, N_Vector tmp1, N_Vector tmp2); static int nlsKinsolResiduals(N_Vector u, N_Vector fval, void *user_data); - static void sundialsErrorHandlerFunction(int error_code, const char *module, const char *function, char *msg, void *user_data); - static void sundialsInfoHandlerFunction(const char *module, const char *function, char *msg, void *user_data); + static void sundialsErrorHandlerFunction(int line, const char *func, const char *file, const char *msg, + SUNErrCode err_code, void *err_user_data, SUNContext sunctx); }; class AlgLoop diff --git a/src/OMSimulatorLib/CMakeLists.txt b/src/OMSimulatorLib/CMakeLists.txt index 3303b5d32..a177b7790 100644 --- a/src/OMSimulatorLib/CMakeLists.txt +++ b/src/OMSimulatorLib/CMakeLists.txt @@ -76,8 +76,8 @@ target_include_directories(OMSimulatorLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(OMSimulatorLib PUBLIC oms::public_includes - oms::3rd::kinsol - oms::3rd::cvode + oms::3rd::sundials::kinsol + oms::3rd::sundials::cvode oms::3rd::fmi4c oms::3rd::minizip oms::3rd::zlib @@ -107,8 +107,8 @@ target_include_directories(OMSimulatorLib_static PUBLIC ${CMAKE_CURRENT_SOURCE_D target_link_libraries(OMSimulatorLib_static PUBLIC oms::public_includes - oms::3rd::kinsol - oms::3rd::cvode + oms::3rd::sundials::kinsol + oms::3rd::sundials::cvode oms::3rd::fmi4c oms::3rd::minizip oms::3rd::zlib diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index 0fd686514..19f817035 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -63,7 +63,7 @@ namespace } } -int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data) +int oms::cvode_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC* system = (SystemSC*)user_data; oms_status_enu_t status; @@ -111,7 +111,7 @@ int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data) return 0; } -int oms::cvode_rhs_algebraic(realtype t, N_Vector y, N_Vector ydot, void* user_data) +int oms::cvode_rhs_algebraic(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC* system = (SystemSC*)user_data; @@ -123,7 +123,7 @@ int oms::cvode_rhs_algebraic(realtype t, N_Vector y, N_Vector ydot, void* user_d return 0; } -int oms::cvode_roots(realtype t, N_Vector y, realtype *gout, void *user_data) +int oms::cvode_roots(sunrealtype t, N_Vector y, sunrealtype *gout, void *user_data) { logDebug("cvode_roots at time " + std::to_string(t)); SystemSC* system = (SystemSC*)user_data; @@ -372,7 +372,21 @@ oms_status_enu_t oms::SystemSC::initialize() if (algebraic) n_states = 1; - solverData.cvode.y = N_VNew_Serial(static_cast(n_states)); + /* Create the SUNDIALS context every other SUNDIALS object is created with */ + if (SUNContext_Create(SUN_COMM_NULL, &solverData.cvode.sunctx) != SUN_SUCCESS) + logError("SUNDIALS_ERROR: SUNContext_Create() failed"); + + /* Mute SUNDIALS' own output, use OMSimulator's logger */ + { + SUNLogger logger = NULL; + if (SUNContext_GetLogger(solverData.cvode.sunctx, &logger) == SUN_SUCCESS && logger != NULL) + { + SUNLogger_SetErrorFilename(logger, ""); + SUNLogger_SetWarningFilename(logger, ""); + } + } + + solverData.cvode.y = N_VNew_Serial(static_cast(n_states), solverData.cvode.sunctx); if (!solverData.cvode.y) logError("SUNDIALS_ERROR: N_VNew_Serial() failed - returned NULL pointer"); if (algebraic) @@ -383,7 +397,7 @@ oms_status_enu_t oms::SystemSC::initialize() NV_Ith_S(solverData.cvode.y, k) = states[j][i]; //N_VPrint_Serial(solverData.cvode.y); - solverData.cvode.abstol = N_VNew_Serial(static_cast(n_states)); + solverData.cvode.abstol = N_VNew_Serial(static_cast(n_states), solverData.cvode.sunctx); if (!solverData.cvode.abstol) logError("SUNDIALS_ERROR: N_VNew_Serial() failed - returned NULL pointer"); if (algebraic) @@ -396,7 +410,7 @@ oms_status_enu_t oms::SystemSC::initialize() // Call CVodeCreate to create the solver memory and specify the // Backward Differentiation Formula and the use of a Newton iteration - solverData.cvode.mem = CVodeCreate(CV_BDF); + solverData.cvode.mem = CVodeCreate(CV_BDF, solverData.cvode.sunctx); if (!solverData.cvode.mem) logError("SUNDIALS_ERROR: CVodeCreate() failed - returned NULL pointer"); int flag = CVodeSetUserData(solverData.cvode.mem, (void*)this); @@ -417,13 +431,13 @@ oms_status_enu_t oms::SystemSC::initialize() if (flag < 0) logError("SUNDIALS_ERROR: CVodeSVtolerances() failed with flag = " + std::to_string(flag)); // Call N_VNew_Serial and SUNDenseMatrix to generate dense vector abd natrix for lin. solver module - solverData.cvode.liny = N_VNew_Serial(n_states); + solverData.cvode.liny = N_VNew_Serial(n_states, solverData.cvode.sunctx); if (solverData.cvode.liny == NULL) logError("SUNDIALS_ERROR: N_VNew_Serial() failed"); - solverData.cvode.J = SUNDenseMatrix(n_states, n_states); - if (solverData.cvode.J == NULL) logError("SUNDIALS_ERROR: N_VNew_Serial() failed"); + solverData.cvode.J = SUNDenseMatrix(n_states, n_states, solverData.cvode.sunctx); + if (solverData.cvode.J == NULL) logError("SUNDIALS_ERROR: SUNDenseMatrix() failed"); // Call SUNLinSol_Dense to creat linear solver object - solverData.cvode.linSol = SUNLinSol_Dense(solverData.cvode.liny, solverData.cvode.J); + solverData.cvode.linSol = SUNLinSol_Dense(solverData.cvode.liny, solverData.cvode.J, solverData.cvode.sunctx); if (solverData.cvode.linSol == NULL) logError("SUNDIALS_ERROR: SUNLinSol_Dense() failed"); // Call CVodeSetLinearSolver to set the dense linear solver */ @@ -498,6 +512,7 @@ oms_status_enu_t oms::SystemSC::terminate() N_VDestroy_Serial(solverData.cvode.y); N_VDestroy_Serial(solverData.cvode.abstol); CVodeFree(&(solverData.cvode.mem)); + SUNContext_Free(&(solverData.cvode.sunctx)); solverData.cvode.mem = NULL; } @@ -565,6 +580,7 @@ oms_status_enu_t oms::SystemSC::reset() N_VDestroy_Serial(solverData.cvode.y); N_VDestroy_Serial(solverData.cvode.abstol); CVodeFree(&(solverData.cvode.mem)); + SUNContext_Free(&(solverData.cvode.sunctx)); solverData.cvode.mem = nullptr; } diff --git a/src/OMSimulatorLib/SystemSC.h b/src/OMSimulatorLib/SystemSC.h index 019b02262..bedd441da 100644 --- a/src/OMSimulatorLib/SystemSC.h +++ b/src/OMSimulatorLib/SystemSC.h @@ -40,18 +40,20 @@ #include "System.h" #include "OMSimulator/Types.h" -#include /* prototypes for CVODE fcts., consts. */ -#include /* serial N_Vector types, fcts., macros */ -#include /* Default dense linear solver */ +#include /* SUNContext */ +#include /* SUNLogger */ +#include /* prototypes for CVODE fcts., consts. */ +#include /* serial N_Vector types, fcts., macros */ +#include /* Default dense linear solver */ namespace oms { class Model; class ComponentFMUME; class Component; - int cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data); - int cvode_rhs_algebraic(realtype t, N_Vector y, N_Vector ydot, void* user_data); - int cvode_roots(realtype t, N_Vector y, realtype *gout, void* user_data); + int cvode_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + int cvode_rhs_algebraic(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + int cvode_roots(sunrealtype t, N_Vector y, sunrealtype *gout, void* user_data); class SystemSC : public System { @@ -110,6 +112,7 @@ namespace oms struct SolverDataCVODE_t { + SUNContext sunctx; /* SUNDIALS simulation context */ void *mem; N_Vector y; SUNLinearSolver linSol; /* linear solver object */ @@ -124,9 +127,9 @@ namespace oms SolverDataCVODE_t cvode; } solverData; - friend int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data); - friend int oms::cvode_rhs_algebraic(realtype t, N_Vector y, N_Vector ydot, void* user_data); - friend int oms::cvode_roots(realtype t, N_Vector y, realtype *gout, void* user_data); + friend int oms::cvode_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + friend int oms::cvode_rhs_algebraic(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + friend int oms::cvode_roots(sunrealtype t, N_Vector y, sunrealtype *gout, void* user_data); }; } diff --git a/src/OMSimulatorLib/SystemSC3.cpp b/src/OMSimulatorLib/SystemSC3.cpp index 87c9fdf0c..9f119cdd1 100644 --- a/src/OMSimulatorLib/SystemSC3.cpp +++ b/src/OMSimulatorLib/SystemSC3.cpp @@ -47,7 +47,7 @@ #include #include -int oms::cvode_rhs3(realtype t, N_Vector y, N_Vector ydot, void* user_data) +int oms::cvode_rhs3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC3* system = (SystemSC3*)user_data; oms_status_enu_t status; @@ -94,7 +94,7 @@ int oms::cvode_rhs3(realtype t, N_Vector y, N_Vector ydot, void* user_data) return 0; } -int oms::cvode_rhs_algebraic3(realtype t, N_Vector y, N_Vector ydot, void* user_data) +int oms::cvode_rhs_algebraic3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC3* system = (SystemSC3*)user_data; @@ -106,7 +106,7 @@ int oms::cvode_rhs_algebraic3(realtype t, N_Vector y, N_Vector ydot, void* user_ return 0; } -int oms::cvode_roots3(realtype t, N_Vector y, realtype *gout, void *user_data) +int oms::cvode_roots3(sunrealtype t, N_Vector y, sunrealtype *gout, void *user_data) { logDebug("cvode_roots at time " + std::to_string(t)); SystemSC3* system = (SystemSC3*)user_data; @@ -350,7 +350,21 @@ oms_status_enu_t oms::SystemSC3::initialize() if (algebraic) n_states = 1; - solverData.cvode.y = N_VNew_Serial(static_cast(n_states)); + /* Create the SUNDIALS context every other SUNDIALS object is created with */ + if (SUNContext_Create(SUN_COMM_NULL, &solverData.cvode.sunctx) != SUN_SUCCESS) + logError("SUNDIALS_ERROR: SUNContext_Create() failed"); + + /* Mute SUNDIALS' own output, use OMSimulator's logger */ + { + SUNLogger logger = NULL; + if (SUNContext_GetLogger(solverData.cvode.sunctx, &logger) == SUN_SUCCESS && logger != NULL) + { + SUNLogger_SetErrorFilename(logger, ""); + SUNLogger_SetWarningFilename(logger, ""); + } + } + + solverData.cvode.y = N_VNew_Serial(static_cast(n_states), solverData.cvode.sunctx); if (!solverData.cvode.y) logError("SUNDIALS_ERROR: N_VNew_Serial() failed - returned NULL pointer"); if (algebraic) @@ -361,7 +375,7 @@ oms_status_enu_t oms::SystemSC3::initialize() NV_Ith_S(solverData.cvode.y, k) = states[j][i]; //N_VPrint_Serial(solverData.cvode.y); - solverData.cvode.abstol = N_VNew_Serial(static_cast(n_states)); + solverData.cvode.abstol = N_VNew_Serial(static_cast(n_states), solverData.cvode.sunctx); if (!solverData.cvode.abstol) logError("SUNDIALS_ERROR: N_VNew_Serial() failed - returned NULL pointer"); if (algebraic) @@ -374,7 +388,7 @@ oms_status_enu_t oms::SystemSC3::initialize() // Call CVodeCreate to create the solver memory and specify the // Backward Differentiation Formula and the use of a Newton iteration - solverData.cvode.mem = CVodeCreate(CV_BDF); + solverData.cvode.mem = CVodeCreate(CV_BDF, solverData.cvode.sunctx); if (!solverData.cvode.mem) logError("SUNDIALS_ERROR: CVodeCreate() failed - returned NULL pointer"); int flag = CVodeSetUserData(solverData.cvode.mem, (void*)this); @@ -395,13 +409,13 @@ oms_status_enu_t oms::SystemSC3::initialize() if (flag < 0) logError("SUNDIALS_ERROR: CVodeSVtolerances() failed with flag = " + std::to_string(flag)); // Call N_VNew_Serial and SUNDenseMatrix to generate dense vector abd natrix for lin. solver module - solverData.cvode.liny = N_VNew_Serial(n_states); + solverData.cvode.liny = N_VNew_Serial(n_states, solverData.cvode.sunctx); if (solverData.cvode.liny == NULL) logError("SUNDIALS_ERROR: N_VNew_Serial() failed"); - solverData.cvode.J = SUNDenseMatrix(n_states, n_states); - if (solverData.cvode.J == NULL) logError("SUNDIALS_ERROR: N_VNew_Serial() failed"); + solverData.cvode.J = SUNDenseMatrix(n_states, n_states, solverData.cvode.sunctx); + if (solverData.cvode.J == NULL) logError("SUNDIALS_ERROR: SUNDenseMatrix() failed"); // Call SUNLinSol_Dense to creat linear solver object - solverData.cvode.linSol = SUNLinSol_Dense(solverData.cvode.liny, solverData.cvode.J); + solverData.cvode.linSol = SUNLinSol_Dense(solverData.cvode.liny, solverData.cvode.J, solverData.cvode.sunctx); if (solverData.cvode.linSol == NULL) logError("SUNDIALS_ERROR: SUNLinSol_Dense() failed"); // Call CVodeSetLinearSolver to set the dense linear solver */ @@ -476,6 +490,7 @@ oms_status_enu_t oms::SystemSC3::terminate() N_VDestroy_Serial(solverData.cvode.y); N_VDestroy_Serial(solverData.cvode.abstol); CVodeFree(&(solverData.cvode.mem)); + SUNContext_Free(&(solverData.cvode.sunctx)); solverData.cvode.mem = NULL; } @@ -543,6 +558,7 @@ oms_status_enu_t oms::SystemSC3::reset() N_VDestroy_Serial(solverData.cvode.y); N_VDestroy_Serial(solverData.cvode.abstol); CVodeFree(&(solverData.cvode.mem)); + SUNContext_Free(&(solverData.cvode.sunctx)); solverData.cvode.mem = nullptr; } diff --git a/src/OMSimulatorLib/SystemSC3.h b/src/OMSimulatorLib/SystemSC3.h index b6488b098..31bad106b 100644 --- a/src/OMSimulatorLib/SystemSC3.h +++ b/src/OMSimulatorLib/SystemSC3.h @@ -40,18 +40,20 @@ #include "System.h" #include "OMSimulator/Types.h" -#include /* prototypes for CVODE fcts., consts. */ -#include /* serial N_Vector types, fcts., macros */ -#include /* Default dense linear solver */ +#include /* SUNContext */ +#include /* SUNLogger */ +#include /* prototypes for CVODE fcts., consts. */ +#include /* serial N_Vector types, fcts., macros */ +#include /* Default dense linear solver */ namespace oms { class Model; class ComponentFMU3ME; class Component; - int cvode_rhs3(realtype t, N_Vector y, N_Vector ydot, void* user_data); - int cvode_rhs_algebraic3(realtype t, N_Vector y, N_Vector ydot, void* user_data); - int cvode_roots3(realtype t, N_Vector y, realtype *gout, void* user_data); + int cvode_rhs3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + int cvode_rhs_algebraic3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + int cvode_roots3(sunrealtype t, N_Vector y, sunrealtype *gout, void* user_data); class SystemSC3 : public System { @@ -108,6 +110,7 @@ namespace oms struct SolverDataCVODE_t { + SUNContext sunctx; /* SUNDIALS simulation context */ void *mem; N_Vector y; SUNLinearSolver linSol; /* linear solver object */ @@ -122,9 +125,9 @@ namespace oms SolverDataCVODE_t cvode; } solverData; - friend int oms::cvode_rhs3(realtype t, N_Vector y, N_Vector ydot, void* user_data); - friend int oms::cvode_rhs_algebraic3(realtype t, N_Vector y, N_Vector ydot, void* user_data); - friend int oms::cvode_roots3(realtype t, N_Vector y, realtype *gout, void* user_data); + friend int oms::cvode_rhs3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + friend int oms::cvode_rhs_algebraic3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data); + friend int oms::cvode_roots3(sunrealtype t, N_Vector y, sunrealtype *gout, void* user_data); }; } From 8554296e68c3f893831885cd453cacece9bd9581 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:04:04 +0200 Subject: [PATCH 2/8] Also silence info and debug logging --- src/OMSimulatorLib/AlgLoop.cpp | 5 +++-- src/OMSimulatorLib/SystemSC.cpp | 2 ++ src/OMSimulatorLib/SystemSC3.cpp | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/OMSimulatorLib/AlgLoop.cpp b/src/OMSimulatorLib/AlgLoop.cpp index 1cf3116ae..7720fa820 100644 --- a/src/OMSimulatorLib/AlgLoop.cpp +++ b/src/OMSimulatorLib/AlgLoop.cpp @@ -293,14 +293,15 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons return NULL; } - /* Mute SUNDIALS' own logger: since SUNDIALS 7 package level messages go to - stderr/stdout by default, and we report solver failures ourselves. */ + /* Mute SUNDIALS' own output, use OMSimulator's logger */ { SUNLogger logger = NULL; if (SUNContext_GetLogger(kinsolSolver->sunctx, &logger) == SUN_SUCCESS && logger != NULL) { SUNLogger_SetErrorFilename(logger, ""); SUNLogger_SetWarningFilename(logger, ""); + SUNLogger_SetInfoFilename(logger, ""); + SUNLogger_SetDebugFilename(logger, ""); } } diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index 19f817035..221f45978 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -383,6 +383,8 @@ oms_status_enu_t oms::SystemSC::initialize() { SUNLogger_SetErrorFilename(logger, ""); SUNLogger_SetWarningFilename(logger, ""); + SUNLogger_SetInfoFilename(logger, ""); + SUNLogger_SetDebugFilename(logger, ""); } } diff --git a/src/OMSimulatorLib/SystemSC3.cpp b/src/OMSimulatorLib/SystemSC3.cpp index 9f119cdd1..cc59f4fbf 100644 --- a/src/OMSimulatorLib/SystemSC3.cpp +++ b/src/OMSimulatorLib/SystemSC3.cpp @@ -361,6 +361,8 @@ oms_status_enu_t oms::SystemSC3::initialize() { SUNLogger_SetErrorFilename(logger, ""); SUNLogger_SetWarningFilename(logger, ""); + SUNLogger_SetInfoFilename(logger, ""); + SUNLogger_SetDebugFilename(logger, ""); } } From dd0a7bc9b41a34fffaa2c9633fa5737b2642c68e Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Mon, 27 Jul 2026 18:08:22 +0200 Subject: [PATCH 3/8] Prevent CVODE too close error * Skip steps with |tout - tn| < 2*uround*max(|tn|,|tout|) * Go directly to tout --- src/OMSimulatorLib/SystemSC.cpp | 57 +++++++++++++++++++++++-------- src/OMSimulatorLib/SystemSC3.cpp | 58 ++++++++++++++++++++++++-------- 2 files changed, 87 insertions(+), 28 deletions(-) diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index 221f45978..bb053e3ca 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,18 @@ namespace } } +// Smallest interval CVODE is willing to integrate over. CVODE rejects any call +// with |tout - tn| < 2*uround*max(|tn|,|tout|) and returns CV_TOO_CLOSE. Up to +// SUNDIALS 5.4.0 that check was part of the initial step size heuristic (cvHin) +// and therefore skipped whenever CVodeSetInitStep() was used, which OMSimulator +// always does; since SUNDIALS 7.x it is checked unconditionally on the first +// step after CVodeInit()/CVodeReInit(). Use a slightly larger margin than CVODE +// so we never hand it an interval it refuses. +static double cvodeTooCloseTolerance(double t1, double t2) +{ + return 10.0 * std::numeric_limits::epsilon() * std::max(std::fabs(t1), std::fabs(t2)); +} + int oms::cvode_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC* system = (SystemSC*)user_data; @@ -873,24 +886,40 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() while (time < end_time) { - logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(end_time)); - for (size_t j=0, k=0; j < fmus.size(); ++j) - for (size_t i=0; i < nStates[j]; ++i, ++k) - NV_Ith_S(solverData.cvode.y, k) = states[j][i]; + const fmi2Real tout = std::min(tnext, end_time); - flag = CVode(solverData.cvode.mem, std::min(tnext, end_time), solverData.cvode.y, &time, CV_NORMAL); - - for (size_t i = 0, j=0; i < fmus.size(); ++i) + // An event that lands within a few ULPs of tout leaves an interval behind + // that is pure floating point noise. Snap to tout instead of asking CVODE + // to integrate it, which it would reject with CV_TOO_CLOSE. + if (tout - time <= cvodeTooCloseTolerance(time, tout)) { - if (0 == nStates[i]) - continue; + logDebug("CVode: skipping negligible interval " + std::to_string(time) + " -> " + std::to_string(tout)); + time = tout; + if (time != tnext) + break; // nothing left to integrate for this step + flag = CV_SUCCESS; // the time event at tnext is handled below + } + else + { + logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(tout)); + for (size_t j=0, k=0; j < fmus.size(); ++j) + for (size_t i=0; i < nStates[j]; ++i, ++k) + NV_Ith_S(solverData.cvode.y, k) = states[j][i]; - for (size_t k = 0; k < nStates[i]; k++, j++) - states[i][k] = NV_Ith_S(solverData.cvode.y, j); + flag = CVode(solverData.cvode.mem, tout, solverData.cvode.y, &time, CV_NORMAL); - // set states - status = fmus[i]->setContinuousStates(states[i]); - if (oms_status_ok != status) return status; + for (size_t i = 0, j=0; i < fmus.size(); ++i) + { + if (0 == nStates[i]) + continue; + + for (size_t k = 0; k < nStates[i]; k++, j++) + states[i][k] = NV_Ith_S(solverData.cvode.y, j); + + // set states + status = fmus[i]->setContinuousStates(states[i]); + if (oms_status_ok != status) return status; + } } if (flag == CV_ROOT_RETURN || time == tnext) diff --git a/src/OMSimulatorLib/SystemSC3.cpp b/src/OMSimulatorLib/SystemSC3.cpp index cc59f4fbf..b2144aa67 100644 --- a/src/OMSimulatorLib/SystemSC3.cpp +++ b/src/OMSimulatorLib/SystemSC3.cpp @@ -43,10 +43,24 @@ #include "ssd/Tags.h" #include +#include #include +#include #include #include +// Smallest interval CVODE is willing to integrate over. CVODE rejects any call +// with |tout - tn| < 2*uround*max(|tn|,|tout|) and returns CV_TOO_CLOSE. Up to +// SUNDIALS 5.4.0 that check was part of the initial step size heuristic (cvHin) +// and therefore skipped whenever CVodeSetInitStep() was used, which OMSimulator +// always does; since SUNDIALS 7.x it is checked unconditionally on the first +// step after CVodeInit()/CVodeReInit(). Use a slightly larger margin than CVODE +// so we never hand it an interval it refuses. +static double cvodeTooCloseTolerance(double t1, double t2) +{ + return 10.0 * std::numeric_limits::epsilon() * std::max(std::fabs(t1), std::fabs(t2)); +} + int oms::cvode_rhs3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC3* system = (SystemSC3*)user_data; @@ -847,24 +861,40 @@ oms_status_enu_t oms::SystemSC3::doStepCVODE() while (time < end_time) { - logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(end_time)); - for (size_t j=0, k=0; j < fmus.size(); ++j) - for (size_t i=0; i < nStates[j]; ++i, ++k) - NV_Ith_S(solverData.cvode.y, k) = states[j][i]; + const fmi3Float64 tout = std::min(tnext, end_time); - flag = CVode(solverData.cvode.mem, std::min(tnext, end_time), solverData.cvode.y, &time, CV_NORMAL); - - for (size_t i = 0, j=0; i < fmus.size(); ++i) + // An event that lands within a few ULPs of tout leaves an interval behind + // that is pure floating point noise. Snap to tout instead of asking CVODE + // to integrate it, which it would reject with CV_TOO_CLOSE. + if (tout - time <= cvodeTooCloseTolerance(time, tout)) { - if (0 == nStates[i]) - continue; + logDebug("CVode: skipping negligible interval " + std::to_string(time) + " -> " + std::to_string(tout)); + time = tout; + if (time != tnext) + break; // nothing left to integrate for this step + flag = CV_SUCCESS; // the time event at tnext is handled below + } + else + { + logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(tout)); + for (size_t j=0, k=0; j < fmus.size(); ++j) + for (size_t i=0; i < nStates[j]; ++i, ++k) + NV_Ith_S(solverData.cvode.y, k) = states[j][i]; - for (size_t k = 0; k < nStates[i]; k++, j++) - states[i][k] = NV_Ith_S(solverData.cvode.y, j); + flag = CVode(solverData.cvode.mem, tout, solverData.cvode.y, &time, CV_NORMAL); - // set states - status = fmus[i]->setContinuousStates(states[i]); - if (oms_status_ok != status) return status; + for (size_t i = 0, j=0; i < fmus.size(); ++i) + { + if (0 == nStates[i]) + continue; + + for (size_t k = 0; k < nStates[i]; k++, j++) + states[i][k] = NV_Ith_S(solverData.cvode.y, j); + + // set states + status = fmus[i]->setContinuousStates(states[i]); + if (oms_status_ok != status) return status; + } } if (flag == CV_ROOT_RETURN || time == tnext) From 7a4ee3b5abbd4248b9176b43ae466812215c4ec9 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Mon, 27 Jul 2026 18:15:28 +0200 Subject: [PATCH 4/8] Try something naive --- src/OMSimulatorLib/SystemSC.cpp | 4 +--- src/OMSimulatorLib/SystemSC3.cpp | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index bb053e3ca..39c249009 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -895,9 +895,7 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() { logDebug("CVode: skipping negligible interval " + std::to_string(time) + " -> " + std::to_string(tout)); time = tout; - if (time != tnext) - break; // nothing left to integrate for this step - flag = CV_SUCCESS; // the time event at tnext is handled below + flag = CV_SUCCESS; // handled below, either as a time event or as a completed step } else { diff --git a/src/OMSimulatorLib/SystemSC3.cpp b/src/OMSimulatorLib/SystemSC3.cpp index b2144aa67..ae692fe0d 100644 --- a/src/OMSimulatorLib/SystemSC3.cpp +++ b/src/OMSimulatorLib/SystemSC3.cpp @@ -870,9 +870,7 @@ oms_status_enu_t oms::SystemSC3::doStepCVODE() { logDebug("CVode: skipping negligible interval " + std::to_string(time) + " -> " + std::to_string(tout)); time = tout; - if (time != tnext) - break; // nothing left to integrate for this step - flag = CV_SUCCESS; // the time event at tnext is handled below + flag = CV_SUCCESS; // handled below, either as a time event or as a completed step } else { From 44c54efa0c9ba4bf9b54af780eedf8c49ec5ae6a Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:10:21 +0200 Subject: [PATCH 5/8] Remove cvodeTooCloseTolerance again --- src/OMSimulatorLib/SystemSC.cpp | 54 +++++++++------------------------ 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index 39c249009..6ae2093a1 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -64,18 +64,6 @@ namespace } } -// Smallest interval CVODE is willing to integrate over. CVODE rejects any call -// with |tout - tn| < 2*uround*max(|tn|,|tout|) and returns CV_TOO_CLOSE. Up to -// SUNDIALS 5.4.0 that check was part of the initial step size heuristic (cvHin) -// and therefore skipped whenever CVodeSetInitStep() was used, which OMSimulator -// always does; since SUNDIALS 7.x it is checked unconditionally on the first -// step after CVodeInit()/CVodeReInit(). Use a slightly larger margin than CVODE -// so we never hand it an interval it refuses. -static double cvodeTooCloseTolerance(double t1, double t2) -{ - return 10.0 * std::numeric_limits::epsilon() * std::max(std::fabs(t1), std::fabs(t2)); -} - int oms::cvode_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC* system = (SystemSC*)user_data; @@ -886,38 +874,24 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() while (time < end_time) { - const fmi2Real tout = std::min(tnext, end_time); - - // An event that lands within a few ULPs of tout leaves an interval behind - // that is pure floating point noise. Snap to tout instead of asking CVODE - // to integrate it, which it would reject with CV_TOO_CLOSE. - if (tout - time <= cvodeTooCloseTolerance(time, tout)) - { - logDebug("CVode: skipping negligible interval " + std::to_string(time) + " -> " + std::to_string(tout)); - time = tout; - flag = CV_SUCCESS; // handled below, either as a time event or as a completed step - } - else - { - logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(tout)); - for (size_t j=0, k=0; j < fmus.size(); ++j) - for (size_t i=0; i < nStates[j]; ++i, ++k) - NV_Ith_S(solverData.cvode.y, k) = states[j][i]; + logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(end_time)); + for (size_t j=0, k=0; j < fmus.size(); ++j) + for (size_t i=0; i < nStates[j]; ++i, ++k) + NV_Ith_S(solverData.cvode.y, k) = states[j][i]; - flag = CVode(solverData.cvode.mem, tout, solverData.cvode.y, &time, CV_NORMAL); + flag = CVode(solverData.cvode.mem, std::min(tnext, end_time), solverData.cvode.y, &time, CV_NORMAL); - for (size_t i = 0, j=0; i < fmus.size(); ++i) - { - if (0 == nStates[i]) - continue; + for (size_t i = 0, j=0; i < fmus.size(); ++i) + { + if (0 == nStates[i]) + continue; - for (size_t k = 0; k < nStates[i]; k++, j++) - states[i][k] = NV_Ith_S(solverData.cvode.y, j); + for (size_t k = 0; k < nStates[i]; k++, j++) + states[i][k] = NV_Ith_S(solverData.cvode.y, j); - // set states - status = fmus[i]->setContinuousStates(states[i]); - if (oms_status_ok != status) return status; - } + // set states + status = fmus[i]->setContinuousStates(states[i]); + if (oms_status_ok != status) return status; } if (flag == CV_ROOT_RETURN || time == tnext) From a3e61ee33b879697fae69e0dd08a4482eeb8e80e Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:24:01 +0200 Subject: [PATCH 6/8] Update doc string for sundialsErrorHandlerFunction --- src/OMSimulatorLib/AlgLoop.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/OMSimulatorLib/AlgLoop.cpp b/src/OMSimulatorLib/AlgLoop.cpp index 7720fa820..6f11a6070 100644 --- a/src/OMSimulatorLib/AlgLoop.cpp +++ b/src/OMSimulatorLib/AlgLoop.cpp @@ -62,13 +62,26 @@ inline bool checkFlag(int flag, std::string functionName) } /** - * @brief Error handler function given to KINSOL. + * @brief Error handler function of type SUNErrHandlerFn. * - * @param errorCode Error code from KINSOL - * @param module Name of the module reporting the error. - * @param function Name of the function in which the error occurred. - * @param msg Error Message. - * @param user_data Pointer to user data. Unused. + * Pushed onto the error handler stack of the SUNDIALS context, so it is called + * for errors reported by KINSOL and by any other SUNDIALS module created with + * that context. + * + * @param line Line number in the SUNDIALS source file where the + * error occurred. + * @param func_name Name of the SUNDIALS function in which the error + * occurred. + * @param file Name of the SUNDIALS source file where the error + * occurred. + * @param msg Error message. Can be NULL, in which case the message + * belonging to `err_code` is used. + * @param err_code Error code. Either a SUNErrCode or a package level + * code, e.g. one of the KIN_* constants. + * @param err_user_data Pointer to user data given to + * SUNContext_PushErrHandler(), a KINSOL_USER_DATA + * pointer. Can be NULL. + * @param sunctx SUNDIALS context reporting the error. Unused. */ void oms::KinsolSolver::sundialsErrorHandlerFunction(int line, const char *func_name, const char *file, const char *msg, @@ -77,7 +90,7 @@ void oms::KinsolSolver::sundialsErrorHandlerFunction(int line, const char *func_ { KINSOL_USER_DATA* kinsolUserData; std::string systNum = "unknown"; - std::string mod = std::string(file) + ":" + std::to_string(line); + std::string file_location = std::string(file) + ":" + std::to_string(line); std::string func = func_name; if (err_user_data != NULL) @@ -89,7 +102,7 @@ void oms::KinsolSolver::sundialsErrorHandlerFunction(int line, const char *func_ /* Package level codes (KIN_* and friends) are not SUNErrCodes, so SUNGetErrMsg() only makes sense when SUNDIALS did not supply a message - same rule as SUNDIALS' own default handler. */ - logError("SUNDIALS_ERROR: [system] " + systNum + " [at] " + mod + " | [function] " + func + logError("SUNDIALS_ERROR: [system] " + systNum + " [at] " + file_location + " | [function] " + func + " | [error_code] " + std::to_string(err_code) + "\n" + std::string(msg ? msg : SUNGetErrMsg(err_code))); } From 2ee177deeb49d9e5d6d36249df6398257cda0ccd Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:31:21 +0200 Subject: [PATCH 7/8] Remove cvodeTooCloseTolerance from FMI 3.0 case --- src/OMSimulatorLib/SystemSC.cpp | 8 ++--- src/OMSimulatorLib/SystemSC3.cpp | 54 +++++++++----------------------- 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/src/OMSimulatorLib/SystemSC.cpp b/src/OMSimulatorLib/SystemSC.cpp index 6ae2093a1..37104408a 100644 --- a/src/OMSimulatorLib/SystemSC.cpp +++ b/src/OMSimulatorLib/SystemSC.cpp @@ -44,9 +44,7 @@ #include "ssd/Tags.h" #include -#include #include -#include #include #include @@ -874,12 +872,14 @@ oms_status_enu_t oms::SystemSC::doStepCVODE() while (time < end_time) { - logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(end_time)); + const fmi3Float64 tout = std::min(tnext, end_time); + + logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(tout)); for (size_t j=0, k=0; j < fmus.size(); ++j) for (size_t i=0; i < nStates[j]; ++i, ++k) NV_Ith_S(solverData.cvode.y, k) = states[j][i]; - flag = CVode(solverData.cvode.mem, std::min(tnext, end_time), solverData.cvode.y, &time, CV_NORMAL); + flag = CVode(solverData.cvode.mem, tout, solverData.cvode.y, &time, CV_NORMAL); for (size_t i = 0, j=0; i < fmus.size(); ++i) { diff --git a/src/OMSimulatorLib/SystemSC3.cpp b/src/OMSimulatorLib/SystemSC3.cpp index ae692fe0d..1794d6022 100644 --- a/src/OMSimulatorLib/SystemSC3.cpp +++ b/src/OMSimulatorLib/SystemSC3.cpp @@ -43,24 +43,10 @@ #include "ssd/Tags.h" #include -#include #include -#include #include #include -// Smallest interval CVODE is willing to integrate over. CVODE rejects any call -// with |tout - tn| < 2*uround*max(|tn|,|tout|) and returns CV_TOO_CLOSE. Up to -// SUNDIALS 5.4.0 that check was part of the initial step size heuristic (cvHin) -// and therefore skipped whenever CVodeSetInitStep() was used, which OMSimulator -// always does; since SUNDIALS 7.x it is checked unconditionally on the first -// step after CVodeInit()/CVodeReInit(). Use a slightly larger margin than CVODE -// so we never hand it an interval it refuses. -static double cvodeTooCloseTolerance(double t1, double t2) -{ - return 10.0 * std::numeric_limits::epsilon() * std::max(std::fabs(t1), std::fabs(t2)); -} - int oms::cvode_rhs3(sunrealtype t, N_Vector y, N_Vector ydot, void* user_data) { SystemSC3* system = (SystemSC3*)user_data; @@ -863,36 +849,24 @@ oms_status_enu_t oms::SystemSC3::doStepCVODE() { const fmi3Float64 tout = std::min(tnext, end_time); - // An event that lands within a few ULPs of tout leaves an interval behind - // that is pure floating point noise. Snap to tout instead of asking CVODE - // to integrate it, which it would reject with CV_TOO_CLOSE. - if (tout - time <= cvodeTooCloseTolerance(time, tout)) - { - logDebug("CVode: skipping negligible interval " + std::to_string(time) + " -> " + std::to_string(tout)); - time = tout; - flag = CV_SUCCESS; // handled below, either as a time event or as a completed step - } - else - { - logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(tout)); - for (size_t j=0, k=0; j < fmus.size(); ++j) - for (size_t i=0; i < nStates[j]; ++i, ++k) - NV_Ith_S(solverData.cvode.y, k) = states[j][i]; + logDebug("CVode: " + std::to_string(time) + " -> " + std::to_string(tout)); + for (size_t j=0, k=0; j < fmus.size(); ++j) + for (size_t i=0; i < nStates[j]; ++i, ++k) + NV_Ith_S(solverData.cvode.y, k) = states[j][i]; - flag = CVode(solverData.cvode.mem, tout, solverData.cvode.y, &time, CV_NORMAL); + flag = CVode(solverData.cvode.mem, tout, solverData.cvode.y, &time, CV_NORMAL); - for (size_t i = 0, j=0; i < fmus.size(); ++i) - { - if (0 == nStates[i]) - continue; + for (size_t i = 0, j=0; i < fmus.size(); ++i) + { + if (0 == nStates[i]) + continue; - for (size_t k = 0; k < nStates[i]; k++, j++) - states[i][k] = NV_Ith_S(solverData.cvode.y, j); + for (size_t k = 0; k < nStates[i]; k++, j++) + states[i][k] = NV_Ith_S(solverData.cvode.y, j); - // set states - status = fmus[i]->setContinuousStates(states[i]); - if (oms_status_ok != status) return status; - } + // set states + status = fmus[i]->setContinuousStates(states[i]); + if (oms_status_ok != status) return status; } if (flag == CV_ROOT_RETURN || time == tnext) From 55b25d714140f7dcc343e51bc3aeff3e364e4348 Mon Sep 17 00:00:00 2001 From: AnHeuermann <38031952+AnHeuermann@users.noreply.github.com> Date: Fri, 31 Jul 2026 13:11:15 +0200 Subject: [PATCH 8/8] Update 3rdParty again --- 3rdParty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdParty b/3rdParty index bb4be742c..714704485 160000 --- a/3rdParty +++ b/3rdParty @@ -1 +1 @@ -Subproject commit bb4be742c171721c9efe19979b1b7236ac9e0b69 +Subproject commit 714704485cca375a76d3e5d27909d9efe0e1cb63