Source code for synkit.CRN.Construct.smiles
from __future__ import annotations
from typing import Any, Optional
try:
from rdkit import Chem
except ImportError:
Chem = None
[docs]
def mol_from_smiles_safe(smiles: str) -> Optional[Any]:
if Chem is None or not smiles:
return None
try:
return Chem.MolFromSmiles(smiles, sanitize=False)
except Exception:
return None
[docs]
def sanitize_safe(mol: Any) -> bool:
try:
Chem.SanitizeMol(mol)
return True
except Exception:
return False
[docs]
def has_atom_maps(mol: Any) -> bool:
try:
return any(a.GetAtomMapNum() > 0 for a in mol.GetAtoms())
except Exception:
return False
[docs]
def strip_maps_and_canonical_from_mol(mol: Any) -> Optional[str]:
try:
for a in mol.GetAtoms():
a.SetAtomMapNum(0)
mol_nomap = Chem.RemoveAllHs(mol)
return Chem.MolToSmiles(mol_nomap, canonical=True)
except Exception:
return None
[docs]
def canonical_from_mol(mol: Any) -> Optional[str]:
try:
return Chem.MolToSmiles(mol, canonical=True)
except Exception:
return None
[docs]
def assign_deterministic_maps_and_canonical(nomap_smiles: str) -> Optional[str]:
try:
mol2 = Chem.MolFromSmiles(nomap_smiles, sanitize=False)
if mol2 is None:
return None
if not sanitize_safe(mol2):
return None
for a in mol2.GetAtoms():
a.SetAtomMapNum(a.GetIdx() + 1)
return Chem.MolToSmiles(mol2, canonical=True)
except Exception:
return None
[docs]
def standardize_smiles_rdkit(smiles: str, *, keep_aam: bool) -> Optional[str]:
"""
Standardize one SMILES string.
Behavior:
- If RDKit is unavailable: passthrough.
- If keep_aam=False: strip atom maps, remove Hs, return canonical SMILES.
- If keep_aam=True:
* keep mapped canonical SMILES if maps already exist
* otherwise assign deterministic maps and return canonical mapped SMILES
"""
if not smiles:
return None
if Chem is None:
return smiles
mol = mol_from_smiles_safe(smiles)
if mol is None:
return None
if not sanitize_safe(mol):
return None
if not keep_aam:
return strip_maps_and_canonical_from_mol(mol)
if has_atom_maps(mol):
return canonical_from_mol(mol)
nomap = strip_maps_and_canonical_from_mol(mol)
if nomap is None:
return None
return assign_deterministic_maps_and_canonical(nomap)