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)
 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 ('nonContainers', [], type=list,
47 info="a list of container names that are not actual containers but should be treated as non-containers.")
48 self.addOption ('treeName', 'analysis', type=str,
49 info="name of the output TTree to save.")
50 self.addOption ('streamName', 'ANALYSIS', type=str,
51 info="name of the output stream to save the tree in.")
52 self.addOption ('metTermName', 'Final', type=str,
53 info="the name of the MET term to save, turning the MET "
54 "container into a single object.")
55 self.addOption ('truthMetTermName', 'NonInt', type=str,
56 info="the name of the truth MET term to save, turning the MET "
57 "container into a single object.")
58 self.addOption ('storeSelectionFlags', True, type=bool,
59 info="whether to store one branch for each object selection.")
60 self.addOption ('selectionFlagPrefix', 'select', type=str,
61 info="the prefix used when naming selection branches.")
62 self.addOption ('commands', [], type=list,
63 info="a list of strings containing commands (regexp strings "
64 "prefaced by the keywords `enable` or `disable`) to turn on/off the "
65 "writing of branches to the output ntuple. If left empty, do not modify "
66 "the scheduled output branches.")
67 self.addOption ('commandsOnlyForDSIDs', {}, type=dict,
68 info="a dictionary with individual DSIDs as keys, and a list of strings "
69 "like for the `commands` option as items. These `commands` will only be run "
70 "for the corresponding DSID.")
71 self.addOption ('alwaysAddNosys', False, type=bool,
72 info="If set to `True`, all branches will be given a systematics suffix, "
73 "even if they have no systematics (beyond the nominal).")
74 self.addOption ('skipRedundantSelectionFlags', True, type=bool,
75 info="remove the redundant 'outputSelect' branches created by the Thinning step. "
76 "These could however be used to simplify downstream workflows, as in Easyjet. "
77 "The default is True.")
78 self.addOption ('outputFormat', 'TTree', type=str,
79 info="The output format. The default is 'TTree'.")
80 self.addOption ('defaultBasketSize', None, type=int,
81 info="default basket size for all branches in the output tree. "
82 "If not set (the default), no basket size is configured and ROOT's "
83 "default will be used.")
84

Member Function Documentation

◆ branchSortOrder()

python.OutputAnalysisConfig.OutputAnalysisConfig.branchSortOrder ( rule)
static

Definition at line 92 of file OutputAnalysisConfig.py.

92 def branchSortOrder (rule):
93 return rule.split('->')[1].strip()
94

◆ createOutputAlgs()

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

Definition at line 95 of file OutputAnalysisConfig.py.

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

◆ 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 335 of file OutputAnalysisConfig.py.

335 def createSelectionFlagBranches(self, config):
336 """
337 For each container and for each selection, create a single pass variable in output NTuple,
338 which aggregates all the selections flag of the given selection. For example, this can include
339 pT, eta selections, some object ID selection, overlap removal, etc.
340 The goal is to have only one flag per object and working point in the output NTuple.
341 """
342 originalContainersSeen = []
343 for prefix in self.containers.keys() :
344 outputContainerName = self.containers[prefix]
345 containerName = config.getOutputContainerOrigin(outputContainerName)
346 if containerName in originalContainersSeen:
347 continue
348 else:
349 originalContainersSeen.append(containerName)
350
351 # EventInfo is one obvious example of a container that has no object selections
352 if containerName == 'EventInfo':
353 continue
354
355 # Get the selection names, except the systematic-dependent version of the FTAG
356 # selection flag, as it's already saved as a systematic-independent output branch
357 selectionNames = config.getSelectionNames(containerName, excludeFrom={'ftag'})
358 for selectionName in selectionNames:
359 # skip default selection
360 if selectionName == '':
361 continue
362 # skip selection coming from the Thinning block
363 if self.skipRedundantSelectionFlags and "outputSelect" in selectionName:
364 continue
365 self.makeSelectionSummaryAlg(config, containerName, selectionName)
366

◆ instanceName()

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

Definition at line 85 of file OutputAnalysisConfig.py.

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

◆ makeAlgs()

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

Definition at line 110 of file OutputAnalysisConfig.py.

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

367 def makeSelectionSummaryAlg(self, config, containerName, selectionName):
368 """
369 Schedule an algorithm to pick up all cut flags for a given selectionName.
370 The summary selection flag is written to output as selectionFlagPrefix_selectionName.
371 """
372 alg = config.createAlgorithm( 'CP::AsgSelectionAlg',
373 f'ObjectSelectionSummary_{containerName}_{selectionName}')
374 selectionDecoration = f'baselineSelection_{selectionName}_%SYS%'
375 alg.selectionDecoration = f'{selectionDecoration},as_char'
376 alg.particles = config.readName (containerName)
377 alg.preselection = config.getFullSelection (containerName, selectionName)
378 config.addOutputVar (containerName, selectionDecoration, self.selectionFlagPrefix + '_' + selectionName)

Member Data Documentation

◆ alwaysAddNosys

python.OutputAnalysisConfig.OutputAnalysisConfig.alwaysAddNosys

Definition at line 265 of file OutputAnalysisConfig.py.

◆ commands

python.OutputAnalysisConfig.OutputAnalysisConfig.commands

Definition at line 205 of file OutputAnalysisConfig.py.

◆ containers

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

Definition at line 114 of file OutputAnalysisConfig.py.

◆ containersFullMET

python.OutputAnalysisConfig.OutputAnalysisConfig.containersFullMET

Definition at line 127 of file OutputAnalysisConfig.py.

◆ containersOnlyForDSIDs

python.OutputAnalysisConfig.OutputAnalysisConfig.containersOnlyForDSIDs

Definition at line 145 of file OutputAnalysisConfig.py.

◆ containersOnlyForMC

python.OutputAnalysisConfig.OutputAnalysisConfig.containersOnlyForMC

Definition at line 142 of file OutputAnalysisConfig.py.

◆ metTermName

python.OutputAnalysisConfig.OutputAnalysisConfig.metTermName

Definition at line 287 of file OutputAnalysisConfig.py.

◆ metVars

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

Definition at line 117 of file OutputAnalysisConfig.py.

◆ outputFormat

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

Definition at line 300 of file OutputAnalysisConfig.py.

◆ storeSelectionFlags

python.OutputAnalysisConfig.OutputAnalysisConfig.storeSelectionFlags

Definition at line 179 of file OutputAnalysisConfig.py.

◆ truthMetTermName

python.OutputAnalysisConfig.OutputAnalysisConfig.truthMetTermName

Definition at line 295 of file OutputAnalysisConfig.py.

◆ truthMetVars

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

Definition at line 118 of file OutputAnalysisConfig.py.

◆ vars

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

Definition at line 115 of file OutputAnalysisConfig.py.

◆ varsOnlyForMC

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

Definition at line 116 of file OutputAnalysisConfig.py.


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