GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
LSODE.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1993-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cinttypes>
31 #include <sstream>
32 
33 #include "LSODE.h"
34 #include "f77-fcn.h"
35 #include "lo-error.h"
36 #include "quit.h"
37 
38 typedef F77_INT (*lsode_fcn_ptr) (const F77_INT&, const double&, double*,
39  double*, F77_INT&);
40 
41 typedef F77_INT (*lsode_jac_ptr) (const F77_INT&, const double&, double*,
42  const F77_INT&, const F77_INT&, double*,
43  const F77_INT&);
44 
45 extern "C"
46 {
47  F77_RET_T
49  F77_DBLE&, F77_INT&, F77_DBLE&, const F77_DBLE*,
52  F77_INT&);
53 }
54 
58 
59 static F77_INT
60 lsode_f (const F77_INT& neq, const double& time, double *, double *deriv,
61  F77_INT& ierr)
62 {
63  ColumnVector tmp_deriv;
64 
65  // NOTE: this won't work if LSODE passes copies of the state vector.
66  // In that case we have to create a temporary vector object
67  // and copy.
68 
69  tmp_deriv = (*user_fun) (*tmp_x, time);
70 
71  if (tmp_deriv.isempty ())
72  ierr = -1;
73  else
74  {
75  for (F77_INT i = 0; i < neq; i++)
76  deriv[i] = tmp_deriv.elem (i);
77  }
78 
79  return 0;
80 }
81 
82 static F77_INT
83 lsode_j (const F77_INT& neq, const double& time, double *, const F77_INT&,
84  const F77_INT&, double *pd, const F77_INT& nrowpd)
85 {
86  Matrix tmp_jac (neq, neq);
87 
88  // NOTE: this won't work if LSODE passes copies of the state vector.
89  // In that case we have to create a temporary vector object
90  // and copy.
91 
92  tmp_jac = (*user_jac) (*tmp_x, time);
93 
94  for (F77_INT j = 0; j < neq; j++)
95  for (F77_INT i = 0; i < neq; i++)
96  pd[nrowpd * j + i] = tmp_jac (i, j);
97 
98  return 0;
99 }
100 
102 LSODE::do_integrate (double tout)
103 {
105 
106  static F77_INT nn = 0;
107 
108  if (! initialized || restart || ODEFunc::reset || LSODE_options::reset)
109  {
110  integration_error = false;
111 
112  initialized = true;
113 
114  istate = 1;
115 
116  F77_INT n = octave::to_f77_int (size ());
117 
118  nn = n;
119 
120  octave_idx_type max_maxord = 0;
121 
122  if (integration_method () == "stiff")
123  {
124  max_maxord = 5;
125 
126  if (jac)
127  method_flag = 21;
128  else
129  method_flag = 22;
130 
131  liw = 20 + n;
132  lrw = 22 + n * (9 + n);
133  }
134  else
135  {
136  max_maxord = 12;
137 
138  method_flag = 10;
139 
140  liw = 20;
141  lrw = 22 + 16 * n;
142  }
143 
144  iwork.resize (dim_vector (liw, 1));
145 
146  for (F77_INT i = 4; i < 9; i++)
147  iwork(i) = 0;
148 
149  rwork.resize (dim_vector (lrw, 1));
150 
151  for (F77_INT i = 4; i < 9; i++)
152  rwork(i) = 0;
153 
154  octave_idx_type maxord = maximum_order ();
155 
156  if (maxord >= 0)
157  {
158  if (maxord > 0 && maxord <= max_maxord)
159  {
160  iwork(4) = octave::to_f77_int (maxord);
161  iopt = 1;
162  }
163  else
164  {
165  // FIXME: Should this be a warning?
166  (*current_liboctave_error_handler)
167  ("lsode: invalid value for maximum order");
168  integration_error = true;
169  return retval;
170  }
171  }
172 
173  if (stop_time_set)
174  {
175  itask = 4;
176  rwork(0) = stop_time;
177  iopt = 1;
178  }
179  else
180  {
181  itask = 1;
182  }
183 
184  restart = false;
185 
186  // ODEFunc
187 
188  // NOTE: this won't work if LSODE passes copies of the state vector.
189  // In that case we have to create a temporary vector object
190  // and copy.
191 
192  tmp_x = &x;
193 
194  user_fun = function ();
196 
197  ColumnVector xdot = (*user_fun) (x, t);
198 
199  if (x.numel () != xdot.numel ())
200  {
201  // FIXME: Should this be a warning?
202  (*current_liboctave_error_handler)
203  ("lsode: inconsistent sizes for state and derivative vectors");
204 
205  integration_error = true;
206  return retval;
207  }
208 
209  ODEFunc::reset = false;
210 
211  // LSODE_options
212 
213  rel_tol = relative_tolerance ();
214  abs_tol = absolute_tolerance ();
215 
216  F77_INT abs_tol_len = octave::to_f77_int (abs_tol.numel ());
217 
218  if (abs_tol_len == 1)
219  itol = 1;
220  else if (abs_tol_len == n)
221  itol = 2;
222  else
223  {
224  // FIXME: Should this be a warning?
225  (*current_liboctave_error_handler)
226  ("lsode: inconsistent sizes for state and absolute tolerance vectors");
227 
228  integration_error = true;
229  return retval;
230  }
231 
232  double iss = initial_step_size ();
233  if (iss >= 0.0)
234  {
235  rwork(4) = iss;
236  iopt = 1;
237  }
238 
239  double maxss = maximum_step_size ();
240  if (maxss >= 0.0)
241  {
242  rwork(5) = maxss;
243  iopt = 1;
244  }
245 
246  double minss = minimum_step_size ();
247  if (minss >= 0.0)
248  {
249  rwork(6) = minss;
250  iopt = 1;
251  }
252 
253  F77_INT sl = octave::to_f77_int (step_limit ());
254  if (sl > 0)
255  {
256  iwork(5) = sl;
257  iopt = 1;
258  }
259 
260  LSODE_options::reset = false;
261  }
262 
263  double *px = x.fortran_vec ();
264 
265  double *pabs_tol = abs_tol.fortran_vec ();
266 
267  F77_INT *piwork = iwork.fortran_vec ();
268  double *prwork = rwork.fortran_vec ();
269 
270  F77_INT tmp_istate = octave::to_f77_int (istate);
271 
272  F77_XFCN (dlsode, DLSODE, (lsode_f, nn, px, t, tout, itol, rel_tol,
273  pabs_tol, itask, tmp_istate, iopt, prwork, lrw,
274  piwork, liw, lsode_j, method_flag));
275 
276  istate = tmp_istate;
277 
278  switch (istate)
279  {
280  case 1: // prior to initial integration step.
281  case 2: // lsode was successful.
282  retval = x;
283  t = tout;
284  break;
285 
286  case -1: // excess work done on this call (perhaps wrong mf).
287  case -2: // excess accuracy requested (tolerances too small).
288  case -3: // invalid input detected (see printed message).
289  case -4: // repeated error test failures (check all inputs).
290  case -5: // repeated convergence failures (perhaps bad Jacobian
291  // supplied or wrong choice of mf or tolerances).
292  case -6: // error weight became zero during problem. (solution
293  // component i vanished, and atol or atol(i) = 0.)
294  case -13: // return requested in user-supplied function.
295  integration_error = true;
296  break;
297 
298  default:
299  integration_error = true;
300  (*current_liboctave_error_handler)
301  ("unrecognized value of istate (= %" OCTAVE_IDX_TYPE_FORMAT ") "
302  "returned from lsode", istate);
303  break;
304  }
305 
306  return retval;
307 }
308 
309 std::string
311 {
312  std::string retval;
313 
314  std::ostringstream buf;
315  buf << t;
316  std::string t_curr = buf.str ();
317 
318  switch (istate)
319  {
320  case 1:
321  retval = "prior to initial integration step";
322  break;
323 
324  case 2:
325  retval = "successful exit";
326  break;
327 
328  case 3:
329  retval = "prior to continuation call with modified parameters";
330  break;
331 
332  case -1:
333  retval = "excess work on this call (t = " + t_curr +
334  "; perhaps wrong integration method)";
335  break;
336 
337  case -2:
338  retval = "excess accuracy requested (tolerances too small)";
339  break;
340 
341  case -3:
342  retval = "invalid input detected (see printed message)";
343  break;
344 
345  case -4:
346  retval = "repeated error test failures (t = " + t_curr +
347  "; check all inputs)";
348  break;
349 
350  case -5:
351  retval = "repeated convergence failures (t = " + t_curr +
352  "; perhaps bad Jacobian supplied or wrong choice of integration method or tolerances)";
353  break;
354 
355  case -6:
356  retval = "error weight became zero during problem. (t = " + t_curr +
357  "; solution component i vanished, and atol or atol(i) == 0)";
358  break;
359 
360  case -13:
361  retval = "return requested in user-supplied function (t = "
362  + t_curr + ')';
363  break;
364 
365  default:
366  retval = "unknown error state";
367  break;
368  }
369 
370  return retval;
371 }
372 
373 Matrix
375 {
376  Matrix retval;
377 
378  octave_idx_type n_out = tout.numel ();
379  F77_INT n = octave::to_f77_int (size ());
380 
381  if (n_out > 0 && n > 0)
382  {
383  retval.resize (n_out, n);
384 
385  for (F77_INT i = 0; i < n; i++)
386  retval.elem (0, i) = x.elem (i);
387 
388  for (octave_idx_type j = 1; j < n_out; j++)
389  {
390  ColumnVector x_next = do_integrate (tout.elem (j));
391 
392  if (integration_error)
393  return retval;
394 
395  for (F77_INT i = 0; i < n; i++)
396  retval.elem (j, i) = x_next.elem (i);
397  }
398  }
399 
400  return retval;
401 }
402 
403 Matrix
404 LSODE::do_integrate (const ColumnVector& tout, const ColumnVector& tcrit)
405 {
406  Matrix retval;
407 
408  octave_idx_type n_out = tout.numel ();
409  F77_INT n = octave::to_f77_int (size ());
410 
411  if (n_out > 0 && n > 0)
412  {
413  retval.resize (n_out, n);
414 
415  for (F77_INT i = 0; i < n; i++)
416  retval.elem (0, i) = x.elem (i);
417 
418  octave_idx_type n_crit = tcrit.numel ();
419 
420  if (n_crit > 0)
421  {
422  octave_idx_type i_crit = 0;
423  octave_idx_type i_out = 1;
424  double next_crit = tcrit.elem (0);
425  double next_out;
426  while (i_out < n_out)
427  {
428  bool do_restart = false;
429 
430  next_out = tout.elem (i_out);
431  if (i_crit < n_crit)
432  next_crit = tcrit.elem (i_crit);
433 
434  bool save_output = false;
435  double t_out;
436 
437  if (next_crit == next_out)
438  {
439  set_stop_time (next_crit);
440  t_out = next_out;
441  save_output = true;
442  i_out++;
443  i_crit++;
444  do_restart = true;
445  }
446  else if (next_crit < next_out)
447  {
448  if (i_crit < n_crit)
449  {
450  set_stop_time (next_crit);
451  t_out = next_crit;
452  save_output = false;
453  i_crit++;
454  do_restart = true;
455  }
456  else
457  {
458  clear_stop_time ();
459  t_out = next_out;
460  save_output = true;
461  i_out++;
462  }
463  }
464  else
465  {
466  set_stop_time (next_crit);
467  t_out = next_out;
468  save_output = true;
469  i_out++;
470  }
471 
472  ColumnVector x_next = do_integrate (t_out);
473 
474  if (integration_error)
475  return retval;
476 
477  if (save_output)
478  {
479  for (F77_INT i = 0; i < n; i++)
480  retval.elem (i_out-1, i) = x_next.elem (i);
481  }
482 
483  if (do_restart)
484  force_restart ();
485  }
486  }
487  else
488  {
489  retval = do_integrate (tout);
490 
491  if (integration_error)
492  return retval;
493  }
494  }
495 
496  return retval;
497 }
static F77_INT nn
Definition: DASPK.cc:66
F77_INT(* lsode_jac_ptr)(const F77_INT &, const double &, double *, const F77_INT &, const F77_INT &, double *, const F77_INT &)
Definition: LSODE.cc:41
static F77_INT lsode_f(const F77_INT &neq, const double &time, double *, double *deriv, F77_INT &ierr)
Definition: LSODE.cc:60
F77_INT(* lsode_fcn_ptr)(const F77_INT &, const double &, double *, double *, F77_INT &)
Definition: LSODE.cc:38
static ODEFunc::ODEJacFunc user_jac
Definition: LSODE.cc:56
F77_RET_T F77_FUNC(dlsode, DLSODE)(lsode_fcn_ptr
F77_RET_T F77_INT F77_DBLE F77_DBLE F77_DBLE F77_INT F77_DBLE const F77_DBLE F77_INT F77_INT F77_INT F77_DBLE F77_INT F77_INT F77_INT F77_INT &static ODEFunc::ODERHSFunc user_fun
Definition: LSODE.cc:52
static ColumnVector * tmp_x
Definition: LSODE.cc:57
static F77_INT lsode_j(const F77_INT &neq, const double &time, double *, const F77_INT &, const F77_INT &, double *pd, const F77_INT &nrowpd)
Definition: LSODE.cc:83
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:499
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
octave_f77_int_type itol
Definition: LSODE.h:69
octave_f77_int_type iopt
Definition: LSODE.h:68
std::string error_message(void) const
Definition: LSODE.cc:310
ColumnVector do_integrate(double t)
Definition: LSODE.cc:102
octave_f77_int_type method_flag
Definition: LSODE.h:66
bool initialized
Definition: LSODE.h:64
octave_f77_int_type itask
Definition: LSODE.h:67
Array< double > rwork
Definition: LSODE.h:75
octave_f77_int_type lrw
Definition: LSODE.h:72
Array< double > abs_tol
Definition: LSODE.h:79
Array< octave_f77_int_type > iwork
Definition: LSODE.h:74
double rel_tol
Definition: LSODE.h:77
octave_f77_int_type liw
Definition: LSODE.h:71
Definition: dMatrix.h:42
ColumnVector(* ODERHSFunc)(const ColumnVector &, double)
Definition: ODEFunc.h:39
Matrix(* ODEJacFunc)(const ColumnVector &, double)
Definition: ODEFunc.h:40
ODEJacFunc jac
Definition: ODEFunc.h:88
bool reset
Definition: ODEFunc.h:95
ODEJacFunc jacobian_function(void) const
Definition: ODEFunc.h:76
bool restart
Definition: base-de.h:116
virtual void force_restart(void)
Definition: base-de.h:98
double t
Definition: base-de.h:110
bool integration_error
Definition: base-de.h:118
double stop_time
Definition: base-de.h:112
octave_idx_type size(void) const
Definition: base-de.h:79
octave_idx_type istate
Definition: base-de.h:120
ColumnVector x
Definition: base-de.h:108
void set_stop_time(double tt)
Definition: base-de.h:85
bool stop_time_set
Definition: base-de.h:114
void clear_stop_time(void)
Definition: base-de.h:92
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
subroutine dlsode(F, NEQ, Y, T, TOUT, ITOL, RTOL, ATOL, ITASK, ISTATE, IOPT, RWORK, LRW, IWORK, LIW, JAC, MF)
Definition: dlsode.f:3
#define F77_XFCN(f, F, args)
Definition: f77-fcn.h:44
double F77_DBLE
Definition: f77-fcn.h:301
octave_f77_int_type F77_INT
Definition: f77-fcn.h:305
F77_RET_T(F77_CONST_CHAR_ARG_DECL, F77_CONST_CHAR_ARG_DECL, F77_CONST_CHAR_ARG_DECL, const F77_INT &, const F77_INT &, const F77_INT &, F77_INT &, F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, F77_DBLE *, F77_DBLE *, const F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, F77_INT *, F77_INT &F77_CHAR_ARG_LEN_DECL F77_CHAR_ARG_LEN_DECL F77_CHAR_ARG_LEN_DECL)
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE const F77_INT F77_INT & ierr
octave_idx_type n
Definition: mx-inlines.cc:753
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811