简体   繁体   English

与matplotlib对称的streamplot

[英]Symmetric streamplot with matplotlib

I'm trying to plot the streamlines of a magnetic field around a sphere using matplotlib, and it does work quite nicely. 我正在尝试使用matplotlib绘制球体周围磁场的流线,它确实很好用。 However, the resulting image is not symmetric, but it should be (I think). 但是,生成的图像不是对称的,但应该是(我认为)。 在此输入图像描述

This is the code used to generate the image. 这是用于生成图像的代码。 Excuse the length, but I thought it would be better than just posting a non-working snippet. 请原谅,但我认为这比发布一个不起作用的片段更好。 Also, it's not very pythonic; 而且,它不是非常pythonic; that's because I converted it from Matlab, which was easier than I expected. 那是因为我从Matlab转换它,这比我预期的要容易。

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle

def cart2spherical(x, y, z):
    r = np.sqrt(x**2 + y**2 + z**2)
    phi = np.arctan2(y, x)
    theta = np.arccos(z/r)
    if r == 0:
        theta = 0
    return (r, theta, phi)

def S(theta, phi):
    S = np.array([[np.sin(theta)*np.cos(phi), np.cos(theta)*np.cos(phi), -np.sin(phi)],
                  [np.sin(theta)*np.sin(phi), np.cos(theta)*np.sin(phi),  np.cos(phi)],
                  [np.cos(theta),             -np.sin(theta),             0]])
    return S

def computeB(r, theta, phi, a=1, muR=100, B0=1):
    delta = (muR - 1)/(muR + 2)
    if r > a:
        Bspherical = B0*np.array([np.cos(theta) * (1 + 2*delta*a**3 / r**3),
                                  np.sin(theta) * (delta*a**3 / r**3 - 1),
                                  0])
        B = np.dot(S(theta, phi), Bspherical)
    else:
        B = 3*B0*(muR / (muR + 2)) * np.array([0, 0, 1])
    return B

Z, X = np.mgrid[-2.5:2.5:1000j, -2.5:2.5:1000j]
Bx = np.zeros(np.shape(X))
Bz = np.zeros(np.shape(X))
Babs = np.zeros(np.shape(X))
for i in range(len(X)):
    for j in range(len(Z)):
        r, theta, phi = cart2spherical(X[0, i], 0, Z[j, 0])
        B = computeB(r, theta, phi)
        Bx[i, j], Bz[i, j] = B[0], B[2]
        Babs[i, j] = np.sqrt(B[0]**2 + B[1]**2 + B[2]**2)

fig=plt.figure()
ax=fig.add_subplot(111)

plt.streamplot(X, Z, Bx, Bz, color='k', linewidth=0.8*Babs, density=1.3,
               minlength=0.9, arrowstyle='-')
ax.add_patch(Circle((0, 0), radius=1, facecolor='none', linewidth=2))
plt.axis('equal')
plt.axis('off')
fig.savefig('streamlines.pdf', transparent=True, bbox_inches='tight', pad_inches=0)

Quoting from the documentation: 引用文档:

 density : float or 2-tuple Controls the closeness of streamlines. When density = 1, the domain is divided into a 25x25 grid—density linearly scales this grid. Each cell in the grid can have, at most, one traversing streamline. For different densities in each direction, use [density_x, density_y]. 

so you are getting aliasing effects between the cells it uses to decide where the stream lines are, and the symmetries of your problem. 因此,您在用于确定流线所在位置的单元格以及问题的对称性之间会产生锯齿效果。 You need to carefully choose your grid size (of the data) and the density. 您需要仔细选择网格大小(数据)和密度。

It is also sensitive to where the box boundaries are relative to the top of the sphere. 它对于框边界相对于球体顶部的位置也很敏感。 Is the center of your sphere on a data grid point or between the data grid points? 是您的球体的数据网格点或数据网格点之间的中心? If it is on a grid point then the box that contains the center point will be different than the boxes adjacent to it. 如果它网格点上,则包含中心点的框将与其相邻的框不同。

I am not familiar with exactly how it decides which stream lines to draw, but I could imagine that it is some sort of greedy algorithm and hence will give different results walking towards the high density region and away density region. 我不知道它究竟是如何决定绘制哪条流线的,但我可以想象它是某种贪婪的算法,因此会给出高密度区域和远离密度区域的不同结果。

To be clear, you issue is not that the stream lines are wrong , they are valid stream lines, it is that you find the result not aesthetically pleasing. 要明确的是,问题不在于流线是错误的 ,它们是有效的流线,而是您发现结果不美观。

First of all, for curiosity, why would you want to plot symmetric data? 首先,出于好奇,你为什么要绘制对称数据? Why plotting half of isn't fine? 为什么密谋一半不好?

Said that, this is a possible hack. 说,这是一个可能的黑客。 You can use mask arrays as Hooked suggested to plot half of it: 您可以使用掩码数组,因为Hooked建议绘制其中的一半:

mask = X>0
BX_OUT = Bx.copy()
BZ_OUT = Bz.copy()
BX_OUT[mask] = None
BZ_OUT[mask] = None
res = plt.streamplot(X, Z, BX_OUT, BZ_OUT, color='k', 
           arrowstyle='-',linewidth=1,density=2)

then you save in res the result from streamplot, extract the lines and plot them with the opposite X coordinate. 然后从resamplot中保存res结果,提取线条并用相反的X坐标绘制它们。

lines = res.lines.get_paths()
for l in lines:
    plot(-l.vertices.T[0],l.vertices.T[1],'k')

I used this hack to extract streamlines and arrows from a 2D plot, then apply a 3D transformation and plot it with mplot3d. 我使用这个hack从2D绘图中提取流线和箭头,然后应用3D变换并使用mplot3d绘制它。 A picture is in one of my questions here . 一张图片是我的一个问题在这里

Use a mask to separate the two regions of interest: 使用掩码分隔两个感兴趣的区域:

mask = np.sqrt(X**2+Z**2)<1

BX_OUT = Bx.copy()
BZ_OUT = Bz.copy()
BX_OUT[mask] = None
BZ_OUT[mask] = None
plt.streamplot(X, Z, BX_OUT, BZ_OUT, color='k', 
               arrowstyle='-', density=2)

BX_IN = Bx.copy()
BZ_IN = Bz.copy()
BX_IN[~mask] = None
BZ_IN[~mask] = None
plt.streamplot(X, Z, BX_IN, BZ_IN, color='r', 
               arrowstyle='-', density=2)

在此输入图像描述

The resulting plot isn't exactly symmetric, but by giving the algorithm a hint, it's far closer than what you had before. 得到的图不是完全对称的,但通过给出算法提示,它比你之前的更接近。 Play with the density of the grid via meshgrid and the density parameter to achieve the effect you are looking for. 通过meshgriddensity参数使用网格density来实现您正在寻找的效果。

Use physics, instead... The magnetic field is symmetrical with respect to the z (vertical) axis! 使用物理学,而不是......磁场相对于z(垂直)轴是对称的! So you just need two streamplot 's: 所以你只需要两个streamplot

plt.streamplot(X, Z, Bx, Bz, color='k', linewidth=0.8*Babs, density=1.3, minlength=0.9, arrowstyle='-')
plt.streamplot(-X, Z, -Bx, Bz, color='k', linewidth=0.8*Babs, density=1.3, minlength=0.9, arrowstyle='-')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM