File: test_PDB_SASA.py

package info (click to toggle)
python-biopython 1.85%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 126,372 kB
  • sloc: xml: 1,047,995; python: 332,722; ansic: 16,944; sql: 1,208; makefile: 140; sh: 81
file content (159 lines) | stat: -rw-r--r-- 5,587 bytes parent folder | download | duplicates (2)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# Copyright 2020 Joao Rodrigues. All rights reserved.
#
# This file is part of the Biopython distribution and governed by your
# choice of the "Biopython License Agreement" or the "BSD 3-Clause License".
# Please see the LICENSE file that should have been included as part of this
# package.

"""Unit tests for the Bio.PDB.SASA module: Surface Accessibility Calculations."""

import copy
import pathlib
import unittest
import warnings

from Bio.PDB import PDBParser
from Bio.PDB.SASA import ShrakeRupley

DATADIR = pathlib.Path(__file__).parent / "PDB"


class TestShrakeRupley(unittest.TestCase):
    """Tests for SR algorithm."""

    # Expected values obtained with freesasa 2.0.3 and custom config file.
    # e.g. cmd: --shrake-rupley --resolution 100 -n-threads 1 --probe-radius 1.4

    @classmethod
    def setUpClass(cls):
        """One-time setup for all tests."""
        cls.parser = p = PDBParser(QUIET=1)
        with warnings.catch_warnings():
            structure = p.get_structure("X", DATADIR / "1LCD.pdb")
            model = structure[0]

            # Remove HETATM and Hs for simplicity/speed
            for r in list(model.get_residues()):
                if r.id[0] == " ":
                    for a in list(r):
                        if a.element == "H":
                            r.detach_child(a.name)
                else:
                    c = r.parent
                    c.detach_child(r.id)

            cls.model = model

    # General Parameters
    def test_default_algorithm(self):
        """Run Shrake-Rupley with default parameters."""
        m = copy.deepcopy(self.model)  # modifies atom.sasa

        sasa = ShrakeRupley()
        sasa.compute(m)

        result = [a.sasa for a in m.get_atoms()][:5]
        expected = [50.36, 31.40, 10.87, 12.86, 2.42]
        for a, b in zip(result, expected):
            self.assertAlmostEqual(a, b, places=2)

    def test_higher_resolution(self):
        """Run Shrake-Rupley with 960 points per sphere."""
        m = copy.deepcopy(self.model)  # modifies atom.sasa

        sasa = ShrakeRupley(n_points=960)
        sasa.compute(m)

        result = [a.sasa for a in m.get_atoms()][:5]
        expected = [51.90, 31.45, 12.45, 12.72, 3.02]
        for a, b in zip(result, expected):
            self.assertAlmostEqual(a, b, places=2)

    def test_custom_radii(self):
        """Run Shrake-Rupley with custom radii."""
        m = copy.deepcopy(self.model)  # modifies atom.sasa

        sasa = ShrakeRupley(radii_dict={"C": 5.00})
        sasa.compute(m)

        result = [a.sasa for a in m.get_atoms()][:5]
        expected = [0.0, 190.45, 41.18, 0.0, 36.03]
        for a, b in zip(result, expected):
            self.assertAlmostEqual(a, b, places=2)

    # Compute parameters
    def test_level_R(self):
        """Run Shrake-Rupley with level R."""
        m = copy.deepcopy(self.model)  # modifies atom.sasa

        sasa = ShrakeRupley()
        sasa.compute(m, level="R")

        for r in m.get_residues():
            atom_sum = sum(a.sasa for a in r)
            self.assertAlmostEqual(atom_sum, r.sasa, places=2)

    def test_level_C(self):
        """Run Shrake-Rupley with level C."""
        m = copy.deepcopy(self.model)  # modifies atom.sasa

        sasa = ShrakeRupley()
        sasa.compute(m, level="C")

        for c in m.get_chains():
            atom_sum = sum(a.sasa for a in c.get_atoms())
            self.assertAlmostEqual(atom_sum, c.sasa, places=2)

    # Exceptions
    def test_fail_probe_radius(self):
        """Raise exception on bad probe_radius parameter."""
        with self.assertRaisesRegex(ValueError, "must be a positive number"):
            sasa = ShrakeRupley(probe_radius=-1.40)

    def test_fail_n_points(self):
        """Raise exception on bad n_points parameter."""
        with self.assertRaisesRegex(ValueError, "must be larger than 1"):
            sasa = ShrakeRupley(n_points=0)

    def test_fail_compute_entity_type(self):
        """Raise exception on unsupported entity type."""
        with self.assertRaisesRegex(ValueError, "Invalid entity type"):
            sasa = ShrakeRupley()
            sasa.compute([1, 2, 3, 4, 5])

    def test_fail_compute_entity_level(self):
        """Raise exception on input Atom entity."""
        atom = list(self.model.get_atoms())[0]
        with self.assertRaisesRegex(ValueError, "Invalid entity type"):
            sasa = ShrakeRupley()
            sasa.compute(atom)

    def test_fail_compute_level_1(self):
        """Raise exception on invalid level parameter: X."""
        with self.assertRaisesRegex(ValueError, "Invalid level"):
            sasa = ShrakeRupley()
            sasa.compute(self.model, level="X")

    def test_fail_compute_level_2(self):
        """Raise exception on invalid level parameter: S > C."""
        chain = self.model["A"]
        with self.assertRaisesRegex(ValueError, "be equal or smaller than"):
            sasa = ShrakeRupley()
            sasa.compute(chain, level="S")  # Chain is a child of Structure.

    def test_fail_empty_entity(self):
        """Raise exception on invalid level parameter: S > C."""
        sasa = ShrakeRupley()

        r = copy.deepcopy(self.model["A"].child_list[0])
        for a in list(r):
            r.detach_child(a.name)  # empty residue

        self.assertEqual(len(r.child_list), 0)
        with self.assertRaisesRegex(ValueError, "Entity has no child atoms"):
            sasa.compute(r)


if __name__ == "__main__":
    runner = unittest.TextTestRunner(verbosity=2)
    unittest.main(testRunner=runner)