Atom and Bond Traversal

OEChem molecules contain atoms and bonds which have APIs described by the OEAtomBase and OEBondBase abstract base-classes respectively. Atoms and bonds in OEChem can only be created and destroyed in the context of an OEChem molecule. While they can be accessed as pointers through various member functions of molecules, their memory is owned by the molecules and they are deallocated during the molecules’ destruction. Attempting to use references to atoms or bonds of a molecule after the molecule has gone out of scope results in undefined behavior.

Iterators

The standard way of processing each item or member of a set or collection in OEChem is by the use of an iterator. The use of iterators is a common abstraction (or design pattern) in object oriented programming because it hides the way the collection/container is implemented from the user. Hence a set of atoms could be implemented internally as an array, a linked list, a hash table, or any similar data structure, but its behavior to the programmer is independent of the actual implementation. An iterator can be thought of as a current position indicator.

OEChem makes extensive effort to support the Java iteration syntax.

for (MyType obj : list)
  obj.DoSomething();

Atom and Bond Iteration

Listing 1 shows the minimal use of OEChem‘s iterators. These examples use the OEMolBase methods GetAtoms and GetBonds, which return iterators over the atoms and bonds of a molecule, respectively.

Listing 1: Using iterators to loop over atoms and bonds

package openeye.docexamples.oechem;

import openeye.oechem.*;

public class LoopingOverMolAtomsBonds {
    public static void main(String argv[]) {
        OEGraphMol mol = new OEGraphMol();
        oechem.OESmilesToMol(mol, "c1cocc1");

        System.out.println("atoms");
        for (OEAtomBase atom : mol.GetAtoms()) {
            System.out.println(atom.GetAtomicNum());
        }

        System.out.println("bonds");
        for (OEBondBase bond : mol.GetBonds()) {
            System.out.println(bond.GetOrder());
        }
    }
}

Note

Listing 1 introduced the GetAtomicNum and GetOrder methods. These and other OEAtomBase and OEBondBase methods will be covered in more detail in chapters Atom Properties and Bond Properties, respectively.

Bonds of an Atom Iteration

The exact same idiom is used for iterating over the bonds attached to an atom. The GetBonds method returns an iterator over the bonds connected to that atom. Listing 2 shows how to use this iterator to determine the explicit degree of an atom, i.e. the number of bonds to it, not including bonds to implicit hydrogen atoms.

Listing 2: Looping over the bonds of an atom

package openeye.docexamples.oechem;

import openeye.oechem.*;

public class LoopingOverBonds {
    private static int MyGetExplicitDegree(OEAtomBase atom) {
        int result=0;

        for (OEBondBase bond : atom.GetBonds()) {
            result++;
        }

        return result;
    }

    public static void main(String argv[]) {
        OEGraphMol mol = new OEGraphMol();
        oechem.OESmilesToMol(mol, "c1cocc1Br");

        for (OEAtomBase atom : mol.GetAtoms()) {
            System.out.println("Atom " + atom.GetIdx() + " has degree " + MyGetExplicitDegree(atom));
        }
    }
}

Atom Neighbor Iteration

Often it is not the bonds around the atoms that you wish to loop over, but the neighboring atoms. One way to do this would be to use the GetBonds method described in the previous section and use the GetNbr method on each OEBondBase to get the atom across the bond from the input atom.

Listing 3: Finding the neighbors of an atom (version 1)

package openeye.docexamples.oechem;

import openeye.oechem.*;

public class LoopingOverNeighbors1 {
    public static void main(String argv[]) {
        OEGraphMol mol = new OEGraphMol();
        oechem.OESmilesToMol(mol, "c1cocc1Br");

        for (OEAtomBase atom : mol.GetAtoms()) {

            System.out.print("Atom: " + atom.GetIdx() + " Neighbors:");

            for (OEBondBase bond : atom.GetBonds())
                System.out.print(" " + bond.GetNbr(atom).GetIdx());

            System.out.println();
        }
    }
}

However this can be done even more conveniently using the GetAtoms method of an OEAtomBase directly, which allows loops over the neighbor atoms.

Listing 4: Finding the neighbors of an atom (version 2)

package openeye.docexamples.oechem;

import openeye.oechem.*;

public class LoopingOverNeighbors2 {
    public static void main(String argv[]) {
        OEGraphMol mol = new OEGraphMol();
        oechem.OESmilesToMol(mol, "c1cocc1Br");

        for (OEAtomBase atom : mol.GetAtoms()) {

            System.out.print("Atom: " + atom.GetIdx() + " Neighbors:");

            for (OEAtomBase nbr : atom.GetAtoms())
                System.out.print(" " + nbr.GetIdx());

            System.out.println();
        }
    }
}

Atom or Bond Subset Iteration

It can sometimes be useful to loop over a subset of the atoms or bonds of a molecule. Traditionally, this is done with if statements inside a loop, but it can sometimes be cleaner and more convenient to subset the members being looped over inside the iterator itself. To do this, many of OEChem‘s iterator generation functions (such as GetAtoms) can take an argument which determines which subset of the object to loop over (these functions are called functors are detailed in the chapter Predicates Functors). The details of these functions are not important here. Instead, a programmer can simply use the predefined functors to control their loops. Listing 5 shows the use of the predicate OEHasAtomicNum to loop over only carbon atoms in a molecule.

Listing 5: Looping over carbon atoms only

package openeye.docexamples.oechem;

import openeye.oechem.*;

public class LoopingOverCarbon {
    public static void main(String argv[]) {
        OEGraphMol mol = new OEGraphMol();
        oechem.OESmilesToMol(mol, "c1c(Br)occ1CCC");

        System.out.print("Carbon atoms:");
        for (OEAtomBase atom : mol.GetAtoms(new OEHasAtomicNum(OEElemNo.C)))
            System.out.print(" " + atom.GetIdx());

        System.out.println();
    }
}

See also

For a complete list of built-in predicates, see Built-in Functors section.

Iterator Methods

Iterators offer a much wider range of iteration possibilities. For example, the iterator can be reused by using the ToFirst method. Or, the order of iteration can be rearranged with the Sort method.

The following table describes how to use the same rich set of iterator operations that C++ offers.

Description C+ + Java
Increment ++i i.Increment()
Increment by n i += n i.Increment(n)
Decrement --i i.Decrement()
Decrement by n i -= n i.Decrement(n)
Go to first i.ToFirst() i.ToFirst()
Go to last i.ToLast() i.ToLast()
Current Access operator-> i.Target()
Validity operator bool i.IsValid()

Listing 6 shows how to use an OEAtomBase iterator to loop over the atoms in a molecule in reverse order and print their atomic numbers.

Note

The order of the atoms returned by OEMolBase.GetAtoms can be controlled by OEMolBase.OrderAtoms.

Listing 6: Looping over atoms in reverse order

package openeye.docexamples.oechem;

import openeye.oechem.*;

public class ReverseIteration {
    public static void main(String argv[]) {
        OEGraphMol mol = new OEGraphMol();    
        oechem.OESmilesToMol(mol, "n1ccccc1"); 

        OEAtomBaseIter aitr = mol.GetAtoms();
        for (aitr.ToLast(); aitr.IsValid(); aitr.Decrement())
            System.out.println(aitr.Target().GetAtomicNum());
    }
}