1 """@namespace IMP.mmcif.data
2 @brief Classes to represent data structures used in mmCIF.
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; mmCIF bead will cover "
59 % (str(fragment), resinds[0], resinds[-1]))
62 def _get_all_state_provenance(state_h, top_h, types):
63 """Yield all provenance information for the given state.
64 If the given State Hierarchy node contains no provenance information,
65 fall back to any provenance information for the top-level node
71 if count == 0
and top_h
is not None:
76 class _CustomDNAAlphabet(ihm.Alphabet):
77 """Custom DNA alphabet that maps A,C,G,T (rather than DA,DC,DG,DT
79 _comps = dict([cc.code_canonical, cc]
80 for cc
in ihm.DNAAlphabet._comps.values())
83 class _EntityMapper(dict):
84 """Handle mapping from IMP chains to CIF entities.
85 Multiple components may map to the same entity if they
87 def __init__(self, system):
90 self._sequence_dict = {}
92 self._alphabet_map = {
93 IMP.atom.UnknownChainType: ihm.LPeptideAlphabet,
94 IMP.atom.Protein: ihm.LPeptideAlphabet,
95 IMP.atom.RNA: ihm.RNAAlphabet,
96 IMP.atom.DNA: _CustomDNAAlphabet}
98 def _get_sequence_from_residues(self, chain, seq_from_res):
99 seq_id_begin, seq = seq_from_res
101 raise ValueError(
"Chain %s has no sequence and no residues"
103 missing_seq = [ind + seq_id_begin
104 for (ind, res)
in enumerate(seq)
if res
is None]
107 "Chain %s has no declared sequence; tried to determine the "
108 "sequence from Residues, but the following residue indices "
109 "have no residue type (perhaps covered only by Fragments): %s"
110 % (chain, str(missing_seq)))
111 return seq_id_begin - 1, tuple(seq)
113 def add(self, chain, seq_from_res=None):
114 sequence = chain.get_sequence()
115 offset = chain.get_sequence_offset()
117 if seq_from_res
is not None:
118 offset, sequence = self._get_sequence_from_residues(
121 raise ValueError(
"Chain %s has no sequence" % chain)
124 alphabet = self._alphabet_map[chain.get_chain_type()]()
125 sequence = tuple(alphabet[x]
for x
in sequence)
126 if sequence
not in self._sequence_dict:
127 entity = ihm.Entity(sequence)
128 self.system.entities.append(entity)
129 self._entities.append(entity)
130 self._sequence_dict[sequence] = entity
131 uniprot = chain.get_uniprot_accession()
133 up = ihm.reference.UniProtSequence.from_accession(uniprot)
134 entity.references.append(up)
135 self[chain] = self._sequence_dict[sequence]
136 return self[chain], offset
139 """Yield all entities"""
140 return self._entities
143 def _assign_id(obj, seen_objs, obj_by_id):
144 """Assign a unique ID to obj, and track all ids in obj_by_id."""
145 if obj
not in seen_objs:
146 if not hasattr(obj,
'id'):
147 obj_by_id.append(obj)
148 obj.id = len(obj_by_id)
149 seen_objs[obj] = obj.id
151 obj.id = seen_objs[obj]
155 """An mmCIF component. This is an instance of an _Entity. Multiple
156 _Components may map to the same _Entity but must have unique
157 asym_ids. A _Component is similar to an IMP Chain but multiple
158 Chains may map to the same _Component (the Chains represent the
159 same structure, just in different states, and potentially in
160 different IMP Models). A _Component may also represent something
161 that is described by an experiment but was not modeled by IMP, and
162 so no Chains map to it but a string name does."""
163 def __init__(self, entity, asym_id, name):
164 self.entity, self.asym_id, self.name = entity, asym_id, name
167 class _ComponentMapper:
168 """Handle mapping from IMP Chains to CIF AsymUnits."""
169 def __init__(self, system):
172 self._used_entities = set()
173 self._all_components = []
176 def __getitem__(self, chain):
177 asym_id, map_key, name = self._handle_chain(chain)
178 return self._map[map_key]
180 def _handle_chain(self, chain):
182 asym_id = chain.get_id()
183 name = mol.get_name()
if mol
else None
186 map_key =
"name", name
188 map_key =
"asym_id", asym_id
189 return asym_id, map_key, name
191 def add(self, chain, entity, offset):
192 """Add a chain (an IMP Chain object)"""
193 asym_id, map_key, name = self._handle_chain(chain)
194 if map_key
not in self._map:
195 component = _Component(entity, asym_id, name)
196 if entity
not in self._used_entities:
197 self._used_entities.add(entity)
201 entity.description = \
202 component.name.split(
"@")[0].split(
".")[0]
203 self._all_components.append(component)
204 asym = ihm.AsymUnit(entity, name, id=asym_id,
205 auth_seq_id_map=offset)
206 self.system.asym_units.append(asym)
207 component.asym_unit = asym
208 self._map[map_key] = component
210 component = self._map[map_key]
211 if component.entity != entity:
212 raise ValueError(
"Two chains have the same ID (%s) but "
213 "different sequences - rename one of the "
214 "chains" % component.asym_unit.id)
215 if component.asym_unit.auth_seq_id_map != offset:
217 "Two chains have the same ID (%s) but different offsets "
218 "(%d, %d) - this is not supported"
219 % (component.asym_unit.id,
220 component.asym_unit.auth_seq_id_map, offset))
224 """Get all components"""
225 return self._all_components
228 class _RepSegmentFactory:
229 """Make ihm.representation.Segment objects for each set of contiguous
230 particles with the same representation"""
231 def __init__(self, asym):
234 self.offset = asym.auth_seq_id_map
236 self.imp_residue_range = ()
238 def add(self, particle, starting_model):
239 """Add a new particle to the last segment (and return None).
240 Iff the particle could not be added, return the segment and start
242 (resrange, rigid_body,
243 is_res, is_atom) = self._get_particle_info(particle)
245 def start_new_segment():
246 self.particles = [particle]
247 self.imp_residue_range = resrange
248 self.rigid_body = rigid_body
250 self.is_atom = is_atom
251 self.starting_model = starting_model
252 if not self.particles:
255 elif (type(particle) == type(self.particles[0])
256 and is_res == self.is_res
257 and is_atom == self.is_atom
258 and resrange[0] <= self.imp_residue_range[1] + 1
259 and starting_model == self.starting_model
260 and self._same_rigid_body(rigid_body)):
262 self.particles.append(particle)
263 self.imp_residue_range = (self.imp_residue_range[0], resrange[1])
266 seg = self.get_last()
271 """Return the last segment, or None"""
274 asym = self.asym(self.imp_residue_range[0] - self.offset,
275 self.imp_residue_range[1] - self.offset)
277 return ihm.representation.AtomicSegment(
278 asym_unit=asym, rigid=self.rigid_body
is not None,
279 starting_model=self.starting_model)
281 return ihm.representation.ResidueSegment(
283 rigid=self.rigid_body
is not None, primitive=
'sphere',
284 starting_model=self.starting_model)
286 return ihm.representation.FeatureSegment(
288 rigid=self.rigid_body
is not None, primitive=
'sphere',
289 count=len(self.particles),
290 starting_model=self.starting_model)
292 def _same_rigid_body(self, rigid_body):
295 if self.rigid_body
is None and rigid_body
is None:
297 elif self.rigid_body
is None or rigid_body
is None:
300 return self.rigid_body == rigid_body
302 def _get_particle_info(self, p):
309 return (p.get_index(), p.get_index()), rigid_body,
True,
False
312 return (res.get_index(), res.get_index()), rigid_body,
False,
True
314 resinds = p.get_residue_indexes()
315 return (resinds[0], resinds[-1]), rigid_body,
False,
False
316 raise TypeError(
"Unknown particle ", p)
319 def _get_all_structure_provenance(p):
320 """Yield all StructureProvenance decorators for the given particle."""
324 class _StartingModelAtomHandler:
325 def __init__(self, templates, asym):
327 self._last_res_index =
None
328 self.templates = templates
331 def _residue_first_atom(self, res):
332 """Return True iff we're looking at the first atom in this residue"""
334 ind = res.get_index()
335 if ind != self._last_res_index:
336 self._last_res_index = ind
339 def handle_residue(self, res, comp_id, seq_id, offset):
340 res_name = res.get_residue_type().get_string()
344 if res_name ==
'MSE' and comp_id ==
'MET':
345 if self._residue_first_atom(res):
350 assert len(self.templates) == 0
351 self._seq_dif.append(ihm.startmodel.MSESeqDif(
352 res.get_index(), seq_id))
353 elif res_name != comp_id:
354 if self._residue_first_atom(res):
355 print(
"WARNING: Starting model residue %s does not match "
356 "that in the output model (%s) for chain %s residue %d. "
357 "Check offset (currently %d)."
358 % (res_name, comp_id, self.asym._id, seq_id, offset))
359 self._seq_dif.append(ihm.startmodel.SeqDif(
360 db_seq_id=res.get_index(), seq_id=seq_id,
362 details=
"Mutation of %s to %s" % (res_name, comp_id)))
364 def get_ihm_atoms(self, particles, offset):
368 element = atom.get_element()
370 atom_name = atom.get_atom_type().get_string()
371 het = atom_name.startswith(
'HET:')
373 atom_name = atom_name[4:]
376 seq_id = res.get_index() + offset
377 comp_id = self.asym.entity.sequence[seq_id-1].id
378 self.handle_residue(res, comp_id, seq_id, offset)
379 yield ihm.model.Atom(asym_unit=self.asym, seq_id=seq_id,
380 atom_id=atom_name, type_symbol=element,
381 x=coord[0], y=coord[1], z=coord[2],
382 het=het, biso=atom.get_temperature_factor())
385 class _StartingModel(ihm.startmodel.StartingModel):
386 _eq_keys = [
'filename',
'asym_id',
'offset']
388 def __init__(self, asym_unit, struc_prov):
389 self.filename = struc_prov[0].get_filename()
391 asym_unit=asym_unit(1, 1),
395 offset=struc_prov[0].get_residue_offset())
397 def _add_residue(self, resind):
400 seq_id_begin = self.asym_unit.seq_id_range[0]
401 if seq_id_begin == 0:
402 seq_id_begin = seq_id_end
403 self.asym_unit = self.asym_unit.asym(seq_id_begin, seq_id_end)
410 return tuple([self.__class__]
411 + [getattr(self, x)
for x
in self._eq_keys])
413 def __eq__(self, other):
414 return other
is not None and self._eq_vals() == other._eq_vals()
417 return hash(self._eq_vals())
419 def _set_sources_datasets(self, system, datasets):
421 if (hasattr(ihm.metadata,
'CIFParser')
422 and self.filename.endswith(
'.cif')):
423 p = ihm.metadata.CIFParser()
424 elif (hasattr(ihm.metadata,
'BinaryCIFParser')
425 and self.filename.endswith(
'.bcif')):
426 p = ihm.metadata.BinaryCIFParser()
428 p = ihm.metadata.PDBParser()
429 r = p.parse_file(self.filename)
430 system.software.extend(r.get(
'software', []))
431 dataset = datasets.add(r[
'dataset'])
433 templates = r.get(
'templates', {}).get(self.asym_id, [])
436 system.locations.append(t.alignment_file)
438 datasets.add(t.dataset)
439 self.dataset = dataset
440 self.templates = templates
441 self.metadata = r.get(
'metadata', [])
443 def _read_coords(self):
444 """Read the coordinates for this starting model"""
450 rng = self.asym_unit.seq_id_range
452 hier, residue_indexes=list(range(rng[0] - self.offset,
453 rng[1] + 1 - self.offset)))
456 def get_seq_dif(self):
460 mh = _StartingModelAtomHandler(self.templates, self.asym_unit)
461 m, sel = self._read_coords()
462 for a
in mh.get_ihm_atoms(sel.get_selected_particles(), self.offset):
464 self._seq_dif = mh._seq_dif
467 class _StartingModelFinder:
468 """Map IMP particles to starting model objects"""
469 def __init__(self, asym, existing_starting_models, system, datasets):
470 self._seen_particles = {}
472 self._seen_starting_models = {}
473 for sm
in existing_starting_models:
474 self._seen_starting_models[sm] = sm
475 self._system = system
476 self._datasets = datasets
478 def find(self, particle):
479 """Return a StartingModel object, or None, for this particle"""
480 def _get_starting_model(sp, resind):
481 s = _StartingModel(self._asym, sp)
482 if s
not in self._seen_starting_models:
483 self._seen_starting_models[s] = s
484 s._set_sources_datasets(self._system, self._datasets)
485 self._system.orphan_starting_models.append(s)
486 s = self._seen_starting_models[s]
488 s._add_residue(resind)
493 sp = list(_get_all_structure_provenance(particle))
495 return _get_starting_model(sp, resind)
503 pi = h.get_particle_index()
504 seen_parents.append(pi)
506 if pi
in self._seen_particles:
507 sp = self._seen_particles[pi]
508 if sp
and sp[0]
and resind
is not None:
509 sp[0]._add_residue(resind)
510 return sp[0]
if sp
else None
512 sp = list(_get_all_structure_provenance(h))
513 self._seen_particles[pi] = []
515 s = _get_starting_model(sp, resind)
518 for spi
in seen_parents:
519 self._seen_particles[spi].append(s)
525 """Store all datasets used."""
526 def __init__(self, system):
533 """Add and return a new dataset."""
534 if d
not in self._datasets:
535 self._datasets[d] = d
536 self.system.orphan_datasets.append(d)
537 return self._datasets[d]
539 def add_group(self, datasets, name):
540 """Add and return a new group of datasets"""
544 for dataset
in datasets:
545 if dataset
not in seen:
549 if d
not in self._groups:
550 g = ihm.dataset.DatasetGroup(d, name=name)
552 self.system.orphan_dataset_groups.append(g)
553 return self._groups[d]
556 """Yield all datasets"""
557 return self._datasets.keys()
561 """Keep track of all Software objects."""
565 cites = {
'Integrative Modeling Platform (IMP)': ihm.citations.imp,
566 'IMP PMI module': ihm.citations.pmi}
568 def __init__(self, system):
570 self._by_namever = {}
575 for p
in _get_all_state_provenance(
577 self._add_provenance(p)
579 def _add_provenance(self, p):
580 """Add Software from SoftwareProvenance"""
582 name = p.get_software_name()
583 version = p.get_version()
584 if (name, version)
not in self._by_namever:
585 s = ihm.Software(name=name,
586 classification=
'integrative model building',
587 description=
None, version=version,
588 location=p.get_location(),
589 citation=self.cites.get(name))
590 self.system.software.append(s)
591 self._by_namever[name, version] = s
592 return self._by_namever[name, version]
594 def _add_previous_provenance(self, prov):
595 """Add Software from a previous SoftwareProvenance, if any"""
599 prov = prov.get_previous()
602 class _ExternalFiles:
603 """Track all externally-referenced files
604 (i.e. anything that refers to a Location that isn't
605 a DatabaseLocation)."""
606 def __init__(self, system):
612 for p
in _get_all_state_provenance(
614 self._add_provenance(p)
616 def _add_provenance(self, p):
617 """Add external file from ScriptProvenance"""
619 path = p.get_filename()
620 if path
not in self._by_path:
621 loc = ihm.location.WorkflowFileLocation(
622 path=p.get_filename(),
623 details=
'Integrative modeling Python script')
624 self.system.locations.append(loc)
625 self._by_path[path] = loc
626 return self._by_path[path]
629 class _ProtocolStep(ihm.protocol.Step):
630 """A single step (e.g. sampling, refinement) in a protocol."""
631 def __init__(self, prov, num_models_begin, assembly, all_software):
632 method = prov.get_method()
633 if prov.get_number_of_replicas() > 1:
634 method =
"Replica exchange " + method
639 method=method, name=
'Sampling',
640 num_models_begin=num_models_begin,
641 num_models_end=prov.get_number_of_frames(),
643 multi_state=
False, ordered=
False,
646 software=all_software._add_previous_provenance(prov))
648 def add_combine(self, prov):
649 self.num_models_end = prov.get_number_of_frames()
650 return self.num_models_end
653 class _Protocol(ihm.protocol.Protocol):
654 """A modeling protocol.
655 Each protocol consists of a number of protocol steps (e.g. sampling,
656 refinement) followed by a number of postprocessing steps (e.g.
657 filtering, rescoring, clustering)"""
659 def add_step(self, prov, num_models, assembly, all_software):
662 if len(self.steps) == 0:
663 raise ValueError(
"CombineProvenance with no previous sampling")
664 return self.steps[-1].add_combine(prov)
666 ps = _ProtocolStep(prov, num_models, assembly, all_software)
667 self.steps.append(ps)
668 return ps.num_models_end
670 def add_postproc(self, prov, num_models, assembly):
671 if not self.analyses:
672 self.analyses.append(ihm.analysis.Analysis())
674 pp = ihm.analysis.FilterStep(
675 feature=
'energy/score', assembly=assembly,
676 num_models_begin=num_models,
677 num_models_end=prov.get_number_of_frames())
680 pp = ihm.analysis.ClusterStep(
681 feature=
'RMSD', assembly=assembly, num_models_begin=num_models,
682 num_models_end=num_models)
684 raise ValueError(
"Unhandled provenance", prov)
685 self.analyses[-1].steps.append(pp)
686 return pp.num_models_end
690 """Track all modeling protocols used."""
691 def __init__(self, system):
694 def _add_protocol(self, prot):
700 def step_equal(x, y):
702 return {x: y
for x, y
in d.__dict__.items()
703 if x !=
'dataset_group'}
705 return (type(x) == type(y)
706 and get_dict(x) == get_dict(y))
708 def analysis_equal(x, y):
709 return (len(x.steps) == len(y.steps)
710 and all(step_equal(a, b)
711 for (a, b)
in zip(x.steps, y.steps)))
713 for existing
in self.system.orphan_protocols:
714 if (len(existing.steps) == len(prot.steps)
715 and len(existing.analyses) == len(prot.analyses)
716 and all(step_equal(x, y)
717 for (x, y)
in zip(existing.steps, prot.steps))
718 and all(analysis_equal(x, y)
719 for (x, y)
in zip(existing.analyses, prot.analyses))):
721 self.system.orphan_protocols.append(prot)
724 def _add_hierarchy(self, h, top_h, modeled_assembly, all_software):
730 for p
in reversed(list(_get_all_state_provenance(
731 h, top_h, types=prot_types + pp_types))):
732 if isinstance(p, pp_types):
733 num_models = prot.add_postproc(p, num_models, modeled_assembly)
738 self._add_protocol(prot)
740 num_models = prot.add_step(p, num_models, modeled_assembly,
743 if len(prot.steps) > 0:
744 return self._add_protocol(prot)
747 class _CoordinateHandler:
748 def __init__(self, system, datasets):
749 self._system = system
750 self._datasets = datasets
751 self._representation = ihm.representation.Representation()
759 def get_residue_sequence(self, ps):
760 """Determine the primary sequence based on Residue particles.
761 Return the index of the first residue and the sequence, as a list
762 of ihm.ChemComp objects (or None)"""
767 restyp[residue.get_index()] = residue.get_residue_type()
769 restyp[p.get_index()] = p.get_residue_type()
771 resinds = p.get_residue_indexes()
777 seq_id_begin = min(restyp.keys())
778 seq_id_end = max(restyp.keys())
779 return (seq_id_begin,
780 [_imp_to_ihm[restyp.get(x)]
781 for x
in range(seq_id_begin, seq_id_end + 1)])
783 def add_chain(self, ps, asym):
786 return s == asym
or hasattr(s,
'asym')
and s.asym == asym
790 smf = _StartingModelFinder(
791 asym, [s
for s
in self._system.orphan_starting_models
792 if matches_asym(s.asym_unit)],
793 self._system, self._datasets)
794 segfactory = _RepSegmentFactory(asym)
795 offset = asym.auth_seq_id_map
797 starting_model = smf.find(p)
798 seg = segfactory.add(p, starting_model)
800 self._representation.append(seg)
801 self._add_atom_or_sphere(p, asym, offset)
802 last = segfactory.get_last()
804 self._representation.append(last)
806 def _add_atom_or_sphere(self, p, asym, offset):
810 element = p.get_element()
812 atom_name = p.get_atom_type().get_string()
813 het = atom_name.startswith(
'HET:')
815 atom_name = atom_name[4:]
816 self._atoms.append(ihm.model.Atom(
817 asym_unit=asym, seq_id=residue.get_index() - offset,
818 atom_id=atom_name, type_symbol=element,
819 x=xyz[0], y=xyz[1], z=xyz[2], het=het,
820 biso=p.get_temperature_factor(),
821 occupancy=p.get_occupancy()))
824 resinds = p.get_residue_indexes()
828 sbegin = send = p.get_index()
830 xyz = xyzr.get_coordinates()
831 self._spheres.append(ihm.model.Sphere(
832 asym_unit=asym, seq_id_range=(sbegin - offset, send - offset),
833 x=xyz[0], y=xyz[1], z=xyz[2], radius=xyzr.get_radius()))
835 def get_structure_particles(self, h):
836 """Return particles sorted by residue index"""
838 if h.get_number_of_children() == 0:
840 if not h.get_is_valid():
841 raise ValueError(
"Invalid hierarchy as input")
843 hierarchy=h, resolution=0.).get_selected_particles():
846 ps.append((residue.get_index(), residue))
849 resinds = fragment.get_residue_indexes()
850 _check_sequential(fragment, resinds)
851 resind = resinds[len(resinds) // 2]
852 ps.append((resind, fragment))
856 ps.append((residue.get_index(), atom))
857 return [p[1]
for p
in sorted(ps, key=operator.itemgetter(0))]
860 class _ModelAssemblies:
861 def __init__(self, system):
863 self._seen_assemblies = {}
865 def add(self, asyms):
868 if asyms
not in self._seen_assemblies:
869 assembly = ihm.Assembly(
870 asyms, name=
"Modeled assembly",
871 description=
"All components modeled by IMP")
872 self.system.orphan_assemblies.append(assembly)
873 self._seen_assemblies[asyms] = assembly
874 return self._seen_assemblies[asyms]
877 class _Representations:
878 def __init__(self, system):
885 for existing
in self.system.orphan_representations:
886 if (len(existing) == len(rep)
887 and all(type(x) == type(y)
888 and x.__dict__ == y.__dict__
889 for (x, y)
in zip(existing, rep))):
891 self.system.orphan_representations.append(rep)
Select non water and non hydrogen atoms.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
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)
Hierarchy read_pdb_any(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-like file.
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.