Individual Chain Construction

Last update: 04 Mar 2022 [History] [Edit]

Anatomy of a trigger

A trigger, sometimes also called a chain, is the combination of a level-1 (L1) hardware trigger seed and some high-level trigger (HLT) software reconstruction algorithms (that e.g. reconstruct some muons) and hypotheses (that e.g. make sure these muons pass the correct thresholds). Both the L1 and HLT components have an associated prescale, which says what fraction of possible times you want the trigger to actually run and select events.

Let’s start by going through a simple trigger that some of you might use in your analyses: HLT_2mu14_L12MU10.

This trigger has a level-1 seed of L1_2MU10. This means that, in order for the HLT algorithms to run, the hardware trigger must satisfy the requirement of two, 10 GeV muon regions of interest (RoIs).

This trigger is a primary trigger, meaning it is identified as being one of the key triggers for the ATLAS physics programme. Additionally, it is unprescaled, meaning the prescale of this trigger is always exactly one at both L1 and HLT: there is no artificial reduction in the fraction of events that could pass this chain. The L1_2MU10 item is flagged as passing in all events that have two, 10 GeV muon RoIs, and we process the HLT algorithms on all events that the L1 item passes.

In events where the L1 item has been satisfied, the HLT runs muon reconstruction around all the muon regions of interest that are of the correct pT. These RoIs are said to seed the HLT reconstruction. For example, in HLT_2mu14_L12MU10, the muon reconstruction is seeded by all MU10 RoIs, but the pT thresholds do not have to match - this is a choice that we make when configuring the trigger.

After each step in the reconstruction, a ComboHypo runs, and makes sure that there are actually two unique HLT objects - i.e. that the trigger satisfies a multiplicity of 2.

Triggers have legs, which each leg being something that has multiplicity N > 0 that is unique from everything else in the chain. Each leg must have its own L1 seed, but these seeds do not have to actually match the L1 item that seeds the trigger itself.

For example, the chain HLT_2mu14_L12MU10 has one leg with multiplicity two, and 2mu14 is seeded by 2MU10. The chain HLT_2mu10_j40_L12MU10 has two legs, one with multiplicity two and one with multiplicity one, and the seeding must be explicitly defined in the menu, since it is not implicit in the L1 item. The chain HLT_2mu10_mu6noL1_L12MU10 also has two legs (and needs explicit L1 seeding), as does HLT_mu6_mu4_L1MU6_2MU4.

An example of a real, five leg chain that is currently in the menu is HLT_j70_0eta320_j50_0eta490_j0_dijetSEP50j12etSEP1000djmassSEPdjdphi240_xe90_tcpufit_xe50_cell_L1MJJ-500-NFF, where each of the five legs have multiplicity one. The legs are:

j0_dijetSEP50j12etSEP1000djmassSEPdjdphi240 ==> this is not an object in the traditional sense, but is the calculated dijet pairs that have dPhi and invariant mass cuts applied
xe50_cell ==> MET algorithms are combined with different thresholds to have optimal signal efficiency and background rejection.

Potential improvements

  • menu files go into Menu/ folder, but all the config and setup files go into MenuConfig/
  • having ChainProp defined inside is confusing, because we use ChainDef to refer to something else. This should be changed to, at the very least.

Defining a chain inside a menu file - the ChainProp

jargon alert: menu file, menu python file, menu - these refer to the file that has a the long list of triggers in it - for example, or

Chains are defined with a ChainProp inside of the menu files themselves. The ChainProp object is a namedtuple, defined inside The ChainProp has a very limited set of properties:

Property Use Default value
name the name of the trigger -
l1SeedThresholds what L1 threshold seeds each leg []
stream what stream(s) does this trigger seed ['Main']
groups what prescale/rate groups should this trigger be in []
mergingStrategy should the legs be parallel or serial merged, or should the menu determine which automatically auto
mergingOrder unknown []
mergingOffset unknown -1
topoStartFrom unknown False
monGroups unknown []

We can now go through these one-by-one:

Trigger name

The trigger name (often also called the chain name) encodes nearly everything for the chain. Every piece of the name holds some important meaning, and it must follow the Run 3 naming convention.

Names are structured HLT_ + the important stuff + _L1ItemThatSeedsTheChain.

L1 seed thresholds

The L1 seed threshold must be a list with the same length as the number of legs in the trigger. Jets, MET, and noL1 muons run unseeded, and must be seeded by FSNOSEED. Electrons and photons must be seeded by an EM threshold, muons by a MU threshold, and taus by a TAU threshold.

Thresholds must be defined in the L1 menu, but do not need to correspond to the L1 item that seeds the chain.

For example, the chain HLT_2mu10_e5_L12MU10 should be seeded by ['MU10','EM3']: but this does not mean that the L1 item needs to actually contain EM3. In this case, what happens is the very first HLT step applies a requirement on EM3 in addition to 2MU10, and if there are no EM3 RoIs in the event the reconstruction does not proceed and the trigger is marked as failing.

In the muon case discussed above, the choice of MU10 to seed muon reconstruction seems obvious, but it is not always so cut and dry: for example, should e9 be seeded by EM8VH or EM7? Never guess, always check with the relevant signature!


The stream indicates where the events selected by the trigger are going to be written out. All possible choices are listed inside For physics triggers, this is generally Main, i.e. physics_Main, but this will not always be the case. You should be especially careful to clarify exactly which stream the trigger should go into when preparing monitoring or partial event partial event building (PEB) triggers!

As you can see in the table above, the stream parameter is set to default as ['Main']. Thus, for many triggers in the menu this stream is not explicitly defined.

The only case in which a trigger should go into more than one stream is when one of those streams is 'express'. No trigger should go only into express, so if you are requested to add a trigger into the express stream when it does not already have an explicit stream=[PhysicsStream] in the ChainProp, make sure to add this. i.e. we want this:
not this:


The groups list is used for three purposes:

  • To mark the purpose of trigger (primary or supporting)
    • Not all trigger will have this designation, but all primary triggers should.
    • This flag should only be added when specifically instructed to by menu coordination.
    • do not flag your own triggers as primary or support without explicit approval
  • To mark that the trigger belongs to specific prescale groups
    • The only current flag here is PS:Online, which means that these triggers do not run in either MC or data reprocessings - we only intend them to run online. They are disabled in the default prescale sets prepared when the menu is generated.
    • Examples of such triggers are HLT_timeburner_L1All, beamspot triggers, and minimum bias triggers.
  • To add the trigger to specific rate (RATE) and bandwidth (BW) groups
    • This allows us to monitor the rate of the OR all triggers in a named group
    • These rates show up in e.g. pbeast and are saved into IS for later plotting.
    • When adding combined triggers, make sure to be aware of what groups you’re adding triggers to.

Most of the groups should be designated using the variables set inside These are imported at the top of the menu file (if the required group is not yet imported, you can add it). Please be careful to add the variables in a way such that the list has depth 1, i.e. do this:

SingleMuonGroup = ['RATE:SingleMuon', 'BW:Muon']
PrimaryGroup = ['Primary']
groups = SingleMuonGroup + PrimaryGroup

not this:

groups = [SingleMuonGroup + PrimaryGroup]

Be careful to set the groups in the Run 3 style - if you are implementing Run 2 triggers, don’t blindly copy over the groups!

Merging strategy / order / offset

mergingStrategy: Unless you are performing a specific test, there is no reason to specify this variable. The default is auto, which means that alignment strategy hardcoded in is what specifies how the legs are going to be merged. If the default merging is not doing what you think the chain should be, do not attempt to force the strategy to parallel or serial. This will likely cause stalls at runtime. Instead, this should be discussed in detail on JIRA.

mergingOrder: ?? no idea, we don’t use it

mergingOffset: ?? no idea, we don’t use it


I think this isn’t used either - maybe a holdover from Run 2?

Monitoring groups

Not really sure, to be filled in.

Chain dictionaries

Chain dictionaries (what we colloquially call a chainDict) are an expanded dictionary created from default parameters set by each signature and the parameters configured in the ChainProp (i.e. the chain name). Since each signature has a different set of necessary parameters, the ChainDicts look different between signatures. However, they all follow the same basic structure.

The parsing from ChainProp to ChainDict happens inside the file, and which is called per-chain from the function xxx inside

It is crucial that the parameters in the ChainDict be correctly set, since this is what the signature code later uses to configure the the algorithms and hypos that comprise a trigger.

Every signature

We’ll start by walking through a simple, single