autogeom 0.0.1__py3-none-any.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.
- autogeom/__init__.py +114 -0
- autogeom/_convert.py +207 -0
- autogeom/_geometry.py +440 -0
- autogeom/_measure.py +461 -0
- autogeom/_trajectory.py +144 -0
- autogeom/_transform.py +312 -0
- autogeom/_view.py +220 -0
- autogeom/element.py +254 -0
- autogeom/elements-data.json +1418 -0
- autogeom/exc.py +13 -0
- autogeom/rd.py +623 -0
- autogeom/types.py +50 -0
- autogeom/units.py +6 -0
- autogeom-0.0.1.dist-info/METADATA +110 -0
- autogeom-0.0.1.dist-info/RECORD +17 -0
- autogeom-0.0.1.dist-info/WHEEL +4 -0
- autogeom-0.0.1.dist-info/licenses/LICENSE +21 -0
autogeom/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Molecular geometries: the `Geometry` model and the functions that operate on it."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.0.1"
|
|
4
|
+
|
|
5
|
+
from . import element, rd, types
|
|
6
|
+
from ._convert import (
|
|
7
|
+
formula,
|
|
8
|
+
from_inchi,
|
|
9
|
+
from_rdkit_mol,
|
|
10
|
+
from_smiles,
|
|
11
|
+
inchi,
|
|
12
|
+
rdkit_mol,
|
|
13
|
+
smiles,
|
|
14
|
+
)
|
|
15
|
+
from ._geometry import (
|
|
16
|
+
Geometry,
|
|
17
|
+
from_xyz_block,
|
|
18
|
+
from_xyz_file,
|
|
19
|
+
join,
|
|
20
|
+
remove_atoms,
|
|
21
|
+
select_atoms,
|
|
22
|
+
xyz_block,
|
|
23
|
+
xyz_file,
|
|
24
|
+
)
|
|
25
|
+
from ._measure import (
|
|
26
|
+
adjacency_matrix,
|
|
27
|
+
angle,
|
|
28
|
+
angles,
|
|
29
|
+
bonds,
|
|
30
|
+
center_of_mass,
|
|
31
|
+
dihedral,
|
|
32
|
+
dihedrals,
|
|
33
|
+
distance,
|
|
34
|
+
distance_keys,
|
|
35
|
+
distance_matrix,
|
|
36
|
+
inertia_tensor,
|
|
37
|
+
inertial_frame_rotation,
|
|
38
|
+
principal_axes,
|
|
39
|
+
)
|
|
40
|
+
from ._trajectory import (
|
|
41
|
+
from_xyz_trajectory_block,
|
|
42
|
+
from_xyz_trajectory_file,
|
|
43
|
+
xyz_trajectory_block,
|
|
44
|
+
xyz_trajectory_comments,
|
|
45
|
+
xyz_trajectory_file,
|
|
46
|
+
)
|
|
47
|
+
from ._transform import (
|
|
48
|
+
alignment_rotation,
|
|
49
|
+
axis_alignment_rotation,
|
|
50
|
+
center,
|
|
51
|
+
reflect,
|
|
52
|
+
rotate,
|
|
53
|
+
translate,
|
|
54
|
+
twist,
|
|
55
|
+
twist_angle,
|
|
56
|
+
)
|
|
57
|
+
from ._view import View, render_gif, render_svg, view
|
|
58
|
+
from .exc import ElementNotFoundError, GeometryConversionError, XYZFormatError
|
|
59
|
+
from .units import ureg
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"ElementNotFoundError",
|
|
63
|
+
"Geometry",
|
|
64
|
+
"GeometryConversionError",
|
|
65
|
+
"View",
|
|
66
|
+
"XYZFormatError",
|
|
67
|
+
"adjacency_matrix",
|
|
68
|
+
"alignment_rotation",
|
|
69
|
+
"angle",
|
|
70
|
+
"angles",
|
|
71
|
+
"axis_alignment_rotation",
|
|
72
|
+
"bonds",
|
|
73
|
+
"center",
|
|
74
|
+
"center_of_mass",
|
|
75
|
+
"dihedral",
|
|
76
|
+
"dihedrals",
|
|
77
|
+
"distance",
|
|
78
|
+
"distance_keys",
|
|
79
|
+
"distance_matrix",
|
|
80
|
+
"element",
|
|
81
|
+
"formula",
|
|
82
|
+
"from_inchi",
|
|
83
|
+
"from_rdkit_mol",
|
|
84
|
+
"from_smiles",
|
|
85
|
+
"from_xyz_block",
|
|
86
|
+
"from_xyz_file",
|
|
87
|
+
"from_xyz_trajectory_block",
|
|
88
|
+
"from_xyz_trajectory_file",
|
|
89
|
+
"inchi",
|
|
90
|
+
"inertia_tensor",
|
|
91
|
+
"inertial_frame_rotation",
|
|
92
|
+
"join",
|
|
93
|
+
"principal_axes",
|
|
94
|
+
"rd",
|
|
95
|
+
"rdkit_mol",
|
|
96
|
+
"reflect",
|
|
97
|
+
"remove_atoms",
|
|
98
|
+
"render_gif",
|
|
99
|
+
"render_svg",
|
|
100
|
+
"rotate",
|
|
101
|
+
"select_atoms",
|
|
102
|
+
"smiles",
|
|
103
|
+
"translate",
|
|
104
|
+
"twist",
|
|
105
|
+
"twist_angle",
|
|
106
|
+
"types",
|
|
107
|
+
"ureg",
|
|
108
|
+
"view",
|
|
109
|
+
"xyz_block",
|
|
110
|
+
"xyz_file",
|
|
111
|
+
"xyz_trajectory_block",
|
|
112
|
+
"xyz_trajectory_comments",
|
|
113
|
+
"xyz_trajectory_file",
|
|
114
|
+
]
|
autogeom/_convert.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Conversions between geometries and RDKit molecules, InChI, SMILES, and formulas."""
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from rdkit import Chem
|
|
7
|
+
from rdkit.Chem import Mol
|
|
8
|
+
|
|
9
|
+
from . import rd
|
|
10
|
+
from ._geometry import Geometry
|
|
11
|
+
from ._measure import adjacency_matrix
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"formula",
|
|
15
|
+
"from_inchi",
|
|
16
|
+
"from_rdkit_mol",
|
|
17
|
+
"from_smiles",
|
|
18
|
+
"inchi",
|
|
19
|
+
"rdkit_mol",
|
|
20
|
+
"smiles",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def rdkit_mol(geo: Geometry) -> Mol:
|
|
25
|
+
"""Convert a geometry to an RDKit molecule.
|
|
26
|
+
|
|
27
|
+
Connectivity comes from `adjacency_matrix` (with valence caps). Bond orders,
|
|
28
|
+
formal charges, and radicals are perceived to match the geometry's charge
|
|
29
|
+
and spin (see `autogeom.rd.from_connectivity`), and stereochemistry is
|
|
30
|
+
assigned from the 3D coordinates.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
geo: Geometry.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
RDKit molecule with perceived bonding and stereochemistry.
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
NotImplementedError: If the geometry contains a metal.
|
|
40
|
+
GeometryConversionError: If no Lewis structure is consistent with the
|
|
41
|
+
connectivity, charge, and spin.
|
|
42
|
+
"""
|
|
43
|
+
amat = adjacency_matrix(geo, enforce_valence=True)
|
|
44
|
+
bonds = [(int(i), int(j)) for i, j in zip(*np.nonzero(np.triu(amat)), strict=True)]
|
|
45
|
+
mol = rd.from_connectivity(
|
|
46
|
+
geo.symbols,
|
|
47
|
+
bonds,
|
|
48
|
+
charge=geo.charge,
|
|
49
|
+
spin=geo.spin,
|
|
50
|
+
coords=geo.coordinates,
|
|
51
|
+
)
|
|
52
|
+
return rd.assign_stereochemistry(mol, in_place=True)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def from_rdkit_mol(mol: Mol) -> Geometry:
|
|
56
|
+
"""Convert an RDKit molecule to a geometry, embedding coordinates if missing.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
mol: RDKit molecule.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The geometry.
|
|
63
|
+
"""
|
|
64
|
+
if not rd.has_coordinates(mol):
|
|
65
|
+
mol = rd.add_coordinates(mol)
|
|
66
|
+
|
|
67
|
+
return Geometry(
|
|
68
|
+
symbols=rd.symbols(mol),
|
|
69
|
+
coordinates=rd.coordinates(mol),
|
|
70
|
+
charge=rd.charge(mol),
|
|
71
|
+
spin=rd.spin(mol),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def inchi(geo: Geometry) -> str:
|
|
76
|
+
"""Generate the standard InChI string of a geometry with RDKit.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
geo: Geometry.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
InChI string.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
NotImplementedError: If the geometry contains a metal.
|
|
86
|
+
GeometryConversionError: If no Lewis structure is consistent with the
|
|
87
|
+
connectivity, charge, and spin.
|
|
88
|
+
ValueError: If RDKit fails to generate an InChI.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
>>> import autogeom
|
|
92
|
+
>>> geo = autogeom.from_smiles("O")
|
|
93
|
+
>>> autogeom.inchi(geo)
|
|
94
|
+
'InChI=1S/H2O/h1H2'
|
|
95
|
+
|
|
96
|
+
%seealso from_inchi, smiles, formula
|
|
97
|
+
"""
|
|
98
|
+
return rd.inchi(rdkit_mol(geo))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def from_inchi(inchi: str) -> Geometry:
|
|
102
|
+
"""Embed a geometry for an InChI string with RDKit.
|
|
103
|
+
|
|
104
|
+
Coordinates are embedded with a fixed random seed, so they are reproducible.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
inchi: InChI string.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The embedded geometry.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: If the InChI string is invalid.
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
>>> import autogeom
|
|
117
|
+
>>> geo = autogeom.from_inchi("InChI=1S/H2O/h1H2")
|
|
118
|
+
>>> geo.symbols
|
|
119
|
+
['O', 'H', 'H']
|
|
120
|
+
|
|
121
|
+
%seealso inchi, from_smiles
|
|
122
|
+
"""
|
|
123
|
+
return from_rdkit_mol(rd.from_inchi(inchi, with_coords=True))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def smiles(geo: Geometry) -> str:
|
|
127
|
+
"""Generate the canonical SMILES string of a geometry with RDKit.
|
|
128
|
+
|
|
129
|
+
Stereochemistry perceived from the 3D coordinates is included, and hydrogens
|
|
130
|
+
are implicit.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
geo: Geometry.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
SMILES string.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
NotImplementedError: If the geometry contains a metal.
|
|
140
|
+
GeometryConversionError: If no Lewis structure is consistent with the
|
|
141
|
+
connectivity, charge, and spin.
|
|
142
|
+
|
|
143
|
+
Example:
|
|
144
|
+
>>> import autogeom
|
|
145
|
+
>>> geo = autogeom.from_smiles("C(O)C")
|
|
146
|
+
>>> autogeom.smiles(geo)
|
|
147
|
+
'CCO'
|
|
148
|
+
|
|
149
|
+
%seealso from_smiles, inchi
|
|
150
|
+
"""
|
|
151
|
+
return Chem.MolToSmiles(Chem.RemoveAllHs(rdkit_mol(geo)))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def from_smiles(smiles: str) -> Geometry:
|
|
155
|
+
"""Embed a geometry for a SMILES string with RDKit.
|
|
156
|
+
|
|
157
|
+
Coordinates are embedded with a fixed random seed, so they are reproducible.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
smiles: SMILES string.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
The embedded geometry.
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
ValueError: If the SMILES string is invalid.
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> import autogeom
|
|
170
|
+
>>> geo = autogeom.from_smiles("[OH]")
|
|
171
|
+
>>> geo.symbols, geo.spin
|
|
172
|
+
(['O', 'H'], 1)
|
|
173
|
+
|
|
174
|
+
%seealso smiles, from_inchi
|
|
175
|
+
"""
|
|
176
|
+
return from_rdkit_mol(rd.from_smiles(smiles, with_coords=True))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def formula(geo: Geometry) -> str:
|
|
180
|
+
"""Generate the molecular formula of a geometry in Hill order.
|
|
181
|
+
|
|
182
|
+
With carbon present, C comes first, then H, then the remaining elements
|
|
183
|
+
alphabetically. Without carbon, all elements (including H) are alphabetical.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
geo: Geometry.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Molecular formula.
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
>>> import autogeom
|
|
193
|
+
>>> autogeom.formula(autogeom.from_smiles("CO"))
|
|
194
|
+
'CH4O'
|
|
195
|
+
>>> autogeom.formula(autogeom.from_smiles("Cl"))
|
|
196
|
+
'ClH'
|
|
197
|
+
"""
|
|
198
|
+
counts = Counter(geo.symbols)
|
|
199
|
+
|
|
200
|
+
ordered = []
|
|
201
|
+
if "C" in counts:
|
|
202
|
+
ordered.append(("C", counts.pop("C")))
|
|
203
|
+
if "H" in counts:
|
|
204
|
+
ordered.append(("H", counts.pop("H")))
|
|
205
|
+
ordered.extend(sorted(counts.items()))
|
|
206
|
+
|
|
207
|
+
return "".join(s if n == 1 else f"{s}{n}" for s, n in ordered)
|