GNU Octave  4.4.1
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-2018 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <string>
28 
29 #include <iomanip>
30 #include <iostream>
31 
32 #include "DASSL.h"
33 
34 #include "defun.h"
35 #include "error.h"
36 #include "errwarn.h"
37 #include "ovl.h"
38 #include "ov-fcn.h"
39 #include "ov-cell.h"
40 #include "pager.h"
41 #include "parse.h"
42 #include "unwind-prot.h"
43 #include "utils.h"
44 #include "variables.h"
45 
46 #include "DASSL-opts.cc"
47 
48 // Global pointer for user defined function required by dassl.
50 
51 // Global pointer for optional user defined jacobian function.
53 
54 // Have we warned about imaginary values returned from user function?
55 static bool warned_fcn_imaginary = false;
56 static bool warned_jac_imaginary = false;
57 
58 // Is this a recursive call?
59 static int call_depth = 0;
60 
63  double t, octave_idx_type& ires)
64 {
66 
67  assert (x.numel () == xdot.numel ());
68 
69  octave_value_list args;
70 
71  args(2) = t;
72  args(1) = xdot;
73  args(0) = x;
74 
75  if (dassl_fcn)
76  {
78 
79  try
80  {
81  tmp = octave::feval (dassl_fcn, args, 1);
82  }
83  catch (octave::execution_exception& e)
84  {
85  err_user_supplied_eval (e, "dassl");
86  }
87 
88  int tlen = tmp.length ();
89  if (tlen == 0 || ! tmp(0).is_defined ())
90  err_user_supplied_eval ("dassl");
91 
92  if (! warned_fcn_imaginary && tmp(0).iscomplex ())
93  {
94  warning ("dassl: ignoring imaginary part returned from user-supplied function");
95  warned_fcn_imaginary = true;
96  }
97 
98  retval = tmp(0).vector_value ();
99 
100  if (tlen > 1)
101  ires = tmp(1).int_value ();
102 
103  if (retval.isempty ())
104  err_user_supplied_eval ("dassl");
105  }
106 
107  return retval;
108 }
109 
110 Matrix
112  double t, double cj)
113 {
114  Matrix retval;
115 
116  assert (x.numel () == xdot.numel ());
117 
118  octave_value_list args;
119 
120  args(3) = cj;
121  args(2) = t;
122  args(1) = xdot;
123  args(0) = x;
124 
125  if (dassl_jac)
126  {
128 
129  try
130  {
131  tmp = octave::feval (dassl_jac, args, 1);
132  }
133  catch (octave::execution_exception& e)
134  {
135  err_user_supplied_eval (e, "dassl");
136  }
137 
138  int tlen = tmp.length ();
139  if (tlen == 0 || ! tmp(0).is_defined ())
140  err_user_supplied_eval ("dassl");
141 
142  if (! warned_jac_imaginary && tmp(0).iscomplex ())
143  {
144  warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
145  warned_jac_imaginary = true;
146  }
147 
148  retval = tmp(0).matrix_value ();
149 
150  if (retval.isempty ())
151  err_user_supplied_eval ("dassl");
152  }
153 
154  return retval;
155 }
156 
157 DEFMETHOD (dassl, interp, args, nargout,
158  doc: /* -*- texinfo -*-
159 @deftypefn {} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} dassl (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
160 Solve a set of differential-algebraic equations.
161 
162 @code{dassl} solves the set of equations
163 @tex
164 $$ 0 = f (x, \dot{x}, t) $$
165 with
166 $$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
167 @end tex
168 @ifnottex
169 
170 @example
171 0 = f (x, xdot, t)
172 @end example
173 
174 @noindent
175 with
176 
177 @example
178 x(t_0) = x_0, xdot(t_0) = xdot_0
179 @end example
180 
181 @end ifnottex
182 The solution is returned in the matrices @var{x} and @var{xdot},
183 with each row in the result matrices corresponding to one of the
184 elements in the vector @var{t}. The first element of @var{t}
185 should be @math{t_0} and correspond to the initial state of the
186 system @var{x_0} and its derivative @var{xdot_0}, so that the first
187 row of the output @var{x} is @var{x_0} and the first row
188 of the output @var{xdot} is @var{xdot_0}.
189 
190 The first argument, @var{fcn}, is a string, inline, or function handle
191 that names the function @math{f} to call to compute the vector of
192 residuals for the set of equations. It must have the form
193 
194 @example
195 @var{res} = f (@var{x}, @var{xdot}, @var{t})
196 @end example
197 
198 @noindent
199 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
200 scalar.
201 
202 If @var{fcn} is a two-element string array or a two-element cell array
203 of strings, inline functions, or function handles, the first element names
204 the function @math{f} described above, and the second element names a
205 function to compute the modified Jacobian
206 
207 @tex
208 $$
209 J = {\partial f \over \partial x}
210  + c {\partial f \over \partial \dot{x}}
211 $$
212 @end tex
213 @ifnottex
214 
215 @example
216 @group
217  df df
218 jac = -- + c ------
219  dx d xdot
220 @end group
221 @end example
222 
223 @end ifnottex
224 
225 The modified Jacobian function must have the form
226 
227 @example
228 @group
229 
230 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
231 
232 @end group
233 @end example
234 
235 The second and third arguments to @code{dassl} specify the initial
236 condition of the states and their derivatives, and the fourth argument
237 specifies a vector of output times at which the solution is desired,
238 including the time corresponding to the initial condition.
239 
240 The set of initial states and derivatives are not strictly required to
241 be consistent. In practice, however, @sc{dassl} is not very good at
242 determining a consistent set for you, so it is best if you ensure that
243 the initial values result in the function evaluating to zero.
244 
245 The fifth argument is optional, and may be used to specify a set of
246 times that the DAE solver should not integrate past. It is useful for
247 avoiding difficulties with singularities and points where there is a
248 discontinuity in the derivative.
249 
250 After a successful computation, the value of @var{istate} will be
251 greater than zero (consistent with the Fortran version of @sc{dassl}).
252 
253 If the computation is not successful, the value of @var{istate} will be
254 less than zero and @var{msg} will contain additional information.
255 
256 You can use the function @code{dassl_options} to set optional
257 parameters for @code{dassl}.
258 @seealso{daspk, dasrt, lsode}
259 @end deftypefn */)
260 {
261  int nargin = args.length ();
262 
264  print_usage ();
265 
266  warned_fcn_imaginary = false;
267  warned_jac_imaginary = false;
268 
270 
272 
274  call_depth++;
275 
276  if (call_depth > 1)
277  error ("dassl: invalid recursive call");
278 
279  octave::symbol_table& symtab = interp.get_symbol_table ();
280 
281  std::string fcn_name, fname, jac_name, jname;
282  dassl_fcn = nullptr;
283  dassl_jac = nullptr;
284 
285  octave_value f_arg = args(0);
286 
287  if (f_arg.iscell ())
288  {
289  Cell c = f_arg.cell_value ();
290  if (c.numel () == 1)
291  f_arg = c(0);
292  else if (c.numel () == 2)
293  {
294  if (c(0).is_function_handle () || c(0).is_inline_function ())
295  dassl_fcn = c(0).function_value ();
296  else
297  {
298  fcn_name = unique_symbol_name ("__dassl_fcn__");
299  fname = "function y = ";
300  fname.append (fcn_name);
301  fname.append (" (x, xdot, t) y = ");
302  dassl_fcn = extract_function (c(0), "dassl", fcn_name, fname,
303  "; endfunction");
304  }
305 
306  if (dassl_fcn)
307  {
308  if (c(1).is_function_handle () || c(1).is_inline_function ())
309  dassl_jac = c(1).function_value ();
310  else
311  {
312  jac_name = unique_symbol_name ("__dassl_jac__");
313  jname = "function jac = ";
314  jname.append (jac_name);
315  jname.append (" (x, xdot, t, cj) jac = ");
316  dassl_jac = extract_function (c(1), "dassl", jac_name,
317  jname, "; endfunction");
318 
319  if (! dassl_jac)
320  {
321  if (fcn_name.length ())
322  symtab.clear_function (fcn_name);
323  dassl_fcn = nullptr;
324  }
325  }
326  }
327  }
328  else
329  error ("dassl: incorrect number of elements in cell array");
330  }
331 
332  if (! dassl_fcn && ! f_arg.iscell ())
333  {
334  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
335  dassl_fcn = f_arg.function_value ();
336  else
337  {
338  switch (f_arg.rows ())
339  {
340  case 1:
341  do
342  {
343  fcn_name = unique_symbol_name ("__dassl_fcn__");
344  fname = "function y = ";
345  fname.append (fcn_name);
346  fname.append (" (x, xdot, t) y = ");
347  dassl_fcn = extract_function (f_arg, "dassl", fcn_name,
348  fname, "; endfunction");
349  }
350  while (0);
351  break;
352 
353  case 2:
354  {
356 
357  fcn_name = unique_symbol_name ("__dassl_fcn__");
358  fname = "function y = ";
359  fname.append (fcn_name);
360  fname.append (" (x, xdot, t) y = ");
361  dassl_fcn = extract_function (tmp(0), "dassl", fcn_name,
362  fname, "; endfunction");
363 
364  if (dassl_fcn)
365  {
366  jac_name = unique_symbol_name ("__dassl_jac__");
367  jname = "function jac = ";
368  jname.append (jac_name);
369  jname.append (" (x, xdot, t, cj) jac = ");
370  dassl_jac = extract_function (tmp(1), "dassl",
371  jac_name, jname,
372  "; endfunction");
373 
374  if (! dassl_jac)
375  {
376  if (fcn_name.length ())
377  symtab.clear_function (fcn_name);
378  dassl_fcn = nullptr;
379  }
380  }
381  }
382  }
383  }
384  }
385 
386  if (! dassl_fcn)
387  return retval;
388 
389  ColumnVector state = args(1).xvector_value ("dassl: initial state X_0 must be a vector");
390 
391  ColumnVector deriv = args(2).xvector_value ("dassl: initial derivatives XDOT_0 must be a vector");
392 
393  ColumnVector out_times = args(3).xvector_value ("dassl: output time variable T must be a vector");
394 
395  ColumnVector crit_times;
396  int crit_times_set = 0;
397  if (nargin > 4)
398  {
399  crit_times = args(4).xvector_value ("dassl: list of critical times T_CRIT must be a vector");
400 
401  crit_times_set = 1;
402  }
403 
404  if (state.numel () != deriv.numel ())
405  error ("dassl: X and XDOT_0 must have the same size");
406 
407  double tzero = out_times (0);
408 
410  if (dassl_jac)
412 
413  DASSL dae (state, deriv, tzero, func);
414 
415  dae.set_options (dassl_opts);
416 
417  Matrix output;
418  Matrix deriv_output;
419 
420  if (crit_times_set)
421  output = dae.integrate (out_times, deriv_output, crit_times);
422  else
423  output = dae.integrate (out_times, deriv_output);
424 
425  if (fcn_name.length ())
426  symtab.clear_function (fcn_name);
427  if (jac_name.length ())
428  symtab.clear_function (jac_name);
429 
430  std::string msg = dae.error_message ();
431 
432  if (dae.integration_ok ())
433  {
434  retval(0) = output;
435  retval(1) = deriv_output;
436  }
437  else
438  {
439  if (nargout < 3)
440  error ("dassl: %s", msg.c_str ());
441 
442  retval(0) = Matrix ();
443  retval(1) = Matrix ();
444  }
445 
446  retval(2) = static_cast<double> (dae.integration_state ());
447  retval(3) = msg;
448 
449  return retval;
450 }
451 
452 /*
453 ## dassl-1.m
454 ##
455 ## Test dassl() function
456 ##
457 ## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
458 ## Comalco Research and Technology
459 ## 20 May 1998
460 ##
461 ## Problem
462 ##
463 ## y1' = -y2, y1(0) = 1
464 ## y2' = y1, y2(0) = 0
465 ##
466 ## Solution
467 ##
468 ## y1(t) = cos(t)
469 ## y2(t) = sin(t)
470 ##
471 %!function res = __f (x, xdot, t)
472 %! res = [xdot(1)+x(2); xdot(2)-x(1)];
473 %!endfunction
474 
475 %!test
476 %!
477 %! x0 = [1; 0];
478 %! xdot0 = [0; 1];
479 %! t = (0:1:10)';
480 %!
481 %! tol = 100 * dassl_options ("relative tolerance");
482 %!
483 %! [x, xdot] = dassl ("__f", x0, xdot0, t);
484 %!
485 %! y = [cos(t), sin(t)];
486 %!
487 %! assert (x, y, tol);
488 
489 ## dassl-2.m
490 ##
491 ## Test dassl() function
492 ##
493 ## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
494 ## Comalco Research and Technology
495 ## 20 May 1998
496 ##
497 ## Based on SLATEC quick check for DASSL by Linda Petzold
498 ##
499 ## Problem
500 ##
501 ## x1' + 10*x1 = 0, x1(0) = 1
502 ## x1 + x2 = 1, x2(0) = 0
503 ##
504 ##
505 ## Solution
506 ##
507 ## x1(t) = exp(-10*t)
508 ## x2(t) = 1 - x(1)
509 ##
510 %!function res = __f (x, xdot, t)
511 %! res = [xdot(1)+10*x(1); x(1)+x(2)-1];
512 %!endfunction
513 
514 %!test
515 %!
516 %! x0 = [1; 0];
517 %! xdot0 = [-10; 10];
518 %! t = (0:0.2:1)';
519 %!
520 %! tol = 500 * dassl_options ("relative tolerance");
521 %!
522 %! [x, xdot] = dassl ("__f", x0, xdot0, t);
523 %!
524 %! y = [exp(-10*t), 1-exp(-10*t)];
525 %!
526 %! assert (x, y, tol);
527 
528 %!test
529 %! old_tol = dassl_options ("absolute tolerance");
530 %! dassl_options ("absolute tolerance", eps);
531 %! assert (dassl_options ("absolute tolerance") == eps);
532 %! ## Restore old value of tolerance
533 %! dassl_options ("absolute tolerance", old_tol);
534 
535 %!error dassl_options ("foo", 1, 2)
536 */
OCTINTERP_API octave_value_list feval(const std::string &name, const octave_value_list &args=octave_value_list(), int nargout=0)
static int call_depth
Definition: dassl.cc:59
Definition: Cell.h:37
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:135
fname
Definition: load-save.cc:767
bool isempty(void) const
Definition: ov.h:529
Definition: DASSL.h:35
Matrix integrate(const ColumnVector &tout, Matrix &xdot_out)
Definition: DASSL.cc:356
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
Matrix dassl_user_jacobian(const ColumnVector &x, const ColumnVector &xdot, double t, double cj)
Definition: dassl.cc:111
std::string error_message(void) const
Definition: DASSL.cc:504
void error(const char *fmt,...)
Definition: error.cc:578
octave_idx_type integration_state(void) const
Definition: base-de.h:99
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function t
Definition: ov-usr-fcn.cc:997
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
static octave_function * dassl_jac
Definition: dassl.cc:52
i e
Definition: data.cc:2591
std::string unique_symbol_name(const std::string &basename)
Definition: variables.cc:498
bool integration_ok(void) const
Definition: base-de.h:97
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function xample nargout(@histc)
Definition: ov-usr-fcn.cc:997
bool is_function_handle(void) const
Definition: ov.h:749
static octave_function * dassl_fcn
Definition: dassl.cc:49
bool iscell(void) const
Definition: ov.h:536
octave_idx_type rows(void) const
Definition: ov.h:472
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
octave_function * function_value(bool silent=false) const
Definition: dMatrix.h:36
static uint32_t state[624]
Definition: randmtzig.cc:183
static bool warned_fcn_imaginary
Definition: dassl.cc:55
void warning(const char *fmt,...)
Definition: error.cc:801
octave::unwind_protect frame
Definition: graphics.cc:12190
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:148
octave_function * extract_function(const octave_value &arg, const std::string &warn_for, const std::string &fname, const std::string &header, const std::string &trailer)
Definition: variables.cc:125
ColumnVector dassl_user_function(const ColumnVector &x, const ColumnVector &xdot, double t, octave_idx_type &ires)
Definition: dassl.cc:62
args.length() nargin
Definition: file-io.cc:589
void clear_function(const std::string &name)
Definition: symtab.h:392
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:958
static bool warned_jac_imaginary
Definition: dassl.cc:56
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
Cell cell_value(void) const
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x
bool is_inline_function(void) const
Definition: ov.h:755
DAEFunc & set_jacobian_function(DAEJacFunc j)
Definition: DAEFunc.h:84