IMP logo
IMP Reference Guide  develop.385bf31a7a,2026/08/05
The Integrative Modeling Platform
output.py
1 """@namespace IMP.pmi.output
2  Classes for writing output files and processing them.
3 """
4 
5 import IMP
6 import IMP.atom
7 import IMP.core
8 import IMP.pmi
9 import IMP.pmi.tools
10 import IMP.pmi.io
11 import os
12 import sys
13 import ast
14 import RMF
15 import numpy as np
16 import operator
17 import itertools
18 import warnings
19 import string
20 import ihm.format
21 import collections
22 import pickle
23 
24 
25 class _ChainIDs:
26  """Map indices to multi-character chain IDs.
27  We label the first 26 chains A-Z, then we move to two-letter
28  chain IDs: AA through AZ, then BA through BZ, through to ZZ.
29  This continues with longer chain IDs."""
30  def __getitem__(self, ind):
31  chars = string.ascii_uppercase
32  lc = len(chars)
33  ids = []
34  while ind >= lc:
35  ids.append(chars[ind % lc])
36  ind = ind // lc - 1
37  ids.append(chars[ind])
38  return "".join(reversed(ids))
39 
40 
42  """Base class for capturing a modeling protocol.
43  Unlike simple output of model coordinates, a complete
44  protocol includes the input data used, details on the restraints,
45  sampling, and clustering, as well as output models.
46  Use via IMP.pmi.topology.System.add_protocol_output().
47 
48  @see IMP.pmi.mmcif.ProtocolOutput for a concrete subclass that outputs
49  mmCIF files.
50  """
51  pass
52 
53 
54 def _flatten(seq):
55  for elt in seq:
56  if isinstance(elt, (tuple, list)):
57  for elt2 in _flatten(elt):
58  yield elt2
59  else:
60  yield elt
61 
62 
63 def _disambiguate_chain(chid, seen_chains):
64  """Make sure that the chain ID is unique; warn and correct if it isn't"""
65  # Handle null chain IDs
66  if chid == '\0':
67  chid = ' '
68 
69  if chid in seen_chains:
70  warnings.warn("Duplicate chain ID '%s' encountered" % chid,
72 
73  for suffix in itertools.count(1):
74  new_chid = chid + "%d" % suffix
75  if new_chid not in seen_chains:
76  seen_chains.add(new_chid)
77  return new_chid
78  seen_chains.add(chid)
79  return chid
80 
81 
82 def _write_pdb_internal(flpdb, particle_infos_for_pdb, geometric_center,
83  write_all_residues_per_bead):
84  for n, tupl in enumerate(particle_infos_for_pdb):
85  (xyz, atom_type, residue_type,
86  chain_id, residue_index, all_indexes, radius) = tupl
87  if atom_type is None:
88  atom_type = IMP.atom.AT_CA
89  if write_all_residues_per_bead and all_indexes is not None:
90  for residue_number in all_indexes:
91  flpdb.write(
92  IMP.atom.get_pdb_string((xyz[0] - geometric_center[0],
93  xyz[1] - geometric_center[1],
94  xyz[2] - geometric_center[2]),
95  n+1, atom_type, residue_type,
96  chain_id[:1], residue_number, ' ',
97  1.00, radius))
98  else:
99  flpdb.write(
100  IMP.atom.get_pdb_string((xyz[0] - geometric_center[0],
101  xyz[1] - geometric_center[1],
102  xyz[2] - geometric_center[2]),
103  n+1, atom_type, residue_type,
104  chain_id[:1], residue_index, ' ',
105  1.00, radius))
106  flpdb.write("ENDMDL\n")
107 
108 
109 _Entity = collections.namedtuple('_Entity', ('id', 'seq'))
110 _ChainInfo = collections.namedtuple('_ChainInfo', ('entity', 'name'))
111 
112 
113 def _get_chain_info(chains, root_hier):
114  chain_info = {}
115  entities = {}
116  all_entities = []
117  for mol in IMP.atom.get_by_type(root_hier, IMP.atom.MOLECULE_TYPE):
119  chain_id = chains[molname]
120  chain = IMP.atom.Chain(mol)
121  seq = chain.get_sequence()
122  if seq not in entities:
123  entities[seq] = e = _Entity(id=len(entities)+1, seq=seq)
124  all_entities.append(e)
125  entity = entities[seq]
126  info = _ChainInfo(entity=entity, name=molname)
127  chain_info[chain_id] = info
128  return chain_info, all_entities
129 
130 
131 def _write_mmcif_internal(flpdb, particle_infos_for_pdb, geometric_center,
132  write_all_residues_per_bead, chains, root_hier):
133  # get dict with keys=chain IDs, values=chain info
134  chain_info, entities = _get_chain_info(chains, root_hier)
135 
136  writer = ihm.format.CifWriter(flpdb)
137  writer.start_block('model')
138  with writer.category("_entry") as lp:
139  lp.write(id='model')
140 
141  with writer.loop("_entity", ["id", "type"]) as lp:
142  for e in entities:
143  lp.write(id=e.id, type="polymer")
144 
145  with writer.loop("_entity_poly",
146  ["entity_id", "pdbx_seq_one_letter_code"]) as lp:
147  for e in entities:
148  lp.write(entity_id=e.id, pdbx_seq_one_letter_code=e.seq)
149 
150  with writer.loop("_struct_asym", ["id", "entity_id", "details"]) as lp:
151  # Longer chain IDs (e.g. AA) should always come after shorter (e.g. Z)
152  for chid in sorted(chains.values(), key=lambda x: (len(x.strip()), x)):
153  ci = chain_info[chid]
154  lp.write(id=chid, entity_id=ci.entity.id, details=ci.name)
155 
156  with writer.loop("_atom_site",
157  ["group_PDB", "type_symbol", "label_atom_id",
158  "label_comp_id", "label_asym_id", "label_seq_id",
159  "auth_seq_id",
160  "Cartn_x", "Cartn_y", "Cartn_z", "label_entity_id",
161  "pdbx_pdb_model_num",
162  "id"]) as lp:
163  ordinal = 1
164  for n, tupl in enumerate(particle_infos_for_pdb):
165  (xyz, atom_type, residue_type,
166  chain_id, residue_index, all_indexes, radius) = tupl
167  ci = chain_info[chain_id]
168  if atom_type is None:
169  atom_type = IMP.atom.AT_CA
170  c = (xyz[0] - geometric_center[0],
171  xyz[1] - geometric_center[1],
172  xyz[2] - geometric_center[2])
173  if write_all_residues_per_bead and all_indexes is not None:
174  for residue_number in all_indexes:
175  lp.write(group_PDB='ATOM',
176  type_symbol='C',
177  label_atom_id=atom_type.get_string(),
178  label_comp_id=residue_type.get_string(),
179  label_asym_id=chain_id,
180  label_seq_id=residue_index,
181  auth_seq_id=residue_index, Cartn_x=c[0],
182  Cartn_y=c[1], Cartn_z=c[2], id=ordinal,
183  pdbx_pdb_model_num=1,
184  label_entity_id=ci.entity.id)
185  ordinal += 1
186  else:
187  lp.write(group_PDB='ATOM', type_symbol='C',
188  label_atom_id=atom_type.get_string(),
189  label_comp_id=residue_type.get_string(),
190  label_asym_id=chain_id,
191  label_seq_id=residue_index,
192  auth_seq_id=residue_index, Cartn_x=c[0],
193  Cartn_y=c[1], Cartn_z=c[2], id=ordinal,
194  pdbx_pdb_model_num=1,
195  label_entity_id=ci.entity.id)
196  ordinal += 1
197 
198 
199 class Output:
200  """Class for easy writing of PDBs, RMFs, and stat files
201 
202  @note Model should be updated prior to writing outputs.
203  """
204  def __init__(self, ascii=True, atomistic=False):
205  self.dictionary_pdbs = {}
206  self._pdb_mmcif = {}
207  self.dictionary_rmfs = {}
208  self.dictionary_stats = {}
209  self.dictionary_stats2 = {}
210  self.best_score_list = None
211  self.nbestscoring = None
212  self.prefixes = []
213  self.replica_exchange = False
214  self.ascii = ascii
215  self.initoutput = {}
216  self.residuetypekey = IMP.StringKey("ResidueName")
217  # 1-character chain IDs, suitable for PDB output
218  self.chainids = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
219  "abcdefghijklmnopqrstuvwxyz0123456789"
220  # Multi-character chain IDs, suitable for mmCIF output
221  self.multi_chainids = _ChainIDs()
222  self.dictchain = {} # keys are molecule names, values are chain ids
223  self.particle_infos_for_pdb = {}
224  self.atomistic = atomistic
225 
226  def get_pdb_names(self):
227  """Get a list of all PDB files being output by this instance"""
228  return list(self.dictionary_pdbs.keys())
229 
230  def get_rmf_names(self):
231  return list(self.dictionary_rmfs.keys())
232 
233  def get_stat_names(self):
234  return list(self.dictionary_stats.keys())
235 
236  def _init_dictchain(self, name, prot, multichar_chain=False, mmcif=False):
237  self.dictchain[name] = {}
238  seen_chains = set()
239 
240  # attempt to find PMI objects.
241  self.atomistic = True # detects automatically
242  for n, mol in enumerate(IMP.atom.get_by_type(
243  prot, IMP.atom.MOLECULE_TYPE)):
244  chid = IMP.atom.Chain(mol).get_id()
245  if not mmcif and len(chid) > 1:
246  raise ValueError(
247  "The system contains at least one chain ID (%s) that "
248  "is more than 1 character long; this cannot be "
249  "represented in PDB. Either write mmCIF files "
250  "instead, or assign 1-character IDs to all chains "
251  "(this can be done with the `chain_ids` argument to "
252  "BuildSystem.add_state())." % chid)
253  chid = _disambiguate_chain(chid, seen_chains)
255  self.dictchain[name][molname] = chid
256 
257  def init_pdb(self, name, prot, mmcif=False):
258  """Init PDB Writing.
259  @param name The PDB filename
260  @param prot The hierarchy to write to this pdb file
261  @param mmcif If True, write PDBs in mmCIF format
262  @note if the PDB name is 'System' then will use Selection
263  to get molecules
264  """
265  flpdb = open(name, 'w')
266  flpdb.close()
267  self.dictionary_pdbs[name] = prot
268  self._pdb_mmcif[name] = mmcif
269  self._init_dictchain(name, prot, mmcif=mmcif)
270 
271  def write_psf(self, filename, name):
272  flpsf = open(filename, 'w')
273  flpsf.write("PSF CMAP CHEQ" + "\n")
274  index_residue_pair_list = {}
275  (particle_infos_for_pdb, geometric_center) = \
276  self.get_particle_infos_for_pdb_writing(name)
277  nparticles = len(particle_infos_for_pdb)
278  flpsf.write(str(nparticles) + " !NATOM" + "\n")
279  for n, p in enumerate(particle_infos_for_pdb):
280  atom_index = n+1
281  residue_type = p[2]
282  chain = p[3]
283  resid = p[4]
284  flpsf.write('{0:8d}{1:1s}{2:4s}{3:1s}{4:4s}{5:1s}{6:4s}{7:1s}'
285  '{8:4s}{9:1s}{10:4s}{11:14.6f}{12:14.6f}{13:8d}'
286  '{14:14.6f}{15:14.6f}'.format(
287  atom_index, " ", chain, " ", str(resid), " ",
288  '"'+residue_type.get_string()+'"', " ", "C",
289  " ", "C", 1.0, 0.0, 0, 0.0, 0.0))
290  flpsf.write('\n')
291  if chain not in index_residue_pair_list:
292  index_residue_pair_list[chain] = [(atom_index, resid)]
293  else:
294  index_residue_pair_list[chain].append((atom_index, resid))
295 
296  # now write the connectivity
297  indexes_pairs = []
298  for chain in sorted(index_residue_pair_list.keys()):
299 
300  ls = index_residue_pair_list[chain]
301  # sort by residue
302  ls = sorted(ls, key=lambda tup: tup[1])
303  # get the index list
304  indexes = [x[0] for x in ls]
305  # get the contiguous pairs
306  indexes_pairs.extend(IMP.pmi.tools.sublist_iterator(
307  indexes, lmin=2, lmax=2))
308  nbonds = len(indexes_pairs)
309  flpsf.write(str(nbonds)+" !NBOND: bonds"+"\n")
310 
311  # save bonds in fixed column format
312  for i in range(0, len(indexes_pairs), 4):
313  for bond in indexes_pairs[i:i+4]:
314  flpsf.write('{0:8d}{1:8d}'.format(*bond))
315  flpsf.write('\n')
316 
317  del particle_infos_for_pdb
318  flpsf.close()
319 
320  def write_pdb(self, name, appendmode=True,
321  translate_to_geometric_center=False,
322  write_all_residues_per_bead=False):
323 
324  (particle_infos_for_pdb,
325  geometric_center) = self.get_particle_infos_for_pdb_writing(name)
326 
327  if not translate_to_geometric_center:
328  geometric_center = (0, 0, 0)
329 
330  filemode = 'a' if appendmode else 'w'
331  with open(name, filemode) as flpdb:
332  if self._pdb_mmcif[name]:
333  _write_mmcif_internal(flpdb, particle_infos_for_pdb,
334  geometric_center,
335  write_all_residues_per_bead,
336  self.dictchain[name],
337  self.dictionary_pdbs[name])
338  else:
339  _write_pdb_internal(flpdb, particle_infos_for_pdb,
340  geometric_center,
341  write_all_residues_per_bead)
342 
343  def get_prot_name_from_particle(self, name, p):
344  """Get the protein name from the particle.
345  This is done by traversing the hierarchy."""
346  return IMP.pmi.get_molecule_name_and_copy(p), True
347 
348  def get_particle_infos_for_pdb_writing(self, name):
349  # index_residue_pair_list={}
350 
351  # the resindexes dictionary keep track of residues that have
352  # been already added to avoid duplication
353  # highest resolution have highest priority
354  resindexes_dict = {}
355 
356  # this dictionary will contain the sequence of tuples needed to
357  # write the pdb
358  particle_infos_for_pdb = []
359 
360  geometric_center = [0, 0, 0]
361  atom_count = 0
362 
363  # select highest resolution, if hierarchy is non-empty
364  if (not IMP.core.XYZR.get_is_setup(self.dictionary_pdbs[name])
365  and self.dictionary_pdbs[name].get_number_of_children() == 0):
366  ps = []
367  else:
368  sel = IMP.atom.Selection(self.dictionary_pdbs[name], resolution=0)
369  ps = sel.get_selected_particles()
370 
371  for n, p in enumerate(ps):
372  protname, is_a_bead = self.get_prot_name_from_particle(name, p)
373 
374  if protname not in resindexes_dict:
375  resindexes_dict[protname] = []
376 
377  if IMP.atom.Atom.get_is_setup(p) and self.atomistic:
378  residue = IMP.atom.Residue(IMP.atom.Atom(p).get_parent())
379  rt = residue.get_residue_type()
380  resind = residue.get_index()
381  atomtype = IMP.atom.Atom(p).get_atom_type()
382  xyz = list(IMP.core.XYZ(p).get_coordinates())
383  radius = IMP.core.XYZR(p).get_radius()
384  geometric_center[0] += xyz[0]
385  geometric_center[1] += xyz[1]
386  geometric_center[2] += xyz[2]
387  atom_count += 1
388  particle_infos_for_pdb.append(
389  (xyz, atomtype, rt, self.dictchain[name][protname],
390  resind, None, radius))
391  resindexes_dict[protname].append(resind)
392 
394 
395  residue = IMP.atom.Residue(p)
396  resind = residue.get_index()
397  # skip if the residue was already added by atomistic resolution
398  # 0
399  if resind in resindexes_dict[protname]:
400  continue
401  else:
402  resindexes_dict[protname].append(resind)
403  rt = residue.get_residue_type()
404  xyz = IMP.core.XYZ(p).get_coordinates()
405  radius = IMP.core.XYZR(p).get_radius()
406  geometric_center[0] += xyz[0]
407  geometric_center[1] += xyz[1]
408  geometric_center[2] += xyz[2]
409  atom_count += 1
410  particle_infos_for_pdb.append(
411  (xyz, None, rt, self.dictchain[name][protname], resind,
412  None, radius))
413 
414  elif IMP.atom.Fragment.get_is_setup(p) and not is_a_bead:
415  resindexes = list(IMP.pmi.tools.get_residue_indexes(p))
416  resind = resindexes[len(resindexes) // 2]
417  if resind in resindexes_dict[protname]:
418  continue
419  else:
420  resindexes_dict[protname].append(resind)
421  rt = IMP.atom.ResidueType('BEA')
422  xyz = IMP.core.XYZ(p).get_coordinates()
423  radius = IMP.core.XYZR(p).get_radius()
424  geometric_center[0] += xyz[0]
425  geometric_center[1] += xyz[1]
426  geometric_center[2] += xyz[2]
427  atom_count += 1
428  particle_infos_for_pdb.append(
429  (xyz, None, rt, self.dictchain[name][protname], resind,
430  resindexes, radius))
431 
432  else:
433  if is_a_bead:
434  rt = IMP.atom.ResidueType('BEA')
435  resindexes = list(IMP.pmi.tools.get_residue_indexes(p))
436  if len(resindexes) > 0:
437  resind = resindexes[len(resindexes) // 2]
438  xyz = IMP.core.XYZ(p).get_coordinates()
439  radius = IMP.core.XYZR(p).get_radius()
440  geometric_center[0] += xyz[0]
441  geometric_center[1] += xyz[1]
442  geometric_center[2] += xyz[2]
443  atom_count += 1
444  particle_infos_for_pdb.append(
445  (xyz, None, rt, self.dictchain[name][protname],
446  resind, resindexes, radius))
447 
448  if atom_count > 0:
449  geometric_center = (geometric_center[0] / atom_count,
450  geometric_center[1] / atom_count,
451  geometric_center[2] / atom_count)
452 
453  # sort by chain ID, then residue index. Longer chain IDs (e.g. AA)
454  # should always come after shorter (e.g. Z)
455  particle_infos_for_pdb = sorted(particle_infos_for_pdb,
456  key=lambda x: (len(x[3]), x[3], x[4]))
457 
458  return (particle_infos_for_pdb, geometric_center)
459 
460  def write_pdbs(self, appendmode=True, mmcif=False):
461  for pdb in self.dictionary_pdbs.keys():
462  self.write_pdb(pdb, appendmode)
463 
464  def init_pdb_best_scoring(self, prefix, prot, nbestscoring,
465  replica_exchange=False, mmcif=False,
466  best_score_file='best.scores.rex.py'):
467  """Prepare for writing best-scoring PDBs (or mmCIFs) for a
468  sampling run.
469 
470  @param prefix Initial part of each PDB filename (e.g. 'model').
471  @param prot The top-level Hierarchy to output.
472  @param nbestscoring The number of best-scoring files to output.
473  @param replica_exchange Whether to combine best scores from a
474  replica exchange run.
475  @param mmcif If True, output models in mmCIF format. If False
476  (the default) output in legacy PDB format.
477  @param best_score_file The filename to use for replica
478  exchange scores.
479  """
480 
481  self._pdb_best_scoring_mmcif = mmcif
482  fileext = '.cif' if mmcif else '.pdb'
483  self.prefixes.append(prefix)
484  self.replica_exchange = replica_exchange
485  if not self.replica_exchange:
486  # common usage
487  # if you are not in replica exchange mode
488  # initialize the array of scores internally
489  self.best_score_list = []
490  else:
491  # otherwise the replicas must communicate
492  # through a common file to know what are the best scores
493  self.best_score_file_name = best_score_file
494  self.best_score_list = []
495  with open(self.best_score_file_name, "w") as best_score_file:
496  best_score_file.write(
497  "self.best_score_list=" + str(self.best_score_list) + "\n")
498 
499  self.nbestscoring = nbestscoring
500  for i in range(self.nbestscoring):
501  name = prefix + "." + str(i) + fileext
502  flpdb = open(name, 'w')
503  flpdb.close()
504  self.dictionary_pdbs[name] = prot
505  self._pdb_mmcif[name] = mmcif
506  self._init_dictchain(name, prot, mmcif=mmcif)
507 
508  def write_pdb_best_scoring(self, score):
509  if self.nbestscoring is None:
510  print("Output.write_pdb_best_scoring: init_pdb_best_scoring "
511  "not run")
512 
513  mmcif = self._pdb_best_scoring_mmcif
514  fileext = '.cif' if mmcif else '.pdb'
515  # update the score list
516  if self.replica_exchange:
517  # read the self.best_score_list from the file
518  with open(self.best_score_file_name) as fh:
519  self.best_score_list = ast.literal_eval(
520  fh.read().split('=')[1])
521 
522  if len(self.best_score_list) < self.nbestscoring:
523  self.best_score_list.append(score)
524  self.best_score_list.sort()
525  index = self.best_score_list.index(score)
526  for prefix in self.prefixes:
527  for i in range(len(self.best_score_list) - 2, index - 1, -1):
528  oldname = prefix + "." + str(i) + fileext
529  newname = prefix + "." + str(i + 1) + fileext
530  # rename on Windows fails if newname already exists
531  if os.path.exists(newname):
532  os.unlink(newname)
533  os.rename(oldname, newname)
534  filetoadd = prefix + "." + str(index) + fileext
535  self.write_pdb(filetoadd, appendmode=False)
536 
537  else:
538  if score < self.best_score_list[-1]:
539  self.best_score_list.append(score)
540  self.best_score_list.sort()
541  self.best_score_list.pop(-1)
542  index = self.best_score_list.index(score)
543  for prefix in self.prefixes:
544  for i in range(len(self.best_score_list) - 1,
545  index - 1, -1):
546  oldname = prefix + "." + str(i) + fileext
547  newname = prefix + "." + str(i + 1) + fileext
548  os.rename(oldname, newname)
549  filenametoremove = prefix + \
550  "." + str(self.nbestscoring) + fileext
551  os.remove(filenametoremove)
552  filetoadd = prefix + "." + str(index) + fileext
553  self.write_pdb(filetoadd, appendmode=False)
554 
555  if self.replica_exchange:
556  # write the self.best_score_list to the file
557  with open(self.best_score_file_name, "w") as best_score_file:
558  best_score_file.write(
559  "self.best_score_list=" + str(self.best_score_list) + '\n')
560 
561  def init_rmf(self, name, hierarchies, rs=None, geometries=None,
562  listofobjects=None):
563  """
564  Initialize an RMF file
565 
566  @param name the name of the RMF file
567  @param hierarchies the hierarchies to be included (it is a list)
568  @param rs optional, the restraint sets (it is a list)
569  @param geometries optional, the geometries (it is a list)
570  @param listofobjects optional, the list of objects for the stat
571  (it is a list)
572  """
573  rh = RMF.create_rmf_file(name)
574  IMP.rmf.add_hierarchies(rh, hierarchies)
575  cat = None
576  outputkey_rmfkey = None
577 
578  if rs is not None:
579  IMP.rmf.add_restraints(rh, rs)
580  if geometries is not None:
581  IMP.rmf.add_geometries(rh, geometries)
582  dict_objects = []
583  callable_objects = []
584  if listofobjects is not None:
585  cat = rh.get_category("stat")
586  outputkey_rmfkey = {}
587  for o in listofobjects:
588  if not hasattr(o, "get_output"):
589  raise ValueError(
590  "Output: object %s doesn't have get_output() method"
591  % str(o))
592  # get_output() can return either a dict or a callable;
593  # store these in different lists
594  output = o.get_output()
595  if callable(output):
596  callable_objects.append(output)
597  output = output(None)
598  else:
599  dict_objects.append(o)
600  for outputkey in output:
601  rmftag = RMF.string_tag
602  if isinstance(output[outputkey], float):
603  rmftag = RMF.float_tag
604  elif isinstance(output[outputkey], int):
605  rmftag = RMF.int_tag
606  elif isinstance(output[outputkey], str):
607  rmftag = RMF.string_tag
608  else:
609  rmftag = RMF.string_tag
610  rmfkey = rh.get_key(cat, outputkey, rmftag)
611  outputkey_rmfkey[outputkey] = rmfkey
612  outputkey_rmfkey["rmf_file"] = \
613  rh.get_key(cat, "rmf_file", RMF.string_tag)
614  outputkey_rmfkey["rmf_frame_index"] = \
615  rh.get_key(cat, "rmf_frame_index", RMF.int_tag)
616 
617  self.dictionary_rmfs[name] = (rh, cat, outputkey_rmfkey,
618  dict_objects, callable_objects)
619 
620  def add_restraints_to_rmf(self, name, objectlist):
621  for o in _flatten(objectlist):
622  try:
623  rs = o.get_restraint_for_rmf()
624  if not isinstance(rs, (list, tuple)):
625  rs = [rs]
626  except: # noqa: E722
627  rs = [o.get_restraint()]
629  self.dictionary_rmfs[name][0], rs)
630 
631  def add_geometries_to_rmf(self, name, objectlist):
632  for o in objectlist:
633  geos = o.get_geometries()
634  IMP.rmf.add_geometries(self.dictionary_rmfs[name][0], geos)
635 
636  def add_particle_pair_from_restraints_to_rmf(self, name, objectlist):
637  for o in objectlist:
638 
639  pps = o.get_particle_pairs()
640  for pp in pps:
642  self.dictionary_rmfs[name][0],
644 
645  def write_rmf(self, name):
646  IMP.rmf.save_frame(self.dictionary_rmfs[name][0])
647  if self.dictionary_rmfs[name][1] is not None:
648  outputkey_rmfkey = self.dictionary_rmfs[name][2]
649  dict_objects = self.dictionary_rmfs[name][3]
650  callable_objects = self.dictionary_rmfs[name][4]
651 
652  def all_output():
653  for obj in dict_objects:
654  yield obj.get_output()
655  for obj in callable_objects:
656  yield obj(None)
657 
658  for output in all_output():
659  for outputkey in output:
660  rmfkey = outputkey_rmfkey[outputkey]
661  try:
662  n = self.dictionary_rmfs[name][0].get_root_node()
663  n.set_value(rmfkey, output[outputkey])
664  except NotImplementedError:
665  continue
666  rmfkey = outputkey_rmfkey["rmf_file"]
667  self.dictionary_rmfs[name][0].get_root_node().set_value(
668  rmfkey, name)
669  rmfkey = outputkey_rmfkey["rmf_frame_index"]
670  nframes = self.dictionary_rmfs[name][0].get_number_of_frames()
671  self.dictionary_rmfs[name][0].get_root_node().set_value(
672  rmfkey, nframes-1)
673  self.dictionary_rmfs[name][0].flush()
674 
675  def close_rmf(self, name):
676  rh = self.dictionary_rmfs[name][0]
677  del self.dictionary_rmfs[name]
678  del rh
679 
680  def write_rmfs(self):
681  for rmfinfo in self.dictionary_rmfs.keys():
682  self.write_rmf(rmfinfo[0])
683 
684  def set_output_entry(self, key, value):
685  self.initoutput.update({key: value})
686 
687  def get_stat(self, name):
688  output = {}
689  for obj in self.dictionary_stats[name]:
690  output.update(obj.get_output())
691  return output
692 
693  def write_test(self, name, listofobjects):
694  flstat = open(name, 'w')
695  output = self.initoutput
696  for o in listofobjects:
697  if (not hasattr(o, "get_test_output")
698  and not hasattr(o, "get_output")):
699  raise ValueError(
700  "Output: object %s doesn't have get_output() or "
701  "get_test_output() method" % str(o))
702  self.dictionary_stats[name] = listofobjects
703 
704  for obj in self.dictionary_stats[name]:
705  try:
706  d = obj.get_test_output()
707  except AttributeError:
708  d = obj.get_output()
709  if callable(d):
710  # Get any scores using the current IMP Model
711  d = d(None)
712  # remove all entries that begin with _ (private entries)
713  dfiltered = dict((k, v) for k, v in d.items() if k[0] != "_")
714  output.update(dfiltered)
715  flstat.write("%s \n" % output)
716  flstat.close()
717 
718  def test(self, name, listofobjects, tolerance=1e-5):
719  output = self.initoutput
720  for o in listofobjects:
721  if (not hasattr(o, "get_test_output")
722  and not hasattr(o, "get_output")):
723  raise ValueError(
724  "Output: object %s doesn't have get_output() or "
725  "get_test_output() method" % str(o))
726  for obj in listofobjects:
727  try:
728  out = obj.get_test_output()
729  except AttributeError:
730  out = obj.get_output()
731  if callable(out):
732  # Get any scores using the current IMP Model
733  out = out(None)
734  output.update(out)
735 
736  flstat = open(name, 'r')
737 
738  passed = True
739  for fl in flstat:
740  test_dict = ast.literal_eval(fl)
741  for k in test_dict:
742  if k in output:
743  old_value = str(test_dict[k])
744  new_value = str(output[k])
745  try:
746  float(old_value)
747  is_float = True
748  except ValueError:
749  is_float = False
750 
751  if is_float:
752  fold = float(old_value)
753  fnew = float(new_value)
754  diff = abs(fold - fnew)
755  if diff > tolerance:
756  print("%s: test failed, old value: %s new value %s; "
757  "diff %f > %f" % (str(k), str(old_value),
758  str(new_value), diff,
759  tolerance), file=sys.stderr)
760  passed = False
761  elif test_dict[k] != output[k]:
762  if len(old_value) < 50 and len(new_value) < 50:
763  print("%s: test failed, old value: %s new value %s"
764  % (str(k), old_value, new_value),
765  file=sys.stderr)
766  passed = False
767  else:
768  print("%s: test failed, omitting results (too long)"
769  % str(k), file=sys.stderr)
770  passed = False
771 
772  else:
773  print("%s from old objects (file %s) not in new objects"
774  % (str(k), str(name)), file=sys.stderr)
775  flstat.close()
776  return passed
777 
778  def get_environment_variables(self):
779  import os
780  return str(os.environ)
781 
782  def get_versions_of_relevant_modules(self):
783  import IMP
784  versions = {}
785  versions["IMP_VERSION"] = IMP.get_module_version()
786  versions["PMI_VERSION"] = IMP.pmi.get_module_version()
787  try:
788  import IMP.isd2
789  versions["ISD2_VERSION"] = IMP.isd2.get_module_version()
790  except ImportError:
791  pass
792  try:
793  import IMP.isd_emxl
794  versions["ISD_EMXL_VERSION"] = IMP.isd_emxl.get_module_version()
795  except ImportError:
796  pass
797  return versions
798 
799  def init_stat2(self, name, listofobjects, extralabels=None,
800  listofsummedobjects=None, jax_model=None, append=False):
801  """Write the header for a stat file in v2 format.
802  Lines can then be written to the stat file by calling write_stat2()
803  with the same file name.
804 
805  @param name The file name to write to.
806  @param listofobjects PMI objects that will be reported in the file.
807  Each object must implement the get_output() method.
808  This can either return a dict containing data from the
809  current state of the model, or a callable which returns
810  a similar dict of data each time it is called.
811  """
812  # this is a new stat file that should be less
813  # space greedy!
814  # listofsummedobjects must be in the form
815  # [([obj1,obj2,obj3,obj4...],label)]
816  # extralabels
817 
818  if listofsummedobjects is None:
819  listofsummedobjects = []
820  if extralabels is None:
821  extralabels = []
822  output = {}
823  stat2_keywords = {"STAT2HEADER": "STAT2HEADER"}
824  stat2_keywords.update(
825  {"STAT2HEADER_ENVIRON": str(self.get_environment_variables())})
826  stat2_keywords.update(
827  {"STAT2HEADER_IMP_VERSIONS":
828  str(self.get_versions_of_relevant_modules())})
829  stat2_inverse = {}
830 
831  dict_objects = []
832  callable_objects = []
833  for obj in listofobjects:
834  if not hasattr(obj, "get_output"):
835  raise ValueError(
836  "Output: object %s doesn't have get_output() method"
837  % str(obj))
838  else:
839  # get_output() can return either a dict or a callable;
840  # store these in different lists
841  d = obj.get_output()
842  if callable(d):
843  callable_objects.append(d)
844  d = d(jax_model)
845  else:
846  dict_objects.append(obj)
847  # remove all entries that begin with _ (private entries)
848  dfiltered = dict((k, v)
849  for k, v in d.items() if k[0] != "_")
850  output.update(dfiltered)
851 
852  # check for customizable entries
853  for obj in listofsummedobjects:
854  for t in obj[0]:
855  if not hasattr(t, "get_output"):
856  raise ValueError(
857  "Output: object %s doesn't have get_output() method"
858  % str(t))
859  else:
860  if "_TotalScore" not in t.get_output():
861  raise ValueError(
862  "Output: object %s doesn't have _TotalScore "
863  "entry to be summed" % str(t))
864  else:
865  output.update({obj[1]: 0.0})
866 
867  for k in extralabels:
868  output.update({k: 0.0})
869 
870  for n, k in enumerate(output):
871  stat2_keywords.update({n: k})
872  stat2_inverse.update({k: n})
873 
874  if append:
875  self._check_append_header(name, stat2_keywords)
876  else:
877  with open(name, 'w') as flstat:
878  flstat.write("%s \n" % stat2_keywords)
879 
880  self.dictionary_stats2[name] = (
881  dict_objects, callable_objects,
882  stat2_inverse,
883  listofsummedobjects,
884  extralabels)
885 
886  def _check_append_header(self, name, stat2_keywords):
887  """Verify that existing file header matches our data structure"""
888  with open(name) as flstat:
889  header = flstat.readline()
890  d = ast.literal_eval(header)
891  if not isinstance(d, dict) or 'STAT2HEADER' not in d:
892  raise ValueError(
893  f"stat file {name} first line is not a valid header")
894  d = {k: v for (k, v) in d.items() if isinstance(k, int)}
895  newd = {k: v for (k, v) in stat2_keywords.items()
896  if isinstance(k, int)}
897  if d != newd:
898  raise ValueError(
899  f"stat file {name} header does not match append data")
900 
901  def _count_stat2_nframe(self, name, nframe_key, nframe):
902  """Count the number of stat file lines up to the given frame"""
903  with open(name, "r") as flstat:
904  header = flstat.readline()
905  d = ast.literal_eval(header)
906  keymap = {v: k for (k, v) in d.items() if isinstance(k, int)}
907  nframe_key = keymap[nframe_key]
908  nline = 0
909  while True:
910  line = flstat.readline()
911  nline += 1
912  if not line:
913  return None
914  nframe_file = int(ast.literal_eval(line)[nframe_key])
915  if nframe_file >= nframe:
916  return nline - 1
917 
918  def _truncate_stat2_nline(self, name, nline):
919  """Truncate the given stat file to have exactly `nline` non-header
920  lines"""
921  # Open in binary mode because we only care about line endings, not
922  # encoding; this might be a little faster
923  with open(name, "rb+") as flstat:
924  _ = flstat.readline()
925  for _ in range(nline):
926  _ = flstat.readline()
927  flstat.truncate(flstat.tell())
928 
929  def write_stat2(self, name, appendmode=True, jax_model=None):
930  """Write a single line to a stat file previously created
931  with init_stat2().
932 
933  @param name The file name to write to.
934  """
935  output = {}
936  (dict_objects, callable_objects, stat2_inverse, listofsummedobjects,
937  extralabels) = self.dictionary_stats2[name]
938 
939  def all_output():
940  for obj in dict_objects:
941  yield obj.get_output()
942  for obj in callable_objects:
943  yield obj(jax_model)
944 
945  # writing objects
946  for od in all_output():
947  dfiltered = dict((k, v) for k, v in od.items() if k[0] != "_")
948  for k in dfiltered:
949  output.update({stat2_inverse[k]: od[k]})
950 
951  # writing summedobjects
952  for so in listofsummedobjects:
953  partial_score = 0.0
954  for t in so[0]:
955  d = t.get_output()
956  partial_score += float(d["_TotalScore"])
957  output.update({stat2_inverse[so[1]]: str(partial_score)})
958 
959  # writing extralabels
960  for k in extralabels:
961  if k in self.initoutput:
962  output.update({stat2_inverse[k]: self.initoutput[k]})
963  else:
964  output.update({stat2_inverse[k]: "None"})
965 
966  with open(name, 'a' if appendmode else 'w') as flstat:
967  flstat.write("%s \n" % output)
968 
969  def write_stats2(self):
970  for stat in self.dictionary_stats2.keys():
971  self.write_stat2(stat)
972 
973 
975  """Collect statistics from ProcessOutput.get_fields().
976  Counters of the total number of frames read, plus the models that
977  passed the various filters used in get_fields(), are provided."""
978  def __init__(self):
979  self.total = 0
980  self.passed_get_every = 0
981  self.passed_filterout = 0
982  self.passed_filtertuple = 0
983 
984 
986  """A class for reading stat files (either rmf or ascii v1 and v2)"""
987  def __init__(self, filename):
988  self.filename = filename
989  self.isstat1 = False
990  self.isstat2 = False
991  self.isrmf = False
992 
993  if self.filename is None:
994  raise ValueError("No file name provided. Use -h for help")
995 
996  try:
997  # let's see if that is an rmf file
998  rh = RMF.open_rmf_file_read_only(self.filename)
999  self.isrmf = True
1000  cat = rh.get_category('stat')
1001  rmf_klist = rh.get_keys(cat)
1002  self.rmf_names_keys = dict([(rh.get_name(k), k)
1003  for k in rmf_klist])
1004  del rh
1005 
1006  except IOError:
1007  f = open(self.filename, "r")
1008  # try with an ascii stat file
1009  # get the keys from the first line
1010  for line in f.readlines():
1011  d = ast.literal_eval(line)
1012  self.klist = list(d.keys())
1013  # check if it is a stat2 file
1014  if "STAT2HEADER" in self.klist:
1015  self.isstat2 = True
1016  for k in self.klist:
1017  if "STAT2HEADER" in str(k):
1018  # if print_header: print k, d[k]
1019  del d[k]
1020  stat2_dict = d
1021  # get the list of keys sorted by value
1022  kkeys = [k[0]
1023  for k in sorted(stat2_dict.items(),
1024  key=operator.itemgetter(1))]
1025  self.klist = [k[1]
1026  for k in sorted(stat2_dict.items(),
1027  key=operator.itemgetter(1))]
1028  self.invstat2_dict = {}
1029  for k in kkeys:
1030  self.invstat2_dict.update({stat2_dict[k]: k})
1031  else:
1033  "statfile v1 is deprecated. "
1034  "Please convert to statfile v2.\n")
1035  self.isstat1 = True
1036  self.klist.sort()
1037 
1038  break
1039  f.close()
1040 
1041  def get_keys(self):
1042  if self.isrmf:
1043  return sorted(self.rmf_names_keys.keys())
1044  else:
1045  return self.klist
1046 
1047  def show_keys(self, ncolumns=2, truncate=65):
1048  IMP.pmi.tools.print_multicolumn(self.get_keys(), ncolumns, truncate)
1049 
1050  def get_fields(self, fields, filtertuple=None, filterout=None, get_every=1,
1051  statistics=None):
1052  '''
1053  Get the desired field names, and return a dictionary.
1054  Namely, "fields" are the queried keys in the stat file
1055  (eg. ["Total_Score",...])
1056  The returned data structure is a dictionary, where each key is
1057  a field and the value is the time series (ie, frame ordered series)
1058  of that field (ie, {"Total_Score":[Score_0,Score_1,Score_2,,...],....})
1059 
1060  @param fields (list of strings) queried keys in the stat file
1061  (eg. "Total_Score"....)
1062  @param filterout specify if you want to "grep" out something from
1063  the file, so that it is faster
1064  @param filtertuple a tuple that contains
1065  ("TheKeyToBeFiltered",relationship,value)
1066  where relationship = "<", "==", or ">"
1067  @param get_every only read every Nth line from the file
1068  @param statistics if provided, accumulate statistics in an
1069  OutputStatistics object
1070  '''
1071 
1072  if statistics is None:
1073  statistics = OutputStatistics()
1074  outdict = {}
1075  for field in fields:
1076  outdict[field] = []
1077 
1078  # print fields values
1079  if self.isrmf:
1080  rh = RMF.open_rmf_file_read_only(self.filename)
1081  nframes = rh.get_number_of_frames()
1082  for i in range(nframes):
1083  statistics.total += 1
1084  # "get_every" and "filterout" not enforced for RMF
1085  statistics.passed_get_every += 1
1086  statistics.passed_filterout += 1
1087  rh.set_current_frame(RMF.FrameID(i))
1088  if filtertuple is not None:
1089  keytobefiltered = filtertuple[0]
1090  relationship = filtertuple[1]
1091  value = filtertuple[2]
1092  datavalue = rh.get_root_node().get_value(
1093  self.rmf_names_keys[keytobefiltered])
1094  if self.isfiltered(datavalue, relationship, value):
1095  continue
1096 
1097  statistics.passed_filtertuple += 1
1098  for field in fields:
1099  outdict[field].append(rh.get_root_node().get_value(
1100  self.rmf_names_keys[field]))
1101 
1102  else:
1103  f = open(self.filename, "r")
1104  line_number = 0
1105 
1106  for line in f.readlines():
1107  statistics.total += 1
1108  if filterout is not None:
1109  if filterout in line:
1110  continue
1111  statistics.passed_filterout += 1
1112  line_number += 1
1113 
1114  if line_number % get_every != 0:
1115  if line_number == 1 and self.isstat2:
1116  statistics.total -= 1
1117  statistics.passed_filterout -= 1
1118  continue
1119  statistics.passed_get_every += 1
1120  try:
1121  d = ast.literal_eval(line)
1122  except: # noqa: E722
1123  print("# Warning: skipped line number " + str(line_number)
1124  + " not a valid line")
1125  continue
1126 
1127  if self.isstat1:
1128 
1129  if filtertuple is not None:
1130  keytobefiltered = filtertuple[0]
1131  relationship = filtertuple[1]
1132  value = filtertuple[2]
1133  datavalue = d[keytobefiltered]
1134  if self.isfiltered(datavalue, relationship, value):
1135  continue
1136 
1137  statistics.passed_filtertuple += 1
1138  [outdict[field].append(d[field]) for field in fields]
1139 
1140  elif self.isstat2:
1141  if line_number == 1:
1142  statistics.total -= 1
1143  statistics.passed_filterout -= 1
1144  statistics.passed_get_every -= 1
1145  continue
1146 
1147  if filtertuple is not None:
1148  keytobefiltered = filtertuple[0]
1149  relationship = filtertuple[1]
1150  value = filtertuple[2]
1151  datavalue = d[self.invstat2_dict[keytobefiltered]]
1152  if self.isfiltered(datavalue, relationship, value):
1153  continue
1154 
1155  statistics.passed_filtertuple += 1
1156  [outdict[field].append(d[self.invstat2_dict[field]])
1157  for field in fields]
1158 
1159  f.close()
1160 
1161  return outdict
1162 
1163  def isfiltered(self, datavalue, relationship, refvalue):
1164  dofilter = False
1165  try:
1166  _ = float(datavalue)
1167  except ValueError:
1168  raise ValueError("ProcessOutput.filter: datavalue cannot be "
1169  "converted into a float")
1170 
1171  if relationship == "<":
1172  if float(datavalue) >= refvalue:
1173  dofilter = True
1174  if relationship == ">":
1175  if float(datavalue) <= refvalue:
1176  dofilter = True
1177  if relationship == "==":
1178  if float(datavalue) != refvalue:
1179  dofilter = True
1180  return dofilter
1181 
1182 
1184  """ class to allow more advanced handling of RMF files.
1185  It is both a container and a IMP.atom.Hierarchy.
1186  - it is iterable (while loading the corresponding frame)
1187  - Item brackets [] load the corresponding frame
1188  - slice create an iterator
1189  - can relink to another RMF file
1190  """
1191  def __init__(self, model, rmf_file_name):
1192  """
1193  @param model: the IMP.Model()
1194  @param rmf_file_name: str, path of the rmf file
1195  """
1196  self.model = model
1197  try:
1198  self.rh_ref = RMF.open_rmf_file_read_only(rmf_file_name)
1199  except TypeError:
1200  raise TypeError("Wrong rmf file name or type: %s"
1201  % str(rmf_file_name))
1202  hs = IMP.rmf.create_hierarchies(self.rh_ref, self.model)
1203  IMP.rmf.load_frame(self.rh_ref, RMF.FrameID(0))
1204  self.root_hier_ref = hs[0]
1205  super().__init__(self.root_hier_ref)
1206  self.model.update()
1207  self.ColorHierarchy = None
1208 
1209  def link_to_rmf(self, rmf_file_name):
1210  """
1211  Link to another RMF file
1212  """
1213  self.rh_ref = RMF.open_rmf_file_read_only(rmf_file_name)
1214  IMP.rmf.link_hierarchies(self.rh_ref, [self])
1215  if self.ColorHierarchy:
1216  self.ColorHierarchy.method()
1217  RMFHierarchyHandler.set_frame(self, 0)
1218 
1219  def set_frame(self, index):
1220  try:
1221  IMP.rmf.load_frame(self.rh_ref, RMF.FrameID(index))
1222  except: # noqa: E722
1223  print("skipping frame %s:%d\n" % (self.current_rmf, index))
1224  self.model.update()
1225 
1226  def get_number_of_frames(self):
1227  return self.rh_ref.get_number_of_frames()
1228 
1229  def __getitem__(self, int_slice_adaptor):
1230  if isinstance(int_slice_adaptor, int):
1231  self.set_frame(int_slice_adaptor)
1232  return int_slice_adaptor
1233  elif isinstance(int_slice_adaptor, slice):
1234  return self.__iter__(int_slice_adaptor)
1235  else:
1236  raise TypeError("Unknown Type")
1237 
1238  def __len__(self):
1239  return self.get_number_of_frames()
1240 
1241  def __iter__(self, slice_key=None):
1242  if slice_key is None:
1243  for nframe in range(len(self)):
1244  yield self[nframe]
1245  else:
1246  for nframe in list(range(len(self)))[slice_key]:
1247  yield self[nframe]
1248 
1249 
1250 class CacheHierarchyCoordinates:
1251  def __init__(self, StatHierarchyHandler):
1252  self.xyzs = []
1253  self.nrms = []
1254  self.rbs = []
1255  self.nrm_coors = {}
1256  self.xyz_coors = {}
1257  self.rb_trans = {}
1258  self.current_index = None
1259  self.rmfh = StatHierarchyHandler
1260  rbs, xyzs = IMP.pmi.tools.get_rbs_and_beads([self.rmfh])
1261  self.model = self.rmfh.get_model()
1262  self.rbs = rbs
1263  for xyz in xyzs:
1265  nrm = IMP.core.NonRigidMember(xyz)
1266  self.nrms.append(nrm)
1267  else:
1268  fb = IMP.core.XYZ(xyz)
1269  self.xyzs.append(fb)
1270 
1271  def do_store(self, index):
1272  self.rb_trans[index] = {}
1273  self.nrm_coors[index] = {}
1274  self.xyz_coors[index] = {}
1275  for rb in self.rbs:
1276  self.rb_trans[index][rb] = rb.get_reference_frame()
1277  for nrm in self.nrms:
1278  self.nrm_coors[index][nrm] = nrm.get_internal_coordinates()
1279  for xyz in self.xyzs:
1280  self.xyz_coors[index][xyz] = xyz.get_coordinates()
1281  self.current_index = index
1282 
1283  def do_update(self, index):
1284  if self.current_index != index:
1285  for rb in self.rbs:
1286  rb.set_reference_frame(self.rb_trans[index][rb])
1287  for nrm in self.nrms:
1288  nrm.set_internal_coordinates(self.nrm_coors[index][nrm])
1289  for xyz in self.xyzs:
1290  xyz.set_coordinates(self.xyz_coors[index][xyz])
1291  self.current_index = index
1292  self.model.update()
1293 
1294  def get_number_of_frames(self):
1295  return len(self.rb_trans.keys())
1296 
1297  def __getitem__(self, index):
1298  if isinstance(index, int):
1299  return index in self.rb_trans.keys()
1300  else:
1301  raise TypeError("Unknown Type")
1302 
1303  def __len__(self):
1304  return self.get_number_of_frames()
1305 
1306 
1308  """ class to link stat files to several rmf files """
1309  def __init__(self, model=None, stat_file=None,
1310  number_best_scoring_models=None, score_key=None,
1311  StatHierarchyHandler=None, cache=None):
1312  """
1313 
1314  @param model: IMP.Model()
1315  @param stat_file: either 1) a list or 2) a single stat file names
1316  (either rmfs or ascii, or pickled data or pickled cluster),
1317  3) a dictionary containing an rmf/ascii
1318  stat file name as key and a list of frames as values
1319  @param number_best_scoring_models:
1320  @param StatHierarchyHandler: copy constructor input object
1321  @param cache: cache coordinates and rigid body transformations.
1322  """
1323 
1324  if StatHierarchyHandler is not None:
1325  # overrides all other arguments
1326  # copy constructor: create a copy with
1327  # different RMFHierarchyHandler
1328  self.model = StatHierarchyHandler.model
1329  self.data = StatHierarchyHandler.data
1330  self.number_best_scoring_models = \
1331  StatHierarchyHandler.number_best_scoring_models
1332  self.is_setup = True
1333  self.current_rmf = StatHierarchyHandler.current_rmf
1334  self.current_frame = None
1335  self.current_index = None
1336  self.score_threshold = StatHierarchyHandler.score_threshold
1337  self.score_key = StatHierarchyHandler.score_key
1338  self.cache = StatHierarchyHandler.cache
1339  super().__init__(self.model, self.current_rmf)
1340  if self.cache:
1341  self.cache = CacheHierarchyCoordinates(self)
1342  else:
1343  self.cache = None
1344  self.set_frame(0)
1345 
1346  else:
1347  # standard constructor
1348  self.model = model
1349  self.data = []
1350  self.number_best_scoring_models = number_best_scoring_models
1351  self.cache = cache
1352 
1353  if score_key is None:
1354  self.score_key = "Total_Score"
1355  else:
1356  self.score_key = score_key
1357  self.is_setup = None
1358  self.current_rmf = None
1359  self.current_frame = None
1360  self.current_index = None
1361  self.score_threshold = None
1362 
1363  if isinstance(stat_file, str):
1364  self.add_stat_file(stat_file)
1365  elif isinstance(stat_file, list):
1366  for f in stat_file:
1367  self.add_stat_file(f)
1368 
1369  def add_stat_file(self, stat_file):
1370  try:
1371  '''check that it is not a pickle file with saved data
1372  from a previous calculation'''
1373  self.load_data(stat_file)
1374 
1375  if self.number_best_scoring_models:
1376  scores = self.get_scores()
1377  max_score = sorted(scores)[
1378  0:min(len(self), self.number_best_scoring_models)][-1]
1379  self.do_filter_by_score(max_score)
1380 
1381  except pickle.UnpicklingError:
1382  '''alternatively read the ascii stat files'''
1383  try:
1384  scores, rmf_files, rmf_frame_indexes, features = \
1385  self.get_info_from_stat_file(stat_file,
1386  self.score_threshold)
1387  except (KeyError, SyntaxError):
1388  # in this case check that is it an rmf file, probably
1389  # without stat stored in
1390  try:
1391  # let's see if that is an rmf file
1392  rh = RMF.open_rmf_file_read_only(stat_file)
1393  nframes = rh.get_number_of_frames()
1394  scores = [0.0]*nframes
1395  rmf_files = [stat_file]*nframes
1396  rmf_frame_indexes = range(nframes)
1397  features = {}
1398  except: # noqa: E722
1399  return
1400 
1401  if len(set(rmf_files)) > 1:
1402  raise ("Multiple RMF files found")
1403 
1404  if not rmf_files:
1405  print("StatHierarchyHandler: Error: Trying to set none as "
1406  "rmf_file (probably empty stat file), aborting")
1407  return
1408 
1409  for n, index in enumerate(rmf_frame_indexes):
1410  featn_dict = dict([(k, features[k][n]) for k in features])
1411  self.data.append(IMP.pmi.output.DataEntry(
1412  stat_file, rmf_files[n], index, scores[n], featn_dict))
1413 
1414  if self.number_best_scoring_models:
1415  scores = self.get_scores()
1416  max_score = sorted(scores)[
1417  0:min(len(self), self.number_best_scoring_models)][-1]
1418  self.do_filter_by_score(max_score)
1419 
1420  if not self.is_setup:
1421  RMFHierarchyHandler.__init__(
1422  self, self.model, self.get_rmf_names()[0])
1423  if self.cache:
1424  self.cache = CacheHierarchyCoordinates(self)
1425  else:
1426  self.cache = None
1427  self.is_setup = True
1428  self.current_rmf = self.get_rmf_names()[0]
1429 
1430  self.set_frame(0)
1431 
1432  def save_data(self, filename='data.pkl'):
1433  with open(filename, 'wb') as fl:
1434  pickle.dump(self.data, fl)
1435 
1436  def load_data(self, filename='data.pkl'):
1437  with open(filename, 'rb') as fl:
1438  data_structure = pickle.load(fl)
1439  # first check that it is a list
1440  if not isinstance(data_structure, list):
1441  raise TypeError(
1442  "%filename should contain a list of IMP.pmi.output.DataEntry "
1443  "or IMP.pmi.output.Cluster" % filename)
1444  # second check the types
1445  if all(isinstance(item, IMP.pmi.output.DataEntry)
1446  for item in data_structure):
1447  self.data = data_structure
1448  elif all(isinstance(item, IMP.pmi.output.Cluster)
1449  for item in data_structure):
1450  nmodels = 0
1451  for cluster in data_structure:
1452  nmodels += len(cluster)
1453  self.data = [None]*nmodels
1454  for cluster in data_structure:
1455  for n, data in enumerate(cluster):
1456  index = cluster.members[n]
1457  self.data[index] = data
1458  else:
1459  raise TypeError(
1460  "%filename should contain a list of IMP.pmi.output.DataEntry "
1461  "or IMP.pmi.output.Cluster" % filename)
1462 
1463  def set_frame(self, index):
1464  if self.cache is not None and self.cache[index]:
1465  self.cache.do_update(index)
1466  else:
1467  nm = self.data[index].rmf_name
1468  fidx = self.data[index].rmf_index
1469  if nm != self.current_rmf:
1470  self.link_to_rmf(nm)
1471  self.current_rmf = nm
1472  self.current_frame = -1
1473  if fidx != self.current_frame:
1474  RMFHierarchyHandler.set_frame(self, fidx)
1475  self.current_frame = fidx
1476  if self.cache is not None:
1477  self.cache.do_store(index)
1478 
1479  self.current_index = index
1480 
1481  def __getitem__(self, int_slice_adaptor):
1482  if isinstance(int_slice_adaptor, int):
1483  self.set_frame(int_slice_adaptor)
1484  return self.data[int_slice_adaptor]
1485  elif isinstance(int_slice_adaptor, slice):
1486  return self.__iter__(int_slice_adaptor)
1487  else:
1488  raise TypeError("Unknown Type")
1489 
1490  def __len__(self):
1491  return len(self.data)
1492 
1493  def __iter__(self, slice_key=None):
1494  if slice_key is None:
1495  for i in range(len(self)):
1496  yield self[i]
1497  else:
1498  for i in range(len(self))[slice_key]:
1499  yield self[i]
1500 
1501  def do_filter_by_score(self, maximum_score):
1502  self.data = [d for d in self.data if d.score <= maximum_score]
1503 
1504  def get_scores(self):
1505  return [d.score for d in self.data]
1506 
1507  def get_feature_series(self, feature_name):
1508  return [d.features[feature_name] for d in self.data]
1509 
1510  def get_feature_names(self):
1511  return self.data[0].features.keys()
1512 
1513  def get_rmf_names(self):
1514  return [d.rmf_name for d in self.data]
1515 
1516  def get_stat_files_names(self):
1517  return [d.stat_file for d in self.data]
1518 
1519  def get_rmf_indexes(self):
1520  return [d.rmf_index for d in self.data]
1521 
1522  def get_info_from_stat_file(self, stat_file, score_threshold=None):
1523  po = ProcessOutput(stat_file)
1524  fs = po.get_keys()
1525  models = IMP.pmi.io.get_best_models(
1526  [stat_file], score_key=self.score_key, feature_keys=fs,
1527  rmf_file_key="rmf_file", rmf_file_frame_key="rmf_frame_index",
1528  prefiltervalue=score_threshold, get_every=1)
1529 
1530  scores = [float(y) for y in models[2]]
1531  rmf_files = models[0]
1532  rmf_frame_indexes = models[1]
1533  features = models[3]
1534  return scores, rmf_files, rmf_frame_indexes, features
1535 
1536 
1538  '''
1539  A class to store data associated to a model
1540  '''
1541  def __init__(self, stat_file=None, rmf_name=None, rmf_index=None,
1542  score=None, features=None):
1543  self.rmf_name = rmf_name
1544  self.rmf_index = rmf_index
1545  self.score = score
1546  self.features = features
1547  self.stat_file = stat_file
1548 
1549  def __repr__(self):
1550  s = "IMP.pmi.output.DataEntry\n"
1551  s += "---- stat file %s \n" % (self.stat_file)
1552  s += "---- rmf file %s \n" % (self.rmf_name)
1553  s += "---- rmf index %s \n" % (str(self.rmf_index))
1554  s += "---- score %s \n" % (str(self.score))
1555  s += "---- number of features %s \n" % (str(len(self.features.keys())))
1556  return s
1557 
1558 
1559 class Cluster:
1560  '''
1561  A container for models organized into clusters
1562  '''
1563  def __init__(self, cid=None):
1564  self.cluster_id = cid
1565  self.members = []
1566  self.precision = None
1567  self.center_index = None
1568  self.members_data = {}
1569 
1570  def add_member(self, index, data=None):
1571  self.members.append(index)
1572  self.members_data[index] = data
1573  self.average_score = self.compute_score()
1574 
1575  def compute_score(self):
1576  try:
1577  score = sum([d.score for d in self])/len(self)
1578  except AttributeError:
1579  score = None
1580  return score
1581 
1582  def __repr__(self):
1583  s = "IMP.pmi.output.Cluster\n"
1584  s += "---- cluster_id %s \n" % str(self.cluster_id)
1585  s += "---- precision %s \n" % str(self.precision)
1586  s += "---- average score %s \n" % str(self.average_score)
1587  s += "---- number of members %s \n" % str(len(self.members))
1588  s += "---- center index %s \n" % str(self.center_index)
1589  return s
1590 
1591  def __getitem__(self, int_slice_adaptor):
1592  if isinstance(int_slice_adaptor, int):
1593  index = self.members[int_slice_adaptor]
1594  return self.members_data[index]
1595  elif isinstance(int_slice_adaptor, slice):
1596  return self.__iter__(int_slice_adaptor)
1597  else:
1598  raise TypeError("Unknown Type")
1599 
1600  def __len__(self):
1601  return len(self.members)
1602 
1603  def __iter__(self, slice_key=None):
1604  if slice_key is None:
1605  for i in range(len(self)):
1606  yield self[i]
1607  else:
1608  for i in range(len(self))[slice_key]:
1609  yield self[i]
1610 
1611  def __add__(self, other):
1612  self.members += other.members
1613  self.members_data.update(other.members_data)
1614  self.average_score = self.compute_score()
1615  self.precision = None
1616  self.center_index = None
1617  return self
1618 
1619 
1620 def plot_clusters_populations(clusters):
1621  indexes = []
1622  populations = []
1623  for cluster in clusters:
1624  indexes.append(cluster.cluster_id)
1625  populations.append(len(cluster))
1626 
1627  import matplotlib.pyplot as plt
1628  fig, ax = plt.subplots()
1629  ax.bar(indexes, populations, 0.5, color='r')
1630  ax.set_ylabel('Population')
1631  ax.set_xlabel(('Cluster index'))
1632  plt.show()
1633 
1634 
1635 def plot_clusters_precisions(clusters):
1636  indexes = []
1637  precisions = []
1638  for cluster in clusters:
1639  indexes.append(cluster.cluster_id)
1640 
1641  prec = cluster.precision
1642  print(cluster.cluster_id, prec)
1643  if prec is None:
1644  prec = 0.0
1645  precisions.append(prec)
1646 
1647  import matplotlib.pyplot as plt
1648  fig, ax = plt.subplots()
1649  ax.bar(indexes, precisions, 0.5, color='r')
1650  ax.set_ylabel('Precision [A]')
1651  ax.set_xlabel(('Cluster index'))
1652  plt.show()
1653 
1654 
1655 def plot_clusters_scores(clusters):
1656  indexes = []
1657  values = []
1658  for cluster in clusters:
1659  indexes.append(cluster.cluster_id)
1660  values.append([])
1661  for data in cluster:
1662  values[-1].append(data.score)
1663 
1664  plot_fields_box_plots("scores.pdf", values, indexes, frequencies=None,
1665  valuename="Scores", positionname="Cluster index",
1666  xlabels=None, scale_plot_length=1.0)
1667 
1668 
1669 class CrossLinkIdentifierDatabase:
1670  def __init__(self):
1671  self.clidb = dict()
1672 
1673  def check_key(self, key):
1674  if key not in self.clidb:
1675  self.clidb[key] = {}
1676 
1677  def set_unique_id(self, key, value):
1678  self.check_key(key)
1679  self.clidb[key]["XLUniqueID"] = str(value)
1680 
1681  def set_protein1(self, key, value):
1682  self.check_key(key)
1683  self.clidb[key]["Protein1"] = str(value)
1684 
1685  def set_protein2(self, key, value):
1686  self.check_key(key)
1687  self.clidb[key]["Protein2"] = str(value)
1688 
1689  def set_residue1(self, key, value):
1690  self.check_key(key)
1691  self.clidb[key]["Residue1"] = int(value)
1692 
1693  def set_residue2(self, key, value):
1694  self.check_key(key)
1695  self.clidb[key]["Residue2"] = int(value)
1696 
1697  def set_idscore(self, key, value):
1698  self.check_key(key)
1699  self.clidb[key]["IDScore"] = float(value)
1700 
1701  def set_state(self, key, value):
1702  self.check_key(key)
1703  self.clidb[key]["State"] = int(value)
1704 
1705  def set_sigma1(self, key, value):
1706  self.check_key(key)
1707  self.clidb[key]["Sigma1"] = str(value)
1708 
1709  def set_sigma2(self, key, value):
1710  self.check_key(key)
1711  self.clidb[key]["Sigma2"] = str(value)
1712 
1713  def set_psi(self, key, value):
1714  self.check_key(key)
1715  self.clidb[key]["Psi"] = str(value)
1716 
1717  def get_unique_id(self, key):
1718  return self.clidb[key]["XLUniqueID"]
1719 
1720  def get_protein1(self, key):
1721  return self.clidb[key]["Protein1"]
1722 
1723  def get_protein2(self, key):
1724  return self.clidb[key]["Protein2"]
1725 
1726  def get_residue1(self, key):
1727  return self.clidb[key]["Residue1"]
1728 
1729  def get_residue2(self, key):
1730  return self.clidb[key]["Residue2"]
1731 
1732  def get_idscore(self, key):
1733  return self.clidb[key]["IDScore"]
1734 
1735  def get_state(self, key):
1736  return self.clidb[key]["State"]
1737 
1738  def get_sigma1(self, key):
1739  return self.clidb[key]["Sigma1"]
1740 
1741  def get_sigma2(self, key):
1742  return self.clidb[key]["Sigma2"]
1743 
1744  def get_psi(self, key):
1745  return self.clidb[key]["Psi"]
1746 
1747  def set_float_feature(self, key, value, feature_name):
1748  self.check_key(key)
1749  self.clidb[key][feature_name] = float(value)
1750 
1751  def set_int_feature(self, key, value, feature_name):
1752  self.check_key(key)
1753  self.clidb[key][feature_name] = int(value)
1754 
1755  def set_string_feature(self, key, value, feature_name):
1756  self.check_key(key)
1757  self.clidb[key][feature_name] = str(value)
1758 
1759  def get_feature(self, key, feature_name):
1760  return self.clidb[key][feature_name]
1761 
1762  def write(self, filename):
1763  with open(filename, 'wb') as handle:
1764  pickle.dump(self.clidb, handle)
1765 
1766  def load(self, filename):
1767  with open(filename, 'rb') as handle:
1768  self.clidb = pickle.load(handle)
1769 
1770 
1771 def plot_fields(fields, output, framemin=None, framemax=None):
1772  """Plot the given fields and save a figure as `output`.
1773  The fields generally are extracted from a stat file
1774  using ProcessOutput.get_fields()."""
1775  import matplotlib as mpl
1776  mpl.use('Agg')
1777  import matplotlib.pyplot as plt
1778 
1779  plt.rc('lines', linewidth=4)
1780  fig, axs = plt.subplots(nrows=len(fields))
1781  fig.set_size_inches(10.5, 5.5 * len(fields))
1782  plt.rc('axes')
1783 
1784  n = 0
1785  for key in fields:
1786  if framemin is None:
1787  framemin = 0
1788  if framemax is None:
1789  framemax = len(fields[key])
1790  x = list(range(framemin, framemax))
1791  y = [float(y) for y in fields[key][framemin:framemax]]
1792  if len(fields) > 1:
1793  axs[n].plot(x, y)
1794  axs[n].set_title(key, size="xx-large")
1795  axs[n].tick_params(labelsize=18, pad=10)
1796  else:
1797  axs.plot(x, y)
1798  axs.set_title(key, size="xx-large")
1799  axs.tick_params(labelsize=18, pad=10)
1800  n += 1
1801 
1802  # Tweak spacing between subplots to prevent labels from overlapping
1803  plt.subplots_adjust(hspace=0.3)
1804  plt.savefig(output)
1805 
1806 
1807 def plot_field_histogram(name, values_lists, valuename=None, bins=40,
1808  colors=None, format="png", reference_xline=None,
1809  yplotrange=None, xplotrange=None, normalized=True,
1810  leg_names=None):
1811  '''Plot a list of histograms from a value list.
1812  @param name the name of the plot
1813  @param values_lists the list of list of values eg: [[...],[...],[...]]
1814  @param valuename the y-label
1815  @param bins the number of bins
1816  @param colors If None, will use rainbow. Else will use specific list
1817  @param format output format
1818  @param reference_xline plot a reference line parallel to the y-axis
1819  @param yplotrange the range for the y-axis
1820  @param normalized whether the histogram is normalized or not
1821  @param leg_names names for the legend
1822  '''
1823 
1824  import matplotlib as mpl
1825  mpl.use('Agg')
1826  import matplotlib.pyplot as plt
1827  import matplotlib.cm as cm
1828  plt.figure(figsize=(18.0, 9.0))
1829 
1830  if colors is None:
1831  colors = cm.rainbow(np.linspace(0, 1, len(values_lists)))
1832  for nv, values in enumerate(values_lists):
1833  col = colors[nv]
1834  if leg_names is not None:
1835  label = leg_names[nv]
1836  else:
1837  label = str(nv)
1838  try:
1839  plt.hist(
1840  [float(y) for y in values], bins=bins, color=col,
1841  density=normalized, histtype='step', lw=4, label=label)
1842  except AttributeError:
1843  plt.hist(
1844  [float(y) for y in values], bins=bins, color=col,
1845  normed=normalized, histtype='step', lw=4, label=label)
1846 
1847  # plt.title(name,size="xx-large")
1848  plt.tick_params(labelsize=12, pad=10)
1849  if valuename is None:
1850  plt.xlabel(name, size="xx-large")
1851  else:
1852  plt.xlabel(valuename, size="xx-large")
1853  plt.ylabel("Frequency", size="xx-large")
1854 
1855  if yplotrange is not None:
1856  plt.ylim()
1857  if xplotrange is not None:
1858  plt.xlim(xplotrange)
1859 
1860  plt.legend(loc=2)
1861 
1862  if reference_xline is not None:
1863  plt.axvline(
1864  reference_xline,
1865  color='red',
1866  linestyle='dashed',
1867  linewidth=1)
1868 
1869  plt.savefig(name + "." + format, dpi=150, transparent=True)
1870 
1871 
1872 def plot_fields_box_plots(name, values, positions, frequencies=None,
1873  valuename="None", positionname="None",
1874  xlabels=None, scale_plot_length=1.0):
1875  '''
1876  Plot time series as boxplots.
1877  fields is a list of time series, positions are the x-values
1878  valuename is the y-label, positionname is the x-label
1879  '''
1880 
1881  import matplotlib as mpl
1882  mpl.use('Agg')
1883  import matplotlib.pyplot as plt
1884 
1885  bps = []
1886  fig = plt.figure(figsize=(float(len(positions))*scale_plot_length, 5.0))
1887  fig.canvas.manager.set_window_title(name)
1888 
1889  ax1 = fig.add_subplot(111)
1890 
1891  plt.subplots_adjust(left=0.1, right=0.990, top=0.95, bottom=0.4)
1892 
1893  bps.append(plt.boxplot(values, notch=0, sym='', vert=1,
1894  whis=1.5, positions=positions))
1895 
1896  plt.setp(bps[-1]['boxes'], color='black', lw=1.5)
1897  plt.setp(bps[-1]['whiskers'], color='black', ls=":", lw=1.5)
1898 
1899  if frequencies is not None:
1900  for n, v in enumerate(values):
1901  plist = [positions[n]]*len(v)
1902  ax1.plot(plist, v, 'gx', alpha=0.7, markersize=7)
1903 
1904  # print ax1.xaxis.get_majorticklocs()
1905  if xlabels is not None:
1906  ax1.set_xticklabels(xlabels)
1907  plt.xticks(rotation=90)
1908  plt.xlabel(positionname)
1909  plt.ylabel(valuename)
1910 
1911  plt.savefig(name + ".pdf", dpi=150)
1912  plt.show()
1913 
1914 
1915 def plot_xy_data(x, y, title=None, out_fn=None, display=True,
1916  set_plot_yaxis_range=None, xlabel=None, ylabel=None):
1917  import matplotlib as mpl
1918  mpl.use('Agg')
1919  import matplotlib.pyplot as plt
1920  plt.rc('lines', linewidth=2)
1921 
1922  fig, ax = plt.subplots(nrows=1)
1923  fig.set_size_inches(8, 4.5)
1924  if title is not None:
1925  fig.canvas.manager.set_window_title(title)
1926 
1927  ax.plot(x, y, color='r')
1928  if set_plot_yaxis_range is not None:
1929  x1, x2, y1, y2 = plt.axis()
1930  y1 = set_plot_yaxis_range[0]
1931  y2 = set_plot_yaxis_range[1]
1932  plt.axis((x1, x2, y1, y2))
1933  if title is not None:
1934  ax.set_title(title)
1935  if xlabel is not None:
1936  ax.set_xlabel(xlabel)
1937  if ylabel is not None:
1938  ax.set_ylabel(ylabel)
1939  if out_fn is not None:
1940  plt.savefig(out_fn + ".pdf")
1941  if display:
1942  plt.show()
1943  plt.close(fig)
1944 
1945 
1946 def plot_scatter_xy_data(x, y, labelx="None", labely="None",
1947  xmin=None, xmax=None, ymin=None, ymax=None,
1948  savefile=False, filename="None.eps", alpha=0.75):
1949 
1950  import matplotlib as mpl
1951  mpl.use('Agg')
1952  import matplotlib.pyplot as plt
1953  from matplotlib import rc
1954  rc('font', **{'family': 'sans-serif', 'sans-serif': ['Helvetica']})
1955 
1956  fig, axs = plt.subplots(1)
1957 
1958  axs0 = axs
1959 
1960  axs0.set_xlabel(labelx, size="xx-large")
1961  axs0.set_ylabel(labely, size="xx-large")
1962  axs0.tick_params(labelsize=18, pad=10)
1963 
1964  plot2 = []
1965 
1966  plot2.append(axs0.plot(x, y, 'o', color='k', lw=2, ms=0.1, alpha=alpha,
1967  c="w"))
1968 
1969  axs0.legend(
1970  loc=0,
1971  frameon=False,
1972  scatterpoints=1,
1973  numpoints=1,
1974  columnspacing=1)
1975 
1976  fig.set_size_inches(8.0, 8.0)
1977  fig.subplots_adjust(left=0.161, right=0.850, top=0.95, bottom=0.11)
1978  if (ymin is not None) and (ymax is not None):
1979  axs0.set_ylim(ymin, ymax)
1980  if (xmin is not None) and (xmax is not None):
1981  axs0.set_xlim(xmin, xmax)
1982 
1983  if savefile:
1984  fig.savefig(filename, dpi=300)
1985 
1986 
1987 def get_graph_from_hierarchy(hier):
1988  graph = []
1989  depth_dict = {}
1990  depth = 0
1991  (graph, depth, depth_dict) = recursive_graph(
1992  hier, graph, depth, depth_dict)
1993 
1994  # filters node labels according to depth_dict
1995  node_labels_dict = {}
1996  for key in depth_dict:
1997  if depth_dict[key] < 3:
1998  node_labels_dict[key] = key
1999  else:
2000  node_labels_dict[key] = ""
2001  draw_graph(graph, labels_dict=node_labels_dict)
2002 
2003 
2004 def recursive_graph(hier, graph, depth, depth_dict):
2005  depth = depth + 1
2006  nameh = IMP.atom.Hierarchy(hier).get_name()
2007  index = str(hier.get_particle().get_index())
2008  name1 = nameh + "|#" + index
2009  depth_dict[name1] = depth
2010 
2011  children = IMP.atom.Hierarchy(hier).get_children()
2012 
2013  if len(children) == 1 or children is None:
2014  depth = depth - 1
2015  return (graph, depth, depth_dict)
2016 
2017  else:
2018  for c in children:
2019  (graph, depth, depth_dict) = recursive_graph(
2020  c, graph, depth, depth_dict)
2021  nameh = IMP.atom.Hierarchy(c).get_name()
2022  index = str(c.get_particle().get_index())
2023  namec = nameh + "|#" + index
2024  graph.append((name1, namec))
2025 
2026  depth = depth - 1
2027  return (graph, depth, depth_dict)
2028 
2029 
2030 def draw_graph(graph, labels_dict=None, graph_layout='spring',
2031  node_size=5, node_color=None, node_alpha=0.3,
2032  node_text_size=11, fixed=None, pos=None,
2033  edge_color='blue', edge_alpha=0.3, edge_thickness=1,
2034  edge_text_pos=0.3,
2035  validation_edges=None,
2036  text_font='sans-serif',
2037  out_filename=None):
2038 
2039  import matplotlib as mpl
2040  mpl.use('Agg')
2041  import networkx as nx
2042  import matplotlib.pyplot as plt
2043  from math import sqrt, pi
2044 
2045  # create networkx graph
2046  G = nx.Graph()
2047 
2048  # add edges
2049  if isinstance(edge_thickness, list):
2050  for edge, weight in zip(graph, edge_thickness):
2051  G.add_edge(edge[0], edge[1], weight=weight)
2052  else:
2053  for edge in graph:
2054  G.add_edge(edge[0], edge[1])
2055 
2056  if node_color is None:
2057  node_color_rgb = (0, 0, 0)
2058  node_color_hex = "000000"
2059  else:
2061  tmpcolor_rgb = []
2062  tmpcolor_hex = []
2063  for node in G.nodes():
2064  cctuple = cc.rgb(node_color[node])
2065  tmpcolor_rgb.append((cctuple[0]/255,
2066  cctuple[1]/255,
2067  cctuple[2]/255))
2068  tmpcolor_hex.append(node_color[node])
2069  node_color_rgb = tmpcolor_rgb
2070  node_color_hex = tmpcolor_hex
2071 
2072  # get node sizes if dictionary
2073  if isinstance(node_size, dict):
2074  tmpsize = []
2075  for node in G.nodes():
2076  size = sqrt(node_size[node])/pi*10.0
2077  tmpsize.append(size)
2078  node_size = tmpsize
2079 
2080  for n, node in enumerate(G.nodes()):
2081  color = node_color_hex[n]
2082  size = node_size[n]
2083  nx.set_node_attributes(
2084  G, "graphics",
2085  {node: {'type': 'ellipse', 'w': size, 'h': size,
2086  'fill': '#' + color, 'label': node}})
2087  nx.set_node_attributes(
2088  G, "LabelGraphics",
2089  {node: {'type': 'text', 'text': node, 'color': '#000000',
2090  'visible': 'true'}})
2091 
2092  for edge in G.edges():
2093  nx.set_edge_attributes(
2094  G, "graphics",
2095  {edge: {'width': 1, 'fill': '#000000'}})
2096 
2097  for ve in validation_edges:
2098  print(ve)
2099  if (ve[0], ve[1]) in G.edges():
2100  print("found forward")
2101  nx.set_edge_attributes(
2102  G, "graphics",
2103  {ve: {'width': 1, 'fill': '#00FF00'}})
2104  elif (ve[1], ve[0]) in G.edges():
2105  print("found backward")
2106  nx.set_edge_attributes(
2107  G, "graphics",
2108  {(ve[1], ve[0]): {'width': 1, 'fill': '#00FF00'}})
2109  else:
2110  G.add_edge(ve[0], ve[1])
2111  print("not found")
2112  nx.set_edge_attributes(
2113  G, "graphics",
2114  {ve: {'width': 1, 'fill': '#FF0000'}})
2115 
2116  # these are different layouts for the network you may try
2117  # shell seems to work best
2118  if graph_layout == 'spring':
2119  print(fixed, pos)
2120  graph_pos = nx.spring_layout(G, k=1.0/8.0, fixed=fixed, pos=pos)
2121  elif graph_layout == 'spectral':
2122  graph_pos = nx.spectral_layout(G)
2123  elif graph_layout == 'random':
2124  graph_pos = nx.random_layout(G)
2125  else:
2126  graph_pos = nx.shell_layout(G)
2127 
2128  # draw graph
2129  nx.draw_networkx_nodes(G, graph_pos, node_size=node_size,
2130  alpha=node_alpha, node_color=node_color_rgb,
2131  linewidths=0)
2132  nx.draw_networkx_edges(G, graph_pos, width=edge_thickness,
2133  alpha=edge_alpha, edge_color=edge_color)
2134  nx.draw_networkx_labels(
2135  G, graph_pos, labels=labels_dict, font_size=node_text_size,
2136  font_family=text_font)
2137  if out_filename:
2138  plt.savefig(out_filename)
2139  nx.write_gml(G, 'out.gml')
2140  plt.show()
2141 
2142 
2143 def draw_table():
2144 
2145  # still an example!
2146 
2147  from ipyD3 import d3object
2148  from IPython.display import display
2149 
2150  d3 = d3object(width=800,
2151  height=400,
2152  style='JFTable',
2153  number=1,
2154  d3=None,
2155  title='Example table with d3js',
2156  desc='An example table created created with d3js with '
2157  'data generated with Python.')
2158  data = [[1277.0, 654.0, 288.0, 1976.0, 3281.0, 3089.0, 10336.0, 4650.0,
2159  4441.0, 4670.0, 944.0, 110.0],
2160  [1318.0, 664.0, 418.0, 1952.0, 3581.0, 4574.0, 11457.0, 6139.0,
2161  7078.0, 6561.0, 2354.0, 710.0],
2162  [1783.0, 774.0, 564.0, 1470.0, 3571.0, 3103.0, 9392.0, 5532.0,
2163  5661.0, 4991.0, 2032.0, 680.0],
2164  [1301.0, 604.0, 286.0, 2152.0, 3282.0, 3369.0, 10490.0, 5406.0,
2165  4727.0, 3428.0, 1559.0, 620.0],
2166  [1537.0, 1714.0, 724.0, 4824.0, 5551.0, 8096.0, 16589.0, 13650.0,
2167  9552.0, 13709.0, 2460.0, 720.0],
2168  [5691.0, 2995.0, 1680.0, 11741.0, 16232.0, 14731.0, 43522.0,
2169  32794.0, 26634.0, 31400.0, 7350.0, 3010.0],
2170  [1650.0, 2096.0, 60.0, 50.0, 1180.0, 5602.0, 15728.0, 6874.0,
2171  5115.0, 3510.0, 1390.0, 170.0],
2172  [72.0, 60.0, 60.0, 10.0, 120.0, 172.0, 1092.0, 675.0, 408.0,
2173  360.0, 156.0, 100.0]]
2174  data = [list(i) for i in zip(*data)]
2175  sRows = [['January',
2176  'February',
2177  'March',
2178  'April',
2179  'May',
2180  'June',
2181  'July',
2182  'August',
2183  'September',
2184  'October',
2185  'November',
2186  'Deecember']]
2187  sColumns = [['Prod {0}'.format(i) for i in range(1, 9)],
2188  [None, '', None, None, 'Group 1', None, None, 'Group 2']]
2189  d3.addSimpleTable(data,
2190  fontSizeCells=[12, ],
2191  sRows=sRows,
2192  sColumns=sColumns,
2193  sRowsMargins=[5, 50, 0],
2194  sColsMargins=[5, 20, 10],
2195  spacing=0,
2196  addBorders=1,
2197  addOutsideBorders=-1,
2198  rectWidth=45,
2199  rectHeight=0
2200  )
2201  html = d3.render(mode=['html', 'show'])
2202  display(html)
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: Residue.h:158
A container for models organized into clusters.
Definition: output.py:1559
A class for reading stat files (either rmf or ascii v1 and v2)
Definition: output.py:985
atom::Hierarchies create_hierarchies(RMF::FileConstHandle fh, Model *m)
RMF::FrameID save_frame(RMF::FileHandle file, std::string name="")
Save the current state of the linked objects as a new RMF frame.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: atom/Atom.h:245
def plot_field_histogram
Plot a list of histograms from a value list.
Definition: output.py:1807
def plot_fields_box_plots
Plot time series as boxplots.
Definition: output.py:1872
Utility classes and functions for reading and storing PMI files.
def get_best_models
Given a list of stat files, read them all and find the best models.
Miscellaneous utilities.
Definition: pmi/tools.py:1
A class to store data associated to a model.
Definition: output.py:1537
void handle_use_deprecated(std::string message)
Break in this method in gdb to find deprecated uses at runtime.
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:736
void write_pdb(const Selection &mhd, TextOutput out, unsigned int model=1)
Collect statistics from ProcessOutput.get_fields().
Definition: output.py:974
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: XYZR.h:47
def get_fields
Get the desired field names, and return a dictionary.
Definition: output.py:1050
Warning related to handling of structures.
static bool get_is_setup(Model *m, ParticleIndex pi)
Definition: Fragment.h:46
def link_to_rmf
Link to another RMF file.
Definition: output.py:1209
std::string get_molecule_name_and_copy(atom::Hierarchy h)
Walk up a PMI2 hierarchy/representations and get the "molname.copynum".
Definition: pmi/utilities.h:85
The standard decorator for manipulating molecular structures.
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
def init_pdb
Init PDB Writing.
Definition: output.py:257
int get_number_of_frames(const ::npctransport_proto::Assignment &config, double time_step)
A decorator for a particle representing an atom.
Definition: atom/Atom.h:238
Base class for capturing a modeling protocol.
Definition: output.py:41
The type for a residue.
def write_stat2
Write a single line to a stat file previously created with init_stat2().
Definition: output.py:929
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
A base class for Keys.
Definition: Key.h:45
void add_hierarchies(RMF::NodeHandle fh, const atom::Hierarchies &hs)
Class for easy writing of PDBs, RMFs, and stat files.
Definition: output.py:199
void add_geometries(RMF::NodeHandle parent, const display::GeometriesTemp &r)
Add geometries to a given parent node.
void add_restraints(RMF::NodeHandle fh, const Restraints &hs)
A decorator for a particle that is part of a rigid body but not rigid.
Definition: rigid_bodies.h:659
Display a segment connecting a pair of particles.
Definition: XYZR.h:170
A decorator for a residue.
Definition: Residue.h:137
Basic functionality that is expected to be used by a wide variety of IMP users.
def get_pdb_names
Get a list of all PDB files being output by this instance.
Definition: output.py:226
def get_prot_name_from_particle
Get the protein name from the particle.
Definition: output.py:343
class to link stat files to several rmf files
Definition: output.py:1307
class to allow more advanced handling of RMF files.
Definition: output.py:1183
void link_hierarchies(RMF::FileConstHandle fh, const atom::Hierarchies &hs)
def plot_fields
Plot the given fields and save a figure as output.
Definition: output.py:1771
void add_geometry(RMF::FileHandle file, display::Geometry *r)
Add a single geometry to the file.
Store info for a chain of a protein.
Definition: Chain.h:61
Python classes to represent, score, sample and analyze models.
def init_pdb_best_scoring
Prepare for writing best-scoring PDBs (or mmCIFs) for a sampling run.
Definition: output.py:464
Functionality for loading, creating, manipulating and scoring atomic structures.
def get_rbs_and_beads
Returns unique objects in original order.
Definition: pmi/tools.py:1135
Select hierarchy particles identified by the biological name.
Definition: Selection.h:70
def init_rmf
Initialize an RMF file.
Definition: output.py:561
def get_residue_indexes
Retrieve the residue indexes for the given particle.
Definition: pmi/tools.py:499
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: rigid_bodies.h:661
def init_stat2
Write the header for a stat file in v2 format.
Definition: output.py:799
std::string get_module_version()
Return the version of this module, as a string.
def sublist_iterator
Yield all sublists of length >= lmin and <= lmax.
Definition: pmi/tools.py:574
A decorator for a particle with x,y,z coordinates and a radius.
Definition: XYZR.h:27