1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 """
28 Prepare and run a HEX docking.
29 """
30
31 from Biskit import PDBModel, PDBDope, molUtils
32 import Biskit.tools as t
33
34 from HexParser import HexParser
35 from ComplexList import ComplexList
36 import hexTools
37 import settings
38
39 import re
40 import os.path
41 from time import localtime, sleep
42 from threading import Thread, RLock, Condition
43 import numpy.oldnumeric as N
44
46 pass
47
49 """
50 Prepare and run a hex docking for one or several models
51 of a receptor and ligand pair. Collect docking results
52 into a growing ComplexList.
53
54 The docking runs are started in seperate Threads. External objects
55 can register a method with call_when_done() that is called whenever
56 a docking has finished. The waitForLastHex() method can be called
57 to wait until all currently running dockings are finished.
58
59 The models in the 2 given dictionaries might get different chainIds but
60 Docker doesn't save them.
61
62 @todo: implement that as JobMaster / JobSlave instead, so that also the
63 Hex-file parsing is distributed.
64 """
65
66 - def __init__( self, recDic, ligDic, recPdb=None, ligPdb=None,
67 comPdb=None, out='hex_%s', soln=512,
68 recChainId=None, ligChainId=None, macDock=None,
69 bin=settings.hex_bin, verbose=1 ):
70 """
71 @param recDic: receptor dictionary
72 @type recDic: dict
73 @param ligDic: ligand dictionary
74 @type ligDic: dict
75 @param recPdb: hex-formatted PDB with rec models
76 @type recPdb: str
77 @param ligPdb: hex-formatted PDB with lig models
78 @type ligPdb: str
79 @param comPdb: hex-formatted PDB with com models
80 @type comPdb: str
81 @param soln: number of solutions to keep from HEX (default 512)
82 @type soln: int
83 @param recChainId: force chain IDs only for HEX
84 @type recChainId: [str]
85 @param ligChainId: force chain IDs only for HEX
86 @type ligChainId: [str]
87 @param out: out folder name, will be formatted against date
88 @type out: str
89 @param macDock: force macro docking ON or OFF (default: size decides)
90 @type macDock: 1|0
91 @param bin: path to HEX binary
92 @type bin: str
93 @param verbose: verbosity level (default: 1)
94 @type verbose: 1|0
95 """
96 self.lock = RLock()
97 self.lockMsg = Condition( self.lock )
98
99 t.ensure( macDock, int, [None] )
100 t.ensure( recDic, dict, )
101 t.ensure( ligDic, dict, )
102
103 self.verbose = verbose
104
105 self.recDic = recDic
106 self.ligDic = ligDic
107
108
109 self.recChainId = recChainId
110 self.ligChainId = ligChainId
111
112
113 self.out = self.prepareOutFolder( out )
114
115 self.comPdb = None
116 if comPdb:
117 self.comPdb = t.absfile( comPdb )
118
119
120 self.recHexPdbs = {}
121 self.ligHexPdbs = {}
122
123
124 self.runDic = {}
125
126 if recPdb:
127
128 for k in self.recDic:
129 self.recHexPdbs[ k ] = t.absfile( recPdb )
130 else:
131
132 self.recHexPdbs = self.prepareHexPdbs( self.recDic,
133 self.recChainId, 'rec')
134
135 if ligPdb:
136 for k in self.ligDic:
137 self.ligHexPdbs[ k ] = t.absfile( recPdb )
138 else:
139 self.ligHexPdbs = self.prepareHexPdbs( self.ligDic,
140 self.ligChainId, 'lig')
141
142 self.bin = bin
143
144 self.call_when_done = None
145 self.call_when_failed=None
146
147
148 self.macroDock = macDock
149
150 self.running = []
151 self.hexDone = 0
152
153 self.result = ComplexList()
154
155 self.soln = soln
156
157
159 """
160 Setup an output folder
161
162 @param fout: outfile name
163 @type fout: str
164
165 @return: out path
166 @rtype: str
167 """
168
169 try:
170 datestr = "%02i%02i" % (localtime()[1], localtime()[2])
171 fout = fout % ( datestr )
172 if not os.path.exists( t.absfile( fout ) ):
173 os.mkdir( t.absfile( fout ) )
174
175
176 except:
177 if not os.path.exists( t.absfile( fout ) ):
178 os.mkdir( t.absfile( fout ) )
179
180
181 if not os.path.exists( t.absfile( fout ) + '/rec' ):
182 os.mkdir( t.absfile( fout ) + '/rec' )
183
184 if not os.path.exists( t.absfile( fout ) + '/lig' ):
185 os.mkdir( t.absfile( fout ) + '/lig' )
186
187 return t.absfile( fout )
188
189
191 """
192 set chaiID for Hex pdb files
193
194 @param m: model
195 @type m: PDBModel
196 @param ids: chain id, len(ids) == m.lenChains
197 @type ids: [str]
198
199 @return: m is changed directly
200 @rtype: PDBModel
201 """
202 if ids:
203 ids = t.toList( ids )
204 cMap = m.chainMap()
205
206 for chain in range( m.lenChains() ):
207 idx = N.nonzero( cMap == chain )
208 for i in idx:
209 m.aProfiles['chain_id'][i] = ids[chain]
210
211
213 """
214 create HEX-formatted PDB for each model
215
216 @param modelDic: model dictionary to be prepared for HEX
217 @type modelDic: dict
218 @param idList: force chain IDs for HEX pdb files
219 @type idList: [str]
220 @param subDir: 'rec' or 'lig'
221 @type subDir: str
222
223 @return: model dictionary, { modelNumber : file_name_created, .. }
224 @rtype: dict
225 """
226 result = {}
227 for k in modelDic:
228
229 m = modelDic[k].clone()
230
231 fhex = self.out + '/%s/%s_%03d_hex.pdb' % (subDir, m.pdbCode, k)
232 if os.path.exists( fhex ) and self.verbose:
233 print "using old ", os.path.split( fhex )[1]
234
235 else:
236 m.remove( m.maskH() )
237 m = molUtils.sortAtomsOfModel(m)
238 self.__setChainID( m, idList )
239
240 hexTools.createHexPdb_single( m, fhex )
241 if self.verbose: print "created ", fhex
242
243 result[ k ] = fhex
244
245 return result
246
247
249 """
250 Create a HEX macro file for docking a rec lig pair.
251
252 @param nRec: model number rec in model dictionaries
253 @type nRec: int
254 @param nLig: model number lig in model dictionaries
255 @type nLig: int
256
257 @return: macro file name, future out file name
258 @rtype: (str, str)
259
260 @raise DockerError: if macro dock option is different from previous
261 call
262 """
263
264 recPdb, ligPdb = self.recHexPdbs[nRec], self.ligHexPdbs[nLig]
265
266
267 rec, lig = self.recDic[ nRec ], self.ligDic[ nLig ]
268
269 fout_base = self.out+'/%s_%i-%s_%i' % (rec.pdbCode, nRec,
270 lig.pdbCode, nLig)
271
272 if os.path.exists( t.absfile( fout_base + '_hex.mac' ) ) and self.verbose:
273 fmac = t.absfile( fout_base + '_hex.mac' )
274 fout = t.absfile( fout_base + '_hex.out' )
275 macro = self.macroDock
276 print "Dock setup: using old ", os.path.split( fmac )[1]
277 else:
278 silent=1
279 if self.verbose: silent=0
280
281 fmac, fout, macro = hexTools.createHexInp(recPdb,rec, ligPdb,lig,
282 self.comPdb, outFile=fout_base,
283 macDock=self.macroDock, sol=self.soln,
284 silent=silent)
285
286 if self.macroDock == None:
287 self.macroDock = macro
288 else:
289 if self.macroDock != macro:
290 raise DockerError('MacroDock setting changed to %i: %i : %i' %\
291 ( macro, nRec, nLig) )
292
293
294 self.runDic[ fmac ] = ( nRec, nLig, fout )
295
296 return fmac, fout
297
298
299 - def runHex( self, finp, log=0, ncpu=2, nice=0, host=os.uname()[1] ):
300 """
301 @param finp: hex macro file
302 @type finp: str
303 @param log: write log file
304 @type log: 1|0
305 @param ncpu: number of cpus to use
306 @type ncpu: int
307 @param nice: nice value for HEX job (default: 0)
308 @type nice: int
309 @param host: host to run jon on
310 @type host: str
311 """
312 flog = finp + '.log'
313
314 cmd = "%s -ncpu %i -nice %i -noexec < %s > %s"
315 cmd = cmd % (self.bin, ncpu, nice, finp, flog )
316
317 cmd = "ssh %s %s" % (host, cmd)
318
319 runner = RunThread( cmd, self, finp=finp, host=host,
320 log=log, verbose=self.verbose )
321
322 self.lock.acquire()
323
324 self.lastCmd = cmd
325 self.running += [runner]
326
327 runner.start()
328
329 self.lock.release()
330
331
333 """
334 """
335 mrec = []
336 mlig = []
337 for c in lst:
338 if c.rec_model not in mrec:
339 mrec += [ c.rec_model ]
340 if c.lig_model not in mlig:
341 mlig += [ c.lig_model ]
342
343 return len( mrec ), len( mlig )
344
345
347 """
348 Do something after hex has finished. Notify all threads waiting
349 on self.lockMsg
350 """
351 self.lock.acquire()
352
353
354 self.result += runner.result
355
356 if self.call_when_done:
357 self.call_when_done( runner )
358
359 self.running.remove( runner )
360
361 self.hexDone += 1
362
363 self.lockMsg.notifyAll()
364 self.lock.release()
365
366
368 """
369 notify of failed hex run.
370 """
371 self.lock.acquire()
372
373 self.running.remove( runner )
374
375 if self.call_when_failed:
376 self.call_when_failed( runner )
377
378 self.lockMsg.notifyAll()
379 self.lock.release()
380
381
383 """
384 Return after the last hex thread has finished.
385 """
386 self.lock.acquire()
387
388 while len( self.running ) > 0:
389 self.lockMsg.wait()
390
391 self.lock.release()
392 return
393
394
396 """
397 funct( RunThread )
398 """
399 self.lock.acquire()
400 self.call_when_done = funct
401 self.lock.release()
402
404 """
405 funct( RunThread )
406 """
407 self.lock.acquire()
408 self.call_when_failed = funct
409 self.lock.release()
410
411
413 """
414 @todo: implement that as JobSlave instead
415 """
416
417 - def __init__( self, cmd, owner, finp=None, log=0,
418 host=None, verbose=1, **kw ):
419 """
420 @param cmd: command to execute
421 @type cmd: str
422 @param owner: Docker job to run to run
423 @type owner: Docker instance
424 @param finp: hex macro file (default: None)
425 @type finp: str
426 @param log: write log file (default: 0)
427 @type log: 1|0
428 @param host: host to run jon on (default: None, localhost)
429 @type host: str
430 @param kw: optional key/value pairs
431 @type kw: key=value
432 """
433 Thread.__init__( self, name=(host or 'local'), **kw )
434 self.cmd = cmd
435 self.owner = owner
436 self.finp = finp
437 self.log = log
438 self.host = host or os.uname()[1]
439 self.nRec, self.nLig, self.fout = self.owner.runDic[ self.finp ]
440 self.output = None
441 self.status = 0
442 self.result = None
443 self.verbose = verbose
444
445
447 """
448 Run HEX job.
449
450 @raise DockerError: if HEX exists with error
451 """
452 try:
453 if not os.path.exists( self.fout ):
454
455 if self.verbose: print "Executing: ", self.host, ' ', t.stripFilename(self.finp)
456
457 cmd_lst = self.cmd.split()
458
459 self.status = os.spawnvp(os.P_WAIT, cmd_lst[0], cmd_lst )
460
461 waited = 0
462 while waited < 25 and not os.path.exists( self.fout ):
463 sleep( 5 )
464 waited += 5
465
466 if self.status != 0:
467 raise DockerError, 'Hex returned exit status %i' % self.status
468
469
470 self.__hackHexOut( self.nRec, self.nLig, self.fout )
471
472 parser = HexParser(self.fout, self.owner.recDic,
473 self.owner.ligDic)
474
475
476 self.result = parser.parseHex()
477
478 self.done()
479
480 except:
481 self.failed()
482
483
485 """
486 HEX job done.
487 """
488 self.owner.doneHex( self )
489
490
492 """
493 If HEX job fails
494 """
495 print "FAILED: ", self.host, ' ', t.stripFilename(self.finp)
496 print "\tJob details:"
497 print "\tCommand: ", self.cmd
498 print "\tinput: ", self.finp
499 print "\tHex log: ", self.log
500 print "\tHex out: ", self.fout
501 print
502 print "\t", t.lastError()
503
504 self.owner.failedHex( self )
505
506
508 """
509 Replace current model numbers in HEX out file by given nRec and nLig.
510 Only to be used with one-against-one dockings because all
511 ReceptorModel and LigandModel entries are replaced by nRec, nLig.
512
513 @param nRec: receptor number
514 @type nRec: int
515 @param nLig: ligand number
516 @type nLig: int
517 @param fout: name of existing out file, is overwritten
518 @type fout: str
519 """
520 old_out = open( fout, 'r' )
521 contents = old_out.readlines()
522
523 oldRec = 'ReceptorModel: \d+$'
524 oldLig = 'LigandModel: \d+$'
525 correctRec = 'ReceptorModel: %i' % nRec
526 correctLig = 'LigandModel: %i' % nLig
527 contents = [re.sub(oldRec, correctRec, i ) for i in contents]
528 contents = [re.sub(oldLig, correctLig, i ) for i in contents]
529 old_out.close()
530
531 new_out = open( fout, 'w')
532 new_out.writelines(contents )
533 new_out.close()
534
535
536
537
538
539
540 import Biskit.test as BT
541
543 """Base class for short and long Test case"""
544
546 import tempfile
547 self.out_folder = tempfile.mktemp('_test_docker_%s')
548
551
553 """ """
554 import time, os
555 import os.path
556
557 ligDic = t.Load( t.testRoot() + '/multidock/lig/1A19_models.dic' )
558
559
560
561
562
563 rec_dir = t.testRoot() + '/multidock/rec'
564 com_dir = t.testRoot() + '/multidock/com'
565
566 if not os.path.exists( rec_dir ):
567
568 if os.path.lexists( rec_dir ):
569 os.unlink( rec_dir )
570 os.symlink( t.testRoot() + '/dock/rec', rec_dir )
571
572 if not os.path.exists( com_dir ):
573
574 if os.path.lexists( com_dir ):
575 os.unlink( com_dir )
576 os.symlink( t.testRoot() + '/dock/com', com_dir )
577
578 recDic = t.Load( t.testRoot() + '/multidock/rec/1A2P_model.dic' )
579
580 self.d = Docker( recDic, ligDic, out=self.out_folder,
581 verbose=self.local )
582
583
584 fmac1, fout = self.d.createHexInp( 1, 2 )
585 if run:
586 self.d.runHex( fmac1, log=1, ncpu=2 )
587
588 self.d.waitForLastHex()
589
590
591
592
593
594 if self.local: print "ALL jobs submitted."
595
596
598 """Test case running a complete hex docking (ca. 30 min/CPU)"""
599
600 TAGS = [ BT.EXE, BT.LONG ]
601
605
613
614 if __name__ == '__main__':
615
616 BT.localTest()
617