Вычисление атомных орбиталей водорода

Уравнение Шрёдингера для атомов, содержащих более одного электрона, не может быть решено в аналитическом виде. В связи с этим рассматривают приближённые методы, наиболее существенным из которых является метод самосогласованного поля (Self consistent field(SCF) method). Идея метода заключается в том, что каждый электрон в атоме рассматривается как движущийся в самосогласованном поле, создаваемом ядром вместе со всеми остальными электронами.

Построение самосогласованного поля может осуществляться либо методом последовательных приближений (изначально предложенным Хартри) или прямым вариационным методом.

Существенно, что вычисления методом самосогласованного поля весьма громоздки, особенно для сложных атомов. Для них применяются другие методы — метод Томаса-Ферми, метод функционала плотности а также различные приближённые методы решения уравнений Хартри-Фока — например, метод Хартри-Фока-Слейтера или Хартри-Фока-Рутана.

Метод Хартри-Фока — в квантовой механике приближённый метод решения уравнения Шрёдингера путём сведения многочастичной задачи к одночастичной в предположении, что каждая частица двигается в некотором усреднённом самосогласованном поле, создаваемом всеми остальными частицами системы.

Метод состоит из нескольких стадий. На первом этапе решается задача о движении электрона в определённом модельном потенциале, который должен как можно лучше отображать взаимодействие выбранного электрона с ядрами атомов и другими электронами. Найденные волновые функции используются для того, чтобы определить взаимодействие электрона с другими электронами и ядрами, уточняя потенциал. В дальнейшем опять решается задача нахождения волновых функций электрона для нового потенциала и нахождения из него следующего, более точного. Процедура продолжается до достижения сходимости.

Волновая функция многоэлектронной системы выбирается в виде детерминанта Слэтера. Уравнения Хартри — Фока являют собой одноэлектронные уравнения типа уравнения Шрёдингера, которым соответствуют орбитали $ {\displaystyle \varphi _{j}} \phi _{j} $, отвечающие минимальным значениям энергии молекулярной системы. В простейшем случае уравнения Хартри — Фока имеют вид:

$ \displaystyle {\hat {F}}[\{\phi _{j}\}](1)={\hat {H}}^{\text{core}}(1)+\sum _{j=1}^{N/2}[2{\hat {J}}_{j}(1)-{\hat {K}}_{j}(1)] $,

где фокиан $ \displaystyle {\hat {F}}[\{\phi _{j}\}](1) $ является оператором Гамильтона для одного электрона, находящегося в самосогласованном поле. Фокиан состоит из суммы одноэлектронного оператора $ {\hat {H}}^{\text{core}}(1) $, равного сумме оператора кинетической энергии электрона (1) и оператора потенциальной энергии его взаимодействия со всеми ядрами:

$ \displaystyle {\hat {H}}^{\text{core}}(1)=-{\frac {1}{2}}\nabla _{1}^{2}-\sum _{\alpha }{\frac {Z_{\alpha }}{r_{1\alpha }}} $,

и суммы операторов $ {\displaystyle (2{\hat {J}}_{j}(1)-{\hat {K}}_{j}(1))} $, определяющих взаимодействие рассматриваемого электрона (1) с усреднённым полем остальных электронов. Действие двух последних операторов на орбиталь $ \phi _{j} $ определяется следующими соотношениями:

$ \displaystyle {\widehat {J}}_{j}(1)f(1)=f(1)\int {\left|\phi _{j}(2)\right|}^{2}{\frac {1}{r_{12}}}\,dr_{2} $ — оператор Кулона, учитывающий взаимодействие с орбиталью $ {\displaystyle j} $-го электрона,

$ \displaystyle {\hat {K}}_{j}(x_{1})f(x_{1})=\phi _{j}(x_{1})\int {{\frac {\phi _{j}^{*}(x_{2})f(x_{2})}{r_{12}}}\mathrm {d} x_{2}} $ — обменный оператор

Метод Хартри-Фока-Рутана (ХФР) — это алгебраический подход к решению уравнений Хартри-Фока, в котором неизвестные одноэлектронные функции-орбитали ищутся в виде линейных комбинаций функций заданного вида — атомных орбиталей (приближение ЛКАО).

In [1]:
import numpy as np
import scipy.special as sp
import scipy.misc
from IPython.display import display,Image

import os, sys

Импортируем функции для квантовых расчетов:

In [2]:
import hfscf
In [3]:
! cat hfscf.py
# -*- coding: utf-8 -*-
"""
Created on Wed Nov  1 14:31:00 2017

@author: Rony J. Letona
@email: zronyj@gmail.com

Descripcion
-----------
Pequeno script para realizar el calculo de la energia y forma de los orbitales
de la molecula de hidrogeno H2 o HeH+ mediante el metodo de Hartree-Fock con
3 sets de bases diferentes: STO-2G, STO-3G y STO-6G.

Referencias
-----------

(1) Cruzeiro, V. W., Roitberg, A., & Polfer, N. C. (2016). Interactively
    Applying the Variational Method to the Dihydrogen Molecule: Exploring
    Bonding and Antibonding. Journal of Chemical Education, 93(9), 1578-1585.
    doi:10.1021/acs.jchemed.6b00017

(2) EMSL Basis Set Exchange. (n.d.). Recuperado noviembre 08, 2017,
    de https://bse.pnl.gov/bse/portal

(3) Feller, D. (1996). The role of databases in support of computational
    chemistry calculations. Journal of Computational Chemistry, 17(13),
    1571-1586.
    doi:10.1002/(sici)1096-987x(199610)17:13<1571::aid-jcc9>3.0.co;2-p

(4) Page, T. R., Boots, C. A., & Freitag, M. A. (2008). Quantum Chemistry:
    Restricted Hartree-Fock SCF Calculations Using Microsoft Excel. Journal of
    Chemical Education, 85(1), 159. doi:10.1021/ed085p159

(5) Schuchardt, K. L., Didier, B. T., Elsethagen, T., Sun, L., Gurumoorthi, V.,
    Chase, J., . . . Windus, T. L. (2007). Basis Set Exchange:  A Community
    Database for Computational Sciences. Journal of Chemical Information and
    Modeling, 47(3), 1045-1052. doi:10.1021/ci600510j

(6) Stewart, B., Hylton, D. J., & Ravi, N. (2013). A Systematic Approach for
    Understanding Slater–Gaussian Functions in Computational Chemistry. Journal
    of Chemical Education, 90(5), 609-612. doi:10.1021/ed300807y

(7) Szabo, A., & Ostlund, N. S. (1996). Modern quantum chemistry: introduction
    to advanced electronic structure theory. Mineola, NY: Dover Publications.

"""

from sympy import *
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# *************************
# Sets de bases
# Referencia (2)
# *************************
    
GTO = {"H":{}, "He":{}}

# Coeficiente exponencial Alfa para Hidrogeno
GTO["H"]["a"] = {2:[1.309756377, 0.233135974],
                 3:[3.42525091, 0.62391373, 0.16885540], 
                 6:[35.52322122, 6.513143725, 1.822142904,
                    0.625955266, 0.243076747, 0.100112428]}

# Coeficiente C para Hidrogeno
GTO["H"]["c"] = {2:[0.430128498, 0.678913531],
                 3:[0.15432897, 0.53532814, 0.44463454],
                 6:[0.00916359628, 0.04936149294, 0.16853830490,
                    0.37056279970, 0.41649152980, 0.13033408410]}

# Coeficiente exponencial Alfa para Helio
GTO["He"]["a"] = {2:[2.4328790, 0.4330510],
                  3:[6.36242139, 1.15892300, 0.31364979],
                  6:[65.98456824, 12.09819836, 3.384639924,
                     1.162715163, 0.451516322, 0.185959356]}

# Coeficiente C para Helio
GTO["He"]["c"] = {2:[0.430128498, 0.678913531],
                  3:[0.15432897, 0.53532814, 0.44463454],
                  6:[0.00916359628, 0.04936149294, 0.16853830490,
                     0.37056279970, 0.41649152980, 0.13033408410]}

# *********************************
# Funciones de onda (GTOs)
# Referencia (7) p. 153 eq. (3.203)
# *********************************
a, r, R = symbols("alpha r R")

phi = (2 * a / pi)**(3./4) * exp(-a * (r - R)**2)

def Phi (rn, Ru, base = GTO["H"], b = 3):
    wf = 0
    for i in range(b):
        wf += base["c"][b][i] * \
        phi.subs([(a, base["a"][b][i]), (r, rn), (R, Ru)])
    return wf

def Psi (r, R, base = GTO["H"], b = 3):
    wf = 0
    for i in range(b):
        a = base["a"][b][i]
        wf += base["c"][b][i] * (2 * a / np.pi)**0.75 * np.exp(-a * (r - R)**2)
    return wf

# ***********************************
# Funcion para graficar los orbitales
# con respecto a la distancia
# Referencia (1)
# Referencia (4)
# ***********************************
def orbital (c, r = 1.4632, b1 = GTO["H"], b2 = GTO["H"], b = 3):
    dom = np.arange(-3.5,r+3.55,0.05)
    psi1 = [c[0,0] * Psi(x, 0, base=b1, b=b) + c[1,0] * \
            Psi(x, r, base=b2, b=b) for x in dom]
    psi2 = [c[0,1] * Psi(x, 0, base=b1, b=b) + c[1,1] * \
            Psi(x, r, base=b2, b=b) for x in dom]
    psi3 = [y**2 for y in psi1]
    psi4 = [y**2 for y in psi2]
    plt.title("Funciones de onda resultantes del calculo HF-SCF")
    plt.plot(dom, psi1, "r--", label="$\Psi_1$")
    plt.plot(dom, psi2, "b--", label="$\Psi_2$")
    plt.plot(dom, psi3, "g-", label="$\Psi_{1}^{2}$")
    plt.plot(dom, psi4, "k-", label="$\Psi_{2}^{2}$")
    plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
    plt.grid(True)
    plt.show()

def orbital2D (C, X, F, r = 1.4632, b1 = GTO["H"], b2 = GTO["H"], b = 3, delta=0.02):
    domx = np.arange(-3.5,r+3.5+delta,delta)
    domy = np.arange(-1.5-r/2,1.5+r/2+delta,delta)
    densmap1 = [[0]*len(domx)]*len(domy)
    densmap2 = [[0]*len(domx)]*len(domy)
    for y in range(len(domy)):
        q1 = Psi(domy[y], 0, base=b1, b=b)
        q2 = Psi(domy[y], 0, base=b2, b=b)
        for x in range(len(domx)):
            p1 = Psi(domx[x], 0, base=b1, b=b)
            p2 = Psi(domx[x], r, base=b2, b=b)
            densmap1[y][x] = (C[0,0]*p1*q1 + C[1,0]*p2*q2)**2
            densmap2[y][x] = (C[0,1]*p1*q1 + C[1,1]*p2*q2)**2
        densmap1[y] = tuple(densmap1[y])
        densmap2[y] = tuple(densmap2[y])
    dm1 = np.array(densmap1, dtype=np.float32)
    dm2 = np.array(densmap2, dtype=np.float32)
    energias = ener_orbs(X, F)
    fig = plt.figure()
    e1 = energias[0]
    a1 = fig.add_subplot(221)
    a1.title.set_text("Orbital 1\nE = " + str(e1))
    a1.imshow(dm1,cmap=cm.hot)
    e2 = energias[1]
    a2 = fig.add_subplot(224)
    a2.title.set_text("Orbital 2\nE = " + str(e2))
    a2.imshow(dm2,cmap=cm.hot)
    plt.show()

# **********************************
# Funcion para diagonalizar matrices
# Referencia (7) p. 144 eq. (3.169)
# **********************************
def diagon(m=np.matrix([[1,-0.5],[-2,1.5]])):
    m_eigenval, m_eigenvec = np.linalg.eig(m)
    mvp = np.matrix(np.diag([1/i**0.5 for i in m_eigenval.tolist()]),
                    dtype=np.float64)
    return (m_eigenvec * mvp)[::-1]

# **************************************
# Funcion para diagonalizar la matriz F'
# Referencia (7) p. 427
# **************************************
def diagon2(m=np.matrix([[1,-0.5],[-2,1.5]])):
    if abs(m[0,0] - m[1,1]) < 1E-8:
        temp = np.pi/4
    elif (abs(m[0,1]) < 1E-8) or (abs(m[1,0]) < 1E-8):
        temp = np.pi/4
    else:
        temp = 0.5 * np.arctan( 2*m[0,1] / (m[0,0] - m[1,1]) )
    ed = np.cos(temp)
    ec = np.sin(temp)
    return np.matrix( [[ed,ec],[ec,-ed]] )
    
# *******************************
# Matriz de traslape S
# Referencia (4)
# Referencia (7) p. 412 eq. (A.9)
# *******************************
def s (b1 = GTO["H"], b2 = GTO["H"], r = 0, b = 3):
    suv = 0
    for i in range(b-1,-1,-1):
        for j in range(b-1,-1,-1):
            du = (2*b1["a"][b][i]/np.pi)**0.75 * b1["c"][b][i]
            dv = (2*b2["a"][b][j]/np.pi)**0.75 * b2["c"][b][j]
            Sa = b1["a"][b][i] + b2["a"][b][j]
            suv += du * dv * (np.pi/Sa)**1.5 * \
            np.exp( -((b1["a"][b][i] * b2["a"][b][j])/Sa) * r**2 )
    return suv

def S(R = [0,1.4632], b1 = GTO["H"], b2 = GTO["H"], b = 3):
    MS = [[R[0]-R[0],R[1]-R[0]],
          [R[0]-R[1],R[1]-R[1]]]
    for i in range(2):
        for j in range(2):
            MS[i][j] = s(b1=b1, b2=b2, r=MS[i][j], b=b)
    return np.matrix(MS, dtype=np.float64)

# ********************************
# Matriz de energia cinetica T
# Referencia (7) p. 412 eq. (A.11)
# ********************************
def t(b1 = GTO["H"], b2 = GTO["H"], r = 0, b = 3):
    tuv = 0
    for i in range(b-1,-1,-1):
        for j in range(b-1,-1,-1):
            du = (2*b1["a"][b][i]/np.pi)**0.75 * b1["c"][b][i]
            dv = (2*b2["a"][b][j]/np.pi)**0.75 * b2["c"][b][j]
            Sa = b1["a"][b][i] + b2["a"][b][j]
            Ma = b1["a"][b][i] * b2["a"][b][j]
            tuv += du * dv * (Ma/Sa) * (3 - 2*Ma/Sa * r**2) * \
            (np.pi/Sa)**1.5 * np.exp( -((Ma/Sa) * r**2 ))
    return tuv

def T(R=[0,1.4632], b1 = GTO["H"], b2 = GTO["H"], b = 3):
    MT = [[R[0]-R[0],R[1]-R[0]],
          [R[0]-R[1],R[1]-R[1]]]
    for i in range(2):
        for j in range(2):
            MT[i][j] = t(b1=b1, b2=b2, r=MT[i][j], b=b)
    return np.matrix(MT, dtype=np.float64)

# ********************************
# Matriz de energia potencial V
# Referencia (7) p. 415 eq. (A.33)
# ********************************
def v (b1 = GTO["H"], b2 = GTO["H"], r=1.4632, Rp=[0,0,1,1], Z=[1,1], b = 3):
    vuv = 0
    for k in range(len(Z)):
        R = [l*r for l in Rp[2*k:2*k+2]]
        for i in range(b-1,-1,-1):
            for j in range(b-1,-1,-1):
                du = (2*b1["a"][b][i]/np.pi)**0.75 * b1["c"][b][i]
                dv = (2*b2["a"][b][j]/np.pi)**0.75 * b2["c"][b][j]
                Sa = b1["a"][b][i] + b2["a"][b][j]
                Rr = (b1["a"][b][i] * R[0] + b2["a"][b][j] * R[1]) / Sa
                Ruv = abs(R[1]-R[0])
                Rur = (Rr - Ruv)**2
                t = Sa*Rur
                if t < 1E-5:
                    F = 1 - t
                else:
                    F = 0.5*(np.pi/t)**0.5 * erf(t**0.5)
                temp = (-2*du*dv*np.pi*Z[k]/Sa) * F * \
                np.exp( -((b1["a"][b][i] * b2["a"][b][j])/Sa * Ruv**2 ))
                vuv += N(temp)
    return vuv

def V (r = 1.4632, Z=[1,1], b1 = GTO["H"], b2 = GTO["H"], b = 3):
    MV = [[[0,0,1,1],[0,1,1,0]],
          [[1,0,0,1],[1,1,0,0]]]
    for i in range(2):
        for j in range(2):
            MV[i][j] = v(b1=b1, b2=b2, r=r, Rp=MV[i][j], Z=Z, b=b)
    return np.matrix(MV, dtype=np.float64)

# *********************************
# Matriz de Hamiltonano H
# Referencia (7) p. 141 eq. (3.153)
# *********************************
def H (R=[0,1.4632], Z=[1,1], b1 = GTO["H"], b2 = GTO["H"], b = 3):
    th = T(R = R, b1 = b1, b2 = b2, b = 3)
    vh = V(r = R[1] - R[0], Z = Z, b1 = b1, b2 = b2, b = b)
    h = th + vh
    return h

# **********************************
# Integral de dos electrones <uv|ls>
# Referencia (4)
# Referencia (7) p. 416 eq. (A.41)
# **********************************
def ide (o = [0,0,0,0], R=[0,1], b1 = GTO["H"], b2 = GTO["H"], b = 3):
    res = 0
    base = [b1,b2]
    for i in range(b-1,-1,-1):
        for j in range(b-1,-1,-1):
            for k in range(b-1,-1,-1):
                for l in range(b-1,-1,-1):
                    s1 = base[o[0]]["a"][b][i] + base[o[1]]["a"][b][j]
                    s2 = base[o[2]]["a"][b][k] + base[o[3]]["a"][b][l]
                    s3 = s1 + s2
                    m1 = base[o[0]]["a"][b][i] * base[o[1]]["a"][b][j]
                    m2 = base[o[2]]["a"][b][k] * base[o[3]]["a"][b][l]
                    index = [i,j,k,l]
                    d = [(2*base[o[m]]["a"][b][index[m]]/np.pi)**0.75 * \
                         base[o[m]]["c"][b][index[m]] for m in range(4)]
                    contrac = np.prod(d)
                    R1 = R[o[1]] - R[o[0]]
                    R2 = R[o[3]] - R[o[2]]
                    Rr = (base[o[0]]["a"][b][i] * R[o[0]] + \
                          base[o[1]]["a"][b][j] * R[o[1]]) / s1
                    Rs = (base[o[2]]["a"][b][k] * R[o[2]] + \
                          base[o[3]]["a"][b][l] * R[o[3]]) / s2
                    Rsr = (Rs - Rr)**2
                    piterm = 2*np.pi**2.5 / (s1*s2*s3**0.5)
                    expterm = np.exp(-m1/s1 * R1**2 - m2/s2 * R2**2)
                    t = s1 * s2 / s3 * Rsr
                    if t < 1E-5:
                        F = 1 - t
                    else:
                        F = 0.5 * (np.pi/t)**0.5 * erf(t**0.5)
                    res += contrac * piterm * expterm * F
    return N(res)

# *************************************
# Matriz de dos electrones G
# Referencia (7) p. 141 eq. (3.154)
# Referencia (7) p. 427
# *************************************
def G (r=1.4632, p=np.matrix([[0,0],[0,0]], dtype=np.float64), b1=GTO["H"],
       b2=GTO["H"], b=3):
    g = [[0,0],[0,0]]
    for u in range(2):
        for v in range(2):
            for w in range(2):
                for x in range(2):
                    j = ide(o=[u,v,w,x], R=[0,r], b1=b1, b2=b2, b=b)
                    k = ide(o=[u,x,w,v], R=[0,r], b1=b1, b2=b2, b=b)
                    g[u][v] += p[w,x] * ( j - 0.5 * k )
    return np.matrix(g, dtype=np.float64)

# *********************************
# Matriz de densidad electronica P
# Referencia (7) p. 139 eq. (3.145)
# *********************************
def P (C=np.matrix([[0,0],[0,0]], dtype=np.float64)):
    p = [[C[0,0]*C[0,0], C[0,0]*C[1,0]],
         [C[1,0]*C[0,0], C[1,0]*C[1,0]]]
    return 2*np.matrix(p, dtype=np.float64)


# **********************************
# Calculo de energia de cada orbital
# Referencia (4)
# **********************************
def ener_orbs(x, f):
    return np.diag(x.getH() * f * x)

# *********************************
# Calculo de energia electronica
# Referencia (7) p. 150 eq. (3.184)
# *********************************
def ener_elec(p, h, f):
    m = 0.5 * p.reshape(1,4) * (h + f).reshape(1,4).T
    return m[0,0]

# *********************************
# Calculo de energia total
# Referencia (7) p. 150 eq. (3.185)
# *********************************
def ener_tot(r=1.4632, Z=[1,1], elec=0):
    nuc = Z[0] * Z[1] / r
    return nuc + elec

Метод Хартри-Фока-Рутана

Базисные наборы представляют собой компромисс между вычислительными возможностями и желаемой точностью. Существует два типа базисных функций (атомных орбиталей) – орбитали слэйтеровского типа (STO – Slater Type Orbitals) и гауссовского типа (GTO – Gaussian Type Orbitals).

Базисные функции гауссова и слэтеровского типа, локализованные на разных атомах, не ортогональны друг другу. Пусть в качестве базисного набора выбран ряд таких неортогональных функций.

Основная идея:уменьшение количества интегралов, за счётобнуления перекрывания некоторых орбиталей.Если два ядра далеко друг от друга то вероятно центрированныек ядрам функции не перекрываются. Основной результат с точки зрения уравнений – это матрица обмена, $ S $ = 1.Тогда уравнение Рутан-Хола будет выглядеть как:

$ {\mathbf {F}}{\mathbf {C}}={\mathbf {S}}{\mathbf {C}}{\mathbf {\epsilon }} $

где $F$ – матрица оператора Фока, $S$ – матрица перекрытия для базисных функций.

Алгоритм расчета с помощью метода Хартри Фока

  • Входные данные: В качестве входящих данных задаются координаты атомов, заряды ядер, полное число электронов и базисные функции.

  • Получение матриц: Получение матриц, не зависящих от собственных векторов (матрица перекрытия $S_{pq} $, матрица одноэлектронного гамильтониана $H_{pq} $, двухэлектронные интегралы.

  • Приведение матрицы перекрытия к единичной: Расчет матрицы $V_{pq} $.

  • Диагонализация матрицы Фока: Используя матрицу Vpq, решается обобщенное уравнение на собственные значения.

  • Построение новой матрицы плотности: Используя полученные собственные векторы, строится новая матрица плотности.

In [4]:
x = Image(filename='plot.jpg')
display(x)
In [5]:
# Import some libraries to have the maths functions and plotting
import matplotlib.pyplot as plt

x = np.linspace(-5,5,num=1000)
r = abs(x)

zeta = 1.0

psi_STO = (zeta**3/np.pi)**(0.5)*np.exp(-zeta*r)

plt.figure(figsize=(4,3))
plt.plot(x,psi_STO)
Out[5]:
[<matplotlib.lines.Line2D at 0x7fd24d8e76d0>]

Функция расчёта орбиталей на основе подхода Хартри-Фока-Рутана

In [6]:
def SCF (r = 1.4632, Z=[1,1], b1 = hfscf.GTO["H"], b2 = hfscf.GTO["H"], b = 3, vbs=False):
    
    R = [0, r]   # r - electronic degrees of freedom, 
                 # R - nuclear degrees of freedom.
        
    # Generate orbital overlap matrix S
    
    if vbs: print("*) Generando matriz de traslape S.")
    s_scf = hfscf.S(R, b1, b2, b)
              
    # Generate Hamiltonian H for Fock operator
              
    if vbs: print("\n*) Generando hamiltoniano H.")
    h_scf = hfscf.H(R, Z, b1, b2, b)
        
    # Diagonalize matrix S and find matrix X
    
    if vbs: print("\n*) Diagonalizando matriz S y hallando matriz diagonal X.")
    X = hfscf.diagon(s_scf)
    Xa = X.getH()
    
    # Estimate density matrix P
    
    if vbs: print("\n*) Creando matriz de densidad P.")
    p_scf = np.matrix([[0,0],[0,0]], dtype=np.float64)  # Referencia (7) p. 148
    
    # Begin iterative process
    
    if vbs: print("\n*) Comenzando con el SCF.")
    for iteracion in range(50):
        
        # Build Fock matrix F
        # F = H + G
        # G = p*(J-0.5*K), p - p_psf, J - Coulomb operator, K - exchange operator
        
        if vbs: print("\n**) Generando la matriz de Fock: calculando \ integrales de dos electrones.")
        g_scf = hfscf.G(r, p_scf, b1, b2, b)
        f_scf = h_scf + g_scf   # Referencia (7) p. 141 eq. (3.154)
        
        # Hartree-Fock equations to Hartree-Fock-Roothaan ones
        
        # Build matrix F '
        # F' = X_adj * F * X
        # X_adj - hermitian adjoint from the complex Hilbert space
        
        if vbs: print("**) Cambiando la base de F.")
        f_tra = Xa * f_scf * X
        
        # Diagonalize matrix F and construct matrix C'
        
        if vbs: print("**) Diagonalizando F' y generando C'.")
        c_tra = hfscf.diagon2(f_tra)
        
        # Build matrix C
        # C = X * C'
        
        if vbs: print("**) Construyendo matriz de coeficientes C.")
        c_scf = X * c_tra
        
        # Build matrix P from matrix C
        
        if vbs: print("**) Recalculando matriz de densidad P.")
        p_temp = hfscf.P(c_scf)
        
        # Calculating of electron and total energies
        
        electron_E  = hfscf.ener_elec(p_temp, h_scf, f_scf)
        total_E = hfscf.ener_tot(r, Z, 0)
        
        # Plots
        
        hfscf.orbital(c_tra, r, b1, b2, b)   # 1D plot
        hfscf.orbital2D(c_scf, X, f_scf, r, b1, b2, b, 0.02)   # 2D plot
        
        print("\nConcluida la " + str(iteracion + 1) + ". iteracion.\n")
        
        # Review convergence
        
        if np.linalg.norm(p_temp - p_scf) < 1E-4: # Referencia (7) p. 148
            print("\n\n-->El campo autoconsistente SI ha convergido!")   # The self-consistent 
                                                                         # field has converged!
            return {"S":s_scf,"H":h_scf,"X": X,"F":f_scf,"C":c_scf,"P":p_temp}
        else:
            p_scf = p_temp
    print("\n\n-->El campo autoconsistente NO ha convergido!\nRevisar supuestos.")
    return {"S":s_scf,"H":h_scf,"X": X,"F":f_scf,"C":c_scf,"P":p_temp}
In [7]:
print(SCF())
Concluida la 1. iteracion.

Concluida la 2. iteracion.

Concluida la 3. iteracion.

Concluida la 4. iteracion.

Concluida la 5. iteracion.

Concluida la 6. iteracion.

Concluida la 7. iteracion.



-->El campo autoconsistente SI ha convergido!
{'C': matrix([[ 0.55258114, -1.17442421],
        [ 0.55258013,  1.17442468]]), 'F': matrix([[-0.34684107, -0.57771139],
        [-0.57771139, -0.34684028]]), 'H': matrix([[-1.09920375, -0.91954841],
        [-0.91954841, -1.09920375]]), 'P': matrix([[0.61069182, 0.61069071],
        [0.61069071, 0.6106896 ]]), 'S': matrix([[0.99999999, 0.63749012],
        [0.63749012, 0.99999999]]), 'X': matrix([[ 0.55258063,  1.17442445],
        [ 0.55258063, -1.17442445]])}

Расчет самосогласованного поля

In [8]:
# Coeff is the d_n variable in the equation above
Coeff = np.array([[1.00000,0.0000000,0.000000],
                  [0.678914,0.430129,0.000000],
                  [0.444635,0.535328,0.154329]])

# Expon is the alpha variable in the equation above
Expon = np.array([[0.270950,0.000000,0.000000],
                  [0.151623,0.851819,0.000000],
                  [0.109818,0.405771,2.227660]]) 

psi_CGF_STO1G = Coeff[0,0]*(2*Expon[0,0]/np.pi)**(0.75)*np.exp(-Expon[0,0]*r**2)
psi_CGF_STO2G = Coeff[1,0]*(2*Expon[1,0]/np.pi)**(0.75)*np.exp(-Expon[1,0]*r**2) \
                + Coeff[1,1]*(2*Expon[1,1]/np.pi)**(0.75)*np.exp(-Expon[1,1]*r**2) \
                + Coeff[1,2]*(2*Expon[1,2]/np.pi)**(0.75)*np.exp(-Expon[1,2]*r**2)
psi_CGF_STO3G = Coeff[2,0]*(2*Expon[2,0]/np.pi)**(0.75)*np.exp(-Expon[2,0]*r**2) \
                + Coeff[2,1]*(2*Expon[2,1]/np.pi)**(0.75)*np.exp(-Expon[2,1]*r**2) \
                + Coeff[2,2]*(2*Expon[2,2]/np.pi)**(0.75)*np.exp(-Expon[2,2]*r**2)
    
# Plot the three functions
plt.figure(figsize=(5,3))
plt.title("Approximations to a STO with CGF")
plt.plot(x,psi_STO,label="STO")
plt.plot(x,psi_CGF_STO1G,label="STO-1G")
plt.legend()
plt.figure(figsize=(5,3))
plt.plot(x,psi_STO,label="STO")
plt.plot(x,psi_CGF_STO2G,label="STO-2G")
plt.legend()
plt.figure(figsize=(5,3))
plt.plot(x,psi_STO,label="STO")
plt.plot(x,psi_CGF_STO3G,label="STO-3G")
plt.legend()
Out[8]:
<matplotlib.legend.Legend at 0x7fd24c396fd0>

Интегралы

In [9]:
def S_int(A,B,Rab2):
    """
    Calculates the overlap between two gaussian functions 
    
    """
    return (np.pi/(A+B))**1.5*np.exp(-A*B*Rab2/(A+B))
In [10]:
def T_int(A,B,Rab2):
    """
    Calculates the kinetic energy integrals for un-normalised primitives
    
    """
    return A*B/(A+B)*(3.0-2.0*A*B*Rab2/(A+B))*(np.pi/(A+B))**1.5*np.exp(-A*B*Rab2/(A+B))
In [11]:
def V_int(A,B,Rab2,Rcp2,Zc):
    """
    Calculates the un-normalised nuclear attraction integrals
    """
    V = 2.0*np.pi/(A+B)*F0((A+B)*Rcp2)*np.exp(-A*B*Rab2/(A+B))
    return -V*Zc
In [12]:
# Mathematical functions

def F0(t):
    """
    F function for 1s orbital
    """
    if (t<1e-6):
        return 1.0-t/3.0
    else:
        return 0.5*(np.pi/t)**0.5*sp.erf(t**0.5)
    
def erf(t):
    """
    Approximation for the error function
    """
    P = 0.3275911
    A = [0.254829592,-0.284496736,1.421413741,-1.453152027,1.061405429]
    T = 1.0/(1+P*t)
    Tn=T
    Poly = A[0]*Tn
    for i in range(1,5):
        Tn=Tn*T
        Poly=Poly*A[i]*Tn
    return 1.0-Poly*np.exp(-t*t)
In [13]:
def TwoE(A,B,C,D,Rab2,Rcd2,Rpq2):
    """
    Calculate two electron integrals
    A,B,C,D are the exponents alpha, beta, etc.
    Rab2 equals squared distance between centre A and centre B
    """
    return 2.0*(np.pi**2.5)/((A+B)*(C+D)*np.sqrt(A+B+C+D))*F0((A+B)*(C+D)*Rpq2/(A+B+C+D))*np.exp(-A*B*Rab2/(A+B)-C*D*Rcd2/(C+D))
In [14]:
def Intgrl(N,R,Zeta1,Zeta2,Za,Zb):
    """
    Declares the variables and compiles the integrals.
    """
    
    global S12,T11,T12,T22,V11A,V12A,V22A,V11B,V12B,V22B,V1111,V2111,V2121,V2211,V2221,V2222
    
    
    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
    
    R2 = R*R
    
    # The coefficients for the contracted Gaussian functions are below
    Coeff = np.array([[1.00000,0.0000000,0.000000],
                      [0.678914,0.430129,0.000000],
                      [0.444635,0.535328,0.154329]])
    
    Expon = np.array([[0.270950,0.000000,0.000000],
                      [0.151623,0.851819,0.000000],
                      [0.109818,0.405771,2.227660]])
    D1 = np.zeros([3])
    A1 = np.zeros([3])
    D2 = np.zeros([3])
    A2 = np.zeros([3])
    
    # This loop constructs the contracted Gaussian functions
    for i in range(N):
        A1[i] = Expon[N-1,i]*(Zeta1**2)
        D1[i] = Coeff[N-1,i]*((2.0*A1[i]/np.pi)**0.75)
        A2[i] = Expon[N-1,i]*(Zeta2**2)
        D2[i] = Coeff[N-1,i]*((2.0*A2[i]/np.pi)**0.75)
    
    # Calculate one electron integrals 
    # Centre A is first atom centre B is second atom
    # Origin is on second atom
    # V12A - off diagonal nuclear attraction to centre A etc.
    for i in range(N):
        for j in range(N):
            # Rap2 - squared distance between centre A and centre P
            Rap = A2[j]*R/(A1[i]+A2[j])
            Rap2 = Rap**2
            Rbp2 = (R-Rap)**2
            S12 = S12 + S_int(A1[i],A2[j],R2)*D1[i]*D2[j]
            T11 = T11 + T_int(A1[i],A1[j],0.0)*D1[i]*D1[j]
            T12 = T12 + T_int(A1[i],A2[j],R2)*D1[i]*D2[j]
            T22 = T22 + T_int(A2[i],A2[j],0.0)*D2[i]*D2[j]
            V11A = V11A + V_int(A1[i],A1[j],0.0,0.0,Za)*D1[i]*D1[j]
            V12A = V12A + V_int(A1[i],A2[j],R2,Rap2,Za)*D1[i]*D2[j]
            V22A = V22A + V_int(A2[i],A2[j],0.0,R2,Za)*D2[i]*D2[j]
            V11B = V11B + V_int(A1[i],A1[j],0.0,R2,Zb)*D1[i]*D1[j]
            V12B = V12B + V_int(A1[i],A2[j],R2,Rbp2,Zb)*D1[i]*D2[j]
            V22B = V22B + V_int(A2[i],A2[j],0.0,0.0,Zb)*D2[i]*D2[j]
    
    # Calculate two electron integrals
    
    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]
    return 
In [15]:
def Colect(N,R,Zeta1,Zeta2,Za,Zb):
    """
    Takes the basic integrals and assembles the relevant matrices, 
    that are S,H,X,XT and Two electron integrals
    """
    # Form core hamiltonian
    H[0,0] = T11+V11A+V11B
    H[0,1] = T12+V12A+V12B
    H[1,0] = H[0,1]
    H[1,1] = T22+V22A+V22B

    # Form overlap matrix
    S[0,0] = 1.0
    S[0,1] = S12
    S[1,0] = S12
    S[1,1] = 1.0
    
    # This is S^-1/2
    X[0,0] = 1.0/np.sqrt(2.0*(1.0+S12))
    X[1,0] = X[0,0]
    X[0,1] = 1.0/np.sqrt(2.0*(1.0-S12))
    X[1,1] = -X[0,1]
    
    # This is the coulomb and exchange term (aa|bb) and (ab|ba)
    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
In [16]:
# Self consistent field calculation (SCF):
# 1. Guess an initial density matrix $\boldsymbol{P}$ (for this example will use the initial guess $ P=0 $)
# 2. From $\boldsymbol{P}$ calculate the Fock matrix
# 3. Calculate $\boldsymbol{F'}=\boldsymbol{S}^{-1/2}\boldsymbol{F}\boldsymbol{S}^{1/2}$
# 4. Solve the eigenvalue problem using the secular equation $|\boldsymbol{F'}-\boldsymbol{E}\boldsymbol{I}|=0$ giving the eigenvalues $\boldsymbol{E}$ and eigenvectors $\boldsymbol{C'}$ which can be solved by diagonalising $\boldsymbol{F'}$
# 5. Calculate the molecular orbitals coefficients by reverting $\boldsymbol{C'}=\boldsymbol{S}^{-1/2}\boldsymbol{C}$
# 6. Calculate the new density matrix $\boldsymbol{P}$ from the matrix $\boldsymbol{C}$ 
# 7. Check to see if the energy has converged if not then head back to step 6 and repeat.


def SCF(N,R,Zeta1,Zeta2,Za,Zb,G):
    """
    Performs the SCF iterations
    """
    Crit = 1e-11 # Convergence critera
    Maxit = 250 # Maximum number of iterations
    Iter=0
    
    ######## STEP 1. Guess an initial density matrix ########
    # Use core hamiltonian for initial guess of F, I.E. (P=0)
    P = np.zeros([2,2])
    
    Energy = 0.0
    
    while (Iter<Maxit):
        Iter += 1
        print(Iter)
        
        ######## STEP 2. calculate the Fock matrix ########
        # Form two electron part of Fock matrix from P
        G = np.zeros([2,2]) # This is the two electron contribution in the equations above
        for i in range(2):
            for j in range(2):
                for k in range(2):
                    for l in range(2):
                        G[i,j]=G[i,j]+P[k,l]*(TT[i,j,k,l]-0.5*TT[i,j,k,l])

        # Add core hamiltonian H^CORE to get fock matrix
        F = H+G
        
        # Calculate the electronic energy
        Energy = np.sum(0.5*P*(H+F))
        
        print('Electronic energy = ',Energy)
        
        ######## STEP 3. Calculate F' (remember S^-1/2 is X and S^1/2 is X.T) ########
        G = np.matmul(F,X)
        Fprime = np.matmul(X.T,G)
        
        ######## STEP 4. Solve the eigenvalue problem ########
        # Diagonalise transformed Fock matrix
        Diag(Fprime,Cprime,E)
        
        ######## STEP 5. Calculate the molecular orbitals coefficients ########
        # Transform eigen vectors to get matrix C
        C = np.matmul(X,Cprime)
        
        ######## STEP 6. Calculate the new density matrix from the old P ########
        Oldp = np.array(P)
        P = np.zeros([2,2])
        
        # Form new density matrix
        for i in range(2):
            for j in range(2):
                #Save present density matrix before creating a new one
                for k in range(1):
                    P[i,j] += 2.0*C[i,k]*C[j,k]

        ######## STEP 7. Check to see if the energy has converged ########
        Delta = 0.0
        # Calculate delta the difference between the old density matrix Old P and the new P
        Delta = (P-Oldp)
        Delta = np.sqrt(np.sum(Delta**2)/4.0)
        print("Delta",Delta)
        
        #Check for convergence
        if (Delta<Crit):
            # Add nuclear repulsion to get the total energy
            Energytot = Energy+Za*Zb/R
            print("Calculation converged with electronic energy:",Energy)
            print("Calculation converged with total energy:",Energytot)
            print("Density matrix", P)
            print("Mulliken populations",np.matmul(P,S))
            print("Coeffients",C)
            
            break
In [17]:
def FormG():
    """
    Calculate the G matrix from the density matrix and two electron integals
    """
    for i in range(2):
        for j in range(2):
            G[i,j]=0.0
            for k in range(2):
                for l in range(2):
                    G[i,j]=G[i,j]+P[k,l]*(TT[i,j,k,l]-0.5*TT[i,j,k,l])
                    
def Mult(A,B,C_,IM,M):
    """
    Multiples two square matrices A and B to get C
    """
    for i in range(M):
        for j in range(M):
            for k in range(M):
                C_[i,j] = A[i,j]*B[i,j]
                
def Diag(Fprime,Cprime,E):
    """
    Diagonalises F to give eigenvectors in C and eigen values in E, theta is the angle describing the solution
    """
    # 
    import math
    # Angle for heteronuclear diatonic
    Theta = 0.5*math.atan(2.0*Fprime[0,1]/(Fprime[0,0]-Fprime[1,1]))
    #print('Theta', Theta)
    
    Cprime[0,0] = np.cos(Theta)
    Cprime[1,0] = np.sin(Theta)
    Cprime[0,1] = np.sin(Theta)
    Cprime[1,1] = -np.cos(Theta)
    
    E[0,0] = Fprime[0,0]*np.cos(Theta)**2+Fprime[1,1]*np.sin(Theta)**2+Fprime[0,1]*np.sin(2.0*Theta)
    E[1,1] = Fprime[1,1]*np.cos(Theta)**2+Fprime[0,0]*np.sin(Theta)**2-Fprime[0,1]*np.sin(2.0*Theta)
    
    if (E[1,1] <= E[0,0]):
        Temp = E[1,1]
        E[1,1] = E[0,0]
        E[0,0] = Temp
        Temp = Cprime[0,1]
        Cprime[0,1] = Cprime[0,0]
        Cprime[0,0] = Temp
        Temp = Cprime[1,1]
        Cprime[1,1]=Cprime[1,0]
        Cprime[1,0]=Temp
    return
In [18]:
def HFCALC(N,R,Zeta1,Zeta2,Za,Zb,G):
    """
    Calculates the integrals constructs the matrices and then runs the SCF calculation
    """
    # Calculate one and two electron integrals
    Intgrl(N,R,Zeta1,Zeta2,Za,Zb)
    # Put all integals into array
    Colect(N,R,Zeta1,Zeta2,Za,Zb)
    # Perform the SCF calculation
    SCF(N,R,Zeta1,Zeta2,Za,Zb,G)
    return
In [19]:
"""
Let's set up the variables and perform the calculations
"""
global H,S,X,XT,TT,G,C,P,Oldp,F,Fprime,Cprime,E,Zb

H = np.zeros([2,2])
S = np.zeros([2,2])
X = np.zeros([2,2])
XT = np.zeros([2,2])
TT = np.zeros([2,2,2,2])
G = np.zeros([2,2])
C = np.zeros([2,2])

P = np.zeros([2,2])
Oldp = np.zeros([2,2])
F = np.zeros([2,2])
Fprime = np.zeros([2,2])
Cprime = np.zeros([2,2])
E = np.zeros([2,2])

Energy = 0.0
Delta = 0.0

N = 3
R = 1.4632
Zeta1 = 2.0925
Zeta2 = 1.24
Za = 2.0
Zb = 1.0
HFCALC(N,R,Zeta1,Zeta2,Za,Zb,G)
1
('Electronic energy = ', 0.0)
('Delta', 0.8828668530136917)
2
('Electronic energy = ', -4.141862876133924)
('Delta', 0.4263766628496478)
3
('Electronic energy = ', -4.212505158147097)
('Delta', 0.16975144403838754)
4
('Electronic energy = ', -4.224917134805615)
('Delta', 0.07267662655588335)
5
('Electronic energy = ', -4.227057963457619)
('Delta', 0.030557750857188388)
6
('Electronic energy = ', -4.2274451653170315)
('Delta', 0.012968917066136268)
7
('Electronic energy = ', -4.227514202089319)
('Delta', 0.005484174932893008)
8
('Electronic energy = ', -4.227526599371511)
('Delta', 0.0023227903428170793)
9
('Electronic energy = ', -4.227528819312722)
('Delta', 0.0009831518344974117)
10
('Electronic energy = ', -4.227529217321419)
('Delta', 0.0004162497141851593)
11
('Electronic energy = ', -4.227529288642739)
('Delta', 0.0001762119957375153)
12
('Electronic energy = ', -4.227529301425998)
('Delta', 7.460002282548679e-05)
13
('Electronic energy = ', -4.22752930371699)
('Delta', 3.1581529191478555e-05)
14
('Electronic energy = ', -4.227529304127593)
('Delta', 1.3369996256528571e-05)
15
('Electronic energy = ', -4.227529304201182)
('Delta', 5.660147518148093e-06)
16
('Electronic energy = ', -4.227529304214371)
('Delta', 2.396210241800624e-06)
17
('Electronic energy = ', -4.227529304216735)
('Delta', 1.014429316219236e-06)
18
('Electronic energy = ', -4.227529304217159)
('Delta', 4.294561133852487e-07)
19
('Electronic energy = ', -4.227529304217233)
('Delta', 1.8180914907929353e-07)
20
('Electronic energy = ', -4.2275293042172475)
('Delta', 7.696844345507004e-08)
21
('Electronic energy = ', -4.22752930421725)
('Delta', 3.2584395202991233e-08)
22
('Electronic energy = ', -4.227529304217251)
('Delta', 1.3794521310145388e-08)
23
('Electronic energy = ', -4.22752930421725)
('Delta', 5.839876050606837e-09)
24
('Electronic energy = ', -4.227529304217251)
('Delta', 2.4722969625894194e-09)
25
('Electronic energy = ', -4.227529304217251)
('Delta', 1.0466405412830377e-09)
26
('Electronic energy = ', -4.227529304217251)
('Delta', 4.4309262759580606e-10)
27
('Electronic energy = ', -4.227529304217251)
('Delta', 1.8758207014503013e-10)
28
('Electronic energy = ', -4.227529304217251)
('Delta', 7.941234693562014e-11)
29
('Electronic energy = ', -4.22752930421725)
('Delta', 3.3618876020147625e-11)
30
('Electronic energy = ', -4.227529304217251)
('Delta', 1.4232286447314373e-11)
31
('Electronic energy = ', -4.227529304217251)
('Delta', 6.025252121340928e-12)
('Calculation converged with electronic energy:', -4.227529304217251)
('Calculation converged with total energy:', -2.860662163703309)
('Density matrix', array([[1.28614168, 0.54017322],
       [0.54017322, 0.22687011]]))
('Mulliken populations', array([[1.52963579, 1.11992783],
       [0.64243955, 0.47036421]]))
('Coeffients', array([[ 0.80191698, -0.78226577],
       [ 0.33680121,  1.06844537]]))

Молекулярные орбитали

In [20]:
C = np.matmul(X,Cprime)
P = np.array([[ 1.28614168,0.54017322],
 [ 0.54017322 ,0.22687011]])

x = np.linspace(-8,8,num=1000)
r1 = abs(x+R/2)
r2 = abs(x-R/2)

psi_CGF_STO3G_He = Coeff[2,0]*(2*Expon[2,0]/np.pi)**(0.75)*np.exp(-Expon[2,0]*r1**2) \
                + Coeff[2,1]*(2*Expon[2,1]/np.pi)**(0.75)*np.exp(-Expon[2,1]*r1**2) \
                + Coeff[2,2]*(2*Expon[2,2]/np.pi)**(0.75)*np.exp(-Expon[2,2]*r1**2)
        
psi_CGF_STO3G_H = Coeff[2,0]*(2*Expon[2,0]/np.pi)**(0.75)*np.exp(-Expon[2,0]*r2**2) \
                + Coeff[2,1]*(2*Expon[2,1]/np.pi)**(0.75)*np.exp(-Expon[2,1]*r2**2) \
                + Coeff[2,2]*(2*Expon[2,2]/np.pi)**(0.75)*np.exp(-Expon[2,2]*r2**2)

density = np.zeros(x.shape)
        
density = density + P[0,0]*psi_CGF_STO3G_He*psi_CGF_STO3G_He
density = density + P[1,1]*psi_CGF_STO3G_H*psi_CGF_STO3G_H
density = density + 2*P[0,1]*psi_CGF_STO3G_He*psi_CGF_STO3G_H
In [21]:
plt.figure(figsize=(7,4))
plt.title("Molecular orbitals")

plt.plot(x,C[0,0]*psi_CGF_STO3G_He+C[1,0]*psi_CGF_STO3G_H,label="Bonding")
plt.plot(x,C[0,1]*psi_CGF_STO3G_He+C[1,1]*psi_CGF_STO3G_H,label="Anti-bonding")
plt.legend(loc=4)
Out[21]:
<matplotlib.legend.Legend at 0x7fd1f9547310>
In [22]:
plt.figure(figsize=(7,4))
plt.title("Electron density $\Psi^{2}$")
plt.plot(x,density)
Out[22]:
[<matplotlib.lines.Line2D at 0x7fd1f94ed650>]

Ссылки