I am wanting to solve a system of ODEs where for the first 30,000 seconds, I want one of my state variables to start from the same initial value. After those 30,000 seconds, I want to change the initial value of that state variable to something different and simulate the system for the rest of time. Here is my code:
def ode_rhs(y, t):
ydot[0] = -p[7]*y[0]*y[1] + p[8]*y[8] + p[9]*y[8]
ydot[1] = -p[7]*y[0]*y[1] + p[8]*y[8]
ydot[2] = -p[10]*y[2]*y[3] + p[11]*y[9] + p[12]*y[9]
ydot[3] = -p[13]*y[3]*y[6] + p[14]*y[10] + p[15]*y[10] - p[10]*y[2]*y[3] + p[11]*y[9] + p[9]*y[8] - p[21]*y[3]
ydot[4] = -p[19]*y[4]*y[5] - p[16]*y[4]*y[5] + p[17]*y[11] - p[23]*y[4] + y[7]*p[20]
ydot[5] = -p[19]*y[4]*y[5] + p[15]*y[10] - p[16]*y[4]*y[5] + p[17]*y[11] + p[18]*y[11] + p[12]*y[9] - p[22]*y[5]
ydot[6] = -p[13]*y[3]*y[6] + p[14]*y[10] - p[22]*y[6] - p[25]*y[6] - p[23]*y[6]
ydot[7] = 0
ydot[8] = p[7]*y[0]*y[1] - p[8]*y[8] - p[9]*y[8]
ydot[9] = p[10]*y[2]*y[3] - p[11]*y[9] - p[12]*y[9] - p[21]*y[9]
ydot[10] = p[13]*y[3]*y[6] - p[14]*y[10] - p[15]*y[10] - p[22]*y[10] - p[21]*y[10] - p[23]*y[10]
ydot[11] = p[19]*y[4]*y[5] + p[16]*y[4]*y[5] - p[17]*y[11] - p[18]*y[11] - p[22]*y[11] - p[23]*y[11]
ydot[12] = p[22]*y[10] + p[22]*y[11] + p[22]*y[5] + p[22]*y[6] + p[21]*y[10] + p[21]*y[3] + p[21]*y[9] + p[24]*y[13] + p[25]*y[6] + p[23]*y[10] + p[23]*y[11] + p[23]*y[4] + p[23]*y[6]
ydot[13] = p[15]*y[10] + p[18]*y[11] - p[24]*y[13]
return ydot
pysb.bng.generate_equations(model)
alias_model_components()
p = np.array([k.value for k in model.parameters])
ydot = np.zeros(len(model.odes))
y0 = np.zeros(len(model.odes))
y0[0:7] = p[0:7]
t = np.linspace(0.0,1000000.0,100000)
r = odeint(ode_rhs,y0,t)
So, in other words, I want to set y0[1] to the same value (100) each time odeint
is called for the first 30,000 seconds. I'm effectively trying to let the system equilibrate for an amount of time before inputing a signal into the system. I thought about doing something like if t < 30000: y0[1] = 100
as the first line of my ode_rhs()
function, but I'm not quite sure that works.
It sounds like you want y1(t) to remain constant (with the value 100) for the equilibration phase. You can do this by ensuring that dy1(t)/dt = 0 during this phase. There are (at least) two ways you can accomplish that. The first is to modify the calculation of
ydot[1]
inode_rhs
as follows:and use the intitial condition 100 for
y[1]
.Note that this introduces a discontinuity in the right-hand side of the system, but the adaptive solver used by
odeint
(the Fortran code LSODA) is usually robust enough to handle it.Here's a self-contained example. I've made
p
andt1
arguments toode_rhs
.t1
is the duration of the equilibration phase.A slight variation of the above method is to modify the system by adding a parameter that is either 0 or 1. When the parameter is 0, the equlibration system is solved, and when the parameter is 1, the full system is solved. The code for
ydot[1]
(in my smaller example) is thenwhere
full
is the parameter.To handle the equilibration phase, the system is solved once on 0 <= t < t1 with
full=0
. Then the final value of the equilibration solution is used as the initial condition to the second solution, run withfull=1
. The advantage of this method is that you are not forcing the solver to deal with the discontinuity.Here's how it looks in code.
And here's the plot that it generates (the plot from the first example is almost exactly the same):
This answer did not work me, as I needed to alter my initial conditions periodically. Therefore, I would like to propose alternative solution, which is to alternate conditions for differential equation inside the function itself:
We look at the value of t and adjust it:
Here it is inside a function:
That allows to call the function to solve the equation exactly once.
And more importantly, you can now adjust parameters periodically inside the equation. For instance, say you have t = [0:200] and you want to change value of full every 20 steps, you can do so like this: