Related
I have add the assumption of nonnegative for variables x and r so why I can't plot this?
this is my code:
# Calculate the surface area of y = sqrt(r^2 - x^2)
# revolved about the x-axis
import matplotlib.pyplot as plt
import numpy as np
import sympy as sy
x = sy.Symbol("x", nonnegative=True)
r = sy.Symbol("r", nonnegative=True)
def f(x):
return sy.sqrt(r**2 - x**2)
def fd(x):
return sy.simplify(sy.diff(f(x), x))
def f2(x):
return sy.sqrt((1 + (fd(x)**2)))
def vx(x):
return 2*sy.pi*(f(x)*sy.sqrt(1 + (fd(x) ** 2)))
vxi = sy.Integral(vx(x), (x, -r, r))
vxf = vxi.simplify().doit()
vxn = vxf.evalf()
n = 100
fig = plt.figure(figsize=(14, 7))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222, projection='3d')
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224, projection='3d')
x = np.linspace(1, 3, 3)
# Plot the circle
y = np.sqrt(r ** 2 - x ** 2)
t = np.linspace(0, np.pi * 2, n)
xn = np.outer(x, np.cos(t))
yn = np.outer(x, np.sin(t))
zn = np.zeros_like(xn)
for i in range(len(x)):
zn[i:i + 1, :] = np.full_like(zn[0, :], y[i])
ax1.plot(x, y)
ax1.set_title("$f(x)$")
ax2.plot_surface(xn, yn, zn)
ax2.set_title("$f(x)$: Revolution around $y$")
# find the inverse of the function
y_inverse = x
x_inverse = np.sqrt(r ** 2 - y_inverse ** 2)
xn_inverse = np.outer(x_inverse, np.cos(t))
yn_inverse = np.outer(x_inverse, np.sin(t))
zn_inverse = np.zeros_like(xn_inverse)
for i in range(len(x_inverse)):
zn_inverse[i:i + 1, :] = np.full_like(zn_inverse[0, :], y_inverse[i])
ax3.plot(x_inverse, y_inverse)
ax3.set_title("Inverse of $f(x)$")
ax4.plot_surface(xn_inverse, yn_inverse, zn_inverse)
ax4.set_title("$f(x)$: Revolution around $x$ \n Surface Area = {}".format(vxn))
plt.tight_layout()
plt.show()
That's because at this line of code:
y = np.sqrt(r ** 2 - x ** 2)
r is still a Sympy's symbol. You need to assign a number to r.
I am trying to plot a single pendulum using Eulers method and with given theta values and formula in python but I am getting an Attribute error on FuncAnimation saying 'FuncAnimation' object has no attribute '_resize_id'. Does anyone know what I'm doing wrong here?
# Liður 2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def ydot(t, y):
g = 9.81
l = 1
z1 = y[1]
z2 = -g/l*np.sin(y[0])
return np.array([z1, z2])
def eulerstep(t, x, h):
return ([x[j]+h*ydot(t,x)[j] for j in range(len(x))])
def eulersmethod(Theta0, T, n):
z = Theta0
h = T/n
t = [i*h for i in range(n)]
theta = [[],[]]
for i in range(n):
z = eulerstep(t[i], z, h)
theta[0].append(z[0])
theta[1].append(z[1])
return(t, theta[0], theta[1])
def animate_pendulum(x, y, h):
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(autoscale_on = False, xlim=(-2.2, 2.2), ylim = (-2.2, 2.2))
ax.grid()
line = ax.plot([],[], 'o', c='blue', lw=1)
time_text = ax.text(0.05, 0.9, '', transform = ax.transAxes)
def animate(i):
xline = [0, x[1]]
yline = [0, y[1]]
line.set_data(xline, yline)
time_text.set_text(f"time = {i*h:1f}s")
return line, time_text
ani = FuncAnimation(
fig, animate, len(x), interval = h*1000, blit = True, repeat = False
)
plt.show()
def min():
L=2
T=20
n=500
h=T/n
y_0 = [np.pi/12, 0]
t, angle, velocity = eulersmethod(y_0, T, n)
x, y = L*np.sin(angle[:]), -L*np.cos(angle[:])
animate_pendulum(x, y, h)
min()
Axes.plot() returns a list of lines, see the docs. You Can unpack the list by adding a comma at the variable assignment. This code runs on my machine:
# Liður 2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def ydot(t, y):
g = 9.81
l = 1
z1 = y[1]
z2 = -g/l*np.sin(y[0])
return np.array([z1, z2])
def eulerstep(t, x, h):
return ([x[j]+h*ydot(t,x)[j] for j in range(len(x))])
def eulersmethod(Theta0, T, n):
z = Theta0
h = T/n
t = [i*h for i in range(n)]
theta = [[],[]]
for i in range(n):
z = eulerstep(t[i], z, h)
theta[0].append(z[0])
theta[1].append(z[1])
return(t, theta[0], theta[1])
def animate_pendulum(x, y, h):
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(autoscale_on = False, xlim=(-2.2, 2.2), ylim = (-2.2, 2.2))
ax.grid()
line, = ax.plot([],[], 'o', c='blue', lw=1)
time_text = ax.text(0.05, 0.9, '', transform = ax.transAxes)
def animate(i):
xline = [0, x[1]]
yline = [0, y[1]]
line.set_data(xline, yline)
time_text.set_text(f"time = {i*h:1f}s")
return line, time_text
ani = FuncAnimation(
fig, animate, len(x), interval = h*1000, blit = True, repeat = False
)
plt.show()
def min():
L=2
T=20
n=500
h=T/n
y_0 = [np.pi/12, 0]
t, angle, velocity = eulersmethod(y_0, T, n)
x, y = L*np.sin(angle[:]), -L*np.cos(angle[:])
animate_pendulum(x, y, h)
min()
I've created an animated plot of a wavefunction, psi:
def psi(x, t):
real = 0.4*np.cos(0.4*x - 0.08*t) + 0.6*np.cos(0.6*x - 0.18*t)
imag = 0.4*np.sin(0.4*x - 0.08*t) + 0.6*np.sin(0.6*x - 0.18*t)
square = real**2 + imag**2
return real, imag, square
I've then animated it successfully, however on adding the axvspan fill I've encountered an issue:
x = np.linspace(-10, 1000, 10000)
fig, (ax1, ax2) = plt.subplots(2,1)
line1, = ax1.plot([], [])
line2, = ax1.plot([], [])
line3, = ax2.plot([], [])
line = [line1, line2, line3]
def animate(i):
y1, y2, y3 = psi(x, t=i/2)
line1.set_data(x, y1)
line2.set_data(x, y2)
line3.set_data(x, y3)
spline = UnivariateSpline(x, y3-max(y3)/2, s=0)
r1, r2 = spline.roots()
ax2.axvspan(r1, r2, facecolor='b', alpha=0.5)
plt.legend(['Max Probability = %1.3f' % (max(y3))])
return line,
anim = animation.FuncAnimation(fig, animate, frames=600, interval = 100, blit=False, repeat=False)
It starts like
and it ends like .
Every iteration of the animation function, the fill increases across the page having started off as filling half the graph (I'd include a gif but at the moment that's a struggle I'm having with anaconda). I'm working under the assumption this is because the axes don't clear properly, however with the blit=false I assumed this wouldn't be a problem?
As asked for - the full psi function is detailed below:
n = 15
amp_scale = np.linspace(0, 0.8, n)
amp_init = norm.pdf(amp_scale, 0.4, 0.2)
#normalise wavefunction to prob=1
amp = []
for i in range(n):
amp_val = amp_init[i]/sum(amp_init)
amp.append(amp_val)
k = np.linspace(1.4, 2.6, n)
def psi (x, t=1, n=1, a = 1, k = 1, m = 1):
psi_real = 0
psi_imag = 0
for i in range(n):
a_val = a[i]
k_val = k[i]
w = (k_val**2)/(2*m)
psi_real+=a_val*np.cos(k_val*x - w*t)
psi_imag+=a_val*np.sin(k_val*x - w*t)
psi_squared = psi_real**2 + psi_imag**2
return psi_real, psi_imag, psi_squared
In your current code, a new axvspan() is continually added, never removed. You could explicitly remove the old span inside animate(). Or, similarly to what happens to the lines, update the position. A span is internally represented as a polygon, of which the coordinates can be set via .set_xy().
The function psi in the post doesn't seem to be the same as the function that generated the example plots. This also made that I couldn't get to work to calculate the spline and the roots. I replaced them by some simpler positions to show how the span can be updated during the animation.
The code also adds explicit x and y limits, as they weren't set in the question's code.
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
from scipy.interpolate import UnivariateSpline
def psi(x, t):
# the function from the question is adapted to more resemble the plot
real = (0.4 * np.cos(0.4 * x - 0.08 * t) + 0.6 * np.cos(0.6 * x - 0.18 * t)) * np.exp(- (x - t) ** 2 / 5000)
imag = (0.4 * np.sin(0.4 * x - 0.08 * t) + 0.6 * np.sin(0.6 * x - 0.18 * t)) * np.exp(- (x - t) ** 2 / 5000)
square = real ** 2 + imag ** 2
return real, imag, square
x = np.linspace(-10, 1000, 10000)
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
line1, = ax1.plot([], [])
line2, = ax1.plot([], [])
line3, = ax2.plot([], [])
line = [line1, line2, line3]
span1 = ax2.axvspan(0, 0, facecolor='b', alpha=0.2)
ax1.set_xlim(x[0], x[-1]/2)
ax1.set_ylim(-1, 1)
ax2.set_ylim(0, 1.1)
def animate(i):
y1, y2, y3 = psi(x, t=i / 2)
line1.set_data(x, y1)
line2.set_data(x, y2)
line3.set_data(x, y3)
# this didn't work for me, spline.roots() gave me a long array of values
spline = UnivariateSpline(x, y3 - max(y3) / 2, s=0)
r1, r2 = spline.roots()[[0, -1]] # [[0, -1]] takes the first and the last
# r1, r2 = i - 50, i + 50
span1.set_xy([[r1, 0], [r1, 1], [r2, 1], [r2, 0], [r1, 0]])
plt.legend(['Max Probability = %1.3f' % (max(y3))])
return line, span1, ax2.legend_,
anim = animation.FuncAnimation(fig, animate, frames=600, interval=100, blit=False, repeat=False)
plt.plot()
The resulting end frame looks like (note that a slightly different function is used):
I would like to predict a ball trajectory by fitting its 3d coordinates into a parabola. Below is my code. But instead of a parabola, I got a straight line. If you have any clue about it, please let me know. Thanks!
# draw scatter coordiante
fig = plt.figure()
ax = plt.axes(projection = '3d')
x_list = []
y_list = []
z_list = []
for x in rm_list:
x_list.append(x[0][0])
y_list.append(x[0][1])
z_list.append(x[0][2])
x = np.array(x_list)
y = np.array(y_list)
z = np.array(z_list)
ax.scatter(x, y, z)
# curve fit
def func(x, a, b, c, d):
return a * x[0]**2 + b * x[1]**2 + c * x[0] * x[1] + d
data = np.column_stack([x_list, y_list, z_list])
popt, _ = curve_fit(func, data[:,:2].T, ydata=data[:,2])
a, b, c, d = popt
print('y= %.5f * x ^ 2 + %.5f * y ^ 2 + %.5f * x * y + %.5f' %(a, b, c, d))
x1 = np.linspace(0.3, 0.4, 100)
y1 = np.linspace(0.02, 0.06, 100)
z1 = a * x1 ** 2 + b * y1 ** 2 + c * x1 * y1 + d
ax.plot(x1, y1, z1, color='green')
plt.show()
Update 1
After changing the func to ax^2 + by^2 + cxy + dx + ey + f, I got a parabola but not fitting to the coordinate.
That you have your underlying timestamp data makes the fitting procedure easier:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
from numpy.polynomial import Polynomial
# test data generation with some noise
# here read in your data
np.random.seed(123)
n = 40
x_param = [ 1, 21, -1]
y_param = [12, -3, 0]
z_param = [-3, 0, -2]
px = Polynomial(x_param)
py = Polynomial(y_param)
pz = Polynomial(z_param)
t = np.random.choice(np.linspace (-3000, 2000, 1000)/500, n)
x = px(t) + np.random.random(n)
y = py(t) + np.random.random(n)
z = pz(t) + np.random.random(n)
# here start the real calculations
# draw scatter coordinates of raw data
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.scatter(x, y, z, label="raw data")
# curve fit function
def func(t, x2, x1, x0, y2, y1, y0, z2, z1, z0):
Px=Polynomial([x2, x1, x0])
Py=Polynomial([y2, y1, y0])
Pz=Polynomial([z2, z1, z0])
return np.concatenate([Px(t), Py(t), Pz(t)])
# curve fit
# start values are not necessary for this example
# but make it your rule to always provide start values for curve_fit
start_vals = [ 1, 10, 1,
10, 1, 1,
-1, -1, -1]
xyz = np.concatenate([x, y, z])
popt, _ = curve_fit(func, t, xyz, p0=start_vals)
print(popt)
#[ 1.58003630e+00 2.10059868e+01 -1.00401965e+00
# 1.25895591e+01 -2.97374035e+00 -3.23358241e-03
# -2.44293562e+00 3.96407428e-02 -1.99671092e+00]
# regularly spaced fit data
t_fit = np.linspace(min(t), max(t), 100)
xyz_fit = func(t_fit, *popt).reshape(3, -1)
ax.plot(xyz_fit[0, :], xyz_fit[1, :], xyz_fit[2, :], color="green", label="fitted data")
ax.legend()
plt.show()
Sample output:
I have to solve a problem "Fringes of Young" using integrals in Python with "quad" and "args"
Formula of the intensity on the screen for M(X,Y) for a source size R is the following :
A source point S have the coordinates (xs=0,ys) with -R/2<=ys<=R/2
I need to create a function to calculate the intensity I(X,Y,R) using "args" of "quad".
Then, plot I(0,Y,10e-6) for Y between -0.01 and 0.01, also, I(0,Y,0.002),I(0,Y,0.003),I(0,Y,0.004). Any idea where is my fault?
My code :
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad
y_min = -0.01
y_max = +0.01
R = y_max-y_min
y = np.linspace(y_min, y_max, 100)
X = 0
Y = 0
d = 1
D = 10
s = 10
Lambda = 0.5e-3
delta_s = lambda ys,X,Y : np.sqrt(X**2+(Y-d/2)**2+D**2)+np.sqrt((ys-d/2)**2+s**2)- \
np.sqrt(X**2+(Y+d/2)**2+D**2)-np.sqrt((ys+d/2)**2+s**2)
def integrand(y_s,x,y):
value = 2*(1+np.cos(2*np.pi*delta_s(x,y,y_s)/Lambda))
return value
def calcul_XYR(X,Y,R):
compteur = 0
I_XYR = [] # array for I(X,Y,R)
while compteur < len(y-1):
Y = y[compteur]
print(Y)
I_XYR.append(1/R*quad(integrand, -R/2, R/2, args=(X,Y))[0])
compteur+=1
return I_XYR
plt.figure(figsize=(7, 5))
plt.title("Franges de Young - Figure 3")
plt.axis([y_min, 0.015, 0, 4])
plt.xlabel("Y (mm)")
plt.ylabel("Intensity (a.u.)")
plt.plot(y, calcul_XYR(0,Y,1e-6), '-', color="red", label=r'$R=10^{-6}$')
plt.plot(y, calcul_XYR(0,Y,0.002), '-', color="blue", label=r'$R=0.002$')
plt.plot(y, calcul_XYR(0,Y,0.003), '-', color="black", label=r'$R=0.003$')
plt.plot(y, calcul_XYR(0,Y,0.004), '-', color="green", label=r'$R=0.004$')
plt.legend(loc='right', bbox_to_anchor=(1.00, 0.3))
plt.savefig('question 3 figure.pdf', format='pdf')
plt.show()
Result :
Expected :
I'd also like to plot (using imshow with parameters : cmp(gray),vmin,vmax) a 2D image corresponding to I(X,Y,1e-06). (X between -10 to 10).
The main mistake is the order of the parameters to delta_s. It is defined as delta_s = lambda ys, X, Y, but called as delta_s(X, Y, ys).
Also, calcul_XYR doesn't use its parameter Y, so it's better removed. The loop can be written as for Y in y.
Here is the modified code to generate the desired plot:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad
y_min = -0.01
y_max = +0.01
#R = y_max - y_min
y = np.linspace(y_min, y_max, 100)
X = 0
Y = 0
d = 1
D = 10
s = 10
Lambda = 0.5e-3
def delta_s(X, Y, ys):
return np.sqrt(X ** 2 + (Y - d / 2) ** 2 + D ** 2) + np.sqrt((ys - d / 2) ** 2 + s ** 2) - \
np.sqrt(X ** 2 + (Y + d / 2) ** 2 + D ** 2) - np.sqrt((ys + d / 2) ** 2 + s ** 2)
def integrand(y_s, x, y):
return 2 * (1 + np.cos(2 * np.pi * delta_s(x, y, y_s) / Lambda))
def calcul_XR(X, R):
I_XYR = [] # array for I(X,Y,R)
for Y in y:
I_XYR.append(1 / R * quad(integrand, -R / 2, R / 2, args=(X, Y))[0])
return I_XYR
plt.figure(figsize=(7, 5))
plt.title("Franges de Young - Figure 3")
plt.axis([y_min, 0.015, 0, 4])
plt.xlabel("Y (mm)")
plt.ylabel("Intensity (a.u.)")
plt.plot(y, calcul_XR(0, 1e-6), '-', color="red", label=r'$R=10^{-6}$')
plt.plot(y, calcul_XR(0, 0.002), '-', color="blue", label=r'$R=0.002$')
plt.plot(y, calcul_XR(0, 0.003), '-', color="black", label=r'$R=0.003$')
plt.plot(y, calcul_XR(0, 0.004), '-', color="green", label=r'$R=0.004$')
plt.legend(loc='right', bbox_to_anchor=(1.00, 0.3))
plt.savefig('question 3 figure.pdf', format='pdf')
plt.show()
The following code displays an image of the function:
x_min = -10
x_max = 10
x = np.linspace(x_min, x_max, 100)
R = 1e-6
plt.figure(figsize=(7, 5))
graphe1 = []
for xi in x:
graphe1.append(calcul_XYR(xi, R))
graphe1 = np.array(graphe1).T #convert to numpy array and transpose
# imshow normally starts displaying at the top `origin='lower'` reverses this;
# the extent is used to tell imshow what the x and y limits of the image are, to correctly put the ticks
# without `aspect='auto'` imshow seems to want to display x and y with the same scale
# interpolation='bilinear' tells to smooth out the pixels
plt.imshow(graphe1, cmap=plt.cm.gray, vmin=None, vmax=None,
extent=[x_min, x_max, y_min, y_max],
aspect='auto', origin='lower', interpolation='bilinear')
plt.xlabel('X')
plt.ylabel('Y')
plt.title(f'R={R}')
plt.show()