在3D表面图上叠加2D流图。

huangapple go评论62阅读模式
英文:

Overlay 2D stream plot on 3D surface plot

问题

我正在研究一个三维轴对称系统(这里为了简单起见,我展示了一个球体)。我可以在x-z平面上解决问题,以获得如下流图所示的矢量场:

在3D表面图上叠加2D流图。

鉴于问题的对称性,我可以围绕z轴旋转这个解决方案,以获得完整的三维矢量场。现在,我想以某种方式制作这个解决方案的三维图。我能想到的最直接的方法是制作一个不包括x>0和y<0区域的系统的表面图,就像这样:

在3D表面图上叠加2D流图。

然后将之前的流图叠加在(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:

在3D表面图上叠加2D流图。

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:

在3D表面图上叠加2D流图。

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=&#39;cubic&#39;)
z_vector_interp = griddata((xx, zz), m_Car_T[2], (x_grid, z_grid), method=&#39;cubic&#39;)

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=&#39;k&#39;, density=1.1) #, maxlength=6, integration_direction=&#39;both&#39;

xrange = np.linspace(0,Rbase,100)
ax.plot(xrange/ell,-np.sqrt(Rbase**2-xrange**2)/ell,&#39;k&#39;)
zcap = np.sign(gamma)*Rcap+center
ax.plot(xrange/ell,(np.sign(gamma)*np.sqrt(Rcap**2-xrange**2)+center)/ell,&#39;k&#39;)
ax.plot((0,0),(-Rbase/ell,zcap/ell),&#39;k&#39;)

ax.set_ylabel(&#39;$z/\ell$&#39;); ax.set_ylim(-(Rbase+0.1)/ell,(Rbase+0.1)/ell)
ax.set_xlabel(&#39;$x/\ell$&#39;); ax.set_xlim(-0.1/ell,(Rbase+0.1)/ell)
plt.colorbar(sm,label=r&#39;$|\mathbf{m}|$&#39;)
# plt.axis(&#39;off&#39;)
# plt.axis(&quot;image&quot;) 

plt.show()

###################
###### Surface plot

width, height = matplotlib.figure.figaspect(1.2)*1.5
fig, ax = plt.subplots(1, 1, figsize=(width, height), subplot_kw={&quot;projection&quot;: &quot;3d&quot;})

### 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] &lt; 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=&#39;grey&#39;)

### 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=&#39;gainsboro&#39;)

ax.plot([0,0],[0,0],[-Rbase/ell,(Rcap+center)/ell],&#39;k-&#39;,zorder=100)

ax.set_xlabel(&#39;$x/\ell$&#39;); ax.set_ylabel(&#39;$y/\ell$&#39;); ax.set_zlabel(&#39;$z/\ell$&#39;)
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()

在3D表面图上叠加2D流图。


<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(&quot;all&quot;)
    
    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={&quot;projection&quot;:&quot;3d&quot;, &quot;computed_zorder&quot;:False})
    ax.plot_surface(X, Y, Z, color=&quot;gainsboro&quot;, 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=&quot;k&quot;, 
              antialiased=True, lw=0.5)
    ax.set_aspect(&quot;equal&quot;)
    ax.set_xlabel(&quot;x&quot;)
    ax.set_ylabel(&quot;y&quot;)
    ax.set_zlabel(&quot;z&quot;)
    fig.tight_layout()

[![enter image description here][1]][1]


  [1]: https://i.stack.imgur.com/Rerpb.jpg

</details>



huangapple
  • 本文由 发表于 2023年6月5日 23:04:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76407750.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定