def runAntechamber(force_field, file, output_ext="mol2", charge=None): """ A wrapper around antechamber. Parameters ---------- force_field : str Which force field to use. Only "gaff" and "gaff2" are accepted. file : str Name of input file. output_ext : str Output extension. charge : int The net charge of the molecule. Default: automatic detection by antechamber. Returns ------- output_name : str Absolute path of the output parametrised file. """ input_base, input_ext = _os.path.splitext(file)[0], file.split(".")[-1] if input_ext.lower() in ["mol", "sdf"]: _babel.babelTransform(file, output_extension="mol2", pH=None) input_ext = "mol2" output_name = "%s_antechamber.%s" % (input_base, output_ext) commandstr = "antechamber -i '%s' -fi %s -o '%s' -fo %s -c bcc -at %s -s 2" % ( file, input_ext, output_name, output_ext, force_field) if charge is not None: commandstr += " -nc {}".format(charge) _runexternal.runExternal(commandstr, procname="antechamber") return _os.path.abspath(output_name)
def runParmchk(force_field, file): """ A wrapper around parmchk2. Parameters ---------- force_field : str Which force field to use. Only "gaff" and "gaff2" are accepted. file : str Name of the file generated by antechamber. Returns ------- filename_output : str Absolute path to the extra parameter file generated by parmchk2. """ input_base, input_ext = _os.path.splitext(file)[0], file.split(".")[-1] commandstr = "parmchk2 -i '{0}.{1}' -f {1} -o '{0}.frcmod' -s %s".format( input_base, input_ext, force_field) _runexternal.runExternal(commandstr, procname="parmchk2") return _os.path.abspath(input_base + ".frcmod")
def babelTransform(input_filename, output_extension="mol2", pH=None, generate_3D_coords=False): """ A light wrapper of some of OpenBabel's file conversion capabilities. Parameters ---------- input_filename : str Name of the input file. output_extension : str Type of output extension. pH : float or None Hydrogenate the molecule for a certain pH. None means no hydrogenation. generate_3D_coords : bool Whether to generate 3D coordinates for the molecule. Returns ------- filename : str The absolute path to the output file. """ if input_filename is None: return None input_filebase, input_extension = _os.path.splitext( input_filename)[0], input_filename.split(".")[-1] output_filename = _os.path.abspath( _os.path.basename(input_filebase) + "." + output_extension) command = "obabel -i{0} \"{1}\" -o{2} -O \"{3}\"".format( input_extension, input_filename, output_extension, output_filename) if pH is not None: command += " -p {}".format(pH) if generate_3D_coords: command += " --gen3d" _runexternal.runExternal(command, procname="OpenBabel") return _os.path.abspath(output_filename)
def generateMBARData(self, n_cores=None, n_nodes=1, cont=True): """ Manually generates input data for MBAR using gmx mdrun -rerun using N topologies and N trajectories generated from N simulations (N squared energy evaluations are needed). Doesn't work with constrained simulations. For most cases one should instead use the XVG files readily generated by GROMACS during the simulation. Parameters ---------- n_cores : int or None, optional Number of cores used. Default: the same as the number of replicas. n_nodes : int, optional Number of nodes used. cont : bool, optional Whether to overwrite existing files or to continue from the last one. Returns ------- mbar_data : numpy.ndarray Data which is to be passed to pymbar. """ # GROMACS might generate a bunch of warnings when we apply a non-dummy Hamiltonian to a trajectory with dummies # due to clashes and backup too many structures. Here we suppress this backing up gmx_suppress_dump = _os.environ[ "GMX_SUPPRESS_DUMP"] if "GMX_SUPPRESS_DUMP" in _os.environ.keys( ) else None _os.environ["GMX_SUPPRESS_DUMP"] = "1" self.mbar_data = [] _logging.info("Generating Energy files...") with self._workdir: with _fileio.Dir("MBAR"): for i, file in enumerate(self.files): self.mbar_data += [[]] gro, top = file["gro"], file["top"] for j, file in enumerate(self.files): run = True trr = file["trr"] filebase = "Energy_%d_%d" % (i, j) # we only overwrite the last generated energies, because they might be incomplete if cont: filebase_next = "Energy_%d_%d" % ( i + (j + 1) // len(self.files), (j + 1) % len(self.files)) files_current = _glob.glob( "%s/%s.xvg" % (_os.getcwd(), filebase)) files_next = _glob.glob( "%s/%s.xvg" % (_os.getcwd(), filebase_next)) if len(files_current) and len(files_next): run = False if run: # use the last protocol or create a default one if len(self.protocols): protocol = self.protocols[-1] else: protocol = _Protocol.Protocol( use_preset="default", **self.lambda_dict) protocol.init_lambda_state = i protocol.skip_positions = 0 protocol.skip_velocities = 0 protocol.skip_forces = 0 protocol.write_derivatives = False protocol.random_velocities = False protocol.constraint = "no" protocol.__setattr__("continuation", "yes") protocol.__setattr__("dhdl-print-energy", "potential") protocol.__setattr__("calc-lambda-neighbors", "0") protocol.__setattr__("calc-lambda-neighbors", "0") mdp = protocol.write("GROMACS", filebase=filebase) tpr = filebase + ".tpr" grompp_command = "%s grompp -maxwarn 10 -f '%s' -c '%s' -p '%s' -o '%s'" % ( _PC.GROMACSEXE, mdp, gro, top, tpr) _runexternal.runExternal(grompp_command, procname="gmx grompp") if n_cores is None: mdrun_command = "%s mdrun -s '%s' -rerun '%s' -deffnm '%s'" % ( _PC.GROMACSEXE, tpr, trr, filebase) else: ppn = n_cores // n_nodes mdrun_command = "{0} -np {1} --map-by ppr:{2}:node {3} mdrun -s '{4}' -rerun '{5}' " \ "-deffnm {6}".format(_PC.MPIEXE, n_cores, ppn, _PC.GROMACSMPIEXE, tpr, trr, filebase) _runexternal.runExternal(mdrun_command, procname="gmx mdrun") self.mbar_data[i] += list( _MDAnalysis.auxiliary.XVG.XVGReader( filebase + ".xvg")._auxdata_values[:, 1]) # restore the original environment variable del _os.environ["GMX_SUPPRESS_DUMP"] if gmx_suppress_dump is not None: _os.environ["GMX_SUPPRESS_DUMP"] = gmx_suppress_dump return self.mbar_data
def runSimulation(self, name, multi=False, multidir=False, single_lambda=None, use_mpi=False, mdrun_mpi=False, gpu_id=None, use_preset=None, replex=None, plumed_file=None, n_cores_per_process=None, n_nodes=1, n_processes=None, dlb=False, gmx_kwargs=None, **protocol_params): """ Runs a simulation in GROMACS. Parameters ---------- name : str The name of the simulation. multi : bool, optional Whether to run the simulations in parallel, using -multi. multidir : bool, optional Whether to run the simulations in parallel, using -multidir. Overrides multi. single_lambda : None, int, optional An integer runs a simulation at a single lambda value and overrides multi and replex. None runs all lambda values. use_mpi : bool, optional Whether to use ProtoCaller.GROMACSEXE or GROMACSMPIEXE. mdrun_mpi : bool, optional Whether to call mdrun as "mdrun" or as "mdrun_mpi". gpu_id : str, optional Which GPU id to be used, if applicable. use_preset : str, Protocaller.Protocol.Protocol, None Which default preset to use. One of: "minimisation", "equilibration_nvt", "equilibration_npt", "production" "vacuum". You can alternatively pass a custom protocol here, in which case **protocol_params will not be used. replex : int or None, optional Attempts replica exchange after replex number of steps using PLUMED. None means no replica exchange. Overrides use_mpi and multi if not None. plumed_file : str, optional If replex is True, it uses this file as a configuration for PLUMED, otherwise an empty file is used. n_cores_per_process : int or None, optional Number of cores used per process. Default: let GROMACS decide. n_nodes : int, optional Number of physical nodes used. n_processes : int, optional Number of processes. Default: the same as the number of simulation. dlb : bool Whether to enable dynamic load balancing. Default is False due to some possible instabilities with gmx_mpi mdrun in some cases. gmx_kwargs : dict Additional arguments to be passed to mdrun. The keys of the dictionary need to be the name of the option, e.g. "cpt" for checkpoint interval, while the values need to be the value of the option if it permits one or None if it doesn't. If the values contain "{}" while the user is running the lambda windows in serial, this will be replaced by the lambda number. protocol_params Keyword arguments passed to ProtoCaller.Protocol.Protocol. """ # perform some checks and initialise some default values if n_processes is None: n_processes = 1 if single_lambda else self.lambda_size if n_nodes > 1 or multi or multidir: use_mpi = True if single_lambda is not None: replex, multi, multidir = None, False, False if replex is not None: use_mpi, multi = True, True if mdrun_mpi: MDRUNEXE = _os.path.dirname(_PC.GROMACSMPIEXE) + "/mdrun_mpi" elif use_mpi: MDRUNEXE = _PC.GROMACSMPIEXE + " mdrun" else: MDRUNEXE = _PC.GROMACSEXE + " mdrun" if use_mpi or mdrun_mpi: base_mdrun_command = f"{_PC.MPIEXE} -np {n_processes} --map-by ppr:{n_processes // n_nodes}:node {MDRUNEXE}" else: base_mdrun_command = MDRUNEXE gmx_kwargs = {} if gmx_kwargs is None else gmx_kwargs gmx_kwargs["dlb"] = "yes" if dlb else "no" if n_cores_per_process is not None: gmx_kwargs["ntomp"] = n_cores_per_process if gpu_id is not None: gmx_kwargs["gpu_id"] = gpu_id # this is due to incompatibility between GROMACS 2019+ and multi multidir = True if self._gmx_version( _PC.GROMACSMPIEXE) >= 2019 and multi else multidir # initialise the protocol if isinstance(use_preset, _Protocol.Protocol): protocol = use_preset else: protocol = _Protocol.Protocol(use_preset=use_preset, **protocol_params, **self.lambda_dict) self.protocols += [protocol] # run the simulations _logging.info(f"Running {name}...") with self._workdir: with _fileio.Dir(name, overwrite=False): # run single lambda if needed if single_lambda is not None: it = [single_lambda] else: it = range(self.lambda_size) # call grompp for each lambda for i in it: filebase = name if multidir else f"{name}_{i}" dirname = f"Lambda_{i}" if multidir else "." with _fileio.Dir(dirname): protocol.init_lambda_state = i self.files[i]["mdp"] = _os.path.abspath( protocol.write(engine="GROMACS", filebase=filebase)) grompp_args = { "-f": "mdp", "-c": "gro", "-p": "top", "-t": "cpt", } grompp_command = f"{_PC.GROMACSEXE} grompp -maxwarn 10 -o {filebase}.tpr" for grompp_arg, filetype in grompp_args.items(): if filetype in self.files[i].keys(): grompp_command += f" {grompp_arg} '{self.files[i][filetype]}'" _runexternal.runExternal(grompp_command, procname="gmx grompp") # call the simulations consecutively for each lambda if multi is not specified if not (multi or multidir): for i in it: filebase = f"{name}_{i}" gmx_kwargs["s"] = f"'{filebase}.tpr'" gmx_kwargs["deffnm"] = f"'{filebase}'" # run GROMACS mdrun_command = self._dict_to_arguments( base_mdrun_command, gmx_kwargs, i) _runexternal.runExternal(mdrun_command, procname="gmx mdrun") # update files after the run self._update_files(i, filebase) # alternatively, run all simulations in parallel else: filebase = name if multidir else name + "_" gmx_kwargs["s"] = f"'{filebase}.tpr'" gmx_kwargs["deffnm"] = f"'{filebase}'" if multidir: gmx_kwargs["multidir"] = " ".join(f"Lambda_{i}" for i in it) else: gmx_kwargs["multi"] = f"{self.lambda_size}" # run replica exchange with PLUMED if needed if replex is not None: if plumed_file is None: plumed_file = "plumed.dat" open(plumed_file, "a").close() gmx_kwargs["plumed"] = f"'{plumed_file}'" gmx_kwargs["replex"] = replex gmx_kwargs["hrex"] = None # run GROMACS mdrun_command = self._dict_to_arguments( base_mdrun_command, gmx_kwargs, i) _runexternal.runExternal(mdrun_command, procname="gmx mdrun") # update files after the run for i in it: filebase = name if multidir else f"{name}_{i}" dirname = f"Lambda_{i}" if multidir else "." with _fileio.Dir(dirname): self._update_files(i, filebase)
def runSimulation(self, name, multi=False, single_lambda=None, use_mpi=False, use_preset=None, replex=None, n_cores_per_process=None, n_nodes=1, n_processes=None, dlb=False, **protocol_params): """ Runs a simulation in GROMACS. Parameters ---------- name : str The name of the simulation. multi : bool, optional Whether to run the simulations in parallel, using -multi. single_lambda : None, int, optional An integer runs a simulation at a single lambda value and overrides multi and replex. None runs all lambda values. use_mpi : bool, optional Whether to use ProtoCaller.GROMACSEXE or GROMACSMPIEXE. use_preset : str, None Which default preset to use. One of: "minimisation", "equilibration_nvt", "equilibration_npt", "production" "vacuum". replex : int or None, optional Attempts replica exchange after replex number of steps using PLUMED. None means no replica exchange. Overrides use_mpi and multi if not None. n_cores_per_process : int or None, optional Number of cores used per process. Default: let GROMACS decide. n_nodes : int, optional Number of physical nodes used. n_processes : int, optional Number of processes. Default: the same as the number of simulation. dlb : bool Whether to enable dynamic load balancing. Default is False due to some possible instabilities with gmx_mpi mdrun in some cases. protocol_params Keyword arguments passed to ProtoCaller.Protocol.Protocol. """ if n_processes is None: n_processes = 1 if single_lambda else self.lambda_size ppn = n_processes // n_nodes if n_nodes > 1: use_mpi = True if single_lambda is not None: replex = None multi = False if replex is not None: multi = True use_mpi = True dlb = "yes" if dlb else "no" protocol = _Protocol.Protocol(use_preset=use_preset, **protocol_params, **self.lambda_dict) self.protocols += [protocol] _logging.info("Running %s..." % name) with self._workdir: with _fileio.Dir(name, overwrite=True): # run single lambda if needed if single_lambda is not None: it = [single_lambda] else: it = range(self.lambda_size) # call grompp for every lambda for i in it: filebase = "%s_%d" % (name, i) protocol.init_lambda_state = i self.files[i]["mdp"] = _os.path.abspath(protocol.write(engine="GROMACS", filebase=filebase)) grompp_args = { "-f": "mdp", "-c": "gro", "-p": "top", "-t": "cpt", } grompp_command = "%s grompp -maxwarn 10 -o %s.tpr" % (_PC.GROMACSEXE, filebase) for grompp_arg, filetype in grompp_args.items(): if filetype in self.files[i].keys(): grompp_command += " %s '%s'" % (grompp_arg, self.files[i][filetype]) _runexternal.runExternal(grompp_command, procname="gmx grompp") # call the simulation consecutively for every lambda if multi is not specified if not multi: if not use_mpi: mdrun_command = "{0} mdrun -s '{1}.tpr' -deffnm {1} -dlb {2}".format( _PC.GROMACSEXE, filebase, dlb) else: mdrun_command = "{0} -np {1} --map-by ppr:{2}:node {3} " \ "mdrun -s '{4}.tpr' -deffnm {4} -dlb {5}".format( _PC.MPIEXE, n_processes, ppn, _PC.GROMACSMPIEXE, filebase, dlb) if n_cores_per_process: mdrun_command += " -ntomp {}".format(n_cores_per_process) _runexternal.runExternal(mdrun_command, procname="gmx mdrun") # update files after the run self.files[i] = {"top" : self.files[i]["top"],} output_files = _glob.glob("%s.*" % filebase) for output_file in output_files: ext = output_file.split(".")[-1].lower() if ext != "tpr": self.files[i][ext] = _os.path.abspath(output_file) # alternatively, run all simulations in parallel if multi: filebase = name + "_" mdrun_command = "{0} -np {1} --map-by ppr:{2}:node {3} mdrun -multi {4} -s {5}.tpr " \ "-deffnm {5} -dlb {6}".format( _PC.MPIEXE, n_processes, ppn, _PC.GROMACSMPIEXE, self.lambda_size, filebase, dlb) # run replica exchange with PLUMED if needed if replex is not None: open("plumed.dat", "a").close() mdrun_command += " -plumed plumed.dat -replex {} -hrex".format(replex) if n_cores_per_process: mdrun_command += " -ntomp {}".format(n_cores_per_process) _runexternal.runExternal(mdrun_command, procname="gmx mdrun") # update files after the run for i in range(self.lambda_size): filebase = "%s_%d" % (name, i) self.files[i] = {"top": self.files[i]["top"], } output_files = _glob.glob("%s.*" % filebase) for output_file in output_files: ext = output_file.split(".")[-1].lower() if ext != "tpr": self.files[i][ext] = _os.path.abspath(output_file)
def solvate(complex, params=None, box_length=8, shell=0, neutralise=True, ion_conc=0.154, centre=True, work_dir=None, filebase="complex"): """ Uses gmx solvate and gmx genion to solvate the system and (optionally) add NaCl ions. This function preserves the crystal water molecules. Parameters ---------- complex : BioSimSpace.System or parmed.structure.Structure The input unsolvated system. params : ProtoCaller.Parametrise.Params The input force field parameters. box_length : float, iterable Size of the box in nm. shell : float Places a layer of water of the specified thickness in nm around the solute. neutralise : bool Whether to add counterions to neutralise the system. ion_conc : float Ion concentration of NaCl in mol/L. centre : bool Whether to centre the system. work_dir : str Work directory. Default: current directory. filebase : str Output base name of the file. Returns ------- complex : BioSimSpace.System or parmed.structure.Structure The solvated system. """ if params is None: params = _parametrise.Params() if isinstance(complex, _pmd.Structure): centrefunc = _pmdwrap.centre chargefunc = lambda x: round(sum([atom.charge for atom in x.atoms])) readfunc = _pmdwrap.openFilesAsParmed elif _PC.BIOSIMSPACE and isinstance(complex, (_BSS._SireWrappers._molecule.Molecule, _BSS._SireWrappers._system.System)): if isinstance(complex, _BSS._SireWrappers._molecule.Molecule): complex = complex.toSystem() centrefunc = _BSSwrap.centre chargefunc = lambda x: round(x.charge().magnitude()) readfunc = _BSS.IO.readMolecules else: raise TypeError("Cannot solvate object of type %s" % type(complex)) if not isinstance(box_length, _Iterable): box_length = 3 * [box_length] if work_dir is None: work_dir = _os.path.basename(_tempfile.TemporaryDirectory().name) temp = True else: temp = False with _fileio.Dir(dirname=work_dir, temp=temp): # centre if centre: complex, box_length, _ = centrefunc(complex, box_length) # solvate with gmx solvate and load unparametrised waters into memory files = _PC.IO.GROMACS.saveAsGromacs(filebase, complex) # reloading the complex fixes some problems with ParmEd if isinstance(complex, _pmd.Structure): complex = _pmdwrap.openFilesAsParmed(files) new_gro = filebase + "_solvated.gro" command = "{0} solvate -shell {1} -box {2[0]} {2[1]} {2[2]} -cp \"{3}\" -o \"{4}\"".format( _PC.GROMACSEXE, shell, box_length, files[1], new_gro) if params.water_points == 4: command += " -cs tip4p.gro" _runexternal.runExternal(command, procname="gmx solvate") complex_solvated = _pmd.load_file(new_gro, skip_bonds=True) waters = complex_solvated[":SOL"] # prepare waters for tleap and parametrise for residue in waters.residues: residue.name = "WAT" for i, atom in enumerate(waters.atoms): if "H" in atom.name: atom.name = atom.name[0] + atom.name[2] elif "O" in atom.name: atom.name = "O" else: atom.name = "EPW" # here we only parametrise a single water molecule in order to gain performance waters_prep_filenames = [ filebase + "_waters.top", filebase + "_waters.gro" ] waters[":1"].save(filebase + "_single_wat.pdb") water = _parametrise.parametriseAndLoadPmd( params, filebase + "_single_wat.pdb", "water") _pmdwrap.saveFilesFromParmed(waters, [waters_prep_filenames[1]], combine="all") _pmdwrap.saveFilesFromParmed(water, [waters_prep_filenames[0]]) for line in _fileinput.input(waters_prep_filenames[0], inplace=True): line_new = line.split() if len(line_new) == 2 and line_new == ["WAT", "1"]: line = line.replace( "1", "{}".format(len(waters.positions) // params.water_points)) print(line, end="") waters_prep = _pmdwrap.openFilesAsParmed(waters_prep_filenames) waters_prep.box = _pmd.load_file(files[1], skip_bonds=True).box if any([neutralise, ion_conc, shell]): for residue in waters_prep.residues: residue.name = "SOL" _pmdwrap.saveFilesFromParmed(waters_prep, waters_prep_filenames) # add ions if any([neutralise, ion_conc, shell]): # write an MDP file _protocol.Protocol(use_preset="default").write("GROMACS", "ions") # neutralise if needed charge = chargefunc(complex) if neutralise else 0 volume = box_length[0] * box_length[1] * box_length[2] * 10**-24 n_Na, n_Cl = [ int(volume * 6.022 * 10**23 * ion_conc) - abs(charge) // 2 ] * 2 if neutralise: if charge < 0: n_Na -= charge else: n_Cl += charge # add ions with gmx genion ions_prep_filenames = [ filebase + "_ions.top", filebase + "_ions.gro" ] command = "{0} grompp -f ions.mdp -p {1} -c {2} -o \"{3}_solvated.tpr\"".format( _PC.GROMACSEXE, *waters_prep_filenames, filebase) _runexternal.runExternal(command, procname="gmx grompp") command = "{{ echo 2; }} | {0} genion -s \"{1}_solvated.tpr\" -o \"{2}\" -nn {3} -np {4}".format( _PC.GROMACSEXE, filebase, ions_prep_filenames[1], n_Cl, n_Na) _runexternal.runExternal(command, procname="gmx genion") # prepare waters for tleap ions = _pmd.load_file(ions_prep_filenames[1], skip_bonds=True) for residue in ions.residues: if residue.name == "SOL": residue.name = "WAT" # here we only parametrise single ions to gain performance ion = ions[":WAT"][":1"] + ions[":NA"][":1"] + ions[":CL"][":1"] max_len = len(ion.residues) ion.save(filebase + "_single_ion.pdb") ion = _parametrise.parametriseAndLoadPmd( params, filebase + "_single_ion.pdb", "water") _pmdwrap.saveFilesFromParmed(ions, [ions_prep_filenames[1]], combine="all") _pmdwrap.saveFilesFromParmed(ion, [ions_prep_filenames[0]]) mol_dict = {} for line in _fileinput.input(ions_prep_filenames[0], inplace=True): line_new = line.split() if len(line_new) == 2 and line_new[0] in [ "WAT", "NA", "CL" ] and line_new[1] == "1": n_mols = len(ions[":{}".format(line_new[0])].positions) if line_new[0] == "WAT": n_mols //= params.water_points line = line.replace("1", "{}".format(n_mols)) mol_dict[line_new[0]] = line # preserve the order of water, sodium and chloride if len(mol_dict) == max_len: for x in ["WAT", "NA", "CL"]: if x in mol_dict.keys(): print(mol_dict[x], end="") mol_dict = {} else: print(line, end="") return complex + readfunc(ions_prep_filenames) else: complex + readfunc(waters_prep_filenames)
def runTleap(force_fields=None, files=None, param_files=None, id=None, disulfide_bonds=None): """ Writes and runs tleap scripts. Parameters ---------- force_fields : [str] All force fields to be loaded in tleap. files : [str] All regular files to be loaded in tleap. param_files : [str] All parameter files to be loaded in tleap. id : str The name of the molecule. Default: equal to molecule_type. disulfide_bonds : [[ProtoCaller.IO.PDB.Residue, ProtoCaller.IO.PDB.Residue]]: Residues between which there is a disulfide bond. Returns ------- filenames : [str] Absolute paths of the final parametrised files. The first file is a topology file and the second file is a coordinate file. """ if id is None: id = "molecule" if disulfide_bonds is None: disulfide_bonds = [] filename_tleap = "tleap_script_%s.in" % id filenames = [id + ".prmtop", id + ".inpcrd"] with open(filename_tleap, "w+") as out: for force_field in force_fields: out.write("source \"%s\"\n" % returnFFPath(force_field)) if param_files is not None: for param_file in param_files: ext = param_file.split(".")[-1].lower() if ext in ["frcmod", "frcfld"]: command = "loadamberparams" elif ext in ["off", "lib"]: command = "loadoff" elif ext == "prep": command = "loadamberprep" else: _warnings.warn( "%s is not a valid parameter file. Skipping value..." % param_file) continue out.write("%s \"%s\"\n" % (command, param_file)) for file in files: ext = file.split(".")[-1] if ext == "pqr": ext = "pdb" out.write("MOL = load%s \"%s\"\n" % (ext, file)) for disulfide_bond in disulfide_bonds: out.write("bond MOL.%d.SG MOL.%d.SG\n" % (disulfide_bond[0].resSeq, disulfide_bond[1].resSeq)) # add support for cofactors name = "MOL" if files else id out.write("check {}\n".format(name)) out.write("saveAmberParm {0} {1} {2}\n".format(name, *filenames)) out.write("quit\n") _runexternal.runExternal("tleap -f %s" % filename_tleap, procname="tleap") return [_os.path.abspath(f) for f in filenames]