52

この記事は最終更新日から5年以上が経過しています。

[Pythonによる科学・技術計算] 静電位に対する2次元ラプラス・ポアソン方程式のヤコビ法による数値解法,楕円型偏微分方程式,境界値問題

最終更新日 投稿日 2017年08月12日

はじめに

Pythonで楕円型偏微分方程式を数値的に解く。ラプラス方程式は電磁気学にとどまらず,物理学で広く応用されることから,学習価値は大きいだろう。物理以外でも,偏微分方程式の境界値問題の数値解法という点で学べることがあると思う。ただし本計算で用いた手法は収束が遅く実用的ではない。さらに高速な数値解法がある[2]。

本稿では2次元ラプラス方程式およびポアソン方程式を差分化し,繰り返し法で電位を決定する(ヤコビ法)。得られた電位から電場も求める。なお,方程式の導出とアルゴリズムの概略は[補遺]にまとめた。興味のある方は参照されたい。

● [追記]2017年8月14日
SOR法でポアソン方程式を解くコードを掲載した(参考文献の下のほう)。
ヤコビ法よりも50倍以上高速である。


例題の問題設定

例題(1)
ラプラス方程式を解く。
境界条件は図に示したとおり,y=0で\phi=Vボルト与えている。それ以外の境界でアースされ,0ボルトになっている。このときの静電位を決定し可視化する。

スクリーンショット 2017-08-13 0.15.39.png

例題(2)
ポアソン方程式を解く。
例題(1)で考えた領域の中に外部電荷Qを入れる。このときの静電位ϕと電場E=ϕを求め可視化する。
スクリーンショット 2017-08-13 0.15.44.png

例題(1) ポアソン方程式

コード中,アニメーションも生成されるようにしている。解\phi(x,y)の収束していく様子を調べるためである。

追記 2017年12月17日: jupyter上での動作を想定している。jupyterを使わない場合はコード中の"%matplotlib nbagg"をコメントアウトすること。masa_stone22さまのご指摘より。

laplace.py3


"""
ラプラス方程式: 数値解法, ヤコビ(Jacobi)法: 
12 Aug. 2017
"""
%matplotlib nbagg 
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation # アニメーション作成のためのメソッドをインポート

fig = plt.figure()
anim = [] 

# 
delta_L=0.1  # グリッド幅
LL = 10 # 正方形の幅
L = int(LL/delta_L)

V = 5.0 # 1辺の境界の電位
convegence_criterion = 10**-3  # 収束条件。 精度を上げるならこの値を小さくする。

phi = np.zeros([L+1,L+1])
phi[0,:] = V # 境界条件
phi2 = np.empty ([L+1,L+1])

#for plot
im=plt.imshow(phi,cmap='hsv')
anim.append([im])


# メイン
delta = 1.0
n_iter=0
conv_check=[]
while delta > convegence_criterion:
    if n_iter % 100 ==0:  # 収束状況のモニタリング
        print("iteration No =", n_iter, "delta=",delta)
    conv_check.append([n_iter, delta])
    for i in range(L+1):
        for j in range(L+1):
            if i ==0 or i ==L or j==0 or j==L:
                phi2[i,j] = phi[i,j]
            else: 
                phi2[i,j] = (phi[i+1,j] + phi[i-1,j] + phi[i,j+1] + phi[i,j-1])/4 # 補遺:式(11)を参照
    delta = np.max(abs(phi-phi2))

    phi, phi2 = phi2, phi
    n_iter+=1

    im=plt.imshow(phi,cmap='hsv')
    anim.append([im])

#for plot        
plt.colorbar () # カラーバーの表示 
plt.xlabel('X')
plt.ylabel('Y')
ani = ArtistAnimation(fig, anim, interval=100, blit=True,repeat_delay=1000)
ani.save("t.gif", writer='imagemagick')  
plt.show()

結果(1) ラプラス方程式の解

t.png

y=0の境界条件ϕ=5を満たしている。境界近傍で電位の変化が大きい。

例題(2) 外部電荷を埋め込んだ場合のポアソン方程式の解

外部電荷密度c=1000,境界の電位をV=5とした。

Poisson.py3
"""
ポアソン方程式:  ヤコビ(Jacobi)法 
電場も表示: 
12 Aug. 2017
"""
%matplotlib nbagg 
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation # アニメーション作成のためのメソッドをインポート

anim = [] 

# 
delta_L=0.01 #グリッド幅
LL = 1 # 正方形の幅
L = int(LL/delta_L)

V = 5.0 # 境界y=0の電位 (境界条件)
convegence_criterion = 10**-5

phi = np.zeros([L+1,L+1])
phi[0,:] = V # 境界条件
phi2 = np.empty ([L+1,L+1])

# 電荷密度の設定
eps0=1
charge= np.zeros([L+1,L+1])
c=1000 #電荷密度

CC=int(L/4)
CC2=int(L/2)

# 電荷密度をx=[L/4, L/2], y=[L/4,L/2]の領域に埋め込む
for i in range(CC, CC2+1):
    for j in range(CC,CC2+1):
        charge[i,j] = c
#

#for plot
im=plt.imshow(phi,cmap='hsv')
anim.append([im])


# メイン
delta = 1.0
n_iter=0
conv_check=[]
while delta > convegence_criterion:
    if n_iter % 100 ==0:  # 収束状況のモニタリング
        print("iteration No =", n_iter, "delta=",delta)
    conv_check.append([n_iter, delta])
    for i in range(L+1):
        for j in range(L+1):
            if i ==0 or i ==L or j==0 or j==L:
                phi2[i,j] = phi[i,j]
            else: 
                phi2[i,j] = (phi[i+1,j] + phi[i-1,j] + phi[i,j+1] + phi[i,j-1])/4 + (delta_L**2/(4*eps0))*charge[i,j]
    delta = np.max(abs(phi-phi2))

    phi, phi2 = phi2, phi
    n_iter+=1

    im=plt.imshow(phi,cmap='hsv')
    anim.append([im])


#for plot    

pp=plt.colorbar (orientation="vertical") # カラーバーの表示 
pp.set_label("phi", fontname="Ariel", fontsize=24)
plt.xlabel('X',fontname="Ariel", fontsize=24)
plt.ylabel('Y',fontname="Ariel", fontsize=24)
plt.title('Solution of the Poisson equation for electrostatic potential ')
#ani = ArtistAnimation(fig, anim, interval=10, blit=True,repeat_delay=1000)
#ani.save("t.gif", writer='imagemagick')  

plt.show()



#電場の計算
Ex = np.zeros([L,L])
Ey = np.zeros([L,L])

for i in range(L):
    for j in range(L):
        Ex[i,j] = -(phi[i+1,j]-phi[i,j])/delta_L
        Ey[i,j] = -(phi[i,j+1]-phi[i,j])/delta_L

plt.figure()

X, Y= np.meshgrid(np.arange(0, L,1), np.arange(0, L,1))  # メッシュ生成
plt.quiver(X,Y,Ex,Ey,color='red',angles='xy',scale_units='xy', scale=40) # ベクトル場をプロット

plt.xlim([0,L]) # 描くXの範囲
plt.ylim([0,L]) # 描くyの範囲

# グラフ描画
plt.grid()
plt.draw()
plt.show()


結果(2): 外部電荷を埋め込んだ系のポアソン方程式の解

tt.png

y=0での境界条件はϕ=5である。埋め込んだ外部電荷により,例題(1)と比べて電位が大きく異なっている。

ttt.png

電場の様子。(#y軸が逆になってしまった)。この図はdelta_L=0.1として描いた。
電位の変動から期待されるように,外部電荷近傍で電場が大きく変動している。


補遺

補遺(1): 理論

静電場Eと電位ϕの間になりたつ関係式
E=ϕ
と, 電荷密度ρEとの間になりたつガウスの法則

E=ρϵ0

を出発点とする。

(1)を(2)に代入すると

Δϕ(x,y,z)=ρ(x,y,z)ϵ0

を得る。これがポアソン(Poisson)方程式である。
ここでϵ0は真空中の誘電率である。

空間のいたるところで真電荷が存在しない場合(ρ(x,y,z)=0),ポアソン方程式は

Δϕ(x,y,z)=0
となる。これがラプラス(Laplace)方程式である。

補遺(2): 数値解法: ヤコビ法

二次元を考える。
ポアソン方程式は

2ϕ(x,y)x2+2ϕ(x,y)y2=ρ(x,y,z)ϵ0

となる。
xおよびy方向に幅Δで区切られた2次元格子(下図参照)を考える。
点(x+Δ,y)で電位ϕ(x,y)をテイラー展開すると,

ϕ(x+Δ,y)=ϕ(x,y)+ϕxΔ+122ϕx2Δ2+163ϕx3Δ3+1244ϕx4Δ4+O(Δ5)

ϕ(xΔ,y)=ϕ(x,y)ϕxΔ+122ϕx2Δ2163ϕx3Δ3+1244ϕx4Δ4+O(Δ5)

式(6)と式(7)を足し合わせると,

2ϕ(x,y)x2=ϕ(x+Δ,y)+ϕ(xΔ,y)2ϕ(x,y)Δ2+O(Δ2)

yに対しても同様にテイラー展開を行って,
2ϕ(x,y)y2=ϕ(x,y+Δ)+ϕ(x,yΔ)2ϕ(x,y)Δ2+O(Δ2)

を得る。
式(8),(9)を(5)へ代入し少し整理して,

ϕ(x,y)=14[ϕ(x+Δ,y)+ϕ(xΔ,y)+ϕ(x,y+Δ)+ϕ(x,yΔ)]+ρ(x,y)4ϵ0Δ2+O(Δ4)

を得る。これはポアソン方程式の差分表現である。
誤差はO(Δ4)である。

ρ(x,y)=0のときに

ϕ(x,y)=14[ϕ(x+Δ,y)+ϕ(xΔ,y)+ϕ(x,y+Δ)+ϕ(x,yΔ)]+O(Δ4)

となる。これはラプラス方程式の差分表現である。

数値計算では,配列の添え字を(xi,yj)とすると式(10)は

ϕ(xi,yj)=14[ϕ(xi+1,yj)+ϕ(xi1,yj)+ϕ(xi,yj+1)+ϕ(xi,yj1)]+ρ(xi,yj)4ϵ0Δ2 
となる。

(xi,yj)における電位ϕ(i,j)は周囲の4点の値の平均値に電荷密度ρ(x,y)に比例した量を足すことで近似される(下図)。

スクリーンショット 2017-08-12 23.38.26.png

はじめに境界条件と領域内でのポテンシャルの推定値を与え,式(12)を全領域で解き,領域内の解が収束するまで繰り返す。これがヤコビ法である。収束のための繰り返しステップ数はデータ点数のおよそ二乗に比例する[1]。

この方法は収束が大変遅く実用的ではないが,現代的な方法を理解するための基盤となる。


参考文献

ヤコビ法はおよびそれを若干改した善計算手法であるガウス-ザイデル法では収束が遅く実用的ではない。より速いスキームとして逐次過緩和法(SOR法)などがある[1,2]。

[1] 「ニューメリカルレシピ・イン・シー 日本語版―C言語による数値計算のレシピ」技術評論社,1993.

ネットで閲覧可能なものとしてのとして,

を挙げておく。

追記: 2017年8月14日

SOR法,ガウスザイデル法に関する記事を投稿した。

[2] Qiita記事, [Pythonによる科学・技術計算] ラプラス方程式に対するヤコビ法・ガウス-ザイデル法・SOR法の収束速度の比較,偏微分方程式,境界値問題,数値計算

追記: SOR法によるポアソン方程式を解くコード

[2]の記事で説明したSOR法で例題(2)のポアソン方程式を解くコードを掲載する。
ヤコビ方よりも十倍以上はやく収束するので,こちらを使うと良い。


"""
ポアソン方程式: 数値解法, SOR法 
電場も表示:
14 Aug. 2017
"""
%matplotlib nbagg 
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation # アニメーション作成のためのメソッドをインポート
import matplotlib.animation as animation


anim = [] 

# 
delta_Lx=0.01
delta_Ly=0.01
LLx = 1 # 正方形の幅
LLy= 1

Lx = int(LLx/delta_Lx)
Ly = int(LLy/delta_Ly)



V = 5.0 # 電圧
convegence_criterion = 10**-5

phi = np.zeros([Lx+1,Ly+1])
phi_Bound= np.zeros([Lx+1,Ly+1])
phi_Bound[0,:] = V # 境界条件

# 電荷密度の設定
eps0=1
charge= np.zeros([Lx+1,Ly+1])
c=1000

CC=int(Lx/4)
CC2=int(Ly/2)

for i in range(CC, CC2+1):
    for j in range(CC,CC2+1):
        charge[i,j] = c
#


# for SOR method
aa_recta=0.5*(np.cos(np.pi/Lx)+np.cos(np.pi/Ly))
omega_SOR_recta = 2/(1+np.sqrt(1-aa_recta**2)) # SOR法における加速パラメータ
print("omega_SOR_rect=",omega_SOR_recta)



#for plot
im=plt.imshow(phi,cmap='hsv')
anim.append([im])


# メイン
delta = 1.0
n_iter=0
conv_check=[]
while delta > convegence_criterion:
    phi_in=phi.copy()
    if n_iter % 100 ==0:  # 収束状況のモニタリング
        print("iteration No =", n_iter, "delta=",delta)
    conv_check.append([n_iter, delta])
    for i in range(Lx+1):
        for j in range(Ly+1):
            if i ==0 or i ==Lx or j==0 or j==Ly:
                phi[i,j] = phi_Bound[i,j]
            else: 
                phi[i,j] = phi[i,j]+omega_SOR_recta *((phi[i+1,j] + phi[i-1,j] + phi[i,j+1] + phi[i,j-1])/4-phi[i,j]+ (delta_Lx*delta_Ly/(4*eps0))*charge[i,j]) # SOR = ガウス-ザイデル + OR
    delta = np.max(abs(phi-phi_in))


    n_iter+=1

    im=plt.imshow(phi,cmap='hsv')
    anim.append([im])


#for plot    

pp=plt.colorbar (orientation="vertical") # カラーバーの表示 
pp.set_label("phi", fontname="Ariel", fontsize=24)
plt.xlabel('X',fontname="Ariel", fontsize=24)
plt.ylabel('Y',fontname="Ariel",  fontsize=24)
plt.title('Solution of the Poisson equation for electrostatic potential ')
#ani = ArtistAnimation(fig, anim, interval=10, blit=True,repeat_delay=1000)
#ani.save("t.gif", writer='imagemagick')  

plt.show()



#電場の計算
Ex = np.zeros([Lx,Ly])
Ey = np.zeros([Lx,Ly])

for i in range(Lx):
    for j in range(Ly):
        Ex[i,j] = -(phi[i+1,j]-phi[i,j])/delta_Lx
        Ey[i,j] = -(phi[i,j+1]-phi[i,j])/delta_Ly

plt.figure()

X, Y= np.meshgrid(np.arange(0, Lx,1), np.arange(0, Ly,1))  # メッシュ生成
plt.quiver(X,Y,Ex,Ey,color='red',angles='xy',scale_units='xy', scale=40) # ベクトル場をプロット

plt.xlim([0,Lx]) # 描くXの範囲
plt.ylim([0,Ly]) # 描くyの範囲

# グラフ描画
plt.grid()
plt.draw()
plt.show()

新規登録して、もっと便利にQiitaを使ってみよう

  1. あなたにマッチした記事をお届けします
  2. 便利な情報をあとで効率的に読み返せます
ログインすると使える機能について

コメント

この記事にコメントはありません。
あなたもコメントしてみませんか :)
新規登録
すでにアカウントを持っている方はログイン
52

Qiitaにログインして、便利な機能を使ってみませんか?

あなたにマッチした記事をお届けします

便利な情報をあとから読み返せます