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.
- chython/__init__.py +31 -0
- chython/_functions.py +69 -0
- chython/algorithms/__init__.py +18 -0
- chython/algorithms/_isomorphism.cp312-win_amd64.pyd +0 -0
- chython/algorithms/_isomorphism.pyx +189 -0
- chython/algorithms/aromatics/__init__.py +27 -0
- chython/algorithms/aromatics/_rules.py +124 -0
- chython/algorithms/aromatics/kekule.py +530 -0
- chython/algorithms/aromatics/test/__init__.py +18 -0
- chython/algorithms/aromatics/test/test_kekule.py +160 -0
- chython/algorithms/aromatics/test/test_thiele.py +263 -0
- chython/algorithms/aromatics/thiele.py +226 -0
- chython/algorithms/calculate2d/__init__.py +23 -0
- chython/algorithms/calculate2d/clean2d.js +1 -0
- chython/algorithms/calculate2d/molecule.py +149 -0
- chython/algorithms/calculate2d/reaction.py +80 -0
- chython/algorithms/depict.py +523 -0
- chython/algorithms/fingerprints/__init__.py +48 -0
- chython/algorithms/fingerprints/linear.py +208 -0
- chython/algorithms/fingerprints/morgan.py +137 -0
- chython/algorithms/fingerprints/test/__init__.py +18 -0
- chython/algorithms/fingerprints/test/test_linear.py +127 -0
- chython/algorithms/fingerprints/test/test_morgan.py +79 -0
- chython/algorithms/isomorphism.py +667 -0
- chython/algorithms/mapping/__init__.py +28 -0
- chython/algorithms/mapping/_groups.py +94 -0
- chython/algorithms/mapping/_reactions.py +72 -0
- chython/algorithms/mapping/attention.py +216 -0
- chython/algorithms/mapping/fixmapper.py +84 -0
- chython/algorithms/mapping/groups.py +137 -0
- chython/algorithms/mcs.py +202 -0
- chython/algorithms/morgan.py +82 -0
- chython/algorithms/rings.py +551 -0
- chython/algorithms/smiles.py +556 -0
- chython/algorithms/standardize/__init__.py +30 -0
- chython/algorithms/standardize/_charged.py +115 -0
- chython/algorithms/standardize/_groups.py +926 -0
- chython/algorithms/standardize/_metal_organics.py +189 -0
- chython/algorithms/standardize/_reagents.py +44 -0
- chython/algorithms/standardize/_salts.py +68 -0
- chython/algorithms/standardize/molecule.py +479 -0
- chython/algorithms/standardize/reaction.py +429 -0
- chython/algorithms/standardize/resonance.py +196 -0
- chython/algorithms/standardize/salts.py +128 -0
- chython/algorithms/standardize/saturation.py +378 -0
- chython/algorithms/standardize/test/__init__.py +18 -0
- chython/algorithms/standardize/test/test_groups.py +118 -0
- chython/algorithms/stereo.py +1222 -0
- chython/algorithms/tautomers/__init__.py +171 -0
- chython/algorithms/tautomers/_acid.py +59 -0
- chython/algorithms/tautomers/_base.py +116 -0
- chython/algorithms/tautomers/_keto_enol.py +107 -0
- chython/algorithms/tautomers/acid_base.py +214 -0
- chython/algorithms/tautomers/heteroarenes.py +108 -0
- chython/algorithms/tautomers/keto_enol.py +148 -0
- chython/algorithms/tautomers/test/__init__.py +18 -0
- chython/algorithms/tautomers/test/test_tautomers.py +80 -0
- chython/algorithms/test/__init__.py +18 -0
- chython/algorithms/test/test_isomorphism.py +58 -0
- chython/algorithms/test/test_smiles.py +76 -0
- chython/algorithms/x3dom.py +355 -0
- chython/containers/__init__.py +43 -0
- chython/containers/_pack_v2.cp312-win_amd64.pyd +0 -0
- chython/containers/_pack_v2.pyx +271 -0
- chython/containers/_unpack_v0v2.cp312-win_amd64.pyd +0 -0
- chython/containers/_unpack_v0v2.pyx +300 -0
- chython/containers/bonds.py +255 -0
- chython/containers/cgr.py +115 -0
- chython/containers/graph.py +200 -0
- chython/containers/molecule.py +695 -0
- chython/containers/query.py +66 -0
- chython/containers/reaction.py +324 -0
- chython/exceptions.py +114 -0
- chython/files/MRVrw.py +523 -0
- chython/files/PDBrw.py +257 -0
- chython/files/RDFrw.py +298 -0
- chython/files/SDFrw.py +221 -0
- chython/files/__init__.py +29 -0
- chython/files/_convert.py +207 -0
- chython/files/_mapping.py +117 -0
- chython/files/_xyz.cp312-win_amd64.pyd +0 -0
- chython/files/_xyz.pyx +74 -0
- chython/files/daylight/__init__.py +24 -0
- chython/files/daylight/parser.py +157 -0
- chython/files/daylight/smarts.py +107 -0
- chython/files/daylight/smiles.py +245 -0
- chython/files/daylight/test/__init__.py +18 -0
- chython/files/daylight/test/test_daylight_smarts.py +289 -0
- chython/files/daylight/test/test_daylight_smiles.py +101 -0
- chython/files/daylight/test/test_parser.py +118 -0
- chython/files/daylight/test/test_tokenize.py +77 -0
- chython/files/daylight/tokenize.py +376 -0
- chython/files/libinchi/__init__.py +22 -0
- chython/files/libinchi/libinchi.dll +0 -0
- chython/files/libinchi/wrapper.py +555 -0
- chython/files/mdl/__init__.py +25 -0
- chython/files/mdl/emol.py +209 -0
- chython/files/mdl/erxn.py +67 -0
- chython/files/mdl/mol.py +144 -0
- chython/files/mdl/read.py +225 -0
- chython/files/mdl/rxn.py +67 -0
- chython/files/mdl/stereo.py +63 -0
- chython/files/mdl/write.py +165 -0
- chython/files/xyz.py +78 -0
- chython/periodictable/__init__.py +69 -0
- chython/periodictable/base/__init__.py +25 -0
- chython/periodictable/base/dynamic.py +149 -0
- chython/periodictable/base/element.py +452 -0
- chython/periodictable/base/groups.py +90 -0
- chython/periodictable/base/periods.py +46 -0
- chython/periodictable/base/query.py +473 -0
- chython/periodictable/base/vector.py +119 -0
- chython/periodictable/groupI.py +252 -0
- chython/periodictable/groupII.py +223 -0
- chython/periodictable/groupIII.py +1154 -0
- chython/periodictable/groupIV.py +210 -0
- chython/periodictable/groupIX.py +196 -0
- chython/periodictable/groupV.py +192 -0
- chython/periodictable/groupVI.py +183 -0
- chython/periodictable/groupVII.py +162 -0
- chython/periodictable/groupVIII.py +161 -0
- chython/periodictable/groupX.py +166 -0
- chython/periodictable/groupXI.py +164 -0
- chython/periodictable/groupXII.py +158 -0
- chython/periodictable/groupXIII.py +239 -0
- chython/periodictable/groupXIV.py +262 -0
- chython/periodictable/groupXV.py +276 -0
- chython/periodictable/groupXVI.py +457 -0
- chython/periodictable/groupXVII.py +324 -0
- chython/periodictable/groupXVIII.py +260 -0
- chython/reactor/__init__.py +24 -0
- chython/reactor/base.py +212 -0
- chython/reactor/deprotection.py +565 -0
- chython/reactor/groups.py +128 -0
- chython/reactor/reactions/__init__.py +157 -0
- chython/reactor/reactions/_amidation.py +58 -0
- chython/reactor/reactions/_amine_isocyanate.py +52 -0
- chython/reactor/reactions/_buchwald_hartwig.py +50 -0
- chython/reactor/reactions/_esterification.py +49 -0
- chython/reactor/reactions/_macmillan.py +43 -0
- chython/reactor/reactions/_reductive_amination.py +50 -0
- chython/reactor/reactions/_sonogashira.py +49 -0
- chython/reactor/reactions/_sulfonamidation.py +52 -0
- chython/reactor/reactions/_suzuki_miyaura.py +88 -0
- chython/reactor/reactions/ufe.py +99 -0
- chython/reactor/reactor.py +170 -0
- chython/reactor/retro/__init__.py +77 -0
- chython/reactor/retro/_amidation.py +39 -0
- chython/reactor/retro/_aryl_amination.py +32 -0
- chython/reactor/retro/_mitsunobu.py +49 -0
- chython/reactor/retro/_sonogashira.py +49 -0
- chython/reactor/retro/_suzuki_miyaura.py +54 -0
- chython/reactor/scaffold.py +122 -0
- chython/reactor/test/__init__.py +18 -0
- chython/reactor/test/test_deprotection.py +57 -0
- chython/reactor/test/test_reactor.py +40 -0
- chython/reactor/test/test_scaffold.py +57 -0
- chython/reactor/test/test_transformer.py +40 -0
- chython/reactor/transformer.py +62 -0
- chython/utils/__init__.py +57 -0
- chython/utils/free_wilson.py +114 -0
- chython/utils/grid.py +130 -0
- chython/utils/rdkit.py +165 -0
- chython/utils/retro.py +139 -0
- chython/utils/svg.py +57 -0
- chython/utils/test/__init__.py +18 -0
- chython/utils/test/test_rdkit.py +71 -0
- chython-2.4.dist-info/LICENSE +165 -0
- chython-2.4.dist-info/METADATA +101 -0
- chython-2.4.dist-info/RECORD +171 -0
- chython-2.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2020-2024 Ramil Nugmanov <nougmanoff@protonmail.com>
|
|
4
|
+
# Copyright 2020 Dinar Batyrshin <batyrshin-dinar@mail.ru>
|
|
5
|
+
# This file is part of chython.
|
|
6
|
+
#
|
|
7
|
+
# chython is free software; you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
9
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU Lesser General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
18
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
#
|
|
20
|
+
from math import acos, sqrt
|
|
21
|
+
from typing import TYPE_CHECKING, Union
|
|
22
|
+
from .depict import _render_config
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from chython import MoleculeContainer
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def plane_normal(nmx, nmy, nmz, nox, noy, noz):
|
|
30
|
+
# return normal to plane of two vectors nm and no
|
|
31
|
+
# m <--- n
|
|
32
|
+
# \
|
|
33
|
+
# v
|
|
34
|
+
# o
|
|
35
|
+
return nmy * noz - nmz * noy, nox * nmz - nmx * noz, nmx * noy - nmy * nox
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def unit_vector(nmx, nmy, nmz):
|
|
39
|
+
nmd = sqrt(nmx ** 2 + nmy ** 2 + nmz ** 2)
|
|
40
|
+
return nmx / nmd, nmy / nmd, nmz / nmd
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_angle(nx, ny, nz, mx, my, mz):
|
|
44
|
+
ch = (nx * mx + ny * my + nz * mz) ** 2
|
|
45
|
+
zn = (nx ** 2 + ny ** 2 + nz ** 2) * (mx ** 2 + my ** 2 + mz ** 2)
|
|
46
|
+
if ch < .0001:
|
|
47
|
+
return 1.
|
|
48
|
+
elif zn < .0001:
|
|
49
|
+
return .0
|
|
50
|
+
else:
|
|
51
|
+
return sqrt(1 - ch / zn)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def vector_normal(nmx, nmy, nmz):
|
|
55
|
+
# return normal to vector nm
|
|
56
|
+
if not -.0001 < nmx < .0001:
|
|
57
|
+
return (- nmy - nmz) / nmx, 1, 1
|
|
58
|
+
elif not -.0001 < nmy < .0001:
|
|
59
|
+
return 1, (- nmx - nmz) / nmy, 1
|
|
60
|
+
else:
|
|
61
|
+
return 1, 1, (- nmx - nmy) / nmz
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class JupyterWidget:
|
|
65
|
+
def __init__(self, xml, width, height):
|
|
66
|
+
self.xml = xml
|
|
67
|
+
self.width = width
|
|
68
|
+
self.height = height
|
|
69
|
+
|
|
70
|
+
def _repr_html_(self):
|
|
71
|
+
return ("<script type='text/javascript' src='https://www.x3dom.org/download/x3dom.js'></script>"
|
|
72
|
+
"<link rel='stylesheet' type='text/css' href='https://www.x3dom.org/download/x3dom.css'>"
|
|
73
|
+
f'<div style="width: {self.width}; height: {self.height}">{self.xml}</div>')
|
|
74
|
+
|
|
75
|
+
def __html__(self):
|
|
76
|
+
return self._repr_html_()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _render_aromatic_bond(n_x, n_y, n_z, m_x, m_y, m_z, c_x, c_y, c_z):
|
|
80
|
+
aromatic_space = _render_config['aromatic_space']
|
|
81
|
+
|
|
82
|
+
# n aligned xyz
|
|
83
|
+
nc_x, nc_y, nc_z = c_x - n_x, c_y - n_y, c_z - n_z
|
|
84
|
+
mc_x, mc_y, mc_z = c_x - m_x, c_y - m_y, c_z - m_z
|
|
85
|
+
|
|
86
|
+
nc_ln = sqrt(nc_x ** 2 + nc_y ** 2 + nc_z ** 2)
|
|
87
|
+
mc_ln = sqrt(mc_x ** 2 + mc_y ** 2 + mc_z ** 2)
|
|
88
|
+
sin1 = get_angle(m_x - n_x, m_y - n_y, m_z - n_z, nc_x, nc_y, nc_z)
|
|
89
|
+
sin2 = get_angle(n_x - m_x, n_y - m_y, n_z - m_z, mc_x, mc_y, mc_z)
|
|
90
|
+
|
|
91
|
+
if sin1 < .0001 or sin2 < .0001 or nc_ln < .0001 or mc_ln < .0001:
|
|
92
|
+
return
|
|
93
|
+
else:
|
|
94
|
+
coef1 = aromatic_space / (nc_ln * sin1)
|
|
95
|
+
coef2 = aromatic_space / (mc_ln * sin2)
|
|
96
|
+
return nc_x * coef1, nc_y * coef1, nc_z * coef1, mc_x * coef2, mc_y * coef2, mc_z * coef2
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _render_dashes(nx, ny, nz, nmx, nmy, nmz, nm_ln, r_angle=None):
|
|
100
|
+
bond_radius = _render_config['bond_radius']
|
|
101
|
+
bond_color = _render_config['bond_color']
|
|
102
|
+
|
|
103
|
+
if r_angle is None:
|
|
104
|
+
dash1, dash2 = _render_config['aromatic_dashes']
|
|
105
|
+
r_angle = acos(nmy / nm_ln)
|
|
106
|
+
else:
|
|
107
|
+
dash1, dash2 = _render_config['dashes']
|
|
108
|
+
|
|
109
|
+
xml = []
|
|
110
|
+
dashes_sum = dash1 + dash2
|
|
111
|
+
if dashes_sum < .0001:
|
|
112
|
+
raise ValueError('Dashes should be nonzero')
|
|
113
|
+
|
|
114
|
+
d = dashes_sum / nm_ln
|
|
115
|
+
dx, dy, dz = nmx * d, nmy * d, nmz * d
|
|
116
|
+
b = int((nm_ln - dash1) // dashes_sum)
|
|
117
|
+
t = (nm_ln - (b * dashes_sum)) / nm_ln
|
|
118
|
+
nx, ny, nz = nx + nmx * t / 2, ny + nmy * t / 2, nz + nmz * t / 2
|
|
119
|
+
for _ in range(b):
|
|
120
|
+
xml.append(f" <transform translation='{nx:.2f} {ny:.2f} {nz:.2f}' rotation='{nmz:.2f} 0 "
|
|
121
|
+
f"{-nmx:.2f} {r_angle:.2f}'>\n <shape>\n <appearance>\n"
|
|
122
|
+
f" <material diffusecolor='{bond_color}'>\n </material>\n"
|
|
123
|
+
f" </appearance>\n <cylinder radius='{bond_radius}' height='{dash1:.2f}'>\n"
|
|
124
|
+
" </cylinder>\n </shape>\n </transform>\n")
|
|
125
|
+
nx += dx
|
|
126
|
+
ny += dy
|
|
127
|
+
nz += dz
|
|
128
|
+
xml.append(f" <transform translation='{nx:.2f} {ny:.2f} {nz:.2f}' rotation='{nmz:.2f} 0 "
|
|
129
|
+
f"{-nmx:.2f} {r_angle:.2f}'>\n <shape>\n <appearance>\n"
|
|
130
|
+
f" <material diffusecolor='{bond_color}'>\n </material>\n"
|
|
131
|
+
f" </appearance>\n <cylinder radius='{bond_radius}' height='{dash1:.2f}'>\n"
|
|
132
|
+
" </cylinder>\n </shape>\n </transform>\n")
|
|
133
|
+
return xml
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class X3domMolecule:
|
|
137
|
+
__slots__ = ()
|
|
138
|
+
|
|
139
|
+
def depict3d(self: Union['MoleculeContainer', 'X3domMolecule'], index: int = 0) -> str:
|
|
140
|
+
"""Get X3DOM XML string.
|
|
141
|
+
|
|
142
|
+
:param index: index of conformer
|
|
143
|
+
"""
|
|
144
|
+
if not hasattr(self, '_conformers'):
|
|
145
|
+
raise ValueError('No conformers stored within structure')
|
|
146
|
+
try:
|
|
147
|
+
xyz = self._conformers[index]
|
|
148
|
+
except IndexError:
|
|
149
|
+
raise IndexError('Invalid conformer index')
|
|
150
|
+
|
|
151
|
+
mx = sum(x for x, _, _ in xyz.values()) / len(xyz)
|
|
152
|
+
my = sum(y for _, y, _ in xyz.values()) / len(xyz)
|
|
153
|
+
mz = sum(z for _, _, z in xyz.values()) / len(xyz)
|
|
154
|
+
xyz = {n: (x - mx, y - my, z - mz) for n, (x, y, z) in xyz.items()}
|
|
155
|
+
atoms = self.__render_atoms(xyz)
|
|
156
|
+
bonds = self.__render_bonds(xyz)
|
|
157
|
+
return f'<x3d width=100% height=100%>\n <scene>\n{atoms}{bonds} </scene>\n</x3d>'
|
|
158
|
+
|
|
159
|
+
def view3d(self, index: int = 0, width='600px', height='400px'):
|
|
160
|
+
"""
|
|
161
|
+
Jupyter widget for 3D visualization.
|
|
162
|
+
|
|
163
|
+
:param index: index of conformer
|
|
164
|
+
:param width: widget width
|
|
165
|
+
:param height: widget height
|
|
166
|
+
"""
|
|
167
|
+
return JupyterWidget(self.depict3d(index), width, height)
|
|
168
|
+
|
|
169
|
+
def __render_atoms(self: 'MoleculeContainer', xyz):
|
|
170
|
+
font = _render_config['font_size']
|
|
171
|
+
carbon = _render_config['carbon']
|
|
172
|
+
radius = _render_config['atom_radius']
|
|
173
|
+
colors = _render_config['atoms_colors']
|
|
174
|
+
mapping_color = _render_config['mapping_color']
|
|
175
|
+
|
|
176
|
+
if radius < 0:
|
|
177
|
+
multiplier = -radius
|
|
178
|
+
radius = 0
|
|
179
|
+
elif not radius:
|
|
180
|
+
multiplier = .2
|
|
181
|
+
|
|
182
|
+
atoms = []
|
|
183
|
+
if carbon:
|
|
184
|
+
for n, a in self.atoms():
|
|
185
|
+
r = radius or a.atomic_radius * multiplier
|
|
186
|
+
fr = r * 0.71
|
|
187
|
+
atoms.append(f" <transform translation='{' '.join(format(x, '.2f') for x in xyz[n])}'>\n"
|
|
188
|
+
" <billboard axisOfRotation='0 0 0'>\n <group>\n"
|
|
189
|
+
f" <transform translation='{fr:.2f} {fr:.2f} 0'>\n"
|
|
190
|
+
' <shape>\n <appearance>\n'
|
|
191
|
+
f" <material diffuseColor='{mapping_color}'/>\n"
|
|
192
|
+
f" </appearance>\n <text string='{a.atomic_symbol}'>\n"
|
|
193
|
+
f" <fontstyle family='sans' size='{font:.2f}' justify='first'/>\n"
|
|
194
|
+
" </text>\n </shape>\n </transform>\n"
|
|
195
|
+
" <shape>\n <appearance>\n"
|
|
196
|
+
f" <material diffuseColor='{colors[a.atomic_number - 1]}'/>\n"
|
|
197
|
+
f" </appearance>\n <sphere radius='{r:.2f}'/>\n"
|
|
198
|
+
" </shape>\n </group>\n </billboard>\n </transform>\n")
|
|
199
|
+
else:
|
|
200
|
+
for n, a in self.atoms():
|
|
201
|
+
r = radius or a.atomic_radius * multiplier
|
|
202
|
+
atoms.append(f" <transform translation='{' '.join(format(x, '.2f') for x in xyz[n])}'>\n"
|
|
203
|
+
" <shape>\n <appearance>\n"
|
|
204
|
+
f" <material diffuseColor='{colors[a.atomic_number - 1]}'/>\n"
|
|
205
|
+
f" </appearance>\n <sphere radius='{r:.2f}'/>\n"
|
|
206
|
+
" </shape>\n </transform>\n")
|
|
207
|
+
return ''.join(atoms)
|
|
208
|
+
|
|
209
|
+
def __render_bonds(self: 'MoleculeContainer', xyz):
|
|
210
|
+
bonds = self._bonds
|
|
211
|
+
|
|
212
|
+
bond_color = _render_config['bond_color']
|
|
213
|
+
bond_radius = _render_config['bond_radius']
|
|
214
|
+
double_space = _render_config['double_space']
|
|
215
|
+
triple_space = _render_config['triple_space']
|
|
216
|
+
r1 = triple_space * sqrt(3) / 3
|
|
217
|
+
r2 = triple_space * sqrt(3) / 6
|
|
218
|
+
|
|
219
|
+
xml = []
|
|
220
|
+
lengths = {}
|
|
221
|
+
doubles = {}
|
|
222
|
+
half_triple = triple_space / 2
|
|
223
|
+
for n, m, bond in self.bonds():
|
|
224
|
+
nx, ny, nz = xyz[n]
|
|
225
|
+
mx, my, mz = xyz[m]
|
|
226
|
+
|
|
227
|
+
nmx, nmy, nmz = mx - nx, my - ny, mz - nz
|
|
228
|
+
length = sqrt(nmx ** 2 + nmy ** 2 + nmz ** 2)
|
|
229
|
+
if length < .001:
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
rotation_angle = acos(nmy / length)
|
|
233
|
+
lengths[(n, m)] = lengths[(m, n)] = (length, rotation_angle)
|
|
234
|
+
x, y, z = nx + nmx / 2, ny + nmy / 2, nz + nmz / 2
|
|
235
|
+
if bond in (1, 4):
|
|
236
|
+
xml.append(f" <transform translation='{x:.2f} {y:.2f} {z:.2f}' rotation='{nmz:.2f} 0 "
|
|
237
|
+
f"{-nmx:.2f} {rotation_angle:.2f}'>\n <shape>\n <appearance>\n"
|
|
238
|
+
f" <material diffusecolor='{bond_color}'>\n </material>\n"
|
|
239
|
+
f" </appearance>\n <cylinder radius='{bond_radius}' height='{length:.2f}'>\n"
|
|
240
|
+
" </cylinder>\n </shape>\n </transform>\n")
|
|
241
|
+
elif bond == 2:
|
|
242
|
+
if n in doubles:
|
|
243
|
+
# normal for plane n m o
|
|
244
|
+
norm_x, norm_y, norm_z = plane_normal(nmx, nmy, nmz, *doubles[n])
|
|
245
|
+
elif m in doubles:
|
|
246
|
+
# normal for plane n m o
|
|
247
|
+
norm_x, norm_y, norm_z = plane_normal(nmx, nmy, nmz, *doubles[m])
|
|
248
|
+
else:
|
|
249
|
+
third = next((x for x in bonds[n] if x != m), None)
|
|
250
|
+
if third:
|
|
251
|
+
ox, oy, oz = xyz[third]
|
|
252
|
+
nox, noy, noz = ox - nx, oy - ny, oz - nz
|
|
253
|
+
else:
|
|
254
|
+
third = next((x for x in bonds[m] if x != n), None)
|
|
255
|
+
if third:
|
|
256
|
+
ox, oy, oz = xyz[third]
|
|
257
|
+
nox, noy, noz = ox - nx, oy - ny, oz - nz
|
|
258
|
+
else:
|
|
259
|
+
nox, noy, noz = vector_normal(nmx, nmy, nmz)
|
|
260
|
+
|
|
261
|
+
# normal for plane n m o
|
|
262
|
+
normx, normy, normz = unit_vector(*plane_normal(nmx, nmy, nmz, nox, noy, noz))
|
|
263
|
+
|
|
264
|
+
# normal for plane n m normal
|
|
265
|
+
norm_x, norm_y, norm_z = plane_normal(nmx, nmy, nmz, normx, normy, normz)
|
|
266
|
+
|
|
267
|
+
doubles[n] = doubles[m] = (norm_x, norm_y, norm_z)
|
|
268
|
+
norm_dist = sqrt(norm_x ** 2 + norm_y ** 2 + norm_z ** 2)
|
|
269
|
+
|
|
270
|
+
if norm_dist < .0001:
|
|
271
|
+
coef = double_space * 10000
|
|
272
|
+
else:
|
|
273
|
+
coef = double_space / norm_dist
|
|
274
|
+
|
|
275
|
+
dx, dy, dz = norm_x * coef, norm_y * coef, norm_z * coef
|
|
276
|
+
xml.append(
|
|
277
|
+
f" <transform translation='{x + dx:.2f} {y + dy:.2f} {z + dz:.2f}' rotation='{nmz:.2f} 0 "
|
|
278
|
+
f"{-nmx:.2f} {rotation_angle:.2f}'>\n <shape>\n <appearance>\n"
|
|
279
|
+
f" <material diffusecolor='{bond_color}'>\n </material>\n"
|
|
280
|
+
f" </appearance>\n <cylinder radius='{bond_radius}' height='{length:.2f}'>\n"
|
|
281
|
+
" </cylinder>\n </shape>\n </transform>\n")
|
|
282
|
+
xml.append(
|
|
283
|
+
f" <transform translation='{x - dx:.2f} {y - dy:.2f} {z - dz:.2f}' rotation='{nmz:.2f} 0 "
|
|
284
|
+
f"{-nmx:.2f} {rotation_angle:.2f}'>\n <shape>\n <appearance>\n"
|
|
285
|
+
f" <material diffusecolor='{bond_color}'>\n </material>\n"
|
|
286
|
+
f" </appearance>\n <cylinder radius='{bond_radius}' height='{length:.2f}'>\n"
|
|
287
|
+
" </cylinder>\n </shape>\n </transform>\n")
|
|
288
|
+
elif bond == 3:
|
|
289
|
+
nox, noy, noz = vector_normal(nmx, nmy, nmz)
|
|
290
|
+
|
|
291
|
+
# normal for plane n m o
|
|
292
|
+
normx, normy, normz = unit_vector(*plane_normal(nmx, nmy, nmz, nox, noy, noz))
|
|
293
|
+
vecrx, vecry, vecrz = normx * r1, normy * r1, normz * r1
|
|
294
|
+
|
|
295
|
+
# normal for plane n m normal
|
|
296
|
+
norm_x, norm_y, norm_z = unit_vector(*plane_normal(nmx, nmy, nmz, normx, normy, normz))
|
|
297
|
+
vecx, vecy, vecz = norm_x * half_triple, norm_y * half_triple, norm_z * half_triple
|
|
298
|
+
|
|
299
|
+
xml.append(f" <transform translation='{x + vecrx:.2f} {y + vecry:.2f} {z + vecrz:.2f}'"
|
|
300
|
+
f" rotation='{nmz:.2f} 0 {-nmx:.2f} {rotation_angle:.2f}'>\n <shape>\n"
|
|
301
|
+
f" <appearance>\n <material diffusecolor='{bond_color}'>\n"
|
|
302
|
+
f" </material>\n </appearance>\n <cylinder radius='{bond_radius}'"
|
|
303
|
+
f" height='{length:.2f}'>\n </cylinder>\n </shape>\n </transform>\n")
|
|
304
|
+
|
|
305
|
+
xx, yy, zz = x - normx * r2, y - normy * r2, z - normz * r2
|
|
306
|
+
xml.append(f" <transform translation='{xx - vecx:.2f} {yy - vecy:.2f} {zz - vecz:.2f}'"
|
|
307
|
+
f" rotation='{nmz:.2f} 0 {-nmx:.2f} {rotation_angle:.2f}'>\n <shape>\n"
|
|
308
|
+
f" <appearance>\n <material diffusecolor='{bond_color}'>\n"
|
|
309
|
+
f" </material>\n </appearance>\n <cylinder radius='{bond_radius}'"
|
|
310
|
+
f" height='{length:.2f}'>\n </cylinder>\n </shape>\n </transform>\n")
|
|
311
|
+
xml.append(f" <transform translation='{xx + vecx:.2f} {yy + vecy:.2f} {zz + vecz:.2f}'"
|
|
312
|
+
f" rotation='{nmz:.2f} 0 {-nmx:.2f} {rotation_angle:.2f}'>\n <shape>\n"
|
|
313
|
+
f" <appearance>\n <material diffusecolor='{bond_color}'>\n"
|
|
314
|
+
f" </material>\n </appearance>\n <cylinder radius='{bond_radius}'"
|
|
315
|
+
f" height='{length:.2f}'>\n </cylinder>\n </shape>\n </transform>\n")
|
|
316
|
+
else:
|
|
317
|
+
xml.extend(_render_dashes(nx, ny, nz, nmx, nmy, nmz, length, r_angle=rotation_angle))
|
|
318
|
+
|
|
319
|
+
for ring in self.aromatic_rings:
|
|
320
|
+
cx = sum(xyz[n][0] for n in ring) / len(ring)
|
|
321
|
+
cy = sum(xyz[n][1] for n in ring) / len(ring)
|
|
322
|
+
cz = sum(xyz[n][2] for n in ring) / len(ring)
|
|
323
|
+
|
|
324
|
+
for n, m in zip(ring, ring[1:]):
|
|
325
|
+
nx, ny, nz = xyz[n]
|
|
326
|
+
mx, my, mz = xyz[m]
|
|
327
|
+
|
|
328
|
+
aromatic = _render_aromatic_bond(nx, ny, nz, mx, my, mz, cx, cy, cz)
|
|
329
|
+
if aromatic:
|
|
330
|
+
veca_x, veca_y, veca_z, vecb_x, vecb_y, vecb_z = aromatic
|
|
331
|
+
ax, ay, az = nx + veca_x, ny + veca_y, nz + veca_z
|
|
332
|
+
abx, aby, abz = mx + vecb_x - ax, my + vecb_y - ay, mz + vecb_z - az
|
|
333
|
+
ab_ln = sqrt(abx ** 2 + aby ** 2 + abz ** 2)
|
|
334
|
+
if ab_ln < .0001:
|
|
335
|
+
continue
|
|
336
|
+
else:
|
|
337
|
+
xml.extend(_render_dashes(ax, ay, az, abx, aby, abz, ab_ln))
|
|
338
|
+
|
|
339
|
+
i, j = ring[-1], ring[0]
|
|
340
|
+
nx, ny, nz = xyz[i]
|
|
341
|
+
mx, my, mz = xyz[j]
|
|
342
|
+
aromatic = _render_aromatic_bond(nx, ny, nz, mx, my, mz, cx, cy, cz)
|
|
343
|
+
if aromatic:
|
|
344
|
+
veca_x, veca_y, veca_z, vecb_x, vecb_y, vecb_z = aromatic
|
|
345
|
+
ax, ay, az = nx + veca_x, ny + veca_y, nz + veca_z
|
|
346
|
+
abx, aby, abz = mx + vecb_x - ax, my + vecb_y - ay, mz + vecb_z - az
|
|
347
|
+
ab_ln = sqrt(abx ** 2 + aby ** 2 + abz ** 2)
|
|
348
|
+
if ab_ln < .0001:
|
|
349
|
+
continue
|
|
350
|
+
else:
|
|
351
|
+
xml.extend(_render_dashes(ax, ay, az, abx, aby, abz, ab_ln))
|
|
352
|
+
return ''.join(xml)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
__all__ = ['X3domMolecule']
|
|
@@ -0,0 +1,43 @@
|
|
|
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 typing import Union
|
|
20
|
+
from zlib import decompress
|
|
21
|
+
from .bonds import *
|
|
22
|
+
from .cgr import *
|
|
23
|
+
from .molecule import *
|
|
24
|
+
from .query import *
|
|
25
|
+
from .reaction import *
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def unpach(data: bytes, /, *, compressed=True) -> Union[MoleculeContainer, ReactionContainer]:
|
|
29
|
+
if compressed:
|
|
30
|
+
data = decompress(data)
|
|
31
|
+
try:
|
|
32
|
+
return MoleculeContainer.unpack(data, compressed=False)
|
|
33
|
+
except ValueError:
|
|
34
|
+
pass
|
|
35
|
+
# second try
|
|
36
|
+
return ReactionContainer.unpack(data, compressed=False)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
unpack = unpach
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = [x for x in locals() if x.endswith('Container')]
|
|
43
|
+
__all__.extend(['Bond', 'QueryBond', 'unpack', 'unpach'])
|
|
Binary file
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2022-2025 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
|
+
cimport cython
|
|
20
|
+
from cpython.mem cimport PyMem_Malloc, PyMem_Free
|
|
21
|
+
from libc.math cimport ldexp, frexp
|
|
22
|
+
from libc.string cimport memset
|
|
23
|
+
|
|
24
|
+
# Format V2 specification::
|
|
25
|
+
#
|
|
26
|
+
# Big endian bytes order
|
|
27
|
+
# 8 bit - 0x02 (current format specification)
|
|
28
|
+
# 12 bit - number of atoms
|
|
29
|
+
# 12 bit - cis/trans stereo block size
|
|
30
|
+
#
|
|
31
|
+
# Atom block 9 bytes (repeated):
|
|
32
|
+
# 12 bit - atom number
|
|
33
|
+
# 4 bit - number of neighbors
|
|
34
|
+
# 2 bit tetrahedron sign (00 - not stereo, 10 or 11 - has stereo)
|
|
35
|
+
# 2 bit - allene sign
|
|
36
|
+
# 5 bit - isotope (00000 - not specified, over = isotope - common_isotope + 16)
|
|
37
|
+
# 7 bit - atomic number (<=118)
|
|
38
|
+
# 32 bit - XY float16 coordinates
|
|
39
|
+
# 3 bit - hydrogens (0-7). Note: 7 == None
|
|
40
|
+
# 4 bit - charge (charge + 4. possible range -4 - 4)
|
|
41
|
+
# 1 bit - radical state
|
|
42
|
+
# Connection table: flatten list of neighbors. neighbors count stored in atom block.
|
|
43
|
+
# For example CC(=O)O - {1: [2], 2: [1, 3, 4], 3: [2], 4: [2]} >> [2, 1, 3, 4, 2, 2].
|
|
44
|
+
# Repeated block (equal to bonds count).
|
|
45
|
+
# 24 bit - paired 12 bit numbers.
|
|
46
|
+
# Bonds order block 3 bit per bond zero-padded to full byte at the end.
|
|
47
|
+
# Cis/trans data block (repeated):
|
|
48
|
+
# 24 bit - atoms pair
|
|
49
|
+
# 7 bit - zero padding. in future can be used for extra bond-level stereo, like atropoisomers.
|
|
50
|
+
# 1 bit - sign
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@cython.nonecheck(False)
|
|
54
|
+
@cython.boundscheck(False)
|
|
55
|
+
@cython.cdivision(True)
|
|
56
|
+
@cython.wraparound(False)
|
|
57
|
+
def pack(object molecule):
|
|
58
|
+
cdef bint b = True # binary flag
|
|
59
|
+
cdef char charge
|
|
60
|
+
cdef unsigned char atomic_number, ngb_count, isotope, bond, s = 0, buffer_b, buffer_o, stereo, hcr
|
|
61
|
+
cdef unsigned char *data
|
|
62
|
+
cdef unsigned short atoms_count, bonds_count = 0, cis_trans_count, n, m, tn, tm
|
|
63
|
+
cdef unsigned int size, atoms_shift = 4, bonds_shift, order_shift, cis_trans_shift # can be > 2^16
|
|
64
|
+
cdef unsigned char[4096] seen # atom number is 12 bit, thus, can be any value up to 4095. numbers are not continuous
|
|
65
|
+
|
|
66
|
+
cdef bytes py_pack
|
|
67
|
+
cdef dict py_ngb, py_atoms, py_bonds, py_stereo
|
|
68
|
+
cdef tuple py_tuple
|
|
69
|
+
cdef object py_atom, py_bond, py_nan_int, py_obj
|
|
70
|
+
|
|
71
|
+
# map molecule to vars
|
|
72
|
+
py_atoms = molecule._atoms
|
|
73
|
+
py_bonds = molecule._bonds
|
|
74
|
+
py_stereo = molecule._stereo_cis_trans_terminals
|
|
75
|
+
|
|
76
|
+
# calculate elements count
|
|
77
|
+
atoms_count = len(py_atoms)
|
|
78
|
+
cis_trans_count = molecule._cis_trans_count
|
|
79
|
+
|
|
80
|
+
for py_ngb in py_bonds.values():
|
|
81
|
+
bonds_count += len(py_ngb)
|
|
82
|
+
bonds_count /= 2 # graph is bidirected
|
|
83
|
+
|
|
84
|
+
# calculate pack blocks entries
|
|
85
|
+
size = bonds_count * 3 # bonds bits
|
|
86
|
+
if size % 8: # partial byte fill
|
|
87
|
+
size = size / 8 + 1
|
|
88
|
+
else:
|
|
89
|
+
size /= 8
|
|
90
|
+
bonds_shift = 4 + 9 * atoms_count # connection table starting byte
|
|
91
|
+
order_shift = bonds_shift + 3 * bonds_count # bond orders block starting byte
|
|
92
|
+
cis_trans_shift = size + order_shift # cis-trans block starting byte
|
|
93
|
+
size = cis_trans_shift + 4 * cis_trans_count # total pack size
|
|
94
|
+
|
|
95
|
+
# allocate pack in memory
|
|
96
|
+
data = <unsigned char *> PyMem_Malloc(size * sizeof(unsigned char))
|
|
97
|
+
if not data:
|
|
98
|
+
raise MemoryError()
|
|
99
|
+
|
|
100
|
+
memset(seen, 0, 4096 * sizeof(unsigned char)) # erase random data
|
|
101
|
+
|
|
102
|
+
# start pack collection
|
|
103
|
+
data[0] = 2 # header. specification version 2
|
|
104
|
+
data[1] = atoms_count >> 4 # 5-12b of atom count value
|
|
105
|
+
data[2] = atoms_count << 4 | cis_trans_count >> 8 # 1-4b of atom count value, 9-12b of cis-trans count value
|
|
106
|
+
data[3] = cis_trans_count # 1-8b of cis-trans count value
|
|
107
|
+
|
|
108
|
+
for py_obj, py_atom in py_atoms.items():
|
|
109
|
+
py_ngb = py_bonds[py_obj]
|
|
110
|
+
ngb_count = len(py_ngb)
|
|
111
|
+
n = py_obj # cast to C
|
|
112
|
+
seen[n] = 1
|
|
113
|
+
atomic_number = py_atom.atomic_number
|
|
114
|
+
|
|
115
|
+
py_nan_int = py_atom._isotope # direct access
|
|
116
|
+
if py_nan_int is None:
|
|
117
|
+
isotope = 0
|
|
118
|
+
else:
|
|
119
|
+
isotope = <short> py_nan_int - common_isotopes[atomic_number]
|
|
120
|
+
|
|
121
|
+
py_nan_int = py_atom._stereo
|
|
122
|
+
if py_nan_int is None:
|
|
123
|
+
stereo = 0
|
|
124
|
+
# V2 specification
|
|
125
|
+
# 2 bit tetrahedron | 2 bit allene | 0000
|
|
126
|
+
elif py_nan_int:
|
|
127
|
+
if ngb_count == 2: # allene
|
|
128
|
+
stereo = 0x30
|
|
129
|
+
else:
|
|
130
|
+
stereo = 0xc0
|
|
131
|
+
else:
|
|
132
|
+
if ngb_count == 2: # allene
|
|
133
|
+
stereo = 0x20
|
|
134
|
+
else:
|
|
135
|
+
stereo = 0x80
|
|
136
|
+
|
|
137
|
+
# precalculate atom attrs
|
|
138
|
+
# should be done independently, due to possible randomness in dicts order.
|
|
139
|
+
# 3 bit - hydrogens (0-7) | 4 bit - charge | 1 bit - radical
|
|
140
|
+
py_nan_int = py_atom._implicit_hydrogens
|
|
141
|
+
if py_nan_int is None:
|
|
142
|
+
hcr = 0xe0 # 0b11100000
|
|
143
|
+
else:
|
|
144
|
+
hcr = <unsigned char> py_nan_int << 5
|
|
145
|
+
|
|
146
|
+
charge = py_atom._charge
|
|
147
|
+
hcr |= (charge + 4) << 1
|
|
148
|
+
if py_atom._is_radical:
|
|
149
|
+
hcr |= 1
|
|
150
|
+
|
|
151
|
+
data[atoms_shift] = n >> 4 # 5-12b AN
|
|
152
|
+
data[atoms_shift + 1] = n << 4 | ngb_count # 1-4b AN, 4b NC
|
|
153
|
+
data[atoms_shift + 2] = stereo | isotope >> 1 # TS , AS , 4b I
|
|
154
|
+
data[atoms_shift + 3] = isotope << 7 | atomic_number # 1bI , A
|
|
155
|
+
|
|
156
|
+
# 2 float16 big endian
|
|
157
|
+
double_to_float16(py_atom.x, &data[atoms_shift + 4])
|
|
158
|
+
double_to_float16(py_atom.y, &data[atoms_shift + 6])
|
|
159
|
+
|
|
160
|
+
data[atoms_shift + 8] = hcr
|
|
161
|
+
atoms_shift += 9
|
|
162
|
+
|
|
163
|
+
# collect connection table
|
|
164
|
+
for m, py_bond in py_ngb.items():
|
|
165
|
+
if b: # 8 + 4
|
|
166
|
+
data[bonds_shift] = m >> 4
|
|
167
|
+
bonds_shift += 1
|
|
168
|
+
buffer_b = m << 4
|
|
169
|
+
b = False # switch
|
|
170
|
+
else: # 4 + 8
|
|
171
|
+
data[bonds_shift] = buffer_b | m >> 8
|
|
172
|
+
bonds_shift += 1
|
|
173
|
+
data[bonds_shift] = m
|
|
174
|
+
bonds_shift += 1 # next free 3 bytes block
|
|
175
|
+
b = True
|
|
176
|
+
|
|
177
|
+
if not seen[m]:
|
|
178
|
+
bond = <unsigned char> py_bond._order - 1
|
|
179
|
+
# 3 3 2 | 1 3 3 1 | 2 3 3
|
|
180
|
+
if s == 0:
|
|
181
|
+
buffer_o = bond << 5
|
|
182
|
+
s = 1
|
|
183
|
+
elif s == 1:
|
|
184
|
+
buffer_o |= bond << 2
|
|
185
|
+
s = 2
|
|
186
|
+
elif s == 2:
|
|
187
|
+
data[order_shift] = buffer_o | bond >> 1
|
|
188
|
+
order_shift += 1
|
|
189
|
+
buffer_o = bond << 7
|
|
190
|
+
s = 3
|
|
191
|
+
elif s == 3:
|
|
192
|
+
buffer_o |= bond << 4
|
|
193
|
+
s = 4
|
|
194
|
+
elif s == 4:
|
|
195
|
+
buffer_o |= bond << 1
|
|
196
|
+
s = 5
|
|
197
|
+
elif s == 5:
|
|
198
|
+
data[order_shift] = buffer_o | bond >> 2
|
|
199
|
+
order_shift += 1
|
|
200
|
+
buffer_o = bond << 6
|
|
201
|
+
s = 6
|
|
202
|
+
elif s == 6:
|
|
203
|
+
buffer_o |= bond << 3
|
|
204
|
+
s = 7
|
|
205
|
+
else: # 7
|
|
206
|
+
data[order_shift] = buffer_o | bond
|
|
207
|
+
order_shift += 1
|
|
208
|
+
s = 0
|
|
209
|
+
|
|
210
|
+
py_nan_int = py_bond._stereo
|
|
211
|
+
if py_nan_int is not None:
|
|
212
|
+
py_tuple = py_stereo[py_obj]
|
|
213
|
+
tn, tm = py_tuple
|
|
214
|
+
data[cis_trans_shift] = tn >> 4
|
|
215
|
+
data[cis_trans_shift + 1] = tn << 4 | tm >> 8
|
|
216
|
+
data[cis_trans_shift + 2] = tm
|
|
217
|
+
data[cis_trans_shift + 3] = py_nan_int
|
|
218
|
+
cis_trans_shift += 4
|
|
219
|
+
|
|
220
|
+
if s: # flush buffer
|
|
221
|
+
data[order_shift] = buffer_o
|
|
222
|
+
|
|
223
|
+
try:
|
|
224
|
+
py_pack = data[:size]
|
|
225
|
+
finally:
|
|
226
|
+
PyMem_Free(data)
|
|
227
|
+
return py_pack
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
cdef short[119] common_isotopes
|
|
231
|
+
common_isotopes[:] = [0, -15, -12, -9, -7, -5, -4, -2, 0, 3, 4, 7, 8, 11, 12, 15, 16, 19, 24, 23, 24, 29,
|
|
232
|
+
32, 35, 36, 39, 40, 43, 43, 48, 49, 54, 57, 59, 63, 64, 68, 69, 72, 73, 75, 77,
|
|
233
|
+
80, 82, 85, 87, 90, 92, 96, 99, 103, 106, 112, 111, 115, 117, 121, 123, 124, 125,
|
|
234
|
+
128, 129, 134, 136, 141, 143, 147, 149, 151, 153, 157, 159, 162, 165, 168, 170,
|
|
235
|
+
174, 176, 179, 181, 185, 188, 191, 193, 193, 194, 206, 207, 210, 211, 216, 215,
|
|
236
|
+
222, 221, 228, 227, 231, 231, 235, 236, 241, 242, 243, 244, 245, 254, 253, 254,
|
|
237
|
+
254, 262, 265, 265, 269, 262, 273, 273, 277, 281, 278]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
cdef void double_to_float16(double x, unsigned char* p):
|
|
241
|
+
# adopted from cpython source code
|
|
242
|
+
cdef unsigned char sign
|
|
243
|
+
cdef int e
|
|
244
|
+
cdef double f
|
|
245
|
+
cdef unsigned short bits
|
|
246
|
+
|
|
247
|
+
if x == 0.:
|
|
248
|
+
p[0] = p[1] = 0
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
sign = x < 0.
|
|
252
|
+
if sign:
|
|
253
|
+
x = -x
|
|
254
|
+
f = frexp(x, &e)
|
|
255
|
+
e -= 1
|
|
256
|
+
if f < .5 or f >= 1. or e >= 16 or e < -25:
|
|
257
|
+
p[0] = p[1] = 0
|
|
258
|
+
return # ignore big values
|
|
259
|
+
|
|
260
|
+
f *= 2.0
|
|
261
|
+
if e < -14:
|
|
262
|
+
f = ldexp(f, 14 + e)
|
|
263
|
+
e = 0
|
|
264
|
+
else:
|
|
265
|
+
e += 15
|
|
266
|
+
f -= 1.
|
|
267
|
+
|
|
268
|
+
f *= 1024.
|
|
269
|
+
bits = <unsigned short> f | (e << 10) | (sign << 15)
|
|
270
|
+
p[0] = bits >> 8
|
|
271
|
+
p[1] = bits
|
|
Binary file
|