Matplotlib 中的 Quiver 绘图箭头太大得离谱

Quiver plot arrows in Matplotlib are ridiculously too large

我正在努力创建一个可以模拟电场线的 python 脚本,但是箭袋图的箭头太大了。我试过更改单位和比例,但 matplotlib 上的文档对我来说也毫无意义......这似乎只是一个主要问题,当系统中只有一次收费时,但箭头仍然略微过大任意数量的费用。箭头往往在所有情况下都过大,但只有一个粒子时最明显。

import matplotlib.pyplot as plt
import numpy as np
import sympy as sym
import astropy as astro

k = 9 * 10 ** 9


def get_inputs():
    inputs_loop = False
    while inputs_loop is False:
        """"
        get inputs
        """
        inputs_loop = True
        particles_loop = False
        while particles_loop is False:
            try:
                particles_loop = True
                """
                get n particles with n charges.
                """
                num_particles = int(raw_input('How many particles are in the system? '))
                parts = []
                for i in range(num_particles):
                    parts.append([float(raw_input("What is the charge of particle %s in Coulombs? " % (str(i + 1)))),
                                  [float(raw_input("What is the x position of particle %s? " % (str(i + 1)))),
                                   float(raw_input('What is the y position of particle %s? ' % (str(i + 1))))]])
            except ValueError:
                print 'Could not convert input to proper data type. Please try again.'
                particles_loop = False
    return parts
def vec_addition(vectors):
    x_sum = 0
    y_sum = 0
    for b in range(len(vectors)):
        x_sum += vectors[b][0]
        y_sum += vectors[b][1]
    return [x_sum,y_sum]

def electric_field(particle, point):
    if particle[0] > 0:
        """
        Electric field exitation is outwards

        If the x position of the particle is > the point, then a different calculation must be made than in not.
        """
        field_vector_x = k * (
        particle[0] / np.sqrt((particle[1][0] - point[0]) ** 2 + (particle[1][1] - point[1]) ** 2) ** 2) * \
                         (np.cos(np.arctan2((point[1] - particle[1][1]), (point[0] - particle[1][0]))))
        field_vector_y = k * (
        particle[0] / np.sqrt((particle[1][0] - point[0]) ** 2 + (particle[1][1] - point[1]) ** 2) ** 2) * \
                         (np.sin(np.arctan2((point[1] - particle[1][1]), (point[0] - particle[1][0]))))
        """
        Defining the direction of the components
        """
        if point[1] < particle[1][1] and field_vector_y > 0:
            print field_vector_y
            field_vector_y *= -1
        elif point[1] > particle[1][1] and field_vector_y < 0:
            print field_vector_y
            field_vector_y *= -1
        else:
            pass
        if point[0] < particle[1][0] and field_vector_x > 0:
            print field_vector_x
            field_vector_x *= -1
        elif point[0] > particle[1][0] and field_vector_x < 0:
            print field_vector_x
            field_vector_x *= -1
        else:
            pass
        """
        If the charge is negative
        """
    elif particle[0] < 0:
        field_vector_x = k * (
            particle[0] / np.sqrt((particle[1][0] - point[0]) ** 2 + (particle[1][1] - point[1]) ** 2) ** 2) * (
                             np.cos(np.arctan2((point[1] - particle[1][1]), (point[0] - particle[1][0]))))
        field_vector_y = k * (
            particle[0] / np.sqrt((particle[1][0] - point[0]) ** 2 + (particle[1][1] - point[1]) ** 2) ** 2) * (
                             np.sin(np.arctan2((point[1] - particle[1][1]), (point[0] - particle[1][0]))))
        """
        Defining the direction of the components
        """
        if point[1] > particle[1][1] and field_vector_y > 0:
            print field_vector_y
            field_vector_y *= -1
        elif point[1] < particle[1][1] and field_vector_y < 0:
            print field_vector_y
            field_vector_y *= -1
        else:
            pass
        if point[0] > particle[1][0] and field_vector_x > 0:
            print field_vector_x
            field_vector_x *= -1
        elif point[0] < particle[1][0] and field_vector_x < 0:
            print field_vector_x
            field_vector_x *= -1
        else:
            pass
    return [field_vector_x, field_vector_y]

def main(particles):
    """
    Graphs the electrical field lines.
    :param particles:
    :return:
    """
    """
    plot particle positions
    """
    particle_x = 0
    particle_y = 0
    for i in range(len(particles)):
        if particles[i][0]<0:
            particle_x = particles[i][1][0]
            particle_y = particles[i][1][1]
            plt.plot(particle_x,particle_y,'r+',linewidth=1.5)
        else:
            particle_x = particles[i][1][0]
            particle_y = particles[i][1][1]
            plt.plot(particle_x,particle_y,'r_',linewidth=1.5)
    """
    Plotting out the quiver plot.
    """
    parts_x = [particles[i][1][0] for i in range(len(particles))]
    graph_x_min = min(parts_x)
    graph_x_max = max(parts_x)
    x,y = np.meshgrid(np.arange(graph_x_min-(graph_x_max-graph_x_min),graph_x_max+(graph_x_max-graph_x_min)),
                      np.arange(graph_x_min-(graph_x_max-graph_x_min),graph_x_max+(graph_x_max-graph_x_min)))
    if len(particles)<2:
        for x_pos in range(int(particles[0][1][0]-10),int(particles[0][1][0]+10)):
            for y_pos in range(int(particles[0][1][0]-10),int(particles[0][1][0]+10)):
                vecs = []
                for particle_n in particles:
                    vecs.append(electric_field(particle_n, [x_pos, y_pos]))
                final_vector = vec_addition(vecs)
                distance = np.sqrt((final_vector[0] - x_pos) ** 2 + (final_vector[1] - y_pos) ** 2)
                plt.quiver(x_pos, y_pos, final_vector[0], final_vector[1], distance, angles='xy', scale_units='xy',
                           scale=1, width=0.05)
        plt.axis([particles[0][1][0]-10,particles[0][1][0]+10,
                  particles[0][1][0] - 10, particles[0][1][0] + 10])
    else:
        for x_pos in range(int(graph_x_min-(graph_x_max-graph_x_min)),int(graph_x_max+(graph_x_max-graph_x_min))):
            for y_pos in range(int(graph_x_min-(graph_x_max-graph_x_min)),int(graph_x_max+(graph_x_max-graph_x_min))):
                vecs = []
                for particle_n in particles:
                    vecs.append(electric_field(particle_n,[x_pos,y_pos]))
                final_vector = vec_addition(vecs)
                distance = np.sqrt((final_vector[0]-x_pos)**2+(final_vector[1]-y_pos)**2)
                plt.quiver(x_pos,y_pos,final_vector[0],final_vector[1],distance,angles='xy',units='xy')
        plt.axis([graph_x_min-(graph_x_max-graph_x_min),graph_x_max+(graph_x_max-graph_x_min),graph_x_min-(graph_x_max-graph_x_min),graph_x_max+(graph_x_max-graph_x_min)])
    plt.grid()
    plt.show()



g = get_inputs()
main(g)}

您可以设置比例,使其大致对应于 uv 向量。

plt.quiver(x_pos, y_pos, final_vector[0], final_vector[1], scale=1e9, units="xy")

这将导致如下结果:

如果我解释正确,您想绘制点电荷的场矢量。环顾四周其他人是如何做到这一点的,人们发现例如this blog entry 克里斯蒂安·希尔。他使用 streamplot 而不是 quiver 但我们可能会采用计算字段的代码并替换绘图。

无论如何,我们不想要也不需要 100 个不同的箭袋图,就像问题的代码中那样,而只需要一个绘制整个字段的箭袋图。如果我们想让场矢量的长度表示场强,我们当然会 运行 遇到一个问题,因为幅度与粒子距离的 3 次方成正比。一个解决方案可能是缩放场在绘图之前以对数方式计算,这样箭头长度仍然以某种方式可见,即使在距离粒子一定距离处也是如此。 quiver 图的比例参数然后可用于调整箭头的长度,以便它们以某种方式适合其他图参数。

""" Original code by Christian Hill 
    http://scipython.com/blog/visualizing-a-vector-field-with-matplotlib/ 
    Changes made to display the field as a quiver plot instead of streamlines
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle

def E(q, r0, x, y):
    """Return the electric field vector E=(Ex,Ey) due to charge q at r0."""
    den = ((x-r0[0])**2 + (y-r0[1])**2)**1.5
    return q * (x - r0[0]) / den, q * (y - r0[1]) / den

# Grid of x, y points
nx, ny = 32, 32
x = np.linspace(-2, 2, nx)
y = np.linspace(-2, 2, ny)
X, Y = np.meshgrid(x, y)


charges = [[5.,[-1,0]],[-5.,[+1,0]]]


# Electric field vector, E=(Ex, Ey), as separate components
Ex, Ey = np.zeros((ny, nx)), np.zeros((ny, nx))
for charge in charges:
    ex, ey = E(*charge, x=X, y=Y)
    Ex += ex
    Ey += ey

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

f = lambda x:np.sign(x)*np.log10(1+np.abs(x))

ax.quiver(x, y, f(Ex), f(Ey), scale=33)

# Add filled circles for the charges themselves
charge_colors = {True: 'red', False: 'blue'}
for q, pos in charges:
    ax.add_artist(Circle(pos, 0.05, color=charge_colors[q>0]))

ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
ax.set_aspect('equal')
plt.show()

(请注意,此处的字段未以任何方式归一化,这对于可视化而言根本无关紧要。)

另一种选择是查看例如this code 也从点电荷绘制场线。