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  if (nrq_ > 0 || nuq_ > 0) {
339  THROWING(CVodeQuadInitB, m->mem, m->whichB, rhsQB, m->v_adj_pu);
340  if (quad_err_con_) {
341  THROWING(CVodeSetQuadErrConB, m->mem, m->whichB, true);
342  THROWING(CVodeQuadSStolerancesB, m->mem, m->whichB, reltol_, abstol_);
343  }
344  }
345 
346  // Mark initialized
347  m->first_callB = false;
348  } else {
349  // Save solver stats offsets before reset
350  save_offsets(m);
351 
352  // Reinitialize solver
353  THROWING(CVodeReInitB, m->mem, m->whichB, m->t, m->v_adj_xz);
354  if (nrq_ > 0 || nuq_ > 0) {
355  THROWING(CVodeQuadReInitB, m->mem, m->whichB, m->v_adj_pu);
356  }
357  }
358 }
359 
360 void CvodesInterface::retreat(IntegratorMemory* mem, const double* u,
361  double* adj_x, double* adj_p, double* adj_u) const {
362  auto m = to_mem(mem);
363 
364  // Set controls
365  casadi_copy(u, nu_, m->u);
366 
367  // Integrate, unless already at desired time
368  if (m->t_next < m->t) {
369  THROWING(CVodeB, m->mem, m->t_next, CV_NORMAL);
370  double tret;
371  THROWING(CVodeGetB, m->mem, m->whichB, &tret, m->v_adj_xz);
372  if (nrq_ > 0 || nuq_ > 0) {
373  THROWING(CVodeGetQuadB, m->mem, m->whichB, &tret, m->v_adj_pu);
374  }
375  }
376 
377  // Save outputs
378  casadi_copy(NV_DATA_S(m->v_adj_xz), nrx_, adj_x);
379  casadi_copy(NV_DATA_S(m->v_adj_pu), nrq_, adj_p);
380  casadi_copy(NV_DATA_S(m->v_adj_pu) + nrq_, nuq_, adj_u);
381 
382  // Get stats
383  CVodeMem cv_mem = static_cast<CVodeMem>(m->mem);
384  CVadjMem ca_mem = cv_mem->cv_adj_mem;
385  CVodeBMem cvB_mem = ca_mem->cvB_mem;
386  THROWING(CVodeGetIntegratorStats, cvB_mem->cv_mem, &m->nstepsB,
387  &m->nfevalsB, &m->nlinsetupsB, &m->netfailsB, &m->qlastB,
388  &m->qcurB, &m->hinusedB, &m->hlastB, &m->hcurB, &m->tcurB);
389  THROWING(CVodeGetNonlinSolvStats, cvB_mem->cv_mem, &m->nnitersB, &m->nncfailsB);
390 
391  // Add offset corresponding to counters that were set to zero at reinitializations
392  add_offsets(m);
393 }
394 
395 void CvodesInterface::cvodes_error(const char* module, int flag) {
396  // Successfull return or warning
397  if (flag>=CV_SUCCESS) return;
398  // Construct error message
399  char* flagname = CVodeGetReturnFlagName(flag);
400  std::stringstream ss;
401  ss << module << " returned \"" << flagname << "\". Consult CVODES documentation.";
402  free(flagname); // NOLINT
403  casadi_error(ss.str());
404 }
405 
406 void CvodesInterface::ehfun(int error_code, const char *module, const char *function,
407  char *msg, void *user_data) {
408  try {
409  casadi_assert_dev(user_data);
410  auto m = to_mem(user_data);
411  auto& s = m->self;
412  if (!s.disable_internal_warnings_) {
413  uerr() << msg << std::endl;
414  }
415  } catch(std::exception& e) {
416  uerr() << "ehfun failed: " << e.what() << std::endl;
417  }
418 }
419 
420 int CvodesInterface::rhsQF(double t, N_Vector x, N_Vector qdot, void *user_data) {
421  try {
422  auto m = to_mem(user_data);
423  auto& s = m->self;
424  if (s.calc_quadF(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(qdot))) return 1;
425 
426  return 0;
427  } catch(std::exception& e) { // non-recoverable error
428  uerr() << "rhsQ failed: " << e.what() << std::endl;
429  return -1;
430  }
431 }
432 
433 int CvodesInterface::rhsB(double t, N_Vector x, N_Vector rx, N_Vector rxdot, void *user_data) {
434  try {
435  casadi_assert_dev(user_data);
436  auto m = to_mem(user_data);
437  auto& s = m->self;
438  if (s.calc_daeB(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(rx), nullptr, m->adj_q,
439  NV_DATA_S(rxdot), nullptr)) return 1;
440  // Negate (note definition of g)
441  casadi_scal(s.nrx_, -1., NV_DATA_S(rxdot));
442  return 0;
443  } catch(std::exception& e) { // non-recoverable error
444  uerr() << "rhsB failed: " << e.what() << std::endl;
445  return -1;
446  }
447 }
448 
449 int CvodesInterface::rhsQB(double t, N_Vector x, N_Vector rx, N_Vector ruqdot, void *user_data) {
450  try {
451  casadi_assert_dev(user_data);
452  auto m = to_mem(user_data);
453  auto& s = m->self;
454  if (s.calc_quadB(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(rx), nullptr,
455  NV_DATA_S(ruqdot), NV_DATA_S(ruqdot) + s.nrq_)) return 1;
456 
457  // Negate (note definition of g)
458  casadi_scal((s.nrq_ + s.nuq_), -1., NV_DATA_S(ruqdot));
459 
460  return 0;
461  } catch(std::exception& e) { // non-recoverable error
462  uerr() << "rhsQB failed: " << e.what() << std::endl;
463  return -1;
464  }
465 }
466 
467 int CvodesInterface::jtimesF(N_Vector v, N_Vector Jv, double t, N_Vector x,
468  N_Vector xdot, void *user_data, N_Vector tmp) {
469  try {
470  auto m = to_mem(user_data);
471  auto& s = m->self;
472  if (s.calc_jtimesF(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(v), nullptr,
473  NV_DATA_S(Jv), nullptr)) return 1;
474  return 0;
475  } catch(std::exception& e) { // non-recoverable error
476  uerr() << "jtimesF failed: " << e.what() << std::endl;
477  return -1;
478  }
479 }
480 
481 int CvodesInterface::jtimesB(N_Vector v, N_Vector Jv, double t, N_Vector x,
482  N_Vector rx, N_Vector rxdot, void *user_data, N_Vector tmpB) {
483  try {
484  auto m = to_mem(user_data);
485  auto& s = m->self;
486  // The function is linear so the Jacobian-times-vector function is the function itself
487  if (s.calc_daeB(m, t, NV_DATA_S(x), nullptr, NV_DATA_S(v), nullptr, nullptr,
488  NV_DATA_S(Jv), nullptr)) return 1;
489  return 0;
490  } catch(std::exception& e) { // non-recoverable error
491  uerr() << "jtimesB failed: " << e.what() << std::endl;
492  return -1;
493  }
494 }
495 
496 int CvodesInterface::psolveF(double t, N_Vector x, N_Vector xdot, N_Vector r,
497  N_Vector z, double gamma, double delta, int lr, void *user_data, N_Vector tmp) {
498  try {
499  auto m = to_mem(user_data);
500  auto& s = m->self;
501 
502  // Get right-hand sides in m->tmp1
503  double* v = NV_DATA_S(r);
504  casadi_copy(v, s.nx_, m->tmp1);
505 
506  // Solve for undifferentiated right-hand-side, save to output
507  if (s.linsolF_.solve(m->jacF, m->tmp1, 1, false, m->mem_linsolF)) return 1;
508  v = NV_DATA_S(z); // possibly different from r
509  casadi_copy(m->tmp1, s.nx1_, v);
510 
511  // Sensitivity equations
512  if (s.nfwd_ > 0) {
513  // Second order correction
514  if (s.second_order_correction_) {
515  // The outputs will double as seeds for jtimesF
516  casadi_clear(v + s.nx1_, s.nx_ - s.nx1_);
517  if (s.calc_jtimesF(m, t, NV_DATA_S(x), nullptr, v, nullptr, m->tmp2, nullptr)) return 1;
518 
519  // Subtract m->tmp2 from m->tmp1, scaled with -gamma
520  casadi_axpy(s.nx_ - s.nx1_, m->gamma, m->tmp2 + s.nx1_, m->tmp1 + s.nx1_);
521  }
522 
523  // Solve for sensitivity right-hand-sides
524  if (s.linsolF_.solve(m->jacF, m->tmp1 + s.nx1_, s.nfwd_, false, m->mem_linsolF)) return 1;
525 
526  // Save to output, reordered
527  casadi_copy(m->tmp1 + s.nx1_, s.nx_ - s.nx1_, v + s.nx1_);
528  }
529 
530  return 0;
531  } catch(std::exception& e) { // non-recoverable error
532  uerr() << "psolve failed: " << e.what() << std::endl;
533  return -1;
534  }
535 }
536 
537 int CvodesInterface::psolveB(double t, N_Vector x, N_Vector xB, N_Vector xdotB, N_Vector rvecB,
538  N_Vector zvecB, double gammaB, double deltaB, int lr, void *user_data, N_Vector tmpB) {
539  try {
540  auto m = to_mem(user_data);
541  auto& s = m->self;
542 
543  // Get right-hand sides in m->tmp1
544  double* v = NV_DATA_S(rvecB);
545  casadi_copy(v, s.nrx_, m->tmp1);
546 
547  // Solve for undifferentiated right-hand-side, save to output
548  if (s.linsolF_.solve(m->jacF, m->tmp1, s.nadj_, true, m->mem_linsolF)) return 1;
549  v = NV_DATA_S(zvecB); // possibly different from rvecB
550  casadi_copy(m->tmp1, s.nrx1_ * s.nadj_, v);
551 
552  // Sensitivity equations
553  if (s.nfwd_ > 0) {
554  // Second order correction
555  if (s.second_order_correction_) {
556  // The outputs will double as seeds for calc_daeB
557  casadi_clear(v + s.nrx1_ * s.nadj_, s.nrx_ - s.nrx1_ * s.nadj_);
558  if (s.calc_daeB(m, t, NV_DATA_S(x), nullptr, v, nullptr, nullptr, m->tmp2, nullptr))
559  return 1;
560  // Subtract m->tmp2 from m->tmp1, scaled with gammaB
561  casadi_axpy(s.nrx_ - s.nrx1_ * s.nadj_, -m->gammaB, m->tmp2 + s.nrx1_ * s.nadj_,
562  m->tmp1 + s.nrx1_ * s.nadj_);
563  }
564 
565  // Solve for sensitivity right-hand-sides
566  if (s.linsolF_.solve(m->jacF, m->tmp1 + s.nx1_, s.nadj_ * s.nfwd_,
567  true, m->mem_linsolF)) return 1;
568 
569  // Save to output, reordered
570  casadi_copy(m->tmp1 + s.nx1_, s.nx_ - s.nx1_, v + s.nx1_);
571  }
572 
573  return 0;
574  } catch(std::exception& e) { // non-recoverable error
575  uerr() << "psolveB failed: " << e.what() << std::endl;
576  return -1;
577  }
578 }
579 
580 int CvodesInterface::psetupF(double t, N_Vector x, N_Vector xdot, booleantype jok,
581  booleantype *jcurPtr, double gamma, void *user_data,
582  N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) {
583  try {
584  auto m = to_mem(user_data);
585  auto& s = m->self;
586  // Store gamma for later
587  m->gamma = gamma;
588 
589  // Sparsity patterns
590  const Sparsity& sp_jac_ode_x = s.get_function("jacF").sparsity_out(0);
591  const Sparsity& sp_jacF = s.linsolF_.sparsity();
592 
593  // Calculate Jacobian, if necessary
594  if (s.always_recalculate_jacobian_ || !jcurPtr || *jcurPtr == 0) {
595  // Re(calculate) Jacobian
596  if (s.calc_jacF(m, t, NV_DATA_S(x), nullptr,
597  m->jac_ode_x, nullptr, nullptr, nullptr)) return 1;
598 
599  // Jacobian is now current
600  if (jcurPtr) *jcurPtr = 1;
601  }
602 
603  // Project to expected sparsity pattern (with diagonal)
604  casadi_project(m->jac_ode_x, sp_jac_ode_x, m->jacF, sp_jacF, m->w);
605 
606  // Scale and shift diagonal
607  const casadi_int *colind = sp_jacF.colind(), *row = sp_jacF.row();
608  for (casadi_int c = 0; c < sp_jacF.size2(); ++c) {
609  for (casadi_int k = colind[c]; k < colind[c + 1]; ++k) {
610  casadi_int r = row[k];
611  // Scale Jacobian
612  m->jacF[k] *= -gamma;
613  // Add contribution to diagonal
614  if (r == c) m->jacF[k] += 1;
615  }
616  }
617 
618  // Prepare the solution of the linear system (e.g. factorize)
619  if (s.linsolF_.nfact(m->jacF, m->mem_linsolF)) return 1;
620 
621  return 0;
622  } catch(std::exception& e) { // non-recoverable error
623  uerr() << "psetup failed: " << e.what() << std::endl;
624  return -1;
625  }
626 }
627 
628 int CvodesInterface::psetupB(double t, N_Vector x, N_Vector rx, N_Vector rxdot,
629  booleantype jokB, booleantype *jcurPtrB, double gammaB,
630  void *user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B) {
631  try {
632  auto m = to_mem(user_data);
633  // Store gamma for later
634  m->gammaB = gammaB;
635  // We use the same linear solver for the forward problem as for the backward problem
636  return psetupF(t, x, nullptr, jokB, jcurPtrB, -gammaB, user_data, tmp1B, tmp2B, tmp3B);
637 
638  } catch(std::exception& e) { // non-recoverable error
639  uerr() << "psetupB failed: " << e.what() << std::endl;
640  return -1;
641  }
642 }
643 
644 int CvodesInterface::lsetupF(CVodeMem cv_mem, int convfail, N_Vector x, N_Vector xdot,
645  booleantype *jcurPtr, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) {
646  try {
647  auto m = to_mem(cv_mem->cv_lmem);
648 
649  // Call the preconditioner setup function (which sets up the linear solver)
650  return psetupF(cv_mem->cv_tn, x, xdot, FALSE, jcurPtr,
651  cv_mem->cv_gamma, static_cast<void*>(m), vtemp1, vtemp2, vtemp3);
652 
653  } catch(std::exception& e) { // non-recoverable error
654  uerr() << "lsetup failed: " << e.what() << std::endl;
655  return -1;
656  }
657 }
658 
659 int CvodesInterface::lsetupB(CVodeMem cv_mem, int convfail, N_Vector x, N_Vector xdot,
660  booleantype *jcurPtr, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) {
661  try {
662  auto m = to_mem(cv_mem->cv_lmem);
663  CVadjMem ca_mem;
664  //CVodeBMem cvB_mem;
665 
666  // Current time
667  double t = cv_mem->cv_tn; // TODO(Joel): is this correct?
668  double gamma = cv_mem->cv_gamma;
669 
670  cv_mem = static_cast<CVodeMem>(cv_mem->cv_user_data);
671  ca_mem = cv_mem->cv_adj_mem;
672  //cvB_mem = ca_mem->ca_bckpbCrt;
673 
674  // Get FORWARD solution from interpolation.
675  int flag = ca_mem->ca_IMget(cv_mem, t, ca_mem->ca_ytmp, nullptr);
676  if (flag != CV_SUCCESS) casadi_error("Could not interpolate forward states");
677 
678  // Call the preconditioner setup function (which sets up the linear solver)
679  return psetupB(t, ca_mem->ca_ytmp, x, xdot, FALSE, jcurPtr,
680  gamma, static_cast<void*>(m), vtemp1, vtemp2, vtemp3);
681 
682  } catch(std::exception& e) { // non-recoverable error
683  uerr() << "lsetupB failed: " << e.what() << std::endl;
684  return -1;
685  }
686 }
687 
688 int CvodesInterface::lsolveF(CVodeMem cv_mem, N_Vector b, N_Vector weight,
689  N_Vector x, N_Vector xdot) {
690  try {
691  auto m = to_mem(cv_mem->cv_lmem);
692  //auto& s = m->self;
693 
694  // Current time
695  double t = cv_mem->cv_tn;
696 
697  // Scaling factor before J
698  double gamma = cv_mem->cv_gamma;
699 
700  // Accuracy
701  double delta = 0.0;
702 
703  // Left/right preconditioner
704  casadi_int lr = 1;
705 
706  // Call the preconditioner solve function (which solves the linear system)
707  return psolveF(t, x, xdot, b, b, gamma, delta, lr, static_cast<void*>(m), nullptr);
708 
709  } catch(std::exception& e) { // non-recoverable error
710  uerr() << "lsolveF failed: " << e.what() << std::endl;
711  return -1;
712  }
713 }
714 
715 int CvodesInterface::lsolveB(CVodeMem cv_mem, N_Vector b, N_Vector weight,
716  N_Vector x, N_Vector xdot) {
717  try {
718  auto m = to_mem(cv_mem->cv_lmem);
719  CVadjMem ca_mem;
720  //CVodeBMem cvB_mem;
721 
722  // Current time
723  double t = cv_mem->cv_tn; // TODO(Joel): is this correct?
724  double gamma = cv_mem->cv_gamma;
725 
726  cv_mem = static_cast<CVodeMem>(cv_mem->cv_user_data);
727 
728  ca_mem = cv_mem->cv_adj_mem;
729  //cvB_mem = ca_mem->ca_bckpbCrt;
730 
731  // Get FORWARD solution from interpolation.
732  int flag = ca_mem->ca_IMget(cv_mem, t, ca_mem->ca_ytmp, nullptr);
733  if (flag != CV_SUCCESS) casadi_error("Could not interpolate forward states");
734 
735  // Accuracy
736  double delta = 0.0;
737 
738  // Left/right preconditioner
739  int lr = 1;
740 
741  // Call the preconditioner solve function (which solves the linear system)
742  return psolveB(t, ca_mem->ca_ytmp, x, xdot, b, b, gamma, delta, lr,
743  static_cast<void*>(m), nullptr);
744 
745  } catch(std::exception& e) { // non-recoverable error
746  uerr() << "lsolveB failed: " << e.what() << std::endl;
747  return -1;
748  }
749 }
750 
752  this->mem = nullptr;
753 
754  // Reset checkpoints counter
755  this->ncheck = 0;
756 }
757 
759  if (this->mem_linsolF >= 0) self.linsolF_.release(this->mem_linsolF);
760  if (this->mem) CVodeFree(&this->mem);
761 }
762 
764  int version = s.version("CvodesInterface", 1, 3);
765  s.unpack("CvodesInterface::lmm", lmm_);
766  s.unpack("CvodesInterface::iter", iter_);
767 
768  if (version>=2) {
769  s.unpack("CvodesInterface::min_step_size", min_step_size_);
770  } else {
771  min_step_size_ = 0;
772  }
773 
774  if (version >= 3) {
775  s.unpack("CvodesInterface::always_recalculate_jacobian", always_recalculate_jacobian_);
776  }
777 }
778 
781  s.version("CvodesInterface", 3);
782 
783  s.pack("CvodesInterface::lmm", lmm_);
784  s.pack("CvodesInterface::iter", iter_);
785  s.pack("CvodesInterface::min_step_size", min_step_size_);
786  s.pack("CvodesInterface::always_recalculate_jacobian", always_recalculate_jacobian_);
787 }
788 
789 } // 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