def makeTriggerAnalysisSequence(dataType, triggerChains=[], prescaleLumiCalcFiles=[]): """Create a basic trigger analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") triggerChains -- a list of trigger chains prescaleLumiCalcFiles -- a list of lumicalc files to calculate trigger prescales """ if dataType not in ["data", "mc", "afii"]: raise ValueError("invalid data type: " + dataType) # Create the analysis algorithm sequence object: seq = AnaAlgSequence("TriggerAnalysisSequence") # Create public trigger tools xAODConfTool = createPublicTool('TrigConf::xAODConfigTool', 'xAODConfigTool') decisionTool = createPublicTool('Trig::TrigDecisionTool', 'TrigDecisionTool') decisionTool.ConfigTool = '%s/%s' % \ ( xAODConfTool.getType(), xAODConfTool.getName() ) seq.addPublicTool(xAODConfTool) seq.addPublicTool(decisionTool) if triggerChains: # Set up the trigger selection: alg = createAlgorithm('CP::TrigEventSelectionAlg', 'TrigEventSelectorAlg') alg.tool = '%s/%s' % \ ( decisionTool.getType(), decisionTool.getName() ) alg.triggers = list(triggerChains) alg.selectionDecoration = 'trigPassed' seq.append(alg, inputPropName=None) # Calculate trigger prescales if dataType == 'data' and prescaleLumiCalcFiles: alg = createAlgorithm('CP::TrigPrescalesAlg', 'TrigPrescalesAlg') addPrivateTool(alg, 'pileupReweightingTool', 'CP::PileupReweightingTool') alg.pileupReweightingTool.LumiCalcFiles = prescaleLumiCalcFiles alg.pileupReweightingTool.TrigDecisionTool = '%s/%s' % \ ( decisionTool.getType(), decisionTool.getName() ) alg.triggers = [ lumicalc.split(':')[-1] for lumicalc in prescaleLumiCalcFiles if ':' in lumicalc ] alg.triggersAll = list(triggerChains) alg.prescaleDecoration = 'prescale' seq.append(alg, inputPropName=None) # Return the sequence: return seq
def makePileupAnalysisSequence(dataType, userPileupConfigs=[], userLumicalcFiles=[], autoConfig=False): """Create a PRW analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") """ if dataType not in ["data", "mc", "afii"]: raise ValueError("invalid data type: " + dataType) # Create the analysis algorithm sequence object: seq = AnaAlgSequence("PileupAnalysisSequence") muMcFiles = userPileupConfigs[:] if autoConfig: from PileupReweighting.AutoconfigurePRW import getLumiCalcFiles, getMCMuFiles userLumicalcFiles = getLumiCalcFiles() if len(muMcFiles) == 0: muMcFiles = getMCMuFiles() else: from AthenaCommon import Logging prwlog = Logging.logging.getLogger('makePileupAnalysisSequence') prwlog.warning('Sent autoconfig and userPileupConfigs=' + str(userPileupConfigs)) prwlog.warning( 'Ignoring autoconfig and keeping user-specified files') if userLumicalcFiles == []: muDataFiles = [ "GoodRunsLists/data15_13TeV/20170619/PHYS_StandardGRL_All_Good_25ns_276262-284484_OflLumi-13TeV-008.root", "GoodRunsLists/data16_13TeV/20180129/PHYS_StandardGRL_All_Good_25ns_297730-311481_OflLumi-13TeV-009.root", "GoodRunsLists/data17_13TeV/20180619/physics_25ns_Triggerno17e33prim.lumicalc.OflLumi-13TeV-010.root", "GoodRunsLists/data18_13TeV/20190708/ilumicalc_histograms_None_348885-364292_OflLumi-13TeV-010.root" ] else: muDataFiles = userLumicalcFiles[:] # Set up the only algorithm of the sequence: alg = createAlgorithm('CP::PileupReweightingAlg', 'PileupReweightingAlg') addPrivateTool(alg, 'pileupReweightingTool', 'CP::PileupReweightingTool') alg.pileupReweightingTool.ConfigFiles = muMcFiles alg.pileupReweightingTool.LumiCalcFiles = muDataFiles seq.append(alg, inputPropName='eventInfo', outputPropName='eventInfoOut', affectingSystematics='(^PRW_.*)') # Return the sequence: return seq
def makeEventSelectionAnalysisSequence(dataType, runPrimaryVertexSelection=True, runEventCleaning=False, userGRLFiles=[]): """Create a basic event selection analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") runPrimaryVertexSelection -- whether to run primary vertex selection runEventCleaning -- wether to run event cleaning userGRLFiles -- a list of GRL files to select data from """ if dataType not in ["data", "mc", "afii"]: raise ValueError("invalid data type: " + dataType) # Create the analysis algorithm sequence object: seq = AnaAlgSequence("EventSelectionAnalysisSequence") if dataType == 'data': grlFiles = userGRLFiles[:] # Set up the GRL selection: alg = createAlgorithm('GRLSelectorAlg', 'GRLSelectorAlg') addPrivateTool(alg, 'Tool', 'GoodRunsListSelectionTool') alg.Tool.GoodRunsListVec = grlFiles seq.append(alg, inputPropName=None) # Skip events with no primary vertex: if runPrimaryVertexSelection: alg = createAlgorithm('CP::VertexSelectionAlg', 'PrimaryVertexSelectorAlg') alg.VertexContainer = 'PrimaryVertices' alg.MinVertices = 1 seq.append(alg, inputPropName=None) # Set up the event cleaning selection: if runEventCleaning: alg = createAlgorithm('CP::EventFlagSelectionAlg', 'EventFlagSelectorAlg') alg.selectionFlags = ['DFCommonJets_eventClean_LooseBad,as_char'] seq.append(alg, inputPropName=None) # Return the sequence: return seq
def makeGeneratorAnalysisSequence(dataType, saveCutBookkeepers=False, runNumber=0, cutBookkeepersSystematics=False): """Create a generator analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("mc" or "afii") saveCutBookkeepers -- save cut bokkeepers information into output file runNumber -- MC run number cutBookkeepersSystematics -- store CutBookkeepers systematics """ if dataType not in ["mc", "afii"]: raise ValueError("invalid data type: " + dataType) if saveCutBookkeepers and not runNumber: raise ValueError("invalid run number: " + 0) # Create the analysis algorithm sequence object: seq = AnaAlgSequence("GeneratorAnalysisSequence") # Set up the CutBookkeepers algorithm: if saveCutBookkeepers: alg = createAlgorithm('CP::AsgCutBookkeeperAlg', 'CutBookkeeperAlg') alg.runNumber = runNumber alg.enableSystematics = cutBookkeepersSystematics addPrivateTool(alg, 'truthWeightTool', 'PMGTools::PMGTruthWeightTool') seq.append(alg, inputPropName=None) # Set up the weights algorithm: alg = createAlgorithm('CP::PMGTruthWeightAlg', 'PMGTruthWeightAlg') addPrivateTool(alg, 'truthWeightTool', 'PMGTools::PMGTruthWeightTool') alg.decoration = 'generatorWeight_%SYS%' alg.decorationRegex = '(^GEN_.*)' seq.append(alg, inputPropName='eventInfo', affectingSystematics='(^GEN_.*)') # Return the sequence: return seq
def makeRScanJetAnalysisSequence(seq, cutlist, cutlength, dataType, jetCollection, jetInput, radius, postfix=''): """Add algorithms for the R-scan jets. Keyword arguments seq -- The sequence to add the algorithms to cutlist -- Insert any cuts into this cutlength -- Insert the lengths of any cuts into this dataType -- The data type to run on ("data", "mc" or "afii") jetCollection -- The jet container to run on. jetInput -- The type of input used, read from the collection name. radius -- The radius of the r-scan jets. postfix -- String to be added to the end of all public names. """ if jetInput != "LCTopo": raise ValueError( "Unsupported input type '{0}' for R-scan jets!".format(jetInput)) # Prepare the jet calibration algorithm alg = createAlgorithm('CP::JetCalibrationAlg', 'JetCalibrationAlg' + postfix) addPrivateTool(alg, 'calibrationTool', 'JetCalibrationTool') alg.calibrationTool.JetCollection = jetCollection[:-4] alg.calibrationTool.ConfigFile = \ "JES_MC16Recommendation_Rscan{0}LC_18Dec2018_R21.config".format(radius) if dataType == 'data': alg.calibrationTool.CalibSequence = "JetArea_Residual_EtaJES_GSC_Insitu" else: alg.calibrationTool.CalibSequence = "JetArea_Residual_EtaJES_GSC" alg.calibrationTool.IsData = (dataType == 'data') seq.append(alg, inputPropName='jets', outputPropName='jetsOut', stageName='calibration') # Logging would be good print("WARNING: uncertainties for R-Scan jets are not yet released!")
def makeElectronAnalysisSequence(dataType, workingPoint, deepCopyOutput=False, shallowViewOutput=True, postfix='', recomputeLikelihood=False, chargeIDSelection=False, isolationCorrection=False, crackVeto=False, ptSelectionOutput=False, enableCutflow=False, enableKinematicHistograms=False): """Create an electron analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") workingPoint -- The working point to use deepCopyOutput -- If set to 'True', the output containers will be standalone, deep copies (slower, but needed for xAOD output writing) shallowViewOutput -- Create a view container if required 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. recomputeLikelihood -- Whether to rerun the LH. If not, use derivation flags chargeIDSelection -- Whether or not to perform charge ID/flip selection isolationCorrection -- Whether or not to perform isolation correction crackVeto -- Whether or not to perform eta crack veto ptSelectionOutput -- Whether or not to apply pt selection when creating output containers. enableCutflow -- Whether or not to dump the cutflow enableKinematicHistograms -- Whether or not to dump the kinematic histograms """ # Make sure we received a valid data type. if dataType not in ['data', 'mc', 'afii']: raise ValueError('Invalid data type: %' % dataType) if postfix != '': postfix = '_' + postfix pass # Make sure selection options make sense if deepCopyOutput and shallowViewOutput: raise ValueError( "deepCopyOutput and shallowViewOutput can't both be true!") splitWP = workingPoint.split('.') if len(splitWP) != 2: raise ValueError( 'working point should be of format "likelihood.isolation", not ' + workingPoint) likelihoodWP = splitWP[0] isolationWP = splitWP[1] # Create the analysis algorithm sequence object: seq = AnaAlgSequence("ElectronAnalysisSequence" + postfix) # Variables keeping track of the selections being applied. selectionDecorNames = [] selectionDecorCount = [] # Set up the eta-cut on all electrons prior to everything else alg = createAlgorithm('CP::AsgSelectionAlg', 'ElectronEtaCutAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = 'selectEta' + postfix + ',as_bits' addPrivateTool(alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool') alg.selectionTool.maxEta = 2.47 if crackVeto: alg.selectionTool.etaGapLow = 1.37 alg.selectionTool.etaGapHigh = 1.52 alg.selectionTool.useClusterEta = True seq.append(alg, inputPropName='particles', outputPropName='particlesOut', stageName='calibration') selectionDecorNames.append(alg.selectionDecoration) if crackVeto: selectionDecorCount.append(5) else: selectionDecorCount.append(4) # Set up the track selection algorithm: alg = createAlgorithm('CP::AsgLeptonTrackSelectionAlg', 'ElectronTrackSelectionAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits' alg.maxD0Significance = 5 alg.maxDeltaZ0SinTheta = 0.5 seq.append(alg, inputPropName='particles', stageName='selection') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(3) # Set up the likelihood ID selection algorithm # It is safe to do this before calibration, as the cluster E is used alg = createAlgorithm('CP::AsgSelectionAlg', 'ElectronLikelihoodAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = 'selectLikelihood' + postfix + ',as_bits' selectionDecorNames.append(alg.selectionDecoration) if recomputeLikelihood: # Rerun the likelihood ID addPrivateTool(alg, 'selectionTool', 'AsgElectronLikelihoodTool') alg.selectionTool.primaryVertexContainer = 'PrimaryVertices' alg.selectionTool.WorkingPoint = likelihoodWP selectionDecorCount.append(7) else: # Select from Derivation Framework flags addPrivateTool(alg, 'selectionTool', 'CP::AsgFlagSelectionTool') dfFlag = "DFCommonElectronsLH" + likelihoodWP.split('LH')[0] alg.selectionTool.selectionFlags = [dfFlag] selectionDecorCount.append(1) seq.append(alg, inputPropName='particles', stageName='selection') # Select electrons only with good object quality. alg = createAlgorithm('CP::AsgSelectionAlg', 'ElectronObjectQualityAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = 'goodOQ' + postfix + ',as_bits' addPrivateTool(alg, 'selectionTool', 'CP::EgammaIsGoodOQSelectionTool') alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSELECTRON seq.append(alg, inputPropName='particles', stageName='calibration') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(1) # Set up the calibration and smearing algorithm: alg = createAlgorithm('CP::EgammaCalibrationAndSmearingAlg', 'ElectronCalibrationAndSmearingAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) addPrivateTool(alg, 'calibrationAndSmearingTool', 'CP::EgammaCalibrationAndSmearingTool') alg.calibrationAndSmearingTool.ESModel = 'es2018_R21_v0' alg.calibrationAndSmearingTool.decorrelationModel = '1NP_v1' if dataType == 'afii': alg.calibrationAndSmearingTool.useAFII = 1 else: alg.calibrationAndSmearingTool.useAFII = 0 pass seq.append(alg, inputPropName='egammas', outputPropName='egammasOut', affectingSystematics='(^EG_RESOLUTION_.*)|(^EG_SCALE_.*)', stageName='calibration') # Set up the the pt selection ptSelectionDecoration = 'selectPt' + postfix + ',as_bits' alg = createAlgorithm('CP::AsgSelectionAlg', 'ElectronPtCutAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = ptSelectionDecoration addPrivateTool(alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool') alg.selectionTool.minPt = 4.5e3 seq.append(alg, inputPropName='particles', stageName='selection') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(2) # Set up the isolation correction algorithm: if isolationCorrection: alg = createAlgorithm('CP::EgammaIsolationCorrectionAlg', 'ElectronIsolationCorrectionAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) addPrivateTool(alg, 'isolationCorrectionTool', 'CP::IsolationCorrectionTool') if dataType == 'data': alg.isolationCorrectionTool.IsMC = 0 else: alg.isolationCorrectionTool.IsMC = 1 pass seq.append(alg, inputPropName='egammas', outputPropName='egammasOut', stageName='calibration') # Set up the isolation selection algorithm: if isolationWP != 'NonIso': alg = createAlgorithm('CP::EgammaIsolationSelectionAlg', 'ElectronIsolationSelectionAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = 'isolated' + postfix + ',as_bits' addPrivateTool(alg, 'selectionTool', 'CP::IsolationSelectionTool') alg.selectionTool.ElectronWP = isolationWP seq.append(alg, inputPropName='egammas', stageName='selection') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(1) # Select electrons only if they don't appear to have flipped their charge. if chargeIDSelection: alg = createAlgorithm('CP::AsgSelectionAlg', 'ElectronChargeIDSelectionAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.selectionDecoration = 'chargeID' + postfix + ',as_bits' addPrivateTool(alg, 'selectionTool', 'AsgElectronChargeIDSelectorTool') alg.selectionTool.TrainingFile = \ 'ElectronPhotonSelectorTools/ChargeID/ECIDS_20180731rel21Summer2018.root' alg.selectionTool.WorkingPoint = 'Loose' alg.selectionTool.CutOnBDT = -0.337671 # Loose 97% seq.append(alg, inputPropName='particles', stageName='selection') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(1) pass # Set up an algorithm used for decorating baseline electron selection: alg = createAlgorithm('CP::AsgSelectionAlg', 'ElectronSelectionSummary' + postfix) addPrivateTool(alg, 'selectionTool', 'CP::AsgFlagSelectionTool') alg.selectionTool.selectionFlags = selectionDecorNames[:] alg.selectionDecoration = 'baselineSelection' + postfix + ',as_char' seq.append(alg, inputPropName='particles', stageName='selection') # Set up an algorithm used to create electron selection cutflow: if enableCutflow: alg = createAlgorithm('CP::ObjectCutFlowHistAlg', 'ElectronCutFlowDumperAlg' + postfix) alg.histPattern = 'electron_cflow_%SYS%' + postfix alg.selection = selectionDecorNames[:] alg.selectionNCuts = selectionDecorCount[:] seq.append(alg, inputPropName='input', stageName='selection') # Set up an algorithm dumping the kinematic properties of the electrons: if enableKinematicHistograms: alg = createAlgorithm('CP::KinematicHistAlg', 'ElectronKinematicDumperAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.histPattern = 'electron_%VAR%_%SYS%' + postfix seq.append(alg, inputPropName='input', stageName='selection') # Set up the output selection if shallowViewOutput or deepCopyOutput: selectionDecorNamesOutput = selectionDecorNames[:] if not ptSelectionOutput: selectionDecorNamesOutput.remove(ptSelectionDecoration) # Set up an algorithm that makes a view container using the selections # performed previously: if shallowViewOutput: alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'ElectronViewFromSelectionAlg' + postfix) alg.selection = selectionDecorNamesOutput[:] seq.append(alg, inputPropName='input', outputPropName='output', stageName='selection') pass # Set up the electron efficiency correction algorithm: alg = createAlgorithm('CP::ElectronEfficiencyCorrectionAlg', 'ElectronEfficiencyCorrectionAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) addPrivateTool(alg, 'efficiencyCorrectionTool', 'AsgElectronEfficiencyCorrectionTool') alg.scaleFactorDecoration = 'effSF' + postfix + '_%SYS%' alg.scaleFactorDecorationRegex = '(^EL_EFF_Reco.*)' alg.efficiencyCorrectionTool.RecoKey = "Reconstruction" alg.efficiencyCorrectionTool.CorrelationModel = "TOTAL" if dataType == 'afii': alg.efficiencyCorrectionTool.ForceDataType = \ ROOT.PATCore.ParticleDataType.Fast elif dataType == 'mc': alg.efficiencyCorrectionTool.ForceDataType = \ ROOT.PATCore.ParticleDataType.Full pass alg.outOfValidity = 2 #silent alg.outOfValidityDeco = 'bad_eff' + postfix if dataType != 'data': seq.append(alg, inputPropName='electrons', affectingSystematics='(^EL_EFF_Reco.*)', stageName='efficiency') pass # Set up a final deep copy making algorithm if requested: if deepCopyOutput: alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'ElectronDeepCopyMaker' + postfix) alg.selection = selectionDecorNamesOutput[:] alg.deepCopy = True seq.append(alg, inputPropName='input', outputPropName='output', stageName='selection') pass # Return the sequence: return seq
def makeLargeRJetAnalysisSequence(seq, cutlist, cutlength, dataType, jetCollection, jetInput, postfix='', largeRMass="Comb"): """Add algorithms for the R=1.0 jets. Keyword arguments seq -- The sequence to add the algorithms to cutlist -- Insert any cuts into this cutlength -- Insert the lengths of any cuts into this dataType -- The data type to run on ("data", "mc" or "afii") jetCollection -- The jet container to run on. jetInput -- The type of input used, read from the collection name. postfix -- String to be added to the end of all public names. largeRMass -- Which large-R mass definition to use. Ignored if not running on large-R jets ("Comb", "Calo", "TCC", "TA") """ if largeRMass not in ["Comb", "Calo", "TCC", "TA"]: raise ValueError( "Invalid large-R mass defintion {0}!".format(largeRMass)) if jetInput not in ["LCTopo", "TrackCaloCluster"]: raise ValueError( "Unsupported input type '{0}' for large-R jets!".format(jetInput)) if jetInput == "TrackCaloCluster": # Only one mass defintion supported if largeRMass != "Calo": raise ValueError( "Unsupported large-R TCC jet mass '{0}'!".format(largeRMass)) configFile = "JES_MC16recommendation_FatJet_TCC_JMS_calo_30Oct2018.config" else: if largeRMass == "Comb": configFile = "JES_MC16recommendation_FatJet_Trimmed_JMS_comb_17Oct2018.config" elif largeRMass == "Calo": configFile = "JES_MC16recommendation_FatJet_Trimmed_JMS_calo_12Oct2018.config" elif largeRMass == "TCC": configFile = "JES_MC16recommendation_FatJet_TCC_JMS_calo_30Oct2018.config" else: configFile = "JES_MC16recommendation_FatJet_Trimmed_JMS_TA_12Oct2018.config" # Prepare the jet calibration algorithm alg = createAlgorithm('CP::JetCalibrationAlg', 'JetCalibrationAlg' + postfix) addPrivateTool(alg, 'calibrationTool', 'JetCalibrationTool') alg.calibrationTool.JetCollection = jetCollection[:-4] alg.calibrationTool.ConfigFile = configFile alg.calibrationTool.CalibSequence = "EtaJES_JMS" alg.calibrationTool.IsData = 0 seq.append(alg, inputPropName='jets', outputPropName='jetsOut', stageName='calibration') # Jet uncertainties alg = createAlgorithm('CP::JetUncertaintiesAlg', 'JetUncertaintiesAlg' + postfix) # R=1.0 jets have a validity range alg.outOfValidity = 2 # SILENT alg.outOfValidityDeco = 'outOfValidity' addPrivateTool(alg, 'uncertaintiesTool', 'JetUncertaintiesTool') alg.uncertaintiesTool.JetDefinition = jetCollection[:-4] alg.uncertaintiesTool.ConfigFile = \ "rel21/Moriond2018/R10_{0}Mass_all.config".format(largeRMass) alg.uncertaintiesTool.MCType = "MC16a" alg.uncertaintiesTool.IsData = (dataType == "data") seq.append(alg, inputPropName='jets', outputPropName='jetsOut', affectingSystematics=largeRSysts, stageName='calibration') cutlist.append('outOfValidity') cutlength.append(1)
def makeSmallRJetAnalysisSequence(seq, cutlist, cutlength, dataType, jetCollection, jetInput, postfix='', runJvtUpdate=True, runFJvtUpdate=True, runJvtSelection=True, runFJvtSelection=True, runJvtEfficiency=True, runFJvtEfficiency=True, reduction="Global", JEROption="Simple"): """Add algorithms for the R=0.4 jets. Keyword arguments seq -- The sequence to add the algorithms to cutlist -- Insert any cuts into this cutlength -- Insert the lengths of any cuts into this dataType -- The data type to run on ("data", "mc" or "afii") jetCollection -- The jet container to run on. jetInput -- The type of input used, read from the collection name. postfix -- String to be added to the end of all public names. runJvtUpdate -- Determines whether or not to update JVT on the jets runFJvtUpdate -- Determines whether or not to update forward JVT on the jets runJvtSelection -- Determines whether or not to run JVT selection on the jets runFJvtSelection -- Determines whether or not to run forward JVT selection on the jets runJvtEfficiency -- Determines whether or not to calculate the JVT efficiency runFJvtEfficiency -- Determines whether or not to calculate the forward JVT efficiency reduction -- Which NP reduction scheme should be used (All, Global, Category, Scenario) JEROption -- Which variant of the reduction should be used (All, Full, Simple). Note that not all combinations of reduction and JEROption are valid! """ if jetInput not in ["EMTopo", "EMPFlow"]: raise ValueError( "Unsupported input type '{0}' for R=0.4 jets!".format(jetInput)) # Prepare the jet calibration algorithm alg = createAlgorithm('CP::JetCalibrationAlg', 'JetCalibrationAlg' + postfix) addPrivateTool(alg, 'calibrationTool', 'JetCalibrationTool') alg.calibrationTool.JetCollection = jetCollection[:-4] # Get the correct string to use in the config file name if dataType == 'afii': configFile = "JES_MC16Recommendation_AFII_{0}_Apr2019_Rel21.config" else: configFile = "JES_MC16Recommendation_Consolidated_{0}_Apr2019_Rel21.config" if jetInput == "EMPFlow": configFile = configFile.format("PFlow") else: configFile = configFile.format(jetInput) alg.calibrationTool.ConfigFile = configFile if dataType == 'data': alg.calibrationTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Insitu' else: alg.calibrationTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Smear' alg.calibrationTool.IsData = (dataType == 'data') seq.append(alg, inputPropName='jets', outputPropName='jetsOut', stageName='calibration') # Jet uncertainties # Prepare the config file if reduction == "All" and JEROption == "All": alg.uncertaintiesTool.ConfigFile = "R4_AllNuisanceParameters_AllJERNP.config" elif "Scenario" in reduction: if JEROption != "Simple": raise ValueError( "Invalid uncertainty configuration - Scenario* reductions can " "only be used together with the Simple JEROption") configFile = "R4_{0}_SimpleJER.config".format(reduction) elif reduction in ["Global", "Category" ] and JEROption in ["Simple", "Full"]: configFile = "R4_{0}Reduction_{1}JER.config".format( reduction, JEROption) else: raise ValueError( "Invalid combination of reduction and JEROption settings: " "reduction: {0}, JEROption: {1}".format(reduction, JEROption)) alg = createAlgorithm('CP::JetUncertaintiesAlg', 'JetUncertaintiesTool' + postfix) addPrivateTool(alg, 'uncertaintiesTool', 'JetUncertaintiesTool') alg.uncertaintiesTool.JetDefinition = jetCollection[:-4] # Add the correct directory on the front alg.uncertaintiesTool.ConfigFile = "rel21/Fall2018/" + configFile alg.uncertaintiesTool.MCType = "AFII" if dataType == "afii" else "MC16" alg.uncertaintiesTool.IsData = (dataType == 'data') seq.append(alg, inputPropName='jets', outputPropName='jetsOut', affectingSystematics=smallRSysts, stageName='calibration') # Set up the JVT update algorithm: if runJvtUpdate: alg = createAlgorithm('CP::JvtUpdateAlg', 'JvtUpdateAlg' + postfix) addPrivateTool(alg, 'jvtTool', 'JetVertexTaggerTool') seq.append(alg, inputPropName='jets', outputPropName='jetsOut', stageName='selection') if runFJvtUpdate: alg = createAlgorithm('CP::JetModifierAlg', 'JetModifierAlg' + postfix) addPrivateTool(alg, 'modifierTool', 'JetForwardJvtTool') alg.modifierTool.OutputDec = "passFJVT" #Output decoration # fJVT WPs depend on the MET WP # see https://twiki.cern.ch/twiki/bin/view/AtlasProtected/EtmissRecommendationsRel21p2#fJVT_and_MET alg.modifierTool.UseTightOP = 1 # 1 = Tight, 0 = Loose alg.modifierTool.EtaThresh = 2.5 # Eta dividing central from forward jets alg.modifierTool.ForwardMaxPt = 120.0e3 #Max Pt to define fwdJets for JVT seq.append(alg, inputPropName='jets', outputPropName='jetsOut', stageName='selection') pass # Set up the jet efficiency scale factor calculation algorithm # Change the truthJetCollection property to AntiKt4TruthWZJets if preferred if runJvtSelection: alg = createAlgorithm('CP::JvtEfficiencyAlg', 'JvtEfficiencyAlg' + postfix) addPrivateTool(alg, 'efficiencyTool', 'CP::JetJvtEfficiency') if jetInput == 'EMPFlow': alg.efficiencyTool.SFFile = 'JetJvtEfficiency/Moriond2018/JvtSFFile_EMPFlow.root' alg.efficiencyTool.MaxPtForJvt = 60e3 else: alg.efficiencyTool.SFFile = 'JetJvtEfficiency/Moriond2018/JvtSFFile_EMTopoJets.root' alg.efficiencyTool.MaxPtForJvt = 120e3 alg.efficiencyTool.WorkingPoint = 'Tight' if jetInput == 'EMPFlow' else 'Medium' alg.selection = 'jvt_selection' alg.scaleFactorDecoration = 'jvt_effSF_%SYS%' alg.scaleFactorDecorationRegex = jvtSysts # Disable scale factor decorations if running on data # We still want to run the JVT selection if not runJvtEfficiency or dataType == 'data': alg.scaleFactorDecoration = '' alg.truthJetCollection = '' alg.outOfValidity = 2 alg.outOfValidityDeco = 'no_jvt' alg.skipBadEfficiency = 0 seq.append(alg, inputPropName='jets', affectingSystematics=jvtSysts, stageName='selection') if runFJvtSelection: alg = createAlgorithm('CP::JvtEfficiencyAlg', 'ForwardJvtEfficiencyAlg') addPrivateTool(alg, 'efficiencyTool', 'CP::JetJvtEfficiency') alg.efficiencyTool.SFFile = 'JetJvtEfficiency/Moriond2018/fJvtSFFile.root' alg.efficiencyTool.WorkingPoint = 'Tight' alg.dofJVT = True alg.fJVTStatus = 'passFJVT,as_char' alg.selection = 'fjvt_selection' alg.scaleFactorDecoration = 'fjvt_effSF_%SYS%' alg.scaleFactorDecorationRegex = fjvtSysts # Disable scale factor decorations if running on data # We still want to run the JVT selection if not runFJvtEfficiency or dataType == 'data': alg.scaleFactorDecoration = '' alg.truthJetCollection = '' alg.outOfValidity = 2 alg.outOfValidityDeco = 'no_fjvt' alg.skipBadEfficiency = 0 seq.append(alg, inputPropName='jets', affectingSystematics=fjvtSysts, stageName='selection') # Return the sequence: return seq, cutlist, cutlength
def makeSequence(dataType): algSeq = AlgSequence() # Set up the systematics loader/handler algorithm: sysLoader = createAlgorithm('CP::SysListLoaderAlg', 'SysLoaderAlg') sysLoader.sigmaRecommended = 1 algSeq += sysLoader # Include, and then set up the jet analysis algorithm sequence: from JetAnalysisAlgorithms.JetAnalysisSequence import makeJetAnalysisSequence jetContainer = 'AntiKt4EMPFlowJets' jetSequence = makeJetAnalysisSequence(dataType, jetContainer) jetSequence.configure(inputName=jetContainer, outputName='AnalysisJets_%SYS%') # Add all algorithms to the job: algSeq += jetSequence # Set up a selection alg for demonstration purposes # Also to avoid warnings from building MET with very soft electrons selalg = createAlgorithm('CP::AsgSelectionAlg', 'METEleSelAlg') addPrivateTool(selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool') selalg.selectionTool.minPt = 10e3 selalg.selectionTool.maxEta = 2.47 selalg.selectionDecoration = 'selectPtEta' selalg.particles = 'Electrons' # We need to copy here, because w/o an output container, it's assumed # that the input container is non-const selalg.particlesOut = 'DecorElectrons_%SYS%' algSeq += selalg # Now make a view container holding only the electrons for the MET calculation viewalg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'METEleViewAlg') viewalg.selection = ['selectPtEta'] viewalg.input = 'DecorElectrons_%SYS%' viewalg.output = 'METElectrons_%SYS%' algSeq += viewalg # Include, and then set up the met analysis algorithm sequence: from MetAnalysisAlgorithms.MetAnalysisSequence import makeMetAnalysisSequence metSequence = makeMetAnalysisSequence(dataType, metSuffix=jetContainer[:-4]) metSequence.configure(inputName={ 'jets': 'AnalysisJets_%SYS%', 'muons': 'Muons', 'electrons': 'METElectrons_%SYS%' }, outputName='AnalysisMET_%SYS%', affectingSystematics={ 'jets': jetSequence.affectingSystematics(), 'muons': '(^$)', 'electrons': '(^$)' }) # Add the sequence to the job: algSeq += metSequence # Write the freshly produced MET object(s) to an output file: treeMaker = createAlgorithm('CP::TreeMakerAlg', 'TreeMaker') treeMaker.TreeName = 'met' algSeq += treeMaker ntupleMaker = createAlgorithm('CP::AsgxAODNTupleMakerAlg', 'NTupleMaker') ntupleMaker.TreeName = 'met' ntupleMaker.Branches = [ 'EventInfo.runNumber -> runNumber', 'EventInfo.eventNumber -> eventNumber', 'AnalysisMET_%SYS%.mpx -> met_%SYS%_mpx', 'AnalysisMET_%SYS%.mpy -> met_%SYS%_mpy', 'AnalysisMET_%SYS%.sumet -> met_%SYS%_sumet', 'AnalysisMET_%SYS%.name -> met_%SYS%_name', ] ntupleMaker.systematicsRegex = '.*' algSeq += ntupleMaker treeFiller = createAlgorithm('CP::TreeFillerAlg', 'TreeFiller') treeFiller.TreeName = 'met' algSeq += treeFiller return algSeq
def makeMetAnalysisSequence(dataType, metSuffix, postfix='', useFJVT=True, treatPUJets=True): """Create a met analysis algorithm sequence After creating the sequence object, it needs to be configured with a call like: metSequence.configure( inputName = { 'jets' : 'AntiKt4EMPFlowJets_%SYS%', 'electrons' : 'AnalysisElectrons_%SYS%', 'photons' : 'AnalysisPhotons_%SYS%', 'muons' : 'AnalysisMuons_%SYS%', 'taus' : 'AnalysisTaus_%STS%', }, outputName = 'AnalysisMET_%SYS%', affectingSystematics = { 'jets' : '(^$)|(^JET_.*)', 'electrons' : '(^$)|(^EG_.*)|(^EL_.*)', 'photons' : '(^$)|(^EG_.*)|(^PH_.*)', 'muons' : '(^$)|(^MUON_.*)', 'taus' : '(^$)|(^TAUS_.*)', } ) Note that defining a jet container is mandatory, but all other input containers are optional. Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") metSuffix -- Suffix for the (core) MET objects to use from the input (file) useFJVT -- Use FJVT decision for the calculation treatPUJets -- Treat pile-up jets in the MET significance calculation """ if dataType not in ["data", "mc", "afii"]: raise ValueError("invalid data type: " + dataType) if not useFJVT and treatPUJets: raise ValueError("MET significance pile-up treatment requires fJVT") # Remove b-tagging calibration from the MET suffix name btIndex = metSuffix.find('_BTagging') if btIndex != -1: metSuffix = metSuffix[:btIndex] # Create the analysis algorithm sequence object: seq = AnaAlgSequence("MetAnalysisSequence" + postfix) # Set up the met maker algorithm: alg = createAlgorithm('CP::MetMakerAlg', 'MetMakerAlg' + postfix) addPrivateTool(alg, 'makerTool', 'met::METMaker') alg.makerTool.DoPFlow = 'PFlow' in metSuffix if useFJVT: alg.makerTool.JetRejectionDec = 'passFJVT' alg.metCore = 'MET_Core_' + metSuffix alg.metAssociation = 'METAssoc_' + metSuffix seq.append(alg, inputPropName={ 'jets': 'jets', 'electrons': 'electrons', 'photons': 'photons', 'muons': 'muons', 'taus': 'taus', 'invisible': 'invisible' }, outputPropName='met', affectingSystematics='(^MET_.*)') if dataType != "data": alg = createAlgorithm('CP::MetSystematicsAlg', 'MetSystematicsAlg' + postfix) addPrivateTool(alg, 'systematicsTool', 'met::METSystematicsTool') seq.append(alg, inputPropName='met', affectingSystematics='(^MET_.*)') pass # Set up the met builder algorithm: alg = createAlgorithm('CP::MetBuilderAlg', 'MetBuilderAlg' + postfix) seq.append(alg, inputPropName='met') # Set up the met significance algorithm: alg = createAlgorithm('CP::MetSignificanceAlg', 'MetSignificanceAlg' + postfix) addPrivateTool(alg, 'significanceTool', 'met::METSignificance') alg.significanceTool.SoftTermParam = 0 alg.significanceTool.TreatPUJets = treatPUJets alg.significanceTool.IsAFII = dataType == "afii" seq.append(alg, inputPropName='met') # Return the sequence: return seq
def makePhotonAnalysisSequence(dataType, workingPoint, deepCopyOutput=False, postfix='', recomputeIsEM=False, enableCutflow=False, enableKinematicHistograms=False): """Create a photon analysis algorithm sequence Keywrod arguments: dataType -- The data type to run on ("data", "mc" or "afii") workingPoint -- The working point to use deepCopyOutput -- If set to 'True', the output containers will be standalone, deep copies (slower, but needed for xAOD output writing) 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. recomputeIsEM -- Whether to rerun the cut-based selection. If not, use derivation flags enableCutflow -- Whether or not to dump the cutflow enableKinematicHistograms -- Whether or not to dump the kinematic histograms """ # Make sure we received a valid data type. if dataType not in ['data', 'mc', 'afii']: raise ValueError('Invalid data type: %' % dataType) if postfix != '': postfix = '_' + postfix pass splitWP = workingPoint.split('.') if len(splitWP) != 2: raise ValueError( 'working point should be of format "quality.isolation", not ' + workingPoint) qualityWP = splitWP[0] isolationWP = splitWP[1] if qualityWP == 'Tight': quality = ROOT.egammaPID.PhotonTight pass elif qualityWP == 'Loose': quality = ROOT.egammaPID.PhotonLoose pass else: raise Exception('unknown photon quality working point "' + qualityWP + '" should be Tight or Loose') # Create the analysis algorithm sequence object: seq = AnaAlgSequence("PhotonAnalysisSequence" + postfix) # Variables keeping track of the selections being applied. selectionDecorNames = [] selectionDecorCount = [] # Set up the photon selection algorithm: alg = createAlgorithm('CP::AsgSelectionAlg', 'PhotonIsEMSelectorAlg' + postfix) alg.selectionDecoration = 'selectEM' selectionDecorNames.append(alg.selectionDecoration) if recomputeIsEM: # Rerun the cut-based ID addPrivateTool(alg, 'selectionTool', 'AsgPhotonIsEMSelector') alg.selectionTool.isEMMask = quality alg.selectionTool.ConfigFile = \ 'ElectronPhotonSelectorTools/offline/20180116/PhotonIsEMTightSelectorCutDefs.conf' selectionDecorCount.append(32) else: # Select from Derivation Framework flags addPrivateTool(alg, 'selectionTool', 'CP::AsgFlagSelectionTool') dfFlag = 'DFCommonPhotonsIsEM' + qualityWP alg.selectionTool.selectionFlags = [dfFlag] selectionDecorCount.append(1) pass seq.append(alg, inputPropName='particles', outputPropName='particlesOut', stageName='calibration') # Select electrons only with good object quality. alg = createAlgorithm('CP::AsgSelectionAlg', 'PhotonObjectQualityAlg' + postfix) alg.selectionDecoration = 'goodOQ' addPrivateTool(alg, 'selectionTool', 'CP::EgammaIsGoodOQSelectionTool') alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSPHOTON seq.append(alg, inputPropName='particles', outputPropName='particlesOut', stageName='calibration') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(1) # Only run subsequent processing on the objects passing all of these cuts. # Since these are independent of the photon calibration, and this speeds # up the job. alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'PhotonPreSelViewFromSelectionAlg' + postfix) alg.selection = selectionDecorNames[:] seq.append(alg, inputPropName='input', outputPropName='output', stageName='calibration') # Set up the calibration ans smearing algorithm. alg = createAlgorithm('CP::EgammaCalibrationAndSmearingAlg', 'PhotonCalibrationAndSmearingAlg' + postfix) addPrivateTool(alg, 'calibrationAndSmearingTool', 'CP::EgammaCalibrationAndSmearingTool') alg.calibrationAndSmearingTool.ESModel = 'es2018_R21_v0' alg.calibrationAndSmearingTool.decorrelationModel = '1NP_v1' if dataType == 'afii': alg.calibrationAndSmearingTool.useAFII = 1 else: alg.calibrationAndSmearingTool.useAFII = 0 pass seq.append(alg, inputPropName='egammas', outputPropName='egammasOut', affectingSystematics='(^EG_RESOLUTION_.*)|(^EG_SCALE_.*)', stageName='calibration') # should this be applied to data? or to AFII? alg = createAlgorithm('CP::PhotonShowerShapeFudgeAlg', 'PhotonShowerShapeFudgeAlg' + postfix) addPrivateTool(alg, 'showerShapeFudgeTool', 'ElectronPhotonShowerShapeFudgeTool') alg.showerShapeFudgeTool.Preselection = 21 # 21 = MC15 alg.showerShapeFudgeTool.FFCalibFile = \ 'ElectronPhotonShowerShapeFudgeTool/v1/PhotonFudgeFactors.root' #only for rel21 seq.append(alg, inputPropName='photons', outputPropName='photonsOut', stageName='calibration') # Set up the isolation correction algorithm. alg = createAlgorithm('CP::EgammaIsolationCorrectionAlg', 'PhotonIsolationCorrectionAlg' + postfix) addPrivateTool(alg, 'isolationCorrectionTool', 'CP::IsolationCorrectionTool') if dataType == 'data': alg.isolationCorrectionTool.IsMC = 0 else: alg.isolationCorrectionTool.IsMC = 1 pass seq.append(alg, inputPropName='egammas', outputPropName='egammasOut', stageName='selection') # Set up the isolation selection algorithm: alg = createAlgorithm('CP::EgammaIsolationSelectionAlg', 'PhotonIsolationSelectionAlg' + postfix) alg.selectionDecoration = 'isolated' + postfix addPrivateTool(alg, 'selectionTool', 'CP::IsolationSelectionTool') alg.selectionTool.PhotonWP = isolationWP seq.append(alg, inputPropName='egammas', outputPropName='egammasOut', stageName='selection') selectionDecorNames.append(alg.selectionDecoration) selectionDecorCount.append(1) # Set up the photon efficiency correction algorithm. alg = createAlgorithm('CP::PhotonEfficiencyCorrectionAlg', 'PhotonEfficiencyCorrectionAlg' + postfix) addPrivateTool(alg, 'efficiencyCorrectionTool', 'AsgPhotonEfficiencyCorrectionTool') alg.scaleFactorDecoration = 'effSF' + postfix alg.efficiencyCorrectionTool.MapFilePath = \ 'PhotonEfficiencyCorrection/2015_2017/rel21.2/Winter2018_Prerec_v1/map0.txt' if dataType == 'afii': alg.efficiencyCorrectionTool.ForceDataType = \ ROOT.PATCore.ParticleDataType.Fast elif dataType == 'mc': alg.efficiencyCorrectionTool.ForceDataType = \ ROOT.PATCore.ParticleDataType.Full pass alg.outOfValidity = 2 #silent alg.outOfValidityDeco = 'bad_eff' + postfix if dataType != 'data': seq.append(alg, inputPropName='photons', outputPropName='photonsOut', affectingSystematics='(^PH_EFF_.*)', stageName='efficiency') selectionDecorNames.append(alg.outOfValidityDeco) selectionDecorCount.append(1) pass # Set up an algorithm used to create photon selection cutflow: if enableCutflow: alg = createAlgorithm('CP::ObjectCutFlowHistAlg', 'PhotonCutFlowDumperAlg' + postfix) alg.histPattern = 'photon_cflow_%SYS%' + postfix alg.selection = selectionDecorNames[:] alg.selectionNCuts = selectionDecorCount[:] seq.append(alg, inputPropName='input', stageName='selection') # Set up an algorithm that makes a view container using the selections # performed previously: alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'PhotonViewFromSelectionAlg' + postfix) alg.selection = selectionDecorNames[:] seq.append(alg, inputPropName='input', outputPropName='output', stageName='selection') # Set up an algorithm dumping the kinematic properties of the photons: if enableKinematicHistograms: alg = createAlgorithm('CP::KinematicHistAlg', 'PhotonKinematicDumperAlg' + postfix) alg.preselection = "&&".join(selectionDecorNames) alg.histPattern = 'photon_%VAR%_%SYS%' + postfix seq.append(alg, inputPropName='input', stageName='selection') # Set up a final deep copy making algorithm if requested: if deepCopyOutput: alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'PhotonDeepCopyMaker' + postfix) alg.deepCopy = True seq.append(alg, inputPropName='input', outputPropName='output', stageName='selection') pass # Return the sequence: return seq
ROOT.SH.ScanDir().filePattern('DAOD_PHYS.21569875._001323.pool.root.1').scan( sh, inputFilePath) sh.printContent() # Create an EventLoop job. job = ROOT.EL.Job() job.sampleHandler(sh) job.options().setDouble(ROOT.EL.Job.optMaxEvents, 500) job.options().setString(ROOT.EL.Job.optSubmitDirMode, 'unique-link') # Create the algorithm's configuration. from AnaAlgorithm.DualUseConfig import createAlgorithm from AnaAlgorithm.DualUseConfig import addPrivateTool alg = createAlgorithm('MyxAODAnalysis', 'AnalysisAlg') addPrivateTool(alg, 'grlTool', 'GoodRunsListSelectionTool') # configure the properties of the GRL tool fullGRLFilePath = os.getenv( "ALRB_TutorialData" ) + "/data16_13TeV.periodAllYear_DetStatus-v89-pro21-01_DQDefects-00-02-04_PHYS_StandardGRL_All_Good_25ns.xml" alg.grlTool.GoodRunsListVec = [fullGRLFilePath] alg.grlTool.PassThrough = 0 # if true (default) will ignore result of GRL and will just pass all events # later on we'll add some configuration options for our algorithm that go here alg.ElectronPtCut = 30000.0 alg.SampleName = 'Zee' # Add our algorithm to the job job.algsAdd(alg)
def makeDiTauAnalysisSequence( dataType, workingPoint, deepCopyOutput = False, postfix = '' ): """Create a tau analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") deepCopyOutput -- If set to 'True', the output containers will be standalone, deep copies (slower, but needed for xAOD output writing) 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. """ if dataType not in ["data", "mc", "afii"] : raise ValueError ("invalid data type: " + dataType) if postfix != '' : postfix = '_' + postfix pass splitWP = workingPoint.split ('.') if len (splitWP) != 1 : raise ValueError ('working point should be of format "quality", not ' + workingPoint) # using enum value from: https://gitlab.cern.ch/atlas/athena/blob/21.2/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h # the dictionary is missing in Athena, so hard-coding values here if splitWP[0] == 'Tight' : IDLevel = 4 # ROOT.TauAnalysisTools.JETIDBDTTIGHT pass elif splitWP[0] == 'Medium' : IDLevel = 3 # ROOT.TauAnalysisTools.JETIDBDTMEDIUM pass elif splitWP[0] == 'Loose' : IDLevel = 2 # ROOT.TauAnalysisTools.JETIDBDTLOOSE pass else : raise ValueError ("invalid tau quality: \"" + splitWP[0] + "\", allowed values are Tight, Medium, Loose, " + "VeryLoose") # Create the analysis algorithm sequence object: seq = AnaAlgSequence( "DiTauAnalysisSequence" + postfix ) # Set up the tau 4-momentum smearing algorithm: alg = createAlgorithm( 'CP::DiTauSmearingAlg', 'DiTauSmearingAlg' + postfix ) addPrivateTool( alg, 'smearingTool', 'TauAnalysisTools::DiTauSmearingTool' ) seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut', affectingSystematics = '(^TAUS_TRUEHADDITAU_SME_TES_.*)', stageName = 'calibration' ) # Set up an algorithm dumping the properties of the taus, for debugging: alg = createAlgorithm( 'CP::KinematicHistAlg', 'DiTauKinematicDumperAlg' + postfix ) alg.histPattern = "tau_%VAR%_%SYS%" seq.append( alg, inputPropName = 'input', stageName = 'selection' ) # Set up the algorithm calculating the efficiency scale factors for the # taus: alg = createAlgorithm( 'CP::DiTauEfficiencyCorrectionsAlg', 'DiTauEfficiencyCorrectionsAlg' + postfix ) addPrivateTool( alg, 'efficiencyCorrectionsTool', 'TauAnalysisTools::DiTauEfficiencyCorrectionsTool' ) alg.efficiencyCorrectionsTool.IDLevel = IDLevel alg.scaleFactorDecoration = 'tau_effSF' + postfix # alg.outOfValidity = 2 #silent # alg.outOfValidityDeco = "bad_eff" seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut', affectingSystematics = '(^TAUS_TRUEHADDITAU_EFF_JETID_.*)', stageName = 'efficiency' ) # Set up the tau truth matching algorithm: if dataType != 'data': alg = createAlgorithm( 'CP::DiTauTruthMatchingAlg', 'DiTauTruthMatchingAlg' + postfix ) addPrivateTool( alg, 'matchingTool', 'TauAnalysisTools::DiTauTruthMatchingTool' ) alg.matchingTool.WriteTruthTaus = 1 seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut', stageName = 'selection' ) pass # Set up a final deep copy making algorithm if requested: if deepCopyOutput: alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'DiTauDeepCopyMaker' + postfix ) alg.deepCopy = True seq.append( alg, inputPropName = 'input', outputPropName = 'output', stageName = 'selection' ) pass # Return the sequence: return seq
def makeOverlapAnalysisSequence( dataType, inputLabel = '', outputLabel = 'passesOR', linkOverlapObjects = False, doMuPFJetOR=False, doEleEleOR = False, doElectrons = True, doMuons = True, doJets = True, doTaus = True, doPhotons = True, doFatJets = False, bJetLabel = '', boostedLeptons = False, postfix = '', enableCutflow = False ): """Function creating the overlap removal algorithm sequence The function sets up a multi-input/multi-output analysis algorithm sequnce, which needs to be used in a quite particular way. First off you need to set the arguments of this function correctly. Then, you need to call the configure(...) method on the algorithm sequence returned by this function in the following way: overlapSequence.configure( inputName = { 'electrons' : 'AnalysisElectrons_%SYS%', 'photons' : 'AnalysisPhotons_%SYS%', 'muons' : 'AnalysisMuons_%SYS%', 'jets' : 'AnalysisJets_%SYS%', 'taus' : 'AnalysisTauJets_%SYS%' }, outputName = { 'electrons' : 'AnalysisElectronsOR_%SYS%', 'photons' : 'AnalysisPhotonsOR_%SYS%', 'muons' : 'AnalysisMuonsOR_%SYS%', 'jets' : 'AnalysisJetsOR_%SYS%', 'taus' : 'AnalysisTauJetsOR_%SYS%' }, affectingSystematics = { 'electrons' : '(^$)|(^EG_.*)|(^EL_.*)', 'photons' : '(^$)|(^EG_.*)|(^PH_.*)', 'muons' : '(^$)|(^MUON_.*)', 'jets' : '(^$)|(^JET_.*)', 'taus' : '(^$)|(^TAUS_.*)' } ) Where: - You need to provide input and output names in pairs, you must not skip specifying an output name if you specified an input name, and vice versa. - You only define inputs/outputs that your analysis uses. The "labels" of the possible inputs/outputs are: "electrons", "photons", "muons", "jets", "taus" and "fatJets". - You have to define with affectingSystematics which systematic variations are affecting the containers you passed to the sequence as inputs. If left empty, the configuration assumes that no systematic variation is affecting the input(s). Function keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") inputLabel -- Any possible label to pick up the selected objects with. If left empty, all objects from the input containers are considered. outputLabel -- Decoration put on the output variables. Set to "true" for objects passing the overlap removal. linkOverlapObjects -- Set up an element link between overlapping objects doMuPFJetOR -- Set up overlap removal for PFlow jets that are acutally muons doEleEleOR -- Set up electron-electron overlap removal doXXXX -- these flags enable/disable object types to configure tools for: doElectrons, doMuons, doJets, doTaus, doPhotons, doFatJets. bJetLabel -- Flag to select b-jets with. If left empty, no b-jets are used in the overlap removal. boostedLeptons -- Set to True to enable boosted lepton overlap removal enableCutflow -- Whether or not to dump the cutflow """ if dataType not in ["data", "mc", "afii"] : raise ValueError ("invalid data type: " + dataType) # Create the analysis algorithm sequence object: seq = AnaAlgSequence( 'OverlapAnalysisSequence' + postfix ) # Create the overlap removal algorithm: alg = createAlgorithm( 'CP::OverlapRemovalAlg', 'OverlapRemovalAlg' + postfix ) # Create its main tool, and set its basic properties: addPrivateTool( alg, 'overlapTool', 'ORUtils::OverlapRemovalTool' ) alg.overlapTool.InputLabel = inputLabel alg.overlapTool.OutputLabel = outputLabel # By default the OverlapRemovalTool would flag objects that need to be # suppressed, with a "true" value. But since the analysis algorithms expect # the opposite behaviour from selection flags, we need to tell the tool # explicitly to use the "true" flag on objects that pass the overlap # removal. alg.overlapTool.OutputPassValue = True # Set up overlap removal for PFlow jets that are acutally muons, if requested. if doMuPFJetOR: addPrivateTool( alg, 'overlapTool.MuPFJetORT', 'ORUtils::MuPFJetOverlapTool' ) alg.overlapTool.MuPFJetORT.InputLabel = inputLabel alg.overlapTool.MuPFJetORT.OutputLabel = outputLabel alg.overlapTool.MuPFJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.MuPFJetORT.OutputPassValue = True pass # Set up the electron-electron overlap removal, if requested. if doElectrons and doEleEleOR: addPrivateTool( alg, 'overlapTool.EleEleORT', 'ORUtils::EleEleOverlapTool' ) alg.overlapTool.EleEleORT.InputLabel = inputLabel alg.overlapTool.EleEleORT.OutputLabel = outputLabel alg.overlapTool.EleEleORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.EleEleORT.OutputPassValue = True pass # Set up the electron-muon overlap removal. if doElectrons and doMuons: addPrivateTool( alg, 'overlapTool.EleMuORT', 'ORUtils::EleMuSharedTrkOverlapTool' ) alg.overlapTool.EleMuORT.InputLabel = inputLabel alg.overlapTool.EleMuORT.OutputLabel = outputLabel alg.overlapTool.EleMuORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.EleMuORT.OutputPassValue = True pass # Set up the electron-(narrow-)jet overlap removal. if doElectrons and doJets: addPrivateTool( alg, 'overlapTool.EleJetORT', 'ORUtils::EleJetOverlapTool' ) alg.overlapTool.EleJetORT.InputLabel = inputLabel alg.overlapTool.EleJetORT.OutputLabel = outputLabel alg.overlapTool.EleJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.EleJetORT.BJetLabel = bJetLabel alg.overlapTool.EleJetORT.UseSlidingDR = boostedLeptons alg.overlapTool.EleJetORT.OutputPassValue = True pass # Set up the muon-(narrow-)jet overlap removal. if doMuons and doJets: addPrivateTool( alg, 'overlapTool.MuJetORT', 'ORUtils::MuJetOverlapTool' ) alg.overlapTool.MuJetORT.InputLabel = inputLabel alg.overlapTool.MuJetORT.OutputLabel = outputLabel alg.overlapTool.MuJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.MuJetORT.BJetLabel = bJetLabel alg.overlapTool.MuJetORT.UseSlidingDR = boostedLeptons alg.overlapTool.MuJetORT.OutputPassValue = True pass # Set up the tau-electron overlap removal. if doTaus and doElectrons: addPrivateTool( alg, 'overlapTool.TauEleORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.TauEleORT.InputLabel = inputLabel alg.overlapTool.TauEleORT.OutputLabel = outputLabel alg.overlapTool.TauEleORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.TauEleORT.DR = 0.2 alg.overlapTool.TauEleORT.OutputPassValue = True pass # Set up the tau-muon overlap removal. if doTaus and doMuons: addPrivateTool( alg, 'overlapTool.TauMuORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.TauMuORT.InputLabel = inputLabel alg.overlapTool.TauMuORT.OutputLabel = outputLabel alg.overlapTool.TauMuORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.TauMuORT.DR = 0.2 alg.overlapTool.TauMuORT.OutputPassValue = True pass # Set up the tau-(narrow-)jet overlap removal. if doTaus and doJets: addPrivateTool( alg, 'overlapTool.TauJetORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.TauJetORT.InputLabel = inputLabel alg.overlapTool.TauJetORT.OutputLabel = outputLabel alg.overlapTool.TauJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.TauJetORT.DR = 0.2 alg.overlapTool.TauJetORT.OutputPassValue = True pass # Set up the photon-electron overlap removal. if doPhotons and doElectrons: addPrivateTool( alg, 'overlapTool.PhoEleORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.PhoEleORT.InputLabel = inputLabel alg.overlapTool.PhoEleORT.OutputLabel = outputLabel alg.overlapTool.PhoEleORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.PhoEleORT.OutputPassValue = True pass # Set up the photon-muon overlap removal. if doPhotons and doMuons: addPrivateTool( alg, 'overlapTool.PhoMuORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.PhoMuORT.InputLabel = inputLabel alg.overlapTool.PhoMuORT.OutputLabel = outputLabel alg.overlapTool.PhoMuORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.PhoMuORT.OutputPassValue = True pass # Set up the photon-(narrow-)jet overlap removal. if doPhotons and doJets: addPrivateTool( alg, 'overlapTool.PhoJetORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.PhoJetORT.InputLabel = inputLabel alg.overlapTool.PhoJetORT.OutputLabel = outputLabel alg.overlapTool.PhoJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.PhoJetORT.OutputPassValue = True pass # Set up the electron-fat-jet overlap removal. if doElectrons and doFatJets: addPrivateTool( alg, 'overlapTool.EleFatJetORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.EleFatJetORT.InputLabel = inputLabel alg.overlapTool.EleFatJetORT.OutputLabel = outputLabel alg.overlapTool.EleFatJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.EleFatJetORT.DR = 1.0 alg.overlapTool.EleFatJetORT.OutputPassValue = True pass # Set up the (narrow-)jet-fat-jet overlap removal. if doJets and doFatJets: addPrivateTool( alg, 'overlapTool.JetFatJetORT', 'ORUtils::DeltaROverlapTool' ) alg.overlapTool.JetFatJetORT.InputLabel = inputLabel alg.overlapTool.JetFatJetORT.OutputLabel = outputLabel alg.overlapTool.JetFatJetORT.LinkOverlapObjects = linkOverlapObjects alg.overlapTool.JetFatJetORT.DR = 1.0 alg.overlapTool.JetFatJetORT.OutputPassValue = True pass # Add the algorithm to the analysis sequence. seq.append( alg, inputPropName = { 'electrons' : 'electrons', 'muons' : 'muons', 'jets' : 'jets', 'taus' : 'taus', 'photons' : 'photons', 'fatJets' : 'fatJets' }, outputPropName = { 'electrons' : 'electronsOut', 'muons' : 'muonsOut', 'jets' : 'jetsOut', 'taus' : 'tausOut', 'photons' : 'photonsOut', 'fatJets' : 'fatJetsOut' } ) # Add view container creation algorithms for all types. for container in [ ( 'electrons', doElectrons ), ( 'muons', doMuons ), ( 'jets', doJets ), ( 'taus', doTaus ), ( 'photons', doPhotons ), ( 'fatJets', doFatJets ) ]: # Skip setting up a view container if the type is not being processed. if not container[ 1 ]: continue # Set up a cutflow alg. if enableCutflow: alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'OverlapRemovalCutFlowDumperAlg_%s' % container[ 0 ] + postfix ) alg.histPattern = container[ 0 ] + postfix + '_OR_cflow_%SYS%' if inputLabel: alg.selection = [ '%s,as_char' % inputLabel, '%s,as_char' % outputLabel ] alg.selectionNCuts = [1, 1] else: alg.selection = [ '%s,as_char' % outputLabel ] alg.selectionNCuts = [1] seq.append( alg, inputPropName = { container[ 0 ] : 'input' } ) # Set up a view container for the type. alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'OverlapRemovalViewMaker_%s' % container[ 0 ] + postfix ) alg.selection = [ '%s,as_char' % outputLabel ] seq.append( alg, inputPropName = { container[ 0 ] : 'input' }, outputPropName = { container[ 0 ] : 'output' } ) pass # Return the sequence: return seq
def makeMuonAnalysisSequence(dataType, workingPoint, deepCopyOutput=False, shallowViewOutput=True, postfix='', ptSelectionOutput=False, qualitySelectionOutput=True, enableCutflow=False, enableKinematicHistograms=False): """Create a muon analysis algorithm sequence Keyword arguments: dataType -- The data type to run on ("data", "mc" or "afii") workingPoint -- The working point to use deepCopyOutput -- If set to 'True', the output containers will be standalone, deep copies (slower, but needed for xAOD output writing) shallowViewOutput -- Create a view container if required 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. ptSelectionOutput -- Whether or not to apply pt selection when creating output containers. qualitySelectionOutput -- Whether or not to apply muon quality selection when creating output containers. enableCutflow -- Whether or not to dump the cutflow enableKinematicHistograms -- Whether or not to dump the kinematic histograms """ if dataType not in ["data", "mc", "afii"]: raise ValueError("invalid data type: " + dataType) if postfix != '': postfix = '_' + postfix pass # Make sure selection options make sense if deepCopyOutput and shallowViewOutput: raise ValueError( "deepCopyOutput and shallowViewOutput can't both be true!") splitWP = workingPoint.split('.') if len(splitWP) != 2: raise ValueError( 'working point should be of format "quality.isolation", not ' + workingPoint) sfWorkingPoint = splitWP[0] if splitWP[0] == 'Tight': quality = ROOT.xAOD.Muon.Tight pass elif splitWP[0] == 'Medium': quality = ROOT.xAOD.Muon.Medium pass elif splitWP[0] == 'Loose': quality = ROOT.xAOD.Muon.Loose pass elif splitWP[0] == 'VeryLoose': quality = ROOT.xAOD.Muon.VeryLoose pass elif splitWP[0] == 'HighPt': quality = 4 pass elif splitWP[0] == 'LowPtEfficiency': quality = 5 pass else: raise ValueError("invalid muon quality: \"" + splitWP[0] + "\", allowed values are Tight, Medium, Loose, " + "VeryLoose, HighPt, LowPtEfficiency") if not splitWP[1] in ["Iso", "NonIso"]: raise ValueError('invalid muon isolation \"' + splitWP[1] + '\", allowed values are Iso, NonIso') # Create the analysis algorithm sequence object: seq = AnaAlgSequence("MuonAnalysisSequence" + postfix) seq.addMetaConfigDefault("selectionDecorNames", []) seq.addMetaConfigDefault("selectionDecorNamesOutput", []) seq.addMetaConfigDefault("selectionDecorCount", []) # Set up the eta-cut on all muons prior to everything else alg = createAlgorithm('CP::AsgSelectionAlg', 'MuonEtaCutAlg' + postfix) addPrivateTool(alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool') alg.selectionTool.maxEta = 2.5 alg.selectionDecoration = 'selectEta' + postfix + ',as_bits' seq.append(alg, inputPropName='particles', outputPropName='particlesOut', stageName='selection', metaConfig={ 'selectionDecorNames': [alg.selectionDecoration], 'selectionDecorNamesOutput': [alg.selectionDecoration], 'selectionDecorCount': [2] }, dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Set up the track selection algorithm: alg = createAlgorithm('CP::AsgLeptonTrackSelectionAlg', 'MuonTrackSelectionAlg' + postfix) alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits' alg.maxD0Significance = 3 alg.maxDeltaZ0SinTheta = 0.5 seq.append(alg, inputPropName='particles', stageName='selection', metaConfig={ 'selectionDecorNames': [alg.selectionDecoration], 'selectionDecorNamesOutput': [alg.selectionDecoration], 'selectionDecorCount': [3] }, dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Set up the muon calibration and smearing algorithm: alg = createAlgorithm('CP::MuonCalibrationAndSmearingAlg', 'MuonCalibrationAndSmearingAlg' + postfix) addPrivateTool(alg, 'calibrationAndSmearingTool', 'CP::MuonCalibrationPeriodTool') seq.append(alg, inputPropName='muons', outputPropName='muonsOut', affectingSystematics= '(^MUON_ID$)|(^MUON_MS$)|(^MUON_SAGITTA_.*)|(^MUON_SCALE$)', stageName='calibration', dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Set up the the pt selection alg = createAlgorithm('CP::AsgSelectionAlg', 'MuonPtCutAlg' + postfix) alg.selectionDecoration = 'selectPt' + postfix + ',as_bits' addPrivateTool(alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool') alg.selectionTool.minPt = 3e3 seq.append(alg, inputPropName='particles', stageName='selection', metaConfig={ 'selectionDecorNames': [alg.selectionDecoration], 'selectionDecorNamesOutput': [alg.selectionDecoration] if ptSelectionOutput else [], 'selectionDecorCount': [2] }, dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Setup the muon quality selection alg = createAlgorithm('CP::MuonSelectionAlgV2', 'MuonSelectionAlg' + postfix) addPrivateTool(alg, 'selectionTool', 'CP::MuonSelectionTool') alg.selectionTool.MuQuality = quality alg.selectionDecoration = 'good_muon' + postfix + ',as_bits' alg.badMuonVetoDecoration = 'is_bad' + postfix + ',as_char' seq.append(alg, inputPropName='muons', stageName='selection', metaConfig={ 'selectionDecorNames': [alg.selectionDecoration], 'selectionDecorNamesOutput': [alg.selectionDecoration] if qualitySelectionOutput else [], 'selectionDecorCount': [4] }, dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Set up the isolation calculation algorithm: if splitWP[1] != 'NonIso': alg = createAlgorithm('CP::MuonIsolationAlg', 'MuonIsolationAlg' + postfix) addPrivateTool(alg, 'isolationTool', 'CP::IsolationSelectionTool') alg.isolationDecoration = 'isolated_muon' + postfix + ',as_bits' seq.append(alg, inputPropName='muons', outputPropName='muonsOut', stageName='selection', metaConfig={ 'selectionDecorNames': [alg.isolationDecoration], 'selectionDecorNamesOutput': [alg.isolationDecoration], 'selectionDecorCount': [1] }, dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) pass # Set up an algorithm used for decorating baseline muon selection: alg = createAlgorithm('CP::AsgSelectionAlg', 'MuonSelectionSummary' + postfix) addPrivateTool(alg, 'selectionTool', 'CP::AsgFlagSelectionTool') alg.selectionDecoration = 'baselineSelection' + postfix + ',as_char' seq.append(alg, inputPropName='particles', stageName='selection', dynConfig={ 'selectionTool.selectionFlags': lambda meta: meta["selectionDecorNames"] }) # Set up an algorithm used to create muon selection cutflow: if enableCutflow: alg = createAlgorithm('CP::ObjectCutFlowHistAlg', 'MuonCutFlowDumperAlg' + postfix) alg.histPattern = 'muon' + postfix + '_cflow_%SYS%' seq.append(alg, inputPropName='input', stageName='selection', dynConfig={ 'selection': lambda meta: meta["selectionDecorNames"], 'selectionNCuts': lambda meta: meta["selectionDecorCount"] }) # Set up an algorithm that makes a view container using the selections # performed previously: if shallowViewOutput: alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'MuonViewFromSelectionAlg' + postfix) seq.append(alg, inputPropName='input', outputPropName='output', stageName='selection', dynConfig={ 'selection': lambda meta: meta["selectionDecorNamesOutput"] }) # Set up the efficiency scale factor calculation algorithm: alg = createAlgorithm('CP::MuonEfficiencyScaleFactorAlg', 'MuonEfficiencyScaleFactorAlg' + postfix) addPrivateTool(alg, 'efficiencyScaleFactorTool', 'CP::MuonEfficiencyScaleFactors') alg.scaleFactorDecoration = 'muon_effSF' + postfix + "_%SYS%" alg.scaleFactorDecorationRegex = '(^MUON_EFF_RECO.*)' alg.outOfValidity = 2 #silent alg.outOfValidityDeco = 'bad_eff' + postfix alg.efficiencyScaleFactorTool.WorkingPoint = sfWorkingPoint if dataType != 'data': seq.append(alg, inputPropName='muons', affectingSystematics='(^MUON_EFF_RECO.*)', stageName='efficiency', dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Set up an algorithm dumping the kinematic properties of the muons: if enableKinematicHistograms: alg = createAlgorithm('CP::KinematicHistAlg', 'MuonKinematicDumperAlg' + postfix) alg.histPattern = 'muon' + postfix + '_%VAR%_%SYS%' seq.append(alg, inputPropName='input', stageName='selection', dynConfig={ 'preselection': lambda meta: "&&".join(meta["selectionDecorNames"]) }) # Set up a final deep copy making algorithm if requested: if deepCopyOutput: alg = createAlgorithm('CP::AsgViewFromSelectionAlg', 'MuonDeepCopyMaker' + postfix) alg.deepCopy = True seq.append(alg, inputPropName='input', outputPropName='output', stageName='selection', dynConfig={ 'selection': lambda meta: meta["selectionDecorNamesOutput"] }) pass # Return the sequence: return seq