I am trying to animate a set of 5 coupled ODEs with matplotlib depicting flow change in time.
My project is to determine how is pollutant advancing through the Great Lakes depending on the starting lake and different flow speeds. For this I have 5 ODEs. So far I've managed to make a model and get results for different flows and different starting positions. I'm currently stuck on animating those results. I made some animations but they are not working properly.
!https://imgur.com/a/1MVTLsY
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import matplotlib.animation as anim
""" Volume [m^3] """
V1 = 12004e9 # Superior
V2 = 3550e9 # Huron
V3 = 4860e9 # Michigan
V4 = 499e9 # Erie
V5 = 1656e9 # Ontario
""" Flows [m^3/s] """
r1 = 2.2e3*3.154e7 #->Superior
r2 = 1.6e3*3.154e7 #->Huron
r3 = 1.5e3*3.154e7 #->Michigan
r4 = 0.7e3*3.154e7 #->Erie
r5 = 1.1e3*3.154e7 #->Ontario
r6 = 5.3e3*3.154e7 #Huron->Erie
r7 = 6e3*3.154e7 #Erie->Ontario
r8 = 7.1e3*3.154e7 #Ontario->
r = np.array([r1,r2,r3,r4,r5,r6,r7,r8])
""" Time """
t=18000
T = np.linspace(0,t,t+1)
""" Initial Condition"""
x10 = 0.001*V1
S0 = np.array([x10,0,0,0,0])
def Sup(S,T,R):
dx1dt = -R[0]/V1*S[0]
dx2dt = R[0]/V1*S[0] + R[2]/V3*S[2] - R[5]/V2*S[1]
dx3dt = -R[2]/V3*S[2]
dx4dt = R[5]/V2*S[1] - R[6]/V4*S[3]
dx5dt = R[6]/V4*S[3] - R[7]/V5*S[4]
return np.array([dx1dt, dx2dt, dx3dt, dx4dt, dx5dt])
R = r #there are multiple flow variants used later in the code
S1 = odeint(Sup,S0,T,args=(R,))
SSuperior = S1[:,0]
SHuron = S1[:,1]
SMichigan = S1[:,2]
SErie = S1[:,3]
SOntario = S1[:,4]
""" Animation"""
fig = plt.figure()
ax = plt.axes(xlim = (-50, 8500))
line, = ax.plot([], [], animated = True, lw = 2)
def init():
line.set_data([],[])
ax.set_xlim(T.min(),T.max())
ax.set_ylim(SOntario.min(), SOntario.max())
return line,
def animate(i):
line.set_data(T,SOntario[i])
return line,
anim = anim.FuncAnimation(fig, animate, frames = 500, init_func = init, blit=True )
My first expected animation was just to animate the plotting, but all I can get is a horizontal line moving up and down. While this is partially right it is not the expected animation
Related
This is driving me crazy. I'm trying to create an animation of a heatmap where every frame shows a different time stamp. I have created the data structures, imported ffmpeg, and I tried another package too (celluloid that seems very good). I tried an example with a simple line plot and it does animate. The result is always the same: I get the last frame, not an animation. Here is my code in case anyone can spot the error... Thank you in advance.
------------- Code --------------
from matplotlib import animation, rc
from IPython.display import HTML
data2 = df[['ProducerName', 'MemOccupied']]
data2 = data2.sort_index().sort_values('ProducerName', kind='mergesort')
data2 = data2[['MemOccupied']]
fig = plt.figure()
camera = Camera(fig)
df_heatmap = pd.DataFrame(data2)
df_heatmap = df_heatmap.dropna()
df_heatmap = df_heatmap.resample('1T').mean()
count = df_heatmap.count()
index_series = df_heatmap.index
def init():
#plt.clf()
ax = sns.heatmap(data2[data2.index == index_series[1]], cbar_kws={'label': 'Memory Occupied (GB)'}, cmap='Blues_r')
ax.set(ylabel=None)
#return (ax,)
def animate(i):
plt.clf()
ax = sns.heatmap(data2[data2.index == index_series[i]], cbar_kws={'label': 'Memory Occupied (GB)'}, cmap='Blues_r')
ax.set(ylabel=None)
#return (ax,)
anim = animation.FuncAnimation(fig = fig, func = animate, init_func=init, frames=count-1,repeat = True, interval = 200)
HTML(anim.to_html5_video())
I pretty much deleted the last code and started new. I added a new class called Object which is the replacement for the lists called body_1 and body_2. Also all the calculations are now done from within the Object class. Most previous existing issues were resolved through this process but there is still one that presists. I believe its inside the StartVelocity() function which creates the v1/2 needed to start the Leapfrog algorithm. This should give me a geostationary Orbit but as clearly visible the Satelite escapes very quickly after zooming through earth.
Codes are:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from object import Object
import numpy as np
class Simulation:
def __init__(self):
# Index: 0 Name, 1 Position, 2 Velocity, 3 Mass
body_1 = Object("Earth", "g", "r",
np.array([[0.0], [0.0], [0.0]]),
np.array([[0.0], [0.0], [0.0]]),
5.9722 * 10**24)
body_2 = Object("Satelite", "b", "r",
np.array([[42164.0], [0.0], [0.0]]),
np.array([[0.0], [3075.4], [0.0]]),
5000.0)
self.bodies = [body_1, body_2]
def ComputePath(self, time_limit, time_step):
time_range = np.arange(0, time_limit, time_step)
for body in self.bodies:
body.StartVelocity(self.bodies, time_step)
for T in time_range:
for body in self.bodies:
body.Leapfrog(self.bodies, time_step)
def PlotObrit(self):
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for body in self.bodies:
body.ReshapePath()
X, Y, Z = [], [], []
for position in body.path:
X.append(position[0])
Y.append(position[1])
Z.append(position[2])
ax.plot(X, Y, Z, f"{body.linecolor}--")
for body in self.bodies:
last_pos = body.path[-1]
ax.plot(last_pos[0], last_pos[1], last_pos[2], f"{body.bodycolor}o", label=body.name)
ax.set_xlabel("x-Axis")
ax.set_ylabel("y-Axis")
ax.set_zlabel("z-Axis")
ax.legend()
fig.savefig("Leapfrog.png")
if __name__ == "__main__":
sim = Simulation()
sim.ComputePath(0.5, 0.01)
sim.PlotObrit()
import numpy as np
class Object:
def __init__(self, name, bodycolor, linecolor, pos_0, vel_0, mass):
self.name = name
self.bodycolor = bodycolor
self.linecolor = linecolor
self.position = pos_0
self.velocity = vel_0
self.mass = mass
self.path = []
def StartVelocity(self, other_bodies, time_step):
force = self.GetForce(other_bodies)
self.velocity += (force / self.mass) * time_step * 0.5
def Leapfrog(self, other_bodies, time_step):
self.position += self.velocity * time_step
self.velocity += (self.GetForce(other_bodies) / self.mass) * time_step
self.path.append(self.position.copy())
def GetForce(self, other_bodies):
force = 0
for other_body in other_bodies:
if other_body != self:
force += self.Force(other_body)
return force
def Force(self, other_body):
G = 6.673 * 10**-11
dis_vec = other_body.position - self.position
dis_mag = np.linalg.norm(dis_vec)
dir_vec = dis_vec / dis_mag
for_mag = G * (self.mass * other_body.mass) / dis_mag**2
for_vec = for_mag * dir_vec
return for_vec
def ReshapePath(self):
for index, position in enumerate(self.path):
self.path[index] = position.reshape(3).tolist()
Im aware that Body 2's position has to be multiplied by 1000 to get meters but it would just fly in a straight line if i would do that and there would be no signs of gravitational forces what so ever.
The constant G is in kg-m-sec units. The radius of the satellite orbit however only makes sense in km, else the orbit would be inside the Earth core. Then the speed in m/sec gives a near circular orbit with negligible eccentricity. (Code from a math.SE question on Kepler law quantities)
import math as m
G = 6.673e-11*1e-9 # km^3 s^-2 kg^-1
M_E = 5.9722e24 # kg
R_E = 6378.137 # km
R_sat = 42164.0 # km from Earth center
V_sat = 3075.4/1000 # km/s
theta = 0
r0 = R_sat
dotr0 = V_sat*m.sin(theta)
dotphi0 = -V_sat/r0*m.cos(theta)
R = (r0*V_sat*m.cos(theta))**2/(G*M_E)
wx = R/r0-1; wy = -dotr0*(R/(G*M_E))**0.5
E = (wx*wx+wy*wy)**0.5; psi = m.atan2(wy,wx)
T = m.pi/(G*M_E)**0.5*(R/(1-E*E))**1.5
print(f"orbit constants R={R} km, E={E}, psi={psi} rad")
print(f"above ground: min={R/(1+E)-R_E} km, max={R/(1-E)-R_E} km")
print(f"T={2*T} sec, {T/1800} h")
with output
orbit constants R=42192.12133271948 km, E=0.0006669512550867562, psi=-0.0 rad
above ground: min=35785.863 km, max=35842.14320159004 km
T=86258.0162673565 sec, 23.960560074265697 h
for r(phi)=R/(1+E*cos(phi-psi))
Implementing these changes in your code and calling with
sim.ComputePath(86e+3, 600.0)
gives a nice circular orbit
I am trying to generate an animation for a large data with a dynamic grid (ocean waves). I have managed to write a script that is functional but it is time and resource consuming. I was hoping if anyone could see what i can improve in my code to help speed it up.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
from matplotlib import animation as anim
import xarray as xr
import PySimpleGUI as sg
import sys
#file importing mechanism
jan = xr.open_dataset('Model/Bar_mig/xboutput_equi.nc')
########################## labeling the variables in the data-set ##############
nx = jan.variables['globalx']
globaltime = jan.variables['globaltime']
zb = jan.variables['zb'][:,0,:]
zs = jan.variables['zs'][:,0,:]
ccz = jan.variables['ccz'][:,:,0,:]
uz = jan.variables['uz'][:,:,0,:]
nz = np.array(range(0,100))
nx = (np.array(range(0,nx.size)))
globaltime_ar = np.array(globaltime)
conc = np.vstack(globaltime_ar)
newdf = pd.DataFrame(conc)
itr = len(newdf.index)
uz1=np.flip(uz,0)
uz2=np.flip(np.flip(uz1,1),axis=0)
depth1 = (zb)
a = np.array(depth1)
b = pd.DataFrame(a)
depth = b.dropna(axis=1, how='all')
zba1 = (np.array(zb))
zsa1 = (np.array(-zs))
zba = pd.DataFrame(zba1)
zsa = pd.DataFrame(zsa1)
This is how i am setting up the dynamic grid. ( example of the the output)
#dynamic grid
for w in range(0,itr):
AA=[]
sizer = depth.iloc[w,]
sizer1 = sizer.dropna(axis=0, how='all')
for j in range(0,sizer1.size):
maxi = -zsa.iloc[w,j]
mini = depth.iloc[w,j]
step = mini/nz.shape[-1]
globals()['col_{}'.format(j)] = pd.DataFrame(np.linspace(maxi,mini,nz.shape[-1],endpoint=True))
globals()['col_{}'.format(j)] = globals()['col_{}'.format(j)].reset_index(drop=True)
AA.append(globals()['col_{}'.format(j)])
globals()['df_{}'.format(w)] = pd.concat(AA, axis=1).iloc[:nz.size]
globals()['df_{}'.format(w)].columns = range(globals()['df_{}'.format(w)].shape[1])
AA.clear()
sg.OneLineProgressMeter('My meter title', w, itr-1, 'key')
from matplotlib import animation as anim
fig = plt.figure(figsize=(15,7.5)) # Create a dummy figure
ax = plt.axes() # Set the axis rigid
mywriter = anim.FFMpegWriter()
scale=1
def animate(w):
w = w*scale
plt.clf()
plt.title(str(w) + 'hr')
y = globals()['df_{}'.format(w)]
x = np.array([nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx,nx])
data2 = np.flip(ccz[w*1,:,:],0)
cont = plt.pcolor(x,y,np.array(data2), cmap = 'jet',
vmin = 0, vmax = 0.03
)
plt.colorbar(label='Concentration Profile ($m^3/m^3$)')
plt.fill_between(nx,min(zb[0])-1,zb[w],color = 'yellow')
point = 350
plt.xlim(point,nx.shape[-1])
plt.ylim(min(zb[0,point:point+1]),max(zb[0,point:]))
plt.xlabel('Cross shore distance (m)')
plt.ylabel('Depth (m)')
fig.tight_layout()
return cont,
ani = anim.FuncAnimation(fig, animate, interval = 1, frames=itr)
ani.save('Sed_Con.mp4', writer=mywriter)
The code below generates a animated basemap, but not exactly the one I want: I want the scatterplot from the previous frame to disappear, but it persists through the remainder of the animation.
I suspect it has something to do with my not understanding what the basemap really is. I understand calling it on lat/lons to project them to x/y, but I don't entirely get what's going on when I call event_map.scatter().
import random
import os
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import animation
import pandas as pd
from IPython.display import HTML
# Enables animation display directly in IPython
#(http://jakevdp.github.io/blog/2013/05/12/embedding-matplotlib-animations/)
from tempfile import NamedTemporaryFile
VIDEO_TAG = """<video controls>
<source src="data:video/x-m4v;base64,{0}" type="video/mp4">
Your browser does not support the video tag.
</video>"""
def anim_to_html(anim):
if not hasattr(anim, '_encoded_video'):
with NamedTemporaryFile(suffix='.mp4') as f:
anim.save(f.name, fps=20, extra_args=['-vcodec', 'libx264'])
video = open(f.name, "rb").read()
anim._encoded_video = video.encode("base64")
return VIDEO_TAG.format(anim._encoded_video)
def display_animation(anim):
plt.close(anim._fig)
return HTML(anim_to_html(anim))
animation.Animation._repr_html_ = anim_to_html
FRAMES = 20
POINTS_PER_FRAME = 30
LAT_MIN = 40.5
LAT_MAX = 40.95
LON_MIN = -74.15
LON_MAX = -73.85
FIGSIZE = (10,10)
MAP_BACKGROUND = '.95'
MARKERSIZE = 20
#Make Sample Data
data_frames = {}
for i in range(FRAMES):
lats = [random.uniform(LAT_MIN, LAT_MAX) for x in range(POINTS_PER_FRAME)]
lons = [random.uniform(LON_MIN, LON_MAX) for x in range(POINTS_PER_FRAME)]
data_frames[i] = pd.DataFrame({'lat':lats, 'lon':lons})
class AnimatedMap(object):
""" An animated scatter plot over a basemap"""
def __init__(self, data_frames):
self.dfs = data_frames
self.fig = plt.figure(figsize=FIGSIZE)
self.event_map = Basemap(projection='merc',
resolution='i', area_thresh=1.0, # Medium resolution
lat_0 = (LAT_MIN + LAT_MAX)/2, lon_0=(LON_MIN + LON_MAX)/2, # Map center
llcrnrlon=LON_MIN, llcrnrlat=LAT_MIN, # Lower left corner
urcrnrlon=LON_MAX, urcrnrlat=LAT_MAX) # Upper right corner
self.ani = animation.FuncAnimation(self.fig, self.update, frames=FRAMES, interval=1000,
init_func=self.setup_plot, blit=True,
repeat=False)
def setup_plot(self):
self.event_map.drawcoastlines()
self.event_map.drawcounties()
self.event_map.fillcontinents(color=MAP_BACKGROUND) # Light gray
self.event_map.drawmapboundary()
self.scat = self.event_map.scatter(x = [], y=[], s=MARKERSIZE,marker='o', zorder=10)
return self.scat
def project_lat_lons(self, i):
df = data_frames[i]
x, y = self.event_map(df.lon.values, df.lat.values)
x_y = pd.DataFrame({'x': x, 'y': y}, index=df.index)
df = df.join(x_y)
return df
def update(self, i):
"""Update the scatter plot."""
df = self.project_lat_lons(i)
self.scat = self.event_map.scatter(x = df.x.values, y=df.y.values, marker='o', zorder=10)
return self.scat,
s = AnimatedMap(data_frames)
s.ani
It looks like you're simply adding a new scatter plot at each update. What you should do instead is change the data in the existing path collection at each update. Try something along the lines of
def update(self, i):
"""Update the scatter plot."""
df = self.project_lat_lons(i)
new_offsets = np.vstack([df.x.values, df.y.values]).T
self.scat.set_offsets(new_offsets)
return self.scat,
Note that I haven't tested this.
I'm trying to make an animation with a sequence of datafiles in mayavi. Unfortunately i have noticed that camera doesn't lock (it is zooming and zooming out). I think it is happening because the Z componrnt of my mesh is changing and mayavi is trying to recalculate scales.
How can I fix it?
import numpy
from mayavi import mlab
mlab.figure(size = (1024,768),bgcolor = (1,1,1))
mlab.view(azimuth=45, elevation=60, distance=0.01, focalpoint=(0,0,0))
#mlab.move(forward=23, right=32, up=12)
for i in range(8240,8243):
n=numpy.arange(10,400,20)
k=numpy.arange(10,400,20)
[x,y] = numpy.meshgrid(k,n)
z=numpy.zeros((20,20))
z[:] = 5
M = numpy.loadtxt('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\1disk_j9.5xyz\\'+'{0:05}'.format(i)+'.txt')
Mx = M[:,0]; My = M[:,1]; Mz = M[:,2]
Mx = Mx.reshape(20,20); My = My.reshape(20,20); Mz = Mz.reshape(20,20);
s = mlab.quiver3d(x,y,z,Mx, My, -Mz, mode="cone",resolution=40,scale_factor=0.016,color = (0.8,0.8,0.01))
Mz = numpy.loadtxt('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\Mzi\\' + '{0:05}'.format(i) + '.txt')
n=numpy.arange(2.5,400,2)
k=numpy.arange(2.5,400,2)
[x,y] = numpy.meshgrid(k,n)
f = mlab.mesh(x, y, -Mz/1.5,representation = 'wireframe',opacity=0.3,line_width=1)
mlab.savefig('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\figs\\'+'{0:05}'.format(i)+'.png')
mlab.clf()
#mlab.savefig('B:\\Dropbox\\Master.Diploma\\figures\\vortex.png')
print(i)
mlab.show()
for anyone still interested in this, you could try wrapping whatever work you're doing in this context, which will disable rendering and return the disable_render value and camera views to their original states after the context exits.
with constant_camera_view():
do_stuff()
Here's the class:
class constant_camera_view(object):
def __init__(self):
pass
def __enter__(self):
self.orig_no_render = mlab.gcf().scene.disable_render
if not self.orig_no_render:
mlab.gcf().scene.disable_render = True
cc = mlab.gcf().scene.camera
self.orig_pos = cc.position
self.orig_fp = cc.focal_point
self.orig_view_angle = cc.view_angle
self.orig_view_up = cc.view_up
self.orig_clipping_range = cc.clipping_range
def __exit__(self, t, val, trace):
cc = mlab.gcf().scene.camera
cc.position = self.orig_pos
cc.focal_point = self.orig_fp
cc.view_angle = self.orig_view_angle
cc.view_up = self.orig_view_up
cc.clipping_range = self.orig_clipping_range
if not self.orig_no_render:
mlab.gcf().scene.disable_render = False
if t != None:
print t, val, trace
ipdb.post_mortem(trace)
I do not really see the problem in your plot but to reset the view after each plotting instance insert your view point:
mlab.view(azimuth=45, elevation=60, distance=0.01, focalpoint=(0,0,0))
directly above your mlab.savefig callwithin your for loop .
You could just use the vmin and vmax function in your mesh command, if u do so the scale will not change with your data and your camera should stay where it is.
Like this:
f = mlab.mesh(x, y, -Mz/1.5,representation = 'wireframe',vmin='''some value''',vmax='''some value''',opacity=0.3,line_width=1)