3Copyright (C) 1996, 1997, 2002, 2003, 2004, 2005, 2006, 2007, 2009
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 "DASPK-opts.cc"
48// Global pointer for user defined function required by daspk.
49static octave_function *daspk_fcn;
51// Global pointer for optional user defined jacobian function.
52static octave_function *daspk_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;
62daspk_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 = daspk_fcn->do_multi_index_op (1, args);
81 gripe_user_supplied_eval ("daspk");
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 ("daspk: 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 ("daspk");
103 gripe_user_supplied_eval ("daspk");
110daspk_user_jacobian (const ColumnVector& x, const ColumnVector& xdot,
115 assert (x.capacity () == xdot.capacity ());
117 octave_value_list args;
126 octave_value_list tmp = daspk_jac->do_multi_index_op (1, args);
130 gripe_user_supplied_eval ("daspk");
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 ("daspk: 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 ("daspk");
149 gripe_user_supplied_eval ("daspk");
155#define DASPK_ABORT() \
158#define DASPK_ABORT1(msg) \
161 ::error ("daspk: " msg); \
166#define DASPK_ABORT2(fmt, arg) \
169 ::error ("daspk: " fmt, arg); \
174DEFUN_DLD (daspk, args, nargout,
176@deftypefn {Loadable Function} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} daspk (@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\
192x(t_0) = x_0, xdot(t_0) = xdot_0\n\
196The solution is returned in the matrices @var{x} and @var{xdot},\n\
197with each row in the result matrices corresponding to one of the\n\
198elements in the vector @var{t}. The first element of @var{t}\n\
199should be @math{t_0} and correspond to the initial state of the\n\
200system @var{x_0} and its derivative @var{xdot_0}, so that the first\n\
201row of the output @var{x} is @var{x_0} and the first row\n\
202of the output @var{xdot} is @var{xdot_0}.\n\
204The first argument, @var{fcn}, is a string, inline, or function handle\n\
205that names the function @math{f} to call to compute the vector of\n\
206residuals for the set of equations. It must have the form\n\
209@var{res} = f (@var{x}, @var{xdot}, @var{t})\n\
213in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a\n\
216If @var{fcn} is a two-element string array or a two-element cell array\n\
217of strings, inline functions, or function handles, the first element names\n\
218the function @math{f} described above, and the second element names a\n\
219function to compute the modified Jacobian\n\
222J = {\\partial f \\over \\partial x}\n\
223 + c {\\partial f \\over \\partial \\dot{x}}\n\
231jac = -- + c ------\n\
237The modified Jacobian function must have the form\n\
242@var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})\n\
247The second and third arguments to @code{daspk} specify the initial\n\
248condition of the states and their derivatives, and the fourth argument\n\
249specifies a vector of output times at which the solution is desired,\n\
250including the time corresponding to the initial condition.\n\
252The set of initial states and derivatives are not strictly required to\n\
253be consistent. If they are not consistent, you must use the\n\
254@code{daspk_options} function to provide additional information so\n\
255that @code{daspk} can compute a consistent starting point.\n\
257The fifth argument is optional, and may be used to specify a set of\n\
258times that the DAE solver should not integrate past. It is useful for\n\
259avoiding difficulties with singularities and points where there is a\n\
260discontinuity in the derivative.\n\
262After a successful computation, the value of @var{istate} will be\n\
263greater than zero (consistent with the Fortran version of @sc{Daspk}).\n\
265If the computation is not successful, the value of @var{istate} will be\n\
266less than zero and @var{msg} will contain additional information.\n\
268You can use the function @code{daspk_options} to set optional\n\
269parameters for @code{daspk}.\n\
273 octave_value_list retval;
275 warned_fcn_imaginary = false;
276 warned_jac_imaginary = false;
278 unwind_protect frame;
280 frame.protect_var (call_depth);
284 DASPK_ABORT1 ("invalid recursive call");
286 int nargin = args.length ();
288 if (nargin > 3 && nargin < 6)
290 std::string fcn_name, fname, jac_name, jname;
294 octave_value f_arg = args(0);
296 if (f_arg.is_cell ())
298 Cell c = f_arg.cell_value ();
301 else if (c.length() == 2)
303 if (c(0).is_function_handle () || c(0).is_inline_function ())
304 daspk_fcn = c(0).function_value ();
307 fcn_name = unique_symbol_name ("__daspk_fcn__");
308 fname = "function y = ";
309 fname.append (fcn_name);
310 fname.append (" (x, xdot, t) y = ");
311 daspk_fcn = extract_function
312 (c(0), "daspk", fcn_name, fname, "; endfunction");
317 if (c(1).is_function_handle () || c(1).is_inline_function ())
318 daspk_jac = c(1).function_value ();
321 jac_name = unique_symbol_name ("__daspk_jac__");
322 jname = "function jac = ";
323 jname.append(jac_name);
324 jname.append (" (x, xdot, t, cj) jac = ");
325 daspk_jac = extract_function
326 (c(1), "daspk", jac_name, jname, "; endfunction");
330 if (fcn_name.length())
331 clear_function (fcn_name);
338 DASPK_ABORT1 ("incorrect number of elements in cell array");
341 if (!daspk_fcn && ! f_arg.is_cell())
343 if (f_arg.is_function_handle () || f_arg.is_inline_function ())
344 daspk_fcn = f_arg.function_value ();
347 switch (f_arg.rows ())
352 fcn_name = unique_symbol_name ("__daspk_fcn__");
353 fname = "function y = ";
354 fname.append (fcn_name);
355 fname.append (" (x, xdot, t) y = ");
356 daspk_fcn = extract_function
357 (f_arg, "daspk", fcn_name, fname, "; endfunction");
364 string_vector tmp = f_arg.all_strings ();
368 fcn_name = unique_symbol_name ("__daspk_fcn__");
369 fname = "function y = ";
370 fname.append (fcn_name);
371 fname.append (" (x, xdot, t) y = ");
372 daspk_fcn = extract_function
373 (tmp(0), "daspk", fcn_name, fname, "; endfunction");
377 jac_name = unique_symbol_name ("__daspk_jac__");
378 jname = "function jac = ";
379 jname.append(jac_name);
380 jname.append (" (x, xdot, t, cj) jac = ");
381 daspk_jac = extract_function
382 (tmp(1), "daspk", jac_name, jname,
387 if (fcn_name.length())
388 clear_function (fcn_name);
398 if (error_state || ! daspk_fcn)
401 ColumnVector state = ColumnVector (args(1).vector_value ());
404 DASPK_ABORT1 ("expecting state vector as second argument");
406 ColumnVector deriv (args(2).vector_value ());
409 DASPK_ABORT1 ("expecting derivative vector as third argument");
411 ColumnVector out_times (args(3).vector_value ());
414 DASPK_ABORT1 ("expecting output time vector as fourth argument");
416 ColumnVector crit_times;
417 int crit_times_set = 0;
420 crit_times = ColumnVector (args(4).vector_value ());
423 DASPK_ABORT1 ("expecting critical time vector as fifth argument");
428 if (state.capacity () != deriv.capacity ())
429 DASPK_ABORT1 ("x and xdot must have the same size");
431 double tzero = out_times (0);
433 DAEFunc func (daspk_user_function);
435 func.set_jacobian_function (daspk_user_jacobian);
437 DASPK dae (state, deriv, tzero, func);
438 dae.set_options (daspk_opts);
444 output = dae.integrate (out_times, deriv_output, crit_times);
446 output = dae.integrate (out_times, deriv_output);
448 if (fcn_name.length())
449 clear_function (fcn_name);
450 if (jac_name.length())
451 clear_function (jac_name);
455 std::string msg = dae.error_message ();
458 retval(2) = static_cast<double> (dae.integration_state ());
460 if (dae.integration_ok ())
462 retval(1) = deriv_output;
467 retval(1) = Matrix ();
468 retval(0) = Matrix ();
471 error ("daspk: %s", msg.c_str ());