├── methanol.gro
├── em.mdp
├── ions.mdp
├── methanol.itp
├── nvt.mdp
├── npt.mdp
├── md.mdp
└── cgenff_charmm2gmx.py
/methanol.gro:
--------------------------------------------------------------------------------
1 | One methanol
2 | 6
3 | 1MET C 1 0.697 0.788 0.843
4 | 1MET H 2 0.600 0.829 0.873
5 | 1MET H 3 0.781 0.853 0.870
6 | 1MET H 4 0.706 0.773 0.736
7 | 1MET O 5 0.701 0.664 0.911
8 | 1MET H 6 0.761 0.600 0.875
9 | 1.20000 1.20000 1.20000
10 |
--------------------------------------------------------------------------------
/em.mdp:
--------------------------------------------------------------------------------
1 | ; em.mdp - used as input into grompp to generate em.tpr
2 | ; Parameters describing what to do, when to stop and what to save
3 | integrator = steep ; Algorithm (steep = steepest descent minimization)
4 | emtol = 1000.0 ; Stop minimization when the maximum force < 1000.0 kJ/mol/nm
5 | emstep = 0.01 ; Minimization step size
6 | nsteps = 50000 ; Maximum number of minimization steps to perform
7 |
8 | ; Parameters describing how to find the neighbors of each atom and how to calculate the interactions
9 | nstlist = 1 ; Frequency to update the neighbor list and long range forces
10 | cutoff-scheme = Verlet ; Buffered neighbor searching
11 | ns_type = grid ; Method to determine neighbor list (simple, grid)
12 | coulombtype = PME ; Treatment of long range electrostatic interactions
13 | rcoulomb = 1.0 ; Short-range electrostatic cut-off
14 | rvdw = 1.0 ; Short-range Van der Waals cut-off
15 | pbc = xyz ; Periodic Boundary Conditions in all 3 dimensions
16 |
--------------------------------------------------------------------------------
/ions.mdp:
--------------------------------------------------------------------------------
1 | ; ions.mdp - used as input to grompp to generate ions.tpr
2 | ; Parameters describing what to do, when to stop, and what to save
3 | integrator = steep ; Algorithm (steep = steepest descent minimization)
4 | emtol = 1000.0 ; Stop minimization when the maximum force < 1000.0 kJ/mol/nm
5 | emstep = 0.01 ; Minimization step size
6 | nsteps = 50000 ; Maximum number of (minimization) steps to perform
7 |
8 | ; Parameters describing how to find the neighbors of each atom and how to calculate the interactions
9 | nstlist = 1 ; Frequency to update the neighbor list and long range forces
10 | cutoff-scheme = Verlet ; Buffered neighbor searching
11 | ns_type = grid ; Method to determine neighbor list (simple, grid)
12 | coulombtype = cutoff ; Treatment of long range electrostatic interactions
13 | rcoulomb = 1.0 ; Short-range electrostatic cut-off
14 | rvdw = 1.0 ; Short-range Van der Waals cut-off
15 | pbc = xyz ; Periodic Boundary Conditions in all 3 dimensions to minimize edge effects in a finite system
16 |
--------------------------------------------------------------------------------
/methanol.itp:
--------------------------------------------------------------------------------
1 | ; This include topology was generated for the paper below:
2 | ; Carl Caleman, Paul J. van Maaren, Minyan Hong, Jochen S. Hub,
3 | ; Luciano T. Costa and David van der Spoel
4 | ; Force Field Benchmark of Organic Liquids: Density,
5 | ; Enthalpy of Vaporization, Heat Capacities, Surface Tension,
6 | ; Isothermal Compressibility, Volumetric Expansion Coefficient,
7 | ; and Dielectric Constant
8 | ; J. Chem. Theor. Comput. 8 (2012) 61-74
9 | ; http://dx.doi.org/10.1021/ct200731v
10 | ; REFERENCE Caleman2012a
11 | ; CORRECTED DvdS 2011-03-02
12 |
13 | [ moleculetype ]
14 | ; Name nrexcl
15 | MET 3
16 |
17 | [ atoms ]
18 | ; nr type resnr residue atom cgnr charge mass typeB chargeB massB
19 | 1 opls_157 1 MOL C 1 0.145 12.011
20 | 2 opls_156 1 MOL H 2 0.04 1.008
21 | 3 opls_156 1 MOL H 3 0.04 1.008
22 | 4 opls_156 1 MOL H 4 0.04 1.008
23 | 5 opls_154 1 MOL O 5 -0.683 15.9994
24 | 6 opls_155 1 MOL H 6 0.418 1.008
25 |
26 | [ bonds ]
27 | ; ai aj funct c0 c1 c2 c3
28 | 1 2 1
29 | 1 3 1
30 | 1 4 1
31 | 1 5 1
32 | 5 6 1
33 |
34 | [ pairs ]
35 | ; ai aj funct c0 c1 c2 c3
36 | 2 6 1
37 | 3 6 1
38 | 4 6 1
39 |
40 | [ angles ]
41 | ; ai aj ak funct c0 c1 c2 c3
42 | 2 1 3 1
43 | 2 1 4 1
44 | 2 1 5 1
45 | 3 1 4 1
46 | 3 1 5 1
47 | 4 1 5 1
48 | 1 5 6 1
49 |
50 | [ dihedrals ]
51 | ; ai aj ak al funct c0 c1 c2 c3 c4 c5
52 | 2 1 5 6 3
53 | 3 1 5 6 3
54 | 4 1 5 6 3
55 |
56 |
--------------------------------------------------------------------------------
/nvt.mdp:
--------------------------------------------------------------------------------
1 | title = OPLS Lysozyme NVT equilibration
2 | define = -DPOSRES ; position restrain the protein
3 | ; Run parameters
4 | integrator = md ; leap-frog integrator
5 | nsteps = 50000 ; 2 * 50000 = 100 ps
6 | dt = 0.002 ; 2 fs
7 | ; Output control
8 | nstxout = 500 ; save coordinates every 1.0 ps
9 | nstvout = 500 ; save velocities every 1.0 ps
10 | nstenergy = 500 ; save energies every 1.0 ps
11 | nstlog = 500 ; update log file every 1.0 ps
12 | ; Bond parameters
13 | continuation = no ; first dynamics run
14 | constraint_algorithm = lincs ; holonomic constraints
15 | constraints = h-bonds ; bonds involving H are constrained
16 | lincs_iter = 1 ; accuracy of LINCS
17 | lincs_order = 4 ; also related to accuracy
18 | ; Nonbonded settings
19 | cutoff-scheme = Verlet ; Buffered neighbor searching
20 | ns_type = grid ; search neighboring grid cells
21 | nstlist = 10 ; 20 fs, largely irrelevant with Verlet
22 | rcoulomb = 1.0 ; short-range electrostatic cutoff (in nm)
23 | rvdw = 1.0 ; short-range van der Waals cutoff (in nm)
24 | DispCorr = EnerPres ; account for cut-off vdW scheme
25 | ; Electrostatics
26 | coulombtype = PME ; Particle Mesh Ewald for long-range electrostatics
27 | pme_order = 4 ; cubic interpolation
28 | fourierspacing = 0.16 ; grid spacing for FFT
29 | ; Temperature coupling is on
30 | tcoupl = V-rescale ; modified Berendsen thermostat
31 | tc-grps = Protein Non-Protein ; two coupling groups - more accurate
32 | tau_t = 0.1 0.1 ; time constant, in ps
33 | ref_t = 300 300 ; reference temperature, one for each group, in K
34 | ; Pressure coupling is off
35 | pcoupl = no ; no pressure coupling in NVT
36 | ; Periodic boundary conditions
37 | pbc = xyz ; 3-D PBC
38 | ; Velocity generation
39 | gen_vel = yes ; assign velocities from Maxwell distribution
40 | gen_temp = 300 ; temperature for Maxwell distribution
41 | gen_seed = -1 ; generate a random seed
42 |
--------------------------------------------------------------------------------
/npt.mdp:
--------------------------------------------------------------------------------
1 | title = OPLS Lysozyme NPT equilibration
2 | define = -DPOSRES ; position restrain the protein
3 | ; Run parameters
4 | integrator = md ; leap-frog integrator
5 | nsteps = 50000 ; 2 * 50000 = 100 ps
6 | dt = 0.002 ; 2 fs
7 | ; Output control
8 | nstxout = 500 ; save coordinates every 1.0 ps
9 | nstvout = 500 ; save velocities every 1.0 ps
10 | nstenergy = 500 ; save energies every 1.0 ps
11 | nstlog = 500 ; update log file every 1.0 ps
12 | ; Bond parameters
13 | continuation = yes ; Restarting after NVT
14 | constraint_algorithm = lincs ; holonomic constraints
15 | constraints = h-bonds ; bonds involving H are constrained
16 | lincs_iter = 1 ; accuracy of LINCS
17 | lincs_order = 4 ; also related to accuracy
18 | ; Nonbonded settings
19 | cutoff-scheme = Verlet ; Buffered neighbor searching
20 | ns_type = grid ; search neighboring grid cells
21 | nstlist = 10 ; 20 fs, largely irrelevant with Verlet scheme
22 | rcoulomb = 1.0 ; short-range electrostatic cutoff (in nm)
23 | rvdw = 1.0 ; short-range van der Waals cutoff (in nm)
24 | DispCorr = EnerPres ; account for cut-off vdW scheme
25 | ; Electrostatics
26 | coulombtype = PME ; Particle Mesh Ewald for long-range electrostatics
27 | pme_order = 4 ; cubic interpolation
28 | fourierspacing = 0.16 ; grid spacing for FFT
29 | ; Temperature coupling is on
30 | tcoupl = V-rescale ; modified Berendsen thermostat
31 | tc-grps = Protein Non-Protein ; two coupling groups - more accurate
32 | tau_t = 0.1 0.1 ; time constant, in ps
33 | ref_t = 300 300 ; reference temperature, one for each group, in K
34 | ; Pressure coupling is on
35 | pcoupl = Parrinello-Rahman ; Pressure coupling on in NPT
36 | pcoupltype = isotropic ; uniform scaling of box vectors
37 | tau_p = 2.0 ; time constant, in ps
38 | ref_p = 1.0 ; reference pressure, in bar
39 | compressibility = 4.5e-5 ; isothermal compressibility of water, bar^-1
40 | refcoord_scaling = com
41 | ; Periodic boundary conditions
42 | pbc = xyz ; 3-D PBC
43 | ; Velocity generation
44 | gen_vel = no ; Velocity generation is off
45 |
--------------------------------------------------------------------------------
/md.mdp:
--------------------------------------------------------------------------------
1 | title = OPLS Lysozyme NPT equilibration
2 | ; Run parameters
3 | integrator = md ; leap-frog integrator
4 | nsteps = 500000 ; 0.002 * 500000 = 1000 ps (1 ns)
5 | dt = 0.002 ; 2 fs
6 | ; Output control
7 | nstxout = 0 ; suppress bulky .trr file by specifying
8 | nstvout = 0 ; 0 for output frequency of nstxout,
9 | nstfout = 0 ; nstvout, and nstfout
10 | nstenergy = 5000 ; save energies every 10.0 ps
11 | nstlog = 5000 ; update log file every 10.0 ps
12 | nstxout-compressed = 5000 ; save compressed coordinates every 10.0 ps
13 | compressed-x-grps = System ; save the whole system
14 | ; Bond parameters
15 | continuation = yes ; Restarting after NPT
16 | constraint_algorithm = lincs ; holonomic constraints
17 | constraints = h-bonds ; bonds involving H are constrained
18 | lincs_iter = 1 ; accuracy of LINCS
19 | lincs_order = 4 ; also related to accuracy
20 | ; Neighborsearching
21 | cutoff-scheme = Verlet ; Buffered neighbor searching
22 | ns_type = grid ; search neighboring grid cells
23 | nstlist = 10 ; 20 fs, largely irrelevant with Verlet scheme
24 | rcoulomb = 1.0 ; short-range electrostatic cutoff (in nm)
25 | rvdw = 1.0 ; short-range van der Waals cutoff (in nm)
26 | ; Electrostatics
27 | coulombtype = PME ; Particle Mesh Ewald for long-range electrostatics
28 | pme_order = 4 ; cubic interpolation
29 | fourierspacing = 0.16 ; grid spacing for FFT
30 | ; Temperature coupling is on
31 | tcoupl = V-rescale ; modified Berendsen thermostat
32 | tc-grps = Protein Non-Protein ; two coupling groups - more accurate
33 | tau_t = 0.1 0.1 ; time constant, in ps
34 | ref_t = 300 300 ; reference temperature, one for each group, in K
35 | ; Pressure coupling is on
36 | pcoupl = Parrinello-Rahman ; Pressure coupling on in NPT
37 | pcoupltype = isotropic ; uniform scaling of box vectors
38 | tau_p = 2.0 ; time constant, in ps
39 | ref_p = 1.0 ; reference pressure, in bar
40 | compressibility = 4.5e-5 ; isothermal compressibility of water, bar^-1
41 | ; Periodic boundary conditions
42 | pbc = xyz ; 3-D PBC
43 | ; Dispersion correction
44 | DispCorr = EnerPres ; account for cut-off vdW scheme
45 | ; Velocity generation
46 | gen_vel = no ; Velocity generation is off
47 |
--------------------------------------------------------------------------------
/cgenff_charmm2gmx.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # USAGE: ./cgenff_charmm2gmx.py DRUG drug.mol2 drug.str charmm36.ff
4 | # Tested with Python 2.7.3. Requires numpy and networkx
5 |
6 | # Copyright (C) 2014 E. Prabhu Raman prabhu@outerbanks.umaryland.edu
7 | #
8 | # For help/questions/bug reports, please contact Justin Lemkul jalemkul@outerbanks.umaryland.edu
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU Affero General Public License as
12 | # published by the Free Software Foundation, either version 3 of the
13 | # License, or (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU Affero General Public License for more details.
19 | #
20 |
21 |
22 | # EXAMPLE: You have a drug-like molecule in drug.mol2 file
23 | # ParamChem returns a CHARMM stream file drug.str with topology and parameters
24 | # INPUT
25 | # The program needs four inputs:
26 | # (1) The first argument (resname) is found in the RESI entry in the CHARMM stream file; for example
27 | # RESI DRUG 0.000 ! here DRUG is the resname
28 | # (2) drug.mol2 is the .mol2 which you supplied to ParamChem
29 | # (3) drug.str contains the topology entry and CGenFF parameters that ParamChem generates for you
30 | # (4) charmm36.ff should contain the CHARMM force-field in GROMACS format
31 | # Download it from: http://mackerell.umaryland.edu/CHARMM_ff_params.html
32 |
33 | # OUTPUT
34 | # The program will generate 4 output files ("DRUG" is converted to lowercase and the files are named accordingly):
35 | # (1) drug.itp - contains GROMACS itp
36 | # (2) drug.prm - contains parameters obtained from drug.str which are converted to GROMACS format and units
37 | # (3) drug.top - A Gromacs topology file which incorporates (1) and (2)
38 | # (4) drug_ini.pdb - Coordinates of the molecule obtained from drug.mol2
39 |
40 | # The program has been tested only on CHARMM stream files containing topology and parameters of a single molecule.
41 |
42 | import string
43 | import re
44 | import sys
45 | import os
46 | import numpy as np
47 | import networkx as nx
48 |
49 | #=================================================================================================================
50 | def check_versions(str_filename,ffdoc_filename):
51 | ffver = 0 # CGenFF version in force field directory
52 | strver = 0 # CGenFF version in stream file
53 | f = open(str_filename, 'r')
54 | for line in f.readlines():
55 | if line.startswith("* For use with CGenFF version"):
56 | entry = re.split('\s+', string.lstrip(line))
57 | strver = entry[6]
58 | print "--Version of CGenFF detected in ",str_filename,":",strver
59 | f.close()
60 | f = open(ffdoc_filename, 'r')
61 | for line in f.readlines():
62 | if line.startswith("Parameters taken from CHARMM36 and CGenFF"):
63 | entry = re.split('\s+', string.lstrip(line))
64 | ffver = entry[6]
65 | print "--Version of CGenFF detected in ",ffdoc_filename,":",ffver
66 | f.close()
67 |
68 | # warn the user about version mismatch
69 | if strver != ffver:
70 | print "\nWARNING: CGenFF versions are not equivalent!\n"
71 |
72 | # in case something has gone horribly wrong
73 | if (strver == 0) or (ffver == 0):
74 | print "\nERROR: Could not detect CGenFF version. Exiting.\n"
75 | exit()
76 |
77 | #-----------------------------------------------------------------------
78 | def read_gmx_atomtypes(filename):
79 | atomtypes = []
80 | f = open(filename, 'r')
81 | for line in f.readlines():
82 | if line.startswith(";"):
83 | continue
84 | if line == '\n':
85 | continue
86 | entry = re.split('\s+', string.lstrip(line))
87 | var = [entry[0],entry[1]]
88 | atomtypes.append(var)
89 | f.close()
90 | return atomtypes
91 | #-----------------------------------------------------------------------
92 | def get_filelist_from_gmx_forcefielditp(ffdir,ffparentfile):
93 | filelist=[]
94 | f = open(ffdir+"/"+ffparentfile, 'r')
95 | for line in f.readlines():
96 | if line.startswith("#include"):
97 | entry = re.split('\s+', string.lstrip(line))
98 | filename = ffdir + "/" + entry[1].replace("\"","")
99 | filelist.append(filename)
100 | return filelist
101 | #-----------------------------------------------------------------------
102 | def read_gmx_anglpars(filename):
103 |
104 | angllines = []
105 | f = open(filename, 'r')
106 | section="NONE"
107 | for line in f.readlines():
108 | if line.startswith(";"):
109 | continue
110 | if line.startswith("\n"):
111 | continue
112 | if line.startswith("["):
113 | section="NONE"
114 | if(section=="ANGL"):
115 | angllines.append(line)
116 | if line.startswith("[ angletypes ]"):
117 | section="ANGL"
118 |
119 | anglpars = []
120 | anglpar = {}
121 | for line in angllines:
122 | entry = re.split('\s+', string.lstrip(line))
123 | ai, aj, ak, eq = entry[0],entry[1],entry[2],float(entry[4])
124 | anglpars.append([ai,aj,ak,eq])
125 |
126 | return anglpars
127 | #-----------------------------------------------------------------------
128 | def get_charmm_rtp_lines(filename,molname):
129 | foundmol=0
130 | store=0
131 | rtplines=[]
132 | f = open(filename, 'r')
133 | section="NONE"
134 | for line in f.readlines():
135 | if(store==1) and line.startswith("RESI"):
136 | store=0
137 |
138 | if line.startswith("RESI"):
139 | entry = re.split('\s+', string.lstrip(line))
140 | rtfmolname=entry[1]
141 | if(rtfmolname == molname):
142 | store=1
143 |
144 | if line.startswith("END"):
145 | store=0
146 |
147 | if(store==1):
148 | rtplines.append(line)
149 |
150 | return rtplines
151 | #-----------------------------------------------------------------------
152 | def get_charmm_prm_lines(filename):
153 | foundmol=0
154 | store=0
155 | prmlines=[]
156 | f = open(filename, 'r')
157 | section="NONE"
158 | for line in f.readlines():
159 |
160 | if line.startswith("END"):
161 | section="NONE"
162 | store=0
163 |
164 | if(store):
165 | prmlines.append(line)
166 |
167 | if line.startswith("read para"):
168 | section="PRM"
169 | store=1
170 |
171 |
172 | return prmlines
173 | #-----------------------------------------------------------------------
174 | def parse_charmm_topology(rtplines):
175 | topology = {}
176 | noblanks = filter(lambda x: len(x.strip())>0, rtplines)
177 | nocomments = filter(lambda x: x.strip()[0] not in ['*','!'], noblanks)
178 | section = "BONDS" # default
179 | state = "free"
180 | for line in nocomments:
181 | if state == "free":
182 | if line.find("MASS") == 0:
183 | if "ATOMS" not in topology.keys():
184 | topology["ATOMS"] = {}
185 | s = line.split()
186 | idx,name,mass,type = int(s[1]),s[2],float(s[3]),s[4]
187 | if line.find("!"):
188 | comment = line[line.find("!")+1:].strip()
189 | else:
190 | comment = ""
191 | topology["ATOMS"][name] = [idx,mass,type,comment]
192 | elif line.find("DECL") == 0:
193 | if "DECL" not in topology.keys():
194 | topology["DECL"] = []
195 | decl = line.split()[1]
196 | topology["DECL"].append(decl)
197 | elif line.find("DEFA") == 0:
198 | topology["DEFA"] = line[4:]
199 | elif line.find("AUTO") == 0:
200 | topology["AUTO"] = line[4:]
201 | elif line.find("RESI") == 0:
202 | if "RESI" not in topology.keys():
203 | topology["RESI"] = {}
204 | state = "resi"
205 | s = line.split()
206 | resname, charge = s[1],float(s[2])
207 | topology["RESI"][resname] = {}
208 | topology["RESI"][resname]["charge"] = charge
209 | topology["RESI"][resname]["cmaps"] = []
210 | topology["RESI"][resname]["bonds"] = []
211 | topology["RESI"][resname]["impropers"] = []
212 | topology["RESI"][resname]["double_bonds"] = []
213 | group = -1
214 | elif line.find("PRES") == 0:
215 | state = "pres"
216 | s = line.split()
217 | presname, charge = s[1],float(s[2])
218 | elif line.find("END") == 0:
219 | return topology
220 | elif state == "resi":
221 | if line.find("RESI")==0:
222 | state = "resi"
223 | s = line.split()
224 | resname, charge = s[1],float(s[2])
225 | topology["RESI"][resname] = {}
226 | topology["RESI"][resname]["charge"] = charge
227 | topology["RESI"][resname]["cmaps"] = []
228 | topology["RESI"][resname]["bonds"] = []
229 | topology["RESI"][resname]["impropers"] = []
230 | topology["RESI"][resname]["double_bonds"] = []
231 | #topology["RESI"][resname]["groups"] = []
232 | group = -1
233 |
234 | elif line.find("GROU")==0:
235 | group += 1
236 | topology["RESI"][resname][group] = []
237 | elif line.find("ATOM")==0:
238 | if line.find('!'):
239 | line = line[:line.find('!')]
240 | s = line.split()
241 | name,type,charge = s[1],s[2],float(s[3])
242 | topology["RESI"][resname][group].append((name,type,charge))
243 | elif line.find("BOND")==0:
244 | if line.find('!'):
245 | line = line[:line.find('!')]
246 | s = line.split()
247 | nbond = (len(s)-1)/2
248 | for i in range(nbond):
249 | p,q = s[1+2*i],s[2+2*i]
250 | topology["RESI"][resname]["bonds"].append((p,q))
251 | elif line.find("DOUB")==0:
252 | if line.find('!'):
253 | line = line[:line.find('!')]
254 | s = line.split()
255 | ndouble = (len(s)-1)/2
256 | for i in range(ndouble):
257 | p,q = s[1+2*i],s[2+2*i]
258 | topology["RESI"][resname]["double_bonds"].append((p,q))
259 | elif line.find("IMPR")==0:
260 | if line.find('!'):
261 | line = line[:line.find('!')]
262 | s = line.split()
263 | nimproper = (len(s)-1)/4
264 | for i in range(nimproper):
265 | impr = s[1+4*i],s[2+4*i],s[3+4*i],s[4+4*i]
266 | topology["RESI"][resname]["impropers"].append(impr)
267 | elif line.find("CMAP")==0:
268 | if line.find('!'):
269 | line = line[:line.find('!')]
270 | s = line.split()
271 | #nimproper = (len(s)-1)/4
272 | #for i in range(nimproper):
273 | cmap = s[1:9]
274 | topology["RESI"][resname]["cmaps"].append(cmap)
275 | elif line.find("DONOR")==0:
276 | continue # ignore for now
277 | elif line.find("ACCEPTOR")==0:
278 | continue
279 | elif line.find("IC")==0:
280 | continue
281 |
282 | return topology
283 | #-----------------------------------------------------------------------
284 | def parse_charmm_parameters(prmlines):
285 |
286 | parameters = {}
287 | cmapkey = ()
288 | noblanks = filter(lambda x: len(x.strip())>0, prmlines)
289 | nocomments = filter(lambda x: x.strip()[0] not in ['*','!'], noblanks)
290 | section = "ATOM" # default
291 | for line in nocomments:
292 | #print line
293 | sectionkeys = [ "BOND", "ANGL", "DIHE", \
294 | "IMPR", "CMAP", "NONB", "HBON", "NBFI" ]
295 | key = line.split()[0]
296 |
297 | #exit()
298 |
299 | if key[0:4] in sectionkeys:
300 | section = key[0:4]
301 | continue
302 |
303 | if section not in parameters.keys():
304 | parameters[section] = []
305 |
306 | #print line
307 | if section == "BOND":
308 | if line.find('!'):
309 | line = line[:line.find('!')]
310 | s = line.split()
311 | ai, aj, kij, rij = s[0],s[1],float(s[2]),float(s[3])
312 | parameters["BOND"].append((ai,aj,kij,rij))
313 | elif section == "ANGL":
314 | if line.find('!'):
315 | line = line[:line.find('!')]
316 | s = line.split()
317 | ai, aj, ak = s[0],s[1],s[2]
318 | other = map(float,s[3:])
319 | parameters["ANGL"].append([ai,aj,ak]+other)
320 | elif section == "DIHE":
321 | if line.find('!'):
322 | line = line[:line.find('!')]
323 | s = line.split()
324 | ai, aj, ak, al, k, n, d = s[0],s[1],s[2],s[3],float(s[4]),int(s[5]),float(s[6])
325 | parameters["DIHE"].append([ai,aj,ak,al,k,n,d])
326 | elif section == "IMPR":
327 | if line.find('!'):
328 | line = line[:line.find('!')]
329 | s = line.split()
330 | ai, aj, ak, al, k, d = s[0],s[1],s[2],s[3],float(s[4]),float(s[6])
331 | parameters["IMPR"].append([ai,aj,ak,al,k,d])
332 | elif section == "CMAP":
333 | if line.find('!'):
334 | line = line[:line.find('!')]
335 | if cmapkey == ():
336 | s = line.split()
337 | a,b,c,d,e,f,g,h = s[0:8]
338 | N = int(s[8])
339 | cmapkey = (a,b,c,d,e,f,g,h,N)
340 | cmaplist = []
341 | else:
342 | cmapdata = map(float,line.split())
343 | cmaplist += cmapdata
344 | if len(cmaplist) == N**2:
345 | parameters["CMAP"].append([cmapkey,cmaplist])
346 | cmapkey = ()
347 | elif section == "NONB":
348 | if line.find("cutnb")>=0 or line.find("wmin")>=0 or line.find("CUTNB")>=0 or line.find("WMIN")>=0 :
349 | continue
350 | bang = line.find('!')
351 | if bang>0:
352 | comment = line[bang+1:]
353 | prm = line[:bang].split()
354 | else:
355 | comment = ""
356 | prm = line.split()
357 | atname = prm[0]
358 | epsilon = -float(prm[2])
359 | half_rmin = float(prm[3])
360 | parameters["NONB"].append((atname,epsilon,half_rmin))
361 |
362 | if len(prm)>4:
363 | epsilon14 = -float(prm[5])
364 | half_rmin14 = float(prm[6])
365 | if "NONBONDED14" not in parameters.keys():
366 | parameters["NONBONDED14"] = []
367 | parameters["NONBONDED14"].append((atname,epsilon14,half_rmin14))
368 |
369 |
370 | return parameters
371 | #-----------------------------------------------------------------------
372 | def write_gmx_bon(parameters,header_comments,filename):
373 | kcal2kJ = 4.18400
374 |
375 | outp = open(filename,"w")
376 | outp.write("%s\n"%(header_comments))
377 | outp.write("[ bondtypes ]\n")
378 | kbond_conversion = 2.0*kcal2kJ/(0.1)**2 # [kcal/mol]/A**2 -> [kJ/mol]/nm**2
379 | # factor of 0.5 because charmm bonds are Eb(r)=Kb*(r-r0)**2
380 | rbond_conversion = .1 # A -> nm
381 | outp.write(";%7s %8s %5s %12s %12s\n"%("i","j","func","b0","kb"))
382 | if(parameters.has_key("BOND")):
383 | for p in parameters["BOND"]:
384 | ai,aj,kij,rij = p
385 | rij *= rbond_conversion
386 | kij *= kbond_conversion
387 | outp.write("%8s %8s %5i %12.8f %12.2f\n"%(ai,aj,1,rij,kij))
388 |
389 | kangle_conversion = 2.0*kcal2kJ # [kcal/mol]/rad**2 -> [kJ/mol]/rad**2
390 | # factor of 0.5 because charmm angles are Ea(r)=Ka*(a-a0)**2
391 | kub_conversion = 2.0*kcal2kJ/(0.1)**2 # [kcal/mol]/A**2 -> [kJ/mol]/nm**2prm
392 | ub0_conversion = 0.1 # A -> nm
393 |
394 | outp.write("\n\n[ angletypes ]\n")
395 | outp.write(";%7s %8s %8s %5s %12s %12s %12s %12s\n"\
396 | %("i","j","k","func","theta0","ktheta","ub0","kub"))
397 | if(parameters.has_key("ANGL")):
398 | for p in parameters["ANGL"]:
399 | if len(p) == 5:
400 | ai,aj,ak,kijk,theta = p
401 | kub = 0.0
402 | ub0 = 0.0
403 | else:
404 | ai,aj,ak,kijk,theta,kub,ub0 = p
405 |
406 | kijk *= kangle_conversion
407 | kub *= kub_conversion
408 | ub0 *= ub0_conversion
409 | outp.write("%8s %8s %8s %5i %12.6f %12.6f %12.8f %12.2f\n"\
410 | %(ai,aj,ak,5,theta,kijk,ub0,kub))
411 |
412 | kdihe_conversion = kcal2kJ
413 | outp.write("\n\n[ dihedraltypes ]\n")
414 | outp.write(";%7s %8s %8s %8s %5s %12s %12s %5s\n"\
415 | %("i","j","k","l","func","phi0","kphi","mult"))
416 | #parameters["DIHEDRALS"].sort(demote_wildcards)
417 | if(parameters.has_key("DIHE")):
418 | for p in parameters["DIHE"]:
419 | ai,aj,ak,al,k,n,d = p
420 | k *= kdihe_conversion
421 | outp.write("%8s %8s %8s %8s %5i %12.6f %12.6f %5i\n"\
422 | %(ai,aj,ak,al,9,d,k,n))
423 |
424 | kimpr_conversion = kcal2kJ*2 # see above
425 | outp.write("\n\n[ dihedraltypes ]\n")
426 | outp.write("; 'improper' dihedrals \n")
427 | outp.write(";%7s %8s %8s %8s %5s %12s %12s\n"\
428 | %("i","j","k","l","func","phi0","kphi"))
429 | if(parameters.has_key("IMPR")):
430 | #parameters["IMPROPERS"].sort(demote_wildcards)
431 | for p in parameters["IMPR"]:
432 | ai,aj,ak,al,k,d = p
433 | k *= kimpr_conversion
434 | outp.write("%8s %8s %8s %8s %5i %12.6f %12.6f\n"\
435 | %(ai,aj,ak,al,2,d,k))
436 |
437 | outp.close()
438 | return
439 | #-----------------------------------------------------------------------
440 | def write_gmx_mol_top(filename,ffdir,prmfile,itpfile,molname):
441 | outp = open(filename,"w")
442 | outp.write("#include \"%s/forcefield.itp\"\n" % (ffdir))
443 | outp.write("\n")
444 | outp.write("; additional params for the molecule\n")
445 | outp.write("#include \"%s\"\n" % (prmfile))
446 | outp.write("\n")
447 | outp.write("#include \"%s\"\n" % (itpfile))
448 | outp.write("\n")
449 | outp.write("#include \"%s/tip3p.itp\"\n" % (ffdir))
450 | outp.write("#ifdef POSRES_WATER\n")
451 | outp.write("; Position restraint for each water oxygen\n")
452 | outp.write("[ position_restraints ]\n")
453 | outp.write("; i funct fcx fcy fcz\n")
454 | outp.write(" 1 1 1000 1000 1000\n")
455 | outp.write("#endif\n")
456 | outp.write("\n")
457 | outp.write("; Include topology for ions\n")
458 | outp.write("#include \"%s/ions.itp\"\n" % (ffdir))
459 | outp.write("\n")
460 | outp.write("[ system ]\n")
461 | outp.write("; Name\n")
462 | outp.write("mol\n")
463 | outp.write("\n")
464 | outp.write("[ molecules ]\n")
465 | outp.write("; Compound #mols\n")
466 | outp.write("%s 1\n" % (molname))
467 | outp.write("\n")
468 |
469 | outp.close()
470 | #=================================================================================================================
471 | class atomgroup:
472 | """
473 | A class that contains the data structures and functions to store and process
474 | data related to groups of atoms (read molecules)
475 |
476 | USAGE: m = atomgroup()
477 | """
478 |
479 | #TODO, remove this molcount, it is not useful and is confusing
480 | molcount=0 #counts the number of molecules in the group
481 |
482 | def __init__(self):
483 | self.molcount = self.molcount+1 #TODO
484 | self.G = nx.Graph()
485 | self.name = ""
486 | self.natoms = 0
487 | self.nbonds = 0
488 | self.angles = []
489 | self.nangles = 0
490 | self.dihedrals = []
491 | self.ndihedrals = 0
492 | self.impropers = []
493 | self.nimpropers = 0
494 | #self.coord=np.zeros((self.natoms,3),dtype=float)
495 |
496 | #-----------------------------------------------------------------------
497 | def read_charmm_rtp(self,rtplines,atomtypes):
498 | """
499 | Reads CHARMM rtp
500 | Reads atoms, bonds, impropers
501 | Stores connectivity as a graph
502 | Autogenerates angles and dihedrals
503 |
504 | USAGE: m = atomgroup() ; m.read_charmm_rtp(rtplines,atomtypes)
505 |
506 | """
507 | #initialize everything
508 | self.G = nx.Graph()
509 | self.name = ""
510 | self.natoms = 0
511 | self.nbonds = 0
512 | self.angles = []
513 | self.nangles = 0
514 | self.dihedrals = []
515 | self.ndihedrals = 0
516 | self.impropers = []
517 | self.nimpropers = 0
518 |
519 | atm = {}
520 |
521 | for line in rtplines:
522 | if line.find('!'):
523 | line = line[:line.find('!')]
524 |
525 | if line.startswith("RESI"):
526 | entry = re.split('\s+', string.lstrip(line))
527 | self.name=entry[1]
528 |
529 | if line.startswith("ATOM"):
530 | entry = re.split('\s+', string.lstrip(line))
531 | atm[self.natoms] = {'type':entry[2], 'resname':self.name, 'name':entry[1],
532 | 'charge':float(entry[3]),'mass':float(0.00), 'beta':float(0.0),
533 | 'x':float(9999.9999),'y':float(9999.9999),'z':float(9999.9999),'segid':self.name, 'resid':'1' }
534 |
535 | for typei in atomtypes:
536 | if(typei[0] == atm[self.natoms]['type']):
537 | atm[self.natoms]['mass'] = float(typei[1])
538 | break
539 |
540 | self.G.add_node(self.natoms, atm[self.natoms])
541 | self.natoms=self.natoms+1
542 |
543 | if line.startswith("BOND") or line.startswith("DOUB"):
544 | entry = re.split('\s+', string.rstrip(string.lstrip(line)))
545 | numbonds = int((len(entry)-1)/2)
546 | for bondi in range(0,numbonds):
547 | found1 = False
548 | found2 = False
549 | for i in range(0,self.natoms):
550 | if(atm[i]['name'] == entry[(bondi*2)+1]):
551 | found1 = True
552 | break
553 | for j in range(0,self.natoms):
554 | if(atm[j]['name'] == entry[(bondi*2)+2]):
555 | found2 = True
556 | break
557 | if(not found1):
558 | print "Error:atomgroup:read_charmm_rtp> Atomname not found in top",entry[(bondi*2)+1]
559 | if(not found2):
560 | print "Error:atomgroup:read_charmm_rtp> Atomname not found in top",entry[(bondi*2)+2]
561 | self.G.add_edge(i,j)
562 | self.G[i][j]['order']='1' # treat all bonds as single for now
563 | self.nbonds=self.nbonds+1
564 |
565 | if line.startswith("IMP"):
566 | entry = re.split('\s+', string.lstrip(line))
567 | numimpr = int((len(entry)-2)/4)
568 | for impi in range(0,numimpr):
569 | for i in range(0,self.natoms):
570 | if(atm[i]['name'] == entry[(impi*4)+1]):
571 | break
572 | for j in range(0,self.natoms):
573 | if(atm[j]['name'] == entry[(impi*4)+2]):
574 | break
575 | for k in range(0,self.natoms):
576 | if(atm[k]['name'] == entry[(impi*4)+3]):
577 | break
578 | for l in range(0,self.natoms):
579 | if(atm[l]['name'] == entry[(impi*4)+4]):
580 | break
581 | var = [i,j,k,l]
582 | self.impropers.append(var)
583 |
584 | self.nimpropers = len(self.impropers)
585 | if(self.ndihedrals > 0 or self.nangles > 0):
586 | print "WARNING:atomgroup:read_charmm_rtp> Autogenerating angl-dihe even though they are preexisting",self.nangles,self.ndihedrals
587 | self.autogen_angl_dihe()
588 | self.coord = np.zeros((self.natoms,3),dtype=float)
589 | #-----------------------------------------------------------------------
590 | def autogen_angl_dihe(self):
591 | self.angles = []
592 | for atomi in range(0,self.natoms):
593 | nblist = []
594 | for nb in self.G.neighbors(atomi):
595 | nblist.append(nb)
596 | for i in range(0,len(nblist)-1):
597 | for j in range(i+1,len(nblist)):
598 | var = [nblist[i],atomi,nblist[j]]
599 | self.angles.append(var)
600 | self.nangles = len(self.angles)
601 | self.dihedrals = []
602 | for i,j in self.G.edges_iter():
603 | nblist1 = []
604 | for nb in self.G.neighbors(i):
605 | if(nb != j):
606 | nblist1.append(nb)
607 | nblist2 = []
608 | for nb in self.G.neighbors(j):
609 | if(nb != i):
610 | nblist2.append(nb)
611 | if(len(nblist1) > 0 and len(nblist2) > 0 ):
612 | for ii in range(0,len(nblist1)):
613 | for jj in range(0,len(nblist2)):
614 | var = [nblist1[ii],i,j,nblist2[jj]]
615 | if(var[0] != var[3]):
616 | self.dihedrals.append(var)
617 | self.ndihedrals = len(self.dihedrals)
618 | #-----------------------------------------------------------------------
619 | def get_nonplanar_dihedrals(self,angl_params):
620 | nonplanar_dihedrals=[]
621 | cutoff=179.9
622 | for var in self.dihedrals:
623 | d1=self.G.node[var[0]]['type']
624 | d2=self.G.node[var[1]]['type']
625 | d3=self.G.node[var[2]]['type']
626 | d4=self.G.node[var[3]]['type']
627 | keep=1
628 | for angl_param in angl_params:
629 | p1=angl_param[0]
630 | p2=angl_param[1]
631 | p3=angl_param[2]
632 | eq=angl_param[3]
633 | if( d2==p2 and ( ( d1==p1 and d3==p3) or (d1==p3 and d3==p1))):
634 | if(eq > cutoff):
635 | keep=-1
636 | break
637 | if( d3==p2 and ( (d2==p1 and d4==p3) or (d2==p3 and d4==p1))):
638 | if(eq > cutoff):
639 | keep=-1
640 | break
641 |
642 |
643 | if(keep==1):
644 | nonplanar_dihedrals.append(var)
645 |
646 | return nonplanar_dihedrals
647 | #-----------------------------------------------------------------------
648 | def write_gmx_itp(self,filename,angl_params):
649 | f = open(filename, 'w')
650 | f.write("; Created by cgenff_charmm2gmx.py\n")
651 | f.write("\n")
652 | f.write("[ moleculetype ]\n")
653 | f.write("; Name nrexcl\n")
654 | f.write("%s 3\n" % self.name)
655 | f.write("\n")
656 | f.write("[ atoms ]\n")
657 | f.write("; nr type resnr residue atom cgnr charge mass typeB chargeB massB\n")
658 | f.write("; residue 1 %s rtp %s q qsum\n" % (self.name,self.name))
659 | pairs14 = nx.Graph()
660 | for atomi in range(0,self.natoms):
661 | pairs14.add_node(atomi)
662 | f.write("%6d %10s %6s %6s %6s %6d %10.3f %10.3f ;\n" %
663 | ( atomi+1,self.G.node[atomi]['type'],
664 | self.G.node[atomi]['resid'],self.name,self.G.node[atomi]['name'],atomi+1,
665 | self.G.node[atomi]['charge'],self.G.node[atomi]['mass'] ) )
666 | f.write("\n")
667 | f.write("[ bonds ]\n")
668 | f.write("; ai aj funct c0 c1 c2 c3\n")
669 | for i,j in self.G.edges_iter():
670 | f.write("%5d %5d 1\n" % (i+1,j+1) )
671 | f.write("\n")
672 | f.write("[ pairs ]\n")
673 | f.write("; ai aj funct c0 c1 c2 c3\n")
674 | for var in self.dihedrals:
675 | if (len(nx.dijkstra_path(self.G,var[0],var[3])) == 4): #this is to remove 1-2 and 1-3 included in dihedrals of rings
676 | pairs14.add_edge(var[0],var[3])
677 | for i,j in pairs14.edges_iter():
678 | f.write("%5d %5d 1\n" % (i+1,j+1) )
679 | f.write("\n")
680 | f.write("[ angles ]\n")
681 | f.write("; ai aj ak funct c0 c1 c2 c3\n")
682 | for var in self.angles:
683 | f.write("%5d %5d %5d 5\n" % (var[0]+1,var[1]+1,var[2]+1) )
684 | f.write("\n")
685 | f.write("[ dihedrals ]\n")
686 | f.write("; ai aj ak al funct c0 c1 c2 c3 c4 c5\n")
687 | nonplanar_dihedrals=self.get_nonplanar_dihedrals(angl_params)
688 | for var in nonplanar_dihedrals:
689 | f.write("%5d %5d %5d %5d 9\n" % (var[0]+1,var[1]+1,var[2]+1,var[3]+1) )
690 | f.write("\n")
691 | if(self.nimpropers > 0):
692 | f.write("[ dihedrals ]\n")
693 | f.write("; ai aj ak al funct c0 c1 c2 c3\n")
694 | for var in self.impropers:
695 | f.write("%5d %5d %5d %5d 2\n" % (var[0]+1,var[1]+1,var[2]+1,var[3]+1) )
696 | f.write("\n")
697 | f.close()
698 |
699 | #-----------------------------------------------------------------------
700 | def read_mol2_coor_only(self,filename):
701 | check_natoms = 0
702 | check_nbonds = 0
703 | f = open(filename, 'r')
704 | atm = {}
705 | section="NONE"
706 | for line in f.readlines():
707 | secflag=False
708 | if line.startswith("@"):
709 | secflag=True
710 | section="NONE"
711 |
712 | if((section=="NATO") and (not secflag)):
713 | entry = re.split('\s+', string.lstrip(line))
714 | check_natoms=int(entry[0])
715 | check_nbonds=int(entry[1])
716 | if(check_natoms != self.natoms):
717 | print "Error in atomgroup.py: read_mol2_coor_only: no. of atoms in mol2 (%d) and top (%d) are unequal" % (check_natoms, self.natoms)
718 | print "Usually this means the specified residue name does not match between str and mol2 files"
719 | #print check_natoms,self.natoms
720 | exit()
721 | if(check_nbonds != self.nbonds):
722 | print "Error in atomgroup.py: read_mol2_coor_only: no. of bonds in mol2 (%d) and top (%d) are unequal" % (check_nbonds, self.nbonds)
723 | #print check_nbonds,self.nbonds
724 | exit()
725 |
726 | section="NONE"
727 |
728 | if((section=="MOLE") and (not secflag)):
729 | self.name=line.strip()
730 | section="NATO" #next line after @MOLECULE contains atom, bond numbers
731 |
732 | if((section=="ATOM") and (not secflag)):
733 | entry = re.split('\s+', string.lstrip(line))
734 | ## guard against blank lines
735 | if (len(entry) > 1):
736 | atomi = int(entry[0])-1
737 | self.G.node[atomi]['x'] = float(entry[2])
738 | self.G.node[atomi]['y'] = float(entry[3])
739 | self.G.node[atomi]['z'] = float(entry[4])
740 | self.coord[atomi][0] = float(entry[2])
741 | self.coord[atomi][1] = float(entry[3])
742 | self.coord[atomi][2] = float(entry[4])
743 |
744 | if line.startswith("@MOLECULE"):
745 | section="MOLE"
746 | if line.startswith("@ATOM"):
747 | section="ATOM"
748 | if line.startswith("@BOND"):
749 | section="BOND"
750 | #-----------------------------------------------------------------------
751 | def write_pdb(self,f):
752 | for atomi in range(0,self.natoms):
753 | if(len(self.G.node[atomi]['name']) > 4):
754 | print "error in atomgroup.write_pdb(): atom name > characters"
755 | exit()
756 | f.write("%-6s%5d %-4s %-4s%5s%12.3f%8.3f%8.3f%6.2f%6.2f\n" %
757 | ("ATOM",atomi+1,self.G.node[atomi]['name'],self.name,self.G.node[atomi]['resid'],self.coord[atomi][0],
758 | self.coord[atomi][1],self.coord[atomi][2],1.0,self.G.node[atomi]['beta']))
759 | f.write("END\n")
760 |
761 | #=================================================================================================================
762 |
763 |
764 | if(len(sys.argv) != 5):
765 | print "Usage: RESNAME drug.mol2 drug.str charmm36.ff"
766 | exit()
767 |
768 | mol_name = sys.argv[1]
769 | mol2_name = sys.argv[2]
770 | rtp_name = sys.argv[3]
771 | ffdir = sys.argv[4]
772 | atomtypes_filename = ffdir + "/atomtypes.atp"
773 |
774 | print "NOTE1: Code tested with python 2.7.3. Your version:",sys.version
775 | print ""
776 | print "NOTE2: Please be sure to use the same version of CGenFF in your simulations that was used during parameter generation:"
777 | check_versions(rtp_name,ffdir + "/forcefield.doc")
778 | print ""
779 | print "NOTE3: In order to avoid duplicated parameters, do NOT select the 'Include parameters that are already in CGenFF' option when uploading a molecule into CGenFF."
780 |
781 |
782 | #for output
783 | itpfile = mol_name.lower() + ".itp"
784 | prmfile = mol_name.lower() + ".prm"
785 | initpdbfile = mol_name.lower() + "_ini.pdb"
786 | topfile = mol_name.lower() +".top"
787 |
788 | atomtypes = read_gmx_atomtypes(atomtypes_filename)
789 |
790 | angl_params = [] #needed for detecting triple bonds
791 | filelist = get_filelist_from_gmx_forcefielditp(ffdir,"forcefield.itp")
792 | for filename in filelist:
793 | anglpars = read_gmx_anglpars(filename)
794 | angl_params = angl_params + anglpars
795 |
796 |
797 | m = atomgroup()
798 | rtplines=get_charmm_rtp_lines(rtp_name,mol_name)
799 | m.read_charmm_rtp(rtplines,atomtypes)
800 |
801 |
802 | m.read_mol2_coor_only(mol2_name)
803 | f = open(initpdbfile, 'w')
804 | m.write_pdb(f)
805 | f.close()
806 |
807 |
808 | prmlines=get_charmm_prm_lines(rtp_name)
809 | params = parse_charmm_parameters(prmlines)
810 | write_gmx_bon(params,"",prmfile)
811 | anglpars = read_gmx_anglpars(prmfile)
812 | angl_params = angl_params + anglpars # append the new angl params
813 |
814 |
815 | m.write_gmx_itp(itpfile,angl_params)
816 | write_gmx_mol_top(topfile,ffdir,prmfile,itpfile,mol_name)
817 |
818 | print "============ DONE ============"
819 | print "Conversion complete."
820 | print "The molecule topology has been written to %s" % (itpfile)
821 | print "Additional parameters needed by the molecule are written to %s, which needs to be included in the system .top" % (prmfile)
822 | print "============ DONE ============"
823 |
824 | exit()
825 |
826 |
--------------------------------------------------------------------------------