def generateTopologyFromOEMol(molecule): """ Generate an OpenMM Topology object from an OEMol molecule. Parameters ---------- molecule : openeye.oechem.OEMol The molecule from which a Topology object is to be generated. Returns ------- topology : simtk.openmm.app.Topology The Topology object generated from `molecule`. """ # Create a Topology object with one Chain and one Residue. from simtk.openmm.app import Topology topology = Topology() chain = topology.addChain() resname = molecule.GetTitle() residue = topology.addResidue(resname, chain) # Create atoms in the residue. for atom in molecule.GetAtoms(): name = atom.GetName() element = Element.getByAtomicNumber(atom.GetAtomicNum()) atom = topology.addAtom(name, element, residue) # Create bonds. atoms = {atom.name: atom for atom in topology.atoms()} for bond in molecule.GetBonds(): topology.addBond(atoms[bond.GetBgn().GetName()], atoms[bond.GetEnd().GetName()]) return topology
def generateTopologyFromOEMol(molecule): """ Generate an OpenMM Topology object from an OEMol molecule. Parameters ---------- molecule : openeye.oechem.OEMol The molecule from which a Topology object is to be generated. Returns ------- topology : simtk.openmm.app.Topology The Topology object generated from `molecule`. """ # Create a Topology object with one Chain and one Residue. from simtk.openmm.app import Topology topology = Topology() chain = topology.addChain() resname = molecule.GetTitle() residue = topology.addResidue(resname, chain) # Create atoms in the residue. for atom in molecule.GetAtoms(): name = atom.GetName() element = Element.getByAtomicNumber(atom.GetAtomicNum()) atom = topology.addAtom(name, element, residue) # Create bonds. atoms = { atom.name : atom for atom in topology.atoms() } for bond in molecule.GetBonds(): topology.addBond(atoms[bond.GetBgn().GetName()], atoms[bond.GetEnd().GetName()]) return topology
def generate_unique_atom_names(molecule): """ Check if an oemol has unique atom names, and if not, then assigns them Parameters ---------- molecule : openeye.oechem.OEMol object oemol object to check Returns ------- molecule : openeye.oechem.OEMol object oemol, either unchanged if atom names are already unique, or newly generated atom names """ atom_names = [] atom_count = 0 for atom in molecule.GetAtoms(): atom_names.append(atom.GetName()) atom_count += 1 if len(set(atom_names)) == atom_count: # one name per atom therefore unique _logger.info(f'molecule {molecule.GetTitle()} \ has unique atom names already') return molecule else: # generating new atom names from collections import defaultdict from simtk.openmm.app.element import Element _logger.info(f'molecule {molecule.GetTitle()} \ does not have unique atom names. Generating now...') element_counts = defaultdict(int) for atom in molecule.GetAtoms(): element = Element.getByAtomicNumber(atom.GetAtomicNum()) element_counts[element._symbol] += 1 name = element._symbol + str(element_counts[element._symbol]) atom.SetName(name) return molecule
def generateResidueTemplate(molecule, residue_atoms=None, normalize=True, gaff_version='gaff'): """ Generate an residue template for simtk.openmm.app.ForceField using GAFF/AM1-BCC. This requires the OpenEye toolkit. Parameters ---------- molecule : openeye.oechem.OEMol The molecule to be parameterized. The molecule must have explicit hydrogens. Net charge will be inferred from the net formal charge on each molecule. Partial charges will be determined automatically using oequacpac and canonical AM1-BCC charging rules. residue_atomset : set of OEAtom, optional, default=None If not None, only the atoms in this set will be used to construct the residue template normalize : bool, optional, default=True If True, normalize the molecule by checking aromaticity, adding explicit hydrogens, and renaming by IUPAC name. gaff_version : str, default = 'gaff' One of ['gaff', 'gaff2']; selects which atom types to use. Returns ------- template : simtk.openmm.app.forcefield._TemplateData Residue template for ForceField using atom types and parameters from `gaff.xml` or `gaff2.xml`. additional_parameters_ffxml : str Contents of ForceField `ffxml` file defining additional parameters from parmchk(2). Notes ----- The residue template will be named after the molecule title. This method preserves stereochemistry during AM1-BCC charge parameterization. Atom names in molecules will be assigned Tripos atom names if any are blank or not unique. """ # Set the template name based on the molecule title plus a globally unique UUID. from uuid import uuid4 template_name = molecule.GetTitle() + '-' + str(uuid4()) # If any atom names are not unique, atom names _ensureUniqueAtomNames(molecule) # Compute net formal charge. net_charge = _computeNetCharge(molecule) # Generate canonical AM1-BCC charges and a reference conformation. molecule = get_charges(molecule, strictStereo=False, keep_confs=1, normalize=normalize) # DEBUG: This may be necessary. molecule.SetTitle('MOL') # Create temporary directory for running antechamber. import tempfile tmpdir = tempfile.mkdtemp() prefix = 'molecule' input_mol2_filename = os.path.join(tmpdir, prefix + '.tripos.mol2') gaff_mol2_filename = os.path.join(tmpdir, prefix + '.gaff.mol2') frcmod_filename = os.path.join(tmpdir, prefix + '.frcmod') # Write Tripos mol2 file as antechamber input. _writeMolecule(molecule, input_mol2_filename, standardize=normalize) # Parameterize the molecule with antechamber. run_antechamber(template_name, input_mol2_filename, charge_method=None, net_charge=net_charge, gaff_mol2_filename=gaff_mol2_filename, frcmod_filename=frcmod_filename, gaff_version=gaff_version) # Read the resulting GAFF mol2 file as a ParmEd structure. from openeye import oechem ifs = oechem.oemolistream(gaff_mol2_filename) ifs.SetFlavor( oechem.OEFormat_MOL2, oechem.OEIFlavor_MOL2_DEFAULT | oechem.OEIFlavor_MOL2_M2H | oechem.OEIFlavor_MOL2_Forcefield) m2h = True oechem.OEReadMolecule(ifs, molecule) ifs.close() # If residue_atoms = None, add all atoms to the residues if residue_atoms == None: residue_atoms = [atom for atom in molecule.GetAtoms()] # Modify partial charges so that charge on residue atoms is integral. residue_charge = 0.0 sum_of_absolute_charge = 0.0 for atom in residue_atoms: charge = atom.GetPartialCharge() residue_charge += charge sum_of_absolute_charge += abs(charge) excess_charge = residue_charge - net_charge if sum_of_absolute_charge == 0.0: sum_of_absolute_charge = 1.0 for atom in residue_atoms: charge = atom.GetPartialCharge() atom.SetPartialCharge(charge + excess_charge * (abs(charge) / sum_of_absolute_charge)) # Create residue template. template = ForceField._TemplateData(template_name) for (index, atom) in enumerate(molecule.GetAtoms()): atomname = atom.GetName() typename = atom.GetType() element = Element.getByAtomicNumber(atom.GetAtomicNum()) charge = atom.GetPartialCharge() parameters = {'charge': charge} atom_template = ForceField._TemplateAtomData(atomname, typename, element, parameters) template.atoms.append(atom_template) for bond in molecule.GetBonds(): if (bond.GetBgn() in residue_atoms) and (bond.GetEnd() in residue_atoms): template.addBondByName(bond.GetBgn().GetName(), bond.GetEnd().GetName()) elif (bond.GetBgn() in residue_atoms) and (bond.GetEnd() not in residue_atoms): template.addExternalBondByName(bond.GetBgn().GetName()) elif (bond.GetBgn() not in residue_atoms) and (bond.GetEnd() in residue_atoms): template.addExternalBondByName(bond.GetEnd().GetName()) # Generate ffxml file contents for parmchk-generated frcmod output. leaprc = StringIO('parm = loadamberparams %s' % frcmod_filename) params = parmed.amber.AmberParameterSet.from_leaprc(leaprc) params = parmed.openmm.OpenMMParameterSet.from_parameterset(params) ffxml = StringIO() params.write(ffxml) return template, ffxml.getvalue()
def generateResidueTemplate(molecule, residue_atoms=None): """ Generate an residue template for simtk.openmm.app.ForceField using GAFF/AM1-BCC. This requires the OpenEye toolkit. Parameters ---------- molecule : openeye.oechem.OEMol The molecule to be parameterized. The molecule must have explicit hydrogens. Charge will be inferred from the net formal charge. residue_atomset : set of OEAtom, optional, default=None If not None, only the atoms in this set will be used to construct the residue template Returns ------- template : simtk.openmm.app.forcefield._TemplateData Residue template for ForceField using atom types and parameters from `gaff.xml`. additional_parameters_ffxml : str Contents of ForceField `ffxml` file defining additional parameters from parmchk(2). Note that this method preserves stereochemistry during AM1-BCC charge parameterization. """ # Generate a unique residue template name to avoid namespace collisions. # TODO: Can we come up with a more intelligent name? #from uuid import uuid4 #template_name = str(uuid4()) template_name = molecule.GetTitle() # Compute net formal charge. from openeye import oechem oechem.OEAssignFormalCharges(molecule) charges = [ atom.GetFormalCharge() for atom in molecule.GetAtoms() ] net_charge = np.array(charges).sum() # Generate canonical AM1-BCC charges and a reference conformation. molecule = get_charges(molecule, strictStereo=False, keep_confs=1) # Create temporary directory for running antechamber. import tempfile tmpdir = tempfile.mkdtemp() input_mol2_filename = os.path.join(tmpdir, template_name + '.tripos.mol2') gaff_mol2_filename = os.path.join(tmpdir, template_name + '.gaff.mol2') frcmod_filename = os.path.join(tmpdir, template_name + '.frcmod') # Write Tripos mol2 file as antechamber input. ofs = oechem.oemolostream(input_mol2_filename) oechem.OEWriteMolecule(ofs, molecule) ofs.close() # Parameterize the molecule with antechamber. run_antechamber(template_name, input_mol2_filename, charge_method=None, net_charge=net_charge, gaff_mol2_filename=gaff_mol2_filename, frcmod_filename=frcmod_filename) # Read the resulting GAFF mol2 file as a ParmEd structure. ifs = oechem.oemolistream(gaff_mol2_filename) ifs.SetFlavor(oechem.OEFormat_MOL2, oechem.OEIFlavor_MOL2_DEFAULT | oechem.OEIFlavor_MOL2_M2H | oechem.OEIFlavor_MOL2_Forcefield) m2h = True oechem.OEReadMolecule(ifs, molecule) ifs.close() # If residue_atoms = None, add all atoms to the residues if residue_atoms == None: residue_atoms = [ atom for atom in molecule.GetAtoms() ] # Modify partial charges so that charge on residue atoms is integral. residue_charge = 0.0 sum_of_absolute_charge = 0.0 for atom in residue_atoms: charge = atom.GetPartialCharge() residue_charge += charge sum_of_absolute_charge += abs(charge) excess_charge = residue_charge - net_charge if sum_of_absolute_charge == 0.0: sum_of_absolute_charge = 1.0 for atom in residue_atoms: charge = atom.GetPartialCharge() atom.SetPartialCharge( charge + excess_charge * (abs(charge) / sum_of_absolute_charge) ) # Create residue template. template = ForceField._TemplateData(template_name) for (index, atom) in enumerate(molecule.GetAtoms()): atomname = atom.GetName() typename = atom.GetType() element = Element.getByAtomicNumber(atom.GetAtomicNum()) charge = atom.GetPartialCharge() parameters = { 'charge' : charge } atom_template = ForceField._TemplateAtomData(atomname, typename, element, parameters) template.atoms.append(atom_template) for bond in molecule.GetBonds(): if (bond.GetBgn() in residue_atoms) and (bond.GetEnd() in residue_atoms): template.addBondByName(bond.GetBgn().GetName(), bond.GetEnd().GetName()) elif (bond.GetBgn() in residue_atoms) and (bond.GetEnd() not in residue_atoms): template.addExternalBondByName(bond.GetBgn().GetName()) elif (bond.GetBgn() not in residue_atoms) and (bond.GetEnd() in residue_atoms): template.addExternalBondByName(bond.GetEnd().GetName()) # Generate ffxml file contents for parmchk-generated frcmod output. leaprc = StringIO("parm = loadamberparams %s" % frcmod_filename) params = parmed.amber.AmberParameterSet.from_leaprc(leaprc) params = parmed.openmm.OpenMMParameterSet.from_parameterset(params) ffxml = StringIO() params.write(ffxml) return template, ffxml.getvalue()
def generateResidueTemplate(molecule, residue_atoms=None, normalize=True, gaff_version='gaff'): """ Generate an residue template for simtk.openmm.app.ForceField using GAFF/AM1-BCC. This requires the OpenEye toolkit. Parameters ---------- molecule : openeye.oechem.OEMol The molecule to be parameterized. The molecule must have explicit hydrogens. Net charge will be inferred from the net formal charge on each molecule. Partial charges will be determined automatically using oequacpac and canonical AM1-BCC charging rules. residue_atomset : set of OEAtom, optional, default=None If not None, only the atoms in this set will be used to construct the residue template normalize : bool, optional, default=True If True, normalize the molecule by checking aromaticity, adding explicit hydrogens, and renaming by IUPAC name. gaff_version : str, default = 'gaff' One of ['gaff', 'gaff2']; selects which atom types to use. Returns ------- template : simtk.openmm.app.forcefield._TemplateData Residue template for ForceField using atom types and parameters from `gaff.xml` or `gaff2.xml`. additional_parameters_ffxml : str Contents of ForceField `ffxml` file defining additional parameters from parmchk(2). Notes ----- The residue template will be named after the molecule title. This method preserves stereochemistry during AM1-BCC charge parameterization. Atom names in molecules will be assigned Tripos atom names if any are blank or not unique. """ # Set the template name based on the molecule title plus a globally unique UUID. from uuid import uuid4 template_name = molecule.GetTitle() + '-' + str(uuid4()) # If any atom names are not unique, atom names _ensureUniqueAtomNames(molecule) # Compute net formal charge. net_charge = _computeNetCharge(molecule) # Generate canonical AM1-BCC charges and a reference conformation. molecule = get_charges(molecule, strictStereo=False, keep_confs=1, normalize=normalize) # DEBUG: This may be necessary. molecule.SetTitle('MOL') # Create temporary directory for running antechamber. import tempfile tmpdir = tempfile.mkdtemp() prefix = 'molecule' input_mol2_filename = os.path.join(tmpdir, prefix + '.tripos.mol2') gaff_mol2_filename = os.path.join(tmpdir, prefix + '.gaff.mol2') frcmod_filename = os.path.join(tmpdir, prefix + '.frcmod') # Write Tripos mol2 file as antechamber input. _writeMolecule(molecule, input_mol2_filename, standardize=normalize) # Parameterize the molecule with antechamber. run_antechamber(template_name, input_mol2_filename, charge_method=None, net_charge=net_charge, gaff_mol2_filename=gaff_mol2_filename, frcmod_filename=frcmod_filename, gaff_version=gaff_version) # Read the resulting GAFF mol2 file as a ParmEd structure. from openeye import oechem ifs = oechem.oemolistream(gaff_mol2_filename) ifs.SetFlavor(oechem.OEFormat_MOL2, oechem.OEIFlavor_MOL2_DEFAULT | oechem.OEIFlavor_MOL2_M2H | oechem.OEIFlavor_MOL2_Forcefield) m2h = True oechem.OEReadMolecule(ifs, molecule) ifs.close() # If residue_atoms = None, add all atoms to the residues if residue_atoms == None: residue_atoms = [ atom for atom in molecule.GetAtoms() ] # Modify partial charges so that charge on residue atoms is integral. residue_charge = 0.0 sum_of_absolute_charge = 0.0 for atom in residue_atoms: charge = atom.GetPartialCharge() residue_charge += charge sum_of_absolute_charge += abs(charge) excess_charge = residue_charge - net_charge if sum_of_absolute_charge == 0.0: sum_of_absolute_charge = 1.0 for atom in residue_atoms: charge = atom.GetPartialCharge() atom.SetPartialCharge( charge + excess_charge * (abs(charge) / sum_of_absolute_charge) ) # Create residue template. template = ForceField._TemplateData(template_name) for (index, atom) in enumerate(molecule.GetAtoms()): atomname = atom.GetName() typename = atom.GetType() element = Element.getByAtomicNumber(atom.GetAtomicNum()) charge = atom.GetPartialCharge() parameters = { 'charge' : charge } atom_template = ForceField._TemplateAtomData(atomname, typename, element, parameters) template.atoms.append(atom_template) for bond in molecule.GetBonds(): if (bond.GetBgn() in residue_atoms) and (bond.GetEnd() in residue_atoms): template.addBondByName(bond.GetBgn().GetName(), bond.GetEnd().GetName()) elif (bond.GetBgn() in residue_atoms) and (bond.GetEnd() not in residue_atoms): template.addExternalBondByName(bond.GetBgn().GetName()) elif (bond.GetBgn() not in residue_atoms) and (bond.GetEnd() in residue_atoms): template.addExternalBondByName(bond.GetEnd().GetName()) # Generate ffxml file contents for parmchk-generated frcmod output. leaprc = StringIO('parm = loadamberparams %s' % frcmod_filename) params = parmed.amber.AmberParameterSet.from_leaprc(leaprc) params = parmed.openmm.OpenMMParameterSet.from_parameterset(params) ffxml = StringIO() params.write(ffxml) return template, ffxml.getvalue()