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