Spaces:
Running
Running
fix: remove pybel bindings (causes segfault on Render), use obabel CLI subprocess only
Browse files
bioai-platform/backend/app/tools/docking.py
CHANGED
|
@@ -31,48 +31,11 @@ def _find_obabel() -> str | None:
|
|
| 31 |
VINA_CMD = shutil.which("vina") or "/usr/local/bin/vina"
|
| 32 |
OBABEL_CMD = _find_obabel()
|
| 33 |
|
| 34 |
-
# ββ in-process OpenBabel bindings (no subprocess β saves ~200 MB RAM) βββ
|
| 35 |
-
|
| 36 |
-
def _pdb_to_pdbqt_pybel(pdb_path: str, out_path: str) -> bool:
|
| 37 |
-
try:
|
| 38 |
-
from openbabel import pybel
|
| 39 |
-
mol = next(pybel.readfile("pdb", pdb_path))
|
| 40 |
-
mol.write("pdbqt", out_path, overwrite=True)
|
| 41 |
-
if not os.path.exists(out_path):
|
| 42 |
-
return False
|
| 43 |
-
# PDBQT writer treats the molecule as flexible by default, adding
|
| 44 |
-
# ROOT/BRANCH/ENDROOT/ENDBRANCH. For a rigid receptor these tags
|
| 45 |
-
# cause "Unknown or inappropriate tag found in rigid receptor" from
|
| 46 |
-
# vina. Strip them β the ATOM records are already correct.
|
| 47 |
-
with open(out_path) as _f:
|
| 48 |
-
_lines = _f.readlines()
|
| 49 |
-
_keep = [l for l in _lines
|
| 50 |
-
if not l.startswith(("ROOT", "ENDROOT", "BRANCH", "ENDBRANCH",
|
| 51 |
-
"TORSDOF", "TORSDEG", "TORSLIB"))]
|
| 52 |
-
if len(_keep) != len(_lines):
|
| 53 |
-
with open(out_path, "w") as _f:
|
| 54 |
-
_f.writelines(_keep)
|
| 55 |
-
return True
|
| 56 |
-
except Exception:
|
| 57 |
-
return False
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
def _smiles_to_pdbqt_pybel(smiles: str, out_path: str) -> bool:
|
| 61 |
-
try:
|
| 62 |
-
from openbabel import pybel
|
| 63 |
-
mol = pybel.readstring("smi", smiles)
|
| 64 |
-
mol.addh()
|
| 65 |
-
mol.make3D()
|
| 66 |
-
mol.write("pdbqt", out_path, overwrite=True)
|
| 67 |
-
return os.path.exists(out_path)
|
| 68 |
-
except Exception:
|
| 69 |
-
return False
|
| 70 |
-
|
| 71 |
# ββ self-healing binary download βββββββββββββββββββββββββββββββββββββββββ
|
| 72 |
_VINA_URL = "https://github.com/ccsb-scripps/AutoDock-Vina/releases/download/v1.2.7/vina_1.2.7_linux_x86_64"
|
| 73 |
|
| 74 |
def _pip_install_obabel() -> str | None:
|
| 75 |
-
"""Install openbabel
|
| 76 |
import subprocess as _sp
|
| 77 |
logger.info("obabel not found β installing openbabel-wheel via pip")
|
| 78 |
try:
|
|
@@ -589,39 +552,37 @@ class DockingTool(BaseTool):
|
|
| 589 |
with open(clean_path, "w") as f:
|
| 590 |
f.write(cleaned)
|
| 591 |
|
| 592 |
-
# 3. Convert protein to PDBQT (
|
| 593 |
protein_pdbqt = os.path.join(tmpdir, "protein.pdbqt")
|
| 594 |
-
if not
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
return {"error": f"Protein PDBQT preparation failed: {err}"}
|
| 608 |
|
| 609 |
-
# 4. Convert SMILES to 3D PDBQT (
|
| 610 |
ligand_pdbqt = os.path.join(tmpdir, "ligand.pdbqt")
|
| 611 |
-
if not
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
return {"error": f"Ligand PDBQT preparation failed: {err}"}
|
| 625 |
|
| 626 |
# 5. Determine binding site box
|
| 627 |
center = _find_ligand_center(pdb_content)
|
|
|
|
| 31 |
VINA_CMD = shutil.which("vina") or "/usr/local/bin/vina"
|
| 32 |
OBABEL_CMD = _find_obabel()
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
# ββ self-healing binary download βββββββββββββββββββββββββββββββββββββββββ
|
| 35 |
_VINA_URL = "https://github.com/ccsb-scripps/AutoDock-Vina/releases/download/v1.2.7/vina_1.2.7_linux_x86_64"
|
| 36 |
|
| 37 |
def _pip_install_obabel() -> str | None:
|
| 38 |
+
"""Install openbabel CLI via pip and return the obabel path."""
|
| 39 |
import subprocess as _sp
|
| 40 |
logger.info("obabel not found β installing openbabel-wheel via pip")
|
| 41 |
try:
|
|
|
|
| 552 |
with open(clean_path, "w") as f:
|
| 553 |
f.write(cleaned)
|
| 554 |
|
| 555 |
+
# 3. Convert protein to PDBQT (via obabel CLI subprocess)
|
| 556 |
protein_pdbqt = os.path.join(tmpdir, "protein.pdbqt")
|
| 557 |
+
if not OBABEL_CMD:
|
| 558 |
+
OBABEL_CMD = _pip_install_obabel()
|
| 559 |
+
if not OBABEL_CMD:
|
| 560 |
+
return {"error": "Open Babel not available β cannot prepare protein PDBQT."}
|
| 561 |
+
proc = await asyncio.create_subprocess_exec(
|
| 562 |
+
OBABEL_CMD, clean_path, "-O", protein_pdbqt, "-xr",
|
| 563 |
+
stdout=asyncio.subprocess.PIPE,
|
| 564 |
+
stderr=asyncio.subprocess.PIPE,
|
| 565 |
+
)
|
| 566 |
+
_, stderr = await proc.communicate()
|
| 567 |
+
if proc.returncode != 0 or not os.path.exists(protein_pdbqt):
|
| 568 |
+
err = stderr.decode() if stderr else "obabel failed"
|
| 569 |
+
return {"error": f"Protein PDBQT preparation failed: {err}"}
|
|
|
|
| 570 |
|
| 571 |
+
# 4. Convert SMILES to 3D PDBQT (via obabel CLI subprocess)
|
| 572 |
ligand_pdbqt = os.path.join(tmpdir, "ligand.pdbqt")
|
| 573 |
+
if not OBABEL_CMD:
|
| 574 |
+
OBABEL_CMD = _pip_install_obabel()
|
| 575 |
+
if not OBABEL_CMD:
|
| 576 |
+
return {"error": "Open Babel not available β cannot prepare ligand PDBQT."}
|
| 577 |
+
proc = await asyncio.create_subprocess_exec(
|
| 578 |
+
OBABEL_CMD, f"-:{smiles}", "-O", ligand_pdbqt, "--gen3d", "-h",
|
| 579 |
+
stdout=asyncio.subprocess.PIPE,
|
| 580 |
+
stderr=asyncio.subprocess.PIPE,
|
| 581 |
+
)
|
| 582 |
+
_, stderr = await proc.communicate()
|
| 583 |
+
if proc.returncode != 0 or not os.path.exists(ligand_pdbqt):
|
| 584 |
+
err = stderr.decode() if stderr else "obabel failed"
|
| 585 |
+
return {"error": f"Ligand PDBQT preparation failed: {err}"}
|
|
|
|
| 586 |
|
| 587 |
# 5. Determine binding site box
|
| 588 |
center = _find_ligand_center(pdb_content)
|