I'd like to plot pulse propagation in such a way at each step, it plots the pulse shape. In other words, I want a serie of x-z plots, for each values of y. Something like this (without color):
How can I do this using matplotlib (or Mayavi)? Here is what I did so far:
def drawPropagation(beta2, C, z):
""" beta2 in ps / km
C is chirp
z is an array of z positions """
T = numpy.linspace(-10, 10, 100)
sx = T.size
sy = z.size
T = numpy.tile(T, (sy, 1))
z = numpy.tile(z, (sx, 1)).T
U = 1 / numpy.sqrt(1 - 1j*beta2*z * (1 + 1j * C)) * numpy.exp(- 0.5 * (1 + 1j * C) * T * T / (1 - 1j*beta2*z*(1 + 1j*C)))
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
surf = ax.plot_wireframe(T, z, abs(U))
Change to:
ax.plot_wireframe(T, z, abs(U), cstride=1000)
and call:
drawPropagation(1.0, 1.0, numpy.linspace(-2, 2, 10))
will create the following graph:
If you need the curve been filled with white color:
import numpy
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot
from matplotlib.collections import PolyCollection
def drawPropagation(beta2, C, z):
""" beta2 in ps / km
C is chirp
z is an array of z positions """
T = numpy.linspace(-10, 10, 100)
sx = T.size
sy = z.size
T = numpy.tile(T, (sy, 1))
z = numpy.tile(z, (sx, 1)).T
U = 1 / numpy.sqrt(1 - 1j*beta2*z * (1 + 1j * C)) * numpy.exp(- 0.5 * (1 + 1j * C) * T * T / (1 - 1j*beta2*z*(1 + 1j*C)))
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
U = numpy.abs(U)
verts = []
for i in xrange(T.shape[0]):
verts.append(zip(T[i, :], U[i, :]))
poly = PolyCollection(verts, facecolors=(1,1,1,1), edgecolors=(0,0,1,1))
ax.add_collection3d(poly, zs=z[:, 0], zdir='y')
ax.set_xlim3d(numpy.min(T), numpy.max(T))
ax.set_ylim3d(numpy.min(z), numpy.max(z))
ax.set_zlim3d(numpy.min(U), numpy.max(U))
drawPropagation(1.0, 1.0, numpy.linspace(-2, 2, 10))
pyplot.show()
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'd like to create a plot with a specified figure size in which the 3D axes try to use the entire available space while maintaining an equal aspect ratio on all axis.
My current attempt shows a clipping rectangle that hides part of the mesh.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(5, 2.5))
ax = fig.add_subplot(projection='3d')
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1, 1, 1, 0.5]))
u = v = np.linspace(0, 2 * np.pi, 50)
u, v = np.meshgrid(u, v)
X = np.cos(v) * (6 - (5/4 + np.sin(3 * u)) * np.sin(u - 3 * v))
Y = (6 - (5/4 + np.sin(3 * u)) * np.sin(u - 3 * v)) * np.sin(v)
Z = -np.cos(u - 3 * v) * (5/4 + np.sin(3 * u))
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, color=[0.7] * 3, linewidth=0.25, edgecolor="k")
ax.set_box_aspect([ub - lb for lb, ub in (getattr(ax, f'get_{a}lim')() for a in 'xyz')])
plt.show()
What can I do?
I copied the code you provided and just add 4 lines at the end.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
plt.close()
fig = plt.figure(figsize=(5, 2.5))
ax = fig.add_subplot(projection='3d')
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1, 1, 1, 0.5]))
u = v = np.linspace(0, 2 * np.pi, 50)
u, v = np.meshgrid(u, v)
X = np.cos(v) * (6 - (5/4 + np.sin(3 * u)) * np.sin(u - 3 * v))
Y = (6 - (5/4 + np.sin(3 * u)) * np.sin(u - 3 * v)) * np.sin(v)
Z = -np.cos(u - 3 * v) * (5/4 + np.sin(3 * u))
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, color=[0.7] * 3, linewidth=0.25, edgecolor="k")
# ax.set_box_aspect([ub - lb for lb, ub in (getattr(ax, f'get_{a}lim')() for a in 'xyz')])
left, right = plt.xlim()
ax.set_zlim(left, right)
ax.set_ylim(left, right)
plt.tight_layout()
I comment out the line ax.set_box_aspect since it gives me an error. The output of the above is:
--- edit ---
I have an idea for a workaround to make it works in matplotlib v3.4.2:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import os
plt.close()
# your code starts here, with a little modification
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(projection='3d')
u = v = np.linspace(0, 2 * np.pi, 50)
u, v = np.meshgrid(u, v)
X = np.cos(v) * (6 - (5/4 + np.sin(3 * u)) * np.sin(u - 3 * v))
Y = (6 - (5/4 + np.sin(3 * u)) * np.sin(u - 3 * v)) * np.sin(v)
Z = -np.cos(u - 3 * v) * (5/4 + np.sin(3 * u))
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, color=[0.7] * 3, linewidth=0.25, edgecolor="k")
# set the axes limits
left, right = plt.xlim()
ax.set_zlim(left, right)
ax.set_ylim(left, right)
# zoom in to the plot
ax.dist = 6
# make everything other than the plot itself transparent
fig.patch.set_alpha(0)
ax.patch.set_alpha(0)
ax.axis('off')
plt.tight_layout()
# save plot as image
plt.savefig('plotted.png')
# remove the axes where the image was plotted
ax.remove()
# resize figsize
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(5, 2.5)
# add fake axes for gridlines as in 3d plot to make it look like a real plot
# skip this part if the gridlines are unnecessary
ax_bg = fig.add_subplot(111, projection='3d')
ax_bg.dist = 3
# add axes in cartesian coordinates (xy-plane) for the image
ax = fig.add_subplot(111)
fig.patch.set_alpha(1)
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
im = plt.imread('plotted.png')
h, w, dc = im.shape # (height=500, width=500, depth/color=4)
im_cropped = im[120:390, :, :] # this is manually adjusted
ax.axis('off')
ax.imshow(im_cropped)
# delete the saved image
os.remove('plotted.png')
Output is:
I don't know if it will work in your particular context, but it works for just fulfilling your question.
Let me know if something is unclear.
This snippet of code is going to generate the following two pictures, which represents a complex function. Is it possible to apply the colors of the first image to the surface? If so, how?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import hsv_to_rgb
def saw_func(x, dx, a, b):
x = x / dx - np.floor(x / dx)
return a + (b - a) * x
def domain_coloring(mag, arg, phaseres=20):
arg[arg < 0] += 2 * np.pi
arg /= (2 * np.pi)
blackp = saw_func(arg, 1 / phaseres, 0.75, 1)
blackm = saw_func(np.log(mag), 2 * np.pi / phaseres, 0.75, 1)
black = blackp * blackm
H = arg
S, V = np.ones_like(H), black
return (hsv_to_rgb(np.dstack([H, S, V])) * 255).astype(np.uint8)
x = y = np.linspace(-2, 2, 500)
x, y = np.meshgrid(x, y)
z = x + 1j * y
f = (z - 1) / (z**2 + z + 1)
mag, arg = np.absolute(f), np.angle(f)
img = domain_coloring(mag, arg)
fig1, ax1 = plt.subplots()
ax1.imshow(
img,
extent = [np.amin(x), np.amax(x), np.amin(y), np.amax(y)],
interpolation = "nearest",
origin = "lower",
)
plt.show()
fig2 = plt.figure()
ax2 = fig2.add_subplot(1, 1, 1, projection="3d")
ax2.plot_surface(x, y, mag)
ax2.set_zlim([0, 10])
plt.show()
This tutorial example uses a parameter facecolors=. The colors need to be rgb values between 0 and 1. The example code uses a 200x200 grid, as 500x500 is rather slow (and also has more problems with artifacts at the asymptotes). rstride and cstride are set to 1 as default plot_surface skips points.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import hsv_to_rgb
def saw_func(x, dx, a, b):
x = x / dx - np.floor(x / dx)
return a + (b - a) * x
def domain_coloring(mag, arg, phaseres=20):
arg[arg < 0] += 2 * np.pi
arg /= (2 * np.pi)
blackp = saw_func(arg, 1 / phaseres, 0.75, 1)
blackm = saw_func(np.log(mag), 2 * np.pi / phaseres, 0.75, 1)
black = blackp * blackm
H = arg
S, V = np.ones_like(H), black
return hsv_to_rgb(np.dstack([H, S, V]))
x = y = np.linspace(-2, 2, 200)
x, y = np.meshgrid(x, y)
z = x + 1j * y
f = (z - 1) / (z**2 + z + 1)
mag, arg = np.absolute(f), np.angle(f)
img = domain_coloring(mag, arg)
fig2 = plt.figure()
ax2 = fig2.add_subplot(1, 1, 1, projection="3d")
ax2.plot_surface(x, y, mag, facecolors=img)
ax2.set_zlim([0, 10])
plt.show()
The idea is to plot the following vector field:
I have two main issue with it:
1) I do not know how to make sure that the arrows are not too long (I know I have to use length, but how?).
2) I am told to use Numpyto draw the vector field but again, how?
This is what I have tried:
# The components of the vector field
F_x = y*e**x
F_y = x**2 + e**x
F_z = z**2*e**z
# The grid
xf = np.linspace(-0.15, 2.25, 8)
yf = np.linspace(-0.15, 2.25, 8)
zf = np.linspace(-0.75, 2.50, 8)
X_grid, Y_grid, Z_grid = np.meshgrid(xf, yf, zf)
# The arrows; how to deal with them?
dx = 1
#dy = ...
#dz = ...
# Standardize the arrows; In this way all arrows have the same length.
length = np.sqrt(dx**2 + dy**2 + dz**2)
dx_N = dx/length
dy_N = dy/length
dz_N = dz/length
#how to involve numpy in the process??
# Drawing the figure
fig, ax = plt.subplots(1, 1)
ax.quiver(X_grid, Y_grid, Z_grid, dx_N, dy_N, dz_N, dy, dz, cmap=plt.get_cmap('gnuplot2'))
plt.show()
Thanks
EDIT
Based on the provided link I tried:
from sympy import *
x,y,z = sp.symbols('x y z', real = True)
import matplotlib.pyplot as plt
x, y, z = np.meshgrid(np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2))
F_x = y * exp(x)
F_y = x**2 + exp(x)
F_z = z**2 * exp(z)
# Normalize the arrows:
F_x = F_x / np.sqrt(F_x**2 + F_y**2 + F_z**2)
F_y = F_y / np.sqrt(F_x**2 + F_y**2 + F_z**2)
F_z = F_z / np.sqrt(F_x**2 + F_y**2 + F_z**2)
plt.figure()
plt.title('Vector field')
Q = plt.quiver(x, y, z, F_x, F_y, F_z, units='width')
qk = plt.quiverkey(Q, 0.9, 0.9, 2, r'$2 \frac{m}{s}$', labelpos='E',
coordinates='figure')#I don't understand this line
The TypeError: Shape should contain integers only comes up.
The problem is that I don't understand this part of the code:
qk = plt.quiverkey(Q, 0.9, 0.9, 2, r'$2 \frac{m}{s}$', labelpos='E',
coordinates='figure')
I am still stuck on how to plot this vector field
Assume that you want a 3D quiver, you can check out the matplotlib tutorial on quiver3D. And to control the arrow size, check out the Axes3d.quiver library doc, especially the parameters.
A quick snippet:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
x, y, z = np.meshgrid(np.arange(0, 2*np.pi, .5), np.arange(0, 2*np.pi, .5), np.arange(0, 2*np.pi, .5))
F_x = y * np.exp(x)
F_y = x**2 + np.exp(x)
F_z = z**2 * np.exp(z)
fig = plt.figure()
ax = fig.gca(projection='3d')
Q = ax.quiver(x, y, z, F_x, F_y, F_z, length=0.3, normalize=True)
But 3d quiver plot can be very crowded! : )
The quiver() method is a great tool to render vector fields. Since Matplotlib is a two-dimensional plotting library, we need to import the mplot3d toolkit to generate a three-dimensional plot.
Here's a good example:
Dependencies:
Axes3D for 3D rendering
Pyplot to get a MATLAB-like plotting framework
Numpy for numeric-array manipulation
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.25),
np.arange(-0.8, 1, 0.25),
np.arange(-0.8, 1, 0.8))
u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) * np.sin(np.pi * z))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(x, y, z, u, v, w,
length=0.15,
color='Purple'
)
ax.view_init(elev=10, azim=30)
ax.dist=8
plt.show()
Win 10 x64 Anaconda Python 2.7
I'm plotting an involute spiral onto a Gaussian surface with the following code..
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Spiral parameters
samNum = 1000
spConst = 10.0
t = np.linspace(0, 6*np.pi, samNum)
# Coordinates of involute spiral on xy-plane
coords = np.zeros([samNum, 3])
coords[:,0] = spConst * (np.cos(t) + t * np.sin(t)) # x coord
coords[:,1] = spConst * (np.sin(t) - t * np.cos(t)) # y coord
# Paramters for 2D Gaussian surface
amp = 200
sigma_x = 75.0
sigma_y = 75.0
theta = np.pi
a = np.cos(theta)**2 / (2 * sigma_x**2) + np.sin(theta)**2 / (2 * sigma_y**2)
b = -np.sin(2 * theta) / (4 * sigma_x**2) + np.sin(2 * theta) / (4 * sigma_y**2)
c = np.sin(theta)**2 / (2 * sigma_x**2) + np.cos(theta)**2 / (2 * sigma_y**2)
# z coords of spiral projected onto Gaussian surface
coords[:,2] = amp * np.exp(-(a * coords[:,0]**2 - 2 * b * coords[:,0]*coords[:,1] + c * coords[:,1]**2)) # z coord
# plot 3D spiral
ax.scatter(coords[:,0], coords[:,1], coords[:,2], s=1, c='k')
# plot lines projecting 3D spiral on to the xy-plane
for p in range(samNum):
ax.plot([coords[p,0], coords[p,0]], [coords[p,1], coords[p,1]], [0, coords[p,2]], color='g', linewidth=0.1)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
This gives the following output...
I would like to convert the green ribbon into a continuous surface. I have had a look at parametric surfaces in matplotlib but cant get my head around how to covert this into a surface.
So is this possible? Any pointers appreciated.
In principle you have everything you need already there,
t = np.linspace(0, 6*np.pi, samNum)
T, Z = np.meshgrid(t, [0,1])
X = spConst * (np.cos(T) + T* np.sin(T))
Y = spConst * (np.sin(T) - T * np.cos(T))
gives you the X and Y coordinates, and the upper Z coordinate is obtained via Z[1,:] = coords[:,2].
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Spiral parameters
samNum = 1000
spConst = 10.0
t = np.linspace(0, 6*np.pi, samNum)
T, Z = np.meshgrid(t, [0,1])
X = spConst * (np.cos(T) + T* np.sin(T))
Y = spConst * (np.sin(T) - T * np.cos(T))
# Coordinates of involute spiral on xy-plane
coords = np.zeros([samNum, 3])
coords[:,0] = spConst * (np.cos(t) + t * np.sin(t)) # x coord
coords[:,1] = spConst * (np.sin(t) - t * np.cos(t)) # y coord
# Paramters for 2D Gaussian surface
amp = 200
sigma_x = 75.0
sigma_y = 75.0
theta = np.pi
a = np.cos(theta)**2 / (2 * sigma_x**2) + np.sin(theta)**2 / (2 * sigma_y**2)
b = -np.sin(2 * theta) / (4 * sigma_x**2) + np.sin(2 * theta) / (4 * sigma_y**2)
c = np.sin(theta)**2 / (2 * sigma_x**2) + np.cos(theta)**2 / (2 * sigma_y**2)
# z coords of spiral projected onto Gaussian surface
coords[:,2] = amp * np.exp(-(a * coords[:,0]**2 - 2 * b * coords[:,0]*coords[:,1] + c * coords[:,1]**2)) # z coord
Z[1,:] = coords[:,2]
ax.plot_surface(X,Y,Z)
# plot 3D spiral
ax.scatter(coords[:,0], coords[:,1], coords[:,2], s=1, c='k')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()