Newer
Older
# Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
# AnaAlgorithm import(s):
from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock

Baptiste Ravina
committed
from AthenaConfiguration.Enums import LHCPeriod
from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
class SystematicsCategories(Enum):
JETS = ['JET_']
ELECTRONS = ['EG_', 'EL_']
MUONS = ['MUON_']
PHOTONS = ['EG_', 'PH_']
TAUS = ['TAUS_']
MET = ['MET_']
EVENT = ['GEN_', 'PRW_']
FTAG = ['FT_']
class CommonServicesConfig (ConfigBlock) :
"""the ConfigBlock for common services
The idea here is that all algorithms need some common services, and I should
provide configuration blocks for those. For now there is just a single
block, but in the future I might break out e.g. the systematics service.
"""
def __init__ (self) :
super (CommonServicesConfig, self).__init__ ()
self.addOption ('runSystematics', None, type=bool,
info="whether to turn on the computation of systematic variations. "
"The default is to run them on MC.")
self.addOption ('filterSystematics', None, type=str,
info="a regexp string against which the systematics names will be "
"matched. Only positive matches are retained and used in the evaluation "
"of the various algorithms.")
self.addOption ('onlySystematicsCategories', None, type=list,
info="a list of strings defining categories of systematics to enable "
"(only recommended for studies / partial ntuple productions). Choose amongst: "
"jets, electrons, muons, photons, taus, met, ftag, event. This option is overridden "
"by 'filterSystematics'.")
self.addOption ('systematicsHistogram', None , type=str,
info="the name (string) of the histogram to which a list of executed "
"systematics will be printed. The default is None (don't write out "
"the histogram).")
def makeAlgs (self, config) :
sysService = config.createService( 'CP::SystematicsSvc', 'SystematicsSvc' )
if config.dataType() is not DataType.Data:
if self.runSystematics is not None :
runSystematics = self.runSystematics

Joseph Earl Lambert
committed
elif config.noSystematics() is not None :
# if option not set:
# check to see if set in config accumulator
self.runSystematics = not config.noSystematics()
runSystematics = self.runSystematics
else :
runSystematics = True
else:
runSystematics = False
if runSystematics :
sysService.sigmaRecommended = 1
if self.onlySystematicsCategories is not None:
# Convert strings to enums and validate
requested_categories = []
for category_str in self.onlySystematicsCategories:
try:
category_enum = SystematicsCategories[category_str.upper()]
requested_categories += category_enum.value
except KeyError:
raise ValueError(f"Invalid systematics category passed to option 'onlySystematicsCategories': {category_str}. Must be one of {', '.join(category.name for category in SystematicsCategories)}")
# Construct regex pattern as logical-OR of category names
if len(requested_categories):
sysService.systematicsRegex = "^(?=.*(" + "|".join(requested_categories) + ")|$).*"
if self.filterSystematics is not None:
sysService.systematicsRegex = self.filterSystematics
config.createService( 'CP::SelectionNameSvc', 'SelectionNameSvc')
if self.systematicsHistogram is not None:
sysDumper = config.createAlgorithm( 'CP::SysListDumperAlg', 'SystematicsPrinter' )
sysDumper.histogramName = self.systematicsHistogram
class PileupReweightingBlock (ConfigBlock):
"""the ConfigBlock for pileup reweighting"""
def __init__ (self) :
super (PileupReweightingBlock, self).__init__ ()
self.addOption ('campaign', None, type=None,
info="the MC campaign for the PRW auto-configuration.")
self.addOption ('files', None, type=None,
info="the input files being processed (list of strings). "
"Alternative to auto-configuration.")
self.addOption ('useDefaultConfig', True, type=bool,
info="whether to use the central PRW files. The default is True.")
self.addOption ('userLumicalcFiles', None, type=None,
info="user-provided lumicalc files (list of strings). Alternative "
"to auto-configuration.")
self.addOption ('userLumicalcFilesPerCampaign', None, type=None,
info="user-provided lumicalc files (dictionary of list of strings, "
"with MC campaigns as the keys). Alternative to auto-configuration.")
self.addOption ('userPileupConfigs', None, type=None,
info="user-provided PRW files (list of strings). Alternative to "
"auto-configuration. Alternative to auto-configuration.")
self.addOption ('userPileupConfigsPerCampaign', None, type=None,
info="user-provided PRW files (dictionary of list of strings, with "
"MC campaigns as the keys)")
def makeAlgs (self, config) :
from Campaigns.Utils import Campaign
try:
from AthenaCommon.Logging import logging
except ImportError:
import logging
log = logging.getLogger('makePileupAnalysisSequence')

Joseph Earl Lambert
committed
if config.isPhyslite():
# PHYSLITE already has these variables defined, just need to copy them to the output
log.info(f'Physlite does not need pileup reweighting. Variables will be copied from input instead. {config.isPhyslite}')
config.addOutputVar ('EventInfo', 'runNumber', 'runNumber', noSys=True)
config.addOutputVar ('EventInfo', 'eventNumber', 'eventNumber', noSys=True)
if config.dataType() is not DataType.Data:
config.addOutputVar ('EventInfo', 'mcChannelNumber', 'mcChannelNumber', noSys=True)
config.addOutputVar ('EventInfo', 'PileupWeight_%SYS%', 'weight_pileup')
if config.geometry() is LHCPeriod.Run2:
config.addOutputVar ('EventInfo', 'beamSpotWeight', 'weight_beamspot', noSys=True)

Joseph Earl Lambert
committed
return
# check files from autoconfig flags
if self.files is None and config.autoconfigFlags() is not None:
self.files = config.autoconfigFlags().Input.Files
campaign = self.campaign
# if user didn't explicitly configure campaign, let's try setting it from metadata
# only needed on MC
if config.dataType() is not DataType.Data and self.campaign is None:
# if we used autoconfigflags, campaign is auto-determined
if config.campaign() is not None and config.campaign() is not Campaign.Unknown:
campaign = config.campaign()
log.info(f'Auto-configuring campaign for PRW from flags: {campaign.value}')
else:
# we try to determine campaign from files if above failed
if self.files is not None:
from Campaigns.Utils import getMCCampaign
campaign = getMCCampaign(self.files)
if campaign and campaign is not Campaign.Unknown:
log.info(f'Auto-configuring campaign for PRW from files: {campaign.value}')
else:
log.info('Campaign could not be determined.')

Joseph Earl Lambert
committed
toolConfigFiles = []
toolLumicalcFiles = []
# PRW config files should only be configured if we run on MC
if config.dataType() is not DataType.Data:
# check if user provides per-campaign pileup config list
if self.userPileupConfigs is not None and self.userPileupConfigsPerCampaign is not None:
raise ValueError('Both userPileupConfigs and userPileupConfigsPerCampaign specified, '
'use only one of the options!')
if self.userPileupConfigsPerCampaign is not None:
if not campaign:
raise Exception('userPileupConfigsPerCampaign requires campaign to be configured!')
if campaign is Campaign.Unknown:
raise Exception('userPileupConfigsPerCampaign used, but campaign = Unknown!')
try:
toolConfigFiles = self.userPileupConfigsPerCampaign[campaign.value][:]
log.info('Using user provided per-campaign PRW configuration')
except KeyError as e:
raise KeyError(f'Unconfigured campaign {e} for userPileupConfigsPerCampaign!')
elif self.userPileupConfigs is not None:
toolConfigFiles = self.userPileupConfigs[:]
log.info('Using user provided PRW configuration')
from PileupReweighting.AutoconfigurePRW import getConfigurationFiles
if campaign and campaign is not Campaign.Unknown:
toolConfigFiles = getConfigurationFiles(campaign=campaign,
files=self.files,
useDefaultConfig=self.useDefaultConfig,

Baptiste Ravina
committed
data_type=config.dataType())
if self.useDefaultConfig:
log.info('Auto-configuring universal/default PRW config')
else:
log.info('Auto-configuring per-sample PRW config files based on input files')
else:
log.info('No campaign specified, no PRW config files configured')
# check if user provides per-campaign lumical config list
if self.userLumicalcFilesPerCampaign is not None and self.userLumicalcFiles is not None:
raise ValueError('Both userLumicalcFiles and userLumicalcFilesYear specified, '
'use only one of the options!')
if self.userLumicalcFilesPerCampaign is not None:
try:
toolLumicalcFiles = self.userLumicalcFilesPerCampaign[campaign.value][:]
log.info('Using user-provided per-campaign lumicalc files')
except KeyError as e:
raise KeyError(f'Unconfigured campaign {e} for userLumicalcFilesPerCampaign!')
elif self.userLumicalcFiles is not None:
toolLumicalcFiles = self.userLumicalcFiles[:]
log.info('Using user-provided lumicalc files')
if campaign and campaign is not Campaign.Unknown:
from PileupReweighting.AutoconfigurePRW import getLumicalcFiles
toolLumicalcFiles = getLumicalcFiles(campaign)
log.info('Using auto-configured lumicalc files')
else:
log.info('No campaign specified, no lumicalc files configured for PRW')
log.info('Data needs no lumicalc and PRW configuration files')
# Set up the only algorithm of the sequence:
alg = config.createAlgorithm( 'CP::PileupReweightingAlg', 'PileupReweightingAlg' )
config.addPrivateTool( 'pileupReweightingTool', 'CP::PileupReweightingTool' )
alg.pileupReweightingTool.ConfigFiles = toolConfigFiles
if not toolConfigFiles and config.dataType() is not DataType.Data:
log.info("No PRW config files provided. Disabling reweighting")
# Setting the weight decoration to the empty string disables the reweighting
alg.pileupWeightDecoration = ""
alg.pileupReweightingTool.LumiCalcFiles = toolLumicalcFiles
config.addOutputVar ('EventInfo', 'runNumber', 'runNumber', noSys=True)
config.addOutputVar ('EventInfo', 'eventNumber', 'eventNumber', noSys=True)
if config.dataType() is not DataType.Data:

Baptiste Ravina
committed
config.addOutputVar ('EventInfo', 'mcChannelNumber', 'mcChannelNumber', noSys=True)
if toolConfigFiles:
config.addOutputVar ('EventInfo', 'PileupWeight_%SYS%', 'weight_pileup')
if config.geometry() is LHCPeriod.Run2:

Baptiste Ravina
committed
config.addOutputVar ('EventInfo', 'beamSpotWeight', 'weight_beamspot', noSys=True)
class GeneratorAnalysisBlock (ConfigBlock):
"""the ConfigBlock for generator algorithms"""
def __init__ (self) :
super (GeneratorAnalysisBlock, self).__init__ ()
self.addOption ('saveCutBookkeepers', True, type=bool,
info="whether to save the cut bookkeepers information into the "
"output file. The default is True.")
self.addOption ('runNumber', None, type=int,
info="the MC runNumber (int). The default is None (autoconfigure "
"from metadata).")
self.addOption ('cutBookkeepersSystematics', None, type=bool,
info="whether to also save the cut bookkeepers systematics. The "
"default is None (follows the global systematics flag). Set to "
"False or True to override.")
def makeAlgs (self, config) :
if config.dataType() is DataType.Data:

Baptiste Ravina
committed
# there are no generator weights in data!
return
if self.runNumber is None:
self.runNumber = config.runNumber()
if self.saveCutBookkeepers and not self.runNumber:

Baptiste Ravina
committed
raise ValueError ("invalid run number: " + str(self.runNumber))
# Set up the CutBookkeepers algorithm:
if self.saveCutBookkeepers:
alg = config.createAlgorithm('CP::AsgCutBookkeeperAlg', 'CutBookkeeperAlg')
alg.runNumber = self.runNumber
if self.cutBookkeepersSystematics:
alg.enableSystematics = self.cutBookkeepersSystematics
else:
alg.enableSystematics = not config.noSystematics()
config.addPrivateTool( 'truthWeightTool', 'PMGTools::PMGTruthWeightTool' )
# Set up the weights algorithm:
alg = config.createAlgorithm( 'CP::PMGTruthWeightAlg', 'PMGTruthWeightAlg' )
config.addPrivateTool( 'truthWeightTool', 'PMGTools::PMGTruthWeightTool' )
alg.decoration = 'generatorWeight_%SYS%'

Baptiste Ravina
committed
config.addOutputVar ('EventInfo', 'generatorWeight_%SYS%', 'weight_mc')
class PtEtaSelectionBlock (ConfigBlock):
"""the ConfigBlock for a pt-eta selection"""
def __init__ (self, containerName='', selectionName='') :
super (PtEtaSelectionBlock, self).__init__ ()
self.addOption ('containerName', containerName, type=str,
noneAction='error',
info="the name of the input container.")
self.addOption ('selectionName', selectionName, type=str,
noneAction='error',
info="the name of the selection to append this to. The default is "
"'' (empty string), meaning that the cuts are applied to every "
"object within the container. Specifying a name (e.g. loose) "
"applies the cut only to those object who also pass that selection.")
self.addOption ('postfix', '', type=str,
info="a postfix to apply to decorations and algorithm names. "
"Typically not needed here since we tend apply a single set of "
"pT and eta cuts to a given type of object.")
self.addOption ('minPt', None, type=float,
info="minimum pT value to cut on, in MeV. No default value.")
self.addOption ('maxEta', None, type=float,
info="maximum |eta| value to cut on. No default value.")
self.addOption ('selectionDecoration', 'selectPtEta', type=str,
info="the name of the decoration to set.")
self.addOption ('useClusterEta', False, type=bool,
info="whether to use the cluster eta (etaBE(2)) instead of the object "
"eta (for electrons and photons). The default is False.")
def makeAlgs (self, config) :
postfix = self.postfix
if postfix != '' and postfix[0] != '_' :
postfix = '_' + postfix
alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PtEtaSelectionAlg' + self.containerName + postfix )
config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
if self.minPt is not None :
alg.selectionTool.minPt = self.minPt
if self.maxEta is not None :
alg.selectionTool.maxEta = self.maxEta

Baptiste Ravina
committed
alg.selectionTool.useClusterEta = self.useClusterEta
alg.selectionDecoration = self.selectionDecoration
alg.particles = config.readName (self.containerName)
alg.preselection = config.getPreselection (self.containerName, '')
config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration)
class ObjectCutFlowBlock (ConfigBlock):
"""the ConfigBlock for an object cutflow"""
def __init__ (self, containerName='', selectionName='') :
super (ObjectCutFlowBlock, self).__init__ ()
self.addOption ('containerName', containerName, type=str,
noneAction='error',
info="the name of the input container.")
self.addOption ('selectionName', selectionName, type=str,
noneAction='error',
info="the name of the selection to perform the cutflow for. The "
"default is '' (empty string), meaning that the cutflow is "
"performed for every object within the container. Specifying a "
"name (e.g. loose) generates the cutflow only for those object "
"that also pass that selection.")
self.addOption ('postfix', '', type=str,
info="a postfix to apply to decorations and algorithm names. "
"Typically not needed here.")
def makeAlgs (self, config) :
postfix = self.postfix
if postfix != '' and postfix[0] != '_' :
postfix = '_' + postfix
alg = config.createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'CutFlowDumperAlg_' + self.containerName + '_' + self.selectionName + postfix )
alg.histPattern = 'cflow_' + self.containerName + "_" + self.selectionName + postfix + '_%SYS%'
alg.selections = config.getSelectionCutFlow (self.containerName, self.selectionName)
alg.input = config.readName (self.containerName)
alg.histTitle = "Object Cutflow: " + self.containerName + "." + self.selectionName
class EventCutFlowBlock (ConfigBlock):
"""the ConfigBlock for an event-level cutflow"""
def __init__ (self, containerName='', selectionName='') :
super (EventCutFlowBlock, self).__init__ ()
self.addOption ('containerName', containerName, type=str,
noneAction='error',
info="the name of the input container, typically EventInfo.")
self.addOption ('selectionName', selectionName, type=str,
noneAction='error',
info="the name of an optional selection decoration to use.")
self.addOption ('customSelections', [], type=None,
info="the selections for which to generate cutflow histograms. If "
"a single string, corresponding to a particular event selection, "
"the event cutflow for that selection will be looked up. If a list "
"of strings, will use explicitly those selections. If left blank, "
"all selections attached to the container will be looked up.")
self.addOption ('postfix', '', type=str,
info="a postfix to apply in the naming of cutflow histograms. Set "
"it when defining multiple cutflows.")
def makeAlgs (self, config) :
postfix = self.postfix
if postfix != '' and postfix[0] != '_' :
postfix = '_' + postfix
alg = config.createAlgorithm( 'CP::EventCutFlowHistAlg', 'CutFlowDumperAlg_' + self.containerName + '_' + self.selectionName + postfix )
alg.histPattern = 'cflow_' + self.containerName + "_" + self.selectionName + postfix + '_%SYS%'
# find out which selection decorations to use
if isinstance(self.customSelections, str):
# user provides a dynamic reference to selections, corresponding to an EventSelection alg
alg.selections = config.getEventCutFlow(self.customSelections)
elif len(self.customSelections) > 0:
# user provides a list of hardcoded selections
alg.selections = self.customSelections
else:
# user provides nothing: get all available selections from EventInfo directly
alg.selections = config.getSelectionCutFlow (self.containerName, self.selectionName)
if self.selectionName:
alg.preselection = self.selectionName + '_%SYS%'
alg.eventInfo = config.readName (self.containerName)
alg.histTitle = "Event Cutflow: " + self.containerName + "." + self.selectionName
class OutputThinningBlock (ConfigBlock):
"""the ConfigBlock for output thinning"""
def __init__ (self, containerName='', configName='') :
# configName is not used. To be removed.
super (OutputThinningBlock, self).__init__ ()
self.addOption ('containerName', containerName, type=str,
noneAction='error',
info="the name of the input container.")
self.addOption ('postfix', '', type=str,
info="a postfix to apply to decorations and algorithm names. "
"Typically not needed here.")
self.addOption ('selection', '', type=str,
info="the name of an optional selection decoration to use.")
self.addOption ('selectionName', '', type=str,
info="the name of the selection to append this to. The default is "
"'' (empty string), meaning that the cuts are applied to every "
"object within the container. Specifying a name (e.g. loose) "
"applies the cut only to those object who also pass that selection.")
self.addOption ('outputName', None, type=str,
info="an optional name for the output container.")
# TODO: add info string
self.addOption ('deepCopy', False, type=bool,
info="")
self.addOption ('sortPt', False, type=bool,
info="whether to sort objects in pt")
# TODO: add info string
self.addOption ('noUniformSelection', False, type=bool,
info="")
def makeAlgs (self, config) :
postfix = self.postfix
if postfix != '' and postfix[0] != '_' :
postfix = '_' + postfix
selection = config.getFullSelection (self.containerName, self.selectionName)
if selection == '' :
selection = self.selection
elif self.selection != '' :
selection = selection + '&&' + self.selection
if selection != '' and not self.noUniformSelection :
alg = config.createAlgorithm( 'CP::AsgUnionSelectionAlg', 'UnionSelectionAlg' + self.containerName + postfix)
alg.preselection = selection
alg.particles = config.readName (self.containerName)
alg.selectionDecoration = 'outputSelect' + postfix
selection = 'outputSelect' + postfix
alg = config.createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'DeepCopyAlg' + self.containerName + postfix )
alg.input = config.readName (self.containerName)
if self.outputName is not None :
alg.output = self.outputName + '_%SYS%'
config.addOutputContainer (self.containerName, self.outputName)
else :
alg.output = config.copyName (self.containerName)
if selection != '' :
alg.selection = [selection]
else :
alg.selection = []
alg.deepCopy = self.deepCopy
if self.sortPt and not config.noSystematics() :
raise ValueError ("Sorting by pt is not supported with systematics")
alg.sortPt = self.sortPt
class IFFLeptonDecorationBlock (ConfigBlock):
"""the ConfigBlock for the IFF classification of leptons"""
def __init__ (self, containerName='') :
super (IFFLeptonDecorationBlock, self).__init__()
self.addOption ('containerName', containerName, type=str,
noneAction='error',
info="the name of the input electron or muon container.")
self.addOption ('separateChargeFlipElectrons', True, type=bool,
info="whether to consider charged-flip electrons as a separate class. "
"The default is True (recommended).")
self.addOption ('decoration', 'IFFClass_%SYS%', type=str,
info="the name (str) of the decoration set by the IFF "
"TruthClassificationTool. The default is 'IFFClass_%SYS%'.")
def makeAlgs (self, config) :
# the classification is only for MC
if config.dataType() is DataType.Data: return
particles = config.readName(self.containerName)
alg = config.createAlgorithm( 'CP::AsgClassificationDecorationAlg', 'IFFClassifierAlg' + self.containerName )
# the IFF classification tool
config.addPrivateTool( 'tool', 'TruthClassificationTool')
# label charge-flipped electrons as such
alg.tool.separateChargeFlipElectrons = self.separateChargeFlipElectrons
alg.decoration = self.decoration
alg.particles = particles
# write the decoration only once to the output
config.addOutputVar(self.containerName, alg.decoration, alg.decoration.split("_%SYS%")[0], noSys=True)
class PerEventSFBlock (ConfigBlock):
"""the ConfigBlock for the AsgEventScaleFactorAlg"""
def __init__ (self, algoName=''):
super(PerEventSFBlock, self).__init__()
self.addOption('algoName', algoName, type=str,
noneAction='error',
info="unique name given to the underlying algorithm computing the "
"per-event scale factors")
self.addOption('particles', '', type=str,
info="the input object container, with a possible selection, in the "
"format container or container.selection.")
self.addOption('objectSF', '', type=str,
info="the name of the per-object SF decoration to be used.")
self.addOption('eventSF', '', type=str,
info="the name of the per-event SF decoration.")
def makeAlgs(self, config):
if config.dataType() is DataType.Data:
return
particles, selection = config.readNameAndSelection(self.particles)
alg = config.createAlgorithm('CP::AsgEventScaleFactorAlg', self.algoName)
alg.particles = particles
alg.preselection = selection
alg.scaleFactorInputDecoration = self.objectSF
alg.scaleFactorOutputDecoration = self.eventSF
config.addOutputVar('EventInfo', alg.scaleFactorOutputDecoration,
alg.scaleFactorOutputDecoration.split("_%SYS%")[0])
class SelectionDecorationBlock (ConfigBlock):
"""the ConfigBlock to add selection decoration to a container"""
def __init__ (self, containers='') :
super (SelectionDecorationBlock, self).__init__ ()
# TODO: add info string
self.addOption('containers', containers, type=str,
noneAction='error',
info="")
def makeAlgs(self, config):
for container in self.containers:
originContainerName = config.getOutputContainerOrigin(container)
selectionNames = config.getSelectionNames(originContainerName)
for selectionName in selectionNames:
# skip default selection
if selectionName == '':
continue
alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
f'SelectionDecoration_{originContainerName}_{selectionName}')
selectionDecoration = f'baselineSelection_{selectionName}_%SYS%'
alg.selectionDecoration = f'{selectionDecoration},as_char'
alg.particles = config.readName (originContainerName)
alg.preselection = config.getFullSelection (originContainerName,
selectionName)
config.addOutputVar(
originContainerName, selectionDecoration, selectionName)
def makeCommonServicesConfig( seq ):
"""Create the common services config"""
seq.append (CommonServicesConfig ())
def makePileupReweightingConfig( seq, campaign=None, files=None, useDefaultConfig=None, userLumicalcFiles=None, userPileupConfigs=None ):
"""Create a PRW analysis config
Keyword arguments:
"""
# TO DO: add explanation of the keyword arguments, left to experts
config = PileupReweightingBlock ()
config.setOptionValue ('campaign', campaign)
config.setOptionValue ('files', files)
config.setOptionValue ('useDefaultConfig', useDefaultConfig)
config.setOptionValue ('userLumicalcFiles', userLumicalcFiles)
config.setOptionValue ('userPileupConfigs', userPileupConfigs)
def makeGeneratorAnalysisConfig( seq,
saveCutBookkeepers=None,
runNumber=None,
cutBookkeepersSystematics=None ):
"""Create a generator analysis algorithm sequence
Keyword arguments:
saveCutBookkeepers -- save cut bokkeepers information into output file
runNumber -- MC run number
cutBookkeepersSystematics -- store CutBookkeepers systematics
"""
config = GeneratorAnalysisBlock ()
config.setOptionValue ('saveCutBookkeepers', saveCutBookkeepers)
config.setOptionValue ('runNumber', runNumber)
config.setOptionValue ('cutBookkeepersSystematics', cutBookkeepersSystematics)
seq.append (config)
def makePtEtaSelectionConfig( seq, containerName,
*, postfix = None, minPt = None, maxEta = None,

Baptiste Ravina
committed
useClusterEta = None,
selectionDecoration = None, selectionName = ''):
"""Create a pt-eta kinematic selection config
Keyword arguments:
containerName -- name of the container
postfix -- a postfix to apply to decorations and algorithm
names. this is mostly used/needed when using this
sequence with multiple working points to ensure all
names are unique.
minPt -- minimum pt value
maxEta -- maximum eta value

Baptiste Ravina
committed
useClusterEta -- use cluster eta (for electrons/photons) instead of track eta
selectionDecoration -- the name of the decoration to set
selectionName -- the name of the selection to append this to
"""
config = PtEtaSelectionBlock (containerName, selectionName)
config.setOptionValue ('postfix',postfix)
config.setOptionValue ('minPt',minPt)
config.setOptionValue ('maxEta',maxEta)
config.setOptionValue ('selectionDecoration',selectionDecoration)
config.setOptionValue ('useClusterEta',useClusterEta)
seq.append (config)
def makeObjectCutFlowConfig( seq, containerName,
*, postfix = None, selectionName):
"""Create a pt-eta kinematic selection config
Keyword arguments:
containerName -- name of the container
postfix -- a postfix to apply to decorations and algorithm
names. this is mostly used/needed when using this
sequence with multiple working points to ensure all
names are unique.
selectionName -- the name of the selection to do the cutflow for
"""
config = ObjectCutFlowBlock (containerName, selectionName)
config.setOptionValue ('postfix',postfix)
def makeEventCutFlowConfig( seq, containerName,
*, postfix = None, selectionName, customSelections = None):
"""Create an event-level cutflow config
Keyword arguments:
containerName -- name of the container
postfix -- a postfix to apply to decorations and algorithm names.
selectionName -- the name of the selection to do the cutflow for
customSelections -- a list of decorations to use in the cutflow, to override the retrieval of all decorations
"""
config = EventCutFlowBlock (containerName, selectionName)
config.setOptionValue ('postfix', postfix)
config.setOptionValue ('customSelections', customSelections)
def makeOutputThinningConfig( seq, containerName,
*, postfix = None, selection = None, selectionName = None, outputName = None, configName='Thinning'):
"""Create an output thinning config
This will do a consistent selection of output containers (if there
is a preselection or a selection specified) and then creates a set
of view containers (or deep copies) based on that selection.
Keyword arguments:
containerName -- name of the container
postfix -- a postfix to apply to decorations and algorithm
names. this is mostly used/needed when using this
sequence with multiple working points to ensure all
names are unique.
selection -- the name of an optional selection decoration to use
outputName -- an optional name for the output container
"""
config = OutputThinningBlock (containerName, configName)
config.setOptionValue ('postfix', postfix)
config.setOptionValue ('selection', selection)
config.setOptionValue ('selectionName', selectionName)
config.setOptionValue ('outputName', outputName)
seq.append (config)