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 "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
00048 static octave_function *dassl_fcn;
00049
00050
00051 static octave_function *dassl_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 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
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569