3 import AnaAlgorithm.DualUseConfig
as DualUseConfig
4 from AthenaConfiguration.Enums
import LHCPeriod, FlagEnum
12 deprecationWarningCategory = FutureWarning
15 message = f
"{func.__qualname__} is deprecated."
17 message +=
" " + reason
19 @functools.wraps(func)
20 def wrapper(*args, **kwargs):
23 category=deprecationWarningCategory,
26 return func(*args, **kwargs)
32 """holds the various data types as an enum"""
39 """all the data for a given selection that has been registered
41 the bits argument is for backward compatibility, does nothing, and will be
42 removed in the future."""
45 *, bits=0, preselection=None, comesFrom = '',
47 self.
name = selectionName
49 if preselection
is not None :
59 """all the data for a given variables in the output that has been registered"""
61 def __init__ (self, origContainerName, variableName,
70 return f
'OutputConfig("{self.outputContainerName}.{self.variableName}" [enabled={self.enabled}])'
73 """all the auto-generated meta-configuration data for a single container
75 This tracks the naming of all temporary containers, as well as all the
76 selection decorations."""
78 def __init__ (self, name, sourceName, *, originalName = None, isMet = False, noSysSuffix) :
94 raise Exception (
"should not get here, reading container name before created: " + self.
name)
102 """map an internal name to a name for systematics data handles
104 Right now this just means appending a _%SYS% to the name."""
106 return name +
"_%SYS%"
121 """a class that accumulates a configuration from blocks into an
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
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
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):
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)
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")
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")
171 raise ValueError(
"Cannot pass both dataYear and flags/autoconfigFromFlags arguments")
173 if self.
_flags.Input.isMC:
174 if self.
_flags.Sim.ISF.Simulator.usesFastCaloSim():
175 dataType = DataType.FastSim
177 dataType = DataType.FullSim
179 dataType = DataType.Data
180 isPhyslite =
'StreamDAOD_PHYSLITE' in self.
_flags.Input.ProcessingTags
181 from TrigDecisionTool.TrigDecisionToolHelpers
import (
182 getRun3NavigationContainerFromInput_forAnalysisBase)
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
189 raise ValueError (
"need to specify dataType if flags are not set")
191 if isinstance(dataType, str):
193 dataType = DataType.FullSim
194 elif dataType ==
'afii':
195 dataType = DataType.FastSim
198 if isPhyslite
is None:
200 if geometry
is not None:
202 geometry = LHCPeriod(geometry)
203 if geometry
is LHCPeriod.Run1:
204 raise ValueError (
"invalid Run geometry: %s" % geometry.value)
205 flags.GeoModel.Run = geometry
207 flags.Input.MCChannelNumber = dsid
208 if campaign
is not None:
209 flags.Input.MCCampaign = campaign
211 flags.Input.DataYear = dataYear
212 if runNumber
is None:
216 flags.Input.RunNumbers = [runNumber]
217 hltSummary =
'HLTNav_Summary_DAODSlimmed'
249 if DualUseConfig.useComponentAccumulator:
250 from AthenaConfiguration.ComponentAccumulator
import ComponentAccumulator
255 self.
CA.addSequence(algSeq)
258 raise ValueError (
"need to pass algSeq if not using ComponentAccumulator")
262 """noSystematics flag used by CommonServices block"""
267 """Athena configuration flags"""
272 """Athena configuration flags
274 This is a backward compatibility version of the flags property,
275 which is preferred."""
279 """the data type we run on (data, fullsim, fastsim)"""
283 """whether we run on PHYSLITE"""
287 """the LHC Run period we run on"""
288 return self.
_flags.GeoModel.Run
291 """the mcChannelNumber or DSID of the sample we run on"""
292 return self.
_flags.Input.MCChannelNumber
295 """the MC campaign we run on"""
296 return self.
_flags.Input.MCCampaign
299 """the MC runNumber"""
300 return int(self.
_flags.Input.RunNumbers[0])
303 """for data, the corresponding year; for MC, zero"""
304 return self.
_flags.Input.DataYear
307 """the dictionary of MC generators and their versions for the sample we run on"""
308 return self.
_flags.Input.GeneratorsInfo
311 """the HLTSummary configuration to be used for the trigger decision tool"""
315 """the current postfix to be appended to algorithm names
317 Blocks should not call this directly, but rather implement the
318 instanceName method, which will be used to generate the postfix
323 """set the current postfix to be appended to algorithm names
325 Blocks should not call this directly, but rather implement the
326 instanceName method, which will be used to generate the postfix
329 if re.compile (
'^[_a-zA-Z0-9]*$').match (postfix)
is None :
330 raise ValueError (
'invalid algorithm postfix: ' + postfix)
333 elif postfix[0] !=
'_' :
339 """get the algorithm with the given name
341 Despite the name this will also return services and tools. It is
342 mostly meant for internal use, particularly for the property
350 """create a new algorithm and register it as the current algorithm"""
354 raise Exception (
'duplicate algorithms: ' + name +
' with algPostfix=' + self.
_algPostfix)
356 alg = DualUseConfig.createReentrantAlgorithm (type, name)
358 alg = DualUseConfig.createAlgorithm (type, name)
360 if DualUseConfig.useComponentAccumulator:
362 self.
CA.addEventAlgo(alg,self.
_algSeq.name)
364 self.
CA.addEventAlgo(alg)
372 raise Exception (
'unknown algorithm requested: ' + name)
375 raise Exception (
'change to algorithm object: ' + name)
380 '''create a new service and register it as the "current algorithm"'''
384 raise Exception (
'duplicate service: ' + name)
385 service = DualUseConfig.createService (type, name)
388 if DualUseConfig.isAthena:
389 if DualUseConfig.useComponentAccumulator:
390 self.
CA.addService(service)
399 raise Exception (
'unknown service requested: ' + name)
405 '''create a new public tool and register it as the "current algorithm"'''
409 raise Exception (
'duplicate public tool: ' + name)
410 tool = DualUseConfig.createPublicTool (type, name)
413 if DualUseConfig.isAthena:
414 if DualUseConfig.useComponentAccumulator:
415 self.
CA.addPublicTool(tool)
424 raise Exception (
'unknown public tool requested: ' + name)
430 """add a private tool to the current algorithm"""
432 DualUseConfig.addPrivateTool (self.
_currentAlg, propertyName, toolType)
436 *, originalName = None, isMet = False) :
437 """set the (default) name of the source/original container
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
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
451 self.
_containerConfig[containerName] = ContainerConfig (containerName, sourceName, noSysSuffix = self.
_noSysSuffix, originalName = originalName, isMet = isMet)
455 """register that the given container will be made and return
460 raise Exception (
"trying to write container configured for input: " + containerName)
462 raise Exception (
"trying to write container twice: " + containerName)
464 if isMet
is not None :
470 """get the name of the "current copy" of the given container
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.
477 raise Exception (
"no source container for: " + containerName)
482 """register that a copy of the container will be made and return
485 raise Exception (
"unknown container: " + containerName)
491 """ask whether we want/need a copy of the container
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.
497 raise Exception (
"no source container for: " + containerName)
502 """get the "original" name of the given container
504 This is mostly/exclusively used for jet containers, so that
505 subsequent configurations know which jet container they
509 raise Exception (
"container unknown: " + containerName)
512 raise Exception (
"no original name for: " + containerName)
515 def getContainerMeta (self, containerName, metaField, defaultValue=None, *, failOnMiss=False) :
516 """get the meta information for the given container
518 This is used to pass down meta-information from the
519 configuration to the algorithms.
522 raise Exception (
"container unknown: " + containerName)
526 raise Exception (
'unknown meta-field' + metaField +
' on container ' + containerName)
530 """set the meta information for the given container
532 This is used to pass down meta-information from the
533 configuration to the algorithms.
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)
542 """whether the given container is registered as a MET container
544 This is mostly/exclusively used for determining whether to
545 write out the whole container or just a single MET term.
548 raise Exception (
"container unknown: " + containerName)
553 """get the name of the "current copy" of the given container, and the
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".
560 split = containerName.split (
".")
562 objectName = split[0]
564 elif len(split) == 2 :
565 objectName = split[0]
566 selectionName = split[1]
568 raise Exception (
'invalid object selection name: ' + containerName)
573 """switch to the next configuration pass
575 Configuration happens in two steps, with all the blocks processed
576 twice. This switches from the first to the second pass.
579 raise Exception (
"already performed final pass")
589 """get the preselection string for the given selection on the given
593 raise ValueError (
'invalid selection name: ' + selectionName)
598 for selection
in config.selections :
599 if (selection.name ==
'' or selection.name == selectionName)
and \
600 selection.preselection :
601 decorations += [selection.decoration]
605 return '&&'.join (decorations)
609 *, skipBase = False, excludeFrom = None) :
611 """get the selection string for the given selection on the given
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.
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
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.')
633 if excludeFrom
is None :
635 elif not isinstance(excludeFrom, set) :
636 raise ValueError (
'invalid excludeFrom argument (need set of strings): ' +
str(excludeFrom))
643 if selectionName !=
'' and \
646 while selectionName !=
'' :
649 result += selectionName[0]
650 selectionName = selectionName[1:]
652 subname = match.group(0)
653 subresult = self.
getFullSelection (containerName, subname, skipBase =
True, excludeFrom=excludeFrom)
655 result +=
'(' + subresult +
')'
658 selectionName = selectionName[len(subname):]
659 subresult = self.
getFullSelection (containerName,
'', excludeFrom=excludeFrom)
661 result = subresult +
'&&(' + result +
')'
662 return '(' + result +
')' if result !=
'' else ''
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)
679 """get the individual selections as a list for producing the cutflow for
680 the given selection on the given container
682 This can only handle individual selections, not selection
683 expressions (e.g. `loose||tight`).
694 if selectionName !=
'' and \
696 raise ValueError (
'not allowed to do cutflow on selection expression: ' + selectionName)
700 for selection
in config.selections :
701 if (selection.name ==
'' or selection.name == selectionName) :
702 decorations += [selection.decoration]
708 """register a new event cutflow, adding it to the dictionary with key 'selection'
709 and value 'decorations', a list of decorated selections
713 raise ValueError (
'the event cutflow dictionary already contains an entry ' + selection)
720 """get the list of decorated selections for an event cutflow, corresponding to
728 """add another selection decoration to the selection of the given
729 name for the given container"""
731 raise ValueError (
'invalid selection name: ' + selectionName)
735 selection = SelectionConfig (selectionName, decoration, **kwargs)
736 config.selections.append (selection)
740 """register a copy of a container used in outputs"""
742 raise KeyError (
"container unknown: " + containerName)
744 raise KeyError (
"duplicate output container name: " + outputContainerName)
749 """check whether a given container has been registered in outputs"""
754 """Get the name of the actual container, for which an output is registered"""
761 raise KeyError (
"output container unknown: " + outputContainerName)
765 *, noSys=False, enabled=True) :
766 """add an output variable for the given container to the output
770 raise KeyError (
"container unknown: " + containerName)
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
779 """get the output variables for the given container"""
783 raise KeyError (
"unknown container for output: " + containerName)
788 """Retrieve set of unique selections defined for a given container"""
791 if excludeFrom
is None:
793 elif not isinstance(excludeFrom, set) :
794 raise ValueError (
'invalid excludeFrom argument (need set of strings): ' +
str(excludeFrom))
799 selectionNames =
set()
800 for selection
in config.selections:
801 if selection.comesFrom
in excludeFrom:
804 if selection.writeToOutput:
805 selectionNames.add(selection.name)
806 return selectionNames