3Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009 John W. Eaton
5This file is part of Octave.
7Octave is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by the
9Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
12Octave is distributed in the hope that it will be useful, but WITHOUT
13ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17You should have received a copy of the GNU General Public License
18along with Octave; see the file COPYING. If not, see
19<http://www.gnu.org/licenses/>.
31#include "lo-mappers.h"
41#include "unwind-prot.h"
45#include "DASRT-opts.cc"
47// Global pointers for user defined function required by dasrt.
48static octave_function *dasrt_f;
49static octave_function *dasrt_j;
50static octave_function *dasrt_cf;
52// Have we warned about imaginary values returned from user function?
53static bool warned_fcn_imaginary = false;
54static bool warned_jac_imaginary = false;
55static bool warned_cf_imaginary = false;
57// Is this a recursive call?
58static int call_depth = 0;
61dasrt_user_f (const ColumnVector& x, const ColumnVector& xdot,
62 double t, octave_idx_type&)
66 assert (x.capacity () == xdot.capacity ());
68 octave_value_list args;
76 octave_value_list tmp = dasrt_f->do_multi_index_op (1, args);
80 gripe_user_supplied_eval ("dasrt");
84 if (tmp.length () > 0 && tmp(0).is_defined ())
86 if (! warned_fcn_imaginary && tmp(0).is_complex_type ())
88 warning ("dasrt: ignoring imaginary part returned from user-supplied function");
89 warned_fcn_imaginary = true;
92 retval = ColumnVector (tmp(0).vector_value ());
94 if (error_state || retval.length () == 0)
95 gripe_user_supplied_eval ("dasrt");
98 gripe_user_supplied_eval ("dasrt");
105dasrt_user_cf (const ColumnVector& x, double t)
109 octave_value_list args;
116 octave_value_list tmp = dasrt_cf->do_multi_index_op (1, args);
120 gripe_user_supplied_eval ("dasrt");
124 if (tmp.length () > 0 && tmp(0).is_defined ())
126 if (! warned_cf_imaginary && tmp(0).is_complex_type ())
128 warning ("dasrt: ignoring imaginary part returned from user-supplied constraint function");
129 warned_cf_imaginary = true;
132 retval = ColumnVector (tmp(0).vector_value ());
134 if (error_state || retval.length () == 0)
135 gripe_user_supplied_eval ("dasrt");
138 gripe_user_supplied_eval ("dasrt");
145dasrt_user_j (const ColumnVector& x, const ColumnVector& xdot,
150 assert (x.capacity () == xdot.capacity ());
152 octave_value_list args;
161 octave_value_list tmp = dasrt_j->do_multi_index_op (1, args);
165 gripe_user_supplied_eval ("dasrt");
169 int tlen = tmp.length ();
170 if (tlen > 0 && tmp(0).is_defined ())
172 if (! warned_jac_imaginary && tmp(0).is_complex_type ())
174 warning ("dasrt: ignoring imaginary part returned from user-supplied jacobian function");
175 warned_jac_imaginary = true;
178 retval = tmp(0).matrix_value ();
180 if (error_state || retval.length () == 0)
181 gripe_user_supplied_eval ("dasrt");
184 gripe_user_supplied_eval ("dasrt");
193#define DASRT_ABORT1(msg) \
196 ::error ("dasrt: " msg); \
201#define DASRT_ABORT2(fmt, arg) \
204 ::error ("dasrt: " fmt, arg); \
209DEFUN_DLD (dasrt, args, nargout,
211@deftypefn {Loadable Function} {[@var{x}, @var{xdot}, @var{t_out}, @var{istat}, @var{msg}] =} dasrt (@var{fcn} [, @var{g}], @var{x_0}, @var{xdot_0}, @var{t} [, @var{t_crit}])\n\
212Solve the set of differential-algebraic equations\n\
214$$ 0 = f (x, \\dot{x}, t) $$\n\
216$$ x(t_0) = x_0, \\dot{x}(t_0) = \\dot{x}_0 $$\n\
227x(t_0) = x_0, xdot(t_0) = xdot_0\n\
231with functional stopping criteria (root solving).\n\
233The solution is returned in the matrices @var{x} and @var{xdot},\n\
234with each row in the result matrices corresponding to one of the\n\
235elements in the vector @var{t_out}. The first element of @var{t}\n\
236should be @math{t_0} and correspond to the initial state of the\n\
237system @var{x_0} and its derivative @var{xdot_0}, so that the first\n\
238row of the output @var{x} is @var{x_0} and the first row\n\
239of the output @var{xdot} is @var{xdot_0}.\n\
241The vector @var{t} provides an upper limit on the length of the\n\
242integration. If the stopping condition is met, the vector\n\
243@var{t_out} will be shorter than @var{t}, and the final element of\n\
244@var{t_out} will be the point at which the stopping condition was met,\n\
245and may not correspond to any element of the vector @var{t}.\n\
247The first argument, @var{fcn}, is a string, inline, or function handle\n\
248that names the function @math{f} to call to compute the vector of\n\
249residuals for the set of equations. It must have the form\n\
252@var{res} = f (@var{x}, @var{xdot}, @var{t})\n\
256in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a\n\
259If @var{fcn} is a two-element string array or a two-element cell array\n\
260of strings, inline functions, or function handles, the first element names\n\
261the function @math{f} described above, and the second element names a\n\
262function to compute the modified Jacobian\n\
266J = {\\partial f \\over \\partial x}\n\
267 + c {\\partial f \\over \\partial \\dot{x}}\n\
275jac = -- + c ------\n\
282The modified Jacobian function must have the form\n\
287@var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})\n\
292The optional second argument names a function that defines the\n\
293constraint functions whose roots are desired during the integration.\n\
294This function must have the form\n\
297@var{g_out} = g (@var{x}, @var{t})\n\
300and return a vector of the constraint function values.\n\
301If the value of any of the constraint functions changes sign, @sc{Dasrt}\n\
302will attempt to stop the integration at the point of the sign change.\n\
304If the name of the constraint function is omitted, @code{dasrt} solves\n\
305the same problem as @code{daspk} or @code{dassl}.\n\
307Note that because of numerical errors in the constraint functions\n\
308due to roundoff and integration error, @sc{Dasrt} may return false\n\
309roots, or return the same root at two or more nearly equal values of\n\
310@var{T}. If such false roots are suspected, the user should consider\n\
311smaller error tolerances or higher precision in the evaluation of the\n\
312constraint functions.\n\
314If a root of some constraint function defines the end of the problem,\n\
315the input to @sc{Dasrt} should nevertheless allow integration to a\n\
316point slightly past that root, so that @sc{Dasrt} can locate the root\n\
319The third and fourth arguments to @code{dasrt} specify the initial\n\
320condition of the states and their derivatives, and the fourth argument\n\
321specifies a vector of output times at which the solution is desired,\n\
322including the time corresponding to the initial condition.\n\
324The set of initial states and derivatives are not strictly required to\n\
325be consistent. In practice, however, @sc{Dassl} is not very good at\n\
326determining a consistent set for you, so it is best if you ensure that\n\
327the initial values result in the function evaluating to zero.\n\
329The sixth argument is optional, and may be used to specify a set of\n\
330times that the DAE solver should not integrate past. It is useful for\n\
331avoiding difficulties with singularities and points where there is a\n\
332discontinuity in the derivative.\n\
334After a successful computation, the value of @var{istate} will be\n\
335greater than zero (consistent with the Fortran version of @sc{Dassl}).\n\
337If the computation is not successful, the value of @var{istate} will be\n\
338less than zero and @var{msg} will contain additional information.\n\
340You can use the function @code{dasrt_options} to set optional\n\
341parameters for @code{dasrt}.\n\
342@seealso{daspk, dasrt, lsode}\n\
345 octave_value_list retval;
347 warned_fcn_imaginary = false;
348 warned_jac_imaginary = false;
349 warned_cf_imaginary = false;
351 unwind_protect frame;
353 frame.protect_var (call_depth);
357 DASRT_ABORT1 ("invalid recursive call");
361 int nargin = args.length ();
363 if (nargin < 4 || nargin > 6)
369 std::string fcn_name, fname, jac_name, jname;
374 // Check all the arguments. Are they the right animals?
376 // Here's where I take care of f and j in one shot:
378 octave_value f_arg = args(0);
380 if (f_arg.is_cell ())
382 Cell c = f_arg.cell_value ();
385 else if (c.length() == 2)
387 if (c(0).is_function_handle () || c(0).is_inline_function ())
388 dasrt_f = c(0).function_value ();
391 fcn_name = unique_symbol_name ("__dasrt_fcn__");
392 fname = "function y = ";
393 fname.append (fcn_name);
394 fname.append (" (x, xdot, t) y = ");
395 dasrt_f = extract_function
396 (c(0), "dasrt", fcn_name, fname, "; endfunction");
401 if (c(1).is_function_handle () || c(1).is_inline_function ())
402 dasrt_j = c(1).function_value ();
405 jac_name = unique_symbol_name ("__dasrt_jac__");
406 jname = "function jac = ";
407 jname.append(jac_name);
408 jname.append (" (x, xdot, t, cj) jac = ");
409 dasrt_j = extract_function
410 (c(1), "dasrt", jac_name, jname, "; endfunction");
414 if (fcn_name.length())
415 clear_function (fcn_name);
422 DASRT_ABORT1 ("incorrect number of elements in cell array");
425 if (!dasrt_f && ! f_arg.is_cell())
427 if (f_arg.is_function_handle () || f_arg.is_inline_function ())
428 dasrt_f = f_arg.function_value ();
431 switch (f_arg.rows ())
434 fcn_name = unique_symbol_name ("__dasrt_fcn__");
435 fname = "function y = ";
436 fname.append (fcn_name);
437 fname.append (" (x, xdot, t) y = ");
438 dasrt_f = extract_function
439 (f_arg, "dasrt", fcn_name, fname, "; endfunction");
444 string_vector tmp = args(0).all_strings ();
448 fcn_name = unique_symbol_name ("__dasrt_fcn__");
449 fname = "function y = ";
450 fname.append (fcn_name);
451 fname.append (" (x, xdot, t) y = ");
452 dasrt_f = extract_function
453 (tmp(0), "dasrt", fcn_name, fname, "; endfunction");
457 jac_name = unique_symbol_name ("__dasrt_jac__");
458 jname = "function jac = ";
459 jname.append(jac_name);
460 jname.append (" (x, xdot, t, cj) jac = ");
461 dasrt_j = extract_function
462 (tmp(1), "dasrt", jac_name, jname, "; endfunction");
473 ("first arg should be a string or 2-element string array");
478 if (error_state || (! dasrt_f))
481 DAERTFunc func (dasrt_user_f);
485 if (args(1).is_function_handle() || args(1).is_inline_function())
487 dasrt_cf = args(1).function_value();
490 DASRT_ABORT1 ("expecting function name as argument 2");
494 func.set_constraint_function (dasrt_user_cf);
496 else if (args(1).is_string ())
498 dasrt_cf = is_valid_function (args(1), "dasrt", true);
500 DASRT_ABORT1 ("expecting function name as argument 2");
504 func.set_constraint_function (dasrt_user_cf);
507 ColumnVector state (args(argp++).vector_value ());
510 DASRT_ABORT2 ("expecting state vector as argument %d", argp);
512 ColumnVector stateprime (args(argp++).vector_value ());
516 ("expecting time derivative of state vector as argument %d", argp);
518 ColumnVector out_times (args(argp++).vector_value ());
522 ("expecting output time vector as %s argument %d", argp);
524 double tzero = out_times (0);
526 ColumnVector crit_times;
528 bool crit_times_set = false;
532 crit_times = ColumnVector (args(argp++).vector_value ());
536 ("expecting critical time vector as argument %d", argp);
538 crit_times_set = true;
542 func.set_jacobian_function (dasrt_user_j);
546 DASRT dae = DASRT (state, stateprime, tzero, func);
548 dae.set_options (dasrt_opts);
551 output = dae.integrate (out_times, crit_times);
553 output = dae.integrate (out_times);
555 if (fcn_name.length())
556 clear_function (fcn_name);
557 if (jac_name.length())
558 clear_function (jac_name);
562 std::string msg = dae.error_message ();
565 retval(3) = static_cast<double> (dae.integration_state ());
567 if (dae.integration_ok ())
569 retval(2) = output.times ();
570 retval(1) = output.deriv ();
571 retval(0) = output.state ();
575 retval(2) = Matrix ();
576 retval(1) = Matrix ();
577 retval(0) = Matrix ();
580 error ("dasrt: %s", msg.c_str ());