|
|
|
|
|
import os |
|
import shutil |
|
from . import logger |
|
from subprocess import PIPE, Popen |
|
from os.path import join, isfile |
|
import platform |
|
|
|
|
|
_name = "FoldX" |
|
if platform.system() == "Windows": |
|
_bin_name = "foldx.exe" |
|
else: |
|
_bin_name = "foldx" |
|
|
|
|
|
|
|
class FoldxWrap: |
|
""" |
|
Expects a path to folder with FoldX binary and rotabase.txt files and a script dir with repairPDB.txt file |
|
If the file is not present in scripts dir, the class will attempt to create it |
|
This class can be used to run energy minimization and mutant creation with foldx |
|
""" |
|
def __init__(self, foldx_dir="", work_dir="", skip_minimization=False, ph=None): |
|
self.bin_dir = foldx_dir |
|
self.agg_work_dir = work_dir |
|
self.skip = skip_minimization |
|
self.ph = ph if ph else 7.0 |
|
self.repair_cmd = [ |
|
"--pdb", "input.pdb", |
|
"--command", "RepairPDB", |
|
"--water", "-CRYSTAL", |
|
"--pH", "%.2f" % self.ph] |
|
self.build_mutant_cmd = [ |
|
"--pdb", "input_Repair.pdb", |
|
"--command", "BuildModel", |
|
"--mutant-file", "individual_list.txt", |
|
"--pH", "%.2f" % self.ph] |
|
try: |
|
self._check_files() |
|
except logger.FoldXError: |
|
raise |
|
|
|
def minimize_energy(self, working_dir="", cleanup=True, skip_min=False): |
|
try: |
|
os.chdir(working_dir) |
|
except OSError: |
|
raise logger.FoldXError("FoldX run in a non-existing directory %s" % working_dir) |
|
if not isfile(join(working_dir, "input.pdb")): |
|
raise logger.FoldXError("input.pdb file not present if FoldX working directory %s" % working_dir) |
|
if not isfile(join(working_dir, "rotabase.txt")): |
|
|
|
try: |
|
os.symlink(join(self.bin_dir, "rotabase.txt"), join(working_dir, "rotabase.txt")) |
|
except (OSError, AttributeError): |
|
shutil.copyfile(join(self.bin_dir, "rotabase.txt"), join(working_dir, "rotabase.txt")) |
|
fold_cmd = [join(self.bin_dir, _bin_name)] + self.repair_cmd |
|
if not skip_min: |
|
self._run_foldx(cmd=fold_cmd, msg="Starting FoldX energy minimalization", outfile="finalRepair.out") |
|
else: |
|
|
|
shutil.move("input.pdb", "input_Repair.pdb") |
|
if cleanup: |
|
try: |
|
shutil.move("input_Repair.fxout", "RepairPDB.fxout") |
|
shutil.move("input_Repair.pdb", "folded.pdb") |
|
except IOError: |
|
raise logger.FoldXError("FoldX didn't produce expected repair files. " |
|
"Can't continue without it. This is unexpected and could indicate FoldX issues.") |
|
|
|
def build_mutant(self, working_dir="", mutation_list=""): |
|
os.chdir(working_dir) |
|
logger.to_file(filename="individual_list.txt", content=mutation_list) |
|
self.minimize_energy(working_dir=working_dir, cleanup=False, skip_min=self.skip) |
|
_cmd = [join(self.bin_dir, _bin_name)] + self.build_mutant_cmd |
|
self._run_foldx(cmd=_cmd, msg="Building mutant model", outfile="buildMutant.out") |
|
try: |
|
shutil.move("input_Repair_1.pdb", "input.pdb") |
|
except IOError: |
|
raise logger.FoldXError("FoldX didn't produce expected mutant file. " |
|
"Can't continue without it. This is unexpected and could indicate FoldX issues.") |
|
self._cleanup() |
|
|
|
@staticmethod |
|
def _run_foldx(cmd, msg='', outfile=''): |
|
"""Assumes the directory it's run from contains """ |
|
logger.info(module_name="FoldX", msg=msg) |
|
logger.debug(module_name="FoldX", msg="Starting FoldX with %s" % " ".join(cmd)) |
|
out, err = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
|
if err: |
|
logger.warning(module_name="FoldX", msg=err) |
|
logger.to_file(filename=outfile, content=out, allow_err=False) |
|
|
|
|
|
def _cleanup(self): |
|
with open("Dif_input_Repair.fxout", 'r') as enelog: |
|
d = enelog.readlines()[-1] |
|
d = d.split()[1] + " kcal/mol" |
|
logger.to_file(filename=join(self.agg_work_dir, "MutantEnergyDiff"), content=d) |
|
|
|
try: |
|
os.mkdir(join(self.agg_work_dir, "foldxOutput")) |
|
except OSError: |
|
logger.log_file(module_name=_name, msg="Couldn't create foldxOutput folder (probably already exists)") |
|
shutil.move("Raw_input_Repair.fxout", |
|
join(self.agg_work_dir, "foldxOutput/Stability_RepairPDB_input.fxout")) |
|
shutil.move("Dif_input_Repair.fxout", |
|
join(self.agg_work_dir, "foldxOutput/Dif_BuildModel_RepairPDB_input.fxout")) |
|
|
|
def _check_files(self): |
|
if not isfile(join(self.bin_dir,"rotabase.txt")) or not isfile(join(self.bin_dir,_bin_name)): |
|
raise logger.FoldXError("Provided FoldX directory (%s) misses either %s or %s " % |
|
(self.bin_dir, "rotabase.txt", _bin_name) + |
|
"files which are required for the program to run.") |
|
|