Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
2017_LbtoD0pmunu_Kmunu_mc_background_Lb2pK-Mu.py 32.33 KiB

##########################################################
# AUTOMATICALLY GENERATED FILE IN ganga_options_generic.py
simulation = True
noPID = True
no_restrip = False
stripping_stream = 'Semileptonic'
stripping_line = 'B2XTauTauLeptonic_Lb2pK_Mu_line'
decay = '(( Lambda_b0 -> ^(D0 -> ^mu+ ^mu-) ^(Lambda(1520)0 -> ^p+ ^K-) )) ||                                     (( Lambda_b~0 -> ^(D0 -> ^mu- ^mu+) ^(Lambda(1520)~0 -> ^p~- ^K+) ))                                   '
branches = {'mother': '(( Lambda_b0 -> (D0 -> mu+ mu-) (Lambda(1520)0 -> p+ K-) )) || (( Lambda_b~0 -> (D0 -> mu- mu+) (Lambda(1520)~0 -> p~- K+) ))', 'jpsi': '(( Lambda_b0 -> ^(D0 -> mu+ mu-) (Lambda(1520)0 -> p+ K-) )) || (( Lambda_b~0 -> ^(D0 -> mu- mu+) (Lambda(1520)~0 -> p~- K+) ))', 'd1': '(( Lambda_b0 -> (D0 -> ^mu+ mu-) (Lambda(1520)0 -> p+ K-) )) || (( Lambda_b~0 -> (D0 -> ^mu- mu+) (Lambda(1520)~0 -> p~- K+) ))', 'd2': '(( Lambda_b0 -> (D0 -> mu+ ^mu-) (Lambda(1520)0 -> p+ K-) )) || (( Lambda_b~0 -> (D0 -> mu- ^mu+) (Lambda(1520)~0 -> p~- K+) ))', 'inter': '(( Lambda_b0 -> (D0 -> mu+ mu-) ^(Lambda(1520)0 -> p+ K-) )) || (( Lambda_b~0 -> (D0 -> mu- mu+) ^(Lambda(1520)~0 -> p~- K+) ))', 'h1': '(( Lambda_b0 -> (D0 -> mu+ mu-) (Lambda(1520)0 -> ^p+ K-) )) || (( Lambda_b~0 -> (D0 -> mu- mu+) (Lambda(1520)~0 -> ^p~- K+) ))', 'h2': '(( Lambda_b0 -> (D0 -> mu+ mu-) (Lambda(1520)0 -> p+ ^K-) )) || (( Lambda_b~0 -> (D0 -> mu- mu+) (Lambda(1520)~0 -> p~- ^K+) ))'}
year = '2017'
mcdecay = 'lbtopX'
# END AUTOMATICALLY GENERATED FILE IN ganga_options_generic.py
# From here on, the file dv_options_generic.py will be used
# (Stripping will be added first for the lb2pkemu line)
###########################################################

try:
    simulation = simulation
    noPID = noPID
    no_restrip = no_restrip
    stripping_stream = stripping_stream
    stripping_line = stripping_line
    decay = decay
    branches = branches
    year = year
    mcdecay = mcdecay

except NameError as error:
    raise RuntimeError("""{error} - most likely due to incorrect usage; don't run this file directly.
                       The variables 'simulation', 'noPID', 'stripping_line', 'decay', 'branches', 'year', 'mcdecay'  have to
                       be specified first. Correct usage is to run 'ganga_options_generic.py'""")

# don't use f-strings as not supported pre python 3.6!
print("Simulation = {0}".format(simulation))
print("noPID = {0}".format(noPID))
print("no_restrip = {0}".format(no_restrip))
print("year = {0}".format(year))
print("mcdecay descriptor = {0}".format(mcdecay))
print("stripping stream = {0}".format(stripping_stream))
print("stripping line = {0}".format(stripping_line))
print("decay = {0}".format(decay))
print("branches = {0}".format(branches))


from Gaudi.Configuration import *
from LHCbKernel.Configuration import *
from DaVinci.Configuration import *
from DecayTreeTuple.Configuration import *

from Configurables import DaVinci, DecayTreeTuple, MCDecayTreeTuple
from Configurables import TupleToolTISTOS
from Configurables import EventNodeKiller, ProcStatusCheck
from Configurables import LoKi__HDRFilter

from StrippingConf.Configuration import StrippingConf, StrippingStream
from StrippingSettings.Utils import strippingConfiguration
from StrippingArchive.Utils import buildStreams
from StrippingArchive import strippingArchive

##############################################

print("\n## Determined stripping line to be: \n ===>", stripping_line)

print("\n## config determined")
print("\n===> stream = ", stripping_stream)
print("\n===> line = ", stripping_line)
print("\n===> decay = ", decay)
print("\n===> branches = ", branches)
#################################
# ## Stream and stripping line ###
stream = stripping_stream
line = stripping_line

print("\n## Determined stripping line to be: \n ===>", stripping_line)

###########################
# ## Restripping process ###

# In the case that we are dealing with simulation, we may need to restrip due to the fact
# that the stripping version for the simulation is different (older) than that of the
# stripping line

# In the case that we remove the PID requirements, this changes the restripping process
# since we edit the config file to remove these requirements

if simulation == True:
    if no_restrip == False:
        event_node_killer = EventNodeKiller('StripKiller')
        event_node_killer.Nodes = ['/Event/AllStreams', '/Event/Strip']
    ####################
        if noPID == False: # conventional restrip
            if year == '2016':
                strip = 'stripping28r2p2'
            elif year == '2017':
                strip = 'stripping29r2p3'
            elif year == '2018':
                strip = 'stripping34r0p3'
        
            streams = buildStreams(stripping=strippingConfiguration(strip),
                                   archive=strippingArchive(strip))

            custom_stream = StrippingStream('AllStreams')
            custom_line = 'Stripping'+line

            for stream in streams:
                for sline in stream.lines:
                    if sline.name() == custom_line:
                        custom_stream.appendLines([sline])
        #####################
        else: # remove PID requirements and restrip
            # in this case we need to edit the config to remove PID requirements
            # different stripping versions for different years
            if year == '2016':
                from StrippingArchive.Stripping28r2p2.StrippingRD.StrippingB2XTauTauLeptonic import B2XTauTauLeptonicConf as builder
                from StrippingArchive.Stripping28r2p2.StrippingRD.StrippingB2XTauTauLeptonic import default_config as config
            elif year == '2017':
                from StrippingArchive.Stripping29r2p3.StrippingRD.StrippingB2XTauTauLeptonic import B2XTauTauLeptonicConf as builder
                from StrippingArchive.Stripping29r2p3.StrippingRD.StrippingB2XTauTauLeptonic import default_config as config
            elif year == '2018':
                from StrippingArchive.Stripping34r0p3.StrippingRD.StrippingB2XTauTauLeptonic import B2XTauTauLeptonicConf as builder
                from StrippingArchive.Stripping34r0p3.StrippingRD.StrippingB2XTauTauLeptonic import default_config as config

            # print the unedited config file
            print("OLD CONFIG below")
            print(config['CONFIG'])
            print("OLD CONFIG above")

            # edit the config
            config1 = config['CONFIG']
            config1['MuonPID'] = -1001
            config1['ElectronPID'] = -1001
            config1['UseNoPIDsHadrons'] = True

            # then print the edited config file
            print("NEW CONFIG below")
            print(config1)
            print("NEW CONFIG above")

            lb = builder('B2XTauTauLeptonic', config1)
            custom_stream = StrippingStream('MyStream')
            # Now we have the stream and the line and can add the lines to the stream
            target_line = 'Stripping' + line
            for sline in lb.lines():
                if sline.name() == target_line:
                    custom_stream.appendLines([sline])
    #######################
        filterBadEvents = ProcStatusCheck()
        sc = StrippingConf(Streams=[custom_stream],
                            MaxCandidates=10000,
                            AcceptBadEvents=False,
                            BadEventSelection=filterBadEvents)
    
        ### Fill tuple with DecayTreeTuple ###
        dtt = DecayTreeTuple('DecayTreeTuple')
        dtt.Inputs = ['/Event/Phys/{0}/Particles'.format(line)]    
    else: # no restrip
        ### Fill tuple with DecayTreeTuple ###
        dtt = DecayTreeTuple('DecayTreeTuple')
        dtt.Inputs = ['/Event/{0}/Phys/{1}/Particles'.format(stream,line)]    

else: # data rather than simulation
    ### Fill tuple with DecayTreeTuple ###
    dtt = DecayTreeTuple('DecayTreeTuple')
    dtt.Inputs = ['/Event/{0}/Phys/{1}/Particles'.format(stream,line)]     
########################################
# ## Define decay and create branches ###
dtt.Decay = decay
dtt.addBranches(branches)

######################
# ## Add tupletools ###
# # Generic tupletools ##
dtt.ToolList = [] #reset toollist to add verbose to geometry/kinematic

SubMassTool = dtt.addTupleTool('TupleToolSubMass')

#
#    will create Sum_{p=2,max) C(N,p) new entries in the nTuple called [head]_M[ij...]
#    Particle daughters are sorted by PID at each branch of the tree (cc-independant)
#
#**** Substitution property
#
#    usage : TupleTool.Substitution += ["pi+ => K+"] TupleTool.Substitution += ["K+ => pi+"]
#    produce alternative mass with substituted PID pi<->K (cc is assumed)
#
#-change only one pion (K) at once in case of several pion (K) in the decay tree (producing separate output par pion (K) )
#
#**** DoubleSubstitution property
#
#    usage : TupleTool.DoubleSubstitution += ["K+/pi- => pi+/K-"] TupleTool.DoubleSubstitution += ["K+/K-" => pi+/pi-"]
#    change all [K+pi-]cc ([K+K-]cc) pairs to (pi+K-)cc ([pi+pi-]cc)
#    change only one pair at once in case of several pairs in the decay tree (producing separate output per pair)
#
#    "/" separator is not mandatory : K+pi- syntax is allowed (just a bit slower to parse)


GeometryTool = dtt.addTupleTool('TupleToolGeometry') #Fill geometry related information for DecayTreeTuple
GeometryTool.Verbose=True
# head_MINIP : minimum impact parameter on any PV
# head_MINIPCHI2 : minimum chi2 IP on all PVs
# head_ENDVERTEX_[X|Y|Z] : decay vertex position for composite particles
# head_ENDVERTEX_[X|Y|Z]ERR : decay vertex position error estimate for composite particles
# head_ENDVERTEX_CHI2 : decay vertex chi2
# head_ENDVERTEX_NDOF : decay vertex nDoF
# head_OWNPV_[X|Y|Z] : related primary vertex position
# head_OWNPV_[X|Y|Z]ERR : related primary vertex position error estimate for composite particles
# head_OWNPV_CHI2 : related primary vertex chi2
# head_OWNPV_NDOF : related primary vertex nDoF
# head_IP_OWNPV : impact parameter with respect to the PhysDesktop::relatedVertex() considered particle
# head_IPCHI2_OWNPV : impact parameter chi2 with respect to the relatedVertex() considered particle
# head_FD_OWNPV : flight distance of composite particle wrt. the relatedVertex() considered particle
# head_FDCHI2_OWNPV : flight distance significance in units of chi2 wrt. the relatedVertex() considered particle
# head_DIRA_OWNPV : direction angle wrt. the PhysDesktop::relatedVertex() considered particle

# Verbose being true gives:
# head_TOPPV_[X|Y|Z] : PhysDesktop::relatedVertex() of the top of decay chain position
# head_TOPPV_[X|Y|Z]ERR : PhysDesktop::relatedVertex() of the top of decay chain position error estimate
# head_TOPPV_CHI2 : PhysDesktop::relatedVertex() of the top of decay chain chi2
# head_TOPPV_NDOF : PhysDesktop::relatedVertex() of the top of decay chain nDoF
# head_IP_TOPPV : impact parameter with respect to the PhysDesktop::relatedVertex() of the top of decay chain
# head_IPCHI2_TOPPV : impact parameter chi2 with respect to the relatedVertex() of the top of decay chain
# head_FD_TOPPV : flight distance of composite particle wrt. the relatedVertex() of the top of decay chain
# head_FDCHI2_TOPPV : flight distance significance in units of chi2 wrt. the PhysDesktop::relatedVertex() of the top of decay chain
# head_DIRA_TOPPV : direction angle wrt. the relatedVertex() of the top of decay chain
# head_ORIVX_[X|Y|Z] : ancestor's related primary vertex position (when applicable)
# head_ORIVX_[X|Y|Z]ERR : ancestor's related primary vertex position error estimate (when applicable)
# head_ORIVX_CHI2 : ancestor's related primary vertex chi2 (when applicable)
# head_ORIVX_NDOF : ancestor's related primary vertex nDoF (when applicable)
# head_IP_ORIVX : impact parameter with respect to the ancestor's vertex (when applicable)
# head_IPCHI2_ORIVX : impact parameter chi2 with respect to the ancestor's vertex (when applicable)
# head_FD_ORIVX : flight distance of composite particle wrt. the ancestor's vertex (when applicable)
# head_FDCHI2_ORIVX : flight distance significance in units of chi2 wrt. ancestor's vertex (when applicable)
# head_DIRA_ORIVX : direction angle wrt. ancestor's vertex (when applicable)

KinematicTool = dtt.addTupleTool('TupleToolKinematic') #Fill kinematic information for DecayTreeTuple
KinematicTool.Verbose=True
# head_P : momentum's amplitude
# head_PT : transverse momentum
# head_P[E|X|Y|Z] : four vector momentum
# head_MM : measured mass (or assigned mass in case of 'basic' particle
# head_M : mass calculated from momentum four-vector
# head_MMERR : error on the measured mass (only for non-basic parts)

# Verbose being true gives:
# head_REFP[X|Y|Z]: one point the particle momentum extrapolation goes through
# head_PreFitMass: Mass of 4-vectors of daughters, not yet extrapolated to the head decay vertex (only for composites). 
#       This is the quantity used in ADAMASS or AM in CombineParticles.CombinationCut
# prefix_AtVtx_P[X|Y|Z]: momentum information of basic particles at origin vertex position

TrackInfoTool = dtt.addTupleTool('TupleToolTrackInfo') #Fill track information for DecayTreeTuple
TrackInfoTool.Verbose=True
# X_TRACK_CHI2NDOF : track chi2/ndof
# X_TRACK_TYPE : track type
# X_TRACK_PCHI2 : track Chi2 probability
# X_TRACK_GhostProb : Ghost probability (run NeuralNetTmva to fill it)
# X_TRACK_CloneDist : Only available for 2009 data

# Verbose being true gives:
# X_TRACK_CHI2 : track chi2
# X_TRACK_NDOF : track ndof
# X_TRACK_VeloCHI2NDOF : Track fit velo chi2/nDoF
# X_TRACK_TCHI2NDOF : Track fit T chi2/nDoF
# X_TRACK_VELO_UTID : hopefully unique double constructed from multiplying all Velo hit IDs
# X_TRACK_TT_UTID : hopefully unique double constructed from multiplying all TT hit IDs
# X_TRACK_IT_UTID : hopefully unique double constructed from multiplying all IT hit IDs
# X_TRACK_OT_UTID : hopefully unique double constructed from multiplying all OT hit IDs
# X_TRACK_VP_UTID : hopefully unique double constructed from multiplying all VP hit IDs
# X_TRACK_UT_UTID : hopefully unique double constructed from multiplying all UT hit IDs
# X_TRACK_FT_UTID : hopefully unique double constructed from multiplying all FT hit IDs
# X_TRACK_nVeloHits : Number of Velo hits on the track
# X_TRACK_nVeloRHits : Number of Velo R hits on the track
# X_TRACK_nVeloPhiHits : Number of Velo phi hits on the track
# X_TRACK_nVeloPileUpHits : Number of Velo pile-up hits on the track
# X_TRACK_nTTHits : Number of TT hits on the track
# X_TRACK_nITHits : Number of IT hits on the track
# X_TRACK_nOTHits : Number of OT hits on the track
# X_TRACK_nVPHits : Number of VP hits on the track
# X_TRACK_nUTHits : Number of UT hits on the track
# X_TRACK_nFTHits : Number of FT hits on the track
# X_TRACK_FirstMeasurementX: x position of state at 'FirstMeasurement'
# X_TRACK_FirstMeasurementY: y position of state at 'FirstMeasurement'
# X_TRACK_FirstMeasurementZ: z position of state at 'FirstMeasurement'
# X_TRACK_History: Algorithm which the track was made with
# X_TRACK_qOverp : q/p of state at 'FirstMeasurement'
# X_TRACK_Tx : x slope of state at 'FirstMeasurement'
# X_TRACK_Ty : y slope of state at 'FirstMeasurement'

EventInfoTool = dtt.addTupleTool('TupleToolEventInfo') #Event and Run number for DecayTreeTuple
EventInfoTool.Verbose=True
# runNumber: well, the run number
# eventNumber:
# BCID and BC type
# Odin and Hlt TCKs
# GPS time
# If the property Mu is given it will fill the Mu of the run. A working dictionary can be 
#       found at https://twiki.cern.ch/twiki/bin/view/LHCb/NuMuPileUp

# if Verbose is on, also gps time in year,month,day,hour,min,second Note that months are 
#       numbered [0-11]. That's a convention. Sorry.

PropertimeTool = dtt.addTupleTool('TupleToolPropertime') #Fills the propertime for DecayTreeTuple
# PropertimeTool.Verbose=True   
# head_TAU
# head_TAUERR
# head_TAUCHI2

RecoStatsTool = dtt.addTupleTool('TupleToolRecoStats') #Fills Reco stats, from RecSummary. 
# RecoStatsTool.Verbose=True

PidTool = dtt.addTupleTool('TupleToolPid') #DLL and PID information to be stored in a Tuple 
# PidTool.Verbose=True
# head_ID : particleID().pid();
# For the long lived particles (isBasicParticle()).
# head_PIDe : LHCb::ProtoParticle::CombDLLe
# head_PIDmu : LHCb::ProtoParticle::CombDLLmu
# head_PIDK : LHCb::ProtoParticle::CombDLLk
# head_PIDp : LHCb::ProtoParticle::CombDLLp

ANNPIDTool = dtt.addTupleTool('TupleToolANNPID') #ProbNN values for "Electron", "Muon", "Pion", "Kaon", "Proton", "Ghost"
# ANNPIDTool.Verbose=True

AnglesTool = dtt.addTupleTool('TupleToolAngles') #Fill MC Particle with decay angle in mother frame
# AnglesTool.Verbose=True
# head_CosTheta : angle in mother's frame
# if WRTMother is false, will calculate angle in frame of top of tree

PrimariesTool = dtt.addTupleTool('TupleToolPrimaries') #Primary vertices properties for DecayTreeTuple
# PrimariesTool.Verbose=True
# coordinates PVX, PVY, PVZ
# errors PVXERR, PVYERR, PVZERR
# vertex chi2 PVCHI
# vertex ndf PVNDOF
# Nb of tracks used to do the vertex PVNTRACKS

## Isolation variables ##

# cone isolation #
ConeIsoTool = dtt.addTupleTool('TupleToolConeIsolation')
# choose cones to have a deltaR of 0.5, 0.7, 0.9 radians
ConeIsoTool.MinConeSize = 0.6
ConeIsoTool.MaxConeSize = 0.6
# ConeIsoTool.SizeStep = 0.2
# Fill asymmetry, delta, and component variables
# ConeIsoTool.FillAsymmetry = True
# ConeIsoTool.FillDeltas = True
# ConeIsoTool.FillComponents = True


# head_cc: charged cone
# head_nc: neutral cone

# head_XX_mult : number of objects inside the cone
# head_XX_sPT : scalar-summed pT of the objects inside the cone
# head_XX_vPT : vector-summed pT of the objects inside the cone
# head_XX_P : x, y and z components of the cone momentum
# head_XX_asy_P : momentum asymmetry between the head and the cone defined as (head_P - head_XX_P) / (head_P + head_XX_P)
# head_XX_asy_P : x, y, z and transverse components of the momentum asymmetry
# head_XX_deltaEta : difference in eta between the head and the cone
# head_XX_deltaPhi : difference in phi between the head and the cone
# head_XX_IT : transverse isolation of the head in the cone, defined as head_PT / (head_P + head_XX_P)_T
# head_IT : transverse isolation of the head in the charged and neutral cones, defined as head_PT / (head_P + head_cc_P + head_nc_P)_T
# head_cc_maxPt_Q : charge of the max-pT object in the charged cone
# head_XX_maxPt_P : x, y, z (and e) components of the max-pT object momentum in the cone
# head_MasshPi0: invariant mass of the seed-Pi0 combinations
# head_Pi0_DeltaR: DeltaR between the seed and the pi0 directions
# head_Pi0_E, head_Pi0_PX, head_Pi0_PY, head_Pi0_PZ: four momentum of the pi0
# head_Pi0_M: invariant mass of the pi0
# head_Pi0Ph1_CL, head_Pi0Ph2_CL: confidence levels of the (photon) pi0 daughters

# track isolation #
TrackIsoTool = dtt.addTupleTool('TupleToolTrackIsolation')
# choose cones to have a deltaR of 0.5, 0.7, 0.9 radians
TrackIsoTool.MinConeAngle = 0.6
TrackIsoTool.MaxConeAngle = 0.6
# TrackIsoTool.StepSize = 0.2
# fill asymmetry and DeltaAngles variables
TrackIsoTool.FillAsymmetry = True
# TrackIsoTool.FillDeltaAngles = True

# Open up a cone around head, exclude all tracks that are in the decay descriptor 
# (i.e. that belong to the decay you are looking for), build the variables with 
# the remaining tracks.

#     head_cmult : Number of tracks inside cone.
#     head_cp : Summed p inside cone
#     head_cpt : Summed pt inside cone
#     head_cpx : Summed px inside cone
#     head_cpy : Summed py inside cone
#     head_cpz : Summed pz inside cone

# If Verbose, or other flags are set:

# Asymmetry variables

#     head_pasy : (head_P - head_cp)/(head_P + head_cp)
#     head_ptasy : (head_PT - head_cpt)/(head_PT + head_cpt)
#     head_pxasy : (head_Px - head_cpx)/(head_Px + head_cpx)
#     head_pyasy : (head_Py - head_cpy)/(head_Py + head_cpy)
#     head_pzasy : (head_Pz - head_cpz)/(head_Pz + head_cpz) Delta angle variables
#     head_DeltaEta : Difference in eta between summed tracks and head
#     head_DeltaPhi : Difference in phi between summed tracks and head


# MinConeAngle: Set the minimal deltaR of the cone (default = 0.5), in radians
# MaxConeAngle: Set the maximum deltaR of the cone (default = 1.0), in radians
# StepSize: Set the step of deltaR between two iterations (default = 0.1), in radians
# TrackType: Set the type of tracks which are considered inside the cone (default = 3)
# FillAsymmetry: Flag to fill the asymmetry variables (default = false)
# FillDeltaAngles: Flag to fill the delta angle variables (default = false) 

# vertex isolation #
VtxIsoTool = dtt.addTupleTool('TupleToolVtxIsoln')

# head_NOPARTWITHINDCHI2WDW : no. of non-signal particles that when added to vertex give delta chi2 < specified window
# head_NOPARTWITHINCHI2WDW : no. of non-signal particles that when added to vertex give chi2 < specified window 
# head_SMALLESTCHI2: chi2 of smallest chi2 combination with any of the input Particles 
# head_SMALLESTDELTACHI2: delta chi2 of smallest delta chi2 combination with any of the input Particles
#### updates:
# (head)_NumVtxWithinChi2WindowOneTrack: number of particles that generate a vertex within a chi2 window
# (head)_SmallestDeltaChi2OneTrack: smallest delta chi2 when adding one track
# (head)_SmallestDeltaChi2MassOneTrack: mass of the candidate with the smallest delta chi2
# (head)_SmallestDeltaChi2TwoTracks: smallest delta chi2 when adding one track to the combination 
#       that has the smallest delta chi2 when adding one track
# (head)_SmallestDeltaChi2MassTwoTracks: mass of the candidate with the smallest delta chi2 when adding 
#       one track to the combination that has the smallest delta chi2 when adding one track

## Hybrid tupletool ##
all_hybrid = dtt.addTupleTool('LoKi::Hybrid::TupleTool/LoKi_All')
all_hybrid.Variables = {
    'ETA' : "ETA", #pseudorapidity
    'PHI' : "PHI", #asimuthal angle
    'MINIPCHI2' : "MIPCHI2DV(PRIMARY)",
    #The special version of LoKi::Particles::MinImpParChi2 functor which gets all the primary 
    #vertices from desktop.
    'MINIP' : "MIPDV(PRIMARY)",
    #The special version of LoKi::Particles::MinImpPar functor which gets all the primary 
    #vertices from desktop. 
    'IPCHI2_OWNPV' : "BPVIPCHI2()",
    #The special "context-dependent" version of LoKi::Particles::ImpParChi2 functor which gets 
    #the related primary vertex from IPhysDesktop tool
    'IP_OWNPV' : "BPVIP()",
    #The special "context-dependent" version of LoKi::Particles::ImpPar functor which gets the 
    #related primary vertex from IPhysDesktop tool.
    'DIRA_OWNPV' : "BPVDIRA",
    'ghost' : "TRGHP", #simple evaluator of "ghost probability"
    'TrackCHI2DOF' : 'TRCHI2DOF',
    'FD_CHI2' : "BPVVDCHI2",
    #BPV = Adaptor to "best-primary-vertex", VDCHI2 = Evaluator of the chi2 of GEOMETRY distance
    #between the particle "endVertex" and "the vertex". 
    'VCHI2DOF' : 'VFASPF(VCHI2/VDOF)',
    # #Decay tree fitter functions:
    # 'DTF_CHI2NDOF'    : "DTF_CHI2NDOF( True )",
    # #Simple evaluator of $\chi^2$ per degree of freedom for the decay tree fit.
    # 'DTF_VCHI2NDOF'   : "DTF_FUN ( VFASPF(VCHI2/VDOF) , True )",
    # #VCHI2 = evaluator of the Chi2 of the vertex / VDOF = evaluator of the number of degrees 
    # #of freedom for the vertex
    # 'DTF_MASS_JpsiConstr' : "DTF_FUN ( M , True , 'J/psi(1S)' )" , 
    # 'DTF_MASS' : "DTF_FUN ( M , True )" ,
    # 'DTF_CTAU"'        : "DTF_CTAU( 0, True )",
    # #Evaluate $c\tau$ for the dauthter particle in the decay tree.
    # 'DTF_CTAUS'       : "DTF_CTAUSIGNIFICANCE( 0, True )"
    # #Evaluate $ \frac{c\tau}{\sigma \left( c\tau\right) } $ for the dauthter particle 
    # #in the decay tree.
}

lb_hybrid = dtt.mother.addTupleTool('LoKi::Hybrid::TupleTool/LoKi_Lb')
lb_hybrid.Variables = {
    'DOCAjpsiinter' : "DOCA(1,2)",
    'DOCAmotherjpsi' : "DOCA(0,1)",
    'DOCAmotherinter' : "DOCA(0,2)",
    'DOCAjpsiinterCHI2' : "DOCACHI2(1,2)",
    'DOCAmotherjpsiCHI2' : "DOCACHI2(0,1)",
    'DOCAmotherinterCHI2' : "DOCACHI2(0,2)"
}
jpsi_hybrid = dtt.jpsi.addTupleTool('LoKi::Hybrid::TupleTool/LoKi_jpsi')
jpsi_hybrid.Variables = {
    'DOCAd1d2' : "DOCA(1,2)",
    'DOCAjpsid1' : "DOCA(0,1)",
    'DOCAjpsid2' : "DOCA(0,2)",
    'DOCAd1d2CHI2' : "DOCACHI2(1,2)",
    'DOCAjpsid1CHI2' : "DOCACHI2(0,1)",
    'DOCAjpsid2CHI2' : "DOCACHI2(0,2)"
}
inter_hybrid = dtt.inter.addTupleTool('LoKi::Hybrid::TupleTool/LoKi_inter')
inter_hybrid.Variables = {
    #note that the end vertex of the intermediate hadron is also the end vertex of the lb
    'DOCAh1h2' : "DOCA(1,2)",
    'DOCAmotherh1' : "DOCA(0,1)",
    'DOCAmotherh2' : "DOCA(0,2)",
    'DOCAh1h2CHI2' : "DOCACHI2(1,2)",
    'DOCAmotherh1CHI2' : "DOCACHI2(0,1)",
    'DOCAmotherh2CHI2' : "DOCACHI2(0,2)"
}

################
### Triggers ###
# Add trigger list #

L0Triggers = ['L0MuonDecision','L0DiMuonDecision','L0HadronDecision']
Hlt1Triggers = [# muon lines
                'Hlt1TrackMuonDecision','Hlt1SingleMuonNoIPDecision','Hlt1SingleMuonHighPTDecision',
                # dimuon lines
                'Hlt1DiMuonHighMassDecision','Hlt1DiMuonLowMassDecision',
                # MVA lines
                'Hlt1TrackMVADecision','Hlt1TrackMVALooseDecision','Hlt1TrackMuonMVADecision',
                'Hlt1TwoTrackMVALooseDecision','Hlt1TwoTrackMVADecision'
                ]
Hlt2Triggers = [# topo lines
                'Hlt2Topo2BodyDecision','Hlt2Topo3BodyDecision','Hlt2Topo4BodyDecision',
                'Hlt2TopoMu2BodyDecision','Hlt2TopoMu3BodyDecision','Hlt2TopoMu4BodyDecision',
                'Hlt2TopoMuMu2BodyDecision','Hlt2TopoMuMu3BodyDecision','Hlt2TopoMuMu4BodyDecision',
                # muon lines
                'Hlt2SingleMuonDecision','Hlt2SingleMuonHighPTDecision','Hlt2SingleMuonLowPTDecision',
                'Hlt2SingleMuonRareDecision','Hlt2SingleMuonVHighPTDecision',
                # dimuon lines
                'Hlt2DiMuonDecision','Hlt2DiMuonJPsiDecision','Hlt2DiMuonDetachedDecision',
                'Hlt2DiMuonDetachedHeavyDecision','Hlt2DiMuonDetachedJPsiDecision',
                'Hlt2DiMuonDetachedPsi2SDecision','Hlt2DiMuonSoftDecision'
                ]
# Electrons in Sel
if '_E_' in stripping_line or '_MuE_' in stripping_line or '_EPi_' in stripping_line:
    L0Triggers += ["L0ElectronDecision","L0ElectronHiDecision","L0PhotonDecision","L0PhotonHiDecision"]
    Hlt1Triggers += ["Hlt1SingleElectronNoIPDecision"]
    Hlt2Triggers += ["Hlt2TopoE2BodyDecision","Hlt2TopoE3BodyDecision","Hlt2TopoE4BodyDecision", 
                     "Hlt2TopoEE2BodyDecision","Hlt2TopoEE3BodyDecision","Hlt2TopoEE4BodyDecision", 
                     "Hlt2TopoMuE2BodyDecision","Hlt2TopoMuE3BodyDecision","Hlt2TopoMuE4BodyDecision"
                     ]

AllTriggers = L0Triggers + Hlt1Triggers + Hlt2Triggers

MuonTriggers = [# L0
                'L0MuonDecision',
                # Hlt1
                'Hlt1TrackMuonDecision','Hlt1SingleMuonNoIPDecision','Hlt1SingleMuonHighPTDecision',
                'Hlt1TrackMVADecision','Hlt1TrackMVALooseDecision','Hlt1TrackMuonMVADecision',
                # Hlt2
                'Hlt2SingleMuonDecision','Hlt2SingleMuonHighPTDecision',
                'Hlt2SingleMuonLowPTDecision','Hlt2SingleMuonRareDecision','Hlt2SingleMuonVHighPTDecision'
                ]
HadronTriggers = [# L0
                'L0HadronDecision',
                # Hlt1
                'Hlt1TrackMVADecision','Hlt1TrackMVALooseDecision'
                ]
ElectronTriggers = [# L0
                     "L0ElectronDecision","L0ElectronHiDecision","L0PhotonDecision","L0PhotonHiDecision", 
                    # Hlt1
                    "Hlt1SingleElectronNoIPDecision","Hlt1TrackMVADecision"]

# TupleToolTISTOS #
# It saves the trigger TIS/TOS decisions for each particle for each Hlt Selection
dtt.mother.ToolList += [ "TupleToolTISTOS" ]
dtt.mother.addTool( TupleToolTISTOS, name = "TupleToolTISTOS" )
dtt.mother.TupleToolTISTOS.Verbose = True
dtt.mother.TupleToolTISTOS.TriggerList = AllTriggers
dtt.d2.ToolList += [ "TupleToolTISTOS" ]
dtt.d2.addTool( TupleToolTISTOS, name = "TupleToolTISTOS" )
dtt.d2.TupleToolTISTOS.Verbose = True
dtt.d2.TupleToolTISTOS.TriggerList = HadronTriggers
dtt.d1.ToolList += [ "TupleToolTISTOS" ]
dtt.d1.addTool( TupleToolTISTOS, name = "TupleToolTISTOS" )
dtt.d1.TupleToolTISTOS.Verbose = True
dtt.d1.TupleToolTISTOS.TriggerList = HadronTriggers
dtt.h1.ToolList += [ "TupleToolTISTOS" ]
dtt.h1.addTool( TupleToolTISTOS, name = "TupleToolTISTOS" )
dtt.h1.TupleToolTISTOS.Verbose = True
if '_Mu_' in stripping_line or '_MuE_' in stripping_line or '_MuPi_' in stripping_line:
    dtt.h1.TupleToolTISTOS.TriggerList = MuonTriggers
elif '_EPi_' in stripping_line:
    dtt.h1.TupleToolTISTOS.TriggerList = ElectronTriggers
elif '_PiPi_' in stripping_line:
     dtt.h1.TupleToolTISTOS.TriggerList = HadronTriggers
# else: # TODO remove
#     print('LOCAL:    ERROR WITH h1 TRIGGER ASSIGNMENT')

dtt.h2.ToolList += [ "TupleToolTISTOS" ]
dtt.h2.addTool( TupleToolTISTOS, name = "TupleToolTISTOS" )
dtt.h2.TupleToolTISTOS.Verbose = True
if '_E_' in stripping_line or '_MuE_' in stripping_line:
    dtt.h2.TupleToolTISTOS.TriggerList = ElectronTriggers
elif '_EPi_' in stripping_line or '_PiPi_' in stripping_line:
     dtt.h2.TupleToolTISTOS.TriggerList = HadronTriggers

## MC truth value tupletools ##
if simulation == True:
    MCTruthTool = dtt.addTupleTool('TupleToolMCTruth') #Fill MC truth info if a link is present 
    # head_TRUEID : true pid
    MCTruthTool.ToolList = [
        "MCTupleToolKinematic", 
        # head_TRUEP[E|X|Y|Z] : true four vector momentum
        # head_TRUEPT : true transverse momentum, PT
        # head_TRUEORIGINVERTEX_[X|Y|Z] : position of the true origin vertex.
        # head_TRUEENDVERTEX_[X|Y|Z] : position of the true end vertex (the first one)
        # head_TRUEISSTABLE : MCAssociate has no daughters.
        # head_TRUETAU : true propertime
        "MCTupleToolHierarchy",
        # head_MC_MOTHER_ID : true mc mother ID
        # head_MC_MOTHER_KEY : true mc mother key
        # head_MC_GD_MOTHER_ID : grand mother ID
        # head_MC_GD_MOTHER_KEY : grand mother key
        # head_MC_GD_GD_MOTHER_ID : grand grand mother ID
        # head_MC_GD_GD_MOTHER_KEY : grand grand mother key
    ]

    ## Hybrid tupletool ##
    hybrid_mc_dtt = dtt.addTupleTool('LoKi::Hybrid::MCTupleTool/LoKi_All_MC')
    hybrid_mc_dtt.Variables = {
        'MCETA' : "MCETA", #pseudorapidity
        'MCPHI' : "MCPHI", #asimuthal angle
    }

    MCBackgroundInfoTool = dtt.addTupleTool('TupleToolMCBackgroundInfo') #Fill the info from IBackgroundCategory. 
    # head_BKGCAT : category

    # add mcdecaytreetuple
    
    # mcdtt decay descriptor
    mcdecay_strings = {
        'lbtopkll': "[Lambda_b0 ==> ^p+ ^K- ^l+ ^l-]CC",
        'lbtopkllpi0': "[Lambda_b0 ==> ^p+ ^K- ^l+ ^l- ^pi0]CC",
        'lbtopkjpsi': '[Lambda_b0 ==> ^p+ ^K- ^J/psi(1S)]CC',
        'lbtopX': "([Lambda_b0]cc --> [p+]cc ... )", # only mother branch
        'xibtopX': "([Xi_b0]cc --> [p+]cc ... )", # only mother branch
        'bstokX': "([B_s0]cc --> [K+]cc ... )", # only mother branch
        'b0tokX': "([B0]cc --> [K+]cc ... )", # only mother branch
        'b0toDX': "([B0]cc --> [D-]cc ... )", # only mother branch
        'b0toD0X': "([B0]cc --> [D0]cc ... )", # only mother branch
        'bstoDsX': "([B_s0]cc --> [D_s-]cc ... )", # only mother branch
        'btoD0X': "([B+]cc --> [D0]cc ... )", # only mother branch
        'lbtolcX': "([Lambda_b0]cc --> [Lambda_c+]cc ... )", # only mother branch
        'b0toDsX': "([B0]cc --> [D_s-]cc ... )", # only mother branch
        'b0topX': "([B0]cc --> [p+]cc ... )", # only mother branch
    }
    print("\n## Matching mcdecay identifier to correct string: \n===> ", mcdecay)
    mcdecay = mcdecay_strings[mcdecay]
    print("===>  matched: ", mcdecay)

    def Make_MCTuple(decay):
        MCTuple = MCDecayTreeTuple("MCDecayTreeTuple")

        MCTuple.Decay = decay
        MCTuple.setDescriptorTemplate( decay )
        MCTuple.ToolList = [
            "MCTupleToolKinematic"
            , "MCTupleToolHierarchy"
            , "MCTupleToolAngles"
            , "MCTupleToolPID"
            , "TupleToolEventInfo"
            , "TupleToolRecoStats"
        ]
        ## Hybrid tupletool ##
        hybrid_mc_mcdtt = MCTuple.addTupleTool('LoKi::Hybrid::MCTupleTool/LoKi_All_MC')
        hybrid_mc_mcdtt.Variables = {
            'MCETA' : "MCETA", #pseudorapidity
            'MCPHI' : "MCPHI", #asimuthal angle
        }
        return MCTuple
    

    MCTuple = Make_MCTuple(mcdecay)

#########################
# ## Configure DaVinci ###
from Configurables import GaudiSequencer
MySequencer = GaudiSequencer('Sequence')
if simulation == True:
    MySequencer.Members = [event_node_killer, sc.sequence(), dtt, MCTuple]
    # MySequencer.Members = [event_node_killer, sc.sequence(),dtt]
else:
    MySequencer.Members = [dtt]

DaVinci().UserAlgorithms += [MySequencer]
DaVinci().EvtMax = -1
DaVinci().PrintFreq = 1000