def test_amber_binary_mixture(): sustiva_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") etoh_filename = utils.get_data_filename("chemicals/etoh/etoh_renamed.mol2") trj0, trj1 = md.load(sustiva_filename), md.load(etoh_filename) # Hack to assign unique residue names that are consistent with contents of mol2 files trj0.top.residue(0).name = "LIG" trj1.top.residue(0).name = "LI2" trj_list = [trj0, trj1] with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. box_filename = "./box.pdb" box_trj = packmol.pack_box(trj_list, [25, 50]) box_trj.save(box_filename) gaff_mol2_filename0, frcmod_filename0 = amber.run_antechamber( "sustiva", sustiva_filename, charge_method=None) gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber( "etoh", etoh_filename, charge_method=None) mol2_filenames = [gaff_mol2_filename0, gaff_mol2_filename1] frcmod_filenames = [frcmod_filename0, frcmod_filename1] prmtop_filename = "./out.prmtop" inpcrd_filename = "./out.inpcrd" tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, box_filename, prmtop_filename, inpcrd_filename) print(tleap_cmd)
def test_gromacs_merge(): etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") benzene_filename = utils.get_data_filename("chemicals/benzene/benzene.mol2") with utils.enter_temp_directory(): #Prevents creating lots of tleap/antechamber files everywhere #Generate frcmod files, mol2 files gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber( "etoh", etoh_filename, charge_method = None) gaff_mol2_filename2, frcmod_filename2 = amber.run_antechamber( "benzene", benzene_filename, charge_method = None) #Set file names prmtop_filename1 = "./out1.prmtop" prmtop_filename2 = "./out2.prmtop" crd_filename1 = "./out1.inpcrd" crd_filename2 = "./out2.inpcrd" top_filename1 = "./out1.top" top_filename2 = "./out2.top" gro_filename1 = "./out1.gro" gro_filename2 = "./out2.gro" #Generate AMBER files amber.run_tleap( 'etoh', gaff_mol2_filename1, frcmod_filename1, prmtop_filename1, crd_filename1 ) amber.run_tleap( 'benzene', gaff_mol2_filename2, frcmod_filename2, prmtop_filename2, crd_filename2 ) #Convert to GROMACS utils.convert_via_acpype( "etoh", prmtop_filename1, crd_filename1, out_top = top_filename1, out_gro = gro_filename1 ) utils.convert_via_acpype( "benzene", prmtop_filename2, crd_filename2, out_top = top_filename2, out_gro = gro_filename2 ) #Merge topologies gromacs.merge_topologies( [ top_filename1, top_filename2], './combined.top', 'combined', molecule_numbers = [1, 5], molecule_names = ['etoh', 'benzene'] ) #Test editing of molecule numbers in topology file gromacs.change_molecules_section( './combined.top', './edited.top', ['etoh', 'benzene'], [10, 20] )
def test_amber_binary_mixture(): sustiva_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") etoh_filename = utils.get_data_filename("chemicals/etoh/etoh_renamed.mol2") trj0, trj1 = md.load(sustiva_filename), md.load(etoh_filename) # Hack to assign unique residue names that are consistent with contents of mol2 files trj0.top.residue(0).name = "LIG" trj1.top.residue(0).name = "LI2" trj_list = [trj0, trj1] with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. box_filename = "./box.pdb" box_trj = packmol.pack_box(trj_list, [25, 50]) box_trj.save(box_filename) gaff_mol2_filename0, frcmod_filename0 = amber.run_antechamber("sustiva", sustiva_filename, charge_method=None) gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber("etoh", etoh_filename, charge_method=None) mol2_filenames = [gaff_mol2_filename0, gaff_mol2_filename1] frcmod_filenames = [frcmod_filename0, frcmod_filename1] prmtop_filename = "./out.prmtop" inpcrd_filename = "./out.inpcrd" tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, box_filename, prmtop_filename, inpcrd_filename) print(tleap_cmd)
def test_gromacs_merge(): etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") benzene_filename = utils.get_data_filename("chemicals/benzene/benzene.mol2") with utils.enter_temp_directory(): #Prevents creating lots of tleap/antechamber files everywhere #Generate frcmod files, mol2 files gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber( "etoh", etoh_filename, charge_method = None) gaff_mol2_filename2, frcmod_filename2 = amber.run_antechamber( "benzene", benzene_filename, charge_method = None) #Set file names prmtop_filename1 = "./out1.prmtop" prmtop_filename2 = "./out2.prmtop" crd_filename1 = "./out1.inpcrd" crd_filename2 = "./out2.inpcrd" top_filename1 = "./out1.top" top_filename2 = "./out2.top" gro_filename1 = "./out1.gro" gro_filename2 = "./out2.gro" #Generate AMBER files amber.run_tleap( 'etoh', gaff_mol2_filename1, frcmod_filename1, prmtop_filename1, crd_filename1 ) amber.run_tleap( 'benzene', gaff_mol2_filename2, frcmod_filename2, prmtop_filename2, crd_filename2 ) #Convert to GROMACS utils.amber_to_gromacs( "etoh", prmtop_filename1, crd_filename1, out_top = top_filename1, out_gro = gro_filename1 ) utils.amber_to_gromacs( "benzene", prmtop_filename2, crd_filename2, out_top = top_filename2, out_gro = gro_filename2 ) #Merge topologies gromacs.merge_topologies( [ top_filename1, top_filename2], './combined.top', 'combined', molecule_numbers = [1, 5], molecule_names = ['etoh', 'benzene'] ) #Test editing of molecule numbers in topology file gromacs.change_molecules_section( './combined.top', './edited.top', ['etoh', 'benzene'], [10, 20] )
def test_acpype_conversion(): molecule_name = 'sustiva' input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber(molecule_name, input_filename, charge_method=None) prmtop, inpcrd = amber.run_tleap(molecule_name, gaff_mol2_filename, frcmod_filename) out_top, out_gro = utils.convert_via_acpype( molecule_name, prmtop, inpcrd )
def test_amber_box(): etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") trj_list = [md.load(etoh_filename)] with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. box_filename = "./box.pdb" box_trj = packmol.pack_box(trj_list, [50]) box_trj.save(box_filename) gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber( "etoh", etoh_filename, charge_method=None) mol2_filenames = [gaff_mol2_filename1] frcmod_filenames = [frcmod_filename1] prmtop_filename = "./out.prmtop" inpcrd_filename = "./out.inpcrd" tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, box_filename, prmtop_filename, inpcrd_filename) print(tleap_cmd)
def test_run_antechamber_charges(): molecule_name = "acetate" input_filename = utils.get_data_filename("chemicals/acetate/acetate.mol2") with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber( molecule_name, input_filename, charge_method=None, net_charge=-1)
def oemols_to_ffxml(molecules, base_molecule_name="lig"): """Generate an OpenMM ffxml object and MDTraj trajectories from multiple OEMols Parameters ---------- molecules : list(OEMole) Molecules WITH CHARGES. Each can have multiple conformations. WILL GIVE UNDEFINED RESULTS IF NOT CHARGED. base_molecule_name : str, optional, default='lig' Base name of molecule to use inside parameter files. Returns ------- trajectories : list(mdtraj.Trajectory) List of MDTraj Trajectories for molecule. May contain multiple frames ffxml : StringIO StringIO representation of ffxml file. Notes ----- We allow multiple different molecules at once so that they can all be included in a single ffxml file, which is currently the only recommended way to simulate multiple GAFF molecules in a single simulation. For most applications, you will have just a single molecule: e.g. molecules = [my_oemol] The resulting ffxml StringIO object can be directly input to OpenMM e.g. `forcefield = app.ForceField(ffxml)` This will generate a lot of temporary files, so you may want to use utils.enter_temp_directory() to avoid clutter. """ all_trajectories = [] gaff_mol2_filenames = [] frcmod_filenames = [] print(os.getcwd()) for i, molecule in enumerate(molecules): trajectories = [] for j in range(molecule.NumConfs()): molecule_name = "%s-%d-%d" % (base_molecule_name, i, j) mol2_filename = "./%s.mol2" % molecule_name _unused = molecule_to_mol2(molecule, mol2_filename, conformer=j) gaff_mol2_filename, frcmod_filename = run_antechamber( molecule_name, mol2_filename, charge_method=None ) # It's redundant to run antechamber on each frame, fix me later. traj = md.load(gaff_mol2_filename) trajectories.append(traj) if j == 0: # Only need 1 frame of forcefield files gaff_mol2_filenames.append(gaff_mol2_filename) frcmod_filenames.append(frcmod_filename) # Create a trajectory with all frames of the current molecule traj = trajectories[0].join(trajectories[1:]) all_trajectories.append(traj) ffxml = create_ffxml_file(gaff_mol2_filenames, frcmod_filenames, override_mol2_residue_name=base_molecule_name) return all_trajectories, ffxml
def test_run_antechamber_resname(): molecule_name = "sustiva" input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber(molecule_name, input_filename, charge_method=None, resname=True) with open(gaff_mol2_filename, 'r') as fin: fin.readline() assert fin.readline().strip() == molecule_name
def oemols_to_ffxml(molecules, base_molecule_name="lig"): """Generate an OpenMM ffxml object and MDTraj trajectories from multiple OEMols Parameters ---------- molecules : list(OEMole) Molecules WITH CHARGES. Each can have multiple conformations. WILL GIVE UNDEFINED RESULTS IF NOT CHARGED. base_molecule_name : str, optional, default='lig' Base name of molecule to use inside parameter files. Returns ------- trajectories : list(mdtraj.Trajectory) List of MDTraj Trajectories for molecule. May contain multiple frames ffxml : StringIO StringIO representation of ffxml file. Notes ----- We allow multiple different molecules at once so that they can all be included in a single ffxml file, which is currently the only recommended way to simulate multiple GAFF molecules in a single simulation. For most applications, you will have just a single molecule: e.g. molecules = [my_oemol] The resulting ffxml StringIO object can be directly input to OpenMM e.g. `forcefield = app.ForceField(ffxml)` This will generate a lot of temporary files, so you may want to use utils.enter_temp_directory() to avoid clutter. """ all_trajectories = [] gaff_mol2_filenames = [] frcmod_filenames = [] print(os.getcwd()) for i, molecule in enumerate(molecules): trajectories = [] for j in range(molecule.NumConfs()): molecule_name = "%s-%d-%d" % (base_molecule_name, i, j) mol2_filename = "./%s.mol2" % molecule_name _unused = molecule_to_mol2(molecule, mol2_filename, conformer=j) gaff_mol2_filename, frcmod_filename = run_antechamber(molecule_name, mol2_filename, charge_method=None) # It's redundant to run antechamber on each frame, fix me later. traj = md.load(gaff_mol2_filename) trajectories.append(traj) if j == 0: # Only need 1 frame of forcefield files gaff_mol2_filenames.append(gaff_mol2_filename) frcmod_filenames.append(frcmod_filename) # Create a trajectory with all frames of the current molecule traj = trajectories[0].join(trajectories[1:]) all_trajectories.append(traj) ffxml = create_ffxml_file(gaff_mol2_filenames, frcmod_filenames, override_mol2_residue_name=base_molecule_name) return all_trajectories, ffxml
def test_amber_water_mixture(): water_filename = utils.get_data_filename("chemicals/water/water.mol2") etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") sustiva_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. shutil.copy( water_filename, 'c1.mol2' ) shutil.copy( etoh_filename, 'c2.mol2' ) shutil.copy( sustiva_filename, 'c3.mol2') water_filename = 'c1.mol2' etoh_filename = 'c2.mol2' sustiva_filename = 'c3.mol2' #Randomize residue names to avoid clashes utils.randomize_mol2_residue_names( [ water_filename, etoh_filename, sustiva_filename] ) trj0, trj1, trj2 = md.load(water_filename), md.load(etoh_filename), md.load(sustiva_filename) trj_list = [trj0, trj1, trj2] box_filename = "./box.pdb" box_trj = packmol.pack_box(trj_list, [300, 25, 3]) box_trj.save(box_filename) gaff_mol2_filename0, frcmod_filename0 = amber.run_antechamber("water", water_filename, charge_method=None) gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber("etoh", etoh_filename, charge_method=None) gaff_mol2_filename2, frcmod_filename2 = amber.run_antechamber("sustiva", sustiva_filename, charge_method=None) mol2_filenames = [gaff_mol2_filename0, gaff_mol2_filename1, gaff_mol2_filename2] frcmod_filenames = [frcmod_filename0, frcmod_filename1, frcmod_filename2] prmtop_filename = "./out.prmtop" inpcrd_filename = "./out.inpcrd" shutil.copy(box_filename, 'renamed.pdb') tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, 'renamed.pdb', prmtop_filename, inpcrd_filename) print(tleap_cmd) #Also do here for case of GAFF water tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, box_filename, prmtop_filename, inpcrd_filename, water_model = None) print(tleap_cmd) #Also do here for case of SPC tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, 'renamed.pdb', prmtop_filename, inpcrd_filename, water_model = 'SPC') print(tleap_cmd)
def smiles_to_antechamber(smiles_string, gaff_mol2_filename, frcmod_filename, residue_name="MOL", strictStereo=False, protonation=False): """Build a molecule from a smiles string and run antechamber, generating GAFF mol2 and frcmod files from a smiles string. Charges will be generated using the OpenEye QuacPac AM1-BCC implementation. Parameters ---------- smiles_string : str Smiles string of molecule to construct and charge gaff_mol2_filename : str Filename of mol2 file output of antechamber, with charges created from openeye frcmod_filename : str Filename of frcmod file output of antechamber. Most likely this file will be almost empty, at least for typical molecules. residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. This might be useful for downstream applications if the residue names are required to be unique. strictStereo : bool, optional, default=False If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html protonation : bool, optional, default=False If True, uses OESetNeutralpHModel to set a pH model for the molecule to attempt to obtain protonation states appropriate for neutral pH. Depending on the application this may or may not be what you want, e.g. for hydration free energy calculations you may want the typical depicted (neutral) form. """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for oechem!")) oequacpac = import_("openeye.oequacpac") # Get the absolute path so we can find these filenames from inside a temporary directory. gaff_mol2_filename = os.path.abspath(gaff_mol2_filename) frcmod_filename = os.path.abspath(frcmod_filename) m = smiles_to_oemol(smiles_string) if protonation: oequacpac.OESetNeutralpHModel(m) m = get_charges(m, strictStereo=strictStereo, keep_confs=1) with enter_temp_directory( ): # Avoid dumping 50 antechamber files in local directory. _unused = molecule_to_mol2(m, "./tmp.mol2", residue_name=residue_name) net_charge = oechem.OENetCharge(m) tmp_gaff_mol2_filename, tmp_frcmod_filename = run_antechamber( "tmp", "./tmp.mol2", charge_method=None, net_charge=net_charge) # USE OE AM1BCC charges! shutil.copy(tmp_gaff_mol2_filename, gaff_mol2_filename) shutil.copy(tmp_frcmod_filename, frcmod_filename)
def test_run_tleap(): molecule_name = "sustiva" input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber( molecule_name, input_filename, charge_method=None) prmtop, inpcrd = amber.run_tleap(molecule_name, gaff_mol2_filename, frcmod_filename)
def test_run_antechamber_resname(): molecule_name = "sustiva" input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber( molecule_name, input_filename, charge_method=None, resname=True) with open(gaff_mol2_filename, 'r') as fin: fin.readline() assert fin.readline().strip() == molecule_name
def test_acpype_conversion(): molecule_name = 'sustiva' input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber( molecule_name, input_filename, charge_method=None) prmtop, inpcrd = amber.run_tleap(molecule_name, gaff_mol2_filename, frcmod_filename) out_top, out_gro = utils.convert_via_acpype(molecule_name, prmtop, inpcrd)
def test_gromacs_solvate(): etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") with utils.enter_temp_directory(): #Prevents creating lots of tleap/antechamber files everywhere #Generate frcmod files, mol2 files gaff_mol2_filename, frcmod_filename = amber.run_antechamber( "etoh", etoh_filename, charge_method = None) #Amber setup amber.run_tleap( 'etoh', gaff_mol2_filename, frcmod_filename, 'etoh.prmtop', 'etoh.crd' ) #GROMACS conversion utils.convert_via_acpype( 'etoh', 'etoh.prmtop', 'etoh.crd', 'etoh.top', 'etoh.gro' ) #Solvate gromacs.do_solvate( 'etoh.top', 'etoh.gro', 'etoh_solvated.top', 'etoh_solvated.gro', 1.2, 'dodecahedron', 'spc216', 'tip3p.itp' )
def test_gromacs_solvate(): etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") with utils.enter_temp_directory(): #Prevents creating lots of tleap/antechamber files everywhere #Generate frcmod files, mol2 files gaff_mol2_filename, frcmod_filename = amber.run_antechamber( "etoh", etoh_filename, charge_method = None) #Amber setup amber.run_tleap( 'etoh', gaff_mol2_filename, frcmod_filename, 'etoh.prmtop', 'etoh.crd' ) #GROMACS conversion utils.amber_to_gromacs( 'etoh', 'etoh.prmtop', 'etoh.crd', 'etoh.top', 'etoh.gro' ) #Solvate gromacs.do_solvate( 'etoh.top', 'etoh.gro', 'etoh_solvated.top', 'etoh_solvated.gro', 1.2, 'dodecahedron', 'spc216', 'tip3p.itp' )
def smiles_to_antechamber(smiles_string, gaff_mol2_filename, frcmod_filename, residue_name="MOL", strictStereo=False, protonation=False): """Build a molecule from a smiles string and run antechamber, generating GAFF mol2 and frcmod files from a smiles string. Charges will be generated using the OpenEye QuacPac AM1-BCC implementation. Parameters ---------- smiles_string : str Smiles string of molecule to construct and charge gaff_mol2_filename : str Filename of mol2 file output of antechamber, with charges created from openeye frcmod_filename : str Filename of frcmod file output of antechamber. Most likely this file will be almost empty, at least for typical molecules. residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. This might be useful for downstream applications if the residue names are required to be unique. strictStereo : bool, optional, default=False If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html protonation : bool, optional, default=False If True, uses OESetNeutralpHModel to set a pH model for the molecule to attempt to obtain protonation states appropriate for neutral pH. Depending on the application this may or may not be what you want, e.g. for hydration free energy calculations you may want the typical depicted (neutral) form. """ oechem = import_("openeye.oechem") if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for oechem!")) oequacpac = import_("openeye.oequacpac") # Get the absolute path so we can find these filenames from inside a temporary directory. gaff_mol2_filename = os.path.abspath(gaff_mol2_filename) frcmod_filename = os.path.abspath(frcmod_filename) m = smiles_to_oemol(smiles_string) if protonation: oequacpac.OESetNeutralpHModel(m) m = get_charges(m, strictStereo=strictStereo, keep_confs=1) with enter_temp_directory(): # Avoid dumping 50 antechamber files in local directory. _unused = molecule_to_mol2(m, "./tmp.mol2", residue_name=residue_name) net_charge = oechem.OENetCharge(m) tmp_gaff_mol2_filename, tmp_frcmod_filename = run_antechamber("tmp", "./tmp.mol2", charge_method=None, net_charge=net_charge) # USE OE AM1BCC charges! shutil.copy(tmp_gaff_mol2_filename, gaff_mol2_filename) shutil.copy(tmp_frcmod_filename, frcmod_filename)
def test_parmed_conversion(): molecule_name = 'sustiva' input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. #Make sure conversion runs gaff_mol2_filename, frcmod_filename = amber.run_antechamber( molecule_name, input_filename, charge_method=None) prmtop, inpcrd = amber.run_tleap(molecule_name, gaff_mol2_filename, frcmod_filename) out_top, out_gro = utils.amber_to_gromacs(molecule_name, prmtop, inpcrd, precision=8) #Test energies before and after conversion #Set up amber system a = parmed.amber.AmberParm(prmtop, inpcrd) ambersys = a.createSystem() ambercon = mmmm.Context(ambersys, mm.VerletIntegrator(0.001)) ambercon.setPositions(a.positions) #Set up GROMACS system g = parmed.load_file(out_top) gro = parmed.gromacs.GromacsGroFile.parse(out_gro) g.box = gro.box g.positions = gro.positions gromacssys = g.createSystem() gromacscon = mmmm.Context(gromacssys, mm.VerletIntegrator(0.001)) gromacscon.setPositions(g.positions) #Check energies a_energies = parmed.openmm.utils.energy_decomposition(a, ambercon) g_energies = parmed.openmm.utils.energy_decomposition(g, gromacscon) #Check components tolerance = 1e-5 ok = True for key in a_energies.keys(): diff = np.abs(a_energies[key] - g_energies[key]) if diff / np.abs(a_energies[key]) > tolerance: ok = False print( "In testing AMBER to GROMACS conversion, %s energy differs by %.5g, which is more than a fraction %.2g of the total, so conversion appears not to be working properly." % (key, diff, tolerance)) if not ok: raise (ValueError( "AMBER to GROMACS conversion yields energies which are too different." ))
def oemol_to_antechamber(m, gaff_mol2_filename, frcmod_filename, residue_name="MOL", strictStereo=False): """ Build a molecule from a mol2 file and run antechamber, generating GAFF mol2 and frcmod files from a smiles string. Charges will be generated using the OpenEye QuacPac AM1-BCC implementation. Created by hacking openmoltools/openeye.py Parameters ---------- m : oechem molecule object Molecule to construct and charge gaff_mol2_filename : str Filename of mol2 file output of antechamber, with charges created from openeye frcmod_filename : str Filename of frcmod file output of antechamber. Most likely this file will be almost empty, at least for typical molecules. residue_name : str, optional, default="MOL" OpenEye writes mol2 files with <0> as the residue / ligand name. This chokes many mol2 parsers, so we replace it with a string of your choosing. This might be useful for downstream applications if the residue names are required to be unique. strictStereo : bool, optional, default=False If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html """ #oechem = import_("openeye.oechem") #if not oechem.OEChemIsLicensed(): raise(ImportError("Need License for oechem!")) # Get the absolute path so we can find these filenames from inside a temporary directory. gaff_mol2_filename = os.path.abspath(gaff_mol2_filename) frcmod_filename = os.path.abspath(frcmod_filename) m = openeye.get_charges(m, strictStereo=strictStereo, keep_confs=1) with enter_temp_directory(): # Avoid dumping 50 antechamber files in local directory. _unused = openeye.molecule_to_mol2(m, "./tmp.mol2", residue_name=residue_name) net_charge = oechem.OENetCharge(m) tmp_gaff_mol2_filename, tmp_frcmod_filename = amber.run_antechamber("tmp", "./tmp.mol2", charge_method=None, net_charge=net_charge) # USE OE AM1BCC charges! shutil.copy(tmp_gaff_mol2_filename, gaff_mol2_filename) shutil.copy(tmp_frcmod_filename, frcmod_filename)
def test_amber_box(): etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") trj_list = [md.load(etoh_filename)] with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. box_filename = "./box.pdb" box_trj = packmol.pack_box(trj_list, [50]) box_trj.save(box_filename) gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber("etoh", etoh_filename, charge_method=None) mol2_filenames = [gaff_mol2_filename1] frcmod_filenames = [frcmod_filename1] prmtop_filename = "./out.prmtop" inpcrd_filename = "./out.inpcrd" tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, box_filename, prmtop_filename, inpcrd_filename) print(tleap_cmd)
def generate_amber_files(self, ligand_name, file): """ Generates the prmtop and inpcrd files for a ligand. Parameters ---------- ligand_name : str The name of the ligand. file : str Mol2 file of the ligand. Returns ------- prmtop_filename : str Amber prmtop file produced by tleap. inpcrd_filename : str Amber inpcrd file produced by tleap. """ gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber( ligand_name, file, resname=True, charge_method=None) source_mol2 = os.path.abspath(gaff_mol2_filename1) source_frcmod = os.path.abspath(frcmod_filename1) destination = os.path.abspath('data') shutil.move(source_mol2, os.path.join(destination, os.path.basename(source_mol2))) shutil.move(source_frcmod, os.path.join(destination, os.path.basename(source_frcmod))) amber.run_tleap( ligand_name, os.path.join(destination, os.path.basename(source_mol2)), os.path.join(destination, os.path.basename(source_frcmod)), f'{ligand_name}.prmtop', f'{ligand_name}.inpcrd') source_prmtop = os.path.abspath(f'{ligand_name}.prmtop') source_inpcrd = os.path.abspath(f'{ligand_name}.inpcrd') shutil.move(source_prmtop, os.path.join(destination, os.path.basename(source_prmtop))) shutil.move(source_inpcrd, os.path.join(destination, os.path.basename(source_inpcrd))) return os.path.join(destination, os.path.basename(source_prmtop)), os.path.join( destination, os.path.basename(source_inpcrd))
def _gaff2xml(*filenames, **kwargs): """ Use OpenMolTools wrapper to run antechamber programatically and auto parametrize requested molecules. Parameters ---------- filenames: list of str List of the filenames of the molecules to parametrize Returns ------- ffxmls : StringIO Compiled ffxml file produced by antechamber and openmoltools converter """ frcmods, gaffmol2s = [], [] for filename in filenames: name = '.'.join(filename.split('.')[:-1]) gaffmol2, frcmod = run_antechamber(name, filename, **kwargs) frcmods.append(frcmod) gaffmol2s.append(gaffmol2) return create_ffxml_file(gaffmol2s, frcmods)
def test_parmed_conversion(): molecule_name = 'sustiva' input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. #Make sure conversion runs gaff_mol2_filename, frcmod_filename = amber.run_antechamber(molecule_name, input_filename, charge_method=None) prmtop, inpcrd = amber.run_tleap(molecule_name, gaff_mol2_filename, frcmod_filename) out_top, out_gro = utils.amber_to_gromacs( molecule_name, prmtop, inpcrd, precision = 8 ) #Test energies before and after conversion #Set up amber system a = parmed.amber.AmberParm( prmtop, inpcrd ) ambersys = a.createSystem() ambercon = mmmm.Context( ambersys, mm.VerletIntegrator(0.001)) ambercon.setPositions( a.positions ) #Set up GROMACS system g = parmed.load_file( out_top ) gro = parmed.gromacs.GromacsGroFile.parse( out_gro ) g.box = gro.box g.positions = gro.positions gromacssys = g.createSystem() gromacscon = mmmm.Context( gromacssys, mm.VerletIntegrator(0.001)) gromacscon.setPositions( g.positions ) #Check energies a_energies = parmed.openmm.utils.energy_decomposition( a, ambercon ) g_energies = parmed.openmm.utils.energy_decomposition( g, gromacscon ) #Check components tolerance = 1e-5 ok = True for key in a_energies.keys(): diff = np.abs(a_energies[key] - g_energies[key] ) if diff/np.abs(a_energies[key]) > tolerance: ok = False print("In testing AMBER to GROMACS conversion, %s energy differs by %.5g, which is more than a fraction %.2g of the total, so conversion appears not to be working properly." % ( key, diff, tolerance) ) if not ok: raise(ValueError("AMBER to GROMACS conversion yields energies which are too different."))
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 generateForceFieldFromMolecules(molecules, ignoreFailures=False, generateUniqueNames=False, normalize=True, gaff_version='gaff'): """ Generate ffxml file containing additional parameters and residue templates for simtk.openmm.app.ForceField using GAFF/AM1-BCC. This requires the OpenEye toolkit. Parameters ---------- molecules : list of openeye.oechem.OEMol The molecules to be parameterized. All molecules 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. ignoreFailures: bool, optional, default=False Determines whether to add a failed molecule to the list of failed molecules (True), or raise an Exception (False). generateUniqueNames : bool, optional, default=False If True, will generate globally unique names for templates. 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 ------- ffxml : str Contents of ForceField `ffxml` file defining additional parameters from parmchk(2) and residue templates. failed_molecule_list : list of openeye.oechem.OEMol List of the oemols that could not be parameterized. Only returned if ignoreFailures=True Notes ----- This method preserves stereochemistry during AM1-BCC charge parameterization. Residue template names will be set from molecule names. Atom names in molecules will be assigned Tripos atom names if any are blank or not unique. """ if not generateUniqueNames: # Check template names are unique. template_names = set() for molecule in molecules: template_name = molecule.GetTitle() if template_name == '<0>': raise Exception("Molecule '%s' has invalid name" % template_name) if template_name in template_names: raise Exception("Molecule '%s' has template name collision." % template_name) template_names.add(template_name) # Process molecules. import tempfile tmpdir = tempfile.mkdtemp() olddir = os.getcwd() os.chdir(tmpdir) leaprc = "" failed_molecule_list = [] for (molecule_index, molecule) in enumerate(molecules): # Set the template name based on the molecule title. if generateUniqueNames: from uuid import uuid4 template_name = molecule.GetTitle() + '-' + str(uuid4()) else: template_name = molecule.GetTitle() # 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. if not ignoreFailures: molecule = get_charges(molecule, strictStereo=False, keep_confs=1, normalize=normalize) else: try: molecule = get_charges(molecule, strictStereo=False, keep_confs=1, normalize=normalize) except: failed_molecule_list.append(molecule) # Create a unique prefix. prefix = 'molecule%010d' % molecule_index # Create temporary directory for running antechamber. input_mol2_filename = prefix + '.tripos.mol2' gaff_mol2_filename = prefix + '.gaff.mol2' frcmod_filename = prefix + '.frcmod' # Write Tripos mol2 file as antechamber input. _writeMolecule(molecule, input_mol2_filename, standardize=normalize) # Parameterize the molecule with antechamber. run_antechamber(prefix, input_mol2_filename, charge_method=None, net_charge=net_charge, gaff_mol2_filename=gaff_mol2_filename, frcmod_filename=frcmod_filename, gaff_version=gaff_version) # Append to leaprc input for parmed. leaprc += '%s = loadmol2 %s\n' % (prefix, gaff_mol2_filename) leaprc += 'loadamberparams %s\n' % frcmod_filename # Generate ffxml file contents for parmchk-generated frcmod output. leaprc = StringIO(leaprc) params = parmed.amber.AmberParameterSet.from_leaprc(leaprc) params = parmed.openmm.OpenMMParameterSet.from_parameterset(params) ffxml = StringIO() params.write(ffxml) # TODO: Clean up temporary directory. os.chdir(olddir) if ignoreFailures: return ffxml.getvalue(), failed_molecule_list else: return ffxml.getvalue()
def gcrt2prmtop(gcrt_file): system_name=gcrt_file.split('.')[0] amber.run_antechamber(system_name, gcrt_file, charge_method=None, input_format='gcrt') amber.run_tleap(system_name, system_name+'.gaff.mol2',system_name+'.frcmod',system_name+'.prmtop',system_name+'.crd') return None
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 test_amber_water_mixture(): water_filename = utils.get_data_filename("chemicals/water/water.mol2") etoh_filename = utils.get_data_filename("chemicals/etoh/etoh.mol2") sustiva_filename = utils.get_data_filename( "chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory( ): # Prevents creating tons of GAFF files everywhere. shutil.copy(water_filename, 'c1.mol2') shutil.copy(etoh_filename, 'c2.mol2') shutil.copy(sustiva_filename, 'c3.mol2') water_filename = 'c1.mol2' etoh_filename = 'c2.mol2' sustiva_filename = 'c3.mol2' #Randomize residue names to avoid clashes utils.randomize_mol2_residue_names( [water_filename, etoh_filename, sustiva_filename]) trj0, trj1, trj2 = md.load(water_filename), md.load( etoh_filename), md.load(sustiva_filename) trj_list = [trj0, trj1, trj2] box_filename = "./box.pdb" box_trj = packmol.pack_box(trj_list, [300, 25, 3]) box_trj.save(box_filename) gaff_mol2_filename0, frcmod_filename0 = amber.run_antechamber( "water", water_filename, charge_method=None) gaff_mol2_filename1, frcmod_filename1 = amber.run_antechamber( "etoh", etoh_filename, charge_method=None) gaff_mol2_filename2, frcmod_filename2 = amber.run_antechamber( "sustiva", sustiva_filename, charge_method=None) mol2_filenames = [ gaff_mol2_filename0, gaff_mol2_filename1, gaff_mol2_filename2 ] frcmod_filenames = [ frcmod_filename0, frcmod_filename1, frcmod_filename2 ] prmtop_filename = "./out.prmtop" inpcrd_filename = "./out.inpcrd" shutil.copy(box_filename, 'renamed.pdb') tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, 'renamed.pdb', prmtop_filename, inpcrd_filename) print(tleap_cmd) #Also do here for case of GAFF water tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, box_filename, prmtop_filename, inpcrd_filename, water_model=None) print(tleap_cmd) #Also do here for case of SPC tleap_cmd = amber.build_mixture_prmtop(mol2_filenames, frcmod_filenames, 'renamed.pdb', prmtop_filename, inpcrd_filename, water_model='SPC') print(tleap_cmd)
def test_run_tleap(): molecule_name = "sustiva" input_filename = utils.get_data_filename("chemicals/sustiva/sustiva.mol2") with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber(molecule_name, input_filename, charge_method=None) prmtop, inpcrd = amber.run_tleap(molecule_name, gaff_mol2_filename, frcmod_filename)
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 generateForceFieldFromMolecules(molecules): """ Generate ffxml file containing additional parameters and residue templates for simtk.openmm.app.ForceField using GAFF/AM1-BCC. This requires the OpenEye toolkit. Parameters ---------- molecules : list of openeye.oechem.OEMol The molecules to be parameterized. All molecules 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. Returns ------- ffxml : str Contents of ForceField `ffxml` file defining additional parameters from parmchk(2) and residue templates. Notes ----- This method preserves stereochemistry during AM1-BCC charge parameterization. Residue template names will be set from molecule names. Atom names in molecules will be assigned Tripos atom names if any are blank or not unique. """ # Check template names are unique. template_names = set() for molecule in molecules: template_name = molecule.GetTitle() if template_name == '<0>': raise Exception("Molecule '%s' has invalid name" % template_name) if template_name in template_names: raise Exception("Molecule '%s' has template name collision." % template_name) template_names.add(template_name) # Process molecules. import tempfile tmpdir = tempfile.mkdtemp() olddir = os.getcwd() os.chdir(tmpdir) leaprc = "" for (molecule_index, molecule) in enumerate(molecules): # Set the template name based on the molecule title. template_name = molecule.GetTitle() # 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) # Create a unique prefix. prefix = 'molecule%010d' % molecule_index # Create temporary directory for running antechamber. input_mol2_filename = prefix + '.tripos.mol2' gaff_mol2_filename = prefix + '.gaff.mol2' frcmod_filename = prefix + '.frcmod' # Write Tripos mol2 file as antechamber input. _writeMolecule(molecule, input_mol2_filename) # Parameterize the molecule with antechamber. run_antechamber(prefix, input_mol2_filename, charge_method=None, net_charge=net_charge, gaff_mol2_filename=gaff_mol2_filename, frcmod_filename=frcmod_filename) # Append to leaprc input for parmed. leaprc += '%s = loadmol2 %s\n' % (prefix, gaff_mol2_filename) leaprc += 'loadamberparams %s\n' % frcmod_filename # Generate ffxml file contents for parmchk-generated frcmod output. leaprc = StringIO(leaprc) params = parmed.amber.AmberParameterSet.from_leaprc(leaprc) params = parmed.openmm.OpenMMParameterSet.from_parameterset(params) ffxml = StringIO() params.write(ffxml) # TODO: Clean up temporary directory. os.chdir(olddir) return ffxml.getvalue()
def test_run_antechamber_charges(): molecule_name = "acetate" input_filename = utils.get_data_filename("chemicals/acetate/acetate.mol2") with utils.enter_temp_directory(): # Prevents creating tons of GAFF files everywhere. gaff_mol2_filename, frcmod_filename = amber.run_antechamber(molecule_name, input_filename, charge_method=None, net_charge=-1)