英文:
Overlay 2D stream plot on 3D surface plot
问题
我正在研究一个三维轴对称系统(这里为了简单起见,我展示了一个球体)。我可以在x-z平面上解决问题,以获得如下流图所示的矢量场:
鉴于问题的对称性,我可以围绕z轴旋转这个解决方案,以获得完整的三维矢量场。现在,我想以某种方式制作这个解决方案的三维图。我能想到的最直接的方法是制作一个不包括x>0和y<0区域的系统的表面图,就像这样:
然后将之前的流图叠加在(y=0且x>0)和(x=0且y<0)平面上,这些平面是由于表面的“剪切”而产生的。有关如何实现这一点的任何建议吗?或者也许有其他更好的方法来可视化三维结果吗?
这是一个生成这些图的示例代码(使用的x_Car和m_Car仅为示例):
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
# ...(代码省略,包括定义了一些变量和生成流图和表面图的代码)...
plt.show()
更新:
@Jared建议使用矢量图来实现这一点。虽然如果没有其他办法,这可能是一种方法,但我真的更喜欢流图,因为它可以更好地展示数据(我的一些场是更加复杂的,连续的线条可以更好地可视化它们,而不是离散的箭头)。有没有办法实现这一点?
英文:
I'm working on a 3D axis-symmetric system (here for simplicity I'm showing a sphere). I can solve the problem in the x-z plane to find a vector field like the one in the following stream plot:
Given the symmetry of the problem, I can rotate this solution around the z-axis to get the full 3D vector field. Now, I would like to somehow make a 3D plot of this solution. The most straightforward way I could think of is to make a surface plot of the system without the x>0 && y<0 region, like this:
And then overlay the previous stream plot on the (y=0 && x>0) and (x=0 && y<0) planes resulting from the "cut-out" of the surface. Any suggestions on how to achieve this? Or is there maybe another better way to visualise the 3D result?
Here's a MWC that generates the figures (the x_Car and m_Car used are just an example):
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
ell = 0.62
gamma = 1
Rbase, Rcap, center = 0.62, 0.62, 0
###################
###### Stream plot
res = 200
x_Car = [[-1.8417224772056704e-16, -0.0, -0.5726312223685197],
[7.597102430294396e-17, 0.0, -0.6203504908994001],
[0.020037100834806584, 0.0, -0.5857436781333181],
[0.030439155856322415, 0.0, -0.6196032515649681],
[0.020156894294067102, 0.0, -0.5477190352848064],
[-1.6882456041048852e-16, -0.0, -0.5249119538377125]]
m_Car = [[-1.60647705e-05, 0.00000000e+00, -2.43349475e-01],
[ 0.00022923, 0. , -0.21359168],
[ 0.00627664, 0. , -0.23712676],
[ 0.01131077, 0. , -0.21533309],
[ 0.00655443, 0. , -0.25987232],
[ 2.65801174e-05, 0.00000000e+00, -2.72980539e-01]]
m_Car_T, x_Car_T = np.array(m_Car).T, np.array(x_Car).T
cm = matplotlib.cm.coolwarm
norm = matplotlib.colors.Normalize(vmin=0,vmax=1)
sm = matplotlib.cm.ScalarMappable(cmap=cm, norm=norm)
xx, zz = x_Car_T[0], x_Car_T[2]
x_grid, z_grid = np.meshgrid(np.linspace(xx.min(),xx.max(),res),np.linspace(zz.min(),zz.max(),res))
x_vector_interp = griddata((xx, zz), m_Car_T[0], (x_grid, z_grid), method='cubic')
z_vector_interp = griddata((xx, zz), m_Car_T[2], (x_grid, z_grid), method='cubic')
width, height = matplotlib.figure.figaspect(1.5)*1.2
fig, ax = plt.subplots(1,1,figsize=(width,height))
ax.pcolormesh(x_grid/ell, z_grid/ell, np.sqrt(x_vector_interp**2+z_vector_interp**2), norm=norm, cmap=cm)
ax.streamplot(x_grid/ell, z_grid/ell, x_vector_interp, z_vector_interp,
color='k', density=1.1) #, maxlength=6, integration_direction='both'
xrange = np.linspace(0,Rbase,100)
ax.plot(xrange/ell,-np.sqrt(Rbase**2-xrange**2)/ell,'k')
zcap = np.sign(gamma)*Rcap+center
ax.plot(xrange/ell,(np.sign(gamma)*np.sqrt(Rcap**2-xrange**2)+center)/ell,'k')
ax.plot((0,0),(-Rbase/ell,zcap/ell),'k')
ax.set_ylabel('$z/\ell$'); ax.set_ylim(-(Rbase+0.1)/ell,(Rbase+0.1)/ell)
ax.set_xlabel('$x/\ell$'); ax.set_xlim(-0.1/ell,(Rbase+0.1)/ell)
plt.colorbar(sm,label=r'$|\mathbf{m}|$')
# plt.axis('off')
# plt.axis("image")
plt.show()
###################
###### Surface plot
width, height = matplotlib.figure.figaspect(1.2)*1.5
fig, ax = plt.subplots(1, 1, figsize=(width, height), subplot_kw={"projection": "3d"})
### Plot the cap
theta, phi = np.mgrid[0:np.pi/2:180j, 0:1.5*np.pi:270j] # phi = alti, theta = azi
X = Rcap*np.sin(theta)*np.cos(phi)
Y = Rcap*np.sin(theta)*np.sin(phi)
Z = Rcap*np.cos(theta)+center
for i in range(len(theta)):
for j in range(len(phi[0])):
if Z[i,j] < 0:
Z[i,j] = np.nan
else:
pass
ax.plot_surface(X/ell, Y/ell, Z/ell, linewidth=0, antialiased=False, rstride=1, cstride=1, color='grey')
### Plot the base
theta, phi = np.mgrid[np.pi/2:np.pi:180j, 0:1.5*np.pi:270j] # phi = alti, theta = azi
X = Rbase*np.sin(theta)*np.cos(phi)
Y = Rbase*np.sin(theta)*np.sin(phi)
Z = Rbase*np.cos(theta)
ax.plot_surface(X/ell, Y/ell, Z/ell, linewidth=0, antialiased=False, rstride=1, cstride=1, color='gainsboro')
ax.plot([0,0],[0,0],[-Rbase/ell,(Rcap+center)/ell],'k-',zorder=100)
ax.set_xlabel('$x/\ell$'); ax.set_ylabel('$y/\ell$'); ax.set_zlabel('$z/\ell$')
ax.set_box_aspect([1,1,(2*Rbase)/(Rbase+Rcap-center)])
plt.show()
UPDATE
@Jared had suggested a way to do this with a quiver plot. Although this could be a way if nothing else is possible, I would really prefer to have the stream plot, as it shows the data a lot better (some of my fields are a lot more intricate and the continuous lines help visualise them much better than the discrete arrows). Is there any way to achieve this?
答案1
得分: 2
为了实现这一点,您可以使用彩色曲面图来显示轮廓,并使用箭头图来显示向量。我不得不调整zorder
以使图在彼此前面。不过,缺点是如果您有一个交互式图并围绕它旋转,图层效果将不再起作用。
import numpy as np
import matplotlib.pyplot as plt
plt.close("all")
N = 20
theta = np.linspace(0, np.pi, N)
phi = np.linspace(0, 3*np.pi/2, N)
Theta, Phi = np.meshgrid(theta, phi)
X = np.sin(Theta)*np.cos(Phi)
Y = np.sin(Theta)*np.sin(Phi)
Z = np.cos(Theta)
r = np.linspace(0, 1, N)
theta = np.linspace(-np.pi/2, np.pi/2, N)
R_vector, Theta_vector= np.meshgrid(r, theta)
X_vector = R_vector*np.cos(Theta_vector)
Z_vector= R_vector*np.sin(Theta_vector)
Y_vector= np.zeros_like(X_vector)
U = X_vector+ 2*Z_vector
V = -Z_vector
W = np.zeros_like(U)
magnitudes = np.sqrt(U**2 + V**2 + W**2)
U /= 10*magnitudes.max()
V /= 10*magnitudes.max()
W /= 10*magnitudes.max()
fig, ax = plt.subplots(subplot_kw={"projection":"3d", "computed_zorder":False})
ax.plot_surface(X, Y, Z, color="gainsboro", zorder=1)
ax.plot_surface(X_vector, Y_vector, Z_vector,
facecolors=plt.cm.coolwarm(magnitudes), zorder=2)
ax.quiver(X_vector, Y_vector, Z_vector, U, V, W, color="k",
antialiased=True, lw=0.5)
ax.set_aspect("equal")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
fig.tight_layout()
<details>
<summary>英文:</summary>
To do this, you can use a colored surface plot to show the contour and a quiver plot to show the vectors. I had to adjust the `zorder` to get the plots in front of each other. The downside of that is the layering effect no longer works if you have an interactive figure and rotate around it.
import numpy as np
import matplotlib.pyplot as plt
plt.close("all")
N = 20
theta = np.linspace(0, np.pi, N)
phi = np.linspace(0, 3*np.pi/2, N)
Theta, Phi = np.meshgrid(theta, phi)
X = np.sin(Theta)*np.cos(Phi)
Y = np.sin(Theta)*np.sin(Phi)
Z = np.cos(Theta)
r = np.linspace(0, 1, N)
theta = np.linspace(-np.pi/2, np.pi/2, N)
R_vector, Theta_vector= np.meshgrid(r, theta)
X_vector = R_vector*np.cos(Theta_vector)
Z_vector= R_vector*np.sin(Theta_vector)
Y_vector= np.zeros_like(X_vector)
U = X_vector+ 2*Z_vector
V = -Z_vector
W = np.zeros_like(U)
magnitudes = np.sqrt(U**2 + V**2 + W**2)
U /= 10*magnitudes.max()
V /= 10*magnitudes.max()
W /= 10*magnitudes.max()
fig, ax = plt.subplots(subplot_kw={"projection":"3d", "computed_zorder":False})
ax.plot_surface(X, Y, Z, color="gainsboro", zorder=1)
ax.plot_surface(X_vector, Y_vector, Z_vector,
facecolors=plt.cm.coolwarm(magnitudes), zorder=2)
ax.quiver(X_vector, Y_vector, Z_vector, U, V, W, color="k",
antialiased=True, lw=0.5)
ax.set_aspect("equal")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
fig.tight_layout()
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/Rerpb.jpg
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论