Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
52d3970
Add py3Dmol backend for orbital isosurfaces
NCCU-Schultz-Lab Jun 12, 2026
83254aa
Vendor 3Dmol.js; add offline bootstrap
NCCU-Schultz-Lab Jun 16, 2026
3779284
Add pytest-xdist and stabilize async test
NCCU-Schultz-Lab Jun 16, 2026
6429b3e
Report structure provenance and persist viz
NCCU-Schultz-Lab Jun 16, 2026
eab2737
Use RDKit FF for preopt, unify result cards
NCCU-Schultz-Lab Jun 16, 2026
0ed190e
Load vendored 3Dmol per-view; tolerate offline installs
NCCU-Schultz-Lab Jun 16, 2026
808d46e
Stamp .dev_install_stamp on pip failure
NCCU-Schultz-Lab Jun 16, 2026
1191cec
Bump version to v0.3.0; document offline features
NCCU-Schultz-Lab Jun 16, 2026
10cf9fe
Add Cancel button & guard Clear during run
NCCU-Schultz-Lab Jun 17, 2026
bd7eb7d
Add interactive pre-opt preview (Keep/Revert)
NCCU-Schultz-Lab Jun 17, 2026
71224b3
Add interactive pre-opt preview stepper
NCCU-Schultz-Lab Jun 18, 2026
a21cee3
Improve preopt preview capture and playback
NCCU-Schultz-Lab Jun 18, 2026
773ee0b
Clear stale run status and fix cancel cleanup
NCCU-Schultz-Lab Jun 18, 2026
94ff5bc
Add single-viewer vibrational animation
NCCU-Schultz-Lab Jun 18, 2026
a09c4dd
Defer startup tasks (GPU, history) and tests
NCCU-Schultz-Lab Jun 18, 2026
737c6f0
Make classical pre-opt Preview-only
NCCU-Schultz-Lab Jun 18, 2026
25be651
Prevent stacked vib animations; add live FPS update
NCCU-Schultz-Lab Jun 18, 2026
e507b96
Handle CuPy GPU arrays in session calc
NCCU-Schultz-Lab Jun 18, 2026
57636fa
Bump version to 0.4.0 and add changelog
NCCU-Schultz-Lab Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ nul
/.claude/
/temp - untracked/
/logs
.claudeignore
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,64 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht

## [Unreleased]

## [0.4.0] - 2026-06-18

Interactive-visualization and offline-readiness release. Adds molecular-orbital
isosurfaces and an interactive pre-optimization preview, reworks the 3D viewers
to preserve camera orientation across frames, makes all 3D rendering work
offline, and substantially speeds up startup.

### Added

- **Molecular-orbital isosurfaces (py3Dmol)** — interactive HOMO / LUMO / MO
isosurface viewer rendered with py3Dmol; the downsampled Plotly path remains a
fallback.
- **Interactive classical pre-optimization** — a **Preview** button relaxes the
geometry with a fast bonded force field (RDKit MMFF94 → UFF) and animates the
relaxation in place; **Keep this geometry** adopts it as the active structure
or **Revert** discards it. Stepper controls (play/pause, prev/next, scrub
slider, and an input ⇄ relaxed flip) let you compare geometries, and the
captured trajectory is sampled at even RMSD spacing for smooth playback.
- **Cancel button** — stop a running calculation cooperatively at the next SCF
cycle / optimization step.
- **Live vibrational-animation framerate** — the Vib fps setting updates the
running animation immediately.

### Changed

- **Single persistent 3D viewers** — the trajectory and vibrational-mode viewers
now load all frames into one py3Dmol viewer and switch frames/modes
client-side, so the camera (rotation/zoom) is preserved across steps and modes
and there is no per-frame rebuild or flicker.
- **Pre-optimization is Preview-only** — the silent "classical pre-optimize"
checkbox is gone; pre-optimization happens only through the transparent
Preview → Keep/Revert flow, so nothing relaxes the geometry invisibly. (The
separate QM "geometry optimization before calculation" option is unchanged.)
- **Offline-first 3D rendering** — 3Dmol.js is vendored and loaded per-view from
a local `data:` URI instead of a CDN, so every 3D view works with no network
(the build fails if the vendored asset is missing). Native launchers tolerate
offline `pip install`.
- **Faster startup** — GPU detection and History/Compare population are deferred
off the synchronous construction path, so the UI paints in ~1 s instead of
~15 s; the GPU status badge and dropdowns fill in shortly after.
- **Clear** of the live calculation log is disabled while a calculation runs.

### Fixed

- **GPU-offloaded result extraction** — HOMO–LUMO gap, dipole moment, and
Mulliken charges are now reported for GPU runs. CuPy arrays are copied to host
before extraction, and Mulliken falls back to the CPU object (gpu4pyscf does
not implement population analysis on the GPU).
- **Vibrational animation glitchiness** — stacked animation loops (a new loop
started on every mode switch) made playback jittery and too fast and ignored
the framerate setting; exactly one loop now runs.
- **Cancel status** no longer sticks on "Cancelling…" after a calculation is
cancelled.
- **Stale run status** — "Pre-optimized geometry accepted." is cleared when a new
molecule is loaded or a preview is reverted.
- Structure provenance is reported and the input viewer is persisted across
reloads.

## [0.3.0] - 2026-06-11

Structure-sourcing release. Repairs the external-database structure search and
Expand Down
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,22 @@ research and classroom use.

## What it does

- **Molecule input** — paste XYZ coordinates, draw from a 20+ preset library,
or search PubChem by name or SMILES
- **Molecule input** — paste XYZ coordinates, browse an indexed three-tier
bundled library (20 presets + 156 curated molecules + ~1,900 QM9 structures,
searchable by name/formula), or run a structure search by name, SMILES,
InChI, PubChem CID, InChIKey, or CAS number (PubChem → NCI CACTUS → offline
bundled-library fallback; SMILES/InChI resolve locally with no network)
- **Offline-first** — runs with no internet: the bundled molecule library and
the 3D viewer's JavaScript (3Dmol.js) are vendored, so structure lookup and
every 3D view work in an air-gapped classroom. (Network is used only for the
optional live PubChem/CACTUS search.)
- **3D visualization** — interactive py3Dmol viewer (py3Dmol-first; optional
plotlymol3d fallback for non-trajectory tasks). A capability-aware backend
router picks the right renderer per task, and a Status-tab toggle persists
your default-backend preference between sessions
- **In-session calculations** — RHF, UHF, 9 DFT functionals, MP2, NMR
shielding, TD-DFT UV-Vis, and 1D PES scans via PySCF, running in your
Python kernel (no batch submission)
- **In-session calculations** — RHF, UHF, 9 DFT functionals, MP2, CCSD,
CCSD(T), NMR shielding, TD-DFT UV-Vis, and 1D PES scans via PySCF, running
in your Python kernel (no batch submission)
- **Implicit solvent** — PCM solvation (Water, Ethanol, THF, DMSO,
Acetonitrile) via a single checkbox
- **Rich results** — total energy, HOMO-LUMO gap, Mulliken charges, dipole
Expand Down Expand Up @@ -320,6 +327,8 @@ Five step-by-step notebooks in [`notebooks/tutorials/`](notebooks/tutorials/):
| HSE06 | DFT screened hybrid | Band gaps, large molecules |
| PBE-D3 | DFT GGA + dispersion | Van der Waals complexes, stacking |
| MP2 | Post-HF | Accurate energetics for small molecules (O(N⁵)) |
| CCSD | Post-HF coupled cluster | High-accuracy small-molecule energies (O(N⁶)) |
| CCSD(T) | Post-HF coupled cluster | Benchmark "gold standard" energies (O(N⁷); CPU only) |

### Calculation types

Expand Down Expand Up @@ -378,22 +387,27 @@ quantui/ Main package
optimizer.py QM geometry optimization with trajectory
visualization_py3dmol.py 3D viewer (py3Dmol-first; plotlymol fallback)
viz_backend_router.py Capability-aware backend router (pure function)
viz_assets.py Offline-safe 3Dmol.js loading (vendored, no CDN)
user_settings.py Persistent user preferences (~/.quantui/settings.json)
vib_cache.py On-disk cache of rendered vib-mode HTML
orbital_visualization.py Orbital energy diagrams + cube-file viewer
pubchem.py PubChem molecule search
pubchem.py Structure search client (PubChem + RDKit)
cactus.py NCI CACTUS resolver (fallback structure source)
structure_providers.py Unified resolver chain with offline fallback
molecule_library.py Indexed 3-tier bundled molecule library
comparison.py Side-by-side result tables
results_storage.py Timestamped result persistence (schema v2)
calc_log.py Performance + event logging, time estimation
issue_tracker.py In-app bug-report DB
benchmarks.py Timing calibration benchmark suite
config.py Methods, basis sets, solvent/NMR options, presets
ase_bridge.py ASE structure I/O
preopt.py LJ force-field pre-optimization
preopt.py RDKit MMFF94/UFF force-field pre-optimization
data/ Bundled library (SQLite + manifests) + vendored 3Dmol.js
notebooks/
molecule_computations.ipynb Main user-facing interface (3-cell launcher)
tutorials/ Step-by-step guided notebooks (01–05)
tests/ pytest test suite (~1000 tests)
tests/ pytest test suite (~1500 tests; run in parallel via pytest-xdist)
apptainer/ Container definition for reproducible deployment
local-setup/ Conda environment definition
pyproject.toml Package metadata and tool config
Expand Down
37 changes: 27 additions & 10 deletions apptainer/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# QuantUI — Apptainer Container

The Apptainer container packages Python, PySCF, ASE, py3Dmol, and Voilà
The Apptainer container packages Python, PySCF, RDKit, ASE, py3Dmol, and Voilà
into a single portable `.sif` file. It is the **recommended path for Windows
users** (via WSL) and for anyone who wants a zero-installation experience —
students copy one file and run it.

**Runs fully offline.** The container bundles everything it needs — the
3-tier molecule library and the 3D viewer's JavaScript (3Dmol.js) are vendored,
so structure lookup and every 3D view (molecules, trajectories, vibrations,
orbital isosurfaces) work with **no internet connection**. Network is only ever
used for the optional live PubChem/CACTUS structure search. Ideal for an
air-gapped or restricted-network classroom.

---

## Contents
Expand Down Expand Up @@ -85,8 +92,8 @@ bash apptainer/build.sh --clean --test
bash apptainer/build.sh --fakeroot
```

Build time: **~6 minutes** on a modern laptop with a good internet connection.
Final image size: **~4–5 GB**.
Build time: **~20–40 minutes** (dominated by the conda solve + PySCF download)
on a modern laptop with a good internet connection. Final image size: **~4–5 GB**.

The script must be run from the **repo root** (not from `apptainer/`) because
the `.def` file copies the entire repo root into the container with
Expand Down Expand Up @@ -247,6 +254,14 @@ B3LYP/STO-3G: -75.312587 Ha converged: True
| `PBE` | DFT GGA | Large molecules, speed-critical |
| `PBE0` | DFT hybrid | Charge transfer, band gaps |
| `M06-2X` | DFT meta-hybrid | Reaction barriers, thermochemistry |
| `wB97X-D`, `CAM-B3LYP` | DFT range-separated | Non-covalent / charge-transfer / UV-Vis |
| `M06-L`, `HSE06`, `PBE-D3` | DFT (meta-GGA / screened / dispersion) | Large systems, band gaps, vdW complexes |
| `MP2` | Post-HF | Accurate small-molecule energetics (O(N⁵)) |
| `CCSD`, `CCSD(T)` | Post-HF coupled cluster | Benchmark-quality energies (O(N⁶)/O(N⁷); (T) is CPU-only) |

Six calculation types run over these: Single Point, Geometry Opt, Frequency
(+ thermochemistry / IR), UV-Vis (TD-DFT), NMR shielding, and 1D PES scan; PCM
implicit solvent (Water, Ethanol, THF, DMSO, Acetonitrile) is a single checkbox.

### Basis sets

Expand Down Expand Up @@ -377,12 +392,14 @@ sudo apt-get install -y apptainer
| Layer | Contents |
| --- | --- |
| Base | `continuumio/miniconda3:latest` (Debian + conda) |
| conda-forge | jupyter, jupyterlab, ipywidgets, pyscf, numpy, scipy, matplotlib, plotly, h5py |
| conda-forge | jupyter, jupyterlab, ipywidgets, notebook, pyscf, numpy, scipy, matplotlib, plotly, h5py, rdkit |
| pip | voila, ase, py3dmol, requests |
| QuantUI | installed from `/opt/quantui` (the repo root, copied at build time) |
| QuantUI | installed from `/opt/quantui` (the repo root, copied at build time) — bundles the molecule library + vendored 3Dmol.js for offline use |

The `.git` directory and `__pycache__` folders are removed during build to
keep the image lean.
The `.git` directory, `__pycache__` folders, and internal dev files are removed
during build to keep the image lean. A build-time check asserts the vendored
3Dmol.js is present and the viewer is CDN-free, so a broken offline build fails
fast instead of shipping blank 3D views.

---

Expand All @@ -392,12 +409,12 @@ Edit `%labels` in `quantui.def` to bump the version string, then rebuild:

```singularity
%labels
Version "0.2.0"
Version "0.3.0"
```

Tag the git commit and push so the version is traceable:

```bash
git tag v0.2.0
git push origin v0.2.0
git tag v0.3.0
git push origin v0.3.0
```
7 changes: 6 additions & 1 deletion apptainer/quantui.def
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ FROM: continuumio/miniconda3:latest
%labels
Maintainer "Jonathan Schultz"
Purpose "Local teaching interface for quantum chemistry calculations"
Version "0.1.0"
Version "0.4.0"

%environment
export PATH="/opt/conda/bin:${PATH}"
Expand Down Expand Up @@ -72,6 +72,11 @@ FROM: continuumio/miniconda3:latest
python -c "import pyscf; print('PySCF OK')"
python -c "import rdkit; print('RDKit OK')"
python -c "import py3Dmol; print('py3Dmol OK')"
# Offline 3D: the vendored 3Dmol.js must be bundled and make_view must emit a
# CDN-free viewer that loads it from a local data: URI, or every 3D view
# blanks with no network (the whole point of the offline build). Fail the
# build here rather than ship blank viewers.
python -c "from quantui.viz_assets import _JS_PATH, _js_data_uri, make_view; assert _JS_PATH.exists() and _JS_PATH.stat().st_size > 100000, 'vendored 3Dmol.js missing/too small'; assert _js_data_uri().startswith('data:text/javascript;base64,'); v = make_view(width=80, height=80); v.addModel('1\nH\nH 0 0 0', 'xyz'); h = v._make_html(); assert 'jsdelivr' not in h and 'data:text/javascript;base64,' in h, 'viewer not loading vendored 3Dmol offline'; print('offline 3Dmol OK')"
python -c "import ase; print('ASE OK')"
python -c "from quantui import optimize_geometry; print('optimize_geometry OK')"
python -c "from quantui import run_freq_calc, run_tddft_calc; print('freq/tddft OK')"
Expand Down
2 changes: 1 addition & 1 deletion docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ quantui log tail -n 200 | grep -i error | tail -5

```
2026-05-25T13:55:22.421910+00:00 viz_route_decision task=molecule_preview pref=auto chosen=py3dmol reason=auto -> task primary (py3dmol)
2026-05-25T13:55:22.470028+00:00 startup QuantUI 0.2.0 started
2026-05-25T13:55:22.470028+00:00 startup QuantUI 0.3.0 started (viz backend pref=auto)
2026-05-25T14:08:14.102544+00:00 calc_done B3LYP/STO-3G on H2O elapsed_s=1.2 converged=True gpu_used=True gpu_name=NVIDIA GeForce RTX 4050 Laptop GPU
```

Expand Down
23 changes: 14 additions & 9 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@
<nav class="nav" role="navigation">
<div class="nav__inner">
<a class="nav__logo" href="#">QuantUI</a>
<span class="nav__badge">v0.2 &middot; MIT</span>
<span class="nav__badge">v0.3 &middot; MIT</span>
<a class="nav__link" href="#features">Features</a>
<a class="nav__link" href="#install">Install</a>
<a class="nav__link" href="#tutorials">Tutorials</a>
Expand All @@ -356,6 +356,7 @@
<div class="hero__eyebrow">
<span class="pill">Open-source PySCF frontend</span>
<span class="pill pill--teal">No cluster required</span>
<span class="pill pill--teal">Runs offline</span>
<span class="pill pill--teal">GPU-ready</span>
</div>
<h1 class="hero__title">Free, open, and<br>interactive quantum chemistry</h1>
Expand All @@ -376,7 +377,7 @@ <h1 class="hero__title">Free, open, and<br>interactive quantum chemistry</h1>
<div class="hero__meta">
<span class="hero__stat">Python 3.9&ndash;3.11</span>
<span class="hero__sep">&middot;</span>
<span class="hero__stat">1280+ tests</span>
<span class="hero__stat">1500+ tests</span>
<span class="hero__sep">&middot;</span>
<span class="hero__stat">MIT License</span>
<span class="hero__sep">&middot;</span>
Expand Down Expand Up @@ -440,9 +441,11 @@ <h2 class="section__title">A complete PySCF workflow</h2>
<div class="feature-card__icon">⚗️</div>
<div class="feature-card__title">Molecule Input</div>
<p class="feature-card__body">
Paste XYZ coordinates, draw from a 20+ molecule preset library,
search PubChem by name or SMILES, or load ASE structure files
(<code class="inline-code">.xyz</code>, <code class="inline-code">.cif</code>, <code class="inline-code">.mol</code>).
Paste XYZ, browse an indexed three-tier bundled library (presets +
curated + ~1,900 QM9 structures, searchable by name/formula), or run
a structure search by name, SMILES, InChI, CID, or CAS &mdash;
PubChem &rarr; NCI CACTUS &rarr; an <strong>offline</strong>
bundled-library fallback, so the search still works with no network.
</p>
</div>

Expand All @@ -451,9 +454,11 @@ <h2 class="section__title">A complete PySCF workflow</h2>
<div class="feature-card__title">3D Visualization</div>
<p class="feature-card__body">
py3Dmol-first interactive viewer with a capability-aware
backend router. Optimization trajectories and vibrational
modes animate inline with user-tunable playback FPS and an
on-disk cache for instant replay.
backend router. Molecules, optimization trajectories,
vibrational modes, and orbital isosurfaces all render inline
&mdash; and <strong>fully offline</strong> (3Dmol.js is vendored,
never fetched from a CDN). Tunable playback FPS + on-disk cache
for instant replay.
</p>
</div>

Expand Down Expand Up @@ -576,7 +581,7 @@ <h2 class="section__title">Platform support</h2>
<tr>
<td><strong>Windows native</strong></td>
<td><span class="badge badge--partial">Partial</span></td>
<td>UI, PubChem search, and 3D visualization work; PySCF calculations require the Apptainer container</td>
<td>UI, structure search, and 3D visualization work; PySCF calculations require the Apptainer container</td>
</tr>
</tbody>
</table>
Expand Down
11 changes: 9 additions & 2 deletions launch-native-jupyter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ echo "Using Python: $(command -v python)"
echo "Using Jupyter: $(command -v jupyter)"

# Reinstall editable package only when pyproject metadata changed, or on first run.
# Must not abort the launch when offline (set -e) — pip fetches build deps from
# PyPI; fail fast + non-fatal. The editable source is live regardless.
if [ ! -f .dev_install_stamp ] || [ pyproject.toml -nt .dev_install_stamp ]; then
pip install -e . -q
touch .dev_install_stamp
if pip install -e . -q --timeout=5 --retries=0; then
touch .dev_install_stamp
else
# Stamp even on failure so offline launches don't retry pip every time.
echo "[QuantUI] editable reinstall skipped (offline?) - using live source"
touch .dev_install_stamp
fi
fi

# Prevent stale bytecode from WSL2 DrvFs mtime quirks.
Expand Down
11 changes: 10 additions & 1 deletion launch-native.bat
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ REM Clears quantui/__pycache__ on every launch to prevent stale .pyc bytecode
REM (WSL2 DrvFs does not reliably propagate Windows-side mtime changes, so Python
REM may load pre-edit bytecode even after source changes — see GOTCHAS.md).
REM PYTHONDONTWRITEBYTECODE=1 prevents a new stale cache from accumulating.
start "QuantUI [native]" wsl -d Ubuntu -- bash -c "cd '%WSLPATH%' && source ~/miniconda3/etc/profile.d/conda.sh && conda activate quantui && if [ pyproject.toml -nt .dev_install_stamp ] || ! python -c 'import quantui' 2>/dev/null; then pip install -e . -q && touch .dev_install_stamp; fi && rm -rf quantui/__pycache__ && PYTHONDONTWRITEBYTECODE=1 voila notebooks/molecule_computations.ipynb --no-browser --port=8867 --ServerApp.disable_check_xsrf=True"
REM The editable reinstall MUST NOT block launch when offline: pip fetches
REM build deps (setuptools) from PyPI, which hangs/fails with no network. So it
REM runs with a short timeout + no retries, is non-fatal, and the chain
REM continues to Voila with `; ` (not `&&`) regardless. It also stamps
REM .dev_install_stamp even when skipped, so a one-time pyproject change can't
REM make EVERY subsequent offline launch retry (and re-delay on) pip — the
REM reinstall is attempted once, then skipped. quantui/*.py + package-data are
REM live in editable mode, so a skipped reinstall is harmless offline; re-run
REM `pip install -e .` manually when online if you add a real dependency.
start "QuantUI [native]" wsl -d Ubuntu -- bash -c "cd '%WSLPATH%' && source ~/miniconda3/etc/profile.d/conda.sh && conda activate quantui && if [ pyproject.toml -nt .dev_install_stamp ] || ! python -c 'import quantui' 2>/dev/null; then pip install -e . -q --timeout=5 --retries=0 && touch .dev_install_stamp || { echo '[QuantUI] editable reinstall skipped (offline?) - using live source'; touch .dev_install_stamp; }; fi; rm -rf quantui/__pycache__ && PYTHONDONTWRITEBYTECODE=1 voila notebooks/molecule_computations.ipynb --no-browser --port=8867 --ServerApp.disable_check_xsrf=True"

echo Waiting for Voila to start...
timeout /t 6 /nobreak > nul
Expand Down
Loading
Loading