IMP logo
IMP Reference Guide  develop.63b38c487d,2024/12/21
The Integrative Modeling Platform
saxs.py
1 """@namespace IMP.pmi.restraints.saxs
2 Restraints for handling small angle x-ray (SAXS) data.
3 """
4 
5 import math
6 import IMP
7 import IMP.core
8 import IMP.algebra
9 import IMP.atom
10 import IMP.container
11 import IMP.pmi.tools
12 import IMP.pmi.restraints
13 import IMP.saxs
14 import IMP.isd
15 try:
16  import IMP.isd2
17 except ImportError:
18  pass
19 import warnings
20 
21 
22 class SAXSRestraint(IMP.pmi.restraints.RestraintBase):
23  """Basic SAXS restraint."""
24 
25  _include_in_rmf = True
26 
27  def __init__(self, input_objects, saxs_datafile, weight=1.0,
28  ff_type=IMP.saxs.HEAVY_ATOMS, label=None, maxq="standard"):
29  """Builds the restraint.
30  @param input_objects A list of hierarchies or PMI objects that the
31  SAXS restraint will be applied to. This hierarchy must be
32  atomic unless ff_type=IMP.saxs.RESIDUES is used.
33  @param saxs_datafile the SAXS .dat file.
34  @param weight Restraint score coefficient
35  @param ff_type the form factor to use, of the following types:
36  - IMP.saxs.HEAVY_ATOMS: use form factors with implicit
37  hydrogens
38  - IMP.saxs.ALL_ATOMS: use individual form factors for all
39  atoms. Does not build missing hydrogens.
40  - IMP.saxs.CA_ATOMS: use residue based form factors
41  centered at CA atoms
42  - IMP.saxs.RESIDUES: use residue based form factors
43  using per-residue beads
44  @param label Label for the restraint in outputs
45  @param maxq Maximum q value that the restraint will be evaluated at.
46  If set to 'standard' (the default), the following values will
47  be used (these values were eyeballed by comparing ALL_ATOM
48  calculated SAXS profiles to those calculated with the reduced
49  representations, so could be improved):
50  - For ff_type = ALL_ATOMS: 0.5
51  - HEAVY_ATOMS: 0.4
52  - CA_ATOMS and RESIDUES: 0.15
53  """
54  # Get all hierarchies.
55  hiers = IMP.pmi.tools.input_adaptor(input_objects,
56  flatten=True)
57  model = list(hiers)[0].get_model()
58  super().__init__(model, label=label, weight=weight)
59 
60  # Determine maxq to compare computed and experimental profiles
61  if maxq == "standard":
62  if ff_type == IMP.saxs.CA_ATOMS or ff_type == IMP.saxs.RESIDUES:
63  maxq = 0.15
64  elif ff_type == IMP.saxs.HEAVY_ATOMS:
65  maxq = 0.4
66  else:
67  maxq = 0.5
68  elif isinstance(maxq, float):
69  if maxq < 0.01 or maxq > 4.0:
70  raise Exception(
71  "SAXSRestraint: maxq must be set between 0.01 and 4.0")
72  if (ff_type == IMP.saxs.CA_ATOMS or ff_type == IMP.saxs.RESIDUES) \
73  and maxq > 0.15:
74  warnings.warn("SAXSRestraint: for residue-resolved form "
75  "factors, a maxq > 0.15 is not recommended!",
77  else:
78  raise Exception(
79  "SAXSRestraint: maxq must be set to a number between 0.01 "
80  "and 4.0")
81 
82  # Depending on the type of FF used, get the correct particles
83  # from the hierarchies list and create an IMP::saxs::Profile()
84  # at the appropriate maxq.
85  if ff_type == IMP.saxs.RESIDUES:
86  self.particles = IMP.atom.Selection(
87  hiers, resolution=1).get_selected_particles()
88  self.profile = IMP.saxs.Profile(saxs_datafile, False, maxq)
89  elif ff_type == IMP.saxs.CA_ATOMS:
90  self.particles = IMP.atom.Selection(
91  hiers, atom_type=IMP.atom.AT_CA).get_selected_particles()
92  self.profile = IMP.saxs.Profile(saxs_datafile, False, maxq)
93  elif ff_type == IMP.saxs.HEAVY_ATOMS or ff_type == IMP.saxs.ALL_ATOMS:
94  self.particles = IMP.atom.Selection(
95  hiers, resolution=0).get_selected_particles()
96  self.profile = IMP.saxs.Profile(saxs_datafile, False, maxq)
97  else:
98  raise Exception(
99  "SAXSRestraint: Must provide an IMP.saxs atom type: "
100  "RESIDUES, CA_ATOMS, HEAVY_ATOMS or ALL_ATOMS")
101 
102  if len(self.particles) == 0:
103  raise Exception("SAXSRestraint: There are no selected particles")
104 
105  self.restraint = IMP.saxs.Restraint(self.particles, self.profile,
106  ff_type)
107  self.rs.add_restraint(self.restraint)
108 
109 
110 class SAXSISDRestraint(IMP.pmi.restraints.RestraintBase):
111 
112  """Basic SAXS restraint using ISD."""
113 
114  def __init__(self, representation, profile, resolution=0, weight=1,
115  ff_type=IMP.saxs.HEAVY_ATOMS, label=None):
116 
117  if not hasattr(IMP, 'isd2'):
118  raise ImportError("Module isd2 not installed. "
119  "Cannot use SAXSISDRestraint")
120 
121  model = representation.prot.get_model()
122  super().__init__(model, label=label, weight=weight)
123 
124  self.taumaxtrans = 0.05
125  self.prof = IMP.saxs.Profile(profile)
126 
127  self.atoms = IMP.pmi.tools.select(
128  representation,
129  resolution=resolution)
130 
131  # gamma nuisance
132  self.gamma = IMP.pmi.tools.SetupNuisance(
133  self.model, 1., 0., None, False).get_particle()
134 
135  # sigma nuisance
136  self.sigma = IMP.pmi.tools.SetupNuisance(
137  self.model, 10.0, 0., None, False).get_particle()
138 
139  # tau nuisance, optimized
140  self.tau = IMP.pmi.tools.SetupNuisance(self.model, 1., 0., None, False,
141  ).get_particle()
142 
143  # c1 and c2, optimized
144  self.c1 = IMP.pmi.tools.SetupNuisance(self.model, 1.0, 0.95, 1.05,
145  True).get_particle()
146  self.c2 = IMP.pmi.tools.SetupNuisance(self.model, 0.0, -2., 4.,
147  True).get_particle()
148 
149  # weight, optimized
150  self.w = IMP.pmi.tools.SetupWeight(self.model).get_particle()
151  IMP.isd.Weight(self.w).set_weights_are_optimized(False)
152 
153  # take identity covariance matrix for the start
154  self.cov = [[1 if i == j else 0 for j in range(self.prof.size())]
155  for i in range(self.prof.size())]
156 
157  print("create saxs restraint")
158  self.saxs = IMP.isd2.SAXSRestraint(self.prof, self.sigma, self.tau,
159  self.gamma, self.w, self.c1,
160  self.c2)
161  self.saxs.add_scatterer(self.atoms, self.cov, ff_type)
162 
163  self.rs.add_restraint(self.saxs)
164 
165  # self.saxs_stuff={'nuis':(sigma,gamma),'cov':cov,
166  # 'exp':prof,'th':tmp}
167 
168  self.rs2 = self._create_restraint_set('Prior')
169  # jeffreys restraints for nuisances
170  j1 = IMP.isd.JeffreysRestraint(self.model, self.sigma)
171  self.rs2.add_restraint(j1)
172  j2 = IMP.isd.JeffreysRestraint(self.model, self.tau)
173  self.rs2.add_restraint(j2)
174  j3 = IMP.isd.JeffreysRestraint(self.model, self.gamma)
175  self.rs2.add_restraint(j3)
176 
177  pw = IMP.isd.Weight(self.w)
178  pw.set_weights(pw.get_unit_simplex().get_barycenter())
179  pw.set_weights_are_optimized(True)
180 
181  def optimize_sigma(self):
182  """Set sigma to the value that maximizes its conditional likelihood"""
183  self.model.update()
184  sigma2hat = self.saxs.get_sigmasq_scale_parameter() \
185  / (self.saxs.get_sigmasq_shape_parameter() + 1)
186  IMP.isd.Scale(self.sigma).set_scale(math.sqrt(sigma2hat))
187 
188  def optimize_gamma(self):
189  """Set gamma to the value that maximizes its conditional likelihood"""
190  self.model.update()
191  gammahat = math.exp(self.saxs.get_loggamma_variance_parameter() *
192  self.saxs.get_loggamma_jOg_parameter())
193  IMP.isd.Scale(self.gamma).set_scale(gammahat)
194 
195  def optimize_tau(self, ltaumin=-2, ltaumax=3, npoints=100):
196  values = []
197  self.model.update()
198  IMP.atom.write_pdb(self.atoms, 'tauvals.pdb')
199  fl = open('tauvals.txt', 'w')
200  for tauval in self._logspace(ltaumin, ltaumax, npoints):
201  IMP.isd.Scale(self.tau).set_scale(tauval)
202  try:
203  values.append((self.model.evaluate(False), tauval))
204  except: # noqa: E722
205  pass
206  fl.write('%G %G\n' % (values[-1][1], values[-1][0]))
207  values.sort()
208  ltcenter = math.log(values[0][1]) / math.log(10)
209  spacing = (ltaumax - ltaumin) / float(npoints)
210  values = []
211  for tauval in self._logspace(
212  ltcenter - 2 * spacing, ltcenter + 2 * spacing,
213  npoints):
214  IMP.isd.Scale(self.tau).set_scale(tauval)
215  values.append((self.model.evaluate(False), tauval))
216  fl.write('%G %G\n' % (values[-1][1], values[-1][0]))
217  values.sort()
218  IMP.isd.Scale(self.tau).set_scale(values[0][1])
219 
220  def get_gamma_value(self):
221  """Get value of gamma."""
222  return self.gamma.get_scale()
223 
224  def set_taumaxtrans(self, taumaxtrans):
225  self.taumaxtrans = taumaxtrans
226 
227  def draw_sigma(self):
228  """Draw 1/sigma2 from gamma distribution."""
229  self.model.update()
230  self.saxs.draw_sigma()
231 
232  def draw_gamma(self):
233  """Draw gamma from lognormal distribution."""
234  self.model.update()
235  self.saxs.draw_gamma()
236 
237  def update_covariance_matrix(self):
238  c1 = IMP.isd.Nuisance(self.c1).get_nuisance()
239  c2 = IMP.isd.Nuisance(self.c2).get_nuisance()
240  # tau = IMP.isd.Nuisance(self.tau).get_nuisance()
241  tau = 1.0
242  self.cov = IMP.isd2.compute_relative_covariance(self.atoms, c1, c2,
243  tau, self.prof)
244  # for i in xrange(len(self.cov)):
245  # for j in xrange(len(self.cov)):
246  # self.cov[i][j] = self.cov[i][j]/tau**2
247  self.saxs.set_cov(0, self.cov)
248 
249  def write_covariance_matrix(self, fname):
250  fl = open(fname, 'w')
251  for line in self.cov:
252  for i in line:
253  fl.write('%G ' % i)
254  fl.write('\n')
255 
256  def get_output(self):
257  output = super().get_output()
258  suffix = self._get_label_suffix()
259  output["SAXSISDRestraint_Sigma" +
260  suffix] = str(self.sigma.get_scale())
261  output["SAXSISDRestraint_Tau" + suffix] = str(self.tau.get_scale())
262  output["SAXSISDRestraint_Gamma" +
263  suffix] = str(self.gamma.get_scale())
264  return output
265 
266  @staticmethod
267  def _logspace(a, b, num=100):
268  """Mimic numpy's logspace function"""
269  for i in range(num):
270  val = a + float(b - a) / float(num - 1) * i
271  yield 10 ** val
Add weights to a particle.
Definition: Weight.h:29
Various classes to hold sets of particles.
def optimize_gamma
Set gamma to the value that maximizes its conditional likelihood.
Definition: saxs.py:188
Calculate score based on fit to SAXS profile.
Miscellaneous utilities.
Definition: tools.py:1
void write_pdb(const Selection &mhd, TextOutput out, unsigned int model=1)
Add scale parameter to particle.
Definition: Scale.h:24
Classes to handle different kinds of restraints.
Basic SAXS restraint.
Definition: saxs.py:22
def input_adaptor
Adapt things for PMI (degrees of freedom, restraints, ...) Returns list of list of hierarchies...
Definition: tools.py:872
def optimize_sigma
Set sigma to the value that maximizes its conditional likelihood.
Definition: saxs.py:181
def draw_gamma
Draw gamma from lognormal distribution.
Definition: saxs.py:232
Add nuisance parameter to particle.
Definition: Nuisance.h:27
Basic SAXS restraint using ISD.
Definition: saxs.py:110
def get_gamma_value
Get value of gamma.
Definition: saxs.py:220
def __init__
Builds the restraint.
Definition: saxs.py:47
Basic functionality that is expected to be used by a wide variety of IMP users.
General purpose algebraic and geometric methods that are expected to be used by a wide variety of IMP...
The general base class for IMP exceptions.
Definition: exception.h:48
def draw_sigma
Draw 1/sigma2 from gamma distribution.
Definition: saxs.py:227
Functionality for loading, creating, manipulating and scoring atomic structures.
Select hierarchy particles identified by the biological name.
Definition: Selection.h:70
Support for small angle X-ray scattering (SAXS) data.
Warning for probably incorrect input parameters.
Inferential scoring building on methods developed as part of the Inferential Structure Determination ...