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.
- quantui/__init__.py +311 -0
- quantui/analytics.py +609 -0
- quantui/app.py +5650 -0
- quantui/app_analysis.py +662 -0
- quantui/app_builders.py +2465 -0
- quantui/app_exports.py +194 -0
- quantui/app_formatters.py +493 -0
- quantui/app_history.py +624 -0
- quantui/app_runflow.py +1544 -0
- quantui/app_visualization.py +2620 -0
- quantui/ase_bridge.py +236 -0
- quantui/benchmarks.py +1543 -0
- quantui/c_stderr.py +124 -0
- quantui/cactus.py +88 -0
- quantui/calc_log.py +1116 -0
- quantui/calculator.py +204 -0
- quantui/cancellation.py +88 -0
- quantui/cli.py +288 -0
- quantui/comparison.py +306 -0
- quantui/config.py +725 -0
- quantui/data/js/3Dmol-min.js +2 -0
- quantui/data/js/3Dmol-min.js.LICENSE.txt +5 -0
- quantui/data/library/library.sqlite +0 -0
- quantui/data/manifests/bulk_qm9.json +1 -0
- quantui/data/manifests/curated.json +15482 -0
- quantui/data/manifests/presets.json +816 -0
- quantui/descriptor_cards.py +186 -0
- quantui/freq_calc.py +712 -0
- quantui/freq_ir_workers.py +229 -0
- quantui/gpu_offload.py +278 -0
- quantui/help_content.py +474 -0
- quantui/ir_plot.py +130 -0
- quantui/issue_tracker.py +170 -0
- quantui/live_log.py +387 -0
- quantui/log_utils.py +492 -0
- quantui/molecule.py +577 -0
- quantui/molecule_library.py +433 -0
- quantui/nmr_calc.py +437 -0
- quantui/optimizer.py +670 -0
- quantui/orbital_visualization.py +1102 -0
- quantui/pes_scan.py +420 -0
- quantui/preopt.py +355 -0
- quantui/progress.py +111 -0
- quantui/pubchem.py +1157 -0
- quantui/reorganization_energy.py +435 -0
- quantui/results_storage.py +902 -0
- quantui/security.py +14 -0
- quantui/session_calc.py +622 -0
- quantui/structure_providers.py +277 -0
- quantui/tddft_calc.py +307 -0
- quantui/user_settings.py +238 -0
- quantui/utils.py +287 -0
- quantui/vib_cache.py +247 -0
- quantui/visualization_py3dmol.py +593 -0
- quantui/viz_assets.py +101 -0
- quantui/viz_backend_router.py +243 -0
- quantui-0.5.1.dist-info/METADATA +533 -0
- quantui-0.5.1.dist-info/RECORD +62 -0
- quantui-0.5.1.dist-info/WHEEL +5 -0
- quantui-0.5.1.dist-info/entry_points.txt +2 -0
- quantui-0.5.1.dist-info/licenses/LICENSE +21 -0
- 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)
|