import sys import math import simtk.openmm.app.element as element import simtk.unit as unit elements = {} for elem in element.Element._elements_by_symbol.values(): num = elem.atomic_number if num not in elements or elem.mass < elements[num].mass: elements[num] = elem OTHER = 0 ATOMS = 1 CONNECT = 2 CONNECTIVITY = 3 RESIDUECONNECT = 4 section = OTHER residueAtoms = {} residueBonds = {} residueConnections = {} types = [] masses = {} resAtomTypes = {} vdwEquivalents = {} vdw = {} charge = {} bonds = [] angles = [] torsions = [] impropers = [] charge14scale = 1.0/1.2 epsilon14scale = 0.5 skipResidues = ['CIO', 'IB'] # "Generic" ions defined by Amber, which are identical to other real ions skipClasses = ['OW', 'HW'] # Skip water atoms, since we define these in separate files def addAtom(residue, atomName, atomClass, element, charge): if residue is None: return residueAtoms[residue].append([atomName, len(types)]) types.append((atomClass, element, charge)) def addBond(residue, atom1, atom2): if residue is None: return residueBonds[residue].append((atom1, atom2)) def addExternalBond(residue, atom): if residue is None: return if atom != -1: residueConnections[residue] += [atom] # Load input files. for inputfile in sys.argv[1:]: if inputfile.endswith('.lib') or inputfile.endswith('.off'): # Read a library file for line in open(inputfile): if line.startswith('!entry'): fields = line.split('.') residue = fields[1] if residue in skipResidues: residue = None continue key = fields[3].split()[0] if key == 'atoms': section = ATOMS residueAtoms[residue] = [] residueBonds[residue] = [] residueConnections[residue] = [] elif key == 'connect': section = CONNECT elif key == 'connectivity': section = CONNECTIVITY elif key == 'residueconnect': section = RESIDUECONNECT else: section = OTHER elif section == ATOMS: fields = line.split() atomName = fields[0][1:-1] atomClass = fields[1][1:-1] if fields[6] == '-1': # Workaround for bug in some Amber files. if atomClass[0] == 'C': elem = elements[6] elif atomClass[0] == 'H': elem = elements[1] else: raise ValueError('Illegal atomic number: '+line) else: elem = elements[int(fields[6])] charge = float(fields[7]) addAtom(residue, atomName, atomClass, elem, charge) elif section == CONNECT: addExternalBond(residue, int(line)-1) elif section == CONNECTIVITY: fields = line.split() addBond(residue, int(fields[0])-1, int(fields[1])-1) elif section == RESIDUECONNECT: # Some Amber files have errors in them, incorrectly listing atoms that should not be # connected in the first two positions. We therefore rely on the "connect" section for # those, using this block only for other external connections. for atom in [int(x)-1 for x in line.split()[2:]]: addExternalBond(residue, atom) elif inputfile.endswith('.in'): lines = open(inputfile).read().split('\n') i = 2 while True: if lines[i].strip() == 'STOP': break i += 1 residue = lines[i].strip() # Hack to get unique residue names, since different files use the same names. if inputfile.endswith('nt.in'): residue = 'N'+residue if inputfile.endswith('ct.in'): residue = 'C'+residue residueAtoms[residue] = [] residueBonds[residue] = [] residueConnections[residue] = [] atoms = [] mainchain = [] i += 7 while len(lines[i].rstrip()) > 0: fields = lines[i].split() atoms.append((fields[1], fields[2], float(fields[10]))) # (name, type, charge) if fields[3] == 'M': mainchain.append(len(atoms)-1) bondedTo = int(fields[4])-4 if bondedTo >= 0: addBond(residue, len(atoms)-1, bondedTo) i += 1 while True: if lines[i].strip() == 'LOOP': i += 1 while len(lines[i].rstrip()) > 0: fields = lines[i].strip().split() bondFrom = [j for j in range(len(atoms)) if fields[0] == atoms[j][0]][0] bondTo = [j for j in range(len(atoms)) if fields[1] == atoms[j][0]][0] addBond(residue, bondFrom, bondTo) i += 1 if lines[i].strip() != 'DONE': i += 1 continue for atom in atoms: try: el = element.Element.getBySymbol(atom[1][0]) except: el = None addAtom(residue, atom[0], atom[1], el, atom[2]) # Hack for figuring out external bonds. We'll have to fix up disulfide bonds and capping groups manually. if len(mainchain) > 0 and not inputfile.endswith('nt.in'): addExternalBond(residue, mainchain[0]) if len(mainchain) > 1 and not inputfile.endswith('ct.in'): addExternalBond(residue, mainchain[-1]) i += 1 break elif inputfile.endswith('.dat'): # Read a force field file. block = 0 continueTorsion = False for line in open(inputfile): line = line.strip() if block == 0: # Title block += 1 elif block == 1: # Mass fields = line.split() if len(fields) == 0: block += 1 else: masses[fields[0]] = float(fields[1]) elif block == 2: # Hydrophilic atoms block += 1 elif block == 3: # Bonds if len(line) == 0: block += 1 elif '-' in line: fields = line[5:].split() bonds.append((line[:2].strip(), line[3:5].strip(), fields[0], fields[1])) elif block == 4: # Angles if len(line) == 0: block += 1 else: fields = line[8:].split() angles.append((line[:2].strip(), line[3:5].strip(), line[6:8].strip(), fields[0], fields[1])) elif block == 5: # Torsions if len(line) == 0: block += 1 else: fields = line[11:].split() periodicity = int(float(fields[3])) if continueTorsion: torsions[-1] += [float(fields[1])/float(fields[0]), fields[2], abs(periodicity)] else: torsions.append([line[:2].strip(), line[3:5].strip(), line[6:8].strip(), line[9:11].strip(), float(fields[1])/float(fields[0]), fields[2], abs(periodicity)]) continueTorsion = (periodicity < 0) elif block == 6: # Improper torsions if len(line) == 0: block += 1 else: fields = line[11:].split() impropers.append((line[:2].strip(), line[3:5].strip(), line[6:8].strip(), line[9:11].strip(), fields[0], fields[1], fields[2])) elif block == 7: # 10-12 hbond potential if len(line) == 0: block += 1 elif block == 8: # VDW equivalents if len(line) == 0: block += 1 else: fields = line.split() for atom in fields[1:]: vdwEquivalents[atom] = fields[0] elif block == 9: # VDW type block += 1 vdwType = line.split()[1] if vdwType not in ['RE', 'AC']: raise ValueError('Nonbonded type (KINDNB) must be RE or AC') elif block == 10: # VDW parameters if len(line) == 0: block += 1 else: fields = line.split() vdw[fields[0]] = (fields[1], fields[2]) else: # Assume it's a frcmod file. block = '' continueTorsion = False first = True for line in open(inputfile): line = line.strip() if len(line) == 0 or first: block = None first = False elif block is None: block = line elif block.startswith('MASS'): fields = line.split() masses[fields[0]] = float(fields[1]) elif block.startswith('BOND'): fields = line[5:].split() bonds.append((line[:2].strip(), line[3:5].strip(), fields[0], fields[1])) elif block.startswith('ANGL'): fields = line[8:].split() angles.append((line[:2].strip(), line[3:5].strip(), line[6:8].strip(), fields[0], fields[1])) elif block.startswith('DIHE'): fields = line[11:].split() periodicity = int(float(fields[3])) if continueTorsion: torsions[-1] += [float(fields[1])/float(fields[0]), fields[2], abs(periodicity)] else: torsions.append([line[:2].strip(), line[3:5].strip(), line[6:8].strip(), line[9:11].strip(), float(fields[1])/float(fields[0]), fields[2], abs(periodicity)]) continueTorsion = (periodicity < 0) elif block.startswith('IMPR'): fields = line[11:].split() impropers.append((line[:2].strip(), line[3:5].strip(), line[6:8].strip(), line[9:11].strip(), fields[0], fields[1], fields[2])) elif block.startswith('NONB'): fields = line.split() vdw[fields[0]] = (fields[1], fields[2]) # Reduce the list of atom types. If multiple hydrogens are bound to the same heavy atom, # they should all use the same type. removeType = [False]*len(types) for res in residueAtoms: if res not in residueBonds: continue atomBonds = [[] for atom in residueAtoms[res]] for bond in residueBonds[res]: atomBonds[bond[0]].append(bond[1]) atomBonds[bond[1]].append(bond[0]) for index, atom in enumerate(residueAtoms[res]): hydrogens = [x for x in atomBonds[index] if types[residueAtoms[res][x][1]][1] == element.hydrogen] for h in hydrogens[1:]: removeType[residueAtoms[res][h][1]] = True residueAtoms[res][h][1] = residueAtoms[res][hydrogens[0]][1] newTypes = [] replaceWithType = [0]*len(types) for i in range(len(types)): if not removeType[i]: newTypes.append(types[i]) replaceWithType[i] = len(newTypes)-1 types = newTypes for res in residueAtoms: for atom in residueAtoms[res]: atom[1] = replaceWithType[atom[1]] # Create the XML output. def fix(atomClass): if atomClass == 'X': return '' return atomClass print "" print " " for index, type in enumerate(types): if type[1] is None: el = "" mass = 0 else: el = type[1].symbol mass = type[1].mass.value_in_unit(unit.amu) print """ """ % (index, type[0], el, mass) print " " print " " for res in sorted(residueAtoms): print """ """ % res for atom in residueAtoms[res]: print " " % tuple(atom) if res in residueBonds: for bond in residueBonds[res]: print """ """ % bond if res in residueConnections: for bond in residueConnections[res]: print """ """ % bond print " " print " " print " " processed = set() for bond in bonds: signature = (bond[0], bond[1]) if signature in processed: continue if any([c in skipClasses for c in signature]): continue processed.add(signature) length = float(bond[3])*0.1 k = float(bond[2])*2*100*4.184 print """ """ % (bond[0], bond[1], str(length), str(k)) print " " print " " processed = set() for angle in angles: signature = (angle[0], angle[1], angle[2]) if signature in processed: continue if any([c in skipClasses for c in signature]): continue processed.add(signature) theta = float(angle[4])*math.pi/180.0 k = float(angle[3])*2*4.184 print """ """ % (angle[0], angle[1], angle[2], str(theta), str(k)) print " " print " " processed = set() for tor in reversed(torsions): signature = (fix(tor[0]), fix(tor[1]), fix(tor[2]), fix(tor[3])) if signature in processed: continue if any([c in skipClasses for c in signature]): continue processed.add(signature) tag = " " print """ """ % (charge14scale, epsilon14scale) sigmaScale = 0.1*2.0/(2.0**(1.0/6.0)) for index, type in enumerate(types): atomClass = type[0] q = type[2] if atomClass in vdwEquivalents: atomClass = vdwEquivalents[atomClass] if atomClass in vdw: params = [float(x) for x in vdw[atomClass]] if vdwType == 'RE': sigma = params[0]*sigmaScale epsilon = params[1]*4.184 else: sigma = (params[0]/params[1])**(1.0/6.0) epsilon = 4.184*params[1]*params[1]/(4*params[0]) else: sigma = 1 epsilon = 0 if sigma == 0 or epsilon == 0: sigma, epsilon = 1, 0 if q != 0 or epsilon != 0: print """ """ % (index, q, sigma, epsilon) print " " print ""