KDTree counts during matplotlib.animation - python

I am trying to find the best way to use KDTree.query_ball_point function to find the concentration of particles within 'r' distance, for every particle at each step of the animation, and have the number value from this count used in the conc equation. Essentially I am trying to make the settling (stokes) velocity a function of the concentration of surrounding particles.
So far my attempts have not worked, below is the code I am working with (excluding my failed KDTree attempts)
UPDATE: I have added in the def "concentration" to evaluate the concentration of nearest neighboring particles at every step of the animation. Additionally, I have changed the "r" (radius) to be constant so there is no variation in the size of particles so that the effects of the concentration of neighboring particles on the particle settling velocity can be observed more clearly (currently not working at all).
The problem: I can't figure out what is wrong with def concentration. there seems to be a bug, as the animation will not run.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.animation import FuncAnimation
import random
import pdb
from scipy import spatial
from scipy.spatial import KDTree
n=5
sigma=0.01
#m = np.random.uniform(n)
pp = 2.56 # pp = particle density (Sphene=3.53) (Feldspar=2.56)
#(g/cm^3)
pf = 2.7 # pf = fluid density(g/cm^3)
g = 9.8 # g = gravity (m/s^2)
r = 0.003 # r = radius of sphere (meter)
mu = 0.53 # mu = dynamic viscosity of fluid (log10Poise)
rp = 0.0005 #radius around particle to check for nearest neighbor
fig, ax = plt.subplots()
az = plt.axes(xlim=(-1, 1), ylim=(-100, 0))
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], 'ro', animated=True)
#pdb.set_trace()
def v_stokes(pp,pf,g,r,mu):
top=2*(pp-pf)*g*(r**2)
bottom=9*mu
ws=top/bottom
return ws
def init():
ax.set_xlim( -2, 2)
ax.set_ylim(-10, 0)
return ln,
def concentration(xdata, ydata, rp):
pdb.set_trace()
coords = list(zip(xdata, ydata))
tree = spatial.KDTree(coords)
test = np.column_stack([xdata, ydata])
nnl = tree.query_ball_point(test, rp) #nearest neighbors as a list
#(had tree in here before test but shape was wrong)
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt,
def update(frame):
global xdata
global ydata
global rp
global concentration
xdata = xdata + np.random.normal(0, sigma, n)
#ydata = ydata + np.random.normal(-0.1, sigma, n)
wss = v_stokes(pp,pf,g,r,mu)
if frame == 0.0:
ydata = np.zeros(len(xdata)) #makes the ydata length = xdata at
#time 0
cp = concentration(xdata, ydata, rp)
ydata = ydata + (wss*(1-cp))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True, interval=50)
plt.show()
Below is the code that I started with, basic animation of particles.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.animation import FuncAnimation
import random
n=100
sigma=0.1
nt=2000
fig, ax = plt.subplots()
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], 'ro', animated=True)
def init():
ax.set_xlim( -5, 5)
ax.set_ylim(-10, 0)
return ln,
def update(frame):
global xdata
global ydata
xdata = xdata + np.random.normal(0, sigma, n)
ydata = ydata + np.random.normal(-0.1, sigma, n)
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True, in)
plt.show()

Related

Wrong matplotlib animation

I have the following code that should draw a cycloid with animation and save it to a gif
but after running the program, a white square appears that covers everything, I can't find the reason cycloid_animation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation, PillowWriter
plt.rcParams['animation.html'] = 'html5'
R = 1
def circle(a, b, r):
# (a,b): the center of the circle
# r: the radius of the circle
# T: The number of the segments
T = 100
x, y = [0]*T, [0]*T
for i,theta in enumerate(np.linspace(0,2*np.pi,T)):
x[i] = a + r*np.cos(theta)
y[i] = b + r*np.sin(theta)
return x, y
# Calculate the cycloid line
thetas = np.linspace(0,4*np.pi,100)
cycloid_x = R*(thetas-np.sin(thetas))
cycloid_y = R*(1-np.cos(thetas))
cycloid_c = R*thetas
fig = plt.figure()
lns = []
trans = plt.axes().transAxes
for i in range(len(thetas)):
x,y = circle(cycloid_c[i], R, R)
ln1, = plt.plot(x, y, 'g-', lw=2)
ln2, = plt.plot(cycloid_x[:i+1] ,cycloid_y[:i+1], 'r-', lw=2)
ln3, = plt.plot(cycloid_x[i], cycloid_y[i], 'bo', markersize=4)
ln4, = plt.plot([cycloid_c[i], cycloid_x[i]], [R,cycloid_y[i]], 'y-', lw=2)
tx1 = plt.text(0.05, 0.8, r'$\theta$ = %.2f $\pi$' % (thetas[i]/np.pi), transform=trans)
lns.append([ln1,ln2,ln3,ln4,tx1])
plt.xlim(0,15)
plt.ylim(0,3)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.axes().set_aspect('equal')
ani = animation.ArtistAnimation(fig, lns, interval=50)
#ani.save('cycloid_ArtistAnimation.mp4',writer='ffmpeg')
ani.save('cycloid_ArtistAnimation.gif',writer='pillow')
ani
Each time you call plt.axis() you are creating a new axis on top of the figure. Since what you want is to get the current axis and then apply the transformations, after creating the figure you should call plt.gca() to get the current axis and use that instead.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation, PillowWriter
plt.rcParams['animation.html'] = 'html5'
R = 1
def circle(a, b, r):
# (a,b): the center of the circle
# r: the radius of the circle
# T: The number of the segments
T = 100
x, y = [0]*T, [0]*T
for i,theta in enumerate(np.linspace(0,2*np.pi,T)):
x[i] = a + r*np.cos(theta)
y[i] = b + r*np.sin(theta)
return x, y
# Calculate the cycloid line
thetas = np.linspace(0,4*np.pi,100)
cycloid_x = R*(thetas-np.sin(thetas))
cycloid_y = R*(1-np.cos(thetas))
cycloid_c = R*thetas
fig = plt.figure()
lns = []
trans = plt.gca().transAxes #<=== HERE
for i in range(len(thetas)):
x,y = circle(cycloid_c[i], R, R)
ln1, = plt.plot(x, y, 'g-', lw=2)
ln2, = plt.plot(cycloid_x[:i+1] ,cycloid_y[:i+1], 'r-', lw=2)
ln3, = plt.plot(cycloid_x[i], cycloid_y[i], 'bo', markersize=4)
ln4, = plt.plot([cycloid_c[i], cycloid_x[i]], [R,cycloid_y[i]], 'y-', lw=2)
tx1 = plt.text(0.05, 0.8, r'$\theta$ = %.2f $\pi$' % (thetas[i]/np.pi), transform=trans)
lns.append([ln1,ln2,ln3,ln4,tx1])
plt.xlim(0,15)
plt.ylim(0,3)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.gca().set_aspect('equal') #<=== And HERE
ani = animation.ArtistAnimation(fig, lns, interval=50)
#ani.save('cycloid_ArtistAnimation.mp4',writer='ffmpeg')
ani.save('cycloid_ArtistAnimation.gif',writer='pillow')

matplotlib animation: datas appears on the plot at the beginning

I am trying to calculate one of the basic decay simulation and plot the results as animation. Without animation results fine but when i try to create animation both line appears fully developed at the first time step. The change over time is not as it should be. What am I doing wrong? I'm open to suggestions.
Original plot:
plot
Here the code:
# I135 Xe135 decay.
"""
EQUATIONS:
dIdt = (-Lambda_I * N_I)
dXedt = ((-Lambda_Xe * N_Xe) + (Lambda_I * N_I))
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from scipy.integrate import odeint
def model(z,t):
"""
INITIAL CONDITIONS:
I(0) = 1e8 atoms
Xe(0) = 0 atom
PARAMETERS:
Lambda_I
Lambda_Xe
"""
Lambda_I = (np.log(2) / 6.57)
Lambda_Xe = (np.log(2) / 9.2)
N_I = z[0]
N_Xe = z[1]
dIdt = (-Lambda_I * N_I)
dXedt = ((-Lambda_Xe * N_Xe) + (Lambda_I * N_I))
return [dIdt,dXedt]
z0 = [1e8, 0.0] # initial conditions for N_Xe and N_I
n = 10000
max_time = 100
t = np.linspace(0,max_time,n)
N_I = np.zeros(n)
N_Xe = np.zeros(n)
# Solution
for i in range(n):
z = odeint(model,z0,t)
z0 = z[1]
N_I[i] = z0[0]
N_Xe[i] = z0[1]
# Graph and animation
fig, ax = plt.subplots(figsize=(8,8))
ax.set_ylim(0, 1e8)
ax.set_xlim(0, max_time)
line1, = ax.plot(N_I, 'b-', linewidth=2)
line2, = ax.plot(N_Xe,'g-.', linewidth=2)
plt.rcParams['font.size'] = '14'
plt.minorticks_on()
plt.tick_params(axis="both", which="major", length=12, labelsize=12, width=1, color="black")
plt.tick_params(axis="both", which="minor", length=6, labelsize=10, width=0.8, color="black")
plt.title('I-135 Xe-135 Decay Sim.', fontsize=18)
plt.xlabel('Time (h)', fontsize=16)
plt.ylabel('N', fontsize=16)
plt.legend(['I-135','Xe-135'],prop={'size': 12})
plt.grid(color = 'black', linestyle = '--', linewidth = 0.6)
def animate(j):
line1.set_xdata(np.linspace(0,j,n))
line2.set_xdata(np.linspace(0,j,n))
return [line1,line2,]
ani = animation.FuncAnimation(fig, animate, frames=max_time, blit=True, interval=10, save_count=10)
plt.show()
f = r"C://Users/USER_NAME/Desktop/animation.gif"
writergif = animation.PillowWriter(fps=30)
ani.save(f, writer=writergif)

Brownian Motion 3D representation

So far I have code for Brownian Motion in 1D and 2D. My 3D graph is obviously incorrect, since my x,y,and z data variables are all the same. I just don't know what to set them too. I'm trying to follow https://www.mathworks.com/matlabcentral/fileexchange/32067-brownian-motion?focused=5191300&tab=function Here's my code:
import numpy as np
from pylab import show
from math import sqrt
from scipy.stats import norm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
def brownian(x0, n, dt, delta, out=None):
# n : The number of steps to take.
# dt : time step
# delta : "speed" of motion
# out :If `out` is NOT None, it specifies the array in which to put the
# result. If `out` is None, a new numpy array is created and returned.
x0 = np.asarray(x0) #I.C
r = norm.rvs(size=x0.shape + (n,), scale=delta*sqrt(dt)) #generate n numbers for sample
if out is None: #create out array
out = np.empty(r.shape)
np.cumsum(r, axis=-1, out=out) #cumulative sum for random variables
out += np.expand_dims(x0, axis=-1)#initial condition.
return out
def main():
fig = plt.figure(1) #prepare plot
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax = fig.add_subplot(2, 3, 3, projection='3d')
delta = 2 # The Wiener process parameter.
T = 10.0
N = 500# Number of steps.
dt = T/N
m = 5 # Number of "lines"
x = np.empty((m,N+1))# Create an empty array to store the realizations.
x[:, 0] = 0# Initial values of x.
brownian(x[:,0], N, dt, delta, out=x[:,1:])
t = np.linspace(0.0, T, N+1)
for i in range(m):
ax1.plot(t, x[i])
ax1.set_title('1D Brownian Motion')
ax1.set_xlabel('t')
ax1.set_ylabel('x')
ax1.grid(True)
ax2.plot(x[0],x[1])
ax2.plot(x[0,0],x[1,0], 'go') #begin
ax2.plot(x[0,-1], x[1,-1], 'ro') #end
ax2.set_title('2D Brownian Motion')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.axis('equal')
ax2.grid(True)
#Data for a three-dimensional line
zline = np.linspace(0, 15, 1000)
xline = np.sin(zline)
yline = np.cos(zline)
ax.plot3D(xline, yline, zline, 'gray')
# Data for three-dimensional scattered points
zdata = brownian(x[:,0], N, dt, delta, out=x[:,1:])
xdata = brownian(x[:,0], N, dt, delta, out=x[:,1:])
ydata = brownian(x[:,0], N, dt, delta, out=x[:,1:])
ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='hot');
ax.set_title('3D Brownian Motion')
show()
return
main()
The first call to brownian makes 5 lines, since x[:, 0] has shape (5,):
brownian(x[:,0], N, dt, delta, out=x[:,1:])
So you could use any 3 of them to generate a 3D Brownian motion:
xdata, ydata, zdata = x[:3,:]
import numpy as np
from pylab import show
from math import sqrt
from scipy.stats import norm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
def brownian(x0, n, dt, delta, out=None):
# n : The number of steps to take.
# dt : time step
# delta : "speed" of motion
# out :If `out` is NOT None, it specifies the array in which to put the
# result. If `out` is None, a new numpy array is created and returned.
x0 = np.asarray(x0) #I.C
r = norm.rvs(size=x0.shape + (n,), scale=delta*sqrt(dt)) #generate n numbers for sample
if out is None: #create out array
out = np.empty(r.shape)
np.cumsum(r, axis=-1, out=out) #cumulative sum for random variables
out += np.expand_dims(x0, axis=-1)#initial condition.
return out
def main():
fig = plt.figure(1) #prepare plot
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax = fig.add_subplot(2, 3, 3, projection='3d')
delta = 2 # The Wiener process parameter.
T = 10.0
N = 500# Number of steps.
dt = T/N
m = 5 # Number of "lines"
x = np.empty((m,N+1))# Create an empty array to store the realizations.
x[:, 0] = 0# Initial values of x.
brownian(x[:,0], N, dt, delta, out=x[:,1:])
t = np.linspace(0.0, T, N+1)
for i in range(m):
ax1.plot(t, x[i])
ax1.set_title('1D Brownian Motion')
ax1.set_xlabel('t')
ax1.set_ylabel('x')
ax1.grid(True)
ax2.plot(x[0],x[1])
ax2.plot(x[0,0],x[1,0], 'go') #begin
ax2.plot(x[0,-1], x[1,-1], 'ro') #end
ax2.set_title('2D Brownian Motion')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.axis('equal')
ax2.grid(True)
xdata, ydata, zdata = x[:3,:]
ax.plot3D(xdata, ydata, zdata)
ax.set_title('3D Brownian Motion')
show()
return
main()

For loop inside matplotlib.animation (FuncAnimation) to evaluate variable at every step for all points

I am trying to find the best way to use KDTree.query_ball_point function to find the concentration of particles within 'r' distance, for every particle at each step of the animation, and have the number value from this count used in the conc equation. Essentially I am trying to make the settling (stokes) velocity a function of the concentration of surrounding particles.
So far my attempts have not worked, below is the code I am working with (excluding my failed KDTree attempts)
I have changed the "r" (radius) so there is no variation in the size of particles so that the effects of the concentration on particle settling velocity can be observed more clearly (currently not working at all). However, "c" within the for loop must be doing something because the animation is working (will not work without "c").
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.animation import FuncAnimation
import random
import pdb
from scipy import spatial
from scipy.spatial import KDTree
n=5
sigma=0.01
#m = np.random.uniform(n)
pp = 2.56 # pp = particle density (Sphene=3.53) (Feldspar=2.56)
#(g/cm^3)
pf = 2.7 # pf = fluid density(g/cm^3)
g = 9.8 # g = gravity (m/s^2)
r = 0.003 # r = radius of sphere (meter)
mu = 0.53 # mu = dynamic viscosity of fluid (log10Poise)
rp = 0.0005 #radius around particle to check for nearest neighbor
fig, ax = plt.subplots()
az = plt.axes(xlim=(-1, 1), ylim=(-100, 0))
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], 'ro', animated=True)
#pdb.set_trace()
def v_stokes(pp,pf,g,r,mu):
top=2*(pp-pf)*g*(r**2)
bottom=9*mu
ws=top/bottom
return ws
def init():
ax.set_xlim( -2, 2)
ax.set_ylim(-10, 0)
return ln,
def concentration(xdata, ydata, rp):
pdb.set_trace()
coords = list(zip(xdata, ydata))
tree = spatial.KDTree(coords)
test = np.column_stack([xdata, ydata])
nnl = tree.query_ball_point(test, rp) #nearest neighbors as a list
#(had tree in here before test but shape was wrong)
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt,
def update(frame):
global xdata
global ydata
global rp
global concentration
xdata = xdata + np.random.normal(0, sigma, n)
#ydata = ydata + np.random.normal(-0.1, sigma, n)
wss = v_stokes(pp,pf,g,r,mu)
if frame == 0.0:
ydata = np.zeros(len(xdata)) #makes the ydata length = xdata at
#time 0
cp = concentration(xdata, ydata, rp)
ydata = ydata + (wss*(1-cp))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True, interval=50)
plt.show()

Play/Pause/Stop and timer in Python animation

I created an animation (see code) that works perfectly. But, cannot manage to add 1) a Play/Pause/Stop button or "onClick()": nothing happens and the animation keeps on running. 2) Same thing for the timer, that I cannot see. Is is related to the 3D scatter ?
# -*- coding: utf-8 -*-
"""
Create a random distribution of points (cluster),
change the color and size of points
move the cluster (r, theta, phi)
This is done iteratively until we close the figure.
===================================================
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
pause = True
def onClick(event):
global pause
pause ^= True
# We reset the main parameters of the plot
def update_plot(i, color, size, elev, azim, dist, scat):
global pause
# Set colors...
scat.set_array(color[i])
# Set sizes...
scat.set_sizes(size[i])
# Set elevation annd azimuth...
ax.view_init(elev=elev[i], azim=azim[i])
# Set distance...
ax.dist=dist[i]
return scat,
# How many frame?
numframes = 500
# How many points?
numpoints = 200
# Initialization the position (x, y, z), the color (c) and the size (s) of the points
mu, sigma = 0, 0.25 # mean and standard deviation
x = np.random.normal(mu, sigma, numpoints)
y = np.random.normal(mu, sigma, numpoints)
z = np.random.normal(mu, sigma, numpoints)
c, s = np.random.random((2, numpoints))
# Definition of the data for the new values for each new plot
color_data = np.random.random((numframes, numpoints))
size_data = 200*np.random.random((numframes, numpoints))
elev_data = np.linspace(0, 360, numframes)
azim_data = np.linspace(0, 360, numframes)
dist_data = np.linspace(50, 1, numframes)
fig = plt.figure()
ax = Axes3D(fig, axisbg='black')
# We do not want the axis
ax.set_axis_off()
# This is where we plot the cluster
scat = ax.scatter(x, y, z, c=c, s=s, alpha=0.5)
xmin = np.min(x)
xmax = np.max(x)
ax.set_xlim(xmin,xmax)
ax.set_ylim(xmin,xmax)
ax.set_zlim(xmin,xmax)
# This is the animation. In fargs, we provide the data for each new plot.
fig.canvas.mpl_connect('button_press_event', onClick)
ani = animation.FuncAnimation(fig, update_plot, frames=range(numframes),
fargs=(color_data, size_data,
elev_data, azim_data, dist_data,
scat), blit=False, interval=10, repeat=True)
plt.show()

Categories

Resources