Plotting 3D vectors using matplotlib - python

I followed this thread in order to implement plotting 3D vectors.
When running the code with the given examples of vectors it seems fine, but when I try to use my own vectors I'm getting vectors without direction:
I'm trying to understand what is wrong with my data.
def plot_vectors(vectors):
fig = plt.figure()
# ax = fig.gca(projection='3d')
# ax = Axes3D(fig)
ax = fig.add_subplot(111, projection='3d')
for vector in vectors:
# v1 = np.array([vector[0], vector[1], vector[2]])
v_length = np.linalg.norm(vector)
x = vector[0]
y = vector[1]
z = vector[2]
# Make the direction data for the arrows
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))
ax.quiver(x, y, z, u, v, w, pivot='tail', length=v_length, arrow_length_ratio=0.3 / v_length)
# ax.quiver(vector[0], vector[1], vector[2], vector[3], vector[4], vector[5],
# pivot='tail', length=vlength, arrow_length_ratio=0.3 / v_length)
# ax.set_xlim([-4, 4])
# ax.set_ylim([-4, 4])
# ax.set_zlim([0, 4])
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
run it with these vectors for example:
plot_vectors([[95.38065011522669, -15.867331426507993, 267.4801091009956],
[-52.379559088282384, -1.0021180591632532, 163.80875985057938]])

I ran your code and looks like using .3 / v_length for the arrow_length_ratio yields a super tiny arrow head for your values of x, y, and z. I would use a different calculation here...
perhaps something like .001 * v_length will work in this case.
I would play around with it until you find something that you like and that works for all your data!

Related

Correctly interpolate 3D vector field with python scipy

GOAL
My goal is to interpolate a 3D vector field using python.
CODE
Original Vector field
import numpy as np
import matplotlib.pyplot as plt
# For interpolation
from scipy.interpolate import RegularGridInterpolator
#%% VECTOR FIELD
xx, yy, zz = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
uu = np.sin(np.pi * xx) * np.cos(np.pi * yy) * np.cos(np.pi * zz)
vv = -np.cos(np.pi * xx) * np.sin(np.pi * yy) * np.cos(np.pi * zz)
ww = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * xx) * np.cos(np.pi * yy) *
np.sin(np.pi * zz))
# Ravel -> make 1D lists
x = np.ravel(xx)
y = np.ravel(yy)
z = np.ravel(zz)
u = np.ravel(uu)
v = np.ravel(vv)
w = np.ravel(ww)
Interpolation Function (something is wrong here)
#%% INTERPOLATION FUNCTION
def interpolate_field(x,y,z,u,v,w,new_points):
x = np.unique(x)
y = np.unique(y)
z = np.unique(z)
u = np.reshape(u, (len(x), len(y), len(z)))
v = np.reshape(u, (len(x), len(y), len(z)))
w = np.reshape(u, (len(x), len(y), len(z)))
u_int_f = RegularGridInterpolator((x, y, z), u)
v_int_f = RegularGridInterpolator((x, y, z), v)
w_int_f = RegularGridInterpolator((x, y, z), w)
u_int = u_int_f(new_points)
v_int = v_int_f(new_points)
w_int = w_int_f(new_points)
return u_int, v_int, w_int
Evaluate interpolation at new points
#%% EVALUATE INTERPOLATION FUNCTION
new_grid = np.meshgrid(
np.linspace(np.min(x), np.max(x), 20),
np.linspace(np.min(y), np.max(y), 20),
np.linspace(np.min(z), np.max(z), 3)
, indexing="xy")
# create list of new_points
new_points = np.vstack(list(map(np.ravel, new_grid))).T
# get vector field values at new points
uint, vint, wint = interpolate_field(x,y,z,u,v,w,new_points)
new_points = np.array(new_points)
xn = new_points[:,0]
yn = new_points[:,1]
zn = new_points[:,2]
# PLOT
fig = plt.figure(dpi=300)
ax = fig.gca(projection='3d')
ax.quiver(xn, yn, zn, uint, vint, wint, length=0.1)
plt.show()
THE ISSUE
As you can see something is wrong with the interpolation function since the resulting vector plot does not show the same behavior as the original at all.
Here is a way to do it using RegularGridInterpolator:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import RegularGridInterpolator
def interp_field(field, coords, shape):
interpolator = RegularGridInterpolator(
(gridZ[:, 0, 0], gridY[0, :, 0], gridX[0, 0, :]), field)
return interpolator(coords).reshape(shape)
gridZ, gridY, gridX = np.mgrid[-0.8:1:3j, -0.8:1:10j, -0.8:1:10j]
u = np.sin(np.pi * gridX) * np.cos(np.pi * gridY) * np.cos(np.pi * gridZ)
v = -np.cos(np.pi * gridX) * np.sin(np.pi * gridY) * np.cos(np.pi * gridZ)
w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * gridX) * np.cos(np.pi * gridY) *
np.sin(np.pi * gridZ))
interp_gridZ, interp_gridY, interp_gridX = np.mgrid[
-0.8:1:3j, -0.8:1:20j, -0.8:1:20j]
interp_coords = np.column_stack(
(interp_gridZ.flatten(), interp_gridY.flatten(), interp_gridX.flatten()))
interp_u = interp_field(u, interp_coords, interp_gridX.shape)
interp_v = interp_field(v, interp_coords, interp_gridX.shape)
interp_w = interp_field(w, interp_coords, interp_gridX.shape)
fig, (ax, bx) = plt.subplots(ncols=2, subplot_kw=dict(projection='3d'),
constrained_layout=True)
ax.quiver(gridX, gridY, gridZ, u, v, w, length=0.3)
bx.quiver(interp_gridX, interp_gridY, interp_gridZ,
interp_u, interp_v, interp_w, length=0.3)
plt.show()
The resulting plot looks like that:

Plotting 3D Vector Field

I am trying to plot a 3D vector field. I used the following example as guidance:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
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))
ax.quiver(x, y, z, u, v, w, length=0.1)
plt.show()
This example comes from the matplotlib examples library. However, I wanted to try a different function to replace u, v and w like this:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
x,y,z = np.meshgrid(np.arange(-0.1,0.005,0.1),np.arange(-0.1,0.005,0.1),np.arange(-0.1,0.005,0.1))
VolMag = 3.218E-6 #Volume of magnet in experiment in m^3
BR = np.sqrt(x**2 + y**2 + z**2)
MagMoment = np.array([0,0,(BInput*VolMag)/u0])
Bx = (u0/(4*np.pi))*(3*x*MagMoment*z)/BR**5
By = (u0/(4*np.pi))*(3*y*MagMoment*z)/BR**5
Bz = (u0/(4*np.pi))*((3*z*MagMoment*(z**2)/BR**5 - MagMoment/BR**3)
ax.quiver(x,y,z,Bx,By,Bz,length=0.1)
plt.show()
This is giving me an "invalid syntax" error for line 16. Why is this? I only changed the function and some of the names of the definitions.
It looks like your missing a closing parenthesis on this line.
Bz = (u0/(4*np.pi))*((3*z*MagMoment*(z**2)/BR**5 - MagMoment/BR**3)
should be
Bz = (u0/(4*np.pi))*((3*z*MagMoment*(z**2)/BR**5 - MagMoment/BR**3))

How to plot a spherical segment in Python?

How can one plot a spherical segment, specifically a sphere "slice" in Python?
I know how to plot a sphere surface via
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
plt.show()
or some variation of that code, but I'm struggeling to plot only a part of the sphere, leading to an image like the following:
https://en.wikipedia.org/wiki/Spherical_segment#/media/File:LaoHaiKugelschicht1.png
If I vary the code I presented above by manipulating the definitions of u and v, e.g.:
u = np.linspace(0, 2 * np.pi, 20)
v = np.linspace(0, np.pi, 20)
the sphere is still presented as a whole, but with a very poor resolution.
Changing the starting point of the linspace range doens't seem to change anything.
I have figured out something that works for me. Here you go:
q = 0.5 # defines upper starting point of the spherical segment
p = 0.8 # defines ending point of the spherical segment as ratio
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(q, p * np.pi, p * 100)
x = r * np.outer(np.cos(u), np.sin(v)) + x_0
y = r * np.outer(np.sin(u), np.sin(v)) + y_0
z = r * np.outer(np.ones(np.size(u)), np.cos(v)) + z_0
ax.plot_surface(x, y, z, color='b')

Adding colors to a 3d quiver plot in matplotlib

I want to have colors corresponding to a colormap in my 3d quiver plot. The 2d version of the plot has an optional array that is used to map colors to the arrows. How can I create the same effect in the 3d version?
3D quiver plots are a brand-new feature in 1.4 it (and it's documentation) might still be a bit rough around the edges. In this case we can try to use the fact that the quiver is implemented as a LineCollection which (eventually) inherits from ScalarMappable which means it knows what a colormap is and the returned artist has the method set_array.
Building on the docs here
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
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))
q = ax.quiver(x, y, z, u, v, w, length=0.1, cmap='Reds', lw=2)
q.set_array(np.random.rand(np.prod(x.shape)))
plt.show()
However, you will note the heads are a different color than the shaft which is due to a implementation detail of the way that it is implemented each part is drawn as it's own line.
Directly using the Norm and color map functions and passing the result to colors might be a better course.
You can use colors argument to specify custom colors for each arrow, although the way to do it is not at all straight-forward (as for matplotlib 2.0.0). I have specified in this issue the logic of how the quiver plot is actually drawn and a work-around to specify coloring. You can check this gist for a simple example which can generate a graph similar to this one:
To summarize, here are the steps to follow:
suppose you have a list (of size x*y*z) of 3-tuples (or 4-tuples for RGBA) specifying the RGB values (between 0~1) of each vector to be drawn.
filter out the RGB (or RGBA) tuples corresponding to vectors of length 0 since they won't be drawn actually.
let [color_1, color_2, ..., color_n] be the list you obtained after step 2, you should specify colors=[color_1, color_2, ..., color_n, color_1, color_1, color_2, color_2, ..., color_n, color_n]since actually the "-" part (consisting of 1 line) of all the non-zero arrows "->" wil be drawn first, then comes the ">" part (consisting of 2 lines).
Hope this helps.
Building on #tacaswell and #sytrus answers, here is an example of coloring a 3d quiver plot
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
# Make the grid
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
# Make the direction data for the arrows
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))
# Color by azimuthal angle
c = np.arctan2(v, u)
# Flatten and normalize
c = (c.ravel() - c.min()) / c.ptp()
# Repeat for each body line and two head lines
c = np.concatenate((c, np.repeat(c, 2)))
# Colormap
c = plt.cm.hsv(c)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(x, y, z, u, v, w, colors=c, length=0.1, normalize=True)
plt.show()
Expanding on the answer from #slek120. I had an issue where vectors of length zero were present. These messed up the correspondence between the arrow tip colors. My solution is to give them a nonzero length and make them transparent. For some reason that I don't understand, simply discarding them didn't work. Adding a small change to the last part, a colorbar can be included as well. The colorbar asks explicitely for q.set_array(). This changes the color, but q.set_edgecolor(c); q.set_facecolor(c) lets you insert your custom colormap.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
cmap = 'hsv'
# Make the grid
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
# Make the direction data for the arrows
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)
# check what happens if all values are zero
# no quivers are plotted, colors don't match anymore
u[:,2:4] = v[:,2:4] = w[:,2:4] = 0
# change values that are zero to something close to zero
uvw = np.vstack((u[np.newaxis],v[np.newaxis],w[np.newaxis]))
norm = np.linalg.norm(uvw, axis = 0)
max_norm = np.max(norm)
mask = norm == 0
min_norm = 0.3 # you want every arrow to be longer than this fraction of max_norm
# rescale vs for illustrative purposes, so small vectors become visible
# and zero vectors become nonzero so colors of the arrow shaft and head correspond. Later these are made transparent
uvw = uvw + min_norm * np.tile(mask[np.newaxis], (3, 1, 1, 1)) / max_norm
# recalculate norms so you don't divide by zero
norm = np.linalg.norm(uvw, axis=0)
uvw = min_norm * uvw / norm + (1 - min_norm) * uvw / max_norm
u, v, w = uvw
# Color by azimuthal angle
c = np.arctan2(v, u)
# Flatten and normalize
c = (c.ravel() - c.min()) / c.ptp()
# Adjust for missing quivers
# c = c[np.nonzero((u.ravel() != 0) * (v.ravel() != 0) * (w.ravel() != 0))]
# Repeat for each body line and two head lines
c = np.concatenate((c, np.repeat(c, 2)))
repeated_mask = np.concatenate((mask.ravel(), np.repeat(mask.ravel(), 2)))
# Colormap
c = getattr(plt.cm, cmap)(c)
# set zero values transparent, you made them nonzero not to mess up the tip colors
c[repeated_mask, 3] = 0.1
fig = plt.figure()
ax = fig.gca(projection='3d')
q = ax.quiver(x, y, z, u, v, w, cmap = cmap, length=0.1)
q.set_array(np.linspace(0,max_norm,10))
fig.colorbar(q)
q.set_edgecolor(c)
q.set_facecolor(c)
plt.show()

Matplotlib plot pulse propagation in 3d

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()

Categories