IMP logo
IMP Reference Guide  2.20.2
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 from IMP.sampcon.rmsd_calculation import parse_symm_groups_for_pyrmsd
8 
9 
10 def parse_custom_ranges(ranges_file):
11  if not ranges_file:
12  return []
13  with open(ranges_file) as fh:
14  d = {}
15  exec(fh.read(), d)
16  return d['density_custom_ranges']
17 
18 
19 def get_particles_from_superposed(
20  cluster_conform_i, cluster_conform_0, align, ps, trans,
21  symm_groups=None):
22  def _to_vector3ds(numpy_array):
23  # No need to fit the whole array - we only need 4 non-coplanar points,
24  # so 100 should be plenty
25  return [IMP.algebra.Vector3D(c) for c in numpy_array[:100]]
26 
27  if align:
28  calculator_name = "QCP_SERIAL_CALCULATOR"
29  else:
30  calculator_name = "NOSUP_SERIAL_CALCULATOR"
31 
32  conforms = numpy.array([cluster_conform_0, cluster_conform_i])
33 
34  if symm_groups is None:
35  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
36  calculator_name,
37  conforms)
38  else:
39  if calculator_name == 'NOSUP_SERIAL_CALCULATOR':
40  # calc_symm_groups are enough without any fitting
41  s1 = parse_symm_groups_for_pyrmsd(symm_groups)
42  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
43  calculator_name,
44  fittingCoordsets=conforms,
45  calculationCoordsets=conforms,
46  calcSymmetryGroups=s1,
47  fitSymmetryGroups=[])
48  else:
49  calculator = pyRMSD.RMSDCalculator.RMSDCalculator(
50  calculator_name,
51  fittingCoordsets=conforms,
52  calcSymmetryGroups=[],
53  fitSymmetryGroups=symm_groups)
54 
55  check1 = (calculator_name == 'NOSUP_SERIAL_CALCULATOR')
56  check2 = symm_groups is not None
57  if check1 and check2:
58  # superposed calc_coords returned if calc-coords
59  # specified while creating the calculator
60  rmsd, superposed_fit, _calc_fit = calculator.pairwise(
61  0, 1, get_superposed_coordinates=True)
62  else:
63  rmsd, superposed_fit = calculator.pairwise(
64  0, 1, get_superposed_coordinates=True)
65  # Get transformation from pyRMSD reference on the first call.
66  # This is somewhat inefficient (since we are essentially repeating
67  # the pyRMSD calculation) but pyRMSD doesn't appear to make its
68  # reference orientation available.
69  if trans is None:
71  _to_vector3ds(superposed_fit[0]), _to_vector3ds(cluster_conform_0))
72 
73  for particle_index in range(len(superposed_fit[1])):
74  # Transform from pyRMSD back to original reference
75  IMP.core.XYZ(ps[particle_index]).set_coordinates(
76  trans * IMP.algebra.Vector3D(superposed_fit[1][particle_index]))
77 
78  return rmsd, ps, trans
79 
80 
81 class GetModelDensity(object):
82  """Compute mean density maps from structures.
83  Keeps a dictionary of density maps,
84  keys are in the custom ranges. When you call add_subunits_density, it adds
85  particle coordinates to the existing density maps.
86  """
87 
88  def __init__(self, custom_ranges=None, resolution=20.0, voxel=5.0,
89  bead_names=None):
90  """Constructor.
91  @param list of particles decorated with mass, radius, and XYZ
92  @param resolution The MRC resolution of the output map
93  (in Angstrom unit)
94  @param voxel The voxel size for the output map (lower is slower)
95  """
96 
97  self.MRCresolution = resolution
98  self.voxel = voxel
99  self.count_models = 0.0
100  self.densities = {}
101  self.bead_names = bead_names
102  self.custom_ranges = custom_ranges
103 
104  # for each custom range get the particle indices that will be
105  # added to the density for that custom range
106  self.particle_indices_in_custom_ranges = {}
107 
108  for density_name in self.custom_ranges:
109  self.particle_indices_in_custom_ranges[density_name] = []
110 
111  # go through each bead, put it in the appropriate custom range(s)
112  for index, beadname in enumerate(self.bead_names):
113  for density_name in self.custom_ranges:
114  # each domain in the list custom_ranges[density_name]
115  for domain in self.custom_ranges[density_name]:
116  if self._is_contained(beadname, domain):
117  self.particle_indices_in_custom_ranges[
118  density_name].append(index)
119  break # already added particle to this custom range
120 
121  def normalize_density(self):
122  pass
123 
124  def _create_density_from_particles(self, ps, name,
125  kernel_type='GAUSSIAN'):
126  '''Internal function for adding to densities.
127  pass XYZR particles with mass and create a density from them.
128  kernel type options are GAUSSIAN, BINARIZED_SPHERE, and SPHERE.'''
129  dmap = IMP.em.SampledDensityMap(ps, self.MRCresolution, self.voxel)
130  dmap.calcRMS()
131  dmap.set_was_used(True)
132 
133  if name not in self.densities:
134  self.densities[name] = dmap
135  else:
136  bbox1 = IMP.em.get_bounding_box(self.densities[name])
137  bbox2 = IMP.em.get_bounding_box(dmap)
138  bbox1 += bbox2
139  dmap3 = IMP.em.create_density_map(bbox1, self.voxel)
140  dmap3.set_was_used(True)
141  dmap3.add(dmap)
142  dmap3.add(self.densities[name])
143  self.densities[name] = dmap3
144 
145  def _is_contained(self, bead_name, domain):
146  """ domain can be the name of a single protein or a tuple
147  (start_residue,end_residue,protein_name)
148  bead is a string of type moleculeName_startResidue_endResidue
149  """
150 
151  (bead_protein, bead_res_start,
152  bead_res_end, bead_copy) = bead_name.rsplit("_", 3)
153 
154  # protein name and copy number check
155  if isinstance(domain, tuple):
156  domain_protein = domain[2]
157  else:
158  domain_protein = domain
159  # A period indicates that we have a copy number
160  if "." in domain_protein:
161  spl = domain_protein.split(".")
162  domain_protein = spl[0]
163  domain_copy = int(spl[1])
164  else:
165  domain_copy = bead_copy = -1
166 
167  if bead_protein != domain_protein or int(bead_copy) != domain_copy:
168  return False
169 
170  # residue range check
171  if isinstance(domain, tuple):
172  bead_residues = set(range(int(bead_res_start),
173  int(bead_res_end)+1))
174  domain_residues = set(range(int(domain[0]),
175  int(domain[1])+1))
176  return not domain_residues.isdisjoint(bead_residues)
177  else:
178  return True
179 
180  def add_subunits_density(self, ps):
181  """Add a frame to the densities.
182  @param ps List of particles decorated with XYZR and Mass.
183  """
184  self.count_models += 1.0
185  # initialize custom list of particles
186  particles_custom_ranges = {}
187  for density_name in self.custom_ranges:
188  particles_custom_ranges[density_name] = []
189 
190  # add each particle to the relevant custom list
191  for density_name in self.custom_ranges:
192  for particle_index \
193  in self.particle_indices_in_custom_ranges[density_name]:
194  particles_custom_ranges[density_name].append(
195  ps[particle_index])
196 
197  # finally, add each custom particle list to the density
198  for density_name in self.custom_ranges:
199  self._create_density_from_particles(
200  particles_custom_ranges[density_name], density_name)
201 
202  def get_density_keys(self):
203  return list(self.densities.keys())
204 
205  def get_density(self, name):
206  """Get the current density for some component name"""
207  if name not in self.densities:
208  return None
209  else:
210  return self.densities[name]
211 
212  def write_mrc(self, path=".", file_prefix=""):
213  densmap = {}
214  for density_name in self.densities:
215  mrc = os.path.join(path, file_prefix + "_" + density_name + ".mrc")
216  self.densities[density_name].multiply(1. / self.count_models)
217  densmap[density_name] = mrc
218  IMP.em.write_map(
219  self.densities[density_name], mrc,
221  return densmap
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:674
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:505
VectorD< 3 > Vector3D
Definition: VectorD.h:425
Transformation3D get_transformation_aligning_first_to_second(Vector3Ds a, Vector3Ds b)