ATLAS Offline Software
OutputAnalysisConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2022 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 import copy, re
7 
8 class OutputAnalysisConfig (ConfigBlock):
9  """the ConfigBlock for the MET configuration"""
10 
11  def __init__ (self) :
12  super (OutputAnalysisConfig, self).__init__ ()
13  self.addOption ('postfix', '', type=str,
14  info="a postfix to apply to decorations and algorithm names. "
15  "Typically not needed here.")
16  self.addOption ('vars', [], type=None,
17  info="a list of mappings (list of strings) between containers and "
18  "decorations to output branches. The default is [] (empty list).")
19  self.addOption ('varsOnlyForMC', [], type=None,
20  info="same as vars, but for MC-only variables so as to avoid a "
21  "crash when running on data. The default is [] (empty list).")
22  self.addOption ('metVars', [], type=None,
23  info="a list of mappings (list of strings) between containers "
24  "and decorations to output branches. Specficially for MET "
25  "variables, where only the final MET term is retained. "
26  "The default is [] (empty list).")
27  self.addOption ('containers', {}, type=None,
28  info="a dictionary mapping prefixes (key) to container names "
29  "(values) to be used when saving to the output tree. Branches "
30  "are then of the form prefix_decoration.")
31  # TODO: add info string
32  self.addOption ('containersOnlyForMC', [], type=None,
33  info="")
34  self.addOption ('treeName', 'analysis', type=str,
35  info="name of the output TTree to save. The default is analysis.")
36  self.addOption ('metTermName', 'Final', type=str,
37  info="the name (string) of the MET term to save, turning the MET "
38  "container into a single object. The default is 'Final'.")
39  # TODO: add info strng
40  self.addOption ('storeSelectionFlags', True, type=bool,
41  info="")
42  # TODO: add info strng
43  self.addOption ('selectionFlagPrefix', 'select', type=str,
44  info="")
45  self.addOption ('commands', [], type=None,
46  info="a list of strings containing commands (regexp strings "
47  "prefaced by the keywords enable or disable) to turn on/off the "
48  "writing of branches to the output ntuple. The default is None "
49  "(no modification to the scheduled output branches).")
50  self.addOption ('alwaysAddNosys', False, type=bool,
51  info="If set to True, all branches will be given a systematics suffix, "
52  "even if they have no systematics (beyond the nominal).")
53 
54 
55  def makeAlgs (self, config) :
56 
57  self.vars = set(self.vars)
59  # merge the MC-specific branches and containers into the main list/dictionary only if we are not running on data
60  if config.dataType() is not DataType.Data:
61  self.vars |= self.varsOnlyForMC
62  self.containers.update(self.containersOnlyForMC)
63 
64  if self.storeSelectionFlags:
65  self.createSelectionFlagBranches(config)
66 
67  outputConfigs = {}
68  for prefix in self.containers.keys() :
69  containerName = self.containers[prefix]
70  outputDict = config.getOutputVars (containerName)
71  for outputName in outputDict :
72  outputConfig = copy.deepcopy (outputDict[outputName])
73  if containerName == 'EventInfo' :
74  outputConfig.outputContainerName = outputConfig.origContainerName
75  elif outputConfig.outputContainerName != outputConfig.origContainerName :
76  outputConfig.outputContainerName = containerName + '_%SYS%'
77  else :
78  outputConfig.outputContainerName = config.readName (containerName)
79  outputConfigs[prefix + outputName] = outputConfig
80 
81  for command in self.commands :
82  words = command.split (' ')
83  if len (words) == 0 :
84  raise ValueError ('received empty command for "commands" option')
85  if words[0] == 'enable' :
86  if len (words) != 2 :
87  raise ValueError ('enable takes exactly one argument: ' + command)
88  used = False
89  for name in outputConfigs :
90  if re.match (words[1], name) :
91  outputConfigs[name].enabled = True
92  used = True
93  if not used and config.dataType() is not DataType.Data:
94  raise KeyError ('unknown branch pattern for enable: ' + words[1])
95  elif words[0] == 'disable' :
96  if len (words) != 2 :
97  raise ValueError ('disable takes exactly one argument: ' + command)
98  used = False
99  for name in outputConfigs :
100  if re.match (words[1], name) :
101  outputConfigs[name].enabled = False
102  used = True
103  if not used and config.dataType() is not DataType.Data:
104  raise KeyError ('unknown branch pattern for disable: ' + words[1])
105  else :
106  raise KeyError ('unknown command for "commands" option: ' + words[0])
107 
108  autoVars = []
109  autoMetVars = []
110  for outputName in outputConfigs :
111  outputConfig = outputConfigs[outputName]
112  if outputConfig.enabled :
113  if config.isMetContainer (outputConfig.origContainerName) :
114  myVars = autoMetVars
115  else :
116  myVars = autoVars
117  if outputConfig.noSys :
118  outputConfig.outputContainerName = outputConfig.outputContainerName.replace ('%SYS%', 'NOSYS')
119  outputConfig.variableName = outputConfig.variableName.replace ('%SYS%', 'NOSYS')
120  if self.alwaysAddNosys :
121  outputName += "_NOSYS"
122  else :
123  outputName += '_%SYS%'
124  myVars += [outputConfig.outputContainerName + '.' + outputConfig.variableName + ' -> ' + outputName]
125 
126  postfix = self.postfix
127 
128  # Add an ntuple dumper algorithm:
129  treeMaker = config.createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' + postfix )
130  treeMaker.TreeName = self.treeName
131  # the auto-flush setting still needs to be figured out
132  #treeMaker.TreeAutoFlush = 0
133 
134  if len (self.vars) + len (autoVars) :
135  ntupleMaker = config.createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' + postfix )
136  ntupleMaker.TreeName = self.treeName
137  branchList = list(self.vars | set(autoVars))
138  branchList.sort()
139  branchList_nosys = [branch for branch in branchList if "%SYS%" not in branch]
140  branchList_sys = [branch for branch in branchList if "%SYS%" in branch]
141  ntupleMaker.Branches = branchList_nosys + branchList_sys
142  # ntupleMaker.OutputLevel = 2 # For output validation
143 
144  if len (self.metVars) + len (autoMetVars) > 0:
145  ntupleMaker = config.createAlgorithm( 'CP::AsgxAODMetNTupleMakerAlg', 'MetNTupleMaker' + postfix )
146  ntupleMaker.TreeName = self.treeName
147  branchList = self.metVars + autoMetVars
148  branchList.sort()
149  branchList_nosys = [branch for branch in branchList if "%SYS%" not in branch]
150  branchList_sys = [branch for branch in branchList if "%SYS%" in branch]
151  ntupleMaker.Branches = branchList_nosys + branchList_sys
152  ntupleMaker.termName = self.metTermName
153  #ntupleMaker.OutputLevel = 2 # For output validation
154 
155  treeFiller = config.createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' + postfix )
156  treeFiller.TreeName = self.treeName
157 
158 
159  def createSelectionFlagBranches(self, config):
160  """
161  For each container and for each selection, create a single pass variable in output NTuple,
162  which aggregates all the selections flag of the given selection. For example, this can include
163  pT, eta selections, some object ID selection, overlap removal, etc.
164  The goal is to have only one flag per object and working point in the output NTuple.
165  """
166  for prefix in self.containers.keys() :
167  outputContainerName = self.containers[prefix]
168  containerName = config.getOutputContainerOrigin(outputContainerName)
169 
170  # EventInfo is one obvious example of a container that has no object selections
171  if containerName == 'EventInfo':
172  continue
173 
174  selectionNames = config.getSelectionNames(containerName)
175  for selectionName in selectionNames:
176  # skip default selection
177  if selectionName == '':
178  continue
179  self.makeSelectionSummaryAlg(config, containerName, selectionName)
180 
181  def makeSelectionSummaryAlg(self, config, containerName, selectionName):
182  """
183  Schedule an algorithm to pick up all cut flags for a given selectionName.
184  The summary selection flag is written to output as selectionFlagPrefix_selectionName.
185  """
186  alg = config.createAlgorithm( 'CP::AsgSelectionAlg',
187  f'ObjectSelectionSummary_{containerName}_{selectionName}')
188  selectionDecoration = f'baselineSelection_{selectionName}_%SYS%'
189  alg.selectionDecoration = f'{selectionDecoration},as_char'
190  alg.particles = config.readName (containerName)
191  alg.preselection = config.getFullSelection (containerName, selectionName)
192  config.addOutputVar (containerName, selectionDecoration, self.selectionFlagPrefix + '_' + selectionName)
python.OutputAnalysisConfig.OutputAnalysisConfig.makeAlgs
def makeAlgs(self, config)
Definition: OutputAnalysisConfig.py:55
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.OutputAnalysisConfig.OutputAnalysisConfig.vars
vars
Definition: OutputAnalysisConfig.py:57
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:224
python.OutputAnalysisConfig.OutputAnalysisConfig.makeSelectionSummaryAlg
def makeSelectionSummaryAlg(self, config, containerName, selectionName)
Definition: OutputAnalysisConfig.py:181
dqt_zlumi_pandas.update
update
Definition: dqt_zlumi_pandas.py:42
python.OutputAnalysisConfig.OutputAnalysisConfig.varsOnlyForMC
varsOnlyForMC
Definition: OutputAnalysisConfig.py:58
python.OutputAnalysisConfig.OutputAnalysisConfig.createSelectionFlagBranches
def createSelectionFlagBranches(self, config)
Definition: OutputAnalysisConfig.py:159
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
python.OutputAnalysisConfig.OutputAnalysisConfig.__init__
def __init__(self)
Definition: OutputAnalysisConfig.py:11
python.OutputAnalysisConfig.OutputAnalysisConfig
Definition: OutputAnalysisConfig.py:8