0

I have a system of differential equations. The solutions depend on a parameter beta. I want to create a slider so I can change this parameter and display the changes of the solution curves directly in my plot. I almost got it I think, but I miss one piece.

My code

N = 1

#Initial conditions
I0 = 0.01
S0= N - I0

#System of diff. equations
def system(x, t, beta, gamma ):
    I, S = x

    dIdt = (beta/gamma*S-1)*I*gamma
    dSdt = -(beta/gamma*S-1)*I*gamma

    return dIdt, dSdt

#Parameters initial value
beta = 0.03
gamma = 0.017

#Initial cond. vector
y0 = I0, S0

#time grid
t = np.linspace(0, 1300, 1300)

# Solution 
sol = odeint(system, y0, t, args=(beta, gamma))


################ Animations part ##################

fig, ax = plt.subplots()
plt.subplots_adjust(bottom = 0.25)

#solution curves for I and S
infected, = plt.plot(t, sol[:,0])
recovered, = plt.plot(t, sol[:,1])

axbeta = plt.axes([0.125, 0.1, 0.5, 0.05])

sliderbeta = Slider(axbeta, 'beta', 0, 1, valinit=beta)

def update_beta(val):
    beta_value = sliderbeta.val
    ??????????????????????????????????????
    fig.canvas.draw_idle()

sliderbeta.on_changed(update_beta)

plt.show()

I don't know how to acces my initial beta value and how to replace it by beta_value. I guess there is some line missing where I put the question marks.

Jailbone
  • 167
  • 1
  • 9

1 Answers1

1

You take any ODE integration out of the global scope and migrate it to the update function. Following Automatically Rescale ylim and xlim in Matplotlib, one needs to add commands to compute the new limits and apply them.

# line objects for the solution curves for I and S
infected, = ax.plot([0], [0])
recovered, = ax.plot([0], [0])

def update_beta(beta):
    # if triggered as call-back it passes the current slider value
    # Solution 
    sol = odeint(system, y0, t, args=(beta, gamma))
    # update the data for I and S
    infected.set_data(t, sol[:,0])
    recovered.set_data(t, sol[:,1])
    # recompute the ax.dataLim
    ax.relim()
    # update ax.viewLim using the new dataLim
    ax.autoscale_view()
    fig.canvas.draw_idle()

And finally, to get an initial plot on startup, call this update function from the global scope once

update_beta(beta)
Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
  • I did as you mentioned. It is not working. I do not need the on_changed method for my slider? – Jailbone Apr 09 '20 at 15:54
  • 1
    Yes, one needs to add relim+autoscale to get it to display properly. And everything that I did not mention remains as it was. – Lutz Lehmann Apr 09 '20 at 16:44
  • It works now! Thank you! I got one more question: What if I want to add a button, so that on click it animates the transition of beta, say from 0 to 1, automatically? In addition to being able to set it manually like it is now. I'll be happy with an approach. – Jailbone Apr 09 '20 at 16:54
  • 1
    This can very quickly get confusing visually if the y axis keeps rescaling. In principle you could make a loop inside the update function and use a pause or sleep function to control the speed. The button would then change if the transition loop has length 1 or 10. But I never tried something like this with matplotlib, so there might be better ways. Also look at matplotlib.animation. – Lutz Lehmann Apr 09 '20 at 17:26