quantui 0.7.0__tar.gz → 0.8.1__tar.gz

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 (86) hide show
  1. {quantui-0.7.0 → quantui-0.8.1}/CHANGELOG.md +136 -0
  2. {quantui-0.7.0/quantui.egg-info → quantui-0.8.1}/PKG-INFO +85 -8
  3. {quantui-0.7.0 → quantui-0.8.1}/README.md +80 -6
  4. {quantui-0.7.0 → quantui-0.8.1}/pyproject.toml +17 -2
  5. {quantui-0.7.0 → quantui-0.8.1}/quantui/__init__.py +1 -1
  6. {quantui-0.7.0 → quantui-0.8.1}/quantui/analytics.py +28 -16
  7. {quantui-0.7.0 → quantui-0.8.1}/quantui/app.py +674 -119
  8. {quantui-0.7.0 → quantui-0.8.1}/quantui/app_analysis.py +349 -40
  9. {quantui-0.7.0 → quantui-0.8.1}/quantui/app_builders.py +579 -69
  10. quantui-0.8.1/quantui/app_exports.py +886 -0
  11. {quantui-0.7.0 → quantui-0.8.1}/quantui/app_formatters.py +135 -106
  12. {quantui-0.7.0 → quantui-0.8.1}/quantui/app_history.py +3 -1
  13. quantui-0.8.1/quantui/app_measurement.py +425 -0
  14. {quantui-0.7.0 → quantui-0.8.1}/quantui/app_runflow.py +352 -70
  15. {quantui-0.7.0 → quantui-0.8.1}/quantui/app_visualization.py +308 -18
  16. quantui-0.8.1/quantui/app_xyz_input.py +252 -0
  17. {quantui-0.7.0 → quantui-0.8.1}/quantui/benchmarks.py +24 -4
  18. {quantui-0.7.0 → quantui-0.8.1}/quantui/calc_log.py +42 -3
  19. {quantui-0.7.0 → quantui-0.8.1}/quantui/calculator.py +8 -0
  20. {quantui-0.7.0 → quantui-0.8.1}/quantui/checkpoint.py +58 -1
  21. {quantui-0.7.0 → quantui-0.8.1}/quantui/cli.py +9 -3
  22. {quantui-0.7.0 → quantui-0.8.1}/quantui/config.py +1 -0
  23. quantui-0.8.1/quantui/connectivity.py +294 -0
  24. {quantui-0.7.0 → quantui-0.8.1}/quantui/data/library/library.sqlite +0 -0
  25. quantui-0.8.1/quantui/data/manifests/inorganic.json +1412 -0
  26. quantui-0.8.1/quantui/density_fitting.py +89 -0
  27. {quantui-0.7.0 → quantui-0.8.1}/quantui/descriptor_cards.py +17 -8
  28. {quantui-0.7.0 → quantui-0.8.1}/quantui/estimator_eval.py +10 -5
  29. {quantui-0.7.0 → quantui-0.8.1}/quantui/freq_calc.py +26 -2
  30. {quantui-0.7.0 → quantui-0.8.1}/quantui/help_content.py +134 -12
  31. quantui-0.8.1/quantui/inorganic_guards.py +146 -0
  32. {quantui-0.7.0 → quantui-0.8.1}/quantui/log_utils.py +44 -3
  33. quantui-0.8.1/quantui/measurement.py +80 -0
  34. {quantui-0.7.0 → quantui-0.8.1}/quantui/molecule.py +14 -27
  35. quantui-0.8.1/quantui/mulliken_plot.py +58 -0
  36. {quantui-0.7.0 → quantui-0.8.1}/quantui/nmr_calc.py +13 -0
  37. quantui-0.8.1/quantui/nmr_plot.py +118 -0
  38. {quantui-0.7.0 → quantui-0.8.1}/quantui/optimizer.py +75 -1
  39. {quantui-0.7.0 → quantui-0.8.1}/quantui/orbital_visualization.py +172 -11
  40. quantui-0.8.1/quantui/populations_overlay.py +319 -0
  41. {quantui-0.7.0 → quantui-0.8.1}/quantui/preopt.py +222 -11
  42. {quantui-0.7.0 → quantui-0.8.1}/quantui/progress.py +3 -2
  43. {quantui-0.7.0 → quantui-0.8.1}/quantui/pubchem.py +30 -4
  44. {quantui-0.7.0 → quantui-0.8.1}/quantui/reorganization_energy.py +76 -1
  45. {quantui-0.7.0 → quantui-0.8.1}/quantui/results_storage.py +11 -3
  46. {quantui-0.7.0 → quantui-0.8.1}/quantui/session_calc.py +68 -10
  47. quantui-0.8.1/quantui/spin_presets.py +276 -0
  48. {quantui-0.7.0 → quantui-0.8.1}/quantui/tddft_calc.py +17 -1
  49. {quantui-0.7.0 → quantui-0.8.1}/quantui/theme.py +116 -9
  50. {quantui-0.7.0 → quantui-0.8.1}/quantui/user_settings.py +18 -0
  51. {quantui-0.7.0 → quantui-0.8.1}/quantui/vib_cache.py +9 -2
  52. {quantui-0.7.0 → quantui-0.8.1}/quantui/visualization_py3dmol.py +150 -26
  53. quantui-0.8.1/quantui/xyz_input.py +211 -0
  54. {quantui-0.7.0 → quantui-0.8.1/quantui.egg-info}/PKG-INFO +85 -8
  55. {quantui-0.7.0 → quantui-0.8.1}/quantui.egg-info/SOURCES.txt +12 -0
  56. {quantui-0.7.0 → quantui-0.8.1}/quantui.egg-info/requires.txt +5 -1
  57. quantui-0.7.0/quantui/app_exports.py +0 -318
  58. {quantui-0.7.0 → quantui-0.8.1}/LICENSE +0 -0
  59. {quantui-0.7.0 → quantui-0.8.1}/MANIFEST.in +0 -0
  60. {quantui-0.7.0 → quantui-0.8.1}/SECURITY.md +0 -0
  61. {quantui-0.7.0 → quantui-0.8.1}/quantui/ase_bridge.py +0 -0
  62. {quantui-0.7.0 → quantui-0.8.1}/quantui/c_stderr.py +0 -0
  63. {quantui-0.7.0 → quantui-0.8.1}/quantui/cactus.py +0 -0
  64. {quantui-0.7.0 → quantui-0.8.1}/quantui/cancellation.py +0 -0
  65. {quantui-0.7.0 → quantui-0.8.1}/quantui/comparison.py +0 -0
  66. {quantui-0.7.0 → quantui-0.8.1}/quantui/data/js/3Dmol-min.js +0 -0
  67. {quantui-0.7.0 → quantui-0.8.1}/quantui/data/js/3Dmol-min.js.LICENSE.txt +0 -0
  68. {quantui-0.7.0 → quantui-0.8.1}/quantui/data/manifests/bulk_qm9.json +0 -0
  69. {quantui-0.7.0 → quantui-0.8.1}/quantui/data/manifests/curated.json +0 -0
  70. {quantui-0.7.0 → quantui-0.8.1}/quantui/data/manifests/presets.json +0 -0
  71. {quantui-0.7.0 → quantui-0.8.1}/quantui/freq_ir_workers.py +0 -0
  72. {quantui-0.7.0 → quantui-0.8.1}/quantui/gpu_offload.py +0 -0
  73. {quantui-0.7.0 → quantui-0.8.1}/quantui/ir_plot.py +0 -0
  74. {quantui-0.7.0 → quantui-0.8.1}/quantui/issue_tracker.py +0 -0
  75. {quantui-0.7.0 → quantui-0.8.1}/quantui/live_log.py +0 -0
  76. {quantui-0.7.0 → quantui-0.8.1}/quantui/molecule_library.py +0 -0
  77. {quantui-0.7.0 → quantui-0.8.1}/quantui/pes_scan.py +0 -0
  78. {quantui-0.7.0 → quantui-0.8.1}/quantui/security.py +0 -0
  79. {quantui-0.7.0 → quantui-0.8.1}/quantui/structure_providers.py +0 -0
  80. {quantui-0.7.0 → quantui-0.8.1}/quantui/utils.py +0 -0
  81. {quantui-0.7.0 → quantui-0.8.1}/quantui/viz_assets.py +0 -0
  82. {quantui-0.7.0 → quantui-0.8.1}/quantui/viz_backend_router.py +0 -0
  83. {quantui-0.7.0 → quantui-0.8.1}/quantui.egg-info/dependency_links.txt +0 -0
  84. {quantui-0.7.0 → quantui-0.8.1}/quantui.egg-info/entry_points.txt +0 -0
  85. {quantui-0.7.0 → quantui-0.8.1}/quantui.egg-info/top_level.txt +0 -0
  86. {quantui-0.7.0 → quantui-0.8.1}/setup.cfg +0 -0
@@ -7,6 +7,142 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.1] - 2026-08-26
11
+
12
+ ### Added
13
+
14
+ - **PNG export on the geometry-optimization trajectory, vibrational mode, and
15
+ molecule viewers** — the same camera-accurate Save-PNG capture already
16
+ available on the orbital isosurface and reorganization-energy geometry
17
+ viewers now covers every 3-D viewer in the app. The molecule viewer (shown
18
+ on the Calculate, Results, and Analysis tabs) captures only on the py3Dmol
19
+ backend, since Plotly already provides its own "download plot as PNG"
20
+ button; each of its three tabs gets an independent capture button so a
21
+ save from one tab can never be mislabeled with another tab's molecule or
22
+ overwrite another tab's file.
23
+
24
+ ## [0.8.0] - 2026-08-25
25
+
26
+ ### Added
27
+
28
+ - **Transition-metal and coordination-complex support.** QuantUI now
29
+ recognizes and handles metal centers throughout the pipeline instead of
30
+ erroring or silently mishandling them: a pre-run guard catches
31
+ basis-coverage and charge/multiplicity problems before a run starts (with a
32
+ one-click "Switch to def2-SVP" fix when that alone resolves it); a GFN-FF
33
+ (xtb) pre-optimization backend actually relaxes coordination complexes,
34
+ since RDKit's organic valence model can't; the 3D viewer falls back to
35
+ py3Dmol instead of showing a "Visualization failed" box; a fetched
36
+ structure that resolves to a disconnected salt (e.g. cisplatin as separate
37
+ NH₃/HCl/Pt fragments) is flagged before you compute the wrong geometry;
38
+ coordination bonds are drawn dashed since 3Dmol.js's own bond perception
39
+ draws none to a metal; and an oxidation-state → spin-state multiplicity
40
+ suggestion engine proposes — never auto-sets — physically reasonable
41
+ multiplicities, naming the strong- vs weak-field ligand ambiguity where one
42
+ exists.
43
+ - **14 bundled inorganic examples** (up from 3), GFN-FF-validated, spanning
44
+ octahedral (aqua and cyanide, high- and low-spin), tetrahedral, and
45
+ square-planar geometries — a teaching spread across spin states and
46
+ charges.
47
+ - **Density fitting (RI)** as an opt-in SCF speedup — a large win for TD-DFT
48
+ and larger systems, roughly neutral for small single points, so it defaults
49
+ off. Shown on/off in the run-header provenance banner and flagged on the
50
+ result card ("⚡ RI") whenever it was actually applied.
51
+ - **Mulliken Populations Analysis panel.** Per-atom Mulliken charges as a
52
+ table and bar chart after Single Point and Geometry Optimization runs, with
53
+ its own 3D viewer below the energy-level diagram: atoms colored by charge,
54
+ a center-of-mass dipole arrow, a color-vividness slider, and a
55
+ magnitude-only fallback when an older History result has ‖μ‖ but no saved
56
+ vector.
57
+ - **NMR stick plot** with a ¹H/¹³C nucleus toggle, mirroring the existing
58
+ IR/UV-Vis panels; UV-Vis gains λ min/max axis-bound inputs.
59
+ - **Click-to-measure atom selection** on the Analysis-tab viewer — click 2, 3,
60
+ or 4 atoms to read a bond length, angle, or dihedral, GaussView-style
61
+ (py3Dmol only for now; the Plotly viewer shows an explanatory message
62
+ instead of a silent no-op).
63
+ - **Interactive XYZ input.** An atom-row builder and a cleanup-coordinates
64
+ accept/reject preview on the Calculate tab. Loading no longer enforces
65
+ charge/multiplicity parity at parse time — an odd-electron geometry loads
66
+ with a note instead of an error — and charge/multiplicity now stay owned by
67
+ Calculation Setup.
68
+ - **Suggest charge & multiplicity from geometry** — a Calculation Setup
69
+ button proposes a neutral charge and a parity-compatible multiplicity from
70
+ the loaded structure, with an explicit Apply step rather than a silent
71
+ auto-fill.
72
+ - **Export provenance.** Cube, PNG, and XYZ exports now carry method, basis,
73
+ and resolution (plus charge/multiplicity for XYZ) as embedded metadata, not
74
+ just filenames. Reorganization Energy gained its own XYZ export — one file
75
+ per distinct geometry — and a generalized PNG-capture bridge shared with
76
+ the isosurface viewer.
77
+ - **Reorganization Energy is checkpointed.** The run most likely to be
78
+ interrupted — two geometry optimizations plus four SCF energies under one
79
+ Calculate-tab run — can now resume mid-leg instead of restarting from
80
+ scratch.
81
+ - Orbital isosurfaces gained **smoothed meshes** (no more visible
82
+ marching-cubes facets) and a **wireframe finish toggle**.
83
+ - TD-DFT's live status label now surfaces **per-root convergence**
84
+ ("TD-DFT root 3 converged @ 16.663 eV") during the excited-state solve,
85
+ instead of only a generic heartbeat.
86
+ - A representative example gallery in the README and GitHub Pages site, and
87
+ new documentation for inorganic/coordination-complex support.
88
+
89
+ ### Fixed
90
+
91
+ - **Heavy-element runs on an ECP basis were silently wrong.** Cisplatin and
92
+ similar complexes on LANL2DZ or def2 were built with the basis set but no
93
+ effective core potential attached, so PySCF ran all-electron against a
94
+ valence-only basis — converging to a nonphysical energy (positive HOMO)
95
+ with garbage gradients, which made a geometry optimization look like it was
96
+ diverging. ECPs are now attached consistently across every calculation
97
+ type.
98
+ - **Frequency time estimates for app runs were systematically inflated.** The
99
+ estimator's fallback cost model wasn't told which pool a run came from, so
100
+ small-molecule Frequency estimates drew on the calibration pool's
101
+ fresh-subprocess import overhead instead of the app-run pool.
102
+ - **Density fitting's "⚡ RI" badge could silently vanish.** TD-DFT, NMR,
103
+ Frequency, and Geometry Optimization results discarded the density-fitting
104
+ flag instead of storing it, and results were never persisting it to disk
105
+ for *any* calculation type — so even a live single point's badge
106
+ disappeared on History replay.
107
+ - **A warm-start rejection could abort an otherwise-good calculation.** A
108
+ GPU-migrated mean-field rejecting a host density, or an incompatible
109
+ chkfile density, now logs a warning and retries from a scratch guess
110
+ instead of failing the whole run.
111
+ - **Resuming a resumed-and-interrupted-again optimization under-reported its
112
+ progress** — the BFGS step counter wasn't seeded from the checkpoint, so it
113
+ restarted counting at 0 instead of continuing from what had already been
114
+ banked.
115
+ - The resume list's Restore/Discard buttons now log an unexpected exception
116
+ instead of letting it escape into ipywidgets' click dispatch, matching
117
+ every other action button.
118
+ - **Converged-but-near-degenerate metal runs no longer show a stale "HOMO ==
119
+ LUMO" warning** (observed on ferrocene) — the check now looks at the
120
+ converged gap, not PySCF's initial-guess density, which is always
121
+ near-degenerate for a bare d-manifold starting guess.
122
+ - Click-to-measure's highlight halo no longer sits buried inside larger atoms
123
+ — it was a fixed radius; ball-and-stick atoms render at a radius scaled
124
+ from each element's VDW radius.
125
+ - Analysis-tab polish: the welcome logo's SVG fill token, accordions
126
+ expanding automatically when they have data, scroll-to-top on tab entry,
127
+ and a shared seed-geometry dropdown for NMR Shielding.
128
+ - **Older History results without saved Mulliken data now say so
129
+ explicitly**, with a magnitude-only note for dipoles recorded before the
130
+ full vector was saved, instead of a blank or misleading panel.
131
+ - The CPU Apptainer build crashing on a floating `miniforge3:latest` base
132
+ image — libmamba choking on a fuzzy version pin. See
133
+ `apptainer/quantui.def`.
134
+
135
+ ### Changed
136
+
137
+ - Three separate calc-type label→key mappings, and three separate
138
+ implementations of the Hill-formula algorithm, had drifted apart; each is
139
+ now a single shared helper.
140
+ - `python -m quantui.estimator_eval`'s replay is now linear time instead of
141
+ quadratic.
142
+ - ~380 scattered hex color literals across the chrome files are now one
143
+ theme token map.
144
+ - Real type checking restored in CI.
145
+
10
146
  ## [0.7.0] - 2026-08-06
11
147
 
12
148
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantui
3
- Version: 0.7.0
3
+ Version: 0.8.1
4
4
  Summary: An open-source frontend for DFT and post-HF quantum chemistry with PySCF
5
5
  Author-email: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
6
6
  License: MIT License
@@ -62,6 +62,9 @@ Requires-Dist: pyscf<3,>=2.13.0; extra == "pyscf"
62
62
  Requires-Dist: pyscf-properties; extra == "pyscf"
63
63
  Provides-Extra: ase
64
64
  Requires-Dist: ase<4,>=3.22.0; extra == "ase"
65
+ Provides-Extra: xtb
66
+ Requires-Dist: xtb>=22.1; extra == "xtb"
67
+ Requires-Dist: ase<4,>=3.22.0; extra == "xtb"
65
68
  Provides-Extra: app
66
69
  Requires-Dist: voila<0.6,>=0.5.0; extra == "app"
67
70
  Requires-Dist: ipykernel<8,>=6.0.0; extra == "app"
@@ -81,7 +84,7 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
81
84
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
82
85
  Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
83
86
  Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
84
- Requires-Dist: mypy>=1.0.0; extra == "dev"
87
+ Requires-Dist: mypy<2.4,>=1.10; extra == "dev"
85
88
  Requires-Dist: types-requests>=2.28.0; extra == "dev"
86
89
  Requires-Dist: black~=26.5.1; python_version >= "3.10" and extra == "dev"
87
90
  Requires-Dist: black~=25.11.0; python_version < "3.10" and extra == "dev"
@@ -109,13 +112,49 @@ research and classroom use.
109
112
 
110
113
  ---
111
114
 
115
+ ## Example results
116
+
117
+ Real output from QuantUI, straight from the app:
118
+
119
+ <table>
120
+ <tr>
121
+ <td width="50%" align="center" valign="top">
122
+ <img src="docs/images/cisplatin_lumo.png" alt="Cisplatin LUMO isosurface" width="400"><br>
123
+ <b>Cisplatin LUMO</b><br>
124
+ <sub>Molecular-orbital isosurface · B3LYP/LANL2DZ (ECP on Pt &amp; Cl)</sub>
125
+ </td>
126
+ <td width="50%" align="center" valign="top">
127
+ <img src="docs/images/aspartame_orbital_diagram.png" alt="Aspartame orbital energy-level diagram" width="300"><br>
128
+ <b>Aspartame orbital energies</b><br>
129
+ <sub>Occupied/virtual levels · HOMO–LUMO gap 6.09 eV · B3LYP/6-31G*</sub>
130
+ </td>
131
+ </tr>
132
+ <tr>
133
+ <td width="50%" align="center" valign="top">
134
+ <img src="docs/images/benzene_ir_spectrum.png" alt="Benzene IR spectrum" width="400"><br>
135
+ <b>Benzene IR spectrum</b><br>
136
+ <sub>Analytical Hessian · ωB97X-D/6-31G · the four IR-active bands</sub>
137
+ </td>
138
+ <td width="50%" align="center" valign="top">
139
+ <img src="docs/images/cisplatin_optimization.png" alt="Cisplatin geometry-optimization energy convergence" width="400"><br>
140
+ <b>Geometry optimization</b><br>
141
+ <sub>Cisplatin relaxing over 21 BFGS steps · B3LYP/LANL2DZ</sub>
142
+ </td>
143
+ </tr>
144
+ </table>
145
+
146
+ ▶ **[Live interactive gallery →](https://the-schultz-lab.github.io/QuantUI/#examples)** — rotate the 3D structures, and watch the optimization trajectory and a vibrational mode animate.
147
+
148
+ ---
149
+
112
150
  ## What it does
113
151
 
114
- - **Molecule input** — paste XYZ coordinates, browse an indexed three-tier
115
- bundled library (20 presets + 156 curated molecules + ~1,900 QM9 structures,
116
- searchable by name/formula), or run a structure search by name, SMILES,
117
- InChI, PubChem CID, InChIKey, or CAS number (PubChem → NCI CACTUS → offline
118
- bundled-library fallback; SMILES/InChI resolve locally with no network)
152
+ - **Molecule input** — paste XYZ coordinates, browse an indexed bundled library
153
+ (organic presets + curated molecules + ~1,900 QM9 structures + **14
154
+ ready-to-run coordination complexes**, searchable by name/formula), or run a
155
+ structure search by name, SMILES, InChI, PubChem CID, InChIKey, or CAS number
156
+ (PubChem → NCI CACTUS → offline bundled-library fallback; SMILES/InChI resolve
157
+ locally with no network)
119
158
  - **Offline-first** — runs with no internet: the bundled molecule library and
120
159
  the 3D viewer's JavaScript (3Dmol.js) are vendored, so structure lookup and
121
160
  every 3D view work in an air-gapped classroom. (Network is used only for the
@@ -139,6 +178,18 @@ research and classroom use.
139
178
  animation; vibrational frequency analysis with animated normal modes,
140
179
  user-tunable playback FPS, and a per-result-directory disk cache so mode
141
180
  switches on repeat visits and history replay are instant
181
+ - **Inorganic / coordination complexes** — first-class support for
182
+ transition-metal chemistry the organic pipeline can't handle: 14 bundled
183
+ metal complexes (octahedral / tetrahedral / square-planar; aqua, ammine,
184
+ cyanide, carbonyl, halide, oxo) with correct charge and spin; a pre-run guard
185
+ that catches a metal on an incompatible basis (nudges to def2-SVP / LANL2DZ)
186
+ and an impossible charge/multiplicity before the calculation starts; a
187
+ **spin-state helper** that suggests a multiplicity from a metal centre's
188
+ oxidation state and geometry (both high- and low-spin where the ligand field
189
+ decides — you pick); optional **GFN-FF (xtb) pre-optimization** that relaxes a
190
+ metal complex the classical organic force field can't; a warning when a name
191
+ search returns a disconnected salt instead of the coordinated complex; and a
192
+ viewer that draws the coordination bonds so the metal is never a lone dot
142
193
  - **Results persistence** — every calculation is saved automatically to a
143
194
  timestamped directory; a built-in browser lets you reload past results
144
195
  after a kernel restart; the full `pyscf.log` is shown inline
@@ -255,6 +306,26 @@ and result cards will display the compute device.
255
306
  Whenever gpu4pyscf can't offload a particular call, QuantUI falls back
256
307
  to CPU automatically and the result card reflects which device ran.
257
308
 
309
+ ### Optional: GFN-FF metal pre-optimization (xtb)
310
+
311
+ The classical (MMFF/UFF) pre-optimizer relies on RDKit's organic valence
312
+ model, which can't handle a transition metal. Install
313
+ [xtb](https://github.com/grimme-lab/xtb) to enable **GFN-FF**, a general
314
+ force field that relaxes coordination complexes across the whole periodic
315
+ table. Fully optional — without it, metal pre-opt simply reports that it
316
+ isn't available and points you to the DFT geometry optimization.
317
+
318
+ ```bash
319
+ # Linux (PyPI wheels bundle the compiled library):
320
+ pip install quantui[xtb]
321
+
322
+ # Windows / macOS (no PyPI wheel — use conda-forge):
323
+ conda install -c conda-forge xtb-python
324
+ ```
325
+
326
+ QuantUI detects xtb automatically and routes metal pre-optimizations through
327
+ GFN-FF; organic molecules still use the faster RDKit force field.
328
+
258
329
  ---
259
330
 
260
331
  ## Quick start
@@ -456,7 +527,13 @@ Five step-by-step notebooks in [`notebooks/tutorials/`](https://github.com/The-S
456
527
  ### Basis sets
457
528
 
458
529
  STO-3G (fast, good for learning) → 3-21G → 6-31G / 6-31G\* / 6-31G\*\* →
459
- cc-pVDZ / cc-pVTZ → def2-SVP / def2-TZVP
530
+ cc-pVDZ / cc-pVTZ → def2-SVP / def2-TZVP → LANL2DZ
531
+
532
+ For **transition metals and heavy elements**, use **def2-SVP** / **def2-TZVP**
533
+ or **LANL2DZ** — these carry the effective core potentials that cover metals,
534
+ whereas the Pople (`6-31G…`) and Dunning (`cc-pV…`) sets do not. QuantUI's
535
+ pre-run guard flags a metal on an incompatible basis and offers a one-click
536
+ switch to def2-SVP before the calculation starts.
460
537
 
461
538
  ---
462
539
 
@@ -18,13 +18,49 @@ research and classroom use.
18
18
 
19
19
  ---
20
20
 
21
+ ## Example results
22
+
23
+ Real output from QuantUI, straight from the app:
24
+
25
+ <table>
26
+ <tr>
27
+ <td width="50%" align="center" valign="top">
28
+ <img src="docs/images/cisplatin_lumo.png" alt="Cisplatin LUMO isosurface" width="400"><br>
29
+ <b>Cisplatin LUMO</b><br>
30
+ <sub>Molecular-orbital isosurface · B3LYP/LANL2DZ (ECP on Pt &amp; Cl)</sub>
31
+ </td>
32
+ <td width="50%" align="center" valign="top">
33
+ <img src="docs/images/aspartame_orbital_diagram.png" alt="Aspartame orbital energy-level diagram" width="300"><br>
34
+ <b>Aspartame orbital energies</b><br>
35
+ <sub>Occupied/virtual levels · HOMO–LUMO gap 6.09 eV · B3LYP/6-31G*</sub>
36
+ </td>
37
+ </tr>
38
+ <tr>
39
+ <td width="50%" align="center" valign="top">
40
+ <img src="docs/images/benzene_ir_spectrum.png" alt="Benzene IR spectrum" width="400"><br>
41
+ <b>Benzene IR spectrum</b><br>
42
+ <sub>Analytical Hessian · ωB97X-D/6-31G · the four IR-active bands</sub>
43
+ </td>
44
+ <td width="50%" align="center" valign="top">
45
+ <img src="docs/images/cisplatin_optimization.png" alt="Cisplatin geometry-optimization energy convergence" width="400"><br>
46
+ <b>Geometry optimization</b><br>
47
+ <sub>Cisplatin relaxing over 21 BFGS steps · B3LYP/LANL2DZ</sub>
48
+ </td>
49
+ </tr>
50
+ </table>
51
+
52
+ ▶ **[Live interactive gallery →](https://the-schultz-lab.github.io/QuantUI/#examples)** — rotate the 3D structures, and watch the optimization trajectory and a vibrational mode animate.
53
+
54
+ ---
55
+
21
56
  ## What it does
22
57
 
23
- - **Molecule input** — paste XYZ coordinates, browse an indexed three-tier
24
- bundled library (20 presets + 156 curated molecules + ~1,900 QM9 structures,
25
- searchable by name/formula), or run a structure search by name, SMILES,
26
- InChI, PubChem CID, InChIKey, or CAS number (PubChem → NCI CACTUS → offline
27
- bundled-library fallback; SMILES/InChI resolve locally with no network)
58
+ - **Molecule input** — paste XYZ coordinates, browse an indexed bundled library
59
+ (organic presets + curated molecules + ~1,900 QM9 structures + **14
60
+ ready-to-run coordination complexes**, searchable by name/formula), or run a
61
+ structure search by name, SMILES, InChI, PubChem CID, InChIKey, or CAS number
62
+ (PubChem → NCI CACTUS → offline bundled-library fallback; SMILES/InChI resolve
63
+ locally with no network)
28
64
  - **Offline-first** — runs with no internet: the bundled molecule library and
29
65
  the 3D viewer's JavaScript (3Dmol.js) are vendored, so structure lookup and
30
66
  every 3D view work in an air-gapped classroom. (Network is used only for the
@@ -48,6 +84,18 @@ research and classroom use.
48
84
  animation; vibrational frequency analysis with animated normal modes,
49
85
  user-tunable playback FPS, and a per-result-directory disk cache so mode
50
86
  switches on repeat visits and history replay are instant
87
+ - **Inorganic / coordination complexes** — first-class support for
88
+ transition-metal chemistry the organic pipeline can't handle: 14 bundled
89
+ metal complexes (octahedral / tetrahedral / square-planar; aqua, ammine,
90
+ cyanide, carbonyl, halide, oxo) with correct charge and spin; a pre-run guard
91
+ that catches a metal on an incompatible basis (nudges to def2-SVP / LANL2DZ)
92
+ and an impossible charge/multiplicity before the calculation starts; a
93
+ **spin-state helper** that suggests a multiplicity from a metal centre's
94
+ oxidation state and geometry (both high- and low-spin where the ligand field
95
+ decides — you pick); optional **GFN-FF (xtb) pre-optimization** that relaxes a
96
+ metal complex the classical organic force field can't; a warning when a name
97
+ search returns a disconnected salt instead of the coordinated complex; and a
98
+ viewer that draws the coordination bonds so the metal is never a lone dot
51
99
  - **Results persistence** — every calculation is saved automatically to a
52
100
  timestamped directory; a built-in browser lets you reload past results
53
101
  after a kernel restart; the full `pyscf.log` is shown inline
@@ -164,6 +212,26 @@ and result cards will display the compute device.
164
212
  Whenever gpu4pyscf can't offload a particular call, QuantUI falls back
165
213
  to CPU automatically and the result card reflects which device ran.
166
214
 
215
+ ### Optional: GFN-FF metal pre-optimization (xtb)
216
+
217
+ The classical (MMFF/UFF) pre-optimizer relies on RDKit's organic valence
218
+ model, which can't handle a transition metal. Install
219
+ [xtb](https://github.com/grimme-lab/xtb) to enable **GFN-FF**, a general
220
+ force field that relaxes coordination complexes across the whole periodic
221
+ table. Fully optional — without it, metal pre-opt simply reports that it
222
+ isn't available and points you to the DFT geometry optimization.
223
+
224
+ ```bash
225
+ # Linux (PyPI wheels bundle the compiled library):
226
+ pip install quantui[xtb]
227
+
228
+ # Windows / macOS (no PyPI wheel — use conda-forge):
229
+ conda install -c conda-forge xtb-python
230
+ ```
231
+
232
+ QuantUI detects xtb automatically and routes metal pre-optimizations through
233
+ GFN-FF; organic molecules still use the faster RDKit force field.
234
+
167
235
  ---
168
236
 
169
237
  ## Quick start
@@ -365,7 +433,13 @@ Five step-by-step notebooks in [`notebooks/tutorials/`](https://github.com/The-S
365
433
  ### Basis sets
366
434
 
367
435
  STO-3G (fast, good for learning) → 3-21G → 6-31G / 6-31G\* / 6-31G\*\* →
368
- cc-pVDZ / cc-pVTZ → def2-SVP / def2-TZVP
436
+ cc-pVDZ / cc-pVTZ → def2-SVP / def2-TZVP → LANL2DZ
437
+
438
+ For **transition metals and heavy elements**, use **def2-SVP** / **def2-TZVP**
439
+ or **LANL2DZ** — these carry the effective core potentials that cover metals,
440
+ whereas the Pople (`6-31G…`) and Dunning (`cc-pV…`) sets do not. QuantUI's
441
+ pre-run guard flags a metal on an incompatible basis and offers a one-click
442
+ switch to def2-SVP before the calculation starts.
369
443
 
370
444
  ---
371
445
 
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
 
9
9
  [project]
10
10
  name = "quantui"
11
- version = "0.7.0"
11
+ version = "0.8.1"
12
12
  description = "An open-source frontend for DFT and post-HF quantum chemistry with PySCF"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.9"
@@ -118,6 +118,17 @@ ase = [
118
118
  "ase>=3.22.0,<4",
119
119
  ]
120
120
 
121
+ # GFN-FF metal-capable classical pre-optimization via xtb (Grimme's general
122
+ # force field). RDKit's organic valence model can't touch a transition metal;
123
+ # GFN-FF covers the whole periodic table. quantui.preopt uses it when present
124
+ # and falls back gracefully otherwise. xtb publishes **Linux pip wheels only** —
125
+ # on Windows/macOS install it from conda-forge (see local-setup/environment.yml).
126
+ # Depends on ase (the optimizer driving the relaxation).
127
+ xtb = [
128
+ "xtb>=22.1",
129
+ "ase>=3.22.0,<4",
130
+ ]
131
+
121
132
  # Voilà app server — hides notebook code; students see only the widget UI.
122
133
  # Run with: voila notebooks/molecule_computations.ipynb
123
134
  app = [
@@ -164,7 +175,11 @@ dev = [
164
175
  "pytest-cov>=4.0.0",
165
176
  "pytest-mock>=3.10.0",
166
177
  "pytest-xdist>=3.0.0", # parallel test execution (-n=auto in addopts)
167
- "mypy>=1.0.0",
178
+ "mypy>=1.10,<2.4", # pinned, not an open floor — same reasoning as
179
+ # black/ruff below: it must agree with .pre-commit-config.yaml's rev,
180
+ # which is what CI enforces (M-TYPECHECK TYPE.2). A newer mypy silently
181
+ # disagrees: 2.x drops support for python_version = "3.9" (this repo's
182
+ # floor) and reports a different error set than the pinned check.
168
183
  "types-requests>=2.28.0",
169
184
  # Formatter/linter versions are pinned to a compatible range, NOT an open
170
185
  # floor: they must agree with the revs in .pre-commit-config.yaml, which is
@@ -7,7 +7,7 @@ Calculations run locally in the Jupyter session — no cluster or SLURM required
7
7
  PySCF requires Linux/macOS/WSL. Windows users should use the Apptainer container.
8
8
  """
9
9
 
10
- __version__ = "0.7.0"
10
+ __version__ = "0.8.1"
11
11
 
12
12
  import logging
13
13
  from typing import Any
@@ -34,7 +34,7 @@ import statistics
34
34
  from collections import defaultdict
35
35
  from datetime import datetime, timezone
36
36
  from pathlib import Path
37
- from typing import Optional
37
+ from typing import Optional, cast
38
38
 
39
39
  from quantui.calc_log import _log_dir, get_perf_history, get_prediction_history
40
40
 
@@ -280,11 +280,17 @@ def _bar_chart_html(
280
280
  margin=dict(l=40, r=20, t=10, b=40),
281
281
  plot_bgcolor="#ffffff",
282
282
  )
283
- return pio.to_html(
284
- fig,
285
- include_plotlyjs="inline" if include_plotlyjs else False,
286
- full_html=False,
287
- config={"displayModeBar": False},
283
+ # plotly has no bundled type stubs; --ignore-missing-imports leaves
284
+ # pio.to_html untyped. It genuinely returns str (verified: this
285
+ # function's other returns are explicit None for the no-data case).
286
+ return cast(
287
+ str,
288
+ pio.to_html(
289
+ fig,
290
+ include_plotlyjs="inline" if include_plotlyjs else False,
291
+ full_html=False,
292
+ config={"displayModeBar": False},
293
+ ),
288
294
  )
289
295
 
290
296
 
@@ -344,11 +350,14 @@ def _timeline_html(records: list[dict], *, include_plotlyjs: bool) -> Optional[s
344
350
  plot_bgcolor="#ffffff",
345
351
  legend=dict(orientation="h", x=0, y=1.05),
346
352
  )
347
- return pio.to_html(
348
- fig,
349
- include_plotlyjs="inline" if include_plotlyjs else False,
350
- full_html=False,
351
- config={"displayModeBar": False},
353
+ return cast(
354
+ str,
355
+ pio.to_html(
356
+ fig,
357
+ include_plotlyjs="inline" if include_plotlyjs else False,
358
+ full_html=False,
359
+ config={"displayModeBar": False},
360
+ ),
352
361
  )
353
362
 
354
363
 
@@ -452,11 +461,14 @@ def _prediction_scatter_html(
452
461
  plot_bgcolor="#ffffff",
453
462
  legend=dict(orientation="h", x=0, y=1.05),
454
463
  )
455
- return pio.to_html(
456
- fig,
457
- include_plotlyjs="inline" if include_plotlyjs else False,
458
- full_html=False,
459
- config={"displayModeBar": False},
464
+ return cast(
465
+ str,
466
+ pio.to_html(
467
+ fig,
468
+ include_plotlyjs="inline" if include_plotlyjs else False,
469
+ full_html=False,
470
+ config={"displayModeBar": False},
471
+ ),
460
472
  )
461
473
 
462
474