Source code for rnamake.residue

import uuid
import numpy as np

import residue_type, util, basic_io, exceptions

# TODO work on removing this stuff, ResidueState is going into MotifState module
# This stuff is getting removed. but need now
###############################################################################

class ResidueState(object):
    def __init__(self, uuid):
        self.uuid = uuid
        self.beads = []

    def copy(self):
        return ResidueState(self.uuid)

class ResidueStateType(object):
    NORM = 0
    END =  1

class ResidueState2Bead(ResidueState):
    def __init__(self, uuid, sugar, base, type=ResidueStateType.NORM):
        super(self.__class__, self).__init__(uuid)
        self.sugar = sugar
        self.base = base
        self.beads = [sugar, base]
        self.type = type

    def copy(self):
        sugar = np.copy(self.sugar)
        base  = np.copy(self.base)
        return ResidueState2Bead(self.uuid, sugar, base, self.type)

    def to_str(self):
        return basic_io.points_to_str(self.beads) + "," + str(self.type)

    def update(self, r, t):
        self.beads = np.dot(self.beads, r) + t
        self.sugar = self.beads[0]
        self.base = self.beads[1]

class ResidueState3Bead(ResidueState):
    def __init__(self, uuid, sugar, base, phos,type=ResidueStateType.NORM):
        super(self.__class__, self).__init__(uuid)
        self.sugar = sugar
        self.base = base
        self.phos = phos
        self.beads = [sugar, base, phos]
        self.type = type

    def copy(self):
        sugar = np.copy(self.sugar)
        base  = np.copy(self.base)
        phos  = np.copy(self.phos)
        return ResidueState3Bead(self.uuid, sugar, base, phos, self.type)

    def to_str(self):
        return basic_io.points_to_str(self.beads) + "," + str(self.type)

    def update(self, r, t):
        self.beads = np.dot(self.beads, r) + t
        self.sugar = self.beads[0]
        self.base = self.beads[1]
        self.phos = self.beads[2]


def get_residue_state(r, type=ResidueStateType.NORM):
    phos_atoms, sugar_atoms, base_atoms = [], [], []

    for i, a in enumerate(r.atoms):
        if a is None:
            continue
        if   i < 3:
            phos_atoms.append(a)
        elif i < 12:
            sugar_atoms.append(a)
        else:
            base_atoms.append(a)

    return ResidueState3Bead(r.uuid,
                             util.center(sugar_atoms),
                             util.center(base_atoms),
                             util.center(phos_atoms),
                             type)

###############################################################################

[docs]class BeadType(object): """ BeadType is an ENUM type. This is to specify which center of atoms each bead represents. Phosphate (0): P, OP1, OP2\n Sugar (1): O5',C5',C4',O4',C3',O3',C1',C2',O2'\n Base (2): All remaining atoms """ PHOS = 0 # P, OP1, OP2 SUGAR = 1 # O5', C5', C4', O4', C3', O3', C1', C2', O2' BASE = 2 # All remaining
[docs]class Bead(object): """ Bead class stores information related to keeping track of steric clashes between residues during building. They are never used outside the Residue class :param btype: type of the bead either Phos(Phosphate), Sugar or Base, of the atoms used generate the center :type btype: BeadType :param center: The geometric center of the group of atoms :type center: numpy array """ __slots__ = ["center", "btype"] def __init__(self, center, btype): self.center, self.btype = center, btype def __repr__(self): center = basic_io.point_to_str(self.center) return "<Bead(btype='%s', center='%s')>" % (self.type_name(), center)
[docs] def copy(self): """ returns a deep copy of current bead object :returns: copy of bead object :rtype: Bead """ return Bead(np.copy(self.center), self.btype)
[docs] def type_name(self): """ returns name of btype in string form :returns: name of btype type :rtype: str """ if self.btype == 0: return "PHOSPHATE" if self.btype == 1: return "SUGAR" if self.btype == 2: return "BASE"
[docs]class Residue(object): """ Store residue information from pdb file, stores all Atom objects that belong to residue. Implementation is designed to be extremely lightweight. :param rtype: residue type, stores information about residue :type rtype: residue_type.ResidueType :param name: residue name :type name: str :param num: residue num :type num: int :param chain_id: chain identification :type chain_id: str :param i_code: insertion code, optional argument if not supplied will be set to :type i_code: str :attributes: `atoms` : atom.Atom holds all atoms that belong to this residue object `name` : str name of residue, ex. ADE, GUA etc `num` : int residue num `rtype` : residue_type.ResidueType Information about residue type each nucleic acid has its own type `chain_id` : str chain indentification string, ex. 'A' or 'B' `i_code`: str residue insertion code `uuid` : uuid.uuid1 the unique id to indentify residue :examples: .. code-block:: python # generating a new residue >>> rts = residue_type.ResidueTypeSet() >>> rtype = rts.get_rtype_by_resname("ADE") >>> r = Residue(rtype, "ADE", 1, "A") >>> print r.name A #using test residue >>> import rnamake.unittests.instances >>> r = rnamake.unittests.instances.residue() >>> print r.name G >>> a = r.get_atom("C1'") >>> print a.coords [-23.806 -50.289 86.732] >>> r.get_beads() [<Bead(btype='SUGAR', center='-24.027 -48.5001111111 86.368')>, <Bead(btype='BASE', center='-21.2186363636 -52.048 85.1157272727')>] #a fast way of saving coordinate information to file >>> r.to_str() "GUA,G,103,A,,N,N,N,O5' -26.469 -47.756 84.669,C5' -25.05 -47.579 84.775,C4' -24.521 -48.156 86.068,O4' -24.861 -49.568 86.118,C3' -23.009 -48.119 86.281,O3' -22.548 -46.872 86.808,C1' -23.806 -50.289 86.732,C2' -22.812 -49.259 87.269,O2' -23.167 -48.903 88.592,N1 -19.538 -52.485 85.025,C2 -19.717 -51.643 86.097,N2 -18.624 -51.354 86.809,N3 -20.884 -51.124 86.445,C4 -21.881 -51.521 85.623,C5 -21.811 -52.356 84.527,C6 -20.546 -52.91 84.164,O6 -20.273 -53.677 83.228,N7 -23.063 -52.513 83.947,C8 -23.858 -51.786 84.686,N9 -23.21 -51.159 85.722," #get PDB formmated coordinates back out >>> r.to_pdb_str() ATOM 1 O5' G A 103 -26.469 -47.756 84.669 1.00 0.00 ATOM 2 C5' G A 103 -25.050 -47.579 84.775 1.00 0.00 ATOM 3 C4' G A 103 -24.521 -48.156 86.068 1.00 0.00 ATOM 4 O4' G A 103 -24.861 -49.568 86.118 1.00 0.00 ATOM 5 C3' G A 103 -23.009 -48.119 86.281 1.00 0.00 ATOM 6 O3' G A 103 -22.548 -46.872 86.808 1.00 0.00 . . . """ __slots__ = [ "rtype", "name", "num", "chain_id", "i_code", "uuid", "atoms"] def __init__(self, rtype, name, num, chain_id, i_code=""): self.rtype = rtype self.name = name self.num = num self.chain_id = chain_id self.i_code = i_code self.uuid = uuid.uuid1() # fix pickle bug if len(self.i_code) == 0: self.i_code = "" self.atoms = [] def __repr__(self): return "<Residue('%s%d%s chain %s')>" % ( self.name, self.num, self.i_code, self.chain_id)
[docs] def short_name(self): """gets letter of residue, i.e. A or G etc :return: letter for residue :rtype: str """ return self.rtype.name[0]
[docs] def setup_atoms(self, atoms): """ put atoms in correct positon in internal atom list, also corrects some named atom names to their correct name :param atoms: list of atom objects that are to be part of this residue :type atoms: list of Atom objects """ self.atoms = [None for x in self.rtype.atom_map.keys()] for a in atoms: name_change = self.rtype.get_correct_atom_name(a) if name_change is not None: a.name = name_change[1] if a.name in self.rtype.atom_map: pos = self.rtype.atom_map[a.name] self.atoms[pos] = a
[docs] def get_atom(self, atom_name): """ get atom object by its name :param atom_name: name :type atom_name: str Examples: .. code-block:: python >>> r = rnamake.unittests.instances.residue() >>> a = r.get_atom("C1'") >>> print a.coords [-23.806 -50.289 86.732] """ try: index = self.rtype.atom_map[atom_name] return self.atoms[index] except KeyError: raise exceptions.ResidueException("cannot find atom")
[docs] def connected_to(self, res, cutoff=3.0): """ Determine if another residue is connected to this residue, returns 0 if res is not connected to self, returns 1 if connection is going from 5' to 3' and returns -1 if connection is going from 3' to 5' :param res: another residue :param cutoff: distance to be considered connected, default: 3 Angstroms :type res: Residue :type cutoff: int :rtype: int """ # 5' to 3' o3_atom = self.get_atom("O3'") p_atom = res.get_atom("P") if o3_atom and p_atom: if util.distance(o3_atom.coords, p_atom.coords) < cutoff: return 1 # 3' to 5' p_atom = self.get_atom("P") o3_atom = res.get_atom("O3'") if o3_atom and p_atom: if util.distance(o3_atom.coords, p_atom.coords) < cutoff: return -1 return 0
[docs] def get_beads(self): """ Generates steric beads required for checking for steric clashes between motifs. Each residues has three beads modeled after the typical three bead models used in coarse grain modeling. The three beads are: Phosphate: P, OP1, OP2\n Sugar : O5',C5',C4',O4',C3',O3',C1',C2',O2'\n Base : All remaining atoms if there are for example no phosphate atoms only 2 beads will be returned. .. code-block:: python >>> import rnamake.unittests.instances >>> r = rnamake.unittests.instances.residue() >>> r.get_beads() [<Bead(btype='SUGAR', center='-24.027 -48.5001111111 86.368')>, <Bead(btype='BASE', center='-21.2186363636 -52.048 85.1157272727')>] """ phos_atoms, sugar_atoms, base_atoms = [], [], [] for i, a in enumerate(self.atoms): if a is None: continue if i < 3: phos_atoms.append(a) elif i < 12: sugar_atoms.append(a) else: base_atoms.append(a) beads = [] types = [BeadType.PHOS, BeadType.SUGAR, BeadType.BASE] for i, alist in enumerate([phos_atoms, sugar_atoms, base_atoms]): if len(alist) > 0: beads.append(Bead(util.center(alist), types[i])) return beads
[docs] def copy(self): """ performs a deep copy of Residue object :rtype: Residue :examples: .. code-block:: python >>> import rnamake.unittests.instances >>> r = rnamake.unittests.instances.residue() >>> r_copy = r.copy() >>> r_copy.name G """ copied_r = Residue(self.rtype, self.name, self.num, self.chain_id, self.i_code) copied_r.atoms = [None for x in range(len(self.atoms))] for i, a in enumerate(self.atoms): if a is None: continue copied_r.atoms[i] = a.copy() copied_r.uuid = self.uuid return copied_r
[docs] def new_uuid(self): """ give residue a new unique indentifier code. There is probably no reason why you should call this unless writing a new, motif structure. """ self.uuid = uuid.uuid1()
[docs] def to_str(self): """ stringifes residue object :returns: stringified residue object .. code-block:: python >>> import rnamake.unittests.instances >>> r = rnamake.unittests.instances.residue() >>> r.to_str() "GUA,G,103,A,,N,N,N,O5' -26.469 -47.756 84.669,C5' -25.05 -47.579 84.775,C4' -24.521 -48.156 86.068,O4' -24.861 -49.568 86.118,C3' -23.009 -48.119 86.281,O3' -22.548 -46.872 86.808,C1' -23.806 -50.289 86.732,C2' -22.812 -49.259 87.269,O2' -23.167 -48.903 88.592,N1 -19.538 -52.485 85.025,C2 -19.717 -51.643 86.097,N2 -18.624 -51.354 86.809,N3 -20.884 -51.124 86.445,C4 -21.881 -51.521 85.623,C5 -21.811 -52.356 84.527,C6 -20.546 -52.91 84.164,O6 -20.273 -53.677 83.228,N7 -23.063 -52.513 83.947,C8 -23.858 -51.786 84.686,N9 -23.21 -51.159 85.722," """ s = self.rtype.name + "," + self.name + "," + str(self.num) + "," + \ self.chain_id + "," + self.i_code + "," for a in self.atoms: if a is None: s += "N," else: s += a.to_str() + "," return s
[docs] def to_pdb_str(self, acount=1, return_acount=0, rnum=-1, chain_id=""): """ returns pdb formatted string of residue's coordinate information :param acount: current atom index, default: 1 :param return_acount: final atom index after current atoms, default: 0 :param rnum: starting residue number, default: -1 :param chain_id: the chain id of the chain, i.e. "A", "B" etc :type acount: int :type return_acount: int :type rnum: int :type chain_id: str :rtype: str :examples: .. code-block:: python >>> import rnamake.unittests.instances >>> r = rnamake.unittests.instances.residue() >>> r.to_pdb_str() ATOM 1 O5' G A 103 -26.469 -47.756 84.669 1.00 0.00 ATOM 2 C5' G A 103 -25.050 -47.579 84.775 1.00 0.00 ATOM 3 C4' G A 103 -24.521 -48.156 86.068 1.00 0.00 ATOM 4 O4' G A 103 -24.861 -49.568 86.118 1.00 0.00 ATOM 5 C3' G A 103 -23.009 -48.119 86.281 1.00 0.00 ATOM 6 O3' G A 103 -22.548 -46.872 86.808 1.00 0.00 . . . """ num = self.num cid = self.chain_id if rnum != -1: num = rnum if chain_id != "": cid = chain_id s = "" for a in self.atoms: if a is None: continue s += basic_io.PDBLINE_GE100K % \ ('ATOM', acount, a.name, '', self.rtype.name[0], cid, num, '', a.coords[0], a.coords[1], a.coords[2], 1.00, 0.00, '', '') acount += 1 if return_acount: return s, acount else: return s
[docs] def to_pdb(self, fname="residue.pdb"): """ Writes a PDB string formmated verision of this Residue object to file :param fname: filename of output PDB file, default="chain.pdb" :type fname: str :return: None """ f = open(fname, "w") s = self.to_pdb_str() f.write(s) f.close()