OpenStructure
dockq.py
Go to the documentation of this file.
1 import sys
2 import os
3 import subprocess
4 import tempfile
5 import shutil
6 
7 from ost import io
8 from ost import mol
9 
10 def _Setup(mdl, ref, mdl_ch1, mdl_ch2, ref_ch1, ref_ch2):
11  """ Performs parameter checks and dumps files for DockQ
12 
13  In case of dimeric interfaces the respective chains are selected from
14  mdl/trg, renamed to A/B and dumped to disk.
15 
16  In case of interfaces with more chains involved, we simply select the
17  specified chains and do no renaming before dumping to disk.
18  """
19  if isinstance(mdl_ch1, str):
20  mdl_ch1 = [mdl_ch1]
21  if isinstance(mdl_ch2, str):
22  mdl_ch2 = [mdl_ch2]
23  if isinstance(ref_ch1, str):
24  ref_ch1 = [ref_ch1]
25  if isinstance(ref_ch2, str):
26  ref_ch2 = [ref_ch2]
27 
28  if len(mdl_ch1) == 0:
29  raise RuntimeError("mdl_ch1 is empty")
30  if len(mdl_ch2) == 0:
31  raise RuntimeError("mdl_ch2 is empty")
32 
33  if len(mdl_ch1) != len(ref_ch1):
34  raise RuntimeError("mdl_ch1/ref_ch1 inconsistent in size")
35  if len(mdl_ch2) != len(ref_ch2):
36  raise RuntimeError("mdl_ch2/ref_ch2 inconsistent in size")
37 
38  for cname in mdl_ch1:
39  ch = mdl.FindChain(cname)
40  if not ch.IsValid():
41  raise RuntimeError(f"Chain {cname} specified in mdl_ch1 not "
42  f"present in mdl")
43 
44  for cname in mdl_ch2:
45  ch = mdl.FindChain(cname)
46  if not ch.IsValid():
47  raise RuntimeError(f"Chain {cname} specified in mdl_ch2 not "
48  f"present in mdl")
49 
50  for cname in ref_ch1:
51  ch = ref.FindChain(cname)
52  if not ch.IsValid():
53  raise RuntimeError(f"Chain {cname} specified in ref_ch1 not "
54  f"present in ref")
55 
56  for cname in ref_ch2:
57  ch = ref.FindChain(cname)
58  if not ch.IsValid():
59  raise RuntimeError(f"Chain {cname} specified in ref_ch2 not "
60  f"present in ref")
61 
62  mdl_to_dump = mdl.CreateFullView()
63  ref_to_dump = ref.CreateFullView()
64 
65  if len(mdl_ch1) == 1 and len(mdl_ch2) == 1:
66  # Dimer processing of mdl => Create new entity only containing
67  # the two specified chains and rename them to A, B
68  mdl_to_dump = mol.CreateEntityFromView(mdl_to_dump, True)
69  tmp = mol.CreateEntity()
70  ed = tmp.EditXCS()
71  ch1 = mdl_to_dump.FindChain(mdl_ch1[0])
72  ed.InsertChain("A", ch1, deep=True)
73  ch2 = mdl_to_dump.FindChain(mdl_ch2[0])
74  ed.InsertChain("B", ch2, deep=True)
75  mdl_ch1 = ["A"]
76  mdl_ch2 = ["B"]
77  mdl_to_dump = tmp
78 
79  # Same for ref
80  ref_to_dump = mol.CreateEntityFromView(ref_to_dump, True)
81  tmp = mol.CreateEntity()
82  ed = tmp.EditXCS()
83  ch1 = ref_to_dump.FindChain(ref_ch1[0])
84  ed.InsertChain("A", ch1, deep=True)
85  ch2 = ref_to_dump.FindChain(ref_ch2[0])
86  ed.InsertChain("B", ch2, deep=True)
87  ref_ch1 = ["A"]
88  ref_ch2 = ["B"]
89  ref_to_dump = tmp
90  else:
91  # Interface with more chains...
92  raise NotImplementedError("DockQ computations beyond two interacting "
93  "chains has not been properly tested...")
94 
95  # first write structures to string, only create a tmpdir and the actual
96  # files if this succeeds
97  mdl_str = io.EntityToPDBStr(mdl_to_dump)
98  ref_str = io.EntityToPDBStr(ref_to_dump)
99 
100  tmp_dir = tempfile.mkdtemp()
101  with open(os.path.join(tmp_dir, "mdl.pdb"), 'w') as fh:
102  fh.write(mdl_str)
103  with open(os.path.join(tmp_dir, "ref.pdb"), 'w') as fh:
104  fh.write(ref_str)
105 
106  return (tmp_dir, mdl_ch1, mdl_ch2, ref_ch1, ref_ch2)
107 
109  """ DockQ result object
110  """
111  def __init__(self, Fnat, Fnonnat, native_contacts, model_contacts, iRMS,
112  LRMS, DockQ):
113  self._Fnat_Fnat = Fnat
114  self._Fnonnat_Fnonnat = Fnonnat
115  self._native_contacts_native_contacts = native_contacts
116  self._model_contacts_model_contacts = model_contacts
117  self._iRMS_iRMS = iRMS
118  self._LRMS_LRMS = LRMS
119  self._DockQ_DockQ = DockQ
120 
121  @property
122  def Fnat(self):
123  """ DockQ - Fnat output
124 
125  :type: :class:`float`
126  """
127  return self._Fnat_Fnat
128 
129  @property
130  def Fnonnat(self):
131  """ DockQ - Fnonnat output
132 
133  :type: :class:`float`
134  """
135  return self._Fnonnat_Fnonnat
136 
137  @property
138  def native_contacts(self):
139  """ DockQ - number native contacts
140 
141  :type: :class:`int`
142  """
143  return self._native_contacts_native_contacts
144 
145  @property
146  def model_contacts(self):
147  """ DockQ - number model contacts
148 
149  :type: :class:`int`
150  """
151  return self._model_contacts_model_contacts
152 
153  @property
154  def iRMS(self):
155  """ DockQ - iRMS output
156 
157  :type: :class:`float`
158  """
159  return self._iRMS_iRMS
160 
161  @property
162  def LRMS(self):
163  """ DockQ - LMRS output
164 
165  :type: :class:`float`
166  """
167  return self._LRMS_LRMS
168 
169  @property
170  def DockQ(self):
171  """ DockQ - DockQ output
172 
173  :type: :class:`float`
174  """
175  return self._DockQ_DockQ
176 
177  def JSONSummary(self):
178  """ Returns JSON serializable summary
179  """
180  return {"Fnat": self.FnatFnat,
181  "Fnonnat": self.FnonnatFnonnat,
182  "native_contacts": self.native_contactsnative_contacts,
183  "model_contacts": self.model_contactsmodel_contacts,
184  "iRMS": self.iRMSiRMS,
185  "LRMS": self.LRMSLRMS,
186  "DockQ": self.DockQDockQ}
187 
188  @staticmethod
189  def FromDockQOutput(output):
190  """ Static constructor from raw DockQ output
191 
192  :param output: Raw output from DockQ executable
193  :type output: :class:`str`
194  :returns: Object of type :class:`DockQResult`
195  """
196  Fnat = None
197  Fnonnat = None
198  native_contacts = None
199  model_contacts = None
200  iRMS = None
201  LRMS = None
202  DockQ = None
203 
204  for line in output.splitlines():
205  if line.startswith('*'):
206  continue
207  if line.startswith("Fnat"):
208  Fnat = float(line.split()[1])
209  native_contacts = int(line.split()[5])
210  elif line.startswith("Fnonnat"):
211  Fnonnat = float(line.split()[1])
212  model_contacts = int(line.split()[5])
213  elif line.startswith("iRMS"):
214  iRMS = float(line.split()[1])
215  elif line.startswith("LRMS"):
216  LRMS = float(line.split()[1])
217  elif line.startswith("DockQ"):
218  DockQ = float(line.split()[1])
219 
220  return DockQResult(Fnat, Fnonnat, native_contacts, model_contacts,
221  iRMS, LRMS, DockQ)
222 
223 
224 def DockQ(dockq_exec, mdl, ref, mdl_ch1, mdl_ch2, ref_ch1,
225  ref_ch2):
226  """ Computes DockQ for specified interface
227 
228  DockQ is available from https://github.com/bjornwallner/DockQ -
229  For this binding to work, DockQ must be properly installed and its
230  dependencies must be available (numpy, Biopython).
231 
232  :param dockq_exec: Path to DockQ.py script from DockQ repository
233  :type dockq_exec: :class:`str`
234  :param mdl: Model structure
235  :type mdl: :class:`ost.mol.EntityView`/:class:`ost.mol.EntityHandle`
236  :param ref: Reference structure, i.e. native structure
237  :type ref: :class:`ost.mol.EntityView`/:class:`ost.mol.EntityHandle`
238  :param mdl_ch1: Specifies chain(s) in model constituting first part of
239  interface
240  :type mdl_ch1: :class:`str`/:class:`list` of :class:`str`
241  :param mdl_ch2: Specifies chain(s) in model constituting second part of
242  interface
243  :type mdl_ch2: :class:`str`/:class:`list` of :class:`str`
244  :param ref_ch1: ref equivalent of mdl_ch1
245  :type ref_ch1: :class:`str`/:class:`list` of :class:`str`
246  :param ref_ch2: ref equivalent of mdl_ch2
247  :type ref_ch2: :class:`str`/:class:`list` of :class:`str`
248  :returns: Result object of type :class:`DockQResult`
249  """
250  if not os.path.exists(dockq_exec):
251  raise RuntimeError(f"DockQ executable ({dockq_exec}) does not exist")
252 
253  tmp_dir, mdl_ch1, mdl_ch2, ref_ch1, ref_ch2 = \
254  _Setup(mdl, ref, mdl_ch1, mdl_ch2, ref_ch1, ref_ch2)
255 
256  cmd = [sys.executable, dockq_exec, os.path.join(tmp_dir, "mdl.pdb"),
257  os.path.join(tmp_dir, "ref.pdb")]
258 
259  # add mdl/ref chains
260  cmd.append("-model_chain1")
261  cmd += mdl_ch1
262  cmd.append("-model_chain2")
263  cmd += mdl_ch2
264  cmd.append("-native_chain1")
265  cmd += ref_ch1
266  cmd.append("-native_chain2")
267  cmd += ref_ch2
268 
269  proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
270 
271  shutil.rmtree(tmp_dir) # cleanup, no matter if DockQ executed successfully
272 
273  if proc.returncode != 0:
274  raise RuntimeError("DockQ run failed - returncode: " + \
275  str(proc.returncode) + ", stderr: " + \
276  proc.stderr.decode() + ", stdout: " + \
277  proc.stdout.decode())
278 
279  if proc.stderr.decode() != "":
280  raise RuntimeError("DockQ run failed - stderr: " + \
281  proc.stderr.decode() + ", stdout: " + \
282  proc.stdout.decode())
283 
284  return DockQResult.FromDockQOutput(proc.stdout.decode())
def __init__(self, Fnat, Fnonnat, native_contacts, model_contacts, iRMS, LRMS, DockQ)
Definition: dockq.py:112
def DockQ(dockq_exec, mdl, ref, mdl_ch1, mdl_ch2, ref_ch1, ref_ch2)
Definition: dockq.py:225