GPU-accelerated proton radiography
Project description
GPU-accelerated forward modelling of proton radiographs from magnetised plasma.
Runs the full relativistic Boris orbit through measured or simulated electromagnetic fields — not a paraxial approximation — and produces synthetic radiographs in detector/film coordinates for comparison with experimental RCF data, subject to detector-response and experimental-geometry modelling.
✓ 12/12 physics validation tests passing
✓ Reproducible, self-documenting run directories
✓ CLI + GUI workflows
✓ Python API (pip install prad)
✓ Re-render without re-tracing particles
Tested platforms
| Platform | GPU | Backend | Status |
|---|---|---|---|
| macOS Apple Silicon | Apple M4 | MoltenVK/Vulkan | Validation suite passing; ~9 B steps/s peak |
| Ubuntu 22.04 Linux | NVIDIA RTX 4090 | NVIDIA Vulkan 1.3.277 | Validation suite passing; ~34 B steps/s peak |
Example radiographs
Three MHD instability geometries, computed in seconds on a laptop GPU:
| z-pinch | kink instability | sausage instability |
|---|---|---|
Each image is a synthetic proton radiograph — the spatial structure directly reflects the path-integrated field topology.
GUI
Deck parameters, run status, and the 3D radiograph — all in one view.
Why this tool
Proton radiography is sensitive to the path-integrated field, not just its peak value. The mapping from field structure to film pattern is nonlinear and depends on geometry — magnification, detector distance, source divergence. Paraxial approximations fail in the strong-field, large-deflection regimes common in modern pulsed-power experiments.
This tool runs the full relativistic Boris orbit, so you can:
- See where paraxial approximations break down and by how much
- Forward-model field topologies and compare directly to experimental films
- Design detector geometry before committing to a shot
Prerequisites
| Dependency | Purpose | Install |
|---|---|---|
| Rust (stable) | Build the engine | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
glslangValidator |
GLSL → SPIR-V shader compilation | brew install glslang |
| MoltenVK (macOS) | Vulkan over Metal | brew install molten-vk |
| Python 3.9+ | Validation suite, Python API | system or pyenv |
Quick start
# Build (also compiles shaders via build.rs)
cd rust && cargo build --release && cd ..
# Scaffold a working deck from a preset
./rust/target/release/proton_tracer init zpinch -o my_run.toml
# Inspect resolved geometry before running
./rust/target/release/proton_tracer explain my_run.toml
# Schema check
./rust/target/release/proton_tracer validate my_run.toml
# Run — produces a self-contained output directory
./rust/target/release/proton_tracer run my_run.toml -o runs/zpinch_01
macOS / MoltenVK — set these before running:
export VK_ICD_FILENAMES=/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json
export DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH
See docs/quickstart.md for the full install walkthrough.
Python API
prad is a Python wrapper around the engine. Install it with pip:
pip install prad
Run a simulation and get results as numpy arrays in three lines:
import prad
result = prad.run(
"data/zpinch.bfld",
energy_MeV=14.7,
n_particles=200_000,
source_distance_mm=80.0,
detector_distance_mm=100.0,
)
counts = result.raw_counts # numpy uint32, 1024×1024
print(result.diagnostics) # hit_fraction, n_hits, …
result.show() # display inline (Jupyter / matplotlib)
You can also pass a field constructed from a numpy array directly:
import numpy as np, prad
B = np.zeros((64, 64, 64, 3), dtype=np.float32)
B[:, :, :, 2] = 5.0 # 5 T uniform Bz
field = prad.Field.from_array(B, bounds_m=(-0.05, 0.05, -0.05, 0.05, -0.05, 0.05))
result = prad.run(field, n_particles=100_000)
See docs/python_api.md for the full API reference.
Subcommands
| Command | Purpose |
|---|---|
run <deck> [-o dir] |
Batch run — GPU compute, full run directory output |
gui [deck] |
Interactive launcher with live progress |
explain <deck> |
Print resolved geometry and step budget — no GPU |
validate <deck> |
Schema check only — no GPU |
init [preset] [-o deck.toml] |
Emit a starter deck (blank / zpinch / kink-strong) |
demo [preset] |
Run a built-in preset without writing a deck |
render <run_dir> |
Re-render radiograph from saved counts — no GPU |
sweep <deck> --param k=v1,v2 |
Parameter sweep — one run directory per point |
inspect <run_dir|sweep_dir> |
Print run or sweep summary |
analyze <run_dir> |
Count statistics |
Run directory layout
Every run produces a self-contained directory. Share it with a colleague and they can
re-render or analyse without re-running anything.
runs/zpinch_01/
input_deck.toml ← exact copy of deck used
resolved_config.json ← fully resolved SI parameters
metadata.json ← hardware, git hash, field SHA-256, timing
log.txt ← full terminal output mirror
counts/
raw_counts.bin ← u32 [H×W] detector hit counts
processed_counts.bin ← f32 [H×W] after detector response
images/
radiograph.png
Energy spectra
Three proton source spectra are supported:
| Mode | Deck field | Typical use |
|---|---|---|
| Monoenergetic | energy_MeV |
D–T fusion protons (14.7 MeV), accelerator beams |
| Gaussian spread | energy_spread_percent |
Slightly impure mono sources, calibration |
| Exponential / TNSA | temperature_MeV, cutoff_mev |
Laser-accelerated proton sources |
# TNSA example
[source]
temperature_MeV = 3.0
cutoff_mev = 40.0
n_particles = 200000
All three modes feed into the same relativistic Boris integrator — u = γv is computed
exactly regardless of the drawn energy. See docs/spectra.md for details.
Parameter sweeps
# Energy scan — four runs, zipped
proton_tracer sweep zpinch.toml \
--param source.energy_MeV=5,10,15,20
# Range syntax
proton_tracer sweep zpinch.toml \
--param source.energy_MeV=5:20:5
# Paired sweep — same-length lists, zipped
proton_tracer sweep zpinch.toml \
--param source.energy_MeV=5,10,15 \
--param numerics.max_steps=10000,20000,30000
Output: runs/sweep_001/ with one run directory per point and a live sweep_manifest.json.
Performance
Measured on Apple M4 (prad v0.3.0):
| Field | 100,000 particles | 1,000,000 particles |
|---|---|---|
| zero field | 0.34 s | 1.87 s |
| z-pinch | 0.31 s | 1.88 s |
| kink | 0.35 s | 1.92 s |
| sausage | 0.32 s | 1.91 s |
Peak step throughput: 9.0 B steps/s.
vs PlasmaPy — in a matched simplified uniform-field particle-tracing benchmark (10,000 particles, uniform Bz = 1 T, same geometry), prad's GPU backend is substantially faster than a single-core PlasmaPy CPU Boris workflow. This comparison isolates the forward particle-tracing step only; PlasmaPy is a broader plasma-physics ecosystem and is not being replaced by prad.
| PlasmaPy (CPU) | prad (GPU) | |
|---|---|---|
| Wall time | 42.8 s | 0.20 s |
| At-scale speedup (1 M particles) | — | ≈ 2280× |
Additional Linux/NVIDIA validation on an RTX 4090 reached ~34 B particle-steps/s
on the benchmark configuration, corresponding to roughly ~2 M particles/s for
the tested step budget. See benchmarks/validation/nvidia_rtx4090_ubuntu2204.txt.
See docs/benchmark.md to reproduce these numbers.
Validation
python3 validate.py # uses existing binary
python3 validate.py --build # build first, then validate
12 physics tests: B-only regression, zero-field straight-line projection, uniform E-field deflection (sign and magnitude), relativistic Boris energy conservation (14.7000 MeV recovered to sub-eV accuracy), pencil/point/disk source geometry, Gaussian energy spread, exponential/TNSA spectrum (mean KE ≈ T, hard cutoff enforced), Gaussian blur, Poisson noise reproducibility, and 60 MeV relativistic momentum initialisation (γ ≈ 1.064).
Documentation
| Doc | Contents |
|---|---|
| docs/quickstart.md | Full install → first run walkthrough |
| docs/python_api.md | Python API reference (prad.run, Field, RunResult) |
| docs/geometry.md | Coordinate system, detector geometry, source types |
| docs/spectra.md | Mono, Gaussian, and TNSA energy spectra |
| docs/input_decks.md | TOML schema, all fields, --set overrides |
| docs/run_artifacts.md | Run directory anatomy and reproducibility |
| docs/file_formats.md | .bfld, binary count formats, metadata schema |
| docs/rendering.md | Counts → PNG pipeline, re-render without GPU |
| docs/sweeps.md | Parameter sweeps, syntax, sweep manifest |
| docs/benchmark.md | Throughput scaling, physics sanity cases, PlasmaPy comparison |
| docs/validation.md | Physics test descriptions and tolerances |
| docs/gui.md | Deck launcher workflow |
| docs/limitations.md | Honest constraints and known gaps |
License
MIT — see LICENSE.
Citation
If you use prad in published work, please cite:
@software{dolezal2026prad,
author = {Dolezal, Jonas},
title = {{prad}: {GPU}-accelerated relativistic proton radiography},
year = {2026},
version = {0.3.0},
url = {https://github.com/JonasDolezal07/gpu-proton-radiography},
license = {MIT},
}
A CITATION.cff file is also provided for automated citation tooling (GitHub "Cite this repository").
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file prad-0.3.1-py3-none-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: prad-0.3.1-py3-none-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 4.8 MB
- Tags: Python 3, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77c059917323dfbad3379e53b242d0a35a332430a8869c2e4876700f4fd87c11
|
|
| MD5 |
bda1fb05bc7c3f759051ea3610a34192
|
|
| BLAKE2b-256 |
792f8a22f22470b91c55ab500dbb3be9245ef7c35015b476cd98595fa14b7e7d
|
Provenance
The following attestation bundles were made for prad-0.3.1-py3-none-manylinux_2_28_x86_64.whl:
Publisher:
release.yml on JonasDolezal07/gpu-proton-radiography
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
prad-0.3.1-py3-none-manylinux_2_28_x86_64.whl -
Subject digest:
77c059917323dfbad3379e53b242d0a35a332430a8869c2e4876700f4fd87c11 - Sigstore transparency entry: 1592162641
- Sigstore integration time:
-
Permalink:
JonasDolezal07/gpu-proton-radiography@a3d13a0b1e2caf63291579a9c24b1e1cd8e3cf67 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/JonasDolezal07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a3d13a0b1e2caf63291579a9c24b1e1cd8e3cf67 -
Trigger Event:
push
-
Statement type:
File details
Details for the file prad-0.3.1-py3-none-macosx_10_13_universal2.whl.
File metadata
- Download URL: prad-0.3.1-py3-none-macosx_10_13_universal2.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3, macOS 10.13+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
049b54e269b3e404edab3b3b4cbb3cb6d8a15759513b26fd6c6b46554cb58147
|
|
| MD5 |
b07c55d3a84855ad1c61524a4dc51ca3
|
|
| BLAKE2b-256 |
45a7763a79eca09ce478cd0db1d79a15ba0127357a353c2cb4a13209abd61c18
|
Provenance
The following attestation bundles were made for prad-0.3.1-py3-none-macosx_10_13_universal2.whl:
Publisher:
release.yml on JonasDolezal07/gpu-proton-radiography
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
prad-0.3.1-py3-none-macosx_10_13_universal2.whl -
Subject digest:
049b54e269b3e404edab3b3b4cbb3cb6d8a15759513b26fd6c6b46554cb58147 - Sigstore transparency entry: 1592162613
- Sigstore integration time:
-
Permalink:
JonasDolezal07/gpu-proton-radiography@a3d13a0b1e2caf63291579a9c24b1e1cd8e3cf67 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/JonasDolezal07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a3d13a0b1e2caf63291579a9c24b1e1cd8e3cf67 -
Trigger Event:
push
-
Statement type: