A high-performance C++20 library for reading, writing, and manipulating particle phase space files across multiple Monte Carlo simulation ecosystems. ParticleZoo provides a unified API that abstracts away format-specific details, enabling seamless interoperability between different simulation codes and workflows.
ParticleZoo serves as a universal translator and processor for particle phase space data, which represents the position, momentum, energy, and other properties of particles at specific locations in Monte Carlo simulations. The library is designed around a common Particle data model that can represent particles from any supported format, with automatic format detection and conversion capabilities.
- Unified API: Single interface to work with multiple phase space formats
- Format Transparency: Automatic format detection with explicit override options
- High Performance: Efficient binary I/O with configurable buffering
- Extensible Architecture: Plugin-style registry system for adding new formats
- Unit Consistency: Built-in unit system ensures proper dimensional handling
- Memory Efficient: Streaming interfaces for processing large files
- Cross-Platform: Windows, Linux and macOS support with standard build tools
The library includes built-in support for major Monte Carlo simulation formats:
- EGS (EGSnrc):
.egsphspfiles in MODE0 and MODE2, including suffixed variants (.egsphsp1, etc.) - IAEA:
.IAEAphspInternational Atomic Energy Agency format with header files - TOPAS:
.phspfiles in Binary, ASCII, and Limited variants - penEasy:
.datASCII format from the PENELOPE simulation code - ROOT (optional):
.rootfiles generated with the CERN ROOT framework. Includes build-in templates for TOPAS and OpenGATE generated files. Also supports custom branch mappings
Additional formats can be added through the extensible registry system without modifying core library code.
Particle Class: The central data model representing a particle with position, momentum, energy, weight, and type information. Supports both standard properties and format-specific extensions through a flexible property system.
PhaseSpaceFileReader: Abstract base class for reading phase space files. Implementations handle format-specific parsing while presenting a common streaming interface.
PhaseSpaceFileWriter: Abstract base class for writing phase space files. Handles format-specific serialization with proper history counting.
FormatRegistry: Plugin-style system for registering and creating readers/writers. Enables runtime format discovery and automatic format detection from file extensions.
ByteBuffer: High-performance binary I/O buffer for efficient reading of large files with configurable buffering strategies.
The Particle class provides access to:
- Spatial coordinates: Position (x, y, z) in configurable units
- Momentum: Direction cosines and kinetic energy
- Particle properties: PDG particle codes, statistical weight
- History tracking: Original history numbers and incremental counters
- Format-specific data: Extensible property system for specialized information
ParticleZoo includes a comprehensive unit system that ensures dimensional consistency across different formats:
// Length units: mm, cm, m
float x_in_cm = particle.getX() / cm;
float y_in_mm = particle.getY() / mm;
// Energy units: eV, keV, MeV, GeV
float energy_MeV = particle.getKineticEnergy() / MeV;- Operating System: Linux, macOS, or Windows
- Compiler / Toolchain:
- Linux/macOS: C++20 compatible compiler (GCC 10+, Clang 13+)
- Windows: Visual Studio 2019 or later with C++ development tools (MSVC)
- Build Tools:
- Linux/macOS: GNU Make
- Windows: Windows Command Prompt or PowerShell (uses
build.bat)
- Optional Dependencies:
- CERN ROOT (for ROOT format support)
- Linux/macOS: requires
root-configin PATH - Windows: not supported by build.bat script, requires manual compilation
- Linux/macOS: requires
- CERN ROOT (for ROOT format support)
# Configure the build system (auto-detects compiler and dependencies)
./configure [--prefix=/your/installation/prefix]
# Build the library and tools
make # Release build (default)
make debug # Debug build with symbols
make release # Explicitly build release version
# Install (optional)
make install # defaults to /usr/local for the install PREFIX
make install PREFIX=/usr/local# Configure, build, and optionally install
build.bat [--prefix=C:\path\to\install] [debug|release]
build.bat install [--prefix=C:\path\to\install] [debug|release]The build system creates the following artifacts:
Release build
- Linux/macOS:
build/gcc/release/ - Windows:
build/msvc/release/- Static library:
- Linux/macOS:
libparticlezoo.a - Windows:
libparticlezoo.lib
- Linux/macOS:
- Executables:
- Linux/macOS:
PHSPConvert,PHSPCombine,PHSPImage,PHSPSplit - Windows:
PHSPConvert.exe,PHSPCombine.exe,PHSPImage.exe,PHSPSplit.exe
- Linux/macOS:
- Dynamic library (Windows only):
build/msvc/release/bin/particlezoo.dll
- Static library:
Debug build
- Linux/macOS:
build/gcc/debug/ - Windows:
build/msvc/debug/- Static library with debug symbols:
- Linux/macOS:
libparticlezoo.a - Windows:
libparticlezoo.lib
- Linux/macOS:
- Debug executables:
- Linux/macOS:
PHSPConvert, etc. - Windows:
PHSPConvert.exe, etc.
- Linux/macOS:
- Dynamic library (Windows only):
build/msvc/debug/bin/particlezoo.dll
- Static library with debug symbols:
Installation (optional)
- Linux/macOS (with
make install):- Headers:
$PREFIX/include/particlezoo/ - Static Library:
$PREFIX/lib/libparticlezoo.a - Executables:
$PREFIX/bin/PHSPConvert, etc.
- Headers:
- Windows (with
build.bat install):- Headers:
%PREFIX%\include\particlezoo\ - Static Library:
%PREFIX%\lib\particlezoo.lib - Executables and DLL:
%PREFIX%\bin\PHSPConvert.exe, etc.
- Headers:
The configure script (Linux/macOS) accepts the following options:
--prefix=PATH- Installation prefix (default:/usr/local)--no-root- Disable ROOT support even if available
The build.bat script (Windows) accepts the following options:
--prefix=PATH- Installation prefix (default:%LOCALAPPDATA%)
Here's a simple example showing how to read from one format and write to another:
#include <particlezoo/PhaseSpaceFileReader.h>
#include <particlezoo/PhaseSpaceFileWriter.h>
#include <particlezoo/utilities/formats.h>
using namespace ParticleZoo;
int main() {
// Register standard formats
FormatRegistry::RegisterStandardFormats();
// Create readers and writers - format auto-detected from extension
auto reader = FormatRegistry::CreateReader("input.IAEAphsp");
auto writer = FormatRegistry::CreateWriter("output.egsphsp");
// Process all particles
while (reader->hasMoreParticles()) {
Particle particle = reader->getNextParticle();
// Optionally modify particle properties
// particle.setWeight(particle.getWeight() * 2.0);
writer->writeParticle(particle);
}
// Clean up
writer->close();
reader->close();
return 0;
}Many format readers and writers accept configuration options:
// Create options map for custom behavior
// Requires: #include <particlezoo/ROOT/ROOTphsp.h>
UserOptions options;
// Example: select predefined ROOT template when reading ROOT files
options[ParticleZoo::ROOT::ROOTFormatCommand] = { std::string("TOPAS") };
// Create reader with our user options
auto reader = FormatRegistry::CreateReader("ROOT", "simulation.root", options);Many formats support holding certain values (e.g. X, Y, Z) constant across all particles to reduce file sizes.
// Create default options map
UserOptions options;
// Create the flags for the fixed values and set the Z value to be constant at 100 cm
FixedValues fixedValues;
fixedValues.zIsConstant = true;
fixedValues.constantZ = 100 * cm;
// Create writer with explicit format, options, and a fixed Z value
auto writer = FormatRegistry::CreateWriter("IAEA", "simulation.IAEAphsp", options, fixedValues);The Particle class provides extensive access to particle properties:
Particle p = reader->getNextParticle();
// Basic properties
float x = p.getX() / cm; // Position in cm
float y = p.getY() / cm;
float z = p.getZ() / cm;
float energy = p.getKineticEnergy() / MeV; // Energy in MeV
float weight = p.getWeight(); // Statistical weight
// Direction (unit vector)
float dx = p.getDirectionalCosineX();
float dy = p.getDirectionalCosineY();
float dz = p.getDirectionalCosineZ();
Different formats support different features. The library provides access to format-specific properties:
// EGS-specific latch information
if (p.hasIntProperty(IntPropertyType::EGS_LATCH)) {
int latch = p.getIntProperty(IntPropertyType::EGS_LATCH);
}
// PENELOPE-specific interaction flags
if (p.hasIntProperty(IntPropertyType::PENELOPE_ILB1)) {
int ilb1 = p.getIntProperty(IntPropertyType::PENELOPE_ILB1);
}The library uses standard C++ exception handling:
try {
auto reader = FormatRegistry::CreateReader("nonexistent.phsp");
// ... process particles
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error: " << e.what() << std::endl;
}ParticleZoo includes several command-line utilities that demonstrate the library's capabilities:
Converts phase space files between different formats:
# Auto-detect formats from file extensions
PHSPConvert input.egsphsp output.IAEAphsp
# Explicitly specify formats
PHSPConvert --inputFormat EGS --outputFormat IAEA input.file output.file
# Limit particle count
PHSPConvert --maxParticles 1000000 input.IAEAphsp output.phsp
# Optional: project particles to a plane during conversion
PHSPConvert --projectToZ 100.0 input.phsp output.IAEAphspCombines multiple phase space files into a single output:
# Combine multiple files
PHSPCombine --outputFile combined.IAEAphsp file1.egsphsp file2.egsphsp file3.egsphsp
# Mix formats during combination
PHSPCombine --outputFile result.phsp input1.IAEAphsp input2.egsphsp
# Preserve constant values in the output file if all input files have the same constant values
PHSPCombine --preserveConstants --outputFile result.IAEAphsp input1.IAEAphsp input2.IAEAphspCreates 2D particle fluence or energy fluence images from phase space data. Can output either a detailed TIFF image with raw fluence data stored in 32-bit floats (default) which can be analyzed directly in third party tools like ImageJ, or in a simple bitmap BMP image with automatic constrast for easy visualization:
# Generate a flattened XY plane image (default)
PHSPImage beam.egsphsp fluence_map.tiff
# Generate project the particles to a specific XY plane (e.g. 100 cm or isocenter)
PHSPImage --projectTo 100 beam.egsphsp projection.tiff
# Custom plane and energy weighting, particles are not relocated, only particles located at
# Y = 5 cm +- a default margin of 0.25 cm will be counted (margin for XZ plane can be changed
# with the --tolerance parameter)
PHSPImage --outputFormat BMP --projectionType none --plane XZ --planeLocation 5.0 --energyWeighted simulation.IAEAphsp dose_profile.bmpSplits a single phase space file into multiple (roughly) equally sized output files. History boundaries are respected, so individual files may differ slightly in size.
# Split a file into multiple parts
PHSPSplit --splitNumber 10 input.egsphsp
# Use short flag and specify output format
PHSPSplit -n 5 --outputFormat IAEA input.egsphspTo add support for a new phase space format:
- Implement Reader: Inherit from
PhaseSpaceFileReaderand implement virtual methods - Implement Writer: Inherit from
PhaseSpaceFileWriterand implement virtual methods - Register Format: Add registration call to connect file extensions with your implementations
Example registration:
SupportedFormat myFmt{"MyFormat", "My custom phase space format", ".myext"};
FormatRegistry::RegisterFormat(
myFmt,
[](const std::string& file, const UserOptions& opts) -> std::unique_ptr<PhaseSpaceFileReader> {
return std::make_unique<MyFormatReader>(file, opts);
},
[](const std::string& file, const UserOptions& opts, const FixedValues & fixedValues) -> std::unique_ptr<PhaseSpaceFileWriter> {
return std::make_unique<MyFormatWriter>(file, opts, fixedValues);
}
);The Particle class supports custom properties through the property system:
// Add custom integer property
particle.setIntProperty(IntPropertyType::CUSTOM, 42);
// Add custom float property
particle.setFloatProperty(FloatPropertyType::CUSTOM, 3.14f);
// Add custom boolean property
particle.setBoolProperty(BoolPropertyType::CUSTOM, true);When compiled with ROOT support, ParticleZoo can read and write ROOT-based phase space files using predefined templates or custom branch mappings.
# Use TOPAS template
PHSPConvert --inputFormat ROOT --ROOT-format TOPAS input.root output.IAEAphsp
# Use OpenGATE template
PHSPConvert --inputFormat ROOT --ROOT-format OpenGATE simulation.root converted.egsphspFor ROOT files with non-standard branch names:
PHSPConvert --inputFormat ROOT \
--ROOT-tree-name MyTree \
--ROOT-energy E_kin \
--ROOT-position-x pos_x \
--ROOT-position-y pos_y \
--ROOT-position-z pos_z \
--ROOT-weight stat_weight \
input.root output.phspAvailable branch mapping options:
--ROOT-tree-name <name>- ROOT tree name--ROOT-energy <branch>- Energy branch--ROOT-weight <branch>- Statistical weight branch--ROOT-position-x/y/z <branch>- Position branches--ROOT-cosine-x/y/z <branch>- Direction cosines--ROOT-cosine-z-sign <branch>- Boolean flag for Z-direction sign--ROOT-pdg-code <branch>- Particle type identifier--ROOT-history-number <branch>- History counter
- ParticleZoo uses streaming I/O to minimize memory footprint
- Configurable buffer sizes for optimal performance vs. memory trade-offs
- Large files can be processed with constant memory usage
- Use binary formats when possible for faster I/O
- Consider particle limits (
--maxParticles) for testing and prototyping - Enable compiler optimizations (
make release) for production use - ROOT format may be slower due to tree structure overhead
Build Problems:
- "config.status not found" → Run
./configurebeforemake - "The C++ standard in this build does not match ROOT configuration" → The ROOT installation on your system was compiled with a different C++ standard than ParticleZoo. It may still work, but it cannot be guaranteed. Either rebuild both with the same C++ standard or use at your own risk.
- "ROOT support: no" → Ensure
root-configis in PATH and re-run./configure - "checking whether g++ accepts -std=c++20... no" → Update compiler (GCC 10+ or Clang 13+)
Runtime Issues:
- "Unknown format" → Use
--inputFormatto explicitly specify format - "File not found" → Check file paths and permissions
Performance Issues:
- Large files processing slowly → Consider using
--maxParticlesfor testing - Memory usage too high → Check if streaming interface is being used properly
For additional support:
- Use
--formatsoption to verify supported formats at runtime - Try explicit format specification with
--inputFormat/--outputFormat - Verify file integrity using third-party format-specific validation tools if available
This project is licensed under the MIT License. See the LICENSE file for details.
Copyright (c) 2025 Daniel O'Brien