3
# The purpose of this script is to create a moltemplate lt file for the oplsaa.
4
# forcefield. This will assist researchers in building complex simulations using
5
# this OPLS-UA and the OPLS-AA forcefields.
7
__author__="Jason Lambert"
8
# (some additional corrections by Miguel Gonzalez and Andrew Jewett)
13
from operator import itemgetter
15
g_program_name = __file__.split('/')[-1]
17
# To do that, first make a copy of the \"oplsaa.prm\" file
18
# (which can be downloaded from the TINKER web site).
19
# The lines in this file beginning with the word \"atoms\" should
20
# define the atoms which you plan to put in your simulation. All other
21
# lines beginning with the word \"atoms\" should be deleted.
22
# (Leave the other sections of this file alone.)
27
#input data from file containing opls aa force field parameters.
29
f=open(sys.argv[1],"r")
31
sys.stderr.write("Error: \n"
32
" You need to specify a file name as an input argument:\n"
33
" python oplsaa_moltemplate.py <forcefield file name>\n"
34
" (or the file name is specified incorrectly)\n")
38
sys.stderr.write(g_program_name+", version "+__version__+"\n"
39
"Reading parameter file...\n")
43
g=open("oplsaa.lt","w")
51
# Ignore/Comment out lines before the "## Atom Type Definitions ##" section.
53
for i in range(0, len(lines)):
54
if (lines[i].find("## Atom Type Definitions ##") != -1):
57
lines[i] = '# ' + lines[i]
60
# As of late 2014, there appear to be 906 atom types, but we don't assume this.
61
# First try to infer out how many atom types there were in the original
62
# oplsaa.prm file, or at least find an upper bound on the atom-type numbers.
63
# (Keep track of the maximum value of the first column in the "atom" section.)
67
# skip over text after a # comment character
70
line = (line[:ic]).strip()
73
# now look for lines beginning with the word "atom"
75
if ((len(tokens)>2) and (tokens[0] == "atom")):
77
if (int(tokens[1]) > max_atomType):
78
max_atomType = int(tokens[1])
80
if num_atomTypes > 25:
82
"(Note: If your computer freezes while running "+g_program_name+",\n"
83
" it could be because you forgot to edit the .prm file.\n"
84
" The original \"oplsaa.prm\" file distributed with TINKER has over 900 atom\n"
85
" types. If you run "+g_program_name+" on this file, it may freeze or\n"
86
" crash. Instead, run "+g_program_name+" on a SUBSET of the OPLS atoms\n"
87
" relevant to your problem. To do that, delete the lines from the .prm\n"
88
" file beginning with \"atom\" which you do not need.)\n\n")
90
#temporary storage file
91
h=open("temp.txt","w+")
92
atom_lookup={} #this dictionary contains all the atom ffid's as a key and the number of atoms with that key
93
#atom=[[10000,10000] for i in range(906)] <- don't assume there are 906 atoms
94
atom=[[-10000,-10000] for i in range(0,max_atomType+1)]
95
#charge_by_type={} # lookup charge by atom type
96
#vdw_by_type={} # lookup epsilon & sigma paramters by atom type
97
charge_by_type=[0.0 for i in range(0,max_atomType+1)] # lookup charge by atom
98
vdw_by_type=[(0.0,0.0) for i in range(0,max_atomType+1)] # lookup epsilon & sigma
102
#atom is declared this way so for sorting purposes.
103
#atom contains the following data upon allocation
104
#atom[][0]=atom_id( Important for partial charges and non_bonded interactions)
105
#atom[][1]=atom_ffid( Important for stretches, bending, torsions and impropers)
107
#atom[][3]=partial charge
108
#atom[][4]=non_bonding sigma
109
#atom[][5]=non_bonding epsilon
110
#atom[][6]=atom comment
112
#bond contains the following data
115
#bond[2]=bond spring constant(OPLS-aa compatible)
116
#bond[3]=equilibrium bond distance(Angstrom)
118
#angle contains the following data
119
#angle[0]=atom 1 ffid
120
#angle[1]=atom 2 ffid
121
#angle[2]=atom 3 ffid
122
#angle[3]=spring constant
123
#angle[4]=equilibrium angle (degrees)
125
#dihedral contains the following data
126
#dihedral[0]=atom 1 ffid
127
#dihedral[1]=atom 2 ffid
128
#dihedral[2]=atom 3 ffid
129
#dihedral[3]=atom 4 ffid
135
#improper[0]=atom 1 ffid
136
#improper[1]=atom 2 ffid(central atom)
137
#improper[2]=atom 3 ffid
138
#improper[3]=atom 4 ffid
139
#improper[4]=spring coefficient
140
#improper[5]=equilibrium angle
143
#This section gets all the parameters from the force field file
146
# skip over text after a # comment character
149
line = (line[:ic]).strip()
153
if line.find("atom") == 0:
155
atom[int(line[1])-1]=[int(line[1]),int(line[2]),float(line[-2]),
156
0.0,0.0,0.0," ".join(line[3:-2])]
157
elif line.find("vdw") == 0:
159
#vdw_temp.append([float(line[1]),float(line[2]),float(line[3])])
160
if (int(line[1]) <= max_atomType):
161
vdw_by_type[int(line[1])] = (float(line[2]),float(line[3]))
162
elif line.find("bond") == 0:
164
bond.append([int(line[1]),int(line[2]),float(line[3]),float(line[4])])
165
elif line.find("angle") == 0:
167
angle.append([int(line[1]),int(line[2]),int(line[3]),
168
float(line[4]),float(line[5])])
169
elif line.find("torsion") == 0:
171
dihedral.append([int(line[1]),int(line[2]),int(line[3]),int(line[4]),
172
float(line[5]),float(line[8]), float(line[11]), 0.0])
173
elif line.find("charge") == 0:
175
#charge_temp.append([int(line[1]),float(line[2])])
176
if (int(line[1]) <= max_atomType):
177
charge_by_type[int(line[1])] = float(line[2])
178
elif line.find("imptors") == 0:
180
improper.append([int(line[1]), int(line[2]),
181
int(line[3]), int(line[4]), float(line[5]), float(line[6])])
184
sys.stderr.write("WARNING: The number of atom types in your file exceeds 600\n"
185
" (You were supposed to edit out the atoms you don't need.\n"
186
" Not doing this may crash your computer.)\n"
189
reply = sys.stdin.readline()
190
if find(reply.strip().lower(), 'y') != 0:
193
#adding the charge and Lennard Jones parameters to
195
#----------------------------------------------#
196
for i in range(0,len(atom)):
197
atom_type_num = atom[i][0]
198
#q = charge_by_type.get(atomTypeNum)
201
if atom_type_num != -10000:
202
q = charge_by_type[atom_type_num]
205
for i in range(0,len(atom)):
206
atom_type_num = atom[i][0]
207
#vdw_params = vdw_by_type.get(atomTypeNum)
209
# atom[i][4] = vdw_params[0]
210
# atom[i][5] = vdw_params[1]
211
if atom_type_num != -10000:
212
vdw_params = vdw_by_type[atom_type_num]
213
atom[i][4] = vdw_params[0]
214
atom[i][5] = vdw_params[1]
219
#----------------------------------------------------------#
220
#begin writing content to lt file
221
g.write("OPLSAA {\n\n" )
223
#write out the atom masses
224
#----------------------------------------------------------#
225
g.write(" write_once(\"Data Masses\"){\n")#checked with gaff
226
for i,x in enumerate(atom):
228
g.write(" @atom:{} {} #{} partial charge={}\n".format(
229
x[0],x[2],x[6],x[3]))
230
g.write(" } #(end of atom masses)\n\n")
231
#----------------------------------------------------------#
234
#write out the pair coefficients
235
#----------------------------------------------------------#
236
g.write(" write_once(\"In Settings\"){\n")#checked with gaff
237
for i,x in enumerate(atom):
239
g.write(" pair_coeff @atom:{0} @atom:{0} lj/cut/coul/long {1} {2}\n".format(x[0],x[5],x[4]))
240
g.write(" } #(end of pair coeffs)\n\n")
242
g.write(" write_once(\"In Charges\"){\n")#checked with gaff
243
for i,x in enumerate(atom):
245
g.write(" set type @atom:{0} charge {1}\n".format(x[0],x[3]))
246
g.write(" } #(end of atom charges)\n\n")
248
#-----------------------------------------------------------#
250
# This part of the code creates a lookup dictionary
251
# that allows you to find every type of atom by its
252
# force field id. force field id is the id number
253
# relevant to bonds, angles, dihedrals, and impropers.
254
# This greatly increases the speed of angle, bond, dihedral
255
# and improper assignment.
256
#------------------------------------------------------------#
257
atom=sorted(atom,key=itemgetter(1))
261
atom_lookup[x[1]].append(x[0])
263
atom_lookup[x[1]]=[x[0]]
266
#-------------------------------------------------------------#
269
#writing out the bond coefficients and bond parameters#
270
#-------------------------------------------------------------#
271
g.write(" write_once(\"In Settings\") {\n")
274
for y in atom_lookup.get(x[0],[]):
275
for z in atom_lookup.get(x[1],[]):
276
#g.write(" bond_coeff @bond:{}-{} harmonic {} {}\n".format(y,z,x[2]/2,x[3]))
277
# Miguel Gonzales corrected this line to:
278
g.write(" bond_coeff @bond:{}-{} harmonic {} {}\n".format(y,z,x[2],x[3]))
279
h.write(" @bond:{0}-{1} @atom:{0} @atom:{1}\n".format(y,z))
280
g.write(" } #(end of bond_coeffs)\n\n")
282
g.write(" write_once(\"Data Bonds By Type\") {\n")
283
for line in h.readlines():
285
g.write(" } #(end of bonds by type)\n\n")
288
#-----------------------------------------------------------#
289
h=open("temp.txt","w+")
291
#writing out angle coefficients and angles by type.---------#
292
#-----------------------------------------------------------#
293
g.write(" write_once(\"Data Angles By Type\"){\n")
295
for y in atom_lookup.get(x[0],[]):
296
for z in atom_lookup.get(x[1],[]):
297
for u in atom_lookup.get(x[2],[]):
299
#h.write(" angle_coeff @angle:{}-{}-{} harmonic {} {}\n".format(y,z,u,x[3]/2.0,x[4]))
300
# Miguel Gonzales corrected this line:
301
h.write(" angle_coeff @angle:{}-{}-{} harmonic {} {}\n".format(y,z,u,x[3],x[4]))
302
g.write(" @angle:{0}-{1}-{2} @atom:{0} @atom:{1} @atom:{2}\n".format(y,z,u))
305
g.write(" } #(end of angles by type)\n\n")
307
g.write(" write_once(\"In Settings\" ){\n")
308
for line in h.readlines():
310
g.write(" } #(end of angle_coeffs)\n\n")
313
#----------------------------------------------------------#
315
#writing dihedrals by type and dihedral coefficients-------#
316
h=h=open("temp.txt","w+")
317
g.write(" write_once(\"Data Dihedrals By Type\") {\n")
320
for y in atom_lookup.get(x[0],[]):
321
for z in atom_lookup.get(x[1],[]):
322
for u in atom_lookup.get(x[2],[]):
323
for v in atom_lookup.get(x[3],[]):
324
if x[0]!=0 and x[3]!=0:
325
g.write(" @dihedral:{0}-{1}-{2}-{3} @atom:{0} @atom:{1} @atom:{2} @atom:{3}\n".format(
327
h.write(" dihedral_coeff @dihedral:{}-{}-{}-{} opls {} {} {} {}\n".format(
328
y,z,u,v,x[4],x[5],x[6],x[7]))
329
elif x[0]==0 and x[3]!=0:
330
g.write(" @dihedral:0-{1}-{2}-{3} @atom:{0} @atom:{1} @atom:{2} @atom:{3}\n".format(
332
h.write(" dihedral_coeff @dihedral:0-{}-{}-{} opls {} {} {} {}\n".format(
333
z,u,v,x[4],x[5],x[6],x[7]))
334
elif x[0]==0 and x[3]==0:
335
g.write(" @dihedral:0-{1}-{2}-0 @atom:{0} @atom:{1} @atom:{2} @atom:{3}\n".format(
337
#h.write(" dihedral_coeff @dihedral:0-{}-{}-0 harmonic {} {} {} {}\n".format(
338
h.write(" dihedral_coeff @dihedral:0-{}-{}-0 opls {} {} {} {}\n".format(
339
z,u,x[4],x[5],x[6],x[7]))
342
g.write(" } #(end of Dihedrals by type)\n\n")
344
g.write(" write_once(\"In Settings\") {\n")
345
for line in h.readlines():
347
g.write(" } #(end of dihedral_coeffs)\n\n")
349
#-----------------------------------------------------------------------#
351
#----writing out improper coefficients and impropers by type------------#
352
h=open("temp.txt","w+")
353
g.write(" write_once(\"Data Impropers By Type (opls_imp.py)\") {\n")
355
for y in atom_lookup.get(x[0],[]):
356
for z in atom_lookup.get(x[1],[]):
357
for u in atom_lookup.get(x[2],[]):
358
for v in atom_lookup.get(x[3],[]):
359
# Notation: let I,J,K,L denote the atom types ("biotypes")
360
# listed in the order they appear in the "oplsaa.prm" file.
361
# (I think J and L are represented by "u" and "v" in the code here.)
362
# It looks like the "oplsaa.prm" file distributed with tinker
363
# treats the third atom ("K") as the central atom.
364
# After checking the code, it appears that the improper angle is
365
# calculated as the angle between the I,J,K and the J,K,L planes
366
if x[0]==0 and x[1]==0 and x[3]==0:
367
g.write(" @improper:0-0-{2}-0 @atom:{0} @atom:{1} @atom:{2} @atom:{3}\n".format(y,z,u,v))
368
h.write(" improper_coeff @improper:0-0-{2}-0 harmonic {4} {5} \n".format(y,z,u,v,x[4]/2,180))
370
g.write(" @improper:0-0-{2}-{3} @atom:{0} @atom:{1} @atom:{2} @atom:{3}\n".format(y,z,u,v))
371
h.write(" improper_coeff @improper:0-0-{2}-{3} harmonic {4} {5} \n".format(y,z,u,v,x[4]/2,180))
374
g.write(" } #(end of impropers by type)\n\n")
376
g.write(" write_once(\"In Settings\") {\n")
377
for line in h.readlines():
379
g.write(" } #(end of improp_coeffs)\n\n")
380
#-----------------------------------------------------------------------#
382
#This section writes out the input parameters required for an opls-aa simulation
384
g.write(" write_once(\"In Init\") {\n")
385
g.write(" units real\n")
386
g.write(" atom_style full\n")
387
g.write(" bond_style hybrid harmonic\n")
388
g.write(" angle_style hybrid harmonic\n")
389
g.write(" dihedral_style hybrid opls\n")
390
g.write(" improper_style hybrid harmonic\n")
391
#g.write(" pair_style hybrid lj/cut/coul/cut 10.0 10.0\n")
392
g.write(" pair_style hybrid lj/cut/coul/long 10.0 10.0\n")
393
g.write(" pair_modify mix geometric\n")
394
g.write(" special_bonds lj/coul 0.0 0.0 0.5\n")
395
g.write(" kspace_style pppm 0.0001\n")
396
g.write(" } #end of init parameters\n\n")
397
g.write("} # OPLSAA\n")
401
os.remove("temp.txt")
404
sys.stderr.write("...finished.\n")