Nov-27-2019, 11:56 PM
Hi,
I am trying to animate/simulate a sprinkler. I tried it in 2D, and got the output I wanted (moving points, iterative creation of points/droplets, hitting the ground and stopping).
I thought changing it to 3D would not be an issue, however I am only getting a single stationary point as my output on the 3D graph. The point is located at the starting position of (0.0, 0.0, 0.0). I think the problem may by with the update function, where set_offsets may only be for a 2D graph? I looked through the matplotlib documentation, and couldn't find anything similar for a 3D plot.
https://matplotlib.org/api/_as_gen/mpl_t...s3d.Axes3D
Going from 2D to 3D, I changed the position, velocity, and acceleration from 1x2 arrays ro 1x3 arrays. I also changed the max positions from maxs[1] to maxs[2], etc.
Is there an error in my code/logic or is the problem with the update function?
I am trying to animate/simulate a sprinkler. I tried it in 2D, and got the output I wanted (moving points, iterative creation of points/droplets, hitting the ground and stopping).
I thought changing it to 3D would not be an issue, however I am only getting a single stationary point as my output on the 3D graph. The point is located at the starting position of (0.0, 0.0, 0.0). I think the problem may by with the update function, where set_offsets may only be for a 2D graph? I looked through the matplotlib documentation, and couldn't find anything similar for a 3D plot.
https://matplotlib.org/api/_as_gen/mpl_t...s3d.Axes3D
Going from 2D to 3D, I changed the position, velocity, and acceleration from 1x2 arrays ro 1x3 arrays. I also changed the max positions from maxs[1] to maxs[2], etc.
Is there an error in my code/logic or is the problem with the update function?
import matplotlib import matplotlib.pyplot as plt from matplotlib import animation import numpy as np from math import sin, cos from mpl_toolkits.mplot3d import Axes3D import random #Parameters rho = 1.225 c = 0.5 v0 = 50 g = 9.81 #Timing tmax = 10 nframes = 100 time = np.linspace(0,tmax, nframes) dt = time[1]-time[0] #Waterdroplets ndrops = 100 #Positioning maxs = [0.0, 0.0, 0.0] rmax = [0.0008, 0.0065] #Droplet sizing theta = np.radians(np.random.normal(37, 8, 80)) phi = np.radians(np.random.normal(3, 1.07, 80)) radii = np.random.normal(0.004, 0.001, 20) class drop: def __init__(self,pos,vel,r): self.pos = pos self.vel = vel self.r = r class sprinkler: def __init__(self): self.fig = plt.figure() self.ax = self.fig.add_subplot(111, projection = '3d') self.drops = [None]*ndrops # creates empty list of length ndrops self.maxt = 0.0 #Find the maximum flight time for each droplet #and the maximum distance the droplets will travel theta = np.radians(np.random.normal(37, 8, 80)) phi = np.radians(np.random.normal(3, 1.07, 80)) radii = np.random.normal(0.004, 0.001, 80) for i in range(len(phi)): m = [drop([0.0, 0.1, 0.1], [v0*cos(theta[i])*cos(phi[i]), v0*cos(theta[i])*sin(theta[i]), v0*sin(theta[i])],0.0008), drop([0.0, 0.1, 0.1], [v0*cos(theta[i])*cos(phi[i]), v0*cos(theta[i])*sin(theta[i]), v0*sin(theta[i])],0.0065)] for d in m: t = 0.0 coef = -0.5*c*np.pi*d.r**2*rho mass = 4/3*np.pi*d.r**3*1000 while d.pos[2] > 0: a = np.power(d.vel, 2) * coef * np.sign(d.vel)/mass a[2] -= g d.pos += (d.vel + a * dt) * dt d.vel += a * dt t += dt if d.pos[2] > maxs[2]: maxs[2] = d.pos[2] if d.pos[1] > maxs[1]: maxs[1] = d.pos[1] if d.pos[0] > maxs[0]: maxs[0] = d.pos[0] if d.pos[2] < 0.0: if t > self.maxt: self.maxt = t break print('Max time is:',self.maxt) print('Max positions are:', maxs) #create initial droplets for ii in range(ndrops): phiang = random.randint(0,len(phi)-1) thetang = random.randint(0,len(theta)-1) rad = random.randint(0,len(radii)-1) self.drops[ii] = drop([0.0, 0.0, 0.1], [v0*cos(theta[thetang])*cos(phi[phiang]), v0*cos(theta[thetang])*sin(phi[phiang]), v0*sin(theta[thetang])], radii[random.randint(0,len(radii)-1)]) #initiate animation ani = animation.FuncAnimation(self.fig, self.update, init_func = self.setup, interval = 200, frames = nframes) ani.save('Sprinkler.mp4', fps = 20, extra_args=['-vcodec', 'libx264']) plt.show() #Setup is used to initiate the 3D axis and plot def setup(self): self.scat = self.ax.scatter([d.pos[0] for d in self.drops], [d.pos[1] for d in self.drops], [d.pos[2] for d in self.drops], 'b.') self.ax.set_xlim(-1, 100) self.ax.set_ylim(-1, 100) self.ax.set_zlim(0, 50) self.ax.set_xlabel('X Distance') self.ax.set_ylabel('Y Distance') self.ax.set_zlabel('Height') return self.scat #Update function updates the animation each frame. def update(self, frame): if time[frame] <(tmax-self.maxt*1.1): self.create(ndrops) self.step() #offset moves each point along accoring to the step function self.scat.set_offsets([[d.pos[0] for d in self.drops], [d.pos[1] for d in self.drops], [d.pos[2] for d in self.drops]]) return self.scat, #New droplets are created at each frame to have iterative droplets coming from the sprinkler def create(self, i): for l in range(i): phiang = random.randint(0,len(phi)-1) thetang = random.randint(0,len(theta)-1) rad = random.randint(0,len(radii)-1) self.drops.append(drop([0.0, 0.0, 0.0], [v0*cos(theta[thetang])*cos(phi[phiang]), v0*cos(theta[thetang])*sin(phi[phiang]), v0*sin(theta[thetang])], radii[rad])) #step function moves each droplet along at each interval dt def step(self): for x in range(len(self.drops)): coef = -0.5*c*np.pi*self.drops[x].r**2*rho #drag force coefficient mass = 4/3*np.pi*self.drops[x].r**3*1000 #mass of droplet a = np.power(self.drops[x].vel,2) * coef * np.sign(self.drops[x].vel)/mass #acceleration a[2] = a[2]-g self.drops[x].pos += np.array(self.drops[x].vel)*dt +0.5*a*dt**2 self.drops[x].vel += a*dt #When droplet hits ground, it stops moving if self.drops[x].pos[2] < 0.0: self.drops[x].pos[2] = 0.0 self.drops[x].vel = [0.0, 0.0, 0.0]