量子化学計算は高度なシミュレーションソフト(GaussianやGAMESSなど)が必要と思っていませんか?
実は簡単な分子のハートリー・フォック計算(RHF法)であれば、わずか200行ほどプログラミングするだけで量子化学のシミュレーションができます。
最低限のpython知識だけで作成したRHF法のプログラミングコードを利用して量子化学を体験してみましょう。
HeH+をpythonで量子化学プログラミング
最低限のpython知識しかなくても、簡単な分子であれば200行程度プログラミングするだけで量子化学計算ができます。(必要となるpythonの知識は配列、ループと条件分岐くらいかと思います。)
以下のプログラミングコードを実行すると、最も簡単な異核二原子分子であるHeH$^+$の量子化学計算(RHF)が実施され、HOMOとLUMOの分子軌道係数(coff_matrix)を得ることができます。
プログラミングコードはこちら
「python_RHF.py」などの名前で全コードを保存し、実行してみてください
「No module named ‘numpy’」とエラーが出たときは、numpyをインストールしてください。
import math
import numpy as np
import numpy.linalg as LA
np.set_printoptions(precision=6,floatmode='fixed')
# 原子数、基底関数数、電子数を入力
n_atm = 2 # HeH+の原子数は2つ
n_sto = 3 # STO-3Gは3つのガウス軌道の和で1つの原子軌道を表す
n_basis = 2 # Heの1s軌道、Hの1s軌道で基底関数数は2つとする
n_elec = 2 # HeH+の電子数は2
n_dim = 3 # x,y,zの3次元
dis_HeH = 1.4 # He-H+の結合長(bohr)
# 配列宣言
s_matrix = np.zeros((n_basis,n_basis))
t_matrix = np.zeros((n_basis,n_basis))
v_matrix = np.zeros((n_basis,n_basis))
e_matrix = np.zeros((n_basis,n_basis,n_basis,n_basis))
m_matrix = np.zeros((n_basis,n_basis))
pc=np.zeros((n_dim))
pp=np.zeros((n_dim))
pq=np.zeros((n_dim))
PI = 3.1415926
# 原子核の電荷を入力
CHG = [2,1]
# 原子座標を入力
C = [[0.0000 ,0.0000,0.0000],
[dis_HeH,0.0000,0.0000]]
# STO-3Gの係数を入力
EXX = [[ 6.362421394 ,1.1589229990,0.3136497915 ],
[ 3.425250914 ,0.6239137298,0.1688554040 ]]
CXX = [[ 0.4406303066,0.4261584492,0.1328149745],
[ 0.2769343551,0.2678388516,0.0834736711]]
# STO-3G基底関数の中心座標を入力
CO = [[0.0000 ,0.0000,0.0000],
[dis_HeH,0.0000,0.0000]]
# 重なり行列を計算 s_t[n][m] = (n|m)
for n in range(n_basis):
for m in range(n_basis):
d2 = (CO[n][0]-CO[m][0])**2+(CO[n][1]-CO[m][1])**2+(CO[n][2]-CO[m][2])**2
for i in range(n_sto):
for j in range(n_sto):
zeta = EXX[n][i]+EXX[m][j]
gzai = EXX[n][i]*EXX[m][j]/zeta
sss = math.exp(-gzai*d2)*((PI/zeta)**1.5)
cxij = CXX[n][i]*CXX[m][j]
s_matrix[n,m] += cxij * sss
print("s_matrix")
print(s_matrix)
# 運動エネルギー積分を計算 t_t[n][m] = (n|-1/2 nabla |m)
for n in range(n_basis):
for m in range(n_basis):
d2 = (CO[n][0]-CO[m][0])**2+(CO[n][1]-CO[m][1])**2+(CO[n][2]-CO[m][2])**2
for i in range(n_sto):
for j in range(n_sto):
zeta = EXX[n][i]+EXX[m][j]
gzai = EXX[n][i]*EXX[m][j]/zeta
sss = math.exp(-gzai*d2)*((PI/zeta)**1.5)
tss = gzai*(3-2*gzai*d2)*sss
cxij = CXX[n][i]*CXX[m][j]
t_matrix[n,m] += cxij * tss
print("t_matrix")
print(t_matrix)
# 核引力積分を計算 v_t[n][m] = (n|1/r|m)
for n in range(n_basis):
for m in range(n_basis):
for l in range(n_atm):
for ndim in range(n_dim):
pc[ndim] =C[l][ndim]
for i in range(n_sto):
for j in range(n_sto):
zeta1 = EXX[n][i]+EXX[m][j]
gzai1 = EXX[n][i]*EXX[m][j]/zeta1
cxij = CXX[n][i]*CXX[m][j]
for ndim in range(n_dim):
pp[ndim] =(EXX[n][i]*CO[n][ndim]+EXX[m][j]*CO[m][ndim])/zeta1
d2_ab = (CO[n][0]-CO[m][0])**2+(CO[n][1]-CO[m][1])**2+(CO[n][2]-CO[m][2])**2
d2_pc = (pc[0]-pp[0])**2+(pc[1]-pp[1])**2+(pc[2]-pp[2])**2
if(d2_pc<0.0001):
f0 = 1
else:
f0 = 0.5*math.sqrt(PI/(zeta1*d2_pc))*math.erf(math.sqrt(zeta1*d2_pc))
vss = 2*PI/zeta1*math.exp(-gzai1*d2_ab)*f0
v_matrix[n,m] += cxij * vss * (-CHG[l])
print("v_matrix")
print(v_matrix)
# 2電子積分を計算 e_t[l][m][n][o] = (lm|no)
for l in range(n_basis):
for m in range(n_basis):
for n in range(n_basis):
for o in range(n_basis):
d2_ab = (CO[l][0]-CO[m][0])**2+(CO[l][1]-CO[m][1])**2+(CO[l][2]-CO[m][2])**2
d2_cd = (CO[n][0]-CO[o][0])**2+(CO[n][1]-CO[o][1])**2+(CO[n][2]-CO[o][2])**2
for h in range(n_sto):
for i in range(n_sto):
for j in range(n_sto):
for k in range(n_sto):
zeta1 = EXX[l][h]+EXX[m][i]
gzai1 = EXX[l][h]*EXX[m][i]/zeta1
zeta2 = EXX[n][j]+EXX[o][k]
gzai2 = EXX[n][j]*EXX[o][k]/zeta2
rho = zeta1*zeta2/(zeta1+zeta2)
cxijkl= CXX[l][h]*CXX[m][i]*CXX[n][j]*CXX[o][k]
for ndim in range(n_dim):
pp[ndim] =(EXX[l][h]*CO[l][ndim]+EXX[m][i]*CO[m][ndim])/zeta1
pq[ndim] =(EXX[n][j]*CO[n][ndim]+EXX[o][k]*CO[o][ndim])/zeta2
d2_pq = (pp[0]-pq[0])**2+(pp[1]-pq[1])**2+(pp[2]-pq[2])**2
z_ab = math.sqrt(2)*(PI**1.25)/zeta1*math.exp(-gzai1*d2_ab)
z_cd = math.sqrt(2)*(PI**1.25)/zeta2*math.exp(-gzai2*d2_cd)
if(d2_pq<0.0001):
f0 = 1
else:
f0 = 0.5*math.sqrt(PI/(rho*d2_pq))*math.erf(math.sqrt(rho*d2_pq))
e_ssss = 1.0/math.sqrt(zeta1+zeta2)*z_ab*z_cd*f0
e_matrix[l,m,n,o] += cxijkl * e_ssss
print()
print("START SCF CALCULATION")
# S行列を対角化 U' * S * U = M
w_matrix, u_matrix = LA.eig(s_matrix)
u_d_matrix = u_matrix.T
# 変換行列XとX'をつくる、 X = U * M^(-0.5)
for i in range(n_basis):
m_matrix[i][i] = 1/math.sqrt(w_matrix[i])
x_matrix = u_matrix @ m_matrix
# ハミルトニアン行列hをつくり、左右からX'とXをかけてa行列を作る a = X' * h * X
h_matrix = t_matrix + v_matrix
a_matrix = x_matrix.T @ h_matrix @ x_matrix
# a行列を対角化する固有ベクトルwをつくる w' * a * w = eps 固有値の小さい順番で並び変える
eps_matrix,w_matrix = LA.eig(a_matrix)
index = np.argsort(eps_matrix)[::1]
w_matrix = w_matrix[:,index]
# 変換行列xと固有ベクトルwから初期分子軌道係数coffが得られる coff = x * w
coff_matrix = x_matrix @ w_matrix
# SCF計算開始、電子エネルギーeneが収束するまで行う
ene_save = 0
for n_scf in range(10):
p_matrix = np.zeros((n_basis,n_basis))
for n in range(n_basis):
for m in range(n_basis):
for i in range( int(n_elec/2) ):
p_matrix[n][m] += 2* coff_matrix[n][i] * coff_matrix[m][i]
# フォック行列hをつくる
f_matrix = t_matrix + v_matrix
for n in range(n_basis):
for m in range(n_basis):
for i in range(n_basis):
for j in range(n_basis):
f_matrix[n][m] += p_matrix[i][j]*(e_matrix[n][m][i][j]-0.5*e_matrix[n][i][m][j])
# 電子エネルギーeneを計算し、 ene = 0.5 * p * ( h + f ) 出力する
ene = 0
for n in range(n_basis):
for m in range(n_basis):
ene += 0.5*p_matrix[n][m]*(h_matrix[m][n]+f_matrix[m][n])
print("scf_roop",n_scf+1,"elec_ene",round(ene,7))
# 収束判定を行う
if(abs(ene - ene_save))<10 ** (-7):
print("SCF CONVERSION")
print()
break
# 次回roopの時に収束判定を行えるようにするため、エネルギ値をene_saveとして保存する
ene_save = ene
# フォック行列hに、左右からX'とXをかけてa行列を作る a = X' * h * X
a_matrix = x_matrix.T @ f_matrix @ x_matrix
# a行列を対角化する固有ベクトルwをつくる w' * a * w = eps 固有値の小さい順番で並び変える
eps_matrix,w_matrix = LA.eig(a_matrix)
index = np.argsort(eps_matrix)[::1]
w_matrix = w_matrix[:,index]
# 変換行列xと固有ベクトルwから分子軌道係数coffが得られる coff = x * w
coff_matrix = x_matrix @ w_matrix
# 得られた分子軌道を出力する
print("coff_matrix")
print(coff_matrix)
例えば、coff_matrixの1列目の値$\{-0.872167,-0.202795\}$はHOMO軌道におけるHe 1s, H 1sの軌道係数を表しています。
次のセクションから、このプログラミングコードを11ステップに分けて順番に解説していきます
量子化学計算を行うためのインプット
量子化学計算に限らず、何かしらシミュレーションをする上では、前提となるインプット条件が必要になります。STEP1~STEP3まででpythonでHeH$^+$を量子化学計算するためのインプットを記述します。
今回はHeH$^+$の量子化学計算を行うので、HeとHの電荷($2,1$)と結合長1.4bohr(0.7408Å)を設定します。
# 原子数、基底関数数、電子数を入力
n_atm = 2 # HeH+の原子数は2つ
n_sto = 3 # STO-3Gは3つのガウス軌道の和で1つの原子軌道を表す
n_basis = 2 # Heの1s軌道、Hの1s軌道で基底関数数は2つとする
n_elec = 2 # HeH+の電子数は2
n_dim = 3 # x,y,zの3次元
dis_HeH = 1.4 # He-H+の結合長(bohr)
量子化学計算では行列計算が必ず必要なので、行列を表すための配列を宣言します。RHF計算で必要な行列の次元の多くは基底関数の数と同じになります。
# 配列宣言
s_matrix = np.zeros((n_basis,n_basis))
t_matrix = np.zeros((n_basis,n_basis))
(以下略)
分子軌道に基づく量子化学計算では基底関数の設定が欠かせません。今回はSTO-3Gと呼ばれる基底関数を設定しています。これを使うと、例えばHeの1s軌道は$\chi_{\text{He1s}}=0.44e^{-6.36r^2}$$+0.42e^{-1.15r^2}+0.13e^{-0.31r^2}$と表現できます。($r$は原子からの距離)
# 原子核の電荷を入力
CHG = [2,1]
# 原子座標を入力
C = [[0.0000 ,0.0000,0.0000],
[dis_HeH,0.0000,0.0000]]
# STO-3Gの係数を入力
EXX = [[ 6.362421394 ,1.1589229990,0.3136497915 ],
[ 3.425250914 ,0.6239137298,0.1688554040 ]]
CXX = [[ 0.4406303066,0.4261584492,0.1328149745],
[ 0.2769343551,0.2678388516,0.0834736711]]
# STO-3G基底関数の中心座標を入力
CO = [[0.0000 ,0.0000,0.0000],
[dis_HeH,0.0000,0.0000]]
これまでですでに40行超えてるけど、大丈夫?
大丈夫です。最終的に200行(コメント、空行含む)で作成できます。
必要となる電子積分の計算
SCF計算を行う前に、重なり行列$\boldsymbol{S}$、運動エネルギー積分$\boldsymbol{T}$、核引力積分$\boldsymbol{V}$、2電子積分$\boldsymbol{E}$を計算する必要があります。STEP4~STEP7でこれらの行列要素を計算していきます。
このページで紹介しているプログラミングコードの内、およそ半分程度(90行)がこの行列計算の部分です。
2つのs軌道$\chi_A=\exp(-a_i(r-r_A)^2)$と$\chi_B=\exp(-b_j(r-r_B)^2)$どうしの重なり積分は$\zeta_{ij}=a_i+b_j$、$\xi_{ij}=a_i*b_j/\zeta_{ij}$と2つの軌道の距離$d2_{ij}$を利用すると$sss=\exp(-\xi_{ij}*d2_{ij})*(\frac{\pi}{\zeta_{ij}})^{1.5}$で計算できます。
これを利用して、pythonで重なり行列のコードを作成すると次の通りになります。
# 重なり行列を計算 s_matrix[n][m] = (n|m)
for n in range(n_basis):
for m in range(n_basis):
d2 = (CO[n][0]-CO[m][0])**2+(CO[n][1]-CO[m][1])**2+(CO[n][2]-CO[m][2])**2
for i in range(n_sto):
for j in range(n_sto):
zeta = EXX[n][i]+EXX[m][j]
gzai = EXX[n][i]*EXX[m][j]/zeta
sss = math.exp(-gzai*d2)*((PI/zeta)**1.5)
cxij = CXX[n][i]*CXX[m][j]
s_matrix[n,m] += cxij * sss
print("s_matrix")
print(s_matrix)
2つのs軌道どうしの運動エネルギー積分は$tss=\xi_{ij}(3-2\xi_{ij}*{d2_{ij}})*sss$で計算できます。
これを利用して、pythonで運動量エネルギー積分のコードを作成すると次の通りになります。
# 運動エネルギー積分を計算 t_matrix[n][m] = (n|-1/2 nabla |m)
for n in range(n_basis):
for m in range(n_basis):
d2 = (CO[n][0]-CO[m][0])**2+(CO[n][1]-CO[m][1])**2+(CO[n][2]-CO[m][2])**2
for i in range(n_sto):
for j in range(n_sto):
zeta = EXX[n][i]+EXX[m][j]
gzai = EXX[n][i]*EXX[m][j]/zeta
sss = math.exp(-gzai*d2)*((PI/zeta)**1.5)
tss = gzai*(3-2*gzai*d2)*sss
cxij = CXX[n][i]*CXX[m][j]
t_matrix[n,m] += cxij * tss
print("t_matrix")
print(t_matrix)
位置$(C_x,C_y,C_z)$にある原子核Cによる2つのs軌道の核引力積分は、2つのs軌道の内分点$(P_x,P_y,P_z)$との距離$d2_{pc}$を利用することで、$vss=\frac{2\pi}{\zeta}\exp(-\xi*d2_{ij})*f0$で計算できます。ただし、$f0=\int^1_0\exp(-\zeta_{ij}*d2_{pc}t^2)\text{d}t$$=(0.5*\sqrt{\frac{\pi}{\zeta_{ij}*d2_{pc}}}\text{erf}(\zeta_{ij}*d2_{pc})$です。
これを利用すると核引力積分のコードを作成できます。
コードはこちら
# 核引力積分を計算 v_matrix[n][m] = (n|1/r|m)
for n in range(n_basis):
for m in range(n_basis):
for l in range(n_atm):
for ndim in range(n_dim):
pc[ndim] =C[l][ndim]
for i in range(n_sto):
for j in range(n_sto):
zeta1 = EXX[n][i]+EXX[m][j]
gzai1 = EXX[n][i]*EXX[m][j]/zeta1
cxij = CXX[n][i]*CXX[m][j]
for ndim in range(n_dim):
pp[ndim] =(EXX[n][i]*CO[n][ndim]+EXX[m][j]*CO[m][ndim])/zeta1
d2_ab = (CO[n][0]-CO[m][0])**2+(CO[n][1]-CO[m][1])**2+(CO[n][2]-CO[m][2])**2
d2_pc = (pc[0]-pp[0])**2+(pc[1]-pp[1])**2+(pc[2]-pp[2])**2
if(d2_pc<0.0001):
f0 = 1
else:
f0 = 0.5*math.sqrt(PI/(zeta1*d2_pc))*math.erf(math.sqrt(zeta1*d2_pc))
vss = 2*PI/zeta1*math.exp(-gzai1*d2_ab)*f0
v_matrix[n,m] += cxij * vss * (-CHG[l])
print("v_matrix")
print(v_matrix)
4つのs軌道$\chi_A=\exp(-a_i(r-r_A)^2)$、$\chi_B=\exp(-b_j(r-r_B)^2)$、$\chi_C=\exp(-c_k(r-r_C)^2)$と$\chi_D=\exp(-d_l(r-r_D)^2)$による2電子積分は$\zeta_{1}=a_i+b_j$、$\zeta_{2}=c_k+d_l$、$\xi_{1}=\frac{a_i*b_j}{\zeta_{1}}$、$\xi_{2}=\frac{c_k*d_l}{\zeta_{2}}$、軌道間の距離$d2_{AB}$、$d2_{CD}$と、軌道を内分点PとQを利用すると、$essss=\frac{2\pi^{5/2}}{\zeta_1*\zeta_2}$$\exp(-\xi_1*d2_{ab})\exp(-\xi_2*d2_{cd})*f0$で表せます。ただし、$\rho=\frac{\zeta_1*\zeta_2}{\zeta_1+\zeta_2}$、$f0=\int^1_0\exp(-\rho*d2_{pq}t^2)\text{d}t$です。
コードはこちら
# 2電子積分を計算 e_matrix[l][m][n][o] = (lm|no)
for l in range(n_basis):
for m in range(n_basis):
for n in range(n_basis):
for o in range(n_basis):
d2_ab = (CO[l][0]-CO[m][0])**2+(CO[l][1]-CO[m][1])**2+(CO[l][2]-CO[m][2])**2
d2_cd = (CO[n][0]-CO[o][0])**2+(CO[n][1]-CO[o][1])**2+(CO[n][2]-CO[o][2])**2
for h in range(n_sto):
for i in range(n_sto):
for j in range(n_sto):
for k in range(n_sto):
zeta1 = EXX[l][h]+EXX[m][i]
gzai1 = EXX[l][h]*EXX[m][i]/zeta1
zeta2 = EXX[n][j]+EXX[o][k]
gzai2 = EXX[n][j]*EXX[o][k]/zeta2
rho = zeta1*zeta2/(zeta1+zeta2)
cxijkl= CXX[l][h]*CXX[m][i]*CXX[n][j]*CXX[o][k]
for ndim in range(n_dim):
pp[ndim] =(EXX[l][h]*CO[l][ndim]+EXX[m][i]*CO[m][ndim])/zeta1
pq[ndim] =(EXX[n][j]*CO[n][ndim]+EXX[o][k]*CO[o][ndim])/zeta2
d2_pq = (pp[0]-pq[0])**2+(pp[1]-pq[1])**2+(pp[2]-pq[2])**2
z_ab = math.sqrt(2)*(PI**1.25)/zeta1*math.exp(-gzai1*d2_ab)
z_cd = math.sqrt(2)*(PI**1.25)/zeta2*math.exp(-gzai2*d2_cd)
if(d2_pq<0.0001):
f0 = 1
else:
f0 = 0.5*math.sqrt(PI/(rho*d2_pq))*math.erf(math.sqrt(rho*d2_pq))
e_ssss = 1.0/math.sqrt(zeta1+zeta2)*z_ab*z_cd*f0
e_matrix[l,m,n,o] += cxijkl * e_ssss
初期分子軌道の準備
今の段階で$\boldsymbol{S}$、$\boldsymbol{V}$、$\boldsymbol{T}$など必要な行列計算が完了しており、$\boldsymbol{FC}=\boldsymbol{SC}\epsilon$を解けば分子軌道$\boldsymbol{C}$を得ることができますが、$\boldsymbol{S}$があることによって行列の固有値方程式になっていません。
そのため、STEP8で$\boldsymbol{FC}=\boldsymbol{SC}\epsilon$を固有値方程式$\boldsymbol{AC’}=\epsilon\boldsymbol{C’}$の形に変換するための変換行列$\boldsymbol{X}$を重なり行列$\boldsymbol{S}$から作成し、これを利用してSTEP9で初期分子軌道を生成させます。
変換行列$\boldsymbol{X}$は重なり行列$\boldsymbol{S}$を$\boldsymbol{M}$へ対角化する行列$\boldsymbol{U}$を使って、$\boldsymbol{X}=\boldsymbol{U}*\boldsymbol{M}^{-\frac{1}{2}}$で作成します。
# S行列を対角化 U' * S * U = M
w_matrix, u_matrix = LA.eig(s_matrix)
# 変換行列XとX'をつくる、 X = U * M^(-0.5)
for i in range(n_basis):
m_matrix[i][i] = 1/math.sqrt(w_matrix[i])
x_matrix = u_matrix @ m_matrix
ここまででほぼ下準備は完了です。
変換行列$\boldsymbol{X}$を使って、ハミルトニアン行列$\boldsymbol{H}=\boldsymbol{T}+\boldsymbol{V}$をA行列($\boldsymbol{A}=\boldsymbol{X^\dagger}*\boldsymbol{H}*\boldsymbol{X}$)に変換し、A行列の固有ベクトル$\boldsymbol{W}$を使うことで分子軌道を$\boldsymbol{C}=\boldsymbol{X}*\boldsymbol{W}$で得ることができます。
# ハミルトニアン行列hをつくり、左右からX'とXをかけてa行列を作る a = X' * h * X
h_matrix = t_matrix + v_matrix
a_matrix = x_matrix.T @ h_matrix @ x_matrix
# a行列を対角化する固有ベクトルwをつくる w' * a * w = eps 固有値の小さい順番で並び変える
eps_matrix,w_matrix = LA.eig(a_matrix)
index = np.argsort(eps_matrix)[::1]
w_matrix = w_matrix[:,index]
# 変換行列xと固有ベクトルwから分子軌道係数coffが得られる coff = x * w
coff_matrix = x_matrix @ w_matrix
SCF計算開始
下準備に長らく時間がかかりましたが、初期分子軌道を得たので、STEP10~STEP11でSCF(繰り返し計算)を行います。
SCF部分は残りの50行ほどです。
繰り返し計算ではすでに得られている分子軌道$\boldsymbol{C}$をもとに密度行列$\boldsymbol{P}$を得て、電子間反発が加味されたフォック行列$\boldsymbol{F}$を計算します。
この密度行列$\boldsymbol{P}$とフォック行列$\boldsymbol{F}$から電子状態のエネルギー$E$を計算します。
# SCF計算開始、電子エネルギーeneが収束するまで行う
ene_save = 0
for n_scf in range(10):
p_matrix = np.zeros((n_basis,n_basis))
for n in range(n_basis):
for m in range(n_basis):
for i in range( int(n_elec/2) ):
p_matrix[n][m] += 2* coff_matrix[n][i] * coff_matrix[m][i]
# フォック行列hをつくる
f_matrix = t_matrix + v_matrix
for n in range(n_basis):
for m in range(n_basis):
for i in range(n_basis):
for j in range(n_basis):
f_matrix[n][m] += p_matrix[i][j]*(e_matrix[n][m][i][j]-0.5*e_matrix[n][i][m][j])
# 電子エネルギーeneを計算し、 ene = 0.5 * p * ( h + f ) 出力する
ene = 0
for n in range(n_basis):
for m in range(n_basis):
ene += 0.5*p_matrix[n][m]*(h_matrix[m][n]+f_matrix[m][n])
print("scf_roop",n_scf+1,"elec_ene",round(ene,7))
SCF計算ではいつ繰り返しを終了するかを判定しなければなりません。
今回は電子状態のエネルギー$E$が繰り返し計算前後で値が一定値以下になれば計算を終了することにして、得られた分子軌道を出力するようにしています。
電子状態のエネルギーが一定になっていなければフォック行列$\boldsymbol{F}$について、$\boldsymbol{FC}=\boldsymbol{SC}\epsilon$を解いて、新しい分子軌道$\boldsymbol{C}$を得て、計算ループを継続します。
# 収束判定を行う
if(abs(ene - ene_save))<10 ** (-7):
print("SCF CONVERSION")
print()
break
# 次回roopの時に収束判定を行えるようにするため、エネルギ値をene_saveとして保存する
ene_save = ene
# フォック行列hに、左右からX'とXをかけてa行列を作る a = X' * h * X
a_matrix = x_matrix.T @ f_matrix @ x_matrix
# a行列を対角化する固有ベクトルwをつくる w' * a * w = eps 固有値の小さい順番で並び変える
eps_matrix,w_matrix = LA.eig(a_matrix)
index = np.argsort(eps_matrix)[::1]
w_matrix = w_matrix[:,index]
# 変換行列xと固有ベクトルwから分子軌道係数coffが得られる coff = x * w
coff_matrix = x_matrix @ w_matrix
# 得られた分子軌道を出力する
print("coff_matrix")
print(coff_matrix)
まとめ
このページではHeH$^+$(水素化ヘリウムイオン)の量子化学計算を行うpythonコードを11ステップに分けて解説しました。
実際の量子化学パッケージ(GAMESSなど)でのプログラミングコードはもっと長く、変数名も抽象的になりますが、RHFやDFTなどでは大きくは量子化学計算の流れが変わることはありません。
プログラミングコードのHe-H結合長を変えて計算を行うと、安定な結合長の最適化も行うことができますので、皆さんも一度チャレンジしてみてください。