_ode.py 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372
  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. # XXX: Integrators must have:
  31. # ===========================
  32. # cvode - C version of vode and vodpk with many improvements.
  33. # Get it from http://www.netlib.org/ode/cvode.tar.gz.
  34. # To wrap cvode to Python, one must write the extension module by
  35. # hand. Its interface is too much 'advanced C' that using f2py
  36. # would be too complicated (or impossible).
  37. #
  38. # How to define a new integrator:
  39. # ===============================
  40. #
  41. # class myodeint(IntegratorBase):
  42. #
  43. # runner = <odeint function> or None
  44. #
  45. # def __init__(self,...): # required
  46. # <initialize>
  47. #
  48. # def reset(self,n,has_jac): # optional
  49. # # n - the size of the problem (number of equations)
  50. # # has_jac - whether user has supplied its own routine for Jacobian
  51. # <allocate memory,initialize further>
  52. #
  53. # def run(self,f,jac,y0,t0,t1,f_params,jac_params): # required
  54. # # this method is called to integrate from t=t0 to t=t1
  55. # # with initial condition y0. f and jac are user-supplied functions
  56. # # that define the problem. f_params,jac_params are additional
  57. # # arguments
  58. # # to these functions.
  59. # <calculate y1>
  60. # if <calculation was unsuccessful>:
  61. # self.success = 0
  62. # return t1,y1
  63. #
  64. # # In addition, one can define step() and run_relax() methods (they
  65. # # take the same arguments as run()) if the integrator can support
  66. # # these features (see IntegratorBase doc strings).
  67. #
  68. # if myodeint.runner:
  69. # IntegratorBase.integrator_classes.append(myodeint)
  70. __all__ = ['ode', 'complex_ode']
  71. import re
  72. import warnings
  73. from numpy import asarray, array, zeros, isscalar, real, imag, vstack
  74. from . import _vode
  75. from . import _dop
  76. from . import _lsoda
  77. _dop_int_dtype = _dop.types.intvar.dtype
  78. _vode_int_dtype = _vode.types.intvar.dtype
  79. _lsoda_int_dtype = _lsoda.types.intvar.dtype
  80. # ------------------------------------------------------------------------------
  81. # User interface
  82. # ------------------------------------------------------------------------------
  83. class ode:
  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 as e:
  361. # f2py issue with tuple returns, see ticket 1187.
  362. raise ValueError(
  363. 'Function to integrate must not return a tuple.'
  364. ) from e
  365. return self._y
  366. def successful(self):
  367. """Check if integration was successful."""
  368. try:
  369. self._integrator
  370. except AttributeError:
  371. self.set_integrator('')
  372. return self._integrator.success == 1
  373. def get_return_code(self):
  374. """Extracts the return code for the integration to enable better control
  375. if the integration fails.
  376. In general, a return code > 0 implies success, while a return code < 0
  377. implies failure.
  378. Notes
  379. -----
  380. This section describes possible return codes and their meaning, for available
  381. integrators that can be selected by `set_integrator` method.
  382. "vode"
  383. =========== =======
  384. Return Code Message
  385. =========== =======
  386. 2 Integration successful.
  387. -1 Excess work done on this call. (Perhaps wrong MF.)
  388. -2 Excess accuracy requested. (Tolerances too small.)
  389. -3 Illegal input detected. (See printed message.)
  390. -4 Repeated error test failures. (Check all input.)
  391. -5 Repeated convergence failures. (Perhaps bad Jacobian
  392. supplied or wrong choice of MF or tolerances.)
  393. -6 Error weight became zero during problem. (Solution
  394. component i vanished, and ATOL or ATOL(i) = 0.)
  395. =========== =======
  396. "zvode"
  397. =========== =======
  398. Return Code Message
  399. =========== =======
  400. 2 Integration successful.
  401. -1 Excess work done on this call. (Perhaps wrong MF.)
  402. -2 Excess accuracy requested. (Tolerances too small.)
  403. -3 Illegal input detected. (See printed message.)
  404. -4 Repeated error test failures. (Check all input.)
  405. -5 Repeated convergence failures. (Perhaps bad Jacobian
  406. supplied or wrong choice of MF or tolerances.)
  407. -6 Error weight became zero during problem. (Solution
  408. component i vanished, and ATOL or ATOL(i) = 0.)
  409. =========== =======
  410. "dopri5"
  411. =========== =======
  412. Return Code Message
  413. =========== =======
  414. 1 Integration successful.
  415. 2 Integration successful (interrupted by solout).
  416. -1 Input is not consistent.
  417. -2 Larger nsteps is needed.
  418. -3 Step size becomes too small.
  419. -4 Problem is probably stiff (interrupted).
  420. =========== =======
  421. "dop853"
  422. =========== =======
  423. Return Code Message
  424. =========== =======
  425. 1 Integration successful.
  426. 2 Integration successful (interrupted by solout).
  427. -1 Input is not consistent.
  428. -2 Larger nsteps is needed.
  429. -3 Step size becomes too small.
  430. -4 Problem is probably stiff (interrupted).
  431. =========== =======
  432. "lsoda"
  433. =========== =======
  434. Return Code Message
  435. =========== =======
  436. 2 Integration successful.
  437. -1 Excess work done on this call (perhaps wrong Dfun type).
  438. -2 Excess accuracy requested (tolerances too small).
  439. -3 Illegal input detected (internal error).
  440. -4 Repeated error test failures (internal error).
  441. -5 Repeated convergence failures (perhaps bad Jacobian or tolerances).
  442. -6 Error weight became zero during problem.
  443. -7 Internal workspace insufficient to finish (internal error).
  444. =========== =======
  445. """
  446. try:
  447. self._integrator
  448. except AttributeError:
  449. self.set_integrator('')
  450. return self._integrator.istate
  451. def set_f_params(self, *args):
  452. """Set extra parameters for user-supplied function f."""
  453. self.f_params = args
  454. return self
  455. def set_jac_params(self, *args):
  456. """Set extra parameters for user-supplied function jac."""
  457. self.jac_params = args
  458. return self
  459. def set_solout(self, solout):
  460. """
  461. Set callable to be called at every successful integration step.
  462. Parameters
  463. ----------
  464. solout : callable
  465. ``solout(t, y)`` is called at each internal integrator step,
  466. t is a scalar providing the current independent position
  467. y is the current soloution ``y.shape == (n,)``
  468. solout should return -1 to stop integration
  469. otherwise it should return None or 0
  470. """
  471. if self._integrator.supports_solout:
  472. self._integrator.set_solout(solout)
  473. if self._y is not None:
  474. self._integrator.reset(len(self._y), self.jac is not None)
  475. else:
  476. raise ValueError("selected integrator does not support solout,"
  477. " choose another one")
  478. def _transform_banded_jac(bjac):
  479. """
  480. Convert a real matrix of the form (for example)
  481. [0 0 A B] [0 0 0 B]
  482. [0 0 C D] [0 0 A D]
  483. [E F G H] to [0 F C H]
  484. [I J K L] [E J G L]
  485. [I 0 K 0]
  486. That is, every other column is shifted up one.
  487. """
  488. # Shift every other column.
  489. newjac = zeros((bjac.shape[0] + 1, bjac.shape[1]))
  490. newjac[1:, ::2] = bjac[:, ::2]
  491. newjac[:-1, 1::2] = bjac[:, 1::2]
  492. return newjac
  493. class complex_ode(ode):
  494. """
  495. A wrapper of ode for complex systems.
  496. This functions similarly as `ode`, but re-maps a complex-valued
  497. equation system to a real-valued one before using the integrators.
  498. Parameters
  499. ----------
  500. f : callable ``f(t, y, *f_args)``
  501. Rhs of the equation. t is a scalar, ``y.shape == (n,)``.
  502. ``f_args`` is set by calling ``set_f_params(*args)``.
  503. jac : callable ``jac(t, y, *jac_args)``
  504. Jacobian of the rhs, ``jac[i,j] = d f[i] / d y[j]``.
  505. ``jac_args`` is set by calling ``set_f_params(*args)``.
  506. Attributes
  507. ----------
  508. t : float
  509. Current time.
  510. y : ndarray
  511. Current variable values.
  512. Examples
  513. --------
  514. For usage examples, see `ode`.
  515. """
  516. def __init__(self, f, jac=None):
  517. self.cf = f
  518. self.cjac = jac
  519. if jac is None:
  520. ode.__init__(self, self._wrap, None)
  521. else:
  522. ode.__init__(self, self._wrap, self._wrap_jac)
  523. def _wrap(self, t, y, *f_args):
  524. f = self.cf(*((t, y[::2] + 1j * y[1::2]) + f_args))
  525. # self.tmp is a real-valued array containing the interleaved
  526. # real and imaginary parts of f.
  527. self.tmp[::2] = real(f)
  528. self.tmp[1::2] = imag(f)
  529. return self.tmp
  530. def _wrap_jac(self, t, y, *jac_args):
  531. # jac is the complex Jacobian computed by the user-defined function.
  532. jac = self.cjac(*((t, y[::2] + 1j * y[1::2]) + jac_args))
  533. # jac_tmp is the real version of the complex Jacobian. Each complex
  534. # entry in jac, say 2+3j, becomes a 2x2 block of the form
  535. # [2 -3]
  536. # [3 2]
  537. jac_tmp = zeros((2 * jac.shape[0], 2 * jac.shape[1]))
  538. jac_tmp[1::2, 1::2] = jac_tmp[::2, ::2] = real(jac)
  539. jac_tmp[1::2, ::2] = imag(jac)
  540. jac_tmp[::2, 1::2] = -jac_tmp[1::2, ::2]
  541. ml = getattr(self._integrator, 'ml', None)
  542. mu = getattr(self._integrator, 'mu', None)
  543. if ml is not None or mu is not None:
  544. # Jacobian is banded. The user's Jacobian function has computed
  545. # the complex Jacobian in packed format. The corresponding
  546. # real-valued version has every other column shifted up.
  547. jac_tmp = _transform_banded_jac(jac_tmp)
  548. return jac_tmp
  549. @property
  550. def y(self):
  551. return self._y[::2] + 1j * self._y[1::2]
  552. def set_integrator(self, name, **integrator_params):
  553. """
  554. Set integrator by name.
  555. Parameters
  556. ----------
  557. name : str
  558. Name of the integrator
  559. **integrator_params
  560. Additional parameters for the integrator.
  561. """
  562. if name == 'zvode':
  563. raise ValueError("zvode must be used with ode, not complex_ode")
  564. lband = integrator_params.get('lband')
  565. uband = integrator_params.get('uband')
  566. if lband is not None or uband is not None:
  567. # The Jacobian is banded. Override the user-supplied bandwidths
  568. # (which are for the complex Jacobian) with the bandwidths of
  569. # the corresponding real-valued Jacobian wrapper of the complex
  570. # Jacobian.
  571. integrator_params['lband'] = 2 * (lband or 0) + 1
  572. integrator_params['uband'] = 2 * (uband or 0) + 1
  573. return ode.set_integrator(self, name, **integrator_params)
  574. def set_initial_value(self, y, t=0.0):
  575. """Set initial conditions y(t) = y."""
  576. y = asarray(y)
  577. self.tmp = zeros(y.size * 2, 'float')
  578. self.tmp[::2] = real(y)
  579. self.tmp[1::2] = imag(y)
  580. return ode.set_initial_value(self, self.tmp, t)
  581. def integrate(self, t, step=False, relax=False):
  582. """Find y=y(t), set y as an initial condition, and return y.
  583. Parameters
  584. ----------
  585. t : float
  586. The endpoint of the integration step.
  587. step : bool
  588. If True, and if the integrator supports the step method,
  589. then perform a single integration step and return.
  590. This parameter is provided in order to expose internals of
  591. the implementation, and should not be changed from its default
  592. value in most cases.
  593. relax : bool
  594. If True and if the integrator supports the run_relax method,
  595. then integrate until t_1 >= t and return. ``relax`` is not
  596. referenced if ``step=True``.
  597. This parameter is provided in order to expose internals of
  598. the implementation, and should not be changed from its default
  599. value in most cases.
  600. Returns
  601. -------
  602. y : float
  603. The integrated value at t
  604. """
  605. y = ode.integrate(self, t, step, relax)
  606. return y[::2] + 1j * y[1::2]
  607. def set_solout(self, solout):
  608. """
  609. Set callable to be called at every successful integration step.
  610. Parameters
  611. ----------
  612. solout : callable
  613. ``solout(t, y)`` is called at each internal integrator step,
  614. t is a scalar providing the current independent position
  615. y is the current soloution ``y.shape == (n,)``
  616. solout should return -1 to stop integration
  617. otherwise it should return None or 0
  618. """
  619. if self._integrator.supports_solout:
  620. self._integrator.set_solout(solout, complex=True)
  621. else:
  622. raise TypeError("selected integrator does not support solouta,"
  623. + "choose another one")
  624. # ------------------------------------------------------------------------------
  625. # ODE integrators
  626. # ------------------------------------------------------------------------------
  627. def find_integrator(name):
  628. for cl in IntegratorBase.integrator_classes:
  629. if re.match(name, cl.__name__, re.I):
  630. return cl
  631. return None
  632. class IntegratorConcurrencyError(RuntimeError):
  633. """
  634. Failure due to concurrent usage of an integrator that can be used
  635. only for a single problem at a time.
  636. """
  637. def __init__(self, name):
  638. msg = ("Integrator `%s` can be used to solve only a single problem "
  639. "at a time. If you want to integrate multiple problems, "
  640. "consider using a different integrator "
  641. "(see `ode.set_integrator`)") % name
  642. RuntimeError.__init__(self, msg)
  643. class IntegratorBase:
  644. runner = None # runner is None => integrator is not available
  645. success = None # success==1 if integrator was called successfully
  646. istate = None # istate > 0 means success, istate < 0 means failure
  647. supports_run_relax = None
  648. supports_step = None
  649. supports_solout = False
  650. integrator_classes = []
  651. scalar = float
  652. def acquire_new_handle(self):
  653. # Some of the integrators have internal state (ancient
  654. # Fortran...), and so only one instance can use them at a time.
  655. # We keep track of this, and fail when concurrent usage is tried.
  656. self.__class__.active_global_handle += 1
  657. self.handle = self.__class__.active_global_handle
  658. def check_handle(self):
  659. if self.handle is not self.__class__.active_global_handle:
  660. raise IntegratorConcurrencyError(self.__class__.__name__)
  661. def reset(self, n, has_jac):
  662. """Prepare integrator for call: allocate memory, set flags, etc.
  663. n - number of equations.
  664. has_jac - if user has supplied function for evaluating Jacobian.
  665. """
  666. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  667. """Integrate from t=t0 to t=t1 using y0 as an initial condition.
  668. Return 2-tuple (y1,t1) where y1 is the result and t=t1
  669. defines the stoppage coordinate of the result.
  670. """
  671. raise NotImplementedError('all integrators must define '
  672. 'run(f, jac, t0, t1, y0, f_params, jac_params)')
  673. def step(self, f, jac, y0, t0, t1, f_params, jac_params):
  674. """Make one integration step and return (y1,t1)."""
  675. raise NotImplementedError('%s does not support step() method' %
  676. self.__class__.__name__)
  677. def run_relax(self, f, jac, y0, t0, t1, f_params, jac_params):
  678. """Integrate from t=t0 to t>=t1 and return (y1,t)."""
  679. raise NotImplementedError('%s does not support run_relax() method' %
  680. self.__class__.__name__)
  681. # XXX: __str__ method for getting visual state of the integrator
  682. def _vode_banded_jac_wrapper(jacfunc, ml, jac_params):
  683. """
  684. Wrap a banded Jacobian function with a function that pads
  685. the Jacobian with `ml` rows of zeros.
  686. """
  687. def jac_wrapper(t, y):
  688. jac = asarray(jacfunc(t, y, *jac_params))
  689. padded_jac = vstack((jac, zeros((ml, jac.shape[1]))))
  690. return padded_jac
  691. return jac_wrapper
  692. class vode(IntegratorBase):
  693. runner = getattr(_vode, 'dvode', None)
  694. messages = {-1: 'Excess work done on this call. (Perhaps wrong MF.)',
  695. -2: 'Excess accuracy requested. (Tolerances too small.)',
  696. -3: 'Illegal input detected. (See printed message.)',
  697. -4: 'Repeated error test failures. (Check all input.)',
  698. -5: 'Repeated convergence failures. (Perhaps bad'
  699. ' Jacobian supplied or wrong choice of MF or tolerances.)',
  700. -6: 'Error weight became zero during problem. (Solution'
  701. ' component i vanished, and ATOL or ATOL(i) = 0.)'
  702. }
  703. supports_run_relax = 1
  704. supports_step = 1
  705. active_global_handle = 0
  706. def __init__(self,
  707. method='adams',
  708. with_jacobian=False,
  709. rtol=1e-6, atol=1e-12,
  710. lband=None, uband=None,
  711. order=12,
  712. nsteps=500,
  713. max_step=0.0, # corresponds to infinite
  714. min_step=0.0,
  715. first_step=0.0, # determined by solver
  716. ):
  717. if re.match(method, r'adams', re.I):
  718. self.meth = 1
  719. elif re.match(method, r'bdf', re.I):
  720. self.meth = 2
  721. else:
  722. raise ValueError('Unknown integration method %s' % method)
  723. self.with_jacobian = with_jacobian
  724. self.rtol = rtol
  725. self.atol = atol
  726. self.mu = uband
  727. self.ml = lband
  728. self.order = order
  729. self.nsteps = nsteps
  730. self.max_step = max_step
  731. self.min_step = min_step
  732. self.first_step = first_step
  733. self.success = 1
  734. self.initialized = False
  735. def _determine_mf_and_set_bands(self, has_jac):
  736. """
  737. Determine the `MF` parameter (Method Flag) for the Fortran subroutine `dvode`.
  738. In the Fortran code, the legal values of `MF` are:
  739. 10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25,
  740. -11, -12, -14, -15, -21, -22, -24, -25
  741. but this Python wrapper does not use negative values.
  742. Returns
  743. mf = 10*self.meth + miter
  744. self.meth is the linear multistep method:
  745. self.meth == 1: method="adams"
  746. self.meth == 2: method="bdf"
  747. miter is the correction iteration method:
  748. miter == 0: Functional iteraton; no Jacobian involved.
  749. miter == 1: Chord iteration with user-supplied full Jacobian.
  750. miter == 2: Chord iteration with internally computed full Jacobian.
  751. miter == 3: Chord iteration with internally computed diagonal Jacobian.
  752. miter == 4: Chord iteration with user-supplied banded Jacobian.
  753. miter == 5: Chord iteration with internally computed banded Jacobian.
  754. Side effects: If either self.mu or self.ml is not None and the other is None,
  755. then the one that is None is set to 0.
  756. """
  757. jac_is_banded = self.mu is not None or self.ml is not None
  758. if jac_is_banded:
  759. if self.mu is None:
  760. self.mu = 0
  761. if self.ml is None:
  762. self.ml = 0
  763. # has_jac is True if the user provided a Jacobian function.
  764. if has_jac:
  765. if jac_is_banded:
  766. miter = 4
  767. else:
  768. miter = 1
  769. else:
  770. if jac_is_banded:
  771. if self.ml == self.mu == 0:
  772. miter = 3 # Chord iteration with internal diagonal Jacobian.
  773. else:
  774. miter = 5 # Chord iteration with internal banded Jacobian.
  775. else:
  776. # self.with_jacobian is set by the user in the call to ode.set_integrator.
  777. if self.with_jacobian:
  778. miter = 2 # Chord iteration with internal full Jacobian.
  779. else:
  780. miter = 0 # Functional iteraton; no Jacobian involved.
  781. mf = 10 * self.meth + miter
  782. return mf
  783. def reset(self, n, has_jac):
  784. mf = self._determine_mf_and_set_bands(has_jac)
  785. if mf == 10:
  786. lrw = 20 + 16 * n
  787. elif mf in [11, 12]:
  788. lrw = 22 + 16 * n + 2 * n * n
  789. elif mf == 13:
  790. lrw = 22 + 17 * n
  791. elif mf in [14, 15]:
  792. lrw = 22 + 18 * n + (3 * self.ml + 2 * self.mu) * n
  793. elif mf == 20:
  794. lrw = 20 + 9 * n
  795. elif mf in [21, 22]:
  796. lrw = 22 + 9 * n + 2 * n * n
  797. elif mf == 23:
  798. lrw = 22 + 10 * n
  799. elif mf in [24, 25]:
  800. lrw = 22 + 11 * n + (3 * self.ml + 2 * self.mu) * n
  801. else:
  802. raise ValueError('Unexpected mf=%s' % mf)
  803. if mf % 10 in [0, 3]:
  804. liw = 30
  805. else:
  806. liw = 30 + n
  807. rwork = zeros((lrw,), float)
  808. rwork[4] = self.first_step
  809. rwork[5] = self.max_step
  810. rwork[6] = self.min_step
  811. self.rwork = rwork
  812. iwork = zeros((liw,), _vode_int_dtype)
  813. if self.ml is not None:
  814. iwork[0] = self.ml
  815. if self.mu is not None:
  816. iwork[1] = self.mu
  817. iwork[4] = self.order
  818. iwork[5] = self.nsteps
  819. iwork[6] = 2 # mxhnil
  820. self.iwork = iwork
  821. self.call_args = [self.rtol, self.atol, 1, 1,
  822. self.rwork, self.iwork, mf]
  823. self.success = 1
  824. self.initialized = False
  825. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  826. if self.initialized:
  827. self.check_handle()
  828. else:
  829. self.initialized = True
  830. self.acquire_new_handle()
  831. if self.ml is not None and self.ml > 0:
  832. # Banded Jacobian. Wrap the user-provided function with one
  833. # that pads the Jacobian array with the extra `self.ml` rows
  834. # required by the f2py-generated wrapper.
  835. jac = _vode_banded_jac_wrapper(jac, self.ml, jac_params)
  836. args = ((f, jac, y0, t0, t1) + tuple(self.call_args) +
  837. (f_params, jac_params))
  838. y1, t, istate = self.runner(*args)
  839. self.istate = istate
  840. if istate < 0:
  841. unexpected_istate_msg = 'Unexpected istate={:d}'.format(istate)
  842. warnings.warn('{:s}: {:s}'.format(self.__class__.__name__,
  843. self.messages.get(istate, unexpected_istate_msg)))
  844. self.success = 0
  845. else:
  846. self.call_args[3] = 2 # upgrade istate from 1 to 2
  847. self.istate = 2
  848. return y1, t
  849. def step(self, *args):
  850. itask = self.call_args[2]
  851. self.call_args[2] = 2
  852. r = self.run(*args)
  853. self.call_args[2] = itask
  854. return r
  855. def run_relax(self, *args):
  856. itask = self.call_args[2]
  857. self.call_args[2] = 3
  858. r = self.run(*args)
  859. self.call_args[2] = itask
  860. return r
  861. if vode.runner is not None:
  862. IntegratorBase.integrator_classes.append(vode)
  863. class zvode(vode):
  864. runner = getattr(_vode, 'zvode', None)
  865. supports_run_relax = 1
  866. supports_step = 1
  867. scalar = complex
  868. active_global_handle = 0
  869. def reset(self, n, has_jac):
  870. mf = self._determine_mf_and_set_bands(has_jac)
  871. if mf in (10,):
  872. lzw = 15 * n
  873. elif mf in (11, 12):
  874. lzw = 15 * n + 2 * n ** 2
  875. elif mf in (-11, -12):
  876. lzw = 15 * n + n ** 2
  877. elif mf in (13,):
  878. lzw = 16 * n
  879. elif mf in (14, 15):
  880. lzw = 17 * n + (3 * self.ml + 2 * self.mu) * n
  881. elif mf in (-14, -15):
  882. lzw = 16 * n + (2 * self.ml + self.mu) * n
  883. elif mf in (20,):
  884. lzw = 8 * n
  885. elif mf in (21, 22):
  886. lzw = 8 * n + 2 * n ** 2
  887. elif mf in (-21, -22):
  888. lzw = 8 * n + n ** 2
  889. elif mf in (23,):
  890. lzw = 9 * n
  891. elif mf in (24, 25):
  892. lzw = 10 * n + (3 * self.ml + 2 * self.mu) * n
  893. elif mf in (-24, -25):
  894. lzw = 9 * n + (2 * self.ml + self.mu) * n
  895. lrw = 20 + n
  896. if mf % 10 in (0, 3):
  897. liw = 30
  898. else:
  899. liw = 30 + n
  900. zwork = zeros((lzw,), complex)
  901. self.zwork = zwork
  902. rwork = zeros((lrw,), float)
  903. rwork[4] = self.first_step
  904. rwork[5] = self.max_step
  905. rwork[6] = self.min_step
  906. self.rwork = rwork
  907. iwork = zeros((liw,), _vode_int_dtype)
  908. if self.ml is not None:
  909. iwork[0] = self.ml
  910. if self.mu is not None:
  911. iwork[1] = self.mu
  912. iwork[4] = self.order
  913. iwork[5] = self.nsteps
  914. iwork[6] = 2 # mxhnil
  915. self.iwork = iwork
  916. self.call_args = [self.rtol, self.atol, 1, 1,
  917. self.zwork, self.rwork, self.iwork, mf]
  918. self.success = 1
  919. self.initialized = False
  920. if zvode.runner is not None:
  921. IntegratorBase.integrator_classes.append(zvode)
  922. class dopri5(IntegratorBase):
  923. runner = getattr(_dop, 'dopri5', None)
  924. name = 'dopri5'
  925. supports_solout = True
  926. messages = {1: 'computation successful',
  927. 2: 'computation successful (interrupted by solout)',
  928. -1: 'input is not consistent',
  929. -2: 'larger nsteps is needed',
  930. -3: 'step size becomes too small',
  931. -4: 'problem is probably stiff (interrupted)',
  932. }
  933. def __init__(self,
  934. rtol=1e-6, atol=1e-12,
  935. nsteps=500,
  936. max_step=0.0,
  937. first_step=0.0, # determined by solver
  938. safety=0.9,
  939. ifactor=10.0,
  940. dfactor=0.2,
  941. beta=0.0,
  942. method=None,
  943. verbosity=-1, # no messages if negative
  944. ):
  945. self.rtol = rtol
  946. self.atol = atol
  947. self.nsteps = nsteps
  948. self.max_step = max_step
  949. self.first_step = first_step
  950. self.safety = safety
  951. self.ifactor = ifactor
  952. self.dfactor = dfactor
  953. self.beta = beta
  954. self.verbosity = verbosity
  955. self.success = 1
  956. self.set_solout(None)
  957. def set_solout(self, solout, complex=False):
  958. self.solout = solout
  959. self.solout_cmplx = complex
  960. if solout is None:
  961. self.iout = 0
  962. else:
  963. self.iout = 1
  964. def reset(self, n, has_jac):
  965. work = zeros((8 * n + 21,), float)
  966. work[1] = self.safety
  967. work[2] = self.dfactor
  968. work[3] = self.ifactor
  969. work[4] = self.beta
  970. work[5] = self.max_step
  971. work[6] = self.first_step
  972. self.work = work
  973. iwork = zeros((21,), _dop_int_dtype)
  974. iwork[0] = self.nsteps
  975. iwork[2] = self.verbosity
  976. self.iwork = iwork
  977. self.call_args = [self.rtol, self.atol, self._solout,
  978. self.iout, self.work, self.iwork]
  979. self.success = 1
  980. def run(self, f, jac, y0, t0, t1, f_params, jac_params):
  981. x, y, iwork, istate = self.runner(*((f, t0, y0, t1) +
  982. tuple(self.call_args) + (f_params,)))
  983. self.istate = istate
  984. if istate < 0:
  985. unexpected_istate_msg = 'Unexpected istate={:d}'.format(istate)
  986. warnings.warn('{:s}: {:s}'.format(self.__class__.__name__,
  987. self.messages.get(istate, unexpected_istate_msg)))
  988. self.success = 0
  989. return y, x
  990. def _solout(self, nr, xold, x, y, nd, icomp, con):
  991. if self.solout is not None:
  992. if self.solout_cmplx:
  993. y = y[::2] + 1j * y[1::2]
  994. return self.solout(x, y)
  995. else:
  996. return 1
  997. if dopri5.runner is not None:
  998. IntegratorBase.integrator_classes.append(dopri5)
  999. class dop853(dopri5):
  1000. runner = getattr(_dop, 'dop853', None)
  1001. name = 'dop853'
  1002. def __init__(self,
  1003. rtol=1e-6, atol=1e-12,
  1004. nsteps=500,
  1005. max_step=0.0,
  1006. first_step=0.0, # determined by solver
  1007. safety=0.9,
  1008. ifactor=6.0,
  1009. dfactor=0.3,
  1010. beta=0.0,
  1011. method=None,
  1012. verbosity=-1, # no messages if negative
  1013. ):
  1014. super().__init__(rtol, atol, nsteps, max_step, first_step, safety,
  1015. ifactor, dfactor, beta, method, 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,), _dop_int_dtype)
  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,), _lsoda_int_dtype)
  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)