仮想計算機構

IT業界と無縁な派遣社員のブログ

電気力線みたいな線を描画する

SageMath を用いて電磁気学などで使われる概念についてプロットしてみます。物理学的な厳密性は投げ出しているので差し引いて読んでください。

まず、電荷をA~Dと名付け、位置を xs に、電荷の値を qs に格納しておきます。

A = vector([0,0])
B = vector([5,0])
C = vector([0,5])
D = vector([5,5])

xs = [A,B,C,D]
qs = [1,-2,-3,2]

電荷だけをプロットしてみます。

charges = []
for x,q in zip(xs,qs):
    if q>0:
        charges.append(text("+",x,bounding_box={'boxstyle':'circle', 'fc':'w'}))
    else:
        charges.append(text("-",x,bounding_box={'boxstyle':'circle', 'fc':'w'}))
sum(charges)

f:id:riverta1992:20210207222401p:plain

電荷が4つあるときの電場の様子をベクトル場で可視化してみます。

def g_vector_field(xs,qs,x_min,x_max,y_min,y_max):
    x,y=var('x,y')
    v = vector([x,y])
    E = 0
    Q = 1
    for p,q in zip(xs,qs):
        E += Q*q*(v-p)/((v-p).norm())^3
    E = E/E.norm()
    return plot_vector_field(E(x,y),(x,x_min,x_max),(y,y_min,y_max))

上で定義した関数では、ベクトルを正規化して描くようにしました。

sage: vf = g_vector_field(xs,qs,-8,13,-8,13)
sage: vf
Launched png viewer for Graphics object consisting of 1 graphics primitive

f:id:riverta1992:20210207224112p:plain

次にさきほど定義した電荷のプロットと重ねてみます。

sage: vf+sum(charges)
Launched png viewer for Graphics object consisting of 5 graphics primitives

f:id:riverta1992:20210207223822p:plain

電気力線を描くために必要な関数を以下で定義します。厳密にいうと本数を指定して描画するようにしているので電気力線ではありませんが。

import sys

radius = 0.1

def close(p,xs,qs,x_min,x_max,y_min,y_max):
    for x,q in zip(xs,qs):
        if q<0 and (p-x).norm() < radius:
            return True
    if p[0] < x_min or x_max < p[0]:
        return True
    if p[1] < y_min or y_max < p[1]:
        return True
    return False

def points(theta,S,xs,qs,x_min,x_max,y_min,y_max):
    Q=1
    P = S + 0.1*vector([cos(theta),sin(theta)])

    N = 1000
    l = [S,P]
    for i in range(N):
        E = 0
        for x,q in zip(xs,qs):
            E += Q*q*(P-x)/((P-x).norm())^3
        E = E/E.norm()
        P += 0.1*vector([float(E[0]),float(E[1])])
        l.append(P)
        sys.stdout.write("\r{}".format(i+1))
        sys.stdout.flush()
        if close(P,xs,qs,x_min,x_max,y_min,y_max):
            break
    print("")
    return l

def charge_plot(xs,qs):
    g = []
    for x,q in zip(xs,qs):
        if q>0:
            g.append(text("+",x,bounding_box={'boxstyle':'circle', 'fc':'w'}))
        else:
            g.append(text("-",x,bounding_box={'boxstyle':'circle', 'fc':'w'}))
    return sum(g)

def ef_line(num_line,xs,qs,cs,x_min,x_max,y_min,y_max):
    gs = []
    for x,q in zip(xs,qs):
        if q > 0:
            gs.append([points(2*i*pi/num_line,x,xs,qs,x_min,x_max,y_min,y_max) for i in range(num_line)])
    
    g = sum([sum([line(gs[i][j],xmin=x_min,xmax=x_max,ymin=y_min,ymax=y_max,color=cs[i]) for j in range(num_line)]) for i in range(len(gs))])

    return g + charge_plot(xs,qs)

正の電荷は A と D なので、この2点から出る電気力線を描いてみます。

sage: g_A = sum([line(points(i*pi/4,A,xs,qs,-8,13,-8,13)) for i in range(8)])
sage: g_D = sum([line(points(i*pi/4,D,xs,qs,-8,13,-8,13)) for i in range(8)])
sage: g_A+g_D+charge_plot(xs,qs)
Launched png viewer for Graphics object consisting of 20 graphics primitives

f:id:riverta1992:20210207230215p:plain

さきほど可視化したベクトル場と重ねます。

sage: vf+g_A+g_D+charge_plot(xs,qs)
Launched png viewer for Graphics object consisting of 21 graphics primitives

当たり前ですが、線と各点のベクトルが同じ方向を向いています。

f:id:riverta1992:20210207230600p:plain

最後に、D の電荷を +2 から +19 まで変化させたときの電気力線の変化を調べてみます。

sage: frames = [ef_line(24,xs,[1,-2,-3,i],["blue","red","green","purple"],-8,13,-8,13) for i in sxrange(2,20)]

+2 のとき

sage: frames[0]
Launched png viewer for Graphics object consisting of 52 graphics primitives

f:id:riverta1992:20210207221912p:plain

+19 のとき

sage: frames[-1]
Launched png viewer for Graphics object consisting of 52 graphics primitives

f:id:riverta1992:20210207221927p:plain

アニメーションにする。

sage: animate(frames)
Launched gif viewer for Animation with 18 frames

f:id:riverta1992:20210207220919g:plain