├── 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 | --------------------------------------------------------------------------------