From a04a397b70e645a533b81242d3369660496b6863 Mon Sep 17 00:00:00 2001
From: Gabriel Studer <gabriel.studer@unibas.ch>
Date: Wed, 18 Jan 2023 08:25:34 +0100
Subject: [PATCH] integrate OST DockQ implementation in Scorer object

---
 modules/mol/alg/pymod/scoring.py | 72 ++++++++------------------------
 1 file changed, 17 insertions(+), 55 deletions(-)

diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py
index 359aa0e67..b09c0d9df 100644
--- a/modules/mol/alg/pymod/scoring.py
+++ b/modules/mol/alg/pymod/scoring.py
@@ -9,10 +9,10 @@ from ost.mol.alg import lddt
 from ost.mol.alg import qsscore
 from ost.mol.alg import chain_mapping
 from ost.mol.alg import stereochemistry
+from ost.mol.alg import dockq
 from ost.mol.alg.lddt import lDDTScorer
 from ost.mol.alg.qsscore import QSScorer
 from ost.mol.alg import Molck, MolckSettings
-from ost.bindings import dockq
 from ost.bindings import cadscore
 import numpy as np
 
@@ -114,11 +114,6 @@ class Scorer:
                                        to optimize for QS-score. Everything
                                        above is treated with a heuristic.
     :type naive_chain_mapping_thresh: :class:`int` 
-    :param dockq_exec: Explicit path to DockQ.py script from DockQ installation
-                       from https://github.com/bjornwallner/DockQ. If not given,
-                       DockQ.py must be in PATH if any of the DockQ related
-                       attributes is requested.
-    :type dockq_exec: :class:`str`
     :param cad_score_exec: Explicit path to voronota-cadscore executable from
                            voronota installation from 
                            https://github.com/kliment-olechnovic/voronota. If
@@ -128,7 +123,7 @@ class Scorer:
     """
     def __init__(self, model, target, resnum_alignments=False,
                  molck_settings = None, naive_chain_mapping_thresh=12,
-                 dockq_exec = None, cad_score_exec = None):
+                 cad_score_exec = None):
 
         if isinstance(model, mol.EntityView):
             self._model = mol.CreateEntityFromView(model, False)
@@ -183,7 +178,6 @@ class Scorer:
         Molck(self._target, conop.GetDefaultLib(), molck_settings)
         self.resnum_alignments = resnum_alignments
         self.naive_chain_mapping_thresh = naive_chain_mapping_thresh
-        self.dockq_exec = dockq_exec
         self.cad_score_exec = cad_score_exec
 
         # lazily evaluated attributes
@@ -868,12 +862,6 @@ class Scorer:
                                "that are consistent between target and model "
                                "chains, i.e. only work if resnum_alignments "
                                "is True at Scorer construction.")
-        try:
-            dockq_exec = settings.Locate("DockQ.py",
-                                         explicit_file_name=self.dockq_exec)
-        except Exception as e:
-            raise RuntimeError("DockQ.py must be in PATH for DockQ "
-                               "scoring") from e
 
         flat_mapping = self.mapping.GetFlatMapping()
         # list of [trg_ch1, trg_ch2, mdl_ch1, mdl_ch2]
@@ -896,38 +884,22 @@ class Scorer:
             if trg_ch1 in flat_mapping and trg_ch2 in flat_mapping:
                 mdl_ch1 = flat_mapping[trg_ch1]
                 mdl_ch2 = flat_mapping[trg_ch2]
-                try:
-                    res = dockq.DockQ(dockq_exec, self.model, self.target,
-                                      mdl_ch1, mdl_ch2, trg_ch1, trg_ch2)
-                except Exception as e:
-                    if "AssertionError: length of native is zero" in str(e):
-                        # happens if target interface has no native contacts
-                        continue
-                    else:
-                        raise
-
-                if res.native_contacts > 0:
+                res = dockq.DockQ(self.model, self.target, mdl_ch1, mdl_ch2,
+                                  trg_ch1, trg_ch2)
+                if res["nnat"] > 0:
                     self._dockq_interfaces.append((trg_ch1, trg_ch2,
                                                    mdl_ch1, mdl_ch2))
-                    self._dockq_native_contacts.append(res.native_contacts)
-                    self._dockq_scores.append(res.DockQ)
+                    self._dockq_native_contacts.append(res["nnat"])
+                    self._dockq_scores.append(res["DockQ"])
             else:
                 # interface which is not covered by mdl... let's run DockQ with
                 # trg as trg/mdl in order to get the native contacts out
-                try:
-                    res = dockq.DockQ(dockq_exec, self.target, self.target,
-                                      trg_ch1, trg_ch2, trg_ch1, trg_ch2)
-                    counts = res.native_contacts
-                    if counts > 0:
-                        self._dockq_nonmapped_interfaces.append((trg_ch1,
-                                                                 trg_ch2))
-                        self._dockq_nonmapped_interfaces_counts.append(counts)
-                except Exception as e:
-                    if "AssertionError: length of native is zero" in str(e):
-                        # happens if target interface has no native contacts
-                        continue
-                    else:
-                        raise
+                res = dockq.DockQ(self.target, self.target,
+                                  trg_ch1, trg_ch2, trg_ch1, trg_ch2)
+                if res["nnat"] > 0:
+                    self._dockq_nonmapped_interfaces.append((trg_ch1,
+                                                             trg_ch2))
+                    self._dockq_nonmapped_interfaces_counts.append(res["nnat"])
 
         # there are 4 types of combined scores
         # - simple average
@@ -1273,22 +1245,12 @@ class Scorer:
                                "that are consistent between target and model "
                                "chains, i.e. only work if resnum_alignments "
                                "is True at Scorer construction.")
-        try:
-            dockq_exec = settings.Locate("DockQ.py",
-                                         explicit_file_name=self.dockq_exec)
-        except Exception as e:
-            raise RuntimeError("DockQ.py must be in PATH for DockQ "
-                               "scoring") from e
         m = self._qs_ent_from_patches(mdl_patch_one, mdl_patch_two)
         t = self._qs_ent_from_patches(trg_patch_one, trg_patch_two)
-        try:
-            dockq_result = dockq.DockQ(dockq_exec, t, m, "A", "B", "A", "B")
-        except Exception as e:
-            if "AssertionError: length of native is zero" in str(e):
-                return 0.0
-            else:
-                raise
-        return dockq_result.DockQ
+        dockq_result = dockq.DockQ(t, m, "A", "B", "A", "B")
+        if dockq_result["nnat"] > 0:
+            return 0.0
+        return dockq_result["DockQ"]
 
     def _qs_ent_from_patches(self, patch_one, patch_two):
         """ Constructs Entity with two chains named "A" and "B""
-- 
GitLab