| 1 | def new_residue_from_template(model, template, chain_id, center,
|
|---|
| 2 | residue_number=None, insert_code=' ', b_factor=50, precedes=None):
|
|---|
| 3 | '''
|
|---|
| 4 | Create a new residue based on a template, and add it to the model.
|
|---|
| 5 | '''
|
|---|
| 6 | if residue_number is None:
|
|---|
| 7 | if chain_id in model.residues.chain_ids:
|
|---|
| 8 | residue_number = suggest_new_residue_number_for_ligand(model, chain_id)
|
|---|
| 9 | else:
|
|---|
| 10 | residue_number = 0
|
|---|
| 11 | import numpy
|
|---|
| 12 | from chimerax.atomic import Atom
|
|---|
| 13 | t_coords = numpy.array([a.coord for a in template.atoms])
|
|---|
| 14 | t_center = t_coords.mean(axis=0)
|
|---|
| 15 | t_coords += numpy.array(center) - t_center
|
|---|
| 16 | tatom_to_atom = {}
|
|---|
| 17 | r = model.new_residue(template.name, chain_id, residue_number,
|
|---|
| 18 | insert=insert_code, precedes=precedes)
|
|---|
| 19 | from chimerax.atomic.struct_edit import add_bond
|
|---|
| 20 | for i, ta in enumerate(template.atoms):
|
|---|
| 21 | a = tatom_to_atom[ta] = model.new_atom(ta.name, ta.element)
|
|---|
| 22 | a.coord = t_coords[i]
|
|---|
| 23 | a.bfactor = b_factor
|
|---|
| 24 | r.add_atom(a)
|
|---|
| 25 | for tn in ta.neighbors:
|
|---|
| 26 | n = tatom_to_atom.get(tn, None)
|
|---|
| 27 | if n is not None:
|
|---|
| 28 | add_bond(a, n)
|
|---|
| 29 | return r
|
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 | def add_amino_acid_residue(model, resname, prev_res=None, next_res=None,
|
|---|
| 33 | chain_id=None, number=None, center=None, insertion_code=' ', b_factor=50,
|
|---|
| 34 | occupancy=1, phi=-135, psi=135):
|
|---|
| 35 | session = model.session
|
|---|
| 36 | if (not chain_id or not number or not center) and (not prev_res and not next_res):
|
|---|
| 37 | raise TypeError('If no anchor residues are specified, chain ID, '
|
|---|
| 38 | 'number and center must be provided!')
|
|---|
| 39 | if prev_res and next_res:
|
|---|
| 40 | raise TypeError('Cannot specify both previous and next residues!')
|
|---|
| 41 | other_atom = None
|
|---|
| 42 | insertion_point = None
|
|---|
| 43 |
|
|---|
| 44 | if prev_res:
|
|---|
| 45 | pri = model.residues.index(prev_res)
|
|---|
| 46 | if pri > 0 and pri < len(model.residues)-1:
|
|---|
| 47 | insertion_point = model.residues[pri+1]
|
|---|
| 48 | catom = prev_res.find_atom('C')
|
|---|
| 49 | for n in catom.neighbors:
|
|---|
| 50 | if n.residue != prev_res:
|
|---|
| 51 | raise TypeError('This residue already has another bonded to its '
|
|---|
| 52 | 'C terminus!')
|
|---|
| 53 | chain_id = prev_res.chain_id
|
|---|
| 54 | oxt = prev_res.find_atom('OXT')
|
|---|
| 55 | if oxt is not None:
|
|---|
| 56 | oxt.delete()
|
|---|
| 57 | elif next_res:
|
|---|
| 58 | insertion_point = next_res
|
|---|
| 59 | natom = next_res.find_atom('N')
|
|---|
| 60 | for n in natom.neighbors:
|
|---|
| 61 | if n.residue != next_res:
|
|---|
| 62 | raise TypeError('This residue already has another bonded to its '
|
|---|
| 63 | 'N terminus!')
|
|---|
| 64 | chain_id = next_res.chain_id
|
|---|
| 65 | for hname in ('H2', 'H3'):
|
|---|
| 66 | h = next_res.find_atom(hname)
|
|---|
| 67 | if h is not None:
|
|---|
| 68 | h.delete()
|
|---|
| 69 | h = next_res.find_atom('H1')
|
|---|
| 70 | if h is not None:
|
|---|
| 71 | h.name='H'
|
|---|
| 72 | if next_res.name == 'PRO':
|
|---|
| 73 | h = next_res.find_atom('H')
|
|---|
| 74 | if h:
|
|---|
| 75 | h.delete()
|
|---|
| 76 | if number is None:
|
|---|
| 77 | if prev_res:
|
|---|
| 78 | number = prev_res.number + 1
|
|---|
| 79 | elif next_res:
|
|---|
| 80 | number = next_res.number - 1
|
|---|
| 81 |
|
|---|
| 82 | from chimerax import mmcif
|
|---|
| 83 | tmpl = mmcif.find_template_residue(session, resname)
|
|---|
| 84 | import numpy
|
|---|
| 85 | # delete extraneous atoms
|
|---|
| 86 | r = new_residue_from_template(model, tmpl, chain_id, [0,0,0], number,
|
|---|
| 87 | insert_code=insertion_code, b_factor=b_factor, precedes=insertion_point)
|
|---|
| 88 | r.atoms[numpy.in1d(r.atoms.names, ['OXT', 'HXT', 'H2', 'H1', 'HN1', 'HN2'])].delete()
|
|---|
| 89 |
|
|---|
| 90 | # Translate and rotate residue to (roughly) match the desired position
|
|---|
| 91 | if not next_res and not prev_res:
|
|---|
| 92 | r.atoms.coords += numpy.array(center) - r.atoms.coords.mean(axis=0)
|
|---|
| 93 | else:
|
|---|
| 94 | from chimerax.geometry import align_points
|
|---|
| 95 | from chimerax.atomic.struct_edit import add_bond
|
|---|
| 96 | if prev_res:
|
|---|
| 97 | add_bond(r.find_atom('N'), prev_res.find_atom('C'))
|
|---|
| 98 | n_pos = _find_next_N_position(prev_res)
|
|---|
| 99 | ca_pos = _find_next_CA_position(n_pos, prev_res)
|
|---|
| 100 | c_pos = _find_next_C_position(ca_pos, n_pos, prev_res, phi)
|
|---|
| 101 | target_coords = numpy.array([n_pos, ca_pos, c_pos])
|
|---|
| 102 | align_coords = numpy.array([r.find_atom(a).coord for a in ['N', 'CA', 'C']])
|
|---|
| 103 | elif next_res:
|
|---|
| 104 | add_bond(r.find_atom('C'), next_res.find_atom('N'))
|
|---|
| 105 | c_pos = _find_prev_C_position(next_res, psi)
|
|---|
| 106 | ca_pos = _find_prev_CA_position(c_pos, next_res)
|
|---|
| 107 | o_pos = _find_prev_O_position(c_pos, next_res)
|
|---|
| 108 | target_coords = numpy.array([c_pos, ca_pos, o_pos])
|
|---|
| 109 | align_coords = numpy.array([r.find_atom(a).coord for a in ['C', 'CA', 'O']])
|
|---|
| 110 |
|
|---|
| 111 | tf = align_points(align_coords, target_coords)[0]
|
|---|
| 112 | r.atoms.coords = tf*r.atoms.coords
|
|---|
| 113 | if r.name in ('GLU', 'ASP'):
|
|---|
| 114 | fix_amino_acid_protonation_state(r)
|
|---|
| 115 | if r.name == 'PRO':
|
|---|
| 116 | r.atoms[r.atoms.names=='H'].delete()
|
|---|
| 117 |
|
|---|
| 118 | r.atoms.bfactors = b_factor
|
|---|
| 119 | r.atoms.occupancies = occupancy
|
|---|
| 120 |
|
|---|
| 121 | return r
|
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
|
|---|
| 125 | def _find_next_N_position(prev_res):
|
|---|
| 126 | from chimerax.atomic.struct_edit import find_pt
|
|---|
| 127 | bond_length = 1.34
|
|---|
| 128 | angle = 120
|
|---|
| 129 | dihedral = 180
|
|---|
| 130 | a1 = prev_res.find_atom('C')
|
|---|
| 131 | a2 = prev_res.find_atom('CA')
|
|---|
| 132 | a3 = prev_res.find_atom('O')
|
|---|
| 133 | return find_pt(*[a.coord for a in [a1, a2, a3]], bond_length, angle, dihedral)
|
|---|
| 134 |
|
|---|
| 135 | def _find_next_CA_position(n_pos, prev_res):
|
|---|
| 136 | from chimerax.atomic.struct_edit import find_pt
|
|---|
| 137 | bond_length = 1.48
|
|---|
| 138 | angle = 124
|
|---|
| 139 | omega = 180
|
|---|
| 140 | c = prev_res.find_atom('C')
|
|---|
| 141 | ca = prev_res.find_atom('CA')
|
|---|
| 142 | return find_pt(n_pos, *[a.coord for a in [c, ca]], bond_length, angle, omega)
|
|---|
| 143 |
|
|---|
| 144 | def _find_next_C_position(ca_pos, n_pos, prev_res, phi):
|
|---|
| 145 | from chimerax.atomic.struct_edit import find_pt
|
|---|
| 146 | bond_length = 1.53
|
|---|
| 147 | angle = 120
|
|---|
| 148 | c = prev_res.find_atom('C')
|
|---|
| 149 | return find_pt(ca_pos, n_pos, c.coord, bond_length, angle, phi)
|
|---|
| 150 |
|
|---|
| 151 |
|
|---|
| 152 | def _find_prev_C_position(next_res, psi):
|
|---|
| 153 | from chimerax.atomic.struct_edit import find_pt
|
|---|
| 154 | bond_length = 1.34
|
|---|
| 155 | angle = 120
|
|---|
| 156 | a1 = next_res.find_atom('N')
|
|---|
| 157 | a2 = next_res.find_atom('CA')
|
|---|
| 158 | a3 = next_res.find_atom('C')
|
|---|
| 159 | return find_pt(*[a.coord for a in [a1, a2, a3]], bond_length, angle, psi)
|
|---|
| 160 |
|
|---|
| 161 | def _find_prev_CA_position(c_pos, next_res):
|
|---|
| 162 | from chimerax.atomic.struct_edit import find_pt
|
|---|
| 163 | bond_length = 1.53
|
|---|
| 164 | angle = 120
|
|---|
| 165 | omega = 180
|
|---|
| 166 | n = next_res.find_atom('N')
|
|---|
| 167 | ca = next_res.find_atom('CA')
|
|---|
| 168 | return find_pt(c_pos, *[a.coord for a in [n, ca]], bond_length, angle, omega)
|
|---|
| 169 |
|
|---|
| 170 | def _find_prev_O_position(c_pos, next_res):
|
|---|
| 171 | from chimerax.atomic.struct_edit import find_pt
|
|---|
| 172 | bond_length = 1.22
|
|---|
| 173 | angle = 120
|
|---|
| 174 | dihedral = 0
|
|---|
| 175 | n = next_res.find_atom('N')
|
|---|
| 176 | ca = next_res.find_atom('CA')
|
|---|
| 177 | return find_pt(c_pos, *[a.coord for a in (n, ca)], bond_length, angle, dihedral)
|
|---|
| 178 |
|
|---|
| 179 | def add_aa_cmd(session, residue, resname):
|
|---|
| 180 | from chimerax.core.errors import UserError
|
|---|
| 181 | if len(residue) != 1:
|
|---|
| 182 | raise UserError('Please select a single residue!')
|
|---|
| 183 | residue = residue[0]
|
|---|
| 184 | from chimerax.atomic import Residue
|
|---|
| 185 | if residue.polymer_type != Residue.PT_AMINO:
|
|---|
| 186 | raise UserError('Selection must be an amino acid from a protein chain!')
|
|---|
| 187 | aa_neighbors = []
|
|---|
| 188 | for n in residue.neighbors:
|
|---|
| 189 | if n.polymer_type == Residue.PT_AMINO:
|
|---|
| 190 | is_peptide_bond = False
|
|---|
| 191 | for b in residue.bonds_between(n):
|
|---|
| 192 | if all([a.element.name in ('N','C') for a in b.atoms]):
|
|---|
| 193 | is_peptide_bond = True
|
|---|
| 194 | if is_peptide_bond:
|
|---|
| 195 | aa_neighbors.append(n)
|
|---|
| 196 | if len(aa_neighbors) != 1:
|
|---|
| 197 | raise UserError('Selection is not a terminal residue!')
|
|---|
| 198 | n = aa_neighbors[0]
|
|---|
| 199 | if (n.number < residue.number or
|
|---|
| 200 | (n.number == residue.number and n.insertion_code < residue.insertion_code)):
|
|---|
| 201 | prev_res = residue
|
|---|
| 202 | next_res = None
|
|---|
| 203 | else:
|
|---|
| 204 | next_res = residue
|
|---|
| 205 | prev_res = None
|
|---|
| 206 | add_amino_acid_residue(residue.structure, resname, prev_res=prev_res, next_res=next_res)
|
|---|
| 207 |
|
|---|
| 208 | from chimerax.core.commands import (
|
|---|
| 209 | register, CmdDesc,
|
|---|
| 210 | StringArg
|
|---|
| 211 | )
|
|---|
| 212 | from chimerax.atomic import ResiduesArg
|
|---|
| 213 |
|
|---|
| 214 | desc = CmdDesc(
|
|---|
| 215 | required=[
|
|---|
| 216 | ('residue', ResiduesArg),
|
|---|
| 217 | ('resname', StringArg)
|
|---|
| 218 | ]
|
|---|
| 219 | )
|
|---|
| 220 | register('addaa', desc, add_aa_cmd, logger=session.logger)
|
|---|