diff --git a/doc/interface/solver.rst b/doc/interface/solver.rst index d700f50cb..5254380ec 100644 --- a/doc/interface/solver.rst +++ b/doc/interface/solver.rst @@ -58,6 +58,15 @@ Group /input/solver **Type:** int **Range:** :math:`\{ 0, \dots, 7\}` **Length:** 1 ============= =================================== ============= +``TIMEOUT`` + + Timeout in seconds. Simulation is aborted if wall clock time exceeds the provided value. + Values less than or equal :math:`0` are ignored (i.e., no timeout is applied). + + ================ ====================== ============= + **Type:** double **Range:** :math:`> 0` **Length:** 1 + ================ ====================== ============= + .. _FFSolverTime: Group /solver/time_integrator diff --git a/include/cadet/Notification.hpp b/include/cadet/Notification.hpp index 1782031b2..835dd8f1f 100644 --- a/include/cadet/Notification.hpp +++ b/include/cadet/Notification.hpp @@ -76,6 +76,17 @@ class CADET_API INotificationCallback * @return @c true if time integrator should continue, otherwise @c false */ virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) = 0; + + /** + * @brief Called when the time integrator performs a linear solve (e.g., in a Newton iteration) + * + * @param[in] section Index of the current time section + * @param[in] time Current process time + * @param[in] state Current state vector + * @param[in] stateDot Current time derivative of the state vector + * @return @c true if time integrator should continue, otherwise @c false + */ + virtual bool timeIntegrationLinearSolve(unsigned int section, double time, double const* state, double const* stateDot) = 0; }; } // namespace cadet diff --git a/include/cadet/Simulator.hpp b/include/cadet/Simulator.hpp index 94ccbe216..53b3b6615 100644 --- a/include/cadet/Simulator.hpp +++ b/include/cadet/Simulator.hpp @@ -723,6 +723,7 @@ class CADET_API ISimulator /** * @brief Sets the receiver for notifications + * @details The callback is not owned by the simulator. * @param[in] nc Object to receive notifications or @c nullptr to disable notifications */ virtual void setNotificationCallback(INotificationCallback* nc) CADET_NOEXCEPT = 0; diff --git a/include/cadet/cadet.h b/include/cadet/cadet.h index 292a73ca1..f3173442b 100644 --- a/include/cadet/cadet.h +++ b/include/cadet/cadet.h @@ -164,6 +164,10 @@ extern "C" */ enum cdtErrors { + /** + * @brief Simulator not initialized + */ + cdtSimulatorNotInitialized = -4, /** * @brief Requested data has not been stored / is not available */ @@ -401,6 +405,7 @@ extern "C" * @details Before this function is called, a simulation has to be run successfully. * @param [in] drv Driver handle * @param [out] nUnits Number of unit operations + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getNumUnitOp)(cdtDriver* drv, int* nUnits); @@ -410,6 +415,7 @@ extern "C" * @param [in] drv Driver handle * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] nParTypes Number of particle types + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getNumParTypes)(cdtDriver* drv, int unitOpId, int* nParTypes); @@ -418,6 +424,7 @@ extern "C" * @details Before this function is called, a simulation has to be run successfully. * @param [in] drv Driver handle * @param [out] nSens Number of parameter sensitivities + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getNumSensitivities)(cdtDriver* drv, int* nSens); @@ -432,6 +439,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionInlet)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -446,6 +454,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionOutlet)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -462,6 +471,7 @@ extern "C" * @param [out] nRadialCells Number of radial cells * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionBulk)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nComp, bool* keepAxialSingletonDimension); @@ -481,6 +491,7 @@ extern "C" * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionParticle)(cdtDriver* drv, int unitOpId, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nComp, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); /** @@ -499,6 +510,7 @@ extern "C" * @param [out] nBound Number of bound states * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionSolid)(cdtDriver* drv, int unitOpId, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nBound, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -516,6 +528,7 @@ extern "C" * @param [out] nParticleTypes Number of particle types * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionFlux)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParticleTypes, int* nComp, bool* keepAxialSingletonDimension); @@ -527,6 +540,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] time Time array pointer * @param [out] data Data array pointer + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionVolume)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime); @@ -541,6 +555,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeInlet)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -555,6 +570,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeOutlet)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -571,6 +587,7 @@ extern "C" * @param [out] nRadialCells Number of radial cells * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeBulk)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nComp, bool* keepAxialSingletonDimension); @@ -590,6 +607,7 @@ extern "C" * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeParticle)(cdtDriver* drv, int unitOpId, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nComp, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -609,6 +627,7 @@ extern "C" * @param [out] nBound Number of bound states * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeSolid)(cdtDriver* drv, int unitOpId, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nBound, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -626,6 +645,7 @@ extern "C" * @param [out] nParticleTypes Number of particle types * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeFlux)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParticleTypes, int* nComp, bool* keepAxialSingletonDimension); @@ -637,6 +657,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] time Time array pointer * @param [out] data Data array pointer + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionDerivativeVolume)(cdtDriver* drv, int unitOpId, double const** time, double const** data, int* nTime); @@ -651,6 +672,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityInlet)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -666,6 +688,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityOutlet)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -683,6 +706,7 @@ extern "C" * @param [out] nRadialCells Number of radial cells * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityBulk)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nComp, bool* keepAxialSingletonDimension); @@ -703,6 +727,7 @@ extern "C" * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityParticle)(cdtDriver* drv, int unitOpId, int sensIdx, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nComp, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -723,6 +748,7 @@ extern "C" * @param [out] nBound Number of bound states * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivitySolid)(cdtDriver* drv, int unitOpId, int sensIdx, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nBound, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -741,6 +767,7 @@ extern "C" * @param [out] nParticleTypes Number of particle types * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityFlux)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParticleTypes, int* nComp, bool* keepAxialSingletonDimension); @@ -753,6 +780,7 @@ extern "C" * @param [in] sensIdx Sensitivity ID * @param [out] time Time array pointer * @param [out] data Data array pointer + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityVolume)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime); @@ -768,6 +796,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeInlet)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -783,6 +812,7 @@ extern "C" * @param [out] nTime Number of time points * @param [out] nPort Number of ports * @param [out] nComp Number of components + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeOutlet)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nPort, int* nComp); @@ -800,6 +830,7 @@ extern "C" * @param [out] nRadialCells Number of radial cells * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeBulk)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nComp, bool* keepAxialSingletonDimension); @@ -820,6 +851,7 @@ extern "C" * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeParticle)(cdtDriver* drv, int unitOpId, int sensIdx, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nComp, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -840,6 +872,7 @@ extern "C" * @param [out] nBound Number of bound states * @param [out] keepAxialSingletonDimension Keep axial singleton dimension * @param [out] keepParticleSingletonDimension Keep particle singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeSolid)(cdtDriver* drv, int unitOpId, int sensIdx, int parType, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParShells, int* nBound, bool* keepAxialSingletonDimension, bool* keepParticleSingletonDimension); @@ -858,6 +891,7 @@ extern "C" * @param [out] nParticleTypes Number of particle types * @param [out] nComp Number of components * @param [out] keepAxialSingletonDimension Keep axial singleton dimension + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeFlux)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime, int* nAxialCells, int* nRadialCells, int* nParticleTypes, int* nComp, bool* keepAxialSingletonDimension); @@ -871,6 +905,7 @@ extern "C" * @param [out] time Time array pointer * @param [out] data Data array pointer * @param [out] nTime Number of time points + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSensitivityDerivativeVolume)(cdtDriver* drv, int unitOpId, int sensIdx, double const** time, double const** data, int* nTime); @@ -880,6 +915,7 @@ extern "C" * @param [in] drv Driver handle * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastState)(cdtDriver* drv, double const** state, int* nStates); @@ -889,6 +925,7 @@ extern "C" * @param [in] drv Driver handle * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastStateTimeDerivative)(cdtDriver* drv, double const** state, int* nStates); @@ -899,6 +936,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastUnitState)(cdtDriver* drv, int unitOpId, double const** state, int* nStates); @@ -909,6 +947,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastUnitStateTimeDerivative)(cdtDriver* drv, int unitOpId, double const** state, int* nStates); @@ -919,6 +958,7 @@ extern "C" * @param [in] sensIdx Sensitivity ID * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastSensitivityState)(cdtDriver* drv, int sensIdx, double const** state, int* nStates); @@ -929,6 +969,7 @@ extern "C" * @param [in] sensIdx Sensitivity ID * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastSensitivityStateTimeDerivative)(cdtDriver* drv, int sensIdx, double const** state, int* nStates); @@ -940,6 +981,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastSensitivityUnitState)(cdtDriver* drv, int sensIdx, int unitOpId, double const** state, int* nStates); @@ -951,6 +993,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] state State vector pointer * @param [out] nStates Number of entries in the state vector + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getLastSensitivityUnitStateTimeDerivative)(cdtDriver* drv, int sensIdx, int unitOpId, double const** state, int* nStates); @@ -962,6 +1005,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] data Data array pointer * @param [out] nCoords Number of coordinates + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getPrimaryCoordinates)(cdtDriver* drv, int unitOpId, double const** data, int* nCoords); @@ -973,6 +1017,7 @@ extern "C" * @param [in] unitOpId ID of the unit operation whose solution is returned * @param [out] data Data array pointer * @param [out] nCoords Number of coordinates + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSecondaryCoordinates)(cdtDriver* drv, int unitOpId, double const** data, int* nCoords); @@ -985,6 +1030,7 @@ extern "C" * @param [in] parType Particle type index * @param [out] data Data array pointer * @param [out] nCoords Number of coordinates + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getParticleCoordinates)(cdtDriver* drv, int unitOpId, int parType, double const** data, int* nCoords); @@ -995,6 +1041,7 @@ extern "C" * @param [in] drv Driver handle * @param [out] time Time array pointer * @param [out] nTime Number of time points + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getSolutionTimes)(cdtDriver* drv, double const** time, int* nTime); @@ -1003,9 +1050,20 @@ extern "C" * @details Before this function is called, a simulation has to be run successfully. * @param [in] drv Driver handle * @param [out] timeSim Simulation run time pointer + * @return @c cdtOK on success, a negative value indicating the error otherwise */ cdtResult (*getTimeSim)(cdtDriver* drv, double* timeSim); + /** + * @brief Set a timeout on the current simulator + * @details The timeout persists for the full lifetime of the simulator. + * @param [in] drv Driver handle + * @param [in] timeoutSec Timeout for a time integration process in seconds. A value + * smaller or equal to @c 0.0 means no timeout (default). + * @return @c cdtOK on success, a negative value indicating the error otherwise + */ + cdtResult (*setTimeout)(cdtDriver* drv, double timeoutSec); + } cdtAPIv010000; /** diff --git a/include/common/Driver.hpp b/include/common/Driver.hpp index 0f83eea26..3eadbd053 100644 --- a/include/common/Driver.hpp +++ b/include/common/Driver.hpp @@ -26,6 +26,8 @@ #include "cadet/cadet.hpp" #include "common/SolutionRecorderImpl.hpp" +#include "common/MultiCallback.hpp" +#include "common/TimeoutCallback.hpp" namespace cadet { @@ -182,6 +184,7 @@ class Driver Driver() : _sim(nullptr), _builder(nullptr), _storage(nullptr), _writeLastState(false), _writeLastStateSens(false) { _builder = cadetCreateModelBuilder(); + _callbacks.addCallback(&_timeout); } ~Driver() CADET_NOEXCEPT @@ -236,16 +239,27 @@ class Driver cadetDestroySimulator(_sim); _sim = cadetCreateSimulator(); + _sim->setNotificationCallback(&_callbacks); // Configure main solver parameters pp.pushScope("solver"); _sim->configure(pp); - + // Configure section times std::vector secTimes; std::vector secCont; extractSectionTimes(pp, secTimes, secCont); + // Set timeout if provided, otherwise keep previously set timeout + if (pp.exists("TIMEOUT")) + { + const double timeoutSec = pp.getDouble("TIMEOUT"); + if (timeoutSec > 0.0) + _timeout.setTimeout(timeoutSec); + else + _timeout.setTimeout(-1.0); + } + pp.popScope(); // solver scope pp.pushScope("model"); @@ -735,10 +749,20 @@ class Driver inline cadet::InternalStorageSystemRecorder* solution() CADET_NOEXCEPT { return _storage; } inline cadet::InternalStorageSystemRecorder const* solution() const CADET_NOEXCEPT { return _storage; } + void clearCallbacks() CADET_NOEXCEPT { + _callbacks.clear(); + _callbacks.addCallback(&_timeout); + } + + void addCallback(cadet::INotificationCallback* cb) { _callbacks.addCallback(cb); } + + void setTimeout(double seconds) { _timeout.setTimeout(seconds); } protected: cadet::ISimulator* _sim; //!< Simulator owned by this driver cadet::IModelBuilder* _builder; //!< Model builder owned by this driver cadet::InternalStorageSystemRecorder* _storage; //!< Storage for results + cadet::MultiCallback _callbacks; //!< Callbacks considered during time integration + cadet::TimeoutCallback _timeout; //!< Timeout for simulations bool _writeLastState; std::vector _writeLastStateUnitId; diff --git a/include/common/MultiCallback.hpp b/include/common/MultiCallback.hpp new file mode 100644 index 000000000..9518b5068 --- /dev/null +++ b/include/common/MultiCallback.hpp @@ -0,0 +1,88 @@ +// ============================================================================= +// CADET +// +// Copyright © The CADET Authors +// Please see the CONTRIBUTORS.md file. +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the GNU Public License v3.0 (or, at +// your option, any later version) which accompanies this distribution, and +// is available at http://www.gnu.org/licenses/gpl.html +// ============================================================================= + +/** + * @file + * Provides a callback that forwards to multiple other callbacks. + */ + +#ifndef CADET_MULTI_CALLBACK_HPP_ +#define CADET_MULTI_CALLBACK_HPP_ + +#include +#include "cadet/Notification.hpp" + +namespace cadet +{ + + class MultiCallback : public cadet::INotificationCallback + { + public: + MultiCallback() { } + virtual ~MultiCallback() CADET_NOEXCEPT { } + + virtual void timeIntegrationStart() + { + for (auto callback : _callbacks) + callback->timeIntegrationStart(); + } + + virtual void timeIntegrationEnd() + { + for (auto callback : _callbacks) + callback->timeIntegrationEnd(); + } + + virtual void timeIntegrationError(char const* message, unsigned int section, double time, double progress) + { + for (auto callback : _callbacks) + callback->timeIntegrationError(message, section, time, progress); + } + + virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, double progress) + { + bool shouldContinue = true; + for (auto callback : _callbacks) + shouldContinue &= callback->timeIntegrationSection(section, time, state, stateDot, progress); + + return shouldContinue; + } + + virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) + { + bool shouldContinue = true; + for (auto callback : _callbacks) + shouldContinue &= callback->timeIntegrationStep(section, time, state, stateDot, progress); + + return shouldContinue; + } + + virtual bool timeIntegrationLinearSolve(unsigned int section, double time, double const* state, double const* stateDot) + { + bool shouldContinue = true; + for (auto callback : _callbacks) + shouldContinue &= callback->timeIntegrationLinearSolve(section, time, state, stateDot); + + return shouldContinue; + } + + std::vector const& callbacks() const CADET_NOEXCEPT { return _callbacks; } + std::vector& callbacks() CADET_NOEXCEPT { return _callbacks; } + + void addCallback(cadet::INotificationCallback* callback) { _callbacks.push_back(callback); } + void clear() { _callbacks.clear(); } + protected: + std::vector _callbacks; + }; +} + +#endif // CADET_MULTI_CALLBACK_HPP_ diff --git a/include/common/TimeoutCallback.hpp b/include/common/TimeoutCallback.hpp new file mode 100644 index 000000000..fc48f84e7 --- /dev/null +++ b/include/common/TimeoutCallback.hpp @@ -0,0 +1,79 @@ +// ============================================================================= +// CADET +// +// Copyright © The CADET Authors +// Please see the CONTRIBUTORS.md file. +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the GNU Public License v3.0 (or, at +// your option, any later version) which accompanies this distribution, and +// is available at http://www.gnu.org/licenses/gpl.html +// ============================================================================= + +/** + * @file + * Provides a callback that terminates time integration when a given amount of + * time has been exceeded. + */ + +#ifndef CADET_TIMEOUT_CALLBACK_HPP_ +#define CADET_TIMEOUT_CALLBACK_HPP_ + +#include "common/Timer.hpp" +#include "cadet/Notification.hpp" + +namespace cadet +{ + + class TimeoutCallback : public cadet::INotificationCallback + { + public: + TimeoutCallback() : _timeout(-1.0) { } + virtual ~TimeoutCallback() CADET_NOEXCEPT { } + + void setTimeout(double seconds) { _timeout = seconds; } + + virtual void timeIntegrationStart() + { + _timer.start(); + } + + virtual void timeIntegrationEnd() + { + _timer.reset(); + } + + virtual void timeIntegrationError(char const* message, unsigned int section, double time, double progress) { } + + virtual bool timeIntegrationSection(unsigned int section, double time, double const* state, double const* stateDot, double progress) + { + return shouldStop(); + } + + virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) + { + return shouldStop(); + } + + virtual bool timeIntegrationLinearSolve(unsigned int section, double time, double const* state, double const* stateDot) + { + return shouldStop(); + } + protected: + bool shouldStop() + { + if (_timeout <= 0.0) + return true; + + _timer.stop(); + const bool cont_sim = _timer.totalElapsedTime() <= _timeout; + _timer.start(); + return cont_sim; + } + + Timer _timer; + double _timeout; + }; +} + +#endif // CADET_TIMEOUT_CALLBACK_HPP_ diff --git a/include/common/Timer.hpp b/include/common/Timer.hpp index b279417c1..daba3bef9 100644 --- a/include/common/Timer.hpp +++ b/include/common/Timer.hpp @@ -53,6 +53,14 @@ namespace cadet return elapsed; } + /** + * @brief Resets the total elapsed time to @c 0.0 + */ + void reset() + { + _totalElapsed = 0.0; + } + /** * @brief Returns the total elapsed time between all start() and stop() calls in seconds * @return Total elapsed time in seconds diff --git a/src/cadet-cli/cadet-cli.cpp b/src/cadet-cli/cadet-cli.cpp index 8011a9981..cdbfb59fc 100644 --- a/src/cadet-cli/cadet-cli.cpp +++ b/src/cadet-cli/cadet-cli.cpp @@ -140,16 +140,17 @@ class ProgressBarNotifier : public cadet::INotificationCallback { snprintf(_secStrBuffer.data(), _secStrBuffer.size(), "Init Sec %3u", section); _progBar.update(progress, _secStrBuffer.data()); - return !cadet::stopExecutionRequested(); + return true; } virtual bool timeIntegrationStep(unsigned int section, double time, double const* state, double const* stateDot, double progress) { snprintf(_secStrBuffer.data(), _secStrBuffer.size(), "Section %3u", section); _progBar.update(progress, _secStrBuffer.data()); - return !cadet::stopExecutionRequested(); + return true; } + virtual bool timeIntegrationLinearSolve(unsigned int section, double time, double const* state, double const* stateDot) { return true; } protected: cadet::ProgressBar _progBar; std::vector _secStrBuffer; @@ -175,6 +176,11 @@ class SignalHandlingNotifier : public cadet::INotificationCallback { return !cadet::stopExecutionRequested(); } + + virtual bool timeIntegrationLinearSolve(unsigned int section, double time, double const* state, double const* stateDot) + { + return !cadet::stopExecutionRequested(); + } }; @@ -226,7 +232,7 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show dc.configure(drv, inFileName); } - std::unique_ptr shn = nullptr; + std::unique_ptr shn = std::make_unique(); #ifndef CADET_BENCHMARK_MODE // Select between progress bar or signal handling only @@ -236,17 +242,16 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show if (showProgressBar) { pb = std::make_unique(); - drv.simulator()->setNotificationCallback(pb.get()); + drv.addCallback(pb.get()); + drv.addCallback(shn.get()); } else { - shn = std::make_unique(); - drv.simulator()->setNotificationCallback(shn.get()); + drv.addCallback(shn.get()); } #else // Always handle signals in benchmark mode (no progress bar overhead) - shn = std::make_unique(); - drv.simulator()->setNotificationCallback(shn.get()); + drv.addCallback(shn.get()); #endif diff --git a/src/libcadet/SimulatorImpl.cpp b/src/libcadet/SimulatorImpl.cpp index 7ed710052..4a9b78851 100644 --- a/src/libcadet/SimulatorImpl.cpp +++ b/src/libcadet/SimulatorImpl.cpp @@ -326,6 +326,12 @@ namespace cadet const double tol = IDA_mem->ida_epsNewt; LOG(Trace) << "==> Solve at t = " << t << " alpha = " << alpha << " tol = " << tol; + if (sim->_notification) + { + const unsigned int secIdx = sim->getCurrentSection(t); + if (!sim->_notification->timeIntegrationLinearSolve(secIdx, t, NVEC_DATA(y), NVEC_DATA(yDot))) + return IDA_TOO_MUCH_WORK; + } return sim->_model->linearSolve(t, alpha, tol, NVEC_DATA(rhs), NVEC_DATA(weight), cadet::ConstSimulationState{NVEC_DATA(y), NVEC_DATA(yDot)}); } diff --git a/src/libcadet/api/CAPIv1.cpp b/src/libcadet/api/CAPIv1.cpp index e6164094c..7545dd880 100644 --- a/src/libcadet/api/CAPIv1.cpp +++ b/src/libcadet/api/CAPIv1.cpp @@ -20,6 +20,7 @@ #include "Logging.hpp" #include "common/Driver.hpp" +#include "common/TimeoutCallback.hpp" #define CADET_XSTR(a) #a @@ -493,7 +494,7 @@ namespace v1 return nullptr; } - InternalStorageSystemRecorder* const sysRec = realDrv->solution(); + InternalStorageSystemRecorder* const sysRec = realDrv->solution(); if (!sysRec) { LOG(Error) << "System solution recorder not available"; @@ -734,6 +735,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; if (state) @@ -752,6 +756,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; if (state) @@ -770,6 +777,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; unsigned int sliceStart; @@ -792,6 +802,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; unsigned int sliceStart; @@ -815,6 +828,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; const std::vector lastY = sim->getLastSensitivities(len); @@ -837,6 +853,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; const std::vector lastY = sim->getLastSensitivityDerivatives(len); @@ -859,6 +878,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; unsigned int sliceStart; @@ -885,6 +907,9 @@ namespace v1 return cdtErrorInvalidInputs; cadet::ISimulator* const sim = realDrv->simulator(); + if (!sim) + return cdtSimulatorNotInitialized; + unsigned int len = 0; unsigned int sliceStart; @@ -994,12 +1019,22 @@ namespace v1 if (!realDrv) return cdtErrorInvalidInputs; - if (timeSim) - *timeSim = realDrv->simulator()->lastSimulationDuration(); + if (timeSim) + *timeSim = realDrv->simulator()->lastSimulationDuration(); return cdtOK; } + cdtResult setTimeout(cdtDriver* drv, double timeout) + { + Driver* const realDrv = drv->driver; + if (!realDrv) + return cdtErrorInvalidInputs; + + realDrv->setTimeout(timeout); + return cdtOK; + } + } // namespace v1 } // namespace api @@ -1042,12 +1077,12 @@ extern "C" ptr->getSolutionDerivativeVolume = &cadet::api::v1::getSolutionDerivativeVolume; ptr->getSensitivityInlet = &cadet::api::v1::getSensitivityInlet; - ptr->getSensitivityOutlet = &cadet::api::v1::getSensitivityOutlet; + ptr->getSensitivityOutlet = &cadet::api::v1::getSensitivityOutlet; ptr->getSensitivityBulk = &cadet::api::v1::getSensitivityBulk; ptr->getSensitivityParticle = &cadet::api::v1::getSensitivityParticle; ptr->getSensitivitySolid = &cadet::api::v1::getSensitivitySolid; ptr->getSensitivityFlux = &cadet::api::v1::getSensitivityFlux; - ptr->getSensitivityVolume = &cadet::api::v1::getSensitivityVolume; + ptr->getSensitivityVolume = &cadet::api::v1::getSensitivityVolume; ptr->getSensitivityDerivativeInlet = &cadet::api::v1::getSensitivityDerivativeInlet; ptr->getSensitivityDerivativeOutlet = &cadet::api::v1::getSensitivityDerivativeOutlet; @@ -1074,6 +1109,8 @@ extern "C" ptr->getTimeSim = &cadet::api::v1::getTimeSim; + ptr->setTimeout = &cadet::api::v1::setTimeout; + return cdtOK; }