def ParameterizeOE(path): """ Reads in the PDB from 'RunDocking' and outputs 'charged.mol2' of the ligand Then runs antechamber to convert this to coordinate (.inpcrd) and parameter (.prmtop) files. """ from openeye import oechem, oeomega, oequacpac mol = oechem.OEMol() ifs = oechem.oemolistream() if ifs.open(f'{path}/lig.pdb'): oechem.OEReadMolecule(ifs, mol) ifs.close() if not oequacpac.OEAssignCharges(mol, oequacpac.OEAM1BCCCharges()): raise (RuntimeError("OEAssignCharges failed.")) ofs = oechem.oemolostream() if ofs.open(f'{path}/charged.mol2'): oechem.OEWriteMolecule(ofs, mol) import subprocess with working_directory(path): subprocess.check_output( f'antechamber -i lig.pdb -fi pdb -o lig.mol2 -fo mol2 -pf y -an y -a charged.mol2 -fa mol2 -ao crg', shell=True) subprocess.check_output(f'parmchk2 -i lig.mol2 -f mol2 -o lig.frcmod', shell=True) # Wrap tleap with open(f'leap.in', 'w+') as leap: leap.write("source leaprc.protein.ff14SBonlysc\n") leap.write("source leaprc.gaff\n") leap.write("source leaprc.water.tip3p\n") leap.write("set default PBRadii mbondi3\n") leap.write("rec = loadPDB apo.pdb # May need full filepath?\n") leap.write("saveAmberParm rec apo.prmtop apo.inpcrd\n") leap.write("lig = loadmol2 lig.mol2\n") leap.write("loadAmberParams lig.frcmod\n") leap.write("com = combine {rec lig}\n") leap.write("saveAmberParm lig lig.prmtop lig.inpcrd\n") leap.write("saveAmberParm com com.prmtop com.inpcrd\n") leap.write("savepdb com com.pdb\n") leap.write("addIons2 rec NA 0\n") leap.write("addIons2 rec CL 0\n") leap.write("solvateBox rec TIP3PBOX 14\n") leap.write("savepdb rec apo_sol.pdb\n") leap.write("saveAmberParm rec apo_sol.prmtop apo_sol.inpcrd\n") leap.write("addIons2 lig NA 0\n") leap.write("addIons2 lig CL 0\n") leap.write("solvateBox lig TIP3PBOX 14\n") leap.write("savepdb lig lig_sol.pdb\n") leap.write("saveAmberParm lig lig_sol.prmtop lig_sol.inpcrd\n") leap.write("addIons2 com NA 0\n") leap.write("addIons2 com CL 0\n") leap.write("solvateBox com TIP3PBOX 14\n") leap.write("savepdb com com_sol.pdb\n") leap.write("saveAmberParm com com_sol.prmtop com_sol.inpcrd\n") leap.write("quit\n") subprocess.check_output(f'tleap -f leap.in', shell=True)
def test_improper_pyramidal(verbose=False): """Test implement of impropers on ammonia.""" from openeye import oeomega from openeye import oequacpac from oeommtools.utils import oemol_to_openmmTop, openmmTop_to_oemol from openforcefield.utils import get_data_file_path, extractPositionsFromOEMol # Prepare ammonia mol = oechem.OEMol() oechem.OESmilesToMol(mol, 'N') oechem.OEAddExplicitHydrogens(mol) omega = oeomega.OEOmega() omega.SetMaxConfs(100) omega.SetIncludeInput(False) omega.SetStrictStereo(False) # Generate charges chargeEngine = oequacpac.OEAM1BCCCharges() status = omega(mol) oequacpac.OEAssignCharges(mol, chargeEngine) # Assign atom names oechem.OETriposAtomTypes(mol) oechem.OETriposAtomNames(mol) # Set up minimization ff = ForceField('test_forcefields/ammonia_minimal.offxml') topology, positions = oemol_to_openmmTop(mol) system = ff.createSystem(topology, [mol], verbose=verbose) positions = extractPositionsFromOEMol(mol) integrator = openmm.VerletIntegrator(2.0 * unit.femtoseconds) simulation = app.Simulation(topology, system, integrator) simulation.context.setPositions(positions) # Minimize energy simulation.minimizeEnergy() state = simulation.context.getState(getEnergy=True, getPositions=True) energy = state.getPotentialEnergy() / unit.kilocalories_per_mole newpositions = state.getPositions() outmol = openmmTop_to_oemol(topology, state.getPositions()) # Sum angles around the N atom for atom in outmol.GetAtoms(oechem.OEIsInvertibleNitrogen()): aidx = atom.GetIdx() nbors = list(atom.GetAtoms()) ang1 = math.degrees(oechem.OEGetAngle(outmol, nbors[0], atom, nbors[1])) ang2 = math.degrees(oechem.OEGetAngle(outmol, nbors[1], atom, nbors[2])) ang3 = math.degrees(oechem.OEGetAngle(outmol, nbors[2], atom, nbors[0])) ang_sum = math.fsum([ang1, ang2, ang3]) # Check that sum of angles around N is within 1 degree of 353.5 if abs(ang_sum - 353.5) > 1.0: raise Exception( "Improper torsion for ammonia differs too much from reference partly pyramidal geometry." "The reference case has a sum of H-N-H angles (3x) at 353.5 degrees; the test case has a sum of {}." .format(ang_sum))
def prepare_ligand(self, lig): with self.logger("prepare_ligand") as logger: ## prepare ligand oemol = oechem.OEMol(lig) oemol.SetTitle("UNL") oechem.OEAddExplicitHydrogens(oemol) ofs = oechem.oemolostream(f'{self.dirpath}/lig.mol2') oechem.OEWriteMolecule(ofs, oemol) ofs.close() ofs = oechem.oemolostream(f'{self.dirpath}/lig.pdb') oechem.OEWriteMolecule(ofs, oemol) ofs.close() self.lig = oechem.OEMol(oemol) oequacpac.OEAssignCharges(oemol, oequacpac.OEAM1BCCCharges()) ofs = oechem.oemolostream(f'{self.dirpath}/charged.mol2') oechem.OEWriteMolecule(ofs, oemol) ofs.close() self.charged_lig = oechem.OEMol(oemol)
def __setup_system_im(self, pdbfile: str = None): with self.logger("__setup_system_im") as logger: try: with tempfile.TemporaryDirectory() as dirpath: dirpath = self.config.tempdir() # Move inital file over to new system. shutil.copy(pdbfile, f"{dirpath}/init.pdb") # Assign charges and extract new ligand cmd.reinitialize() cmd.load(f'{dirpath}/init.pdb') cmd.remove("polymer") cmd.remove("resn HOH or resn Cl or resn Na") cmd.save(f'{dirpath}/lig.pdb') cmd.save(f'{dirpath}/lig.mol2') ifs = oechem.oemolistream(f'{dirpath}/lig.pdb') oemol = oechem.OEMol() oechem.OEReadMolecule(ifs, oemol) ifs.close() ofs = oechem.oemolostream() oemol.SetTitle("UNL") oechem.OEAddExplicitHydrogens(oemol) oequacpac.OEAssignCharges(oemol, oequacpac.OEAM1BCCCharges()) if ofs.open(f'{dirpath}/charged.mol2'): oechem.OEWriteMolecule(ofs, oemol) ofs.close() # remove hydrogens and ligand from PDB cmd.reinitialize() cmd.load(f'{dirpath}/init.pdb') cmd.remove("not polymer") cmd.remove("hydrogens") cmd.save(f'{dirpath}/apo.pdb') with working_directory(dirpath): subprocess.run( f'antechamber -i lig.pdb -fi pdb -o lig.mol2 -fo mol2 -pf y -an y -a charged.mol2 -fa mol2 -ao crg'.split( " "), check=True, capture_output=True) subprocess.run(f'parmchk2 -i lig.mol2 -f mol2 -o lig.frcmod'.split(" "), check=True, capture_output=True) try: subprocess.run('pdb4amber -i apo.pdb -o apo_new.pdb --reduce --dry'.split(" "), check=True, capture_output=True) except subprocess.CalledProcessError as e: logger.error("Known bug, pdb4amber returns error when there was no error", e.stdout, e.stderr) pass # Wrap tleap with open('leap.in', 'w+') as leap: leap.write("source leaprc.protein.ff14SBonlysc\n") leap.write("source leaprc.phosaa10\n") leap.write("source leaprc.gaff2\n") leap.write("set default PBRadii mbondi3\n") leap.write("rec = loadPDB apo_new.pdb # May need full filepath?\n") leap.write("saveAmberParm rec apo.prmtop apo.inpcrd\n") leap.write("lig = loadmol2 lig.mol2\n") leap.write("loadAmberParams lig.frcmod\n") leap.write("saveAmberParm lig lig.prmtop lig.inpcrd\n") leap.write("com = combine {rec lig}\n") leap.write("saveAmberParm com com.prmtop com.inpcrd\n") leap.write("quit\n") try: subprocess.run('tleap -f leap.in'.split(" "), check=True, capture_output=True) except subprocess.CalledProcessError as e: logger.error("tleap error", e.output.decode("UTF-8")) exit() prmtop = app.AmberPrmtopFile(f'com.prmtop') inpcrd = app.AmberInpcrdFile(f'com.inpcrd') for comp in ['com', 'apo', 'lig']: for ext in ['prmtop', 'inpcrd']: shutil.copy(f'{dirpath}/{comp}.{ext}', f"{self.config.tempdir()}/{comp}_{self.params_written}.{ext}") self.system = prmtop.createSystem(**self.params) if self.config.relax_ligand: mod_parms = copy.deepcopy(self.params) mod_parms['constraints'] = None self._unconstrained_system = prmtop.createSystem(**mod_parms) self.boxvec = self.system.getDefaultPeriodicBoxVectors() self.topology, self.positions = prmtop.topology, inpcrd.positions with open("{}".format(self.config.pdb_file_name), 'w') as f: app.PDBFile.writeFile(self.topology, self.positions, file=f, keepIds=True) logger.log("wrote ", "{}".format(self.config.pdb_file_name)) with open("{}".format(self.config.pdb_file_name), 'r') as f: self.pdb = app.PDBFile(f) self.params_written += 1 return self.system, self.topology, self.positions except Exception as e: logger.error("EXCEPTION CAUGHT BAD SPOT", e)
def applyffExcipients(excipients, opt): """ This function applies the selected force field to the excipients Parameters: ----------- excipients: OEMol molecule The excipients molecules to parametrize opt: python dictionary The options used to parametrize the excipients Return: ------- excipient_structure: Parmed structure instance The parametrized excipient parmed structure """ # OpenMM topology and positions from OEMol topology, positions = oeommutils.oemol_to_openmmTop(excipients) # Try to apply the selected FF on the excipients forcefield = app.ForceField(opt['protein_forcefield']) # List of the unrecognized excipients unmatched_res_list = forcefield.getUnmatchedResidues(topology) # Unique unrecognized excipient names templates = set() for res in unmatched_res_list: templates.add(res.name) if templates: # Some excipients are not recognized oechem.OEThrow.Info("The following excipients are not recognized " "by the protein FF: {}" "\nThey will be parametrized by using the FF: {}".format(templates, opt['other_forcefield'])) # Create a bit vector mask used to split recognized from un-recognize excipients bv = oechem.OEBitVector(excipients.GetMaxAtomIdx()) bv.NegateBits() # Dictionary containing the name and the parmed structures of the unrecognized excipients unrc_excipient_structures = {} # Dictionary used to skip already selected unrecognized excipients and count them unmatched_excp = {} # Ordered list of the unrecognized excipients unmatched_res_order = [] for r_name in templates: unmatched_excp[r_name] = 0 hv = oechem.OEHierView(excipients) for chain in hv.GetChains(): for frag in chain.GetFragments(): for hres in frag.GetResidues(): r_name = hres.GetOEResidue().GetName() if r_name not in unmatched_excp: continue else: unmatched_res_order.append(r_name) if unmatched_excp[r_name]: # Test if we have selected the unknown excipient # Set Bit mask atms = hres.GetAtoms() for at in atms: bv.SetBitOff(at.GetIdx()) unmatched_excp[r_name] += 1 else: unmatched_excp[r_name] = 1 # Create AtomBondSet to extract from the whole excipient system # the current selected FF unknown excipient atms = hres.GetAtoms() bond_set = set() for at in atms: bv.SetBitOff(at.GetIdx()) bonds = at.GetBonds() for bond in bonds: bond_set.add(bond) atom_bond_set = oechem.OEAtomBondSet(atms) for bond in bond_set: atom_bond_set.AddBond(bond) # Create the unrecognized excipient OEMol unrc_excp = oechem.OEMol() if not oechem.OESubsetMol(unrc_excp, excipients, atom_bond_set): oechem.OEThrow.Fatal("Is was not possible extract the residue: {}".format(r_name)) # Charge the unrecognized excipient if not oequacpac.OEAssignCharges(unrc_excp, oequacpac.OEAM1BCCCharges(symmetrize=True)): oechem.OEThrow.Fatal("Is was not possible to " "charge the extract residue: {}".format(r_name)) # If GAFF or GAFF2 is selected as FF check for tleap command if opt['other_forcefield'] in ['GAFF', 'GAFF2']: ff_utils.ParamLigStructure(oechem.OEMol(), opt['other_forcefield']).checkTleap if opt['other_forcefield'] == 'SMIRNOFF': unrc_excp = oeommutils.sanitizeOEMolecule(unrc_excp) # Parametrize the unrecognized excipient by using the selected FF pmd = ff_utils.ParamLigStructure(unrc_excp, opt['other_forcefield'], prefix_name=opt['prefix_name']+'_'+r_name) unrc_excp_struc = pmd.parameterize() unrc_excp_struc.residues[0].name = r_name unrc_excipient_structures[r_name] = unrc_excp_struc # Recognized FF excipients pred_rec = oechem.OEAtomIdxSelected(bv) rec_excp = oechem.OEMol() oechem.OESubsetMol(rec_excp, excipients, pred_rec) if rec_excp.NumAtoms() > 0: top_known, pos_known = oeommutils.oemol_to_openmmTop(rec_excp) ff_rec = app.ForceField(opt['protein_forcefield']) try: omm_system = ff_rec.createSystem(top_known, rigidWater=False) rec_struc = parmed.openmm.load_topology(top_known, omm_system, xyz=pos_known) except: oechem.OEThrow.Fatal("Error in the recognised excipient parametrization") # Unrecognized FF excipients bv.NegateBits() pred_unrc = oechem.OEAtomIdxSelected(bv) unrc_excp = oechem.OEMol() oechem.OESubsetMol(unrc_excp, excipients, pred_unrc) # Unrecognized FF excipients coordinates oe_coord_dic = unrc_excp.GetCoords() unrc_coords = np.ndarray(shape=(unrc_excp.NumAtoms(), 3)) for at_idx in oe_coord_dic: unrc_coords[at_idx] = oe_coord_dic[at_idx] # It is important the order used to assemble the structures. In order to # avoid mismatch between the coordinates and the structures, it is convenient # to use the unrecognized residue order unmatched_res_order_count = [] i = 0 while i < len(unmatched_res_order): res_name = unmatched_res_order[i] for j in range(i+1, len(unmatched_res_order)): if unmatched_res_order[j] == res_name: continue else: break if i == (len(unmatched_res_order) - 1): num = 1 unmatched_res_order_count.append((res_name, num)) break else: num = j - i unmatched_res_order_count.append((res_name, num)) i = j # Merge all the unrecognized Parmed structure unrc_struc = parmed.Structure() for pair in unmatched_res_order_count: res_name = pair[0] nums = pair[1] unrc_struc = unrc_struc + nums*unrc_excipient_structures[res_name] # Set the unrecognized coordinates unrc_struc.coordinates = unrc_coords # Set the parmed excipient structure merging # the unrecognized and recognized parmed # structures together if rec_excp.NumAtoms() > 0: excipients_structure = unrc_struc + rec_struc else: excipients_structure = unrc_struc return excipients_structure else: # All the excipients are recognized by the selected FF omm_system = forcefield.createSystem(topology, rigidWater=False) excipients_structure = parmed.openmm.load_topology(topology, omm_system, xyz=positions) return excipients_structure
def get_charges(molecule, max_confs=800, strict_stereo=True, normalize=True, keep_confs=None, legacy=True): """Generate charges for an OpenEye OEMol molecule. Parameters ---------- molecule : OEMol Molecule for which to generate conformers. Omega will be used to generate max_confs conformations. max_confs : int, optional, default=800 Max number of conformers to generate strictStereo : bool, optional, default=True If False, permits smiles strings with unspecified stereochemistry. See https://docs.eyesopen.com/omega/usage.html normalize : bool, optional, default=True If True, normalize the molecule by checking aromaticity, adding explicit hydrogens, and renaming by IUPAC name. keep_confs : int, optional, default=None If None, apply the charges to the provided conformation and return this conformation, unless no conformation is present. Otherwise, return some or all of the generated conformations. If -1, all generated conformations are returned. Otherwise, keep_confs = N will return an OEMol with up to N generated conformations. Multiple conformations are still used to *determine* the charges. legacy : bool, default=True If False, uses the new OpenEye charging engine. See https://docs.eyesopen.com/toolkits/python/quacpactk/OEProtonFunctions/OEAssignCharges.html# Returns ------- charged_copy : OEMol A molecule with OpenEye's recommended AM1BCC charge selection scheme. Notes ----- Roughly follows http://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html """ # If there is no geometry, return at least one conformation. if molecule.GetConfs() == 0: keep_confs = 1 if not oechem.OEChemIsLicensed(): raise (ImportError("Need License for OEChem!")) if not oequacpac.OEQuacPacIsLicensed(): raise (ImportError("Need License for oequacpac!")) if normalize: molecule = normalize_molecule(molecule) else: molecule = oechem.OEMol(molecule) charged_copy = generate_conformers( molecule, max_confs=max_confs, strict_stereo=strict_stereo) # Generate up to max_confs conformers if not legacy: # 2017.2.1 OEToolkits new charging function status = oequacpac.OEAssignCharges(charged_copy, oequacpac.OEAM1BCCCharges()) if not status: raise (RuntimeError("OEAssignCharges failed.")) else: # AM1BCCSym recommended by Chris Bayly to KAB+JDC, Oct. 20 2014. status = oequacpac.OEAssignPartialCharges( charged_copy, oequacpac.OECharges_AM1BCCSym) if not status: raise (RuntimeError( "OEAssignPartialCharges returned error code %d" % status)) #Determine conformations to return if keep_confs == None: #If returning original conformation original = molecule.GetCoords() #Delete conformers over 1 for k, conf in enumerate(charged_copy.GetConfs()): if k > 0: charged_copy.DeleteConf(conf) #Copy coordinates to single conformer charged_copy.SetCoords(original) elif keep_confs > 0: logger().debug( "keep_confs was set to %s. Molecule positions will be reset." % keep_confs) #Otherwise if a number is provided, return this many confs if available for k, conf in enumerate(charged_copy.GetConfs()): if k > keep_confs - 1: charged_copy.DeleteConf(conf) elif keep_confs == -1: #If we want all conformations, continue pass else: #Not a valid option to keep_confs raise (ValueError('Not a valid option to keep_confs in get_charges.')) return charged_copy