ATLAS Offline Software
TriggerAnalysisConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2 
3 from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
4 from AnalysisAlgorithmsConfig.ConfigSequence import groupBlocks
5 from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType, ConfigAccumulator
6 from AthenaConfiguration.Enums import LHCPeriod
7 
8 
9 def is_year_in_current_period(config: ConfigAccumulator, year: int | str) -> bool:
10  """
11  Utility function to check whether the year is valid for the current configuration
12  """
13  if config.geometry() is LHCPeriod.Run2 and year >= 2022:
14  return False
15  if config.geometry() is LHCPeriod.Run3 and year < 2022:
16  return False
17 
18  return True
19 
20 
21 class TriggerAnalysisBlock (ConfigBlock):
22  """the ConfigBlock for trigger analysis"""
23 
24  def __init__ (self) :
25  super (TriggerAnalysisBlock, self).__init__ ()
26  self.addOption ('triggerChainsPerYear', {}, type=None,
27  info="a dictionary with key (string) the year and value (list of "
28  "strings) the trigger chains. You can also use || within a string "
29  "to enforce an OR of triggers without looking up the individual "
30  "triggers. Used for both trigger selection and SFs. "
31  "The default is {} (empty dictionary).")
32  self.addOption ('multiTriggerChainsPerYear', {}, type=None,
33  info="a dictionary with key (string) a trigger set name and value a "
34  "triggerChainsPerYear dictionary, following the previous convention. "
35  "Relevant for analyses using different triggers in different categories, "
36  "where the trigger global scale factors shouldn't be combined. "
37  "The default is {} (empty dictionary).")
38  self.addOption ('triggerChainsForSelection', [], type=None,
39  info="a list of trigger chains (list of strings) to be used for "
40  "trigger selection. Only set it if you need a different setup "
41  "than for trigger SFs. The default is [] (empty list).")
42  self.addOption ('triggerChainsForDecoration', [], type=None,
43  info="a list of trigger chains (list of strings) to be used for "
44  "trigger decoration, if it needs to be different from the selection one. "
45  "The default is [] (empty list).")
46  self.addOption ('prescaleDecoration', 'prescale', type=str,
47  info="name (prefix) of decoration for trigger prescales.")
48  self.addOption ('prescaleLumiCalcFiles', [], type=None,
49  info="a list of lumical files (list of strings) to calculate "
50  "trigger prescales. The default is [] (empty list). Mutually "
51  "exclusive with prescaleLumiCalcFilesPerYear")
52  self.addOption ('prescaleLumiCalcFilesPerYear', {}, type=None,
53  info="a dicrionary with key (string) the year and value (list of "
54  "strings) the list of lumicalc files to calculate trigger prescales "
55  "for an individual data year. The default is {} (empty dictionary). "
56  "Mutually exclusive with prescaleLumiCalcFiles")
57  self.addOption ('prescaleTriggersFormula', '', type=str,
58  info="a formula used in (un)prescaling, producing overall prescale "
59  "factor instead of prescale per trigger.")
60  self.addOption ('prescaleMC', False, type=bool,
61  info="ouput trigger prescales when running on MC. The default is False.")
62  self.addOption ('unprescaleData', False, type=bool,
63  info="ouput trigger prescales when running on Data. The default is False.")
64  self.addOption ('prescaleIncludeAllYearsPerRun', False, type=bool,
65  info="if True, trigger prescales will include all configured years "
66  "from prescaleLumiCalcFilesPerYear in all jobs. The default is False.")
67  self.addOption ('splitPerLHCRun', False, type=bool,
68  info="if True, trigger branches will only be processed for the current LHC run.")
69  self.addOption ('noFilter', False, type=bool,
70  info="do not apply an event filter. The default is False, i.e. "
71  "remove events not passing trigger selection and matching.")
72  # TODO: add info string
73  self.addOption ('noL1', False, type=bool,
74  info="")
75 
76  def instanceName (self) :
77  """Return the instance name for this block"""
78  return '' # no instance name, as this is a singleton block
79 
80  @staticmethod
82  # Might have already been added
83  toolName = "TrigDecisionTool"
84  if toolName in config._algorithms:
85  return config._algorithms[toolName]
86 
87  # Create public trigger tools
88  xAODConfTool = config.createPublicTool("TrigConf::xAODConfigTool", "xAODConfigTool")
89  decisionTool = config.createPublicTool("Trig::TrigDecisionTool", toolName)
90  decisionTool.ConfigTool = f"{xAODConfTool.getType()}/{xAODConfTool.getName()}"
91  decisionTool.HLTSummary = config.hltSummary()
92  if config.geometry() is LHCPeriod.Run3:
93  # Read Run 3 navigation
94  # (options are "TrigComposite" for R3 or "TriggElement" for R2,
95  # R2 navigation is not kept in most DAODs)
96  decisionTool.NavigationFormat = "TrigComposite"
97 
98  return decisionTool
99 
100  @staticmethod
101  def makeTriggerMatchingTool(config, decisionTool):
102  # Might have already been added
103  toolName = "TrigMatchingTool"
104  if toolName in config._algorithms:
105  return config._algorithms[toolName]
106 
107  # Create public trigger tools
108  if config.geometry() is LHCPeriod.Run3:
109  drScoringTool = config.createPublicTool("Trig::DRScoringTool", "DRScoringTool")
110  matchingTool = config.createPublicTool("Trig::R3MatchingTool", toolName)
111  matchingTool.ScoringTool = f"{drScoringTool.getType()}/{drScoringTool.getName()}"
112  matchingTool.TrigDecisionTool = f"{decisionTool.getType()}/{decisionTool.getName()}"
113  else:
114  matchingTool = config.createPublicTool("Trig::MatchFromCompositeTool", toolName)
115  if config.isPhyslite():
116  matchingTool.InputPrefix = "AnalysisTrigMatch_"
117  return matchingTool
118 
119  @staticmethod
120  def _get_lumicalc_triggers(lumicalc_files: list[str]) -> list[str]:
121  return [lumicalc.split(":")[-1] for lumicalc in lumicalc_files if ":" in lumicalc]
122 
123  def makeTriggerSelectionAlg(self, config, decisionTool):
124 
125  # Set up the trigger selection:
126  alg = config.createAlgorithm( 'CP::TrigEventSelectionAlg', 'TrigEventSelectionAlg' )
127  alg.tool = '%s/%s' % \
128  ( decisionTool.getType(), decisionTool.getName() )
129  alg.triggers = self.triggerChainsForSelection
130  alg.selectionDecoration = 'trigPassed'
131  alg.noFilter = self.noFilter
132  alg.noL1 = self.noL1
133 
134  for t in self.triggerChainsForSelection :
135  t = t.replace(".", "p").replace("-", "_")
136  config.addOutputVar ('EventInfo', 'trigPassed_' + t, 'trigPassed_' + t, noSys=True)
137 
138  # for decoration, make sure we only get the triggers not already in the selection list
139  triggerChainsForDeco = list(set(self.triggerChainsForDecoration) - set(self.triggerChainsForSelection))
140  if triggerChainsForDeco :
141  alg = config.createAlgorithm( 'CP::TrigEventSelectionAlg', 'TrigEventSelectionAlgDeco' )
142  alg.tool = '%s/%s' % \
143  ( decisionTool.getType(), decisionTool.getName() )
144  alg.triggers = triggerChainsForDeco
145  alg.selectionDecoration = 'trigPassed'
146  alg.noFilter = True
147  alg.noL1 = self.noL1
148 
149  for t in triggerChainsForDeco :
150  t = t.replace(".", "p").replace("-", "_")
151  config.addOutputVar ('EventInfo', 'trigPassed_' + t, 'trigPassed_' + t, noSys=True)
152 
153  # Calculate trigger prescales
154  if (self.prescaleLumiCalcFiles or self.prescaleLumiCalcFilesPerYear) and (
155  self.unprescaleData if config.dataType() is DataType.Data else self.prescaleMC
156  ):
157 
158  lumicalc_files = []
159  if self.prescaleLumiCalcFiles:
160  lumicalc_files = self.prescaleLumiCalcFiles
161  elif self.prescaleLumiCalcFilesPerYear:
162  from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import get_input_years, get_year_data
163 
164  years = get_input_years(config)
165  for year in years:
166  lumicalc_files.extend(get_year_data(self.prescaleLumiCalcFilesPerYear, year))
167 
168  prescale_triggers = self._get_lumicalc_triggers(lumicalc_files)
169  prescale_triggersAll = self.triggerChainsForSelection
170 
171  # Schedule trigger prescale output branches
172  prescale_triggers_output = set(prescale_triggers)
173  if self.prescaleIncludeAllYearsPerRun and self.prescaleLumiCalcFilesPerYear:
174  all_lumicalc_files = [
175  lumicalc
176  for lumicalc_year, lumicalc_values in self.prescaleLumiCalcFilesPerYear.items()
177  for lumicalc in lumicalc_values
178  if is_year_in_current_period(config, int(lumicalc_year))
179  ]
180  prescale_triggers_output.update(self._get_lumicalc_triggers(all_lumicalc_files))
181  prescale_triggersAll = list(prescale_triggers_output)
182 
183  if not self.prescaleTriggersFormula and not prescale_triggersAll:
184  return
185 
186  alg = config.createAlgorithm( 'CP::TrigPrescalesAlg', 'TrigPrescalesAlg' )
187  config.addPrivateTool( 'pileupReweightingTool', 'CP::PileupReweightingTool' )
188  alg.pileupReweightingTool.LumiCalcFiles = lumicalc_files
189  alg.selectionDecoration = 'trigPassed'
190  alg.prescaleMC = config.dataType() is not DataType.Data
191  alg.prescaleDecoration = self.prescaleDecoration
192  if self.prescaleTriggersFormula != '':
193  alg.prescaleTriggersFormula = self.prescaleTriggersFormula
194  config.addOutputVar("EventInfo", alg.prescaleDecoration, alg.prescaleDecoration, noSys=True)
195  else:
196  alg.triggers = prescale_triggers
197  alg.triggersAll = prescale_triggersAll
198 
199  # Schedule trigger prescale output branches
200  for trigger in prescale_triggers_output:
201  trigger = trigger.replace("-", "_")
202  config.addOutputVar(
203  "EventInfo",
204  alg.prescaleDecoration + "_" + trigger,
205  alg.prescaleDecoration + "_" + trigger,
206  noSys=True,
207  )
208 
209  return
210 
211 
212  def makeAlgs (self, config) :
213 
214  if (self.multiTriggerChainsPerYear and self.triggerChainsPerYear and
215  self.triggerChainsPerYear is not self.multiTriggerChainsPerYear.get('')):
216  raise Exception('multiTriggerChainsPerYear and triggerChainsPerYear cannot be configured at the same time!')
217 
218  if self.prescaleLumiCalcFiles and self.prescaleLumiCalcFilesPerYear:
219  raise Exception('prescaleLumiCalcFiles and prescaleLumiCalcFilesPerYear cannot be configured at the same time!')
220 
221  if self.prescaleIncludeAllYearsPerRun and not self.prescaleLumiCalcFilesPerYear:
222  raise Exception('prescaleIncludeAllYearsPerRun requires prescaleLumiCalcFilesPerYear to be configured!')
223 
224  if (self.prescaleLumiCalcFiles or self.prescaleLumiCalcFilesPerYear) and not (
225  self.unprescaleData or self.prescaleMC
226  ):
227  raise Exception('Lumicalc files are provided but no trigger prescale output is configured! Specify output with "unprescaleData" and/or "prescaleMC".')
228 
229  if self.triggerChainsPerYear and not self.multiTriggerChainsPerYear:
230  self.multiTriggerChainsPerYear = {'': self.triggerChainsPerYear}
231 
232  # if we are only given the trigger dictionary, we fill the selection list automatically
233  if self.triggerChainsPerYear and not self.triggerChainsForSelection:
234  triggers_for_selection = set()
235  triggers_for_decoration = set()
236  from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import get_input_years
237  years = get_input_years(config)
238  for trigger_chains in self.multiTriggerChainsPerYear.values():
239  for year, chain_list in trigger_chains.items():
240  if self.splitPerLHCRun and not is_year_in_current_period(config, year):
241  continue
242  target_triggers = triggers_for_selection if int(year) in years else triggers_for_decoration
243  for chain in chain_list:
244  if '||' in chain:
245  chains = chain.split('||')
246  target_triggers.update(map(str.strip, chains))
247  else:
248  target_triggers.add(chain.strip())
249  self.triggerChainsForSelection = list(triggers_for_selection)
250  self.triggerChainsForDecoration += list(triggers_for_decoration)
251 
252  # Create the decision algorithm, keeping track of the decision tool for later
253  decisionTool = self.makeTriggerDecisionTool(config)
254 
256  self.makeTriggerSelectionAlg(config, decisionTool)
257 
258  return
259 
260 
261 
262 @groupBlocks
263 def Trigger(seq):
264  seq.append(TriggerAnalysisBlock())
265  from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import TriggerAnalysisSFBlock
266  seq.append(TriggerAnalysisSFBlock())
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
python.TriggerAnalysisConfig.TriggerAnalysisBlock.makeTriggerDecisionTool
def makeTriggerDecisionTool(config)
Definition: TriggerAnalysisConfig.py:81
python.TriggerAnalysisConfig.TriggerAnalysisBlock.makeAlgs
def makeAlgs(self, config)
Definition: TriggerAnalysisConfig.py:212
python.TriggerAnalysisConfig.TriggerAnalysisBlock.instanceName
def instanceName(self)
Definition: TriggerAnalysisConfig.py:76
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:808
python.TriggerAnalysisConfig.is_year_in_current_period
bool is_year_in_current_period(ConfigAccumulator config, int|str year)
Definition: TriggerAnalysisConfig.py:9
python.TriggerAnalysisSFConfig.get_year_data
list get_year_data(dict dictionary, int|str year)
Definition: TriggerAnalysisSFConfig.py:28
python.TriggerAnalysisConfig.TriggerAnalysisBlock.multiTriggerChainsPerYear
multiTriggerChainsPerYear
Definition: TriggerAnalysisConfig.py:230
python.TriggerAnalysisSFConfig.get_input_years
list[int] get_input_years(ConfigAccumulator config)
Definition: TriggerAnalysisSFConfig.py:35
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.TriggerAnalysisConfig.Trigger
def Trigger(seq)
Definition: TriggerAnalysisConfig.py:263
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:232
python.TriggerAnalysisConfig.TriggerAnalysisBlock.__init__
def __init__(self)
Definition: TriggerAnalysisConfig.py:24
python.TriggerAnalysisConfig.TriggerAnalysisBlock.makeTriggerSelectionAlg
def makeTriggerSelectionAlg(self, config, decisionTool)
Definition: TriggerAnalysisConfig.py:123
python.TriggerAnalysisConfig.TriggerAnalysisBlock
Definition: TriggerAnalysisConfig.py:21
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:71
python.TriggerAnalysisConfig.TriggerAnalysisBlock.triggerChainsForSelection
triggerChainsForSelection
Definition: TriggerAnalysisConfig.py:249
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
python.TriggerAnalysisConfig.TriggerAnalysisBlock._get_lumicalc_triggers
list[str] _get_lumicalc_triggers(list[str] lumicalc_files)
Definition: TriggerAnalysisConfig.py:120
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:127
python.TriggerAnalysisConfig.TriggerAnalysisBlock.makeTriggerMatchingTool
def makeTriggerMatchingTool(config, decisionTool)
Definition: TriggerAnalysisConfig.py:101