1 """@namespace IMP.pmi.restraints
2 Classes to handle different kinds of restraints.
4 PMI restraints generally wrap IMP restraints. Typical features in PMI restraints are:
5 - Easy setup: for example, you can usually create one with a PMI [Molecule](@ref IMP::pmi::topology::Molecule) or a slice from one.
6 - Fast setup from data files. For example you can set up the [CrossLinkingMassSpectrometryRestraint](@ref IMP::pmi::restraints::crosslinking::CrossLinkingMassSpectrometryRestraint) by reading in a cross-link file into a [database](@ref IMP::pmi::io::crosslink::CrossLinkDataBase).
7 - Useful output: reporting functions which are put into log files when running [ReplicaExchange](@ref IMP::pmi::macros::ReplicaExchange).
17 _include_in_rmf =
False
19 """Base class for PMI restraints, which wrap `IMP.Restraint`(s)."""
21 def __init__(self, m, name=None, label=None, weight=1.,
24 @param m The model object
25 @param name The name of the primary restraint set that is wrapped.
26 This is used for outputs and particle/restraint names
27 and should be set by the child class.
28 @param label A unique label to be used in outputs and
29 particle/restraint names.
30 @param weight The weight to apply to all internal restraints.
31 @param restraint_set_class The class to use for the restraint set
34 self.restraint_sets = []
35 self._label_is_set =
False
38 self._label_suffix =
""
42 self.name = self.__class__.__name__
46 self.rs = self._create_restraint_set(name=
None,
47 cls=restraint_set_class)
49 def set_label(self, label):
50 """Set the unique label used in outputs and particle/restraint names.
53 if self._label_is_set:
54 raise ValueError(
"Label has already been set, or restraint has "
55 "already been added to model.")
58 self._label_suffix =
""
60 self._label = str(label)
61 self._label_suffix =
"_" + self._label
62 self._label_is_set =
True
68 def set_weight(self, weight):
69 """Set the weight to apply to all internal restraints.
73 for rs
in self.restraint_sets:
74 rs.set_weight(self.weight)
76 def add_to_model(self):
77 """Add the restraint to the model."""
78 self._label_is_set =
True
79 for rs
in self.restraint_sets:
81 self.model, rs, add_to_rmf=self._include_in_rmf)
84 """Evaluate the score of the restraint."""
85 self._label_is_set =
True
86 return self.weight * self.rs.unprotected_evaluate(
None)
88 def get_restraint_set(self):
89 """Get the primary restraint set."""
90 self._label_is_set =
True
93 def get_restraint(self):
94 """Get the primary restraint set. Identical to `get_restraint_set`."""
95 return self.get_restraint_set()
97 def get_restraint_for_rmf(self):
98 """Get the restraint for visualization in an RMF file."""
99 self._label_is_set =
True
102 def get_particles_to_sample(self):
103 """Get any created particles which should be sampled."""
104 self._label_is_set =
True
107 def get_output(self):
108 """Get outputs to write to stat files."""
109 self._label_is_set =
True
110 scorer = RestraintStatScorer(
"_TotalScore", self, self.rs)
111 suffix =
"_Score" + self._label_suffix
112 scorers = [RestraintStatScorer(rs.get_name() + suffix, self, rs)
113 for rs
in self.restraint_sets] + [scorer]
115 return lambda jm: {s.name: str(s(jm))
for s
in scorers}
117 def _create_restraint_set(self, name=None, cls=IMP.RestraintSet):
118 """Create ``IMP.RestraintSet``."""
122 name = self.name +
"_" + str(name)
123 rs = cls(self.model, name)
124 rs.set_weight(self.weight)
125 self.restraint_sets.append(rs)
126 rs.set_was_used(
True)
130 class _RestraintNuisanceMixin:
132 """Mix-in to add nuisance particle creation functionality to restraint.
134 This class must only be inherited if also inheriting
135 IMP.pmi.restraints.RestraintBase.
138 def __init__(self, *args, **kwargs):
139 super().__init__(*args, **kwargs)
140 self.sampled_nuisances = {}
143 def _create_nuisance(self, init_val, min_val, max_val, max_trans, name,
145 """Create nuisance particle.
146 @param init_val Initial value of nuisance
147 @param min_val Minimum value of nuisance
148 @param max_val Maximum value of nuisance
149 @param max_trans Maximum move to apply to nuisance
150 @param name Name of particle
151 @param is_sampled Nuisance is a sampled particle
152 @see IMP.pmi.tools.SetupNuisance
154 nuis = IMP.pmi.tools.SetupNuisance(
155 self.model, init_val, min_val, max_val,
156 isoptimized=is_sampled).get_particle()
157 nuis_name = self.name +
"_" + name
158 nuis.set_name(nuis_name)
159 self.nuisances[nuis_name] = nuis
161 self.sampled_nuisances[nuis_name] = (nuis, max_trans)
164 def get_particles_to_sample(self):
165 """Get any created particles which should be sampled."""
166 ps = super().get_particles_to_sample()
167 for name, (nuis, max_trans)
in self.sampled_nuisances.items():
168 ps[
"Nuisances_" + name + self._label_suffix] = ([nuis], max_trans)
171 def get_output(self):
172 """Get outputs to write to stat files."""
173 super_output = super().get_output()
176 output = super_output(jm)
177 for nuis_name, nuis
in self.nuisances.items():
178 output[nuis_name + self._label_suffix] = str(nuis.get_scale())
183 class _NuisancesBase:
185 """This base class is used to provide nuisance setup and interface
186 for the ISD cross-link restraints"""
188 sigma_dictionary = {}
191 def create_length(self):
192 """Create a nuisance on the length of the cross-link."""
194 self.lengthissampled =
True
195 lengthminnuis = 0.0000001
196 lengthmaxnuis = 1000.0
199 length = IMP.pmi.tools.SetupNuisance(self.m, lengthinit,
200 lengthminnuis, lengthmaxnuis,
203 self.rslen.add_restraint(
211 def create_sigma(self, resolution):
212 """Create a nuisance on the structural uncertainty."""
213 if isinstance(resolution, str):
216 sigmainit = resolution + 2.0
217 self.sigmaissampled =
True
218 sigmaminnuis = 0.0000001
219 sigmamaxnuis = 1000.0
223 sigma = IMP.pmi.tools.SetupNuisance(self.m, sigmainit, sigmaminnuis,
224 sigmamaxnuis, self.sigmaissampled
226 self.sigma_dictionary[resolution] = (
230 self.rssig.add_restraint(
239 def get_sigma(self, resolution):
240 """Get the nuisance on structural uncertainty."""
241 if resolution
not in self.sigma_dictionary:
242 self.create_sigma(resolution)
243 return self.sigma_dictionary[resolution]
245 def create_psi(self, value):
246 """Create a nuisance on the inconsistency."""
247 if isinstance(value, str):
251 self.psiissampled =
True
252 psiminnuis = 0.0000001
253 psimaxnuis = 0.4999999
257 psi = IMP.pmi.tools.SetupNuisance(self.m, psiinit,
258 psiminnuis, psimaxnuis,
259 self.psiissampled).get_particle()
260 self.psi_dictionary[value] = (
264 self.rspsi.add_restraint(
273 def get_psi(self, value):
274 """Get the nuisance on the inconsistency."""
275 if value
not in self.psi_dictionary:
276 self.create_psi(value)
277 return self.psi_dictionary[value]
Object used to hold a set of restraints.
Python classes to represent, score, sample and analyze models.