GNU Octave  4.4.1
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-2018 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <string>
28 
29 #include <iomanip>
30 #include <iostream>
31 
32 #include "LSODE.h"
33 #include "lo-mappers.h"
34 
35 #include "defun.h"
36 #include "error.h"
37 #include "errwarn.h"
38 #include "ovl.h"
39 #include "ov-fcn.h"
40 #include "ov-cell.h"
41 #include "pager.h"
42 #include "parse.h"
43 #include "pr-output.h"
44 #include "unwind-prot.h"
45 #include "utils.h"
46 #include "variables.h"
47 
48 #include "LSODE-opts.cc"
49 
50 // Global pointer for user defined function required by lsode.
52 
53 // Global pointer for optional user defined jacobian function used by lsode.
55 
56 // Have we warned about imaginary values returned from user function?
57 static bool warned_fcn_imaginary = false;
58 static bool warned_jac_imaginary = false;
59 
60 // Is this a recursive call?
61 static int call_depth = 0;
62 
65 {
67 
68  octave_value_list args;
69  args(1) = t;
70  args(0) = x;
71 
72  if (lsode_fcn)
73  {
75 
76  try
77  {
78  tmp = octave::feval (lsode_fcn, args, 1);
79  }
80  catch (octave::execution_exception& e)
81  {
82  err_user_supplied_eval (e, "lsode");
83  }
84 
85  if (tmp.empty () || ! tmp(0).is_defined ())
86  err_user_supplied_eval ("lsode");
87 
88  if (! warned_fcn_imaginary && tmp(0).iscomplex ())
89  {
90  warning ("lsode: ignoring imaginary part returned from user-supplied function");
91  warned_fcn_imaginary = true;
92  }
93 
94  retval = tmp(0).xvector_value ("lsode: expecting user supplied function to return numeric vector");
95 
96  if (retval.isempty ())
97  err_user_supplied_eval ("lsode");
98  }
99 
100  return retval;
101 }
102 
103 Matrix
105 {
106  Matrix retval;
107 
108  octave_value_list args;
109  args(1) = t;
110  args(0) = x;
111 
112  if (lsode_jac)
113  {
115 
116  try
117  {
118  tmp = octave::feval (lsode_jac, args, 1);
119  }
120  catch (octave::execution_exception& e)
121  {
122  err_user_supplied_eval (e, "lsode");
123  }
124 
125  if (tmp.empty () || ! tmp(0).is_defined ())
126  err_user_supplied_eval ("lsode");
127 
128  if (! warned_jac_imaginary && tmp(0).iscomplex ())
129  {
130  warning ("lsode: ignoring imaginary part returned from user-supplied jacobian function");
131  warned_jac_imaginary = true;
132  }
133 
134  retval = tmp(0).xmatrix_value ("lsode: expecting user supplied jacobian function to return numeric array");
135 
136  if (retval.isempty ())
137  err_user_supplied_eval ("lsode");
138  }
139 
140  return retval;
141 }
142 
143 DEFMETHOD (lsode, interp, args, nargout,
144  doc: /* -*- texinfo -*-
145 @deftypefn {} {[@var{x}, @var{istate}, @var{msg}] =} lsode (@var{fcn}, @var{x_0}, @var{t})
146 @deftypefnx {} {[@var{x}, @var{istate}, @var{msg}] =} lsode (@var{fcn}, @var{x_0}, @var{t}, @var{t_crit})
147 Ordinary Differential Equation (ODE) solver.
148 
149 The set of differential equations to solve is
150 @tex
151 $$ {dx \over dt} = f (x, t) $$
152 with
153 $$ x(t_0) = x_0 $$
154 @end tex
155 @ifnottex
156 
157 @example
158 @group
159 dx
160 -- = f (x, t)
161 dt
162 @end group
163 @end example
164 
165 @noindent
166 with
167 
168 @example
169 x(t_0) = x_0
170 @end example
171 
172 @end ifnottex
173 The solution is returned in the matrix @var{x}, with each row
174 corresponding to an element of the vector @var{t}. The first element
175 of @var{t} should be @math{t_0} and should correspond to the initial
176 state of the system @var{x_0}, so that the first row of the output
177 is @var{x_0}.
178 
179 The first argument, @var{fcn}, is a string, inline, or function handle
180 that names the function @math{f} to call to compute the vector of right
181 hand sides for the set of equations. The function must have the form
182 
183 @example
184 @var{xdot} = f (@var{x}, @var{t})
185 @end example
186 
187 @noindent
188 in which @var{xdot} and @var{x} are vectors and @var{t} is a scalar.
189 
190 If @var{fcn} is a two-element string array or a two-element cell array
191 of strings, inline functions, or function handles, the first element names
192 the function @math{f} described above, and the second element names a
193 function to compute the Jacobian of @math{f}. The Jacobian function
194 must have the form
195 
196 @example
197 @var{jac} = j (@var{x}, @var{t})
198 @end example
199 
200 @noindent
201 in which @var{jac} is the matrix of partial derivatives
202 @tex
203 $$ J = {\partial f_i \over \partial x_j} = \left[\matrix{
204 {\partial f_1 \over \partial x_1}
205  & {\partial f_1 \over \partial x_2}
206  & \cdots
207  & {\partial f_1 \over \partial x_N} \cr
208 {\partial f_2 \over \partial x_1}
209  & {\partial f_2 \over \partial x_2}
210  & \cdots
211  & {\partial f_2 \over \partial x_N} \cr
212  \vdots & \vdots & \ddots & \vdots \cr
213 {\partial f_3 \over \partial x_1}
214  & {\partial f_3 \over \partial x_2}
215  & \cdots
216  & {\partial f_3 \over \partial x_N} \cr}\right]$$
217 @end tex
218 @ifnottex
219 
220 @example
221 @group
222  | df_1 df_1 df_1 |
223  | ---- ---- ... ---- |
224  | dx_1 dx_2 dx_N |
225  | |
226  | df_2 df_2 df_2 |
227  | ---- ---- ... ---- |
228  df_i | dx_1 dx_2 dx_N |
229 jac = ---- = | |
230  dx_j | . . . . |
231  | . . . . |
232  | . . . . |
233  | |
234  | df_N df_N df_N |
235  | ---- ---- ... ---- |
236  | dx_1 dx_2 dx_N |
237 @end group
238 @end example
239 
240 @end ifnottex
241 
242 The second argument specifies the initial state of the system @math{x_0}. The
243 third argument is a vector, @var{t}, specifying the time values for which a
244 solution is sought.
245 
246 The fourth argument is optional, and may be used to specify a set of
247 times that the ODE solver should not integrate past. It is useful for
248 avoiding difficulties with singularities and points where there is a
249 discontinuity in the derivative.
250 
251 After a successful computation, the value of @var{istate} will be 2
252 (consistent with the Fortran version of @sc{lsode}).
253 
254 If the computation is not successful, @var{istate} will be something
255 other than 2 and @var{msg} will contain additional information.
256 
257 You can use the function @code{lsode_options} to set optional
258 parameters for @code{lsode}.
259 @seealso{daspk, dassl, dasrt}
260 @end deftypefn */)
261 {
262  int nargin = args.length ();
263 
265  print_usage ();
266 
267  warned_fcn_imaginary = false;
268  warned_jac_imaginary = false;
269 
271 
273  call_depth++;
274 
275  if (call_depth > 1)
276  error ("lsode: invalid recursive call");
277 
278  octave::symbol_table& symtab = interp.get_symbol_table ();
279 
280  std::string fcn_name, fname, jac_name, jname;
281  lsode_fcn = nullptr;
282  lsode_jac = nullptr;
283 
284  octave_value f_arg = args(0);
285 
286  if (f_arg.iscell ())
287  {
288  Cell c = f_arg.cell_value ();
289  if (c.numel () == 1)
290  f_arg = c(0);
291  else if (c.numel () == 2)
292  {
293  if (c(0).is_function_handle () || c(0).is_inline_function ())
294  lsode_fcn = c(0).function_value ();
295  else
296  {
297  fcn_name = unique_symbol_name ("__lsode_fcn__");
298  fname = "function y = ";
299  fname.append (fcn_name);
300  fname.append (" (x, t) y = ");
301  lsode_fcn = extract_function (c(0), "lsode", fcn_name, fname,
302  "; endfunction");
303  }
304 
305  if (lsode_fcn)
306  {
307  if (c(1).is_function_handle () || c(1).is_inline_function ())
308  lsode_jac = c(1).function_value ();
309  else
310  {
311  jac_name = unique_symbol_name ("__lsode_jac__");
312  jname = "function jac = ";
313  jname.append (jac_name);
314  jname.append (" (x, t) jac = ");
315  lsode_jac = extract_function (c(1), "lsode", jac_name,
316  jname, "; endfunction");
317 
318  if (! lsode_jac)
319  {
320  if (fcn_name.length ())
321  symtab.clear_function (fcn_name);
322  lsode_fcn = nullptr;
323  }
324  }
325  }
326  }
327  else
328  error ("lsode: incorrect number of elements in cell array");
329  }
330 
331  if (! lsode_fcn && ! f_arg.iscell ())
332  {
333  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
334  lsode_fcn = f_arg.function_value ();
335  else
336  {
337  switch (f_arg.rows ())
338  {
339  case 1:
340  do
341  {
342  fcn_name = unique_symbol_name ("__lsode_fcn__");
343  fname = "function y = ";
344  fname.append (fcn_name);
345  fname.append (" (x, t) y = ");
346  lsode_fcn = extract_function (f_arg, "lsode", fcn_name,
347  fname, "; endfunction");
348  }
349  while (0);
350  break;
351 
352  case 2:
353  {
355 
356  fcn_name = unique_symbol_name ("__lsode_fcn__");
357  fname = "function y = ";
358  fname.append (fcn_name);
359  fname.append (" (x, t) y = ");
360  lsode_fcn = extract_function (tmp(0), "lsode", fcn_name,
361  fname, "; endfunction");
362 
363  if (lsode_fcn)
364  {
365  jac_name = unique_symbol_name ("__lsode_jac__");
366  jname = "function jac = ";
367  jname.append (jac_name);
368  jname.append (" (x, t) jac = ");
369  lsode_jac = extract_function (tmp(1), "lsode",
370  jac_name, jname,
371  "; endfunction");
372 
373  if (! lsode_jac)
374  {
375  if (fcn_name.length ())
376  symtab.clear_function (fcn_name);
377  lsode_fcn = nullptr;
378  }
379  }
380  }
381  break;
382 
383  default:
384  error ("lsode: first arg should be a string or 2-element string array");
385  }
386  }
387  }
388 
389  if (! lsode_fcn)
390  error ("lsode: FCN argument is not a valid function name or handle");
391 
392  ColumnVector state = args(1).xvector_value ("lsode: initial state X_0 must be a vector");
393  ColumnVector out_times = args(2).xvector_value ("lsode: output time variable T must be a vector");
394 
395  ColumnVector crit_times;
396 
397  int crit_times_set = 0;
398  if (nargin > 3)
399  {
400  crit_times = args(3).xvector_value ("lsode: list of critical times T_CRIT must be a vector");
401 
402  crit_times_set = 1;
403  }
404 
405  double tzero = out_times (0);
406 
408  if (lsode_jac)
410 
411  LSODE ode (state, tzero, func);
412 
413  ode.set_options (lsode_opts);
414 
415  Matrix output;
416  if (crit_times_set)
417  output = ode.integrate (out_times, crit_times);
418  else
419  output = ode.integrate (out_times);
420 
421  if (fcn_name.length ())
422  symtab.clear_function (fcn_name);
423  if (jac_name.length ())
424  symtab.clear_function (jac_name);
425 
426  std::string msg = ode.error_message ();
427 
429 
430  if (ode.integration_ok ())
431  retval(0) = output;
432  else if (nargout < 2)
433  error ("lsode: %s", msg.c_str ());
434  else
435  retval(0) = Matrix ();
436 
437  retval(1) = static_cast<double> (ode.integration_state ());
438  retval(2) = msg;
439 
440  return retval;
441 }
442 
443 /*
444 
445 ## dassl-1.m
446 ##
447 ## Test lsode() function
448 ##
449 ## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
450 ## Comalco Research and Technology
451 ## 20 May 1998
452 ##
453 ## Problem
454 ##
455 ## y1' = -y2, y1(0) = 1
456 ## y2' = y1, y2(0) = 0
457 ##
458 ## Solution
459 ##
460 ## y1(t) = cos(t)
461 ## y2(t) = sin(t)
462 ##
463 %!function xdot = __f (x, t)
464 %! xdot = [-x(2); x(1)];
465 %!endfunction
466 %!test
467 %!
468 %! x0 = [1; 0];
469 %! xdot0 = [0; 1];
470 %! t = (0:1:10)';
471 %!
472 %! tol = 500 * lsode_options ("relative tolerance");
473 %!
474 %! x = lsode ("__f", x0, t);
475 %!
476 %! y = [cos(t), sin(t)];
477 %!
478 %! assert (x, y, tol);
479 
480 %!function xdotdot = __f (x, t)
481 %! xdotdot = [x(2); -x(1)];
482 %!endfunction
483 %!test
484 %!
485 %! x0 = [1; 0];
486 %! t = [0; 2*pi];
487 %! tol = 100 * dassl_options ("relative tolerance");
488 %!
489 %! x = lsode ("__f", x0, t);
490 %!
491 %! y = [1, 0; 1, 0];
492 %!
493 %! assert (x, y, tol);
494 
495 %!function xdot = __f (x, t)
496 %! xdot = x;
497 %!endfunction
498 %!test
499 %!
500 %! x0 = 1;
501 %! t = [0; 1];
502 %! tol = 100 * dassl_options ("relative tolerance");
503 %!
504 %! x = lsode ("__f", x0, t);
505 %!
506 %! y = [1; e];
507 %!
508 %! assert (x, y, tol);
509 
510 %!test
511 %! lsode_options ("absolute tolerance", eps);
512 %! assert (lsode_options ("absolute tolerance") == eps);
513 
514 %!error lsode_options ("foo", 1, 2)
515 */
OCTINTERP_API octave_value_list feval(const std::string &name, const octave_value_list &args=octave_value_list(), int nargout=0)
static int call_depth
Definition: lsode.cc:61
static bool warned_jac_imaginary
Definition: lsode.cc:58
Definition: Cell.h:37
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:135
fname
Definition: load-save.cc:767
bool isempty(void) const
Definition: ov.h:529
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
Matrix lsode_user_jacobian(const ColumnVector &x, double t)
Definition: lsode.cc:104
static bool warned_fcn_imaginary
Definition: lsode.cc:57
void error(const char *fmt,...)
Definition: error.cc:578
ColumnVector lsode_user_function(const ColumnVector &x, double t)
Definition: lsode.cc:64
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function t
Definition: ov-usr-fcn.cc:997
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
Definition: LSODE.h:33
i e
Definition: data.cc:2591
std::string unique_symbol_name(const std::string &basename)
Definition: variables.cc:498
ODEFunc & set_jacobian_function(ODEJacFunc j)
Definition: ODEFunc.h:75
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function xample nargout(@histc)
Definition: ov-usr-fcn.cc:997
bool is_function_handle(void) const
Definition: ov.h:749
bool iscell(void) const
Definition: ov.h:536
static octave_function * lsode_jac
Definition: lsode.cc:54
octave_idx_type rows(void) const
Definition: ov.h:472
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
octave_function * function_value(bool silent=false) const
Definition: dMatrix.h:36
static uint32_t state[624]
Definition: randmtzig.cc:183
void warning(const char *fmt,...)
Definition: error.cc:801
static octave_function * lsode_fcn
Definition: lsode.cc:51
octave::unwind_protect frame
Definition: graphics.cc:12190
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:148
octave_function * extract_function(const octave_value &arg, const std::string &warn_for, const std::string &fname, const std::string &header, const std::string &trailer)
Definition: variables.cc:125
args.length() nargin
Definition: file-io.cc:589
void clear_function(const std::string &name)
Definition: symtab.h:392
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:958
nd group nd example Use ode
Definition: cellfun.cc:400
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
Cell cell_value(void) const
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x
bool is_inline_function(void) const
Definition: ov.h:755