General AuToGraFS Scripting concepts

AuToGraFS relies on the concept of underlying nets common to many structures. These nets, or topologies can be found in the RCSR and EPINET databases. Once implemented, AuToGraFS will follow these blueprints to generate a framework from building units of compatible geometries. Since these geometries are ideal objects, and most chemical structures are distorted, an optimization step using UFF is necessary for the building units to “snap into place”. Correct handling of bonding information and UFF atom types are crucial to the generation of correct structures.

Components of AuToGraFS

The Fragment class

This is a slightly modified version of the native ASE Atoms object, designed to hold essential data for UFF postprocessing. This englobes the uff atom types, and the bonding information.

from scm.autografs.fragment import Fragment
from ase import Atoms

line = Fragment(ase.Atoms("X2", positions=...),
                mmtypes=["H_", "H_"],
                bonds = [[0,0,1],[0,0,1],[1,1,0]],
                shape="linear",
                unit=None,
                name="a_line_has_no_name")

Where mmtypes is a list of the UFF atom types symbols, ordered as the corresponding atoms in the structure, and bonds is a symmetric numpy array of size equal to the number of atoms. The i,j elements in the bonds array are the bond order between atom i and atom j of the structure. These bond orders should follow the UFF conventions (1.5 for aromatic, 0.5 for coordination bonds, 0.001 for hydrogen bonds…). The shape holds information for AuToGraFS about the connectivity of the fragment. Each dummy atom, with symbol “X” in ASE, represents a point of connection between two fragments. In the above example, the shape is clearly “linear”. This is used to check compatibility between a fragment and a given topology. The unit of a fragment is set internally by AuToGraFS and is of no interest here. The name of a fragment is used only for logging purposes. Individual building blocks of a framework are manipulated through the Fragment class. It is possible to export a fragment from the GUI using edit -> framework -> export fragment. If the UFF atom types are not specified, an automatic typing script will take care of it.

The Model class

This is a container class that holds in one place the topology of a framework and the correctly positioned fragments. Most of the postprocessing methods found in AuToGraFS are Model methods.

from scm.autografs import *

model = autografs.Model(...)

# view the framework in adfinput. set clean to False to keep dummy atoms.
model.view(clean=True, verbose=True)

# write the name.ams and name.run files
model.write(name="framework", clean=True, verbose=True)

# returns a Fragment, with correct bonding information and mmtypes
# if indices is True, it also returns the index of the corresponding fragment in the model for each atom
atoms = model.get_atoms(self, clean=False, indices=False)

# returns a supercell of the framework as a Fragment
supercell = model * (2,2,2)

# deletes a fragment in the framework. if no index is given, the fragment will be chosen at random.
# the defects will cap with hydrogen the empty spaces. returns a Fragment
# here, a linker at random will be deleted
model.insert_defect(indices=None, centers=0, linkers=1)

# flip a linear object
model.flip_fragment(index=the_fragment_index)

# rotates a linear object around its axis by angle in degrees
model.rotate_fragment(index=the_fragment_index, angle=85.0)

# Will add a functional group to the selected fragment on a random hydrogen
# if and only if the hydogen is connected to a carbon.
# for more precise handling, use the GUI for now.
model.functionalize_fragment(functional_group="NH2", index=the_fragment_index)

The Autografs class

This class is the builder in itself. Given a path to a database of building units in .inp format (exportable via the GUI), it will generate any valid framework from a topology name, a center and linker name or objects, and optionally a pillar name or object. The linkers, centers and pillars objects have to be passed as Fragment .

from scm.autografs import *
from random import choice

# create the generator and update the database of building units
mofgen = autografs.Autografs(refresh_database=True, verbose=True, path="path/to/my/fragments/")

# choose a topology from the available one given no center or linkers
topologies = mofgen.get_available_topologies(center=None, linker=None)
topology = choice(topologies)

# choose a center in the list of available centers for the topology
centers = mofgen.get_available_centers(topology=topology, linker=None)
center = choice(centers)

# choose a linker in the list of available linkers for the topology and center
linkers = mofgen.get_available_linkers(topology=topology,  center=center)
linker = choice(linkers)

# modify the linker by changing all hydrogens to fluorine
linker_structure = mofgen.database[linker]
linker_symbols = linker_structure.get_chemical_symbols()
linker_structure.set_chemical_symbols(["F" if l=="H" else l for l in linker_symbols])

# generate the framework using multiprocessing tools and automatic scaling of unit cell
framework = mofgen.make(lable="my_framework", topology=topology, center=center, linker=linker, pillar=None, form_factor=None, parallel=True)

# visualize in adfinput
framework.view()

do not forget to run a quick UFF optimization after generation to optimize lattice and fragment into an optimal configuration.

About the databases of building units

AuToGraFS uses both a binary database format, and structures in the DemonNano file format from which the binary is compiled. To ask AuToGraFS to use a custom directory containing .inp files, simply pass the directory to the path argument when initializing the constructor. The “.inp” file format should be formatted like the following example.

Data: SBUtype = linker
Data: shape = linear
Data: name = benzene
GEOMETRY CARTESIAN
C       0.00000000     -1.39103633      0.00000000     MMTYPE=C_R   QMMM=MM BOND=2/1.5:6/1.5:12/1.0
C      -1.20606758     -0.69440959      0.00000000     MMTYPE=C_R   QMMM=MM BOND=1/1.5:3/1.5:8/1.0
C      -1.20606758      0.69440959     -0.00000000     MMTYPE=C_R   QMMM=MM BOND=2/1.5:4/1.5:10/1.0
C      -0.00000000      1.39103633      0.00000000     MMTYPE=C_R   QMMM=MM BOND=3/1.5:5/1.5:11/1.0
C       1.20606758      0.69440959      0.00000000     MMTYPE=C_R   QMMM=MM BOND=4/1.5:6/1.5:7/1.0
C       1.20606758     -0.69440959     -0.00000000     MMTYPE=C_R   QMMM=MM BOND=1/1.5:5/1.5:9/1.0
H       2.02806392      1.16933783      0.00000000     MMTYPE=H_    QMMM=MM BOND=5/1.0
H      -2.02806392     -1.16933783      0.00000000     MMTYPE=H_    QMMM=MM BOND=2/1.0
H       2.02806392     -1.16933783      0.00000000     MMTYPE=H_    QMMM=MM BOND=6/1.0
H      -2.02806392      1.16933783      0.00000000     MMTYPE=H_    QMMM=MM BOND=3/1.0
X       0.00000000      2.09099413      0.00000000     MMTYPE=H_    QMMM=MM BOND=4/1.0
X       0.00000000     -2.09099413      0.00000000     MMTYPE=H_    QMMM=MM BOND=1/1.0
END

Using the overhauled Atom Typer

Both AuToGraFS and the GUI now use a new python library for the assignment of UFF types to atoms and generation of the bonding matrix. The process goes as follows:

from scm.autografs import atomtyper

# instantiate the typer on a readable molecule file (e.g: "mol.cif")
typer = atomtyper.MolTyper("path/of/fileToType")

# choose the UFF library. options are uff and uff4mof
library = read_db("uff4mof")

# actual typing is done here
typer.type_mol(library)

# prints the bond matrix as a numpy array
# item i, j of matrix is the bond order between atoms i and j
print typer.get_guibonds()

# print a list of UFF types in order
print typer.get_mmtypes()

# write a SCM-UFF input file with correct info
typer.structure.write(name="mytypedmol")