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