changelog shortlog tags changeset files revisions annotate raw

src/DLD-FUNCTIONS/dassl.cc

changeset 10289: 4b124317dc38
parent:40dfc0c99116
author: John W. Eaton <jwe@octave.org>
date: Tue Feb 09 20:58:55 2010 -0500 (69 minutes ago)
permissions: -rw-r--r--
description: base_properties::set_children: account for hidden children
1/*
2
3Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009 John W. Eaton
5
6This file is part of Octave.
7
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.
12
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
16for more details.
17
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/>.
21
22*/
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <string>
29
30#include <iomanip>
31#include <iostream>
32
33#include "DASSL.h"
34
35#include "defun-dld.h"
36#include "error.h"
37#include "gripes.h"
38#include "oct-obj.h"
39#include "ov-fcn.h"
40#include "ov-cell.h"
41#include "pager.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.
49static octave_function *dassl_fcn;
50
51// Global pointer for optional user defined jacobian function.
52static octave_function *dassl_jac;
53
54// Have we warned about imaginary values returned from user function?
55static bool warned_fcn_imaginary = false;
56static bool warned_jac_imaginary = false;
57
58// Is this a recursive call?
59static int call_depth = 0;
60
61ColumnVector
62dassl_user_function (const ColumnVector& x, const ColumnVector& xdot,
63 double t, octave_idx_type& ires)
64{
65 ColumnVector retval;
66
67 assert (x.capacity () == xdot.capacity ());
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 {
77 octave_value_list tmp = dassl_fcn->do_multi_index_op (1, args);
78
79 if (error_state)
80 {
81 gripe_user_supplied_eval ("dassl");
82 return retval;
83 }
84
85 int tlen = tmp.length ();
86 if (tlen > 0 && tmp(0).is_defined ())
87 {
88 if (! warned_fcn_imaginary && tmp(0).is_complex_type ())
89 {
90 warning ("dassl: ignoring imaginary part returned from user-supplied function");
91 warned_fcn_imaginary = true;
92 }
93
94 retval = ColumnVector (tmp(0).vector_value ());
95
96 if (tlen > 1)
97 ires = tmp(1).int_value ();
98
99 if (error_state || retval.length () == 0)
100 gripe_user_supplied_eval ("dassl");
101 }
102 else
103 gripe_user_supplied_eval ("dassl");
104 }
105
106 return retval;
107}
108
109Matrix
110dassl_user_jacobian (const ColumnVector& x, const ColumnVector& xdot,
111 double t, double cj)
112{
113 Matrix retval;
114
115 assert (x.capacity () == xdot.capacity ());
116
117 octave_value_list args;
118
119 args(3) = cj;
120 args(2) = t;
121 args(1) = xdot;
122 args(0) = x;
123
124 if (dassl_jac)
125 {
126 octave_value_list tmp = dassl_jac->do_multi_index_op (1, args);
127
128 if (error_state)
129 {
130 gripe_user_supplied_eval ("dassl");
131 return retval;
132 }
133
134 int tlen = tmp.length ();
135 if (tlen > 0 && tmp(0).is_defined ())
136 {
137 if (! warned_jac_imaginary && tmp(0).is_complex_type ())
138 {
139 warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
140 warned_jac_imaginary = true;
141 }
142
143 retval = tmp(0).matrix_value ();
144
145 if (error_state || retval.length () == 0)
146 gripe_user_supplied_eval ("dassl");
147 }
148 else
149 gripe_user_supplied_eval ("dassl");
150 }
151
152 return retval;
153}
154
155#define DASSL_ABORT() \
156 return retval
157
158#define DASSL_ABORT1(msg) \
159 do \
160 { \
161 ::error ("dassl: " msg); \
162 DASSL_ABORT (); \
163 } \
164 while (0)
165
166#define DASSL_ABORT2(fmt, arg) \
167 do \
168 { \
169 ::error ("dassl: " fmt, arg); \
170 DASSL_ABORT (); \
171 } \
172 while (0)
173
174DEFUN_DLD (dassl, args, nargout,
175 "-*- texinfo -*-\n\
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\
178@tex\n\
179$$ 0 = f (x, \\dot{x}, t) $$\n\
180with\n\
181$$ x(t_0) = x_0, \\dot{x}(t_0) = \\dot{x}_0 $$\n\
182@end tex\n\
183@ifnottex\n\
184\n\
185@example\n\
1860 = f (x, xdot, t)\n\
187@end example\n\
188\n\
189@noindent\n\
190with\n\
191\n\
192@example\n\
193x(t_0) = x_0, xdot(t_0) = xdot_0\n\
194@end example\n\
195\n\
196@end ifnottex\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\
204\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\
208\n\
209@example\n\
210@var{res} = f (@var{x}, @var{xdot}, @var{t})\n\
211@end example\n\
212\n\
213@noindent\n\
214in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a\n\
215scalar.\n\
216\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\
221\n\
222@tex\n\
223$$\n\
224J = {\\partial f \\over \\partial x}\n\
225 + c {\\partial f \\over \\partial \\dot{x}}\n\
226$$\n\
227@end tex\n\
228@ifnottex\n\
229@example\n\
230@group\n\
231 df df\n\
232jac = -- + c ------\n\
233 dx d xdot\n\
234@end group\n\
235@end example\n\
236@end ifnottex\n\
237\n\
238The modified Jacobian function must have the form\n\
239\n\
240@example\n\
241@group\n\
242\n\
243@var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})\n\
244\n\
245@end group\n\
246@end example\n\
247\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\
252\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\
257\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\
262\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\
265\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\
268\n\
269You can use the function @code{dassl_options} to set optional\n\
270parameters for @code{dassl}.\n\
271@seealso{daspk, dasrt, lsode}\n\
272@end deftypefn")
273{
274 octave_value_list retval;
275
276 warned_fcn_imaginary = false;
277 warned_jac_imaginary = false;
278
279 unwind_protect frame;
280
281 frame.protect_var (call_depth);
282 call_depth++;
283
284 if (call_depth > 1)
285 DASSL_ABORT1 ("invalid recursive call");
286
287 int nargin = args.length ();
288
289 if (nargin > 3 && nargin < 6 && nargout < 5)
290 {
291 std::string fcn_name, fname, jac_name, jname;
292 dassl_fcn = 0;
293 dassl_jac = 0;
294
295 octave_value f_arg = args(0);
296
297 if (f_arg.is_cell ())
298 {
299 Cell c = f_arg.cell_value ();
300 if (c.length() == 1)
301 f_arg = c(0);
302 else if (c.length() == 2)
303 {
304 if (c(0).is_function_handle () || c(0).is_inline_function ())
305 dassl_fcn = c(0).function_value ();
306 else
307 {
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");
314 }
315
316 if (dassl_fcn)
317 {
318 if (c(1).is_function_handle () || c(1).is_inline_function ())
319 dassl_jac = c(1).function_value ();
320 else
321 {
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");
328
329 if (!dassl_jac)
330 {
331 if (fcn_name.length())
332 clear_function (fcn_name);
333 dassl_fcn = 0;
334 }
335 }
336 }
337 }
338 else
339 DASSL_ABORT1 ("incorrect number of elements in cell array");
340 }
341
342 if (!dassl_fcn && ! f_arg.is_cell())
343 {
344 if (f_arg.is_function_handle () || f_arg.is_inline_function ())
345 dassl_fcn = f_arg.function_value ();
346 else
347 {
348 switch (f_arg.rows ())
349 {
350 case 1:
351 do
352 {
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");
359 }
360 while (0);
361 break;
362
363 case 2:
364 {
365 string_vector tmp = f_arg.all_strings ();
366
367 if (! error_state)
368 {
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");
375
376 if (dassl_fcn)
377 {
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,
384 "; endfunction");
385
386 if (!dassl_jac)
387 {
388 if (fcn_name.length())
389 clear_function (fcn_name);
390 dassl_fcn = 0;
391 }
392 }
393 }
394 }
395 }
396 }
397 }
398
399 if (error_state || ! dassl_fcn)
400 DASSL_ABORT ();
401
402 ColumnVector state = ColumnVector (args(1).vector_value ());
403
404 if (error_state)
405 DASSL_ABORT1 ("expecting state vector as second argument");
406
407 ColumnVector deriv (args(2).vector_value ());
408
409 if (error_state)
410 DASSL_ABORT1 ("expecting derivative vector as third argument");
411
412 ColumnVector out_times (args(3).vector_value ());
413
414 if (error_state)
415 DASSL_ABORT1 ("expecting output time vector as fourth argument");
416
417 ColumnVector crit_times;
418 int crit_times_set = 0;
419 if (nargin > 4)
420 {
421 crit_times = ColumnVector (args(4).vector_value ());
422
423 if (error_state)
424 DASSL_ABORT1 ("expecting critical time vector as fifth argument");
425
426 crit_times_set = 1;
427 }
428
429 if (state.capacity () != deriv.capacity ())
430 DASSL_ABORT1 ("x and xdot must have the same size");
431
432 double tzero = out_times (0);
433
434 DAEFunc func (dassl_user_function);
435 if (dassl_jac)
436 func.set_jacobian_function (dassl_user_jacobian);
437
438 DASSL dae (state, deriv, tzero, func);
439
440 dae.set_options (dassl_opts);
441
442 Matrix output;
443 Matrix deriv_output;
444
445 if (crit_times_set)
446 output = dae.integrate (out_times, deriv_output, crit_times);
447 else
448 output = dae.integrate (out_times, deriv_output);
449
450 if (fcn_name.length())
451 clear_function (fcn_name);
452 if (jac_name.length())
453 clear_function (jac_name);
454
455 if (! error_state)
456 {
457 std::string msg = dae.error_message ();
458
459 retval(3) = msg;
460 retval(2) = static_cast<double> (dae.integration_state ());
461
462 if (dae.integration_ok ())
463 {
464 retval(1) = deriv_output;
465 retval(0) = output;
466 }
467 else
468 {
469 retval(1) = Matrix ();
470 retval(0) = Matrix ();
471
472 if (nargout < 3)
473 error ("dassl: %s", msg.c_str ());
474 }
475 }
476 }
477 else
478 print_usage ();
479
480 return retval;
481}
482
483/*
484
485%% dassl-1.m
486%%
487%% Test dassl() function
488%%
489%% Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
490%% Comalco Research and Technology
491%% 20 May 1998
492%%
493%% Problem
494%%
495%% y1' = -y2, y1(0) = 1
496%% y2' = y1, y2(0) = 0
497%%
498%% Solution
499%%
500%% y1(t) = cos(t)
501%% y2(t) = sin(t)
502%!function res = f (x, xdot, t)
503%! res = [xdot(1)+x(2); xdot(2)-x(1)];
504%!test
505%!
506%! x0 = [1; 0];
507%! xdot0 = [0; 1];
508%! t = (0:1:10)';
509%!
510%! tol = 100 * dassl_options ("relative tolerance");
511%!
512%!
513%! [x, xdot] = dassl ("f", x0, xdot0, t);
514%!
515%! y = [cos(t), sin(t)];
516%!
517%! assert(all (all (abs (x - y) < tol)));
518
519%% dassl-2.m
520%%
521%% Test dassl() function
522%%
523%% Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
524%% Comalco Research and Technology
525%% 20 May 1998
526%%
527%% Based on SLATEC quick check for DASSL by Linda Petzold
528%%
529%% Problem
530%%
531%% x1' + 10*x1 = 0, x1(0) = 1
532%% x1 + x2 = 1, x2(0) = 0
533%%
534%%
535%% Solution
536%%
537%% x1(t) = exp(-10*t)
538%% x2(t) = 1 - x(1)
539%!function res = f (x, xdot, t)
540%! res = [xdot(1)+10*x(1); x(1)+x(2)-1];
541%!test
542%!
543%! x0 = [1; 0];
544%! xdot0 = [-10; 10];
545%! t = (0:0.2:1)';
546%!
547%! tol = 500 * dassl_options ("relative tolerance");
548%!
549%!
550%! [x, xdot] = dassl ("f", x0, xdot0, t);
551%!
552%! y = [exp(-10*t), 1-exp(-10*t)];
553%!
554%! assert(all (all (abs (x - y) < tol)));
555
556%!test
557%! dassl_options ("absolute tolerance", eps);
558%! assert(dassl_options ("absolute tolerance") == eps);
559
560%!error <Invalid call to dassl_options.*> dassl_options ("foo", 1, 2);
561
562*/