Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rotate arrowheads in 3D quiver in matplotlib?

I'm trying to replicate the following plot using Python and Matplotlib.

Plot I want to replicate

However, the best I have been able to produce is the following:

My attempt

The main issue here is the not in-plane arrows heads, even if I am not satisfied with the quality of the plot in general. I've searched for a solution to use a 2D quiver in a 3D plot, but I haven't found any useful information about how to do that. Is there another way to achieve in-plane arrowheads?

import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

params = {
   'font.family' : 'serif',
   'mathtext.fontset': 'stix',
   'axes.labelsize': 13,
   'legend.fontsize': 8,
   'xtick.labelsize': 13,
   'ytick.labelsize': 13,
   'text.usetex': True,
   'figure.figsize': [10, 5]
   }


plt.rcParams.update(params)

plt.close('all')


x_ax = np.linspace(-10, 10, 24)
y_ax = np.linspace(-10, 10, 24)

x, y = np.meshgrid(x_ax, y_ax, indexing='ij')

r = np.sqrt(x**2 + y**2)

j_x = -y/r*(- np.exp(-np.abs(r)) + np.exp(-np.abs(r)/2) )*2
j_y = +x/r*(- np.exp(-np.abs(r)) + np.exp(-np.abs(r)/2) )*2

#c = np.arctan2(x, -y)
c = np.sqrt(j_x**2 + j_y**2)
c = (c.ravel() - c.min()) / c.ptp()
c = np.concatenate((c, np.repeat(c, 2)))
c = cm.jet(c)
#c = plt.cm.hsv(c)

fig = plt.figure()
ax = fig.gca(projection='3d')

ax.quiver(x, y, 0, j_x, j_y, 0, colors=c, length=1.2, pivot='middle')


t = np.linspace(-10, 10, 200)

psi = 1 - np.exp(-np.abs(t))
b = np.exp(-t**2)
j_abs = np.abs(t)*np.exp(-t**2)*2
#j_abs = (- np.exp(-np.abs(t)) + np.exp(-np.abs(t)/2) )*2

ax.plot(t, psi, zs=0, zdir='y', label=r"$|\psi|$")
ax.plot(t, b, zs=0, zdir='y', label=r"$|\vec B|$")
ax.plot(t, j_abs, zs=0, zdir='y', label=r"$|\vec j|$")

ax.legend()

ax.set_proj_type('ortho')
ax.set_axis_off()

ax.set_zlim([-0.2, 1.4])
ax.view_init(elev=45, azim=90)
ax.dist=5
fig.savefig("vortex.pdf", bbox_inches="tight")
like image 962
skdys Avatar asked Nov 17 '22 01:11

skdys


1 Answers

Maybe mplot3d is not the right tool here, because this is not a truly 3-dimensional plot, but just a combination of two 2-dimensional plots. Consider this approach:

  1. Plot the arrows for the bottom plane in two dimensions, as they would look from above the center, and save the plot as a square image.
  2. Create another image as a projection of the first one, viewed from the desired perspective. E.g. with warpPerspective() from OpenCV.
  3. Make a new plot containing the three lineplots, inserting the image from 2. with plt.imshow().

I guess this is roughly how the original plot above was made. It will take care of effects such as the arrowheads being in the plane, and arrows in the foreground being larger than those in the background.

like image 62
Arne Avatar answered Jan 08 '23 14:01

Arne