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     """Warning raised when an expert-only configuration option is used.""" 
   35 if not any(f[0] == 
'error' and f[2] 
is ExpertModeWarning 
for f 
in warnings.filters):
 
   36     warnings.simplefilter(
'error', ExpertModeWarning)
 
   39     """holds the various data types as an enum""" 
   46     """all the data for a given selection that has been registered 
   48     the bits argument is for backward compatibility, does nothing, and will be 
   49     removed in the future.""" 
   52                   *, bits=0, preselection=None, comesFrom = '',
 
   54         self.
name = selectionName
 
   56         if preselection 
is not None :
 
   66     """all the data for a given variables in the output that has been registered""" 
   68     def __init__ (self, origContainerName, variableName,
 
   77         return f
'OutputConfig("{self.outputContainerName}.{self.variableName}" [enabled={self.enabled}])' 
   80     """all the auto-generated meta-configuration data for a single container 
   82     This tracks the naming of all temporary containers, as well as all the 
   83     selection decorations.""" 
   85     def __init__ (self, name, sourceName, *, originalName = None, isMet = False, noSysSuffix) :
 
  101                 raise Exception (
"should not get here, reading container name before created: " + self.
name)
 
  109         """map an internal name to a name for systematics data handles 
  111         Right now this just means appending a _%SYS% to the name.""" 
  113             return name + 
"_%SYS%" 
  128     """a class that accumulates a configuration from blocks into an 
  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 
  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 
  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):
 
  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)
 
  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")
 
  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")
 
  178                 raise ValueError(
"Cannot pass both dataYear and flags/autoconfigFromFlags arguments")
 
  180             if self.
_flags.Input.isMC:
 
  181                 if self.
_flags.Sim.ISF.Simulator.usesFastCaloSim():
 
  182                     dataType = DataType.FastSim
 
  184                     dataType = DataType.FullSim
 
  186                 dataType = DataType.Data
 
  187             isPhyslite = 
'StreamDAOD_PHYSLITE' in self.
_flags.Input.ProcessingTags
 
  188             from TrigDecisionTool.TrigDecisionToolHelpers 
import (
 
  189                 getRun3NavigationContainerFromInput_forAnalysisBase)
 
  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
 
  196                 raise ValueError (
"need to specify dataType if flags are not set")
 
  198             if isinstance(dataType, str):
 
  200                     dataType = DataType.FullSim
 
  201                 elif dataType == 
'afii':
 
  202                     dataType = DataType.FastSim
 
  205             if isPhyslite 
is None:
 
  207             if geometry 
is not None:
 
  209                 geometry = LHCPeriod(geometry)
 
  210                 if geometry 
is LHCPeriod.Run1:
 
  211                     raise ValueError (
"invalid Run geometry: %s" % geometry.value)
 
  212                 flags.GeoModel.Run = geometry
 
  214                 flags.Input.MCChannelNumber = dsid
 
  215             if campaign 
is not None:
 
  216                 flags.Input.MCCampaign = campaign
 
  218                 flags.Input.DataYear = dataYear
 
  219             if runNumber 
is None:
 
  223             flags.Input.RunNumbers = [runNumber]
 
  224             hltSummary = 
'HLTNav_Summary_DAODSlimmed' 
  256         if DualUseConfig.useComponentAccumulator:
 
  257             from AthenaConfiguration.ComponentAccumulator 
import ComponentAccumulator
 
  262                 self.
CA.addSequence(algSeq)
 
  265                 raise ValueError (
"need to pass algSeq if not using ComponentAccumulator")
 
  269         """noSystematics flag used by CommonServices block""" 
  274         """Athena configuration flags""" 
  279         """Athena configuration flags 
  281         This is a backward compatibility version of the flags property, 
  282         which is preferred.""" 
  286         """the data type we run on (data, fullsim, fastsim)""" 
  290         """whether we run on PHYSLITE""" 
  294         """the LHC Run period we run on""" 
  295         return self.
_flags.GeoModel.Run
 
  298         """the mcChannelNumber or DSID of the sample we run on""" 
  299         return self.
_flags.Input.MCChannelNumber
 
  302         """the MC campaign we run on""" 
  303         return self.
_flags.Input.MCCampaign
 
  306         """the MC runNumber""" 
  307         return int(self.
_flags.Input.RunNumbers[0])
 
  310         """for data, the corresponding year; for MC, zero""" 
  311         return self.
_flags.Input.DataYear
 
  314         """the dictionary of MC generators and their versions for the sample we run on""" 
  315         return self.
_flags.Input.GeneratorsInfo
 
  318         """the HLTSummary configuration to be used for the trigger decision tool""" 
  322         """the current postfix to be appended to algorithm names 
  324         Blocks should not call this directly, but rather implement the 
  325         instanceName method, which will be used to generate the postfix 
  330         """set the current postfix to be appended to algorithm names 
  332         Blocks should not call this directly, but rather implement the 
  333         instanceName method, which will be used to generate the postfix 
  336         if re.compile (
'^[_a-zA-Z0-9]*$').match (postfix) 
is None :
 
  337             raise ValueError (
'invalid algorithm postfix: ' + postfix)
 
  340         elif postfix[0] != 
'_' :
 
  346         """get the algorithm with the given name 
  348         Despite the name this will also return services and tools. It is 
  349         mostly meant for internal use, particularly for the property 
  357         """create a new algorithm and register it as the current algorithm""" 
  361                 raise Exception (
'duplicate algorithms: ' + name + 
' with algPostfix=' + self.
_algPostfix)
 
  363                 alg = DualUseConfig.createReentrantAlgorithm (type, name)
 
  365                 alg = DualUseConfig.createAlgorithm (type, name)
 
  367             if DualUseConfig.useComponentAccumulator:
 
  369                     self.
CA.addEventAlgo(alg,self.
_algSeq.name)
 
  371                     self.
CA.addEventAlgo(alg)
 
  379                 raise Exception (
'unknown algorithm requested: ' + name)
 
  382                 raise Exception (
'change to algorithm object: ' + name)
 
  387         '''create a new service and register it as the "current algorithm"''' 
  391                 raise Exception (
'duplicate service: ' + name)
 
  392             service = DualUseConfig.createService (type, name)
 
  395             if DualUseConfig.isAthena:
 
  396                 if DualUseConfig.useComponentAccumulator:
 
  397                     self.
CA.addService(service)
 
  406                 raise Exception (
'unknown service requested: ' + name)
 
  412         '''create a new public tool and register it as the "current algorithm"''' 
  416                 raise Exception (
'duplicate public tool: ' + name)
 
  417             tool = DualUseConfig.createPublicTool (type, name)
 
  420             if DualUseConfig.isAthena:
 
  421                 if DualUseConfig.useComponentAccumulator:
 
  422                     self.
CA.addPublicTool(tool)
 
  431                 raise Exception (
'unknown public tool requested: ' + name)
 
  437         """add a private tool to the current algorithm""" 
  439             DualUseConfig.addPrivateTool (self.
_currentAlg, propertyName, toolType)
 
  443                        *, originalName = None, isMet = False) :
 
  444         """set the (default) name of the source/original container 
  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 
  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 
  458             self.
_containerConfig[containerName] = ContainerConfig (containerName, sourceName, noSysSuffix = self.
_noSysSuffix, originalName = originalName, isMet = isMet)
 
  462         """register that the given container will be made and return 
  467             raise Exception (
"trying to write container configured for input: " + containerName)
 
  469             raise Exception (
"trying to write container twice: " + containerName)
 
  471         if isMet 
is not None :
 
  477         """get the name of the "current copy" of the given container 
  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. 
  484             raise Exception (
"no source container for: " + containerName)
 
  489         """register that a copy of the container will be made and return 
  492             raise Exception (
"unknown container: " + containerName)
 
  498         """ask whether we want/need a copy of the container 
  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. 
  504             raise Exception (
"no source container for: " + containerName)
 
  509         """get the "original" name of the given container 
  511         This is mostly/exclusively used for jet containers, so that 
  512         subsequent configurations know which jet container they 
  516             raise Exception (
"container unknown: " + containerName)
 
  519             raise Exception (
"no original name for: " + containerName)
 
  522     def getContainerMeta (self, containerName, metaField, defaultValue=None, *, failOnMiss=False) :
 
  523         """get the meta information for the given container 
  525         This is used to pass down meta-information from the 
  526         configuration to the algorithms. 
  529             raise Exception (
"container unknown: " + containerName)
 
  533             raise Exception (
'unknown meta-field' + metaField + 
' on container ' + containerName)
 
  537         """set the meta information for the given container 
  539         This is used to pass down meta-information from the 
  540         configuration to the algorithms. 
  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)
 
  549         """whether the given container is registered as a MET container 
  551         This is mostly/exclusively used for determining whether to 
  552         write out the whole container or just a single MET term. 
  555             raise Exception (
"container unknown: " + containerName)
 
  560         """get the name of the "current copy" of the given container, and the 
  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". 
  567         split = containerName.split (
".")
 
  569             objectName = split[0]
 
  571         elif len(split) == 2 :
 
  572             objectName = split[0]
 
  573             selectionName = split[1]
 
  575             raise Exception (
'invalid object selection name: ' + containerName)
 
  580         """switch to the next configuration pass 
  582         Configuration happens in two steps, with all the blocks processed 
  583         twice.  This switches from the first to the second pass. 
  586             raise Exception (
"already performed final pass")
 
  596         """get the preselection string for the given selection on the given 
  600             raise ValueError (
'invalid selection name: ' + selectionName)
 
  605         for selection 
in config.selections :
 
  606             if (selection.name == 
'' or selection.name == selectionName) 
and \
 
  607                selection.preselection :
 
  608                 decorations += [selection.decoration]
 
  612             return '&&'.join (decorations)
 
  616                           *, skipBase = False, excludeFrom = None) :
 
  618         """get the selection string for the given selection on the given 
  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. 
  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 
  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.')
 
  640         if excludeFrom 
is None :
 
  642         elif not isinstance(excludeFrom, set) :
 
  643             raise ValueError (
'invalid excludeFrom argument (need set of strings): ' + 
str(excludeFrom))
 
  650         if selectionName != 
'' and \
 
  653             while selectionName != 
'' :
 
  656                     result += selectionName[0]
 
  657                     selectionName = selectionName[1:]
 
  659                     subname = match.group(0)
 
  660                     subresult = self.
getFullSelection (containerName, subname, skipBase = 
True, excludeFrom=excludeFrom)
 
  662                         result += 
'(' + subresult + 
')' 
  665                     selectionName = selectionName[len(subname):]
 
  666             subresult = self.
getFullSelection (containerName, 
'', excludeFrom=excludeFrom)
 
  668                 result = subresult + 
'&&(' + result + 
')' 
  669             return '(' + result + 
')' if result !=
'' else '' 
  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)
 
  686         """get the individual selections as a list for producing the cutflow for 
  687         the given selection on the given container 
  689         This can only handle individual selections, not selection 
  690         expressions (e.g. `loose||tight`). 
  701         if selectionName != 
'' and \
 
  703             raise ValueError (
'not allowed to do cutflow on selection expression: ' + selectionName)
 
  707         for selection 
in config.selections :
 
  708             if (selection.name == 
'' or selection.name == selectionName) :
 
  709                 decorations += [selection.decoration]
 
  715         """register a new event cutflow, adding it to the dictionary with key 'selection' 
  716         and value 'decorations', a list of decorated selections 
  720                 raise ValueError (
'the event cutflow dictionary already contains an entry ' + selection)
 
  727         """get the list of decorated selections for an event cutflow,  corresponding to 
  735         """add another selection decoration to the selection of the given 
  736         name for the given container""" 
  738             raise ValueError (
'invalid selection name: ' + selectionName)
 
  742         selection = SelectionConfig (selectionName, decoration, **kwargs)
 
  743         config.selections.append (selection)
 
  747         """register a copy of a container used in outputs""" 
  749             raise KeyError (
"container unknown: " + containerName)
 
  751             raise KeyError (
"duplicate output container name: " + outputContainerName)
 
  756         """check whether a given container has been registered in outputs""" 
  761         """Get the name of the actual container, for which an output is registered""" 
  768                 raise KeyError (
"output container unknown: " + outputContainerName)
 
  772                       *, noSys=False, enabled=True) :
 
  773         """add an output variable for the given container to the output 
  777             raise KeyError (
"container unknown: " + containerName)
 
  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
 
  786         """get the output variables for the given container""" 
  790             raise KeyError (
"unknown container for output: " + containerName)
 
  795         """Retrieve set of unique selections defined for a given container""" 
  798         if excludeFrom 
is None:
 
  800         elif not isinstance(excludeFrom, set) :
 
  801             raise ValueError (
'invalid excludeFrom argument (need set of strings): ' + 
str(excludeFrom))
 
  806         selectionNames = 
set()
 
  807         for selection 
in config.selections:
 
  808             if selection.comesFrom 
in excludeFrom:
 
  811             if selection.writeToOutput:
 
  812                 selectionNames.add(selection.name)
 
  813         return selectionNames