runtime_integrator.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2026 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 "runtime_integrator.hpp"
27 #include "casadi_misc.hpp"
28 
29 namespace casadi {
30 
31 RuntimeIntegrator::RuntimeIntegrator(const std::string& name, const Function& dae, double t0,
32  const std::vector<double>& tout) : Integrator(name, dae, t0, tout) {
33 }
34 
36 }
37 
40  {{"control_group_size",
41  {OT_INTVECTOR,
42  "Number of output intervals in each control group, over which the controls, one "
43  "column each, are constant: a run of steps [default: 1 for each output interval]"}},
44  {"step_size",
45  {OT_DOUBLE,
46  "Fixed steps: each run of steps, over a control group or up to the end without "
47  "controls, is divided into equal steps of at most step_size [0: adaptive steps]"}},
48  {"abstol",
49  {OT_DOUBLE,
50  "Absolute tolerance [1e-8]"}},
51  {"reltol",
52  {OT_DOUBLE,
53  "Relative tolerance [1e-6]"}},
54  {"max_num_steps",
55  {OT_INT,
56  "Adaptive steps: maximum number of steps per output interval [10000]"}},
57  {"step0",
58  {OT_DOUBLE,
59  "Adaptive steps: initial step size [default: estimated]"}},
60  {"max_step_size",
61  {OT_DOUBLE,
62  "Adaptive steps: maximum step size [default: unbounded]"}},
63  {"min_step_size",
64  {OT_DOUBLE,
65  "Adaptive steps: minimum step size [1e-14]"}},
66  {"quad_err_con",
67  {OT_BOOL,
68  "Adaptive steps: include the quadratures in the error control [true]"}},
69  {"max_tape_steps",
70  {OT_INT,
71  "Adaptive steps: capacity, in steps, of a tape in the work vector, for adjoint "
72  "sensitivities; needed for code generation [0: tape of unbounded size]. "
73  "With fixed steps, the tape is sized exactly."}}
74  }
75 };
76 
77 void RuntimeIntegrator::init(const Dict& opts) {
78  // Control groups, before the base class sizes the controls; a single one without controls
79  group_start_ = range(nt() + 1);
80  if (oracle_.numel_in(DYN_U) == 0) group_start_ = {0, nt()};
81  auto cgs = opts.find("control_group_size");
82  if (cgs != opts.end()) {
83  group_start_ = {0};
84  for (casadi_int n : cgs->second.to_int_vector()) {
85  casadi_assert(n > 0, "'control_group_size' entries must be positive");
86  group_start_.push_back(group_start_.back() + n);
87  }
88  casadi_assert(group_start_.back() == nt(), "'control_group_size' must sum to the number "
89  "of output times, " + str(nt()) + ", got " + str(group_start_.back()));
90  }
91 
92  // Call the base class init
93  Integrator::init(opts);
94 
95  // Controls and control adjoints by output interval, for sparsity propagation
96  if (ngroup() < nt()) alloc_w((nu_ + nuq_) * nt(), true);
97 
98  // Default options
99  step_size_ = 0;
100  abstol_ = 1e-8;
101  reltol_ = 1e-6;
102  max_num_steps_ = 10000;
103  step0_ = 0;
104  max_step_size_ = 0;
105  min_step_size_ = 1e-14;
106  quad_err_con_ = true;
107  max_tape_steps_ = 0;
108 
109  // Read options
110  for (auto&& op : opts) {
111  if (op.first == "step_size") {
112  step_size_ = op.second;
113  } else if (op.first == "abstol") {
114  abstol_ = op.second;
115  } else if (op.first == "reltol") {
116  reltol_ = op.second;
117  } else if (op.first == "max_num_steps") {
118  max_num_steps_ = op.second;
119  } else if (op.first == "step0") {
120  step0_ = op.second;
121  } else if (op.first == "max_step_size") {
122  max_step_size_ = op.second;
123  } else if (op.first == "min_step_size") {
124  min_step_size_ = op.second;
125  } else if (op.first == "quad_err_con") {
126  quad_err_con_ = op.second;
127  } else if (op.first == "max_tape_steps") {
128  max_tape_steps_ = op.second;
129  }
130  }
131  casadi_assert(step_size_ >= 0, "'step_size' must be nonnegative");
132 
133  // Error control on the nominal states only: derivative integrators share the mesh
134  if (derivative_of_.is_null()) {
135  nx_err_ = nx1_;
136  nz_err_ = nz1_;
137  nq_err_ = nq1_;
138  } else {
140  casadi_assert_dev(d != nullptr);
141  nx_err_ = d->nx_err_;
142  nz_err_ = d->nz_err_;
143  nq_err_ = d->nq_err_;
144  }
145 
146  // Right-hand side [ode; alg; quad] and its vector-Jacobian product, one function each
147  create_function("rhsF", dyn_in(), {"ode", "alg", "quad"});
148  if (nadj_ > 0) {
149  create_function(rdae_, "vjpB", bdyn_in(), {"adj_x", "adj_z", "adj_p", "adj_u"});
150  }
151 
152  // Zero-crossing functions and their time derivative; the jump at an event, its derivatives
153  if (ne_ > 0) {
154  create_function("zeroF", dyn_in(), {"zero"});
155  create_forward("zeroF", 1);
156  if (has_function("transition")) {
157  Function jump = jump_function();
158  set_function(jump, "jumpF");
159  if (nfwd_ > 0) create_forward("jumpF", nfwd_);
160  if (nadj_ > 0) {
161  // Adjoint seeds of x, z, q to adjoints of x, z, p, u
162  std::vector<MX> in = jump.mx_in(), out = jump(in), seed;
163  for (casadi_int i = 0; i < jump.n_out(); ++i) {
164  Sparsity sp = repmat(jump.sparsity_out(i), 1, nadj_);
165  seed.push_back(MX::sym("adj_" + jump.name_out(i), sp));
166  }
167  std::vector<MX> arg = in;
168  arg.insert(arg.end(), out.begin(), out.end());
169  arg.insert(arg.end(), seed.begin(), seed.end());
170  std::vector<MX> adj = jump.reverse(nadj_)(arg);
171  in.insert(in.end(), seed.begin(), seed.end());
172  set_function(Function("jumpB", in, {adj[1], adj[2], adj[4], adj[5]}), "jumpB");
173  if (nfwd_ > 0) create_forward("jumpB", nfwd_);
174  }
175  }
176  }
177 
178  // Jacobian blocks w.r.t. x, z and time, in the order of casadi_ivp_jac_t
179  sp_jac_.clear();
180  if (needs_jacobian()) {
181  Function jac = create_function("jacF", {"t", "x", "z", "p", "u"},
182  {"jac:ode:x", "jac:alg:x", "jac:quad:x", "jac:ode:z", "jac:alg:z", "jac:quad:z",
183  "jac:ode:t", "jac:alg:t", "jac:quad:t"});
184  for (casadi_int k = 0; k < IVP_NUM_JAC; ++k) sp_jac_.push_back(jac.sparsity_out(k));
185  if (nadj_ > 0) set_function(jac.reverse(nadj_), "jacB");
186  }
187 
188  // Forward sensitivities of all of these
189  if (nfwd_ > 0) {
190  create_forward("rhsF", nfwd_);
191  if (nadj_ > 0) create_forward("vjpB", nfwd_);
192  if (needs_jacobian()) {
193  create_forward("jacF", nfwd_);
194  if (nadj_ > 0) create_forward("jacB", nfwd_);
195  }
196  }
197 }
198 
200  // State after the event selected by w as a function of the state before it: moved along
201  // the trajectory to the event surface, through the transition and back to t, to second
202  // order in the time s to the surface. At an event s vanishes, so the value is that of the
203  // transition and the first and second derivatives are those of the event, including the
204  // dependence of its time. Algebraic states are consistent along the way: the given ones
205  // are only starting points
206  MX t = MX::sym("t"), s = MX::sym("s"), w = MX::sym("w", ne_), q = MX::sym("q", nq1_);
209  const Function& tr = get_function("transition");
210  // Argument i of f, empty if f does not depend on it
211  auto arg = [](const Function& f, casadi_int i, const MX& v) -> MX {
212  return f.nnz_in(i) == 0 ? MX(f.sparsity_in(i)) : v;
213  };
214  auto dae = [&](const MX& tt, const MX& xx, const MX& zz) -> std::vector<MX> {
215  return oracle_(std::vector<MX>{arg(oracle_, DYN_T, tt), xx, zz, p, u});
216  };
217  // Algebraic states consistent with (tt, xx), from zz
218  Function z_solve;
219  if (nz1_ > 0) {
220  MX zs = MX::sym("z", z.sparsity()), ts = MX::sym("t"), xs = MX::sym("x", x.sparsity());
221  Function alg("alg", {zs, ts, xs, p, u}, {dae(ts, xs, zs)[DYN_ALG]});
222  z_solve = rootfinder("z_consistent", "fast_newton", alg,
223  Dict{{"abstol", 1e-14}, {"abstolStep", 1e-14}, {"max_iter", 50}});
224  }
225  auto consistent = [&](const MX& tt, const MX& xx, const MX& zz) -> MX {
226  return nz1_ == 0 ? zz : z_solve(std::vector<MX>{zz, tt, xx, p, u}).at(0);
227  };
228  // Rates of x, q at (tt, xx) and their derivatives along the trajectory
229  auto rates = [&](const MX& tt, const MX& xx, const MX& zz, std::vector<MX>& r) -> void {
230  std::vector<MX> r0 = dae(tt, xx, consistent(tt, xx, zz));
231  MX x1 = xx + s * r0[DYN_ODE];
232  std::vector<MX> r1 = dae(tt + s, x1, consistent(tt + s, x1, zz));
233  r = MX::substitute({r0[DYN_ODE], r0[DYN_QUAD], MX::jacobian(r1[DYN_ODE], s),
234  MX::jacobian(r1[DYN_QUAD], s)}, {s}, {MX(0)});
235  };
236  // Time to the surface of the selected zero-crossing function
237  std::vector<MX> r;
238  rates(t, x, z, r);
239  MX x_s = x + s * r[0] + 0.5 * s * s * r[2];
240  MX g = MX::dot(w, dae(t + s, x_s, consistent(t + s, x_s, z))[DYN_ZERO]);
241  MX g_s = MX::jacobian(g, s);
242  std::vector<MX> gv = MX::substitute({g, g_s, MX::jacobian(g_s, s)}, {s}, {MX(0)});
243  MX s1 = -gv[0] / gv[1];
244  MX ss = s1 - 0.5 * gv[2] * s1 * s1 / gv[1];
245  // Transition on the surface
246  std::vector<double> index(ne_);
247  for (casadi_int i = 0; i < ne_; ++i) index[i] = static_cast<double>(i);
248  MX x_ev = x + ss * r[0] + 0.5 * ss * ss * r[2];
249  MX q_ev = q + ss * r[1] + 0.5 * ss * ss * r[3];
250  std::vector<MX> post = tr(std::vector<MX>{arg(tr, EVENT_INDEX, MX::dot(w, MX(DM(index)))),
251  arg(tr, EVENT_T, t + ss), x_ev, consistent(t + ss, x_ev, z), arg(tr, EVENT_P, p),
252  arg(tr, EVENT_U, u)});
253  // Back to t along the trajectory after the event
254  MX z_post = consistent(t + ss, post[EVENT_POST_X], post[EVENT_POST_Z]);
255  rates(t + ss, post[EVENT_POST_X], z_post, r);
256  MX x_post = post[EVENT_POST_X] - ss * r[0] + 0.5 * ss * ss * r[2];
257  MX q_post = q_ev - ss * r[1] + 0.5 * ss * ss * r[3];
258  return Function("jumpF", {t, x, z, q, p, u, w}, {x_post, z_post, q_post},
259  {"t", "x", "z", "q", "p", "u", "w"}, {"post_x", "post_z", "post_q"});
260 }
261 
263  // Integrator at the start of the chain of derivatives
264  const RuntimeIntegrator* r = this;
266  return r;
267 }
268 
270  // Problem structure for the shared part of the kernel
271  p_.nx1 = nx1_;
272  p_.nz1 = nz1_;
273  p_.nq1 = nq1_;
274  p_.np1 = np1_;
275  p_.nu1 = nu1_;
276  p_.nfwd = nfwd_;
277  p_.nadj = nadj_;
278  p_.n_stages = alpha_.size();
279  p_.a = get_ptr(a_);
280  p_.alpha = get_ptr(alpha_);
281  p_.m = get_ptr(m_);
282  p_.n_dense = n_dense_;
284  for (casadi_int k = 0; k < IVP_NUM_JAC; ++k) {
285  p_.sp_jac[k] = sp_jac_.empty() ? nullptr : static_cast<const casadi_int*>(sp_jac_[k]);
286  }
289  p_.nx_err = nx_err_;
290  p_.nz_err = nz_err_;
291  p_.nq_err = quad_err_con_ ? nq_err_ : 0;
292  p_.abstol = abstol_;
293  p_.reltol = reltol_;
295  p_.m_err = m_err_.empty() ? nullptr : get_ptr(m_err_);
296  p_.safety = 0.9;
297  p_.fac_min = 0.2;
298  p_.fac_max = 6;
301  p_.h_init = step0_;
303  p_.ne = ne_;
304  p_.has_transition = has_function("transition");
309  casadi_ivp_setup(&p_);
310  calls_ = task_calls("");
311 }
312 
314  // Forward directions folded into the DAE change the arithmetic of the nominal states: with
315  // adaptive steps, replay the steps of a nominal integration with the original functions
316  const RuntimeIntegrator* r = root();
317  if (!has_function("rhsF_nom") && r != this && r->nx1_ != nx1_ && step_size_ == 0) {
318  for (auto&& calls : r->task_calls("")) {
319  for (auto&& c : calls) {
320  std::string fname = c.fname + "_nom";
321  if (!has_function(fname)) set_function(r->get_function(c.fname), fname);
322  }
323  }
324  }
325  replay_ = has_function("rhsF_nom");
326  if (!replay_) return;
327  calls_nom_ = root()->task_calls("_nom");
328  p_nom_ = root()->p_;
329  p_nom_.tape_size = 0;
330  casadi_ivp_setup(&p_nom_);
331 }
332 
334  // Shared kernel data, and of the nominal integration
335  casadi_int sz_arg = 0, sz_res = 0, sz_iw = 0, sz_w = 0;
336  casadi_ivp_work(&p_, &sz_arg, &sz_res, &sz_iw, &sz_w);
337  if (replay_) casadi_ivp_work(&p_nom_, &sz_arg, &sz_res, &sz_iw, &sz_w);
338  alloc_w(sz_w, true);
339  // Parameters and controls in generated code
340  alloc_w(np_ + nu_, true);
341 }
342 
344  // Steps: a user bound with adaptive steps, a run of steps per control group with fixed steps
345  casadi_int ny = nx_ + nz_ + nq_, n_step = max_tape_steps_, n_init, n_event = 0;
346  if (step_size_ > 0) {
347  n_step = 0;
348  for (casadi_int j = 0; j < ngroup(); ++j) {
349  double t = j == 0 ? t0_ : tout_[group_start_[j] - 1];
350  n_step += casadi_ivp_steps(tout_[group_start_[j + 1] - 1] - t, step_size_);
351  }
352  }
353  if (nadj_ == 0 || n_step == 0) return 0;
354  // Every output interval may start with a consistent initialization
355  n_init = nz_ > 0 ? nt() : 0;
356  if (ne_ > 0) {
357  // Event records, room for one more with each step; after an event, a consistent
358  // initialization and, with fixed steps, a new run of steps
359  n_event = max_events_ + 1;
360  if (nz_ > 0) n_init += max_events_;
361  if (step_size_ > 0) n_step += max_events_;
362  }
363  return n_step * (ny * (static_cast<casadi_int>(alpha_.size()) + 1) + nu_ + 5)
364  + (n_init + n_event) * (ny + nu_ + 5);
365 }
366 
367 void RuntimeIntegrator::set_work(void* mem, const double**& arg, double**& res,
368  casadi_int*& iw, double*& w) const {
369  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
370  Integrator::set_work(mem, arg, res, iw, w);
371  m->d.prob = &p_;
372  casadi_ivp_set_work(&m->d, &arg, &res, &iw, &w);
373  if (replay_) {
374  m->d_nom.prob = &p_nom_;
375  casadi_ivp_set_work(&m->d_nom, &arg, &res, &iw, &w);
376  m->d.nom = &m->d_nom;
377  }
378 }
379 
380 int RuntimeIntegrator::init_mem(void* mem) const {
381  if (Integrator::init_mem(mem)) return 1;
382  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
383  casadi_ivp_clear_stats(&m->d);
384  return 0;
385 }
386 
388  // Carry out the tasks requested by the kernel, for it or for the nominal integration
389  while (kernel(m)) {
391  if (a == &m->d ? task(m) : eval_task(m, a, calls_nom_)) a->fail = 1;
392  }
393 }
394 
395 void RuntimeIntegrator::reset(IntegratorMemory* mem, bool first_call) const {
396  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
397  casadi_ivp_data<double>* d = &m->d;
398  // State y = [x; z; q]
399  casadi_copy(m->x, nx_, d->y);
400  casadi_copy(m->z, nz_, d->y + nx_);
401  casadi_copy(m->q, nq_, d->y + nx_ + nz_);
402  casadi_ivp_reset(d, m->t, m->u, first_call);
403 }
404 
406  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
407  casadi_ivp_data<double>* d = &m->d;
408  casadi_ivp_advance(d, m->t_next, m->t_stop, nrx_ > 0);
409  run(m);
410  casadi_assert(d->status != IVP_TAPE_FAILED, name_ + ": the tape of " + str(max_tape_steps_)
411  + " steps is full at t = " + str(d->t) + ", increase max_tape_steps");
412  casadi_assert(d->status != IVP_TOO_MANY_EVENTS, name_ + ": more than max_events = "
413  + str(max_events_) + " events, the last at t = " + str(d->t_ev));
414  casadi_assert(d->status != IVP_EVENT_FAILED, name_ + ": event near t = " + str(d->t_ev)
415  + " not located within event_acceptable_tol");
416  if (d->status != IVP_SUCCESS) {
417  if (verbose_) {
418  casadi_warning(name_ + ": integration failed at t = " + str(d->t) + ", status "
419  + str(static_cast<casadi_int>(d->status)));
420  }
421  return 1;
422  }
423  casadi_copy(d->y_out, nx_, m->x);
424  casadi_copy(d->y_out + nx_, nz_, m->z);
425  casadi_copy(d->y_out + nx_ + nz_, nq_, m->q);
426  return 0;
427 }
428 
430  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
431  casadi_ivp_reset_backward(&m->d);
432 }
433 
435  const double* adj_x, const double* adj_z, const double* adj_q) const {
436  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
437  double* seed = m->d.seed;
438  casadi_copy(adj_x, nrx_, seed);
439  casadi_copy(adj_z, nrz_, seed + nrx_);
440  casadi_copy(adj_q, nrp_, seed + nrx_ + nrz_);
441  casadi_ivp_impulse(&m->d, m->t);
442  run(m);
443  casadi_assert(m->d.status == IVP_SUCCESS, name_ + ": adjoint seed at t = "
444  + str(m->t) + " failed, status " + str(static_cast<casadi_int>(m->d.status)));
445 }
446 
448  if (i == INTEGRATOR_U) return Sparsity::dense(nu1_, ngroup() * (1 + nfwd_));
449  return Integrator::get_sparsity_in(i);
450 }
451 
453  if (i == INTEGRATOR_ADJ_U) return Sparsity::dense(nuq1_, nadj_ * (1 + nfwd_) * ngroup());
455 }
456 
457 int RuntimeIntegrator::eval(const double** arg, double** res,
458  casadi_int* iw, double* w, void* mem) const {
459  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
460  const double* u = arg[INTEGRATOR_U];
461  const double* adj_xf = arg[INTEGRATOR_ADJ_XF];
462  const double* adj_zf = arg[INTEGRATOR_ADJ_ZF];
463  const double* adj_qf = arg[INTEGRATOR_ADJ_QF];
464  double* xf = res[INTEGRATOR_XF];
465  double* zf = res[INTEGRATOR_ZF];
466  double* qf = res[INTEGRATOR_QF];
467  double* adj_u = res[INTEGRATOR_ADJ_U];
468  setup(m, arg + INTEGRATOR_NUM_IN, res + INTEGRATOR_NUM_OUT, iw, w);
469  set_q(m, nullptr);
470  set_x(m, arg[INTEGRATOR_X0]);
471  set_z(m, arg[INTEGRATOR_Z0]);
472  set_p(m, arg[INTEGRATOR_P]);
473  m->num_events = 0;
474  m->t = t0_;
475  // Forward integration: a run of steps per control group
476  for (casadi_int j = 0; j < ngroup(); ++j) {
477  set_u(m, u ? u + j * nu_ : nullptr);
478  m->t_stop = tout_[group_start_[j + 1] - 1];
479  reset(m, j == 0);
480  for (m->k = group_start_[j]; m->k < group_start_[j + 1]; ++m->k) {
481  m->t_next = tout_[m->k];
482  if (advance_noevent(m)) return 1;
483  m->t = m->t_next;
484  get_x(m, xf ? xf + m->k * nx_ : nullptr);
485  get_z(m, zf ? zf + m->k * nz_ : nullptr);
486  get_q(m, qf ? qf + m->k * nq_ : nullptr);
487  }
488  }
489  // Discrete adjoint, output interval by output interval
490  if (nrx_ > 0) {
491  resetB(m);
492  for (casadi_int j = ngroup(); j-- > 0; ) {
493  // Control adjoints of this group only
494  casadi_clear(m->d.adj_u, nuq_);
495  for (m->k = group_start_[j + 1]; m->k-- > group_start_[j]; ) {
496  m->t = tout_[m->k];
497  impulseB(m, adj_xf ? adj_xf + m->k * nrx_ : nullptr,
498  adj_zf ? adj_zf + m->k * nrz_ : nullptr, adj_qf ? adj_qf + m->k * nrp_ : nullptr);
499  m->t_next = m->k > 0 ? tout_[m->k - 1] : t0_;
500  retreat(m, nullptr, m->k == 0 ? res[INTEGRATOR_ADJ_X0] : nullptr,
501  m->k == 0 ? res[INTEGRATOR_ADJ_P] : nullptr,
502  adj_u && m->k == group_start_[j] ? adj_u + j * nuq_ : nullptr);
503  }
504  }
505  }
506  join_results(m);
507  if (print_stats_) print_stats(m);
508  return 0;
509 }
510 
511 namespace {
512  // Dependencies with a column per control group to one with a column per output interval
513  void groups_to_intervals(const std::vector<casadi_int>& gs, const bvec_t* g, bvec_t* k,
514  casadi_int n) {
515  for (size_t j = 0; j + 1 < gs.size(); ++j) {
516  for (casadi_int i = gs[j]; i < gs[j + 1]; ++i) std::copy_n(g + j * n, n, k + i * n);
517  }
518  }
519  // Dependencies with a column per output interval added to one with a column per control group
520  void intervals_to_groups(const std::vector<casadi_int>& gs, const bvec_t* k, bvec_t* g,
521  casadi_int n) {
522  for (size_t j = 0; j + 1 < gs.size(); ++j) {
523  for (casadi_int i = gs[j]; i < gs[j + 1]; ++i) {
524  for (casadi_int l = 0; l < n; ++l) g[j * n + l] |= k[i * n + l];
525  }
526  }
527  }
528 } // namespace
529 
531  casadi_int* iw, bvec_t* w, void* mem) const {
532  if (ne_ > 0 || ngroup() == nt()) return Integrator::sp_forward(arg, res, iw, w, mem);
533  // Controls and control adjoints by output interval
534  const bvec_t* u = arg[INTEGRATOR_U];
535  bvec_t* adj_u = res[INTEGRATOR_ADJ_U];
536  bvec_t* u_k = w; w += nu_ * nt();
537  bvec_t* adj_u_k = w; w += nuq_ * nt();
538  if (u) groups_to_intervals(group_start_, u, u_k, nu_);
539  arg[INTEGRATOR_U] = u ? u_k : nullptr;
540  res[INTEGRATOR_ADJ_U] = adj_u ? adj_u_k : nullptr;
541  int flag = Integrator::sp_forward(arg, res, iw, w, mem);
542  arg[INTEGRATOR_U] = u;
543  res[INTEGRATOR_ADJ_U] = adj_u;
544  if (adj_u) {
545  std::fill_n(adj_u, nuq_ * ngroup(), 0);
546  intervals_to_groups(group_start_, adj_u_k, adj_u, nuq_);
547  }
548  return flag;
549 }
550 
552  casadi_int* iw, bvec_t* w, void* mem) const {
553  if (ne_ > 0 || ngroup() == nt()) return Integrator::sp_reverse(arg, res, iw, w, mem);
554  // Controls and control adjoints by output interval
555  bvec_t* u = arg[INTEGRATOR_U];
556  bvec_t* adj_u = res[INTEGRATOR_ADJ_U];
557  bvec_t* u_k = w; w += nu_ * nt();
558  bvec_t* adj_u_k = w; w += nuq_ * nt();
559  std::fill_n(u_k, nu_ * nt(), 0);
560  if (adj_u) {
561  groups_to_intervals(group_start_, adj_u, adj_u_k, nuq_);
562  std::fill_n(adj_u, nuq_ * ngroup(), 0);
563  }
564  arg[INTEGRATOR_U] = u ? u_k : nullptr;
565  res[INTEGRATOR_ADJ_U] = adj_u ? adj_u_k : nullptr;
566  int flag = Integrator::sp_reverse(arg, res, iw, w, mem);
567  arg[INTEGRATOR_U] = u;
568  res[INTEGRATOR_ADJ_U] = adj_u;
569  if (u) intervals_to_groups(group_start_, u_k, u, nu_);
570  return flag;
571 }
572 
573 void RuntimeIntegrator::retreat(IntegratorMemory* mem, const double* u,
574  double* adj_x, double* adj_p, double* adj_u) const {
575  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
576  casadi_ivp_data<double>* d = &m->d;
577  // The controls of each step are taped
578  casadi_ivp_retreat(d, m->t, m->t_next);
579  run(m);
580  casadi_assert(d->status == IVP_SUCCESS,
581  name_ + ": adjoint sweep failed, status " + str(static_cast<casadi_int>(d->status)));
582  casadi_copy(d->ry, nrx_, adj_x);
583  casadi_copy(d->adj_p, nrq_, adj_p);
584  casadi_copy(d->adj_u, nuq_, adj_u);
585 }
586 
587 int RuntimeIntegrator::task(RuntimeIntegratorMemory* m) const {
588  casadi_ivp_data<double>* d = &m->d;
589  if (d->task == IVP_TAPE_RESERVE) {
590  // The tape persists between evaluations: grow it only when too small
591  if (m->tape.size() < static_cast<size_t>(d->tape_need)) {
592  m->tape.resize(std::max(static_cast<size_t>(d->tape_need), 2 * m->tape.size()));
593  }
594  d->tape = get_ptr(m->tape);
595  d->tape_cap = m->tape.size();
596  return 0;
597  }
598  return eval_task(m, d, calls_);
599 }
600 
601 int RuntimeIntegrator::eval_task(RuntimeIntegratorMemory* m, casadi_ivp_data<double>* d,
602  const std::vector<std::vector<TaskCall>>& calls) const {
603  // Adaptive step size control, or function evaluations as described by the task table
604  if (d->task == IVP_STEP_CONTROL) {
605  casadi_ivp_control(d);
606  return 0;
607  }
608  if (d->task == IVP_EVENT) {
609  while (casadi_ivp_event(d)) {
610  if (eval_task(m, d, calls)) d->fail = 1;
611  }
612  return 0;
613  }
614  if (d->task == IVP_EVENT_ADJ) {
615  while (casadi_ivp_event_adj(d)) {
616  if (eval_task(m, d, calls)) d->fail = 1;
617  }
618  return 0;
619  }
620  for (const TaskCall& c : calls.at(d->task)) {
621  std::fill_n(m->arg, c.n_in, nullptr);
622  std::fill_n(m->res, c.n_out, nullptr);
623  for (const Slot& e : c.arg) m->arg[e.i] = slot_ptr(m, d, e);
624  for (const Slot& e : c.res) m->res[e.i] = slot_ptr(m, d, e);
625  if (calc_function(m, c.fname)) return 1;
626  }
627  return 0;
628 }
629 
630 double* RuntimeIntegrator::slot_ptr(RuntimeIntegratorMemory* m, casadi_ivp_data<double>* d,
631  const Slot& e) const {
632  // Buffer of a function argument or result, as slot_expr in generated code
633  double* b = nullptr;
634  switch (e.src) {
635  case SRC_T: b = &d->t_arg; break;
636  case SRC_Y: b = const_cast<double*>(d->y_arg); break;
637  case SRC_P: b = m->p; break;
638  case SRC_U: b = d->u_step; break;
639  case SRC_RES: b = d->res; break;
640  case SRC_W: b = const_cast<double*>(d->w); break;
641  case SRC_JAC: b = d->jac; break;
642  case SRC_JAC_FWD: b = d->jac_fwd; break;
643  case SRC_JAC_ADJ: b = d->jac_adj; break;
644  case SRC_JAC_ADJ_FWD: b = d->jac_adj_fwd; break;
645  case SRC_VJP_X: b = d->vjp_x; break;
646  case SRC_VJP_Z: b = d->vjp_z; break;
647  case SRC_VJP_P: b = d->vjp_p; break;
648  case SRC_VJP_U: b = d->vjp_u; break;
649  case SRC_EV_ONE: b = &d->ev_one; break;
650  case SRC_EV_W: b = d->ev_w; break;
651  case SRC_YDOT: b = d->ydot; break;
652  case SRC_EDOT: b = d->edot; break;
653  }
654  return b + e.off;
655 }
656 
657 std::string RuntimeIntegrator::slot_expr(const std::string& d, const Slot& e) {
658  // Buffer of a function argument or result in generated code, as slot_ptr
659  std::string b;
660  switch (e.src) {
661  case SRC_T: b = "&" + d + ".t_arg"; break;
662  case SRC_Y: b = d + ".y_arg"; break;
663  case SRC_P: b = "pv"; break;
664  case SRC_U: b = d + ".u_step"; break;
665  case SRC_RES: b = d + ".res"; break;
666  case SRC_W: b = d + ".w"; break;
667  case SRC_JAC: b = d + ".jac"; break;
668  case SRC_JAC_FWD: b = d + ".jac_fwd"; break;
669  case SRC_JAC_ADJ: b = d + ".jac_adj"; break;
670  case SRC_JAC_ADJ_FWD: b = d + ".jac_adj_fwd"; break;
671  case SRC_VJP_X: b = d + ".vjp_x"; break;
672  case SRC_VJP_Z: b = d + ".vjp_z"; break;
673  case SRC_VJP_P: b = d + ".vjp_p"; break;
674  case SRC_VJP_U: b = d + ".vjp_u"; break;
675  case SRC_EV_ONE: b = "&" + d + ".ev_one"; break;
676  case SRC_EV_W: b = d + ".ev_w"; break;
677  case SRC_YDOT: b = d + ".ydot"; break;
678  case SRC_EDOT: b = d + ".edot"; break;
679  }
680  return e.off == 0 ? b : b + "+" + str(e.off);
681 }
682 
683 std::vector<std::vector<RuntimeIntegrator::TaskCall>> RuntimeIntegrator::task_calls(
684  const std::string& suffix) const {
685  // For each task, the functions to call and the buffers of their arguments and results
686  std::vector<std::vector<TaskCall>> calls(IVP_TAPE_RESERVE);
687  static_assert(static_cast<int>(BDYN_U) == static_cast<int>(DYN_U)
688  && static_cast<int>(BDYN_T) == static_cast<int>(DYN_T),
689  "Backward DAE inputs start with t, x, z, p, u");
690  const casadi_int nj = IVP_NUM_JAC;
691  // Results of the adjoint functions: adjoints of x, z, p, u and their forward derivatives
692  const Src vjp[4] = {SRC_VJP_X, SRC_VJP_Z, SRC_VJP_P, SRC_VJP_U};
693  const casadi_int n_vjp[4] = {nrx1_ * nadj_, nrz1_ * nadj_, nrq1_ * nadj_, nuq1_ * nadj_};
694  auto call = [&](const std::string& fname) -> TaskCall {
695  const Function& f = get_function(fname);
696  return {fname + suffix, f.n_in(), f.n_out(), {}, {}};
697  };
698  // Inputs t, x, z, p, u at offset i; forward seeds are the sensitivity parts
699  auto dyn = [&](TaskCall& c, casadi_int i, bool fwd) -> void {
700  if (!fwd) c.arg.push_back({i + DYN_T, SRC_T, 0});
701  c.arg.push_back({i + DYN_X, SRC_Y, fwd ? nx1_ : 0});
702  c.arg.push_back({i + DYN_Z, SRC_Y, nx_ + (fwd ? nz1_ : 0)});
703  c.arg.push_back({i + DYN_P, SRC_P, fwd ? np1_ : 0});
704  c.arg.push_back({i + DYN_U, SRC_U, fwd ? nu1_ : 0});
705  };
706  // Jacobian blocks in buffer src with ndir directions, block k at slot i + k
707  auto blocks = [&](std::vector<Slot>& v, casadi_int i, Src src, casadi_int ndir) -> void {
708  for (casadi_int k = 0; k < nj; ++k) v.push_back({i + k, src, ndir * p_.jac_off[k]});
709  };
710  // Right-hand side [ode; alg; quad] at (t, y)
711  TaskCall rhs = call("rhsF");
712  dyn(rhs, 0, false);
713  rhs.res = {{0, SRC_RES, 0}, {1, SRC_RES, nx_}, {2, SRC_RES, nx_ + nz_}};
714  calls[IVP_EVAL_RHS].push_back(rhs);
715  if (nfwd_ > 0) {
716  TaskCall c = call(forward_name("rhsF", nfwd_));
717  dyn(c, 0, false);
718  for (casadi_int k = 0; k < 3; ++k) {
719  c.arg.push_back({DYN_NUM_IN + k, SRC_RES, rhs.res[k].off});
720  }
721  dyn(c, DYN_NUM_IN + 3, true);
722  c.res = {{0, SRC_RES, nx1_}, {1, SRC_RES, nx_ + nz1_}, {2, SRC_RES, nx_ + nz_ + nq1_}};
723  calls[IVP_EVAL_RHS].push_back(c);
724  }
725  if (needs_jacobian()) {
726  // Jacobian blocks; forward derivatives give the augmented off-diagonal blocks
727  TaskCall jac = call("jacF");
728  dyn(jac, 0, false);
729  blocks(jac.res, 0, SRC_JAC, 1);
730  calls[IVP_EVAL_JAC].push_back(jac);
731  if (nfwd_ > 0) {
732  TaskCall c = call(forward_name("jacF", nfwd_));
733  dyn(c, 0, false);
734  blocks(c.arg, DYN_NUM_IN, SRC_JAC, 1);
735  dyn(c, DYN_NUM_IN + nj, true);
736  blocks(c.res, 0, SRC_JAC_FWD, nfwd_);
737  calls[IVP_EVAL_JAC].push_back(c);
738  }
739  }
740  if (ne_ > 0) {
741  // Zero-crossing functions; derivative in the direction (1, ydot); forward sensitivities
742  TaskCall zero = call("zeroF");
743  dyn(zero, 0, false);
744  zero.res = {{0, SRC_RES, 0}};
745  calls[IVP_EVAL_ZERO].push_back(zero);
746  TaskCall c = call(forward_name("zeroF", 1));
747  dyn(c, 0, false);
748  c.arg.push_back({DYN_NUM_IN, SRC_RES, 0});
749  c.arg.push_back({DYN_NUM_IN + 1 + DYN_T, SRC_EV_ONE, 0});
750  c.arg.push_back({DYN_NUM_IN + 1 + DYN_X, SRC_YDOT, 0});
751  c.arg.push_back({DYN_NUM_IN + 1 + DYN_Z, SRC_YDOT, nx_});
752  c.res = {{0, SRC_EDOT, 0}};
753  calls[IVP_EVAL_ZERO].push_back(c);
754  if (has_function("jumpF")) {
755  // Inputs t, x, z, q, p, u, w of the jump at offset i; forward seeds are the sensitivities
756  auto jump = [&](TaskCall& c, casadi_int i, bool fwd) -> void {
757  if (!fwd) c.arg.push_back({i, SRC_T, 0});
758  c.arg.push_back({i + 1, SRC_Y, fwd ? nx1_ : 0});
759  c.arg.push_back({i + 2, SRC_Y, nx_ + (fwd ? nz1_ : 0)});
760  c.arg.push_back({i + 3, SRC_Y, nx_ + nz_ + (fwd ? nq1_ : 0)});
761  c.arg.push_back({i + 4, SRC_P, fwd ? np1_ : 0});
762  c.arg.push_back({i + 5, SRC_U, fwd ? nu1_ : 0});
763  if (!fwd) c.arg.push_back({i + 6, SRC_EV_W, 0});
764  };
765  TaskCall jf = call("jumpF");
766  jump(jf, 0, false);
767  jf.res = {{0, SRC_RES, 0}, {1, SRC_RES, nx_}, {2, SRC_RES, nx_ + nz_}};
768  calls[IVP_EVAL_JUMP].push_back(jf);
769  if (nfwd_ > 0) {
770  c = call(forward_name("jumpF", nfwd_));
771  jump(c, 0, false);
772  for (casadi_int k = 0; k < 3; ++k) c.arg.push_back({7 + k, SRC_RES, jf.res[k].off});
773  jump(c, 10, true);
774  c.res = {{0, SRC_RES, nx1_}, {1, SRC_RES, nx_ + nz1_}, {2, SRC_RES, nx_ + nz_ + nq1_}};
775  calls[IVP_EVAL_JUMP].push_back(c);
776  }
777  if (nadj_ > 0) {
778  // Adjoint seeds of x, z, q to adjoints of x, z, p, u, with forward-over-adjoint
779  TaskCall jb = call("jumpB");
780  jump(jb, 0, false);
781  jb.arg.push_back({7, SRC_W, 0});
782  jb.arg.push_back({8, SRC_W, nrx_});
783  jb.arg.push_back({9, SRC_W, nrx_ + nrz_});
784  for (casadi_int k = 0; k < 4; ++k) jb.res.push_back({k, vjp[k], 0});
785  calls[IVP_EVAL_JUMP_ADJ].push_back(jb);
786  if (nfwd_ > 0) {
787  c = call(forward_name("jumpB", nfwd_));
788  c.arg = jb.arg;
789  for (casadi_int k = 0; k < 4; ++k) c.arg.push_back({10 + k, vjp[k], 0});
790  jump(c, 14, true);
791  c.arg.push_back({21, SRC_W, nrx1_ * nadj_});
792  c.arg.push_back({22, SRC_W, nrx_ + nrz1_ * nadj_});
793  c.arg.push_back({23, SRC_W, nrx_ + nrz_ + nrp1_ * nadj_});
794  for (casadi_int k = 0; k < 4; ++k) c.res.push_back({k, vjp[k], n_vjp[k]});
795  calls[IVP_EVAL_JUMP_ADJ].push_back(c);
796  }
797  }
798  }
799  }
800  if (nadj_ > 0) {
801  // [dF/dx; dF/dz; dF/dp; dF/du]^T w, with forward-over-adjoint
802  TaskCall vjpb = call("vjpB");
803  dyn(vjpb, 0, false);
804  vjpb.arg.push_back({BDYN_ADJ_ODE, SRC_W, 0});
805  vjpb.arg.push_back({BDYN_ADJ_ALG, SRC_W, nrx_});
806  vjpb.arg.push_back({BDYN_ADJ_QUAD, SRC_W, nrx_ + nrz_});
807  for (casadi_int k = 0; k < 4; ++k) vjpb.res.push_back({k, vjp[k], 0});
808  calls[IVP_EVAL_VJP].push_back(vjpb);
809  if (nfwd_ > 0) {
810  TaskCall c = call(forward_name("vjpB", nfwd_));
811  casadi_int i = BDYN_NUM_IN + 4;
812  c.arg = vjpb.arg;
813  for (casadi_int k = 0; k < 4; ++k) c.arg.push_back({BDYN_NUM_IN + k, vjp[k], 0});
814  dyn(c, i, true);
815  c.arg.push_back({i + BDYN_ADJ_ODE, SRC_W, nrx1_ * nadj_});
816  c.arg.push_back({i + BDYN_ADJ_ALG, SRC_W, nrx_ + nrz1_ * nadj_});
817  c.arg.push_back({i + BDYN_ADJ_QUAD, SRC_W, nrx_ + nrz_ + nrp1_ * nadj_});
818  for (casadi_int k = 0; k < 4; ++k) c.res.push_back({k, vjp[k], n_vjp[k]});
819  calls[IVP_EVAL_VJP].push_back(c);
820  }
821  if (needs_jacobian()) {
822  // d/d(x, z, p, u) <jac_adj, Jacobian blocks>, with forward-over-adjoint
823  TaskCall jacb = call("jacB");
824  dyn(jacb, 0, false);
825  blocks(jacb.arg, DYN_NUM_IN, SRC_JAC, 1);
826  blocks(jacb.arg, DYN_NUM_IN + nj, SRC_JAC_ADJ, nadj_);
827  for (casadi_int k = 0; k < 4; ++k) jacb.res.push_back({DYN_X + k, vjp[k], 0});
828  calls[IVP_EVAL_JAC_ADJ].push_back(jacb);
829  if (nfwd_ > 0) {
830  TaskCall c = call(forward_name("jacB", nfwd_));
831  casadi_int i = DYN_NUM_IN + 2 * nj, i_fwd = i + DYN_NUM_IN;
832  c.arg = jacb.arg;
833  for (casadi_int k = 0; k < 4; ++k) c.arg.push_back({i + DYN_X + k, vjp[k], 0});
834  dyn(c, i_fwd, true);
835  blocks(c.arg, i_fwd + DYN_NUM_IN + nj, SRC_JAC_ADJ_FWD, nadj_ * nfwd_);
836  for (casadi_int k = 0; k < 4; ++k) c.res.push_back({DYN_X + k, vjp[k], n_vjp[k]});
837  calls[IVP_EVAL_JAC_ADJ].push_back(c);
838  }
839  }
840  }
841  return calls;
842 }
843 
844 bool RuntimeIntegrator::has_codegen() const {
845  // Adjoint sensitivities need a tape of bounded size
846  return nadj_ == 0 || tape_capacity() > 0;
847 }
848 
849 void RuntimeIntegrator::codegen_declarations(CodeGenerator& g) const {
850  // The functions of the task tables
851  for (auto* calls : {&calls_, &calls_nom_}) {
852  for (auto&& task : *calls) {
853  for (auto&& c : task) g.add_dependency(get_function(c.fname));
854  }
855  }
856 }
857 
858 void RuntimeIntegrator::codegen_body(CodeGenerator& g) const {
859  casadi_assert(has_codegen(), name_ + ": adjoint sensitivities with adaptive steps need "
860  "max_tape_steps for code generation");
861  g.add_auxiliary(CodeGenerator::AUX_IVP);
862  if (step_size_ == 0) g.add_auxiliary(CodeGenerator::AUX_IVP_ADAPT);
863  if (ne_ > 0) g.add_auxiliary(CodeGenerator::AUX_IVP_EVENT);
864  const std::string nl = ";\n", method = plugin_name();
865  g.local("p", "struct casadi_ivp_prob");
866  g.local("d", "struct casadi_ivp_data");
867  g.local("pe", "struct casadi_" + method + "_prob");
868  g.local("e", "struct casadi_" + method + "_data");
869  if (replay_) {
870  g.local("pn", "struct casadi_ivp_prob");
871  g.local("dn", "struct casadi_ivp_data");
872  g.local("pen", "struct casadi_" + method + "_prob");
873  g.local("en", "struct casadi_" + method + "_data");
874  }
875  g.local("sarg", "const casadi_real", "**");
876  g.local("sres", "casadi_real", "**");
877  g.local("pv", "casadi_real", "*");
878  if (nu_ > 0) g.local("uv", "casadi_real", "*");
879  g.local("j", "casadi_int");
880  g.local("k", "casadi_int");
881 
882  g.comment("Problem structure");
883  codegen_ivp_prob(g, "p", p_.tape_size);
884  if (replay_) {
885  g.comment("Nominal integration, whose steps are replayed");
886  root()->codegen_ivp_prob(g, "pn", 0);
887  }
888 
889  g.comment("Work vectors");
890  g << "sarg = arg + " << INTEGRATOR_NUM_IN << "; sres = res + " << INTEGRATOR_NUM_OUT << nl;
891  g << "pv = w; w += " << np_ << nl;
892  if (nu_ > 0) g << "uv = w; w += " << nu_ << nl;
893  g << "d.prob = &p" << nl;
894  g << "casadi_ivp_set_work(&d, &arg, &res, &iw, &w)" << nl;
895  if (replay_) {
896  g << "dn.prob = &pn" << nl;
897  g << "casadi_ivp_set_work(&dn, &arg, &res, &iw, &w)" << nl;
898  g << "d.nom = &dn" << nl;
899  }
900  g.comment("Method");
901  codegen_method(g);
902 
903  g.comment("Initial state, parameters");
904  g << g.copy("arg[" + str(INTEGRATOR_X0) + "]", nx_, "d.y") << "\n";
905  g << g.copy("arg[" + str(INTEGRATOR_Z0) + "]", nz_, "d.y+" + str(nx_)) << "\n";
906  g << g.clear("d.y+" + str(nx_ + nz_), nq_) << "\n";
907  g << g.copy("arg[" + str(INTEGRATOR_P) + "]", np_, "pv") << "\n";
908 
909  g.comment("Forward integration: a run of steps per control group, as RuntimeIntegrator::eval");
910  std::string tout = g.constant(tout_), gs = g.constant(group_start_), t0 = g.constant(t0_);
911  g << "for (j = 0; j < " << ngroup() << "; ++j) {\n";
912  if (nu_ > 0) {
913  g << g.copy("arg[" + str(INTEGRATOR_U) + "] ? arg[" + str(INTEGRATOR_U) + "] + j*"
914  + str(nu_) + " : 0", nu_, "uv") << "\n";
915  }
916  g << "casadi_ivp_reset(&d, j ? " << tout << "[" << gs << "[j]-1] : " << t0 << ", "
917  << (nu_ > 0 ? "uv" : "0") << ", j == 0)" << nl;
918  g << "for (k = " << gs << "[j]; k < " << gs << "[j+1]; ++k) {\n";
919  g << "casadi_ivp_advance(&d, " << tout << "[k], " << tout << "[" << gs << "[j+1]-1], "
920  << (nrx_ > 0 ? 1 : 0) << ")" << nl;
921  codegen_loop(g);
922  g << "if (d.status != IVP_SUCCESS) return 1" << nl;
923  g << "if (res[" << INTEGRATOR_XF << "]) " << g.copy("d.y_out", nx_, "res["
924  + str(INTEGRATOR_XF) + "] + k*" + str(nx_)) << "\n";
925  g << "if (res[" << INTEGRATOR_ZF << "]) " << g.copy("d.y_out+" + str(nx_), nz_, "res["
926  + str(INTEGRATOR_ZF) + "] + k*" + str(nz_)) << "\n";
927  g << "if (res[" << INTEGRATOR_QF << "]) " << g.copy("d.y_out+" + str(nx_ + nz_), nq_,
928  "res[" + str(INTEGRATOR_QF) + "] + k*" + str(nq_)) << "\n";
929  g << "}\n}\n";
930 
931  if (nrx_ > 0) {
932  g.comment("Discrete adjoint, output interval by output interval");
933  g << "casadi_ivp_reset_backward(&d)" << nl;
934  g << "for (j = " << ngroup() - 1 << "; j >= 0; --j) {\n";
935  g.comment("Control adjoints of this group only");
936  g << g.clear("d.adj_u", nuq_) << "\n";
937  g << "for (k = " << gs << "[j+1]-1; k >= " << gs << "[j]; --k) {\n";
938  g.comment("Impulse from the adjoint seeds at output time k");
939  const casadi_int adj_in[3] = {INTEGRATOR_ADJ_XF, INTEGRATOR_ADJ_ZF, INTEGRATOR_ADJ_QF};
940  const casadi_int adj_n[3] = {nrx_, nrz_, nrp_};
941  const casadi_int adj_off[3] = {0, nrx_, nrx_ + nrz_};
942  for (casadi_int i = 0; i < 3; ++i) {
943  if (adj_n[i] == 0) continue;
944  g << g.copy("arg[" + str(adj_in[i]) + "] ? arg[" + str(adj_in[i]) + "] + k*"
945  + str(adj_n[i]) + " : 0", adj_n[i], "d.seed+" + str(adj_off[i])) << "\n";
946  }
947  g << "casadi_ivp_impulse(&d, " << tout << "[k])" << nl;
948  codegen_loop(g);
949  g << "if (d.status != IVP_SUCCESS) return 1" << nl;
950  g << "casadi_ivp_retreat(&d, " << tout << "[k], k > 0 ? " << tout << "[k-1] : " << t0
951  << ")" << nl;
952  codegen_loop(g);
953  g << "if (d.status != IVP_SUCCESS) return 1" << nl;
954  g << "}\n";
955  g << "if (res[" << INTEGRATOR_ADJ_U << "]) " << g.copy("d.adj_u", nuq_, "res["
956  + str(INTEGRATOR_ADJ_U) + "] + j*" + str(nuq_)) << "\n";
957  g << "}\n";
958  g << "if (res[" << INTEGRATOR_ADJ_X0 << "]) " << g.copy("d.ry", nrx_, "res["
959  + str(INTEGRATOR_ADJ_X0) + "]") << "\n";
960  g << "if (res[" << INTEGRATOR_ADJ_P << "]) " << g.copy("d.adj_p", nrq_, "res["
961  + str(INTEGRATOR_ADJ_P) + "]") << "\n";
962  }
963 }
964 
965 void RuntimeIntegrator::codegen_loop(CodeGenerator& g) const {
966  // Carry out the tasks requested by the kernel, for it or for the nominal integration
967  g << "while (casadi_" << plugin_name() << "(&d, &e)) {\n";
968  if (replay_) {
969  g << "if (d.active == &d) {\n";
970  codegen_task(g, "d", calls_);
971  g << "} else {\n";
972  codegen_task(g, "dn", calls_nom_);
973  g << "}\n";
974  } else {
975  codegen_task(g, "d", calls_);
976  }
977  g << "}\n";
978 }
979 
980 void RuntimeIntegrator::codegen_ivp_prob(CodeGenerator& g, const std::string& p,
981  casadi_int tape_size) const {
982  // Shared problem structure in variable p, as set_ivp_prob
983  auto set = [&](const std::string& field, const std::string& value) -> void {
984  g << p << "." << field << " = " << value << ";\n";
985  };
986  set("nx1", str(nx1_));
987  set("nz1", str(nz1_));
988  set("nq1", str(nq1_));
989  set("np1", str(np1_));
990  set("nu1", str(nu1_));
991  set("nfwd", str(nfwd_));
992  set("nadj", str(nadj_));
993  set("n_stages", str(alpha_.size()));
994  set("a", g.constant(a_));
995  set("alpha", g.constant(alpha_));
996  set("m", g.constant(m_));
997  set("n_dense", str(n_dense_));
998  set("m_dense", g.constant(m_dense_));
999  for (casadi_int k = 0; k < IVP_NUM_JAC; ++k) {
1000  set("sp_jac[" + str(k) + "]", sp_jac_.empty() ? "0" : g.sparsity(sp_jac_[k]));
1001  }
1002  set("tape_size", str(tape_size));
1003  set("step_size", g.constant(p_.step_size));
1004  set("nx_err", str(p_.nx_err));
1005  set("nz_err", str(p_.nz_err));
1006  set("nq_err", str(p_.nq_err));
1007  set("abstol", g.constant(p_.abstol));
1008  set("reltol", g.constant(p_.reltol));
1009  set("order_err", str(p_.order_err));
1010  set("m_err", m_err_.empty() ? "0" : g.constant(m_err_));
1011  set("safety", g.constant(p_.safety));
1012  set("fac_min", g.constant(p_.fac_min));
1013  set("fac_max", g.constant(p_.fac_max));
1014  set("h_min", g.constant(p_.h_min));
1015  set("h_max", g.constant(p_.h_max));
1016  set("h_init", g.constant(p_.h_init));
1017  set("max_num_steps", str(p_.max_num_steps));
1018  set("ne", str(p_.ne));
1019  if (p_.ne > 0) {
1020  set("has_transition", str(p_.has_transition));
1021  set("event_tol", g.constant(p_.event_tol));
1022  set("event_acceptable_tol", g.constant(p_.event_acceptable_tol));
1023  set("max_event_iter", str(p_.max_event_iter));
1024  set("max_events", str(p_.max_events));
1025  }
1026  g << "casadi_ivp_setup(&" << p << ");\n";
1027 }
1028 
1029 void RuntimeIntegrator::codegen_task(CodeGenerator& g, const std::string& d,
1030  const std::vector<std::vector<TaskCall>>& calls, bool in_event) const {
1031  // Straight-line code for the task table, as eval_task: direct calls, no function pointers
1032  static const char* task_name[] = {"IVP_EVAL_RHS", "IVP_EVAL_JAC", "IVP_EVAL_VJP",
1033  "IVP_EVAL_JAC_ADJ", "IVP_EVAL_ZERO", "IVP_EVAL_JUMP", "IVP_EVAL_JUMP_ADJ"};
1034  g << "switch (" << d << ".task) {\n";
1035  for (casadi_int task = 0; task < static_cast<casadi_int>(calls.size()); ++task) {
1036  if (calls[task].empty()) continue;
1037  // Tasks of the events are requested by casadi_ivp_event and casadi_ivp_event_adj only
1038  bool ev = task >= IVP_EVAL_ZERO && task <= IVP_EVAL_JUMP_ADJ;
1039  if (in_event != ev) continue;
1040  g << "case " << task_name[task] << ":\n";
1041  // Slots set earlier in the same case need not be set again
1042  std::map<casadi_int, std::string> arg_set, res_set;
1043  for (const TaskCall& c : calls[task]) {
1044  std::vector<std::string> arg(c.n_in, "0"), res(c.n_out, "0");
1045  for (const Slot& e : c.arg) arg[e.i] = slot_expr(d, e);
1046  for (const Slot& e : c.res) res[e.i] = slot_expr(d, e);
1047  for (casadi_int i = 0; i < c.n_in; ++i) {
1048  if (arg_set.count(i) && arg_set[i] == arg[i]) continue;
1049  g << "sarg[" << i << "] = " << arg[i] << ";\n";
1050  arg_set[i] = arg[i];
1051  }
1052  for (casadi_int i = 0; i < c.n_out; ++i) {
1053  if (res_set.count(i) && res_set[i] == res[i]) continue;
1054  g << "sres[" << i << "] = " << res[i] << ";\n";
1055  res_set[i] = res[i];
1056  }
1057  // On failure, skip the remaining evaluations of the task
1058  std::string flag = g(get_function(c.fname), "sarg", "sres", "iw", "w");
1059  g << "if (" << flag << ") {\n" << d << ".fail = 1;\nbreak;\n}\n";
1060  }
1061  g << "break;\n";
1062  }
1063  if (step_size_ == 0 && !in_event) {
1064  g << "case IVP_STEP_CONTROL:\n";
1065  g << "casadi_ivp_control(&" << d << ");\n";
1066  g << "break;\n";
1067  }
1068  if (ne_ > 0 && !in_event) {
1069  for (casadi_int adj = 0; adj <= (nadj_ > 0 ? 1 : 0); ++adj) {
1070  g << "case IVP_EVENT" << (adj ? "_ADJ" : "") << ":\n";
1071  g << "while (casadi_ivp_event" << (adj ? "_adj" : "") << "(&" << d << ")) {\n";
1072  codegen_task(g, d, calls, true);
1073  g << "}\n";
1074  g << "break;\n";
1075  }
1076  }
1077  g << "default: " << d << ".fail = 1;\n";
1078  g << "}\n";
1079 }
1080 
1081 Dict RuntimeIntegrator::get_stats(void* mem) const {
1082  Dict stats = Integrator::get_stats(mem);
1083  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
1084  stats["n_steps"] = m->d.n_steps;
1085  stats["n_reject"] = m->d.n_reject;
1086  stats["n_rhs"] = m->d.n_rhs;
1087  stats["n_jac"] = m->d.n_jac;
1088  stats["n_vjp"] = m->d.n_vjp;
1089  stats["n_events"] = m->d.n_events;
1090  return stats;
1091 }
1092 
1093 void RuntimeIntegrator::print_stats(IntegratorMemory* mem) const {
1094  auto m = static_cast<RuntimeIntegratorMemory*>(mem);
1095  print("%s: %lld steps, %lld rejected, %lld rhs, %lld jac, %lld vjp, %lld events\n",
1096  plugin_name(), static_cast<long long>(m->d.n_steps), static_cast<long long>(m->d.n_reject),
1097  static_cast<long long>(m->d.n_rhs), static_cast<long long>(m->d.n_jac),
1098  static_cast<long long>(m->d.n_vjp), static_cast<long long>(m->d.n_events));
1099 }
1100 
1101 RuntimeIntegrator::RuntimeIntegrator(DeserializingStream& s) : Integrator(s) {
1102  s.version("RuntimeIntegrator", 1);
1103  s.unpack("RuntimeIntegrator::group_start", group_start_);
1104  s.unpack("RuntimeIntegrator::nx_err", nx_err_);
1105  s.unpack("RuntimeIntegrator::nz_err", nz_err_);
1106  s.unpack("RuntimeIntegrator::nq_err", nq_err_);
1107  s.unpack("RuntimeIntegrator::sp_jac", sp_jac_);
1108  s.unpack("RuntimeIntegrator::step_size", step_size_);
1109  s.unpack("RuntimeIntegrator::abstol", abstol_);
1110  s.unpack("RuntimeIntegrator::reltol", reltol_);
1111  s.unpack("RuntimeIntegrator::step0", step0_);
1112  s.unpack("RuntimeIntegrator::max_step_size", max_step_size_);
1113  s.unpack("RuntimeIntegrator::min_step_size", min_step_size_);
1114  s.unpack("RuntimeIntegrator::max_num_steps", max_num_steps_);
1115  s.unpack("RuntimeIntegrator::max_tape_steps", max_tape_steps_);
1116  s.unpack("RuntimeIntegrator::quad_err_con", quad_err_con_);
1117 }
1118 
1121  s.version("RuntimeIntegrator", 1);
1122  s.pack("RuntimeIntegrator::group_start", group_start_);
1123  s.pack("RuntimeIntegrator::nx_err", nx_err_);
1124  s.pack("RuntimeIntegrator::nz_err", nz_err_);
1125  s.pack("RuntimeIntegrator::nq_err", nq_err_);
1126  s.pack("RuntimeIntegrator::sp_jac", sp_jac_);
1127  s.pack("RuntimeIntegrator::step_size", step_size_);
1128  s.pack("RuntimeIntegrator::abstol", abstol_);
1129  s.pack("RuntimeIntegrator::reltol", reltol_);
1130  s.pack("RuntimeIntegrator::step0", step0_);
1131  s.pack("RuntimeIntegrator::max_step_size", max_step_size_);
1132  s.pack("RuntimeIntegrator::min_step_size", min_step_size_);
1133  s.pack("RuntimeIntegrator::max_num_steps", max_num_steps_);
1134  s.pack("RuntimeIntegrator::max_tape_steps", max_tape_steps_);
1135  s.pack("RuntimeIntegrator::quad_err_con", quad_err_con_);
1136 }
1137 
1138 } // namespace casadi
Helper class for C code generation.
std::string add_dependency(const Function &f)
Add a function dependency.
std::string copy(const std::string &arg, std::size_t n, const std::string &res)
Create a copy operation.
void comment(const std::string &s)
Write a comment line (ignored if not verbose)
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
std::string clear(const std::string &res, std::size_t n)
Create a fill operation.
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
size_t sz_res() const
Get required length of res field.
void call(const std::vector< M > &arg, std::vector< M > &res, bool always_inline, bool never_inline) const
Call a function, templated.
size_t sz_w() const
Get required length of w field.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
size_t sz_arg() const
Get required length of arg field.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
size_t sz_iw() const
Get required length of iw field.
Function derivative_of_
If the function is the derivative of another function.
Function object.
Definition: function.hpp:126
casadi_int numel_in() const
Get the number of elements (rows times columns) of one input, or of all.
Definition: function.cpp:1011
const MX mx_in(casadi_int ind) const
Create fresh symbols with the names and sparsity patterns of the inputs.
Definition: function.cpp:1781
const Sparsity & sparsity_out(casadi_int ind) const
Get the sparsity pattern of an output, by index or name.
Definition: function.cpp:1183
FunctionInternal * get() const
Definition: function.cpp:505
Function reverse(casadi_int nadj) const
Get a function computing nadj reverse (adjoint) directional derivatives.
Definition: function.cpp:1332
const Sparsity & sparsity_in(casadi_int ind) const
Get the sparsity pattern of an input, by index or name.
Definition: function.cpp:1167
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:975
casadi_int nnz_in() const
Get the number of structural nonzeros of one input, or of all inputs.
Definition: function.cpp:1003
const std::vector< std::string > & name_out() const
Get the names of all outputs, in order.
Definition: function.cpp:1117
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
bool is_null() const
Is a null pointer?
Internal storage for integrator related data.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
casadi_int nfwd_
Number of sensitivities.
void init(const Dict &opts) override
Initialize.
Definition: integrator.cpp:645
static const Options options_
Options.
void set_z(IntegratorMemory *m, const double *z) const
void get_z(IntegratorMemory *m, double *z) const
void set_x(IntegratorMemory *m, const double *x) const
Function rdae_
Backwards DAE function.
Sparsity get_sparsity_in(casadi_int i) override
Sparsities of function inputs and outputs.
Definition: integrator.cpp:284
casadi_int nrx_
Number of states for the backward integration.
casadi_int max_events_
Maximum total number of events during the simulation.
void get_q(IntegratorMemory *m, double *q) const
int init_mem(void *mem) const override
Initalize memory block.
Definition: integrator.cpp:916
casadi_int np_
Number of forward and backward parameters.
casadi_int nt() const
Number of output times.
void set_u(IntegratorMemory *m, const double *u) const
bool print_stats_
Options.
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity forward.
void get_x(IntegratorMemory *m, double *x) const
Sparsity get_sparsity_out(casadi_int i) override
Sparsities of function inputs and outputs.
Definition: integrator.cpp:298
static std::vector< std::string > bdyn_in()
IO conventions for continuous time dynamics.
Definition: integrator.cpp:96
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: integrator.cpp:886
std::vector< double > tout_
Output time grid.
double event_tol_
Termination tolerance for the event iteration.
void set_q(IntegratorMemory *m, const double *q) const
double t0_
Initial time.
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity backwards.
double event_acceptable_tol_
Acceptable tolerance for the event iteration.
casadi_int max_event_iter_
Maximum number of event iterations for a single event.
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.
void set_p(IntegratorMemory *m, const double *p) const
MX - Matrix expression.
Definition: mx.hpp:145
static MX substitute(const MX &ex, const MX &v, const MX &vdef)
Definition: mx.cpp:1518
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:615
static MX jacobian(const MX &f, const MX &x, const Dict &opts=Dict())
Definition: mx.cpp:1913
static MX dot(const MX &x, const MX &y)
Definition: mx.cpp:738
void set_function(const Function &fcn, const std::string &fname, bool jit=false)
Function oracle_
Oracle: Used to generate other functions.
Function create_function(const Function &oracle, const std::string &fname, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const Function::AuxOut &aux=Function::AuxOut(), const Dict &opts=Dict())
void join_results(OracleMemory *m) const
Combine results from different threads.
Function create_forward(const std::string &fname, casadi_int nfwd)
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
std::vector< std::string > get_function() const override
Get list of dependency functions.
bool has_function(const std::string &fname) const override
bool verbose_
Verbose printout.
Base class of one-step integrators driven by a reverse communication kernel.
casadi_int tape_capacity() const
Capacity of the tape in the work vector (0: unbounded, in the virtual machine only)
void set_ivp_prob()
Shared problem structure, from the method coefficients.
RuntimeIntegrator(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Constructor.
std::vector< double > m_dense_
std::vector< double > alpha_
std::vector< Sparsity > sp_jac_
void set_nominal_prob()
Problem of the nominal integration whose steps are replayed, if any.
std::vector< casadi_int > group_start_
casadi_ivp_prob< double > p_nom_
void retreat(IntegratorMemory *mem, const double *u, double *adj_x, double *adj_p, double *adj_u) const override
Retreat solution in time.
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.
const RuntimeIntegrator * root() const
Integrator at the start of the chain of derivatives.
void run(RuntimeIntegratorMemory *m) const
Carry out the tasks requested by the kernel.
void resetB(IntegratorMemory *mem) const override
Reset the backward problem.
void print_stats(IntegratorMemory *mem) const override
Print solver statistics.
Sparsity get_sparsity_out(casadi_int i) override
~RuntimeIntegrator() override
Destructor.
void reset(IntegratorMemory *mem, bool first_call) const override
Reset the forward problem at the start or after a change in the controls.
void init(const Dict &opts) override
Initialize: functions of the kernel tasks.
casadi_ivp_prob< double > p_
casadi_int ngroup() const
Number of control groups, columns of the controls.
virtual int kernel(RuntimeIntegratorMemory *m) const =0
Call the kernel of the method: returns 1 while a task is requested.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
virtual bool needs_jacobian() const =0
Does the method linearize (tasks EVAL_JAC, EVAL_JAC_ADJ)?
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically: a run of steps per control group, events in the kernel.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
int advance_noevent(IntegratorMemory *mem) const override
Advance solution in time.
int init_mem(void *mem) const override
Initalize memory block.
std::vector< double > m_err_
void alloc_ivp_work()
Work vectors of the shared kernel data.
Function jump_function() const
State after an event as a function of the state before it, see casadi_ivp_event.hpp.
Sparsity get_sparsity_in(casadi_int i) override
static const Options options_
Options.
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:200
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern.
Definition: sparsity.cpp:1041
std::vector< std::string > dyn_in()
Get the input scheme of a DAE function.
Definition: integrator.cpp:232
Function rootfinder(const std::string &name, const std::string &solver, const SXDict &rfp, const Dict &opts)
Definition: rootfinder.cpp:111
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
@ INTEGRATOR_ADJ_U
Adjoint sensitivities corresponding to the control vector.
Definition: integrator.hpp:978
@ INTEGRATOR_QF
Quadrature state at all output times.
Definition: integrator.hpp:970
@ INTEGRATOR_ADJ_P
Adjoint sensitivities corresponding to the parameter vector.
Definition: integrator.hpp:976
@ INTEGRATOR_ZF
Algebraic variable at all output times.
Definition: integrator.hpp:968
@ INTEGRATOR_XF
Differential state at all output times.
Definition: integrator.hpp:966
@ INTEGRATOR_NUM_OUT
Number of output arguments of an integrator.
Definition: integrator.hpp:980
@ INTEGRATOR_ADJ_X0
Adjoint sensitivities corresponding to the initial state.
Definition: integrator.hpp:972
@ INTEGRATOR_U
Piecewise constant control, a new control interval starts at each output time.
Definition: integrator.hpp:952
@ INTEGRATOR_ADJ_QF
Adjoint seeds corresponding to the quadratures at the output times.
Definition: integrator.hpp:958
@ INTEGRATOR_ADJ_ZF
Adjoint seeds corresponding to the algebraic variables at the output times.
Definition: integrator.hpp:956
@ INTEGRATOR_P
Parameters.
Definition: integrator.hpp:950
@ INTEGRATOR_ADJ_XF
Adjoint seeds corresponding to the states at the output times.
Definition: integrator.hpp:954
@ INTEGRATOR_Z0
Initial guess for the algebraic variable at the initial time.
Definition: integrator.hpp:948
@ INTEGRATOR_NUM_IN
Number of input arguments of an integrator.
Definition: integrator.hpp:960
@ INTEGRATOR_X0
Differential state at the initial time.
Definition: integrator.hpp:946
unsigned long long bvec_t
@ EVENT_INDEX
Definition: integrator.hpp:929
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
@ DYN_NUM_IN
Definition: integrator.hpp:917
@ EVENT_POST_Z
Definition: integrator.hpp:940
@ EVENT_POST_X
Definition: integrator.hpp:939
@ OT_INTVECTOR
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
Matrix< double > DM
Definition: dm_fwd.hpp:33
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.
Options metadata for a class.
Definition: options.hpp:40
casadi_int tape_need
Definition: casadi_ivp.hpp:241
casadi_ivp_data< T1 > * active
Definition: casadi_ivp.hpp:191
const T1 * w
Definition: casadi_ivp.hpp:206
casadi_ivp_task_t task
Definition: casadi_ivp.hpp:196
casadi_ivp_flag_t status
Definition: casadi_ivp.hpp:201
casadi_int tape_cap
Definition: casadi_ivp.hpp:241
casadi_int n_steps
Definition: casadi_ivp.hpp:267
const T1 * y_arg
Definition: casadi_ivp.hpp:206
casadi_int order_err
Definition: casadi_ivp.hpp:134
casadi_int nz_err
Definition: casadi_ivp.hpp:130
casadi_int n_dense
Definition: casadi_ivp.hpp:120
const T1 * m
Definition: casadi_ivp.hpp:118
const T1 * m_err
Definition: casadi_ivp.hpp:135
const casadi_int * sp_jac[IVP_NUM_JAC]
Definition: casadi_ivp.hpp:123
const T1 * alpha
Definition: casadi_ivp.hpp:118
const T1 * m_dense
Definition: casadi_ivp.hpp:121
casadi_int tape_size
Definition: casadi_ivp.hpp:125
casadi_int max_num_steps
Definition: casadi_ivp.hpp:137
casadi_int max_event_iter
Definition: casadi_ivp.hpp:143
casadi_int max_events
Definition: casadi_ivp.hpp:143
casadi_int nx_err
Definition: casadi_ivp.hpp:130
casadi_int nz1
Definition: casadi_ivp.hpp:112
casadi_int np1
Definition: casadi_ivp.hpp:112
casadi_int nfwd
Definition: casadi_ivp.hpp:114
casadi_int nu1
Definition: casadi_ivp.hpp:112
casadi_int nx1
Definition: casadi_ivp.hpp:112
casadi_int nadj
Definition: casadi_ivp.hpp:114
casadi_int jac_off[IVP_NUM_JAC+1]
Definition: casadi_ivp.hpp:147
const T1 * a
Definition: casadi_ivp.hpp:118
casadi_int ne
Definition: casadi_ivp.hpp:140
casadi_int nq1
Definition: casadi_ivp.hpp:112
casadi_int nq_err
Definition: casadi_ivp.hpp:130
casadi_int n_stages
Definition: casadi_ivp.hpp:117