Loading [MathJax]/extensions/tex2jax.js
libpspm
All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
lsoda.cpp
Go to the documentation of this file.
1 /*
2  * HISTORY:
3  * This is a CPP version of the LSODA library for integration into MOOSE
4  somulator.
5  * The original was aquired from
6  * http://www.ccl.net/cca/software/SOURCES/C/kinetics2/index.shtml and modified
7  by
8  * Heng Li <lh3lh3@gmail.com>. Heng merged several C files into one and added a
9  * simpler interface. [Available
10  here](http://lh3lh3.users.sourceforge.net/download/lsoda.c)
11 
12  * The original source code came with no license or copyright
13  * information. Heng Li released his modification under the MIT/X11 license. I
14  * maintain the same license. I have removed quite a lot of text/comments from
15  * this library. Please refer to the standard documentation.
16  *
17  * Contact: Dilawar Singh <dilawars@ncbs.res.in>
18 */
19 
20 #include "lsoda.h"
21 #include <algorithm>
22 #include <cassert>
23 #include <cmath>
24 #include <iostream>
25 #include <memory>
26 #include <numeric>
27 #include <vector>
28 
29 //#include "helper.h"
30 
31 using namespace std;
32 
33 
35 {
36  // Initialize arrays.
37  mord = {{12, 5}};
38  sm1 = {{0., 0.5, 0.575, 0.55, 0.45, 0.35, 0.25, 0.2, 0.15, 0.1, 0.075, 0.05, 0.025}};
39  el = {{0}};
40  cm1 = {{0}};
41  cm2 = {{0}};
42 }
43 
45 {
46 }
47 
48 bool LSODA::abs_compare(double a, double b)
49 {
50  return (std::abs(a) < std::abs(b));
51 }
52 
53 /* Purpose : Find largest component of double vector dx */
54 size_t LSODA::idamax1(const vector<double> &dx, const size_t n, const size_t offset = 0)
55 {
56 
57  size_t v = 0, vmax = 0;
58  size_t idmax = 1;
59  for(size_t i = 1; i <= n; i++) {
60  v = abs(dx[i + offset]);
61  if(v > vmax) {
62  vmax = v;
63  idmax = i;
64  }
65  }
66  return idmax;
67 
68  // Following has failed with seg-fault. Probably issue with STL.
69  // return std::max_element( dx.begin()+1+offset, dx.begin()+1+n, LSODA::abs_compare) -
70  // dx.begin() - offset;
71 }
72 
73 /* Purpose : scalar vector multiplication
74  dx = da * dx
75 */
77  const double da, vector<double> &dx, const size_t n, const size_t offset = 0)
78 {
79  // FIXME: n is not used here. why?
80  (void)n;
81 
82  std::transform(dx.begin() + 1 + offset, dx.end(), dx.begin() + 1 + offset,
83  [&da](double x) -> double { return da * x; });
84 }
85 
86 /* Purpose : Inner product dx . dy */
87 double LSODA::ddot1(const vector<double> &a, const vector<double> &b, const size_t n,
88  const size_t offsetA = 0, const size_t offsetB = 0)
89 {
90  double sum = 0.0;
91  for(size_t i = 1; i <= n; i++)
92  sum += a[i + offsetA] * b[i + offsetB];
93  return sum;
94 }
95 
96 void LSODA::daxpy1(const double da, const vector<double> &dx, vector<double> &dy,
97  const size_t n, const size_t offsetX = 0, const size_t offsetY = 0)
98 {
99 
100  for(size_t i = 1; i <= n; i++)
101  dy[i + offsetY] = da * dx[i + offsetX] + dy[i + offsetY];
102 }
103 
104 // See BLAS documentation. The first argument has been changed to vector.
105 void LSODA::dgesl(const vector<vector<double>> &a, const size_t n, vector<int> &ipvt,
106  vector<double> &b, const size_t job)
107 {
108  size_t k, j;
109  double t;
110 
111  /*
112  Job = 0, solve a * x = b.
113  */
114  if(job == 0) {
115  /*
116  First solve L * y = b.
117  */
118  for(k = 1; k <= n; k++) {
119  t = ddot1(a[k], b, k - 1);
120  b[k] = (b[k] - t) / a[k][k];
121  }
122  /*
123  Now solve U * x = y.
124  */
125  for(k = n - 1; k >= 1; k--) {
126  b[k] = b[k] + ddot1(a[k], b, n - k, k, k);
127  j = ipvt[k];
128  if(j != k) {
129  t = b[j];
130  b[j] = b[k];
131  b[k] = t;
132  }
133  }
134  return;
135  }
136  /*
137  Job = nonzero, solve Transpose(a) * x = b.
138 
139  First solve Transpose(U) * y = b.
140  */
141  for(k = 1; k <= n - 1; k++) {
142  j = ipvt[k];
143  t = b[j];
144  if(j != k) {
145  b[j] = b[k];
146  b[k] = t;
147  }
148  daxpy1(t, a[k], b, n - k, k, k);
149  }
150  /*
151  Now solve Transpose(L) * x = y.
152  */
153  for(k = n; k >= 1; k--) {
154  b[k] = b[k] / a[k][k];
155  t = -b[k];
156  daxpy1(t, a[k], b, k - 1);
157  }
158 }
159 
160 // See BLAS documentation. All double* has been changed to std::vector .
162  vector<vector<double>> &a, const size_t n, vector<int> &ipvt, size_t *const info)
163 {
164  size_t j = 0, k = 0, i = 0;
165  double t = 0.0;
166 
167  /* Gaussian elimination with partial pivoting. */
168 
169  *info = 0;
170  for(k = 1; k <= n - 1; k++) {
171  /*
172  Find j = pivot index. Note that a[k]+k-1 is the address of
173  the 0-th element of the row vector whose 1st element is a[k][k].
174  */
175  j = idamax1(a[k], n - k + 1, k - 1) + k - 1;
176  ipvt[k] = j;
177  /*
178  Zero pivot implies this row already triangularized.
179  */
180  if(a[k][j] == 0.) {
181  *info = k;
182  continue;
183  }
184  /*
185  Interchange if necessary.
186  */
187  if(j != k) {
188  t = a[k][j];
189  a[k][j] = a[k][k];
190  a[k][k] = t;
191  }
192  /*
193  Compute multipliers.
194  */
195  t = -1. / a[k][k];
196  dscal1(t, a[k], n - k, k);
197 
198  /*
199  Column elimination with row indexing.
200  */
201  for(i = k + 1; i <= n; i++) {
202  t = a[i][j];
203  if(j != k) {
204  a[i][j] = a[i][k];
205  a[i][k] = t;
206  }
207  daxpy1(t, a[k], a[i], n - k, k, k);
208  }
209  } /* end k-loop */
210 
211  ipvt[n] = n;
212  if(a[n][n] == 0.)
213  *info = n;
214 }
215 
216 /* Terminate lsoda due to illegal input. */
217 void LSODA::terminate(int *istate)
218 {
219  if(illin == 5)
220  cerr << "[lsoda] repeated occurrence of illegal input. run aborted.. "
221  "apparent infinite loop."
222  << endl;
223  else {
224  illin++;
225  *istate = -3;
226  }
227 }
228 
229 /* Terminate lsoda due to various error conditions. */
230 void LSODA::terminate2(vector<double> &y, double *t)
231 {
232  for(size_t i = 1; i <= n; i++)
233  y[i] = yh_[1][i];
234  *t = tn_;
235  illin = 0;
236  return;
237 }
238 
239 
240 
241 void LSODA::ewset(const vector<double> &ycur)
242 {
243  switch(itol_) {
244  case 1:
245  for(size_t i = 1; i <= n; i++)
246  ewt[i] = rtol_[1] * fabs(ycur[i]) + atol_[1];
247  break;
248  case 2:
249  for(size_t i = 1; i <= n; i++)
250  ewt[i] = rtol_[1] * fabs(ycur[i]) + atol_[i];
251  break;
252  case 3:
253  for(size_t i = 1; i <= n; i++)
254  ewt[i] = rtol_[i] * fabs(ycur[i]) + atol_[1];
255  break;
256  case 4:
257  for(size_t i = 1; i <= n; i++)
258  ewt[i] = rtol_[i] * fabs(ycur[i]) + atol_[i];
259  break;
260  }
261 
262 } /* end ewset */
263 
264 /*
265  Intdy computes interpolated values of the k-th derivative of the
266  dependent variable vector y, and stores it in dky. This routine
267  is called within the package with k = 0 and *t = tout, but may
268  also be called by the user for any k up to the current order.
269  ( See detailed instructions in the usage documentation. )
270 
271  The computed values in dky are gotten by interpolation using the
272  Nordsieck history array yh_. This array corresponds uniquely to a
273  vector-valued polynomial of degree nqcur or less, and dky is set
274  to the k-th derivative of this polynomial at t.
275  The formula for dky is
276 
277  q
278  dky[i] = sum c[k][j] * ( t - tn_ )^(j-k) * h_^(-j) * yh_[j+1][i]
279  j=k
280 
281  where c[k][j] = j*(j-1)*...*(j-k+1), q = nqcur, tn_ = tcur, h_ = hcur.
282  The quantities nq = nqcur, l = nq+1, n = neq, tn_, and h_ are declared
283  static globally. The above sum is done in reverse order.
284  *iflag is returned negative if either k or t is out of bounds.
285 */
286 void LSODA::intdy(double t, int k, vector<double> &dky, int *iflag)
287 {
288  int ic, jp1 = 0;
289  double c, r, s, tp;
290 
291  *iflag = 0;
292  if(k < 0 || k > (int)nq) {
293  fprintf(stderr, "[intdy] k = %d illegal\n", k);
294  *iflag = -1;
295  return;
296  }
297  tp = tn_ - hu - 100. * ETA * (tn_ + hu);
298  if((t - tp) * (t - tn_) > 0.) {
299  fprintf(
300  stderr, "intdy -- t = %g illegal. t not in interval tcur - hu to tcur\n", t);
301  *iflag = -2;
302  return;
303  }
304  s = (t - tn_) / h_;
305  ic = 1;
306  for(size_t jj = l - k; jj <= nq; jj++)
307  ic *= jj;
308  c = (double)ic;
309  for(size_t i = 1; i <= n; i++)
310  dky[i] = c * yh_[l][i];
311 
312  for(int j = nq - 1; j >= k; j--) {
313  jp1 = j + 1;
314  ic = 1;
315  for(int jj = jp1 - k; jj <= j; jj++)
316  ic *= jj;
317  c = (double)ic;
318 
319  for(size_t i = 1; i <= n; i++)
320  dky[i] = c * yh_[jp1][i] + s * dky[i];
321  }
322  if(k == 0)
323  return;
324  r = pow(h_, (double)(-k));
325 
326  for(size_t i = 1; i <= n; i++)
327  dky[i] *= r;
328 
329 } /* end intdy */
330 
331 void LSODA::cfode(int meth_)
332 {
333  int i, nq, nqm1, nqp1;
334  double agamq, fnq, fnqm1, pc[13], pint, ragq, rqfac, rq1fac, tsign, xpin;
335  /*
336  cfode is called by the integrator routine to set coefficients
337  needed there. The coefficients for the current method, as
338  given by the value of meth_, are set for all orders and saved.
339  The maximum order assumed here is 12 if meth_ = 1 and 5 if meth_ = 2.
340  ( A smaller value of the maximum order is also allowed. )
341  cfode is called once at the beginning of the problem, and
342  is not called again unless and until meth_ is changed.
343 
344  The elco array contains the basic method coefficients.
345  The coefficients el[i], 1 < i < nq+1, for the method of
346  order nq are stored in elco[nq][i]. They are given by a generating
347  polynomial, i.e.,
348 
349  l(x) = el[1] + el[2]*x + ... + el[nq+1]*x^nq.
350 
351  For the implicit Adams method, l(x) is given by
352 
353  dl/dx = (x+1)*(x+2)*...*(x+nq-1)/factorial(nq-1), l(-1) = 0.
354 
355  For the bdf methods, l(x) is given by
356 
357  l(x) = (x+1)*(x+2)*...*(x+nq)/k,
358 
359  where k = factorial(nq)*(1+1/2+...+1/nq).
360 
361  The tesco array contains test constants used for the
362  local error test and the selection of step size and/or order.
363  At order nq, tesco[nq][k] is used for the selection of step
364  size at order nq-1 if k = 1, at order nq if k = 2, and at order
365  nq+1 if k = 3.
366  */
367  if(meth_ == 1) {
368  elco[1][1] = 1.;
369  elco[1][2] = 1.;
370  tesco[1][1] = 0.;
371  tesco[1][2] = 2.;
372  tesco[2][1] = 1.;
373  tesco[12][3] = 0.;
374  pc[1] = 1.;
375  rqfac = 1.;
376  for(nq = 2; nq <= 12; nq++) {
377  /*
378  The pc array will contain the coefficients of the polynomial
379 
380  p(x) = (x+1)*(x+2)*...*(x+nq-1).
381 
382  Initially, p(x) = 1.
383  */
384  rq1fac = rqfac;
385  rqfac = rqfac / (double)nq;
386  nqm1 = nq - 1;
387  fnqm1 = (double)nqm1;
388  nqp1 = nq + 1;
389  /*
390  Form coefficients of p(x)*(x+nq-1).
391  */
392  pc[nq] = 0.;
393  for(i = nq; i >= 2; i--)
394  pc[i] = pc[i - 1] + fnqm1 * pc[i];
395  pc[1] = fnqm1 * pc[1];
396  /*
397  Compute integral, -1 to 0, of p(x) and x*p(x).
398  */
399  pint = pc[1];
400  xpin = pc[1] / 2.;
401  tsign = 1.;
402  for(i = 2; i <= nq; i++) {
403  tsign = -tsign;
404  pint += tsign * pc[i] / (double)i;
405  xpin += tsign * pc[i] / (double)(i + 1);
406  }
407  /*
408  Store coefficients in elco and tesco.
409  */
410  elco[nq][1] = pint * rq1fac;
411  elco[nq][2] = 1.;
412  for(i = 2; i <= nq; i++)
413  elco[nq][i + 1] = rq1fac * pc[i] / (double)i;
414  agamq = rqfac * xpin;
415  ragq = 1. / agamq;
416  tesco[nq][2] = ragq;
417  if(nq < 12)
418  tesco[nqp1][1] = ragq * rqfac / (double)nqp1;
419  tesco[nqm1][3] = ragq;
420  } /* end for */
421  return;
422  } /* end if ( meth_ == 1 ) */
423 
424  /* meth_ = 2. */
425  pc[1] = 1.;
426  rq1fac = 1.;
427 
428  /*
429  The pc array will contain the coefficients of the polynomial
430  p(x) = (x+1)*(x+2)*...*(x+nq).
431  Initially, p(x) = 1.
432  */
433  for(nq = 1; nq <= 5; nq++) {
434  fnq = (double)nq;
435  nqp1 = nq + 1;
436  /*
437  Form coefficients of p(x)*(x+nq).
438  */
439  pc[nqp1] = 0.;
440  for(i = nq + 1; i >= 2; i--)
441  pc[i] = pc[i - 1] + fnq * pc[i];
442  pc[1] *= fnq;
443  /*
444  Store coefficients in elco and tesco.
445  */
446  for(i = 1; i <= nqp1; i++)
447  elco[nq][i] = pc[i] / pc[2];
448  elco[nq][2] = 1.;
449  tesco[nq][1] = rq1fac;
450  tesco[nq][2] = ((double)nqp1) / elco[nq][1];
451  tesco[nq][3] = ((double)(nq + 2)) / elco[nq][1];
452  rq1fac /= fnq;
453  }
454  return;
455 
456 } /* end cfode */
457 
458 void LSODA::scaleh(double *rh, double *pdh)
459 {
460  double r;
461  /*
462  If h_ is being changed, the h_ ratio rh is checked against rmax, hmin,
463  and hmxi, and the yh_ array is rescaled. ialth is set to l = nq + 1
464  to prevent a change of h_ for that many steps, unless forced by a
465  convergence or error test failure.
466  */
467  *rh = min(*rh, rmax);
468  *rh = *rh / max(1., fabs(h_) * hmxi * *rh);
469  /*
470  If meth_ = 1, also restrict the new step size by the stability region.
471  If this reduces h_, set irflag to 1 so that if there are roundoff
472  problems later, we can assume that is the cause of the trouble.
473  */
474  if(meth_ == 1) {
475  irflag = 0;
476  *pdh = max(fabs(h_) * pdlast, 0.000001);
477  if((*rh * *pdh * 1.00001) >= sm1[nq]) {
478  *rh = sm1[nq] / *pdh;
479  irflag = 1;
480  }
481  }
482  r = 1.;
483  for(size_t j = 2; j <= l; j++) {
484  r *= *rh;
485  for(size_t i = 1; i <= n; i++)
486  yh_[j][i] *= r;
487  }
488  h_ *= *rh;
489  rc *= *rh;
490  ialth = l;
491 
492 } /* end scaleh */
493 
494 /*
495  This function routine computes the weighted max-norm
496  of the vector of length n contained in the array v, with weights
497  contained in the array w of length n.
498 
499  vmnorm = max( i = 1, ..., n ) fabs( v[i] ) * w[i].
500 */
501 double LSODA::vmnorm(const size_t n, const vector<double> &v, const vector<double> &w)
502 {
503  double vm = 0.;
504  for(size_t i = 1; i <= n; i++)
505  vm = max(vm, fabs(v[i]) * w[i]);
506  return vm;
507 }
508 
509 double LSODA::fnorm(int n, const vector<vector<double>> &a, const vector<double> &w)
510 
511 /*
512  This subroutine computes the norm of a full n by n matrix,
513  stored in the array a, that is consistent with the weighted max-norm
514  on vectors, with weights stored in the array w.
515 
516  fnorm = max(i=1,...,n) ( w[i] * sum(j=1,...,n) fabs( a[i][j] ) / w[j] )
517 */
518 
519 {
520  double an = 0, sum = 0;
521 
522  for(size_t i = 1; i <= (size_t)n; i++) {
523  sum = 0.;
524  for(size_t j = 1; j <= (size_t)n; j++)
525  sum += fabs(a[i][j]) / w[j];
526  an = max(an, sum * w[i]);
527  }
528  return an;
529 }
530 
531 
532 void LSODA::corfailure(double *told, double *rh, size_t *ncf, size_t *corflag)
533 {
534  ncf++;
535  rmax = 2.;
536  tn_ = *told;
537  for(size_t j = nq; j >= 1; j--)
538  for(size_t i1 = j; i1 <= nq; i1++)
539  for(size_t i = 1; i <= n; i++)
540  yh_[i1][i] -= yh_[i1 + 1][i];
541 
542  if(fabs(h_) <= hmin * 1.00001 || *ncf == mxncf) {
543  *corflag = 2;
544  return;
545  }
546  *corflag = 1;
547  *rh = 0.25;
548  ipup = miter;
549 }
550 
551 /*
552  This routine manages the solution of the linear system arising from
553  a chord iteration. It is called if miter != 0.
554  If miter is 2, it calls dgesl to accomplish this.
555  If miter is 5, it calls dgbsl.
556 
557  y = the right-hand side vector on input, and the solution vector
558  on output.
559 */
560 void LSODA::solsy(vector<double> &y)
561 {
562  iersl = 0;
563  if(miter != 2) {
564  printf("solsy -- miter != 2\n");
565  return;
566  }
567  if(miter == 2)
568  dgesl(wm_, n, ipvt, y, 0);
569  return;
570 }
571 
572 void LSODA::methodswitch(double dsm, double pnorm, double *pdh, double *rh)
573 {
574  int lm1, lm1p1, lm2, lm2p1, nqm1, nqm2;
575  double rh1, rh2, rh1it, exm2, dm2, exm1, dm1, alpha, exsm;
576 
577  /*
578  We are current using an Adams method. Consider switching to bdf.
579  If the current order is greater than 5, assume the problem is
580  not stiff, and skip this section.
581  If the Lipschitz constant and error estimate are not polluted
582  by roundoff, perform the usual test.
583  Otherwise, switch to the bdf methods if the last step was
584  restricted to insure stability ( irflag = 1 ), and stay with Adams
585  method if not. When switching to bdf with polluted error estimates,
586  in the absence of other information, double the step size.
587 
588  When the estimates are ok, we make the usual test by computing
589  the step size we could have (ideally) used on this step,
590  with the current (Adams) method, and also that for the bdf.
591  If nq > mxords, we consider changing to order mxords on switching.
592  Compare the two step sizes to decide whether to switch.
593  The step size advantage must be at least ratio = 5 to switch.
594  */
595  if(meth_ == 1) {
596  if(nq > 5)
597  return;
598  if(dsm <= (100. * pnorm * ETA) || pdest == 0.) {
599  if(irflag == 0)
600  return;
601  rh2 = 2.;
602  nqm2 = min(nq, mxords);
603  }
604  else {
605  exsm = 1. / (double)l;
606  rh1 = 1. / (1.2 * pow(dsm, exsm) + 0.0000012);
607  rh1it = 2. * rh1;
608  *pdh = pdlast * fabs(h_);
609  if((*pdh * rh1) > 0.00001)
610  rh1it = sm1[nq] / *pdh;
611  rh1 = min(rh1, rh1it);
612  if(nq > mxords) {
613  nqm2 = mxords;
614  lm2 = mxords + 1;
615  exm2 = 1. / (double)lm2;
616  lm2p1 = lm2 + 1;
617  dm2 = vmnorm(n, yh_[lm2p1], ewt) / cm2[mxords];
618  rh2 = 1. / (1.2 * pow(dm2, exm2) + 0.0000012);
619  }
620  else {
621  dm2 = dsm * (cm1[nq] / cm2[nq]);
622  rh2 = 1. / (1.2 * pow(dm2, exsm) + 0.0000012);
623  nqm2 = nq;
624  }
625  if(rh2 < ratio * rh1)
626  return;
627  }
628  /*
629  The method switch test passed. Reset relevant quantities for bdf.
630  */
631  *rh = rh2;
632  icount = 20;
633  meth_ = 2;
634  miter = jtyp;
635  pdlast = 0.;
636  nq = nqm2;
637  l = nq + 1;
638  return;
639  } /* end if ( meth_ == 1 ) */
640 
641  /*
642  We are currently using a bdf method, considering switching to Adams.
643  Compute the step size we could have (ideally) used on this step,
644  with the current (bdf) method, and also that for the Adams.
645  If nq > mxordn, we consider changing to order mxordn on switching.
646  Compare the two step sizes to decide whether to switch.
647  The step size advantage must be at least 5/ratio = 1 to switch.
648  If the step size for Adams would be so small as to cause
649  roundoff pollution, we stay with bdf.
650  */
651  exsm = 1. / (double)l;
652  if(mxordn < nq) {
653  nqm1 = mxordn;
654  lm1 = mxordn + 1;
655  exm1 = 1. / (double)lm1;
656  lm1p1 = lm1 + 1;
657  dm1 = vmnorm(n, yh_[lm1p1], ewt) / cm1[mxordn];
658  rh1 = 1. / (1.2 * pow(dm1, exm1) + 0.0000012);
659  }
660  else {
661  dm1 = dsm * (cm2[nq] / cm1[nq]);
662  rh1 = 1. / (1.2 * pow(dm1, exsm) + 0.0000012);
663  nqm1 = nq;
664  exm1 = exsm;
665  }
666  rh1it = 2. * rh1;
667  *pdh = pdnorm * fabs(h_);
668  if((*pdh * rh1) > 0.00001)
669  rh1it = sm1[nqm1] / *pdh;
670  rh1 = min(rh1, rh1it);
671  rh2 = 1. / (1.2 * pow(dsm, exsm) + 0.0000012);
672  if((rh1 * ratio) < (5. * rh2))
673  return;
674  alpha = max(0.001, rh1);
675  dm1 *= pow(alpha, exm1);
676  if(dm1 <= 1000. * ETA * pnorm)
677  return;
678  /*
679  The switch test passed. Reset relevant quantities for Adams.
680  */
681  *rh = rh1;
682  icount = 20;
683  meth_ = 1;
684  miter = 0;
685  pdlast = 0.;
686  nq = nqm1;
687  l = nq + 1;
688 } /* end methodswitch */
689 
690 /*
691  This routine returns from stoda to lsoda. Hence freevectors() is
692  not executed.
693 */
695 {
696  double r = 1. / tesco[nqu][2];
697  for(size_t i = 1; i <= n; i++)
698  acor[i] *= r;
699  hold = h_;
700  jstart = 1;
701 }
702 
703 /*
704  Regardless of the success or failure of the step, factors
705  rhdn, rhsm, and rhup are computed, by which h_ could be multiplied
706  at order nq - 1, order nq, or order nq + 1, respectively.
707  In the case of a failure, rhup = 0. to avoid an order increase.
708  The largest of these is determined and the new order chosen
709  accordingly. If the order is to be increased, we compute one
710  additional scaled derivative.
711 
712  orderflag = 0 : no change in h_ or nq,
713  1 : change in h_ but not nq,
714  2 : change in both h_ and nq.
715 */
717  double *rhup, double dsm, double *pdh, double *rh, size_t *orderflag)
718 {
719  size_t newq = 0;
720  double exsm, rhdn, rhsm, ddn, exdn, r;
721 
722  *orderflag = 0;
723 
724  exsm = 1. / (double)l;
725  rhsm = 1. / (1.2 * pow(dsm, exsm) + 0.0000012);
726 
727  rhdn = 0.;
728  if(nq != 1) {
729  ddn = vmnorm(n, yh_[l], ewt) / tesco[nq][1];
730  exdn = 1. / (double)nq;
731  rhdn = 1. / (1.3 * pow(ddn, exdn) + 0.0000013);
732  }
733  /*
734  If meth_ = 1, limit rh accordinfg to the stability region also.
735  */
736  if(meth_ == 1) {
737  *pdh = max(fabs(h_) * pdlast, 0.000001);
738  if(l < lmax)
739  *rhup = min(*rhup, sm1[l] / *pdh);
740  rhsm = min(rhsm, sm1[nq] / *pdh);
741  if(nq > 1)
742  rhdn = min(rhdn, sm1[nq - 1] / *pdh);
743  pdest = 0.;
744  }
745  if(rhsm >= *rhup) {
746  if(rhsm >= rhdn) {
747  newq = nq;
748  *rh = rhsm;
749  }
750  else {
751  newq = nq - 1;
752  *rh = rhdn;
753  if(kflag < 0 && *rh > 1.)
754  *rh = 1.;
755  }
756  }
757  else {
758  if(*rhup <= rhdn) {
759  newq = nq - 1;
760  *rh = rhdn;
761  if(kflag < 0 && *rh > 1.)
762  *rh = 1.;
763  }
764  else {
765  *rh = *rhup;
766  if(*rh >= 1.1) {
767  r = el[l] / (double)l;
768  nq = l;
769  l = nq + 1;
770  for(size_t i = 1; i <= n; i++)
771  yh_[l][i] = acor[i] * r;
772 
773  *orderflag = 2;
774  return;
775  }
776  else {
777  ialth = 3;
778  return;
779  }
780  }
781  }
782  /*
783  If meth_ = 1 and h_ is restricted by stability, bypass 10 percent test.
784  */
785  if(1 == meth_) {
786  if((*rh * *pdh * 1.00001) < sm1[newq])
787  if(kflag == 0 && *rh < 1.1) {
788  ialth = 3;
789  return;
790  }
791  }
792  else {
793  if(kflag == 0 && *rh < 1.1) {
794  ialth = 3;
795  return;
796  }
797  }
798  if(kflag <= -2)
799  *rh = min(*rh, 0.2);
800  /*
801  If there is a change of order, reset nq, l, and the coefficients.
802  In any case h_ is reset according to rh and the yh_ array is rescaled.
803  Then exit or redo the step.
804  */
805  if(newq == nq) {
806  *orderflag = 1;
807  return;
808  }
809  nq = newq;
810  l = nq + 1;
811  *orderflag = 2;
812 
813 } /* end orderswitch */
814 
816 /*
817  The el vector and related constants are reset
818  whenever the order nq is changed, or at the start of the problem.
819 */
820 {
821  array<double, 14> ep1;
822 
823  ep1 = elco[nq];
824  for(size_t i = 1; i <= l; i++)
825  el[i] = ep1[i];
826  rc = rc * el[1] / el0;
827  el0 = el[1];
828  conit = 0.5 / (double)(nq + 2);
829 }
830 
832 {
833  // Does nothing. USE c++ memory mechanism here.
834 }
835 
836 
837 void LSODA::resize_system(int nyh, int lenyh){
838  // yh_ and wm_ need a clear() because otherwise, only additional elements will have the new length nyh+1
839  yh_.clear(); yh_.resize(lenyh + 1, std::vector<double>(nyh + 1, 0.0));
840  wm_.clear(); wm_.resize(nyh + 1, std::vector<double>(nyh + 1, 0.0));
841  ewt.resize(1 + nyh, 0);
842  savf.resize(1 + nyh, 0);
843  acor.resize(nyh + 1, 0.0);
844  ipvt.resize(nyh + 1, 0.0);
845 }
846 
847 
849  return nfe;
850 }
851 
852 
853 int LSODA::get_istate() const{
854  return iState;
855 }
856 
857 void LSODA::set_istate(int n){
858  iState = n;
859 }
void orderswitch(double *rhup, double dsm, double *pdh, double *rh, size_t *orderflag)
Definition: lsoda.cpp:716
void terminate(int *istate)
Definition: lsoda.cpp:217
static bool abs_compare(double a, double b)
Definition: lsoda.cpp:48
void ewset(const std::vector< double > &ycur)
Definition: lsoda.cpp:241
void scaleh(double *rh, double *pdh)
Definition: lsoda.cpp:458
STL namespace.
void solsy(std::vector< double > &y)
Definition: lsoda.cpp:560
void _freevectors(void)
Definition: lsoda.cpp:831
int get_fncalls()
Definition: lsoda.cpp:848
double vmnorm(const size_t n, const std::vector< double > &v, const std::vector< double > &w)
Definition: lsoda.cpp:501
double fnorm(int n, const std::vector< std::vector< double >> &a, const std::vector< double > &w)
Definition: lsoda.cpp:509
void cfode(int meth_)
Definition: lsoda.cpp:331
~LSODA()
Definition: lsoda.cpp:44
void dscal1(const double da, std::vector< double > &dx, const size_t n, const size_t offset)
Definition: lsoda.cpp:76
void terminate2(std::vector< double > &y, double *t)
Definition: lsoda.cpp:230
void intdy(double t, int k, std::vector< double > &dky, int *iflag)
Definition: lsoda.cpp:286
void corfailure(double *told, double *rh, size_t *ncf, size_t *corflag)
Definition: lsoda.cpp:532
void endstoda(void)
Definition: lsoda.cpp:694
size_t idamax1(const std::vector< double > &dx, const size_t n, const size_t offset)
Definition: lsoda.cpp:54
void resetcoeff(void)
Definition: lsoda.cpp:815
void resize_system(int nyh, int lenyh)
Definition: lsoda.cpp:837
void methodswitch(double dsm, double pnorm, double *pdh, double *rh)
Definition: lsoda.cpp:572
void dgesl(const std::vector< std::vector< double >> &a, const size_t n, std::vector< int > &ipvt, std::vector< double > &b, const size_t job)
Definition: lsoda.cpp:105
void dgefa(std::vector< std::vector< double >> &a, const size_t n, std::vector< int > &ipvt, size_t *const info)
Definition: lsoda.cpp:161
double ddot1(const std::vector< double > &a, const std::vector< double > &b, const size_t n, const size_t offsetA, const size_t offsetB)
Definition: lsoda.cpp:87
void daxpy1(const double da, const std::vector< double > &dx, std::vector< double > &dy, const size_t n, const size_t offsetX, const size_t offsetY)
Definition: lsoda.cpp:96
LSODA()
Definition: lsoda.cpp:34
void set_istate(int n)
Definition: lsoda.cpp:857
int get_istate() const
Definition: lsoda.cpp:853