Indexing Bonds

Bonds represent the chemical bonds between atoms in a molecule. A Bond is a molecular container that contains the two atoms that make up the bond.

For example, let’s look at the aladip system again.

>>> import sire as sr
>>> mols = sr.load(sr.expand(sr.tutorial_url, ["ala.top", "ala.crd"]))
>>> mol = mols[0]
>>> print(mol)
Molecule( ACE:2   num_atoms=22 num_residues=3 )

We can get all of the bonds using the bonds() function.

>>> print(mol.bonds())
SelectorBond( size=21
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => C:5 )
2: Bond( CH3:2 => HH32:3 )
3: Bond( CH3:2 => HH33:4 )
4: Bond( C:5 => O:6 )
...
16: Bond( N:17 => H:18 )
17: Bond( N:17 => CH3:19 )
18: Bond( CH3:19 => HH32:21 )
19: Bond( CH3:19 => HH33:22 )
20: Bond( CH3:19 => HH31:20 )
)

The result (a SelectorBond) is a molecular container for bonds. Like all molecular containers, it can be indexed,

>>> print(mol.bonds()[0])
Bond( HH31:1 => CH3:2 )

sliced

>>> print(mol.bonds()[0:5])
SelectorBond( size=5
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => C:5 )
2: Bond( CH3:2 => HH32:3 )
3: Bond( CH3:2 => HH33:4 )
4: Bond( C:5 => O:6 )
)

or accessed by a list of indicies

>>> print(mol.bonds()[ [0, 2, 4, 6, 8] ])
SelectorBond( size=5
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => C:5 )
2: Bond( C:5 => O:6 )
3: Bond( N:7 => H:8 )
4: Bond( CA:9 => HA:10 )
)

The Bond object is also a molecular container, so can be indexed, searched and sliced just like any other container.

>>> bond = mol.bonds()[0]
>>> print(bond[0])
Atom( HH31:1  [  18.45,    3.49,   12.44] )
>>> print(bond[1])
Atom( CH3:2   [  18.98,    3.45,   13.39] )

Accessing bonds by atom

You can also find bonds by looking for their constituent atoms. For example,

>>> print(mol.bonds("atomnum 1", "atomnum 2"))
SelectorBond( size=1
0: Bond( HH31:1 => CH3:2 )
)

Returns the bonds between atoms with numbers 1 and 2. If there are no bonds that match, then an empty list is returned.

>>> print(mol.bonds("atomnum 1", "atomnum 5"))
SelectorBond::empty

If you are sure that there is only a single bond that matches, then you can use the bond() function

>>> print(mol.bond("atomnum 1", "atomnum 2"))
Bond( HH31:1 => CH3:2 )

This will raise a KeyError if multiple bonds match, or if no bonds match.

You can use any valid atom identifier to identify the atoms. This includes search strings, e.g. finding all of the bonds between carbon and hydrogen atoms.

>>> print(mol.bonds("element C", "element H"))
SelectorBond( size=10
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH32:3 )
2: Bond( CH3:2 => HH33:4 )
3: Bond( CA:9 => HA:10 )
4: Bond( CB:11 => HB1:12 )
5: Bond( CB:11 => HB2:13 )
6: Bond( CB:11 => HB3:14 )
7: Bond( CH3:19 => HH31:20 )
8: Bond( CH3:19 => HH32:21 )
9: Bond( CH3:19 => HH33:22 )
)
>>> print(mol.bonds("element C", "not element H"))
SelectorBond( size=9
0: Bond( CH3:2 => C:5 )
1: Bond( C:5 => O:6 )
2: Bond( C:5 => N:7 )
3: Bond( N:7 => CA:9 )
4: Bond( CA:9 => CB:11 )
5: Bond( CA:9 => C:15 )
6: Bond( C:15 => O:16 )
7: Bond( C:15 => N:17 )
8: Bond( N:17 => CH3:19 )
)

or using the atom identifying types

>>> print(mol.bonds(sr.atomid("HH31", 1), sr.atomid("CH3", 2)))
SelectorBond( size=1
0: Bond( HH31:1 => CH3:2 )
)

or using complex search strings, here finding the bonds between atoms in two residues

>>> print(mol.bonds("atoms in residx 0", "atoms in residx 1"))
SelectorBond( size=1
0: Bond( C:5 => N:7 )
)

or mixing and matching searches

>>> print(mol.bonds(sr.atomid("C", 5), "element N"))
SelectorBond( size=1
0: Bond( C:5 => N:7 )
)

Passing in a single atom identifier will return all of the bonds that involve that atom (or atoms).

>>> print(mol.bonds("atomnum 2"))
SelectorBond( size=4
0: Bond( CH3:2 => C:5 )
1: Bond( HH31:1 => CH3:2 )
2: Bond( CH3:2 => HH32:3 )
3: Bond( CH3:2 => HH33:4 )
)

This has returned all of the bonds that involve atom number 2, while

>>> print(mol.bonds("element C"))
SelectorBond( size=19
0: Bond( CH3:2 => HH33:4 )
1: Bond( CH3:2 => C:5 )
2: Bond( HH31:1 => CH3:2 )
3: Bond( CH3:2 => HH32:3 )
4: Bond( C:5 => O:6 )
...
14: Bond( C:15 => N:17 )
15: Bond( CH3:19 => HH31:20 )
16: Bond( CH3:19 => HH32:21 )
17: Bond( CH3:19 => HH33:22 )
18: Bond( N:17 => CH3:19 )
)

gets all of the bonds that involve carbon.

Note that you can also use "*" to match anything, so

>>> print(mol.bonds("element C", "*"))
SelectorBond( size=19
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH32:3 )
2: Bond( CH3:2 => HH33:4 )
3: Bond( CH3:2 => C:5 )
4: Bond( C:5 => O:6 )
...
14: Bond( C:15 => N:17 )
15: Bond( N:17 => CH3:19 )
16: Bond( CH3:19 => HH31:20 )
17: Bond( CH3:19 => HH32:21 )
18: Bond( CH3:19 => HH33:22 )
)

gives the same result.

Accessing bonds by residue

You can also access bonds by residue, by passing in residue identifiers. Passing in two residues identifiers, such as here

>>> print(mol.bonds("residx 0", "residx 1"))
SelectorBond( size=1
0: Bond( C:5 => N:7 )
)

gives all of the bonds that are between those two residues.

While passing in a single residue identifier

>>> print(mol.bonds("residx 0"))
SelectorBond( size=6
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH33:4 )
2: Bond( CH3:2 => C:5 )
3: Bond( CH3:2 => HH32:3 )
4: Bond( C:5 => O:6 )
5: Bond( C:5 => N:7 )
)

gives all of the bonds that involve atoms in this residue (including the bonds to other residues).

If you want the bonds that are contained only within the residue, then use the bonds function on that residue,

>>> print(mol["residx 0"].bonds())
SelectorBond( size=5
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH33:4 )
2: Bond( CH3:2 => C:5 )
3: Bond( CH3:2 => HH32:3 )
4: Bond( C:5 => O:6 )
)

Calling the bonds function on any molecular container will return the bonds that involve only the atoms that are fully contained in that container.

Note

We have shown searching for bonds by residue. You can also search for bonds by chain or segment if your molecule has chains or segments. So print(mol.bonds("chainidx 0", "chainidx 1")) would print the bonds between the first two chains.

Searching for bonds

You can use search terms to look for bonds. Use bonds in X to search for bonds within whatever matches X, e.g.

>>> print(mol["bonds in residx 0"])
SelectorBond( size=5
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH33:4 )
2: Bond( CH3:2 => C:5 )
3: Bond( CH3:2 => HH32:3 )
4: Bond( C:5 => O:6 )
)

Note

bonds in returns only those bonds whose atoms are wholly contained within whatever matches. So, in this case, these are only the bonds within the first residue. It doesn’t include the bond from this residue to another residue.

If you want bonds that involve any atom in X then use bonds with X, e.g.

>>> print(mol["bonds with residx 0"])
SelectorBond( size=6
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH32:3 )
2: Bond( CH3:2 => HH33:4 )
3: Bond( CH3:2 => C:5 )
4: Bond( C:5 => O:6 )
5: Bond( C:5 => N:7 )
)

or

>>> print(mol["bonds with atomnum 2"])
SelectorBond( size=4
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH32:3 )
2: Bond( CH3:2 => HH33:4 )
3: Bond( CH3:2 => C:5 )
)

or

>>> print(mol["bonds with element C"])
SelectorBond( size=19
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => C:5 )
2: Bond( CH3:2 => HH32:3 )
3: Bond( CH3:2 => HH33:4 )
4: Bond( C:5 => O:6 )
...
14: Bond( C:15 => N:17 )
15: Bond( N:17 => CH3:19 )
16: Bond( CH3:19 => HH31:20 )
17: Bond( CH3:19 => HH32:21 )
18: Bond( CH3:19 => HH33:22 )
)

You can find bonds to something using bonds to X, e.g.

>>> print(mol["bonds to resnum 1"])
SelectorBond( size=1
0: Bond( C:5 => N:7 )
)
>>> print(mol["bonds to atomnum 2"])
SelectorBond( size=4
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH32:3 )
2: Bond( CH3:2 => HH33:4 )
3: Bond( CH3:2 => C:5 )
)
>>> print(mol["bonds to element carbon"])
SelectorBond( size=16
0: Bond( HH31:1 => CH3:2 )
1: Bond( CH3:2 => HH32:3 )
2: Bond( CH3:2 => HH33:4 )
3: Bond( C:5 => O:6 )
4: Bond( C:5 => N:7 )
...
11: Bond( C:15 => N:17 )
12: Bond( N:17 => CH3:19 )
13: Bond( CH3:19 => HH31:20 )
14: Bond( CH3:19 => HH32:21 )
15: Bond( CH3:19 => HH33:22 )
)

Note

Note that bonds to excludes bonds that are in the selection. This means that bonds to element carbon excludes carbon-carbon bonds. If you want all bonds involving carbon, then use bonds with element carbon.

You can search for bonds between two groups, using bond from X to Y,

>>> print(mol["bonds from resnum 1 to resnum 2"])
SelectorBond( size=1
0: Bond( C:5 => N:7 )
)
>>> print(mol["bonds from element carbon to element carbon"])
SelectorBond( size=3
0: Bond( CH3:2 => C:5 )
1: Bond( CA:9 => CB:11 )
2: Bond( CA:9 => C:15 )
)
>>> print(mol["bonds from atomnum 1 to atomnum 2"])
SelectorBond( size=1
0: Bond( HH31:1 => CH3:2 )
)

And, like all molecule containers, you can perform searches in any container across any number of molecules, e.g.

>>> print(mols["bonds from element O to element H"])
SelectorMBond( size=1260
0: MolNum(3) Bond( O:23 => H1:24 )
1: MolNum(3) Bond( O:23 => H2:25 )
2: MolNum(4) Bond( O:26 => H1:27 )
3: MolNum(4) Bond( O:26 => H2:28 )
4: MolNum(5) Bond( O:29 => H1:30 )
...
1255: MolNum(630) Bond( O:1904 => H2:1906 )
1256: MolNum(631) Bond( O:1907 => H1:1908 )
1257: MolNum(631) Bond( O:1907 => H2:1909 )
1258: MolNum(632) Bond( O:1910 => H1:1911 )
1259: MolNum(632) Bond( O:1910 => H2:1912 )
)

Note

This has returned a SelectorMBond, which is the multi-molecule version of the SelectorBond container.

Uniquely identifying a bond

Bonds are identified by their BondID. This is a pair of AtomID identifiers, one for each of the two atoms to be identified. While the atom identifier can be any type, it is best to use atom indexes, as these uniquely identify atoms in a molecule. A BondID comprised of two AtomIdx identifiers will uniquely identify a single bond.

You can easily construct a BondID using the sire.bondid() function, e.g.

>>> print(sr.bondid(0, 1))
Bond( AtomIdx(0), AtomIdx(1) )

constructs a BondID from atom indexes,

>>> print(sr.bondid("O", "H1"))
Bond( AtomName('O'), AtomName('H1') )

constructs one from atom names, and

>>> print(sr.bondid(sr.atomid(1), sr.atomid(2)))
Bond( AtomNum(1), AtomNum(2) )

constructs one from atom numbers.

You can mix and match the IDs if you want.

You can then use the BondID to index, just like any other identifier class.

>>> print(mols[sr.bondid("O", "H1")])
SelectorMBond( size=630
0: MolNum(3) Bond( O:23 => H1:24 )
1: MolNum(4) Bond( O:26 => H1:27 )
2: MolNum(5) Bond( O:29 => H1:30 )
3: MolNum(6) Bond( O:32 => H1:33 )
4: MolNum(7) Bond( O:35 => H1:36 )
...
625: MolNum(628) Bond( O:1898 => H1:1899 )
626: MolNum(629) Bond( O:1901 => H1:1902 )
627: MolNum(630) Bond( O:1904 => H1:1905 )
628: MolNum(631) Bond( O:1907 => H1:1908 )
629: MolNum(632) Bond( O:1910 => H1:1911 )
)

gives all of the bonds between the atoms called O and H1 in all molecules, while

>>> print(mols[0][sr.bondid(0, 1)])
Bond( HH31:1 => CH3:2 )

gives just the bond between the first and second atoms in the first molecule, and

>>> print(mols[sr.bondid(0, 1)])
SelectorMBond( size=631
0: MolNum(2) Bond( HH31:1 => CH3:2 )
1: MolNum(3) Bond( O:23 => H1:24 )
2: MolNum(4) Bond( O:26 => H1:27 )
3: MolNum(5) Bond( O:29 => H1:30 )
4: MolNum(6) Bond( O:32 => H1:33 )
...
626: MolNum(628) Bond( O:1898 => H1:1899 )
627: MolNum(629) Bond( O:1901 => H1:1902 )
628: MolNum(630) Bond( O:1904 => H1:1905 )
629: MolNum(631) Bond( O:1907 => H1:1908 )
630: MolNum(632) Bond( O:1910 => H1:1911 )
)

gives the bond between the first and second atoms in each molecule.