3 """@namespace IMP.pmi.tools
4 Miscellaneous utilities.
12 from collections.abc
import MutableSet
16 from time
import process_time
19 from collections
import defaultdict, OrderedDict, namedtuple
26 _JAXData = namedtuple(
'_JAXData', (
'model',
'space'))
29 def _get_system_for_hier(hier):
30 """Given a hierarchy, return the System that created it, or None"""
33 if hier
and not hasattr(hier,
'get_parent'):
40 if hasattr(hier,
'_pmi2_system'):
41 h = hier._pmi2_system()
46 for s
in IMP.pmi.topology.System._all_systems:
50 hier = hier.get_parent()
53 def _all_protocol_outputs(hier):
54 """Iterate over all (ProtocolOutput, State) pairs for the
56 system = _get_system_for_hier(hier)
58 for state
in system.states:
59 for p
in state._protocol_output:
63 def _add_pmi_provenance(p):
64 """Tag the given particle as being created by the current version
69 location=
"https://integrativemodeling.org")
73 def _get_restraint_set_keys():
74 if not hasattr(_get_restraint_set_keys,
'pmi_rs_key'):
75 _get_restraint_set_keys.pmi_rs_key =
IMP.ModelKey(
"PMI restraints")
76 _get_restraint_set_keys.rmf_rs_key =
IMP.ModelKey(
"RMF restraints")
77 return (_get_restraint_set_keys.pmi_rs_key,
78 _get_restraint_set_keys.rmf_rs_key)
81 def _add_restraint_sets(model, mk, mk_rmf):
84 model.add_data(mk, rs)
85 model.add_data(mk_rmf, rs_rmf)
90 """Add a PMI restraint to the model.
91 Since Model.add_restraint() no longer exists (in modern IMP restraints
92 should be added to a ScoringFunction instead) store them instead in
93 a RestraintSet, and keep a reference to it in the Model.
95 If `add_to_rmf` is True, also add the restraint to a separate list
96 of restraints that will be written out to RMF files (by default, most
97 PMI restraints are not)."""
98 mk, mk_rmf = _get_restraint_set_keys()
99 if model.get_has_data(mk):
100 rs = IMP.RestraintSet.get_from(model.get_data(mk))
101 rs_rmf = IMP.RestraintSet.get_from(model.get_data(mk_rmf))
103 rs, rs_rmf = _add_restraint_sets(model, mk, mk_rmf)
104 rs.add_restraint(restraint)
106 rs_rmf.add_restraint(restraint)
110 """Get a RestraintSet containing all PMI restraints added to the model.
111 If `rmf` is True, return only the subset of these restraints that
112 should be written out to RMF files."""
113 mk, mk_rmf = _get_restraint_set_keys()
114 if not model.get_has_data(mk):
115 warnings.warn(
"no restraints added to model yet",
117 _add_restraint_sets(model, mk, mk_rmf)
119 return IMP.RestraintSet.get_from(model.get_data(mk_rmf))
121 return IMP.RestraintSet.get_from(model.get_data(mk))
125 """Collect timing information.
126 Add an instance of this class to outputobjects to get timing information
131 @param isdelta if True (the default) then report the time since the
132 last use of this class; if False, report cumulative time."""
133 self.starttime = process_time()
135 self.isdelta = isdelta
137 def set_label(self, labelstr):
138 self.label = labelstr
140 def get_output(self):
143 newtime = process_time()
144 output[
"Stopwatch_" + self.label +
"_delta_seconds"] \
145 = str(newtime - self.starttime)
146 self.starttime = newtime
148 output[
"Stopwatch_" + self.label +
"_elapsed_seconds"] \
149 = str(process_time() - self.starttime)
155 def __init__(self, m, initialvalue, minvalue, maxvalue, isoptimized=True,
163 nuisance.set_lower(minvalue)
165 nuisance.set_upper(maxvalue)
168 nuisance.set_is_optimized(nuisance.get_nuisance_key(), isoptimized)
169 self.nuisance = nuisance
171 def get_particle(self):
177 def __init__(self, m, isoptimized=True, nweights_or_weights=None):
179 if isinstance(nweights_or_weights, int):
181 pw, nweights_or_weights
185 nweights_or_weights = list(nweights_or_weights)
187 pw, nweights_or_weights
191 self.weight.set_weights_are_optimized(isoptimized)
193 def get_particle(self):
199 def __init__(self, m, center, normal, isoptimized=True):
202 self.surface.set_coordinates_are_optimized(isoptimized)
203 self.surface.set_normal_is_optimized(isoptimized)
205 def get_particle(self):
209 def get_cross_link_data(directory, filename, dist, omega, sigma,
210 don=
None, doff=
None, prior=0, type_of_profile=
"gofr"):
212 (distmin, distmax, ndist) = dist
213 (omegamin, omegamax, nomega) = omega
214 (sigmamin, sigmamax, nsigma) = sigma
217 with open(filen)
as xlpot:
218 dictionary = ast.literal_eval(xlpot.readline())
220 xpot = dictionary[directory][filename][
"distance"]
221 pot = dictionary[directory][filename][type_of_profile]
223 dist_grid =
get_grid(distmin, distmax, ndist,
False)
224 omega_grid = get_log_grid(omegamin, omegamax, nomega)
225 sigma_grid = get_log_grid(sigmamin, sigmamax, nsigma)
227 if don
is not None and doff
is not None:
247 def get_grid(gmin, gmax, ngrid, boundaries):
249 dx = (gmax - gmin) / float(ngrid)
250 for i
in range(0, ngrid + 1):
251 if not boundaries
and i == 0:
253 if not boundaries
and i == ngrid:
255 grid.append(gmin + float(i) * dx)
259 def get_log_grid(gmin, gmax, ngrid):
261 for i
in range(0, ngrid + 1):
262 grid.append(gmin * math.exp(float(i) / ngrid * math.log(gmax / gmin)))
268 example '"{ID_Score}" > 28 AND "{Sample}" ==
269 "%10_1%" OR ":Sample}" == "%10_2%" OR ":Sample}"
270 == "%10_3%" OR ":Sample}" == "%8_1%" OR ":Sample}" == "%8_2%"'
273 import pyparsing
as pp
275 operator = pp.Regex(
">=|<=|!=|>|<|==|in").setName(
"operator")
276 value = pp.QuotedString(
278 r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?")
279 identifier = pp.Word(pp.alphas, pp.alphanums +
"_")
280 comparison_term = identifier | value
281 condition = pp.Group(comparison_term + operator + comparison_term)
283 expr = pp.operatorPrecedence(condition, [
284 (
"OR", 2, pp.opAssoc.LEFT, ),
285 (
"AND", 2, pp.opAssoc.LEFT, ),
288 parsedstring = str(expr.parseString(inputstring)) \
294 .replace(
"{",
"float(entry['") \
295 .replace(
"}",
"'])") \
296 .replace(
":",
"str(entry['") \
297 .replace(
"}",
"'])") \
298 .replace(
"AND",
"and") \
303 def open_file_or_inline_text(filename):
305 fl = open(filename,
"r")
307 fl = filename.split(
"\n")
311 def get_ids_from_fasta_file(fastafile):
313 with open(fastafile)
as ff:
316 ids.append(line[1:-1])
322 this function works with plain hierarchies, as read from the pdb,
323 no multi-scale hierarchies
330 atom_type=IMP.atom.AT_CA)
338 print(
"get_closest_residue_position: exiting while loop "
341 p = sel.get_selected_particles()
346 print(
"get_closest_residue_position: got NO residues for hierarchy "
347 "%s and residue %i" % (hier, resindex))
349 "get_closest_residue_position: got NO residues for hierarchy "
350 "%s and residue %i" % (hier, resindex))
353 "got multiple residues for hierarchy %s and residue %i; the list "
355 % (hier, resindex, str([pp.get_name()
for pp
in p])))
360 Return the residue index gaps and contiguous segments in the hierarchy.
362 @param hierarchy hierarchy to examine
363 @param start first residue index
364 @param end last residue index
366 @return A list of lists of the form
367 [[1,100,"cont"],[101,120,"gap"],[121,200,"cont"]]
370 for n, rindex
in enumerate(range(start, end + 1)):
372 atom_type=IMP.atom.AT_CA)
374 if len(sel.get_selected_particles()) == 0:
378 rindexcont = start - 1
379 if rindexgap == rindex - 1:
385 gaps.append([rindex, rindex,
"gap"])
391 rindexgap = start - 1
393 if rindexcont == rindex - 1:
400 gaps.append([rindex, rindex,
"cont"])
411 def set_map_element(self, xvalue, yvalue):
412 self.map[xvalue] = yvalue
414 def get_map_element(self, invalue):
415 if isinstance(invalue, float):
419 dist = (invalue - x) * (invalue - x)
428 return self.map[minx]
429 elif isinstance(invalue, str):
430 return self.map[invalue]
432 raise TypeError(
"wrong type for map")
436 """New tuple format: molname OR (start,stop,molname,copynum,statenum)
437 Copy and state are optional. Can also use 'None' for them which will
438 get all. You can also pass -1 for stop which will go to the end.
439 Returns the particles
442 kwds[
'resolution'] = resolution
443 if isinstance(tuple_selection, str):
444 kwds[
'molecule'] = tuple_selection
445 elif isinstance(tuple_selection, tuple):
446 rbegin = tuple_selection[0]
447 rend = tuple_selection[1]
448 kwds[
'molecule'] = tuple_selection[2]
450 copynum = tuple_selection[3]
451 if copynum
is not None:
452 kwds[
'copy_index'] = copynum
456 statenum = tuple_selection[4]
457 if statenum
is not None:
458 kwds[
'state_index'] = statenum
465 residue_indexes=range(1, rbegin),
467 return s.get_selected_particles()
469 kwds[
'residue_indexes'] = range(rbegin, rend+1)
471 return s.get_selected_particles()
474 def get_db_from_csv(csvfilename, encoding=None):
477 with open(csvfilename, encoding=encoding)
as fh:
478 csvr = csv.DictReader(fh)
480 outputlist.append(ls)
485 '''Return the component name provided a particle and a list of names'''
487 protname = root.get_name()
489 while protname
not in list_of_names:
490 root0 = root.get_parent()
493 protname = root0.get_name()
498 if "Beads" in protname:
501 return (protname, is_a_bead)
506 Retrieve the residue indexes for the given particle.
508 The particle must be an instance of Fragment,Residue, Atom or Molecule
509 or else returns an empty list
520 resind_tmp = IMP.pmi.tools.OrderedSet()
527 resind = list(resind_tmp)
533 def sort_by_residues(particles):
536 sorted_particles_residues = sorted(
538 key=
lambda tup: tup[1])
539 particles = [p[0]
for p
in sorted_particles_residues]
548 """Synchronize data over a parallel run"""
549 from mpi4py
import MPI
550 comm = MPI.COMM_WORLD
551 rank = comm.Get_rank()
552 number_of_processes = comm.size
555 comm.send(data, dest=0, tag=11)
558 for i
in range(1, number_of_processes):
559 data_tmp = comm.recv(source=i, tag=11)
560 if isinstance(data, list):
562 elif isinstance(data, dict):
563 data.update(data_tmp)
565 raise TypeError(
"data not supported, use list or dictionaries")
567 for i
in range(1, number_of_processes):
568 comm.send(data, dest=i, tag=11)
571 data = comm.recv(source=0, tag=11)
581 Yield all sublists of length >= lmin and <= lmax
587 for j
in range(i + lmin, min(n + 1, i + 1 + lmax)):
591 def flatten_list(ls):
592 return [item
for sublist
in ls
for item
in sublist]
596 """ Yield successive length-sized chunks from a list.
598 for i
in range(0, len(list), length):
599 yield list[i:i + length]
602 def chunk_list_into_segments(seq, num):
604 avg = len(seq) / float(num)
608 while last < len(seq):
609 out.append(seq[int(last):int(last + avg)])
617 ''' This class stores integers
618 in ordered compact lists eg:
620 the methods help splitting and merging the internal lists
622 s=Segments([1,2,3]) is [[1,2,3]]
623 s.add(4) is [[1,2,3,4]] (add right)
624 s.add(3) is [[1,2,3,4]] (item already existing)
625 s.add(7) is [[1,2,3,4],[7]] (new list)
626 s.add([8,9]) is [[1,2,3,4],[7,8,9]] (add item right)
627 s.add([5,6]) is [[1,2,3,4,5,6,7,8,9]] (merge)
628 s.remove(3) is [[1,2],[4,5,6,7,8,9]] (split)
633 '''index can be a integer or a list of integers '''
634 if isinstance(index, int):
635 self.segs = [[index]]
636 elif isinstance(index, list):
637 self.segs = [[index[0]]]
641 raise TypeError(
"index must be an int or list of ints")
644 '''index can be a integer or a list of integers '''
645 if isinstance(index, numbers.Integral):
648 for n, s
in enumerate(self.segs):
656 if mergeright
is None and mergeleft
is None:
657 self.segs.append([index])
658 if mergeright
is not None and mergeleft
is None:
659 self.segs[mergeright].append(index)
660 if mergeleft
is not None and mergeright
is None:
661 self.segs[mergeleft] = [index]+self.segs[mergeleft]
662 if mergeleft
is not None and mergeright
is not None:
663 self.segs[mergeright] = \
664 self.segs[mergeright]+[index]+self.segs[mergeleft]
665 del self.segs[mergeleft]
667 for n
in range(len(self.segs)):
670 self.segs.sort(key=
lambda tup: tup[0])
672 elif isinstance(index, list):
676 raise TypeError(
"index must be an int or list of ints")
679 '''index can be a integer'''
680 for n, s
in enumerate(self.segs):
685 self.segs[n] = s[:-1]
687 i = self.segs[n].index(index)
689 self.segs.append(s[i+1:])
690 for n
in range(len(self.segs)):
692 if len(self.segs[n]) == 0:
694 self.segs.sort(key=
lambda tup: tup[0])
697 ''' Returns a flatten list '''
698 return [item
for sublist
in self.segs
for item
in sublist]
702 for seg
in self.segs:
703 ret_tmp += str(seg[0])+
"-"+str(seg[-1])+
","
704 ret = ret_tmp[:-1]+
"]"
712 def normal_density_function(expected_value, sigma, x):
714 1 / math.sqrt(2 * math.pi) / sigma *
715 math.exp(-(x - expected_value) ** 2 / 2 / sigma / sigma)
719 def log_normal_density_function(expected_value, sigma, x):
721 1 / math.sqrt(2 * math.pi) / sigma / x *
722 math.exp(-(math.log(x / expected_value) ** 2 / 2 / sigma / sigma))
726 def print_multicolumn(list_of_strings, ncolumns=2, truncate=40):
732 for i
in range(len(ls) % cols):
735 split = [ls[i:i + len(ls) // cols]
736 for i
in range(0, len(ls), len(ls) // cols)]
737 for row
in zip(*split):
738 print(
"".join(str.ljust(i, truncate)
for i
in row))
742 '''Change color code to hexadecimal to rgb'''
744 self._NUMERALS =
'0123456789abcdefABCDEF'
745 self._HEXDEC = dict((v, int(v, 16))
for v
in
746 (x+y
for x
in self._NUMERALS
747 for y
in self._NUMERALS))
748 self.LOWERCASE, self.UPPERCASE =
'x',
'X'
750 def rgb(self, triplet):
751 return (float(self._HEXDEC[triplet[0:2]]),
752 float(self._HEXDEC[triplet[2:4]]),
753 float(self._HEXDEC[triplet[4:6]]))
755 def triplet(self, rgb, lettercase=None):
756 if lettercase
is None:
757 lettercase = self.LOWERCASE
758 return format(rgb[0] << 16 | rgb[1] << 8 | rgb[2],
'06'+lettercase)
762 class OrderedSet(MutableSet):
764 def __init__(self, iterable=None):
766 end += [
None, end, end]
768 if iterable
is not None:
774 def __contains__(self, key):
775 return key
in self.map
778 if key
not in self.map:
781 curr[2] = end[1] = self.map[key] = [key, curr, end]
783 def discard(self, key):
785 key, prev, next = self.map.pop(key)
792 while curr
is not end:
796 def __reversed__(self):
799 while curr
is not end:
803 def pop(self, last=True):
805 raise KeyError(
'set is empty')
815 return '%s()' % (self.__class__.__name__,)
816 return '%s(%r)' % (self.__class__.__name__, list(self))
818 def __eq__(self, other):
819 if isinstance(other, OrderedSet):
820 return len(self) == len(other)
and list(self) == list(other)
821 return set(self) == set(other)
825 """Store objects in order they were added, but with default type.
826 Source: http://stackoverflow.com/a/4127426/2608793
828 def __init__(self, *args, **kwargs):
830 self.default_factory =
None
832 if not (args[0]
is None or callable(args[0])):
833 raise TypeError(
'first argument must be callable or None')
834 self.default_factory = args[0]
836 super().__init__(*args, **kwargs)
838 def __missing__(self, key):
839 if self.default_factory
is None:
841 self[key] = default = self.default_factory()
844 def __reduce__(self):
845 args = (self.default_factory,)
if self.default_factory
else ()
846 return self.__class__, args,
None,
None, self.items()
852 """Extract frame from RMF file and fill coordinates. Must be identical
855 @param hier The (System) hierarchy to fill (e.g. after you've built it)
856 @param rmf_fn The file to extract from
857 @param frame_num The frame number to extract
859 rh = RMF.open_rmf_file_read_only(rmf_fn)
865 def input_adaptor(stuff, pmi_resolution=0, flatten=False, selection_tuple=None,
866 warn_about_slices=
True):
867 """Adapt things for PMI (degrees of freedom, restraints, ...)
868 Returns list of list of hierarchies, separated into Molecules if possible.
869 The input can be a list, or a list of lists (iterable of ^1 or
871 (iterable of ^2) Hierarchy -> returns input as list of list of hierarchies,
872 only one entry, not grouped by molecules.
873 (iterable of ^2) PMI::System/State/Molecule/TempResidue ->
874 returns residue hierarchies, grouped in molecules, at requested
877 @param stuff Can be one of the following inputs:
878 IMP Hierarchy, PMI System/State/Molecule/TempResidue, or a
879 list/set (of list/set) of them.
880 Must be uniform input, however. No mixing object types.
881 @param pmi_resolution For selecting, only does it if you pass PMI
882 objects. Set it to "all" if you want all resolutions!
883 @param flatten Set to True if you just want all hierarchies in one list.
884 @param warn_about_slices Print a warning if you are requesting only part
885 of a bead. Sometimes you just don't care!
886 @note since this relies on IMP::atom::Selection, this will not return
887 any objects if they weren't built! But there should be no problem
888 if you request unbuilt residues - they should be ignored.
894 if hasattr(stuff,
'__iter__'):
897 thelist = list(stuff)
900 if all(hasattr(el,
'__iter__')
for el
in thelist):
901 thelist = [i
for sublist
in thelist
for i
in sublist]
902 elif any(hasattr(el,
'__iter__')
for el
in thelist):
903 raise Exception(
'input_adaptor: input_object must be a list '
904 'or a list of lists')
913 except (NotImplementedError, TypeError):
925 if is_system
or is_state
or is_molecule
or is_temp_residue:
932 for state
in system.get_states():
933 mdict = state.get_molecules()
934 for molname
in mdict:
935 for copy
in mdict[molname]:
936 indexes_per_mol[copy] += \
937 [r.get_index()
for r
in copy.get_residues()]
940 mdict = state.get_molecules()
941 for molname
in mdict:
942 for copy
in mdict[molname]:
943 indexes_per_mol[copy] += [r.get_index()
944 for r
in copy.get_residues()]
946 for molecule
in stuff:
947 indexes_per_mol[molecule] += [r.get_index()
948 for r
in molecule.get_residues()]
950 for tempres
in stuff:
951 indexes_per_mol[tempres.get_molecule()].append(
953 for mol
in indexes_per_mol:
954 if pmi_resolution ==
'all':
958 mol.get_hierarchy(), residue_indexes=indexes_per_mol[mol])
961 resolution=pmi_resolution,
962 residue_indexes=indexes_per_mol[mol])
963 ps = sel.get_selected_particles()
966 if warn_about_slices:
967 rset = set(indexes_per_mol[mol])
977 resbreak = maxf
if minf == minset
else minset-1
979 'You are trying to select only part of the '
980 'bead %s:%i-%i. The residues you requested '
981 'are %i-%i. You can fix this by: '
982 '1) requesting the whole bead/none of it; or'
983 '2) break the bead up by passing '
984 'bead_extra_breaks=[\'%i\'] in '
985 'molecule.add_representation()'
986 % (mol.get_name(), minset, maxset, minf, maxf,
992 if pmi_resolution ==
'all':
998 h, resolution=pmi_resolution).get_selected_particles()
1001 hier_list = [hier_list]
1003 raise Exception(
'input_adaptor: you passed something of wrong type '
1004 'or a list with mixed types')
1006 if flatten
and pmi_input:
1007 return [h
for sublist
in hier_list
for h
in sublist]
1013 """Returns sequence-sorted segments array, each containing the first
1014 particle the last particle and the first residue index."""
1016 from operator
import itemgetter
1019 raise ValueError(
"only pass stuff from one Molecule, please")
1034 segs.append((start, end, startres))
1035 return sorted(segs, key=itemgetter(2))
1039 """Decorate the sequence-consecutive particles from a PMI2 molecule
1040 with a bond, so that they appear connected in the rmf file"""
1042 for x
in range(len(SortedSegments) - 1):
1044 last = SortedSegments[x][1]
1045 first = SortedSegments[x + 1][0]
1047 p1 = last.get_particle()
1048 p2 = first.get_particle()
1061 """ Just get the leaves from a list of hierarchies """
1062 lvs = list(itertools.chain.from_iterable(
1068 """Perform selection using the usual keywords but return ALL
1069 resolutions (BEADS and GAUSSIANS).
1070 Returns in flat list!
1075 if hier
is not None:
1078 warnings.warn(
"You passed nothing to select_at_all_resolutions()",
1086 raise Exception(
'select_at_all_resolutions: you have to pass '
1089 raise Exception(
'select_at_all_resolutions: you have to pass '
1091 if 'resolution' in kwargs
or 'representation_type' in kwargs:
1092 raise Exception(
"don't pass resolution or representation_type "
1095 representation_type=IMP.atom.BALLS,
1098 representation_type=IMP.atom.DENSITIES,
1100 ret |= OrderedSet(selB.get_selected_particles())
1101 ret |= OrderedSet(selD.get_selected_particles())
1110 """Utility to retrieve particles from a hierarchy within a
1111 zone around a set of ps.
1112 @param hier The hierarchy in which to look for neighbors
1113 @param target_ps The particles for zoning
1114 @param sel_zone The maximum distance
1115 @param entire_residues If True, will grab entire residues
1116 @param exclude_backbone If True, will only return sidechain particles
1120 backbone_types = [
'C',
'N',
'CB',
'O']
1121 if exclude_backbone:
1124 test_ps = test_sel.get_selected_particles()
1125 nn = IMP.algebra.NearestNeighbor3D([
IMP.core.XYZ(p).get_coordinates()
1128 for target
in target_ps:
1129 zone |= set(nn.get_in_ball(
IMP.core.XYZ(target).get_coordinates(),
1131 zone_ps = [test_ps[z]
for z
in zone]
1136 zone_ps = [h.get_particle()
for h
in final_ps]
1141 """Returns unique objects in original order"""
1145 if not hasattr(hiers,
'__iter__'):
1152 rbs_ordered.append(rb)
1157 rbs_ordered.append(rb)
1161 return rbs_ordered, beads
1165 "This function returns the parent molecule hierarchies of given objects"
1166 stuff =
input_adaptor(input_objects, pmi_resolution=
'all', flatten=
True)
1171 while not (is_molecule
or is_root):
1172 root = IMP.atom.get_root(h)
1179 return list(molecules)
1182 def get_molecules_dictionary(input_objects):
1183 moldict = defaultdict(list)
1185 name = mol.get_name()
1186 moldict[name].append(mol)
1193 def get_molecules_dictionary_by_copy(input_objects):
1194 moldict = defaultdict(dict)
1196 name = mol.get_name()
1198 moldict[name][c] = mol
1202 def get_selections_dictionary(input_objects):
1203 moldict = IMP.pmi.tools.get_molecules_dictionary(input_objects)
1204 seldict = defaultdict(list)
1205 for name, mols
in moldict.items():
1212 """Given a list of PMI objects, returns all density hierarchies within
1213 these objects. The output of this function can be inputted into
1214 things such as EM restraints. This function is intended to gather
1215 density particles appended to molecules (and not other hierarchies
1216 which might have been appended to the root node directly).
1225 i, representation_type=IMP.atom.DENSITIES).get_selected_particles()
1230 max_translation=300., max_rotation=2.0 * math.pi,
1231 avoidcollision_rb=
True, avoidcollision_fb=
False,
1232 cutoff=10.0, niterations=100,
1234 excluded_rigid_bodies=[],
1235 hierarchies_excluded_from_collision=[],
1236 hierarchies_included_in_collision=[],
1238 return_debug=
False):
1239 """Shuffle particles. Used to restart the optimization.
1240 The configuration of the system is initialized by placing each
1241 rigid body and each bead randomly in a box. If `bounding_box` is
1242 specified, the particles are placed inside this box; otherwise, each
1243 particle is displaced by up to max_translation angstroms, and randomly
1244 rotated. Effort is made to place particles far enough from each other to
1245 prevent any steric clashes.
1246 @param objects Can be one of the following inputs:
1247 IMP Hierarchy, PMI System/State/Molecule/TempResidue, or
1249 @param max_translation Max translation (rbs and flexible beads)
1250 @param max_rotation Max rotation (rbs only)
1251 @param avoidcollision_rb check if the particle/rigid body was
1252 placed close to another particle; uses the optional
1253 arguments cutoff and niterations
1254 @param avoidcollision_fb Advanced. Generally you want this False because
1255 it's hard to shuffle beads.
1256 @param cutoff Distance less than this is a collision
1257 @param niterations How many times to try avoiding collision
1258 @param bounding_box Only shuffle particles within this box.
1259 Defined by ((x1,y1,z1),(x2,y2,z2)).
1260 @param excluded_rigid_bodies Don't shuffle these rigid body objects
1261 @param hierarchies_excluded_from_collision Don't count collision
1263 @param hierarchies_included_in_collision Hierarchies that are not
1264 shuffled, but should be included in collision calculation
1266 @param verbose Give more output
1267 @note Best to only call this function after you've set up degrees
1269 For debugging purposes, returns: <shuffled indexes>,
1270 <collision avoided indexes>
1275 pmi_resolution=
'all',
1278 if len(rigid_bodies) > 0:
1279 mdl = rigid_bodies[0].get_model()
1280 elif len(flexible_beads) > 0:
1281 mdl = flexible_beads[0].get_model()
1283 raise Exception(
"Could not find any particles in the hierarchy")
1284 if len(rigid_bodies) == 0:
1285 print(
"shuffle_configuration: rigid bodies were not initialized")
1289 gcpf.set_distance(cutoff)
1293 hierarchies_excluded_from_collision, pmi_resolution=
'all',
1297 hierarchies_included_in_collision, pmi_resolution=
'all', flatten=
True)
1299 collision_excluded_idxs = set(
1301 for h
in collision_excluded_hierarchies
1304 collision_included_idxs = set(
1306 for h
in collision_included_hierarchies
1313 all_idxs.append(p.get_particle_index())
1315 collision_excluded_idxs.add(p.get_particle_index())
1317 if bounding_box
is not None:
1318 ((x1, y1, z1), (x2, y2, z2)) = bounding_box
1319 ub = IMP.algebra.Vector3D(x1, y1, z1)
1320 lb = IMP.algebra.Vector3D(x2, y2, z2)
1323 all_idxs = set(all_idxs) | collision_included_idxs
1324 all_idxs = all_idxs - collision_excluded_idxs
1326 print(
'shuffling', len(rigid_bodies),
'rigid bodies')
1327 for rb
in rigid_bodies:
1328 if rb
not in excluded_rigid_bodies:
1330 if avoidcollision_rb:
1331 rb_idxs = set(rb.get_member_particle_indexes()) - \
1332 collision_excluded_idxs
1333 other_idxs = all_idxs - rb_idxs
1335 debug.append([rb, other_idxs
if avoidcollision_rb
else set()])
1338 while niter < niterations:
1339 rbxyz = (rb.get_x(), rb.get_y(), rb.get_z())
1356 rbxyz, max_translation, max_rotation)
1361 if avoidcollision_rb
and other_idxs:
1363 npairs = len(gcpf.get_close_pairs(mdl,
1371 print(
"shuffle_configuration: rigid body placed "
1372 "close to other %d particles, trying "
1373 "again..." % npairs)
1374 print(
"shuffle_configuration: rigid body name: "
1376 if niter == niterations:
1378 "tried the maximum number of iterations to "
1379 "avoid collisions, increase the distance "
1384 print(
'shuffling', len(flexible_beads),
'flexible beads')
1385 for fb
in flexible_beads:
1387 if avoidcollision_fb:
1389 other_idxs = all_idxs - fb_idxs
1395 while niter < niterations:
1402 fbxyz, max_translation, max_rotation)
1407 xyz = memb.get_internal_coordinates()
1412 rf = memb.get_rigid_body().get_reference_frame()
1413 glob_to_int = rf.get_transformation_from()
1414 memb.set_internal_coordinates(
1415 glob_to_int.get_transformed(translation))
1417 xyz_transformed = transformation.get_transformed(xyz)
1418 memb.set_internal_coordinates(xyz_transformed)
1421 [xyz, other_idxs
if avoidcollision_fb
else set()])
1429 -d.get_coordinates())
1435 [d, other_idxs
if avoidcollision_fb
else set()])
1442 if avoidcollision_fb:
1444 npairs = len(gcpf.get_close_pairs(mdl,
1452 print(
"shuffle_configuration: floppy body placed close "
1453 "to other %d particles, trying again..." % npairs)
1454 if niter == niterations:
1456 "tried the maximum number of iterations to avoid "
1457 "collisions, increase the distance cutoff")
1464 class ColorHierarchy:
1466 def __init__(self, hier):
1467 import matplotlib
as mpl
1469 import matplotlib.pyplot
as plt
1473 hier.ColorHierarchy = self
1478 self.method = self.nochange
1486 def get_color(self, fl):
1489 def get_log_scale(self, fl):
1492 return math.log(fl+eps)
1494 def color_by_resid(self):
1495 self.method = self.color_by_resid
1496 self.scheme = self.mpl.cm.rainbow
1497 for mol
in self.mols:
1504 c = self.get_color(float(ri)/self.last)
1508 avr = sum(ris)/len(ris)
1509 c = self.get_color(float(avr)/self.last)
1512 def color_by_uncertainty(self):
1513 self.method = self.color_by_uncertainty
1514 self.scheme = self.mpl.cm.jet
1521 self.first = self.get_log_scale(1.0)
1522 self.last = self.get_log_scale(100.0)
1524 value = self.get_log_scale(unc_dict[p])
1525 if value >= self.last:
1527 if value <= self.first:
1529 c = self.get_color((value-self.first) / (self.last-self.first))
1532 def get_color_bar(self, filename):
1533 import matplotlib
as mpl
1535 import matplotlib.pyplot
as plt
1537 fig = plt.figure(figsize=(8, 3))
1538 ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
1541 norm = mpl.colors.Normalize(vmin=0.0, vmax=1.0)
1543 if self.method == self.color_by_uncertainty:
1544 angticks = [1.0, 2.5, 5.0, 10.0, 25.0, 50.0, 100.0]
1548 vvalue = (self.get_log_scale(at)-self.first) \
1549 / (self.last-self.first)
1550 if vvalue <= 1.0
and vvalue >= 0.0:
1551 vvalues.append(vvalue)
1552 marks.append(str(at))
1553 cb1 = mpl.colorbar.ColorbarBase(
1554 ax1, cmap=cmap, norm=norm, ticks=vvalues,
1555 orientation=
'horizontal')
1556 print(self.first, self.last, marks, vvalues)
1557 cb1.ax.set_xticklabels(marks)
1558 cb1.set_label(
'Angstorm')
1559 plt.savefig(filename, dpi=150, transparent=
True)
1564 """Given a Chimera color name or hex color value, return RGB"""
1565 d = {
'aquamarine': (0.4980392156862745, 1.0, 0.8313725490196079),
1566 'black': (0.0, 0.0, 0.0),
1567 'blue': (0.0, 0.0, 1.0),
1568 'brown': (0.6470588235, 0.16470588235294117, 0.16470588235294117),
1569 'chartreuse': (0.4980392156862745, 1.0, 0.0),
1570 'coral': (1.0, 0.4980392156862745, 0.3137254901960784),
1571 'cornflower blue': (0.39215686, 0.58431372549, 0.9294117647058824),
1572 'cyan': (0.0, 1.0, 1.0),
1573 'dark cyan': (0.0, 0.5450980392156862, 0.5450980392156862),
1574 'dark gray': (0.6627450980, 0.6627450980392157, 0.6627450980392157),
1575 'dark green': (0.0, 0.39215686274509803, 0.0),
1576 'dark khaki': (0.74117647, 0.7176470588235294, 0.4196078431372549),
1577 'dark magenta': (0.5450980392156862, 0.0, 0.5450980392156862),
1578 'dark olive green': (0.333333333, 0.419607843, 0.1843137254901961),
1579 'dark red': (0.5450980392156862, 0.0, 0.0),
1580 'dark slate blue': (0.28235294, 0.239215686, 0.5450980392156862),
1581 'dark slate gray': (0.1843137, 0.30980392, 0.30980392156862746),
1582 'deep pink': (1.0, 0.0784313725490196, 0.5764705882352941),
1583 'deep sky blue': (0.0, 0.7490196078431373, 1.0),
1584 'dim gray': (0.41176470, 0.4117647058823529, 0.4117647058823529),
1585 'dodger blue': (0.11764705882352941, 0.5647058823529412, 1.0),
1586 'firebrick': (0.6980392, 0.13333333333333333, 0.13333333333333333),
1587 'forest green': (0.13333333, 0.5450980392156862, 0.13333333333333333),
1588 'gold': (1.0, 0.8431372549019608, 0.0),
1589 'goldenrod': (0.85490196, 0.6470588235294118, 0.12549019607843137),
1590 'gray': (0.7450980392156863, 0.7450980392156863, 0.7450980392156863),
1591 'green': (0.0, 1.0, 0.0),
1592 'hot pink': (1.0, 0.4117647058823529, 0.7058823529411765),
1593 'khaki': (0.9411764705882353, 0.9019607843137255, 0.5490196078431373),
1594 'light blue': (0.67843137, 0.8470588235294118, 0.9019607843137255),
1595 'light gray': (0.82745098, 0.8274509803921568, 0.8274509803921568),
1596 'light green': (0.56470588, 0.9333333333333333, 0.5647058823529412),
1597 'light sea green': (0.125490, 0.6980392156862745, 0.6666666666666666),
1598 'lime green': (0.1960784, 0.803921568627451, 0.19607843137254902),
1599 'magenta': (1.0, 0.0, 1.0),
1600 'medium blue': (0.1960784, 0.19607843137254902, 0.803921568627451),
1601 'medium purple': (0.57647, 0.4392156862745098, 0.8588235294117647),
1602 'navy blue': (0.0, 0.0, 0.5019607843137255),
1603 'olive drab': (0.4196078, 0.5568627450980392, 0.13725490196078433),
1604 'orange red': (1.0, 0.27058823529411763, 0.0),
1605 'orange': (1.0, 0.4980392156862745, 0.0),
1606 'orchid': (0.85490196, 0.4392156862745098, 0.8392156862745098),
1607 'pink': (1.0, 0.7529411764705882, 0.796078431372549),
1608 'plum': (0.8666666666666667, 0.6274509803921569, 0.8666666666666667),
1609 'purple': (0.62745098, 0.12549019607843137, 0.9411764705882353),
1610 'red': (1.0, 0.0, 0.0),
1611 'rosy brown': (0.7372549, 0.5607843137254902, 0.5607843137254902),
1612 'salmon': (0.980392, 0.5019607843137255, 0.4470588235294118),
1613 'sandy brown': (0.956862745, 0.6431372549019608, 0.3764705882352941),
1614 'sea green': (0.18039, 0.5450980392156862, 0.3411764705882353),
1615 'sienna': (0.6274509, 0.3215686274509804, 0.17647058823529413),
1616 'sky blue': (0.52941176, 0.807843137254902, 0.9215686274509803),
1617 'slate gray': (0.439215686, 0.50196078, 0.5647058823529412),
1618 'spring green': (0.0, 1.0, 0.4980392156862745),
1619 'steel blue': (0.2745098, 0.50980392, 0.70588235),
1620 'tan': (0.8235294117647058, 0.7058823529411765, 0.5490196078431373),
1621 'turquoise': (0.25098039, 0.87843137, 0.81568627),
1622 'violet red': (0.81568627, 0.125490196, 0.56470588235),
1623 'white': (1.0, 1.0, 1.0),
1624 'yellow': (1.0, 1.0, 0.0)}
1625 if colorname.startswith(
'#'):
1626 return tuple(int(colorname[i:i+2], 16) / 255.
for i
in (1, 3, 5))
1632 """Score a single restraint, for use in stat files.
1633 Every time this object is called, it returns the weighted score
1634 of the restraint for the current model configuration. This is
1635 primarily used in a PMI restraint's get_output() method to obtain
1636 restraint scores for stat file output.
1638 @param name The name of the score in the stat file.
1639 @param weight_object A Python object with a 'weight' attribute
1640 (usually a PMI restraint) which is used to weight the score.
1641 @param restraint The IMP Restraint to score.
1643 def __init__(self, name, weight_object, restraint):
1644 self.name, self.restraint = name, restraint
1645 self._weight_object = weight_object
1646 self._jax_score =
None
1649 """Get the score for the given JAX data if `jax_data` is given,
1650 otherwise for the current IMP Model."""
1651 if jax_data
is not None:
1652 if self._jax_score
is None:
1654 ji = self.restraint._get_jax(space=jax_data.space)
1655 self._jax_score = jax.jit(ji.score_func)
1656 return self._jax_score(jax_data.model)
1658 weight = self._weight_object.weight
1659 return weight * self.restraint.unprotected_evaluate(
None)
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.
Extends the functionality of IMP.atom.Molecule.
def add_script_provenance
Tag the given particle with the current Python script.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Set of Python classes to create a multi-state, multi-resolution IMP hierarchy.
static Weight setup_particle(Model *m, ParticleIndex pi)
Set up an empty Weight.
A decorator for a particle which has bonds.
Rotation3D get_random_rotation_3d(const Rotation3D ¢er, double distance)
Pick a rotation at random near the provided one.
An exception for an invalid usage of IMP.
std::string get_module_version()
Return the version of this module, as a string.
static Surface setup_particle(Model *m, ParticleIndex pi)
Add uncertainty to a particle.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
static bool get_is_setup(const IMP::ParticleAdaptor &p)
GenericHierarchies get_leaves(Hierarchy mhd)
Get all the leaves of the bit of hierarchy.
Represent the root node of the global IMP.atom.Hierarchy.
def add_imp_provenance
Tag the given particle as being created by the current version of IMP.
Vector3D get_random_vector_in(const Cylinder3D &c)
Generate a random vector in a cylinder with uniform density.
Bond create_bond(Bonded a, Bonded b, Bond o)
Connect the two wrapped particles by a custom bond.
Object used to hold a set of restraints.
Stores a named protein chain.
static bool get_is_setup(Model *m, ParticleIndex pi)
def add_software_provenance
Tag the given particle with the software used to create it.
A decorator for keeping track of copies of a molecule.
ParticleIndexPairs get_indexes(const ParticlePairsTemp &ps)
Get the indexes from a list of particle pairs.
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.
void transform(XYZ a, const algebra::Transformation3D &tr)
Apply a transformation to the particle.
static Bonded setup_particle(Model *m, ParticleIndex pi)
void load_frame(RMF::FileConstHandle file, RMF::FrameID frame)
Load the given RMF frame into the state of the linked objects.
A decorator for a particle with x,y,z coordinates.
static Scale setup_particle(Model *m, ParticleIndex pi)
A decorator for a particle that is part of a rigid body but not rigid.
Find all nearby pairs by testing all pairs.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
A decorator for a residue.
DensityGrid get_grid(IMP::em::DensityMap *in)
Return a dense grid containing the voxels of the passed density map.
General purpose algebraic and geometric methods that are expected to be used by a wide variety of IMP...
static bool get_is_setup(const IMP::ParticleAdaptor &p)
static bool get_is_setup(Model *m, ParticleIndex p)
Check if the particle has the needed attributes for a cast to succeed.
The general base class for IMP exceptions.
Rotation3D get_identity_rotation_3d()
Return a rotation that does not do anything.
void link_hierarchies(RMF::FileConstHandle fh, const atom::Hierarchies &hs)
Class to handle individual particles of a Model object.
Bond get_bond(Bonded a, Bonded b)
Get the bond between two particles.
Stores a list of Molecules all with the same State index.
std::string get_data_path(std::string file_name)
Return the full path to one of this module's data files.
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.
static bool get_is_setup(Model *m, ParticleIndex pi)
A decorator for a rigid body.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Hierarchies get_leaves(const Selection &h)
A decorator for a molecule.
Select hierarchy particles identified by the biological name.
Support for the RMF file format for storing hierarchical molecular data and markup.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Warning for probably incorrect input parameters.
Transformation3D get_random_local_transformation(Vector3D origin, double max_translation=5., double max_angle_in_rad=0.26)
Get a local transformation.
Temporarily stores residue information, even without structure available.
Inferential scoring building on methods developed as part of the Inferential Structure Determination ...