使用 matplotlib 或其他 foss 工具绘制奇异向量场的视觉上吸引人的方法

计算科学 Python 可视化
2021-11-24 02:21:30

获得奇异矢量场的视觉吸引力图的最佳方法是什么(如果您还想可视化场强)。例如,我正在使用两个点电荷的电场,如下例所示:

from pylab import *
from scipy.integrate import odeint
from matplotlib import animation
from matplotlib import cm
import numpy as np

rc('font', **{'family': 'serif', 'serif': ['Computer Modern']})
rc('text', usetex=True)

## Set up charges

class charge:
    def __init__(self, q, pos):
        self.q=q
        self.pos=pos


chargesPlus=[]
chargesMinus=[]

#for i in arange(0,1,1):
chargesPlus.append(charge(1,[3,0]))
chargesMinus.append(charge(-1,[-3,0]))
charges = chargesPlus + chargesMinus


def E_point_charge(q, a, x, y,r):
    return q*(x-a[0])/((x-a[0])**2+(y-a[1])**2)**(1.5), \
        q*(y-a[1])/((x-a[0])**2+(y-a[1])**2)**(1.5)


def E_total(x, y, charges):
    Ex, Ey=0, 0
    for C in charges:
        E=E_point_charge(C.q, C.pos, x, y,1)
        Ex=Ex+E[0]
        Ey=Ey+E[1]
    return [Ex, Ey]


domain =2

## Cut Quiver plot
def cut(r):
    if r < domain:
        return 0
    else:
        return 1

cutv = np.vectorize(cut)


def cut_total(charges,x): 
    c = 1
    for C in charges: 
        r = sqrt((C.pos[0] - x[0])**2 + (C.pos[1] - x[1])**2)
        c = c*cutv(r)
        print c
        print C.pos[0],C.pos[1]
    return c

fig = figure()

ax = fig.add_subplot(1,1,1)


xMin,xMax=-15,15
yMin,yMax=-10,10

#ax.plot(x,y)
ax.axis('tight')
xlim([xMin,xMax])
ylim([yMin,yMax])


# plot point charges
for C in charges:
    if C.q>0:
        plot(C.pos[0], C.pos[1], 'bo', ms=8*sqrt(C.q))
    if C.q<0:
        plot(C.pos[0], C.pos[1], 'ro', ms=8*sqrt(-C.q))


xG,yG = meshgrid(linspace(xMin,xMax,25),linspace(yMin,yMax,25))

# plot vector field
E_totalX,E_totalY = E_total(xG,yG,charges)

EAbs = (E_totalX**2 + E_totalY**2)**(0.5)
E_XX = E_totalX/EAbs
E_YY = E_totalY/EAbs
#EAbs = np.nan_to_num(EAbs) 

#ax.streamplot(xG,yG,E_XX,E_YY,color=EAbs,cmap=cm.autumn)
ax.quiver(xG,yG,E_XX,E_YY,EAbs,cmap=cm.GnBu)

xlabel('x')
ylabel('y')
ax.set_aspect(1)


plt.savefig('fig1.png')

E_totalX = E_totalX*cut_total(charges,[xG,yG])
E_totalY = E_totalY*cut_total(charges,[xG,yG])

ax.cla()

# plot point charges
for C in charges:
    if C.q>0:
        plot(C.pos[0], C.pos[1], 'bo', ms=8*sqrt(C.q))
    if C.q<0:
        plot(C.pos[0], C.pos[1], 'ro', ms=8*sqrt(-C.q))


ax.quiver(xG,yG,E_totalX,E_totalY,pivot='middle',minshaft=0.1,minlength=0.3,headlength=2,headaxislength=2,headwidth=3,scale=4,alpha=0.4,width=0.002,linestyle='solid')

xlabel('x')
ylabel('y')
ax.set_aspect(1)

plt.savefig('fig2.png')
#show()

输出如下所示:

图。1

图2

第一个例子的问题是如何选择颜色图。我使用了来自http://matplotlib.org/examples/color/colormaps_reference.html的不同地图,但没有一个给我一个真正令人满意的结果。

第一个例子的问题是 cut 函数不能自动工作。您需要手动剪切特定区域。

那么你对改进这些情节有什么建议呢?

如果您有其他 FOSS 工具,可以更好或更轻松地完成这些绘图,请分享。请不要分享 LaTeX 解决方案,因为我在 TeX.sx 上询问了如何使用 PsTricks 或类似方法解决此问题的相应问题:https ://tex.stackexchange.com/questions/225176/visualize-singular-vector-带 tikz 或 pstricks 和朋友的字段

我认为问题应该是社区维基。

编辑 由于特别是问题的颜色图部分尚未得到回答,我添加了另一个简单的示例,其中颜色图看起来不太好:

%matplotlib inline
from pylab import *

X=linspace(-2,2,40)
Y=linspace(-2,2,24)
X,Y=meshgrid(X, Y)


def E(x,y):
    r = sqrt(x**6 + y**6)
    return (x/r,y/r)

def E_dir(x,y):
    #direction field
    Ex,Ey=E(x,y)
    n=sqrt(Ex**2+Ey**2)
    return [Ex/n, Ey/n]

Ex,Ey = E(X,Y)
Exdir,Eydir = E_dir(X,Y)
EE=sqrt(Ex**2+Ex**2)
E
Q  = quiver(X,Y,Exdir,Eydir,EE,cmap='autumn')
show()

几乎所有的图片看起来都是红色的。

输出

4个回答

[我以您的示例程序为起点,并从 matplotlib wiki 改编了Colormap Normalization。]

几乎所有的图片看起来都是红色的。

的确。他们的问题是您的数据存在非常小的差异,并且由于颜色图是线性缩放的,几乎所有的图都将映射到颜色条的下限。

Q = quiver(X,Y,Exdir,Eydir,EE,cmap='autumn')

在此处输入图像描述

这意味着我们需要重新调整颜色条。对于分歧,对数刻度听起来不错。首先我们需要

import matplotlib.colors as colors

所以我们可以弄乱颜色。接下来我们简单的说

Q = quiver(X,Y,Exdir,Eydir,EE,cmap='autumn',
           norm=colors.LogNorm(vmin=EE.min(),vmax=EE.max()))

这将以对数方式缩放颜色条。

在此处输入图像描述

这看起来已经好一点了。现在让我们使用另一个颜色图,因为黄色很难看到。我认为coolwarm看起来不错并且适合不同的数据(我认为 Paraview 默认使用这个)。

Q = quiver(X,Y,Exdir,Eydir,EE,cmap='coolwarm',
           norm=colors.LogNorm(vmin=EE.min(),vmax=EE.max()))

在此处输入图像描述

作为最后一步,让我们添加一个漂亮的颜色条来指示分歧。

fig = figure()
Q = quiver(X,Y,Exdir,Eydir,EE,cmap='coolwarm',
           norm=colors.LogNorm(vmin=EE.min(),vmax=EE.max()))
fig.colorbar(Q,extend='max')

在此处输入图像描述

当然,您也可以像在 MATLAB 答案中那样将颜色图放在背景中。

fig = figure()
I = imshow(EE,extent=[np.min(X),np.max(X),np.min(Y),np.max(Y)],cmap='autumn')
Q = quiver(X,Y,Exdir,Eydir)
fig.colorbar(I,extend='max')

在此处输入图像描述

或对数缩放

fig = figure()
I = imshow(EE,extent=[np.min(X),np.max(X),np.min(Y),np.max(Y)],cmap='autumn',
           norm=colors.LogNorm(vmin=EE.min(),vmax=EE.max()))
Q = quiver(X,Y,Exdir,Eydir)
fig.colorbar(I,extend='max')

在此处输入图像描述

另一种方法是将数据裁剪到最大值。该命令可以使用密钥imshow自行执行此操作,因为您必须使用. 这会将某个阈值之外的所有值设置为该阈值。vmaxquivernumpy.clip

numpy.clip(a, a_min, a_max, out=None)

但是由于您的函数变化如此之快,在这种情况下您可能也必须使用对数颜色图。

fig = figure()
Q = quiver(X,Y,Exdir,Eydir,clip(EE,None,1),cmap='autumn')
fig.colorbar(Q,extend='max')

在此处输入图像描述

我有一些简单的 Matlab 代码为向量绘制小箭头。然后我使用颜色图来指示下面的强度。我觉得他们看起来很酷。

电线

这是箭袋绘图仪功能的示例。它为箭袋/箭头生成顶点,然后您可以使用 fill() 函数进行绘制。

%   INPUTS:
%       U,V = Quiver x- and y-components.
%       X,Y = x- and y-position of the center of the quiver stem.
%
%   OUTPUTS:
%       Xv,Yv = Quiver vertices.

function [Xv, Yv] = quiver_vertices(U,V,X,Y)

%  Magnitude of vector.
MAG = sqrt(U.^2 + V.^2);

%  Quiver stem half-thickness.
T = MAG/40;

%  Arrowhead thickness.
AH  = MAG/3;            %  Height
AHx = AH/3;             %  Width

%  Solve for angle of quiver relative to x-axis.
THETA = atan2(V,U);
sinT  = sin(THETA);
cosT  = cos(THETA);

%  Solve for tips of quiver stem.
Xa = X + U/2 - AH*cosT;
Xb = X - U/2;
Ya = Y + V/2 - AH*sinT;
Yb = Y - V/2;

%  Solve for quiver arrowhead vertices.
PHI = pi/2 - THETA;
Xt1 = X + U/2;
Yt1 = Y + V/2;
Xt2 = Xa + AHx*cos(PHI);
Yt2 = Ya - AHx*sin(PHI);
Xt3 = Xa - AHx*cos(PHI);
Yt3 = Ya + AHx*sin(PHI);

%  Solve for vertices of quiver stem.
Xa1 = Xa - T*sinT;
Xa2 = Xa + T*sinT;
Xb1 = Xb - T*sinT;
Xb2 = Xb + T*sinT;
Ya1 = Ya + T*cosT;
Ya2 = Ya - T*cosT;
Yb1 = Yb + T*cosT;
Yb2 = Yb - T*cosT;

%  Fill the quiver vertices.
Xv = [Xt1 Xt2 Xa2 Xb2 Xb1 Xa1 Xt3 Xt1];
Yv = [Yt1 Yt2 Ya2 Yb2 Yb1 Ya1 Yt3 Yt1];

如前所述,主要问题不在于可视化算法,而在于数据。因此,您应该对这些值使用不同的缩放比例(标准化)。此外,如果您想为字形(在这种情况下为箭头)着色,它们的大小应该允许它们具有美学价值(如颜色)。然后,我稍微修改了之前提供的答案以显示颜色图/矢量方向字段。

我还为矢量场的可视化添加了另一个选项:流图。除此之外,我的回答只是对人们已经提到的内容的总结。

请参阅下面的代码。

from __future__ import division, print_function
import numpy as np
import matplotlib.pyplot as plt


def elec_field(x, y, x_coords=[0], y_coords=[0], charges=[1]):
    Ex = np.zeros_like(x)
    Ey = np.zeros_like(x)
    for x0, y0, q in zip(x_coords, y_coords, charges):
        R = np.sqrt((x - x0)**2 + (y - y0)**2) + 1e-6
        Ex += q*(x - x0)/R**3
        Ey += q*(y - y0)/R**3
    return Ex, Ey


y, x = np.mgrid[-3:3:21j, -3:3:21j]
Ex, Ey = elec_field(x, y, x_coords=[-1, 1], y_coords=[0,0],
                    charges=[1, -1])
Emag = np.sqrt(Ex**2 + Ey**2)
dir_x = Ex/Emag
dir_y = Ey/Emag

plt.figure(figsize=(8, 6))
plt.subplot(221)
plt.quiver(x[::2, ::2], y[::2, ::2], dir_x[::2, ::2], dir_y[::2, ::2],
           np.log10(Emag[::2, ::2]), cmap="autumn", scale_units="inches",
           scale=5, width=0.015, pivot="mid")
plt.colorbar()
plt.axis("image")

plt.subplot(222)
plt.streamplot(x, y, Ex, Ey, color=np.log10(Emag),cmap="autumn")
plt.colorbar()
plt.axis("image")


plt.subplot(223)
plt.contourf(x, y, np.log10(Emag), cmap="autumn")
plt.colorbar()
plt.quiver(x[::2, ::2], y[::2, ::2], dir_x[::2, ::2], dir_y[::2, ::2],
           scale_units="inches", scale=5, width=0.010, pivot="mid")
plt.axis("image")


plt.subplot(224)
plt.contourf(x, y, np.log10(Emag), cmap="autumn")
plt.colorbar()
plt.streamplot(x, y, Ex/Emag, Ey/Emag, color="black")
plt.axis("image")

plt.savefig("vector_field.png", dpi=300)
plt.show()

这是结果

在此处输入图像描述

我认为以对数刻度呈现颜色和矢量长度的数据是有意义的。这是基于您的示例的代码片段。

from matplotlib.colors import LogNorm
from pylab import *

X=linspace(-2,2,40)
Y=linspace(-2,2,24)
X,Y=meshgrid(X, Y)


def E(x,y):
    r = sqrt(x**6 + y**6)
    return (x/r,y/r)

def E_dir(x,y):
#direction field
Ex,Ey=E(x,y)
n=sqrt(Ex**2+Ey**2)
return [Ex/n, Ey/n]

Ex,Ey = E(X,Y)
Exdir,Eydir = E_dir(X,Y)
EE=sqrt(Ex**2+Ey**2)
EElog=log10(EE-EE.min()+1.0)
Exlog=Exdir*EElog
Eylog=Eydir*EElog

colormap='jet'
# your way
ff=figure(1)
ff.clf()
subplot(221)
Q  = quiver(X,Y,Exdir,Eydir,EE,cmap=colormap)
colorbar()
title('linear in color, constant vector length')

# log colormap
subplot(222)
Q  = quiver(X,Y,Exdir,Eydir,EElog,cmap=colormap)
colorbar()
title('log in color (manually), constant vector length')

# a fancier way of doing a log colormap
subplot(223)
Q  = quiver(X,Y,Exdir,Eydir,EE,norm=LogNorm(vmin=EE.min(),vmax=EE.max()),cmap=colormap)
colorbar()
title('log in color (using LogNorm), constant vector length')

# log colormap and vector lengths
subplot(224)
Q  = quiver(X,Y,Exlog,Eylog,EE,norm=LogNorm(vmin=EE.min(),vmax=EE.max()),cmap=colormap,scale=50)
colorbar()
title('log in color and vector length')

show()
  • 第一个情节只是你的复制品,除了我使用不同的色标来突出差异。
  • 第二个和第三个图都使用颜色的对数映射,一个手动完成,一个通过matplotlib.colors.LogNorm(). 请注意,它们并不完全相同。我不确定使用的确切方程式LogNorm()此外,颜色条知道自动方式的日志缩放,但不知道手动方式。
  • 最后一个绘图日志缩放颜色和矢量幅度。对向量幅度应用对数缩放似乎很不礼貌,但如果明确说明它可能是合适的。

四个箭袋图展示了对数颜色和矢量比例。