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) 1996-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 <list>
31 #include <string>
32 
33 #include "LSODE.h"
34 #include "lo-mappers.h"
35 
36 #include "defun.h"
37 #include "error.h"
38 #include "errwarn.h"
39 #include "interpreter-private.h"
40 #include "ovl.h"
41 #include "ov-fcn.h"
42 #include "ov-cell.h"
43 #include "pager.h"
44 #include "parse.h"
45 #include "pr-output.h"
46 #include "unwind-prot.h"
47 #include "utils.h"
48 #include "variables.h"
49 
50 #include "LSODE-opts.cc"
51 
52 // Global pointer for user defined function required by lsode.
54 
55 // Global pointer for optional user defined jacobian function used by lsode.
57 
58 // Have we warned about imaginary values returned from user function?
59 static bool warned_fcn_imaginary = false;
60 static bool warned_jac_imaginary = false;
61 
62 // Is this a recursive call?
63 static int call_depth = 0;
64 
67 {
69 
70  octave_value_list args;
71  args(1) = t;
72  args(0) = x;
73 
74  if (lsode_fcn.is_defined ())
75  {
77 
78  try
79  {
80  tmp = octave::feval (lsode_fcn, args, 1);
81  }
82  catch (octave::execution_exception& e)
83  {
84  err_user_supplied_eval (e, "lsode");
85  }
86 
87  if (tmp.empty () || ! tmp(0).is_defined ())
88  err_user_supplied_eval ("lsode");
89 
90  if (! warned_fcn_imaginary && tmp(0).iscomplex ())
91  {
92  warning ("lsode: ignoring imaginary part returned from user-supplied function");
93  warned_fcn_imaginary = true;
94  }
95 
96  retval = tmp(0).xvector_value ("lsode: expecting user supplied function to return numeric vector");
97 
98  if (retval.isempty ())
99  err_user_supplied_eval ("lsode");
100  }
101 
102  return retval;
103 }
104 
105 Matrix
107 {
108  Matrix retval;
109 
110  octave_value_list args;
111  args(1) = t;
112  args(0) = x;
113 
114  if (lsode_jac.is_defined ())
115  {
116  octave_value_list tmp;
117 
118  try
119  {
120  tmp = octave::feval (lsode_jac, args, 1);
121  }
122  catch (octave::execution_exception& e)
123  {
124  err_user_supplied_eval (e, "lsode");
125  }
126 
127  if (tmp.empty () || ! tmp(0).is_defined ())
128  err_user_supplied_eval ("lsode");
129 
130  if (! warned_jac_imaginary && tmp(0).iscomplex ())
131  {
132  warning ("lsode: ignoring imaginary part returned from user-supplied jacobian function");
133  warned_jac_imaginary = true;
134  }
135 
136  retval = tmp(0).xmatrix_value ("lsode: expecting user supplied jacobian function to return numeric array");
137 
138  if (retval.isempty ())
139  err_user_supplied_eval ("lsode");
140  }
141 
142  return retval;
143 }
144 
145 DEFMETHOD (lsode, interp, args, nargout,
146  doc: /* -*- texinfo -*-
147 @deftypefn {} {[@var{x}, @var{istate}, @var{msg}] =} lsode (@var{fcn}, @var{x_0}, @var{t})
148 @deftypefnx {} {[@var{x}, @var{istate}, @var{msg}] =} lsode (@var{fcn}, @var{x_0}, @var{t}, @var{t_crit})
149 Ordinary Differential Equation (ODE) solver.
150 
151 The set of differential equations to solve is
152 @tex
153 $$ {dx \over dt} = f (x, t) $$
154 with
155 $$ x(t_0) = x_0 $$
156 @end tex
157 @ifnottex
158 
159 @example
160 @group
161 dx
162 -- = f (x, t)
163 dt
164 @end group
165 @end example
166 
167 @noindent
168 with
169 
170 @example
171 x(t_0) = x_0
172 @end example
173 
174 @end ifnottex
175 The solution is returned in the matrix @var{x}, with each row
176 corresponding to an element of the vector @var{t}. The first element
177 of @var{t} should be @math{t_0} and should correspond to the initial
178 state of the system @var{x_0}, so that the first row of the output
179 is @var{x_0}.
180 
181 The first argument, @var{fcn}, is a string, inline, or function handle
182 that names the function @math{f} to call to compute the vector of right
183 hand sides for the set of equations. The function must have the form
184 
185 @example
186 @var{xdot} = f (@var{x}, @var{t})
187 @end example
188 
189 @noindent
190 in which @var{xdot} and @var{x} are vectors and @var{t} is a scalar.
191 
192 If @var{fcn} is a two-element string array or a two-element cell array
193 of strings, inline functions, or function handles, the first element names
194 the function @math{f} described above, and the second element names a
195 function to compute the Jacobian of @math{f}. The Jacobian function
196 must have the form
197 
198 @example
199 @var{jac} = j (@var{x}, @var{t})
200 @end example
201 
202 @noindent
203 in which @var{jac} is the matrix of partial derivatives
204 @tex
205 $$ J = {\partial f_i \over \partial x_j} = \left[\matrix{
206 {\partial f_1 \over \partial x_1}
207  & {\partial f_1 \over \partial x_2}
208  & \cdots
209  & {\partial f_1 \over \partial x_N} \cr
210 {\partial f_2 \over \partial x_1}
211  & {\partial f_2 \over \partial x_2}
212  & \cdots
213  & {\partial f_2 \over \partial x_N} \cr
214  \vdots & \vdots & \ddots & \vdots \cr
215 {\partial f_3 \over \partial x_1}
216  & {\partial f_3 \over \partial x_2}
217  & \cdots
218  & {\partial f_3 \over \partial x_N} \cr}\right]$$
219 @end tex
220 @ifnottex
221 
222 @example
223 @group
224  | df_1 df_1 df_1 |
225  | ---- ---- ... ---- |
226  | dx_1 dx_2 dx_N |
227  | |
228  | df_2 df_2 df_2 |
229  | ---- ---- ... ---- |
230  df_i | dx_1 dx_2 dx_N |
231 jac = ---- = | |
232  dx_j | . . . . |
233  | . . . . |
234  | . . . . |
235  | |
236  | df_N df_N df_N |
237  | ---- ---- ... ---- |
238  | dx_1 dx_2 dx_N |
239 @end group
240 @end example
241 
242 @end ifnottex
243 
244 The second argument specifies the initial state of the system @math{x_0}. The
245 third argument is a vector, @var{t}, specifying the time values for which a
246 solution is sought.
247 
248 The fourth argument is optional, and may be used to specify a set of
249 times that the ODE solver should not integrate past. It is useful for
250 avoiding difficulties with singularities and points where there is a
251 discontinuity in the derivative.
252 
253 After a successful computation, the value of @var{istate} will be 2
254 (consistent with the Fortran version of @sc{lsode}).
255 
256 If the computation is not successful, @var{istate} will be something
257 other than 2 and @var{msg} will contain additional information.
258 
259 You can use the function @code{lsode_options} to set optional
260 parameters for @code{lsode}.
261 @seealso{daspk, dassl, dasrt}
262 @end deftypefn */)
263 {
264  int nargin = args.length ();
265 
266  if (nargin < 3 || nargin > 4)
267  print_usage ();
268 
269  warned_fcn_imaginary = false;
270  warned_jac_imaginary = false;
271 
273 
274  frame.protect_var (call_depth);
275  call_depth++;
276 
277  if (call_depth > 1)
278  error ("lsode: invalid recursive call");
279 
280  octave::symbol_table& symtab = interp.get_symbol_table ();
281 
282  std::string fcn_name, fname, jac_name, jname;
283 
284  lsode_fcn = octave_value ();
285  lsode_jac = octave_value ();
286 
287  octave_value f_arg = args(0);
288 
289  std::list<std::string> parameter_names ({"x", "t"});
290 
291  if (f_arg.iscell ())
292  {
293  Cell c = f_arg.cell_value ();
294  if (c.numel () == 1)
295  f_arg = c(0);
296  else if (c.numel () == 2)
297  {
298  lsode_fcn = octave::get_function_handle (interp, c(0),
299  parameter_names);
300 
301  if (lsode_fcn.is_defined ())
302  {
303  lsode_jac = octave::get_function_handle (interp, c(1),
304  parameter_names);
305 
306  if (lsode_jac.is_undefined ())
307  lsode_fcn = octave_value ();
308  }
309  }
310  else
311  error ("lsode: incorrect number of elements in cell array");
312  }
313 
314  if (lsode_fcn.is_undefined () && ! f_arg.iscell ())
315  {
316  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
317  lsode_fcn = f_arg;
318  else
319  {
320  switch (f_arg.rows ())
321  {
322  case 1:
323  lsode_fcn = octave::get_function_handle (interp, f_arg,
324  parameter_names);
325  break;
326 
327  case 2:
328  {
329  string_vector tmp = f_arg.string_vector_value ();
330 
331  lsode_fcn = octave::get_function_handle (interp, tmp(0),
332  parameter_names);
333 
334  if (lsode_fcn.is_defined ())
335  {
336  lsode_jac = octave::get_function_handle (interp, tmp(1),
337  parameter_names);
338 
339  if (lsode_jac.is_undefined ())
340  lsode_fcn = octave_value ();
341  }
342  }
343  break;
344 
345  default:
346  error ("lsode: first arg should be a string or 2-element string array");
347  }
348  }
349  }
350 
351  if (lsode_fcn.is_undefined ())
352  error ("lsode: FCN argument is not a valid function name or handle");
353 
354  ColumnVector state = args(1).xvector_value ("lsode: initial state X_0 must be a vector");
355  ColumnVector out_times = args(2).xvector_value ("lsode: output time variable T must be a vector");
356 
357  ColumnVector crit_times;
358 
359  int crit_times_set = 0;
360  if (nargin > 3)
361  {
362  crit_times = args(3).xvector_value ("lsode: list of critical times T_CRIT must be a vector");
363 
364  crit_times_set = 1;
365  }
366 
367  double tzero = out_times (0);
368 
370 
371  if (lsode_jac.is_defined ())
373 
374  LSODE ode (state, tzero, func);
375 
376  ode.set_options (lsode_opts);
377 
378  Matrix output;
379  if (crit_times_set)
380  output = ode.integrate (out_times, crit_times);
381  else
382  output = ode.integrate (out_times);
383 
384  if (fcn_name.length ())
385  symtab.clear_function (fcn_name);
386  if (jac_name.length ())
387  symtab.clear_function (jac_name);
388 
389  std::string msg = ode.error_message ();
390 
392 
393  if (ode.integration_ok ())
394  retval(0) = output;
395  else if (nargout < 2)
396  error ("lsode: %s", msg.c_str ());
397  else
398  retval(0) = Matrix ();
399 
400  retval(1) = static_cast<double> (ode.integration_state ());
401  retval(2) = msg;
402 
403  return retval;
404 }
405 
406 /*
407 
408 ## dassl-1.m
409 ##
410 ## Test lsode() function
411 ##
412 ## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
413 ## Comalco Research and Technology
414 ## 20 May 1998
415 ##
416 ## Problem
417 ##
418 ## y1' = -y2, y1(0) = 1
419 ## y2' = y1, y2(0) = 0
420 ##
421 ## Solution
422 ##
423 ## y1(t) = cos(t)
424 ## y2(t) = sin(t)
425 ##
426 %!function xdot = __f (x, t)
427 %! xdot = [-x(2); x(1)];
428 %!endfunction
429 %!test
430 %!
431 %! x0 = [1; 0];
432 %! xdot0 = [0; 1];
433 %! t = (0:1:10)';
434 %!
435 %! tol = 500 * lsode_options ("relative tolerance");
436 %!
437 %! x = lsode ("__f", x0, t);
438 %!
439 %! y = [cos(t), sin(t)];
440 %!
441 %! assert (x, y, tol);
442 
443 %!function xdotdot = __f (x, t)
444 %! xdotdot = [x(2); -x(1)];
445 %!endfunction
446 %!test
447 %!
448 %! x0 = [1; 0];
449 %! t = [0; 2*pi];
450 %! tol = 100 * dassl_options ("relative tolerance");
451 %!
452 %! x = lsode ("__f", x0, t);
453 %!
454 %! y = [1, 0; 1, 0];
455 %!
456 %! assert (x, y, tol);
457 
458 %!function xdot = __f (x, t)
459 %! xdot = x;
460 %!endfunction
461 %!test
462 %!
463 %! x0 = 1;
464 %! t = [0; 1];
465 %! tol = 100 * dassl_options ("relative tolerance");
466 %!
467 %! x = lsode ("__f", x0, t);
468 %!
469 %! y = [1; e];
470 %!
471 %! assert (x, y, tol);
472 
473 %!test
474 %! lsode_options ("absolute tolerance", eps);
475 %! assert (lsode_options ("absolute tolerance") == eps);
476 
477 %!error lsode_options ("foo", 1, 2)
478 */
static LSODE_options lsode_opts
Definition: LSODE-opts.cc:21
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
Definition: Cell.h:43
Definition: LSODE.h:39
std::string error_message(void) const
Definition: LSODE.cc:310
Definition: dMatrix.h:42
ODEFunc & set_jacobian_function(ODEJacFunc j)
Definition: ODEFunc.h:78
virtual ColumnVector integrate(double tt)
Definition: ODE.h:79
bool integration_ok(void) const
Definition: base-de.h:100
octave_idx_type integration_state(void) const
Definition: base-de.h:102
void clear_function(const std::string &name)
Definition: symtab.cc:434
bool empty(void) const
Definition: ovl.h:115
bool iscell(void) const
Definition: ov.h:560
octave_idx_type rows(void) const
Definition: ov.h:504
bool is_defined(void) const
Definition: ov.h:551
Cell cell_value(void) const
bool is_function_handle(void) const
Definition: ov.h:721
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:930
bool is_undefined(void) const
Definition: ov.h:554
bool is_inline_function(void) const
Definition: ov.h:727
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
void warning(const char *fmt,...)
Definition: error.cc:1050
void error(const char *fmt,...)
Definition: error.cc:968
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:152
F77_RET_T const F77_DBLE * x
static int call_depth
Definition: lsode.cc:63
static octave_value lsode_jac
Definition: lsode.cc:56
Matrix lsode_user_jacobian(const ColumnVector &x, double t)
Definition: lsode.cc:106
DEFMETHOD(lsode, interp, args, nargout, doc:)
Definition: lsode.cc:145
static bool warned_jac_imaginary
Definition: lsode.cc:60
static octave_value lsode_fcn
Definition: lsode.cc:53
ColumnVector lsode_user_function(const ColumnVector &x, double t)
Definition: lsode.cc:66
static bool warned_fcn_imaginary
Definition: lsode.cc:59
static uint32_t state[624]
Definition: randmtzig.cc:190
octave_value get_function_handle(interpreter &interp, const octave_value &arg, const std::string &parameter_name)
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:9580
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811