################################################################# ## [032]コヒーレント状態の時間発展 ################################################################# import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation #図全体 fig = plt.figure(figsize=(15, 8)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 24 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理定数 ################################################################# #プランク定数 h = 6.62607015 * 1.0E-34 hbar = h / (2.0 * np.pi) #電子の質量 me = 9.1093837015 * 1.0E-31 #電子ボルト eV = 1.602176634 * 1.0E-19 #ナノメートル nm = 1E-9 #虚数単位 I = 0.0 + 1.0j ################################################################# ## 規格化エルミート多項式 ################################################################# def NormalizedHermitePolynomial( n, x ): H0 = (1.0 / np.pi)**(1.0/4.0) * np.exp( - x**2 / 2.0 ) H1 = (4.0 / np.pi)**(1.0/4.0) * np.exp( - x**2 / 2.0 ) * x if(n==0): return H0 if(n==1): return H1 for m in range(2, n+1): H2 = np.sqrt( 2.0 / m ) * x * H1 - np.sqrt( (m - 1) / m ) * H0 H0 = H1 H1 = H2 return H2 ################################################################# ## 物理系の設定 ################################################################# #調和ポテンシャルの強さ omega = 1.0 * 1.0E+15 A = np.sqrt( me * omega / hbar) #固有関数 def verphi(n, x): barX = A * x return np.sqrt(A) * NormalizedHermitePolynomial( n, barX ) #固有エネルギー(eV) def Energy(n): return (n + 1.0/2.0) * hbar * omega ################################################################# ## コヒーレント状態 ################################################################# NC = 400 def CoherentState(v, x, t): _psi = 0.0 + 0.0j for n in range(NC): _psi += ff(n, v) * verphi(n, x) * np.exp( - I * omega * (n + 1.0/2.0) * t ) return _psi * np.exp( -abs(v)**2 / 2.0) def ff(n, v, ln = 1): if( n==0 ): return 1 if( n==1 ): return v * ln return ff( n-1, v, ln * v / np.sqrt(n) ) ######################################################################### # 波動関数 アニメーション ######################################################################### #計算時間の幅 ts = 0 te = 1000 #調和振動子の周期 T0 = 2 * 2.0 * np.pi / omega #時間間隔 dt = T0 / (te - ts + 1) plt.title( u"コヒーレント状態の時間発展", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\, [{\rm nm}]$", fontsize=20) plt.ylabel(r"$ \psi_v(x,t) $", fontsize=30) L = 9 * nm #アニメーション作成用 ims = [] #描画範囲 x_min = -L/2 x_max = L/2 #描画区間数 NX = 500 #座標点配列の生成 x = np.linspace(x_min, x_max, NX) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.2, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.2, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([x_min/nm, x_max/nm]) plt.ylim([-1.0, 1.0]) #調和ポテンシャルの概形 yE = 1.0 / 2.0 * me * omega**2 * x**2 /eV /30.0 ims = [] #各時刻における波動関数の計算 for tn in range(ts, te+1): #実時間の取得 t = dt * tn #波動関数の計算 v = 4.0 vx1 = CoherentState(1, x, t) vx4 = CoherentState(4, x, t) vx7 = CoherentState(7, x, t) vx1_real = vx1.real / np.sqrt(A) vx1_imag = vx1.imag / np.sqrt(A) vx4_real = vx4.real / np.sqrt(A) vx4_imag = vx4.imag / np.sqrt(A) vx7_real = vx7.real / np.sqrt(A) vx7_imag = vx7.imag / np.sqrt(A) #波動関数の表示 img = plt.plot( x/nm, yE, linestyle='dotted', color="black", linewidth = 1 ) img += plt.plot( x/nm, vx1_real, colors[0], linestyle='solid', linewidth = 3 ) img += plt.plot( x/nm, vx1_imag, colors[1], linestyle='solid', linewidth = 3 ) img += plt.plot( x/nm, vx4_real, colors[2], linestyle='solid', linewidth = 3 ) img += plt.plot( x/nm, vx4_imag, colors[3], linestyle='solid', linewidth = 3 ) img += plt.plot( x/nm, vx7_real, colors[4], linestyle='solid', linewidth = 3 ) img += plt.plot( x/nm, vx7_imag, colors[5], linestyle='solid', linewidth = 3 ) #アニメーションに追加 ims.append( img ) #余白の調整 #plt.subplots_adjust(left=0.15, right=0.90, bottom=0.1, top=0.99) plt.subplots_adjust(left = 0.12, right = 0.95, bottom = 0.10, top = 0.95) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #アニメーションの保存 #ani.save("output.html", writer=animation.HTMLWriter()) #ani.save('anim.gif', writer='pillow') #ani.save("output.mp4", writer="ffmpeg", dpi=300) #グラフの表示 plt.show()
【第8話】サイクロイド曲線に経路が拘束された粒子の運動【ルンゲクッタで行こう!⑧】
############################################################################ # [008]サイクロイド曲線に経路が拘束された粒子の運動 ############################################################################ import os import numpy as np import random import matplotlib.pyplot as plt import matplotlib.animation as animation random.seed(0) #図全体 fig = plt.figure(figsize=(15, 8)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 16 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理系の設定 ################################################################# #質量 m = 1.0 #時間区間 t_min = 0 t_max = 20.0 #時間区間数 NT = 20000 skip = 20 #時間間隔 dt = (t_max - t_min) / NT #座標点配列の生成 ts = np.linspace(t_min, t_max, NT + 1) #重力加速度 g = np.array([0.0, 0.0, -10.0]) ################################################################# ## 経路の設定 ################################################################# class Path: #コンストラクタ def __init__(self): #始点と終点のx座標 self.x_start = -1 self.x_end = 1 #サイクロイド曲線パラメータr self.r = ( self.x_end - self.x_start ) / ( 2 * np.pi ) #始点と終点のz座標 self.path_z0 = 2 * self.r ; #最下点が0となるように #サイクロイド曲線始点の位置ベクトル self.start = np.array([self.x_start, 0.0, self.path_z0]) #経路の位置ベクトルを指定する媒介変数関数 def position( self, theta ): x = self.r * ( theta - np.sin(theta) ) + self.start[0] y = 0 z = - self.r * ( 1 - np.cos(theta) ) + self.start[2] return np.array([x, y, z]) #接線ベクトルを指定する媒介変数関数 def tangent( self, theta ): A = 1.0 / np.sqrt( 2.0 ) x = A * np.sqrt( 1.0 - np.cos(theta)) y = 0 z = - A * np.sqrt( 1.0 + np.cos(theta)) if( np.sin(theta) < 0 ): z = - z return np.array([x, y, z]) #曲率ベクトルを指定する媒介変数関数 def curvature( self, theta ): A = - 1.0 / ( 4.0 * self.r ) x = - A * np.sqrt( ( 1.0 + np.cos(theta) ) / ( 1.0 - np.cos(theta) ) ) y = 0 z = - A if( np.abs(x) == np.inf ): x = 0 z = 0 if( np.sin(theta) < 0 ): x = - x return np.array([x, y, z]) #媒介変数の取得 def getTheta( self, r, v ): #球体の相対位置ベクトル bar_r = r -self.start if( np.abs( v[0] ) > 0.1 ): _r = np.abs(bar_r[2]) / 2.0 * ( 1.0 + (v[2]/v[0])**2 ) else : _r = self.r if( bar_r[0] < _r * np.pi ) : theta = np.arccos( 1.0 + bar_r[2]/_r ) else: theta = 2.0 * np.pi - np.arccos( 1.0 + bar_r[2]/_r ) return theta #経路の生成 path = Path() #補正パラメータ compensationK = 100.0; #補正ばね弾性係数 compensationGamma = 1.0; #補正粘性抵抗係数 compensationFactor = 1; #補正因子 #粒子に加わる力 def F (self, t, r_t, v_t ): #外場(重力)の計算 fe = m * g #経路自体の運動 v0 = np.array([0.0, 0.0, 0.0]) a0 = np.array([0.0, 0.0, 0.0]) #相対速度 bar_v = v_t - v0 #媒介変数の取得 theta = path.getTheta ( r_t, bar_v ) #媒介変数に対する位置ベクトル、接線ベクトル、曲率ベクトルの計算 position = path.position( theta ) tangent = path.tangent( theta ) curvature = path.curvature( theta ) #微係数dl/dtとd^2l/dt^2を計算 dl_dt = np.dot( bar_v, tangent ) d2l_dt2 = np.dot( fe, tangent ) / m - np.dot( a0, tangent) #加速度を計算 a = a0 + d2l_dt2 * tangent + dl_dt * dl_dt * curvature a_length = np.sqrt(np.sum( a**2 )) #補正倍率 ratio = a_length * compensationFactor #ズレ位置ベクトル bar_r = r_t - position #補正ばね弾性力 fk = -compensationK * ratio * bar_r #法線ベクトル curvature_length = np.sqrt(np.sum( curvature**2 )) n1 = curvature / curvature_length n2 = np.cross(n1, tangent ) #補正粘性抵抗力 fgamma1 = -compensationGamma * np.dot( bar_v, n1 ) * ratio * n1 fgamma2 = -compensationGamma * np.dot( bar_v, n2 ) * ratio * n2 #経路補正力を加える a += fk + fgamma1 + fgamma2 return m * a ###################################### # 4次のルンゲ・クッタ クラス ###################################### class RK4: #コンストラクタ def __init__(self, m, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ): self.m = m #現時刻の位置と速度 self.r = r0.copy() self.v = v0.copy() #1ステップ前の時刻の位置と速度 self._r = r0.copy() self._v = v0.copy() #差分 self.dr = np.array([0.0, 0.0, 0.0]) self.dv = np.array([0.0, 0.0, 0.0]) #時間間隔 self.dt = dt #内部 self.__v1 = np.array([0.0, 0.0, 0.0]) self.__v2 = np.array([0.0, 0.0, 0.0]) self.__v3 = np.array([0.0, 0.0, 0.0]) self.__v4 = np.array([0.0, 0.0, 0.0]) self.__a1 = np.array([0.0, 0.0, 0.0]) self.__a2 = np.array([0.0, 0.0, 0.0]) self.__a3 = np.array([0.0, 0.0, 0.0]) self.__a4 = np.array([0.0, 0.0, 0.0]) #速度を与えるメソッド def V(self, t, r, v): return v.copy() ########################################################## #加速度を与えるメソッド def A(self, t, r_t, v_t): return F(self, t, r_t, v_t ) / self.m ########################################################## #時間発展を計算するメソッド def timeEvolution(self, t): #1段目 self.__v1 = self.V( t, self.r, self.v ) self.__a1 = self.A( t, self.r, self.v ) #2段目 self.__v2 = self.V( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) self.__a2 = self.A( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) #3段目 self.__v3 = self.V( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) self.__a3 = self.A( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) #4段目 self.__v4 = self.V( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) self.__a4 = self.A( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) #差分の計算 self.dr = self.dt * ( self.__v1 + 2.0 * self.__v2 + 2.0 * self.__v3 + self.__v4 ) / 6.0 self.dv = self.dt * ( self.__a1 + 2.0 * self.__a2 + 2.0 * self.__a3 + self.__a4 ) / 6.0 ################################################################# ## 計算開始 ################################################################# #初期条件 particles =[] for i in range(20): #球体の初期位置 theta0 = np.pi #初速度 x0 = path.r * ( theta0 - np.sin(theta0) ) + path.start[0] y0 = 0 z0 = - path.r * ( 1.0 - np.cos(theta0)) + path.start[2] #初期条件 r0 = np.array([ x0, y0, z0 ]) v0 = np.array([ 0.170 * (i+1), 0.0, 0.0 ]) particles.append( RK4( m, dt, r0, v0 ) ) #################################### ## 描画用関数 #################################### #アニメーション作成用 ims=[] #円座標点配列の生成 thetas = np.linspace(0, 2.0*np.pi, 200) p = path.position( thetas ) cirsx = p[0] cirsy = p[2] def plot( t ): #各コマを描画 img = plt.plot(cirsx, cirsy, color = "black", linestyle='dotted', linewidth = 1 ) i = 0 for particle in particles: img += plt.plot([particle.r[0]], [particle.r[2]], color = colors[i], marker = 'o', markersize = 20, linestyle='solid', linewidth = 0 ) i += 1 if (i>9): i = 0 time = plt.text(0.8, 0.717, "t = " + str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18) #テキストをグラフに追加 img.append( time ) ims.append( img ) E = 1.0/2.0 * m * np.sum( particles[0].v**2 ) - m * np.dot( g, particles[0].r ) print( "t = {:.4f}".format(t)," E =", E ) #################################### ## 各時刻における運動方程式の計算 #################################### for tn in range(len(ts)): t = ts[tn] if( tn%skip == 0 ): plot(t) #ルンゲ・クッタ法による時間発展 for particle in particles: particle.timeEvolution( t ) #位置と速度を更新 for particle in particles: particle.r += particle.dr particle.v += particle.dv ################################################################# ## 分子運動アニメーション ################################################################# plt.title( u"サイクロイド曲線経路に拘束された球体の運動", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.ylabel(r"$z\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-1.1, 1.1]) plt.ylim([-0.1, 0.7]) plt.subplots_adjust(left = 0.12, right = 0.97, bottom = 0.10, top = 0.90) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #ani.save("output.html", writer=animation.HTMLWriter()) #ani.save("output.gif", writer="imagemagick") ani.save("output.mp4", writer="ffmpeg", dpi=300) #グラフの表示 plt.show()
【第7話】経路が拘束された粒子の運動【ルンゲクッタで行こう!⑦】
############################################################################ # [007]経路に拘束された剛体球の運動 ############################################################################ import os import numpy as np import random import matplotlib.pyplot as plt import matplotlib.animation as animation random.seed(0) #図全体 fig = plt.figure(figsize=(8, 8)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 16 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理系の設定 ################################################################# #質量 m = 1.0 #時間区間 t_min = 0 t_max = 10.0 #時間区間数 NT = 1000 skip = 10 #時間間隔 dt = (t_max - t_min) / NT #座標点配列の生成 ts = np.linspace(t_min, t_max, NT + 1) #重力加速度 g = np.array([0.0, 0.0, -10.0]) ################################################################# ## 経路の設定 ################################################################# class Path: #コンストラクタ def __init__(self): #円の半径 self.R = 1 #円の中心位置ベクトル self.center = np.array([0.0, 0.0, self.R]) #経路の位置ベクトルを指定する媒介変数関数 def position( self, theta ): x = self.R * np.cos(theta) y = 0 z = self.R * np.sin(theta) + self.center[2] return np.array([x, y, z]) #接線ベクトルを指定する媒介変数関数 def tangent( self, theta ): x = -np.sin(theta) y = 0 z = np.cos(theta) return np.array([x, y, z]) #曲率ベクトルを指定する媒介変数関数 def curvature( self, theta ): x = -np.cos(theta) / self.R y = 0 z = -np.sin(theta) / self.R return np.array([x, y, z]) #媒介変数の取得 def getTheta( self, r ): #相対位置ベクトル bar_r = r - self.center bar_r_length = np.sqrt(np.sum( bar_r**2 )) sinTheta = bar_r[2] / bar_r_length if( sinTheta > 0 ): theta = np.arccos( bar_r[0] / bar_r_length ) else: theta = 2.0 * np.pi - np.arccos( bar_r[0] / bar_r_length ) return theta #経路の生成 path = Path() #補正パラメータ compensationK = 1.0; #補正ばね弾性係数 compensationGamma = 1.0; #補正粘性抵抗係数 compensationFactor = 0; #補正因子 #粒子に加わる力 def F (self, t, r_t, v_t ): #外場(重力)の計算 fe = m * g #媒介変数の取得 theta = path.getTheta ( r_t ) #媒介変数に対する位置ベクトル、接線ベクトル、曲率ベクトルの計算 position = path.position( theta ) tangent = path.tangent( theta ) curvature = path.curvature( theta ) #経路自体の運動 v0 = np.array([0.0, 0.0, 0.0]) a0 = np.array([0.0, 0.0, 0.0]) #相対速度 bar_v = v_t - v0 #微係数dl/dtとd^2l/dt^2を計算 dl_dt = np.dot( bar_v, tangent ) d2l_dt2 = np.dot( fe, tangent ) / m - np.dot( a0, tangent) #加速度を計算 a = a0 + d2l_dt2 * tangent + dl_dt * dl_dt * curvature a_length = np.sqrt(np.sum( a**2 )) #補正倍率 ratio = a_length * compensationFactor #ズレ位置ベクトル bar_r = r_t - position #補正ばね弾性力 fk = -compensationK * ratio * bar_r #法線ベクトル curvature_length = np.sqrt(np.sum( curvature**2 )) n1 = curvature / curvature_length n2 = np.cross(n1, tangent ) #補正粘性抵抗力 fgamma1 = -compensationGamma * np.dot( bar_v, n1 ) * ratio * n1 fgamma2 = -compensationGamma * np.dot( bar_v, n2 ) * ratio * n2 #経路補正力を加える a += fk + fgamma1 + fgamma2 return m * a ###################################### # 4次のルンゲ・クッタ クラス ###################################### class RK4: #コンストラクタ def __init__(self, m, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ): self.m = m #現時刻の位置と速度 self.r = r0.copy() self.v = v0.copy() #1ステップ前の時刻の位置と速度 self._r = r0.copy() self._v = v0.copy() #差分 self.dr = np.array([0.0, 0.0, 0.0]) self.dv = np.array([0.0, 0.0, 0.0]) #時間間隔 self.dt = dt #内部 self.__v1 = np.array([0.0, 0.0, 0.0]) self.__v2 = np.array([0.0, 0.0, 0.0]) self.__v3 = np.array([0.0, 0.0, 0.0]) self.__v4 = np.array([0.0, 0.0, 0.0]) self.__a1 = np.array([0.0, 0.0, 0.0]) self.__a2 = np.array([0.0, 0.0, 0.0]) self.__a3 = np.array([0.0, 0.0, 0.0]) self.__a4 = np.array([0.0, 0.0, 0.0]) #速度を与えるメソッド def V(self, t, r, v): return v.copy() ########################################################## #加速度を与えるメソッド def A(self, t, r_t, v_t): return F(self, t, r_t, v_t ) / self.m ########################################################## #時間発展を計算するメソッド def timeEvolution(self, t): #1段目 self.__v1 = self.V( t, self.r, self.v ) self.__a1 = self.A( t, self.r, self.v ) #2段目 self.__v2 = self.V( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) self.__a2 = self.A( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) #3段目 self.__v3 = self.V( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) self.__a3 = self.A( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) #4段目 self.__v4 = self.V( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) self.__a4 = self.A( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) #差分の計算 self.dr = self.dt * ( self.__v1 + 2.0 * self.__v2 + 2.0 * self.__v3 + self.__v4 ) / 6.0 self.dv = self.dt * ( self.__a1 + 2.0 * self.__a2 + 2.0 * self.__a3 + self.__a4 ) / 6.0 ################################################################# ## 計算開始 ################################################################# #初期条件 particles =[] for i in range(20): particles.append( RK4( m, dt, np.array([ 0.0, 0.0, 2.0 ]), np.array([ 0.001 * (i+1), 0.0, 0.0 ] ) ) ) #################################### ## 描画用関数 #################################### #アニメーション作成用 ims=[] #円座標点配列の生成 thetas = np.linspace(0, 2*np.pi, 200) cirsx = np.cos( thetas ) cirsy = np.sin( thetas ) + 1.0 def plot( t ): #各コマを描画 img = plt.plot(cirsx, cirsy, color = "black", linestyle='dotted', linewidth = 1 ) i = 0 for particle in particles: img += plt.plot([particle.r[0]], [particle.r[2]], color = colors[i], marker = 'o', markersize = 20, linestyle='solid', linewidth = 0 ) i += 1 if (i>9): i = 0 time = plt.text(0.8, 2.18, "t = " + str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18) #テキストをグラフに追加 img.append( time ) ims.append( img ) E = 1.0/2.0 * m * np.sum( particles[0].v**2 ) - m * np.dot( g, particles[0].r ) print( "t = {:.4f}".format(t)," E =", E ) #################################### ## 各時刻における運動方程式の計算 #################################### for tn in range(len(ts)): t = ts[tn] if( tn%skip == 0 ): plot(t) #ルンゲ・クッタ法による時間発展 for particle in particles: particle.timeEvolution( t ) #位置と速度を更新 for particle in particles: particle.r += particle.dr particle.v += particle.dv ################################################################# ## 分子運動アニメーション ################################################################# plt.title( u"円経路に拘束された球体の運動", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.ylabel(r"$z\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-1.1, 1.1]) plt.ylim([-0.1, 2.1]) plt.subplots_adjust(left = 0.12, right = 0.97, bottom = 0.10, top = 0.90) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #ani.save("output.html", writer=animation.HTMLWriter()) #ani.save("output.gif", writer="imagemagick") #ani.save("output.mp4", writer="ffmpeg", dpi=300) #グラフの表示 #plt.show()
【第31話】調和ポテンシャル中の波動関数の初期配置の与え方【Pythonコピペで量子力学完全攻略マニュアル】
################################################################# ## 調和ポテンシャル中の電子の固有関数 初期値の与え方 ################################################################# import math import numpy as np import scipy.integrate as integrate import matplotlib.pyplot as plt import matplotlib.animation as animation #図全体 fig = plt.figure(figsize=(15, 8)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 24 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理定数 ################################################################# #プランク定数 h = 6.62607015 * 1.0E-34 hbar = h / (2.0 * np.pi) #電子の質量 me = 9.1093837015 * 1.0E-31 #電子ボルト eV = 1.602176634 * 1.0E-19 #ナノメートル nm = 1E-9 #虚数単位 I = 0.0 + 1.0j ################################################################# ## 規格化エルミート多項式 ################################################################# def NormalizedHermitePolynomial( n, x ): H0 = (1.0 / np.pi)**(1.0/4.0) * np.exp( - x**2 / 2.0 ) H1 = (4.0 / np.pi)**(1.0/4.0) * np.exp( - x**2 / 2.0 ) * x if(n==0): return H0 if(n==1): return H1 for m in range(2, n+1): H2 = np.sqrt( 2.0 / m ) * x * H1 - np.sqrt( (m - 1) / m ) * H0 H0 = H1 H1 = H2 return H2 ################################################################# ## 物理系の設定 ################################################################# #量子井戸の幅(m) omega = 1.0 * 1.0E+15 A = np.sqrt( me * omega / hbar) #固有関数 def verphi(n, x): barX = A * x return np.sqrt(A) * NormalizedHermitePolynomial( n, barX ) #固有エネルギー(eV) def Energy(n): return (n + 1.0/2.0) * hbar * omega #状態数 NS = 100 ################################################################# ## 初期分布:ガウス分布 ################################################################# sigma = 1.0 * 10**10 x0 = 1.0 * nm def verphi0(x): return ( sigma**2 / np.pi )**(1.0/4.0) * np.exp( - 1.0/2.0 * sigma**2 * (x - x0)**2 ) #被積分関数 def integral_orthonomality(x, n): return verphi(n, x) * verphi0(x) L = 7 * nm #描画範囲 x_min = -L/2 x_max = L/2 cn = [] for n in range( NS + 1 ): #ガウス・ルジャンドル積分 result = integrate.quad( integral_orthonomality, #被積分関数 x_min, x_max, #積分区間の下端と上端 args = ( n ) #被積分関数へ渡す引数 ) cn.append( result[0] ) #ターミナルへ出力 print( "(" + str(n) + ") " + str( result[0]) ) ################################################################# ## 波動関数 ################################################################# def psi(x, t): _psi = 0.0 + 0.0j for n in range(NS): _psi += cn[n] * verphi(n, x) * np.exp( - I * ( n + 1.0/2.0) * omega * t ) return _psi ######################################################################### # 波動関数 アニメーション ######################################################################### #計算時間の幅 ts = 0 te = 100 #基底状態の周期 T0 = 2.0 * np.pi * hbar / Energy(0) print( "基底状態の周期:" + str(T0) + " [s]" ) #時間間隔 dt = T0 / (te - ts + 1) plt.title( u"調和ポテンシャル中の波動関数", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\, [{\rm nm}]$", fontsize=20) L = 7 * nm #アニメーション作成用 ims = [] #描画範囲 x_min = -L/2 x_max = L/2 #描画区間数 NX = 500 #座標点配列の生成 x = np.linspace(x_min, x_max, NX) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.2, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.2, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-3.5, 3.5]) plt.ylim([-1.5, 6.5]) #調和ポテンシャルの概形 yE = 1.0 / 2.0 * me * omega**2 * x**2 /eV /5.0 ims = [] #各時刻における波動関数の計算 for tn in range(ts, te): #実時間の取得 t = dt * tn #波動関数の計算 ys = psi(x, t).real / np.sqrt(A) * 2+ yE #波動関数の表示 img = plt.plot( x/nm, yE, linestyle='dotted', color="black", linewidth = 1 ) img += plt.plot( x/nm, ys, colors[0], linestyle='solid', linewidth = 3 ) #アニメーションに追加 ims.append( img ) #余白の調整 #plt.subplots_adjust(left=0.15, right=0.90, bottom=0.1, top=0.99) plt.subplots_adjust(left = 0.05, right = 0.95, bottom = 0.10, top = 0.95) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #アニメーションの保存 #ani.save("output.html", writer=animation.HTMLWriter()) #ani.save('anim.gif', writer='pillow') #ani.save("output.mp4", writer="ffmpeg", dpi=300) #グラフの表示 plt.show()
【はやくち解説】エルミート多項式の有難みって?【Pythonコピペで量子力学完全攻略マニュアル】
################################################################# ## 規格化エルミート多項式 ################################################################# import math import numpy as np import scipy.integrate as integrate import matplotlib.pyplot as plt #図全体 fig = plt.figure(figsize=(9, 9)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 24 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] #インデックス最大数 NS = 6 ################################################################# ## エルミート多項式 ################################################################# def NormalizedHermitePolynomial( n, x ): H0 = (1.0 / np.pi)**(1.0/4.0) * np.exp( - x**2 / 2.0 ) H1 = (4.0 / np.pi)**(1.0/4.0) * np.exp( - x**2 / 2.0 ) * x if(n==0): return H0 if(n==1): return H1 for m in range(2, n+1): H2 = np.sqrt( 2.0 / m ) * x * H1 - np.sqrt( (m - 1) / m ) * H0 H0 = H1 H1 = H2 return H2 ################################################################# ## 直交性の確認 ################################################################# #被積分関数 def integral_orthonomality(x, n, m): return NormalizedHermitePolynomial( m, x ) * NormalizedHermitePolynomial(n, x) for n in range( NS + 1 ): for m in range( NS + 1 ): #ガウス・ルジャンドル積分 result = integrate.quad( integral_orthonomality, #被積分関数 -10, 10, #積分区間の下端と上端 args=( n, m ) #被積分関数へ渡す引数 ) #ターミナルへ出力 print( "(" + str(n) + ", " + str(m) + ") " + str( result[0]) ) ######################################################################### # グラフの描画(固有関数) ######################################################################### plt.title( u"規格化エルミート多項式", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x$", fontsize=30) #描画区間数 NX = 500 #描画範囲 x_min = -5 x_max = 5 #座標点配列の生成 x = np.linspace(x_min, x_max, NX) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.6, linestyle = "-", linewidth = 0) plt.grid(which = "major", axis = "y", alpha = 0.6, linestyle = "-", linewidth = 0) #描画範囲を設定 plt.xlim([x_min , x_max]) plt.ylim([-0.5, 10]) plt.yticks(color="None") plt.tick_params(length=0) #グラフの描画 ys = [ 0 ] * (NS + 1) for n in range( NS + 1 ): ys[n] = NormalizedHermitePolynomial( n, x ) + n * 1.5 for n in range( NS + 1 ): plt.plot(x, ys[n], linestyle='solid', linewidth = 5 ) plt.text( 5.1, n * 1.5 - 0.12, r"$H_" + str(n) + "$", fontsize = 25) plt.hlines([n * 1.5], -5, 5, "#000000", linestyles='dotted', linewidth = 1 ) #余白の調整 #plt.subplots_adjust(left=0.15, right=0.90, bottom=0.1, top=0.99) plt.subplots_adjust(left = 0.05, right = 0.92, bottom = 0.12, top = 0.95) #グラフの表示 plt.show()
【第29話】調和ポテンシャル中の電子状態【Pythonコピペで量子力学完全攻略マニュアル】
################################################################# ## 調和ポテンシャル中の電子の固有関数 ################################################################# import math import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation #図全体 fig = plt.figure(figsize=(8, 8)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 24 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理定数 ################################################################# #プランク定数 h = 6.62607015 * 1.0E-34 hbar = h / (2.0 * np.pi) #電子の質量 me = 9.1093837015 * 1.0E-31 #電子ボルト eV = 1.602176634 * 1.0E-19 #ナノメートル nm = 1E-9 #虚数単位 I = 0.0 + 1.0j ################################################################# ## 物理系の設定 ################################################################# #量子井戸の幅(m) omega = 1.0 * 1.0E+15 A = np.sqrt( me * omega / hbar) #エルミート多項式 def HermitePolynomial( n, x ): x0 = 1 x1 = 2.0 * x if(n==0): return x0 if(n==1): return x1 for m in range(2, n+1): x2 = 2.0 * x * x1 - 2.0 * ( m - 1 ) * x0 x0 = x1 x1 = x2 return x2 #固有関数 def verphi(n, x): barX = A * x return ( A**2 / np.pi) **(1.0/4.0) * 1.0 / np.sqrt( 2**n * math.factorial(n) ) * np.exp( - 1.0/2.0 * barX **2 ) * HermitePolynomial( n, barX ) #固有エネルギー(eV) def Energy(n): return (n + 1.0/2.0) * hbar * omega #波動関数 def psi(n, x, t): return verphi(n, x) * np.exp( - I * ( n + 1.0 / 2.0 ) * omega * t ) #状態数 NS = 4 #固有エネルギーの表示(eV) for n in range( NS + 1 ): if( n == 0 ): text = "基底状態" else: text = "第" + str(n) + "励起状態" print( text + ":" + str(Energy( n )/eV) + " [eV]" ) #計算時間の幅 ts = 0 te = 100 #基底状態の周期 T0 = 2.0 * np.pi * hbar / Energy(0) print( "基底状態の周期:" + str(T0) + " [s]" ) ######################################################################### # 波動関数 アニメーション ######################################################################### #時間間隔 dt = T0 / (te - ts + 1) plt.title( u"調和ポテンシャル中の電子の固有関数", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\, [{\rm nm}]$", fontsize=20) L = 6 * nm #アニメーション作成用 ims = [] #描画範囲 x_min = -L/2 x_max = L/2 #描画区間数 NX = 500 #座標点配列の生成 x = np.linspace(x_min, x_max, NX) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.2, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.2, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-3, 3]) plt.ylim([-0.5, 7.5]) NS = 6 #調和ポテンシャルの概形 yE = 1.0 / 2.0 * me * omega**2 * x**2 /eV #各時刻における波動関数の計算 for tn in range(ts, te): #実時間の取得 t = dt * tn img = plt.plot( x/nm, yE, linestyle='dotted', color="black", linewidth = 1 ) #各コマを描画 for n in range( NS + 1 ): #波動関数の計算 y = psi(n, x, t).real / np.sqrt(A) * 0.8 + n + 0.5 #波動関数の表示 img += plt.plot( x/nm, y, colors[n], linestyle='solid', linewidth = 3 ) #アニメーションに追加 ims.append( img ) #E_0~E_6の表示 for n in range( 0, NS + 1 ): plt.text( 3.1, 0.5 + n - 0.12, r"$E_" + str(n) + "$", fontsize = 25) plt.hlines([0.5 + n], -3, 3, "#CCCCCC", linestyles='dotted', linewidth = 1 ) #余白の調整 #plt.subplots_adjust(left=0.15, right=0.90, bottom=0.1, top=0.99) plt.subplots_adjust(left = 0.05, right = 0.92, bottom = 0.10, top = 0.95) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #アニメーションの保存 #ani.save("output.html", writer=animation.HTMLWriter()) #ani.save('anim.gif', writer='pillow') ani.save("output.mp4", writer="ffmpeg", dpi=300) #グラフの表示 plt.show()
【第6話】剛体球同士の衝突力計算アルゴリズム【ルンゲクッタで行こう!⑥】
############################################################################ # 箱の中のN個の自由粒子(古典力学) ############################################################################ import os import numpy as np import random import matplotlib.pyplot as plt import matplotlib.animation as animation random.seed(0) #図全体 fig = plt.figure(figsize=(10, 10)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 16 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理系の設定 ################################################################# #質量 m_min = 1.0 m_max = 10.0 #粒子の半径 radius_min = 0.002 radius_max = 0.02 #粒子数 NM = 100 #箱の長さ L = 1.0 #粒子に加わる力 fex = np.array([0.0, 0.0, 0.0]) #初期位置と速度 v_max = 2.0 #粒子に加わる力 def F (self, t, r_t, v_t ): return self.fex + self.fc - self.m * self.beta * v_t #時間区間 t_min = 0 t_max = 5.0 #時間区間数 NT = 500 skip = 1 #時間間隔 dt = (t_max - t_min) / NT #座標点配列の生成 ts = np.linspace(t_min, t_max, NT + 1) # lw = 0.01 ###################################### # 4次のルンゲ・クッタ クラス ###################################### class RK4: #コンストラクタ def __init__(self, m, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ): self.m = m #現時刻の位置と速度 self.r = r0.copy() self.v = v0.copy() #1ステップ前の時刻の位置と速度 self._r = r0.copy() self._v = v0.copy() #差分 self.dr = np.array([0.0, 0.0, 0.0]) self.dv = np.array([0.0, 0.0, 0.0]) #時間間隔 self.dt = dt #外力 self.fex = np.array([0.0, 0.0, 0.0]) #衝突力 self.fc = np.array([0.0, 0.0, 0.0]) #粘性係数 self.beta = 0 #半径 self.radius = 0 #内部 self.__v1 = np.array([0.0, 0.0, 0.0]) self.__v2 = np.array([0.0, 0.0, 0.0]) self.__v3 = np.array([0.0, 0.0, 0.0]) self.__v4 = np.array([0.0, 0.0, 0.0]) self.__a1 = np.array([0.0, 0.0, 0.0]) self.__a2 = np.array([0.0, 0.0, 0.0]) self.__a3 = np.array([0.0, 0.0, 0.0]) self.__a4 = np.array([0.0, 0.0, 0.0]) #速度を与えるメソッド def V(self, t, r, v): return v.copy() ########################################################## #加速度を与えるメソッド def A(self, t, r_t, v_t): return F(self, t, r_t, v_t ) / self.m ########################################################## #時間発展を計算するメソッド def timeEvolution(self, t): #1段目 self.__v1 = self.V( t, self.r, self.v ) self.__a1 = self.A( t, self.r, self.v ) #2段目 self.__v2 = self.V( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) self.__a2 = self.A( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) #3段目 self.__v3 = self.V( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) self.__a3 = self.A( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) #4段目 self.__v4 = self.V( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) self.__a4 = self.A( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) #差分の計算 self.dr = self.dt * ( self.__v1 + 2.0 * self.__v2 + 2.0 * self.__v3 + self.__v4 ) / 6.0 self.dv = self.dt * ( self.__a1 + 2.0 * self.__a2 + 2.0 * self.__a3 + self.__a4 ) / 6.0 ################################################################# ## 時間発展の計算 ################################################################# #壁との衝突判定(戻値:法線ベクトル) def CollisionWallDetection( particle ): #衝突リスト collisionlist = [] if(particle.r[0] + particle.radius > L): collisionlist.append({ "type": 1, "normal" : np.array([-1.0, 0.0, 0.0]) }) if(particle.r[0] - particle.radius < 0): collisionlist.append({ "type": 1, "normal" : np.array([1.0, 0.0, 0.0]) }) if(particle.r[1] + particle.radius > L): collisionlist.append({ "type": 1, "normal" : np.array([0.0, -1.0, 0.0]) }) if(particle.r[1] - particle.radius < 0): collisionlist.append({ "type": 1, "normal" : np.array([0.0, 1.0, 0.0]) }) if(particle.r[2] + particle.radius > L): collisionlist.append({ "type": 1, "normal" : np.array([0.0, 0.0, -1.0]) }) if(particle.r[2] - particle.radius < 0): collisionlist.append({ "type": 1, "normal" : np.array([0.0, 0.0, 1.0]) }) return collisionlist #粒子の運動エネルギー def T(): _T = 0 for nm in range(NM): _T += 1.0/2.0 * particles[nm].m * np.sum( particles[nm].v**2 ) return _T #粒子の位置エネルギー def U(): _R = 0 for nm in range(NM): _R += - np.dot(particles[nm].fex, particles[nm].r) return _R #系の時間発展の計算 def timeEvolution( t, timeScale ): #複数衝突 cylinderCollisionList = [] for nm in range(NM): #時間スケールの指定 particles[nm].dt = dt / timeScale #ルンゲ・クッタ法による時間発展 particles[nm].fc = np.array([0.0, 0.0, 0.0]) particles[nm].timeEvolution( t ) #位置と速度を更新 particles[nm].r += particles[nm].dr particles[nm].v += particles[nm].dv #壁との衝突リスト CollisionWallList = CollisionWallDetection( particles[nm] ) #衝突あり if( len(CollisionWallList) > 0 ): #2つ以上の壁との衝突がある場合 → 高精度計算モードへ if( len(CollisionWallList) > 1 ): return False #以下、衝突が1箇所 normal = CollisionWallList[ 0 ]["normal"] #いったん巻き戻しておく particles[nm].r -= particles[nm].dr particles[nm].v -= particles[nm].dv #衝突力 if( CollisionWallList[ 0 ]["type"] == 1 ): #壁との衝突 #print(nm) #相対速度 barV = - particles[nm].v particles[nm].fc = (2.0 * particles[nm].m * np.dot(barV, normal) * normal ) / particles[nm].dt - np.dot(particles[nm].fex, normal) * normal #位置と速度を更新 particles[nm].timeEvolution( t ) particles[nm].r += particles[nm].dr particles[nm].v += particles[nm].dv #粒子同士の衝突判定 for nm1 in range(NM): for nm2 in range(NM): if( nm1 >= nm2 ): continue #2点間ベクトル r12 = particles[nm2].r - particles[nm1].r r12_length = np.sqrt(np.sum( r12**2 )) if( r12_length < particles[nm1].radius + particles[nm2].radius ): #print( t, nm1, nm2 ) #すでに衝突力が与えられている場合 → 高精度計算モードへ if( (particles[nm1].fc[0] != 0 or particles[nm1].fc[1] != 0 or particles[nm1].fc[2] != 0) or ( particles[nm2].fc[0] != 0 or particles[nm2].fc[1] != 0 or particles[nm2].fc[2] != 0 )): #時間発展:失敗 return False n12 = r12 / r12_length v12 = particles[nm2].v - particles[nm1].v m1 = particles[nm1].m m2 = particles[nm2].m barf = particles[nm2].fex / m2 - particles[nm1].fex / m1 f21 = 2.0 * m1 * m2 / ( m1 + m2 ) * np.dot(v12, n12) * n12 / particles[nm1].dt f21 += m1 * m2 / ( m1 + m2 ) * np.dot(barf, n12) * n12 f12 = -f21 #衝突力の設定 particles[nm1].fc = f21 particles[nm2].fc = f12 #いったん巻き戻しておく particles[nm1].r -= particles[nm1].dr particles[nm1].v -= particles[nm1].dv particles[nm2].r -= particles[nm2].dr particles[nm2].v -= particles[nm2].dv #時間発展を計算 particles[nm1].timeEvolution( t ) particles[nm2].timeEvolution( t ) #位置と速度を更新 particles[nm1].r += particles[nm1].dr particles[nm1].v += particles[nm1].dv particles[nm2].r += particles[nm2].dr particles[nm2].v += particles[nm2].dv #2点間ベクトル r12 = particles[nm2].r - particles[nm1].r r12_length = np.sqrt(np.sum( r12**2 )) #計算後の2粒子が重なっている場合は失敗 if( r12_length < particles[nm1].radius + particles[nm2].radius ): #時間発展:失敗 return False #計算後、壁とあるいは粒子同士が重なっている場合は失敗 for nm1 in range(NM): #壁との衝突リスト CollisionWallList = CollisionWallDetection( particles[nm1] ) #衝突あり if( len(CollisionWallList) > 0 ): #時間発展:失敗 return False for nm2 in range(NM): if( nm1 >= nm2 ): continue #2点間ベクトル r12 = particles[nm2].r - particles[nm1].r r12_length = np.sqrt(np.sum( r12**2 )) if( r12_length < particles[nm1].radius + particles[nm2].radius ): #時間発展:失敗 return False #時間発展:正常 return True #高精度計算モード def highPrecisionMode(t, level): print ( "レベル" , level, "t=", round(t, level+3 )) timeScale = 10**level for nm in range(NM): particles[nm].r = particles[nm]._r.copy() particles[nm].v = particles[nm]._v.copy() for _n in range(0, 10): t_level = t + dt / timeScale * _n result_level = timeEvolution( t_level, timeScale ) if( result_level == False ): #もっと高精度が必要 level += 1 highPrecisionMode(t_level, level) level -= 1 else: #時間発展前の位置と速度を保持 for nm in range(NM): particles[nm]._r = particles[nm].r.copy() particles[nm]._v = particles[nm].v.copy() ################################################################# ## 計算開始 ################################################################# #粒子 particles = [] for nm in range(NM): radius = radius_min + ( radius_max - radius_min) * random.random() m = m_min + ( m_max - m_min) * random.random() theta_v = 2.0 * np.pi * random.random() phi_v = 0 * 2.0 * np.pi * random.random() _v_max = v_max * random.random() v0 = np.array([ _v_max * np.sin( theta_v ) * np.cos( phi_v ), _v_max * np.sin( theta_v ) * np.sin( phi_v ), _v_max * np.cos( theta_v ) ]) for i in range(1000): r0 = np.array([ radius + (L - 2.0 * radius) * random.random(), L / 2.0, radius + (L - 2.0 * radius) * random.random() ]) flag = True for p in particles: #2点間ベクトル r12 = r0 - p.r r12_length = np.sqrt(np.sum( r12**2 )) if( r12_length < radius + p.radius ): flag = False break if(flag == False): continue particles.append( RK4( m, dt, r0, v0 )) particles[ len( particles ) - 1 ].radius = radius break #粒子外力の設定 for nm in range(NM): particles[nm].fex = fex #################################### ## 描画用関数 #################################### #アニメーション作成用 ims=[] #ピストン位置の配列 zs = [] def plot( t ): #各コマを描画 for nm in range(NM): markersize = particles[nm].radius * 1000 colorRG = 1.0 - particles[nm].m / m_max if( nm == 0 ): img = plt.plot([particles[nm].r[0]], [particles[nm].r[2]], color = (colorRG, colorRG, 1.0), marker = 'o', markersize = markersize, linestyle='solid', linewidth = 0 ) else: img += plt.plot([particles[nm].r[0]], [particles[nm].r[2]], color = (colorRG, colorRG, 1.0), marker = 'o', markersize = markersize, linestyle='solid', linewidth = 0 ) time = plt.text(0.9, 1.12, "t = " + str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18) #テキストをグラフに追加 img.append( time ) ims.append( img ) E = T() + U() print( "t = {:.4f}".format(t)," E =", E ) #################################### ## 各時刻における運動方程式の計算 #################################### for tn in range(len(ts)): t = ts[tn] if( tn%skip == 0 ): plot(t) #時間発展前の位置と速度を保持 for nm in range(NM): particles[nm]._r = particles[nm].r.copy() particles[nm]._v = particles[nm].v.copy() level = 0 #時間発展の計算 result = timeEvolution( t, 1 ) #複数衝突を検知した場合 if( result == False ): level += 1 highPrecisionMode(t, level ) ################################################################# ## 分子運動アニメーション ################################################################# plt.title( u"箱の中の粒子の運動", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.ylabel(r"$z\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-0.1, 1.1]) plt.ylim([-0.1, 1.1]) #箱の概形 plt.vlines([0-lw], -2*lw, L + 2*lw, "black", linestyles='solid', linewidth = 10 ) plt.vlines([L+lw], -2*lw, L + 2*lw, "black", linestyles='solid', linewidth = 10 ) plt.hlines([-lw], 0, L, "black", linestyles='solid', linewidth = 10 ) plt.hlines([L+lw], 0, L, "black", linestyles='solid', linewidth = 10 ) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #ani.save("output.html", writer=animation.HTMLWriter()) #ani.save("output.gif", writer="imagemagick") #ani.save("output100.mp4", writer="ffmpeg", dpi=300) #グラフの表示 plt.show()
【統計力学001】理想気体で状態方程式シミュレーション!【Pythonで分子動力学入門①】
############################################################################ # シリンダーの中のN個の粒子 ############################################################################ import os import numpy as np import random import matplotlib.pyplot as plt import matplotlib.animation as animation random.seed(0) #図全体 fig = plt.figure(figsize=(10, 10)) #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 16 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ################################################################# ## 物理系の設定 ################################################################# #質量 m = 1.0 M = 1000.0 #粒子数 N = 100 #箱の長さ L = 1.0 #シリンダーに加わる外力:重力 Fex = np.array([0.0, 0.0, -10.0 * M ]) #シリンダーの粘性係数 beta = 0 #粒子に加わる力 fex = np.array([0.0, 0.0, 0.0 ]) #初期位置と速度 v_max = 10.0 #粒子に加わる力 def F (self, t, r_t, v_t ): return self.fex + self.fc - self.m * self.beta * v_t #時間区間 t_min = 0 t_max = 2.0 #時間区間数 NT = 2000 skip = 5 #時間間隔 dt = (t_max - t_min) / NT #座標点配列の生成 ts = np.linspace(t_min, t_max, NT + 1) # lw = 0.01 ###################################### # 4次のルンゲ・クッタ クラス ###################################### class RK4: #コンストラクタ def __init__(self, m, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ): self.m = m #現時刻の位置と速度 self.r = r0.copy() self.v = v0.copy() #1ステップ前の時刻の位置と速度 self._r = r0.copy() self._v = v0.copy() #差分 self.dr = np.array([0.0, 0.0, 0.0]) self.dv = np.array([0.0, 0.0, 0.0]) #時間間隔 self.dt = dt #外力 self.fex = np.array([0.0, 0.0, 0.0]) #衝突力 self.fc = np.array([0.0, 0.0, 0.0]) #粘性係数 self.beta = 0 #内部 self.__v1 = np.array([0.0, 0.0, 0.0]) self.__v2 = np.array([0.0, 0.0, 0.0]) self.__v3 = np.array([0.0, 0.0, 0.0]) self.__v4 = np.array([0.0, 0.0, 0.0]) self.__a1 = np.array([0.0, 0.0, 0.0]) self.__a2 = np.array([0.0, 0.0, 0.0]) self.__a3 = np.array([0.0, 0.0, 0.0]) self.__a4 = np.array([0.0, 0.0, 0.0]) #速度を与えるメソッド def V(self, t, r, v): return v.copy() ########################################################## #加速度を与えるメソッド def A(self, t, r_t, v_t): return F(self, t, r_t, v_t ) / self.m ########################################################## #時間発展を計算するメソッド def timeEvolution(self, t): #1段目 self.__v1 = self.V( t, self.r, self.v ) self.__a1 = self.A( t, self.r, self.v ) #2段目 self.__v2 = self.V( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) self.__a2 = self.A( t + self.dt / 2.0, self.r + self.__v1 * self.dt / 2.0, self.v + self.__a1 * self.dt / 2.0 ) #3段目 self.__v3 = self.V( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) self.__a3 = self.A( t + self.dt / 2.0, self.r + self.__v2 * self.dt / 2.0, self.v + self.__a2 * self.dt / 2.0 ) #4段目 self.__v4 = self.V( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) self.__a4 = self.A( t + self.dt, self.r + self.__v3 * self.dt, self.v + self.__a3 * self.dt ) #差分の計算 self.dr = self.dt * ( self.__v1 + 2.0 * self.__v2 + 2.0 * self.__v3 + self.__v4 ) / 6.0 self.dv = self.dt * ( self.__a1 + 2.0 * self.__a2 + 2.0 * self.__a3 + self.__a4 ) / 6.0 ################################################################# ## 時間発展の計算 ################################################################# #衝突判定(戻値:法線ベクトル) def CollisionDetection( r, R ): #衝突リスト collisionlist = [] if(r[0] > L): collisionlist.append({ "type": 1, "normal" : np.array([-1.0, 0.0, 0.0]) }) if(r[0] < 0): collisionlist.append({ "type": 1, "normal" : np.array([1.0, 0.0, 0.0]) }) if(r[1] > L): collisionlist.append({ "type": 1, "normal" : np.array([0.0, -1.0, 0.0]) }) if(r[1] < 0): collisionlist.append({ "type": 1, "normal" : np.array([0.0, 1.0, 0.0]) }) if(r[2] > R[2]): collisionlist.append({ "type": 2, "normal" : np.array([0.0, 0.0, -1.0]) }) if(r[2] < 0): collisionlist.append({ "type": 1, "normal" : np.array([0.0, 0.0, 1.0]) }) return collisionlist #運動エネルギー def T(): _T = 1.0/2.0 * M * np.sum( rk4_M.v**2 ) for n in range(N): _T += 1.0/2.0 * m * np.sum( rk4_ms[n].v**2 ) return _T #位置エネルギー def U(): _R = - np.dot(rk4_M.fex, rk4_M.r) for n in range(N): _R += - np.dot(rk4_ms[n].fex, rk4_ms[n].r) return _R #粒子の運動エネルギー def T_particle(): _T = 0 for n in range(N): _T += 1.0/2.0 * m * rk4_ms[n].v[2]**2 return _T #シリンダーの位置エネルギー(底を基準) def U_cylinder(): _R = - np.dot(rk4_M.fex, rk4_M.r) + M * L/2 return _R #系の時間発展の計算 def timeEvolution( t, timeScale ): #時間スケールの指定 rk4_M.dt = dt / timeScale rk4_M.fc = np.array([0.0, 0.0, 0.0]) rk4_M.timeEvolution( t ) rk4_M.r += rk4_M.dr rk4_M.v += rk4_M.dv #複数衝突 cylinderCollisionList = [] for mn in updateList: #時間スケールの指定 rk4_ms[mn].dt = dt / timeScale #ルンゲ・クッタ法による時間発展 rk4_ms[mn].fc = np.array([0.0, 0.0, 0.0]) rk4_ms[mn].timeEvolution( t ) #位置と速度を更新 rk4_ms[mn].r += rk4_ms[mn].dr rk4_ms[mn].v += rk4_ms[mn].dv #衝突リスト collisionlist = CollisionDetection( rk4_ms[mn].r, rk4_M.r ) #衝突あり if( len(collisionlist) > 0 ): #2つ以上の壁orシリンダーとの衝突がある場合 → 高精度計算モードへ if( len(collisionlist) > 1 ): return False #以下、衝突が1箇所 normal = collisionlist[ 0 ]["normal"] #いったん巻き戻しておく rk4_ms[mn].r -= rk4_ms[mn].dr rk4_ms[mn].v -= rk4_ms[mn].dv #衝突力 if( collisionlist[ 0 ]["type"] == 1 ): #壁との衝突 #相対速度 barV = - rk4_ms[mn].v rk4_ms[mn].fc = (2.0 * m * np.dot(barV, normal) * normal ) / rk4_ms[mn].dt - np.dot(rk4_ms[mn].fex, normal) * normal #位置と速度を更新 rk4_ms[mn].timeEvolution( t ) rk4_ms[mn].r += rk4_ms[mn].dr rk4_ms[mn].v += rk4_ms[mn].dv if( collisionlist[ 0 ]["type"] == 2 ): #ピストンとの衝突 cylinderCollisionList.append( { "mn": mn, "normal":normal} ) #ピストンとの衝突ありの場合 if( len(cylinderCollisionList) > 0 ): #ピストンをいったん巻き戻して rk4_M.r -= rk4_M.dr rk4_M.v -= rk4_M.dv #衝突数が1個の場合 if( len(cylinderCollisionList) == 1 ): mn = cylinderCollisionList[0]["mn"] normal = cylinderCollisionList[0]["normal"] #衝突力の計算 barV = rk4_M.v - rk4_ms[mn].v barFex = rk4_M.fex / M - rk4_ms[mn].fex / m rk4_ms[mn].fc = 2.0 * m * M / ( m + M ) * np.dot(barV, normal) * normal / rk4_ms[mn].dt rk4_ms[mn].fc += m * M / ( m + M ) * np.dot(barFex, normal) * normal rk4_M.fc = - rk4_ms[mn].fc #時間発展を計算 rk4_ms[mn].timeEvolution( t ) rk4_M.timeEvolution( t ) #位置と速度を更新 rk4_M.r += rk4_M.dr rk4_M.v += rk4_M.dv rk4_ms[mn].r += rk4_ms[mn].dr rk4_ms[mn].v += rk4_ms[mn].dv #衝突数が2個以上の場合 if(len(cylinderCollisionList) > 1): #時間発展:失敗 return False #時間発展:正常 return True #高精度計算モード def highPrecisionMode(t, level): print ( "レベル" , level, "t=", round(t, level+3 )) timeScale = 10**level #時間ステップ計算前に巻き戻す rk4_M.r = rk4_M._r.copy() rk4_M.v = rk4_M._v.copy() for mn in range(N): rk4_ms[mn].r = rk4_ms[mn]._r.copy() rk4_ms[mn].v = rk4_ms[mn]._v.copy() for _n in range(0, 10): t_level = t + dt / timeScale * _n result_level = timeEvolution( t_level, timeScale ) if( result_level == False ): #もっと高精度が必要 level += 1 highPrecisionMode(t_level, level) level -= 1 else: #時間発展前の位置と速度を保持 rk4_M._r = rk4_M.r.copy() rk4_M._v = rk4_M.v.copy() for mn in updateList: rk4_ms[mn]._r = rk4_ms[mn].r.copy() rk4_ms[mn]._v = rk4_ms[mn].v.copy() ################################################################# ## 計算開始 ################################################################# #初速度配列 v0s = [] #初期運動エネルギーの計算(z成分のみ) Tz0 = 0 for n in range(N): theta_v = np.pi * random.random() phi_v = 2.0 * np.pi * random.random() _v_max = v_max * random.random() v0s.append( np.array([ _v_max * np.sin( theta_v ) * np.cos( phi_v ), _v_max * np.sin( theta_v ) * np.sin( phi_v ), _v_max * np.cos( phi_v ) ])) Tz0 += 1.0/2.0 * m * v0s[len(v0s)-1][2]**2 #ピストンの初期位置 h0 = L #2*Tz0/(M*10) rk4_ms = [] for n in range(N): r0 = np.array([ L * random.random(), L * random.random(), h0 * random.random() ]) rk4_ms.append( RK4(m, dt, r0, v0s[n]) ) #粒子外力の設定 for n in range(N): rk4_ms[n].fex = fex #シリンダーの初期値 R0 = np.array([0.0, 0.0, h0 ]) V0 = np.array([0.0, 0.0, 0.0]) rk4_M = RK4(M, dt, R0, V0) rk4_M.beta = beta rk4_M.fex = Fex #################################### ## 描画用関数 #################################### #アニメーション作成用 ims=[] #ピストン位置の配列 zs = [] def plot( t ): #各コマを描画 img = plt.plot([0-lw, L+lw], [rk4_M.r[2] + lw, rk4_M.r[2] + lw], colors[1], linestyle='solid', linewidth = 10 ) for n in range(N): img += plt.plot([rk4_ms[n].r[0]], [rk4_ms[n].r[2]], colors[0], marker = 'o', markersize = 10, linestyle='solid', linewidth = 0 ) time = plt.text(0.9, 1.12, "t = " + str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18) #テキストをグラフに追加 img.append( time ) ims.append( img ) E = T() + U() print( "{:.2f}".format(t), E ) zs.append( [t, rk4_M.r[2]] ) #print( "{:.2f}".format(t), U_cylinder(), T_particle(), 3.0 * U_cylinder()/T_particle() ) #################################### ## 各時刻における運動方程式の計算 #################################### for tn in range(len(ts)): t = ts[tn] if( tn%skip == 0 ): plot(t) #計算対象粒子リスト(ここでは全部) updateList = list(range(0, N)) #時間発展前の位置と速度を保持 rk4_M._r = rk4_M.r.copy() rk4_M._v = rk4_M.v.copy() for mn in updateList: rk4_ms[mn]._r = rk4_ms[mn].r.copy() rk4_ms[mn]._v = rk4_ms[mn].v.copy() level = 0 #時間発展の計算 result = timeEvolution( t, 1 ) #複数衝突を検知した場合 if( result == False ): level += 1 highPrecisionMode(t, level ) #フォルダ指定 dir_path = "output/" #フォルダ生成 os.makedirs(dir_path, exist_ok = True) filepath = dir_path + "z.txt" #ファイルへ保存 np.savetxt( filepath, zs) ################################################################# ## 分子運動アニメーション ################################################################# plt.title( u"シリンダーの中の粒子の運動", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel(r"$x\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.ylabel(r"$z\,[{\rm m}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-0.1, 1.1]) plt.ylim([-0.1, 1.1]) #箱の概形 plt.vlines([0-lw], -2*lw, L, "black", linestyles='solid', linewidth = 10 ) plt.vlines([L+lw], -2*lw, L, "black", linestyles='solid', linewidth = 10 ) plt.hlines([-lw], 0, L, "black", linestyles='solid', linewidth = 10 ) #アニメーションの生成 ani = animation.ArtistAnimation(fig, ims, interval=10) #ani.save("output.html", writer=animation.HTMLWriter()) ani.save("output.gif", writer="imagemagick") #ani.save("output100.mp4", writer="ffmpeg", dpi=300) #グラフの表示 #plt.show() ######################################################################### # グラフの描画(エネルギー固有値) ######################################################################### fig1 = plt.figure(figsize=(15, 8)) plt.title( u"ピストンの位置", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( u"時間" + r"$\,[{\rm s}]$", fontsize=30, fontname="Yu Gothic", fontweight=1000 ) plt.ylabel( u"高さ" + r"$\,[{\rm m}]$", fontsize=30, fontname="Yu Gothic", fontweight=1000 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([0, t_max]) plt.ylim([0, L]) xs = [] ys = [] for zt in zs: xs.append( zt[0] ) ys.append( zt[1] ) #グラフを描画 plt.plot(xs, ys, linewidth = 5) #グラフの表示 plt.show()
【第28話】量子ビットの作り方5:1量子ビットの完成!!【量子ドットコンピュータの原理⑤】
############################################################################ # 衝立ありの無限に深い量子井戸に静電場を加えた電子状態 ############################################################################ import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation import scipy.integrate as integrate import numpy as np import numpy.linalg as LA #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 24 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ###################################### # 物理定数 ###################################### #プランク定数 h = 6.6260896 * 10**-34 hbar = h / (2.0 * np.pi) #電子の質量 me = 9.10938215 * 10**-31 #電子ボルト eV = 1.60217733 * 10**-19 #電気素量 e = 1.60217733 * 10**-19 #複素数 I = 0.0 + 1.0j ###################################### # 物理系の設定 ###################################### #量子井戸の幅 L = 1.0 * 10**-9 #計算区間 x_min = -L / 2.0 x_max = L / 2.0 #状態数 n_max = 30 #行列の要素数 DIM = n_max + 1 #空間分割数 NX = 500 #空間刻み間隔 nm = 1.0 * 10**-9 #壁の厚さ W = L / 5 #壁の高さの最大値 V_max = 30.0 * eV #電場の強さ Ex_max = 1.0 * 10**8 #電場強度の分割数 NEx = 10 #基底状態と励起状態 N = 2 ###################################### # 衝立なし・静電場無しの解析解 ###################################### #固有関数 def varphi(n, x): kn = np.pi * (n + 1) / L return np.sqrt(2.0 / L) * np.sin(kn * (x + L / 2.0)) #ポテンシャル項 def V(x, Ex): if(abs(x) <= W / 2.0): return e * Ex * x + V_max else: return e * Ex * x #固有エネルギー def Energy(n): kn = np.pi * (n + 1) / L return hbar * hbar * kn * kn / (2.0 * me) ###################################### # 静電場ごとの固有関数の計算 ###################################### #被積分関数(行列要素計算用) def integral_matrixElement(x, n1, n2, Ex): return varphi(n1 ,x) * V(x, Ex) * varphi(n2, x) / eV #被積分関数(平均計算用) def average_x(x, a): sum = 0 for n in range(n_max + 1): sum += a[n] * varphi(n, x) return x * sum**2 #固有値・固有ベクトルの初期化 eigenvalues = [0] * (NEx + 1) vectors = [0] * (NEx + 1) for nEx in range(NEx + 1): eigenvalues[nEx] = [] vectors[nEx] = [] #存在確率分布グラフ描画用の配列初期化 xs = [] phi = [0] * (NEx + 1) for nEx in range(NEx + 1): phi[nEx] = [0] * N for n in range( len(phi[nEx]) ): phi[nEx][n] = [0] * (NX + 1) #中心の電場依存性グラフ描画用の配列初期化 averageX = [0] * N for n in range(len(averageX)): averageX[n] = [0] * (NEx + 1) #静電場強度ごとに for nEx in range(NEx + 1): print("電場強度:" + str( nEx * 100 / NEx ) + "%") #静電場の強度を設定 Ex = Ex_max / NEx * nEx #エルミート行列(リスト) matrix = [] ###行列要素の計算 for n1 in range(n_max + 1): col=[] for n2 in range(n_max + 1): #ガウス・ルジャンドル積分 result = integrate.quad( integral_matrixElement, #被積分関数 x_min, x_max, #積分区間の下端と上端 args=(n1, n2, Ex) #被積分関数へ渡す引数 ) real = result[0] imag = 0j #無静電場のエネルギー固有値(対角成分) En = Energy(n1)/eV if (n1 == n2) else 0 #行の要素を追加 col.append( En + real ) #行を追加 matrix.append( col ) #リスト → 行列 matrix = np.array( matrix ) ###固有値と固有ベクトルの計算 result = LA.eig( matrix ) eig = result[0] #固有値 vec = result[1] #固有ベクトル #小さい順に並べるためのインデックス(配列) index = np.argsort( eig ) #固有値を小さい順に並び替え eigenvalues[nEx] = eig[ index ] #転置行列 vec = vec.T #固有ベクトルの並び替え vectors[nEx] = vec[ index ] ### 検算:MA-EA=0 ? sum = 0 for i in range(DIM): v = matrix @ vectors[nEx][i] - eigenvalues[nEx][i] * vectors[nEx][i] for j in range(DIM): sum += abs(v[j])**2 print("|MA-EA| =" + str(sum)) ###固有関数の空間分布 for nx in range(NX+1): x = x_min + (x_max - x_min) / NX * nx if(nEx == 0): xs.append( x/nm ) for n in range( len(phi[nEx]) ): for m in range(n_max+1): phi[nEx][n][nx] += vectors[nEx][n][m] * varphi(m, x) #描画用データの整形 phi[nEx][n][nx] = abs(phi[nEx][n][nx])**2 / (1.0 * 10**9) for n in range(len(averageX)): #ガウス・ルジャンドル積分 result = integrate.quad( average_x, #被積分関数 x_min, x_max, #積分区間の下端と上端 args=(vectors[nEx][n]) #被積分関数へ渡す引数 ) #計算結果の取得 averageX[n][nEx] = result[0] * (1.0 * 10**9) ######################################################################### # グラフの描画(エネルギー固有値) ######################################################################### fig1 = plt.figure(figsize=(15, 8)) plt.title( u"無限に深い量子井戸(衝立あり)に静電場を加えたときの固有エネルギー", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( u"静電場の強さ" + r"$\times 10^7 \,[{\rm V/m}]$", fontsize=30, fontname="Yu Gothic", fontweight=1000 ) plt.ylabel( r"$ E\,[{\rm eV}]$", fontsize=30, fontname="Yu Gothic", fontweight=1000 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([0, 10]) #x軸 exs = range( NEx + 1) #y軸 En_0 = [] En_1 = [] for nEx in range(NEx + 1): En_0.append( eigenvalues[nEx][0] ) En_1.append( eigenvalues[nEx][1] ) #print( str(nV) + " " + str( eigenvalues[nV][0] ) + " " + str( eigenvalues[nV][1] )) #基底状態と第1励起状態のグラフを描画 plt.plot(exs, En_0, marker="o", markersize=15, linewidth = 5) plt.plot(exs, En_1, marker="o", markersize=15, linewidth = 5) #グラフの表示 plt.show() ######################################################################### # グラフの描画(基底状態) ######################################################################### fig2 = plt.figure(figsize=(15, 8)) plt.title( u"無限に深い量子井戸(衝立あり)に静電場を加えたときの基底状態", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( r"$x\, [{\rm nm}]$", fontsize=30 ) plt.ylabel( r"$|\varphi^{(m)}(x)|^2$", fontsize=30 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #井戸の概形 plt.vlines([-0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.hlines([0], -0.5, 0.5, "black", linestyles='solid', linewidth = 10 ) #衝立の概形 plt.vlines([-0.1], -1, 3, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.1], -1, 3, "black", linestyles='solid', linewidth = 10 ) plt.hlines([3], -0.106, 0.106, "black", linestyles='solid', linewidth = 10 ) #描画範囲を設定 plt.xlim([-0.6, 0.6]) plt.ylim([0, 5.0]) #各 for nEx in range(NEx + 1): plt.plot(xs, phi[nEx][0] , linewidth = 5) #グラフの表示 plt.show() ######################################################################### # グラフの描画(第1励起状態) ######################################################################### fig3 = plt.figure(figsize=(15, 8)) plt.title( u"無限に深い量子井戸(衝立あり)に静電場を加えたときの第1励起状態", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( r"$x\, [{\rm nm}]$", fontsize=30 ) plt.ylabel( r"$ |\varphi^{(m)}(x)|^2$", fontsize=30 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-0.6, 0.6]) plt.ylim([0, 5.0]) #井戸の概形 plt.vlines([-0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.hlines([0], -0.5, 0.5, "black", linestyles='solid', linewidth = 10 ) #衝立の概形 plt.vlines([-0.1], -1, 3, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.1], -1, 3, "black", linestyles='solid', linewidth = 10 ) plt.hlines([3], -0.106, 0.106, "black", linestyles='solid', linewidth = 10 ) for nEx in range(NEx + 1): plt.plot(xs, phi[nEx][1] , linewidth = 5) #グラフの表示 plt.show()
【第27話】量子ビットの作り方4:量子井戸の真ん中に衝立を配置したときの電子状態【量子ドットコンピュータの原理④】
############################################################################ # 衝立ありの無限に深い量子井戸中の電子状態 ############################################################################ import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation import scipy.integrate as integrate import numpy as np import numpy.linalg as LA #全体設定 plt.rcParams['font.family'] = 'Times New Roman' #フォント plt.rcParams['font.size'] = 24 #フォントサイズ plt.rcParams["mathtext.fontset"] = 'cm' #数式用フォント #カラーリストの取得 colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] ###################################### # 物理定数 ###################################### #プランク定数 h = 6.6260896 * 10**-34 hbar = h / (2.0 * np.pi) #電子の質量 me = 9.10938215 * 10**-31 #電子ボルト eV = 1.60217733 * 10**-19 #電気素量 e = 1.60217733 * 10**-19 #複素数 I = 0.0 + 1.0j ###################################### # 物理系の設定 ###################################### #量子井戸の幅 L = 1.0 * 10**-9 #計算区間 x_min = -L / 2.0 x_max = L / 2.0 #状態数 n_max = 30 #行列の要素数 DIM = n_max + 1 #空間分割数 NX = 500 #空間刻み間隔 nm = 1.0 * 10**-9 #壁の厚さ W = L / 5 #壁の高さの最大値 V_max = 30.0 * eV #壁の高さの分割数 NV = 30 ###################################### # 衝立なしの解析解 ###################################### #固有関数 def verphi(n, x): kn = np.pi * (n + 1) / L return np.sqrt(2.0 / L) * np.sin(kn * (x + L / 2.0)) #ポテンシャル項 def V(x, V0): if(abs(x) <= W / 2.0): return V0 else: return 0 #固有エネルギー def Energy(n): kn = np.pi * (n + 1) / L return hbar * hbar * kn * kn / (2.0 * me) ###################################### # 衝立ごとの固有関数の計算 ###################################### #被積分関数(行列要素計算用) def integral_matrixElement(x, n1, n2, V0): return verphi(n1 ,x) * V(x, V0) * verphi(n2, x) / eV #被積分関数(平均計算用) def average_x(x, a): sum = 0 for n in range(n_max + 1): sum += a[n] * verphi(n, x) return x * sum**2 #固有値・固有ベクトルの初期化 eigenvalues = [0] * (NV + 1) vectors = [0] * (NV + 1) for nV in range(NV + 1): eigenvalues[nV] = [] vectors[nV] = [] #存在確率分布グラフ描画用の配列初期化 xs = [] phi = [0] * (NV + 1) for nV in range(NV + 1): phi[nV] = [0] * 2 for n in range( len(phi[nV]) ): phi[nV][n] = [0] * (NX + 1) #中心の電場依存性グラフ描画用の配列初期化 averageX = [0] * 2 for n in range(len(averageX)): averageX[n] = [0] * (NV + 1) #衝立の高さごとに for nV in range(NV + 1): #if(nV == 0): continue print("衝立の高さ:" + str( nV * 100 / NV ) + "%") #衝立を設定 V0 = V_max / NV * nV #エルミート行列(リスト) matrix = [] ###行列要素の計算 for n1 in range(n_max + 1): col=[] for n2 in range(n_max + 1): #ガウス・ルジャンドル積分 result = integrate.quad( integral_matrixElement, #被積分関数 x_min, x_max, #積分区間の下端と上端 args=(n1, n2, V0) #被積分関数へ渡す引数 ) real = result[0] #print("(" + str(n1) + "," + str(n2) + ") " + str(real) ) imag = 0j #無静電場のエネルギー固有値(対角成分) En = Energy(n1)/eV if (n1 == n2) else 0 #行の要素を追加 col.append( En + real ) #行を追加 matrix.append( col ) #リスト → 行列 matrix = np.array( matrix ) ###固有値と固有ベクトルの計算 result = LA.eig( matrix ) eig = result[0] #固有値 vec = result[1] #固有ベクトル #小さい順に並べるためのインデックス(配列) index = np.argsort( eig ) #固有値を小さい順に並び替え eigenvalues[nV] = eig[ index ] #転置行列 vec = vec.T #固有ベクトルの並び替え vectors[nV] = vec[ index ] ### 検算:MA-EA=0 ? sum = 0 for i in range(DIM): v = matrix @ vectors[nV][i] - eigenvalues[nV][i] * vectors[nV][i] for j in range(DIM): sum += abs(v[j])**2 print("|MA-EA| =" + str(sum)) ###固有関数の空間分布 for nx in range(NX+1): x = x_min + (x_max - x_min) / NX * nx if(nV == 0): xs.append( x/nm ) for n in range( len(phi[nV]) ): for m in range(n_max+1): phi[nV][n][nx] += vectors[nV][n][m] * verphi(m, x) #描画用データの整形 phi[nV][n][nx] = abs(phi[nV][n][nx])**2 / (1.0 * 10**9) for n in range(len(averageX)): #ガウス・ルジャンドル積分 result = integrate.quad( average_x, #被積分関数 x_min, x_max, #積分区間の下端と上端 args=(vectors[nV][n]) #被積分関数へ渡す引数 ) #計算結果の取得 averageX[n][nV] = result[0] * (1.0 * 10**9) ######################################################################### # グラフの描画(エネルギー固有値) ######################################################################### fig1 = plt.figure(figsize=(15, 8)) plt.title( u"無限に深い量子井戸に衝立を加えたときの固有エネルギー", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( u"衝立の高さ" + r"$ V\,[{\rm eV}]$", fontsize=30, fontname="Yu Gothic", fontweight=1000 ) plt.ylabel( r"$ E\,[{\rm eV}]$", fontsize=30, fontname="Yu Gothic", fontweight=1000 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([0, 30]) #x軸 exs = range( NV + 1 ) #y軸 En_0 = [] En_1 = [] for nV in range(NV + 1): En_0.append( eigenvalues[nV][0] ) En_1.append( eigenvalues[nV][1] ) print( str(nV) + " " + str( eigenvalues[nV][0] ) + " " + str( eigenvalues[nV][1] ) ) #基底状態と第1励起状態のグラフを描画 plt.plot(exs, En_0, marker="o", markersize=15, linewidth = 5) plt.plot(exs, En_1, marker="o", markersize=15, linewidth = 5) #グラフの表示 plt.show() ######################################################################### # グラフの描画(基底状態) ######################################################################### fig2 = plt.figure(figsize=(15, 8)) plt.title( u"無限に深い量子井戸に衝立を加えたときの基底状態", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( r"$x\, [{\rm nm}]$", fontsize=30 ) plt.ylabel( r"$|\varphi^{(m)}(x)|^2$", fontsize=30 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-0.6, 0.6]) plt.ylim([0, 3.0]) #井戸の概形 plt.vlines([-0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.hlines([0], -0.5, 0.5, "black", linestyles='solid', linewidth = 10 ) #衝立の概形 plt.vlines([-0.1], -1, 2, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.1], -1, 2, "black", linestyles='solid', linewidth = 10 ) plt.hlines([2], -0.106, 0.106, "black", linestyles='solid', linewidth = 10 ) for nV in range(NV + 1): plt.plot(xs, phi[nV][0], linewidth = 5) #グラフの表示 plt.show() ######################################################################### # グラフの描画(第1励起状態) ######################################################################### fig3 = plt.figure(figsize=(15, 8)) plt.title( u"無限に深い量子井戸に静電場を加えたときの第1励起状態", fontsize=20, fontname="Yu Gothic", fontweight=1000) plt.xlabel( r"$x\, [{\rm nm}]$", fontsize=30 ) plt.ylabel( r"$ |\varphi^{(m)}(x)|^2$", fontsize=30 ) #余白の調整 plt.subplots_adjust(left = 0.1, right = 0.98, bottom=0.12, top = 0.95) #罫線の描画 plt.grid(which = "major", axis = "x", alpha = 0.8, linestyle = "-", linewidth = 1) plt.grid(which = "major", axis = "y", alpha = 0.8, linestyle = "-", linewidth = 1) #描画範囲を設定 plt.xlim([-0.6, 0.6]) plt.ylim([0, 3.0]) #井戸の概形 plt.vlines([-0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.5], -1, 20, "black", linestyles='solid', linewidth = 10 ) plt.hlines([0], -0.5, 0.5, "black", linestyles='solid', linewidth = 10 ) #衝立の概形 plt.vlines([-0.1], -1, 2, "black", linestyles='solid', linewidth = 10 ) plt.vlines([0.1], -1, 2, "black", linestyles='solid', linewidth = 10 ) plt.hlines([2], -0.106, 0.106, "black", linestyles='solid', linewidth = 10 ) for nV in range(NV + 1): plt.plot(xs, phi[nV][1] , linewidth = 5) #グラフの表示 plt.show()