1 """@namespace IMP.mmcif.restraint
2 @brief Map IMP restraints to mmCIF categories
10 def _get_by_residue(m, p):
11 """Determine whether the given particle represents a specific residue
12 or a more coarse-grained object."""
18 """Get the numerical value of the Scale particle"""
20 raise ValueError(
"not scale particle")
25 """Map ParticleIndexes to ihm.AsymUnit"""
26 def __init__(self, m, components, ignore_non_structural=False):
28 self.components = components
29 self._seen_ranges = {}
30 self._ignore_non_structural = ignore_non_structural
32 def __getitem__(self, pi):
42 if not self._ignore_non_structural:
43 raise KeyError(
"Could not find top-level Chain for "
44 + m.get_particle_name(pi))
46 def get_feature(self, ps):
47 """Get an ihm.restraint.Feature that covers the given particles"""
56 rng = asym(rind, rind)
60 rng = asym(rinds[0], rinds[-1])
62 raise ValueError(
"Unsupported particle type %s" % str(p))
64 if len(rngs) > 0
and rngs[-1].asym == asym \
65 and rngs[-1].seq_id_range[1] == rng.seq_id_range[0] - 1:
66 rngs[-1].seq_id_range = (rngs[-1].seq_id_range[0],
73 if hrngs
in self._seen_ranges:
74 return self._seen_ranges[hrngs]
76 feat = ihm.restraint.ResidueFeature(rngs)
77 self._seen_ranges[hrngs] = feat
81 def _parse_restraint_info(info):
82 """Convert RestraintInfo object to Python dict"""
86 info.set_was_used(
True)
87 for typ
in (
'int',
'float',
'string',
'filename',
'floats',
'filenames',
89 for i
in range(getattr(info,
'get_number_of_' + typ)()):
90 key = getattr(info,
'get_%s_key' % typ)(i)
91 value = getattr(info,
'get_%s_value' % typ)(i)
96 def _get_restraint_assembly(imp_restraint, components):
97 """Get the assembly corresponding to all input particles for
99 asym_map = _AsymMapper(imp_restraint.get_model(), components,
100 ignore_non_structural=
True)
104 asyms = sorted((a
for a
in asyms
if a
is not None),
105 key=operator.attrgetter(
'id'))
109 class _EM3DRestraint(ihm.restraint.EM3DRestraint):
110 def __init__(self, imp_restraint, info, components, system):
113 asyms = _get_restraint_assembly(imp_restraint, components)
115 assembly = ihm.Assembly(
116 asyms, name=
"EM subassembly",
117 description=
"All components that fit the EM map")
119 self._filename = info[
'filename']
120 self._asyms = tuple(asyms)
121 p = IMP.mmcif.metadata._GMMParser()
122 r = p.parse_file(info[
'filename'])
124 dataset=r[
'dataset'], assembly=assembly,
125 number_of_gaussians=r[
'number_of_gaussians'],
126 fitting_method=
'Gaussian mixture model')
128 def _get_signature(self):
129 return (
"EM3DRestraint", self._filename, self._asyms,
130 self.number_of_gaussians, self.fitting_method)
132 def add_model_fit(self, imp_restraint, model):
133 info = _parse_restraint_info(imp_restraint.get_dynamic_info())
134 self.fits[model] = ihm.restraint.EM3DRestraintFit(
135 cross_correlation_coefficient=info[
'cross correlation'])
138 def _make_em2d_restraint(imp_restraint, info, components, system):
139 for i
in range(len(info[
'image files'])):
140 yield _EM2DRestraint(imp_restraint, info, components, i)
143 class _EM2DRestraint(ihm.restraint.EM2DRestraint):
144 def __init__(self, imp_restraint, info, components, image_number):
145 asyms = _get_restraint_assembly(imp_restraint, components)
147 assembly = ihm.Assembly(
148 asyms, name=
"2D EM subassembly",
149 description=
"All components that fit the EM images")
151 self._image_number = image_number
152 self._filename = info[
'image files'][image_number]
153 self._asyms = tuple(asyms)
155 loc = ihm.location.InputFileLocation(
157 details=
"Electron microscopy class average")
158 dataset = ihm.dataset.EM2DClassDataset(loc)
161 dataset=dataset, assembly=assembly,
163 number_raw_micrographs=info[
'micrographs number']
or None,
164 pixel_size_width=info[
'pixel size'],
165 pixel_size_height=info[
'pixel size'],
166 image_resolution=info[
'resolution'],
167 number_of_projections=info[
'projection number'])
169 def _get_signature(self):
170 return (
"EM2DRestraint", self._filename, self._asyms,
171 self.number_raw_micrographs, self.pixel_size_width,
172 self.pixel_size_height, self.image_resolution,
173 self.number_of_projections)
175 def add_model_fit(self, imp_restraint, model):
176 info = _parse_restraint_info(imp_restraint.get_dynamic_info())
177 ccc = info[
'cross correlation'][self._image_number]
178 transform = self._get_transformation(model, info, self._image_number)
179 rot = transform.get_rotation()
180 rm = [[e
for e
in rot.get_rotation_matrix_row(i)]
for i
in range(3)]
181 self.fits[model] = ihm.restraint.EM2DRestraintFit(
182 cross_correlation_coefficient=ccc, rot_matrix=rm,
183 tr_vector=transform.get_translation())
185 def _get_transformation(self, model, info, nimage):
186 """Get the transformation that places the model on image nimage"""
187 r = info[
'rotation'][nimage * 4: nimage * 4 + 4]
188 t = info[
'translation'][nimage * 3: nimage * 3 + 3]
193 class _SAXSRestraint(ihm.restraint.SASRestraint):
194 def __init__(self, imp_restraint, info, components, system):
195 asyms = _get_restraint_assembly(imp_restraint, components)
197 assembly = ihm.Assembly(
198 asyms, name=
"SAXS subassembly",
199 description=
"All components that fit the SAXS profile")
201 self._filename = info[
'filename']
202 self._asyms = tuple(asyms)
203 loc = ihm.location.InputFileLocation(
204 info[
'filename'], details=
'SAXS profile')
205 dataset = ihm.dataset.SASDataset(loc)
207 dataset=dataset, assembly=assembly,
208 segment=
False, fitting_method=
'IMP SAXS restraint',
209 fitting_atom_type=info[
'form factor type'],
212 def _get_signature(self):
213 return (
"SAXSRestraint", self._filename, self._asyms,
214 self.segment, self.fitting_method, self.fitting_atom_type,
217 def add_model_fit(self, imp_restraint, model):
219 self.fits[model] = ihm.restraint.SASRestraintFit(chi_value=
None)
222 class _CrossLinkRestraint(ihm.restraint.CrossLinkRestraint):
223 def __init__(self, imp_restraint, info, components, system):
225 loc = ihm.location.InputFileLocation(
226 info[
'filename'], details=
'Crosslinks')
227 dataset = ihm.dataset.CXMSDataset(loc)
228 linker = ihm.ChemDescriptor(
229 auth_name=info[
'linker author name'],
230 chemical_name=info.get(
'linker chemical name'),
231 smiles=info.get(
'linker smiles'),
232 smiles_canonical=info.get(
'linker smiles canonical'),
233 inchi=info.get(
'linker inchi'),
234 inchi_key=info.get(
'linker inchi key'))
236 dataset=dataset, linker=linker)
238 cmap = {e.description: e
for e
in system.entities}
239 dist = ihm.restraint.UpperBoundDistanceRestraint(info[
'linker length'])
240 asym = _AsymMapper(imp_restraint.get_model(), components)
241 self._add_all_links(IMP.RestraintSet.get_from(imp_restraint), cmap,
244 def _add_all_links(self, rset, cmap, asym, dist):
245 """Add info for each cross-link in the given RestraintSet"""
246 for link
in rset.restraints:
249 child_rs = IMP.RestraintSet.get_from(link)
253 self._add_all_links(child_rs, cmap, asym, dist)
255 info = _parse_restraint_info(link.get_static_info())
257 r1 = cmap[info[
'protein1']].residue(info[
'residue1'])
258 r2 = cmap[info[
'protein2']].residue(info[
'residue2'])
259 ex_xl = ihm.restraint.ExperimentalCrossLink(residue1=r1,
261 self.experimental_cross_links.append([ex_xl])
264 endp1, endp2 = info[
'endpoints']
267 if _get_by_residue(m, endp1)
and _get_by_residue(m, endp2):
268 cls = ihm.restraint.ResidueCrossLink
270 cls = ihm.restraint.FeatureCrossLink
271 xl = cls(ex_xl, asym1=asym1, asym2=asym2, distance=dist,
273 psi=_get_scale(m, info[
'psis'][0]),
274 sigma1=_get_scale(m, info[
'sigmas'][0]),
275 sigma2=_get_scale(m, info[
'sigmas'][1]))
276 self.cross_links.append(xl)
278 def _get_signature(self):
282 return (
"CXMSRestraint", tuple(self._info.items()),
283 len(self.cross_links), len(self.experimental_cross_links))
285 def add_model_fit(self, imp_restraint, model):
289 class _GeometricRestraint(ihm.restraint.GeometricRestraint):
290 """Base for all geometric restraints"""
292 def __init__(self, imp_restraint, info, components, system):
294 asym = _AsymMapper(imp_restraint.get_model(), components)
297 geometric_object=self._geom_object,
298 feature=asym.get_feature(
300 distance=self._get_distance(info),
301 harmonic_force_constant=1. / info[
'sigma'],
304 def _get_distance(self, info):
307 def add_model_fit(self, imp_restraint, model):
310 def _get_signature(self):
311 return (
"GeometricRestraint", self.feature, self.geometric_object,
312 tuple(self._info.items()))
315 class _ZAxialRestraint(_GeometricRestraint):
316 """Handle an IMP.npc.ZAxialRestraint"""
317 _geom_object = ihm.geometry.XYPlane()
319 def _get_distance(self, info):
320 return ihm.restraint.LowerUpperBoundDistanceRestraint(
321 info[
'lower bound'], info[
'upper bound'])
324 class _AllRestraints:
325 """Map IMP restraints to mmCIF objects"""
327 "IMP.isd.GaussianEMRestraint": _EM3DRestraint,
328 "IMP.pmi.CrossLinkingMassSpectrometryRestraint": _CrossLinkRestraint,
329 "IMP.em2d.PCAFitRestraint": _make_em2d_restraint,
330 "IMP.npc.ZAxialPositionRestraint": _ZAxialRestraint,
331 "IMP.saxs.Restraint": _SAXSRestraint}
333 def __init__(self, system, components):
334 self._system = system
335 self._components = components
336 self._seen_restraints = {}
338 def add(self, restraint):
339 """Add and return a new restraint"""
340 sig = restraint._get_signature()
341 if sig
not in self._seen_restraints:
342 self._system.restraints.append(restraint)
343 self._seen_restraints[sig] = restraint
344 return self._seen_restraints[sig]
346 def handle(self, restraint, ihm_models):
347 """Handle an individual IMP restraint.
348 Yield wrapped version(s) of the restraint if it is handled in
349 mmCIF (one IMP restraint may map to multiple mmCIF restraints).
350 These may be new or existing restraint objects."""
351 info = _parse_restraint_info(restraint.get_static_info())
352 if 'type' in info
and info[
'type']
in self._typemap:
353 cif_rs = self._typemap[info[
'type']](restraint,
354 info, self._components,
361 cif_r = self.add(cif_r)
362 for model
in ihm_models:
363 cif_r.add_model_fit(restraint, model)
static bool get_is_setup(const IMP::ParticleAdaptor &p)
A decorator to associate a particle with a part of a protein/DNA/RNA.
static bool get_is_setup(const IMP::ParticleAdaptor &p)
Add scale parameter to particle.
static bool get_is_setup(Model *m, ParticleIndex pi)
The standard decorator for manipulating molecular structures.
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
static bool get_is_setup(const IMP::ParticleAdaptor &p)
ParticlesTemp get_input_particles(const ModelObjectsTemp &mos)
Return all the input particles for a given ModelObject.
A decorator for a residue.
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.
Store info for a chain of a protein.