4 from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
5 from AnalysisAlgorithmsConfig.ConfigAccumulator
import DataType
6 from AnalysisAlgorithmsConfig.ConfigSequence
import filter_dsids
7 from AthenaCommon.Logging
import logging
11 """the ConfigBlock for the MET configuration"""
14 super (OutputAnalysisConfig, self).__init__ ()
15 self.addOption (
'postfix',
'', type=str,
16 info=
"a postfix to apply to decorations and algorithm names. "
17 "Typically not needed here.")
18 self.addOption (
'vars', [], type=
None,
19 info=
"a list of mappings (list of strings) between containers and "
20 "decorations to output branches. The default is [] (empty list).")
21 self.addOption (
'varsOnlyForMC', [], type=
None,
22 info=
"same as vars, but for MC-only variables so as to avoid a "
23 "crash when running on data. The default is [] (empty list).")
24 self.addOption (
'metVars', [], type=
None,
25 info=
"a list of mappings (list of strings) between containers "
26 "and decorations to output branches. Specficially for MET "
27 "variables, where only the final MET term is retained. "
28 "The default is [] (empty list).")
29 self.addOption (
'truthMetVars', [], type=
None,
30 info=
"a list of mappings (list of strings) between containers "
31 "and decorations to output branches for truth MET. "
32 "The default is [] (empty list).")
33 self.addOption (
'containers', {}, type=
None,
34 info=
"a dictionary mapping prefixes (key) to container names "
35 "(values) to be used when saving to the output tree. Branches "
36 "are then of the form prefix_decoration.")
37 self.addOption (
'containersOnlyForMC', {}, type=
None,
38 info=
"same as containers, but for MC-only containers so as to avoid "
39 "a crash when running on data.")
40 self.addOption (
'containersOnlyForDSIDs', {}, type=
None,
41 info=
"specify which DSIDs are allowed to produce a given container. "
42 "This works like 'onlyForDSIDs': pass a list of DSIDs or regexps.")
43 self.addOption (
'treeName',
'analysis', type=str,
44 info=
"name of the output TTree to save. The default is analysis.")
45 self.addOption (
'streamName',
'ANALYSIS', type=str,
46 info=
"name of the output stream to save the tree in. "
47 "The default is ANALYSIS.")
48 self.addOption (
'metTermName',
'Final', type=str,
49 info=
"the name (string) of the MET term to save, turning the MET "
50 "container into a single object. The default is 'Final'.")
51 self.addOption (
'truthMetTermName',
'NonInt', type=str,
52 info=
"the name (string) of the truth MET term to save, turning the MET "
53 "container into a single object. The default is 'NonInt'.")
55 self.addOption (
'storeSelectionFlags',
True, type=bool,
58 self.addOption (
'selectionFlagPrefix',
'select', type=str,
60 self.addOption (
'commands', [], type=
None,
61 info=
"a list of strings containing commands (regexp strings "
62 "prefaced by the keywords enable or disable) to turn on/off the "
63 "writing of branches to the output ntuple. The default is None "
64 "(no modification to the scheduled output branches).")
65 self.addOption (
'commandsOnlyForDSIDs', {}, type=
None,
66 info=
"a dictionary with individual DSIDs as keys, and a list of strings "
67 "like for the 'commands' option as items. These 'commands' will only be run "
68 "for the corresponding DSID.")
69 self.addOption (
'alwaysAddNosys',
False, type=bool,
70 info=
"If set to True, all branches will be given a systematics suffix, "
71 "even if they have no systematics (beyond the nominal).")
75 return rule.split(
'->')[1].strip()
78 """A helper function to create output algorithm"""
79 alg = config.createAlgorithm(
'CP::AsgxAODMetNTupleMakerAlg' if isMet
else 'CP::AsgxAODNTupleMakerAlg', name)
80 alg.TreeName = self.treeName
81 alg.RootStreamName = self.streamName
82 branchList =
list(vars)
84 branchList_nosys = [branch
for branch
in branchList
if "%SYS%" not in branch]
85 branchList_sys = [branch
for branch
in branchList
if "%SYS%" in branch]
86 alg.Branches = branchList_nosys + branchList_sys
91 log = logging.getLogger(
'OutputAnalysisConfig')
99 if config.dataType()
is not DataType.Data:
107 keys_message = [
repr(key)
for key
in overlapping_keys]
108 raise KeyError(f
"containersOnlyForMC would overwrite the following container keys: {', '.join(keys_message)}")
111 self.containers.update(self.containersOnlyForMC)
113 self.containersOnlyForMC.
clear()
116 for container,dsid_filters
in self.containersOnlyForDSIDs.
items():
117 if container
not in self.containers:
118 log.warning(f
"Skipping unrecognised container {container} for DSID-filtering in OutputAnalysisConfig...")
120 if not filter_dsids (dsid_filters, config):
122 self.containers.pop (container)
124 if self.storeSelectionFlags:
128 for prefix
in self.containers.
keys() :
129 containerName = self.containers[prefix]
130 outputDict = config.getOutputVars (containerName)
131 for outputName
in outputDict :
132 outputConfig = copy.deepcopy (outputDict[outputName])
133 if containerName != outputConfig.origContainerName
or config.checkOutputContainer(containerName):
134 outputConfig.outputContainerName = containerName +
'_%SYS%'
136 outputConfig.outputContainerName = config.readName(containerName)
137 outputConfigs[prefix + outputName] = outputConfig
140 for dsid, dsid_commands
in self.commandsOnlyForDSIDs.
items():
142 self.commands += dsid_commands
144 for command
in self.commands :
145 words = command.split (
' ')
146 if len (words) == 0 :
147 raise ValueError (
'received empty command for "commands" option')
148 if words[0] ==
'enable' :
149 if len (words) != 2 :
150 raise ValueError (
'enable takes exactly one argument: ' + command)
152 for name
in outputConfigs :
153 if re.match (words[1], name) :
154 outputConfigs[name].enabled =
True
156 if not used
and config.dataType()
is not DataType.Data:
157 raise KeyError (
'unknown branch pattern for enable: ' + words[1])
158 elif words[0] ==
'disable' :
159 if len (words) != 2 :
160 raise ValueError (
'disable takes exactly one argument: ' + command)
162 for name
in outputConfigs :
163 if re.match (words[1], name) :
164 outputConfigs[name].enabled =
False
166 if not used
and config.dataType()
is not DataType.Data:
167 raise KeyError (
'unknown branch pattern for disable: ' + words[1])
169 raise KeyError (
'unknown command for "commands" option: ' + words[0])
173 autoTruthMetVars =
set()
174 for outputName
in outputConfigs :
175 outputConfig = outputConfigs[outputName]
176 if outputConfig.enabled :
177 if config.isMetContainer (outputConfig.origContainerName):
178 if "Truth" in outputConfig.origContainerName:
179 myVars = autoTruthMetVars
184 if outputConfig.noSys :
185 outputConfig.outputContainerName = outputConfig.outputContainerName.replace (
'%SYS%',
'NOSYS')
186 outputConfig.variableName = outputConfig.variableName.replace (
'%SYS%',
'NOSYS')
187 if self.alwaysAddNosys :
188 outputName +=
"_NOSYS"
190 outputName +=
'_%SYS%'
191 myVars.add(f
"{outputConfig.outputContainerName}.{outputConfig.variableName} -> {outputName}")
194 postfix = self.postfix
196 postfix = self.treeName
199 treeMaker = config.createAlgorithm(
'CP::TreeMakerAlg', f
'TreeMaker{postfix}' )
200 treeMaker.TreeName = self.treeName
201 treeMaker.RootStreamName = self.streamName
205 if self.
vars or autoVars:
208 if self.
metVars or autoMetVars:
209 ntupleMaker = self.
createOutputAlgs(config, f
'MetNTupleMaker{postfix}', self.
metVars | autoMetVars, isMet=
True)
210 ntupleMaker.termName = self.metTermName
212 if config.dataType()
is not DataType.Data
and (self.
truthMetVars or autoTruthMetVars):
214 ntupleMaker.termName = self.truthMetTermName
216 treeFiller = config.createAlgorithm(
'CP::TreeFillerAlg',
'TreeFiller' + postfix )
217 treeFiller.TreeName = self.treeName
218 treeFiller.RootStreamName = self.streamName
224 For each container and for each selection, create a single pass variable in output NTuple,
225 which aggregates all the selections flag of the given selection. For example, this can include
226 pT, eta selections, some object ID selection, overlap removal, etc.
227 The goal is to have only one flag per object and working point in the output NTuple.
229 originalContainersSeen = []
230 for prefix
in self.containers.
keys() :
231 outputContainerName = self.containers[prefix]
232 containerName = config.getOutputContainerOrigin(outputContainerName)
233 if containerName
in originalContainersSeen:
236 originalContainersSeen.append(containerName)
239 if containerName ==
'EventInfo':
242 selectionNames = config.getSelectionNames(containerName)
243 for selectionName
in selectionNames:
245 if selectionName ==
'':
251 Schedule an algorithm to pick up all cut flags for a given selectionName.
252 The summary selection flag is written to output as selectionFlagPrefix_selectionName.
254 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
255 f
'ObjectSelectionSummary_{containerName}_{selectionName}')
256 selectionDecoration = f
'baselineSelection_{selectionName}_%SYS%'
257 alg.selectionDecoration = f
'{selectionDecoration},as_char'
258 alg.particles = config.readName (containerName)
259 alg.preselection = config.getFullSelection (containerName, selectionName)
260 config.addOutputVar (containerName, selectionDecoration, self.selectionFlagPrefix +
'_' + selectionName)