Living organisms manufacture an assortment of biopolymers: proteins are polymers of amino-acid residues, DNA and RNA are polymers of nucleotide bases, and polysaccharides such as cellulose and starch are polymers of sugars. In a description of these often very large molecules, it is useful to include with each atom information about the monomer it resides in, the chain the monomer is in, the model the chain is in, etc. OEChem‘s OEResidue is a container for just such information, and more.
|Property||Type||Get Method||Set Method|
|NMR Model Number||int||GetModelNumber||SetModelNumber|
|Atom Serial Number||int||GetSerialNumber||SetSerialNumber|
|Alternate Location Code||char||GetAlternateLocation||SetAlternateLocation|
Note that OEResidue is actually a part of OEChem, it is included in this OEBio chapter because it underlies much of OEBio’s functionality. Table: Properties of OEResidues lists each property along with the Get and Set methods for each. Figure: PDB format atom records shows how these properties are encoded for a structure in PDB format.
The residue number, insertion code, chain ID, model number and fragment number properties describe a hierarchy of structural elements, but do not impose any explicit hierarchy. Because the biological world is inherently messy, the flexibility of a flat representation such as this is very useful. For example, because it does not require a chain to be either above or below a fragment, it can describe both a chain with gaps, composed of multiple covalently bonded fragments and one that is covalently bonded to another chain. And, although residues are in a given sequence order, it does not require the polymer to be linear and can easily handle a glycosylated protein where a chain may have a tree-like, rather than linear, structure.
On the other hand, for some tasks a hierarchical view of biopolymers is more natural and more efficient, despite the limitations of a hierarchy. For more information, see the section A Hierarchy View.
Note that although infrequently used, the insertion code modifies the residue number. When comparing residue numbers it is necessary to compare both the residue number and the insertion code. The best way to avoid errors when comparing OEResidues is to use OESameResidue.
Listing 1: Accessing OEResidue properties
import sys from openeye import oechem def CalcResCounts(mol): if not oechem.OEHasResidues(mol): oechem.OEPerceiveResidues(mol, oechem.OEPreserveResInfo_All) resCt = 0 hetCt = 0 prevRes = oechem.OEResidue() for atom in mol.GetAtoms(): thisRes = oechem.OEAtomGetResidue(atom) if not oechem.OESameResidue(prevRes, thisRes): resCt += 1 if thisRes.IsHetAtom(): hetCt += 1 prevRes = thisRes print("Molecule: %s" % mol.GetTitle()) print("Residues: %d (%d hets)" % (resCt, hetCt)) def main(argv=[__name__]): if len(argv) != 2: oechem.OEThrow.Usage("%s <mol-infile>" % argv) ims = oechem.oemolistream() if not ims.open(argv): oechem.OEThrow.Fatal("Unable to open %s for reading" % argv) for mol in ims.GetOEGraphMols(): CalcResCounts(mol) if __name__ == "__main__": sys.exit(main(sys.argv))
It is important to keep in mind that OEResidues are not residues, but instead are information blocks associated with atoms. Several of the properties of an OEResidue such as atom serial number and occupancy are not residue properties at all, but instead are properties of a specific atom. Changing an OEResidue property will not modify anything in the molecule until the OEResidue is saved back into the molecule, and this must be done for each atom for which the property is to be changed.
Listing 2: Updating OEResidue properties
for atom in mol.GetAtoms(): thisRes = oechem.OEAtomGetResidue(atom) if oechem.OEGetResidueIndex(thisRes) == oechem.OEResidueIndex_MSE: thisRes.SetName("MET") # modify res properties thisRes.SetHetAtom(False) oechem.OEAtomSetResidue(atom, thisRes) # store updated residue if atom.GetAtomicNum() == oechem.OEElemNo_Se: atom.SetAtomicNum(oechem.OEElemNo_S) # fix atom type & name atom.SetName(" SD ")
The input routines for Protein Data Bank (PDB) files are setup to automatically perceive OEResidue properties but the perception can also be done explicitly, if required, as shown below.
Listing 3: Perceiving OEResidues
if not oechem.OEHasResidues(mol): oechem.OEPerceiveResidues(mol, oechem.OEPreserveResInfo_All)
At times, it may be more convenient to loop over residues or chains directly, rather than looping over atoms and keeping track as OEResidue properties change. The OEHierView class supports this approach by representing a biopolymer molecule as a hierarchy of the components: OEHierChain, OEHierFragment, and OEHierResidue.
Listing 4: Looping over hierarchical components
import sys from openeye import oechem def CalcResCounts(mol): hv = oechem.OEHierView(mol) chainCt = 0 fragCt = 0 resCt = 0 watCt = 0 for chain in hv.GetChains(): chainCt += 1 for frag in chain.GetFragments(): fragCt += 1 for hres in frag.GetResidues(): resCt += 1 if (oechem.OEGetResidueIndex(hres.GetOEResidue()) == oechem.OEResidueIndex_HOH): watCt += 1 print("Molecule : %s" % mol.GetTitle()) print("Chains : %d" % chainCt) print("Fragments: %d" % fragCt) print("Residues : %d (%d waters)" % (resCt, watCt)) def main(argv=[__name__]): if len(argv) != 2: oechem.OEThrow.Usage("%s <mol-infile>" % argv) ims = oechem.oemolistream() if not ims.open(argv): oechem.OEThrow.Fatal("Unable to open %s for reading" % argv) for mol in ims.GetOEGraphMols(): if not oechem.OEHasResidues(mol): oechem.OEPerceiveResidues(mol, oechem.OEPreserveResInfo_All) CalcResCounts(mol) if __name__ == "__main__": sys.exit(main(sys.argv))
Be aware that OEHierView works with what amounts to a snapshot of the molecule, so if changes are made to the original molecule (such as adding or deleting atoms) it may be necessary to rebuild the OEHierView.
The OEHier... classes provide direct access to chains, fragments and residues using common identifiers such as chain id, residue name and residue number.
Listing 5: Selecting a residue
hv = oechem.OEHierView(mol) hres = hv.GetResidue("A", "LEU", 27) for atom in hres.GetAtoms(): # only this residue's atoms res = oechem.OEAtomGetResidue(atom) print(res.GetSerialNumber(), atom.GetName())
Two proteins can be sequence aligned with the function OEGetAlignment which returns the class OESequenceAlignment. Several different amino acid distance matrices are supplied, including OESeqAlignmentMethod_PAM250 [Dayhoff-1978] for closely related proteins, and OESeqAlignmentMethod_BLOSUM62 [Henikoff-1992] & OESeqAlignmentMethod_GONNET [Gonnet-1992] for more distantly related proteins.
Alignments can be written out in standard format using the function OEWriteAlignment.
The function OERMSD will calculate the backbone or C-\(\alpha\) RMSD of previously aligned proteins.
The space group and unit cell parameters (a, b, c, \(\alpha\), \(\beta\), \(\gamma\)) for an x-ray crystal structure can be accessed and modified using the following OEBio functions:
The function OEExpandCrystalSymmetry will replicate the molecule to fill-in each of the symmetry related molecules in other regions of the crystal lattice, out to a specified radius.
OEBio provides functionality for perceiving secondary structural elements in proteins. Perception is performed according to the method of Kabsch and Sander [Kabsch-1983] and identifies helices, beta sheets, and turns based on hydrogen bonding patterns within a protein. The function OEPerceiveSecondaryStructure determines secondary structure for a protein molecule, and stores the assigned secondary structure types in the OEResidue associated with each atom in the protein. Secondary structure assignments are stored as single integer values within OEResidue, into which are packed the type (helix, sheet, etc.), helix/sheet/turn ID numbers, and strand identifiers for individual strands within beta sheets. To help decode the secondary structure information packed in these integers, several functions are provided:
These functions all take an integer argument, which is typically the value returned from OEResidue.GetSecondaryStructure. An additional function, OESecondaryStructurePacked, will do the reverse operation, namely, taking a secondary structure type, ID, and optional strand ID, and packing them into an integer suitable for use with OEResidue.SetSecondaryStructure.
Secondary structure types are defined in the OESecondaryStructure namespace.
A functor is also provided to help identify atoms belonging to particular secondary structure features. OEHasSecondaryStructure takes an integer argument. This integer can be one of the above constants (e.g., to find all atoms belonging to alpha helices); the value retrieved from a call to OEResidue.GetSecondaryStructure (e.g., to find all atoms belonging to the same helix as a particular residue); or a value returned from OESecondaryStructurePacked, which can be used to find atoms from a specific secondary structural element, such as Helix number 4.
A number of atom predicates are provided in OEBio to make it easy to identify atoms with particular properties:
This last predicate uses PDBAtoms (such as CA) to refer to specific atom types in a Protein Data Bank (PDB) structure.
The function OEHasBondedResidues indicates whether the atoms in each residue have bonds to the same residue.