ATLAS Offline Software
ConfigAccumulator.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 import AnaAlgorithm.DualUseConfig as DualUseConfig
4 from AthenaConfiguration.Enums import LHCPeriod, FlagEnum
5 import re
6 
7 import warnings
8 import functools
9 
10 # warn about deprecations with a FutureWarning instead of a
11 # DeprecationWarning, because DeprecatedWarning is not shown by default
12 deprecationWarningCategory = FutureWarning
13 def deprecated(reason: str = ""):
14  def decorator(func):
15  message = f"{func.__qualname__} is deprecated."
16  if reason:
17  message += " " + reason
18 
19  @functools.wraps(func)
20  def wrapper(*args, **kwargs):
21  warnings.warn(
22  message,
23  category=deprecationWarningCategory,
24  stacklevel=2,
25  )
26  return func(*args, **kwargs)
27 
28  return wrapper
29  return decorator
30 
31 class DataType(FlagEnum):
32  """holds the various data types as an enum"""
33  Data = 'data'
34  FullSim = 'fullsim'
35  FastSim = 'fastsim'
36 
37 
39  """all the data for a given selection that has been registered
40 
41  the bits argument is for backward compatibility, does nothing, and will be
42  removed in the future."""
43 
44  def __init__ (self, selectionName, decoration,
45  *, bits=0, preselection=None, comesFrom = '',
46  writeToOutput=True) :
47  self.name = selectionName
48  self.decoration = decoration
49  if preselection is not None :
50  self.preselection = preselection
51  else :
52  self.preselection = (selectionName == '')
53  self.comesFrom = comesFrom
54  self.writeToOutput = writeToOutput
55 
56 
57 
58 class OutputConfig :
59  """all the data for a given variables in the output that has been registered"""
60 
61  def __init__ (self, origContainerName, variableName,
62  *, noSys, enabled) :
63  self.origContainerName = origContainerName
64  self.outputContainerName = None
65  self.variableName = variableName
66  self.noSys = noSys
67  self.enabled = enabled
68 
69  def __repr__ (self):
70  return f'OutputConfig("{self.outputContainerName}.{self.variableName}" [enabled={self.enabled}])'
71 
73  """all the auto-generated meta-configuration data for a single container
74 
75  This tracks the naming of all temporary containers, as well as all the
76  selection decorations."""
77 
78  def __init__ (self, name, sourceName, *, originalName = None, isMet = False, noSysSuffix) :
79  self.name = name
80  self.sourceName = sourceName
81  self.originalName = originalName
82  self.noSysSuffix = noSysSuffix
83  self.index = 0
84  self.maxIndex = None
85  self.viewIndex = 1
86  self.isMet = isMet
87  self.selections = []
88  self.outputs = {}
89  self.meta = {}
90 
91  def currentName (self) :
92  if self.index == 0 :
93  if self.sourceName is None :
94  raise Exception ("should not get here, reading container name before created: " + self.name)
95  return self.sourceName
96  if self.maxIndex and self.index == self.maxIndex :
97  return self.systematicsName(self.name, noSysSuffix=self.noSysSuffix)
98  return self.systematicsName(f"{self.name}_STEP{self.index}", noSysSuffix=self.noSysSuffix)
99 
100  @staticmethod
101  def systematicsName (name, *, noSysSuffix) :
102  """map an internal name to a name for systematics data handles
103 
104  Right now this just means appending a _%SYS% to the name."""
105  if not noSysSuffix :
106  return name + "_%SYS%"
107  else :
108  return name
109 
110  def nextPass (self) :
111  self.maxIndex = self.index
112  self.index = 0
113  self.viewIndex = 1
114  self.selections = []
115  self.outputs = {}
116  self.meta = {}
117 
118 
119 
121  """a class that accumulates a configuration from blocks into an
122  algorithm sequence
123 
124  This is used as argument to the ConfigurationBlock methods, which
125  need to be called in the correct order. This class will track all
126  meta-information that needs to be communicated between blocks
127  during configuration, and also add the created algorithms to the
128  sequence.
129 
130  Use/access of containers in the event store is handled via
131  references that this class hands out. This happens in a separate
132  step before the algorithms are created, as the naming of
133  containers will depend on where in the chain the container is
134  used.
135  """
136 
137  def __init__ (self, *, flags=None, algSeq=None, noSysSuffix=False, noSystematics=None, dataType=None, isPhyslite=None, geometry=None, dsid=0, campaign=None, runNumber=None, autoconfigFromFlags=None, dataYear=0):
138 
139  # Historically we have used the identifier
140  # `autoconfigFromFlags`, but in the rest of the code base
141  # `flags` is used. So for now we allow either, and can hopefully
142  # at some point remove the former (21 Aug 25).
143  if autoconfigFromFlags is not None:
144  if flags is not None:
145  raise ValueError("Cannot pass both flags and autoconfigFromFlags arguments")
146  flags = autoconfigFromFlags
147  warnings.warn ('Using autoconfigFromFlags parameter is deprecated, use flags instead', category=deprecationWarningCategory, stacklevel=2)
148  self._flags = flags
149 
150  # Historically the user was expected to pass in meta-data
151  # manually, which was a complete underestimate of the amount of
152  # meta-data needed. The current recommendation is to pass in a
153  # configuration flags object instead. The code below will raise
154  # an error if both are done, and if no configuration flags are
155  # passed in, it will try to create a flags object from the
156  # passed in parameters.
157  if self._flags is not None:
158  if dataType is not None:
159  raise ValueError("Cannot pass both dataType and flags/autoconfigFromFlags arguments")
160  if isPhyslite is not None:
161  raise ValueError("Cannot pass both isPhyslite and flags/autoconfigFromFlags arguments")
162  if geometry is not None:
163  raise ValueError("Cannot pass both geometry and flags/autoconfigFromFlags arguments")
164  if dsid != 0:
165  raise ValueError("Cannot pass both dsid and flags/autoconfigFromFlags arguments")
166  if campaign is not None:
167  raise ValueError("Cannot pass both campaign and flags/autoconfigFromFlags arguments")
168  if runNumber is not None:
169  raise ValueError("Cannot pass both runNumber and flags/autoconfigFromFlags arguments")
170  if dataYear != 0:
171  raise ValueError("Cannot pass both dataYear and flags/autoconfigFromFlags arguments")
172 
173  if self._flags.Input.isMC:
174  if self._flags.Sim.ISF.Simulator.usesFastCaloSim():
175  dataType = DataType.FastSim
176  else:
177  dataType = DataType.FullSim
178  else:
179  dataType = DataType.Data
180  isPhyslite = 'StreamDAOD_PHYSLITE' in self._flags.Input.ProcessingTags
181  from TrigDecisionTool.TrigDecisionToolHelpers import (
182  getRun3NavigationContainerFromInput_forAnalysisBase)
184  else:
185  warnings.warn ('it is deprecated to configure meta-data for analysis configuration manually, please read the configuration flags via the meta-data reader', category=deprecationWarningCategory, stacklevel=2)
186  from AthenaConfiguration.AllConfigFlags import initConfigFlags
187  flags = initConfigFlags()
188  if dataType is None:
189  raise ValueError ("need to specify dataType if flags are not set")
190  # legacy mappings of string arguments
191  if isinstance(dataType, str):
192  if dataType == 'mc':
193  dataType = DataType.FullSim
194  elif dataType == 'afii':
195  dataType = DataType.FastSim
196  else:
197  dataType = DataType(dataType)
198  if isPhyslite is None:
199  isPhyslite = False
200  if geometry is not None:
201  # allow possible string argument for `geometry` and convert it to enum
202  geometry = LHCPeriod(geometry)
203  if geometry is LHCPeriod.Run1:
204  raise ValueError ("invalid Run geometry: %s" % geometry.value)
205  flags.GeoModel.Run = geometry
206  if dsid != 0:
207  flags.Input.MCChannelNumber = dsid
208  if campaign is not None:
209  flags.Input.MCCampaign = campaign
210  if dataYear != 0:
211  flags.Input.DataYear = dataYear
212  if runNumber is None:
213  # not sure if we should just use a default run number
214  # here, or just report nothing
215  runNumber = 284500
216  flags.Input.RunNumbers = [runNumber]
217  hltSummary = 'HLTNav_Summary_DAODSlimmed'
218  flags.lock()
219  self._flags = flags
220 
221  # These don't seem to have a direct equivalent in the
222  # configuration flags. For now I'm keeping them (21 Aug 25), but
223  # they might be replaced with something that is more directly in
224  # the configuration flags in the future.
225  self._dataType = dataType
226  self._isPhyslite = isPhyslite
227  self._hltSummary = hltSummary
228 
229  # From here on, we are no longer dealing with flags or
230  # meta-data, but actual internal variables we need to manage the
231  # creation of components.
232  self._algSeq = algSeq
233  self._noSystematics = noSystematics
234  self._noSysSuffix = noSysSuffix
235  self._algPostfix = ''
238  self._pass = 0
239  self._algorithms = {}
240  self._currentAlg = None
241  self._selectionNameExpr = re.compile ('[A-Za-z_][A-Za-z_0-9]+')
242  self.setSourceName ('EventInfo', 'EventInfo')
243  self._eventcutflow = {}
244 
245  # If we are in an Athena environment with ComponentAccumulator configuration
246  # then the AlgSequence, which is Gaudi.AthSequencer, does not support '+=',
247  # and we in any case want to produce an output ComponentAccumulator
248  self.CA = None
249  if DualUseConfig.useComponentAccumulator:
250  from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
251  self.CA = ComponentAccumulator()
252  # if we have a component accumulator the user is not required to pass
253  # in a sequence, but if they do let's add it
254  if algSeq :
255  self.CA.addSequence(algSeq)
256  else :
257  if algSeq is None :
258  raise ValueError ("need to pass algSeq if not using ComponentAccumulator")
259 
260 
261  def noSystematics (self) :
262  """noSystematics flag used by CommonServices block"""
263  return self._noSystematics
264 
265  @property
266  def flags (self) :
267  """Athena configuration flags"""
268  return self._flags
269 
270  @deprecated("use the flags property instead")
271  def autoconfigFlags (self) :
272  """Athena configuration flags
273 
274  This is a backward compatibility version of the flags property,
275  which is preferred."""
276  return self._flags
277 
278  def dataType (self) :
279  """the data type we run on (data, fullsim, fastsim)"""
280  return self._dataType
281 
282  def isPhyslite (self) :
283  """whether we run on PHYSLITE"""
284  return self._isPhyslite
285 
286  def geometry (self) :
287  """the LHC Run period we run on"""
288  return self._flags.GeoModel.Run
289 
290  def dsid(self) :
291  """the mcChannelNumber or DSID of the sample we run on"""
292  return self._flags.Input.MCChannelNumber
293 
294  def campaign(self) :
295  """the MC campaign we run on"""
296  return self._flags.Input.MCCampaign
297 
298  def runNumber(self) :
299  """the MC runNumber"""
300  return int(self._flags.Input.RunNumbers[0])
301 
302  def dataYear(self) :
303  """for data, the corresponding year; for MC, zero"""
304  return self._flags.Input.DataYear
305 
306  def generatorInfo(self) :
307  """the dictionary of MC generators and their versions for the sample we run on"""
308  return self._flags.Input.GeneratorsInfo
309 
310  def hltSummary(self) :
311  """the HLTSummary configuration to be used for the trigger decision tool"""
312  return self._hltSummary
313 
314  def algPostfix (self) :
315  """the current postfix to be appended to algorithm names
316 
317  Blocks should not call this directly, but rather implement the
318  instanceName method, which will be used to generate the postfix
319  automatically."""
320  return self._algPostfix
321 
322  def setAlgPostfix (self, postfix : str) :
323  """set the current postfix to be appended to algorithm names
324 
325  Blocks should not call this directly, but rather implement the
326  instanceName method, which will be used to generate the postfix
327  automatically."""
328  # make sure the postfix matches the expected format ([_a-zA-Z0-9]*)
329  if re.compile ('^[_a-zA-Z0-9]*$').match (postfix) is None :
330  raise ValueError ('invalid algorithm postfix: ' + postfix)
331  if postfix == '' :
332  self._algPostfix = ''
333  elif postfix[0] != '_' :
334  self._algPostfix = '_' + postfix
335  else :
336  self._algPostfix = postfix
337 
338  def getAlgorithm (self, name : str):
339  """get the algorithm with the given name
340 
341  Despite the name this will also return services and tools. It is
342  mostly meant for internal use, particularly for the property
343  overrides."""
344  name = name + self._algPostfix
345  if name not in self._algorithms:
346  return None
347  return self._algorithms[name]
348 
349  def createAlgorithm (self, type, name, reentrant=False) :
350  """create a new algorithm and register it as the current algorithm"""
351  name = name + self._algPostfix
352  if self._pass == 0 :
353  if name in self._algorithms :
354  raise Exception ('duplicate algorithms: ' + name + ' with algPostfix=' + self._algPostfix)
355  if reentrant:
356  alg = DualUseConfig.createReentrantAlgorithm (type, name)
357  else:
358  alg = DualUseConfig.createAlgorithm (type, name)
359 
360  if DualUseConfig.useComponentAccumulator:
361  if self._algSeq :
362  self.CA.addEventAlgo(alg,self._algSeq.name)
363  else :
364  self.CA.addEventAlgo(alg)
365  else:
366  self._algSeq += alg
367  self._algorithms[name] = alg
368  self._currentAlg = alg
369  return alg
370  else :
371  if name not in self._algorithms :
372  raise Exception ('unknown algorithm requested: ' + name)
373  self._currentAlg = self._algorithms[name]
374  if self.CA and self._currentAlg != self.CA.getEventAlgo(name) :
375  raise Exception ('change to algorithm object: ' + name)
376  return self._algorithms[name]
377 
378 
379  def createService (self, type, name) :
380  '''create a new service and register it as the "current algorithm"'''
381  name = name + self._algPostfix
382  if self._pass == 0 :
383  if name in self._algorithms :
384  raise Exception ('duplicate service: ' + name)
385  service = DualUseConfig.createService (type, name)
386  # Avoid importing AthenaCommon.AppMgr in a CA Athena job
387  # as it modifies Gaudi behaviour
388  if DualUseConfig.isAthena:
389  if DualUseConfig.useComponentAccumulator:
390  self.CA.addService(service)
391  else:
392  # We're not, so let's remember this as a "normal" algorithm:
393  self._algSeq += service
394  self._algorithms[name] = service
395  self._currentAlg = service
396  return service
397  else :
398  if name not in self._algorithms :
399  raise Exception ('unknown service requested: ' + name)
400  self._currentAlg = self._algorithms[name]
401  return self._algorithms[name]
402 
403 
404  def createPublicTool (self, type, name) :
405  '''create a new public tool and register it as the "current algorithm"'''
406  name = name + self._algPostfix
407  if self._pass == 0 :
408  if name in self._algorithms :
409  raise Exception ('duplicate public tool: ' + name)
410  tool = DualUseConfig.createPublicTool (type, name)
411  # Avoid importing AthenaCommon.AppMgr in a CA Athena job
412  # as it modifies Gaudi behaviour
413  if DualUseConfig.isAthena:
414  if DualUseConfig.useComponentAccumulator:
415  self.CA.addPublicTool(tool)
416  else:
417  # We're not, so let's remember this as a "normal" algorithm:
418  self._algSeq += tool
419  self._algorithms[name] = tool
420  self._currentAlg = tool
421  return tool
422  else :
423  if name not in self._algorithms :
424  raise Exception ('unknown public tool requested: ' + name)
425  self._currentAlg = self._algorithms[name]
426  return self._algorithms[name]
427 
428 
429  def addPrivateTool (self, propertyName, toolType) :
430  """add a private tool to the current algorithm"""
431  if self._pass == 0 :
432  DualUseConfig.addPrivateTool (self._currentAlg, propertyName, toolType)
433 
434 
435  def setSourceName (self, containerName, sourceName,
436  *, originalName = None, isMet = False) :
437  """set the (default) name of the source/original container
438 
439  This is essentially meant to allow using e.g. the muon
440  configuration and the user not having to manually specify that
441  they want to use the Muons/AnalysisMuons container from the
442  input file.
443 
444  In addition it allows to set the original name of the
445  container (which may be different from the source name), which
446  is mostly/exclusively used for jet containers, so that
447  subsequent configurations know which jet container they
448  operate on.
449  """
450  if containerName not in self._containerConfig :
451  self._containerConfig[containerName] = ContainerConfig (containerName, sourceName, noSysSuffix = self._noSysSuffix, originalName = originalName, isMet = isMet)
452 
453 
454  def writeName (self, containerName, *, isMet=None) :
455  """register that the given container will be made and return
456  its name"""
457  if containerName not in self._containerConfig :
458  self._containerConfig[containerName] = ContainerConfig (containerName, sourceName = None, noSysSuffix = self._noSysSuffix)
459  if self._containerConfig[containerName].sourceName is not None :
460  raise Exception ("trying to write container configured for input: " + containerName)
461  if self._containerConfig[containerName].index != 0 :
462  raise Exception ("trying to write container twice: " + containerName)
463  self._containerConfig[containerName].index += 1
464  if isMet is not None :
465  self._containerConfig[containerName].isMet = isMet
466  return self._containerConfig[containerName].currentName()
467 
468 
469  def readName (self, containerName) :
470  """get the name of the "current copy" of the given container
471 
472  As extra copies get created during processing this will track
473  the correct name of the current copy. Optionally one can pass
474  in the name of the container before the first copy.
475  """
476  if containerName not in self._containerConfig :
477  raise Exception ("no source container for: " + containerName)
478  return self._containerConfig[containerName].currentName()
479 
480 
481  def copyName (self, containerName) :
482  """register that a copy of the container will be made and return
483  its name"""
484  if containerName not in self._containerConfig :
485  raise Exception ("unknown container: " + containerName)
486  self._containerConfig[containerName].index += 1
487  return self._containerConfig[containerName].currentName()
488 
489 
490  def wantCopy (self, containerName) :
491  """ask whether we want/need a copy of the container
492 
493  This usually only happens if no copy of the container has been
494  made yet and the copy is needed to allow modifications, etc.
495  """
496  if containerName not in self._containerConfig :
497  raise Exception ("no source container for: " + containerName)
498  return self._containerConfig[containerName].index == 0
499 
500 
501  def originalName (self, containerName) :
502  """get the "original" name of the given container
503 
504  This is mostly/exclusively used for jet containers, so that
505  subsequent configurations know which jet container they
506  operate on.
507  """
508  if containerName not in self._containerConfig :
509  raise Exception ("container unknown: " + containerName)
510  result = self._containerConfig[containerName].originalName
511  if result is None :
512  raise Exception ("no original name for: " + containerName)
513  return result
514 
515  def getContainerMeta (self, containerName, metaField, defaultValue=None, *, failOnMiss=False) :
516  """get the meta information for the given container
517 
518  This is used to pass down meta-information from the
519  configuration to the algorithms.
520  """
521  if containerName not in self._containerConfig :
522  raise Exception ("container unknown: " + containerName)
523  if metaField in self._containerConfig[containerName].meta :
524  return self._containerConfig[containerName].meta[metaField]
525  if failOnMiss :
526  raise Exception ('unknown meta-field' + metaField + ' on container ' + containerName)
527  return defaultValue
528 
529  def setContainerMeta (self, containerName, metaField, value, *, allowOverwrite=False) :
530  """set the meta information for the given container
531 
532  This is used to pass down meta-information from the
533  configuration to the algorithms.
534  """
535  if containerName not in self._containerConfig :
536  raise Exception ("container unknown: " + containerName)
537  if not allowOverwrite and metaField in self._containerConfig[containerName].meta :
538  raise Exception ('duplicate meta-field' + metaField + ' on container ' + containerName)
539  self._containerConfig[containerName].meta[metaField] = value
540 
541  def isMetContainer (self, containerName) :
542  """whether the given container is registered as a MET container
543 
544  This is mostly/exclusively used for determining whether to
545  write out the whole container or just a single MET term.
546  """
547  if containerName not in self._containerConfig :
548  raise Exception ("container unknown: " + containerName)
549  return self._containerConfig[containerName].isMet
550 
551 
552  def readNameAndSelection (self, containerName, *, excludeFrom = None) :
553  """get the name of the "current copy" of the given container, and the
554  selection string
555 
556  This is mostly meant for MET and OR for whom the actual object
557  selection is relevant, and which as such allow to pass in the
558  working point as "ObjectName.WorkingPoint".
559  """
560  split = containerName.split (".")
561  if len(split) == 1 :
562  objectName = split[0]
563  selectionName = ''
564  elif len(split) == 2 :
565  objectName = split[0]
566  selectionName = split[1]
567  else :
568  raise Exception ('invalid object selection name: ' + containerName)
569  return self.readName (objectName), self.getFullSelection (objectName, selectionName, excludeFrom=excludeFrom)
570 
571 
572  def nextPass (self) :
573  """switch to the next configuration pass
574 
575  Configuration happens in two steps, with all the blocks processed
576  twice. This switches from the first to the second pass.
577  """
578  if self._pass != 0 :
579  raise Exception ("already performed final pass")
580  for name in self._containerConfig :
581  self._containerConfig[name].nextPass ()
582  self._pass = 1
583  self._currentAlg = None
584  self._outputContainers = {}
585 
586 
587  def getPreselection (self, containerName, selectionName, *, asList = False) :
588 
589  """get the preselection string for the given selection on the given
590  container
591  """
592  if selectionName != '' and not self._selectionNameExpr.fullmatch (selectionName) :
593  raise ValueError ('invalid selection name: ' + selectionName)
594  if containerName not in self._containerConfig :
595  return ""
596  config = self._containerConfig[containerName]
597  decorations = []
598  for selection in config.selections :
599  if (selection.name == '' or selection.name == selectionName) and \
600  selection.preselection :
601  decorations += [selection.decoration]
602  if asList :
603  return decorations
604  else :
605  return '&&'.join (decorations)
606 
607 
608  def getFullSelection (self, containerName, selectionName,
609  *, skipBase = False, excludeFrom = None) :
610 
611  """get the selection string for the given selection on the given
612  container
613 
614  This can handle both individual selections or selection
615  expressions (e.g. `loose||tight`) with the later being
616  properly expanded. Either way the base selection (i.e. the
617  selection without a name) will always be applied on top.
618 
619  containerName --- the container the selection is defined on
620  selectionName --- the name of the selection, or a selection
621  expression based on multiple named selections
622  skipBase --- will avoid the base selection, and should normally
623  not be used by the end-user.
624  excludeFrom --- a set of string names of selection sources to exclude
625  e.g. to exclude OR selections from MET
626  """
627  if "." in containerName:
628  raise ValueError (f'invalid containerName argument: {containerName} , it contains a "." '
629  'which is used to indicate container+selection. You should only pass the container.')
630  if containerName not in self._containerConfig :
631  return ""
632 
633  if excludeFrom is None :
634  excludeFrom = set()
635  elif not isinstance(excludeFrom, set) :
636  raise ValueError ('invalid excludeFrom argument (need set of strings): ' + str(excludeFrom))
637 
638  # Check if this is actually a selection expression,
639  # e.g. `A||B` and if so translate it into a complex expression
640  # for the user. I'm not trying to do any complex syntax
641  # recognition, but instead just produce an expression that the
642  # C++ parser ought to be able to read.
643  if selectionName != '' and \
644  not self._selectionNameExpr.fullmatch (selectionName) :
645  result = ''
646  while selectionName != '' :
647  match = self._selectionNameExpr.match (selectionName)
648  if not match :
649  result += selectionName[0]
650  selectionName = selectionName[1:]
651  else :
652  subname = match.group(0)
653  subresult = self.getFullSelection (containerName, subname, skipBase = True, excludeFrom=excludeFrom)
654  if subresult != '' :
655  result += '(' + subresult + ')'
656  else :
657  result += 'true'
658  selectionName = selectionName[len(subname):]
659  subresult = self.getFullSelection (containerName, '', excludeFrom=excludeFrom)
660  if subresult != '' :
661  result = subresult + '&&(' + result + ')'
662  return '(' + result + ')' if result !='' else ''
663 
664  config = self._containerConfig[containerName]
665  decorations = []
666  hasSelectionName = False
667  for selection in config.selections :
668  if ((selection.name == '' and not skipBase) or selection.name == selectionName) and (selection.comesFrom not in excludeFrom) :
669  decorations += [selection.decoration]
670  if selection.name == selectionName :
671  hasSelectionName = True
672  if not hasSelectionName and selectionName != '' :
673  raise KeyError ('invalid selection name: ' + containerName + '.' + selectionName)
674  return '&&'.join (decorations)
675 
676 
677  def getSelectionCutFlow (self, containerName, selectionName) :
678 
679  """get the individual selections as a list for producing the cutflow for
680  the given selection on the given container
681 
682  This can only handle individual selections, not selection
683  expressions (e.g. `loose||tight`).
684 
685  """
686  if containerName not in self._containerConfig :
687  return []
688 
689  # Check if this is actually a selection expression,
690  # e.g. `A||B` and if so translate it into a complex expression
691  # for the user. I'm not trying to do any complex syntax
692  # recognition, but instead just produce an expression that the
693  # C++ parser ought to be able to read.
694  if selectionName != '' and \
695  not self._selectionNameExpr.fullmatch (selectionName) :
696  raise ValueError ('not allowed to do cutflow on selection expression: ' + selectionName)
697 
698  config = self._containerConfig[containerName]
699  decorations = []
700  for selection in config.selections :
701  if (selection.name == '' or selection.name == selectionName) :
702  decorations += [selection.decoration]
703  return decorations
704 
705 
706  def addEventCutFlow (self, selection, decorations) :
707 
708  """register a new event cutflow, adding it to the dictionary with key 'selection'
709  and value 'decorations', a list of decorated selections
710  """
711  if self._pass == 0:
712  if selection in self._eventcutflow.keys():
713  raise ValueError ('the event cutflow dictionary already contains an entry ' + selection)
714  else:
715  self._eventcutflow[selection] = decorations
716 
717 
718  def getEventCutFlow (self, selection) :
719 
720  """get the list of decorated selections for an event cutflow, corresponding to
721  key 'selection'
722  """
723  return self._eventcutflow[selection]
724 
725 
726  def addSelection (self, containerName, selectionName, decoration,
727  **kwargs) :
728  """add another selection decoration to the selection of the given
729  name for the given container"""
730  if selectionName != '' and not self._selectionNameExpr.fullmatch (selectionName) :
731  raise ValueError ('invalid selection name: ' + selectionName)
732  if containerName not in self._containerConfig :
733  self._containerConfig[containerName] = ContainerConfig (containerName, containerName, noSysSuffix=self._noSysSuffix)
734  config = self._containerConfig[containerName]
735  selection = SelectionConfig (selectionName, decoration, **kwargs)
736  config.selections.append (selection)
737 
738 
739  def addOutputContainer (self, containerName, outputContainerName) :
740  """register a copy of a container used in outputs"""
741  if containerName not in self._containerConfig :
742  raise KeyError ("container unknown: " + containerName)
743  if outputContainerName in self._outputContainers :
744  raise KeyError ("duplicate output container name: " + outputContainerName)
745  self._outputContainers[outputContainerName] = containerName
746 
747 
748  def checkOutputContainer (self, containerName) :
749  """check whether a given container has been registered in outputs"""
750  return containerName in self._outputContainers.values()
751 
752 
753  def getOutputContainerOrigin (self, outputContainerName) :
754  """Get the name of the actual container, for which an output is registered"""
755  try:
756  return self._outputContainers[outputContainerName]
757  except KeyError:
758  try:
759  return self._containerConfig[outputContainerName].name
760  except KeyError:
761  raise KeyError ("output container unknown: " + outputContainerName)
762 
763 
764  def addOutputVar (self, containerName, variableName, outputName,
765  *, noSys=False, enabled=True) :
766  """add an output variable for the given container to the output
767  """
768 
769  if containerName not in self._containerConfig :
770  raise KeyError ("container unknown: " + containerName)
771  baseConfig = self._containerConfig[containerName].outputs
772  if outputName in baseConfig :
773  raise KeyError ("duplicate output variable name: " + outputName)
774  config = OutputConfig (containerName, variableName, noSys=noSys, enabled=enabled)
775  baseConfig[outputName] = config
776 
777 
778  def getOutputVars (self, containerName) :
779  """get the output variables for the given container"""
780  if containerName in self._outputContainers :
781  containerName = self._outputContainers[containerName]
782  if containerName not in self._containerConfig :
783  raise KeyError ("unknown container for output: " + containerName)
784  return self._containerConfig[containerName].outputs
785 
786 
787  def getSelectionNames (self, containerName, excludeFrom = None) :
788  """Retrieve set of unique selections defined for a given container"""
789  if containerName not in self._containerConfig :
790  return []
791  if excludeFrom is None:
792  excludeFrom = set()
793  elif not isinstance(excludeFrom, set) :
794  raise ValueError ('invalid excludeFrom argument (need set of strings): ' + str(excludeFrom))
795 
796  config = self._containerConfig[containerName]
797  # because cuts are registered individually, selection names can repeat themselves
798  # but we are interested in unique names only
799  selectionNames = set()
800  for selection in config.selections:
801  if selection.comesFrom in excludeFrom:
802  continue
803  # skip flags which should be disabled in output
804  if selection.writeToOutput:
805  selectionNames.add(selection.name)
806  return selectionNames
python.ConfigAccumulator.ConfigAccumulator.getPreselection
def getPreselection(self, containerName, selectionName, *asList=False)
Definition: ConfigAccumulator.py:587
python.ConfigAccumulator.ConfigAccumulator.hltSummary
def hltSummary(self)
Definition: ConfigAccumulator.py:310
python.TrigDecisionToolHelpers.getRun3NavigationContainerFromInput_forAnalysisBase
def getRun3NavigationContainerFromInput_forAnalysisBase(flags)
Definition: TrigDecisionToolHelpers.py:22
python.ConfigAccumulator.ConfigAccumulator.dsid
def dsid(self)
Definition: ConfigAccumulator.py:290
python.JetAnalysisCommon.ComponentAccumulator
ComponentAccumulator
Definition: JetAnalysisCommon.py:302
python.ConfigAccumulator.ContainerConfig.nextPass
def nextPass(self)
Definition: ConfigAccumulator.py:110
python.ConfigAccumulator.ConfigAccumulator.flags
def flags(self)
Definition: ConfigAccumulator.py:266
python.ConfigAccumulator.ContainerConfig.viewIndex
viewIndex
Definition: ConfigAccumulator.py:85
python.ConfigAccumulator.SelectionConfig.__init__
def __init__(self, selectionName, decoration, *bits=0, preselection=None, comesFrom='', writeToOutput=True)
Definition: ConfigAccumulator.py:44
python.ConfigAccumulator.ConfigAccumulator.getContainerMeta
def getContainerMeta(self, containerName, metaField, defaultValue=None, *failOnMiss=False)
Definition: ConfigAccumulator.py:515
python.ConfigAccumulator.ConfigAccumulator.createPublicTool
def createPublicTool(self, type, name)
Definition: ConfigAccumulator.py:404
python.ConfigAccumulator.ConfigAccumulator.getOutputContainerOrigin
def getOutputContainerOrigin(self, outputContainerName)
Definition: ConfigAccumulator.py:753
python.ConfigAccumulator.ConfigAccumulator.addSelection
def addSelection(self, containerName, selectionName, decoration, **kwargs)
Definition: ConfigAccumulator.py:726
python.ConfigAccumulator.ConfigAccumulator.isPhyslite
def isPhyslite(self)
Definition: ConfigAccumulator.py:282
python.ConfigAccumulator.ConfigAccumulator.setContainerMeta
def setContainerMeta(self, containerName, metaField, value, *allowOverwrite=False)
Definition: ConfigAccumulator.py:529
python.ConfigAccumulator.ConfigAccumulator._algPostfix
_algPostfix
Definition: ConfigAccumulator.py:235
python.ConfigAccumulator.ConfigAccumulator.__init__
def __init__(self, *flags=None, algSeq=None, noSysSuffix=False, noSystematics=None, dataType=None, isPhyslite=None, geometry=None, dsid=0, campaign=None, runNumber=None, autoconfigFromFlags=None, dataYear=0)
Definition: ConfigAccumulator.py:137
python.ConfigAccumulator.ContainerConfig.originalName
originalName
Definition: ConfigAccumulator.py:81
python.ConfigAccumulator.SelectionConfig
Definition: ConfigAccumulator.py:38
python.ConfigAccumulator.OutputConfig.enabled
enabled
Definition: ConfigAccumulator.py:66
python.ConfigAccumulator.SelectionConfig.comesFrom
comesFrom
Definition: ConfigAccumulator.py:51
python.ConfigAccumulator.ConfigAccumulator.addOutputVar
def addOutputVar(self, containerName, variableName, outputName, *noSys=False, enabled=True)
Definition: ConfigAccumulator.py:764
python.ConfigAccumulator.ConfigAccumulator.campaign
def campaign(self)
Definition: ConfigAccumulator.py:294
python.ConfigAccumulator.ConfigAccumulator._selectionNameExpr
_selectionNameExpr
Definition: ConfigAccumulator.py:241
python.ConfigAccumulator.ConfigAccumulator._containerConfig
_containerConfig
Definition: ConfigAccumulator.py:236
python.ConfigAccumulator.ConfigAccumulator.readNameAndSelection
def readNameAndSelection(self, containerName, *excludeFrom=None)
Definition: ConfigAccumulator.py:552
python.ConfigAccumulator.ConfigAccumulator
Definition: ConfigAccumulator.py:120
python.ConfigAccumulator.ConfigAccumulator.getAlgorithm
def getAlgorithm(self, str name)
Definition: ConfigAccumulator.py:338
python.ConfigAccumulator.ContainerConfig.systematicsName
def systematicsName(name, *noSysSuffix)
Definition: ConfigAccumulator.py:101
python.ConfigAccumulator.ConfigAccumulator.generatorInfo
def generatorInfo(self)
Definition: ConfigAccumulator.py:306
python.ConfigAccumulator.ConfigAccumulator.getSelectionNames
def getSelectionNames(self, containerName, excludeFrom=None)
Definition: ConfigAccumulator.py:787
python.ConfigAccumulator.ConfigAccumulator.setSourceName
def setSourceName(self, containerName, sourceName, *originalName=None, isMet=False)
Definition: ConfigAccumulator.py:435
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:808
python.ConfigAccumulator.ConfigAccumulator.getEventCutFlow
def getEventCutFlow(self, selection)
Definition: ConfigAccumulator.py:718
python.ConfigAccumulator.ContainerConfig.currentName
def currentName(self)
Definition: ConfigAccumulator.py:91
python.ConfigAccumulator.ContainerConfig.maxIndex
maxIndex
Definition: ConfigAccumulator.py:84
python.ConfigAccumulator.ConfigAccumulator._algorithms
_algorithms
Definition: ConfigAccumulator.py:239
python.ConfigAccumulator.OutputConfig.outputContainerName
outputContainerName
Definition: ConfigAccumulator.py:63
python.ConfigAccumulator.SelectionConfig.writeToOutput
writeToOutput
Definition: ConfigAccumulator.py:52
python.ConfigAccumulator.ConfigAccumulator.copyName
def copyName(self, containerName)
Definition: ConfigAccumulator.py:481
python.ConfigAccumulator.ConfigAccumulator.getOutputVars
def getOutputVars(self, containerName)
Definition: ConfigAccumulator.py:778
python.ConfigAccumulator.ConfigAccumulator.addEventCutFlow
def addEventCutFlow(self, selection, decorations)
Definition: ConfigAccumulator.py:706
python.ConfigAccumulator.ConfigAccumulator._noSysSuffix
_noSysSuffix
Definition: ConfigAccumulator.py:234
python.ConfigAccumulator.ConfigAccumulator.dataType
def dataType(self)
Definition: ConfigAccumulator.py:278
python.ConfigAccumulator.ConfigAccumulator._pass
_pass
Definition: ConfigAccumulator.py:238
python.ConfigAccumulator.ConfigAccumulator._isPhyslite
_isPhyslite
Definition: ConfigAccumulator.py:226
python.ConfigAccumulator.OutputConfig
Definition: ConfigAccumulator.py:58
python.ConfigAccumulator.ConfigAccumulator.noSystematics
def noSystematics(self)
Definition: ConfigAccumulator.py:261
python.ConfigAccumulator.ConfigAccumulator._hltSummary
_hltSummary
Definition: ConfigAccumulator.py:227
python.ConfigAccumulator.ConfigAccumulator.runNumber
def runNumber(self)
Definition: ConfigAccumulator.py:298
python.ConfigAccumulator.ConfigAccumulator.checkOutputContainer
def checkOutputContainer(self, containerName)
Definition: ConfigAccumulator.py:748
python.ConfigAccumulator.ConfigAccumulator.geometry
def geometry(self)
Definition: ConfigAccumulator.py:286
python.ConfigAccumulator.OutputConfig.__init__
def __init__(self, origContainerName, variableName, *noSys, enabled)
Definition: ConfigAccumulator.py:61
python.ConfigAccumulator.deprecated
def deprecated(str reason="")
Definition: ConfigAccumulator.py:13
python.ConfigAccumulator.ContainerConfig.isMet
isMet
Definition: ConfigAccumulator.py:86
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:232
python.ConfigAccumulator.ContainerConfig.index
index
Definition: ConfigAccumulator.py:83
python.ConfigAccumulator.ConfigAccumulator._dataType
_dataType
Definition: ConfigAccumulator.py:225
python.ConfigAccumulator.ConfigAccumulator._eventcutflow
_eventcutflow
Definition: ConfigAccumulator.py:243
python.ConfigAccumulator.ContainerConfig.meta
meta
Definition: ConfigAccumulator.py:89
python.ConfigAccumulator.ConfigAccumulator._algSeq
_algSeq
Definition: ConfigAccumulator.py:232
python.ConfigAccumulator.SelectionConfig.preselection
preselection
Definition: ConfigAccumulator.py:48
python.ConfigAccumulator.ConfigAccumulator._outputContainers
_outputContainers
Definition: ConfigAccumulator.py:237
python.ConfigAccumulator.ConfigAccumulator.createAlgorithm
def createAlgorithm(self, type, name, reentrant=False)
Definition: ConfigAccumulator.py:349
python.ConfigAccumulator.ConfigAccumulator._currentAlg
_currentAlg
Definition: ConfigAccumulator.py:240
python.ConfigAccumulator.ConfigAccumulator.dataYear
def dataYear(self)
Definition: ConfigAccumulator.py:302
python.ConfigAccumulator.ConfigAccumulator.getFullSelection
def getFullSelection(self, containerName, selectionName, *skipBase=False, excludeFrom=None)
Definition: ConfigAccumulator.py:608
python.ConfigAccumulator.ConfigAccumulator.isMetContainer
def isMetContainer(self, containerName)
Definition: ConfigAccumulator.py:541
python.ConfigAccumulator.ConfigAccumulator.addPrivateTool
def addPrivateTool(self, propertyName, toolType)
Definition: ConfigAccumulator.py:429
python.ConfigAccumulator.ContainerConfig.noSysSuffix
noSysSuffix
Definition: ConfigAccumulator.py:82
python.ConfigAccumulator.ContainerConfig.outputs
outputs
Definition: ConfigAccumulator.py:88
python.ConfigAccumulator.ContainerConfig.__init__
def __init__(self, name, sourceName, *originalName=None, isMet=False, noSysSuffix)
Definition: ConfigAccumulator.py:78
python.ConfigAccumulator.ConfigAccumulator.algPostfix
def algPostfix(self)
Definition: ConfigAccumulator.py:314
python.ConfigAccumulator.ConfigAccumulator.addOutputContainer
def addOutputContainer(self, containerName, outputContainerName)
Definition: ConfigAccumulator.py:739
python.ConfigAccumulator.ConfigAccumulator._noSystematics
_noSystematics
Definition: ConfigAccumulator.py:233
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
python.AllConfigFlags.initConfigFlags
def initConfigFlags()
Definition: AllConfigFlags.py:19
python.ConfigAccumulator.ContainerConfig.name
name
Definition: ConfigAccumulator.py:79
python.ConfigAccumulator.SelectionConfig.name
name
Definition: ConfigAccumulator.py:45
python.ConfigAccumulator.OutputConfig.noSys
noSys
Definition: ConfigAccumulator.py:65
python.ConfigAccumulator.ConfigAccumulator.getSelectionCutFlow
def getSelectionCutFlow(self, containerName, selectionName)
Definition: ConfigAccumulator.py:677
python.ConfigAccumulator.ConfigAccumulator._flags
_flags
Definition: ConfigAccumulator.py:148
python.ConfigAccumulator.ConfigAccumulator.writeName
def writeName(self, containerName, *isMet=None)
Definition: ConfigAccumulator.py:454
python.ConfigAccumulator.ConfigAccumulator.createService
def createService(self, type, name)
Definition: ConfigAccumulator.py:379
python.ConfigAccumulator.ContainerConfig.sourceName
sourceName
Definition: ConfigAccumulator.py:80
python.ConfigAccumulator.ConfigAccumulator.originalName
def originalName(self, containerName)
Definition: ConfigAccumulator.py:501
python.ConfigAccumulator.ConfigAccumulator.autoconfigFlags
def autoconfigFlags(self)
Definition: ConfigAccumulator.py:271
str
Definition: BTagTrackIpAccessor.cxx:11
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:801
python.ConfigAccumulator.DataType
Definition: ConfigAccumulator.py:31
python.ConfigAccumulator.OutputConfig.__repr__
def __repr__(self)
Definition: ConfigAccumulator.py:69
python.ConfigAccumulator.OutputConfig.origContainerName
origContainerName
Definition: ConfigAccumulator.py:62
python.ConfigAccumulator.SelectionConfig.decoration
decoration
Definition: ConfigAccumulator.py:46
python.ConfigAccumulator.ConfigAccumulator.CA
CA
Definition: ConfigAccumulator.py:248
python.ConfigAccumulator.ConfigAccumulator.setAlgPostfix
def setAlgPostfix(self, str postfix)
Definition: ConfigAccumulator.py:322
python.ConfigAccumulator.ContainerConfig.selections
selections
Definition: ConfigAccumulator.py:87
python.ConfigAccumulator.ContainerConfig
Definition: ConfigAccumulator.py:72
python.ConfigAccumulator.ConfigAccumulator.nextPass
def nextPass(self)
Definition: ConfigAccumulator.py:572
python.ConfigAccumulator.ConfigAccumulator.wantCopy
def wantCopy(self, containerName)
Definition: ConfigAccumulator.py:490
python.ConfigAccumulator.ConfigAccumulator.readName
def readName(self, containerName)
Definition: ConfigAccumulator.py:469
python.ConfigAccumulator.OutputConfig.variableName
variableName
Definition: ConfigAccumulator.py:64