GNU Octave  8.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-2023 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 
50 
51 // Global pointer for user defined function required by dassl.
52 static octave_value dassl_fcn;
53 
54 // Global pointer for optional user defined jacobian function.
56 
57 // Have we warned about imaginary values returned from user function?
58 static bool warned_fcn_imaginary = false;
59 static bool warned_jac_imaginary = false;
60 
61 // Is this a recursive call?
62 static int call_depth = 0;
63 
64 static ColumnVector
66  double t, octave_idx_type& ires)
67 {
68  ColumnVector retval;
69 
70  error_unless (x.numel () == xdot.numel ());
71 
72  octave_value_list args;
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");
98  warned_fcn_imaginary = true;
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 
113 static Matrix
115  double t, double cj)
116 {
117  Matrix retval;
118 
119  error_unless (x.numel () == xdot.numel ());
120 
121  octave_value_list args;
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  {
130  octave_value_list tmp;
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");
148  warned_jac_imaginary = true;
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 
160 DEFMETHOD (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})
163 Solve a set of differential-algebraic equations.
164 
165 @code{dassl} solves the set of equations
166 @tex
167 $$ 0 = f (x, \dot{x}, t) $$
168 with
169 $$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
170 @end tex
171 @ifnottex
172 
173 @example
174 0 = f (x, xdot, t)
175 @end example
176 
177 @noindent
178 with
179 
180 @example
181 x(t_0) = x_0, xdot(t_0) = xdot_0
182 @end example
183 
184 @end ifnottex
185 The solution is returned in the matrices @var{x} and @var{xdot},
186 with each row in the result matrices corresponding to one of the
187 elements in the vector @var{t}. The first element of @var{t}
188 should be @math{t_0} and correspond to the initial state of the
189 system @var{x_0} and its derivative @var{xdot_0}, so that the first
190 row of the output @var{x} is @var{x_0} and the first row
191 of the output @var{xdot} is @var{xdot_0}.
192 
193 The first argument, @var{fcn}, is a string, inline, or function handle
194 that names the function @math{f} to call to compute the vector of
195 residuals 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
202 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
203 scalar.
204 
205 If @var{fcn} is a two-element string array or a two-element cell array
206 of strings, inline functions, or function handles, the first element names
207 the function @math{f} described above, and the second element names a
208 function to compute the modified Jacobian
209 
210 @tex
211 $$
212 J = {\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
221 jac = -- + c ------
222  dx d xdot
223 @end group
224 @end example
225 
226 @end ifnottex
227 
228 The 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 
238 The second and third arguments to @code{dassl} specify the initial
239 condition of the states and their derivatives, and the fourth argument
240 specifies a vector of output times at which the solution is desired,
241 including the time corresponding to the initial condition.
242 
243 The set of initial states and derivatives are not strictly required to
244 be consistent. In practice, however, @sc{dassl} is not very good at
245 determining a consistent set for you, so it is best if you ensure that
246 the initial values result in the function evaluating to zero.
247 
248 The fifth argument is optional, and may be used to specify a set of
249 times that the DAE solver should not integrate past. It is useful for
250 avoiding difficulties with singularities and points where there is a
251 discontinuity in the derivative.
252 
253 After a successful computation, the value of @var{istate} will be
254 greater than zero (consistent with the Fortran version of @sc{dassl}).
255 
256 If the computation is not successful, the value of @var{istate} will be
257 less than zero and @var{msg} will contain additional information.
258 
259 You can use the function @code{dassl_options} to set optional
260 parameters 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 
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 = 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 ())
304  dassl_fcn = octave_value ();
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 ())
336  dassl_fcn = octave_value ();
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, fcn);
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 
OCTAVE_END_NAMESPACE(octave)
static DASSL_options dassl_opts
Definition: DASSL-opts.cc:21
OCTARRAY_OVERRIDABLE_FUNC_API bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:651
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
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_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
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value dassl_fcn
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:1054
void error(const char *fmt,...)
Definition: error.cc:979
void error_unless(bool cond)
Definition: error.h:549
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:152
octave_value get_function_handle(interpreter &interp, const octave_value &arg, const std::string &parameter_name)
F77_RET_T const F77_DBLE * x
class OCTAVE_API Matrix
Definition: mx-fwd.h:31
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:10370
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
static uint32_t state[624]
Definition: randmtzig.cc:193