IMP logo
IMP Reference Guide  develop.266d43d110,2026/09/24
The Integrative Modeling Platform
pmi/tools.py
1 #!/usr/bin/env python
2 
3 """@namespace IMP.pmi.tools
4  Miscellaneous utilities.
5 """
6 
7 import IMP
8 import IMP.algebra
9 import IMP.isd
10 import IMP.pmi
11 import IMP.pmi.topology
12 from collections.abc import MutableSet
13 import itertools
14 import math
15 import ast
16 from time import process_time
17 import RMF
18 import IMP.rmf
19 from collections import defaultdict, OrderedDict, namedtuple
20 import warnings
21 import numbers
22 
23 
24 # Keep track of JAX data (the actual model coordinates, plus the
25 # space we are working in)
26 _JAXData = namedtuple('_JAXData', ('model', 'space'))
27 
28 
29 def _get_system_for_hier(hier):
30  """Given a hierarchy, return the System that created it, or None"""
31  # If we are given the raw particle, get the corresponding Hierarchy
32  # decorator if available
33  if hier and not hasattr(hier, 'get_parent'):
35  hier = IMP.atom.Hierarchy(hier)
36  else:
37  return None
38  while hier:
39  # See if we labeled the Python object directly with the System
40  if hasattr(hier, '_pmi2_system'):
41  h = hier._pmi2_system()
42  if h:
43  return h
44  # Otherwise (maybe we got a new Python wrapper around the same C++
45  # object), try all extant systems
46  for s in IMP.pmi.topology.System._all_systems:
47  if s.hier == hier:
48  return s
49  # Try the next level up in the hierarchy
50  hier = hier.get_parent()
51 
52 
53 def _all_protocol_outputs(hier):
54  """Iterate over all (ProtocolOutput, State) pairs for the
55  given hierarchy"""
56  system = _get_system_for_hier(hier)
57  if system:
58  for state in system.states:
59  for p in state._protocol_output:
60  yield p
61 
62 
63 def _add_pmi_provenance(p):
64  """Tag the given particle as being created by the current version
65  of PMI."""
68  p, name="IMP PMI module", version=IMP.pmi.get_module_version(),
69  location="https://integrativemodeling.org")
71 
72 
73 def _get_restraint_set_keys():
74  if not hasattr(_get_restraint_set_keys, 'pmi_rs_key'):
75  _get_restraint_set_keys.pmi_rs_key = IMP.ModelKey("PMI restraints")
76  _get_restraint_set_keys.rmf_rs_key = IMP.ModelKey("RMF restraints")
77  return (_get_restraint_set_keys.pmi_rs_key,
78  _get_restraint_set_keys.rmf_rs_key)
79 
80 
81 def _add_restraint_sets(model, mk, mk_rmf):
82  rs = IMP.RestraintSet(model, "All PMI restraints")
83  rs_rmf = IMP.RestraintSet(model, "All PMI RMF restraints")
84  model.add_data(mk, rs)
85  model.add_data(mk_rmf, rs_rmf)
86  return rs, rs_rmf
87 
88 
89 def add_restraint_to_model(model, restraint, add_to_rmf=False):
90  """Add a PMI restraint to the model.
91  Since Model.add_restraint() no longer exists (in modern IMP restraints
92  should be added to a ScoringFunction instead) store them instead in
93  a RestraintSet, and keep a reference to it in the Model.
94 
95  If `add_to_rmf` is True, also add the restraint to a separate list
96  of restraints that will be written out to RMF files (by default, most
97  PMI restraints are not)."""
98  mk, mk_rmf = _get_restraint_set_keys()
99  if model.get_has_data(mk):
100  rs = IMP.RestraintSet.get_from(model.get_data(mk))
101  rs_rmf = IMP.RestraintSet.get_from(model.get_data(mk_rmf))
102  else:
103  rs, rs_rmf = _add_restraint_sets(model, mk, mk_rmf)
104  rs.add_restraint(restraint)
105  if add_to_rmf:
106  rs_rmf.add_restraint(restraint)
107 
108 
109 def get_restraint_set(model, rmf=False):
110  """Get a RestraintSet containing all PMI restraints added to the model.
111  If `rmf` is True, return only the subset of these restraints that
112  should be written out to RMF files."""
113  mk, mk_rmf = _get_restraint_set_keys()
114  if not model.get_has_data(mk):
115  warnings.warn("no restraints added to model yet",
117  _add_restraint_sets(model, mk, mk_rmf)
118  if rmf:
119  return IMP.RestraintSet.get_from(model.get_data(mk_rmf))
120  else:
121  return IMP.RestraintSet.get_from(model.get_data(mk))
122 
123 
124 class Stopwatch:
125  """Collect timing information.
126  Add an instance of this class to outputobjects to get timing information
127  in a stat file."""
128 
129  def __init__(self, isdelta=True):
130  """Constructor.
131  @param isdelta if True (the default) then report the time since the
132  last use of this class; if False, report cumulative time."""
133  self.starttime = process_time()
134  self.label = "None"
135  self.isdelta = isdelta
136 
137  def set_label(self, labelstr):
138  self.label = labelstr
139 
140  def get_output(self):
141  output = {}
142  if self.isdelta:
143  newtime = process_time()
144  output["Stopwatch_" + self.label + "_delta_seconds"] \
145  = str(newtime - self.starttime)
146  self.starttime = newtime
147  else:
148  output["Stopwatch_" + self.label + "_elapsed_seconds"] \
149  = str(process_time() - self.starttime)
150  return output
151 
152 
153 class SetupNuisance:
154 
155  def __init__(self, m, initialvalue, minvalue, maxvalue, isoptimized=True,
156  name=None):
157 
158  p = IMP.Particle(m)
159  if name:
160  p.set_name(name)
161  nuisance = IMP.isd.Scale.setup_particle(p, initialvalue)
162  if minvalue:
163  nuisance.set_lower(minvalue)
164  if maxvalue:
165  nuisance.set_upper(maxvalue)
166 
167  # m.add_score_state(IMP.core.SingletonConstraint(IMP.isd.NuisanceRangeModifier(),None,nuisance))
168  nuisance.set_is_optimized(nuisance.get_nuisance_key(), isoptimized)
169  self.nuisance = nuisance
170 
171  def get_particle(self):
172  return self.nuisance
173 
174 
175 class SetupWeight:
176 
177  def __init__(self, m, isoptimized=True, nweights_or_weights=None):
178  pw = IMP.Particle(m)
179  if isinstance(nweights_or_weights, int):
180  self.weight = IMP.isd.Weight.setup_particle(
181  pw, nweights_or_weights
182  )
183  else:
184  try:
185  nweights_or_weights = list(nweights_or_weights)
186  self.weight = IMP.isd.Weight.setup_particle(
187  pw, nweights_or_weights
188  )
189  except (TypeError, IMP.UsageException):
190  self.weight = IMP.isd.Weight.setup_particle(pw)
191  self.weight.set_weights_are_optimized(isoptimized)
192 
193  def get_particle(self):
194  return self.weight
195 
196 
197 class SetupSurface:
198 
199  def __init__(self, m, center, normal, isoptimized=True):
200  p = IMP.Particle(m)
201  self.surface = IMP.core.Surface.setup_particle(p, center, normal)
202  self.surface.set_coordinates_are_optimized(isoptimized)
203  self.surface.set_normal_is_optimized(isoptimized)
204 
205  def get_particle(self):
206  return self.surface
207 
208 
209 def get_cross_link_data(directory, filename, dist, omega, sigma,
210  don=None, doff=None, prior=0, type_of_profile="gofr"):
211 
212  (distmin, distmax, ndist) = dist
213  (omegamin, omegamax, nomega) = omega
214  (sigmamin, sigmamax, nsigma) = sigma
215 
216  filen = IMP.isd.get_data_path("CrossLinkPMFs.dict")
217  with open(filen) as xlpot:
218  dictionary = ast.literal_eval(xlpot.readline())
219 
220  xpot = dictionary[directory][filename]["distance"]
221  pot = dictionary[directory][filename][type_of_profile]
222 
223  dist_grid = get_grid(distmin, distmax, ndist, False)
224  omega_grid = get_log_grid(omegamin, omegamax, nomega)
225  sigma_grid = get_log_grid(sigmamin, sigmamax, nsigma)
226 
227  if don is not None and doff is not None:
228  xlmsdata = IMP.isd.CrossLinkData(
229  dist_grid,
230  omega_grid,
231  sigma_grid,
232  xpot,
233  pot,
234  don,
235  doff,
236  prior)
237  else:
238  xlmsdata = IMP.isd.CrossLinkData(
239  dist_grid,
240  omega_grid,
241  sigma_grid,
242  xpot,
243  pot)
244  return xlmsdata
245 
246 
247 def get_grid(gmin, gmax, ngrid, boundaries):
248  grid = []
249  dx = (gmax - gmin) / float(ngrid)
250  for i in range(0, ngrid + 1):
251  if not boundaries and i == 0:
252  continue
253  if not boundaries and i == ngrid:
254  continue
255  grid.append(gmin + float(i) * dx)
256  return grid
257 
258 
259 def get_log_grid(gmin, gmax, ngrid):
260  grid = []
261  for i in range(0, ngrid + 1):
262  grid.append(gmin * math.exp(float(i) / ngrid * math.log(gmax / gmin)))
263  return grid
264 
265 
267  '''
268  example '"{ID_Score}" > 28 AND "{Sample}" ==
269  "%10_1%" OR ":Sample}" == "%10_2%" OR ":Sample}"
270  == "%10_3%" OR ":Sample}" == "%8_1%" OR ":Sample}" == "%8_2%"'
271  '''
272 
273  import pyparsing as pp
274 
275  operator = pp.Regex(">=|<=|!=|>|<|==|in").setName("operator")
276  value = pp.QuotedString(
277  '"') | pp.Regex(
278  r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?")
279  identifier = pp.Word(pp.alphas, pp.alphanums + "_")
280  comparison_term = identifier | value
281  condition = pp.Group(comparison_term + operator + comparison_term)
282 
283  expr = pp.operatorPrecedence(condition, [
284  ("OR", 2, pp.opAssoc.LEFT, ),
285  ("AND", 2, pp.opAssoc.LEFT, ),
286  ])
287 
288  parsedstring = str(expr.parseString(inputstring)) \
289  .replace("[", "(") \
290  .replace("]", ")") \
291  .replace(",", " ") \
292  .replace("'", " ") \
293  .replace("%", "'") \
294  .replace("{", "float(entry['") \
295  .replace("}", "'])") \
296  .replace(":", "str(entry['") \
297  .replace("}", "'])") \
298  .replace("AND", "and") \
299  .replace("OR", "or")
300  return parsedstring
301 
302 
303 def open_file_or_inline_text(filename):
304  try:
305  fl = open(filename, "r")
306  except IOError:
307  fl = filename.split("\n")
308  return fl
309 
310 
311 def get_ids_from_fasta_file(fastafile):
312  ids = []
313  with open(fastafile) as ff:
314  for line in ff:
315  if line[0] == ">":
316  ids.append(line[1:-1])
317  return ids
318 
319 
320 def get_closest_residue_position(hier, resindex, terminus="N"):
321  '''
322  this function works with plain hierarchies, as read from the pdb,
323  no multi-scale hierarchies
324  '''
325  p = []
326  niter = 0
327  while len(p) == 0:
328  niter += 1
329  sel = IMP.atom.Selection(hier, residue_index=resindex,
330  atom_type=IMP.atom.AT_CA)
331 
332  if terminus == "N":
333  resindex += 1
334  if terminus == "C":
335  resindex -= 1
336 
337  if niter >= 10000:
338  print("get_closest_residue_position: exiting while loop "
339  "without result")
340  break
341  p = sel.get_selected_particles()
342 
343  if len(p) == 1:
344  return IMP.core.XYZ(p[0]).get_coordinates()
345  elif len(p) == 0:
346  print("get_closest_residue_position: got NO residues for hierarchy "
347  "%s and residue %i" % (hier, resindex))
348  raise Exception(
349  "get_closest_residue_position: got NO residues for hierarchy "
350  "%s and residue %i" % (hier, resindex))
351  else:
352  raise ValueError(
353  "got multiple residues for hierarchy %s and residue %i; the list "
354  "of particles is %s"
355  % (hier, resindex, str([pp.get_name() for pp in p])))
356 
357 
358 def get_residue_gaps_in_hierarchy(hierarchy, start, end):
359  '''
360  Return the residue index gaps and contiguous segments in the hierarchy.
361 
362  @param hierarchy hierarchy to examine
363  @param start first residue index
364  @param end last residue index
365 
366  @return A list of lists of the form
367  [[1,100,"cont"],[101,120,"gap"],[121,200,"cont"]]
368  '''
369  gaps = []
370  for n, rindex in enumerate(range(start, end + 1)):
371  sel = IMP.atom.Selection(hierarchy, residue_index=rindex,
372  atom_type=IMP.atom.AT_CA)
373 
374  if len(sel.get_selected_particles()) == 0:
375  if n == 0:
376  # set the initial condition
377  rindexgap = start
378  rindexcont = start - 1
379  if rindexgap == rindex - 1:
380  # residue is contiguous with the previously discovered gap
381  gaps[-1][1] += 1
382  else:
383  # residue is not contiguous with the previously discovered gap
384  # hence create a new gap tuple
385  gaps.append([rindex, rindex, "gap"])
386  # update the index of the last residue gap
387  rindexgap = rindex
388  else:
389  if n == 0:
390  # set the initial condition
391  rindexgap = start - 1
392  rindexcont = start
393  if rindexcont == rindex - 1:
394  # residue is contiguous with the previously discovered
395  # continuous part
396  gaps[-1][1] += 1
397  else:
398  # residue is not contiguous with the previously discovered
399  # continuous part, hence create a new cont tuple
400  gaps.append([rindex, rindex, "cont"])
401  # update the index of the last residue gap
402  rindexcont = rindex
403  return gaps
404 
405 
406 class map:
407 
408  def __init__(self):
409  self.map = {}
410 
411  def set_map_element(self, xvalue, yvalue):
412  self.map[xvalue] = yvalue
413 
414  def get_map_element(self, invalue):
415  if isinstance(invalue, float):
416  n = 0
417  mindist = 1
418  for x in self.map:
419  dist = (invalue - x) * (invalue - x)
420 
421  if n == 0:
422  mindist = dist
423  minx = x
424  if dist < mindist:
425  mindist = dist
426  minx = x
427  n += 1
428  return self.map[minx]
429  elif isinstance(invalue, str):
430  return self.map[invalue]
431  else:
432  raise TypeError("wrong type for map")
433 
434 
435 def select_by_tuple_2(hier, tuple_selection, resolution):
436  """New tuple format: molname OR (start,stop,molname,copynum,statenum)
437  Copy and state are optional. Can also use 'None' for them which will
438  get all. You can also pass -1 for stop which will go to the end.
439  Returns the particles
440  """
441  kwds = {} # going to accumulate keywords
442  kwds['resolution'] = resolution
443  if isinstance(tuple_selection, str):
444  kwds['molecule'] = tuple_selection
445  elif isinstance(tuple_selection, tuple):
446  rbegin = tuple_selection[0]
447  rend = tuple_selection[1]
448  kwds['molecule'] = tuple_selection[2]
449  try:
450  copynum = tuple_selection[3]
451  if copynum is not None:
452  kwds['copy_index'] = copynum
453  except: # noqa: E722
454  pass
455  try:
456  statenum = tuple_selection[4]
457  if statenum is not None:
458  kwds['state_index'] = statenum
459  except: # noqa: E722
460  pass
461  if rend == -1:
462  if rbegin > 1:
463  s = IMP.atom.Selection(hier, **kwds)
464  s -= IMP.atom.Selection(hier,
465  residue_indexes=range(1, rbegin),
466  **kwds)
467  return s.get_selected_particles()
468  else:
469  kwds['residue_indexes'] = range(rbegin, rend+1)
470  s = IMP.atom.Selection(hier, **kwds)
471  return s.get_selected_particles()
472 
473 
474 def get_db_from_csv(csvfilename, encoding=None):
475  import csv
476  outputlist = []
477  with open(csvfilename, encoding=encoding) as fh:
478  csvr = csv.DictReader(fh)
479  for ls in csvr:
480  outputlist.append(ls)
481  return outputlist
482 
483 
484 def get_prot_name_from_particle(p, list_of_names):
485  '''Return the component name provided a particle and a list of names'''
486  root = p
487  protname = root.get_name()
488  is_a_bead = False
489  while protname not in list_of_names:
490  root0 = root.get_parent()
491  if root0 == IMP.atom.Hierarchy():
492  return (None, None)
493  protname = root0.get_name()
494 
495  # check if that is a bead
496  # this piece of code might be dangerous if
497  # the hierarchy was called Bead :)
498  if "Beads" in protname:
499  is_a_bead = True
500  root = root0
501  return (protname, is_a_bead)
502 
503 
505  '''
506  Retrieve the residue indexes for the given particle.
507 
508  The particle must be an instance of Fragment,Residue, Atom or Molecule
509  or else returns an empty list
510  '''
511  resind = []
513  resind = IMP.atom.Fragment(hier).get_residue_indexes()
515  resind = [IMP.atom.Residue(hier).get_index()]
516  elif IMP.atom.Atom.get_is_setup(hier):
517  a = IMP.atom.Atom(hier)
518  resind = [IMP.atom.Residue(a.get_parent()).get_index()]
520  resind_tmp = IMP.pmi.tools.OrderedSet()
521  for lv in IMP.atom.get_leaves(hier):
525  for ind in get_residue_indexes(lv):
526  resind_tmp.add(ind)
527  resind = list(resind_tmp)
528  else:
529  resind = []
530  return resind
531 
532 
533 def sort_by_residues(particles):
534  particles_residues = [(p, list(IMP.pmi.tools.get_residue_indexes(p)))
535  for p in particles]
536  sorted_particles_residues = sorted(
537  particles_residues,
538  key=lambda tup: tup[1])
539  particles = [p[0] for p in sorted_particles_residues]
540  return particles
541 
542 #
543 # Parallel Computation
544 #
545 
546 
548  """Synchronize data over a parallel run"""
549  from mpi4py import MPI
550  comm = MPI.COMM_WORLD
551  rank = comm.Get_rank()
552  number_of_processes = comm.size
553  comm.Barrier()
554  if rank != 0:
555  comm.send(data, dest=0, tag=11)
556 
557  elif rank == 0:
558  for i in range(1, number_of_processes):
559  data_tmp = comm.recv(source=i, tag=11)
560  if isinstance(data, list):
561  data += data_tmp
562  elif isinstance(data, dict):
563  data.update(data_tmp)
564  else:
565  raise TypeError("data not supported, use list or dictionaries")
566 
567  for i in range(1, number_of_processes):
568  comm.send(data, dest=i, tag=11)
569 
570  if rank != 0:
571  data = comm.recv(source=0, tag=11)
572  return data
573 
574 #
575 # Lists and iterators
576 #
577 
578 
579 def sublist_iterator(ls, lmin=1, lmax=None):
580  '''
581  Yield all sublists of length >= lmin and <= lmax
582  '''
583  if lmax is None:
584  lmax = len(ls)
585  n = len(ls)
586  for i in range(n):
587  for j in range(i + lmin, min(n + 1, i + 1 + lmax)):
588  yield ls[i:j]
589 
590 
591 def flatten_list(ls):
592  return [item for sublist in ls for item in sublist]
593 
594 
595 def list_chunks_iterator(list, length):
596  """ Yield successive length-sized chunks from a list.
597  """
598  for i in range(0, len(list), length):
599  yield list[i:i + length]
600 
601 
602 def chunk_list_into_segments(seq, num):
603  seq = list(seq)
604  avg = len(seq) / float(num)
605  out = []
606  last = 0.0
607 
608  while last < len(seq):
609  out.append(seq[int(last):int(last + avg)])
610  last += avg
611 
612  return out
613 
614 
615 class Segments:
616 
617  ''' This class stores integers
618  in ordered compact lists eg:
619  [[1,2,3],[6,7,8]]
620  the methods help splitting and merging the internal lists
621  Example:
622  s=Segments([1,2,3]) is [[1,2,3]]
623  s.add(4) is [[1,2,3,4]] (add right)
624  s.add(3) is [[1,2,3,4]] (item already existing)
625  s.add(7) is [[1,2,3,4],[7]] (new list)
626  s.add([8,9]) is [[1,2,3,4],[7,8,9]] (add item right)
627  s.add([5,6]) is [[1,2,3,4,5,6,7,8,9]] (merge)
628  s.remove(3) is [[1,2],[4,5,6,7,8,9]] (split)
629  etc.
630  '''
631 
632  def __init__(self, index):
633  '''index can be a integer or a list of integers '''
634  if isinstance(index, int):
635  self.segs = [[index]]
636  elif isinstance(index, list):
637  self.segs = [[index[0]]]
638  for i in index[1:]:
639  self.add(i)
640  else:
641  raise TypeError("index must be an int or list of ints")
642 
643  def add(self, index):
644  '''index can be a integer or a list of integers '''
645  if isinstance(index, numbers.Integral):
646  mergeleft = None
647  mergeright = None
648  for n, s in enumerate(self.segs):
649  if index in s:
650  return 0
651  else:
652  if s[0]-index == 1:
653  mergeleft = n
654  if index-s[-1] == 1:
655  mergeright = n
656  if mergeright is None and mergeleft is None:
657  self.segs.append([index])
658  if mergeright is not None and mergeleft is None:
659  self.segs[mergeright].append(index)
660  if mergeleft is not None and mergeright is None:
661  self.segs[mergeleft] = [index]+self.segs[mergeleft]
662  if mergeleft is not None and mergeright is not None:
663  self.segs[mergeright] = \
664  self.segs[mergeright]+[index]+self.segs[mergeleft]
665  del self.segs[mergeleft]
666 
667  for n in range(len(self.segs)):
668  self.segs[n].sort()
669 
670  self.segs.sort(key=lambda tup: tup[0])
671 
672  elif isinstance(index, list):
673  for i in index:
674  self.add(i)
675  else:
676  raise TypeError("index must be an int or list of ints")
677 
678  def remove(self, index):
679  '''index can be a integer'''
680  for n, s in enumerate(self.segs):
681  if index in s:
682  if s[0] == index:
683  self.segs[n] = s[1:]
684  elif s[-1] == index:
685  self.segs[n] = s[:-1]
686  else:
687  i = self.segs[n].index(index)
688  self.segs[n] = s[:i]
689  self.segs.append(s[i+1:])
690  for n in range(len(self.segs)):
691  self.segs[n].sort()
692  if len(self.segs[n]) == 0:
693  del self.segs[n]
694  self.segs.sort(key=lambda tup: tup[0])
695 
696  def get_flatten(self):
697  ''' Returns a flatten list '''
698  return [item for sublist in self.segs for item in sublist]
699 
700  def __repr__(self):
701  ret_tmp = "["
702  for seg in self.segs:
703  ret_tmp += str(seg[0])+"-"+str(seg[-1])+","
704  ret = ret_tmp[:-1]+"]"
705  return ret
706 
707 #
708 # Tools to simulate data
709 #
710 
711 
712 def normal_density_function(expected_value, sigma, x):
713  return (
714  1 / math.sqrt(2 * math.pi) / sigma *
715  math.exp(-(x - expected_value) ** 2 / 2 / sigma / sigma)
716  )
717 
718 
719 def log_normal_density_function(expected_value, sigma, x):
720  return (
721  1 / math.sqrt(2 * math.pi) / sigma / x *
722  math.exp(-(math.log(x / expected_value) ** 2 / 2 / sigma / sigma))
723  )
724 
725 
726 def print_multicolumn(list_of_strings, ncolumns=2, truncate=40):
727 
728  ls = list_of_strings
729 
730  cols = ncolumns
731  # add empty entries after ls
732  for i in range(len(ls) % cols):
733  ls.append(" ")
734 
735  split = [ls[i:i + len(ls) // cols]
736  for i in range(0, len(ls), len(ls) // cols)]
737  for row in zip(*split):
738  print("".join(str.ljust(i, truncate) for i in row))
739 
740 
742  '''Change color code to hexadecimal to rgb'''
743  def __init__(self):
744  self._NUMERALS = '0123456789abcdefABCDEF'
745  self._HEXDEC = dict((v, int(v, 16)) for v in
746  (x+y for x in self._NUMERALS
747  for y in self._NUMERALS))
748  self.LOWERCASE, self.UPPERCASE = 'x', 'X'
749 
750  def rgb(self, triplet):
751  return (float(self._HEXDEC[triplet[0:2]]),
752  float(self._HEXDEC[triplet[2:4]]),
753  float(self._HEXDEC[triplet[4:6]]))
754 
755  def triplet(self, rgb, lettercase=None):
756  if lettercase is None:
757  lettercase = self.LOWERCASE
758  return format(rgb[0] << 16 | rgb[1] << 8 | rgb[2], '06'+lettercase)
759 
760 
761 # -------------- Collections --------------- #
762 class OrderedSet(MutableSet):
763 
764  def __init__(self, iterable=None):
765  self.end = end = []
766  end += [None, end, end] # sentinel node for doubly linked list
767  self.map = {} # key --> [key, prev, next]
768  if iterable is not None:
769  self |= iterable
770 
771  def __len__(self):
772  return len(self.map)
773 
774  def __contains__(self, key):
775  return key in self.map
776 
777  def add(self, key):
778  if key not in self.map:
779  end = self.end
780  curr = end[1]
781  curr[2] = end[1] = self.map[key] = [key, curr, end]
782 
783  def discard(self, key):
784  if key in self.map:
785  key, prev, next = self.map.pop(key)
786  prev[2] = next
787  next[1] = prev
788 
789  def __iter__(self):
790  end = self.end
791  curr = end[2]
792  while curr is not end:
793  yield curr[0]
794  curr = curr[2]
795 
796  def __reversed__(self):
797  end = self.end
798  curr = end[1]
799  while curr is not end:
800  yield curr[0]
801  curr = curr[1]
802 
803  def pop(self, last=True):
804  if not self:
805  raise KeyError('set is empty')
806  if last:
807  key = self.end[1][0]
808  else:
809  key = self.end[2][0]
810  self.discard(key)
811  return key
812 
813  def __repr__(self):
814  if not self:
815  return '%s()' % (self.__class__.__name__,)
816  return '%s(%r)' % (self.__class__.__name__, list(self))
817 
818  def __eq__(self, other):
819  if isinstance(other, OrderedSet):
820  return len(self) == len(other) and list(self) == list(other)
821  return set(self) == set(other)
822 
823 
824 class OrderedDefaultDict(OrderedDict):
825  """Store objects in order they were added, but with default type.
826  Source: http://stackoverflow.com/a/4127426/2608793
827  """
828  def __init__(self, *args, **kwargs):
829  if not args:
830  self.default_factory = None
831  else:
832  if not (args[0] is None or callable(args[0])):
833  raise TypeError('first argument must be callable or None')
834  self.default_factory = args[0]
835  args = args[1:]
836  super().__init__(*args, **kwargs)
837 
838  def __missing__(self, key):
839  if self.default_factory is None:
840  raise KeyError(key)
841  self[key] = default = self.default_factory()
842  return default
843 
844  def __reduce__(self): # optional, for pickle support
845  args = (self.default_factory,) if self.default_factory else ()
846  return self.__class__, args, None, None, self.items()
847 
848 
849 # -------------- PMI2 Tools --------------- #
850 
851 def set_coordinates_from_rmf(hier, rmf_fn, frame_num=0):
852  """Extract frame from RMF file and fill coordinates. Must be identical
853  topology.
854 
855  @param hier The (System) hierarchy to fill (e.g. after you've built it)
856  @param rmf_fn The file to extract from
857  @param frame_num The frame number to extract
858  """
859  rh = RMF.open_rmf_file_read_only(rmf_fn)
860  IMP.rmf.link_hierarchies(rh, [hier])
861  IMP.rmf.load_frame(rh, RMF.FrameID(frame_num))
862  del rh
863 
864 
865 def input_adaptor(stuff, pmi_resolution=0, flatten=False, selection_tuple=None,
866  warn_about_slices=True):
867  """Adapt things for PMI (degrees of freedom, restraints, ...)
868  Returns list of list of hierarchies, separated into Molecules if possible.
869  The input can be a list, or a list of lists (iterable of ^1 or
870  iterable of ^2)
871  (iterable of ^2) Hierarchy -> returns input as list of list of hierarchies,
872  only one entry, not grouped by molecules.
873  (iterable of ^2) PMI::System/State/Molecule/TempResidue ->
874  returns residue hierarchies, grouped in molecules, at requested
875  resolution
876 
877  @param stuff Can be one of the following inputs:
878  IMP Hierarchy, PMI System/State/Molecule/TempResidue, or a
879  list/set (of list/set) of them.
880  Must be uniform input, however. No mixing object types.
881  @param pmi_resolution For selecting, only does it if you pass PMI
882  objects. Set it to "all" if you want all resolutions!
883  @param flatten Set to True if you just want all hierarchies in one list.
884  @param warn_about_slices Print a warning if you are requesting only part
885  of a bead. Sometimes you just don't care!
886  @note since this relies on IMP::atom::Selection, this will not return
887  any objects if they weren't built! But there should be no problem
888  if you request unbuilt residues - they should be ignored.
889  """
890 
891  if stuff is None:
892  return stuff
893 
894  if hasattr(stuff, '__iter__'):
895  if len(stuff) == 0:
896  return stuff
897  thelist = list(stuff)
898 
899  # iter of iter of should be ok
900  if all(hasattr(el, '__iter__') for el in thelist):
901  thelist = [i for sublist in thelist for i in sublist]
902  elif any(hasattr(el, '__iter__') for el in thelist):
903  raise Exception('input_adaptor: input_object must be a list '
904  'or a list of lists')
905 
906  stuff = thelist
907  else:
908  stuff = [stuff]
909 
910  # check that it is a hierarchy homogeneously:
911  try:
912  is_hierarchy = all(IMP.atom.Hierarchy.get_is_setup(s) for s in stuff)
913  except (NotImplementedError, TypeError):
914  is_hierarchy = False
915  # get the other types homogeneously
916  is_system = all(isinstance(s, IMP.pmi.topology.System) for s in stuff)
917  is_state = all(isinstance(s, IMP.pmi.topology.State) for s in stuff)
918  is_molecule = all(isinstance(s, IMP.pmi.topology.Molecule) for s in stuff)
919  is_temp_residue = all(isinstance(s, IMP.pmi.topology.TempResidue)
920  for s in stuff)
921 
922  # now that things are ok, do selection if requested
923  hier_list = []
924  pmi_input = False
925  if is_system or is_state or is_molecule or is_temp_residue:
926  # if PMI, perform selection using gathered indexes
927  pmi_input = True
928  # key is Molecule object, value are residues
929  indexes_per_mol = OrderedDefaultDict(list)
930  if is_system:
931  for system in stuff:
932  for state in system.get_states():
933  mdict = state.get_molecules()
934  for molname in mdict:
935  for copy in mdict[molname]:
936  indexes_per_mol[copy] += \
937  [r.get_index() for r in copy.get_residues()]
938  elif is_state:
939  for state in stuff:
940  mdict = state.get_molecules()
941  for molname in mdict:
942  for copy in mdict[molname]:
943  indexes_per_mol[copy] += [r.get_index()
944  for r in copy.get_residues()]
945  elif is_molecule:
946  for molecule in stuff:
947  indexes_per_mol[molecule] += [r.get_index()
948  for r in molecule.get_residues()]
949  else: # is_temp_residue
950  for tempres in stuff:
951  indexes_per_mol[tempres.get_molecule()].append(
952  tempres.get_index())
953  for mol in indexes_per_mol:
954  if pmi_resolution == 'all':
955  # because you select from the molecule,
956  # this will start the search from the base resolution
958  mol.get_hierarchy(), residue_indexes=indexes_per_mol[mol])
959  else:
960  sel = IMP.atom.Selection(mol.get_hierarchy(),
961  resolution=pmi_resolution,
962  residue_indexes=indexes_per_mol[mol])
963  ps = sel.get_selected_particles()
964 
965  # check that you don't have any incomplete fragments!
966  if warn_about_slices:
967  rset = set(indexes_per_mol[mol])
968  for p in ps:
970  fset = set(IMP.atom.Fragment(p).get_residue_indexes())
971  if not fset <= rset:
972  minset = min(fset)
973  maxset = max(fset)
974  found = fset & rset
975  minf = min(found)
976  maxf = max(found)
977  resbreak = maxf if minf == minset else minset-1
978  warnings.warn(
979  'You are trying to select only part of the '
980  'bead %s:%i-%i. The residues you requested '
981  'are %i-%i. You can fix this by: '
982  '1) requesting the whole bead/none of it; or'
983  '2) break the bead up by passing '
984  'bead_extra_breaks=[\'%i\'] in '
985  'molecule.add_representation()'
986  % (mol.get_name(), minset, maxset, minf, maxf,
987  resbreak), IMP.pmi.ParameterWarning)
988  hier_list.append([IMP.atom.Hierarchy(p) for p in ps])
989  elif is_hierarchy:
990  # check
991  ps = []
992  if pmi_resolution == 'all':
993  for h in stuff:
995  else:
996  for h in stuff:
997  ps += IMP.atom.Selection(
998  h, resolution=pmi_resolution).get_selected_particles()
999  hier_list = [IMP.atom.Hierarchy(p) for p in ps]
1000  if not flatten:
1001  hier_list = [hier_list]
1002  else:
1003  raise Exception('input_adaptor: you passed something of wrong type '
1004  'or a list with mixed types')
1005 
1006  if flatten and pmi_input:
1007  return [h for sublist in hier_list for h in sublist]
1008  else:
1009  return hier_list
1010 
1011 
1013  """Returns sequence-sorted segments array, each containing the first
1014  particle the last particle and the first residue index."""
1015 
1016  from operator import itemgetter
1017  hiers = IMP.pmi.tools.input_adaptor(mol)
1018  if len(hiers) > 1:
1019  raise ValueError("only pass stuff from one Molecule, please")
1020  hiers = hiers[0]
1021  segs = []
1022  for h in hiers:
1023  try:
1024  start = IMP.atom.Hierarchy(h).get_children()[0]
1025  except IndexError:
1026  start = IMP.atom.Hierarchy(h)
1027 
1028  try:
1029  end = IMP.atom.Hierarchy(h).get_children()[-1]
1030  except IndexError:
1031  end = IMP.atom.Hierarchy(h)
1032 
1033  startres = IMP.pmi.tools.get_residue_indexes(start)[0]
1034  segs.append((start, end, startres))
1035  return sorted(segs, key=itemgetter(2))
1036 
1037 
1038 def display_bonds(mol):
1039  """Decorate the sequence-consecutive particles from a PMI2 molecule
1040  with a bond, so that they appear connected in the rmf file"""
1041  SortedSegments = get_sorted_segments(mol)
1042  for x in range(len(SortedSegments) - 1):
1043 
1044  last = SortedSegments[x][1]
1045  first = SortedSegments[x + 1][0]
1046 
1047  p1 = last.get_particle()
1048  p2 = first.get_particle()
1049  if not IMP.atom.Bonded.get_is_setup(p1):
1051  if not IMP.atom.Bonded.get_is_setup(p2):
1053 
1056  IMP.atom.Bonded(p1),
1057  IMP.atom.Bonded(p2), 1)
1058 
1059 
1060 def get_all_leaves(list_of_hs):
1061  """ Just get the leaves from a list of hierarchies """
1062  lvs = list(itertools.chain.from_iterable(
1063  IMP.atom.get_leaves(item) for item in list_of_hs))
1064  return lvs
1065 
1066 
1067 def select_at_all_resolutions(hier=None, hiers=None, **kwargs):
1068  """Perform selection using the usual keywords but return ALL
1069  resolutions (BEADS and GAUSSIANS).
1070  Returns in flat list!
1071  """
1072 
1073  if hiers is None:
1074  hiers = []
1075  if hier is not None:
1076  hiers.append(hier)
1077  if len(hiers) == 0:
1078  warnings.warn("You passed nothing to select_at_all_resolutions()",
1080  return []
1081  ret = OrderedSet()
1082  for hsel in hiers:
1083  try:
1084  htest = IMP.atom.Hierarchy.get_is_setup(hsel)
1085  except: # noqa: E722
1086  raise Exception('select_at_all_resolutions: you have to pass '
1087  'an IMP Hierarchy')
1088  if not htest:
1089  raise Exception('select_at_all_resolutions: you have to pass '
1090  'an IMP Hierarchy')
1091  if 'resolution' in kwargs or 'representation_type' in kwargs:
1092  raise Exception("don't pass resolution or representation_type "
1093  "to this function")
1094  selB = IMP.atom.Selection(hsel, resolution=IMP.atom.ALL_RESOLUTIONS,
1095  representation_type=IMP.atom.BALLS,
1096  **kwargs)
1097  selD = IMP.atom.Selection(hsel, resolution=IMP.atom.ALL_RESOLUTIONS,
1098  representation_type=IMP.atom.DENSITIES,
1099  **kwargs)
1100  ret |= OrderedSet(selB.get_selected_particles())
1101  ret |= OrderedSet(selD.get_selected_particles())
1102  return list(ret)
1103 
1104 
1106  target_ps,
1107  sel_zone,
1108  entire_residues,
1109  exclude_backbone):
1110  """Utility to retrieve particles from a hierarchy within a
1111  zone around a set of ps.
1112  @param hier The hierarchy in which to look for neighbors
1113  @param target_ps The particles for zoning
1114  @param sel_zone The maximum distance
1115  @param entire_residues If True, will grab entire residues
1116  @param exclude_backbone If True, will only return sidechain particles
1117  """
1118 
1119  test_sel = IMP.atom.Selection(hier)
1120  backbone_types = ['C', 'N', 'CB', 'O']
1121  if exclude_backbone:
1122  test_sel -= IMP.atom.Selection(
1123  hier, atom_types=[IMP.atom.AtomType(n) for n in backbone_types])
1124  test_ps = test_sel.get_selected_particles()
1125  nn = IMP.algebra.NearestNeighbor3D([IMP.core.XYZ(p).get_coordinates()
1126  for p in test_ps])
1127  zone = set()
1128  for target in target_ps:
1129  zone |= set(nn.get_in_ball(IMP.core.XYZ(target).get_coordinates(),
1130  sel_zone))
1131  zone_ps = [test_ps[z] for z in zone]
1132  if entire_residues:
1133  final_ps = set()
1134  for z in zone_ps:
1135  final_ps |= set(IMP.atom.Hierarchy(z).get_parent().get_children())
1136  zone_ps = [h.get_particle() for h in final_ps]
1137  return zone_ps
1138 
1139 
1141  """Returns unique objects in original order"""
1142  rbs = set()
1143  beads = []
1144  rbs_ordered = []
1145  if not hasattr(hiers, '__iter__'):
1146  hiers = [hiers]
1147  for p in get_all_leaves(hiers):
1149  rb = IMP.core.RigidMember(p).get_rigid_body()
1150  if rb not in rbs:
1151  rbs.add(rb)
1152  rbs_ordered.append(rb)
1154  rb = IMP.core.NonRigidMember(p).get_rigid_body()
1155  if rb not in rbs:
1156  rbs.add(rb)
1157  rbs_ordered.append(rb)
1158  beads.append(p)
1159  else:
1160  beads.append(p)
1161  return rbs_ordered, beads
1162 
1163 
1164 def get_molecules(input_objects):
1165  "This function returns the parent molecule hierarchies of given objects"
1166  stuff = input_adaptor(input_objects, pmi_resolution='all', flatten=True)
1167  molecules = set()
1168  for h in stuff:
1169  is_root = False
1170  is_molecule = False
1171  while not (is_molecule or is_root):
1172  root = IMP.atom.get_root(h)
1173  if root == h:
1174  is_root = True
1175  is_molecule = IMP.atom.Molecule.get_is_setup(h)
1176  if is_molecule:
1177  molecules.add(IMP.atom.Molecule(h))
1178  h = h.get_parent()
1179  return list(molecules)
1180 
1181 
1182 def get_molecules_dictionary(input_objects):
1183  moldict = defaultdict(list)
1184  for mol in IMP.pmi.tools.get_molecules(input_objects):
1185  name = mol.get_name()
1186  moldict[name].append(mol)
1187 
1188  for mol in moldict:
1189  moldict[mol].sort(key=lambda x: IMP.atom.Copy(x).get_copy_index())
1190  return moldict
1191 
1192 
1193 def get_molecules_dictionary_by_copy(input_objects):
1194  moldict = defaultdict(dict)
1195  for mol in IMP.pmi.tools.get_molecules(input_objects):
1196  name = mol.get_name()
1197  c = IMP.atom.Copy(mol).get_copy_index()
1198  moldict[name][c] = mol
1199  return moldict
1200 
1201 
1202 def get_selections_dictionary(input_objects):
1203  moldict = IMP.pmi.tools.get_molecules_dictionary(input_objects)
1204  seldict = defaultdict(list)
1205  for name, mols in moldict.items():
1206  for m in mols:
1207  seldict[name].append(IMP.atom.Selection(m))
1208  return seldict
1209 
1210 
1211 def get_densities(input_objects):
1212  """Given a list of PMI objects, returns all density hierarchies within
1213  these objects. The output of this function can be inputted into
1214  things such as EM restraints. This function is intended to gather
1215  density particles appended to molecules (and not other hierarchies
1216  which might have been appended to the root node directly).
1217  """
1218  # Note that Densities can only be selected at the Root or Molecule
1219  # level and not at the Leaves level.
1220  # we'll first get all molecule hierarchies corresponding to the leaves.
1221  molecules = get_molecules(input_objects)
1222  densities = []
1223  for i in molecules:
1224  densities += IMP.atom.Selection(
1225  i, representation_type=IMP.atom.DENSITIES).get_selected_particles()
1226  return densities
1227 
1228 
1229 def shuffle_configuration(objects,
1230  max_translation=300., max_rotation=2.0 * math.pi,
1231  avoidcollision_rb=True, avoidcollision_fb=False,
1232  cutoff=10.0, niterations=100,
1233  bounding_box=None,
1234  excluded_rigid_bodies=[],
1235  hierarchies_excluded_from_collision=[],
1236  hierarchies_included_in_collision=[],
1237  verbose=False,
1238  return_debug=False):
1239  """Shuffle particles. Used to restart the optimization.
1240  The configuration of the system is initialized by placing each
1241  rigid body and each bead randomly in a box. If `bounding_box` is
1242  specified, the particles are placed inside this box; otherwise, each
1243  particle is displaced by up to max_translation angstroms, and randomly
1244  rotated. Effort is made to place particles far enough from each other to
1245  prevent any steric clashes.
1246  @param objects Can be one of the following inputs:
1247  IMP Hierarchy, PMI System/State/Molecule/TempResidue, or
1248  a list/set of them
1249  @param max_translation Max translation (rbs and flexible beads)
1250  @param max_rotation Max rotation (rbs only)
1251  @param avoidcollision_rb check if the particle/rigid body was
1252  placed close to another particle; uses the optional
1253  arguments cutoff and niterations
1254  @param avoidcollision_fb Advanced. Generally you want this False because
1255  it's hard to shuffle beads.
1256  @param cutoff Distance less than this is a collision
1257  @param niterations How many times to try avoiding collision
1258  @param bounding_box Only shuffle particles within this box.
1259  Defined by ((x1,y1,z1),(x2,y2,z2)).
1260  @param excluded_rigid_bodies Don't shuffle these rigid body objects
1261  @param hierarchies_excluded_from_collision Don't count collision
1262  with these bodies
1263  @param hierarchies_included_in_collision Hierarchies that are not
1264  shuffled, but should be included in collision calculation
1265  (for fixed regions)
1266  @param verbose Give more output
1267  @note Best to only call this function after you've set up degrees
1268  of freedom
1269  For debugging purposes, returns: <shuffled indexes>,
1270  <collision avoided indexes>
1271  """
1272 
1273  # checking input
1274  hierarchies = IMP.pmi.tools.input_adaptor(objects,
1275  pmi_resolution='all',
1276  flatten=True)
1277  rigid_bodies, flexible_beads = get_rbs_and_beads(hierarchies)
1278  if len(rigid_bodies) > 0:
1279  mdl = rigid_bodies[0].get_model()
1280  elif len(flexible_beads) > 0:
1281  mdl = flexible_beads[0].get_model()
1282  else:
1283  raise Exception("Could not find any particles in the hierarchy")
1284  if len(rigid_bodies) == 0:
1285  print("shuffle_configuration: rigid bodies were not initialized")
1286 
1287  # gather all particles
1289  gcpf.set_distance(cutoff)
1290 
1291  # Add particles from excluded hierarchies to excluded list
1292  collision_excluded_hierarchies = IMP.pmi.tools.input_adaptor(
1293  hierarchies_excluded_from_collision, pmi_resolution='all',
1294  flatten=True)
1295 
1296  collision_included_hierarchies = IMP.pmi.tools.input_adaptor(
1297  hierarchies_included_in_collision, pmi_resolution='all', flatten=True)
1298 
1299  collision_excluded_idxs = set(
1300  leaf.get_particle().get_index()
1301  for h in collision_excluded_hierarchies
1302  for leaf in IMP.core.get_leaves(h))
1303 
1304  collision_included_idxs = set(
1305  leaf.get_particle().get_index()
1306  for h in collision_included_hierarchies
1307  for leaf in IMP.core.get_leaves(h))
1308 
1309  # Excluded collision with Gaussians
1310  all_idxs = [] # expand to representations?
1311  for p in IMP.pmi.tools.get_all_leaves(hierarchies):
1313  all_idxs.append(p.get_particle_index())
1315  collision_excluded_idxs.add(p.get_particle_index())
1316 
1317  if bounding_box is not None:
1318  ((x1, y1, z1), (x2, y2, z2)) = bounding_box
1319  ub = IMP.algebra.Vector3D(x1, y1, z1)
1320  lb = IMP.algebra.Vector3D(x2, y2, z2)
1321  bb = IMP.algebra.BoundingBox3D(ub, lb)
1322 
1323  all_idxs = set(all_idxs) | collision_included_idxs
1324  all_idxs = all_idxs - collision_excluded_idxs
1325  debug = []
1326  print('shuffling', len(rigid_bodies), 'rigid bodies')
1327  for rb in rigid_bodies:
1328  if rb not in excluded_rigid_bodies:
1329  # gather particles to avoid with this transform
1330  if avoidcollision_rb:
1331  rb_idxs = set(rb.get_member_particle_indexes()) - \
1332  collision_excluded_idxs
1333  other_idxs = all_idxs - rb_idxs
1334 
1335  debug.append([rb, other_idxs if avoidcollision_rb else set()])
1336  # iterate, trying to avoid collisions
1337  niter = 0
1338  while niter < niterations:
1339  rbxyz = (rb.get_x(), rb.get_y(), rb.get_z())
1340 
1341  # local transform
1342  if bounding_box:
1343  translation = IMP.algebra.get_random_vector_in(bb)
1344  # First move to origin
1345  transformation_orig = IMP.algebra.Transformation3D(
1347  -IMP.core.XYZ(rb).get_coordinates())
1348  IMP.core.transform(rb, transformation_orig)
1350  transformation = IMP.algebra.Transformation3D(rotation,
1351  translation)
1352 
1353  else:
1354  transformation = \
1356  rbxyz, max_translation, max_rotation)
1357 
1358  IMP.core.transform(rb, transformation)
1359 
1360  # check collisions
1361  if avoidcollision_rb and other_idxs:
1362  mdl.update()
1363  npairs = len(gcpf.get_close_pairs(mdl,
1364  list(other_idxs),
1365  list(rb_idxs)))
1366  if npairs == 0:
1367  break
1368  else:
1369  niter += 1
1370  if verbose:
1371  print("shuffle_configuration: rigid body placed "
1372  "close to other %d particles, trying "
1373  "again..." % npairs)
1374  print("shuffle_configuration: rigid body name: "
1375  + rb.get_name())
1376  if niter == niterations:
1377  raise ValueError(
1378  "tried the maximum number of iterations to "
1379  "avoid collisions, increase the distance "
1380  "cutoff")
1381  else:
1382  break
1383 
1384  print('shuffling', len(flexible_beads), 'flexible beads')
1385  for fb in flexible_beads:
1386  # gather particles to avoid
1387  if avoidcollision_fb:
1388  fb_idxs = set(IMP.get_indexes([fb]))
1389  other_idxs = all_idxs - fb_idxs
1390  if not other_idxs:
1391  continue
1392 
1393  # iterate, trying to avoid collisions
1394  niter = 0
1395  while niter < niterations:
1396  if bounding_box:
1397  translation = IMP.algebra.get_random_vector_in(bb)
1398  transformation = IMP.algebra.Transformation3D(translation)
1399  else:
1400  fbxyz = IMP.core.XYZ(fb).get_coordinates()
1402  fbxyz, max_translation, max_rotation)
1403 
1404  # For gaussians, treat this fb as an rb
1406  memb = IMP.core.NonRigidMember(fb)
1407  xyz = memb.get_internal_coordinates()
1408  if bounding_box:
1409  # 'translation' is the new desired position in global
1410  # coordinates; we need to convert that to internal
1411  # coordinates first using the rigid body's ref frame
1412  rf = memb.get_rigid_body().get_reference_frame()
1413  glob_to_int = rf.get_transformation_from()
1414  memb.set_internal_coordinates(
1415  glob_to_int.get_transformed(translation))
1416  else:
1417  xyz_transformed = transformation.get_transformed(xyz)
1418  memb.set_internal_coordinates(xyz_transformed)
1419  if niter == 0:
1420  debug.append(
1421  [xyz, other_idxs if avoidcollision_fb else set()])
1422  else:
1423  d = IMP.core.XYZ(fb)
1424  if bounding_box:
1425  # Translate to origin first
1426  if IMP.core.RigidBody.get_is_setup(fb.get_particle()):
1428  IMP.core.RigidBody(fb.get_particle()),
1429  -d.get_coordinates())
1430  else:
1431  IMP.core.transform(d, -d.get_coordinates())
1432  d = IMP.core.XYZ(fb)
1433  if niter == 0:
1434  debug.append(
1435  [d, other_idxs if avoidcollision_fb else set()])
1436  if IMP.core.RigidBody.get_is_setup(fb.get_particle()):
1438  IMP.core.RigidBody(fb.get_particle()), transformation)
1439  else:
1440  IMP.core.transform(d, transformation)
1441 
1442  if avoidcollision_fb:
1443  mdl.update()
1444  npairs = len(gcpf.get_close_pairs(mdl,
1445  list(other_idxs),
1446  list(fb_idxs)))
1447 
1448  if npairs == 0:
1449  break
1450  else:
1451  niter += 1
1452  print("shuffle_configuration: floppy body placed close "
1453  "to other %d particles, trying again..." % npairs)
1454  if niter == niterations:
1455  raise ValueError(
1456  "tried the maximum number of iterations to avoid "
1457  "collisions, increase the distance cutoff")
1458  else:
1459  break
1460  if return_debug:
1461  return debug
1462 
1463 
1464 class ColorHierarchy:
1465 
1466  def __init__(self, hier):
1467  import matplotlib as mpl
1468  mpl.use('Agg')
1469  import matplotlib.pyplot as plt
1470  self.mpl = mpl
1471  self.plt = plt
1472 
1473  hier.ColorHierarchy = self
1474  self.hier = hier
1476  self.mols = [IMP.pmi.topology.PMIMoleculeHierarchy(mol)
1477  for mol in mols]
1478  self.method = self.nochange
1479  self.scheme = None
1480  self.first = None
1481  self.last = None
1482 
1483  def nochange(self):
1484  pass
1485 
1486  def get_color(self, fl):
1487  return IMP.display.Color(*self.scheme(fl)[0:3])
1488 
1489  def get_log_scale(self, fl):
1490  import math
1491  eps = 1.0
1492  return math.log(fl+eps)
1493 
1494  def color_by_resid(self):
1495  self.method = self.color_by_resid
1496  self.scheme = self.mpl.cm.rainbow
1497  for mol in self.mols:
1498  self.first = 1
1499  self.last = len(IMP.pmi.topology.PMIMoleculeHierarchy(
1500  mol).get_residue_indexes())
1501  for p in IMP.atom.get_leaves(mol):
1503  ri = IMP.atom.Residue(p).get_index()
1504  c = self.get_color(float(ri)/self.last)
1505  IMP.display.Colored(p).set_color(c)
1508  avr = sum(ris)/len(ris)
1509  c = self.get_color(float(avr)/self.last)
1510  IMP.display.Colored(p).set_color(c)
1511 
1512  def color_by_uncertainty(self):
1513  self.method = self.color_by_uncertainty
1514  self.scheme = self.mpl.cm.jet
1515  ps = IMP.atom.get_leaves(self.hier)
1516  unc_dict = {}
1517  for p in ps:
1519  u = IMP.pmi.Uncertainty(p).get_uncertainty()
1520  unc_dict[p] = u
1521  self.first = self.get_log_scale(1.0)
1522  self.last = self.get_log_scale(100.0)
1523  for p in unc_dict:
1524  value = self.get_log_scale(unc_dict[p])
1525  if value >= self.last:
1526  value = self.last
1527  if value <= self.first:
1528  value = self.first
1529  c = self.get_color((value-self.first) / (self.last-self.first))
1530  IMP.display.Colored(p).set_color(c)
1531 
1532  def get_color_bar(self, filename):
1533  import matplotlib as mpl
1534  mpl.use('Agg')
1535  import matplotlib.pyplot as plt
1536  plt.clf()
1537  fig = plt.figure(figsize=(8, 3))
1538  ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
1539 
1540  cmap = self.scheme
1541  norm = mpl.colors.Normalize(vmin=0.0, vmax=1.0)
1542 
1543  if self.method == self.color_by_uncertainty:
1544  angticks = [1.0, 2.5, 5.0, 10.0, 25.0, 50.0, 100.0]
1545  vvalues = []
1546  marks = []
1547  for at in angticks:
1548  vvalue = (self.get_log_scale(at)-self.first) \
1549  / (self.last-self.first)
1550  if vvalue <= 1.0 and vvalue >= 0.0:
1551  vvalues.append(vvalue)
1552  marks.append(str(at))
1553  cb1 = mpl.colorbar.ColorbarBase(
1554  ax1, cmap=cmap, norm=norm, ticks=vvalues,
1555  orientation='horizontal')
1556  print(self.first, self.last, marks, vvalues)
1557  cb1.ax.set_xticklabels(marks)
1558  cb1.set_label('Angstorm')
1559  plt.savefig(filename, dpi=150, transparent=True)
1560  plt.show()
1561 
1562 
1563 def color2rgb(colorname):
1564  """Given a Chimera color name or hex color value, return RGB"""
1565  d = {'aquamarine': (0.4980392156862745, 1.0, 0.8313725490196079),
1566  'black': (0.0, 0.0, 0.0),
1567  'blue': (0.0, 0.0, 1.0),
1568  'brown': (0.6470588235, 0.16470588235294117, 0.16470588235294117),
1569  'chartreuse': (0.4980392156862745, 1.0, 0.0),
1570  'coral': (1.0, 0.4980392156862745, 0.3137254901960784),
1571  'cornflower blue': (0.39215686, 0.58431372549, 0.9294117647058824),
1572  'cyan': (0.0, 1.0, 1.0),
1573  'dark cyan': (0.0, 0.5450980392156862, 0.5450980392156862),
1574  'dark gray': (0.6627450980, 0.6627450980392157, 0.6627450980392157),
1575  'dark green': (0.0, 0.39215686274509803, 0.0),
1576  'dark khaki': (0.74117647, 0.7176470588235294, 0.4196078431372549),
1577  'dark magenta': (0.5450980392156862, 0.0, 0.5450980392156862),
1578  'dark olive green': (0.333333333, 0.419607843, 0.1843137254901961),
1579  'dark red': (0.5450980392156862, 0.0, 0.0),
1580  'dark slate blue': (0.28235294, 0.239215686, 0.5450980392156862),
1581  'dark slate gray': (0.1843137, 0.30980392, 0.30980392156862746),
1582  'deep pink': (1.0, 0.0784313725490196, 0.5764705882352941),
1583  'deep sky blue': (0.0, 0.7490196078431373, 1.0),
1584  'dim gray': (0.41176470, 0.4117647058823529, 0.4117647058823529),
1585  'dodger blue': (0.11764705882352941, 0.5647058823529412, 1.0),
1586  'firebrick': (0.6980392, 0.13333333333333333, 0.13333333333333333),
1587  'forest green': (0.13333333, 0.5450980392156862, 0.13333333333333333),
1588  'gold': (1.0, 0.8431372549019608, 0.0),
1589  'goldenrod': (0.85490196, 0.6470588235294118, 0.12549019607843137),
1590  'gray': (0.7450980392156863, 0.7450980392156863, 0.7450980392156863),
1591  'green': (0.0, 1.0, 0.0),
1592  'hot pink': (1.0, 0.4117647058823529, 0.7058823529411765),
1593  'khaki': (0.9411764705882353, 0.9019607843137255, 0.5490196078431373),
1594  'light blue': (0.67843137, 0.8470588235294118, 0.9019607843137255),
1595  'light gray': (0.82745098, 0.8274509803921568, 0.8274509803921568),
1596  'light green': (0.56470588, 0.9333333333333333, 0.5647058823529412),
1597  'light sea green': (0.125490, 0.6980392156862745, 0.6666666666666666),
1598  'lime green': (0.1960784, 0.803921568627451, 0.19607843137254902),
1599  'magenta': (1.0, 0.0, 1.0),
1600  'medium blue': (0.1960784, 0.19607843137254902, 0.803921568627451),
1601  'medium purple': (0.57647, 0.4392156862745098, 0.8588235294117647),
1602  'navy blue': (0.0, 0.0, 0.5019607843137255),
1603  'olive drab': (0.4196078, 0.5568627450980392, 0.13725490196078433),
1604  'orange red': (1.0, 0.27058823529411763, 0.0),
1605  'orange': (1.0, 0.4980392156862745, 0.0),
1606  'orchid': (0.85490196, 0.4392156862745098, 0.8392156862745098),
1607  'pink': (1.0, 0.7529411764705882, 0.796078431372549),
1608  'plum': (0.8666666666666667, 0.6274509803921569, 0.8666666666666667),
1609  'purple': (0.62745098, 0.12549019607843137, 0.9411764705882353),
1610  'red': (1.0, 0.0, 0.0),
1611  'rosy brown': (0.7372549, 0.5607843137254902, 0.5607843137254902),
1612  'salmon': (0.980392, 0.5019607843137255, 0.4470588235294118),
1613  'sandy brown': (0.956862745, 0.6431372549019608, 0.3764705882352941),
1614  'sea green': (0.18039, 0.5450980392156862, 0.3411764705882353),
1615  'sienna': (0.6274509, 0.3215686274509804, 0.17647058823529413),
1616  'sky blue': (0.52941176, 0.807843137254902, 0.9215686274509803),
1617  'slate gray': (0.439215686, 0.50196078, 0.5647058823529412),
1618  'spring green': (0.0, 1.0, 0.4980392156862745),
1619  'steel blue': (0.2745098, 0.50980392, 0.70588235),
1620  'tan': (0.8235294117647058, 0.7058823529411765, 0.5490196078431373),
1621  'turquoise': (0.25098039, 0.87843137, 0.81568627),
1622  'violet red': (0.81568627, 0.125490196, 0.56470588235),
1623  'white': (1.0, 1.0, 1.0),
1624  'yellow': (1.0, 1.0, 0.0)}
1625  if colorname.startswith('#'):
1626  return tuple(int(colorname[i:i+2], 16) / 255. for i in (1, 3, 5))
1627  else:
1628  return d[colorname]
1629 
1630 
1632  """Score a single restraint, for use in stat files.
1633  Every time this object is called, it returns the weighted score
1634  of the restraint for the current model configuration. This is
1635  primarily used in a PMI restraint's get_output() method to obtain
1636  restraint scores for stat file output.
1637 
1638  @param name The name of the score in the stat file.
1639  @param weight_object A Python object with a 'weight' attribute
1640  (usually a PMI restraint) which is used to weight the score.
1641  @param restraint The IMP Restraint to score.
1642  """
1643  def __init__(self, name, weight_object, restraint):
1644  self.name, self.restraint = name, restraint
1645  self._weight_object = weight_object
1646  self._jax_score = None
1647 
1648  def __call__(self, jax_data=None):
1649  """Get the score for the given JAX data if `jax_data` is given,
1650  otherwise for the current IMP Model."""
1651  if jax_data is not None:
1652  if self._jax_score is None:
1653  import jax
1654  ji = self.restraint._get_jax(space=jax_data.space)
1655  self._jax_score = jax.jit(ji.score_func)
1656  return self._jax_score(jax_data.model)
1657  else:
1658  weight = self._weight_object.weight
1659  return weight * self.restraint.unprotected_evaluate(None)
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: Molecule.h:35
def list_chunks_iterator
Yield successive length-sized chunks from a list.
Definition: pmi/tools.py:595
Simple 3D transformation class.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: Residue.h:158
def select_at_all_resolutions
Perform selection using the usual keywords but return ALL resolutions (BEADS and GAUSSIANS).
Definition: pmi/tools.py:1067
Represent an RGB color.
Definition: Color.h:25
A decorator to associate a particle with a part of a protein/DNA/RNA.
Definition: Fragment.h:20
Extends the functionality of IMP.atom.Molecule.
def display_bonds
Decorate the sequence-consecutive particles from a PMI2 molecule with a bond, so that they appear con...
Definition: pmi/tools.py:1038
def add_script_provenance
Tag the given particle with the current Python script.
def get_restraint_set
Get a RestraintSet containing all PMI restraints added to the model.
Definition: pmi/tools.py:109
def remove
index can be a integer
Definition: pmi/tools.py:678
def shuffle_configuration
Shuffle particles.
Definition: pmi/tools.py:1247
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: atom/Atom.h:245
def get_particles_within_zone
Utility to retrieve particles from a hierarchy within a zone around a set of ps.
Definition: pmi/tools.py:1105
Set of Python classes to create a multi-state, multi-resolution IMP hierarchy.
def __init__
Constructor.
Definition: pmi/tools.py:129
static Weight setup_particle(Model *m, ParticleIndex pi)
Set up an empty Weight.
Definition: Weight.h:48
A decorator for a particle which has bonds.
Rotation3D get_random_rotation_3d(const Rotation3D &center, double distance)
Pick a rotation at random near the provided one.
def get_molecules
This function returns the parent molecule hierarchies of given objects.
Definition: pmi/tools.py:1164
An exception for an invalid usage of IMP.
Definition: exception.h:122
std::string get_module_version()
Return the version of this module, as a string.
Change color code to hexadecimal to rgb.
Definition: pmi/tools.py:741
This class stores integers in ordered compact lists eg: [[1,2,3],[6,7,8]] the methods help splitting ...
Definition: pmi/tools.py:615
The type of an atom.
static Surface setup_particle(Model *m, ParticleIndex pi)
Definition: Surface.h:45
Add uncertainty to a particle.
Definition: Uncertainty.h:24
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: rigid_bodies.h:640
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: XYZ.h:49
GenericHierarchies get_leaves(Hierarchy mhd)
Get all the leaves of the bit of hierarchy.
def get_prot_name_from_particle
Return the component name provided a particle and a list of names.
Definition: pmi/tools.py:484
Represent the root node of the global IMP.atom.Hierarchy.
def get_residue_gaps_in_hierarchy
Return the residue index gaps and contiguous segments in the hierarchy.
Definition: pmi/tools.py:358
def add_imp_provenance
Tag the given particle as being created by the current version of IMP.
def add
index can be a integer or a list of integers
Definition: pmi/tools.py:643
Vector3D get_random_vector_in(const Cylinder3D &c)
Generate a random vector in a cylinder with uniform density.
def get_all_leaves
Just get the leaves from a list of hierarchies.
Definition: pmi/tools.py:1060
Bond create_bond(Bonded a, Bonded b, Bond o)
Connect the two wrapped particles by a custom bond.
Object used to hold a set of restraints.
Definition: RestraintSet.h:41
Stores a named protein chain.
def color2rgb
Given a Chimera color name or hex color value, return RGB.
Definition: pmi/tools.py:1563
def input_adaptor
Adapt things for PMI (degrees of freedom, restraints, ...) Returns list of list of hierarchies...
Definition: pmi/tools.py:877
static bool get_is_setup(Model *m, ParticleIndex pi)
Definition: Fragment.h:46
def add_software_provenance
Tag the given particle with the software used to create it.
A decorator for keeping track of copies of a molecule.
Definition: Copy.h:28
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)
def select_by_tuple_2
New tuple format: molname OR (start,stop,molname,copynum,statenum) Copy and state are optional...
Definition: pmi/tools.py:435
A decorator for a particle representing an atom.
Definition: atom/Atom.h:238
def scatter_and_gather
Synchronize data over a parallel run.
Definition: pmi/tools.py:547
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:89
static Bonded setup_particle(Model *m, ParticleIndex pi)
Score a single restraint, for use in stat files.
Definition: pmi/tools.py:1631
def __call__
Get the score for the given JAX data if jax_data is given, otherwise for the current IMP Model...
Definition: pmi/tools.py:1648
def __init__
index can be a integer or a list of integers
Definition: pmi/tools.py:632
void load_frame(RMF::FileConstHandle file, RMF::FrameID frame)
Load the given RMF frame into the state of the linked objects.
A decorator for a particle with x,y,z coordinates.
Definition: XYZ.h:30
def get_flatten
Returns a flatten list.
Definition: pmi/tools.py:696
static Scale setup_particle(Model *m, ParticleIndex pi)
Definition: Scale.h:30
A base class for Keys.
Definition: Key.h:45
Collect timing information.
Definition: pmi/tools.py:124
A decorator for a particle that is part of a rigid body but not rigid.
Definition: rigid_bodies.h:659
def get_closest_residue_position
this function works with plain hierarchies, as read from the pdb, no multi-scale hierarchies ...
Definition: pmi/tools.py:320
Find all nearby pairs by testing all pairs.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: rigid_bodies.h:167
A decorator for a residue.
Definition: Residue.h:137
DensityGrid get_grid(IMP::em::DensityMap *in)
Return a dense grid containing the voxels of the passed density map.
General purpose algebraic and geometric methods that are expected to be used by a wide variety of IMP...
static bool get_is_setup(const IMP::ParticleAdaptor &p)
static bool get_is_setup(Model *m, ParticleIndex p)
Check if the particle has the needed attributes for a cast to succeed.
The general base class for IMP exceptions.
Definition: exception.h:48
Rotation3D get_identity_rotation_3d()
Return a rotation that does not do anything.
Definition: Rotation3D.h:352
def get_densities
Given a list of PMI objects, returns all density hierarchies within these objects.
Definition: pmi/tools.py:1211
void link_hierarchies(RMF::FileConstHandle fh, const atom::Hierarchies &hs)
Class to handle individual particles of a Model object.
Definition: Particle.h:45
Bond get_bond(Bonded a, Bonded b)
Get the bond between two particles.
Stores a list of Molecules all with the same State index.
std::string get_data_path(std::string file_name)
Return the full path to one of this module's data files.
def set_coordinates_from_rmf
Extract frame from RMF file and fill coordinates.
Definition: pmi/tools.py:851
int get_copy_index(Hierarchy h)
Walk up the hierarchy to find the current copy index.
Python classes to represent, score, sample and analyze models.
static bool get_is_setup(Model *m, ParticleIndex pi)
Definition: Uncertainty.h:30
A decorator for a rigid body.
Definition: rigid_bodies.h:80
def cross_link_db_filter_parser
example '"{ID_Score}" > 28 AND "{Sample}" == "%10_1%" OR ":Sample}" == "%10_2%" OR ":Sample...
Definition: pmi/tools.py:266
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: core/Gaussian.h:64
def get_sorted_segments
Returns sequence-sorted segments array, each containing the first particle the last particle and the ...
Definition: pmi/tools.py:1012
def get_rbs_and_beads
Returns unique objects in original order.
Definition: pmi/tools.py:1140
Hierarchies get_leaves(const Selection &h)
A decorator for a molecule.
Definition: Molecule.h:24
Select hierarchy particles identified by the biological name.
Definition: Selection.h:70
Support for the RMF file format for storing hierarchical molecular data and markup.
def get_residue_indexes
Retrieve the residue indexes for the given particle.
Definition: pmi/tools.py:504
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: rigid_bodies.h:661
Warning for probably incorrect input parameters.
Transformation3D get_random_local_transformation(Vector3D origin, double max_translation=5., double max_angle_in_rad=0.26)
Get a local transformation.
Temporarily stores residue information, even without structure available.
Store objects in order they were added, but with default type.
Definition: pmi/tools.py:824
Inferential scoring building on methods developed as part of the Inferential Structure Determination ...
def sublist_iterator
Yield all sublists of length >= lmin and <= lmax.
Definition: pmi/tools.py:579
A particle with a color.
Definition: Colored.h:23