00001 """ inputgen class
00002
00003 Create an APBS input file using psize data
00004
00005 Written by Todd Dolinsky based on original sed script by Nathan Baker
00006
00007 ----------------------------
00008
00009 Version: $Id: inputgen.py 1552 2010-02-10 17:46:27Z yhuang01 $
00010
00011 APBS -- Adaptive Poisson-Boltzmann Solver
00012
00013 Nathan A. Baker (baker@biochem.wustl.edu)
00014 Dept. Biochemistry and Molecular Biophysics
00015 Center for Computational Biology
00016 Washington University in St. Louis
00017
00018 Additional contributing authors listed in the code documentation.
00019
00020 Copyright (c) 2002-2010, Washington University in St. Louis.
00021 Portions Copyright (c) 2002-2010. Nathan A. Baker
00022 Portions Copyright (c) 1999-2002. The Regents of the University of California.
00023 Portions Copyright (c) 1995. Michael Holst
00024
00025 All rights reserved.
00026
00027 Redistribution and use in source and binary forms, with or without
00028 modification, are permitted provided that the following conditions are met:
00029
00030 * Redistributions of source code must retain the above copyright notice, this
00031 list of conditions and the following disclaimer.
00032
00033 * Redistributions in binary form must reproduce the above copyright notice,
00034 this list of conditions and the following disclaimer in the documentation
00035 and/or other materials provided with the distribution.
00036
00037 * Neither the name of Washington University in St. Louis nor the names of its
00038 contributors may be used to endorse or promote products derived from this
00039 software without specific prior written permission.
00040
00041 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00042 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00043 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00044 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
00045 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00046 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00047 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00048 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00049 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00050 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00051 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00052
00053
00054 ----------------------------
00055 """
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 import string, sys
00075 import psize
00076 import pickle
00077
00078 class Elec:
00079 """
00080 An object for the ELEC section of an APBS input file
00081 """
00082 def __init__(self, pqrpath, size, method, asyncflag, istrng=0, potdx=0):
00083 """
00084 Initialize the variables that can be set in this object
00085 Users can modify any of these variables (that's why
00086 they're here!)
00087 """
00088
00089
00090
00091
00092 self.dime = size.getFineGridPoints()
00093 gmem = 200.0 * self.dime[0] * self.dime[1] * self.dime[2] / 1024.0 / 1024.0
00094 if method == "":
00095 if gmem > size.getConstant("gmemceil"): method = "mg-para"
00096 else: method = "mg-auto"
00097
00098 if method == "mg-para":
00099 self.dime = size.getSmallest()
00100
00101 self.method = method
00102 self.istrng = istrng
00103 self.glen = size.getCoarseGridDims()
00104 self.cglen = size.getCoarseGridDims()
00105 self.fglen = size.getFineGridDims()
00106 self.pdime = size.getProcGrid()
00107
00108 self.label = ""
00109 self.nlev = 4
00110 self.ofrac = 0.1
00111 self.async = 0
00112 self.asyncflag = asyncflag
00113 self.cgcent = "mol 1"
00114 self.fgcent = "mol 1"
00115 self.gcent = "mol 1"
00116 self.mol = 1
00117 self.lpbe = 1
00118 self.npbe = 0
00119 self.bcfl = "sdh"
00120 self.ion = [[-1,1.815],[1,1.875]]
00121 self.pdie = 2.0
00122 self.sdie = 78.54
00123 self.srfm = "smol"
00124 self.chgm = "spl2"
00125 self.sdens = 10.0
00126 self.srad = 1.4
00127 self.swin = 0.3
00128 self.temp = 298.15
00129 self.gamma = 0.105
00130 self.calcenergy = "total"
00131 self.calcforce = "no"
00132 if potdx == 1:
00133 self.write = [["pot", "dx", pqrpath]]
00134 else:
00135 self.write = [["pot", "dx", "pot"]]
00136
00137 def __str__(self):
00138 """
00139 Return the elec statement as a string. Check the method
00140 to see which keywords to use.
00141 """
00142 text = "elec %s\n" % self.label
00143 text += " %s\n" % self.method
00144 text += " dime %i %i %i\n" % (self.dime[0], self.dime[1], self.dime[2])
00145 if self.method == "mg-manual":
00146 text += " nlev %i\n" % self.nlev
00147 text += " glen %.3f %.3f %.3f\n" % (self.glen[0], self.glen[1], self.glen[2])
00148 text += " gcent %s\n" % self.gcent
00149 elif self.method == "mg-auto":
00150 text += " cglen %.4f %.4f %.4f\n" % (self.cglen[0], self.cglen[1], self.cglen[2])
00151 text += " fglen %.4f %.4f %.4f\n" % (self.fglen[0], self.fglen[1], self.fglen[2])
00152 text += " cgcent %s\n" % self.cgcent
00153 text += " fgcent %s\n" % self.fgcent
00154 elif self.method == "mg-para":
00155 text += " pdime %i %i %i\n" % (self.pdime[0], self.pdime[1], self.pdime[2])
00156 text += " ofrac %.1f\n" % self.ofrac
00157 text += " cglen %.4f %.4f %.4f\n" % (self.cglen[0], self.cglen[1], self.cglen[2])
00158 text += " fglen %.4f %.4f %.4f\n" % (self.fglen[0], self.fglen[1], self.fglen[2])
00159 text += " cgcent %s\n" % self.cgcent
00160 text += " fgcent %s\n" % self.fgcent
00161 if self.asyncflag == 1:
00162 text += " async %i\n" % self.async
00163 text += " mol %i\n" % self.mol
00164 if self.lpbe: text += " lpbe\n"
00165 else: text += " npbe\n"
00166 text += " bcfl %s\n" % self.bcfl
00167 if self.istrng > 0:
00168 for ion in self.ion:
00169 text += " ion charge %.2f conc %.3f radius %.4f\n" % (ion[0], self.istrng, ion[1])
00170 text += " pdie %.4f\n" % self.pdie
00171 text += " sdie %.4f\n" % self.sdie
00172 text += " srfm %s\n" % self.srfm
00173 text += " chgm %s\n" % self.chgm
00174 text += " sdens %.2f\n" % self.sdens
00175 text += " srad %.2f\n" % self.srad
00176 text += " swin %.2f\n" % self.swin
00177 text += " temp %.2f\n" % self.temp
00178 text += " calcenergy %s\n" % self.calcenergy
00179 text += " calcforce %s\n" % self.calcforce
00180 for write in self.write:
00181 text += " write %s %s %s\n" % (write[0], write[1], write[2])
00182 text += "end\n"
00183 return text
00184
00185 class Input:
00186 """
00187 The input class. Each input object is one APBS input file.
00188 """
00189
00190 def __init__(self, pqrpath, size, method, asyncflag, istrng=0, potdx=0):
00191 """
00192 Initialize the input file class. Each input file contains
00193 a PQR name, a list of elec objects, and a list of strings
00194 containing print statements. For starters assume two
00195 ELEC statements are needed, one for the inhomgenous and
00196 the other for the homogenous dielectric calculations.
00197
00198 Users can edit the elec statements and the print statements.
00199
00200 This assumes you have already run psize, either by
00201 size.runPsize(/path/to/pqr) or
00202
00203 size.parseString(string)
00204 size.setAll()
00205
00206 Parameters
00207 pqrpath: The path to the PQR file (string)
00208 size: The Psize object (psize)
00209 method: The method (para, auto, manual, async) to use
00210 asyncflag: 1 if async is desired, 0 otherwise
00211 """
00212
00213 self.pqrpath = pqrpath
00214 self.asyncflag = asyncflag
00215
00216
00217
00218 elec1 = Elec(pqrpath, size, method, asyncflag, istrng, potdx)
00219 if potdx == 0:
00220 elec2 = Elec(pqrpath, size, method, asyncflag, istrng, potdx)
00221 setattr(elec2, "sdie", 2.0)
00222 setattr(elec2, "write", [])
00223 else:
00224 elec2 = ""
00225 self.elecs = [elec1, elec2]
00226
00227 i = string.rfind(pqrpath, "/") + 1
00228 self.pqrname = pqrpath[i:]
00229
00230 if potdx == 0:
00231 self.prints = ["print elecEnergy 2 - 1 end"]
00232 else:
00233 self.prints = []
00234
00235 def __str__(self):
00236 """
00237 Return the text of the input file
00238 """
00239 text = "read\n"
00240 text += " mol pqr %s\n" % self.pqrname
00241 text += "end\n"
00242 for elec in self.elecs:
00243 text += str(elec)
00244 for prints in self.prints:
00245 text += prints
00246 text += "\nquit\n"
00247 return text
00248
00249 def printInputFiles(self):
00250 """
00251 Make the input file(s) associated with this object
00252 """
00253 period = string.find(self.pqrpath,".")
00254 if self.asyncflag == 1:
00255 outname = self.pqrpath[0:period] + "-para.in"
00256
00257
00258 for elec in self.elecs:
00259 elec.asyncflag = 0
00260 file = open(outname, "w")
00261 file.write(str(self))
00262 file.close()
00263
00264
00265 elec = self.elecs[0]
00266
00267 nproc = elec.pdime[0] * elec.pdime[1] * elec.pdime[2]
00268 for i in range(int(nproc)):
00269 outname = self.pqrpath[0:period] + "-PE%i.in" % i
00270 for elec in self.elecs:
00271 elec.asyncflag = 1
00272 elec.async = i
00273 file = open(outname, "w")
00274 file.write(str(self))
00275 file.close()
00276
00277 else:
00278 if period > 0:
00279 outname = self.pqrpath[0:period] + ".in"
00280 else:
00281 outname = self.pqrpath + ".in"
00282 file = open(outname, "w")
00283 file.write(str(self))
00284 file.close()
00285
00286 def dumpPickle(self):
00287 """
00288 Make a Python pickle associated with the APBS input parameters
00289 """
00290 period = string.find(self.pqrpath,".")
00291 if period > 0:
00292 outname = self.pqrpath[0:period] + "-input.p"
00293 else:
00294 outname = self.pqrpath + "-input.p"
00295 pfile = open(outname, "w")
00296 pickle.dump(self, pfile)
00297 pfile.close()
00298
00299 def splitInput(filename):
00300 """
00301 Split the parallel input file into multiple async file names
00302
00303 Parameters
00304 filename: The path to the original parallel input
00305 file (string)
00306 """
00307 nproc = 0
00308 file = open(filename, 'rU')
00309 text = ""
00310 while 1:
00311 line = file.readline()
00312 if line == "": break
00313 text += line
00314 line = string.strip(line)
00315 if line.startswith("pdime"):
00316 words = string.split(line)
00317 nproc = int(words[1]) * int(words[2]) * int(words[3])
00318
00319 if nproc == 0:
00320 sys.stderr.write("%s is not a valid APBS parallel input file!\n" % filename)
00321 sys.stderr.write("The inputgen script was unable to asynchronize this file!\n")
00322 sys.exit(2)
00323
00324 period = string.find(filename,".")
00325 for i in range(nproc):
00326 outname = filename[0:period] + "-PE%i.in" % i
00327 outtext = string.replace(text, "mg-para\n","mg-para\n async %i\n" % i)
00328 outfile = open(outname, "w")
00329 outfile.write(outtext)
00330 outfile.close()
00331
00332 def usage():
00333 """
00334 Display the usage information for this script
00335 """
00336 size = psize.Psize()
00337 usage = "\n"
00338 usage = usage + "Use this script to generate new APBS input files or split an existing\n"
00339 usage = usage + "parallel input file into multiple async files.\n\n"
00340 usage = usage + "Usage: inputgen.py [opts] <filename>\n"
00341 usage = usage + "Optional Arguments:\n"
00342 usage = usage + " --help : Display this text\n"
00343 usage = usage + " --split : Split an existing parallel input file to multiple\n"
00344 usage = usage + " async input files.\n"
00345 usage = usage + " --potdx : Create an input to compute an electrostatic potential map.\n"
00346 usage = usage + " --method=<value> : Force output file to write a specific APBS ELEC\n"
00347 usage = usage + " method. Options are para (parallel), auto\n"
00348 usage = usage + " (automatic), manual (manual), or async (asynchronous).\n"
00349 usage = usage + " solve. async will result in multiple input files.\n"
00350 usage = usage + " --cfac=<value> : Factor by which to expand molecular dimensions to\n"
00351 usage = usage + " get coarse grid dimensions.\n"
00352 usage = usage + " [default = %g]\n" % size.getConstant("cfac")
00353 usage = usage + " --fadd=<value> : Amount to add to molecular dimensions to get fine\n"
00354 usage = usage + " grid dimensions.\n"
00355 usage = usage + " [default = %g]\n" % size.getConstant("fadd")
00356 usage = usage + " --space=<value> : Desired fine mesh resolution\n"
00357 usage = usage + " [default = %g]\n" % size.getConstant("space")
00358 usage = usage + " --gmemfac=<value> : Number of bytes per grid point required\n"
00359 usage = usage + " for sequential MG calculation\n"
00360 usage = usage + " [default = %g]\n" % size.getConstant("gmemfac")
00361 usage = usage + " --gmemceil=<value> : Max MB allowed for sequential MG\n"
00362 usage = usage + " calculation. Adjust this to force the\n"
00363 usage = usage + " script to perform faster calculations (which\n"
00364 usage = usage + " require more parallelism).\n"
00365 usage = usage + " [default = %g]\n" % size.getConstant("gmemceil")
00366 usage = usage + " --ofrac=<value> : Overlap factor between mesh partitions\n"
00367 usage = usage + " [default = %g]\n" % size.getConstant("ofrac")
00368 usage = usage + " --redfac=<value> : The maximum factor by which a domain\n"
00369 usage = usage + " dimension can be reduced during focusing\n"
00370 usage = usage + " [default = %g]\n" % size.getConstant("redfac")
00371 usage = usage + " --istrng=<value> : Ionic strength (M). Na+ anc Cl- ions will be used\n"
00372 sys.stderr.write(usage)
00373 sys.exit(2)
00374
00375 def main():
00376
00377 import getopt
00378 filename = ""
00379 shortOptList = ""
00380 longOptList = ["help","split","potdx","method=","cfac=","space=","gmemceil=","gmemfac=","ofrac=","redfac=","istrng="]
00381
00382 try:
00383 opts, args = getopt.getopt(sys.argv[1:], shortOptList, longOptList)
00384 except getopt.GetoptError, details:
00385 sys.stderr.write("Option error (%s)!\n" % details)
00386 usage()
00387
00388 if len(args) != 1:
00389 sys.stderr.write("Invalid argument list!\n")
00390 usage()
00391 else:
00392 filename = args[0]
00393
00394 method = ""
00395 size = psize.Psize()
00396 async = 0
00397 split = 0
00398 istrng = 0
00399 potdx = 0
00400
00401 for o, a in opts:
00402 if o == "--help":
00403 usage()
00404 if o == "--split": split = 1
00405 if o == "--potdx": potdx = 1
00406 if o == "--method":
00407 if a == "para":
00408 sys.stdout.write("Forcing a parallel calculation\n")
00409 method = "mg-para"
00410 elif a == "auto":
00411 sys.stdout.write("Forcing a sequential calculation\n")
00412 method = "mg-auto"
00413 elif a == "async":
00414 sys.stdout.write("Forcing an asynchronous calculation\n")
00415 method = "mg-para"
00416 async = 1
00417 elif a == "manual":
00418 sys.stdout.write("Forcing a manual calculation\n")
00419 method = "mg-manual"
00420 else:
00421 sys.stdout.write("Incorrect method argument: %s\n" % a)
00422 sys.stdout.write("Defaulting to memory dependent result\n")
00423 if o == "--cfac":
00424 size.setConstant("cfac", float(a))
00425 if o == "--space":
00426 size.setConstant("space", float(a))
00427 if o == "--gmemfac":
00428 size.setConstant("gmemfac", int(a))
00429 if o == "--gmemceil":
00430 size.setConstant("gmemceil", int(a))
00431 if o == "--ofrac":
00432 size.setConstant("ofrac", float(a))
00433 if o == "--redfac":
00434 size.setConstant("redfac", float(a))
00435 if o == "--istrng":
00436 istrng = float(a)
00437
00438 if split == 1:
00439 splitInput(filename)
00440 else:
00441 size.runPsize(filename)
00442 input = Input(filename, size, method, async, istrng, potdx)
00443 input.printInputFiles()
00444
00445 if __name__ == "__main__": main()