Skip to content

Commit d4a1869

Browse files
Feature: Add number_hydrogens= keyword argument to capping ILP
1 parent 2d8e13f commit d4a1869

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

helpers/capping.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
def get_best_capped_molecule_with_ILP(
1212
molecule: 'Molecule',
1313
net_charge: Optional[int] = None,
14+
number_hydrogens: Optional[int] = None,
1415
enforce_octet_rule: bool = True,
1516
allow_radicals: bool = False,
1617
debug: Optional[TextIO] = None,
@@ -21,6 +22,7 @@ def get_best_capped_molecule_with_ILP(
2122
Args:
2223
``molecule``: Molecule to be capped. Some of its atoms should have the ``capped`` attribute set to False.
2324
``net_charge``: (Optional) Constraint the total net charge for the capped molecule.
25+
``number_hydrogens``: (Optional) Constraint the total number of hydrogens for the capped molecule (including hydrogens already present in the molecule).
2426
``enforce_octet_rule``: (Optional) Constraint organic elements (H, C, N, O) to satisfy the octet rule.
2527
``allow_radicals``: (Optional) Allow unpaired non-bonded electrons.
2628
``debug``: (Optional) Print very detailed debugging information
@@ -94,6 +96,8 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
9496

9597
ELECTRON_MULTIPLIER = (2 if not allow_radicals else 1)
9698

99+
hydrogens_before_capping = len([1 for atom in molecule.atoms.values() if atom.element == 'H'])
100+
97101
counter_charges = {}
98102
fragment_switches, fragment_scores, fragment_H_scores = {}, {}, {}
99103
capping_atoms_for = {}
@@ -187,7 +191,8 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
187191
]
188192

189193
H_size_objective = MAX(lpSum([F_i * fragment_H_scores[uncapped_atom_id, i] for ((uncapped_atom_id, i), F_i) in fragment_switches.items()]))
190-
if sum([fragment_H_scores[uncapped_atom_id, i] for ((uncapped_atom_id, i), F_i) in fragment_switches.items()]) != 0:
194+
has_non_null_H_size_objective = (sum([fragment_H_scores[uncapped_atom_id, i] for ((uncapped_atom_id, i), F_i) in fragment_switches.items()]) != 0)
195+
if has_non_null_H_size_objective:
191196
OBJECTIVES.append(H_size_objective)
192197

193198
total_size_objective = MIN(lpSum([F_i * fragment_scores[uncapped_atom_id, i] for ((uncapped_atom_id, i), F_i) in fragment_switches.items()]))
@@ -202,6 +207,17 @@ def possible_capping_strategies_for_atom(atom: Atom) -> List[Capping_Strategy]:
202207
if net_charge is not None:
203208
problem += (lpSum(charges.values()) == net_charge, 'Known net charge')
204209

210+
if number_hydrogens is not None:
211+
problem += (
212+
lpSum(
213+
[
214+
F_i * fragment_H_scores[uncapped_atom_id, i]
215+
for ((uncapped_atom_id, i), F_i) in fragment_switches.items()
216+
],
217+
) + hydrogens_before_capping == number_hydrogens,
218+
'Total number of hydrogens: {0}'.format(number_hydrogens)
219+
)
220+
205221
for atom in molecule.atoms.values():
206222
problem += (
207223
charges[atom.index]

test_peptide_capping.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import unittest
66
from sys import stdout
77
from os.path import basename
8+
from typing import Optional
89

910
from fragment_capping.helpers.molecule import Atom, Molecule, molecule_from_pdb_str
1011
from fragment_capping.helpers.compare import compare_capped_molecules
@@ -20,7 +21,7 @@ def element_for_line(line: str) -> str:
2021
def atom_name_for_line(line: str) -> str:
2122
return line[13:16].replace(' ', '')
2223

23-
def test_capping_peptide(peptide_pdb_filepath: str, peptide_net_charge: int) -> None:
24+
def test_capping_peptide(peptide_pdb_filepath: str, peptide_net_charge: int, peptide_number_hydrogens: Optional[int] = None) -> None:
2425
peptide_name, _ = basename(peptide_pdb_filepath).split('.')
2526

2627
with open(peptide_pdb_filepath) as fh:
@@ -72,6 +73,7 @@ def enforce_valence_aromatic_rings() -> None:
7273
new_molecule = input_molecule.get_best_capped_molecule_with_ILP(
7374
enforce_octet_rule=True,
7475
net_charge=old_charge,
76+
number_hydrogens=peptide_number_hydrogens,
7577
)
7678
print((datetime.now() - now).total_seconds())
7779
new_formula = new_molecule.formula(charge=True)
@@ -100,13 +102,13 @@ def enforce_valence_aromatic_rings() -> None:
100102

101103
class Test_Peptide_Capping(unittest.TestCase):
102104
def test_capping_AAA(self):
103-
test_capping_peptide('pdbs/AAA.pdb', 0)
105+
test_capping_peptide('pdbs/AAA.pdb', 0, None)
104106

105107
def test_capping_ARNDCEQGHILKMFPSTWYV(self):
106-
test_capping_peptide('pdbs/ARNDCEQGHILKMFPSTWYV.pdb', 0)
108+
test_capping_peptide('pdbs/ARNDCEQGHILKMFPSTWYV.pdb', 0, None)
107109

108110
def test_capping_2OVN_all(self) -> None:
109-
test_capping_peptide('pdbs/2OVN_with_connects.pdb', -4)
111+
test_capping_peptide('pdbs/2OVN_with_connects.pdb', -4, 144)
110112

111113
if __name__ == '__main__':
112114
unittest.main(warnings='ignore', verbosity=2)

0 commit comments

Comments
 (0)