Interactive 3D quantum orbital visualiser in modern C++, with a native OpenGL viewer and a TypeScript browser interface.
Exact hydrogenic wavefunctions from the Schrödinger equation, scaled by nuclear charge (Z) or Slater (Z_\text{eff}), so the same code works for any element.
A computational-physics / visualisation project that:
- Samples electron probability density (|\psi_{nlm}|^2) from exact hydrogenic orbitals
- Renders a live OpenGL point cloud of those samples
- Optionally animates particles along the quantum probability current
- Optionally ray-marches a volumetric density field on the GPU (fragment shader)
- Loads element data from
data/elements.json(Z, symbol, config, mass)
Console-only mode remains available when OpenGL is unavailable (writes .xyz clouds).
Physics sampling and visualisation ideas are informed by:
- kavan010/Atoms — Hydrogen Quantum Orbital Visualizer
(real-time OpenGL point clouds, raytracer, Laguerre/Legendre sampling)
This project extends that direction with:
- Multi-element support via (Z) / (Z_\text{eff})
- Modular CMake layout and clean separation of physics vs rendering
- Dual CDF sampling (radial + angular)
- Probability-current flow integrated into the live viewer
- Optional-at-build-time CUDA acceleration with runtime CPU fallback
| Feature | Status |
|---|---|
| OpenGL point cloud of (|\psi|^2) | Done |
| Orbit / zoom camera | Done |
| Live change of (n, l, m) | Done |
| Any element via JSON | Done |
| Dual CDF sampling | Done |
| Probability-current flow (key P) | Done |
| GPU volumetric raytracer (key T) | Done |
| CUDA parallel sampling & updates | Implemented; GPU validation pending |
| TypeScript orbital UI and local JSON transport | Implemented; C++ sampling/current, WebGL point preview |
| Two-orbital time-dependent interference | Implemented; pure-Z, n ≤ 4, density snapshots |
| Provisional browser demo | C++ compiled to WebAssembly; CPU physics, WebGL points |
| Agentic AI workflow helpers | Optional |
| Input | Action |
|---|---|
| Mouse drag | Orbit |
| Scroll | Zoom |
| N / B | (n) ↑ / ↓ |
| L / K | (l) ↑ / ↓ |
| M / J | (m) ↑ / ↓ |
| P | Toggle probability-current flow |
| T | Toggle POINTS ↔ RAYTRACE |
| , / . | Flow speed ↓ / ↑ |
| + / - | Particle count |
| [ / ] | Point size |
| R | Resample |
| Esc | Quit |
- Radial (R_{nl}(r)): associated Laguerre recurrence +
tgammanormalisation - Angular (Y_l^m): associated Legendre
- Sampling: CDF inversion for (r) and (\theta); (\phi) uniform
- Probability current (atomic units, polar axis = Y):
[ \mathbf{v} = \frac{m}{r\sin\theta},\hat\phi ]
- Energy: (E_n = -13.6,Z^2/n^2) eV (exact for one-electron ions)
Console input requires whole integers for atomic numbers and m, exact orbital labels (1s through 7f, with l < n), and explicit p/s and v/c choices. Invalid input exits with an error instead of silently substituting another orbital. Both exported clouds and the viewer use Y as the polar axis. Current animation rotates samples about Y, preserving their radius and height.
Slater screening follows the grouped shielding rules and uses the expanded neutral electron configuration in the element JSON. The selected ns/np, nd, or nf group must be occupied. For unoccupied groups or custom elements without a configuration, select pure Z instead. Screening is an approximation; it does not model excited-state electron rearrangements. Invalid sampler parameters and nonfinite distributions are rejected with errors.
Run the screening and sampling regressions with ctest --test-dir build --output-on-failure.
From the repository root, using CMake 3.16+ and a C++17 compiler:
# Debian / Ubuntu / WSL dependencies for the viewer
sudo apt install build-essential cmake \
libglfw3-dev libglew-dev libglm-dev libgl1-mesa-dev
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failureThe test suite includes offscreen shader regressions when EGL is available; those checks are skipped if an offscreen context cannot be created.
When graphics dependencies are missing, CMake builds console mode. To explicitly check that fallback:
cmake -S . -B build-console -DCMAKE_DISABLE_FIND_PACKAGE_OpenGL=ON
cmake --build build-console
ctest --test-dir build-console --output-on-failure./build/atom_sim --visual
./build/atom_sim --consoleFor the local browser interface (Python 3.10+ and Node.js 22+):
npm --prefix web ci
npm --prefix web run build
python3 tools/serve.py --binary build/atom_apiOpen http://127.0.0.1:8000. Choose an element/state, sample its probability
cloud, orbit/zoom the preview, and step or play probability current through C++.
The server also works with a console-only native build. See the
local UI and JSON transport guide for controls, tests, units,
preview limits, and the single-active-run behavior.
Both modes prompt for the element, orbital, and screening choice. Nonspherical
orbitals also prompt for integer m. Running without an option prompts for the mode
when graphics support is compiled. --help lists the command-line options.
XYZ output is written to the current directory in atomic units (a0). Failure to
open or finish writing the file returns a nonzero exit status. Viewer startup
also returns a nonzero exit status if context creation, shader compilation, or
shader linking fails.
cmake --install build --prefix "$HOME/.local"
"$HOME/.local/bin/atom_sim" --consoleThe database searches these locations in order:
QM_DATA_DIR/elements.json, when the environment variable is set.data/elements.jsonrelative to the current directory.- The installed data directory relative to the executable (also after relocating an installation or launching through PATH).
- The installation data directory configured by CMake.
- The source tree's data directory, for development builds.
If none loads, the program prints a warning and uses its six-element emergency
table. An explicit path supplied to ElementDatabase bypasses automatic discovery.
export LIBGL_ALWAYS_SOFTWARE=1
export GALLIUM_DRIVER=llvmpipe
./build/atom_sim --visualIf graphics initialization still fails, use console mode and import the XYZ file
into a viewer that supports particle clouds. Coordinates are in a0; convert to
angstroms when needed using the factor in Constants.hpp.
atoms/
├── CMakeLists.txt
├── README.md
├── AGENTS.md
├── cmake/ # generated data-path configuration template
├── data/elements.json # entries Z = 1 … 119
├── include/ # public headers
├── src/ # implementations
├── tools/serve.py # loopback UI server and native worker bridge
├── web/ # TypeScript UI, point renderer, and browser tests
└── tests/ # physics, input, and installation regressions
The dataset includes an entry for Z = 119. Its scientific metadata and the other element records still need a provenance review.
- Fix screening, validate finite distributions, and cover the crash with tests.
- Fix C++17 portability, input parsing, coordinates, and probability flow.
- Repair documentation, installed data discovery, and export/shader errors.
- Implement CUDA sampling and flow kernels with CPU fallback and parity/benchmark runner.
- Validate CUDA compilation and CPU runtime fallback in CI.
- Validate numerical parity and performance on NVIDIA hardware.
- Match ray-march camera projection, bound rays to orbital size, and use consistent density/opacity.
- Measure representative higher-orbital convergence and software-renderer performance; tune stepping.
- Validate full-window renderer performance on hardware GPUs.
CUDA implementation lives in cuda/. QM_ENABLE_CUDA defaults to ON and detects
an available CUDA compiler; use -DQM_ENABLE_CUDA=OFF for an explicit CPU build.
Missing compilers, unavailable devices, and CUDA runtime errors retain CPU sampling
and flow. Sampling uses the same validated CPU-built CDF tables; GPU random streams
are reproducible but differ from the CPU generator. GPU flow uses the same exact
rotation about Y. The initial implementation copies results back to host memory;
it does not yet use CUDA/OpenGL interop or persistent device buffers.
Run ./build/compute_tests --cuda for numerical checks and end-to-end CPU/GPU
timings at 50k, 500k, and 1M particles. CTest marks CUDA parity skipped without a
usable device. CUDA compilation and CPU runtime fallback passed the first GitHub Actions run
in the CUDA development container. GPU measurements remain unverified in the
current development environment (no nvcc and blocked NVIDIA device access).
A single local CPU baseline for hydrogen 1s measured sampling/flow respectively:
50k: 18.6/1.0 ms; 500k: 184.8/8.1 ms; 1M: 382.1/15.8 ms.
These are indicative wall-clock measurements, not GPU speedup claims.
Later work includes scientific data provenance and native Windows build instructions.
Hydrogenic wavefunctions are exact for the nonrelativistic one-electron model. Multi-electron screening is approximate. Ray-march rendering remains a visual approximation: rays intersect the sampling sphere, use 256 midpoint steps on a grid concentrated near the nucleus, and share an orbital-wide density scale estimated from radial/angular grids. Exponential opacity includes step length and uses an orbital-size scale for visualization; displayed opacity is not a measured observable. Both rendering paths use a 45-degree vertical field of view. Offscreen tests cover ray misses, cameras inside the volume, charge scaling, camera distance, and step convergence for representative s/p/d/f states through n=7 from two viewing angles. These checks compare float images against a 4096-step numerical reference; they are not a proof of accuracy for every state, camera, or resolution. For quantitative chemistry, use a suitable Hartree–Fock or DFT package.
Run EGL_PLATFORM=surfaceless LIBGL_ALWAYS_SOFTWARE=1 ./build/shader_tests --benchmark
for optional synchronized frame timings and convergence errors. CTest runs the
image regressions without timing assertions. See renderer measurements
for the sampling method, local baseline, and remaining validation.
The intended product is an interactive quantum and particle physics simulator: users choose a supported particle or enter its defining state/parameters, then explore its modeled behavior and visual representation. A TypeScript UI is the implemented interface for the current C++ atomic-orbital model.
Coverage must grow through explicit, validated physics models. Arbitrary particle input alone cannot determine dynamics: the model also needs interactions, initial state, boundary conditions, and a regime of validity. Visuals must identify whether they show probability density, a field, detector observables, or another quantity, and disclose approximations. Elementary particles are not assigned invented shapes.
- Define supported particle categories, inputs, observables, and model validity.
- Implement a graphics-independent orbital model/backend adapter with validated requests and execution metadata.
- Choose local HTTP/native-worker transport and connect a TypeScript interactive UI.
- Add validated models beyond hydrogenic atomic orbitals incrementally.
- Add optional orbital sonification: density scan, explicit audio mapping, WAV export.
- Add normalized two-orbital superpositions and time-dependent interference visualization.
- Prepare a provisional browser demo with the same C++ physics compiled to WebAssembly.
- Extend interference to sonification and validate additional basis/state combinations.
- Explore lattice dynamics and phonons as a separate model extension.
The model scope and contract defines the first supported
model, validated inputs, units, output meanings, and required UI disclosures.
Its typed C++ request/result contract is implemented by OrbitalModel in the
qm_model CMake target, with a 1,000,000-sample limit, configurable seed, explicit
CPU or automatic CUDA/CPU selection, stable errors, and atomic-unit current
updates. Console and viewer execution share the same sampling/flow dispatch.
The local JSON API and TypeScript UI use that adapter; full clouds stay in C++
while the browser displays at most 20,000 preview samples. The UI also supports the validated two-orbital model; see
superposition physics and limits. Future models remain
unavailable until validated.
Sonification maps simulation data to audible signals; its audio is not literal sound emitted by an isolated orbital. Phonons require interacting lattice atoms, which the existing model does not simulate.
GitHub Actions runs on pull requests, pushes to main,
v* tags, and manual dispatch. It builds and tests Linux console and OpenGL modes
with CUDA disabled, requires software-EGL shader coverage, and separately compiles
the CUDA backend in a CUDA 12.6 development container. That container has no GPU:
it tests runtime CPU fallback; GPU parity/performance validation remains pending.
The CUDA compile target uses architecture 75 and is not shipped as a release binary.
Each successful Linux job uploads an installed package containing bin/atom_sim,
bin/atom_api, element data, the compiled browser UI and local server, docs,
README, and a SHA-256 checksum. Test logs are retained for 14 days. A separate
frontend job builds the pinned WebAssembly toolchain and runs TypeScript, native,
and browser-engine tests; release delivery
requires it to pass along with native jobs.
Pushing a version tag (for example v1.2.0) publishes both tested packages to
GitHub Releases only after all build/test jobs pass. Re-running the tag workflow
updates assets on its existing release. No custom credentials are required;
only the release job receives repository write permission.
Packages target Ubuntu 24.04 x86_64. The OpenGL package requires GLFW, GLEW, GLM,
and Mesa/OpenGL runtime libraries; install the viewer dependencies listed above.
Extract a package, then run ./bin/atom_sim --console or --visual.
To open the packaged UI, run
python3 share/atom_sim/tools/serve.py --binary bin/atom_api and open its local
URL. Node is not needed for packaged UI use. The provisional browser demo guide describes the static
WebAssembly deployment, browser limits, and build instructions.
The first CI run passed console tests and CUDA compilation/runtime fallback.
The OpenGL job could not acquire a GitHub-hosted runner; local software-EGL checks
provide rendering coverage, but a successful remote OpenGL run remains pending.
Branch protection can then require Linux (console), Linux (opengl),
and CUDA compile and CPU runtime fallback before merging.