1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """
24 Run WhatIf, calculate and collect accessibility data
25 """
26
27 import Numeric as N
28 import string
29 import tempfile
30
31 from Biskit import Executor, TemplateError
32
33 import Biskit.tools as T
34
35
37 pass
38
40 """
41 WhatIf
42 ======
43 Run WhatIf accessibility calculation
44
45 Result
46 ------
47 A WhatIf log file containing the accessibilities (in square Angstrom)
48 per residue. The ASA is specified for the entire residue as well for
49 the backbone and side chain.
50
51 Reference
52 ---------
53 U{ http://swift.cmbi.kun.nl/whatif/}
54
55 @note: WhatIf is not free software, therefore the calculations performed
56 by this class can now also be performed by the freeware program
57 SurfaceRacer, see L{ Biskit.SurfaceRacer }.
58 """
59
60 whatif_script ="""SOUP \nINISOU
61
62 GETMOL \n%(f_pdb)s \nTEST
63
64 ACCESS \nINIACC
65 SETACC \nALL \n0 \n
66
67 DOLOG \n%(f_relativeASA)s \n0
68 VACACC \nN \nALL
69 NOLOG
70
71 DOLOG \n%(f_residueASA)s \n0
72 SHOACC \nALL \n0
73 NOLOG"""
74
75
77 """
78 @param model: PDBModel
79 @type model:
80
81 @param kw: additional key=value parameters for Executor:
82 @type kw: key=value pairs
83 ::
84 debug - 0|1, keep all temporary files (default: 0)
85 verbose - 0|1, print progress messages to log (log != STDOUT)
86 node - str, host for calculation (None->local) NOT TESTED
87 (default: None)
88 nice - int, nice level (default: 0)
89 log - Biskit.LogFile, program log (None->STOUT) (default: None)
90 """
91 Executor.__init__( self, 'whatif', template=self.whatif_script,
92 f_out='/dev/null', **kw )
93
94 self.f_pdb = tempfile.mktemp('_whatif.pdb')
95 self.f_relativeASA = tempfile.mktemp('_whatif_relative.log')
96 self.f_residueASA = tempfile.mktemp('_whatif_residue.log')
97
98 self.model = model.clone( deepcopy=1 )
99
100
102 """
103 Overrides Executor method.
104 """
105 self.__prepareModel( self.model, self.f_pdb )
106
107
109 """
110 Prepare a model that WhatIf likes.
111 - consecutive numbering of residues
112 - remove hydrogens (will be deleted anyway)
113
114 @param m: model
115 @type m: PDBModel
116 @param f_pdb_out: name of pdb file to write
117 @type f_pdb_out: str
118 """
119
120 m = m.compress( m.maskHeavy() )
121 m.renumberResidues()
122 m.writePdb( f_pdb_out, ter=0 )
123
124
141
142
144 """
145 Overrides Executor method
146 """
147 return self.error is None
148
149
156
157
159 """
160 Read solvent accessibility calculated with WHATIF and return array
161 of ASA-values. First column is the total ASA, second column the ASA
162 of the backbone, the third column is the ASA of the side-chain.
163
164 @return: array (3 x len(residues)) with ASA values
165 @rtype: array
166 """
167
168
169 lines = open(self.f_residueASA).readlines()[14:-27]
170 ASA_values = map(lambda line: string.split(line)[-3:], lines)
171 ASA_values = N.array(map(lambda row: map(string.atof, row),
172 ASA_values))
173
174 return ASA_values
175
176
178 """
179 Read the relative ASA data. Return dic indexed by resNumber
180 with atom and relative accessibility (%)
181
182 @return: relative atom ASA dictionary. e.g::
183 { 1: { 'N':34.6, 'CA':81.5 .. }, 2 : {.. }.... }
184 @rtype: dict of dict
185 """
186 AtomASA = {}
187
188 logFile = open( self.f_relativeASA )
189
190 while 1:
191 line = logFile.readline()
192
193
194 if line[:8] == 'Residue:':
195
196 residue = int( string.split(line)[1] )
197 AtomASA[residue] = {}
198
199
200 line = logFile.readline()
201 line = logFile.readline()
202
203 line = logFile.readline()
204 while line[:20] != ' ':
205
206 atom = string.split(line)[0]
207 value = float( string.split(line)[-1] )
208 AtomASA[ residue ][ atom ] = value
209
210 line = logFile.readline()
211
212 if line == '':
213 break
214
215 return AtomASA
216
217
218 - def __exposedResidues( self, ASA_values, sidechainCut=0.0,
219 backboneCut=0.0, totalCut=0.0 ):
220 """
221 Decide what is a surface exposed residue and what is not.
222 sidechainCut, backboneCut, totalCut - float, cutoff value
223 for what will be considered as a exposed residue. All
224 three values have to pass the test.
225
226 @param ASA_values: array with ASA values for side chains, backbone
227 and total calculated in L{__read_residueASA}.
228 @type ASA_values: array
229 @param sidechainCut: cutoff ASA value for considering the side chain
230 to consider thew residue being exposed
231 (default: 0.0)
232 @type sidechainCut: float
233 @param backboneCut: cutoffvalue for back bone ASA
234 @type backboneCut: float
235 @param totalCut: cutoff for total ASA
236 @type totalCut: float
237
238 @return: residue mask, where 0 = burried
239 @rtype: [1|0]
240 """
241 col_0 = N.greater( N.transpose(ASA_values)[0], totalCut )
242 col_1 = N.greater( N.transpose(ASA_values)[1], backboneCut )
243 col_2 = N.greater( N.transpose(ASA_values)[2], sidechainCut )
244
245 col_012 = N.concatenate( ([col_0],[col_1],[col_2]) )
246
247 exposedList = N.greater(N.sum(col_012), 0)
248
249 return exposedList
250
251
253 """
254 Takes a PDBModel and returns three lists::
255
256 relativeASA - a list of atom relative accessibilities
257 (in % - compared to the resideu in a
258 Gly-XXX-Gly tripeptide, an approximation
259 of the unfolded state)
260
261 -> N.array( 1 x N_atoms )
262
263 residueASA - array 3 x N_residues of float
264 acc.surf.area per res: sidechain, backbone, total
265
266
267 burriedResidues - a burried atom mask
268 note: cutoff values set above
269
270 -> [0, 1, 0, 0, 1, 1, 1, 0 ...]
271
272 @return: relative accessibility, residue ASA and burried atom mask
273 @rtype: array, array, [1|0]
274 """
275
276 resASA = self.__read_residueASA( )
277
278
279 burriedResidues = self.__exposedResidues( resASA )
280
281
282 atomRelASA = self.__read_relativeASA( )
283
284 return self.__asaAtomList( atomRelASA ), resASA, burriedResidues
285
286
288 """
289 convert ASA dict to list of values with len == atom number
290
291 @param relativeASA: see __read_relativeASA()
292 @type relativeASA: dict
293
294 @return: relative accessibilities e.g. [ 98.0, 0.0, 12.0, .. ],
295 Numpy array 1 x N(atoms)
296 @rtype: [float]
297 """
298 result = []
299
300 resIndex = 1
301 for res in self.model.resList():
302
303 for atom in res:
304
305 if relativeASA.has_key( resIndex ) \
306 and relativeASA[ resIndex ].has_key( atom['name'] ):
307
308 result.append( relativeASA[ resIndex ][ atom['name' ]] )
309 else:
310 result.append( 0.0 )
311
312 resIndex += 1
313
314 return result
315
316
317
318
319
320
322 """
323 Test class
324 """
325
326 - def run( self, local=0 ):
327 """
328 run function test
329
330 @param local: transfer local variables to global and perform
331 other tasks only when run locally
332 @type local: 1|0
333
334 @return: sum of the total ASA for each residue
335 @rtype: float
336 """
337 from Biskit import PDBModel
338
339
340
341 f = T.testRoot()+"/com/1BGS.pdb"
342 m = PDBModel(f)
343
344 m = m.compress( m.maskProtein() )
345 m = m.compress( m.maskHeavy() )
346
347
348 x = WhatIf( m, debug=0, verbose=0 )
349
350
351 atomAcc, resAcc, resMask = x.run()
352
353 if local:
354
355 m_ref = PDBModel(f)
356 m_ref = m.compress( m.maskProtein() )
357 for k in m_ref.atoms[0].keys():
358 ref = [ m_ref.atoms[0][k] for i in range( m_ref.lenAtoms() ) ]
359 mod = [ m.atoms[0][k] for i in range( m.lenAtoms() ) ]
360 if not ref == mod:
361 print 'Not equal ', k
362 else:
363 print 'Equal ', k
364
365
366 from Pymoler import Pymoler
367 pm = Pymoler()
368 model = pm.addPdb( m, '1' )
369 pm.colorRes( '1', resAcc[:,0] )
370 pm.show()
371
372 print "\nResult for first 10 atoms/residues: "
373 print '\nAccessability (A^2):\n', atomAcc[:10]
374 print '\nResidue accessability (A^2)'
375 print '[total, backbone, sidechain]:\n', resAcc[:10]
376 print '\nExposed residue mask:\n',resMask[:10]
377 print '\nTotal atom accessability (A^2): %.2f'%sum(atomAcc)
378 print ' residue accessability (A^2): %.2f'%sum(resAcc)[0]
379
380 globals().update( locals() )
381
382 return N.sum(resAcc[:,0])
383
384
386 """
387 Precalculated result to check for consistent performance.
388
389 @return: sum of the total ASA for each residue
390 @rtype: float
391 """
392 return 2814.6903
393
394
395 if __name__ == '__main__':
396
397 test = Test()
398
399 assert abs( test.run( local=1 ) - test.expected_result() ) < 1e-8
400