Matplotlib polar does not plot as expected - python

I'm trying to insert a polar plot "version" of a cartesian plot. Here's what it should look like (according to a similar program done in Matlab):
And here's enough of the code to show the weirdness I'm getting:
from scipy import pi, sin, cos, log
from numpy import radians as rad
from numpy import log10, linspace
from matplotlib import pyplot as plt
############## Inputs ##############
#Beamwidth, in degrees
BW = 5
############## Constants for calculations ##############
# 0 = uniform/sin, 1 = cos, 2 = cos^2, etc
#Peak Pattern break points, from Table 3
p0, p1, p2, p3, p4 = -5.75, -14.4, -22.3, -31.5, -39.4
#Average pattern break points, from Table 3
a0, a1 ,a2, a3, a4 = -12.16, -20.6, -29, -37.6, -42.5
#Constant added to peak pattern to convert it to average, from Table 3
c0, c1, c2, c3, c4 = -3.72, -4.32, -4.6, -4.2, -2.61
#Mask floor levels, from Table 3
floor0, floor1, floor2, floor3, floor4 = -30, -50, -60, -70, -80
############## Calculations ##############
#Lists for plotting purposes
u_x = list(linspace(0,90,500))
u0_norm_y = list()
u0_peak_y = list()
u0_avg_y = list()
##Calculations start
for ang in u_x:
########## Uniform
u0 = pi * 50.8 * sin(rad(ang)) / BW
def u0_norm(ang):
if ang == 0:
return 0
else:
return 20 * log10(abs(sin(u0) / u0))
def u0_peak(ang, u0_norm):
if ang == 0:
return 0
elif u0_norm(ang) > p0:
return u0_norm(ang)
elif -8.584 * log(2.876 * ang / BW) > floor0:
return -8.584 * log(2.876 * ang / BW)
else:
return floor0
def u0_avg(ang, u0_norm):
if ang == 0:
return 0
elif u0_norm(ang) > a0:
return u0_norm(ang)
elif -8.584 * log(2.876 * ang / BW) + c0 > floor0:
return -8.584 * log(2.876 * ang / BW) + c0
else:
return floor0
u0_peak_y.append(u0_peak(ang, u0_norm))
u0_norm_y.append(u0_norm(ang))
u0_avg_y.append(u0_avg(ang, u0_norm))
############## Plots ##############
#Uniform
fig1 = plt.figure()
ax1 = plt.subplot(121)
ax2 = plt.subplot(122, polar = True)
ax1.plot(u_x, u0_norm_y, label= "Normalized Pattern")
ax1.plot(u_x, u0_peak_y, label= "Peak")
ax1.plot(u_x, u0_avg_y, label= "Average")
ax1.set_title("Uniform Pattern")
ax1.set_xlabel("Angle (degrees)")
ax1.set_ylabel("Normalized Antenna Pattern (dB)")
ax2.set_theta_zero_location("N")
ax2.set_theta_direction(-1)
ax2.plot(u_x, u0_norm_y, label= "Normalized Pattern")
ax2.plot(u_x, u0_peak_y, label= "Peak")
ax2.plot(u_x, u0_avg_y, label= "Average")
ax2.set_thetamin(0)
ax2.set_thetamax(90)
ax1.grid(True)
plt.tight_layout()
plt.subplots_adjust(wspace = 0.4)
plt.show()
Obviously, something has gone terribly wrong. My plot is an actual mess. I'm assuming something about the way polar plots are created vs cartesian plots, code-wise, is very different, but I haven't been able to find any real detail about this.

You are mixing up radians and degrees (the intuition of ImportanceOfBeingErnest was correct).
Remove the conversion to radians you do when defining u0 and convert to radians directly the input u_x.
from scipy import pi, sin, cos, log
from numpy import radians as rad
from numpy import log10, linspace
from matplotlib import pyplot as plt
############## Inputs ##############
#Beamwidth, in degrees
BW = 5
############## Constants for calculations ##############
# 0 = uniform/sin, 1 = cos, 2 = cos^2, etc
#Peak Pattern break points, from Table 3
p0, p1, p2, p3, p4 = -5.75, -14.4, -22.3, -31.5, -39.4
#Average pattern break points, from Table 3
a0, a1 ,a2, a3, a4 = -12.16, -20.6, -29, -37.6, -42.5
#Constant added to peak pattern to convert it to average, from Table 3
c0, c1, c2, c3, c4 = -3.72, -4.32, -4.6, -4.2, -2.61
#Mask floor levels, from Table 3
floor0, floor1, floor2, floor3, floor4 = -30, -50, -60, -70, -80
############## Calculations ##############
#Lists for plotting purposes
u_x = list(linspace(0,rad(90),500))
u0_norm_y = list()
u0_peak_y = list()
u0_avg_y = list()
##Calculations start
for ang in u_x:
########## Uniform
u0 = pi * 50.8 * sin((ang)) / BW
def u0_norm(ang):
if ang == 0:
return 0
else:
return 20 * log10(abs(sin(u0) / u0))
def u0_peak(ang, u0_norm):
if ang == 0:
return 0
elif u0_norm(ang) > p0:
return u0_norm(ang)
elif -8.584 * log(2.876 * ang / BW) > floor0:
return -8.584 * log(2.876 * ang / BW)
else:
return floor0
def u0_avg(ang, u0_norm):
if ang == 0:
return 0
elif u0_norm(ang) > a0:
return u0_norm(ang)
elif -8.584 * log(2.876 * ang / BW) + c0 > floor0:
return -8.584 * log(2.876 * ang / BW) + c0
else:
return floor0
u0_peak_y.append(u0_peak(ang, u0_norm))
u0_norm_y.append(u0_norm(ang))
u0_avg_y.append(u0_avg(ang, u0_norm))
############## Plots ##############
#Uniform
fig1 = plt.figure()
ax1 = plt.subplot(121)
ax2 = plt.subplot(122, polar = True)
ax1.plot(u_x, u0_norm_y, label= "Normalized Pattern")
ax1.plot(u_x, u0_peak_y, label= "Peak")
ax1.plot(u_x, u0_avg_y, label= "Average")
ax1.set_title("Uniform Pattern")
ax1.set_xlabel("Angle (degrees)")
ax1.set_ylabel("Normalized Antenna Pattern (dB)")
ax2.set_theta_zero_location("N")
ax2.set_theta_direction(-1)
ax2.plot(u_x, u0_norm_y, label= "Normalized Pattern")
ax2.plot(u_x, u0_peak_y, label= "Peak")
ax2.plot(u_x, u0_avg_y, label= "Average")
ax2.set_thetamin(0)
ax2.set_thetamax(90)
ax1.grid(True)
plt.tight_layout()
plt.subplots_adjust(wspace = 0.4)
plt.show()
Here is what I think is a better version of the same code, with little edits.
from scipy import pi, sin, cos, log
from numpy import radians as rad
from numpy import log10, linspace
from matplotlib import pyplot as plt
############## Inputs ##############
#Beamwidth, in degrees
BW = 5
############## Constants for calculations ##############
# 0 = uniform/sin, 1 = cos, 2 = cos^2, etc
#Peak Pattern break points, from Table 3
p0, p1, p2, p3, p4 = -5.75, -14.4, -22.3, -31.5, -39.4
#Average pattern break points, from Table 3
a0, a1 ,a2, a3, a4 = -12.16, -20.6, -29, -37.6, -42.5
#Constant added to peak pattern to convert it to average, from Table 3
c0, c1, c2, c3, c4 = -3.72, -4.32, -4.6, -4.2, -2.61
#Mask floor levels, from Table 3
floor0, floor1, floor2, floor3, floor4 = -30, -50, -60, -70, -80
############## Calculations ##############
#Lists for plotting purposes
u_x = list(linspace(0,rad(90),500))
u0_norm_y = list()
u0_peak_y = list()
u0_avg_y = list()
##Function definition
def u0_norm(ang, u0):
if ang == 0:
return 0
else:
return 20 * log10(abs(sin(u0) / u0))
def u0_peak(ang, u0):
if ang == 0:
return 0
elif u0_norm(ang, u0) > p0:
return u0_norm(ang, u0)
elif -8.584 * log(2.876 * ang / BW) > floor0:
return -8.584 * log(2.876 * ang / BW)
else:
return floor0
def u0_avg(ang, u0):
if ang == 0:
return 0
elif u0_norm(ang, u0) > a0:
return u0_norm(ang, u0)
elif -8.584 * log(2.876 * ang / BW) + c0 > floor0:
return -8.584 * log(2.876 * ang / BW) + c0
else:
return floor0
for ang in u_x:
########## Uniform
u0 = pi * 50.8 * sin(ang) / BW
u0_peak_y.append(u0_peak(ang, u0))
u0_norm_y.append(u0_norm(ang, u0))
u0_avg_y.append(u0_avg(ang, u0))
############## Plots ##############
#Uniform
fig1 = plt.figure()
ax1 = plt.subplot(121)
ax2 = plt.subplot(122, polar = True)
ax1.plot(u_x, u0_norm_y, label= "Normalized Pattern")
ax1.plot(u_x, u0_peak_y, label= "Peak")
ax1.plot(u_x, u0_avg_y, label= "Average")
ax1.set_title("Uniform Pattern")
ax1.set_xlabel("Angle (radians)")
ax1.set_ylabel("Normalized Antenna Pattern (dB)")
ax2.set_theta_zero_location("N")
ax2.set_theta_direction(-1)
ax2.plot(u_x, u0_norm_y, label= "Normalized Pattern")
ax2.plot(u_x, u0_peak_y, label= "Peak")
ax2.plot(u_x, u0_avg_y, label= "Average")
ax2.set_thetamin(0)
ax2.set_thetamax(90)
ax1.grid(True)
plt.tight_layout()
plt.subplots_adjust(wspace = 0.4)
plt.show()

Related

chart_pie index to generate

I need help to generate this graph, especially with domains limits
and the arrow indicating the domain
all I can do is generate the domains name but not the limits and arrow
The following code will produce something like what you require:
from matplotlib import pyplot as plt
from matplotlib.patches import Wedge
import numpy as np
labels = ["Obésité\nmassive", "Obésité", "Surpoids", "Normal", "Maigreur"]
innerlabels = [">40", "30 à 40", "25 à 30", "18,5 à 25", "< 18,5"]
colours = ["red", "darkorange", "orange", "green", "blue"]
fig, ax = plt.subplots(figsize=(6, 6), dpi=200)
theta = 0
dtheta = 180 / len(labels)
width = 0.35
def pol2cart(rho, phi):
x = rho * np.cos(phi)
y = rho * np.sin(phi)
return(x, y)
patches = []
for i in range(len(labels)):
# outer wedge
wedge = Wedge(0, r=1, width=width, theta1=theta, theta2=(theta + dtheta), fc=colours[i], alpha=0.6, edgecolor="whitesmoke")
ax.add_patch(wedge)
# inner wedge
wedge = Wedge(0, r=1 - width, width=width, theta1=theta, theta2=(theta + dtheta), fc=colours[i], edgecolor="whitesmoke")
ax.add_patch(wedge)
theta += dtheta
# add text label
tr = 1 - (width / 2)
ta = theta - dtheta / 2
x, y = pol2cart(tr, np.deg2rad(ta))
textangle = -np.fmod(90 - ta, 180)
ax.text(x, y, labels[i], rotation=textangle, va="center", ha="center", color="white", fontweight="bold")
# inner labels
tr = (1 - width) - (width / 2)
x, y = pol2cart(tr, np.deg2rad(ta))
textangle = -np.fmod(90 - ta, 180)
ax.text(x, y, innerlabels[i], rotation=textangle, va="center", ha="center", color="white")
ax.set_xlim([-1, 1])
ax.set_ylim([0, 1])
ax.set_axis_off()
ax.set_aspect("equal")
def bmiposition(bmi):
"""
Get angular position of BMI arrow.
"""
from scipy.interpolate import interp1d
bmiranges = [(0, 18.5), (18.5, 25), (25, 30), (30, 40), (40, 80)]
angrange = [(180 - dtheta * i, 180 - dtheta * (i + 1)) for i in range(len(bmiranges))]
interpfuncs = []
for i in range(len(bmiranges)):
interpfuncs.append(interp1d(bmiranges[i], angrange[i], kind="linear"))
bmiang = np.piecewise(
bmi,
[bmiranges[i][0] < bmi <= bmiranges[i][1] for i in range(len(bmiranges))],
interpfuncs,
)
return bmiang
bmi = 22.5 # set BMI
# add arrow
pos = bmiposition(bmi) # get BMI angle
x, y = pol2cart(0.25, np.deg2rad(pos))
ax.arrow(0, 0, x, y, head_length=0.125, width=0.025, fc="k")
ax.plot(0, 0, 'ko', ms=10) # circle at origin
giving:

Unable to label inside ring pie chart

The following code :
import matplotlib.pyplot as plt
import numpy as np
def autopct_format(values):
def my_format(pct):
total = sum(values)
val = int(round(pct*total/100.0)/1000000)
if val == 0:
return ""
else:
return '{:.1f}%\n({v:,d})'.format(pct, v=val)
return my_format
fig, ax = plt.subplots(1,1,figsize=(10,10),dpi=100,layout="constrained")
ax.axis('equal')
width = 0.3
#Color
A, B, C=[plt.cm.Blues, plt.cm.Reds, plt.cm.Greens]
#OUTSIDE
cin = [A(0.5),A(0.4),A(0.3),B(0.5),B(0.4),B(0.3),B(0.2),B(0.1), C(0.5),C(0.4),C(0.3)]
Labels_Smalls = ['groupA', 'groupB', 'groupC']
labels = ['A.1', 'A.2', 'A.3', 'B.1', 'B.2', 'C.1', 'C.2', 'C.3',
'C.4', 'C.5']
Sizes_Detail = [4,3,5,6,5,10,5,5,4,6]
Sizes = [12,11,30]
pie2, _ ,junk = ax.pie(Sizes_Detail ,radius=1,
labels=labels,labeldistance=0.85,
autopct=autopct_format(Sizes_Detail) ,pctdistance = 1.15,
colors=cin)
for ea, eb in zip(pie2, _):
mang =(ea.theta1 + ea.theta2)/2
tourner = 360 - mang
eb.set_rotation(mang+tourner) # rotate the label by (mean_angle + 270)
eb.set_va("center")
eb.set_ha("center")
plt.setp(pie2, width=width, edgecolor='white')
#INSIDE
pie, _, junk = ax.pie(Sizes, radius=1-width,
autopct=autopct_format(Sizes) ,pctdistance = 0.8,
colors = [A(0.6), B(0.6), C(0.6)])
plt.setp(pie, width=width, edgecolor='white')
plt.margins(0,0)
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
kw = dict(arrowprops=dict(arrowstyle="-"),
bbox=bbox_props, zorder=3, va="center")
for i, p in enumerate(pie):
ang = (p.theta2 - p.theta1)/2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
connectionstyle = "angle,angleA=0,angleB={}".format(ang)
kw["arrowprops"].update({"connectionstyle": connectionstyle})
ax.annotate(Labels_Smalls[i], xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
horizontalalignment=horizontalalignment, **kw)
for ea, eb in zip(pie, _):
mang =(ea.theta1 + ea.theta2)/2. # get mean_angle of the wedge
#print(mang, eb.get_rotation())
tourner = 360 - mang
eb.set_rotation(mang+tourner) # rotate the label by (mean_angle + 270)
eb.set_va("center")
eb.set_ha("center")
gives the following output :
But It does only link the outside pie and not the inside one, so how would I do the same chart but with the arrow linking the inside pie to the bbox ?

How to show image with lines plotted over the image on tkinter window?

This program reads an image which is in location C:/Square.png and lines are plotted over it. The plot title is also defined. I want to show this whole image in tkinter window. How do I do it?
This is the image. The name has to be changed and we can run the code.
https://imgur.com/RkV02yY
import math
import matplotlib.pyplot as plt
def plot_output(opt_w, opt_h, n_x, n_y):
y_start, y_end = 100, 425
x_start, x_end = 25, 400
img = plt.imread("C:/Square.png") #Please change the location
fig, ax = plt.subplots(figsize=(10, 10))
plt.axis('off')
ax.imshow(img)
x_interval = (x_end - x_start)/n_x*2
h_x = range(x_start, x_end, 5)
for i in range(0,int(n_y)):
if i != 0:
ax.plot(h_x, [y_start + (y_end-y_start)/n_y*i]*len(h_x), '--', linewidth=5, color='firebrick')
plt.title(str(int(n_x*n_y)) + ' ABCD\n'+'TYUI:'+str(opt_w)+', Yummy:'+str(opt_h))
def get_get(min_w, min_h, max_w, max_h, PL, PH, min_t, max_t, cost_m, cost_a):
x = 1
if max_w < PL:
x = math.ceil(PL / max_w)
cost_rest = cost_m * PL * PH * (max_t + min_t) / 2 + cost_a * PH * x
cost_y = float("inf")
y = None
if min_h == 0:
min_h = 1
for i in range(math.ceil(PH / max_h), math.floor(PH / min_h)+1):
tmp_cost = cost_m * PL * PH * (max_t - min_t) / 2 / i + cost_a * PL * i
if tmp_cost < cost_y:
cost_y = tmp_cost
y = i
opt_w, opt_h, opt_cost = PL/x, PH/y, cost_rest + cost_y
plot_output(opt_w, opt_h, x, y)
return opt_w, opt_h, opt_cost
PL=30
PH=10
min_t=0.1
max_t=0.3
cost_m=0.1
cost_a=0.1
min_w=0.5
min_h=0.5
max_w=4
max_h=3
get_get(min_w, min_h, max_w, max_h, PL, PH, min_t, max_t, cost_m, cost_a)
You need to add plt.show()
import math
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("Tkagg")
def plot_output(opt_w, opt_h, n_x, n_y):
y_start, y_end = 100, 425
x_start, x_end = 25, 400
img = plt.imread("C:/Square.png") #Please change the location
fig, ax = plt.subplots(figsize=(10, 10))
plt.axis('off')
ax.imshow(img)
x_interval = (x_end - x_start)/n_x*2
h_x = range(x_start, x_end, 5)
for i in range(0,int(n_y)):
if i != 0:
ax.plot(h_x, [y_start + (y_end-y_start)/n_y*i]*len(h_x), '--', linewidth=5, color='firebrick')
plt.title(str(int(n_x*n_y)) + ' ABCD\n'+'TYUI:'+str(opt_w)+', Yummy:'+str(opt_h))
plt.show()
def get_get(min_w, min_h, max_w, max_h, PL, PH, min_t, max_t, cost_m, cost_a):
x = 1
if max_w < PL:
x = math.ceil(PL / max_w)
cost_rest = cost_m * PL * PH * (max_t + min_t) / 2 + cost_a * PH * x
cost_y = float("inf")
y = None
if min_h == 0:
min_h = 1
for i in range(math.ceil(PH / max_h), math.floor(PH / min_h)+1):
tmp_cost = cost_m * PL * PH * (max_t - min_t) / 2 / i + cost_a * PL * i
if tmp_cost < cost_y:
cost_y = tmp_cost
y = i
opt_w, opt_h, opt_cost = PL/x, PH/y, cost_rest + cost_y
plot_output(opt_w, opt_h, x, y)
return opt_w, opt_h, opt_cost
PL=30
PH=10
min_t=0.1
max_t=0.3
cost_m=0.1
cost_a=0.1
min_w=0.5
min_h=0.5
max_w=4
max_h=3
get_get(min_w, min_h, max_w, max_h, PL, PH, min_t, max_t, cost_m, cost_a)
Edit: I forgot to change the backend to tkinter

My circles are not matching up with my planets orbits

My planets orbits are not matching up with my circular paths im plotting of the orbits, I have matched in my for loop the distance for every planet with the distance they are from the center.(image is attached)
from pylab import *
from matplotlib.animation import *
Here is where I am defining the circes I will plot to show the paths of the orbits at the "def circle..."
def circle(x0,y0,R,N) :
''' create circle, given center x0,y0, input radius R,
and number of points N, then
output arrays of x and y coordinates '''
theta = linspace(0.0,2.0*pi,N)
x = R * cos(theta) + x0
y = R * sin(theta) + y0
return x,y
a= array([.39,.72,1,1.52,5.20,9.54,19.2,30.1]) #astronomical units of #the planets in order of the planets(i.e mercury, venus, earth,mars...)
period = a**(3.0/2.0)
I am taking the distane of the planets and inputting them in array, to be able to use a for loop to graph the circles.
#distance of the planets
d= array([390,790,980, 1520,5200,9540,19200,30100])#same order of the #planets as well
#attributes of the sun
x0 =0
y0 = 0
r_s=70*1.2#actual earth radius is 695e6
#radius of the planets
r_Ear = 63.781#e in m {for all the planets}
r_Merc= 24
r_Ven = 60
r_Mars= 33.9
r_Jup = 700
r_Sat = 582
r_Ura = 253
r_Nep = 246
the actual distance of the planets
#Distance of the planets
d_Ear = 1000#152
d_Merc= 390#70
d_Ven = 790#109
d_Mars= 1520#249
d_Jup = 5200#816
d_Sat = 9540#1514
d_Ura = 19200#3003
d_Nep = 30100#4545
fig = plt.figure()
ax = plt.axes(xlim=(-1e4-5000, 1e4+5000), ylim=(-1e4-5000, 1e4+5000), aspect=True)
This is where I am looping over, to plot 8 circles. Theres a link here to see the plot I have generated.
for i in range(8) :
x, y = circle(0.0,0.0,d[i],10000) # orbit
plot(x,y,':k')
[enter image description here][1]
The rest are the patches of the planets and the FuncAnimation.
Sun = plt.Circle((x0, y0), radius=r_s, ec='yellow', fc='yellow', lw=3)
Mercury = plt.Circle((0, 0), radius=r_Merc, ec='brown', fc='brown', lw=3)
Venus = plt.Circle((0, 0), radius=r_Ven, ec='brown', fc='brown', lw=3)
Earth = plt.Circle((0, 0), radius=r_Ear, ec='black', fc='black', lw=3)
Mars = plt.Circle((0, 0), radius=r_Mars, ec='brown', fc='brown', lw=3)
Jupiter = plt.Circle((0, 0), radius=r_Jup, ec='green', fc='green', lw=3)
Saturn = plt.Circle((0, 0), radius=r_Sat, ec='green', fc='green', lw=3)
Uranus = plt.Circle((0, 0), radius=r_Ura, ec='green', fc='green', lw=3)
Neptune = plt.Circle((0, 0), radius=r_Nep, ec='green', fc='green', lw=3)
ax.add_patch(Sun)
def init():
ax.add_patch(Earth)
ax.add_patch(Mercury)
ax.add_patch(Venus)
ax.add_patch(Mars)
ax.add_patch(Jupiter)
ax.add_patch(Saturn)
ax.add_patch(Uranus)
ax.add_patch(Neptune)
return Mercury, Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,
def animate(i):
theta = radians(i)
mx = d_Merc*np.cos(theta/period[0]) - d_Merc*np.sin(theta/period[0])
my = d_Merc*np.sin(theta/period[0]) + d_Merc*np.cos(theta/period[0])
vx = d_Ven *np.cos(theta/period[1]) - d_Ven*np.sin(theta/period[1])
vy = d_Ven *np.cos(theta/period[1]) + d_Ven*np.sin(theta/period[1])
ex = d_Ear*np.cos(theta/period[2]) - d_Ear*np.sin(theta/period[2])
ey = d_Ear*np.sin(theta/period[2]) + d_Ear*np.cos(theta/period[2])
Mx = d_Mars*np.cos(theta/period[3]) - d_Mars*np.sin(theta/period[3])
My = d_Mars*np.sin(theta/period[3]) + d_Mars*np.cos(theta/period[3])
Jx = d_Jup*np.cos(theta/period[4]) - d_Jup*np.sin(theta/period[4])
Jy = d_Jup*np.sin(theta/period[4]) + d_Jup*np.cos(theta/period[4])
Sx = d_Sat*np.cos(theta/period[5]) - d_Sat*np.sin(theta/period[5])
Sy = d_Sat*np.sin(theta/period[5]) + d_Sat*np.cos(theta/period[5])
Ux = d_Ura*np.cos(theta/period[6]) - d_Ura*np.sin(theta/period[6])
Uy = d_Ura*np.sin(theta/period[6]) + d_Ura*np.cos(theta/period[6])
Nx = d_Nep*np.cos(theta/period[7]) - d_Nep*np.sin(theta/period[7])
Ny = d_Nep*np.sin(theta/period[7]) + d_Nep*np.cos(theta/period[7])
Mercury.center = (mx, my)
Mercury._angle = i
Venus.center = (vx, vy)
Venus._angle = i
Earth.center = (ex, ey)
Earth._angle = i
Mars.center = (Mx, My)
Mars.angle =i
Jupiter.center = (Jx, Jy)
Jupiter._angle = i
Saturn.center = (Sx, Sy)
Saturn._angle = i
Uranus.center = (Ux, Uy)
Uranus.angle = i
Neptune.center = (Nx, Ny)
Neptune._angle = i
return Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune,
anim = FuncAnimation(fig, animate, init_func=init, frames=1080,
interval=25, blit=True)
plt.show()
I'm not following your math on this:
mx = d_Merc*np.cos(theta/period[0]) - d_Merc*np.sin(theta/period[0])
my = d_Merc*np.sin(theta/period[0]) + d_Merc*np.cos(theta/period[0])
You'll be more correct if you change that to this:
mx = d_Merc*np.cos(theta/period[0])
my = d_Merc*np.sin(theta/period[0])
And even more correct if you take the size of the planet into account
mx = (d_Merc+(r_Merc/2))*np.cos(theta/period[0])
my = (d_Merc+(r_Merc/2))*np.sin(theta/period[0])
That will fix your basic issue I think.
Beyond that:
d_Ear differs from Earth's value in the d array. 980 vs. 1000.
vy = d_Ven *np.cos(theta/period[1]) is not correct. That needs
to be vy = d_Ven *np.sin(theta/period[1])
As far as the code goes, you may consider using a few dictionaries to
avoid repetition like with your d array.

FuncAnimation with multiple particles

Below is a model that I have been working on for a few weeks now, slowly adding more complexity as I learn how to code (coding newbie). If it is not clear, I am trying to create a model of particles of 2 different densities that settle according to stokes settling velocity (as a function of concentration). I am having trouble getting the animation to work with the second particle (working code with one animated particle at the bottom). I have broken out the variables for the two different particles in an attempt to debug the code, but have not had any luck determining what I am doing wrong.
Any help would be greatly appreciated!
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 = 100
n2 = 50
sigma = 0.01
sigma2 = 0.01
pp = 2.56 # pp = particle density (Sphene=3.53) #(Feldspar=2.56)
#(g/cm^3)
pp2 = 3.53
pf = 2.7 # pf = fluid density(g/cm^3)
pf2 = 2.7
g = 9.8 # g = gravity (m/s^2)
g2 = 9.8
r = 0.003 # r = radius of sphere (meter)
r2 = 0.0002
mu = 0.53 # mu = dynamic viscosity of fluid (log10Poise)
mu2 = 0.53
rp = 0.01 #radius around particle to check for nearest neighbor
rp2 = 0.001
dt = 0.008
dt2 = 0.008
fig, ax = plt.subplots()
az = plt.axes(xlim=(-.5, .5), ylim=(-1, 0))
xdata, ydata = [0.0], [0.0]
xdata2, ydata2 = [0.0], [0.0]
ln, = plt.plot([], [], marker= 'o',
markerfacecolor='r',markeredgecolor='k', linestyle='None',
animated=True)
ln2, = plt.plot([], [], marker= 's',
markerfacecolor='b',markeredgecolor='k', linestyle='None',
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 v_stokes2(pp2,pf2,g2,r2,mu2):
top2=2*(pp2-pf2)*g2*(r2**2)
bottom2=9*mu2
ws2=top2/bottom2
return ws2
def init():
ax.set_xlim( -2, 2)
ax.set_ylim(-10, 0)
return ln, ln2,
def concentration(xdata, ydata, rp):
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)
#pdb.set.trace()
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt
def concentration2(xdata2, ydata2, rp2):
coords2 = list(zip(xdata2, ydata2))
tree2 = spatial.KDTree(coords2)
test2 = np.column_stack([xdata2, ydata2])
nnl2 = tree2.query_ball_point(test2, rp2)
nnt2 = np.zeros(len(nnl2)) #nearest neighbors total
for i in range(len(nnt2)):
nnt2[i] = len(nnl2[i])
return nnt2
#y0 = []
#y1 = []
#y2 = []
#y3 = []
#y4 = []
def update(frame):
global xdata
global ydata
global concentration, v_stokes, pp, pf, g, r, mu, rp, dt, n, sigma
xdata = xdata + np.random.normal(0, 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 print(ydata)
rp = 0.003
if frame > 10:
rp = 0.008
cp = concentration(xdata, ydata, rp)
if np.any(cp == 1):
cp = cp-1
if frame > 0.0:
#y0.append(ydata)
#y1.append(wss)
#y2.append(cp)
#y3.append(dt)
#y4.append(xdata)
ydata = ydata + (wss*(1-cp)**3) - dt # [0]
for v in ydata:
if v < -1.001:
ydata = -1
ln.set_data(xdata, ydata)
return ln,
def update2(frame2):
global xdata2
global ydata2
global concentration2, v_stokes2, pp2, pf2, g2, r2, mu2, rp2, dt2,
n2, sigma2
xdata2 = xdata2 + np.random.normal(0, sigma2, n2)
wss2 = v_stokes2(pp2,pf2,g2,r2,mu2)
if frame2 == 0.0:
ydata2 = np.zeros(len(xdata2)) #makes the ydata length = xdata
#at time 0 print(ydata)
rp2 = 0.003
if frame2 > 10:
rp2 = 0.008
cp2 = concentration2(xdata2, ydata2, rp2)
if np.any(cp2 == 1):
cp2 = cp2-1
if frame2 > 0.0:
#y5.append(ydata2)
#y6.append(wss2)
#y7.append(cp2)
#y8.append(dt2)
#y9.append(xdata2)
ydata2 = ydata2 + (wss2*(1-cp2)**3) - dt2 # [0]
for v2 in ydata2:
if v2 < -1.001:
ydata2 = -1
ln2.set_data(xdata2, ydata2)
return ln2,
def update_all(i):
l1 = update(i)
l2 = update2(i)
return l1, l2,
ani = FuncAnimation(fig, update_all, frames=range(0,200),
init_func=init, blit=True, interval=100, repeat =
False)
# change frames=range(0:1000) to change the number of frames
plt.show()
Below is the original working code with one animated particle:
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=100
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.01 #radius around particle to check for nearest neighbor
dt =0.008
fig, ax = plt.subplots()
az = plt.axes(xlim=(-.5, .5), ylim=(-1, 0))
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], marker= 'o',
markerfacecolor='r',markeredgecolor='k', linestyle='None',
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):
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)
#pdb.set.trace()
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt
y0 = []
y1 = []
y2 = []
y3 = []
y4 = []
def update(frame):
global xdata
global ydata
global concentration, v_stokes, pp, pf, g, r, mu, rp, dt, n
xdata = xdata + np.random.normal(0, sigma, n)
wss = v_stokes(pp,pf,g,r,mu)
#print(wss)
if frame == 0.0:
ydata = np.zeros(len(xdata)) #makes the ydata length = xdata at
#time 0 print(ydata)
rp = 0.003
if frame > 10:
rp = 0.008
if frame > 30:
rp = 0.01
if frame > 50: #notice that the particles move up instead of just
#slowing down
rp = 0.013
cp = concentration(xdata, ydata, rp)
#print(cp)
if np.any(cp == 1):
cp = cp-1
#print(xdata)
print(cp)
if frame > 0.0:
y0.append(ydata)
y1.append(wss)
y2.append(cp)
y3.append(dt)
y4.append(xdata)
ydata = ydata + (wss*(1-cp)**3) - dt # [0]
for v in ydata:
if v < -1.001:
ydata = -1
#if np.all(ydata) > 0:
#print(ydata)
#print(frame)
#print(ydata[0:5])
#ydata = ydata + (wss*(1-cp))
ln.set_data(xdata, ydata)
return ln,
#print(test)
ani = FuncAnimation(fig, update, frames=range(0,200),
init_func=init, blit=True, interval=100, repeat =
False)
# change frames=range(0:1000) to change the number of frames
plt.show()
The matplotlib.animation.FuncAnimation(fig, func, ...) expects as its second argument a callable (e.g. a function) to update the plot.
func : callable
The function to call at each frame. The first argument will be the next value in frames. Any additional positional arguments can be supplied via the fargs parameter.
If you have two functions to update, you would need to merge them into one,
def update_all(i):
l1 = update(i)
l2 = update2(i)
return l1,l2
ani = FuncAnimation(fig, update_all, frames=range(0,200), init_func=init, ...)
Mind that you need to define the plots in the same manner as usual, ln, = plt.plot and that you need to return the line object itself in the two updating functions.
Complete running code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy import spatial
n=100
sigma= 0.01
pp = 2.56 # pp = particle density (Sphene=3.53) #(Feldspar=2.56)
#(g/cm^3)
pp2 = 3.53
pf = 2.7 # pf = fluid density(g/cm^3)
pf2 = 2.7
g = 9.8 # g = gravity (m/s^2)
g2 = 9.8
r = 0.003 # r = radius of sphere (meter)
r2 = 0.0002
mu = 0.53 # mu = dynamic viscosity of fluid (log10Poise)
mu2 = 0.53
rp = 0.01 #radius around particle to check for nearest neighbor
rp2 = 0.001
dt = 0.008
dt2 = 0.008
fig, ax = plt.subplots()
az = plt.axes(xlim=(-.5, .5), ylim=(-1, 0))
xdata, ydata = [0.0], [0.0]
xdata2, ydata2 = [0.0], [0.0]
ln, = plt.plot([], [], marker= 'o',
markerfacecolor='r',markeredgecolor='k', linestyle='None',
animated=True)
ln2, = plt.plot([], [], marker= 's',
markerfacecolor='b',markeredgecolor='k', linestyle='None',
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 v_stokes2(pp2,pf2,g2,r2,mu2):
top2=2*(pp2-pf2)*g2*(r2**2)
bottom2=9*mu2
ws2=top2/bottom2
return ws2
def init():
ax.set_xlim( -2, 2)
ax.set_ylim(-10, 0)
return ln, ln2
def concentration(xdata, ydata, rp):
coords = list(zip(xdata, ydata))
tree = spatial.KDTree(coords)
test = np.column_stack([xdata, ydata])
nnl = tree.query_ball_point(test, rp)
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt
def concentration2(xdata2, ydata2, rp2):
coords2 = list(zip(xdata2, ydata2))
tree2 = spatial.KDTree(coords2)
test2 = np.column_stack([xdata2, ydata2])
nnl2 = tree2.query_ball_point(test2, rp2)
nnt2 = np.zeros(len(nnl2)) #nearest neighbors total
for i in range(len(nnt2)):
nnt2[i] = len(nnl2[i])
return nnt2
y0 = [];y1 = [];y2 = [];y3 = [];y4 = []
def update(frame):
global xdata
global ydata
global concentration, v_stokes, pp, pf, g, r, mu, rp, dt, n
xdata = xdata + np.random.normal(0, 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 print(ydata)
rp = 0.003
if frame > 10:
rp = 0.008
if frame > 30:
rp = 0.01
if frame > 50: #notice that the particles move up instead of just
#slowing down
rp = 0.013
cp = concentration(xdata, ydata, rp)
if np.any(cp == 1):
cp = cp-1
#print(cp)
if frame > 0.0:
y0.append(ydata)
y1.append(wss)
y2.append(cp)
y3.append(dt)
y4.append(xdata)
ydata = ydata + (wss*(1-cp)**3) - dt # [0]
for j,v in enumerate(ydata):
if v < -1.001:
ydata[j] = -1
ln.set_data(xdata, ydata)
return ln
def update2(frame2):
global xdata2
global ydata2
global concentration2, v_stokes, pp2, pf2, g2, r2, mu2, rp2, dt2, n
xdata2 = xdata2 + np.random.normal(0, sigma, n)
wss2 = v_stokes2(pp2,pf2,g2,r2,mu2)
if frame2 == 0.0:
ydata2 = np.zeros(len(xdata2)) #makes the ydata length = xdata
#at time 0 print(ydata)
rp2 = 0.003
if frame2 > 10:
rp2 = 0.008
if frame2 > 30:
rp2 = 0.01
if frame2 > 50: #notice that the particles move up instead of just
#slowing down
rp2 = 0.013
cp2 = concentration2(xdata2, ydata2, rp2)
if np.any(cp2 == 1):
cp2 = cp2-1
if frame2 > 0.0:
y0.append(ydata2)
y1.append(wss2)
y2.append(cp2)
y3.append(dt2)
y4.append(xdata2)
ydata2 = ydata2 + (wss2*(1-cp2)**3) - dt2 # [0]
for j,v in enumerate(ydata2):
if v < -1.001:
ydata2[j] = -1
ln2.set_data(xdata2, ydata2)
return ln2
def update_all(i):
l1 = update(i)
l2 = update2(i)
return l1,l2
ani = FuncAnimation(fig, update_all, frames=range(0,200),
init_func=init, blit=True, interval=100, repeat = False)
plt.show()
Since the code seems mostly redundant for the two particle classes, I would suggest to simplify it by putting it into a class.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy import spatial
n=100
sigma= 0.01
pp = 2.56 # pp = particle density (Sphene=3.53) #(Feldspar=2.56)
#(g/cm^3)
pp2 = 3.53
pf = 2.7 # pf = fluid density(g/cm^3)
pf2 = 2.7
g = 9.8 # g = gravity (m/s^2)
g2 = 9.8
r = 0.003 # r = radius of sphere (meter)
r2 = 0.0002
mu = 0.53 # mu = dynamic viscosity of fluid (log10Poise)
mu2 = 0.53
rp = 0.01 #radius around particle to check for nearest neighbor
rp2 = 0.001
dt = 0.008
dt2 = 0.008
class Particles():
def __init__(self,ax, xdata,
pp, pf, g, r, mu, rp, dt, n,
marker="o",mfc="r"):
self.xdata=xdata
self.ydata = np.zeros(len(self.xdata))
self.pp=pp;self.pf=pf;self.g=g;self.r=r;self.mu=mu
self.rp=rp;self.dt=dt;self.n=n
self.ln, = ax.plot([], [], marker= marker,
markerfacecolor=mfc,markeredgecolor='k', linestyle='None',
animated=True)
def v_stokes(self, pp,pf,g,r,mu):
top=2*(pp-pf)*g*(r**2)
bottom=9*mu
return top/bottom
def concentration(self,xdata, ydata, rp):
coords = list(zip(xdata, ydata))
tree = spatial.KDTree(coords)
test = np.column_stack([xdata, ydata])
nnl = tree.query_ball_point(test, rp)
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt
def update(self, frame):
self.xdata += np.random.normal(0, sigma, n)
if frame == 0:
self.ydata = np.zeros(len(self.xdata))
wss = self.v_stokes(self.pp,self.pf,self.g,self.r,self.mu)
cp = self.concentration(self.xdata, self.ydata, self.rp)
if np.any(cp == 1):
cp = cp-1
if frame > 0.0:
self.ydata += (wss*(1-cp)**3) - dt
for j,v in enumerate(self.ydata):
if v < -1.001:
self.ydata[j] = -1
self.ln.set_data(self.xdata, self.ydata)
return self.ln
fig, ax = plt.subplots()
xdata, xdata2 = [0.0], [0.0]
p1 = Particles(ax,xdata, pp, pf, g, r, mu, rp, dt, n,
marker="o",mfc="r")
p2 = Particles(ax,xdata2, pp2, pf2, g2, r2, mu2, rp2, dt2, n,
marker="s",mfc="b")
def init():
ax.set_xlim(-2, 2)
ax.set_ylim(-1, 1)
return p1.ln, p2.ln
def update(i):
l1 = p1.update(i)
l2 = p2.update(i)
return l1, l2
ani = FuncAnimation(fig, update, frames=range(0,200),
init_func=init, blit=True, interval=100, repeat = False)
plt.show()

Categories

Resources