33.7. Counting Optical Photons
Table of Contents
Introduction
Counting optical photons is central to simulations of scintillators, Cherenkov detectors, and photodetectors such as PMTs and SiPMs. In Geant4 you never count “light intensity” directly. Instead you track individual optical photons and decide when a photon has been “detected.” This chapter explains practical strategies to count those photons in a Geant4 application and to convert raw photon counts into quantities such as photoelectron numbers or detector signals.
The focus here is on how to record and interpret optical photons that reach your detector, not on how those photons are produced or transported, which is covered in other chapters.
What Does It Mean to “Count” Optical Photons?
In a real detector you never see every optical photon that reaches the sensor. Some are absorbed in dead layers, some reflect away, and only a fraction produce a measurable signal. In a simulation you can choose different levels of realism:
- Count every optical photon that arrives at a sensor surface.
- Count only the photons that would generate a photoelectron, using a quantum efficiency model.
- Simulate a more detailed electronic response such as pulses or integrated charge.
For most applications in this course, the second option, counting photoelectrons, is the most relevant. You will typically define a “photon detection event” whenever an optical photon reaches a simplified photodetector and is probabilistically converted into a detected hit based on quantum efficiency.
A counted optical photon in your analysis should always correspond to a clearly defined physical quantity, most commonly a photoelectron created at a photodetector surface. Be explicit in your code and documentation about what a “count” represents.
Strategies for Counting Optical Photons in Geant4
There are three common strategies to count optical photons, each with advantages for different use cases.
Counting Photons in a Sensitive Detector
The most structured method is to attach a sensitive detector to the logical volume that represents your photodetector, for example a SiPM window or PMT photocathode. Every time an optical photon interacts with this volume, the sensitive detector creates a hit.
You implement this by deriving a class from G4VSensitiveDetector, such as MyPhotonSD, and overriding ProcessHits(G4Step step, G4TouchableHistory history).
Inside ProcessHits, you first check that the current track is an optical photon. Then you can decide whether the photon is detected, and if so, record a hit.
A typical logical structure is:
- Check particle type.
- Decide detection using quantum efficiency or other probability.
- If detected, create a hit that stores time, position, and ID of the detector element.
- Increment per-event counters or fill analysis objects.
This method makes use of the existing Geant4 hit and collection mechanisms, which integrates naturally with the analysis system and event structure.
Counting Photons in a Boundary Process
Another common approach is to count photons inside the optical boundary process. The class G4OpBoundaryProcess handles reflection, refraction, and absorption at surfaces. You can either use it as is and interpret its status, or implement custom logic around it.
If you want to trigger counting precisely when an optical photon crosses from one specific volume to another, you can:
- Check in
SteppingActionwhether the step is at a boundary. - Query the boundary process for its
GetStatus(). - When status indicates that the photon is transmitted into the photodetector, register a photon count.
This is more advanced, but it offers fine control at the interface of materials and surfaces and can distinguish between reflected, absorbed, or transmitted photons.
Counting Photons in SteppingAction
For simple setups you can implement counting directly in your SteppingAction. In UserSteppingAction(const G4Step* step):
- Check that the track is an optical photon.
- Check whether the post step point is inside a detector volume, or whether the step crossed into such a volume.
- If so, increment a counter and optionally terminate the photon track.
This approach is quick to implement for simple examples, although it does not make use of hits collections and is less structured for complex detectors with many channels.
Using a Sensitive Detector for Optical Photons
Using a sensitive detector is usually the cleanest way to count photons when you have one or more photodetector volumes.
Defining an Optical Photon Sensitive Detector
You begin by creating a class derived from G4VSensitiveDetector, for example:
MyPhotonSD.hh declares the class and the hit collection, while MyPhotonSD.cc defines logic in ProcessHits.
Key elements inside ProcessHits are:
You retrieve the track and confirm that the particle is an optical photon by comparing its definition with G4OpticalPhoton::Definition().
You extract the global time using step->GetPreStepPoint()->GetGlobalTime() or GetPostStepPoint() if you define detection at the post step.
You get the position from the post step point and the detector element ID by using G4TouchableHistory, usually through step->GetPreStepPoint()->GetTouchable().
You create a new hit object that stores at least the detection time, position, and channel ID. Then you add it to the event’s hit collection.
This gives you a clear list of detected photons for each event.
Assigning the Sensitive Detector to a Photodetector Volume
Once the sensitive detector class is implemented, you must attach it to the appropriate logical volumes in your detector construction or in a dedicated SD manager class.
You register the sensitive detector with the G4SDManager, then call SetSensitiveDetector on the logical volume representing the photodetector surface or active region. Any optical photon that enters or interacts inside that logical volume will trigger ProcessHits.
It is important to choose the right volume. Often, you use a very thin volume that stands for the photocathode or silicon active region. That gives you a well defined location where detection occurs and avoids counting photons that only pass through insensitive material.
Quantum Efficiency and Detection Probability
In most detectors, not every photon that hits the photosensitive area creates a photoelectron. The fraction that is detected is called the quantum efficiency, often abbreviated QE, and it can depend on wavelength.
Modeling Quantum Efficiency
Quantum efficiency gives the probability that a photon of a given wavelength is detected. A simple model assumes a constant QE over all relevant wavelengths:
$$ P_{\text{detect}} = \text{QE} $$
For a wavelength dependent QE you can store a table of $(\lambda, \text{QE})$ values and interpolate between them. In Geant4 optical physics, wavelength is usually expressed in energy units, so you may define the QE as a function of photon energy instead, for example using a G4MaterialPropertiesTable.
A typical approach uses a random number $u$ uniformly distributed in $[0,1)$:
To decide if a photon is detected, draw a random number $u$ and detect the photon if
$$ u < P_{\text{detect}} \quad \text{(for example the quantum efficiency)}. $$
This converts a continuous detection probability into a binary detection event.
In your sensitive detector, you retrieve a random number from the Geant4 random engine, compute $P_{\text{detect}}$ for the current photon, and then decide whether the photon is detected. If it is detected, you create a hit and count it. If not, you simply let the track continue or kill it, depending on your choice.
Relation Between Photons and Photoelectrons
The mean number of photoelectrons for $N_{\gamma}$ photons arriving at the detector is
$$ \langle N_{\text{pe}} \rangle = \text{QE} \cdot N_{\gamma}. $$
In a Monte Carlo simulation, you do not use only this mean value, you explicitly simulate the random number of detected photons using the Bernoulli trial described above. This ensures that fluctuations in the number of detected photons are naturally included, which is crucial for realistic detector response.
If you want a purely analytical estimate, for example to check your simulation, you can use Poisson statistics if photons are independent and rare.
Counting Photons per Event and per Detector Channel
Once you detect individual photons, you need to organize the information per event and per detector element.
Per Event Counting
The natural place to accumulate counts for each event is in EventAction. You typically do not sum directly inside the sensitive detector, because event boundaries are managed at a higher level.
A common pattern is:
You use hits collections. Each sensitive detector creates hits, and at the end of the event you sum the number of hits in each collection.
Alternatively, you update a per event counter when a hit is created. It can be stored in an event level data structure that resets at the beginning of each event.
In both cases, you finally fill histograms or ntuples with variables such as the total number of detected photons in the event, the number of hits within some time window, or the time distribution of hits.
Per Detector Channel Counting
Real photodetector systems usually have many channels. You might have a ring of SiPMs or an array of PMTs. In that case each hit must include a channel identifier. This is typically derived from a copy number or other information stored in the G4TouchableHistory of the step.
A typical design stores the channel ID as an integer member in the hit class. You then allocate per channel accumulators, for example a vector of integers indexed by channel ID, and increase the appropriate entry for each hit. At the end of the event you have one count per channel, which you can output or analyze.
This channel index forms the basis for building images, position reconstruction, or energy sharing calculations.
Timing Information and Time Windows
Counting optical photons is not only about how many photons are detected, but often also about when they are detected. Many detectors rely on timing for coincidence measurements, time of flight, or pile up studies.
Recording Photon Arrival Times
Each hit you create should include the global time when the photon was detected. You can get this time from the step points. Usually you record GetGlobalTime() at the moment you decide that detection occurs.
This time is measured from the start of the event. If your primary particles have their own time distribution, it will be reflected in the photon detection times.
You can then build histograms of detection times to study scintillation decay, optical propagation, or detector time resolution.
Applying Time Windows
To emulate an electronic gate or coincidence window, you apply selection criteria based on the detection times. For example, to count photons in the time window $[t_1, t_2]$, you count only those hits where
$$ t_1 \leq t_{\text{hit}} < t_2. $$
When applying a time window, always define it precisely in terms of global time (or another explicit time reference) and consistently apply
$$ t_{\text{start}} \leq t_{\text{hit}} < t_{\text{end}} $$
for counting or coincidence logic.
You implement this either directly when creating hits, by discarding hits outside the window, or later in EventAction or in offline analysis, by filtering the list of hits.
For coincidence measurements, for example in PET, you count events only when two or more detectors register hits within a narrow time window. That is done by comparing hit times across channels.
Avoiding Double Counting and Managing Photon Tracks
When counting optical photons you must avoid counting the same photon more than once. This can happen if your logic is triggered multiple times for a single photon, for example if the photon undergoes several steps inside the sensitive volume.
Defining a Clear Detection Criterion
The simplest way to avoid double counting is to define detection at a single, well defined condition, such as:
Photon first crossing from the scintillator into the photodetector active volume.
Photon’s first step within the detector sensitive volume.
Photon’s absorption in a special “photocathode” volume.
You implement this by checking that the pre step point is outside and the post step point is inside the volume of interest, or by using a boundary status from the optical boundary process.
Killing the Track After Detection
A practical method to ensure that a detected photon is counted only once is to terminate its track after detection. Right after you create a hit, you can set:
track->SetTrackStatus(fStopAndKill);
This stops the photon and prevents further steps or hits.
If you terminate an optical photon track after detection, you must ensure that you have already recorded all information you need such as time, position, and channel ID, because no further steps or interactions of that photon will be simulated.
This approach improves performance and prevents double counting, but it must be used carefully if you want to model partial absorption or secondary optical processes after detection, which in simple detector models are usually ignored.
Integrating Photon Counting with the Geant4 Analysis System
Once you have counted photons, you usually want to store the results and analyze them.
Histograms of Photon Counts
A very common first analysis is to build a histogram of the number of detected photons per event. That can represent, for example, the light yield of a scintillator for a given energy deposition.
Using G4AnalysisManager, you can create a 1D histogram where the x axis is the number of detected photons or photoelectrons, and fill it once per event with the total count for that event. Another useful histogram is the number of photons per channel, which can be used to see response uniformity.
Ntuples for Detailed Analysis
For detailed studies you can create ntuples with one row per event that include:
Total detected photons per event.
Photon counts per detector channel.
Timing variables such as earliest hit time, mean hit time, or number of hits in a specific time window.
Additional context such as deposited energy, event ID, or particle type.
After the run finishes, you write the ntuples to file, for example in ROOT format, and perform more advanced analysis, like fitting timing distributions, reconstructing interaction positions, or comparing simulated light yields with measurements.
Relating Simulated Photon Counts to Detector Signals
Raw photon counts or photoelectron counts often need to be converted into detector signals for comparison with real data, such as charge, voltage, or ADC channels.
From Photoelectrons to Signal
A simple linear model relates the mean signal amplitude $A$ to the number of photoelectrons:
$$ A = G \cdot N_{\text{pe}}, $$
where $G$ is an effective gain factor in suitable units. For example, for a PMT the gain might convert each photoelectron into a fixed mean charge.
In a more realistic model, there are fluctuations in gain, electronic noise, and saturation. You can implement a simple smearing by drawing the signal amplitude from a Gaussian distribution:
$$ A_{\text{measured}} \sim \mathcal{N}\left(G \cdot N_{\text{pe}}, \sigma^2(N_{\text{pe}})\right), $$
where $\sigma^2$ may include contributions from Poisson statistics of the photoelectrons, gain variation, and electronics noise.
In the context of this course, you can treat $N_{\text{pe}}$ as the primary simulation result and apply the conversion to signals either in C++ or in a later ROOT analysis step.
Comparing with Experimental Data
When comparing simulation with measured data, make sure that the same definition of “count” is used. If the experiment reports photoelectrons, your simulated counts should represent photoelectrons, including QE and collection efficiency. If the experiment reports integrated charge or ADC channels, you need to apply a conversion from photoelectrons to those quantities and include detector resolution.
Clear documentation is essential. In your analysis scripts and reports, always state whether a histogram is in units of photons, photoelectrons, or electronic signal, and what assumptions you used for quantum efficiency and gain.
Summary
Counting optical photons in Geant4 means defining and recording detection events for individual optical photons, usually at photodetector volumes. The main ingredients are:
A sensitive detector or equivalent logic in SteppingAction or at a boundary process to detect photons when they interact with a detector volume.
A probabilistic model of detection, typically based on quantum efficiency, to convert arriving photons into photoelectron counts.
Careful per event and per channel accumulation of hits, with timing information and optional time windows for coincidence or gating.
Avoidance of double counting by defining a unique detection criterion and often terminating photon tracks after detection.
Integration with the Geant4 analysis system to create histograms and ntuples of photon counts, times, and channel responses.
When designed carefully, this approach lets you build realistic simulations of optical detectors that can be directly compared with experimental data in terms of photoelectrons, timing distributions, and detector signals.
Views: 11
KAHIBARO