idas_interface.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "idas_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
28 
29 // Macro for error handling
30 #define THROWING(fcn, ...) \
31 idas_error(CASADI_STR(fcn), fcn(__VA_ARGS__))
32 
33 namespace casadi {
34 
35 extern "C"
36 int CASADI_INTEGRATOR_IDAS_EXPORT
37  casadi_register_integrator_idas(Integrator::Plugin* plugin) {
38  plugin->creator = IdasInterface::creator;
39  plugin->name = "idas";
40  plugin->doc = IdasInterface::meta_doc.c_str();
41  plugin->version = CASADI_VERSION;
42  plugin->options = &IdasInterface::options_;
43  plugin->deserialize = &IdasInterface::deserialize;
44  return 0;
45 }
46 
47 extern "C"
48 void CASADI_INTEGRATOR_IDAS_EXPORT casadi_load_integrator_idas() {
50 }
51 
52 IdasInterface::IdasInterface(const std::string& name, const Function& dae,
53  double t0, const std::vector<double>& tout) : SundialsInterface(name, dae, t0, tout) {
54 }
55 
57  clear_mem();
58 }
59 
62  {{"suppress_algebraic",
63  {OT_BOOL,
64  "Suppress algebraic variables in the error testing"}},
65  {"calc_ic",
66  {OT_BOOL,
67  "Use IDACalcIC to get consistent initial conditions."}},
68  {"constraints",
69  {OT_INTVECTOR,
70  "Constrain the solution y=[x,z]. 0 (default): no constraint on yi, "
71  "1: yi >= 0.0, -1: yi <= 0.0, 2: yi > 0.0, -2: yi < 0.0."}},
72  {"calc_icB",
73  {OT_BOOL,
74  "Use IDACalcIC to get consistent initial conditions for "
75  "backwards system [default: equal to calc_ic]."}},
76  {"abstolv",
78  "Absolute tolerarance for each component"}},
79  {"max_step_size",
80  {OT_DOUBLE,
81  "Maximim step size"}},
82  {"first_time",
83  {OT_DOUBLE,
84  "First requested time as a fraction of the time interval"}},
85  {"cj_scaling",
86  {OT_BOOL,
87  "IDAS scaling on cj for the user-defined linear solver module"}},
88  {"init_xdot",
90  "Initial values for the state derivatives"}}
91  }
92 };
93 
94 void IdasInterface::init(const Dict& opts) {
95  if (verbose_) casadi_message(name_ + "::init");
96 
97  // Call the base class init
99 
100  // Default options
101  cj_scaling_ = true;
102  calc_ic_ = true;
103  suppress_algebraic_ = false;
104 
105  // Read options
106  for (auto&& op : opts) {
107  if (op.first=="init_xdot") {
108  init_xdot_ = op.second;
109  } else if (op.first=="cj_scaling") {
110  cj_scaling_ = op.second;
111  } else if (op.first=="calc_ic") {
112  calc_ic_ = op.second;
113  } else if (op.first=="suppress_algebraic") {
114  suppress_algebraic_ = op.second;
115  } else if (op.first=="constraints") {
116  y_c_ = op.second;
117  } else if (op.first=="abstolv") {
118  abstolv_ = op.second;
119  }
120  }
121 
122  // Default dependent options
124  first_time_ = tout_.back();
125 
126  // Read dependent options
127  for (auto&& op : opts) {
128  if (op.first=="calc_icB") {
129  calc_icB_ = op.second;
130  } else if (op.first=="first_time") {
131  first_time_ = op.second;
132  }
133  }
134 
135  // Get initial conditions for the state derivatives
136  if (init_xdot_.empty()) {
137  init_xdot_.resize(nx_, 0);
138  } else {
139  casadi_assert(
140  init_xdot_.size()==nx_,
141  "Option \"init_xdot\" has incorrect length. Expecting " + str(nx_) + ", "
142  "but got " + str(init_xdot_.size()) + ". "
143  "Note that this message may actually be generated by the augmented integrator. "
144  "In that case, make use of the 'augmented_options' options "
145  "to correct 'init_xdot' for the augmented integrator.");
146  }
147 
148  // Leave forward sensitivity states unconstrained.
149  if (nfwd_ > 0 && !y_c_.empty() && y_c_.size() == nx1_ + nz1_) {
150  y_c_.insert(y_c_.begin() + nx1_, nx_ - nx1_, 0);
151  y_c_.resize(nx_ + nz_, 0);
152  opts_["constraints"] = y_c_;
153  }
154 
155  // Constraints
156  casadi_assert(y_c_.size() == nx_+nz_ || y_c_.empty(),
157  "Constraint vector if supplied, must be of length nx+nz, but got "
158  + str(y_c_.size()) + " and nx+nz = " + str(nx_+nz_) + ".");
159 
160  // For Jacobian calculation
161  alloc_w(nx_ + nz_); // casadi_copy_block
162 }
163 
164 int IdasInterface::resF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr, void *user_data) {
165  try {
166  auto m = to_mem(user_data);
167  auto& s = m->self;
168  if (s.calc_daeF(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
169  NV_DATA_S(rr), NV_DATA_S(rr) + s.nx_)) return 1;
170 
171  // Subtract state derivative to get residual
172  casadi_axpy(s.nx_, -1., NV_DATA_S(xzdot), NV_DATA_S(rr));
173  return 0;
174  } catch(std::exception& e) { // non-recoverable error
175  uerr() << "res failed: " << e.what() << std::endl;
176  return -1;
177  }
178 }
179 
180 void IdasInterface::ehfun(int error_code, const char *module, const char *function,
181  char *msg, void *eh_data) {
182  try {
183  //auto m = to_mem(eh_data);
184  //auto& s = m->self;
185  uerr() << msg << std::endl;
186  } catch(std::exception& e) {
187  uerr() << "ehfun failed: " << e.what() << std::endl;
188  }
189 }
190 
191 int IdasInterface::jtimesF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr, N_Vector v,
192  N_Vector Jv, double cj, void *user_data, N_Vector tmp1, N_Vector tmp2) {
193  try {
194  auto m = to_mem(user_data);
195  auto& s = m->self;
196  if (s.calc_jtimesF(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
197  NV_DATA_S(v), NV_DATA_S(v) + s.nx_,
198  NV_DATA_S(Jv), NV_DATA_S(Jv) + s.nx_)) return 1;
199 
200  // Subtract state derivative to get residual
201  casadi_axpy(s.nx_, -cj, NV_DATA_S(v), NV_DATA_S(Jv));
202 
203  return 0;
204  } catch(std::exception& e) { // non-recoverable error
205  uerr() << "jtimesF failed: " << e.what() << std::endl;
206  return -1;
207  }
208 }
209 
210 int IdasInterface::jtimesB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz,
211  N_Vector rxzdot, N_Vector resvalB, N_Vector v, N_Vector Jv,
212  double cjB, void *user_data, N_Vector tmp1B, N_Vector tmp2B) {
213  try {
214  auto m = to_mem(user_data);
215  auto& s = m->self;
216  // The function is linear so the Jacobian-times-vector function is the function itself
217  if (s.calc_daeB(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
218  NV_DATA_S(v), NV_DATA_S(v) + s.nrx_, nullptr,
219  NV_DATA_S(Jv), NV_DATA_S(Jv) + s.nrx_)) return 1;
220  // Subtract state derivative to get residual
221  casadi_axpy(s.nrx_, cjB, NV_DATA_S(v), NV_DATA_S(Jv));
222 
223  return 0;
224  } catch(std::exception& e) { // non-recoverable error
225  uerr() << "jtimesB failed: " << e.what() << std::endl;
226  return -1;
227  }
228 }
229 
230 int IdasInterface::init_mem(void* mem) const {
231  if (SundialsInterface::init_mem(mem)) return 1;
232  auto m = to_mem(mem);
233 
234  // Create IDAS memory block
235  m->mem = IDACreate();
236  casadi_assert(m->mem!=nullptr, "IDACreate: Creation failed");
237 
238  // Set error handler function
239  THROWING(IDASetErrHandlerFn, m->mem, ehfun, m);
240 
241  // Set user data
242  THROWING(IDASetUserData, m->mem, m);
243 
244  // Allocate n-vectors for ivp
245  m->v_xzdot = N_VNew_Serial(nx_+nz_);
246 
247  // Initialize Idas
248  double t0 = 0;
249  N_VConst(0.0, m->v_xz);
250  N_VConst(0.0, m->v_xzdot);
251  IDAInit(m->mem, resF, t0, m->v_xz, m->v_xzdot);
252  if (verbose_) casadi_message("IDA initialized");
253 
254  // Include algebraic variables in error testing
255  THROWING(IDASetSuppressAlg, m->mem, suppress_algebraic_);
256 
257  // Maxinum order for the multistep method
258  THROWING(IDASetMaxOrd, m->mem, max_multistep_order_);
259 
260  // Initial step size
261  if (step0_!=0) THROWING(IDASetInitStep, m->mem, step0_);
262 
263  // Set maximum step size
264  if (max_step_size_!=0) THROWING(IDASetMaxStep, m->mem, max_step_size_);
265 
266  // Set constraints
267  if (!y_c_.empty()) {
268  N_Vector domain = N_VNew_Serial(nx_+nz_);
269  std::copy(y_c_.begin(), y_c_.end(), NV_DATA_S(domain));
270 
271  // Pass to IDA
272  int flag = IDASetConstraints(m->mem, domain);
273  casadi_assert_dev(flag==IDA_SUCCESS);
274 
275  // Free the temporary vector
276  N_VDestroy_Serial(domain);
277  }
278 
279  // Maximum order of method
280  if (max_order_) THROWING(IDASetMaxOrd, m->mem, max_order_);
281 
282  // Coeff. in the nonlinear convergence test
283  if (nonlin_conv_coeff_!=0) THROWING(IDASetNonlinConvCoef, m->mem, nonlin_conv_coeff_);
284 
285  // Scaling
286  if (!abstolv_.empty()) {
287  // Vector absolute tolerances
288  N_Vector nv_abstol = N_VNew_Serial(static_cast<long>(abstolv_.size()));
289  std::copy(abstolv_.begin(), abstolv_.end(), NV_DATA_S(nv_abstol));
290  THROWING(IDASVtolerances, m->mem, reltol_, nv_abstol);
291  N_VDestroy_Serial(nv_abstol);
292  } else if (scale_abstol_) {
293  // Scale absolute tolerances with nominal values
294  THROWING(IDASVtolerances, m->mem, reltol_, m->abstolv);
295  } else {
296  // Scalar absolute tolerances
297  THROWING(IDASStolerances, m->mem, reltol_, abstol_);
298  }
299 
300  // Maximum number of steps
301  THROWING(IDASetMaxNumSteps, m->mem, max_num_steps_);
302 
303  // Set algebraic components
304  N_Vector id = N_VNew_Serial(nx_+nz_);
305  std::fill_n(NV_DATA_S(id), nx_, 1);
306  std::fill_n(NV_DATA_S(id)+nx_, nz_, 0);
307 
308  // Pass this information to IDAS
309  THROWING(IDASetId, m->mem, id);
310 
311  // Delete the allocated memory
312  N_VDestroy_Serial(id);
313 
314  // attach a linear solver
315  if (newton_scheme_==SD_DIRECT) {
316  // Direct scheme
317  IDAMem IDA_mem = IDAMem(m->mem);
318  IDA_mem->ida_lmem = m;
319  IDA_mem->ida_lsetup = lsetupF;
320  IDA_mem->ida_lsolve = lsolveF;
321  IDA_mem->ida_setupNonNull = TRUE;
322  } else {
323  // Iterative scheme
324  switch (newton_scheme_) {
325  case SD_DIRECT: casadi_assert_dev(0);
326  case SD_GMRES: THROWING(IDASpgmr, m->mem, max_krylov_); break;
327  case SD_BCGSTAB: THROWING(IDASpbcg, m->mem, max_krylov_); break;
328  case SD_TFQMR: THROWING(IDASptfqmr, m->mem, max_krylov_); break;
329  }
330  THROWING(IDASpilsSetJacTimesVecFn, m->mem, jtimesF);
331  if (use_precon_) THROWING(IDASpilsSetPreconditioner, m->mem, psetupF, psolveF);
332  }
333 
334  // Quadrature equations
335  if (nq1_ > 0) {
336 
337  // Initialize quadratures in IDAS
338  THROWING(IDAQuadInit, m->mem, rhsQF, m->v_q);
339 
340  // Should the quadrature errors be used for step size control?
341  if (quad_err_con_) {
342  THROWING(IDASetQuadErrCon, m->mem, true);
343 
344  // Quadrature error tolerances
345  // TODO(Joel): vector absolute tolerances
346  THROWING(IDAQuadSStolerances, m->mem, reltol_, abstol_);
347  }
348  }
349 
350  if (verbose_) casadi_message("Attached linear solver");
351 
352  // Adjoint sensitivity problem
353  if (nadj_ > 0) {
354  m->v_adj_xzdot = N_VNew_Serial(nrx_+nrz_);
355  N_VConst(0.0, m->v_adj_xz);
356  N_VConst(0.0, m->v_adj_xzdot);
357  }
358  if (verbose_) casadi_message("Initialized adjoint sensitivities");
359 
360  // Initialize adjoint sensitivities
361  if (nadj_ > 0) {
362  int interpType = interp_==SD_HERMITE ? IDA_HERMITE : IDA_POLYNOMIAL;
363  THROWING(IDAAdjInit, m->mem, steps_per_checkpoint_, interpType);
364  }
365 
366  m->first_callB = true;
367  return 0;
368 }
369 
370 void IdasInterface::reset(IntegratorMemory* mem, bool first_call) const {
371  if (verbose_) casadi_message(name_ + "::reset");
372  auto m = to_mem(mem);
373 
374  // Reset the base classes
375  SundialsInterface::reset(mem, first_call);
376 
377  // Only reinitialize solver at first call or if event handling is required, as cvodes
378  if (first_call || ne_ > 0) {
379  // Re-initialize, from the initial guess of the state derivatives at the first call
380  if (first_call) {
381  N_VConst(0.0, m->v_xzdot);
382  std::copy(init_xdot_.begin(), init_xdot_.end(), NV_DATA_S(m->v_xzdot));
383  }
384 
385  THROWING(IDAReInit, m->mem, m->t, m->v_xz, m->v_xzdot);
386 
387  // Re-initialize quadratures
388  if (nq1_ > 0) THROWING(IDAQuadReInit, m->mem, m->v_q);
389 
390  // Correct initial conditions, if necessary
391  if (calc_ic_) {
392  THROWING(IDACalcIC, m->mem, IDA_YA_YDP_INIT , first_time_);
393  THROWING(IDAGetConsistentIC, m->mem, m->v_xz, m->v_xzdot);
394  }
395  }
396 
397  // Re-initialize backward integration
398  if (first_call && nadj_ > 0) THROWING(IDAAdjReInit, m->mem);
399 }
400 
402  auto m = to_mem(mem);
403 
404  // Do not integrate past change in input signals or past the end
405  // The event handling may cause the stop time to become smaller than internal time reached,
406  // in which case the stop time cannot be enforced
407  if (m->t_stop >= m->tcur) {
408  THROWING(IDASetStopTime, m->mem, m->t_stop);
409  }
410 
411  // Integrate, unless already at desired time
412  double ttol = 1e-9; // tolerance
413  if (fabs(m->t - m->t_next) >= ttol) {
414  // Integrate forward ...
415  double tret = m->t;
416  if (nrx_>0) { // ... with taping
417  THROWING(IDASolveF, m->mem, m->t_next, &tret, m->v_xz, m->v_xzdot, IDA_NORMAL, &m->ncheck);
418  } else { // ... without taping
419  THROWING(IDASolve, m->mem, m->t_next, &tret, m->v_xz, m->v_xzdot, IDA_NORMAL);
420  }
421  // Get quadratures
422  if (nq1_ > 0) THROWING(IDAGetQuad, m->mem, &tret, m->v_q);
423  }
424 
425  // Set function outputs
426  casadi_copy(NV_DATA_S(m->v_xz), nx_ + nz_, m->x);
427  casadi_copy(NV_DATA_S(m->v_q), nq_, m->q);
428 
429  // Get stats
430  THROWING(IDAGetIntegratorStats, m->mem, &m->nsteps, &m->nfevals, &m->nlinsetups,
431  &m->netfails, &m->qlast, &m->qcur, &m->hinused, &m->hlast, &m->hcur, &m->tcur);
432  THROWING(IDAGetNonlinSolvStats, m->mem, &m->nniters, &m->nncfails);
433 
434  return 0;
435 }
436 
438  if (verbose_) casadi_message(name_ + "::resetB");
439  auto m = to_mem(mem);
440 
441  // Reset initial guess
442  N_VConst(0.0, m->v_adj_xz);
443 
444  // Reset the base classes
446 
447  // Reset initial guess
448  N_VConst(0.0, m->v_adj_xzdot);
449 }
450 
451 void IdasInterface::z_impulseB(IdasMemory* m, const double* adj_z) const {
452  // Quick return if nothing to propagate
453  if (all_zero(adj_z, nrz_)) return;
454  // We have the following solved nonlinear system of equations:
455  // f_alg(x, z) == 0,
456  // which implicitly defines z as a function of x
457  // Linearize w.r.t. x:
458  // df_alg/dz * dz/dx + df_alg/dx == 0 <=> dz/dx = -inv(df_alg/dz)*df_alg/dx
459  // Want to calculate:
460  // adj_x = (dz/dx)^T * adj_z = -(df_alg/dx)^T * inv((df_alg/dz)^T) * adj_z
461  // = -(df_alg/dx)^T * w,
462  // where
463  // (df_alg/dz)^T * w = adj_z
464  // Augment linear system to get the system we are able to factorize
465  // [(df_ode/dx)^T - cj*I, (df_alg/dx)^T; (df_ode/dz)^T, (df_alg/dz)^T] * [v; w] = [0; adj_z]
466  // (Re)factorize linear system
467  if (psetupF(m->t, m->v_xz, m->v_xzdot, nullptr, m->cj_last, m, nullptr, nullptr, nullptr))
468  casadi_error("Linear system factorization for backwards initial conditions failed");
469  // Right-hand-side for linear system in m->tmp2
470  casadi_clear(m->tmp2, nrx_);
471  casadi_copy(adj_z, nrz_, m->tmp2 + nrx_);
472  // Solve transposed linear system of equations (Note: m->tmp2 not used since rxz null)
473  if (solve_transposed(m, m->t, NV_DATA_S(m->v_xz), nullptr, m->tmp2, m->tmp2)) {
474  casadi_error("Linear system solve for backwards initial conditions failed");
475  }
476  // Calculate: -adj_x = (df_alg/dx)^T * w
477  casadi_clear(m->tmp2, nrx_);
478  if (calc_daeB(m, m->t, NV_DATA_S(m->v_xz), NV_DATA_S(m->v_xz) + nx_,
479  m->tmp2, m->tmp2 + nrx_, nullptr, m->tmp1, m->tmp1 + nrx_)) {
480  casadi_error("Adjoint seed propagation for backwards initial conditions failed");
481  }
482  // Add contribution to backward state
483  casadi_axpy(nrx_, -1., m->tmp1, NV_DATA_S(m->v_adj_xz));
484  // Direct contribution: -adj_p = (df_alg/dp)^T * w, -adj_u = (df_alg/du)^T * w
485  if (nrq_ + nuq_ > 0) {
486  // quadB is linear in the seeds: evaluate with and without w to cancel the adj_quad term
487  for (double s : {-1., 1.}) {
488  if (calc_quadB(m, m->t, NV_DATA_S(m->v_xz), NV_DATA_S(m->v_xz) + nx_,
489  m->tmp2, s < 0 ? m->tmp2 + nrx_ : nullptr, m->tmp1, m->tmp1 + nrq_)) {
490  casadi_error("Adjoint seed propagation for backwards initial conditions failed");
491  }
492  casadi_axpy(nrq_ + nuq_, s, m->tmp1, NV_DATA_S(m->v_adj_pu));
493  }
494  }
495 }
496 
498  const double* adj_x, const double* adj_z, const double* adj_q) const {
499  auto m = to_mem(mem);
500 
501  // Call method in base class
502  SundialsInterface::impulseB(mem, adj_x, adj_z, adj_q);
503 
504  // Propagate impulse from adj_z to adj_x
505  z_impulseB(m, adj_z);
506 
507  if (m->first_callB) {
508  // Create backward problem
509  THROWING(IDACreateB, m->mem, &m->whichB);
510  THROWING(IDAInitB, m->mem, m->whichB, resB, m->t, m->v_adj_xz, m->v_adj_xzdot);
511  THROWING(IDASStolerancesB, m->mem, m->whichB, reltol_, abstol_);
512  THROWING(IDASetUserDataB, m->mem, m->whichB, m);
513  THROWING(IDASetMaxNumStepsB, m->mem, m->whichB, max_num_steps_);
514 
515 
516  // Set algebraic components
517  N_Vector id = N_VNew_Serial(nrx_+nrz_);
518  std::fill_n(NV_DATA_S(id), nrx_, 1);
519  std::fill_n(NV_DATA_S(id)+nrx_, nrz_, 0);
520  THROWING(IDASetIdB, m->mem, m->whichB, id);
521  N_VDestroy_Serial(id);
522 
523  // attach linear solver
524  if (newton_scheme_==SD_DIRECT) {
525  // Direct scheme
526  IDAMem IDA_mem = IDAMem(m->mem);
527  IDAadjMem IDAADJ_mem = IDA_mem->ida_adj_mem;
528  IDABMem IDAB_mem = IDAADJ_mem->IDAB_mem;
529  IDAB_mem->ida_lmem = m;
530  IDAB_mem->IDA_mem->ida_lmem = m;
531  IDAB_mem->IDA_mem->ida_lsetup = lsetupB;
532  IDAB_mem->IDA_mem->ida_lsolve = lsolveB;
533  IDAB_mem->IDA_mem->ida_setupNonNull = TRUE;
534  } else {
535  // Iterative scheme
536  switch (newton_scheme_) {
537  case SD_DIRECT: casadi_assert_dev(0);
538  case SD_GMRES: THROWING(IDASpgmrB, m->mem, m->whichB, max_krylov_); break;
539  case SD_BCGSTAB: THROWING(IDASpbcgB, m->mem, m->whichB, max_krylov_); break;
540  case SD_TFQMR: THROWING(IDASptfqmrB, m->mem, m->whichB, max_krylov_); break;
541  }
542  THROWING(IDASpilsSetJacTimesVecFnB, m->mem, m->whichB, jtimesB);
543  if (use_precon_) THROWING(IDASpilsSetPreconditionerB, m->mem, m->whichB, psetupB, psolveB);
544  }
545 
546  // Quadratures for the adjoint problem
547  if (nrq_ > 0 || nuq_ > 0) {
548  THROWING(IDAQuadInitB, m->mem, m->whichB, rhsQB, m->v_adj_pu);
549  if (quad_err_con_) {
550  THROWING(IDASetQuadErrConB, m->mem, m->whichB, true);
551  THROWING(IDAQuadSStolerancesB, m->mem, m->whichB, reltol_, abstol_);
552  }
553  }
554 
555  // Mark initialized
556  m->first_callB = false;
557  } else {
558  // Re-initialize
559  THROWING(IDAReInitB, m->mem, m->whichB, m->t, m->v_adj_xz, m->v_adj_xzdot);
560  if (nrq_ > 0 || nuq_ > 0) {
561  // Workaround (bug in SUNDIALS)
562  // THROWING(IDAQuadReInitB, m->mem, m->whichB[dir], m->rq[dir]);
563  void* memB = IDAGetAdjIDABmem(m->mem, m->whichB);
564  THROWING(IDAQuadReInit, memB, m->v_adj_pu);
565  }
566  }
567 
568  // Correct initial values for the integration if necessary
569  if (calc_icB_ && m->k == nt() - 1) {
570  THROWING(IDACalcICB, m->mem, m->whichB, t0_, m->v_xz, m->v_xzdot);
571  THROWING(IDAGetConsistentICB, m->mem, m->whichB, m->v_adj_xz, m->v_adj_xzdot);
572  }
573 }
574 
575 void IdasInterface::retreat(IntegratorMemory* mem, const double* u,
576  double* adj_x, double* adj_p, double* adj_u) const {
577  auto m = to_mem(mem);
578 
579  // Set controls
580  casadi_copy(u, nu_, m->u);
581 
582  // Integrate, unless already at desired time
583  if (m->t_next < m->t) {
584  double tret = m->t;
585  THROWING(IDASolveB, m->mem, m->t_next, IDA_NORMAL);
586  THROWING(IDAGetB, m->mem, m->whichB, &tret, m->v_adj_xz, m->v_adj_xzdot);
587  if (nrq_ > 0 || nuq_ > 0) {
588  THROWING(IDAGetQuadB, m->mem, m->whichB, &tret, m->v_adj_pu);
589  }
590  // Interpolate to get current state
591  THROWING(IDAGetAdjY, m->mem, m->t_next, m->v_xz, m->v_xzdot);
592  }
593 
594  // Save outputs
595  casadi_copy(NV_DATA_S(m->v_adj_xz), nrx_, adj_x);
596  casadi_copy(NV_DATA_S(m->v_adj_pu), nrq_, adj_p);
597  casadi_copy(NV_DATA_S(m->v_adj_pu) + nrq_, nuq_, adj_u);
598 
599  // Get stats
600  IDAMem IDA_mem = IDAMem(m->mem);
601  IDAadjMem IDAADJ_mem = IDA_mem->ida_adj_mem;
602  IDABMem IDAB_mem = IDAADJ_mem->IDAB_mem;
603  THROWING(IDAGetIntegratorStats, IDAB_mem->IDA_mem, &m->nstepsB, &m->nfevalsB,
604  &m->nlinsetupsB, &m->netfailsB, &m->qlastB, &m->qcurB, &m->hinusedB,
605  &m->hlastB, &m->hcurB, &m->tcurB);
606  THROWING(IDAGetNonlinSolvStats, IDAB_mem->IDA_mem, &m->nnitersB, &m->nncfailsB);
607 
608  // Add offset corresponding to counters that were set to zero at reinitializations
609  add_offsets(m);
610 }
611 
612 void IdasInterface::idas_error(const char* module, int flag) {
613  // Successfull return or warning
614  if (flag>=IDA_SUCCESS) return;
615  // Construct error message
616  char* flagname = IDAGetReturnFlagName(flag);
617  std::stringstream ss;
618  ss << module << " returned \"" << flagname << "\". Consult IDAS documentation.";
619  free(flagname); // NOLINT
620  casadi_error(ss.str());
621 }
622 
623 int IdasInterface::rhsQF(double t, N_Vector xz, N_Vector xzdot, N_Vector qdot, void *user_data) {
624  try {
625  auto m = to_mem(user_data);
626  auto& s = m->self;
627  if (s.calc_quadF(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_, NV_DATA_S(qdot))) return 1;
628 
629  return 0;
630  } catch(std::exception& e) { // non-recoverable error
631  uerr() << "rhsQ failed: " << e.what() << std::endl;
632  return -1;
633  }
634 }
635 
636 int IdasInterface::resB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz,
637  N_Vector rxzdot, N_Vector rr, void *user_data) {
638  try {
639  auto m = to_mem(user_data);
640  auto& s = m->self;
641  if (s.calc_daeB(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
642  NV_DATA_S(rxz), NV_DATA_S(rxz) + s.nrx_, m->adj_q,
643  NV_DATA_S(rr), NV_DATA_S(rr) + s.nrx_)) return 1;
644 
645  // Subtract state derivative to get residual
646  casadi_axpy(s.nrx_, 1., NV_DATA_S(rxzdot), NV_DATA_S(rr));
647 
648  return 0;
649  } catch(std::exception& e) { // non-recoverable error
650  uerr() << "resB failed: " << e.what() << std::endl;
651  return -1;
652  }
653 }
654 
655 int IdasInterface::rhsQB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz,
656  N_Vector rxzdot, N_Vector ruqdot, void *user_data) {
657  try {
658  auto m = to_mem(user_data);
659  auto& s = m->self;
660  if (s.calc_quadB(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
661  NV_DATA_S(rxz), NV_DATA_S(rxz) + s.nrx_,
662  NV_DATA_S(ruqdot), NV_DATA_S(ruqdot) + s.nrq_)) return 1;
663 
664  // Negate (note definition of g)
665  casadi_scal(s.nrq_ + s.nuq_, -1., NV_DATA_S(ruqdot));
666 
667  return 0;
668  } catch(std::exception& e) { // non-recoverable error
669  uerr() << "resQB failed: " << e.what() << std::endl;
670  return -1;
671  }
672 }
673 
674 int IdasInterface::psolveF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr,
675  N_Vector rvec, N_Vector zvec, double cj, double delta, void *user_data, N_Vector tmp) {
676  try {
677  auto m = to_mem(user_data);
678  auto& s = m->self;
679 
680  // Get right-hand sides in m->tmp1, ordered by sensitivity directions
681  double* vx = NV_DATA_S(rvec);
682  double* vz = vx + s.nx_;
683  double* v_it = m->tmp1;
684  for (int d = 0; d <= s.nfwd_; ++d) {
685  casadi_copy(vx + d * s.nx1_, s.nx1_, v_it);
686  v_it += s.nx1_;
687  casadi_copy(vz + d * s.nz1_, s.nz1_, v_it);
688  v_it += s.nz1_;
689  }
690 
691  // Solve for undifferentiated right-hand-side, save to output
692  if (s.linsolF_.solve(m->jacF, m->tmp1, 1, false, m->mem_linsolF))
693  return 1;
694  vx = NV_DATA_S(zvec); // possibly different from rvec
695  vz = vx + s.nx_;
696  casadi_copy(m->tmp1, s.nx1_, vx);
697  casadi_copy(m->tmp1 + s.nx1_, s.nz1_, vz);
698 
699  // Sensitivity equations
700  if (s.nfwd_ > 0) {
701  // Second order correction
702  if (s.second_order_correction_) {
703  // The outputs will double as seeds for jtimesF
704  casadi_clear(vx + s.nx1_, s.nx_ - s.nx1_);
705  casadi_clear(vz + s.nz1_, s.nz_ - s.nz1_);
706  if (s.calc_jtimesF(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
707  vx, vz, m->tmp2, m->tmp2 + s.nx_)) return 1;
708 
709  // Subtract m->tmp2 (reordered) from m->tmp1
710  v_it = m->tmp1 + s.nx1_ + s.nz1_;
711  for (int d = 1; d <= s.nfwd_; ++d) {
712  casadi_axpy(s.nx1_, -1., m->tmp2 + d*s.nx1_, v_it);
713  v_it += s.nx1_;
714  casadi_axpy(s.nz1_, -1., m->tmp2 + s.nx_ + d*s.nz1_, v_it);
715  v_it += s.nz1_;
716  }
717  }
718 
719  // Solve for sensitivity right-hand-sides
720  if (s.linsolF_.solve(m->jacF, m->tmp1 + s.nx1_ + s.nz1_, s.nfwd_,
721  false, m->mem_linsolF)) return 1;
722 
723  // Save to output, reordered
724  v_it = m->tmp1 + s.nx1_ + s.nz1_;
725  for (int d = 1; d <= s.nfwd_; ++d) {
726  casadi_copy(v_it, s.nx1_, vx + d * s.nx1_);
727  v_it += s.nx1_;
728  casadi_copy(v_it, s.nz1_, vz + d * s.nz1_);
729  v_it += s.nz1_;
730  }
731  }
732 
733  return 0;
734  } catch(std::exception& e) { // non-recoverable error
735  uerr() << "psolve failed: " << e.what() << std::endl;
736  return -1;
737  }
738 }
739 
740 int IdasInterface::solve_transposed(IdasMemory* m, double t, const double* xz, const double* rxz,
741  const double* rhs, double* sol) const {
742  // Get right-hand sides in m->tmp1, ordered by sensitivity directions
743  double* v_it = m->tmp1;
744  for (int d = 0; d <= nfwd_; ++d) {
745  for (int a = 0; a < nadj_; ++a) {
746  casadi_copy(rhs + (d * nadj_ + a) * nrx1_, nrx1_, v_it);
747  v_it += nrx1_;
748  casadi_copy(rhs + nrx_ + (d * nadj_ + a) * nrz1_, nrz1_, v_it);
749  v_it += nrz1_;
750  }
751  }
752 
753  // Solve for undifferentiated right-hand-side, save to output
754  if (linsolF_.solve(m->jacF, m->tmp1, nadj_, true, m->mem_linsolF)) return 1;
755  for (int a = 0; a < nadj_; ++a) {
756  casadi_copy(m->tmp1 + a * (nrx1_ + nrz1_), nrx1_, sol + a * nrx1_);
757  casadi_copy(m->tmp1 + a * (nrx1_ + nrz1_) + nrx1_, nrz1_, sol + nrx_ + a * nrz1_);
758  }
759 
760  // Sensitivity equations
761  if (nfwd_ > 0) {
762  // Second order correction
763  if (second_order_correction_ && rxz) {
764  // The outputs will double as seeds for calc_daeB
765  casadi_clear(sol + nrx1_ * nadj_, nrx_ - nrx1_ * nadj_);
766  casadi_clear(sol + nrx_ + nrz1_ * nadj_, nrz_ - nrz1_ * nadj_);
767 
768  // Get second-order-correction, save to m->tmp2
769  if (calc_daeB(m, t, xz, xz + nx_, sol, sol + nrx_, nullptr,
770  m->tmp2, m->tmp2 + nrx_)) return 1;
771 
772  // Subtract m->tmp2 (reordered) from m->tmp1
773  v_it = m->tmp1 + (nrx1_ + nrz1_) * nadj_;
774  for (int d = 1; d <= nfwd_; ++d) {
775  for (int a = 0; a < nadj_; ++a) {
776  casadi_axpy(nrx1_, -1., m->tmp2 + nrx1_ * (d * nadj_ + a), v_it);
777  v_it += nrx1_;
778  casadi_axpy(nrz1_, -1., m->tmp2 + nrx_ + nrz1_ * (d * nadj_ + a), v_it);
779  v_it += nrz1_;
780  }
781  }
782  }
783 
784  // Solve for sensitivity right-hand-sides
785  if (linsolF_.solve(m->jacF, m->tmp1 + nrx1_ * nadj_ + nrz1_ * nadj_,
786  nadj_ * nfwd_, true, m->mem_linsolF)) return 1;
787 
788  // Save to output, reordered
789  v_it = m->tmp1 + (nrx1_ + nrz1_) * nadj_;
790  for (int d = 1; d <= nfwd_; ++d) {
791  for (int a = 0; a < nadj_; ++a) {
792  casadi_copy(v_it, nrx1_, sol + nrx1_ * (d * nadj_ + a));
793  v_it += nrx1_;
794  casadi_copy(v_it, nrz1_, sol + nrx_ + nrz1_ * (d * nadj_ + a));
795  v_it += nrz1_;
796  }
797  }
798  }
799 
800  return 0;
801 }
802 
803 int IdasInterface::psolveB(double t, N_Vector xz, N_Vector xzdot, N_Vector xzB,
804  N_Vector xzdotB, N_Vector resvalB, N_Vector rvecB,
805  N_Vector zvecB, double cjB, double deltaB,
806  void *user_data, N_Vector tmpB) {
807  try {
808  auto m = to_mem(user_data);
809  auto& s = m->self;
810  return s.solve_transposed(m, t, NV_DATA_S(xz), NV_DATA_S(xzB),
811  NV_DATA_S(rvecB), NV_DATA_S(zvecB));
812 
813  } catch(std::exception& e) { // non-recoverable error
814  uerr() << "psolveB failed: " << e.what() << std::endl;
815  return -1;
816  }
817 }
818 
819 template<typename T1>
820 void casadi_copy_block(const T1* x, const casadi_int* sp_x, T1* y, const casadi_int* sp_y,
821  casadi_int r_begin, casadi_int c_begin, T1* w) {
822  // x and y should be distinct
823  casadi_int nrow_x, ncol_x, ncol_y, i_x, i_y, j, el, r_end;
824  const casadi_int *colind_x, *row_x, *colind_y, *row_y;
825  nrow_x = sp_x[0];
826  ncol_x = sp_x[1];
827  colind_x = sp_x+2; row_x = sp_x + 2 + ncol_x+1;
828  ncol_y = sp_y[1];
829  colind_y = sp_y+2; row_y = sp_y + 2 + ncol_y+1;
830  // End of the rows to be copied
831  r_end = r_begin + nrow_x;
832  // w will correspond to a column of x, initialize to zero
833  casadi_clear(w, nrow_x);
834  // Loop over columns in x
835  for (i_x = 0; i_x < ncol_x; ++i_x) {
836  // Corresponding row in y
837  i_y = i_x + c_begin;
838  // Copy x entries to w
839  for (el=colind_x[i_x]; el<colind_x[i_x + 1]; ++el) w[row_x[el]] = x[el];
840  // Copy entries to y, if in interval
841  for (el=colind_y[i_y]; el<colind_y[i_y + 1]; ++el) {
842  j = row_y[el];
843  if (j >= r_begin && j < r_end) y[el] = w[j - r_begin];
844  }
845  // Restore w
846  for (el=colind_x[i_x]; el<colind_x[i_x + 1]; ++el) w[row_x[el]] = 0;
847  }
848 }
849 
850 int IdasInterface::psetupF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr,
851  double cj, void* user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3) {
852  try {
853  auto m = to_mem(user_data);
854  auto& s = m->self;
855 
856  // Sparsity patterns
857  const Function& jacF = s.get_function("jacF");
858  const Sparsity& sp_jac_ode_x = jacF.sparsity_out(JACF_ODE_X);
859  const Sparsity& sp_jac_alg_x = jacF.sparsity_out(JACF_ALG_X);
860  const Sparsity& sp_jac_ode_z = jacF.sparsity_out(JACF_ODE_Z);
861  const Sparsity& sp_jac_alg_z = jacF.sparsity_out(JACF_ALG_Z);
862  const Sparsity& sp_jacF = s.linsolF_.sparsity();
863 
864  // Calculate Jacobian blocks
865  if (s.calc_jacF(m, t, NV_DATA_S(xz), NV_DATA_S(xz) + s.nx_,
866  m->jac_ode_x, m->jac_alg_x, m->jac_ode_z, m->jac_alg_z)) return 1;
867 
868  // Copy to jacF structure
869  casadi_int nx_jac = sp_jac_ode_x.size1(); // excludes sensitivity equations
870  casadi_copy_block(m->jac_ode_x, sp_jac_ode_x, m->jacF, sp_jacF, 0, 0, m->w);
871  casadi_copy_block(m->jac_alg_x, sp_jac_alg_x, m->jacF, sp_jacF, nx_jac, 0, m->w);
872  casadi_copy_block(m->jac_ode_z, sp_jac_ode_z, m->jacF, sp_jacF, 0, nx_jac, m->w);
873  casadi_copy_block(m->jac_alg_z, sp_jac_alg_z, m->jacF, sp_jacF, nx_jac, nx_jac, m->w);
874 
875  // Shift diagonal corresponding to jac_ode_x
876  const casadi_int *colind = sp_jacF.colind(), *row = sp_jacF.row();
877  for (casadi_int c = 0; c < nx_jac; ++c) {
878  for (casadi_int k = colind[c]; k < colind[c + 1]; ++k) {
879  if (row[k] == c) m->jacF[k] -= cj;
880  }
881  }
882 
883  // Factorize the linear system
884  if (s.linsolF_.nfact(m->jacF, m->mem_linsolF)) return 1;
885  m->cj_last = cj;
886 
887  return 0;
888  } catch(std::exception& e) { // non-recoverable error
889  uerr() << "psetup failed: " << e.what() << std::endl;
890  return -1;
891  }
892 }
893 
894 int IdasInterface::psetupB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz, N_Vector rxzdot,
895  N_Vector rresval, double cj, void *user_data, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B) {
896  try {
897  // We use the same linear solver for the forward problem as for the backward problem
898  return psetupF(t, xz, nullptr, nullptr, -cj, user_data, tmp1B, tmp2B, tmp3B);
899 
900  } catch(std::exception& e) { // non-recoverable error
901  uerr() << "psetupB failed: " << e.what() << std::endl;
902  return -1;
903  }
904 }
905 
906 int IdasInterface::lsetupF(IDAMem IDA_mem, N_Vector xz, N_Vector xzdot, N_Vector resp,
907  N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) {
908  // Current time
909  double t = IDA_mem->ida_tn;
910 
911  // Multiple of df_dydot to be added to the matrix
912  double cj = IDA_mem->ida_cj;
913 
914  // Call the preconditioner setup function (which sets up the linear solver)
915  return psetupF(t, xz, xzdot, nullptr, cj, IDA_mem->ida_lmem,
916  vtemp1, vtemp1, vtemp3);
917 }
918 
919 int IdasInterface::lsetupB(IDAMem IDA_mem, N_Vector xzB, N_Vector xzdotB, N_Vector respB,
920  N_Vector vtemp1B, N_Vector vtemp2B, N_Vector vtemp3B) {
921  try {
922  auto m = to_mem(IDA_mem->ida_lmem);
923  //auto& s = m->self;
924  IDAadjMem IDAADJ_mem;
925  //IDABMem IDAB_mem;
926 
927  // Current time
928  double t = IDA_mem->ida_tn; // TODO(Joel): is this correct?
929  // Multiple of df_dydot to be added to the matrix
930  double cj = IDA_mem->ida_cj;
931 
932  IDA_mem = static_cast<IDAMem>(IDA_mem->ida_user_data);
933 
934  IDAADJ_mem = IDA_mem->ida_adj_mem;
935  //IDAB_mem = IDAADJ_mem->ia_bckpbCrt;
936 
937  // Get FORWARD solution from interpolation.
938  if (IDAADJ_mem->ia_noInterp==FALSE) {
939  int flag = IDAADJ_mem->ia_getY(IDA_mem, t, IDAADJ_mem->ia_yyTmp, IDAADJ_mem->ia_ypTmp,
940  nullptr, nullptr);
941  if (flag != IDA_SUCCESS) casadi_error("Could not interpolate forward states");
942  }
943  // Call the preconditioner setup function (which sets up the linear solver)
944  return psetupB(t, IDAADJ_mem->ia_yyTmp, IDAADJ_mem->ia_ypTmp,
945  xzB, xzdotB, nullptr, cj, static_cast<void*>(m), vtemp1B, vtemp1B, vtemp3B);
946 
947  } catch(std::exception& e) { // non-recoverable error
948  uerr() << "lsetupB failed: " << e.what() << std::endl;
949  return -1;
950  }
951 }
952 
953 int IdasInterface::lsolveF(IDAMem IDA_mem, N_Vector b, N_Vector weight, N_Vector xz,
954  N_Vector xzdot, N_Vector rr) {
955  try {
956  auto m = to_mem(IDA_mem->ida_lmem);
957  auto& s = m->self;
958 
959  // Current time
960  double t = IDA_mem->ida_tn;
961 
962  // Multiple of df_dydot to be added to the matrix
963  double cj = IDA_mem->ida_cj;
964 
965  // Accuracy
966  double delta = 0.0;
967 
968  // Call the preconditioner solve function (which solves the linear system)
969  int flag = psolveF(t, xz, xzdot, rr, b, b, cj,
970  delta, static_cast<void*>(m), nullptr);
971  if (flag) return flag;
972 
973  // Scale the correction to account for change in cj
974  if (s.cj_scaling_) {
975  double cjratio = IDA_mem->ida_cjratio;
976  if (cjratio != 1.0) N_VScale(2.0/(1.0 + cjratio), b, b);
977  }
978 
979  return 0;
980  } catch(std::exception& e) { // non-recoverable error
981  uerr() << "lsolve failed: " << e.what() << std::endl;
982  return -1;
983  }
984 }
985 
986 int IdasInterface::lsolveB(IDAMem IDA_mem, N_Vector b, N_Vector weight, N_Vector xzB,
987  N_Vector xzdotB, N_Vector rrB) {
988  try {
989  auto m = to_mem(IDA_mem->ida_lmem);
990  auto& s = m->self;
991  IDAadjMem IDAADJ_mem;
992  //IDABMem IDAB_mem;
993  int flag;
994 
995  // Current time
996  double t = IDA_mem->ida_tn; // TODO(Joel): is this correct?
997  // Multiple of df_dydot to be added to the matrix
998  double cj = IDA_mem->ida_cj;
999  double cjratio = IDA_mem->ida_cjratio;
1000 
1001  IDA_mem = (IDAMem) IDA_mem->ida_user_data;
1002  IDAADJ_mem = IDA_mem->ida_adj_mem;
1003  //IDAB_mem = IDAADJ_mem->ia_bckpbCrt;
1004 
1005  // Get FORWARD solution from interpolation.
1006  if (IDAADJ_mem->ia_noInterp==FALSE) {
1007  flag = IDAADJ_mem->ia_getY(IDA_mem, t, IDAADJ_mem->ia_yyTmp, IDAADJ_mem->ia_ypTmp,
1008  nullptr, nullptr);
1009  if (flag != IDA_SUCCESS) casadi_error("Could not interpolate forward states");
1010  }
1011 
1012  // Accuracy
1013  double delta = 0.0;
1014 
1015  // Call the preconditioner solve function (which solves the linear system)
1016  flag = psolveB(t, IDAADJ_mem->ia_yyTmp, IDAADJ_mem->ia_ypTmp, xzB, xzdotB,
1017  rrB, b, b, cj, delta, static_cast<void*>(m), nullptr);
1018  if (flag) return flag;
1019 
1020  // Scale the correction to account for change in cj
1021  if (s.cj_scaling_) {
1022  if (cjratio != 1.0) N_VScale(2.0/(1.0 + cjratio), b, b);
1023  }
1024  return 0;
1025  } catch(std::exception& e) { // non-recoverable error
1026  uerr() << "lsolveB failed: " << e.what() << std::endl;
1027  return -1;
1028  }
1029 }
1030 
1032  this->mem = nullptr;
1033  this->v_xzdot = nullptr;
1034  this->v_adj_xzdot = nullptr;
1035  this->cj_last = nan;
1036 
1037  // Reset checkpoints counter
1038  this->ncheck = 0;
1039 }
1040 
1042  if (this->mem) IDAFree(&this->mem);
1043  if (this->v_xzdot) N_VDestroy_Serial(this->v_xzdot);
1044  if (this->v_adj_xzdot) N_VDestroy_Serial(this->v_adj_xzdot);
1045  if (this->mem_linsolF >= 0) self.linsolF_.release(this->mem_linsolF);
1046 }
1047 
1049  int version = s.version("IdasInterface", 1, 2);
1050  s.unpack("IdasInterface::cj_scaling", cj_scaling_);
1051  s.unpack("IdasInterface::calc_ic", calc_ic_);
1052  s.unpack("IdasInterface::calc_icB", calc_icB_);
1053  s.unpack("IdasInterface::suppress_algebraic", suppress_algebraic_);
1054  s.unpack("IdasInterface::abstolv", abstolv_);
1055  s.unpack("IdasInterface::first_time", first_time_);
1056  s.unpack("IdasInterface::init_xdot", init_xdot_);
1057 
1058  if (version>=2) {
1059  s.unpack("IdasInterface::max_step_size", max_step_size_);
1060  s.unpack("IdasInterface::y_c", y_c_);
1061  } else {
1062  max_step_size_ = 0;
1063  }
1064 }
1065 
1068  s.version("IdasInterface", 2);
1069  s.pack("IdasInterface::cj_scaling", cj_scaling_);
1070  s.pack("IdasInterface::calc_ic", calc_ic_);
1071  s.pack("IdasInterface::calc_icB", calc_icB_);
1072  s.pack("IdasInterface::suppress_algebraic", suppress_algebraic_);
1073  s.pack("IdasInterface::abstolv", abstolv_);
1074  s.pack("IdasInterface::first_time", first_time_);
1075  s.pack("IdasInterface::init_xdot", init_xdot_);
1076  s.pack("IdasInterface::max_step_size", max_step_size_);
1077  s.pack("IdasInterface::y_c", y_c_);
1078 }
1079 
1080 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
Function object.
Definition: function.hpp:126
std::vector< std::string > get_function() const
Get the names of the functions this function directly owns.
Definition: function.cpp:2041
const Sparsity & sparsity_out(casadi_int ind) const
Get the sparsity pattern of an output, by index or name.
Definition: function.cpp:1183
'idas' plugin for Integrator
static int rhsQF(double t, N_Vector xz, N_Vector xzdot, N_Vector qdot, void *user_data)
static int jtimesB(double t, N_Vector xz, N_Vector xzdot, N_Vector xzB, N_Vector xzdotB, N_Vector resvalB, N_Vector vB, N_Vector JvB, double cjB, void *user_data, N_Vector tmp1B, N_Vector tmp2B)
int init_mem(void *mem) const override
Initalize memory block.
static int resF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr, void *user_data)
void retreat(IntegratorMemory *mem, const double *u, double *adj_x, double *adj_p, double *adj_u) const override
Retreat solution in time.
void resetB(IntegratorMemory *mem) const override
Reset the backward problem and take time to tf.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
int solve_transposed(IdasMemory *m, double t, const double *xz, const double *rxz, const double *rhs, double *sol) const
Solve transposed linear system.
void impulseB(IntegratorMemory *mem, const double *adj_x, const double *adj_z, const double *adj_q) const override
Introduce an impulse into the backwards integration at the current time.
static void idas_error(const char *module, int flag)
std::vector< double > init_xdot_
static int psetupB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz, N_Vector rxzdot, N_Vector resvalB, double cjB, void *user_dataB, N_Vector tmp1B, N_Vector tmp2B, N_Vector tmp3B)
static int lsolveB(IDAMem IDA_mem, N_Vector b, N_Vector weight, N_Vector ycur, N_Vector xzdotcur, N_Vector rescur)
static int lsetupF(IDAMem IDA_mem, N_Vector xz, N_Vector xzdot, N_Vector resp, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3)
static const std::string meta_doc
A documentation string.
static int lsolveF(IDAMem IDA_mem, N_Vector b, N_Vector weight, N_Vector ycur, N_Vector xzdotcur, N_Vector rescur)
static int resB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz, N_Vector rxzdot, N_Vector rr, void *user_data)
IdasInterface(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Constructor.
static int rhsQB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz, N_Vector rxzdot, N_Vector ruqdot, void *user_data)
std::vector< double > abstolv_
static const Options options_
Options.
void reset(IntegratorMemory *mem, bool first_call) const override
Reset the forward solver at the start or after an event.
static int psolveF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr, N_Vector rvec, N_Vector zvec, double cj, double delta, void *user_data, N_Vector tmp)
static Integrator * creator(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Create a new integrator.
void init(const Dict &opts) override
Initialize.
static int psetupF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr, double cj, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
static void ehfun(int error_code, const char *module, const char *function, char *msg, void *eh_data)
~IdasInterface() override
Destructor.
static int lsetupB(IDAMem IDA_mem, N_Vector xz, N_Vector xzdot, N_Vector resp, N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3)
static IdasMemory * to_mem(void *mem)
Cast to memory object.
static int psolveB(double t, N_Vector xz, N_Vector xzdot, N_Vector rxz, N_Vector rxzdot, N_Vector resvalB, N_Vector rvecB, N_Vector zvecB, double cjB, double deltaB, void *user_dataB, N_Vector tmpB)
void z_impulseB(IdasMemory *m, const double *adj_z) const
Propagate impulse from adj_z to adj_x.
std::vector< casadi_int > y_c_
int advance_noevent(IntegratorMemory *mem) const override
Advance solution in time.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
static int jtimesF(double t, N_Vector xz, N_Vector xzdot, N_Vector rr, N_Vector v, N_Vector Jv, double cj, void *user_data, N_Vector tmp1, N_Vector tmp2)
casadi_int nfwd_
Number of sensitivities.
casadi_int nrx_
Number of states for the backward integration.
casadi_int nt() const
Number of output times.
Dict opts_
Copy of the options.
static bool all_zero(const double *v, casadi_int n)
Helper function: Vector has only zeros?
std::vector< double > tout_
Output time grid.
double t0_
Initial time.
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.
DM solve(const DM &A, const DM &B, bool tr=false) const
Solve the linear system A*X = B (or A'*X = B with tr true)
Definition: linsol.cpp:73
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
General sparsity class.
Definition: sparsity.hpp:200
casadi_int size1() const
Number of rows.
Definition: sparsity.cpp:124
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:166
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:170
Linsol linsolF_
Linear solver.
enum casadi::SundialsInterface::InterpType interp_
void impulseB(IntegratorMemory *mem, const double *adj_x, const double *adj_z, const double *adj_q) const override
Introduce an impulse into the backwards integration at the current time.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
enum casadi::SundialsInterface::NewtonScheme newton_scheme_
void reset(IntegratorMemory *mem, bool first_call) const override
Reset the forward solver at the start or after an event.
int init_mem(void *mem) const override
Initalize memory block.
void add_offsets(SundialsMemory *m) const
Add stats offsets to stats.
int calc_quadB(SundialsMemory *m, double t, const double *x, const double *z, const double *adj_ode, const double *adj_alg, double *adj_p, double *adj_u) const
void init(const Dict &opts) override
Initialize.
void resetB(IntegratorMemory *mem) const override
Reset the backward problem and take time to tf.
int calc_daeB(SundialsMemory *m, double t, const double *x, const double *z, const double *adj_ode, const double *adj_alg, const double *adj_quad, double *adj_x, double *adj_z) const
static const Options options_
Options.
The casadi namespace.
Definition: archiver.cpp:28
int CASADI_INTEGRATOR_IDAS_EXPORT casadi_register_integrator_idas(Integrator::Plugin *plugin)
std::ostream & uerr()
void CASADI_INTEGRATOR_IDAS_EXPORT casadi_load_integrator_idas()
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
@ OT_INTVECTOR
@ OT_DOUBLEVECTOR
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void casadi_copy_block(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, casadi_int r_begin, casadi_int c_begin, T1 *w)
const double nan
Not a number.
Definition: calculus.hpp:53
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
void casadi_axpy(casadi_int n, T1 alpha, const T1 *x, T1 *y)
AXPY: y <- a*x + y.
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.
void * mem
Idas memory block.
~IdasMemory()
Destructor.
double cj_last
cj used in the last factorization
IdasMemory(const IdasInterface &s)
Constructor.
Options metadata for a class.
Definition: options.hpp:40
int mem_linsolF
Linear solver memory objects.
int ncheck
number of checkpoints stored so far