【第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()

【第5話】シリンダー内の粒子の運動計算アルゴリズム【ルンゲクッタで行こう!⑤】

############################################################################
# シリンダーの中の1粒子運動シミュレーション
############################################################################
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

#図全体
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 = 10.0
radius = 0.05
#箱の長さ
L = 2.0
#重力加速度
g = np.array([0.0, 0.0, -10.0])

def F ( t, m, r_t, v_t, fc ):
	return m * g + fc

#初期位置と速度
r0 = np.array([0.0 , 0.0, 0.0])
v0 = np.array([0.5, 0.0, 2.5])
R0 = np.array([0.0 , 0.0, L/2])
V0 = np.array([0.0, 0.0, 0.0])
#時間区間
t_min = 0
t_max = 10.0
#時間区間数
NT =  2000
skip =  1
#時間間隔
dt = (t_max - t_min) / NT
#座標点配列の生成
ts = np.linspace(t_min, t_max, NT + 1)

#衝突判定(戻値:法線ベクトル)
def CollisionDetection( r, R ):
	if(r[0] + radius > L/2):
		return 1, np.array([-1.0, 0.0, 0.0])
	if(r[0] - radius < -L/2):
		return 1, np.array([1.0, 0.0, 0.0])
	if(r[1] + radius > L/2):
		return 1, np.array([0.0, -1.0, 0.0])
	if(r[1] - radius < -L/2):
		return 1, np.array([0.0, 1.0, 0.0])
	if(r[2] + radius> R[2]):
		return 2, np.array([0.0, 0.0, -1.0])
	if(r[2] - radius < -L/2):
		return 1, np.array([0.0, 0.0, 1.0])
	#衝突なし
	return False, np.array([0.0, 0.0, 0.0])
	
######################################
# 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()
		self.dr = np.array([0.0, 0.0, 0.0])
		self.dv = np.array([0.0, 0.0, 0.0])
		self.dt = dt
		self.fc = np.array([0.0, 0.0, 0.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( t, self.m, r_t, v_t, self.fc ) / 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 T(v, V):
	return 1.0/2.0 * m * np.sum( v**2 ) + 1.0/2.0 * M * np.sum( V**2 )
def U(r, R):
	return  - m * np.dot(g, r) - M * np.dot(g, R)
#################################################################
## 計算開始
#インスタンス
rk4_m = RK4(m, dt, r0, v0)
rk4_M = RK4(M, dt, R0, V0)

#アニメーション作成用
ims=[]
def plot( t, r, v, R, V ):
	E = T(v, V) + U(r, R)
	print( "{:.2f}".format(t), E )
	#各コマを描画
	img  = plt.plot([r[0]], [r[2]], colors[0], marker = 'o', markersize = 20, linestyle='solid', linewidth = 0 )
	img += plt.plot([-L/2, L/2], [R[2],R[2]], colors[1], linestyle='solid', linewidth = 10 )
	time = plt.text(0.8, 1.25, "t = " +  str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18)
	#テキストをグラフに追加
	img.append( time )
	ims.append( img )

#各時刻における運動方程式の計算
for tn in range(len(ts)):
	t = ts[tn]
	if( tn%skip == 0 ): 
		plot(t, rk4_m.r, rk4_m.v, rk4_M.r, rk4_M.v )

	#ルンゲ・クッタ法による時間発展
	rk4_m.fc = np.array([0.0, 0.0, 0.0])
	rk4_M.fc = np.array([0.0, 0.0, 0.0])
	rk4_m.timeEvolution( t )
	rk4_M.timeEvolution( t )
	#位置と速度を更新
	rk4_m.r += rk4_m.dr
	rk4_m.v += rk4_m.dv
	rk4_M.r += rk4_M.dr
	rk4_M.v += rk4_M.dv

	CollisionFlag , n = CollisionDetection( rk4_m.r, rk4_M.r )
	if( CollisionFlag ):
		#いったん巻き戻して
		rk4_m.r -= rk4_m.dr
		rk4_m.v -= rk4_m.dv
		rk4_M.r -= rk4_M.dr
		rk4_M.v -= rk4_M.dv
		#衝突力
		if( CollisionFlag == 1 ):
			barV = - rk4_m.v
			rk4_m.fc = (2.0 * m * np.dot(barV, n) * n ) / dt - m * np.dot(g, n) * n
			rk4_M.fc = np.array([0.0, 0.0, 0.0])
		elif( CollisionFlag == 2 ):
			barV = rk4_M.v - rk4_m.v
			rk4_m.fc = 2.0 * m * M / ( m + M ) * np.dot(barV, n) * n / dt
			rk4_M.fc = - rk4_m.fc

		rk4_m.timeEvolution( t )
		rk4_M.timeEvolution( t )
		#位置と速度を更新
		rk4_m.r += rk4_m.dr
		rk4_m.v += rk4_m.dv
		rk4_M.r += rk4_M.dr
		rk4_M.v += rk4_M.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.2, 1.2])
plt.ylim([-1.2, 1.2])

#箱の概形
plt.vlines([-L/2], -L/2, L/2, "black", linestyles='solid', linewidth = 10 )
plt.vlines([L/2], -L/2, L/2, "black", linestyles='solid', linewidth = 10 )
plt.hlines([-L/2], -L/2, L/2, "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("output.mp4", writer="ffmpeg", dpi=300)

#グラフの表示
plt.show()

【第4話】強制振り子の計算アルゴリズム【ルンゲクッタで行こう!④】

############################################################################
# 強制振り子運動シミュレーション
############################################################################
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

#図全体
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

#支点の初期位置
box_r0 = np.array([0.0, 0.0, 0.0])

#初期位置と速度
r0 = np.array([0.0, 0.0, -1.0])
v0 = np.array([0.0, 0.0, 0.0])

#位置ベクトル
L0 = r0 - box_r0
#ひもの長さ
L0_abs = np.sqrt( np.sum( L0**2 ) )

#強制振動の大きさと角振動数
box_l = 0.2
box_omega = 0.5 * (2.0 * np.pi) 

#支点の運動関数
def boxFunction( t ):
	box_rx = box_r0[0] + box_l * np.sin( box_omega * t)
	box_vx = box_l * box_omega * np.cos( box_omega * t)
	box_ax = - box_l * box_omega**2 * np.sin( box_omega * t)
	box_r = np.array([box_rx, 0.0, 0.0])
	box_v = np.array([box_vx, 0.0, 0.0])
	box_a = np.array([box_ax, 0.0, 0.0])
	return box_r, box_v, box_a


#重力加速度
g = np.array([0.0, 0.0, -10.0])
#補正パラメータ
compensationK = 0.0
compensationGamma = 0.0

#張力ベクトル
def S ( t, r_t, v_t):
	#支点の位置ベクトル
	box_r, box_v, box_a = boxFunction(t)	
	#おもり位置ベクトル
	L_t = r_t - box_r
	#おもり速度ベクトル
	v_bar = v_t - box_v
	#ひもの長さ
	L_abs = np.sqrt( np.sum(L_t**2) )

	return -m / L_abs ** 2 * ( np.sum(v_bar**2) - np.dot(box_a, L_t) + np.dot(g, L_t) ) * L_t

def F ( t, r_t, v_t ):
	#支点の位置ベクトル
	box_r, box_v, box_a = boxFunction(t)

	#おもり位置ベクトル
	L_t = r_t - box_r
	#おもり速度ベクトル
	v_bar = v_t - box_v
	#ひもの長さ
	L_abs = np.sqrt( np.sum( L_t**2 ) )
	#おもり速度の大きさ
	v_bar_abs = np.sqrt( np.sum( v_bar**2 ) )
	#単位方向ベクトル
	L_hat = L_t.copy() / L_abs
	#張力ベクトル
	S_ = S( t, r_t, v_t ) 
	#張力の大きさ
	S_coefficient = np.sqrt( np.sum( S_**2 ) )

	#補正力ベクトル
	compensationK = S_coefficient 
	compensationGamma = v_bar_abs * 10
	fc1 = - compensationK * ( L_abs - L0_abs ) * L_hat
	fc2 = - compensationGamma * np.dot(v_bar, L_hat) * L_hat

	#合力ベクトル
	return  S_ + m * g + fc1 + fc2


#時間区間
t_min = 0
t_max = 40.0
#時間区間数
NT = 4000
skip =  2
#時間間隔
dt = (t_max - t_min) / NT
#座標点配列の生成
ts = np.linspace(t_min, t_max, NT + 1)

######################################
# 4次のルンゲ・クッタ クラス
######################################
class RK4:
	#コンストラクタ
	def __init__(self, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ):
		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( t, r_t, v_t ) / 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 

#################################################################
## 計算開始

#インスタンス
rk4 = RK4(dt, r0, v0)

#アニメーション作成用
ims=[]
def plot( t, r ):
	box_r, box_v, box_a = boxFunction(t)
	print( "{:.2f}".format(t),  np.sqrt((r[0]-box_r[0])**2 + (r[2]-box_r[2])**2) - 1  )

	#各コマを描画
	img  = plt.plot([box_r[0]], [box_r[2]], colors[0], marker = 's', markersize = 10, linestyle='solid', linewidth = 0 )
	img  += plt.plot([r[0]], [r[2]], colors[1], marker = 'o', markersize = 20, linestyle='solid', linewidth = 0 )
	img  += plt.plot([box_r[0], r[0]], [box_r[2], r[2]], colors[1], linestyle='solid', linewidth = 1 )
	time = plt.text(0.8, 1.25, "t = " +  str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18)
	#テキストをグラフに追加
	img.append( time )
	ims.append( img )

#各時刻における運動方程式の計算
for tn in range(len(ts)):
	t = ts[tn]
	if( tn%skip == 0 ): 
		plot(t, rk4.r )
	#ルンゲ・クッタ法による時間発展
	rk4.timeEvolution( t )
	#位置と速度を更新
	rk4.r += rk4.dr
	rk4.v += rk4.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.2, 1.2])
plt.ylim([-1.2, 1.2])

#アニメーションの生成
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()

【第26話】【結果は渋いです】2次元電子波束を斜めに薄膜へ(一応トンネル効果)

#################################################################
## 薄膜へ照射したガウス波束(トンネル効果)
#################################################################
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mat
import matplotlib.animation as animation
from multiprocessing import Pool

flag_calculate = False
flag_animation = True

#図全体
aspect = 0.9
fig = plt.figure(figsize=(6, 6 * aspect))
#全体設定
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']

#並列数
ParallelNumber = 6
#フォルダ指定
dir_path = "output30/"
#フォルダ生成
os.makedirs(dir_path, exist_ok = True)
#################################################################
## 物理定数
#################################################################
#プランク定数
h = 6.62607015 * 1.0E-34
hbar = h / (2.0 * np.pi)
#電子の質量
me = 9.1093837015 * 1.0E-31
#電子ボルト
eV = 1.602176634 * 1.0E-19
#虚数単位
I = 0.0 + 1.0j

######################################
# 物理系の設定
######################################
#波束の中心エネルギー
E0 = 10.0 * eV
#入射角度
theta0 = 30.0 / 180 * np.pi
#重ね合わせの数
NK = 100
#ガウス分布の幅
sigma = 0.1 / ( 1.25 * 10**-9 )
#波数の間隔
dk = 10.0 * sigma / NK
#ガウス分布の中心波数
k0 = np.sqrt( 2.0 * me * E0 / hbar**2)
kx0 = k0 * np.cos(theta0)
ky0 = k0 * np.sin(theta0)

#空間刻み間隔
nm = 1E-9
#空間刻み数
NX = 150
NY = 150
#壁の高さ
V1 = 0.0 * eV
V2 = 10.5 * eV
V3 = 0.0 * eV
#壁の幅
d = 1.0 * nm

#計算時間の幅
ts = -200
te = 200
#時間間隔
dt = 1.0 * 10**-16

#空間幅
x_min = -30.0 * nm
x_max =  30.0 * nm
y_min = x_min
y_max = x_max

######################################
# 反射係数と透過係数を計算する関数定義
######################################
def ReflectionCoefficient( MM ):
	return - MM[1][0] / MM[1][1] 
def TransmissionCoefficient(MM):
	return (MM[0][0] * MM[1][1] - MM[0][1] * MM[1][0] ) / MM[1][1]
def calculateCoefficient( k1, k2, k3, cos1, cos2, cos3, d):
	# 転送行列の定義
	M21 = np.zeros((2,2), dtype=np.complex)
	T2  = np.zeros((2,2), dtype=np.complex)
	M32 = np.zeros((2,2), dtype=np.complex)

	# 境界
	M21[0][0] = (1.0 + 0.0j + ( k1 * cos1 ) / ( k2 * cos2 ) ) / 2.0
	M21[0][1] = (1.0 + 0.0j - ( k1 * cos1 ) / ( k2 * cos2 ) ) / 2.0
	M21[1][0] = (1.0 + 0.0j - ( k1 * cos1 ) / ( k2 * cos2 ) ) / 2.0
	M21[1][1] = (1.0 + 0.0j + ( k1 * cos1 ) / ( k2 * cos2 ) ) / 2.0
	M32[0][0] = (1.0 + 0.0j + ( k2 * cos2 ) / ( k3 * cos3 ) ) / 2.0
	M32[0][1] = (1.0 + 0.0j - ( k2 * cos2 ) / ( k3 * cos3 ) ) / 2.0
	M32[1][0] = (1.0 + 0.0j - ( k2 * cos2 ) / ( k3 * cos3 ) ) / 2.0
	M32[1][1] = (1.0 + 0.0j + ( k2 * cos2 ) / ( k3 * cos3 ) ) / 2.0
	T2[0][0] = np.exp( I * k2 * cos2 * d )
	T2[0][1] = 0
	T2[1][0] = 0
	T2[1][1] = np.exp( - I * k2 * cos2 * d )
	#転送行列の計算
	M31 = M32 @ T2 @ M21
	#反射係数と透過係数の計算
	rc = ReflectionCoefficient(M31)
	tc = TransmissionCoefficient(M31)
	return rc, tc, M21

#透過係数と反射係数と転送行列の配列
rcs = np.zeros((NK, NK))
tcs = np.zeros((NK, NK))
rcs = np.array(rcs, dtype=complex)
tcs = np.array(tcs, dtype=complex)
M21s = [ 0 ] * NK
for n1 in range(NK):
	M21s[ n1 ] = [ np.zeros((2,2), dtype=np.complex) ] * NK
for kxn in range(NK):
	for kyn in range(NK):
		kx = kx0 + dk * (kxn - NK/2)
		ky = ky0 + dk * (kyn - NK/2)
		k = np.sqrt( kx**2 + ky**2 )
		k1 = np.sqrt(np.complex128( k**2 - 2.0 * me * V1 / hbar**2 ))
		k2 = np.sqrt(np.complex128( k**2 - 2.0 * me * V2 / hbar**2 ))
		k3 = np.sqrt(np.complex128( k**2 - 2.0 * me * V3 / hbar**2 ))
		sin = ky / k
		cos = kx / k
		sin1 = sin * k / k1
		sin2 = sin * k / k2
		sin3 = sin * k / k3
		cos1 = np.sqrt(np.complex128( 1.0 - sin1**2 ))
		cos2 = np.sqrt(np.complex128( 1.0 - sin2**2 ))
		cos3 = np.sqrt(np.complex128( 1.0 - sin3**2 ))
		rcs[kxn][kyn], tcs[kxn][kyn], M21s[kxn][kyn] = calculateCoefficient( k1, k2, k3, cos1, cos2, cos3, d)

######################################
# 波動関数の計算
######################################
#ガウス波束の初期位置
x0 = 0 #-20.0 * nm * np.cos(theta0)
y0 = 0 #-20.0 * nm * np.sin(theta0)

#ガウス波束の値を計算する関数
def Psi( x, y, t ):
	#波動関数値の初期化
	psi = 0 + 0j
	#各波数ごとの寄与を足し合わせる
	for kxn in range(NK):
		for kyn in range(NK):
			#各波数を取得
			kx = kx0 + dk * (kxn - NK/2)
			ky = ky0 + dk * (kyn - NK/2)
			k = np.sqrt( kx**2 + ky**2 )
			k1 = np.sqrt(np.complex128( k**2 - 2.0 * me * V1 / hbar**2 ))
			k2 = np.sqrt(np.complex128( k**2 - 2.0 * me * V2 / hbar**2 ))
			k3 = np.sqrt(np.complex128( k**2 - 2.0 * me * V3 / hbar**2 ))
			sin = ky / k
			cos = kx / k
			sin1 = sin * k / k1
			sin2 = sin * k / k2
			sin3 = sin * k / k3
			cos1 = np.sqrt(np.complex128( 1.0 - sin1**2 ))
			cos2 = np.sqrt(np.complex128( 1.0 - sin2**2 ))
			cos3 = np.sqrt(np.complex128( 1.0 - sin3**2 ))
			k1x = k1 * cos1
			k1y = k1 * sin1
			k2x = k2 * cos2
			k2y = k2 * sin2
			k3x = k3 * cos3
			k3y = k3 * sin3

			#波数から各振動数を取得
			omega = hbar / (2.0 * me) * ( kx**2  + ky**2 )

			W = np.exp( - I * k1x * x0 - I * k1y * y0) * np.exp( -( (kx - kx0) / (2.0 * sigma) )**2  -( (ky - ky0) / (2.0 * sigma) )**2 )
			if( x < 0 ):
				psi += np.exp( I * ( k1x * x + k1y * y ) - I * omega * t ) * W
				psi += rcs[kxn][kyn] * np.exp( I * ( - k1x * x + k1y * y ) - I * omega * t ) * W
			elif( x < d ):
				A2_p = M21s[kxn][kyn][0][0] + M21s[kxn][kyn][0][1] * rcs[kxn][kyn]
				A2_m = M21s[kxn][kyn][1][0] + M21s[kxn][kyn][1][1] * rcs[kxn][kyn]
				psi += A2_p * np.exp( I * (   k2x * x + k2y * y ) - I * omega * t ) * W
				psi += A2_m * np.exp( I * ( - k2x * x + k2y * y ) - I * omega * t ) * W
			elif( d <= x ):
				psi += tcs[kxn][kyn] * np.exp( I * ( k3x * (x - d) + k3y * y ) - I * omega * t ) * W

	return psi

#基準となる振幅を取得
psi_abs = abs(Psi( x0, 0, 0 ))

######################################
# 空間分布の計算
######################################
#実時間
t = 0
#x軸・y軸のメッシュ生成
xs = np.linspace(x_min, x_max, NX)
ys = np.linspace(y_min, y_max, NY)
X, Y = np.meshgrid(xs, ys)

#並列用ラッパー関数
def wrap_timeEvolution( data ):
	return timeEvolution( *data ) 

#時間発展の計算
def timeEvolution( filepath , tn ):
	#実時間
	t = ( ts + tn ) * dt
	Z = np.sin( np.complex128(X/nm) )
	for ix in range(NX):
		for iy in range(NY):
			x = xs[ix]
			y = ys[iy]
			Z[iy][ix] = Psi( x, y, t ) / psi_abs

	#ファイルへ保存
	np.savetxt( filepath, abs(Z))

#アニメーションフレーム作成用
def update( tn ):
	#グラフ消去
	fig.clear()
	#両軸位置の設定
	axes = fig.add_axes(    [0.07, 0.1/aspect, 0.78, 0.78/aspect])
	#カラーバー位置の設定
	bar_axes = fig.add_axes([0.87, 0.1/aspect, 0.05, 0.78/aspect])
	#データ読み込み	
	filepath = dir_path + str(tn + ts) + ".txt"
	Z = np.loadtxt( filepath )
	#透過領域の振幅を定数倍
	for ny in range(len(X)):
		for nx in range(len(X[ny])):
			if(X[ny][nx] > 1.0 * nm ):
				Z[ny][nx] = Z[ny][nx] * 1000

	Z = Z[:-1, :-1]

	#2次元カラーグラフ描画
	img = axes.pcolormesh( X/nm, Y/nm, Z, vmin=0.0, vmax=1.0, cmap=cmap)
	#カラーバーの追加
	fig.colorbar(img, cax=bar_axes)



####### メイン関数として実行 ########
if __name__ == "__main__":

	#計算開始
	if(flag_calculate):
		list = []
		for tn in range( te - ts + 1 ):
			filepath = dir_path + str(tn + ts) + ".txt"
			list.append( (filepath, tn) )

		with Pool(processes=ParallelNumber) as p:
			p.map(wrap_timeEvolution, list)

	#アニメーション作成
	if( flag_animation ):
		#カラーマップの配色配列
		color_list = []
		color_list.append( [ 0,   "black" ] )
		color_list.append( [ 1,   "white" ] )

		#カラーマップ用オブジェクトの生成
		cmap = mat.colors.LinearSegmentedColormap.from_list('cmap', color_list)

		#アニメーションの設定
		ani = animation.FuncAnimation( fig, update, range(te - ts + 1), 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()

【第3話】拘束条件の改良!【ルンゲクッタで行こう!③】

20210.08.27 アルゴリズムを改良しました。破綻しません。

############################################################################
# 単振り子運動シミュレーション(補正力あり)
############################################################################
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

#図全体
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
#ひもの長さ
r_abs0 = 1.0
#重力加速度
g = np.array([0.0, 0.0, -10.0])
#補正パラメータ
compensationK = 100.0
compensationGamma = 0.0

#張力ベクトル
def S ( t, r_t, v_t ):
	r_abs = np.sqrt( np.sum(r_t**2) )
	return -m / r_abs ** 2 * ( np.sum(v_t**2) + np.dot(g, r_t) ) * r_t

def F ( t, r_t, v_t ):
	r = np.sqrt( np.sum( r_t**2 ) )
	v = np.sqrt( np.sum( v_t**2 ) )
	r_hat = r_t.copy() / r
	v_hat = v_t.copy() / v
	fc  = - compensationK * ( r - r_abs0 ) * r_hat
	fc += - compensationGamma * np.dot(v, r_hat) * r_hat
	return S( t, r_t, v_t ) + m * g + fc


#初期位置と速度
r0 = np.array([0.0 , 0.0, -r_abs0])
v0 = np.array([6.0, 0.0, 0.0])
#時間区間
t_min = 0
t_max = 10.0
#時間区間数
NT = 1000
skip =  1
#時間間隔
dt = (t_max - t_min) / NT
#座標点配列の生成
ts = np.linspace(t_min, t_max, NT + 1)

######################################
# 4次のルンゲ・クッタ クラス
######################################
class RK4:
	#コンストラクタ
	def __init__(self, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ):
		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( t, r_t, v_t ) / 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 

#################################################################
## 計算開始

#インスタンス
rk4 = RK4(dt, r0, v0)

#アニメーション作成用
ims=[]
def plot( t, r ):
	print( "{:.2f}".format(t),  np.sqrt(r[0]**2 + r[2]**2) - 1  )
	#各コマを描画
	img  = plt.plot([0], [0], colors[0], marker = 'o', markersize = 10, linestyle='solid', linewidth = 0 )
	img  += plt.plot([r[0]], [r[2]], colors[1], marker = 'o', markersize = 20, linestyle='solid', linewidth = 0 )
	img  += plt.plot([0, r[0]], [0, r[2]], colors[1], linestyle='solid', linewidth = 1 )
	time = plt.text(0.8, 1.25, "t = " +  str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18)
	#テキストをグラフに追加
	img.append( time )
	ims.append( img )

#各時刻における運動方程式の計算
for tn in range(len(ts)):
	t = ts[tn]
	if( tn%skip == 0 ): 
		plot(t, rk4.r )
	#ルンゲ・クッタ法による時間発展
	rk4.timeEvolution( t )
	#位置と速度を更新
	rk4.r += rk4.dr
	rk4.v += rk4.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.2, 1.2])
plt.ylim([-1.2, 1.2])

#アニメーションの生成
#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()

【第25話】量子ビットの作り方3:静電場の与えたときの電子状態【量子ドットコンピュータの原理③】

############################################################################
# 無限に深い量子井戸中の電子に静電場を加えたときの固有状態(ステップ3)
############################################################################
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

#図全体
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.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 * 10**-9
#計算区間
x_min = -L / 2.0
x_max = L / 2.0
#状態数
n_max = 10
#行列の要素数
DIM = n_max + 1
#空間分割数
NX = 500
#空間刻み間隔
nm = 1.0 * 10**-9
#座標点配列の生成
x = np.linspace(x_min, x_max, NX)
#電場の強さ
Ex = 1.0 * 10**10
#電場の強さ
Ex_max = 1.0 * 10**10
#電場強度の分割数
NEx = 10

#静電場無しの固有関数
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, Ex):
	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 verphi(n1 ,x) * V(x, Ex) * verphi(n2, x) / eV


######################################
# 展開係数の計算
######################################
#固有値・固有ベクトルの初期化
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] * 2


#中心の電場依存性グラフ描画用の配列初期化
averageX = [0] * 2
for n in range(len(averageX)):
	averageX[n] = [0] * (NEx + 1)


######################################
# 静電場を加えた固有関数
######################################
def verphi_Ex(nEx, n, x):
	phi = 0 + 0j
	for m in range(n_max+1):
		phi += vectors[nEx][n][m] * verphi(m, x)	
	return phi


######################################
# #静電場強度ごとの固有関数の計算
######################################
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))


#アニメーション作成用
ims = []
for nEx in range(NEx + 1):
	###固有関数の空間分布
	phi[nEx][0] = abs(verphi_Ex(nEx, 0, x))**2 / (1.0 * 10**9)
	phi[nEx][1] = abs(verphi_Ex(nEx, 1, x))**2 / (1.0 * 10**9)
	#各コマを描画
	img  = plt.plot(x/nm, phi[nEx][0], colors[0], linestyle='solid', linewidth = 5 )
	img += plt.plot(x/nm, phi[nEx][1], colors[1], linestyle='solid', linewidth = 5 )
	time = plt.text(0.4, 4.1, "電場強度:" + str( nEx * 100 / NEx ) + "%" , ha='left', va='center', fontsize=12, fontname="Yu Gothic")
	#テキストをグラフに追加
	img.append( time )
	ims.append( img )


#########################################################################
# グラフの描画(固有関数)
#########################################################################
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, 4.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 )


#アニメーションの生成
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()


【第2話】単振り子の作り方【ルンゲクッタで行こう!②】

20210.08.27 アルゴリズムを改良しました。破綻しません。

############################################################################
# 単振り子運動シミュレーション
############################################################################
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

#図全体
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
#ひもの長さ
r = 1.0
#重力加速度
g = np.array([0.0, 0.0, -10.0])

#張力ベクトル
def S ( t, r_t, v_t ):
	vv = np.sum(v_t**2)
	bb = np.dot(g, r_t)
	r_abs = np.sqrt( np.sum(r_t**2) )
	S_abs =  -m * (vv + bb) / (r_abs ** 2)
	return S_abs * r_t 

def F ( t, r_t, v_t ):
	return S( t, r_t, v_t ) + m * g

#初期位置と速度
r0 = np.array([0.0 , 0.0, -r])
v0 = np.array([6.0, 0.0, 0.0])
#時間区間
t_min = 0
t_max = 10.0
#時間区間数
NT = 1000
skip = 1
#時間間隔
dt = (t_max - t_min) / NT
#座標点配列の生成
ts = np.linspace(t_min, t_max, NT + 1)

######################################
# 4次のルンゲ・クッタ クラス
######################################
class RK4:
	#コンストラクタ
	def __init__(self, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ):
		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( t, r_t, v_t ) / 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 

#################################################################
## 計算開始

#インスタンス
rk4 = RK4(dt, r0, v0)

#アニメーション作成用
ims=[]
def plot( t, r ):
	print( "{:.2f}".format(t),  np.sqrt(r[0]**2 + r[2]**2) - 1  )
	#各コマを描画
	img  = plt.plot([0], [0], colors[0], marker = 'o', markersize = 10, linestyle='solid', linewidth = 0 )
	img  += plt.plot([r[0]], [r[2]], colors[1], marker = 'o', markersize = 20, linestyle='solid', linewidth = 0 )
	img  += plt.plot([0, r[0]], [0, r[2]], colors[1], linestyle='solid', linewidth = 1 )
	time = plt.text(0.8, 1.25, "t = " +  str("{:.2f}".format(t)) + r"$[{\rm s}]$" , ha='left', va='center', fontsize=18)
	#テキストをグラフに追加
	img.append( time )
	ims.append( img )

#各時刻における運動方程式の計算
for tn in range(len(ts)):
	t = ts[tn]
	if( tn%skip == 0 ): 
		plot(t, rk4.r )
	#ルンゲ・クッタ法による時間発展
	rk4.timeEvolution( t )
	#位置と速度を更新
	rk4.r += rk4.dr
	rk4.v += rk4.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.2, 1.2])
plt.ylim([-1.2, 1.2])

#アニメーションの生成
#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()

【第24話】静電場の与え方【量子ドットコンピュータの原理②】

############################################################################
# 無限に深い量子井戸中の電子に静電場を加えたときの固有状態(ステップ1)
############################################################################
import math
import cmath
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.integrate as integrate

#図全体
fig1 = plt.figure(figsize=(10, 6))
#全体設定
plt.rcParams['font.family'] = 'Times New Roman' #フォント
plt.rcParams['font.size'] = 12 #フォントサイズ

#複素数
I = 0.0 + 1.0j

######################################
# 物理定数
######################################
#プランク定数
h = 6.6260896 * 10**-34
hbar = h / (2.0 * math.pi)
#電子の質量
me = 9.10938215 * 10**-31
#電子ボルト
eV = 1.60217733 * 10**-19
#電気素量
e = 1.60217733 * 10**-19

######################################
# 物理系の設定
######################################
#量子井戸の幅
L = 1.0 * 10**-9
#計算区間
x_min = -L / 2.0
x_max = L / 2.0
#状態数
n_max = 10
#電場の強さ
Ex = 1.0*10**10

#固有関数
def verphi(n, x):
    kn = math.pi * (n + 1) / L
    return math.sqrt(2.0 / L) * math.sin(kn * (x + L / 2.0))

#ポテンシャル項
def V(x, Ex):
    return(e * Ex * x)

#被積分関数
def integral_matrixElement(x, n1, n2, Ex):
    return verphi(n1 ,x) * V(x, Ex) * verphi(n2, x) / eV

###行列要素の計算
for n1 in range(n_max + 1):
    for n2 in range(n_max + 1):
        #ガウス・ルジャンドル積分
        result = integrate.quad(
            integral_matrixElement, #被積分関数
            x_min, x_max,           #積分区間の下端と上端
            args=(n1,n2, Ex)            #被積分関数へ渡す引数
        )
        real = result[0]
        imag = 0
        #ターミナルへ出力
        print( "(" + str(n1) + ", " + str(n2) + ")  " + str(real))

【第23話】正規直交系の有難みって?【Pythonコピペで量子力学完全攻略マニュアル】

#################################################################
## 無限に深い量子井戸の固有関数の直交性の確認
#################################################################
import numpy as np
import scipy.integrate as integrate

#################################################################
## 物理定数
#################################################################
#プランク定数
h = 6.62607015 * 1.0E-34
hbar = h / (2.0 * np.pi)
#電子の質量
me = 9.1093837015 * 1.0E-31
#電子ボルト
eV = 1.602176634 * 1.0E-19

#################################################################
## 物理系の設定
#################################################################
#量子井戸の幅(m)
L = 1.0 * 10**-9
#計算区間
x_min = -L / 2.0
x_max = L / 2.0

#固有関数
def verphi(n, x):
    kn = np.pi * (n + 1) / L
    return np.sqrt(2.0 / L) * np.sin(kn * (x + L / 2.0))

#被積分関数
def integral_orthonomality(x, n, m):
    return verphi(m, x) * verphi(n, x)

#状態数
NS = 3
for n in range( NS + 1 ):
    for m in range( NS + 1 ):
        #ガウス・ルジャンドル積分
        result = integrate.quad(
            integral_orthonomality, #被積分関数
            x_min, x_max,           #積分区間の下端と上端
            args=( n, m )           #被積分関数へ渡す引数
        )
        #ターミナルへ出力
        print( "(" + str(n) + ", " + str(m) + ")  " + str( result[0]) )

【第1話】ルンゲクッタで行こう!①【Pythonコピペで古典力学完全攻略マニュアル】

############################################################################
# 単振動運動シミュレーション
############################################################################
import numpy as np
import matplotlib.pyplot as plt

#図全体
fig = plt.figure(figsize=(12, 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']

#################################################################
## 物理定数
#################################################################
#質量
m = 1.0
#ばね定数
k = 1.0

######################################
# 4次のルンゲ・クッタ クラス
######################################
class RK4:
    #コンストラクタ
    def __init__(self, dt = 0.01, r0 = np.array([0.0, 0.0, 0.0]), v0 = np.array([0.0, 0.0, 0.0]) ):
        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, v):
        return -r.copy() * k / 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 

#################################################################
## 物理系の設定
#################################################################

#初期位置と速度
r0 = np.array([0.0, 0.0, 10.0])
v0 = np.array([0.0, 0.0, 0.0])
#時間区間
t_min = 0
t_max = 20.0

#時間区間数
NT = 1000
skip = 10
#時間間隔
dt = (t_max - t_min) / NT
#座標点配列の生成
ts = np.linspace(t_min, t_max, NT + 1)
#インスタンス
rk4 = RK4(dt, r0, v0)
#グラフ描画用位置データ
tgs = []
rgs = []
ags = []

#解析解:初期位置のみ指定
def AnalyticalSolution( t ):
    omega = np.sqrt( k / m )
    return r0[2] *np.cos(omega * t)

#各時刻における運動方程式の計算
for tn in range(len(ts)):
    t = ts[tn]
    if( tn == 0 ):
        tgs.append( t ) 
        rgs.append( rk4.r[2] )
        ags.append( AnalyticalSolution( t ) )
        continue
    #ルンゲ・クッタ法による時間発展
    rk4.timeEvolution( t )
    #位置と速度を更新
    rk4.r += rk4.dr
    rk4.v += rk4.dv
    if( tn%skip == 0  ): 
        tgs.append( t ) 
        rgs.append( rk4.r[2] )
        ags.append( AnalyticalSolution( t ) )

plt.title( u"単振動運動(数値解と解析解)", fontsize=20, fontname="Yu Gothic", fontweight=1000)
plt.xlabel(u"時刻" + r"$\,[{\rm s}]$" , fontsize=20, fontname="Yu Gothic", fontweight=1000)
plt.ylabel(u"位置" + r"$\,[{\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)
#スケールの指定
#ax = plt.gca()
#ax.set_yscale('log')
#ax.set_xscale('log')
#描画範囲を設定
plt.xlim([0, 20])
plt.ylim([-10.5, 10.5])
#プロット
plt.plot(tgs, ags, linestyle='solid', linewidth = 1 )
plt.plot(tgs, rgs, marker = 'o', markersize = 10, linestyle='solid', linewidth = 0 )
#グラフの表示
plt.show()