4 from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
5 from AnalysisAlgorithmsConfig.ConfigAccumulator
import DataType
6 from AnalysisAlgorithmsConfig.ConfigBlock
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 (
'containersFullMET', {}, type=
None,
38 info=
"same as containers, but for MET containers that should be "
39 "saved with all terms (as opposed to just the final term). This "
40 "is useful for special studies. A container can appear both here and "
41 "in containers (with different prefixes).")
42 self.addOption (
'containersOnlyForMC', {}, type=
None,
43 info=
"same as containers, but for MC-only containers so as to avoid "
44 "a crash when running on data.")
45 self.addOption (
'containersOnlyForDSIDs', {}, type=
None,
46 info=
"specify which DSIDs are allowed to produce a given container. "
47 "This works like 'onlyForDSIDs': pass a list of DSIDs or regexps.")
48 self.addOption (
'treeName',
'analysis', type=str,
49 info=
"name of the output TTree to save. The default is analysis.")
50 self.addOption (
'streamName',
'ANALYSIS', type=str,
51 info=
"name of the output stream to save the tree in. "
52 "The default is ANALYSIS.")
53 self.addOption (
'metTermName',
'Final', type=str,
54 info=
"the name (string) of the MET term to save, turning the MET "
55 "container into a single object. The default is 'Final'.")
56 self.addOption (
'truthMetTermName',
'NonInt', type=str,
57 info=
"the name (string) of the truth MET term to save, turning the MET "
58 "container into a single object. The default is 'NonInt'.")
60 self.addOption (
'storeSelectionFlags',
True, type=bool,
63 self.addOption (
'selectionFlagPrefix',
'select', type=str,
65 self.addOption (
'commands', [], type=
None,
66 info=
"a list of strings containing commands (regexp strings "
67 "prefaced by the keywords enable or disable) to turn on/off the "
68 "writing of branches to the output ntuple. The default is None "
69 "(no modification to the scheduled output branches).")
70 self.addOption (
'commandsOnlyForDSIDs', {}, type=
None,
71 info=
"a dictionary with individual DSIDs as keys, and a list of strings "
72 "like for the 'commands' option as items. These 'commands' will only be run "
73 "for the corresponding DSID.")
74 self.addOption (
'alwaysAddNosys',
False, type=bool,
75 info=
"If set to True, all branches will be given a systematics suffix, "
76 "even if they have no systematics (beyond the nominal).")
77 self.addOption (
'skipRedundantSelectionFlags',
True, type=bool,
78 info=
"remove the redundant 'outputSelect' branches created by the Thinning step. "
79 "These could however be used to simplify downstream workflows, as in Easyjet. "
80 "The default is True.")
85 """Return the instance name for this block"""
86 if self.postfix
is not None and self.postfix !=
'':
92 return rule.split(
'->')[1].strip()
95 """A helper function to create output algorithm"""
96 alg = config.createAlgorithm(
'CP::AsgxAODMetNTupleMakerAlg' if isMet
else 'CP::AsgxAODNTupleMakerAlg', name)
97 alg.TreeName = self.treeName
98 alg.RootStreamName = self.streamName
99 branchList =
list(vars)
101 branchList_nosys = [branch
for branch
in branchList
if "%SYS%" not in branch]
102 branchList_sys = [branch
for branch
in branchList
if "%SYS%" in branch]
103 alg.Branches = branchList_nosys + branchList_sys
108 log = logging.getLogger(
'OutputAnalysisConfig')
123 keys_message = [
repr(key)
for key
in overlapping_keys]
124 raise KeyError(f
"containersFullMET would overwrite the following container keys: {', '.join(keys_message)}")
126 self.
containers.update(self.containersFullMET)
129 if config.dataType()
is not DataType.Data:
137 keys_message = [
repr(key)
for key
in overlapping_keys]
138 raise KeyError(f
"containersOnlyForMC would overwrite the following container keys: {', '.join(keys_message)}")
141 self.
containers.update(self.containersOnlyForMC)
143 self.containersOnlyForMC.
clear()
146 if self.containersOnlyForDSIDs:
147 for container, dsid_filters
in self.containersOnlyForDSIDs.
items():
149 log.warning(
"Skipping unrecognised container prefix '%s' for DSID-filtering in OutputAnalysisConfig...", container)
151 if not filter_dsids (dsid_filters, config):
153 log.info(
"Skipping container prefix '%s' due to DSID filtering...", container)
156 var_container = var.split(
'.')[0].
replace(
'_NOSYS',
'').
replace(
'_%SYS%',
'')
157 if var_container == self.
containers[container]:
159 log.info(
"Skipping branch definition '%s' for excluded container %s...", var, var_container)
163 self.containersOnlyForDSIDs.
clear()
168 if self.storeSelectionFlags:
174 outputDict = config.getOutputVars (containerName)
175 for outputName
in outputDict :
176 outputConfig = copy.deepcopy (outputDict[outputName])
177 if containerName != outputConfig.origContainerName
or config.checkOutputContainer(containerName):
178 outputConfig.outputContainerName = containerName +
'_%SYS%'
180 outputConfig.outputContainerName = config.readName(containerName)
181 outputConfig.prefix = prefix
184 if prefix
in self.containersFullMET
and outputConfig.variableName ==
'name':
185 outputConfig.enabled =
True
186 outputConfigs[prefix + outputName] = outputConfig
189 for dsid, dsid_commands
in self.commandsOnlyForDSIDs.
items():
191 self.commands += dsid_commands
193 outputConfigsRename = {}
194 for command
in self.commands :
195 words = command.split (
' ')
196 if len (words) == 0 :
197 raise ValueError (
'received empty command for "commands" option')
198 optional = words[0] ==
'optional'
201 if words[0] ==
'enable' :
202 if len (words) != 2 :
203 raise ValueError (
'enable takes exactly one argument: ' + command)
205 for name
in outputConfigs :
206 if re.match (words[1], name) :
207 outputConfigs[name].enabled =
True
209 if not used
and not optional
and config.dataType()
is not DataType.Data:
210 raise KeyError (
'unknown branch pattern for enable: ' + words[1])
211 elif words[0] ==
'disable' :
212 if len (words) != 2 :
213 raise ValueError (
'disable takes exactly one argument: ' + command)
215 for name
in outputConfigs :
216 if re.match (words[1], name) :
217 outputConfigs[name].enabled =
False
219 if not used
and not optional
and config.dataType()
is not DataType.Data:
220 raise KeyError (
'unknown branch pattern for disable: ' + words[1])
221 elif words[0] ==
'rename' :
222 if len (words) != 3 :
223 raise ValueError (
'rename takes exactly two arguments: ' + command)
225 for name
in outputConfigs :
226 if re.match (words[1], name) :
227 new_name = re.sub (words[1], words[2], name)
228 outputConfigsRename[new_name] = copy.deepcopy(outputConfigs[name])
229 outputConfigs[name].enabled =
False
231 if not used
and not optional
and config.dataType()
is not DataType.Data:
232 raise KeyError (
'unknown branch pattern for rename: ' + words[1])
234 raise KeyError (
'unknown command for "commands" option: ' + words[0])
237 outputConfigs.update(outputConfigsRename)
241 autoTruthMetVars =
set()
242 for outputName, outputConfig
in outputConfigs.items():
243 if outputConfig.enabled :
244 if config.isMetContainer (outputConfig.origContainerName)
and outputConfig.prefix
not in self.containersFullMET:
245 if "Truth" in outputConfig.origContainerName:
246 myVars = autoTruthMetVars
251 if outputConfig.noSys :
252 outputConfig.outputContainerName = outputConfig.outputContainerName.replace (
'%SYS%',
'NOSYS')
253 outputConfig.variableName = outputConfig.variableName.replace (
'%SYS%',
'NOSYS')
254 if self.alwaysAddNosys :
255 outputName +=
"_NOSYS"
257 outputName +=
'_%SYS%'
258 myVars.add(f
"{outputConfig.outputContainerName}.{outputConfig.variableName} -> {outputName}")
261 treeMaker = config.createAlgorithm(
'CP::TreeMakerAlg',
'TreeMaker' )
262 treeMaker.TreeName = self.treeName
263 treeMaker.RootStreamName = self.streamName
267 if self.
vars or autoVars:
270 if self.
metVars or autoMetVars:
272 ntupleMaker.termName = self.metTermName
274 if config.dataType()
is not DataType.Data
and (self.
truthMetVars or autoTruthMetVars):
276 ntupleMaker.termName = self.truthMetTermName
278 treeFiller = config.createAlgorithm(
'CP::TreeFillerAlg',
'TreeFiller' )
279 treeFiller.TreeName = self.treeName
280 treeFiller.RootStreamName = self.streamName
286 For each container and for each selection, create a single pass variable in output NTuple,
287 which aggregates all the selections flag of the given selection. For example, this can include
288 pT, eta selections, some object ID selection, overlap removal, etc.
289 The goal is to have only one flag per object and working point in the output NTuple.
291 originalContainersSeen = []
294 containerName = config.getOutputContainerOrigin(outputContainerName)
295 if containerName
in originalContainersSeen:
298 originalContainersSeen.append(containerName)
301 if containerName ==
'EventInfo':
304 selectionNames = config.getSelectionNames(containerName)
305 for selectionName
in selectionNames:
307 if selectionName ==
'':
310 if self.skipRedundantSelectionFlags
and "outputSelect" in selectionName:
316 Schedule an algorithm to pick up all cut flags for a given selectionName.
317 The summary selection flag is written to output as selectionFlagPrefix_selectionName.
319 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
320 f
'ObjectSelectionSummary_{containerName}_{selectionName}')
321 selectionDecoration = f
'baselineSelection_{selectionName}_%SYS%'
322 alg.selectionDecoration = f
'{selectionDecoration},as_char'
323 alg.particles = config.readName (containerName)
324 alg.preselection = config.getFullSelection (containerName, selectionName)
325 config.addOutputVar (containerName, selectionDecoration, self.selectionFlagPrefix +
'_' + selectionName)