• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

tools/manip/psize.py

00001 #!/usr/bin/env python
00002 # You may need to edit the above to point to your version of Python 2.0
00003 """
00004 psize.py 
00005 Get dimensions and other interesting information from a PQR file
00006 
00007 Originally written by Dave Sept
00008 Additional APBS-specific features added by Nathan Baker
00009 Ported to Python/Psize class by Todd Dolinsky and subsequently hacked by
00010 Nathan Baker
00011 
00012 Version:  $Id: psize.py 1552 2010-02-10 17:46:27Z yhuang01 $
00013 
00014 APBS -- Adaptive Poisson-Boltzmann Solver
00015 
00016   Nathan A. Baker (baker@biochem.wustl.edu)
00017   Dept. Biochemistry and Molecular Biophysics
00018   Center for Computational Biology
00019   Washington University in St. Louis
00020 
00021   Additional contributing authors listed in the code documentation.
00022 
00023 Copyright (c) 2002-2010, Washington University in St. Louis.
00024 Portions Copyright (c) 2002-2010.  Nathan A. Baker
00025 Portions Copyright (c) 1999-2002.  The Regents of the University of California.
00026 Portions Copyright (c) 1995.  Michael Holst
00027 
00028 All rights reserved.
00029 
00030 Redistribution and use in source and binary forms, with or without
00031 modification, are permitted provided that the following conditions are met: 
00032 
00033 * Redistributions of source code must retain the above copyright notice, this
00034 list of conditions and the following disclaimer.  
00035 
00036 * Redistributions in binary form must reproduce the above copyright notice,
00037 this list of conditions and the following disclaimer in the documentation
00038 and/or other materials provided with the distribution.
00039 
00040 * Neither the name of Washington University in St. Louis nor the names of its
00041 contributors may be used to endorse or promote products derived from this
00042 software without specific prior written permission.
00043 
00044 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00045 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00046 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00047 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
00048 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00049 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00050 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00051 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00052 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00053 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00054 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00055 
00056 """
00057 
00058 import string, sys, getopt
00059 from sys import stdout, stderr
00060 from math import log
00061 
00062 class Psize:
00063            """Master class for parsing input files and suggesting settings"""
00064            def __init__(self):
00065                       self.constants = {"cfac": 1.7, "fadd":20, "space": 0.50, "gmemfac": 200, "gmemceil": 400, "ofrac":0.1, "redfac": 0.25 }
00066                       self.minlen = [None, None, None]
00067                       self.maxlen = [None, None, None]
00068                       self.q = 0.0
00069                       self.gotatom = 0
00070                       self.gothet = 0
00071                       self.olen = [0.0, 0.0, 0.0]
00072                       self.cen = [0.0, 0.0, 0.0]
00073                       self.clen = [0.0, 0.0, 0.0]
00074                       self.flen = [0.0, 0.0, 0.0]
00075                       self.n = [0, 0, 0]
00076                       self.np = [0.0, 0.0, 0.0]
00077                       self.nsmall = [0,0,0]
00078                       self.nfocus = 0
00079 
00080            def parseString(self, structure):
00081                       """ Parse the input structure as a string in PDB or PQR format """
00082                       lines = string.split(structure, "\n")
00083                       self.parseLines(lines)
00084 
00085            def parseInput(self, filename):
00086                       """ Parse input structure file in PDB or PQR format """
00087                       file = open(filename, "r")
00088                       self.parseLines(file.readlines())
00089 
00090            def parseLines(self, lines):
00091                       """ Parse the lines """
00092                       for line in lines:
00093                                  if string.find(line,"ATOM") == 0:
00094                                             subline = string.replace(line[30:], "-", " -")
00095                                             words = string.split(subline)
00096                                             if len(words) < 4:    
00097                                                        continue
00098                                             self.gotatom = self.gotatom + 1
00099                                             self.q = self.q + float(words[3])
00100                                             rad = float(words[4])
00101                                             center = []
00102                                             for word in words[0:3]:
00103                                                        center.append(float(word))
00104                                             for i in range(3):
00105                                                        if self.minlen[i] == None or center[i]-rad < self.minlen[i]:
00106                                                                   self.minlen[i] = center[i]-rad
00107                                                        if self.maxlen[i] == None or center[i]+rad > self.maxlen[i]:
00108                                                                   self.maxlen[i] = center[i]+rad
00109                                  elif string.find(line, "HETATM") == 0:
00110                                             self.gothet = self.gothet + 1
00111                                             # Special handling for no ATOM entries in the pqr file, only HETATM entries
00112                                             if self.gotatom == 0:
00113                                                        subline = string.replace(line[30:], "-", " -")
00114                                                        words = string.split(subline)
00115                                                        if len(words) < 4:    
00116                                                                   continue
00117                                                        self.q = self.q + float(words[3])
00118                                                        rad = float(words[4])
00119                                                        center = []
00120                                                        for word in words[0:3]:
00121                                                                   center.append(float(word))
00122                                                        for i in range(3):
00123                                                                   if self.minlen[i] == None or center[i]-rad < self.minlen[i]:
00124                                                                              self.minlen[i] = center[i]-rad
00125                                                                   if self.maxlen[i] == None or center[i]+rad > self.maxlen[i]:
00126                                                                              self.maxlen[i] = center[i]+rad
00127 
00128            def setConstant(self, name, value):
00129                       """ Set a constant to a value; returns 0 if constant not found """
00130                       try:
00131                                  self.constants[name] = value
00132                                  return 1
00133                       except KeyError:
00134                                  return 0
00135 
00136            def getConstant(self, name):
00137                       """ Get a constant value; raises KeyError if constant not found """
00138                       return self.constants[name]
00139 
00140            def setLength(self, maxlen, minlen):
00141                       """ Compute molecule dimensions """
00142                       for i in range(3):
00143                                  self.olen[i] = maxlen[i] - minlen[i]
00144                                  if self.olen[i] < 0.1:
00145                                             self.olen[i] = 0.1
00146                       return self.olen
00147 
00148            def setCoarseGridDims(self, olen):
00149                       """ Compute coarse mesh dimensions """
00150                       for i in range(3):
00151                                  self.clen[i] = self.constants["cfac"] * olen[i]
00152                       return self.clen
00153 
00154            def setFineGridDims(self, olen, clen):
00155                       """ Compute fine mesh dimensions """
00156                       for i in range(3):
00157                                  self.flen[i] = olen[i] + self.constants["fadd"]
00158                                  if self.flen[i] > clen[i]:
00159                                             self.flen[i] = clen[i]
00160                       return self.flen
00161 
00162            def setCenter(self, maxlen, minlen):
00163                       """ Compute molecule center """
00164                       for i in range(3):
00165                                  self.cen[i] = (maxlen[i] + minlen[i]) / 2
00166                       return self.cen
00167 
00168            def setFineGridPoints(self, flen):
00169                       """ Compute mesh grid points, assuming 4 levels in MG hierarchy """
00170                       tn = [0,0,0]
00171                       for i in range(3):
00172                                  tn[i] = int(flen[i]/self.constants["space"] + 0.5)
00173                                  self.n[i] = 32*(int((tn[i] - 1) / 32.0 + 0.5)) + 1
00174                                  if self.n[i] < 33:
00175                                             self.n[i] = 33
00176                       return self.n
00177 
00178            def setSmallest(self, n):
00179                       """ Compute parallel division in case memory requirement above
00180                       ceiling Find the smallest dimension and see if the number of
00181                       grid points in that dimension will fit below the memory ceiling
00182                       Reduce nsmall until an nsmall^3 domain will fit into memory """
00183                       nsmall = []
00184                       for i in range(3):
00185                                  nsmall.append(n[i])
00186                       while 1:
00187                                  nsmem = 200.0 * nsmall[0] * nsmall[1] * nsmall[2] / 1024 / 1024
00188                                  if nsmem < self.constants["gmemceil"]: 
00189                                             break
00190                                  else:
00191                                             i = nsmall.index(max(nsmall))
00192                                             nsmall[i] = 32 * ((nsmall[i] - 1)/32 - 1) + 1
00193                                             if nsmall <= 0:
00194                                                        stdout.write("You picked a memory ceiling that is too small\n")
00195                                                        sys.exit(0)                      
00196 
00197                       self.nsmall = nsmall
00198                       return nsmall
00199 
00200            def setProcGrid(self, n, nsmall):
00201                       """ Calculate the number of processors required to span each
00202                       dimension """
00203 
00204                       zofac = 1 + 2 * self.constants["ofrac"]
00205                       for i in range(3):
00206                                  self.np[i] = n[i]/float(nsmall[i])
00207                                  if self.np[i] > 1: self.np[i] = int(zofac*n[1]/nsmall[i] + 1.0)
00208                       return self.np
00209 
00210            def setFocus(self, flen, np, clen):
00211                       """ Calculate the number of levels of focusing required for
00212                       each processor subdomain """
00213 
00214                       nfoc = [0,0,0]
00215                       for i in range(3):
00216                                  nfoc[i] = int(log((flen[i]/np[i])/clen[i])/log(self.constants["redfac"]) + 1.0)
00217                       nfocus = nfoc[0]
00218                       if nfoc[1] > nfocus: nfocus = nfoc[1]
00219                       if nfoc[2] > nfocus: nfocus = nfoc[2]
00220                       if nfocus > 0: nfocus = nfocus + 1
00221                       self.nfocus = nfocus
00222 
00223            def setAll(self):
00224                       """ Set up all of the things calculated individually above """
00225                       maxlen = self.getMax()
00226                       minlen = self.getMin()
00227                       self.setLength(maxlen, minlen)
00228                       olen = self.getLength()
00229            
00230                       self.setCoarseGridDims(olen)
00231                       clen = self.getCoarseGridDims()             
00232            
00233                       self.setFineGridDims(olen, clen)
00234                       flen = self.getFineGridDims()
00235            
00236                       self.setCenter(maxlen, minlen)
00237                       cen = self.getCenter()
00238            
00239                       self.setFineGridPoints(flen)
00240                       n = self.getFineGridPoints()
00241            
00242                       self.setSmallest(n)
00243                       nsmall = self.getSmallest()
00244            
00245                       self.setProcGrid(n, nsmall)
00246                       np = self.getProcGrid()
00247            
00248                       self.setFocus(flen, np, clen)
00249                       nfocus = self.getFocus()
00250 
00251            def getMax(self): return self.maxlen
00252            def getMin(self): return self.minlen
00253            def getCharge(self): return self.q
00254            def getLength(self): return self.olen
00255            def getCoarseGridDims(self): return self.clen
00256            def getFineGridDims(self): return self.flen
00257            def getCenter(self): return self.cen
00258            def getFineGridPoints(self): return self.n
00259            def getSmallest(self): return self.nsmall
00260            def getProcGrid(self): return self.np
00261            def getFocus(self): return self.nfocus
00262 
00263            def runPsize(self, filename):
00264                       """ Parse input PQR file and set parameters """
00265                       self.parseInput(filename)
00266                       self.setAll()
00267 
00268            def printResults(self):
00269                       """ Return a string with the formatted results """
00270 
00271                       str = "\n"
00272            
00273                       if self.gotatom > 0:
00274 
00275                                  maxlen = self.getMax()
00276                                  minlen = self.getMin()
00277                                  q = self.getCharge()
00278                                  olen = self.getLength()
00279                                  clen = self.getCoarseGridDims()             
00280                                  flen = self.getFineGridDims()
00281                                  cen = self.getCenter()
00282                                  n = self.getFineGridPoints()
00283                                  nsmall = self.getSmallest()
00284                                  np = self.getProcGrid()
00285                                  nfocus = self.getFocus()
00286            
00287                                  # Compute memory requirements
00288                                  nsmem = 200.0 * nsmall[0] * nsmall[1] * nsmall[2] / 1024 / 1024
00289                                  gmem = 200.0 * n[0] * n[1] * n[2] / 1024 / 1024
00290 
00291                                  # Print the calculated entries
00292                                  str = str + "################# MOLECULE INFO ####################\n"
00293                                  str = str + "Number of ATOM entries = %i\n" % self.gotatom
00294                                  str = str + "Number of HETATM entries (ignored) = %i\n" % self.gothet
00295                                  str = str + "Total charge = %.3f e\n" % q
00296                                  str = str + "Dimensions = %.3f x %.3f x %.3f A\n" % (olen[0], olen[1], olen[2])
00297                                  str = str + "Center = %.3f x %.3f x %.3f A\n" % (cen[0], cen[1], cen[2])
00298                                  str = str + "Lower corner = %.3f x %.3f x %.3f A\n" % (minlen[0], minlen[1], minlen[2])
00299                                  str = str + "Upper corner = %.3f x %.3f x %.3f A\n" % (maxlen[0], maxlen[1], maxlen[2])
00300            
00301                                  str = str + "\n"
00302                                  str = str + "############## GENERAL CALCULATION INFO #############\n"
00303                                  str = str + "Coarse grid dims = %.3f x %.3f x %.3f A\n" % (clen[0],
00304 clen[1]    , clen[2])
00305                                  str = str + "Fine grid dims = %.3f x %.3f x %.3f A\n" % (flen[0], flen[1], flen[2])
00306                                  str = str + "Num. fine grid pts. = %i x %i x %i\n" % (n[0], n[1], n[2])
00307            
00308                                  if gmem > self.constants["gmemceil"]:
00309                                             str = str + "Parallel solve required (%.3f MB > %.3f MB)\n" % (gmem, self.constants["gmemceil"])
00310                                             str = str + "Total processors required = %i\n" % (np[0]*np[1]*np[2])
00311                                             str = str + "Proc. grid = %i x %i x %i\n" % (np[0], np[1], np[2])
00312                                             str = str + "Grid pts. on each proc. = %i x %i x %i\n" % (nsmall[0], nsmall[1], nsmall[2])
00313                                             xglob = np[0]*round(nsmall[0]/(1 + 2*self.constants["ofrac"]) - .001)
00314                                             yglob = np[1]*round(nsmall[1]/(1 + 2*self.constants["ofrac"]) - .001)
00315                                             zglob = np[2]*round(nsmall[2]/(1 + 2*self.constants["ofrac"]) - .001)
00316                                             if np[0] == 1: 
00317                                                        xglob = nsmall[0]
00318                                             if np[1] == 1: 
00319                                                        yglob = nsmall[1]
00320                                             if np[2] == 1: 
00321                                                        zglob = nsmall[2]
00322                                             str = str + "Fine mesh spacing = %g x %g x %g A\n" % (flen[0]/(xglob-1), flen[1]/(yglob-1), flen[2]/(zglob-1))
00323                                             str = str + "Estimated mem. required for parallel solve = %.3f MB/proc.\n" % nsmem
00324                                             ntot = nsmall[0]*nsmall[1]*nsmall[2]
00325            
00326                                  else:
00327                                             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))
00328                                             str = str + "Estimated mem. required for sequential solve = %.3f MB\n" % gmem
00329                                             ntot = n[0]*n[1]*n[2]
00330                       
00331                                  str = str + "Number of focusing operations = %i\n" % nfocus
00332            
00333                                  str = str + "\n"
00334                                  str = str + "################# ESTIMATED REQUIREMENTS ####################\n"
00335                                  str = str + "Memory per processor                   = %.3f MB\n" % (200.0*ntot/1024/1024)
00336                                  str = str + "Grid storage requirements (ASCII)      = %.3f MB\n" % (8.0*12*np[0]*np[1]*np[2]*ntot/1024/1024)
00337                                  str = str + "\n"
00338 
00339                       else:
00340                                  str = str + "No ATOM entires in file!\n\n"
00341 
00342                       return str
00343 
00344 def usage(rc):
00345            """ Print usage information and exit with error code rc """
00346            psize = Psize()
00347            s = "\n"
00348            s = s + "Psize script (part of APBS)\n\n"
00349            s = s + "Usage: psize.py [opts] <filename>\n\n"
00350            s = s + "Optional Arguments:\n"
00351            s = s + "  --help, -h\n"
00352            s = s + "             Display this text\n"
00353            s = s + "  --cfac=<value> [default = %g]\n" % psize.getConstant("cfac")
00354            s = s + "\
00355                       Factor by which to expand molecular dimensions to get coarse\n\
00356                       grid dimensions.  This value might need to be increased if\n\
00357                       very simple boundary conditions or very highly charged molecules\n\
00358                       are used.\n"
00359            s = s + "  --fadd=<value> [default = %g]\n" % psize.getConstant("fadd")
00360            s = s + "\
00361                       Amount (in A) to add to molecular dimensions to get fine grid\n\
00362                       dimensions. This value might need to be increased in the\n\
00363                       visualization of highly charged molecules to prevent isocontours\n\
00364                       from being truncated/clipped.\n"
00365            s = s + "  --space=<value>       [default = %g]\n" % psize.getConstant("space")
00366            s = s + "\
00367                       The desired fine mesh spacing (in A).  This should be 0.5 A or\n\
00368                       less for quantitative calculations but can be increased for\n\
00369                       coarse visualization.\n"
00370            s = s + "  --gmemceil=<value> [default = %g]\n" % psize.getConstant("gmemceil")
00371            s = s + "\
00372                       Maximum memory (in MB) available per-processor for a calculation.\n\
00373                       This should be adjusted to fit your machine.  If the calculation\n\
00374                       exceeds this value, psize will recommend settings for parallel\n\
00375                       focusing.\n"
00376            s = s + "  --ofrac=<value> [default = %g]\n" % psize.getConstant("ofrac")
00377            s = s + "\
00378                       Desired overlap between parallel focusing grids.  Although the\n\
00379                       default value works for many calculations, the best setting can\n\
00380                       be somewhat system-dependent.  Users are encouraged to check\n\
00381                       multiple values of ofrac for quantitative calcualtions.\n"
00382            s = s + "  --gmemfac=<value> [default = %g]\n" % psize.getConstant("gmemfac")
00383            s = s + "\
00384                       APBS memory usage (in bytes per grid point) for a sequential\n\
00385                       multigrid calculatiaon.  This value should not need to be\n\
00386                       adjusted unless the program has been modified.\n"
00387            s = s + "  --redfac=<value> [default = %g]\n" % psize.getConstant("redfac")
00388            s = s + "\
00389                       APBS maximum reduction of grid spacing during focusing.  This\n\
00390                       value should not need to be adjusted unless the program has\n\
00391                       been modified.\n"
00392 
00393            stderr.write(s)
00394            sys.exit(rc)
00395 
00396 def main():
00397            """ Main driver for this script """
00398            filename = ""
00399            shortOptList = "h"
00400            longOptList = ["help", "cfac=", "fadd=", "space=", "gmemfac=", "gmemceil=", "ofrac=", "redfac=" ]
00401            try:
00402                       opts, args = getopt.getopt(sys.argv[1:], shortOptList, longOptList)
00403            except getopt.GetoptError, details:
00404                       stderr.write("Option error (%s)!\n" % details)
00405                       usage(2)
00406            if len(args) != 1: 
00407                       stderr.write("Invalid argument list!\n")
00408                       usage(2)
00409            else:
00410                       filename = args[0]
00411 
00412            psize = Psize()       
00413 
00414            for o, a in opts:
00415                       if (o.lower() == "--help") or (o == "-h"):
00416                                  usage(0)
00417                       if o.lower() == "--cfac":
00418                                  psize.setConstant("cfac", float(a))
00419                       if o.lower() == "--fadd":
00420                                  psize.setConstant("fadd", int(a))
00421                       if o.lower() == "--space":
00422                                  psize.setConstant("space", float(a))
00423                       if o.lower() == "--gmemfac":
00424                                  psize.setConstant("gmemfac", int(a))
00425                       if o.lower() == "--gmemceil":
00426                                  psize.setConstant("gmemceil",  int(a))
00427                       if o.lower() == "--ofrac":
00428                                  psize.setConstant("ofrac", float(a))
00429                       if o.lower() == "--redfac":
00430                                  psize.setConstant("redfac", float(a))
00431 
00432            psize.runPsize(filename)
00433            stdout.write("# Constants used: \n");
00434            for key in psize.constants.keys():
00435                       stdout.write("# \t%s: %s\n" % (key, psize.constants[key]))
00436            stdout.write("# Run:\n")
00437            stdout.write("#    `%s --help`\n" % sys.argv[0])
00438            stdout.write("# for more information on these default values\n" )
00439            stdout.write(psize.printResults())
00440 
00441 if __name__ == "__main__": main()

Generated on Wed Oct 20 2010 11:12:19 for APBS by  doxygen 1.7.2