OpenStructure
scoring.py
Go to the documentation of this file.
1 import os
2 from ost import mol
3 from ost import seq
4 from ost import io
5 from ost import conop
6 from ost import settings
7 from ost import geom
8 from ost import LogScript, LogInfo, LogDebug
9 from ost.mol.alg import lddt
10 from ost.mol.alg import qsscore
11 from ost.mol.alg import chain_mapping
12 from ost.mol.alg import stereochemistry
13 from ost.mol.alg import dockq
14 from ost.mol.alg.lddt import lDDTScorer
15 from ost.mol.alg.qsscore import QSScorer
16 from ost.mol.alg.contact_score import ContactScorer
17 from ost.mol.alg.contact_score import ContactEntity
18 from ost.mol.alg import GDT
19 from ost.mol.alg import Molck, MolckSettings
20 from ost import bindings
21 from ost.bindings import cadscore
22 from ost.bindings import tmtools
23 import numpy as np
24 
26  """Scorer specific for a reference/model pair
27 
28  Finds best possible binding site representation of reference in model given
29  lDDT score. Uses :class:`ost.mol.alg.chain_mapping.ChainMapper` to deal with
30  chain mapping.
31 
32  :param reference: Reference structure
33  :type reference: :class:`ost.mol.EntityView`/:class:`ost.mol.EntityHandle`
34  :param model: Model structure
35  :type model: :class:`ost.mol.EntityView`/:class:`ost.mol.EntityHandle`
36  :param residue_number_alignment: Passed to ChainMapper constructor
37  :type residue_number_alignment: :class:`bool`
38  """
39  def __init__(self, reference, model,
40  residue_number_alignment=False):
41  self.chain_mapperchain_mapper = chain_mapping.ChainMapper(reference,
42  resnum_alignments=residue_number_alignment)
43  self.refref = self.chain_mapperchain_mapper.target
44  self.mdlmdl = model
45 
46  def ScoreBS(self, ligand, radius = 4.0, lddt_radius=10.0):
47  """Computes binding site lDDT score given *ligand*. Best possible
48  binding site representation is selected by lDDT but other scores such as
49  CA based RMSD and GDT are computed too and returned.
50 
51  :param ligand: Defines the scored binding site, i.e. provides positions
52  to perform proximity search
53  :type ligand: r'((Residue)|(Chain)|(Entity))((View)|(Handle))'
54  :param radius: Reference residues with any atom position within *radius*
55  of *ligand* consitute the scored binding site
56  :type radius: :class:`float`
57  :param lddt_radius: Passed as *inclusion_radius* to
58  :class:`ost.mol.alg.lddt.lDDTScorer`
59  :type lddt_radius: :class:`float`
60  :returns: Object of type :class:`ost.mol.alg.chain_mapping.ReprResult`
61  containing all atom lDDT score and mapping information.
62  None if no representation could be found.
63  """
64 
65  # create view of reference binding site
66  ref_residues_hashes = set() # helper to keep track of added residues
67  for ligand_at in ligand.atoms:
68  close_atoms = self.refref.FindWithin(ligand_at.GetPos(), radius)
69  for close_at in close_atoms:
70  ref_res = close_at.GetResidue()
71  h = ref_res.handle.GetHashCode()
72  if h not in ref_residues_hashes:
73  ref_residues_hashes.add(h)
74 
75  # reason for doing that separately is to guarantee same ordering of
76  # residues as in underlying entity. (Reorder by ResNum seems only
77  # available on ChainHandles)
78  ref_bs = self.refref.CreateEmptyView()
79  for ch in self.refref.chains:
80  for r in ch.residues:
81  if r.handle.GetHashCode() in ref_residues_hashes:
82  ref_bs.AddResidue(r, mol.ViewAddFlag.INCLUDE_ALL)
83 
84  # gogogo
85  bs_repr = self.chain_mapperchain_mapper.GetRepr(ref_bs, self.mdlmdl,
86  inclusion_radius = lddt_radius)
87  if len(bs_repr) >= 1:
88  return bs_repr[0]
89  else:
90  return None
91 
92 
93 class Scorer:
94  """ Helper class to access the various scores available from ost.mol.alg
95 
96  Deals with structure cleanup, chain mapping, interface identification etc.
97  Intermediate results are available as attributes.
98 
99  :param model: Model structure - a deep copy is available as :attr:`~model`.
100  Additionally, :func:`ost.mol.alg.Molck` using *molck_settings*
101  is applied.
102  :type model: :class:`ost.mol.EntityHandle`/:class:`ost.mol.EntityView`
103  :param target: Target structure - a deep copy is available as :attr:`~target`.
104  Additionally, :func:`ost.mol.alg.Molck` using *molck_settings*
105  is applied.
106  :type target: :class:`ost.mol.EntityHandle`/:class:`ost.mol.EntityView`
107  :param resnum_alignments: Whether alignments between chemically equivalent
108  chains in *model* and *target* can be computed
109  based on residue numbers. This can be assumed in
110  benchmarking setups such as CAMEO/CASP.
111  :type resnum_alignments: :class:`bool`
112  :param molck_settings: Settings used for Molck on *model* and *target*, if
113  set to None, a default object is constructed by
114  setting everything except rm_zero_occ_atoms and
115  colored to True in
116  :class:`ost.mol.alg.MolckSettings` constructor.
117  :type molck_settings: :class:`ost.mol.alg.MolckSettings`
118  :param cad_score_exec: Explicit path to voronota-cadscore executable from
119  voronota installation from
120  https://github.com/kliment-olechnovic/voronota. If
121  not given, voronota-cadscore must be in PATH if any
122  of the CAD score related attributes is requested.
123  :type cad_score_exec: :class:`str`
124  :param custom_mapping: Provide custom chain mapping between *model* and
125  *target*. Dictionary with target chain names as key
126  and model chain names as value.
127  :attr:`~mapping` is constructed from this.
128  :type custom_mapping: :class:`dict`
129  :param custom_rigid_mapping: Provide custom chain mapping between *model*
130  and *target*. Dictionary with target chain
131  names as key and model chain names as value.
132  :attr:`~rigid_mapping` is constructed from
133  this.
134  :type custom_rigid_mapping: :class:`dict`
135  :param usalign_exec: Explicit path to USalign executable used to compute
136  TM-score. If not given, TM-score will be computed
137  with OpenStructure internal copy of USalign code.
138  :type usalign_exec: :class:`str`
139  :param lddt_no_stereochecks: Whether to compute lDDT without stereochemistry
140  checks
141  :type lddt_no_stereochecks: :class:`bool`
142  :param n_max_naive: Parameter for chain mapping. If the number of possible
143  mappings is <= *n_max_naive*, the full
144  mapping solution space is enumerated to find the
145  the optimum. A heuristic is used otherwise. The default
146  of 40320 corresponds to an octamer (8! = 40320).
147  A structure with stoichiometry A6B2 would be
148  6!*2! = 1440 etc.
149  :type n_max_naive: :class:`int`
150  :param oum: Override USalign Mapping. Inject rigid_mapping of
151  :class:`Scorer` object into USalign to compute TM-score.
152  Experimental feature with limitations.
153  :type oum: :class:`bool`
154  :param min_pep_length: Relevant parameter if short peptides are involved in
155  scoring. Minimum peptide length for a chain in the
156  target structure to be considered in chain mapping.
157  The chain mapping algorithm first performs an all vs.
158  all pairwise sequence alignment to identify \"equal\"
159  chains within the target structure. We go for simple
160  sequence identity there. Short sequences can be
161  problematic as they may produce high sequence identity
162  alignments by pure chance.
163  :type min_pep_length: :class:`int`
164  :param min_nuc_length: Relevant parameter if short nucleotides are involved
165  in scoring. Minimum nucleotide length for a chain in
166  the target structure to be considered in chain
167  mapping. The chain mapping algorithm first performs
168  an all vs. all pairwise sequence alignment to
169  identify \"equal\" chains within the target
170  structure. We go for simple sequence identity there.
171  Short sequences can be problematic as they may
172  produce high sequence identity alignments by pure
173  chance.
174  :type min_nuc_length: :class:`int`
175  :param lddt_add_mdl_contacts: lDDT specific flag. Only using contacts in
176  lDDT that are within a certain distance
177  threshold in the target does not penalize
178  for added model contacts. If set to True, this
179  flag will also consider target contacts
180  that are within the specified distance
181  threshold in the model but not necessarily in
182  the target. No contact will be added if the
183  respective atom pair is not resolved in the
184  target.
185  :type lddt_add_mdl_contacts: :class:`bool`
186  :param dockq_capri_peptide: Flag that changes two things in the way DockQ
187  and its underlying scores are computed which is
188  proposed by the CAPRI community when scoring
189  peptides (PMID: 31886916).
190  ONE: Two residues are considered in contact if
191  any of their atoms is within 5A. This is
192  relevant for fnat and fnonat scores. CAPRI
193  suggests to lower this threshold to 4A for
194  protein-peptide interactions.
195  TWO: irmsd is computed on interface residues. A
196  residue is defined as interface residue if any
197  of its atoms is within 10A of another chain.
198  CAPRI suggests to lower the default of 10A to
199  8A in combination with only considering CB atoms
200  for protein-peptide interactions.
201  This flag has no influence on patch_dockq
202  scores.
203  :type dockq_capri_peptide: :class:`bool`
204  :param lddt_symmetry_settings: Passed as *symmetry_settings* parameter to
205  lDDT scorer. Default: None
206  :type lddt_symmetry_settings: :class:`ost.mol.alg.lddt.SymmetrySettings`
207  :param lddt_inclusion_radius: lDDT inclusion radius.
208  """
209  def __init__(self, model, target, resnum_alignments=False,
210  molck_settings = None, cad_score_exec = None,
211  custom_mapping=None, custom_rigid_mapping=None,
212  usalign_exec = None, lddt_no_stereochecks=False,
213  n_max_naive=40320, oum=False, min_pep_length = 6,
214  min_nuc_length = 4, lddt_add_mdl_contacts=False,
215  dockq_capri_peptide=False, lddt_symmetry_settings = None,
216  lddt_inclusion_radius = 15.0):
217 
218  self._target_orig_target_orig = target
219  self._model_orig_model_orig = model
220 
221  if isinstance(self._model_orig_model_orig, mol.EntityView):
222  self._model_model = mol.CreateEntityFromView(self._model_orig_model_orig, False)
223  else:
224  self._model_model = self._model_orig_model_orig.Copy()
225 
226  if isinstance(self._target_orig_target_orig, mol.EntityView):
227  self._target_target = mol.CreateEntityFromView(self._target_orig_target_orig, False)
228  else:
229  self._target_target = self._target_orig_target_orig.Copy()
230 
231  if molck_settings is None:
232  molck_settings = MolckSettings(rm_unk_atoms=True,
233  rm_non_std=False,
234  rm_hyd_atoms=True,
235  rm_oxt_atoms=True,
236  rm_zero_occ_atoms=False,
237  colored=False,
238  map_nonstd_res=True,
239  assign_elem=True)
240  LogScript("Cleaning up input structures")
241  Molck(self._model_model, conop.GetDefaultLib(), molck_settings)
242  Molck(self._target_target, conop.GetDefaultLib(), molck_settings)
243 
244  if resnum_alignments:
245  # If we're dealing with resnum alignments, we ensure that
246  # consecutive peptide and nucleotide residues are connected based
247  # on residue number information. The conop.Processor only connects
248  # these things if the bonds are actually feasible which can lead to
249  # weird behaviour in stereochemistry checks. Let's say N and C are
250  # too close, it's reported as a clash. If they're too far apart,
251  # they're not reported at all. If we're not dealing with resnum
252  # alignments, we're out of luck as we have no direct residue
253  # connectivity information from residue numbers
254  self._resnum_connect_resnum_connect(self._model_model)
255  self._resnum_connect_resnum_connect(self._target_target)
256 
257  self._model_model = self._model_model.Select("peptide=True or nucleotide=True")
258  self._target_target = self._target_target.Select("peptide=True or nucleotide=True")
259 
260  # catch models which have empty chain names
261  for ch in self._model_model.chains:
262  if ch.GetName().strip() == "":
263  raise RuntimeError("Model chains must have valid chain names")
264  if ch.GetName().strip() == "'" or ch.GetName().strip() == '"':
265  raise RuntimeError("Model chains cannot be named with quote signs (' or \"\")")
266 
267  # catch targets which have empty chain names
268  for ch in self._target_target.chains:
269  if ch.GetName().strip() == "":
270  raise RuntimeError("Target chains must have valid chain names")
271  if ch.GetName().strip() == "'" or ch.GetName().strip() == '"':
272  raise RuntimeError("Target chains cannot be named with quote signs (' or \"\")")
273 
274  if resnum_alignments:
275  # In case of resnum_alignments, we have some requirements on
276  # residue numbers in the chain mapping: 1) no ins codes 2) strictly
277  # increasing residue numbers.
278  for ch in self._model_model.chains:
279  ins_codes = [r.GetNumber().GetInsCode() for r in ch.residues]
280  if len(set(ins_codes)) != 1 or ins_codes[0] != '\0':
281  raise RuntimeError("Residue numbers in each model chain "
282  "must not contain insertion codes if "
283  "resnum_alignments are enabled")
284  nums = [r.GetNumber().GetNum() for r in ch.residues]
285  if not all(i < j for i, j in zip(nums, nums[1:])):
286  raise RuntimeError("Residue numbers in each model chain "
287  "must be strictly increasing if "
288  "resnum_alignments are enabled")
289 
290  for ch in self._target_target.chains:
291  ins_codes = [r.GetNumber().GetInsCode() for r in ch.residues]
292  if len(set(ins_codes)) != 1 or ins_codes[0] != '\0':
293  raise RuntimeError("Residue numbers in each target chain "
294  "must not contain insertion codes if "
295  "resnum_alignments are enabled")
296  nums = [r.GetNumber().GetNum() for r in ch.residues]
297  if not all(i < j for i, j in zip(nums, nums[1:])):
298  raise RuntimeError("Residue numbers in each target chain "
299  "must be strictly increasing if "
300  "resnum_alignments are enabled")
301 
302  if usalign_exec is not None:
303  if not os.path.exists(usalign_exec):
304  raise RuntimeError(f"USalign exec ({usalign_exec}) "
305  f"not found")
306  if not os.access(usalign_exec, os.X_OK):
307  raise RuntimeError(f"USalign exec ({usalign_exec}) "
308  f"is not executable")
309 
310  self.resnum_alignmentsresnum_alignments = resnum_alignments
311  self.cad_score_execcad_score_exec = cad_score_exec
312  self.usalign_execusalign_exec = usalign_exec
313  self.lddt_no_stereocheckslddt_no_stereochecks = lddt_no_stereochecks
314  self.n_max_naiven_max_naive = n_max_naive
315  self.oumoum = oum
316  self.min_pep_lengthmin_pep_length = min_pep_length
317  self.min_nuc_lengthmin_nuc_length = min_nuc_length
318  self.lddt_add_mdl_contactslddt_add_mdl_contacts = lddt_add_mdl_contacts
319  self.dockq_capri_peptidedockq_capri_peptide = dockq_capri_peptide
320  self.lddt_symmetry_settingslddt_symmetry_settings = lddt_symmetry_settings
321  self.lddt_inclusion_radiuslddt_inclusion_radius = lddt_inclusion_radius
322 
323  # lazily evaluated attributes
324  self._stereochecked_model_stereochecked_model = None
325  self._stereochecked_target_stereochecked_target = None
326  self._model_clashes_model_clashes = None
327  self._model_bad_bonds_model_bad_bonds = None
328  self._model_bad_angles_model_bad_angles = None
329  self._target_clashes_target_clashes = None
330  self._target_bad_bonds_target_bad_bonds = None
331  self._target_bad_angles_target_bad_angles = None
332  self._trimmed_model_trimmed_model = None
333  self._chain_mapper_chain_mapper = None
334  self._mapping_mapping = None
335  self._rigid_mapping_rigid_mapping = None
336  self._model_interface_residues_model_interface_residues = None
337  self._target_interface_residues_target_interface_residues = None
338  self._aln_aln = None
339  self._stereochecked_aln_stereochecked_aln = None
340  self._pepnuc_aln_pepnuc_aln = None
341  self._trimmed_aln_trimmed_aln = None
342 
343  # lazily constructed scorer objects
344  self._lddt_scorer_lddt_scorer = None
345  self._bb_lddt_scorer_bb_lddt_scorer = None
346  self._qs_scorer_qs_scorer = None
347  self._contact_scorer_contact_scorer = None
348  self._trimmed_contact_scorer_trimmed_contact_scorer = None
349 
350  # lazily computed scores
351  self._lddt_lddt = None
352  self._local_lddt_local_lddt = None
353  self._aa_local_lddt_aa_local_lddt = None
354  self._bb_lddt_bb_lddt = None
355  self._bb_local_lddt_bb_local_lddt = None
356  self._ilddt_ilddt = None
357 
358  self._qs_global_qs_global = None
359  self._qs_best_qs_best = None
360  self._qs_target_interfaces_qs_target_interfaces = None
361  self._qs_model_interfaces_qs_model_interfaces = None
362  self._qs_interfaces_qs_interfaces = None
363  self._per_interface_qs_global_per_interface_qs_global = None
364  self._per_interface_qs_best_per_interface_qs_best = None
365 
366  self._contact_target_interfaces_contact_target_interfaces = None
367  self._contact_model_interfaces_contact_model_interfaces = None
368  self._native_contacts_native_contacts = None
369  self._model_contacts_model_contacts = None
370  self._trimmed_model_contacts_trimmed_model_contacts = None
371  self._ics_precision_ics_precision = None
372  self._ics_recall_ics_recall = None
373  self._ics_ics = None
374  self._per_interface_ics_precision_per_interface_ics_precision = None
375  self._per_interface_ics_recall_per_interface_ics_recall = None
376  self._per_interface_ics_per_interface_ics = None
377  self._ips_precision_ips_precision = None
378  self._ips_recall_ips_recall = None
379  self._ips_ips = None
380  self._per_interface_ips_precision_per_interface_ips_precision = None
381  self._per_interface_ips_recall_per_interface_ips_recall = None
382  self._per_interface_ips_per_interface_ips = None
383 
384  # subset of contact scores that operate on trimmed model
385  # i.e. no contacts from model residues that are not present in
386  # target
387  self._ics_trimmed_ics_trimmed = None
388  self._ics_precision_trimmed_ics_precision_trimmed = None
389  self._ics_recall_trimmed_ics_recall_trimmed = None
390  self._per_interface_ics_precision_trimmed_per_interface_ics_precision_trimmed = None
391  self._per_interface_ics_recall_trimmed_per_interface_ics_recall_trimmed = None
392  self._per_interface_ics_trimmed_per_interface_ics_trimmed = None
393  self._ips_trimmed_ips_trimmed = None
394  self._ips_precision_trimmed_ips_precision_trimmed = None
395  self._ips_recall_trimmed_ips_recall_trimmed = None
396  self._per_interface_ips_precision_trimmed_per_interface_ips_precision_trimmed = None
397  self._per_interface_ips_recall_trimmed_per_interface_ips_recall_trimmed = None
398  self._per_interface_ips_trimmed_per_interface_ips_trimmed = None
399 
400  self._dockq_target_interfaces_dockq_target_interfaces = None
401  self._dockq_interfaces_dockq_interfaces = None
402  self._fnat_fnat = None
403  self._fnonnat_fnonnat = None
404  self._irmsd_irmsd = None
405  self._lrmsd_lrmsd = None
406  self._nnat_nnat = None
407  self._nmdl_nmdl = None
408  self._dockq_scores_dockq_scores = None
409  self._dockq_ave_dockq_ave = None
410  self._dockq_wave_dockq_wave = None
411  self._dockq_ave_full_dockq_ave_full = None
412  self._dockq_wave_full_dockq_wave_full = None
413 
414  self._mapped_target_pos_mapped_target_pos = None
415  self._mapped_model_pos_mapped_model_pos = None
416  self._mapped_target_pos_full_bb_mapped_target_pos_full_bb = None
417  self._mapped_model_pos_full_bb_mapped_model_pos_full_bb = None
418  self._transformed_mapped_model_pos_transformed_mapped_model_pos = None
419  self._n_target_not_mapped_n_target_not_mapped = None
420  self._transform_transform = None
421 
422  self._rigid_mapped_target_pos_rigid_mapped_target_pos = None
423  self._rigid_mapped_model_pos_rigid_mapped_model_pos = None
424  self._rigid_mapped_target_pos_full_bb_rigid_mapped_target_pos_full_bb = None
425  self._rigid_mapped_model_pos_full_bb_rigid_mapped_model_pos_full_bb = None
426  self._rigid_transformed_mapped_model_pos_rigid_transformed_mapped_model_pos = None
427  self._rigid_n_target_not_mapped_rigid_n_target_not_mapped = None
428  self._rigid_transform_rigid_transform = None
429 
430  self._gdt_window_sizes_gdt_window_sizes = [7, 9, 12, 24, 48]
431  self._gdt_05_gdt_05 = None
432  self._gdt_1_gdt_1 = None
433  self._gdt_2_gdt_2 = None
434  self._gdt_4_gdt_4 = None
435  self._gdt_8_gdt_8 = None
436  self._gdtts_gdtts = None
437  self._gdtha_gdtha = None
438  self._rmsd_rmsd = None
439 
440  self._cad_score_cad_score = None
441  self._local_cad_score_local_cad_score = None
442 
443  self._patch_qs_patch_qs = None
444  self._patch_dockq_patch_dockq = None
445 
446  self._tm_score_tm_score = None
447  self._usalign_mapping_usalign_mapping = None
448 
449  if custom_mapping is not None:
450  self._mapping_mapping = self._construct_custom_mapping_construct_custom_mapping(custom_mapping)
451 
452  if custom_rigid_mapping is not None:
453  self._rigid_mapping_rigid_mapping = \
454  self._construct_custom_mapping_construct_custom_mapping(custom_rigid_mapping)
455  LogDebug("Scorer sucessfully initialized")
456 
457  @property
458  def model(self):
459  """ Model with Molck cleanup
460 
461  :type: :class:`ost.mol.EntityHandle`
462  """
463  return self._model_model
464 
465  @property
466  def model_orig(self):
467  """ The original model passed at object construction
468 
469  :type: :class:`ost.mol.EntityHandle`/:class:`ost.mol.EntityView`
470  """
471  return self._model_orig_model_orig
472 
473  @property
474  def target(self):
475  """ Target with Molck cleanup
476 
477  :type: :class:`ost.mol.EntityHandle`
478  """
479  return self._target_target
480 
481  @property
482  def target_orig(self):
483  """ The original target passed at object construction
484 
485  :type: :class:`ost.mol.EntityHandle`/:class:`ost.mol.EntityView`
486  """
487  return self._target_orig_target_orig
488 
489  @property
490  def aln(self):
491  """ Alignments of :attr:`~model`/:attr:`~target` chains
492 
493  Alignments for each pair of chains mapped in :attr:`~mapping`.
494  First sequence is target sequence, second sequence the model sequence.
495 
496  :type: :class:`list` of :class:`ost.seq.AlignmentHandle`
497  """
498  if self._aln_aln is None:
499  self._compute_aln_compute_aln()
500  return self._aln_aln
501 
502  @property
503  def stereochecked_aln(self):
504  """ Stereochecked equivalent of :attr:`~aln`
505 
506  The alignments may differ, as stereochecks potentially remove residues
507 
508  :type: :class:`list` of :class:`ost.seq.AlignmentHandle`
509  """
510  if self._stereochecked_aln_stereochecked_aln is None:
511  self._compute_stereochecked_aln_compute_stereochecked_aln()
512  return self._stereochecked_aln_stereochecked_aln
513 
514  @property
515  def pepnuc_aln(self):
516  """ Alignments of :attr:`~model_orig`/:attr:`~target_orig` chains
517 
518  Selects for peptide and nucleotide residues before sequence
519  extraction. Includes residues that would be removed by molck in
520  structure preprocessing.
521 
522  :type: :class:`list` of :class:`ost.seq.AlignmentHandle`
523  """
524  if self._pepnuc_aln_pepnuc_aln is None:
525  self._compute_pepnuc_aln_compute_pepnuc_aln()
526  return self._pepnuc_aln_pepnuc_aln
527 
528  @property
529  def trimmed_aln(self):
530  """ Alignments of :attr:`~trimmed_model`/:attr:`~target` chains
531 
532  Alignments for each pair of chains mapped in :attr:`~mapping`.
533  First sequence is target sequence, second sequence the model sequence.
534 
535  :type: :class:`list` of :class:`ost.seq.AlignmentHandle`
536  """
537  if self._trimmed_aln_trimmed_aln is None:
538  self._trim_model_trim_model()
539  return self._trimmed_aln_trimmed_aln
540 
541  @property
543  """ View of :attr:`~model` that has stereochemistry checks applied
544 
545  First, a selection for peptide/nucleotide residues is performed,
546  secondly peptide sidechains with stereochemical irregularities are
547  removed (full residue if backbone atoms are involved). Irregularities
548  are clashes or bond lengths/angles more than 12 standard deviations
549  from expected values.
550 
551  :type: :class:`ost.mol.EntityView`
552  """
553  if self._stereochecked_model_stereochecked_model is None:
554  self._do_stereochecks_do_stereochecks()
555  return self._stereochecked_model_stereochecked_model
556 
557  @property
558  def model_clashes(self):
559  """ Clashing model atoms
560 
561  :type: :class:`list` of :class:`ost.mol.alg.stereochemistry.ClashInfo`
562  """
563  if self._model_clashes_model_clashes is None:
564  self._do_stereochecks_do_stereochecks()
565  return self._model_clashes_model_clashes
566 
567  @property
568  def model_bad_bonds(self):
569  """ Model bonds with unexpected stereochemistry
570 
571  :type: :class:`list` of
572  :class:`ost.mol.alg.stereochemistry.BondViolationInfo`
573  """
574  if self._model_bad_bonds_model_bad_bonds is None:
575  self._do_stereochecks_do_stereochecks()
576  return self._model_bad_bonds_model_bad_bonds
577 
578  @property
579  def model_bad_angles(self):
580  """ Model angles with unexpected stereochemistry
581 
582  :type: :class:`list` of
583  :class:`ost.mol.alg.stereochemistry.AngleViolationInfo`
584  """
585  if self._model_bad_angles_model_bad_angles is None:
586  self._do_stereochecks_do_stereochecks()
587  return self._model_bad_angles_model_bad_angles
588 
589  @property
591  """ Same as :attr:`~stereochecked_model` for :attr:`~target`
592 
593  :type: :class:`ost.mol.EntityView`
594  """
595  if self._stereochecked_target_stereochecked_target is None:
596  self._do_stereochecks_do_stereochecks()
597  return self._stereochecked_target_stereochecked_target
598 
599  @property
600  def target_clashes(self):
601  """ Clashing target atoms
602 
603  :type: :class:`list` of :class:`ost.mol.alg.stereochemistry.ClashInfo`
604  """
605  if self._target_clashes_target_clashes is None:
606  self._do_stereochecks_do_stereochecks()
607  return self._target_clashes_target_clashes
608 
609  @property
610  def target_bad_bonds(self):
611  """ Target bonds with unexpected stereochemistry
612 
613  :type: :class:`list` of
614  :class:`ost.mol.alg.stereochemistry.BondViolationInfo`
615  """
616  if self._target_bad_bonds_target_bad_bonds is None:
617  self._do_stereochecks_do_stereochecks()
618  return self._target_bad_bonds_target_bad_bonds
619 
620  @property
621  def target_bad_angles(self):
622  """ Target angles with unexpected stereochemistry
623 
624  :type: :class:`list` of
625  :class:`ost.mol.alg.stereochemistry.AngleViolationInfo`
626  """
627  if self._target_bad_angles_target_bad_angles is None:
628  self._do_stereochecks_do_stereochecks()
629  return self._target_bad_angles_target_bad_angles
630 
631  @property
632  def trimmed_model(self):
633  """ :attr:`~model` trimmed to target
634 
635  Removes residues that are not covered by :class:`target` given
636  :attr:`~mapping`. In other words: no model residues without experimental
637  evidence from :class:`target`.
638 
639  :type: :class:`ost.mol.EntityView`
640  """
641  if self._trimmed_model_trimmed_model is None:
642  self._trim_model_trim_model()
643  return self._trimmed_model_trimmed_model
644 
645  @property
646  def chain_mapper(self):
647  """ Chain mapper object for given :attr:`~target`
648 
649  :type: :class:`ost.mol.alg.chain_mapping.ChainMapper`
650  """
651  if self._chain_mapper_chain_mapper is None:
652  self._chain_mapper_chain_mapper = chain_mapping.ChainMapper(self.targettarget,
653  n_max_naive=1e9,
654  resnum_alignments=self.resnum_alignmentsresnum_alignments,
655  min_pep_length=self.min_pep_lengthmin_pep_length,
656  min_nuc_length=self.min_nuc_lengthmin_nuc_length)
657  return self._chain_mapper_chain_mapper
658 
659  @property
660  def mapping(self):
661  """ Full chain mapping result for :attr:`~target`/:attr:`~model`
662 
663  Computed with :func:`ost.mol.alg.ChainMapper.GetMapping`
664 
665  :type: :class:`ost.mol.alg.chain_mapping.MappingResult`
666  """
667  if self._mapping_mapping is None:
668  LogScript("Computing chain mapping")
669  self._mapping_mapping = \
670  self.chain_mapperchain_mapper.GetMapping(self.modelmodel,
671  n_max_naive = self.n_max_naiven_max_naive)
672  return self._mapping_mapping
673 
674  @property
675  def rigid_mapping(self):
676  """ Full chain mapping result for :attr:`~target`/:attr:`~model`
677 
678  Computed with :func:`ost.mol.alg.ChainMapper.GetRMSDMapping`
679 
680  :type: :class:`ost.mol.alg.chain_mapping.MappingResult`
681  """
682  if self._rigid_mapping_rigid_mapping is None:
683  LogScript("Computing rigid chain mapping")
684  self._rigid_mapping_rigid_mapping = \
685  self.chain_mapperchain_mapper.GetRMSDMapping(self.modelmodel)
686  return self._rigid_mapping_rigid_mapping
687 
688  @property
690  """ Interface residues in :attr:`~model`
691 
692  Thats all residues having a contact with at least one residue from
693  another chain (CB-CB distance <= 8A, CA in case of Glycine)
694 
695  :type: :class:`dict` with chain names as key and and :class:`list`
696  with residue numbers of the respective interface residues.
697  """
698  if self._model_interface_residues_model_interface_residues is None:
699  self._model_interface_residues_model_interface_residues = \
700  self._get_interface_residues_get_interface_residues(self.modelmodel)
701  return self._model_interface_residues_model_interface_residues
702 
703  @property
705  """ Same as :attr:`~model_interface_residues` for :attr:`~target`
706 
707  :type: :class:`dict` with chain names as key and and :class:`list`
708  with residue numbers of the respective interface residues.
709  """
710  if self._target_interface_residues_target_interface_residues is None:
711  self._target_interface_residues_target_interface_residues = \
712  self._get_interface_residues_get_interface_residues(self.targettarget)
713  return self._target_interface_residues_target_interface_residues
714 
715  @property
716  def lddt_scorer(self):
717  """ lDDT scorer for :attr:`~target`/:attr:`~stereochecked_target`
718 
719  Depending on :attr:`~lddt_no_stereocheck` and
720  :attr:`~lddt_symmetry_settings`.
721 
722  :type: :class:`ost.mol.alg.lddt.lDDTScorer`
723  """
724  if self._lddt_scorer_lddt_scorer is None:
725  if self.lddt_no_stereocheckslddt_no_stereochecks:
726  self._lddt_scorer_lddt_scorer = lDDTScorer(self.targettarget,
727  symmetry_settings = self.lddt_symmetry_settingslddt_symmetry_settings,
728  inclusion_radius = self.lddt_inclusion_radiuslddt_inclusion_radius)
729  else:
730  self._lddt_scorer_lddt_scorer = lDDTScorer(self.stereochecked_targetstereochecked_target,
731  symmetry_settings = self.lddt_symmetry_settingslddt_symmetry_settings,
732  inclusion_radius = self.lddt_inclusion_radiuslddt_inclusion_radius)
733  return self._lddt_scorer_lddt_scorer
734 
735  @property
736  def bb_lddt_scorer(self):
737  """ Backbone only lDDT scorer for :attr:`~target`
738 
739  No stereochecks applied for bb only lDDT which considers CA atoms
740  for peptides and C3' atoms for nucleotides.
741 
742  :type: :class:`ost.mol.alg.lddt.lDDTScorer`
743  """
744  if self._bb_lddt_scorer_bb_lddt_scorer is None:
745  self._bb_lddt_scorer_bb_lddt_scorer = lDDTScorer(self.targettarget, bb_only=True,
746  symmetry_settings = self.lddt_symmetry_settingslddt_symmetry_settings,
747  inclusion_radius = self.lddt_inclusion_radiuslddt_inclusion_radius)
748  return self._bb_lddt_scorer_bb_lddt_scorer
749 
750  @property
751  def qs_scorer(self):
752  """ QS scorer constructed from :attr:`~mapping`
753 
754  The scorer object is constructed with default parameters and relates to
755  :attr:`~model` and :attr:`~target` (no stereochecks).
756 
757  :type: :class:`ost.mol.alg.qsscore.QSScorer`
758  """
759  if self._qs_scorer_qs_scorer is None:
760  self._qs_scorer_qs_scorer = QSScorer.FromMappingResult(self.mappingmapping)
761  return self._qs_scorer_qs_scorer
762 
763  @property
764  def contact_scorer(self):
765  if self._contact_scorer_contact_scorer is None:
766  self._contact_scorer_contact_scorer = ContactScorer.FromMappingResult(self.mappingmapping)
767  return self._contact_scorer_contact_scorer
768 
769  @property
771  if self._trimmed_contact_scorer_trimmed_contact_scorer is None:
772  self._trimmed_contact_scorer_trimmed_contact_scorer = ContactScorer(self.mappingmapping.target,
773  self.mappingmapping.chem_groups,
774  self.trimmed_modeltrimmed_model,
775  self.trimmed_alntrimmed_aln)
776  return self._trimmed_contact_scorer_trimmed_contact_scorer
777 
778  @property
779  def lddt(self):
780  """ Global lDDT score in range [0.0, 1.0]
781 
782  Computed based on :attr:`~stereochecked_model`. In case of oligomers,
783  :attr:`~mapping` is used.
784 
785  :type: :class:`float`
786  """
787  if self._lddt_lddt is None:
788  self._compute_lddt_compute_lddt()
789  return self._lddt_lddt
790 
791  @property
792  def local_lddt(self):
793  """ Per residue lDDT scores in range [0.0, 1.0]
794 
795  Computed based on :attr:`~stereochecked_model` but scores for all
796  residues in :attr:`~model` are reported. If a residue has been removed
797  by stereochemistry checks, the respective score is set to 0.0. If a
798  residue is not covered by the target or is in a chain skipped by the
799  chain mapping procedure (happens for super short chains), the respective
800  score is set to None. In case of oligomers, :attr:`~mapping` is used.
801 
802  :type: :class:`dict`
803  """
804  if self._local_lddt_local_lddt is None:
805  self._compute_lddt_compute_lddt()
806  return self._local_lddt_local_lddt
807 
808  @property
809  def aa_local_lddt(self):
810  """ Per atom lDDT scores in range [0.0, 1.0]
811 
812  Computed based on :attr:`~stereochecked_model` but scores for all
813  atoms in :attr:`~model` are reported. If an atom has been removed
814  by stereochemistry checks, the respective score is set to 0.0. If an
815  atom is not covered by the target or is in a chain skipped by the
816  chain mapping procedure (happens for super short chains), the respective
817  score is set to None. In case of oligomers, :attr:`~mapping` is used.
818 
819  :type: :class:`dict`
820  """
821  if self._aa_local_lddt_aa_local_lddt is None:
822  self._compute_lddt_compute_lddt()
823  return self._aa_local_lddt_aa_local_lddt
824 
825  @property
826  def bb_lddt(self):
827  """ Backbone only global lDDT score in range [0.0, 1.0]
828 
829  Computed based on :attr:`~model` on backbone atoms only. This is CA for
830  peptides and C3' for nucleotides. No stereochecks are performed. In case
831  of oligomers, :attr:`~mapping` is used.
832 
833  :type: :class:`float`
834  """
835  if self._bb_lddt_bb_lddt is None:
836  self._compute_bb_lddt_compute_bb_lddt()
837  return self._bb_lddt_bb_lddt
838 
839  @property
840  def bb_local_lddt(self):
841  """ Backbone only per residue lDDT scores in range [0.0, 1.0]
842 
843  Computed based on :attr:`~model` on backbone atoms only. This is CA for
844  peptides and C3' for nucleotides. No stereochecks are performed. If a
845  residue is not covered by the target or is in a chain skipped by the
846  chain mapping procedure (happens for super short chains), the respective
847  score is set to None. In case of oligomers, :attr:`~mapping` is used.
848 
849  :type: :class:`dict`
850  """
851  if self._bb_local_lddt_bb_local_lddt is None:
852  self._compute_bb_lddt_compute_bb_lddt()
853  return self._bb_local_lddt_bb_local_lddt
854 
855  @property
856  def ilddt(self):
857  """ Global interface lDDT score in range [0.0, 1.0]
858 
859  This is lDDT only based on inter-chain contacts. Value is None if no
860  such contacts are present. For example if we're dealing with a monomer.
861  Computed based on :attr:`~stereochecked_model` and :attr:`~mapping` for
862  chain mapping.
863 
864  :type: :class:`float`
865  """
866  if self._ilddt_ilddt is None:
867  # the whole None business kind of invalidates the idea of lazy
868  # evaluation. The assumption is that this is called only once...
869  self._compute_ilddt_compute_ilddt()
870  return self._ilddt_ilddt
871 
872 
873  @property
874  def qs_global(self):
875  """ Global QS-score
876 
877  Computed based on :attr:`~model` using :attr:`~mapping`
878 
879  :type: :class:`float`
880  """
881  if self._qs_global_qs_global is None:
882  self._compute_qs_compute_qs()
883  return self._qs_global_qs_global
884 
885  @property
886  def qs_best(self):
887  """ Global QS-score - only computed on aligned residues
888 
889  Computed based on :attr:`~model` using :attr:`~mapping`. The QS-score
890  computation only considers contacts between residues with a mapping
891  between target and model. As a result, the score won't be lowered in
892  case of additional chains/residues in any of the structures.
893 
894  :type: :class:`float`
895  """
896  if self._qs_best_qs_best is None:
897  self._compute_qs_compute_qs()
898  return self._qs_best_qs_best
899 
900  @property
902  """ Interfaces in :attr:`~target` with non-zero contribution to
903  :attr:`~qs_global`/:attr:`~qs_best`
904 
905  Chain names are lexicographically sorted.
906 
907  :type: :class:`list` of :class:`tuple` with 2 elements each:
908  (trg_ch1, trg_ch2)
909  """
910  if self._qs_target_interfaces_qs_target_interfaces is None:
911  self._qs_target_interfaces_qs_target_interfaces = self.qs_scorerqs_scorer.qsent1.interacting_chains
912  self._qs_target_interfaces_qs_target_interfaces = \
913  [(min(x[0],x[1]), max(x[0],x[1])) for x in self._qs_target_interfaces_qs_target_interfaces]
914  return self._qs_target_interfaces_qs_target_interfaces
915 
916  @property
918  """ Interfaces in :attr:`~model` with non-zero contribution to
919  :attr:`~qs_global`/:attr:`~qs_best`
920 
921  Chain names are lexicographically sorted.
922 
923  :type: :class:`list` of :class:`tuple` with 2 elements each:
924  (mdl_ch1, mdl_ch2)
925  """
926  if self._qs_model_interfaces_qs_model_interfaces is None:
927  self._qs_model_interfaces_qs_model_interfaces = self.qs_scorerqs_scorer.qsent2.interacting_chains
928  self._qs_model_interfaces_qs_model_interfaces = \
929  [(min(x[0],x[1]), max(x[0],x[1])) for x in self._qs_model_interfaces_qs_model_interfaces]
930 
931  return self._qs_model_interfaces_qs_model_interfaces
932 
933  @property
934  def qs_interfaces(self):
935  """ Interfaces in :attr:`~qs_target_interfaces` that can be mapped
936  to :attr:`~model`.
937 
938  Target chain names are lexicographically sorted.
939 
940  :type: :class:`list` of :class:`tuple` with 4 elements each:
941  (trg_ch1, trg_ch2, mdl_ch1, mdl_ch2)
942  """
943  if self._qs_interfaces_qs_interfaces is None:
944  self._qs_interfaces_qs_interfaces = list()
945  flat_mapping = self.mappingmapping.GetFlatMapping()
946  for i in self.qs_target_interfacesqs_target_interfaces:
947  if i[0] in flat_mapping and i[1] in flat_mapping:
948  self._qs_interfaces_qs_interfaces.append((i[0], i[1],
949  flat_mapping[i[0]],
950  flat_mapping[i[1]]))
951  return self._qs_interfaces_qs_interfaces
952 
953  @property
955  """ QS-score for each interface in :attr:`~qs_interfaces`
956 
957  :type: :class:`list` of :class:`float`
958  """
959  if self._per_interface_qs_global_per_interface_qs_global is None:
960  self._compute_per_interface_qs_scores_compute_per_interface_qs_scores()
961  return self._per_interface_qs_global_per_interface_qs_global
962 
963  @property
965  """ QS-score for each interface in :attr:`~qs_interfaces`
966 
967  Only computed on aligned residues
968 
969  :type: :class:`list` of :class:`float`
970  """
971  if self._per_interface_qs_best_per_interface_qs_best is None:
972  self._compute_per_interface_qs_scores_compute_per_interface_qs_scores()
973  return self._per_interface_qs_best_per_interface_qs_best
974 
975  @property
976  def native_contacts(self):
977  """ Native contacts
978 
979  A contact is a pair or residues from distinct chains that have
980  a minimal heavy atom distance < 5A. Contacts are specified as
981  :class:`tuple` with two strings in format:
982  <cname>.<rnum>.<ins_code>
983 
984  :type: :class:`list` of :class:`tuple`
985  """
986  if self._native_contacts_native_contacts is None:
987  self._native_contacts_native_contacts = self.contact_scorercontact_scorer.cent1.hr_contacts
988  return self._native_contacts_native_contacts
989 
990  @property
991  def model_contacts(self):
992  """ Same for :attr:`~model`
993  """
994  if self._model_contacts_model_contacts is None:
995  self._model_contacts_model_contacts = self.contact_scorercontact_scorer.cent2.hr_contacts
996  return self._model_contacts_model_contacts
997 
998  @property
1000  """ Same for :attr:`~trimmed_model`
1001  """
1002  if self._trimmed_model_contacts_trimmed_model_contacts is None:
1003  self._trimmed_model_contacts_trimmed_model_contacts = self.trimmed_contact_scorertrimmed_contact_scorer.cent2.hr_contacts
1004  return self._trimmed_model_contacts_trimmed_model_contacts
1005 
1006  @property
1008  """ Interfaces in :class:`target` which have at least one contact
1009 
1010  Contact as defined in :attr:`~native_contacts`,
1011  chain names are lexicographically sorted.
1012 
1013  :type: :class:`list` of :class:`tuple` with 2 elements each
1014  (trg_ch1, trg_ch2)
1015  """
1016  if self._contact_target_interfaces_contact_target_interfaces is None:
1017  tmp = self.contact_scorercontact_scorer.cent1.interacting_chains
1018  tmp = [(min(x[0],x[1]), max(x[0],x[1])) for x in tmp]
1019  self._contact_target_interfaces_contact_target_interfaces = tmp
1020  return self._contact_target_interfaces_contact_target_interfaces
1021 
1022  @property
1024  """ Interfaces in :class:`model` which have at least one contact
1025 
1026  Contact as defined in :attr:`~native_contacts`,
1027  chain names are lexicographically sorted.
1028 
1029  :type: :class:`list` of :class:`tuple` with 2 elements each
1030  (mdl_ch1, mdl_ch2)
1031  """
1032  if self._contact_model_interfaces_contact_model_interfaces is None:
1033  tmp = self.contact_scorercontact_scorer.cent2.interacting_chains
1034  tmp = [(min(x[0],x[1]), max(x[0],x[1])) for x in tmp]
1035  self._contact_model_interfaces_contact_model_interfaces = tmp
1036  return self._contact_model_interfaces_contact_model_interfaces
1037 
1038  @property
1039  def ics_precision(self):
1040  """ Fraction of model contacts that are also present in target
1041 
1042  :type: :class:`float`
1043  """
1044  if self._ics_precision_ics_precision is None:
1045  self._compute_ics_scores_compute_ics_scores()
1046  return self._ics_precision_ics_precision
1047 
1048  @property
1049  def ics_recall(self):
1050  """ Fraction of target contacts that are correctly reproduced in model
1051 
1052  :type: :class:`float`
1053  """
1054  if self._ics_recall_ics_recall is None:
1055  self._compute_ics_scores_compute_ics_scores()
1056  return self._ics_recall_ics_recall
1057 
1058  @property
1059  def ics(self):
1060  """ ICS (Interface Contact Similarity) score
1061 
1062  Combination of :attr:`~ics_precision` and :attr:`~ics_recall`
1063  using the F1-measure
1064 
1065  :type: :class:`float`
1066  """
1067  if self._ics_ics is None:
1068  self._compute_ics_scores_compute_ics_scores()
1069  return self._ics_ics
1070 
1071  @property
1073  """ Per-interface ICS precision
1074 
1075  :attr:`~ics_precision` for each interface in
1076  :attr:`~contact_target_interfaces`
1077 
1078  :type: :class:`list` of :class:`float`
1079  """
1080  if self._per_interface_ics_precision_per_interface_ics_precision is None:
1081  self._compute_ics_scores_compute_ics_scores()
1082  return self._per_interface_ics_precision_per_interface_ics_precision
1083 
1084 
1085  @property
1087  """ Per-interface ICS recall
1088 
1089  :attr:`~ics_recall` for each interface in
1090  :attr:`~contact_target_interfaces`
1091 
1092  :type: :class:`list` of :class:`float`
1093  """
1094  if self._per_interface_ics_recall_per_interface_ics_recall is None:
1095  self._compute_ics_scores_compute_ics_scores()
1096  return self._per_interface_ics_recall_per_interface_ics_recall
1097 
1098  @property
1100  """ Per-interface ICS (Interface Contact Similarity) score
1101 
1102  :attr:`~ics` for each interface in
1103  :attr:`~contact_target_interfaces`
1104 
1105  :type: :class:`float`
1106  """
1107 
1108  if self._per_interface_ics_per_interface_ics is None:
1109  self._compute_ics_scores_compute_ics_scores()
1110  return self._per_interface_ics_per_interface_ics
1111 
1112  @property
1113  def ips_precision(self):
1114  """ Fraction of model interface residues that are also interface
1115  residues in target
1116 
1117  :type: :class:`float`
1118  """
1119  if self._ips_precision_ips_precision is None:
1120  self._compute_ips_scores_compute_ips_scores()
1121  return self._ips_precision_ips_precision
1122 
1123  @property
1124  def ips_recall(self):
1125  """ Fraction of target interface residues that are also interface
1126  residues in model
1127 
1128  :type: :class:`float`
1129  """
1130  if self._ips_recall_ips_recall is None:
1131  self._compute_ips_scores_compute_ips_scores()
1132  return self._ips_recall_ips_recall
1133 
1134  @property
1135  def ips(self):
1136  """ IPS (Interface Patch Similarity) score
1137 
1138  Jaccard coefficient of interface residues in target and their mapped
1139  counterparts in model
1140 
1141  :type: :class:`float`
1142  """
1143  if self._ips_ips is None:
1144  self._compute_ips_scores_compute_ips_scores()
1145  return self._ips_ips
1146 
1147  @property
1148  def ics_trimmed(self):
1149  """ Same as :attr:`~ics` but with trimmed model
1150 
1151  Model is trimmed to residues which can me mapped to target in order
1152  to not penalize contacts in the model for which we have no experimental
1153  evidence.
1154 
1155  :type: :class:`float`
1156  """
1157  if self._ics_trimmed_ics_trimmed is None:
1158  self._compute_ics_scores_trimmed_compute_ics_scores_trimmed()
1159  return self._ics_trimmed_ics_trimmed
1160 
1161  @property
1163  """ Same as :attr:`~ics_precision` but with trimmed model
1164 
1165  Model is trimmed to residues which can me mapped to target in order
1166  to not penalize contacts in the model for which we have no experimental
1167  evidence.
1168 
1169  :type: :class:`float`
1170  """
1171  if self._ics_precision_trimmed_ics_precision_trimmed is None:
1172  self._compute_ics_scores_trimmed_compute_ics_scores_trimmed()
1173  return self._ics_precision_trimmed_ics_precision_trimmed
1174 
1175  @property
1177  """ Same as :attr:`~ics_recall` but with trimmed model
1178 
1179  Model is trimmed to residues which can me mapped to target in order
1180  to not penalize contacts in the model for which we have no experimental
1181  evidence.
1182 
1183  :type: :class:`float`
1184  """
1185  if self._ics_recall_trimmed_ics_recall_trimmed is None:
1186  self._compute_ics_scores_trimmed_compute_ics_scores_trimmed()
1187  return self._ics_recall_trimmed_ics_recall_trimmed
1188 
1189  @property
1191  """ Same as :attr:`~per_interface_ics_precision` but with :attr:`~trimmed_model`
1192 
1193  :attr:`~ics_precision_trimmed` for each interface in
1194  :attr:`~contact_target_interfaces`
1195 
1196  :type: :class:`list` of :class:`float`
1197  """
1198  if self._per_interface_ics_precision_trimmed_per_interface_ics_precision_trimmed is None:
1199  self._compute_ics_scores_trimmed_compute_ics_scores_trimmed()
1200  return self._per_interface_ics_precision_trimmed_per_interface_ics_precision_trimmed
1201 
1202 
1203  @property
1205  """ Same as :attr:`~per_interface_ics_recall` but with :attr:`~trimmed_model`
1206 
1207  :attr:`~ics_recall_trimmed` for each interface in
1208  :attr:`~contact_target_interfaces`
1209 
1210  :type: :class:`list` of :class:`float`
1211  """
1212  if self._per_interface_ics_recall_trimmed_per_interface_ics_recall_trimmed is None:
1213  self._compute_ics_scores_trimmed_compute_ics_scores_trimmed()
1214  return self._per_interface_ics_recall_trimmed_per_interface_ics_recall_trimmed
1215 
1216  @property
1218  """ Same as :attr:`~per_interface_ics` but with :attr:`~trimmed_model`
1219 
1220  :attr:`~ics` for each interface in
1221  :attr:`~contact_target_interfaces`
1222 
1223  :type: :class:`float`
1224  """
1225 
1226  if self._per_interface_ics_trimmed_per_interface_ics_trimmed is None:
1227  self._compute_ics_scores_trimmed_compute_ics_scores_trimmed()
1228  return self._per_interface_ics_trimmed_per_interface_ics_trimmed
1229 
1230  @property
1231  def ips_trimmed(self):
1232  """ Same as :attr:`~ips` but with trimmed model
1233 
1234  Model is trimmed to residues which can me mapped to target in order
1235  to not penalize contacts in the model for which we have no experimental
1236  evidence.
1237 
1238  :type: :class:`float`
1239  """
1240  if self._ips_trimmed_ips_trimmed is None:
1241  self._compute_ips_scores_trimmed_compute_ips_scores_trimmed()
1242  return self._ips_trimmed_ips_trimmed
1243 
1244  @property
1246  """ Same as :attr:`~ips_precision` but with trimmed model
1247 
1248  Model is trimmed to residues which can me mapped to target in order
1249  to not penalize contacts in the model for which we have no experimental
1250  evidence.
1251 
1252  :type: :class:`float`
1253  """
1254  if self._ips_precision_trimmed_ips_precision_trimmed is None:
1255  self._compute_ips_scores_trimmed_compute_ips_scores_trimmed()
1256  return self._ips_precision_trimmed_ips_precision_trimmed
1257 
1258  @property
1260  """ Same as :attr:`~ips_recall` but with trimmed model
1261 
1262  Model is trimmed to residues which can me mapped to target in order
1263  to not penalize contacts in the model for which we have no experimental
1264  evidence.
1265 
1266  :type: :class:`float`
1267  """
1268  if self._ips_recall_trimmed_ips_recall_trimmed is None:
1269  self._compute_ips_scores_trimmed_compute_ips_scores_trimmed()
1270  return self._ips_recall_trimmed_ips_recall_trimmed
1271 
1272  @property
1274  """ Per-interface IPS precision
1275 
1276  :attr:`~ips_precision` for each interface in
1277  :attr:`~contact_target_interfaces`
1278 
1279  :type: :class:`list` of :class:`float`
1280  """
1281  if self._per_interface_ips_precision_per_interface_ips_precision is None:
1282  self._compute_ips_scores_compute_ips_scores()
1283  return self._per_interface_ips_precision_per_interface_ips_precision
1284 
1285  @property
1287  """ Per-interface IPS recall
1288 
1289  :attr:`~ips_recall` for each interface in
1290  :attr:`~contact_target_interfaces`
1291 
1292  :type: :class:`list` of :class:`float`
1293  """
1294  if self._per_interface_ics_recall_per_interface_ics_recall is None:
1295  self._compute_ips_scores_compute_ips_scores()
1296  return self._per_interface_ips_recall_per_interface_ips_recall
1297 
1298  @property
1300  """ Per-interface IPS (Interface Patch Similarity) score
1301 
1302  :attr:`~ips` for each interface in
1303  :attr:`~contact_target_interfaces`
1304 
1305  :type: :class:`list` of :class:`float`
1306  """
1307 
1308  if self._per_interface_ips_per_interface_ips is None:
1309  self._compute_ips_scores_compute_ips_scores()
1310  return self._per_interface_ips_per_interface_ips
1311 
1312  @property
1314  """ Same as :attr:`~per_interface_ips_precision` but with :attr:`~trimmed_model`
1315 
1316  :attr:`~ips_precision_trimmed` for each interface in
1317  :attr:`~contact_target_interfaces`
1318 
1319  :type: :class:`list` of :class:`float`
1320  """
1321  if self._per_interface_ips_precision_trimmed_per_interface_ips_precision_trimmed is None:
1322  self._compute_ips_scores_trimmed_compute_ips_scores_trimmed()
1323  return self._per_interface_ips_precision_trimmed_per_interface_ips_precision_trimmed
1324 
1325 
1326  @property
1328  """ Same as :attr:`~per_interface_ips_recall` but with :attr:`~trimmed_model`
1329 
1330  :attr:`~ics_recall_trimmed` for each interface in
1331  :attr:`~contact_target_interfaces`
1332 
1333  :type: :class:`list` of :class:`float`
1334  """
1335  if self._per_interface_ips_recall_trimmed_per_interface_ips_recall_trimmed is None:
1336  self._compute_ips_scores_trimmed_compute_ips_scores_trimmed()
1337  return self._per_interface_ips_recall_trimmed_per_interface_ips_recall_trimmed
1338 
1339  @property
1341  """ Same as :attr:`~per_interface_ips` but with :attr:`~trimmed_model`
1342 
1343  :attr:`~ics` for each interface in
1344  :attr:`~contact_target_interfaces`
1345 
1346  :type: :class:`float`
1347  """
1348 
1349  if self._per_interface_ips_trimmed_per_interface_ips_trimmed is None:
1350  self._compute_ips_scores_trimmed_compute_ips_scores_trimmed()
1351  return self._per_interface_ips_trimmed_per_interface_ips_trimmed
1352 
1353  @property
1355  """ Interfaces in :attr:`~target` that are relevant for DockQ
1356 
1357  All interfaces in :attr:`~target` with non-zero contacts that are
1358  relevant for DockQ. Includes protein-protein, protein-nucleotide and
1359  nucleotide-nucleotide interfaces. Chain names for each interface are
1360  lexicographically sorted.
1361 
1362  :type: :class:`list` of :class:`tuple` with 2 elements each:
1363  (trg_ch1, trg_ch2)
1364  """
1365  if self._dockq_target_interfaces_dockq_target_interfaces is None:
1366 
1367  # interacting chains are identified with ContactEntity
1368  contact_d = 5.0
1369  if self.dockq_capri_peptidedockq_capri_peptide:
1370  contact_d = 4.0
1371  cent = ContactEntity(self.targettarget, contact_mode = "aa",
1372  contact_d = contact_d)
1373 
1374  # fetch lexicographically sorted interfaces
1375  interfaces = cent.interacting_chains
1376  interfaces = [(min(x[0],x[1]), max(x[0],x[1])) for x in interfaces]
1377 
1378  pep_seqs = set([s.GetName() for s in self.chain_mapperchain_mapper.polypep_seqs])
1379  nuc_seqs = set([s.GetName() for s in self.chain_mapperchain_mapper.polynuc_seqs])
1380 
1381  seqs = pep_seqs.union(nuc_seqs)
1382  self._dockq_target_interfaces_dockq_target_interfaces = list()
1383  for interface in interfaces:
1384  if interface[0] in seqs and interface[1] in seqs:
1385  self._dockq_target_interfaces_dockq_target_interfaces.append(interface)
1386 
1387  return self._dockq_target_interfaces_dockq_target_interfaces
1388 
1389  @property
1390  def dockq_interfaces(self):
1391  """ Interfaces in :attr:`~dockq_target_interfaces` that can be mapped
1392  to model
1393 
1394  Target chain names are lexicographically sorted
1395 
1396  :type: :class:`list` of :class:`tuple` with 4 elements each:
1397  (trg_ch1, trg_ch2, mdl_ch1, mdl_ch2)
1398  """
1399  if self._dockq_interfaces_dockq_interfaces is None:
1400  self._dockq_interfaces_dockq_interfaces = list()
1401  flat_mapping = self.mappingmapping.GetFlatMapping()
1402  for i in self.dockq_target_interfacesdockq_target_interfaces:
1403  if i[0] in flat_mapping and i[1] in flat_mapping:
1404  self._dockq_interfaces_dockq_interfaces.append((i[0], i[1],
1405  flat_mapping[i[0]],
1406  flat_mapping[i[1]]))
1407  return self._dockq_interfaces_dockq_interfaces
1408 
1409  @property
1410  def dockq_scores(self):
1411  """ DockQ scores for interfaces in :attr:`~dockq_interfaces`
1412 
1413  :class:`list` of :class:`float`
1414  """
1415  if self._dockq_scores_dockq_scores is None:
1416  self._compute_dockq_scores_compute_dockq_scores()
1417  return self._dockq_scores_dockq_scores
1418 
1419  @property
1420  def fnat(self):
1421  """ fnat scores for interfaces in :attr:`~dockq_interfaces`
1422 
1423  fnat: Fraction of native contacts that are also present in model
1424 
1425  :class:`list` of :class:`float`
1426  """
1427  if self._fnat_fnat is None:
1428  self._compute_dockq_scores_compute_dockq_scores()
1429  return self._fnat_fnat
1430 
1431  @property
1432  def nnat(self):
1433  """ N native contacts for interfaces in :attr:`~dockq_interfaces`
1434 
1435  :class:`list` of :class:`int`
1436  """
1437  if self._nnat_nnat is None:
1438  self._compute_dockq_scores_compute_dockq_scores()
1439  return self._nnat_nnat
1440 
1441  @property
1442  def nmdl(self):
1443  """ N model contacts for interfaces in :attr:`~dockq_interfaces`
1444 
1445  :class:`list` of :class:`int`
1446  """
1447  if self._nmdl_nmdl is None:
1448  self._compute_dockq_scores_compute_dockq_scores()
1449  return self._nmdl_nmdl
1450 
1451  @property
1452  def fnonnat(self):
1453  """ fnonnat scores for interfaces in :attr:`~dockq_interfaces`
1454 
1455  fnat: Fraction of model contacts that are not present in target
1456 
1457  :class:`list` of :class:`float`
1458  """
1459  if self._fnonnat_fnonnat is None:
1460  self._compute_dockq_scores_compute_dockq_scores()
1461  return self._fnonnat_fnonnat
1462 
1463  @property
1464  def irmsd(self):
1465  """ irmsd scores for interfaces in :attr:`~dockq_interfaces`
1466 
1467  irmsd: RMSD of interface (RMSD computed on backbone atoms) which
1468  consists of each residue that has at least one heavy atom within 10A of
1469  other chain. Backbone atoms for proteins: "CA","C","N","O", for
1470  nucleotides: "P", "OP1", "OP2", "O2'", "O3'", "O4'", "O5'", "C1'",
1471  "C2'", "C3'", "C4'", "C5'".
1472 
1473  :class:`list` of :class:`float`
1474  """
1475  if self._irmsd_irmsd is None:
1476  self._compute_dockq_scores_compute_dockq_scores()
1477  return self._irmsd_irmsd
1478 
1479  @property
1480  def lrmsd(self):
1481  """ lrmsd scores for interfaces in :attr:`~dockq_interfaces`
1482 
1483  lrmsd: The two chains involved in the interface are superposed based on
1484  the receptor (rigid min RMSD superposition) and the ligand RMSD is
1485  reported. Receptor is the chain with more residues. Superposition and
1486  RMSD is computed on same backbone atoms as :attr:`~irmsd`.
1487 
1488  :class:`list` of :class:`float`
1489  """
1490  if self._lrmsd_lrmsd is None:
1491  self._compute_dockq_scores_compute_dockq_scores()
1492  return self._lrmsd_lrmsd
1493 
1494  @property
1495  def dockq_ave(self):
1496  """ Average of DockQ scores in :attr:`~dockq_scores`
1497 
1498  In its original implementation, DockQ only operates on single
1499  interfaces. Thus the requirement to combine scores for higher order
1500  oligomers.
1501 
1502  :type: :class:`float`
1503  """
1504  if self._dockq_ave_dockq_ave is None:
1505  self._compute_dockq_scores_compute_dockq_scores()
1506  return self._dockq_ave_dockq_ave
1507 
1508  @property
1509  def dockq_wave(self):
1510  """ Same as :attr:`~dockq_ave`, weighted by native contacts
1511 
1512  :type: :class:`float`
1513  """
1514  if self._dockq_wave_dockq_wave is None:
1515  self._compute_dockq_scores_compute_dockq_scores()
1516  return self._dockq_wave_dockq_wave
1517 
1518  @property
1519  def dockq_ave_full(self):
1520  """ Same as :attr:`~dockq_ave` but penalizing for missing interfaces
1521 
1522  Interfaces that are not covered in model are added as 0.0
1523  in average computation.
1524 
1525  :type: :class:`float`
1526  """
1527  if self._dockq_ave_full_dockq_ave_full is None:
1528  self._compute_dockq_scores_compute_dockq_scores()
1529  return self._dockq_ave_full_dockq_ave_full
1530 
1531  @property
1532  def dockq_wave_full(self):
1533  """ Same as :attr:`~dockq_ave_full`, but weighted
1534 
1535  Interfaces that are not covered in model are added as 0.0 in
1536  average computations and the respective weights are derived from
1537  number of contacts in respective target interface.
1538  """
1539  if self._dockq_wave_full_dockq_wave_full is None:
1540  self._compute_dockq_scores_compute_dockq_scores()
1541  return self._dockq_wave_full_dockq_wave_full
1542 
1543  @property
1545  """ Mapped representative positions in target
1546 
1547  Thats CA positions for peptide residues and C3' positions for
1548  nucleotides. Has same length as :attr:`~mapped_model_pos` and mapping
1549  is based on :attr:`~mapping`.
1550 
1551  :type: :class:`ost.geom.Vec3List`
1552  """
1553  if self._mapped_target_pos_mapped_target_pos is None:
1554  self._extract_mapped_pos_extract_mapped_pos()
1555  return self._mapped_target_pos_mapped_target_pos
1556 
1557  @property
1559  """ Mapped representative positions in target
1560 
1561  Thats the equivalent of :attr:`~mapped_target_pos` but containing more
1562  backbone atoms (N, CA, C for peptide residues and O5', C5', C4', C3', O3
1563  for nucleotide residues). mapping is based on :attr:`~mapping`.
1564 
1565  :type: :class:`ost.geom.Vec3List`
1566  """
1567  if self._mapped_target_pos_full_bb_mapped_target_pos_full_bb is None:
1568  self._extract_mapped_pos_full_bb_extract_mapped_pos_full_bb()
1569  return self._mapped_target_pos_full_bb_mapped_target_pos_full_bb
1570 
1571  @property
1572  def mapped_model_pos(self):
1573  """ Mapped representative positions in model
1574 
1575  Thats CA positions for peptide residues and C3' positions for
1576  nucleotides. Has same length as :attr:`~mapped_target_pos` and mapping
1577  is based on :attr:`~mapping`.
1578 
1579  :type: :class:`ost.geom.Vec3List`
1580  """
1581  if self._mapped_model_pos_mapped_model_pos is None:
1582  self._extract_mapped_pos_extract_mapped_pos()
1583  return self._mapped_model_pos_mapped_model_pos
1584 
1585  @property
1587  """ Mapped representative positions in model
1588 
1589  Thats the equivalent of :attr:`~mapped_model_pos` but containing more
1590  backbone atoms (N, CA, C for peptide residues and O5', C5', C4', C3', O3
1591  for nucleotide residues). mapping is based on :attr:`~mapping`.
1592 
1593  :type: :class:`ost.geom.Vec3List`
1594  """
1595  if self._mapped_model_pos_full_bb_mapped_model_pos_full_bb is None:
1596  self._extract_mapped_pos_full_bb_extract_mapped_pos_full_bb()
1597  return self._mapped_model_pos_full_bb_mapped_model_pos_full_bb
1598 
1599  @property
1601  """ :attr:`~mapped_model_pos` with :attr:`~transform` applied
1602 
1603  :type: :class:`ost.geom.Vec3List`
1604  """
1605  if self._transformed_mapped_model_pos_transformed_mapped_model_pos is None:
1606  self._transformed_mapped_model_pos_transformed_mapped_model_pos = \
1607  geom.Vec3List(self.mapped_model_posmapped_model_pos)
1608  self._transformed_mapped_model_pos_transformed_mapped_model_pos.ApplyTransform(self.transformtransform)
1609  return self._transformed_mapped_model_pos_transformed_mapped_model_pos
1610 
1611  @property
1613  """ Number of target residues which have no mapping to model
1614 
1615  :type: :class:`int`
1616  """
1617  if self._n_target_not_mapped_n_target_not_mapped is None:
1618  self._extract_mapped_pos_extract_mapped_pos()
1619  return self._n_target_not_mapped_n_target_not_mapped
1620 
1621  @property
1622  def transform(self):
1623  """ Transform: :attr:`~mapped_model_pos` onto :attr:`~mapped_target_pos`
1624 
1625  Computed using Kabsch minimal rmsd algorithm. If number of positions
1626  is too small (< 3), :attr:`~mapped_model_pos_full_bb` and
1627  :attr:`~mapped_target_pos_full_bb` are used.
1628 
1629  :type: :class:`ost.geom.Mat4`
1630  """
1631  if self._transform_transform is None:
1632  if len(self.mapped_model_posmapped_model_pos) < 3:
1633  if len(self.mapped_model_pos_full_bbmapped_model_pos_full_bb) >=3:
1634  res = mol.alg.SuperposeSVD(self.mapped_model_pos_full_bbmapped_model_pos_full_bb,
1635  self.mapped_target_pos_full_bbmapped_target_pos_full_bb)
1636  self._transform_transform = res.transformation
1637  else:
1638  # there is really nothing we can do => set identity matrix
1639  self._transform_transform = geom.Mat4()
1640  else:
1641  res = mol.alg.SuperposeSVD(self.mapped_model_posmapped_model_pos,
1642  self.mapped_target_posmapped_target_pos)
1643  self._transform_transform = res.transformation
1644  return self._transform_transform
1645 
1646  @property
1648  """ Mapped representative positions in target
1649 
1650  Thats CA positions for peptide residues and C3' positions for
1651  nucleotides. Has same length as :attr:`~rigid_mapped_model_pos` and mapping
1652  is based on :attr:`~rigid_mapping`.
1653 
1654  :type: :class:`ost.geom.Vec3List`
1655  """
1656  if self._rigid_mapped_target_pos_rigid_mapped_target_pos is None:
1657  self._extract_rigid_mapped_pos_extract_rigid_mapped_pos()
1658  return self._rigid_mapped_target_pos_rigid_mapped_target_pos
1659 
1660  @property
1662  """ Mapped representative positions in target
1663 
1664  Thats the equivalent of :attr:`~rigid_mapped_target_pos` but containing
1665  more backbone atoms (N, CA, C for peptide residues and O5', C5', C4',
1666  C3', O3 for nucleotide residues). mapping is based on :attr:`~mapping`.
1667 
1668  :type: :class:`ost.geom.Vec3List`
1669  """
1670  if self._rigid_mapped_target_pos_full_bb_rigid_mapped_target_pos_full_bb is None:
1671  self._extract_rigid_mapped_pos_full_bb_extract_rigid_mapped_pos_full_bb()
1672  return self._rigid_mapped_target_pos_full_bb_rigid_mapped_target_pos_full_bb
1673 
1674  @property
1676  """ Mapped representative positions in model
1677 
1678  Thats CA positions for peptide residues and C3' positions for
1679  nucleotides. Has same length as :attr:`~mapped_target_pos` and mapping
1680  is based on :attr:`~rigid_mapping`.
1681 
1682  :type: :class:`ost.geom.Vec3List`
1683  """
1684  if self._rigid_mapped_model_pos_rigid_mapped_model_pos is None:
1685  self._extract_rigid_mapped_pos_extract_rigid_mapped_pos()
1686  return self._rigid_mapped_model_pos_rigid_mapped_model_pos
1687 
1688  @property
1690  """ Mapped representative positions in model
1691 
1692  Thats the equivalent of :attr:`~rigid_mapped_model_pos` but containing
1693  more backbone atoms (N, CA, C for peptide residues and O5', C5', C4',
1694  C3', O3 for nucleotide residues). mapping is based on :attr:`~mapping`.
1695 
1696  :type: :class:`ost.geom.Vec3List`
1697  """
1698  if self._rigid_mapped_model_pos_full_bb_rigid_mapped_model_pos_full_bb is None:
1699  self._extract_rigid_mapped_pos_full_bb_extract_rigid_mapped_pos_full_bb()
1700  return self._rigid_mapped_model_pos_full_bb_rigid_mapped_model_pos_full_bb
1701 
1702  @property
1704  """ :attr:`~rigid_mapped_model_pos` with :attr:`~rigid_transform` applied
1705 
1706  :type: :class:`ost.geom.Vec3List`
1707  """
1708  if self._rigid_transformed_mapped_model_pos_rigid_transformed_mapped_model_pos is None:
1709  self._rigid_transformed_mapped_model_pos_rigid_transformed_mapped_model_pos = \
1710  geom.Vec3List(self.rigid_mapped_model_posrigid_mapped_model_pos)
1711  self._rigid_transformed_mapped_model_pos_rigid_transformed_mapped_model_pos.ApplyTransform(self.rigid_transformrigid_transform)
1712  return self._rigid_transformed_mapped_model_pos_rigid_transformed_mapped_model_pos
1713 
1714  @property
1716  """ Number of target residues which have no rigid mapping to model
1717 
1718  :type: :class:`int`
1719  """
1720  if self._rigid_n_target_not_mapped_rigid_n_target_not_mapped is None:
1721  self._extract_rigid_mapped_pos_extract_rigid_mapped_pos()
1722  return self._rigid_n_target_not_mapped_rigid_n_target_not_mapped
1723 
1724  @property
1725  def rigid_transform(self):
1726  """ Transform: :attr:`~rigid_mapped_model_pos` onto :attr:`~rigid_mapped_target_pos`
1727 
1728  Computed using Kabsch minimal rmsd algorithm. If number of positions
1729  is too small (< 3), :attr:`~rigid_mapped_model_pos_full_bb` and
1730  :attr:`~rigid_mapped_target_pos_full_bb` are used.
1731 
1732  :type: :class:`ost.geom.Mat4`
1733  """
1734  if self._rigid_transform_rigid_transform is None:
1735  if len(self.rigid_mapped_model_posrigid_mapped_model_pos) < 3:
1736  if len(self.rigid_mapped_model_pos_full_bbrigid_mapped_model_pos_full_bb) >= 3:
1737  res = mol.alg.SuperposeSVD(self.rigid_mapped_model_pos_full_bbrigid_mapped_model_pos_full_bb,
1738  self.rigid_mapped_target_pos_full_bbrigid_mapped_target_pos_full_bb)
1739  self._rigid_transform_rigid_transform = res.transformation
1740  else:
1741  # there is really nothing we can do => set identity matrix
1742  self._rigid_transform_rigid_transform = geom.Mat4()
1743  else:
1744  res = mol.alg.SuperposeSVD(self.rigid_mapped_model_posrigid_mapped_model_pos,
1745  self.rigid_mapped_target_posrigid_mapped_target_pos)
1746  self._rigid_transform_rigid_transform = res.transformation
1747  return self._rigid_transform_rigid_transform
1748 
1749  @property
1750  def gdt_05(self):
1751  """ Fraction CA (C3' for nucleotides) that can be superposed within 0.5A
1752 
1753  Uses :attr:`~rigid_mapped_model_pos` and :attr:`~rigid_mapped_target_pos`.
1754  Similar iterative algorithm as LGA tool
1755 
1756  :type: :class:`float`
1757  """
1758  if self._gdt_05_gdt_05 is None:
1759  N = list()
1760  wsizes = self._gdt_window_sizes_gdt_window_sizes + [len(self.rigid_mapped_model_posrigid_mapped_model_pos)]
1761  for window_size in wsizes:
1762  n = GDT(self.rigid_mapped_model_posrigid_mapped_model_pos,
1763  self.rigid_mapped_target_posrigid_mapped_target_pos,
1764  window_size, 1000, 0.5)[0]
1765  N.append(n)
1766  n = max(N)
1767  n_full = len(self.rigid_mapped_target_posrigid_mapped_target_pos) + self.rigid_n_target_not_mappedrigid_n_target_not_mapped
1768  if n_full > 0:
1769  self._gdt_05_gdt_05 = float(n) / n_full
1770  else:
1771  self._gdt_05_gdt_05 = 0.0
1772  return self._gdt_05_gdt_05
1773 
1774  @property
1775  def gdt_1(self):
1776  """ Fraction CA (C3' for nucleotides) that can be superposed within 1.0A
1777 
1778  Uses :attr:`~rigid_mapped_model_pos` and :attr:`~rigid_mapped_target_pos`.
1779  Similar iterative algorithm as LGA tool
1780 
1781  :type: :class:`float`
1782  """
1783  if self._gdt_1_gdt_1 is None:
1784  N = list()
1785  wsizes = self._gdt_window_sizes_gdt_window_sizes + [len(self.rigid_mapped_model_posrigid_mapped_model_pos)]
1786  for window_size in wsizes:
1787  n = GDT(self.rigid_mapped_model_posrigid_mapped_model_pos,
1788  self.rigid_mapped_target_posrigid_mapped_target_pos,
1789  window_size, 1000, 1.0)[0]
1790  N.append(n)
1791  n = max(N)
1792  n_full = len(self.rigid_mapped_target_posrigid_mapped_target_pos) + self.rigid_n_target_not_mappedrigid_n_target_not_mapped
1793  if n_full > 0:
1794  self._gdt_1_gdt_1 = float(n) / n_full
1795  else:
1796  self._gdt_1_gdt_1 = 0.0
1797  return self._gdt_1_gdt_1
1798 
1799  @property
1800  def gdt_2(self):
1801  """ Fraction CA (C3' for nucleotides) that can be superposed within 2.0A
1802 
1803  Uses :attr:`~rigid_mapped_model_pos` and :attr:`~rigid_mapped_target_pos`.
1804  Similar iterative algorithm as LGA tool
1805 
1806 
1807  :type: :class:`float`
1808  """
1809  if self._gdt_2_gdt_2 is None:
1810  N = list()
1811  wsizes = self._gdt_window_sizes_gdt_window_sizes + [len(self.rigid_mapped_model_posrigid_mapped_model_pos)]
1812  for window_size in wsizes:
1813  n = GDT(self.rigid_mapped_model_posrigid_mapped_model_pos,
1814  self.rigid_mapped_target_posrigid_mapped_target_pos,
1815  window_size, 1000, 2.0)[0]
1816  N.append(n)
1817  n = max(N)
1818  n_full = len(self.rigid_mapped_target_posrigid_mapped_target_pos) + self.rigid_n_target_not_mappedrigid_n_target_not_mapped
1819  if n_full > 0:
1820  self._gdt_2_gdt_2 = float(n) / n_full
1821  else:
1822  self._gdt_2_gdt_2 = 0.0
1823  return self._gdt_2_gdt_2
1824 
1825  @property
1826  def gdt_4(self):
1827  """ Fraction CA (C3' for nucleotides) that can be superposed within 4.0A
1828 
1829  Uses :attr:`~rigid_mapped_model_pos` and :attr:`~rigid_mapped_target_pos`.
1830  Similar iterative algorithm as LGA tool
1831 
1832  :type: :class:`float`
1833  """
1834  if self._gdt_4_gdt_4 is None:
1835  N = list()
1836  wsizes = self._gdt_window_sizes_gdt_window_sizes + [len(self.rigid_mapped_model_posrigid_mapped_model_pos)]
1837  for window_size in wsizes:
1838  n = GDT(self.rigid_mapped_model_posrigid_mapped_model_pos,
1839  self.rigid_mapped_target_posrigid_mapped_target_pos,
1840  window_size, 1000, 4.0)[0]
1841  N.append(n)
1842  n = max(N)
1843  n_full = len(self.rigid_mapped_target_posrigid_mapped_target_pos) + self.rigid_n_target_not_mappedrigid_n_target_not_mapped
1844  if n_full > 0:
1845  self._gdt_4_gdt_4 = float(n) / n_full
1846  else:
1847  self._gdt_4_gdt_4 = 0.0
1848  return self._gdt_4_gdt_4
1849 
1850  @property
1851  def gdt_8(self):
1852  """ Fraction CA (C3' for nucleotides) that can be superposed within 8.0A
1853 
1854  Similar iterative algorithm as LGA tool
1855 
1856  :type: :class:`float`
1857  """
1858  if self._gdt_8_gdt_8 is None:
1859  N = list()
1860  wsizes = self._gdt_window_sizes_gdt_window_sizes + [len(self.rigid_mapped_model_posrigid_mapped_model_pos)]
1861  for window_size in wsizes:
1862  n = GDT(self.rigid_mapped_model_posrigid_mapped_model_pos,
1863  self.rigid_mapped_target_posrigid_mapped_target_pos,
1864  window_size, 1000, 8.0)[0]
1865  N.append(n)
1866  n = max(N)
1867  n_full = len(self.rigid_mapped_target_posrigid_mapped_target_pos) + self.rigid_n_target_not_mappedrigid_n_target_not_mapped
1868  if n_full > 0:
1869  self._gdt_8_gdt_8 = float(n) / n_full
1870  else:
1871  self._gdt_8_gdt_8 = 0.0
1872  return self._gdt_8_gdt_8
1873 
1874 
1875  @property
1876  def gdtts(self):
1877  """ avg GDT with thresholds: 8.0A, 4.0A, 2.0A and 1.0A
1878 
1879  :type: :class:`float`
1880  """
1881  if self._gdtts_gdtts is None:
1882  LogScript("Computing GDT-TS score")
1883  self._gdtts_gdtts = (self.gdt_1gdt_1 + self.gdt_2gdt_2 + self.gdt_4gdt_4 + self.gdt_8gdt_8) / 4
1884  return self._gdtts_gdtts
1885 
1886  @property
1887  def gdtha(self):
1888  """ avg GDT with thresholds: 4.0A, 2.0A, 1.0A and 0.5A
1889 
1890  :type: :class:`float`
1891  """
1892  if self._gdtha_gdtha is None:
1893  LogScript("Computing GDT-HA score")
1894  self._gdtha_gdtha = (self.gdt_05gdt_05 + self.gdt_1gdt_1 + self.gdt_2gdt_2 + self.gdt_4gdt_4) / 4
1895  return self._gdtha_gdtha
1896 
1897  @property
1898  def rmsd(self):
1899  """ RMSD
1900 
1901  Computed on :attr:`~rigid_transformed_mapped_model_pos` and
1902  :attr:`~rigid_mapped_target_pos`
1903 
1904  :type: :class:`float`
1905  """
1906  if self._rmsd_rmsd is None:
1907  LogScript("Computing RMSD")
1908  self._rmsd_rmsd = \
1909  self.rigid_mapped_target_posrigid_mapped_target_pos.GetRMSD(self.rigid_transformed_mapped_model_posrigid_transformed_mapped_model_pos)
1910  return self._rmsd_rmsd
1911 
1912  @property
1913  def cad_score(self):
1914  """ The global CAD atom-atom (AA) score
1915 
1916  Computed based on :attr:`~model`. In case of oligomers, :attr:`~mapping`
1917  is used.
1918 
1919  :type: :class:`float`
1920  """
1921  if self._cad_score_cad_score is None:
1922  self._compute_cad_score_compute_cad_score()
1923  return self._cad_score_cad_score
1924 
1925  @property
1926  def local_cad_score(self):
1927  """ The per-residue CAD atom-atom (AA) scores
1928 
1929  Computed based on :attr:`~model`. In case of oligomers, :attr:`~mapping`
1930  is used.
1931 
1932  :type: :class:`dict`
1933  """
1934  if self._local_cad_score_local_cad_score is None:
1935  self._compute_cad_score_compute_cad_score()
1936  return self._local_cad_score_local_cad_score
1937 
1938  @property
1939  def patch_qs(self):
1940  """ Patch QS-scores for each residue in :attr:`~model_interface_residues`
1941 
1942  Representative patches for each residue r in chain c are computed as
1943  follows:
1944 
1945  * mdl_patch_one: All residues in c with CB (CA for GLY) positions within
1946  8A of r and within 12A of residues from any other chain.
1947  * mdl_patch_two: Closest residue x to r in any other chain gets
1948  identified. Patch is then constructed by selecting all residues from
1949  any other chain within 8A of x and within 12A from any residue in c.
1950  * trg_patch_one: Chain name and residue number based mapping from
1951  mdl_patch_one
1952  * trg_patch_two: Chain name and residue number based mapping from
1953  mdl_patch_two
1954 
1955  Results are stored in the same manner as
1956  :attr:`~model_interface_residues`, with corresponding scores instead of
1957  residue numbers. Scores for residues which are not
1958  :class:`mol.ChemType.AMINOACIDS` are set to None. Additionally,
1959  interface patches are derived from :attr:`~model`. If they contain
1960  residues which are not covered by :attr:`~target`, the score is set to
1961  None too.
1962 
1963  :type: :class:`dict` with chain names as key and and :class:`list`
1964  with scores of the respective interface residues.
1965  """
1966  if self._patch_qs_patch_qs is None:
1967  self._compute_patchqs_scores_compute_patchqs_scores()
1968  return self._patch_qs_patch_qs
1969 
1970  @property
1971  def patch_dockq(self):
1972  """ Same as :attr:`~patch_qs` but for DockQ scores
1973  """
1974  if self._patch_dockq_patch_dockq is None:
1975  self._compute_patchdockq_scores_compute_patchdockq_scores()
1976  return self._patch_dockq_patch_dockq
1977 
1978  @property
1979  def tm_score(self):
1980  """ TM-score computed with USalign
1981 
1982  USalign executable can be specified with usalign_exec kwarg at Scorer
1983  construction, an OpenStructure internal copy of the USalign code is
1984  used otherwise.
1985 
1986  :type: :class:`float`
1987  """
1988  if self._tm_score_tm_score is None:
1989  self._compute_tmscore_compute_tmscore()
1990  return self._tm_score_tm_score
1991 
1992  @property
1993  def usalign_mapping(self):
1994  """ Mapping computed with USalign
1995 
1996  Dictionary with target chain names as key and model chain names as
1997  values. No guarantee that all chains are mapped. USalign executable
1998  can be specified with usalign_exec kwarg at Scorer construction, an
1999  OpenStructure internal copy of the USalign code is used otherwise.
2000 
2001  :type: :class:`dict`
2002  """
2003  if self._usalign_mapping_usalign_mapping is None:
2004  self._compute_tmscore_compute_tmscore()
2005  return self._usalign_mapping_usalign_mapping
2006 
2007  def _aln_helper(self, target, model):
2008  # perform required alignments - cannot take the alignments from the
2009  # mapping results as we potentially remove stuff there as compared
2010  # to self.model and self.target
2011  trg_seqs = dict()
2012  for ch in target.chains:
2013  cname = ch.GetName()
2014  s = ''.join([r.one_letter_code for r in ch.residues])
2015  s = seq.CreateSequence(ch.GetName(), s)
2016  s.AttachView(target.Select(f"cname={mol.QueryQuoteName(cname)}"))
2017  trg_seqs[ch.GetName()] = s
2018  mdl_seqs = dict()
2019  for ch in model.chains:
2020  cname = ch.GetName()
2021  s = ''.join([r.one_letter_code for r in ch.residues])
2022  s = seq.CreateSequence(cname, s)
2023  s.AttachView(model.Select(f"cname={mol.QueryQuoteName(cname)}"))
2024  mdl_seqs[ch.GetName()] = s
2025 
2026  alns = list()
2027  trg_pep_chains = [s.GetName() for s in self.chain_mapperchain_mapper.polypep_seqs]
2028  trg_nuc_chains = [s.GetName() for s in self.chain_mapperchain_mapper.polynuc_seqs]
2029  trg_pep_chains = set(trg_pep_chains)
2030  trg_nuc_chains = set(trg_nuc_chains)
2031  for trg_ch, mdl_ch in self.mappingmapping.GetFlatMapping().items():
2032  if mdl_ch in mdl_seqs and trg_ch in trg_seqs:
2033  if trg_ch in trg_pep_chains:
2034  stype = mol.ChemType.AMINOACIDS
2035  elif trg_ch in trg_nuc_chains:
2036  stype = mol.ChemType.NUCLEOTIDES
2037  else:
2038  raise RuntimeError("Chain name inconsistency... ask "
2039  "Gabriel")
2040  alns.append(self.chain_mapperchain_mapper.Align(trg_seqs[trg_ch],
2041  mdl_seqs[mdl_ch],
2042  stype))
2043  alns[-1].AttachView(0, trg_seqs[trg_ch].GetAttachedView())
2044  alns[-1].AttachView(1, mdl_seqs[mdl_ch].GetAttachedView())
2045  return alns
2046 
2047  def _compute_pepnuc_aln(self):
2048  query = "peptide=true or nucleotide=true"
2049  pep_nuc_target = self.target_origtarget_orig.Select(query)
2050  pep_nuc_model = self.model_origmodel_orig.Select(query)
2051  self._pepnuc_aln_pepnuc_aln = self._aln_helper_aln_helper(pep_nuc_target, pep_nuc_model)
2052 
2053  def _compute_aln(self):
2054  self._aln_aln = self._aln_helper_aln_helper(self.targettarget, self.modelmodel)
2055 
2056  def _compute_stereochecked_aln(self):
2057  self._stereochecked_aln_stereochecked_aln = self._aln_helper_aln_helper(self.stereochecked_targetstereochecked_target,
2058  self.stereochecked_modelstereochecked_model)
2059 
2060  def _compute_lddt(self):
2061  LogScript("Computing all-atom lDDT")
2062  # lDDT requires a flat mapping with mdl_ch as key and trg_ch as value
2063  flat_mapping = self.mappingmapping.GetFlatMapping(mdl_as_key=True)
2064 
2065  # make alignments accessible by mdl seq name
2066  stereochecked_alns = dict()
2067  for aln in self.stereochecked_alnstereochecked_aln:
2068  mdl_seq = aln.GetSequence(1)
2069  stereochecked_alns[mdl_seq.name] = aln
2070  alns = dict()
2071  for aln in self.alnaln:
2072  mdl_seq = aln.GetSequence(1)
2073  alns[mdl_seq.name] = aln
2074 
2075  # score variables to be set
2076  lddt_score = None
2077  local_lddt = None
2078  aa_local_lddt = None
2079 
2080  if self.lddt_no_stereocheckslddt_no_stereochecks:
2081  lddt_chain_mapping = dict()
2082  for mdl_ch, trg_ch in flat_mapping.items():
2083  if mdl_ch in alns:
2084  lddt_chain_mapping[mdl_ch] = trg_ch
2085  lddt_score = self.lddt_scorerlddt_scorer.lDDT(self.modelmodel,
2086  chain_mapping = lddt_chain_mapping,
2087  residue_mapping = alns,
2088  check_resnames=False,
2089  local_lddt_prop="lddt",
2090  add_mdl_contacts = self.lddt_add_mdl_contactslddt_add_mdl_contacts,
2091  set_atom_props=True)[0]
2092  local_lddt = dict()
2093  aa_local_lddt = dict()
2094  for r in self.modelmodel.residues:
2095 
2096  cname = r.GetChain().GetName()
2097  if cname not in local_lddt:
2098  local_lddt[cname] = dict()
2099  aa_local_lddt[cname] = dict()
2100 
2101  rnum = r.GetNumber()
2102  if rnum not in aa_local_lddt[cname]:
2103  aa_local_lddt[cname][rnum] = dict()
2104 
2105  if r.HasProp("lddt"):
2106  score = round(r.GetFloatProp("lddt"), 3)
2107  local_lddt[cname][rnum] = score
2108  else:
2109  # not covered by trg or skipped in chain mapping procedure
2110  # the latter happens if its part of a super short chain
2111  local_lddt[cname][rnum] = None
2112 
2113  for a in r.atoms:
2114  if a.HasProp("lddt"):
2115  score = round(a.GetFloatProp("lddt"), 3)
2116  aa_local_lddt[cname][rnum][a.GetName()] = score
2117  else:
2118  # not covered by trg or skipped in chain mapping
2119  # procedure the latter happens if its part of a
2120  # super short chain
2121  aa_local_lddt[cname][rnum][a.GetName()] = None
2122 
2123 
2124  else:
2125  lddt_chain_mapping = dict()
2126  for mdl_ch, trg_ch in flat_mapping.items():
2127  if mdl_ch in stereochecked_alns:
2128  lddt_chain_mapping[mdl_ch] = trg_ch
2129  lddt_score = self.lddt_scorerlddt_scorer.lDDT(self.stereochecked_modelstereochecked_model,
2130  chain_mapping = lddt_chain_mapping,
2131  residue_mapping = stereochecked_alns,
2132  check_resnames=False,
2133  local_lddt_prop="lddt",
2134  add_mdl_contacts = self.lddt_add_mdl_contactslddt_add_mdl_contacts,
2135  set_atom_props=True)[0]
2136  local_lddt = dict()
2137  aa_local_lddt = dict()
2138  for r in self.modelmodel.residues:
2139  cname = r.GetChain().GetName()
2140  if cname not in local_lddt:
2141  local_lddt[cname] = dict()
2142  aa_local_lddt[cname] = dict()
2143  rnum = r.GetNumber()
2144  if rnum not in aa_local_lddt[cname]:
2145  aa_local_lddt[cname][rnum] = dict()
2146 
2147  if r.HasProp("lddt"):
2148  score = round(r.GetFloatProp("lddt"), 3)
2149  local_lddt[cname][rnum] = score
2150 
2151  trg_r = None
2152  mdl_r = None
2153 
2154  for a in r.atoms:
2155  if a.HasProp("lddt"):
2156  score = round(a.GetFloatProp("lddt"), 3)
2157  aa_local_lddt[cname][rnum][a.GetName()] = score
2158  else:
2159  # the target residue is there since we have a score
2160  # for the residue.
2161  # opt 1: The atom was never there in the
2162  # stereochecked target => None
2163  # opt 2: The atom has been removed in the model
2164  # stereochecks but is there in stereochecked
2165  # target => 0.0
2166  if trg_r is None:
2167  if cname in flat_mapping:
2168  for col in alns[cname]:
2169  if col[0] != '-' and col[1] != '-':
2170  if col.GetResidue(1).number == r.number:
2171  trg_r = col.GetResidue(0)
2172  break
2173  if trg_r is not None:
2174  trg_cname = trg_r.GetChain().GetName()
2175  trg_rnum = trg_r.GetNumber()
2176  tmp = self.stereochecked_targetstereochecked_target.FindResidue(trg_cname,
2177  trg_rnum)
2178  if tmp.IsValid():
2179  trg_r = tmp
2180 
2181  if mdl_r is None:
2182  tmp = self.stereochecked_modelstereochecked_model.FindResidue(cname, rnum)
2183  if tmp.IsValid():
2184  mdl_r = tmp
2185 
2186  if trg_r is not None and not trg_r.FindAtom(a.GetName()).IsValid():
2187  # opt 1
2188  aa_local_lddt[cname][rnum][a.GetName()] = None
2189  elif trg_r is not None and trg_r.FindAtom(a.GetName()).IsValid() and \
2190  mdl_r is not None and not mdl_r.FindAtom(a.GetName()).IsValid():
2191  # opt 2
2192  aa_local_lddt[cname][rnum][a.GetName()] = 0.0
2193  else:
2194  # unknown issue
2195  aa_local_lddt[cname][rnum][a.GetName()] = None
2196 
2197  else:
2198  mdl_res = self.stereochecked_modelstereochecked_model.FindResidue(cname, rnum)
2199  if mdl_res.IsValid():
2200  # not covered by trg or skipped in chain mapping procedure
2201  # the latter happens if its part of a super short chain
2202  local_lddt[cname][rnum] = None
2203  for a in r.atoms:
2204  aa_local_lddt[cname][rnum][a.GetName()] = None
2205  else:
2206  # opt 1: removed by stereochecks => assign 0.0
2207  # opt 2: removed by stereochecks AND not covered by ref
2208  # => assign None
2209  # fetch trg residue from non-stereochecked aln
2210  trg_r = None
2211  if cname in flat_mapping:
2212  for col in alns[cname]:
2213  if col[0] != '-' and col[1] != '-':
2214  if col.GetResidue(1).number == r.number:
2215  trg_r = col.GetResidue(0)
2216  break
2217  if trg_r is not None:
2218  trg_cname = trg_r.GetChain().GetName()
2219  trg_rnum = trg_r.GetNumber()
2220  tmp = self.stereochecked_targetstereochecked_target.FindResidue(trg_cname,
2221  trg_rnum)
2222  if tmp.IsValid():
2223  trg_r = tmp
2224 
2225  if trg_r is None:
2226  local_lddt[cname][rnum] = None
2227  for a in r.atoms:
2228  aa_local_lddt[cname][rnum][a.GetName()] = None
2229  else:
2230  local_lddt[cname][rnum] = 0.0
2231  for a in r.atoms:
2232  if trg_r.FindAtom(a.GetName()).IsValid():
2233  aa_local_lddt[cname][rnum][a.GetName()] = 0.0
2234  else:
2235  aa_local_lddt[cname][rnum][a.GetName()] = None
2236 
2237  self._lddt_lddt = lddt_score
2238  self._local_lddt_local_lddt = local_lddt
2239  self._aa_local_lddt_aa_local_lddt = aa_local_lddt
2240 
2241  def _compute_bb_lddt(self):
2242  LogScript("Computing backbone lDDT")
2243  # make alignments accessible by mdl seq name
2244  alns = dict()
2245  for aln in self.alnaln:
2246  mdl_seq = aln.GetSequence(1)
2247  alns[mdl_seq.name] = aln
2248 
2249  # lDDT requires a flat mapping with mdl_ch as key and trg_ch as value
2250  flat_mapping = self.mappingmapping.GetFlatMapping(mdl_as_key=True)
2251  lddt_chain_mapping = dict()
2252  for mdl_ch, trg_ch in flat_mapping.items():
2253  if mdl_ch in alns:
2254  lddt_chain_mapping[mdl_ch] = trg_ch
2255 
2256  lddt_score = self.bb_lddt_scorerbb_lddt_scorer.lDDT(self.modelmodel,
2257  chain_mapping = lddt_chain_mapping,
2258  residue_mapping = alns,
2259  check_resnames=False,
2260  local_lddt_prop="bb_lddt",
2261  add_mdl_contacts = self.lddt_add_mdl_contactslddt_add_mdl_contacts)[0]
2262  local_lddt = dict()
2263  for r in self.modelmodel.residues:
2264  cname = r.GetChain().GetName()
2265  if cname not in local_lddt:
2266  local_lddt[cname] = dict()
2267  if r.HasProp("bb_lddt"):
2268  score = round(r.GetFloatProp("bb_lddt"), 3)
2269  local_lddt[cname][r.GetNumber()] = score
2270  else:
2271  # not covered by trg or skipped in chain mapping procedure
2272  # the latter happens if its part of a super short chain
2273  local_lddt[cname][r.GetNumber()] = None
2274 
2275  self._bb_lddt_bb_lddt = lddt_score
2276  self._bb_local_lddt_bb_local_lddt = local_lddt
2277 
2278  def _compute_ilddt(self):
2279  LogScript("Computing all-atom ilDDT")
2280  # lDDT requires a flat mapping with mdl_ch as key and trg_ch as value
2281  flat_mapping = self.mappingmapping.GetFlatMapping(mdl_as_key=True)
2282 
2283  if self.lddt_no_stereocheckslddt_no_stereochecks:
2284  alns = dict()
2285  for aln in self.alnaln:
2286  mdl_seq = aln.GetSequence(1)
2287  alns[mdl_seq.name] = aln
2288  lddt_chain_mapping = dict()
2289  for mdl_ch, trg_ch in flat_mapping.items():
2290  if mdl_ch in alns:
2291  lddt_chain_mapping[mdl_ch] = trg_ch
2292  self._ilddt_ilddt = self.lddt_scorerlddt_scorer.lDDT(self.modelmodel,
2293  chain_mapping = lddt_chain_mapping,
2294  residue_mapping = alns,
2295  check_resnames=False,
2296  local_lddt_prop="lddt",
2297  add_mdl_contacts = self.lddt_add_mdl_contactslddt_add_mdl_contacts,
2298  no_intrachain=True)[0]
2299  else:
2300  alns = dict()
2301  for aln in self.stereochecked_alnstereochecked_aln:
2302  mdl_seq = aln.GetSequence(1)
2303  alns[mdl_seq.name] = aln
2304  lddt_chain_mapping = dict()
2305  for mdl_ch, trg_ch in flat_mapping.items():
2306  if mdl_ch in alns:
2307  lddt_chain_mapping[mdl_ch] = trg_ch
2308  self._ilddt_ilddt = self.lddt_scorerlddt_scorer.lDDT(self.stereochecked_modelstereochecked_model,
2309  chain_mapping = lddt_chain_mapping,
2310  residue_mapping = alns,
2311  check_resnames=False,
2312  local_lddt_prop="lddt",
2313  add_mdl_contacts = self.lddt_add_mdl_contactslddt_add_mdl_contacts,
2314  no_intrachain=True)[0]
2315 
2316 
2317  def _compute_qs(self):
2318  LogScript("Computing global QS-score")
2319  qs_score_result = self.qs_scorerqs_scorer.Score(self.mappingmapping.mapping)
2320  self._qs_global_qs_global = qs_score_result.QS_global
2321  self._qs_best_qs_best = qs_score_result.QS_best
2322 
2323  def _compute_per_interface_qs_scores(self):
2324  LogScript("Computing per-interface QS-score")
2325  self._per_interface_qs_global_per_interface_qs_global = list()
2326  self._per_interface_qs_best_per_interface_qs_best = list()
2327 
2328  for interface in self.qs_interfacesqs_interfaces:
2329  trg_ch1 = interface[0]
2330  trg_ch2 = interface[1]
2331  mdl_ch1 = interface[2]
2332  mdl_ch2 = interface[3]
2333  qs_res = self.qs_scorerqs_scorer.ScoreInterface(trg_ch1, trg_ch2,
2334  mdl_ch1, mdl_ch2)
2335  self._per_interface_qs_best_per_interface_qs_best.append(qs_res.QS_best)
2336  self._per_interface_qs_global_per_interface_qs_global.append(qs_res.QS_global)
2337 
2338  def _compute_ics_scores(self):
2339  LogScript("Computing ICS scores")
2340  contact_scorer_res = self.contact_scorercontact_scorer.ScoreICS(self.mappingmapping.mapping)
2341  self._ics_precision_ics_precision = contact_scorer_res.precision
2342  self._ics_recall_ics_recall = contact_scorer_res.recall
2343  self._ics_ics = contact_scorer_res.ics
2344  self._per_interface_ics_precision_per_interface_ics_precision = list()
2345  self._per_interface_ics_recall_per_interface_ics_recall = list()
2346  self._per_interface_ics_per_interface_ics = list()
2347  flat_mapping = self.mappingmapping.GetFlatMapping()
2348  for trg_int in self.contact_target_interfacescontact_target_interfaces:
2349  trg_ch1 = trg_int[0]
2350  trg_ch2 = trg_int[1]
2351  if trg_ch1 in flat_mapping and trg_ch2 in flat_mapping:
2352  mdl_ch1 = flat_mapping[trg_ch1]
2353  mdl_ch2 = flat_mapping[trg_ch2]
2354  res = self.contact_scorercontact_scorer.ScoreICSInterface(trg_ch1, trg_ch2,
2355  mdl_ch1, mdl_ch2)
2356  self._per_interface_ics_precision_per_interface_ics_precision.append(res.precision)
2357  self._per_interface_ics_recall_per_interface_ics_recall.append(res.recall)
2358  self._per_interface_ics_per_interface_ics.append(res.ics)
2359  else:
2360  self._per_interface_ics_precision_per_interface_ics_precision.append(None)
2361  self._per_interface_ics_recall_per_interface_ics_recall.append(None)
2362  self._per_interface_ics_per_interface_ics.append(None)
2363 
2364  def _trim_model(self):
2365  trimmed_mdl = mol.CreateEntityFromView(self.mappingmapping.model, True)
2366  trimmed_aln = dict()
2367 
2368  for trg_cname, mdl_cname in self.mappingmapping.GetFlatMapping().items():
2369  mdl_ch = trimmed_mdl.FindChain(mdl_cname)
2370  aln = self.mappingmapping.alns[(trg_cname, mdl_cname)]
2371 
2372  # some limited test that stuff matches
2373  assert(mdl_ch.GetResidueCount() == \
2374  len(aln.GetSequence(1).GetGaplessString()))
2375 
2376  mdl_residues = mdl_ch.residues
2377  mdl_res_idx = 0
2378  aligned_mdl_seq = ['-'] * aln.GetLength()
2379  for col_idx, col in enumerate(aln):
2380  if col[0] != '-' and col[1] != '-':
2381  mdl_res = mdl_residues[mdl_res_idx]
2382  mdl_res.SetIntProp("aligned", 1)
2383  aligned_mdl_seq[col_idx] = col[1]
2384  if col[1] != '-':
2385  mdl_res_idx += 1
2386  aligned_mdl_seq = ''.join(aligned_mdl_seq)
2387  aligned_mdl_seq = seq.CreateSequence(mdl_cname, aligned_mdl_seq)
2388 
2389  new_aln = seq.CreateAlignment()
2390  new_aln.AddSequence(aln.GetSequence(0))
2391  new_aln.AddSequence(aligned_mdl_seq)
2392  trimmed_aln[(trg_cname, mdl_cname)] = new_aln
2393 
2394  self._trimmed_model_trimmed_model = trimmed_mdl.Select("graligned:0=1")
2395  self._trimmed_aln_trimmed_aln = trimmed_aln
2396 
2397  def _compute_ics_scores_trimmed(self):
2398  LogScript("Computing ICS scores trimmed")
2399 
2400  # this is an ugly hack without any efficiency in mind
2401  # we're simply taking the entities from mapper and construct
2402  # a new contact scorer from scratch
2403 
2404  contact_scorer_res = self.trimmed_contact_scorertrimmed_contact_scorer.ScoreICS(self.mappingmapping.mapping)
2405  self._ics_trimmed_ics_trimmed = contact_scorer_res.ics
2406  self._ics_precision_trimmed_ics_precision_trimmed = contact_scorer_res.precision
2407  self._ics_recall_trimmed_ics_recall_trimmed = contact_scorer_res.recall
2408 
2409  self._per_interface_ics_precision_trimmed_per_interface_ics_precision_trimmed = list()
2410  self._per_interface_ics_recall_trimmed_per_interface_ics_recall_trimmed = list()
2411  self._per_interface_ics_trimmed_per_interface_ics_trimmed = list()
2412  flat_mapping = self.mappingmapping.GetFlatMapping()
2413  for trg_int in self.contact_target_interfacescontact_target_interfaces:
2414  trg_ch1 = trg_int[0]
2415  trg_ch2 = trg_int[1]
2416  if trg_ch1 in flat_mapping and trg_ch2 in flat_mapping:
2417  mdl_ch1 = flat_mapping[trg_ch1]
2418  mdl_ch2 = flat_mapping[trg_ch2]
2419  res = self.trimmed_contact_scorertrimmed_contact_scorer.ScoreICSInterface(trg_ch1, trg_ch2,
2420  mdl_ch1, mdl_ch2)
2421  self._per_interface_ics_precision_trimmed_per_interface_ics_precision_trimmed.append(res.precision)
2422  self._per_interface_ics_recall_trimmed_per_interface_ics_recall_trimmed.append(res.recall)
2423  self._per_interface_ics_trimmed_per_interface_ics_trimmed.append(res.ics)
2424  else:
2425  self._per_interface_ics_precision_trimmed_per_interface_ics_precision_trimmed.append(None)
2426  self._per_interface_ics_recall_trimmed_per_interface_ics_recall_trimmed.append(None)
2427  self._per_interface_ics_trimmed_per_interface_ics_trimmed.append(None)
2428 
2429  def _compute_ips_scores(self):
2430  LogScript("Computing IPS scores")
2431  contact_scorer_res = self.contact_scorercontact_scorer.ScoreIPS(self.mappingmapping.mapping)
2432  self._ips_precision_ips_precision = contact_scorer_res.precision
2433  self._ips_recall_ips_recall = contact_scorer_res.recall
2434  self._ips_ips = contact_scorer_res.ips
2435 
2436  self._per_interface_ips_precision_per_interface_ips_precision = list()
2437  self._per_interface_ips_recall_per_interface_ips_recall = list()
2438  self._per_interface_ips_per_interface_ips = list()
2439  flat_mapping = self.mappingmapping.GetFlatMapping()
2440  for trg_int in self.contact_target_interfacescontact_target_interfaces:
2441  trg_ch1 = trg_int[0]
2442  trg_ch2 = trg_int[1]
2443  if trg_ch1 in flat_mapping and trg_ch2 in flat_mapping:
2444  mdl_ch1 = flat_mapping[trg_ch1]
2445  mdl_ch2 = flat_mapping[trg_ch2]
2446  res = self.contact_scorercontact_scorer.ScoreIPSInterface(trg_ch1, trg_ch2,
2447  mdl_ch1, mdl_ch2)
2448  self._per_interface_ips_precision_per_interface_ips_precision.append(res.precision)
2449  self._per_interface_ips_recall_per_interface_ips_recall.append(res.recall)
2450  self._per_interface_ips_per_interface_ips.append(res.ips)
2451  else:
2452  self._per_interface_ips_precision_per_interface_ips_precision.append(None)
2453  self._per_interface_ips_recall_per_interface_ips_recall.append(None)
2454  self._per_interface_ips_per_interface_ips.append(None)
2455 
2456  def _compute_ips_scores_trimmed(self):
2457  LogScript("Computing IPS scores trimmed")
2458 
2459  # this is an ugly hack without any efficiency in mind
2460  # we're simply taking the entities from mapper and construct
2461  # a new contact scorer from scratch
2462  contact_scorer_res = self.trimmed_contact_scorertrimmed_contact_scorer.ScoreIPS(self.mappingmapping.mapping)
2463  self._ips_precision_trimmed_ips_precision_trimmed = contact_scorer_res.precision
2464  self._ips_recall_trimmed_ips_recall_trimmed = contact_scorer_res.recall
2465  self._ips_trimmed_ips_trimmed = contact_scorer_res.ips
2466 
2467  self._per_interface_ips_precision_trimmed_per_interface_ips_precision_trimmed = list()
2468  self._per_interface_ips_recall_trimmed_per_interface_ips_recall_trimmed = list()
2469  self._per_interface_ips_trimmed_per_interface_ips_trimmed = list()
2470  flat_mapping = self.mappingmapping.GetFlatMapping()
2471  for trg_int in self.contact_target_interfacescontact_target_interfaces:
2472  trg_ch1 = trg_int[0]
2473  trg_ch2 = trg_int[1]
2474  if trg_ch1 in flat_mapping and trg_ch2 in flat_mapping:
2475  mdl_ch1 = flat_mapping[trg_ch1]
2476  mdl_ch2 = flat_mapping[trg_ch2]
2477  res = self.trimmed_contact_scorertrimmed_contact_scorer.ScoreIPSInterface(trg_ch1, trg_ch2,
2478  mdl_ch1, mdl_ch2)
2479  self._per_interface_ips_precision_trimmed_per_interface_ips_precision_trimmed.append(res.precision)
2480  self._per_interface_ips_recall_trimmed_per_interface_ips_recall_trimmed.append(res.recall)
2481  self._per_interface_ips_trimmed_per_interface_ips_trimmed.append(res.ips)
2482  else:
2483  self._per_interface_ips_precision_trimmed_per_interface_ips_precision_trimmed.append(None)
2484  self._per_interface_ips_recall_trimmed_per_interface_ips_recall_trimmed.append(None)
2485  self._per_interface_ips_trimmed_per_interface_ips_trimmed.append(None)
2486 
2487  def _compute_dockq_scores(self):
2488  LogScript("Computing DockQ")
2489 
2490  if self.dockq_capri_peptidedockq_capri_peptide and len(self.chain_mapperchain_mapper.polynuc_seqs) > 0:
2491  raise RuntimeError("Cannot compute DockQ for reference structures "
2492  "with nucleotide chains if dockq_capri_peptide "
2493  "is enabled.")
2494 
2495  # lists with values in contact_target_interfaces
2496  self._dockq_scores_dockq_scores = list()
2497  self._fnat_fnat = list()
2498  self._fnonnat_fnonnat = list()
2499  self._irmsd_irmsd = list()
2500  self._lrmsd_lrmsd = list()
2501  self._nnat_nnat = list()
2502  self._nmdl_nmdl = list()
2503 
2504  dockq_alns = dict()
2505  for aln in self.alnaln:
2506  trg_s = aln.GetSequence(0)
2507  mdl_s = aln.GetSequence(1)
2508  dockq_alns[(trg_s.GetName(), mdl_s.GetName())] = aln
2509 
2510  for interface in self.dockq_interfacesdockq_interfaces:
2511  trg_ch1 = interface[0]
2512  trg_ch2 = interface[1]
2513  mdl_ch1 = interface[2]
2514  mdl_ch2 = interface[3]
2515  aln1 = dockq_alns[(trg_ch1, mdl_ch1)]
2516  aln2 = dockq_alns[(trg_ch2, mdl_ch2)]
2517  if self.dockq_capri_peptidedockq_capri_peptide:
2518  res = dockq.DockQ(self.modelmodel, self.targettarget, mdl_ch1, mdl_ch2,
2519  trg_ch1, trg_ch2, ch1_aln=aln1,
2520  ch2_aln=aln2, contact_dist_thresh = 4.0,
2521  interface_dist_thresh=8.0,
2522  interface_cb = True)
2523  else:
2524  res = dockq.DockQ(self.modelmodel, self.targettarget, mdl_ch1, mdl_ch2,
2525  trg_ch1, trg_ch2, ch1_aln=aln1,
2526  ch2_aln=aln2)
2527 
2528  self._fnat_fnat.append(res["fnat"])
2529  self._fnonnat_fnonnat.append(res["fnonnat"])
2530  self._irmsd_irmsd.append(res["irmsd"])
2531  self._lrmsd_lrmsd.append(res["lrmsd"])
2532  self._dockq_scores_dockq_scores.append(res["DockQ"])
2533  self._nnat_nnat.append(res["nnat"])
2534  self._nmdl_nmdl.append(res["nmdl"])
2535 
2536  # keep track of native counts in target interfaces which are
2537  # not covered in model in order to compute
2538  # dockq_ave_full/dockq_wave_full in the end
2539  not_covered_counts = list()
2540  proc_trg_interfaces = set([(x[0], x[1]) for x in self.dockq_interfacesdockq_interfaces])
2541  for interface in self.dockq_target_interfacesdockq_target_interfaces:
2542  if interface not in proc_trg_interfaces:
2543  # let's run DockQ with trg as trg/mdl in order to get the native
2544  # contacts out - no need to pass alns as the residue numbers
2545  # match for sure
2546  trg_ch1 = interface[0]
2547  trg_ch2 = interface[1]
2548 
2549  if self.dockq_capri_peptidedockq_capri_peptide:
2550  res = dockq.DockQ(self.targettarget, self.targettarget,
2551  trg_ch1, trg_ch2, trg_ch1, trg_ch2,
2552  contact_dist_thresh = 4.0,
2553  interface_dist_thresh=8.0,
2554  interface_cb = True)
2555  else:
2556  res = dockq.DockQ(self.targettarget, self.targettarget,
2557  trg_ch1, trg_ch2, trg_ch1, trg_ch2)
2558 
2559  not_covered_counts.append(res["nnat"])
2560 
2561  # there are 4 types of combined scores
2562  # - simple average
2563  # - average weighted by native_contacts
2564  # - the two above including nonmapped_contact_interfaces => set DockQ to 0.0
2565  scores = np.array(self._dockq_scores_dockq_scores)
2566  weights = np.array(self._nnat_nnat)
2567  if len(scores) > 0:
2568  self._dockq_ave_dockq_ave = np.mean(scores)
2569  else:
2570  self._dockq_ave_dockq_ave = 0.0
2571  self._dockq_wave_dockq_wave = np.sum(np.multiply(weights/np.sum(weights), scores))
2572  scores = np.append(scores, [0.0]*len(not_covered_counts))
2573  weights = np.append(weights, not_covered_counts)
2574  if len(scores) > 0:
2575  self._dockq_ave_full_dockq_ave_full = np.mean(scores)
2576  else:
2577  self._dockq_ave_full_dockq_ave_full = 0.0
2578  self._dockq_wave_full_dockq_wave_full = np.sum(np.multiply(weights/np.sum(weights),
2579  scores))
2580 
2581  def _extract_mapped_pos(self):
2582  self._mapped_target_pos_mapped_target_pos = geom.Vec3List()
2583  self._mapped_model_pos_mapped_model_pos = geom.Vec3List()
2584  self._n_target_not_mapped_n_target_not_mapped = 0
2585  processed_trg_chains = set()
2586  for trg_ch, mdl_ch in self.mappingmapping.GetFlatMapping().items():
2587  processed_trg_chains.add(trg_ch)
2588  aln = self.mappingmapping.alns[(trg_ch, mdl_ch)]
2589  for col in aln:
2590  if col[0] != '-' and col[1] != '-':
2591  trg_res = col.GetResidue(0)
2592  mdl_res = col.GetResidue(1)
2593  trg_at = trg_res.FindAtom("CA")
2594  mdl_at = mdl_res.FindAtom("CA")
2595  if not trg_at.IsValid():
2596  trg_at = trg_res.FindAtom("C3'")
2597  if not mdl_at.IsValid():
2598  mdl_at = mdl_res.FindAtom("C3'")
2599  self._mapped_target_pos_mapped_target_pos.append(trg_at.GetPos())
2600  self._mapped_model_pos_mapped_model_pos.append(mdl_at.GetPos())
2601  elif col[0] != '-':
2602  self._n_target_not_mapped_n_target_not_mapped += 1
2603  # count number of trg residues from non-mapped chains
2604  for ch in self.mappingmapping.target.chains:
2605  if ch.GetName() not in processed_trg_chains:
2606  self._n_target_not_mapped_n_target_not_mapped += len(ch.residues)
2607 
2608  def _extract_mapped_pos_full_bb(self):
2609  self._mapped_target_pos_full_bb_mapped_target_pos_full_bb = geom.Vec3List()
2610  self._mapped_model_pos_full_bb_mapped_model_pos_full_bb = geom.Vec3List()
2611  exp_pep_atoms = ["N", "CA", "C"]
2612  exp_nuc_atoms = ["\"O5'\"", "\"C5'\"", "\"C4'\"", "\"C3'\"", "\"O3'\""]
2613  trg_pep_chains = [s.GetName() for s in self.chain_mapperchain_mapper.polypep_seqs]
2614  trg_nuc_chains = [s.GetName() for s in self.chain_mapperchain_mapper.polynuc_seqs]
2615  for trg_ch, mdl_ch in self.mappingmapping.GetFlatMapping().items():
2616  aln = self.mappingmapping.alns[(trg_ch, mdl_ch)]
2617  trg_ch = aln.GetSequence(0).GetName()
2618  if trg_ch in trg_pep_chains:
2619  exp_atoms = exp_pep_atoms
2620  elif trg_ch in trg_nuc_chains:
2621  exp_atoms = exp_nuc_atoms
2622  else:
2623  # this should be guaranteed by the chain mapper
2624  raise RuntimeError("Unexpected error - contact OST developer")
2625  for col in aln:
2626  if col[0] != '-' and col[1] != '-':
2627  trg_res = col.GetResidue(0)
2628  mdl_res = col.GetResidue(1)
2629  for aname in exp_atoms:
2630  trg_at = trg_res.FindAtom(aname)
2631  mdl_at = mdl_res.FindAtom(aname)
2632  if not (trg_at.IsValid() and mdl_at.IsValid()):
2633  # this should be guaranteed by the chain mapper
2634  raise RuntimeError("Unexpected error - contact OST "
2635  "developer")
2636  self._mapped_target_pos_full_bb_mapped_target_pos_full_bb.append(trg_at.GetPos())
2637  self._mapped_model_pos_full_bb_mapped_model_pos_full_bb.append(mdl_at.GetPos())
2638 
2639 
2640  def _extract_rigid_mapped_pos(self):
2641  self._rigid_mapped_target_pos_rigid_mapped_target_pos = geom.Vec3List()
2642  self._rigid_mapped_model_pos_rigid_mapped_model_pos = geom.Vec3List()
2643  self._rigid_n_target_not_mapped_rigid_n_target_not_mapped = 0
2644  processed_trg_chains = set()
2645  for trg_ch, mdl_ch in self.rigid_mappingrigid_mapping.GetFlatMapping().items():
2646  processed_trg_chains.add(trg_ch)
2647  aln = self.rigid_mappingrigid_mapping.alns[(trg_ch, mdl_ch)]
2648  for col in aln:
2649  if col[0] != '-' and col[1] != '-':
2650  trg_res = col.GetResidue(0)
2651  mdl_res = col.GetResidue(1)
2652  trg_at = trg_res.FindAtom("CA")
2653  mdl_at = mdl_res.FindAtom("CA")
2654  if not trg_at.IsValid():
2655  trg_at = trg_res.FindAtom("C3'")
2656  if not mdl_at.IsValid():
2657  mdl_at = mdl_res.FindAtom("C3'")
2658  self._rigid_mapped_target_pos_rigid_mapped_target_pos.append(trg_at.GetPos())
2659  self._rigid_mapped_model_pos_rigid_mapped_model_pos.append(mdl_at.GetPos())
2660  elif col[0] != '-':
2661  self._rigid_n_target_not_mapped_rigid_n_target_not_mapped += 1
2662  # count number of trg residues from non-mapped chains
2663  for ch in self.rigid_mappingrigid_mapping.target.chains:
2664  if ch.GetName() not in processed_trg_chains:
2665  self._rigid_n_target_not_mapped_rigid_n_target_not_mapped += len(ch.residues)
2666 
2667  def _extract_rigid_mapped_pos_full_bb(self):
2668  self._rigid_mapped_target_pos_full_bb_rigid_mapped_target_pos_full_bb = geom.Vec3List()
2669  self._rigid_mapped_model_pos_full_bb_rigid_mapped_model_pos_full_bb = geom.Vec3List()
2670  exp_pep_atoms = ["N", "CA", "C"]
2671  exp_nuc_atoms = ["\"O5'\"", "\"C5'\"", "\"C4'\"", "\"C3'\"", "\"O3'\""]
2672  trg_pep_chains = [s.GetName() for s in self.chain_mapperchain_mapper.polypep_seqs]
2673  trg_nuc_chains = [s.GetName() for s in self.chain_mapperchain_mapper.polynuc_seqs]
2674  for trg_ch, mdl_ch in self.rigid_mappingrigid_mapping.GetFlatMapping().items():
2675  aln = self.mappingmapping.alns[(trg_ch, mdl_ch)]
2676  trg_ch = aln.GetSequence(0).GetName()
2677  if trg_ch in trg_pep_chains:
2678  exp_atoms = exp_pep_atoms
2679  elif trg_ch in trg_nuc_chains:
2680  exp_atoms = exp_nuc_atoms
2681  else:
2682  # this should be guaranteed by the chain mapper
2683  raise RuntimeError("Unexpected error - contact OST developer")
2684  for col in aln:
2685  if col[0] != '-' and col[1] != '-':
2686  trg_res = col.GetResidue(0)
2687  mdl_res = col.GetResidue(1)
2688  for aname in exp_atoms:
2689  trg_at = trg_res.FindAtom(aname)
2690  mdl_at = mdl_res.FindAtom(aname)
2691  if not (trg_at.IsValid() and mdl_at.IsValid()):
2692  # this should be guaranteed by the chain mapper
2693  raise RuntimeError("Unexpected error - contact OST "
2694  "developer")
2695  self._rigid_mapped_target_pos_full_bb_rigid_mapped_target_pos_full_bb.append(trg_at.GetPos())
2696  self._rigid_mapped_model_pos_full_bb_rigid_mapped_model_pos_full_bb.append(mdl_at.GetPos())
2697 
2698  def _compute_cad_score(self):
2699  if not self.resnum_alignmentsresnum_alignments:
2700  raise RuntimeError("CAD score computations rely on residue numbers "
2701  "that are consistent between target and model "
2702  "chains, i.e. only work if resnum_alignments "
2703  "is True at Scorer construction.")
2704  try:
2705  LogScript("Computing CAD score")
2706  cad_score_exec = \
2707  settings.Locate("voronota-cadscore",
2708  explicit_file_name=self.cad_score_execcad_score_exec)
2709  except Exception as e:
2710  raise RuntimeError("voronota-cadscore must be in PATH for CAD "
2711  "score scoring") from e
2712  cad_bin_dir = os.path.dirname(cad_score_exec)
2713  m = self.mappingmapping.GetFlatMapping(mdl_as_key=True)
2714  cad_result = cadscore.CADScore(self.modelmodel, self.targettarget,
2715  mode = "voronota",
2716  label="localcad",
2717  old_regime=False,
2718  cad_bin_path=cad_bin_dir,
2719  chain_mapping=m)
2720 
2721  local_cad = dict()
2722  for r in self.modelmodel.residues:
2723  cname = r.GetChain().GetName()
2724  if cname not in local_cad:
2725  local_cad[cname] = dict()
2726  if r.HasProp("localcad"):
2727  score = round(r.GetFloatProp("localcad"), 3)
2728  local_cad[cname][r.GetNumber()] = score
2729  else:
2730  local_cad[cname][r.GetNumber()] = None
2731 
2732  self._cad_score_cad_score = cad_result.globalAA
2733  self._local_cad_score_local_cad_score = local_cad
2734 
2735  def _get_repr_view(self, ent):
2736  """ Returns view with representative peptide atoms => CB, CA for GLY
2737 
2738  Ensures that each residue has exactly one atom with assertions
2739 
2740  :param ent: Entity for which you want the representative view
2741  :param ent: :class:`ost.mol.EntityHandle`/:class:`ost.mol.EntityView`
2742  :returns: :class:`ost.mol.EntityView` derived from ent
2743  """
2744  repr_ent = ent.Select("(aname=\"CB\" or (rname=\"GLY\" and aname=\"CA\"))")
2745  for r in repr_ent.residues:
2746  assert(len(r.atoms) == 1)
2747  return repr_ent
2748 
2749  def _get_interface_residues(self, ent):
2750  """ Get interface residues
2751 
2752  Thats all residues having a contact with at least one residue from another
2753  chain (CB-CB distance <= 8A, CA in case of Glycine)
2754 
2755  :param ent: Model for which to extract interface residues
2756  :type ent: :class:`ost.mol.EntityView`
2757  :returns: :class:`dict` with chain names as key and and :class:`list`
2758  with residue numbers of the respective interface residues.
2759  """
2760  # select for representative positions => CB, CA for GLY
2761  repr_ent = self._get_repr_view_get_repr_view(ent)
2762  result = {ch.GetName(): list() for ch in ent.chains}
2763  for ch in ent.chains:
2764  cname = ch.GetName()
2765  sel = repr_ent.Select(f"(cname={mol.QueryQuoteName(cname)} and 8 <> [cname!={mol.QueryQuoteName(cname)}])")
2766  result[cname] = [r.GetNumber() for r in sel.residues]
2767  return result
2768 
2769  def _do_stereochecks(self):
2770  """ Perform stereochemistry checks on model and target
2771  """
2772  LogInfo("Performing stereochemistry checks on model and target")
2773  data = stereochemistry.GetDefaultStereoData()
2774  l_data = stereochemistry.GetDefaultStereoLinkData()
2775 
2776  a, b, c, d = stereochemistry.StereoCheck(self.modelmodel, stereo_data = data,
2777  stereo_link_data = l_data)
2778  self._stereochecked_model_stereochecked_model = a
2779  self._model_clashes_model_clashes = b
2780  self._model_bad_bonds_model_bad_bonds = c
2781  self._model_bad_angles_model_bad_angles = d
2782 
2783  a, b, c, d = stereochemistry.StereoCheck(self.targettarget, stereo_data = data,
2784  stereo_link_data = l_data)
2785  self._stereochecked_target_stereochecked_target = a
2786  self._target_clashes_target_clashes = b
2787  self._target_bad_bonds_target_bad_bonds = c
2788  self._target_bad_angles_target_bad_angles = d
2789 
2790 
2791  def _get_interface_patches(self, mdl_ch, mdl_rnum):
2792  """ Select interface patches representative for specified residue
2793 
2794  The patches for specified residue r in chain c are selected as follows:
2795 
2796  * mdl_patch_one: All residues in c with CB (CA for GLY) positions within 8A
2797  of r and within 12A of residues from any other chain.
2798  * mdl_patch_two: Closest residue x to r in any other chain gets identified.
2799  Patch is then constructed by selecting all residues from any other chain
2800  within 8A of x and within 12A from any residue in c.
2801  * trg_patch_one: Chain name and residue number based mapping from
2802  mdl_patch_one
2803  * trg_patch_two: Chain name and residue number based mapping from
2804  mdl_patch_two
2805 
2806  :param mdl_ch: Name of chain in *self.model* of residue of interest
2807  :type mdl_ch: :class:`str`
2808  :param mdl_rnum: Residue number of residue of interest
2809  :type mdl_rnum: :class:`ost.mol.ResNum`
2810  :returns: Tuple with 5 elements: 1) :class:`bool` flag whether all residues
2811  in *mdl* patches are covered in *trg* 2) mtl_patch_one
2812  3) mdl_patch_two 4) trg_patch_one 5) trg_patch_two
2813  """
2814  # select for representative positions => CB, CA for GLY
2815  repr_mdl = self._get_repr_view_get_repr_view(self.modelmodel.Select("peptide=true"))
2816 
2817  # get position for specified residue
2818  r = self.modelmodel.FindResidue(mdl_ch, mdl_rnum)
2819  if not r.IsValid():
2820  raise RuntimeError(f"Cannot find residue {mdl_rnum} in chain {mdl_ch}")
2821  if r.GetName() == "GLY":
2822  at = r.FindAtom("CA")
2823  else:
2824  at = r.FindAtom("CB")
2825  if not at.IsValid():
2826  raise RuntimeError("Cannot find interface views for res without CB/CA")
2827  r_pos = at.GetPos()
2828 
2829  # mdl_patch_one contains residues from the same chain as r
2830  # => all residues within 8A of r and within 12A of any other chain
2831 
2832  # q1 selects for everything in same chain and within 8A of r_pos
2833  q1 = f"(cname={mol.QueryQuoteName(mdl_ch)} and 8 <> {{{r_pos[0]},{r_pos[1]},{r_pos[2]}}})"
2834  # q2 selects for everything within 12A of any other chain
2835  q2 = f"(12 <> [cname!={mol.QueryQuoteName(mdl_ch)}])"
2836  mdl_patch_one = self.modelmodel.CreateEmptyView()
2837  sel = repr_mdl.Select(" and ".join([q1, q2]))
2838  for r in sel.residues:
2839  mdl_r = self.modelmodel.FindResidue(r.GetChain().GetName(), r.GetNumber())
2840  mdl_patch_one.AddResidue(mdl_r, mol.ViewAddFlag.INCLUDE_ALL)
2841 
2842  # mdl_patch_two contains residues from all other chains. In detail:
2843  # the closest residue to r is identified in any other chain, and the
2844  # patch is filled with residues that are within 8A of that residue and
2845  # within 12A of chain from r
2846  sel = repr_mdl.Select(f"(cname!={mol.QueryQuoteName(mdl_ch)})")
2847  close_stuff = sel.FindWithin(r_pos, 8)
2848  min_pos = None
2849  min_dist = 42.0
2850  for close_at in close_stuff:
2851  dist = geom.Distance(r_pos, close_at.GetPos())
2852  if dist < min_dist:
2853  min_pos = close_at.GetPos()
2854  min_dist = dist
2855 
2856  # q1 selects for everything not in mdl_ch but within 8A of min_pos
2857  q1 = f"(cname!={mol.QueryQuoteName(mdl_ch)} and 8 <> {{{min_pos[0]},{min_pos[1]},{min_pos[2]}}})"
2858  # q2 selects for everything within 12A of mdl_ch
2859  q2 = f"(12 <> [cname={mol.QueryQuoteName(mdl_ch)}])"
2860  mdl_patch_two = self.modelmodel.CreateEmptyView()
2861  sel = repr_mdl.Select(" and ".join([q1, q2]))
2862  for r in sel.residues:
2863  mdl_r = self.modelmodel.FindResidue(r.GetChain().GetName(), r.GetNumber())
2864  mdl_patch_two.AddResidue(mdl_r, mol.ViewAddFlag.INCLUDE_ALL)
2865 
2866  # transfer mdl residues to trg
2867  flat_mapping = self.mappingmapping.GetFlatMapping(mdl_as_key=True)
2868  full_trg_coverage = True
2869  trg_patch_one = self.targettarget.CreateEmptyView()
2870  for r in mdl_patch_one.residues:
2871  trg_r = None
2872  mdl_cname = r.GetChain().GetName()
2873  if mdl_cname in flat_mapping:
2874  aln = self.mappingmapping.alns[(flat_mapping[mdl_cname], mdl_cname)]
2875  for col in aln:
2876  if col[0] != '-' and col[1] != '-':
2877  if col.GetResidue(1).GetNumber() == r.GetNumber():
2878  trg_r = col.GetResidue(0)
2879  break
2880  if trg_r is not None:
2881  trg_patch_one.AddResidue(trg_r.handle,
2882  mol.ViewAddFlag.INCLUDE_ALL)
2883  else:
2884  full_trg_coverage = False
2885 
2886  trg_patch_two = self.targettarget.CreateEmptyView()
2887  for r in mdl_patch_two.residues:
2888  trg_r = None
2889  mdl_cname = r.GetChain().GetName()
2890  if mdl_cname in flat_mapping:
2891  aln = self.mappingmapping.alns[(flat_mapping[mdl_cname], mdl_cname)]
2892  for col in aln:
2893  if col[0] != '-' and col[1] != '-':
2894  if col.GetResidue(1).GetNumber() == r.GetNumber():
2895  trg_r = col.GetResidue(0)
2896  break
2897  if trg_r is not None:
2898  trg_patch_two.AddResidue(trg_r.handle,
2899  mol.ViewAddFlag.INCLUDE_ALL)
2900  else:
2901  full_trg_coverage = False
2902 
2903  return (full_trg_coverage, mdl_patch_one, mdl_patch_two,
2904  trg_patch_one, trg_patch_two)
2905 
2906  def _compute_patchqs_scores(self):
2907  LogScript("Computing patch QS-scores")
2908  self._patch_qs_patch_qs = dict()
2909  for cname, rnums in self.model_interface_residuesmodel_interface_residues.items():
2910  scores = list()
2911  for rnum in rnums:
2912  score = None
2913  r = self.modelmodel.FindResidue(cname, rnum)
2914  if r.IsValid() and r.GetChemType() == mol.ChemType.AMINOACIDS:
2915  full_trg_coverage, mdl_patch_one, mdl_patch_two, \
2916  trg_patch_one, trg_patch_two = \
2917  self._get_interface_patches_get_interface_patches(cname, rnum)
2918  if full_trg_coverage:
2919  score = self._patchqs_patchqs(mdl_patch_one, mdl_patch_two,
2920  trg_patch_one, trg_patch_two)
2921  scores.append(score)
2922  self._patch_qs_patch_qs[cname] = scores
2923 
2924  def _compute_patchdockq_scores(self):
2925  LogScript("Computing patch DockQ scores")
2926  self._patch_dockq_patch_dockq = dict()
2927  for cname, rnums in self.model_interface_residuesmodel_interface_residues.items():
2928  scores = list()
2929  for rnum in rnums:
2930  score = None
2931  r = self.modelmodel.FindResidue(cname, rnum)
2932  if r.IsValid() and r.GetChemType() == mol.ChemType.AMINOACIDS:
2933  full_trg_coverage, mdl_patch_one, mdl_patch_two, \
2934  trg_patch_one, trg_patch_two = \
2935  self._get_interface_patches_get_interface_patches(cname, rnum)
2936  if full_trg_coverage:
2937  score = self._patchdockq_patchdockq(mdl_patch_one, mdl_patch_two,
2938  trg_patch_one, trg_patch_two)
2939  scores.append(score)
2940  self._patch_dockq_patch_dockq[cname] = scores
2941 
2942  def _patchqs(self, mdl_patch_one, mdl_patch_two, trg_patch_one, trg_patch_two):
2943  """ Score interface residue patches with QS-score
2944 
2945  In detail: Construct two entities with two chains each. First chain
2946  consists of residues from <x>_patch_one and second chain consists of
2947  <x>_patch_two. The returned score is the QS-score between the two
2948  entities
2949 
2950  :param mdl_patch_one: Interface patch representing scored residue
2951  :type mdl_patch_one: :class:`ost.mol.EntityView`
2952  :param mdl_patch_two: Interface patch representing scored residue
2953  :type mdl_patch_two: :class:`ost.mol.EntityView`
2954  :param trg_patch_one: Interface patch representing scored residue
2955  :type trg_patch_one: :class:`ost.mol.EntityView`
2956  :param trg_patch_two: Interface patch representing scored residue
2957  :type trg_patch_two: :class:`ost.mol.EntityView`
2958  :returns: PatchQS score
2959  """
2960  qs_ent_mdl = self._qs_ent_from_patches_qs_ent_from_patches(mdl_patch_one, mdl_patch_two)
2961  qs_ent_trg = self._qs_ent_from_patches_qs_ent_from_patches(trg_patch_one, trg_patch_two)
2962 
2963  alnA = seq.CreateAlignment()
2964  s = ''.join([r.one_letter_code for r in mdl_patch_one.residues])
2965  alnA.AddSequence(seq.CreateSequence("A", s))
2966  s = ''.join([r.one_letter_code for r in trg_patch_one.residues])
2967  alnA.AddSequence(seq.CreateSequence("A", s))
2968 
2969  alnB = seq.CreateAlignment()
2970  s = ''.join([r.one_letter_code for r in mdl_patch_two.residues])
2971  alnB.AddSequence(seq.CreateSequence("B", s))
2972  s = ''.join([r.one_letter_code for r in trg_patch_two.residues])
2973  alnB.AddSequence(seq.CreateSequence("B", s))
2974  alns = {("A", "A"): alnA, ("B", "B"): alnB}
2975 
2976  scorer = QSScorer(qs_ent_mdl, [["A"], ["B"]], qs_ent_trg, alns)
2977  score_result = scorer.Score([["A"], ["B"]])
2978 
2979  return score_result.QS_global
2980 
2981  def _patchdockq(self, mdl_patch_one, mdl_patch_two, trg_patch_one,
2982  trg_patch_two):
2983  """ Score interface residue patches with DockQ
2984 
2985  In detail: Construct two entities with two chains each. First chain
2986  consists of residues from <x>_patch_one and second chain consists of
2987  <x>_patch_two. The returned score is the QS-score between the two
2988  entities
2989 
2990  :param mdl_patch_one: Interface patch representing scored residue
2991  :type mdl_patch_one: :class:`ost.mol.EntityView`
2992  :param mdl_patch_two: Interface patch representing scored residue
2993  :type mdl_patch_two: :class:`ost.mol.EntityView`
2994  :param trg_patch_one: Interface patch representing scored residue
2995  :type trg_patch_one: :class:`ost.mol.EntityView`
2996  :param trg_patch_two: Interface patch representing scored residue
2997  :type trg_patch_two: :class:`ost.mol.EntityView`
2998  :returns: DockQ score
2999  """
3000  m = self._qs_ent_from_patches_qs_ent_from_patches(mdl_patch_one, mdl_patch_two)
3001  t = self._qs_ent_from_patches_qs_ent_from_patches(trg_patch_one, trg_patch_two)
3002  dockq_result = dockq.DockQ(t, m, "A", "B", "A", "B")
3003  if dockq_result["nnat"] > 0:
3004  return dockq_result["DockQ"]
3005  return 0.0
3006 
3007  def _qs_ent_from_patches(self, patch_one, patch_two):
3008  """ Constructs Entity with two chains named "A" and "B""
3009 
3010  Blindly adds all residues from *patch_one* to chain A and residues from
3011  patch_two to chain B.
3012  """
3013  ent = mol.CreateEntity()
3014  ed = ent.EditXCS()
3015  added_ch = ed.InsertChain("A")
3016  for r in patch_one.residues:
3017  added_r = ed.AppendResidue(added_ch, r.GetName())
3018  added_r.SetChemClass(str(r.GetChemClass()))
3019  for a in r.atoms:
3020  ed.InsertAtom(added_r, a.handle)
3021  added_ch = ed.InsertChain("B")
3022  for r in patch_two.residues:
3023  added_r = ed.AppendResidue(added_ch, r.GetName())
3024  added_r.SetChemClass(str(r.GetChemClass()))
3025  for a in r.atoms:
3026  ed.InsertAtom(added_r, a.handle)
3027  return ent
3028 
3029  def _construct_custom_mapping(self, mapping):
3030  """ constructs a full blown MappingResult object from simple dict
3031 
3032  :param mapping: mapping with trg chains as key and mdl ch as values
3033  :type mapping: :class:`dict`
3034  """
3035  LogInfo("Setting custom chain mapping")
3036 
3037  chain_mapper = self.chain_mapperchain_mapper
3038  chem_mapping, chem_group_alns, mdl = \
3039  chain_mapper.GetChemMapping(self.modelmodel)
3040 
3041  # now that we have a chem mapping, lets do consistency checks
3042  # - check whether chain names are unique and available in structures
3043  # - check whether the mapped chains actually map to the same chem groups
3044  if len(mapping) != len(set(mapping.keys())):
3045  raise RuntimeError(f"Expect unique trg chain names in mapping. Got "
3046  f"{mapping.keys()}")
3047  if len(mapping) != len(set(mapping.values())):
3048  raise RuntimeError(f"Expect unique mdl chain names in mapping. Got "
3049  f"{mapping.values()}")
3050 
3051  trg_chains = set([ch.GetName() for ch in chain_mapper.target.chains])
3052  mdl_chains = set([ch.GetName() for ch in mdl.chains])
3053  for k,v in mapping.items():
3054  if k not in trg_chains:
3055  raise RuntimeError(f"Target chain \"{k}\" is not available "
3056  f"in target processed for chain mapping "
3057  f"({trg_chains})")
3058  if v not in mdl_chains:
3059  raise RuntimeError(f"Model chain \"{v}\" is not available "
3060  f"in model processed for chain mapping "
3061  f"({mdl_chains})")
3062 
3063  for trg_ch, mdl_ch in mapping.items():
3064  trg_group_idx = None
3065  mdl_group_idx = None
3066  for idx, group in enumerate(chain_mapper.chem_groups):
3067  if trg_ch in group:
3068  trg_group_idx = idx
3069  break
3070  for idx, group in enumerate(chem_mapping):
3071  if mdl_ch in group:
3072  mdl_group_idx = idx
3073  break
3074  if trg_group_idx is None or mdl_group_idx is None:
3075  raise RuntimeError("Could not establish a valid chem grouping "
3076  "of chain names provided in custom mapping.")
3077 
3078  if trg_group_idx != mdl_group_idx:
3079  raise RuntimeError(f"Chem group mismatch in custom mapping: "
3080  f"target chain \"{trg_ch}\" groups with the "
3081  f"following chemically equivalent target "
3082  f"chains: "
3083  f"{chain_mapper.chem_groups[trg_group_idx]} "
3084  f"but model chain \"{mdl_ch}\" maps to the "
3085  f"following target chains: "
3086  f"{chain_mapper.chem_groups[mdl_group_idx]}")
3087 
3088  pairs = set([(trg_ch, mdl_ch) for trg_ch, mdl_ch in mapping.items()])
3089  ref_mdl_alns = \
3090  chain_mapping._GetRefMdlAlns(chain_mapper.chem_groups,
3091  chain_mapper.chem_group_alignments,
3092  chem_mapping,
3093  chem_group_alns,
3094  pairs = pairs)
3095 
3096  # translate mapping format
3097  final_mapping = list()
3098  for ref_chains in chain_mapper.chem_groups:
3099  mapped_mdl_chains = list()
3100  for ref_ch in ref_chains:
3101  if ref_ch in mapping:
3102  mapped_mdl_chains.append(mapping[ref_ch])
3103  else:
3104  mapped_mdl_chains.append(None)
3105  final_mapping.append(mapped_mdl_chains)
3106 
3107  alns = dict()
3108  for ref_group, mdl_group in zip(chain_mapper.chem_groups,
3109  final_mapping):
3110  for ref_ch, mdl_ch in zip(ref_group, mdl_group):
3111  if ref_ch is not None and mdl_ch is not None:
3112  aln = ref_mdl_alns[(ref_ch, mdl_ch)]
3113  trg_view = chain_mapper.target.Select(f"cname={mol.QueryQuoteName(ref_ch)}")
3114  mdl_view = mdl.Select(f"cname={mol.QueryQuoteName(mdl_ch)}")
3115  aln.AttachView(0, trg_view)
3116  aln.AttachView(1, mdl_view)
3117  alns[(ref_ch, mdl_ch)] = aln
3118 
3119  return chain_mapping.MappingResult(chain_mapper.target, mdl,
3120  chain_mapper.chem_groups,
3121  chem_mapping,
3122  final_mapping, alns)
3123 
3124  def _compute_tmscore(self):
3125  res = None
3126  if self.usalign_execusalign_exec is None:
3127  LogScript("Computing TM-score with built-in USalign")
3128  if self.oumoum:
3129  flat_mapping = self.rigid_mappingrigid_mapping.GetFlatMapping()
3130  LogInfo("Overriding TM-score chain mapping")
3131  res = res = bindings.WrappedMMAlign(self.modelmodel, self.targettarget,
3132  mapping=flat_mapping)
3133  else:
3134  res = bindings.WrappedMMAlign(self.modelmodel, self.targettarget)
3135  else:
3136  LogScript("Computing TM-score with USalign exectuable")
3137  if self.oumoum:
3138  LogInfo("Overriding TM-score chain mapping")
3139  flat_mapping = self.rigid_mappingrigid_mapping.GetFlatMapping()
3140  res = tmtools.USAlign(self.modelmodel, self.targettarget,
3141  usalign = self.usalign_execusalign_exec,
3142  custom_chain_mapping = flat_mapping)
3143  else:
3144  res = tmtools.USAlign(self.modelmodel, self.targettarget,
3145  usalign = self.usalign_execusalign_exec)
3146 
3147  self._tm_score_tm_score = res.tm_score
3148  self._usalign_mapping_usalign_mapping = dict()
3149  for a,b in zip(res.ent1_mapped_chains, res.ent2_mapped_chains):
3150  self._usalign_mapping_usalign_mapping[b] = a
3151 
3152  def _resnum_connect(self, ent):
3153  ed = None
3154  for ch in ent.chains:
3155  res_list = ch.residues
3156  for i in range(len(res_list) - 1):
3157  ra = res_list[i]
3158  rb = res_list[i+1]
3159  if ra.GetNumber().GetNum() + 1 == rb.GetNumber().GetNum():
3160  if ra.IsPeptideLinking() and rb.IsPeptideLinking():
3161  c = ra.FindAtom("C")
3162  n = rb.FindAtom("N")
3163  if c.IsValid() and n.IsValid() and not mol.BondExists(c, n):
3164  if ed is None:
3165  ed = ent.EditXCS(mol.BUFFERED_EDIT)
3166  ed.Connect(c,n,1)
3167  elif ra.IsNucleotideLinking() and rb.IsNucleotideLinking():
3168  o = ra.FindAtom("O3'")
3169  p = rb.FindAtom("P")
3170  if o.IsValid() and p.IsValid()and not mol.BondExists(o, p):
3171  if ed is None:
3172  ed = ent.EditXCS(mol.BUFFERED_EDIT)
3173  ed.Connect(o,p,1)
3174 
3175 
3176 # specify public interface
3177 __all__ = ('lDDTBSScorer', 'Scorer',)
definition of EntityView
Definition: entity_view.hh:86
def target_interface_residues(self)
Definition: scoring.py:704
def _patchqs(self, mdl_patch_one, mdl_patch_two, trg_patch_one, trg_patch_two)
Definition: scoring.py:2942
def _get_repr_view(self, ent)
Definition: scoring.py:2735
def qs_target_interfaces(self)
Definition: scoring.py:901
def rigid_mapped_model_pos(self)
Definition: scoring.py:1675
def _compute_per_interface_qs_scores(self)
Definition: scoring.py:2323
def model_interface_residues(self)
Definition: scoring.py:689
def stereochecked_target(self)
Definition: scoring.py:590
def per_interface_ips_precision(self)
Definition: scoring.py:1273
def rigid_mapped_target_pos_full_bb(self)
Definition: scoring.py:1661
def _aln_helper(self, target, model)
Definition: scoring.py:2007
def per_interface_ics_recall(self)
Definition: scoring.py:1086
def transformed_mapped_model_pos(self)
Definition: scoring.py:1600
def _compute_patchqs_scores(self)
Definition: scoring.py:2906
def _compute_stereochecked_aln(self)
Definition: scoring.py:2056
def rigid_transformed_mapped_model_pos(self)
Definition: scoring.py:1703
def trimmed_model_contacts(self)
Definition: scoring.py:999
def _compute_ips_scores_trimmed(self)
Definition: scoring.py:2456
def per_interface_ips_trimmed(self)
Definition: scoring.py:1340
def contact_model_interfaces(self)
Definition: scoring.py:1023
def _patchdockq(self, mdl_patch_one, mdl_patch_two, trg_patch_one, trg_patch_two)
Definition: scoring.py:2982
def per_interface_ips_recall(self)
Definition: scoring.py:1286
def per_interface_ics_precision(self)
Definition: scoring.py:1072
def _resnum_connect(self, ent)
Definition: scoring.py:3152
def _get_interface_residues(self, ent)
Definition: scoring.py:2749
def mapped_target_pos_full_bb(self)
Definition: scoring.py:1558
def __init__(self, model, target, resnum_alignments=False, molck_settings=None, cad_score_exec=None, custom_mapping=None, custom_rigid_mapping=None, usalign_exec=None, lddt_no_stereochecks=False, n_max_naive=40320, oum=False, min_pep_length=6, min_nuc_length=4, lddt_add_mdl_contacts=False, dockq_capri_peptide=False, lddt_symmetry_settings=None, lddt_inclusion_radius=15.0)
Definition: scoring.py:216
def rigid_mapped_target_pos(self)
Definition: scoring.py:1647
def dockq_target_interfaces(self)
Definition: scoring.py:1354
def trimmed_contact_scorer(self)
Definition: scoring.py:770
def per_interface_ics_recall_trimmed(self)
Definition: scoring.py:1204
def _compute_patchdockq_scores(self)
Definition: scoring.py:2924
def _get_interface_patches(self, mdl_ch, mdl_rnum)
Definition: scoring.py:2791
def _extract_rigid_mapped_pos(self)
Definition: scoring.py:2640
def contact_target_interfaces(self)
Definition: scoring.py:1007
def per_interface_qs_global(self)
Definition: scoring.py:954
def _qs_ent_from_patches(self, patch_one, patch_two)
Definition: scoring.py:3007
def rigid_mapped_model_pos_full_bb(self)
Definition: scoring.py:1689
def per_interface_qs_best(self)
Definition: scoring.py:964
def _extract_rigid_mapped_pos_full_bb(self)
Definition: scoring.py:2667
def per_interface_ips_precision_trimmed(self)
Definition: scoring.py:1313
def _compute_ics_scores_trimmed(self)
Definition: scoring.py:2397
def rigid_n_target_not_mapped(self)
Definition: scoring.py:1715
def per_interface_ics_precision_trimmed(self)
Definition: scoring.py:1190
def per_interface_ips_recall_trimmed(self)
Definition: scoring.py:1327
def mapped_model_pos_full_bb(self)
Definition: scoring.py:1586
def per_interface_ics_trimmed(self)
Definition: scoring.py:1217
def _construct_custom_mapping(self, mapping)
Definition: scoring.py:3029
def _extract_mapped_pos_full_bb(self)
Definition: scoring.py:2608
def ScoreBS(self, ligand, radius=4.0, lddt_radius=10.0)
Definition: scoring.py:46
def __init__(self, reference, model, residue_number_alignment=False)
Definition: scoring.py:40
Real DLLEXPORT_OST_GEOM Distance(const Line2 &l, const Vec2 &v)
void Molck(ost::mol::EntityHandle &ent, ost::conop::CompoundLibPtr lib, const MolckSettings &settings, bool prune=true)
void GDT(const geom::Vec3List &mdl_pos, const geom::Vec3List &ref_pos, int window_size, int max_windows, Real distance_thresh, int &n_superposed, geom::Mat4 &transform)