Release v0.2.0: docs refresh, viz backend router, vib renderer + cache, telemetry.#15
Merged
Conversation
Introduce a pure-function router (quantui.viz_backend_router) that resolves which 3D rendering backend (py3Dmol or plotlymol3d) to use per visualization task based on user preference and runtime availability. Implements VizTask, VizPreference, VizBackend enums, BackendAvailability probe, and an immutable Decision result that includes chosen backend, optional fallback, and a human-readable reason. Encodes a task->(primary,fallback) policy (single-backend tasks ignore preference) and robust fallback logic. Adds exhaustive unit tests (tests/test_viz_backend_router.py) covering the full task × preference × availability matrix, immutability and reason-string requirements.
Introduce a new UserSettings facility to persist user preferences to ~/.quantui/settings.json (overrideable via QUANTUI_SETTINGS_PATH). The settings module provides a small schema (viz section with default_backend), atomic writes, schema-versioning, and robust fallbacks for malformed/missing files. Integrate settings into the app: load settings at startup, snapshot viz backend availability, expose a "Default 3D backend" ToggleButtons control in the Status tab, and persist changes when the user updates the preference. Add comprehensive unit tests covering load/save roundtrips, fallback paths, atomic write semantics, and path-resolution behavior.
Introduce per-task backend routing and sync logic for 3D visualization: resolve_backend/select_backend are used to pick backends per VizTask, and a new _set_viz_preference centralizes preference updates (with optional persistence). Add a sync lock (_viz_sync_in_progress) and an analysis-tab backend toggle + small "Rendering with:" label to keep Calculate/Analysis toggles in parity without observer echo loops. At startup the persisted preference is resolved and widgets are aligned via _initialize_viz_state_from_preference; _rerender_3d_views re-renders visible viewers through the router. Hard-wire trajectory frame rendering to py3Dmol (update task policy) to avoid Plotly/RequireJS flicker; trajectory rendering now uses router decisions and emits explicit error frames when py3Dmol is unavailable. Implement atomic HTML swaps for frame output to eliminate layout flashes, add prev/next arrow navigation, fix frame_out sizing, and improve py3Dmol/plotlymol fallbacks in trajectory playback. Refactor show_result_3d to call the router per output/task and track the analysis-displayed molecule so toggling backends can re-render the Analysis viewer. Update viz_backend_router policy (TRAJECTORY_FRAME -> py3Dmol-only) and extend tests: adjust existing router tests and add comprehensive tests (tests/test_viz_backend_sync.py) for toggle parity, sync behaviour, startup preference application, and router-driven rendering.
Replace traj_output Output with a VBox and switch to manipulating children atomically to avoid deferred/asynchronous widget display that left the Trajectory accordion empty. Add a safety-net cache (_last_traj_result) set by trajectory-populators and cleared on context reset so on_traj_expand can recover when a prior auto-render consumed _pending_traj_result. Update on_traj_expand to detect empty children, restore from cache, show a loading placeholder via children, and log recovery/metrics. Update show_opt_trajectory to build Plotly HTML into an Output.outputs entry and set traj_output.children = (energy_holder, panel) to ensure scripts execute and avoid the stale-empty UI. Minor related updates: clear traj_output by resetting children and set _last_traj_result alongside _pending_traj_result in pop_* trajectory helpers.
Introduce a py3Dmol-based vibrational animation path and resilient backend dispatch while keeping plotlymol3d optional. show_vib_animation now builds dropdown options from raw frequencies (skipping near-zero modes), treats vib_data as optional, and caches the Frequency result in _last_vib_freq_result. Added _vib_err for consistent user-facing errors, _render_vib_mode_py3dmol to generate 24-frame sinusoidal multi-frame XYZ payloads and atomically swap HTML into app.vib_output, and _render_vib_mode_plotlymol for the existing plotlymol3d flow. render_vib_mode now dispatches via the viz backend router (py3Dmol preferred, plotlymol3d fallback) and logging events were enriched. on_vib_mode_changed updated to allow py3Dmol rendering when vib_data is None. Added comprehensive tests (tests/test_vib_py3dmol_render.py) covering py3Dmol rendering, frame count, amplitude scaling, error handling, and backend dispatch.
Add a persistent vibrational-animation framerate preference and robust caching/staleness protections. - Introduce viz.vib_framerate_fps in user settings with validation and sane defaults. - Add an IntSlider in the Status panel to control and persist the vib FPS. - Add app._vib_render_token and wire render_token through vib rendering paths so older background renders bail out and cannot overwrite newer outputs. - Implement atomic output swaps via _swap_vib_output to avoid transient empty states and layout reflow. - Wire fps into py3Dmol animate interval and include fps in the vib cache key. - Add quantui.vib_cache module to save/load per-result-directory HTML blobs (index.json + mode_NNN.html), with atomic writes, parameter matching (n_frames, amplitude, renderer, fps), tolerance for floating amplitude, and graceful error handling. - Update vib render functions to accept fps and render_token, consult user settings when fps is not provided, check cache hits and persist cache on render success. - Update and add tests: user settings tests for vib framerate, comprehensive vib_cache unit tests, and integration/tests for rendering token staleness and fps behavior. These changes improve UX by enabling configurable playback speed, eliminating stale-render races on rapid mode switches, and making repeated visits/history playback instant via on-disk caching.
Add prev/next buttons and fixed-size output for vibrational viewer; wire up observers and click handlers in QuantUIApp to enable one-step mode navigation and avoid layout flicker. Introduce a synchronous vib-cache-hit fast path to swap cached py3Dmol HTML without showing a transient "Rendering…" placeholder, and ensure the vib wrapper accepts render_token kwargs to avoid silent thread TypeErrors. Implement camera-persistence JS and a reset hook so interactive camera/view is preserved across mode switches (but reset when a new frequency result loads). Add a _viz_render_event context manager to emit lifecycle telemetry (viz_render_start / viz_render_done / viz_render_error) for render operations and wrap vib/trajectory/structure renders to log backend, task, elapsed time and extras. Harden error handling so telemetry is logged while user-facing errors are shown but worker threads don't crash. Include new unit tests for telemetry and the render_token wrapper, and small adjustments to vib/plotlymol/py3dmol render flows to integrate these features.
Add release artifacts and documentation for v0.2.0: add CHANGELOG.md, bump package version (pyproject.toml and quantui.__version__), and expand README/docs with new features, launch instructions, and updated test baseline. Introduce launch-native.command (macOS launcher) and update .gitattributes to enforce LF for .command files. Enrich .github/copilot-instructions.md with modular UI layout, viz backend router, user settings, vib cache, telemetry, and other developer notes. Update packaging metadata (keywords/classifiers) and site copy (docs/index.html) to reflect the new modular UI and capabilities.
Provide a compatibility shim for enum.StrEnum when running on Python <3.11: import sys and Enum, and define a StrEnum subclass that preserves StrEnum behavior (members compare equal to strings and stringify to their value). This keeps the VizTask enum implementation working on supported Python 3.9/3.10 environments while using the stdlib StrEnum on 3.11+.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[0.2.0] - 2026-05-22
First substantial release after
v0.1.0. The codebase moved from a singlemonolithic
app.pyto a modular package, added six PySCF-backed calculationtypes end-to-end, introduced a results-persistence layer with history replay,
and shipped a complete visualization stack (3D viewer with selectable backend,
trajectory animation, IR/UV-Vis/PES plots, orbital isosurfaces, vibrational
mode animation with caching). UI runs as a Voilà app suitable for classroom
deployment.
Added
Calculations
optimizer.py) — ASE-BFGS driver around a customPySCF calculator; per-step trajectory persisted.
freq_calc.py) — Hessian viapyscf.hessian, ZPVE, thermochemistry (H/S/G at 298 K), IR intensities viapyscf.prop.infraredor a numerical-derivative fallback for compatibilityacross PySCF versions.
tddft_calc.py) — excitation energies, oscillatorstrengths, wavelengths; full spectrum plot in the Analysis tab.
nmr_calc.py) — GIAO shielding viapyscf.nmr(corepreferred over
pyscf-propertiesto dodge a known upstream bug); ¹H/¹³Cchemical shifts relative to TMS.
pes_scan.py) — bond / angle / dihedral; energy profile +per-step geometry animation.
single checkbox in the Calculate tab.
Analysis & visualization
Vibrational, IR Spectrum, PES Scan, Isosurface, UV-Vis, NMR) wired through
a
_PANEL_REGISTRYso live runs and history replay share one code path.ir_plot.py) — stick plot + Lorentzian-broadenedcurve; broadening toggle and FWHM slider.
orbital_visualization.py) — energy-leveldiagram (matplotlib → Plotly HTML) and cube-file isosurface viewer with
HOMO-1/HOMO/LUMO/LUMO+1 toggle.
Voilà-deferred-display blank frames; py3Dmol-only render path with
prev/next arrow navigation.
amplitude scaling, prev/next mode nav, and dropdown skipping near-zero
modes.
viz_backend_router.py) — purefunction that picks py3Dmol or plotlymol3d per
VizTaskbased on userpreference and runtime availability; immutable
Decisioncarries chosenbackend, fallback, and reason.
_viz_render_eventcontext manager emitsviz_render_start/viz_render_done/viz_render_errorJSONL eventswith backend, task,
elapsed_ms, and extras at every render dispatch.diff table.
Persistence & logging
results_storage.py) — every run is saved to atimestamped directory containing
result.json(schema v2, additive-only),pyscf.log, optionaltrajectory.json/orbitals.npz/thumbnail.png.restart; replay path is identical to live-run analysis activation.
calc_log.py) —perf_log.jsonlper converged run +event_log.jsonlfor startup/calc/error events; 7-day auto-prune.electron-count) populates "Estimated time" before each run.
benchmarks.py) — one-click calibration suite topopulate the time-estimator history with real machine data.
issue_tracker.py) — in-app bug-report UI writing to alocal
issues.db.user_settings.py) — stored at~/.quantui/settings.json(override viaQUANTUI_SETTINGS_PATH). Schemais section-based for additive growth, with atomic writes and graceful
fallback to defaults on corruption.
vib_cache.py) — per-result-dirvib_frames/of pre-rendered py3Dmol HTML keyed by(mode, n_frames, amplitude, renderer, fps). Mode switches on repeatvisits and history replay are instant.
viz.vib_framerate_fpsexposed as anIntSlider in the Status tab (clamped 1–120, default 10); included in the
vib cache key so changing FPS invalidates cleanly.
UI
app.py(orchestration) plusapp_analysis.py,app_builders.py,app_exports.py,app_formatters.py,app_history.py,app_runflow.py,app_visualization.py.Status — with a floating Help overlay (not a tab).
reset), default-3D-backend toggle, vib-FPS slider.
jumping the page.
pyscf.log..pyscript export.
Tooling & dev
quantui-winenv baseline; the 97 skips are PySCF-gated Linux-only tests).(
test_sp_analysis_history.py,test_geo_opt_analysis_history.py,test_freq_analysis_history.py,test_tddft_analysis_history.py,test_nmr_analysis_history.py,test_pes_scan_analysis_history.py).test_code_quality.pyenforces:include_plotlyjs="cdn"anywhere (fails silently in offline Voilà).except: passblocks.test_viz_backend_router.py+test_viz_backend_sync.py— fulltask × preference × availability matrix and Calculate/Analysis toggle sync.
test_vib_cache.py,test_vib_py3dmol_render.py,test_viz_render_telemetry.py— vib animation + telemetry coverage._layout(...)helper sanitiseswidgets.Layoutkwargs to eliminate a4808 → 13 traitlets warning regression.
_safe_cbwrapper around every.observe()callback so exceptions surfacein the Log tab instead of disappearing into the Voilà kernel console.
io_loopis cached at startup; thread-spawned callbacks are queuedonto the main thread to avoid
RuntimeError: no current event loop.launch-native.bat(Windows / WSL) andlaunch-native.command(macOS / Linux) — double-clickable, port8867,stamp-based editable-install skip, browser auto-open. README documents
pinning each to the Start menu / Dock as a real app.
launch-native-jupyter.bat) and Apptainerlauncher improvements.
Docs
.github/copilot-instructions.md— canonical AI-assistant context (nowthe single source of truth for any AI assistant working on this repo).
CLAUDE.md— Claude-specific session/workflow context (git-ignored).Changed
plotlymol3dremains an optionalfallback for non-trajectory tasks; trajectory rendering is hard-wired to
py3Dmol to avoid Plotly/RequireJS flicker.
plotly.io.to_html(..., include_plotlyjs="require")inside
widgets.HTML, notdisplay(fig), so threaded renders work andoffline Voilà loads correctly.
pyscfis now an optional extra (pip install quantui[pyscf]); thepackage imports cleanly on Windows with PySCF unavailable.
QuantUI-localtoQuantUI.Fixed
traj_outputfrom
OutputtoVBoxand use atomic children-swap so deferredwidget-display is no longer a blank-frame risk.
_vib_render_token)causes stale background renders to bail rather than overwriting newer
output.
$3Dmol.GLViewerstate across atomic HTML swaps; reset only on agenuinely new frequency result.
pyscf.prop.infraredrename; bothInfrared.kernel()and the olderIR API are supported.
formula + method + basis no longer overwrite each other.
on accordion expand to handle RequireJS / display-deferral edge cases.
Removed
visualization.py(PlotlyMol fallback) — replaced by the router-backedvisualization_py3dmol.pypath.job_manager.py,storage.py,slurm_errors.py, SLURM config templates.