Back to home page

EIC code displayed by LXR

 
 

    


Warning, /eic-opticks/README.md is written in an unsupported language. File is not indexed.

0001 This simulation package interfaces NVIDIA OptiX with Geant4 to accelerate
0002 optical photon transport for physics experiments. It supports detector
0003 geometries defined in the GDML format and is based on the work by Simon Blyth,
0004 whose original Opticks framework can be found
0005 [here](https://simoncblyth.bitbucket.io/opticks/).
0006 
0007 
0008 ## Prerequisites
0009 
0010 Before building or running this package, ensure that your system meets both the
0011 hardware and software requirements listed below.
0012 
0013 * A CUDA-capable NVIDIA GPU
0014 
0015 * CUDA 12+
0016 * NVIDIA OptiX 7+
0017 * Geant4 11+
0018 * CMake 3.18+
0019 * Python 3.8+
0020 
0021 OptiX releases have specific [minimum NVIDIA driver
0022 requirements](https://developer.nvidia.com/designworks/optix/downloads/legacy):
0023 
0024 | OptiX version | Release date  | Minimum driver required |
0025 |---            |---:           |---                      |
0026 | 9.0.0         | February 2025 | 570                     |
0027 | 8.1.0         | October 2024  | 555                     |
0028 | 8.0.0         | August 2023   | 535                     |
0029 | 7.7.0         | March 2023    | 530.41                  |
0030 | 7.6.0         | October 2022  | 522.25                  |
0031 | 7.5.0         | June 2022     | 515.48                  |
0032 | 7.4.0         | November 2021 | 495.89                  |
0033 | 7.3.0         | April 2021    | 465.84                  |
0034 | 7.2.0         | October 2020  | 455.28                  |
0035 | 7.1.0         | June 2020     | 450                     |
0036 | 7.0.0         | August 2019   | 435.80                  |
0037 
0038 Optionally, if you plan to develop or run the simulation in a containerized
0039 environment, ensure that your system has the following tools installed:
0040 
0041 * [Docker Engine](https://docs.docker.com/engine/install/)
0042 * NVIDIA container toolkit ([installation guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html))
0043 
0044 ## Build
0045 
0046 ```shell
0047 git clone https://github.com/BNLNPPS/eic-opticks.git
0048 cmake -S eic-opticks -B build
0049 cmake --build build
0050 ```
0051 
0052 ## Docker
0053 
0054 Build latest `eic-opticks` image by hand:
0055 
0056 ```shell
0057 docker build -t ghcr.io/bnlnpps/eic-opticks:latest https://github.com/BNLNPPS/eic-opticks.git
0058 ```
0059 
0060 Build and run for development:
0061 
0062 ```shell
0063 docker build -t ghcr.io/bnlnpps/eic-opticks:develop --target=develop .
0064 ```
0065 
0066 Example commands for interactive and non-interactive tests:
0067 
0068 ```shell
0069 docker run --rm -it -v $HOME/.Xauthority:/root/.Xauthority -e DISPLAY=$DISPLAY --net=host ghcr.io/bnlnpps/eic-opticks:develop
0070 
0071 docker run --rm -it -v $HOME:/esi -v $HOME/eic-opticks:/workspaces/eic-opticks -e DISPLAY=$DISPLAY -e HOME=/esi --net=host ghcr.io/bnlnpps/eic-opticks:develop
0072 
0073 docker run ghcr.io/bnlnpps/eic-opticks bash -c 'simg4ox -g tests/geom/sphere_leak.gdml -m tests/run.mac -c sphere_leak'
0074 ```
0075 
0076 
0077 ## Singularity
0078 
0079 ```shell
0080 singularity run --nv -B eic-opticks-prefix/:/opt/eic-opticks -B eic-opticks:/workspaces/eic-opticks docker://ghcr.io/bnlnpps/eic-opticks:develop
0081 ```
0082 
0083 
0084 ## Running a test job at NERSC (Perlmutter)
0085 
0086 To submit a test run of `eic-opticks` on Perlmutter, use the following example. Make sure to update
0087 any placeholder values as needed.
0088 
0089 ```
0090 sbatch scripts/submit.sh
0091 ```
0092 
0093 ```
0094 #!/bin/bash
0095 
0096 #SBATCH -N 1                    # number of nodes
0097 #SBATCH -C gpu                  # constraint: use GPU partition
0098 #SBATCH -G 1                    # request 1 GPU
0099 #SBATCH -q regular              # queue
0100 #SBATCH -J eic-opticks          # job name
0101 #SBATCH --mail-user=<USER_EMAIL>
0102 #SBATCH --mail-type=ALL
0103 #SBATCH -A m4402                # allocation account
0104 #SBATCH -t 00:05:00             # time limit (hh:mm:ss)
0105 
0106 # Path to your image on Perlmutter
0107 IMAGE="docker:bnlnpps/eic-opticks:develop"
0108 CMD='cd /src/eic-opticks && simg4ox -g $OPTICKS_HOME/tests/geom/sphere_leak.gdml -m $OPTICKS_HOME/tests/run.mac -c sphere_leak'
0109 
0110 # Launch the container using Shifter
0111 srun -n 1 -c 8 --cpu_bind=cores -G 1 --gpu-bind=single:1 shifter --image=$IMAGE /bin/bash -l -c "$CMD"
0112 ```
0113 
0114 
0115 ## Optical Surface Models in Geant4
0116 
0117 In Geant4, optical surface properties such as **finish**, **model**, and **type** are defined using enums in the
0118 `G4OpticalSurface` and `G4SurfaceProperty` header files:
0119 
0120 - [`G4OpticalSurface.hh`](https://github.com/Geant4/geant4/blob/geant4-11.3-release/source/materials/include/G4OpticalSurface.hh#L52-L113)
0121 - [`G4SurfaceProperty.hh`](https://github.com/Geant4/geant4/blob/geant4-11.3-release/source/materials/include/G4SurfaceProperty.hh#L58-L68)
0122 
0123 These enums allow users to configure how optical photons interact with surfaces, controlling behaviors like reflection,
0124 transmission, and absorption based on physical models and surface qualities. The string values corresponding to these
0125 enums (e.g. `"ground"`, `"glisur"`, `"dielectric_dielectric"`) can also be used directly in **GDML** files when defining
0126 `<opticalsurface>` elements for geometry. Geant4 will parse these attributes and apply the corresponding surface
0127 behavior.
0128 
0129 For a physics-motivated explanation of how Geant4 handles optical photon boundary interactions, refer to the [Geant4
0130 Application Developer Guide — Boundary
0131 Process](https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/TrackingAndPhysics/physicsProcess.html#boundary-process).
0132 
0133 ```gdml
0134 <gdml>
0135   ...
0136   <solids>
0137     <opticalsurface finish="ground" model="glisur" name="medium_surface" type="dielectric_dielectric" value="1">
0138       <property name="EFFICIENCY" ref="EFFICIENCY_DEF"/>
0139       <property name="REFLECTIVITY" ref="REFLECTIVITY_DEF"/>
0140     </opticalsurface>
0141   </solids>
0142   ...
0143 </gdml>
0144 ```
0145 
0146 
0147 ## Examples
0148 
0149 EIC-Opticks provides several examples demonstrating GPU-accelerated optical photon simulation:
0150 
0151 | Example | Physics | Geometry | Use Case |
0152 |---------|---------|----------|----------|
0153 | `GPUCerenkov` | Cerenkov only | Simple nested boxes (raindrop) | Basic Cerenkov testing |
0154 | `GPURaytrace` | Cerenkov + Scintillation | 8x8 CsI crystal + SiPM array | Realistic detector simulation |
0155 | `GPUPhotonSource` | Optical photons (torch) | Any GDML | G4 + GPU side-by-side validation |
0156 | `GPUPhotonSourceMinimal` | Optical photons (torch) | Any GDML | GPU-only test |
0157 | `GPUPhotonFileSource` | Optical photons (text file) | Any GDML | GPU-only, user-defined photons from file |
0158 
0159 ### Example 1: GPUCerenkov (Cerenkov Only)
0160 
0161 The `GPUCerenkov` example uses the **opticks_raindrop** geometry - a simple nested box configuration
0162 designed for testing Cerenkov photon production and GPU raytracing:
0163 
0164 ```
0165 Geometry: opticks_raindrop.gdml
0166 ├── VACUUM world (240×240×240 mm)
0167 │   └── Lead box (220×220×220 mm)
0168 │       └── Air box (200×200×200 mm)
0169 │           └── Water box (100×100×100 mm) ← Cerenkov medium
0170 ```
0171 
0172 When charged particles traverse the water volume above the Cerenkov threshold, optical photons
0173 are generated and traced on the GPU. This is a minimal example for validating the eic-opticks pipeline.
0174 
0175 ```bash
0176 # Run with raindrop geometry (Cerenkov only)
0177 GPUCerenkov -g tests/geom/opticks_raindrop.gdml -m run.mac
0178 ```
0179 
0180 **Source files:** `src/GPUCerenkov.cpp`, `src/GPUCerenkov.h`
0181 
0182 ### Example 2: GPURaytrace (Cerenkov + Scintillation)
0183 
0184 The `GPURaytrace` example demonstrates a realistic detector configuration with both Cerenkov
0185 and scintillation physics using the **8x8 SiPM array** geometry (not validated yet):
0186 
0187 ```
0188 Geometry: 8x8SiPM_w_CSI_optial_grease.gdml
0189 ├── Air world (100×100×100 mm)
0190 │   ├── 64 CsI crystal pixels (2×2×8 mm each) ← Scintillation + Cerenkov
0191 │   ├── Optical grease layer (17.4×17.4×0.1 mm)
0192 │   ├── Entrance window (17.4×17.4×0.1 mm)
0193 │   └── 64 SiPM active areas (2×2×0.2 mm each) ← Photon detectors
0194 ```
0195 
0196 This geometry models a pixelated scintillator calorimeter with:
0197 - **CsI(Tl) crystals**: Produce both Cerenkov and scintillation photons
0198 - **Optical coupling**: Grease and window layers for photon transmission
0199 - **SiPM readout**: 8×8 array of silicon photomultipliers with dead space modeling
0200 
0201 ```bash
0202 # Run with 8x8 SiPM array geometry (Cerenkov + Scintillation)
0203 GPURaytrace -g tests/geom/8x8SiPM_w_CSI_optial_grease.gdml -m tests/run.mac
0204 
0205 # Check output for Cerenkov (ID=0) and scintillation (ID=1) photons
0206 grep -c "CreationProcessID=0" opticks_hits_output.txt  # Cerenkov
0207 grep -c "CreationProcessID=1" opticks_hits_output.txt  # Scintillation
0208 ```
0209 
0210 **Source files:** `src/GPURaytrace.cpp`, `src/GPURaytrace.h`
0211 
0212 ### Example 3: GPUPhotonSource (G4 + GPU Validation)
0213 
0214 `GPUPhotonSource` generates optical photons from a configurable torch source and runs
0215 both Geant4 and eic-opticks GPU simulation in parallel on the same input photons. This
0216 enables direct comparison of hit counts and positions between the two engines.
0217 
0218 Both engines detect photons using the same mechanism: border surface physics. On the G4
0219 side the `SteppingAction` records a hit when `G4OpBoundaryProcess` reports Detection at
0220 the optical surface, matching how eic-opticks detects photons on the GPU.
0221 
0222 | Argument | Description | Default |
0223 |----------|-------------|---------|
0224 | `-g, --gdml` | Path to GDML file | `geom.gdml` |
0225 | `-c, --config` | Config file name (without `.json`) | `dev` |
0226 | `-m, --macro` | Path to G4 macro | `run.mac` |
0227 | `-i, --interactive` | Open interactive viewer | off |
0228 | `-s, --seed` | Fixed random seed | time-based |
0229 
0230 ```bash
0231 GPUPhotonSource -g tests/geom/opticks_raindrop.gdml -c dev -m run.mac -s 42
0232 ```
0233 
0234 **Output:**
0235 - `opticks_hits_output.txt` — eic-opticks GPU hits, one line per hit
0236 - `g4_hits_output.txt` — Geant4 hits in the same format
0237 
0238 Hit format (both files): `time wavelength (pos_x, pos_y, pos_z) (mom_x, mom_y, mom_z) (pol_x, pol_y, pol_z)`
0239 
0240 **Source files:** `src/GPUPhotonSource.cpp`, `src/GPUPhotonSource.h`
0241 
0242 ### Example 4: GPUPhotonSourceMinimal (GPU-Only)
0243 
0244 `GPUPhotonSourceMinimal` is a stripped-down version of `GPUPhotonSource` that runs
0245 **only** eic-opticks GPU simulation. All G4 optical photon tracking infrastructure
0246 (sensitive detectors, stepping actions, tracking actions) is removed. Geant4 is used
0247 solely for geometry loading and hosting the event loop.
0248 
0249 Use this when you only need GPU results and want faster execution.
0250 
0251 | Argument | Description | Default |
0252 |----------|-------------|---------|
0253 | `-g, --gdml` | Path to GDML file | `geom.gdml` |
0254 | `-c, --config` | Config file name (without `.json`) | `dev` |
0255 | `-m, --macro` | Path to G4 macro | `run.mac` |
0256 | `-i, --interactive` | Open interactive viewer | off |
0257 | `-s, --seed` | Fixed random seed | time-based |
0258 
0259 ```bash
0260 GPUPhotonSourceMinimal -g tests/geom/opticks_raindrop.gdml -c dev -m run.mac -s 42
0261 ```
0262 
0263 **Output:** `opticks_hits_output.txt` — one hit per line
0264 
0265 **Source files:** `src/GPUPhotonSourceMinimal.cpp`, `src/GPUPhotonSourceMinimal.h`
0266 
0267 ### Example 5: GPUPhotonFileSource (File Input, GPU-Only)
0268 
0269 `GPUPhotonFileSource` reads optical photons from a plain text file and runs
0270 GPU-only simulation via eic-opticks. Each line in the input file defines one
0271 photon with 11 space-separated values:
0272 
0273 ```
0274 # pos_x pos_y pos_z time mom_x mom_y mom_z pol_x pol_y pol_z wavelength
0275 -10.0 -30.0 -90.0  0.0  0.0 0.287348 0.957826  1.0 0.0 0.0  420.0
0276 -10.0 -30.0 -90.0  0.0  0.0 0.287348 0.957826  1.0 0.0 0.0  450.0
0277 ```
0278 
0279 - Positions are in mm, time in ns, wavelength in nm
0280 - Momentum direction should be normalized
0281 - Polarization should be transverse to momentum and normalized
0282 - Lines starting with `#` are comments and blank lines are skipped
0283 
0284 | Argument | Description | Default |
0285 |----------|-------------|---------|
0286 | `-g, --gdml` | Path to GDML file | `geom.gdml` |
0287 | `-p, --photons` | Path to input photon text file | (required) |
0288 | `-m, --macro` | Path to G4 macro | `run.mac` |
0289 | `-i, --interactive` | Open interactive viewer | off |
0290 | `-s, --seed` | Fixed random seed | time-based |
0291 
0292 ```bash
0293 GPUPhotonFileSource -g tests/geom/opticks_raindrop.gdml -p my_photons.txt -m run.mac
0294 ```
0295 
0296 **Output:** `opticks_hits_output.txt` — one hit per line
0297 
0298 **Source files:** `src/GPUPhotonFileSource.cpp`, `src/GPUPhotonFileSource.h`
0299 
0300 ### Torch configuration
0301 
0302 `GPUPhotonSource` and `GPUPhotonSourceMinimal` read photon source parameters from a
0303 JSON config file (default `config/dev.json`). Key fields:
0304 
0305 | Field | Description |
0306 |-------|-------------|
0307 | `type` | Source shape: `disc`, `sphere`, `point` |
0308 | `radius` | Size of the source area (mm) |
0309 | `pos` | Center position `[x, y, z]` (mm) |
0310 | `mom` | Emission direction `[x, y, z]` (normalized automatically) |
0311 | `numphoton` | Number of photons to generate |
0312 | `wavelength` | Photon wavelength (nm) |
0313 
0314 ### Code Differences
0315 
0316 | Feature | GPUCerenkov | GPURaytrace | GPUPhotonSource | GPUPhotonSourceMinimal | GPUPhotonFileSource |
0317 |---------|-------------|-------------|-----------------|----------------------|---------------------|
0318 | Cerenkov genstep collection | ✓ | ✓ | ✗ | ✗ | ✗ |
0319 | Scintillation genstep collection | ✗ | ✓ | ✗ | ✗ | ✗ |
0320 | Torch photon generation | ✗ | ✗ | ✓ | ✓ | ✗ |
0321 | Photon input from text file | ✗ | ✗ | ✗ | ✗ | ✓ |
0322 | G4 optical photon tracking | ✓ | ✓ | ✓ | ✗ | ✗ |
0323 | GPU simulation (eic-opticks) | ✓ | ✓ | ✓ | ✓ | ✓ |
0324 | Multi-threaded | ✓ | ✓ | ✗ | ✗ | ✗ |
0325 
0326 `GPUCerenkov` and `GPURaytrace` collect gensteps from charged particle interactions and
0327 pass them to eic-opticks for GPU photon generation and tracing. `GPUPhotonSource` and
0328 `GPUPhotonSourceMinimal` instead generate photons directly from a torch configuration.
0329 `GPUPhotonSource` runs both G4 and GPU tracking for validation, while
0330 `GPUPhotonSourceMinimal` skips G4 tracking entirely for a lean simplistic code so showcase what is needed for GPU only.
0331 `GPUPhotonFileSource` reads photons from a user-provided text file, enabling custom photon
0332 distributions without code changes.
0333 
0334 
0335 ### GDML Scintillation Properties for Geant4 11.x + eic-opticks
0336 
0337 For scintillation to work with both Geant4 11.x and eic-opticks GPU simulation, the GDML
0338 must define properties using the correct syntax:
0339 
0340 1. **Const properties** (yield, time constants) must use `coldim="1"` matrices:
0341 ```xml
0342 <define>
0343     <matrix coldim="1" name="SCINT_YIELD" values="5000.0"/>
0344     <matrix coldim="1" name="FAST_TIME_CONST" values="21.5"/>
0345 </define>
0346 ```
0347 
0348 2. **Both old and new style property names** are required for eic-opticks compatibility:
0349 ```xml
0350 <material name="Crystal">
0351     <!-- New Geant4 11.x names -->
0352     <property name="SCINTILLATIONYIELD" ref="SCINT_YIELD"/>
0353     <property name="SCINTILLATIONCOMPONENT1" ref="SCINT_SPECTRUM"/>
0354     <property name="SCINTILLATIONTIMECONSTANT1" ref="FAST_TIME_CONST"/>
0355     <!-- Old-style names for Opticks U4Scint -->
0356     <property name="FASTCOMPONENT" ref="SCINT_SPECTRUM"/>
0357     <property name="SLOWCOMPONENT" ref="SCINT_SPECTRUM"/>
0358     <property name="REEMISSIONPROB" ref="REEMISSION_PROB"/>
0359 </material>
0360 ```
0361 
0362 See `tests/geom/8x8SiPM_w_CSI_optial_grease.gdml` for a complete working example.
0363 
0364 ## User/developer defined inputs
0365 
0366 ### Defining primary particles
0367 
0368 There are certain user defined inputs that the user/developer has to define. In
0369 the ```src/GPUCerenkov``` example that imports ```src/GPUCerenkov.h``` we provide
0370 a working example with a simple geometry. The User/developer has to change the
0371 following details: **Number of primary particles** to simulate in a macro file
0372 and the **number of G4 threads**. For example:
0373 
0374 ```
0375 /run/numberOfThreads {threads}
0376 /run/verbose 1
0377 /process/optical/cerenkov/setStackPhotons {flag}
0378 /run/initialize
0379 /run/beamOn 500
0380 ```
0381 
0382 Here setStackPhotons defines **whether G4 will propagate optical photons or
0383 not**. In production eic-opticks (GPU) takes care of the optical photon propagation.
0384 Additionally the user has to define the **starting position**, **momentum** etc
0385 of the primary particles define in the **GeneratePrimaries** function in
0386 ```src/GPUCerenkov.h```. The hits of the optical photons are returned in the
0387 **EndOfRunAction** function. If more photons are simulated than can fit in the
0388 GPU RAM the execution of a GPU call should be moved to **EndOfEventAction**
0389 together with retriving the hits.
0390 
0391 ### Loading in geometry into EIC-Opticks
0392 
0393 EIC-Opticks can import geometries with GDML format automatically. There are
0394 about 10 primitives supported now, eg. G4Box. G4Trd or G4Trap are not supported
0395 yet, we are working on them. ```GPUCerenkov``` takes GDML files through
0396 arguments, eg. ```GPUCerenkov -g mygdml.gdml```.
0397 
0398 The GDML must define all optical properties of surfaces of materials including:
0399 - Efficiency (used by EIC-Opticks to specify detection efficiency and assign
0400   sensitive surfaces)
0401 - Refractive index
0402 - Group velocity
0403 - Reflectivity
0404 - Etc.
0405 
0406 
0407 ## Performance studies
0408 
0409 In order to quantify the speed-up achieved by EIC-Opticks compared to G4 we
0410 provide a python code that runs the same G4 simulation with and without tracking
0411 optical photons in G4. The difference of the runs will yield the time required
0412 to simulate photons. Meanwhile the same photons are simulated on GPU with
0413 EIC-Opticks and the simulation time is saved.
0414 
0415 ```
0416 mkdir -p /tmp/out/dev
0417 mkdir -p /tmp/out/rel
0418 
0419 docker build -t eic-opticks:perf-dev --target=develop
0420 docker run --rm -t -v /tmp/out:/tmp/out eic-opticks:perf-dev run-performance -o /tmp/out/dev
0421 
0422 docker build -t eic-opticks:perf-rel --target=release
0423 docker run --rm -t -v /tmp/out:/tmp/out eic-opticks:perf-rel run-performance -o /tmp/out/rel
0424 ```
0425 
0426 ### Debug Analysis with `optiphy/ana/photon_history_summary.py`
0427 
0428 The script analyzes GPU optical photon simulation output to debug where photons
0429 went and why: which were detected, absorbed, scattered, or trapped bouncing
0430 forever, and whether the physics (wavelength shifting, energy conservation)
0431 worked correctly. Without it, the simulation is a black box that only reports
0432 a hit count.
0433 
0434 #### Prerequisites
0435 
0436 The simulation must be run with `OPTICKS_EVENT_MODE` set so that output arrays
0437 are saved to disk. The default mode (`Minimal`) only gathers hits into memory
0438 and does **not** write `.npy` files.
0439 
0440 ```bash
0441 export OPTICKS_EVENT_MODE=HitPhoton   # saves photon, hit, inphoton, record, seq
0442 ```
0443 
0444 Valid modes for this script: `HitPhoton`, `DebugLite`, `DebugHeavy`.
0445 
0446 #### Running a simulation with output saving
0447 
0448 ```bash
0449 OPTICKS_EVENT_MODE=HitPhoton GPUPhotonSourceMinimal -g tests/geom/wls_test.gdml -c wls_test -m tests/run.mac -s 42
0450 ```
0451 
0452 #### Output file location
0453 
0454 Opticks writes `.npy` arrays to:
0455 
0456 ```
0457 $TMP/GEOM/$GEOM/<ExecutableName>/ALL0_no_opticks_event_name/A000/
0458 ```
0459 
0460 | Variable | Default | Override |
0461 |----------|---------|---------|
0462 | `$TMP` | `/tmp/$USER/opticks` | `export TMP=/path/to/tmp` |
0463 | `$GEOM` | `GEOM` | `export GEOM=mygeom` |
0464 
0465 For example, with defaults:
0466 
0467 ```
0468 /tmp/$USER/opticks/GEOM/GEOM/GPUPhotonSourceMinimal/ALL0_no_opticks_event_name/A000/
0469 ├── photon.npy      # (N, 4, 4) float32 — final state of all photons
0470 ├── hit.npy         # (H, 4, 4) float32 — detected photons only
0471 ├── inphoton.npy    # (N, 4, 4) float32 — input photons before simulation
0472 ├── record.npy      # (N, 32, 4, 4) float32 — step-by-step history (up to 32 steps)
0473 ├── seq.npy         # (N, 2, 2) uint64 — compressed step sequence per photon
0474 ├── genstep.npy     # generation step parameters
0475 └── domain.npy      # domain compression parameters
0476 ```
0477 
0478 #### Running the analysis
0479 
0480 ```bash
0481 # Basic summary tables:
0482 python optiphy/ana/photon_history_summary.py <event_folder>
0483 
0484 # Auto-resolves A000 subfolder:
0485 python optiphy/ana/photon_history_summary.py /tmp/$USER/opticks/GEOM/GEOM/GPUPhotonSourceMinimal/ALL0_no_opticks_event_name
0486 
0487 # Show step-by-step trace for specific photons:
0488 python optiphy/ana/photon_history_summary.py <path> --trace 0,227,235
0489 
0490 # Show all non-detected (lost) photons with full traces:
0491 python optiphy/ana/photon_history_summary.py <path> --lost
0492 
0493 # Filter by terminal flag:
0494 python optiphy/ana/photon_history_summary.py <path> --flag BULK_ABSORB
0495 
0496 # Show per-photon detail for first 20 photons:
0497 python optiphy/ana/photon_history_summary.py <path> --detail 20
0498 ```
0499 
0500 #### Output tables
0501 
0502 The script prints six summary tables by default: photon outcomes by terminal
0503 flag with hit rate and MaxBounce truncation count, cumulative flagmask history
0504 distribution, wavelength statistics with energy conservation check, position/time
0505 stats, ranked step sequence histories decoded from seq nibbles, and step count
0506 distribution flagging truncated photons. The `--lost`, `--trace`, and `--flag`
0507 options drill into specific photons with full step-by-step position, wavelength,
0508 and flag traces for debugging exactly where and why a photon died.
0509 
0510 | Table | Description |
0511 |-------|-------------|
0512 | **Photon Outcomes** | Counts by terminal flag (SURFACE_DETECT, BULK_ABSORB, etc.) with boundary indices, hitmask, and MaxBounce truncation count |
0513 | **Photon Histories** | Unique cumulative flagmask combinations (e.g. TO\|RE\|BT\|SD) |
0514 | **Wavelength Analysis** | Input vs output wavelengths, shift count, energy conservation check |
0515 | **Position / Time** | Radius and time statistics |
0516 | **Sequence Histories** | Top N step-by-step sequences from seq.npy (e.g. "TO RE BT SD") |
0517 | **Step Counts** | Distribution of steps per photon, flags truncated ones at max |
0518 
0519 #### Photon data layout (`sphoton.h`)
0520 
0521 Each photon is a `(4, 4)` float32 matrix — four quads of four floats.
0522 `photon.npy` holds the final state, `hit.npy` the detected subset,
0523 `inphoton.npy` the initial state, and `record.npy` stores the full
0524 step-by-step history (up to 32 bounces per photon, same quad layout per step).
0525 
0526 | Quad | .x | .y | .z | .w |
0527 |------|-----|-----|-----|-----|
0528 | q0 | pos.x | pos.y | pos.z | time |
0529 | q1 | mom.x | mom.y | mom.z | iindex |
0530 | q2 | pol.x | pol.y | pol.z | wavelength (nm) |
0531 | q3 | orient\_boundary\_flag | identity | index | flagmask |
0532 
0533 #### q3 bit packing
0534 
0535 q3 is stored as float32 but interpreted as uint32 via `.view(np.uint32)`.
0536 This is where the physics outcome lives:
0537 
0538 **q3.x** (`orient_boundary_flag`) packs three fields into 32 bits:
0539 
0540 ```
0541 bit 31          : orient sign (1 = inward-facing surface normal)
0542 bits 30-16      : boundary index (which pair of materials the photon is at)
0543 bits 15-0       : terminal flag (what happened last)
0544 ```
0545 
0546 Python extraction:
0547 ```python
0548 q3    = photon[:, 3, :].view(np.uint32)
0549 flag  = q3[:, 0] & 0xFFFF             # terminal flag
0550 bnd   = (q3[:, 0] >> 16) & 0x7FFF     # boundary index
0551 ```
0552 
0553 **q3.w** (`flagmask`) is the cumulative OR of every flag set during the
0554 photon's lifetime. Each call to `set_flag(f)` does both
0555 `flag = f` (replace) and `flagmask |= f` (accumulate). So a single integer
0556 tells you the full history — e.g. `0x0854` = `TO|RE|SD|BT` means torch
0557 generation, WLS re-emission, boundary transmit, surface detect.
0558 
0559 #### Sequence history encoding (`sseq.h`)
0560 
0561 `seq.npy` shape is `(N, 2, 2)` uint64. Each photon gets two pairs of uint64:
0562 `seqhis[2]` (flag history) and `seqbnd[2]` (boundary history).
0563 
0564 Each uint64 holds 16 slots of 4-bit nibbles (total 32 slots across the pair).
0565 Flag nibbles store the **find-first-set** (FFS) of the flag bit:
0566 TORCH (bit 2) stores as nibble 3, SURFACE_DETECT (bit 6) stores as 7.
0567 
0568 ```python
0569 seqhis = seq[:, 0, :]   # two uint64 per photon
0570 nibble = (seqhis[0] >> (4 * slot)) & 0xF   # for slot 0-15
0571 flag   = 1 << (nibble - 1)                  # reconstruct flag
0572 ```
0573 
0574 This gives a compact chronological per-step history in 16 bytes per photon.
0575 
0576 #### Hit determination and MaxBounce
0577 
0578 A photon is a "hit" when `(flagmask & hitmask) == hitmask`. The default
0579 hitmask is `SD` (SURFACE_DETECT = 0x40), but with CustomART PMTs it may be
0580 `EC` (EFFICIENCY_COLLECT = 0x2000). The script reads the actual hitmask from
0581 `NPFold_meta.txt` in the event folder.
0582 
0583 Photons that exhaust the bounce limit (`MaxBounce`, default 31, giving 32
0584 steps) receive **no special flag** — the propagation loop simply exits and the
0585 photon keeps whatever terminal flag it had at its last step. These are typically
0586 photons trapped by total internal reflection. The script detects them by
0587 checking `step_count == max_steps` and reports the count in the outcome table.
0588 
0589 #### Flag definitions (`OpticksPhoton.h`)
0590 
0591 Flags are a power-of-two enum where each GPU physics process gets one bit:
0592 
0593 | Flag | Hex | Abbrev | Description |
0594 |------|-----|--------|-------------|
0595 | CERENKOV | 0x0001 | CK | Cerenkov generation |
0596 | SCINTILLATION | 0x0002 | SI | Scintillation generation |
0597 | TORCH | 0x0004 | TO | Torch (flashlight) source |
0598 | BULK_ABSORB | 0x0008 | AB | Absorbed in bulk material |
0599 | BULK_REEMIT | 0x0010 | RE | Re-emitted (scintillation or WLS) |
0600 | BULK_SCATTER | 0x0020 | SC | Rayleigh scattered |
0601 | SURFACE_DETECT | 0x0040 | SD | Detected at surface |
0602 | SURFACE_ABSORB | 0x0080 | SA | Absorbed at surface |
0603 | SURFACE_DREFLECT | 0x0100 | DR | Diffuse reflection at surface |
0604 | SURFACE_SREFLECT | 0x0200 | SR | Specular reflection at surface |
0605 | BOUNDARY_REFLECT | 0x0400 | BR | Fresnel reflection at boundary |
0606 | BOUNDARY_TRANSMIT | 0x0800 | BT | Transmitted through boundary |
0607 | NAN_ABORT | 0x1000 | NA | Aborted due to NaN (geometry error) |
0608 | EFFICIENCY_COLLECT | 0x2000 | EC | Collected by CustomART PMT efficiency |
0609 | EFFICIENCY_CULL | 0x4000 | EL | Culled by CustomART PMT efficiency |
0610 | MISS | 0x8000 | MI | Missed all geometry |