Package Biskit :: Package Dock :: Module ContactSlave
[hide private]
[frames] | no frames]

Source Code for Module Biskit.Dock.ContactSlave

  1  ## Automatically adapted for numpy.oldnumeric Mar 26, 2007 by alter_code1.py 
  2   
  3  ## 
  4  ## Biskit, a toolkit for the manipulation of macromolecular structures 
  5  ## Copyright (C) 2004-2006 Raik Gruenberg & Johan Leckner 
  6  ## 
  7  ## This program is free software; you can redistribute it and/or 
  8  ## modify it under the terms of the GNU General Public License as 
  9  ## published by the Free Software Foundation; either version 2 of the 
 10  ## License, or any later version. 
 11  ## 
 12  ## This program is distributed in the hope that it will be useful, 
 13  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 15  ## General Public License for more details. 
 16  ## 
 17  ## You find a copy of the GNU General Public License in the file 
 18  ## license.txt along with this program; if not, write to the Free 
 19  ## Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 20  ## 
 21  ## 
 22  ## last $Date: 2007/03/26 18:40:42 $ 
 23  ## last $Author: graik $ 
 24  ## $Revision: 2.11 $ 
 25   
 26  """ 
 27  Calculate contact matrix and some scores for complexes. 
 28  """ 
 29   
 30  from Biskit.PVM import JobSlave 
 31  import Biskit.tools as T 
 32  from Biskit import mathUtils as MU 
 33  from Biskit.LogFile import StdLog, LogFile 
 34  import numpy.oldnumeric as N 
 35  from Complex import Complex 
 36  import os.path 
 37  import time 
 38   
 39   
40 -class ContactSlave(JobSlave):
41 """ 42 Calculate contact matrix and some scores for complexes. 43 ContactMaster creates several instances of this class, each on one node. 44 """ 45
46 - def version( self ):
47 """ 48 Version of Dock.Complex 49 50 @return: version of class 51 @rtype: str 52 """ 53 return 'ContactSlave $Revision: 2.11 $'
54 55
56 - def initialize(self, params):
57 """ 58 Copy the parameters that ContactMaster is passing in as dict into 59 fields of this class. 60 61 @param params: defined in L{ContactMaster} 62 @type params: dict 63 """ 64 self.ferror = params['ferror'] 65 self.log = StdLog() 66 if params['log']: ## log to same file as master 67 self.log = LogFile( self.flog ) 68 self.verbose = params['verbose'] 69 70 ## reference complex data 71 self.c_ref_res_4_5 = self.c_ref_atom_4_5 = None 72 self.c_ref_atom_10 = None 73 self.mask_rec = self.mask_lig = None 74 self.mask_interface = self.mask_interface_bb = None 75 self.ref_interface_model = self.ref_interface_model_bb = None 76 77 ## reduced models and reduced reference contacts 78 self.reduced_recs = self.reduced_ligs = None 79 self.c_ref_ratom_10 = None 80 81 ## reference residue / atom contact matrices 82 if params.get( 'c_ref_res_4_5', None): 83 self.c_ref_res_4_5= MU.unpackBinaryMatrix(params['c_ref_res_4_5']) 84 85 self.c_ref_atom_4_5 = MU.unpackBinaryMatrix( 86 params['c_ref_atom_4_5']) 87 self.c_ref_atom_10 = MU.unpackBinaryMatrix( 88 params['c_ref_atom_10']) 89 90 ## atom masks for casting 91 if params.get('mask_rec', None): 92 self.mask_rec = MU.unpackBinaryMatrix( params['mask_rec'] ) 93 self.mask_lig = MU.unpackBinaryMatrix( params['mask_lig'] ) 94 95 ## for interface rms calculation 96 if params.get('mask_interface', None): 97 self.mask_interface = MU.unpackBinaryMatrix( 98 params['mask_interface']) 99 self.mask_interface_bb = MU.unpackBinaryMatrix( 100 params['mask_interface_bb'] ) 101 102 self.ref_interface_model = params['ref_interface_model'] 103 self.ref_interface_model_bb = params['ref_interface_model_bb'] 104 105 ## reduced rec and lig models indexed by source 106 self.reduced_recs = params['reduced_recs'] 107 self.reduced_ligs = params['reduced_ligs'] 108 109 if params.get( 'c_ref_ratom_10', None): 110 self.c_ref_ratom_10= MU.unpackBinaryMatrix( 111 params['c_ref_ratom_10']) 112 113 ## only calculate certain values 114 self.force = params.get('force', [] )
115 116
117 - def reportError(self, msg, soln ):
118 """ 119 Report any errors to log 120 121 @param msg: error message 122 @type msg: str 123 @param soln: solution number for complex giving the error 124 @type soln: int 125 """ 126 try: 127 s = '%s on %s, soln %i\n' % (msg, os.uname()[1], soln) 128 s += '\t' + T.lastError() + '\n' 129 s += 'TraceBack: \n' + T.lastErrorTrace() + '\n' 130 f = open( self.ferror, 'a' ) 131 f.write( s ) 132 f.close() 133 except: 134 f = open('ErrorReportError_ContactSlave','a') 135 f.write('') 136 f.close()
137 138
139 - def __containsAny( self, lst_or_dic, *items ):
140 """ 141 Check if dictionary or list contains items. 142 143 @param lst_or_dic: lokk for items in this 144 @type lst_or_dic: list OR dict 145 @param items: items to look for 146 @type items: any 147 148 @return: result of test 149 @rtype: 1|0 150 """ 151 for i in items: 152 if i in lst_or_dic: 153 return 1 154 return 0
155 156
157 - def requested( self, c, *keys ):
158 """ 159 Determine the keys in an info dictionary of a complex that 160 need to be calculated or updated 161 162 @param c: Complex 163 @type c: Complex 164 @param keys: key or keys for c.infos dict 165 @type keys: str OR [str] 166 167 @return: 1 if the given value should be calculated for the 168 given complex 169 @rtype: 1|0 170 """ 171 ## force update 172 if self.force and self.__containsAny( self.force, *keys): 173 return 1 174 175 ## fill empty or nonexisting fields 176 for k in keys: 177 if not self.force and c.get(k, None) is None: 178 return 1 179 180 return 0
181 182
183 - def pickleError( self, o ):
184 """ 185 Pickle object to disc. 186 187 @param o: object to pickle 188 @type o: any 189 """ 190 try: 191 fname = self.ferror + '_dat' 192 if not os.path.exists( fname ): 193 T.Dump( o, fname ) 194 except Exception, why: 195 f = open('ErrorReportError_ContactSlave','a') 196 f.write('Could not pickle error infos\n') 197 f.write( str( why ) ) 198 f.close()
199 200
201 - def calcContacts( self, soln, c ):
202 """ 203 Calculate contact matrices and fraction of native contacts, residue- 204 and atom-based, with different distance cutoffs. 205 206 @param soln: solution number 207 @type soln: int 208 @param c: Complex 209 @type c: Complex 210 """ 211 try: 212 if self.requested(c, 'fnac_4.5') and self.c_ref_atom_4_5 != None: 213 ## cache pairwise atom distances for following calculations 214 contacts = c.atomContacts( 4.5, self.mask_rec, self.mask_lig, 215 cache=1, map_back=0 ) 216 ref = N.ravel( self.c_ref_atom_4_5 ) 217 218 c['fnac_4.5'] = N.sum( N.ravel(contacts) * ref )\ 219 / float( N.sum(ref)) 220 221 if self.requested(c, 'fnac_10') and self.c_ref_atom_10 != None: 222 223 contacts = c.atomContacts( 10., self.mask_rec, self.mask_lig, 224 cache=1, map_back=0 ) 225 226 ref = N.ravel( self.c_ref_atom_10 ) 227 c['fnac_10'] = N.sum( N.ravel(contacts) * ref ) \ 228 / float( N.sum(ref)) 229 230 if self.requested(c, 'c_res_4.5') \ 231 or ( self.c_ref_res_4_5 != None \ 232 and (self.requested(c,'fnrc_4.5','fnSurf_rec'))): 233 234 res_cont = c.resContacts( 4.5, 235 cache=self.requested(c, 'c_res_4.5')) 236 237 if self.c_ref_res_4_5 != None \ 238 and self.requested(c, 'fnrc_4.5' ): 239 ref = N.ravel( self.c_ref_res_4_5 ) 240 c['fnrc_4.5'] = N.sum(N.ravel(res_cont)*ref) \ 241 /float(N.sum(ref)) 242 243 if self.c_ref_res_4_5 != None \ 244 and self.requested(c, 'fnSurf_rec'): 245 r, l = c.fractionNativeSurface(res_cont, 246 self.c_ref_res_4_5 ) 247 c['fnSurf_rec'] = r 248 c['fnSurf_lig'] = l 249 250 except: 251 m1 = m2 = s = 0 252 try: 253 m1, m2, s = c.get('model1',0), c.get('model2',0),\ 254 c.get('soln',0) 255 except: 256 pass 257 self.reportError('contact error (r %i : l %i, #%i)'%\ 258 (m1,m2,s), soln)
259 ## self.pickleError( {'com':c, 'mrec':self.mask_rec, 260 ## 'mlig':self.mask_lig } ) 261 262
263 - def calcReducedContacts( self, soln, c ):
264 """ 265 Get contact matrices and/or fnarc from reduced-atom models. 266 267 @param soln: solution number 268 @type soln: int 269 @param c: Complex 270 @type c: Complex 271 """ 272 if not (self.reduced_recs and self.reduced_ligs): 273 return 274 275 if not self.requested(c,'c_ratom_10','fnarc_10'): 276 return 277 278 try: 279 ## create Complex with same orientation but reduced coordinates 280 red_rec = self.reduced_recs[ c.rec_model.source ] 281 red_lig = self.reduced_ligs[ c.lig_model.source ] 282 red_com = Complex( red_rec, red_lig, c.ligandMatrix ) 283 284 contacts = red_com.atomContacts( 10.0, cache=1 ) 285 286 if self.requested(c, 'c_ratom_10'): 287 c['c_ratom_10'] = MU.packBinaryMatrix(contacts) 288 289 if self.c_ref_ratom_10 is not None: 290 ref = N.ravel( self.c_ref_ratom_10 ) 291 c['fnarc_10'] = N.sum( N.ravel(contacts) * ref )\ 292 / float( N.sum(ref)) 293 294 except: 295 self.reportError('reduced contacts error', soln)
296 ## self.pickleError({'com':c, 'red_recs':self.reduced_recs, 297 ## 'red_ligs':self.reduced_ligs}) 298 299
300 - def calcInterfaceRms( self, soln, c ):
301 """ 302 RMS between this and reference interface atoms after superposition. 303 - rms_if_bb, considers backbone of interface residues (10 A cutoff) 304 (same as used for CAPRI) 305 - rms_if, considers all atoms of more tightly defined interf. 306 residues (correlates better with fraction of native contacts) 307 308 @param soln: solution number 309 @type soln: int 310 @param c: Complex 311 @type c: Complex 312 """ 313 try: 314 if self.requested(c, 'rms_if_bb') and self.ref_interface_model_bb: 315 316 m = c.model().compress(self.mask_interface_bb) 317 c['rms_if_bb'] = self.ref_interface_model_bb.rms( m ) 318 319 if self.requested(c, 'rms_if') and self.ref_interface_model: 320 321 m = c.model().compress( self.mask_interface ) 322 c['rms_if'] = self.ref_interface_model.rms( m ) 323 324 except: 325 self.reportError('interface rms error', soln)
326 327 328 ## def calcProsa( self, soln, c ): 329 ## """ProsaII energy score""" 330 ## if self.requested( c, 'eProsa'): 331 ## try: 332 ## prosaE = c.prosaEnergy() 333 ## c['eProsa'] = prosaE 334 ## except: 335 ## self.reportError('Prosa Error', soln ) 336 ## c['eProsa'] = None 337 338
339 - def calcProsa( self, soln, c ):
340 """ 341 Prosa2003 energy score 342 343 @param soln: solution number 344 @type soln: int 345 @param c: Complex 346 @type c: Complex 347 """ 348 # import socket 349 if self.requested( c, 'eProsa'): 350 try: 351 prosaE = c.prosa2003Energy() 352 c['eProsa'] = prosaE 353 except: 354 c['eProsa'] = None 355 # c['eProsa'] = socket.gethostname() 356 self.reportError('Prosa Error', soln )
357 358
359 - def calcPairScore( self, soln, c ):
360 """ 361 calculate contact pair score 362 363 @param soln: solution number 364 @type soln: int 365 @param c: Complex 366 @type c: Complex 367 """ 368 if self.requested( c,'ePairScore'): 369 try: 370 pairScore = c.contPairScore(cutoff=6.0, log=self.log, 371 verbose=self.verbose ) 372 c['ePairScore'] = pairScore 373 except: 374 c['ePairScore'] = None 375 self.reportError('PairScore Error', soln )
376 377
378 - def calcConservation( self, soln, c, method ):
379 """ 380 calculate conservation score 381 382 @param soln: solution number 383 @type soln: int 384 @param c: Complex 385 @type c: Complex 386 @param method: scoring method to use see L{Complex.conservationScore} 387 @type method: str 388 """ 389 if self.requested( c, method): 390 try: 391 c[method] = c.conservationScore( method, log=self.log, 392 verbose=self.verbose ) 393 except: 394 self.reportError('Conservation score Error', soln)
395 396
397 - def calcFoldX( self, soln, c ):
398 """ 399 calculate fold-X binding energies 400 401 @param soln: solution number 402 @type soln: int 403 @param c: Complex 404 @type c: Complex 405 """ 406 if self.requested( c, 'foldX' ): 407 try: 408 c['foldX'] = c.foldXEnergy() 409 except: 410 self.reportError('FoldX Error', soln)
411 412
413 - def go(self, cmplxDic):
414 """ 415 Obtain contact matrix for all complexes. 416 417 @param cmplxDic: dictionary of complexes:: 418 {soln:Complex, soln:Complex, ...} 419 @type cmplxDic: {int:Complex} 420 421 @return: similar dictionary with Complex.info['soln'] as keys and 422 file names of matrices as value:: 423 { soln : fname, soln : fname ....} 424 @rtype: {int:str} 425 426 """ 427 result = {} 428 429 startTime = time.time() 430 431 for soln, c in cmplxDic.items(): 432 T.flushPrint( "%i," % soln ) 433 434 ## if not os.path.exists( T.absfile('~/debug.dic') ): 435 ## T.Dump( cmplxDic, T.absfile('~/debug.dic') ) 436 437 self.calcContacts( soln, c ) 438 439 self.calcInterfaceRms( soln, c ) 440 441 self.calcReducedContacts( soln, c ) 442 443 ## TODO: Prosa will not run when called via conatacSlave, runs as it should when 444 ## called as c.prosa2003Energy() in the interpreter. What's wroong here? 445 ## For mow the Prosa calculation is skipped. 446 ## 447 self.calcProsa( soln, c ) 448 449 self.calcPairScore( soln, c ) ## uses res-contacts 450 451 self.calcFoldX( soln, c ) ##uses rec/lig.info['foldX'] if available 452 453 for method in ['cons_ent', 'cons_max', 'cons_abs']: 454 self.calcConservation( soln, c, method ) 455 456 c['__version_contacter'] = self.version() 457 458 result[ soln ] = c 459 460 c.slim() 461 462 ## if not os.path.exists(T.absfile('~/debug_afterslave.dic') ): 463 ## T.Dump( cmplxDic, T.absfile('~/debug_afterslave.dic') ) 464 465 466 print "\navg time for last %i complexes: %f s" %\ 467 ( len(cmplxDic), (time.time()-startTime)/len(cmplxDic)) 468 469 return result
470 471 472 import Biskit.test as BT 473
474 -class Test(BT.BiskitTest):
475 """Test ContactSlave locally without running the master. 476 477 This allows to test the master and slave without using 478 external nodes. The master still requires a running PVM 479 though. 480 """ 481 482 TAGS = [ BT.PVM ] 483
484 - def prepare(self):
485 import tempfile 486 self.cl_out = tempfile.mktemp('_test.cl')
487
488 - def test_ContactSlave(self):
489 """Dock.ContactSlave test (local)""" 490 import os 491 from Biskit.Dock.ContactMaster import ContactMaster 492 493 ## load complex list (docking result) and reference complex 494 lst = T.Load( T.testRoot() + "/dock/hex/complexes.cl") 495 lst = lst[:3] 496 refcom = T.Load( T.testRoot() + "/com/ref.complex") 497 498 ## let ContactMaster prepare everything but don't run it 499 self.master = ContactMaster( lst, verbose = self.local, 500 log=self.log, 501 refComplex = refcom, 502 outFile = self.cl_out ) 503 504 jobs = self.master.data 505 506 self.slave = ContactSlave() 507 self.slave.initialize( self.master.getInitParameters(1) ) 508 509 if self.local or self.VERBOSITY > 2: 510 self.log.writeln("Currently available info records (from hex):") 511 self.log.writeln( repr(jobs[0].info.keys()) ) 512 self.log.writeln( "Calculating all scores for %i complexes..." \ 513 % len(jobs) ) 514 515 self.result = self.slave.go( jobs ) 516 517 if self.local or self.VERBOSITY > 2: 518 self.log.writeln("info records after contacting: ") 519 self.log.writeln( repr(jobs[0].info.keys()) ) 520 if self.local: 521 print "new scores are available in 'result[0-2].info'" 522 523 ## verify fraction of native atom contacts for third complex 524 self.assertAlmostEqual( self.result[2]['fnac_10'], 525 0.11533600168527491, 7 )
526
527 - def cleanUp(self):
528 T.tryRemove( self.cl_out )
529 530 ## ## PROFILING: 531 ## in slave window: 532 ## slave.stop() 533 ## import profile 534 ## profile.run( 'slave._go( slave.dic )', 'report.out' ) 535 536 ## ## Analyzing 537 ## import pstats 538 ## p = pstats.Stats('report.out') 539 540 ## ## long steps and methods calling them 541 ## p.sort_stats('cumulative').print_stats(20) 542 ## p.print_callers(0.1) 543 544 ## ## time consuming methods 545 ## p.sort_stats('time').print_stats(10) 546 547 548 if __name__ == '__main__': 549 550 ## BT.localTest() 551 552 import os, sys 553 554 if len(sys.argv) == 2: 555 556 niceness = int(sys.argv[1]) 557 os.nice(niceness) 558 559 slave = ContactSlave() 560 slave.start() 561