quantui 0.5.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.
Files changed (62) hide show
  1. quantui/__init__.py +311 -0
  2. quantui/analytics.py +609 -0
  3. quantui/app.py +5650 -0
  4. quantui/app_analysis.py +662 -0
  5. quantui/app_builders.py +2465 -0
  6. quantui/app_exports.py +194 -0
  7. quantui/app_formatters.py +493 -0
  8. quantui/app_history.py +624 -0
  9. quantui/app_runflow.py +1544 -0
  10. quantui/app_visualization.py +2620 -0
  11. quantui/ase_bridge.py +236 -0
  12. quantui/benchmarks.py +1543 -0
  13. quantui/c_stderr.py +124 -0
  14. quantui/cactus.py +88 -0
  15. quantui/calc_log.py +1116 -0
  16. quantui/calculator.py +204 -0
  17. quantui/cancellation.py +88 -0
  18. quantui/cli.py +288 -0
  19. quantui/comparison.py +306 -0
  20. quantui/config.py +725 -0
  21. quantui/data/js/3Dmol-min.js +2 -0
  22. quantui/data/js/3Dmol-min.js.LICENSE.txt +5 -0
  23. quantui/data/library/library.sqlite +0 -0
  24. quantui/data/manifests/bulk_qm9.json +1 -0
  25. quantui/data/manifests/curated.json +15482 -0
  26. quantui/data/manifests/presets.json +816 -0
  27. quantui/descriptor_cards.py +186 -0
  28. quantui/freq_calc.py +712 -0
  29. quantui/freq_ir_workers.py +229 -0
  30. quantui/gpu_offload.py +278 -0
  31. quantui/help_content.py +474 -0
  32. quantui/ir_plot.py +130 -0
  33. quantui/issue_tracker.py +170 -0
  34. quantui/live_log.py +387 -0
  35. quantui/log_utils.py +492 -0
  36. quantui/molecule.py +577 -0
  37. quantui/molecule_library.py +433 -0
  38. quantui/nmr_calc.py +437 -0
  39. quantui/optimizer.py +670 -0
  40. quantui/orbital_visualization.py +1102 -0
  41. quantui/pes_scan.py +420 -0
  42. quantui/preopt.py +355 -0
  43. quantui/progress.py +111 -0
  44. quantui/pubchem.py +1157 -0
  45. quantui/reorganization_energy.py +435 -0
  46. quantui/results_storage.py +902 -0
  47. quantui/security.py +14 -0
  48. quantui/session_calc.py +622 -0
  49. quantui/structure_providers.py +277 -0
  50. quantui/tddft_calc.py +307 -0
  51. quantui/user_settings.py +238 -0
  52. quantui/utils.py +287 -0
  53. quantui/vib_cache.py +247 -0
  54. quantui/visualization_py3dmol.py +593 -0
  55. quantui/viz_assets.py +101 -0
  56. quantui/viz_backend_router.py +243 -0
  57. quantui-0.5.1.dist-info/METADATA +533 -0
  58. quantui-0.5.1.dist-info/RECORD +62 -0
  59. quantui-0.5.1.dist-info/WHEEL +5 -0
  60. quantui-0.5.1.dist-info/entry_points.txt +2 -0
  61. quantui-0.5.1.dist-info/licenses/LICENSE +21 -0
  62. quantui-0.5.1.dist-info/top_level.txt +1 -0
quantui/ase_bridge.py ADDED
@@ -0,0 +1,236 @@
1
+ """
2
+ ASE (Atomic Simulation Environment) integration bridge for QuantUI.
3
+
4
+ Provides converters between QuantUI Molecule and ASE Atoms objects,
5
+ a file-format-agnostic structure reader built on ase.io, and a
6
+ curated molecule library for educational use.
7
+
8
+ All ASE imports are guarded by ASE_AVAILABLE so this module can be
9
+ imported safely even when ASE is not installed.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import TYPE_CHECKING, Dict, Tuple, Union
17
+
18
+ if TYPE_CHECKING:
19
+ import ase as _ase_type
20
+
21
+ from .molecule import Molecule
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Soft import — module works without ASE, just degrades gracefully
28
+ # ---------------------------------------------------------------------------
29
+
30
+ ASE_AVAILABLE = False
31
+ try:
32
+ import ase as _ase # noqa: F401 — presence check only
33
+
34
+ ASE_AVAILABLE = True
35
+ except ImportError:
36
+ pass
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Curated educational molecule presets
41
+ #
42
+ # Each entry: display_label -> (ase_name, charge, multiplicity)
43
+ # Multiplicity > 1 is set for well-known open-shell ground states so the
44
+ # notebook can warn students about spin state automatically.
45
+ # ---------------------------------------------------------------------------
46
+
47
+ ASE_MOLECULE_PRESETS: Dict[str, Tuple[str, int, int]] = {
48
+ # Diatomics
49
+ "H₂ — Hydrogen": ("H2", 0, 1),
50
+ "N₂ — Nitrogen": ("N2", 0, 1),
51
+ "O₂ — Oxygen (triplet)": ("O2", 0, 3),
52
+ "F₂ — Fluorine": ("F2", 0, 1),
53
+ "HF — Hydrogen fluoride": ("HF", 0, 1),
54
+ "HCl — Hydrogen chloride": ("HCl", 0, 1),
55
+ "CO — Carbon monoxide": ("CO", 0, 1),
56
+ # Triatomics
57
+ "H₂O — Water": ("H2O", 0, 1),
58
+ "CO₂ — Carbon dioxide": ("CO2", 0, 1),
59
+ "SO₂ — Sulfur dioxide": ("SO2", 0, 1),
60
+ "H₂S — Hydrogen sulfide": ("SH2", 0, 1),
61
+ # Polyatomics
62
+ "NH₃ — Ammonia": ("NH3", 0, 1),
63
+ "PH₃ — Phosphine": ("PH3", 0, 1),
64
+ "CH₄ — Methane": ("CH4", 0, 1),
65
+ # Organic
66
+ "C₂H₂ — Acetylene": ("C2H2", 0, 1),
67
+ "C₂H₄ — Ethylene": ("C2H4", 0, 1),
68
+ "C₂H₆ — Ethane": ("C2H6", 0, 1),
69
+ "CH₃OH — Methanol": ("CH3OH", 0, 1),
70
+ # Aromatic
71
+ "C₆H₆ — Benzene": ("C6H6", 0, 1),
72
+ }
73
+
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Public API
77
+ # ---------------------------------------------------------------------------
78
+
79
+
80
+ def is_ase_available() -> bool:
81
+ """Return True if ASE is installed and importable."""
82
+ return ASE_AVAILABLE
83
+
84
+
85
+ def molecule_to_atoms(molecule: Molecule) -> _ase_type.Atoms:
86
+ """
87
+ Convert a QuantUI Molecule to an ASE Atoms object.
88
+
89
+ Charge and multiplicity are stored in ``atoms.info`` so they survive a
90
+ round-trip through :func:`atoms_to_molecule`.
91
+
92
+ Args:
93
+ molecule: QuantUI Molecule object.
94
+
95
+ Returns:
96
+ ``ase.Atoms`` with identical symbols and positions (Ångströms).
97
+
98
+ Raises:
99
+ ImportError: If ASE is not installed.
100
+ """
101
+ if not ASE_AVAILABLE:
102
+ raise ImportError(
103
+ "ASE is not installed.\n"
104
+ " pip install ase\n"
105
+ " # or: conda install -c conda-forge ase"
106
+ )
107
+ from ase import Atoms # type: ignore[import]
108
+
109
+ atoms = Atoms(
110
+ symbols=molecule.atoms,
111
+ positions=molecule.coordinates,
112
+ )
113
+ atoms.info["charge"] = molecule.charge
114
+ atoms.info["multiplicity"] = molecule.multiplicity
115
+ return atoms
116
+
117
+
118
+ def atoms_to_molecule(
119
+ atoms: _ase_type.Atoms,
120
+ charge: int = 0,
121
+ multiplicity: int = 1,
122
+ ) -> Molecule:
123
+ """
124
+ Convert an ASE Atoms object to a QuantUI Molecule.
125
+
126
+ Charge and multiplicity are read from ``atoms.info`` when present;
127
+ otherwise the supplied default arguments are used.
128
+
129
+ Args:
130
+ atoms: ASE Atoms object.
131
+ charge: Fallback total charge (used when not in ``atoms.info``).
132
+ multiplicity: Fallback spin multiplicity (used when not in ``atoms.info``).
133
+
134
+ Returns:
135
+ Validated QuantUI Molecule object.
136
+ """
137
+ from .molecule import Molecule # local import to avoid circular deps
138
+
139
+ resolved_charge = int(atoms.info.get("charge", charge))
140
+ resolved_mult = int(atoms.info.get("multiplicity", multiplicity))
141
+
142
+ return Molecule(
143
+ atoms=list(atoms.get_chemical_symbols()),
144
+ coordinates=[list(map(float, pos)) for pos in atoms.get_positions()],
145
+ charge=resolved_charge,
146
+ multiplicity=resolved_mult,
147
+ )
148
+
149
+
150
+ def read_structure_file(
151
+ path: Union[str, Path],
152
+ charge: int = 0,
153
+ multiplicity: int = 1,
154
+ **ase_kwargs: object,
155
+ ) -> Molecule:
156
+ """
157
+ Read a molecular structure file using ``ase.io`` and return a Molecule.
158
+
159
+ ASE auto-detects the format from the file extension. Supported formats
160
+ include XYZ (.xyz), PDB (.pdb), MDL MOL (.mol), SDF (.sdf), CIF (.cif),
161
+ and many others. MOL/SDF reading additionally requires rdkit.
162
+
163
+ Args:
164
+ path: Path to the structure file.
165
+ charge: Total molecular charge (used when not encoded in the file).
166
+ multiplicity: Spin multiplicity (used when not encoded in the file).
167
+ **ase_kwargs: Extra keyword arguments forwarded to ``ase.io.read()``.
168
+
169
+ Returns:
170
+ QuantUI Molecule object.
171
+
172
+ Raises:
173
+ ImportError: If ASE is not installed.
174
+ FileNotFoundError: If the file does not exist.
175
+ ValueError: If the file cannot be parsed.
176
+ """
177
+ if not ASE_AVAILABLE:
178
+ raise ImportError(
179
+ "ASE is not installed.\n"
180
+ " pip install ase\n"
181
+ " # or: conda install -c conda-forge ase"
182
+ )
183
+ import ase.io # type: ignore[import]
184
+
185
+ path = Path(path)
186
+ if not path.exists():
187
+ raise FileNotFoundError(f"Structure file not found: {path}")
188
+
189
+ try:
190
+ atoms = ase.io.read(str(path), **ase_kwargs)
191
+ except Exception as exc:
192
+ raise ValueError(
193
+ f"Could not read '{path.name}': {exc}\n"
194
+ "Supported formats: XYZ, PDB, MOL, SDF, CIF, and others via ASE."
195
+ ) from exc
196
+
197
+ return atoms_to_molecule(atoms, charge=charge, multiplicity=multiplicity)
198
+
199
+
200
+ def ase_molecule_library(name: str) -> Molecule:
201
+ """
202
+ Fetch a molecule from ASE's built-in molecular library.
203
+
204
+ Wraps ``ase.build.molecule()`` which provides pre-optimised geometries
205
+ for ~100 small molecules including common diatomics, polyatomics, and
206
+ simple organic molecules.
207
+
208
+ Args:
209
+ name: Molecule identifier as recognised by ``ase.build.molecule()``
210
+ (e.g. ``'H2O'``, ``'CH4'``, ``'C6H6'``).
211
+
212
+ Returns:
213
+ QuantUI Molecule with the ASE-library geometry.
214
+
215
+ Raises:
216
+ ImportError: If ASE is not installed.
217
+ KeyError: If the molecule name is not in the ASE library.
218
+ """
219
+ if not ASE_AVAILABLE:
220
+ raise ImportError(
221
+ "ASE is not installed.\n"
222
+ " pip install ase\n"
223
+ " # or: conda install -c conda-forge ase"
224
+ )
225
+ from ase.build import molecule as _ase_molecule # type: ignore[import]
226
+
227
+ try:
228
+ atoms = _ase_molecule(name)
229
+ except Exception as exc:
230
+ raise KeyError(
231
+ f"Molecule '{name}' not found in ASE library.\n"
232
+ "See: https://wiki.fysik.dtu.dk/ase/ase/build/build.html#molecules"
233
+ ) from exc
234
+
235
+ logger.info("Loaded '%s' from ASE molecule library (%d atoms)", name, len(atoms))
236
+ return atoms_to_molecule(atoms)