IMP logo
IMP Reference Guide  develop.266d43d110,2026/09/24
The Integrative Modeling Platform
pmi/topology/__init__.py
1 """@namespace IMP.pmi.topology
2  Set of Python classes to create a multi-state, multi-resolution IMP hierarchy.
3 * Start by creating a System with
4  `model = IMP.Model(); s = IMP.pmi.topology.System(model)`. The System
5  will store all the states.
6 * Then call System.create_state(). You can easily create a multistate system
7  by calling this function multiple times.
8 * For each State, call State.create_molecule() to add a Molecule (a uniquely
9  named polymer). This function returns the Molecule object which can be
10  passed to various PMI functions.
11 * Some useful functions to help you set up your Molecules:
12  * Access the sequence residues with slicing (Molecule[a:b]) or functions
13  like Molecule.get_atomic_residues() and Molecule.get_non_atomic_residues().
14  These functions all return Python sets for easy set arithmetic using
15  & (and), | (or), - (difference)
16  * Molecule.add_structure() to add structural information from an mmCIF,
17  BinaryCIF, or PDB file.
18  * Molecule.add_representation() to create a representation unit - here you
19  can choose bead resolutions as well as alternate representations like
20  densities or ideal helices.
21  * Molecule.create_clone() lets you set up a molecule with identical
22  representations, just a different chain ID. Use Molecule.create_copy()
23  if you want a molecule with the same sequence but that allows custom
24  representations.
25 * Once data has been added and representations chosen, call System.build()
26  to create a canonical IMP hierarchy.
27 * Following hierarchy construction, setup rigid bodies, flexible beads, etc
28  in IMP::pmi::dof.
29 * Check your representation with a nice printout:
30  IMP::atom::show_with_representation()
31 
32 See a [comprehensive example](https://integrativemodeling.org/nightly/doc/ref/pmi_2multiscale_8py-example.html) for using these classes.
33 
34 Alternatively one can construct the entire topology and degrees of freedom
35 via formatted text file with TopologyReader and
36 IMP::pmi::macros::BuildSystem(). This is used in the
37 [PMI tutorial](@ref rnapolii_stalk). Note that this only allows a limited
38 set of the full options available to PMI users
39 (rigid bodies only, fixed resolutions).
40 """ # noqa: E501
41 
42 import IMP
43 import IMP.atom
44 import IMP.algebra
45 import IMP.pmi
46 import IMP.pmi.tools
47 import IMP.pmi.alphabets
48 import os
49 import re
50 from collections import defaultdict, namedtuple
51 from . import system_tools
52 from bisect import bisect_left
53 from math import pi, cos, sin
54 from operator import itemgetter
55 import weakref
56 import warnings
57 
58 
59 def _build_ideal_helix(model, residues, coord_finder):
60  """Creates an ideal helix from the specified residue range
61  Residues MUST be contiguous.
62  This function actually adds them to the TempResidue hierarchy
63  """
64  created_hiers = []
65 
66  # this function creates a CAlpha helix structure (which can be used
67  # for coarsening)
68  prev_idx = -9999
69  for n, tempres in enumerate(residues):
70  if tempres.get_has_structure():
71  raise ValueError("You tried to build ideal_helix for a residue "
72  "that already has structure: %s" % tempres)
73  if n > 0 and tempres.get_index() != prev_idx + 1:
74  raise ValueError(
75  "Passed non-contiguous segment to "
76  "build_ideal_helix for %s" % tempres.get_molecule())
77 
78  # New residue particle will replace the TempResidue's existing
79  # (empty) hierarchy
80  rp = IMP.Particle(model)
81  rp.set_name("Residue_%i" % tempres.get_index())
82 
83  # Copy the original residue type and index
84  this_res = IMP.atom.Residue.setup_particle(rp, tempres.get_hierarchy())
85 
86  # Create the CAlpha
87  ap = IMP.Particle(model)
89  x = 2.3 * cos(n * 2 * pi / 3.6)
90  y = 2.3 * sin(n * 2 * pi / 3.6)
91  z = 6.2 / 3.6 / 2 * n * 2 * pi / 3.6
92  d.set_coordinates(IMP.algebra.Vector3D(x, y, z))
93  d.set_radius(1.0)
94  # Decorating as Atom also decorates as Mass
95  a = IMP.atom.Atom.setup_particle(ap, IMP.atom.AT_CA)
96  IMP.atom.Mass(ap).set_mass(110.0)
97  this_res.add_child(a)
98 
99  # Add this structure to the TempResidue
100  tempres.set_structure(this_res)
101  created_hiers.append(this_res)
102  prev_idx = tempres.get_index()
103  # the coord finder is for placing beads (later)
104  coord_finder.add_residues(created_hiers)
105 
106 
107 class _SystemBase:
108  """The base class for System, State and Molecule
109  classes. It contains shared functions in common to these classes
110  """
111 
112  def __init__(self, model=None):
113  if model is None:
114  self.model = IMP.Model()
115  else:
116  self.model = model
117 
118  def _create_hierarchy(self):
119  """create a new hierarchy"""
120  tmp_part = IMP.Particle(self.model)
121  return IMP.atom.Hierarchy.setup_particle(tmp_part)
122 
123  def _create_child(self, parent_hierarchy):
124  """create a new hierarchy, set it as child of the input
125  one, and return it"""
126  child_hierarchy = self._create_hierarchy()
127  parent_hierarchy.add_child(child_hierarchy)
128  return child_hierarchy
129 
130  def build(self):
131  """Build the coordinates of the system.
132  Loop through stored(?) hierarchies and set up coordinates!"""
133  pass
134 
135 
136 class _OurWeakRef:
137  """A simple wrapper around weakref.ref which can be pickled.
138  Note that we throw the reference away at pickle time. It should
139  be able to be reconstructed from System._all_systems anyway."""
140 
141  def __init__(self, system):
142  self._ref = weakref.ref(system)
143 
144  def __call__(self):
145  if hasattr(self, '_ref'):
146  return self._ref()
147 
148  def __getstate__(self):
149  return None
150 
151 
152 class System(_SystemBase):
153  """Represent the root node of the global IMP.atom.Hierarchy."""
154 
155  _all_systems = weakref.WeakSet()
156 
157  def __init__(self, model=None, name="System"):
158  """Constructor.
159 
160  @param model The IMP::Model in which to construct this system.
161  @param name The name of the top-level hierarchy node.
162  """
163  super().__init__(model)
164  # A second Model used for temporary storage of starting models
165  self._start_model = IMP.Model()
166  self._number_of_states = 0
167  self._protocol_output = []
168  self.states = []
169  self.built = False
170 
171  System._all_systems.add(self)
172 
173  # the root hierarchy node
174  self.hier = self._create_hierarchy()
175  self.hier.set_name(name)
176  self.hier._pmi2_system = _OurWeakRef(self)
177 
178  def get_states(self):
179  """Get a list of all State objects in this system"""
180  return self.states
181 
182  def create_state(self):
183  """Makes and returns a new IMP.pmi.topology.State in this system"""
184  self._number_of_states += 1
185  state = State(self, self._number_of_states-1)
186  self.states.append(state)
187  return state
188 
189  def __repr__(self):
190  return self.hier.get_name()
191 
193  """Returns the total number of states generated"""
194  return self._number_of_states
195 
196  def get_hierarchy(self):
197  """Return the top-level IMP.atom.Hierarchy node for this system"""
198  return self.hier
199 
200  def build(self, **kwargs):
201  """Build all states"""
202  if not self.built:
203  for state in self.states:
204  state.build(**kwargs)
205  self.built = True
206  for po in self._protocol_output:
207  po.finalize_build()
208  # Free memory used by starting models
209  del self._start_model
210  return self.hier
211 
212  def add_protocol_output(self, p):
213  """Capture details of the modeling protocol.
214  @param p an instance of IMP.pmi.output.ProtocolOutput or a subclass.
215  """
216  self._protocol_output.append(p)
217 # p._each_metadata.append(self._metadata)
218 # p._file_datasets.append(self._file_dataset)
219  for state in self.states:
220  state._add_protocol_output(p, self)
221 
222 
223 class State(_SystemBase):
224  """Stores a list of Molecules all with the same State index.
225  Also stores number of copies of each Molecule for easy selection.
226  """
227  def __init__(self, system, state_index):
228  """Define a new state
229  @param system the PMI System
230  @param state_index the index of the new state
231  @note It's expected that you will not use this constructor directly,
232  but rather create it with System.create_state()
233  """
234  self.model = system.get_hierarchy().get_model()
235  self.system = system
236  self.hier = self._create_child(system.get_hierarchy())
237  self.short_name = self.long_name = "State_" + str(state_index)
238  self.hier.set_name(self.short_name)
239  # key is molecule name. value are the molecule copies!
240  self.molecules = IMP.pmi.tools.OrderedDict()
241  IMP.atom.State.setup_particle(self.hier, state_index)
242  self.built = False
243  self._protocol_output = []
244  for p in system._protocol_output:
245  self._add_protocol_output(p, system)
246 
247  _start_model = property(lambda self: self.system._start_model)
248 
249  def __repr__(self):
250  return self.system.__repr__()+'.'+self.hier.get_name()
251 
252  def _add_protocol_output(self, p, system):
253  state = p._add_state(self)
254  self._protocol_output.append((p, state))
255  state.model = system.model
256  state.prot = self.hier
257 
258  def get_molecules(self):
259  """Return a dictionary where key is molecule name and value
260  is a list of all copies of that molecule in setup order"""
261  return self.molecules
262 
263  def get_molecule(self, name, copy_num=0):
264  """Access a molecule by name and copy number
265  @param name The molecule name used during setup
266  @param copy_num The copy number based on input order.
267  Default: 0. Set to 'all' to get all copies
268  """
269  if name not in self.molecules:
270  raise KeyError("Could not find molname %s" % name)
271  if copy_num == 'all':
272  return self.molecules[name]
273  else:
274  return self.molecules[name][copy_num]
275 
276  def create_molecule(self, name, sequence='', chain_id='',
277  alphabet=IMP.pmi.alphabets.amino_acid,
278  uniprot=None):
279  """Create a new Molecule within this State
280  @param name the name of the molecule (string);
281  it must not be already used
282  @param sequence sequence (string)
283  @param chain_id Chain ID to assign to this molecule
284  @param alphabet Mapping from FASTA codes to residue types
285  @param uniprot UniProt accession, if available
286  """
287  # check whether the molecule name is already assigned
288  if name in self.molecules:
289  raise ValueError('Cannot use a molecule name already used')
290 
291  # check for something that looks like a copy number
292  if re.search(r'\.\d+$', name):
293  warnings.warn(
294  "It is recommended not to end the molecule name with "
295  ".(number) as it may be confused with the copy number "
296  "(the copy number for new molecules is always 0, so to "
297  "select this molecule, use '%s.0'). Use create_clone() or "
298  "create_copy() instead if a copy of an existing molecule "
299  "is desired." % name, IMP.pmi.ParameterWarning)
300 
301  mol = Molecule(self, name, sequence, chain_id, copy_num=0,
302  alphabet=alphabet, uniprot=uniprot)
303  self.molecules[name] = [mol]
304  return mol
305 
306  def get_hierarchy(self):
307  """Get the IMP.atom.Hierarchy node for this state"""
308  return self.hier
309 
310  def get_number_of_copies(self, molname):
311  """Get the number of copies of the given molecule (by name)
312 
313  @param molname The name of the molecule
314  """
315  return len(self.molecules[molname])
316 
317  def _register_copy(self, molecule):
318  molname = molecule.get_hierarchy().get_name()
319  self.molecules[molname].append(molecule)
320 
321  def build(self, **kwargs):
322  """Build all molecules (automatically makes clones)"""
323  if not self.built:
324  for molname in self.molecules:
325  # We want to update ProtocolOutput in forward order so
326  # that, e.g. we get nice chain IDs in the mmCIF output,
327  # but we want to build the sequence in reverse order
328  for mol in self.molecules[molname]:
329  mol._build_protocol_output()
330  for mol in reversed(self.molecules[molname]):
331  mol.build(protocol_output=False, **kwargs)
332  for mol in self.molecules[molname]:
333  mol._finalize_build()
334  self.built = True
335  return self.hier
336 
337 
338 # Track residues read from PDB files
339 _PDBElement = namedtuple('PDBElement', ['offset', 'filename', 'chain_id'])
340 
341 
342 class _RepresentationHandler:
343  """Handle PMI representation and use it to populate that of any attached
344  ProtocolOutput objects"""
345  def __init__(self, name, pos, pdb_elements):
346  self.name = name
347  self.pos = pos
348  self.last_index = None
349  self.last_pdb_index = None
350  self.pdb_for_residue = {}
351  for residues, pdb in pdb_elements:
352  for r in residues:
353  self.pdb_for_residue[r.get_index()] = pdb
354 
355  def _get_pdb(self, h):
356  """Return a PDBElement if the given hierarchy was read from a
357  PDB file"""
359  rind = IMP.atom.Residue(h).get_index()
360  return self.pdb_for_residue.get(rind, None)
361 
362  def __call__(self, res):
363  """Handle a single residue"""
364  if len(self.pos) == 0:
365  return
366  h = res.hier
367  pi = h.get_particle_index()
368  # Do nothing if we already saw this hierarchy
369  if self.last_index is None or pi != self.last_index:
370  pdb = self._get_pdb(h)
371  self.last_index = pi
372  if pdb:
373  assert IMP.atom.Fragment.get_is_setup(h.get_parent())
374  frag = IMP.atom.Fragment(h.get_parent())
375  fragi = frag.get_particle_index()
376  # Do nothing if we already saw this PDB fragment
377  if self.last_pdb_index is not None \
378  and self.last_pdb_index == fragi:
379  return
380  self.last_pdb_index = fragi
381  indices = frag.get_residue_indexes()
382  for p, state in self.pos:
383  p.add_pdb_element(state, self.name,
384  indices[0], indices[-1], pdb.offset,
385  pdb.filename, pdb.chain_id, frag)
387  frag = IMP.atom.Fragment(h)
388  indices = frag.get_residue_indexes()
389  for p, state in self.pos:
390  p.add_bead_element(state, self.name,
391  indices[0], indices[-1], 1, h)
393  resind = IMP.atom.Residue(h).get_index()
394  for p, state in self.pos:
395  p.add_bead_element(state, self.name, resind, resind, 1, h)
396  else:
397  raise TypeError("Unhandled hierarchy %s" % str(h))
398 
399 
400 class Molecule(_SystemBase):
401  """Stores a named protein chain.
402  This class is constructed from within the State class.
403  It wraps an IMP.atom.Molecule and IMP.atom.Copy.
404  Structure is read using this class.
405  Resolutions and copies can be registered, but are only created
406  when build() is called.
407 
408  A Molecule acts like a simple Python list of residues, and can be indexed
409  by integer (starting at zero) or by string (starting at 1).
410  """
411 
412  def __init__(self, state, name, sequence, chain_id, copy_num,
413  mol_to_clone=None, alphabet=IMP.pmi.alphabets.amino_acid,
414  uniprot=None):
415  """The user should not call this directly; instead call
416  State.create_molecule()
417 
418  @param state The parent PMI State
419  @param name The name of the molecule (string)
420  @param sequence Sequence (string)
421  @param chain_id The chain of this molecule
422  @param copy_num Store the copy number
423  @param mol_to_clone The original molecule (for cloning ONLY)
424  @note It's expected that you will not use this constructor directly,
425  but rather create a Molecule with State.create_molecule()
426  """
427  # internal data storage
428  self.model = state.get_hierarchy().get_model()
429  self.state = state
430  self.sequence = sequence
431  self.built = False
432  self.mol_to_clone = mol_to_clone
433  self.alphabet = alphabet
434  self.representations = [] # list of stuff to build
435  self._pdb_elements = []
436  self.uniprot = uniprot
437  # residues with representation
438  self._represented = IMP.pmi.tools.OrderedSet()
439  # helps you place beads by storing structure
440  self.coord_finder = _FindCloseStructure()
441  # list of OrderedSets of tempresidues set to ideal helix
442  self._ideal_helices = []
443 
444  # create root node and set it as child to passed parent hierarchy
445  self.hier = self._create_child(self.state.get_hierarchy())
446  self.hier.set_name(name)
447  IMP.atom.Copy.setup_particle(self.hier, copy_num)
448  self._name_with_copy = "%s.%d" % (name, copy_num)
449  # store the sequence
450  self.chain = IMP.atom.Chain.setup_particle(self.hier, chain_id)
451  self.chain.set_sequence(self.sequence)
452  self.chain.set_chain_type(alphabet.get_chain_type())
453  if self.uniprot:
454  self.chain.set_uniprot_accession(self.uniprot)
455  # create TempResidues from the sequence (if passed)
456  self.residues = []
457  for ns, s in enumerate(sequence):
458  r = TempResidue(self, s, ns+1, ns, alphabet)
459  self.residues.append(r)
460 
461  _start_model = property(lambda self: self.state._start_model)
462 
463  def __repr__(self):
464  return self.state.__repr__() + '.' + self.get_name() + '.' + \
465  str(IMP.atom.Copy(self.hier).get_copy_index())
466 
467  def __getitem__(self, val):
468  if isinstance(val, int):
469  return self.residues[val]
470  elif isinstance(val, str):
471  return self.residues[int(val)-1]
472  elif isinstance(val, slice):
473  return IMP.pmi.tools.OrderedSet(self.residues[val])
474  else:
475  raise TypeError("Indexes must be int or str")
476 
477  def get_hierarchy(self):
478  """Return the IMP Hierarchy corresponding to this Molecule"""
479  return self.hier
480 
481  def get_name(self):
482  """Return this Molecule name"""
483  return self.hier.get_name()
484 
485  def get_state(self):
486  """Return the State containing this Molecule"""
487  return self.state
488 
489  def get_ideal_helices(self):
490  """Returns list of OrderedSets with requested ideal helices"""
491  return self._ideal_helices
492 
493  def residue_range(self, a, b, stride=1):
494  """Get residue range from a to b, inclusive.
495  Use integers to get 0-indexing, or strings to get PDB-indexing"""
496  if isinstance(a, int) and isinstance(b, int) \
497  and isinstance(stride, int):
498  return IMP.pmi.tools.OrderedSet(self.residues[a:b+1:stride])
499  elif isinstance(a, str) and isinstance(b, str) \
500  and isinstance(stride, int):
501  return IMP.pmi.tools.OrderedSet(
502  self.residues[int(a)-1:int(b):stride])
503  else:
504  raise TypeError("Range ends must be int or str. "
505  "Stride must be int.")
506 
507  def get_residues(self):
508  """Return all modeled TempResidues as a set"""
509  all_res = IMP.pmi.tools.OrderedSet(self.residues)
510  return all_res
511 
512  def get_represented(self):
513  """Return set of TempResidues that have representation"""
514  return self._represented
515 
517  """Return a set of TempResidues that have associated structure
518  coordinates"""
519  atomic_res = IMP.pmi.tools.OrderedSet()
520  for res in self.residues:
521  if res.get_has_structure():
522  atomic_res.add(res)
523  return atomic_res
524 
526  """Return a set of TempResidues that don't have associated
527  structure coordinates"""
528  non_atomic_res = IMP.pmi.tools.OrderedSet()
529  for res in self.residues:
530  if not res.get_has_structure():
531  non_atomic_res.add(res)
532  return non_atomic_res
533 
534  def create_copy(self, chain_id):
535  """Create a new Molecule with the same name and sequence but a
536  higher copy number. Returns the Molecule. No structure or
537  representation will be copied!
538 
539  @param chain_id Chain ID of the new molecule
540  """
541  mol = Molecule(
542  self.state, self.get_name(), self.sequence, chain_id,
543  copy_num=self.state.get_number_of_copies(self.get_name()))
544  self.state._register_copy(mol)
545  return mol
546 
547  def create_clone(self, chain_id):
548  """Create a Molecule clone (automatically builds same structure
549  and representation)
550 
551  @param chain_id If you want to set the chain ID of the copy
552  to something
553  @note You cannot add structure or representations to a clone!
554  """
555  mol = Molecule(
556  self.state, self.get_name(), self.sequence, chain_id,
557  copy_num=self.state.get_number_of_copies(self.get_name()),
558  mol_to_clone=self)
559  self.state._register_copy(mol)
560  return mol
561 
562  def add_structure(self, pdb_fn, chain_id, res_range=[],
563  offset=0, model_num=None, ca_only=False,
564  soft_check=False):
565  """Read a structure and store the coordinates.
566  @return the atomic residues (as a set)
567  @param pdb_fn The file to read (in PDB, mmCIF or BinaryCIF format)
568  @param chain_id Chain ID to read
569  @param res_range Add only a specific set of residues from the PDB
570  file. res_range[0] is the starting and res_range[1]
571  is the ending residue index.
572  @param offset Apply an offset to the residue indexes of the PDB
573  file. This number is added to the PDB sequence.
574  PMI uses 1-based FASTA numbering internally (the
575  first residue in the sequence is numbered 1, and
576  so on). If the PDB chain is not also numbered
577  starting from 1, apply an offset to make it match
578  the FASTA. For example, if the PDB is numbered
579  starting from -5, use an offset of 6 (-5 + 6 = 1).
580  @param model_num Read multi-model PDB and return that model
581  @param ca_only Only read the CA positions from the PDB file
582  @param soft_check If True, it only warns if there are sequence
583  mismatches between the PDB and the Molecule (FASTA)
584  sequence, and uses the sequence from the PDB.
585  If False (Default), it raises an error when there
586  are sequence mismatches.
587  @note If you are adding structure without a FASTA file, set soft_check
588  to True.
589  """
590  if self.mol_to_clone is not None:
591  raise ValueError('You cannot call add_structure() for a clone')
592 
593  self.pdb_fn = pdb_fn
594 
595  # get IMP.atom.Residues from the pdb file
596  rhs = system_tools.get_structure(self._start_model, pdb_fn, chain_id,
597  res_range, offset,
598  ca_only=ca_only)
599  self.coord_finder.add_residues(rhs)
600 
601  if len(self.residues) == 0:
602  warnings.warn(
603  "Substituting PDB residue type with FASTA residue type. "
604  "Potentially dangerous.", IMP.pmi.StructureWarning)
605 
606  # Store info for ProtocolOutput usage later
607  self._pdb_elements.append(
608  (rhs, _PDBElement(offset=offset, filename=pdb_fn,
609  chain_id=chain_id)))
610 
611  # load those into TempResidue object
612  # collect integer indexes of atomic residues to return
613  atomic_res = IMP.pmi.tools.OrderedSet()
614  for nrh, rh in enumerate(rhs):
615  pdb_idx = rh.get_index()
616  raw_idx = pdb_idx - 1
617 
618  # add ALA to fill in gaps
619  while len(self.residues) < pdb_idx:
620  r = TempResidue(self, 'A', len(self.residues)+1,
621  len(self.residues),
622  IMP.pmi.alphabets.amino_acid)
623  self.residues.append(r)
624  self.sequence += 'A'
625 
626  internal_res = self.residues[raw_idx]
627  if len(self.sequence) < raw_idx:
628  self.sequence += IMP.atom.get_one_letter_code(
629  rh.get_residue_type())
630  internal_res.set_structure(rh, soft_check)
631  atomic_res.add(internal_res)
632 
633  self.chain.set_sequence(self.sequence)
634  return atomic_res
635 
637  residues=None,
638  resolutions=[],
639  bead_extra_breaks=[],
640  bead_ca_centers=True,
641  bead_default_coord=[0, 0, 0],
642  density_residues_per_component=None,
643  density_prefix=None,
644  density_force_compute=False,
645  density_voxel_size=1.0,
646  setup_particles_as_densities=False,
647  ideal_helix=False,
648  color=None):
649  """Set the representation for some residues. Some options
650  (beads, ideal helix) operate along the backbone. Others (density
651  options) are volumetric.
652  Some of these you can combine e.g., beads+densities or helix+densities
653  See @ref pmi_resolution
654  @param residues Set of PMI TempResidues for adding the representation.
655  Can use Molecule slicing to get these, e.g. mol[a:b]+mol[c:d]
656  If None, will select all residues for this Molecule.
657  @param resolutions Resolutions for beads representations.
658  If structured, will average along backbone, breaking at
659  sequence breaks. If unstructured, will just create beads.
660  Pass an integer or list of integers
661  @param bead_extra_breaks Additional breakpoints for splitting beads.
662  The value can be the 0-ordered position, after which it'll
663  insert the break.
664  Alternatively pass PDB-style (1-ordered) indices as a string.
665  I.e., bead_extra_breaks=[5,25] is the same as ['6','26']
666  @param bead_ca_centers Set to True if you want the resolution=1 beads
667  to be at CA centers (otherwise will average atoms to get
668  center). Defaults to True.
669  @param bead_default_coord Advanced feature. Normally beads are placed
670  at the nearest structure. If no structure provided (like an
671  all bead molecule), the beads go here.
672  @param density_residues_per_component Create density (Gaussian
673  Mixture Model) for these residues. Must also supply
674  density_prefix
675  @param density_prefix Prefix (assuming '.txt') to read components
676  from or write to.
677  If exists, will read unless you set density_force_compute=True.
678  Will also write map (prefix+'.mrc').
679  Must also supply density_residues_per_component.
680  @param density_force_compute Set true to force overwrite density file.
681  @param density_voxel_size Advanced feature. Set larger if densities
682  taking too long to rasterize.
683  Set to 0 if you don't want to create the MRC file
684  @param setup_particles_as_densities Set to True if you want each
685  particle to be its own density.
686  Useful for all-atom models or flexible beads.
687  Mutually exclusive with density_ options
688  @param ideal_helix Create idealized helix structures for these
689  residues at resolution 1.
690  Any other resolutions passed will be coarsened from there.
691  Resolution 0 will not work; you may have to use MODELLER
692  to do that (for now).
693  @param color the color applied to the hierarchies generated.
694  Format options: tuple (r,g,b) with values 0 to 1;
695  float (from 0 to 1, a map from Blue to Green to Red);
696  a [Chimera name](https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/colortables.html);
697  a hex RGB string (e.g. "#ff0000");
698  an IMP.display.Color object
699  @note You cannot call add_representation multiple times for the
700  same residues.
701  """ # noqa: E501
702 
703  # can't customize clones
704  if self.mol_to_clone is not None:
705  raise ValueError(
706  'You cannot call add_representation() for a clone.'
707  ' Maybe use a copy instead.')
708 
709  # format input
710  if residues is None:
711  res = IMP.pmi.tools.OrderedSet(self.residues)
712  elif residues == self:
713  res = IMP.pmi.tools.OrderedSet(self.residues)
714  elif type(residues) is IMP.pmi.topology.TempResidue:
715  res = IMP.pmi.tools.OrderedSet([residues])
716  elif hasattr(residues, '__iter__'):
717  if len(residues) == 0:
718  raise Exception(
719  'You passed an empty set to add_representation')
720  if type(residues) is IMP.pmi.tools.OrderedSet \
721  and type(next(iter(residues))) is TempResidue:
722  res = residues
723  elif (type(residues) is set
724  and type(next(iter(residues))) is TempResidue):
725  res = IMP.pmi.tools.OrderedSet(residues)
726  elif type(residues) is list and type(residues[0]) is TempResidue:
727  res = IMP.pmi.tools.OrderedSet(residues)
728  else:
729  raise Exception("You passed an iterable of something other "
730  "than TempResidue", res)
731  else:
732  raise Exception("add_representation: you must pass a set of "
733  "residues or nothing(=all residues)")
734 
735  # check that each residue has not been represented yet
736  ov = res & self._represented
737  if ov:
738  raise Exception('You have already added representation for ' +
739  self.get_hierarchy().get_name() + ': ' +
740  ov.__repr__())
741  self._represented |= res
742 
743  # check you aren't creating multiple resolutions without structure
744  if not hasattr(resolutions, '__iter__'):
745  if type(resolutions) is int:
746  resolutions = [resolutions]
747  else:
748  raise Exception("you tried to pass resolutions that are not "
749  "int or list-of-int")
750  if len(resolutions) > 1 and not ideal_helix:
751  for r in res:
752  if not r.get_has_structure():
753  raise Exception(
754  'You are creating multiple resolutions for '
755  'unstructured regions. This will have unexpected '
756  'results.')
757 
758  # check density info is consistent
759  if density_residues_per_component or density_prefix:
760  if not density_residues_per_component and density_prefix:
761  raise Exception(
762  'If requesting density, must provide '
763  'density_residues_per_component AND density_prefix')
764  if density_residues_per_component and setup_particles_as_densities:
765  raise Exception(
766  'Cannot create both volumetric density '
767  '(density_residues_per_component) AND '
768  'individual densities (setup_particles_as_densities) '
769  'in the same representation')
770  if len(resolutions) > 1 and setup_particles_as_densities:
771  raise Exception(
772  'You have multiple bead resolutions but are attempting to '
773  'set them all up as individual Densities. '
774  'This could have unexpected results.')
775 
776  # check helix not accompanied by other resolutions
777  # (densities OK though!)
778  if ideal_helix:
779  if 0 in resolutions:
780  raise Exception(
781  "For ideal helices, cannot build resolution 0: "
782  "you have to do that in MODELLER")
783  if 1 not in resolutions:
784  resolutions = [1] + list(resolutions)
785  self._ideal_helices.append(res)
786 
787  # check residues are all part of this molecule:
788  for r in res:
789  if r.get_molecule() != self:
790  raise Exception(
791  'You are adding residues from a different molecule to',
792  self.__repr__())
793 
794  # unify formatting for extra breaks
795  breaks = []
796  for b in bead_extra_breaks:
797  if isinstance(b, str):
798  breaks.append(int(b)-1)
799  else:
800  breaks.append(b)
801  # store the representation group
802  self.representations.append(_Representation(
803  res, resolutions, breaks, bead_ca_centers, bead_default_coord,
804  density_residues_per_component, density_prefix,
805  density_force_compute, density_voxel_size,
806  setup_particles_as_densities, ideal_helix, color))
807 
808  def _all_protocol_output(self):
809  return self.state._protocol_output
810 
811  def _build_protocol_output(self):
812  """Add molecule name and sequence to any ProtocolOutput objects"""
813  if not self.built:
814  name = self.hier.get_name()
815  for po, state in self._all_protocol_output():
816  po.create_component(state, name, True,
817  asym_name=self._name_with_copy)
818  po.add_component_sequence(state, name, self.sequence,
819  asym_name=self._name_with_copy,
820  alphabet=self.alphabet,
821  uniprot=self.uniprot)
822 
823  def _finalize_build(self):
824  # For clones, pass the representation of the original molecule
825  # to ProtocolOutput
826  if self.mol_to_clone:
827  rephandler = _RepresentationHandler(
828  self._name_with_copy, list(self._all_protocol_output()),
829  self.mol_to_clone._pdb_elements)
830  for res in self.mol_to_clone.residues:
831  if res.hier:
832  rephandler(res)
833 
834  def build(self, protocol_output=True):
835  """Create all parts of the IMP hierarchy
836  including Atoms, Residues, and Fragments/Representations and,
837  finally, Copies.
838  Will only build requested representations.
839  @note Any residues assigned a resolution must have an IMP.atom.Residue
840  hierarchy containing at least a CAlpha. For missing residues,
841  these can be constructed from the PDB file.
842  """
843  if not self.built:
844  if protocol_output:
845  self._build_protocol_output()
846  # if requested, clone structure and representations
847  # BEFORE building original
848  if self.mol_to_clone is not None:
849  for nr, r in enumerate(self.mol_to_clone.residues):
850  if r.get_has_structure():
851  clone = IMP.atom.create_clone(r.get_hierarchy())
852  self.residues[nr].set_structure(
853  IMP.atom.Residue(clone), soft_check=True)
854  for old_rep in self.mol_to_clone.representations:
855  new_res = IMP.pmi.tools.OrderedSet()
856  for r in old_rep.residues:
857  new_res.add(self.residues[r.get_internal_index()])
858  self._represented.add(
859  self.residues[r.get_internal_index()])
860  new_rep = _Representation(
861  new_res, old_rep.bead_resolutions,
862  old_rep.bead_extra_breaks, old_rep.bead_ca_centers,
863  old_rep.bead_default_coord,
864  old_rep.density_residues_per_component,
865  old_rep.density_prefix, False,
866  old_rep.density_voxel_size,
867  old_rep.setup_particles_as_densities,
868  old_rep.ideal_helix, old_rep.color)
869  self.representations.append(new_rep)
870  self.coord_finder = self.mol_to_clone.coord_finder
871 
872  # give a warning for all residues that don't have representation
873  no_rep = [r for r in self.residues if r not in self._represented]
874  if len(no_rep) > 0:
875  warnings.warn(
876  'Residues without representation in molecule %s: %s'
877  % (self.get_name(), system_tools.resnums2str(no_rep)),
879 
880  # first build any ideal helices (fills in structure for
881  # the TempResidues)
882  for rep in self.representations:
883  if rep.ideal_helix:
884  _build_ideal_helix(self._start_model, rep.residues,
885  self.coord_finder)
886 
887  # build all the representations
888  built_reps = []
889 
890  rephandler = _RepresentationHandler(
891  self._name_with_copy, list(self._all_protocol_output()),
892  self._pdb_elements)
893 
894  for rep in self.representations:
895  built_reps += system_tools.build_representation(
896  self, rep, self.coord_finder, rephandler)
897 
898  # sort them before adding as children
899  built_reps.sort(
900  key=lambda r: IMP.atom.Fragment(r).get_residue_indexes()[0])
901  for br in built_reps:
902  self.hier.add_child(br)
903  br.update_parents()
904  self.built = True
905 
906  for res in self.residues:
907  # first off, store the highest resolution available
908  # in residue.hier
909  new_ps = IMP.atom.Selection(
910  self.hier,
911  residue_index=res.get_index(),
912  resolution=1).get_selected_particles()
913  if len(new_ps) > 0:
914  new_p = new_ps[0]
915  if IMP.atom.Atom.get_is_setup(new_p):
916  # if only found atomic, store the residue
917  new_hier = IMP.atom.get_residue(IMP.atom.Atom(new_p))
918  else:
919  # otherwise just store what you found
920  new_hier = IMP.atom.Hierarchy(new_p)
921  res.hier = new_hier
922  # Clones will be handled in _finalize_build() instead
923  # (can't handle them here as the parent of the clone
924  # isn't built yet)
925  if self.mol_to_clone is None:
926  rephandler(res)
927  else:
928  res.hier = None
929  self._represented = IMP.pmi.tools.OrderedSet(
930  [a for a in self._represented])
931  print('done building', self.get_hierarchy())
932  return self.hier
933 
934  def get_particles_at_all_resolutions(self, residue_indexes=None):
935  """Helpful utility for getting particles at all resolutions from
936  this molecule. Can optionally pass a set of residue indexes"""
937  if not self.built:
938  raise Exception(
939  "Cannot get all resolutions until you build the Molecule")
940  if residue_indexes is None:
941  residue_indexes = [r.get_index() for r in self.get_residues()]
943  self.get_hierarchy(), residue_indexes=residue_indexes)
944  return ps
945 
946 
947 class _Representation:
948  """Private class just to store a representation request"""
949  def __init__(self,
950  residues,
951  bead_resolutions,
952  bead_extra_breaks,
953  bead_ca_centers,
954  bead_default_coord,
955  density_residues_per_component,
956  density_prefix,
957  density_force_compute,
958  density_voxel_size,
959  setup_particles_as_densities,
960  ideal_helix,
961  color):
962  self.residues = residues
963  self.bead_resolutions = bead_resolutions
964  self.bead_extra_breaks = bead_extra_breaks
965  self.bead_ca_centers = bead_ca_centers
966  self.bead_default_coord = bead_default_coord
967  self.density_residues_per_component = density_residues_per_component
968  self.density_prefix = density_prefix
969  self.density_force_compute = density_force_compute
970  self.density_voxel_size = density_voxel_size
971  self.setup_particles_as_densities = setup_particles_as_densities
972  self.ideal_helix = ideal_helix
973  self.color = color
974 
975 
976 class _FindCloseStructure:
977  """Utility to get the nearest observed coordinate"""
978  def __init__(self):
979  self.coords = []
980 
981  def add_residues(self, residues):
982  for r in residues:
983  idx = IMP.atom.Residue(r).get_index()
984  catypes = [IMP.atom.AT_CA, system_tools._AT_HET_CA]
985  ca = IMP.atom.Selection(
986  r, atom_types=catypes).get_selected_particles()
987  p = IMP.atom.Selection(
988  r, atom_type=IMP.atom.AtomType("P")).get_selected_particles()
989  if len(ca) == 1:
990  xyz = IMP.core.XYZ(ca[0]).get_coordinates()
991  self.coords.append([idx, xyz])
992  elif len(p) == 1:
993  xyz = IMP.core.XYZ(p[0]).get_coordinates()
994  self.coords.append([idx, xyz])
995  else:
996  raise ValueError("_FindCloseStructure: wrong selection")
997 
998  self.coords.sort(key=itemgetter(0))
999 
1000  def find_nearest_coord(self, query):
1001  if self.coords == []:
1002  return None
1003  keys = [r[0] for r in self.coords]
1004  pos = bisect_left(keys, query)
1005  if pos == 0:
1006  ret = self.coords[0]
1007  elif pos == len(self.coords):
1008  ret = self.coords[-1]
1009  else:
1010  before = self.coords[pos - 1]
1011  after = self.coords[pos]
1012  if after[0] - query < query - before[0]:
1013  ret = after
1014  else:
1015  ret = before
1016  return ret[1]
1017 
1018 
1020  """A dictionary-like wrapper for reading and storing sequence data.
1021  Keys are FASTA sequence names, and each value a string of one-letter
1022  codes.
1023 
1024  The FASTA header may contain multiple fields split by pipe (|)
1025  characters. If so, the FASTA sequence name is the first field and
1026  the second field (if present) is the UniProt accession.
1027  For example, ">cop9|Q13098" yields a FASTA sequence name of "cop9"
1028  and UniProt accession of "Q13098".
1029  """
1030  def __init__(self, fasta_fn, name_map=None):
1031  """Read a FASTA file and extract all the requested sequences
1032  @param fasta_fn sequence file
1033  @param name_map dictionary mapping the FASTA name to final stored name
1034  """
1035  # Mapping from sequence name to primary sequence
1036  self.sequences = IMP.pmi.tools.OrderedDict()
1037  # Mapping from sequence name to UniProt accession, if available
1038  self.uniprot = {}
1039  self.read_sequences(fasta_fn, name_map)
1040 
1041  def __len__(self):
1042  return len(self.sequences)
1043 
1044  def __contains__(self, x):
1045  return x in self.sequences
1046 
1047  def __getitem__(self, key):
1048  if type(key) is int:
1049  allseqs = list(self.sequences.keys())
1050  try:
1051  return self.sequences[allseqs[key]]
1052  except IndexError:
1053  raise IndexError("You tried to access sequence number %d "
1054  "but there's only %d" % (key, len(allseqs)))
1055  else:
1056  return self.sequences[key]
1057 
1058  def __iter__(self):
1059  return self.sequences.__iter__()
1060 
1061  def __repr__(self):
1062  ret = ''
1063  for s in self.sequences:
1064  ret += '%s\t%s\n' % (s, self.sequences[s])
1065  return ret
1066 
1067  def read_sequences(self, fasta_fn, name_map=None):
1068  code = None
1069  seq = None
1070  with open(fasta_fn, 'r') as fh:
1071  for (num, line) in enumerate(fh):
1072  if line.startswith('>'):
1073  if seq is not None:
1074  self.sequences[code] = seq.strip('*')
1075  spl = line[1:].split('|')
1076  code = spl[0].strip()
1077  if name_map is not None:
1078  try:
1079  code = name_map[code]
1080  except KeyError:
1081  pass
1082  seq = ''
1083  if len(spl) >= 2:
1084  up_accession = spl[1].strip()
1085  self.uniprot[code] = up_accession
1086  else:
1087  line = line.rstrip()
1088  if line: # Skip blank lines
1089  if seq is None:
1090  raise Exception(
1091  "Found FASTA sequence before first header "
1092  "at line %d: %s" % (num + 1, line))
1093  seq += line
1094  if seq is not None:
1095  self.sequences[code] = seq.strip('*')
1096 
1097 
1099  """Data structure for reading and storing sequence data from PDBs.
1100 
1101  @see fasta_pdb_alignments."""
1102  def __init__(self, model, pdb_fn, name_map=None):
1103  """Read a PDB file and return all sequences for each contiguous
1104  fragment
1105  @param pdb_fn file
1106  @param name_map dictionary mapping the pdb chain id to final
1107  stored name
1108  """
1109  self.model = model
1110  # self.sequences data-structure: (two-key dictionary)
1111  # it contains all contiguous fragments:
1112  # chain_id, tuples indicating first and last residue, sequence
1113  # example:
1114  # key1, key2, value
1115  # A (77, 149) VENPSLDLEQYAASYSGLMR....
1116  # A (160, 505) PALDTAWVEATRKKALLKLEKLDTDLKNYKGNSIK.....
1117  # B (30, 180) VDLENQYYNSKALKEDDPKAALSSFQKVLELEGEKGEWGF...
1118  # B (192, 443) TQLLEIYALEIQMYTAQKNNKKLKALYEQSLHIKSAIPHPL
1119  self.sequences = IMP.pmi.tools.OrderedDict()
1120  self.read_sequences(pdb_fn, name_map)
1121 
1122  def read_sequences(self, pdb_fn, name_map):
1123  read_file = IMP.atom.read_pdb
1124  if pdb_fn.endswith('.cif'):
1125  read_file = IMP.atom.read_mmcif
1126  t = read_file(pdb_fn, self.model, IMP.atom.ATOMPDBSelector())
1127  cs = IMP.atom.get_by_type(t, IMP.atom.CHAIN_TYPE)
1128  for c in cs:
1129  id = IMP.atom.Chain(c).get_id()
1130  print(id)
1131  if name_map:
1132  try:
1133  id = name_map[id]
1134  except KeyError:
1135  print("Chain ID %s not in name_map, skipping" % id)
1136  continue
1137  rs = IMP.atom.get_by_type(c, IMP.atom.RESIDUE_TYPE)
1138  rids = []
1139  rids_olc_dict = {}
1140  for r in rs:
1141  dr = IMP.atom.Residue(r)
1142  rid = dr.get_index()
1143 
1144  isprotein = dr.get_is_protein()
1145  isrna = dr.get_is_rna()
1146  isdna = dr.get_is_dna()
1147  if isprotein:
1148  olc = IMP.atom.get_one_letter_code(dr.get_residue_type())
1149  rids.append(rid)
1150  rids_olc_dict[rid] = olc
1151  elif isdna:
1152  if dr.get_residue_type() == IMP.atom.DADE:
1153  olc = "A"
1154  if dr.get_residue_type() == IMP.atom.DURA:
1155  olc = "U"
1156  if dr.get_residue_type() == IMP.atom.DCYT:
1157  olc = "C"
1158  if dr.get_residue_type() == IMP.atom.DGUA:
1159  olc = "G"
1160  if dr.get_residue_type() == IMP.atom.DTHY:
1161  olc = "T"
1162  rids.append(rid)
1163  rids_olc_dict[rid] = olc
1164  elif isrna:
1165  if dr.get_residue_type() == IMP.atom.ADE:
1166  olc = "A"
1167  if dr.get_residue_type() == IMP.atom.URA:
1168  olc = "U"
1169  if dr.get_residue_type() == IMP.atom.CYT:
1170  olc = "C"
1171  if dr.get_residue_type() == IMP.atom.GUA:
1172  olc = "G"
1173  if dr.get_residue_type() == IMP.atom.THY:
1174  olc = "T"
1175  rids.append(rid)
1176  rids_olc_dict[rid] = olc
1177  group_rids = self.group_indexes(rids)
1178  contiguous_sequences = IMP.pmi.tools.OrderedDict()
1179  for group in group_rids:
1180  sequence_fragment = ""
1181  for i in range(group[0], group[1]+1):
1182  sequence_fragment += rids_olc_dict[i]
1183  contiguous_sequences[group] = sequence_fragment
1184  self.sequences[id] = contiguous_sequences
1185 
1186  def group_indexes(self, indexes):
1187  from itertools import groupby
1188  ranges = []
1189  for k, g in groupby(enumerate(indexes), lambda x: x[0]-x[1]):
1190  group = [x[1] for x in g]
1191  ranges.append((group[0], group[-1]))
1192  return ranges
1193 
1194 
1195 def fasta_pdb_alignments(fasta_sequences, pdb_sequences, show=False):
1196  '''This function computes and prints the alignment between the
1197  fasta file and the pdb sequence, computes the offsets for each contiguous
1198  fragment in the PDB.
1199  @param fasta_sequences IMP.pmi.topology.Sequences object
1200  @param pdb_sequences IMP.pmi.topology.PDBSequences object
1201  @param show boolean default False, if True prints the alignments.
1202  The input objects should be generated using map_name dictionaries
1203  such that fasta_id
1204  and pdb_chain_id are mapping to the same protein name. It needs BioPython.
1205  Returns a dictionary of offsets, organized by peptide range (group):
1206  example: offsets={"ProtA":{(1,10):1,(20,30):10}}'''
1207  from Bio import pairwise2
1208  from Bio.pairwise2 import format_alignment
1209  if type(fasta_sequences) is not IMP.pmi.topology.Sequences:
1210  raise Exception("Fasta sequences not type IMP.pmi.topology.Sequences")
1211  if type(pdb_sequences) is not IMP.pmi.topology.PDBSequences:
1212  raise Exception("pdb sequences not type IMP.pmi.topology.PDBSequences")
1213  offsets = IMP.pmi.tools.OrderedDict()
1214  for name in fasta_sequences.sequences:
1215  print(name)
1216  seq_fasta = fasta_sequences.sequences[name]
1217  if name not in pdb_sequences.sequences:
1218  print("Fasta id %s not in pdb names, aligning against every "
1219  "pdb chain" % name)
1220  pdbnames = pdb_sequences.sequences.keys()
1221  else:
1222  pdbnames = [name]
1223  for pdbname in pdbnames:
1224  for group in pdb_sequences.sequences[pdbname]:
1225  if group[1] - group[0] + 1 < 7:
1226  continue
1227  seq_frag_pdb = pdb_sequences.sequences[pdbname][group]
1228  if show:
1229  print("########################")
1230  print(" ")
1231  print("protein name", pdbname)
1232  print("fasta id", name)
1233  print("pdb fragment", group)
1234  align = pairwise2.align.localms(seq_fasta, seq_frag_pdb,
1235  2, -1, -.5, -.1)[0]
1236  for a in [align]:
1237  offset = a[3] + 1 - group[0]
1238  if show:
1239  print("alignment sequence start-end",
1240  (a[3] + 1, a[4] + 1))
1241  print("offset from pdb to fasta index", offset)
1242  print(format_alignment(*a))
1243  if name not in offsets:
1244  offsets[pdbname] = {}
1245  if group not in offsets[pdbname]:
1246  offsets[pdbname][group] = offset
1247  else:
1248  if group not in offsets[pdbname]:
1249  offsets[pdbname][group] = offset
1250  return offsets
1251 
1252 
1254  "Temporarily stores residue information, even without structure available."
1255  # Consider implementing __hash__ so you can select.
1256  def __init__(self, molecule, code, index, internal_index, alphabet):
1257  """setup a TempResidue
1258  @param molecule PMI Molecule to which this residue belongs
1259  @param code one-letter residue type code
1260  @param index PDB index
1261  @param internal_index The number in the sequence
1262  """
1263  # these attributes should be immutable
1264  self.molecule = molecule
1265  self.rtype = alphabet.get_residue_type_from_one_letter_code(code)
1266  self.pdb_index = index
1267  self.internal_index = internal_index
1268  self.copy_index = IMP.atom.Copy(self.molecule.hier).get_copy_index()
1269  self.state_index = \
1270  IMP.atom.State(self.molecule.state.hier).get_state_index()
1271  # these are expected to change
1272  self._structured = False
1273  self.hier = IMP.atom.Residue.setup_particle(
1274  IMP.Particle(molecule._start_model), self.rtype, index)
1275 
1276  def __str__(self):
1277  return str(self.state_index) + "_" + self.molecule.get_name() + "_" \
1278  + str(self.copy_index) + "_" + self.get_code() \
1279  + str(self.get_index())
1280 
1281  def __repr__(self):
1282  return self.__str__()
1283 
1284  def __key(self):
1285  # this returns the immutable attributes only
1286  return (self.state_index, self.molecule, self.copy_index, self.rtype,
1287  self.pdb_index, self.internal_index)
1288 
1289  def __eq__(self, other):
1290  return (type(other) == type(self) # noqa: E721
1291  and self.__key() == other.__key())
1292 
1293  def __hash__(self):
1294  return hash(self.__key())
1295 
1296  def get_index(self):
1297  return self.pdb_index
1298 
1299  def get_internal_index(self):
1300  return self.internal_index
1301 
1302  def get_code(self):
1303  return IMP.atom.get_one_letter_code(self.get_residue_type())
1304 
1305  def get_residue_type(self):
1306  return self.rtype
1307 
1308  def get_hierarchy(self, model=None):
1309  if model is not None and self.hier.get_model() != model:
1310  # Need to clone if it needs to be in a different model
1311  return IMP.atom.create_clone(self.hier, model)
1312  else:
1313  return self.hier
1314 
1315  def get_molecule(self):
1316  return self.molecule
1317 
1318  def get_has_structure(self):
1319  return self._structured
1320 
1321  def set_structure(self, res, soft_check=False):
1322  if res.get_residue_type() != self.get_residue_type():
1323  if (res.get_residue_type() == IMP.atom.MSE
1324  and self.get_residue_type() == IMP.atom.MET):
1325  # MSE in the PDB file is OK to match with MET in the FASTA
1326  # sequence
1327  pass
1328  elif soft_check:
1329  # note from commit a2c13eaa1 we give priority to the
1330  # FASTA and not the PDB
1331  warnings.warn(
1332  'Inconsistency between FASTA sequence and PDB sequence. '
1333  'FASTA type %s %s and PDB type %s'
1334  % (self.get_index(), self.hier.get_residue_type(),
1335  res.get_residue_type()),
1337  self.hier.set_residue_type((self.get_residue_type()))
1338  self.rtype = self.get_residue_type()
1339  else:
1340  raise Exception(
1341  'ERROR: PDB residue index', self.get_index(), 'is',
1342  IMP.atom.get_one_letter_code(res.get_residue_type()),
1343  'and sequence residue is', self.get_code())
1344 
1345  for a in res.get_children():
1346  self.hier.add_child(a)
1347  atype = IMP.atom.Atom(a).get_atom_type()
1348  a.get_particle().set_name(
1349  'Atom %s of residue %i' % (atype.__str__().strip('"'),
1350  self.hier.get_index()))
1351  self._structured = True
1352 
1353 
1355  """Automatically setup System and Degrees of Freedom with a formatted
1356  text file.
1357  The file is read in and each part of the topology is stored as a
1358  ComponentTopology object for input into IMP::pmi::macros::BuildSystem.
1359  The topology file should be in a simple pipe-delimited format:
1360  @code{.txt}
1361 |molecule_name|color|fasta_fn|fasta_id|pdb_fn|chain|residue_range|pdb_offset|bead_size|em_residues_per_gaussian|rigid_body|super_rigid_body|chain_of_super_rigid_bodies|flags|
1362 |Rpb1 |blue |1WCM.fasta|1WCM:A|1WCM.pdb|A|1,1140 |0|10|0|1|1,3|1||
1363 |Rpb1 |blue |1WCM.fasta|1WCM:A|1WCM.pdb|A|1141,1274|0|10|0|2|1,3|1||
1364 |Rpb1 |blue |1WCM.fasta|1WCM:A|1WCM.pdb|A|1275,END |0|10|0|3|1,3|1||
1365 |Rpb2 |red |1WCM.fasta|1WCM:B|1WCM.pdb|B|all |0|10|0|4|2,3|2||
1366 |Rpb2.1 |green |1WCM.fasta|1WCM:B|1WCM.pdb|B|all |0|10|0|4|2,3|2||
1367 
1368  @endcode
1369 
1370  These are the fields you can enter:
1371  - `molecule_name`: Name of the molecule (chain). Serves as the parent
1372  hierarchy for this structure. Multiple copies of the same molecule
1373  can be created by appending a copy number after a period; if none is
1374  specified, a copy number of 0 is assumed (e.g. Rpb2.1 is the second copy
1375  of Rpb2 or Rpb2.0).
1376  - `color`: The color used in the output RMF file. Uses
1377  [Chimera names](https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/colortables.html),
1378  (e.g. "red"), or R,G,B values as three comma-separated floating point
1379  numbers from 0 to 1 (e.g. "1.0, 0.0, 0.0") or a 6-digit hex string
1380  starting with '#' (e.g. #ff0000).
1381  - `fasta_fn`: Name of FASTA file containing this component.
1382  - `fasta_id`: String found in FASTA sequence header line. The sequence read
1383  from the file is assumed to be a protein sequence. If it should instead
1384  be treated as RNA or DNA, add an ',RNA' or ',DNA' suffix. For example,
1385  a `fasta_id` of 'myseq,RNA' will read the sequence 'myseq' from the
1386  FASTA file and treat it as RNA. The FASTA header may contain multiple
1387  fields split by pipe (|) characters. If so, the FASTA sequence name is
1388  the first field and the second field (if present) is the UniProt
1389  accession. For example, ">cop9|Q13098" yields a FASTA sequence name
1390  of "cop9" and UniProt accession of "Q13098". If such an accession is
1391  present, it is added to the generated structure (and ultimately
1392  recorded in any output RMF file).
1393  - `pdb_fn`: Name of PDB, mmCIF, or BinaryCIF file with coordinates
1394  (if available). If left empty, will set up as BEADS (you can also
1395  specify "BEADS") Can also write "IDEAL_HELIX".
1396  - `chain`: Chain ID of this domain in the PDB, mmCIF or BinaryCIF file.
1397  This is the "author-provided" chain ID for mmCIF or BinaryCIF files,
1398  not the asym_id.
1399  - `residue_range`: Comma delimited pair defining range.
1400  Can leave empty or use 'all' for entire sequence from PDB file.
1401  The second item in the pair can be END to select the last residue in the
1402  PDB chain.
1403  - `pdb_offset`: Offset to sync PDB residue numbering with FASTA numbering.
1404  For example, an offset of -10 would match the first residue in the
1405  FASTA file (which is always numbered sequentially starting from 1) with
1406  residue 11 in the PDB file.
1407  - `bead_size`: The size (in residues) of beads used to model areas not
1408  covered by PDB coordinates. These will be built automatically.
1409  - `em_residues`: The number of Gaussians used to model the electron
1410  density of this domain. Set to zero if no EM fitting will be done.
1411  The GMM files will be written to <gmm_dir>/<component_name>_<em_res>.txt
1412  (and .mrc)
1413  - `rigid_body`: Leave empty if this object is not in a rigid body.
1414  Otherwise, this is a number corresponding to the rigid body containing
1415  this object. The number itself is just used for grouping things.
1416  - `super_rigid_body`: Add a mover that periodically moves several related
1417  domains as if they were a single large rigid body. In between such moves,
1418  the domains move independently. This can improve sampling.
1419  - `chain_of_super_rigid_bodies`: Do super-rigid-body moves (as above)
1420  for all adjacent pairs of domains in the chain.
1421  - `flags` additional flags for advanced options
1422  @note All filenames are relative to the paths specified in the constructor.
1423 
1424  """ # noqa: E501
1425  def __init__(self, topology_file, pdb_dir='./', fasta_dir='./',
1426  gmm_dir='./'):
1427  """Constructor.
1428  @param topology_file Pipe-delimited file specifying the topology
1429  @param pdb_dir Relative path to the pdb directory
1430  @param fasta_dir Relative path to the fasta directory
1431  @param gmm_dir Relative path to the GMM directory
1432  """
1433  self.topology_file = topology_file
1434  # key=molname, value=TempMolecule
1435  self.molecules = IMP.pmi.tools.OrderedDict()
1436  self.pdb_dir = pdb_dir
1437  self.fasta_dir = fasta_dir
1438  self.gmm_dir = gmm_dir
1439  self._components = self.read(topology_file)
1440 
1441  def write_topology_file(self, outfile):
1442  with open(outfile, "w") as f:
1443  f.write("|molecule_name|color|fasta_fn|fasta_id|pdb_fn|chain|"
1444  "residue_range|pdb_offset|bead_size|"
1445  "em_residues_per_gaussian|rigid_body|super_rigid_body|"
1446  "chain_of_super_rigid_bodies|\n")
1447  for c in self._components:
1448  output = c.get_str()+'\n'
1449  f.write(output)
1450  return outfile
1451 
1452  def get_components(self, topology_list="all"):
1453  """ Return list of ComponentTopologies for selected components
1454  @param topology_list List of indices to return"""
1455  if topology_list == "all":
1456  topologies = self._components
1457  else:
1458  topologies = []
1459  for i in topology_list:
1460  topologies.append(self._components[i])
1461  return topologies
1462 
1463  def get_molecules(self):
1464  return self.molecules
1465 
1466  def read(self, topology_file, append=False):
1467  """Read system components from topology file. append=False will erase
1468  current topology and overwrite with new
1469  """
1470  is_topology = False
1471  is_directories = False
1472  linenum = 1
1473  if not append:
1474  self._components = []
1475 
1476  with open(topology_file) as infile:
1477  for line in infile:
1478  if line.lstrip() == "" or line[0] == "#":
1479  continue
1480  elif line.split('|')[1].strip() in ("molecule_name"):
1481  is_topology = True
1482  is_directories = False
1483  old_format = False
1484  continue
1485  elif line.split('|')[1] == "component_name":
1486  is_topology = True
1488  "Old-style topology format (using "
1489  "|component_name|) is deprecated. Please switch to "
1490  "the new-style format (using |molecule_name|)\n")
1491  old_format = True
1492  is_directories = False
1493  continue
1494  elif line.split('|')[1] == "directories":
1496  "Setting directories in the topology file "
1497  "is deprecated. Please do so through the "
1498  "TopologyReader constructor. Note that new-style "
1499  "paths are relative to the current working "
1500  "directory, not the topology file.\n")
1501  is_directories = True
1502  elif is_directories:
1503  fields = line.split('|')
1504  setattr(self, fields[1],
1505  IMP.get_relative_path(topology_file, fields[2]))
1506  if is_topology:
1507  new_component = self._parse_line(line, linenum, old_format)
1508  self._components.append(new_component)
1509  linenum += 1
1510  return self._components
1511 
1512  def _parse_line(self, component_line, linenum, old_format):
1513  """Parse a line of topology values and matches them to their key.
1514  Checks each value for correct syntax
1515  Returns a list of Component objects
1516  fields:
1517  """
1518  c = _Component()
1519  values = [s.strip() for s in component_line.split('|')]
1520  errors = []
1521 
1522  # Required fields
1523  if old_format:
1524  c.molname = values[1]
1525  c.copyname = ''
1526  c._domain_name = values[2]
1527  c.color = 'blue'
1528  else:
1529  names = values[1].split('.')
1530  if len(names) == 1:
1531  c.molname = names[0]
1532  c.copyname = ''
1533  elif len(names) == 2:
1534  c.molname = names[0]
1535  c.copyname = names[1]
1536  else:
1537  c.molname = names[0]
1538  c.copyname = names[1]
1539  errors.append("Molecule name should be <molecule.copyID>")
1540  errors.append("For component %s line %d "
1541  % (c.molname, linenum))
1542  c._domain_name = c.molname + '.' + c.copyname
1543  colorfields = values[2].split(',')
1544  if len(colorfields) == 3:
1545  c.color = [float(x) for x in colorfields]
1546  if any([x > 1 for x in c.color]):
1547  c.color = [x/255 for x in c.color]
1548  else:
1549  c.color = values[2]
1550  c._orig_fasta_file = values[3]
1551  c.fasta_file = values[3]
1552  fasta_field = values[4].split(",")
1553  c.fasta_id = fasta_field[0]
1554  c.fasta_flag = None
1555  if len(fasta_field) > 1:
1556  c.fasta_flag = fasta_field[1]
1557  c._orig_pdb_input = values[5]
1558  pdb_input = values[5]
1559  tmp_chain = values[6]
1560  rr = values[7]
1561  offset = values[8]
1562  bead_size = values[9]
1563  emg = values[10]
1564  if old_format:
1565  rbs = srbs = csrbs = ''
1566  else:
1567  rbs = values[11]
1568  srbs = values[12]
1569  csrbs = values[13]
1570 
1571  if c.molname not in self.molecules:
1572  self.molecules[c.molname] = _TempMolecule(c)
1573  else:
1574  # COPY OR DOMAIN
1575  c._orig_fasta_file = \
1576  self.molecules[c.molname].orig_component._orig_fasta_file
1577  c.fasta_id = self.molecules[c.molname].orig_component.fasta_id
1578  self.molecules[c.molname].add_component(c, c.copyname)
1579 
1580  # now cleanup input
1581  c.fasta_file = os.path.join(self.fasta_dir, c._orig_fasta_file)
1582  if pdb_input == "":
1583  errors.append("PDB must have BEADS, IDEAL_HELIX, or filename")
1584  errors.append("For component %s line %d is not correct"
1585  "|%s| was given." % (c.molname, linenum, pdb_input))
1586  elif pdb_input in ("IDEAL_HELIX", "BEADS"):
1587  c.pdb_file = pdb_input
1588  else:
1589  c.pdb_file = os.path.join(self.pdb_dir, pdb_input)
1590 
1591  # PDB chain must be one or two characters
1592  if len(tmp_chain) == 1 or len(tmp_chain) == 2:
1593  c.chain = tmp_chain
1594  else:
1595  errors.append(
1596  "PDB Chain identifier must be one or two characters.")
1597  errors.append("For component %s line %d is not correct"
1598  "|%s| was given."
1599  % (c.molname, linenum, tmp_chain))
1600 
1601  # Optional fields
1602  # Residue Range
1603  if rr.strip() == 'all' or str(rr) == "":
1604  c.residue_range = None
1605  elif (len(rr.split(',')) == 2 and self._is_int(rr.split(',')[0]) and
1606  (self._is_int(rr.split(',')[1]) or rr.split(',')[1] == 'END')):
1607  # Make sure that is residue range is given, there are only
1608  # two values and they are integers
1609  c.residue_range = (int(rr.split(',')[0]), rr.split(',')[1])
1610  if c.residue_range[1] != 'END':
1611  c.residue_range = (c.residue_range[0], int(c.residue_range[1]))
1612  # Old format used -1 for the last residue
1613  if old_format and c.residue_range[1] == -1:
1614  c.residue_range = (c.residue_range[0], 'END')
1615  else:
1616  errors.append("Residue Range format for component %s line %d is "
1617  "not correct" % (c.molname, linenum))
1618  errors.append(
1619  "Correct syntax is two comma separated integers: "
1620  "|start_res, end_res|. end_res can also be END to select the "
1621  "last residue in the chain. |%s| was given." % rr)
1622  errors.append("To select all residues, indicate |\"all\"|")
1623 
1624  # PDB Offset
1625  if self._is_int(offset):
1626  c.pdb_offset = int(offset)
1627  elif len(offset) == 0:
1628  c.pdb_offset = 0
1629  else:
1630  errors.append("PDB Offset format for component %s line %d is "
1631  "not correct" % (c.molname, linenum))
1632  errors.append("The value must be a single integer. |%s| was given."
1633  % offset)
1634 
1635  # Bead Size
1636  if self._is_int(bead_size):
1637  c.bead_size = int(bead_size)
1638  elif len(bead_size) == 0:
1639  c.bead_size = 0
1640  else:
1641  errors.append("Bead Size format for component %s line %d is "
1642  "not correct" % (c.molname, linenum))
1643  errors.append("The value must be a single integer. |%s| was given."
1644  % bead_size)
1645 
1646  # EM Residues Per Gaussian
1647  if self._is_int(emg):
1648  if int(emg) > 0:
1649  c.density_prefix = os.path.join(self.gmm_dir,
1650  c.get_unique_name())
1651  c.gmm_file = c.density_prefix + '.txt'
1652  c.mrc_file = c.density_prefix + '.gmm'
1653 
1654  c.em_residues_per_gaussian = int(emg)
1655  else:
1656  c.em_residues_per_gaussian = 0
1657  elif len(emg) == 0:
1658  c.em_residues_per_gaussian = 0
1659  else:
1660  errors.append("em_residues_per_gaussian format for component "
1661  "%s line %d is not correct" % (c.molname, linenum))
1662  errors.append("The value must be a single integer. |%s| was given."
1663  % emg)
1664 
1665  # rigid bodies
1666  if len(rbs) > 0:
1667  if not self._is_int(rbs):
1668  errors.append(
1669  "rigid bodies format for component "
1670  "%s line %d is not correct" % (c.molname, linenum))
1671  errors.append("Each RB must be a single integer, or empty. "
1672  "|%s| was given." % rbs)
1673  c.rigid_body = int(rbs)
1674 
1675  # super rigid bodies
1676  if len(srbs) > 0:
1677  srbs = srbs.split(',')
1678  for i in srbs:
1679  if not self._is_int(i):
1680  errors.append(
1681  "super rigid bodies format for component "
1682  "%s line %d is not correct" % (c.molname, linenum))
1683  errors.append(
1684  "Each SRB must be a single integer. |%s| was given."
1685  % srbs)
1686  c.super_rigid_bodies = srbs
1687 
1688  # chain of super rigid bodies
1689  if len(csrbs) > 0:
1690  if not self._is_int(csrbs):
1691  errors.append(
1692  "em_residues_per_gaussian format for component "
1693  "%s line %d is not correct" % (c.molname, linenum))
1694  errors.append(
1695  "Each CSRB must be a single integer. |%s| was given."
1696  % csrbs)
1697  c.chain_of_super_rigid_bodies = csrbs
1698 
1699  # done
1700  if errors:
1701  raise ValueError("Fix Topology File syntax errors and rerun: "
1702  + "\n".join(errors))
1703  else:
1704  return c
1705 
1706  def set_gmm_dir(self, gmm_dir):
1707  """Change the GMM dir"""
1708  self.gmm_dir = gmm_dir
1709  for c in self._components:
1710  c.gmm_file = os.path.join(self.gmm_dir,
1711  c.get_unique_name() + ".txt")
1712  c.mrc_file = os.path.join(self.gmm_dir,
1713  c.get_unique_name() + ".mrc")
1714  print('new gmm', c.gmm_file)
1715 
1716  def set_pdb_dir(self, pdb_dir):
1717  """Change the PDB dir"""
1718  self.pdb_dir = pdb_dir
1719  for c in self._components:
1720  if c._orig_pdb_input not in ("", "None", "IDEAL_HELIX", "BEADS"):
1721  c.pdb_file = os.path.join(self.pdb_dir, c._orig_pdb_input)
1722 
1723  def set_fasta_dir(self, fasta_dir):
1724  """Change the FASTA dir"""
1725  self.fasta_dir = fasta_dir
1726  for c in self._components:
1727  c.fasta_file = os.path.join(self.fasta_dir, c._orig_fasta_file)
1728 
1729  def _is_int(self, s):
1730  # is this string an integer?
1731  try:
1732  float(s)
1733  return float(s).is_integer()
1734  except ValueError:
1735  return False
1736 
1737  def get_rigid_bodies(self):
1738  """Return list of lists of rigid bodies (as domain name)"""
1739  rbl = defaultdict(list)
1740  for c in self._components:
1741  if c.rigid_body:
1742  rbl[c.rigid_body].append(c.get_unique_name())
1743  return rbl.values()
1744 
1746  """Return list of lists of super rigid bodies (as domain name)"""
1747  rbl = defaultdict(list)
1748  for c in self._components:
1749  for rbnum in c.super_rigid_bodies:
1750  rbl[rbnum].append(c.get_unique_name())
1751  return rbl.values()
1752 
1754  "Return list of lists of chains of super rigid bodies (as domain name)"
1755  rbl = defaultdict(list)
1756  for c in self._components:
1757  for rbnum in c.chain_of_super_rigid_bodies:
1758  rbl[rbnum].append(c.get_unique_name())
1759  return rbl.values()
1760 
1761 
1762 class _TempMolecule:
1763  """Store the Components and any requests for copies"""
1764  def __init__(self, init_c):
1765  self.molname = init_c.molname
1766  self.domains = IMP.pmi.tools.OrderedDefaultDict(list)
1767  self.add_component(init_c, init_c.copyname)
1768  self.orig_copyname = init_c.copyname
1769  self.orig_component = self.domains[init_c.copyname][0]
1770 
1771  def add_component(self, component, copy_id):
1772  self.domains[copy_id].append(component)
1773  component.domainnum = len(self.domains[copy_id])-1
1774 
1775  def __repr__(self):
1776  return ','.join('%s:%i'
1777  % (k, len(self.domains[k])) for k in self.domains)
1778 
1779 
1780 class _Component:
1781  """Stores the components required to build a standard IMP hierarchy
1782  using IMP.pmi.BuildModel()
1783  """
1784  def __init__(self):
1785  self.molname = None
1786  self.copyname = None
1787  self.domainnum = 0
1788  self.fasta_file = None
1789  self._orig_fasta_file = None
1790  self.fasta_id = None
1791  self.fasta_flag = None
1792  self.pdb_file = None
1793  self._orig_pdb_input = None
1794  self.chain = None
1795  self.residue_range = None
1796  self.pdb_offset = 0
1797  self.bead_size = 10
1798  self.em_residues_per_gaussian = 0
1799  self.gmm_file = ''
1800  self.mrc_file = ''
1801  self.density_prefix = ''
1802  self.color = 0.1
1803  self.rigid_body = None
1804  self.super_rigid_bodies = []
1805  self.chain_of_super_rigid_bodies = []
1806 
1807  def _l2s(self, rng):
1808  return ",".join("%s" % x for x in rng)
1809 
1810  def __repr__(self):
1811  return self.get_str()
1812 
1813  def get_unique_name(self):
1814  return "%s.%s.%i" % (self.molname, self.copyname, self.domainnum)
1815 
1816  def get_str(self):
1817  res_range = self.residue_range
1818  if self.residue_range is None:
1819  res_range = []
1820  name = self.molname
1821  if self.copyname != '':
1822  name += '.' + self.copyname
1823  if self.chain is None:
1824  chain = ' '
1825  else:
1826  chain = self.chain
1827  color = self.color
1828  if isinstance(color, list):
1829  color = ','.join([str(x) for x in color])
1830  fastaid = self.fasta_id
1831  if self.fasta_flag:
1832  fastaid += "," + self.fasta_flag
1833  a = '|' + '|'.join([name, color, self._orig_fasta_file, fastaid,
1834  self._orig_pdb_input, chain,
1835  self._l2s(list(res_range)),
1836  str(self.pdb_offset),
1837  str(self.bead_size),
1838  str(self.em_residues_per_gaussian),
1839  str(self.rigid_body) if self.rigid_body else '',
1840  self._l2s(self.super_rigid_bodies),
1841  self._l2s(self.chain_of_super_rigid_bodies)]) + '|'
1842  return a
1843 
1844 
1846  '''Extends the functionality of IMP.atom.Molecule'''
1847 
1848  def __init__(self, hierarchy):
1849  super().__init__(hierarchy)
1850 
1851  def get_state_index(self):
1852  state = self.get_parent()
1853  return IMP.atom.State(state).get_state_index()
1854 
1855  def get_copy_index(self):
1856  return IMP.atom.Copy(self).get_copy_index()
1857 
1858  def get_extended_name(self):
1859  return self.get_name() + "." + \
1860  str(self.get_copy_index()) + \
1861  "." + str(self.get_state_index())
1862 
1863  def get_sequence(self):
1864  return IMP.atom.Chain(self).get_sequence()
1865 
1866  def get_residue_indexes(self):
1868 
1869  def get_residue_segments(self):
1870  return IMP.pmi.tools.Segments(self.get_residue_indexes())
1871 
1872  def get_chain_id(self):
1873  return IMP.atom.Chain(self).get_id()
1874 
1875  def __repr__(self):
1876  s = 'PMIMoleculeHierarchy '
1877  s += self.get_name()
1878  s += " " + "Copy " + str(IMP.atom.Copy(self).get_copy_index())
1879  s += " " + "State " + str(self.get_state_index())
1880  s += " " + "N residues " + str(len(self.get_sequence()))
1881  return s
def build
Build all molecules (automatically makes clones)
Add mass to a particle.
Definition: Mass.h:23
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
Hierarchy get_parent() const
Get the parent particle.
def get_atomic_residues
Return a set of TempResidues that have associated structure coordinates.
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 get_residues
Return all modeled TempResidues as a set.
std::string get_unique_name(std::string templ)
Return a unique name produced from the string.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Definition: atom/Atom.h:245
static Atom setup_particle(Model *m, ParticleIndex pi, Atom other)
Definition: atom/Atom.h:246
def build
Build all states.
def __init__
Read a FASTA file and extract all the requested sequences.
static XYZR setup_particle(Model *m, ParticleIndex pi)
Definition: XYZR.h:48
def __init__
Read a PDB file and return all sequences for each contiguous fragment.
def get_states
Get a list of all State objects in this system.
def fasta_pdb_alignments
This function computes and prints the alignment between the fasta file and the pdb sequence...
def get_ideal_helices
Returns list of OrderedSets with requested ideal helices.
Miscellaneous utilities.
Definition: pmi/tools.py:1
def get_number_of_copies
Get the number of copies of the given molecule (by name)
def get_chains_of_super_rigid_bodies
Return list of lists of chains of super rigid bodies (as domain name)
def __init__
The user should not call this directly; instead call State.create_molecule()
void handle_use_deprecated(std::string message)
Break in this method in gdb to find deprecated uses at runtime.
def residue_range
Get residue range from a to b, inclusive.
def get_molecule
Access a molecule by name and copy number.
def __init__
Define a new state.
def add_representation
Set the representation for some residues.
static State setup_particle(Model *m, ParticleIndex pi, unsigned int index)
Definition: State.h:39
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.
def create_molecule
Create a new Molecule within this State.
def build
Create all parts of the IMP hierarchy including Atoms, Residues, and Fragments/Representations and...
static Residue setup_particle(Model *m, ParticleIndex pi, ResidueType t, int index, int insertion_code)
Definition: Residue.h:160
char get_one_letter_code(ResidueType c)
Get the 1-letter amino acid code from the residue type.
def get_non_atomic_residues
Return a set of TempResidues that don't have associated structure coordinates.
Represent the root node of the global IMP.atom.Hierarchy.
def get_name
Return this Molecule name.
def get_hierarchy
Return the IMP Hierarchy corresponding to this Molecule.
def get_components
Return list of ComponentTopologies for selected components.
def get_hierarchy
Get the IMP.atom.Hierarchy node for this state.
Class for storing model, its restraints, constraints, and particles.
Definition: Model.h:86
Stores a named protein chain.
Warning related to handling of structures.
static bool get_is_setup(Model *m, ParticleIndex pi)
Definition: Fragment.h:46
A decorator for keeping track of copies of a molecule.
Definition: Copy.h:28
Select all non-alternative ATOM records.
Definition: pdb.h:128
static Hierarchy setup_particle(Model *m, ParticleIndex pi, ParticleIndexesAdaptor children=ParticleIndexesAdaptor())
Create a Hierarchy of level t by adding the needed attributes.
def set_fasta_dir
Change the FASTA dir.
def get_hierarchy
Return the top-level IMP.atom.Hierarchy node for this system.
The standard decorator for manipulating molecular structures.
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
Data structure for reading and storing sequence data from PDBs.
A decorator for a particle representing an atom.
Definition: atom/Atom.h:238
std::string get_relative_path(std::string base, std::string relative)
Return a path to a file relative to another file.
A decorator for a particle with x,y,z coordinates.
Definition: XYZ.h:30
def create_clone
Create a Molecule clone (automatically builds same structure and representation)
def add_structure
Read a structure and store the coordinates.
int get_state_index(Hierarchy h)
Walk up the hierarchy to find the current state.
def add_protocol_output
Capture details of the modeling protocol.
def get_molecules
Return a dictionary where key is molecule name and value is a list of all copies of that molecule in ...
static Copy setup_particle(Model *m, ParticleIndex pi, Int number)
Create a decorator for the numberth copy.
Definition: Copy.h:42
def read
Read system components from topology file.
def get_state
Return the State containing this Molecule.
A decorator for a residue.
Definition: Residue.h:137
General purpose algebraic and geometric methods that are expected to be used by a wide variety of IMP...
Automatically setup System and Degrees of Freedom with a formatted text file.
The general base class for IMP exceptions.
Definition: exception.h:48
def get_rigid_bodies
Return list of lists of rigid bodies (as domain name)
Associate an integer "state" index with a hierarchy node.
Definition: State.h:27
Residue get_residue(Atom d, bool nothrow=false)
Return the Residue containing this atom.
Mapping between FASTA one-letter codes and residue types.
Definition: alphabets.py:1
Class to handle individual particles of a Model object.
Definition: Particle.h:45
Stores a list of Molecules all with the same State index.
def get_represented
Return set of TempResidues that have representation.
Store info for a chain of a protein.
Definition: Chain.h:61
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.
A dictionary-like wrapper for reading and storing sequence data.
def create_copy
Create a new Molecule with the same name and sequence but a higher copy number.
Functionality for loading, creating, manipulating and scoring atomic structures.
std::string get_chain_id(Hierarchy h)
Walk up the hierarchy to determine the chain id.
def get_particles_at_all_resolutions
Helpful utility for getting particles at all resolutions from this molecule.
static Chain setup_particle(Model *m, ParticleIndex pi, std::string id)
Definition: Chain.h:84
A decorator for a molecule.
Definition: Molecule.h:24
Select hierarchy particles identified by the biological name.
Definition: Selection.h:70
def get_number_of_states
Returns the total number of states generated.
def get_super_rigid_bodies
Return list of lists of super rigid bodies (as domain name)
def get_residue_indexes
Retrieve the residue indexes for the given particle.
Definition: pmi/tools.py:504
Warning for probably incorrect input parameters.
Temporarily stores residue information, even without structure available.
def create_state
Makes and returns a new IMP.pmi.topology.State in this system.
Store objects in order they were added, but with default type.
Definition: pmi/tools.py:824