Skip to content

Commit

Permalink
[Reactor] Suppress recoverable errors in RHS function by default
Browse files Browse the repository at this point in the history
Accumulate errors and show them if the integrator actually fails. In "verbose"
mode, the errors are shown as they occur.
  • Loading branch information
speth committed Oct 24, 2016
1 parent 7724c84 commit ecb2868
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 18 deletions.
1 change: 1 addition & 0 deletions include/cantera/numerics/CVodesIntegrator.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class CVodesIntegrator : public Integrator

size_t m_neq;
void* m_cvode_mem;
FuncEval* m_func;
double m_t0;
double m_time; //!< The current integrator time
N_Vector m_y, m_abstol;
Expand Down
38 changes: 37 additions & 1 deletion include/cantera/numerics/FuncEval.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Cantera
class FuncEval
{
public:
FuncEval() {}
FuncEval();
virtual ~FuncEval() {}

/**
Expand All @@ -38,6 +38,17 @@ class FuncEval
*/
virtual void eval(double t, double* y, double* ydot, double* p)=0;

//! Evaluate the right-hand side using return code to indicate status.
/*!
* Errors are indicated using the return value, rather than by throwing
* exceptions. This method is used when calling from a C-based integrator
* such as CVODES. Exceptions may either be stored or printed, based on the
* setting of suppressErrors().
* @returns 0 for a successful evaluation; 1 after a potentially-
* recoverable error; -1 after an unrecoverable error.
*/
int eval_nothrow(double t, double* y, double* ydot);

/**
* Fill the solution vector with the initial conditions
* at initial time t0.
Expand All @@ -62,13 +73,38 @@ class FuncEval
return m_sens_params.size();
}

//! Enable or disable suppression of errors when calling eval()
void suppressErrors(bool suppress) {
m_suppress_errors = suppress;
}

//! Get current state of error suppression
bool suppressErrors() const {
return m_suppress_errors;
};

//! Return a string containing the text of any suppressed errors
std::string getErrors() const;

//! Clear any previously-stored suppressed errors
void clearErrors() {
m_errors.clear();
};

//! Values for the problem parameters for which sensitivities are computed
//! This is the array which is perturbed and passed back as the fourth
//! argument to eval().
vector_fp m_sens_params;

//! Scaling factors for each sensitivity parameter
vector_fp m_paramScales;

protected:
// If true, errors are accumulated in m_errors. Otherwise, they are printed
bool m_suppress_errors;

//! Errors occuring during function evaluations
std::vector<std::string> m_errors;
};

}
Expand Down
1 change: 1 addition & 0 deletions include/cantera/zeroD/ReactorNet.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class ReactorNet : public FuncEval
//! reactor network.
void setVerbose(bool v = true) {
m_verbose = v;
suppressErrors(!m_verbose);
}

//! Return a reference to the integrator.
Expand Down
36 changes: 19 additions & 17 deletions src/numerics/CVodesIntegrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,8 @@ extern "C" {
*/
static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot, void* f_data)
{
try {
FuncEval* f = (FuncEval*) f_data;
f->eval(t, NV_DATA_S(y), NV_DATA_S(ydot), f->m_sens_params.data());
} catch (CanteraError& err) {
std::cerr << err.what() << std::endl;
return 1; // possibly recoverable error
} catch (std::exception& err) {
std::cerr << "cvodes_rhs: unhandled exception:" << std::endl;
std::cerr << err.what() << std::endl;
return -1; // unrecoverable error
} catch (...) {
std::cerr << "cvodes_rhs: unhandled exception of uknown type" << std::endl;
return -1; // unrecoverable error
}
return 0; // successful evaluation
FuncEval* f = (FuncEval*) f_data;
return f->eval_nothrow(t, NV_DATA_S(y), NV_DATA_S(ydot));
}

//! Function called by CVodes when an error is encountered instead of
Expand All @@ -78,6 +65,7 @@ extern "C" {
CVodesIntegrator::CVodesIntegrator() :
m_neq(0),
m_cvode_mem(0),
m_func(0),
m_t0(0.0),
m_y(0),
m_abstol(0),
Expand Down Expand Up @@ -251,6 +239,8 @@ void CVodesIntegrator::initialize(double t0, FuncEval& func)
m_neq = func.neq();
m_t0 = t0;
m_time = t0;
m_func = &func;
func.clearErrors();

if (m_y) {
N_VDestroy_Serial(m_y); // free solution vector if already allocated
Expand Down Expand Up @@ -333,6 +323,8 @@ void CVodesIntegrator::reinitialize(double t0, FuncEval& func)
m_t0 = t0;
m_time = t0;
func.getState(NV_DATA_S(m_y));
m_func = &func;
func.clearErrors();

int result = CVodeReInit(m_cvode_mem, m_t0, m_y);
if (result != CV_SUCCESS) {
Expand Down Expand Up @@ -393,10 +385,15 @@ void CVodesIntegrator::integrate(double tout)
}
int flag = CVode(m_cvode_mem, tout, m_y, &m_time, CV_NORMAL);
if (flag != CV_SUCCESS) {
string f_errs = m_func->getErrors();
if (!f_errs.empty()) {
f_errs = "Exceptions caught during RHS evaluation:\n" + f_errs;
}
throw CanteraError("CVodesIntegrator::integrate",
"CVodes error encountered. Error code: {}\n{}\n"
"{}"
"Components with largest weighted error estimates:\n{}",
flag, m_error_message, getErrorInfo(10));
flag, m_error_message, f_errs, getErrorInfo(10));
}
m_sens_ok = false;
}
Expand All @@ -405,10 +402,15 @@ double CVodesIntegrator::step(double tout)
{
int flag = CVode(m_cvode_mem, tout, m_y, &m_time, CV_ONE_STEP);
if (flag != CV_SUCCESS) {
string f_errs = m_func->getErrors();
if (!f_errs.empty()) {
f_errs = "Exceptions caught during RHS evaluation:\n" + f_errs;
}
throw CanteraError("CVodesIntegrator::step",
"CVodes error encountered. Error code: {}\n{}\n"
"{}"
"Components with largest weighted error estimates:\n{}",
flag, m_error_message, getErrorInfo(10));
flag, f_errs, m_error_message, getErrorInfo(10));

}
m_sens_ok = false;
Expand Down
54 changes: 54 additions & 0 deletions src/numerics/FuncEval.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "cantera/numerics/FuncEval.h"
#include <sstream>

namespace Cantera
{

FuncEval::FuncEval()
: m_suppress_errors(false)
{
}

int FuncEval::eval_nothrow(double t, double* y, double* ydot)
{
try {
eval(t, y, ydot, m_sens_params.data());
} catch (CanteraError& err) {
if (suppressErrors()) {
m_errors.push_back(err.getMessage());
} else {
writelog(err.what());
}
return 1; // possibly recoverable error
} catch (std::exception& err) {
if (suppressErrors()) {
m_errors.push_back(err.what());
} else {
writelog("FuncEval::eval_nothrow: unhandled exception:\n");
writelog(err.what());
writelogendl();
}
return -1; // unrecoverable error
} catch (...) {
std::string msg = "FuncEval::eval_nothrow: unhandled exception"
" of unknown type\n";
if (suppressErrors()) {
m_errors.push_back(msg);
} else {
writelog(msg);
}
return -1; // unrecoverable error
}
return 0; // successful evaluation
}

std::string FuncEval::getErrors() const {
std::stringstream errs;
for (const auto& err : m_errors) {
errs << err;
errs << "\n";
}
return errs.str();
}

}
1 change: 1 addition & 0 deletions src/zeroD/ReactorNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ReactorNet::ReactorNet() :
m_verbose(false)
{
m_integ = newIntegrator("CVODE");
suppressErrors(true);

// use backward differencing, with a full Jacobian computed
// numerically, and use a Newton linear iterator
Expand Down

0 comments on commit ecb2868

Please sign in to comment.