IMP logo
IMP Reference Guide  develop.6f18bfa751,2025/09/20
The Integrative Modeling Platform
bayesianem/restraint.py
1 """@namespace IMP.bayesianem
2 Restraints for handling electron microscopy maps.
3 """
4 
5 from __future__ import print_function
6 import IMP
7 import IMP.core
8 import IMP.algebra
9 import IMP.atom
10 import IMP.container
11 import IMP.bayesianem
12 import IMP.isd
13 import IMP.pmi.tools
14 import IMP.pmi.mmcif
15 import IMP.isd.gmm_tools
16 from math import sqrt
17 
18 
20  """Fit Gaussian-decorated particles to an EM map
21  (also represented with a set of Gaussians)
22  \note This class wraps an IMP::bayesianem::GaussianEMRestraint
23  """
24  def __init__(self, densities,
25  target_fn='',
26  target_ps=[],
27  cutoff_dist_model_model=10.0,
28  cutoff_dist_model_data=10.0,
29  target_mass_scale=1.0,
30  target_mass=None,
31  target_radii_scale=3.0,
32  model_radii_scale=1.0,
33  slope=0.0,
34  spherical_gaussians=False,
35  close_pair_container=None,
36  backbone_slope=False,
37  scale_target_to_mass=False,
38  weight=1.0,
39  target_is_rigid_body=False):
40  """Constructor.
41  @param densities The Gaussian-decorated particles to be restrained
42  @param target_fn GMM file of the target density map
43  (alternatively, pass the ps)
44  @param target_ps List of Gaussians of the target map
45  (alternatively, pass the filename)
46  @param cutoff_dist_model_model Distance in model-model close
47  pair container
48  @param cutoff_dist_model_data Distance in model-data close pair
49  container. Usually can set to zero because we multiply the
50  target radii
51  @param target_mass_scale Scale up the target densities so that
52  the mass is accurate.
53  Needed if the GMM you generated was not already scaled.
54  To make it the same as model mass, set scale_to_target_mass=True
55  @param target_mass Sets the mass of the target density to the given
56  value. Default is None. This will override target_mass_scale
57  argument
58  @param target_radii_scale Scale the target density radii -
59  only used for the close pair container.
60  If you keep this at 3.0 or so you don't have to use cutoff dist.
61  @param model_radii_scale Scale the model density radii - only used
62  for the close pair container
63  @param slope Linear term added to help bring model into the density
64  @param spherical_gaussians Set to True for a speed bonus when
65  the model densities are spheres. (This means you don't have
66  to do a matrix multiplication if they rotate.)
67  @param close_pair_container Pass a close pair container for
68  the model if you already have one (e.g. for an excluded
69  volume restraint.) May give a speed bonus.
70  @param backbone_slope Only apply slope to backbone particles -
71  only matters for atomic
72  @param scale_target_to_mass Set True if you would need to scale
73  target to EXACTLY the model mass
74  @param weight The restraint weight
75  @param target_is_rigid_body Set True if you want to put the target
76  density particles into a rigid body that need to be sampled
77  (e.g.,when you need to fit one density against another one).
78  Default is False.
79  """
80  # some parameters
81  self.label = "None"
82  self.sigmaissampled = True
83  self.sigmamaxtrans = 0.3
84  self.sigmamin = 0.0001
85  self.sigmamax = 100.0
86  self.sigmainit = 1.0
87  self.label = "None"
88  self.densities = densities
89  self.em_root_hier = None
90 
91  # setup target GMM
92  self.m = self.densities[0].get_model()
93 
94  if scale_target_to_mass:
95  def hierarchy_mass(h):
96  leaves = IMP.atom.get_leaves(h)
97  return sum(IMP.atom.Mass(p).get_mass() for p in leaves)
98  target_mass = sum(hierarchy_mass(h) for h in densities)
99  print('will scale target mass by', target_mass_scale)
100 
101  if target_fn != '':
102  self._set_dataset(target_fn)
103  self.target_ps = []
105  target_fn,
106  self.target_ps,
107  self.m,
108  radius_scale=target_radii_scale,
109  mass_scale=target_mass_scale)
110  elif target_ps != []:
111  self.target_ps = target_ps
112  else:
113  print('Gaussian EM restraint: must provide target density file '
114  'or properly set up target densities')
115  return
116 
117  if target_mass:
118  tmass = sum([IMP.atom.Mass(p).get_mass() for p in self.target_ps])
119  scale = target_mass / tmass
120  print('will set target mass to', target_mass, tmass, scale)
121  for p in self.target_ps:
122  ms = IMP.atom.Mass(p).get_mass()
123  IMP.atom.Mass(p).set_mass(ms*scale)
124 
125  for p, state in IMP.pmi.tools._all_protocol_outputs(densities[0]):
126  p.add_em3d_restraint(state, self.target_ps, self.densities, self)
127 
128  # setup model GMM
129  self.model_ps = []
130  for h in self.densities:
131  self.model_ps += [k.get_particle() for k in IMP.atom.get_leaves(h)]
132  if model_radii_scale != 1.0:
133  for p in self.model_ps:
134  rmax = sqrt(max(IMP.core.Gaussian(p).get_variances())) * \
135  model_radii_scale
136  if not IMP.core.XYZR.get_is_setup(p):
138  else:
139  IMP.core.XYZR(p).set_radius(rmax)
140  # wrap target particles in rigid body if requested
141  if target_is_rigid_body:
142  self.rb = IMP.atom.create_rigid_body(self.target_ps)
143  else:
144  self.rb = None
145 
146  # sigma particle
147  self.sigmaglobal = IMP.pmi.tools.SetupNuisance(
148  self.m, self.sigmainit, self.sigmamin, self.sigmamax,
149  self.sigmaissampled).get_particle()
150 
151  # create restraint
152  print('target num particles', len(self.target_ps),
153  'total weight', sum([IMP.atom.Mass(p).get_mass()
154  for p in self.target_ps]))
155  print('model num particles', len(self.model_ps),
156  'total weight', sum([IMP.atom.Mass(p).get_mass()
157  for p in self.model_ps]))
158 
159  update_model = not spherical_gaussians
160  self.gaussianEM_restraint = IMP.bayesianem.GaussianEMRestraint(
161  self.m,
162  IMP.get_indexes(self.model_ps),
163  IMP.get_indexes(self.target_ps),
164  self.sigmaglobal.get_particle().get_index(),
165  cutoff_dist_model_model,
166  cutoff_dist_model_data,
167  slope,
168  update_model, backbone_slope)
169  if target_fn != '':
170  self.gaussianEM_restraint.set_density_filename(target_fn)
171 
172  print('done EM setup')
173  self.rs = IMP.RestraintSet(self.m, 'GaussianEMRestraint')
174  self.rs.add_restraint(self.gaussianEM_restraint)
175  self.set_weight(weight)
176 
177  def _set_dataset(self, target_fn):
178  """Set the dataset to point to the input file"""
180  self.dataset = p.parse_file(target_fn)['dataset']
181 
183  '''
184  aligns the center of mass of the target GMM on the center of mass
185  of the model
186  '''
187  target_com = IMP.algebra.Vector3D(0, 0, 0)
188  target_mass = 0.0
189  for p in self.target_ps:
190  mass = IMP.atom.Mass(p).get_mass()
191  pos = IMP.core.XYZ(p).get_coordinates()
192  target_com += pos * mass
193  target_mass += mass
194  target_com /= target_mass
195  print('target com', target_com)
196  model_com = IMP.algebra.Vector3D(0, 0, 0)
197  model_mass = 0.0
198  for p in self.model_ps:
199  mass = IMP.atom.Mass(p).get_mass()
200  pos = IMP.core.XYZ(p).get_coordinates()
201  model_com += pos * mass
202  model_mass += mass
203  model_com /= model_mass
204  print('model com', model_com)
205 
206  v = target_com - model_com
207  print('translating with', -v)
209  for p in self.target_ps:
210  IMP.core.transform(IMP.core.RigidBody(p), transformation)
211  # IMP.pmi.tools.translate_hierarchies(self.densities,v)
212 
213  def get_center_of_mass(self, target=True):
214  '''Returns the geometric center of the GMM particles
215  @param target = True - returns target map gmm COM
216  @param target = False - returns model gmm COM'''
217  com = IMP.algebra.Vector3D(0, 0, 0)
218  total_mass = 0.0
219  if target:
220  ps = self.target_ps
221  else:
222  ps = self.model_ps
223  for p in ps:
224  mass = IMP.atom.Mass(p).get_mass()
225  pos = IMP.core.XYZ(p).get_coordinates()
226  com += pos * mass
227  total_mass += mass
228  com /= total_mass
229  return com
230 
232  '''
233  aligns the center of mass of the target GMM on the origin
234  '''
235  target_com = self.get_center_of_mass()
236  print('target com', target_com)
237  model_com = IMP.algebra.Vector3D(0, 0, 0)
238  print('model com', model_com)
239  v = target_com - model_com
240  print('translating with', -v)
242  for p in self.target_ps:
243  IMP.core.transform(IMP.core.RigidBody(p), transformation)
244  # IMP.pmi.tools.translate_hierarchies(self.densities,v)
245 
246  def center_model_on_target_density(self, input_object):
247  '''
248  aligns the model on the target density
249  @param input_objects IMP.pmi.representation.Representation
250  or IMP.pmi.topology.State
251  '''
252  if type(input_object) is IMP.pmi.representation.Representation:
253  hier = input_object.prot
254  elif type(input_object) is IMP.pmi.topology.State:
255  hier = input_object.get_hierarchy()
256  else:
257  raise Exception("Input must be a Representation or "
258  "topology.State object")
259  target_com = self.get_center_of_mass()
260  print('target com', target_com)
261  model_com = self.get_center_of_mass(target=False)
262  print('model com', model_com)
263  v = target_com - model_com
264  print('translating with', v)
266 
267  rigid_bodies = set()
268  XYZRs = set()
269 
270  for p in IMP.atom.get_leaves(hier):
272  rb = IMP.core.RigidBodyMember(p).get_rigid_body()
273  rigid_bodies.add(rb)
275  XYZRs.add(p)
276 
277  for rb in list(rigid_bodies):
278  IMP.core.transform(rb, transformation)
279 
280  for p in list(XYZRs):
281  IMP.core.transform(IMP.core.XYZ(p), transformation)
282 
284  '''
285  align the model on target GMM
286  '''
287  target_com = self.get_center_of_mass()
288  print('target com', target_com)
289  model_com = self.get_center_of_mass(target=False)
290  print('model com', model_com)
291  v = target_com - model_com
292  print('translating with', v)
294 
295  rigid_bodies = set()
296  XYZRs = set()
297 
298  for p in self.model_ps:
300  rb = IMP.core.RigidBodyMember(p).get_rigid_body()
301  rigid_bodies.add(rb)
303  XYZRs.add(p)
304 
305  for rb in list(rigid_bodies):
306  IMP.core.transform(rb, transformation)
307 
308  for p in list(XYZRs):
309  IMP.core.transform(IMP.core.XYZ(p), transformation)
310 
311  def set_weight(self, weight):
312  '''
313  set the weight of the restraint
314  @param weight
315  '''
316  self.weight = weight
317  self.rs.set_weight(weight)
318 
319  def set_label(self, label):
320  self.label = label
321 
322  def add_to_model(self):
323  IMP.pmi.tools.add_restraint_to_model(self.m, self.rs)
324 
325  def get_particles_to_sample(self):
326  ps = {}
327  if self.sigmaissampled:
328  ps["Nuisances_GaussianEMRestraint_sigma_" +
329  self.label] = ([self.sigmaglobal], self.sigmamaxtrans)
330  if self.rb:
331  ps["Rigid_Bodies_GaussianEMRestraint_"+self.label] = (
332  [self.rb],
333  4,
334  0.25)
335  return ps
336 
337  def get_rigid_body(self):
338  if self.rb is None:
339  raise Exception("No rigid body created for GMM particles. "
340  "Ensure target_is_rigid_body is set to True")
341  return self.rb
342 
344  '''
345  returns a hierarchy whose leaves are the gaussian particles
346  of the target GMM
347  '''
348  if self.em_root_hier is None:
349  self.em_root_hier = IMP.atom.Copy.setup_particle(
350  IMP.Particle(self.m), 0)
351  self.em_root_hier.set_name(
352  "GaussianEMRestraint_density_" + self.label)
353  for p in self.target_ps:
354  self.em_root_hier.add_child(p)
355  return self.em_root_hier
356 
358  ''' Can add a target GMM to a Hierarchy.
359  For PMI2 a state object may also be passed'''
360  if type(inp) is IMP.pmi.topology.State:
361  inp.get_hierarchy().add_child(self.get_density_as_hierarchy())
362  elif type(inp) is IMP.atom.Hierarchy:
363  inp.add_child(self.get_density_as_hierarchy())
364  else:
365  raise Exception("Can only add a density to a PMI State object "
366  "or IMP.atom.Hierarchy. You passed a", type(inp))
367 
368  def get_restraint_set(self):
369  return self.rs
370 
371  def get_output(self):
372  self.m.update()
373  output = {}
374  score = self.weight * self.rs.unprotected_evaluate(None)
375  output["_TotalScore"] = str(score)
376  output["GaussianEMRestraint_" +
377  self.label] = str(score)
378  output["GaussianEMRestraint_sigma_" +
379  self.label] = str(self.sigmaglobal.get_scale())
380  return output
381 
382  def evaluate(self):
383  return self.weight * self.rs.unprotected_evaluate(None)
384 
385  def write_target_gmm_to_mrc(self, fileout=None, voxel_size=5.0):
386  '''Writes target GMM file to MRC'''
387  if fileout is None:
388  fileout = "Gaussian_map_" + self.label + ".mrc"
389  IMP.isd.gmm_tools.write_gmm_to_map(self.target_ps, fileout, voxel_size)
390  return fileout
Tools for handling Gaussian Mixture Models.
Definition: gmm_tools.py:1
Add mass to a particle.
Definition: Mass.h:23
Simple 3D transformation class.
Support for the mmCIF file format.
Definition: mmcif.py:1
def get_center_of_mass
Returns the geometric center of the GMM particles.
Restraints for handling electron microscopy maps.
A member of a rigid body, it has internal (local) coordinates.
Definition: rigid_bodies.h:634
def center_model_on_target_density
aligns the model on the target density
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: rigid_bodies.h:635
Various classes to hold sets of particles.
static XYZR setup_particle(Model *m, ParticleIndex pi)
Definition: XYZR.h:48
def center_target_density_on_origin
aligns the center of mass of the target GMM on the origin
double get_mass(ResidueType c)
Get the mass from the residue type.
def add_target_density_to_hierarchy
Can add a target GMM to a Hierarchy.
Miscellaneous utilities.
Definition: pmi/tools.py:1
Creates a restraint between two Gaussian Mixture Models, "model" and "density".
Extract metadata from an EM density GMM file.
Definition: mmcif.py:1785
Fit Gaussian-decorated particles to an EM map (also represented with a set of Gaussians) ...
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: XYZR.h:47
Object used to hold a set of restraints.
Definition: RestraintSet.h:41
ParticleIndexPairs get_indexes(const ParticlePairsTemp &ps)
Get the indexes from a list of particle pairs.
The standard decorator for manipulating molecular structures.
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
void transform(XYZ a, const algebra::Transformation3D &tr)
Apply a transformation to the particle.
def add_restraint_to_model
Add a PMI restraint to the model.
Definition: pmi/tools.py:84
def center_on_target_density
align the model on target GMM
A decorator for a particle with x,y,z coordinates.
Definition: XYZ.h:30
def write_gmm_to_map
write density map from GMM.
Definition: gmm_tools.py:118
static Copy setup_particle(Model *m, ParticleIndex pi, Int number)
Create a decorator for the numberth copy.
Definition: Copy.h:42
Basic functionality that is expected to be used by a wide variety of IMP users.
General purpose algebraic and geometric methods that are expected to be used by a wide variety of IMP...
def write_target_gmm_to_mrc
Writes target GMM file to MRC.
The general base class for IMP exceptions.
Definition: exception.h:48
VectorD< 3 > Vector3D
Definition: VectorD.h:408
IMP::core::RigidBody create_rigid_body(Hierarchy h)
Class to handle individual particles of a Model object.
Definition: Particle.h:43
Stores a list of Molecules all with the same State index.
A decorator for a rigid body.
Definition: rigid_bodies.h:82
def center_target_density_on_model
aligns the center of mass of the target GMM on the center of mass of the model
Functionality for loading, creating, manipulating and scoring atomic structures.
Hierarchies get_leaves(const Selection &h)
def decorate_gmm_from_text
read the output from write_gmm_to_text, decorate as Gaussian and Mass
Definition: gmm_tools.py:22
def get_density_as_hierarchy
returns a hierarchy whose leaves are the gaussian particles of the target GMM
Inferential scoring building on methods developed as part of the Inferential Structure Determination ...
A decorator for a particle with x,y,z coordinates and a radius.
Definition: XYZR.h:27