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