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/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