dassl.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1996-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <string>
00028 
00029 #include <iomanip>
00030 #include <iostream>
00031 
00032 #include "DASSL.h"
00033 
00034 #include "defun-dld.h"
00035 #include "error.h"
00036 #include "gripes.h"
00037 #include "oct-obj.h"
00038 #include "ov-fcn.h"
00039 #include "ov-cell.h"
00040 #include "pager.h"
00041 #include "unwind-prot.h"
00042 #include "utils.h"
00043 #include "variables.h"
00044 
00045 #include "DASSL-opts.cc"
00046 
00047 // Global pointer for user defined function required by dassl.
00048 static octave_function *dassl_fcn;
00049 
00050 // Global pointer for optional user defined jacobian function.
00051 static octave_function *dassl_jac;
00052 
00053 // Have we warned about imaginary values returned from user function?
00054 static bool warned_fcn_imaginary = false;
00055 static bool warned_jac_imaginary = false;
00056 
00057 // Is this a recursive call?
00058 static int call_depth = 0;
00059 
00060 ColumnVector
00061 dassl_user_function (const ColumnVector& x, const ColumnVector& xdot,
00062                      double t, octave_idx_type& ires)
00063 {
00064   ColumnVector retval;
00065 
00066   assert (x.capacity () == xdot.capacity ());
00067 
00068   octave_value_list args;
00069 
00070   args(2) = t;
00071   args(1) = xdot;
00072   args(0) = x;
00073 
00074   if (dassl_fcn)
00075     {
00076       octave_value_list tmp = dassl_fcn->do_multi_index_op (1, args);
00077 
00078       if (error_state)
00079         {
00080           gripe_user_supplied_eval ("dassl");
00081           return retval;
00082         }
00083 
00084       int tlen = tmp.length ();
00085       if (tlen > 0 && tmp(0).is_defined ())
00086         {
00087           if (! warned_fcn_imaginary && tmp(0).is_complex_type ())
00088             {
00089               warning ("dassl: ignoring imaginary part returned from user-supplied function");
00090               warned_fcn_imaginary = true;
00091             }
00092 
00093           retval = ColumnVector (tmp(0).vector_value ());
00094 
00095           if (tlen > 1)
00096             ires = tmp(1).int_value ();
00097 
00098           if (error_state || retval.length () == 0)
00099             gripe_user_supplied_eval ("dassl");
00100         }
00101       else
00102         gripe_user_supplied_eval ("dassl");
00103     }
00104 
00105   return retval;
00106 }
00107 
00108 Matrix
00109 dassl_user_jacobian (const ColumnVector& x, const ColumnVector& xdot,
00110                      double t, double cj)
00111 {
00112   Matrix retval;
00113 
00114   assert (x.capacity () == xdot.capacity ());
00115 
00116   octave_value_list args;
00117 
00118   args(3) = cj;
00119   args(2) = t;
00120   args(1) = xdot;
00121   args(0) = x;
00122 
00123   if (dassl_jac)
00124     {
00125       octave_value_list tmp = dassl_jac->do_multi_index_op (1, args);
00126 
00127       if (error_state)
00128         {
00129           gripe_user_supplied_eval ("dassl");
00130           return retval;
00131         }
00132 
00133       int tlen = tmp.length ();
00134       if (tlen > 0 && tmp(0).is_defined ())
00135         {
00136           if (! warned_jac_imaginary && tmp(0).is_complex_type ())
00137             {
00138               warning ("dassl: ignoring imaginary part returned from user-supplied jacobian function");
00139               warned_jac_imaginary = true;
00140             }
00141 
00142           retval = tmp(0).matrix_value ();
00143 
00144           if (error_state || retval.length () == 0)
00145             gripe_user_supplied_eval ("dassl");
00146         }
00147       else
00148         gripe_user_supplied_eval ("dassl");
00149     }
00150 
00151   return retval;
00152 }
00153 
00154 #define DASSL_ABORT() \
00155   return retval
00156 
00157 #define DASSL_ABORT1(msg) \
00158   do \
00159     { \
00160       ::error ("dassl: " msg); \
00161       DASSL_ABORT (); \
00162     } \
00163   while (0)
00164 
00165 #define DASSL_ABORT2(fmt, arg) \
00166   do \
00167     { \
00168       ::error ("dassl: " fmt, arg); \
00169       DASSL_ABORT (); \
00170     } \
00171   while (0)
00172 
00173 DEFUN_DLD (dassl, args, nargout,
00174   "-*- texinfo -*-\n\
00175 @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\
00176 Solve the set of differential-algebraic equations\n\
00177 @tex\n\
00178 $$ 0 = f (x, \\dot{x}, t) $$\n\
00179 with\n\
00180 $$ x(t_0) = x_0, \\dot{x}(t_0) = \\dot{x}_0 $$\n\
00181 @end tex\n\
00182 @ifnottex\n\
00183 \n\
00184 @example\n\
00185 0 = f (x, xdot, t)\n\
00186 @end example\n\
00187 \n\
00188 @noindent\n\
00189 with\n\
00190 \n\
00191 @example\n\
00192 x(t_0) = x_0, xdot(t_0) = xdot_0\n\
00193 @end example\n\
00194 \n\
00195 @end ifnottex\n\
00196 The solution is returned in the matrices @var{x} and @var{xdot},\n\
00197 with each row in the result matrices corresponding to one of the\n\
00198 elements in the vector @var{t}.  The first element of @var{t}\n\
00199 should be @math{t_0} and correspond to the initial state of the\n\
00200 system @var{x_0} and its derivative @var{xdot_0}, so that the first\n\
00201 row of the output @var{x} is @var{x_0} and the first row\n\
00202 of the output @var{xdot} is @var{xdot_0}.\n\
00203 \n\
00204 The first argument, @var{fcn}, is a string, inline, or function handle\n\
00205 that names the function @math{f} to call to compute the vector of\n\
00206 residuals for the set of equations.  It must have the form\n\
00207 \n\
00208 @example\n\
00209 @var{res} = f (@var{x}, @var{xdot}, @var{t})\n\
00210 @end example\n\
00211 \n\
00212 @noindent\n\
00213 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a\n\
00214 scalar.\n\
00215 \n\
00216 If @var{fcn} is a two-element string array or a two-element cell array\n\
00217 of strings, inline functions, or function handles, the first element names\n\
00218 the function @math{f} described above, and the second element names a\n\
00219 function to compute the modified Jacobian\n\
00220 \n\
00221 @tex\n\
00222 $$\n\
00223 J = {\\partial f \\over \\partial x}\n\
00224   + c {\\partial f \\over \\partial \\dot{x}}\n\
00225 $$\n\
00226 @end tex\n\
00227 @ifnottex\n\
00228 \n\
00229 @example\n\
00230 @group\n\
00231       df       df\n\
00232 jac = -- + c ------\n\
00233       dx     d xdot\n\
00234 @end group\n\
00235 @end example\n\
00236 \n\
00237 @end ifnottex\n\
00238 \n\
00239 The modified Jacobian function must have the form\n\
00240 \n\
00241 @example\n\
00242 @group\n\
00243 \n\
00244 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})\n\
00245 \n\
00246 @end group\n\
00247 @end example\n\
00248 \n\
00249 The second and third arguments to @code{dassl} specify the initial\n\
00250 condition of the states and their derivatives, and the fourth argument\n\
00251 specifies a vector of output times at which the solution is desired,\n\
00252 including the time corresponding to the initial condition.\n\
00253 \n\
00254 The set of initial states and derivatives are not strictly required to\n\
00255 be consistent.  In practice, however, @sc{dassl} is not very good at\n\
00256 determining a consistent set for you, so it is best if you ensure that\n\
00257 the initial values result in the function evaluating to zero.\n\
00258 \n\
00259 The fifth argument is optional, and may be used to specify a set of\n\
00260 times that the DAE solver should not integrate past.  It is useful for\n\
00261 avoiding difficulties with singularities and points where there is a\n\
00262 discontinuity in the derivative.\n\
00263 \n\
00264 After a successful computation, the value of @var{istate} will be\n\
00265 greater than zero (consistent with the Fortran version of @sc{dassl}).\n\
00266 \n\
00267 If the computation is not successful, the value of @var{istate} will be\n\
00268 less than zero and @var{msg} will contain additional information.\n\
00269 \n\
00270 You can use the function @code{dassl_options} to set optional\n\
00271 parameters for @code{dassl}.\n\
00272 @seealso{daspk, dasrt, lsode}\n\
00273 @end deftypefn")
00274 {
00275   octave_value_list retval;
00276 
00277   warned_fcn_imaginary = false;
00278   warned_jac_imaginary = false;
00279 
00280   unwind_protect frame;
00281 
00282   frame.protect_var (call_depth);
00283   call_depth++;
00284 
00285   if (call_depth > 1)
00286     DASSL_ABORT1 ("invalid recursive call");
00287 
00288   int nargin = args.length ();
00289 
00290   if (nargin > 3 && nargin < 6 && nargout < 5)
00291     {
00292       std::string fcn_name, fname, jac_name, jname;
00293       dassl_fcn = 0;
00294       dassl_jac = 0;
00295 
00296       octave_value f_arg = args(0);
00297 
00298       if (f_arg.is_cell ())
00299         {
00300           Cell c = f_arg.cell_value ();
00301           if (c.length() == 1)
00302             f_arg = c(0);
00303           else if (c.length() == 2)
00304             {
00305               if (c(0).is_function_handle () || c(0).is_inline_function ())
00306                 dassl_fcn = c(0).function_value ();
00307               else
00308                 {
00309                   fcn_name = unique_symbol_name ("__dassl_fcn__");
00310                   fname = "function y = ";
00311                   fname.append (fcn_name);
00312                   fname.append (" (x, xdot, t) y = ");
00313                   dassl_fcn = extract_function
00314                     (c(0), "dassl", fcn_name, fname, "; endfunction");
00315                 }
00316 
00317               if (dassl_fcn)
00318                 {
00319                   if (c(1).is_function_handle () || c(1).is_inline_function ())
00320                     dassl_jac = c(1).function_value ();
00321                   else
00322                     {
00323                         jac_name = unique_symbol_name ("__dassl_jac__");
00324                         jname = "function jac = ";
00325                         jname.append(jac_name);
00326                         jname.append (" (x, xdot, t, cj) jac = ");
00327                         dassl_jac = extract_function
00328                           (c(1), "dassl", jac_name, jname, "; endfunction");
00329 
00330                         if (!dassl_jac)
00331                           {
00332                             if (fcn_name.length())
00333                               clear_function (fcn_name);
00334                             dassl_fcn = 0;
00335                           }
00336                     }
00337                 }
00338             }
00339           else
00340             DASSL_ABORT1 ("incorrect number of elements in cell array");
00341         }
00342 
00343       if (!dassl_fcn && ! f_arg.is_cell())
00344         {
00345           if (f_arg.is_function_handle () || f_arg.is_inline_function ())
00346             dassl_fcn = f_arg.function_value ();
00347           else
00348             {
00349               switch (f_arg.rows ())
00350                 {
00351                 case 1:
00352                   do
00353                     {
00354                       fcn_name = unique_symbol_name ("__dassl_fcn__");
00355                       fname = "function y = ";
00356                       fname.append (fcn_name);
00357                       fname.append (" (x, xdot, t) y = ");
00358                       dassl_fcn = extract_function
00359                         (f_arg, "dassl", fcn_name, fname, "; endfunction");
00360                     }
00361                   while (0);
00362                   break;
00363 
00364                 case 2:
00365                   {
00366                     string_vector tmp = f_arg.all_strings ();
00367 
00368                     if (! error_state)
00369                       {
00370                         fcn_name = unique_symbol_name ("__dassl_fcn__");
00371                         fname = "function y = ";
00372                         fname.append (fcn_name);
00373                         fname.append (" (x, xdot, t) y = ");
00374                         dassl_fcn = extract_function
00375                           (tmp(0), "dassl", fcn_name, fname, "; endfunction");
00376 
00377                         if (dassl_fcn)
00378                           {
00379                             jac_name = unique_symbol_name ("__dassl_jac__");
00380                             jname = "function jac = ";
00381                             jname.append(jac_name);
00382                             jname.append (" (x, xdot, t, cj) jac = ");
00383                             dassl_jac = extract_function
00384                               (tmp(1), "dassl", jac_name, jname,
00385                                "; endfunction");
00386 
00387                             if (!dassl_jac)
00388                               {
00389                                 if (fcn_name.length())
00390                                   clear_function (fcn_name);
00391                                 dassl_fcn = 0;
00392                               }
00393                           }
00394                       }
00395                   }
00396                 }
00397             }
00398         }
00399 
00400       if (error_state || ! dassl_fcn)
00401         DASSL_ABORT ();
00402 
00403       ColumnVector state = ColumnVector (args(1).vector_value ());
00404 
00405       if (error_state)
00406         DASSL_ABORT1 ("expecting state vector as second argument");
00407 
00408       ColumnVector deriv (args(2).vector_value ());
00409 
00410       if (error_state)
00411         DASSL_ABORT1 ("expecting derivative vector as third argument");
00412 
00413       ColumnVector out_times (args(3).vector_value ());
00414 
00415       if (error_state)
00416         DASSL_ABORT1 ("expecting output time vector as fourth argument");
00417 
00418       ColumnVector crit_times;
00419       int crit_times_set = 0;
00420       if (nargin > 4)
00421         {
00422           crit_times = ColumnVector (args(4).vector_value ());
00423 
00424           if (error_state)
00425             DASSL_ABORT1 ("expecting critical time vector as fifth argument");
00426 
00427           crit_times_set = 1;
00428         }
00429 
00430       if (state.capacity () != deriv.capacity ())
00431         DASSL_ABORT1 ("x and xdot must have the same size");
00432 
00433       double tzero = out_times (0);
00434 
00435       DAEFunc func (dassl_user_function);
00436       if (dassl_jac)
00437         func.set_jacobian_function (dassl_user_jacobian);
00438 
00439       DASSL dae (state, deriv, tzero, func);
00440 
00441       dae.set_options (dassl_opts);
00442 
00443       Matrix output;
00444       Matrix deriv_output;
00445 
00446       if (crit_times_set)
00447         output = dae.integrate (out_times, deriv_output, crit_times);
00448       else
00449         output = dae.integrate (out_times, deriv_output);
00450 
00451       if (fcn_name.length())
00452         clear_function (fcn_name);
00453       if (jac_name.length())
00454         clear_function (jac_name);
00455 
00456       if (! error_state)
00457         {
00458           std::string msg = dae.error_message ();
00459 
00460           retval(3) = msg;
00461           retval(2) = static_cast<double> (dae.integration_state ());
00462 
00463           if (dae.integration_ok ())
00464             {
00465               retval(1) = deriv_output;
00466               retval(0) = output;
00467             }
00468           else
00469             {
00470               retval(1) = Matrix ();
00471               retval(0) = Matrix ();
00472 
00473               if (nargout < 3)
00474                 error ("dassl: %s", msg.c_str ());
00475             }
00476         }
00477     }
00478   else
00479     print_usage ();
00480 
00481   return retval;
00482 }
00483 
00484 /*
00485 
00486 %% dassl-1.m
00487 %%
00488 %% Test dassl() function
00489 %%
00490 %% Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
00491 %%         Comalco Research and Technology
00492 %%         20 May 1998
00493 %%
00494 %% Problem
00495 %%
00496 %%    y1' = -y2,   y1(0) = 1
00497 %%    y2' =  y1,   y2(0) = 0
00498 %%
00499 %% Solution
00500 %%
00501 %%    y1(t) = cos(t)
00502 %%    y2(t) = sin(t)
00503 
00504 %!function res = __f (x, xdot, t)
00505 %!  res = [xdot(1)+x(2); xdot(2)-x(1)];
00506 %!endfunction
00507 
00508 %!test
00509 %!
00510 %! x0 = [1; 0];
00511 %! xdot0 = [0; 1];
00512 %! t = (0:1:10)';
00513 %!
00514 %! tol = 100 * dassl_options ("relative tolerance");
00515 %!
00516 %!
00517 %! [x, xdot] = dassl ("__f", x0, xdot0, t);
00518 %!
00519 %! y = [cos(t), sin(t)];
00520 %!
00521 %! assert(all (all (abs (x - y) < tol)));
00522 
00523 %% dassl-2.m
00524 %%
00525 %% Test dassl() function
00526 %%
00527 %% Author: David Billinghurst (David.Billinghurst@riotinto.com.au)
00528 %%         Comalco Research and Technology
00529 %%         20 May 1998
00530 %%
00531 %% Based on SLATEC quick check for DASSL by Linda Petzold
00532 %%
00533 %% Problem
00534 %%
00535 %%   x1' + 10*x1 = 0,   x1(0) = 1
00536 %%   x1  + x2    = 1,   x2(0) = 0
00537 %%
00538 %%
00539 %% Solution
00540 %%
00541 %%  x1(t) = exp(-10*t)
00542 %%  x2(t) = 1 - x(1)
00543 
00544 %!function res = __f (x, xdot, t)
00545 %!  res = [xdot(1)+10*x(1); x(1)+x(2)-1];
00546 %!endfunction
00547 
00548 %!test
00549 %!
00550 %! x0 = [1; 0];
00551 %! xdot0 = [-10; 10];
00552 %! t = (0:0.2:1)';
00553 %!
00554 %! tol = 500 * dassl_options ("relative tolerance");
00555 %!
00556 %!
00557 %! [x, xdot] = dassl ("__f", x0, xdot0, t);
00558 %!
00559 %! y = [exp(-10*t), 1-exp(-10*t)];
00560 %!
00561 %! assert(all (all (abs (x - y) < tol)));
00562 
00563 %!test
00564 %! dassl_options ("absolute tolerance", eps);
00565 %! assert(dassl_options ("absolute tolerance") == eps);
00566 
00567 %!error <Invalid call to dassl_options> dassl_options ("foo", 1, 2);
00568 
00569 */
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines