Ticket #4597: add_aa.py

File add_aa.py, 8.1 KB (added by Tristan Croll, 4 years ago)

Added by email2trac

Line 
1def 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
32def 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
125def _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
135def _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
144def _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
152def _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
161def _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
170def _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
179def 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
208from chimerax.core.commands import (
209 register, CmdDesc,
210 StringArg
211)
212from chimerax.atomic import ResiduesArg
213
214desc = CmdDesc(
215 required=[
216 ('residue', ResiduesArg),
217 ('resname', StringArg)
218 ]
219)
220register('addaa', desc, add_aa_cmd, logger=session.logger)