# phasma
**Terminal interface for the [caustic](https://github.com/resonant-jovian/caustic) Vlasov–Poisson solver.**
[](https://crates.io/crates/phasma)
[](https://www.gnu.org/licenses/gpl-3.0)
[](https://thanks.dev/u/gh/resonant-jovian)
[](https://github.com/resonant-jovian/phasma/actions/workflows/test.yml)
[](https://github.com/resonant-jovian/phasma/actions/workflows/clippy.yml)
> [!IMPORTANT]
> Pre-0.1.0 — the interface and configuration format may change without notice. Until version 1.0.0 it should not be relied upon for production workloads.
---
### Highlights
- **10 live TUI tabs** — density heatmaps, phase-space projections (all 9 x-v marginals), energy conservation, radial profiles, HT/TT rank evolution, performance breakdown, Poisson diagnostics
- **14 execution modes** — interactive TUI, auto-run, batch (headless/HPC), playback, side-by-side comparison, live monitoring, parameter sweep, convergence study, regression testing, config wizard
- **10 physical models** — Plummer, Hernquist, King, NFW, Zel'dovich pancake, mergers, tidal streams, exponential disk, uniform perturbation, custom 6D arrays
- **7 Poisson solvers, 14 time integrators** — FFT (periodic + isolated), VGF, tensor, multigrid, spherical harmonics, Barnes-Hut tree; Strang/Yoshida/Lie splitting, unsplit RK2-4, BUG, Lawson, RKN6, and more
- **LoMaC conservation** — optional mass/momentum/energy-preserving framework
- **12 export formats** — CSV, JSON, Parquet, VTK, SVG, NumPy (.npy), Markdown report, animation frames, ZIP archive
- **26 preset configs** — ready-to-run TOML files for every model and solver combination
- **4 themes, 7 colormaps** — dark/light/solarized/gruvbox; viridis/inferno/plasma/magma/grayscale/cubehelix/coolwarm
- **Responsive layout** — adapts to terminal size (compact 40x12, normal 80x24, wide 160x50+)
- **HPC-ready** — headless batch mode with structured output for SLURM, PBS, or any job scheduler
---
## Contents
- [For Everyone](#for-everyone) — What phasma does, install, TUI basics, keyboard controls
- [For Researchers](#for-researchers) — Models, config, batch/sweep/convergence, presets, export, citation
- [For Developers](#for-developers) — CLI, modes, keyboard detail, project structure, architecture
- [Development](#development) | [License](#license)
---
## For Everyone
phasma is a [ratatui](https://ratatui.rs/)-based terminal application for running and visualizing 6D Vlasov–Poisson simulations powered by the [caustic](https://github.com/resonant-jovian/caustic) solver library. It runs entirely in the terminal — over SSH, in tmux, on headless compute nodes — with real-time density and phase-space heatmaps, energy conservation charts, radial profiles, performance dashboards, history scrubbing, and export to 12 formats.
### Install
#### From source
```bash
git clone https://github.com/resonant-jovian/phasma
cd phasma
cargo build --release
./target/release/phasma
```
#### From crates.io
```bash
cargo install phasma
```
#### Man page
```bash
phasma --generate-man > phasma.1
sudo mv phasma.1 /usr/share/man/man1/
```
#### Dependencies
- **Rust 1.85+** (edition 2024)
- **caustic** — the solver library, pulled automatically as a cargo dependency
- **ratatui** + **crossterm** — terminal rendering (no external dependencies)
### Quick start
```bash
# Launch the TUI — browse and load configs from the Setup tab
phasma
# Load a preset and start immediately
phasma --config configs/balanced.toml --run
# Headless batch mode for HPC / SLURM
phasma --config configs/nfw_high_res.toml --batch
# Verbose output — see every build step and per-step diagnostics
phasma --config configs/balanced.toml --run --verbose
# Replay a completed batch run
phasma --playback output/run_20260310_143022/
```
A minimal config:
```toml
[model]
type = "plummer"
```
> [!NOTE]
> All fields have sensible defaults. Smart defaults auto-fill boundary conditions, Poisson solver, domain extent, and t_final based on model type.
### First run
Launch phasma with no arguments to enter the interactive TUI:
```bash
phasma
```
You land on the **Setup tab** (F1). Press `Ctrl+P` to open the preset selector, pick `plummer` (a good starting point), and press `Enter` to load it. The setup tab shows a structured summary of the loaded config, including a memory estimate.
Press `Space` to start the simulation. The run starts on a background thread and all tabs update in real time.
### Navigating the TUI
Switch between tabs using `F1`-`F10` or `Tab`/`Shift+Tab`:
| Setup | F1 | Config browser, memory estimate, preset selector |
| Run Control | F2 | Progress bar, density/phase-space thumbnails, energy chart, log stream |
| Density | F3 | 2D projected density heatmap (xy/xz/yz), zoom, log scale |
| Phase Space | F4 | All 9 x_i-v_j marginal projections |
| Energy | F5 | Conservation time series: E(t), T(t), W(t), drift, mass, Casimir, entropy |
| Rank | F6 | HT/TT rank evolution, singular value spectrum |
| Profiles | F7 | Radial density, velocity dispersion, enclosed mass, circular velocity |
| Performance | F8 | Step timing breakdown, timestep chart, memory usage |
| Poisson | F9 | Residual, potential power spectrum |
| Settings | F10 | Theme and colormap selector |
Use `Left`/`Right` arrows to scrub through simulation history. Press `Backspace` to jump back to live. Press `?` for the help overlay, `q` to quit.
### Keyboard controls
| `F1`–`F10` | Switch tabs |
| `Tab` / `Shift+Tab` | Next / previous tab |
| `Space` | Pause / resume simulation |
| `Left` / `Right` | Scrub backward / forward through history |
| `Backspace` | Jump to live (exit scrub mode) |
| `?` | Toggle help overlay |
| `e` | Open export menu |
| `T` | Cycle theme |
| `C` | Cycle colormap |
| `Ctrl+S` | Save current config to TOML file |
| `Ctrl+O` | Load config (jump to Setup tab) |
| `/` | Jump-to-time dialog |
| `:` | Command palette |
| `a` | Add annotation at current time |
| `Ctrl+B` | Toggle bookmark panel |
| `q` | Quit (with confirmation if sim is running) |
> [!TIP]
> **Writing TOML configs?** See [For Researchers](#for-researchers) for the full config reference, models, and presets.
> **Building on phasma?** See [For Developers](#for-developers) for the project structure, CLI reference, and architecture.
---
## For Researchers
### Supported models
| Plummer | `plummer` | Isotropic sphere with analytic DF, $f(E)$ |
| Hernquist | `hernquist` | Galaxy model with closed-form DF |
| King | `king` | Tidally truncated (Poisson-Boltzmann ODE + RK4) |
| NFW | `nfw` | Dark matter halo (numerical Eddington inversion) |
| Zel'dovich | `zeldovich` | Single-mode cosmological pancake |
| Merger | `merger` | Two-body superposition of equilibrium ICs |
| Tidal | `tidal` | Progenitor in external host potential |
| Disk | `disk_exponential` / `disk_stability` | Exponential disk with Shu DF and Toomre $Q$ |
| Uniform perturbation | `uniform_perturbation` | Perturbed Maxwellian (Jeans instability) |
| Custom file | `custom_file` | User-provided 6D .npy array |
### Your first config
Start from a preset and modify it. Create a file `my_sim.toml`:
```toml
[model]
type = "plummer"
total_mass = 1.0
scale_radius = 1.0
[domain]
spatial_extent = 10.0
velocity_extent = 3.0
spatial_resolution = 16
velocity_resolution = 16
[solver]
poisson = "fft_isolated"
integrator = "yoshida"
[time]
t_final = 15.0
cfl_factor = 0.35
```
The three most important sections:
- **`[model]`** — what you are simulating (type, mass, scale radius)
- **`[domain]`** — the 6D box (spatial/velocity extent and resolution, boundary conditions)
- **`[solver]`** — numerical methods (Poisson solver, time integrator, representation)
The 26 preset files in `configs/` are good templates — copy one and adjust.
### Config file reference
All sections and fields are optional — sensible defaults are provided. The full config has 10 top-level sections.
#### `[domain]` — Simulation domain
| `spatial_extent` | float | `10.0` | Half-width of the spatial box. Domain spans $[-L, L]^3$. |
| `velocity_extent` | float | `5.0` | Half-width of the velocity box. Domain spans $[-V, V]^3$. |
| `spatial_resolution` | integer | `8` | Grid cells per spatial dimension. Must be power-of-2 for FFT solvers. |
| `velocity_resolution` | integer | `8` | Grid cells per velocity dimension. Must be power-of-2 for FFT solvers. |
| `boundary` | string | `"periodic\|truncated"` | Spatial BC \| velocity BC. Options: `periodic`, `isolated`, `reflecting` \| `truncated`, `open` |
| `coordinates` | string | `"cartesian"` | Coordinate system |
| `gravitational_constant` | float | `1.0` | Value of G |
> [!TIP]
> Memory: $N_x^3 \times N_v^3 \times 8$ bytes. A $16^3 \times 16^3$ grid = 128 MB. A $32^3 \times 32^3$ grid = 8 GB.
```toml
[domain]
spatial_extent = 10.0
velocity_extent = 3.0
spatial_resolution = 16
velocity_resolution = 16
```
#### `[model]` — Initial conditions
| `type` | string | `"plummer"` | IC model type |
| `total_mass` | float | `1.0` | Total system mass |
| `scale_radius` | float | `1.0` | Characteristic scale radius |
**`[model.king]`** — Tidally truncated King model
| `w0` | yes | Dimensionless central potential $W_0$ (typical 3.0–9.0) |
**`[model.nfw]`** — NFW dark matter halo
| `concentration` | yes | $c = r_{\mathrm{vir}} / r_s$ (typical 5–20) |
| `virial_mass` | no | Virial mass (default: total_mass) |
| `velocity_anisotropy` | no | `"isotropic"` or beta value |
**`[model.zeldovich]`** — Zel'dovich pancake
| `amplitude` | yes | Perturbation amplitude (0.1–1.0) |
| `wave_number` | yes | Mode wave number |
**`[model.merger]`** — Two-body merger
| `separation` | yes | Initial separation distance |
| `mass_ratio` | yes | m2/m1 (1.0 = equal mass) |
| `relative_velocity` | no | Relative velocity (default: 0) |
| `impact_parameter` | no | Impact parameter (default: 0) |
| `scale_radius_1` | no | Scale radius of body 1 |
| `scale_radius_2` | no | Scale radius of body 2 |
**`[model.tidal]`** — Tidal stream
| `progenitor_type` | yes | `"plummer"`, `"hernquist"`, `"king"`, `"nfw"` |
| `progenitor_mass` | yes | Progenitor mass |
| `progenitor_scale_radius` | yes | Progenitor scale radius |
| `progenitor_position` | yes | [x, y, z] position |
| `progenitor_velocity` | yes | [vx, vy, vz] velocity |
| `host_type` | yes | `"point_mass"`, `"nfw_fixed"`, `"logarithmic"` |
| `host_mass` | yes | Host mass |
| `host_scale_radius` | yes | Host scale radius |
**`[model.uniform_perturbation]`** — Perturbed uniform Maxwellian
| `background_density` | yes | Mean density |
| `velocity_dispersion` | yes | Thermal velocity dispersion |
| `perturbation_amplitude` | yes | Perturbation amplitude |
| `perturbation_wavenumber` | yes | [kx, ky, kz] wave vector |
**`[model.disk]`** — Exponential disk
| `disk_mass` | no | Disk mass |
| `disk_scale_length` | no | Scale length |
| `radial_velocity_dispersion` | no | Radial velocity dispersion |
**`[model.custom_file]`** — Custom 6D array
| `file_path` | yes | Path to .npy file |
| `format` | yes | `"npy"` |
#### `[solver]` — Numerical methods
| `representation` | string | `"uniform"` | Phase-space representation |
| `poisson` | string | `"fft_periodic"` | Poisson solver |
| `advection` | string | `"semi_lagrangian"` | Advection scheme |
| `integrator` | string | `"strang"` | Time integrator |
| `conservation` | string | `"none"` | Conservation scheme |
**Phase-space representations**
| `uniform` / `uniform_grid` | Full 6D grid | $O(N^6)$ |
| `hierarchical_tucker` / `ht` | HT tensor decomposition | $O(N^3 r^3)$ |
| `tensor_train` | Tensor-train decomposition | $O(Nr^2 d)$ |
| `sheet_tracker` | Lagrangian sheet tracker | $O(N^3)$ |
| `spectral` / `velocity_ht` | Hermite velocity basis | $O(N^3 M)$ |
| `amr` | Adaptive mesh refinement | varies |
| `hybrid` | Sheet/grid hybrid | varies |
**Poisson solvers**
| `fft_periodic` / `fft` | FFT periodic | periodic |
| `fft_isolated` | Hockney-Eastwood zero-padded FFT *(deprecated)* | isolated |
| `vgf` / `vgf_isolated` | Vico-Greengard-Ferrando spectral isolated | isolated |
| `tensor` / `tensor_poisson` | Braess-Hackbusch exponential sum | isolated |
| `multigrid` | V-cycle multigrid (red-black GS) | isolated |
| `spherical` / `spherical_harmonics` | Legendre decomposition + radial ODE | spherical |
| `tree` / `barnes_hut` | Barnes-Hut octree | isolated |
**Time integrators**
| `strang` | 2nd | 3 | Strang operator splitting (symplectic) |
| `yoshida` | 4th | 7 | Yoshida splitting — best conservation |
| `lie` | 1st | 2 | Lie splitting (simplest, least accurate) |
| `unsplit` / `unsplit_rk4` | 4th | — | Unsplit method-of-lines RK4 |
| `unsplit_rk2` | 2nd | — | Unsplit RK2 |
| `unsplit_rk3` | 3rd | — | Unsplit RK3 |
| `rkei` | 3rd | 3 | SSP-RK3 exponential integrator (unsplit) |
| `adaptive` / `adaptive_strang` | 2nd | 3 | Strang with adaptive timestep control |
| `blanes_moan` / `bm4` | 4th | — | Blanes-Moan optimized splitting |
| `rkn6` | 6th | — | 6th-order Runge-Kutta-Nystrom splitting |
| `bug` | varies | — | Basis Update & Galerkin (BUG) for HT tensors |
| `rk_bug` / `rk_bug3` | varies | — | Runge-Kutta BUG variant |
| `parallel_bug` / `pbug` | varies | — | Parallelized BUG |
| `lawson` / `lawson_rk4` | varies | — | Lawson Runge-Kutta exponential integrator |
**HT/TT solver options `[solver.ht]`**
| `max_rank` | integer | `100` | Maximum rank per node |
| `initial_rank` | integer | `16` | Initial rank (spectral modes) |
| `tolerance` | float | `1e-6` | HSVD truncation tolerance |
```toml
[solver]
representation = "uniform"
poisson = "fft_isolated"
advection = "semi_lagrangian"
integrator = "yoshida"
conservation = "none" # or "lomac" for mass/momentum/energy conservation
```
#### `[time]` — Time stepping
| `t_final` | float | `10.0` | Simulation end time |
| `dt_mode` | string | `"adaptive"` | `"adaptive"` or `"fixed"` |
| `dt_fixed` | float | `0.1` | Fixed timestep |
| `cfl_factor` | float | `0.5` | CFL safety factor (0, 1] |
| `dt_min` | float | `1e-6` | Minimum timestep (adaptive) |
| `dt_max` | float | `1.0` | Maximum timestep (adaptive) |
#### `[output]` — Output settings
| `directory` | string | `"output"` | Base output directory |
| `prefix` | string | `"run"` | Subdirectory prefix |
| `snapshot_interval` | float | `1.0` | Time between snapshot saves |
| `checkpoint_interval` | float | `10.0` | Time between checkpoints |
| `diagnostics_interval` | float | `0.1` | Time between diagnostics rows |
| `format` | string | `"binary"` | Snapshot format |
#### `[exit]` — Termination conditions
> [!IMPORTANT]
> The simulation exits when **any** enabled condition triggers.
| `energy_drift_tolerance` | float | `0.5` | Max $\lvert \Delta E / E_0 \rvert$ |
| `mass_drift_tolerance` | float | `0.1` | Max $\lvert \Delta M / M_0 \rvert$ |
| `virial_equilibrium` | bool | `false` | Exit when virial ratio stabilizes |
| `virial_tolerance` | float | `0.05` | Virial equilibrium tolerance |
| `wall_clock_limit` | float | none | Max seconds |
| `cfl_violation` | bool | `true` | Exit on CFL violation |
| `steady_state` | bool | `false` | Exit on steady state |
| `steady_state_tolerance` | float | `1e-6` | Steady state threshold |
| `casimir_drift_tolerance` | float | `0.0` | Max Casimir drift (0 = disabled) |
| `caustic_formation` | bool | `false` | Exit when first caustic forms |
#### `[performance]` — Performance tuning
| `num_threads` | integer | `0` | Rayon threads (0 = all available) |
| `memory_budget_gb` | float | `4.0` | Memory budget for validation warnings |
| `simd` | bool | `true` | Enable SIMD |
| `allocator` | string | `"system"` | `"system"`, `"jemalloc"`, `"mimalloc"` |
#### `[playback]` — Playback settings
| `source_directory` | string | none | Snapshot directory path |
| `fps` | float | `10.0` | Playback frames per second |
| `loop_playback` | bool | `false` | Loop at end |
#### `[logging]` — Logging
| `level` | string | `"info"` | Log level (`"trace"`, `"debug"`, `"info"`, `"warn"`, `"error"`) |
| `file` | string | none | Log file path |
| `structured` | bool | `false` | Structured JSON logging |
#### `[appearance]` — TUI appearance
| `theme` | string | `"dark"` | `"dark"`, `"light"`, `"solarized"`, `"gruvbox"` |
| `colormap_default` | string | `"viridis"` | `"viridis"`, `"inferno"`, `"plasma"`, `"magma"`, `"grayscale"`, `"cubehelix"`, `"coolwarm"` |
| `braille_density` | bool | `true` | Braille-based density rendering |
| `border_style` | string | `"rounded"` | `"rounded"`, `"plain"`, `"double"` |
| `square_pixels` | bool | `true` | Compensate for non-square terminal cells in all heatmaps (F2, F3, F4) |
| `min_columns` | integer | `80` | Minimum terminal width |
| `min_rows` | integer | `24` | Minimum terminal height |
### Batch simulations
For long or headless runs, use `--batch`:
```bash
phasma --config my_sim.toml --batch
```
This runs without a TUI and writes structured output:
```
output/<prefix>_YYYYMMDD_HHMMSS/
config.toml -- copy of input config
diagnostics.csv -- time series (appended each step)
snapshots/
state_000000.json -- periodic SimState snapshots
state_000001.json
...
state_final.json -- last state
metadata.json -- version, timing, exit reason, snapshot count
```
Replay a batch run in the TUI:
```bash
phasma --playback output/run_20260310_143022/
```
### Parameter sweeps and convergence studies
**Sweep** — run every combination in a Cartesian product of parameter values:
```toml
base_config = "configs/balanced.toml"
output_dir = "output/sweep"
[sweep]
parameters = ["domain.spatial_resolution", "solver.integrator"]
[sweep.values]
"domain.spatial_resolution" = [8, 16, 32]
"solver.integrator" = ["strang", "yoshida"]
```
```bash
phasma --sweep sweep.toml
```
**Convergence study** — run at increasing resolutions and compute convergence rates:
```toml
base_config = "configs/balanced.toml"
output_dir = "output/convergence"
[convergence]
resolutions = [8, 16, 32, 64]
velocity_scale = true
metrics = ["energy_drift", "mass_drift"]
```
```bash
phasma --convergence convergence.toml
```
Convergence rates are computed as $\log_2(\epsilon_N / \epsilon_{2N})$.
### Monitoring and comparison
Watch a running batch job live:
```bash
phasma --monitor output/run_20260310_143022/ # manual refresh
phasma --tail output/run_20260310_143022/ # auto-advances to latest
```
Compare two completed runs side-by-side:
```bash
phasma --compare output/run_A/ output/run_B/
```
Generate a markdown comparison report across multiple runs:
```bash
phasma --batch-compare output/run_A/ output/run_B/ output/run_C/ --report comparison.md
```
### Preset configurations
phasma ships with 26 preset TOML configurations in `configs/`:
#### Plummer sphere variants
| `plummer` | $16^3 \times 16^3$ | Strang | Default Plummer starting point |
| `plummer_64` | $16^3 \times 16^3$ | Strang | 64-cell spatial grid variant |
| `plummer_128` | $16^3 \times 16^3$ | Strang | 128-cell spatial grid variant |
| `plummer_hires` | $32^3 \times 32^3$ | Yoshida | High-resolution (~8 GB) |
| `plummer_yoshida` | $16^3 \times 16^3$ | Yoshida | 4th-order integrator comparison |
| `plummer_unsplit` | $16^3 \times 16^3$ | Unsplit RK4 | Method-of-lines integrator |
#### Advanced representations
| `plummer_ht` | Plummer | HT tensor | Hierarchical Tucker compressed |
| `plummer_tt` | Plummer | TT decomposition | Tensor-train representation |
| `plummer_spectral` | Plummer | Spectral velocity | Hermite velocity basis |
| `plummer_lomac` | Plummer | LoMaC conservation | Mass/momentum/energy preserving |
#### Alternative Poisson solvers
| `plummer_tensor_poisson` | Plummer | Exp-sum tensor | Braess-Hackbusch isolated |
| `plummer_multigrid` | Plummer | V-cycle multigrid | Red-black Gauss-Seidel |
| `plummer_spherical` | Plummer | Spherical harmonics | Legendre + radial ODE |
| `nfw_tree` | NFW | Barnes-Hut tree | Octree gravity |
#### Other equilibrium models
| `hernquist` | Hernquist | Galaxy model |
| `king` | King ($W_0=6$) | Tidally truncated equilibrium |
| `nfw` | NFW ($c=10$) | Dark matter halo |
#### Multi-body and cosmological
| `merger_equal` | 2x Plummer (equal mass) | Head-on collision |
| `merger_unequal` | 2x Plummer (3:1) | Unequal mass ratio |
| `zeldovich` | Zel'dovich | Caustic formation |
| `disk_bar` | Exponential disk | Disk stability (Toomre $Q$) |
| `tidal_point` | Tidal Plummer | Point-mass host stream generation |
| `tidal_nfw` | Tidal + NFW host | NFW host potential |
#### Stability and testing
| `jeans_unstable` | Gravitational instability growth rate |
| `jeans_stable` | Stable mode (should not grow) |
| `debug` | Minimal 4^3 x 4^3 grid for debugging |
### Export formats
Press `e` to open the export menu:
| `1` | SVG screenshot | Current view as vector graphics |
| `2` | CSV time series | Diagnostics history |
| `3` | JSON time series | Diagnostics history |
| `4` | Parquet time series | Columnar diagnostics |
| `5` | TOML config | Current config |
| `6` | VTK snapshot | ParaView-compatible density |
| `7` | NumPy .npy | Raw density array |
| `8` | Markdown report | Full simulation report |
| `9` | Frame sequence | Animation frames |
| `0` | CSV radial profiles | Radial density/dispersion |
| `a` | Parquet performance | Step timing data |
| `z` | ZIP archive | Everything: config, diagnostics, snapshots, scripts |
### Companion: caustic
[caustic](https://github.com/resonant-jovian/caustic) is the solver library that powers phasma. It provides the 6D Vlasov–Poisson simulation engine, phase-space representations, Poisson solvers, time integrators, IC generators, diagnostics, and the LoMaC conservation framework. phasma contains no solver logic — it constructs a `caustic::Simulation` from config and renders live diagnostics.
### Citation
```bibtex
@software{phasma,
title = {phasma: Terminal interface for the caustic Vlasov-Poisson solver},
url = {https://github.com/resonant-jovian/phasma},
year = {2026}
}
```
---
## For Developers
### Modes of operation
```mermaid
%%{init: {'theme': 'neutral'}}%%
graph TD
CLI[CLI] --> TUI & Run & Batch & Playback & Compare & Monitor & Sweep & Conv & Wizard & Reg & BC
TUI[Interactive TUI]
Run[--run]
Batch[--batch]
Playback[--playback]
Compare[--compare]
Monitor[--monitor / --tail]
Sweep[--sweep]
Conv[--convergence]
Wizard[--wizard]
Reg[--regression-test]
BC[--batch-compare]
```
| Interactive TUI | (default) | Browse configs, run, monitor live |
| Auto-run | `--run` | Load config and start sim immediately |
| Batch | `--batch` | Headless, saves to disk, suitable for HPC |
| Playback | `--playback DIR` | Replay saved snapshots with scrubbing |
| Comparison | `--compare DIR DIR` | Side-by-side two-run comparison |
| Monitor | `--monitor DIR` | Watch a running batch job live |
| Tail | `--tail DIR` | Like monitor, auto-advances |
| Wizard | `--wizard` | Guided interactive config generation |
| Sweep | `--sweep TOML` | Parameter sweep (Cartesian product) |
| Convergence | `--convergence TOML` | Resolution convergence study |
| Regression | `--regression-test DIR` | CI-compatible regression test (exit 0/1) |
| Batch compare | `--batch-compare DIR...` | Markdown comparison report |
### CLI reference
| `-c`, `--config` | `PATH` | Path to simulation config file (TOML) |
| `--run` | — | Start simulation immediately |
| `--batch` | — | Headless batch mode |
| `-v`, `--verbose` | — | Detailed logging of every build step and per-step diagnostics |
| `--playback` | `DIR` | Replay saved snapshots in TUI |
| `--compare` | `DIR DIR` | Side-by-side comparison of two runs |
| `--sweep` | `TOML` | Parameter sweep |
| `--convergence` | `TOML` | Convergence study |
| `--regression-test` | `DIR` | Regression test (exit 0 on pass, 1 on fail) |
| `--monitor` | `DIR` | Watch a batch job's output directory |
| `--tail` | `DIR` | Like `--monitor`, auto-advances to latest |
| `--wizard` | — | Interactive config wizard |
| `--save-preset` | `NAME` | Save loaded config as a named preset |
| `--batch-compare` | `DIR DIR [...]` | Markdown comparison report across runs |
| `--report` | `PATH` | Output path for `--batch-compare` (default: `comparison_report.md`) |
| `--generate-man` | — | Print roff man page to stdout |
### Verbose mode
`--verbose` / `-v` enables detailed logging during simulation startup and stepping:
```
[verbose] Loading config from: configs/balanced.toml
[verbose] Config loaded: model=plummer, repr=uniform, poisson=fft_isolated, integrator=yoshida
[verbose] Domain: spatial_extent=10, velocity_extent=3, N_x=16, N_v=16
[verbose] Phase-space grid: 16^3 x 16^3 = 16777216 cells (128.0 MB)
[verbose] Domain built in 0.3 ms
[verbose] Building IC: model=plummer, M=1, a=1
[verbose] IC sampled in 1842.1 ms — 8388608 non-zero cells out of 16777216
[verbose] Building phase-space representation: uniform
[verbose] Representation built in 0.0 ms
[verbose] Building Poisson solver: fft_isolated
[verbose] Poisson solver built in 12.4 ms
[verbose] Building integrator: yoshida
[verbose] Assembling simulation: G=1, t_final=15, cfl=0.35, conservation=none
[verbose] Simulation assembled in 45.3 ms
[verbose] Exit conditions wired: 3 active (energy_drift_tol=0.5, mass_drift_tol=0.1)
[verbose] Build complete in 1902.8 ms
```
In TUI mode these messages appear in the F2 Run Control tab log panel.
### Keyboard controls (per-tab)
#### Run Control (F2)
| `p` / `Space` | Pause / resume |
| `s` | Stop simulation |
| `r` | Restart simulation |
| `1`–`3` | Log filter: all / warn+ / error only |
#### Density (F3)
| `x` / `y` / `z` | Change projection axis |
| `+` / `-` / scroll | Zoom in / out |
| `r` | Reset zoom |
| `l` | Toggle log scale |
| `c` | Cycle colormap |
| `i` | Toggle info bar |
#### Phase Space (F4)
| `1`–`3` | Select spatial dimension (x, y, z) |
| `4`–`6` | Select velocity dimension (vx, vy, vz) |
| `+` / `-` / scroll | Zoom in / out |
| `r` / `0` | Reset zoom |
| `l` | Toggle log scale |
| `c` | Cycle colormap / comparison view |
| `p` | Toggle physical aspect ratio (default on) |
| `s` | Toggle stream-count overlay |
| `i` | Toggle info bar |
#### Energy (F5)
| `t` / `k` / `w` | Toggle traces: total / kinetic / potential |
| `d` | Toggle drift view |
| `1`–`4` | Select panel: energy, mass, Casimir, entropy |
#### Profiles (F7)
| `1`–`5` | Select profile: density, dispersion, mass, v_circ, anisotropy |
| `l` | Toggle log scale |
| `a` | Toggle analytic overlay |
| `b` | Adjust bin count |
#### Playback
| `[` / `]` | Step backward / forward one frame |
| `{` / `}` | Jump 10 frames |
| `Home` / `End` | Jump to start / end |
| `<` / `>` | Decrease / increase playback speed |
### Batch output layout
```
output/<prefix>_YYYYMMDD_HHMMSS/
config.toml -- copy of input config
diagnostics.csv -- time series (appended each step)
snapshots/
state_000000.json -- periodic SimState snapshots
state_000001.json
...
state_final.json -- last state
metadata.json -- version, timing, exit reason, snapshot count
```
This directory is the input for `--playback`, `--monitor`, `--compare`, `--regression-test`, and `--batch-compare`.
### Layout modes
phasma adapts to terminal size:
| Compact | 40x12 – 79x23 | Single-panel tabs, abbreviated labels |
| Normal | 80x24 – 159x49 | Standard layout |
| Wide | 160x50+ | Three-column panels where applicable |
Panels below minimum size show a "(too small)" placeholder.
### Project structure
```
phasma/
├── Cargo.toml
├── configs/ # 26 preset TOML configurations
│ ├── balanced.toml
│ ├── default.toml
│ ├── nfw_high_res.toml
│ ├── ...
│ └── debug.toml
└── src/
├── main.rs # Entry point, mode dispatch
├── sim.rs # caustic integration, verbose logging, SimState
├── config/
│ ├── mod.rs # PhasmaConfig schema (serde, all sections)
│ ├── defaults.rs # Memory estimation, smart defaults
│ ├── presets.rs # Preset save/load
│ ├── validate.rs # Config validation
│ └── history.rs # Recent config tracking
├── runner/
│ ├── batch.rs # Headless batch runner
│ ├── wizard.rs # Interactive config wizard
│ ├── sweep.rs # Parameter sweep
│ ├── convergence.rs # Convergence study
│ ├── compare.rs # Batch comparison report
│ ├── regression.rs # Regression testing
│ └── monitor.rs # Filesystem watcher for --monitor/--tail
├── data/
│ ├── live.rs # Live data provider (ring buffer, scrub history)
│ ├── playback.rs # Playback data provider
│ └── comparison.rs # Comparison data provider (A/B/diff)
├── tui/
│ ├── app.rs # Application state machine, global keybindings
│ ├── cli.rs # CLI argument definitions (clap)
│ ├── tabs/ # 10 tab implementations
│ ├── widgets/ # Reusable widgets (heatmap, colorbar, sparkline table)
│ ├── status_bar.rs # Bottom bar (ETA, throughput, RSS memory, rank)
│ ├── help.rs # Help overlay (?-key)
│ ├── export_menu.rs # 12-item export format selector
│ ├── command_palette.rs # Command palette (:)
│ ├── layout.rs # Responsive layout (compact/normal/wide)
│ └── guard.rs # Terminal size guard
├── export/ # Export format implementations
├── colormaps/ # Terminal colormaps (viridis, inferno, plasma, etc.)
├── themes.rs # Color themes (dark, light, solarized, gruvbox)
├── annotations.rs # Time annotations and bookmarks
├── notifications.rs # Desktop notifications on sim events
└── session.rs # Session state persistence
```
### Relationship to caustic
```mermaid
%%{init: {'theme': 'neutral'}}%%
graph TD
Config[TOML Config] --> Bridge[build_from_config]
Bridge --> Sim[caustic::Simulation]
Sim --> BG[Background Thread]
BG --> DP[DataProvider]
DP --> TUI[TUI] & BatchOut[Batch Output]
TUI --> Export[Export]
```
phasma is a **consumer** of the caustic library. It provides no solver logic — it constructs a `caustic::Simulation` from user input, runs it on a background thread, and renders live diagnostics.
| 6D Vlasov-Poisson simulation engine | ratatui TUI with 10 live tabs |
| 8 phase-space representations | TOML config loading with 26 presets |
| 10 Poisson solvers | Real-time density/phase-space heatmaps |
| 14 time integrators | Energy conservation charts and radial profiles |
| 10 IC generators | History scrubbing, playback, comparison |
| LoMaC conservation framework | Batch mode, sweeps, convergence studies |
| Diagnostics and exit conditions | Export (CSV, JSON, NPY, Parquet, VTK, ZIP) |
| rayon parallelism | Verbose logging (`--verbose`) |
To use caustic directly in your own application:
```rust
use caustic::*;
let domain = Domain::builder()
.spatial_extent(10.0)
.velocity_extent(3.0)
.spatial_resolution(16)
.velocity_resolution(16)
.t_final(10.0)
.build()?;
let ic = PlummerIC::new(1.0, 1.0, 1.0);
let snap = sample_on_grid(&ic, &domain);
let mut sim = Simulation::builder()
.domain(domain)
.poisson_solver(VgfPoisson::new(&domain))
.advector(SemiLagrangian::new())
.integrator(YoshidaSplitting::new(1.0))
.initial_conditions(snap)
.time_final(10.0)
.build()?;
while let Ok(None) = sim.step() {
println!("t={:.4}, E={:.6}", sim.time, sim.diagnostics.history.last().unwrap().total_energy);
}
```
> [!NOTE]
> **Code quality**: `warnings = "forbid"` — all rustc warnings are hard errors. `clippy::unwrap_used`, `expect_used`, `panic` = `"deny"` in non-test code.
---
## Development
`dev.sh` provides unified commands for testing, benchmarking, and profiling:
```bash
./dev.sh doctor # check all prerequisites, show install commands
./dev.sh test # run all tests (debug, parallel)
./dev.sh test --release # run in release mode
./dev.sh test --all # test both phasma and caustic
./dev.sh bench # run caustic benchmarks (from here)
./dev.sh profile flamegraph # generate flamegraph SVG
./dev.sh profile tracy # build with Tracy, run
./dev.sh profile dhat # heap profiling
./dev.sh profile samply # samply record (browser UI)
./dev.sh build --fast # fast-release profile build
./dev.sh build --profiling # release + debug symbols
./dev.sh lint # clippy + fmt --check
./dev.sh info # show project info, features, profiles
```
<details>
<summary>Prerequisites</summary>
```bash
# Cargo tools
cargo install flamegraph samply
# System packages (Arch Linux)
sudo pacman -S --needed perf valgrind heaptrack kcachegrind massif-visualizer
# Optional: Tracy profiler (AUR)
yay -S tracy
```
**Ubuntu / Debian:**
```bash
cargo install flamegraph samply
sudo apt install linux-tools-common linux-tools-$(uname -r) valgrind heaptrack kcachegrind massif-visualizer
```
Run `./dev.sh doctor` to check which tools are installed.
</details>
---
## Minimum supported Rust version
Rust edition 2024, targeting **stable Rust 1.85+**.
## Support
If phasma is useful to your research or projects, consider supporting development via [thanks.dev](https://thanks.dev/u/gh/resonant-jovian).
## License
GNU General Public License v3.0. See [LICENSE](LICENSE).