# 均勻帶電圓環的電場
> 作者:王一哲
> 日期:2019/12/1
<br />
## 前言
這是一個選修物理上第6章靜電學的題目,但是題目只會問圓心處或是圓心上一段距離處的電場,因為這些位置才能利用對稱性計算電場量值,如果想計算其它位置的電場該怎麼辦?由於我們在 VPython 教學〈[靜電力及簡諧](https://hackmd.io/@yizhewang/SJHKW2SzX?type=view#%E7%A8%8B%E5%BC%8F-19-3)〉程式 19-3 已經寫過類似的東西,應該可以從這支程式開始修改。
<br />
## 用 VPython 計算空間中各處的電場
[GlowScript 版本](https://www.glowscript.org/#/user/yizhe/folder/Public/program/ChragedCircleVPython)
```python=
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)
```
<br />
在這支程式中,我試著用箭頭畫出各處的電場量值及方向,可以產生3種資料:
1. 1維:沿著x軸方向各點的電場,使用第40、41行程式碼,並將第44 ~ 53行改為註解。
2. 2維:xy平面各點的電場,使用第44 ~ 46行程式碼,並將第40、41行及第50 ~ 53行改為註解。
3. 3維:空間中各點的電場,使用第50 ~ 53行程式碼,並將第40 ~ 46行改為註解。
當箭頭數量較少時,會缺少某些位置的電場;但是當箭頭數量較多時,畫面會太雜亂,幾乎看不出任何東西。因此,我決定只用這支程式產生資料,將資料存成文字檔案,再用另一支程式繪圖。
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/zPz5ZWv.png">
<div style="text-align:center">空間中各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/zUWbWLZ.png">
<div style="text-align:center">xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/8Fvttgy.png">
<div style="text-align:center">x軸上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/eWr6qKj.png">
<div style="text-align:center">xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R,但箭頭數量太多</div>
<br />
## 用streamplot繪製xy平面上各點的電場
```python=
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()
```
<br />
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 指令繪圖。
<br />
$$
\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}
$$
<div style="text-align:center">資料轉置前</div>
<br />
$$
\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}
$$
<div style="text-align:center">資料轉置後</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/jSzAcxO.png">
<div style="text-align:center">xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R,沒有限制資料範圍</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/xsCjBqb.png">
<div style="text-align:center">xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R,有限制資料範圍</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/8IPgKtV.png">
<div style="text-align:center">xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 4.1R,沒有限制資料範圍</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/JL3OrNl.png">
<div style="text-align:center">xy平面上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 4.1R,有限制資料範圍</div>
<br />
## 用plot繪製x軸上各點的電場
```python=
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()
```
<br />
1. 第4、5行:假設資料存在ElectricFieldCircleData.csv,我先用 **numpy.loadtxt** 將第0欄及第3欄的資料讀出來並儲存到x、Ex這兩個1維陣列。
2. 第8 ~ 19行:用 plot 指令繪圖。
3. 第14行:因為很靠近圓環處的電場量值會特別大,為了看出其它位置的電場強度差異,需要限制y軸的繪圖範圍。
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/LuwKTvh.png">
<div style="text-align:center">x軸上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 2.1R</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/yZH0LbA.png">
<div style="text-align:center">x軸上各點電場示意圖,圓環半徑 R = 1,畫面寬度 L = 4.1R</div>
<br />
## 結語
由於VPython裡內建向量運算功能,對於計算電場相當方便,但是執行時效率較差,如果取的點較多可能需要等幾分鐘。理論上應該可以用numpy ndarray直接計算各點的電場,這樣執行速度會快很多,但是目前做出來的東西有點問題,還需要再嘗試看看。
<br />
## 參考資料
1. **numpy.loadtxt**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html
2. **matplotlib.streamplot**:https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html
3. StackOverflow **plot streamlines with matplotlib from file**:https://stackoverflow.com/questions/55207810/plot-streamlines-with-matplotlib-from-file
---
###### tags:`Physics`、`Python`、`VPython`