1 """ psize class
2
3 Get dimensions and other information from a PQR file.
4
5 Originally written by Dave Sept
6 Additional APBS-specific features added by Nathan Baker
7 Ported to Python/Psize class by Todd Dolinsky and subsequently
8 hacked by Nathan Baker
9
10 ----------------------------
11
12 PDB2PQR -- An automated pipeline for the setup, execution, and analysis of
13 Poisson-Boltzmann electrostatics calculations
14
15 Nathan A. Baker (baker@biochem.wustl.edu)
16 Todd Dolinsky (todd@ccb.wustl.edu)
17 Dept. of Biochemistry and Molecular Biophysics
18 Center for Computational Biology
19 Washington University in St. Louis
20
21 Jens Nielsen (Jens.Nielsen@ucd.ie)
22 University College Dublin
23
24 Additional contributing authors listed in documentation and supporting
25 package licenses.
26
27 Copyright (c) 2003-2007. Washington University in St. Louis.
28 All Rights Reserved.
29
30 This file is part of PDB2PQR.
31
32 PDB2PQR is free software; you can redistribute it and/or modify
33 it under the terms of the GNU General Public License as published by
34 the Free Software Foundation; either version 2 of the License, or
35 (at your option) any later version.
36
37 PDB2PQR is distributed in the hope that it will be useful,
38 but WITHOUT ANY WARRANTY; without even the implied warranty of
39 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 GNU General Public License for more details.
41
42 You should have received a copy of the GNU General Public License
43 along with PDB2PQR; if not, write to the Free Software
44 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
45
46 ----------------------------
47 """
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 import string, sys
73 from sys import stdout
74 from math import log
75
78 self.constants = {"CFAC":1.7, "FADD":20, "SPACE":0.50, "GMEMFAC":200, "GMEMCEIL":400, "OFAC":0.1, "REDFAC":0.25, "TFAC_ALPHA":9e-5, "TFAC_XEON":3e-4, "TFAC_SPARC": 5e-4}
79 self.minlen = [360.0, 360.0, 360.0]
80 self.maxlen = [0.0, 0.0, 0.0]
81 self.q = 0.0
82 self.gotatom = 0
83 self.gothet = 0
84 self.olen = [0.0, 0.0, 0.0]
85 self.cen = [0.0, 0.0, 0.0]
86 self.clen = [0.0, 0.0, 0.0]
87 self.flen = [0.0, 0.0, 0.0]
88 self.n = [0, 0, 0]
89 self.np = [0.0, 0.0, 0.0]
90 self.nsmall = [0,0,0]
91 self.nfocus = 0
92
94 """ Parse the input structure as a string in PDB or PQR format """
95 lines = string.split(structure, "\n")
96 self.parseLines(lines)
97
102
104 """ Parse the lines """
105 for line in lines:
106 if string.find(line,"ATOM") == 0:
107 subline = string.replace(line[30:], "-", " -")
108 words = string.split(subline)
109 if len(words) < 4:
110
111
112
113 continue
114 self.gotatom = self.gotatom + 1
115 self.q = self.q + float(words[3])
116 rad = float(words[4])
117 center = []
118 for word in words[0:3]:
119 center.append(float(word))
120 for i in range(3):
121 self.minlen[i] = min(center[i]-rad, self.minlen[i])
122 self.maxlen[i] = max(center[i]+rad, self.maxlen[i])
123 elif string.find(line, "HETATM") == 0:
124 self.gothet = self.gothet + 1
125
127 """ Set a constant to a value; returns 0 if constant not found """
128 try:
129 self.constants[name] = value
130 return 1
131 except KeyError:
132 return 0
133
135 """ Get a constant value; raises KeyError if constant not found """
136 return self.constants[name]
137
139 """ Compute molecule dimensions """
140 for i in range(3):
141 self.olen[i] = maxlen[i] - minlen[i]
142 if self.olen[i] < 0.1:
143 self.olen[i] = 0.1
144 return self.olen
145
146
148 """ Compute coarse mesh dimensions """
149 for i in range(3):
150 self.clen[i] = self.constants["CFAC"] * olen[i]
151 return self.clen
152
154 """ Compute fine mesh dimensions """
155 for i in range(3):
156 self.flen[i] = olen[i] + self.constants["FADD"]
157 if self.flen[i] > clen[i]:
158
159
160
161 self.flen[i] = clen[i]
162 return self.flen
163
165 """ Compute molecule center """
166 for i in range(3):
167 self.cen[i] = (maxlen[i] + minlen[i]) / 2
168 return self.cen
169
170
172 """ Compute mesh grid points, assuming 4 levels in MG hierarchy """
173 tn = [0,0,0]
174 for i in range(3):
175 tn[i] = int(flen[i]/self.constants["SPACE"] + 0.5)
176 self.n[i] = 32*(int((tn[i] - 1) / 32 + 0.5)) + 1
177 if self.n[i] < 33:
178 self.n[i] = 33
179 return self.n
180
182 """ Compute parallel division in case memory requirement above ceiling
183 Find the smallest dimension and see if the number of grid points in
184 that dimension will fit below the memory ceiling
185 Reduce nsmall until an nsmall^3 domain will fit into memory """
186 nsmall = []
187 for i in range(3):
188 nsmall.append(n[i])
189 while 1:
190 nsmem = 200.0 * nsmall[0] * nsmall[1] * nsmall[2] / 1024 / 1024
191 if nsmem < self.constants["GMEMCEIL"]: break
192 else:
193 i = nsmall.index(max(nsmall))
194 nsmall[i] = 32 * ((nsmall[i] - 1)/32 - 1) + 1
195 if nsmall <= 0:
196 stdout.write("You picked a memory ceiling that is too small\n")
197 sys.exit(0)
198
199 self.nsmall = nsmall
200 return nsmall
201
203 """ Calculate the number of processors required to span each
204 dimension """
205
206 zofac = 1 + 2 * self.constants["OFAC"]
207 for i in range(3):
208 self.np[i] = n[i]/float(nsmall[i])
209 if self.np[i] > 1: self.np[i] = int(zofac*n[1]/nsmall[i] + 1.0)
210 return self.np
211
213 """ Calculate the number of levels of focusing required for each
214 processor subdomain """
215
216 nfoc = [0,0,0]
217 for i in range(3):
218 nfoc[i] = int(log((flen[i]/np[i])/clen[i])/log(self.constants["REDFAC"]) + 1.0)
219 nfocus = nfoc[0]
220 if nfoc[1] > nfocus: nfocus = nfoc[1]
221 if nfoc[2] > nfocus: nfocus = nfoc[2]
222 if nfocus > 0: nfocus = nfocus + 1
223 self.nfocus = nfocus
224
252
253 - def getMax(self): return self.maxlen
254 - def getMin(self): return self.minlen
264
269
271 """ Return a string with the formatted results """
272
273 str = "\n"
274
275 if self.gotatom > 0:
276
277 maxlen = self.getMax()
278 minlen = self.getMin()
279 q = self.getCharge()
280 olen = self.getLength()
281 clen = self.getCoarseGridDims()
282 flen = self.getFineGridDims()
283 cen = self.getCenter()
284 n = self.getFineGridPoints()
285 nsmall = self.getSmallest()
286 np = self.getProcGrid()
287 nfocus = self.getFocus()
288
289
290
291 nsmem = 200.0 * nsmall[0] * nsmall[1] * nsmall[2] / 1024 / 1024
292 gmem = 200.0 * n[0] * n[1] * n[2] / 1024 / 1024
293
294
295
296 tsolve_alpha = nfocus*nsmall[0]*nsmall[1]*nsmall[2]*self.constants["TFAC_ALPHA"];
297 tsolve_xeon = nfocus*nsmall[0]*nsmall[1]*nsmall[2]*self.constants["TFAC_XEON"];
298 tsolve_sparc = nfocus*nsmall[0]*nsmall[1]*nsmall[2]*self.constants["TFAC_SPARC"];
299
300
301 str = str + "################# MOLECULE INFO ####################\n"
302 str = str + "Number of ATOM entries = %i\n" % self.gotatom
303 str = str + "Number of HETATM entries (ignored) = %i\n" % self.gothet
304 str = str + "Total charge = %.3f e\n" % q
305 str = str + "Dimensions = %.3f x %.3f x %.3f A\n" % (olen[0], olen[1], olen[2])
306 str = str + "Center = %.3f x %.3f x %.3f A\n" % (cen[0], cen[1], cen[2])
307 str = str + "Lower corner = %.3f x %.3f x %.3f A\n" % (minlen[0], minlen[1], minlen[2])
308 str = str + "Upper corner = %.3f x %.3f x %.3f A\n" % (maxlen[0], maxlen[1], maxlen[2])
309
310 str = str + "\n"
311 str = str + "############## GENERAL CALCULATION INFO #############\n"
312 str = str + "Coarse grid dims = %.3f x %.3f x %.3f A\n" % (clen[0],
313 clen[1], clen[2])
314 str = str + "Fine grid dims = %.3f x %.3f x %.3f A\n" % (flen[0], flen[1], flen[2])
315 str = str + "Num. fine grid pts. = %i x %i x %i\n" % (n[0], n[1], n[2])
316
317 if gmem > self.constants["GMEMCEIL"]:
318 str = str + "Parallel solve required (%.3f MB > %.3f MB)\n" % (gmem, self.constants["GMEMCEIL"])
319 str = str + "Total processors required = %i\n" % (np[0]*np[1]*np[2])
320 str = str + "Proc. grid = %i x %i x %i\n" % (np[0], np[1], np[2])
321 str = str + "Grid pts. on each proc. = %i x %i x %i\n" % (nsmall[0], nsmall[1], nsmall[2])
322 xglob = np[0]*round(nsmall[0]/(1 + 2*self.constants["OFAC"]) - .001)
323 yglob = np[1]*round(nsmall[1]/(1 + 2*self.constants["OFAC"]) - .001)
324 zglob = np[2]*round(nsmall[2]/(1 + 2*self.constants["OFAC"]) - .001)
325 if np[0] == 1: xglob = nsmall[0]
326 if np[1] == 1: yglob = nsmall[1]
327 if np[2] == 1: zglob = nsmall[2]
328 str = str + "Fine mesh spacing = %g x %g x %g A\n" % (flen[0]/(xglob-1), flen[1]/(yglob-1), flen[2]/(zglob-1))
329 str = str + "Estimated mem. required for parallel solve = %.3f MB/proc.\n" % nsmem
330 ntot = nsmall[0]*nsmall[1]*nsmall[2]
331
332 else:
333 str = str + "Fine mesh spacing = %g x %g x %g A\n" % (flen[0]/(n[0]-1), flen[1]/(n[1]-1), flen[2]/(n[2]-1))
334 str = str + "Estimated mem. required for sequential solve = %.3f MB\n" % gmem
335 ntot = n[0]*n[1]*n[2]
336
337 str = str + "Number of focusing operations = %i\n" % nfocus
338
339 str = str + "\n"
340 str = str + "################# ESTIMATED REQUIREMENTS ####################\n"
341 str = str + "Memory per processor = %.3f MB\n" % (200.0*ntot/1024/1024)
342 str = str + "Grid storage requirements (ASCII) = %.3f MB\n" % (8.0*12*np[0]*np[1]*np[2]*ntot/1024/1024)
343 str = str + "Grid storage requirements (XDR) = %.3f MB\n" % (8.0*ntot*np[0]*np[1]*np[2]/1024/1024)
344 str = str + "Time to solve on 667 MHz EV67 Alpha = %.3f sec\n" % tsolve_alpha
345 str = str + "Time to solve on 500 MHz PIII Xeon = %.3f sec\n" % tsolve_xeon
346 str = str + "Time to solve on 400 MHz UltraSparc II = %.3f sec\n" % tsolve_sparc
347 str = str + "\n"
348
349 else:
350 str = str + "No ATOM entires in file!\n\n"
351
352 return str
353
395
397 import getopt
398 filename = ""
399 shortOptList = ""
400 longOptList = ["help", "CFAC=", "FADD=", "SPACE=", "GMEMFAC=", "GMEMCEIL=", "OFAC=", "REDFAC=", "TFAC_ALPHA=", "TFAC_XEON=", "TFAC_ALPHA="]
401 try:
402 opts, args = getopt.getopt(sys.argv[1:], shortOptList, longOptList)
403 except getopt.GetoptError, details:
404 sys.stderr.write("Option error (%s)!\n" % details)
405 usage()
406 if len(args) != 1:
407 sys.stderr.write("Invalid argument list!\n")
408 usage()
409 else:
410 filename = args[0]
411
412 psize = Psize()
413
414 for o, a in opts:
415 if o == "--help":
416 usage()
417 if o == "--CFAC":
418 psize.setConstant("CFAC", float(a))
419 if o == "--FADD":
420 psize.setConstant("FADD", int(a))
421 if o == "--SPACE":
422 psize.setConstant("SPACE", float(a))
423 if o == "--GMEMFAC":
424 psize.setConstant("GMEMFAC", int(a))
425 if o == "--GMEMCEIL":
426 psize.setConstant("GMEMCEIL", int(a))
427 if o == "--OFAC":
428 psize.setConstant("OFAC", float(a))
429 if o == "--REDFAC":
430 psize.setConstant("REDFAC", float(a))
431 if o == "--TFAC_ALPHA":
432 psize.setConstant("TFAC_ALPHA", float(a))
433 if o == "--TFAC_XEON":
434 psize.setConstant("TFAC_XEON", float(a))
435 if o == "--TFAC_SPARC":
436 psize.setConstant("TFAC_SPARC", float(a))
437
438 psize.runPsize(filename)
439 sys.stdout.write("Default constants used (./psize.py --help for more information):\n")
440 sys.stdout.write("%s\n" % psize.constants)
441 sys.stdout.write(psize.printResults())
442
443
444 if __name__ == "__main__": main()
445