Evaluating trigger efficiencies with TISTOS and TriggerCalib
Learning Objectives
- Learn how the TISTOS method is used to calculate trigger efficiencies
- Learn how to use TriggerCalib to apply the TISTOS method
- This is where you'll find example python scripts for running the TriggerCalib tool
Other documentation
This section summarises two main sources:
- "TriggerCalib: a turnkey package for estimating LHCb trigger efficiencies" J. Albrecht et al., 2025, [arXiv:2505.15951] (submitted to JINST)
- Official TriggerCalib documentation
These sources go into more detail on both TISTOS and TriggerCalib—if you have questions, these may well have the answers!
In analysis, it is important to have a strong understanding of the selections applied to your data. Generally, efficiencies can be computed by examining the effect of a selection in MC, taking the ratio of candidates before and after the selection as the efficiency. This works well for simple properties such as kinematic distributions, but breaks down when you try to apply this method to complex properties, which may not be well-modelled in the data. One such example is of trigger selections, which typically depend on many properties, often including properties such as PID variables. If we wish to understand trigger efficiencies, we must look for a method which let's us use the data directly.
The TISTOS method
The TISTOS method gives us a data-driven alternative to evaluate trigger efficiencies. If we try to directly apply the method described above to data, our trigger efficiency \(\varepsilon\) would be written as
\( \varepsilon_\mathrm{Trig.} = \frac{N_\mathrm{Trig.}}{N_\mathrm{Sel.}} \)
where \(N_\mathrm{Sel.}\) and \(N_\mathrm{Trig.}\) are the number of events before and after the trigger, respectively. The problem is that the trigger is inherently destructive: we don't save events which we don't trigger on! TISTOS gives us a way to get around this.
To briefly summarise the method, as described in [1], we start by defining our signal candidate within the event, e.g., a \(B^+\to J/\psi\left(\mu^+\mu^-\right) K^+\) decay. A positive trigger decision could be caused by our signal candidate (or part of it, e.g., one of the \(\mu^\pm\) 1), we call this trigger on signal (TOS), or from somewhere else in our event,we call this trigger independent of signal (TIS),. We can also have the case where an event is both TIS and TOS, where our signal candidate was sufficient for a trigger to fire, but the trigger also fires on another object, e.g., from the decay of the other \(b\)-quark in the \(b\overline{b}\) pair from which we get our \(B^+\). We label these events as TISTOS.
To get our trigger efficiency, we do some maneouvering of our event counts. Firstly, writing our \(\varepsilon_\mathrm{Trig.}\) out,
\( \varepsilon_\mathrm{Trig.} = \frac{N_\mathrm{Trig}}{N_\mathrm{Sel}} = \frac{N_\mathrm{Trig}}{N_\mathrm{TIS}} \frac{N_\mathrm{TIS}}{N_\mathrm{Sel}} = \frac{N_\mathrm{Trig}}{N_\mathrm{TIS}} \varepsilon_\mathrm{TIS}. \)
If we assume that the TIS efficiency \(\varepsilon_\mathrm{TIS}\) is identical in any subsample of data, then we can compute this in the subsample of events which are TOS:
\( \varepsilon_\mathrm{TIS} = \varepsilon_\mathrm{TIS|TOS} = \frac{N_\mathrm{TISTOS}}{N_\mathrm{TOS}}. \)
Bringing this all together, we get a trigger efficiency in quantities we know!
\( \varepsilon_\mathrm{Trig.} = \frac{N_\mathrm{Trig}}{N_\mathrm{Sel}} = \frac{N_\mathrm{Trig}}{N_\mathrm{TIS}} \frac{N_\mathrm{TISTOS}}{N_\mathrm{TOS}}. \)
The assumption we made isn't strictly true: the TIS and TOS categories can be (and are) correlated. This correlation can be circumvented by computing the counts in bins, such that the correlation is small in sufficiently small phase space bins. Applying this, we finally get to
\( \varepsilon_\mathrm{Trig.} = \frac{N_\mathrm{Trig}}{\sum\limits_i \frac{N^i_\mathrm{TIS}N^i_\mathrm{TOS}}{N^i_\mathrm{TISTOS}}}. \)
Implementing this comes with a few different caveats and pitfalls. Fortunately, the TriggerCalib software package is now available, which provides a centralised implementation of this method.
When not to use TISTOS
The TISTOS method cannot be applied to the "top-level" trigger decisions, i.e., HLT2 for data from HLT2 and sprucing for data which has been spruced. This is because we take the reconstruction from this level and hence all of the events are TOS. This would therefore give us \(N_\mathrm{TIS}=N_\mathrm{TISTOS}\), i.e., \(\varepsilon_\mathrm{TIS|TOS} = 1\).
Calculating efficiencies with TriggerCalib
TriggerCalib is a standalone Python package, installable with pip
, developed to make trigger efficiency corrections as simple as possible.
This won't be discussed in detail here, but a full user guide with tutorials can be found in [2].
The procedure for computing efficiencies with TriggerCalib, specifically with the HltEff
calculator, is as follows:
- Specify a binning scheme: either directly specify the bin edged or specify variables and number of bins, and let TriggerCalib do the work.
- Choose a background mitigation approach: TriggerCalib supports sideband subtraction, fit-and-count, and on-the-fly computation of sWeights.
Alternatively you can pass existing per-event weights, including pre-determined sWeights, to
HltEff
. - Pass your dataset to
HltEff
and save/inspect the resulting efficiencies.
Many more details can be found in [2], though questions are always welcome in the TriggerCalib support Mattermost channel.
-
This is typically the case for inclusive triggers, which usually look for interesting parts of an event. For example
Hlt1TrackMVA
, which looks for tracks of a good quality at HLT1; these tracks make up some part of the signal candidate on which the line has triggered. Many more details can be found in [2], though questions are always welcome in the TriggerCalib support Mattermost channel. ↩