NBO with ADF

NBO 6.0 is a tool for Natural Bond order analysis that uses the results of an ADF calculation. More information about NBO can be found in the corresponding section of the ADF manual.

NBO analysis is performed based on a prior ADF calculation (with some special keywords) by using two separate binary executables: adfnbo and gennbo6. In this case no special job type is created for these binaries. Instead of that we extend the AMSJob class in such a way that calls of adfnbo and gennbo6 are appended to the usual ADF runscript. We also make sure that all the required ADF keywords are present in the initial ADF input file. Input keywords for adfnbo are taken from myjob.settings.adfnbo. All this work happens in prerun(). No specialized Results subclass is defined for ADFNBOJob.

The source code of the whole module:

import os

from scm.plams.core.functions import log
from scm.plams.interfaces.adfsuite.ams import AMSJob

__all__ = ["ADFNBOJob"]


class ADFNBOJob(AMSJob):

    def prerun(self):  # noqa F811
        s = self.settings.input.ADF
        s.fullfock = True
        s.aomat2file = True
        s.symmetry = "NoSym"
        s.basis.core = "None"
        if "save" in s:
            if isinstance(s.save, str):
                s.save += " TAPE15"
            elif isinstance(s.save, list):
                s.save.append("TAPE15")
            else:
                log(
                    "WARNING: 'SAVE TAPE15' could not be added to the input settings of {}. Make sure (thisjob).settings.input.save is a string or a list.".format(
                        self.name
                    ),
                    1,
                )
        else:
            s.save = "TAPE15"

        if isinstance(self.settings.adfnbo, list):
            adfnbo_input = self.settings.adfnbo
        else:
            adfnbo_input = ["write", "spherical", "fock"]
            log(
                "WARNING: (thisjob).settings.adfnbo should be a list. Using default settings: write, fock, spherical", 1
            )

        self.settings.runscript.post = (
            'cp "' + os.path.join(self.path, "adf.rkf") + '" TAPE21\n'
            "$AMSBIN/adfnbo <<eor\n" + "\n".join(adfnbo_input) + "\neor\n\n$AMSBIN/gennbo6 FILE47\n"
        )

To follow along, either

Worked Example

Initialization

from scm.plams import Settings, Molecule, init, AMSJob
from scm.plams.recipes.adfnbo import ADFNBOJob

# this line is not required in AMS2025+
init()
PLAMS working folder: /path/plams/ADFNBO/plams_workdir.002

Define molecule

# mol = Molecule("methane.xyz")
def get_molecule(input_string):
    job = AMSJob.from_input(input_string)
    return job.molecule[""]


mol = get_molecule(
    """
System
    Atoms
         C      0.000000      0.000000      0.000000
         H      0.631600      0.631600      0.631600
         H      0.631600     -0.631600     -0.631600
         H     -0.631600      0.631600     -0.631600
         H     -0.631600     -0.631600      0.631600
    End
End
"""
)

Create and run job

s = Settings()
s.input.AMS.Task = "SinglePoint"
s.input.ADF.basis.type = "DZP"
s.input.ADF.xc.lda = "SCF VWN"
s.input.ADF.relativity.level = "scalar"
s.adfnbo = ["write", "spherical", "fock"]

j = ADFNBOJob(molecule=mol, settings=s)
r = j.run()
[05.03|14:48:57] JOB plamsjob STARTED
[05.03|14:48:57] JOB plamsjob RUNNING
[05.03|14:48:59] JOB plamsjob FINISHED
[05.03|14:48:59] JOB plamsjob SUCCESSFUL

Complete Python code

#!/usr/bin/env amspython
# coding: utf-8

# ## Initialization

from scm.plams import Settings, Molecule, init, AMSJob
from scm.plams.recipes.adfnbo import ADFNBOJob

# this line is not required in AMS2025+
init()


# ## Define molecule


# mol = Molecule("methane.xyz")
def get_molecule(input_string):
    job = AMSJob.from_input(input_string)
    return job.molecule[""]


mol = get_molecule(
    """
System
    Atoms
         C      0.000000      0.000000      0.000000
         H      0.631600      0.631600      0.631600
         H      0.631600     -0.631600     -0.631600
         H     -0.631600      0.631600     -0.631600
         H     -0.631600     -0.631600      0.631600
    End
End
"""
)


# ## Create and run job

s = Settings()
s.input.AMS.Task = "SinglePoint"
s.input.ADF.basis.type = "DZP"
s.input.ADF.xc.lda = "SCF VWN"
s.input.ADF.relativity.level = "scalar"
s.adfnbo = ["write", "spherical", "fock"]

j = ADFNBOJob(molecule=mol, settings=s)
r = j.run()


# ## Print results

lines = r.get_output_chunk(begin="NATURAL BOND ORBITALS (Summary):", end="Charge unit", inc_begin=True, inc_end=True)
for line in lines:
    print(line)