Spaces:
Running
Running
File size: 3,787 Bytes
36929e7 ea71194 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | from app.services.docking_pose_analytics import cluster_poses, pose_rmsd_matrix, water_mediated_interactions
PDBQT = """MODEL 1
HETATM 1 O1 LIG A 1 4.000 0.000 0.000 1.00 0.00 O
HETATM 2 C1 LIG A 1 5.000 0.000 0.000 1.00 0.00 C
ENDMDL
MODEL 2
HETATM 1 O1 LIG A 1 4.500 0.000 0.000 1.00 0.00 O
HETATM 2 C1 LIG A 1 5.500 0.000 0.000 1.00 0.00 C
ENDMDL
MODEL 3
HETATM 1 O1 LIG A 1 10.000 0.000 0.000 1.00 0.00 O
HETATM 2 C1 LIG A 1 11.000 0.000 0.000 1.00 0.00 C
ENDMDL
"""
PDB_WITH_WATER = """ATOM 1 N LYS A 1 0.000 0.000 0.000 1.00 20.00 N
ATOM 2 CA LYS A 1 -1.000 0.000 0.000 1.00 20.00 C
HETATM 3 O HOH A 101 2.000 0.000 0.000 1.00 20.00 O
END
"""
def test_pairwise_pose_rmsd_is_symmetric_and_zero_on_diagonal():
result = pose_rmsd_matrix(PDBQT)
matrix = result["matrix_angstrom"]
assert result["models"] == [1, 2, 3]
assert matrix[0][0] == 0.0
assert matrix[0][1] == matrix[1][0] == 0.5
assert result["symmetry_corrected"] is False
def test_pose_clustering_separates_distant_pose():
result = cluster_poses(PDBQT, cutoff_angstrom=2.0)
assert result["cluster_count"] == 2
memberships = [set(c["models"]) for c in result["clusters"]]
assert {1, 2} in memberships
assert {3} in memberships
def test_water_bridge_is_geometric_and_heuristic():
result = water_mediated_interactions(PDB_WITH_WATER, PDBQT, cutoff_angstrom=3.5)
assert result["status"] == "OBSERVED"
assert result["bridge_count"] >= 1
assert result["evidence_class"] == "Heuristic"
assert "Geometry alone" in result["limitation"]
def test_water_screen_covers_every_pose_and_retains_atom_identity():
result = water_mediated_interactions(PDB_WITH_WATER, PDBQT)
assert result["pose_count"] == 3
summaries = {p["model"]: p for p in result["poses"]}
assert summaries[1]["bridge_count"] == 1
assert summaries[2]["bridge_count"] == 1
assert summaries[3]["bridge_count"] == 0
assert {b["model"] for b in result["bridges"]} == {1, 2}
assert result["bridges"][0]["water_identity"] == "A:HOH101:O"
reordered = PDBQT.replace(" O", " X").replace(" C", " O").replace(" X", " C")
assert water_mediated_interactions(PDB_WITH_WATER, reordered)["bridges"][0]["ligand_polar_atom_index"] == 2
def test_autodock_atom_types_and_heavy_atom_rmsd():
from app.services.docking_pose_analytics import _element
assert _element("HETATM 1 O1 LIG A 1 4.000 0.000 0.000 1.00 0.00 -0.300 OA") == "O"
hydrogen = "HETATM 3 H1 LIG A 1 0.000 0.000 0.000 1.00 0.00 H"
first = PDBQT.split("ENDMDL")[0] + "\n" + hydrogen + "\nENDMDL\n"
second = first.replace("MODEL 1", "MODEL 2").replace("H1 LIG A 1 0.000", "H1 LIG A 1 20.000")
result = pose_rmsd_matrix(first + second)
assert result["matrix_angstrom"][0][1] == 0.0
assert result["hydrogens_excluded"] is True
def test_water_evidence_hashes_inputs_and_distinguishes_missing_receptor():
import hashlib
result = water_mediated_interactions(PDB_WITH_WATER, PDBQT)
assert result["provenance"]["original_pdb_sha256"] == hashlib.sha256(PDB_WITH_WATER.encode()).hexdigest()
assert water_mediated_interactions("", PDBQT)["status"] == "UNAVAILABLE"
no_water = "\n".join(l for l in PDB_WITH_WATER.splitlines() if "HOH" not in l)
result = water_mediated_interactions(no_water, PDBQT)
assert result["status"] == "NOT_OBSERVED"
assert len(result["poses"]) == 3
|