casadi_ivp_adapt.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 // Adaptive step size control of the one-step integrators, see casadi_ivp.hpp: the kernel
21 // requests it as task IVP_STEP_CONTROL, so only integrators with adaptive steps need this file
22 
23 // C-REPLACE "static_cast<T1>" "(casadi_real) "
24 // C-REPLACE "fmin" "casadi_fmin"
25 // C-REPLACE "fmax" "casadi_fmax"
26 // C-REPLACE "fabs" "casadi_fabs"
27 // C-REPLACE "casadi_ivp_prob<T1>" "struct casadi_ivp_prob"
28 // C-REPLACE "casadi_ivp_data<T1>" "struct casadi_ivp_data"
29 
30 // SYMBOL "ivp_step_size"
31 template<typename T1>
32 void casadi_ivp_step_size(casadi_ivp_data<T1>* d) {
33  // Size h of a step from t; h = 0 if an initial estimate is needed first
34  const casadi_ivp_prob<T1>* p = d->prob;
35  d->last_clipped = 0;
36  d->last_step = 0;
37  if (d->h_next <= 0) {
38  if (p->h_init <= 0) {
39  d->h = 0;
40  return;
41  }
42  d->h_next = p->h_init;
43  }
44  d->h = p->h_max > 0 ? fmin(d->h_next, p->h_max) : d->h_next;
45  if (d->t + 1.05 * d->h >= d->t_stop) {
46  // Hit the stop time exactly
47  d->last_clipped = d->t + d->h > d->t_stop;
48  d->h = d->t_stop - d->t;
49  d->last_step = 1;
50  } else if (d->t + 2 * d->h > d->t_stop) {
51  // Avoid a tiny last step
52  d->h = 0.5 * (d->t_stop - d->t);
53  }
54 }
55 
56 // SYMBOL "ivp_initial_step"
57 template<typename T1>
58 void casadi_ivp_initial_step(casadi_ivp_data<T1>* d, const T1* f0) {
59  // Initial step size from |y0| and |f(y0)| over the error controlled states
60  const casadi_ivp_prob<T1>* p = d->prob;
61  casadi_int i;
62  T1 sc, d0 = 0, d1 = 0, h;
63  for (i = 0; i < p->nx_err; ++i) {
64  sc = p->abstol + p->reltol * fabs(d->y[i]);
65  d0 += (d->y[i] / sc) * (d->y[i] / sc);
66  d1 += (f0[i] / sc) * (f0[i] / sc);
67  }
68  d0 = sqrt(d0 / static_cast<T1>(p->nx_err));
69  d1 = sqrt(d1 / static_cast<T1>(p->nx_err));
70  h = (d0 < 1e-5 || d1 < 1e-5) ? 1e-6 : 0.01 * d0 / d1;
71  h = fmin(h, d->t_stop - d->t);
72  d->h_next = fmax(h, p->h_min);
73 }
74 
75 // SYMBOL "ivp_error_norm"
76 template<typename T1>
77 T1 casadi_ivp_error_norm(casadi_ivp_data<T1>* d) {
78  // Weighted RMS norm of the error estimate over the error controlled entries
79  const casadi_ivp_prob<T1>* p = d->prob;
80  casadi_int k, i, n, offset[3], n_err[3];
81  T1 ri, sum;
82  T1* e = d->y_stage;
83  // e = sum_j m_err_j U_j
84  casadi_clear(e, p->ny);
85  casadi_mv_dense(d->U, p->ny, p->n_stages, p->m_err, e, 0);
86  offset[0] = 0; offset[1] = p->nx; offset[2] = p->nx + p->nz;
87  n_err[0] = p->nx_err; n_err[1] = p->nz_err; n_err[2] = p->nq_err;
88  sum = 0;
89  n = 0;
90  for (k = 0; k < 3; ++k) {
91  for (i = offset[k]; i < offset[k] + n_err[k]; ++i) {
92  ri = e[i] / (p->abstol + p->reltol * fmax(fabs(d->y0[i]), fabs(d->y[i])));
93  sum += ri * ri;
94  n++;
95  }
96  }
97  return n > 0 ? sqrt(sum / static_cast<T1>(n)) : 0;
98 }
99 
100 // SYMBOL "ivp_reject"
101 template<typename T1>
102 void casadi_ivp_reject(casadi_ivp_data<T1>* d, T1 fac) {
103  // Restore the state, shrink the step by fac; status IVP_STEP_TOO_SMALL below h_min
104  const casadi_ivp_prob<T1>* p = d->prob;
105  casadi_copy(d->y0, p->ny, d->y);
106  d->n_reject++;
107  d->last_rejected = 1;
108  d->h_next = d->h * fac;
109  d->accepted = 0;
110  if (d->h_next < p->h_min) d->status = IVP_STEP_TOO_SMALL;
111 }
112 
113 // SYMBOL "ivp_error_test"
114 template<typename T1>
115 void casadi_ivp_error_test(casadi_ivp_data<T1>* d) {
116  // Error test of the candidate y, PI controller for the next step size
117  const casadi_ivp_prob<T1>* p = d->prob;
118  T1 err, k, fac;
119  err = casadi_ivp_error_norm(d);
120  k = static_cast<T1>(p->order_err + 1);
121  if (!(err <= 1)) {
122  // Reject (also if not a number)
123  fac = 0.25;
124  if (casadi_ivp_isfinite(err)) fac = fmax(p->fac_min, p->safety * pow(err, -1. / k));
125  casadi_ivp_reject(d, fac);
126  return;
127  }
128  // PI controller
129  err = fmax(err, 1e-10);
130  fac = p->safety * pow(err, -0.7 / k) * pow(d->err_prev, 0.4 / k);
131  fac = fmin(p->fac_max, fmax(p->fac_min, fac));
132  if (d->last_rejected) fac = fmin(fac, 1.);
133  d->err_prev = fmax(err, 1e-4);
134  // A step clipped at the stop time does not shrink the proposal
135  d->h_next = d->last_clipped ? fmax(d->h_next, d->h * fac) : d->h * fac;
136  d->accepted = 1;
137 }
138 
139 // SYMBOL "ivp_control"
140 template<typename T1>
141 void casadi_ivp_control(casadi_ivp_data<T1>* d) {
142  // Carry out the step size control requested by the kernel (task IVP_STEP_CONTROL)
143  switch (d->control) {
144  case IVP_CONTROL_INIT:
145  casadi_ivp_initial_step(d, d->U);
146  casadi_ivp_step_size(d);
147  break;
148  case IVP_CONTROL_SIZE:
149  casadi_ivp_step_size(d);
150  break;
151  case IVP_CONTROL_TEST:
152  casadi_ivp_error_test(d);
153  break;
154  case IVP_CONTROL_FAIL:
155  casadi_ivp_reject(d, 0.25);
156  break;
157  }
158 }
T sum(const std::vector< T > &values)
sum
casadi_ivp_flag_t status
Definition: casadi_ivp.hpp:201
const casadi_ivp_prob< T1 > * prob
Definition: casadi_ivp.hpp:189
casadi_ivp_control_t control
Definition: casadi_ivp.hpp:198
casadi_int n_reject
Definition: casadi_ivp.hpp:267
casadi_int order_err
Definition: casadi_ivp.hpp:134
casadi_int nz_err
Definition: casadi_ivp.hpp:130
const T1 * m_err
Definition: casadi_ivp.hpp:135
casadi_int nx
Definition: casadi_ivp.hpp:145
casadi_int ny
Definition: casadi_ivp.hpp:145
casadi_int nx_err
Definition: casadi_ivp.hpp:130
casadi_int nz
Definition: casadi_ivp.hpp:145
casadi_int nq_err
Definition: casadi_ivp.hpp:130
casadi_int n_stages
Definition: casadi_ivp.hpp:117