9 log = logging.getLogger(
"buildxlinks")
16 from sets
import Set
as set
23 Class defining a cross-link
26 def __init__(self, id1, chain1, residue1,
27 id2, chain2, residue2,
31 @param id1 Id of the first component of the cross-link
32 @param chain1 Chain of the first component
33 @param residue1 Residue cross-linked in the first component
34 @param id2 Id of the second component of the cross-link
35 @param chain2 chain1 Chain of the second component
36 @param residue2 Residue cross-linked in the second component
37 @param distance Maximum distance
40 self.first_chain = chain1
41 self.first_residue = residue1
43 self.second_chain = chain2
44 self.second_residue = residue2
45 self.distance = distance
48 xl =
Xlink( self.first_id, self.first_chain, self.first_residue,
49 self.second_id, self.second_chain, self.second_residue,
53 def __eq__(self, other):
54 if self.first_id != other.first_id
or \
55 self.second_id != other.second_id
or \
56 self.first_chain != other.first_chain
or \
57 self.second_chain != other.second_chain
or \
58 self.first_residue != other.first_residue
or \
59 self.second_residue != other.second_residue
or \
60 abs(other.distance-self.distance) > 0.01:
66 swaps the order of the residues in the restraint
69 self.first_id, self.second_id = self.second_id, self.first_id
70 self.first_residue, self.second_residue = \
71 self.second_residue, self.first_residue
72 self.first_chain, self.second_chain = \
73 self.second_chain, self.first_chain
77 s =
"Cross Link: %s %s %d - %s %s %d. Distance %f" % (self.first_id,
78 self.first_chain, self.first_residue,
79 self.second_id, self.second_chain,
80 self.second_residue, self.distance )
85 Generate a unique name for the restraint.
86 @note: The name cannot start with a number, upsets sqlite
88 name =
"cl_%s_%s%d_%s_%s%d" % (self.first_id, self.first_chain,
89 self.first_residue, self.second_id,
90 self.second_chain, self.second_residue)
95 def build_xlinks_graph(xlinks_dict):
97 Build a set of nodes and edges from the set of crosslinking
99 @param xlinks_dict a XlinksDict class
104 for key
in xlinks_dict.keys():
107 edge = [key[0],key[1]]
109 if edge
not in edges:
111 log.debug(
"Subunits %s", subunits)
112 log.debug(
"Edges %s", edges)
113 return subunits, edges
117 Description of crosslinking restraints as a python
119 The keys are a pair with the ids of the cross-linked subunits.
120 Note: The pairs are considered in alphabetic order
122 def add(self, xlink):
124 Add a xlink. It is ensured that the id of the first element is
125 is lower that the second
128 if xlink.second_id < xlink.first_id:
130 key = (xlink.first_id, xlink.second_id)
131 if key
not in self.keys():
133 self[key].append(xlink)
135 def get_xlinks_for_pair(self, pair_ids):
137 @param pair_ids Ids fo the subunits that are cross-linked
140 xlinks_list = self[pair_ids]
141 ys = [xl.clone()
for xl
in xlinks_list]
145 xlinks_list = self[(pair_ids[1], pair_ids[0])]
146 ys = [xl.clone()
for xl
in xlinks_list]
159 return self[pair_ids]
161 print pair_ids,"NOT FOUND, swapping to",(pair_ids[1], pair_ids[0])
162 # If not found, invert the pair
163 xlinks_list = self[(pair_ids[1], pair_ids[0])]
164 # swap the xlinks, so the first element of the pair corresponds to the
165 # first id in the xlink
166 for xl in xlinks_list:
168 for xl in xlinks_list:
175 Compute the order of the docking experiments. The order is derived
176 from the cross-linking restraints:
177 1) The subunit with the highest number of cross-linkns with others
178 works as the first receptor (A)
179 2) All the subunits cross-linked with A are docked into A.
180 3) The next receptor (B) is the subunit that has the highest number of
182 4) All the subunits cross-linked to B are docked into B (expect A)
183 5) The procedure is repeated until there are no more cross-links
189 def set_components_and_connections(self, subunits, edges):
191 Instead of setting the xlink restraints, init the graph directly
192 Example of what to pass with the structure in 3sfd
193 G.add_nodes_from(["A", "B", "C", "D"])
194 G.add_edges_from([("B","C"), ("B","A"), ("B","D"), ("C","D")])
195 ("B","C") means that there are cross-links between B and C
197 self.G.add_nodes_from(subunits)
198 self.G.add_edges_from(edges)
202 Sets the xlinks used for computing the docking order
203 @param xlinks_dict XlinksDict class
205 subunits, edges = build_xlinks_graph(xlinks_dict)
206 self.set_components_and_connections(subunits, edges)
208 def get_docking_order(self):
209 """ return the order to dock components from the cross links """
211 degs = self.G.degree(self.G.nodes())
212 log.debug(
"Degrees: %s",degs )
213 sorted_degrees = [(v,k)
for v,k
in zip(degs.values(),degs.keys())]
214 sorted_degrees.sort()
215 sorted_degrees.reverse()
217 receptors_considered = []
218 for degree, node
in sorted_degrees:
219 for n
in self.G.neighbors(node):
220 if not n
in receptors_considered:
221 docking_pairs.append((node, n))
222 receptors_considered.append(node)
223 log.info(
"The suggested order for the docking pairs is %s",
230 Puts two subunits together using the Xlinkins restraints. The solutions
231 offered by this class are just an initial position of the components
234 def clear_xlinks(self):
235 self.xlinks_list = []
239 Sets the xlinks used for the computation fo the intial rough
241 @param xlinks_list A list of Xlink classes
242 residue1 belongs to the receptor and residue2 belongs to the ligand
244 self.xlinks_list = xlinks_list
246 def set_pdbs(self, fn_receptor, fn_ligand):
248 Set the name of the PDB files of the receptor and the ligand
254 self.h_receptor = atom.read_pdb(fn_receptor, self.m_receptor, sel)
256 self.h_ligand = atom.read_pdb(fn_ligand, self.m_ligand, sel)
260 Set the hierarchies (atom.Hierarchy objects) for the receptor and
265 self.h_receptor = h_receptor
266 self.h_ligand = h_ligand
270 Sets the rigid bodies (core.RigidBody objects) for the receptor and
275 self.rb_receptor = rb_receptor
276 self.rb_ligand = rb_ligand
280 Movest the ligand close to the receptor based on the xlinks
281 provided by set_xlinks()
283 log.debug(
"Moving ligand close to the receptor using the xlinks")
284 n = len(self.xlinks_list)
292 Get the particle representing a residue in a hierarchy
293 @param h atom.Hierarchy containing the residue
294 @param ch The chain id
295 @param res index of the residue
298 return s.get_selected_particles()[0]
302 Get the coordinates for a residue in a molecular hierarchy
303 @param h atom.Hierarchy object
304 @param ch The chain id
305 @param res Residue index
308 return core.XYZ(p).get_coordinates()
312 Write a pdb file the coordinates of the ligand
315 atom.write_pdb(self.h_ligand, fn)
319 Put the residues in a random distance between 0 and the maximum
320 cross-linkin distance
322 xl = self.xlinks_list[0]
325 sph = alg.Sphere3D(center, xl.distance)
326 v = alg.get_random_vector_in(sph)
327 ref = self.rb_ligand.get_reference_frame()
328 coords = ref.get_transformation_to().get_translation()
329 R = ref.get_transformation_to().get_rotation()
332 log.debug(
"Ligand residue before moving %s", lig)
333 displacement = v - lig
334 T = alg.Transformation3D(R, coords + displacement)
335 self.rb_ligand.set_reference_frame(alg.ReferenceFrame3D(T))
338 log.debug(
"ligand after moving %s", new_coords)
343 Function equivalent to move_one_xlink() for the case where there
344 are more than one cross-link restraints available.
345 Puts the ligand residues as close as possible to the receptor
350 for xl
in self.xlinks_list:
357 log.debug(
"Receptor residues before moving %s", rec_coords)
358 log.debug(
"Ligand residues before moving %s", lig_coords)
359 ref = self.rb_ligand.get_reference_frame()
360 Tr = alg.get_transformation_aligning_first_to_second(lig_coords,
362 T = ref.get_transformation_to()
363 newT = alg.compose(Tr, T)
364 self.rb_ligand.set_reference_frame(alg.ReferenceFrame3D(newT))
366 moved_lig_coords = []
367 for xl
in self.xlinks_list:
370 moved_lig_coords.append(c)
371 log.debug(
"Ligand residues after moving %s", moved_lig_coords)