kinsol_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 "kinsol_interface.hpp"
27 
28 namespace casadi {
29 
30  extern "C"
31  int CASADI_ROOTFINDER_KINSOL_EXPORT
32  casadi_register_rootfinder_kinsol(Rootfinder::Plugin* plugin) {
33  plugin->creator = KinsolInterface::creator;
34  plugin->name = "kinsol";
35  plugin->doc = KinsolInterface::meta_doc.c_str();
36  plugin->version = CASADI_VERSION;
37  plugin->options = &KinsolInterface::options_;
38  return 0;
39  }
40 
41  extern "C"
42  void CASADI_ROOTFINDER_KINSOL_EXPORT casadi_load_rootfinder_kinsol() {
44  }
45 
65  KinsolInterface::KinsolInterface(const std::string& name, const Function& f)
66  : Rootfinder(name, f) {
67 
68  u_scale_ = nullptr;
69  f_scale_ = nullptr;
70 
71  // Default options
72  exact_jac_ = true;
74  max_iter_ = 0;
75  print_level_ = 0;
76  maxl_ = 0;
77  upper_bandwidth_ = -1;
78  lower_bandwidth_ = -1;
79  use_preconditioner_ = false;
80  abstol_ = 1e-6;
81  }
82 
84  if (u_scale_) N_VDestroy_Serial(u_scale_);
85  if (f_scale_) N_VDestroy_Serial(f_scale_);
86  clear_mem();
87  }
88 
91  {{"max_iter",
92  {OT_INT,
93  "Maximum number of Newton iterations. Putting 0 sets the default value of KinSol."}},
94  {"print_level",
95  {OT_INT,
96  "Verbosity level"}},
97  {"abstol",
98  {OT_DOUBLE,
99  "Stopping criterion tolerance"}},
100  {"linear_solver_type",
101  {OT_STRING,
102  "dense|banded|iterative|user_defined"}},
103  {"upper_bandwidth",
104  {OT_INT,
105  "Upper bandwidth for banded linear solvers"}},
106  {"lower_bandwidth",
107  {OT_INT,
108  "Lower bandwidth for banded linear solvers"}},
109  {"max_krylov",
110  {OT_INT,
111  "Maximum Krylov space dimension"}},
112  {"exact_jacobian",
113  {OT_BOOL,
114  "Use exact Jacobian information"}},
115  {"iterative_solver",
116  {OT_STRING,
117  "gmres|bcgstab|tfqmr"}},
118  {"f_scale",
120  "Equation scaling factors"}},
121  {"u_scale",
123  "Variable scaling factors"}},
124  {"pretype",
125  {OT_STRING,
126  "Type of preconditioner"}},
127  {"use_preconditioner",
128  {OT_BOOL,
129  "Precondition an iterative solver"}},
130  {"strategy",
131  {OT_STRING,
132  "Globalization strategy"}},
133  {"disable_internal_warnings",
134  {OT_BOOL,
135  "Disable KINSOL internal warning messages"}}
136  }
137  };
138 
139  void KinsolInterface::init(const Dict& opts) {
140  // Initialize the base classes
141  Rootfinder::init(opts);
142 
143  // Default (temporary) options
144  std::string strategy = "none";
145  std::vector<double> u_scale;
146  std::vector<double> f_scale;
147  std::string linear_solver_type = "dense";
148  std::string iterative_solver = "gmres";
149 
150  // Read options
151  for (auto&& op : opts) {
152  if (op.first=="strategy") {
153  strategy = op.second.to_string();
154  } else if (op.first=="exact_jacobian") {
155  exact_jac_ = op.second;
156  } else if (op.first=="u_scale") {
157  u_scale = op.second;
158  } else if (op.first=="f_scale") {
159  f_scale = op.second;
160  } else if (op.first=="disable_internal_warnings") {
161  disable_internal_warnings_ = op.second;
162  } else if (op.first=="max_iter") {
163  max_iter_ = op.second;
164  } else if (op.first=="print_level") {
165  print_level_ = op.second;
166  } else if (op.first=="linear_solver_type") {
167  linear_solver_type = op.second.to_string();
168  } else if (op.first=="max_krylov") {
169  maxl_ = op.second;
170  } else if (op.first=="upper_bandwidth") {
171  upper_bandwidth_ = op.second;
172  } else if (op.first=="lower_bandwidth") {
173  lower_bandwidth_ = op.second;
174  } else if (op.first=="iterative_solver") {
175  iterative_solver = op.second.to_string();
176  } else if (op.first=="use_preconditioner") {
177  use_preconditioner_ = op.second;
178  } else if (op.first=="abstol") {
179  abstol_ = op.second;
180  }
181  }
182 
183  // Get globalization strategy
184  if (strategy=="linesearch") {
185  strategy_ = KIN_LINESEARCH;
186  } else {
187  casadi_assert_dev(strategy=="none");
188  strategy_ = KIN_NONE;
189  }
190 
191  // Allocate N_Vectors
192  if (u_scale_) N_VDestroy_Serial(u_scale_);
193  if (f_scale_) N_VDestroy_Serial(f_scale_);
194  u_scale_ = N_VNew_Serial(n_);
195  f_scale_ = N_VNew_Serial(n_);
196 
197  // Set scaling factors on variables
198  if (!u_scale.empty()) {
199  casadi_assert_dev(u_scale.size()==NV_LENGTH_S(u_scale_));
200  std::copy(u_scale.begin(), u_scale.end(), NV_DATA_S(u_scale_));
201  } else {
202  N_VConst(1.0, u_scale_);
203  }
204 
205  // Set scaling factors on equations
206  if (!f_scale.empty()) {
207  casadi_assert_dev(f_scale.size()==NV_LENGTH_S(f_scale_));
208  std::copy(f_scale.begin(), f_scale.end(), NV_DATA_S(f_scale_));
209  } else {
210  N_VConst(1.0, f_scale_);
211  }
212 
213  // Type of linear solver
214  if (linear_solver_type=="dense") {
216  if (exact_jac_) {
217  // For storing Jacobian nonzeros
218  alloc_w(sp_jac_.nnz(), true);
219  }
220  } else if (linear_solver_type=="banded") {
222  casadi_assert_dev(upper_bandwidth_>=0);
223  casadi_assert_dev(lower_bandwidth_>=0);
224  if (exact_jac_) {
225  // For storing Jacobian nonzeros
226  alloc_w(sp_jac_.nnz(), true);
227  }
228  } else if (linear_solver_type=="iterative") {
230  if (iterative_solver=="gmres") {
232  } else if (iterative_solver=="bcgstab") {
234  } else if (iterative_solver=="tfqmr") {
236  } else {
237  casadi_error("KINSOL: Unknown sparse solver");
238  }
239  if (exact_jac_) {
240  get_jtimes();
241  }
242  } else if (linear_solver_type=="user_defined") {
244 
245  // Form the Jacobian-times-vector function
246  get_jtimes();
247 
248  // Allocate space for Jacobian
249  alloc_w(sp_jac_.nnz(), true);
250  } else {
251  casadi_error("Unknown linear solver");
252  }
253  }
254 
255  void KinsolInterface::set_work(void* mem, const double**& arg, double**& res,
256  casadi_int*& iw, double*& w) const {
257  Rootfinder::set_work(mem, arg, res, iw, w);
258  auto m = static_cast<KinsolMemory*>(mem);
259  m->jac = w; w += sp_jac_.nnz();
260  }
261 
263  std::vector<std::string> jtimes_in = oracle_.name_in();
264  jtimes_in.push_back("fwd:" + oracle_.name_in(iin_));
265  std::vector<std::string> jtimes_out = {"fwd:" + oracle_.name_out(iout_)};
266  jtimes_ = oracle_.factory("jtimes", jtimes_in, jtimes_out);
267  alloc(jtimes_);
268  }
269 
270  int KinsolInterface::solve(void* mem) const {
271  auto m = static_cast<KinsolMemory*>(mem);
272 
273  // Get the initial guess
274  casadi_copy(m->iarg[iin_], nnz_in(iin_), NV_DATA_S(m->u));
275 
276  // Solve the nonlinear system of equations
277  int flag = KINSol(m->mem, m->u, strategy_, u_scale_, f_scale_);
278  m->success = flag>= KIN_SUCCESS;
279  if (flag<KIN_SUCCESS) kinsol_error("KINSol", flag, error_on_fail_);
280  if (flag==KIN_MAXITER_REACHED) m->unified_return_status = SOLVER_RET_LIMITED;
281 
282  // Warn if not successful return
283  if (verbose_) {
284  if (flag!=KIN_SUCCESS) kinsol_error("KINSol", flag, false);
285  }
286 
287  // Get the solution
288  casadi_copy(NV_DATA_S(m->u), nnz_out(iout_), m->ires[iout_]);
289 
290  // Evaluate auxiliary outputs
291  if (n_out_>0) {
292  // Evaluate f_
293  std::copy_n(m->iarg, n_in_, m->arg);
294  m->arg[iin_] = NV_DATA_S(m->u);
295  std::copy_n(m->ires, n_out_, m->res);
296  m->res[iout_] = nullptr;
297  oracle_(m->arg, m->res, m->iw, m->w, 0);
298  }
299  return 0;
300  }
301 
302  void KinsolInterface::func(KinsolMemory& m, N_Vector u, N_Vector fval) const {
303  // Get nonzeros
304  const double *u_data = NV_DATA_S(u);
305  double *f_data = NV_DATA_S(fval);
306 
307  // Evaluate f_
308  std::copy_n(m.iarg, n_in_, m.arg);
309  m.arg[iin_] = u_data;
310  std::fill_n(m.res, n_out_, nullptr);
311  m.res[iout_] = f_data;
312  oracle_(m.arg, m.res, m.iw, m.w, 0);
313 
314  // Make sure that all entries of the linear system are valid
315  for (int k=0; k<n_; ++k) {
316  try {
317  casadi_assert(!isnan(f_data[k]),
318  "Nonzero " + str(k) + " is not-a-number");
319  casadi_assert(!isinf(f_data[k]),
320  "Nonzero " + str(k) + " is infinite");
321  } catch(std::exception& ex) {
322  std::stringstream ss;
323  ss << ex.what() << std::endl;
324  if (verbose_) {
325  uout() << "u = " << std::vector<double>(u_data, u_data + n_) << std::endl;
326  uout() << "f = " << std::vector<double>(f_data, f_data + n_) << std::endl;
327  }
328  throw CasadiException(ss.str());
329  }
330  }
331  }
332 
333  int KinsolInterface::func_wrapper(N_Vector u, N_Vector fval, void *user_data) {
334  try {
335  casadi_assert_dev(user_data);
336  auto this_ = static_cast<KinsolMemory*>(user_data);
337  this_->self.func(*this_, u, fval);
338  return 0;
339  } catch(std::exception& e) {
340  uerr() << "func failed: " << e.what() << std::endl;
341  return 1;
342  }
343  }
344 
345  int KinsolInterface::djac_wrapper(long N, N_Vector u, N_Vector fu, DlsMat J,
346  void *user_data, N_Vector tmp1, N_Vector tmp2) {
347  try {
348  casadi_assert_dev(user_data);
349  auto this_ = static_cast<KinsolMemory*>(user_data);
350  this_->self.djac(*this_, N, u, fu, J, tmp1, tmp2);
351  return 0;
352  } catch(std::exception& e) {
353  uerr() << "djac failed: " << e.what() << std::endl;;
354  return 1;
355  }
356  }
357 
358  void KinsolInterface::djac(KinsolMemory& m, long N, N_Vector u, N_Vector fu, DlsMat J,
359  N_Vector tmp1, N_Vector tmp2) const {
360  // Evaluate jac_
361  std::copy_n(m.iarg, n_in_, m.arg);
362  m.arg[iin_] = NV_DATA_S(u);
363  std::fill_n(m.res, n_out_+1, nullptr);
364  m.res[0] = m.jac;
365  calc_function(&m, "jac_g_x");
366 
367  // Get sparsity and non-zero elements
368  const casadi_int* colind = sp_jac_.colind();
369  casadi_int ncol = sp_jac_.size2();
370  const casadi_int* row = sp_jac_.row();
371 
372  // Loop over columns
373  for (casadi_int cc=0; cc<ncol; ++cc) {
374  // Loop over non-zero entries
375  for (casadi_int el=colind[cc]; el<colind[cc+1]; ++el) {
376  // Get row
377  casadi_int rr = row[el];
378 
379  // Set the element
380  DENSE_ELEM(J, rr, cc) = m.jac[el];
381  }
382  }
383  }
384 
385  int KinsolInterface::bjac_wrapper(long N, long mupper, long mlower, N_Vector u,
386  N_Vector fu, DlsMat J, void *user_data,
387  N_Vector tmp1, N_Vector tmp2) {
388  try {
389  casadi_assert_dev(user_data);
390  auto this_ = static_cast<KinsolMemory*>(user_data);
391  this_->self.bjac(*this_, N, mupper, mlower, u, fu, J, tmp1, tmp2);
392  return 0;
393  } catch(std::exception& e) {
394  uerr() << "bjac failed: " << e.what() << std::endl;;
395  return 1;
396  }
397  }
398 
399  void KinsolInterface::bjac(KinsolMemory& m, long N, long mupper, long mlower, N_Vector u,
400  N_Vector fu, DlsMat J, N_Vector tmp1, N_Vector tmp2) const {
401  // Evaluate jac_
402  std::copy_n(m.iarg, n_in_, m.arg);
403  m.arg[iin_] = NV_DATA_S(u);
404  std::fill_n(m.res, n_out_+1, nullptr);
405  m.res[0] = m.jac;
406  calc_function(&m, "jac_g_x");
407 
408  // Get sparsity and non-zero elements
409  const casadi_int* colind = sp_jac_.colind();
410  casadi_int ncol = sp_jac_.size2();
411  const casadi_int* row = sp_jac_.row();
412 
413  // Loop over cols
414  for (casadi_int cc=0; cc<ncol; ++cc) {
415  // Loop over non-zero entries
416  for (casadi_int el=colind[cc]; el<colind[cc+1]; ++el) {
417  // Get row
418  casadi_int rr = row[el];
419 
420  // Set the element
421  if (rr-cc>=-mupper && rr-cc<=mlower) {
422  BAND_ELEM(J, rr, cc) = m.jac[el];
423  }
424  }
425  }
426  }
427 
428  int KinsolInterface::jtimes_wrapper(N_Vector v, N_Vector Jv, N_Vector u, int* new_u,
429  void *user_data) {
430  try {
431  casadi_assert_dev(user_data);
432  auto this_ = static_cast<KinsolMemory*>(user_data);
433  this_->self.jtimes(*this_, v, Jv, u, new_u);
434  return 0;
435  } catch(std::exception& e) {
436  uerr() << "jtimes failed: " << e.what() << std::endl;;
437  return 1;
438  }
439  }
440 
441  void KinsolInterface::jtimes(KinsolMemory& m, N_Vector v, N_Vector Jv,
442  N_Vector u, int* new_u) const {
443  // Evaluate f_fwd_
444  std::copy_n(m.iarg, n_in_, m.arg);
445  m.arg[iin_] = NV_DATA_S(u);
446  m.arg[n_in_] = NV_DATA_S(v);
447  m.res[0] = NV_DATA_S(Jv);
448  jtimes_(m.arg, m.res, m.iw, m.w, 0);
449  }
450 
452  psetup_wrapper(N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale,
453  void* user_data, N_Vector tmp1, N_Vector tmp2) {
454  try {
455  casadi_assert_dev(user_data);
456  auto this_ = static_cast<KinsolMemory*>(user_data);
457  this_->self.psetup(*this_, u, uscale, fval, fscale, tmp1, tmp2);
458  return 0;
459  } catch(std::exception& e) {
460  uerr() << "psetup failed: " << e.what() << std::endl;;
461  return 1;
462  }
463  }
464 
466  psetup(KinsolMemory& m, N_Vector u, N_Vector uscale, N_Vector fval,
467  N_Vector fscale, N_Vector tmp1, N_Vector tmp2) const {
468  // Evaluate jac_
469  std::copy_n(m.iarg, n_in_, m.arg);
470  m.arg[iin_] = NV_DATA_S(u);
471  std::fill_n(m.res, n_out_+1, nullptr);
472  m.res[0] = m.jac;
473  if (calc_function(&m, "jac_g_x")) casadi_error("Jacobian calculation failed");
474 
475  // Get sparsity and non-zero elements
476  //const int* colind = sp_jac_.colind();
477  //int ncol = sp_jac_.size2();
478  //const int* row = sp_jac_.row();
479 
480  // Factorize the linear system
481  if (linsol_.nfact(m.jac)) casadi_error("'nfact' failed");
482  }
483 
484  int KinsolInterface::psolve_wrapper(N_Vector u, N_Vector uscale, N_Vector fval,
485  N_Vector fscale, N_Vector v, void* user_data, N_Vector tmp) {
486  try {
487  casadi_assert_dev(user_data);
488  auto this_ = static_cast<KinsolMemory*>(user_data);
489  this_->self.psolve(*this_, u, uscale, fval, fscale, v, tmp);
490  return 0;
491  } catch(std::exception& e) {
492  uerr() << "psolve failed: " << e.what() << std::endl;;
493  return 1;
494  }
495  }
496 
497  void KinsolInterface::psolve(KinsolMemory& m, N_Vector u, N_Vector uscale, N_Vector fval,
498  N_Vector fscale, N_Vector v, N_Vector tmp) const {
499  // Solve the factorized system
500  if (linsol_.solve(m.jac, NV_DATA_S(v))) casadi_error("'solve' failed");
501  }
502 
503  int KinsolInterface::lsetup(KINMem kin_mem) {
504  try {
505  auto m = to_mem(kin_mem->kin_lmem);
506  auto& s = m->self;
507 
508  N_Vector u = kin_mem->kin_uu;
509  N_Vector uscale = kin_mem->kin_uscale;
510  N_Vector fval = kin_mem->kin_fval;
511  N_Vector fscale = kin_mem->kin_fscale;
512  N_Vector tmp1 = kin_mem->kin_vtemp1;
513  N_Vector tmp2 = kin_mem->kin_vtemp2;
514  s.psetup(*m, u, uscale, fval, fscale, tmp1, tmp2);
515 
516  return 0;
517  } catch(std::exception& e) {
518  uerr() << "lsetup failed: " << e.what() << std::endl;;
519  return -1;
520  }
521  }
522 
524  lsolve(KINMem kin_mem, N_Vector x, N_Vector b, double *sJpnorm, double *sFdotJp) {
525  try {
526  auto m = to_mem(kin_mem->kin_lmem);
527  auto& s = m->self;
528 
529  // Get vectors
530  N_Vector u = kin_mem->kin_uu;
531  N_Vector uscale = kin_mem->kin_uscale;
532  N_Vector fval = kin_mem->kin_fval;
533  N_Vector fscale = kin_mem->kin_fscale;
534  N_Vector tmp1 = kin_mem->kin_vtemp1;
535  //N_Vector tmp2 = kin_mem->kin_vtemp2;
536 
537  // Solve the linear system
538  N_VScale(1.0, b, x);
539  s.psolve(*m, u, uscale, fval, fscale, x, tmp1);
540 
541  // Calculate residuals
542  int flag = KINSpilsAtimes(kin_mem, x, b);
543  if (flag) return flag;
544  *sJpnorm = N_VWL2Norm(b, fscale);
545  N_VProd(b, fscale, b);
546  N_VProd(b, fscale, b);
547  *sFdotJp = N_VDotProd(fval, b);
548 
549  return 0;
550  } catch(std::exception& e) {
551  uerr() << "lsolve failed: " << e.what() << std::endl;;
552  return -1;
553  }
554  }
555 
556  void KinsolInterface::ehfun(int error_code, const char *module, const char *function,
557  char *msg, void *eh_data) {
558  try {
559  auto m = to_mem(eh_data);
560  auto& s = m->self;
561  if (!s.disable_internal_warnings_) {
562  uerr() << msg << std::endl;
563  }
564  } catch(std::exception& e) {
565  uerr() << "ehfun failed: " << e.what() << std::endl;
566  }
567  }
568 
569  void KinsolInterface::ihfun(const char *module, const char *function, char *msg, void *ih_data) {
570  try {
571  // auto m = to_mem(ih_data);
572  // auto& s = m->self;
573  uout() << "[" << module << "] " << function << "\n " << msg << std::endl;
574  } catch(std::exception& e) {
575  uout() << "ihfun failed: " << e.what() << std::endl;
576  }
577  }
578 
579  void KinsolInterface::kinsol_error(const std::string& module, int flag, bool fatal) const {
580  // Get the error message
581  const char *id, *msg;
582  switch (flag) {
583  case KIN_SUCCESS:
584  id = "KIN_SUCCES";
585  msg = "KINSol succeeded; the scaled norm of F(u) is less than fnormtol";
586  break;
587  case KIN_INITIAL_GUESS_OK:
588  id = "KIN_INITIAL_GUESS_OK";
589  msg = "The guess u = u0 satisfied the system F(u) = 0 within the tolerances specified.";
590  break;
591  case KIN_STEP_LT_STPTOL:
592  id = "KIN_STEP_LT_STPTOL";
593  msg = "KINSol stopped based on scaled step length. This "
594  "means that the current iterate may be an approximate solution of the "
595  "given nonlinear system, but it is also quite possible that the algorithm"
596  " is 'stalled' (making insufficient progress) near an invalid solution, "
597  "or that the scalar scsteptol is too large.";
598  break;
599  case KIN_MEM_NULL:
600  id = "KIN_MEM_NULL";
601  msg = "The kinsol memory block pointer was NULL.";
602  break;
603  case KIN_ILL_INPUT:
604  id = "KIN_ILL_INPUT";
605  msg = "An input parameter was invalid.";
606  break;
607  case KIN_NO_MALLOC:
608  id = "KIN_NO_MALLOC";
609  msg = "The kinsol memory was not allocated by a call to KINCreate.";
610  break;
611  case KIN_LINESEARCH_NONCONV:
612  id = "KIN_LINESEARCH_NONCONV";
613  msg = "The line search algorithm was unable to find "
614  "an iterate sufficiently distinct from the current iterate, or could not"
615  " find an iterate satisfying the sufficient decrease condition. Failure"
616  " to satisfy the sufficient decrease condition could mean the current "
617  "iterate is 'close' to an approximate solution of the given nonlinear "
618  "system, the difference approximation of the matrix-vector product J(u)v"
619  " is inaccurate, or the real scalar scsteptol is too large.";
620  break;
621  case KIN_MAXITER_REACHED:
622  id = "KIN_MAXITER_REACHED";
623  msg = "The maximum number of nonlinear iterations "
624  "has been reached.";
625  break;
626  case KIN_MXNEWT_5X_EXCEEDED:
627  id = "KIN_MXNEWT_5X_EXCEEDED";
628  msg = "Five consecutive steps have been taken that "
629  "satisfy the inequality || D_u p ||_L2 > 0.99 mxnewtstep, where p "
630  "denotes the current step and mxnewtstep is a scalar upper bound on the "
631  "scaled step length. Such a failure may mean that || D_F F(u)||_L2 "
632  "asymptotes from above to a positive value, or the real scalar "
633  "mxnewtstep is too small.";
634  break;
635  case KIN_LINESEARCH_BCFAIL:
636  id = "KIN_LINESEARCH_BCFAIL";
637  msg = "The line search algorithm was unable to satisfy "
638  "the “beta-condition” for MXNBCF +1 nonlinear iterations (not necessarily "
639  "consecutive), which may indicate the algorithm is making poor progress.";
640  break;
641  case KIN_LINSOLV_NO_RECOVERY:
642  id = "KIN_LINSOLV_NO_RECOVERY";
643  msg = "The user-supplied routine psolve encountered a"
644  " recoverable error, but the preconditioner is already current.";
645  break;
646  case KIN_LINIT_FAIL:
647  id = "KIN_LINIT_FAIL";
648  msg = "The linear solver initialization routine (linit) encountered an error.";
649  break;
650  case KIN_LSETUP_FAIL:
651  id = "KIN_LSETUP_FAIL";
652  msg = "The user-supplied routine pset (used to set up the "
653  "preconditioner data) encountered an unrecoverable error.";
654  break;
655  case KIN_LSOLVE_FAIL:
656  id = "KIN_LSOLVE_FAIL";
657  msg = "Either the user-supplied routine psolve "
658  "(used to to solve the preconditioned linear system) encountered an "
659  "unrecoverable error, or the linear solver routine (lsolve) "
660  "encountered an error condition.";
661  break;
662  case KIN_SYSFUNC_FAIL:
663  id = "KIN_SYSFUNC_FAIL";
664  msg = "The system function failed in an unrecoverable manner.";
665  break;
666  case KIN_FIRST_SYSFUNC_ERR:
667  id = "KIN_FIRST_SYSFUNC_ERR";
668  msg = "The system function failed recoverably at the first call.";
669  break;
670  case KIN_REPTD_SYSFUNC_ERR:
671  id = "KIN_REPTD_SYSFUNC_ERR";
672  msg = "The system function had repeated recoverable errors. "
673  "No recovery is possible.";
674  break;
675  default:
676  id = "N/A";
677  msg = nullptr;
678  }
679 
680  // Construct message
681  std::stringstream ss;
682  if (msg==nullptr) {
683  ss << "Unknown " << (fatal? "error" : "warning") <<" (" << flag << ")"
684  " from module \"" << module << "\".";
685  } else {
686  ss << "Module \"" << module << "\" returned flag \"" << id << "\"." << std::endl;
687  ss << "The description of this flag is: " << std::endl;
688  ss << "\"" << msg << "\"" << std::endl;
689  }
690  ss << "Consult KINSOL documentation for more information.";
691  if (fatal) {
692  casadi_error("nlpsol process failed. "
693  "Set 'error_on_fail' option to false to ignore this error. "
694  + ss.str());
695  } else {
696  casadi_warning(ss.str());
697  }
698  }
699 
701  this->u = nullptr;
702  this->mem = nullptr;
703  }
704 
706  if (this->u) N_VDestroy_Serial(this->u);
707  if (this->mem) KINFree(&this->mem);
708  }
709 
710  int KinsolInterface::init_mem(void* mem) const {
711  if (Rootfinder::init_mem(mem)) return 1;
712  auto m = static_cast<KinsolMemory*>(mem);
713 
714  // Current solution
715  m->u = N_VNew_Serial(n_);
716 
717  // Create KINSOL memory block
718  m->mem = KINCreate();
719 
720  // KINSOL bugfix
721  KINMem kin_mem = KINMem(m->mem);
722  kin_mem->kin_inexact_ls = FALSE;
723 
724  // Set optional inputs
725  int flag = KINSetUserData(m->mem, m);
726  casadi_assert(flag==KIN_SUCCESS, "KINSetUserData");
727 
728  // Set error handler function
729  flag = KINSetErrHandlerFn(m->mem, ehfun, m);
730  casadi_assert(flag==KIN_SUCCESS, "KINSetErrHandlerFn");
731 
732  // Set error handler function
733  flag = KINSetInfoHandlerFn(m->mem, ihfun, m);
734  casadi_assert(flag==KIN_SUCCESS, "KINSetInfoHandlerFn");
735 
736  // Printing
737  flag = KINSetPrintLevel(m->mem, print_level_);
738  casadi_assert(flag==KIN_SUCCESS, "KINSetPrintLevel");
739 
740  // Initialize KINSOL
741  flag = KINInit(m->mem, func_wrapper, m->u);
742  casadi_assert_dev(flag==KIN_SUCCESS);
743 
744  // Setting maximum number of Newton iterations
745  flag = KINSetMaxNewtonStep(m->mem, max_iter_);
746  casadi_assert_dev(flag==KIN_SUCCESS);
747 
748  // Set constraints
749  if (!u_c_.empty()) {
750  N_Vector domain = N_VNew_Serial(n_);
751  std::copy(u_c_.begin(), u_c_.end(), NV_DATA_S(domain));
752 
753  // Pass to KINSOL
754  flag = KINSetConstraints(m->mem, domain);
755  casadi_assert_dev(flag==KIN_SUCCESS);
756 
757  // Free the temporary vector
758  N_VDestroy_Serial(domain);
759  }
760 
761  switch (linear_solver_type_) {
763  // Dense Jacobian
764  flag = KINDense(m->mem, n_);
765  casadi_assert(flag==KIN_SUCCESS, "KINDense");
766 
767  if (exact_jac_) {
768  flag = KINDlsSetDenseJacFn(m->mem, djac_wrapper);
769  casadi_assert(flag==KIN_SUCCESS, "KINDlsSetDenseJacFn");
770  }
771  break;
773  // Banded Jacobian
774  flag = KINBand(m->mem, n_, upper_bandwidth_, lower_bandwidth_);
775  casadi_assert(flag==KIN_SUCCESS, "KINBand");
776 
777  if (exact_jac_) {
778  flag = KINDlsSetBandJacFn(m->mem, bjac_wrapper);
779  casadi_assert(flag==KIN_SUCCESS, "KINDlsBandJacFn");
780  }
781  break;
783  // Attach the sparse solver
784  switch (iterative_solver_) {
786  flag = KINSpgmr(m->mem, maxl_);
787  casadi_assert(flag==KIN_SUCCESS, "KINSpgmr");
788  break;
790  flag = KINSpbcg(m->mem, maxl_);
791  casadi_assert(flag==KIN_SUCCESS, "KINSpbcg");
792  break;
794  flag = KINSptfqmr(m->mem, maxl_);
795  casadi_assert(flag==KIN_SUCCESS, "KINSptfqmr");
796  break;
797  }
798 
799  // Attach functions for Jacobian information
800  if (exact_jac_) {
801  flag = KINSpilsSetJacTimesVecFn(m->mem, jtimes_wrapper);
802  casadi_assert(flag==KIN_SUCCESS, "KINSpilsSetJacTimesVecFn");
803  }
804 
805  // Add a preconditioner
806  if (use_preconditioner_) {
807  flag = KINSpilsSetPreconditioner(m->mem, psetup_wrapper, psolve_wrapper);
808  casadi_assert_dev(flag==KIN_SUCCESS);
809  }
810  break;
812  // Set fields in the IDA memory
813  KINMem kin_mem = KINMem(m->mem);
814  kin_mem->kin_lmem = m;
815  kin_mem->kin_lsetup = lsetup;
816  kin_mem->kin_lsolve = lsolve;
817  kin_mem->kin_setupNonNull = TRUE;
818  break;
819  }
820 
821  // Set stop criterion
822  flag = KINSetFuncNormTol(m->mem, abstol_);
823  casadi_assert_dev(flag==KIN_SUCCESS);
824  return 0;
825  }
826 
827 } // namespace casadi
Casadi exception class.
Definition: exception.hpp:77
size_t n_in_
Number of inputs and outputs.
casadi_int nnz_in() const
Number of input/output nonzeros.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
casadi_int nnz_out() const
Number of input/output nonzeros.
void alloc(const Function &f, bool persistent=false, int num_threads=1)
Ensure work vectors long enough to evaluate function.
Function object.
Definition: function.hpp:60
const std::vector< std::string > & name_in() const
Get input scheme.
Definition: function.cpp:961
Function factory(const std::string &name, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const AuxOut &aux=AuxOut(), const Dict &opts=Dict()) const
Definition: function.cpp:1812
const std::vector< std::string > & name_out() const
Get output scheme.
Definition: function.cpp:965
'kinsol' plugin for Rootfinder
void func(KinsolMemory &m, N_Vector u, N_Vector fval) const
Callback functions (to be updated)
void bjac(KinsolMemory &m, long N, long mupper, long mlower, N_Vector u, N_Vector fu, DlsMat J, N_Vector tmp1, N_Vector tmp2) const
static const std::string meta_doc
A documentation string.
static const Options options_
Options.
static KinsolMemory * to_mem(void *mem)
Cast to memory object.
void jtimes(KinsolMemory &m, N_Vector v, N_Vector Jv, N_Vector u, int *new_u) const
static int psolve_wrapper(N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, N_Vector v, void *user_data, N_Vector tmp)
int init_mem(void *mem) const override
Initalize memory block.
casadi_int strategy_
Globalization strategy.
static Rootfinder * creator(const std::string &name, const Function &f)
Create a new Rootfinder.
static void ehfun(int error_code, const char *module, const char *function, char *msg, void *eh_data)
static int jtimes_wrapper(N_Vector v, N_Vector Jv, N_Vector u, int *new_u, void *user_data)
void psolve(KinsolMemory &m, N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, N_Vector v, N_Vector tmp) const
IterativeSolver iterative_solver_
static void ihfun(const char *module, const char *function, char *msg, void *ih_data)
static int func_wrapper(N_Vector u, N_Vector fval, void *user_data)
Wrappers to callback functions.
static int djac_wrapper(long N, N_Vector u, N_Vector fu, DlsMat J, void *user_data, N_Vector tmp1, N_Vector tmp2)
void kinsol_error(const std::string &module, int flag, bool fatal=true) const
void psetup(KinsolMemory &m, N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, N_Vector tmp1, N_Vector tmp2) const
~KinsolInterface() override
Destructor.
int solve(void *mem) const override
Solve the system of equations and calculate derivatives.
KinsolInterface(const std::string &name, const Function &f)
Constructor.
static int lsetup(KINMem kin_mem)
Callback functions (updated)
void init(const Dict &opts) override
Initialize stage.
static int psetup_wrapper(N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, void *user_data, N_Vector tmp1, N_Vector tmp2)
static int bjac_wrapper(long N, long mupper, long mlower, N_Vector u, N_Vector fu, DlsMat J, void *user_data, N_Vector tmp1, N_Vector tmp2)
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
void djac(KinsolMemory &m, long N, N_Vector u, N_Vector fu, DlsMat J, N_Vector tmp1, N_Vector tmp2) const
static int lsolve(KINMem kin_mem, N_Vector x, N_Vector b, double *sJpnorm, double *sFdotJp)
void nfact(const DM &A) const
Numeric factorization of the linear system.
Definition: linsol.cpp:127
DM solve(const DM &A, const DM &B, bool tr=false) const
Definition: linsol.cpp:73
Function oracle_
Oracle: Used to generate other functions.
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool error_on_fail_
Throw an exception on failure?
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Internal class.
int init_mem(void *mem) const override
Initalize memory block.
Definition: rootfinder.cpp:271
casadi_int n_
Number of equations.
std::vector< casadi_int > u_c_
Constraints on decision variables.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: rootfinder.cpp:298
casadi_int iin_
Indices of the input and output that correspond to the actual root-finding.
static const Options options_
Options.
Linsol linsol_
Linear solver.
void init(const Dict &opts) override
Initialize.
Definition: rootfinder.cpp:197
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
The casadi namespace.
Definition: archiver.cpp:28
std::ostream & uerr()
void CASADI_ROOTFINDER_KINSOL_EXPORT casadi_load_rootfinder_kinsol()
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
@ 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.
int CASADI_ROOTFINDER_KINSOL_EXPORT casadi_register_rootfinder_kinsol(Rootfinder::Plugin *plugin)
std::ostream & uout()
@ SOLVER_RET_LIMITED
void * mem
KINSOL memory block.
KinsolMemory(const KinsolInterface &s)
Constructor.
const KinsolInterface & self
Function object.
N_Vector u
Variable.
Options metadata for a class.
Definition: options.hpp:40