def oe_assign_charges(mol, charge_model="AM1BCCELF10"): """assign partial charges, then premultiply by sqrt(ONE_4PI_EPS0) as an optimization""" # imported here for optional dependency from openeye import oequacpac charge_engines = { "AM1": oequacpac.OEAM1Charges(symmetrize=True), "AM1BCC": oequacpac.OEAM1BCCCharges(symmetrize=True), "AM1BCCELF10": oequacpac.OEAM1BCCELF10Charges(), } charge_engine = charge_engines[charge_model] oemol = convert_to_oe(mol) result = oequacpac.OEAssignCharges(oemol, charge_engine) if result is False: raise Exception("Unable to assign charges") partial_charges = np.array( [atom.GetPartialCharge() for atom in oemol.GetAtoms()]) # https://github.com/proteneer/timemachine#forcefield-gotchas # "The charges have been multiplied by sqrt(ONE_4PI_EPS0) as an optimization." inlined_constant = np.sqrt(constants.ONE_4PI_EPS0) return inlined_constant * partial_charges
def prep_structure(rdmol): mb = Chem.MolToMolBlock(rdmol) ims = oechem.oemolistream() ims.SetFormat(oechem.OEFormat_SDF) ims.openstring(mb) for buf_mol in ims.GetOEMols(): oemol = oechem.OEMol(buf_mol) omega = oeomega.OEOmega() # omega.SetIncludeInput(True) omega.SetMaxSearchTime(30) omega.SetCanonOrder(False) omega.SetSampleHydrogens(True) eWindow = 15.0 omega.SetEnergyWindow(eWindow) # omega.SetMaxConfs(800) omega.SetMaxConfs(400) omega.SetRMSThreshold(1.0) if omega(oemol): result = oequacpac.OEAssignCharges(oemol, oequacpac.OEAM1BCCELF10Charges()) if result is False: return None else: charges = [] for index, atom in enumerate(oemol.GetAtoms()): q = atom.GetPartialCharge() * np.sqrt(ONE_4PI_EPS0) charges.append(q) return charges else: return None
def static_parameterize(mol): """ Parameters ---------- mol: Chem.ROMol molecule to be parameterized. """ # imported here for optional dependency from openeye import oechem from openeye import oequacpac mb = Chem.MolToMolBlock(mol) ims = oechem.oemolistream() ims.SetFormat(oechem.OEFormat_SDF) ims.openstring(mb) for buf_mol in ims.GetOEMols(): oemol = oechem.OEMol(buf_mol) result = oequacpac.OEAssignCharges(oemol, oequacpac.OEAM1BCCELF10Charges()) if result is False: raise Exception('Unable to assign charges') charges = [] for index, atom in enumerate(oemol.GetAtoms()): q = atom.GetPartialCharge() * np.sqrt(constants.ONE_4PI_EPS0) charges.append(q) return np.array(charges)
def prep_structure(rdmol): oemol = convert_to_oe(rdmol) omega = oeomega.OEOmega() # omega.SetIncludeInput(True) omega.SetMaxSearchTime(30) omega.SetCanonOrder(False) omega.SetSampleHydrogens(True) eWindow = 15.0 omega.SetEnergyWindow(eWindow) # omega.SetMaxConfs(800) omega.SetMaxConfs(400) omega.SetRMSThreshold(1.0) if omega(oemol): result = oequacpac.OEAssignCharges(oemol, oequacpac.OEAM1BCCELF10Charges()) if result is False: return None else: charges = [] for index, atom in enumerate(oemol.GetAtoms()): q = atom.GetPartialCharge() * np.sqrt(ONE_4PI_EPS0) charges.append(q) return charges else: return None
def charge_mols(infile, outfile, reffile=None): ### Read in molecules ifs = oechem.oemolistream() if not ifs.open(infile): oechem.OEThrow.Warning("Unable to open %s for reading" % infile) return ### Open output file ofs = oechem.oemolostream() if not ofs.open(outfile): oechem.OEThrow.Fatal("Unable to open %s for writing" % outfile) ### Charge the molecules and write output if reffile is None: for mol in ifs.GetOEMols(): if not oequacpac.OEAssignCharges(mol, oequacpac.OEAM1BCCELF10Charges()): oechem.OEThrow.Warning("Unable to charge mol {}".format( mol.GetTitle())) oechem.OEWriteConstMolecule(ofs, mol) ifs.close() ofs.close() else: ### Read in molecules rfs = oechem.oemolistream() if not rfs.open(reffile): oechem.OEThrow.Warning("Unable to open %s for reading" % reffile) return ### Set coordinates of desired molecule on the mol with charges for in_mol, ref_mol in zip(ifs.GetOEMols(), rfs.GetOEMols()): ref_mol.SetCoords(in_mol.GetCoords()) oechem.OEWriteConstMolecule(ofs, ref_mol) ifs.close() ofs.close()
def charge_mol(mol, omega): """Generate conformers and assign AM1BCCELF10 charges to mol""" if omega(mol): #black box function to assign charges to a molecule oequacpac.OEAssignCharges(mol, oequacpac.OEAM1BCCELF10Charges()) else: print("Failed to generate conformation(s) for molecule %s" % mol.GetTitle())
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 assignELF10charges(molecule, max_confs=800, strictStereo=True): """ This function computes atomic partial charges for an OEMol by using the ELF10 method Parameters: ----------- molecule : OEMol object The molecule that needs to be charged max_confs : integer The max number of conformers used to calculate the atomic partial charges strictStereo : bool a flag used to check if atoms need to have assigned stereo chemistry or not Return: ------- mol_copy : OEMol a copy of the original molecule with assigned atomic partial charges """ mol_copy = molecule.CreateCopy() # The passed molecule could have already conformers. If the conformer number # does not exceed the max_conf threshold then max_confs conformations will # be generated if not mol_copy.GetMaxConfIdx() > 200: # Generate up to max_confs conformers mol_copy = generate_conformers(mol_copy, max_confs=max_confs, strictStereo=strictStereo) # Assign MMFF Atom types if not oechem.OEMMFFAtomTypes(mol_copy): raise RuntimeError("MMFF atom type assignment returned errors") # ELF10 charges status = oequacpac.OEAssignCharges(mol_copy, oequacpac.OEAM1BCCELF10Charges()) if not status: raise RuntimeError("OEAssignCharges returned error code %d" % status) return mol_copy
def AssignChargesByName(mol, name): if name == "noop": return oequacpac.OEAssignCharges(mol, oequacpac.OEChargeEngineNoOp()) elif name == "mmff" or name == "mmff94": return oequacpac.OEAssignCharges(mol, oequacpac.OEMMFF94Charges()) elif name == "am1bcc": return oequacpac.OEAssignCharges(mol, oequacpac.OEAM1BCCCharges()) elif name == "am1bccnosymspt": optimize = True symmetrize = True return oequacpac.OEAssignCharges( mol, oequacpac.OEAM1BCCCharges(not optimize, not symmetrize)) elif name == "amber" or name == "amberff94": return oequacpac.OEAssignCharges(mol, oequacpac.OEAmberFF94Charges()) elif name == "am1bccelf10": return oequacpac.OEAssignCharges(mol, oequacpac.OEAM1BCCELF10Charges()) return False
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)
omega.SetCanonOrder(False) omega.SetStrictStereo(False) omega.SetStrictAtomTypes(False) omega.SetSampleHydrogens( True ) # Word to the wise: skipping this step can lead to significantly different charges! omega.SetEnergyWindow(15.0) omega.SetRMSThreshold( 1.0 ) # Word to the wise: skipping this step can lead to significantly different charges! if omega(mol_multiconf): # generate conformation # Generate am1bcc partial charges oequacpac.OEAssignCharges(mol_multiconf, oequacpac.OEAM1BCCELF10Charges()) # Get total charge conf = mol_multiconf.GetConf(oechem.OEHasConfIdx(0)) absFCharge = 0 sumFCharge = 0 sumPCharge = 0.0 for atm in mol_multiconf.GetAtoms(): sumFCharge += atm.GetFormalCharge() absFCharge += abs(atm.GetFormalCharge()) sumPCharge += atm.GetPartialCharge() oechem.OEThrow.Info( "%s: %d formal charges give total charge %d ; Sum of Partial Charges %5.4f" % (mol_multiconf.GetTitle(), absFCharge, sumFCharge, sumPCharge)) # Output file
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
def parameterize(self, mol): """ Parameters ---------- mol: Chem.ROMol molecule to be parameterized. """ # imported here for optional dependency from openeye import oechem from openeye import oequacpac mb = Chem.MolToMolBlock(mol) ims = oechem.oemolistream() ims.SetFormat(oechem.OEFormat_SDF) ims.openstring(mb) for buf_mol in ims.GetOEMols(): oemol = oechem.OEMol(buf_mol) # AromaticityModel.assign(oe_molecule, bcc_collection.aromaticity_model) AromaticityModel.assign(oemol) # check for cache cache_key = 'AM1Cache' if not mol.HasProp(cache_key): result = oequacpac.OEAssignCharges( oemol, oequacpac.OEAM1Charges(symmetrize=True)) if result is False: raise Exception('Unable to assign charges') am1_charges = [] for index, atom in enumerate(oemol.GetAtoms()): q = atom.GetPartialCharge() * np.sqrt(constants.ONE_4PI_EPS0) am1_charges.append(q) mol.SetProp(cache_key, base64.b64encode(pickle.dumps(am1_charges))) else: am1_charges = pickle.loads(base64.b64decode( mol.GetProp(cache_key))) bond_idxs = [] bond_idx_params = [] for index in range(len(self.smirks)): smirk = self.smirks[index] param = self.params[index] substructure_search = oechem.OESubSearch(smirk) substructure_search.SetMaxMatches(0) matched_bonds = [] matches = [] for match in substructure_search.Match(oemol): matched_indices = { atom_match.pattern.GetMapIdx() - 1: atom_match.target.GetIdx() for atom_match in match.GetAtoms() if atom_match.pattern.GetMapIdx() != 0 } matches.append(matched_indices) for matched_indices in matches: forward_matched_bond = [matched_indices[0], matched_indices[1]] reverse_matched_bond = [matched_indices[1], matched_indices[0]] if (forward_matched_bond in matched_bonds or reverse_matched_bond in matched_bonds or forward_matched_bond in bond_idxs or reverse_matched_bond in bond_idxs): continue matched_bonds.append(forward_matched_bond) bond_idxs.append(forward_matched_bond) bond_idx_params.append(index) bcc_fn = functools.partial(apply_bcc, bond_idxs=np.array(bond_idxs), bond_idx_params=np.array(bond_idx_params, dtype=np.int32), am1_charges=np.array(am1_charges)) charges, vjp_fn = jax.vjp(bcc_fn, self.params) return np.array(charges, dtype=np.float64), vjp_fn
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 hybrid_docking(receptor_path, molecules_path, docked_molecules_path, n_poses=10): """Automated hybrid docking of small molecules to a receptor. Parameters ---------- receptor_path : str Path to PDB file containing receptor and reference ligand, or pre-prepared receptor for docking molecules_path : str Path to file containing one or more molecules (in OpenEye readable format) to be docked. (For example, list of SMILES) docked_molecules_path : str Path to output file to be created to contain docked molecules Uses OpenEye recognized file extension, such as .mol2 or .sdf n_poses : int, optional, default=1 Number of docked poses to generate receptor_filename : str, optional, default=None If not None, the pre-prepared receptor is loaded TODO: How can this API be improved? """ from .docking import create_receptor, load_receptor, pose_molecule from openeye import oedocking, oechem #import openmoltools as moltools # TODO: Bring these methods into this module # Try to load pre-prepared receptor from specified file receptor = oechem.OEGraphMol() print('Attempting to load receptor from {}...'.format(receptor_path)) if not oedocking.OEReadReceptorFile(receptor, receptor_path): # Load complex of receptor and reference ligand complex_istream = oechem.oemolistream(receptor_path) complex = oechem.OEGraphMol() oechem.OEReadMolecule(complex_istream, complex) # Attempt to split into components and build receptor based on reference ligand print('Attempting to split complex into components...') ligand = oechem.OEGraphMol() protein = oechem.OEGraphMol() water = oechem.OEGraphMol() other = oechem.OEGraphMol() if oechem.OESplitMolComplex(ligand, protein, water, other, complex): # Create receptor using bound ligand reference print('Creating receptor using reference ligand...') oedocking.OEMakeReceptor(receptor, protein, ligand) # TODO: We can store prepared receptor file if desired oedocking.OEWriteReceptorFile( receptor, '/home/guoj1/projects/INSPIRE/kinomodel/kinomodel/data/docking/prepared_receptor.oeb' ) else: raise Exception( 'Could not split specified PDB file {} into receptor and reference ligand' .format(receptor_path)) # Open file for writing docked molecules docked_molecules_ostream = oechem.oemolostream(docked_molecules_path) # Configure omega # From canonical recipe: https://docs.eyesopen.com/toolkits/cookbook/python/modeling/am1-bcc.html from openeye import oeomega omega = oeomega.OEOmega() omega.SetIncludeInput(False) omega.SetCanonOrder(False) omega.SetSampleHydrogens(True) eWindow = 15.0 omega.SetEnergyWindow(eWindow) omega.SetMaxConfs(800) omega.SetRMSThreshold(1.0) # Dock all molecules requested dock_method = oedocking.OEDockMethod_Hybrid2 dock_resolution = oedocking.OESearchResolution_Standard dock = oedocking.OEDock(dock_method, dock_resolution) dock.Initialize(receptor) molecules_istream = oechem.oemolistream(molecules_path) molecule = oechem.OEGraphMol() for molecule in molecules_istream.GetOEMols(): print("docking", molecule.GetTitle()) #docked_molecules = pose_molecule(receptor, molecule, n_poses=n_poses) #molecule = moltools.openeye.get_charges(molecule, keep_confs=10) # Generate conformers if not omega(molecule): continue # Apply charges from openeye import oequacpac oequacpac.OEAssignCharges(molecule, oequacpac.OEAM1BCCELF10Charges()) # Dock docked_molecule = oechem.OEGraphMol() dock.DockMultiConformerMolecule(docked_molecule, molecule) sdtag = oedocking.OEDockMethodGetName(dock_method) oedocking.OESetSDScore(docked_molecule, dock, sdtag) dock.AnnotatePose(docked_molecule) oechem.OEWriteMolecule(docked_molecules_ostream, docked_molecule)