3

I'm trying to solve a single first-order ODE using ODEINT. Following is the code. I expect to get 3 values of y for 3 time-points. The issue I'm struggling with is ability to pass nth value of mt and nt to calculate dydt. I think the ODEINT passes all 3 values of mt and nt, instead just 0th, 1st or 2nd, depending on the iteration. Because of this, I get this error:

RuntimeError: The size of the array returned by func (4) does not match the size of y0 (1).

Interestingly, if I replace the initial condition, which is (and should be) a single value as: a0= [2]*4, the code works, but gives me a 4X4 matrix as solution, which seems incorrect.

mt = np.array([3,7,4,2]) # Array of constants
nt = np.array([5,1,9,3]) # Array of constants
c1,c2,c3 = [-0.3,1.4,-0.5] # co-efficients
para = [mt,nt] # Packing parameters

#Test ODE function
def test (y,t,extra):
    m,n = extra
    dydt = c1*c2*m - c1*y - c3*n
    return dydt

a0= [2]  # Initial Condition
tspan = range(len(mt)) # Define tspan

#Solving the ODE
yt= odeint(test, a0,tspan,args=(para,)) 
#Plotting the ODE
plt.plot(tspan,yt,'g')
plt.title('Multiple Parameters Test')
plt.xlabel('Time')
plt.ylabel('Magnitude')

The first order differential equation is:

dy/dt = c1*(c2*mt-y(t)) - c3*nt

This equation represents a part of murine endocrine system, which I am trying to model. The system is analogous to a two-tank system, where the first tank receives a specific hormone [at an unknown rate] but our sensor will detect that level (mt) at specific time intervals (1 second). This tank then feeds into the second tank, where the level of this hormone (y) is detected by another sensor. I labeled the levels using separate variables because the sensors that detect the levels are independent of each other and are not calibrated to each other. 'c2' may be considered as the co-efficient that shows the correlation between the two levels. Also, the transfer of this hormone from tank 1 to tank 2 is diffusion-driven. This hormone is further consumed by a biochemical process (similar to a drain valve for the second tank). At the moment, it is unclear which parameters affect the consumption; however, another sensor can detect the amount of hormone (nt) being consumed at a specific time interval (1 second, in this case too).

Thus, mt and nt are the concentrations/levels of the hormone at specific time points. although only 4-element in length in the code, these arrays are much longer in my study. All sensors report the concentrations at 1 second interval - hence tspan consists of time points separated by 1 second.

The objective is to determine the concentration of this hormone in the second tank (y) mathematically and then optimize the values of these coefficients based on the experimental data. I was able to pass these arrays mt and nt to the defined ODE and solve using ODE45 in MATLAB with no issue. I've been running into this RunTimeError, while trying to replicate the code in Python.

bluetooth
  • 529
  • 6
  • 20
  • If you follow how your variables defined and passed around, you'll see that `m` in `test()` becomes `mt`. `mt` is a numpy array with length 4, so `c1*c2*m` is also a numpy array with length 4 (as is `c3*n`). Then `dydt` is an array with length 4. So you are, in fact, returning an array with length 4 from `test()`. – Warren Weckesser Jan 25 '17 at 20:20
  • Yes, I realized that - . How do I correct this problem so that 'test()' only takes one value of mt and nt - the nth value for nth iteration - and return just one value instead of an array of length 4? – bluetooth Jan 25 '17 at 20:25
  • What do you mean by the "nth iteration"? – Warren Weckesser Jan 25 '17 at 20:26
  • odeint will call the function `test` 4 times for the 4 points in `tspan`, right? I thought each call to `test` by ODE will be called an iteration - no? – bluetooth Jan 25 '17 at 20:29
  • No. `odeint` may call the function `test` many times, with values of `t` that are not necessarily in `tspan`. You have to implement `test` as a function of arbitrary values of `t`. I.e., your function must handle `t=1e-3`, or `t=2.5`, etc. – Warren Weckesser Jan 25 '17 at 20:32
  • @WarrenWeckesser, thank you for the reply - I did not know that. Where do I find this info, since it was not available in the ODEINT docstring. Also, how can I improve this code? What changes do I need to make to this code so that `a0=2` and `yt` produces a `4X1` array? – bluetooth Jan 26 '17 at 01:58
  • Can anyone please help me fix this code? I have tried everything I could; but nothing seems to fix the problem. Thanks in advance! – bluetooth Jan 29 '17 at 19:54
  • 1
    Could you clarify (in the question) the problem that you are trying to solve? My interpretation is that you have a first order differential equation `dy/dt = c1*c2*m(t) - c1*y - c3*n(t)`, but the functions `m(t)` and `n(t)` are only known at the discrete time values t=0, 1, 2, 3. If that is the case, you have to decide how you want to define `m(t)` and `n(t)` for arbitrary `t`. If you can't do that, then you don't have a well-defined differential equation. If my understanding is incorrect, please clarify the *mathematical* problem by explaining it in more detail in the question. – Warren Weckesser Jan 30 '17 at 02:19
  • @WarrenWeckesser, I updated the question with additional details. Please let me know if more information/clarification is needed. – bluetooth Jan 30 '17 at 20:01
  • Anyone out there? – bluetooth Feb 03 '17 at 17:53

1 Answers1

3

As I mentioned in a comment, if you want to model this system using an ordinary differential equation, you have to make an assumption about the values of m and n between sample times. One possible model is to use linear interpolation. Here's a script that uses scipy.interpolate.interp1d to create the functions mfunc(t) and nfunc(t) based on the samples mt and nt.

import numpy as np
from scipy.integrate import odeint
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

mt = np.array([3,7,4,2])  # Array of constants
nt = np.array([5,1,9,3])  # Array of constants

c1, c2, c3 = [-0.3, 1.4, -0.5] # co-efficients

# Create linear interpolators for m(t) and n(t).
sample_times = np.arange(len(mt))
mfunc = interp1d(sample_times, mt, bounds_error=False, fill_value="extrapolate")
nfunc = interp1d(sample_times, nt, bounds_error=False, fill_value="extrapolate")

# Test ODE function
def test (y, t):
    dydt = c1*c2*mfunc(t) - c1*y - c3*nfunc(t)
    return dydt

a0 = [2]  # Initial Condition
tspan = np.linspace(0, sample_times.max(), 8*len(sample_times)+1)
#tspan = sample_times

# Solving the ODE
yt = odeint(test, a0, tspan)

# Plotting the ODE
plt.plot(tspan, yt, 'g')
plt.title('Multiple Parameters Test')
plt.xlabel('Time')
plt.ylabel('Magnitude')
plt.show()

Here is the plot created by the script:

plot

Note that instead of generating the solution only at sample_times (i.e. at times 0, 1, 2, and 3), I set tspan to a denser set of points. This shows the behavior of the model between sample times.

Warren Weckesser
  • 110,654
  • 19
  • 194
  • 214