2019年12月1日 星期日

均勻帶電圓環的電場

作者:王一哲
日期:2019/12/1




前言


這是一個選修物理上第6章靜電學的題目,但是題目只會問圓心處或是圓心上一段距離處的電場,因為這些位置才能利用對稱性計算電場量值,如果想計算其它位置的電場該怎麼辦?由於我們在 VPython 教學〈靜電力及簡諧〉程式 19-3 已經寫過類似的東西,應該可以從這支程式開始修改。



用 VPython 計算空間中各處的電場


GlowScript 版本
from vpython import *

"""
 1. 參數設定, 設定變數及初始值
"""
Q, R, N = 2E-11, 1, 100  # 帶電圓環總電量, 半徑, 分割數量
q, r = Q/N, 0.01*R       # 分割後小球電量, 半徑
L = 4.1*R                # 畫面寬度
ke = 8.988E9             # 靜電力常數
num = 100                # 將顯示的空間每邊切成 N 等份

"""
 2. 產生帶電球體類別, 回傳帶電球體產生的電場
"""
class Ball:
    def __init__(self, pos, radius, color, charge):
        self.pos = pos
        self.radius = radius
        self.color = color
        self.charge = charge
        self.ball = sphere(pos=self.pos, radius=self.radius, charge=self.charge, color=self.color)
    def electric(self, pos2):
        return ke*self.charge / mag2(pos2-self.pos) * norm(pos2-self.pos)

"""
 3. 畫面設定
"""
# 產生動畫視窗
scene = canvas(title="Electric Field (Circle)", width=600, height=600, x=0, y=0,               
               background=color.black, center=vec(0, 0, 0), range=L)

# 產生帶電圓環上計算電場用的小球
balls = [Ball(pos=vec(R*cos(i*2*pi/N), R*sin(i*2*pi/N), 0), radius=r, color=color.white, charge=q) for i in range(N)]

# 計算畫箭頭的位置
locations = []

# 直線, 儲存資料後用 plot 繪圖
'''
for i in range(num+1):
    locations.append(vec(L/num*i - L/2, 0, 0))
'''
# 平面, 儲存資料後用 streamplot 繪圖
for i in range(num+1):
    for j in range(num+1):
        locations.append(vec(L/num*i - L/2, L/num*j - L/2, 0))

# 立體, 展示用, 但是箭頭太多, 不容易看清楚
'''
for i in range(num+1):
    for j in range(num+1):
        for k in range(num+1):
            locations.append(vec(L/num*i - L/2, L/num*j - L/2, L/num*k - L/2))
'''

# 依序讀取串列 locations 的元素, 在對應的位置產生箭頭
fields = [arrow(pos=location, axis=vec(0, 0, 0), color=color.green) for location in locations]

with open("ElectricFieldCircleData.csv", "w", encoding="UTF-8") as file:
    file.write("x, y, E, Ex, Ey\n")
    
# 更新箭頭的長度及方向, 記錄電場強度最大值, 量值接近最大值偏紅色, 量值接近 0 偏綠色
fmax = 0
for field in fields:
    for ball in balls:
        field.axis += ball.electric(field.pos)
    with open("ElectricFieldCircleData.csv", "a", encoding="UTF-8") as file:
        file.write(str(field.pos.x) + "," + str(field.pos.y) + "," + str(field.axis.mag) + "," + str(field.axis.x) + "," + str(field.axis.y) + "\n")
    if(field.axis.mag >= fmax): fmax = field.axis.mag

for field in fields:
    field.color = vec(field.axis.mag/fmax, 1 - field.axis.mag/fmax, 0)





在這支程式中,我試著用箭頭畫出各處的電場量值及方向,可以產生3種資料:

  1. 1維:沿著x軸方向各點的電場,使用第40、41行程式碼,並將第44 ~ 53行改為註解。
  2. 2維:xy平面各點的電場,使用第44 ~ 46行程式碼,並將第40、41行及第50 ~ 53行改為註解。
  3. 3維:空間中各點的電場,使用第50 ~ 53行程式碼,並將第40 ~ 46行改為註解。

當箭頭數量較少時,會缺少某些位置的電場;但是當箭頭數量較多時,畫面會太雜亂,幾乎看不出任何東西。因此,我決定只用這支程式產生資料,將資料存成文字檔案,再用另一支程式繪圖。




空間中各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R




xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R




x軸上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R




xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R,但箭頭數量太多



用streamplot繪製xy平面上各點的電場


import numpy as np
import matplotlib.pyplot as plt

x, y, E0, Ex, Ey = np.loadtxt("ElectricFieldCircleData.csv", delimiter=',', usecols=(0, 1, 2, 3, 4),
                              unpack=True, encoding="UTF-8", skiprows=1)

num = 101
x = x.reshape(num, num)[:, 0]
y = y.reshape(num, num)[0, :]
E0 = E0.reshape(num, num).transpose()
Ex = Ex.reshape(num, num).transpose()
Ey = Ey.reshape(num, num).transpose()
# 限制資料的最大值
E0 = np.clip(E0, 0, 1*np.sqrt(2))
Ex = np.clip(Ex, -1, 1)
Ey = np.clip(Ey, -1, 1)

# 開啟繪圖視窗
fig = plt.figure(figsize=(8, 6), dpi=100)
# 開啟圖片物件
ax = fig.gca()
# 設定圖片標題
ax.set_title("Electric Field Around a Charged Circle", fontsize=16)
# 設定座標軸標籤
ax.set_xlabel(r"$x \mathrm{(m)}$", fontsize=14)
ax.set_ylabel(r"$y \mathrm{(m)}$", fontsize=14)
# 用顏色代表每個點的 E0 數值
con = ax.pcolormesh(x, y, E0, shading="gouraud", cmap=plt.cm.coolwarm)
# 顯示數值及色階對應方式
fig.colorbar(con, shrink=0.5, aspect=5)
# 畫電力線
ax.streamplot(x, y, Ex, Ey, linewidth=1, color='black', density=2, arrowstyle='->', arrowsize=1.5)
# 儲存圖片
plt.savefig("ElectricFieldCircle.png")
# 顯示圖片
plt.show()



  1. 第4、5行:假設資料存在ElectricFieldCircleData.csv,我先用numpy.loadtxt將資料讀出來並儲存到x、y、E0、Ex、Ey這5個1維陣列。由於我使用了每一欄的資料,理論上可以省略 usecols=(0, 1, 2, 3, 4)。
  2. 第7 ~ 12行:為了符合matplotlib.streamplot要求的資料格式,需要改變陣列格式。
    1. 將x變為2維陣列,再取出每一列第0欄的資料,重新指定給x並變成1維陣列。如果沒有修改資料格式,執行時會出現錯誤訊息ValueError: The rows of 'x' must be equal
    2. 將y變為2維陣列,再取出第0列每一欄的資料,重新指定給y並變成1維陣列。如果沒有修改資料格式,執行時會出現錯誤訊息ValueError: The rows of 'x' must be equal
    3. 將E0、Ex、Ey變為2維陣列並轉置,如果沒有轉置,資料對應的x、y位置會跑掉。
  3. 第14 ~ 16行:因為很靠近圓環處的電場量值會特別大,為了看出其它位置的電場強度差異,需要限制資料的最大值。
  4. 第19 ~ 36行:用 streamplot 指令繪圖。




$$
\begin{bmatrix}
x_0 y_0 & x_0 y_1 & x_0 y_2 & \dots & x_0 y_N\\
x_1 y_0 & x_1 y_1 & x_1 y_2 & \dots & x_1 y_N\\
\vdots & \vdots & \vdots & & \vdots \\
x_N y_0 & x_N y_1 & x_N y_2 & \dots & x_N y_N\\
\end{bmatrix}
$$
資料轉置前



$$
\begin{bmatrix}
x_0 y_0 & x_1 y_0 & x_2 y_0 & \dots & x_N y_0\\
x_0 y_1 & x_1 y_1 & x_2 y_1 & \dots & x_N y_1\\
\vdots & \vdots & \vdots & & \vdots \\
x_0 y_N & x_1 y_N & x_2 y_N & \dots & x_N y_N\\
\end{bmatrix}
$$
資料轉置後




xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R,沒有限制資料範圍




xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R,有限制資料範圍




xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 4.1R,沒有限制資料範圍




xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 4.1R,有限制資料範圍



用plot繪製x軸上各點的電場


import numpy as np
import matplotlib.pyplot as plt

x, Ex = np.loadtxt("ElectricFieldCircleData.csv", delimiter=',', usecols=(0, 3),
                   unpack=True, encoding="UTF-8", skiprows=1)

# Ex - x 關係圖
plt.figure(figsize=(6, 4.5), dpi=100)
plt.title('Electric Field Around a Charged Circle', fontsize=16)
plt.xlabel(r'$x ~\mathrm{(m)}$', fontsize=14)
plt.ylabel(r'$E_x ~\mathrm{(N/C)}$', fontsize=14)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.ylim(-4, 4)
plt.subplots_adjust(left=0.12, bottom=0.15)
plt.grid(color='grey', linestyle='--', linewidth=1)
plt.plot(x, Ex, color='blue', marker='o', markersize=5, markeredgecolor='black', linestyle='')
plt.savefig("ElectricFieldCircle2R1D.png")
plt.show()



  1. 第4、5行:假設資料存在ElectricFieldCircleData.csv,我先用numpy.loadtxt將第0欄及第3欄的資料讀出來並儲存到x、Ex這兩個1維陣列。
  2. 第8 ~ 19行:用 plot 指令繪圖。
  3. 第14行:因為很靠近圓環處的電場量值會特別大,為了看出其它位置的電場強度差異,需要限制y軸的繪圖範圍。





x軸上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R




x軸上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 4.1R



結語


由於VPython裡內建向量運算功能,對於計算電場相當方便,但是執行時效率較差,如果取的點較多可能需要等幾分鐘。理論上應該可以用numpy ndarray直接計算各點的電場,這樣執行速度會快很多,但是目前做出來的東西有點問題,還需要再嘗試看看。



參考資料


  1. numpy.loadtxthttps://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html
  2. matplotlib.streamplothttps://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html
  3. StackOverflowplot streamlines with matplotlib from filehttps://stackoverflow.com/questions/55207810/plot-streamlines-with-matplotlib-from-file




HackMD 版本連結:https://hackmd.io/@yizhewang/H1Ahc9xTS

沒有留言:

張貼留言