Skip to content
Snippets Groups Projects
  • Nils Krumnack's avatar
    a79b91f8
    add "proper" views and thinning for full CP algorithm test sequence · a79b91f8
    Nils Krumnack authored
    This removes all the implicit views created by the various sequences,
    and instead creates explicit views as inputs for MET and OR.  And it
    adds actual consistent thinning for all containers for the output.
    And it adds pt/eta cuts for all the objects, which are probably way
    off from what they should be...
    
    I also had to add some extra configuration options/decorations to some
    of the sequences to make it all consistent.
    a79b91f8
    History
    add "proper" views and thinning for full CP algorithm test sequence
    Nils Krumnack authored
    This removes all the implicit views created by the various sequences,
    and instead creates explicit views as inputs for MET and OR.  And it
    adds actual consistent thinning for all containers for the output.
    And it adds pt/eta cuts for all the objects, which are probably way
    off from what they should be...
    
    I also had to add some extra configuration options/decorations to some
    of the sequences to make it all consistent.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
FullCPAlgorithmsTest.py 20.71 KiB
# Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
#
# @author Nils Krumnack

from AnaAlgorithm.AlgSequence import AlgSequence
from AnaAlgorithm.DualUseConfig import createAlgorithm, createService, addPrivateTool
from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import pileupConfigFiles
from AnalysisAlgorithmsConfig.ConfigSequence import ConfigSequence
from AnalysisAlgorithmsConfig.ConfigAccumulator import ConfigAccumulator

# Config:
triggerChains = [
    'HLT_2mu14',
    'HLT_mu20_mu8noL1',
    'HLT_2e17_lhvloose_nod0'
]

muonMinPt = None
muonMaxEta = None

def addOutputCopyAlgorithms (algSeq, postfix, inputContainer, outputContainer, selection) :
    """add a uniformly filtered set of deep copies based on the
    systematics dependent selection"""

    if postfix[0] != '_' :
        postfix = '_' + postfix

    if selection != '' :
        unionalg = createAlgorithm( 'CP::AsgUnionSelectionAlg', 'UnionSelectionAlg' + postfix)
        unionalg.preselection = selection
        unionalg.particles = inputContainer
        unionalg.selectionDecoration = 'outputSelect'
        algSeq += unionalg

    copyalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'DeepCopyAlg' + postfix )
    copyalg.input = inputContainer
    copyalg.output = outputContainer
    if selection != '' :
        copyalg.selection = ['outputSelect']
    else :
        copyalg.selection = []
    copyalg.deepCopy = True
    algSeq += copyalg


def makeSequenceOld (dataType, algSeq, vars, forCompare) :

    # Include, and then set up the pileup analysis sequence:
    prwfiles, lumicalcfiles = pileupConfigFiles(dataType)

    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
        makePileupAnalysisSequence
    pileupSequence = makePileupAnalysisSequence(
        dataType,
        userPileupConfigs=prwfiles,
        userLumicalcFiles=lumicalcfiles,
    )
    pileupSequence.configure( inputName = {}, outputName = {} )

    # Add the pileup sequence to the job:
    algSeq += pileupSequence
    vars += [ 'EventInfo.runNumber     -> runNumber',
             'EventInfo.eventNumber   -> eventNumber', ]


    # Include, and then set up the jet analysis algorithm sequence:
    from JetAnalysisAlgorithms.JetAnalysisSequence import makeJetAnalysisSequence
    jetContainer = 'AntiKt4EMPFlowJets'
    jetSequence = makeJetAnalysisSequence( dataType, jetContainer, enableCutflow=True, enableKinematicHistograms=True, shallowViewOutput = False )

    from FTagAnalysisAlgorithms.FTagAnalysisSequence import makeFTagAnalysisSequence
    makeFTagAnalysisSequence( jetSequence, dataType, jetContainer, noEfficiency = True, legacyRecommendations = True,
                              enableCutflow=True )

    jetSequence.configure( inputName = jetContainer, outputName = 'AnaJets_%SYS%' )


    # Include, and then set up the jet analysis algorithm sequence:
    from JetAnalysisAlgorithms.JetJvtAnalysisSequence import makeJetJvtAnalysisSequence
    jvtSequence = makeJetJvtAnalysisSequence( dataType, jetContainer, enableCutflow=True, shallowViewOutput = False )
    jvtSequence.configure( inputName = { 'jets'      : 'AnaJets_%SYS%' },
                           outputName = {  } )

    # Add the sequences to the job:
    algSeq += jetSequence
    algSeq += jvtSequence
    if not forCompare :
        vars += ['OutJets_%SYS%.pt  -> jet_pt_%SYS%',
                 'OutJets_NOSYS.phi -> jet_phi',
                 'OutJets_NOSYS.eta -> jet_eta', ]
        if dataType != 'data':
            vars += [
                # 'EventInfo.jvt_effSF_%SYS% -> jvtSF_%SYS%',
                # 'EventInfo.fjvt_effSF_%SYS% -> fjvtSF_%SYS%',
                'OutJets_%SYS%.jvt_effSF_%SYS% -> jet_jvtEfficiency_%SYS%',
                # 'OutJets_%SYS%.fjvt_effSF_NOSYS -> jet_fjvtEfficiency_%SYS%',
            ]


    # Include, and then set up the muon analysis algorithm sequence:
    from MuonAnalysisAlgorithms.MuonAnalysisSequence import makeMuonAnalysisSequence
    muonSequenceMedium = makeMuonAnalysisSequence( dataType, deepCopyOutput = False, shallowViewOutput = False,
                                                   workingPoint = 'Medium.NonIso', postfix = 'medium',
                                                   enableCutflow=True, enableKinematicHistograms=True, ptSelectionOutput = True )
    muonSequenceMedium.configure( inputName = 'Muons',
                                  outputName = 'AnaMuonsMedium_%SYS%' )

    # Add the sequence to the job:
    algSeq += muonSequenceMedium

    muonSequenceTight = makeMuonAnalysisSequence( dataType, deepCopyOutput = False, shallowViewOutput = False,
                                                  workingPoint = 'Tight.NonIso', postfix = 'tight',
                                                  enableCutflow=True, enableKinematicHistograms=True, ptSelectionOutput = True )
    muonSequenceTight.removeStage ("calibration")
    muonSequenceTight.configure( inputName = 'AnaMuonsMedium_%SYS%',
                                 outputName = 'AnaMuons_%SYS%')

    # Add the sequence to the job:
    algSeq += muonSequenceTight
    vars += [ 'OutMuons_NOSYS.eta -> mu_eta',
              'OutMuons_NOSYS.phi -> mu_phi',
              'OutMuons_%SYS%.pt  -> mu_pt_%SYS%',
              'OutMuons_%SYS%.baselineSelection_medium -> mu_select_medium_%SYS%',
              'OutMuons_%SYS%.baselineSelection_tight  -> mu_select_tight_%SYS%', ]
    if dataType != 'data':
        vars += [ 'OutMuons_%SYS%.muon_effSF_medium_%SYS% -> mu_effSF_medium_%SYS%',
                  'OutMuons_%SYS%.muon_effSF_tight_%SYS% -> mu_effSF_tight_%SYS%', ]


    # Include, and then set up the electron analysis sequence:
    from EgammaAnalysisAlgorithms.ElectronAnalysisSequence import \
        makeElectronAnalysisSequence
    likelihood = True
    recomputeLikelihood=False
    if likelihood:
        workingpoint = 'LooseLHElectron.Loose_VarRad'
    else:
        workingpoint = 'LooseDNNElectron.Loose_VarRad'
    electronSequence = makeElectronAnalysisSequence( dataType, workingpoint, postfix = 'loose',
                                                     recomputeLikelihood=recomputeLikelihood, enableCutflow=True, enableKinematicHistograms=True, shallowViewOutput = False )
    electronSequence.configure( inputName = 'Electrons',
                                outputName = 'AnaElectrons_%SYS%' )
    algSeq += electronSequence
    if not forCompare :
        vars += [ 'OutElectrons_%SYS%.pt  -> el_pt_%SYS%',
                  'OutElectrons_NOSYS.phi -> el_phi',
                  'OutElectrons_NOSYS.eta -> el_eta',
                  'OutElectrons_%SYS%.baselineSelection_loose -> el_select_loose_%SYS%', ]


    # Include, and then set up the photon analysis sequence:
    from EgammaAnalysisAlgorithms.PhotonAnalysisSequence import \
        makePhotonAnalysisSequence
    photonSequence = makePhotonAnalysisSequence( dataType, 'Tight.FixedCutTight', postfix = 'tight',
                                                 recomputeIsEM=False, enableCutflow=True, enableKinematicHistograms=True, shallowViewOutput = False )
    photonSequence.configure( inputName = 'Photons',
                              outputName = 'AnaPhotons_%SYS%' )
    algSeq += photonSequence
    if not forCompare :
        vars += [ 'OutPhotons_%SYS%.pt  -> ph_pt_%SYS%',
                  'OutPhotons_NOSYS.phi -> ph_phi',
                  'OutPhotons_NOSYS.eta -> ph_eta',
                  'OutPhotons_%SYS%.baselineSelection_tight -> ph_select_tight_%SYS%', ]


    # Include, and then set up the tau analysis algorithm sequence:
    from TauAnalysisAlgorithms.TauAnalysisSequence import makeTauAnalysisSequence
    tauSequence = makeTauAnalysisSequence( dataType, 'Tight', postfix = 'tight',
                                           enableCutflow=True, enableKinematicHistograms=True, shallowViewOutput = False )
    tauSequence.configure( inputName = 'TauJets', outputName = 'AnaTauJets_%SYS%' )

    # Add the sequence to the job:
    algSeq += tauSequence
    if not forCompare :
        vars += [ 'OutTauJets_%SYS%.pt  -> tau_pt_%SYS%',
                'OutTauJets_NOSYS.phi -> tau_phi',
                  'OutTauJets_NOSYS.eta -> tau_eta',
                  'OutTauJets_%SYS%.baselineSelection_tight -> tau_select_tight_%SYS%', ]


    # temporarily disabled until di-taus are supported in R22
    # # Include, and then set up the tau analysis algorithm sequence:
    # from TauAnalysisAlgorithms.DiTauAnalysisSequence import makeDiTauAnalysisSequence
    # diTauSequence = makeDiTauAnalysisSequence( dataType, 'Tight', postfix = 'tight' )
    # diTauSequence.configure( inputName = 'DiTauJets', outputName = 'AnaDiTauJets_%SYS%' )

    # Add the sequence to the job:
    # disabling this, the standard test files don't have DiTauJets
    # algSeq += diTauSequence


    # set up pt-eta selection for all the object types
    # currently disabling most cuts, but leaving these as placeholders
    # the cuts I have are mostly to silence MET building warnings

    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'UserElectronsSelectionAlg' )
    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
    selalg.selectionTool.minPt = 10e3
    #selalg.selectionTool.maxEta = 2.47
    selalg.selectionDecoration = 'selectPtEta'
    selalg.particles = 'AnaElectrons_%SYS%'
    algSeq += selalg

    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'UserPhotonsSelectionAlg' )
    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
    selalg.selectionTool.minPt = 10e3
    #selalg.selectionTool.maxEta = 2.47
    selalg.selectionDecoration = 'selectPtEta'
    selalg.particles = 'AnaPhotons_%SYS%'
    algSeq += selalg

    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'UserMuonsSelectionAlg' )
    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
    if muonMinPt :
        selalg.selectionTool.minPt = muonMinPt
    if muonMaxEta :
        selalg.selectionTool.maxEta = muonMaxEta
    selalg.selectionDecoration = 'selectPtEta'
    selalg.particles = 'AnaMuons_%SYS%'
    algSeq += selalg

    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'UserTauJetsSelectionAlg' )
    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
    #selalg.selectionTool.minPt = 10e3
    #selalg.selectionTool.maxEta = 2.47
    selalg.selectionDecoration = 'selectPtEta'
    selalg.particles = 'AnaTauJets_%SYS%'
    algSeq += selalg

    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'UserJetsSelectionAlg' )
    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
    #selalg.selectionTool.minPt = 10e3
    #selalg.selectionTool.maxEta = 2.47
    selalg.selectionDecoration = 'selectPtEta'
    selalg.particles = 'AnaJets_%SYS%'
    algSeq += selalg



    # Now make view containers for the inputs to the met calculation
    viewalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg','METElectronsViewAlg' )
    viewalg.selection = [ 'selectPtEta', 'baselineSelection_loose,as_char' ]
    viewalg.input = 'AnaElectrons_%SYS%'
    viewalg.output = 'METElectrons_%SYS%'
    algSeq += viewalg

    viewalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg','METPhotonsViewAlg' )
    viewalg.selection = [ 'selectPtEta', 'baselineSelection_tight,as_char' ]
    viewalg.input = 'AnaPhotons_%SYS%'
    viewalg.output = 'METPhotons_%SYS%'
    algSeq += viewalg

    viewalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg','METMuonsViewAlg' )
    viewalg.selection = [ 'selectPtEta', 'baselineSelection_medium,as_char' ]
    viewalg.input = 'AnaMuons_%SYS%'
    viewalg.output = 'METMuons_%SYS%'
    algSeq += viewalg

    viewalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg','METTauJetsViewAlg' )
    viewalg.selection = [ 'selectPtEta', 'baselineSelection_tight,as_char' ]
    viewalg.input = 'AnaTauJets_%SYS%'
    viewalg.output = 'METTauJets_%SYS%'
    algSeq += viewalg

    viewalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg','METJetsViewAlg' )
    viewalg.selection = [ 'selectPtEta' ]
    viewalg.input = 'AnaJets_%SYS%'
    viewalg.output = 'METJets_%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'      : 'METJets_%SYS%',
                                         'taus'      : 'METTauJets_%SYS%',
                                         'muons'     : 'METMuons_%SYS%',
                                         'electrons' : 'METElectrons_%SYS%',
                                         'photons'   : 'METPhotons_%SYS%' },
                           outputName = 'AnaMET_%SYS%' )

    # Add the sequence to the job:
    algSeq += metSequence
    if not forCompare :
        vars += [
            'AnaMET_%SYS%.mpx   -> met_mpx_%SYS%',
            'AnaMET_%SYS%.mpy   -> met_mpy_%SYS%',
            'AnaMET_%SYS%.sumet -> met_sumet_%SYS%',
            'AnaMET_%SYS%.name  -> met_name_%SYS%',
        ]


    # Make view containers holding as inputs for OR
    selectalg = createAlgorithm( 'CP::AsgSelectionAlg','ORElectronsSelectAlg' )
    selectalg.preselection = 'selectPtEta&&baselineSelection_loose,as_char'
    selectalg.particles = 'AnaElectrons_%SYS%'
    selectalg.selectionDecoration = 'preselectOR,as_char'
    algSeq += selectalg

    selectalg = createAlgorithm( 'CP::AsgSelectionAlg','ORPhotonsSelectAlg' )
    selectalg.preselection = 'selectPtEta&&baselineSelection_tight,as_char'
    selectalg.particles = 'AnaPhotons_%SYS%'
    selectalg.selectionDecoration = 'preselectOR,as_char'
    algSeq += selectalg

    selectalg = createAlgorithm( 'CP::AsgSelectionAlg','ORMuonsSelectAlg' )
    selectalg.preselection = 'selectPtEta&&baselineSelection_medium,as_char'
    selectalg.particles = 'AnaMuons_%SYS%'
    selectalg.selectionDecoration = 'preselectOR,as_char'
    algSeq += selectalg

    selectalg = createAlgorithm( 'CP::AsgSelectionAlg','ORTauJetsSelectAlg' )
    selectalg.preselection = 'selectPtEta&&baselineSelection_tight,as_char'
    selectalg.particles = 'AnaTauJets_%SYS%'
    selectalg.selectionDecoration = 'preselectOR,as_char'
    algSeq += selectalg

    selectalg = createAlgorithm( 'CP::AsgSelectionAlg','ORJetsSelectAlg' )
    selectalg.preselection = 'selectPtEta'
    selectalg.particles = 'AnaJets_%SYS%'
    selectalg.selectionDecoration = 'preselectOR,as_char'
    algSeq += selectalg


    # Include, and then set up the overlap analysis algorithm sequence:
    from AsgAnalysisAlgorithms.OverlapAnalysisSequence import \
        makeOverlapAnalysisSequence
    overlapSequence = makeOverlapAnalysisSequence( dataType, doMuPFJetOR=True, doTaus=False, enableCutflow=True, shallowViewOutput = False, inputLabel = 'preselectOR', outputLabel = 'passesOR' )
    overlapSequence.configure(
        inputName = {
            'electrons' : 'AnaElectrons_%SYS%',
            'photons'   : 'AnaPhotons_%SYS%',
            'muons'     : 'AnaMuons_%SYS%',
            'jets'      : 'AnaJets_%SYS%',
            'taus'      : 'AnaTauJets_%SYS%'
        },
        outputName = { } )

    # FIX ME: temporarily disabling this for data, as there are some
    # errors with missing primary vertices
    if dataType != 'data' :
        algSeq += overlapSequence
        if not forCompare :
            vars += [
                'OutJets_%SYS%.passesOR_%SYS% -> jet_select_or_%SYS%',
                'OutElectrons_%SYS%.passesOR_%SYS% -> el_select_or_%SYS%',
                'OutPhotons_%SYS%.passesOR_%SYS% -> ph_select_or_%SYS%',
                'OutMuons_%SYS%.passesOR_%SYS% -> mu_select_or_%SYS%',
                'OutTauJets_%SYS%.passesOR_%SYS% -> tau_select_or_%SYS%',
            ]

    if dataType != 'data' :
        # Include, and then set up the generator analysis sequence:
        from AsgAnalysisAlgorithms.GeneratorAnalysisSequence import \
            makeGeneratorAnalysisSequence
        generatorSequence = makeGeneratorAnalysisSequence( dataType, saveCutBookkeepers=True, runNumber=284500, cutBookkeepersSystematics=True )
        algSeq += generatorSequence
        if not forCompare :
            vars += [ 'EventInfo.generatorWeight_%SYS% -> generatorWeight_%SYS%', ]


    # Include, and then set up the trigger analysis sequence:
    from TriggerAnalysisAlgorithms.TriggerAnalysisSequence import \
        makeTriggerAnalysisSequence
    triggerSequence = makeTriggerAnalysisSequence( dataType, triggerChains=triggerChains )
    # FIXME: temporarily disabling this for comparisons, as there is no
    # corresponding configuration block.  also, maybe it should be possible
    # to disable filtering in the algorithm, i.e. just store the trigger
    # decision without throwing away events.
    if not forCompare :
        algSeq += triggerSequence
        vars += ['EventInfo.trigPassed_' + t + ' -> trigPassed_' + t for t in triggerChains]



    # make filtered output containers

    addOutputCopyAlgorithms (algSeq, 'Electrons', 'AnaElectrons_%SYS%', 'OutElectrons_%SYS%',
                             'selectPtEta&&baselineSelection_loose,as_char')
    addOutputCopyAlgorithms (algSeq, 'Photons', 'AnaPhotons_%SYS%', 'OutPhotons_%SYS%',
                             'selectPtEta&&baselineSelection_tight,as_char')
    addOutputCopyAlgorithms (algSeq, 'Muons', 'AnaMuons_%SYS%', 'OutMuons_%SYS%',
                             'selectPtEta&&baselineSelection_medium,as_char')
    addOutputCopyAlgorithms (algSeq, 'TauJets', 'AnaTauJets_%SYS%', 'OutTauJets_%SYS%',
                             'selectPtEta&&baselineSelection_tight,as_char')
    addOutputCopyAlgorithms (algSeq, 'Jets', 'AnaJets_%SYS%', 'OutJets_%SYS%',
                             'selectPtEta')



def makeSequenceBlocks (dataType, algSeq, vars, forCompare) :

    # Include, and then set up the pileup analysis sequence:
    prwfiles, lumicalcfiles = pileupConfigFiles(dataType)

    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
        makePileupAnalysisSequence
    pileupSequence = makePileupAnalysisSequence(
        dataType,
        userPileupConfigs=prwfiles,
        userLumicalcFiles=lumicalcfiles,
    )
    pileupSequence.configure( inputName = {}, outputName = {} )

    # Add the pileup sequence to the job:
    algSeq += pileupSequence
    vars += [ 'EventInfo.runNumber     -> runNumber',
              'EventInfo.eventNumber   -> eventNumber', ]


    # Include, and then set up the muon analysis algorithm sequence:
    from MuonAnalysisAlgorithms.MuonAnalysisConfig import makeMuonCalibrationConfig, makeMuonWorkingPointConfig

    configSeq = ConfigSequence ()

    makeMuonCalibrationConfig (configSeq, 'AnaMuons')
    makeMuonWorkingPointConfig (configSeq, 'AnaMuons', workingPoint='Medium.Iso', postfix='medium')
    makeMuonWorkingPointConfig (configSeq, 'AnaMuons', workingPoint='Tight.Iso', postfix='tight')
    vars += [ 'OutMuons_NOSYS.eta -> mu_eta',
              'OutMuons_NOSYS.phi -> mu_phi',
              'OutMuons_%SYS%.pt  -> mu_pt_%SYS%',
              'OutMuons_%SYS%.baselineSelection_medium -> mu_select_medium_%SYS%',
              'OutMuons_%SYS%.baselineSelection_tight  -> mu_select_tight_%SYS%', ]
    if dataType != 'data':
        vars += [ 'OutMuons_%SYS%.muon_effSF_medium_%SYS% -> mu_effSF_medium_%SYS%', ]
        vars += [ 'OutMuons_%SYS%.muon_effSF_tight_%SYS% -> mu_effSF_tight_%SYS%', ]

    configAccumulator = ConfigAccumulator (dataType, algSeq)
    configSeq.fullConfigure (configAccumulator)


    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'UserMuonsSelectionAlg' )
    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
    if muonMinPt :
        selalg.selectionTool.minPt = muonMinPt
    if muonMaxEta :
        selalg.selectionTool.maxEta = muonMaxEta
    selalg.selectionDecoration = 'selectPtEta'
    selalg.particles = 'AnaMuons_%SYS%'
    algSeq += selalg
    addOutputCopyAlgorithms (algSeq, 'Muons', 'AnaMuons_%SYS%', 'OutMuons_%SYS%',
                             'selectPtEta&&baselineSelection_medium,as_char')



def makeSequence (dataType, useBlocks, forCompare) :

    algSeq = AlgSequence()

    # Set up the systematics loader/handler service:
    sysService = createService( 'CP::SystematicsSvc', 'SystematicsSvc', sequence = algSeq )
    sysService.sigmaRecommended = 1

    vars = []
    if not useBlocks :
        makeSequenceOld (dataType, algSeq, vars=vars, forCompare=forCompare)
    else :
        makeSequenceBlocks (dataType, algSeq, vars=vars, forCompare=forCompare)


    # Add an ntuple dumper algorithm:
    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
    treeMaker.TreeName = 'analysis'
    algSeq += treeMaker
    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
    ntupleMaker.TreeName = 'analysis'
    ntupleMaker.Branches = vars
    # ntupleMaker.OutputLevel = 2  # For output validation
    algSeq += ntupleMaker
    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
    treeFiller.TreeName = 'analysis'
    algSeq += treeFiller

    return algSeq