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