Diagtwig de flujo simétrico con matplotlib

Estoy tratando de trazar las líneas de stream de un campo magnético alrededor de una esfera usando matplotlib, y funciona bastante bien. Sin embargo, la imagen resultante no es simétrica, pero debería ser (creo). introduzca la descripción de la imagen aquí

Este es el código utilizado para generar la imagen. Disculpe la longitud, pero pensé que sería mejor que solo publicar un fragmento de código que no funciona. Además, no es muy pythonico; eso es porque lo convertí de Matlab, que fue más fácil de lo que esperaba.

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) 

En primer lugar, por curiosidad, ¿por qué querría trazar datos simétricos? ¿Por qué trazar la mitad de no está bien?

Dicho esto, este es un posible hack. Puede usar matrices de máscaras como Hooked sugirió para trazar la mitad:

 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) 

luego guarda en res el resultado de streamplot, extrae las líneas y las traza con la coordenada X opuesta.

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

Utilicé este truco para extraer líneas de flujo y flechas de un trazado 2D, luego apliqué una transformación 3D y lo dibujé con mplot3d. Una imagen está en una de mis preguntas aquí .

Citando de la documentación:

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

por lo tanto, está obteniendo efectos de alias entre las celdas que utiliza para decidir dónde están las líneas de flujo y las simetrías de su problema. Debe elegir cuidadosamente el tamaño de la cuadrícula (de los datos) y la densidad.

También es sensible a donde los límites de la caja son relativos a la parte superior de la esfera. ¿Está el centro de su esfera en un punto de la cuadrícula de datos o entre los puntos de la cuadrícula de datos? Si está en un punto de la cuadrícula, el cuadro que contiene el punto central será diferente de los cuadros adyacentes a él.

No estoy familiarizado con la forma exacta en que decide qué líneas de flujo dibujar, pero puedo imaginar que es una especie de algoritmo codicioso y, por lo tanto, dará diferentes resultados caminando hacia la región de alta densidad y la región de densidad de distancia.

Para que quede claro, no se trata de que las líneas de flujo sean incorrectas , son líneas de flujo válidas, sino que el resultado no es estéticamente agradable.

Use una máscara para separar las dos regiones de interés:

 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) 

introduzca la descripción de la imagen aquí

La gráfica resultante no es exactamente simétrica, pero al darle al algoritmo una sugerencia, está mucho más cerca de lo que tenía antes. Juegue con la densidad de la cuadrícula mediante meshgrid y el parámetro de density para lograr el efecto que está buscando.

Utilice la física, en su lugar … ¡El campo magnético es simétrico con respecto al eje z (vertical)! Así que solo necesitas dos 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='-')