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