diff --git a/Scripts/MCF_Generation/CML/benzene_ua.cml.ff b/Scripts/MCF_Generation/CML/benzene_ua.ff similarity index 88% rename from Scripts/MCF_Generation/CML/benzene_ua.cml.ff rename to Scripts/MCF_Generation/CML/benzene_ua.ff index c92f69b9..75205c99 100644 --- a/Scripts/MCF_Generation/CML/benzene_ua.cml.ff +++ b/Scripts/MCF_Generation/CML/benzene_ua.ff @@ -10,7 +10,10 @@ begin atom-atomtype 6 CH2 end atom-atomtype +vdwtype LJ dihedraltype OPLS +scaling_1_4_vdw 0.0 +scaling_1_4_charge 0.0 nonbonded CH2 diff --git a/Scripts/MCF_Generation/CML/benzene_ua.cml.mcf b/Scripts/MCF_Generation/CML/benzene_ua.mcf similarity index 71% rename from Scripts/MCF_Generation/CML/benzene_ua.cml.mcf rename to Scripts/MCF_Generation/CML/benzene_ua.mcf index 792cb583..82cc4f43 100644 --- a/Scripts/MCF_Generation/CML/benzene_ua.cml.mcf +++ b/Scripts/MCF_Generation/CML/benzene_ua.mcf @@ -1,18 +1,19 @@ !******************************************************************************* -!Molecular connectivity file for benzene_ua.cml.pdb +!Molecular connectivity file for benzene_ua.pdb !******************************************************************************* !Atom Format -!index type element mass charge type parameters -!type="LJ", parms=epsilon sigma +!index type element mass charge vdw_type parameters +!vdw_type="LJ", parms=epsilon sigma +!vdw_type="Mie", parms=epsilon sigma repulsion_exponent dispersion_exponent # Atom_Info 6 -1 C1 C 12.011 0.0 LJ 50.500 3.695 ring -2 C2 C 12.011 0.0 LJ 50.500 3.695 ring -3 C3 C 12.011 0.0 LJ 50.500 3.695 ring -4 C4 C 12.011 0.0 LJ 50.500 3.695 ring -5 C5 C 12.011 0.0 LJ 50.500 3.695 ring -6 C6 C 12.011 0.0 LJ 50.500 3.695 ring +1 CH2 C 12.011 0.0 LJ 50.500 3.695 ring +2 CH2 C 12.011 0.0 LJ 50.500 3.695 ring +3 CH2 C 12.011 0.0 LJ 50.500 3.695 ring +4 CH2 C 12.011 0.0 LJ 50.500 3.695 ring +5 CH2 C 12.011 0.0 LJ 50.500 3.695 ring +6 CH2 C 12.011 0.0 LJ 50.500 3.695 ring !Bond Format !index i j type parameters @@ -60,6 +61,14 @@ # Improper_Info 0 +!Intra Scaling +!vdw_scaling 1-2 1-3 1-4 1-N +!charge_scaling 1-2 1-3 1-4 1-N + +# Intra_Scaling +0. 0. 0.0000 1. +0. 0. 0.0000 1. + !Fragment Format !index number_of_atoms_in_fragment branch_point other_atoms diff --git a/Scripts/MCF_Generation/PDB/argon.ff b/Scripts/MCF_Generation/PDB/argon.ff index c0778d17..693c373e 100644 --- a/Scripts/MCF_Generation/PDB/argon.ff +++ b/Scripts/MCF_Generation/PDB/argon.ff @@ -5,12 +5,13 @@ begin atom-atomtype 1 Argon_type end atom-atomtype +vdwtype LJ dihedraltype none nonbonded Argon_type Sigma 3.40 -Epsilon 120.0 +Epsilon 120.0 atom_type_charge charge diff --git a/Scripts/MCF_Generation/PDB/argon.mcf b/Scripts/MCF_Generation/PDB/argon.mcf index 1b8c0229..b2ae243c 100644 --- a/Scripts/MCF_Generation/PDB/argon.mcf +++ b/Scripts/MCF_Generation/PDB/argon.mcf @@ -2,12 +2,13 @@ !Molecular connectivity file for argon.pdb !******************************************************************************* !Atom Format -!index type element mass charge type parameters -!type="LJ", parms=epsilon sigma +!index type element mass charge vdw_type parameters +!vdw_type="LJ", parms=epsilon sigma +!vdw_type="Mie", parms=epsilon sigma repulsion_exponent dispersion_exponent # Atom_Info 1 -1 Ar1 Ar 39.948 0.0 LJ 120.000 3.400 +1 Argon_type Ar 39.948 0.0 LJ 120.000 3.400 # Bond_Info 0 @@ -21,6 +22,14 @@ # Improper_Info 0 +!Intra Scaling +!vdw_scaling 1-2 1-3 1-4 1-N +!charge_scaling 1-2 1-3 1-4 1-N + +# Intra_Scaling +0. 0. 0.0000 1. +0. 0. 0.0000 1. + !Fragment Format !index number_of_atoms_in_fragment branch_point other_atoms diff --git a/Scripts/MCF_Generation/PDB/c2.ff b/Scripts/MCF_Generation/PDB/c2.ff index 22ec9c29..91176bc9 100644 --- a/Scripts/MCF_Generation/PDB/c2.ff +++ b/Scripts/MCF_Generation/PDB/c2.ff @@ -6,6 +6,7 @@ begin atom-atomtype 2 CH3 end atom-atomtype +vdwtype LJ dihedraltype none nonbonded diff --git a/Scripts/MCF_Generation/PDB/c2.mcf b/Scripts/MCF_Generation/PDB/c2.mcf index 0f3ca3c3..d415d8ca 100644 --- a/Scripts/MCF_Generation/PDB/c2.mcf +++ b/Scripts/MCF_Generation/PDB/c2.mcf @@ -2,13 +2,14 @@ !Molecular connectivity file for c2.pdb !******************************************************************************* !Atom Format -!index type element mass charge type parameters -!type="LJ", parms=epsilon sigma +!index type element mass charge vdw_type parameters +!vdw_type="LJ", parms=epsilon sigma +!vdw_type="Mie", parms=epsilon sigma repulsion_exponent dispersion_exponent # Atom_Info 2 -1 C1 C 12.011 0.0 LJ 98.000 3.750 -2 C2 C 12.011 0.0 LJ 98.000 3.750 +1 CH3 C 12.011 0.0 LJ 98.000 3.750 +2 CH3 C 12.011 0.0 LJ 98.000 3.750 !Bond Format !index i j type parameters @@ -27,6 +28,14 @@ # Improper_Info 0 +!Intra Scaling +!vdw_scaling 1-2 1-3 1-4 1-N +!charge_scaling 1-2 1-3 1-4 1-N + +# Intra_Scaling +0. 0. 0.0000 1. +0. 0. 0.0000 1. + !Fragment Format !index number_of_atoms_in_fragment branch_point other_atoms diff --git a/Scripts/MCF_Generation/PDB/c4.ff b/Scripts/MCF_Generation/PDB/c4.ff index ca5525bf..ba9c3134 100644 --- a/Scripts/MCF_Generation/PDB/c4.ff +++ b/Scripts/MCF_Generation/PDB/c4.ff @@ -8,7 +8,10 @@ begin atom-atomtype 4 CH3 end atom-atomtype +vdwtype LJ dihedraltype OPLS +scaling_1_4_vdw 0.0 +scaling_1_4_charge 0.0 nonbonded CH3 diff --git a/Scripts/MCF_Generation/PDB/c4.mcf b/Scripts/MCF_Generation/PDB/c4.mcf index 5ac99a7e..6ddbb906 100644 --- a/Scripts/MCF_Generation/PDB/c4.mcf +++ b/Scripts/MCF_Generation/PDB/c4.mcf @@ -2,15 +2,16 @@ !Molecular connectivity file for c4.pdb !******************************************************************************* !Atom Format -!index type element mass charge type parameters -!type="LJ", parms=epsilon sigma +!index type element mass charge vdw_type parameters +!vdw_type="LJ", parms=epsilon sigma +!vdw_type="Mie", parms=epsilon sigma repulsion_exponent dispersion_exponent # Atom_Info 4 -1 C1 C 12.011 0.0 LJ 98.000 3.750 -2 C2 C 12.011 0.0 LJ 46.000 3.950 -3 C3 C 12.011 0.0 LJ 46.000 3.950 -4 C4 C 12.011 0.0 LJ 98.000 3.750 +1 CH3 C 12.011 0.0 LJ 98.000 3.750 +2 CH2 C 12.011 0.0 LJ 46.000 3.950 +3 CH2 C 12.011 0.0 LJ 46.000 3.950 +4 CH3 C 12.011 0.0 LJ 98.000 3.750 !Bond Format !index i j type parameters @@ -46,6 +47,14 @@ # Improper_Info 0 +!Intra Scaling +!vdw_scaling 1-2 1-3 1-4 1-N +!charge_scaling 1-2 1-3 1-4 1-N + +# Intra_Scaling +0. 0. 0.0000 1. +0. 0. 0.0000 1. + !Fragment Format !index number_of_atoms_in_fragment branch_point other_atoms diff --git a/Scripts/MCF_Generation/PDB/pentane.ff b/Scripts/MCF_Generation/PDB/pentane.ff index 6df424e8..920213df 100644 --- a/Scripts/MCF_Generation/PDB/pentane.ff +++ b/Scripts/MCF_Generation/PDB/pentane.ff @@ -9,7 +9,10 @@ begin atom-atomtype 5 CH3 end atom-atomtype +vdwtype LJ dihedraltype OPLS +scaling_1_4_vdw 0.0 +scaling_1_4_charge 0.0 nonbonded CH3 diff --git a/Scripts/MCF_Generation/PDB/pentane.mcf b/Scripts/MCF_Generation/PDB/pentane.mcf index 7d630a80..6bc1add8 100644 --- a/Scripts/MCF_Generation/PDB/pentane.mcf +++ b/Scripts/MCF_Generation/PDB/pentane.mcf @@ -2,16 +2,17 @@ !Molecular connectivity file for pentane.pdb !******************************************************************************* !Atom Format -!index type element mass charge type parameters -!type="LJ", parms=epsilon sigma +!index type element mass charge vdw_type parameters +!vdw_type="LJ", parms=epsilon sigma +!vdw_type="Mie", parms=epsilon sigma repulsion_exponent dispersion_exponent # Atom_Info 5 -1 C1 C 12.011 0.0 LJ 98.000 3.750 -2 C2 C 12.011 0.0 LJ 46.000 3.950 -3 C3 C 12.011 0.0 LJ 46.000 3.950 -4 C4 C 12.011 0.0 LJ 46.000 3.950 -5 C5 C 12.011 0.0 LJ 98.000 3.750 +1 CH3 C 12.011 0.0 LJ 98.000 3.750 +2 CH2 C 12.011 0.0 LJ 46.000 3.950 +3 CH2 C 12.011 0.0 LJ 46.000 3.950 +4 CH2 C 12.011 0.0 LJ 46.000 3.950 +5 CH3 C 12.011 0.0 LJ 98.000 3.750 !Bond Format !index i j type parameters @@ -50,6 +51,14 @@ # Improper_Info 0 +!Intra Scaling +!vdw_scaling 1-2 1-3 1-4 1-N +!charge_scaling 1-2 1-3 1-4 1-N + +# Intra_Scaling +0. 0. 0.0000 1. +0. 0. 0.0000 1. + !Fragment Format !index number_of_atoms_in_fragment branch_point other_atoms diff --git a/Scripts/MCF_Generation/mcfgen.py b/Scripts/MCF_Generation/mcfgen.py index 3adb2468..03c0ef3e 100755 --- a/Scripts/MCF_Generation/mcfgen.py +++ b/Scripts/MCF_Generation/mcfgen.py @@ -22,6 +22,8 @@ #******************************************************************************* # SCRIPT: mcfgen.py +# VERSION: 2.0 +# NEW FEATURES: support python2 and python3 # VERSION: 1.1 # NEW FEATURES: read GROMACS forcefield files (.itp or .top) # @@ -35,12 +37,12 @@ #******************************************************************************* from __future__ import division from __future__ import print_function -from builtins import str from builtins import input +from builtins import str from builtins import range from past.utils import old_div -import sys, os, argparse, linecache, re import warnings +import sys, os, argparse, linecache, re #******************************************************************************* # ARGUMENT PARSE @@ -134,21 +136,19 @@ 3) Improper definitions are not currently read from Gromacs forcefield files. """) parser.add_argument('configFile', - help="""CONFIGFILE must be in either .pdb or .cml format. """ + - """A .pdb file can be generated using Gaussview, """ + - """while .cml files can be generated using Avogadro.""") + help="""CONFIGFILE must be in either .pdb or .cml format. """ + + """A .pdb file can be generated using Gaussview, """ + + """while .cml files can be generated using Avogadro.""") parser.add_argument('--ffTemplate', '-t', action='store_true', - help="""Generate a blank force field file template.""") + help="""Generate a blank force field file template.""") parser.add_argument('--ffFile', '-f', - help="""The default FFFILE is molecule.ff, a custom format """ + - """for this script. Alternatively, the forcefile """ + - """parms can be supplied in GROMACS format (.itp).""") + help="""The default FFFILE is molecule.ff, a custom format """ + + """for this script. Alternatively, the forcefile """ + + """parms can be supplied in GROMACS format (.itp).""") parser.add_argument('--mcfFile', '-m', - help="""The default MCFFILE is molecule.mcf.""") + help="""The default MCFFILE is molecule.mcf.""") parser.add_argument('--massDefault', '-d', type=float, - help="""Provide a default mass for unknown elements.""") -parser.add_argument('--verbose', '-v', action='store_true', - help="""Print an atom-by-atom summary of the topology scan""") + help="""Provide a default mass for unknown elements.""") args = parser.parse_args() @@ -301,11 +301,11 @@ def cml_to_pdb(infilename): for i in range(cml_start_atom+1, cml_end_atom): cml_atom_info.append(re.findall('"([^"]*)"', - linecache.getline(infilename, i))) + linecache.getline(infilename, i))) for i in range(cml_start_bonds+1, cml_end_bonds): cml_bond_info.append(re.findall('"([^"]*)"', - linecache.getline(infilename, i))[0].split()) + linecache.getline(infilename, i))[0].split()) filePdb = open(basename+'.pdb','w') @@ -373,12 +373,83 @@ def cml_to_pdb(infilename): filePdb.close() - -def removeTempFromMaster(tempList,masterList): - for i in range(0,len(masterList)): - if masterList[i] == tempList[0]: - for j in range(0,len(tempList)): - del(masterList[-1]) +def lookup(atomlook): + tempfile = open ('temporary.temp', 'r') + for line in tempfile: + linestring = line.split() + if linestring[0]==atomlook: + line=line.split() + return line + tempfile.close() + +def initialize(infilename): + + #This function will set the starting atom for the subsequent ring scan + #It can handle the following special cases: + #1) Argon: If no CONECT keyword is found, it will automatically assume it's argon. + #2) Cyclic UA model: In this case, a 'ghost' molecule will be used as a starting point. + + + global cyclic_ua_atom + conect_found = False + atom ='' + ifile = open(infilename, 'r') + tempfile = open('temporary.temp', 'w') + for line in ifile: + linestring = line.split() + if not line.strip(): + continue + if linestring[0]=='CONECT': + conect_found = True + tempfile.write(line[6:len(line)]) + numconnect=len(linestring)-2 + if numconnect==1: + atom=linestring[1] + cyclic_ua_atom = False + ifile.close() + + if conect_found == False: #This means that this is Argon + atom = '' + cyclic_ua_atom = False + tempfile.close() + return atom + + if cyclic_ua_atom == True and atom == '' : + #This means molecule has at least one cyclic united atom model + tempfile.close() + atom = '0' + + #Rewrite tempfile to include a dummy atom 0 + + tempfile2=open('temporary.temp2','w') + tempfile1=open('temporary.temp','r') + for line in tempfile1: + this_line_new = line.split() + if this_line_new[0] == '1': + this_line_new.append('0') + for each_atom in this_line_new: + tempfile2.write(each_atom+' ') + tempfile2.write('\n') + tempfile2.write('0 1') + + tempfile2.close() + tempfile1.close() + os.system('mv temporary.temp2 temporary.temp') + + return atom + + +def isRing(scannedList, atom): + for i in range(0,len(scannedList)): + if scannedList[i] == atom: + return True + + +def cleanGenList(branchList): + for i in range(0,len(genScannedList)): + if genScannedList[i] == branchList[0]: + for j in range(0,len(branchList)): + del(genScannedList[-1]) return @@ -392,189 +463,190 @@ def isolateRing(theList): for index, item in enumerate(theList): if item == repeatedAtom[0]: location.append(index) - - # loop over atoms in ring and verify that each is bonded to 2 other ring atoms - ring = theList[location[0]:location[1]] - for atom in ring: - nBonds = len([val for val in ring if val in atomConnect[atom]]) - if nBonds < 2: # exo-ring atom - ring.remove(atom) - - ringList.append(ring) - removeTempFromMaster(ringList[-1],wRingList) + ringList.append(theList[location[0]:location[1]]) + #logfile.write("The ring list is: " + str(ringList) + "\n") + cleanGenList(ringList[-1]) -def ringID(iRecursion): - """arguments: - iRecursion, integer = how many times this functino has called itself -returns: - endType, string = reason why branch search terminated - branchList, list = list of atoms scanned so far -""" - iRecursion += 1 - - # This function does a recursive scan on the molecule, - # utilizing the following lists: +def isAlreadyInRing(atom): + for row in ringList: + for i in range(0,len(row)): + if row[i]==atom: + return True - # ringList, nested list - each entry is a list of atom indices in a ring - # scannedList, list of tuples - should have one entry for each atom in the order it is evaluated - # wRingList, list - working ringList, list of possible ring atoms - # wBranchList, list - reset with every ringID() call - # branchList, list - returned by ringID() - - wBranchList=[] - while True: - newAtom, oldAtom = scannedList[-1] - newConnect = atomConnect[newAtom] # list of atoms bonded to newAtom - nBonds=len(newConnect) - - # on recursive calls to ringID(), will hit two atoms twice for each ring - - # if repeat atom has already been IDd as a ring atom, - # terminate this recursive search and continue with the next atom - isAlreadyInRing = any([newAtom in ring for ring in ringList]) - if isAlreadyInRing: - if args.verbose: - print("%4d %-15s" % (newAtom, "already IDd as a ring atom, abort")) - break +def bondID(): - # if repeat atom has not been flagged as a ring atom, flag it now - # ring will be added to ringList by calling isolateRing below - isRing = newAtom in wRingList - if isRing: - if args.verbose: - print("%4d %-15s" % (newAtom, "repeat atom, must be a ring atom")) - wRingList.append(newAtom) - if iRecursion == 1: # on deeper recursion, isolateRing will be called after exiting - isolateRing(wRingList) - return "EndRing", scannedList + #This function will populate the variable "bondList" - wRingList.append(newAtom) - wBranchList.append(newAtom) + tempfile = open('temporary.temp','r') + oldatom=[] + for line in tempfile: + atomlist1=line.split() + newAtom=atomlist1[0] + for i in range(1, len(atomlist1)): + if atomlist1[i] in oldatom: + continue + bondList.append(atomlist1[0] + " " + atomlist1[i]) + oldatom.append(newAtom) + tempfile.close() - # the number of bonds determines the topology type of this atom: - # terminal, linear, branch - if nBonds==1: # terminal - if args.verbose: - print("%4d %-40s" % (newAtom, "terminal")) - if len(scannedList) > 1: # only bonded atom is oldAtom - return "EndPoint", wBranchList - else: # started at a terminal atom, continue onto the next - scannedList.append((newConnect[0],newAtom)) +def scan(newAtom,oldLine,scanning): - elif nBonds==2: # linear - if args.verbose: - print("%4d %-40s" % (newAtom, "linear")) + #This function does a recursive scan on the molecule. The main product of this function + #is the list ringList, which contains atoms that form a ring. - # one bonded atom is oldAtom - # need to select the other atom - for i in range(nBonds): + scannedList=[] + #branchalreadyscanned=[0] + while 1==1: - if newConnect[i]==oldAtom: # skip oldAtom - continue - scannedList.append((newConnect[i],newAtom)) - break # for loop, and continue next iteration of while loop + #logfile.write("Atom " + newAtom + "\n") + + if isAlreadyInRing(newAtom)==True: + #logfile.write("Atom " + newAtom + " was already included in a ring\n\n") + break - elif nBonds > 2: # branch - if args.verbose: - print("%4d %-40s" % (newAtom, "branch")) + if isRing(genScannedList, newAtom)==True: + #logfile.write("A ring was found because atom " + newAtom + + # " was already scanned\n\n") + genScannedList.append(newAtom) + return "EndRing", scannedList - # loop over each atom newAtom is bonded to - # and call ringID() recursively - for j in range(nBonds): - - if newConnect[j]==oldAtom: # skip oldAtom - continue + scannedList.append(newAtom) + genScannedList.append(newAtom) + newLine=lookup(newAtom) + totalBondedAtoms=len(newLine)-1 - # recursive call to ringID() - scannedList.append((newConnect[j],newAtom)) - endType, branchList = ringID(iRecursion) + if totalBondedAtoms>2: #branch point? - # ringID() ends when an endpoint or repeat atom is found + #logfile.write("A branch point was found in atom " + newAtom + "\n") + for j in range(1,totalBondedAtoms+1): + if newLine[j]==oldLine[0]: + #or newLine[j]==branchalreadyscanned[0]: + #If old atom in list is scanned + continue + newAtom=newLine[j] + endType, branchList = scan(newAtom, newLine, True) + #branchalreadyscanned=lookup(newAtom) if endType=="EndPoint": - removeTempFromMaster(branchList,wRingList) + cleanGenList(branchList) if endType=="EndRing": - isolateRing(wRingList) + isolateRing(genScannedList) + #if endType=="EndScan": + break + + for i in range(1,totalBondedAtoms+1): + + if scanning==True and totalBondedAtoms==1: + #logfile.write("End point found at " + newAtom + "\n") + return "EndPoint", scannedList - break # while loop, and return + if scanning==False and totalBondedAtoms==1: #Initial step + scanning = True - return "EndScan", wBranchList + if len(oldLine)==0: + newAtom=newLine[i] + oldLine=newLine + continue + + if newLine[i]==oldLine[0]: #If old atom in list is scanned + continue + + newAtom=newLine[i] + oldLine=newLine + break + + return "EndScan", scannedList def fragID(): #This function will populate the variable "fragList" - # add exo-ring atoms to rings to make ring fragments adjacentatoms=[] - for ring in ringList: - for atom in ring: - nBonds = len(atomConnect[atom]) - if nBonds > 2: - adjacentatoms.append(list(set(atomConnect[atom])-set(ring))) + tempfile=open('temporary.temp','r') + for eachring in ringList: + for eachatom in eachring: + vector=lookup(eachatom) + if len(vector)>3: + adjacentatoms.append(list(set(vector)-set(eachring))) adjacentatoms1=[_f for _f in adjacentatoms if _f] adjacentatoms = [x for sublist in adjacentatoms1 for x in sublist] - fragList.append(ring+adjacentatoms) + fragList.append(eachring+adjacentatoms) adjacentatoms=[] - # find normal fragments - for atom in atomList: - inRing=False - nBonds = len(atomConnect[atom]) - if nBonds > 1: - anchoratom = atom + for line in tempfile: + inaring=False + linestring=line.split() + if len(linestring)>2: + anchoratom=linestring[0] else: continue - for ring in ringList: - if anchoratom in set(ring): - inRing = True + for row in ringList: + if anchoratom in set(row): + inaring=True - if inRing==True: + if inaring==True: continue else: - fragList.append([atom] + atomConnect[atom]) + fragList.append(linestring) + + tempfile.close() def angleID(): #This function will populate the list angleList - for atom in atomList: - nBonds = len(atomConnect[atom]) - nAtomsInFrag = nBonds + 1 - if nAtomsInFrag < 3: + tempfile=open('temporary.temp','r') + for line in tempfile: + atomlist=line.split() + if len(atomlist)<3: continue - apex=atom - for atom1 in atomConnect[atom]: - for atom2 in atomConnect[atom]: - if atom1 == atom2: - continue - ijk = (atom1, apex, atom2) - kji = ijk[::-1] - if ijk not in angleList and kji not in angleList: - angleList.append(ijk) + apex=atomlist[0] + del(atomlist[0]) + for i in range(0,len(atomlist)-1): + for j in range(i+1,len(atomlist)): + angleList.append(atomlist[i] + " " + apex + " " + atomlist[j]) + + removedoublecounting("angles") + + def dihedralID(): #This function populates the variable dihedralList - for atom1 in atomList: - for atom2 in atomConnect[atom1]: - for atom3 in atomConnect[atom2]: - if atom1 == atom3: + tempfile=open('temporary.temp','r') + + for line in tempfile: + atomlist1=line.split() + atom1=atomlist1[0] + for i in range(1,len(atomlist1)): + atomlist2=lookup(atomlist1[i]) + atom2=atomlist2[0] + if atom2==atom1: + continue + for j in range(1,len(atomlist2)): + atomlist3=lookup(atomlist2[j]) + atom3=atomlist3[0] + if atom3==atom2 or atom3==atom1: continue - for atom4 in atomConnect[atom3]: - if atom1 == atom4 or atom2 == atom4: + for k in range(1,len(atomlist3)): + atomlist4=lookup(atomlist3[k]) + atom4=atomlist4[0] + if atom4==atom3 or atom4==atom2 or atom4==atom1: continue - ijkl = (atom1,atom2,atom3,atom4) - lkji = ijkl[::-1] - if ijkl not in dihedralList and lkji not in dihedralList: - dihedralList.append(ijkl) + dihedralList.append(atom1 + " " +atom2 + " " +atom3 + " " +atom4) + + removedoublecounting("dihedrals") + + + def fragInfo(mcfFile): """arguments: @@ -628,14 +700,74 @@ def fragInfo(mcfFile): + +def removedoublecounting(thingtoclean): + + if thingtoclean=="dihedrals": + linesrepeated=[] + cleanlist=[] + for index,row in enumerate(dihedralList): + set1=set(row.split()) + for i in range(index+1, len(dihedralList)): + set2=set(dihedralList[i].split()) + if set1==set2: + linesrepeated.append(i) + + for index,row in enumerate(dihedralList): + if index in linesrepeated: + continue + cleanlist.append(row) + + del(dihedralList[0:len(dihedralList)]) + for i in range(0,len(cleanlist)): + dihedralList.append(cleanlist[i]) + + if thingtoclean=="angles": + linesrepeated=[] + cleanlist=[] + for index,row in enumerate(angleList): + set1=set(row.split()) + for i in range(index+1, len(angleList)): + set2=set(angleList[i].split()) + if set1==set2: + linesrepeated.append(i) + + for index,row in enumerate(angleList): + if index in linesrepeated: + continue + cleanlist.append(row) + + del(angleList[0:len(angleList)]) + for i in range(0,len(cleanlist)): + angleList.append(cleanlist[i]) + + if thingtoclean=="fragConn": + linesrepeated=[] + cleanlist=[] + for index,row in enumerate(fragConn): + set1=set(row.split()) + for i in range(index+1, len(fragConn)): + set2=set(fragConn[i].split()) + if set1==set2: + linesrepeated.append(i) + + for index,row in enumerate(fragConn): + if index in linesrepeated: + continue + cleanlist.append(row) + + del(fragConn[0:len(fragConn)]) + for i in range(0,len(cleanlist)): + fragConn.append(cleanlist[i]) + def fragConnectivity(): #This function will populate the list fragConn group=[] j=0 - if ((cyclic_ua_atom == True and len(ringList)==0) or - len(fragList) < 2): + if cyclic_ua_atom == True and len(ringList)==0: + fragConn.append("0") return for index1, row in enumerate(fragList): @@ -651,16 +783,18 @@ def fragConnectivity(): a=set(group) b=set(otherrow) if a<=b and index1!=index2: - fragConn.append((index1+1, index2+1)) + fragConn.append(str(index1+1) + spacing + str(index2+1)) group=[] - + removedoublecounting("fragConn") + + def fragConnectivityInfo(mcfFile): mcf=open(mcfFile,'a') mcf.write('\n\n# Fragment_Connectivity\n') mcf.write(str(len(fragConn))+'\n') for index, row in enumerate(fragConn): - mcf.write(str(index+1) + spacing + spacing.join([str(x) for x in row])) + mcf.write(str(index+1) + spacing + row) mcf.write('\n') mcf.write('\n\nEND\n') mcf.close() @@ -675,7 +809,7 @@ def ffFileGeneration(infilename,outfilename): global dihedralType if len(dihedralList) > 0: dihedralType = input("Enter the dihedral functional form " + - "(CHARMM/OPLS/harmonic/none): ") + "(CHARMM/OPLS/harmonic/none): ") else: dihedralType = "NONE" #This is just a default. It will not affect molecules with no dihedrals. @@ -721,7 +855,7 @@ def ffFileGeneration(infilename,outfilename): # Write bond entries bondTypesWritten = [] for bond in bondList: - ij = bond + ij = tuple([int(i) for i in bond.split()]) ji = ij[::-1] ijType = tuple([atomParms[i]['type'] for i in ij]) jiType = ijType[::-1] @@ -735,7 +869,7 @@ def ffFileGeneration(infilename,outfilename): # Write angle entries angleTypesWritten = [] for angle in angleList: - ijk = angle + ijk = tuple([int(i) for i in angle.split()]) kji = ijk[::-1] ijkType = tuple([atomParms[i]['type'] for i in ijk]) kjiType = ijkType[::-1] @@ -750,7 +884,7 @@ def ffFileGeneration(infilename,outfilename): if dihedralType != "none": dihedTypesWritten = [] for dihed in dihedralList: - ijkl = dihed + ijkl = tuple([int(i) for i in dihed.split()]) lkji = ijkl[::-1] ijklType = tuple([atomParms[i]['type'] for i in ijkl]) lkjiType = ijklType[::-1] @@ -759,7 +893,7 @@ def ffFileGeneration(infilename,outfilename): dihedTypesWritten.append(ijklType) ff.write("dihedrals\n") ff.write(ijklType[0] + " " + ijklType[1] + " " +ijklType[2] + " " + - ijklType[3] + "\n") + ijklType[3] + "\n") if dihedralType == "CHARMM": ff.write("K \n") @@ -789,12 +923,11 @@ def readPdb(pdbFile): returns: atomList, list of ints = atom numbers from pdb file atomParms, dict = atom parameters: name, type, element, mass - atomConnect, dict = list of atoms bonded to key """ # initialize variables atomList = [] atomParms = {} - atomConnect = {} + bondList = [] numAtomTypes = 0 repeatedIndex = False pdb = open(pdbFile,'r') @@ -845,23 +978,16 @@ def readPdb(pdbFile): if "CONECT" in this_line: if repeatedIndex: raise Error("PDB contains a repeated index. Cannot determine bond " + - "connectivity.") + "connectivity.") else: lineList = line.split() - i = int(lineList[1]) # will match to the PDB index, not the PDB line number - # bondList + i = lineList[1] # will match to the PDB index, not the PDB line number for j in lineList[2:]: if not any([True for bond in bondList if bond[0]==int(j)]): bondList.append((int(i), int(j))) - # atomConnect - atomConnect[i] = [int(j) for j in lineList[2:]] - if len(lineList) == 3: # end point atom - atomConnect['startRingID'] = (i,0) - if 'startRingID' not in atomConnect: - atomConnect['startRingID'] = (atomList[0],0) pdb.close() - return atomList, atomParms, atomConnect, bondList, numAtomTypes + return atomList, atomParms, numAtomTypes class Error(Exception): @@ -905,7 +1031,6 @@ def readNative(ffFile, atomParms): scaling_1_4['charge'] = float(line.split()[1]) elif 'nonbonded' in line: index = ff.readline().strip() #atomType - atomParms[index]['bondtype'] = index # for compatibility with gromacs sigma = float(ff.readline().split()[1]) epsilon = float(ff.readline().split()[1]) if vdwType == 'Mie': @@ -1015,7 +1140,7 @@ def readGromacs(ffFile, atomParms, bondParms, angleParms, dihedralParms, scaling section = line.strip() #store the section header line = ff.readline() while line and not line.isspace(): #section ends with a blank line - if not line.startswith(';') and not line.startswith('#'): + if not line.startswith(';'): data = line.split() if 'default' in section: # Look for function type 1, to make sure this is Lennard-Jones @@ -1034,92 +1159,84 @@ def readGromacs(ffFile, atomParms, bondParms, angleParms, dihedralParms, scaling atomParms[index] = {} atomParms[index]['charge'] = data[6] #store as string elif 'atomtypes' in section: - base = None - for i in range(len(data)): - if data[i] == 'A' or data[i] == 'D': - base=i - break - if base is None: - raise Error('ptype is not A or D. Cassandra only supports point particles.\n') - index = data[0] #atomType + if data[-3] != 'A': + raise Error('ptype is not A. Cassandra only supports point ' + + 'particles.') + index = data[0].upper() #atomType if index not in atomParms: atomParms[index] = {} - atomParms[index]['bondtype'] = data[0] if data[1].isdigit else data[1] - atomParms[index]['mass'] = float(data[base-2]) - atomParms[index]['charge'] = data[base-1] #store as string + atomParms[index]['mass'] = float(data[2]) + atomParms[index]['charge'] = data[3] #store as string if comboRule == '1': - C6 = float(data[base+1]) - C12 = float(data[base+2]) + C6 = float(data[-2]) + C12 = float(data[-1]) sigma = ((old_div(C12, C6))**(1/6.)) * 10 epsilon = old_div(old_div(old_div(C6**2, 4), C12), Rg) elif comboRule == '2' or comboRule == '3': - sigma = float(data[base+1]) * 10 - epsilon = float(data[base+2]) / Rg + sigma = float(data[-2]) * 10 + epsilon = float(data[-1]) / Rg atomParms[index]['vdw'] = (vdwType, epsilon, sigma) # Look for bondParms elif 'bond' in section: if 'type' in section: - index = (data[0], data[1]) #atomTypes + index = (data[0].upper(), data[1].upper()) #atomTypes else: index = (int(data[0]), int(data[1])) #atomNumbers if not any([data[2]=='1', data[2]=='2', data[2]=='3', - data[2]=='4']): + data[2]=='4']): raise Error('Cassandra only supports fixed bonds at this time.') - if len(data) > 3: - b0 = float(data[3]) * 10 - bondParms[index] = ('fixed', b0) + b0 = float(data[3]) * 10 + bondParms[index] = ('fixed', b0) # Look for angleParms elif 'angle' in section: if 'type' in section: - index = (data[0], data[1], data[2]) + index = (data[0].upper(), data[1].upper(), data[2].upper()) else: index = (int(data[0]), int(data[1]), int(data[2])) if data[3]!='1': raise Error('Cassandra supports fixed or harmonic angles.') - if len(data) > 4: - theta = float(data[4]) - ktheta = float(data[5]) / 2. / Rg - angleParms[index] = ('harmonic', ktheta, theta) + theta = float(data[4]) + ktheta = float(data[5]) / 2. / Rg + angleParms[index] = ('harmonic', ktheta, theta) # Look for dihedralParms elif 'dihedral' in section: if 'type' in section: - index = (data[0], data[1], data[2], data[3]) + index = (data[0].upper(), data[1].upper(), data[2].upper(), + data[3].upper()) else: index = (int(data[0]), int(data[1]), int(data[2]), int(data[3])) - if len(data) > 5: - if data[4]=='1' or data[4]=='4' or data[4]=='9': #CHARMM - delta = float(data[5]) - a0 = float(data[6]) - a1 = float(data[7]) - dihedralParms[index] = ('CHARMM', a0, a1, delta) - elif data[4] == '2': #harmonic - phi = float(data[5]) - kphi = float(data[6]) / 2. / Rg - dihedralParms[index] = ('harmonic', kphi, phi) - elif data[4] == '3': #Ryckaert-Bellemans - c0 = float(data[5]) - c1 = float(data[6]) - c2 = float(data[7]) - c3 = float(data[8]) - c4 = float(data[9]) - c5 = float(data[10]) - a0 = c0 + c1 + c2 + c3 - a1 = - c1 - 3. * c3 / 4. - a2 = - c2 / 2. - a3 = - c3 / 4. - if not c4 == 0. and not c5 == 0.: - raise Error('Can only convert Ryckaert-Bellemans dihedrals ' + - 'to OPLS if c4==0 and c5==0.\n') - dihedralParms[index] = ('OPLS', a0, a1, a2, a3) - elif data[4] == '5': #OPLS - a0 = 0. - a1 = float(data[5]) / 2. - a2 = float(data[6]) / 2. - a3 = float(data[7]) / 2. - dihedralParms[index] = ('OPLS', a0, a1, a2, a3) -# else: -# raise Error('Cassandra supports OPLS, CHARMM and harmonic ' + -# 'dihedrals.\n' + line) + if data[4]=='1' or data[4]=='4' or data[4]=='9': #CHARMM + delta = float(data[5]) + a0 = float(data[6]) + a1 = float(data[7]) + dihedralParms[index] = ('CHARMM', a0, a1, delta) + elif data[4] == '2': #harmonic + phi = float(data[5]) + kphi = float(data[6]) / 2. / Rg + dihedralParms[index] = ('harmonic', kphi, phi) + elif data[4] == '3': #Ryckaert-Bellemans + c0 = float(data[5]) + c1 = float(data[6]) + c2 = float(data[7]) + c3 = float(data[8]) + c4 = float(data[9]) + a0 = c0 + c2 / 2. + c4/ 2. + a1 = c1 + 3. * c3 / 4. + a2 = c2 / 2. + c4 / 2. + a3 = c3 / 4. + if not c4 == 0. and c5 == 0.: + raise Error('Can only convert Ryckaert-Bellemans dihedrals ' + + 'to OPLS if c4==0 and c5==0.\n') + dihedralParms[index] = ('OPLS', a0, a1, a2, a3) + elif data[4] == '5': #OPLS + a0 = 0. + a1 = float(data[5]) / 2. + a2 = float(data[6]) / 2. + a3 = float(data[7]) / 2. + dihedralParms[index] = ('OPLS', a0, a1, a2, a3) + else: + raise Error('Cassandra supports OPLS, CHARMM and harmonic ' + + 'dihedrals.') line = ff.readline() line = ff.readline() @@ -1147,7 +1264,7 @@ def checkParms(atomList, bondList, angleList, dihedralList, # Check that we have all the parms we need for i in atomList: - for parm in ['vdw', 'charge', 'element', 'mass', 'bondtype']: + for parm in ['vdw', 'charge', 'element', 'mass']: if parm not in atomParms[i]: iType = atomParms[i]['type'] if parm in atomParms[iType]: @@ -1155,10 +1272,10 @@ def checkParms(atomList, bondList, angleList, dihedralList, else: raise Error(parm + ' parms for atom ' + str(i) + ' not found.') for bond in bondList: - ij = bond + ij = tuple([int(i) for i in bond.split()]) ji = ij[::-1] if ij not in bondParms and ji not in bondParms: - ijType = tuple([atomParms[i]['bondtype'] for i in ij]) + ijType = tuple([atomParms[i]['type'] for i in ij]) jiType = ijType[::-1] if ijType in bondParms: bondParms[ij] = bondParms[ijType] @@ -1167,10 +1284,10 @@ def checkParms(atomList, bondList, angleList, dihedralList, else: raise Error('Bond parms for atoms ' + bond + ' not found.') for angle in angleList: - ijk = angle + ijk = tuple([int(i) for i in angle.split()]) kji = ijk[::-1] if ijk not in angleParms and kji not in angleParms: - ijkType = tuple([atomParms[i]['bondtype'] for i in ijk]) + ijkType = tuple([atomParms[i]['type'] for i in ijk]) kjiType = ijkType[::-1] if ijkType in angleParms: angleParms[ijk] = angleParms[ijkType] @@ -1179,21 +1296,15 @@ def checkParms(atomList, bondList, angleList, dihedralList, else: raise Error('Angle parms for atoms ' + angle + ' not found.') for dihedral in dihedralList: - ijkl = dihedral + ijkl = tuple([int(i) for i in dihedral.split()]) lkji = ijkl[::-1] if ijkl not in dihedralParms and lkji not in dihedralParms: - ijklType = tuple([atomParms[i]['bondtype']for i in ijkl]) + ijklType = tuple([atomParms[i]['type']for i in ijkl]) lkjiType = ijklType[::-1] - jkDefault = tuple(['X', ijklType[1], ijklType[2], 'X']) - kjDefault = tuple(['X', lkjiType[1], lkjiType[2], 'X']) if ijklType in dihedralParms: dihedralParms[ijkl] = dihedralParms[ijklType] elif lkjiType in dihedralParms: dihedralParms[ijkl] = dihedralParms[lkjiType] - elif jkDefault in dihedralParms: - dihedralParms[ijkl] = dihedralParms[jkDefault] - elif kjDefault in dihedralParms: - dihedralParms[ijkl] = dihedralParms[kjDefault] else: raise Error('Dihedral parms for atoms ' + dihedral + ' not found.') @@ -1224,14 +1335,14 @@ def writeMcf(configFile, mcfFile, mcf = open(mcfFile, 'w') mcf.write('!***************************************' + - '****************************************\n') + '****************************************\n') mcf.write('!Molecular connectivity file for ' + configFile + '\n') mcf.write('!***************************************' + - '****************************************\n') + '****************************************\n') mcf.write('!Atom Format\n') mcf.write('!index type element mass charge vdw_type parameters\n' + - '!vdw_type="LJ", parms=epsilon sigma\n' + + '!vdw_type="LJ", parms=epsilon sigma\n' + '!vdw_type="Mie", parms=epsilon sigma repulsion_exponent dispersion_exponent\n') mcf.write('\n# Atom_Info\n') mcf.write(str(len(atomList))+'\n') @@ -1245,7 +1356,7 @@ def writeMcf(configFile, mcfFile, for i in range(1,len(atomParms[pdbIndex]['vdw'])): mcf.write(' %8.3f' % atomParms[pdbIndex]['vdw'][i]) for ring in ringList: - if pdbIndex in ring: mcf.write(' ring') + if str(pdbIndex) in ring: mcf.write(' ring') mcf.write('\n') if bondList: @@ -1256,7 +1367,7 @@ def writeMcf(configFile, mcfFile, mcf.write(str(len(bondList)) + '\n') nBond = 0 for bond in bondList: - ij = bond + ij = tuple([int(i) for i in bond.split()]) #pdb indices if ij not in bondParms: ij = ij[::-1] ijMcf = tuple([atomList.index(i)+1 for i in ij]) #mcf indices nBond = nBond + 1 @@ -1274,7 +1385,7 @@ def writeMcf(configFile, mcfFile, mcf.write(str(len(angleList)) + '\n') nAngle = 0 for angle in angleList: - ijk = angle + ijk = tuple([int(i) for i in angle.split()]) #pdb indices if ijk not in angleParms: ijk = ijk[::-1] ijkMcf = tuple([atomList.index(i)+1 for i in ijk]) #mcf indices nAngle = nAngle + 1 @@ -1297,7 +1408,7 @@ def writeMcf(configFile, mcfFile, mcf.write(str(len(dihedralList)) + '\n') nDihedral = 0 for dihedral in dihedralList: - ijkl = dihedral + ijkl = tuple([int(i) for i in dihedral.split()]) #pdb indices if ijkl not in dihedralParms: ijkl = ijkl[::-1] ijklMcf = tuple([atomList.index(i)+1 for i in ijkl]) #mcf indices nDihedral = nDihedral + 1 @@ -1371,9 +1482,9 @@ def writeMcf(configFile, mcfFile, #******************************************************************************* # MAIN PROGRAM BEGINS HERE #******************************************************************************* -# initialize lists +#INITIALIZE LISTS AND VARIABLES +genScannedList=[] ringList=[] -wRingList=[] fragList=[] angleList=[] dihedralList=[] @@ -1384,49 +1495,64 @@ def writeMcf(configFile, mcfFile, spacing=" " fragConn=[] listofnames=[] -cyclic_ua_atom = False +cyclic_ua_atom = True + +#RING SCAN +#Initialize returns the intial atom +#to start the ring scan +#If molecule is argon, it initial atom is empty +initialatom=initialize(configFile) +if initialatom == '': + tempfile = open('temporary.temp','w') + tempfile.write('1') + tempfile.close() + initialatom = '1' + +scan_result = scan(initialatom,[],False) +if cyclic_ua_atom==True: + #Clean "ghost atom" from temporary_file + tempfile2=open('temporary.temp2','w') + tempfile1=open('temporary.temp','r') + for line in tempfile1: + this_line_new = line.split() + if this_line_new[-1] == '0': + for each_atom in this_line_new[0:-1]: + tempfile2.write(each_atom+" ") + tempfile2.write("\n") + elif '0 1' in line: + continue + else: + tempfile2.write(line) + tempfile2.close() + tempfile1.close() + os.system('mv temporary.temp2 temporary.temp') -# read configFile -print("Reading Modified PDB File...") -atomList, atomParms, atomConnect, bondList, numAtomTypes = readPdb(configFile) - -# ring scan -if len(bondList) > 1: - if args.verbose: - print("%-5s%-40s" % ('Atom', 'Comment')) - print("---- -----------------------------------") - scannedList = [atomConnect['startRingID']] - scan_result = ringID(0) - if args.verbose: - print("---- -----------------------------------") - -# print summary +#IDENTIFY BONDS, FRAGMENTS, ANGLES, DIHEDRALS +bondID() +fragID() +angleID() +dihedralID() +fragConnectivity() + +#PRESENT SCAN SUMMARY TO USER print("\n\n*********Generation of Topology File*********\n") -print("Summary") -print("---- -----------------------------------") -print("%4d %-40s" % (len(bondList), "bonds")) +print("Summary: ") +print("There are " + str(len(bondList)) + " bonds identified.") if cyclic_ua_atom == True and len(scan_result[1]) > 2: print("Cyclic united atom molecule with no branches") else: - comment = "rings" - if len(ringList) > 0 and args.verbose: - comment += ":" - for ring in ringList: - comment += ' [' + ",".join([str(i) for i in ring]) + ']' - print("%4d %-40s" % (len(ringList), comment)) + print("There are " + str(len(ringList)) + " rings identified. These rings are: ") -# id angles, dihedrals, fragments -angleID() -print("%4d %-40s" % (len(angleList), "angles")) -dihedralID() -print("%4d %-40s" % (len(dihedralList), "dihedrals")) -fragID() -print("%4d %-40s" % (len(fragList), "fragments")) -fragConnectivity() -print("---- -----------------------------------") + for row in ringList: + print(row) + print("There are " + str(len(fragList)) + " fragments identified") +print("There are " + str(len(angleList)) + " angles identified") +print("There are " + str(len(dihedralList)) + " dihedrals identified") +print("Reading Modified PDB File...") +atomList, atomParms, numAtomTypes = readPdb(configFile) if ffTemplate: #GENERATE BLANK FORCEFIELD TEMPLATE if os.path.isfile(ffFile) and not os.path.isfile(ffFile + '.BAK'): @@ -1451,13 +1577,14 @@ def writeMcf(configFile, mcfFile, # We need to make sure all the atomNumber indices are populated. atomParms, bondParms, angleParms, dihedralParms = \ checkParms(atomList, bondList, angleList, dihedralList, - atomParms, bondParms, angleParms, dihedralParms) + atomParms, bondParms, angleParms, dihedralParms) # Got all the parms we need? write Mcf. writeMcf(configFile, mcfFile, - atomList, bondList, angleList, dihedralList, ringList, - atomParms, bondParms, angleParms, dihedralParms, improperParms, scaling_1_4) + atomList, bondList, angleList, dihedralList, ringList, + atomParms, bondParms, angleParms, dihedralParms, improperParms, scaling_1_4) +os.system("rm temporary.temp") if infilename_type == 'cml': os.system("rm " + configFile) @@ -1466,4 +1593,4 @@ def writeMcf(configFile, mcfFile, warnings.showwarning("\n\nSupport for Python2 is deprecated in " "Cassandra and will be removed in a future release. Please " "consider switching to Python3.\n\n", DeprecationWarning, - 'library_setup.py', 1465) + 'mcfgen.py', 1592)