Take any detector geometry, run the same events with CPU and GPU optical photon simulation, and find out what RiceOpticks buys you.
OpticalSims is a hybrid CPU–GPU simulation environment built on:
- Geant4 for particle transport and detector simulation.
- RiceOpticks for optical photon propagation on NVIDIA GPUs. It is built on Opticks and NVIDIA OptiX.
Geant4 still handles every particle on the CPU. Scintillation and Cherenkov photons, usually the most expensive part of an event by far, can be handed to the GPU instead. The detector is read from a GDML file at run time, so no C++ is written per experiment. If a geometry can be described in GDML with optical properties and tagged sensors, OpticalSims can simulate it on the CPU, on the GPU, or both, from the same macro.
That makes this repository a test bench for RiceOpticks on any experiment:
| Question | How OpticalSims answers it |
|---|---|
| How much faster is GPU optical simulation for my detector? | Run the same macro with OPTICKS_INTEGRATION_MODE=2 (Geant4 CPU) and =1 (RiceOpticks GPU), then compare the per-event times in the PhotonInfo tree. |
| Does the GPU give the same physics? | Use OPTICKS_INTEGRATION_MODE=3 to run both in each event, then compare the Geant4Hits and OpticksHits trees sensor by sensor. |
| How does the speed-up scale with light yield, detector size or sensor count? | Swap the GDML or macro (particle, energy, number of events) and rerun. The physics list and output stay the same. |
| Is my geometry ready for GPU simulation? | Load it. Opticks converts the whole geometry, materials and surfaces at start-up and reports anything it cannot translate. |
The same workflow already runs on liquid-argon TPCs (DUNE, ProtoDUNE, ICARUS), water Cherenkov detectors (Super-Kamiokande), liquid-scintillator detectors (SNO+) and an ice Cherenkov telescope (IceCube). See Included geometries.
- CUDA Toolkit 13 Update 2 (tested)
- NVIDIA GPU with OptiX 9 support (tested)
- Geant4, with GDML support enabled
- RiceOpticks / Opticks
- ROOT, for output and visualization
All of these come preinstalled in the RiceOpticks container, which is the recommended route.
More information about the container: https://github.com/nuRiceLab/riceopticks-container
# The image is large. Put the Apptainer cache somewhere with space:
# HPC home directories usually do not have enough.
mkdir -p cache/.apptainer/tmp
export APPTAINER_CACHEDIR=$PWD/cache/.apptainer
export APPTAINER_TMPDIR=$PWD/cache/.apptainer/tmp
apptainer pull riceopticks_v1.0r.sif oras://ghcr.io/nuricelab/riceopticks:v1.0r
apptainer shell --nv --bind /cvmfs riceopticks_v1.0r.sif
# inside the container
source /opt/opticks-dev-env.sh
git clone https://github.com/nuRiceLab/OpticalSims
cd OpticalSims
source configure.sh # configures a fresh build/ directory and cd's into it
make -j2git clone https://github.com/nuRiceLab/OpticalSims
cd OpticalSims
mkdir build && cd build
cmake -DWith_Opticks=true -DWITH_GEANT4_VIS=true ..
make -j2Run everything below from build/.
source ../OpticksEnv # Opticks defaults: max photons, bounces, integration mode, ..../gdml_det ../GDML/dune10kt_v5_refactored_1x2x6_nowires_NoField.gdml ../macros/g04.macThe usual Geant4 commands (/gps/..., /run/beamOn) go in the macro. Set the output file with /RunAction/output/file <name>.root.
The integration mode decides who propagates the optical photons:
OPTICKS_INTEGRATION_MODE |
Optical photons simulated by | Use it to |
|---|---|---|
1 |
GPU only (RiceOpticks) | measure GPU throughput |
2 |
CPU only (Geant4) | get the reference CPU time and hits |
3 |
both, in the same event (default in OptickEnv) |
validate GPU hits against Geant4 hit by hit |
A typical benchmark for a new geometry:
source ../OptickEnv; export LC_ALL=C GEOM=MyDetector
export OPTICKS_INTEGRATION_MODE=2; ./gdml_det ../GDML/MyDetector.gdml ../macros/MyDetector.mac # CPU reference
export OPTICKS_INTEGRATION_MODE=1; ./gdml_det ../GDML/MyDetector.gdml ../macros/MyDetector.mac # GPU
export OPTICKS_INTEGRATION_MODE=3; ./gdml_det ../GDML/MyDetector.gdml ../macros/MyDetector.mac # validationGive each run its own /RunAction/output/file. Speed comparisons should use modes 1 and 2: in mode 3 the event time includes both simulations.
Other useful knobs in OptickEnv:
OPTICKS_MAX_PHOTON: GPU photon budget per launch. Raise it for high-energy events in large detectors.OPTICKS_MAX_BOUNCE: maximum number of steps per photon.OPTICKS_EVENT_MODE:Minimalkeeps only what the ROOT output needs;Hitsalso writes.npydebug arrays.CUDA_VISIBLE_DEVICES: chooses the GPU.
| Tree | Contents |
|---|---|
generator |
primary particle: name, pdg, energy, vertex ix,iy,iz,it, direction mx,my,mz, evtID |
OpticksHits |
GPU hits: evtID, hit_Id, SensorID, x,y,z,t, wavelength, boundary |
Geant4Hits |
CPU hits: evtID, SensorID, SensorName, x,y,z,t, wavelength, ProcessID |
PhotonInfo |
photons generated per event: G4ScintPhotons, G4CernPhotons, OScintPhotons, OCerenkovPhotons, wall-clock event Time [s], eventID, BatchID |
step |
optional particle step record |
RiceOpticks only needs a GDML file. To make a detector GPU-ready:
- Give every optical medium a material properties table.
RINDEX,ABSLENGTHand (optionally)RAYLEIGHas functions of photon energy.- A volume whose material has no
RINDEXabsorbs photons, both in Geant4 and in Opticks.
- Scintillators need both naming schemes.
- Geant4 11 names:
SCINTILLATIONYIELD,RESOLUTIONSCALE,SCINTILLATIONCOMPONENT1..3,SCINTILLATIONTIMECONSTANT1..3,SCINTILLATIONYIELD1..3. - The arrays Opticks reads for GPU generation:
FASTCOMPONENT,SLOWCOMPONENT(identical emission spectra) andREEMISSIONPROB.
- Geant4 11 names:
- Tag the sensors.
- Add
SensDet = PhotonDetectorto every sensitive logical volume. - Add an
<opticalsurface>with anEFFICIENCYtable, attached through aSurfaceauxiliary. It becomes a skin surface, or a border surface if its name containsBorder.
- Add
- Sensor IDs. If the sensor's logical-volume name has no
_, each placement's copy number becomes itsSensorID. This is the easiest choice for thousands of identical PMTs, DOMs or SiPM tiles.
<opticalsurface name="MyPMTSurface" model="unified" finish="polished" type="dielectric_metal" value="0">
<property name="EFFICIENCY" ref="PMT_EFFICIENCY"/>
<property name="REFLECTIVITY" ref="PMT_REFLECTIVITY"/>
</opticalsurface>
...
<volume name="volPMT"> <!-- no '_' : copy number = SensorID -->
<materialref ref="G4_Galactic"/>
<solidref ref="PMTSolid"/>
<auxiliary auxtype="SensDet" auxvalue="PhotonDetector"/>
<auxiliary auxtype="Surface" auxvalue="MyPMTSurface"/>
</volume>| Detector | Type | GDML | Light |
|---|---|---|---|
| DUNE FD-HD 10 kt (full and 1×2×6) | LArTPC | GDML/dune10kt_v6_refactored*.gdml |
LAr scintillation |
| DUNE FD-VD 10 kt | LArTPC | GDML/dunevd10kt_fullvd_wires.gdml |
LAr scintillation |
| ProtoDUNE-HD | LArTPC | GDML/protodunehd_v*_refactored*.gdml |
LAr scintillation |
| ICARUS | LArTPC | GDML/icarus_complete_*_overburden*.gdml |
LAr scintillation |
| Super-Kamiokande like | water Cherenkov, 11 096 PMTs | GDML/SK-like_opticks.gdml |
Cherenkov |
| SNO+ like | liquid scintillator (LAB + PPO), 9 362 PMTs | GDML/SNOplus-like_opticks.gdml |
scintillation + Cherenkov |
| IceCube-86 like | ice Cherenkov telescope, 5 160 DOMs | GDML/IceCube86-like_opticks.gdml |
Cherenkov |
The Jupyter notebooks in Analysis/ use uproot and matplotlib:
OpticksHits_general.ipynb: hits per sensor and 3-D heat maps for any GDML geometry, with box-shaped sensors such as DUNE, ICARUS or SiPM arrays. Start here for a new detector.OpticksHits_heatmap.ipynb,OpticksHits_SNOplus.ipynb,OpticksHits_IceCube86.ipynb: geometry-specific versions with unrolled-barrel, sphere-projection and string-by-depth views, plus event displays.
See some examples:
- Run aborts with
locale::facet::_S_create_c_locale name not valid. Fix:export LC_ALL=C. - No optical photons in a water or ice detector. Cherenkov generation is off by default. Enable it with
export G4CerenkovOpticks__ENABLE=1. - Geometry changed but the GPU still sees the old one. Use a new
GEOMname, or delete~/.opticks/GEOM/$GEOM. - Large events are truncated. Increase
OPTICKS_MAX_PHOTON(andOPTICKS_MAX_SLOT) to cover the photons generated per event.
If you use this work, please cite:
@misc{parmaksiz2026acceleratingopticalphotonsimulation,
title={Accelerating Optical Photon Simulation in DUNE with Opticks},
author={Ilker Parmaksiz and Aaron Higuera and Laura Paulucci and Viktor Pec and Estanislao Forino},
year={2026},
eprint={2608.27306},
archivePrefix={arXiv},
primaryClass={hep-ex},
url={https://arxiv.org/abs/2608.27306},
}Copyright 2026 nuRiceLab, Rice University
OpticalSims is licensed under the Apache License, Version 2.0. You may use, modify and redistribute it under the terms of that license. Unless required by applicable law or agreed to in writing, it is distributed on an "AS IS" basis, without warranties or conditions of any kind.: If you use OpticalSims in your research, please cite it as shown in Citation.



