diff --git a/pyteomics/electrochem.py b/pyteomics/electrochem.py index 3c9ec15f..fb0e0f21 100644 --- a/pyteomics/electrochem.py +++ b/pyteomics/electrochem.py @@ -313,11 +313,17 @@ def pI(sequence, pI_range=(0.0, 14.0), precision_pI=0.01, **kwargs): left_x, right_x = pI_range left_y = _charge_for_dict(peptide_dict, [left_x], pK)[0] right_y = _charge_for_dict(peptide_dict, [right_x], pK)[0] + if left_y == 0: + return left_x + if right_y == 0: + return right_x while (right_x - left_x) > precision_pI: if left_y * right_y > 0: return left_x if abs(left_y) < abs(right_y) else right_x middle_x = (left_x + right_x) / 2.0 middle_y = _charge_for_dict(peptide_dict, [middle_x], pK)[0] + if middle_y == 0: + return middle_x if middle_y * left_y < 0: right_x = middle_x right_y = middle_y diff --git a/tests/test_electrochem.py b/tests/test_electrochem.py index bb4877ba..b48a941a 100644 --- a/tests/test_electrochem.py +++ b/tests/test_electrochem.py @@ -72,6 +72,25 @@ def test_pI_precision(self): self.assertTrue( abs(pI('PEPTIDE', precision_pI=precision) - pI_best) < precision) + def test_pI_exact_root(self): + # Only the two terminal groups ionize, so the root is their mean pKa. + expected = (2.34 + 9.69) / 2.0 + for sequence in ('AAA', ['H-', 'A', 'A', 'A', '-OH'], + {'H-': 1, 'A': 3, '-OH': 1}): + for bounds in ((2.34, 9.69), (expected, 14.0), (0.0, expected)): + with self.subTest(sequence=sequence, bounds=bounds): + self.assertEqual(pI(sequence, pI_range=bounds), expected) + + def test_pI_exact_root_custom_pK(self): + self.assertEqual( + pI('AAA', pK={'H-': [(9.0, 1)], '-OH': [(5.0, -1)]}), + 7.0) + self.assertEqual( + pI('AAA', pK={'H-': [(9.0, 1)], '-OH': [(5.0, -1)]}, + pI_range=(5.0, 7.0), + pK_nterm={'H-': {'A': [(7.0, 1)]}}), + 6.0) + def test_charge_input(self): for i in range(0, 14): self.assertAlmostEqual(