GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
dasrt.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2002-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <list>
31 #include <string>
32 
33 #include "DASRT.h"
34 #include "lo-mappers.h"
35 
36 #include "defun.h"
37 #include "error.h"
38 #include "errwarn.h"
39 #include "interpreter-private.h"
40 #include "ovl.h"
41 #include "ov-fcn.h"
42 #include "ov-cell.h"
43 #include "pager.h"
44 #include "parse.h"
45 #include "unwind-prot.h"
46 #include "utils.h"
47 #include "variables.h"
48 
49 #include "DASRT-opts.cc"
50 
51 // Global pointers for user defined function required by dasrt.
55 
56 // Have we warned about imaginary values returned from user function?
57 static bool warned_fcn_imaginary = false;
58 static bool warned_jac_imaginary = false;
59 static bool warned_cf_imaginary = false;
60 
61 // Is this a recursive call?
62 static int call_depth = 0;
63 
64 static ColumnVector
65 dasrt_user_f (const ColumnVector& x, const ColumnVector& xdot,
66  double t, octave_idx_type&)
67 {
69 
70  assert (x.numel () == xdot.numel ());
71 
72  octave_value_list args;
73 
74  args(2) = t;
75  args(1) = xdot;
76  args(0) = x;
77 
78  if (dasrt_fcn.is_defined ())
79  {
81 
82  try
83  {
84  tmp = octave::feval (dasrt_fcn, args, 1);
85  }
86  catch (octave::execution_exception& e)
87  {
88  err_user_supplied_eval (e, "dasrt");
89  }
90 
91  if (tmp.empty () || ! tmp(0).is_defined ())
92  err_user_supplied_eval ("dasrt");
93 
94  if (! warned_fcn_imaginary && tmp(0).iscomplex ())
95  {
96  warning ("dasrt: ignoring imaginary part returned from user-supplied function");
97  warned_fcn_imaginary = true;
98  }
99 
100  retval = tmp(0).vector_value ();
101 
102  if (retval.isempty ())
103  err_user_supplied_eval ("dasrt");
104  }
105 
106  return retval;
107 }
108 
109 static ColumnVector
110 dasrt_user_cf (const ColumnVector& x, double t)
111 {
113 
114  octave_value_list args;
115 
116  args(1) = t;
117  args(0) = x;
118 
119  if (dasrt_cf.is_defined ())
120  {
121  octave_value_list tmp;
122 
123  try
124  {
125  tmp = octave::feval (dasrt_cf, args, 1);
126  }
127  catch (octave::execution_exception& e)
128  {
129  err_user_supplied_eval (e, "dasrt");
130  }
131 
132  if (tmp.empty () || ! tmp(0).is_defined ())
133  err_user_supplied_eval ("dasrt");
134 
135  if (! warned_cf_imaginary && tmp(0).iscomplex ())
136  {
137  warning ("dasrt: ignoring imaginary part returned from user-supplied constraint function");
138  warned_cf_imaginary = true;
139  }
140 
141  retval = tmp(0).vector_value ();
142 
143  if (retval.isempty ())
144  err_user_supplied_eval ("dasrt");
145  }
146 
147  return retval;
148 }
149 
150 static Matrix
152  double t, double cj)
153 {
154  Matrix retval;
155 
156  assert (x.numel () == xdot.numel ());
157 
158  octave_value_list args;
159 
160  args(3) = cj;
161  args(2) = t;
162  args(1) = xdot;
163  args(0) = x;
164 
165  if (dasrt_jac.is_defined ())
166  {
167  octave_value_list tmp;
168 
169  try
170  {
171  tmp = octave::feval (dasrt_jac, args, 1);
172  }
173  catch (octave::execution_exception& e)
174  {
175  err_user_supplied_eval (e, "dasrt");
176  }
177 
178  int tlen = tmp.length ();
179  if (tlen == 0 || ! tmp(0).is_defined ())
180  err_user_supplied_eval ("dasrt");
181 
182  if (! warned_jac_imaginary && tmp(0).iscomplex ())
183  {
184  warning ("dasrt: ignoring imaginary part returned from user-supplied jacobian function");
185  warned_jac_imaginary = true;
186  }
187 
188  retval = tmp(0).matrix_value ();
189 
190  if (retval.isempty ())
191  err_user_supplied_eval ("dasrt");
192  }
193 
194  return retval;
195 }
196 
197 DEFMETHOD (dasrt, interp, args, nargout,
198  doc: /* -*- texinfo -*-
199 @deftypefn {} {[@var{x}, @var{xdot}, @var{t_out}, @var{istat}, @var{msg}] =} dasrt (@var{fcn}, @var{g}, @var{x_0}, @var{xdot_0}, @var{t})
200 @deftypefnx {} {@dots{} =} dasrt (@var{fcn}, @var{g}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
201 @deftypefnx {} {@dots{} =} dasrt (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t})
202 @deftypefnx {} {@dots{} =} dasrt (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
203 Solve a set of differential-algebraic equations.
204 
205 @code{dasrt} solves the set of equations
206 @tex
207 $$ 0 = f (x, \dot{x}, t) $$
208 with
209 $$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
210 @end tex
211 @ifnottex
212 
213 @example
214 0 = f (x, xdot, t)
215 @end example
216 
217 @noindent
218 with
219 
220 @example
221 x(t_0) = x_0, xdot(t_0) = xdot_0
222 @end example
223 
224 @end ifnottex
225 with functional stopping criteria (root solving).
226 
227 The solution is returned in the matrices @var{x} and @var{xdot},
228 with each row in the result matrices corresponding to one of the
229 elements in the vector @var{t_out}. The first element of @var{t}
230 should be @math{t_0} and correspond to the initial state of the
231 system @var{x_0} and its derivative @var{xdot_0}, so that the first
232 row of the output @var{x} is @var{x_0} and the first row
233 of the output @var{xdot} is @var{xdot_0}.
234 
235 The vector @var{t} provides an upper limit on the length of the
236 integration. If the stopping condition is met, the vector
237 @var{t_out} will be shorter than @var{t}, and the final element of
238 @var{t_out} will be the point at which the stopping condition was met,
239 and may not correspond to any element of the vector @var{t}.
240 
241 The first argument, @var{fcn}, is a string, inline, or function handle
242 that names the function @math{f} to call to compute the vector of
243 residuals for the set of equations. It must have the form
244 
245 @example
246 @var{res} = f (@var{x}, @var{xdot}, @var{t})
247 @end example
248 
249 @noindent
250 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
251 scalar.
252 
253 If @var{fcn} is a two-element string array or a two-element cell array
254 of strings, inline functions, or function handles, the first element names
255 the function @math{f} described above, and the second element names a
256 function to compute the modified Jacobian
257 
258 @tex
259 $$
260 J = {\partial f \over \partial x}
261  + c {\partial f \over \partial \dot{x}}
262 $$
263 @end tex
264 @ifnottex
265 
266 @example
267 @group
268  df df
269 jac = -- + c ------
270  dx d xdot
271 @end group
272 @end example
273 
274 @end ifnottex
275 
276 The modified Jacobian function must have the form
277 
278 @example
279 @group
280 
281 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
282 
283 @end group
284 @end example
285 
286 The optional second argument names a function that defines the
287 constraint functions whose roots are desired during the integration.
288 This function must have the form
289 
290 @example
291 @var{g_out} = g (@var{x}, @var{t})
292 @end example
293 
294 @noindent
295 and return a vector of the constraint function values.
296 If the value of any of the constraint functions changes sign, @sc{dasrt}
297 will attempt to stop the integration at the point of the sign change.
298 
299 If the name of the constraint function is omitted, @code{dasrt} solves
300 the same problem as @code{daspk} or @code{dassl}.
301 
302 Note that because of numerical errors in the constraint functions
303 due to round-off and integration error, @sc{dasrt} may return false
304 roots, or return the same root at two or more nearly equal values of
305 @var{T}. If such false roots are suspected, the user should consider
306 smaller error tolerances or higher precision in the evaluation of the
307 constraint functions.
308 
309 If a root of some constraint function defines the end of the problem,
310 the input to @sc{dasrt} should nevertheless allow integration to a
311 point slightly past that root, so that @sc{dasrt} can locate the root
312 by interpolation.
313 
314 The third and fourth arguments to @code{dasrt} specify the initial
315 condition of the states and their derivatives, and the fourth argument
316 specifies a vector of output times at which the solution is desired,
317 including the time corresponding to the initial condition.
318 
319 The set of initial states and derivatives are not strictly required to
320 be consistent. In practice, however, @sc{dassl} is not very good at
321 determining a consistent set for you, so it is best if you ensure that
322 the initial values result in the function evaluating to zero.
323 
324 The sixth argument is optional, and may be used to specify a set of
325 times that the DAE solver should not integrate past. It is useful for
326 avoiding difficulties with singularities and points where there is a
327 discontinuity in the derivative.
328 
329 After a successful computation, the value of @var{istate} will be
330 greater than zero (consistent with the Fortran version of @sc{dassl}).
331 
332 If the computation is not successful, the value of @var{istate} will be
333 less than zero and @var{msg} will contain additional information.
334 
335 You can use the function @code{dasrt_options} to set optional
336 parameters for @code{dasrt}.
337 @seealso{dasrt_options, daspk, dasrt, lsode}
338 @end deftypefn */)
339 {
340  int nargin = args.length ();
341 
342  if (nargin < 4 || nargin > 6)
343  print_usage ();
344 
345  warned_fcn_imaginary = false;
346  warned_jac_imaginary = false;
347  warned_cf_imaginary = false;
348 
350 
352 
353  frame.protect_var (call_depth);
354  call_depth++;
355 
356  if (call_depth > 1)
357  error ("dasrt: invalid recursive call");
358 
359  int argp = 0;
360  std::string fcn_name, fname, jac_name, jname;
361 
362  dasrt_fcn = octave_value ();
363  dasrt_jac = octave_value ();
364  dasrt_cf = octave_value ();
365 
366  // Check all the arguments. Are they the right animals?
367 
368  // Here's where I take care of f and j in one shot:
369 
370  octave_value f_arg = args(0);
371 
372  std::list<std::string> fcn_param_names ({"x", "xdot", "t"});
373  std::list<std::string> jac_param_names ({"x", "xdot", "t", "cj"});
374 
375  if (f_arg.iscell ())
376  {
377  Cell c = f_arg.cell_value ();
378  if (c.numel () == 1)
379  f_arg = c(0);
380  else if (c.numel () == 2)
381  {
382  dasrt_fcn = octave::get_function_handle (interp, c(0),
383  fcn_param_names);
384 
385  if (dasrt_fcn.is_defined ())
386  {
387  dasrt_jac = octave::get_function_handle (interp, c(1),
388  jac_param_names);
389 
390  if (dasrt_jac.is_undefined ())
391  dasrt_fcn = octave_value ();
392  }
393  }
394  else
395  error ("dasrt: incorrect number of elements in cell array");
396  }
397 
398  if (dasrt_fcn.is_undefined () && ! f_arg.iscell ())
399  {
400  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
401  dasrt_fcn = f_arg;
402  else
403  {
404  switch (f_arg.rows ())
405  {
406  case 1:
407  dasrt_fcn = octave::get_function_handle (interp, f_arg,
408  fcn_param_names);
409  break;
410 
411  case 2:
412  {
413  string_vector tmp = f_arg.string_vector_value ();
414 
415  dasrt_fcn = octave::get_function_handle (interp, tmp(0),
416  fcn_param_names);
417 
418  if (dasrt_fcn.is_defined ())
419  {
420  dasrt_jac = octave::get_function_handle (interp, tmp(1),
421  jac_param_names);
422 
423  if (dasrt_jac.is_undefined ())
424  dasrt_fcn = octave_value ();
425  }
426  }
427  break;
428 
429  default:
430  error ("dasrt: first arg should be a string or 2-element string array");
431  }
432  }
433  }
434 
435  if (dasrt_fcn.is_undefined ())
436  error ("dasrt: FCN argument is not a valid function name or handle");
437 
438  DAERTFunc func (dasrt_user_f);
439 
440  argp++;
441 
442  if (args(1).isempty () && args(1).is_double_type ())
443  {
444  // Allow [] to skip constraint function. This feature is
445  // undocumented now, but was supported by earlier versions.
446 
447  argp++;
448  }
449  else
450  {
451  if (args(1).is_function_handle () || args(1).is_inline_function ()
452  || args(1).is_string ())
453  {
454  std::list<std::string> cf_param_names ({"x", "t"});
455 
456  dasrt_cf = octave::get_function_handle (interp, args(1), cf_param_names);
457  }
458 
459  if (dasrt_cf.is_defined ())
460  {
461  argp++;
462 
464  }
465  }
466 
467  if (argp + 3 > nargin)
468  print_usage ();
469 
470  ColumnVector state = args(argp++).xvector_value ("dasrt: initial state X_0 must be a vector");
471 
472  ColumnVector stateprime = args(argp++).xvector_value ("dasrt: initial derivatives XDOT_0 must be a vector");
473 
474  ColumnVector out_times = args(argp++).xvector_value ("dasrt: output time variable T must be a vector");
475 
476  double tzero = out_times (0);
477 
478  ColumnVector crit_times;
479 
480  bool crit_times_set = false;
481 
482  if (argp < nargin)
483  {
484  crit_times = args(argp).xvector_value ("dasrt: list of critical times T_CRIT must be a vector");
485  argp++;
486 
487  crit_times_set = true;
488  }
489 
490  if (dasrt_jac.is_defined ())
492 
493  DASRT_result output;
494 
495  DASRT dae = DASRT (state, stateprime, tzero, func);
496 
497  dae.set_options (dasrt_opts);
498 
499  if (crit_times_set)
500  output = dae.integrate (out_times, crit_times);
501  else
502  output = dae.integrate (out_times);
503 
504  std::string msg = dae.error_message ();
505 
506  if (dae.integration_ok ())
507  {
508  retval(0) = output.state ();
509  retval(1) = output.deriv ();
510  retval(2) = output.times ();
511  }
512  else
513  {
514  if (nargout < 4)
515  error ("dasrt: %s", msg.c_str ());
516 
517  retval(0) = Matrix ();
518  retval(1) = Matrix ();
519  retval(2) = Matrix ();
520  }
521 
522  retval(3) = static_cast<double> (dae.integration_state ());
523  retval(4) = msg;
524 
525  return retval;
526 }
static DASRT_options dasrt_opts
Definition: DASRT-opts.cc:21
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
Definition: Cell.h:43
DAEFunc & set_jacobian_function(DAEJacFunc j)
Definition: DAEFunc.h:87
DAERTFunc & set_constraint_function(DAERTConstrFunc cf)
Definition: DAERTFunc.h:73
ColumnVector times(void) const
Definition: DASRT.h:66
Matrix state(void) const
Definition: DASRT.h:64
Matrix deriv(void) const
Definition: DASRT.h:65
Definition: DASRT.h:78
DASRT_result integrate(const ColumnVector &tout)
Definition: DASRT.cc:376
std::string error_message(void) const
Definition: DASRT.cc:554
Definition: dMatrix.h:42
bool integration_ok(void) const
Definition: base-de.h:100
octave_idx_type integration_state(void) const
Definition: base-de.h:102
bool empty(void) const
Definition: ovl.h:115
octave_idx_type length(void) const
Definition: ovl.h:113
bool iscell(void) const
Definition: ov.h:560
octave_idx_type rows(void) const
Definition: ov.h:504
bool is_defined(void) const
Definition: ov.h:551
Cell cell_value(void) const
bool is_function_handle(void) const
Definition: ov.h:721
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:930
bool is_undefined(void) const
Definition: ov.h:554
bool is_inline_function(void) const
Definition: ov.h:727
static int call_depth
Definition: dasrt.cc:62
static Matrix dasrt_user_j(const ColumnVector &x, const ColumnVector &xdot, double t, double cj)
Definition: dasrt.cc:151
static octave_value dasrt_jac
Definition: dasrt.cc:53
DEFMETHOD(dasrt, interp, args, nargout, doc:)
Definition: dasrt.cc:197
static octave_value dasrt_cf
Definition: dasrt.cc:54
static ColumnVector dasrt_user_cf(const ColumnVector &x, double t)
Definition: dasrt.cc:110
static octave_value dasrt_fcn
Definition: dasrt.cc:52
static ColumnVector dasrt_user_f(const ColumnVector &x, const ColumnVector &xdot, double t, octave_idx_type &)
Definition: dasrt.cc:65
static bool warned_jac_imaginary
Definition: dasrt.cc:58
static bool warned_fcn_imaginary
Definition: dasrt.cc:57
static bool warned_cf_imaginary
Definition: dasrt.cc:59
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
void warning(const char *fmt,...)
Definition: error.cc:1050
void error(const char *fmt,...)
Definition: error.cc:968
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:152
F77_RET_T const F77_DBLE * x
static uint32_t state[624]
Definition: randmtzig.cc:190
octave_value get_function_handle(interpreter &interp, const octave_value &arg, const std::string &parameter_name)
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:9580
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811