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