8

I currently have a system of odes with a time-dependent constant. E.g.

def fun(u, t, a, b, c):
    x = u[0]
    y = u[1]
    z = u[2]
    dx_dt = a * x + y * z
    dy_dt = b * (y-z)
    dz_dt = -x*y+c*y-z
    return [dx_dt, dy_dt, dz_dt]

The constants are "a", "b" and "c". I currently have a list of "a"s for every time-step which I would like to insert at every time-step, when using the scipy ode solver...is this possible?

Thanks!

creampiedonut
  • 327
  • 1
  • 5
  • 17

3 Answers3

12

Yes, this is possible. In the case where a is constant, I guess you called scipy.integrate.odeint(fun, u0, t, args) where fun is defined as in your question, u0 = [x0, y0, z0] is the initial condition, t is a sequence of time points for which to solve for the ODE and args = (a, b, c) are the extra arguments to pass to fun.

In the case where a depends on time, you simply have to reconsider a as a function, for example (given a constant a0):

def a(t):
    return a0 * t

Then you will have to modify fun which computes the derivative at each time step to take the previous change into account:

def fun(u, t, a, b, c):
    x = u[0]
    y = u[1]
    z = u[2]
    dx_dt = a(t) * x + y * z # A change on this line: a -> a(t)
    dy_dt = b * (y - z)
    dz_dt = - x * y + c * y - z
    return [dx_dt, dy_dt, dz_dt]

Eventually, note that u0, t and args remain unchanged and you can again call scipy.integrate.odeint(fun, u0, t, args).

A word about the correctness of this approach. The performance of the approximation of the numerical integration is affected, I don't know precisely how (no theoretical guarantees) but here is a simple example which works:

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.integrate

tmax = 10.0

def a(t):
    if t < tmax / 2.0:
        return ((tmax / 2.0) - t) / (tmax / 2.0)
    else:
        return 1.0

def func(x, t, a):
    return - (x - a(t))

x0 = 0.8
t = np.linspace(0.0, tmax, 1000)
args = (a,)
y = sp.integrate.odeint(func, x0, t, args)

fig = plt.figure()
ax = fig.add_subplot(111)
h1, = ax.plot(t, y)
h2, = ax.plot(t, [a(s) for s in t])
ax.legend([h1, h2], ["y", "a"])
ax.set_xlabel("t")
ax.grid()
plt.show()

enter image description here

I Hope this will help you.

Flabetvibes
  • 3,066
  • 20
  • 32
  • In the case of a kink the average result (for a fixed step size integrator) will have a global error O(dt), the same as the Euler method. With adaptive step size the kink is a point of infinite curvature, reducing the step size down to the bailout point (and thus increasing the number of steps and thus the accumulation of floating point errors). One kink will not have a noticeable influence. – Lutz Lehmann Oct 07 '15 at 19:20
1

No, that is not possible in the literal sense of

"I currently have a list of "a"s for every time-step which I would like to insert at every time-step"

as the solver has adaptive step size control, that is, it will use internal time steps that you have no control over, and each time step uses several evaluations of the function. Thus there is no connection between the solver time steps and the data time steps.

In the extended sense that the given data defines a piecewise constant step function however, there are several approaches to get to a solution.

  1. You can integrate from jump point to jump point, using the ODE function with the constant parameter for this time segment. After that use numpy array operations like concatenate to assemble the full solution.

  2. You can use interpolation functions like numpy.interp or scipy.interpolate.interp1d. The first gives a piecewise linear interpolation, which may not be desired here. The second returns a function object that can be configured to be a "zero-order hold", which is a piecewise constant step function.

  3. You could implement your own logic to go from the time t to the correct values of those parameters. This mostly applies if there is some structure to the data, for instance, if they have the form f(int(t/h)).

Note that the approximation order of the numerical integration is not only bounded by the order of the RK (solve_ivp) or multi-step (odeint) method, but also by the differentiability order of the (parts of) the differential equation. If the ODE is much less smooth than the order of the method, the implicit assumptions for the step size control mechanism are violated, which may result in a very small step size requiring a huge number of integration steps.

Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
  • Actually, that is possible (c.f. my answer). By the way, I'm not sure `scipy.integrate.odeint` use Runge-Kutta internally. I think it is more a mix of methods such as the linear multistep methods (Adams' methods and Backward differentiation formulas method) which are more powerful than the Runge-Kutta method. – Flabetvibes Oct 07 '15 at 14:47
  • 1
    That seems to correspond to the piecewise constant case. – Lutz Lehmann Oct 07 '15 at 19:15
  • thanks for your answer, actually I have the sae problem and I have a piecewise linear dependence on time. You mentioned that It could be interpolated. Can you give more details on that case? – Ibrahim zawra Oct 21 '21 at 13:03
  • @Ibrahimzawra : You have the function `numpy.interp` which does immediate piecewise interpolation, and `scipy.interpolation.interp1d` which returns an interpolation function that can be generated in various modes, inclusive the zero-order hold, that is, a piecewise constant function based on the provided time-value data. – Lutz Lehmann Oct 21 '21 at 13:20
0

I also encountered similar problem. In my case, parameters a, b, and c are not in direct function with time, but determined by x, y, and z at that time. So I have to get x, y, z at time t, and calculate a, b, c for the integration calculation for x, y, z at t+dt. It turns out that if I change dt value, the whole integration result will change dramatically, even to something unreasonable.

Miguel Conde
  • 813
  • 10
  • 22
Enlong Liu
  • 19
  • 1
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/low-quality-posts/30145770) – camille Oct 22 '21 at 22:42
  • 1
    Or at least, to @camille's point, if this is an answer, I'm having a difficult time understanding it. It sounds more like you've encountered a similar problem, have tried some things, and are providing additional information on where you got stuck. Am I misunderstanding? If so, it'd be useful to edit your answer to add more clarity. – Jeremy Caney Oct 23 '21 at 00:50