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