ATLAS Offline Software
Loading...
Searching...
No Matches
python.OutputAnalysisConfig.OutputAnalysisConfig Class Reference
Inheritance diagram for python.OutputAnalysisConfig.OutputAnalysisConfig:
Collaboration diagram for python.OutputAnalysisConfig.OutputAnalysisConfig:

Public Member Functions

 __init__ (self)
 instanceName (self)
 createOutputAlgs (self, config, name, vars)
 makeAlgs (self, config)
 createSelectionFlagBranches (self, config)
 makeSelectionSummaryAlg (self, config, containerName, selectionName)

Static Public Member Functions

 branchSortOrder (rule)

Public Attributes

 containers = dict(self.containers)
 vars = set(self.vars)
 varsOnlyForMC = set(self.varsOnlyForMC)
 varsOnlyForDSIDs = dict(self.varsOnlyForDSIDs)
 metVars = set(self.metVars)
 truthMetVars = set(self.truthMetVars)
 containersFullMET
 containersOnlyForMC
 containersOnlyForDSIDs
 storeSelectionFlags
 commands
 alwaysAddNosys
 metTermName
 truthMetTermName
str outputFormat = 'RNTuple':

Detailed Description

the ConfigBlock for the MET configuration

Definition at line 10 of file OutputAnalysisConfig.py.

Constructor & Destructor Documentation

◆ __init__()

python.OutputAnalysisConfig.OutputAnalysisConfig.__init__ ( self)

Definition at line 13 of file OutputAnalysisConfig.py.

13 def __init__ (self) :
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=list,
19 info="a list of mappings (list of strings) between containers and "
20 "decorations to output branches.")
21 self.addOption ('varsOnlyForMC', [], type=list,
22 info="same as `vars`, but for MC-only variables so as to avoid a "
23 "crash when running on data.")
24 self.addOption ('metVars', [], type=list,
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 self.addOption ('truthMetVars', [], type=list,
29 info="a list of mappings (list of strings) between containers "
30 "and decorations to output branches for truth MET.")
31 self.addOption ('containers', {}, type=dict,
32 info="a dictionary mapping prefixes (key) to container names "
33 "(values) to be used when saving to the output tree. Branches "
34 "are then of the form `prefix_decoration`.")
35 self.addOption ('containersFullMET', {}, type=dict,
36 info="same as `containers`, but for MET containers that should be "
37 "saved with all terms (as opposed to just the final term). This "
38 "is useful for special studies. A container can appear both here and "
39 "in containers (with different prefixes).")
40 self.addOption ('containersOnlyForMC', {}, type=dict,
41 info="same as `containers`, but for MC-only containers so as to avoid "
42 "a crash when running on data.")
43 self.addOption ('containersOnlyForDSIDs', {}, type=dict,
44 info="specify which DSIDs are allowed to produce a given container. "
45 "This works like `onlyForDSIDs`: pass a list of DSIDs or regexps.")
46 self.addOption ('varsOnlyForDSIDs', {}, type=dict,
47 info="specify which DSIDs are allowed to produce a given variable. "
48 "Pass a dictionary with the variable definition as key and a list of DSIDs/regexps as value.")
49 self.addOption ('nonContainers', [], type=list,
50 info="a list of container names that are not actual containers but should be treated as non-containers.")
51 self.addOption ('treeName', 'analysis', type=str,
52 info="name of the output TTree (or RNTuple) to save.")
53 self.addOption ('streamName', 'ANALYSIS', type=str,
54 info="name of the output stream to save the tree in.")
55 self.addOption ('metTermName', 'Final', type=str,
56 info="the name of the MET term to save, turning the MET "
57 "container into a single object.")
58 self.addOption ('truthMetTermName', 'NonInt', type=str,
59 info="the name of the truth MET term to save, turning the MET "
60 "container into a single object.")
61 self.addOption ('storeSelectionFlags', True, type=bool,
62 info="whether to store one branch for each object selection.")
63 self.addOption ('selectionFlagPrefix', 'select', type=str,
64 info="the prefix used when naming selection branches.")
65 self.addOption ('commands', [], type=list,
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. If left empty, do not modify "
69 "the scheduled output branches.")
70 self.addOption ('commandsOnlyForDSIDs', {}, type=dict,
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.")
81 self.addOption ('outputFormat', 'TTree', type=str,
82 info="The output format, `TTree` or `RNTuple`.")
83 self.addOption ('defaultBasketSize', None, type=int,
84 info="default basket size for all branches in the output tree. "
85 "If not set (the default), no basket size is configured and ROOT's "
86 "default will be used.")
87

Member Function Documentation

◆ branchSortOrder()

python.OutputAnalysisConfig.OutputAnalysisConfig.branchSortOrder ( rule)
static

Definition at line 95 of file OutputAnalysisConfig.py.

95 def branchSortOrder (rule):
96 return rule.split('->')[1].strip()
97

◆ createOutputAlgs()

python.OutputAnalysisConfig.OutputAnalysisConfig.createOutputAlgs ( self,
config,
name,
vars )
A helper function to create output algorithm

Definition at line 98 of file OutputAnalysisConfig.py.

98 def createOutputAlgs (self, config, name, vars):
99 """A helper function to create output algorithm"""
100 alg = config.createAlgorithm('CP::AsgxAODNTupleMakerAlg', name)
101 alg.TreeName = self.treeName
102 alg.RootStreamName = self.streamName
103 alg.NonContainers = list(self.nonContainers)
104 branchList = list(vars)
105 branchList.sort(key=self.branchSortOrder)
106 branchList_nosys = [branch for branch in branchList if "%SYS%" not in branch]
107 branchList_sys = [branch for branch in branchList if "%SYS%" in branch]
108 alg.Branches = branchList_nosys + branchList_sys
109 if self.defaultBasketSize is not None:
110 alg.DefaultBasketSize = self.defaultBasketSize
111 return alg
112

◆ createSelectionFlagBranches()

python.OutputAnalysisConfig.OutputAnalysisConfig.createSelectionFlagBranches ( self,
config )
For each container and for each selection, create a single pass variable in output NTuple,
which aggregates all the selections flag of the given selection. For example, this can include
pT, eta selections, some object ID selection, overlap removal, etc.
The goal is to have only one flag per object and working point in the output NTuple.

Definition at line 354 of file OutputAnalysisConfig.py.

354 def createSelectionFlagBranches(self, config):
355 """
356 For each container and for each selection, create a single pass variable in output NTuple,
357 which aggregates all the selections flag of the given selection. For example, this can include
358 pT, eta selections, some object ID selection, overlap removal, etc.
359 The goal is to have only one flag per object and working point in the output NTuple.
360 """
361 originalContainersSeen = []
362 for prefix in self.containers.keys() :
363 outputContainerName = self.containers[prefix]
364 containerName = config.getOutputContainerOrigin(outputContainerName)
365 if containerName in originalContainersSeen:
366 continue
367 else:
368 originalContainersSeen.append(containerName)
369
370 # EventInfo is one obvious example of a container that has no object selections
371 if containerName == 'EventInfo':
372 continue
373
374 # Get the selection names, except the systematic-dependent version of the FTAG
375 # selection flag, as it's already saved as a systematic-independent output branch
376 selectionNames = config.getSelectionNames(containerName, excludeFrom={'ftag'})
377 for selectionName in selectionNames:
378 # skip default selection
379 if selectionName == '':
380 continue
381 # skip selection coming from the Thinning block
382 if self.skipRedundantSelectionFlags and "outputSelect" in selectionName:
383 continue
384 self.makeSelectionSummaryAlg(config, containerName, selectionName)
385

◆ instanceName()

python.OutputAnalysisConfig.OutputAnalysisConfig.instanceName ( self)
Return the instance name for this block

Definition at line 88 of file OutputAnalysisConfig.py.

88 def instanceName (self) :
89 """Return the instance name for this block"""
90 if self.postfix is not None and self.postfix != '':
91 return self.postfix
92 return self.treeName
93

◆ makeAlgs()

python.OutputAnalysisConfig.OutputAnalysisConfig.makeAlgs ( self,
config )

Definition at line 113 of file OutputAnalysisConfig.py.

113 def makeAlgs (self, config) :
114
115 log = logging.getLogger('OutputAnalysisConfig')
116
117 self.containers = dict(self.containers)
118 self.vars = set(self.vars)
119 self.varsOnlyForMC = set(self.varsOnlyForMC)
120 self.varsOnlyForDSIDs = dict(self.varsOnlyForDSIDs)
121 self.metVars = set(self.metVars)
122 self.truthMetVars = set(self.truthMetVars)
123
124 # check for overlaps between containers and containersFullMET
125 overlapping_keys = set(self.containers.keys()).intersection(self.containersFullMET.keys())
126 if overlapping_keys:
127 # convert the set of overlapping keys to a list of strings for the message (represents the empty string too!)
128 keys_message = [repr(key) for key in overlapping_keys]
129 raise KeyError(f"containersFullMET would overwrite the following container keys: {', '.join(keys_message)}")
130 # move items in self.containersFullMET to containers
131 self.containers.update(self.containersFullMET)
132
133 # merge the MC-specific branches and containers into the main list/dictionary only if we are not running on data
134 if config.dataType() is not DataType.Data:
135 self.vars |= self.varsOnlyForMC
136
137 # protect 'containers' against being overwritten
138 # find overlapping keys
139 overlapping_keys = set(self.containers.keys()).intersection(self.containersOnlyForMC.keys())
140 if overlapping_keys:
141 # convert the set of overlapping keys to a list of strings for the message (represents the empty string too!)
142 keys_message = [repr(key) for key in overlapping_keys]
143 raise KeyError(f"containersOnlyForMC would overwrite the following container keys: {', '.join(keys_message)}")
144
145 # move items in self.containersOnlyForMC to self.containers
146 self.containers.update(self.containersOnlyForMC)
147
148 # now filter the containers depending on DSIDs
149 if self.containersOnlyForDSIDs:
150 for container, dsid_filters in self.containersOnlyForDSIDs.items():
151 if container not in self.containers:
152 log.warning("Skipping unrecognised container prefix '%s' for DSID-filtering in OutputAnalysisConfig...", container)
153 continue
154 if not filter_dsids (dsid_filters, config):
155 # if current DSID is not allowed for this container, remove it
156 log.info("Skipping container prefix '%s' due to DSID filtering...", container)
157 # filter branches for validated containers
158 for var in set(self.vars): # make a copy of the list to avoid modifying it while iterating
159 var_container = var.split('.')[0].replace('_NOSYS', '').replace('_%SYS%', '')
160 if var_container == self.containers[container]:
161 self.vars.remove(var)
162 log.info("Skipping branch definition '%s' for excluded container %s...", var, var_container)
163 # filter branches for MET variables
164 for var in set(self.metVars): # make a copy of the list to avoid modifying it while iterating
165 var_container = var.split('.')[0].replace('_NOSYS', '').replace('_%SYS%', '')
166 if var_container == self.containers[container]:
167 self.metVars.remove(var)
168 log.info("Skipping MET branch definition '%s' for excluded container %s...", var, var_container)
169 # filter branches for truth MET variables
170 for var in set(self.truthMetVars): # make a copy of the list to avoid modifying it while iterating
171 var_container = var.split('.')[0].replace('_NOSYS', '').replace('_%SYS%', '')
172 if var_container == self.containers[container]:
173 self.truthMetVars.remove(var)
174 log.info("Skipping truth MET branch definition '%s' for excluded container %s...", var, var_container)
175 # remove the container from the list at the end
176 self.containers.pop (container)
177 # Filter individual variables depending on DSIDs
178 if self.varsOnlyForDSIDs:
179 for var_pattern, dsid_filters in self.varsOnlyForDSIDs.items():
180 if not filter_dsids(dsid_filters, config):
181 # Remove matching variables from user-defined vars collections
182 for var in set(self.vars):
183 if var_pattern in var:
184 self.vars.remove(var)
185 log.info("Skipping branch definition '%s' due to variable-level DSID filtering...", var)
186 for var in set(self.metVars):
187 if var_pattern in var:
188 self.metVars.remove(var)
189 log.info("Skipping MET branch definition '%s' due to variable-level DSID filtering...", var)
190 for var in set(self.truthMetVars):
191 if var_pattern in var:
192 self.truthMetVars.remove(var)
193 log.info("Skipping truth MET branch definition '%s' due to variable-level DSID filtering...", var)
194
195
196 for prefix, container in self.containers.items():
197 origName = config.getOutputContainerOrigin(container)
198 if config.getContainerMeta(origName, "nonContainer", False):
199 self.nonContainers.append(origName)
200
201 if self.storeSelectionFlags:
202 self.createSelectionFlagBranches(config)
203
204 outputConfigs = {}
205 for prefix in self.containers.keys() :
206 containerName = self.containers[prefix]
207 outputDict = config.getOutputVars (containerName)
208 for outputName in outputDict :
209 outputConfig = copy.deepcopy (outputDict[outputName])
210 outputConfig.outputContainerName = config.readName(containerName)
211 outputConfig.prefix = prefix
212 # if the container is a MET container with all terms, we
213 # also need to write out the name of each MET term
214 if prefix in self.containersFullMET and outputConfig.variableName == 'name':
215 outputConfig.enabled = True
216 outputConfigs[prefix + outputName] = outputConfig
217
218 # check for DSID-specific commands
219 for dsid, dsid_commands in self.commandsOnlyForDSIDs.items():
220 if filter_dsids([dsid], config):
221 self.commands += dsid_commands
222
223 outputConfigsRename = {}
224 for command in self.commands :
225 words = command.split (' ')
226 if len (words) == 0 :
227 raise ValueError ('received empty command for "commands" option')
228 optional = words[0] == 'optional'
229 if optional :
230 words = words[1:] # remove the 'optional' keyword
231 if words[0] == 'enable' :
232 if len (words) != 2 :
233 raise ValueError ('enable takes exactly one argument: ' + command)
234 used = False
235 for name in outputConfigs :
236 if re.match (words[1], name) :
237 outputConfigs[name].enabled = True
238 used = True
239 if not used and not optional and config.dataType() is not DataType.Data:
240 raise KeyError ('unknown branch pattern for enable: ' + words[1])
241 elif words[0] == 'disable' :
242 if len (words) != 2 :
243 raise ValueError ('disable takes exactly one argument: ' + command)
244 used = False
245 for name in outputConfigs :
246 if re.match (words[1], name) :
247 outputConfigs[name].enabled = False
248 used = True
249 if not used and not optional and config.dataType() is not DataType.Data:
250 raise KeyError ('unknown branch pattern for disable: ' + words[1])
251 elif words[0] == 'rename' :
252 if len (words) != 3 :
253 raise ValueError ('rename takes exactly two arguments: ' + command)
254 used = False
255 for name in outputConfigs :
256 if re.match (words[1], name) :
257 new_name = re.sub (words[1], words[2], name)
258 outputConfigsRename[new_name] = copy.deepcopy(outputConfigs[name])
259 outputConfigs[name].enabled = False
260 used = True
261 if not used and not optional and config.dataType() is not DataType.Data:
262 raise KeyError ('unknown branch pattern for rename: ' + words[1])
263 else :
264 raise KeyError ('unknown command for "commands" option: ' + words[0])
265
266 # update the outputConfigs with renamed branches
267 outputConfigs.update(outputConfigsRename)
268
269 autoVars = set()
270 autoMetVars = set()
271 autoTruthMetVars = set()
272 for outputName, outputConfig in outputConfigs.items():
273 if outputConfig.enabled :
274 if config.isMetContainer (outputConfig.origContainerName) and outputConfig.prefix not in self.containersFullMET:
275 if "Truth" in outputConfig.origContainerName:
276 myVars = autoTruthMetVars
277 else:
278 myVars = autoMetVars
279 else :
280 myVars = autoVars
281 if outputConfig.noSys :
282 outputConfig.outputContainerName = outputConfig.outputContainerName.replace ('%SYS%', 'NOSYS')
283 outputConfig.variableName = outputConfig.variableName.replace ('%SYS%', 'NOSYS')
284 if self.alwaysAddNosys :
285 outputName += "_NOSYS"
286 else :
287 outputName += '_%SYS%'
288 branchDecl = f"{outputConfig.outputContainerName}.{outputConfig.variableName} -> {outputName}"
289 if outputConfig.auxType is not None :
290 branchDecl += f" type={outputConfig.auxType}"
291 if config.isMetContainer (outputConfig.origContainerName) and outputConfig.prefix not in self.containersFullMET:
292 if "Truth" in outputConfig.origContainerName:
293 branchDecl += f" metTerm={self.truthMetTermName}"
294 else:
295 branchDecl += f" metTerm={self.metTermName}"
296 myVars.add(branchDecl)
297
298 # Unified branch collection for all output formats
299 allBranches = set()
300 allBranches |= self.vars
301 allBranches |= autoVars
302 # Add MET branches
303 userMetVars = set()
304 if self.metVars:
305 for var in self.metVars:
306 userMetVars.add(var + " metTerm=" + self.metTermName)
307 allBranches |= userMetVars
308 allBranches |= autoMetVars
309 # Add truth MET branches (for MC)
310 userTruthMetVars = set()
311 if config.dataType() is not DataType.Data:
312 if self.truthMetVars:
313 for var in self.truthMetVars:
314 userTruthMetVars.add(var + " metTerm=" + self.truthMetTermName)
315 allBranches |= userTruthMetVars
316 allBranches |= autoTruthMetVars
317
318 # Create the output algorithm based on outputFormat
319 if self.outputFormat == 'RNTuple':
320 alg = config.createAlgorithm('CP::RNtupleTreeMakerAlg', 'RNtupleMaker')
321 alg.TreeName = self.treeName
322 alg.RootStreamName = self.streamName
323 alg.OutputStreamName = self.streamName
324 alg.NonContainers = list(self.nonContainers)
325
326 branchList = list(allBranches)
327 branchList.sort(key=self.branchSortOrder)
328 alg.Branches = branchList
329
330 return
331
332 # Add an ntuple dumper algorithm:
333 treeMaker = config.createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
334 treeMaker.TreeName = self.treeName
335 treeMaker.RootStreamName = self.streamName
336 # the auto-flush setting still needs to be figured out
337 #treeMaker.TreeAutoFlush = 0
338
339 if self.vars or autoVars:
340 self.createOutputAlgs(config, 'NTupleMaker', self.vars | autoVars)
341
342 if self.metVars or autoMetVars:
343 self.createOutputAlgs(config, 'MetNTupleMaker', userMetVars | autoMetVars)
344
345 if config.dataType() is not DataType.Data and (self.truthMetVars or autoTruthMetVars):
346 self.createOutputAlgs(config, 'TruthMetNTupleMaker', userTruthMetVars | autoTruthMetVars)
347
348 treeFiller = config.createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
349 treeFiller.TreeName = self.treeName
350 treeFiller.RootStreamName = self.streamName
351
352
353
STL class.
std::vector< std::string > intersection(std::vector< std::string > &v1, std::vector< std::string > &v2)
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:312

◆ makeSelectionSummaryAlg()

python.OutputAnalysisConfig.OutputAnalysisConfig.makeSelectionSummaryAlg ( self,
config,
containerName,
selectionName )
Schedule an algorithm to pick up all cut flags for a given selectionName.
The summary selection flag is written to output as selectionFlagPrefix_selectionName.

Definition at line 386 of file OutputAnalysisConfig.py.

386 def makeSelectionSummaryAlg(self, config, containerName, selectionName):
387 """
388 Schedule an algorithm to pick up all cut flags for a given selectionName.
389 The summary selection flag is written to output as selectionFlagPrefix_selectionName.
390 """
391 alg = config.createAlgorithm( 'CP::AsgSelectionAlg',
392 f'ObjectSelectionSummary_{containerName}_{selectionName}')
393 selectionDecoration = f'baselineSelection_{selectionName}_%SYS%'
394 alg.selectionDecoration = f'{selectionDecoration},as_char'
395 alg.particles = config.readName (containerName)
396 alg.preselection = config.getFullSelection (containerName, selectionName)
397 config.addOutputVar (containerName, selectionDecoration, self.selectionFlagPrefix + '_' + selectionName)

Member Data Documentation

◆ alwaysAddNosys

python.OutputAnalysisConfig.OutputAnalysisConfig.alwaysAddNosys

Definition at line 284 of file OutputAnalysisConfig.py.

◆ commands

python.OutputAnalysisConfig.OutputAnalysisConfig.commands

Definition at line 224 of file OutputAnalysisConfig.py.

◆ containers

python.OutputAnalysisConfig.OutputAnalysisConfig.containers = dict(self.containers)

Definition at line 117 of file OutputAnalysisConfig.py.

◆ containersFullMET

python.OutputAnalysisConfig.OutputAnalysisConfig.containersFullMET

Definition at line 131 of file OutputAnalysisConfig.py.

◆ containersOnlyForDSIDs

python.OutputAnalysisConfig.OutputAnalysisConfig.containersOnlyForDSIDs

Definition at line 149 of file OutputAnalysisConfig.py.

◆ containersOnlyForMC

python.OutputAnalysisConfig.OutputAnalysisConfig.containersOnlyForMC

Definition at line 146 of file OutputAnalysisConfig.py.

◆ metTermName

python.OutputAnalysisConfig.OutputAnalysisConfig.metTermName

Definition at line 306 of file OutputAnalysisConfig.py.

◆ metVars

python.OutputAnalysisConfig.OutputAnalysisConfig.metVars = set(self.metVars)

Definition at line 121 of file OutputAnalysisConfig.py.

◆ outputFormat

str python.OutputAnalysisConfig.OutputAnalysisConfig.outputFormat = 'RNTuple':

Definition at line 319 of file OutputAnalysisConfig.py.

◆ storeSelectionFlags

python.OutputAnalysisConfig.OutputAnalysisConfig.storeSelectionFlags

Definition at line 201 of file OutputAnalysisConfig.py.

◆ truthMetTermName

python.OutputAnalysisConfig.OutputAnalysisConfig.truthMetTermName

Definition at line 314 of file OutputAnalysisConfig.py.

◆ truthMetVars

python.OutputAnalysisConfig.OutputAnalysisConfig.truthMetVars = set(self.truthMetVars)

Definition at line 122 of file OutputAnalysisConfig.py.

◆ vars

python.OutputAnalysisConfig.OutputAnalysisConfig.vars = set(self.vars)

Definition at line 118 of file OutputAnalysisConfig.py.

◆ varsOnlyForDSIDs

python.OutputAnalysisConfig.OutputAnalysisConfig.varsOnlyForDSIDs = dict(self.varsOnlyForDSIDs)

Definition at line 120 of file OutputAnalysisConfig.py.

◆ varsOnlyForMC

python.OutputAnalysisConfig.OutputAnalysisConfig.varsOnlyForMC = set(self.varsOnlyForMC)

Definition at line 119 of file OutputAnalysisConfig.py.


The documentation for this class was generated from the following file: