Filling area below function on 3d plot of 2d slices in Matplotlib - python

I would like to make a 3D plot with several 2D line plot "slices" and shade the area between the x-axis and the curve (i.e. under the curve). When trying to do this with polygons I am getting filling but the correct areas are not being filled. Any help would be most appreciated!
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(15,15))
ax = fig.add_subplot(111, projection='3d')
colors = ['r','b','g','m']
phi = [0,np.pi/4,np.pi/3, np.pi/2]
for c, k in zip(colors, phi):
eps2 = 0.001j
eps = np.linspace(-3,3,10000)
E = eps + eps2
gR = ((1-(((np.cos(k)+np.sin(k)*1j)**2)/((E+np.sqrt(1-E**2)*1j)**4)))/(1+(((np.cos(k)+np.sin(k)*1j)**2)/((E+np.sqrt(1-E**2)*1j)**4))))*1j
N = gR.imag
utol = 2
N[N>utol] = 2
ax.plot(eps, N, k,zdir='y', color=c)
verts = [list(zip(eps,N))]
poly = PolyCollection(verts, facecolors=c)
poly.set_alpha(1)
ax.add_collection3d(poly, zs=k,zdir='y')
ax.set_xlabel('Energy')
ax.set_ylabel('Phi')
ax.set_zlabel('DOS')
ax.set_yticks(phi)
ax.set_zlim(0,2)
ax.set_ylim(0,2)
plt.show()
Incorrect Plot for reference:

You created a polygon by connecting the first and last vertex of your curves. As these vertices have y = 2 everything gets connected with the horizontal line at that y-value.
To close the polygon at zero, repeat the first and the last x-value (np.pad(eps, 1, mode='edge')) and pad the y-values with a zero at both ends (np.pad(N, 1)).
If desired, ax.set_yticklabels(...) can show the y-ticks as a formula with pi.
Further, matplotlib seems to have a serious problem about deciding the relative depth of each polygon, showing them all mixed up. A workaround could be to rotate everything 180 degrees, e.g. by setting ax.view_init(elev=22, azim=130).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(15, 15))
ax = fig.add_subplot(111, projection='3d')
colors = ['r', 'b', 'g', 'm']
phi = [0, np.pi / 4, np.pi / 3, np.pi / 2]
for c, k in zip(colors, phi):
eps2 = 0.001j
eps = np.linspace(-3, 3, 10000)
E = eps + eps2
gR = ((1 - (((np.cos(k) + np.sin(k) * 1j) ** 2) / ((E + np.sqrt(1 - E ** 2) * 1j) ** 4))) / (
1 + (((np.cos(k) + np.sin(k) * 1j) ** 2) / ((E + np.sqrt(1 - E ** 2) * 1j) ** 4)))) * 1j
N = gR.imag
utol = 2
N[N > utol] = 2
ax.plot(eps, N, k, zdir='y', color=c)
verts = [list(zip(np.pad(eps, 1, mode='edge'), np.pad(N, 1)))]
poly = PolyCollection(verts, facecolors=c)
poly.set_alpha(1)
ax.add_collection3d(poly, zs=k, zdir='y')
ax.set_xlabel('Energy')
ax.set_ylabel('Phi')
ax.set_zlabel('DOS')
ax.set_yticks(phi)
ax.set_yticklabels(['$0$' if k == 0 else f'$\pi / {np.pi / k:.0f}$' for k in phi])
ax.set_zlim(0, 2)
ax.set_ylim(0, 2)
ax.view_init(elev=22, azim=130)
plt.show()

Related

Conformal Color Mapping of Complex Functions with 3 or more variables

I used the following code to color map the complex plane onto a hypocycloid. No problems there.
import numpy as np
import matplotlib.pyplot as plt
#setting up the meshgrid for the input space
a = np.arange(-np.pi, np.pi, 0.1)
b = np.arange(-np.pi, np.pi, 0.1)
A, B = np.meshgrid(a, b)
#setting parameters for plotting a circle
angle = np.arange(0, 2*np.pi, 2*np.pi/100)
R = 1
p = R*np.cos(angle)
q = R*np.sin(angle)
#defining a function to color the plane
fx = np.sqrt(A**2 + B**2)
fig, (ax1, ax2) = plt.subplots(1, 2,figsize=(10,5))
ax1.pcolormesh(A,B, fx)
y = (1/3)*(np.exp(1j*A) + np.exp(1j*(B-A)) + np.exp(-1j*B))
# extract real and imaginary parts using numpy
real = y.real
imag = y.imag
# plot the complex numbers
ax2.scatter(real, imag, c=fx, s=7, edgecolor='k', linewidths=0.1)
ax2.plot(p, q, '--k')
#labeling plots
ax1.set_xlim(-np.pi, np.pi)
ax1.set_ylim(-np.pi, np.pi)
ax1.set_ylabel('Imaginary')
ax1.set_xlabel('Real')
ax1.set_title('Input Plane')
ax2.set_xlim(-1.05, 1.05)
ax2.set_ylim(-1.05, 1.05)
ax2.set_ylabel('Imaginary')
ax2.set_xlabel('Real')
ax2.set_title('Output Plane')
plt.grid('--k', axis='both')
plt.show()
Now I would like to extend this so that I can add more variables. For example, using the function y = np.exp(1jA) + np.exp(1j(B-A)) + np.exp(1j*(C-B)) + np.exp(1j*(D-C)) + np.exp(-1j*D)
which would require a 4D meshgrid?? Somehow, updating the meshgrid and using this function doesn't work. How can I fix this?

How to draw circles on the perimeter of a circle?

I'm trying to plot something like this:
I don't know how to find the center of smaller circles in for loops. First, I've tried to plot it with smaller number of circles(for example 2) but I don't know why the smaller circles are semi-circles??
My try:
import numpy as np
import matplotlib.pyplot as plt
r = 2, h = 1, k = 1
axlim = r + np.max((abs(h),np.max(abs(k))))
x = np.linspace(-axlim, axlim, 100)
X,Y = np.meshgrid(x,x)
F = (X-h)**2 + (Y-k)**2 - r**2
plt.contour(X,Y,F,0)
F1 = (X-(h+r))**2 + (Y-k)**2 - (r/3)**2
plt.contour(X,Y,F1,0)
F2 = (X-h)**2 + (Y-(k+r))**2 - (r/3)**2
plt.contour(X,Y,F2,0)
plt.gca().set_aspect('equal')
plt.axis([-4*r, 4*r, -4*r,4*r])
# plt.axis('off')
plt.show()
The output:
Sine, cosine and an angle evenly divided over the range 0, 2picould be used:
import numpy as np
import matplotlib.pyplot as plt
num_circ = 7
rad_large = 7
rad_small = 6
thetas = np.linspace(0, 2 * np.pi, num_circ, endpoint=False)
fig, ax = plt.subplots()
ax.add_patch(plt.Circle((0, 0), rad_large, fc='none', ec='navy'))
for theta in thetas:
ax.add_patch(plt.Circle((rad_large * np.cos(theta), rad_large * np.sin(theta),), rad_small, fc='none', ec='crimson'))
ax.autoscale_view() # calculate the limits for the x and y axis
ax.set_aspect('equal') # show circles as circles
plt.show()

Rounding the edges of a cylinder in matplotlib poly3D

I have the following code which produces a cylinder-like object using matplotlib:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi,nz=7,20
r=1 # radius of cylinder
phi = np.linspace(0,360, nphi)/180.0*np.pi
z= np.linspace(0,1.0,nz)
print z
cols=[]
verts2 = []
for i in range(len(phi)-1):
cp0= r*np.cos(phi[i])
cp1= r*np.cos(phi[i+1])
sp0= r*np.sin(phi[i])
sp1= r*np.sin(phi[i+1])
for j in range(len(z)-1):
z0=z[j]
z1=z[j+1]
verts=[]
verts.append((cp0, sp0, z0))
verts.append((cp1, sp1, z0))
verts.append((cp1, sp1, z1))
verts.append((cp0, sp0, z1))
verts2.append(verts)
value=np.random.rand()
#print value
col=plt.cm.rainbow(0.9)
#print col
cols.append(col)
poly3= Poly3DCollection(verts2, facecolor=cols,edgecolor = "none" )
poly3.set_alpha(0.8)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-1, 1)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 1)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 1)
plt.show()
This code produces the following image:
However as you can see the are sharp corners in the figure. Is there anyway to make these edges rounder so that the figure looks like a proper cylinder with a circular cross-section as opposed to a hexagonal cross-section?
The third argument to
np.linspace
controls how many values you want it to generate. Thus, nphi controls the
number of values in phi, and nz controls the number of values in z:
phi = np.linspace(0,360, nphi)/180.0*np.pi
z = np.linspace(0,1.0,nz)
So if you increase nphi, then you'll get more points along the circle:
cp0 = r*np.cos(phi[i])
sp0 = r*np.sin(phi[i])
For example, try changing nphi, nz = 7,20 to nphi, nz = 70, 2.
Note that there is no need for nz to be greater than 2 since the sides of the
cylinder are flat in the z direction.
By the way, the double for-loop can be replaced by:
PHI, Z = np.meshgrid(phi, z)
CP = r * np.cos(PHI)
SP = r * np.sin(PHI)
XYZ = np.dstack([CP, SP, Z])
verts = np.stack(
[XYZ[:-1, :-1], XYZ[:-1, 1:], XYZ[1:, 1:], XYZ[1:, :-1]], axis=-2).reshape(-1, 4, 3)
So, for example,
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi, nz = 70, 2
r = 1 # radius of cylinder
phi = np.linspace(0, 360, nphi) / 180.0 * np.pi
z = np.linspace(0, 1.0, nz)
PHI, Z = np.meshgrid(phi, z)
CP = r * np.cos(PHI)
SP = r * np.sin(PHI)
XYZ = np.dstack([CP, SP, Z])
verts = np.stack(
[XYZ[:-1, :-1], XYZ[:-1, 1:], XYZ[1:, 1:], XYZ[1:, :-1]], axis=-2).reshape(-1, 4, 3)
cmap = plt.cm.rainbow
cols = cmap(np.random.random())
poly3 = Poly3DCollection(verts, facecolor=cols, edgecolor="none")
poly3.set_alpha(0.8)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-1, 1)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 1)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 1)
plt.show()
yields

Python:Curved surface plot with density colors

I have irregularly spaced mesh points data in the form [[xi1,yi1,zi1], [xi2,yi2,zi2],....]. They form a part of a sphere
I also have data [[x1,y1,z1,n1],[x2,y2,z2,n2]....] where (x1,y1,z1) etc tells the coordinate of the midpoint of each mesh bin and ni are the densities at the corresponding locations. The 3d scatter plot with square markers of the data look like this (where the colors show the value of n)
its side view showing the curvature
I am trying to make this into a smooth surface plot. I have looked into this example matplotlib color but here the gridpoints are equally spaced while in my case they are not, also how would one represent the densities using color in such an irregular grid. I am open to trying other packages other than matplotlib.
Thanks
One method is to manually create and plot a collection of triangles:
(Edit: manually creating and coloring triangles around bin midpoints)
import numpy
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.tri import Triangulation
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.cm as cm
# Generate a dataset
R = 1
# bin midpoints
theta = numpy.linspace(numpy.pi/6, numpy.pi/3, 20) + numpy.pi / 2
phi = numpy.linspace(numpy.pi/6, numpy.pi/3, 20)
ttheta, pphi = numpy.meshgrid(theta, phi)
x = R * numpy.sin(ttheta) * numpy.cos(pphi)
y = R * numpy.sin(ttheta) * numpy.sin(pphi)
z = R * numpy.cos(ttheta)
n = numpy.exp(-(ttheta - numpy.pi/4 - numpy.pi/2)**2 * 20 - (pphi - numpy.pi/4)**2 * 20)
mappable = cm.ScalarMappable(cmap=cm.coolwarm, norm=matplotlib.colors.Normalize(vmin=0, vmax=1))
colors = mappable.to_rgba(n)
# Scatter plot
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.scatter(x.flatten(), y.flatten(), z.flatten(), c=colors.reshape(x.size, 4))
ax.set_xlim(0.2, 0.8)
ax.set_ylim(0.2, 0.8)
ax.set_zlim(-0.9, -0.45)
ax.elev = 50
fig.savefig('t.png')
# Surface plot
# bin vertex spherical coordinates
dtheta = theta[1] - theta[0]
dphi = phi[1] - phi[0]
v_theta = numpy.concatenate([theta - dtheta/2, numpy.array([theta[-1] + dtheta/2])])
v_phi = numpy.concatenate([phi - dphi/2, numpy.array([phi[-1] + dphi/2])])
# bin vertex Cartesian coordinates
v_ttheta, v_pphi = numpy.meshgrid(v_theta, v_phi)
vx = R * numpy.sin(v_ttheta) * numpy.cos(v_pphi)
vy = R * numpy.sin(v_ttheta) * numpy.sin(v_pphi)
vz = R * numpy.cos(v_ttheta)
# Creating triangles and corresponding face colors
triangles = []
facecolors = []
for i in range(v_theta.size - 1):
for j in range(v_phi.size - 1):
triangles.extend([
[(i, j), (i + 1, j), (i, j + 1)],
[(i + 1, j), (i + 1, j + 1), (i, j + 1)]])
facecolors.extend([
colors[i, j],
colors[i, j]
])
triangle_vertices = numpy.array(
[[[vx[i,j], vy[i,j], vz[i,j]] for i, j in t] for t in triangles])
coll = Poly3DCollection(triangle_vertices, facecolors=facecolors, edgecolors=(0,0,0,0))
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.add_collection(coll)
ax.set_xlim(0.2, 0.8)
ax.set_ylim(0.2, 0.8)
ax.set_zlim(-0.9, -0.45)
ax.elev = 50
fig.savefig('t2.png')
The scatter plot:
The surface plot:

Matplotlib boolean coloring

I would like to graph a 3d plot of a sphere, which colors sections differently according to some function of the theta, phi coordinates. I could graph two separate graphs, one of each color, but I'm not sure exactly how the gridding/meshing works when plotting. Viz. I want to grid/mesh over all thetas/phis in a sphere, but then throw out certain pairs given a boolean function. Is this possible? Attached is a picture of a scatterplot that does what I'd like to do with surfaces.
Based on scatter3d_demo.py found in the matplotlib tutorial:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
THETA, PHI = np.ogrid[0:2*np.pi:40j, 0:np.pi:30j]
X = 10 * np.cos(THETA) * np.sin(PHI)
Y = 10 * np.sin(THETA) * np.sin(PHI)
Z = 10 * np.ones_like(THETA) * np.cos(PHI)
def func(THETA, PHI):
mask = (THETA < np.pi/2) & (np.pi/3 < PHI) & (PHI < 2 * np.pi/3)
return np.where(mask, 1, 0.5)
C = func(THETA, PHI)
x = X.ravel()
y = Y.ravel()
z = Z.ravel()
c = C.ravel()
ax.scatter(x, y, z, c=c, s=30, vmin=0, vmax=1)
ax.set_aspect('equal')
plt.show()
yields
Note you could also color patches on a sphere using plot_surface:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
THETA, PHI = np.ogrid[0:2*np.pi:40j, 0:np.pi:30j]
X = 10 * np.cos(THETA) * np.sin(PHI)
Y = 10 * np.sin(THETA) * np.sin(PHI)
Z = 10 * np.ones_like(THETA) * np.cos(PHI)
def func(THETA, PHI):
mask = (THETA < np.pi/2) & (np.pi/3 < PHI) & (PHI < 2 * np.pi/3)
return np.where(mask, 1, 0.5)
C = func(THETA, PHI)
jet = plt.cm.jet
ax.plot_surface(X, Y, Z, rstride=2, cstride=2, facecolors=jet(C))
ax.set_aspect('equal')
plt.show()

Categories

Resources