Current Features:
Can clone from GitHub: https://github.com/LT12/LTPsi
In [ ]:
def calcNuclRepulsion(self):
self.NuclRepEnergy = 0
Z = getAtomicCharge
if self.numAtom > 1:
for i in xrange(self.numAtom):
for j in xrange(i):
R_ij = self.cartMatrix[i,:] - self.cartMatrix[j,:]
nR_ij = np.sqrt(np.dot(R_ij,R_ij))
Z_iZ_j = Z(self.atomType[i]) * Z(self.atomType[j])
self.NuclRepEnergy += Z_iZ_j/nR_ij
In [ ]:
Ints = MolecularIntegrals(self.orbList, self.atomType, self.cartMatrix, 1)
self.CoreHam, self.OverlapMatrix, self.ERT = Ints.CoreHam, Ints.OLMatrix, Ints.ERT
In [ ]:
def symOrthogMatrix(self):
eig, eigVM = eigh(self.OverlapMatrix,turbo = True)
eigT = np.diag(np.reciprocal(np.sqrt(eig)))
self.symS = np.dot(eigVM, np.dot(eigT, eigVM.T))
In [ ]:
def transFock(self, Fock):
self.transFockM = np.dot(self.symS.T,np.dot(Fock,self.symS))
self.eig,eigVM = eigh(self.transFockM,turbo=True)
self.AOcoefs = np.dot(self.symS,eigVM)
In [ ]:
def compDensityMatrix(self):
self.densityM = np.einsum('im ,jm ->ij', self.AOcoefs[:,:self.nOcc],
self.AOcoefs[:,:self.nOcc])
-In order to utilize Numpy's einsum function, sum will be distributed:
$$E_{e^{-}} = \sum_{i}^{N}\sum_{j}^{N}D_{ij}H^{\text{core}}_{ij}+\sum_{i}^{N}\sum_{j}^{N}D_{ij}F_{ij}$$
In [ ]:
def compElectronicEnergy(self, Fock):
self.ElectrEnergy = np.einsum('ij,ij', self.densityM,
self.CoreHam + Fock)
In [ ]:
def compFock(self):
self.FockM = self.CoreHam +\
2 * np.einsum('kl,ijkl->ij',self.densityM,self.ERT)\
- np.einsum('kl,ikjl->ij',self.densityM,self.ERT)
#Storing Fock Matricies for DIIS
self._fockList[self._FockCount % 6] = np.copy(self.FockM)
self._FockCount += 1
if self.itercount>2:
self._DIIS_switch = True
self._Fock_switch = True
In [ ]:
def SCF(self):
self.symOrthogMatrix()
#initial density guess
if self.densityM is None:
self.transFock(self.CoreHam)
self.compDensityMatrix()
self.compElectronicEnergy(self.CoreHam)
self.itercount, prevElecEnergy, self._DIIS_switch, self._FockCount = 0, 0, False, 0
self._fockList,self._errorList = np.ndarray(6,dtype=np.ndarray),np.ndarray(6,dtype=np.ndarray)
self._Fock_switch = True
while True:
if abs(self.ElectrEnergy-prevElecEnergy) < 1E-12: break
prevElecEnergy = self.ElectrEnergy
if self.itercount == 2:
self._DIIS_switch = True
self._Fock_switch = False
if self._DIIS_switch == True:
self.DIIS()
else:
self.compFock()
if self._Fock_switch == True:
self.compErrorMatrix()
self.transFock(self.FockM)
self.compDensityMatrix()
self.compElectronicEnergy(self.FockM)
self.itercount += 1
self.SCFEnergy = self.ElectrEnergy + self.NuclRepEnergy
self.TotalEnergy = self.SCFEnergy
First, we need to transform two-electron integrals from AO basis into MO basis
This can be done naively ($O(N^{8})$) as follows:
$$ (pq|rs) = \sum_{i}\sum_{j}\sum_{k}\sum_{l}C_{i}^{p}C_{j}^{q}C_{k}^{r}C_{l}^{s}(ij|kl)$$
A smarter way ($O(N^{5})$) would be to use partial quarter transformations:
$$ (pq|rs) = \sum_{i}C_{i}^{p}\left[\sum_{j}C_{j}^{q}\left[\sum_{k}C_{k}^{r}\left[\sum_{l}C_{l}^{s}(ij|kl)\right]\right]\right]$$
In [ ]:
#Convert Electron Repulsion Tensor from AO basis to MO basis
MOERT = np.einsum('ijkl, ls -> ijks',MOERT,C)
MOERT = np.einsum('ijks, kr -> ijrs',MOERT,C)
MOERT = np.einsum('ijrs, jq -> iqrs',MOERT,C)
MOERT = np.einsum('iqrs, ip -> pqrs',MOERT,C)
In [ ]:
for i,j,a,b in itertools.product(xrange(nOcc),xrange(nOcc),
xrange(nOcc,N),xrange(nOcc,N)):
self.MP2Energy += MOERT[i,a,j,b] *\
(2 * MOERT[i,a,j,b] - MOERT[i,b,j,a])/\
(OrbEnergy[i] + OrbEnergy[j]
- OrbEnergy[a] - OrbEnergy[b])
In [ ]:
def MP2(self):
#define N-number of orbitals, nOcc-number of occupied orbitals,
#MOERT-ERT in MO basis, Orbenergy- energy of each orbital in
#ordered manner, C-Matrix of AO coefficients
N,nOcc,MOERT,OrbEnergy,C = len(self.orbList),self.nOcc,\
np.copy(self.ERT),self.eig,\
self.AOcoefs
#Convert Electron Repulsion Tensor from AO basis to MO basis
MOERT = np.einsum('ijkl, ls -> ijks',MOERT,C)
MOERT = np.einsum('ijks, kr -> ijrs',MOERT,C)
MOERT = np.einsum('ijrs, jq -> iqrs',MOERT,C)
MOERT = np.einsum('iqrs, ip -> pqrs',MOERT,C)
self.MP2Energy = 0
for i,j,a,b in itertools.product(xrange(nOcc),xrange(nOcc),
xrange(nOcc,N),xrange(nOcc,N)):
self.MP2Energy += MOERT[i,a,j,b] *\
(2 * MOERT[i,a,j,b] - MOERT[i,b,j,a])/\
(OrbEnergy[i] + OrbEnergy[j]
- OrbEnergy[a] - OrbEnergy[b])
self.TotalEnergy += self.MP2Energy