_ode.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371
  1. # Authors: Pearu Peterson, Pauli Virtanen, John Travers
  2. """
  3. First-order ODE integrators.
  4. User-friendly interface to various numerical integrators for solving a
  5. system of first order ODEs with prescribed initial conditions::
  6. d y(t)[i]
  7. --------- = f(t,y(t))[i],
  8. d t
  9. y(t=0)[i] = y0[i],
  10. where::
  11. i = 0, ..., len(y0) - 1
  12. class ode
  13. ---------
  14. A generic interface class to numeric integrators. It has the following
  15. methods::
  16. integrator = ode(f, jac=None)
  17. integrator = integrator.set_integrator(name, **params)
  18. integrator = integrator.set_initial_value(y0, t0=0.0)
  19. integrator = integrator.set_f_params(*args)
  20. integrator = integrator.set_jac_params(*args)
  21. y1 = integrator.integrate(t1, step=False, relax=False)
  22. flag = integrator.successful()
  23. class complex_ode
  24. -----------------
  25. This class has the same generic interface as ode, except it can handle complex
  26. f, y and Jacobians by transparently translating them into the equivalent
  27. real valued system. It supports the real valued solvers (i.e not zvode) and is
  28. an alternative to ode with the zvode solver, sometimes performing better.
  29. """
  30. from __future__ import division, print_function, absolute_import
  31. # XXX: Integrators must have:
  32. # ===========================
  33. # cvode - C version of vode and vodpk with many improvements.
  34. # Get it from http://www.netlib.org/ode/cvode.tar.gz
  35. # To wrap cvode to Python, one must write extension module by
  36. # hand. Its interface is too much 'advanced C' that using f2py
  37. # would be too complicated (or impossible).
  38. #
  39. # How to define a new integrator:
  40. # ===============================
  41. #
  42. # class myodeint(IntegratorBase):
  43. #
  44. # runner = <odeint function> or None
  45. #
  46. # def __init__(self,...): # required
  47. # <initialize>
  48. #
  49. # def reset(self,n,has_jac): # optional
  50. # # n - the size of the problem (number of equations)
  51. # # has_jac - whether user has supplied its own routine for Jacobian
  52. # <allocate memory,initialize further>
  53. #
  54. # def run(self,f,jac,y0,t0,t1,f_params,jac_params): # required
  55. # # this method is called to integrate from t=t0 to t=t1
  56. # # with initial condition y0. f and jac are user-supplied functions
  57. # # that define the problem. f_params,jac_params are additional
  58. # # arguments
  59. # # to these functions.
  60. # <calculate y1>
  61. # if <calculation was unsuccessful>:
  62. # self.success = 0
  63. # return t1,y1
  64. #
  65. # # In addition, one can define step() and run_relax() methods (they
  66. # # take the same arguments as run()) if the integrator can support
  67. # # these features (see IntegratorBase doc strings).
  68. #
  69. # if myodeint.runner:
  70. # IntegratorBase.integrator_classes.append(myodeint)
  71. __all__ = ['ode', 'complex_ode']
  72. __version__ = "$Id$"
  73. __docformat__ = "restructuredtext en"
  74. import re
  75. import warnings
  76. from numpy import asarray, array, zeros, int32, isscalar, real, imag, vstack
  77. from . import vode as _vode
  78. from . import _dop
  79. from . import lsoda as _lsoda
  80. # ------------------------------------------------------------------------------
  81. # User interface
  82. # ------------------------------------------------------------------------------
  83. class ode(object):
  84. """
  85. A generic interface class to numeric integrators.
  86. Solve an equation system :math:`y'(t) = f(t,y)` with (optional) ``jac = df/dy``.
  87. *Note*: The first two arguments of ``f(t, y, ...)`` are in the
  88. opposite order of the arguments in the system definition function used
  89. by `scipy.integrate.odeint`.
  90. Parameters
  91. ----------
  92. f : callable ``f(t, y, *f_args)``
  93. Right-hand side of the differential equation. t is a scalar,
  94. ``y.shape == (n,)``.
  95. ``f_args`` is set by calling ``set_f_params(*args)``.
  96. `f` should return a scalar, array or list (not a tuple).
  97. jac : callable ``jac(t, y, *jac_args)``, optional
  98. Jacobian of the right-hand side, ``jac[i,j] = d f[i] / d y[j]``.
  99. ``jac_args`` is set by calling ``set_jac_params(*args)``.
  100. Attributes
  101. ----------
  102. t : float
  103. Current time.
  104. y : ndarray
  105. Current variable values.
  106. See also
  107. --------
  108. odeint : an integrator with a simpler interface based on lsoda from ODEPACK
  109. quad : for finding the area under a curve
  110. Notes
  111. -----
  112. Available integrators are listed below. They can be selected using
  113. the `set_integrator` method.
  114. "vode"
  115. Real-valued Variable-coefficient Ordinary Differential Equation
  116. solver, with fixed-leading-coefficient implementation. It provides
  117. implicit Adams method (for non-stiff problems) and a method based on
  118. backward differentiation formulas (BDF) (for stiff problems).
  119. Source: http://www.netlib.org/ode/vode.f
  120. .. warning::
  121. This integrator is not re-entrant. You cannot have two `ode`
  122. instances using the "vode" integrator at the same time.
  123. This integrator accepts the following parameters in `set_integrator`
  124. method of the `ode` class:
  125. - atol : float or sequence
  126. absolute tolerance for solution
  127. - rtol : float or sequence
  128. relative tolerance for solution
  129. - lband : None or int
  130. - uband : None or int
  131. Jacobian band width, jac[i,j] != 0 for i-lband <= j <= i+uband.
  132. Setting these requires your jac routine to return the jacobian
  133. in packed format, jac_packed[i-j+uband, j] = jac[i,j]. The
  134. dimension of the matrix must be (lband+uband+1, len(y)).
  135. - method: 'adams' or 'bdf'
  136. Which solver to use, Adams (non-stiff) or BDF (stiff)
  137. - with_jacobian : bool
  138. This option is only considered when the user has not supplied a
  139. Jacobian function and has not indicated (by setting either band)
  140. that the Jacobian is banded. In this case, `with_jacobian` specifies
  141. whether the iteration method of the ODE solver's correction step is
  142. chord iteration with an internally generated full Jacobian or
  143. functional iteration with no Jacobian.
  144. - nsteps : int
  145. Maximum number of (internally defined) steps allowed during one
  146. call to the solver.
  147. - first_step : float
  148. - min_step : float
  149. - max_step : float
  150. Limits for the step sizes used by the integrator.
  151. - order : int
  152. Maximum order used by the integrator,
  153. order <= 12 for Adams, <= 5 for BDF.
  154. "zvode"
  155. Complex-valued Variable-coefficient Ordinary Differential Equation
  156. solver, with fixed-leading-coefficient implementation. It provides
  157. implicit Adams method (for non-stiff problems) and a method based on
  158. backward differentiation formulas (BDF) (for stiff problems).
  159. Source: http://www.netlib.org/ode/zvode.f
  160. .. warning::
  161. This integrator is not re-entrant. You cannot have two `ode`
  162. instances using the "zvode" integrator at the same time.
  163. This integrator accepts the same parameters in `set_integrator`
  164. as the "vode" solver.
  165. .. note::
  166. When using ZVODE for a stiff system, it should only be used for
  167. the case in which the function f is analytic, that is, when each f(i)
  168. is an analytic function of each y(j). Analyticity means that the
  169. partial derivative df(i)/dy(j) is a unique complex number, and this
  170. fact is critical in the way ZVODE solves the dense or banded linear
  171. systems that arise in the stiff case. For a complex stiff ODE system
  172. in which f is not analytic, ZVODE is likely to have convergence
  173. failures, and for this problem one should instead use DVODE on the
  174. equivalent real system (in the real and imaginary parts of y).
  175. "lsoda"
  176. Real-valued Variable-coefficient Ordinary Differential Equation
  177. solver, with fixed-leading-coefficient implementation. It provides
  178. automatic method switching between implicit Adams method (for non-stiff
  179. problems) and a method based on backward differentiation formulas (BDF)
  180. (for stiff problems).
  181. Source: http://www.netlib.org/odepack
  182. .. warning::
  183. This integrator is not re-entrant. You cannot have two `ode`
  184. instances using the "lsoda" integrator at the same time.
  185. This integrator accepts the following parameters in `set_integrator`
  186. method of the `ode` class:
  187. - atol : float or sequence
  188. absolute tolerance for solution
  189. - rtol : float or sequence
  190. relative tolerance for solution
  191. - lband : None or int
  192. - uband : None or int
  193. Jacobian band width, jac[i,j] != 0 for i-lband <= j <= i+uband.
  194. Setting these requires your jac routine to return the jacobian
  195. in packed format, jac_packed[i-j+uband, j] = jac[i,j].
  196. - with_jacobian : bool
  197. *Not used.*
  198. - nsteps : int
  199. Maximum number of (internally defined) steps allowed during one
  200. call to the solver.
  201. - first_step : float
  202. - min_step : float
  203. - max_step : float
  204. Limits for the step sizes used by the integrator.
  205. - max_order_ns : int
  206. Maximum order used in the nonstiff case (default 12).
  207. - max_order_s : int
  208. Maximum order used in the stiff case (default 5).
  209. - max_hnil : int
  210. Maximum number of messages reporting too small step size (t + h = t)
  211. (default 0)
  212. - ixpr : int
  213. Whether to generate extra printing at method switches (default False).
  214. "dopri5"
  215. This is an explicit runge-kutta method of order (4)5 due to Dormand &
  216. Prince (with stepsize control and dense output).
  217. Authors:
  218. E. Hairer and G. Wanner
  219. Universite de Geneve, Dept. de Mathematiques
  220. CH-1211 Geneve 24, Switzerland
  221. e-mail: ernst.hairer@math.unige.ch, gerhard.wanner@math.unige.ch
  222. This code is described in [HNW93]_.
  223. This integrator accepts the following parameters in set_integrator()
  224. method of the ode class:
  225. - atol : float or sequence
  226. absolute tolerance for solution
  227. - rtol : float or sequence
  228. relative tolerance for solution
  229. - nsteps : int
  230. Maximum number of (internally defined) steps allowed during one
  231. call to the solver.
  232. - first_step : float
  233. - max_step : float
  234. - safety : float
  235. Safety factor on new step selection (default 0.9)
  236. - ifactor : float
  237. - dfactor : float
  238. Maximum factor to increase/decrease step size by in one step
  239. - beta : float
  240. Beta parameter for stabilised step size control.
  241. - verbosity : int
  242. Switch for printing messages (< 0 for no messages).
  243. "dop853"
  244. This is an explicit runge-kutta method of order 8(5,3) due to Dormand
  245. & Prince (with stepsize control and dense output).
  246. Options and references the same as "dopri5".
  247. Examples
  248. --------
  249. A problem to integrate and the corresponding jacobian:
  250. >>> from scipy.integrate import ode
  251. >>>
  252. >>> y0, t0 = [1.0j, 2.0], 0
  253. >>>
  254. >>> def f(t, y, arg1):
  255. ... return [1j*arg1*y[0] + y[1], -arg1*y[1]**2]
  256. >>> def jac(t, y, arg1):
  257. ... return [[1j*arg1, 1], [0, -arg1*2*y[1]]]
  258. The integration:
  259. >>> r = ode(f, jac).set_integrator('zvode', method='bdf')
  260. >>> r.set_initial_value(y0, t0).set_f_params(2.0).set_jac_params(2.0)
  261. >>> t1 = 10
  262. >>> dt = 1
  263. >>> while r.successful() and r.t < t1:
  264. ... print(r.t+dt, r.integrate(r.t+dt))
  265. 1 [-0.71038232+0.23749653j 0.40000271+0.j ]
  266. 2.0 [0.19098503-0.52359246j 0.22222356+0.j ]
  267. 3.0 [0.47153208+0.52701229j 0.15384681+0.j ]
  268. 4.0 [-0.61905937+0.30726255j 0.11764744+0.j ]
  269. 5.0 [0.02340997-0.61418799j 0.09523835+0.j ]
  270. 6.0 [0.58643071+0.339819j 0.08000018+0.j ]
  271. 7.0 [-0.52070105+0.44525141j 0.06896565+0.j ]
  272. 8.0 [-0.15986733-0.61234476j 0.06060616+0.j ]
  273. 9.0 [0.64850462+0.15048982j 0.05405414+0.j ]
  274. 10.0 [-0.38404699+0.56382299j 0.04878055+0.j ]
  275. References
  276. ----------
  277. .. [HNW93] E. Hairer, S.P. Norsett and G. Wanner, Solving Ordinary
  278. Differential Equations i. Nonstiff Problems. 2nd edition.
  279. Springer Series in Computational Mathematics,
  280. Springer-Verlag (1993)
  281. """
  282. def __init__(self, f, jac=None):
  283. self.stiff = 0
  284. self.f = f
  285. self.jac = jac
  286. self.f_params = ()
  287. self.jac_params = ()
  288. self._y = []
  289. @property
  290. def y(self):
  291. return self._y
  292. def set_initial_value(self, y, t=0.0):
  293. """Set initial conditions y(t) = y."""
  294. if isscalar(y):
  295. y = [y]
  296. n_prev = len(self._y)
  297. if not n_prev:
  298. self.set_integrator('') # find first available integrator
  299. self._y = asarray(y, self._integrator.scalar)
  300. self.t = t
  301. self._integrator.reset(len(self._y), self.jac is not None)
  302. return self
  303. def set_integrator(self, name, **integrator_params):
  304. """
  305. Set integrator by name.
  306. Parameters
  307. ----------
  308. name : str
  309. Name of the integrator.
  310. integrator_params
  311. Additional parameters for the integrator.
  312. """
  313. integrator = find_integrator(name)
  314. if integrator is None:
  315. # FIXME: this really should be raise an exception. Will that break
  316. # any code?
  317. warnings.warn('No integrator name match with %r or is not '
  318. 'available.' % name)
  319. else:
  320. self._integrator = integrator(**integrator_params)
  321. if not len(self._y):
  322. self.t = 0.0
  323. self._y = array([0.0], self._integrator.scalar)
  324. self._integrator.reset(len(self._y), self.jac is not None)
  325. return self
  326. def integrate(self, t, step=False, relax=False):
  327. """Find y=y(t), set y as an initial condition, and return y.
  328. Parameters
  329. ----------
  330. t : float
  331. The endpoint of the integration step.
  332. step : bool
  333. If True, and if the integrator supports the step method,
  334. then perform a single integration step and return.
  335. This parameter is provided in order to expose internals of
  336. the implementation, and should not be changed from its default
  337. value in most cases.
  338. relax : bool
  339. If True and if the integrator supports the run_relax method,
  340. then integrate until t_1 >= t and return. ``relax`` is not
  341. referenced if ``step=True``.
  342. This parameter is provided in order to expose internals of
  343. the implementation, and should not be changed from its default
  344. value in most cases.
  345. Returns
  346. -------
  347. y : float
  348. The integrated value at t
  349. """
  350. if step and self._integrator.supports_step:
  351. mth = self._integrator.step
  352. elif relax and self._integrator.supports_run_relax:
  353. mth = self._integrator.run_relax
  354. else:
  355. mth = self._integrator.run
  356. try:
  357. self._y, self.t = mth(self.f, self.jac or (lambda: None),
  358. self._y, self.t, t,
  359. self.f_params, self.jac_params)
  360. except SystemError:
  361. # f2py issue with tuple returns, see ticket 1187.
  362. raise ValueError('Function to integrate must not return a tuple.')
  363. return self._y
  364. def successful(self):
  365. """Check if integration was successful."""
  366. try:
  367. self._integrator
  368. except AttributeError:
  369. self.set_integrator('')
  370. return self._integrator.success == 1
  371. def get_return_code(self):
  372. """Extracts the return code for the integration to enable better control
  373. if the integration fails.
  374. In general, a return code > 0 implies success while a return code < 0
  375. implies failure.
  376. Notes
  377. -----
  378. This section describes possible return codes and their meaning, for available
  379. integrators that can be selected by `set_integrator` method.
  380. "vode"
  381. =========== =======
  382. Return Code Message
  383. =========== =======
  384. 2 Integration successful.
  385. -1 Excess work done on this call. (Perhaps wrong MF.)
  386. -2 Excess accuracy requested. (Tolerances too small.)
  387. -3 Illegal input detected. (See printed message.)
  388. -4 Repeated error test failures. (Check all input.)
  389. -5 Repeated convergence failures. (Perhaps bad Jacobian
  390. supplied or wrong choice of MF or tolerances.)
  391. -6 Error weight became zero during problem. (Solution
  392. component i vanished, and ATOL or ATOL(i) = 0.)
  393. =========== =======
  394. "zvode"
  395. =========== =======
  396. Return Code Message
  397. =========== =======
  398. 2 Integration successful.
  399. -1 Excess work done on this call. (Perhaps wrong MF.)
  400. -2 Excess accuracy requested. (Tolerances too small.)
  401. -3 Illegal input detected. (See printed message.)
  402. -4 Repeated error test failures. (Check all input.)
  403. -5 Repeated convergence failures. (Perhaps bad Jacobian
  404. supplied or wrong choice of MF or tolerances.)
  405. -6 Error weight became zero during problem. (Solution
  406. component i vanished, and ATOL or ATOL(i) = 0.)
  407. =========== =======
  408. "dopri5"
  409. =========== =======
  410. Return Code Message
  411. =========== =======
  412. 1 Integration successful.
  413. 2 Integration successful (interrupted by solout).
  414. -1 Input is not consistent.
  415. -2 Larger nsteps is needed.
  416. -3 Step size becomes too small.
  417. -4 Problem is probably stiff (interrupted).
  418. =========== =======
  419. "dop853"
  420. =========== =======
  421. Return Code Message
  422. =========== =======
  423. 1 Integration successful.
  424. 2 Integration successful (interrupted by solout).
  425. -1 Input is not consistent.
  426. -2 Larger nsteps is needed.
  427. -3 Step size becomes too small.
  428. -4 Problem is probably stiff (interrupted).
  429. =========== =======
  430. "lsoda"
  431. =========== =======
  432. Return Code Message
  433. =========== =======
  434. 2 Integration successful.
  435. -1 Excess work done on this call (perhaps wrong Dfun type).
  436. -2 Excess accuracy requested (tolerances too small).
  437. -3 Illegal input detected (internal error).
  438. -4 Repeated error test failures (internal error).
  439. -5 Repeated convergence failures (perhaps bad Jacobian or tolerances).
  440. -6 Error weight became zero during problem.
  441. -7 Internal workspace insufficient to finish (internal error).
  442. =========== =======
  443. """
  444. try:
  445. self._integrator
  446. except AttributeError:
  447. self.set_integrator('')
  448. return self._integrator.istate
  449. def set_f_params(self, *args):
  450. """Set extra parameters for user-supplied function f."""
  451. self.f_params = args
  452. return self
  453. def set_jac_params(self, *args):
  454. """Set extra parameters for user-supplied function jac."""
  455. self.jac_params = args
  456. return self
  457. def set_solout(self, solout):
  458. """
  459. Set callable to be called at every successful integration step.
  460. Parameters
  461. ----------
  462. solout : callable
  463. ``solout(t, y)`` is called at each internal integrator step,
  464. t is a scalar providing the current independent position
  465. y is the current soloution ``y.shape == (n,)``
  466. solout should return -1 to stop integration
  467. otherwise it should return None or 0
  468. """
  469. if self._integrator.supports_solout:
  470. self._integrator.set_solout(solout)
  471. if self._y is not None:
  472. self._integrator.reset(len(self._y), self.jac is not None)
  473. else:
  474. raise ValueError("selected integrator does not support solout,"
  475. " choose another one")
  476. def _transform_banded_jac(bjac):
  477. """
  478. Convert a real matrix of the form (for example)
  479. [0 0 A B] [0 0 0 B]
  480. [0 0 C D] [0 0 A D]
  481. [E F G H] to [0 F C H]
  482. [I J K L] [E J G L]
  483. [I 0 K 0]
  484. That is, every other column is shifted up one.
  485. """
  486. # Shift every other column.
  487. newjac = zeros((bjac.shape[0] + 1, bjac.shape[1]))
  488. newjac[1:, ::2] = bjac[:, ::2]
  489. newjac[:-1, 1::2] = bjac[:, 1::2]
  490. return newjac
  491. class complex_ode(ode):
  492. """
  493. A wrapper of ode for complex systems.
  494. This functions similarly as `ode`, but re-maps a complex-valued
  495. equation system to a real-valued one before using the integrators.
  496. Parameters
  497. ----------
  498. f : callable ``f(t, y, *f_args)``
  499. Rhs of the equation. t is a scalar, ``y.shape == (n,)``.
  500. ``f_args`` is set by calling ``set_f_params(*args)``.
  501. jac : callable ``jac(t, y, *jac_args)``
  502. Jacobian of the rhs, ``jac[i,j] = d f[i] / d y[j]``.
  503. ``jac_args`` is set by calling ``set_f_params(*args)``.
  504. Attributes
  505. ----------
  506. t : float
  507. Current time.
  508. y : ndarray
  509. Current variable values.
  510. Examples
  511. --------
  512. For usage examples, see `ode`.
  513. """
  514. def __init__(self, f, jac=None):
  515. self.cf = f
  516. self.cjac = jac
  517. if jac is None:
  518. ode.__init__(self, self._wrap, None)
  519. else:
  520. ode.__init__(self, self._wrap, self._wrap_jac)
  521. def _wrap(self, t, y, *f_args):
  522. f = self.cf(*((t, y[::2] + 1j * y[1::2]) + f_args))
  523. # self.tmp is a real-valued array containing the interleaved
  524. # real and imaginary parts of f.
  525. self.tmp[::2] = real(f)
  526. self.tmp[1::2] = imag(f)
  527. return self.tmp
  528. def _wrap_jac(self, t, y, *jac_args):
  529. # jac is the complex Jacobian computed by the user-defined function.
  530. jac = self.cjac(*((t, y[::2] + 1j * y[1::2]) + jac_args))
  531. # jac_tmp is the real version of the complex Jacobian. Each complex
  532. # entry in jac, say 2+3j, becomes a 2x2 block of the form
  533. # [2 -3]
  534. # [3 2]
  535. jac_tmp = zeros((2 * jac.shape[0], 2 * jac.shape[1]))
  536. jac_tmp[1::2, 1::2] = jac_tmp[::2, ::2] = real(jac)
  537. jac_tmp[1::2, ::2] = imag(jac)
  538. jac_tmp[::2, 1::2] = -jac_tmp[1::2, ::2]
  539. ml = getattr(self._integrator, 'ml', None)
  540. mu = getattr(self._integrator, 'mu', None)
  541. if ml is not None or mu is not None:
  542. # Jacobian is banded. The user's Jacobian function has computed
  543. # the complex Jacobian in packed format. The corresponding
  544. # real-valued version has every other column shifted up.
  545. jac_tmp = _transform_banded_jac(jac_tmp)
  546. return jac_tmp
  547. @property
  548. def y(self):
  549. return self._y[::2] + 1j * self._y[1::2]
  550. def set_integrator(self, name, **integrator_params):
  551. """
  552. Set integrator by name.
  553. Parameters
  554. ----------
  555. name : str
  556. Name of the integrator
  557. integrator_params
  558. Additional parameters for the integrator.
  559. """
  560. if name == 'zvode':
  561. raise ValueError("zvode must be used with ode, not complex_ode")
  562. lband = integrator_params.get('lband')
  563. uband = integrator_params.get('uband')
  564. if lband is not None or uband is not None:
  565. # The Jacobian is banded. Override the user-supplied bandwidths
  566. # (which are for the complex Jacobian) with the bandwidths of
  567. # the corresponding real-valued Jacobian wrapper of the complex
  568. # Jacobian.
  569. integrator_params['lband'] = 2 * (lband or 0) + 1
  570. integrator_params['uband'] = 2 * (uband or 0) + 1
  571. return ode.set_integrator(self, name, **integrator_params)
  572. def set_initial_value(self, y, t=0.0):
  573. """Set initial conditions y(t) = y."""
  574. y = asarray(y)
  575. self.tmp = zeros(y.size * 2, 'float')
  576. self.tmp[::2] = real(y)
  577. self.tmp[1::2] = imag(y)
  578. return ode.set_initial_value(self, self.tmp, t)
  579. def integrate(self, t, step=False, relax=False):
  580. """Find y=y(t), set y as an initial condition, and return y.
  581. Parameters
  582. ----------
  583. t : float
  584. The endpoint of the integration step.
  585. step : bool
  586. If True, and if the integrator supports the step method,
  587. then perform a single integration step and return.
  588. This parameter is provided in order to expose internals of
  589. the implementation, and should not be changed from its default
  590. value in most cases.
  591. relax : bool
  592. If True and if the integrator supports the run_relax method,
  593. then integrate until t_1 >= t and return. ``relax`` is not
  594. referenced if ``step=True``.
  595. This parameter is provided in order to expose internals of
  596. the implementation, and should not be changed from its default
  597. value in most cases.
  598. Returns
  599. -------
  600. y : float
  601. The integrated value at t
  602. """
  603. y = ode.integrate(self, t, step, relax)
  604. return y[::2] + 1j * y[1::2]
  605. def set_solout(self, solout):
  606. """
  607. Set callable to be called at every successful integration step.
  608. Parameters
  609. ----------
  610. solout : callable
  611. ``solout(t, y)`` is called at each internal integrator step,
  612. t is a scalar providing the current independent position
  613. y is the current soloution ``y.shape == (n,)``
  614. solout should return -1 to stop integration
  615. otherwise it should return None or 0
  616. """
  617. if self._integrator.supports_solout:
  618. self._integrator.set_solout(solout, complex=True)
  619. else:
  620. raise TypeError("selected integrator does not support solouta,"
  621. + "choose another one")
  622. # ------------------------------------------------------------------------------
  623. # ODE integrators
  624. # ------------------------------------------------------------------------------
  625. def find_integrator(name):
  626. for cl in IntegratorBase.integrator_classes:
  627. if re.match(name, cl.__name__, re.I):
  628. return cl
  629. return None
  630. class IntegratorConcurrencyError(RuntimeError):
  631. """
  632. Failure due to concurrent usage of an integrator that can be used
  633. only for a single problem at a time.
  634. """
  635. def __init__(self, name):
  636. msg = ("Integrator `%s` can be used to solve only a single problem "
  637. "at a time. If you want to integrate multiple problems, "
  638. "consider using a different integrator "
  639. "(see `ode.set_integrator`)") % name
  640. RuntimeError.__init__(self, msg)
  641. class IntegratorBase(object):
  642. runner = None # runner is None => integrator is not available
  643. success = None # success==1 if integrator was called successfully
  644. istate = None # istate > 0 means success, istate < 0 means failure
  645. supports_run_relax = None
  646. supports_step = None
  647. supports_solout = False
  648. integrator_classes = []
  649. scalar = float
  650. def acquire_new_handle(self):
  651. # Some of the integrators have internal state (ancient
  652. # Fortran...), and so only one instance can use them at a time.
  653. # We keep track of this, and fail when concurrent usage is tried.
  654. self.__class__.active_global_handle += 1
  655. self.handle = self.__class__.active_global_handle
  656. def check_handle(self):
  657. if self.handle is not self.__class__.active_global_handle:
  658. raise IntegratorConcurrencyError(self.__class__.__name__)
  659. def reset(self, n, has_jac):
  660. """Prepare integrator for call: allocate memory, set flags, etc.
  661. n - number of equations.
  662. has_jac - if user has supplied function for evaluating Jacobian.
  663. """
  664. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  665. """Integrate from t=t0 to t=t1 using y0 as an initial condition.
  666. Return 2-tuple (y1,t1) where y1 is the result and t=t1
  667. defines the stoppage coordinate of the result.
  668. """
  669. raise NotImplementedError('all integrators must define '
  670. 'run(f, jac, t0, t1, y0, f_params, jac_params)')
  671. def step(self, f, jac, y0, t0, t1, f_params, jac_params):
  672. """Make one integration step and return (y1,t1)."""
  673. raise NotImplementedError('%s does not support step() method' %
  674. self.__class__.__name__)
  675. def run_relax(self, f, jac, y0, t0, t1, f_params, jac_params):
  676. """Integrate from t=t0 to t>=t1 and return (y1,t)."""
  677. raise NotImplementedError('%s does not support run_relax() method' %
  678. self.__class__.__name__)
  679. # XXX: __str__ method for getting visual state of the integrator
  680. def _vode_banded_jac_wrapper(jacfunc, ml, jac_params):
  681. """
  682. Wrap a banded Jacobian function with a function that pads
  683. the Jacobian with `ml` rows of zeros.
  684. """
  685. def jac_wrapper(t, y):
  686. jac = asarray(jacfunc(t, y, *jac_params))
  687. padded_jac = vstack((jac, zeros((ml, jac.shape[1]))))
  688. return padded_jac
  689. return jac_wrapper
  690. class vode(IntegratorBase):
  691. runner = getattr(_vode, 'dvode', None)
  692. messages = {-1: 'Excess work done on this call. (Perhaps wrong MF.)',
  693. -2: 'Excess accuracy requested. (Tolerances too small.)',
  694. -3: 'Illegal input detected. (See printed message.)',
  695. -4: 'Repeated error test failures. (Check all input.)',
  696. -5: 'Repeated convergence failures. (Perhaps bad'
  697. ' Jacobian supplied or wrong choice of MF or tolerances.)',
  698. -6: 'Error weight became zero during problem. (Solution'
  699. ' component i vanished, and ATOL or ATOL(i) = 0.)'
  700. }
  701. supports_run_relax = 1
  702. supports_step = 1
  703. active_global_handle = 0
  704. def __init__(self,
  705. method='adams',
  706. with_jacobian=False,
  707. rtol=1e-6, atol=1e-12,
  708. lband=None, uband=None,
  709. order=12,
  710. nsteps=500,
  711. max_step=0.0, # corresponds to infinite
  712. min_step=0.0,
  713. first_step=0.0, # determined by solver
  714. ):
  715. if re.match(method, r'adams', re.I):
  716. self.meth = 1
  717. elif re.match(method, r'bdf', re.I):
  718. self.meth = 2
  719. else:
  720. raise ValueError('Unknown integration method %s' % method)
  721. self.with_jacobian = with_jacobian
  722. self.rtol = rtol
  723. self.atol = atol
  724. self.mu = uband
  725. self.ml = lband
  726. self.order = order
  727. self.nsteps = nsteps
  728. self.max_step = max_step
  729. self.min_step = min_step
  730. self.first_step = first_step
  731. self.success = 1
  732. self.initialized = False
  733. def _determine_mf_and_set_bands(self, has_jac):
  734. """
  735. Determine the `MF` parameter (Method Flag) for the Fortran subroutine `dvode`.
  736. In the Fortran code, the legal values of `MF` are:
  737. 10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25,
  738. -11, -12, -14, -15, -21, -22, -24, -25
  739. but this python wrapper does not use negative values.
  740. Returns
  741. mf = 10*self.meth + miter
  742. self.meth is the linear multistep method:
  743. self.meth == 1: method="adams"
  744. self.meth == 2: method="bdf"
  745. miter is the correction iteration method:
  746. miter == 0: Functional iteraton; no Jacobian involved.
  747. miter == 1: Chord iteration with user-supplied full Jacobian
  748. miter == 2: Chord iteration with internally computed full Jacobian
  749. miter == 3: Chord iteration with internally computed diagonal Jacobian
  750. miter == 4: Chord iteration with user-supplied banded Jacobian
  751. miter == 5: Chord iteration with internally computed banded Jacobian
  752. Side effects: If either self.mu or self.ml is not None and the other is None,
  753. then the one that is None is set to 0.
  754. """
  755. jac_is_banded = self.mu is not None or self.ml is not None
  756. if jac_is_banded:
  757. if self.mu is None:
  758. self.mu = 0
  759. if self.ml is None:
  760. self.ml = 0
  761. # has_jac is True if the user provided a jacobian function.
  762. if has_jac:
  763. if jac_is_banded:
  764. miter = 4
  765. else:
  766. miter = 1
  767. else:
  768. if jac_is_banded:
  769. if self.ml == self.mu == 0:
  770. miter = 3 # Chord iteration with internal diagonal Jacobian.
  771. else:
  772. miter = 5 # Chord iteration with internal banded Jacobian.
  773. else:
  774. # self.with_jacobian is set by the user in the call to ode.set_integrator.
  775. if self.with_jacobian:
  776. miter = 2 # Chord iteration with internal full Jacobian.
  777. else:
  778. miter = 0 # Functional iteraton; no Jacobian involved.
  779. mf = 10 * self.meth + miter
  780. return mf
  781. def reset(self, n, has_jac):
  782. mf = self._determine_mf_and_set_bands(has_jac)
  783. if mf == 10:
  784. lrw = 20 + 16 * n
  785. elif mf in [11, 12]:
  786. lrw = 22 + 16 * n + 2 * n * n
  787. elif mf == 13:
  788. lrw = 22 + 17 * n
  789. elif mf in [14, 15]:
  790. lrw = 22 + 18 * n + (3 * self.ml + 2 * self.mu) * n
  791. elif mf == 20:
  792. lrw = 20 + 9 * n
  793. elif mf in [21, 22]:
  794. lrw = 22 + 9 * n + 2 * n * n
  795. elif mf == 23:
  796. lrw = 22 + 10 * n
  797. elif mf in [24, 25]:
  798. lrw = 22 + 11 * n + (3 * self.ml + 2 * self.mu) * n
  799. else:
  800. raise ValueError('Unexpected mf=%s' % mf)
  801. if mf % 10 in [0, 3]:
  802. liw = 30
  803. else:
  804. liw = 30 + n
  805. rwork = zeros((lrw,), float)
  806. rwork[4] = self.first_step
  807. rwork[5] = self.max_step
  808. rwork[6] = self.min_step
  809. self.rwork = rwork
  810. iwork = zeros((liw,), int32)
  811. if self.ml is not None:
  812. iwork[0] = self.ml
  813. if self.mu is not None:
  814. iwork[1] = self.mu
  815. iwork[4] = self.order
  816. iwork[5] = self.nsteps
  817. iwork[6] = 2 # mxhnil
  818. self.iwork = iwork
  819. self.call_args = [self.rtol, self.atol, 1, 1,
  820. self.rwork, self.iwork, mf]
  821. self.success = 1
  822. self.initialized = False
  823. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  824. if self.initialized:
  825. self.check_handle()
  826. else:
  827. self.initialized = True
  828. self.acquire_new_handle()
  829. if self.ml is not None and self.ml > 0:
  830. # Banded Jacobian. Wrap the user-provided function with one
  831. # that pads the Jacobian array with the extra `self.ml` rows
  832. # required by the f2py-generated wrapper.
  833. jac = _vode_banded_jac_wrapper(jac, self.ml, jac_params)
  834. args = ((f, jac, y0, t0, t1) + tuple(self.call_args) +
  835. (f_params, jac_params))
  836. y1, t, istate = self.runner(*args)
  837. self.istate = istate
  838. if istate < 0:
  839. unexpected_istate_msg = 'Unexpected istate={:d}'.format(istate)
  840. warnings.warn('{:s}: {:s}'.format(self.__class__.__name__,
  841. self.messages.get(istate, unexpected_istate_msg)))
  842. self.success = 0
  843. else:
  844. self.call_args[3] = 2 # upgrade istate from 1 to 2
  845. self.istate = 2
  846. return y1, t
  847. def step(self, *args):
  848. itask = self.call_args[2]
  849. self.call_args[2] = 2
  850. r = self.run(*args)
  851. self.call_args[2] = itask
  852. return r
  853. def run_relax(self, *args):
  854. itask = self.call_args[2]
  855. self.call_args[2] = 3
  856. r = self.run(*args)
  857. self.call_args[2] = itask
  858. return r
  859. if vode.runner is not None:
  860. IntegratorBase.integrator_classes.append(vode)
  861. class zvode(vode):
  862. runner = getattr(_vode, 'zvode', None)
  863. supports_run_relax = 1
  864. supports_step = 1
  865. scalar = complex
  866. active_global_handle = 0
  867. def reset(self, n, has_jac):
  868. mf = self._determine_mf_and_set_bands(has_jac)
  869. if mf in (10,):
  870. lzw = 15 * n
  871. elif mf in (11, 12):
  872. lzw = 15 * n + 2 * n ** 2
  873. elif mf in (-11, -12):
  874. lzw = 15 * n + n ** 2
  875. elif mf in (13,):
  876. lzw = 16 * n
  877. elif mf in (14, 15):
  878. lzw = 17 * n + (3 * self.ml + 2 * self.mu) * n
  879. elif mf in (-14, -15):
  880. lzw = 16 * n + (2 * self.ml + self.mu) * n
  881. elif mf in (20,):
  882. lzw = 8 * n
  883. elif mf in (21, 22):
  884. lzw = 8 * n + 2 * n ** 2
  885. elif mf in (-21, -22):
  886. lzw = 8 * n + n ** 2
  887. elif mf in (23,):
  888. lzw = 9 * n
  889. elif mf in (24, 25):
  890. lzw = 10 * n + (3 * self.ml + 2 * self.mu) * n
  891. elif mf in (-24, -25):
  892. lzw = 9 * n + (2 * self.ml + self.mu) * n
  893. lrw = 20 + n
  894. if mf % 10 in (0, 3):
  895. liw = 30
  896. else:
  897. liw = 30 + n
  898. zwork = zeros((lzw,), complex)
  899. self.zwork = zwork
  900. rwork = zeros((lrw,), float)
  901. rwork[4] = self.first_step
  902. rwork[5] = self.max_step
  903. rwork[6] = self.min_step
  904. self.rwork = rwork
  905. iwork = zeros((liw,), int32)
  906. if self.ml is not None:
  907. iwork[0] = self.ml
  908. if self.mu is not None:
  909. iwork[1] = self.mu
  910. iwork[4] = self.order
  911. iwork[5] = self.nsteps
  912. iwork[6] = 2 # mxhnil
  913. self.iwork = iwork
  914. self.call_args = [self.rtol, self.atol, 1, 1,
  915. self.zwork, self.rwork, self.iwork, mf]
  916. self.success = 1
  917. self.initialized = False
  918. if zvode.runner is not None:
  919. IntegratorBase.integrator_classes.append(zvode)
  920. class dopri5(IntegratorBase):
  921. runner = getattr(_dop, 'dopri5', None)
  922. name = 'dopri5'
  923. supports_solout = True
  924. messages = {1: 'computation successful',
  925. 2: 'comput. successful (interrupted by solout)',
  926. -1: 'input is not consistent',
  927. -2: 'larger nsteps is needed',
  928. -3: 'step size becomes too small',
  929. -4: 'problem is probably stiff (interrupted)',
  930. }
  931. def __init__(self,
  932. rtol=1e-6, atol=1e-12,
  933. nsteps=500,
  934. max_step=0.0,
  935. first_step=0.0, # determined by solver
  936. safety=0.9,
  937. ifactor=10.0,
  938. dfactor=0.2,
  939. beta=0.0,
  940. method=None,
  941. verbosity=-1, # no messages if negative
  942. ):
  943. self.rtol = rtol
  944. self.atol = atol
  945. self.nsteps = nsteps
  946. self.max_step = max_step
  947. self.first_step = first_step
  948. self.safety = safety
  949. self.ifactor = ifactor
  950. self.dfactor = dfactor
  951. self.beta = beta
  952. self.verbosity = verbosity
  953. self.success = 1
  954. self.set_solout(None)
  955. def set_solout(self, solout, complex=False):
  956. self.solout = solout
  957. self.solout_cmplx = complex
  958. if solout is None:
  959. self.iout = 0
  960. else:
  961. self.iout = 1
  962. def reset(self, n, has_jac):
  963. work = zeros((8 * n + 21,), float)
  964. work[1] = self.safety
  965. work[2] = self.dfactor
  966. work[3] = self.ifactor
  967. work[4] = self.beta
  968. work[5] = self.max_step
  969. work[6] = self.first_step
  970. self.work = work
  971. iwork = zeros((21,), int32)
  972. iwork[0] = self.nsteps
  973. iwork[2] = self.verbosity
  974. self.iwork = iwork
  975. self.call_args = [self.rtol, self.atol, self._solout,
  976. self.iout, self.work, self.iwork]
  977. self.success = 1
  978. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  979. x, y, iwork, istate = self.runner(*((f, t0, y0, t1) +
  980. tuple(self.call_args) + (f_params,)))
  981. self.istate = istate
  982. if istate < 0:
  983. unexpected_istate_msg = 'Unexpected istate={:d}'.format(istate)
  984. warnings.warn('{:s}: {:s}'.format(self.__class__.__name__,
  985. self.messages.get(istate, unexpected_istate_msg)))
  986. self.success = 0
  987. return y, x
  988. def _solout(self, nr, xold, x, y, nd, icomp, con):
  989. if self.solout is not None:
  990. if self.solout_cmplx:
  991. y = y[::2] + 1j * y[1::2]
  992. return self.solout(x, y)
  993. else:
  994. return 1
  995. if dopri5.runner is not None:
  996. IntegratorBase.integrator_classes.append(dopri5)
  997. class dop853(dopri5):
  998. runner = getattr(_dop, 'dop853', None)
  999. name = 'dop853'
  1000. def __init__(self,
  1001. rtol=1e-6, atol=1e-12,
  1002. nsteps=500,
  1003. max_step=0.0,
  1004. first_step=0.0, # determined by solver
  1005. safety=0.9,
  1006. ifactor=6.0,
  1007. dfactor=0.3,
  1008. beta=0.0,
  1009. method=None,
  1010. verbosity=-1, # no messages if negative
  1011. ):
  1012. super(self.__class__, self).__init__(rtol, atol, nsteps, max_step,
  1013. first_step, safety, ifactor,
  1014. dfactor, beta, method,
  1015. verbosity)
  1016. def reset(self, n, has_jac):
  1017. work = zeros((11 * n + 21,), float)
  1018. work[1] = self.safety
  1019. work[2] = self.dfactor
  1020. work[3] = self.ifactor
  1021. work[4] = self.beta
  1022. work[5] = self.max_step
  1023. work[6] = self.first_step
  1024. self.work = work
  1025. iwork = zeros((21,), int32)
  1026. iwork[0] = self.nsteps
  1027. iwork[2] = self.verbosity
  1028. self.iwork = iwork
  1029. self.call_args = [self.rtol, self.atol, self._solout,
  1030. self.iout, self.work, self.iwork]
  1031. self.success = 1
  1032. if dop853.runner is not None:
  1033. IntegratorBase.integrator_classes.append(dop853)
  1034. class lsoda(IntegratorBase):
  1035. runner = getattr(_lsoda, 'lsoda', None)
  1036. active_global_handle = 0
  1037. messages = {
  1038. 2: "Integration successful.",
  1039. -1: "Excess work done on this call (perhaps wrong Dfun type).",
  1040. -2: "Excess accuracy requested (tolerances too small).",
  1041. -3: "Illegal input detected (internal error).",
  1042. -4: "Repeated error test failures (internal error).",
  1043. -5: "Repeated convergence failures (perhaps bad Jacobian or tolerances).",
  1044. -6: "Error weight became zero during problem.",
  1045. -7: "Internal workspace insufficient to finish (internal error)."
  1046. }
  1047. def __init__(self,
  1048. with_jacobian=False,
  1049. rtol=1e-6, atol=1e-12,
  1050. lband=None, uband=None,
  1051. nsteps=500,
  1052. max_step=0.0, # corresponds to infinite
  1053. min_step=0.0,
  1054. first_step=0.0, # determined by solver
  1055. ixpr=0,
  1056. max_hnil=0,
  1057. max_order_ns=12,
  1058. max_order_s=5,
  1059. method=None
  1060. ):
  1061. self.with_jacobian = with_jacobian
  1062. self.rtol = rtol
  1063. self.atol = atol
  1064. self.mu = uband
  1065. self.ml = lband
  1066. self.max_order_ns = max_order_ns
  1067. self.max_order_s = max_order_s
  1068. self.nsteps = nsteps
  1069. self.max_step = max_step
  1070. self.min_step = min_step
  1071. self.first_step = first_step
  1072. self.ixpr = ixpr
  1073. self.max_hnil = max_hnil
  1074. self.success = 1
  1075. self.initialized = False
  1076. def reset(self, n, has_jac):
  1077. # Calculate parameters for Fortran subroutine dvode.
  1078. if has_jac:
  1079. if self.mu is None and self.ml is None:
  1080. jt = 1
  1081. else:
  1082. if self.mu is None:
  1083. self.mu = 0
  1084. if self.ml is None:
  1085. self.ml = 0
  1086. jt = 4
  1087. else:
  1088. if self.mu is None and self.ml is None:
  1089. jt = 2
  1090. else:
  1091. if self.mu is None:
  1092. self.mu = 0
  1093. if self.ml is None:
  1094. self.ml = 0
  1095. jt = 5
  1096. lrn = 20 + (self.max_order_ns + 4) * n
  1097. if jt in [1, 2]:
  1098. lrs = 22 + (self.max_order_s + 4) * n + n * n
  1099. elif jt in [4, 5]:
  1100. lrs = 22 + (self.max_order_s + 5 + 2 * self.ml + self.mu) * n
  1101. else:
  1102. raise ValueError('Unexpected jt=%s' % jt)
  1103. lrw = max(lrn, lrs)
  1104. liw = 20 + n
  1105. rwork = zeros((lrw,), float)
  1106. rwork[4] = self.first_step
  1107. rwork[5] = self.max_step
  1108. rwork[6] = self.min_step
  1109. self.rwork = rwork
  1110. iwork = zeros((liw,), int32)
  1111. if self.ml is not None:
  1112. iwork[0] = self.ml
  1113. if self.mu is not None:
  1114. iwork[1] = self.mu
  1115. iwork[4] = self.ixpr
  1116. iwork[5] = self.nsteps
  1117. iwork[6] = self.max_hnil
  1118. iwork[7] = self.max_order_ns
  1119. iwork[8] = self.max_order_s
  1120. self.iwork = iwork
  1121. self.call_args = [self.rtol, self.atol, 1, 1,
  1122. self.rwork, self.iwork, jt]
  1123. self.success = 1
  1124. self.initialized = False
  1125. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  1126. if self.initialized:
  1127. self.check_handle()
  1128. else:
  1129. self.initialized = True
  1130. self.acquire_new_handle()
  1131. args = [f, y0, t0, t1] + self.call_args[:-1] + \
  1132. [jac, self.call_args[-1], f_params, 0, jac_params]
  1133. y1, t, istate = self.runner(*args)
  1134. self.istate = istate
  1135. if istate < 0:
  1136. unexpected_istate_msg = 'Unexpected istate={:d}'.format(istate)
  1137. warnings.warn('{:s}: {:s}'.format(self.__class__.__name__,
  1138. self.messages.get(istate, unexpected_istate_msg)))
  1139. self.success = 0
  1140. else:
  1141. self.call_args[3] = 2 # upgrade istate from 1 to 2
  1142. self.istate = 2
  1143. return y1, t
  1144. def step(self, *args):
  1145. itask = self.call_args[2]
  1146. self.call_args[2] = 2
  1147. r = self.run(*args)
  1148. self.call_args[2] = itask
  1149. return r
  1150. def run_relax(self, *args):
  1151. itask = self.call_args[2]
  1152. self.call_args[2] = 3
  1153. r = self.run(*args)
  1154. self.call_args[2] = itask
  1155. return r
  1156. if lsoda.runner:
  1157. IntegratorBase.integrator_classes.append(lsoda)