Cantera  2.1.2
CVodesIntegrator.cpp
Go to the documentation of this file.
1 /**
2  * @file CVodesIntegrator.cpp
3  */
4 
5 // Copyright 2001 California Institute of Technology
6 #include "cantera/base/config.h"
7 
10 
11 #include <iostream>
12 using namespace std;
13 
14 #if SUNDIALS_VERSION == 22
15 
16 #include "sundials_types.h"
17 #include "sundials_math.h"
18 #include "cvodes.h"
19 #include "cvodes_dense.h"
20 #include "cvodes_diag.h"
21 #include "cvodes_spgmr.h"
22 #include "cvodes_band.h"
23 #include "nvector_serial.h"
24 
25 #else
26 
27 #if SUNDIALS_VERSION >= 23
28 #include "sundials/sundials_types.h"
29 #include "sundials/sundials_math.h"
30 #include "sundials/sundials_nvector.h"
31 #include "nvector/nvector_serial.h"
32 #include "cvodes/cvodes.h"
33 #include "cvodes/cvodes_dense.h"
34 #include "cvodes/cvodes_diag.h"
35 #include "cvodes/cvodes_spgmr.h"
36 #include "cvodes/cvodes_band.h"
37 
38 #else
39 #error unsupported Sundials version!
40 #endif
41 
42 #if SUNDIALS_VERSION >= 24
43 #define CV_SS 1
44 #define CV_SV 2
45 #endif
46 
47 #endif
48 
49 #include <sstream>
50 #include <algorithm>
51 
52 namespace Cantera
53 {
54 
55 class FuncData
56 {
57 public:
58  FuncData(FuncEval* f, int npar = 0) {
59  m_pars.resize(npar, 1.0);
60  m_func = f;
61  }
62  virtual ~FuncData() {}
63  vector_fp m_pars;
64  FuncEval* m_func;
65 };
66 }
67 
68 
69 extern "C" {
70 
71  /**
72  * Function called by cvodes to evaluate ydot given y. The cvode
73  * integrator allows passing in a void* pointer to access
74  * external data. This pointer is cast to a pointer to a instance
75  * of class FuncEval. The equations to be integrated should be
76  * specified by deriving a class from FuncEval that evaluates the
77  * desired equations.
78  * @ingroup odeGroup
79  */
80  static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot,
81  void* f_data)
82  {
83  try {
84  double* ydata = NV_DATA_S(y); //N_VDATA(y);
85  double* ydotdata = NV_DATA_S(ydot); //N_VDATA(ydot);
86  Cantera::FuncData* d = (Cantera::FuncData*)f_data;
87  Cantera::FuncEval* f = d->m_func;
88  if (d->m_pars.size() == 0) {
89  f->eval(t, ydata, ydotdata, NULL);
90  } else {
91  f->eval(t, ydata, ydotdata, DATA_PTR(d->m_pars));
92  }
93  } catch (Cantera::CanteraError& err) {
94  std::cerr << err.what() << std::endl;
95  return 1; // possibly recoverable error
96  } catch (...) {
97  std::cerr << "cvodes_rhs: unhandled exception" << std::endl;
98  return -1; // unrecoverable error
99  }
100  return 0; // successful evaluation
101  }
102 }
103 
104 namespace Cantera
105 {
106 CVodesIntegrator::CVodesIntegrator() :
107  m_neq(0),
108  m_cvode_mem(0),
109  m_t0(0.0),
110  m_y(0),
111  m_abstol(0),
112  m_type(DENSE+NOJAC),
113  m_itol(CV_SS),
114  m_method(CV_BDF),
115  m_iter(CV_NEWTON),
116  m_maxord(0),
117  m_reltol(1.e-9),
118  m_abstols(1.e-15),
119  m_reltolsens(1.0e-5),
120  m_abstolsens(1.0e-4),
121  m_nabs(0),
122  m_hmax(0.0),
123  m_hmin(0.0),
124  m_maxsteps(20000),
125  m_maxErrTestFails(0),
126  m_fdata(0),
127  m_np(0),
128  m_mupper(0), m_mlower(0),
129  m_sens_ok(false)
130 {
131 #if SUNDIALS_VERSION <= 23
132  warn_deprecated("Use of Sundials 2.2 and 2.3",
133  "Support for these versions will be removed in Cantera 2.2.");
134 #endif
135  //m_ropt.resize(OPT_SIZE,0.0);
136  //m_iopt = new long[OPT_SIZE];
137  //fill(m_iopt, m_iopt+OPT_SIZE,0);
138 }
139 
140 CVodesIntegrator::~CVodesIntegrator()
141 {
142  if (m_cvode_mem) {
143  if (m_np > 0) {
144  CVodeSensFree(m_cvode_mem);
145  }
146  CVodeFree(&m_cvode_mem);
147  }
148  if (m_y) {
149  N_VDestroy_Serial(m_y);
150  }
151  if (m_abstol) {
152  N_VDestroy_Serial(m_abstol);
153  }
154  delete m_fdata;
155 
156  //delete[] m_iopt;
157 }
158 
159 double& CVodesIntegrator::solution(size_t k)
160 {
161  return NV_Ith_S(m_y, k);
162 }
163 
165 {
166  return NV_DATA_S(m_y);
167 }
168 
169 void CVodesIntegrator::setTolerances(double reltol, size_t n, double* abstol)
170 {
171  m_itol = CV_SV;
172  m_nabs = n;
173  if (n != m_neq) {
174  if (m_abstol) {
175  N_VDestroy_Serial(m_abstol);
176  }
177  m_abstol = N_VNew_Serial(n);
178  }
179  for (size_t i=0; i<n; i++) {
180  NV_Ith_S(m_abstol, i) = abstol[i];
181  }
182  m_reltol = reltol;
183 }
184 
185 void CVodesIntegrator::setTolerances(double reltol, double abstol)
186 {
187  m_itol = CV_SS;
188  m_reltol = reltol;
189  m_abstols = abstol;
190 }
191 
192 void CVodesIntegrator::setSensitivityTolerances(double reltol, double abstol)
193 {
194  m_reltolsens = reltol;
195  m_abstolsens = abstol;
196 }
197 
199 {
200  m_type = probtype;
201 }
202 
204 {
205  if (t == BDF_Method) {
206  m_method = CV_BDF;
207  } else if (t == Adams_Method) {
208  m_method = CV_ADAMS;
209  } else {
210  throw CVodesErr("unknown method");
211  }
212 }
213 
214 void CVodesIntegrator::setMaxStepSize(doublereal hmax)
215 {
216  m_hmax = hmax;
217  if (m_cvode_mem) {
218  CVodeSetMaxStep(m_cvode_mem, hmax);
219  }
220 }
221 
222 void CVodesIntegrator::setMinStepSize(doublereal hmin)
223 {
224  m_hmin = hmin;
225  if (m_cvode_mem) {
226  CVodeSetMinStep(m_cvode_mem, hmin);
227  }
228 }
229 
230 void CVodesIntegrator::setMaxSteps(int nmax)
231 {
232  m_maxsteps = nmax;
233  if (m_cvode_mem) {
234  CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
235  }
236 }
237 
239 {
240  m_maxErrTestFails = n;
241  if (m_cvode_mem) {
242  CVodeSetMaxErrTestFails(m_cvode_mem, n);
243  }
244 }
245 
247 {
248  if (t == Newton_Iter) {
249  m_iter = CV_NEWTON;
250  } else if (t == Functional_Iter) {
251  m_iter = CV_FUNCTIONAL;
252  } else {
253  throw CVodesErr("unknown iterator");
254  }
255 }
256 
257 void CVodesIntegrator::sensInit(double t0, FuncEval& func)
258 {
259  m_np = func.nparams();
260  size_t nv = func.neq();
261  m_sens_ok = false;
262 
263  doublereal* data;
264  N_Vector y;
265  y = N_VNew_Serial(nv);
266  m_yS = N_VCloneVectorArray_Serial(m_np, y);
267  for (size_t n = 0; n < m_np; n++) {
268  data = NV_DATA_S(m_yS[n]);
269  for (size_t j = 0; j < nv; j++) {
270  data[j] =0.0;
271  }
272  }
273 
274  int flag;
275 
276 #if SUNDIALS_VERSION <= 23
277  flag = CVodeSensMalloc(m_cvode_mem, m_np, CV_STAGGERED, m_yS);
278  if (flag != CV_SUCCESS) {
279  throw CVodesErr("Error in CVodeSensMalloc");
280  }
281  vector_fp atol(m_np, m_abstolsens);
282  double rtol = m_reltolsens;
283  flag = CVodeSetSensTolerances(m_cvode_mem, CV_SS, rtol, DATA_PTR(atol));
284 #elif SUNDIALS_VERSION >= 24
285  flag = CVodeSensInit(m_cvode_mem, m_np, CV_STAGGERED,
286  CVSensRhsFn(0), m_yS);
287 
288  if (flag != CV_SUCCESS) {
289  throw CVodesErr("Error in CVodeSensMalloc");
290  }
291  vector_fp atol(m_np, m_abstolsens);
292  double rtol = m_reltolsens;
293  flag = CVodeSensSStolerances(m_cvode_mem, rtol, DATA_PTR(atol));
294 #endif
295 
296 }
297 
299 {
300  m_neq = func.neq();
301  m_t0 = t0;
302  m_time = t0;
303 
304  if (m_y) {
305  N_VDestroy_Serial(m_y); // free solution vector if already allocated
306  }
307  m_y = N_VNew_Serial(m_neq); // allocate solution vector
308  for (size_t i = 0; i < m_neq; i++) {
309  NV_Ith_S(m_y, i) = 0.0;
310  }
311  // check abs tolerance array size
312  if (m_itol == CV_SV && m_nabs < m_neq) {
313  throw CVodesErr("not enough absolute tolerance values specified.");
314  }
315 
316  func.getInitialConditions(m_t0, m_neq, NV_DATA_S(m_y));
317 
318  if (m_cvode_mem) {
319  CVodeFree(&m_cvode_mem);
320  }
321 
322  /*
323  * Specify the method and the iteration type:
324  * Cantera Defaults:
325  * CV_BDF - Use BDF methods
326  * CV_NEWTON - use newton's method
327  */
328  m_cvode_mem = CVodeCreate(m_method, m_iter);
329  if (!m_cvode_mem) {
330  throw CVodesErr("CVodeCreate failed.");
331  }
332 
333  int flag = 0;
334 #if SUNDIALS_VERSION <= 23
335  if (m_itol == CV_SV) {
336  // vector atol
337  flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, m_y, m_itol,
338  m_reltol, m_abstol);
339  } else {
340  // scalar atol
341  flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, m_y, m_itol,
342  m_reltol, &m_abstols);
343  }
344  if (flag != CV_SUCCESS) {
345  if (flag == CV_MEM_FAIL) {
346  throw CVodesErr("Memory allocation failed.");
347  } else if (flag == CV_ILL_INPUT) {
348  throw CVodesErr("Illegal value for CVodeMalloc input argument.");
349  } else {
350  throw CVodesErr("CVodeMalloc failed.");
351  }
352  }
353 #elif SUNDIALS_VERSION >= 24
354 
355  flag = CVodeInit(m_cvode_mem, cvodes_rhs, m_t0, m_y);
356  if (flag != CV_SUCCESS) {
357  if (flag == CV_MEM_FAIL) {
358  throw CVodesErr("Memory allocation failed.");
359  } else if (flag == CV_ILL_INPUT) {
360  throw CVodesErr("Illegal value for CVodeInit input argument.");
361  } else {
362  throw CVodesErr("CVodeInit failed.");
363  }
364  }
365 
366  if (m_itol == CV_SV) {
367  flag = CVodeSVtolerances(m_cvode_mem, m_reltol, m_abstol);
368  } else {
369  flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols);
370  }
371  if (flag != CV_SUCCESS) {
372  if (flag == CV_MEM_FAIL) {
373  throw CVodesErr("Memory allocation failed.");
374  } else if (flag == CV_ILL_INPUT) {
375  throw CVodesErr("Illegal value for CVodeInit input argument.");
376  } else {
377  throw CVodesErr("CVodeInit failed.");
378  }
379  }
380 #endif
381 
382  // pass a pointer to func in m_data
383  delete m_fdata;
384  m_fdata = new FuncData(&func, func.nparams());
385 
386  //m_data = (void*)&func;
387 #if SUNDIALS_VERSION <= 23
388  flag = CVodeSetFdata(m_cvode_mem, (void*)m_fdata);
389  if (flag != CV_SUCCESS) {
390  throw CVodesErr("CVodeSetFdata failed.");
391  }
392 #elif SUNDIALS_VERSION >= 24
393  flag = CVodeSetUserData(m_cvode_mem, (void*)m_fdata);
394  if (flag != CV_SUCCESS) {
395  throw CVodesErr("CVodeSetUserData failed.");
396  }
397 #endif
398  if (func.nparams() > 0) {
399  sensInit(t0, func);
400  flag = CVodeSetSensParams(m_cvode_mem, DATA_PTR(m_fdata->m_pars),
401  NULL, NULL);
402  }
403  applyOptions();
404 }
405 
406 
407 void CVodesIntegrator::reinitialize(double t0, FuncEval& func)
408 {
409  m_t0 = t0;
410  m_time = t0;
411  //try {
412  func.getInitialConditions(m_t0, m_neq, NV_DATA_S(m_y));
413  //}
414  //catch (CanteraError) {
415  //showErrors();
416  //error("Teminating execution");
417  //}
418 
419  int result;
420 
421 #if SUNDIALS_VERSION <= 23
422  if (m_itol == CV_SV) {
423  result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, m_y,
424  m_itol, m_reltol,
425  m_abstol);
426  } else {
427  result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, m_y,
428  m_itol, m_reltol,
429  &m_abstols);
430  }
431  if (result != CV_SUCCESS) {
432  throw CVodesErr("CVodeReInit failed. result = "+int2str(result));
433  }
434 #elif SUNDIALS_VERSION >= 24
435  result = CVodeReInit(m_cvode_mem, m_t0, m_y);
436  if (result != CV_SUCCESS) {
437  throw CVodesErr("CVodeReInit failed. result = "+int2str(result));
438  }
439 #endif
440 
441  applyOptions();
442 }
443 
445 {
446  if (m_type == DENSE + NOJAC) {
447  long int N = m_neq;
448  CVDense(m_cvode_mem, N);
449  } else if (m_type == DIAG) {
450  CVDiag(m_cvode_mem);
451  } else if (m_type == GMRES) {
452  CVSpgmr(m_cvode_mem, PREC_NONE, 0);
453  } else if (m_type == BAND + NOJAC) {
454  long int N = m_neq;
455  long int nu = m_mupper;
456  long int nl = m_mlower;
457  CVBand(m_cvode_mem, N, nu, nl);
458  } else {
459  throw CVodesErr("unsupported option");
460  }
461 
462  if (m_maxord > 0) {
463  CVodeSetMaxOrd(m_cvode_mem, m_maxord);
464  }
465  if (m_maxsteps > 0) {
466  CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
467  }
468  if (m_hmax > 0) {
469  CVodeSetMaxStep(m_cvode_mem, m_hmax);
470  }
471  if (m_hmin > 0) {
472  CVodeSetMinStep(m_cvode_mem, m_hmin);
473  }
474  if (m_maxErrTestFails > 0) {
475  CVodeSetMaxErrTestFails(m_cvode_mem, m_maxErrTestFails);
476  }
477 }
478 
480 {
481  int flag = CVode(m_cvode_mem, tout, m_y, &m_time, CV_NORMAL);
482  if (flag != CV_SUCCESS) {
483  throw CVodesErr(" CVodes error encountered. Error code: " + int2str(flag) +
484  "\nComponents with largest weighted error estimates:\n" + getErrorInfo(10));
485  }
486  m_sens_ok = false;
487 }
488 
489 double CVodesIntegrator::step(double tout)
490 {
491  int flag = CVode(m_cvode_mem, tout, m_y, &m_time, CV_ONE_STEP);
492  if (flag != CV_SUCCESS) {
493  throw CVodesErr(" CVodes error encountered. Error code: " + int2str(flag) +
494  "\nComponents with largest weighted error estimates:\n" + getErrorInfo(10));
495 
496  }
497  m_sens_ok = false;
498  return m_time;
499 }
500 
502 {
503  long int ne;
504  CVodeGetNumRhsEvals(m_cvode_mem, &ne);
505  return ne;
506  //return m_iopt[NFE];
507 }
508 
509 double CVodesIntegrator::sensitivity(size_t k, size_t p)
510 {
511  if (m_time == m_t0) {
512  // calls to CVodeGetSens are only allowed after a successful time step.
513  return 0.0;
514  }
515  if (!m_sens_ok && m_np) {
516 #if SUNDIALS_VERSION <= 23
517  int flag = CVodeGetSens(m_cvode_mem, m_time, m_yS);
518 #elif SUNDIALS_VERSION >= 24
519  int flag = CVodeGetSens(m_cvode_mem, &m_time, m_yS);
520 #endif
521  if (flag != CV_SUCCESS) {
522  throw CVodesErr("CVodeGetSens failed. Error code: " + int2str(flag));
523  }
524  m_sens_ok = true;
525  }
526 
527  if (k >= m_neq) {
528  throw CVodesErr("sensitivity: k out of range ("+int2str(p)+")");
529  }
530  if (p >= m_np) {
531  throw CVodesErr("sensitivity: p out of range ("+int2str(p)+")");
532  }
533  return NV_Ith_S(m_yS[p],k);
534 }
535 
537 {
538  N_Vector errs = N_VNew_Serial(m_neq);
539  N_Vector errw = N_VNew_Serial(m_neq);
540  CVodeGetErrWeights(m_cvode_mem, errw);
541  CVodeGetEstLocalErrors(m_cvode_mem, errs);
542 
543  vector<pair<pair<double, double>, size_t> > weightedErrors;
544  for (size_t i=0; i<m_neq; i++) {
545  double err = NV_Ith_S(errs, i) * NV_Ith_S(errw, i);
546  weightedErrors.push_back(make_pair(make_pair(-abs(err), err), i));
547  }
548  N_VDestroy(errs);
549  N_VDestroy(errw);
550 
551  sort(weightedErrors.begin(), weightedErrors.end());
552  stringstream s;
553  for (int i=0; i<N; i++) {
554  s << weightedErrors[i].second << ": "
555  << weightedErrors[i].first.second << endl;
556  }
557  return s.str();
558 }
559 
560 }
Backward Differentiation.
Definition: Integrator.h:33
virtual void setProblemType(int probtype)
Set the problem type.
void applyOptions()
Applies user-specified options to the underlying CVODES solver.
std::string int2str(const int n, const std::string &fmt)
Convert an int to a string using a format converter.
Definition: stringUtils.cpp:40
Exception thrown when a CVODES error is encountered.
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:76
virtual void eval(double t, double *y, double *ydot, double *p)=0
Evaluate the right-hand-side function.
virtual void setMaxStepSize(double hmax)
Set the maximum step size.
virtual void setMethod(MethodType t)
Set the solution method.
virtual void setMinStepSize(double hmin)
Set the minimum step size.
virtual void setIterator(IterType t)
Set the linear iterator.
virtual int nEvals() const
The number of function evaluations.
virtual double * solution()
The current value of the solution of the system of equations.
Functional Iteration.
Definition: Integrator.h:45
const char * what() const
Get a description of the error.
virtual size_t neq()=0
Number of equations.
virtual void integrate(double tout)
Integrate the system of equations.
virtual void setSensitivityTolerances(double reltol, double abstol)
Set the sensitivity error tolerances.
virtual std::string getErrorInfo(int N)
Returns a string listing the weighted error estimates associated with each solution component...
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:68
virtual size_t nparams()
Number of sensitivity parameters.
Definition: FuncEval.h:48
virtual void setTolerances(double reltol, size_t n, double *abstol)
Set or reset the number of equations.
virtual void setMaxErrTestFails(int n)
Set the maximum permissible number of error test failures.
bool m_sens_ok
Indicates whether the sensitivities stored in m_yS have been updated for at the current integrator ti...
IterType
Specifies the method used for iteration.
Definition: Integrator.h:41
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:165
Newton Iteration.
Definition: Integrator.h:43
virtual doublereal step(double tout)
Integrate the system of equations.
Contains declarations for string manipulation functions within Cantera.
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
Virtual base class for ODE right-hand-side function evaluators.
Definition: FuncEval.h:23
static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot, void *f_data)
Function called by cvodes to evaluate ydot given y.
MethodType
Specifies the method used to integrate the system of equations.
Definition: Integrator.h:32
double m_time
The current integrator time.
virtual void getInitialConditions(double t0, size_t leny, double *y)=0
Fill the solution vector with the initial conditions at initial time t0.
virtual void initialize(double t0, FuncEval &func)
Initialize the integrator for a new problem.