cvodes_interface.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "cvodes_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
28 
29 #define THROWING(fcn, ...) \
30 cvodes_error(CASADI_STR(fcn), fcn(__VA_ARGS__))
31 
32 namespace casadi {
33 
34 extern "C"
35 int CASADI_INTEGRATOR_CVODES_EXPORT
36 casadi_register_integrator_cvodes(Integrator::Plugin* plugin) {
37  plugin->creator = CvodesInterface::creator;
38  plugin->name = "cvodes";
39  plugin->doc = CvodesInterface::meta_doc.c_str();;
40  plugin->version = CASADI_VERSION;
41  plugin->options = &CvodesInterface::options_;
42  plugin->deserialize = &CvodesInterface::deserialize;
43  return 0;
44 }
45 
46 extern "C"
47 void CASADI_INTEGRATOR_CVODES_EXPORT casadi_load_integrator_cvodes() {
49 }
50 
51 CvodesInterface::CvodesInterface(const std::string& name, const Function& dae,
52  double t0, const std::vector<double>& tout) : SundialsInterface(name, dae, t0, tout) {
53 }
54 
56  clear_mem();
57 }
58 
61  {{"linear_multistep_method",
62  {OT_STRING,
63  "Integrator scheme: BDF|adams"}},
64  {"nonlinear_solver_iteration",
65  {OT_STRING,
66  "Nonlinear solver type: NEWTON|functional"}},
67  {"min_step_size",
68  {OT_DOUBLE,
69  "Min step size [default: 0/0.0]"}},
70  {"fsens_all_at_once",
71  {OT_BOOL,
72  "Calculate all right hand sides of the sensitivity equations at once"}},
73  {"always_recalculate_jacobian",
74  {OT_BOOL,
75  "Recalculate Jacobian before factorizations, even if Jacobian is current [default: true]"}}
76  }
77 };
78 
79 void CvodesInterface::init(const Dict& opts) {
80  if (verbose_) casadi_message(name_ + "::init");
81 
82  // Initialize the base classes
84 
85  // Default options
86  std::string linear_multistep_method = "bdf";
87  std::string nonlinear_solver_iteration = "newton";
88  min_step_size_ = 0;
90 
91  // Read options
92  for (auto&& op : opts) {
93  if (op.first=="linear_multistep_method") {
94  linear_multistep_method = op.second.to_string();
95  } else if (op.first=="min_step_size") {
96  min_step_size_ = op.second;
97  } else if (op.first=="nonlinear_solver_iteration") {
98  nonlinear_solver_iteration = op.second.to_string();
99  } else if (op.first=="always_recalculate_jacobian") {
100  always_recalculate_jacobian_ = op.second;
101  }
102  }
103 
104  // Algebraic variables not supported
105  casadi_assert(nz_==0 && nrz_==0,
106  "CVODES does not support algebraic variables");
107 
108  if (linear_multistep_method=="adams") {
109  lmm_ = CV_ADAMS;
110  } else if (linear_multistep_method=="bdf") {
111  lmm_ = CV_BDF;
112  } else {
113  casadi_error("Unknown linear multistep method: " + linear_multistep_method);
114  }
115 
116  if (nonlinear_solver_iteration=="newton") {
117  iter_ = CV_NEWTON;
118  } else if (nonlinear_solver_iteration=="functional") {
119  iter_ = CV_FUNCTIONAL;
120  } else {
121  casadi_error("Unknown nonlinear solver iteration: " + nonlinear_solver_iteration);
122  }
123 
124  // Misc
125  alloc_w(nx_); // casadi_project
126  alloc_w(nrx_); // casadi_project
127 }
128 
129 int CvodesInterface::init_mem(void* mem) const {
130  if (SundialsInterface::init_mem(mem)) return 1;
131  auto m = to_mem(mem);
132 
133  // Create CVodes memory block
134  m->mem = CVodeCreate(lmm_, iter_);
135  casadi_assert(m->mem!=nullptr, "CVodeCreate: Creation failed");
136 
137  // Set error handler function
138  THROWING(CVodeSetErrHandlerFn, m->mem, ehfun, m);
139 
140  // Set user data
141  THROWING(CVodeSetUserData, m->mem, m);
142 
143  // Initialize CVodes
144  double t0 = 0;
145  THROWING(CVodeInit, m->mem, rhsF, t0, m->v_xz);
146 
147  // Set tolerances
148  if (scale_abstol_) {
149  THROWING(CVodeSVtolerances, m->mem, reltol_, m->abstolv);
150  } else {
151  THROWING(CVodeSStolerances, m->mem, reltol_, abstol_);
152  }
153 
154  // Maximum number of steps
155  THROWING(CVodeSetMaxNumSteps, m->mem, max_num_steps_);
156 
157  // Initial step size
158  if (step0_!=0) THROWING(CVodeSetInitStep, m->mem, step0_);
159 
160  // Min step size
161  if (min_step_size_!=0) THROWING(CVodeSetMinStep, m->mem, min_step_size_);
162 
163  // Max step size
164  if (max_step_size_!=0) THROWING(CVodeSetMaxStep, m->mem, max_step_size_);
165 
166  // Maximum order of method
167  if (max_order_) THROWING(CVodeSetMaxOrd, m->mem, max_order_);
168 
169  // Coeff. in the nonlinear convergence test
170  if (nonlin_conv_coeff_!=0) THROWING(CVodeSetNonlinConvCoef, m->mem, nonlin_conv_coeff_);
171 
172  // attach a linear solver
173  if (newton_scheme_==SD_DIRECT) {
174  // Direct scheme
175  CVodeMem cv_mem = static_cast<CVodeMem>(m->mem);
176  cv_mem->cv_lmem = m;
177  cv_mem->cv_lsetup = lsetupF;
178  cv_mem->cv_lsolve = lsolveF;
179  cv_mem->cv_setupNonNull = TRUE;
180  } else {
181  // Iterative scheme
182  casadi_int pretype = use_precon_ ? PREC_LEFT : PREC_NONE;
183  switch (newton_scheme_) {
184  case SD_DIRECT: casadi_assert_dev(0);
185  case SD_GMRES: THROWING(CVSpgmr, m->mem, pretype, max_krylov_); break;
186  case SD_BCGSTAB: THROWING(CVSpbcg, m->mem, pretype, max_krylov_); break;
187  case SD_TFQMR: THROWING(CVSptfqmr, m->mem, pretype, max_krylov_); break;
188  }
189  THROWING(CVSpilsSetJacTimesVecFn, m->mem, jtimesF);
190  if (use_precon_) THROWING(CVSpilsSetPreconditioner, m->mem, psetupF, psolveF);
191  }
192 
193  // Quadrature equations
194  if (nq_>0) {
195  // Initialize quadratures in CVodes
196  THROWING(CVodeQuadInit, m->mem, rhsQF, m->v_q);
197 
198  // Should the quadrature errors be used for step size control?
199  if (quad_err_con_) {
200  THROWING(CVodeSetQuadErrCon, m->mem, true);
201 
202  // Quadrature error tolerances
203  // TODO(Joel): vector absolute tolerances
204  THROWING(CVodeQuadSStolerances, m->mem, reltol_, abstol_);
205  }
206  }
207 
208  // Initialize adjoint sensitivities
209  if (nrx_>0) {
210  casadi_int interpType = interp_ == SD_HERMITE ? CV_HERMITE : CV_POLYNOMIAL;
211  THROWING(CVodeAdjInit, m->mem, steps_per_checkpoint_, interpType);
212  }
213 
214  m->first_callB = true;
215  return 0;
216 }
217 
218 int CvodesInterface::rhsF(double t, N_Vector x, N_Vector xdot, void *user_data) {
219  try {
220  casadi_assert_dev(user_data);
221  auto m = to_mem(user_data);
222  auto& s = m->self;
223  if (s.calc_daeF(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(xdot), nullptr)) return 1;
224  return 0;
225  } catch(std::exception& e) { // non-recoverable error
226  uerr() << "rhs failed: " << e.what() << std::endl;
227  return -1;
228  }
229 }
230 
231 void CvodesInterface::reset(IntegratorMemory* mem, bool first_call) const {
232  if (verbose_) casadi_message(name_ + "::reset");
233  auto m = to_mem(mem);
234 
235  // Reset the base classes
236  SundialsInterface::reset(mem, first_call);
237 
238  // Only reinitialize solver at first call or if event handling is required
239  // May want to always enable this after more testing
240  if (first_call || ne_ > 0) {
241  // Re-initialize forward integration
242  THROWING(CVodeReInit, m->mem, m->t, m->v_xz);
243 
244  // Re-initialize quadratures
245  if (nq_ > 0) {
246  THROWING(CVodeQuadReInit, m->mem, m->v_q);
247  }
248  }
249 
250  // How is this impacted by CVodeReInit?
251  if (first_call) {
252  // Re-initialize backward integration
253  if (nrx_ > 0) {
254  THROWING(CVodeAdjReInit, m->mem);
255  }
256  }
257 }
258 
260  auto m = to_mem(mem);
261 
262  // Do not integrate past change in input signals or past the end
263  // The event handling may cause the stop time to become smaller than internal time reached,
264  // in which case the stop time cannot be enforced
265  if (m->t_stop >= m->tcur) {
266  THROWING(CVodeSetStopTime, m->mem, m->t_stop);
267  }
268 
269  // Integrate, unless already at desired time
270  const double ttol = 1e-9;
271  if (fabs(m->t - m->t_next) >= ttol) {
272  // Integrate forward ...
273  double tret = m->t;
274  if (nrx_>0) {
275  // ... with taping
276  THROWING(CVodeF, m->mem, m->t_next, m->v_xz, &tret, CV_NORMAL, &m->ncheck);
277  } else {
278  // ... without taping
279  THROWING(CVode, m->mem, m->t_next, m->v_xz, &tret, CV_NORMAL);
280  }
281 
282  // Get quadratures
283  if (nq_ > 0) {
284  THROWING(CVodeGetQuad, m->mem, &tret, m->v_q);
285  }
286  }
287 
288  // Set function outputs
289  casadi_copy(NV_DATA_S(m->v_xz), nx_, m->x);
290  casadi_copy(NV_DATA_S(m->v_q), nq_, m->q);
291 
292  // Get stats
293  THROWING(CVodeGetIntegratorStats, m->mem, &m->nsteps, &m->nfevals, &m->nlinsetups,
294  &m->netfails, &m->qlast, &m->qcur, &m->hinused,
295  &m->hlast, &m->hcur, &m->tcur);
296  THROWING(CVodeGetNonlinSolvStats, m->mem, &m->nniters, &m->nncfails);
297 
298  return 0;
299 }
300 
302  const double* adj_x, const double* adj_z, const double* adj_q) const {
303  auto m = to_mem(mem);
304 
305  // Call method in base class
306  SundialsInterface::impulseB(mem, adj_x, adj_z, adj_q);
307 
308  if (m->first_callB) {
309  // Create backward problem
310  THROWING(CVodeCreateB, m->mem, lmm_, iter_, &m->whichB);
311  THROWING(CVodeInitB, m->mem, m->whichB, rhsB, m->t, m->v_adj_xz);
312  THROWING(CVodeSStolerancesB, m->mem, m->whichB, reltol_, abstol_);
313  THROWING(CVodeSetUserDataB, m->mem, m->whichB, m);
314  if (newton_scheme_==SD_DIRECT) {
315  // Direct scheme
316  CVodeMem cv_mem = static_cast<CVodeMem>(m->mem);
317  CVadjMem ca_mem = cv_mem->cv_adj_mem;
318  CVodeBMem cvB_mem = ca_mem->cvB_mem;
319  cvB_mem->cv_lmem = m;
320  cvB_mem->cv_mem->cv_lmem = m;
321  cvB_mem->cv_mem->cv_lsetup = lsetupB;
322  cvB_mem->cv_mem->cv_lsolve = lsolveB;
323  cvB_mem->cv_mem->cv_setupNonNull = TRUE;
324  } else {
325  // Iterative scheme
326  casadi_int pretype = use_precon_ ? PREC_LEFT : PREC_NONE;
327  switch (newton_scheme_) {
328  case SD_DIRECT: casadi_assert_dev(0);
329  case SD_GMRES: THROWING(CVSpgmrB, m->mem, m->whichB, pretype, max_krylov_); break;
330  case SD_BCGSTAB: THROWING(CVSpbcgB, m->mem, m->whichB, pretype, max_krylov_); break;
331  case SD_TFQMR: THROWING(CVSptfqmrB, m->mem, m->whichB, pretype, max_krylov_); break;
332  }
333  THROWING(CVSpilsSetJacTimesVecFnB, m->mem, m->whichB, jtimesB);
334  if (use_precon_) THROWING(CVSpilsSetPreconditionerB, m->mem, m->whichB, psetupB, psolveB);
335  }
336 
337  // Quadratures for the backward problem
338  THROWING(CVodeQuadInitB, m->mem, m->whichB, rhsQB, m->v_adj_pu);
339  if (quad_err_con_) {
340  THROWING(CVodeSetQuadErrConB, m->mem, m->whichB, true);
341  THROWING(CVodeQuadSStolerancesB, m->mem, m->whichB, reltol_, abstol_);
342  }
343 
344  // Mark initialized
345  m->first_callB = false;
346  } else {
347  // Save solver stats offsets before reset
348  save_offsets(m);
349 
350  // Reinitialize solver
351  THROWING(CVodeReInitB, m->mem, m->whichB, m->t, m->v_adj_xz);
352  THROWING(CVodeQuadReInitB, m->mem, m->whichB, m->v_adj_pu);
353  }
354 }
355 
356 void CvodesInterface::retreat(IntegratorMemory* mem, const double* u,
357  double* adj_x, double* adj_p, double* adj_u) const {
358  auto m = to_mem(mem);
359 
360  // Set controls
361  casadi_copy(u, nu_, m->u);
362 
363  // Integrate, unless already at desired time
364  if (m->t_next < m->t) {
365  THROWING(CVodeB, m->mem, m->t_next, CV_NORMAL);
366  double tret;
367  THROWING(CVodeGetB, m->mem, m->whichB, &tret, m->v_adj_xz);
368  if (nrq_ > 0 || nuq_ > 0) {
369  THROWING(CVodeGetQuadB, m->mem, m->whichB, &tret, m->v_adj_pu);
370  }
371  }
372 
373  // Save outputs
374  casadi_copy(NV_DATA_S(m->v_adj_xz), nrx_, adj_x);
375  casadi_copy(NV_DATA_S(m->v_adj_pu), nrq_, adj_p);
376  casadi_copy(NV_DATA_S(m->v_adj_pu) + nrq_, nuq_, adj_u);
377 
378  // Get stats
379  CVodeMem cv_mem = static_cast<CVodeMem>(m->mem);
380  CVadjMem ca_mem = cv_mem->cv_adj_mem;
381  CVodeBMem cvB_mem = ca_mem->cvB_mem;
382  THROWING(CVodeGetIntegratorStats, cvB_mem->cv_mem, &m->nstepsB,
383  &m->nfevalsB, &m->nlinsetupsB, &m->netfailsB, &m->qlastB,
384  &m->qcurB, &m->hinusedB, &m->hlastB, &m->hcurB, &m->tcurB);
385  THROWING(CVodeGetNonlinSolvStats, cvB_mem->cv_mem, &m->nnitersB, &m->nncfailsB);
386 
387  // Add offset corresponding to counters that were set to zero at reinitializations
388  add_offsets(m);
389 }
390 
391 void CvodesInterface::cvodes_error(const char* module, int flag) {
392  // Successfull return or warning
393  if (flag>=CV_SUCCESS) return;
394  // Construct error message
395  char* flagname = CVodeGetReturnFlagName(flag);
396  std::stringstream ss;
397  ss << module << " returned \"" << flagname << "\". Consult CVODES documentation.";
398  free(flagname); // NOLINT
399  casadi_error(ss.str());
400 }
401 
402 void CvodesInterface::ehfun(int error_code, const char *module, const char *function,
403  char *msg, void *user_data) {
404  try {
405  casadi_assert_dev(user_data);
406  auto m = to_mem(user_data);
407  auto& s = m->self;
408  if (!s.disable_internal_warnings_) {
409  uerr() << msg << std::endl;
410  }
411  } catch(std::exception& e) {
412  uerr() << "ehfun failed: " << e.what() << std::endl;
413  }
414 }
415 
416 int CvodesInterface::rhsQF(double t, N_Vector x, N_Vector qdot, void *user_data) {
417  try {
418  auto m = to_mem(user_data);
419  auto& s = m->self;
420  if (s.calc_quadF(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(qdot))) return 1;
421 
422  return 0;
423  } catch(std::exception& e) { // non-recoverable error
424  uerr() << "rhsQ failed: " << e.what() << std::endl;
425  return -1;
426  }
427 }
428 
429 int CvodesInterface::rhsB(double t, N_Vector x, N_Vector rx, N_Vector rxdot, void *user_data) {
430  try {
431  casadi_assert_dev(user_data);
432  auto m = to_mem(user_data);
433  auto& s = m->self;
434  if (s.calc_daeB(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(rx), nullptr, m->adj_q,
435  NV_DATA_S(rxdot), nullptr)) return 1;
436  // Negate (note definition of g)
437  casadi_scal(s.nrx_, -1., NV_DATA_S(rxdot));
438  return 0;
439  } catch(std::exception& e) { // non-recoverable error
440  uerr() << "rhsB failed: " << e.what() << std::endl;
441  return -1;
442  }
443 }
444 
445 int CvodesInterface::rhsQB(double t, N_Vector x, N_Vector rx, N_Vector ruqdot, void *user_data) {
446  try {
447  casadi_assert_dev(user_data);
448  auto m = to_mem(user_data);
449  auto& s = m->self;
450  if (s.calc_quadB(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(rx), nullptr,
451  NV_DATA_S(ruqdot), NV_DATA_S(ruqdot) + s.nrq_)) return 1;
452 
453  // Negate (note definition of g)
454  casadi_scal((s.nrq_ + s.nuq_), -1., NV_DATA_S(ruqdot));
455 
456  return 0;
457  } catch(std::exception& e) { // non-recoverable error
458  uerr() << "rhsQB failed: " << e.what() << std::endl;
459  return -1;
460  }
461 }
462 
463 int CvodesInterface::jtimesF(N_Vector v, N_Vector Jv, double t, N_Vector x,
464  N_Vector xdot, void *user_data, N_Vector tmp) {
465  try {
466  auto m = to_mem(user_data);
467  auto& s = m->self;
468  if (s.calc_jtimesF(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(v), nullptr,
469  NV_DATA_S(Jv), nullptr)) return 1;
470  return 0;
471  } catch(std::exception& e) { // non-recoverable error
472  uerr() << "jtimesF failed: " << e.what() << std::endl;
473  return -1;
474  }
475 }
476 
477 int CvodesInterface::jtimesB(N_Vector v, N_Vector Jv, double t, N_Vector x,
478  N_Vector rx, N_Vector rxdot, void *user_data, N_Vector tmpB) {
479  try {
480  auto m = to_mem(user_data);
481  auto& s = m->self;
482  // The function is linear so the Jacobian-times-vector function is the function itself
483  if (s.calc_daeB(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(v), nullptr, nullptr,
484  NV_DATA_S(Jv), nullptr)) return 1;
485  return 0;
486  } catch(std::exception& e) { // non-recoverable error
487  uerr() << "jtimesB failed: " << e.what() << std::endl;
488  return -1;
489  }
490 }
491 
492 int CvodesInterface::psolveF(double t, N_Vector x, N_Vector xdot, N_Vector r,
493  N_Vector z, double gamma, double delta, int lr, void *user_data, N_Vector tmp) {
494  try {
495  auto m = to_mem(user_data);
496  auto& s = m->self;
497 
498  // Get right-hand sides in m->tmp1
499  double* v = NV_DATA_S(r);
500  casadi_copy(v, s.nx_, m->tmp1);
501 
502  // Solve for undifferentiated right-hand-side, save to output
503  if (s.linsolF_.solve(m->jacF, m->tmp1, 1, false, m->mem_linsolF)) return 1;
504  v = NV_DATA_S(z); // possibly different from r
505  casadi_copy(m->tmp1, s.nx1_, v);
506 
507  // Sensitivity equations
508  if (s.nfwd_ > 0) {
509  // Second order correction
510  if (s.second_order_correction_) {
511  // The outputs will double as seeds for jtimesF
512  casadi_clear(v + s.nx1_, s.nx_ - s.nx1_);
513  if (s.calc_jtimesF(m, t, NV_DATA_S(x), nullptr, v, nullptr, m->tmp2, nullptr)) return 1;
514 
515  // Subtract m->tmp2 from m->tmp1, scaled with -gamma
516  casadi_axpy(s.nx_ - s.nx1_, m->gamma, m->tmp2 + s.nx1_, m->tmp1 + s.nx1_);
517  }
518 
519  // Solve for sensitivity right-hand-sides
520  if (s.linsolF_.solve(m->jacF, m->tmp1 + s.nx1_, s.nfwd_, false, m->mem_linsolF)) return 1;
521 
522  // Save to output, reordered
523  casadi_copy(m->tmp1 + s.nx1_, s.nx_ - s.nx1_, v + s.nx1_);
524  }
525 
526  return 0;
527  } catch(std::exception& e) { // non-recoverable error
528  uerr() << "psolve failed: " << e.what() << std::endl;
529  return -1;
530  }
531 }
532 
533 int CvodesInterface::psolveB(double t, N_Vector x, N_Vector xB, N_Vector xdotB, N_Vector rvecB,
534  N_Vector zvecB, double gammaB, double deltaB, int lr, void *user_data, N_Vector tmpB) {
535  try {
536  auto m = to_mem(user_data);
537  auto& s = m->self;
538 
539  // Get right-hand sides in m->tmp1
540  double* v = NV_DATA_S(rvecB);
541  casadi_copy(v, s.nrx_, m->tmp1);
542 
543  // Solve for undifferentiated right-hand-side, save to output
544  if (s.linsolF_.solve(m->jacF, m->tmp1, s.nadj_, true, m->mem_linsolF)) return 1;
545  v = NV_DATA_S(zvecB); // possibly different from rvecB
546  casadi_copy(m->tmp1, s.nrx1_ * s.nadj_, v);
547 
548  // Sensitivity equations
549  if (s.nfwd_ > 0) {
550  // Second order correction
551  if (s.second_order_correction_) {
552  // The outputs will double as seeds for calc_daeB
553  casadi_clear(v + s.nrx1_ * s.nadj_, s.nrx_ - s.nrx1_ * s.nadj_);
554  if (s.calc_daeB(m, t, NV_DATA_S(x), nullptr, v, nullptr, nullptr, m->tmp2, nullptr))
555  return 1;
556  // Subtract m->tmp2 from m->tmp1, scaled with gammaB
557  casadi_axpy(s.nrx_ - s.nrx1_ * s.nadj_, -m->gammaB, m->tmp2 + s.nrx1_ * s.nadj_,
558  m->tmp1 + s.nrx1_ * s.nadj_);
559  }
560 
561  // Solve for sensitivity right-hand-sides
562  if (s.linsolF_.solve(m->jacF, m->tmp1 + s.nx1_, s.nadj_ * s.nfwd_,
563  true, m->mem_linsolF)) return 1;
564 
565  // Save to output, reordered
566  casadi_copy(m->tmp1 + s.nx1_, s.nx_ - s.nx1_, v + s.nx1_);
567  }
568 
569  return 0;
570  } catch(std::exception& e) { // non-recoverable error
571  uerr() << "psolveB failed: " << e.what() << std::endl;
572  return -1;
573  }
574 }
575 
576 int CvodesInterface::psetupF(double t, N_Vector x, N_Vector xdot, booleantype jok,
577  booleantype *jcurPtr, double gamma, void *user_data,
578  N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) {
579  try {
580  auto m = to_mem(user_data);
581  auto& s = m->self;
582  // Store gamma for later
583  m->gamma = gamma;
584 
585  // Sparsity patterns
586  const Sparsity& sp_jac_ode_x = s.get_function("jacF").sparsity_out(0);
587  const Sparsity& sp_jacF = s.linsolF_.sparsity();
588 
589  // Calculate Jacobian, if necessary
590  if (s.always_recalculate_jacobian_ || !jcurPtr || *jcurPtr == 0) {
591  // Re(calculate) Jacobian
592  if (s.calc_jacF(m, t, NV_DATA_S(x), nullptr,
593  m->jac_ode_x, nullptr, nullptr, nullptr)) return 1;
594 
595  // Jacobian is now current
596  if (jcurPtr) *jcurPtr = 1;
597  }
598 
599  // Project to expected sparsity pattern (with diagonal)
600  casadi_project(m->jac_ode_x, sp_jac_ode_x, m->jacF, sp_jacF, m->w);
601 
602  // Scale and shift diagonal
603  const casadi_int *colind = sp_jacF.colind(), *row = sp_jacF.row();
604  for (casadi_int c = 0; c < sp_jacF.size2(); ++c) {
605  for (casadi_int k = colind[c]; k < colind[c + 1]; ++k) {
606  casadi_int r = row[k];
607  // Scale Jacobian
608  m->jacF[k] *= -gamma;
609  // Add contribution to diagonal
610  if (r == c) m->jacF[k] += 1;
611  }
612  }
613 
614  // Prepare the solution of the linear system (e.g. factorize)
615  if (s.linsolF_.nfact(m->jacF, m->mem_linsolF)) return 1;
616 
617  return 0;
618  } catch(std::exception& e) { // non-recoverable error
619  uerr() << "psetup failed: " << e.what() << std::endl;
620  return -1;
621  }
622 }
623 
624 int CvodesInterface::psetupB(double t, N_Vector x, N_Vector rx, N_Vector rxdot,
625  booleantype jokB, booleantype *jcurPtrB, double gammaB,
626  void *user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B) {
627  try {
628  auto m = to_mem(user_data);
629  // Store gamma for later
630  m->gammaB = gammaB;
631  // We use the same linear solver for the forward problem as for the backward problem
632  return psetupF(t, x, nullptr, jokB, jcurPtrB, -gammaB, user_data, tmp1B, tmp2B, tmp3B);
633 
634  } catch(std::exception& e) { // non-recoverable error
635  uerr() << "psetupB failed: " << e.what() << std::endl;
636  return -1;
637  }
638 }
639 
640 int CvodesInterface::lsetupF(CVodeMem cv_mem, int convfail, N_Vector x, N_Vector xdot,
641  booleantype *jcurPtr, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) {
642  try {
643  auto m = to_mem(cv_mem->cv_lmem);
644 
645  // Call the preconditioner setup function (which sets up the linear solver)
646  return psetupF(cv_mem->cv_tn, x, xdot, FALSE, jcurPtr,
647  cv_mem->cv_gamma, static_cast<void*>(m), vtemp1, vtemp2, vtemp3);
648 
649  } catch(std::exception& e) { // non-recoverable error
650  uerr() << "lsetup failed: " << e.what() << std::endl;
651  return -1;
652  }
653 }
654 
655 int CvodesInterface::lsetupB(CVodeMem cv_mem, int convfail, N_Vector x, N_Vector xdot,
656  booleantype *jcurPtr, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) {
657  try {
658  auto m = to_mem(cv_mem->cv_lmem);
659  CVadjMem ca_mem;
660  //CVodeBMem cvB_mem;
661 
662  // Current time
663  double t = cv_mem->cv_tn; // TODO(Joel): is this correct?
664  double gamma = cv_mem->cv_gamma;
665 
666  cv_mem = static_cast<CVodeMem>(cv_mem->cv_user_data);
667  ca_mem = cv_mem->cv_adj_mem;
668  //cvB_mem = ca_mem->ca_bckpbCrt;
669 
670  // Get FORWARD solution from interpolation.
671  int flag = ca_mem->ca_IMget(cv_mem, t, ca_mem->ca_ytmp, nullptr);
672  if (flag != CV_SUCCESS) casadi_error("Could not interpolate forward states");
673 
674  // Call the preconditioner setup function (which sets up the linear solver)
675  return psetupB(t, ca_mem->ca_ytmp, x, xdot, FALSE, jcurPtr,
676  gamma, static_cast<void*>(m), vtemp1, vtemp2, vtemp3);
677 
678  } catch(std::exception& e) { // non-recoverable error
679  uerr() << "lsetupB failed: " << e.what() << std::endl;
680  return -1;
681  }
682 }
683 
684 int CvodesInterface::lsolveF(CVodeMem cv_mem, N_Vector b, N_Vector weight,
685  N_Vector x, N_Vector xdot) {
686  try {
687  auto m = to_mem(cv_mem->cv_lmem);
688  //auto& s = m->self;
689 
690  // Current time
691  double t = cv_mem->cv_tn;
692 
693  // Scaling factor before J
694  double gamma = cv_mem->cv_gamma;
695 
696  // Accuracy
697  double delta = 0.0;
698 
699  // Left/right preconditioner
700  casadi_int lr = 1;
701 
702  // Call the preconditioner solve function (which solves the linear system)
703  return psolveF(t, x, xdot, b, b, gamma, delta, lr, static_cast<void*>(m), nullptr);
704 
705  } catch(std::exception& e) { // non-recoverable error
706  uerr() << "lsolveF failed: " << e.what() << std::endl;
707  return -1;
708  }
709 }
710 
711 int CvodesInterface::lsolveB(CVodeMem cv_mem, N_Vector b, N_Vector weight,
712  N_Vector x, N_Vector xdot) {
713  try {
714  auto m = to_mem(cv_mem->cv_lmem);
715  CVadjMem ca_mem;
716  //CVodeBMem cvB_mem;
717 
718  // Current time
719  double t = cv_mem->cv_tn; // TODO(Joel): is this correct?
720  double gamma = cv_mem->cv_gamma;
721 
722  cv_mem = static_cast<CVodeMem>(cv_mem->cv_user_data);
723 
724  ca_mem = cv_mem->cv_adj_mem;
725  //cvB_mem = ca_mem->ca_bckpbCrt;
726 
727  // Get FORWARD solution from interpolation.
728  int flag = ca_mem->ca_IMget(cv_mem, t, ca_mem->ca_ytmp, nullptr);
729  if (flag != CV_SUCCESS) casadi_error("Could not interpolate forward states");
730 
731  // Accuracy
732  double delta = 0.0;
733 
734  // Left/right preconditioner
735  int lr = 1;
736 
737  // Call the preconditioner solve function (which solves the linear system)
738  return psolveB(t, ca_mem->ca_ytmp, x, xdot, b, b, gamma, delta, lr,
739  static_cast<void*>(m), nullptr);
740 
741  } catch(std::exception& e) { // non-recoverable error
742  uerr() << "lsolveB failed: " << e.what() << std::endl;
743  return -1;
744  }
745 }
746 
748  this->mem = nullptr;
749 
750  // Reset checkpoints counter
751  this->ncheck = 0;
752 }
753 
755  if (this->mem_linsolF >= 0) self.linsolF_.release(this->mem_linsolF);
756  if (this->mem) CVodeFree(&this->mem);
757 }
758 
760  int version = s.version("CvodesInterface", 1, 3);
761  s.unpack("CvodesInterface::lmm", lmm_);
762  s.unpack("CvodesInterface::iter", iter_);
763 
764  if (version>=2) {
765  s.unpack("CvodesInterface::min_step_size", min_step_size_);
766  } else {
767  min_step_size_ = 0;
768  }
769 
770  if (version >= 3) {
771  s.unpack("CvodesInterface::always_recalculate_jacobian", always_recalculate_jacobian_);
772  }
773 }
774 
777  s.version("CvodesInterface", 3);
778 
779  s.pack("CvodesInterface::lmm", lmm_);
780  s.pack("CvodesInterface::iter", iter_);
781  s.pack("CvodesInterface::min_step_size", min_step_size_);
782  s.pack("CvodesInterface::always_recalculate_jacobian", always_recalculate_jacobian_);
783 }
784 
785 } // namespace casadi
'cvodes' plugin for Integrator
static int lsolveB(CVodeMem cv_mem, N_Vector b, N_Vector weight, N_Vector x, N_Vector xdot)
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
static int rhsF(double t, N_Vector x, N_Vector xdot, void *user_data)
static CvodesMemory * to_mem(void *mem)
Cast to memory object.
void retreat(IntegratorMemory *mem, const double *u, double *adj_x, double *adj_p, double *adj_u) const override
Retreat solution in time.
~CvodesInterface() override
Destructor.
static int psetupF(double t, N_Vector x, N_Vector xdot, booleantype jok, booleantype *jcurPtr, double gamma, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
void reset(IntegratorMemory *mem, bool first_call) const override
Reset the forward solver at the start or after an event.
CvodesInterface(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Constructor.
static int rhsQB(double t, N_Vector x, N_Vector rx, N_Vector ruqdot, void *user_data)
int advance_noevent(IntegratorMemory *mem) const override
Advance solution in time.
static int lsetupF(CVodeMem cv_mem, int convfail, N_Vector x, N_Vector xdot, booleantype *jcurPtr, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3)
static void cvodes_error(const char *module, int flag)
int init_mem(void *mem) const override
Initalize memory block.
static int psolveB(double t, N_Vector x, N_Vector xB, N_Vector xdotB, N_Vector rvecB, N_Vector zvecB, double gammaB, double deltaB, int lr, void *user_data, N_Vector tmpB)
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void impulseB(IntegratorMemory *mem, const double *adj_x, const double *adj_z, const double *adj_q) const override
Introduce an impulse into the backwards integration at the current time.
static const std::string meta_doc
A documentation string.
static int lsetupB(CVodeMem cv_mem, int convfail, N_Vector x, N_Vector xdot, booleantype *jcurPtr, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3)
static Integrator * creator(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Create a new integrator.
static const Options options_
Options.
void init(const Dict &opts) override
Initialize stage.
bool always_recalculate_jacobian_
Options.
static int jtimesF(N_Vector v, N_Vector Jv, double t, N_Vector x, N_Vector xdot, void *user_data, N_Vector tmp)
static void ehfun(int error_code, const char *module, const char *function, char *msg, void *user_data)
static int psolveF(double t, N_Vector x, N_Vector xdot, N_Vector r, N_Vector z, double gamma, double delta, int lr, void *user_data, N_Vector tmp)
static int rhsQF(double t, N_Vector x, N_Vector qdot, void *user_data)
static int lsolveF(CVodeMem cv_mem, N_Vector b, N_Vector weight, N_Vector x, N_Vector xdot)
static int rhsB(double t, N_Vector x, N_Vector xB, N_Vector xdotB, void *user_data)
static int psetupB(double t, N_Vector x, N_Vector xB, N_Vector xdotB, booleantype jokB, booleantype *jcurPtrB, double gammaB, void *user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B)
static int jtimesB(N_Vector vB, N_Vector JvB, double t, N_Vector x, N_Vector xB, N_Vector xdotB, void *user_data, N_Vector tmpB)
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
Function object.
Definition: function.hpp:60
casadi_int nrx_
Number of states for the backward integration.
casadi_int nu_
Number of controls.
casadi_int nx_
Number of states for the forward integration.
casadi_int ne_
Number of of zero-crossing functions.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
General sparsity class.
Definition: sparsity.hpp:106
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
enum casadi::SundialsInterface::InterpType interp_
void impulseB(IntegratorMemory *mem, const double *adj_x, const double *adj_z, const double *adj_q) const override
Introduce an impulse into the backwards integration at the current time.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
enum casadi::SundialsInterface::NewtonScheme newton_scheme_
void reset(IntegratorMemory *mem, bool first_call) const override
Reset the forward solver at the start or after an event.
int init_mem(void *mem) const override
Initalize memory block.
void add_offsets(SundialsMemory *m) const
Add stats offsets to stats.
void init(const Dict &opts) override
Initialize.
void save_offsets(SundialsMemory *m) const
Save stats offsets before reset.
static const Options options_
Options.
The casadi namespace.
Definition: archiver.cpp:28
std::ostream & uerr()
void casadi_project(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, T1 *w)
Sparse copy: y <- x, w work vector (length >= number of rows)
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void CASADI_INTEGRATOR_CVODES_EXPORT casadi_load_integrator_cvodes()
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
int CASADI_INTEGRATOR_CVODES_EXPORT casadi_register_integrator_cvodes(Integrator::Plugin *plugin)
void casadi_axpy(casadi_int n, T1 alpha, const T1 *x, T1 *y)
AXPY: y <- a*x + y.
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.
CvodesMemory(const CvodesInterface &s)
Constructor.
Options metadata for a class.
Definition: options.hpp:40
int mem_linsolF
Linear solver memory objects.
int ncheck
number of checkpoints stored so far