GNU Octave  4.2.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
dassl.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2017 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 the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 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 <http://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 "unwind-prot.h"
42 #include "utils.h"
43 #include "variables.h"
44 
45 #include "DASSL-opts.cc"
46 
47 // Global pointer for user defined function required by dassl.
49 
50 // Global pointer for optional user defined jacobian function.
52 
53 // Have we warned about imaginary values returned from user function?
54 static bool warned_fcn_imaginary = false;
55 static bool warned_jac_imaginary = false;
56 
57 // Is this a recursive call?
58 static int call_depth = 0;
59 
62  double t, octave_idx_type& ires)
63 {
65 
66  assert (x.numel () == xdot.numel ());
67 
69 
70  args(2) = t;
71  args(1) = xdot;
72  args(0) = x;
73 
74  if (dassl_fcn)
75  {
77 
78  try
79  {
80  tmp = dassl_fcn->do_multi_index_op (1, args);
81  }
82  catch (octave::execution_exception& e)
83  {
84  err_user_supplied_eval (e, "dassl");
85  }
86 
87  int tlen = tmp.length ();
88  if (tlen == 0 || ! tmp(0).is_defined ())
89  err_user_supplied_eval ("dassl");
90 
91  if (! warned_fcn_imaginary && tmp(0).is_complex_type ())
92  {
93  warning ("dassl: ignoring imaginary part returned from user-supplied function");
94  warned_fcn_imaginary = true;
95  }
96 
97  retval = tmp(0).vector_value ();
98 
99  if (tlen > 1)
100  ires = tmp(1).int_value ();
101 
102  if (retval.is_empty ())
103  err_user_supplied_eval ("dassl");
104  }
105 
106  return retval;
107 }
108 
109 Matrix
111  double t, double cj)
112 {
113  Matrix retval;
114 
115  assert (x.numel () == xdot.numel ());
116 
118 
119  args(3) = cj;
120  args(2) = t;
121  args(1) = xdot;
122  args(0) = x;
123 
124  if (dassl_jac)
125  {
127 
128  try
129  {
130  tmp = dassl_jac->do_multi_index_op (1, args);
131  }
132  catch (octave::execution_exception& e)
133  {
134  err_user_supplied_eval (e, "dassl");
135  }
136 
137  int tlen = tmp.length ();
138  if (tlen == 0 || ! tmp(0).is_defined ())
139  err_user_supplied_eval ("dassl");
140 
141  if (! warned_jac_imaginary && tmp(0).is_complex_type ())
142  {
143  warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
144  warned_jac_imaginary = true;
145  }
146 
147  retval = tmp(0).matrix_value ();
148 
149  if (retval.is_empty ())
150  err_user_supplied_eval ("dassl");
151  }
152 
153  return retval;
154 }
155 
156 DEFUN (dassl, args, nargout,
157  doc: /* -*- texinfo -*-
158 @deftypefn {} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} dassl (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
159 Solve the set of differential-algebraic equations
160 @tex
161 $$ 0 = f (x, \dot{x}, t) $$
162 with
163 $$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
164 @end tex
165 @ifnottex
166 
167 @example
168 0 = f (x, xdot, t)
169 @end example
170 
171 @noindent
172 with
173 
174 @example
175 x(t_0) = x_0, xdot(t_0) = xdot_0
176 @end example
177 
178 @end ifnottex
179 The solution is returned in the matrices @var{x} and @var{xdot},
180 with each row in the result matrices corresponding to one of the
181 elements in the vector @var{t}. The first element of @var{t}
182 should be @math{t_0} and correspond to the initial state of the
183 system @var{x_0} and its derivative @var{xdot_0}, so that the first
184 row of the output @var{x} is @var{x_0} and the first row
185 of the output @var{xdot} is @var{xdot_0}.
186 
187 The first argument, @var{fcn}, is a string, inline, or function handle
188 that names the function @math{f} to call to compute the vector of
189 residuals for the set of equations. It must have the form
190 
191 @example
192 @var{res} = f (@var{x}, @var{xdot}, @var{t})
193 @end example
194 
195 @noindent
196 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
197 scalar.
198 
199 If @var{fcn} is a two-element string array or a two-element cell array
200 of strings, inline functions, or function handles, the first element names
201 the function @math{f} described above, and the second element names a
202 function to compute the modified Jacobian
203 
204 @tex
205 $$
206 J = {\partial f \over \partial x}
207  + c {\partial f \over \partial \dot{x}}
208 $$
209 @end tex
210 @ifnottex
211 
212 @example
213 @group
214  df df
215 jac = -- + c ------
216  dx d xdot
217 @end group
218 @end example
219 
220 @end ifnottex
221 
222 The modified Jacobian function must have the form
223 
224 @example
225 @group
226 
227 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
228 
229 @end group
230 @end example
231 
232 The second and third arguments to @code{dassl} specify the initial
233 condition of the states and their derivatives, and the fourth argument
234 specifies a vector of output times at which the solution is desired,
235 including the time corresponding to the initial condition.
236 
237 The set of initial states and derivatives are not strictly required to
238 be consistent. In practice, however, @sc{dassl} is not very good at
239 determining a consistent set for you, so it is best if you ensure that
240 the initial values result in the function evaluating to zero.
241 
242 The fifth argument is optional, and may be used to specify a set of
243 times that the DAE solver should not integrate past. It is useful for
244 avoiding difficulties with singularities and points where there is a
245 discontinuity in the derivative.
246 
247 After a successful computation, the value of @var{istate} will be
248 greater than zero (consistent with the Fortran version of @sc{dassl}).
249 
250 If the computation is not successful, the value of @var{istate} will be
251 less than zero and @var{msg} will contain additional information.
252 
253 You can use the function @code{dassl_options} to set optional
254 parameters for @code{dassl}.
255 @seealso{daspk, dasrt, lsode}
256 @end deftypefn */)
257 {
258  int nargin = args.length ();
259 
260  if (nargin < 4 || nargin > 5)
261  print_usage ();
262 
263  warned_fcn_imaginary = false;
264  warned_jac_imaginary = false;
265 
267 
269 
270  frame.protect_var (call_depth);
271  call_depth++;
272 
273  if (call_depth > 1)
274  error ("dassl: invalid recursive call");
275 
276  std::string fcn_name, fname, jac_name, jname;
277  dassl_fcn = 0;
278  dassl_jac = 0;
279 
280  octave_value f_arg = args(0);
281 
282  if (f_arg.is_cell ())
283  {
284  Cell c = f_arg.cell_value ();
285  if (c.numel () == 1)
286  f_arg = c(0);
287  else if (c.numel () == 2)
288  {
289  if (c(0).is_function_handle () || c(0).is_inline_function ())
290  dassl_fcn = c(0).function_value ();
291  else
292  {
293  fcn_name = unique_symbol_name ("__dassl_fcn__");
294  fname = "function y = ";
295  fname.append (fcn_name);
296  fname.append (" (x, xdot, t) y = ");
297  dassl_fcn = extract_function (c(0), "dassl", fcn_name, fname,
298  "; endfunction");
299  }
300 
301  if (dassl_fcn)
302  {
303  if (c(1).is_function_handle () || c(1).is_inline_function ())
304  dassl_jac = c(1).function_value ();
305  else
306  {
307  jac_name = unique_symbol_name ("__dassl_jac__");
308  jname = "function jac = ";
309  jname.append (jac_name);
310  jname.append (" (x, xdot, t, cj) jac = ");
311  dassl_jac = extract_function (c(1), "dassl", jac_name,
312  jname, "; endfunction");
313 
314  if (! dassl_jac)
315  {
316  if (fcn_name.length ())
317  clear_function (fcn_name);
318  dassl_fcn = 0;
319  }
320  }
321  }
322  }
323  else
324  error ("dassl: incorrect number of elements in cell array");
325  }
326 
327  if (! dassl_fcn && ! f_arg.is_cell ())
328  {
329  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
330  dassl_fcn = f_arg.function_value ();
331  else
332  {
333  switch (f_arg.rows ())
334  {
335  case 1:
336  do
337  {
338  fcn_name = unique_symbol_name ("__dassl_fcn__");
339  fname = "function y = ";
340  fname.append (fcn_name);
341  fname.append (" (x, xdot, t) y = ");
342  dassl_fcn = extract_function (f_arg, "dassl", fcn_name,
343  fname, "; endfunction");
344  }
345  while (0);
346  break;
347 
348  case 2:
349  {
351 
352  fcn_name = unique_symbol_name ("__dassl_fcn__");
353  fname = "function y = ";
354  fname.append (fcn_name);
355  fname.append (" (x, xdot, t) y = ");
356  dassl_fcn = extract_function (tmp(0), "dassl", fcn_name,
357  fname, "; endfunction");
358 
359  if (dassl_fcn)
360  {
361  jac_name = unique_symbol_name ("__dassl_jac__");
362  jname = "function jac = ";
363  jname.append (jac_name);
364  jname.append (" (x, xdot, t, cj) jac = ");
365  dassl_jac = extract_function (tmp(1), "dassl",
366  jac_name, jname,
367  "; endfunction");
368 
369  if (! dassl_jac)
370  {
371  if (fcn_name.length ())
372  clear_function (fcn_name);
373  dassl_fcn = 0;
374  }
375  }
376  }
377  }
378  }
379  }
380 
381  if (! dassl_fcn)
382  return retval;
383 
384  ColumnVector state = args(1).xvector_value ("dassl: initial state X_0 must be a vector");
385 
386  ColumnVector deriv = args(2).xvector_value ("dassl: initial derivatives XDOT_0 must be a vector");
387 
388  ColumnVector out_times = args(3).xvector_value ("dassl: output time variable T must be a vector");
389 
390  ColumnVector crit_times;
391  int crit_times_set = 0;
392  if (nargin > 4)
393  {
394  crit_times = args(4).xvector_value ("dassl: list of critical times T_CRIT must be a vector");
395 
396  crit_times_set = 1;
397  }
398 
399  if (state.numel () != deriv.numel ())
400  error ("dassl: X and XDOT_0 must have the same size");
401 
402  double tzero = out_times (0);
403 
405  if (dassl_jac)
407 
408  DASSL dae (state, deriv, tzero, func);
409 
410  dae.set_options (dassl_opts);
411 
412  Matrix output;
413  Matrix deriv_output;
414 
415  if (crit_times_set)
416  output = dae.integrate (out_times, deriv_output, crit_times);
417  else
418  output = dae.integrate (out_times, deriv_output);
419 
420  if (fcn_name.length ())
421  clear_function (fcn_name);
422  if (jac_name.length ())
423  clear_function (jac_name);
424 
425  std::string msg = dae.error_message ();
426 
427  if (dae.integration_ok ())
428  {
429  retval(0) = output;
430  retval(1) = deriv_output;
431  }
432  else
433  {
434  if (nargout < 3)
435  error ("dassl: %s", msg.c_str ());
436 
437  retval(0) = Matrix ();
438  retval(1) = Matrix ();
439  }
440 
441  retval(2) = static_cast<double> (dae.integration_state ());
442  retval(3) = msg;
443 
444  return retval;
445 }
446 
447 /*
448 ## dassl-1.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 ## Problem
457 ##
458 ## y1' = -y2, y1(0) = 1
459 ## y2' = y1, y2(0) = 0
460 ##
461 ## Solution
462 ##
463 ## y1(t) = cos(t)
464 ## y2(t) = sin(t)
465 ##
466 %!function res = __f (x, xdot, t)
467 %! res = [xdot(1)+x(2); xdot(2)-x(1)];
468 %!endfunction
469 
470 %!test
471 %!
472 %! x0 = [1; 0];
473 %! xdot0 = [0; 1];
474 %! t = (0:1:10)';
475 %!
476 %! tol = 100 * dassl_options ("relative tolerance");
477 %!
478 %! [x, xdot] = dassl ("__f", x0, xdot0, t);
479 %!
480 %! y = [cos(t), sin(t)];
481 %!
482 %! assert (x, y, tol);
483 
484 ## dassl-2.m
485 ##
486 ## Test dassl() function
487 ##
488 ## Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
489 ## Comalco Research and Technology
490 ## 20 May 1998
491 ##
492 ## Based on SLATEC quick check for DASSL by Linda Petzold
493 ##
494 ## Problem
495 ##
496 ## x1' + 10*x1 = 0, x1(0) = 1
497 ## x1 + x2 = 1, x2(0) = 0
498 ##
499 ##
500 ## Solution
501 ##
502 ## x1(t) = exp(-10*t)
503 ## x2(t) = 1 - x(1)
504 ##
505 %!function res = __f (x, xdot, t)
506 %! res = [xdot(1)+10*x(1); x(1)+x(2)-1];
507 %!endfunction
508 
509 %!test
510 %!
511 %! x0 = [1; 0];
512 %! xdot0 = [-10; 10];
513 %! t = (0:0.2:1)';
514 %!
515 %! tol = 500 * dassl_options ("relative tolerance");
516 %!
517 %! [x, xdot] = dassl ("__f", x0, xdot0, t);
518 %!
519 %! y = [exp(-10*t), 1-exp(-10*t)];
520 %!
521 %! assert (x, y, tol);
522 
523 %!test
524 %! old_tol = dassl_options ("absolute tolerance");
525 %! dassl_options ("absolute tolerance", eps);
526 %! assert (dassl_options ("absolute tolerance") == eps);
527 %! ## Restore old value of tolerance
528 %! dassl_options ("absolute tolerance", old_tol);
529 
530 %!error dassl_options ("foo", 1, 2)
531 */
std::string error_message(void) const
Definition: DASSL.cc:500
static int call_depth
Definition: dassl.cc:58
bool is_empty(void) const
Definition: Array.h:575
Definition: Cell.h:37
fname
Definition: load-save.cc:754
virtual octave_value_list do_multi_index_op(int nargout, const octave_value_list &idx)
Definition: ov-base.cc:259
octave_idx_type rows(void) const
Definition: ov.h:489
bool integration_ok(void) const
Definition: base-de.h:98
Definition: DASSL.h:32
Matrix integrate(const ColumnVector &tout, Matrix &xdot_out)
Definition: DASSL.cc:352
OCTINTERP_API void print_usage(void)
Definition: defun.cc:52
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:363
octave_idx_type length(void) const
Definition: ovl.h:96
Matrix dassl_user_jacobian(const ColumnVector &x, const ColumnVector &xdot, double t, double cj)
Definition: dassl.cc:110
void protect_var(T &var)
void error(const char *fmt,...)
Definition: error.cc:570
bool is_cell(void) const
Definition: ov.h:545
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:935
DEFUN(dassl, args, nargout, doc:)
Definition: dassl.cc:156
static octave_function * dassl_jac
Definition: dassl.cc:51
i e
Definition: data.cc:2724
bool is_function_handle(void) const
Definition: ov.h:702
std::string unique_symbol_name(const std::string &basename)
Definition: variables.cc:491
static DASSL_options dassl_opts
Definition: DASSL-opts.cc:20
Cell cell_value(void) const
Definition: ov.cc:1687
void clear_function(const std::string &nm)
Definition: variables.cc:79
JNIEnv void * args
Definition: ov-java.cc:67
void set_options(const DASSL_options &opt)
Definition: DASSL-opts.h:77
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:935
static octave_function * dassl_fcn
Definition: dassl.cc:48
int nargin
Definition: graphics.cc:10115
bool is_inline_function(void) const
Definition: ov.h:708
double tmp
Definition: data.cc:6300
octave_value retval
Definition: data.cc:6294
Definition: dMatrix.h:37
the sparsity preserving column transformation such that that defines the pivoting threshold can be given in which case it defines the c
Definition: lu.cc:138
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:911
static uint32_t state[624]
Definition: randmtzig.cc:184
octave_function * function_value(bool silent=false) const
Definition: ov.cc:1705
static bool warned_fcn_imaginary
Definition: dassl.cc:54
void warning(const char *fmt,...)
Definition: error.cc:788
octave::unwind_protect frame
Definition: graphics.cc:11584
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:142
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:145
ColumnVector dassl_user_function(const ColumnVector &x, const ColumnVector &xdot, double t, octave_idx_type &ires)
Definition: dassl.cc:61
octave_idx_type integration_state(void) const
Definition: base-de.h:100
static bool warned_jac_imaginary
Definition: dassl.cc:55
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:854
F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &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 F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T F77_REAL F77_REAL &F77_RET_T F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE * x
DAEFunc & set_jacobian_function(DAEJacFunc j)
Definition: DAEFunc.h:84