LoginSignup
0
0

More than 1 year has passed since last update.

分子軌道法 ー 計算代数幾何学からのアプローチ

Last updated at Posted at 2022-09-14

# 分子軌道法 ー 計算代数幾何学からのアプローチ

背景

文献[KIKUCHI2013]においてAkihito Kikuchiは、独特の分子軌道法を展開している。

この方法の特徴は次の通り。

  1. 一電子・二電子積分を原子間距離の解析関数を以て表現する。
  2. 波動関数(LCAO係数)も同様に未定変数とする。
  3. エネルギー汎関数(H)、波動関数の規格直交条件(S)は、原子間距離およびLCAO係数の解析関数を以て表現する
  4. 波動関数の規格直交条件(S)のもとでエネルギー汎関数(H)を最小化する。ラグランジュ未定定数(e)が一電子軌道エネルギーとなる。 目的関数は(H)-(e)(S)である。
  5. 目的関数を、原子間距離変数Rの特定の値の周囲でテイラー展開する。よって目的関数は他変数の多項式である。
  6. 目的関数Fをその引数(LCAO係数、核間距離R)で偏微分することで、その根が目的関数の極小を与える連立多項式を作る。
  7. この連立多項式を、「多項式環のイデアル」とみなし、グレブナ基底・準素イデアル分解と呼ばれる計算代数幾何学の手法を適用し、数値的に「解きやすく」、また、電子構造がおのずと開示されるような、複数の連立多項式に分解する。分解後の小さい多項式系を数値的に解く。

[KIKUCHI2013]では一電子・二電子積分はSTO(SLater-Type Orbital)を使って計算している。しかし、STOを用いて二電子積分の解析式をどう計算するかは、多原子系では今なお解決できていない問題である。

そこで、この記事では、STOの代わりに、量子化学計算の標準的な方法であるGTO(Gaussian-Type Orbital)を使って[KIKUCHI2013]の方針を追うことを目的にする。

[KIKUCHI2013] @article{KIKUCHI2013, author="Kikuchi,Akihito", title="An approach to first principles electronic structure computation by symbolic-numeric computation", journal="QScience Connect",
volume="2013:14",year=2013, note="http://dx.doi.org/10.5339/connect.2013.14"}

モデル

HeH+分子を考える。Szabo and Ostlund [szabo2012modern]では、この分子の計算を目的とした古風なFortranプログラムを示している。

本記事の研究では、このFortranプログラムを出発点とする。まずこのプログラムをpythonの数値計算プログラムに書き換え、ついで、pythonの現代的な記号処理ライブラリ(sympy)を用いて、関連するすべての電子積分の解析式・テーラー展開を実行し、多項式型のエネルギー汎関数および波動関数の正規直交条件を構成した。ついで、記号処理による偏微分によって、解くべき連立多項式を作成した。この連立多項式を、計算代数システム「Singular」で処理することで数値解を求める。

[szabo2012modern]@book{szabo2012modern,
title={Modern quantum chemistry: introduction to advanced electronic structure theory},
author={Szabo, Attila and Ostlund, Neil S},
year={2012},
publisher={Courier Corporation}
}

#!/usr/bin/env python

import sympy as sympy
from sympy import symbols, Function,expand,core,sqrt
import numpy as np

from sympy import symbols, Function,expand,sqrt
import numpy as np
import copy

S12,T11,T12,T22,V11A,V12A,V22A,V11B,V12B,V22B,V1111,V2111,V2121,V2211,V2221,V2222=[0 for _ in range(16)]
S=[[0 for _ in range(2)] for _ in range(2)]
X=[[0 for _ in range(2)] for _  in range(2)]
NM=[[0 for _ in range(2)] for _  in range(2)]
XT=[[0 for _ in range(2)] for _ in  range(2)]
H=[[0 for _ in range(2)] for _ in  range(2)]
F=[[0 for _ in range(2)] for _ in  range(2)]
G=[[0 for _ in range(2)] for _ in  range(2)]
C=[[0 for _ in range(2)] for _ in  range(2)]
FPRIME=[[0 for _ in range(2)] for _  in range(2)]
CPRIME=[[0 for _ in range(2)] for _  in range(2)]
P=[[0 for _ in range(2)] for _  in range(2)]
ODLPP=[[0 for _ in range(2)] for _  in range(2)]
E=[[0 for _ in range(2)] for _  in range(2)]
TT=[[[[0 for _ in range(2)] for _  in range(2)] for _ in range(2)] for _ in range(2)]


IOP=2
#
# CHOOSE STO-NG
#
N=3
R=1.4632
#
# Gaussian exponents:
# He:ZETA1; H;ZETA2
#
ZETA1=2.0925
ZETA2=1.24
#
#
#
ZA=2.0
ZB=1.0
#
# ATOMIC DISTANCE 
#
R=symbols("R",positive=True)
# WAVEFUNCTION (x,y)
x, y = symbols("x y")


def F0(ARG):
#
# F0 FUNCTION
#
    PI = np.pi
    if  type(ARG)==float and ARG < 1.0e-6:
        return 1 -ARG/3.
    if  type(ARG)==sympy.core.numbers.Zero and ARG < 1.0e-6:
        return 1 -ARG/3.
    else:
        #print("F0:ARG",ARG)
        return sqrt(PI/ARG)*sympy.erf(sqrt(ARG))/2
    
def getS(A,B,RAB2):
#
# Calculates the overlap matrix
#   
    PI=np.pi
    #print(A,B,RAB2)
    return (PI/(A+B))**1.5*sympy.exp(-A*B*RAB2/(A+B))

def getT(A,B,RAB2):
#
# Calculates the kinetic energy.
#
    PI=np.pi
    return A*B/(A+B)*(3.0-2.0*A*B*RAB2/(A+B))*(PI/(A+B))**1.5*sympy.exp(-A*B*RAB2/(A+B))

def V(A,B,RAB2,RCP2,ZC):
#
#  CALCULATES UN-NORMALIZED NUCLEAR ATTRACTION INTEGRALS
#
    PI=np.pi
    V=2.0*PI/(A+B)*F0((A+B)*RCP2)*sympy.exp(-A*B*RAB2/(A+B))
    return -V*ZC

def TWOE(A,B,C,D,RAB2,RCD2,RPQ2):
#
# CALCULATES TWO-ELECTRON INTEGRALS FOR UN-NORMALIZED PRIMITIVES
# A,B,C,D ARE THE EXPONENTS ALPHA, BETA, ETC.
# RAB2 EQUALS SQUARED DISTANCE BETWEEN CENTER A AND CENTER B, ETC.
#
    PI=np.pi
    return 2.0*(PI**2.5)/((A+B)*(C+D)*np.sqrt(A+B+C+D))*F0((A+B)*(C+D)*RPQ2/(A+B+C+D))*sympy.exp(-A*B*RAB2/(A+B)-C*D*RCD2/(C+D))

def INTGRL_SYMBOL(IOP,N,R,ZETA1,ZETA2,ZA,ZB):
#
#  CALCULATES ALL THE BASIC INTEGRALS NEEDED FOR SCF CALCULATION
#
    global S12,T11,T12,T22,V11A,V12A,V22A,V11B,V12B,V22B,V1111,V2111,V2121,V2211,V2221,V2222
    COEF=[[1.0,0.0,0.0],[0.678914,0.430129,0.0],[0.444635,0.535328,0.154329]]
    EXPON=[[0.270950,0.0,0.0],[0.151623,0.851819,0.0],[0.109818,0.405771,2.22766]]
    R2=R*R
    N=3
    A1=[0 for I in range(N)]
    D1=[0 for I in range(N)]
    A2=[0 for I in range(N)]
    D2=[0 for I in range(N)]
    PI=np.pi

    for i in range(N):
        #print(i,EXPON[N-1][i],COEF[N-1][i],ZETA1,ZETA2)
        A1[i]=EXPON[N-1][i]*ZETA1**2
        D1[i]=COEF[N-1][i]*((2.0*A1[i]/PI)**0.75)
        A2[i]=EXPON[N-1][i]*(ZETA2**2)
        D2[i]=COEF[N-1][i]*((2.0*A2[i]/PI)**0.75)

    #print("A1",A1,"D1",D1,"A2",A2,"D2",D2)
        
    S12=0.0
    T11=0.0
    T12=0.0
    T22=0.0
    V11A=0.0
    V12A=0.0
    V22A=0.0
    V11B=0.0
    V12B=0.0
    V22B=0.0
    V1111=0.0
    V2111=0.0
    V2121=0.0
    V2211=0.0
    V2221=0.0
    V2222=0.0
    
    for I in range(N):
        for J in range(N):
            RAP=A2[J]*R/(A1[I]+A2[J])
            RAP2=RAP**2
            RBP2=(R-RAP)**2
            S12+=getS(A1[I],A2[J],R2)*D1[I]*D2[J]
            T11=T11+getT(A1[I],A1[J],0.0)*D1[I]*D1[J]
            #print(I,J,getT(A1[I],A1[J],0.0),D1[I],D1[J],T11)
            T12=T12+getT(A1[I],A2[J],R2)*D1[I]*D2[J]
            T22=T22+getT(A2[I],A2[J],0.0)*D2[I]*D2[J]
            V11A=V11A+V(A1[I],A1[J],0.0,0.0,ZA)*D1[I]*D1[J]
            V12A=V12A+V(A1[I],A2[J],R2,RAP2,ZA)*D1[I]*D2[J]
            V22A=V22A+V(A2[I],A2[J],0.0,R2,ZA)*D2[I]*D2[J]
            V11B=V11B+V(A1[I],A1[J],0.0,R2,ZB)*D1[I]*D1[J]
            V12B=V12B+V(A1[I],A2[J],R2,RBP2,ZB)*D1[I]*D2[J]
            V22B=V22B+V(A2[I],A2[J],0.0,0.0,ZB)*D2[I]*D2[J]
        
    for I in range(N):
        for J in range(N):
            for K in range(N):
                for L in range(N):
                    RAP=A2[I]*R/(A2[I]+A1[J])
                    RBP=R-RAP
                    RAQ=A2[K]*R/(A2[K]+A1[L])
                    RBQ=R-RAQ
                    RPQ=RAP-RAQ
                    RAP2=RAP*RAP
                    RBP2=RBP*RBP
                    RAQ2=RAQ*RAQ
                    RBQ2=RBQ*RBQ
                    RPQ2=RPQ*RPQ
                    V1111=V1111+TWOE(A1[I],A1[J],A1[K],A1[L],0.0,0.0,0.0)*D1[I]*D1[J]*D1[K]*D1[L]
                    V2111=V2111+TWOE(A2[I],A1[J],A1[K],A1[L],R2,0.0,RAP2)*D2[I]*D1[J]*D1[K]*D1[L]
                    V2121=V2121+TWOE(A2[I],A1[J],A2[K],A1[L],R2,R2,RPQ2)*D2[I]*D1[J]*D2[K]*D1[L]
                    V2211=V2211+TWOE(A2[I],A2[J],A1[K],A1[L],0.0,0.0,R2)*D2[I]*D2[J]*D1[K]*D1[L]
                    V2221=V2221+TWOE(A2[I],A2[J],A2[K],A1[L],0.0,R2,RBQ2)*D2[I]*D2[J]*D2[K]*D1[L]
                    V2222=V2222+TWOE(A2[I],A2[J],A2[K],A2[L],0.0,0.0,0.0)*D2[I]*D2[J]*D2[K]*D2[L]

    

def COLLECT(IOP,N,R,ZETA1,ZETA2,ZA,ZB):
#
# THIS TAKES THE BASIC INTEGRALS FROM COMMON AND ASSEMBLES THE
# RELEVENT MATRICES, THAT IS S,H,X,XT, AND TWO-ELECTRON INTEGRALS
#
    global H,S,X,TT,XT
    H[0][0]=T11+V11A+V11B
    H[0][1]=T12+V12A+V12B
    H[1][0]=H[0][1]
    H[1][1]=T22+V22A+V22B

    S[0][0]=1.
    S[0][1]=S12
    S[1][0]=S[0][1]
    S[1][1]=1.


    TT[0][0][0][0]=V1111
    TT[1][0][0][0]=V2111
    TT[0][1][0][0]=V2111
    TT[0][0][1][0]=V2111
    TT[0][0][0][1]=V2111
    TT[1][0][1][0]=V2121
    TT[0][1][1][0]=V2121
    TT[1][0][0][1]=V2121
    TT[0][1][0][1]=V2121
    TT[1][1][0][0]=V2211
    TT[0][0][1][1]=V2211
    TT[1][1][1][0]=V2221
    TT[1][1][0][1]=V2221
    TT[1][0][1][1]=V2221
    TT[0][1][1][1]=V2221
    TT[1][1][1][1]=V2222


    return

def MATOUT(A,IM,IN,M,N,LABEL):
    print(LABEL)
    print(A)
    return

def MULT(A,B,C,IM,M):
    for i in range(2):
        for j in range(2):
            C[i][j]=0
            for k in range(2):
                C[i][j]+=A[i][k]*B[k][j]
    return 

def FORMG():
#
# Calculates G matrix from density matrix and two-electron integrals
#
    global G,P,TT
    for I in range(2):
        for J in range(2):
            G[I][J]=0
            for K in range(2):
                for L in range(2):
                    #print(I,J,K,L,P[K][L],TT[I][J][K][L],TT[I][L][J][K],G[I][J])
                    G[I][J]+=P[K][L]*(TT[I][J][K][L]-0.5*TT[I][L][J][K])
    #print("G",G)
    return 



def SCF_SYMBOL(IOP,N,R,ZETA1,ZETA2,ZA,ZB):
#
# PREPARES THE ANALYTIC FORMULA OF THE TOTAL ENERGY.
#
    global G,C,FPRIME,CPRIME

    PI=np.pi
    CRIT=1.0e-4
    MAXIT=25
    ITER=0
    for I in range(2):
        for J in range(2):
            P[I][J]=0.
    P[0][0]=x*x*2
    P[0][1]=x*y*2
    P[1][0]=x*y*2
    P[1][1]=y*y*2
    
    MAXIT=1
    for ITER in range(MAXIT):
        
        FORMG()
        for i in range(2):
            for j in range(2):
                F[i][j]=H[i][j]+G[i][j]
        EN=0
        for i in range(2):
            for j in range(2):
                EN+=0.5*P[i][j]*(H[i][j]+F[i][j])

    ENT=EN+ZA*ZB/R

    return EN,ENT

#
# COMPUTES THE ELECTRON ENERGY (EN) and THE TOTAL ENERGY(ENT)
#
INTGRL_SYMBOL(IOP,N,R,ZETA1,ZETA2,ZA,ZB)
COLLECT(IOP,N,R,ZETA1,ZETA2,ZA,ZB)
EN,ENT=SCF_SYMBOL(IOP,N,R,ZETA1,ZETA2,ZA,ZB)

from sympy import Symbol, cos, series

#
print("CHECK THE TOTAL ENERGY: BY THIS SUBSTITUTION WE'LL GET THE VALUE CLOSE TO -2.86...")
#
print(ENT.subs([(x,-0.80191751),(y,-0.33680049),(R,1.463)]))

# TAYLOR EXPANSION OF THE TOTAL ENERGY AROUNR R0=1.5
ENTS=series(expand(ENT),R,1.5)
ENTS2=expand(ENTS.removeO())
print("CHECK THE TOTAL ENERGY: BY THIS SUBSTITUTION WE'LL GET THE VALUE CLOSE TO -2.86...")
print(sympy.N(ENTS2.subs([(x,-0.80191751),(y,-0.33680049),(R,1.463)])))

#
# THE NORMALIZARION CONDITION OF THE WAVE FUNCTION
#
W=[0,0]
W[0]=S[0][0]*x+S[0][1]*y
W[1]=S[1][0]*x+S[1][1]*y
NORMC=expand(x*W[0]+y*W[1])

#
# THE NORMALIZATION CONDITION AFTER TAYLOR EXPANSION
#
NORMCS=expand(series(NORMC,R,1.5).removeO())
print("CHECK THE NOTMALIZATION: BY THIS SUBSTITUTION WE'LL GET THE VALUE CLOSE TO 1...")
print(NORMCS.subs([(x,-0.80191751),(y,-0.33680049),(R,1.463)]))

#
# TURN THE POLYNOMIAL ENERGY FUNCTIONAL INTO A SYMPY-POLYNOMIAL
#
ENTS3=sympy.poly(sympy.N(ENTS2))

#
# GET THE OBJECTIVE FUNTION WITH INTEGER COEFFICIENTS
#

def getINT(x,N):
#
#  ROWND DOWN x*N AFTER THE DECIMAL POINT and get AN INTEGER M
#  THEN X is approximated by M/N. 
#
    return (int(np.floor(x*N)))

AT=ENTS3.terms()
getF=0
for tm in AT:
    p0,p1,p2=tm[0]
    cf=tm[1]
    #print(p0,p1,p2,cf)
    getF+=x**p0*y**p1*R**p2*getINT(cf,10000)

AT=sympy.poly(NORMCS-1).terms()
getNS=0
for tm in AT:
    p0,p1,p2=tm[0]
    cf=tm[1]
    #print(p0,p1,p2,cf)
    getNS+=x**p0*y**p1*R**p2*getINT(cf,10000)

print((NORMCS-1).subs([(x,-0.80191751),(y,-0.33680049),(R,1.463)]))
print(ENTS3.subs([(x,-0.80191751),(y,-0.33680049),(R,1.463)]))
print(getNS.subs([(x,-0.80191751),(y,-0.33680049),(R,1.463)]))

e=symbols("e")

#
# GET THE OBJECTIVE FUNCTION
#
OF=getF-2*e*getNS

#
# PREPARES A Singular SCRIPT. 
#
#  If you are a user of Singular, you simply do as follows.
#
#  >> Singular < SCRIPT.txt
#
stringQ='LIB "solve.lib";option(redSB);\n'
stringQ+='ring r=0,(x,y,e,R),dp;\n'+'poly OBJ='+str(OF)+';\n'
stringQ+='list diffs;\n'
stringQ+='for(int i=1;i<=nvars(r); i=i+1){diffs=insert(diffs,diff(OBJ,var(i)));}\n'
stringQ+='poly fR=100*R-146;\n'
stringQ+='ideal I=fR;\n'
stringQ+='for(int i=1;i<=nvars(r)-1; i=i+1){I=I+diff(OBJ,var(i));}\n'
stringQ+='ideal SI=std(I);\n'
stringQ+='ring s=0,(x,y,e,R),lp;\n'
stringQ+='setring s;\n'
stringQ+='ideal j=fglm(r,SI);\n'
stringQ+='def R=triang_solve(j,10);\n'
stringQ+='setring R;rlist;'

text_file = open("SCRIPT.txt", "w")
 
#write string to file
on = text_file.write(stringQ)
 
#close file
text_file.close()

stringQ

#
# GET THE DIFFERENTIALS OF THE OBJECTIVE FUNCTION.
#
DS=[]
for t in [x,y,e,R]:
    DS.append(sympy.diff(OF,t))
index=0
for i in DS:
    print("poly f"+str(index)+"=",i,";")
    index+=1

今回は、簡単なコメントを入れたプログラムを示す。このプログラムは、実行終了直前にサブプログラムを生成し、これは「Singular」のスクリプトになっている。

このプログラムではHeH+のRHF計算を行っている。

数値計算の結果は、概ねSabo-Ostlundの結果を再現するものである。ただし、Sabo-Ostlundでは得ることのない、励起状態を基底状態ともに、一つの計算で取得することができる。

以下に示すのは、Singularのスクリプトである。
1.目的関数をpoly OBJに定義
2. (x,y)はHe,Hの1s軌道に対するLCAOの係数
3. eはエネルギー固有値
4. Rは核間距離
5. d(OBJ)/dx, d(OBJ)/dy, d(OBJ)/de, d(OBJ)/dRを計算。
6. イデアルIを生成元(d(OBJ)/dx, d(OBJ)/dy, d(OBJ)/de, 100*R-146)で定義。これは
d(OBJ)/dR=0の代わりに、Rを1.46に固定するという処理。
7. Iのグレブナー基底を作る。
8. グレブナー基底を用いて多項式連立方程式を数値的に解く。この際、準素イデアル分解を利用している。

LIB "solve.lib";option(redSB);
ring r=0,(x,y,e,R),dp;
poly OBJ=-782*R**4*x**3*y - 1034*R**4*x**2*y**2 - 1036*R**4*x**2 - 1037*R**4*x*y**3 + 1799*R**4*x*y + 187*R**4*y**2 + 2633*R**4 + 4977*R**3*x**3*y + 3982*R**3*x**2*y**2 + 8767*R**3*x**2 + 6383*R**3*x*y**3 - 10588*R**3*x*y - 303*R**3*y**2 - 19754*R**3 - 6472*R**2*x**3*y + 5751*R**2*x**2*y**2 - 30430*R**2*x**2 - 9875*R**2*x*y**3 + 6621*R**2*x*y - 5877*R**2*y**2 + 59259*R**2 - 19598*R*x**3*y - 47149*R*x**2*y**2 + 54517*R*x**2 - 12230*R*x*y**3 + 75627*R*x*y + 29906*R*y**2 - 88889*R - 2*e*(-419*R**4*x*y + 3017*R**3*x*y - 6319*R**2*x*y - 2791*R*x*y + 10000*x**2 + 19093*x*y + 10000*y**2 - 10000) + 13071*x**4 + 48009*x**3*y + 68137*x**2*y**2 - 90397*x**2 + 36259*x*y**3 - 153801*x*y + 7746*y**4 - 65726*y**2 + 66666;
list diffs;
for(int i=1;i<=nvars(r); i=i+1){diffs=insert(diffs,diff(OBJ,var(i)));}
poly fR=100*R-146;
ideal I=fR;
for(int i=1;i<=nvars(r)-1; i=i+1){I=I+diff(OBJ,var(i));}
ideal SI=std(I);
ring s=0,(x,y,e,R),lp;
setring s;
ideal j=fglm(r,SI);
def R=triang_solve(j,10);
setring R;rlist;

いくつかの解を得る。複素数の解は除外し、実数解は以下の4つ。
[1][2]が基底状態、[3][4]が励起状態である。

[1]:
   [1]:
x:-0.8014629666
   [2]:
y:-0.3370604896
   [3]:
e:-1.599681024
   [4]:
R:1.46
[2]:
   [1]:
x:0.8014629666
   [2]:
y:0.3370604896
   [3]:
e:-1.599681024
   [4]:
R:1.46
[3]:
   [1]:
x:0.6039377034
   [2]:
y:-1.11522499
   [3]:
e:-0.5378807636
   [4]:
R:1.46
[4]:
   [1]:
x:-0.6039377034
   [2]:
y:1.11522499
   [3]:
e:-0.5378807636
   [4]:
R:1.46

結論

この記事では 第一原理量子化学計算(分子軌道計算)を計算代数幾何学の手法に基づいて実行する一例を示した。従来の逐次近似(自己無同着)計算法を用いる必要は全くない。基本的な道具は、解析式の多項式近似と、グレブナー基底の生成、これを用いた数値計算である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0