ATLAS Offline Software
TriggerAnalysisSFConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 # AnaAlgorithm import(s):
4 from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
5 from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
6 from AthenaConfiguration.Enums import LHCPeriod
7 from Campaigns.Utils import Campaign
8 
9 
10 class TriggerAnalysisSFBlock (ConfigBlock):
11  """the ConfigBlock for trigger analysis"""
12 
13  def __init__ (self, configName='') :
14  super (TriggerAnalysisSFBlock, self).__init__ ()
15  self.addDependency('Electrons', required=False)
16  self.addDependency('Photons', required=False)
17  self.addDependency('Muons', required=False)
18  self.addDependency('OverlapRemoval', required=False)
19 
20  self.addOption ('triggerChainsPerYear', {}, type=None,
21  info="a dictionary with key (string) the year and value (list of "
22  "strings) the trigger chains. You can also use || within a string "
23  "to enforce an OR of triggers without looking up the individual "
24  "triggers. Used for both trigger selection and SFs. "
25  "The default is {} (empty dictionary).")
26  self.addOption ('multiTriggerChainsPerYear', {}, type=None,
27  info="a dictionary with key (string) a trigger set name and value a "
28  "triggerChainsPerYear dictionary, following the previous convention. "
29  "Relevant for analyses using different triggers in different categories, "
30  "where the trigger global scale factors shouldn't be combined. "
31  "The default is {} (empty dictionary).")
32  self.addOption ('noFilter', False, type=bool,
33  info="do not apply an event filter. The default is False, i.e. "
34  "remove events not passing trigger selection and matching.")
35  self.addOption ('electronID', '', type=str,
36  info="the electron ID WP (string) to use.")
37  self.addOption ('electronIsol', '', type=str,
38  info="the electron isolation WP (string) to use.")
39  self.addOption ('photonIsol', '', type=str,
40  info="the photon isolation WP (string) to use.")
41  self.addOption ('muonID', '', type=str,
42  info="the muon quality WP (string) to use.")
43  self.addOption ('electrons', '', type=str,
44  info="the input electron container, with a possible selection, in "
45  "the format container or container.selection.")
46  self.addOption ('muons', '', type=str,
47  info="the input muon container, with a possible selection, in the "
48  "format container or container.selection.")
49  self.addOption ('photons', '', type=str,
50  info="the input photon container, with a possible selection, in "
51  "the format container or container.selection.")
52  self.addOption ('noEffSF', False, type=bool,
53  info="disables the calculation of efficiencies and scale factors. "
54  "Experimental! only useful to test a new WP for which scale "
55  "factors are not available. Still performs the global trigger "
56  "matching (same behaviour as on data). The default is False.")
57  self.addOption ('noGlobalTriggerEff', False, type=bool,
58  info="disables the global trigger efficiency tool (including "
59  "matching), which is only suited for electron/muon/photon "
60  "trigger legs. The default is False.")
61 
62 
63  def makeTriggerDecisionTool(self, config):
64  # Might have already been added in TriggerAnalysisBlock
65  if "TrigDecisionTool" in config._algorithms:
66  return config._algorithms["TrigDecisionTool"]
67 
68  # Create public trigger tools
69  xAODConfTool = config.createPublicTool( 'TrigConf::xAODConfigTool', 'xAODConfigTool' )
70  decisionTool = config.createPublicTool( 'Trig::TrigDecisionTool', 'TrigDecisionTool' )
71  decisionTool.ConfigTool = '%s/%s' % \
72  ( xAODConfTool.getType(), xAODConfTool.getName() )
73  if config.geometry() is LHCPeriod.Run3:
74  decisionTool.NavigationFormat = 'TrigComposite' # Read Run 3 navigation (options are "TrigComposite" for R3 or "TriggElement" for R2, R2 navigation is not kept in most DAODs)
75  decisionTool.HLTSummary = 'HLTNav_Summary_DAODSlimmed' # Name of R3 navigation container (if reading from AOD, then "HLTNav_Summary_AODSlimmed" instead)
76 
77  return decisionTool
78 
79  def makeTriggerMatchingTool(self, config, decisionTool):
80 
81  # Create public trigger tools
82  drScoringTool = config.createPublicTool( 'Trig::DRScoringTool', 'DRScoringTool' )
83  if config.geometry() is LHCPeriod.Run3:
84  matchingTool = config.createPublicTool( 'Trig::R3MatchingTool', 'MatchingTool' )
85  matchingTool.ScoringTool = '%s/%s' % \
86  ( drScoringTool.getType(), drScoringTool.getName() )
87  matchingTool.TrigDecisionTool = '%s/%s' % \
88  ( decisionTool.getType(), decisionTool.getName() )
89  else:
90  matchingTool = config.createPublicTool( 'Trig::MatchFromCompositeTool', 'MatchingTool' )
91  if config.isPhyslite():
92  matchingTool.InputPrefix = "AnalysisTrigMatch_"
93 
94  return matchingTool
95 
96 
97  def makeTriggerGlobalEffCorrAlg(self, config, matchingTool, noSF,
98  triggerSuffix=''):
99 
100  alg = config.createAlgorithm( 'CP::TrigGlobalEfficiencyAlg', 'TrigGlobalSFAlg' + triggerSuffix )
101  if config.geometry() is LHCPeriod.Run3:
102  alg.triggers_2022 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2022',[])]
103  alg.triggers_2023 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2023',[])]
104  alg.triggers_2024 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2024',[])]
105  alg.triggers_2025 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2025',[])]
106  if config.campaign() in [Campaign.MC21a, Campaign.MC23a]:
107  if not alg.triggers_2022:
108  raise ValueError( 'TriggerAnalysisConfig: you must provide a set of triggers for the year 2022!' )
109  elif config.campaign() is Campaign.MC23c:
110  if not alg.triggers_2023:
111  raise ValueError( 'TriggerAnalysisConfig: you must provide a set of triggers for the year 2023!' )
112  else:
113  alg.triggers_2015 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2015',[])]
114  alg.triggers_2016 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2016',[])]
115  alg.triggers_2017 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2017',[])]
116  alg.triggers_2018 = [trig.replace("HLT_","").replace(" || ", "_OR_") for trig in self.triggerChainsPerYear.get('2018',[])]
117  if config.campaign() is Campaign.MC20a:
118  if not (alg.triggers_2015 and alg.triggers_2016):
119  raise ValueError( 'TriggerAnalysisConfig: you must provide a set of triggers for the years 2015 and 2016!' )
120  elif config.campaign() is Campaign.MC20d:
121  if not alg.triggers_2017:
122  raise ValueError( 'TriggerAnalysisConfig: you must provide a set of triggers for the year 2017!' )
123  elif config.campaign() is Campaign.MC20e:
124  if not alg.triggers_2018:
125  raise ValueError( 'TriggerAnalysisConfig: you must provide a set of triggers for the year 2018!' )
126 
127  alg.matchingTool = '%s/%s' % ( matchingTool.getType(), matchingTool.getName() )
128  alg.isRun3Geo = config.geometry() is LHCPeriod.Run3
129  alg.scaleFactorDecoration = 'globalTriggerEffSF'+triggerSuffix+'_%SYS%'
130  alg.matchingDecoration = 'globalTriggerMatch'+triggerSuffix+'_%SYS%'
131  alg.eventDecisionOutputDecoration = 'globalTriggerMatch'+triggerSuffix+'_dontsave_%SYS%'
132  alg.doMatchingOnly = config.dataType() is DataType.Data or noSF
133  alg.noFilter = self.noFilter
134  alg.electronID = self.electronID
135  alg.electronIsol = self.electronIsol
136  alg.photonIsol = self.photonIsol
137  alg.muonID = self.muonID
138  if self.electrons:
139  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
140  if self.muons:
141  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
142  if self.photons:
143  alg.photons, alg.photonSelection = config.readNameAndSelection(self.photons)
144  if not (self.electrons or self.muons or self.photons):
145  raise ValueError ('TriggerAnalysisConfig: at least one object collection must be provided! (electrons, muons, photons)' )
146 
147  if config.dataType() is not DataType.Data and not alg.doMatchingOnly:
148  config.addOutputVar ('EventInfo', alg.scaleFactorDecoration, 'globalTriggerEffSF'+triggerSuffix)
149  config.addOutputVar ('EventInfo', alg.matchingDecoration, 'globalTriggerMatch'+triggerSuffix, noSys=False)
150 
151  return
152 
153  def makeAlgs (self, config) :
154 
155  if (self.multiTriggerChainsPerYear and self.triggerChainsPerYear and
157  raise Exception('multiTriggerChainsPerYear and triggerChainsPerYear cannot be configured at the same time!')
158 
159  if self.triggerChainsPerYear and not self.multiTriggerChainsPerYear:
161 
162  # Create the decision algorithm, keeping track of the decision tool for later
163  decisionTool = self.makeTriggerDecisionTool(config)
164 
165  # Now pass it to the matching algorithm, keeping track of the matching tool for later
166  matchingTool = self.makeTriggerMatchingTool(config, decisionTool)
167 
168  # Calculate multi-lepton (electron/muon/photon) trigger efficiencies and SFs
169  if self.multiTriggerChainsPerYear and not self.noGlobalTriggerEff:
170  for suffix, trigger_chains in self.multiTriggerChainsPerYear.items():
171  self.triggerChainsPerYear = trigger_chains
172  self.makeTriggerGlobalEffCorrAlg(config, matchingTool, self.noEffSF, suffix)
173 
174  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:10
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.multiTriggerChainsPerYear
multiTriggerChainsPerYear
Definition: TriggerAnalysisSFConfig.py:160
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeAlgs
def makeAlgs(self, config)
Definition: TriggerAnalysisSFConfig.py:153
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:797
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.triggerChainsPerYear
triggerChainsPerYear
Definition: TriggerAnalysisSFConfig.py:171
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeTriggerDecisionTool
def makeTriggerDecisionTool(self, config)
Definition: TriggerAnalysisSFConfig.py:63
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.__init__
def __init__(self, configName='')
Definition: TriggerAnalysisSFConfig.py:13
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeTriggerMatchingTool
def makeTriggerMatchingTool(self, config, decisionTool)
Definition: TriggerAnalysisSFConfig.py:79
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:127
python.TriggerAnalysisSFConfig.TriggerAnalysisSFBlock.makeTriggerGlobalEffCorrAlg
def makeTriggerGlobalEffCorrAlg(self, config, matchingTool, noSF, triggerSuffix='')
Definition: TriggerAnalysisSFConfig.py:97