Interaction energies

The interaction energy is often more important than the energies of the individual components. For example, we don't really care much what the energies of each of the atoms in a water molecule are, as long as they are more strongly bound in the molecule than in isolation. The difference in energies of the bound system from that of the unbound system is the interaction energy. In a chemical bond this energy can be quite large, but in a van der Waals system, it will be small. For more on this topic see my notes on Intermolecular Interaction energies.

Goals of this exercise

We aim to compute the potential energy surface (PES) for the Ar$_2$ system. This is a weakly bound van der Waals system with a rather simple, 1-dimensional PES. That is good as this makes it relatively simple to calculate and subsequently model using an analytical function. Once we have this function, we will, in principle, be able to input it into a simulation code like DL_POLY to perform simulations of the bulk.

But before we can compute this PES, we need to know which methods to use, and how to get accurate interaction energies, so that our PES is accurate. In this exercise, we will aim to understand the Hartree--Fock (HF) and second-order Moller--Plesset (MP2, also called MBPT2) methods. We will also aim to understand something called the basis-set superposition error (BSSE) and how it can be corrected. At the end of this exercise we will have (or should have) reference energies for this dimer.

The Argon dimer

For two Ar atoms separated by distance R this takes the simple form \begin{equation} E_{\rm int}(R) = E[\mbox{Ar}\cdots\mbox{Ar}](R) - 2*E[\mbox{Ar}] \end{equation} where $E[\mbox{Ar}\cdots\mbox{Ar}](R)$ is the energy of the complex of two Ar atoms separated by distance $R$. And $E[\mbox{Ar}]$ is the energy of a single argon atom. So this looks easy to compute.

An example input file to do this calculation is:

title "Ar dimer no BSSE correction : MP2 interaction energy"

scratch_dir /home/{your user name here}/scratch/

geometry units angstrom "Ar+Ar"
  Ar1 0 0 0
  Ar2 0 0 3.76
end

geometry units angstorm "Ar"
  Ar1 0 0 0
end

basis
  Ar1 library aug-cc-pvtz
  Ar2 library aug-cc-pvtz
end

set geometry "Ar+Ar"
task mp2
unset geometry "Ar+Ar"

set geometry "Ar"
task mp2
unset geometry "Ar"

Save this in file Ar2-noCPcorr-atz.nw. The noCPcorr stands for no counterpoise (CP) correction - I will explain what this means soon.

More about the above file:

Warning

First of all, we are now performing significant calculations so we cannot use the home directory to run our jobs. Instead we use a scratch directory. This is done by defining scratch_dir in the input file. Please change this directory path to include your own user name!!!

I have asked our administrators to create scratch directories for each of you. to check to see if you have one type:

Is your user name listed there? If so, type:

  $ mkdir /scratch/HDD_2T/esm/${USER}/nwchem

This should create a directory for your NWChem scratch files. If it fails that means we have a problem. Let me know.

From now on we will NOT run any job on /home/${USER}!!! }}}

OK, now to the other parts of this file:

Tip

For more on the set and unset command see the NWChem Wiki SET/UNSET

Run the job using

  $ mpirun.mpich -np 1 nwchem Ar2-noCPcorr-atz.nw >& Ar2-noCPcorr-atz.out

Run it to get the energies (in a.u. unless specified):

Basis: aug-cc-pVTZ
Method    E(Ar...Ar)             E(Ar)           Eint (cm-1)
=====
HF       -1053.627091087923  -526.813754874704     +91.88
MP2      -1054.148750447087  -527.074074460254    -132.02
=====

Here I have used the separation of R = 3.76 Angstrom Reference $E_{\rm int}$ at a separation of $R=3.75$ Angstrom is: -99.47 cm-1 (Ref: Patkowski, Murdachaew, Fou, and Szalewicz, Mol. Phys., 103, 10-20 (2005) Have a look at this paper as it contains a lot more data.)

Tip

Conversion factor: 1 Hartree = 219474.631 cm-1

Points to note:

Important

Repeat the calculation using the aug-cc-pVDZ basis. This time you should get

  • $E_{\rm int}({\rm HF}) = 82.575$ cm$^{-1}$, and

  • $E_{\rm int}({\rm MP2}) = -103.24$ cm$^{-1}$

Amazing! MP2 is now in better agreement with the reference energy. But how can this be when we have reduced the basis size?

Let's try and understand this. Make a calculation of $E_{\rm int}$ at the HF and MP2 levels of theory using a range of basis sets:

Important

Plot these energies (both the HF and MP2) as a function of basis set. Use the basis size on the $x$-axis - you will find the size printed in the NWChem output.

Q: Is there a pattern that will allow you to make a reasonable guess at the complete basis limit?

Put your data in a table of the form. Your interaction energies (in cm$^{-1}$) go in the columns labeled 'NoCP' = no counterpoise correction. We will fill in the CP columns next.

Basis set

Basis size

RHF

MP2

RHF

MP2

NoCP

NoCP

CP

CP

cc-pVDZ

cc-pVTZ

aug-cc-pVDZ

aug-cc-pVTZ

Basis Set Superposition Error (BSSE)

There is a problem with these calculations. It's called the Basis set superposition error (BSSE). Here's how it goes:

BSSE: We always use finite basis sets for our calculations of energies. These are incomplete and introduce an inconsistency in the calculations of the energies of the dimer (Ar...Ar) and monomer (Ar). In the former the effective basis space is larger than the latter. If the energy method was variational, this would mean that E(Ar) would not be as low as it should be compared with E(Ar...Ar). The resulting error in Eint is called the basis set superposition error.

Resolution of the problem: Boys and Bernardi proposed using the dimer basis for both calculations. We calculate E(Ar...Ar) as before. But calculate E(Ar) using additionally the basis of the partner. This method of correcting the BSSE is called the counterpoise of CP correction.

In file Ar2/Ar2-CPcorr-atz.nw is an example of how the CP correction can be applied:

title "Ar dimer BSSE corrected MP2 interaction energy" 

scratch_dir /home/{your user name here}/scratch/

geometry units angstrom "Ar+Ar" 
  Ar1 0 0 0 
  Ar2 0 0 3.76
end

geometry units angstrom "Ar+ghost" 
  Ar1 0 0 0 
  Bq2 0 0 3.76
end

basis 
  Ar1 library aug-cc-pvtz 
  Ar2 library aug-cc-pvtz 
  Bq2 library Ar aug-cc-pvtz 
end

set geometry "Ar+Ar"
task mp2
unset geometry "Ar+Ar"

scf; vectors atomic; end

set geometry "Ar+ghost"
task mp2
unset geometry "Ar+ghost"

Let's now repeat the above set of calculations for $E_{\rm int}$ with the CP correction. Use all 6 basis sets specified above.

Important

Include these new energies on your plot of interaction energy versus basis size.

Q: Can you see a pattern this time? Is it clearer or less clear than last time?

Put your data in the table we began in the previous step.

Python code to perform an Ar$_2$ scan

If you wish to calculate the interaction energy for a set of argon separations, the above procedure will be tedious. Instead we can use a script like the one given here:

batch_Ar2_HF_MP2.py

 
import argparse, numpy, subprocess, re, sys
import os
 
# Parse command-line arguments
 
parser = argparse.ArgumentParser(description = """
Create a set of NWChem input files for the Ar2 dimer, run NWChem on them, and
parse the output for interaction energy.
""")
parser.add_argument("-m", "--min", dest="minR", type=float, default=1.,
                    help="minimum Ar separation in A")
parser.add_argument("-M", "--max", dest="maxR", type=float, default=5.,
                    help="maximum Ar separation in A")
parser.add_argument("-d", "--delta", dest="deltaR", type=float, default=0.5,
                    help="step size for Ar separation in A")
parser.add_argument("-b", "--basis", dest="basis", type=str, default="aug-cc-pvtz",
                    help="basis set used in calculation")
parser.add_argument("-o", "--output", dest="output", type=str, default="output.txt",
                    help="output text file")
 
args = parser.parse_args()
 
nwchem_dir = os.environ["NWCHEM_INSTALL"]
username = os.environ["USER"]
scratch = os.environ["SCRATCH"]
nproc = "2"
 
positions = numpy.arange(args.minR, args.maxR + 0.00001, args.deltaR)
print "Basis set ",args.basis
print "positions = ", positions
print "Output to : ",args.output
output = open(args.output,'w')
 
 
# Conversion factor:
au2cm = 219474.631371
 
# Construct input files
 
input_file_template = """
title "Ar dimer BSSE corrected MP2 interaction energy"
 
scratch_dir {scratch}
 
geometry units angstrom "Ar+Ar" noautoz noautosym
 Ar1 0 0 0
 Ar2 0 0 {R}
end
 
geometry units angstrom "Ar+ghost" noautoz noautosym
 Ar1 0 0 0
 Bq2 0 0 {R}
end
 
basis
 Ar1 library    {basis}
 Ar2 library    {basis}
 Bq2 library Ar {basis}
end
 
set geometry "Ar+Ar"
task mp2
unset geometry "Ar+Ar"
 
scf; vectors atomic; end
 
set geometry "Ar+ghost"
task mp2
unset geometry "Ar+ghost"
"""
 
# Regular expressions to find the right lines in the file
 
scf_re = re.compile("SCF energy\s+(-?[0-9]+.[0-9]+)")
mp2_re = re.compile("Total MP2 energy\s+(-?[0-9]+.[0-9]+)")
 
# Dictionary to store the energies we find
 
energies = {}
 
# Main loop through different position values
 
for ar_position in positions:
    print "Current position = ",ar_position
    input_file_name = "in-R{0:5.3f}.nw".format(ar_position)
    output_file_name = "out-R{0:5.3f}.out".format(ar_position)
 
    # Write input file
    with open(input_file_name,"w") as INP:
        INP.write(input_file_template.format(R=ar_position, basis=args.basis,
                    user=username, scratch=scratch))
 
    # Run NWChem
    output_file = open(output_file_name, "w")
    subprocess.call(["mpirun","-np",nproc,"nwchem", input_file_name],
                                                stdout=output_file, stderr=subprocess.STDOUT)
    output_file.close()
 
    # Read output file back in
 
    output_file = open(output_file_name, "r")
    scf_energies = []
    mp2_energies = []
    for line in output_file:
        scf_match = scf_re.search(line)
        if scf_match:
            scf_energies.append(float(scf_match.group(1)))
        else:
            mp2_match = mp2_re.search(line)
            if mp2_match:
                mp2_energies.append(float(mp2_match.group(1)))
    output_file.close()
 
    if not (len(scf_energies) == 2 and len(mp2_energies) == 2):
        print "Apparent error in output file! {:d} SCF energies and {:d} MP2 energies found.".format(
            len(scf_energies), len(mp2_energies))
        sys.exit(1)
 
    energies[ar_position] = scf_energies + mp2_energies
    scf_dimer   = energies[ar_position][0]
    scf_monomer = energies[ar_position][1]
    mp2_dimer   = energies[ar_position][2]
    mp2_monomer = energies[ar_position][3]
    scf_interaction = scf_dimer - 2*scf_monomer
    mp2_interaction = mp2_dimer - 2*mp2_monomer
    print "Eint[SCF] = ",scf_interaction*au2cm
    print "Eint[MP2] = ",mp2_interaction*au2cm
 
# prepare plots
 
scf_dimer = numpy.array([energies[p][0] for p in positions])
scf_monomer = numpy.array([energies[p][1] for p in positions])
mp2_dimer = numpy.array([energies[p][2] for p in positions])
mp2_monomer = numpy.array([energies[p][3] for p in positions])
 
scf_interaction = scf_dimer - 2*scf_monomer
mp2_interaction = mp2_dimer - 2*mp2_monomer
 
# convert to cm-1
scf_interaction = au2cm*scf_interaction
mp2_interaction = au2cm*mp2_interaction
 
output.write(('%10s '*7 + '\n') % 
             ("   R    ","SCF mono","SCF dimr",
              "SCF int ","MP2 mono","MP2 dimr","MP2 int "))
 
for i in range(len(positions)):
    output.write(('%10.6f '*7 + '\n') %
    (positions[i],
    scf_monomer[i], scf_dimer[i], scf_interaction[i],
    mp2_monomer[i], mp2_dimer[i], mp2_interaction[i]))
 
output.close()

Save the above commands in a file called batch_Ar2_HF_MP2.py and make it executable using

  $ chmod +x batch_Ar2_HF_MP2.py

To see the usage use:

  $ ./batch_Ar2_HF_MP2 --help  

you should get:

usage: batch_Ar2_HF_MP2.py [-h] [-m MINR] [-M MAXR] [-d DELTAR] [-b BASIS]
                           [-o OUTPUT]

Create a set of NWChem input files for the Ar2 dimer, run NWChem on them, and
parse the output for interaction energy.

optional arguments:
  -h, --help            show this help message and exit
  -m MINR, --min MINR   minimum Ar separation in A
  -M MAXR, --max MAXR   maximum Ar separation in A
  -d DELTAR, --delta DELTAR
                        step size for Ar separation in A
  -b BASIS, --basis BASIS
                        basis set used in calculation
  -o OUTPUT, --output OUTPUT
                        output text file

Now you should be able to run it using, for example,

  $ ./batch_Ar2_HF_MP2.py -m 3.0 -M 6.0 -d 0.5 -b aug-cc-pVDZ -o Ar2_adz_hf_mp2_scan.txt

This will calculate interaction energies at the HF and MP2 levels of theory for the argon dimer at separations 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.5, 5.0, 5.5 and 6.0 Angstrom and save the results in file Ar2_adz_hf_mp2_scan.txt.

Important

Tasks:

  • Calculate the interaction energies using the aug-cc-pVDZ, aug-cc-pVTZ, and aug-cc-pVQZ basis sets.
  • Plot these using Gnuplot. Make sure you include the data points and use smooth csplines to draw a smooth line between the data points.

  • What pattern do you see?
  • Include data from the paper linked above. These data will be your reference values.
  • How close are your results to these reference values?

Important

Sometimes it is good to do a calculation another way to see what happens:

  • Modify the python code to calculate the interaction energies without the CP correction.
  • Calculate the interaction energies without this correction and plot them as above.
  • What do you conclude?

AJMPublic/teaching/electronic-structure/argon2-pes-hf-mp2 (last edited 2021-04-14 13:52:06 by apw109)