00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00048 static octave_function *daspk_fcn;
00049
00050
00051 static octave_function *daspk_jac;
00052
00053
00054 static bool warned_fcn_imaginary = false;
00055 static bool warned_jac_imaginary = false;
00056
00057
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 }