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