Подтвердить что ты не робот

Симметричный планшет с matplotlib

Я пытаюсь построить линии тока магнитного поля вокруг сферы с помощью matplotlib, и это работает очень хорошо. Однако результирующее изображение не симметрично, но должно быть (я думаю). enter image description here

Это код, используемый для создания изображения. Извините за длину, но я подумал, что это будет лучше, чем просто публикация неработающего фрагмента. Кроме того, это не очень pythonic; потому что я преобразовал его из 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)
4b9b3361

Ответ 1

Прежде всего, для любопытства, почему вы хотите построить симметричные данные? Почему построение половины из них не очень хорошо?

Сказал, что это возможный взлом. Вы можете использовать маски массивов, так как 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)

то вы сохраняете результат res с помощью streamplot, извлекаете строки и рисуете их с противоположной координатой X.

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

Я использовал этот хак, чтобы извлечь линии тока и стрелки из 2D-графика, затем применить 3D-преобразование и построить его с помощью mplot3d. Изображение находится в одном из моих вопросов здесь.

Ответ 2

Цитата из документации:

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].

чтобы вы получали эффекты сглаживания между ячейками, которые он использует, чтобы определить, где находятся линии потока, и симметрии вашей проблемы. Вам необходимо тщательно выбрать размер сетки (данных) и плотность.

Он также чувствителен к тому, где границы ящиков относятся к вершине сферы. Является ли центр вашей сферы на точке сетки данных или между точками сетки данных? Если он находится в точке сетки, то поле, содержащее центральную точку, будет отличаться от соседних с ним ящиков.

Я не знаю точно, как он решает, какие линии потоков рисовать, но я мог представить, что это какой-то жадный алгоритм и, следовательно, даст разные результаты, идущие к области высокой плотности и области плотности.

Чтобы быть ясным, вы делаете не то, что строки потока ошибочны, они являются действительными линиями потока, а потому, что вы находите результат не эстетически приятным.

Ответ 3

Используйте маску для разделения двух интересующих областей:

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)

enter image description here

Полученный график не является точно симметричным, но, давая алгоритму подсказку, он намного ближе, чем то, что у вас было до этого. Играйте с плотностью сетки с помощью meshgrid и параметра density для достижения эффекта, который вы ищете.

Ответ 4

Используйте физику, а не... Магнитное поле симметрично относительно оси z (по вертикали)! Поэтому вам просто нужны два 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='-')