chython 2.4__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. chython/__init__.py +31 -0
  2. chython/_functions.py +69 -0
  3. chython/algorithms/__init__.py +18 -0
  4. chython/algorithms/_isomorphism.cp312-win_amd64.pyd +0 -0
  5. chython/algorithms/_isomorphism.pyx +189 -0
  6. chython/algorithms/aromatics/__init__.py +27 -0
  7. chython/algorithms/aromatics/_rules.py +124 -0
  8. chython/algorithms/aromatics/kekule.py +530 -0
  9. chython/algorithms/aromatics/test/__init__.py +18 -0
  10. chython/algorithms/aromatics/test/test_kekule.py +160 -0
  11. chython/algorithms/aromatics/test/test_thiele.py +263 -0
  12. chython/algorithms/aromatics/thiele.py +226 -0
  13. chython/algorithms/calculate2d/__init__.py +23 -0
  14. chython/algorithms/calculate2d/clean2d.js +1 -0
  15. chython/algorithms/calculate2d/molecule.py +149 -0
  16. chython/algorithms/calculate2d/reaction.py +80 -0
  17. chython/algorithms/depict.py +523 -0
  18. chython/algorithms/fingerprints/__init__.py +48 -0
  19. chython/algorithms/fingerprints/linear.py +208 -0
  20. chython/algorithms/fingerprints/morgan.py +137 -0
  21. chython/algorithms/fingerprints/test/__init__.py +18 -0
  22. chython/algorithms/fingerprints/test/test_linear.py +127 -0
  23. chython/algorithms/fingerprints/test/test_morgan.py +79 -0
  24. chython/algorithms/isomorphism.py +667 -0
  25. chython/algorithms/mapping/__init__.py +28 -0
  26. chython/algorithms/mapping/_groups.py +94 -0
  27. chython/algorithms/mapping/_reactions.py +72 -0
  28. chython/algorithms/mapping/attention.py +216 -0
  29. chython/algorithms/mapping/fixmapper.py +84 -0
  30. chython/algorithms/mapping/groups.py +137 -0
  31. chython/algorithms/mcs.py +202 -0
  32. chython/algorithms/morgan.py +82 -0
  33. chython/algorithms/rings.py +551 -0
  34. chython/algorithms/smiles.py +556 -0
  35. chython/algorithms/standardize/__init__.py +30 -0
  36. chython/algorithms/standardize/_charged.py +115 -0
  37. chython/algorithms/standardize/_groups.py +926 -0
  38. chython/algorithms/standardize/_metal_organics.py +189 -0
  39. chython/algorithms/standardize/_reagents.py +44 -0
  40. chython/algorithms/standardize/_salts.py +68 -0
  41. chython/algorithms/standardize/molecule.py +479 -0
  42. chython/algorithms/standardize/reaction.py +429 -0
  43. chython/algorithms/standardize/resonance.py +196 -0
  44. chython/algorithms/standardize/salts.py +128 -0
  45. chython/algorithms/standardize/saturation.py +378 -0
  46. chython/algorithms/standardize/test/__init__.py +18 -0
  47. chython/algorithms/standardize/test/test_groups.py +118 -0
  48. chython/algorithms/stereo.py +1222 -0
  49. chython/algorithms/tautomers/__init__.py +171 -0
  50. chython/algorithms/tautomers/_acid.py +59 -0
  51. chython/algorithms/tautomers/_base.py +116 -0
  52. chython/algorithms/tautomers/_keto_enol.py +107 -0
  53. chython/algorithms/tautomers/acid_base.py +214 -0
  54. chython/algorithms/tautomers/heteroarenes.py +108 -0
  55. chython/algorithms/tautomers/keto_enol.py +148 -0
  56. chython/algorithms/tautomers/test/__init__.py +18 -0
  57. chython/algorithms/tautomers/test/test_tautomers.py +80 -0
  58. chython/algorithms/test/__init__.py +18 -0
  59. chython/algorithms/test/test_isomorphism.py +58 -0
  60. chython/algorithms/test/test_smiles.py +76 -0
  61. chython/algorithms/x3dom.py +355 -0
  62. chython/containers/__init__.py +43 -0
  63. chython/containers/_pack_v2.cp312-win_amd64.pyd +0 -0
  64. chython/containers/_pack_v2.pyx +271 -0
  65. chython/containers/_unpack_v0v2.cp312-win_amd64.pyd +0 -0
  66. chython/containers/_unpack_v0v2.pyx +300 -0
  67. chython/containers/bonds.py +255 -0
  68. chython/containers/cgr.py +115 -0
  69. chython/containers/graph.py +200 -0
  70. chython/containers/molecule.py +695 -0
  71. chython/containers/query.py +66 -0
  72. chython/containers/reaction.py +324 -0
  73. chython/exceptions.py +114 -0
  74. chython/files/MRVrw.py +523 -0
  75. chython/files/PDBrw.py +257 -0
  76. chython/files/RDFrw.py +298 -0
  77. chython/files/SDFrw.py +221 -0
  78. chython/files/__init__.py +29 -0
  79. chython/files/_convert.py +207 -0
  80. chython/files/_mapping.py +117 -0
  81. chython/files/_xyz.cp312-win_amd64.pyd +0 -0
  82. chython/files/_xyz.pyx +74 -0
  83. chython/files/daylight/__init__.py +24 -0
  84. chython/files/daylight/parser.py +157 -0
  85. chython/files/daylight/smarts.py +107 -0
  86. chython/files/daylight/smiles.py +245 -0
  87. chython/files/daylight/test/__init__.py +18 -0
  88. chython/files/daylight/test/test_daylight_smarts.py +289 -0
  89. chython/files/daylight/test/test_daylight_smiles.py +101 -0
  90. chython/files/daylight/test/test_parser.py +118 -0
  91. chython/files/daylight/test/test_tokenize.py +77 -0
  92. chython/files/daylight/tokenize.py +376 -0
  93. chython/files/libinchi/__init__.py +22 -0
  94. chython/files/libinchi/libinchi.dll +0 -0
  95. chython/files/libinchi/wrapper.py +555 -0
  96. chython/files/mdl/__init__.py +25 -0
  97. chython/files/mdl/emol.py +209 -0
  98. chython/files/mdl/erxn.py +67 -0
  99. chython/files/mdl/mol.py +144 -0
  100. chython/files/mdl/read.py +225 -0
  101. chython/files/mdl/rxn.py +67 -0
  102. chython/files/mdl/stereo.py +63 -0
  103. chython/files/mdl/write.py +165 -0
  104. chython/files/xyz.py +78 -0
  105. chython/periodictable/__init__.py +69 -0
  106. chython/periodictable/base/__init__.py +25 -0
  107. chython/periodictable/base/dynamic.py +149 -0
  108. chython/periodictable/base/element.py +452 -0
  109. chython/periodictable/base/groups.py +90 -0
  110. chython/periodictable/base/periods.py +46 -0
  111. chython/periodictable/base/query.py +473 -0
  112. chython/periodictable/base/vector.py +119 -0
  113. chython/periodictable/groupI.py +252 -0
  114. chython/periodictable/groupII.py +223 -0
  115. chython/periodictable/groupIII.py +1154 -0
  116. chython/periodictable/groupIV.py +210 -0
  117. chython/periodictable/groupIX.py +196 -0
  118. chython/periodictable/groupV.py +192 -0
  119. chython/periodictable/groupVI.py +183 -0
  120. chython/periodictable/groupVII.py +162 -0
  121. chython/periodictable/groupVIII.py +161 -0
  122. chython/periodictable/groupX.py +166 -0
  123. chython/periodictable/groupXI.py +164 -0
  124. chython/periodictable/groupXII.py +158 -0
  125. chython/periodictable/groupXIII.py +239 -0
  126. chython/periodictable/groupXIV.py +262 -0
  127. chython/periodictable/groupXV.py +276 -0
  128. chython/periodictable/groupXVI.py +457 -0
  129. chython/periodictable/groupXVII.py +324 -0
  130. chython/periodictable/groupXVIII.py +260 -0
  131. chython/reactor/__init__.py +24 -0
  132. chython/reactor/base.py +212 -0
  133. chython/reactor/deprotection.py +565 -0
  134. chython/reactor/groups.py +128 -0
  135. chython/reactor/reactions/__init__.py +157 -0
  136. chython/reactor/reactions/_amidation.py +58 -0
  137. chython/reactor/reactions/_amine_isocyanate.py +52 -0
  138. chython/reactor/reactions/_buchwald_hartwig.py +50 -0
  139. chython/reactor/reactions/_esterification.py +49 -0
  140. chython/reactor/reactions/_macmillan.py +43 -0
  141. chython/reactor/reactions/_reductive_amination.py +50 -0
  142. chython/reactor/reactions/_sonogashira.py +49 -0
  143. chython/reactor/reactions/_sulfonamidation.py +52 -0
  144. chython/reactor/reactions/_suzuki_miyaura.py +88 -0
  145. chython/reactor/reactions/ufe.py +99 -0
  146. chython/reactor/reactor.py +170 -0
  147. chython/reactor/retro/__init__.py +77 -0
  148. chython/reactor/retro/_amidation.py +39 -0
  149. chython/reactor/retro/_aryl_amination.py +32 -0
  150. chython/reactor/retro/_mitsunobu.py +49 -0
  151. chython/reactor/retro/_sonogashira.py +49 -0
  152. chython/reactor/retro/_suzuki_miyaura.py +54 -0
  153. chython/reactor/scaffold.py +122 -0
  154. chython/reactor/test/__init__.py +18 -0
  155. chython/reactor/test/test_deprotection.py +57 -0
  156. chython/reactor/test/test_reactor.py +40 -0
  157. chython/reactor/test/test_scaffold.py +57 -0
  158. chython/reactor/test/test_transformer.py +40 -0
  159. chython/reactor/transformer.py +62 -0
  160. chython/utils/__init__.py +57 -0
  161. chython/utils/free_wilson.py +114 -0
  162. chython/utils/grid.py +130 -0
  163. chython/utils/rdkit.py +165 -0
  164. chython/utils/retro.py +139 -0
  165. chython/utils/svg.py +57 -0
  166. chython/utils/test/__init__.py +18 -0
  167. chython/utils/test/test_rdkit.py +71 -0
  168. chython-2.4.dist-info/LICENSE +165 -0
  169. chython-2.4.dist-info/METADATA +101 -0
  170. chython-2.4.dist-info/RECORD +171 -0
  171. chython-2.4.dist-info/WHEEL +4 -0
@@ -0,0 +1,555 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2018-2024 Ramil Nugmanov <nougmanoff@protonmail.com>
4
+ # This file is part of chython.
5
+ #
6
+ # chython is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation; either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program; if not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ from ctypes import c_char, c_double, c_short, c_long, c_char_p, c_byte, POINTER, Structure, cdll, byref
20
+ from itertools import count
21
+ from sysconfig import get_platform
22
+ from warnings import warn
23
+ from .._convert import create_molecule
24
+ from ...containers import MoleculeContainer
25
+ from ...containers.bonds import Bond
26
+ from ...exceptions import ValenceError, IsChiral, NotChiral
27
+ from ...periodictable import H as _H
28
+
29
+
30
+ try:
31
+ from importlib.resources import files, as_file
32
+ except ImportError: # python3.8
33
+ from importlib_resources import files, as_file
34
+
35
+
36
+ H = 1
37
+
38
+
39
+ def inchi(data, /, *, ignore_stereo: bool = False, _cls=MoleculeContainer) -> MoleculeContainer:
40
+ """
41
+ INCHI string parser
42
+ """
43
+ if lib is None:
44
+ raise ImportError('libINCHI not found')
45
+
46
+ structure = INCHIStructure()
47
+ if lib.GetStructFromINCHI(byref(InputINCHI(data)), byref(structure)):
48
+ lib.FreeStructFromINCHI(byref(structure))
49
+ raise ValueError('invalid INCHI')
50
+
51
+ atoms, bonds = [], []
52
+ protium = {}
53
+ deuterium = {}
54
+ tritium = {}
55
+ seen = set()
56
+ for n in range(structure.num_atoms):
57
+ seen.add(n)
58
+ atom = structure.atom[n]
59
+
60
+ atoms.append({'element': atom.atomic_symbol, 'charge': atom.charge, 'x': atom.x, 'y': atom.y,
61
+ 'z': atom.z, 'isotope': atom.isotope, 'is_radical': atom.is_radical,
62
+ 'implicit_hydrogens': atom.implicit_hydrogens, 'delta_isotope': atom.delta_isotope})
63
+ if atom.implicit_protium:
64
+ protium[n] = atom.implicit_protium
65
+ if atom.implicit_deuterium:
66
+ deuterium[n] = atom.implicit_deuterium
67
+ if atom.implicit_tritium:
68
+ tritium[n] = atom.implicit_tritium
69
+
70
+ for k in range(atom.num_bonds):
71
+ m = atom.neighbor[k]
72
+ if m in seen:
73
+ continue
74
+ order = atom.bond_type[k]
75
+ if order:
76
+ bonds.append((n, m, order))
77
+
78
+ stereo_atoms = []
79
+ stereo_allenes = []
80
+ stereo_cumulenes = []
81
+ for i in range(structure.num_stereo0D):
82
+ stereo = structure.stereo0D[i]
83
+ sign = stereo.sign
84
+ if sign is not None:
85
+ if stereo.is_tetrahedral:
86
+ stereo_atoms.append((stereo.central_atom, stereo.neighbors, sign))
87
+ elif stereo.is_allene:
88
+ nn, *_, nm = stereo.neighbors
89
+ stereo_allenes.append((stereo.central_atom, nn, nm, sign))
90
+ elif stereo.is_cumulene:
91
+ nn, n, m, nm = stereo.neighbors
92
+ stereo_cumulenes.append((n, m, nn, nm, sign))
93
+
94
+ lib.FreeStructFromINCHI(byref(structure))
95
+
96
+ tmp = {'atoms': atoms, 'bonds': bonds, 'stereo_atoms': stereo_atoms, 'stereo_allenes': stereo_allenes,
97
+ 'stereo_cumulenes': stereo_cumulenes, 'mapping': list(range(1, len(atoms) + 1)),
98
+ 'protium': protium, 'deuterium': deuterium, 'tritium': tritium}
99
+ mol = create_molecule(tmp, skip_calc_implicit=True, _cls=_cls)
100
+ postprocess_molecule(mol, tmp, ignore_stereo=ignore_stereo)
101
+ return mol
102
+
103
+
104
+ def postprocess_molecule(molecule, data, *, ignore_stereo=False):
105
+ atoms = molecule._atoms
106
+ bonds = molecule._bonds
107
+
108
+ # set hydrogen atoms. INCHI designed for hydrogens handling. hope correctly.
109
+ free = count(len(atoms) + 1)
110
+ to_add = []
111
+ for n, atom in atoms.items():
112
+ # in chython hydrogens never have implicit H. convert to explicit
113
+ if atom == H and atom.implicit_hydrogens:
114
+ for _ in range(atom.implicit_hydrogens):
115
+ to_add.append((n, next(free), _H(implicit_hydrogens=0)))
116
+ atom._implicit_hydrogens = 0
117
+
118
+ for n, p in data['protium'].items():
119
+ to_add.append((n + 1, next(free), _H(isotope=1, implicit_hydrogens=0)))
120
+ for n, p in data['deuterium'].items():
121
+ to_add.append((n + 1, next(free), _H(isotope=2, implicit_hydrogens=0)))
122
+ for n, p in data['tritium'].items():
123
+ to_add.append((n + 1, next(free), _H(isotope=3, implicit_hydrogens=0)))
124
+
125
+ if to_add:
126
+ for n, m, a in to_add:
127
+ atoms[m] = a
128
+ bonds[n][m] = b = Bond(1)
129
+ bonds[m] = {n: b}
130
+ molecule.calc_labels() # reset labels
131
+
132
+ if ignore_stereo or not data['stereo_atoms'] and not data['stereo_cumulenes'] and not data['stereo_allenes']:
133
+ return
134
+
135
+ st = molecule.stereogenic_tetrahedrons
136
+ sa = molecule.stereogenic_allenes
137
+ ctc = molecule._stereo_cis_trans_counterpart
138
+
139
+ stereo = []
140
+ for n, ngb, s in data['stereo_atoms']:
141
+ n += 1
142
+ if n in st:
143
+ stereo.append((molecule.add_atom_stereo, n, [x + 1 for x in ngb], s))
144
+ for n, nn, mn, s in data['stereo_allenes']:
145
+ n += 1
146
+ if n in sa:
147
+ stereo.append((molecule.add_atom_stereo, n, nn + 1, mn + 1, s))
148
+ for n, m, nn, nm, s in data['stereo_cumulenes']:
149
+ n += 1
150
+ if n in ctc:
151
+ stereo.append((molecule.add_cis_trans_stereo, n, m + 1, nn + 1, nm + 1, s))
152
+
153
+ while stereo:
154
+ fail_stereo = []
155
+ old_stereo = len(stereo)
156
+ for f, *args in stereo:
157
+ try:
158
+ f(*args, clean_cache=False)
159
+ except NotChiral:
160
+ fail_stereo.append((f, *args))
161
+ except IsChiral:
162
+ pass
163
+ except ValenceError:
164
+ if 'chython_parsing_log' not in molecule.meta:
165
+ molecule.meta['chython_parsing_log'] = []
166
+ molecule.meta['chython_parsing_log'].append('structure has errors, stereo data skipped')
167
+ molecule.flush_cache()
168
+ break
169
+ else:
170
+ stereo = fail_stereo
171
+ if len(stereo) == old_stereo:
172
+ break
173
+ molecule.flush_stereo_cache()
174
+ continue
175
+ break
176
+
177
+
178
+ class InputINCHI(Structure):
179
+ def __init__(self, string, options=None):
180
+ if options is None:
181
+ options = b''
182
+ else:
183
+ options = ' '.join(f'{opt_flag}{x}' for x in options).encode()
184
+ super().__init__(string.encode(), options)
185
+
186
+ _fields_ = [('szInChI', c_char_p), # InChI ASCII string to be converted to a strucure
187
+ ('szOptions', c_char_p) # InChI options: space-delimited; each is preceded
188
+ # by '/' or '-' depending on OS and compiler
189
+ ]
190
+
191
+
192
+ class Atom(Structure):
193
+ @property
194
+ def atomic_symbol(self):
195
+ return self.elname.decode()
196
+
197
+ @property
198
+ def isotope(self):
199
+ if 0 < self.isotopic_mass < 9000: # OVER NINE THOUSANDS!
200
+ return self.isotopic_mass
201
+
202
+ @property
203
+ def delta_isotope(self):
204
+ if self.isotopic_mass > 9000:
205
+ return self.isotopic_mass - 10_000
206
+
207
+ @property
208
+ def is_radical(self):
209
+ return bool(self.radical)
210
+
211
+ @property
212
+ def implicit_hydrogens(self):
213
+ return self.num_iso_H[0]
214
+
215
+ @property
216
+ def implicit_protium(self):
217
+ return self.num_iso_H[1]
218
+
219
+ @property
220
+ def implicit_deuterium(self):
221
+ return self.num_iso_H[2]
222
+
223
+ @property
224
+ def implicit_tritium(self):
225
+ return self.num_iso_H[3]
226
+
227
+ _fields_ = [('x', c_double), ('y', c_double), ('z', c_double), # atom coordinates
228
+ ('neighbor', c_short * 20), # adjacency list: ordering numbers of the adjacent atoms, >= 0
229
+ ('bond_type', c_byte * 20), # inchi_BondType
230
+ ('bond_stereo', c_byte * 20), # inchi_BondStereo2D; negative if the sharp end points to opposite atom
231
+ ('elname', c_char * 6), # zero-terminated chemical element name: "H", "Si", etc.
232
+ ('num_bonds', c_short), # number of neighbors, bond types and bond stereo in the adjacency list
233
+ ('num_iso_H', c_byte * 4), # implicit hydrogen atoms
234
+ # [0]: number of implicit non-isotopic H
235
+ # (exception: num_iso_H[0]=-1 means INCHI adds implicit H automatically),
236
+ # [1]: number of implicit isotopic 1H (protium),
237
+ # [2]: number of implicit 2H (deuterium),
238
+ # [3]: number of implicit 3H (tritium)
239
+ ('isotopic_mass', c_short), # 0 => non-isotopic; isotopic mass or 10000 + mass - average atomic mass
240
+ ('radical', c_byte), # inchi_Radical,
241
+ ('charge', c_byte)] # positive or negative; 0 => no charge
242
+
243
+
244
+ class Stereo0D(Structure):
245
+ @property
246
+ def is_tetrahedral(self):
247
+ return self.type == 2
248
+
249
+ @property
250
+ def is_allene(self):
251
+ return self.type == 3
252
+
253
+ @property
254
+ def is_cumulene(self):
255
+ return self.type == 1
256
+
257
+ @property
258
+ def neighbors(self):
259
+ return tuple(self.neighbor)
260
+
261
+ @property
262
+ def sign(self):
263
+ if self.parity == 1:
264
+ return True
265
+ elif self.parity == 2:
266
+ return False
267
+
268
+ _fields_ = [('neighbor', c_short * 4), # 4 atoms always
269
+ ('central_atom', c_short), # central tetrahedral atom or a central atom of allene; otherwise NO_ATOM
270
+ ('type', c_byte), # inchi_StereoType0D
271
+ ('parity', c_byte)] # inchi_StereoParity0D
272
+
273
+
274
+ class INCHIStructure(Structure):
275
+ _fields_ = [('atom', POINTER(Atom)), # array of num_atoms elements
276
+ ('stereo0D', POINTER(Stereo0D)), # array of num_stereo0D 0D stereo elements or NULL
277
+ ('num_atoms', c_short), # number of atoms in the structure
278
+ ('num_stereo0D', c_short), # number of 0D stereo elements
279
+ ('szMessage', c_char_p), # Error/warning ASCII message
280
+ ('szLog', c_char_p), # log-file ASCII string, contains a human-readable list
281
+ # of recognized options and possibly an Error/warn message
282
+ ('WarningFlags', (c_long * 2) * 2)]
283
+
284
+ # copy-pasted from INCHI-API
285
+ # * Notes: 1. Atom ordering numbers (i, k, and atom[i].neighbor[j] below)
286
+ # * start from zero; max. ordering number is (num_atoms-1).
287
+ # * 2. inchi_Atom atom[i] is connected to the atom[atom[i].neighbor[j]]
288
+ # * by a bond that has type atom[i].bond_type[j] and 2D stereo type
289
+ # * atom[i].bond_stereo[j] (in case of no stereo
290
+ # * atom[i].bond_stereo[j] = INCHI_BOND_STEREO_NONE)
291
+ # * Index j is in the range 0 <= j <= (atom[i].num_bonds-1)
292
+ # * 3. Any connection (represented by atom[i].neighbor[j],
293
+ # * atom[i].bond_type[j], and atom[i].bond_stereo[j])
294
+ # * should be present in one or both adjacency list:
295
+ # * if k = atom[i].neighbor[j] then i may or may not be present in
296
+ # * atom[k].neighbor[] list. For example, the adjacency lists may be
297
+ # * populated with only such neighbors that atom[i].neighbor[j] < i
298
+ # * All elements of an adjacency list must be different, that is,
299
+ # * a bond must be specified in an adjacency list only once.
300
+ # * 4. in Molfiles usually
301
+ # * (number of implicit H) = Valence - SUM(bond_type[])
302
+ # * 5. Seemingly illogical order of the inchi_Atom members was
303
+ # * chosen in an attempt to avoid alignment problems when
304
+ # * accessing inchi_Atom from unrelated to C programming
305
+ # * languages such as Visual Basic.
306
+ # *******************************************************************/
307
+ #
308
+ # /*******************************************************************
309
+ # 0D Stereo Parity and Type definitions
310
+ # *******************************************************************
311
+ # Note:
312
+ # =====
313
+ # o Below #A is the ordering number of atom A, starting from 0
314
+ # o See parity values corresponding to 'o', 'e', and 'u' in
315
+ # inchi_StereoParity0D definition below)
316
+ #
317
+ # =============================================
318
+ # stereogenic bond >A=B< or cumulene >A=C=C=B<
319
+ # =============================================
320
+ #
321
+ # neighbor[4] : {#X,#A,#B,#Y} in this order
322
+ # X central_atom : NO_ATOM
323
+ # \ X Y type : INCHI_StereoType_DoubleBond
324
+ # A==B \ /
325
+ # \ A==B
326
+ # Y
327
+ #
328
+ # parity= 'e' parity= 'o' unknown parity = 'u'
329
+ #
330
+ # Limitations:
331
+ # ============
332
+ # o Atoms A and B in cumulenes MUST be connected by a chain of double bonds;
333
+ # atoms A and B in a stereogenic 'double bond' may be connected by a double,
334
+ # single, or alternating bond.
335
+ # o One atom may belong to up to 3 stereogenic bonds (i.g. in a fused
336
+ # aromatic structure).
337
+ # o Multiple stereogenic bonds incident to any given atom should
338
+ # either all except possibly one have (possibly different) defined
339
+ # parities ('o' or 'e') or should all have an unknown parity 'u'.
340
+ #
341
+ # Note on parities of alternating stereobonds
342
+ # ===========================================
343
+ # D--E
344
+ # In large rings (see Fig. 1, all // \\
345
+ # atoms are C) all alternating bonds B--C F--G
346
+ # are treated as stereogenic. // \\
347
+ # To avoid "undefined" bond parities A H
348
+ # for bonds BC, DE, FG, HI, JK, LM, AN \ /
349
+ # it is recommended to mark them with N==M J==I
350
+ # parities. \ /
351
+ # L==K Fig. 1
352
+ # Such a marking will make
353
+ # the stereochemical layer unambiguous
354
+ # and it will be different from the B--C F--G
355
+ # stereochemical layer of the second // \\ // \\
356
+ # structure (Fig. 2). A D--E H
357
+ # \ /
358
+ # N==M J==I
359
+ # By default, double and alternating \ /
360
+ # bonds in 8-member and greater rings L==K Fig. 2
361
+ # are treated by InChI as stereogenic.
362
+ #
363
+ #
364
+ # =============================================
365
+ # tetrahedral atom
366
+ # =============================================
367
+ #
368
+ # 4 neighbors
369
+ #
370
+ # X neighbor[4] : {#W, #X, #Y, #Z}
371
+ # | central_atom: #A
372
+ # W--A--Y type : INCHI_StereoType_Tetrahedral
373
+ # |
374
+ # Z
375
+ # parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o'
376
+ # Example (see AXYZW above): if W is above the plane XYZ then parity = 'e'
377
+ #
378
+ # 3 neighbors
379
+ #
380
+ # Y Y neighbor[4] : {#A, #X, #Y, #Z}
381
+ # / / central_atom: #A
382
+ # X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral
383
+ # \ \
384
+ # Z Z
385
+ #
386
+ # parity: if (X,Y,Z) are clockwize when seen from A then parity is 'e',
387
+ # otherwise 'o'
388
+ # unknown parity = 'u'
389
+ # Example (see AXYZ above): if A is above the plane XYZ then parity = 'e'
390
+ # This approach may be used also in case of an implicit H attached to A.
391
+ #
392
+ # =============================================
393
+ # allene
394
+ # =============================================
395
+ #
396
+ # X Y neighbor[4] : {#X,#A,#B,#Y}
397
+ # \ / central_atom : #C
398
+ # A=C=B type : INCHI_StereoType_Allene
399
+ #
400
+ # Y X
401
+ # | |
402
+ # when seen from A along A=C=B: X-A Y-A
403
+ #
404
+ # parity: 'e' 'o'
405
+ #
406
+ # parity: if A, B, Y are clockwise when seen from X then parity is 'e',
407
+ # otherwise 'o'
408
+ # unknown parity = 'u'
409
+ # Example (see XACBY above): if X on the diagram is above the plane ABY
410
+ # then parity is 'o'
411
+ #
412
+ # Limitations
413
+ # ===========
414
+ # o Atoms A and B in allenes MUST be connected by a chain of double bonds;
415
+ #
416
+ #
417
+ # How InChI uses 0D parities
418
+ # ==========================
419
+ #
420
+ # 1. 0D parities are used if all atom coordinates are zeroes.
421
+ #
422
+ # In addition to that:
423
+ #
424
+ # 2. 0D parities are used for Stereobonds, Allenes, or Cumulenes if:
425
+ #
426
+ # 2a. A bond to the end-atom is shorter than MIN_BOND_LEN=0.000001
427
+ # 2b. A ratio of two bond lengths to the end-atom is smaller than MIN_SINE=0.03
428
+ # 2c. In case of a linear fragment X-A=B end-atom A is treated as satisfying 2a-b
429
+ #
430
+ # 0D parities are used if 2a or 2b or 2c applies to one or both end-atoms.
431
+ #
432
+ # 3. 0D parities are used for Tetrahedral Atoms if at least one of 3a-c is true:
433
+ #
434
+ # 3a. One of bonds to the central atom is shorter than MIN_BOND_LEN=0.000001
435
+ # 3b. A ratio of two bond lengths to the central atom is smaller than MIN_SINE=0.03
436
+ # 3c. The four neighbors are almost in one plane or the central atom and
437
+ # its only 3 explicit neighbors are almost in one plane
438
+ #
439
+ # Notes on 0D parities and 'undefined' stereogenic elements
440
+ # =========================================================
441
+ #
442
+ # If 0D parity is to be used according to 1-3 but CH3 CH3
443
+ # has not been provided then the corresponding \ /
444
+ # stereogenic element is considered 'undefined'. C=CH
445
+ # /
446
+ # For example, if in the structure (Fig. 3) H
447
+ # the explicit H has been moved so that it Fig. 3
448
+ # has same coordinates as atom >C= (that is,
449
+ # the length of the bond H-C became zero)
450
+ # then the double bond is assigned 'undefined' CH3 CH3
451
+ # parity which by default is omitted from the \ /
452
+ # Identifier. CH=CH
453
+ #
454
+ # However, the structure on Fig. 4 will have double Fig. 4
455
+ # bond parity 'o' and its parity in the Identifier is (-).
456
+ #
457
+ # Notes on 0D parities in structures containing metals
458
+ # ====================================================
459
+ # Since InChI disconnects bonds to metals the 0D parities upon the
460
+ # disconnection may change in several different ways:
461
+ #
462
+ # 1) previously non-stereogenic bond may become stereogenic:
463
+ #
464
+ # \ / \ /
465
+ # CH==CH disconnection CH==CH
466
+ # \ / ======>
467
+ # M M
468
+ #
469
+ # before the disconnection: after the disconnection:
470
+ # atoms C have valence=5 and the double bond may become
471
+ # the double bond is not stereogenic
472
+ # recognized as stereogenic
473
+ #
474
+ # 2) previously stereogenic bond may become non-stereogenic:
475
+ #
476
+ # M M(+)
477
+ # \ / /
478
+ # N==C disconnection (-)N==C
479
+ # \ ======> \
480
+ #
481
+ # 3) Oddball structures, usually resulting from projecting 3D
482
+ # structures on the plane, may contain fragment like that
483
+ # depicted on Fig. 5:
484
+ #
485
+ # M A M A
486
+ # |\ / B / B
487
+ # | X / disconnection / /
488
+ # |/ \ / ======> / /
489
+ # C===C C===C
490
+ # Fig. 5
491
+ # (X stands for bond intersection)
492
+ #
493
+ # A-C=C-B parity is A-C=C-B parity is
494
+ # trans (e) cis (o) or undefined
495
+ # because the bond because C valence = 3,
496
+ # orientation is same not 4.
497
+ # as on Fig, 6 below:
498
+ #
499
+ # A M
500
+ # \ / Removal of M from the structure
501
+ # C===C on Fig. 5 changes the geometry from trans
502
+ # / \ to cis.
503
+ # M' B Removal of M and M' from the structure
504
+ # Fig. 6 on Fig. 6 does not change the A-C=C-B
505
+ # geometry: it is trans.
506
+ #
507
+ # To resolve the problem InChI API accepts the second parity
508
+ # corresponding to the metal-disconnected structure.
509
+ # To store both bond parities use left shift by 3 bits:
510
+ #
511
+ # inchi_Stereo0D::parity = ParityOfConnected | (ParityOfDisconnected<<3)
512
+ #
513
+ # In case when only disconnected structure parity exists set
514
+ # ParityOfConnected = INCHI_PARITY_UNDEFINED.
515
+ # This is the only case when INCHI_PARITY_UNDEFINED parity
516
+ # may be fed to the InChI.
517
+ #
518
+ # In cases when the bond parity in a disconnected structure exists and
519
+ # differs from the parity in the connected structure the atoms A and B
520
+ # should be non-metals.
521
+ #
522
+
523
+
524
+ lib = None
525
+
526
+ platform = get_platform()
527
+ if platform == 'win-amd64':
528
+ opt_flag = '/'
529
+ libname = 'libinchi.dll'
530
+ elif platform == 'linux-x86_64':
531
+ opt_flag = '-'
532
+ libname = 'libinchi.so'
533
+ elif platform.startswith('macosx') and platform.endswith('x86_64'):
534
+ opt_flag = '-'
535
+ libname = 'libinchi.dynlib'
536
+ elif platform.startswith('macosx') and platform.endswith('arm64'):
537
+ opt_flag = '-'
538
+ libname = 'libinchi_arm64.dylib'
539
+ else:
540
+ warn('unsupported platform for libinchi', ImportWarning)
541
+ libname = None
542
+
543
+ if libname:
544
+ file = files(__package__).joinpath(libname)
545
+ if file.is_file():
546
+ with as_file(file) as f:
547
+ try:
548
+ lib = cdll.LoadLibrary(str(f))
549
+ except OSError:
550
+ warn('libinchi loading problem', ImportWarning)
551
+ else:
552
+ warn('broken package installation. libinchi not found', ImportWarning)
553
+
554
+
555
+ __all__ = ['inchi']
@@ -0,0 +1,25 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2017-2024 Ramil Nugmanov <nougmanoff@protonmail.com>
4
+ # This file is part of chython.
5
+ #
6
+ # chython is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation; either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program; if not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ from .mol import parse_mol_v2000
20
+ from .emol import parse_mol_v3000
21
+ from .rxn import parse_rxn_v2000
22
+ from .erxn import parse_rxn_v3000
23
+ from .stereo import postprocess_molecule
24
+ from .read import MDLRead
25
+ from .write import MOLWrite, EMOLWrite