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.
Files changed (46) hide show
  1. molcrys_kit/__init__.py +26 -0
  2. molcrys_kit/analysis/__init__.py +44 -0
  3. molcrys_kit/analysis/charge.py +183 -0
  4. molcrys_kit/analysis/chemical_env.py +1133 -0
  5. molcrys_kit/analysis/disorder/__init__.py +9 -0
  6. molcrys_kit/analysis/disorder/diagnostics.py +209 -0
  7. molcrys_kit/analysis/disorder/edge_priority.py +85 -0
  8. molcrys_kit/analysis/disorder/graph.py +1630 -0
  9. molcrys_kit/analysis/disorder/info.py +3 -0
  10. molcrys_kit/analysis/disorder/process.py +76 -0
  11. molcrys_kit/analysis/disorder/solver.py +1607 -0
  12. molcrys_kit/analysis/formula_moiety.py +203 -0
  13. molcrys_kit/analysis/interactions.py +219 -0
  14. molcrys_kit/analysis/packing_shell.py +1061 -0
  15. molcrys_kit/analysis/shape.py +733 -0
  16. molcrys_kit/analysis/species.py +54 -0
  17. molcrys_kit/analysis/stoichiometry.py +156 -0
  18. molcrys_kit/constants/__init__.py +261 -0
  19. molcrys_kit/constants/atomic_masses.json +120 -0
  20. molcrys_kit/constants/atomic_radii.json +98 -0
  21. molcrys_kit/constants/config.py +234 -0
  22. molcrys_kit/io/__init__.py +11 -0
  23. molcrys_kit/io/cif.py +812 -0
  24. molcrys_kit/io/output.py +307 -0
  25. molcrys_kit/io/xyz.py +59 -0
  26. molcrys_kit/operations/__init__.py +69 -0
  27. molcrys_kit/operations/builders.py +76 -0
  28. molcrys_kit/operations/defects.py +320 -0
  29. molcrys_kit/operations/desolvation.py +114 -0
  30. molcrys_kit/operations/hydrogen_completion.py +847 -0
  31. molcrys_kit/operations/molecule_manipulation.py +603 -0
  32. molcrys_kit/operations/perturbation.py +122 -0
  33. molcrys_kit/operations/rotation.py +86 -0
  34. molcrys_kit/operations/surface.py +1235 -0
  35. molcrys_kit/structures/__init__.py +24 -0
  36. molcrys_kit/structures/atom.py +68 -0
  37. molcrys_kit/structures/crystal.py +419 -0
  38. molcrys_kit/structures/molecule.py +410 -0
  39. molcrys_kit/structures/polyhedra.py +688 -0
  40. molcrys_kit/utils/__init__.py +29 -0
  41. molcrys_kit/utils/geometry.py +928 -0
  42. molcrys_kit-0.3.0.dist-info/METADATA +286 -0
  43. molcrys_kit-0.3.0.dist-info/RECORD +46 -0
  44. molcrys_kit-0.3.0.dist-info/WHEEL +5 -0
  45. molcrys_kit-0.3.0.dist-info/licenses/LICENSE +21 -0
  46. molcrys_kit-0.3.0.dist-info/top_level.txt +1 -0
@@ -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