1 """@namespace IMP.mmcif.data
2 @brief Classes to represent data structures used in mmCIF.
5 from __future__
import print_function
23 def _fill_imp_to_ihm():
24 d = dict(x
for x
in inspect.getmembers(
IMP.atom)
27 for comp
in ihm.LPeptideAlphabet._comps.values():
29 _imp_to_ihm[d[comp.id]] = comp
31 alpha = ihm.RNAAlphabet()
32 for code
in [
'ADE',
'CYT',
'GUA',
'URA']:
33 _imp_to_ihm[d[code]] = alpha[d[code].get_string()]
34 alpha = ihm.DNAAlphabet()
35 for code
in [
'DADE',
'DCYT',
'DGUA',
'DTHY']:
36 _imp_to_ihm[d[code]] = alpha[d[code].get_string()]
38 _imp_to_ihm[
None] =
None
45 """Given a Hierarchy, walk up and find the parent Molecule"""
53 def _check_sequential(fragment, resinds):
54 for i
in range(1, len(resinds)):
55 if resinds[i - 1] + 1 != resinds[i]:
57 "%s: non-sequential residue indices are not supported"
61 def _get_all_state_provenance(state_h, top_h, types):
62 """Yield all provenance information for the given state.
63 If the given State Hierarchy node contains no provenance information,
64 fall back to any provenance information for the top-level node
70 if count == 0
and top_h
is not None:
75 class _CustomDNAAlphabet(ihm.Alphabet):
76 """Custom DNA alphabet that maps A,C,G,T (rather than DA,DC,DG,DT
78 _comps = dict([cc.code_canonical, cc]
79 for cc
in ihm.DNAAlphabet._comps.values())
82 class _EntityMapper(dict):
83 """Handle mapping from IMP chains to CIF entities.
84 Multiple components may map to the same entity if they
86 def __init__(self, system):
88 super(_EntityMapper, self).__init__()
89 self._sequence_dict = {}
91 self._alphabet_map = {
92 IMP.atom.UnknownChainType: ihm.LPeptideAlphabet,
93 IMP.atom.Protein: ihm.LPeptideAlphabet,
94 IMP.atom.RNA: ihm.RNAAlphabet,
95 IMP.atom.DNA: _CustomDNAAlphabet}
97 def _get_sequence_from_residues(self, chain, seq_from_res):
98 seq_id_begin, seq = seq_from_res
100 raise ValueError(
"Chain %s has no sequence and no residues"
102 missing_seq = [ind + seq_id_begin
103 for (ind, res)
in enumerate(seq)
if res
is None]
106 "Chain %s has no declared sequence; tried to determine the "
107 "sequence from Residues, but the following residue indices "
108 "have no residue type (perhaps covered only by Fragments): %s"
109 % (chain, str(missing_seq)))
110 return seq_id_begin - 1, tuple(seq)
112 def add(self, chain, seq_from_res=None):
113 sequence = chain.get_sequence()
114 offset = chain.get_sequence_offset()
116 if seq_from_res
is not None:
117 offset, sequence = self._get_sequence_from_residues(
120 raise ValueError(
"Chain %s has no sequence" % chain)
123 alphabet = self._alphabet_map[chain.get_chain_type()]()
124 sequence = tuple(alphabet[x]
for x
in sequence)
125 if sequence
not in self._sequence_dict:
126 entity = ihm.Entity(sequence)
127 self.system.entities.append(entity)
128 self._entities.append(entity)
129 self._sequence_dict[sequence] = entity
130 uniprot = chain.get_uniprot_accession()
132 up = ihm.reference.UniProtSequence.from_accession(uniprot)
133 entity.references.append(up)
134 self[chain] = self._sequence_dict[sequence]
135 return self[chain], offset
138 """Yield all entities"""
139 return self._entities
142 def _assign_id(obj, seen_objs, obj_by_id):
143 """Assign a unique ID to obj, and track all ids in obj_by_id."""
144 if obj
not in seen_objs:
145 if not hasattr(obj,
'id'):
146 obj_by_id.append(obj)
147 obj.id = len(obj_by_id)
148 seen_objs[obj] = obj.id
150 obj.id = seen_objs[obj]
153 class _Component(object):
154 """An mmCIF component. This is an instance of an _Entity. Multiple
155 _Components may map to the same _Entity but must have unique
156 asym_ids. A _Component is similar to an IMP Chain but multiple
157 Chains may map to the same _Component (the Chains represent the
158 same structure, just in different states, and potentially in
159 different IMP Models). A _Component may also represent something
160 that is described by an experiment but was not modeled by IMP, and
161 so no Chains map to it but a string name does."""
162 def __init__(self, entity, asym_id, name):
163 self.entity, self.asym_id, self.name = entity, asym_id, name
166 class _ComponentMapper(object):
167 """Handle mapping from IMP Chains to CIF AsymUnits."""
168 def __init__(self, system):
169 super(_ComponentMapper, self).__init__()
171 self._used_entities = set()
172 self._all_components = []
175 def __getitem__(self, chain):
176 asym_id, map_key, name = self._handle_chain(chain)
177 return self._map[map_key]
179 def _handle_chain(self, chain):
181 asym_id = chain.get_id()
182 name = mol.get_name()
if mol
else None
185 map_key =
"name", name
187 map_key =
"asym_id", asym_id
188 return asym_id, map_key, name
190 def add(self, chain, entity, offset):
191 """Add a chain (an IMP Chain object)"""
192 asym_id, map_key, name = self._handle_chain(chain)
193 if map_key
not in self._map:
194 component = _Component(entity, asym_id, name)
195 if entity
not in self._used_entities:
196 self._used_entities.add(entity)
200 entity.description = \
201 component.name.split(
"@")[0].split(
".")[0]
202 self._all_components.append(component)
203 asym = ihm.AsymUnit(entity, name, id=asym_id,
204 auth_seq_id_map=offset)
205 self.system.asym_units.append(asym)
206 component.asym_unit = asym
207 self._map[map_key] = component
209 component = self._map[map_key]
210 if component.entity != entity:
211 raise ValueError(
"Two chains have the same ID (%s) but "
212 "different sequences - rename one of the "
213 "chains" % component.asym_unit.id)
214 if component.asym_unit.auth_seq_id_map != offset:
216 "Two chains have the same ID (%s) but different offsets "
217 "(%d, %d) - this is not supported"
218 % (component.asym_unit.id,
219 component.asym_unit.auth_seq_id_map, offset))
223 """Get all components"""
224 return self._all_components
227 class _RepSegmentFactory(object):
228 """Make ihm.representation.Segment objects for each set of contiguous
229 particles with the same representation"""
230 def __init__(self, asym):
233 self.offset = asym.auth_seq_id_map
235 self.imp_residue_range = ()
237 def add(self, particle, starting_model):
238 """Add a new particle to the last segment (and return None).
239 Iff the particle could not be added, return the segment and start
241 (resrange, rigid_body,
242 is_res, is_atom) = self._get_particle_info(particle)
244 def start_new_segment():
245 self.particles = [particle]
246 self.imp_residue_range = resrange
247 self.rigid_body = rigid_body
249 self.is_atom = is_atom
250 self.starting_model = starting_model
251 if not self.particles:
254 elif (type(particle) == type(self.particles[0])
255 and is_res == self.is_res
256 and is_atom == self.is_atom
257 and resrange[0] <= self.imp_residue_range[1] + 1
258 and starting_model == self.starting_model
259 and self._same_rigid_body(rigid_body)):
261 self.particles.append(particle)
262 self.imp_residue_range = (self.imp_residue_range[0], resrange[1])
265 seg = self.get_last()
270 """Return the last segment, or None"""
273 asym = self.asym(self.imp_residue_range[0] - self.offset,
274 self.imp_residue_range[1] - self.offset)
276 return ihm.representation.AtomicSegment(
277 asym_unit=asym, rigid=self.rigid_body
is not None,
278 starting_model=self.starting_model)
280 return ihm.representation.ResidueSegment(
282 rigid=self.rigid_body
is not None, primitive=
'sphere',
283 starting_model=self.starting_model)
285 return ihm.representation.FeatureSegment(
287 rigid=self.rigid_body
is not None, primitive=
'sphere',
288 count=len(self.particles),
289 starting_model=self.starting_model)
291 def _same_rigid_body(self, rigid_body):
294 if self.rigid_body
is None and rigid_body
is None:
296 elif self.rigid_body
is None or rigid_body
is None:
299 return self.rigid_body == rigid_body
301 def _get_particle_info(self, p):
308 return (p.get_index(), p.get_index()), rigid_body,
True,
False
311 return (res.get_index(), res.get_index()), rigid_body,
False,
True
313 resinds = p.get_residue_indexes()
314 return (resinds[0], resinds[-1]), rigid_body,
False,
False
315 raise TypeError(
"Unknown particle ", p)
318 def _get_all_structure_provenance(p):
319 """Yield all StructureProvenance decorators for the given particle."""
323 class _StartingModelAtomHandler(object):
324 def __init__(self, templates, asym):
326 self._last_res_index =
None
327 self.templates = templates
330 def _residue_first_atom(self, res):
331 """Return True iff we're looking at the first atom in this residue"""
333 ind = res.get_index()
334 if ind != self._last_res_index:
335 self._last_res_index = ind
338 def handle_residue(self, res, comp_id, seq_id, offset):
339 res_name = res.get_residue_type().get_string()
343 if res_name ==
'MSE' and comp_id ==
'MET':
344 if self._residue_first_atom(res):
349 assert len(self.templates) == 0
350 self._seq_dif.append(ihm.startmodel.MSESeqDif(
351 res.get_index(), seq_id))
352 elif res_name != comp_id:
353 if self._residue_first_atom(res):
354 print(
"WARNING: Starting model residue %s does not match "
355 "that in the output model (%s) for chain %s residue %d. "
356 "Check offset (currently %d)."
357 % (res_name, comp_id, self.asym._id, seq_id, offset))
358 self._seq_dif.append(ihm.startmodel.SeqDif(
359 db_seq_id=res.get_index(), seq_id=seq_id,
361 details=
"Mutation of %s to %s" % (res_name, comp_id)))
363 def get_ihm_atoms(self, particles, offset):
367 element = atom.get_element()
369 atom_name = atom.get_atom_type().get_string()
370 het = atom_name.startswith(
'HET:')
372 atom_name = atom_name[4:]
375 seq_id = res.get_index() + offset
376 comp_id = self.asym.entity.sequence[seq_id-1].id
377 self.handle_residue(res, comp_id, seq_id, offset)
378 yield ihm.model.Atom(asym_unit=self.asym, seq_id=seq_id,
379 atom_id=atom_name, type_symbol=element,
380 x=coord[0], y=coord[1], z=coord[2],
381 het=het, biso=atom.get_temperature_factor())
384 class _StartingModel(ihm.startmodel.StartingModel):
385 _eq_keys = [
'filename',
'asym_id',
'offset']
387 def __init__(self, asym_unit, struc_prov):
388 self.filename = struc_prov[0].get_filename()
389 super(_StartingModel, self).__init__(
390 asym_unit=asym_unit(0, 0),
394 offset=struc_prov[0].get_residue_offset())
396 def _add_residue(self, resind):
399 seq_id_begin = self.asym_unit.seq_id_range[0]
400 if seq_id_begin == 0:
401 seq_id_begin = seq_id_end
402 self.asym_unit = self.asym_unit.asym(seq_id_begin, seq_id_end)
409 return tuple([self.__class__]
410 + [getattr(self, x)
for x
in self._eq_keys])
412 def __eq__(self, other):
413 return other
is not None and self._eq_vals() == other._eq_vals()
416 return hash(self._eq_vals())
418 def _set_sources_datasets(self, system, datasets):
420 if (hasattr(ihm.metadata,
'CIFParser')
421 and self.filename.endswith(
'.cif')):
422 p = ihm.metadata.CIFParser()
424 p = ihm.metadata.PDBParser()
425 r = p.parse_file(self.filename)
426 system.software.extend(r.get(
'software', []))
427 dataset = datasets.add(r[
'dataset'])
429 templates = r.get(
'templates', {}).get(self.asym_id, [])
432 system.locations.append(t.alignment_file)
434 datasets.add(t.dataset)
435 self.dataset = dataset
436 self.templates = templates
437 self.metadata = r.get(
'metadata', [])
439 def _read_coords(self):
440 """Read the coordinates for this starting model"""
446 rng = self.asym_unit.seq_id_range
448 hier, residue_indexes=list(range(rng[0] - self.offset,
449 rng[1] + 1 - self.offset)))
452 def get_seq_dif(self):
456 mh = _StartingModelAtomHandler(self.templates, self.asym_unit)
457 m, sel = self._read_coords()
458 for a
in mh.get_ihm_atoms(sel.get_selected_particles(), self.offset):
460 self._seq_dif = mh._seq_dif
463 class _StartingModelFinder(object):
464 """Map IMP particles to starting model objects"""
465 def __init__(self, asym, existing_starting_models, system, datasets):
466 self._seen_particles = {}
468 self._seen_starting_models = {}
469 for sm
in existing_starting_models:
470 self._seen_starting_models[sm] = sm
471 self._system = system
472 self._datasets = datasets
474 def find(self, particle):
475 """Return a StartingModel object, or None, for this particle"""
476 def _get_starting_model(sp, resind):
477 s = _StartingModel(self._asym, sp)
478 if s
not in self._seen_starting_models:
479 self._seen_starting_models[s] = s
480 s._set_sources_datasets(self._system, self._datasets)
481 self._system.orphan_starting_models.append(s)
482 s = self._seen_starting_models[s]
484 s._add_residue(resind)
489 sp = list(_get_all_structure_provenance(particle))
491 return _get_starting_model(sp, resind)
499 pi = h.get_particle_index()
500 seen_parents.append(pi)
502 if pi
in self._seen_particles:
503 sp = self._seen_particles[pi]
504 if sp
and sp[0]
and resind
is not None:
505 sp[0]._add_residue(resind)
506 return sp[0]
if sp
else None
508 sp = list(_get_all_structure_provenance(h))
509 self._seen_particles[pi] = []
511 s = _get_starting_model(sp, resind)
514 for spi
in seen_parents:
515 self._seen_particles[spi].append(s)
520 class _Datasets(object):
521 """Store all datasets used."""
522 def __init__(self, system):
523 super(_Datasets, self).__init__()
529 """Add and return a new dataset."""
530 if d
not in self._datasets:
531 self._datasets[d] = d
532 self.system.orphan_datasets.append(d)
533 return self._datasets[d]
535 def add_group(self, datasets, name):
536 """Add and return a new group of datasets"""
540 for dataset
in datasets:
541 if dataset
not in seen:
545 if d
not in self._groups:
546 g = ihm.dataset.DatasetGroup(d, name=name)
548 self.system.orphan_dataset_groups.append(g)
549 return self._groups[d]
552 """Yield all datasets"""
553 return self._datasets.keys()
556 class _AllSoftware(object):
557 """Keep track of all Software objects."""
561 cites = {
'Integrative Modeling Platform (IMP)': ihm.citations.imp,
562 'IMP PMI module': ihm.citations.pmi}
564 def __init__(self, system):
566 self._by_namever = {}
567 super(_AllSoftware, self).__init__()
571 for p
in _get_all_state_provenance(
573 self._add_provenance(p)
575 def _add_provenance(self, p):
576 """Add Software from SoftwareProvenance"""
578 name = p.get_software_name()
579 version = p.get_version()
580 if (name, version)
not in self._by_namever:
581 s = ihm.Software(name=name,
582 classification=
'integrative model building',
583 description=
None, version=version,
584 location=p.get_location(),
585 citation=self.cites.get(name))
586 self.system.software.append(s)
587 self._by_namever[name, version] = s
588 return self._by_namever[name, version]
590 def _add_previous_provenance(self, prov):
591 """Add Software from a previous SoftwareProvenance, if any"""
595 prov = prov.get_previous()
598 class _ExternalFiles(object):
599 """Track all externally-referenced files
600 (i.e. anything that refers to a Location that isn't
601 a DatabaseLocation)."""
602 def __init__(self, system):
608 for p
in _get_all_state_provenance(
610 self._add_provenance(p)
612 def _add_provenance(self, p):
613 """Add external file from ScriptProvenance"""
615 path = p.get_filename()
616 if path
not in self._by_path:
617 loc = ihm.location.WorkflowFileLocation(
618 path=p.get_filename(),
619 details=
'Integrative modeling Python script')
620 self.system.locations.append(loc)
621 self._by_path[path] = loc
622 return self._by_path[path]
625 class _ProtocolStep(ihm.protocol.Step):
626 """A single step (e.g. sampling, refinement) in a protocol."""
627 def __init__(self, prov, num_models_begin, assembly, all_software):
628 method = prov.get_method()
629 if prov.get_number_of_replicas() > 1:
630 method =
"Replica exchange " + method
631 super(_ProtocolStep, self).__init__(
635 method=method, name=
'Sampling',
636 num_models_begin=num_models_begin,
637 num_models_end=prov.get_number_of_frames(),
639 multi_state=
False, ordered=
False,
642 software=all_software._add_previous_provenance(prov))
644 def add_combine(self, prov):
645 self.num_models_end = prov.get_number_of_frames()
646 return self.num_models_end
649 class _Protocol(ihm.protocol.Protocol):
650 """A modeling protocol.
651 Each protocol consists of a number of protocol steps (e.g. sampling,
652 refinement) followed by a number of postprocessing steps (e.g.
653 filtering, rescoring, clustering)"""
655 def add_step(self, prov, num_models, assembly, all_software):
658 if len(self.steps) == 0:
659 raise ValueError(
"CombineProvenance with no previous sampling")
660 return self.steps[-1].add_combine(prov)
662 ps = _ProtocolStep(prov, num_models, assembly, all_software)
663 self.steps.append(ps)
664 return ps.num_models_end
666 def add_postproc(self, prov, num_models, assembly):
667 if not self.analyses:
668 self.analyses.append(ihm.analysis.Analysis())
670 pp = ihm.analysis.FilterStep(
671 feature=
'energy/score', assembly=assembly,
672 num_models_begin=num_models,
673 num_models_end=prov.get_number_of_frames())
676 pp = ihm.analysis.ClusterStep(
677 feature=
'RMSD', assembly=assembly, num_models_begin=num_models,
678 num_models_end=num_models)
680 raise ValueError(
"Unhandled provenance", prov)
681 self.analyses[-1].steps.append(pp)
682 return pp.num_models_end
685 class _Protocols(object):
686 """Track all modeling protocols used."""
687 def __init__(self, system):
690 def _add_protocol(self, prot):
696 def step_equal(x, y):
698 return {x: y
for x, y
in d.__dict__.items()
699 if x !=
'dataset_group'}
701 return (type(x) == type(y)
702 and get_dict(x) == get_dict(y))
704 def analysis_equal(x, y):
705 return (len(x.steps) == len(y.steps)
706 and all(step_equal(a, b)
707 for (a, b)
in zip(x.steps, y.steps)))
709 for existing
in self.system.orphan_protocols:
710 if (len(existing.steps) == len(prot.steps)
711 and len(existing.analyses) == len(prot.analyses)
712 and all(step_equal(x, y)
713 for (x, y)
in zip(existing.steps, prot.steps))
714 and all(analysis_equal(x, y)
715 for (x, y)
in zip(existing.analyses, prot.analyses))):
717 self.system.orphan_protocols.append(prot)
720 def _add_hierarchy(self, h, top_h, modeled_assembly, all_software):
726 for p
in reversed(list(_get_all_state_provenance(
727 h, top_h, types=prot_types + pp_types))):
728 if isinstance(p, pp_types):
729 num_models = prot.add_postproc(p, num_models, modeled_assembly)
734 self._add_protocol(prot)
736 num_models = prot.add_step(p, num_models, modeled_assembly,
739 if len(prot.steps) > 0:
740 return self._add_protocol(prot)
743 class _CoordinateHandler(object):
744 def __init__(self, system, datasets):
745 self._system = system
746 self._datasets = datasets
747 self._representation = ihm.representation.Representation()
755 def get_residue_sequence(self, ps):
756 """Determine the primary sequence based on Residue particles.
757 Return the index of the first residue and the sequence, as a list
758 of ihm.ChemComp objects (or None)"""
763 restyp[residue.get_index()] = residue.get_residue_type()
765 restyp[p.get_index()] = p.get_residue_type()
767 resinds = p.get_residue_indexes()
773 seq_id_begin = min(restyp.keys())
774 seq_id_end = max(restyp.keys())
775 return (seq_id_begin,
776 [_imp_to_ihm[restyp.get(x)]
777 for x
in range(seq_id_begin, seq_id_end + 1)])
779 def add_chain(self, ps, asym):
782 return s == asym
or hasattr(s,
'asym')
and s.asym == asym
786 smf = _StartingModelFinder(
787 asym, [s
for s
in self._system.orphan_starting_models
788 if matches_asym(s.asym_unit)],
789 self._system, self._datasets)
790 segfactory = _RepSegmentFactory(asym)
791 offset = asym.auth_seq_id_map
793 starting_model = smf.find(p)
794 seg = segfactory.add(p, starting_model)
796 self._representation.append(seg)
797 self._add_atom_or_sphere(p, asym, offset)
798 last = segfactory.get_last()
800 self._representation.append(last)
802 def _add_atom_or_sphere(self, p, asym, offset):
806 element = p.get_element()
808 atom_name = p.get_atom_type().get_string()
809 het = atom_name.startswith(
'HET:')
811 atom_name = atom_name[4:]
812 self._atoms.append(ihm.model.Atom(
813 asym_unit=asym, seq_id=residue.get_index() - offset,
814 atom_id=atom_name, type_symbol=element,
815 x=xyz[0], y=xyz[1], z=xyz[2], het=het,
816 biso=p.get_temperature_factor(),
817 occupancy=p.get_occupancy()))
820 resinds = p.get_residue_indexes()
824 sbegin = send = p.get_index()
826 xyz = xyzr.get_coordinates()
827 self._spheres.append(ihm.model.Sphere(
828 asym_unit=asym, seq_id_range=(sbegin - offset, send - offset),
829 x=xyz[0], y=xyz[1], z=xyz[2], radius=xyzr.get_radius()))
831 def get_structure_particles(self, h):
832 """Return particles sorted by residue index"""
834 if h.get_number_of_children() == 0:
836 if not h.get_is_valid():
837 raise ValueError(
"Invalid hierarchy as input")
839 hierarchy=h, resolution=0.).get_selected_particles():
842 ps.append((residue.get_index(), residue))
845 resinds = fragment.get_residue_indexes()
846 _check_sequential(fragment, resinds)
847 resind = resinds[len(resinds) // 2]
848 ps.append((resind, fragment))
852 ps.append((residue.get_index(), atom))
853 return [p[1]
for p
in sorted(ps, key=operator.itemgetter(0))]
856 class _ModelAssemblies(object):
857 def __init__(self, system):
859 self._seen_assemblies = {}
861 def add(self, asyms):
864 if asyms
not in self._seen_assemblies:
865 assembly = ihm.Assembly(
866 asyms, name=
"Modeled assembly",
867 description=
"All components modeled by IMP")
868 self.system.orphan_assemblies.append(assembly)
869 self._seen_assemblies[asyms] = assembly
870 return self._seen_assemblies[asyms]
873 class _Representations(object):
874 def __init__(self, system):
881 for existing
in self.system.orphan_representations:
882 if (len(existing) == len(rep)
883 and all(type(x) == type(y)
884 and x.__dict__ == y.__dict__
885 for (x, y)
in zip(existing, rep))):
887 self.system.orphan_representations.append(rep)
Select non water and non hydrogen atoms.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Hierarchy read_pdb_or_mmcif(TextInput input, Model *model, PDBSelector *selector=get_default_pdb_selector(), bool select_first_model=true)
Read all the molecules in the first model of the PDB or mmCIF file.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
A decorator to associate a particle with a part of a protein/DNA/RNA.
Track creation of a system fragment from running some software.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
ElementTable & get_element_table()
Track creation of a system fragment from sampling.
def get_molecule
Given a Hierarchy, walk up and find the parent Molecule.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Track creation of a system fragment by combination.
Class for storing model, its restraints, constraints, and particles.
static bool get_is_setup(Model *m, ParticleIndex pi)
void add_hierarchy(RMF::FileHandle fh, atom::Hierarchy hs)
The standard decorator for manipulating molecular structures.
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
A decorator for a particle representing an atom.
Track creation of a system fragment by filtering.
Track creation of a system fragment from a PDB file.
A decorator for a particle with x,y,z coordinates.
A decorator for a residue.
static bool get_is_setup(Model *m, ParticleIndex p)
Check if the particle has the needed attributes for a cast to succeed.
Residue get_residue(Atom d, bool nothrow=false)
Return the Residue containing this atom.
Track creation of a system fragment from clustering.
static bool get_is_setup(Model *m, ParticleIndex pi)
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_all_provenance
Yield all provenance decorators of the given types for the particle.
A decorator for a molecule.
Select hierarchy particles identified by the biological name.
Select all ATOM and HETATM records with the given chain ids.
Track creation of a system fragment from running a script.
A decorator for a particle with x,y,z coordinates and a radius.