GNU Octave  4.2.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
lsode.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2017 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 the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 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 <http://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 "pr-output.h"
43 #include "unwind-prot.h"
44 #include "utils.h"
45 #include "variables.h"
46 
47 #include "LSODE-opts.cc"
48 
49 // Global pointer for user defined function required by lsode.
51 
52 // Global pointer for optional user defined jacobian function used by lsode.
54 
55 // Have we warned about imaginary values returned from user function?
56 static bool warned_fcn_imaginary = false;
57 static bool warned_jac_imaginary = false;
58 
59 // Is this a recursive call?
60 static int call_depth = 0;
61 
64 {
66 
68  args(1) = t;
69  args(0) = x;
70 
71  if (lsode_fcn)
72  {
74 
75  try
76  {
77  tmp = lsode_fcn->do_multi_index_op (1, args);
78  }
79  catch (octave::execution_exception& e)
80  {
81  err_user_supplied_eval (e, "lsode");
82  }
83 
84  if (tmp.empty () || ! tmp(0).is_defined ())
85  err_user_supplied_eval ("lsode");
86 
87  if (! warned_fcn_imaginary && tmp(0).is_complex_type ())
88  {
89  warning ("lsode: ignoring imaginary part returned from user-supplied function");
90  warned_fcn_imaginary = true;
91  }
92 
93  retval = tmp(0).xvector_value ("lsode: expecting user supplied function to return numeric vector");
94 
95  if (retval.is_empty ())
96  err_user_supplied_eval ("lsode");
97  }
98 
99  return retval;
100 }
101 
102 Matrix
104 {
105  Matrix retval;
106 
108  args(1) = t;
109  args(0) = x;
110 
111  if (lsode_jac)
112  {
114 
115  try
116  {
117  tmp = lsode_jac->do_multi_index_op (1, args);
118  }
119  catch (octave::execution_exception& e)
120  {
121  err_user_supplied_eval (e, "lsode");
122  }
123 
124  if (tmp.empty () || ! tmp(0).is_defined ())
125  err_user_supplied_eval ("lsode");
126 
127  if (! warned_jac_imaginary && tmp(0).is_complex_type ())
128  {
129  warning ("lsode: ignoring imaginary part returned from user-supplied jacobian function");
130  warned_jac_imaginary = true;
131  }
132 
133  retval = tmp(0).xmatrix_value ("lsode: expecting user supplied jacobian function to return numeric array");
134 
135  if (retval.is_empty ())
136  err_user_supplied_eval ("lsode");
137  }
138 
139  return retval;
140 }
141 
142 DEFUN (lsode, args, nargout,
143  doc: /* -*- texinfo -*-
144 @deftypefn {} {[@var{x}, @var{istate}, @var{msg}] =} lsode (@var{fcn}, @var{x_0}, @var{t})
145 @deftypefnx {} {[@var{x}, @var{istate}, @var{msg}] =} lsode (@var{fcn}, @var{x_0}, @var{t}, @var{t_crit})
146 Ordinary Differential Equation (ODE) solver.
147 
148 The set of differential equations to solve is
149 @tex
150 $$ {dx \over dt} = f (x, t) $$
151 with
152 $$ x(t_0) = x_0 $$
153 @end tex
154 @ifnottex
155 
156 @example
157 @group
158 dx
159 -- = f (x, t)
160 dt
161 @end group
162 @end example
163 
164 @noindent
165 with
166 
167 @example
168 x(t_0) = x_0
169 @end example
170 
171 @end ifnottex
172 The solution is returned in the matrix @var{x}, with each row
173 corresponding to an element of the vector @var{t}. The first element
174 of @var{t} should be @math{t_0} and should correspond to the initial
175 state of the system @var{x_0}, so that the first row of the output
176 is @var{x_0}.
177 
178 The first argument, @var{fcn}, is a string, inline, or function handle
179 that names the function @math{f} to call to compute the vector of right
180 hand sides for the set of equations. The function must have the form
181 
182 @example
183 @var{xdot} = f (@var{x}, @var{t})
184 @end example
185 
186 @noindent
187 in which @var{xdot} and @var{x} are vectors and @var{t} is a scalar.
188 
189 If @var{fcn} is a two-element string array or a two-element cell array
190 of strings, inline functions, or function handles, the first element names
191 the function @math{f} described above, and the second element names a
192 function to compute the Jacobian of @math{f}. The Jacobian function
193 must have the form
194 
195 @example
196 @var{jac} = j (@var{x}, @var{t})
197 @end example
198 
199 @noindent
200 in which @var{jac} is the matrix of partial derivatives
201 @tex
202 $$ J = {\partial f_i \over \partial x_j} = \left[\matrix{
203 {\partial f_1 \over \partial x_1}
204  & {\partial f_1 \over \partial x_2}
205  & \cdots
206  & {\partial f_1 \over \partial x_N} \cr
207 {\partial f_2 \over \partial x_1}
208  & {\partial f_2 \over \partial x_2}
209  & \cdots
210  & {\partial f_2 \over \partial x_N} \cr
211  \vdots & \vdots & \ddots & \vdots \cr
212 {\partial f_3 \over \partial x_1}
213  & {\partial f_3 \over \partial x_2}
214  & \cdots
215  & {\partial f_3 \over \partial x_N} \cr}\right]$$
216 @end tex
217 @ifnottex
218 
219 @example
220 @group
221  | df_1 df_1 df_1 |
222  | ---- ---- ... ---- |
223  | dx_1 dx_2 dx_N |
224  | |
225  | df_2 df_2 df_2 |
226  | ---- ---- ... ---- |
227  df_i | dx_1 dx_2 dx_N |
228 jac = ---- = | |
229  dx_j | . . . . |
230  | . . . . |
231  | . . . . |
232  | |
233  | df_N df_N df_N |
234  | ---- ---- ... ---- |
235  | dx_1 dx_2 dx_N |
236 @end group
237 @end example
238 
239 @end ifnottex
240 
241 The second and third arguments specify the initial state of the system,
242 @math{x_0}, and the initial value of the independent variable @math{t_0}.
243 
244 The fourth argument is optional, and may be used to specify a set of
245 times that the ODE solver should not integrate past. It is useful for
246 avoiding difficulties with singularities and points where there is a
247 discontinuity in the derivative.
248 
249 After a successful computation, the value of @var{istate} will be 2
250 (consistent with the Fortran version of @sc{lsode}).
251 
252 If the computation is not successful, @var{istate} will be something
253 other than 2 and @var{msg} will contain additional information.
254 
255 You can use the function @code{lsode_options} to set optional
256 parameters for @code{lsode}.
257 @seealso{daspk, dassl, dasrt}
258 @end deftypefn */)
259 {
260  int nargin = args.length ();
261 
262  if (nargin < 3 || nargin > 4)
263  print_usage ();
264 
265  warned_fcn_imaginary = false;
266  warned_jac_imaginary = false;
267 
269 
270  frame.protect_var (call_depth);
271  call_depth++;
272 
273  if (call_depth > 1)
274  error ("lsode: invalid recursive call");
275 
276  std::string fcn_name, fname, jac_name, jname;
277  lsode_fcn = 0;
278  lsode_jac = 0;
279 
280  octave_value f_arg = args(0);
281 
282  if (f_arg.is_cell ())
283  {
284  Cell c = f_arg.cell_value ();
285  if (c.numel () == 1)
286  f_arg = c(0);
287  else if (c.numel () == 2)
288  {
289  if (c(0).is_function_handle () || c(0).is_inline_function ())
290  lsode_fcn = c(0).function_value ();
291  else
292  {
293  fcn_name = unique_symbol_name ("__lsode_fcn__");
294  fname = "function y = ";
295  fname.append (fcn_name);
296  fname.append (" (x, t) y = ");
297  lsode_fcn = extract_function (c(0), "lsode", fcn_name, fname,
298  "; endfunction");
299  }
300 
301  if (lsode_fcn)
302  {
303  if (c(1).is_function_handle () || c(1).is_inline_function ())
304  lsode_jac = c(1).function_value ();
305  else
306  {
307  jac_name = unique_symbol_name ("__lsode_jac__");
308  jname = "function jac = ";
309  jname.append (jac_name);
310  jname.append (" (x, t) jac = ");
311  lsode_jac = extract_function (c(1), "lsode", jac_name,
312  jname, "; endfunction");
313 
314  if (! lsode_jac)
315  {
316  if (fcn_name.length ())
317  clear_function (fcn_name);
318  lsode_fcn = 0;
319  }
320  }
321  }
322  }
323  else
324  error ("lsode: incorrect number of elements in cell array");
325  }
326 
327  if (! lsode_fcn && ! f_arg.is_cell ())
328  {
329  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
330  lsode_fcn = f_arg.function_value ();
331  else
332  {
333  switch (f_arg.rows ())
334  {
335  case 1:
336  do
337  {
338  fcn_name = unique_symbol_name ("__lsode_fcn__");
339  fname = "function y = ";
340  fname.append (fcn_name);
341  fname.append (" (x, t) y = ");
342  lsode_fcn = extract_function (f_arg, "lsode", fcn_name,
343  fname, "; endfunction");
344  }
345  while (0);
346  break;
347 
348  case 2:
349  {
351 
352  fcn_name = unique_symbol_name ("__lsode_fcn__");
353  fname = "function y = ";
354  fname.append (fcn_name);
355  fname.append (" (x, t) y = ");
356  lsode_fcn = extract_function (tmp(0), "lsode", fcn_name,
357  fname, "; endfunction");
358 
359  if (lsode_fcn)
360  {
361  jac_name = unique_symbol_name ("__lsode_jac__");
362  jname = "function jac = ";
363  jname.append (jac_name);
364  jname.append (" (x, t) jac = ");
365  lsode_jac = extract_function (tmp(1), "lsode",
366  jac_name, jname,
367  "; endfunction");
368 
369  if (! lsode_jac)
370  {
371  if (fcn_name.length ())
372  clear_function (fcn_name);
373  lsode_fcn = 0;
374  }
375  }
376  }
377  break;
378 
379  default:
380  error ("lsode: first arg should be a string or 2-element string array");
381  }
382  }
383  }
384 
385  if (! lsode_fcn)
386  error ("lsode: FCN argument is not a valid function name or handle");
387 
388  ColumnVector state = args(1).xvector_value ("lsode: initial state X_0 must be a vector");
389  ColumnVector out_times = args(2).xvector_value ("lsode: output time variable T must be a vector");
390 
391  ColumnVector crit_times;
392 
393  int crit_times_set = 0;
394  if (nargin > 3)
395  {
396  crit_times = args(3).xvector_value ("lsode: list of critical times T_CRIT must be a vector");
397 
398  crit_times_set = 1;
399  }
400 
401  double tzero = out_times (0);
402 
404  if (lsode_jac)
406 
407  LSODE ode (state, tzero, func);
408 
409  ode.set_options (lsode_opts);
410 
411  Matrix output;
412  if (crit_times_set)
413  output = ode.integrate (out_times, crit_times);
414  else
415  output = ode.integrate (out_times);
416 
417  if (fcn_name.length ())
418  clear_function (fcn_name);
419  if (jac_name.length ())
420  clear_function (jac_name);
421 
422  std::string msg = ode.error_message ();
423 
425 
426  if (ode.integration_ok ())
427  retval(0) = output;
428  else if (nargout < 2)
429  error ("lsode: %s", msg.c_str ());
430  else
431  retval(0) = Matrix ();
432 
433  retval(1) = static_cast<double> (ode.integration_state ());
434  retval(2) = msg;
435 
436  return retval;
437 }
438 
439 /*
440 
441 ## dassl-1.m
442 ##
443 ## Test lsode() function
444 ##
445 ## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
446 ## Comalco Research and Technology
447 ## 20 May 1998
448 ##
449 ## Problem
450 ##
451 ## y1' = -y2, y1(0) = 1
452 ## y2' = y1, y2(0) = 0
453 ##
454 ## Solution
455 ##
456 ## y1(t) = cos(t)
457 ## y2(t) = sin(t)
458 ##
459 %!function xdot = __f (x, t)
460 %! xdot = [-x(2); x(1)];
461 %!endfunction
462 %!test
463 %!
464 %! x0 = [1; 0];
465 %! xdot0 = [0; 1];
466 %! t = (0:1:10)';
467 %!
468 %! tol = 500 * lsode_options ("relative tolerance");
469 %!
470 %! x = lsode ("__f", x0, t);
471 %!
472 %! y = [cos(t), sin(t)];
473 %!
474 %! assert (x, y, tol);
475 
476 %!function xdotdot = __f (x, t)
477 %! xdotdot = [x(2); -x(1)];
478 %!endfunction
479 %!test
480 %!
481 %! x0 = [1; 0];
482 %! t = [0; 2*pi];
483 %! tol = 100 * dassl_options ("relative tolerance");
484 %!
485 %! x = lsode ("__f", x0, t);
486 %!
487 %! y = [1, 0; 1, 0];
488 %!
489 %! assert (x, y, tol);
490 
491 %!function xdot = __f (x, t)
492 %! xdot = x;
493 %!endfunction
494 %!test
495 %!
496 %! x0 = 1;
497 %! t = [0; 1];
498 %! tol = 100 * dassl_options ("relative tolerance");
499 %!
500 %! x = lsode ("__f", x0, t);
501 %!
502 %! y = [1; e];
503 %!
504 %! assert (x, y, tol);
505 
506 %!test
507 %! lsode_options ("absolute tolerance", eps);
508 %! assert (lsode_options ("absolute tolerance") == eps);
509 
510 %!error lsode_options ("foo", 1, 2)
511 */
static int call_depth
Definition: lsode.cc:60
static bool warned_jac_imaginary
Definition: lsode.cc:57
bool is_empty(void) const
Definition: Array.h:575
Definition: Cell.h:37
fname
Definition: load-save.cc:754
virtual octave_value_list do_multi_index_op(int nargout, const octave_value_list &idx)
Definition: ov-base.cc:259
octave_idx_type rows(void) const
Definition: ov.h:489
bool integration_ok(void) const
Definition: base-de.h:98
OCTINTERP_API void print_usage(void)
Definition: defun.cc:52
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:363
Matrix lsode_user_jacobian(const ColumnVector &x, double t)
Definition: lsode.cc:103
virtual ColumnVector integrate(double tt)
Definition: ODE.h:75
void protect_var(T &var)
static bool warned_fcn_imaginary
Definition: lsode.cc:56
void error(const char *fmt,...)
Definition: error.cc:570
bool is_cell(void) const
Definition: ov.h:545
ColumnVector lsode_user_function(const ColumnVector &x, double t)
Definition: lsode.cc:63
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:935
Definition: LSODE.h:32
i e
Definition: data.cc:2724
void set_options(const LSODE_options &opt)
Definition: LSODE-opts.h:78
bool is_function_handle(void) const
Definition: ov.h:702
std::string unique_symbol_name(const std::string &basename)
Definition: variables.cc:491
Cell cell_value(void) const
Definition: ov.cc:1687
void clear_function(const std::string &nm)
Definition: variables.cc:79
JNIEnv void * args
Definition: ov-java.cc:67
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:935
int nargin
Definition: graphics.cc:10115
static octave_function * lsode_jac
Definition: lsode.cc:53
bool is_inline_function(void) const
Definition: ov.h:708
double tmp
Definition: data.cc:6300
octave_value retval
Definition: data.cc:6294
Definition: dMatrix.h:37
the sparsity preserving column transformation such that that defines the pivoting threshold can be given in which case it defines the c
Definition: lu.cc:138
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:911
static uint32_t state[624]
Definition: randmtzig.cc:184
octave_function * function_value(bool silent=false) const
Definition: ov.cc:1705
DEFUN(lsode, args, nargout, doc:)
Definition: lsode.cc:142
void warning(const char *fmt,...)
Definition: error.cc:788
static octave_function * lsode_fcn
Definition: lsode.cc:50
octave::unwind_protect frame
Definition: graphics.cc:11584
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:142
bool empty(void) const
Definition: ovl.h:98
static LSODE_options lsode_opts
Definition: LSODE-opts.cc:20
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:145
octave_idx_type integration_state(void) const
Definition: base-de.h:100
nd group nd example Use ode
Definition: cellfun.cc:398
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:854
F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &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 F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T F77_REAL F77_REAL &F77_RET_T F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE * x
std::string error_message(void) const
Definition: LSODE.cc:318