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