Pandaaaa906

Untitled

Jul 29th, 2023
176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.83 KB | None | 0 0
  1. from enum import Enum
  2. from functools import cmp_to_key, partial
  3. from itertools import groupby, chain
  4.  
  5. from indigo import Indigo, IndigoObject
  6. from indigo.renderer import IndigoRenderer
  7.  
  8. _indigo = Indigo()
  9.  
  10. renderer = IndigoRenderer(_indigo)
  11. # _indigo.setOption('render-atom-ids-visible', 'true')
  12. _indigo.setOption('render-stereo-style', 'none')
  13. _indigo.setOption('render-superatom-mode', 'collapse')
  14. _indigo.setOption('render-label-mode', 'terminal-hetero')
  15.  
  16. # smiles = "Cl[C@@H]1[C@@H](N)CCCC1"
  17. smiles = "C[C@@H](C[C@@H](C)Br)C[C@H](CBr)Cl"
  18.  
  19. m = _indigo.loadMolecule(smiles)
  20.  
  21.  
  22. class RSStereoType(str, Enum):
  23.     R = 'R'
  24.     S = 'S'
  25.  
  26.  
  27. def stereo_compare(atom1: IndigoObject, atom2: IndigoObject, *, root):
  28.     if (n1:=atom1.atomicNumber()) != (n2:=atom2.atomicNumber()):
  29.         return (n1 > n2) - (n1 < n2)
  30.     atom1_neighbors = tuple(atom for atom in atom1.iterateNeighbors() if atom.index() != root.index())
  31.     atom2_neighbors = tuple(atom for atom in atom2.iterateNeighbors() if atom.index() != root.index())
  32.     if not atom1_neighbors and not atom2_neighbors:  # should be invalid
  33.         return 1
  34.     if not atom1_neighbors:
  35.         return -1
  36.     if not atom2_neighbors:
  37.         return 1
  38.     left = max(atom1_neighbors, key=cmp_to_key(partial(stereo_compare, root=atom1)))
  39.     right = max(atom2_neighbors, key=cmp_to_key(partial(stereo_compare, root=atom2)))
  40.     return stereo_compare(left, right, root=root)
  41.  
  42.  
  43. def is_clockwise(p1, p2, p3):
  44.     if((p2[0]-p1[0])*(p3[1]-p1[1]) - (p2[1]-p1[1])*(p3[0]-p1[0]))<0:
  45.         return True
  46.     return False
  47.  
  48.  
  49. def mark_stereochemistry(m: IndigoObject):
  50.     assert m.dbgInternalType() == '#02: <molecule>'  # add reaction supports?
  51.     m.layout()
  52.     # 获取所有手性原子
  53.     chiral_centers = tuple(m.iterateStereocenters())
  54.     # 所有手性中心原子的下标集合
  55.     chiral_centers_idx = {atom.index() for atom in chiral_centers}
  56.     # 查出与手性中心相连的所有键
  57.     related_bonds = sorted(tuple(chain.from_iterable(
  58.         ((bond.source().index(), bond), (bond.destination().index(), bond),)
  59.         for bond in m.iterateBonds()
  60.         if bond.source().index() in chiral_centers_idx
  61.         or bond.destination().index() in chiral_centers_idx
  62.     )), key=lambda x: x[0])
  63.     chiral_centers_mapping = {
  64.         k: tuple(x[1] for x in g)
  65.         for k, g in groupby(related_bonds, key=lambda x: x[0])
  66.         if k in chiral_centers_idx
  67.     }
  68.     # 判断最终结果是否需要反转
  69.     chiral_centers_type = {
  70.         idx: False
  71.         if any(b.bondStereo()==5 for b in bonds)
  72.         else (True if any(b.bondStereo()==6 for b in bonds) else None)
  73.         for idx, bonds in chiral_centers_mapping.items()
  74.     }
  75.  
  76.     for chiral_center in chiral_centers:
  77.         center_idx = chiral_center.index()
  78.         if (should_flip := chiral_centers_type[center_idx]) is None:
  79.             continue
  80.         # TODO skip chiral_center is not C?
  81.         neighbors = tuple(atom for atom in chiral_center.iterateNeighbors())  # TODO
  82.         # skip invalid stereo center
  83.         assert len(neighbors) >= 3
  84.         neighbors = sorted(
  85.             neighbors, key=cmp_to_key(partial(stereo_compare, root=chiral_center)),reverse=True
  86.         )
  87.         # TODO assert chiral_center inside of area of neighbors
  88.         atom1, atom2, atom3, *_ = neighbors
  89.         clockwise = is_clockwise(atom1.xyz(), atom2.xyz(), atom3.xyz())
  90.         # consider bond type flip
  91.         if should_flip:
  92.             clockwise = not clockwise
  93.         if clockwise:
  94.             stereo_type = RSStereoType.R
  95.         else:
  96.             stereo_type = RSStereoType.S
  97.         # 根据手性,设定DataSGroup
  98.         m.addDataSGroup([center_idx], [], 'stereochemistry', f"({stereo_type})")
  99.  
  100.  
  101. mark_stereochemistry(m)
  102. renderer.renderToFile(m, "D:/asdf.png")
  103.  
  104. if __name__ == '__main__':
  105.     pass
Add Comment
Please, Sign In to add comment