00001
00002 """ Python APBS No Input Driver File
00003
00004 This module allows a user to run APBS through Python. Use this module if
00005 you wish to include APBS in a Python-based application. This specific
00006 version allows a user to read in data from the Python level without
00007 using the command line - thus enabling the ability to link seamlessly
00008 with other Python programs. Here the 'INPUT and 'PQR' variables are
00009 predetermined global strings, but can (and *should*) be dynamically created
00010 as desired - that's one of the main advantages of using Python!
00011
00012 It is strongly recommended that you edit out any part of this script that
00013 you do not need - many different options are included, but this makes the
00014 code much harder to read. I
00015
00016 The module mimics the main.c driver that is used in the C version of APBS.
00017 The functions which are called are located in apbslib.py, which is
00018 automatically generated by SWIG to wrap each APBS function. See the APBS
00019 documentation for more information about each function.
00020
00021 To access energy, potential, or force vectors for further use, see the
00022 appropriate printResults() function at the top of this script. This is
00023 merely an example - instead of printing the forces and energies you'll
00024 simply want to pass the arrays to other Python functions.
00025
00026 NOTE: You ***MUST*** use
00027
00028 calcforce comps
00029
00030 in the input file for each calculation that you wish to obtain a force
00031 vector - otherwise the vector will NOT be calculated.
00032
00033 Todd Dolinsky (todd@ccb.wustl.edu)
00034 Nathan Baker (baker@biochem.wustl.edu)
00035 Washington University in St. Louis
00036
00037 APBS -- Adaptive Poisson-Boltzmann Solver
00038
00039 Nathan A. Baker (baker@biochem.wustl.edu)
00040 Dept. Biochemistry and Molecular Biophysics
00041 Center for Computational Biology
00042 Washington University in St. Louis
00043
00044 Additional contributing authors listed in the code documentation.
00045
00046 Copyright (c) 2002-2010, Washington University in St. Louis.
00047 Portions Copyright (c) 2002-2010. Nathan A. Baker
00048 Portions Copyright (c) 1999-2002. The Regents of the University of California.
00049 Portions Copyright (c) 1995. Michael Holst
00050
00051 All rights reserved.
00052
00053 Redistribution and use in source and binary forms, with or without
00054 modification, are permitted provided that the following conditions are met:
00055
00056 * Redistributions of source code must retain the above copyright notice, this
00057 list of conditions and the following disclaimer.
00058
00059 * Redistributions in binary form must reproduce the above copyright notice,
00060 this list of conditions and the following disclaimer in the documentation
00061 and/or other materials provided with the distribution.
00062
00063 * Neither the name of Washington University in St. Louis nor the names of its
00064 contributors may be used to endorse or promote products derived from this
00065 software without specific prior written permission.
00066
00067 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00068 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00069 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00070 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
00071 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00072 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00073 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00074 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00075 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00076 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00077 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00078 """
00079
00080 from apbslib import *
00081 import sys, time
00082 import string
00083 import re
00084 from sys import stdout, stderr
00085
00086 __author__ = "Todd Dolinsky, Nathan Baker"
00087 __date__ = "July 2007"
00088
00089 INPUT = """read
00090 mol pqr ion.pqr
00091 end
00092 elec name solvated
00093 mg-manual
00094 dime 65 65 65
00095 nlev 4
00096 grid 0.33 0.33 0.33
00097 gcent mol 1
00098 chgm spl2
00099 mol 1
00100 lpbe
00101 bcfl mdh
00102 ion 1 0.000 2.0
00103 ion -1 0.000 2.0
00104 pdie 1.0
00105 sdie 78.54
00106 chgm spl2
00107 srfm spl2
00108 sdens 10.0
00109 srad 1.4
00110 swin 0.3
00111 temp 298.15
00112 gamma 0.105
00113 calcenergy total
00114 calcforce comps
00115 end
00116 elec name reference
00117 mg-manual
00118 dime 65 65 65
00119 nlev 4
00120 grid 0.33 0.33 0.33
00121 gcent mol 1
00122 mol 1
00123 lpbe
00124 bcfl mdh
00125 ion 1 0.000 2.0
00126 ion -1 0.000 2.0
00127 pdie 1.0
00128 sdie 1.0
00129 chgm spl2
00130 srfm spl2
00131 sdens 10.0
00132 srad 1.4
00133 swin 0.3
00134 temp 298.15
00135 gamma 0.105
00136 calcenergy total
00137 calcforce comps
00138 end
00139
00140 print energy 1 - 2 end
00141
00142 quit
00143 """
00144
00145 PQR = "ATOM 1 I ION 1 0.000 0.000 0.000 1.00 3.00"
00146
00147 Python_kb = 1.3806581e-23
00148 Python_Na = 6.0221367e+23
00149 NOSH_MAXMOL = 20
00150 NOSH_MAXCALC = 20
00151
00152 class APBSError(Exception):
00153 """ APBSError class
00154
00155 The APBSError class inherits off the Exception module and returns
00156 a string defining the nature of the error.
00157 """
00158
00159 def __init__(self, value):
00160 """
00161 Initialize with error message
00162
00163 Parameters
00164 value: Error Message (string)
00165 """
00166 self.value = value
00167
00168 def __str__(self):
00169 """
00170 Return the error message
00171 """
00172 return `self.value`
00173
00174 def getUnitConversion():
00175 """
00176 Get the unit conversion from kT to kJ/mol
00177
00178 Returns
00179 factor: The conversion factor (float)
00180 """
00181 temp = 298.15
00182 factor = Python_kb/1000.0 * temp * Python_Na
00183 return factor
00184
00185 def getHeader():
00186 """ Get header information about APBS
00187 Returns (header)
00188 header: Information about APBS
00189 """
00190
00191 """ Get header information about APBS
00192 Returns (header)
00193 header: Information about APBS
00194 """
00195
00196 header = "\n\n\
00197 ----------------------------------------------------------------------\n\
00198 Adaptive Poisson-Boltzmann Solver (APBS)\n\
00199 Version 1.3\n\
00200 \n\
00201 APBS -- Adaptive Poisson-Boltzmann Solver\n\
00202 \n\
00203 Nathan A. Baker (baker@biochem.wustl.edu)\n\
00204 Dept. Biochemistry and Molecular Biophysics\n\
00205 Center for Computational Biology\n\
00206 Washington University in St. Louis\n\
00207 \n\
00208 Additional contributing authors listed in the code documentation.\n\
00209 \n\
00210 Copyright (c) 2002-2010, Washington University in St. Louis.\n\
00211 Portions Copyright (c) 2002-2010. Nathan A. Baker\n\
00212 Portions Copyright (c) 1999-2002. The Regents of the University of California.\n\
00213 Portions Copyright (c) 1995. Michael Holst\n\
00214 \n\
00215 All rights reserved.\n\
00216 \n\
00217 Redistribution and use in source and binary forms, with or without\n\
00218 modification, are permitted provided that the following conditions are met:\n\
00219 \n\
00220 * Redistributions of source code must retain the above copyright notice, this\n\
00221 list of conditions and the following disclaimer.\n\
00222 \n\
00223 * Redistributions in binary form must reproduce the above copyright notice,\n\
00224 this list of conditions and the following disclaimer in the documentation\n\
00225 and/or other materials provided with the distribution.\n\
00226 \n\
00227 * Neither the name of Washington University in St. Louis nor the names of its\n\
00228 contributors may be used to endorse or promote products derived from this\n\
00229 software without specific prior written permission.\n\
00230 \n\
00231 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
00232 \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
00233 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
00234 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n\
00235 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n\
00236 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n\
00237 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n\
00238 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n\
00239 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n\
00240 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n\
00241 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
00242 ----------------------------------------------------------------------\n\
00243 \n\n"
00244 return header
00245
00246 def getUsage():
00247 """ Get usage information about running APBS via Python
00248 Returns (usage)
00249 usage: Text about running APBS via Python
00250 """
00251
00252 usage = "\n\n\
00253 ----------------------------------------------------------------------\n\
00254 This driver program calculates electrostatic potentials, energies,\n\
00255 and forces using both multigrid methods.\n\
00256 It is invoked as:\n\n\
00257 python noinput.py\n\
00258 ----------------------------------------------------------------------\n\n"
00259
00260 return usage
00261
00262
00263 def printResults(energyList, potList, forceList):
00264 """
00265 Print the results stored in the energy, potential and force lists to
00266 stdout. The arrays are accessed as follows:
00267
00268 energyList[calc #][atom #]: Per-atom energy for a specific calc #
00269
00270 potList[calc #][atom #] : Per-atom potential for a specific calc #
00271
00272 forceList is a little more difficult, as it is a list of dictionaries
00273 of lists:
00274
00275 forceList[calc #]['force type'][atom #][x=0/y=1/z=2 direction ]
00276
00277 So to access the qf x-component force from the first APBS elec
00278 calculation for the 2nd (atom id 1) atom, you can access
00279
00280 forceList[0]['qf'][1][0]
00281
00282 If you plan on using these lists extensively it would be wise to
00283 convert them into Python objects - this format is the cleanest for
00284 getting information back out from C, but not for dealing between
00285 Python functions.
00286 """
00287
00288
00289
00290 factor = getUnitConversion()
00291
00292 for i in range(len(potList)):
00293 list = potList[i]
00294 print "\nPer-atom potentials from calculation %i" % i
00295 for j in range(len(list)):
00296 atom = list[j]
00297 print "\t%i\t%.4f kT/e" % (j, (float(atom)))
00298
00299 for i in range(len(energyList)):
00300 list = energyList[i]
00301 print "\nPer-atom energies from calculation %i" % i
00302 for j in range(len(list)):
00303 atom = list[j]
00304 print "\t%i\t%.4f kJ/mol" % (j, (float(atom) * factor * 0.5))
00305
00306
00307
00308 for i in range(len(forceList)):
00309 qflist = forceList[i]["qf"]
00310 iblist = forceList[i]["ib"]
00311 dblist = forceList[i]["db"]
00312
00313 print "\nPer-atom forces from calculation %i" % i
00314 for j in range(len(qflist)):
00315 qf = "%.3E %.3E %.3E" % (qflist[j][0]*factor, qflist[j][1]*factor, qflist[j][2]*factor)
00316 ib = "%.3E %.3E %.3E" % (iblist[j][0]*factor, iblist[j][1]*factor, iblist[j][2]*factor)
00317 db = "%.3E %.3E %.3E" % (dblist[j][0]*factor, dblist[j][1]*factor, dblist[j][2]*factor)
00318 print "\t%i\t%s (qf)" % (j, qf)
00319 print "\t%i\t%s (ib)" % (j, ib)
00320 print "\t%i\t%s (db)" % (j, db)
00321
00322
00323 def runAPBS(PQR, INPUT):
00324 """ Main driver for testing. Runs APBS on given input file """
00325
00326
00327 com = Vcom_ctor(1)
00328 rank = Vcom_rank(com)
00329 size = Vcom_size(com)
00330 mgparm = MGparm()
00331 pbeparm = PBEparm()
00332 mem = Vmem_ctor("Main")
00333 pbe = new_pbelist(NOSH_MAXMOL)
00334 pmg = new_pmglist(NOSH_MAXMOL)
00335 pmgp = new_pmgplist(NOSH_MAXMOL)
00336 realCenter = double_array(3)
00337 totEnergy = []
00338 x = []
00339 y = []
00340 z = []
00341 chg = []
00342 rad = []
00343 nforce = int_array(NOSH_MAXCALC)
00344 atomforce = new_atomforcelist(NOSH_MAXCALC)
00345
00346
00347 main_timer_start = time.clock()
00348
00349
00350 nosh = NOsh_ctor(rank, size)
00351
00352
00353
00354 if not parseInputFromString(nosh, INPUT):
00355 stderr.write("main: Error while parsing input file.\n")
00356 raise APBSError, "Error occurred!"
00357
00358
00359
00360
00361
00362 alist = new_valist(NOSH_MAXMOL)
00363 atoms = string.split(PQR,"\n")
00364 for i in range(len(atoms)):
00365 atom = atoms[i]
00366 if not (atom.startswith("ATOM") or atom.startswith("HETATM")): continue
00367 if atom == "": continue
00368
00369
00370 haschain = 0
00371 if re.compile("( [A-Z]{3} [A-Z]{1} *\d+)").findall(atom) != []:
00372 haschain = 1
00373
00374 params = string.split(atom)
00375 x.append(float(params[5+haschain]))
00376 y.append(float(params[6+haschain]))
00377 z.append(float(params[7+haschain]))
00378 chg.append(float(params[8+haschain]))
00379 rad.append(float(params[9+haschain]))
00380
00381
00382
00383
00384
00385 myAlist = make_Valist(alist,0)
00386 Valist_load(myAlist, len(x), x,y,z,chg,rad)
00387
00388 if not NOsh_setupElecCalc(nosh, alist):
00389 stderr.write("main: Error setting up calculation.\n")
00390 raise APBSError, "Error setting up calculations!"
00391
00392
00393 for i in range(nosh.ncalc): totEnergy.append(0.0)
00394
00395
00396 energyList = []
00397 potList = []
00398 forceList = []
00399
00400
00401
00402
00403 dielXMap = new_gridlist(NOSH_MAXMOL)
00404 dielYMap = new_gridlist(NOSH_MAXMOL)
00405 dielZMap = new_gridlist(NOSH_MAXMOL)
00406 if loadDielMaps(nosh, dielXMap, dielYMap, dielZMap) != 1:
00407 stderr.write("Error reading dielectric maps!\n")
00408 raise APBSError, "Error reading dielectric maps!"
00409
00410 kappaMap = new_gridlist(NOSH_MAXMOL)
00411 if loadKappaMaps(nosh, kappaMap) != 1:
00412 stderr.write("Error reading kappa maps!\n")
00413 raise APBSError, "Error reading kappa maps!"
00414
00415 chargeMap = new_gridlist(NOSH_MAXMOL)
00416 if loadChargeMaps(nosh, chargeMap) != 1:
00417 stderr.write("Error reading charge maps!\n")
00418 raise APBSError, "Error reading charge maps!"
00419
00420
00421
00422 stdout.write("Preparing to run %d PBE calculations. \n" % nosh.ncalc)
00423
00424 for icalc in xrange(nosh.ncalc):
00425 stdout.write("---------------------------------------------\n")
00426 calc = NOsh_getCalc(nosh, icalc)
00427 mgparm = calc.mgparm
00428 pbeparm = calc.pbeparm
00429 if calc.calctype != 0:
00430 stderr.write("main: Only multigrid calculations supported!\n")
00431 raise APBSError, "Only multigrid calculations supported!"
00432
00433 for k in range(0, nosh.nelec):
00434 if NOsh_elec2calc(nosh,k) >= icalc:
00435 break
00436
00437 name = NOsh_elecname(nosh, k)
00438 if name == "":
00439 stdout.write("CALCULATION #%d: MULTIGRID\n" % (icalc+1))
00440 else:
00441 stdout.write("CALCULATION #%d (%s): MULTIGRID\n" % ((icalc+1),name))
00442 stdout.write("Setting up problem...\n")
00443
00444
00445
00446 if initMG(icalc, nosh, mgparm, pbeparm, realCenter, pbe,
00447 alist, dielXMap, dielYMap, dielZMap, kappaMap, chargeMap,
00448 pmgp, pmg) != 1:
00449 stderr.write("Error setting up MG calculation!\n")
00450 raise APBSError, "Error setting up MG calculation!"
00451
00452
00453
00454
00455 printMGPARM(mgparm, realCenter)
00456 printPBEPARM(pbeparm)
00457
00458
00459
00460 thispmg = get_Vpmg(pmg,icalc)
00461
00462 if solveMG(nosh, thispmg, mgparm.type) != 1:
00463 stderr.write("Error solving PDE! \n")
00464 raise APBSError, "Error Solving PDE!"
00465
00466
00467
00468 if setPartMG(nosh, mgparm, thispmg) != 1:
00469 stderr.write("Error setting partition info!\n")
00470 raise APBSError, "Error setting partition info!"
00471
00472
00473
00474 ret, totEnergy[icalc] = energyMG(nosh, icalc, thispmg, 0, 0.0, 0.0, 0.0, 0.0)
00475
00476
00477
00478
00479 aforce = get_AtomForce(atomforce, icalc)
00480 doforce = wrap_forceMG(mem, nosh, pbeparm, mgparm, thispmg, aforce, alist, nforce, icalc)
00481
00482
00483 writedataMG(rank, nosh, pbeparm, thispmg)
00484
00485
00486 writematMG(rank, nosh, pbeparm, thispmg)
00487
00488
00489
00490 potentials = getPotentials(nosh, pbeparm, thispmg, myAlist)
00491 potList.append(potentials)
00492
00493 energies = getEnergies(thispmg, myAlist)
00494 energyList.append(energies)
00495
00496
00497
00498
00499
00500 if doforce:
00501 forceList.append(getForces(aforce, myAlist))
00502
00503
00504
00505 if nosh.nprint > 0:
00506 stdout.write("---------------------------------------------\n")
00507 stdout.write("PRINT STATEMENTS\n")
00508 for iprint in xrange(nosh.nprint):
00509 if NOsh_printWhat(nosh, iprint) == NPT_ENERGY:
00510 printEnergy(com, nosh, totEnergy, iprint)
00511 elif NOsh_printWhat(nosh, iprint) == NPT_FORCE:
00512 printForce(com, nosh, nforce, atomforce, iprint)
00513 else:
00514 stdout.write("Undefined PRINT keyword!\n")
00515 break
00516
00517 stdout.write("----------------------------------------\n")
00518 stdout.write("CLEANING UP AND SHUTTING DOWN...\n")
00519
00520
00521 killForce(mem, nosh, nforce, atomforce)
00522 killEnergy()
00523 killMG(nosh, pbe, pmgp, pmg)
00524 killChargeMaps(nosh, chargeMap)
00525 killKappaMaps(nosh, kappaMap)
00526 killDielMaps(nosh, dielXMap, dielYMap, dielZMap)
00527 killMolecules(nosh, alist)
00528
00529
00530
00531
00532 delete_double_array(realCenter)
00533 delete_int_array(nforce)
00534 delete_atomforcelist(atomforce)
00535 delete_valist(alist)
00536 delete_gridlist(dielXMap)
00537 delete_gridlist(dielYMap)
00538 delete_gridlist(dielZMap)
00539 delete_gridlist(kappaMap)
00540 delete_gridlist(chargeMap)
00541 delete_pmglist(pmg)
00542 delete_pmgplist(pmgp)
00543 delete_pbelist(pbe)
00544
00545
00546
00547
00548 stdout.write("\n")
00549 stdout.write("Thanks for using APBS!\n\n")
00550
00551
00552 main_timer_stop = time.clock()
00553 stdout.write("Total execution time: %1.6e sec\n" % (main_timer_stop - main_timer_start))
00554
00555 return energyList, potList, forceList
00556
00557 if __name__ == "__main__":
00558
00559
00560 stdout.write(getHeader())
00561 if len(sys.argv) != 1:
00562 stderr.write("main: Called with %d arguments!\n" % len(sys.argv))
00563 stderr.write(getUsage())
00564 raise APBSError, "Incorrect Usage!"
00565
00566 energyList, potList, forceList = runAPBS(PQR, INPUT)
00567
00568
00569
00570 printResults(energyList, potList, forceList)