def alignToEachOther(self, output_filename=None, realign=False, **kwargs): """ Aligns the second ligand to the first ligand. Parameters ---------- output_filename : str The name of the output file. None uses the default value. realign : bool Whether to realign an already aligned ligand. kwargs Keyword arguments to pass to ProtoCaller.Wrappers.rdkitwrapper.alignTwoMolecules. Returns ------- morph : BioSimSpace.System The mixed topology object. mcs : [tuple] The maximum common substructure of the two molecules. """ if self.current_ref in self._morph.keys() and not realign: _logging.info("Morph %s is already aligned to a reference" % self.name) return if output_filename is None: _stdio.stdout_stderr()(self.ligand1.parametrise)(reparametrise=False) _stdio.stdout_stderr()(self.ligand2.parametrise)(reparametrise=False) output_filename = "{}_aligned_{}.{}".format(self.ligand2.name, self.name, self.ligand2.parametrised_files[1].split(".")[-1]) if "two_way_matching" not in kwargs.keys(): kwargs["two_way_matching"] = True if self.current_ref is None: self._ligand1_molecule[None] = self.ligand1.molecule self._ligand1_coords[None] = self.ligand1.parametrised_files[1] lig1_temp = self._ligand1_molecule[self.current_ref] lig2_temp = _copy.deepcopy(self.ligand2.molecule) self._ligand2_molecule[self.current_ref], mcs = _rdkit.alignTwoMolecules(lig1_temp, lig2_temp, **kwargs) self._ligand2_coords[self.current_ref] = _os.path.abspath( _rdkit.saveFromRdkit(self._ligand2_molecule[self.current_ref], output_filename)) # load the shifted ligands into BioSimSpace ligand1_BSS = _BSS.IO.readMolecules(self.parametrised_files1).getMolecules()[0] ligand2_BSS = _BSS.IO.readMolecules(self.parametrised_files2).getMolecules()[0] # translate RDKit mapping into BioSimSpace mapping mapping = {} indices_1 = [atom.index() for atom in ligand1_BSS._sire_object.edit().atoms()] indices_2 = [atom.index() for atom in ligand2_BSS._sire_object.edit().atoms()] for idx1, idx2 in mcs: mapping[indices_1[idx1]] = indices_2[idx2] # finally create morph self._morph[self.current_ref] = _BSS.Align.merge( ligand1_BSS, ligand2_BSS, mapping=mapping, allow_ring_breaking=True) return self._morph[self.current_ref], mcs
def alignToReference(self, ref, output_filename=None, realign=False, **kwargs): """ Aligns the first ligand to a reference ligand. Parameters ---------- ref : ProtoCaller.Ensemble.Ligand.Ligand The reference ligand. output_filename : str The name of the output file. None uses the default value. realign : bool Whether to realign an already aligned ligand. kwargs Keyword arguments to pass to ProtoCaller.Wrappers.rdkitwrapper.alignTwoMolecules. Returns ------- mcs : [tuple] The maximum common substructure of the two molecules. """ self.current_ref = ref if ref in self._ligand1_molecule.keys() and ref in self._ligand1_coords.keys() and not realign: _logging.info("Morph %s is already aligned to a reference" % self.name) return if output_filename is None: _stdio.stdout_stderr()(self.ligand1.parametrise)(reparametrise=False) output_filename = "{}_aligned_{}.{}".format(self.ligand1.name, self.name, self.ligand1.parametrised_files[1].split(".")[-1]) if "two_way_matching" not in kwargs.keys(): kwargs["two_way_matching"] = False lig_temp = _copy.deepcopy(self.ligand1.molecule) self._ligand1_molecule[ref], mcs = _rdkit.alignTwoMolecules(ref.molecule, lig_temp, **kwargs) self._ligand1_coords[ref] = _rdkit.saveFromRdkit(self._ligand1_molecule[ref], output_filename) return mcs
def prepareComplexes(self, replica_temps=None, scale_dummy_bonds=1, dummy_bond_smarts="[*]~[*]", intermediate_files=False, store_complexes=False, output_files=True): """ Batch prepares all complexes with an option to output files for REST(2). Parameters ---------- replica_temps : [float] or None A list of replica temperatures. Everything is normalised with respect to the lowest temperature. None means only output the normal files. scale_dummy_bonds : float Sets the dummy bond length distance as a fraction of the real bond length distance. dummy_bond_smarts : str SMARTS string which indicates which dummy bonds are to be affected by scale_dummy_bonds. intermediate_files : bool Whether to store all intermediate files. store_complexes : bool Whether to store the final complexes as a dictionary of BioSimSpace System objects. output_files : bool Whether to write output files immediately or later via saveSystems. """ # make sure the proteins / ligands are parametrised before proceeding with self.workdir: _stdio.stdout_stderr()(self.protein.parametrise)(params=self.params, reparametrise=False) for morph in self.morphs: _stdio.stdout_stderr()(morph.ligand1.parametrise)(params=self.params, reparametrise=False) _stdio.stdout_stderr()(morph.ligand2.parametrise)(params=self.params, reparametrise=False) # take care of the replicas if there are any if replica_temps is None or len(replica_temps) == 1: scales = [1] else: sorted_list = sorted(replica_temps) if sorted_list != replica_temps: _warnings.warn("Input replica temperatures were not in ascending order. Sorting...") replica_temps = sorted_list scales = [replica_temps[0] / elem for elem in replica_temps] for i, morph in enumerate(self.morphs): if intermediate_files: curdir = _fileio.Dir(morph.name, overwrite=True) else: name = _os.path.basename(_tempfile.TemporaryDirectory().name) curdir = _fileio.Dir(name, overwrite=True, temp=True) with curdir: _logging.info("Creating morph %s..." % morph.name) morph_BSS, mcs = _stdio.stdout_stderr()( morph.alignAndCreateMorph)(self.protein.ligand_ref) morph_BSS = _BSS._SireWrappers.System(morph_BSS) box = self.protein.complex_template._sire_object.property( "space") morph_BSS._sire_object.setProperty("space", box) # here we scale the equilibrium bond lengths if needed if scale_dummy_bonds != 1: n1 = morph.ligand1.molecule.GetNumAtoms() n2 = morph.ligand2.molecule.GetNumAtoms() inv_map = {y: x for x, y in mcs} du2 = [i for i in range(n2) if i not in inv_map.keys()] inv_map = {**{x: n1 + y for x, y in zip(du2, range(n2 - n1))}, **inv_map} mcs_smarts = _Chem.MolFromSmarts(dummy_bond_smarts) matches_lig1 = morph.ligand1.molecule.\ GetSubstructMatches(mcs_smarts) matches_lig2 = morph.ligand2.molecule.\ GetSubstructMatches(mcs_smarts) # here we take care of the index transformation matches_lig2 = [(inv_map[x[0]], inv_map[x[1]]) for x in matches_lig2 if set(x).issubset(inv_map.keys())] matches_total = [*matches_lig1, *matches_lig2] morph_BSS = _BSSwrap.rescaleBondedDummies( morph_BSS, scale_dummy_bonds, {"Merged_Molecule": matches_total} ) complexes = [self.protein.complex_template + morph_BSS] # solvate and save the prepared complex and morph with the appropriate box size _logging.info("Solvating...") complexes = [_solvate.solvate(complexes[0], self.params, box_length=self.box_length_complex, shell=self.shell, neutralise=self.neutralise, ion_conc=self.ion_conc, centre=self.centre, work_dir=curdir.path, filebase="complex")] morph_sol = _solvate.solvate(morph_BSS, self.params, box_length=self.box_length_morph, shell=self.shell, neutralise=self.neutralise, ion_conc=self.ion_conc, centre=self.centre, work_dir=curdir.path, filebase="morph") # rescale complexes for replica exchange if needed if len(scales) != 1: _logging.info("Creating replicas...") complexes = [_BSSwrap.rescaleSystemParams(complexes[0], scale, includelist=["Merged_Molecule"]) for scale in scales] if store_complexes: self.systems_prep[morph.name] = (morph_sol, complexes) if output_files: self.saveSystems({morph.name: (morph_sol, complexes)})
def amberWrapper(params, filename, molecule_type, id=None, charge=None, *args, **kwargs): """ Parametrises an input file according to AMBER force field. Parameters ---------- params : ProtoCaller.Parametrise.Params Force field parameters. filename : str Name of the input file. molecule_type : str The type of the molecule. One of: "protein", "ligand", "cofactor", "water", "simple_anion", "complex_anion", "simple_cation", "complex_cation". id : str The name of the molecule. Default: equal to molecule_type. charge : bool The net charge of the molecule. Default: automatic detection by antechamber. args Positional arguments to be passed to the relevant wrapper. kwargs Keyword arguments to be passed to the relevant wrapper. Returns ------- files : [str] The output parametrised file(s). If there is more than one file, the first one is always the topology file and the second one - the coordinate file. """ if id is None: id = molecule_type force_fields, files, param_files = [], [filename], [] if molecule_type == "protein": force_fields = [params.protein_ff, params.water_ff] elif molecule_type in ["water", "simple_anion", "simple_cation"]: force_fields = [params.water_ff] elif molecule_type == "complex_anion": _warnings.warn( "AMBER parametrisation failed: polyatomic anions not supported") return elif molecule_type == "complex_cation": _warnings.warn( "AMBER parametrisation failed: transition metals not supported") return elif molecule_type == "cofactor": if params.water_ff != "tip3p" or params.protein_ff != "ff99SB": _warnings.warn("All cofactors have been parametrised for use with " "the ff99SB protein force field and the TIP3P " "water model. Be careful when using these " "parameters with %s" % params.water_ff.upper()) files = [] force_fields = [params.protein_ff, params.ligand_ff] param_files = _glob.glob("%s/shared/amber-parameters/cofactors/%s.*" % (_PC.HOMEDIR, id)) # here we override the default parametrisation behaviour for cofactors parametrised_files = runTleap(force_fields=force_fields, files=files, param_files=param_files, id=id, *args, **kwargs) filebase = _os.path.splitext(filename)[0] topol = "{}.prmtop".format(filebase) _os.rename(parametrised_files[0], topol) parametrised_files[0] = topol # convert the parametrised file into PDB and load in RDKit ref = _rdkit.openAsRdkit(filename, removeHs=False) mol = _pmd.openFilesAsParmed(parametrised_files) pdb_file = _pmd.saveFilesFromParmed(mol, [filename], overwrite=True)[0] mol = _rdkit.openAsRdkit(pdb_file, removeHs=False) # align the parametrised file to the molecule and overwrite # previous coordinates mol, mcs = _stdio.stdout_stderr()(_rdkit.alignTwoMolecules) \ (ref, mol, two_way_matching=True, mcs_parameters=dict(atomCompare="elements")) if min(mol.GetNumAtoms(), ref.GetNumAtoms()) != len(mcs): _warnings.warn("The cofactor {} does not perfectly match the " "AMBER parameter file. Please check your " "molecule.".format(id)) _os.remove(parametrised_files[1]) coord = "{}.inpcrd".format(filebase) parametrised_files[1] = _rdkit.saveFromRdkit(mol, coord) return parametrised_files elif molecule_type == "ligand": force_fields = [params.ligand_ff] files = [runAntechamber(params.ligand_ff, filename, charge=charge)] param_files = [runParmchk(params.ligand_ff, files[0])] else: raise ValueError("Value %s for molecule_type not supported " % molecule_type) return runTleap(force_fields=force_fields, files=files, param_files=param_files, id=id, *args, **kwargs)