I can't animate a surface with Mayavi - python

I'm trying to do a simple animation of a surface with Mayavi, but due to the lack of online examples (the few are extremely unclear or not useful) and official documentation, I'm struggling a lot. I wrote a very simple code to show where I've gone so far
import numpy as np
from mayavi import mlab
import time
#Meshgrid + u + Surface
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
u = np.ones((10,10))
surf = mlab.mesh(X,Y,u)
#Surface animation
#mlab.animate(delay=1000)
def anim():
for n in range(1,10):
global u
print(n)
u = u+1
surf.mlab_source.scalars = u
yield
anim()
mlab.show()
So very simple, every iteration I pass from a 10x10 matrix of ones to a 10x10 matrix of twos and so on.
The problems I've encountered are 3
I really can't understand what mlab_source.scalars does: fundamentally I don't know how to tell it that the z axis is changing and in doing so it follows the variation of u
Mayavi opens up, but the grid is all plane and black, like there is some issues with u
I don't understand what yield does: in the actual code I'm writing it stops the computation in a lot of advance (like in 300 steps it stops it at the step 29), but I can't remove it
I really can't get my head around, it's weeks I'm trying, hope somebody helps. Thanks in advance.
Edit:
I tried to extend #E.Klahn code in a case resembling more mine
import numpy as np
from mayavi import mlab
s = 0.01
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
Z = np.ones((10,10))
m = mlab.mesh(X, Y, Z)
#mlab.animate(delay=20)
def anim():
for i in range(1,100):
m.mlab_source.z = np.ones((10,10))*s*i
yield
anim()
mlab.show()
But I obtain only a flat surface going up, not a tridimensional object evolving, such as in his code.
Edit2:
Here the working code just copying the example of #E.Klahn, which I thank very very much
import numpy as np
from mayavi import mlab
x = np.arange(0,1,0.1)
y = np.arange(0,1,0.1)
X,Y = np.meshgrid(x,y)
X,Y=X.T,Y.T #seems an important command
u = np.ones((10,10))
surf = mlab.surf(X,Y,u)
#mlab.animate(delay=500)
def anim():
for n in range(1,10):
print(n)
surf.mlab_source.scalars = 1+np.sin(X)*np.sin(Y)*np.sin(n)
yield
anim()
mlab.show()
I just print it to give continuity to code above and to spot the differences.

Here I've shown the code to plot a cube and animate it so that it grows out from a height of 0.01 to a height of 1 using mlab.mesh. What yield does is that it returns control to the decorator so that the scene can be updated.
import numpy as np
from mayavi import mlab
s = 0.01
X = np.array([[0,0,1,1],[0,0,1,1],[0,0,0,0],[1,1,1,1]])
Y = np.array([[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]])
Z = np.array([[0,0,0,0],[s,s,s,s],[0,0,s,s],[0,0,s,s]])
m = mlab.mesh(X, Y, Z)
#mlab.animate(delay=20)
def anim():
for i in range(1,101):
print(i, end='\r')
m.mlab_source.z = np.array([[0,0,0,0],[s*i,s*i,s*i,s*i],[0,0,s*i,s*i],[0,0,s*i,s*i]])
yield
anim()
mlab.show()
scalars is one way to access the underlying data structure if that data structure has the scalars attribute. However, for mesh that attribute is not used, rather you want to access z and set those values. I would not know what your specific issue with u if unless there is a traceback to work from. The same with yield - I see no reason why that keyword would stop the animation at any particular step.
The code below shows, instead of a box evolving over time, a surface evolving with time.
import numpy as np
from mayavi import mlab
def evolving_function(X, Y, t, Lx=1, Ly=1):
return np.sin(X+Lx*t)*np.sin(Y+Ly*t)
Lx, Ly = 1,2
x = np.linspace(-10,10,100)
y = np.linspace(-10,10,100)
X,Y = np.meshgrid(x,y)
X, Y = X.T, Y.T
s = evolving_function(X, Y, 0, Lx=Lx, Ly=Ly)
m = mlab.surf(X, Y, s)
dt = 50
t = 10
steps = int(10*1000/dt)
#mlab.animate(delay=dt)
def anim():
for i in range(1,steps):
m.mlab_source.scalars = evolving_function(X, Y, dt*i, Lx=Lx, Ly=Ly)
yield
anim()
mlab.show()

Related

Making Music with Python

Trying to create some musical notes by combining harmonic series. Very simple code, but the audio turns up blank. Any thoughts?
from IPython.display import Audio
import numpy as np
import matplotlib.pyplot as plt
def Harmonic(i,linComb):
x=np.linspace(0,3,24000)
y = [0 for _ in x]
weights = linComb
for n in range(0,i):
y += np.sin((2*n+1)*(2*np.pi*weights[n])*(x))/(2*n+1)
plt.plot(x,y)
plt.show()
return y
out = Harmonic(3,[0,2,3])
Audio(data=out, rate=8000)
Stuff I've tried:
Changing the rate
Manipulating the y-values
Ensuring the harmonic function does indeed work
Looking at this answer (same function, but still doesn't work)
Would appreciate any help. Thanks.
The sound generated by the code is audible but weak.
I have no experience in audio programming, but some type of noise resembling a loud beep can be generated by the following:
from IPython.display import Audio
import numpy as np
import matplotlib.pyplot as plt
def Harmonic(i, weights):
x=np.linspace(0,3,24000)
y = [0 for _ in x]
for n in range(0,i):
y += np.sin((2*n+1)*(2*np.pi*weights[n])*(x))/(2*n+1)
plt.plot(x,y)
plt.show()
return y
i = 1000
weights = [1000] * 1000 # Length equal to i
out = Harmonic(i, weights)
Audio(data=out, rate=8000)

Scaling x-axis after IFFT-FFT

See the edit below for details.
I have a dataset, on which I need to perform and IFFT, cut the valueable part of it (by multiplying with a gaussian curve), then FFT back.
First is in angular frequency domain, so an IFFT leads to time domain. Then FFT-ing back should lead to angular frequency again, but I can't seem to find a solution how to get back the original domain. Of course it's easy on the y-values:
yf = np.fft.ifft(y)
#cut the valueable part there..
np.fft.fft(yf)
For the x-value transforms I'm using np.fft.fftfreq the following way:
# x is in ang. frequency domain, that's the reason for the 2*np.pi division
t = np.fft.fftfreq(len(x), d=(x[1]-x[0])/(2*np.pi))
However doing
x = np.fft.fftfreq(len(t), d=2*np.pi*(t[1]-t[0]))
completely not giving me back the original x values. Is that something I'm misunderstanding?
The question can be asked generalized, for example:
import numpy as np
x = np.arange(100)
xx = np.fft.fftfreq(len(x), d = x[1]-x[0])
# how to get back the original x from xx? Is it even possible?
I've tried to use a temporal variable where I store the original x values, but it's not too elegant. I'm looking for some kind of inverse of fftfreq, and in general the possible best solution for that problem.
Thank you.
EDIT:
I will provide the code at the end.
I have a dataset which has angular frequency on x axis and intensity on the y. I want to perfrom IFFT to change to time domain. Unfortunately the x values are not
evenly spaced, so a (linear) interpolation is needed first before IFFT. Then in time domain the transform looks like this:
The next step is to cut one of the symmetrical spikes with a gaussian curve, then FFT back to angular frequency domain (the same where we started). My problem is when I transfrom the x-axis for the IFFT (which I think is correct), I can't get back into the original angular frequency domain. Here is the code, which includes the generator for the dataset too.
import numpy as np
import matplotlib.pyplot as plt
import scipy
from scipy.interpolate import interp1d
C_LIGHT = 299.792
# for easier case, this is zero, so it can be ignored.
def _disp(x, GD=0, GDD=0, TOD=0, FOD=0, QOD=0):
return x*GD+(GDD/2)*x**2+(TOD/6)*x**3+(FOD/24)*x**4+(QOD/120)*x**5
# the generator to make sample datasets
def generator(start, stop, center, delay, GD=0, GDD=0, TOD=0, FOD=0, QOD=0, resolution=0.1, pulse_duration=15, chirp=0):
window = (np.sqrt(1+chirp**2)*8*np.log(2))/(pulse_duration**2)
lamend = (2*np.pi*C_LIGHT)/start
lamstart = (2*np.pi*C_LIGHT)/stop
lam = np.arange(lamstart, lamend+resolution, resolution)
omega = (2*np.pi*C_LIGHT)/lam
relom = omega-center
i_r = np.exp(-(relom)**2/(window))
i_s = np.exp(-(relom)**2/(window))
i = i_r + i_s + 2*np.sqrt(i_r*i_s)*np.cos(_disp(relom, GD=GD, GDD=GDD, TOD=TOD, FOD=FOD, QOD=QOD)+delay*omega)
#since the _disp polynomial is set to be zero, it's just cos(delay*omega)
return omega, i
def interpol(x,y):
''' Simple linear interpolation '''
xs = np.linspace(x[0], x[-1], len(x))
intp = interp1d(x, y, kind='linear', fill_value = 'extrapolate')
ys = intp(xs)
return xs, ys
def ifft_method(initSpectrumX, initSpectrumY, interpolate=True):
if len(initSpectrumY) > 0 and len(initSpectrumX) > 0:
Ydata = initSpectrumY
Xdata = initSpectrumX
else:
raise ValueError
N = len(Xdata)
if interpolate:
Xdata, Ydata = interpol(Xdata, Ydata)
# the (2*np.pi) division is because we have angular frequency, not frequency
xf = np.fft.fftfreq(N, d=(Xdata[1]-Xdata[0])/(2*np.pi)) * N * Xdata[-1]/(N-1)
yf = np.fft.ifft(Ydata)
else:
pass # some irrelevant code there
return xf, yf
def fft_method(initSpectrumX ,initSpectrumY):
if len(initSpectrumY) > 0 and len(initSpectrumX) > 0:
Ydata = initSpectrumY
Xdata = initSpectrumX
else:
raise ValueError
yf = np.fft.fft(Ydata)
xf = np.fft.fftfreq(len(Xdata), d=(Xdata[1]-Xdata[0])*2*np.pi)
# the problem is there, where I transform the x values.
xf = np.fft.ifftshift(xf)
return xf, yf
# the generated data
x, y = generator(1, 3, 2, delay = 1500, resolution = 0.1)
# plt.plot(x,y)
xx, yy = ifft_method(x,y)
#if the x values are correctly scaled, the two symmetrical spikes should appear exactly at delay value
# plt.plot(xx, np.abs(yy))
#do the cutting there, which is also irrelevant now
# the problem is there, in fft_method. The x values are not the same as before transforms.
xxx, yyy = fft_method(xx, yy)
plt.plot(xxx, np.abs(yyy))
#and it should look like this:
#xs = np.linspace(x[0], x[-1], len(x))
#plt.plot(xs, np.abs(yyy))
plt.grid()
plt.show()

How do I convert the x and y values in polar form from these coupled ODEs to to cartesian form and graph them?

I have written this code to model the motion of a spring pendulum
import numpy as np
from scipy.integrate import odeint
from numpy import sin, cos, pi, array
import matplotlib.pyplot as plt
def deriv(z, t):
x, y, dxdt, dydt = z
dx2dt2=(0.415+x)*(dydt)**2-50/1.006*x+9.81*cos(y)
dy2dt2=(-9.81*1.006*sin(y)-2*(dxdt)*(dydt))/(0.415+x)
return np.array([x,y, dx2dt2, dy2dt2])
init = array([0,pi/18,0,0])
time = np.linspace(0.0,10.0,1000)
sol = odeint(deriv,init,time)
def plot(h,t):
n,u,x,y=h
n=(0.4+x)*sin(y)
u=(0.4+x)*cos(y)
return np.array([n,u,x,y])
init2 = array([0.069459271,0.393923101,0,pi/18])
time2 = np.linspace(0.0,10.0,1000)
sol2 = odeint(plot,init2,time2)
plt.xlabel("x")
plt.ylabel("y")
plt.plot(sol2[:,0], sol2[:, 1], label = 'hi')
plt.legend()
plt.show()
where x and y are two variables, and I'm trying to convert x and y to the polar coordinates n (x-axis) and u (y-axis) and then graph n and u on a graph where n is on the x-axis and u is on the y-axis. However, when I graph the code above it gives me:
Instead, I should be getting an image somewhat similar to this:
The first part of the code - from "def deriv(z,t): to sol:odeint(deriv..." is where the values of x and y are generated, and using that I can then turn them into rectangular coordinates and graph them. How do I change my code to do this? I'm new to Python, so I might not understand some of the terminology. Thank you!
The first solution should give you the expected result, but there is a mistake in the implementation of the ode.
The function you pass to odeint should return an array containing the solutions of a 1st-order differential equations system.
In your case what you are solving is
While instead you should be solving
In order to do so change your code to this
import numpy as np
from scipy.integrate import odeint
from numpy import sin, cos, pi, array
import matplotlib.pyplot as plt
def deriv(z, t):
x, y, dxdt, dydt = z
dx2dt2 = (0.415 + x) * (dydt)**2 - 50 / 1.006 * x + 9.81 * cos(y)
dy2dt2 = (-9.81 * 1.006 * sin(y) - 2 * (dxdt) * (dydt)) / (0.415 + x)
return np.array([dxdt, dydt, dx2dt2, dy2dt2])
init = array([0, pi / 18, 0, 0])
time = np.linspace(0.0, 10.0, 1000)
sol = odeint(deriv, init, time)
plt.plot(sol[:, 0], sol[:, 1], label='hi')
plt.show()
The second part of the code looks like you are trying to do a change of coordinate.
I'm not sure why you try to solve the ode again instead of just doing this.
x = sol[:,0]
y = sol[:,1]
def plot(h):
x, y = h
n = (0.4 + x) * sin(y)
u = (0.4 + x) * cos(y)
return np.array([n, u])
n,u = plot( (x,y))
As of now, what you are doing there is solving this system:
Which leads to x=e^t and y=e^t and n' = (0.4 + e^t) * sin(e^t) u' = (0.4 + e^t) * cos(e^t).
Without going too much into the details, with some intuition you could see that this will lead to an attractor as the derivative of n and u will start to switch sign faster and with greater magnitude at an exponential rate, leading to n and u collapsing onto an attractor as shown by your plot.
If you are actually trying to solve another differential equation I would need to see it in order to help you further
This is what happen if you do the transformation and set the time to 1000:

Bifurcation diagram in matplotlib

I'm trying to acquire the bifurcation diagram for the equation below:
(x is a function of t)
as:
And here is my snippet:
import numpy as np
import matplotlib.pyplot as plt
def pitch(r, x):
return r * x + np.power(x,3)- np.power(x,5)
n = 10000
r = np.linspace(-200, 200, n)
iterations = 1000
last = 100
x = 0
for i in range(iterations):
x = pitch(r,x)
if i >= (iterations - last):
plt.plot(r,x, ',k', alpha=0.02)
plt.title("Bifurcation diagram")
plt.show()
But the generated plot is not what it is supposed to be:
Edit:
Here is my recent attempt:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def pitch(s,x,r):
x = s[0]
dxdt = r * x + np.power(x,3)- np.power(x,5)
return [dxdt]
t = np.linspace(0,100)
s0=[-50]
r = np.linspace(-200, 200)
for i in r:
s = odeint(pitch,s0,t, args=(i,))
plt.plot(s,i,',k', alpha=0.02)
plt.title("Bifurcation diagram")
plt.show()
With this error:
raise ValueError("x and y must have same first dimension") ValueError:
x and y must have same first dimension
Could you give me some advice to fix this problem?!
I found a link to this post and decided to post a few remarks that might be helpful to someone stumbling upon it in the future.
I did not analyze the equation in detail but it is clear from the first sight that something interesting would happen when r is close to 0.
So we could study the behavior of the system for r in [-10,10]
You are right to use odeint instead of solving the Cauchy problem using Euler method coded by yourself.
This equation has an attractor in that it soon "forgets" the initial condition and slides towards the attractor, yet the choice of the attractor depends on where in relation to 0 do we start. Large positive initial conditions would slide to the negative attractor and vice versa as - x^5 is the term that defines the behavior at large x.
What we need to do is for each r in the range put a mark at the attractor that the solution slides to for each initial condition.
We first create a canvas to put marks into:
diagram = np.zeros((200,200))
And then for each combination of (r,s0) we put a point on the canvas at (r,s[-1]).
Here is the complete code
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def pitch(s,x,r):
x = s[0]
dxdt = r * x + np.power(x,3)- np.power(x,5)
return [dxdt]
t = np.arange(0,100,2)
s0=[-50]
N = 200 # Number of points along each side of the diagram
diagram = np.zeros((N,N))
rmin,rmax = -10,10
rrange = np.arange(rmin, rmax,(rmax-rmin)/N)
smin,smax = -5.0,5.0
srange = np.arange(smin,smax,2*(smax-smin)/N)
for i in rrange:
for s0 in srange:
s = odeint(pitch,[s0],t, args=(i,))
imgx = int((i-rmin)*N/(rmax-rmin))
imgy = int((s[-1]-smin)/(smax-smin)*N)
imgx = min(N-1,max(0,imgx)) # make sure we stay
imgy = min(N-1,max(0,imgy)) # within the diagram bounds
diagram[imgy,imgx] = 1
plt.title("Bifurcation diagram")
plt.imshow(np.flipud(diagram),cmap=cm.Greys,
extent=[rmin,rmax,smin,smax],aspect=(rmax-rmin)/(smax-smin))
plt.xlabel("r")
plt.ylabel("x")
plt.show()
And the resulting plot
When you zoom in into the region around 0 by setting (rmin,rmax) to (-0.5,0.5) you could see that the branches of the diagram do not start at 0
Instead as in the diagram drawn in the original post the branches start at roughly r=-0.25

Plot Surface instead of parametric curve

I am working on using the forward difference scheme for numerically solving the diffusion function in one dimension. My final plot of the solution should be a surface where the solution u(x,t) is plotted over a grid of x and t values. I have the problem solved, but I can't get the data to be plotted with the grid representation.
I can think of 2 ways to fix this:
1.) My x and t arrays should be one dimensional, but my u array should be a 2D array. Ultimately, I want a square matrix for u, but I am having a hard time coding that. Currently I have a 1D array for u. Here is the code where u is populated.
u = zeros(Nx+1) # unknown u at new time level
u_1 = zeros(Nx+1) # u at the previous time level
# Set initial condition u(x,0) = I(x)
for i in range(0, Nx+1):
#set initial u's to I(xi)
u_1[i] = 25-x[i]**2
for n in range(0, Nt):
# Compute u at inner mesh points
for i in range(1, Nx):
u[i] = u_1[i] + F*(u_1[i-1] - 2*u_1[i] + u_1[i+1])
2.) The above code returns a 1D array for u, is there a way to plot a 3D surface with 3 1D arrays for x,y,z?
Well..., there is a lot of information you haven't provided. For instance you said you wanted a x,y,z plot but haven't said what the x, y and z should be in the context of your plot. Also z is typically z(x,y).
The following recipe assumes a t and x, and u(t,x) as variables to be put into a surface. I imagine is not exactly your idea but it should be adaptable to your exercise:
EDIT: Also your code (which is in the function computeU in this recipe) had a loop for Nt that does not seem to do anything. I've removed it for the purpose of this example.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
def computeU(Nx,x,F,Nt):
u = np.zeros(Nx+1) # unknown u at new time level
u_1 = np.zeros(Nx+1) # u at the previous time level
# Set initial condition u(x,0) = I(x)
for i in range(0, Nx+1):
#set initial u's to I(xi)
u_1[i] = 25-x[i]**2
#for n in range(0, Nt): # I'm not sure what this is doing. It has no effect.
# Compute u at inner mesh points
for i in range(1, Nx):
u[i] = u_1[i] + F*(u_1[i-1] - 2*u_1[i] + u_1[i+1])
return np.hstack((u[:,np.newaxis],u_1[:,np.newaxis]))
Nx = 10
F = 3
Nt = 5
x = np.arange(11)
t = np.arange(2)
X,Y = np.meshgrid(t,x)
Z = computeU(Nx,x,F,Nt)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,linewidth=0, antialiased=False)
plt.show()
Notice how I've used meshgrid to build new t,x (from 1D arrays) to be mapped against your stack of U arrays (which will have the same shape of X,Y - the new t,x). The result is this:

Categories