A lightweight, agnostic 3D event display for particle physics collisions.
Project description
Iris3D: 3D High-Energy Physics Event Visualizer
Iris3D is a lightweight, high-performance Python lib built for high-energy physics (HEP) analysis. Powered by PyVista and VTK, it brings sub-atomic collision data to life inside an interactive, GPU-accelerated 3D viewport.
Iris3D features a fully polymorphic ingestion engine that natively accepts lightweight standard Python rows, columnar NumPy blocks, or heavy CERN Awkward Records from standard analysis pipelines.
It models physical mechanics directly on your screen—calculating realistic track helices bending within an active magnetic field, extruding calorimeter deposits into localized 3D opening cones, and morphing real-time energy clusters into dual-screen 2D unfolded LEGO plots. It has built-in features for live kinematic resonance analysis, interactive filtering widgets, and instant single-key exports to publication-ready images, standalone interactive HTML web pages, or cinematic videos.
Polymorphic Data Ingestion: 3 Supported Input Formats
The load_event() ingestion pipeline is fully polymorphic. It normalizes data from three distinct data layouts into a unified internal event structure.
FORMAT 1: Row-Oriented Layout
Inside the particles and jets keys, data is structured as a list of independent dictionaries (one dict per object). The ingestion parser is resilient to extraneous keys and fills in missing optional parameters (like delta_r) using standard defaults.
row_based_data = {
"metadata": {
"run_id": 402130,
"event_id": 8943210,
"sqrts_gev": 13600.0
},
"particles": [
{"pt": 45.2, "eta": 1.2, "phi": -0.5, "charge": -1, "pid": 11, "name": "e-"},
{"pt": 38.1, "eta": -0.8, "phi": 2.6, "charge": 1, "pid": -11, "name": "e+"},
{"pt": 2.1, "eta": 2.4, "phi": 1.1, "charge": 0, "pid": 22, "name": "gamma", "ignored_extra_key": True}
],
"jets": [
{"energy": 120.5, "eta": 0.4, "phi": -1.2, "delta_r": 0.4},
{"energy": 85.0, "eta": -1.9, "phi": 1.8} # Automatically falls back to default delta_r=0.4
]
}
event_from_rows = load_event(row_based_data)
📊FORMAT 2: Columnar Data Layout
Inside particles and jets, fields are structured as a dictionary of parallel arrays/vectors. Every feature array must have matching dimensions. The parser automatically sanitizes low-level data structures, including decoding byte strings (b"mu+") to standard text string structures.
import numpy as np
columnar_data = {
"metadata": {
"run_id": 402130,
"event_id": 8943211
},
"particles": {
"pt": np.array([55.4, 12.3], dtype=np.float32),
"eta": np.array([0.15, -2.1], dtype=np.float32),
"phi": np.array([-2.8, 0.9], dtype=np.float32),
"charge": np.array([1, -1], dtype=np.int32),
"pid": np.array([13, -13], dtype=np.int32),
"name": np.array([b"mu+", b"mu-"]) # Automatic byte-string to utf-8 conversion
},
"jets": {
"energy": np.array([210.0]),
"eta": np.array([0.05]),
"phi": np.array([-1.4]),
"delta_r": np.array([0.4])
}
}
event_from_columns = load_event(columnar_data)
FORMAT 3: Awkward Records Layout
This format processes an encapsulated native ak.Record representing an individual jagged-array event slice. It seamlessly unpacks variable-length records, converting Awkward categories and text mappings directly into interactive 3D graphical primitives.
import awkward as ak
awkward_data = ak.Record({
"metadata": {
"run_id": 402130,
"event_id": 8943212,
"sqrts_gev": 13600.0
},
"particles": {
"pt": ak.Array([85.0, 62.1, 4.5]),
"eta": ak.Array([-0.4, 0.9, 1.1]),
"phi": ak.Array([1.7, -2.1, 0.3]),
"charge": ak.Array([-1, 1, 0]),
"pid": ak.Array([11, 13, 22]),
"name": ak.Array(["e-", "mu+", "gamma"])
},
"jets": {
"energy": ak.Array([340.5]),
"eta": ak.Array([0.85]),
"phi": ak.Array([-1.9]),
"delta_r": ak.Array([0.4])
}
})
event_from_awkward = load_event(awkward_data)
Summary of Feature Mappings Across All Formats
No matter which layout you feed into load_event(), the parser guarantees mapping across all 3 key blocks:
metadata: Extracted scalars populate the top-left HUD info banner.particles: Curvature orientation is calculated based oncharge. Tracks withcharge: 0are drawn as neutral dashed vectors.jets:energy,eta, andphispecify opening cones in the 3D event viewport and elevate bin pillars in the 2D unfolded LEGO subplot.
Manual
The EventVisualizer class is the central engine of Iris3D. It orchestrates fluid, GPU-accelerated 3D rendering of high-energy physics collision events using PyVista and VTK. It features smart particle identification color-coding, interactive track/jet selection, kinematic analysis tools (like live invariant mass tracking), dynamic kinematical filters, and multi-format export capabilities.
🛠️ Class Initialization & Custom Themes
__init__(theme: str | dict = "cyberpunk")
Initializes the visualizer canvas. You can load a pre-configured theme blueprint or pass a custom layout dictionary.
-
Parameters:
-
theme(strordict): The name of a built-in theme ("cyberpunk","publication", etc.) or a custom dictionary defining color primitives. If a requested theme name is missing, it falls back to"cyberpunk". -
Example Usage:
from iris3d import EventVisualizer
# Initialize using the default cyberpunk aesthetic
visualizer = EventVisualizer(theme="cyberpunk")
Core Methods
1. Static Display Mode (plot_event)
Renders a static snapshot of the collision event with full access to interaction widgets, mouse picking, and subplots.
plot_event(
event: CollisionEvent,
mode: str = "both",
p_scale: float = 1.0,
j_scale: float = 0.01,
B_field: float = 3.8
)
-
Parameters:
-
event(CollisionEvent): The underlying event structure containing the processed meta, tracks, and jet blocks. -
mode(str): Layout style. Options are: -
"both": Split Screen. Displays the 3D detector view on the left, and an unfolded 2D $(\eta, \phi)$ calorimeter Lego plot on the right. -
"detector": Full window view dedicated solely to the 3D geometry layout. -
"lego": Full window view dedicated solely to the 2D histogram towers. -
p_scale(float): Magnification factor for scaling particle lengths. -
j_scale(float): Scale coefficient for the length computation of jet cones. -
B_field(float): Solenoidal magnetic field strength in Tesla (defaults to the CMS benchmark of $3.8\text{ T}$). Controls tracking helix bending equations.
2. Cinematic Animation Mode (animate_event)
Triggers an off-screen sub-stepping interpolation routine that plays back the time-of-flight wavefront of the collision. Beams approach along the $Z$-axis, collide at the vertex $(0,0,0)$, and scatter outward through the layers in real time.
animate_event(
event: CollisionEvent,
mode: str = "both",
p_scale: float = 1.0,
j_scale: float = 0.01,
B_field: float = 3.8,
speed: float = 0.05
)
-
Parameters: Same as
plot_event, with the addition of: -
speed(float): Radial step advancement velocity ($m/\text{frame}$) of the expanding collision wavefront. -
Animation Control:
-
Press the
Spacebarlive to pause/resume the expansion wavefront at any timestamp (e.g., to pause right during the calorimeter showering phase).
Live Window Controls & Keyboard Shortcuts
When looking at either a static or animated visualizer viewport, you can interact with the scene using your mouse and keys:
Mouse-Picking Mechanics (Static only)
- Single Left Click: Selects an element (track or jet) and highlights it in White. Detailed information (Kinematics, PDG ID identity, Energy) is instantly printed onto the upper-left Head-Up Display (HUD) canvas banner.
[SHIFT]+ Left Click: Relativistic Resonance Analysis Mode. Select exactly 2 particle tracks while holdingSHIFT. They will flash Cyan, and the engine will instantly calculate and display their spatial separation $\Delta R$ and combined Invariant Mass $M_{(ab)}$ directly on the HUD screen.
Dynamic Cinematic Sliders (Static)
In static mode, three interactive sliders appear on the bottom-left corner of the 3D viewport:
- Min $p_T$ (GeV): Dynamically filters out low-energy background clutter.
- Max $\eta$: Restricts the longitudinal acceptance window.
- Max $\phi$ (rad): Truncates the azimuthal cross-section view.
- Ghost Mode Implementation: Filtered tracks do not disappear completely; they enter a semi-transparent Ghost Mode (3% opacity) and become non-pickable, allowing you to isolate primary hard scatterings without losing full event context.
📋 Complete Keyboard Shortcuts Reference Table
| Key | Action | Ingress Target / Description |
|---|---|---|
c |
Toggle Selection Filter | Swaps mouse targeting between Particles Mode and Jets Mode. (Cones are unpickable on startup to allow easy access to inner tracking lines). |
f |
Toggle Filter Sliders | Cleanly hides or unhides the $p_T, \eta, \phi$ adjustment slider blocks to declutter the viewport. |
Space |
Pause Animation | Freezes the expanding wavefront mid-flight during an animated run. |
e |
Export Screenshot | Automatically strips HUD text overlay and captures a crisp image asset. |
h |
Export Interactive HTML | Compiles the entire 3D mesh architecture into a portable, WebGL-ready standalone .html page. Requires the Jupyter backend. Run pip install "pyvista[jupyter]" before using. |
r |
Export Spin Video | Triggers the export module to record a clean, 360-degree orbital movie rotation of the scene. Requires video encoding libraries. Run pip install imageio imageio-ffmpeg before using |
Custom Themes
Iris3D comes packaged with built-in visual identities like "cyberpunk", but it also gives you complete aesthetic control over your event displays. You can customize the look of the passive detector structures, backgrounds, HUD text, and individual particle tracks by passing a theme configuration dictionary directly during initialization.
Theme Dictionary Blueprint
A theme dictionary contains hex or standard VTK string color rules mapping to specific graphics primitives. Below is the reference blueprint structure (using the default "cyberpunk" config as an example):
custom_theme = {
"dark": True, # Toggles PyVista dark plotting background context
"bg_detector": "#0f172a", # 3D canvas background color
"bg_lego": "#090d16", # 2D unfolded histogram background color
"grid_color": "#273549", # Reference grid line color
"hud_text": "#38bdf8", # On-screen HUD text overlays
"lego_title": "#fb923c", # Title header on the Lego canvas
# Passive Detector Elements
"detector_ecal": "crimson", # Outer calorimeter shell surface color
"detector_ecal_edge": "firebrick", # Calorimeter boundary mesh lines
"detector_tracker": "deepskyblue", # Inner tracker cylinder volume color
"detector_tracker_edge": "dodgerblue", # Inner tracker boundary lines
"beam": "#38bdf8", # High-energy packet beam lines (animation)
"vertex_static": "magenta", # Interaction point marker in static plots
"vertex_anim": "gray", # Colliding state packet color in animation
"shockwave": "orange", # Radial collision wavefront hull
# Physics Signatures
"jet_cone": "orange", # 3D clustered shower cones
"jet_cone_edge": "darkorange", # 3D cone mesh outlines
"jet_tower": "orange", # 2D LEGO plot histogram boxes
"jet_tower_edge": "white", # 2D LEGO tower boundary lines
"lego_floor": "#1e293b", # 2D histogram baseline layout plane
"lego_floor_edge": "#334155", # 2D layout grid lines
"met": "red", # Missing Transverse Energy ($\vec{E}_T^{\text{miss}}$) vector
# Particle-ID Specific Color Mapping (PDG Keys)
"particles": {
11: "#38bdf8", # Electron (e-)
-11: "#38bdf8", # Positron (e+)
13: "#a855f7", # Muon (mu-)
-13: "#a855f7", # Antimuon (mu+)
22: "#4ade80", # Photon (gamma)
211: "#f43f5e", # Charged Pion (pi+)
-211: "#f43f5e", # Charged Pion (pi-)
"default": "#94a3b8" # Fallback color for unmapped/exotic tracks
}
}
Mapping Custom Particles
The "particles" sub-dictionary supports standard PDG ID integer keys. If your simulation includes custom or rare tracking signatures, you can simply expand the dictionary with their respective PDG integers:
# Extending particle mapping to highlight exotic BSM states
my_theme = {
"particles": {
1000022: "#facc15", # Highlight Neutralinos (~chi_1^0) in Bright Yellow
25: "#ec4899", # Highlight Higgs bosons (H) in Hot Pink
"default": "#64748b" # Standard muted gray for background pile-up tracks
}
}
Load a Custom Theme
To apply your styling rules, pass your dictionary to the theme parameter when initializing the EventVisualizer.
You do not need to specify every single key! The framework features an internal validation layer. It scans your custom dictionary and automatically pads any missing visual parameters using the robust
"cyberpunk"theme.
from iris3d import EventVisualizer
# 1. Define custom elements (only specify what you want to change)
minimal_clean_theme = {
"dark": False,
"bg_detector": "#ffffff", # Force clean white layout
"grid_color": "#e2e8f0",
"hud_text": "#0f172a",
"particles": {
13: "#3b82f6", # Force all Muons to show up Blue
-13: "#3b82f6",
"default": "#94a3b8"
}
}
# 2. Inject the custom configuration into the pipeline
vis = EventVisualizer(theme=minimal_clean_theme)
# 3. Call your plotting logic as usual
vis.plot_event(my_hep_event, mode="both")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 iris3d-0.1.0.tar.gz.
File metadata
- Download URL: iris3d-0.1.0.tar.gz
- Upload date:
- Size: 36.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a708a5f28575360f699c0b9139a3b00c1142f6a98271e7f27294c778cbef40c
|
|
| MD5 |
082ee2d4bd7e7ac99d14e6ba750321d6
|
|
| BLAKE2b-256 |
c2c6beec9c10a10d47b99c5e9d4ee7334cedf6796894b22d63f03ed1ceb36675
|
Provenance
The following attestation bundles were made for iris3d-0.1.0.tar.gz:
Publisher:
pypi-publish.yml on paulhenry46/Iris3D
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
iris3d-0.1.0.tar.gz -
Subject digest:
1a708a5f28575360f699c0b9139a3b00c1142f6a98271e7f27294c778cbef40c - Sigstore transparency entry: 1756750400
- Sigstore integration time:
-
Permalink:
paulhenry46/Iris3D@53a070b12d224c7e4d5de978446c597729d910b7 -
Branch / Tag:
refs/tags/0.1 - Owner: https://github.com/paulhenry46
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@53a070b12d224c7e4d5de978446c597729d910b7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file iris3d-0.1.0-py3-none-any.whl.
File metadata
- Download URL: iris3d-0.1.0-py3-none-any.whl
- Upload date:
- Size: 32.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b25d62b8c7e5a3d5186b2f36962f3e56c0fd4d602ec0bc031f7923e26227d18
|
|
| MD5 |
0fd093f35faa6f739d9ee2a92890f955
|
|
| BLAKE2b-256 |
8e7cffe858c42b3cf8e1f6dec0d6d443f4664d981f0b5fd40a4adf1421837a91
|
Provenance
The following attestation bundles were made for iris3d-0.1.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on paulhenry46/Iris3D
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
iris3d-0.1.0-py3-none-any.whl -
Subject digest:
5b25d62b8c7e5a3d5186b2f36962f3e56c0fd4d602ec0bc031f7923e26227d18 - Sigstore transparency entry: 1756750406
- Sigstore integration time:
-
Permalink:
paulhenry46/Iris3D@53a070b12d224c7e4d5de978446c597729d910b7 -
Branch / Tag:
refs/tags/0.1 - Owner: https://github.com/paulhenry46
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@53a070b12d224c7e4d5de978446c597729d910b7 -
Trigger Event:
release
-
Statement type: