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