GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
DASSL.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1993-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 <cinttypes>
31 #include <sstream>
32 
33 #include "DASSL.h"
34 #include "dMatrix.h"
35 #include "f77-fcn.h"
36 #include "lo-error.h"
37 #include "quit.h"
38 
39 typedef F77_INT (*dassl_fcn_ptr) (const double&, const double *,
40  const double *, double *, F77_INT&,
41  double *, F77_INT *);
42 
43 typedef F77_INT (*dassl_jac_ptr) (const double&, const double *,
44  const double *, double *, const double&,
45  double *, F77_INT *);
46 
47 extern "C"
48 {
49  F77_RET_T
51  F77_DBLE *, F77_DBLE *, F77_DBLE&, const F77_INT *,
52  const F77_DBLE *, const F77_DBLE *, F77_INT&,
53  F77_DBLE *, const F77_INT&, F77_INT *,
54  const F77_INT&, const F77_DBLE *, const F77_INT *,
56 }
57 
58 static DAEFunc::DAERHSFunc user_fcn;
59 static DAEFunc::DAEJacFunc user_jac;
60 
61 static F77_INT nn;
62 
63 static F77_INT
64 ddassl_f (const double& time, const double *state, const double *deriv,
65  double *delta, F77_INT& ires, double *, F77_INT *)
66 {
67  // FIXME: would be nice to avoid copying the data.
68 
69  ColumnVector tmp_deriv (nn);
70  ColumnVector tmp_state (nn);
71  ColumnVector tmp_delta (nn);
72 
73  for (F77_INT i = 0; i < nn; i++)
74  {
75  tmp_deriv.elem (i) = deriv[i];
76  tmp_state.elem (i) = state[i];
77  }
78 
79  octave_idx_type tmp_ires = ires;
80 
81  tmp_delta = user_fcn (tmp_state, tmp_deriv, time, tmp_ires);
82 
83  ires = octave::to_f77_int (tmp_ires);
84 
85  if (ires >= 0)
86  {
87  if (tmp_delta.isempty ())
88  ires = -2;
89  else
90  {
91  for (F77_INT i = 0; i < nn; i++)
92  delta[i] = tmp_delta.elem (i);
93  }
94  }
95 
96  return 0;
97 }
98 
99 static F77_INT
100 ddassl_j (const double& time, const double *state, const double *deriv,
101  double *pd, const double& cj, double *, F77_INT *)
102 {
103  // FIXME: would be nice to avoid copying the data.
104 
105  ColumnVector tmp_state (nn);
106  ColumnVector tmp_deriv (nn);
107 
108  for (F77_INT i = 0; i < nn; i++)
109  {
110  tmp_deriv.elem (i) = deriv[i];
111  tmp_state.elem (i) = state[i];
112  }
113 
114  Matrix tmp_pd = user_jac (tmp_state, tmp_deriv, time, cj);
115 
116  for (F77_INT j = 0; j < nn; j++)
117  for (F77_INT i = 0; i < nn; i++)
118  pd[nn * j + i] = tmp_pd.elem (i, j);
119 
120  return 0;
121 }
122 
124 DASSL::do_integrate (double tout)
125 {
126  ColumnVector retval;
127 
128  if (! m_initialized || m_restart || DAEFunc::m_reset
130  {
131  m_integration_error = false;
132 
133  m_initialized = true;
134 
135  m_info.resize (dim_vector (15, 1));
136 
137  for (F77_INT i = 0; i < 15; i++)
138  m_info(i) = 0;
139 
140  F77_INT n = octave::to_f77_int (size ());
141 
142  m_liw = 21 + n;
143  m_lrw = 40 + 9*n + n*n;
144 
145  nn = n;
146 
147  m_iwork.resize (dim_vector (m_liw, 1));
148  m_rwork.resize (dim_vector (m_lrw, 1));
149 
150  m_info(0) = 0;
151 
152  if (m_stop_time_set)
153  {
154  m_rwork(0) = m_stop_time;
155  m_info(3) = 1;
156  }
157  else
158  m_info(3) = 0;
159 
160  m_restart = false;
161 
162  // DAEFunc
163 
164  user_fcn = DAEFunc::function ();
165  user_jac = DAEFunc::jacobian_function ();
166 
167  if (user_fcn)
168  {
169  octave_idx_type ires = 0;
170 
171  ColumnVector res = (*user_fcn) (m_x, m_xdot, m_t, ires);
172 
173  if (res.numel () != m_x.numel ())
174  {
175  (*current_liboctave_error_handler)
176  ("dassl: inconsistent sizes for state and residual vectors");
177 
178  m_integration_error = true;
179  return retval;
180  }
181  }
182  else
183  {
184  (*current_liboctave_error_handler)
185  ("dassl: no user supplied RHS subroutine!");
186 
187  m_integration_error = true;
188  return retval;
189  }
190 
191  m_info(4) = (user_jac ? 1 : 0);
192 
193  DAEFunc::m_reset = false;
194 
195  // DASSL_options
196 
197  double hmax = maximum_step_size ();
198  if (hmax >= 0.0)
199  {
200  m_rwork(1) = hmax;
201  m_info(6) = 1;
202  }
203  else
204  m_info(6) = 0;
205 
206  double h0 = initial_step_size ();
207  if (h0 >= 0.0)
208  {
209  m_rwork(2) = h0;
210  m_info(7) = 1;
211  }
212  else
213  m_info(7) = 0;
214 
215  F77_INT sl = octave::to_f77_int (step_limit ());
216 
217  if (sl >= 0)
218  {
219  m_info(11) = 1;
220  m_iwork(20) = sl;
221  }
222  else
223  m_info(11) = 0;
224 
225  F77_INT maxord = octave::to_f77_int (maximum_order ());
226  if (maxord >= 0)
227  {
228  if (maxord > 0 && maxord < 6)
229  {
230  m_info(8) = 1;
231  m_iwork(2) = maxord;
232  }
233  else
234  {
235  (*current_liboctave_error_handler)
236  ("dassl: invalid value for maximum order: %"
237  OCTAVE_F77_INT_TYPE_FORMAT, maxord);
238  m_integration_error = true;
239  return retval;
240  }
241  }
242 
243  F77_INT enc = octave::to_f77_int (enforce_nonnegativity_constraints ());
244  m_info(9) = (enc ? 1 : 0);
245 
246  F77_INT ccic = octave::to_f77_int (compute_consistent_initial_condition ());
247  m_info(10) = (ccic ? 1 : 0);
248 
249  m_abs_tol = absolute_tolerance ();
250  m_rel_tol = relative_tolerance ();
251 
252  F77_INT abs_tol_len = octave::to_f77_int (m_abs_tol.numel ());
253  F77_INT rel_tol_len = octave::to_f77_int (m_rel_tol.numel ());
254 
255  if (abs_tol_len == 1 && rel_tol_len == 1)
256  {
257  m_info(1) = 0;
258  }
259  else if (abs_tol_len == n && rel_tol_len == n)
260  {
261  m_info(1) = 1;
262  }
263  else
264  {
265  (*current_liboctave_error_handler)
266  ("dassl: inconsistent sizes for tolerance arrays");
267 
268  m_integration_error = true;
269  return retval;
270  }
271 
272  DASSL_options::m_reset = false;
273  }
274 
275  double *px = m_x.fortran_vec ();
276  double *pxdot = m_xdot.fortran_vec ();
277 
278  F77_INT *pinfo = m_info.fortran_vec ();
279 
280  double *prel_tol = m_rel_tol.fortran_vec ();
281  double *pabs_tol = m_abs_tol.fortran_vec ();
282 
283  double *prwork = m_rwork.fortran_vec ();
284  F77_INT *piwork = m_iwork.fortran_vec ();
285 
286  double *dummy = nullptr;
287  F77_INT *idummy = nullptr;
288 
289  F77_INT tmp_istate = octave::to_f77_int (m_istate);
290 
291  F77_XFCN (ddassl, DDASSL, (ddassl_f, nn, m_t, px, pxdot, tout, pinfo,
292  prel_tol, pabs_tol, tmp_istate, prwork, m_lrw,
293  piwork, m_liw, dummy, idummy, ddassl_j));
294 
295  m_istate = tmp_istate;
296 
297  switch (m_istate)
298  {
299  case 1: // A step was successfully taken in intermediate-output
300  // mode. The code has not yet reached TOUT.
301  case 2: // The integration to TSTOP was successfully completed
302  // (T=TSTOP) by stepping exactly to TSTOP.
303  case 3: // The integration to TOUT was successfully completed
304  // (T=TOUT) by stepping past TOUT. Y(*) is obtained by
305  // interpolation. YPRIME(*) is obtained by interpolation.
306  retval = m_x;
307  m_t = tout;
308  break;
309 
310  case -1: // A large amount of work has been expended. (~500 steps).
311  case -2: // The error tolerances are too stringent.
312  case -3: // The local error test cannot be satisfied because you
313  // specified a zero component in ATOL and the
314  // corresponding computed solution component is zero.
315  // Thus, a pure relative error test is impossible for
316  // this component.
317  case -6: // DDASSL had repeated error test failures on the last
318  // attempted step.
319  case -7: // The corrector could not converge.
320  case -8: // The matrix of partial derivatives is singular.
321  case -9: // The corrector could not converge. There were repeated
322  // error test failures in this step.
323  case -10: // The corrector could not converge because IRES was
324  // equal to minus one.
325  case -11: // IRES equal to -2 was encountered and control is being
326  // returned to the calling program.
327  case -12: // DDASSL failed to compute the initial YPRIME.
328  case -33: // The code has encountered trouble from which it cannot
329  // recover. A message is printed explaining the trouble
330  // and control is returned to the calling program. For
331  // example, this occurs when invalid input is detected.
332  m_integration_error = true;
333  break;
334 
335  default:
336  m_integration_error = true;
337  (*current_liboctave_error_handler)
338  ("unrecognized value of istate (= %" OCTAVE_IDX_TYPE_FORMAT ") "
339  "returned from ddassl", m_istate);
340  break;
341  }
342 
343  return retval;
344 }
345 
346 Matrix
348 {
349  Matrix dummy;
350  return integrate (tout, dummy);
351 }
352 
353 Matrix
354 DASSL::integrate (const ColumnVector& tout, Matrix& xdot_out)
355 {
356  Matrix retval;
357 
358  octave_idx_type n_out = tout.numel ();
359  F77_INT n = octave::to_f77_int (size ());
360 
361  if (n_out > 0 && n > 0)
362  {
363  retval.resize (n_out, n);
364  xdot_out.resize (n_out, n);
365 
366  for (F77_INT i = 0; i < n; i++)
367  {
368  retval.elem (0, i) = m_x.elem (i);
369  xdot_out.elem (0, i) = m_xdot.elem (i);
370  }
371 
372  for (octave_idx_type j = 1; j < n_out; j++)
373  {
374  ColumnVector x_next = do_integrate (tout.elem (j));
375 
377  return retval;
378 
379  for (F77_INT i = 0; i < n; i++)
380  {
381  retval.elem (j, i) = x_next.elem (i);
382  xdot_out.elem (j, i) = m_xdot.elem (i);
383  }
384  }
385  }
386 
387  return retval;
388 }
389 
390 Matrix
391 DASSL::do_integrate (const ColumnVector& tout, const ColumnVector& tcrit)
392 {
393  Matrix dummy;
394  return integrate (tout, dummy, tcrit);
395 }
396 
397 Matrix
398 DASSL::integrate (const ColumnVector& tout, Matrix& xdot_out,
399  const ColumnVector& tcrit)
400 {
401  Matrix retval;
402 
403  octave_idx_type n_out = tout.numel ();
404  F77_INT n = octave::to_f77_int (size ());
405 
406  if (n_out > 0 && n > 0)
407  {
408  retval.resize (n_out, n);
409  xdot_out.resize (n_out, n);
410 
411  for (F77_INT i = 0; i < n; i++)
412  {
413  retval.elem (0, i) = m_x.elem (i);
414  xdot_out.elem (0, i) = m_xdot.elem (i);
415  }
416 
417  octave_idx_type n_crit = tcrit.numel ();
418 
419  if (n_crit > 0)
420  {
421  octave_idx_type i_crit = 0;
422  octave_idx_type i_out = 1;
423  double next_crit = tcrit.elem (0);
424  double next_out;
425  while (i_out < n_out)
426  {
427  bool do_restart = false;
428 
429  next_out = tout.elem (i_out);
430  if (i_crit < n_crit)
431  next_crit = tcrit.elem (i_crit);
432 
433  bool save_output;
434  double t_out;
435 
436  if (next_crit == next_out)
437  {
438  set_stop_time (next_crit);
439  t_out = next_out;
440  save_output = true;
441  i_out++;
442  i_crit++;
443  do_restart = true;
444  }
445  else if (next_crit < next_out)
446  {
447  if (i_crit < n_crit)
448  {
449  set_stop_time (next_crit);
450  t_out = next_crit;
451  save_output = false;
452  i_crit++;
453  do_restart = true;
454  }
455  else
456  {
457  clear_stop_time ();
458  t_out = next_out;
459  save_output = true;
460  i_out++;
461  }
462  }
463  else
464  {
465  set_stop_time (next_crit);
466  t_out = next_out;
467  save_output = true;
468  i_out++;
469  }
470 
471  ColumnVector x_next = do_integrate (t_out);
472 
474  return retval;
475 
476  if (save_output)
477  {
478  for (F77_INT i = 0; i < n; i++)
479  {
480  retval.elem (i_out-1, i) = x_next.elem (i);
481  xdot_out.elem (i_out-1, i) = m_xdot.elem (i);
482  }
483  }
484 
485  if (do_restart)
486  force_restart ();
487  }
488  }
489  else
490  {
491  retval = integrate (tout, xdot_out);
492 
494  return retval;
495  }
496  }
497 
498  return retval;
499 }
500 
501 std::string
503 {
504  std::string retval;
505 
506  std::ostringstream buf;
507  buf << m_t;
508  std::string t_curr = buf.str ();
509 
510  switch (m_istate)
511  {
512  case 1:
513  retval = "a step was successfully taken in intermediate-output mode.";
514  break;
515 
516  case 2:
517  retval = "integration completed by stepping exactly to TOUT";
518  break;
519 
520  case 3:
521  retval = "integration to tout completed by stepping past TOUT";
522  break;
523 
524  case -1:
525  retval = "a large amount of work has been expended (t =" + t_curr + ')';
526  break;
527 
528  case -2:
529  retval = "the error tolerances are too stringent";
530  break;
531 
532  case -3:
533  retval = "error weight became zero during problem. (t = " + t_curr +
534  "; solution component i vanished, and atol or atol(i) == 0)";
535  break;
536 
537  case -6:
538  retval = "repeated error test failures on the last attempted step (t = "
539  + t_curr + ')';
540  break;
541 
542  case -7:
543  retval = "the corrector could not converge (t = " + t_curr + ')';
544  break;
545 
546  case -8:
547  retval = "the matrix of partial derivatives is singular (t = " + t_curr +
548  ')';
549  break;
550 
551  case -9:
552  retval = "the corrector could not converge (t = " + t_curr +
553  "; repeated test failures)";
554  break;
555 
556  case -10:
557  retval = "corrector could not converge because IRES was -1 (t = "
558  + t_curr + ')';
559  break;
560 
561  case -11:
562  retval = "return requested in user-supplied function (t = " + t_curr +
563  ')';
564  break;
565 
566  case -12:
567  retval = "failed to compute consistent initial conditions";
568  break;
569 
570  case -33:
571  retval = "unrecoverable error (see printed message)";
572  break;
573 
574  default:
575  retval = "unknown error state";
576  break;
577  }
578 
579  return retval;
580 }
F77_INT(* dassl_fcn_ptr)(const double &, const double *, const double *, double *, F77_INT &, double *, F77_INT *)
Definition: DASSL.cc:39
F77_INT(* dassl_jac_ptr)(const double &, const double *, const double *, double *, const double &, double *, F77_INT *)
Definition: DASSL.cc:43
F77_RET_T F77_FUNC(ddassl, DDASSL)(dassl_fcn_ptr
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:562
T * fortran_vec()
Size of the specified dimension.
Definition: Array-base.cc:1764
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array-base.cc:1023
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
DAEJacFunc jacobian_function() const
Definition: DAEFunc.h:84
Matrix(* DAEJacFunc)(const ColumnVector &x, const ColumnVector &xdot, double t, double cj)
Definition: DAEFunc.h:46
bool m_reset
Definition: DAEFunc.h:103
DAERHSFunc function() const
Definition: DAEFunc.h:75
ColumnVector(* DAERHSFunc)(const ColumnVector &x, const ColumnVector &xdot, double t, octave_idx_type &ires)
Definition: DAEFunc.h:38
Array< double > relative_tolerance() const
Definition: DASSL-opts.h:135
octave_idx_type compute_consistent_initial_condition() const
Definition: DASSL-opts.h:138
octave_idx_type maximum_order() const
Definition: DASSL-opts.h:147
Array< double > absolute_tolerance() const
Definition: DASSL-opts.h:132
double initial_step_size() const
Definition: DASSL-opts.h:144
double maximum_step_size() const
Definition: DASSL-opts.h:150
octave_idx_type enforce_nonnegativity_constraints() const
Definition: DASSL-opts.h:141
octave_idx_type step_limit() const
Definition: DASSL-opts.h:153
std::string error_message() const
Definition: DASSL.cc:502
Matrix integrate(const ColumnVector &tout, Matrix &xdot_out)
Definition: DASSL.cc:354
ColumnVector do_integrate(double t)
Definition: DASSL.cc:124
Definition: dMatrix.h:42
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:158
ColumnVector m_xdot
Definition: base-dae.h:80
double m_stop_time
Definition: base-de.h:112
bool m_restart
Definition: base-de.h:116
double m_t
Definition: base-de.h:110
octave_idx_type m_istate
Definition: base-de.h:120
virtual void force_restart()
Definition: base-de.h:98
void clear_stop_time()
Definition: base-de.h:92
bool m_stop_time_set
Definition: base-de.h:114
octave_idx_type size() const
Definition: base-de.h:79
bool m_integration_error
Definition: base-de.h:118
ColumnVector m_x
Definition: base-de.h:108
void set_stop_time(double tt)
Definition: base-de.h:85
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
Definition: oct-time.h:64
subroutine ddassl(RES, NEQ, T, Y, YPRIME, TOUT, INFO, RTOL, ATOL, IDID, RWORK, LRW, IWORK, LIW, RPAR, IPAR, JAC)
Definition: ddassl.f:3
#define F77_XFCN(f, F, args)
Definition: f77-fcn.h:45
double F77_DBLE
Definition: f77-fcn.h:302
octave_f77_int_type F77_INT
Definition: f77-fcn.h:306
F77_RET_T(F77_CONST_CHAR_ARG_DECL, F77_CONST_CHAR_ARG_DECL, F77_CONST_CHAR_ARG_DECL, const F77_INT &, const F77_INT &, const F77_INT &, F77_INT &, F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, F77_DBLE *, F77_DBLE *, const F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, const F77_INT &, F77_DBLE *, F77_INT *, F77_INT &F77_CHAR_ARG_LEN_DECL F77_CHAR_ARG_LEN_DECL F77_CHAR_ARG_LEN_DECL)
octave_idx_type n
Definition: mx-inlines.cc:761