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/app_exports.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Export helpers used by QuantUIApp."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .results_storage import _safe_name
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def on_export(app: Any, btn: Any) -> None:
|
|
12
|
+
"""Export a standalone Python calculation script."""
|
|
13
|
+
if app._molecule is None:
|
|
14
|
+
app.export_status.value = "Load a molecule first."
|
|
15
|
+
return
|
|
16
|
+
try:
|
|
17
|
+
from quantui import PySCFCalculation
|
|
18
|
+
|
|
19
|
+
calc = PySCFCalculation(
|
|
20
|
+
app._molecule,
|
|
21
|
+
method=app.method_dd.value,
|
|
22
|
+
basis=app.basis_dd.value,
|
|
23
|
+
)
|
|
24
|
+
# M11 audit fix (2026-07-14): the basis set is embedded verbatim in
|
|
25
|
+
# the filename (e.g. "6-31G*.py"), and "*" is invalid in a Windows
|
|
26
|
+
# filename — this export silently failed there. _safe_name (already
|
|
27
|
+
# used by results_storage for the same purpose) replaces anything
|
|
28
|
+
# that isn't alphanumeric/underscore/hyphen with "x".
|
|
29
|
+
fname = (
|
|
30
|
+
f"{_safe_name(app._molecule.get_formula())}"
|
|
31
|
+
f"_{_safe_name(app.method_dd.value)}_{_safe_name(app.basis_dd.value)}.py"
|
|
32
|
+
)
|
|
33
|
+
calc.generate_calculation_script(Path(fname))
|
|
34
|
+
app.export_status.value = f"Saved: {fname}"
|
|
35
|
+
except Exception as exc:
|
|
36
|
+
app.export_status.value = f"Error: {exc}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def on_export_xyz(app: Any, btn: Any) -> None:
|
|
40
|
+
"""Export molecule geometry to an XYZ file."""
|
|
41
|
+
if app._molecule is None:
|
|
42
|
+
app.struct_export_status.value = "Load a molecule first."
|
|
43
|
+
return
|
|
44
|
+
try:
|
|
45
|
+
mol, method, basis = export_molecule_and_label(app)
|
|
46
|
+
fname = f"{_safe_name(mol.get_formula())}_{_safe_name(method)}_{_safe_name(basis)}.xyz"
|
|
47
|
+
xyz_body = mol.to_xyz_string()
|
|
48
|
+
full_xyz = (
|
|
49
|
+
f"{len(mol.atoms)}\n{mol.get_formula()} {method}/{basis}\n{xyz_body}\n"
|
|
50
|
+
)
|
|
51
|
+
dest = (app._last_result_dir / fname) if app._last_result_dir else Path(fname)
|
|
52
|
+
dest.write_text(full_xyz, encoding="utf-8")
|
|
53
|
+
app.struct_export_status.value = f"Saved: {dest}"
|
|
54
|
+
except Exception as exc:
|
|
55
|
+
app.struct_export_status.value = f"Error: {exc}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def on_export_mol(app: Any, btn: Any) -> None:
|
|
59
|
+
"""Export molecule geometry to a MOL file via RDKit."""
|
|
60
|
+
if app._molecule is None:
|
|
61
|
+
app.struct_export_status.value = "Load a molecule first."
|
|
62
|
+
return
|
|
63
|
+
try:
|
|
64
|
+
from rdkit import Chem
|
|
65
|
+
|
|
66
|
+
mol, method, basis = export_molecule_and_label(app)
|
|
67
|
+
fname = f"{_safe_name(mol.get_formula())}_{_safe_name(method)}_{_safe_name(basis)}.mol"
|
|
68
|
+
rdmol = molecule_to_rdkit(mol)
|
|
69
|
+
if rdmol is None:
|
|
70
|
+
app.struct_export_status.value = "RDKit could not parse the structure."
|
|
71
|
+
return
|
|
72
|
+
mol_block = Chem.MolToMolBlock(rdmol)
|
|
73
|
+
dest = (app._last_result_dir / fname) if app._last_result_dir else Path(fname)
|
|
74
|
+
dest.write_text(mol_block, encoding="utf-8")
|
|
75
|
+
app.struct_export_status.value = f"Saved: {dest}"
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
app.struct_export_status.value = f"Error: {exc}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def on_export_pdb(app: Any, btn: Any) -> None:
|
|
81
|
+
"""Export molecule geometry to a PDB file via RDKit."""
|
|
82
|
+
if app._molecule is None:
|
|
83
|
+
app.struct_export_status.value = "Load a molecule first."
|
|
84
|
+
return
|
|
85
|
+
try:
|
|
86
|
+
from rdkit import Chem
|
|
87
|
+
|
|
88
|
+
mol, method, basis = export_molecule_and_label(app)
|
|
89
|
+
fname = f"{_safe_name(mol.get_formula())}_{_safe_name(method)}_{_safe_name(basis)}.pdb"
|
|
90
|
+
rdmol = molecule_to_rdkit(mol)
|
|
91
|
+
if rdmol is None:
|
|
92
|
+
app.struct_export_status.value = "RDKit could not parse the structure."
|
|
93
|
+
return
|
|
94
|
+
pdb_block = Chem.MolToPDBBlock(rdmol)
|
|
95
|
+
dest = (app._last_result_dir / fname) if app._last_result_dir else Path(fname)
|
|
96
|
+
dest.write_text(pdb_block, encoding="utf-8")
|
|
97
|
+
app.struct_export_status.value = f"Saved: {dest}"
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
app.struct_export_status.value = f"Error: {exc}"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def export_molecule_and_label(app: Any) -> tuple[Any, str, str]:
|
|
103
|
+
"""Return (molecule, method, basis) for structure export.
|
|
104
|
+
|
|
105
|
+
For geometry optimization results, returns the final optimized geometry.
|
|
106
|
+
Falls back to the currently loaded molecule for all other calculation types.
|
|
107
|
+
"""
|
|
108
|
+
from quantui.optimizer import OptimizationResult
|
|
109
|
+
|
|
110
|
+
result = app._last_result
|
|
111
|
+
if isinstance(result, OptimizationResult):
|
|
112
|
+
mol = result.molecule
|
|
113
|
+
else:
|
|
114
|
+
assert app._molecule is not None
|
|
115
|
+
mol = app._molecule
|
|
116
|
+
method = (
|
|
117
|
+
getattr(result, "method", app.method_dd.value)
|
|
118
|
+
if result is not None
|
|
119
|
+
else app.method_dd.value
|
|
120
|
+
)
|
|
121
|
+
basis = (
|
|
122
|
+
getattr(result, "basis", app.basis_dd.value)
|
|
123
|
+
if result is not None
|
|
124
|
+
else app.basis_dd.value
|
|
125
|
+
)
|
|
126
|
+
return mol, method, basis
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def on_iso_export_cube(app: Any, btn: Any) -> None:
|
|
130
|
+
"""Copy the last-generated cube file to the result folder.
|
|
131
|
+
|
|
132
|
+
Reads ``app._last_cube_path`` (set by the isosurface render path in
|
|
133
|
+
``app_visualization.py``) and copies it to
|
|
134
|
+
``<result_dir>/<orbital_label>.cube`` so the user can hand a
|
|
135
|
+
friendly-named cube to Avogadro / VMD / Multiwfn without scrolling
|
|
136
|
+
through ``isosurfaces/<formula>_<orb>_<timestamp>.cube``.
|
|
137
|
+
"""
|
|
138
|
+
from quantui.results_storage import export_cube
|
|
139
|
+
|
|
140
|
+
src = getattr(app, "_last_cube_path", None)
|
|
141
|
+
label = getattr(app, "_last_cube_orbital", None) or "orbital"
|
|
142
|
+
result_dir = getattr(app, "_last_result_dir", None)
|
|
143
|
+
if src is None or not isinstance(src, Path) or not src.exists():
|
|
144
|
+
app._iso_export_status.value = (
|
|
145
|
+
'<span style="color:#b22">Generate an isosurface first.</span>'
|
|
146
|
+
)
|
|
147
|
+
return
|
|
148
|
+
if result_dir is None or not isinstance(result_dir, Path):
|
|
149
|
+
app._iso_export_status.value = (
|
|
150
|
+
'<span style="color:#b22">No result folder available.</span>'
|
|
151
|
+
)
|
|
152
|
+
return
|
|
153
|
+
dest = export_cube(src, result_dir, orbital_label=label)
|
|
154
|
+
if dest is None:
|
|
155
|
+
app._iso_export_status.value = (
|
|
156
|
+
'<span style="color:#b22">Cube export failed (see log).</span>'
|
|
157
|
+
)
|
|
158
|
+
return
|
|
159
|
+
app._iso_export_status.value = f'<span style="color:#2a7">Saved: {dest.name}</span>'
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def on_export_bundle(app: Any, btn: Any) -> None:
|
|
163
|
+
"""Zip the entire result folder for sharing."""
|
|
164
|
+
from quantui.results_storage import export_result_bundle
|
|
165
|
+
|
|
166
|
+
result_dir = getattr(app, "_last_result_dir", None)
|
|
167
|
+
if result_dir is None or not isinstance(result_dir, Path):
|
|
168
|
+
app._export_bundle_status.value = "Run or load a calculation first."
|
|
169
|
+
return
|
|
170
|
+
out_path = export_result_bundle(result_dir)
|
|
171
|
+
if out_path is None:
|
|
172
|
+
app._export_bundle_status.value = "Bundle export failed (see log)."
|
|
173
|
+
return
|
|
174
|
+
app._export_bundle_status.value = f"Saved: {out_path}"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def molecule_to_rdkit(mol: Any) -> Any:
|
|
178
|
+
"""Convert a Molecule to an RDKit Mol with inferred bonds (best-effort)."""
|
|
179
|
+
try:
|
|
180
|
+
from rdkit import Chem
|
|
181
|
+
|
|
182
|
+
xyz_block = f"{len(mol.atoms)}\n{mol.get_formula()}\n{mol.to_xyz_string()}\n"
|
|
183
|
+
rdmol = Chem.MolFromXYZBlock(xyz_block)
|
|
184
|
+
if rdmol is None:
|
|
185
|
+
return None
|
|
186
|
+
try:
|
|
187
|
+
from rdkit.Chem import rdDetermineBonds
|
|
188
|
+
|
|
189
|
+
rdDetermineBonds.DetermineBonds(rdmol, charge=mol.charge)
|
|
190
|
+
except Exception:
|
|
191
|
+
pass
|
|
192
|
+
return rdmol
|
|
193
|
+
except Exception:
|
|
194
|
+
return None
|