Loading [MathJax]/extensions/tex2jax.js
ATLAS Offline Software
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
TriggerAnalysisSFConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 from typing import Iterable, Union
3 
4 from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
5 from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType, ConfigAccumulator
6 from AthenaConfiguration.Enums import LHCPeriod
7 from Campaigns.Utils import Campaign
8 from TriggerAnalysisAlgorithms.TriggerAnalysisConfig import TriggerAnalysisBlock
9 
10 
11 def is_mc_from(config: ConfigAccumulator, campaign_list: Union[Campaign, Iterable[Campaign]]) -> bool:
12  """
13  Utility function to check whether the data type is mc and whether the campaign is in desired list of campaigns
14  without causing an invalid FlugEnum comparison
15  """
16  campaign_list = [campaign_list] if isinstance(campaign_list, Campaign) else campaign_list
17  return config.dataType() is not DataType.Data and config.campaign() in campaign_list
18 
19 
20 def is_data_from(config, data_year_list: Union[int, Iterable[int]]) -> bool:
21  """
22  Utility function to check whether the data type is data and whether the year is in desired list of years
23  """
24  data_year_list = [data_year_list] if isinstance(data_year_list, int) else data_year_list
25  return config.dataType() is DataType.Data and config.dataYear() in data_year_list
26 
27 
28 def get_year_data(dictionary: dict, year: int | str) -> list:
29  """
30  Utility function to get the data for a specific year from a dictionary
31  """
32  return dictionary.get(int(year), dictionary.get(str(year), []))
33 
34 
35 def get_input_years(config: ConfigAccumulator) -> list[int]:
36  """
37  Utility function to get the list of years that the input corresponds to
38  """
39  years = []
40  if config.campaign() is Campaign.MC20a:
41  years = [2015, 2016]
42  elif is_data_from(config, 2015):
43  years = [2015]
44  elif is_data_from(config, 2016):
45  years = [2016]
46  elif config.campaign() is Campaign.MC20d or is_data_from(config, 2017):
47  years = [2017]
48  elif config.campaign() is Campaign.MC20e or is_data_from(config, 2018):
49  years = [2018]
50  elif config.campaign() in [Campaign.MC21a, Campaign.MC23a] or is_data_from(config, 2022):
51  years = [2022]
52  elif config.campaign() in [Campaign.MC23c, Campaign.MC23d] or is_data_from(config, 2023):
53  years = [2023]
54 
55  return years
56 
57 
58 class TriggerAnalysisSFBlock(ConfigBlock):
59  """the ConfigBlock for trigger analysis"""
60  def __init__(self):
61  super(TriggerAnalysisSFBlock, self).__init__()
62  self.addDependency('Electrons', required=False)
63  self.addDependency('Photons', required=False)
64  self.addDependency('Muons', required=False)
65  self.addDependency('Taus', required=False)
66  self.addDependency('OverlapRemoval', required=False)
67 
68  self.addOption ('triggerChainsPerYear', {}, type=None,
69  info="a dictionary with key (string) the year and value (list of "
70  "strings) the trigger chains. You can also use || within a string "
71  "to enforce an OR of triggers without looking up the individual "
72  "triggers. Used for both trigger selection and SFs. "
73  "The default is {} (empty dictionary).")
74  self.addOption ('multiTriggerChainsPerYear', {}, type=None,
75  info="a dictionary with key (string) a trigger set name and value a "
76  "triggerChainsPerYear dictionary, following the previous convention. "
77  "Relevant for analyses using different triggers in different categories, "
78  "where the trigger global scale factors shouldn't be combined. "
79  "The default is {} (empty dictionary).")
80  self.addOption ('noFilter', False, type=bool,
81  info="do not apply an event filter. The default is False, i.e. "
82  "remove events not passing trigger selection and matching.")
83  self.addOption ('electronID', '', type=str,
84  info="the electron ID WP (string) to use.")
85  self.addOption ('electronIsol', '', type=str,
86  info="the electron isolation WP (string) to use.")
87  self.addOption ('photonIsol', '', type=str,
88  info="the photon isolation WP (string) to use.")
89  self.addOption ('muonID', '', type=str,
90  info="the muon quality WP (string) to use.")
91  self.addOption ('electrons', '', type=str,
92  info="the input electron container, with a possible selection, in "
93  "the format container or container.selection.")
94  self.addOption ('muons', '', type=str,
95  info="the input muon container, with a possible selection, in the "
96  "format container or container.selection.")
97  self.addOption ('photons', '', type=str,
98  info="the input photon container, with a possible selection, in "
99  "the format container or container.selection.")
100  self.addOption ('taus', '', type=str,
101  info="the input tau container, with a possible selection, in "
102  "the format container or container.selection.")
103  self.addOption ('noEffSF', False, type=bool,
104  info="disables the calculation of efficiencies and scale factors. "
105  "Experimental! only useful to test a new WP for which scale "
106  "factors are not available. Still performs the global trigger "
107  "matching (same behaviour as on data). The default is False.")
108  self.addOption ('noGlobalTriggerEff', False, type=bool,
109  info="disables the global trigger efficiency tool (including "
110  "matching), which is only suited for electron/muon/photon "
111  "trigger legs. The default is False.")
112  self.addOption ('triggerMatchingChainsPerYear', {}, type=None,
113  info="a dictionary with key (string) the year and value (list of "
114  "strings) the trigger chains. The default is {} (empty dictionary).")
115  self.addOption("includeAllYears", False, type=bool,
116  info="if True, trigger matching will include all configured years "
117  "in all jobs. The default is False.")
118  self.addOption ('postfix', '', type=str,
119  info="a unique identifier for the trigger matching decorations. Only "
120  "useful when defining multiple setups. The default is '' (empty string).")
121 
123  self,
124  config: ConfigAccumulator,
125  matchingTool,
126  noSF: bool,
127  triggerSuffix: str = ''
128  ) -> None:
129  alg = config.createAlgorithm( 'CP::TrigGlobalEfficiencyAlg', 'TrigGlobalSFAlg' + triggerSuffix + self.postfix)
130  if config.geometry() is LHCPeriod.Run3:
131  alg.triggers_2022 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2022)]
132  alg.triggers_2023 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2023)]
133  alg.triggers_2024 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2024)]
134  alg.triggers_2025 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2025)]
135  if is_mc_from(config, [Campaign.MC21a, Campaign.MC23a]) or is_data_from(config, 2022):
136  if not alg.triggers_2022:
137  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the year 2022!')
138  elif is_mc_from(config, [Campaign.MC23c, Campaign.MC23d]) or is_data_from(config, 2023):
139  if not alg.triggers_2023:
140  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the year 2023!')
141  else:
142  alg.triggers_2015 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2015)]
143  alg.triggers_2016 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2016)]
144  alg.triggers_2017 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2017)]
145  alg.triggers_2018 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in get_year_data(self.triggerChainsPerYear, 2018)]
146  if is_mc_from(config, Campaign.MC20a):
147  if not (alg.triggers_2015 and alg.triggers_2016):
148  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the years 2015 and 2016!')
149  elif is_data_from(config, 2015):
150  if not alg.triggers_2015:
151  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the year 2015!')
152  elif is_data_from(config, 2016):
153  if not alg.triggers_2016:
154  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the year 2016!')
155  elif is_mc_from(config, Campaign.MC20d) or is_data_from(config, 2017):
156  if not alg.triggers_2017:
157  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the year 2017!')
158  elif is_mc_from(config, Campaign.MC20e) or is_data_from(config, 2018):
159  if not alg.triggers_2018:
160  raise ValueError('TriggerAnalysisConfig: you must provide a set of triggers for the year 2018!')
161 
162  alg.matchingTool = '%s/%s' % ( matchingTool.getType(), matchingTool.getName() )
163  alg.isRun3Geo = config.geometry() is LHCPeriod.Run3
164  alg.scaleFactorDecoration = 'globalTriggerEffSF' + triggerSuffix + self.postfix + '_%SYS%'
165  alg.matchingDecoration = 'globalTriggerMatch' + triggerSuffix + self.postfix + '_%SYS%'
166  alg.eventDecisionOutputDecoration = 'globalTriggerMatch' + triggerSuffix + self.postfix + '_dontsave_%SYS%'
167  alg.doMatchingOnly = config.dataType() is DataType.Data or noSF
168  alg.noFilter = self.noFilter
169  alg.electronID = self.electronID
170  alg.electronIsol = self.electronIsol
171  alg.photonIsol = self.photonIsol
172  alg.muonID = self.muonID
173  if self.electrons:
174  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
175  if self.muons:
176  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
177  if self.photons:
178  alg.photons, alg.photonSelection = config.readNameAndSelection(self.photons)
179  if not (self.electrons or self.muons or self.photons):
180  raise ValueError('TriggerAnalysisConfig: at least one object collection must be provided! (electrons, muons, photons)' )
181 
182  if config.dataType() is not DataType.Data and not alg.doMatchingOnly:
183  config.addOutputVar('EventInfo', alg.scaleFactorDecoration, 'globalTriggerEffSF' + triggerSuffix + self.postfix)
184  config.addOutputVar('EventInfo', alg.matchingDecoration, 'globalTriggerMatch' + triggerSuffix + self.postfix, noSys=False)
185 
186  return
187 
189  self,
190  config: ConfigAccumulator,
191  matchingTool,
192  particles,
193  trig_string: str = "",
194  trig_chains: set = None,
195  trig_chains_dummy: set = None,
196  ) -> None:
197  if not particles or (not trig_chains and not trig_chains_dummy):
198  return
199 
200  if not any(trig_string in trig for trig in trig_chains) \
201  and not any(trig_string in trig for trig in trig_chains_dummy):
202  return
203 
204  alg = config.createAlgorithm("CP::TrigMatchingAlg", f"TrigMatchingAlg_{trig_string}{self.postfix}")
205  alg.matchingTool = f"{matchingTool.getType()}/{matchingTool.getName()}"
206  alg.matchingDecoration = f"trigMatched{self.postfix}"
207  alg.trigSingleMatchingList = [trig for trig in trig_chains if trig_string in trig]
208  alg.trigSingleMatchingListDummy = [trig for trig in trig_chains_dummy if trig_string in trig]
209  alg.particles, alg.particleSelection = config.readNameAndSelection(particles)
210 
211  for trig in list(alg.trigSingleMatchingList) + list(alg.trigSingleMatchingListDummy):
212  trig = trig.replace(".", "p").replace("-", "_").replace(" ", "")
213  if trig_string in trig:
214  config.addOutputVar(particles.split(".")[0], f"trigMatched_{self.postfix}{trig}", f"trigMatched_{self.postfix}{trig}", noSys=True)
215 
217  self,
218  config: ConfigAccumulator,
219  matchingTool,
220  ) -> None:
221  years = get_input_years(config)
222 
223  triggerMatchingChains = set()
224  triggerMatchingChainsDummy = set()
225  for year in years:
226  for trig in get_year_data(self.triggerMatchingChainsPerYear, year):
227  trig = trig.replace(' || ', '_OR_')
228  triggerMatchingChains.update(trig.split('_OR_'))
229  if self.includeAllYears:
230  triggerMatchingChainsAll = set()
231  for year in self.triggerMatchingChainsPerYear:
232  for trig in get_year_data(self.triggerMatchingChainsPerYear, year):
233  trig = trig.replace(' || ', '_OR_')
234  triggerMatchingChainsAll.update(trig.split('_OR_'))
235  triggerMatchingChainsDummy = triggerMatchingChainsAll - triggerMatchingChains
236 
237  self.createTrigMatching(config, matchingTool, self.electrons, 'HLT_e', triggerMatchingChains, triggerMatchingChainsDummy)
238  self.createTrigMatching(config, matchingTool, self.muons, 'HLT_mu', triggerMatchingChains, triggerMatchingChainsDummy)
239  self.createTrigMatching(config, matchingTool, self.photons, 'HLT_g', triggerMatchingChains, triggerMatchingChainsDummy)
240  self.createTrigMatching(config, matchingTool, self.taus, 'HLT_tau', triggerMatchingChains, triggerMatchingChainsDummy)
241 
242  return
243 
244  def makeAlgs(self, config: ConfigAccumulator) -> None:
245  if (
247  self.triggerChainsPerYear and
249  ):
250  raise Exception('multiTriggerChainsPerYear and triggerChainsPerYear cannot be configured at the same time!')
251 
252  if self.triggerChainsPerYear and not self.multiTriggerChainsPerYear:
254 
255  # Create the decision algorithm, keeping track of the decision tool for later
256  decisionTool = TriggerAnalysisBlock.makeTriggerDecisionTool(config)
257 
258  # Now pass it to the matching algorithm, keeping track of the matching tool for later
259  matchingTool = TriggerAnalysisBlock.makeTriggerMatchingTool(config, decisionTool)
260 
261  # Calculate multi-lepton (electron/muon/photon) trigger efficiencies and SFs
262  if self.multiTriggerChainsPerYear and not self.noGlobalTriggerEff:
263  for suffix, trigger_chains in self.multiTriggerChainsPerYear.items():
264  self.triggerChainsPerYear = trigger_chains
265  self.makeTriggerGlobalEffCorrAlg(config, matchingTool, self.noEffSF, suffix)
266 
267  # Save trigger matching information (currently only single leg trigger are supported)
268  if self.triggerMatchingChainsPerYear:
269  self.makeTrigMatchingAlg(config, matchingTool)
270 
271  return
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock
Definition: TriggerAnalysisSFConfig.py:58
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.multiTriggerChainsPerYear
multiTriggerChainsPerYear
Definition: TriggerAnalysisSFConfig.py:253
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.__init__
def __init__(self)
Definition: TriggerAnalysisSFConfig.py:60
python.TriggerAnalysisSFConfig.is_mc_from
bool is_mc_from(ConfigAccumulator config, Union[Campaign, Iterable[Campaign]] campaign_list)
Definition: TriggerAnalysisSFConfig.py:11
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeTriggerGlobalEffCorrAlg
None makeTriggerGlobalEffCorrAlg(self, ConfigAccumulator config, matchingTool, bool noSF, str triggerSuffix='')
Definition: TriggerAnalysisSFConfig.py:122
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:805
python.TriggerAnalysisSFConfig.get_year_data
list get_year_data(dict dictionary, int|str year)
Definition: TriggerAnalysisSFConfig.py:28
python.TriggerAnalysisSFConfig.is_data_from
bool is_data_from(config, Union[int, Iterable[int]] data_year_list)
Definition: TriggerAnalysisSFConfig.py:20
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeAlgs
None makeAlgs(self, ConfigAccumulator config)
Definition: TriggerAnalysisSFConfig.py:244
python.LArMinBiasAlgConfig.int
int
Definition: LArMinBiasAlgConfig.py:59
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.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.triggerChainsPerYear
triggerChainsPerYear
Definition: TriggerAnalysisSFConfig.py:264
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.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.createTrigMatching
None createTrigMatching(self, ConfigAccumulator config, matchingTool, particles, str trig_string="", set trig_chains=None, set trig_chains_dummy=None)
Definition: TriggerAnalysisSFConfig.py:188
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:71
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeTrigMatchingAlg
None makeTrigMatchingAlg(self, ConfigAccumulator config, matchingTool)
Definition: TriggerAnalysisSFConfig.py:216
str
Definition: BTagTrackIpAccessor.cxx:11