def _assignNamesFromForceFieldTemplates(topology, system, openmm_forcefields_to_use): ''' Assign Amber atom and residue names from specified forcefield templates. Requires OpenMM version >= 5.2. ARGUMENTS topology system openmm_forcefields_to_use (list of strings) - list of XML files of forcefields containing templates RETURNS atoms - list of atom information sorted_atom_indices (list of int) - atom indices in order that they appear in forcefield templates, in case re-sorting is required ''' # Assign AMBER atom and residue names. from simtk.openmm.app import ForceField import simtk.openmm.app.forcefield forcefield = ForceField(*openmm_forcefields_to_use) data = ForceField._SystemData() atomIndices = {} for index, atom in enumerate(topology.atoms()): data.atoms.append(atom) atomIndices[atom] = index # Make a list of all bonds for bond in topology.bonds(): if bond[0] in atomIndices and bond[1] in atomIndices: data.bonds.append(ForceField._BondData(atomIndices[bond[0]], atomIndices[bond[1]])) # Record which atoms are bonded to each other atom bondedToAtom = [] for i in range(len(data.atoms)): bondedToAtom.append(set()) data.atomBonds.append([]) for i in range(len(data.bonds)): bond = data.bonds[i] bondedToAtom[bond.atom1].add(bond.atom2) bondedToAtom[bond.atom2].add(bond.atom1) data.atomBonds[bond.atom1].append(i) data.atomBonds[bond.atom2].append(i) # Find the template matching each residue and assign atom types. sorted_atom_indices = list() for chain in topology.chains(): for res in chain.residues(): template = None matches = None # NOTE: Before OpenMM 5.2, was necessary to convert output of _createResidueSignature using the method simtk.openmm.app.forcefield._signatureToString - this is now done within _createResidueSignature sig = simtk.openmm.app.forcefield._createResidueSignature([atom.element for atom in res.atoms()]) if sig != '': if sig in forcefield._templateSignatures: for t in forcefield._templateSignatures[sig]: # NOTE: No longer necessary to pass atomIndices in OpenMM > 5.2 matches = simtk.openmm.app.forcefield._matchResidue(res, t, bondedToAtom) if matches is not None: template = t break if matches is None: # Check templates involving virtual sites for t in forcefield._templateSignatures[None]: matches = simtk.openmm.app.forcefield._matchResidue(res, t, bondedToAtom) if matches is not None: template = t break if matches is None: raise ValueError('No template found for residue %d (%s). This might mean your input topology is missing some atoms or bonds, or possibly that you are using the wrong force field.' % (res.index+1, res.name)) # Sort matches by order in template. atom_indices = [ atom.index for atom in res.atoms() ] for local_index in range(len(template.atoms)): if local_index in matches: sorted_atom_indices.append(min(atom_indices) + matches.index(local_index)) for (atom, match) in zip(res.atoms(), matches): data.atomType[atom] = template.atoms[match].type # Rename atom (JDC). atom.name = template.atoms[match].name atom.resname = template.name for site in template.virtualSites: if match == site.index: data.virtualSites[atom] = site return (data.atoms, sorted_atom_indices)