casadi_ivp.hpp
1 //
2 // MIT No Attribution
3 //
4 // Copyright (C) 2010-2026 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of this
7 // software and associated documentation files (the "Software"), to deal in the Software
8 // without restriction, including without limitation the rights to use, copy, modify,
9 // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
14 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 //
19 
20 // Shared part of the one-step integrators for M y' = F(t, y), y = [x; z; q], M = diag(I, 0, I):
21 // stages, dense output, tape, output times, stops and the record-wise discrete adjoint
22 // Layouts: x = [x_0; ...; x_nfwd] (nominal, forward), rx = [rx_0 (nx1 x nadj); ...; rx_nfwd]
23 
24 // C-REPLACE "static_cast<int>" "(int) "
25 // C-REPLACE "static_cast<T1>" "(casadi_real) "
26 // C-REPLACE "static_cast<casadi_int>" "(casadi_int) "
27 // C-REPLACE "static_cast<casadi_ivp_record_t>" "(casadi_ivp_record_t) "
28 
29 // SYMBOL "ivp_jac_t"
30 typedef enum {
31  // Jacobian blocks w.r.t. x, z and t, in the order of evaluation by the caller
32  IVP_ODE_X, IVP_ALG_X, IVP_QUAD_X,
33  IVP_ODE_Z, IVP_ALG_Z, IVP_QUAD_Z,
34  IVP_ODE_T, IVP_ALG_T, IVP_QUAD_T,
35  IVP_NUM_JAC
36 } casadi_ivp_jac_t;
37 
38 // SYMBOL "ivp_task_t"
39 typedef enum {
40  // res = F(t_arg, y_arg), with forward sensitivities
41  IVP_EVAL_RHS,
42  // jac, jac_fwd = Jacobian blocks at (t_arg, y_arg), with forward sensitivities
43  IVP_EVAL_JAC,
44  // vjp = dF/d(x,z,p,u)^T w at (t_arg, y_arg), with forward sensitivities
45  IVP_EVAL_VJP,
46  // vjp = d/d(x,z,p,u) <jac_adj, Jacobian blocks> at (t_arg, y_arg), w. forward sensitivities
47  IVP_EVAL_JAC_ADJ,
48  // res = zero-crossing functions at (t_arg, y_arg), edot = derivative in direction (1, ydot)
49  IVP_EVAL_ZERO,
50  // res = y after the event selected by ev_w at (t_arg, y_arg), with forward sensitivities
51  IVP_EVAL_JUMP,
52  // vjp = adjoint of IVP_EVAL_JUMP with seed w, with forward sensitivities
53  IVP_EVAL_JUMP_ADJ,
54  // Enlarge the tape to at least tape_need entries, keeping its contents; set tape, tape_cap
55  IVP_TAPE_RESERVE,
56  // Adaptive step size control, see casadi_ivp_control
57  IVP_STEP_CONTROL,
58  // Events in the accepted step, see casadi_ivp_event
59  IVP_EVENT,
60  // Adjoint of the popped event record, see casadi_ivp_event_adj
61  IVP_EVENT_ADJ
62 } casadi_ivp_task_t;
63 
64 // SYMBOL "ivp_control_t"
65 typedef enum {
66  // Size h of a step from t; h = 0 if an initial estimate from F(t, y) is needed first
67  IVP_CONTROL_SIZE,
68  // Initial estimate from F(t, y) in U, then as IVP_CONTROL_SIZE
69  IVP_CONTROL_INIT,
70  // Error test of the candidate y: accepted, or rejected with y restored
71  IVP_CONTROL_TEST,
72  // Evaluation failed: rejected with y restored
73  IVP_CONTROL_FAIL
74 } casadi_ivp_control_t;
75 
76 // SYMBOL "ivp_entry_t"
77 typedef enum {
78  // Entry points of a method; its own phases start at IVP_NUM_ENTRY
79  IVP_DONE,
80  IVP_ADVANCE,
81  IVP_IMPULSE,
82  IVP_RETREAT,
83  IVP_NUM_ENTRY
84 } casadi_ivp_entry_t;
85 
86 // SYMBOL "ivp_flag_t"
87 typedef enum {
88  IVP_SUCCESS,
89  IVP_MAX_NUM_STEPS,
90  IVP_STEP_TOO_SMALL,
91  IVP_INIT_FAILED,
92  IVP_EVAL_FAILED,
93  IVP_TAPE_FAILED,
94  IVP_TOO_MANY_EVENTS,
95  IVP_EVENT_FAILED
96 } casadi_ivp_flag_t;
97 
98 // SYMBOL "ivp_record_t"
99 typedef enum {
100  // Integrator step from t0 to t0 + h
101  IVP_RECORD_STEP,
102  // Consistent initialization of the algebraic states at t0
103  IVP_RECORD_INIT,
104  // Event at t0: state before it, index in place of h
105  IVP_RECORD_EVENT
106 } casadi_ivp_record_t;
107 
108 // SYMBOL "ivp_prob"
109 template<typename T1>
111  // Nominal block sizes: differential, algebraic, quadrature states, parameters, controls
112  casadi_int nx1, nz1, nq1, np1, nu1;
113  // Number of forward and adjoint directions
114  casadi_int nfwd, nadj;
115  // Stages: increments U_i, arguments y0 + sum_j a_ij U_j at t0 + alpha_i h (a strictly lower
116  // triangular, row-major), solution y1 = y0 + sum_i m_i U_i
117  casadi_int n_stages;
118  const T1 *a, *alpha, *m;
119  // Dense output weights m_i(theta) = sum_q m_dense[q * n_stages + i] theta^(q+1)
120  casadi_int n_dense;
121  const T1* m_dense;
122  // Sparsity patterns of the Jacobian blocks (null: not used), see casadi_ivp_jac_t
123  const casadi_int* sp_jac[IVP_NUM_JAC];
124  // Capacity of the tape in the work vector (0: the caller provides the tape)
125  casadi_int tape_size;
126  // Fixed steps: each run of steps up to a stop has equal steps of at most step_size
127  // (0: adaptive steps)
129  // Error weights: leading entries of x, z, q subject to error control, tolerances
130  casadi_int nx_err, nz_err, nq_err;
132  // Adaptive steps: order and weights of the error estimate sum_i m_err_i U_i, step size
133  // controller, maximum number of steps per output interval
134  casadi_int order_err;
135  const T1* m_err;
137  casadi_int max_num_steps;
138  // Zero-crossing events: number, transition given, tolerances on the event time (acceptable:
139  // negative if none), iterations to locate an event, events per evaluation
140  casadi_int ne;
144  // Derived: forward and adjoint state dimensions, controls, vector lengths
145  casadi_int nx, nz, nq, nu, nrx, nrz, nrq, nrp, nru, ny, nr;
146  // Derived: offsets of the Jacobian blocks in a buffer with one direction, total nonzeros
147  casadi_int jac_off[IVP_NUM_JAC + 1];
148  casadi_int nnz_jac;
149 };
150 // C-REPLACE "casadi_ivp_prob<T1>" "struct casadi_ivp_prob"
151 
152 // SYMBOL "ivp_nnz"
153 inline
154 casadi_int casadi_ivp_nnz(const casadi_int* sp) {
155  // Number of nonzeros of a sparsity pattern, zero if null
156  return sp ? sp[2 + sp[1]] : 0;
157 }
158 
159 // SYMBOL "ivp_setup"
160 template<typename T1>
161 void casadi_ivp_setup(casadi_ivp_prob<T1>* p) {
162  casadi_int k;
163  // Forward states including sensitivities
164  p->nx = p->nx1 * (1 + p->nfwd);
165  p->nz = p->nz1 * (1 + p->nfwd);
166  p->nq = p->nq1 * (1 + p->nfwd);
167  p->nu = p->nu1 * (1 + p->nfwd);
168  // Adjoint states and adjoint parameters, controls, including forward sensitivities
169  p->nrx = p->nx1 * p->nadj * (1 + p->nfwd);
170  p->nrz = p->nz1 * p->nadj * (1 + p->nfwd);
171  p->nrq = p->nq1 * p->nadj * (1 + p->nfwd);
172  p->nrp = p->np1 * p->nadj * (1 + p->nfwd);
173  p->nru = p->nu1 * p->nadj * (1 + p->nfwd);
174  p->ny = p->nx + p->nz + p->nq;
175  p->nr = p->nrx + p->nrz + p->nrq;
176  // Jacobian blocks
177  p->jac_off[0] = 0;
178  for (k = 0; k < IVP_NUM_JAC; ++k) {
179  p->jac_off[k + 1] = p->jac_off[k] + casadi_ivp_nnz(p->sp_jac[k]);
180  }
181  p->nnz_jac = p->jac_off[IVP_NUM_JAC];
182 }
183 
184 // C-REPLACE "casadi_ivp_data<T1>" "struct casadi_ivp_data"
185 // SYMBOL "ivp_data"
186 template<typename T1>
188  // Problem structure
190  // Data whose task is requested: this one, or one the method delegates to
192  // Nominal integration whose steps are replayed (or null), stop after every accepted step
194  int pause;
195  // Task for the caller, where to resume (casadi_ivp_entry_t or a phase of the method), status
196  casadi_ivp_task_t task;
197  // Step size control requested, step accepted by it
198  casadi_ivp_control_t control;
199  int accepted;
200  casadi_int next;
201  casadi_ivp_flag_t status;
202  // Set by the caller if a task could not be carried out
203  int fail;
204  // Task arguments and results
205  T1 t_arg;
206  const T1 *y_arg, *w;
207  T1 *res;
208  // Current time and state, output time, latest time to step to, start of a retreat
210  T1* y;
211  // State at the output time
212  T1* y_out;
213  // Controls set by the caller, controls of the current or popped step
214  const T1* u;
215  T1* u_step;
216  // Current step: start time, size, start state, stage counter
217  T1 t0, h;
218  // End time and end of the output interval of a popped record
220  T1* y0;
221  casadi_int stage;
222  // Stage increments U_i (n_stages x ny), stage argument, dense output weights
223  T1 *U, *y_stage, *w_dense;
224  // Adjoint state, stage adjoints (n_stages x nr), adjoint of the start of the popped step
225  T1 *ry, *rU, *ry0;
226  // Adjoint seed of an output at time t_seed
227  T1* seed;
228  T1 t_seed;
229  // Control adjoints of the popped step from outputs inside it
230  T1* ru_step;
231  // Jacobian blocks and their forward derivatives, nonzeros in casadi_ivp_jac_t order
232  T1 *jac, *jac_fwd;
233  // Adjoint seeds of the Jacobian blocks, with forward derivatives
235  // Results of EVAL_VJP and EVAL_JAC_ADJ: adjoints of x, z, p, u
236  T1 *vjp_x, *vjp_z, *vjp_p, *vjp_u;
237  // Accumulated adjoints of the parameters and controls
238  T1 *adj_p, *adj_u;
239  // Tape: records [U (steps only), y0, u, h, t0, t1, t_out, type]; entries used, capacity, needed
240  T1* tape;
242  // Reset pending, taping requested
243  int reset, taping;
244  // Record type, popped record that belongs to an earlier interval
245  casadi_ivp_record_t record;
247  // Fixed steps: current run start, stop, step size, number of steps, steps taken
249  casadi_int run_n, run_k;
250  // Adaptive steps: proposed next step size, previous error, last step rejected, clipped at
251  // the stop or ending at the stop
254  // Steps taken in the current output interval
255  casadi_int n_steps_interval;
256  // Events: zero-crossing functions at the bracket ends (e0 also at the step start) and at a
257  // trial time, candidates, time derivatives at these, selection of the event, state derivative
258  T1 *e0, *e1, *em, *e_mask, *edot0, *edot1, *edot, *ev_w, *ydot;
259  // Event phase, e0 known at t, state changed by an event (algebraic states inconsistent),
260  // restart after an event
262  // Bracket, trial time, event time and its accuracy, the constant 1
264  // Iterations, event in the last step (-1: none), events in this evaluation
265  casadi_int ev_iter, ev_fired, n_events;
266  // Statistics
267  casadi_int n_steps, n_reject, n_rhs, n_jac, n_vjp;
268 };
269 
270 // SYMBOL "ivp_work"
271 template<typename T1>
272 void casadi_ivp_work(const casadi_ivp_prob<T1>* p, casadi_int* sz_arg,
273  casadi_int* sz_res, casadi_int* sz_iw, casadi_int* sz_w) {
274  // State: y, y_out, y0, stages, stage argument, dense output weights
275  *sz_w += p->ny * (p->n_stages + 4) + p->n_stages;
276  // Adjoint: adjoint state, stage adjoints, adjoint of the step start, seed
277  *sz_w += p->nr * (p->n_stages + 3);
278  // Controls of a step, their adjoints from outputs inside it
279  *sz_w += p->nu + p->nru;
280  // Jacobian blocks, forward derivatives, adjoint seeds and their forward derivatives
281  *sz_w += p->nnz_jac * (1 + p->nfwd) * (1 + p->nadj);
282  // Function adjoints, accumulated adjoints
283  *sz_w += p->nrx + p->nrz + 2 * (p->nrp + p->nru);
284  // Tape
285  *sz_w += p->tape_size;
286  // Events
287  if (p->ne > 0) *sz_w += 8 * p->ne + p->ny;
288 }
289 
290 // SYMBOL "ivp_set_work"
291 template<typename T1>
292 void casadi_ivp_set_work(casadi_ivp_data<T1>* d, const T1*** arg, T1*** res,
293  casadi_int** iw, T1** w) {
294  const casadi_ivp_prob<T1>* p = d->prob;
295  d->y = *w; *w += p->ny;
296  d->y_out = *w; *w += p->ny;
297  d->y0 = *w; *w += p->ny;
298  d->U = *w; *w += p->ny * p->n_stages;
299  d->y_stage = *w; *w += p->ny;
300  d->w_dense = *w; *w += p->n_stages;
301  d->ry = *w; *w += p->nr;
302  d->rU = *w; *w += p->nr * p->n_stages;
303  d->ry0 = *w; *w += p->nr;
304  d->seed = *w; *w += p->nr;
305  d->u_step = *w; *w += p->nu;
306  d->ru_step = *w; *w += p->nru;
307  d->jac = *w; *w += p->nnz_jac;
308  d->jac_fwd = *w; *w += p->nnz_jac * p->nfwd;
309  d->jac_adj = *w; *w += p->nnz_jac * p->nadj;
310  d->jac_adj_fwd = *w; *w += p->nnz_jac * p->nadj * p->nfwd;
311  d->vjp_x = *w; *w += p->nrx;
312  d->vjp_z = *w; *w += p->nrz;
313  d->vjp_p = *w; *w += p->nrp;
314  d->vjp_u = *w; *w += p->nru;
315  d->adj_p = *w; *w += p->nrp;
316  d->adj_u = *w; *w += p->nru;
317  d->tape = *w; *w += p->tape_size;
318  if (p->ne > 0) {
319  d->e0 = *w; *w += p->ne;
320  d->e1 = *w; *w += p->ne;
321  d->em = *w; *w += p->ne;
322  d->e_mask = *w; *w += p->ne;
323  d->edot0 = *w; *w += p->ne;
324  d->edot1 = *w; *w += p->ne;
325  d->edot = *w; *w += p->ne;
326  d->ev_w = *w; *w += p->ne;
327  d->ydot = *w; *w += p->ny;
328  }
329  d->tape_cap = p->tape_size;
330  d->tape_pos = 0;
331  d->next = IVP_DONE;
332  d->status = IVP_SUCCESS;
333  d->fail = 0;
334  d->reset = 0;
335  d->taping = 0;
336  d->record_pending = 0;
337  d->active = d;
338  d->nom = 0;
339  d->pause = 0;
340  d->ev_phase = 0;
341  d->e_valid = 0;
342  d->jump = 0;
343  d->ev_restart = 0;
344  d->ev_dt = 0;
345  d->ev_one = 1;
346  d->ev_fired = -1;
347  d->n_events = 0;
348 }
349 
350 // SYMBOL "ivp_clear_stats"
351 template<typename T1>
352 void casadi_ivp_clear_stats(casadi_ivp_data<T1>* d) {
353  d->n_steps = d->n_reject = d->n_rhs = d->n_jac = d->n_vjp = 0;
354 }
355 
356 // SYMBOL "ivp_reset"
357 template<typename T1>
358 void casadi_ivp_reset(casadi_ivp_data<T1>* d, T1 t, const T1* u, int first_call) {
359  // Start at time t, controls u; the state d->y is set by the caller
360  const casadi_ivp_prob<T1>* p = d->prob;
361  const casadi_ivp_prob<T1>* pn;
362  d->t = t;
363  d->u = u;
364  d->reset = 1;
365  d->e_valid = 0;
366  if (first_call) {
367  d->n_events = 0;
368  d->ev_restart = 0;
369  d->tape_pos = 0;
370  casadi_ivp_clear_stats(d);
371  // Every evaluation starts from the same controller state
372  d->h_next = 0;
373  d->err_prev = 1;
374  d->last_rejected = 0;
375  }
376  if (d->nom) {
377  // Initial nominal states: the leading entries of x, z and q; afterwards, the nominal
378  // integration continues its own trajectory
379  pn = d->nom->prob;
380  if (first_call) {
381  casadi_copy(d->y, pn->nx, d->nom->y);
382  casadi_copy(d->y + p->nx, pn->nz, d->nom->y + pn->nx);
383  casadi_copy(d->y + p->nx + p->nz, pn->nq, d->nom->y + pn->nx + pn->nz);
384  }
385  casadi_ivp_reset(d->nom, t, u, first_call);
386  }
387 }
388 
389 // SYMBOL "ivp_steps"
390 template<typename T1>
391 casadi_int casadi_ivp_steps(T1 len, T1 step_size) {
392  // Number of equal steps covering len, of at most step_size up to rounding
393  casadi_int n = static_cast<casadi_int>(ceil(len / step_size - 1e-9));
394  return n < 1 ? 1 : n;
395 }
396 
397 // SYMBOL "ivp_begin_run"
398 template<typename T1>
399 void casadi_ivp_begin_run(casadi_ivp_data<T1>* d) {
400  // Handle a pending reset: with fixed steps, a new run of steps up to the stop time
401  const casadi_ivp_prob<T1>* p = d->prob;
402  if (p->step_size > 0) {
403  d->run_t0 = d->t;
404  d->run_t1 = d->t_stop;
405  d->run_n = casadi_ivp_steps(d->t_stop - d->t, p->step_size);
406  d->run_h = (d->run_t1 - d->run_t0) / static_cast<T1>(d->run_n);
407  d->run_k = 0;
408  }
409  d->reset = 0;
410  d->jump = 0;
411 }
412 
413 // SYMBOL "ivp_advance"
414 template<typename T1>
415 void casadi_ivp_advance(casadi_ivp_data<T1>* d, T1 t_out, T1 t_stop, int taping) {
416  // Output at t_out, stepping no further than t_stop; tasks follow, see the method
417  d->t_out = t_out;
418  d->t_stop = t_stop;
419  d->taping = taping;
420  d->status = IVP_SUCCESS;
421  d->fail = 0;
422  d->next = IVP_ADVANCE;
423  d->n_steps_interval = 0;
424  if (d->nom) {
425  // The nominal integration stops after every step, to be replayed
426  casadi_ivp_advance(d->nom, t_out, t_stop, 0);
427  d->nom->pause = 1;
428  }
429 }
430 
431 // SYMBOL "ivp_reset_backward"
432 template<typename T1>
433 void casadi_ivp_reset_backward(casadi_ivp_data<T1>* d) {
434  const casadi_ivp_prob<T1>* p = d->prob;
435  casadi_clear(d->ry, p->nr);
436  casadi_clear(d->adj_p, p->nrp);
437  casadi_clear(d->adj_u, p->nru);
438  casadi_clear(d->jac_adj, p->nnz_jac * p->nadj * (1 + p->nfwd));
439  d->record_pending = 0;
440 }
441 
442 // SYMBOL "ivp_retreat"
443 template<typename T1>
444 void casadi_ivp_retreat(casadi_ivp_data<T1>* d, T1 t_start, T1 t_out) {
445  // Adjoint from t_start back to t_out; skips records of output intervals ending after t_start
446  d->t = d->t_start = t_start;
447  d->t_out = t_out;
448  d->status = IVP_SUCCESS;
449  d->next = IVP_RETREAT;
450 }
451 
452 // SYMBOL "ivp_impulse"
453 template<typename T1>
454 void casadi_ivp_impulse(casadi_ivp_data<T1>* d, T1 t) {
455  // Adjoint seed d->seed of the output at time t; tasks follow, see the method
456  d->t_seed = t;
457  d->status = IVP_SUCCESS;
458  d->next = IVP_IMPULSE;
459 }
460 
461 // SYMBOL "ivp_request"
462 template<typename T1>
463 int casadi_ivp_request(casadi_ivp_data<T1>* d, casadi_ivp_task_t task, casadi_int next) {
464  // Hand a task to the caller, resume at phase next
465  d->active = d;
466  d->task = task;
467  d->next = next;
468  d->fail = 0;
469  return 1;
470 }
471 
472 // SYMBOL "ivp_request_control"
473 template<typename T1>
474 int casadi_ivp_request_control(casadi_ivp_data<T1>* d, casadi_ivp_control_t control,
475  casadi_int next) {
476  // Hand step size control to the caller, resume at phase next
477  d->control = control;
478  return casadi_ivp_request(d, IVP_STEP_CONTROL, next);
479 }
480 
481 // SYMBOL "ivp_stop"
482 template<typename T1>
483 int casadi_ivp_stop(casadi_ivp_data<T1>* d, casadi_ivp_flag_t status) {
484  d->status = status;
485  d->next = IVP_DONE;
486  return 0;
487 }
488 
489 // SYMBOL "ivp_jac_nz"
490 template<typename T1>
491 T1* casadi_ivp_jac_nz(const casadi_ivp_data<T1>* d, T1* jac, casadi_int k,
492  casadi_int ndir, casadi_int dir) {
493  // Nonzeros of Jacobian block k, direction dir, in a buffer with ndir directions per block
494  const casadi_int* off = d->prob->jac_off;
495  return jac + ndir * off[k] + dir * (off[k + 1] - off[k]);
496 }
497 
498 // SYMBOL "ivp_jac_mv"
499 template<typename T1>
500 void casadi_ivp_jac_mv(const casadi_ivp_data<T1>* d, casadi_int k,
501  casadi_int dir, const T1* v, T1* r, int tr) {
502  // r += J_k v (or J_k^T v): Jacobian block k (dir < 0) or its forward derivative
503  const casadi_ivp_prob<T1>* p = d->prob;
504  const T1* nz = dir < 0 ? casadi_ivp_jac_nz(d, d->jac, k, 1, 0)
505  : casadi_ivp_jac_nz(d, d->jac_fwd, k, p->nfwd, dir);
506  casadi_mv(nz, p->sp_jac[k], v, r, tr);
507 }
508 
509 // SYMBOL "ivp_add_vjp"
510 template<typename T1>
511 void casadi_ivp_add_vjp(casadi_ivp_data<T1>* d, T1* r) {
512  // Add x, z adjoints of EVAL_VJP/EVAL_JAC_ADJ to r, p, u adjoints to the running sums
513  const casadi_ivp_prob<T1>* p = d->prob;
514  if (r) {
515  casadi_axpy(p->nrx, 1., d->vjp_x, r);
516  casadi_axpy(p->nrz, 1., d->vjp_z, r + p->nrx);
517  }
518  casadi_axpy(p->nrp, 1., d->vjp_p, d->adj_p);
519  casadi_axpy(p->nru, 1., d->vjp_u, d->adj_u);
520 }
521 
522 // SYMBOL "ivp_clear_vjp"
523 template<typename T1>
524 void casadi_ivp_clear_vjp(casadi_ivp_data<T1>* d) {
525  const casadi_ivp_prob<T1>* p = d->prob;
526  casadi_clear(d->vjp_x, p->nrx);
527  casadi_clear(d->vjp_z, p->nrz);
528  casadi_clear(d->vjp_p, p->nrp);
529  casadi_clear(d->vjp_u, p->nru);
530 }
531 
532 // ---------------------------------------------------------------------------------------
533 // Tape: records [U (step records only), y0, u, h, t0, t1, end of the output interval, type]
534 // ---------------------------------------------------------------------------------------
535 
536 // SYMBOL "ivp_tape_full"
537 template<typename T1>
538 int casadi_ivp_tape_full(casadi_ivp_data<T1>* d, casadi_ivp_record_t record) {
539  // No room for a record of this type (a step with events: and an event record)? Sets the
540  // capacity needed
541  const casadi_ivp_prob<T1>* p = d->prob;
542  casadi_int n = (record == IVP_RECORD_STEP ? p->ny * p->n_stages : 0) + p->ny + p->nu + 5;
543  if (record == IVP_RECORD_STEP && p->ne > 0) n += p->ny + p->nu + 5;
544  d->tape_need = d->tape_pos + n;
545  return n > d->tape_cap - d->tape_pos;
546 }
547 
548 // SYMBOL "ivp_reserve"
549 template<typename T1>
550 int casadi_ivp_reserve(casadi_ivp_data<T1>* d, casadi_ivp_record_t record, casadi_int next) {
551  // Room on the tape for a record, if taping? -1 if so, else the value for the method to return
552  if (!d->taping || !casadi_ivp_tape_full(d, record)) return -1;
553  if (d->prob->tape_size > 0) return casadi_ivp_stop(d, IVP_TAPE_FAILED);
554  return casadi_ivp_request(d, IVP_TAPE_RESERVE, next);
555 }
556 
557 // SYMBOL "ivp_tape_push"
558 template<typename T1>
559 void casadi_ivp_tape_push(casadi_ivp_data<T1>* d, const T1* y0, T1 h, T1 t0, T1 t1) {
560  // Append a record of type d->record from t0 to t1, for which room has been reserved
561  const casadi_ivp_prob<T1>* p = d->prob;
562  casadi_int n_u = d->record == IVP_RECORD_STEP ? p->ny * p->n_stages : 0;
563  T1* rec = d->tape + d->tape_pos;
564  casadi_copy(d->U, n_u, rec);
565  rec += n_u;
566  casadi_copy(y0, p->ny, rec);
567  casadi_copy(d->u_step, p->nu, rec + p->ny);
568  rec += p->ny + p->nu;
569  rec[0] = h;
570  rec[1] = t0;
571  rec[2] = t1;
572  rec[3] = d->t_out;
573  rec[4] = d->record;
574  d->tape_pos += n_u + p->ny + p->nu + 5;
575 }
576 
577 // SYMBOL "ivp_tape_pop"
578 template<typename T1>
579 int casadi_ivp_tape_pop(casadi_ivp_data<T1>* d) {
580  // Restore the last record, clear the seeds of its stages; returns 1 if the tape is empty
581  const casadi_ivp_prob<T1>* p = d->prob;
582  casadi_int n_u;
583  T1* rec;
584  if (d->tape_pos < p->ny + p->nu + 5) return 1;
585  rec = d->tape + d->tape_pos;
586  d->record = static_cast<casadi_ivp_record_t>(static_cast<int>(rec[-1]));
587  d->t_record = rec[-2];
588  d->t1 = rec[-3];
589  d->t0 = rec[-4];
590  d->h = rec[-5];
591  n_u = d->record == IVP_RECORD_STEP ? p->ny * p->n_stages : 0;
592  rec -= p->ny + p->nu + 5;
593  casadi_copy(rec, p->ny, d->y0);
594  casadi_copy(rec + p->ny, p->nu, d->u_step);
595  casadi_copy(rec - n_u, n_u, d->U);
596  d->tape_pos -= n_u + p->ny + p->nu + 5;
597  casadi_clear(d->rU, p->nr * p->n_stages);
598  casadi_clear(d->ry0, p->nr);
599  casadi_clear(d->ru_step, p->nru);
600  return 0;
601 }
602 
603 // ---------------------------------------------------------------------------------------
604 // Stages and dense output
605 // ---------------------------------------------------------------------------------------
606 
607 // SYMBOL "ivp_stage_argument"
608 template<typename T1>
609 void casadi_ivp_stage_argument(casadi_ivp_data<T1>* d, casadi_int i) {
610  // y_stage = y0 + sum_{j<i} a_ij U_j, skipping zero coefficients
611  const casadi_ivp_prob<T1>* p = d->prob;
612  casadi_int j;
613  const T1* a = p->a + i * p->n_stages;
614  casadi_copy(d->y0, p->ny, d->y_stage);
615  for (j = 0; j < i; ++j) if (a[j] != 0) casadi_axpy(p->ny, a[j], d->U + j * p->ny, d->y_stage);
616 }
617 
618 // SYMBOL "ivp_dense_weights"
619 template<typename T1>
620 void casadi_ivp_dense_weights(casadi_ivp_data<T1>* d, T1 theta) {
621  // w_i = m_i(theta), the weights of y(t0 + theta h) = y0 + sum_i m_i(theta) U_i
622  const casadi_ivp_prob<T1>* p = d->prob;
623  casadi_int q;
624  T1 th = theta;
625  casadi_clear(d->w_dense, p->n_stages);
626  for (q = 0; q < p->n_dense; ++q) {
627  casadi_axpy(p->n_stages, th, p->m_dense + q * p->n_stages, d->w_dense);
628  th *= theta;
629  }
630 }
631 
632 // SYMBOL "ivp_interpolate"
633 template<typename T1>
634 void casadi_ivp_interpolate(casadi_ivp_data<T1>* d, T1 t, T1* y) {
635  // Dense output of the current step: y = y0 + sum_i m_i(theta) U_i, weights in d->w_dense
636  const casadi_ivp_prob<T1>* p = d->prob;
637  casadi_ivp_dense_weights(d, (t - d->t0) / d->h);
638  casadi_copy(d->y0, p->ny, y);
639  casadi_mv_dense(d->U, p->ny, p->n_stages, d->w_dense, y, 0);
640 }
641 
642 // SYMBOL "ivp_seed_stages"
643 template<typename T1>
644 void casadi_ivp_seed_stages(casadi_ivp_data<T1>* d) {
645  // Adjoint of the dense output with seed d->seed, to the stages and the start of the step
646  const casadi_ivp_prob<T1>* p = d->prob;
647  casadi_int i;
648  for (i = 0; i < p->n_stages; ++i) {
649  casadi_axpy(p->nr, d->w_dense[i], d->seed, d->rU + i * p->nr);
650  }
651  casadi_axpy(p->nr, 1., d->seed, d->ry0);
652 }
653 
654 // ---------------------------------------------------------------------------------------
655 // Step sizes; adaptive control is in casadi_ivp_adapt.hpp
656 // ---------------------------------------------------------------------------------------
657 
658 // SYMBOL "ivp_isfinite"
659 template<typename T1>
660 int casadi_ivp_isfinite(T1 x) {
661  // False for +-inf and NaN
662  return x - x == 0;
663 }
664 
665 // SYMBOL "ivp_step_limit"
666 template<typename T1>
667 int casadi_ivp_step_limit(const casadi_ivp_data<T1>* d) {
668  // No further step allowed?
669  const casadi_ivp_prob<T1>* p = d->prob;
670  if (p->step_size > 0 && !d->nom) return d->run_k >= d->run_n;
671  return d->n_steps_interval >= p->max_num_steps;
672 }
673 
674 // SYMBOL "ivp_prescribed"
675 template<typename T1>
676 int casadi_ivp_prescribed(const casadi_ivp_data<T1>* d) {
677  // Step sizes prescribed: fixed steps, or replayed from the nominal integration
678  return d->prob->step_size > 0 || d->nom != 0;
679 }
680 
681 // ---------------------------------------------------------------------------------------
682 // Building blocks of the forward integration and of the discrete adjoint
683 // ---------------------------------------------------------------------------------------
684 
685 // SYMBOL "ivp_output"
686 template<typename T1>
687 int casadi_ivp_output(casadi_ivp_data<T1>* d) {
688  // State at t_out <= t, in or at the end of the last step; returns 1 if interpolated
689  const casadi_ivp_prob<T1>* p = d->prob;
690  if (d->t == d->t_out) {
691  casadi_copy(d->y, p->ny, d->y_out);
692  return 0;
693  }
694  casadi_ivp_interpolate(d, d->t_out, d->y_out);
695  return 1;
696 }
697 
698 // SYMBOL "ivp_accept"
699 template<typename T1>
700 void casadi_ivp_accept(casadi_ivp_data<T1>* d) {
701  // The step from t0, with y the solution at its end, is accepted
702  const casadi_ivp_prob<T1>* p = d->prob;
703  if (d->nom) {
704  d->t = d->nom->t;
705  } else if (p->step_size > 0) {
706  d->run_k++;
707  d->t = d->run_k == d->run_n ? d->run_t1 : d->run_t0 + static_cast<T1>(d->run_k) * d->run_h;
708  } else {
709  d->t = d->last_step ? d->t_stop : d->t0 + d->h;
710  }
711  d->last_rejected = 0;
712  d->n_steps_interval++;
713  if (d->taping) {
714  d->record = IVP_RECORD_STEP;
715  casadi_ivp_tape_push(d, d->y0, d->h, d->t0, d->t);
716  }
717  d->n_steps++;
718 }
719 
720 // SYMBOL "ivp_back_begin"
721 template<typename T1>
722 int casadi_ivp_back_begin(casadi_ivp_data<T1>* d) {
723  // Record to process next; returns 0 when the retreat is done (tape exhausted, or the
724  // record belongs to an earlier output interval and becomes pending). An event belongs to the
725  // output interval that contains it
726  T1 r;
727  if (d->status != IVP_SUCCESS) return casadi_ivp_stop(d, d->status);
728  for (;;) {
729  if (!d->record_pending && casadi_ivp_tape_pop(d)) return casadi_ivp_stop(d, IVP_SUCCESS);
730  r = d->record == IVP_RECORD_EVENT ? d->t0 : d->t_record;
731  // Records of later output intervals are skipped with zero adjoint
732  if (r > d->t_start) {
733  d->record_pending = 0;
734  continue;
735  }
736  if (d->record == IVP_RECORD_EVENT ? r <= d->t_out : r < d->t_start) {
737  d->record_pending = 1;
738  d->t = d->t_out;
739  return casadi_ivp_stop(d, IVP_SUCCESS);
740  }
741  d->record_pending = 0;
742  return 1;
743  }
744 }
745 
746 // SYMBOL "ivp_back_step_begin"
747 template<typename T1>
748 void casadi_ivp_back_step_begin(casadi_ivp_data<T1>* d) {
749  // Stage adjoints from y1 = y0 + sum_i m_i U_i, added to the seeds of dense outputs
750  const casadi_ivp_prob<T1>* p = d->prob;
751  casadi_int i;
752  for (i = 0; i < p->n_stages; ++i) casadi_axpy(p->nr, p->m[i], d->ry, d->rU + i * p->nr);
753  casadi_axpy(p->nr, 1., d->ry0, d->ry);
754  casadi_axpy(p->nru, 1., d->ru_step, d->adj_u);
755  d->stage = p->n_stages;
756 }
757 
758 // SYMBOL "ivp_impulse_begin"
759 template<typename T1>
760 int casadi_ivp_impulse_begin(casadi_ivp_data<T1>* d) {
761  // Pops the record containing t_seed (records of later intervals had zero seeds); returns
762  // 1 if t_seed is inside its step, with the dense output at t_seed in y_out
763  const casadi_ivp_prob<T1>* p = d->prob;
764  while (!d->record_pending && !casadi_ivp_tape_pop(d)) {
765  d->record_pending = (d->record == IVP_RECORD_EVENT ? d->t0 : d->t_record) <= d->t_seed;
766  }
767  if (!d->record_pending || d->record != IVP_RECORD_STEP || d->t_seed == d->t1) {
768  // Seed of the state at the end of the pending record
769  casadi_axpy(p->nr, 1., d->seed, d->ry);
770  return casadi_ivp_stop(d, IVP_SUCCESS);
771  }
772  casadi_ivp_interpolate(d, d->t_seed, d->y_out);
773  return 1;
774 }
casadi_int tape_need
Definition: casadi_ivp.hpp:241
casadi_ivp_data< T1 > * active
Definition: casadi_ivp.hpp:191
casadi_int n_jac
Definition: casadi_ivp.hpp:267
const T1 * w
Definition: casadi_ivp.hpp:206
casadi_int ev_iter
Definition: casadi_ivp.hpp:265
casadi_ivp_task_t task
Definition: casadi_ivp.hpp:196
casadi_ivp_flag_t status
Definition: casadi_ivp.hpp:201
casadi_int n_events
Definition: casadi_ivp.hpp:265
casadi_ivp_record_t record
Definition: casadi_ivp.hpp:245
const casadi_ivp_prob< T1 > * prob
Definition: casadi_ivp.hpp:189
casadi_int tape_cap
Definition: casadi_ivp.hpp:241
casadi_int n_rhs
Definition: casadi_ivp.hpp:267
casadi_ivp_control_t control
Definition: casadi_ivp.hpp:198
casadi_int run_n
Definition: casadi_ivp.hpp:249
casadi_int n_steps
Definition: casadi_ivp.hpp:267
casadi_int run_k
Definition: casadi_ivp.hpp:249
casadi_int n_steps_interval
Definition: casadi_ivp.hpp:255
const T1 * y_arg
Definition: casadi_ivp.hpp:206
casadi_int n_reject
Definition: casadi_ivp.hpp:267
casadi_int tape_pos
Definition: casadi_ivp.hpp:241
casadi_int next
Definition: casadi_ivp.hpp:200
const T1 * u
Definition: casadi_ivp.hpp:214
casadi_ivp_data< T1 > * nom
Definition: casadi_ivp.hpp:193
casadi_int stage
Definition: casadi_ivp.hpp:221
casadi_int ev_fired
Definition: casadi_ivp.hpp:265
casadi_int n_vjp
Definition: casadi_ivp.hpp:267
casadi_int nnz_jac
Definition: casadi_ivp.hpp:148
casadi_int nrp
Definition: casadi_ivp.hpp:145
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
casadi_int nx
Definition: casadi_ivp.hpp:145
const casadi_int * sp_jac[IVP_NUM_JAC]
Definition: casadi_ivp.hpp:123
casadi_int ny
Definition: casadi_ivp.hpp:145
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 nru
Definition: casadi_ivp.hpp:145
casadi_int nrq
Definition: casadi_ivp.hpp:145
casadi_int max_num_steps
Definition: casadi_ivp.hpp:137
casadi_int nr
Definition: casadi_ivp.hpp:145
casadi_int nrz
Definition: casadi_ivp.hpp:145
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 nrx
Definition: casadi_ivp.hpp:145
casadi_int nz
Definition: casadi_ivp.hpp:145
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 nq
Definition: casadi_ivp.hpp:145
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
casadi_int nu
Definition: casadi_ivp.hpp:145