GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
dassl.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 <string>
31
32#include "DASSL.h"
33
34#include "defun.h"
35#include "error.h"
36#include "errwarn.h"
37#include "interpreter-private.h"
38#include "interpreter.h"
39#include "ovl.h"
40#include "ov-fcn.h"
41#include "ov-cell.h"
42#include "pager.h"
43#include "unwind-prot.h"
44#include "utils.h"
45#include "variables.h"
46
47#include "DASSL-opts.cc"
48
50
51// Global pointer for user defined function required by dassl.
52static octave_value dassl_fcn;
53
54// Global pointer for optional user defined jacobian function.
55static octave_value dassl_jac;
56
57// Have we warned about imaginary values returned from user function?
58static bool warned_fcn_imaginary = false;
59static bool warned_jac_imaginary = false;
60
61// Is this a recursive call?
62static int call_depth = 0;
63
64static ColumnVector
65dassl_user_function (const ColumnVector& x, const ColumnVector& xdot,
66 double t, octave_idx_type& ires)
67{
68 ColumnVector retval;
69
70 panic_unless (x.numel () == xdot.numel ());
71
73
74 args(2) = t;
75 args(1) = xdot;
76 args(0) = x;
77
78 if (dassl_fcn.is_defined ())
79 {
81
82 try
83 {
85
86 tmp = interp.feval (dassl_fcn, args, 1);
87 }
88 catch (execution_exception& ee)
89 {
90 err_user_supplied_eval (ee, "dassl");
91 }
92
93 int tlen = tmp.length ();
94 if (tlen == 0 || ! tmp(0).is_defined ())
95 err_user_supplied_eval ("dassl");
96
97 if (! warned_fcn_imaginary && tmp(0).iscomplex ())
98 {
99 warning ("dassl: ignoring imaginary part returned from user-supplied function");
100 warned_fcn_imaginary = true;
101 }
102
103 retval = tmp(0).vector_value ();
104
105 if (tlen > 1)
106 ires = tmp(1).int_value ();
107
108 if (retval.isempty ())
109 err_user_supplied_eval ("dassl");
110 }
111
112 return retval;
113}
114
115static Matrix
116dassl_user_jacobian (const ColumnVector& x, const ColumnVector& xdot,
117 double t, double cj)
118{
119 Matrix retval;
120
121 panic_unless (x.numel () == xdot.numel ());
122
124
125 args(3) = cj;
126 args(2) = t;
127 args(1) = xdot;
128 args(0) = x;
129
130 if (dassl_jac.is_defined ())
131 {
133
134 try
135 {
136 interpreter& interp = __get_interpreter__ ();
137
138 tmp = interp.feval (dassl_jac, args, 1);
139 }
140 catch (execution_exception& ee)
141 {
142 err_user_supplied_eval (ee, "dassl");
143 }
144
145 int tlen = tmp.length ();
146 if (tlen == 0 || ! tmp(0).is_defined ())
147 err_user_supplied_eval ("dassl");
148
149 if (! warned_jac_imaginary && tmp(0).iscomplex ())
150 {
151 warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
152 warned_jac_imaginary = true;
153 }
154
155 retval = tmp(0).matrix_value ();
156
157 if (retval.isempty ())
158 err_user_supplied_eval ("dassl");
159 }
160
161 return retval;
162}
163
164DEFMETHOD (dassl, interp, args, nargout,
165 doc: /* -*- texinfo -*-
166@deftypefn {} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} dassl (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
167Solve a set of differential-algebraic equations.
168
169@code{dassl} solves the set of equations
170@tex
171$$ 0 = f (x, \dot{x}, t) $$
172with
173$$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
174@end tex
175@ifnottex
176
177@example
1780 = f (x, xdot, t)
179@end example
180
181@noindent
182with
183
184@example
185x(t_0) = x_0, xdot(t_0) = xdot_0
186@end example
187
188@end ifnottex
189The solution is returned in the matrices @var{x} and @var{xdot},
190with each row in the result matrices corresponding to one of the
191elements in the vector @var{t}. The first element of @var{t}
192should be @math{t_0} and correspond to the initial state of the
193system @var{x_0} and its derivative @var{xdot_0}, so that the first
194row of the output @var{x} is @var{x_0} and the first row
195of the output @var{xdot} is @var{xdot_0}.
196
197The first argument, @var{fcn}, is a string, inline, or function handle
198that names the function @math{f} to call to compute the vector of
199residuals for the set of equations. It must have the form
200
201@example
202@var{res} = f (@var{x}, @var{xdot}, @var{t})
203@end example
204
205@noindent
206in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
207scalar.
208
209If @var{fcn} is a two-element string array or a two-element cell array
210of strings, inline functions, or function handles, the first element names
211the function @math{f} described above, and the second element names a
212function to compute the modified Jacobian
213
214@tex
215$$
216J = {\partial f \over \partial x}
217 + c {\partial f \over \partial \dot{x}}
218$$
219@end tex
220@ifnottex
221
222@example
223@group
224 df df
225jac = -- + c ------
226 dx d xdot
227@end group
228@end example
229
230@end ifnottex
231
232The modified Jacobian function must have the form
233
234@example
235@group
236
237@var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
238
239@end group
240@end example
241
242The second and third arguments to @code{dassl} specify the initial
243condition of the states and their derivatives, and the fourth argument
244specifies a vector of output times at which the solution is desired,
245including the time corresponding to the initial condition.
246
247The set of initial states and derivatives are not strictly required to
248be consistent. In practice, however, @sc{dassl} is not very good at
249determining a consistent set for you, so it is best if you ensure that
250the initial values result in the function evaluating to zero.
251
252The fifth argument is optional, and may be used to specify a set of
253times that the DAE solver should not integrate past. It is useful for
254avoiding difficulties with singularities and points where there is a
255discontinuity in the derivative.
256
257After a successful computation, the value of @var{istate} will be
258greater than zero (consistent with the Fortran version of @sc{dassl}).
259
260If the computation is not successful, the value of @var{istate} will be
261less than zero and @var{msg} will contain additional information.
262
263You can use the function @code{dassl_options} to set optional
264parameters for @code{dassl}.
265@seealso{daspk, dasrt, lsode}
266@end deftypefn */)
267{
268 int nargin = args.length ();
269
270 if (nargin < 4 || nargin > 5)
271 print_usage ();
272
273 warned_fcn_imaginary = false;
274 warned_jac_imaginary = false;
275
276 octave_value_list retval (4);
277
278 unwind_protect_var<int> restore_var (call_depth);
279 call_depth++;
280
281 if (call_depth > 1)
282 error ("dassl: invalid recursive call");
283
284 std::string fcn_name, fname, jac_name, jname;
285
286 dassl_fcn = octave_value ();
287 dassl_jac = octave_value ();
288
289 octave_value f_arg = args(0);
290
291 std::list<std::string> fcn_param_names ({"x", "xdot", "t"});
292 std::list<std::string> jac_param_names ({"x", "xdot", "t", "cj"});
293
294 if (f_arg.iscell ())
295 {
296 Cell c = f_arg.cell_value ();
297 if (c.numel () == 1)
298 f_arg = c(0);
299 else if (c.numel () == 2)
300 {
301 dassl_fcn = get_function_handle (interp, c(0), fcn_param_names);
302
303 if (dassl_fcn.is_defined ())
304 {
305 dassl_jac = get_function_handle (interp, c(1), jac_param_names);
306
307 if (dassl_jac.is_undefined ())
308 dassl_fcn = octave_value ();
309 }
310 }
311 else
312 error ("dassl: incorrect number of elements in cell array");
313 }
314
315 if (dassl_fcn.is_undefined () && ! f_arg.iscell ())
316 {
317 if (f_arg.is_function_handle () || f_arg.is_inline_function ())
318 dassl_fcn = f_arg;
319 else
320 {
321 switch (f_arg.rows ())
322 {
323 case 1:
324 dassl_fcn = get_function_handle (interp, f_arg, fcn_param_names);
325 break;
326
327 case 2:
328 {
329 string_vector tmp = f_arg.string_vector_value ();
330
331 dassl_fcn = get_function_handle (interp, tmp(0),
332 fcn_param_names);
333
334 if (dassl_fcn.is_defined ())
335 {
336 dassl_jac = get_function_handle (interp, tmp(1),
337 jac_param_names);
338
339 if (dassl_jac.is_undefined ())
340 dassl_fcn = octave_value ();
341 }
342 }
343 break;
344
345 default:
346 error ("dassl: first arg should be a string or 2-element string array");
347 }
348 }
349 }
350
351 if (dassl_fcn.is_undefined ())
352 error ("dassl: FCN argument is not a valid function name or handle");
353
354 ColumnVector state = args(1).xvector_value ("dassl: initial state X_0 must be a vector");
355
356 ColumnVector deriv = args(2).xvector_value ("dassl: initial derivatives XDOT_0 must be a vector");
357
358 ColumnVector out_times = args(3).xvector_value ("dassl: output time variable T must be a vector");
359
360 ColumnVector crit_times;
361 int crit_times_set = 0;
362 if (nargin > 4)
363 {
364 crit_times = args(4).xvector_value ("dassl: list of critical times T_CRIT must be a vector");
365
366 crit_times_set = 1;
367 }
368
369 if (state.numel () != deriv.numel ())
370 error ("dassl: X and XDOT_0 must have the same size");
371
372 double tzero = out_times (0);
373
374 DAEFunc fcn (dassl_user_function);
375 if (dassl_jac.is_defined ())
376 fcn.set_jacobian_function (dassl_user_jacobian);
377
378 DASSL dae (state, deriv, tzero, fcn);
379
380 dae.set_options (dassl_opts);
381
382 Matrix output;
383 Matrix deriv_output;
384
385 if (crit_times_set)
386 output = dae.integrate (out_times, deriv_output, crit_times);
387 else
388 output = dae.integrate (out_times, deriv_output);
389
390 std::string msg = dae.error_message ();
391
392 if (dae.integration_ok ())
393 {
394 retval(0) = output;
395 retval(1) = deriv_output;
396 }
397 else
398 {
399 if (nargout < 3)
400 error ("dassl: %s", msg.c_str ());
401
402 retval(0) = Matrix ();
403 retval(1) = Matrix ();
404 }
405
406 retval(2) = static_cast<double> (dae.integration_state ());
407 retval(3) = msg;
408
409 return retval;
410}
411
412/*
413## dassl-1.m
414##
415## Test dassl() function
416##
417## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
418## Comalco Research and Technology
419## 20 May 1998
420##
421## Problem
422##
423## y1' = -y2, y1(0) = 1
424## y2' = y1, y2(0) = 0
425##
426## Solution
427##
428## y1(t) = cos(t)
429## y2(t) = sin(t)
430##
431%!function res = __f (x, xdot, t)
432%! res = [xdot(1)+x(2); xdot(2)-x(1)];
433%!endfunction
434
435%!test
436%!
437%! x0 = [1; 0];
438%! xdot0 = [0; 1];
439%! t = (0:1:10)';
440%!
441%! tol = 100 * dassl_options ("relative tolerance");
442%!
443%! [x, xdot] = dassl ("__f", x0, xdot0, t);
444%!
445%! y = [cos(t), sin(t)];
446%!
447%! assert (x, y, tol);
448
449## dassl-2.m
450##
451## Test dassl() function
452##
453## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
454## Comalco Research and Technology
455## 20 May 1998
456##
457## Based on SLATEC quick check for DASSL by Linda Petzold
458##
459## Problem
460##
461## x1' + 10*x1 = 0, x1(0) = 1
462## x1 + x2 = 1, x2(0) = 0
463##
464##
465## Solution
466##
467## x1(t) = exp(-10*t)
468## x2(t) = 1 - x(1)
469##
470%!function res = __f (x, xdot, t)
471%! res = [xdot(1)+10*x(1); x(1)+x(2)-1];
472%!endfunction
473
474%!test
475%!
476%! x0 = [1; 0];
477%! xdot0 = [-10; 10];
478%! t = (0:0.2:1)';
479%!
480%! tol = 500 * dassl_options ("relative tolerance");
481%!
482%! [x, xdot] = dassl ("__f", x0, xdot0, t);
483%!
484%! y = [exp(-10*t), 1-exp(-10*t)];
485%!
486%! assert (x, y, tol);
487
488%!test
489%! old_tol = dassl_options ("absolute tolerance");
490%! dassl_options ("absolute tolerance", eps);
491%! assert (dassl_options ("absolute tolerance") == eps);
492%! ## Restore old value of tolerance
493%! dassl_options ("absolute tolerance", old_tol);
494
495%!error dassl_options ("foo", 1, 2)
496*/
497
498OCTAVE_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
DAEFunc & set_jacobian_function(DAEJacFunc j)
Definition DAEFunc.h:85
Definition DASSL.h:39
std::string error_message() const
Definition DASSL.cc:502
Matrix integrate(const ColumnVector &tout, Matrix &xdot_out)
Definition DASSL.cc:354
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.
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
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value dassl_fcn
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
#define panic_unless(cond)
Definition panic.h:59