daspk.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 "DASPK.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 "DASPK-opts.cc"
00046 
00047 // Global pointer for user defined function required by daspk.
00048 static octave_function *daspk_fcn;
00049 
00050 // Global pointer for optional user defined jacobian function.
00051 static octave_function *daspk_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 daspk_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 (daspk_fcn)
00075     {
00076       octave_value_list tmp = daspk_fcn->do_multi_index_op (1, args);
00077 
00078       if (error_state)
00079         {
00080           gripe_user_supplied_eval ("daspk");
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 ("daspk: 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 ("daspk");
00100         }
00101       else
00102         gripe_user_supplied_eval ("daspk");
00103     }
00104 
00105   return retval;
00106 }
00107 
00108 Matrix
00109 daspk_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 (daspk_jac)
00124     {
00125       octave_value_list tmp = daspk_jac->do_multi_index_op (1, args);
00126 
00127       if (error_state)
00128         {
00129           gripe_user_supplied_eval ("daspk");
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 ("daspk: 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 ("daspk");
00146         }
00147       else
00148         gripe_user_supplied_eval ("daspk");
00149     }
00150 
00151   return retval;
00152 }
00153 
00154 #define DASPK_ABORT() \
00155   return retval
00156 
00157 #define DASPK_ABORT1(msg) \
00158   do \
00159     { \
00160       ::error ("daspk: " msg); \
00161       DASPK_ABORT (); \
00162     } \
00163   while (0)
00164 
00165 #define DASPK_ABORT2(fmt, arg) \
00166   do \
00167     { \
00168       ::error ("daspk: " fmt, arg); \
00169       DASPK_ABORT (); \
00170     } \
00171   while (0)
00172 
00173 DEFUN_DLD (daspk, args, nargout,
00174   "-*- texinfo -*-\n\
00175 @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\
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 @tex\n\
00221 $$\n\
00222 J = {\\partial f \\over \\partial x}\n\
00223   + c {\\partial f \\over \\partial \\dot{x}}\n\
00224 $$\n\
00225 @end tex\n\
00226 @ifnottex\n\
00227 \n\
00228 @example\n\
00229 @group\n\
00230       df       df\n\
00231 jac = -- + c ------\n\
00232       dx     d xdot\n\
00233 @end group\n\
00234 @end example\n\
00235 \n\
00236 @end ifnottex\n\
00237 \n\
00238 The modified Jacobian function must have the form\n\
00239 \n\
00240 @example\n\
00241 @group\n\
00242 \n\
00243 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})\n\
00244 \n\
00245 @end group\n\
00246 @end example\n\
00247 \n\
00248 The second and third arguments to @code{daspk} specify the initial\n\
00249 condition of the states and their derivatives, and the fourth argument\n\
00250 specifies a vector of output times at which the solution is desired,\n\
00251 including the time corresponding to the initial condition.\n\
00252 \n\
00253 The set of initial states and derivatives are not strictly required to\n\
00254 be consistent.  If they are not consistent, you must use the\n\
00255 @code{daspk_options} function to provide additional information so\n\
00256 that @code{daspk} can compute a consistent starting point.\n\
00257 \n\
00258 The fifth argument is optional, and may be used to specify a set of\n\
00259 times that the DAE solver should not integrate past.  It is useful for\n\
00260 avoiding difficulties with singularities and points where there is a\n\
00261 discontinuity in the derivative.\n\
00262 \n\
00263 After a successful computation, the value of @var{istate} will be\n\
00264 greater than zero (consistent with the Fortran version of @sc{daspk}).\n\
00265 \n\
00266 If the computation is not successful, the value of @var{istate} will be\n\
00267 less than zero and @var{msg} will contain additional information.\n\
00268 \n\
00269 You can use the function @code{daspk_options} to set optional\n\
00270 parameters for @code{daspk}.\n\
00271 @seealso{dassl}\n\
00272 @end deftypefn")
00273 {
00274   octave_value_list retval;
00275 
00276   warned_fcn_imaginary = false;
00277   warned_jac_imaginary = false;
00278 
00279   unwind_protect frame;
00280 
00281   frame.protect_var (call_depth);
00282   call_depth++;
00283 
00284   if (call_depth > 1)
00285     DASPK_ABORT1 ("invalid recursive call");
00286 
00287   int nargin = args.length ();
00288 
00289   if (nargin > 3 && nargin < 6)
00290     {
00291       std::string fcn_name, fname, jac_name, jname;
00292       daspk_fcn = 0;
00293       daspk_jac = 0;
00294 
00295       octave_value f_arg = args(0);
00296 
00297       if (f_arg.is_cell ())
00298         {
00299           Cell c = f_arg.cell_value ();
00300           if (c.length() == 1)
00301             f_arg = c(0);
00302           else if (c.length() == 2)
00303             {
00304               if (c(0).is_function_handle () || c(0).is_inline_function ())
00305                 daspk_fcn = c(0).function_value ();
00306               else
00307                 {
00308                   fcn_name = unique_symbol_name ("__daspk_fcn__");
00309                   fname = "function y = ";
00310                   fname.append (fcn_name);
00311                   fname.append (" (x, xdot, t) y = ");
00312                   daspk_fcn = extract_function
00313                     (c(0), "daspk", fcn_name, fname, "; endfunction");
00314                 }
00315 
00316               if (daspk_fcn)
00317                 {
00318                   if (c(1).is_function_handle () || c(1).is_inline_function ())
00319                     daspk_jac = c(1).function_value ();
00320                   else
00321                     {
00322                       jac_name = unique_symbol_name ("__daspk_jac__");
00323                       jname = "function jac = ";
00324                       jname.append(jac_name);
00325                       jname.append (" (x, xdot, t, cj) jac = ");
00326                       daspk_jac = extract_function
00327                         (c(1), "daspk", jac_name, jname, "; endfunction");
00328 
00329                       if (!daspk_jac)
00330                         {
00331                           if (fcn_name.length())
00332                             clear_function (fcn_name);
00333                           daspk_fcn = 0;
00334                         }
00335                     }
00336                 }
00337             }
00338           else
00339             DASPK_ABORT1 ("incorrect number of elements in cell array");
00340         }
00341 
00342       if (!daspk_fcn && ! f_arg.is_cell())
00343         {
00344           if (f_arg.is_function_handle () || f_arg.is_inline_function ())
00345             daspk_fcn = f_arg.function_value ();
00346           else
00347             {
00348               switch (f_arg.rows ())
00349                 {
00350                 case 1:
00351                   do
00352                     {
00353                       fcn_name = unique_symbol_name ("__daspk_fcn__");
00354                       fname = "function y = ";
00355                       fname.append (fcn_name);
00356                       fname.append (" (x, xdot, t) y = ");
00357                       daspk_fcn = extract_function
00358                         (f_arg, "daspk", fcn_name, fname, "; endfunction");
00359                     }
00360                   while (0);
00361                   break;
00362 
00363                 case 2:
00364                   {
00365                     string_vector tmp = f_arg.all_strings ();
00366 
00367                     if (! error_state)
00368                       {
00369                         fcn_name = unique_symbol_name ("__daspk_fcn__");
00370                         fname = "function y = ";
00371                         fname.append (fcn_name);
00372                         fname.append (" (x, xdot, t) y = ");
00373                         daspk_fcn = extract_function
00374                           (tmp(0), "daspk", fcn_name, fname, "; endfunction");
00375 
00376                         if (daspk_fcn)
00377                           {
00378                             jac_name = unique_symbol_name ("__daspk_jac__");
00379                             jname = "function jac = ";
00380                             jname.append(jac_name);
00381                             jname.append (" (x, xdot, t, cj) jac = ");
00382                             daspk_jac = extract_function
00383                               (tmp(1), "daspk", jac_name, jname,
00384                                "; endfunction");
00385 
00386                             if (!daspk_jac)
00387                               {
00388                                 if (fcn_name.length())
00389                                   clear_function (fcn_name);
00390                                 daspk_fcn = 0;
00391                               }
00392                           }
00393                       }
00394                   }
00395                 }
00396             }
00397         }
00398 
00399       if (error_state || ! daspk_fcn)
00400         DASPK_ABORT ();
00401 
00402       ColumnVector state = ColumnVector (args(1).vector_value ());
00403 
00404       if (error_state)
00405         DASPK_ABORT1 ("expecting state vector as second argument");
00406 
00407       ColumnVector deriv (args(2).vector_value ());
00408 
00409       if (error_state)
00410         DASPK_ABORT1 ("expecting derivative vector as third argument");
00411 
00412       ColumnVector out_times (args(3).vector_value ());
00413 
00414       if (error_state)
00415         DASPK_ABORT1 ("expecting output time vector as fourth argument");
00416 
00417       ColumnVector crit_times;
00418       int crit_times_set = 0;
00419       if (nargin > 4)
00420         {
00421           crit_times = ColumnVector (args(4).vector_value ());
00422 
00423           if (error_state)
00424             DASPK_ABORT1 ("expecting critical time vector as fifth argument");
00425 
00426           crit_times_set = 1;
00427         }
00428 
00429       if (state.capacity () != deriv.capacity ())
00430         DASPK_ABORT1 ("x and xdot must have the same size");
00431 
00432       double tzero = out_times (0);
00433 
00434       DAEFunc func (daspk_user_function);
00435       if (daspk_jac)
00436         func.set_jacobian_function (daspk_user_jacobian);
00437 
00438       DASPK dae (state, deriv, tzero, func);
00439       dae.set_options (daspk_opts);
00440 
00441       Matrix output;
00442       Matrix deriv_output;
00443 
00444       if (crit_times_set)
00445         output = dae.integrate (out_times, deriv_output, crit_times);
00446       else
00447         output = dae.integrate (out_times, deriv_output);
00448 
00449       if (fcn_name.length())
00450         clear_function (fcn_name);
00451       if (jac_name.length())
00452         clear_function (jac_name);
00453 
00454       if (! error_state)
00455         {
00456           std::string msg = dae.error_message ();
00457 
00458           retval(3) = msg;
00459           retval(2) = static_cast<double> (dae.integration_state ());
00460 
00461           if (dae.integration_ok ())
00462             {
00463               retval(1) = deriv_output;
00464               retval(0) = output;
00465             }
00466           else
00467             {
00468               retval(1) = Matrix ();
00469               retval(0) = Matrix ();
00470 
00471               if (nargout < 3)
00472                 error ("daspk: %s", msg.c_str ());
00473             }
00474         }
00475     }
00476   else
00477     print_usage ();
00478 
00479   return retval;
00480 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines