3Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009 John W. Eaton
6This file is part of Octave.
8Octave is free software; you can redistribute it and/or modify it
9under the terms of the GNU General Public License as published by the
10Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
13Octave is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18You should have received a copy of the GNU General Public License
19along with Octave; see the file COPYING. If not, see
20<http://www.gnu.org/licenses/>.
42#include "unwind-prot.h"
46#include "DASSL-opts.cc"
48// Global pointer for user defined function required by dassl.
49static octave_function *dassl_fcn;
51// Global pointer for optional user defined jacobian function.
52static octave_function *dassl_jac;
54// Have we warned about imaginary values returned from user function?
55static bool warned_fcn_imaginary = false;
56static bool warned_jac_imaginary = false;
58// Is this a recursive call?
59static int call_depth = 0;
62dassl_user_function (const ColumnVector& x, const ColumnVector& xdot,
63 double t, octave_idx_type& ires)
67 assert (x.capacity () == xdot.capacity ());
69 octave_value_list args;
77 octave_value_list tmp = dassl_fcn->do_multi_index_op (1, args);
81 gripe_user_supplied_eval ("dassl");
85 int tlen = tmp.length ();
86 if (tlen > 0 && tmp(0).is_defined ())
88 if (! warned_fcn_imaginary && tmp(0).is_complex_type ())
90 warning ("dassl: ignoring imaginary part returned from user-supplied function");
91 warned_fcn_imaginary = true;
94 retval = ColumnVector (tmp(0).vector_value ());
97 ires = tmp(1).int_value ();
99 if (error_state || retval.length () == 0)
100 gripe_user_supplied_eval ("dassl");
103 gripe_user_supplied_eval ("dassl");
110dassl_user_jacobian (const ColumnVector& x, const ColumnVector& xdot,
115 assert (x.capacity () == xdot.capacity ());
117 octave_value_list args;
126 octave_value_list tmp = dassl_jac->do_multi_index_op (1, args);
130 gripe_user_supplied_eval ("dassl");
134 int tlen = tmp.length ();
135 if (tlen > 0 && tmp(0).is_defined ())
137 if (! warned_jac_imaginary && tmp(0).is_complex_type ())
139 warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
140 warned_jac_imaginary = true;
143 retval = tmp(0).matrix_value ();
145 if (error_state || retval.length () == 0)
146 gripe_user_supplied_eval ("dassl");
149 gripe_user_supplied_eval ("dassl");
155#define DASSL_ABORT() \
158#define DASSL_ABORT1(msg) \
161 ::error ("dassl: " msg); \
166#define DASSL_ABORT2(fmt, arg) \
169 ::error ("dassl: " fmt, arg); \
174DEFUN_DLD (dassl, args, nargout,
176@deftypefn {Loadable Function} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} dassl (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})\n\
177Solve the set of differential-algebraic equations\n\
179$$ 0 = f (x, \\dot{x}, t) $$\n\
181$$ x(t_0) = x_0, \\dot{x}(t_0) = \\dot{x}_0 $$\n\
193x(t_0) = x_0, xdot(t_0) = xdot_0\n\
197The solution is returned in the matrices @var{x} and @var{xdot},\n\
198with each row in the result matrices corresponding to one of the\n\
199elements in the vector @var{t}. The first element of @var{t}\n\
200should be @math{t_0} and correspond to the initial state of the\n\
201system @var{x_0} and its derivative @var{xdot_0}, so that the first\n\
202row of the output @var{x} is @var{x_0} and the first row\n\
203of the output @var{xdot} is @var{xdot_0}.\n\
205The first argument, @var{fcn}, is a string, inline, or function handle\n\
206that names the function @math{f} to call to compute the vector of\n\
207residuals for the set of equations. It must have the form\n\
210@var{res} = f (@var{x}, @var{xdot}, @var{t})\n\
214in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a\n\
217If @var{fcn} is a two-element string array or a two-element cell array\n\
218of strings, inline functions, or function handles, the first element names\n\
219the function @math{f} described above, and the second element names a\n\
220function to compute the modified Jacobian\n\
224J = {\\partial f \\over \\partial x}\n\
225 + c {\\partial f \\over \\partial \\dot{x}}\n\
232jac = -- + c ------\n\
238The modified Jacobian function must have the form\n\
243@var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})\n\
248The second and third arguments to @code{dassl} specify the initial\n\
249condition of the states and their derivatives, and the fourth argument\n\
250specifies a vector of output times at which the solution is desired,\n\
251including the time corresponding to the initial condition.\n\
253The set of initial states and derivatives are not strictly required to\n\
254be consistent. In practice, however, @sc{Dassl} is not very good at\n\
255determining a consistent set for you, so it is best if you ensure that\n\
256the initial values result in the function evaluating to zero.\n\
258The fifth argument is optional, and may be used to specify a set of\n\
259times that the DAE solver should not integrate past. It is useful for\n\
260avoiding difficulties with singularities and points where there is a\n\
261discontinuity in the derivative.\n\
263After a successful computation, the value of @var{istate} will be\n\
264greater than zero (consistent with the Fortran version of @sc{Dassl}).\n\
266If the computation is not successful, the value of @var{istate} will be\n\
267less than zero and @var{msg} will contain additional information.\n\
269You can use the function @code{dassl_options} to set optional\n\
270parameters for @code{dassl}.\n\
271@seealso{daspk, dasrt, lsode}\n\
274 octave_value_list retval;
276 warned_fcn_imaginary = false;
277 warned_jac_imaginary = false;
279 unwind_protect frame;
281 frame.protect_var (call_depth);
285 DASSL_ABORT1 ("invalid recursive call");
287 int nargin = args.length ();
289 if (nargin > 3 && nargin < 6 && nargout < 5)
291 std::string fcn_name, fname, jac_name, jname;
295 octave_value f_arg = args(0);
297 if (f_arg.is_cell ())
299 Cell c = f_arg.cell_value ();
302 else if (c.length() == 2)
304 if (c(0).is_function_handle () || c(0).is_inline_function ())
305 dassl_fcn = c(0).function_value ();
308 fcn_name = unique_symbol_name ("__dassl_fcn__");
309 fname = "function y = ";
310 fname.append (fcn_name);
311 fname.append (" (x, xdot, t) y = ");
312 dassl_fcn = extract_function
313 (c(0), "dassl", fcn_name, fname, "; endfunction");
318 if (c(1).is_function_handle () || c(1).is_inline_function ())
319 dassl_jac = c(1).function_value ();
322 jac_name = unique_symbol_name ("__dassl_jac__");
323 jname = "function jac = ";
324 jname.append(jac_name);
325 jname.append (" (x, xdot, t, cj) jac = ");
326 dassl_jac = extract_function
327 (c(1), "dassl", jac_name, jname, "; endfunction");
331 if (fcn_name.length())
332 clear_function (fcn_name);
339 DASSL_ABORT1 ("incorrect number of elements in cell array");
342 if (!dassl_fcn && ! f_arg.is_cell())
344 if (f_arg.is_function_handle () || f_arg.is_inline_function ())
345 dassl_fcn = f_arg.function_value ();
348 switch (f_arg.rows ())
353 fcn_name = unique_symbol_name ("__dassl_fcn__");
354 fname = "function y = ";
355 fname.append (fcn_name);
356 fname.append (" (x, xdot, t) y = ");
357 dassl_fcn = extract_function
358 (f_arg, "dassl", fcn_name, fname, "; endfunction");
365 string_vector tmp = f_arg.all_strings ();
369 fcn_name = unique_symbol_name ("__dassl_fcn__");
370 fname = "function y = ";
371 fname.append (fcn_name);
372 fname.append (" (x, xdot, t) y = ");
373 dassl_fcn = extract_function
374 (tmp(0), "dassl", fcn_name, fname, "; endfunction");
378 jac_name = unique_symbol_name ("__dassl_jac__");
379 jname = "function jac = ";
380 jname.append(jac_name);
381 jname.append (" (x, xdot, t, cj) jac = ");
382 dassl_jac = extract_function
383 (tmp(1), "dassl", jac_name, jname,
388 if (fcn_name.length())
389 clear_function (fcn_name);
399 if (error_state || ! dassl_fcn)
402 ColumnVector state = ColumnVector (args(1).vector_value ());
405 DASSL_ABORT1 ("expecting state vector as second argument");
407 ColumnVector deriv (args(2).vector_value ());
410 DASSL_ABORT1 ("expecting derivative vector as third argument");
412 ColumnVector out_times (args(3).vector_value ());
415 DASSL_ABORT1 ("expecting output time vector as fourth argument");
417 ColumnVector crit_times;
418 int crit_times_set = 0;
421 crit_times = ColumnVector (args(4).vector_value ());
424 DASSL_ABORT1 ("expecting critical time vector as fifth argument");
429 if (state.capacity () != deriv.capacity ())
430 DASSL_ABORT1 ("x and xdot must have the same size");
432 double tzero = out_times (0);
434 DAEFunc func (dassl_user_function);
436 func.set_jacobian_function (dassl_user_jacobian);
438 DASSL dae (state, deriv, tzero, func);
440 dae.set_options (dassl_opts);
446 output = dae.integrate (out_times, deriv_output, crit_times);
448 output = dae.integrate (out_times, deriv_output);
450 if (fcn_name.length())
451 clear_function (fcn_name);
452 if (jac_name.length())
453 clear_function (jac_name);
457 std::string msg = dae.error_message ();
460 retval(2) = static_cast<double> (dae.integration_state ());
462 if (dae.integration_ok ())
464 retval(1) = deriv_output;
469 retval(1) = Matrix ();
470 retval(0) = Matrix ();
473 error ("dassl: %s", msg.c_str ());
487%% Test dassl() function
489%% Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
490%% Comalco Research and Technology
495%% y1' = -y2, y1(0) = 1
496%% y2' = y1, y2(0) = 0
502%!function res = f (x, xdot, t)
503%! res = [xdot(1)+x(2); xdot(2)-x(1)];
510%! tol = 100 * dassl_options ("relative tolerance");
513%! [x, xdot] = dassl ("f", x0, xdot0, t);
515%! y = [cos(t), sin(t)];
517%! assert(all (all (abs (x - y) < tol)));
521%% Test dassl() function
523%% Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
524%% Comalco Research and Technology
527%% Based on SLATEC quick check for DASSL by Linda Petzold
531%% x1' + 10*x1 = 0, x1(0) = 1
532%% x1 + x2 = 1, x2(0) = 0
539%!function res = f (x, xdot, t)
540%! res = [xdot(1)+10*x(1); x(1)+x(2)-1];
547%! tol = 500 * dassl_options ("relative tolerance");
550%! [x, xdot] = dassl ("f", x0, xdot0, t);
552%! y = [exp(-10*t), 1-exp(-10*t)];
554%! assert(all (all (abs (x - y) < tol)));
557%! dassl_options ("absolute tolerance", eps);
558%! assert(dassl_options ("absolute tolerance") == eps);
560%!error <Invalid call to dassl_options.*> dassl_options ("foo", 1, 2);