1+ # mypy: disable-error-code="attr-defined"
12# pylint: disable=E0401,E1101,I1101
23"""Filter molecules with kekulization errors.
34Handle molecules with sanitization errors by assign formal charge based on valence."""
45# https://depth-first.com/articles/2020/02/10/a-comprehensive-treatment-of-aromaticity-in-the-smiles-language/
56import argparse
67from pathlib import Path
7- from typing import Dict , Tuple
8+ from typing import Dict , Tuple , Optional
9+ from types import FrameType
10+ import signal # only unix supported
811
912import rdkit
1013from rdkit import Chem
1619input_small_mol_ligand = Path (args .input_small_mol_ligand ).name
1720
1821
22+ class TimeoutException (Exception ):
23+ pass
24+
25+
26+ def timeout (seconds : int , error_message : str = 'Timeout' ) -> None :
27+ """Time out for conformer generation, only works for unix. For windows have to ignore mypy error.
28+
29+ Args:
30+ seconds (int): The time in seconds to wait before timing out
31+ error_message (str, optional): The error message to display. Defaults to 'Timeout'.
32+ """
33+ def handle_timeout (signum : int , frame : Optional [FrameType ]) -> None :
34+ raise TimeoutException (error_message )
35+
36+ signal .signal (signal .SIGALRM , handle_timeout )
37+ signal .alarm (seconds )
38+
39+
1940def adjust_formal_charges (molecule : Chem .SDMolSupplier ) -> Chem .SDMolSupplier :
2041 """Sometimes input structures do not have correct formal charges corresponding
2142 to bond order topology. So choose to trust bond orders assigned and generate formal
@@ -59,14 +80,19 @@ def adjust_formal_charges(molecule: Chem.SDMolSupplier) -> Chem.SDMolSupplier:
5980 atom .SetFormalCharge (chg )
6081 return molecule
6182
83+ # for some ligands can take up to minutes to generate conformer!!
84+
6285
63- def generate_conformer (molecule : Chem .SDMolSupplier ) -> None :
86+ def generate_conformer (molecule : Chem .SDMolSupplier , timeout_value : int = 5 ) -> None :
6487 """ Generate conformer for molecule. Sometimes rdkit embedding can fail,
6588 so use random coordinates if failed at first.
6689
6790 Args:
68- molecule (Chem.SDMolSupplier): _description_
91+ molecule (Chem.SDMolSupplier): The rdkit molecule object
92+ timeout_value (float): The time in seconds to wait before timing out
6993 """
94+ # Set up the signal handler for the timeout
95+ timeout (timeout_value )
7096 ps = AllChem .ETKDGv2 ()
7197 confid = AllChem .EmbedMolecule (molecule , ps )
7298 if confid == - 1 :
@@ -75,6 +101,8 @@ def generate_conformer(molecule: Chem.SDMolSupplier) -> None:
75101 # only want to catch error Bad Conformation Id, dont need to spend time optimizing
76102 AllChem .MMFFOptimizeMolecule (molecule , confId = 0 , maxIters = 1 )
77103
104+ signal .alarm (0 )
105+
78106
79107def is_valid_ligand (molecule : Chem .SDMolSupplier ) -> Tuple [bool , bool , Chem .SDMolSupplier ]:
80108 """Check for sanitization errors, attempt to fix formal charges/valence consistency errors.
0 commit comments