IMP logo
IMP Reference Guide  2.16.0
The Integrative Modeling Platform
precision_rmsd.py
1 from __future__ import print_function
2 import os
3 import numpy
4 import IMP
5 import IMP.em
6 import pyRMSD.RMSDCalculator
7 
8 
9 def parse_custom_ranges(ranges_file):
10  if not ranges_file:
11  return []
12  with open(ranges_file) as fh:
13  d = {}
14  exec(fh.read(), d)
15  return d['density_custom_ranges']
16 
17 
18 def get_particles_from_superposed(
19  cluster_conform_i, cluster_conform_0, align, ps, trans):
20  def _to_vector3ds(numpy_array):
21  # No need to fit the whole array - we only need 4 non-coplanar points,
22  # so 100 should be plenty
23  return [IMP.algebra.Vector3D(c) for c in numpy_array[:100]]
24 
25  if align:
26  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
27  "QCP_SERIAL_CALCULATOR",
28  numpy.array([cluster_conform_0, cluster_conform_i]))
29  else:
30  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
31  "NOSUP_SERIAL_CALCULATOR",
32  numpy.array([cluster_conform_0, cluster_conform_i]))
33 
34  rmsd, superposed_fit = calculator.pairwise(
35  0, 1, get_superposed_coordinates=True)
36  # Get transformation from pyRMSD reference on the first call.
37  # This is somewhat inefficient (since we are essentially repeating
38  # the pyRMSD calculation) but pyRMSD doesn't appear to make its
39  # reference orientation available.
40  if trans is None:
42  _to_vector3ds(superposed_fit[0]), _to_vector3ds(cluster_conform_0))
43 
44  for particle_index in range(len(superposed_fit[1])):
45  # Transform from pyRMSD back to original reference
46  IMP.core.XYZ(ps[particle_index]).set_coordinates(
47  trans * IMP.algebra.Vector3D(superposed_fit[1][particle_index]))
48 
49  return rmsd, ps, trans
50 
51 
52 def get_particles_from_superposed_amb(
53  cluster_conform_i, cluster_conform_0, align, ps, trans, symm_groups):
54 
55  '''Modified superposed function to work with symmetric copies'''
56 
57  def _to_vector3ds(numpy_array):
58  # No need to fit the whole array - we only need 4 non-coplanar points,
59  # so 100 should be plenty
60  return [IMP.algebra.Vector3D(c) for c in numpy_array[:100]]
61 
62  min_rmsd = 10000.0
63 
64  superposed_final_coords = []
65 
66  for perm in pyRMSD.symmTools.symm_permutations(symm_groups):
67  # for each permutation
68 
69  new_cluster_conform_i = cluster_conform_i
70 
71  for sg in perm: # for each symmetric group in perm
72 
73  for [particle0, particle1] in sg:
74  # swap the particles if they are in non-standard order
75  # in this permutation
76  if particle0 > particle1:
77  pyRMSD.symmTools.swap_atoms(
78  new_cluster_conform_i, particle0, particle1)
79 
80  if align:
81  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
82  "QCP_SERIAL_CALCULATOR",
83  numpy.array([cluster_conform_0, new_cluster_conform_i]))
84  else:
85  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
86  "NOSUP_SERIAL_CALCULATOR",
87  numpy.array([cluster_conform_0, new_cluster_conform_i]))
88 
89  rmsd, superposed_fit = calculator.pairwise(
90  0, 1, get_superposed_coordinates=True)
91 
92  if rmsd < min_rmsd:
93  min_rmsd = rmsd
94  superposed_final_coords = superposed_fit
95 
96  # Get transformation from pyRMSD reference on the first call.
97  # This is somewhat inefficient (since we are essentially repeating
98  # the pyRMSD calculation) but pyRMSD doesn't appear to make its
99  # reference orientation available.
100  if trans is None:
102  _to_vector3ds(superposed_final_coords[0]),
103  _to_vector3ds(cluster_conform_0))
104 
105  for particle_index in range(len(superposed_final_coords[1])):
106 
107  # Transform from pyRMSD back to original reference
108  IMP.core.XYZ(ps[particle_index]).set_coordinates(
109  trans * IMP.algebra.Vector3D(
110  superposed_final_coords[1][particle_index]))
111 
112  return min_rmsd, ps, trans
113 
114 
115 class GetModelDensity(object):
116  """Compute mean density maps from structures.
117  Keeps a dictionary of density maps,
118  keys are in the custom ranges. When you call add_subunits_density, it adds
119  particle coordinates to the existing density maps.
120  """
121 
122  def __init__(self, custom_ranges=None, resolution=20.0, voxel=5.0,
123  bead_names=None):
124  """Constructor.
125  @param list of particles decorated with mass, radius, and XYZ
126  @param resolution The MRC resolution of the output map
127  (in Angstrom unit)
128  @param voxel The voxel size for the output map (lower is slower)
129  """
130 
131  self.MRCresolution = resolution
132  self.voxel = voxel
133  self.count_models = 0.0
134  self.densities = {}
135  self.bead_names = bead_names
136  self.custom_ranges = custom_ranges
137 
138  # for each custom range get the particle indices that will be
139  # added to the density for that custom range
140  self.particle_indices_in_custom_ranges = {}
141 
142  for density_name in self.custom_ranges:
143  self.particle_indices_in_custom_ranges[density_name] = []
144 
145  # go through each bead, put it in the appropriate custom range(s)
146  for index, beadname in enumerate(self.bead_names):
147  for density_name in self.custom_ranges:
148  # each domain in the list custom_ranges[density_name]
149  for domain in self.custom_ranges[density_name]:
150  if self._is_contained(beadname, domain):
151  self.particle_indices_in_custom_ranges[
152  density_name].append(index)
153  break # already added particle to this custom range
154 
155  def normalize_density(self):
156  pass
157 
158  def _create_density_from_particles(self, ps, name,
159  kernel_type='GAUSSIAN'):
160  '''Internal function for adding to densities.
161  pass XYZR particles with mass and create a density from them.
162  kernel type options are GAUSSIAN, BINARIZED_SPHERE, and SPHERE.'''
163  dmap = IMP.em.SampledDensityMap(ps, self.MRCresolution, self.voxel)
164  dmap.calcRMS()
165  dmap.set_was_used(True)
166 
167  if name not in self.densities:
168  self.densities[name] = dmap
169  else:
170  bbox1 = IMP.em.get_bounding_box(self.densities[name])
171  bbox2 = IMP.em.get_bounding_box(dmap)
172  bbox1 += bbox2
173  dmap3 = IMP.em.create_density_map(bbox1, self.voxel)
174  dmap3.set_was_used(True)
175  dmap3.add(dmap)
176  dmap3.add(self.densities[name])
177  self.densities[name] = dmap3
178 
179  def _is_contained(self, bead_name, domain):
180  """ domain can be the name of a single protein or a tuple
181  (start_residue,end_residue,protein_name)
182  bead is a string of type moleculeName_startResidue_endResidue
183  """
184 
185  (bead_protein, bead_res_start,
186  bead_res_end, bead_copy) = bead_name.split("_")
187 
188  # protein name and copy number check
189  if isinstance(domain, tuple):
190  domain_protein = domain[2]
191  else:
192  domain_protein = domain
193  # A period indicates that we have a copy number
194  if "." in domain_protein:
195  spl = domain_protein.split(".")
196  domain_protein = spl[0]
197  domain_copy = int(spl[1])
198  else:
199  domain_copy = bead_copy = -1
200 
201  if bead_protein != domain_protein or int(bead_copy) != domain_copy:
202  return False
203 
204  # residue range check
205  if isinstance(domain, tuple):
206  bead_residues = set(range(int(bead_res_start),
207  int(bead_res_end)+1))
208  domain_residues = set(range(int(domain[0]),
209  int(domain[1])+1))
210  return not domain_residues.isdisjoint(bead_residues)
211  else:
212  return True
213 
214  def add_subunits_density(self, ps):
215  """Add a frame to the densities.
216  @param ps List of particles decorated with XYZR and Mass.
217  """
218  self.count_models += 1.0
219  # initialize custom list of particles
220  particles_custom_ranges = {}
221  for density_name in self.custom_ranges:
222  particles_custom_ranges[density_name] = []
223 
224  # add each particle to the relevant custom list
225  for density_name in self.custom_ranges:
226  for particle_index \
227  in self.particle_indices_in_custom_ranges[density_name]:
228  particles_custom_ranges[density_name].append(
229  ps[particle_index])
230 
231  # finally, add each custom particle list to the density
232  for density_name in self.custom_ranges:
233  self._create_density_from_particles(
234  particles_custom_ranges[density_name], density_name)
235 
236  def get_density_keys(self):
237  return list(self.densities.keys())
238 
239  def get_density(self, name):
240  """Get the current density for some component name"""
241  if name not in self.densities:
242  return None
243  else:
244  return self.densities[name]
245 
246  def write_mrc(self, path=".", file_prefix=""):
247  for density_name in self.densities:
248  mrc = os.path.join(path, file_prefix + "_" + density_name + ".mrc")
249  self.densities[density_name].multiply(1. / self.count_models)
250  IMP.em.write_map(
251  self.densities[density_name], mrc,
253  if len(self.densities) == 1:
254  return mrc
255  else:
256  return os.path.join(path, file_prefix + "_*.mrc")
def get_density
Get the current density for some component name.
Compute mean density maps from structures.
Class for sampling a density map from particles.
DensityMap * multiply(const DensityMap *m1, const DensityMap *m2)
Return a density map for which voxel i contains the result of m1[i]*m2[i].
DensityMap * create_density_map(const IMP::algebra::GridD< 3, S, V, E > &arg)
Create a density map from an arbitrary IMP::algebra::GridD.
Definition: DensityMap.h:650
def add_subunits_density
Add a frame to the densities.
Basic utilities for handling cryo-electron microscopy 3D density maps.
A decorator for a particle with x,y,z coordinates.
Definition: XYZ.h:30
algebra::BoundingBoxD< 3 > get_bounding_box(const DensityMap *m)
Definition: DensityMap.h:481
VectorD< 3 > Vector3D
Definition: VectorD.h:421
Transformation3D get_transformation_aligning_first_to_second(Vector3Ds a, Vector3Ds b)