GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
daspk.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-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 "DASPK.h"
34 
35 #include "defun.h"
36 #include "error.h"
37 #include "errwarn.h"
38 #include "interpreter-private.h"
39 #include "ovl.h"
40 #include "ov-fcn.h"
41 #include "ov-cell.h"
42 #include "pager.h"
43 #include "parse.h"
44 #include "unwind-prot.h"
45 #include "utils.h"
46 #include "variables.h"
47 
48 #include "DASPK-opts.cc"
49 
50 // Global pointer for user defined function required by daspk.
52 
53 // Global pointer for optional user defined jacobian function.
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 
60 // Is this a recursive call?
61 static int call_depth = 0;
62 
65  double t, octave_idx_type& ires)
66 {
68 
69  assert (x.numel () == xdot.numel ());
70 
71  octave_value_list args;
72 
73  args(2) = t;
74  args(1) = xdot;
75  args(0) = x;
76 
77  if (daspk_fcn.is_defined ())
78  {
80 
81  try
82  {
83  tmp = octave::feval (daspk_fcn, args, 1);
84  }
85  catch (octave::execution_exception& e)
86  {
87  err_user_supplied_eval (e, "daspk");
88  }
89 
90  int tlen = tmp.length ();
91  if (tlen == 0 || ! tmp(0).is_defined ())
92  err_user_supplied_eval ("daspk");
93 
94  if (! warned_fcn_imaginary && tmp(0).iscomplex ())
95  {
96  warning ("daspk: ignoring imaginary part returned from user-supplied function");
97  warned_fcn_imaginary = true;
98  }
99 
100  retval = tmp(0).vector_value ();
101 
102  if (tlen > 1)
103  ires = tmp(1).idx_type_value ();
104 
105  if (retval.isempty ())
106  err_user_supplied_eval ("daspk");
107  }
108 
109  return retval;
110 }
111 
112 Matrix
114  double t, double cj)
115 {
116  Matrix retval;
117 
118  assert (x.numel () == xdot.numel ());
119 
120  octave_value_list args;
121 
122  args(3) = cj;
123  args(2) = t;
124  args(1) = xdot;
125  args(0) = x;
126 
127  if (daspk_jac.is_defined ())
128  {
129  octave_value_list tmp;
130 
131  try
132  {
133  tmp = octave::feval (daspk_jac, args, 1);
134  }
135  catch (octave::execution_exception& e)
136  {
137  err_user_supplied_eval (e, "daspk");
138  }
139 
140  int tlen = tmp.length ();
141  if (tlen == 0 || ! tmp(0).is_defined ())
142  err_user_supplied_eval ("daspk");
143 
144  if (! warned_jac_imaginary && tmp(0).iscomplex ())
145  {
146  warning ("daspk: ignoring imaginary part returned from user-supplied jacobian function");
147  warned_jac_imaginary = true;
148  }
149 
150  retval = tmp(0).matrix_value ();
151 
152  if (retval.isempty ())
153  err_user_supplied_eval ("daspk");
154  }
155 
156  return retval;
157 }
158 
159 DEFMETHOD (daspk, interp, args, nargout,
160  doc: /* -*- texinfo -*-
161 @deftypefn {} {[@var{x}, @var{xdot}, @var{istate}, @var{msg}] =} daspk (@var{fcn}, @var{x_0}, @var{xdot_0}, @var{t}, @var{t_crit})
162 Solve a set of differential-algebraic equations.
163 
164 @code{daspk} solves the set of equations
165 @tex
166 $$ 0 = f (x, \dot{x}, t) $$
167 with
168 $$ x(t_0) = x_0, \dot{x}(t_0) = \dot{x}_0 $$
169 @end tex
170 @ifnottex
171 
172 @example
173 0 = f (x, xdot, t)
174 @end example
175 
176 @noindent
177 with
178 
179 @example
180 x(t_0) = x_0, xdot(t_0) = xdot_0
181 @end example
182 
183 @end ifnottex
184 The solution is returned in the matrices @var{x} and @var{xdot},
185 with each row in the result matrices corresponding to one of the
186 elements in the vector @var{t}. The first element of @var{t}
187 should be @math{t_0} and correspond to the initial state of the
188 system @var{x_0} and its derivative @var{xdot_0}, so that the first
189 row of the output @var{x} is @var{x_0} and the first row
190 of the output @var{xdot} is @var{xdot_0}.
191 
192 The first argument, @var{fcn}, is a string, inline, or function handle
193 that names the function @math{f} to call to compute the vector of
194 residuals for the set of equations. It must have the form
195 
196 @example
197 @var{res} = f (@var{x}, @var{xdot}, @var{t})
198 @end example
199 
200 @noindent
201 in which @var{x}, @var{xdot}, and @var{res} are vectors, and @var{t} is a
202 scalar.
203 
204 If @var{fcn} is a two-element string array or a two-element cell array
205 of strings, inline functions, or function handles, the first element names
206 the function @math{f} described above, and the second element names a
207 function to compute the modified Jacobian
208 @tex
209 $$
210 J = {\partial f \over \partial x}
211  + c {\partial f \over \partial \dot{x}}
212 $$
213 @end tex
214 @ifnottex
215 
216 @example
217 @group
218  df df
219 jac = -- + c ------
220  dx d xdot
221 @end group
222 @end example
223 
224 @end ifnottex
225 
226 The modified Jacobian function must have the form
227 
228 @example
229 @group
230 
231 @var{jac} = j (@var{x}, @var{xdot}, @var{t}, @var{c})
232 
233 @end group
234 @end example
235 
236 The second and third arguments to @code{daspk} specify the initial
237 condition of the states and their derivatives, and the fourth argument
238 specifies a vector of output times at which the solution is desired,
239 including the time corresponding to the initial condition.
240 
241 The set of initial states and derivatives are not strictly required to
242 be consistent. If they are not consistent, you must use the
243 @code{daspk_options} function to provide additional information so
244 that @code{daspk} can compute a consistent starting point.
245 
246 The fifth argument is optional, and may be used to specify a set of
247 times that the DAE solver should not integrate past. It is useful for
248 avoiding difficulties with singularities and points where there is a
249 discontinuity in the derivative.
250 
251 After a successful computation, the value of @var{istate} will be
252 greater than zero (consistent with the Fortran version of @sc{daspk}).
253 
254 If the computation is not successful, the value of @var{istate} will be
255 less than zero and @var{msg} will contain additional information.
256 
257 You can use the function @code{daspk_options} to set optional
258 parameters for @code{daspk}.
259 @seealso{dassl}
260 @end deftypefn */)
261 {
262  int nargin = args.length ();
263 
264  if (nargin < 4 || nargin > 5)
265  print_usage ();
266 
267  warned_fcn_imaginary = false;
268  warned_jac_imaginary = false;
269 
271 
273 
274  frame.protect_var (call_depth);
275  call_depth++;
276 
277  if (call_depth > 1)
278  error ("daspk: invalid recursive call");
279 
280  std::string fcn_name, fname, jac_name, jname;
281 
282  daspk_fcn = octave_value ();
283  daspk_jac = octave_value ();
284 
285  octave_value f_arg = args(0);
286 
287  std::list<std::string> fcn_param_names ({"x", "xdot", "t"});
288  std::list<std::string> jac_param_names ({"x", "xdot", "t", "cj"});
289 
290  if (f_arg.iscell ())
291  {
292  Cell c = f_arg.cell_value ();
293  if (c.numel () == 1)
294  f_arg = c(0);
295  else if (c.numel () == 2)
296  {
297  daspk_fcn = octave::get_function_handle (interp, c(0),
298  fcn_param_names);
299 
300  if (daspk_fcn.is_defined ())
301  {
302  daspk_jac = octave::get_function_handle (interp, c(1),
303  jac_param_names);
304 
305  if (daspk_jac.is_undefined ())
306  daspk_fcn = octave_value ();
307  }
308  }
309  else
310  error ("daspk: incorrect number of elements in cell array");
311  }
312 
313  if (daspk_fcn.is_undefined () && ! f_arg.iscell ())
314  {
315  if (f_arg.is_function_handle () || f_arg.is_inline_function ())
316  daspk_fcn = f_arg;
317  else
318  {
319  switch (f_arg.rows ())
320  {
321  case 1:
322  daspk_fcn = octave::get_function_handle (interp, f_arg,
323  fcn_param_names);
324  break;
325 
326  case 2:
327  {
328  string_vector tmp = f_arg.string_vector_value ();
329 
330  daspk_fcn = octave::get_function_handle (interp, tmp(0),
331  fcn_param_names);
332 
333  if (daspk_fcn.is_defined ())
334  {
335  daspk_jac = octave::get_function_handle (interp, tmp(1),
336  jac_param_names);
337 
338  if (daspk_jac.is_undefined ())
339  daspk_fcn = octave_value ();
340  }
341  }
342  break;
343 
344  default:
345  error ("daspk: first arg should be a string or 2-element string array");
346  }
347  }
348  }
349 
350  if (daspk_fcn.is_undefined ())
351  error ("daspk: FCN argument is not a valid function name or handle");
352 
353  ColumnVector state = args(1).xvector_value ("daspk: initial state X_0 must be a vector");
354 
355  ColumnVector deriv = args(2).xvector_value ("daspk: initial derivatives XDOT_0 must be a vector");
356 
357  ColumnVector out_times = args(3).xvector_value ("daspk: output time variable T must be a vector");
358 
359  ColumnVector crit_times;
360  int crit_times_set = 0;
361  if (nargin > 4)
362  {
363  crit_times = args(4).xvector_value ("daspk: list of critical times T_CRIT must be a vector");
364 
365  crit_times_set = 1;
366  }
367 
368  if (state.numel () != deriv.numel ())
369  error ("daspk: X_0 and XDOT_0 must have the same size");
370 
371  double tzero = out_times (0);
372 
374  if (daspk_jac.is_defined ())
376 
377  DASPK dae (state, deriv, tzero, func);
378  dae.set_options (daspk_opts);
379 
380  Matrix output;
381  Matrix deriv_output;
382 
383  if (crit_times_set)
384  output = dae.integrate (out_times, deriv_output, crit_times);
385  else
386  output = dae.integrate (out_times, deriv_output);
387 
388  std::string msg = dae.error_message ();
389 
390  if (dae.integration_ok ())
391  {
392  retval(0) = output;
393  retval(1) = deriv_output;
394  }
395  else
396  {
397  if (nargout < 3)
398  error ("daspk: %s", msg.c_str ());
399 
400  retval(0) = Matrix ();
401  retval(1) = Matrix ();
402  }
403 
404  retval(2) = static_cast<double> (dae.integration_state ());
405  retval(3) = msg;
406 
407  return retval;
408 }
static DASPK_options daspk_opts
Definition: DASPK-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
Definition: DASPK.h:41
std::string error_message(void) const
Definition: DASPK.cc:694
Matrix integrate(const ColumnVector &tout, Matrix &xdot_out)
Definition: DASPK.cc:546
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
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: daspk.cc:61
Matrix daspk_user_jacobian(const ColumnVector &x, const ColumnVector &xdot, double t, double cj)
Definition: daspk.cc:113
ColumnVector daspk_user_function(const ColumnVector &x, const ColumnVector &xdot, double t, octave_idx_type &ires)
Definition: daspk.cc:64
static octave_value daspk_jac
Definition: daspk.cc:54
static bool warned_jac_imaginary
Definition: daspk.cc:58
DEFMETHOD(daspk, interp, args, nargout, doc:)
Definition: daspk.cc:159
static octave_value daspk_fcn
Definition: daspk.cc:51
static bool warned_fcn_imaginary
Definition: daspk.cc:57
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