GNU Octave 11.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-2026 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@tex
214$$
215J = {\partial f \over \partial x}
216 + c {\partial f \over \partial \dot{x}}
217$$
218@end tex
219@ifnottex
220
221@example
222@group
223 df df
224jac = -- + c ------
225 dx d xdot
226@end group
227@end example
228
229@end ifnottex
230
231The modified Jacobian function must have the form
232
233@example
234@group
235
236@var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
237
238@end group
239@end example
240
241The second and third arguments to @code{dassl} specify the initial
242condition of the states and their derivatives, and the fourth argument
243specifies a vector of output times at which the solution is desired,
244including the time corresponding to the initial condition.
245
246The set of initial states and derivatives are not strictly required to
247be consistent. In practice, however, @sc{dassl} is not very good at
248determining a consistent set for you, so it is best if you ensure that
249the initial values result in the function evaluating to zero.
250
251The fifth argument is optional, and may be used to specify a set of
252times that the DAE solver should not integrate past. It is useful for
253avoiding difficulties with singularities and points where there is a
254discontinuity in the derivative.
255
256After a successful computation, the value of @var{istate} will be
257greater than zero (consistent with the Fortran version of @sc{dassl}).
258
259If the computation is not successful, the value of @var{istate} will be
260less than zero and @var{msg} will contain additional information.
261
262You can use the function @code{dassl_options} to set optional
263parameters for @code{dassl}.
264@seealso{daspk, dasrt, lsode}
265@end deftypefn */)
266{
267 int nargin = args.length ();
268
269 if (nargin < 4 || nargin > 5)
270 print_usage ();
271
272 warned_fcn_imaginary = false;
273 warned_jac_imaginary = false;
274
275 octave_value_list retval (4);
276
277 unwind_protect_var<int> restore_var (call_depth);
278 call_depth++;
279
280 if (call_depth > 1)
281 error ("dassl: invalid recursive call");
282
283 std::string fcn_name, fname, jac_name, jname;
284
285 dassl_fcn = octave_value ();
286 dassl_jac = octave_value ();
287
288 octave_value f_arg = args(0);
289
290 std::list<std::string> fcn_param_names ({"x", "xdot", "t"});
291 std::list<std::string> jac_param_names ({"x", "xdot", "t", "cj"});
292
293 if (f_arg.iscell ())
294 {
295 Cell c = f_arg.cell_value ();
296 if (c.numel () == 1)
297 f_arg = c(0);
298 else if (c.numel () == 2)
299 {
300 dassl_fcn = get_function_handle (interp, c(0), fcn_param_names);
301
302 if (dassl_fcn.is_defined ())
303 {
304 dassl_jac = get_function_handle (interp, c(1), jac_param_names);
305
306 if (dassl_jac.is_undefined ())
307 dassl_fcn = octave_value ();
308 }
309 }
310 else
311 error ("dassl: incorrect number of elements in cell array");
312 }
313
314 if (dassl_fcn.is_undefined () && ! f_arg.iscell ())
315 {
316 if (f_arg.is_function_handle () || f_arg.is_inline_function ())
317 dassl_fcn = f_arg;
318 else
319 {
320 switch (f_arg.rows ())
321 {
322 case 1:
323 dassl_fcn = get_function_handle (interp, f_arg, fcn_param_names);
324 break;
325
326 case 2:
327 {
328 string_vector tmp = f_arg.string_vector_value ();
329
330 dassl_fcn = get_function_handle (interp, tmp(0),
331 fcn_param_names);
332
333 if (dassl_fcn.is_defined ())
334 {
335 dassl_jac = get_function_handle (interp, tmp(1),
336 jac_param_names);
337
338 if (dassl_jac.is_undefined ())
339 dassl_fcn = octave_value ();
340 }
341 }
342 break;
343
344 default:
345 error ("dassl: first arg should be a string or 2-element string array");
346 }
347 }
348 }
349
350 if (dassl_fcn.is_undefined ())
351 error ("dassl: FCN argument is not a valid function name or handle");
352
353 ColumnVector state = args(1).xvector_value ("dassl: initial state X_0 must be a vector");
354
355 ColumnVector deriv = args(2).xvector_value ("dassl: initial derivatives XDOT_0 must be a vector");
356
357 ColumnVector out_times = args(3).xvector_value ("dassl: output time variable T must be a vector");
358
359 ColumnVector crit_times;
360 int crit_times_set = 0;
361 if (nargin > 4)
362 {
363 crit_times = args(4).xvector_value ("dassl: list of critical times T_CRIT must be a vector");
364
365 crit_times_set = 1;
366 }
367
368 if (state.numel () != deriv.numel ())
369 error ("dassl: X and XDOT_0 must have the same size");
370
371 double tzero = out_times (0);
372
373 DAEFunc fcn (dassl_user_function);
374 if (dassl_jac.is_defined ())
375 fcn.set_jacobian_function (dassl_user_jacobian);
376
377 DASSL dae (state, deriv, tzero, fcn);
378
379 dae.set_options (dassl_opts);
380
381 Matrix output;
382 Matrix deriv_output;
383
384 if (crit_times_set)
385 output = dae.integrate (out_times, deriv_output, crit_times);
386 else
387 output = dae.integrate (out_times, deriv_output);
388
389 std::string msg = dae.error_message ();
390
391 if (dae.integration_ok ())
392 {
393 retval(0) = output;
394 retval(1) = deriv_output;
395 }
396 else
397 {
398 if (nargout < 3)
399 error ("dassl: %s", msg.c_str ());
400
401 retval(0) = Matrix ();
402 retval(1) = Matrix ();
403 }
404
405 retval(2) = static_cast<double> (dae.integration_state ());
406 retval(3) = msg;
407
408 return retval;
409}
410
411/*
412## dassl-1.m
413##
414## Test dassl() function
415##
416## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
417## Comalco Research and Technology
418## 20 May 1998
419##
420## Problem
421##
422## y1' = -y2, y1(0) = 1
423## y2' = y1, y2(0) = 0
424##
425## Solution
426##
427## y1(t) = cos(t)
428## y2(t) = sin(t)
429##
430%!function res = __f (x, xdot, t)
431%! res = [xdot(1)+x(2); xdot(2)-x(1)];
432%!endfunction
433
434%!test
435%!
436%! x0 = [1; 0];
437%! xdot0 = [0; 1];
438%! t = (0:1:10)';
439%!
440%! tol = 100 * dassl_options ("relative tolerance");
441%!
442%! [x, xdot] = dassl ("__f", x0, xdot0, t);
443%!
444%! y = [cos(t), sin(t)];
445%!
446%! assert (x, y, tol);
447
448## dassl-2.m
449##
450## Test dassl() function
451##
452## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
453## Comalco Research and Technology
454## 20 May 1998
455##
456## Based on SLATEC quick check for DASSL by Linda Petzold
457##
458## Problem
459##
460## x1' + 10*x1 = 0, x1(0) = 1
461## x1 + x2 = 1, x2(0) = 0
462##
463##
464## Solution
465##
466## x1(t) = exp(-10*t)
467## x2(t) = 1 - x(1)
468##
469%!function res = __f (x, xdot, t)
470%! res = [xdot(1)+10*x(1); x(1)+x(2)-1];
471%!endfunction
472
473%!test
474%!
475%! x0 = [1; 0];
476%! xdot0 = [-10; 10];
477%! t = (0:0.2:1)';
478%!
479%! tol = 500 * dassl_options ("relative tolerance");
480%!
481%! [x, xdot] = dassl ("__f", x0, xdot0, t);
482%!
483%! y = [exp(-10*t), 1-exp(-10*t)];
484%!
485%! assert (x, y, tol);
486
487%!test
488%! old_tol = dassl_options ("absolute tolerance");
489%! dassl_options ("absolute tolerance", eps);
490%! assert (dassl_options ("absolute tolerance") == eps);
491%! ## Restore old value of tolerance
492%! dassl_options ("absolute tolerance", old_tol);
493
494%!error dassl_options ("foo", 1, 2)
495*/
496
497OCTAVE_END_NAMESPACE(octave)
bool isempty() const
Size of the specified dimension.
Definition Array-base.h:674
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
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:766
bool is_undefined() const
Definition ov.h:593
bool is_inline_function() const
Definition ov.h:772
Cell cell_value() const
octave_idx_type rows() const
Definition ov.h:543
bool is_defined() const
Definition ov.h:590
bool iscell() const
Definition ov.h:602
string_vector string_vector_value(bool pad=false) const
Definition ov.h:984
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:1083
void error(const char *fmt,...)
Definition error.cc:1008
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)
#define panic_unless(cond)
Definition panic.h:59
F77_RET_T const F77_DBLE * x