molcrys-kit 0.3.0__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.
- molcrys_kit/__init__.py +26 -0
- molcrys_kit/analysis/__init__.py +44 -0
- molcrys_kit/analysis/charge.py +183 -0
- molcrys_kit/analysis/chemical_env.py +1133 -0
- molcrys_kit/analysis/disorder/__init__.py +9 -0
- molcrys_kit/analysis/disorder/diagnostics.py +209 -0
- molcrys_kit/analysis/disorder/edge_priority.py +85 -0
- molcrys_kit/analysis/disorder/graph.py +1630 -0
- molcrys_kit/analysis/disorder/info.py +3 -0
- molcrys_kit/analysis/disorder/process.py +76 -0
- molcrys_kit/analysis/disorder/solver.py +1607 -0
- molcrys_kit/analysis/formula_moiety.py +203 -0
- molcrys_kit/analysis/interactions.py +219 -0
- molcrys_kit/analysis/packing_shell.py +1061 -0
- molcrys_kit/analysis/shape.py +733 -0
- molcrys_kit/analysis/species.py +54 -0
- molcrys_kit/analysis/stoichiometry.py +156 -0
- molcrys_kit/constants/__init__.py +261 -0
- molcrys_kit/constants/atomic_masses.json +120 -0
- molcrys_kit/constants/atomic_radii.json +98 -0
- molcrys_kit/constants/config.py +234 -0
- molcrys_kit/io/__init__.py +11 -0
- molcrys_kit/io/cif.py +812 -0
- molcrys_kit/io/output.py +307 -0
- molcrys_kit/io/xyz.py +59 -0
- molcrys_kit/operations/__init__.py +69 -0
- molcrys_kit/operations/builders.py +76 -0
- molcrys_kit/operations/defects.py +320 -0
- molcrys_kit/operations/desolvation.py +114 -0
- molcrys_kit/operations/hydrogen_completion.py +847 -0
- molcrys_kit/operations/molecule_manipulation.py +603 -0
- molcrys_kit/operations/perturbation.py +122 -0
- molcrys_kit/operations/rotation.py +86 -0
- molcrys_kit/operations/surface.py +1235 -0
- molcrys_kit/structures/__init__.py +24 -0
- molcrys_kit/structures/atom.py +68 -0
- molcrys_kit/structures/crystal.py +419 -0
- molcrys_kit/structures/molecule.py +410 -0
- molcrys_kit/structures/polyhedra.py +688 -0
- molcrys_kit/utils/__init__.py +29 -0
- molcrys_kit/utils/geometry.py +928 -0
- molcrys_kit-0.3.0.dist-info/METADATA +286 -0
- molcrys_kit-0.3.0.dist-info/RECORD +46 -0
- molcrys_kit-0.3.0.dist-info/WHEEL +5 -0
- molcrys_kit-0.3.0.dist-info/licenses/LICENSE +21 -0
- molcrys_kit-0.3.0.dist-info/top_level.txt +1 -0
molcrys_kit/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MolCrysKit: A Python toolkit for molecular crystal analysis and manipulation.
|
|
3
|
+
|
|
4
|
+
This toolkit provides functionality for parsing, analyzing, and manipulating
|
|
5
|
+
molecular crystal structures, with a particular focus on molecular crystals
|
|
6
|
+
where well-defined molecules occupy crystallographic sites.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.3.0"
|
|
10
|
+
|
|
11
|
+
from .structures.atom import MolAtom
|
|
12
|
+
from .structures.molecule import CrystalMolecule
|
|
13
|
+
from .structures.crystal import MolecularCrystal
|
|
14
|
+
from .io.cif import read_mol_crystal
|
|
15
|
+
|
|
16
|
+
# For backward compatibility
|
|
17
|
+
Molecule = CrystalMolecule
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"MolAtom",
|
|
21
|
+
"CrystalMolecule",
|
|
22
|
+
"MolecularCrystal",
|
|
23
|
+
"read_mol_crystal",
|
|
24
|
+
# Backward compatibility
|
|
25
|
+
"Molecule",
|
|
26
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from .interactions import *
|
|
2
|
+
from .species import *
|
|
3
|
+
from .stoichiometry import *
|
|
4
|
+
from .chemical_env import ChemicalEnvironment
|
|
5
|
+
from .formula_moiety import Fragment, heavy_signature, match_molecule_to_fragment, parse_moiety_string
|
|
6
|
+
from .charge import MolChargeResult, assign_mol_formal_charges, compute_topo_signature
|
|
7
|
+
from .packing_shell import (
|
|
8
|
+
DEFAULT_CENTROID_OFFSET_FRAC,
|
|
9
|
+
DEFAULT_MOLECULAR_SEARCH_CUTOFF,
|
|
10
|
+
DEFAULT_POLYHEDRON_SEARCH_CUTOFF,
|
|
11
|
+
angular_rmsd_vs_ideals,
|
|
12
|
+
compute_angular_signature,
|
|
13
|
+
detect_coordination_number,
|
|
14
|
+
detect_prism_vs_antiprism,
|
|
15
|
+
find_polyhedra,
|
|
16
|
+
hull_encloses_center,
|
|
17
|
+
planarity_analysis,
|
|
18
|
+
)
|
|
19
|
+
from .shape import classify_shell, cshm, topology_signature
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"ChemicalEnvironment",
|
|
24
|
+
"Fragment",
|
|
25
|
+
"MolChargeResult",
|
|
26
|
+
"DEFAULT_CENTROID_OFFSET_FRAC",
|
|
27
|
+
"DEFAULT_MOLECULAR_SEARCH_CUTOFF",
|
|
28
|
+
"DEFAULT_POLYHEDRON_SEARCH_CUTOFF",
|
|
29
|
+
"angular_rmsd_vs_ideals",
|
|
30
|
+
"assign_mol_formal_charges",
|
|
31
|
+
"classify_shell",
|
|
32
|
+
"compute_angular_signature",
|
|
33
|
+
"compute_topo_signature",
|
|
34
|
+
"cshm",
|
|
35
|
+
"heavy_signature",
|
|
36
|
+
"match_molecule_to_fragment",
|
|
37
|
+
"parse_moiety_string",
|
|
38
|
+
"detect_coordination_number",
|
|
39
|
+
"detect_prism_vs_antiprism",
|
|
40
|
+
"find_polyhedra",
|
|
41
|
+
"hull_encloses_center",
|
|
42
|
+
"planarity_analysis",
|
|
43
|
+
"topology_signature",
|
|
44
|
+
]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Molecular formal charge determination for Tasker surface analysis.
|
|
3
|
+
|
|
4
|
+
This module provides hybrid charge assignment for CrystalMolecule objects:
|
|
5
|
+
1. user-supplied mol_charge_map (formula → charge)
|
|
6
|
+
2. pymatgen BVAnalyzer auto-guess
|
|
7
|
+
3. zero fallback with warning
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
import hashlib
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from ..structures.crystal import MolecularCrystal
|
|
16
|
+
from ..structures.molecule import CrystalMolecule
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def compute_topo_signature(mol: CrystalMolecule) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Compute a topology signature for a molecule.
|
|
22
|
+
|
|
23
|
+
The signature combines the Hill-order chemical formula with a short hash
|
|
24
|
+
of the sorted node-degree sequence from the molecular connectivity graph,
|
|
25
|
+
making it sensitive to both composition and bond topology.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
mol : CrystalMolecule
|
|
30
|
+
Molecule to fingerprint.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
str
|
|
35
|
+
A string of the form "<formula>|<8-char hex hash>" that uniquely
|
|
36
|
+
identifies the molecule type within the crystal.
|
|
37
|
+
"""
|
|
38
|
+
# Check cache first
|
|
39
|
+
if getattr(mol, '_topo_signature', None) is not None:
|
|
40
|
+
return mol._topo_signature
|
|
41
|
+
|
|
42
|
+
formula = mol.get_chemical_formula(mode="hill", empirical=False)
|
|
43
|
+
graph = mol.get_graph()
|
|
44
|
+
degree_seq = sorted(d for _, d in graph.degree())
|
|
45
|
+
degree_hash = hashlib.md5(str(degree_seq).encode()).hexdigest()[:8]
|
|
46
|
+
sig = f"{formula}|{degree_hash}"
|
|
47
|
+
mol._topo_signature = sig
|
|
48
|
+
return sig
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class MolChargeResult:
|
|
53
|
+
"""
|
|
54
|
+
Formal charge assignment result for one distinct molecule topology.
|
|
55
|
+
|
|
56
|
+
Attributes
|
|
57
|
+
----------
|
|
58
|
+
topo_signature : str
|
|
59
|
+
Topology signature (formula + degree-sequence hash).
|
|
60
|
+
formula : str
|
|
61
|
+
Hill-order chemical formula.
|
|
62
|
+
formal_charge : float
|
|
63
|
+
Assigned formal charge.
|
|
64
|
+
source : str
|
|
65
|
+
Origin of the charge value: "user_map", "auto_guess", or
|
|
66
|
+
"none" (zero fallback with warning).
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
topo_signature: str
|
|
70
|
+
formula: str
|
|
71
|
+
formal_charge: float
|
|
72
|
+
source: str # "user_map" | "auto_guess" | "none"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _guess_charge_pymatgen(mol: CrystalMolecule) -> Optional[float]:
|
|
76
|
+
"""
|
|
77
|
+
Attempt to guess the formal charge of a molecule using pymatgen BVAnalyzer.
|
|
78
|
+
|
|
79
|
+
The molecule is placed in a large cubic supercell (100 Å) to eliminate
|
|
80
|
+
spurious periodic interactions, then bond-valence analysis is applied.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
mol : CrystalMolecule
|
|
85
|
+
Molecule whose formal charge is to be guessed.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
float or None
|
|
90
|
+
Summed bond-valence oxidation states, or None on any failure.
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
from pymatgen.core import Structure, Lattice
|
|
94
|
+
from pymatgen.analysis.bond_valence import BVAnalyzer
|
|
95
|
+
|
|
96
|
+
symbols = mol.get_chemical_symbols()
|
|
97
|
+
positions = mol.get_positions()
|
|
98
|
+
big_lattice = Lattice.cubic(100.0)
|
|
99
|
+
pmg_structure = Structure(
|
|
100
|
+
big_lattice,
|
|
101
|
+
species=symbols,
|
|
102
|
+
coords=positions,
|
|
103
|
+
coords_are_cartesian=True,
|
|
104
|
+
)
|
|
105
|
+
valences = BVAnalyzer().get_valences(pmg_structure)
|
|
106
|
+
return float(sum(valences))
|
|
107
|
+
except Exception:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def assign_mol_formal_charges(
|
|
112
|
+
crystal: MolecularCrystal,
|
|
113
|
+
mol_charge_map: Optional[Dict[str, int]] = None,
|
|
114
|
+
) -> Dict[str, MolChargeResult]:
|
|
115
|
+
"""
|
|
116
|
+
Assign formal charges to each distinct molecule topology in a crystal.
|
|
117
|
+
|
|
118
|
+
Uses a three-level hybrid strategy:
|
|
119
|
+
|
|
120
|
+
1. **user_map** – if mol_charge_map contains an entry for the
|
|
121
|
+
molecule formula, that value is used directly.
|
|
122
|
+
2. **auto_guess** – pymatgen :class: is used to estimate
|
|
123
|
+
bond-valence oxidation states, which are summed to give the molecular
|
|
124
|
+
formal charge.
|
|
125
|
+
3. **none** – if both methods fail, formal charge is set to 0 and a
|
|
126
|
+
:class: is emitted. Downstream Tasker analysis will
|
|
127
|
+
degrade to topology-only ordering.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
crystal : MolecularCrystal
|
|
132
|
+
Crystal whose molecule population will be typed and charged.
|
|
133
|
+
mol_charge_map : dict, optional
|
|
134
|
+
Mapping from Hill-order chemical formula (e.g. "C8H9NO2") to
|
|
135
|
+
integer formal charge.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
dict
|
|
140
|
+
Mapping topo_signature -> MolChargeResult for every distinct
|
|
141
|
+
molecule topology found in *crystal*. Multiple molecules sharing
|
|
142
|
+
the same topology contribute only one entry.
|
|
143
|
+
"""
|
|
144
|
+
if mol_charge_map is None:
|
|
145
|
+
mol_charge_map = {}
|
|
146
|
+
|
|
147
|
+
results: Dict[str, MolChargeResult] = {}
|
|
148
|
+
|
|
149
|
+
for mol in crystal.molecules:
|
|
150
|
+
sig = compute_topo_signature(mol)
|
|
151
|
+
if sig in results:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
formula = mol.get_chemical_formula(mode="hill", empirical=False)
|
|
155
|
+
|
|
156
|
+
if formula in mol_charge_map:
|
|
157
|
+
charge = float(mol_charge_map[formula])
|
|
158
|
+
source = "user_map"
|
|
159
|
+
else:
|
|
160
|
+
guessed = _guess_charge_pymatgen(mol)
|
|
161
|
+
if guessed is not None:
|
|
162
|
+
charge = guessed
|
|
163
|
+
source = "auto_guess"
|
|
164
|
+
else:
|
|
165
|
+
charge = 0.0
|
|
166
|
+
source = "none"
|
|
167
|
+
warnings.warn(
|
|
168
|
+
f"Could not determine formal charge for molecule ''{formula}''"
|
|
169
|
+
f" (topo_signature='{sig}'). Defaulting to 0. "
|
|
170
|
+
"Tasker analysis will degrade to topology-only ordering. "
|
|
171
|
+
"Provide a mol_charge_map to suppress this warning.",
|
|
172
|
+
UserWarning,
|
|
173
|
+
stacklevel=2,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
results[sig] = MolChargeResult(
|
|
177
|
+
topo_signature=sig,
|
|
178
|
+
formula=formula,
|
|
179
|
+
formal_charge=charge,
|
|
180
|
+
source=source,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return results
|