AMH: DFT calculations with NWChem

We now re-visit the AMH dimers (both of them) using density functional theory.

We already know from the Ar$_2$ examples we have done that most density functionals are rather poor for van der Waals dimers. The AMH system is not quite a van der Waals system, but it has a strong dispersion contribution, so any density functional we use will need to be corrected for the missing dispersion energy.

Further, according to Griffiths et al., there is a problem with PBE: it overbinds the //P4/nmm// phase due to a charge-transfer problem that manifests in an overbinding at long range.

You will need to test PBE for both of these issues and propose fixes.

You will need to decide on

Scripts

The script on the AMH laboratory page can be modified to do a DFT calculation. I've included an example here.

Here's a script for the molecular dimer. This one does a PBE+D calculation. It's provided only as a template. You will need to decide just which calculation you need.

AMH PBE+D

import argparse, numpy, subprocess, re, sys, os
import numpy, re, sys, subprocess
#import matplotlib.pyplot as plt

# 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("-o", "--output", dest="output", type=str, default="output.txt",
                    help="output text file")
parser.add_argument("-np", "--nproc", dest="nproc", type=int, default=1,
                    help="Number of processors to be used.")
parser.add_argument("-b", "--basis", dest="basis", type=str, default="aug-cc-pvtz",
                    help="basis set used in calculation")

args = parser.parse_args()

nwchem_dir = os.environ["NWCHEM_TOP"]
username = os.environ["USER"]
nproc = args.nproc


# I have commented out the uniform grid:
#positions = numpy.arange(args.minR, args.maxR + 0.00001, args.deltaR)
# Instead, use a non-uniform grid specific to your system:
positions = numpy.array([1.7,2.1,2.3,2.5,2.7,3.1,4.0,5.0,10.0,20.0])

print "Basis set ",args.basis
print "positions = ", positions
print "Output to : ",args.output
print "Number of processors : ",nproc
output = open(args.output,'w')


# Conversion factor:
au2cm = 219474.631371
au2kJ = 2625.499

# Construct input files

input_file_template = """
title "NH3..H2O BSSE corrected DFT interaction energy"

scratch_dir /scratch/HDD_2T/{user}/nwchem

geometry units angstrom "NH3+H2O"
  N       0.00000     0.00000     0.00000
  H1      0.98601     0.00000    -0.30465
  H2     -0.46058     0.84108    -0.38142
  H3     -0.46058    -0.84108    -0.38142
  O       0.00000     0.00000     {RzO}
  H4      0.00000     0.00000     {RzH4}
  H5     -0.96568    -0.00000     {RzH5}
end

geometry units angstrom "NH3+ghost"
  N       0.00000     0.00000     0.00000
  H1      0.98601     0.00000    -0.30465
  H2     -0.46058     0.84108    -0.38142
  H3     -0.46058    -0.84108    -0.38142
  BqO     0.00000     0.00000     {RzO}
  BqH     0.00000     0.00000     {RzH4}
  BqH    -0.96568    -0.00000     {RzH5}
end

geometry units angstrom "ghost+H2O"
  BqN       0.00000     0.00000     0.00000
  BqH       0.98601     0.00000    -0.30465
  BqH      -0.46058     0.84108    -0.38142
  BqH      -0.46058    -0.84108    -0.38142
  O         0.00000     0.00000     {RzO}
  H4        0.00000     0.00000     {RzH4}
  H5       -0.96568    -0.00000     {RzH5}
end

basis
 N   library    {basis}
 H1  library    {basis}
 H2  library    {basis}
 H3  library    {basis}
 O   library    {basis}
 H4  library    {basis}
 H5  library    {basis}
 BqN   library  N  {basis}
 BqH   library  H  {basis}
 BqO   library  O  {basis}
end

dft 
  xc xpbe96 cpbe96
  iterations 200
  disp
  direct
end

set geometry "NH3+H2O"
charge 0
task dft
unset geometry "NH3+H2O"

dft; vectors hcore; end

set geometry "NH3+ghost"
charge 0
task dft
unset geometry "NH3+ghost"

dft; vectors hcore; end

set geometry "ghost+H2O"
charge 0
task dft
unset geometry "ghost+H2O"

"""

# z coordinates for O, H4 and H5 : H2O will be translated along the z-axis
# Units: Angstrom
zO  =  0.0
zH4 = -1.03900 
zH5 =  0.27836

# Regular expressions to find the right lines in the file

dft_re = re.compile("Total DFT energy =\s+(-?[0-9]+.[0-9]+)")

# Dictionary to store the energies we find

energies = {}

# Main loop through different position values

for delta_z in positions:
    print "Current position = ",delta_z
    input_file_name = "in-R{0:5.3f}.nw".format(delta_z)
    output_file_name = "out-R{0:5.3f}.out".format(delta_z)

    # Write input file
    Rz_O  = zO + delta_z  
    Rz_H4 = zH4 + delta_z
    Rz_H5 = zH5 + delta_z
    RzO  = """{0:10.6f}""".format(Rz_O)
    RzH4 = """{0:10.6f}""".format(Rz_H4)
    RzH5 = """{0:10.6f}""".format(Rz_H5)
    input_file = open(input_file_name, "w")
    input_file.write(input_file_template.format(RzO=RzO,RzH4=RzH4,RzH5=RzH5,user=username,basis=args.basis))
    input_file.close()

    # Run NWChem

    output_file = open(output_file_name, "w")
    subprocess.call(["mpirun.mpich","-np",str(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")
    dft_energies = []
    for line in output_file:
        dft_match = dft_re.search(line)
        if dft_match:
            dft_energies.append(float(dft_match.group(1)))
    output_file.close()

    if not (len(dft_energies) == 3):
        print "Apparent error in output file! {0:d} DFT energies found.".format(len(dft_energies))
        sys.exit(1)

    energies[delta_z] = dft_energies
    dft_dimer    = energies[delta_z][0]
    dft_monomerA = energies[delta_z][1]
    dft_monomerB = energies[delta_z][2]
    dft_interaction = dft_dimer - dft_monomerA - dft_monomerB
    print "Eint[DFT] = ",dft_interaction*au2kJ

# prepare plots

dft_dimer = numpy.array([energies[p][0] for p in positions])
dft_monomerA = numpy.array([energies[p][1] for p in positions])
dft_monomerB = numpy.array([energies[p][2] for p in positions])

dft_interaction = dft_dimer - dft_monomerA - dft_monomerB

# convert to kJ/mol
dft_interaction = au2kJ*dft_interaction

output.write(('%10s '*5 + '\n') % 
             ("   R    ","DFT monoA","DFT monoB","DFT dimr","DFT int "))

for i in range(len(positions)):
    output.write(('%10.6f '*5 + '\n') %
    (positions[i],
    dft_monomerA[i], dft_monomerB[i], dft_dimer[i], dft_interaction[i]))

output.close()

Important

Start with the calculations used in by Griffiths et al.

  • PBE
  • PBE0
  • PBE+D
  • PBE0+D

Then consider using the LC functionals you used on Ar$_2$. Do these improve on PBE0+D?

Remember that you need to do this for both dimers.

AJMPublic/teaching/electronic-structure/practical5 (last edited 2021-04-14 13:56:23 by apw109)