00001 """ Python APBS Generalized Born Implementation
00002
00003 This module uses APBS to parameterize the effective Born radii of the
00004 Generalized Born electrostatic model and calculate the electrostatic
00005 solvation energy.
00006
00007 Justin Xiang (jxiang@ccb.wustl.edu)
00008 Todd Dolinsky (todd@ccb.wustl.edu)
00009 Nathan Baker (baker@biochem.wustl.edu)
00010 Washington University in St. Louis
00011
00012 APBS -- Adaptive Poisson-Boltzmann Solver
00013
00014 Nathan A. Baker (baker@biochem.wustl.edu)
00015 Dept. Biochemistry and Molecular Biophysics
00016 Center for Computational Biology
00017 Washington University in St. Louis
00018
00019 Additional contributing authors listed in the code documentation.
00020
00021 Copyright (c) 2002-2010, Washington University in St. Louis.
00022 Portions Copyright (c) 2002-2010. Nathan A. Baker
00023 Portions Copyright (c) 1999-2002. The Regents of the University of California.
00024 Portions Copyright (c) 1995. Michael Holst
00025
00026 All rights reserved.
00027
00028 Redistribution and use in source and binary forms, with or without
00029 modification, are permitted provided that the following conditions are met:
00030
00031 * Redistributions of source code must retain the above copyright notice, this
00032 list of conditions and the following disclaimer.
00033
00034 * Redistributions in binary form must reproduce the above copyright notice,
00035 this list of conditions and the following disclaimer in the documentation
00036 and/or other materials provided with the distribution.
00037
00038 * Neither the name of Washington University in St. Louis nor the names of its
00039 contributors may be used to endorse or promote products derived from this
00040 software without specific prior written permission.
00041
00042 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00043 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00044 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00045 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
00046 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00047 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00048 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00049 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00050 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00051 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00052 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00053 """
00054
00055 import sys, time, getopt
00056 import string
00057 import math
00058 from sys import stdout, stderr
00059 from math import sqrt, pow, exp, pi
00060
00061 __author__ = "Todd Dolinsky, Nathan Baker"
00062 __date__ = "July 2007"
00063
00064 Python_kb = 1.3806581e-23
00065 Python_Na = 6.0221367e+23
00066 Python_e0 = 8.85419e-12
00067 Python_C = 1.602117e-19
00068 NOSH_MAXMOL = 20
00069 NOSH_MAXCALC = 20
00070
00071 class APBSError(Exception):
00072 """ APBSError class
00073
00074 The APBSError class inherits off the Exception module and returns
00075 a string defining the nature of the error.
00076 """
00077
00078 def __init__(self, value):
00079 """
00080 Initialize with error message
00081
00082 Parameters
00083 value: Error Message (string)
00084 """
00085 self.value = value
00086
00087 def __str__(self):
00088 """
00089 Return the error message
00090 """
00091 return `self.value`
00092
00093 def getHeader():
00094 """ Get header information about APBS
00095 Returns (header)
00096 header: Information about APBS
00097 """
00098
00099 header = "\n\n\
00100 ----------------------------------------------------------------------\n\
00101 Adaptive Poisson-Boltzmann Solver (APBS)\n\
00102 Version 1.3\n\
00103 \n\
00104 APBS -- Adaptive Poisson-Boltzmann Solver\n\
00105 \n\
00106 Nathan A. Baker (baker@biochem.wustl.edu)\n\
00107 Dept. Biochemistry and Molecular Biophysics\n\
00108 Center for Computational Biology\n\
00109 Washington University in St. Louis\n\
00110 \n\
00111 Additional contributing authors listed in the code documentation.\n\
00112 \n\
00113 Copyright (c) 2002-2010, Washington University in St. Louis.\n\
00114 Portions Copyright (c) 2002-2010. Nathan A. Baker\n\
00115 Portions Copyright (c) 1999-2002. The Regents of the University of California.\n\
00116 Portions Copyright (c) 1995. Michael Holst\n\
00117 \n\
00118 All rights reserved.\n\
00119 \n\
00120 Redistribution and use in source and binary forms, with or without\n\
00121 modification, are permitted provided that the following conditions are met:\n\
00122 \n\
00123 * Redistributions of source code must retain the above copyright notice, this\n\
00124 list of conditions and the following disclaimer.\n\
00125 \n\
00126 * Redistributions in binary form must reproduce the above copyright notice,\n\
00127 this list of conditions and the following disclaimer in the documentation\n\
00128 and/or other materials provided with the distribution.\n\
00129 \n\
00130 * Neither the name of Washington University in St. Louis nor the names of its\n\
00131 contributors may be used to endorse or promote products derived from this\n\
00132 software without specific prior written permission.\n\
00133 \n\
00134 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
00135 \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
00136 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
00137 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n\
00138 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n\
00139 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n\
00140 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n\
00141 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n\
00142 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n\
00143 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n\
00144 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
00145 ----------------------------------------------------------------------\n\
00146 \n\n"
00147
00148 return header
00149
00150 def getUsage():
00151 """ Get usage information about running APBS via Python
00152 Returns (usage)
00153 usage: Text about running APBS via Python
00154 """
00155
00156 usage = "\n\n\
00157 ----------------------------------------------------------------------\n\
00158 This driver program calculates electrostatic solvation energy from the\n\
00159 Generalized Born model based on a provided parameter file.\n\
00160 It is invoked as:\n\n\
00161 python readGB.py -p parameter_file -i structure.pqr\n\n\
00162 The parameter_file can be generated using runGB modules. It should\n\
00163 contain either, or both of the column fields: Effective Born radii and\n\
00164 self energy. The first line of the parameter_file should indicate the\n\
00165 value of solvent dielectric. The second line specifies what the column\n\
00166 fields are, by flagging 'radii' and/or 'energy'.\n\n\
00167 Optional arguments:\n\
00168 -o <output_parameter> specifies path to output GB parameter file\n\
00169 -m <output_matrix> specifies path to output GB energy matrix\n\
00170 -h or --help prints this help text\n\
00171 ----------------------------------------------------------------------\n\n"
00172
00173 return usage
00174
00175 def main():
00176
00177 main_timer_start = time.clock()
00178
00179 sdie = 0.0
00180
00181
00182 method = 0
00183 column = -1
00184
00185
00186 stdout.write(getHeader())
00187 try:
00188 opts, args = getopt.getopt(sys.argv[1:], "hp:i:o:m:", ["help"])
00189 except getopt.GetoptError:
00190 stdout.write("problem with input format\n")
00191 stdout.write(getUsage())
00192 sys.exit("Incorrect usage!\n")
00193
00194 output_param = ""
00195 output_matrix = ""
00196 for o, a in opts:
00197 if o in ("-h", "--help"):
00198 stdout.write("Printing help text...\n")
00199 stdout.write(getUsage())
00200 sys.exit()
00201 if o == "-p":
00202 if a == "":
00203 stdout.write("parameter file not specified\n")
00204 stdout.write(getUsage())
00205 sys.exit("Incorrect usage!\n")
00206 else:
00207 param_file = a
00208 if o == "-i":
00209 if a == "":
00210 stdout.write("input pqr file not specified\n")
00211 stdout.write(getUsage())
00212 sys.exit("Incorrect usage!\n")
00213 else:
00214 pqr_file = a
00215 if o == "-o":
00216 output_param = a
00217 if o == "-m":
00218 output_matrix = a
00219
00220
00221 try: param_file
00222 except:
00223 stdout.write("parameter file not initiated - check input\n")
00224 stdout.write(getUsage())
00225 sys.exit("Incorrect usage!\n")
00226 try: pqr_file
00227 except:
00228 stdout.write("input pqr file not initiated - check input\n")
00229 stdout.write(getUsage())
00230 sys.exit("Incorrect usage!\n")
00231
00232 stdout.write("Parsing parameter file %s...\n" % param_file)
00233 energylist = []
00234 bradlist = []
00235 try: f = open(param_file, 'r')
00236 except IOError:
00237 stdout.write("cannot open parameter file specified\n")
00238 sys.exit("IOError!\n")
00239 index = 0
00240 for line in f:
00241 term = line.split()
00242 if index == 0:
00243 if term[0].isdigit:
00244 sdie = float(term[0])
00245 else:
00246 stderr.write("main: Parameter format error; First line should be sdie only")
00247 raise APBSError, "Incorrect format!"
00248 elif index == 1:
00249 if len(term) == 1:
00250 if term[0] == "energy":
00251 method = 1
00252 elif len(term) == 2:
00253 if term[0] == "radii":
00254 column = 0
00255 else:
00256 column = 1
00257 else:
00258 stderr.write("main: Parameter format error; Second line should be field flags")
00259 raise APBSError, "Incorrect format!"
00260 else:
00261 if method == 1:
00262 if term[column].isdigit:
00263 energylist.append(float(term[column]))
00264 else:
00265 if term[column].isdigit:
00266 bradlist.append(float(term[column]))
00267 index = index + 1
00268 f.close()
00269
00270 if method:
00271 numAtoms = len(energylist)
00272 stdout.write("Parsed energy for %d atoms.\n" % numAtoms)
00273 else:
00274 numAtoms = len(bradlist)
00275 stdout.write("Parsed Born radii for %d atoms.\n" % numAtoms)
00276
00277 stdout.write("Parsing PQR file %s...\n" % pqr_file)
00278 position = [[0.0 for i in range(3)] for j in range(numAtoms)]
00279 x = []
00280 y = []
00281 z = []
00282 chargelist = []
00283 try: f = open(pqr_file, 'r+')
00284 except IOError:
00285 stdout.write("cannot open input pqr file specified\n")
00286 sys.exit("IOError!\n")
00287 iatom = 0
00288 for line in f:
00289 param = line.split()
00290 if param[0] != "ATOM":
00291 continue
00292 if len(param) == 10:
00293 x.append(float(param[5]))
00294 y.append(float(param[6]))
00295 z.append(float(param[7]))
00296 chargelist.append(float(param[8])*Python_C)
00297 iatom=iatom+1
00298 f.close()
00299 if len(chargelist) != numAtoms:
00300 stderr.write("main: Number of atoms in energy list (%d) doest not match PQR list (%d)\n" %
00301 (len(chargelist),numAtoms))
00302 raise APBSError, "Non-matching energy list and PQR file"
00303 for iatom in range(numAtoms):
00304 position[iatom][0]=x[iatom]
00305 position[iatom][1]=y[iatom]
00306 position[iatom][2]=z[iatom]
00307
00308 stdout.write("Input files parsed...\n")
00309
00310
00311 stdout.write("Starting energy calculations...\n")
00312 dij2 = [[0.0 for i in range(numAtoms)] for j in range(numAtoms)]
00313 fGB = [[0.0 for i in range(numAtoms)] for j in range(numAtoms)]
00314 if method:
00315 for i in xrange(numAtoms):
00316 brad = -pow(chargelist[i],2)*(1-1/sdie)*0.5*Python_Na/(4*pi*Python_e0*energylist[i]*1e3)
00317 bradlist.append(brad)
00318
00319 if output_param != "":
00320 stdout.write("writing parameter file to %s\n" % output_param)
00321 FILE = open(output_param, "w")
00322 FILE.write(str(sdie)+"\n")
00323 if method:
00324 FILE.write("radii\tenergy\n")
00325 parameters = zip(bradlist, energylist)
00326 for i in parameters:
00327 print >> FILE, "\t".join(map(str,i))
00328 else:
00329 FILE.write("radii\n")
00330 for i in bradlist:
00331 FILE.write(str(i)+"\n")
00332 FILE.close()
00333
00334 for i in xrange(numAtoms):
00335 for j in xrange(i+1):
00336
00337 for coord in xrange(3):
00338 dij2[i][j] = dij2[i][j] + pow((position[i][coord]-position[j][coord])*1e-10,2)
00339
00340 d = dij2[i][j]
00341 bradi = bradlist[i]
00342 bradj = bradlist[j]
00343 if j==i:
00344 fGB[i][j]=bradlist[i]
00345 else:
00346 fGB[i][j] = sqrt(d+bradi*bradj*exp(-d/(4.0*bradi*bradj)))
00347
00348
00349
00350 Gpol = 0.0
00351 for i in xrange(numAtoms):
00352 for j in xrange(numAtoms):
00353 if j < i:
00354 Gpol = Gpol + chargelist[i]*chargelist[j]/fGB[i][j]
00355 elif j > i:
00356 Gpol = Gpol + chargelist[i]*chargelist[j]/fGB[j][i]
00357
00358 for i in xrange(numAtoms):
00359 Gpol = Gpol + pow(chargelist[i],2)/bradlist[i]
00360
00361 Gpol = -Gpol*(1-1/sdie)*0.5*1e-3*Python_Na/(4*pi*Python_e0)
00362
00363
00364 stdout.write("\nGB Energy: %.10E kJ/mol\n" % Gpol)
00365
00366
00367 if output_matrix != "":
00368 FILE = open(filename, 'w')
00369 term = 0.0
00370 for i in range(numAtoms):
00371 for j in range(numAtoms):
00372 if j<=i:
00373 term = chargelist[i]*chargelist[j]/fGB[i][j]
00374 term = -term*(1-1/sdie)*0.5*1e-3*Python_Na/(4*pi*Python_e0)
00375 FILE.write(str(term)+"\t")
00376 else:
00377 term = chargelist[i]*chargelist[j]/fGB[j][i]
00378 term = -term*(1-1/sdie)*0.5*1e-3*Python_Na/(4*pi*Python_e0)
00379 FILE.write(str(term)+"\t")
00380 FILE.write("\n")
00381 FILE.close()
00382 stdout.write("Energy matrix output in %s\n" % filename)
00383
00384 stdout.write("\n")
00385 stdout.write("Thanks for using APBS!\n\n")
00386
00387
00388 main_timer_stop = time.clock()
00389 stdout.write("Total execution time: %1.6e sec\n" % (main_timer_stop - main_timer_start))
00390
00391
00392 if __name__ == "__main__": main()