Skip to content

Add timeout to the API #366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/interface/solver.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions include/cadet/Notification.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/cadet/Simulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
58 changes: 58 additions & 0 deletions include/cadet/cadet.h

Large diffs are not rendered by default.

26 changes: 25 additions & 1 deletion include/common/Driver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "cadet/cadet.hpp"

#include "common/SolutionRecorderImpl.hpp"
#include "common/MultiCallback.hpp"
#include "common/TimeoutCallback.hpp"

namespace cadet
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<double> secTimes;
std::vector<bool> 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");
Expand Down Expand Up @@ -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<UnitOpIdx> _writeLastStateUnitId;
Expand Down
88 changes: 88 additions & 0 deletions include/common/MultiCallback.hpp
Original file line number Diff line number Diff line change
@@ -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 <vector>
#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<cadet::INotificationCallback*> const& callbacks() const CADET_NOEXCEPT { return _callbacks; }
std::vector<cadet::INotificationCallback*>& callbacks() CADET_NOEXCEPT { return _callbacks; }

void addCallback(cadet::INotificationCallback* callback) { _callbacks.push_back(callback); }
void clear() { _callbacks.clear(); }
protected:
std::vector<cadet::INotificationCallback*> _callbacks;
};
}

#endif // CADET_MULTI_CALLBACK_HPP_
79 changes: 79 additions & 0 deletions include/common/TimeoutCallback.hpp
Original file line number Diff line number Diff line change
@@ -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_
8 changes: 8 additions & 0 deletions include/common/Timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 13 additions & 8 deletions src/cadet-cli/cadet-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> _secStrBuffer;
Expand All @@ -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();
}
};


Expand Down Expand Up @@ -226,7 +232,7 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show
dc.configure(drv, inFileName);
}

std::unique_ptr<SignalHandlingNotifier> shn = nullptr;
std::unique_ptr<SignalHandlingNotifier> shn = std::make_unique<SignalHandlingNotifier>();

#ifndef CADET_BENCHMARK_MODE
// Select between progress bar or signal handling only
Expand All @@ -236,17 +242,16 @@ int run(const std::string& inFileName, const std::string& outFileName, bool show
if (showProgressBar)
{
pb = std::make_unique<ProgressBarNotifier>();
drv.simulator()->setNotificationCallback(pb.get());
drv.addCallback(pb.get());
drv.addCallback(shn.get());
}
else
{
shn = std::make_unique<SignalHandlingNotifier>();
drv.simulator()->setNotificationCallback(shn.get());
drv.addCallback(shn.get());
}
#else
// Always handle signals in benchmark mode (no progress bar overhead)
shn = std::make_unique<SignalHandlingNotifier>();
drv.simulator()->setNotificationCallback(shn.get());
drv.addCallback(shn.get());
#endif


Expand Down
6 changes: 6 additions & 0 deletions src/libcadet/SimulatorImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)});
}
Expand Down
Loading
Loading