GNU Octave  6.2.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-2021 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 
49 // Global pointer for user defined function required by dassl.
51 
52 // Global pointer for optional user defined jacobian function.
54 
55 // Have we warned about imaginary values returned from user function?
56 static bool warned_fcn_imaginary = false;
57 static bool warned_jac_imaginary = false;
58 
59 // Is this a recursive call?
60 static int call_depth = 0;
61 
64  double t, octave_idx_type& ires)
65 {
67 
68  assert (x.numel () == xdot.numel ());
69 
70  octave_value_list args;
71 
72  args(2) = t;
73  args(1) = xdot;
74  args(0) = x;
75 
76  if (dassl_fcn.is_defined ())
77  {
79 
80  try
81  {
82  tmp = octave::feval (dassl_fcn, args, 1);
83  }
84  catch (octave::execution_exception& e)
85  {
86  err_user_supplied_eval (e, "dassl");
87  }
88 
89  int tlen = tmp.length ();
90  if (tlen == 0 || ! tmp(0).is_defined ())
91  err_user_supplied_eval ("dassl");
92 
93  if (! warned_fcn_imaginary && tmp(0).iscomplex ())
94  {
95  warning ("dassl: ignoring imaginary part returned from user-supplied function");
96  warned_fcn_imaginary = true;
97  }
98 
99  retval = tmp(0).vector_value ();
100 
101  if (tlen > 1)
102  ires = tmp(1).int_value ();
103 
104  if (retval.isempty ())
105  err_user_supplied_eval ("dassl");
106  }
107 
108  return retval;
109 }
110 
111 Matrix
113  double t, double cj)
114 {
115  Matrix retval;
116 
117  assert (x.numel () == xdot.numel ());
118 
119  octave_value_list args;
120 
121  args(3) = cj;
122  args(2) = t;
123  args(1) = xdot;
124  args(0) = x;
125 
126  if (dassl_jac.is_defined ())
127  {
128  octave_value_list tmp;
129 
130  try
131  {
132  tmp = octave::feval (dassl_jac, args, 1);
133  }
134  catch (octave::execution_exception& e)
135  {
136  err_user_supplied_eval (e, "dassl");
137  }
138 
139  int tlen = tmp.length ();
140  if (tlen == 0 || ! tmp(0).is_defined ())
141  err_user_supplied_eval ("dassl");
142 
143  if (! warned_jac_imaginary && tmp(0).iscomplex ())
144  {
145  warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
146  warned_jac_imaginary = true;
147  }
148 
149  retval = tmp(0).matrix_value ();
150 
151  if (retval.isempty ())
152  err_user_supplied_eval ("dassl");
153  }
154 
155  return retval;
156 }
157 
158 DEFMETHOD (dassl, interp, args, nargout,
159  doc: /* -*- texinfo -*-
160 @deftypefn {} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} dassl (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
161 Solve a set of differential-algebraic equations.
162 
163 @code{dassl} solves the set of equations
164 @tex
165 $$ 0 = f (x, \dot{x}, t) $$
166 with
167 $$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
168 @end tex
169 @ifnottex
170 
171 @example
172 0 = f (x, xdot, t)
173 @end example
174 
175 @noindent
176 with
177 
178 @example
179 x(t_0) = x_0, xdot(t_0) = xdot_0
180 @end example
181 
182 @end ifnottex
183 The solution is returned in the matrices @var{x} and @var{xdot},
184 with each row in the result matrices corresponding to one of the
185 elements in the vector @var{t}. The first element of @var{t}
186 should be @math{t_0} and correspond to the initial state of the
187 system @var{x_0} and its derivative @var{xdot_0}, so that the first
188 row of the output @var{x} is @var{x_0} and the first row
189 of the output @var{xdot} is @var{xdot_0}.
190 
191 The first argument, @var{fcn}, is a string, inline, or function handle
192 that names the function @math{f} to call to compute the vector of
193 residuals for the set of equations. It must have the form
194 
195 @example
196 @var{res} = f (@var{x}, @var{xdot}, @var{t})
197 @end example
198 
199 @noindent
200 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
201 scalar.
202 
203 If @var{fcn} is a two-element string array or a two-element cell array
204 of strings, inline functions, or function handles, the first element names
205 the function @math{f} described above, and the second element names a
206 function to compute the modified Jacobian
207 
208 @tex
209 $$
210 J = {\partial f \over \partial x}
211  + c {\partial f \over \partial \dot{x}}
212 $$
213 @end tex
214 @ifnottex
215 
216 @example
217 @group
218  df df
219 jac = -- + c ------
220  dx d xdot
221 @end group
222 @end example
223 
224 @end ifnottex
225 
226 The modified Jacobian function must have the form
227 
228 @example
229 @group
230 
231 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
232 
233 @end group
234 @end example
235 
236 The second and third arguments to @code{dassl} specify the initial
237 condition of the states and their derivatives, and the fourth argument
238 specifies a vector of output times at which the solution is desired,
239 including the time corresponding to the initial condition.
240 
241 The set of initial states and derivatives are not strictly required to
242 be consistent. In practice, however, @sc{dassl} is not very good at
243 determining a consistent set for you, so it is best if you ensure that
244 the initial values result in the function evaluating to zero.
245 
246 The fifth argument is optional, and may be used to specify a set of
247 times that the DAE solver should not integrate past. It is useful for
248 avoiding difficulties with singularities and points where there is a
249 discontinuity in the derivative.
250 
251 After a successful computation, the value of @var{istate} will be
252 greater than zero (consistent with the Fortran version of @sc{dassl}).
253 
254 If the computation is not successful, the value of @var{istate} will be
255 less than zero and @var{msg} will contain additional information.
256 
257 You can use the function @code{dassl_options} to set optional
258 parameters for @code{dassl}.
259 @seealso{daspk, dasrt, lsode}
260 @end deftypefn */)
261 {
262  int nargin = args.length ();
263 
264  if (nargin < 4 || nargin > 5)
265  print_usage ();
266 
267  warned_fcn_imaginary = false;
268  warned_jac_imaginary = false;
269 
271 
273 
274  frame.protect_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 
282  dassl_fcn = octave_value ();
283  dassl_jac = octave_value ();
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 = octave::get_function_handle (interp, c(0),
298  fcn_param_names);
299 
300  if (dassl_fcn.is_defined ())
301  {
302  dassl_jac = octave::get_function_handle (interp, c(1),
303  jac_param_names);
304 
305  if (dassl_jac.is_undefined ())
306  dassl_fcn = octave_value ();
307  }
308  }
309  else
310  error ("dassl: incorrect number of elements in cell array");
311  }
312 
313  if (dassl_fcn.is_undefined () && ! f_arg.iscell ())
314  {
315  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
316  dassl_fcn = f_arg;
317  else
318  {
319  switch (f_arg.rows ())
320  {
321  case 1:
322  dassl_fcn = octave::get_function_handle (interp, f_arg,
323  fcn_param_names);
324  break;
325 
326  case 2:
327  {
328  string_vector tmp = f_arg.string_vector_value ();
329 
330  dassl_fcn = octave::get_function_handle (interp, tmp(0),
331  fcn_param_names);
332 
333  if (dassl_fcn.is_defined ())
334  {
335  dassl_jac = octave::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 
374  if (dassl_jac.is_defined ())
376 
377  DASSL dae (state, deriv, tzero, func);
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 */
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:377
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
Definition: Cell.h:43
DAEFunc & set_jacobian_function(DAEJacFunc j)
Definition: DAEFunc.h:87
Definition: DASSL.h:41
std::string error_message(void) const
Definition: DASSL.cc:500
Matrix integrate(const ColumnVector &tout, Matrix &xdot_out)
Definition: DASSL.cc:352
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:560
octave_idx_type rows(void) const
Definition: ov.h:504
bool is_defined(void) const
Definition: ov.h:551
Cell cell_value(void) const
bool is_function_handle(void) const
Definition: ov.h:721
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:930
bool is_undefined(void) const
Definition: ov.h:554
bool is_inline_function(void) const
Definition: ov.h:727
static int call_depth
Definition: dassl.cc:60
ColumnVector dassl_user_function(const ColumnVector &x, const ColumnVector &xdot, double t, octave_idx_type &ires)
Definition: dassl.cc:63
Matrix dassl_user_jacobian(const ColumnVector &x, const ColumnVector &xdot, double t, double cj)
Definition: dassl.cc:112
static octave_value dassl_jac
Definition: dassl.cc:53
static octave_value dassl_fcn
Definition: dassl.cc:50
DEFMETHOD(dassl, interp, args, nargout, doc:)
Definition: dassl.cc:158
static bool warned_jac_imaginary
Definition: dassl.cc:57
static bool warned_fcn_imaginary
Definition: dassl.cc:56
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
void warning(const char *fmt,...)
Definition: error.cc:1050
void error(const char *fmt,...)
Definition: error.cc:968
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:152
F77_RET_T const F77_DBLE * x
static uint32_t state[624]
Definition: randmtzig.cc:190
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:9580
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811