Architecture
The whole solver on one board: every layer, every kernel, every byte that moves.
How to read the board
Every element is one of five things, and the color tells you which: computation (orange) burns FLOPs, data movement (blue) burns memory bandwidth, memory (magenta) is long-lived state, interfaces (green) are the boundaries where config enters and results leave, and control (gray, dashed) orchestrates without touching bulk data. Click any element for its details, real file path, and cost; press Run to watch a measured solve propagate through the entire system.
The distinction between orange and blue is the whole point. On modern hardware this solver is memory-bandwidth-bound, not compute-bound: the design decisions worth studying are the blue ones.
Three punchlines
- Every GEMM crosses a precision boundary. Bulk storage can be
f32, but every Gram matrix, projection, and eigensolve accumulates inf64. The upcast materializes an N×r copy before each GEMM, and the results are cast back after. Switch the board to the Precision view to see exactly where the casts live. - Parallelism at job granularity, and deliberately OFF inside the GEMM. Sweep jobs run
data-parallel under rayon, while inside one solve the dense algebra runs sequentially
(
Par::Seq): at 64×64 grids, thread synchronization made parallel GEMM 5 to 10 times slower. Knowing where parallelism does NOT pay is the harder half of the discipline. - The operator is not a matrix. Applying the TE Maxwell operator IS six FFTs plus a real-space tensor contraction, working in preallocated scratch that stays hot in L2. No N²-sized object ever exists.
Deep dives
Each layer of the board has its own page:
- Geometry & Dielectric: typed lattices, analytic subpixel smoothing, and the O(N) ε arrays everything else consumes.
- Matrix-Free Operators: the TM 2-FFT and TE 6-FFT apply pipelines, scratch reuse, preconditioners.
- Eigensolver (LOBPCG): the iteration ring, cached blocks, lazy residual norms, SVQB, and the A·Q reuse trick.
- Precision & Memory: the f32/f64 contract, what the casts cost, and why this is a bandwidth story.
- Parallelism & Drivers: jobs-not-kernels, adaptive threading, the sequential k-loop, band tracking.
- Interfaces: CLI, streaming Python (GIL released), and the WebAssembly build this site runs.
Codebase map
| Layer | Crate / module | Size | Source |
|---|---|---|---|
| Storage & precision | core::field, core::backend | traits + Field2D<R> | field.rs |
| FFT backend | backend-cpu | 1.1k LOC | lib.rs |
| Geometry | core::{lattice, crystal, brillouin} | typed lattices, k-paths | lattice.rs |
| Physics | core::{dielectric, operators} | ε build + matrix-free Θ | maxwell.rs |
| Solver | core::eigensolver | LOBPCG, SVQB, deflation | eigensolver.rs |
| Drivers | core::drivers, core::band_tracking | k-loop, warm starts | bandstructure.rs |
| Orchestration | bulk-driver (+-core) | 8.3k LOC | driver.rs |
| Python | python (PyO3) | 3.4k LOC | streaming.rs |
| Browser | backend-wasm | 1.2k LOC | streaming.rs |
The whole workspace is about 42k lines of Rust across 8 crates.