LSODE.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1993-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 <cfloat>
00028 
00029 #include <sstream>
00030 
00031 #include "LSODE.h"
00032 #include "f77-fcn.h"
00033 #include "lo-error.h"
00034 #include "lo-math.h"
00035 #include "quit.h"
00036 
00037 typedef octave_idx_type (*lsode_fcn_ptr) (const octave_idx_type&,
00038                                           const double&, double*,
00039                                           double*, octave_idx_type&);
00040 
00041 typedef octave_idx_type (*lsode_jac_ptr) (const octave_idx_type&,
00042                                           const double&, double*,
00043                                           const octave_idx_type&,
00044                                           const octave_idx_type&,
00045                                           double*, const octave_idx_type&);
00046 
00047 extern "C"
00048 {
00049   F77_RET_T
00050   F77_FUNC (dlsode, DLSODE) (lsode_fcn_ptr, octave_idx_type&, double*,
00051                              double&, double&, octave_idx_type&, double&,
00052                              const double*, octave_idx_type&,
00053                              octave_idx_type&, octave_idx_type&,
00054                              double*, octave_idx_type&, octave_idx_type*,
00055                              octave_idx_type&, lsode_jac_ptr,
00056                              octave_idx_type&);
00057 }
00058 
00059 static ODEFunc::ODERHSFunc user_fun;
00060 static ODEFunc::ODEJacFunc user_jac;
00061 static ColumnVector *tmp_x;
00062 
00063 static octave_idx_type
00064 lsode_f (const octave_idx_type& neq, const double& time, double *,
00065          double *deriv, octave_idx_type& ierr)
00066 {
00067   BEGIN_INTERRUPT_WITH_EXCEPTIONS;
00068 
00069   ColumnVector tmp_deriv;
00070 
00071   // NOTE: this won't work if LSODE passes copies of the state vector.
00072   //       In that case we have to create a temporary vector object
00073   //       and copy.
00074 
00075   tmp_deriv = (*user_fun) (*tmp_x, time);
00076 
00077   if (tmp_deriv.length () == 0)
00078     ierr = -1;
00079   else
00080     {
00081       for (octave_idx_type i = 0; i < neq; i++)
00082         deriv [i] = tmp_deriv.elem (i);
00083     }
00084 
00085   END_INTERRUPT_WITH_EXCEPTIONS;
00086 
00087   return 0;
00088 }
00089 
00090 static octave_idx_type
00091 lsode_j (const octave_idx_type& neq, const double& time, double *,
00092          const octave_idx_type&, const octave_idx_type&, double *pd, const octave_idx_type& nrowpd)
00093 {
00094   BEGIN_INTERRUPT_WITH_EXCEPTIONS;
00095 
00096   Matrix tmp_jac (neq, neq);
00097 
00098   // NOTE: this won't work if LSODE passes copies of the state vector.
00099   //       In that case we have to create a temporary vector object
00100   //       and copy.
00101 
00102   tmp_jac = (*user_jac) (*tmp_x, time);
00103 
00104   for (octave_idx_type j = 0; j < neq; j++)
00105     for (octave_idx_type i = 0; i < neq; i++)
00106       pd [nrowpd * j + i] = tmp_jac (i, j);
00107 
00108   END_INTERRUPT_WITH_EXCEPTIONS;
00109 
00110   return 0;
00111 }
00112 
00113 ColumnVector
00114 LSODE::do_integrate (double tout)
00115 {
00116   ColumnVector retval;
00117 
00118   static octave_idx_type nn = 0;
00119 
00120   if (! initialized || restart || ODEFunc::reset || LSODE_options::reset)
00121     {
00122       integration_error = false;
00123 
00124       initialized = true;
00125 
00126       istate = 1;
00127 
00128       octave_idx_type n = size ();
00129 
00130       nn = n;
00131 
00132       octave_idx_type max_maxord = 0;
00133 
00134       if (integration_method () == "stiff")
00135         {
00136           max_maxord = 5;
00137 
00138           if (jac)
00139             method_flag = 21;
00140           else
00141             method_flag = 22;
00142 
00143           liw = 20 + n;
00144           lrw = 22 + n * (9 + n);
00145         }
00146       else
00147         {
00148           max_maxord = 12;
00149 
00150           method_flag = 10;
00151 
00152           liw = 20;
00153           lrw = 22 + 16 * n;
00154         }
00155 
00156       maxord = maximum_order ();
00157 
00158       iwork.resize (dim_vector (liw, 1));
00159 
00160       for (octave_idx_type i = 4; i < 9; i++)
00161         iwork(i) = 0;
00162 
00163       rwork.resize (dim_vector (lrw, 1));
00164 
00165       for (octave_idx_type i = 4; i < 9; i++)
00166         rwork(i) = 0;
00167 
00168       if (maxord >= 0)
00169         {
00170           if (maxord > 0 && maxord <= max_maxord)
00171             {
00172               iwork(4) = maxord;
00173               iopt = 1;
00174             }
00175           else
00176             {
00177               (*current_liboctave_error_handler)
00178                 ("lsode: invalid value for maximum order");
00179               integration_error = true;
00180               return retval;
00181             }
00182         }
00183 
00184       if (stop_time_set)
00185         {
00186           itask = 4;
00187           rwork(0) = stop_time;
00188           iopt = 1;
00189         }
00190       else
00191         {
00192           itask = 1;
00193         }
00194 
00195       restart = false;
00196 
00197       // ODEFunc
00198 
00199       // NOTE: this won't work if LSODE passes copies of the state vector.
00200       //       In that case we have to create a temporary vector object
00201       //       and copy.
00202 
00203       tmp_x = &x;
00204 
00205       user_fun = function ();
00206       user_jac = jacobian_function ();
00207 
00208       ColumnVector xdot = (*user_fun) (x, t);
00209 
00210       if (x.length () != xdot.length ())
00211         {
00212           (*current_liboctave_error_handler)
00213             ("lsode: inconsistent sizes for state and derivative vectors");
00214 
00215           integration_error = true;
00216           return retval;
00217         }
00218 
00219       ODEFunc::reset = false;
00220 
00221       // LSODE_options
00222 
00223       rel_tol = relative_tolerance ();
00224       abs_tol = absolute_tolerance ();
00225 
00226       octave_idx_type abs_tol_len = abs_tol.length ();
00227 
00228       if (abs_tol_len == 1)
00229         itol = 1;
00230       else if (abs_tol_len == n)
00231         itol = 2;
00232       else
00233         {
00234           (*current_liboctave_error_handler)
00235             ("lsode: inconsistent sizes for state and absolute tolerance vectors");
00236 
00237           integration_error = true;
00238           return retval;
00239         }
00240 
00241       double iss = initial_step_size ();
00242       if (iss >= 0.0)
00243         {
00244           rwork(4) = iss;
00245           iopt = 1;
00246         }
00247 
00248       double maxss = maximum_step_size ();
00249       if (maxss >= 0.0)
00250         {
00251           rwork(5) = maxss;
00252           iopt = 1;
00253         }
00254 
00255       double minss = minimum_step_size ();
00256       if (minss >= 0.0)
00257         {
00258           rwork(6) = minss;
00259           iopt = 1;
00260         }
00261 
00262       octave_idx_type sl = step_limit ();
00263       if (sl > 0)
00264         {
00265           iwork(5) = sl;
00266           iopt = 1;
00267         }
00268 
00269       LSODE_options::reset = false;
00270     }
00271 
00272   double *px = x.fortran_vec ();
00273 
00274   double *pabs_tol = abs_tol.fortran_vec ();
00275 
00276   octave_idx_type *piwork = iwork.fortran_vec ();
00277   double *prwork = rwork.fortran_vec ();
00278 
00279   F77_XFCN (dlsode, DLSODE, (lsode_f, nn, px, t, tout, itol, rel_tol,
00280                              pabs_tol, itask, istate, iopt, prwork, lrw,
00281                              piwork, liw, lsode_j, method_flag));
00282 
00283   switch (istate)
00284     {
00285     case 1:  // prior to initial integration step.
00286     case 2:  // lsode was successful.
00287       retval = x;
00288       t = tout;
00289       break;
00290 
00291     case -1:  // excess work done on this call (perhaps wrong mf).
00292     case -2:  // excess accuracy requested (tolerances too small).
00293     case -3:  // invalid input detected (see printed message).
00294     case -4:  // repeated error test failures (check all inputs).
00295     case -5:  // repeated convergence failures (perhaps bad Jacobian
00296               // supplied or wrong choice of mf or tolerances).
00297     case -6:  // error weight became zero during problem. (solution
00298               // component i vanished, and atol or atol(i) = 0.)
00299     case -13: // return requested in user-supplied function.
00300       integration_error = true;
00301       break;
00302 
00303     default:
00304       integration_error = true;
00305       (*current_liboctave_error_handler)
00306         ("unrecognized value of istate (= %d) returned from lsode",
00307          istate);
00308       break;
00309     }
00310 
00311   return retval;
00312 }
00313 
00314 std::string
00315 LSODE::error_message (void) const
00316 {
00317   std::string retval;
00318 
00319   std::ostringstream buf;
00320   buf << t;
00321   std::string t_curr = buf.str ();
00322 
00323   switch (istate)
00324     {
00325     case 1:
00326       retval = "prior to initial integration step";
00327       break;
00328 
00329     case 2:
00330       retval = "successful exit";
00331       break;
00332 
00333     case 3:
00334       retval = "prior to continuation call with modified parameters";
00335       break;
00336 
00337     case -1:
00338       retval = std::string ("excess work on this call (t = ")
00339         + t_curr + "; perhaps wrong integration method)";
00340       break;
00341 
00342     case -2:
00343       retval = "excess accuracy requested (tolerances too small)";
00344       break;
00345 
00346     case -3:
00347       retval = "invalid input detected (see printed message)";
00348       break;
00349 
00350     case -4:
00351       retval = std::string ("repeated error test failures (t = ")
00352         + t_curr + "; check all inputs)";
00353       break;
00354 
00355     case -5:
00356       retval = std::string ("repeated convergence failures (t = ")
00357         + t_curr
00358         + "; perhaps bad Jacobian supplied or wrong choice of integration method or tolerances)";
00359       break;
00360 
00361     case -6:
00362       retval = std::string ("error weight became zero during problem. (t = ")
00363         + t_curr
00364         + "; solution component i vanished, and atol or atol(i) == 0)";
00365       break;
00366 
00367     case -13:
00368       retval = "return requested in user-supplied function (t = "
00369         + t_curr + ")";
00370       break;
00371 
00372     default:
00373       retval = "unknown error state";
00374       break;
00375     }
00376 
00377   return retval;
00378 }
00379 
00380 Matrix
00381 LSODE::do_integrate (const ColumnVector& tout)
00382 {
00383   Matrix retval;
00384 
00385   octave_idx_type n_out = tout.capacity ();
00386   octave_idx_type n = size ();
00387 
00388   if (n_out > 0 && n > 0)
00389     {
00390       retval.resize (n_out, n);
00391 
00392       for (octave_idx_type i = 0; i < n; i++)
00393         retval.elem (0, i) = x.elem (i);
00394 
00395       for (octave_idx_type j = 1; j < n_out; j++)
00396         {
00397           ColumnVector x_next = do_integrate (tout.elem (j));
00398 
00399           if (integration_error)
00400             return retval;
00401 
00402           for (octave_idx_type i = 0; i < n; i++)
00403             retval.elem (j, i) = x_next.elem (i);
00404         }
00405     }
00406 
00407   return retval;
00408 }
00409 
00410 Matrix
00411 LSODE::do_integrate (const ColumnVector& tout, const ColumnVector& tcrit)
00412 {
00413   Matrix retval;
00414 
00415   octave_idx_type n_out = tout.capacity ();
00416   octave_idx_type n = size ();
00417 
00418   if (n_out > 0 && n > 0)
00419     {
00420       retval.resize (n_out, n);
00421 
00422       for (octave_idx_type i = 0; i < n; i++)
00423         retval.elem (0, i) = x.elem (i);
00424 
00425       octave_idx_type n_crit = tcrit.capacity ();
00426 
00427       if (n_crit > 0)
00428         {
00429           octave_idx_type i_crit = 0;
00430           octave_idx_type i_out = 1;
00431           double next_crit = tcrit.elem (0);
00432           double next_out;
00433           while (i_out < n_out)
00434             {
00435               bool do_restart = false;
00436 
00437               next_out = tout.elem (i_out);
00438               if (i_crit < n_crit)
00439                 next_crit = tcrit.elem (i_crit);
00440 
00441               octave_idx_type save_output;
00442               double t_out;
00443 
00444               if (next_crit == next_out)
00445                 {
00446                   set_stop_time (next_crit);
00447                   t_out = next_out;
00448                   save_output = 1;
00449                   i_out++;
00450                   i_crit++;
00451                   do_restart = true;
00452                 }
00453               else if (next_crit < next_out)
00454                 {
00455                   if (i_crit < n_crit)
00456                     {
00457                       set_stop_time (next_crit);
00458                       t_out = next_crit;
00459                       save_output = 0;
00460                       i_crit++;
00461                       do_restart = true;
00462                     }
00463                   else
00464                     {
00465                       clear_stop_time ();
00466                       t_out = next_out;
00467                       save_output = 1;
00468                       i_out++;
00469                     }
00470                 }
00471               else
00472                 {
00473                   set_stop_time (next_crit);
00474                   t_out = next_out;
00475                   save_output = 1;
00476                   i_out++;
00477                 }
00478 
00479               ColumnVector x_next = do_integrate (t_out);
00480 
00481               if (integration_error)
00482                 return retval;
00483 
00484               if (save_output)
00485                 {
00486                   for (octave_idx_type i = 0; i < n; i++)
00487                     retval.elem (i_out-1, i) = x_next.elem (i);
00488                 }
00489 
00490               if (do_restart)
00491                 force_restart ();
00492             }
00493         }
00494       else
00495         {
00496           retval = do_integrate (tout);
00497 
00498           if (integration_error)
00499             return retval;
00500         }
00501     }
00502 
00503   return retval;
00504 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines