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
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
29 * Check your representation with a nice printout:
30 IMP::atom::show_with_representation()
32 See a [comprehensive example](https://integrativemodeling.org/nightly/doc/ref/pmi_2multiscale_8py-example.html) for using these classes.
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).
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
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
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:
75 "Passed non-contiguous segment to "
76 "build_ideal_helix for %s" % tempres.get_molecule())
81 rp.set_name(
"Residue_%i" % tempres.get_index())
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))
100 tempres.set_structure(this_res)
101 created_hiers.append(this_res)
102 prev_idx = tempres.get_index()
104 coord_finder.add_residues(created_hiers)
108 """The base class for System, State and Molecule
109 classes. It contains shared functions in common to these classes
112 def __init__(self, model=None):
118 def _create_hierarchy(self):
119 """create a new hierarchy"""
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
131 """Build the coordinates of the system.
132 Loop through stored(?) hierarchies and set up coordinates!"""
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."""
141 def __init__(self, system):
142 self._ref = weakref.ref(system)
145 if hasattr(self,
'_ref'):
148 def __getstate__(self):
153 """Represent the root node of the global IMP.atom.Hierarchy."""
155 _all_systems = weakref.WeakSet()
160 @param model The IMP::Model in which to construct this system.
161 @param name The name of the top-level hierarchy node.
166 self._number_of_states = 0
167 self._protocol_output = []
171 System._all_systems.add(self)
174 self.hier = self._create_hierarchy()
175 self.hier.set_name(name)
176 self.hier._pmi2_system = _OurWeakRef(self)
179 """Get a list of all State objects in this system"""
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)
190 return self.hier.get_name()
193 """Returns the total number of states generated"""
194 return self._number_of_states
197 """Return the top-level IMP.atom.Hierarchy node for this system"""
201 """Build all states"""
203 for state
in self.states:
204 state.build(**kwargs)
206 for po
in self._protocol_output:
209 del self._start_model
213 """Capture details of the modeling protocol.
214 @param p an instance of IMP.pmi.output.ProtocolOutput or a subclass.
216 self._protocol_output.append(p)
219 for state
in self.states:
220 state._add_protocol_output(p, self)
224 """Stores a list of Molecules all with the same State index.
225 Also stores number of copies of each Molecule for easy selection.
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()
234 self.model = system.get_hierarchy().get_model()
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)
240 self.molecules = IMP.pmi.tools.OrderedDict()
243 self._protocol_output = []
244 for p
in system._protocol_output:
245 self._add_protocol_output(p, system)
247 _start_model = property(
lambda self: self.system._start_model)
250 return self.system.__repr__()+
'.'+self.hier.get_name()
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
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
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
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]
274 return self.molecules[name][copy_num]
277 alphabet=IMP.pmi.alphabets.amino_acid,
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
288 if name
in self.molecules:
289 raise ValueError(
'Cannot use a molecule name already used')
292 if re.search(
r'\.\d+$', name):
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 "
301 mol =
Molecule(self, name, sequence, chain_id, copy_num=0,
302 alphabet=alphabet, uniprot=uniprot)
303 self.molecules[name] = [mol]
307 """Get the IMP.atom.Hierarchy node for this state"""
311 """Get the number of copies of the given molecule (by name)
313 @param molname The name of the molecule
315 return len(self.molecules[molname])
317 def _register_copy(self, molecule):
318 molname = molecule.get_hierarchy().get_name()
319 self.molecules[molname].append(molecule)
322 """Build all molecules (automatically makes clones)"""
324 for molname
in self.molecules:
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()
339 _PDBElement = namedtuple(
'PDBElement', [
'offset',
'filename',
'chain_id'])
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):
348 self.last_index =
None
349 self.last_pdb_index =
None
350 self.pdb_for_residue = {}
351 for residues, pdb
in pdb_elements:
353 self.pdb_for_residue[r.get_index()] = pdb
355 def _get_pdb(self, h):
356 """Return a PDBElement if the given hierarchy was read from a
360 return self.pdb_for_residue.get(rind,
None)
362 def __call__(self, res):
363 """Handle a single residue"""
364 if len(self.pos) == 0:
367 pi = h.get_particle_index()
369 if self.last_index
is None or pi != self.last_index:
370 pdb = self._get_pdb(h)
375 fragi = frag.get_particle_index()
377 if self.last_pdb_index
is not None \
378 and self.last_pdb_index == fragi:
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)
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)
394 for p, state
in self.pos:
395 p.add_bead_element(state, self.name, resind, resind, 1, h)
397 raise TypeError(
"Unhandled hierarchy %s" % str(h))
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.
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).
412 def __init__(self, state, name, sequence, chain_id, copy_num,
413 mol_to_clone=
None, alphabet=IMP.pmi.alphabets.amino_acid,
415 """The user should not call this directly; instead call
416 State.create_molecule()
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()
428 self.model = state.get_hierarchy().get_model()
430 self.sequence = sequence
432 self.mol_to_clone = mol_to_clone
433 self.alphabet = alphabet
434 self.representations = []
435 self._pdb_elements = []
436 self.uniprot = uniprot
438 self._represented = IMP.pmi.tools.OrderedSet()
440 self.coord_finder = _FindCloseStructure()
442 self._ideal_helices = []
445 self.hier = self._create_child(self.state.get_hierarchy())
446 self.hier.set_name(name)
448 self._name_with_copy =
"%s.%d" % (name, copy_num)
451 self.chain.set_sequence(self.sequence)
452 self.chain.set_chain_type(alphabet.get_chain_type())
454 self.chain.set_uniprot_accession(self.uniprot)
457 for ns, s
in enumerate(sequence):
459 self.residues.append(r)
461 _start_model = property(
lambda self: self.state._start_model)
464 return self.state.__repr__() +
'.' + self.
get_name() +
'.' + \
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])
475 raise TypeError(
"Indexes must be int or str")
478 """Return the IMP Hierarchy corresponding to this Molecule"""
482 """Return this Molecule name"""
483 return self.hier.get_name()
486 """Return the State containing this Molecule"""
490 """Returns list of OrderedSets with requested ideal helices"""
491 return self._ideal_helices
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])
504 raise TypeError(
"Range ends must be int or str. "
505 "Stride must be int.")
508 """Return all modeled TempResidues as a set"""
509 all_res = IMP.pmi.tools.OrderedSet(self.residues)
513 """Return set of TempResidues that have representation"""
514 return self._represented
517 """Return a set of TempResidues that have associated structure
519 atomic_res = IMP.pmi.tools.OrderedSet()
520 for res
in self.residues:
521 if res.get_has_structure():
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
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!
539 @param chain_id Chain ID of the new 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)
548 """Create a Molecule clone (automatically builds same structure
551 @param chain_id If you want to set the chain ID of the copy
553 @note You cannot add structure or representations to a clone!
556 self.state, self.
get_name(), self.sequence, chain_id,
557 copy_num=self.state.get_number_of_copies(self.
get_name()),
559 self.state._register_copy(mol)
563 offset=0, model_num=
None, ca_only=
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
590 if self.mol_to_clone
is not None:
591 raise ValueError(
'You cannot call add_structure() for a clone')
596 rhs = system_tools.get_structure(self._start_model, pdb_fn, chain_id,
599 self.coord_finder.add_residues(rhs)
601 if len(self.residues) == 0:
603 "Substituting PDB residue type with FASTA residue type. "
607 self._pdb_elements.append(
608 (rhs, _PDBElement(offset=offset, filename=pdb_fn,
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
619 while len(self.residues) < pdb_idx:
622 IMP.pmi.alphabets.amino_acid)
623 self.residues.append(r)
626 internal_res = self.residues[raw_idx]
627 if len(self.sequence) < raw_idx:
629 rh.get_residue_type())
630 internal_res.set_structure(rh, soft_check)
631 atomic_res.add(internal_res)
633 self.chain.set_sequence(self.sequence)
639 bead_extra_breaks=[],
640 bead_ca_centers=
True,
641 bead_default_coord=[0, 0, 0],
642 density_residues_per_component=
None,
644 density_force_compute=
False,
645 density_voxel_size=1.0,
646 setup_particles_as_densities=
False,
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
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
675 @param density_prefix Prefix (assuming '.txt') to read components
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
704 if self.mol_to_clone
is not None:
706 'You cannot call add_representation() for a clone.'
707 ' Maybe use a copy instead.')
711 res = IMP.pmi.tools.OrderedSet(self.residues)
712 elif residues == self:
713 res = IMP.pmi.tools.OrderedSet(self.residues)
715 res = IMP.pmi.tools.OrderedSet([residues])
716 elif hasattr(residues,
'__iter__'):
717 if len(residues) == 0:
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:
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)
729 raise Exception(
"You passed an iterable of something other "
730 "than TempResidue", res)
732 raise Exception(
"add_representation: you must pass a set of "
733 "residues or nothing(=all residues)")
736 ov = res & self._represented
738 raise Exception(
'You have already added representation for ' +
741 self._represented |= res
744 if not hasattr(resolutions,
'__iter__'):
745 if type(resolutions)
is int:
746 resolutions = [resolutions]
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:
752 if not r.get_has_structure():
754 'You are creating multiple resolutions for '
755 'unstructured regions. This will have unexpected '
759 if density_residues_per_component
or density_prefix:
760 if not density_residues_per_component
and density_prefix:
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:
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:
772 'You have multiple bead resolutions but are attempting to '
773 'set them all up as individual Densities. '
774 'This could have unexpected results.')
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)
789 if r.get_molecule() != self:
791 'You are adding residues from a different molecule to',
796 for b
in bead_extra_breaks:
797 if isinstance(b, str):
798 breaks.append(int(b)-1)
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))
808 def _all_protocol_output(self):
809 return self.state._protocol_output
811 def _build_protocol_output(self):
812 """Add molecule name and sequence to any ProtocolOutput objects"""
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)
823 def _finalize_build(self):
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:
834 def build(self, protocol_output=True):
835 """Create all parts of the IMP hierarchy
836 including Atoms, Residues, and Fragments/Representations and,
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.
845 self._build_protocol_output()
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(
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
873 no_rep = [r
for r
in self.residues
if r
not in self._represented]
876 'Residues without representation in molecule %s: %s'
877 % (self.
get_name(), system_tools.resnums2str(no_rep)),
882 for rep
in self.representations:
884 _build_ideal_helix(self._start_model, rep.residues,
890 rephandler = _RepresentationHandler(
891 self._name_with_copy, list(self._all_protocol_output()),
894 for rep
in self.representations:
895 built_reps += system_tools.build_representation(
896 self, rep, self.coord_finder, rephandler)
901 for br
in built_reps:
902 self.hier.add_child(br)
906 for res
in self.residues:
911 residue_index=res.get_index(),
912 resolution=1).get_selected_particles()
925 if self.mol_to_clone
is None:
929 self._represented = IMP.pmi.tools.OrderedSet(
930 [a
for a
in self._represented])
935 """Helpful utility for getting particles at all resolutions from
936 this molecule. Can optionally pass a set of residue indexes"""
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()]
947 class _Representation:
948 """Private class just to store a representation request"""
955 density_residues_per_component,
957 density_force_compute,
959 setup_particles_as_densities,
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
976 class _FindCloseStructure:
977 """Utility to get the nearest observed coordinate"""
981 def add_residues(self, residues):
984 catypes = [IMP.atom.AT_CA, system_tools._AT_HET_CA]
986 r, atom_types=catypes).get_selected_particles()
991 self.coords.append([idx, xyz])
994 self.coords.append([idx, xyz])
996 raise ValueError(
"_FindCloseStructure: wrong selection")
998 self.coords.sort(key=itemgetter(0))
1000 def find_nearest_coord(self, query):
1001 if self.coords == []:
1003 keys = [r[0]
for r
in self.coords]
1004 pos = bisect_left(keys, query)
1006 ret = self.coords[0]
1007 elif pos == len(self.coords):
1008 ret = self.coords[-1]
1010 before = self.coords[pos - 1]
1011 after = self.coords[pos]
1012 if after[0] - query < query - before[0]:
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
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".
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
1036 self.sequences = IMP.pmi.tools.OrderedDict()
1039 self.read_sequences(fasta_fn, name_map)
1042 return len(self.sequences)
1044 def __contains__(self, x):
1045 return x
in self.sequences
1047 def __getitem__(self, key):
1048 if type(key)
is int:
1049 allseqs = list(self.sequences.keys())
1051 return self.sequences[allseqs[key]]
1053 raise IndexError(
"You tried to access sequence number %d "
1054 "but there's only %d" % (key, len(allseqs)))
1056 return self.sequences[key]
1059 return self.sequences.__iter__()
1063 for s
in self.sequences:
1064 ret +=
'%s\t%s\n' % (s, self.sequences[s])
1067 def read_sequences(self, fasta_fn, name_map=None):
1070 with open(fasta_fn,
'r') as fh:
1071 for (num, line)
in enumerate(fh):
1072 if line.startswith(
'>'):
1074 self.sequences[code] = seq.strip(
'*')
1075 spl = line[1:].split(
'|')
1076 code = spl[0].strip()
1077 if name_map
is not None:
1079 code = name_map[code]
1084 up_accession = spl[1].strip()
1085 self.uniprot[code] = up_accession
1087 line = line.rstrip()
1091 "Found FASTA sequence before first header "
1092 "at line %d: %s" % (num + 1, line))
1095 self.sequences[code] = seq.strip(
'*')
1099 """Data structure for reading and storing sequence data from PDBs.
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
1106 @param name_map dictionary mapping the pdb chain id to final
1119 self.sequences = IMP.pmi.tools.OrderedDict()
1120 self.read_sequences(pdb_fn, name_map)
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
1127 cs = IMP.atom.get_by_type(t, IMP.atom.CHAIN_TYPE)
1135 print(
"Chain ID %s not in name_map, skipping" % id)
1137 rs = IMP.atom.get_by_type(c, IMP.atom.RESIDUE_TYPE)
1142 rid = dr.get_index()
1144 isprotein = dr.get_is_protein()
1145 isrna = dr.get_is_rna()
1146 isdna = dr.get_is_dna()
1150 rids_olc_dict[rid] = olc
1152 if dr.get_residue_type() == IMP.atom.DADE:
1154 if dr.get_residue_type() == IMP.atom.DURA:
1156 if dr.get_residue_type() == IMP.atom.DCYT:
1158 if dr.get_residue_type() == IMP.atom.DGUA:
1160 if dr.get_residue_type() == IMP.atom.DTHY:
1163 rids_olc_dict[rid] = olc
1165 if dr.get_residue_type() == IMP.atom.ADE:
1167 if dr.get_residue_type() == IMP.atom.URA:
1169 if dr.get_residue_type() == IMP.atom.CYT:
1171 if dr.get_residue_type() == IMP.atom.GUA:
1173 if dr.get_residue_type() == IMP.atom.THY:
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
1186 def group_indexes(self, indexes):
1187 from itertools
import groupby
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]))
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
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
1210 raise Exception(
"Fasta sequences not type IMP.pmi.topology.Sequences")
1212 raise Exception(
"pdb sequences not type IMP.pmi.topology.PDBSequences")
1213 offsets = IMP.pmi.tools.OrderedDict()
1214 for name
in fasta_sequences.sequences:
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 "
1220 pdbnames = pdb_sequences.sequences.keys()
1223 for pdbname
in pdbnames:
1224 for group
in pdb_sequences.sequences[pdbname]:
1225 if group[1] - group[0] + 1 < 7:
1227 seq_frag_pdb = pdb_sequences.sequences[pdbname][group]
1229 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,
1237 offset = a[3] + 1 - group[0]
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
1248 if group
not in offsets[pdbname]:
1249 offsets[pdbname][group] = offset
1254 "Temporarily stores residue information, even without structure available."
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
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
1269 self.state_index = \
1272 self._structured =
False
1274 IMP.Particle(molecule._start_model), self.rtype, index)
1277 return str(self.state_index) +
"_" + self.molecule.get_name() +
"_" \
1278 + str(self.copy_index) +
"_" + self.get_code() \
1279 + str(self.get_index())
1282 return self.__str__()
1286 return (self.state_index, self.molecule, self.copy_index, self.rtype,
1287 self.pdb_index, self.internal_index)
1289 def __eq__(self, other):
1290 return (type(other) == type(self)
1291 and self.__key() == other.__key())
1294 return hash(self.__key())
1297 return self.pdb_index
1299 def get_internal_index(self):
1300 return self.internal_index
1305 def get_residue_type(self):
1308 def get_hierarchy(self, model=None):
1309 if model
is not None and self.hier.get_model() != model:
1311 return IMP.atom.create_clone(self.hier, model)
1315 def get_molecule(self):
1316 return self.molecule
1318 def get_has_structure(self):
1319 return self._structured
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):
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()
1341 'ERROR: PDB residue index', self.get_index(),
'is',
1343 'and sequence residue is', self.get_code())
1345 for a
in res.get_children():
1346 self.hier.add_child(a)
1348 a.get_particle().set_name(
1349 'Atom %s of residue %i' % (atype.__str__().strip(
'"'),
1350 self.hier.get_index()))
1351 self._structured =
True
1355 """Automatically setup System and Degrees of Freedom with a formatted
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:
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||
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
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,
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
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
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.
1425 def __init__(self, topology_file, pdb_dir='./', fasta_dir='./',
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
1433 self.topology_file = topology_file
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)
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'
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
1459 for i
in topology_list:
1460 topologies.append(self._components[i])
1463 def get_molecules(self):
1464 return self.molecules
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
1471 is_directories =
False
1474 self._components = []
1476 with open(topology_file)
as infile:
1478 if line.lstrip() ==
"" or line[0] ==
"#":
1480 elif line.split(
'|')[1].strip()
in (
"molecule_name"):
1482 is_directories =
False
1485 elif line.split(
'|')[1] ==
"component_name":
1488 "Old-style topology format (using "
1489 "|component_name|) is deprecated. Please switch to "
1490 "the new-style format (using |molecule_name|)\n")
1492 is_directories =
False
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],
1507 new_component = self._parse_line(line, linenum, old_format)
1508 self._components.append(new_component)
1510 return self._components
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
1519 values = [s.strip()
for s
in component_line.split(
'|')]
1524 c.molname = values[1]
1526 c._domain_name = values[2]
1529 names = values[1].split(
'.')
1531 c.molname = names[0]
1533 elif len(names) == 2:
1534 c.molname = names[0]
1535 c.copyname = names[1]
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]
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]
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]
1562 bead_size = values[9]
1565 rbs = srbs = csrbs =
''
1571 if c.molname
not in self.molecules:
1572 self.molecules[c.molname] = _TempMolecule(c)
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)
1581 c.fasta_file = os.path.join(self.fasta_dir, c._orig_fasta_file)
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
1589 c.pdb_file = os.path.join(self.pdb_dir, pdb_input)
1592 if len(tmp_chain) == 1
or len(tmp_chain) == 2:
1596 "PDB Chain identifier must be one or two characters.")
1597 errors.append(
"For component %s line %d is not correct"
1599 % (c.molname, linenum, tmp_chain))
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')):
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]))
1613 if old_format
and c.residue_range[1] == -1:
1614 c.residue_range = (c.residue_range[0],
'END')
1616 errors.append(
"Residue Range format for component %s line %d is "
1617 "not correct" % (c.molname, linenum))
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\"|")
1625 if self._is_int(offset):
1626 c.pdb_offset = int(offset)
1627 elif len(offset) == 0:
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."
1636 if self._is_int(bead_size):
1637 c.bead_size = int(bead_size)
1638 elif len(bead_size) == 0:
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."
1647 if self._is_int(emg):
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'
1654 c.em_residues_per_gaussian = int(emg)
1656 c.em_residues_per_gaussian = 0
1658 c.em_residues_per_gaussian = 0
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."
1667 if not self._is_int(rbs):
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)
1677 srbs = srbs.split(
',')
1679 if not self._is_int(i):
1681 "super rigid bodies format for component "
1682 "%s line %d is not correct" % (c.molname, linenum))
1684 "Each SRB must be a single integer. |%s| was given."
1686 c.super_rigid_bodies = srbs
1690 if not self._is_int(csrbs):
1692 "em_residues_per_gaussian format for component "
1693 "%s line %d is not correct" % (c.molname, linenum))
1695 "Each CSRB must be a single integer. |%s| was given."
1697 c.chain_of_super_rigid_bodies = csrbs
1701 raise ValueError(
"Fix Topology File syntax errors and rerun: "
1702 +
"\n".join(errors))
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)
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)
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)
1729 def _is_int(self, s):
1733 return float(s).is_integer()
1738 """Return list of lists of rigid bodies (as domain name)"""
1739 rbl = defaultdict(list)
1740 for c
in self._components:
1742 rbl[c.rigid_body].append(c.get_unique_name())
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())
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())
1762 class _TempMolecule:
1763 """Store the Components and any requests for copies"""
1765 self.molname = init_c.molname
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]
1771 def add_component(self, component, copy_id):
1772 self.domains[copy_id].append(component)
1773 component.domainnum = len(self.domains[copy_id])-1
1776 return ','.join(
'%s:%i'
1777 % (k, len(self.domains[k]))
for k
in self.domains)
1781 """Stores the components required to build a standard IMP hierarchy
1782 using IMP.pmi.BuildModel()
1786 self.copyname =
None
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
1795 self.residue_range =
None
1798 self.em_residues_per_gaussian = 0
1801 self.density_prefix =
''
1803 self.rigid_body =
None
1804 self.super_rigid_bodies = []
1805 self.chain_of_super_rigid_bodies = []
1807 def _l2s(self, rng):
1808 return ",".join(
"%s" % x
for x
in rng)
1811 return self.get_str()
1814 return "%s.%s.%i" % (self.molname, self.copyname, self.domainnum)
1817 res_range = self.residue_range
1818 if self.residue_range
is None:
1821 if self.copyname !=
'':
1822 name +=
'.' + self.copyname
1823 if self.chain
is None:
1828 if isinstance(color, list):
1829 color =
','.join([str(x)
for x
in color])
1830 fastaid = self.fasta_id
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)]) +
'|'
1846 '''Extends the functionality of IMP.atom.Molecule'''
1848 def __init__(self, hierarchy):
1849 super().__init__(hierarchy)
1858 def get_extended_name(self):
1859 return self.get_name() +
"." + \
1860 str(self.get_copy_index()) + \
1861 "." + str(self.get_state_index())
1863 def get_sequence(self):
1866 def get_residue_indexes(self):
1869 def get_residue_segments(self):
1876 s =
'PMIMoleculeHierarchy '
1877 s += self.get_name()
1879 s +=
" " +
"State " + str(self.get_state_index())
1880 s +=
" " +
"N residues " + str(len(self.get_sequence()))
def __init__
setup a TempResidue
def build
Build all molecules (automatically makes clones)
def set_pdb_dir
Change the PDB dir.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
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.
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)
static Atom setup_particle(Model *m, ParticleIndex pi, Atom other)
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)
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.
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 set_gmm_dir
Change the GMM dir.
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)
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)
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.
Stores a named protein chain.
Warning related to handling of structures.
static bool get_is_setup(Model *m, ParticleIndex pi)
A decorator for keeping track of copies of a molecule.
Select all non-alternative ATOM records.
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.
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.
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.
def read
Read system components from topology file.
def get_state
Return the State containing this Molecule.
A decorator for a residue.
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.
def get_rigid_bodies
Return list of lists of rigid bodies (as domain name)
Associate an integer "state" index with a hierarchy node.
Residue get_residue(Atom d, bool nothrow=false)
Return the Residue containing this atom.
Mapping between FASTA one-letter codes and residue types.
Class to handle individual particles of a Model object.
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.
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)
A decorator for a molecule.
Select hierarchy particles identified by the biological name.
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)
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.