'Matplotlib Animation of Streamplot of Bifurcation
I am currently trying to animate the dynamics of a typical saddle node bifurcation ode: dx/dt = r + x^2. Snapshots at specific values of r are realised with the streamplot function from r = -1 to 1. Unfortunately the init function and the animate function are not working properly because .set_array does not work for streamplots. I am also not sure how to update the streams at each iteration in the animate function. My question is how I should modify the animate and init function so that the funcanimation function gives a proper animated plot of the flows.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
nx, ny = .02, .02
x = np.arange(-15, 15, nx)
y = np.arange(-10, 10, ny)
X, Y = np.meshgrid(x, y)
dy = -1 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
color = dyu
fig, ax = plt.subplots()
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
ax.set_xlabel('t')
ax.set_ylabel('x')
def init():
stream.set_array([])
return stream
def animate(iter):
dy = -1 + iter * 0.01 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
stream.set_array(dyu.ravel())
return stream
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=False, repeat=False)
plt.show()
Solution 1:[1]
I worked around this by clearing the lines and arrows in every iteration:
ax.collections = [] # clear lines streamplot
ax.patches = [] # clear arrowheads streamplot
So, I modified your code like this:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
nx, ny = .02, .02
x = np.arange(-15, 15, nx)
y = np.arange(-10, 10, ny)
X, Y = np.meshgrid(x, y)
dy = -1 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
color = dyu
fig, ax = plt.subplots()
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
ax.set_xlabel('t')
ax.set_ylabel('x')
def animate(iter):
ax.collections = [] # clear lines streamplot
ax.patches = [] # clear arrowheads streamplot
dy = -1 + iter * 0.01 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
print(iter)
return stream
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=False, repeat=False)
anim.save('./animation.gif', writer='imagemagick', fps=60)
# plt.show()
Solution 2:[2]
CAUTION: @SebastianBeyer's previously working answer no longer works in 2022. For unknown (and presumably indefensible) reasons, Matplotlib now prohibits attempts to manually replace the axes.patches
list by raising a non-human-readable exception resembling:
AttributeError: can't set attribute 'patches'
Thankfully, yet another working workaround exists. Inspired by @Sheldore's working answer here, you must now iteratively search for and remove all matplotlib.patches.FancyArrowPatch
child artists from the streamplot's axes: e.g.,
# Rather than this...
ax.patches = [] # clear arrowheads streamplot
# ...you must now do this.
from matplotlib.patches import FancyArrowPatch
for artist in ax.get_children():
if isinstance(artist, FancyArrowPatch):
artist.remove()
In full, the post-2020 working solution is now:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from matplotlib.patches import FancyArrowPatch
nx, ny = .02, .02
x = np.arange(-15, 15, nx)
y = np.arange(-10, 10, ny)
X, Y = np.meshgrid(x, y)
dy = -1 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
color = dyu
fig, ax = plt.subplots()
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
ax.set_xlabel('t')
ax.set_ylabel('x')
def animate(iter):
ax.collections = [] # clear lines streamplot
# Clear arrowheads streamplot.
for artist in ax.get_children():
if isinstance(artist, FancyArrowPatch):
artist.remove()
dy = -1 + iter * 0.01 + Y**2
dx = np.ones(dy.shape)
dyu = dy / np.sqrt(dy**2 + dx**2)
dxu = dx / np.sqrt(dy**2 + dx**2)
stream = ax.streamplot(X,Y,dxu, dyu, color=color, density=2, cmap='jet',arrowsize=1)
print(iter)
return stream
anim = animation.FuncAnimation(fig, animate, frames=100, interval=50, blit=False, repeat=False)
anim.save('./animation.gif', writer='imagemagick', fps=60)
# plt.show()
Thanks alot, post-2020 matplotlib. </facepalm>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Sebastian Beyer |
Solution 2 | Pietakio |